[Tinyos-2-commits] CVS: tinyos-2.x/tos/chips/rf2xx/rf212 RF212.h, NONE, 1.1 RF212ActiveMessageC.nc, NONE, 1.1 RF212ActiveMessageP.nc, NONE, 1.1 RF212DriverConfig.nc, NONE, 1.1 RF212DriverLayerC.nc, NONE, 1.1 RF212DriverLayerP-comp.nc, NONE, 1.1 RF212DriverLayerP.nc, NONE, 1.1 RF212Packet.h, NONE, 1.1 RF212PacketC.nc, NONE, 1.1 RF212PacketP.nc, NONE, 1.1

Miklos Maroti mmaroti at users.sourceforge.net
Tue Mar 10 14:43:28 PDT 2009


Update of /cvsroot/tinyos/tinyos-2.x/tos/chips/rf2xx/rf212
In directory ddv4jf1.ch3.sourceforge.com:/tmp/cvs-serv587

Added Files:
	RF212.h RF212ActiveMessageC.nc RF212ActiveMessageP.nc 
	RF212DriverConfig.nc RF212DriverLayerC.nc 
	RF212DriverLayerP-comp.nc RF212DriverLayerP.nc RF212Packet.h 
	RF212PacketC.nc RF212PacketP.nc 
Log Message:
initial RF212 support

--- NEW FILE: RF212.h ---
/*
 * Copyright (c) 2007, Vanderbilt University
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author: Miklos Maroti
 */

#ifndef __RF212_H__
#define __RF212_H__

enum rf212_registers_enum
{
	RF212_TRX_STATUS = 0x01,
	RF212_TRX_STATE = 0x02,
	RF212_TRX_CTRL_0 = 0x03,
	RF212_TRX_CTRL_1 = 0x04,
	RF212_PHY_TX_PWR = 0x05,
	RF212_PHY_RSSI = 0x06,
	RF212_PHY_ED_LEVEL = 0x07,
	RF212_PHY_CC_CCA = 0x08,
	RF212_CCA_THRES = 0x09,
	RF212_IRQ_MASK = 0x0E,
	RF212_IRQ_STATUS = 0x0F,
	RF212_VREG_CTRL = 0x10,
	RF212_BATMON = 0x11,
	RF212_XOSC_CTRL = 0x12,
	RF212_PLL_CF = 0x1A,
	RF212_PLL_DCU = 0x1B,
	RF212_PART_NUM = 0x1C,
	RF212_VERSION_NUM = 0x1D,
	RF212_MAN_ID_0 = 0x1E,
	RF212_MAN_ID_1 = 0x1F,
	RF212_SHORT_ADDR_0 = 0x20,
	RF212_SHORT_ADDR_1 = 0x21,
	RF212_PAN_ID_0 = 0x22,
	RF212_PAN_ID_1 = 0x23,
	RF212_IEEE_ADDR_0 = 0x24,
	RF212_IEEE_ADDR_1 = 0x25,
	RF212_IEEE_ADDR_2 = 0x26,
	RF212_IEEE_ADDR_3 = 0x27,
	RF212_IEEE_ADDR_4 = 0x28,
	RF212_IEEE_ADDR_5 = 0x29,
	RF212_IEEE_ADDR_6 = 0x2A,
	RF212_IEEE_ADDR_7 = 0x2B,
	RF212_XAH_CTRL = 0x2C,
	RF212_CSMA_SEED_0 = 0x2D,
	RF212_CSMA_SEED_1 = 0x2E,
};

enum rf212_trx_status_enums
{
	RF212_CCA_DONE = 1 << 7,
	RF212_CCA_STATUS = 1 << 6,
	RF212_TRX_STATUS_MASK = 0x1F,
	RF212_P_ON = 0,
	RF212_BUSY_RX = 1,
	RF212_BUSY_TX = 2,
	RF212_RX_ON = 6,
	RF212_TRX_OFF = 8,
	RF212_PLL_ON = 9,
	RF212_SLEEP = 15,
	RF212_BUSY_RX_AACK = 16,
	RF212_BUSR_TX_ARET = 17,
	RF212_RX_AACK_ON = 22,
	RF212_TX_ARET_ON = 25,
	RF212_RX_ON_NOCLK = 28,
	RF212_AACK_ON_NOCLK = 29,
	RF212_BUSY_RX_AACK_NOCLK = 30,
	RF212_STATE_TRANSITION_IN_PROGRESS = 31,
};

enum rf212_trx_state_enums
{
	RF212_TRAC_STATUS_MASK = 0xE0,
	RF212_TRAC_SUCCESS = 0,
	RF212_TRAC_CHANNEL_ACCESS_FAILURE = 3 << 5,
	RF212_TRAC_NO_ACK = 5 << 5,
	RF212_TRX_CMD_MASK = 0x1F,
	RF212_NOP = 0,
	RF212_TX_START = 2,
	RF212_FORCE_TRX_OFF = 3,
};

enum rf212_phy_rssi_enums
{
	RF212_RX_CRC_VALID = 1 << 7,
	RF212_RSSI_MASK = 0x1F,
};

enum rf212_phy_cc_cca_enums
{
	RF212_CCA_REQUEST = 1 << 7,
	RF212_CCA_MODE_0 = 0 << 5,
	RF212_CCA_MODE_1 = 1 << 5,
	RF212_CCA_MODE_2 = 2 << 5,
	RF212_CCA_MODE_3 = 3 << 5,
	RF212_CHANNEL_DEFAULT = 11,
	RF212_CHANNEL_MASK = 0x1F,
};

enum rf212_irq_register_enums
{
	RF212_IRQ_BAT_LOW = 1 << 7,
	RF212_IRQ_TRX_UR = 1 << 6,
	RF212_IRQ_AMI = 1 << 5,
	RF212_IRQ_CCA_ED_DONE = 1 << 4,
	RF212_IRQ_TRX_END = 1 << 3,
	RF212_IRQ_RX_START = 1 << 2,
	RF212_IRQ_PLL_UNLOCK = 1 << 1,
	RF212_IRQ_PLL_LOCK = 1 << 0,
};

enum rf212_batmon_enums
{
	RF212_BATMON_OK = 1 << 5,
	RF212_BATMON_VHR = 1 << 4,
	RF212_BATMON_VTH_MASK = 0x0F,
};

enum rf212_vreg_ctrl_enums
{
	RF212_AVREG_EXT = 1 << 7,
	RF212_AVDD_OK = 1 << 6,
	RF212_DVREG_EXT = 1 << 3,
	RF212_DVDD_OK = 1 << 2,
};

enum rf212_xosc_ctrl_enums
{
	RF212_XTAL_MODE_OFF = 0 << 4,
	RF212_XTAL_MODE_EXTERNAL = 4 << 4,
	RF212_XTAL_MODE_INTERNAL = 15 << 4,
};

enum rf212_spi_command_enums
{
	RF212_CMD_REGISTER_READ = 0x80,
	RF212_CMD_REGISTER_WRITE = 0xC0,
	RF212_CMD_REGISTER_MASK = 0x3F,
	RF212_CMD_FRAME_READ = 0x20,
	RF212_CMD_FRAME_WRITE = 0x60,
	RF212_CMD_SRAM_READ = 0x00,
	RF212_CMD_SRAM_WRITE = 0x40,
};

#endif//__RF212_H__

--- NEW FILE: RF212ActiveMessageC.nc ---
/*
 * Copyright (c) 2007, Vanderbilt University
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author: Miklos Maroti
 */

#include <RadioConfig.h>

configuration RF212ActiveMessageC
{
	provides 
	{
		interface SplitControl;

		interface AMSend[am_id_t id];
		interface Receive[am_id_t id];
		interface Receive as Snoop[am_id_t id];

		interface Packet;
		interface AMPacket;
		interface PacketAcknowledgements;
		interface LowPowerListening;
		interface RadioChannel;

		interface PacketField<uint8_t> as PacketLinkQuality;
		interface PacketField<uint8_t> as PacketTransmitPower;
		interface PacketField<uint8_t> as PacketRSSI;

		interface PacketTimeStamp<TRadio, uint32_t> as PacketTimeStampRadio;
		interface PacketTimeStamp<TMilli, uint32_t> as PacketTimeStampMilli;
	}
}

implementation
{
	components RF212ActiveMessageP, RF212PacketC, IEEE154PacketC, RadioAlarmC;

#ifdef RADIO_DEBUG
	components AssertC;
#endif

	RF212ActiveMessageP.IEEE154Packet -> IEEE154PacketC;
	RF212ActiveMessageP.Packet -> RF212PacketC;
	RF212ActiveMessageP.RadioAlarm -> RadioAlarmC.RadioAlarm[unique("RadioAlarm")];

	Packet = RF212PacketC;
	AMPacket = RF212PacketC;
	PacketAcknowledgements = RF212PacketC;
	PacketLinkQuality = RF212PacketC.PacketLinkQuality;
	PacketTransmitPower = RF212PacketC.PacketTransmitPower;
	PacketRSSI = RF212PacketC.PacketRSSI;
	PacketTimeStampRadio = RF212PacketC;
	PacketTimeStampMilli = RF212PacketC;
	LowPowerListening = LowPowerListeningLayerC;
	RadioChannel = MessageBufferLayerC;

	components ActiveMessageLayerC;
#ifdef TFRAMES_ENABLED
	components new DummyLayerC() as IEEE154NetworkLayerC;
#else
	components IEEE154NetworkLayerC;
#endif
#ifdef LOW_POWER_LISTENING
	components LowPowerListeningLayerC;
#else	
	components new DummyLayerC() as LowPowerListeningLayerC;
#endif
	components MessageBufferLayerC;
	components UniqueLayerC;
	components TrafficMonitorLayerC;
#ifdef SLOTTED_MAC
	components SlottedCollisionLayerC as CollisionAvoidanceLayerC;
#else
	components RandomCollisionLayerC as CollisionAvoidanceLayerC;
#endif
	components SoftwareAckLayerC;
	components new DummyLayerC() as CsmaLayerC;
	components RF212DriverLayerC;

	SplitControl = LowPowerListeningLayerC;
	AMSend = ActiveMessageLayerC;
	Receive = ActiveMessageLayerC.Receive;
	Snoop = ActiveMessageLayerC.Snoop;

	ActiveMessageLayerC.Config -> RF212ActiveMessageP;
	ActiveMessageLayerC.AMPacket -> IEEE154PacketC;
	ActiveMessageLayerC.SubSend -> IEEE154NetworkLayerC;
	ActiveMessageLayerC.SubReceive -> IEEE154NetworkLayerC;

	IEEE154NetworkLayerC.SubSend -> UniqueLayerC;
	IEEE154NetworkLayerC.SubReceive -> LowPowerListeningLayerC;

	// the UniqueLayer is wired at two points
	UniqueLayerC.Config -> RF212ActiveMessageP;
	UniqueLayerC.SubSend -> LowPowerListeningLayerC;

	LowPowerListeningLayerC.SubControl -> MessageBufferLayerC;
	LowPowerListeningLayerC.SubSend -> MessageBufferLayerC;
	LowPowerListeningLayerC.SubReceive -> MessageBufferLayerC;
#ifdef LOW_POWER_LISTENING
	LowPowerListeningLayerC.PacketSleepInterval -> RF212PacketC;
	LowPowerListeningLayerC.IEEE154Packet -> IEEE154PacketC;
	LowPowerListeningLayerC.PacketAcknowledgements -> RF212PacketC;
#endif

	MessageBufferLayerC.Packet -> RF212PacketC;
	MessageBufferLayerC.RadioSend -> TrafficMonitorLayerC;
	MessageBufferLayerC.RadioReceive -> UniqueLayerC;
	MessageBufferLayerC.RadioState -> TrafficMonitorLayerC;

	UniqueLayerC.SubReceive -> TrafficMonitorLayerC;

	TrafficMonitorLayerC.Config -> RF212ActiveMessageP;
	TrafficMonitorLayerC.SubSend -> CollisionAvoidanceLayerC;
	TrafficMonitorLayerC.SubReceive -> CollisionAvoidanceLayerC;
	TrafficMonitorLayerC.SubState -> RF212DriverLayerC;

	CollisionAvoidanceLayerC.Config -> RF212ActiveMessageP;
	CollisionAvoidanceLayerC.SubSend -> SoftwareAckLayerC;
	CollisionAvoidanceLayerC.SubReceive -> SoftwareAckLayerC;

	SoftwareAckLayerC.Config -> RF212ActiveMessageP;
	SoftwareAckLayerC.SubSend -> CsmaLayerC;
	SoftwareAckLayerC.SubReceive -> RF212DriverLayerC;

	CsmaLayerC.Config -> RF212ActiveMessageP;
	CsmaLayerC -> RF212DriverLayerC.RadioSend;
	CsmaLayerC -> RF212DriverLayerC.RadioCCA;

	RF212DriverLayerC.RF212DriverConfig -> RF212ActiveMessageP;
}

