]> oss.titaniummirror.com Git - tinyos-2.x.git/commitdiff
Added support for BLIP on Mulle.
authorr-studio <r-studio>
Fri, 9 Apr 2010 15:01:33 +0000 (15:01 +0000)
committerr-studio <r-studio>
Fri, 9 Apr 2010 15:01:33 +0000 (15:01 +0000)
Added fixes for the slow spi bus issue between Mulle and RF230.
Removed one printf statement from mulle/chips/at45db/HplAt...

apps/IPBaseStation/BaseStationC.nc
apps/IPBaseStation/BaseStationP.nc
apps/IPBaseStation/Reset.h
tos/lib/net/blip/ReadLqiC.nc
tos/lib/serial/SerialPacketInfo802_15_4P.nc
tos/platforms/mulle/Ieee154MessageC.nc
tos/platforms/mulle/chips/at45db/HplAt45dbP.nc
tos/platforms/mulle/fix/README
tos/platforms/mulle/fix/RF230DriverHwAckP.nc [new file with mode: 0644]
tos/platforms/mulle/fix/RF230DriverLayerP.nc [new file with mode: 0644]
tos/platforms/mulle/hardware.h

index cbb067a965e02493a7a21ad4196489382de4b7fb..8c801a3adee18738d98d876104256193f1fde421 100644 (file)
@@ -133,7 +133,7 @@ implementation {
   components IPAddressC;
   BaseStationP.IPAddress -> IPAddressC;
 
-#ifdef PLATFORM_IRIS
+#if defined(PLATFORM_IRIS) || defined(PLATFORM_MULLE)
   BaseStationP.RadioChannel -> Radio;
 #else
   components CC2420ControlC;
index 0a27e04ecf9c8e94346dd53ca50f5e5bac76c68f..92db034573332565ffcd251c8e78bf1db80c560d 100644 (file)
@@ -95,7 +95,7 @@ module BaseStationP {
     interface PacketLink;
     interface LowPowerListening;
 
-#ifdef PLATFORM_IRIS
+#if defined(PLATFORM_IRIS) || defined(PLATFORM_MULLE)
     interface RadioChannel;
 #else
     interface CC2420Config;
@@ -414,7 +414,7 @@ implementation
     case CONFIG_ECHO:
       break;
     case CONFIG_SET_PARM:
-#ifdef PLATFORM_IRIS
+#if defined(PLATFORM_IRIS) || defined(PLATFORM_MULLE)
       // we should check the return value, hope it works
       call RadioChannel.setChannel(cmd->rf.channel);
       call IPAddress.setShortAddr(cmd->rf.addr);
@@ -440,7 +440,7 @@ implementation
     return msg;
   }
 
-#ifdef PLATFORM_IRIS
+#if defined(PLATFORM_IRIS) || defined(PLATFORM_MULLE)
   event void RadioChannel.setChannelDone() { }
 #else
   event void CC2420Config.syncDone(error_t error) {
index 28aed0a2dcb920a97e0092c53738caae762eb749..0cebfde42a742c41ea492d39a983f1d2111ff459 100644 (file)
@@ -52,6 +52,15 @@ void resetMote()
        }
 #elif defined(PLATFORM_TELOS) || defined(PLATFORM_TELOSB) || defined(PLATFORM_EPIC) || defined(PLATFORM_SHIMMER) || defined(PLATFORM_SHIMMER2) || defined(PLATFORM_SPAN)
         WDTCTL = 0;
+#elif defined(PLATFORM_MULLE)
+            PRCR.BIT.PRC0 = 1; // Turn off protection on CM registers.
+            PRCR.BIT.PRC1 = 1; // Turn off protection on PM registers.
+            CM0.BIT.CM0_6 = 1;  
+            PM1.BIT.PM1_2 = 1; // Reset on WDT underflow.
+            WDTS = 1; // Start watchdog timer.
+            PRCR.BIT.PRC0 = 0; // Turn on protection on CM registers.
+            PRCR.BIT.PRC1 = 0; // Turn on protection on PM registers.
+            while (1); // Wait for underflow in the watchdog timer.
 #else
 #error "Reset.h not defined/supported for your platform, aborting..."
 #endif
index 7debe7dea98d4ae83e779f21262ce6e016219fb9..cd6005ad9b17cc775143ce4811c1116ace77603a 100644 (file)
@@ -19,7 +19,7 @@ configuration ReadLqiC {
   components CC2420ReadLqiC, CC2420PacketC;
   ReadLqi = CC2420ReadLqiC;
   CC2420ReadLqiC.CC2420Packet -> CC2420PacketC;
-#elif defined(PLATFORM_IRIS) 
+#elif defined(PLATFORM_IRIS) || defined(PLATFORM_MULLE) 
   components RF230ReadLqiC, RF230Ieee154MessageC;
   ReadLqi = RF230ReadLqiC;
   RF230ReadLqiC.SubLqi -> RF230Ieee154MessageC.PacketLinkQuality;
index 45cafe162375cf1f99de327b58f16bf54d71e6e7..b0de588ae153594624d9d4834d7c5e24fc993488 100644 (file)
@@ -34,7 +34,7 @@ module SerialPacketInfo802_15_4P {
   provides interface SerialPacketInfo as Info;
 }
 implementation {
-#ifdef PLATFORM_IRIS
+#if defined(PLATFORM_IRIS) || defined(PLATFORM_MULLE)
   enum {
     HEADER_SIZE = sizeof(rf230packet_header_t),
     FOOTER_SIZE = sizeof(rf230packet_footer_t),
index 1c611f2a490bd5d775ce9e010e10c9a9659b7815..61b3d4b0e4094963889d7d9110ea1efc1857a1ba 100644 (file)
@@ -38,6 +38,7 @@ configuration Ieee154MessageC
                interface PacketAcknowledgements;
                interface LowPowerListening;
                interface PacketLink;
+               interface RadioChannel;
 
                interface PacketTimeStamp<TMicro, uint32_t> as PacketTimeStampMicro;
                interface PacketTimeStamp<TMilli, uint32_t> as PacketTimeStampMilli;
@@ -61,6 +62,7 @@ implementation
        PacketAcknowledgements = MessageC;
        LowPowerListening = MessageC;
        PacketLink = MessageC;
+       RadioChannel = MessageC;
 
        PacketTimeStampMilli = MessageC;
        PacketTimeStampMicro = MessageC;
index fbfbdd0e1a2cc1f683c5bf0e6078df960aeabd98..dfa681b1c81ef9d8987027882f4e764648517eab 100644 (file)
@@ -93,7 +93,6 @@ implementation
   // TODO(Henrik) Move init code to a SplitControl interface and
   //              change the busy wait into a TimerMilli.startOneShot.
   command error_t Init.init() {
-       printf("Ad45db init\n");
        call WP.makeOutput();
        call WP.set();
        call RESET.makeOutput();
index 5c150d7a8d7281cd8fba9110029f2f75e2bb6b56..9f59e36340fa6322104acd644c62d74b280e7458 100644 (file)
@@ -10,3 +10,6 @@ The intention is to never have any files in this folder and if a file is put in
 tos/chips/at45db/At45dbP.nc: After a erase is done the buffer should be invalidated in some way. In the fix the buffer page member is set to AT45_MAX_PAGES, because the indexes of the pages are 0-(AT45_MAX_PAGES-1) thus AT45_MAX_PAGES indicates an invalid page number. Have spoken to David Gay about this. No fix seems to be committed, commit it our selfs?
 
 tos/chips/at45db/LogStorageP.nc: in struct pageinfo there is a member lastRecordOffset which is a uint8_t in the original file. Mulle and platforms using At45DB chips with 10 bit offset addresses need a larger storage unit than uint8_t and is thus changed to uint16_t. This should maybe be typedefed in every platform specific implementation of the at45db. We cant just change it to uint16_t because that would break backwards compatibility for old platforms. Have spoken with David Gay about this to, but nothing has happened.
+
+tos/chips/rf2xx/rf230/RF230DriverHwAckP.nc/RF230DriverLayerP.nc: Because of the use of a software spi bus on Mulle the communication between the RF230 chip and Mulle is to slow to be able to start sending a packet before the whole packet has been uploaded. We are instead uploading the packet first to the RF230 and then triggering the actual send. This may screw up protocols that need very good timing. But not using this may result in packet shifting, meaning that only half the packet is sent first and the second half is sent with the next packet.
+The next Mulle version will have a hardware spi connection between the radio and Mulle. Hopefully this will solve the problem.
diff --git a/tos/platforms/mulle/fix/RF230DriverHwAckP.nc b/tos/platforms/mulle/fix/RF230DriverHwAckP.nc
new file mode 100644 (file)
index 0000000..235ca40
--- /dev/null
@@ -0,0 +1,1035 @@
+/*
+* Copyright (c) 2009, University of Szeged
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* - Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* - Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+* - Neither the name of University of Szeged nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+* OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* Author: Miklos Maroti
+*/
+
+#include <RF230DriverLayer.h>
+#include <Tasklet.h>
+#include <RadioAssert.h>
+#include <TimeSyncMessageLayer.h>
+#include <RadioConfig.h>
+
+module RF230DriverHwAckP
+{
+       provides
+       {
+               interface Init as PlatformInit @exactlyonce();
+               interface Init as SoftwareInit @exactlyonce();
+
+               interface RadioState;
+               interface RadioSend;
+               interface RadioReceive;
+               interface RadioCCA;
+               interface RadioPacket;
+
+               interface PacketField<uint8_t> as PacketTransmitPower;
+               interface PacketField<uint8_t> as PacketRSSI;
+               interface PacketField<uint8_t> as PacketTimeSyncOffset;
+               interface PacketField<uint8_t> as PacketLinkQuality;
+
+               interface PacketAcknowledgements;
+       }
+
+       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 LocalTime<TRadio>;
+
+               interface RF230DriverConfig as Config;
+
+               interface PacketFlag as TransmitPowerFlag;
+               interface PacketFlag as RSSIFlag;
+               interface PacketFlag as TimeSyncFlag;
+
+               interface PacketTimeStamp<TRadio, uint32_t>;
+
+               interface Tasklet;
+               interface RadioAlarm;
+
+               interface PacketFlag as AckReceivedFlag;
+               interface Ieee154PacketLayer;
+               interface ActiveMessageAddress;
+
+#ifdef RADIO_DEBUG
+               interface DiagMsg;
+#endif
+       }
+}
+
+implementation
+{
+       rf230_header_t* getHeader(message_t* msg)
+       {
+               return ((void*)msg) + call Config.headerLength(msg);
+       }
+
+       void* getPayload(message_t* msg)
+       {
+               return ((void*)msg) + call RadioPacket.headerLength(msg);
+       }
+
+       rf230_metadata_t* getMeta(message_t* msg)
+       {
+               return ((void*)msg) + sizeof(message_t) - call RadioPacket.metadataLength(msg);
+       }
+
+/*----------------- 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,
+       };
+
+       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_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
+
+/*----------------- 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),
+               PLL_CALIBRATION_TIME = (uint16_t)(180 * RADIO_ALARM_MICROSEC),
+               CCA_REQUEST_TIME = (uint16_t)(140 * RADIO_ALARM_MICROSEC),
+
+               // 8 undocumented delay, 128 for CSMA, 16 for delay, 5*32 for preamble and SFD
+               TX_SFD_DELAY = (uint16_t)((8 + 128 + 16 + 5*32) * RADIO_ALARM_MICROSEC),
+               
+               // 32 for frame length, 16 for delay
+               RX_SFD_DELAY = (uint16_t)((32 + 16) * RADIO_ALARM_MICROSEC),
+       };
+
+       tasklet_async event void RadioAlarm.fired()
+       {
+               if( state == STATE_SLEEP_2_TRX_OFF )
+                       state = STATE_TRX_OFF;
+               else if( state == STATE_TRX_OFF_2_RX_ON )
+               {
+                       ASSERT( cmd == CMD_TURNON || cmd == CMD_CHANNEL );
+
+                       state = STATE_RX_ON;
+                       cmd = CMD_SIGNAL_DONE;
+               }
+               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_AACK_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;
+
+               return SUCCESS;
+       }
+
+       command error_t SoftwareInit.init()
+       {
+               // for powering up the radio
+               return call SpiResource.request();
+       }
+
+       void initRadio()
+       {
+               uint16_t temp;
+
+               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_TRX_END );
+               writeRegister(RF230_CCA_THRES, RF230_CCA_THRES_VALUE);
+               writeRegister(RF230_PHY_TX_PWR, RF230_TX_AUTO_CRC_ON | (RF230_DEF_RFPOWER & RF230_TX_PWR_MASK));
+
+               txPower = RF230_DEF_RFPOWER & RF230_TX_PWR_MASK;
+               channel = RF230_DEF_CHANNEL & RF230_CHANNEL_MASK;
+               writeRegister(RF230_PHY_CC_CCA, RF230_CCA_MODE_VALUE | channel);
+
+               writeRegister(RF230_XAH_CTRL, 0);
+               writeRegister(RF230_CSMA_SEED_1, 0);
+
+               temp = call ActiveMessageAddress.amGroup();
+               writeRegister(RF230_PAN_ID_0, temp);
+               writeRegister(RF230_PAN_ID_1, temp >> 8);
+
+               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() && call RadioAlarm.isFree() )
+               {
+                       writeRegister(RF230_PHY_CC_CCA, RF230_CCA_MODE_VALUE | channel);
+
+                       if( state == STATE_RX_ON )
+                       {
+                               call RadioAlarm.wait(PLL_CALIBRATION_TIME);
+                               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() && call RadioAlarm.isFree() )
+               {
+                       uint16_t temp;
+       
+                       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);
+
+                       temp = call ActiveMessageAddress.amAddress();
+                       writeRegister(RF230_SHORT_ADDR_0, temp);
+                       writeRegister(RF230_SHORT_ADDR_1, temp >> 8);
+
+                       call RadioAlarm.wait(PLL_CALIBRATION_TIME);
+                       writeRegister(RF230_TRX_STATE, RF230_RX_AACK_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() { }
+
+       task void changeAddress()
+       {
+               call Tasklet.suspend();
+
+               if( isSpiAcquired() )
+               {
+                       uint16_t temp = call ActiveMessageAddress.amAddress();
+                       writeRegister(RF230_SHORT_ADDR_0, temp);
+                       writeRegister(RF230_SHORT_ADDR_1, temp >> 8);
+               }
+               else
+                       post changeAddress();
+
+               call Tasklet.resume();
+       }
+
+       async event void ActiveMessageAddress.changed()
+       {
+               post changeAddress();
+       }
+
+/*----------------- TRANSMIT -----------------*/
+
+       tasklet_norace message_t* txMsg;
+
+       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) & RF230_TX_PWR_MASK;
+
+               if( length != txPower )
+               {
+                       txPower = length;
+                       writeRegister(RF230_PHY_TX_PWR, RF230_TX_AUTO_CRC_ON | txPower);
+               }
+
+               writeRegister(RF230_TRX_STATE, RF230_TX_ARET_ON);
+
+               // do something useful, just to wait a little
+               time32 = call LocalTime.get();
+               timesync = call PacketTimeSyncOffset.isSet(msg) ? ((void*)msg) + 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_TX_ARET_ON )
+               {
+                       ASSERT( (readRegister(RF230_TRX_STATUS) & RF230_TRX_STATUS_MASK) == RF230_BUSY_RX_AACK );
+
+                       writeRegister(RF230_TRX_STATE, RF230_RX_AACK_ON);
+                       return EBUSY;
+               }
+#ifndef RF230_SLOW_SPI_MULLE
+#ifndef RF230_SLOW_SPI
+               atomic
+               {
+                       call SLP_TR.set();
+                       time = call RadioAlarm.getNow();
+               }
+               call SLP_TR.clr();
+#endif
+#endif
+
+               ASSERT( ! radioIrq );
+
+               call SELN.clr();
+               call FastSpiByte.splitWrite(RF230_CMD_FRAME_WRITE);
+
+               data = getPayload(msg);
+               length = getHeader(msg)->length;
+
+               // length | data[0] ... data[length-3] | automatically generated FCS
+               call FastSpiByte.splitReadWrite(length);
+
+               // the FCS is atomatically generated (2 bytes)
+               length -= 2;
+
+               header = call Config.headerPreloadLength();
+               if( header > length )
+                       header = length;
+
+               length -= header;
+
+               // first upload the header to gain some time
+               do {
+                       call FastSpiByte.splitReadWrite(*(data++));
+               }
+               while( --header != 0 );
+#ifndef RF230_SLOW_SPI_MULLE
+#ifdef RF230_SLOW_SPI
+               atomic
+               {
+                       call SLP_TR.set();
+                       time = call RadioAlarm.getNow();
+               }
+               call SLP_TR.clr();
+#endif
+#else
+               atomic
+               {
+                       time = call RadioAlarm.getNow();
+               }
+#endif
+
+               time32 += (int16_t)(time + TX_SFD_DELAY) - (int16_t)(time32);
+
+               if( timesync != 0 )
+                       *(timesync_relative_t*)timesync = (*(timesync_absolute_t*)timesync) - time32;
+
+               while( length-- != 0 )
+                       call FastSpiByte.splitReadWrite(*(data++));
+
+               // wait for the SPI transfer to finish
+               call FastSpiByte.splitRead();
+               
+#ifdef RF230_SLOW_SPI_MULLE
+               atomic
+               {
+                       call SLP_TR.set();
+               }
+               call SLP_TR.clr();
+#endif
+               
+               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_AACK_ON);
+
+               if( timesync != 0 )
+                       *(timesync_absolute_t*)timesync = (*(timesync_relative_t*)timesync) + time32;
+
+               call PacketTimeStamp.set(msg, time32);
+
+#ifdef RADIO_DEBUG_MESSAGES
+               if( call DiagMsg.record() )
+               {
+                       length = getHeader(msg)->length;
+
+                       call DiagMsg.chr('t');
+                       call DiagMsg.uint32(call PacketTimeStamp.isValid(rxMsg) ? call PacketTimeStamp.timestamp(rxMsg) : 0);
+                       call DiagMsg.uint16(call RadioAlarm.getNow());
+                       call DiagMsg.int8(length);
+                       call DiagMsg.hex8s(getPayload(msg), length - 2);
+                       call DiagMsg.send();
+               }
+#endif
+
+               // wait for the TRX_END interrupt
+               txMsg = msg;
+               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_AACK_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;
+               bool crcValid = FALSE;
+
+               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 RadioPacket.maxPayloadLength() + 2 )
+               {
+                       uint8_t read;
+                       uint8_t* data;
+
+                       // initiate the reading
+                       call FastSpiByte.splitWrite(0);
+
+                       data = getPayload(rxMsg);
+                       getHeader(rxMsg)->length = length;
+
+                       // we do not store the CRC field
+                       length -= 2;
+
+                       read = call Config.headerPreloadLength();
+                       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.splitRead());
+
+                               // we should have no other incoming message or buffer underflow
+                               crcValid = ! radioIrq;
+                       }
+               }
+
+               call SELN.set();
+
+               if( crcValid && call PacketTimeStamp.isValid(rxMsg) )
+               {
+                       uint32_t time32 = call PacketTimeStamp.timestamp(rxMsg);
+                       length = getHeader(rxMsg)->length;
+
+/*
+ * If you hate floating point arithmetics and do not care of up to 400 microsecond time stamping errors,
+ * then define RF230_HWACK_SLOPPY_TIMESTAMP, which will be significantly faster.
+ */
+#ifdef RF230_HWACK_SLOPPY_TIMESTAMP
+                       time32 -= (uint16_t)(RX_SFD_DELAY) + ((uint16_t)(length) << (RADIO_ALARM_MILLI_EXP - 5));
+#else
+                       time32 -= (uint16_t)(RX_SFD_DELAY) + (uint16_t)(32.0 * RADIO_ALARM_MICROSEC * (uint16_t)length);
+#endif
+
+                       call PacketTimeStamp.set(rxMsg, time32);
+               }
+
+#ifdef RADIO_DEBUG_MESSAGES
+               if( call DiagMsg.record() )
+               {
+                       length = getHeader(rxMsg)->length;
+
+                       call DiagMsg.chr('r');
+                       call DiagMsg.uint32(call PacketTimeStamp.isValid(rxMsg) ? call PacketTimeStamp.timestamp(rxMsg) : 0);
+                       call DiagMsg.uint16(call RadioAlarm.getNow());
+                       call DiagMsg.int8(crcValid ? length : -length);
+                       call DiagMsg.hex8s(getPayload(rxMsg), length - 2);
+                       call DiagMsg.int8(call PacketRSSI.isSet(rxMsg) ? call PacketRSSI.get(rxMsg) : -1);
+                       call DiagMsg.uint8(call PacketLinkQuality.isSet(rxMsg) ? call PacketLinkQuality.get(rxMsg) : 0);
+                       call DiagMsg.send();
+               }
+#endif
+
+               state = STATE_RX_ON;
+               cmd = CMD_NONE;
+
+               // signal only if it has passed the CRC check
+               if( crcValid )
+                       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_TRX_END )
+                       {
+                               if( cmd == CMD_TRANSMIT )
+                               {
+                                       ASSERT( state == STATE_BUSY_TX_2_RX_ON );
+
+                                       temp = readRegister(RF230_TRX_STATE) & RF230_TRAC_STATUS_MASK;
+
+                                       if( call Ieee154PacketLayer.getAckRequired(txMsg) )
+                                               call AckReceivedFlag.setValue(txMsg, temp != RF230_TRAC_NO_ACK);
+
+                                       state = STATE_RX_ON;
+                                       cmd = CMD_NONE;
+
+                                       signal RadioSend.sendDone(temp != RF230_TRAC_CHANNEL_ACCESS_FAILURE ? SUCCESS : EBUSY);
+
+                                       // TODO: we could have missed a received message
+                                       ASSERT( ! (irq & RF230_IRQ_RX_START) );
+                               }
+                               else if( cmd == CMD_NONE )
+                               {
+                                       ASSERT( state == STATE_RX_ON );
+
+                                       if( irq == RF230_IRQ_TRX_END )
+                                       {
+                                               call PacketRSSI.set(rxMsg, readRegister(RF230_PHY_ED_LEVEL));
+
+                                               // TODO: compensate for packet transmission time when downloading
+                                               time32 = call LocalTime.get();
+                                               time32 += (int16_t)(time) - (int16_t)(time32);
+                                               call PacketTimeStamp.set(rxMsg, time32);
+                                       }
+                                       else
+                                       {
+                                               call PacketRSSI.clear(rxMsg);
+                                               call PacketTimeStamp.clear(rxMsg);
+                                       }
+
+                                       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();
+       }
+
+/*----------------- RadioPacket -----------------*/
+       
+       async command uint8_t RadioPacket.headerLength(message_t* msg)
+       {
+               return call Config.headerLength(msg) + sizeof(rf230_header_t);
+       }
+
+       async command uint8_t RadioPacket.payloadLength(message_t* msg)
+       {
+               return getHeader(msg)->length - 2;
+       }
+
+       async command void RadioPacket.setPayloadLength(message_t* msg, uint8_t length)
+       {
+               ASSERT( 1 <= length && length <= 125 );
+               ASSERT( call RadioPacket.headerLength(msg) + length + call RadioPacket.metadataLength(msg) <= sizeof(message_t) );
+
+               // we add the length of the CRC, which is automatically generated
+               getHeader(msg)->length = length + 2;
+       }
+
+       async command uint8_t RadioPacket.maxPayloadLength()
+       {
+               ASSERT( call Config.maxPayloadLength() - sizeof(rf230_header_t) <= 125 );
+
+               return call Config.maxPayloadLength() - sizeof(rf230_header_t);
+       }
+
+       async command uint8_t RadioPacket.metadataLength(message_t* msg)
+       {
+               return call Config.metadataLength(msg) + sizeof(rf230_metadata_t);
+       }
+
+       async command void RadioPacket.clear(message_t* msg)
+       {
+               // all flags are automatically cleared
+       }
+
+/*----------------- PacketTransmitPower -----------------*/
+
+       async command bool PacketTransmitPower.isSet(message_t* msg)
+       {
+               return call TransmitPowerFlag.get(msg);
+       }
+
+       async command uint8_t PacketTransmitPower.get(message_t* msg)
+       {
+               return getMeta(msg)->power;
+       }
+
+       async command void PacketTransmitPower.clear(message_t* msg)
+       {
+               call TransmitPowerFlag.clear(msg);
+       }
+
+       async command void PacketTransmitPower.set(message_t* msg, uint8_t value)
+       {
+               call TransmitPowerFlag.set(msg);
+               getMeta(msg)->power = value;
+       }
+
+/*----------------- PacketRSSI -----------------*/
+
+       async command bool PacketRSSI.isSet(message_t* msg)
+       {
+               return call RSSIFlag.get(msg);
+       }
+
+       async command uint8_t PacketRSSI.get(message_t* msg)
+       {
+               return getMeta(msg)->rssi;
+       }
+
+       async command void PacketRSSI.clear(message_t* msg)
+       {
+               call RSSIFlag.clear(msg);
+       }
+
+       async command void PacketRSSI.set(message_t* msg, uint8_t value)
+       {
+               // just to be safe if the user fails to clear the packet
+               call TransmitPowerFlag.clear(msg);
+
+               call RSSIFlag.set(msg);
+               getMeta(msg)->rssi = value;
+       }
+
+/*----------------- PacketTimeSyncOffset -----------------*/
+
+       async command bool PacketTimeSyncOffset.isSet(message_t* msg)
+       {
+               return call TimeSyncFlag.get(msg);
+       }
+
+       async command uint8_t PacketTimeSyncOffset.get(message_t* msg)
+       {
+               return call RadioPacket.headerLength(msg) + call RadioPacket.payloadLength(msg) - sizeof(timesync_absolute_t);
+       }
+
+       async command void PacketTimeSyncOffset.clear(message_t* msg)
+       {
+               call TimeSyncFlag.clear(msg);
+       }
+
+       async command void PacketTimeSyncOffset.set(message_t* msg, uint8_t value)
+       {
+               // we do not store the value, the time sync field is always the last 4 bytes
+               ASSERT( call PacketTimeSyncOffset.get(msg) == value );
+
+               call TimeSyncFlag.set(msg);
+       }
+
+/*----------------- 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;
+       }
+
+/*----------------- PacketAcknowledgements -----------------*/
+
+       async command error_t PacketAcknowledgements.requestAck(message_t* msg)
+       {
+               call Ieee154PacketLayer.setAckRequired(msg, TRUE);
+
+               return SUCCESS;
+       }
+
+       async command error_t PacketAcknowledgements.noAck(message_t* msg)
+       {
+               call Ieee154PacketLayer.setAckRequired(msg, FALSE);
+
+               return SUCCESS;
+       }
+
+       async command bool PacketAcknowledgements.wasAcked(message_t* msg)
+       {
+               return call AckReceivedFlag.get(msg);
+       }
+}
diff --git a/tos/platforms/mulle/fix/RF230DriverLayerP.nc b/tos/platforms/mulle/fix/RF230DriverLayerP.nc
new file mode 100644 (file)
index 0000000..dd70730
--- /dev/null
@@ -0,0 +1,1008 @@
+/*
+ * 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 <RF230DriverLayer.h>
+#include <Tasklet.h>
+#include <RadioAssert.h>
+#include <TimeSyncMessageLayer.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;
+               interface RadioPacket;
+
+               interface PacketField<uint8_t> as PacketTransmitPower;
+               interface PacketField<uint8_t> as PacketRSSI;
+               interface PacketField<uint8_t> as PacketTimeSyncOffset;
+               interface PacketField<uint8_t> as PacketLinkQuality;
+       }
+
+       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 LocalTime<TRadio>;
+
+               interface RF230DriverConfig as Config;
+
+               interface PacketFlag as TransmitPowerFlag;
+               interface PacketFlag as RSSIFlag;
+               interface PacketFlag as TimeSyncFlag;
+
+               interface PacketTimeStamp<TRadio, uint32_t>;
+
+               interface Tasklet;
+               interface RadioAlarm;
+
+#ifdef RADIO_DEBUG
+               interface DiagMsg;
+#endif
+       }
+}
+
+implementation
+{
+       rf230_header_t* getHeader(message_t* msg)
+       {
+               return ((void*)msg) + call Config.headerLength(msg);
+       }
+
+       void* getPayload(message_t* msg)
+       {
+               return ((void*)msg) + call RadioPacket.headerLength(msg);
+       }
+
+       rf230_metadata_t* getMeta(message_t* msg)
+       {
+               return ((void*)msg) + sizeof(message_t) - call RadioPacket.metadataLength(msg);
+       }
+
+/*----------------- 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_TX_AUTO_CRC_ON | (RF230_DEF_RFPOWER & RF230_TX_PWR_MASK));
+
+               txPower = RF230_DEF_RFPOWER & RF230_TX_PWR_MASK;
+               channel = RF230_DEF_CHANNEL & 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) & RF230_TX_PWR_MASK;
+
+               if( length != txPower )
+               {
+                       txPower = length;
+                       writeRegister(RF230_PHY_TX_PWR, RF230_TX_AUTO_CRC_ON | txPower);
+               }
+
+               if( call Config.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) ? ((void*)msg) + 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;
+               }
+
+#ifndef RF230_SLOW_SPI_MULLE
+#ifndef RF230_SLOW_SPI
+               atomic
+               {
+                       call SLP_TR.set();
+                       time = call RadioAlarm.getNow();
+               }
+               call SLP_TR.clr();
+#endif
+#endif
+
+               ASSERT( ! radioIrq );
+
+               call SELN.clr();
+               call FastSpiByte.splitWrite(RF230_CMD_FRAME_WRITE);
+
+               data = getPayload(msg);
+               length = getHeader(msg)->length;
+
+               // length | data[0] ... data[length-3] | automatically generated FCS
+               call FastSpiByte.splitReadWrite(length);
+
+               // the FCS is atomatically generated (2 bytes)
+               length -= 2;
+
+               header = call Config.headerPreloadLength();
+               if( header > length )
+                       header = length;
+
+               length -= header;
+
+               // first upload the header to gain some time
+               do {
+                       call FastSpiByte.splitReadWrite(*(data++));
+               }
+               while( --header != 0 );
+
+#ifndef RF230_SLOW_SPI_MULLE
+#ifdef RF230_SLOW_SPI
+               atomic
+               {
+                       call SLP_TR.set();
+                       time = call RadioAlarm.getNow();
+               }
+               call SLP_TR.clr();
+#endif
+#else
+               atomic
+               {
+                       time = call RadioAlarm.getNow();
+               }
+#endif
+
+               time32 += (int16_t)(time + TX_SFD_DELAY) - (int16_t)(time32);
+
+               if( timesync != 0 )
+                       *(timesync_relative_t*)timesync = (*(timesync_absolute_t*)timesync) - time32;
+
+               while( length-- != 0 )
+                       call FastSpiByte.splitReadWrite(*(data++));
+
+               // wait for the SPI transfer to finish
+               call FastSpiByte.splitRead();
+#ifdef RF230_SLOW_SPI_MULLE
+               atomic
+               {
+                       call SLP_TR.set();
+               }
+               call SLP_TR.clr();
+#endif
+               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);
+
+               if( timesync != 0 )
+                       *(timesync_absolute_t*)timesync = (*(timesync_relative_t*)timesync) + time32;
+
+               call PacketTimeStamp.set(msg, time32);
+
+#ifdef RADIO_DEBUG_MESSAGES
+               if( call DiagMsg.record() )
+               {
+                       length = getHeader(msg)->length;
+
+                       call DiagMsg.chr('t');
+                       call DiagMsg.uint32(call PacketTimeStamp.isValid(rxMsg) ? call PacketTimeStamp.timestamp(rxMsg) : 0);
+                       call DiagMsg.uint16(call RadioAlarm.getNow());
+                       call DiagMsg.int8(length);
+                       call DiagMsg.hex8s(getPayload(msg), length - 2);
+                       call DiagMsg.send();
+               }
+#endif
+
+               // 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;
+               uint16_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 RadioPacket.maxPayloadLength() + 2 )
+               {
+                       uint8_t read;
+                       uint8_t* data;
+
+                       // initiate the reading
+                       call FastSpiByte.splitWrite(0);
+
+                       data = getPayload(rxMsg);
+                       getHeader(rxMsg)->length = length;
+                       crc = 0;
+
+                       // we do not store the CRC field
+                       length -= 2;
+
+                       read = call Config.headerPreloadLength();
+                       if( length < read )
+                               read = length;
+
+                       length -= read;
+
+                       do {
+                               crc = RF230_CRCBYTE_COMMAND(crc, *(data++) = call FastSpiByte.splitReadWrite(0));
+                       }
+                       while( --read != 0  );
+
+                       if( signal RadioReceive.header(rxMsg) )
+                       {
+                               while( length-- != 0 )
+                                       crc = RF230_CRCBYTE_COMMAND(crc, *(data++) = call FastSpiByte.splitReadWrite(0));
+
+                               crc = RF230_CRCBYTE_COMMAND(crc, call FastSpiByte.splitReadWrite(0));
+                               crc = RF230_CRCBYTE_COMMAND(crc, call FastSpiByte.splitReadWrite(0));
+
+                               call PacketLinkQuality.set(rxMsg, call FastSpiByte.splitRead());
+                       }
+                       else
+                               crc = 1;
+               }
+               else
+                       crc = 1;
+
+               call SELN.set();
+               state = STATE_RX_ON;
+
+#ifdef RADIO_DEBUG_MESSAGES
+               if( call DiagMsg.record() )
+               {
+                       length = getHeader(rxMsg)->length;
+
+                       call DiagMsg.chr('r');
+                       call DiagMsg.uint32(call PacketTimeStamp.isValid(rxMsg) ? call PacketTimeStamp.timestamp(rxMsg) : 0);
+                       call DiagMsg.uint16(call RadioAlarm.getNow());
+                       call DiagMsg.int8(crc == 0 ? length : -length);
+                       call DiagMsg.hex8s(getPayload(rxMsg), length - 2);
+                       call DiagMsg.int8(call PacketRSSI.isSet(rxMsg) ? call PacketRSSI.get(rxMsg) : -1);
+                       call DiagMsg.uint8(call PacketLinkQuality.isSet(rxMsg) ? call PacketLinkQuality.get(rxMsg) : 0);
+                       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
+
+#ifdef RF230_RSSI_ENERGY
+                       if( irq & RF230_IRQ_TRX_END )
+                       {
+                               if( irq == RF230_IRQ_TRX_END || 
+                                       (irq == (RF230_IRQ_RX_START | RF230_IRQ_TRX_END) && cmd == CMD_NONE) )
+                                       call PacketRSSI.set(rxMsg, readRegister(RF230_PHY_ED_LEVEL));
+                               else
+                                       call PacketRSSI.clear(rxMsg);
+                       }
+#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 );
+
+                                       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();
+       }
+
+/*----------------- RadioPacket -----------------*/
+       
+       async command uint8_t RadioPacket.headerLength(message_t* msg)
+       {
+               return call Config.headerLength(msg) + sizeof(rf230_header_t);
+       }
+
+       async command uint8_t RadioPacket.payloadLength(message_t* msg)
+       {
+               return getHeader(msg)->length - 2;
+       }
+
+       async command void RadioPacket.setPayloadLength(message_t* msg, uint8_t length)
+       {
+               ASSERT( 1 <= length && length <= 125 );
+               ASSERT( call RadioPacket.headerLength(msg) + length + call RadioPacket.metadataLength(msg) <= sizeof(message_t) );
+
+               // we add the length of the CRC, which is automatically generated
+               getHeader(msg)->length = length + 2;
+       }
+
+       async command uint8_t RadioPacket.maxPayloadLength()
+       {
+               ASSERT( call Config.maxPayloadLength() - sizeof(rf230_header_t) <= 125 );
+
+               return call Config.maxPayloadLength() - sizeof(rf230_header_t);
+       }
+
+       async command uint8_t RadioPacket.metadataLength(message_t* msg)
+       {
+               return call Config.metadataLength(msg) + sizeof(rf230_metadata_t);
+       }
+
+       async command void RadioPacket.clear(message_t* msg)
+       {
+               // all flags are automatically cleared
+       }
+
+/*----------------- PacketTransmitPower -----------------*/
+
+       async command bool PacketTransmitPower.isSet(message_t* msg)
+       {
+               return call TransmitPowerFlag.get(msg);
+       }
+
+       async command uint8_t PacketTransmitPower.get(message_t* msg)
+       {
+               return getMeta(msg)->power;
+       }
+
+       async command void PacketTransmitPower.clear(message_t* msg)
+       {
+               call TransmitPowerFlag.clear(msg);
+       }
+
+       async command void PacketTransmitPower.set(message_t* msg, uint8_t value)
+       {
+               call TransmitPowerFlag.set(msg);
+               getMeta(msg)->power = value;
+       }
+
+/*----------------- PacketRSSI -----------------*/
+
+       async command bool PacketRSSI.isSet(message_t* msg)
+       {
+               return call RSSIFlag.get(msg);
+       }
+
+       async command uint8_t PacketRSSI.get(message_t* msg)
+       {
+               return getMeta(msg)->rssi;
+       }
+
+       async command void PacketRSSI.clear(message_t* msg)
+       {
+               call RSSIFlag.clear(msg);
+       }
+
+       async command void PacketRSSI.set(message_t* msg, uint8_t value)
+       {
+               // just to be safe if the user fails to clear the packet
+               call TransmitPowerFlag.clear(msg);
+
+               call RSSIFlag.set(msg);
+               getMeta(msg)->rssi = value;
+       }
+
+/*----------------- PacketTimeSyncOffset -----------------*/
+
+       async command bool PacketTimeSyncOffset.isSet(message_t* msg)
+       {
+               return call TimeSyncFlag.get(msg);
+       }
+
+       async command uint8_t PacketTimeSyncOffset.get(message_t* msg)
+       {
+               return call RadioPacket.headerLength(msg) + call RadioPacket.payloadLength(msg) - sizeof(timesync_absolute_t);
+       }
+
+       async command void PacketTimeSyncOffset.clear(message_t* msg)
+       {
+               call TimeSyncFlag.clear(msg);
+       }
+
+       async command void PacketTimeSyncOffset.set(message_t* msg, uint8_t value)
+       {
+               // we do not store the value, the time sync field is always the last 4 bytes
+               ASSERT( call PacketTimeSyncOffset.get(msg) == value );
+
+               call TimeSyncFlag.set(msg);
+       }
+
+/*----------------- 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;
+       }
+}
index ff2ed9ef32db21b4b49b2e65937241698f68bf2e..e2c63166aa6bd8cdc8dc08f877521a63b8afc68a 100755 (executable)
 #define MAIN_CRYSTAL_SPEED 10 /*MHz*/
 #define PLL_MULTIPLIER M16C62P_PLL_2
 
-#ifndef PLL_ON
-#define RF230_SLOW_SPI
+#define RF230_SLOW_SPI_MULLE
+
+#ifdef RF230_SLOW_SPI_MULLE
+#warning You are using the RF230 driver with a Mulle specific software fix. If you are using some very timecritical network protocols these may not work as intended! \
+You should not remove this fix unless you are totaly sure of what you are doing!
 #endif
 
 #include "m16c62phardware.h" // Header file for the MCU