Changed packet drivers from services to plain processes.
Now tcpip_output() is a function pointer that is supposed to be set via the macro tcpip_set_outputfunc(). Packet drivers do so on process startup. Thus if there are several packet drivers in a Contiki system the one started last is the one actually used. This behaviour is especially useful for the 'IP forwarding' "meta" packet driver.
This commit is contained in:
parent
3f4ec3cf44
commit
080ecc5488
138
doc/example-packet-drv.c
Normal file
138
doc/example-packet-drv.c
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* This is an example of how to write a network device driver ("packet
|
||||||
|
* driver") for Contiki. A packet driver is a regular Contiki process
|
||||||
|
* that does two things:
|
||||||
|
* # Checks for incoming packets and delivers those to the TCP/IP stack
|
||||||
|
* # Provides an output function that transmits packets
|
||||||
|
*
|
||||||
|
* The output function is registered with the Contiki TCP/IP stack,
|
||||||
|
* whereas incoming packets must be checked inside a Contiki process.
|
||||||
|
* We use the same process for checking for incoming packets and for
|
||||||
|
* registering the output function.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We include the "contiki-net.h" file to get all the network functions.
|
||||||
|
*/
|
||||||
|
#include "contiki-net.h"
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/*
|
||||||
|
* We declare the process that we use to register with the TCP/IP stack,
|
||||||
|
* and to check for incoming packets.
|
||||||
|
*/
|
||||||
|
PROCESS(example_packet_driver_process, "Example packet driver process");
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/*
|
||||||
|
* Next, we define the function that transmits packets. This function
|
||||||
|
* is called from the TCP/IP stack when a packet is to be transmitted.
|
||||||
|
* The packet is located in the uip_buf[] buffer, and the length of the
|
||||||
|
* packet is in the uip_len variable.
|
||||||
|
*/
|
||||||
|
u8_t
|
||||||
|
example_packet_driver_output(void)
|
||||||
|
{
|
||||||
|
let_the_hardware_send_the_packet(uip_buf, uip_len);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/*
|
||||||
|
* This is the poll handler function in the process below. This poll
|
||||||
|
* handler function checks for incoming packets and delivers them to
|
||||||
|
* the TCP/IP stack.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
pollhandler(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We assume that we have some hardware device that notifies us when
|
||||||
|
* a new packet has arrived. We also assume that we have a function
|
||||||
|
* that pulls out the new packet (here called
|
||||||
|
* check_and_copy_packet()) and puts it in the uip_buf[] buffer. The
|
||||||
|
* function returns the length of the incoming packet, and we store
|
||||||
|
* it in the global uip_len variable. If the packet is longer than
|
||||||
|
* zero bytes, we hand it over to the TCP/IP stack.
|
||||||
|
*/
|
||||||
|
uip_len = check_and_copy_packet();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The function tcpip_input() delivers the packet in the uip_buf[]
|
||||||
|
* buffer to the TCP/IP stack.
|
||||||
|
*/
|
||||||
|
if(uip_len > 0) {
|
||||||
|
tcpip_input();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we'll make sure that the poll handler is executed repeatedly.
|
||||||
|
* We do this by calling process_poll() with this process as its
|
||||||
|
* argument.
|
||||||
|
*
|
||||||
|
* In many cases, the hardware will cause an interrupt to be executed
|
||||||
|
* when a new packet arrives. For such hardware devices, the interrupt
|
||||||
|
* handler calls process_poll() (which is safe to use in an interrupt
|
||||||
|
* context) instead.
|
||||||
|
*/
|
||||||
|
process_poll(&example_packet_driver_process);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/*
|
||||||
|
* Here we shutdown the hardware in case the process exits.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
exithandler(void)
|
||||||
|
{
|
||||||
|
shutdown_the_hardware();
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/*
|
||||||
|
* Finally, we define the process that does the work.
|
||||||
|
*/
|
||||||
|
PROCESS_THREAD(example_packet_driver_process, ev, data)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This process has a poll handler, so we declare it here. Note that
|
||||||
|
* the PROCESS_POLLHANDLER() macro must come before the PROCESS_BEGIN()
|
||||||
|
* macro.
|
||||||
|
*/
|
||||||
|
PROCESS_POLLHANDLER(pollhandler());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This process has an exit handler, so we declare it here. Note that
|
||||||
|
* the PROCESS_EXITHANDLER() macro must come before the PROCESS_BEGIN()
|
||||||
|
* macro.
|
||||||
|
*/
|
||||||
|
PROCESS_EXITHANDLER(exithandler());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The process begins here.
|
||||||
|
*/
|
||||||
|
PROCESS_BEGIN();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We start with initializing the hardware.
|
||||||
|
*/
|
||||||
|
initialize_the_hardware();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register the driver. This will cause any previously registered driver
|
||||||
|
* to be ignored by the TCP/IP stack.
|
||||||
|
*/
|
||||||
|
tcpip_set_outputfunc(example_packet_driver_output);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we'll make sure that the poll handler is executed initially. We do
|
||||||
|
* this by calling process_poll() with this process as its argument.
|
||||||
|
*/
|
||||||
|
process_poll(&example_packet_driver_process);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* And we wait for the process to exit.
|
||||||
|
*/
|
||||||
|
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_EXIT);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here ends the process.
|
||||||
|
*/
|
||||||
|
PROCESS_END();
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
|
@ -1,148 +0,0 @@
|
||||||
/*
|
|
||||||
* This is an example of how to write a network device driver ("packet
|
|
||||||
* service") for Contiki. A packet service is a regular Contiki
|
|
||||||
* service that does two things:
|
|
||||||
* # Checks for incoming packets and delivers those to the TCP/IP stack
|
|
||||||
* # Provides an output function that transmits packets
|
|
||||||
*
|
|
||||||
* The output function is registered with the Contiki service
|
|
||||||
* mechanism, whereas incoming packets must be checked inside a
|
|
||||||
* Contiki process. We use the same process for checking for incoming
|
|
||||||
* packets and for registering the service.
|
|
||||||
*
|
|
||||||
* NOTE: This example does not work with the uip-fw module (packet
|
|
||||||
* forwarding with multiple interfaces). It only works with a single
|
|
||||||
* interface.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We include the "contiki-net.h" file to get all the network
|
|
||||||
* functions.
|
|
||||||
*/
|
|
||||||
#include "contiki-net.h"
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
/*
|
|
||||||
* We declare the process that we use to register the service, and to
|
|
||||||
* check for incoming packets.
|
|
||||||
*/
|
|
||||||
PROCESS(example_packet_service_process, "Example packet service process");
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
/*
|
|
||||||
* This is the poll handler function in the process below. This poll
|
|
||||||
* handler function checks for incoming packets and delivers them to
|
|
||||||
* the TCP/IP stack.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
pollhandler(void)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* We assume that we have some hardware device that notifies us when
|
|
||||||
* a new packet has arrived. We also assume that we have a function
|
|
||||||
* that pulls out the new packet (here called
|
|
||||||
* check_and_copy_packet()) and puts it in the uip_buf[] buffer. The
|
|
||||||
* function returns the length of the incoming packet, and we store
|
|
||||||
* it in the global uip_len variable. If the packet is longer than
|
|
||||||
* zero bytes, we hand it over to the TCP/IP stack.
|
|
||||||
*/
|
|
||||||
uip_len = check_and_copy_packet();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The function tcpip_input() delivers the packet in the uip_buf[]
|
|
||||||
* buffer to the TCP/IP stack.
|
|
||||||
*/
|
|
||||||
if(uip_len > 0) {
|
|
||||||
tcpip_input();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now we'll make sure that the poll handler is executed
|
|
||||||
* repeatedly. We do this by calling process_poll() with this
|
|
||||||
* process as its argument.
|
|
||||||
*
|
|
||||||
* In many cases, the hardware will cause an interrupt to be
|
|
||||||
* executed when a new packet arrives. For such hardware devices,
|
|
||||||
* the interrupt handler calls process_poll() (which is safe to use
|
|
||||||
* in an interrupt context) instead.
|
|
||||||
*/
|
|
||||||
process_poll(&example_packet_service_process);
|
|
||||||
}
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
/*
|
|
||||||
* Next, we define the function that transmits packets. This function
|
|
||||||
* is called from the TCP/IP stack when a packet is to be
|
|
||||||
* transmitted. The packet is located in the uip_buf[] buffer, and the
|
|
||||||
* length of the packet is in the uip_len variable.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
send_packet(void)
|
|
||||||
{
|
|
||||||
let_the_hardware_send_the_packet(uip_buf, uip_len);
|
|
||||||
}
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
/*
|
|
||||||
* Now we declare the service. We call the service
|
|
||||||
* example_packet_service because of the name of this file. The
|
|
||||||
* service should be an instance of the "packet service" service, so
|
|
||||||
* we give packet_service as the second argument. Finally we give our
|
|
||||||
* send_packet() function as the last argument, because of how the
|
|
||||||
* packet_service interface is defined.
|
|
||||||
*
|
|
||||||
* We'll register this service with the Contiki system in the process
|
|
||||||
* defined below.
|
|
||||||
*/
|
|
||||||
SERVICE(example_packet_service, packet_service, { send_packet });
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
/*
|
|
||||||
* Finally, we define the process that does the work.
|
|
||||||
*/
|
|
||||||
PROCESS_THREAD(example_packet_service_process, ev, data)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This process has a poll handler, so we declare it here. Note that
|
|
||||||
* the PROCESS_POLLHANDLER() macro must come before the
|
|
||||||
* PROCESS_BEGIN() macro.
|
|
||||||
*/
|
|
||||||
PROCESS_POLLHANDLER(pollhandler());
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The process begins here.
|
|
||||||
*/
|
|
||||||
PROCESS_BEGIN();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We start with initializing the hardware.
|
|
||||||
*/
|
|
||||||
initialize_the_hardware();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Register the service. This will cause any other instances of the
|
|
||||||
* same service to be removed.
|
|
||||||
*/
|
|
||||||
SERVICE_REGISTER(example_packet_service);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now we'll make sure that the poll handler is executed
|
|
||||||
* initially. We do this by calling process_poll() with this
|
|
||||||
* process as its argument.
|
|
||||||
*/
|
|
||||||
process_poll(&example_packet_service_process);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* And we wait for either the process to exit, or for the service to
|
|
||||||
* be removed (by someone else).
|
|
||||||
*/
|
|
||||||
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_EXIT ||
|
|
||||||
ev == PROCESS_EVENT_SERVICE_REMOVED);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* And we always end with explicitly removing the service.
|
|
||||||
*/
|
|
||||||
SERVICE_REMOVE(example_packet_service);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Here endeth the process.
|
|
||||||
*/
|
|
||||||
PROCESS_END();
|
|
||||||
}
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
|
@ -1,115 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is an example of how to implement a service in
|
|
||||||
* Contiki. The header file example-service.h defines a service called
|
|
||||||
* "example_service", which we implement in this file.
|
|
||||||
*
|
|
||||||
* This example shows how to define an instance of a service, and how
|
|
||||||
* to write the service's controlling process.
|
|
||||||
*
|
|
||||||
* See the file example-use-service.c for an example of how to call a
|
|
||||||
* service.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "example-service.h"
|
|
||||||
#include "contiki.h"
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
/*
|
|
||||||
* We start by implementing all the functions that the service
|
|
||||||
* offers. In this case, there is only a single function (called
|
|
||||||
* example_function()) and we implement it here. We give it the name
|
|
||||||
* example() and declare it with the "static" keyword to keep the
|
|
||||||
* scope local to this file.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
example(void) {
|
|
||||||
printf("Example service called\n");
|
|
||||||
}
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
/*
|
|
||||||
* This is the instantiation of the service called
|
|
||||||
* "example_service". The service interface is defined in the header
|
|
||||||
* file example-service.h.
|
|
||||||
*
|
|
||||||
* This statement defines the name of this implementation of the
|
|
||||||
* service - example_service_implementation - and defines the
|
|
||||||
* functions that actually implement the functions offered by the
|
|
||||||
* service. In this example, the service consists of a single function
|
|
||||||
* called "example_function()". We implement this function in the
|
|
||||||
* function called "example()" defined above.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
SERVICE(example_service_implementation, /* The name of this instance
|
|
||||||
of the service - used with
|
|
||||||
SERVICE_REGISTER(). */
|
|
||||||
example_service, /* The name of the serivce
|
|
||||||
that is instantiated. */
|
|
||||||
{ example }); /* The list of functions
|
|
||||||
required by the
|
|
||||||
service. In this case, we
|
|
||||||
only have one function. */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* All services needs a controlling process. The controlling process
|
|
||||||
* registers the service with the system when it starts, and is also
|
|
||||||
* notified if the service is removed or replaced.
|
|
||||||
*
|
|
||||||
* We simply call the process "example_service_process" and gives it a
|
|
||||||
* similar textual name.
|
|
||||||
*/
|
|
||||||
PROCESS(example_service_process, "Example service process");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For this example, we use a timer to remove the service after a
|
|
||||||
* certain time. We declare the timer here.
|
|
||||||
*/
|
|
||||||
static struct etimer timer;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Finally, we implement the controlling process.
|
|
||||||
*/
|
|
||||||
PROCESS_THREAD(example_service_process, ev, data)
|
|
||||||
{
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A process thread starts with PROCESS_BEGIN() and ends with
|
|
||||||
* PROCESS_END().
|
|
||||||
*/
|
|
||||||
PROCESS_EXITHANDLER(goto exit);
|
|
||||||
PROCESS_BEGIN();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We register the service instance with a SERVICE_REGISTER()
|
|
||||||
* statement.
|
|
||||||
*/
|
|
||||||
printf("Registering example service\n");
|
|
||||||
SERVICE_REGISTER(example_service_implementation);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We set a timer for four seconds and wait for it to expire - or
|
|
||||||
* for the process to receive an event which requests it to exit.
|
|
||||||
*
|
|
||||||
* The only purpose for the timer is to demonstrate how a service is
|
|
||||||
* removed - it is not something that is commonly done.
|
|
||||||
*/
|
|
||||||
etimer_set(&timer, 4 * CLOCK_SECOND);
|
|
||||||
PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_SERVICE_REMOVED ||
|
|
||||||
etimer_expired(&timer));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* And we remove the service before the process ends. This is a
|
|
||||||
* *very* important step - if the process exits and is unloaded
|
|
||||||
* without first removing its services, the system may crash!
|
|
||||||
*/
|
|
||||||
printf("Removing example service\n");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* And finally the process ends.
|
|
||||||
*/
|
|
||||||
exit:
|
|
||||||
SERVICE_REMOVE(example_service_implementation);
|
|
||||||
PROCESS_END();
|
|
||||||
}
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is an example of how to define a service in Contiki. The
|
|
||||||
* example shows how to define a service interface, and how to give
|
|
||||||
* the service a name.
|
|
||||||
*/
|
|
||||||
#ifndef __EXAMPLE_SERVICE_H__
|
|
||||||
#define __EXAMPLE_SERVICE_H__
|
|
||||||
|
|
||||||
#include "sys/service.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is how we define the service interface, and give the service a
|
|
||||||
* name. The name of this particular service is "example_service" and
|
|
||||||
* the interface consists of a single function, called
|
|
||||||
* example_function().
|
|
||||||
*/
|
|
||||||
SERVICE_INTERFACE(example_service,
|
|
||||||
{
|
|
||||||
void (* example_function)(void);
|
|
||||||
/* More functions can be added here, line by line. */
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We must also give the service a textual name. We do this by using a
|
|
||||||
* special #define statment - we define a macro with the same name as
|
|
||||||
* the service, but postfixed with "_name".
|
|
||||||
*
|
|
||||||
* The textual name is used when looking up services. The name must be
|
|
||||||
* unique within the system.
|
|
||||||
*/
|
|
||||||
#define example_service_name "Example service"
|
|
||||||
|
|
||||||
#endif /* __EXAMPLE_SERVICE_H__ */
|
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
* This file contains an example of how to call a service.
|
|
||||||
*
|
|
||||||
* This program implements a process that calls the service defined in
|
|
||||||
* example-service.h every second.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "contiki.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We must include the header file for the service.
|
|
||||||
*/
|
|
||||||
#include "example-service.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* All Contiki programs must have a process, and we declare it here.
|
|
||||||
*/
|
|
||||||
PROCESS(example_use_service_process, "Use example");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The program is to call the service once every second, so we use an
|
|
||||||
* event timer in order to run every second.
|
|
||||||
*/
|
|
||||||
static struct etimer timer;
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
/*
|
|
||||||
* Here we implement the process.
|
|
||||||
*/
|
|
||||||
PROCESS_THREAD(example_use_service_process, ev, data)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* A process thread starts with PROCESS_BEGIN() and ends with
|
|
||||||
* PROCESS_END().
|
|
||||||
*/
|
|
||||||
PROCESS_BEGIN();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We loop for ever, calling the service once every second.
|
|
||||||
*/
|
|
||||||
while(1) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We set a timer that wakes us up once every second.
|
|
||||||
*/
|
|
||||||
etimer_set(&timer, CLOCK_SECOND);
|
|
||||||
PROCESS_YIELD_UNTIL(etimer_expired(&timer));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We call the service. If the service is not registered, the
|
|
||||||
* SERVICE_CALL() statement does nothing. If we need to know if
|
|
||||||
* the service exists, we can use the SERVICE_FIND() function.
|
|
||||||
*/
|
|
||||||
printf("use example: calling example\n");
|
|
||||||
SERVICE_CALL(example_service, example_function());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* And finally the process ends.
|
|
||||||
*/
|
|
||||||
PROCESS_END();
|
|
||||||
}
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
|
@ -1,10 +1,7 @@
|
||||||
/** \example example-program.c */
|
/** \example example-program.c */
|
||||||
/** \example example-service.c */
|
|
||||||
/** \example example-service.h */
|
|
||||||
/** \example example-use-service.c */
|
|
||||||
/** \example example-pollhandler.c */
|
/** \example example-pollhandler.c */
|
||||||
/** \example example-list.c */
|
/** \example example-list.c */
|
||||||
/** \example example-packet-service.c */
|
/** \example example-packet-drv.c */
|
||||||
/** \example example-psock-server.c */
|
/** \example example-psock-server.c */
|
||||||
|
|
||||||
/** \example test-abc.c */
|
/** \example test-abc.c */
|
||||||
|
|
Loading…
Reference in a new issue