--- NEW FILE: RF212ActiveMessageP.nc ---
/*
 * Copyright (c) 2007, Vanderbilt University
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author: Miklos Maroti
 */

#include <RF212Packet.h>
#include <RadioConfig.h>
#include <Tasklet.h>

module RF212ActiveMessageP
{
	provides
	{
		interface RF212DriverConfig;
		interface SoftwareAckConfig;
		interface UniqueConfig;
		interface CsmaConfig;
		interface TrafficMonitorConfig;
		interface RandomCollisionConfig;
		interface SlottedCollisionConfig;
		interface ActiveMessageConfig;
		interface DummyConfig;
	}

	uses
	{
		interface IEEE154Packet;
		interface Packet;
		interface RadioAlarm;
	}
}

implementation
{
/*----------------- RF212DriverConfig -----------------*/

	async command uint8_t RF212DriverConfig.getLength(message_t* msg)
	{
		return call IEEE154Packet.getLength(msg);
	}

	async command void RF212DriverConfig.setLength(message_t* msg, uint8_t len)
	{
		call IEEE154Packet.setLength(msg, len);
	}

	async command uint8_t* RF212DriverConfig.getPayload(message_t* msg)
	{
		return ((uint8_t*)(call IEEE154Packet.getHeader(msg))) + 1;
	}

	inline rf212packet_metadata_t* getMeta(message_t* msg)
	{
		return (rf212packet_metadata_t*)(msg->metadata);
	}

	async command uint8_t RF212DriverConfig.getHeaderLength()
	{
		// we need the fcf, dsn, destpan and dest
		return 7;
	}

	async command uint8_t RF212DriverConfig.getMaxLength()
	{
		// note, that the ieee154_footer_t is not stored, but we should include it here
		return sizeof(rf212packet_header_t) - 1 + TOSH_DATA_LENGTH + sizeof(ieee154_footer_t);
	}

	async command uint8_t RF212DriverConfig.getDefaultChannel()
	{
		return RF212_DEF_CHANNEL;
	}

	async command bool RF212DriverConfig.requiresRssiCca(message_t* msg)
	{
		return call IEEE154Packet.isDataFrame(msg);
	}

/*----------------- SoftwareAckConfig -----------------*/

	async command bool SoftwareAckConfig.requiresAckWait(message_t* msg)
	{
		return call IEEE154Packet.requiresAckWait(msg);
	}

	async command bool SoftwareAckConfig.isAckPacket(message_t* msg)
	{
		return call IEEE154Packet.isAckFrame(msg);
	}

	async command bool SoftwareAckConfig.verifyAckPacket(message_t* data, message_t* ack)
	{
		return call IEEE154Packet.verifyAckReply(data, ack);
	}

	async command bool SoftwareAckConfig.requiresAckReply(message_t* msg)
	{
		return call IEEE154Packet.requiresAckReply(msg);
	}

	async command void SoftwareAckConfig.createAckPacket(message_t* data, message_t* ack)
	{
		call IEEE154Packet.createAckReply(data, ack);
	}

	async command void SoftwareAckConfig.setAckReceived(message_t* msg, bool acked)
	{
		if( acked )
			getMeta(msg)->flags |= RF212PACKET_WAS_ACKED;
		else
			getMeta(msg)->flags &= ~RF212PACKET_WAS_ACKED;
	}

	async command uint16_t SoftwareAckConfig.getAckTimeout()
	{
		return (uint16_t)(800 * RADIO_ALARM_MICROSEC);
	}

	tasklet_async command void SoftwareAckConfig.reportChannelError()
	{
		signal TrafficMonitorConfig.channelError();
	}

/*----------------- UniqueConfig -----------------*/

	async command uint8_t UniqueConfig.getSequenceNumber(message_t* msg)
	{
		return call IEEE154Packet.getDSN(msg);
	}

	async command void UniqueConfig.setSequenceNumber(message_t* msg, uint8_t dsn)
	{
		call IEEE154Packet.setDSN(msg, dsn);
	}

	async command am_addr_t UniqueConfig.getSender(message_t* msg)
	{
		return call IEEE154Packet.getSrcAddr(msg);
	}

	tasklet_async command void UniqueConfig.reportChannelError()
	{
		signal TrafficMonitorConfig.channelError();
	}

/*----------------- ActiveMessageConfig -----------------*/

	command error_t ActiveMessageConfig.checkPacket(message_t* msg)
	{
		// the user forgot to call clear, we should return EINVAL
		if( ! call IEEE154Packet.isDataFrame(msg) )
			call Packet.clear(msg);

		return SUCCESS;
	}

/*----------------- CsmaConfig -----------------*/

	async command bool CsmaConfig.requiresSoftwareCCA(message_t* msg)
	{
		return call IEEE154Packet.isDataFrame(msg);
	}

/*----------------- TrafficMonitorConfig -----------------*/

	enum
	{
		TRAFFIC_UPDATE_PERIOD = 100,	// in milliseconds
		TRAFFIC_MAX_BYTES = (uint16_t)(TRAFFIC_UPDATE_PERIOD * 1000UL / 32),	// 3125
	};

	async command uint16_t TrafficMonitorConfig.getUpdatePeriod()
	{
		return TRAFFIC_UPDATE_PERIOD;
	}

	async command uint16_t TrafficMonitorConfig.getChannelTime(message_t* msg)
	{
		/* We count in bytes, one byte is 32 microsecond. We are conservative here.
		 *
		 * pure airtime: preable (4 bytes), SFD (1 byte), length (1 byte), payload + CRC (len bytes)
		 * frame separation: 5-10 bytes
		 * ack required: 8-16 byte separation, 11 bytes airtime, 5-10 bytes separation
		 */

		uint8_t len = call IEEE154Packet.getLength(msg);
		return call IEEE154Packet.getAckRequired(msg) ? len + 6 + 16 + 11 + 10 : len + 6 + 10;
	}

	async command am_addr_t TrafficMonitorConfig.getSender(message_t* msg)
	{
		return call IEEE154Packet.getSrcAddr(msg);
	}

	tasklet_async command void TrafficMonitorConfig.timerTick()
	{
		signal SlottedCollisionConfig.timerTick();
	}

/*----------------- RandomCollisionConfig -----------------*/

	/*
	 * We try to use the same values as in CC2420
	 *
	 * CC2420_MIN_BACKOFF = 10 jiffies = 320 microsec
	 * CC2420_BACKOFF_PERIOD = 10 jiffies
	 * initial backoff = 0x1F * CC2420_BACKOFF_PERIOD = 310 jiffies = 9920 microsec
	 * congestion backoff = 0x7 * CC2420_BACKOFF_PERIOD = 70 jiffies = 2240 microsec
	 */

	async command uint16_t RandomCollisionConfig.getMinimumBackoff()
	{
		return (uint16_t)(320 * RADIO_ALARM_MICROSEC);
	}

	async command uint16_t RandomCollisionConfig.getInitialBackoff(message_t* msg)
	{
		return (uint16_t)(9920 * RADIO_ALARM_MICROSEC);
	}

	async command uint16_t RandomCollisionConfig.getCongestionBackoff(message_t* msg)
	{
		return (uint16_t)(2240 * RADIO_ALARM_MICROSEC);
	}

	async command uint16_t RandomCollisionConfig.getTransmitBarrier(message_t* msg)
	{
		uint16_t time;

		// TODO: maybe we should use the embedded timestamp of the message
		time = call RadioAlarm.getNow();

		// estimated response time (download the message, etc) is 5-8 bytes
		if( call IEEE154Packet.requiresAckReply(msg) )
			time += (uint16_t)(32 * (-5 + 16 + 11 + 5) * RADIO_ALARM_MICROSEC);
		else
			time += (uint16_t)(32 * (-5 + 5) * RADIO_ALARM_MICROSEC);

		return time;
	}

	tasklet_async event void RadioAlarm.fired()	{ }

/*----------------- SlottedCollisionConfig -----------------*/

	async command uint16_t SlottedCollisionConfig.getInitialDelay()
	{
		return 300;
	}

	async command uint8_t SlottedCollisionConfig.getScheduleExponent()
	{
		return 1 + RADIO_ALARM_MILLI_EXP;
	}

	async command uint16_t SlottedCollisionConfig.getTransmitTime(message_t* msg)
	{
		// TODO: check if the timestamp is correct
		return getMeta(msg)->timestamp;
	}

	async command uint16_t SlottedCollisionConfig.getCollisionWindowStart(message_t* msg)
	{
		// the preamble (4 bytes), SFD (1 byte), plus two extra for safety
		return getMeta(msg)->timestamp - (uint16_t)(7 * 32 * RADIO_ALARM_MICROSEC);
	}

	async command uint16_t SlottedCollisionConfig.getCollisionWindowLength(message_t* msg)
	{
		return (uint16_t)(2 * 7 * 32 * RADIO_ALARM_MICROSEC);
	}

	default tasklet_async event void SlottedCollisionConfig.timerTick() { }

/*----------------- Dummy -----------------*/

	async command void DummyConfig.nothing()
	{
	}
}

