X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=bertos%2Fmware%2Fevent.h;h=6f88676346f2e70c1af213529652443fff12fe7f;hb=088cc866a57a0402f6ba3f232fcf8f59c79626d1;hp=5fa996c684222c5654984d26522a4e68941bde82;hpb=d62963b4a64efe8d2917f489fefaf586a9a99126;p=bertos.git diff --git a/bertos/mware/event.h b/bertos/mware/event.h index 5fa996c6..6f886763 100644 --- a/bertos/mware/event.h +++ b/bertos/mware/event.h @@ -27,49 +27,142 @@ * the GNU General Public License. * * Copyright 2003, 2004, 2005 Develer S.r.l. (http://www.develer.com/) - * Copyright 1999, 2001, 2003 Bernardo Innocenti - * + * Copyright 1999, 2001, 2003 Bernie Innocenti * --> * + * \defgroup event_handling Event handling module + * \ingroup core + * \{ + * * \brief Events handling * * This module implements a common system for executing * a user defined action calling a hook function. * - * \version $Id$ * - * \author Bernardo Innocenti + * Device drivers often need to wait the completion of some event, usually to + * allow the hardware to accomplish some asynchronous task. + * + * A common approach is to place a busy wait with a cpu_relax() loop that invokes + * the architecture-specific instructions to say that we're not doing much with + * the processor. + * + * Although technically correct, the busy loop degrades the overall system + * performance in presence of multiple processes and power consumption. + * + * With the kernel the natural way to implement such wait/complete mechanism is to + * use signals via sig_wait() and sig_post()/sig_send(). + * + * However, signals in BeRTOS are only available in presence of the kernel (that + * is just a compile-time option). This means that each device driver must provide + * two different interfaces to implement the wait/complete semantic: one with the + * kernel and another without the kernel. + * + * The purpose of the completion events is to provide a generic interface to + * implement a synchronization mechanism to block the execution of code until a + * specific event happens. + * + * This interface does not depend on the presence of the kernel and it + * automatically uses the appropriate event backend to provide the same + * behaviour with or without the kernel. + * + * Example usage (wait for a generic device driver initialization): + * \code + * static Event e; + * + * static void irq_handler(void) + * { + * // Completion event has happened, resume the execution of init() + * event_do(&e); + * } + * + * static void init(void) + * { + * // Declare the generic completion event + * event_initGeneric(&e); + * // Submit the hardware initialization request + * async_hw_init(); + * // Wait for the completion of the event + * event_wait(&e); + * } + * \endcode + * + * Example usage: wait multiple generic events via event_select() + * \code + * Event ev1; + * Event ev2; + * + * void event_notifier(void) + * { + * Event *evs[] = { &ev1, &ev2 }; + * + * event_initGeneric(&ev1); + * event_initGeneric(&ev2); + * + * while (1) + * { + * int id = event_select(evs, countof(evs), + * ms_to_ticks(100)); + * if (id < 0) + * { + * kprintf("no IRQ\n"); + * continue; + * } + * kprintf("IRQ %d happened\n", id); + * } + * } + * + * void irq1_handler(void) + * { + * // do something + * ... + * + * // notify the completion of event 1 + * event_do(&ev1); + * } + * + * void irq2_handler(void) + * { + * // do something + * ... + * + * // notify the completion of event 2 + * event_do(&ev2); + * } + * \endcode + * + * \author Bernie Innocenti + * + * $WIZ$ module_name = "event" */ #ifndef KERN_EVENT_H #define KERN_EVENT_H +#include "cfg/cfg_proc.h" +#include "cfg/cfg_signal.h" +#include "cfg/cfg_timer.h" #include -#include -#if CONFIG_KERNEL - #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS - #include - #endif +#include /* cpu_relax() */ - /* Forward decl */ - struct Process; +#if CONFIG_KERN && CONFIG_KERN_SIGNALS +#include +/* Forward decl */ +struct Process; #endif - -/// User defined callback type -typedef void (*Hook)(void *); - typedef struct Event { void (*action)(struct Event *); union { -#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS +#if CONFIG_KERN && CONFIG_KERN_SIGNALS struct { struct Process *sig_proc; /* Process to be signalled */ sigbit_t sig_bit; /* Signal to send */ + Signal sig; /* Local signal structure (used by generic event) */ } Sig; #endif struct @@ -77,19 +170,25 @@ typedef struct Event Hook func; /* Pointer to softint hook */ void *user_data; /* Data to be passed back to user hook */ } Int; + + struct + { + bool completed; /* Generic event completion */ + } Gen; } Ev; } Event; void event_hook_ignore(Event *event); void event_hook_signal(Event *event); void event_hook_softint(Event *event); +void event_hook_generic(Event *event); +void event_hook_generic_signal(Event *event); /** Initialize the event \a e as a no-op */ #define event_initNone(e) \ ((e)->action = event_hook_ignore) /** Same as event_initNone(), but returns the initialized event */ -INLINE Event event_createNone(void); INLINE Event event_createNone(void) { Event e; @@ -98,11 +197,11 @@ INLINE Event event_createNone(void) } /** Initialize the event \a e with a software interrupt (call function \a f, with parameter \a u) */ -#define event_initSoftInt(e,f,u) \ +#define event_initSoftint(e,f,u) \ ((e)->action = event_hook_softint,(e)->Ev.Int.func = (f), (e)->Ev.Int.user_data = (u)) -/** Same as event_initSoftInt(), but returns the initialized event */ -INLINE Event event_createSoftInt(Hook func, void *user_data) +/** Same as event_initSoftint(), but returns the initialized event */ +INLINE Event event_createSoftint(Hook func, void *user_data) { Event e; e.action = event_hook_softint; @@ -111,9 +210,7 @@ INLINE Event event_createSoftInt(Hook func, void *user_data) return e; } - -#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS - +#if CONFIG_KERN && CONFIG_KERN_SIGNALS /** Initialize the event \a e with a signal (send signal \a s to process \a p) */ #define event_initSignal(e,p,s) \ ((e)->action = event_hook_signal,(e)->Ev.Sig.sig_proc = (p), (e)->Ev.Sig.sig_bit = (s)) @@ -128,12 +225,88 @@ INLINE Event event_createSignal(struct Process *proc, sigbit_t bit) return e; } +/** + * Signal used to implement generic events. + */ +#define EVENT_GENERIC_SIGNAL SIG_SYSTEM6 + +/** Initialize the generic sleepable event \a e */ +#define event_initGeneric(e) \ + ((e)->action = event_hook_generic_signal, \ + (e)->Ev.Sig.sig_proc = proc_current(), \ + (e)->Ev.Sig.sig_bit = EVENT_GENERIC_SIGNAL, \ + (e)->Ev.Sig.sig.wait = 0, (e)->Ev.Sig.sig.recv = 0) +#else +#define event_initGeneric(e) \ + ((e)->action = event_hook_generic, (e)->Ev.Gen.completed = false) +#endif + +/** + * Create a generic sleepable event. + * + * \return the properly initialized generic event structure. + */ +INLINE Event event_createGeneric(void) +{ + Event e; + event_initGeneric(&e); + return e; +} + +/** + * Wait the completion of event \a e. + * + * This function releases the CPU the application is configured to use + * the kernel, otherwise it's just a busy wait. + * \note It's forbidden to use this function inside irq handling functions. + */ +INLINE void event_wait(Event *e) +{ +#if CONFIG_KERN_SIGNALS + e->Ev.Sig.sig_proc = proc_current(); + sig_waitSignal(&e->Ev.Sig.sig, EVENT_GENERIC_SIGNAL); +#else + while (ACCESS_SAFE(e->Ev.Gen.completed) == false) + cpu_relax(); + e->Ev.Gen.completed = false; + MEMORY_BARRIER; #endif +} -/** Trigger an event */ +/** + * Wait for multiple events + * + * On success return the offset in the \a evs vector of the Event that + * happened, -1 if the timeout expires. + * + * NOTE: timeout == 0 means no timeout. + * + * \attention The API is work in progress and may change in future versions. + */ +int event_select(Event **evs, int n, ticks_t timeout); + +/** + * Wait the completion of event \a e or \a timeout elapses. + * + * \return true if the event triggered, false if timed out + * + * \note It's forbidden to use this function inside irq handling functions. + */ +bool event_waitTimeout(Event *e, ticks_t timeout); + +/** + * Trigger an event. + * + * Execute the callback function associated with event \a e. + * + * This function can be used also in interrupt routines, but only if the + * event was created as a signal or generic event. + */ INLINE void event_do(struct Event *e) { e->action(e); } +/** \} */ + #endif /* KERN_EVENT_H */