blob: 12a22398a4f09751b684ed68709db9791c6bae31 [file] [log] [blame]
/**
* \file
* Event kernel, signal dispatcher and handler of uIP events.
* \author Adam Dunkels <adam@dunkels.com>
*
* The Dispatcher module is the event kernel in Contiki and handles
* processes, signals and uIP events. All process execution is
* initiated by the Dispatcher.
*
*
*
* The Dispatcher is the initiator of all program execution in
* Contiki. After the system has been initialized by the boot up code,
* the dispatcher_run() function is called. This function never
* returns, but will set in a loop in which it does two things.
*
* - Pulls the first signal of the signal queue and dispatches this to
* all listening processes.
*
* - Executes the "idle" handlers of all processes that have
* registered.
*
* Only one signal is processes at a time, and the idle handlers of
* all processes are called between two signals are handled.
*
*
*
* A process is defined by a signal handler, a uIP event handler, and
* an idle handler. The signal handler is called when a signal has
* been emitted, 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 idle handler
* is periodically called by the system.
*
* \note The name "idle handler" is a misnomer, since the idle handler
* will be called even though the system is not idle.
*
* A process is started by calling the dispatcher_start()
* function. This function must be called before any other Dispatcher
* function is called. When the function returns, the new process is
* running. The function dispatcher_exit() is used to tell the
* Dispatcher 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 dispatcher_exit() on behalf of
* another process - instead, emit the signal dispatcher_signal_quit
* with the process as a receiver. The other process should then
* listen for this signal, and call dispatcher_exit() when the signal
* is received.
*
*
*
* The Dispatcher can pass signals between different
* processes. Signals are simple messages that consist of a signal
* number and a generic data pointer called the signal data. The
* signal data can be used to pass messages between processes. In
* order for a signal to be delivered to a process, the process must
* be listening for the signal number.
*
* When a process is running, the function dispatcher_listen() can be
* called to register the process as a listener for a signal. When a
* signal for which the process has registered itself as a listener is
* emitted by another process, the process' signal handler will be
* invoked. The signal number and the signal data are passed as
* function parameters to the signal handler. The signal handler must
* check the signal number and do whatever it should do based on the
* value of the signal number.
*
* Every process listens to the dispatcher_signal_quit signal by
* default, and the signal handler function must check for this
* signal. If this signal is received, the process must do any
* necessary clean-ups (i.e., close open windows, deallocate allocated
* memory, etc.) call process_exit(), and call the LOADER_UNLOAD()
* function.
*
* \note It is not possible to unregister a listening signal.
*
*
*
* If a process has registered an idle handler, the Dispatcher will
* call it as often as possible. The idle handler can be used to
* implement timer based functionality (by checking the ek_clock()
* function), or other background processing. The idle handler must
* return to the caller within a short time, or otherwise the system
* will feel sluggish.
*
*
*
* The uIP TCP/IP stack will call the Dispatcher when a uIP event has
* occured. The Dispatcher will find the right process for which the
* event is intended and call the process' uIP handler function.
*/
/*
* 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: dispatcher.c,v 1.16 2003/08/31 22:20:20 adamdunkels Exp $
*
*/
#include "ek.h"
#include "dispatcher.h"
#include "uip.h"
#include "uip-signal.h"
/**
* \internal Holds the currently running process' ID
*
*/
ek_id_t dispatcher_current;
/**
* \internal Pointer to the currently running process structure.
*
*/
struct dispatcher_proc *dispatcher_procs;
static struct dispatcher_proc *curproc;
static ek_id_t ids = 1;
/**
* The "quit" signal.
*
* All processes listens to this signal by default, but each program
* must implement the signal handler for the signal by itself.
*/
ek_signal_t dispatcher_signal_quit;
static ek_signal_t lastsig = 1;
/**
* \internal Structure for holding a TCP port and a process ID.
*/
struct listenport {
u16_t port;
ek_id_t id;
};
static struct listenport listenports[UIP_LISTENPORTS];
#if CC_FUNCTION_POINTER_ARGS
#else /* CC_FUNCTION_POINTER_ARGS */
ek_signal_t dispatcher_sighandler_s;
ek_data_t dispatcher_sighandler_data;
void *dispatcher_uipcall_state;
#endif /* CC_FUNCTION_POINTER_ARGS */
/**
* \internal Structure used for keeping the queue of active signals.
*/
struct signal_data {
ek_signal_t s;
ek_data_t data;
ek_id_t id;
};
static ek_num_signals_t nsignals, fsignal;
static struct signal_data signals[EK_CONF_NUMSIGNALS];
/*-----------------------------------------------------------------------------------*/
/**
* Allocates a signal number.
*
* \return The allocated signal number or EK_SIGNAL_NONE if no signal
* number could be allocated.
*/
/*-----------------------------------------------------------------------------------*/
ek_signal_t
dispatcher_sigalloc(void)
{
return lastsig++;
}
/*-----------------------------------------------------------------------------------*/
/**
* Starts a new process.
*
* Is called by a program in order to start a new process for the
* program. It must be called before any other dispatcher or CTK
* functions.
*
* \param p A pointer to a dispatcher_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
dispatcher_start(CC_REGISTER_ARG struct dispatcher_proc *p)
{
ek_id_t id;
struct dispatcher_proc *q;
again:
do {
id = ids++;
} while(id == EK_ID_NONE);
/* Check if this ID is use. */
for(q = dispatcher_procs; q != NULL; q = q->next) {
if(id == q->id) {
if(id == EK_ID_NONE) {
/* We have tried all the available process IDs, and did not
find any free ones. So we return with an error. */
return EK_ID_NONE;
}
goto again;
}
}
/* Put first on the procs list.*/
p->next = dispatcher_procs;
dispatcher_procs = p;
p->id = id;
/* Make sure we know which processes we are running at the
moment. */
dispatcher_current = id;
curproc = p;
/* All processes must listen to the dispatcher_signal_quit
signal. */
dispatcher_listen(dispatcher_signal_quit);
return id;
}
/*-----------------------------------------------------------------------------------*/
/**
* Causes the process to exit.
*
* Must be called by the process itself before it unloads itself, or
* the system will crash.
*
* \param p A pointer to the process' dispatcher_proc struct that was
* started with dispatcher_start().
*/
/*-----------------------------------------------------------------------------------*/
void
dispatcher_exit(CC_REGISTER_ARG struct dispatcher_proc *p)
{
struct dispatcher_proc *q;
static unsigned char i;
struct listenport *l;
#ifdef WITH_UIP
/* If this process has any listening TCP ports, we remove them. */
l = listenports;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->id == p->id) {
uip_unlisten(l->port);
l->port = 0;
l->id = EK_ID_NONE;
}
++l;
}
#endif /* WITH_UIP */
/* Remove process from the process list. */
if(p == dispatcher_procs) {
dispatcher_procs = dispatcher_procs->next;
} else {
for(q = dispatcher_procs; q != NULL; q = q->next) {
if(q->next == p) {
q->next = p->next;
break;
}
}
}
dispatcher_current = EK_ID_NONE;
curproc = NULL;
}
/*-----------------------------------------------------------------------------------*/
/**
* The real-time clock.
*
* This function may be used by programs for timing.
*
* \return The current wall-clock time in an archicture specific time
* unit.
*/
/*-----------------------------------------------------------------------------------*/
ek_clock_t
ek_clock(void)
{
return clock();
}
/*-----------------------------------------------------------------------------------*/
/**
* Starts to connect to a remote host using TCP.
*
* This function should be called to connect to a remote host using
* the reliable TCP protocol. It is a wrapper to the uIP function
* uip_connect(), with the difference the dispatcher_connect() sends a
* uip_signal_poll signal to the TCP/IP driver which makes the
* connection request to go out immediately instead of being delayed
* for up to 0.5 seconds.
*
* \param ripaddr A pointer to a packed repressentation of the IP
* address of the host to which to connect.
*
* \param port The TCP port number on the remote host in host byte
* order.
*
* \return The connection identifier, or NULL if no connection
* identifier could be allocated.
*/
/*-----------------------------------------------------------------------------------*/
struct uip_conn *
dispatcher_connect(u16_t *ripaddr, u16_t port)
{
struct uip_conn *c;
c = uip_connect(ripaddr, port);
if(c == NULL) {
return NULL;
}
dispatcher_emit(uip_signal_poll, c, DISPATCHER_BROADCAST);
return c;
}
/*-----------------------------------------------------------------------------------*/
/**
* \internal The uIP callback function.
*
* This function is the uIP callback function and is called by the uIP
* code whenever a uIP event occurs. This funcion will find out to
* which process the event should be handed over to, and calls the
* appropriate process' uIP callback function if such a function has
* been registered.
*
*/
/*-----------------------------------------------------------------------------------*/
#ifdef WITH_UIP
void
dispatcher_uipcall(void)
{
register struct dispatcher_proc *p;
register struct dispatcher_uipstate *s;
static u8_t i;
struct listenport *l;
s = (struct dispatcher_uipstate *)uip_conn->appstate;
/* If this is a connection request for a listening port, we must
mark the connection with the right process ID. */
if(uip_connected()) {
l = &listenports[0];
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->port == uip_conn->lport &&
l->id != EK_ID_NONE) {
s->id = l->id;
s->state = NULL;
break;
}
++l;
}
}
for(p = dispatcher_procs; p != NULL; p = p->next) {
if(p->id == s->id &&
p->uiphandler != NULL) {
dispatcher_current = p->id;
curproc = p;
#if CC_FUNCTION_POINTER_ARGS
p->uiphandler(s->state);
#else /* CC_FUNCTION_POINTER_ARGS */
dispatcher_uipcall_state = s->state;
p->uiphandler();
#endif /* CC_FUNCTION_POINTER_ARGS */
return;
}
}
/* If we get here, the process that created the connection has
exited, so we abort this connection. */
uip_abort();
}
#endif /* WITH_UIP */
/*-----------------------------------------------------------------------------------*/
/**
* Opens a TCP port for incoming requests.
*
*
*
* \param port The TCP port number that should be opened for
* listening, in host byte order.
*/
/*-----------------------------------------------------------------------------------*/
void
dispatcher_uiplisten(u16_t port)
{
static unsigned char i;
struct listenport *l;
l = listenports;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->port == 0) {
l->port = HTONS(port);
l->id = dispatcher_current;
#ifdef WITH_UIP
uip_listen(port);
#endif /* WITH_UIP */
break;
}
++l;
}
}
/*-----------------------------------------------------------------------------------*/
/**
* Associates a generic pointer with a uIP connection.
*
* This function is used for registering a pointer to a uIP
* connection. This pointer will be passed as an argument to the
* process' uIP handler function for every uIP event.
*
* \param conn The uIP connection to which the pointer is to be
* associated.
*
* \param appstate The generic pointer that is to be associated with
* the uIP connection.
*/
/*-----------------------------------------------------------------------------------*/
void
dispatcher_markconn(struct uip_conn *conn,
void *appstate)
{
struct dispatcher_uipstate *s;
s = (struct dispatcher_uipstate *)conn->appstate;
s->id = dispatcher_current;
s->state = appstate;
}
/*-----------------------------------------------------------------------------------*/
/**
* Registers the calling process as a listener for a signal.
*
* \param s The signal to which the process should listen.
*
*/
/*-----------------------------------------------------------------------------------*/
void
dispatcher_listen(ek_signal_t s)
{
if(curproc != NULL) {
curproc->signals[s] = 1;
}
}
/*-----------------------------------------------------------------------------------*/
/**
* 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 dispatcher_proc *
dispatcher_process(ek_id_t id)
{
struct dispatcher_proc *p;
for(p = dispatcher_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
dispatcher_init(void)
{
static unsigned char i;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
listenports[i].port = 0;
}
lastsig = 1;
dispatcher_signal_quit = dispatcher_sigalloc();
nsignals = fsignal = 0;
arg_init();
}
/*-----------------------------------------------------------------------------------*/
/**
* \internal Delivers a signal to specific process.
*
* \param s The signal number
* \param data The signal data pointer
* \param id The process to which the signal should be delivered
*/
/*-----------------------------------------------------------------------------------*/
static void CC_FASTCALL
deliver(ek_signal_t s, ek_data_t data,
ek_id_t id)
{
struct dispatcher_proc *p;
for(p = dispatcher_procs; p != NULL; p = p->next) {
if((id == DISPATCHER_BROADCAST ||
p->id == id) &&
p->signals[s] != 0 &&
p->signalhandler != NULL) {
dispatcher_current = p->id;
curproc = p;
#if CC_FUNCTION_POINTER_ARGS
p->signalhandler(s, data);
#else /* CC_FUNCTION_POINTER_ARGS */
dispatcher_sighandler_s = s;
dispatcher_sighandler_data = data;
p->signalhandler();
#endif /* CC_FUNCTION_POINTER_ARGS */
}
}
}
/*-----------------------------------------------------------------------------------*/
/**
* Emits a signal, and delivers the signal immediately.
*
* This function emits a signal and calls the listening processes'
* signal handlers immediately, before returning to the caller. This
* function requires more call stack space than the dispatcher_emit()
* function and should be used with care, and only in situtations
* where the exact implications are known.
*
* In most situations, the dispatcher_emit() function should be used
* instead.
*
* \param s The signal to be emitted.
*
* \param data The auxillary data to be sent with the signal
*
* \param id The process ID to which the signal should be emitted, or
* DISPATCHER_BROADCAST if the signal should be emitted to all
* processes listening for the signal.
*
*/
/*-----------------------------------------------------------------------------------*/
void
dispatcher_fastemit(ek_signal_t s, ek_data_t data,
ek_id_t id)
{
ek_id_t pid;
struct dispatcher_proc *p;
pid = dispatcher_current;
p = curproc;
deliver(s, data, id);
dispatcher_current = pid;
curproc = p;
}
/*-----------------------------------------------------------------------------------*/
/**
* Process the next signal in the signal queue and deliver it to
* listening processes.
*
*/
/*-----------------------------------------------------------------------------------*/
void
dispatcher_process_signal(void)
{
static ek_signal_t s;
static ek_data_t data;
static ek_id_t id;
/* If there are any signals in the queue, take the first one and
walk through the list of processes to see if the signal should be
delivered to any of them. If so, we call the signal handler
function for the process. We only process one signal at a time
and call the idle handlers inbetween. */
if(nsignals > 0) {
/* There are signals that we should deliver. */
s = signals[fsignal].s;
data = signals[fsignal].data;
id = signals[fsignal].id;
/* Since we have seen the new signal, we move pointer upwards
and decrese number. */
fsignal = (fsignal + 1) % EK_CONF_NUMSIGNALS;
--nsignals;
deliver(s, data, id);
}
}
/*-----------------------------------------------------------------------------------*/
/**
* Call each process' idle handler.
*/
/*-----------------------------------------------------------------------------------*/
void
dispatcher_process_idle(void)
{
struct dispatcher_proc *p;
/* Call idle handlers. */
for(p = dispatcher_procs; p != NULL; p = p->next) {
if(p->idle != NULL) {
dispatcher_current = p->id;
curproc = p;
p->idle();
}
}
}
/*-----------------------------------------------------------------------------------*/
/**
* Run the system - process signals and call idle handlers.
*
* This function should be called after all other initialization
* stuff, and will never return.
*/
/*-----------------------------------------------------------------------------------*/
void
dispatcher_run(void)
{
while(1) {
/* Process one signal */
dispatcher_process_signal();
/* Run "idle" handlers */
dispatcher_process_idle();
}
}
/*-----------------------------------------------------------------------------------*/
/**
* Emits a signal to a process.
*
* \param s The signal to be emitted.
*
* \param data The auxillary data to be sent with the signal
*
* \param id The process ID to which the signal should be emitted, or
* DISPATCHER_BROADCAST if the signal should be emitted to all
* processes listening for the signal.
*
* \retval EK_ERR_OK The signal could be emitted.
*
* \retval EK_ERR_FULL The signal queue was full and the signal could
* not be emitted
*/
/*-----------------------------------------------------------------------------------*/
ek_err_t
dispatcher_emit(ek_signal_t s, ek_data_t data, ek_id_t id)
{
static unsigned char snum;
if(nsignals == EK_CONF_NUMSIGNALS) {
return EK_ERR_FULL;
}
if(s != EK_SIGNAL_NONE) {
snum = (fsignal + nsignals) % EK_CONF_NUMSIGNALS;
signals[snum].s = s;
signals[snum].data = data;
signals[snum].id = id;
++nsignals;
}
return EK_ERR_OK;
}
/*-----------------------------------------------------------------------------------*/