--- NEW FILE: RF212DriverConfig.nc ---
/*
 * Copyright (c) 2007, Vanderbilt University
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author: Miklos Maroti
 */

/**
 * This interface needs to be implemented by the MAC to control the behaviour 
 * of the RF212DriverLayerC component.
 */
interface RF212DriverConfig
{
	/**
	 * Returns the length of the PHY payload (including the FCF field).
	 * This value must be in the range [3, 127].
	 */
	async command uint8_t getLength(message_t* msg);

	/**
	 * Sets the length of the PHY payload.
	 */
	async command void setLength(message_t* msg, uint8_t len);

	/**
	 * Returns a pointer to the start of the PHY payload that contains 
	 * getLength()-2 number of bytes. The FCF field (CRC-16) is not stored,
	 * but automatically appended / verified.
	 */
	async command uint8_t* getPayload(message_t* msg);

	/**
	 * Gets the number of bytes we should read before the RadioReceive.header
	 * event is fired. If the length of the packet is less than this amount, 
	 * then that event is fired earlier. The header length must be at least one.
	 */
	async command uint8_t getHeaderLength();

	/**
	 * Returns the maximum PHY length that can be set via the setLength command
	 */
	async command uint8_t getMaxLength();

	/**
	 * This command is used at power up to set the default channel.
	 * The default CC2420 channel is 26.
	 */
	async command uint8_t getDefaultChannel();

	/**
	 * Returns TRUE if before sending this message we should make sure that
	 * the channel is clear via a very basic (and quick) RSSI check.
	 */
	async command bool requiresRssiCca(message_t* msg);
}

--- NEW FILE: RF212DriverLayerC.nc ---
/*
 * Copyright (c) 2007, Vanderbilt University
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author: Miklos Maroti
 */

configuration RF212DriverLayerC
{
	provides
	{
		interface RadioState;
		interface RadioSend;
		interface RadioReceive;
		interface RadioCCA;
	}

	uses interface RF212DriverConfig;
}

implementation
{
	components RF212DriverLayerP, HplRF212C, BusyWaitMicroC, TaskletC, MainC, RadioAlarmC, RF212PacketC, LocalTimeMicroC as LocalTimeRadioC;

	RadioState = RF212DriverLayerP;
	RadioSend = RF212DriverLayerP;
	RadioReceive = RF212DriverLayerP;
	RadioCCA = RF212DriverLayerP;

	RF212DriverConfig = RF212DriverLayerP;

	RF212DriverLayerP.PacketLinkQuality -> RF212PacketC.PacketLinkQuality;
	RF212DriverLayerP.PacketTransmitPower -> RF212PacketC.PacketTransmitPower;
	RF212DriverLayerP.PacketRSSI -> RF212PacketC.PacketRSSI;
	RF212DriverLayerP.PacketTimeSyncOffset -> RF212PacketC.PacketTimeSyncOffset;
	RF212DriverLayerP.PacketTimeStamp -> RF212PacketC;
	RF212DriverLayerP.LocalTime -> LocalTimeRadioC;

	RF212DriverLayerP.RadioAlarm -> RadioAlarmC.RadioAlarm[unique("RadioAlarm")];
	RadioAlarmC.Alarm -> HplRF212C.Alarm;

	RF212DriverLayerP.SELN -> HplRF212C.SELN;
	RF212DriverLayerP.SpiResource -> HplRF212C.SpiResource;
	RF212DriverLayerP.FastSpiByte -> HplRF212C;

	RF212DriverLayerP.SLP_TR -> HplRF212C.SLP_TR;
	RF212DriverLayerP.RSTN -> HplRF212C.RSTN;

	RF212DriverLayerP.IRQ -> HplRF212C.IRQ;
	RF212DriverLayerP.Tasklet -> TaskletC;
	RF212DriverLayerP.BusyWait -> BusyWaitMicroC;

#ifdef RADIO_DEBUG
	components DiagMsgC;
	RF212DriverLayerP.DiagMsg -> DiagMsgC;
#endif

	MainC.SoftwareInit -> RF212DriverLayerP.SoftwareInit;

	components RealMainP;
	RealMainP.PlatformInit -> RF212DriverLayerP.PlatformInit;
}

--- NEW FILE: RF212DriverLayerP-comp.nc ---
/*
 * Copyright (c) 2007, Vanderbilt University
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author: Miklos Maroti
 */

#include <RF230.h>
#include <Tasklet.h>
#include <RadioAssert.h>
#include <GenericTimeSyncMessage.h>
#include <RadioConfig.h>

module RF230DriverLayerP
{
	provides
	{
		interface Init as PlatformInit @exactlyonce();
		interface Init as SoftwareInit @exactlyonce();

		interface RadioState;
		interface RadioSend;
		interface RadioReceive;
		interface RadioCCA;
	}

	uses
	{
		interface GeneralIO as SELN;
		interface Resource as SpiResource;

		interface FastSpiByte;

		interface GeneralIO as SLP_TR;
		interface GeneralIO as RSTN;

		interface GpioCapture as IRQ;

		interface BusyWait<TMicro, uint16_t>;

		interface PacketField<uint8_t> as PacketLinkQuality;
		interface PacketField<uint8_t> as PacketTransmitPower;
		interface PacketField<uint8_t> as PacketRSSI;
		interface PacketField<uint8_t> as PacketTimeSyncOffset;

		interface PacketTimeStamp<TRadio, uint32_t>;
		interface LocalTime<TRadio>;

		interface RF230DriverConfig;
		interface Tasklet;
		interface RadioAlarm;

#ifdef RADIO_DEBUG
		interface DiagMsg;
#endif
	}
}

