[Tinyos-devel] message_t

Miklos Maroti mmaroti at math.u-szeged.hu
Thu Mar 12 14:03:07 PDT 2009


Here is may alternative proposal for the new message_t. Basically, I
would require all message_t to be formated (the old Packet.clear with
some parameters), then everyone knows where the payload is and where
the other header and footer fields are, so we can flexibly set those
values (64 bit addresses, security data), finally we send the message.
You need to format a message only if you do not know (first use) or
want to change its layout (forward).

Comments are welcome.

-------------- next part --------------

 The KISS message_t proposal for TinyOS

First we need to identify the problems with message_t, then seek a 
good solution. I see the following ones:

1) RAM is wasted with the current message_t. For example, if a packet 
has security enabled, then the header needs some 14 extra bytes but 
because of hardware limitations the payload length is reduced by these 
14 bytes. Taking the maximum header, maximum payload, maximum footer 
and maximum metadata sizes wastes a lot of memory.

2) The networks layers cannot be seamlessly mixed because most of the 
parameters are passed with the Send.send command. For example, if we 
want to add an embedded time stamp in the message, then the event time 
is passed in the TimeSyncAMSend.send command along with other stuff. 
However, if we want to use time synchronization with 802.15.4 packets, 
then again a new interface and component need to be developed (e.g. 
TimeSyncIeee154Send or something). The existing LowPowerListening, 
PacketAcknowledgements, PacketLink interfaces do not have this problem 
because they do change the layout of the message (and the metadata 
section thus wasting RAM when not in use).

The limitations of message_t are exposed exactly because that kind of 
network layers are interacting that want to change the layout of the 
message. Realize, that the user payload is just another network layer, 
and we so far circumvent the layout problem by aligning the payload. 
But this is not a scalable solution, and I propose to use a flat 
buffer for message_t:

 * This defines a opaque message buffer. The layout of every message 
 * buffer is [header] [payload] [footer] [free space] [metadata]. 
 * The first three  grow upwards, the metadata grows downwards with 
 * the nesting of components.
typedef struct message_t
	uint8_t buffer[MSG_MAX_LENGTH];
} message_t;

The second problem is harder to solve. The real problem is that the 
headers of a layer cannot be set before the headers of lower layers 
are set because we do not know where to put it into the message_t 
buffer. (Jan Hauer's solution to this problem is to grow the header 
and footer around the payload and allow it to wrap around the message 
buffer, but that solution does not allow the mixing of network layers 
either since all parameters need to be passed in the send command).

One solution would be to use a 

  XXXPacket.format(XXXparameters,..., payloadLength)

command to format the layout of the packet and move the layout 
changing parameters of the current XXXSend.send(...) command to this 
call. You could stack XXXPacket implementations on top of each other 
passing down layout changing XXXparameters that are relevant in the 
lower layers. 

Most of the layout changing parameters can be handled by flags:

enum layout_flags_enum
	LAYOUT_IEEE154_64BIT_ADDRS = 1 << 0,
	LAYOUT_6LOWPAN = 1 << 2,

Of course this could be generated by some unique() magic, also you 
might need to get more bits for the IEEE154 addressing modes. Each 
user would format the message with a layout parameter, then fill in 
all necessary parameters, then send it:

  // create the layout of the message
  call Packet.format(msg, LAYOUT_6LOWPAN | LAYOUT_TIMESYNC, 

  // fill in payload data
  void* payload = call Packet.getPayload(msg);

  // set layer specific configuration
  call Packet6LowPan.setNetwork(msg, ...);
  call PacketAncknowledgements.reqAck(msg);
  call PacketTimeSync.setTimeStamp(msg, stamp);

  // send the message
  call Send.send(msg);

So here are my proposed interfaces. These are the bare minimums, you 
can add syntactic sugar, like AMSend and friends.

interface Send
	command error_t send(message_t* msg);
	command void sendDone(message_t* msg, error_t error);
	command errot_t cancel(message_t* msg);
interface Receive
	message_t receive(message_t* msg);

interface Packet
	 * Returns the header length of the message as seen by the 
	 * network layer implementing this interface. Typical 
	 * implementation would call SubPacket.getHeaderLength(), then
	 * add a constant or read the message at the header section
	 * of this layer (at msg->data + subHeaderLength) and return
	 * a variable sized header
	uint8_t getHeaderLength(message_t* msg);

	 * The same are true for the other functions, even for 
	 * metadata which grows downward
	uint8_t getPayloadLength(message_t* msg);
	uint8_t getFooterLength(message_t* msg);
	uint8_t getMetadataLength(message_t* msg);

	 * Sets only those parts of the message that completely 
	 * the layout of the message and leaves most of the fields 
	error_t format(message_t* msg, uint8_t layoutFlags, uint8_t 

	// convenience function, implementation is given here
	void* getPayload(message_t* msg) {
		return msg->data + getHeaderLength(msg);

These interfaces could be stacked very very nicely. Each network layer 
would also provide a PacketXXX interface to manipulate the fields of 
the message, just like PacketLink, PacketAcknowledgement, 
LowPowerListening, and PacketTimeSync.

When the layout of a message is changed, then the payload data need to 
be copied, but only once. This proposal contains no magic, very easy 
to understand and I think has a lot of flexibility. 


More information about the Tinyos-devel mailing list