mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-29 13:03:36 +01:00
kernel: add a generic callback mechanism
This mechanism allows callbacks to be defined with up to 4 32-bit arguments, and could be extended later. This will hopefully replace the timer_callback_t used in timers and RTC, and will be added to the DMA and USB APIs -- the hard part is to not break source compatibility with previous versions.
This commit is contained in:
parent
fb8d1525f4
commit
a2fd9e3351
2 changed files with 157 additions and 0 deletions
1
TODO
1
TODO
|
@ -1,4 +1,5 @@
|
||||||
Extensions on existing code:
|
Extensions on existing code:
|
||||||
|
* kernel: use GINT_CB() for all callbacks, without breaking the timer API
|
||||||
* kernel: better restore to userspace before panic (ensure BL=0 IMASK=0)
|
* kernel: better restore to userspace before panic (ensure BL=0 IMASK=0)
|
||||||
* kernel: check if cpu_setVBR() really needs to be perma-mapped
|
* kernel: check if cpu_setVBR() really needs to be perma-mapped
|
||||||
* project: add license file
|
* project: add license file
|
||||||
|
|
|
@ -129,4 +129,160 @@ void *gint_inthandler(int event_code, void const *handler, size_t size);
|
||||||
Returns the return value of the callback. */
|
Returns the return value of the callback. */
|
||||||
extern int (*gint_inth_callback)(int (*function)(void *arg), void *arg);
|
extern int (*gint_inth_callback)(int (*function)(void *arg), void *arg);
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Callback functions
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* gint_callback_arg_t: All types of arguments allowed in a callback
|
||||||
|
Other types can be used if casted, notably pointers to custom types can be
|
||||||
|
casted to (void *). */
|
||||||
|
typedef union {
|
||||||
|
/* Integer types */
|
||||||
|
int i;
|
||||||
|
unsigned int u;
|
||||||
|
int32_t i32;
|
||||||
|
uint32_t u32;
|
||||||
|
/* 4-byte floating-point type */
|
||||||
|
float f;
|
||||||
|
/* Pointer to void */
|
||||||
|
void *pv;
|
||||||
|
void const *pv_c;
|
||||||
|
void volatile *pv_v;
|
||||||
|
void volatile const *pv_cv;
|
||||||
|
/* Pointer to int */
|
||||||
|
int *pi;
|
||||||
|
int const *pi_c;
|
||||||
|
int volatile *pi_v;
|
||||||
|
int volatile const *pi_cv;
|
||||||
|
/* Pointer to unsigned int */
|
||||||
|
unsigned int *pu;
|
||||||
|
unsigned int const *pu_c;
|
||||||
|
unsigned int volatile *pu_v;
|
||||||
|
unsigned int volatile const *pu_cv;
|
||||||
|
/* Pointer to int32_t */
|
||||||
|
int32_t *pi32;
|
||||||
|
int32_t const *pi32_c;
|
||||||
|
int32_t volatile *pi32_v;
|
||||||
|
int32_t volatile const *pi32_cv;
|
||||||
|
/* Pointer to uint32_t */
|
||||||
|
uint32_t *pu32;
|
||||||
|
uint32_t const *pu32_c;
|
||||||
|
uint32_t volatile *pu32_v;
|
||||||
|
uint32_t volatile const *pu32_cv;
|
||||||
|
/* Pointer to float */
|
||||||
|
float *pf;
|
||||||
|
float const *pf_c;
|
||||||
|
float volatile *pf_v;
|
||||||
|
float volatile const *pf_cv;
|
||||||
|
/* Pointer to double */
|
||||||
|
double *pd;
|
||||||
|
double const *pd_c;
|
||||||
|
double volatile *pd_v;
|
||||||
|
double volatile const *pd_cv;
|
||||||
|
|
||||||
|
} gint_callback_arg_t;
|
||||||
|
|
||||||
|
/* gint_callback_t: Callback function with up to 4 register arguments */
|
||||||
|
typedef struct {
|
||||||
|
void *function;
|
||||||
|
gint_callback_arg_t args[4];
|
||||||
|
} gint_callback_t;
|
||||||
|
|
||||||
|
/* GINT_CB(): Build a callback object from function and arguments
|
||||||
|
|
||||||
|
This macro builds a callback object (of type gint_callback_t). Callback
|
||||||
|
objects are used in various APIs (timers, RTC, DMA, USB...) to notify the
|
||||||
|
program of events that are caused by the hardware instead of the program.
|
||||||
|
|
||||||
|
Callbacks are often called asynchronously, which means that the function
|
||||||
|
setting up the callback finishes first, and then the callback is called
|
||||||
|
later while some other part of the program is running. This is tricky,
|
||||||
|
because in order to invoke the callback:
|
||||||
|
|
||||||
|
* The code and arguments must still exist, even though the function that
|
||||||
|
provided them has finished long ago;
|
||||||
|
* The call ABI is lost as soon as we store parameters instead of
|
||||||
|
syntactically performing a call in the code.
|
||||||
|
|
||||||
|
For the first issue, the caller has to make sure that every pointer that is
|
||||||
|
passed to the callback will still be valid when the callback is invoked; in
|
||||||
|
particular, pointers to variables on the stack can ony be used if the
|
||||||
|
callback is guaranteed to be called before the function ends (eg. if there
|
||||||
|
is a synchronous wait in the function).
|
||||||
|
|
||||||
|
For the second issue, gint's callback mechanism guarantees ABI compatibility
|
||||||
|
by restricting the arguments that can be passed to the callback.
|
||||||
|
|
||||||
|
* Only arguments that fit into registers can be passed. In practice, this
|
||||||
|
mostly excludes 64-bit integer, double floating-point values, and custom
|
||||||
|
structures. This way, there is a somewhat solid guarantee that the
|
||||||
|
callback function will take arguments in r4...r7.
|
||||||
|
* Only up to 4 arguments can be passed.
|
||||||
|
* Only values of the types listed in gint_callback_arg_t can be passed.
|
||||||
|
|
||||||
|
If you need to work around one of these limitations, pass a pointer to a
|
||||||
|
structure containing your arguments (if the callback is invoked after the
|
||||||
|
current function ends, make the structure static or global).
|
||||||
|
|
||||||
|
If you need to pass a char or a short, cast to an int and have the callback
|
||||||
|
function take an int. If you need to pass a pointer to a type not listed in
|
||||||
|
gint_callback_arg_t (such as a structure), cast it to (void *); the callback
|
||||||
|
function can still take a pointer to the custom type as argument.
|
||||||
|
|
||||||
|
If the conditions for the callback to work are not met, the compiler will
|
||||||
|
emit on of these two errors:
|
||||||
|
|
||||||
|
* error: static assertion failed: "GINT_CB: too many arguments (maximum 4)"
|
||||||
|
-> This is emitted if you have more than 4 arguments.
|
||||||
|
* error: cast to union type from type not present in union
|
||||||
|
-> This is emitted if you pass a parameter of an invalid type.
|
||||||
|
|
||||||
|
Both are followed with a series of compiler notes mentioning the various
|
||||||
|
macros defined below. */
|
||||||
|
#define GINT_CB(func, ...) \
|
||||||
|
((gint_callback_t){ .function = func, .args = { \
|
||||||
|
__VA_OPT__(GINT_CB_ARGS1(__VA_ARGS__)) \
|
||||||
|
}})
|
||||||
|
#define GINT_CB_ARGS1(a1, ...) \
|
||||||
|
(gint_callback_arg_t)(a1), __VA_OPT__(GINT_CB_ARGS2(__VA_ARGS__))
|
||||||
|
#define GINT_CB_ARGS2(a2, ...) \
|
||||||
|
(gint_callback_arg_t)(a2), __VA_OPT__(GINT_CB_ARGS3(__VA_ARGS__))
|
||||||
|
#define GINT_CB_ARGS3(a3, ...) \
|
||||||
|
(gint_callback_arg_t)(a3), __VA_OPT__(GINT_CB_ARGS4(__VA_ARGS__))
|
||||||
|
#define GINT_CB_ARGS4(a4, ...) \
|
||||||
|
({ __VA_OPT__(_Static_assert(0, \
|
||||||
|
"GINT_CB: too many arguments (maximum 4)");) \
|
||||||
|
(gint_callback_arg_t)(a4); })
|
||||||
|
|
||||||
|
/* GINT_CB_NULL: Empty callback */
|
||||||
|
#define GINT_CB_NULL ((gint_callback_t){ .function = NULL, .args = {} })
|
||||||
|
|
||||||
|
/* GINT_CB_SET(): Callback that sets an integer to 1
|
||||||
|
This is defined as a function to make sure the pointer is to an int. */
|
||||||
|
static void GINT_CB_SET_function(int volatile *pointer)
|
||||||
|
{
|
||||||
|
(*pointer) = 1;
|
||||||
|
}
|
||||||
|
static GINLINE gint_callback_t GINT_CB_SET(int volatile *pointer)
|
||||||
|
{
|
||||||
|
return GINT_CB(GINT_CB_SET_function, pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GINT_CB_INC(): Callback that increments an integer */
|
||||||
|
static void GINT_CB_INC_function(int volatile *pointer)
|
||||||
|
{
|
||||||
|
(*pointer)++;
|
||||||
|
}
|
||||||
|
static GINLINE gint_callback_t GINT_CB_INC(int volatile *pointer)
|
||||||
|
{
|
||||||
|
return GINT_CB(GINT_CB_INC_function, pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* gint_callback_invoke(): Invoke a callback function */
|
||||||
|
static GINLINE int gint_callback_invoke(gint_callback_t cb)
|
||||||
|
{
|
||||||
|
int (*f)(int r4, int r5, int r6, int r7) = cb.function;
|
||||||
|
return f(cb.args[0].i, cb.args[1].i, cb.args[2].i, cb.args[3].i);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* GINT_GINT */
|
#endif /* GINT_GINT */
|
||||||
|
|
Loading…
Reference in a new issue