implementation
{
/*----------------- STATE -----------------*/

	tasklet_norace uint8_t state;
	enum
	{
		STATE_P_ON = 0,
		STATE_SLEEP = 1,
		STATE_SLEEP_2_TRX_OFF = 2,
		STATE_TRX_OFF = 3,
		STATE_TRX_OFF_2_RX_ON = 4,
		STATE_RX_ON = 5,
		STATE_BUSY_TX_2_RX_ON = 6,
		STATE_PLL_ON_2_RX_ON = 7,
	};

	tasklet_norace uint8_t cmd;
	enum
	{
		CMD_NONE = 0,			// the state machine has stopped
		CMD_TURNOFF = 1,		// goto SLEEP state
		CMD_STANDBY = 2,		// goto TRX_OFF state
		CMD_TURNON = 3,			// goto RX_ON state
		CMD_TRANSMIT = 4,		// currently transmitting a message
		CMD_RECEIVE = 5,		// currently receiving a message
		CMD_CCA = 6,			// performing clear chanel assesment
		CMD_CHANNEL = 7,		// changing the channel
		CMD_SIGNAL_DONE = 8,		// signal the end of the state transition
		CMD_DOWNLOAD = 9,		// download the received message
	};

	norace bool radioIrq;

	tasklet_norace uint8_t txPower;
	tasklet_norace uint8_t channel;

	tasklet_norace message_t* rxMsg;
	message_t rxMsgBuffer;

	uint16_t capturedTime;	// the current time when the last interrupt has occured

	tasklet_norace uint8_t rssiClear;
	tasklet_norace uint8_t rssiBusy;

/*----------------- REGISTER -----------------*/

	inline void writeRegister(uint8_t reg, uint8_t value)
	{
		ASSERT( call SpiResource.isOwner() );
		ASSERT( reg == (reg & RF230_CMD_REGISTER_MASK) );

		call SELN.clr();
		call FastSpiByte.splitWrite(RF230_CMD_REGISTER_WRITE | reg);
		call FastSpiByte.splitReadWrite(value);
		call FastSpiByte.splitRead();
		call SELN.set();
	}

	inline uint8_t readRegister(uint8_t reg)
	{
		ASSERT( call SpiResource.isOwner() );
		ASSERT( reg == (reg & RF230_CMD_REGISTER_MASK) );

		call SELN.clr();
		call FastSpiByte.splitWrite(RF230_CMD_REGISTER_READ | reg);
		call FastSpiByte.splitReadWrite(0);
		reg = call FastSpiByte.splitRead();
		call SELN.set();

		return reg;
	}

/*----------------- ALARM -----------------*/

	enum
	{
		SLEEP_WAKEUP_TIME = (uint16_t)(880 * RADIO_ALARM_MICROSEC),
		CCA_REQUEST_TIME = (uint16_t)(140 * RADIO_ALARM_MICROSEC),

		TX_SFD_DELAY = (uint16_t)(176 * RADIO_ALARM_MICROSEC),
		RX_SFD_DELAY = (uint16_t)(8 * RADIO_ALARM_MICROSEC),
	};

	tasklet_async event void RadioAlarm.fired()
	{
		if( state == STATE_SLEEP_2_TRX_OFF )
			state = STATE_TRX_OFF;
		else if( cmd == CMD_CCA )
		{
			uint8_t cca;

			ASSERT( state == STATE_RX_ON );

			cmd = CMD_NONE;
			cca = readRegister(RF230_TRX_STATUS);

			ASSERT( (cca & RF230_TRX_STATUS_MASK) == RF230_RX_ON );

			signal RadioCCA.done( (cca & RF230_CCA_DONE) ? ((cca & RF230_CCA_STATUS) ? SUCCESS : EBUSY) : FAIL );
		}
		else
			ASSERT(FALSE);

		// make sure the rest of the command processing is called
		call Tasklet.schedule();
	}

/*----------------- INIT -----------------*/

	command error_t PlatformInit.init()
	{
		call SELN.makeOutput();
		call SELN.set();
		call SLP_TR.makeOutput();
		call SLP_TR.clr();
		call RSTN.makeOutput();
		call RSTN.set();

		rxMsg = &rxMsgBuffer;

		// these are just good approximates
		rssiClear = 0;
		rssiBusy = 90;

		return SUCCESS;
	}

	command error_t SoftwareInit.init()
	{
		// for powering up the radio
		return call SpiResource.request();
	}

	void initRadio()
	{
		call BusyWait.wait(510);

		call RSTN.clr();
		call SLP_TR.clr();
		call BusyWait.wait(6);
		call RSTN.set();

		writeRegister(RF230_TRX_CTRL_0, RF230_TRX_CTRL_0_VALUE);
		writeRegister(RF230_TRX_STATE, RF230_TRX_OFF);

		call BusyWait.wait(510);

		writeRegister(RF230_IRQ_MASK, RF230_IRQ_TRX_UR | RF230_IRQ_PLL_LOCK | RF230_IRQ_TRX_END | RF230_IRQ_RX_START);
		writeRegister(RF230_CCA_THRES, RF230_CCA_THRES_VALUE);
		writeRegister(RF230_PHY_TX_PWR, RF230_DEF_RFPOWER);

		txPower = RF230_DEF_RFPOWER;
		channel = call RF230DriverConfig.getDefaultChannel() & RF230_CHANNEL_MASK;
		writeRegister(RF230_PHY_CC_CCA, RF230_CCA_MODE_VALUE | channel);

		call SLP_TR.set();
		state = STATE_SLEEP;
	}

/*----------------- SPI -----------------*/

	event void SpiResource.granted()
	{
		call SELN.makeOutput();
		call SELN.set();

		if( state == STATE_P_ON )
		{
			initRadio();
			call SpiResource.release();
		}
		else
			call Tasklet.schedule();
	}

	bool isSpiAcquired()
	{
		if( call SpiResource.isOwner() )
			return TRUE;

		if( call SpiResource.immediateRequest() == SUCCESS )
		{
			call SELN.makeOutput();
			call SELN.set();

			return TRUE;
		}

		call SpiResource.request();
		return FALSE;
	}

/*----------------- CHANNEL -----------------*/

	tasklet_async command uint8_t RadioState.getChannel()
	{
		return channel;
	}

	tasklet_async command error_t RadioState.setChannel(uint8_t c)
	{
		c &= RF230_CHANNEL_MASK;

		if( cmd != CMD_NONE )
			return EBUSY;
		else if( channel == c )
			return EALREADY;

		channel = c;
		cmd = CMD_CHANNEL;
		call Tasklet.schedule();

		return SUCCESS;
	}

	inline void changeChannel()
	{
		ASSERT( cmd == CMD_CHANNEL );
		ASSERT( state == STATE_SLEEP || state == STATE_TRX_OFF || state == STATE_RX_ON );

		if( isSpiAcquired() )
		{
			writeRegister(RF230_PHY_CC_CCA, RF230_CCA_MODE_VALUE | channel);

			if( state == STATE_RX_ON )
				state = STATE_TRX_OFF_2_RX_ON;
			else
				cmd = CMD_SIGNAL_DONE;
		}
	}

/*----------------- TURN ON/OFF -----------------*/

	inline void changeState()
	{
		if( (cmd == CMD_STANDBY || cmd == CMD_TURNON)
			&& state == STATE_SLEEP && call RadioAlarm.isFree() )
		{
			call SLP_TR.clr();

			call RadioAlarm.wait(SLEEP_WAKEUP_TIME);
			state = STATE_SLEEP_2_TRX_OFF;
		}
		else if( cmd == CMD_TURNON && state == STATE_TRX_OFF && isSpiAcquired() )
		{
			ASSERT( ! radioIrq );

			readRegister(RF230_IRQ_STATUS); // clear the interrupt register
			call IRQ.captureRisingEdge();

			// setChannel was ignored in SLEEP because the SPI was not working, so do it here
			writeRegister(RF230_PHY_CC_CCA, RF230_CCA_MODE_VALUE | channel);

			writeRegister(RF230_TRX_STATE, RF230_RX_ON);
			state = STATE_TRX_OFF_2_RX_ON;
		}
		else if( (cmd == CMD_TURNOFF || cmd == CMD_STANDBY) 
			&& state == STATE_RX_ON && isSpiAcquired() )
		{
			writeRegister(RF230_TRX_STATE, RF230_FORCE_TRX_OFF);

			call IRQ.disable();
			radioIrq = FALSE;

			state = STATE_TRX_OFF;
		}

		if( cmd == CMD_TURNOFF && state == STATE_TRX_OFF )
		{
			call SLP_TR.set();
			state = STATE_SLEEP;
			cmd = CMD_SIGNAL_DONE;
		}
		else if( cmd == CMD_STANDBY && state == STATE_TRX_OFF )
			cmd = CMD_SIGNAL_DONE;
	}

	tasklet_async command error_t RadioState.turnOff()
	{
		if( cmd != CMD_NONE )
			return EBUSY;
		else if( state == STATE_SLEEP )
			return EALREADY;

		cmd = CMD_TURNOFF;
		call Tasklet.schedule();

		return SUCCESS;
	}
	
	tasklet_async command error_t RadioState.standby()
	{
		if( cmd != CMD_NONE || (state == STATE_SLEEP && ! call RadioAlarm.isFree()) )
			return EBUSY;
		else if( state == STATE_TRX_OFF )
			return EALREADY;

		cmd = CMD_STANDBY;
		call Tasklet.schedule();

		return SUCCESS;
	}

	tasklet_async command error_t RadioState.turnOn()
	{
		if( cmd != CMD_NONE || (state == STATE_SLEEP && ! call RadioAlarm.isFree()) )
			return EBUSY;
		else if( state == STATE_RX_ON )
			return EALREADY;

		cmd = CMD_TURNON;
		call Tasklet.schedule();

		return SUCCESS;
	}

	default tasklet_async event void RadioState.done() { }

/*----------------- TRANSMIT -----------------*/

	tasklet_async command error_t RadioSend.send(message_t* msg)
	{
		uint16_t time;
		uint8_t length;
		uint8_t* data;
		uint8_t header;
		uint32_t time32;
		void* timesync;

		if( cmd != CMD_NONE || state != STATE_RX_ON || ! isSpiAcquired() || radioIrq )
			return EBUSY;

		length = call PacketTransmitPower.isSet(msg) ?
			call PacketTransmitPower.get(msg) : RF230_DEF_RFPOWER;

		if( length != txPower )
		{
			txPower = length;
			writeRegister(RF230_PHY_TX_PWR, txPower);
		}

		if( call RF230DriverConfig.requiresRssiCca(msg) 
				&& (readRegister(RF230_PHY_RSSI) & RF230_RSSI_MASK) > ((rssiClear + rssiBusy) >> 3) )
			return EBUSY;

		writeRegister(RF230_TRX_STATE, RF230_PLL_ON);

		// do something useful, just to wait a little
		time32 = call LocalTime.get();
		timesync = call PacketTimeSyncOffset.isSet(msg) ? msg->data + call PacketTimeSyncOffset.get(msg) : 0;

		// we have missed an incoming message in this short amount of time
		if( (readRegister(RF230_TRX_STATUS) & RF230_TRX_STATUS_MASK) != RF230_PLL_ON )
		{
			ASSERT( (readRegister(RF230_TRX_STATUS) & RF230_TRX_STATUS_MASK) == RF230_BUSY_RX );

			state = STATE_PLL_ON_2_RX_ON;
			return EBUSY;
		}

		atomic
		{
			call SLP_TR.set();
			time = call RadioAlarm.getNow() + TX_SFD_DELAY;
		}
		call SLP_TR.clr();

		ASSERT( ! radioIrq );

		call SELN.clr();
		call FastSpiByte.splitWrite(RF230_CMD_FRAME_WRITE);

		length = call RF230DriverConfig.getLength(msg);
		data = call RF230DriverConfig.getPayload(msg);

		// length | data[0] ... data[length-3] | automatically generated FCS
		call FastSpiByte.splitReadWrite(length);

		// the FCS is atomatically generated (2 bytes)
		length -= 2;

		header = call RF230DriverConfig.getHeaderLength();
		if( header > length )
			header = length;

		length -= header;

		// first upload the header to gain some time
		do {
			call FastSpiByte.splitReadWrite(*(data++));
		}
		while( --header != 0 );

		time32 += (int16_t)(time) - (int16_t)(time32);

		if( timesync != 0 )
			*(timesync_relative_t*)timesync = (*(timesync_absolute_t*)timesync) - time32;

		do {
			call FastSpiByte.splitReadWrite(*(data++));
		}
		while( --length != 0 );

		// wait for the SPI transfer to finish
		call FastSpiByte.splitRead();
		call SELN.set();

		/*
		 * There is a very small window (~1 microsecond) when the RF230 went 
		 * into PLL_ON state but was somehow not properly initialized because 
		 * of an incoming message and could not go into BUSY_TX. I think the
		 * radio can even receive a message, and generate a TRX_UR interrupt
		 * because of concurrent access, but that message probably cannot be
		 * recovered.
		 *
		 * TODO: this needs to be verified, and make sure that the chip is 
		 * not locked up in this case.
		 */

		// go back to RX_ON state when finished
		writeRegister(RF230_TRX_STATE, RF230_RX_ON);

#ifdef RADIO_DEBUG_MESSAGES
		if( call DiagMsg.record() )
		{
			length = call RF230DriverConfig.getLength(msg);

			call DiagMsg.str("tx");
			call DiagMsg.uint16(time);
			call DiagMsg.uint8(length);
			call DiagMsg.hex8s(data, length - 2);
			call DiagMsg.send();
		}
#endif

		if( timesync != 0 )
			*(timesync_absolute_t*)timesync = (*(timesync_relative_t*)timesync) + time32;

		call PacketTimeStamp.set(msg, time32);

		// wait for the TRX_END interrupt
		state = STATE_BUSY_TX_2_RX_ON;
		cmd = CMD_TRANSMIT;

		return SUCCESS;
	}

	default tasklet_async event void RadioSend.sendDone(error_t error) { }
	default tasklet_async event void RadioSend.ready() { }

/*----------------- CCA -----------------*/

	tasklet_async command error_t RadioCCA.request()
	{
		if( cmd != CMD_NONE || state != STATE_RX_ON || ! isSpiAcquired() || ! call RadioAlarm.isFree() )
			return EBUSY;

		// see Errata B7 of the datasheet
		// writeRegister(RF230_TRX_STATE, RF230_PLL_ON);
		// writeRegister(RF230_TRX_STATE, RF230_RX_ON);

		writeRegister(RF230_PHY_CC_CCA, RF230_CCA_REQUEST | RF230_CCA_MODE_VALUE | channel);
		call RadioAlarm.wait(CCA_REQUEST_TIME);
		cmd = CMD_CCA;
		
		return SUCCESS;
	}

	default tasklet_async event void RadioCCA.done(error_t error) { }

/*----------------- RECEIVE -----------------*/

	inline void downloadMessage()
	{
		uint8_t length;
		uint8_t crc;

		call SELN.clr();
		call FastSpiByte.write(RF230_CMD_FRAME_READ);

		// read the length byte
		length = call FastSpiByte.write(0);

		// if correct length
		if( length >= 3 && length <= call RF230DriverConfig.getMaxLength() )
		{
			uint8_t read;
			uint8_t* data;

			// initiate the reading
			call FastSpiByte.splitWrite(0);

			call RF230DriverConfig.setLength(rxMsg, length);
			data = call RF230DriverConfig.getPayload(rxMsg);
			crc = 0;

			// we do not store the CRC field
			length -= 2;

			read = call RF230DriverConfig.getHeaderLength();
			if( length < read )
				read = length;

			length -= read;

			do {
				*(data++) = call FastSpiByte.splitReadWrite(0);
			}
			while( --read != 0  );

			if( signal RadioReceive.header(rxMsg) )
			{
				while( length-- != 0 )
					*(data++) = call FastSpiByte.splitReadWrite(0);

				call FastSpiByte.splitReadWrite(0);	// two CRC bytes
				call FastSpiByte.splitReadWrite(0);

				call PacketLinkQuality.set(rxMsg, call FastSpiByte.splitReadWrite(0));
				call FastSpiByte.splitReadWrite(0);	// ED
				crc = call FastSpiByte.splitRead() & RF230_RX_CRC_VALID;	// RX_STATUS
			}
		}

		call SELN.set();
		state = STATE_RX_ON;

#ifdef RADIO_DEBUG_MESSAGES
		if( call DiagMsg.record() )
		{
			length = call RF230DriverConfig.getLength(rxMsg);

			call DiagMsg.str("rx");
			call DiagMsg.uint32(call PacketTimeStamp.isValid(rxMsg) ? call PacketTimeStamp.timestamp(rxMsg) : 0);
			call DiagMsg.uint16(call RadioAlarm.getNow());
			call DiagMsg.uint8(crc != 0);
			call DiagMsg.uint8(length);
			call DiagMsg.hex8s(call RF230DriverConfig.getPayload(rxMsg), length - 2);
			call DiagMsg.send();
		}
#endif
		
		cmd = CMD_NONE;

		// signal only if it has passed the CRC check
		if( crc != 0 )
			rxMsg = signal RadioReceive.receive(rxMsg);
	}

/*----------------- IRQ -----------------*/

	async event void IRQ.captured(uint16_t time)
	{
		ASSERT( ! radioIrq );

		atomic
		{
			capturedTime = time;
			radioIrq = TRUE;
		}

		call Tasklet.schedule();
	}

	void serviceRadio()
	{
		if( isSpiAcquired() )
		{
			uint16_t time;
			uint32_t time32;
			uint8_t irq;
			uint8_t temp;
			
			atomic time = capturedTime;
			radioIrq = FALSE;
			irq = readRegister(RF230_IRQ_STATUS);

#ifdef RADIO_DEBUG
			// TODO: handle this interrupt
			if( irq & RF230_IRQ_TRX_UR )
			{
				if( call DiagMsg.record() )
				{
					call DiagMsg.str("assert ur");
					call DiagMsg.uint16(call RadioAlarm.getNow());
					call DiagMsg.hex8(readRegister(RF230_TRX_STATUS));
					call DiagMsg.hex8(readRegister(RF230_TRX_STATE));
					call DiagMsg.hex8(irq);
					call DiagMsg.uint8(state);
					call DiagMsg.uint8(cmd);
					call DiagMsg.send();
				}
			}
#endif

			if( irq & RF230_IRQ_PLL_LOCK )
			{
				if( cmd == CMD_TURNON || cmd == CMD_CHANNEL )
				{
					ASSERT( state == STATE_TRX_OFF_2_RX_ON );

					state = STATE_RX_ON;
					cmd = CMD_SIGNAL_DONE;
				}
				else if( cmd == CMD_TRANSMIT )
				{
					ASSERT( state == STATE_BUSY_TX_2_RX_ON );
				}
				else
					ASSERT(FALSE);
			}

			if( irq & RF230_IRQ_RX_START )
			{
				if( cmd == CMD_CCA )
				{
					signal RadioCCA.done(FAIL);
					cmd = CMD_NONE;
				}

				if( cmd == CMD_NONE )
				{
					ASSERT( state == STATE_RX_ON || state == STATE_PLL_ON_2_RX_ON );

					// the most likely place for busy channel, with no TRX_END interrupt
					if( irq == RF230_IRQ_RX_START )
					{
						temp = readRegister(RF230_PHY_RSSI) & RF230_RSSI_MASK;
						rssiBusy += temp - (rssiBusy >> 2);
#ifndef RF230_RSSI_ENERGY
						call PacketRSSI.set(rxMsg, temp);
					}
					else
					{
						call PacketRSSI.clear(rxMsg);
#endif
					}

					/*
					 * The timestamp corresponds to the first event which could not
					 * have been a PLL_LOCK because then cmd != CMD_NONE, so we must
					 * have received a message (and could also have received the 
					 * TRX_END interrupt in the mean time, but that is fine. Also,
					 * we could not be after a transmission, because then cmd = 
					 * CMD_TRANSMIT.
					 */
					if( irq == RF230_IRQ_RX_START ) // just to be cautious
					{
						time32 = call LocalTime.get();
						time32 += (int16_t)(time - RX_SFD_DELAY) - (int16_t)(time32);
						call PacketTimeStamp.set(rxMsg, time32);
					}
					else
						call PacketTimeStamp.clear(rxMsg);

					cmd = CMD_RECEIVE;
				}
				else
					ASSERT( cmd == CMD_TURNOFF );
			}

			if( irq & RF230_IRQ_TRX_END )
			{
				if( cmd == CMD_TRANSMIT )
				{
					ASSERT( state == STATE_BUSY_TX_2_RX_ON );

					state = STATE_RX_ON;
					cmd = CMD_NONE;
					signal RadioSend.sendDone(SUCCESS);

					// TODO: we could have missed a received message
					ASSERT( ! (irq & RF230_IRQ_RX_START) );
				}
				else if( cmd == CMD_RECEIVE )
				{
					ASSERT( state == STATE_RX_ON || state == STATE_PLL_ON_2_RX_ON );
#ifdef RF230_RSSI_ENERGY
					if( irq == RF230_IRQ_TRX_END )
						call PacketRSSI.set(rxMsg, readRegister(RF230_PHY_ED_LEVEL));
					else
						call PacketRSSI.clear(rxMsg);
#endif
					if( state == STATE_PLL_ON_2_RX_ON )
					{
						ASSERT( (readRegister(RF230_TRX_STATUS) & RF230_TRX_STATUS_MASK) == RF230_PLL_ON );

						writeRegister(RF230_TRX_STATE, RF230_RX_ON);
						state = STATE_RX_ON;
					}
					else
					{
						// the most likely place for clear channel (hope to avoid acks)
						rssiClear += (readRegister(RF230_PHY_RSSI) & RF230_RSSI_MASK) - (rssiClear >> 2);
					}

					cmd = CMD_DOWNLOAD;
				}
				else
					ASSERT(FALSE);
			}
		}
	}

	default tasklet_async event bool RadioReceive.header(message_t* msg)
	{
		return TRUE;
	}

	default tasklet_async event message_t* RadioReceive.receive(message_t* msg)
	{
		return msg;
	}

/*----------------- TASKLET -----------------*/

	tasklet_async event void Tasklet.run()
	{
		if( radioIrq )
			serviceRadio();

		if( cmd != CMD_NONE )
		{
			if( cmd == CMD_DOWNLOAD )
				downloadMessage();
			else if( CMD_TURNOFF <= cmd && cmd <= CMD_TURNON )
				changeState();
			else if( cmd == CMD_CHANNEL )
				changeChannel();
			
			if( cmd == CMD_SIGNAL_DONE )
			{
				cmd = CMD_NONE;
				signal RadioState.done();
			}
		}

		if( cmd == CMD_NONE && state == STATE_RX_ON && ! radioIrq )
			signal RadioSend.ready();

		if( cmd == CMD_NONE )
			call SpiResource.release();
	}
}

