For programmers to share common conventions about network interfaces, TinyOS documentation describes the choices made to design network and AM interfaces, and how upper layers should be designed to meet these conventions.
The basic interface is Packet (figure 1.1), which
    provides access to the field that is common to every datagram
    protocol : the payload. Protocols must provide a specific
    interface to give access to the relevant fields of their
    format. To avoid copy of data and memory consumption, the
    components of the different layers share the same packet buffer, a
    message_t  pointer. They rely on underlying packet
    interfaces to know where their data begin.
interface Packet {
  command void clear(message_t* msg);
  command uint8_t payloadLength(message_t* msg);
  command void setPayLoadLength(message_t* msg, uint8_t len);
  command uint8_t maxPayloadLength();
  command void* getPayload(message_t* msg, uint8_t* len);
}
 | 
interface AMPacket {
  command am_addr_t address();
  command am_addr_t destination(message_t* amsg);
  command void setDestination(message_t* amsg, am_addr_t addr);
  command bool isForMe(message_t* amsg);
  command am_id_t type(message_t* amsg);
  command void setType(message_t* amsg, am_id_t t);
}
 | 
interface AMSend {
  command error_t send(am_addr_t addr, message_t* msg, uint8_t len);
  command error_t cancel(message_t* msg);
  event void sendDone(message_t* msg, error_t error);
  command uint8_t maxPayloadLength();
  command void* getPayload(message_t* msg);
}
 | 
The AMPacket interface provides accessors to two
    additional fields : destination and
    type(figure 1.2). The type of an active
    message is a one-byte integer to identify the data format. Though
    it also provides commands to set these fields, this is solely for
    specific purposes beyond the scope of this document. The fields
    will be set during the send process by the send component,
    specified by AMSend
    (figure 1.3).  We can indeed notice
    the addr parameter of the send
    command. There is no way to specify the type though. It is because
    AMSend implementors provide a parameterized interface, with the
    type as a parameter. This let co-existing protocols share a fair
    sending queue, managed by the sender component.
    Since all layers share the same packet buffer, and all rely on a
    specific interface to manipulate messages, a single interface to
    receive packets is needed. It is named Receive, as shown on
    figure 1.4. The commands associated to the payload are
    only here for convenience. 
One can notice that the receive handler is required to return a
    message_t buffer when a packet is received. This is a
    system to avoid memory leaks. Indeed, the receive component has to
    know when the packet is processed and the buffer available, so
    that it can use the buffer for another received packet. If the
    receive handler has to process several packets, it could keep all
    the buffers (which are statically allocated). This would let the receive component out of
    memory, and would prevent other components from receiving
    messages. By returning a buffer, the receive handler provides to
    the receive component a buffer that can be used for then next
    packet that is received. The receive handler can either process
    the packet and return the given buffer, or post a processing task
    and return a new buffer.
interface Receive {
  event message_t* receive(message_t* msg, void* payload, uint8_t len);
  command void* getPayload(message_t* msg, uint8_t* len);
  command uint8_t payloadLength(message_t* msg);
}
 | 
However, interesting work had been done for TinyOS 1.x. A modular network layer was designed to ease implementation of new protocols and code reuse. Some parts of this layer were implemented and provided with TinyOS distribution. This work gives a good basis for our design, it was therefore adapted to TinyOS 2 and our needs. This is discussed in next section.
![]()  | 
The goal of the implementation is to provide to an application a
    component in order to transparently send and receive data in a multihop
    network. We have called this component DymoNetworkC, which
    is a configuration. The wiring provided by this configuration is
    illustrated in figure 1.5. Since DYMO is a routing
    protocol, the configuration must include a datagram protocol to
    transport data on multi-hop routes. It can be any protocol using
    the same address format as DYMO, a 16-bit address in this case. In
    this document, we refer to the transport protocol as MH (for
    Multi-Hop).
    To be used, the network layer need to be started with the
    SplitControl interface. This is implemented by a dedicated
    module, NetControlM, which waits for all other components
    to start before letting the application using the network layer
    (ActiveMessageC implements the link layer). The application
    can then send and receive MHPackets, which can be
    manipulated with the appropriate module.
Sent and received packets are handled by the DispatcherM
    module, and passed to either DymoServiceC or
    MHServiceC, depending on their AM type. These two
    components inspect the packet and decide what to do with it:
    forwarding it, passing it to the upper layer (for
    MHServiceC), or dropping it.
Forwarded packets (which include sent packets) are given to
    AMSenderC, which manages a sendinq queue for each AM
    type. Service components can also generate packets, primarily to
    send control packets (routing and error messages for DYMO, error
    messages for MH). They decide what is the next hop of the packet
    thanks to the routing table component.
DymoServiceC (figure 1.6) and
    MHServiceC (figure 1.7) configurations contain
    the components that implement the algortihms and packet format of
    their respective protocols. Each of them is composed of three
    parts: a forwarding engine, a protocol engine, and a packet
    implementation.
    The ForwardingEngineM module has a very simple role and is
    identical for the two protocol services. Upon receiving a packet from
    the dispatcher through the forward  command, it asks the
    routing engine for what should be done with this packet. The
    routing engine inspects the packet thanks to the packet module,
    decides what to do according to the implemented protocol, and
    possibly updates the packet if it has to be forwarded. It returns
    its decision to the forwarding engine, which performs the
    appropriate action.
If processing a packet is too long, the routing engine may
    immediately return a ``drop'' action, process the packet and
    forward it by itself or send an arror message, since it also has
    access to the AMSend interface.
DymoTableC component stores known routes, that is
    mainly a destination address, a next hop and a hop count. Routing
    information is retrieved from the table via RoutingTable, a
    generic interface for routing tables (described in
    section 1.3.1). The DymoEngineM module has
    more control thanks to the DymoTable interface, mainly to
    update the table and know when a route is needed, so that a route
    request can be issued.
interface RoutingTable {
  /**
   * Request for a route toward a destination.
   * @param Address of the destination node
   * @return The routing information associated to the destination
   */
  command rt_info_t * getRoute(addr_t address);
  /**
   * Signal that a route has been removed from the table.
   * @param route_info Routing information associated to the evicted entry
   * @param r reason of the eviction
   */
  event void evicted(rt_info_t * route_info, reason_t r);
  command uint16_t size();
}
 | 
interface DymoTable {
  /**
   * Update the table with fresh information about a destination.
   * @param route_info The routing information associated to the destination
   */
  command void update(rt_info_t * route_info);
  /**
   * Signal that a component asked for an unknown route, a RREQ should
   * be generated.
   * @param destination Target node of the needed route.
   */
  event void routeNeeded(addr_t destination);
}
 |