:Group: Core Working Group
:Type: Documentary
:Status: Draft
-:TinyOS-Version: 2.x
+:TinyOS-Version: > 2.1
:Author: Philip Levis
:Draft-Created: 10-Dec-2004
The memo documents the interfaces used by packet protocol components in
TinyOS 2.x as well as the structure and implementation of ActiveMessageC,
the basic data-link HIL component. It also documents the virtualized
-active message interfaces AMSender and AMReceiver.
+active message interfaces AMSenderC and AMReceiverC.
1. Introduction
============================================================================
*Packet* interfaces are for accessing message fields and payloads.
*Send* interfaces are for transmitting packets, and are
distinguished by their addressing scheme.
-Finally, the *Receive* interface is for handling packet reception events.
+The *Receive* interface is for handling packet reception events.
+Finally, depending on whether the protocol has a dispatch identifier
+field, the Receive and Send interfaces may be parameterized in order
+to support multiple higher-level clients.
2.1 Packet interfaces
--------------------------------------------------------------------
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);
+ command void* getPayload(message_t* msg, uint8_t len);
}
A component can obtain a pointer to its data region within a packet by
-calling ``getPayload()`` the optional ``len`` argument is for also
-obtaining the size of the data region. A provider of a Packet
-interface MUST check if ``len`` is NULL and ignore it if it is. A
-component can also obtain the size of the data region with a call to
-``payloadLength``.
-
-A component can set the payload length with
-``setPayLoadLength.`` As Send interfaces always include a length
-parameter in their send call, this command is not required for
-sending, and so is never called in common use cases. Instead,
-it is a way for queues and other packet buffering components
-to store the full state of a packet without requiring additional
-memory allocation.
+calling ``getPayload()``. A call to this command includes the length
+the caller requires. The command ``maxPayloadLength`` returns the maximum
+length the payload can be: if the ``len`` parameter to ``getPayload``
+is greater than the value ``maxPayloadLength``would return,
+``getPayload`` MUST return NULL.
+
+
+A component can set the payload length with ``setPayLoadLength.`` A
+component can obtain the size of the data region of packet in use with
+a call to ``payloadLength``. As Send interfaces always include a
+length parameter in their send call, ``setPayLoadLength`` is not
+required for sending, and so is never called in common use
+cases. Instead, it is a way for queues and other packet buffering
+components to store the full state of a packet without requiring
+additional memory allocation.
The distinction between ``payloadLength`` and ``maxPayloadLength``
-comes from whether the packet is being received or sent. In the receive
-case, determining the size of the existing data payload is needed;
-in the send case, a component needs to know how much data it can put
-in the packet.
+comes from whether the packet is being received or sent. In the
+receive case, determining the size of the existing data payload is
+needed; in the send case, a component needs to know how much data it
+can put in the packet. By definition, the return value of
+``payloadLength`` must be less than or equal to the return value of
+``maxPayloadLength``.
The Packet interface assumes that headers have a fixed size.
It is difficult to return a pointer into the data region when its
command void Packet.clear(message_t* msg) {
uint8_t len;
- void* payload = call SubPacket.getPayload(msg, &len);
+ void* payload = call SubPacket.getPayload(msg, call SubPacket.maxPayloadLength());
memset(payload, len, 0);
}
return SubPacket.maxPayloadLength(msg) - SEQNO_OFFSET;
}
- command void* Packet.getPayload(message_t* msg, uint8_t* len) {
- uint8_t* payload = call SubPacket.getPayload(msg, len);
- if (len != NULL) {
- *len -= SEQNO_OFFSET;
+ command void* Packet.getPayload(message_t* msg, uint8_t len) {
+ uint8_t* payload = call SubPacket.getPayload(msg, len + SEQNO_OFFSET);
+ if (payload != NULL) {
+ payload += SEQNO_OFFSET;
}
- return payload + SEQNO_OFFSET;
+ return payload;
}
}
interface AMPacket {
command am_addr_t address();
command am_addr_t destination(message_t* amsg);
+ command am_addr_t source(message_t* amsg);
command void setDestination(message_t* amsg, am_addr_t addr);
+ command void setSource(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);
The command address() returns the local AM address of the
node. AMPacket provides accessors for its two fields, destination and
-type. It does not provide commands to set these fields, as they are
-set in the sending call path (see Section 2.3). The ``setDestination``
-and ``setType`` commands fulfill a similar purpose to
-``Packet.setLength``.
+type. It also provides commands to set these fields, for the same
+reason that Packet allows a caller to set the payload length.
+Packet interfaces SHOULD provide accessors
+and mutators for all of their fields to enable queues and other
+buffering to store values in a packet buffer. Typically, a component
+stores these values in the packet buffer itself (where the field is),
+but when necessary it may use the metadata region of message_t or other
+locations.
2.2 Sending interfaces
--------------------------------------------------------------------
event void sendDone(message_t* msg, error_t error);
command uint8_t maxPayloadLength();
- command void* getPayload(message_t* msg);
+ command void* getPayload(message_t* msg, uint8_t len);
}
while this is the AMSend interface::
event void sendDone(message_t* msg, error_t error);
command uint8_t maxPayloadLength();
- command void* getPayload(message_t* msg);
+ command void* getPayload(message_t* msg, uint8_t len);
}
Sending interfaces MUST include these four commands and one event.
The duplication of some of the commands in Packet is solely for ease
of use: ``maxPayloadLength`` and ``getPayload`` MUST behave
-identically as ``Packet.maxPayloadLength`` and ``Packet.getPayload``,
-with the exception that the latter has no length parameter (it should
-behave as if the length parameter of the ``Packet`` call were
-NULL). Their inclusion is so that components do not have to wire to
+identically as ``Packet.maxPayloadLength`` and ``Packet.getPayload.``
+Their inclusion is so that components do not have to wire to
both Packet and the sending interface for basic use cases.
+When called with a length that is too long for the underlying
+maximum transfer unit (MTU), the send command MUST return ESIZE.
+
+The ``Send`` and ``AMSend`` interfaces have an explicit queue of
+depth one. A call to ``send`` on either of these interfaces MUST
+return EBUSY if a prior call to ``send`` returned SUCCESS but no
+``sendDone`` event has been signaled yet. More explicitly::
+
+ if (call Send.send(...) == SUCCESS &&
+ call Send.send(...) == SUCCESS) {
+ // This block is unreachable.
+ }
+
+Systems that need send queues have two options. They can
+use a QueueC (found in tos/system) to store pending packet pointers
+and serialize them onto sending interface, or they can introduce
+a new sending interface that supports multiple pending transmissions.
+
+The cancel command allows a sender to cancel the current transmission.
+A call to cancel when there is no pending sendDone event MUST return FAIL.
+If there is a pending sendDone event and the cancel returns SUCCESS, then
+the packet layer MUST NOT transmit the packet and MUST signal sendDone
+with ECANCEL as its error code. If there is a pending sendDone event
+and cancel returns FAIL, then sendDone SHOULD occur as if the cancel
+was not called.
+
2.3 Receive interface
--------------------------------------------------------------------
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);
}
A call to ``Receive.getPayload()`` MUST behave identically to a call
A *user* of the Receive interface has three basic options when it
handles a receive event:
-1) Return ``msg`` without touching it.
-2) Copy some data out of ``payload`` and return ``msg``.
-3) Store ``msg`` in its local frame and return a different ``message_t*`` for the lower layer to use.
+ 1) Return ``msg`` without touching it.
+ 2) Copy some data out of ``payload`` and return ``msg``.
+ 3) Store ``msg`` in its local frame and return a different ``message_t*`` for the lower layer to use.
These are simple code examples of the three cases::
}
//Case 3
- message_t* ptr;
+ message_t buf;
+ message_t* ptr = &buf;
message_t* Receive.receive(message_t* msg, void* payload, uint8_t len) {
message_t* tmp = ptr;
ptr = msg;
--------------------------------------------------------------------
A packet protocol MAY have a dispatch identifier. This generally manifests
-as the protocol component provided parameterized interfaces (rather than
-a single interface instances). A dispatch identifier allows multiple
+as the protocol component providing parameterized interfaces (rather than
+a single interface instance). A dispatch identifier allows multiple
services to use a protocol independently. If a protocol provides a
dispatch mechanism, then each dispatch identifier SHOULD correspond to
a single packet format: if an identifier corresponds to multiple packet
a certain am_id_t MUST NOT instantiate another AMSnoopingReceiverC,
AMSnooperC, or AMReceiverC with the same am_id_t.
-4.5 AMSender
+4.5 AMSenderC
--------------------------------------------------------------------
AMSenderC has the following signature::
packets receives a reasonable approximation of an equal share of the
available transmission bandwidth.
-4.6 Power Management
+5. Power Management and Local Address
+============================================================================
+
+In addition to standard datapath interfaces for sending and
+receiving packets, an active message layer also has control interfaces.
+
+
+5.1 Power Management
--------------------------------------------------------------------
The communication virtualizations do not support power management.
techniques, such as TDMA scheduling or low power listening, when
"on."
+5.2 Local Active Message Address
+--------------------------------------------------------------------
+
+An application can change ActiveMessageC's local AM address
+at runtime. This will change which packets a node receives and
+the source address it embeds in packets. To change the local AM
+address at runtime, a component can wire to the component
+``ActiveMessageAddressC``. This component only changes the
+AM address of the default radio stack (AMSenderC, etc.); if
+a radio has multiple stacks those may have other components
+for changing their addresses in a stack-specific fashion.
+
5. HAL Requirements
============================================================================
can be passed to another data link layer (e.g., the UART) without
shifting the data payload. This means that the ``message_header_t`` must
include all data needed for AM fields, which might introduce headers
-in addition to those of the data link. For example, this is the
-structure of the CC2420 header::
+in addition to those of the data link. For example, this is an example
+structure for a CC2420 (802.15.4) header::
typedef nx_struct cc2420_header_t {
nx_uint8_t length;
.. [4] TEP 113: Serial Communication.
-
-
-