--- NEW FILE: RF212DriverLayerP.nc ---
/*
 * Copyright (c) 2007, Vanderbilt University
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author: Miklos Maroti
 */

#include <RF212.h>
#include <Tasklet.h>
#include <RadioAssert.h>
#include <GenericTimeSyncMessage.h>
#include <RadioConfig.h>

module RF212DriverLayerP
{
	provides
	{
		interface Init as PlatformInit @exactlyonce();
		interface Init as SoftwareInit @exactlyonce();

		interface RadioState;
		interface RadioSend;
		interface RadioReceive;
		interface RadioCCA;
	}

	uses
	{
		interface GeneralIO as SELN;
		interface Resource as SpiResource;

		interface FastSpiByte;

		interface GeneralIO as SLP_TR;
		interface GeneralIO as RSTN;

		interface GpioCapture as IRQ;

		interface BusyWait<TMicro, uint16_t>;

		interface PacketField<uint8_t> as PacketLinkQuality;
		interface PacketField<uint8_t> as PacketTransmitPower;
		interface PacketField<uint8_t> as PacketRSSI;
		interface PacketField<uint8_t> as PacketTimeSyncOffset;

		interface PacketTimeStamp<TRadio, uint32_t>;
		interface LocalTime<TRadio>;

		interface RF212DriverConfig;
		interface Tasklet;
		interface RadioAlarm;

#ifdef RADIO_DEBUG
		interface DiagMsg;
#endif
	}
}

