mware: add generic completion events
authorarighi <arighi@38d2e660-2303-0410-9eaa-f027e97ec537>
Fri, 1 Oct 2010 17:32:17 +0000 (17:32 +0000)
committerarighi <arighi@38d2e660-2303-0410-9eaa-f027e97ec537>
Fri, 1 Oct 2010 17:32:17 +0000 (17:32 +0000)
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
bertos/mware/event.h

index 2d8e2052bcd80b96fed9d5f447cb9434e28cf227..24d73d93f7f678371b2e4b3e17ef346d9d3c616a 100644 (file)
 
 
 #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
index 86ec620fc2a179c2be6cf4a3e352f91581472a2b..fcdb90fbcc986db760725dc279464ed7d4d3ddea 100644 (file)
@@ -44,6 +44,9 @@
 #include <cfg/compiler.h>
 #include "cfg/cfg_proc.h"
 #include "cfg/cfg_signal.h"
+#include "cfg/cfg_timer.h"
+
+#include <cpu/power.h> /* 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 <drv/timer.h> /* 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)
 {