X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=bertos%2Fmware%2Fevent.h;h=0111ef8cbfa37fbf866f11c0976fa7f3ecbc14f6;hb=446aae6172b8e362264b6a3d08f7b61fb23898a9;hp=fcdb90fbcc986db760725dc279464ed7d4d3ddea;hpb=0f9fefdfa8adc711290fa202060b3629aae574b8;p=bertos.git diff --git a/bertos/mware/event.h b/bertos/mware/event.h index fcdb90fb..0111ef8c 100644 --- a/bertos/mware/event.h +++ b/bertos/mware/event.h @@ -30,11 +30,63 @@ * 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. * + * + * 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 + * * \author Bernie Innocenti */ @@ -57,7 +109,6 @@ struct Process; #endif - /// User defined callback type typedef void (*Hook)(void *); @@ -72,6 +123,12 @@ typedef struct Event struct Process *sig_proc; /* Process to be signalled */ sigbit_t sig_bit; /* Signal to send */ } Sig; + + struct + { + struct Process *sig_proc; /* Process to be signalled */ + Signal sig; /* Signal structure */ + } SigGen; #endif struct { @@ -90,7 +147,7 @@ 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_timeout(Event *event); +void event_hook_generic_signal(Event *event); /** Initialize the event \a e as a no-op */ #define event_initNone(e) \ @@ -137,24 +194,22 @@ INLINE Event event_createSignal(struct Process *proc, sigbit_t bit) #endif -/** - * Prevent the compiler from optimizing access to the variable \a x, enforcing - * a refetch from memory. This also forbid from reordering successing instances - * of ACCESS_SAFE(). - * - * TODO: move this to cfg/compiler.h - */ -#define ACCESS_SAFE(x) (*(volatile typeof(x) *)&(x)) - #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS /** Initialize the generic sleepable event \a e */ -#define event_initGeneric(e) \ - event_initSignal(e, proc_current(), SIG_SINGLE) +#define event_initGeneric(e) \ + ((e)->action = event_hook_generic_signal, \ + (e)->Ev.SigGen.sig_proc = proc_current(), \ + (e)->Ev.SigGen.sig.wait = 0, (e)->Ev.SigGen.sig.recv = 0) #else #define event_initGeneric(e) \ ((e)->action = event_hook_generic, (e)->Ev.Gen.completed = false) #endif +/** + * Signal used to implement generic events. + */ +#define EVENT_GENERIC_SIGNAL SIG_SYSTEM5 + /** * Create a generic sleepable event. * @@ -169,31 +224,40 @@ INLINE Event event_createGeneric(void) /** * 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 defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS - sig_wait(e->Ev.Sig.sig_bit); + e->Ev.Sig.sig_proc = proc_current(); + sig_waitSignal(&e->Ev.SigGen.sig, EVENT_GENERIC_SIGNAL); #else while (ACCESS_SAFE(e->Ev.Gen.completed) == false) cpu_relax(); + e->Ev.Gen.completed = false; + MEMORY_BARRIER; #endif } #if CONFIG_TIMER_EVENTS #include /* timer_clock() */ -/* TODO: move these macros to drv/timer.h */ -#define TIMER_AFTER(x, y) ((long)(y) - (long)(x) < 0) -#define TIMER_BEFORE(x, y) TIMER_AFTER(y, x) - /** * Wait the completion of event \a e or \a timeout elapses. + * + * \note It's forbidden to use this function inside irq handling functions. */ INLINE bool event_waitTimeout(Event *e, ticks_t timeout) { + bool ret; + #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS - return (sig_waitTimeout(e->Ev.Sig.sig_bit, timeout) & SIG_TIMEOUT) ? + e->Ev.Sig.sig_proc = proc_current(); + ret = (sig_waitTimeoutSignal(&e->Ev.SigGen.sig, + EVENT_GENERIC_SIGNAL, timeout) & SIG_TIMEOUT) ? false : true; #else ticks_t end = timer_clock() + timeout; @@ -201,16 +265,27 @@ INLINE bool event_waitTimeout(Event *e, ticks_t timeout) while ((ACCESS_SAFE(e->Ev.Gen.completed) == false) || TIMER_AFTER(timer_clock(), end)) cpu_relax(); - - return e->Ev.Gen.completed; + ret = e->Ev.Gen.completed; + e->Ev.Gen.completed = false; #endif + MEMORY_BARRIER; + return ret; } #endif /* CONFIG_TIMER_EVENTS */ -/** Trigger an event */ +/** + * 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 */