implementation
{
/*----------------- STATE -----------------*/

	tasklet_norace uint8_t state;
	enum
	{
		STATE_P_ON = 0,
		STATE_SLEEP = 1,
		STATE_SLEEP_2_TRX_OFF = 2,
		STATE_TRX_OFF = 3,
		STATE_TRX_OFF_2_RX_ON = 4,
		STATE_RX_ON = 5,
		STATE_BUSY_TX_2_RX_ON = 6,
		STATE_PLL_ON_2_RX_ON = 7,
	};

	tasklet_norace uint8_t cmd;
	enum
	{
		CMD_NONE = 0,			// the state machine has stopped
		CMD_TURNOFF = 1,		// goto SLEEP state
		CMD_STANDBY = 2,		// goto TRX_OFF state
		CMD_TURNON = 3,			// goto RX_ON state
		CMD_TRANSMIT = 4,		// currently transmitting a message
		CMD_RECEIVE = 5,		// currently receiving a message
		CMD_CCA = 6,			// performing clear chanel assesment
		CMD_CHANNEL = 7,		// changing the channel
		CMD_SIGNAL_DONE = 8,		// signal the end of the state transition
		CMD_DOWNLOAD = 9,		// download the received message
	};

	norace bool radioIrq;

	tasklet_norace uint8_t txPower;
	tasklet_norace uint8_t channel;

	tasklet_norace message_t* rxMsg;
	message_t rxMsgBuffer;

	uint16_t capturedTime;	// the current time when the last interrupt has occured

	tasklet_norace uint8_t rssiClear;
	tasklet_norace uint8_t rssiBusy;

/*----------------- REGISTER -----------------*/

	inline void writeRegister(uint8_t reg, uint8_t value)
	{
		ASSERT( call SpiResource.isOwner() );
		ASSERT( reg == (reg & RF212_CMD_REGISTER_MASK) );

		call SELN.clr();
		call FastSpiByte.splitWrite(RF212_CMD_REGISTER_WRITE | reg);
		call FastSpiByte.splitReadWrite(value);
		call FastSpiByte.splitRead();
		call SELN.set();
	}

	inline uint8_t readRegister(uint8_t reg)
	{
		ASSERT( call SpiResource.isOwner() );
		ASSERT( reg == (reg & RF212_CMD_REGISTER_MASK) );

		call SELN.clr();
		call FastSpiByte.splitWrite(RF212_CMD_REGISTER_READ | reg);
		call FastSpiByte.splitReadWrite(0);
		reg = call FastSpiByte.splitRead();
		call SELN.set();

		return reg;
	}

/*----------------- ALARM -----------------*/

	enum
	{
		SLEEP_WAKEUP_TIME = (uint16_t)(880 * RADIO_ALARM_MICROSEC),
		CCA_REQUEST_TIME = (uint16_t)(140 * RADIO_ALARM_MICROSEC),

		TX_SFD_DELAY = (uint16_t)(176 * RADIO_ALARM_MICROSEC),
		RX_SFD_DELAY = (uint16_t)(8 * RADIO_ALARM_MICROSEC),
	};

	tasklet_async event void RadioAlarm.fired()
	{
		if( state == STATE_SLEEP_2_TRX_OFF )
			state = STATE_TRX_OFF;
		else if( cmd == CMD_CCA )
		{
			uint8_t cca;

			ASSERT( state == STATE_RX_ON );

			cmd = CMD_NONE;
			cca = readRegister(RF212_TRX_STATUS);

			ASSERT( (cca & RF212_TRX_STATUS_MASK) == RF212_RX_ON );

			signal RadioCCA.done( (cca & RF212_CCA_DONE) ? ((cca & RF212_CCA_STATUS) ? SUCCESS : EBUSY) : FAIL );
		}
		else
			ASSERT(FALSE);

		// make sure the rest of the command processing is called
		call Tasklet.schedule();
	}

/*----------------- INIT -----------------*/

	command error_t PlatformInit.init()
	{
		call SELN.makeOutput();
		call SELN.set();
		call SLP_TR.makeOutput();
		call SLP_TR.clr();
		call RSTN.makeOutput();
		call RSTN.set();

		rxMsg = &rxMsgBuffer;

		// these are just good approximates
		rssiClear = 0;
		rssiBusy = 90;

		return SUCCESS;
	}

	command error_t SoftwareInit.init()
	{
		// for powering up the radio
		return call SpiResource.request();
	}

	void initRadio()
	{
		call BusyWait.wait(510);

		call RSTN.clr();
		call SLP_TR.clr();
		call BusyWait.wait(6);
		call RSTN.set();

		writeRegister(RF212_TRX_CTRL_0, RF212_TRX_CTRL_0_VALUE);
		writeRegister(RF212_TRX_STATE, RF212_TRX_OFF);

		call BusyWait.wait(510);

		writeRegister(RF212_IRQ_MASK, RF212_IRQ_TRX_UR | RF212_IRQ_PLL_LOCK | RF212_IRQ_TRX_END | RF212_IRQ_RX_START);
		writeRegister(RF212_CCA_THRES, RF212_CCA_THRES_VALUE);
		writeRegister(RF212_PHY_TX_PWR, RF212_DEF_RFPOWER);

		txPower = RF212_DEF_RFPOWER;
		channel = call RF212DriverConfig.getDefaultChannel() & RF212_CHANNEL_MASK;
		writeRegister(RF212_PHY_CC_CCA, RF212_CCA_MODE_VALUE | channel);

		call SLP_TR.set();
		state = STATE_SLEEP;
	}

/*----------------- SPI -----------------*/

	event void SpiResource.granted()
	{
		call SELN.makeOutput();
		call SELN.set();

		if( state == STATE_P_ON )
		{
			initRadio();
			call SpiResource.release();
		}
		else
			call Tasklet.schedule();
	}

	bool isSpiAcquired()
	{
		if( call SpiResource.isOwner() )
			return TRUE;

		if( call SpiResource.immediateRequest() == SUCCESS )
		{
			call SELN.makeOutput();
			call SELN.set();

			return TRUE;
		}

		call SpiResource.request();
		return FALSE;
	}

/*----------------- CHANNEL -----------------*/

	tasklet_async command uint8_t RadioState.getChannel()
	{
		return channel;
	}

	tasklet_async command error_t RadioState.setChannel(uint8_t c)
	{
		c &= RF212_CHANNEL_MASK;

		if( cmd != CMD_NONE )
			return EBUSY;
		else if( channel == c )
			return EALREADY;

		channel = c;
		cmd = CMD_CHANNEL;
		call Tasklet.schedule();

		return SUCCESS;
	}

	inline void changeChannel()
	{
		ASSERT( cmd == CMD_CHANNEL );
		ASSERT( state == STATE_SLEEP || state == STATE_TRX_OFF || state == STATE_RX_ON );

		if( isSpiAcquired() )
		{
			writeRegister(RF212_PHY_CC_CCA, RF212_CCA_MODE_VALUE | channel);

			if( state == STATE_RX_ON )
				state = STATE_TRX_OFF_2_RX_ON;
			else
				cmd = CMD_SIGNAL_DONE;
		}
	}

/*----------------- TURN ON/OFF -----------------*/

	inline void changeState()
	{
		if( (cmd == CMD_STANDBY || cmd == CMD_TURNON)
			&& state == STATE_SLEEP && call RadioAlarm.isFree() )
		{
			call SLP_TR.clr();

			call RadioAlarm.wait(SLEEP_WAKEUP_TIME);
			state = STATE_SLEEP_2_TRX_OFF;
		}
		else if( cmd == CMD_TURNON && state == STATE_TRX_OFF && isSpiAcquired() )
		{
			ASSERT( ! radioIrq );

			readRegister(RF212_IRQ_STATUS); // clear the interrupt register
			call IRQ.captureRisingEdge();

			// setChannel was ignored in SLEEP because the SPI was not working, so do it here
			writeRegister(RF212_PHY_CC_CCA, RF212_CCA_MODE_VALUE | channel);

			writeRegister(RF212_TRX_STATE, RF212_RX_ON);
			state = STATE_TRX_OFF_2_RX_ON;
		}
		else if( (cmd == CMD_TURNOFF || cmd == CMD_STANDBY) 
			&& state == STATE_RX_ON && isSpiAcquired() )
		{
			writeRegister(RF212_TRX_STATE, RF212_FORCE_TRX_OFF);

			call IRQ.disable();
			radioIrq = FALSE;

			state = STATE_TRX_OFF;
		}

		if( cmd == CMD_TURNOFF && state == STATE_TRX_OFF )
		{
			call SLP_TR.set();
			state = STATE_SLEEP;
			cmd = CMD_SIGNAL_DONE;
		}
		else if( cmd == CMD_STANDBY && state == STATE_TRX_OFF )
			cmd = CMD_SIGNAL_DONE;
	}

	tasklet_async command error_t RadioState.turnOff()
	{
		if( cmd != CMD_NONE )
			return EBUSY;
		else if( state == STATE_SLEEP )
			return EALREADY;

		cmd = CMD_TURNOFF;
		call Tasklet.schedule();

		return SUCCESS;
	}
	
	tasklet_async command error_t RadioState.standby()
	{
		if( cmd != CMD_NONE || (state == STATE_SLEEP && ! call RadioAlarm.isFree()) )
			return EBUSY;
		else if( state == STATE_TRX_OFF )
			return EALREADY;

		cmd = CMD_STANDBY;
		call Tasklet.schedule();

		return SUCCESS;
	}

	tasklet_async command error_t RadioState.turnOn()
	{
		if( cmd != CMD_NONE || (state == STATE_SLEEP && ! call RadioAlarm.isFree()) )
			return EBUSY;
		else if( state == STATE_RX_ON )
			return EALREADY;

		cmd = CMD_TURNON;
		call Tasklet.schedule();

		return SUCCESS;
	}

	default tasklet_async event void RadioState.done() { }

/*----------------- TRANSMIT -----------------*/

	tasklet_async command error_t RadioSend.send(message_t* msg)
	{
		uint16_t time;
		uint8_t length;
		uint8_t* data;
		uint8_t header;
		uint32_t time32;
		void* timesync;

		if( cmd != CMD_NONE || state != STATE_RX_ON || ! isSpiAcquired() || radioIrq )
			return EBUSY;

		length = call PacketTransmitPower.isSet(msg) ?
			call PacketTransmitPower.get(msg) : RF212_DEF_RFPOWER;

		if( length != txPower )
		{
			txPower = length;
			writeRegister(RF212_PHY_TX_PWR, txPower);
		}

		if( call RF212DriverConfig.requiresRssiCca(msg) 
				&& (readRegister(RF212_PHY_RSSI) & RF212_RSSI_MASK) > ((rssiClear + rssiBusy) >> 3) )
			return EBUSY;

		writeRegister(RF212_TRX_STATE, RF212_PLL_ON);

		// do something useful, just to wait a little
		time32 = call LocalTime.get();
		timesync = call PacketTimeSyncOffset.isSet(msg) ? msg->data + call PacketTimeSyncOffset.get(msg) : 0;

		// we have missed an incoming message in this short amount of time
		if( (readRegister(RF212_TRX_STATUS) & RF212_TRX_STATUS_MASK) != RF212_PLL_ON )
		{
			ASSERT( (readRegister(RF212_TRX_STATUS) & RF212_TRX_STATUS_MASK) == RF212_BUSY_RX );

			state = STATE_PLL_ON_2_RX_ON;
			return EBUSY;
		}

		atomic
		{
			call SLP_TR.set();
			time = call RadioAlarm.getNow() + TX_SFD_DELAY;
		}
		call SLP_TR.clr();

		ASSERT( ! radioIrq );

		call SELN.clr();
		call FastSpiByte.splitWrite(RF212_CMD_FRAME_WRITE);

		length = call RF212DriverConfig.getLength(msg);
		data = call RF212DriverConfig.getPayload(msg);

		// length | data[0] ... data[length-3] | automatically generated FCS
		call FastSpiByte.splitReadWrite(length);

		// the FCS is atomatically generated (2 bytes)
		length -= 2;

		header = call RF212DriverConfig.getHeaderLength();
		if( header > length )
			header = length;

		length -= header;

		// first upload the header to gain some time
		do {
			call FastSpiByte.splitReadWrite(*(data++));
		}
		while( --header != 0 );

		time32 += (int16_t)(time) - (int16_t)(time32);

		if( timesync != 0 )
			*(timesync_relative_t*)timesync = (*(timesync_absolute_t*)timesync) - time32;

		do {
			call FastSpiByte.splitReadWrite(*(data++));
		}
		while( --length != 0 );

		// wait for the SPI transfer to finish
		call FastSpiByte.splitRead();
		call SELN.set();

		/*
		 * There is a very small window (~1 microsecond) when the RF212 went 
		 * into PLL_ON state but was somehow not properly initialized because 
		 * of an incoming message and could not go into BUSY_TX. I think the
		 * radio can even receive a message, and generate a TRX_UR interrupt
		 * because of concurrent access, but that message probably cannot be
		 * recovered.
		 *
		 * TODO: this needs to be verified, and make sure that the chip is 
		 * not locked up in this case.
		 */

		// go back to RX_ON state when finished
		writeRegister(RF212_TRX_STATE, RF212_RX_ON);

#ifdef RADIO_DEBUG_MESSAGES
		if( call DiagMsg.record() )
		{
			length = call RF212DriverConfig.getLength(msg);

			call DiagMsg.str("tx");
			call DiagMsg.uint16(time);
			call DiagMsg.uint8(length);
			call DiagMsg.hex8s(data, length - 2);
			call DiagMsg.send();
		}
#endif

		if( timesync != 0 )
			*(timesync_absolute_t*)timesync = (*(timesync_relative_t*)timesync) + time32;

		call PacketTimeStamp.set(msg, time32);

		// wait for the TRX_END interrupt
		state = STATE_BUSY_TX_2_RX_ON;
		cmd = CMD_TRANSMIT;

		return SUCCESS;
	}

	default tasklet_async event void RadioSend.sendDone(error_t error) { }
	default tasklet_async event void RadioSend.ready() { }

/*----------------- CCA -----------------*/

	tasklet_async command error_t RadioCCA.request()
	{
		if( cmd != CMD_NONE || state != STATE_RX_ON || ! isSpiAcquired() || ! call RadioAlarm.isFree() )
			return EBUSY;

		// see Errata B7 of the datasheet
		// writeRegister(RF212_TRX_STATE, RF212_PLL_ON);
		// writeRegister(RF212_TRX_STATE, RF212_RX_ON);

		writeRegister(RF212_PHY_CC_CCA, RF212_CCA_REQUEST | RF212_CCA_MODE_VALUE | channel);
		call RadioAlarm.wait(CCA_REQUEST_TIME);
		cmd = CMD_CCA;
		
		return SUCCESS;
	}

	default tasklet_async event void RadioCCA.done(error_t error) { }

/*----------------- RECEIVE -----------------*/

	inline void downloadMessage()
	{
		uint8_t length;
		uint8_t crc;

		call SELN.clr();
		call FastSpiByte.write(RF212_CMD_FRAME_READ);

		// read the length byte
		length = call FastSpiByte.write(0);

		// if correct length
		if( length >= 3 && length <= call RF212DriverConfig.getMaxLength() )
		{
			uint8_t read;
			uint8_t* data;

			// initiate the reading
			call FastSpiByte.splitWrite(0);

			call RF212DriverConfig.setLength(rxMsg, length);
			data = call RF212DriverConfig.getPayload(rxMsg);
			crc = 0;

			// we do not store the CRC field
			length -= 2;

			read = call RF212DriverConfig.getHeaderLength();
			if( length < read )
				read = length;

			length -= read;

			do {
				*(data++) = call FastSpiByte.splitReadWrite(0);
			}
			while( --read != 0  );

			if( signal RadioReceive.header(rxMsg) )
			{
				while( length-- != 0 )
					*(data++) = call FastSpiByte.splitReadWrite(0);

				call FastSpiByte.splitReadWrite(0);	// two CRC bytes
				call FastSpiByte.splitReadWrite(0);

				call PacketLinkQuality.set(rxMsg, call FastSpiByte.splitReadWrite(0));
				call FastSpiByte.splitReadWrite(0);	// ED
				crc = call FastSpiByte.splitRead() & RF212_RX_CRC_VALID;	// RX_STATUS
			}
		}

		call SELN.set();
		state = STATE_RX_ON;

#ifdef RADIO_DEBUG_MESSAGES
		if( call DiagMsg.record() )
		{
			length = call RF212DriverConfig.getLength(rxMsg);

			call DiagMsg.str("rx");
			call DiagMsg.uint32(call PacketTimeStamp.isValid(rxMsg) ? call PacketTimeStamp.timestamp(rxMsg) : 0);
			call DiagMsg.uint16(call RadioAlarm.getNow());
			call DiagMsg.uint8(crc != 0);
			call DiagMsg.uint8(length);
			call DiagMsg.hex8s(call RF212DriverConfig.getPayload(rxMsg), length - 2);
			call DiagMsg.send();
		}
#endif
		
		cmd = CMD_NONE;

		// signal only if it has passed the CRC check
		if( crc != 0 )
			rxMsg = signal RadioReceive.receive(rxMsg);
	}

/*----------------- IRQ -----------------*/

	async event void IRQ.captured(uint16_t time)
	{
		ASSERT( ! radioIrq );

		atomic
		{
			capturedTime = time;
			radioIrq = TRUE;
		}

		call Tasklet.schedule();
	}

	void serviceRadio()
	{
		if( isSpiAcquired() )
		{
			uint16_t time;
			uint32_t time32;
			uint8_t irq;
			uint8_t temp;
			
			atomic time = capturedTime;
			radioIrq = FALSE;
			irq = readRegister(RF212_IRQ_STATUS);

#ifdef RADIO_DEBUG
			// TODO: handle this interrupt
			if( irq & RF212_IRQ_TRX_UR )
			{
				if( call DiagMsg.record() )
				{
					call DiagMsg.str("assert ur");
					call DiagMsg.uint16(call RadioAlarm.getNow());
					call DiagMsg.hex8(readRegister(RF212_TRX_STATUS));
					call DiagMsg.hex8(readRegister(RF212_TRX_STATE));
					call DiagMsg.hex8(irq);
					call DiagMsg.uint8(state);
					call DiagMsg.uint8(cmd);
					call DiagMsg.send();
				}
			}
#endif

			if( irq & RF212_IRQ_PLL_LOCK )
			{
				if( cmd == CMD_TURNON || cmd == CMD_CHANNEL )
				{
					ASSERT( state == STATE_TRX_OFF_2_RX_ON );

					state = STATE_RX_ON;
					cmd = CMD_SIGNAL_DONE;
				}
				else if( cmd == CMD_TRANSMIT )
				{
					ASSERT( state == STATE_BUSY_TX_2_RX_ON );
				}
				else
					ASSERT(FALSE);
			}

			if( irq & RF212_IRQ_RX_START )
			{
				if( cmd == CMD_CCA )
				{
					signal RadioCCA.done(FAIL);
					cmd = CMD_NONE;
				}

				if( cmd == CMD_NONE )
				{
					ASSERT( state == STATE_RX_ON || state == STATE_PLL_ON_2_RX_ON );

					// the most likely place for busy channel, with no TRX_END interrupt
					if( irq == RF212_IRQ_RX_START )
					{
						temp = readRegister(RF212_PHY_RSSI) & RF212_RSSI_MASK;
						rssiBusy += temp - (rssiBusy >> 2);
#ifndef RF212_RSSI_ENERGY
						call PacketRSSI.set(rxMsg, temp);
					}
					else
					{
						call PacketRSSI.clear(rxMsg);
#endif
					}

					/*
					 * The timestamp corresponds to the first event which could not
					 * have been a PLL_LOCK because then cmd != CMD_NONE, so we must
					 * have received a message (and could also have received the 
					 * TRX_END interrupt in the mean time, but that is fine. Also,
					 * we could not be after a transmission, because then cmd = 
					 * CMD_TRANSMIT.
					 */
					if( irq == RF212_IRQ_RX_START ) // just to be cautious
					{
						time32 = call LocalTime.get();
						time32 += (int16_t)(time - RX_SFD_DELAY) - (int16_t)(time32);
						call PacketTimeStamp.set(rxMsg, time32);
					}
					else
						call PacketTimeStamp.clear(rxMsg);

					cmd = CMD_RECEIVE;
				}
				else
					ASSERT( cmd == CMD_TURNOFF );
			}

			if( irq & RF212_IRQ_TRX_END )
			{
				if( cmd == CMD_TRANSMIT )
				{
					ASSERT( state == STATE_BUSY_TX_2_RX_ON );

					state = STATE_RX_ON;
					cmd = CMD_NONE;
					signal RadioSend.sendDone(SUCCESS);

					// TODO: we could have missed a received message
					ASSERT( ! (irq & RF212_IRQ_RX_START) );
				}
				else if( cmd == CMD_RECEIVE )
				{
					ASSERT( state == STATE_RX_ON || state == STATE_PLL_ON_2_RX_ON );
#ifdef RF212_RSSI_ENERGY
					if( irq == RF212_IRQ_TRX_END )
						call PacketRSSI.set(rxMsg, readRegister(RF212_PHY_ED_LEVEL));
					else
						call PacketRSSI.clear(rxMsg);
#endif
					if( state == STATE_PLL_ON_2_RX_ON )
					{
						ASSERT( (readRegister(RF212_TRX_STATUS) & RF212_TRX_STATUS_MASK) == RF212_PLL_ON );

						writeRegister(RF212_TRX_STATE, RF212_RX_ON);
						state = STATE_RX_ON;
					}
					else
					{
						// the most likely place for clear channel (hope to avoid acks)
						rssiClear += (readRegister(RF212_PHY_RSSI) & RF212_RSSI_MASK) - (rssiClear >> 2);
					}

					cmd = CMD_DOWNLOAD;
				}
				else
					ASSERT(FALSE);
			}
		}
	}

	default tasklet_async event bool RadioReceive.header(message_t* msg)
	{
		return TRUE;
	}

	default tasklet_async event message_t* RadioReceive.receive(message_t* msg)
	{
		return msg;
	}

/*----------------- TASKLET -----------------*/

	tasklet_async event void Tasklet.run()
	{
		if( radioIrq )
			serviceRadio();

		if( cmd != CMD_NONE )
		{
			if( cmd == CMD_DOWNLOAD )
				downloadMessage();
			else if( CMD_TURNOFF <= cmd && cmd <= CMD_TURNON )
				changeState();
			else if( cmd == CMD_CHANNEL )
				changeChannel();
			
			if( cmd == CMD_SIGNAL_DONE )
			{
				cmd = CMD_NONE;
				signal RadioState.done();
			}
		}

		if( cmd == CMD_NONE && state == STATE_RX_ON && ! radioIrq )
			signal RadioSend.ready();

		if( cmd == CMD_NONE )
			call SpiResource.release();
	}
}

