From 0f9fefdfa8adc711290fa202060b3629aae574b8 Mon Sep 17 00:00:00 2001 From: arighi Date: Fri, 1 Oct 2010 17:32:17 +0000 Subject: [PATCH] mware: add generic completion events 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: --------------------------------------------------------------------- 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 a generic completion event */ event_initGeneric(&e); /* Submit the hardware initialization request */ async_hw_init(); /* Wait for the completion of the event */ event_wait(&e); } --------------------------------------------------------------------- git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4392 38d2e660-2303-0410-9eaa-f027e97ec537 --- bertos/mware/event.c | 16 ++++++++- bertos/mware/event.h | 80 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/bertos/mware/event.c b/bertos/mware/event.c index 2d8e2052..24d73d93 100644 --- a/bertos/mware/event.c +++ b/bertos/mware/event.c @@ -38,12 +38,14 @@ #include "event.h" +#include "cfg/cfg_signal.h" +#include "cfg/cfg_timer.h" void event_hook_ignore(UNUSED_ARG(Event *, e)) { } -#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS +#if CONFIG_KERN_SIGNALS void event_hook_signal(Event *e) { sig_post((e)->Ev.Sig.sig_proc, (e)->Ev.Sig.sig_bit); @@ -54,3 +56,15 @@ void event_hook_softint(Event *e) { e->Ev.Int.func(e->Ev.Int.user_data); } + +void event_hook_generic(Event *e) +{ + e->Ev.Gen.completed = true; +} + +#if CONFIG_TIMER_EVENTS +void event_hook_generic_timeout(Event *e) +{ + e->Ev.Gen.completed = true; +} +#endif diff --git a/bertos/mware/event.h b/bertos/mware/event.h index 86ec620f..fcdb90fb 100644 --- a/bertos/mware/event.h +++ b/bertos/mware/event.h @@ -44,6 +44,9 @@ #include #include "cfg/cfg_proc.h" #include "cfg/cfg_signal.h" +#include "cfg/cfg_timer.h" + +#include /* cpu_relax() */ #if CONFIG_KERN #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS @@ -75,12 +78,19 @@ 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_timeout(Event *event); /** Initialize the event \a e as a no-op */ #define event_initNone(e) \ @@ -127,6 +137,76 @@ 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) +#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. + */ +INLINE void event_wait(Event *e) +{ +#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS + sig_wait(e->Ev.Sig.sig_bit); +#else + while (ACCESS_SAFE(e->Ev.Gen.completed) == false) + cpu_relax(); +#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. + */ +INLINE bool event_waitTimeout(Event *e, ticks_t timeout) +{ +#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS + return (sig_waitTimeout(e->Ev.Sig.sig_bit, timeout) & SIG_TIMEOUT) ? + false : true; +#else + ticks_t end = timer_clock() + timeout; + + while ((ACCESS_SAFE(e->Ev.Gen.completed) == false) || + TIMER_AFTER(timer_clock(), end)) + cpu_relax(); + + return e->Ev.Gen.completed; +#endif +} +#endif /* CONFIG_TIMER_EVENTS */ + /** Trigger an event */ INLINE void event_do(struct Event *e) { -- 2.25.1