blob: e06196e936b7c8c56cc7a373429532354ffcb2d5 [file] [log] [blame]
/**
* \defgroup ek The Contiki event kernel
* @{
*
* At the heart of the Contiki desktop environment is the event driven
* Contiki kernel. Using non-preemptive multitasking, the Contiki
* event kernel makes it possible to run several programs in
* parallel. It also provides message passing mechanisms to the
* running programs.
*
* The Contiki kernel is a simple event driven dispatcher which
* handles processes and events. All code execution is
* initiated by the kernel, and applications are implemented as C
* functions that must return within a short time after being
* called.
*
* The kernel does not provide multi-threading. Rather, this is
* implemented as an application library. For threads, see the
* \ref mt "Multithreading library" and \ref pt "Protothreads".
*
* The kernel is the initiator of all program execution in
* Contiki. After the system has been initialized by the boot up code,
* the ek_run() function is called. This function never
* returns, but will sit in a loop in which it does two things.
*
* - Pulls the first event of the event queue and dispatches this to
* all listening processes (ek_process_event()).
*
* - Executes the "poll" handlers of all processes that have
* registered (ek_process_poll()).
*
* Only one event is processes at a time, and the poll handlers of
* all processes are called between two events are handled.
*
*
* A process is defined by an initialization function, a event
* handler, a uIP event handler, and an poll handler. The event
* handler is called when a event has been posted, for which the
* process is currently listening. The uIP event handler is called
* when the uIP TCP/IP stack has an event to deliver to the
* process. Such events can be that new data has arrived on a
* connection, that previously sent data has been acknowledged or that
* a connection has been closed. The poll handler is periodically
* called by the system.
*
* A process is started by calling the ek_start()
* function. This function must be called by the initialization
* function before any other kernel function is called. When the
* function returns, the new process is running.
*
* The initialization function is declared with the special
* LOADER_INIT() macro. The initializaition function takes a single
* argument; a char * pointer.
*
* The function ek_exit() is used to tell the kernel that
* a process has exited. This function must be called by the process
* itself, and must be called the process unloads itself.
*
* \note It is not possible to call ek_exit() on behalf of
* another process - instead, post the event ek_event_quit
* with the process as a receiver. The other process should then
* listen for this event, and call ek_exit() when the event
* is received.
*
*
* The kernel can pass events between different
* processes. Events are simple messages that consist of a event
* number and a generic data pointer called the event data. The
* event data can be used to pass messages between processes. In
* order for a event to be delivered to a process, the process must
* be listening for the event number.
*
* If a process has registered an poll handler, the kernel will
* call it as often as possible. The poll handler can be used to
* implement timer based functionality (by checking the ek_clock()
* function), or other background processing. The poll handler must
* return to the caller within a short time, or otherwise the system
* will become sluggish.
*
*
*/
/**
* \file
* Event kernel.
* \author Adam Dunkels <adam@dunkels.com>
*
* The kernel in Contiki handles processes and events. All process
* execution is initiated by the kernel.
*/
/*
* Copyright (c) 2002-2003, Adam Dunkels.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This file is part of the "ek" event kernel.
*
* $Id: ek.c,v 1.9 2007/06/02 07:32:05 ryohji Exp $
*
*/
#include "ek.h"
#include <string.h> /* for strncmp() */
/**
* \internal Pointer to the currently running process structure.
*
*/
struct ek_proc *ek_procs;
struct ek_proc *ek_proclist[EK_CONF_MAXPROCS];
struct ek_proc *ek_current;
ek_event_t ek_event_quit;
ek_event_t ek_event_msg;
static ek_event_t lastevent;
#if CC_FUNCTION_POINTER_ARGS
#else /* CC_FUNCTION_POINTER_ARGS */
ek_event_t ek_eventhandler_s;
ek_data_t ek_eventhandler_data;
#endif /* CC_FUNCTION_POINTER_ARGS */
/**
* \internal Structure used for keeping the queue of active events.
*/
struct event_data {
ek_event_t s;
ek_data_t data;
ek_id_t id;
};
static ek_num_events_t nevents, fevent;
static struct event_data events[EK_CONF_NUMEVENTS];
volatile unsigned char ek_poll_request;
/*-----------------------------------------------------------------------------------*/
/**
* Allocates a event number.
*
* \return The allocated event number or EK_EVENT_NONE if no event
* number could be allocated.
*/
/*-----------------------------------------------------------------------------------*/
ek_event_t
ek_alloc_event(void)
{
return lastevent++;
}
/*-----------------------------------------------------------------------------------*/
static void
procs_add(struct ek_proc *p)
{
static struct ek_proc *q, *r;
/* The process should be placed on the process list according to the
process' priority. The higher the priority, the earlier on the
list. */
r = NULL;
for(q = ek_procs; q != NULL; q = q->next) {
if(q->prio < p->prio) {
if(r) {
r->next = p;
} else {
ek_procs = p;
}
p->next = q;
return;
}
r = q;
}
if(q == NULL) {
if(r == NULL) {
p->next = ek_procs;
ek_procs = p;
} else {
r->next = p;
p->next = NULL;
}
}
}
/*-----------------------------------------------------------------------------------*/
/**
* Starts a new process.
*
* Is called by a program in order to start a new process for the
* program. This function should be called quite early in the
* initialization procedure of a new process. In partcular, it must be
* called before any other dispatcher functions, or functions of other
* modules that make use of dispatcher functions. Most CTK functions
* call dispatcher functions, and should therefore not be called
* before ek_start() is called.
*
* Example:
\code
static void app_poll(void);
static EK_EVENTHANDLER(app_eventhandler, s, data);
static struct ek_proc p =
{EK_PROC("Generic applications", app_poll, app_eventhandler, NULL)};
static ek_id_t id = EK_ID_NONE;
LOADER_INIT_FUNC(app_init, arg)
{
arg_free(arg);
if(id == EK_ID_NONE) {
id = ek_start(&p);
rest_of_initialization();
}
}
\endcode
*
* \param p A pointer to a ek_proc struct that must be found
* in the process own memory space.
*
* \return The process identifier for the new process or EK_ID_NONE
* if the process could not be started.
*/
/*-----------------------------------------------------------------------------------*/
ek_id_t
ek_start(CC_REGISTER_ARG struct ek_proc *p)
{
ek_id_t id;
for(id = 0; id < EK_CONF_MAXPROCS; ++id) {
if(ek_proclist[id] == NULL) {
break;
}
}
if(id == EK_CONF_MAXPROCS) {
return EK_ID_NONE;
}
ek_proclist[id] = p;
/* Put on the procs list.*/
procs_add(p);
p->id = id;
/* Post an asynchronous event to the process. */
ek_post(id, EK_EVENT_INIT, p);
return id;
}
/*-----------------------------------------------------------------------------------*/
/**
* Exit the currently running process
*
* This function causes the currently running process to exit. The
* function must be called by the process before it unloads itself, or
* the system will crash.
*
*/
/*-----------------------------------------------------------------------------------*/
void
ek_exit(void)
{
register struct ek_proc *q, *p;
p = ek_current;
/* Post a synchronous event to all processes to inform them that
this process is about to exit. This will allow services to
dealloc state associated with this process. */
for(q = ek_procs; q != NULL; q = q->next) {
if(p != q) {
ek_current = q;
if(q->eventhandler != NULL) {
q->eventhandler(EK_EVENT_EXITED, (ek_data_t)p->id);
}
}
}
/* Remove process from the process lists. */
ek_proclist[p->id] = NULL;
if(p == ek_procs) {
ek_procs = ek_procs->next;
} else {
for(q = ek_procs; q != NULL; q = q->next) {
if(q->next == p) {
q->next = p->next;
break;
}
}
}
ek_current = NULL;
}
/**
* \addtogroup kernel
* @{
*/
/*-----------------------------------------------------------------------------------*/
/**
* Finds the process structure for a specific process ID.
*
* \param id The process ID for the process.
*
* \return The process structure for the process, or NULL if there
* process ID was not found.
*/
/*-----------------------------------------------------------------------------------*/
struct ek_proc *
ek_process(ek_id_t id)
{
struct ek_proc *p;
for(p = ek_procs; p != NULL; p = p->next) {
if(p->id == id) {
return p;
}
}
return NULL;
}
/*-----------------------------------------------------------------------------------*/
/**
* Initializes the dispatcher module.
*
* Must be called during the initialization of Contiki.
*
*/
/*-----------------------------------------------------------------------------------*/
void
ek_init(void)
{
int i;
lastevent = EK_EVENT_MAX;
nevents = fevent = 0;
ek_current = ek_procs = NULL;
arg_init();
for(i = 0; i < EK_CONF_MAXPROCS; ++i) {
ek_proclist[i] = NULL;
}
}
/*-----------------------------------------------------------------------------------*/
/**
* Process the next event in the event queue and deliver it to
* listening processes.
*
*/
/*-----------------------------------------------------------------------------------*/
void
ek_process_event(void)
{
static ek_event_t s;
static ek_data_t data;
static ek_id_t id;
static struct ek_proc *p;
/* If there are any events in the queue, take the first one and
walk through the list of processes to see if the event should be
delivered to any of them. If so, we call the event handler
function for the process. We only process one event at a time
and call the poll handlers inbetween. */
if(nevents > 0) {
/* There are events that we should deliver. */
s = events[fevent].s;
data = events[fevent].data;
id = events[fevent].id;
/* Since we have seen the new event, we move pointer upwards
and decrese number. */
fevent = (fevent + 1) % EK_CONF_NUMEVENTS;
--nevents;
/* If this is a broadcast event, we deliver it to all events, in
order of their priority. */
if(id == EK_BROADCAST) {
for(p = ek_procs; p != NULL; p = p->next) {
if(ek_poll_request) {
ek_poll_request = 0;
ek_process_poll();
}
ek_current = p;
if(p->eventhandler != NULL) {
p->eventhandler(s, data);
}
}
} else {
/* This is not a broadcast event, so we deliver it to the
specified process. */
if(ek_poll_request) {
ek_poll_request = 0;
ek_process_poll();
}
p = ek_proclist[id];
if(p != NULL &&
p->eventhandler != NULL) {
ek_current = p;
p->eventhandler(s, data);
/* If the event was an INIT event, we should also put the
process on the process list. */
/* procs_add(p);*/
}
}
}
}
/*-----------------------------------------------------------------------------------*/
/**
* Call each process' poll handler.
*/
/*-----------------------------------------------------------------------------------*/
void
ek_process_poll(void)
{
struct ek_proc *p;
/* Call poll handlers. */
for(p = ek_procs; p != NULL; p = p->next) {
if(ek_poll_request) {
ek_poll_request = 0;
p = ek_procs;
}
if(p->pollhandler != NULL) {
ek_current = p;
p->pollhandler();
}
}
}
/*-----------------------------------------------------------------------------------*/
/**
* Run the system once - call poll handlers and process one event.
*
* This function should be called repeatedly from the main() program
* to actuall run the Contiki system. It calls the necessary poll
* handlers, and processes one event. The function returns the number
* of events that are waiting in the event queue so that the caller
* may choose to put the CPU to sleep when there are no pending
* events.
*
* \return The number of events that are currently waiting in the
* event queue.
*/
/*-----------------------------------------------------------------------------------*/
int
ek_run(void)
{
/* Process "poll" events. */
do {
ek_poll_request = 0;
ek_process_poll();
} while(ek_poll_request != 0);
/* Process one event */
ek_process_event();
return nevents;
}
/*-----------------------------------------------------------------------------------*/
/**
* Post an asynchronous event.
*
* This function posts an asynchronous event to one or more
* processes. The handing of the event is deferred until the target
* process is scheduled by the kernel. An event can be broadcast to
* all processes, in which case all processes in the system will be
* scheduled to handle the event.
*
* \param s The event to be posted.
*
* \param data The auxillary data to be sent with the event
*
* \param id The process ID to which the event should be posted, or
* EK_BROADCAST if the event should be posted to all
* processes.
*
* \retval EK_ERR_OK The event could be posted.
*
* \retval EK_ERR_FULL The event queue was full and the event could
* not be posted.
*/
/*-----------------------------------------------------------------------------------*/
ek_err_t
ek_post(ek_id_t id, ek_event_t s, ek_data_t data)
{
static unsigned char snum;
if(nevents == EK_CONF_NUMEVENTS) {
return EK_ERR_FULL;
}
snum = (fevent + nevents) % EK_CONF_NUMEVENTS;
events[snum].s = s;
events[snum].data = data;
events[snum].id = id;
++nevents;
return EK_ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
void
ek_post_synch(ek_id_t id, ek_event_t ev, ek_data_t data)
{
struct ek_proc *p = ek_current;
ek_current = ek_proclist[id];
ek_current->eventhandler(ev, data);
ek_current = p;
}
/*-----------------------------------------------------------------------------------*/
ek_id_t
ek_find(const char *prefix)
{
struct ek_proc *p;
unsigned short len;
/* Search through all processes and search for the specified process
name. */
len = strlen(prefix);
for(p = ek_procs; p != NULL; p = p->next) {
if(strncmp(prefix, p->name, len) == 0) {
return p->id;
}
}
return EK_ID_NONE;
}
/*-----------------------------------------------------------------------------------*/
void
ek_replace(struct ek_proc *newp, void *arg)
{
register struct ek_proc *p = ek_current;
/* Remove the currently executing process. */
ek_exit();
ek_proclist[p->id] = newp;
/* Put on the procs list.*/
procs_add(newp);
newp->id = p->id;
/* Post an asynchronous event to the process. */
ek_post(p->id, EK_EVENT_REPLACE, arg);
}
/*-----------------------------------------------------------------------------------*/
void *
ek_procstate(ek_id_t id)
{
struct ek_proc *p;
p = ek_proclist[id];
if(p == NULL) {
return NULL;
}
return p->procstate;
}
/*-----------------------------------------------------------------------------------*/
/** @} */