--- NEW FILE: RF212Packet.h ---
/*
 * Copyright (c) 2007, Vanderbilt University
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author: Miklos Maroti
 */

#ifndef __RF212PACKET_H__
#define __RF212PACKET_H__

#include <IEEE154Packet.h>

typedef ieee154_header_t rf212packet_header_t;

typedef nx_struct rf212packet_footer_t
{
	// the time stamp is not recorded here, time stamped messaged cannot have max length
} rf212packet_footer_t;

typedef struct rf212packet_metadata_t
{
	uint8_t flags;
	uint8_t lqi;
	uint8_t power;				// shared between TXPOWER and RSSI
#ifdef LOW_POWER_LISTENING
	uint16_t lpl_sleepint;
#endif
	uint32_t timestamp;
} rf212packet_metadata_t;

enum rf212packet_metadata_flags
{
	RF212PACKET_WAS_ACKED = 0x01,		// PacketAcknowledgements
	RF212PACKET_TIMESTAMP = 0x02,		// PacketTimeStamp
	RF212PACKET_TXPOWER = 0x04,		// PacketTransmitPower
	RF212PACKET_RSSI = 0x08,		// PacketRSSI
	RF212PACKET_TIMESYNC = 0x10,		// PacketTimeSync (update timesync_footer)
	RF212PACKET_LPL_SLEEPINT = 0x20,	// LowPowerListening

	RF212PACKET_CLEAR_METADATA = 0x00,
};

#endif//__RF212PACKET_H__

--- NEW FILE: RF212PacketC.nc ---
/*
 * Copyright (c) 2007, Vanderbilt University
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author: Miklos Maroti
 */

configuration RF212PacketC
{
	provides
	{
		interface Packet;
		interface AMPacket;
		interface PacketAcknowledgements;
		interface PacketField<uint8_t> as PacketLinkQuality;
		interface PacketField<uint8_t> as PacketTransmitPower;
		interface PacketField<uint8_t> as PacketRSSI;
		interface PacketField<uint16_t> as PacketSleepInterval;
		interface PacketField<uint8_t> as PacketTimeSyncOffset;

		interface PacketTimeStamp<TRadio, uint32_t> as PacketTimeStampRadio;
		interface PacketTimeStamp<TMilli, uint32_t> as PacketTimeStampMilli;
	}
}

implementation
{
	components RF212PacketP, IEEE154PacketC, LocalTimeMicroC, LocalTimeMilliC;

	RF212PacketP.IEEE154Packet -> IEEE154PacketC;
	RF212PacketP.LocalTimeRadio -> LocalTimeMicroC;
	RF212PacketP.LocalTimeMilli -> LocalTimeMilliC;

	Packet = RF212PacketP;
	AMPacket = IEEE154PacketC;

	PacketAcknowledgements	= RF212PacketP;
	PacketLinkQuality	= RF212PacketP.PacketLinkQuality;
	PacketTransmitPower	= RF212PacketP.PacketTransmitPower;
	PacketRSSI		= RF212PacketP.PacketRSSI;
	PacketSleepInterval	= RF212PacketP.PacketSleepInterval;
	PacketTimeSyncOffset	= RF212PacketP.PacketTimeSyncOffset;

	PacketTimeStampRadio	= RF212PacketP;
	PacketTimeStampMilli	= RF212PacketP;
}

--- NEW FILE: RF212PacketP.nc ---
/*
 * Copyright (c) 2007, Vanderbilt University
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author: Miklos Maroti
 */

#include <RF212Packet.h>
#include <GenericTimeSyncMessage.h>
#include <RadioConfig.h>

module RF212PacketP
{
	provides
	{
		interface PacketAcknowledgements;
		interface Packet;
		interface PacketField<uint8_t> as PacketLinkQuality;
		interface PacketField<uint8_t> as PacketTransmitPower;
		interface PacketField<uint8_t> as PacketRSSI;
		interface PacketField<uint16_t> as PacketSleepInterval;
		interface PacketField<uint8_t> as PacketTimeSyncOffset;

		interface PacketTimeStamp<TRadio, uint32_t> as PacketTimeStampRadio;
		interface PacketTimeStamp<TMilli, uint32_t> as PacketTimeStampMilli;
	}

	uses
	{
		interface IEEE154Packet;

		interface LocalTime<TRadio> as LocalTimeRadio;
		interface LocalTime<TMilli> as LocalTimeMilli;
	}
}

implementation
{
	enum
	{
		PACKET_LENGTH_INCREASE = 
			sizeof(rf212packet_header_t) - 1	// the 8-bit length field is not counted
			+ sizeof(ieee154_footer_t),		// the CRC is not stored in memory
	};

	inline rf212packet_metadata_t* getMeta(message_t* msg)
	{
		return (rf212packet_metadata_t*)(msg->metadata);
	}

/*----------------- Packet -----------------*/

	command void Packet.clear(message_t* msg) 
	{
		call IEEE154Packet.createDataFrame(msg);

		getMeta(msg)->flags = RF212PACKET_CLEAR_METADATA;
	}

	inline command void Packet.setPayloadLength(message_t* msg, uint8_t len) 
	{
		call IEEE154Packet.setLength(msg, len + PACKET_LENGTH_INCREASE);
	}

	inline command uint8_t Packet.payloadLength(message_t* msg) 
	{
		return call IEEE154Packet.getLength(msg) - PACKET_LENGTH_INCREASE;
	}

	inline command uint8_t Packet.maxPayloadLength()
	{
		return TOSH_DATA_LENGTH;
	}

	command void* Packet.getPayload(message_t* msg, uint8_t len)
	{
		if( len > TOSH_DATA_LENGTH )
			return NULL;

		return msg->data;
	}

/*----------------- PacketAcknowledgements -----------------*/

	async command error_t PacketAcknowledgements.requestAck(message_t* msg)
	{
		call IEEE154Packet.setAckRequired(msg, TRUE);

		return SUCCESS;
	}

	async command error_t PacketAcknowledgements.noAck(message_t* msg)
	{
		call IEEE154Packet.setAckRequired(msg, FALSE);

		return SUCCESS;
	}

	async command bool PacketAcknowledgements.wasAcked(message_t* msg)
	{
		return getMeta(msg)->flags & RF212PACKET_WAS_ACKED;
	}

/*----------------- PacketLinkQuality -----------------*/

	async command bool PacketLinkQuality.isSet(message_t* msg)
	{
		return TRUE;
	}

	async command uint8_t PacketLinkQuality.get(message_t* msg)
	{
		return getMeta(msg)->lqi;
	}

	async command void PacketLinkQuality.clear(message_t* msg)
	{
	}

	async command void PacketLinkQuality.set(message_t* msg, uint8_t value)
	{
		getMeta(msg)->lqi = value;
	}

/*----------------- PacketTimeStampRadio -----------------*/

	async command bool PacketTimeStampRadio.isValid(message_t* msg)
	{
		return getMeta(msg)->flags & RF212PACKET_TIMESTAMP;
	}

	async command uint32_t PacketTimeStampRadio.timestamp(message_t* msg)
	{
		return getMeta(msg)->timestamp;
	}

	async command void PacketTimeStampRadio.clear(message_t* msg)
	{
		getMeta(msg)->flags &= ~RF212PACKET_TIMESTAMP;
	}

	async command void PacketTimeStampRadio.set(message_t* msg, uint32_t value)
	{
		getMeta(msg)->flags |= RF212PACKET_TIMESTAMP;
		getMeta(msg)->timestamp = value;
	}

/*----------------- PacketTimeStampMilli -----------------*/

	async command bool PacketTimeStampMilli.isValid(message_t* msg)
	{
		return call PacketTimeStampRadio.isValid(msg);
	}

	async command uint32_t PacketTimeStampMilli.timestamp(message_t* msg)
	{
		int32_t offset = call PacketTimeStampRadio.timestamp(msg) - call LocalTimeRadio.get();

		return (offset >> RADIO_ALARM_MILLI_EXP) + call LocalTimeMilli.get();
	}

	async command void PacketTimeStampMilli.clear(message_t* msg)
	{
		call PacketTimeStampRadio.clear(msg);
	}

	async command void PacketTimeStampMilli.set(message_t* msg, uint32_t value)
	{
		int32_t offset = (value - call LocalTimeMilli.get()) << RADIO_ALARM_MILLI_EXP;

		call PacketTimeStampRadio.set(msg, offset + call LocalTimeRadio.get());
	}

/*----------------- PacketTransmitPower -----------------*/

	async command bool PacketTransmitPower.isSet(message_t* msg)
	{
		return getMeta(msg)->flags & RF212PACKET_TXPOWER;
	}

	async command uint8_t PacketTransmitPower.get(message_t* msg)
	{
		return getMeta(msg)->power;
	}

	async command void PacketTransmitPower.clear(message_t* msg)
	{
		getMeta(msg)->flags &= ~RF212PACKET_TXPOWER;
	}

	async command void PacketTransmitPower.set(message_t* msg, uint8_t value)
	{
		getMeta(msg)->flags &= ~RF212PACKET_RSSI;
		getMeta(msg)->flags |= RF212PACKET_TXPOWER;
		getMeta(msg)->power = value;
	}

/*----------------- PacketRSSI -----------------*/

	async command bool PacketRSSI.isSet(message_t* msg)
	{
		return getMeta(msg)->flags & RF212PACKET_RSSI;
	}

	async command uint8_t PacketRSSI.get(message_t* msg)
	{
		return getMeta(msg)->power;
	}

	async command void PacketRSSI.clear(message_t* msg)
	{
		getMeta(msg)->flags &= ~RF212PACKET_RSSI;
	}

	async command void PacketRSSI.set(message_t* msg, uint8_t value)
	{
		getMeta(msg)->flags &= ~RF212PACKET_TXPOWER;
		getMeta(msg)->flags |= RF212PACKET_RSSI;
		getMeta(msg)->power = value;
	}

/*----------------- PacketTimeSyncOffset -----------------*/

	async command bool PacketTimeSyncOffset.isSet(message_t* msg)
	{
		return getMeta(msg)->flags & RF212PACKET_TIMESYNC;
	}

	async command uint8_t PacketTimeSyncOffset.get(message_t* msg)
	{
		return call IEEE154Packet.getLength(msg) - PACKET_LENGTH_INCREASE - sizeof(timesync_absolute_t);
	}

	async command void PacketTimeSyncOffset.clear(message_t* msg)
	{
		getMeta(msg)->flags &= ~RF212PACKET_TIMESYNC;
	}

	async command void PacketTimeSyncOffset.set(message_t* msg, uint8_t value)
	{
		// the value is ignored, the offset always points to the timesync footer at the end of the payload
		getMeta(msg)->flags |= RF212PACKET_TIMESYNC;
	}

/*----------------- PacketSleepInterval -----------------*/

	async command bool PacketSleepInterval.isSet(message_t* msg)
	{
		return getMeta(msg)->flags & RF212PACKET_LPL_SLEEPINT;
	}

	async command uint16_t PacketSleepInterval.get(message_t* msg)
	{
#ifdef LOW_POWER_LISTENING
		return getMeta(msg)->lpl_sleepint;
#else
		return 0;
#endif
	}

	async command void PacketSleepInterval.clear(message_t* msg)
	{
		getMeta(msg)->flags &= ~RF212PACKET_LPL_SLEEPINT;
	}

	async command void PacketSleepInterval.set(message_t* msg, uint16_t value)
	{
		getMeta(msg)->flags |= RF212PACKET_LPL_SLEEPINT;

#ifdef LOW_POWER_LISTENING
		getMeta(msg)->lpl_sleepint = value;
#endif
	}
}



More information about the Tinyos-2-commits mailing list