]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/chips/cc2420_tkn154/CC2420TKN154P.nc
Merge TinyOS 2.1.1 into master.
[tinyos-2.x.git] / tos / chips / cc2420_tkn154 / CC2420TKN154P.nc
index 2546253eddd3f02c50205dc94ec298c5aa8fec80..d6a144b97c14a6e64a372c0b91f82b11efb31ad7 100644 (file)
@@ -43,12 +43,15 @@ module CC2420TKN154P
 
   provides {
     interface SplitControl;
+    interface RadioOff;
     interface RadioRx;
     interface RadioTx;
-    interface RadioOff;
+    interface SlottedCsmaCa;
+    interface UnslottedCsmaCa;
     interface EnergyDetection;
     interface Set<bool> as RadioPromiscuousMode;
     interface Timestamp;
+    interface GetNow<bool> as CCA;
   } uses {
     interface Notify<const void*> as PIBUpdate[uint8_t attributeID];
     interface LocalTime<T62500hz> as LocalTime;
@@ -72,88 +75,84 @@ module CC2420TKN154P
     S_STOPPED,
     S_STOPPING,
     S_STARTING,
-
-    S_RADIO_OFF,
     S_ED,
+    S_RADIO_OFF,
 
     S_RESERVE_RX_SPI,
-    S_RX_PREPARED,
+    S_RX_PENDING,
     S_RECEIVING,
     S_OFF_PENDING,
 
-    S_LOAD_TXFIFO,
-    S_TX_LOADED,
-    S_TX_ACTIVE,
-    S_TX_CANCEL,
-    S_TX_DONE,
+    S_LOAD_TXFIFO_NO_CSMA,
+    S_LOAD_TXFIFO_UNSLOTTED,
+    S_LOAD_TXFIFO_SLOTTED,
+    S_TX_PENDING,
+    S_TX_BACKOFF_UNSLOTTED,
+    S_TX_BACKOFF_SLOTTED,
+    S_TX_ACTIVE_NO_CSMA,
+    S_TX_ACTIVE_UNSLOTTED_CSMA,
+    S_TX_ACTIVE_SLOTTED_CSMA,
   } m_state_t;
 
   norace m_state_t m_state = S_STOPPED;
-  norace ieee154_txframe_t *m_txdata;
-  norace error_t m_txError;
-  norace ieee154_reftime_t m_txReferenceTime;
+  norace ieee154_txframe_t *m_txframe;
+  norace error_t m_txResult;
   norace bool m_ackFramePending;
-  uint32_t m_edDuration;
+  norace uint16_t m_remainingBackoff;
+  norace bool m_resume;
+  norace ieee154_csma_t *m_csma;
   bool m_pibUpdated;
-  uint8_t m_numCCA;
-  ieee154_reftime_t *m_t0Tx;
-  uint32_t m_dtTx;
 
-  norace uint8_t m_txLockOnCCAFail;
-  norace bool m_rxAfterTx = FALSE;
-  norace bool m_stop = FALSE;
+  /* timing */
+  norace ieee154_timestamp_t *m_timestamp;
+  norace uint32_t m_dt;
+  norace union {
+    uint32_t regular;
+    ieee154_timestamp_t native;
+  } m_t0;
+
 
+  /* energy detection */
+  int8_t m_maxEnergy;
+  uint32_t m_edDuration;
+  uint32_t m_edStartTime;
+
+  /* task prototypes */
+  task void energyDetectionTask();
   task void startDoneTask();
   task void rxControlStopDoneTask();
-  task void stopTask();
+  task void configSyncTask();
 
+  /* function prototypes */
   uint8_t dBmToPA_LEVEL(int dbm);
   void txDoneRxControlStopped();
   void rxSpiReserved();
   void txSpiReserved();
-  void txDoneSpiReserved();
-  void signalTxDone();
-  void finishTx();
-  void stopContinue();
+  void sendDoneSpiReserved();
   void offSpiReserved();
   void offStopRxDone();
-  void continueTxPrepare();
-  
+  uint16_t getRandomBackoff(uint8_t BE);
+  void loadTxFrame(ieee154_txframe_t *frame);
+  void checkEnableRxForACK();
 
-  /******************************/
-  /* StdControl Operations      */
-  /******************************/
-
-  /****************************************/
-  /*     TelosB Pin connection (debug)    */
-  /*                                      */
-  /* R1 = P6.6 = ADC6, R2 = P6.7 = ADC7   */
-  /* S1 = P2.3 = GIO2, S2 = P2.6 = GIO3   */
-  /* R1 is at 6pin-expansion pin 1,       */               
-  /* R2 is at 6pin-expansion pin 2,       */               
-  /****************************************/
+  /* ----------------------- StdControl Operations ----------------------- */
 
   command error_t SplitControl.start()
   {
-    // debug
-    //P6SEL &= ~0xC0;     // debug PIN: 6.6, 6.7, set to I/O function
-    //P6DIR |= 0xC0;      // output
-    //P6OUT &= ~0xC0;     // low
-    
     atomic {
       if (m_state == S_RADIO_OFF)
         return EALREADY;
-      else {
-        if (m_state != S_STOPPED)
-          return FAIL;
-        m_state = S_STARTING;
-      }
+      else if (m_state != S_STOPPED)
+        return FAIL;
+      m_state = S_STARTING;
     }
-    return call SpiResource.request();
+    call SpiResource.request(); /* continue in startSpiReserved() */
+    return SUCCESS;
   }
 
-  void startReserved()
+  void startSpiReserved()
   {
+    /* we own the SPI bus */
     call CC2420Power.startVReg();
   }
 
@@ -164,8 +163,8 @@ module CC2420TKN154P
 
   async event void CC2420Power.startOscillatorDone() 
   {
-    // default configuration (addresses, etc) has been written
     call CC2420Power.rfOff();
+    call CC2420Power.flushRxFifo();
     call CC2420Tx.unlockChipSpi();
     post startDoneTask();
   }
@@ -181,7 +180,6 @@ module CC2420TKN154P
     call CC2420Config.setTxPower(dBmToPA_LEVEL(IEEE154_DEFAULT_TRANSMITPOWER_dBm));
     call CC2420Config.sync();
     call SpiResource.release();
-    m_stop = FALSE;
     m_state = S_RADIO_OFF;
     signal SplitControl.startDone(SUCCESS);
   }
@@ -191,53 +189,46 @@ module CC2420TKN154P
     atomic {
       if (m_state == S_STOPPED)
         return EALREADY;
+      else if (m_state != S_RADIO_OFF)
+        return FAIL;
+      m_state = S_STOPPING;
     }
-    post stopTask();
+    call SpiResource.request();
     return SUCCESS;
   }
 
-  task void stopTask()
+  void stopSpiReserved()
   {
-    atomic {
-      if (m_state == S_RADIO_OFF)
-        m_state = S_STOPPING;
-    }
-    if (m_state != S_STOPPING)
-      post stopTask(); // this will not happen, because the caller has switched radio off
-    else 
-      if (call RxControl.stop() == EALREADY)
-        stopContinue();
+    /* we own the SPI bus */
+    call CC2420Power.rfOff();
+    call CC2420Power.flushRxFifo();
+    call CC2420Power.stopOscillator(); 
+    call CC2420Power.stopVReg();
+    call CC2420Tx.unlockChipSpi();
+    call SpiResource.release();
+    m_state  = S_STOPPED;
+    signal SplitControl.stopDone(SUCCESS);
   }
 
-  void stopContinue()
-  {
-    call SpiResource.request();
-  }
+  /* ----------------------- Helpers / PIB Updates ----------------------- */
 
-  void stopReserved()
+  /* Returns a random number [0,(2^BE) - 1] (uniform distr.) */
+  /* multiplied by backoff period time (in symbols)          */
+  uint16_t getRandomBackoff(uint8_t BE)
   {
-    // we own the SPI bus
-    atomic {
-      call CC2420Power.rfOff();
-      call CC2420Tx.unlockChipSpi();
-      call TxControl.stop();
-      call CC2420Power.stopOscillator(); 
-      call CC2420Power.stopVReg();
-      call SpiResource.release();
-      m_state  = S_STOPPED;
-      signal SplitControl.stopDone(SUCCESS);
-    }
+    uint16_t res = call Random.rand16();
+    uint16_t mask = 0xFFFF;
+    mask <<= BE;
+    mask = ~mask;
+    res &= mask;
+    return (res * IEEE154_aUnitBackoffPeriod);
   }
 
-  /*********************************/
-  /*  PIB Updates                  */
-  /*********************************/
-  
-  // input: power in dBm, output: PA_LEVEL parameter for cc2420 TXCTRL register
+  /* input: power in dBm, output: PA_LEVEL setting for CC2420 TXCTRL register */
   uint8_t dBmToPA_LEVEL(int dBm)
   {
     uint8_t result;
-    // the cc2420 has 8 discrete (documented) values - we take the closest
+    /* the cc2420 has 8 discrete (documented) values */
     if (dBm >= 0)
       result = 31;
     else if (dBm > -2)
@@ -275,16 +266,32 @@ module CC2420TKN154P
         call CC2420Config.setPanCoordinator(*((ieee154_macPanCoordinator_t*) PIBAttributeValue));
         break;
       case IEEE154_phyTransmitPower:
-        // lower 6 bits are twos-complement in dBm (range -32..+31 dBm)
+        /* lower 6 bits are twos-complement in dBm (range -32 to +31 dBm) */
         txpower = (*((ieee154_phyTransmitPower_t*) PIBAttributeValue)) & 0x3F;
         if (txpower & 0x20)
-          txpower |= 0xC0; // make it negative, to be interpreted as int8_t
+          txpower |= 0xC0; /* make it negative, to be interpreted as int8_t */
         call CC2420Config.setTxPower(dBmToPA_LEVEL((int8_t) txpower));
         break;
       case IEEE154_phyCCAMode:
         call CC2420Config.setCCAMode(*((ieee154_phyCCAMode_t*) PIBAttributeValue));
         break;
     }
+    if (m_state == S_RECEIVING || m_state == S_RX_PENDING)
+      post configSyncTask();
+  }
+
+  task void configSyncTask()
+  {
+    if (call SpiResource.immediateRequest() == SUCCESS) {
+      call CC2420Config.sync(); 
+      if (m_state == S_RECEIVING) {
+        // need to toggle radio state to make changes effective now
+        call CC2420Power.rfOff();
+        call CC2420Power.rxOn();
+      }
+      call SpiResource.release();
+    } else
+      post configSyncTask(); // spin (should be short time, until packet is received)
   }
 
   command void RadioPromiscuousMode.set( bool val )
@@ -292,67 +299,66 @@ module CC2420TKN154P
     call CC2420Config.setPromiscuousMode(val);
   }
 
-  /*********************************/
-  /* Energy Detection              */
-  /*********************************/
+  /* ----------------------- Energy Detection ----------------------- */
 
   command error_t EnergyDetection.start(uint32_t duration)
   {
-    error_t status = FAIL;
     atomic {
-      if (m_state == S_RADIO_OFF){
-        m_state = S_ED;
-        m_edDuration = duration;
-        status = SUCCESS;
-      }
+      if (m_state == S_ED)
+        return EALREADY;
+      else if (m_state != S_RADIO_OFF)
+        return FAIL;
+      m_state = S_ED;
     }
-    if (status == SUCCESS)
-      call SpiResource.request(); // we will not give up the SPI until we're done
-    return status;
+    m_edDuration = duration;
+    m_maxEnergy = -128 + 45; /* initialization (45 will be substracted below) */
+    call SpiResource.request();
+    return SUCCESS;
   }
 
   void edReserved()
   {
-    int8_t value, maxEnergy = -128;
-    uint32_t start = call LocalTime.get();
-    call CC2420Config.sync(); // put PIB changes into operation (if any)
+    call CC2420Config.sync(); /* put PIB changes into operation (if any) */
     call CC2420Power.rxOn();
-    // reading an RSSI value over SPI will usually almost
-    // take as much time as 8 symbols, i.e. there's 
-    // no point using an Alarm here (but maybe a BusyWait?)
-    while (!call TimeCalc.hasExpired(start, m_edDuration)){
-      if (call CC2420Power.rssi(&value) != SUCCESS)
-        continue;
-      if (value > maxEnergy)
-        maxEnergy = value;
-    }
-    // P = RSSI_VAL + RSSI_OFFSET [dBm] 
-    // RSSI_OFFSET is approximately -45.
-    if (maxEnergy > -128)
-      maxEnergy -= 45; 
-    call CC2420Power.rfOff();
-    m_state = S_RADIO_OFF;
-    call SpiResource.release();
-    signal EnergyDetection.done(SUCCESS, maxEnergy);
+    m_edStartTime = call LocalTime.get();
+    post energyDetectionTask();
   }
 
-  /****************************************/
-  /*     Transceiver Off                  */
-  /****************************************/
+  task void energyDetectionTask()
+  {
+    int8_t value;
+    if (call CC2420Power.rssi(&value) == SUCCESS)
+      if (value > m_maxEnergy)
+        m_maxEnergy = value;
+
+    if (call TimeCalc.hasExpired(m_edStartTime, m_edDuration)) {
+      /* P = RSSI_VAL + RSSI_OFFSET [dBm]  */
+      /* RSSI_OFFSET is approximately -45. */
+      m_maxEnergy -= 45; 
+      call CC2420Power.rfOff();
+      call CC2420Power.flushRxFifo();
+      call SpiResource.release();
+      m_state = S_RADIO_OFF;
+      signal EnergyDetection.done(SUCCESS, m_maxEnergy);
+    } else
+      post energyDetectionTask();
+  }
+
+  /* ----------------------- RadioOff ----------------------- */
 
   async command error_t RadioOff.off()
   {
+    error_t result;
     atomic {
       if (m_state == S_RADIO_OFF)
         return EALREADY;
-      else if (m_state != S_RECEIVING && m_state != S_TX_LOADED && m_state != S_RX_PREPARED)
+      else if (m_state != S_RECEIVING)
         return FAIL;
       m_state = S_OFF_PENDING;
     }
-    call SpiResource.release(); // may fail
-    if (call RxControl.stop() == EALREADY) // will trigger offStopRxDone()
-      offStopRxDone();
-    return SUCCESS;
+    result = call RxControl.stop();
+    ASSERT(result == SUCCESS);
+    return result;
   }
   
   void offStopRxDone()
@@ -360,14 +366,14 @@ module CC2420TKN154P
     if (call SpiResource.immediateRequest() == SUCCESS)
       offSpiReserved();
     else
-      call SpiResource.request();  // will trigger offSpiReserved()
+      call SpiResource.request();  /* will continue in offSpiReserved() */
   }
 
   void offSpiReserved()
   {
     call TxControl.stop();
     call CC2420Power.rfOff();
-    call CC2420Config.sync(); // put any PIB updates into operation
+    call CC2420Power.flushRxFifo();
     call CC2420Tx.unlockChipSpi();
     call SpiResource.release();
     m_state = S_RADIO_OFF;
@@ -379,331 +385,532 @@ module CC2420TKN154P
     return m_state == S_RADIO_OFF;
   }
 
-  /****************************************/
-  /*     Receive Operations               */
-  /****************************************/
+  /* ----------------------- RadioRx ----------------------- */
 
-  async command error_t RadioRx.prepare()
+  async command error_t RadioRx.enableRx(uint32_t t0, uint32_t dt)
   {
+    error_t result;
     atomic {
-      if (call RadioRx.isPrepared())
-        return EALREADY;
-      else if (m_state != S_RADIO_OFF)
+      if (m_state != S_RADIO_OFF)
         return FAIL;
       m_state = S_RESERVE_RX_SPI;
     }
-    if (call RxControl.start() != SUCCESS){  // will trigger rxStartRxDone()
-      m_state = S_RADIO_OFF;
-      call Leds.led0On();
-      return FAIL; 
-    }
-    return SUCCESS; 
-  }
-
-  void rxStartRxDone()
-  {
-    if (call SpiResource.immediateRequest() == SUCCESS)   // will trigger rxSpiReserved()
-      rxSpiReserved();
-    else
-      call SpiResource.request();
+    m_t0.regular = t0;
+    m_dt = dt;
+    result = call RxControl.start(); 
+    ASSERT(result == SUCCESS);
+    if (result == SUCCESS)
+      if (call SpiResource.immediateRequest() == SUCCESS)
+        rxSpiReserved();
+      else
+        call SpiResource.request();   /* will continue in rxSpiReserved()  */
+    return result; 
   }
 
   void rxSpiReserved()
   {
-    call CC2420Config.sync(); // put PIB changes into operation
-    call TxControl.start();   // for timestamping
-    m_state = S_RX_PREPARED;
-    signal RadioRx.prepareDone(); // keep owning the SPI
-  }
-
-  async command bool RadioRx.isPrepared()
-  { 
-    return m_state == S_RX_PREPARED;
-  }
-
-  async command error_t RadioRx.receive(ieee154_reftime_t *t0, uint32_t dt)
-  {
+    m_state = S_RX_PENDING;
+    call CC2420Config.sync(); /* put any pending PIB changes into operation      */
+    call TxControl.stop();    /* reset Tx logic for timestamping (SFD interrupt) */
+    call TxControl.start();   
     atomic {
-      if (m_state != S_RX_PREPARED){
-        call Leds.led0On();
-        return FAIL;
-      }
-      if (t0 != NULL && dt)
-        call ReliableWait.waitRx(t0, dt);
-      else
+      if (call TimeCalc.hasExpired(m_t0.regular, m_dt))
         signal ReliableWait.waitRxDone();
+      else
+        call ReliableWait.waitRx(m_t0.regular, m_dt); /* will signal waitRxDone() just in time */
     }
-    return SUCCESS;
   }
 
   async event void ReliableWait.waitRxDone()
   {
+    error_t result;
     atomic {
-      if (call CC2420Power.rxOn() != SUCCESS)
-        call Leds.led0On();
       m_state = S_RECEIVING;
+      result = call CC2420Power.rxOn(); 
     }
+    ASSERT(result == SUCCESS);
     call CC2420Tx.lockChipSpi();
-    call SpiResource.release();    
+    call SpiResource.release();
+    signal RadioRx.enableRxDone();
   }
 
-  event message_t* CC2420Rx.received(message_t *data, ieee154_reftime_t *timestamp) 
+  event message_t* CC2420Rx.received(message_t *frame, ieee154_timestamp_t *timestamp) 
   {
     if (m_state == S_RECEIVING)
-      return signal RadioRx.received(data, timestamp);
+      return signal RadioRx.received(frame, timestamp);
     else
-      return data;
+      return frame;
   }
 
   async command bool RadioRx.isReceiving()
-  { 
+  {
     return m_state == S_RECEIVING;
   }
 
-  /******************************/
-  /*     Transmit Operations    */
-  /******************************/
+  /* ----------------------- RadioTx ----------------------- */
 
-  async command error_t RadioTx.load(ieee154_txframe_t *frame)
+  async command error_t RadioTx.transmit( ieee154_txframe_t *frame, const ieee154_timestamp_t *t0, uint32_t dt )
   {
-    bool startRxControl;
+    if( frame == NULL || frame->header == NULL || 
+        frame->payload == NULL || frame->metadata == NULL || 
+        (frame->headerLen + frame->payloadLen + 2) > IEEE154_aMaxPHYPacketSize )
+      return EINVAL;
+
     atomic {
-      if (m_state != S_RADIO_OFF && m_state != S_TX_LOADED)
+      if( m_state != S_RADIO_OFF )
         return FAIL;
-      startRxControl = (m_state == S_RADIO_OFF);
-      m_txdata = frame;
-      m_state = S_LOAD_TXFIFO;
+      m_state = S_LOAD_TXFIFO_NO_CSMA;
+    }
+    m_txframe = frame;
+    if( t0 == NULL )
+      m_dt = 0;
+    else {
+      memcpy( &m_t0.native, t0, sizeof(ieee154_timestamp_t) );
+      m_dt = dt;
     }
-    if (!startRxControl)
-      continueTxPrepare();
-    else if (call RxControl.start() != SUCCESS) // will trigger continueTxPrepare()
-      call Leds.led0On();
+    loadTxFrame(frame); /* will continue in loadDoneRadioTx() */
     return SUCCESS;
   }
 
-  void continueTxPrepare()
+  void loadDoneRadioTx()
   {
-    if (call SpiResource.immediateRequest() == SUCCESS) 
-      txSpiReserved();
-    else
-      call SpiResource.request(); // will trigger txSpiReserved()
+    /* frame was loaded into TXFIFO */
+    atomic {
+      m_state = S_TX_PENDING;
+      if (m_dt == 0)
+        signal ReliableWait.waitTxDone();
+      else
+        call ReliableWait.waitTx(&m_t0.native, m_dt); /* will signal waitTxDone() just in time */
+    }
   }
 
-  void txSpiReserved()
+  async event void ReliableWait.waitTxDone()
   {
-    call CC2420Config.sync();
-    call TxControl.start();
-    if (call CC2420Tx.loadTXFIFO(m_txdata) != SUCCESS)
-      call Leds.led0On();
+    error_t result;
+    ASSERT(m_state == S_TX_PENDING);
+    atomic {
+      m_state = S_TX_ACTIVE_NO_CSMA;
+      result = call CC2420Tx.send(FALSE); /* transmit without CCA, this must succeed */ 
+      checkEnableRxForACK();
+    }
+    ASSERT(result == SUCCESS); 
   }
 
-  async event void CC2420Tx.loadTXFIFODone(ieee154_txframe_t *data, error_t error)
+  inline void txDoneRadioTx(ieee154_txframe_t *frame, ieee154_timestamp_t *timestamp, error_t result)
+  {
+    /* transmission completed */
+    signal RadioTx.transmitDone(frame, timestamp, result);
+  }
+
+  /* ----------------------- UnslottedCsmaCa ----------------------- */
+
+  async command error_t UnslottedCsmaCa.transmit(ieee154_txframe_t *frame, ieee154_csma_t *csma)
   {
-    if (m_state != S_LOAD_TXFIFO || error != SUCCESS)
-      call Leds.led0On();
-    m_state = S_TX_LOADED;
-    signal RadioTx.loadDone();  // we keep owning the SPI
+    if( frame == NULL || frame->header == NULL || 
+        frame->payload == NULL || frame->metadata == NULL || 
+        (frame->headerLen + frame->payloadLen + 2) > IEEE154_aMaxPHYPacketSize )
+      return EINVAL;
+    atomic {
+      if( m_state != S_RADIO_OFF )
+        return FAIL;
+      m_state = S_LOAD_TXFIFO_UNSLOTTED;
+    }
+    m_txframe = frame;
+    m_csma = csma;
+    loadTxFrame(frame); /* will continue in nextIterationUnslottedCsma()  */
+    return SUCCESS;
   }
 
-  async command ieee154_txframe_t* RadioTx.getLoadedFrame()
+  void nextIterationUnslottedCsma()
   {
-    if (m_state == S_TX_LOADED)
-     return m_txdata;
-    else 
-      return NULL;
+    /* wait for a random time of [0,(2^BE) - 1] backoff slots */
+    uint16_t backoff = getRandomBackoff(m_csma->BE); 
+    m_state = S_TX_BACKOFF_UNSLOTTED;
+    call ReliableWait.waitBackoff(backoff);  /* will continue in waitBackoffDoneUnslottedCsma()  */
   }
 
-  async command error_t RadioTx.transmit(ieee154_reftime_t *t0, uint32_t dt, uint8_t numCCA, bool ackRequest)
+  void waitBackoffDoneUnslottedCsma()
   {
+    /* backoff finished, try to transmit now */
+    int8_t dummy;
+    ieee154_txframe_t *frame = NULL;
+    ieee154_csma_t *csma = NULL;
+
     atomic {
-      if (m_state != S_TX_LOADED)
-        return FAIL;
-      m_numCCA = numCCA;
-      m_t0Tx = t0;
-      m_dtTx = dt;
-      if (numCCA){
-        // for CCA we need to be in Rx mode
-        call CC2420Power.rxOn();
-        call ReliableWait.busyWait(20); // turnaround + CCA valid time
-        if (numCCA == 2){
-          // first CCA is done in software (8 symbols after backoff boundary)
-          if (t0 != NULL){
-            call ReliableWait.waitCCA(t0, dt-IEEE154_aUnitBackoffPeriod-12);
-            return SUCCESS;
-          }
+      /* The CC2420 needs to be in an Rx mode for STXONCCA strobe */
+      /* Note: the receive logic of the CC2420 driver is not yet  */
+      /* started, i.e. we cannot (yet) receive any packets        */
+      call CC2420Power.rxOn();
+      m_state = S_TX_ACTIVE_UNSLOTTED_CSMA;
+
+      /* wait for CC2420 Rx to calibrate + CCA valid time */
+      while (call CC2420Power.rssi(&dummy) != SUCCESS)
+        ;
+
+      /* transmit with a single CCA done in hardware (STXONCCA strobe) */
+      if (call CC2420Tx.send(TRUE) == SUCCESS) {
+        /* frame is being sent now, do we need Rx logic ready for an ACK? */
+        checkEnableRxForACK();
+      } else {
+        /* channel is busy */
+        call CC2420Power.rfOff();
+        /* we might have accidentally caught something during CCA, flush it out */
+        call CC2420Power.flushRxFifo(); 
+        m_csma->NB += 1;
+        if (m_csma->NB > m_csma->macMaxCsmaBackoffs) {
+          /* CSMA-CA failure, we're done. The MAC may decide to retransmit. */
+          frame = m_txframe;
+          csma = m_csma;
+          /* continue below */
+        } else {
+          /* Retry -> next iteration of the unslotted CSMA-CA */
+          m_csma->BE += 1;
+          if (m_csma->BE > m_csma->macMaxBE)
+            m_csma->BE = m_csma->macMaxBE;
+          nextIterationUnslottedCsma();
         }
       }
-      signal ReliableWait.waitCCADone();
     }
+    if (frame != NULL) {
+      call CC2420Tx.unlockChipSpi();
+      call TxControl.stop();
+      call SpiResource.release();
+      m_state = S_RADIO_OFF;
+      signal UnslottedCsmaCa.transmitDone(frame, csma, FALSE, FAIL);
+    }
+  }
+
+  inline void txDoneUnslottedCsma(ieee154_txframe_t *frame, ieee154_csma_t *csma, bool ackPendingFlag, error_t result)
+  {
+    /* transmission completed */
+    signal UnslottedCsmaCa.transmitDone(frame, csma, ackPendingFlag, result);
+  }
+
+  /* ----------------------- SlottedCsmaCa ----------------------- */
+
+    /* The slotted CSMA-CA requires very exact timing, because transmissions
+     * must start on 320 us backoff boundaries. Because it is accessed over SPI
+     * the CC2420 is not good at meeting these timing requirements, so consider 
+     * the "SlottedCsmaCa"-code below as experimental. */
+
+  async command error_t SlottedCsmaCa.transmit(ieee154_txframe_t *frame, ieee154_csma_t *csma,
+      const ieee154_timestamp_t *slot0Time, uint32_t dtMax, bool resume, uint16_t remainingBackoff)
+  {
+    if( frame == NULL || frame->header == NULL || slot0Time == NULL ||
+        frame->payload == NULL || frame->metadata == NULL || 
+        (frame->headerLen + frame->payloadLen + 2) > IEEE154_aMaxPHYPacketSize)
+      return EINVAL;
+    atomic {
+      if( m_state != S_RADIO_OFF )
+        return FAIL;
+      m_state = S_LOAD_TXFIFO_SLOTTED;
+    }
+    m_txframe = frame;
+    m_csma = csma;
+    memcpy( &m_t0.native, slot0Time, sizeof(ieee154_timestamp_t) );
+    m_dt = dtMax;
+    m_resume = resume;
+    m_remainingBackoff = remainingBackoff;
+    loadTxFrame(frame); /* will continue in nextIterationSlottedCsma()  */
     return SUCCESS;
   }
 
-  async event void ReliableWait.waitCCADone()
-  {
-    bool cca = call CC2420Tx.cca();
-    if (m_numCCA == 2 && !cca){
-      // channel is busy
-      ieee154_reftime_t now;
-      call ReferenceTime.getNow(&now, IEEE154_aUnitBackoffPeriod+12);
-      memcpy(&m_txReferenceTime, &now, sizeof(ieee154_reftime_t));
-      m_ackFramePending = FALSE;
-      m_txError = EBUSY;
-      signalTxDone();
-      return;
-    } else {
-      // the second CCA (or first CCA if there's only one) is done in hardware...
-      uint16_t offset = 0;
-      if (m_numCCA)
-        offset = 12;
-      if (m_t0Tx)
-        call ReliableWait.waitTx(m_t0Tx, m_dtTx-offset);
-      else
-        signal ReliableWait.waitTxDone();
+  void nextIterationSlottedCsma()
+  {
+    uint32_t dtTxTarget;
+    uint16_t backoff;
+    ieee154_txframe_t *frame = NULL;
+    ieee154_csma_t *csma = NULL;
+
+    atomic {
+      if (m_resume) {
+        backoff = m_remainingBackoff;
+        m_resume = FALSE;
+      } else
+        backoff = getRandomBackoff(m_csma->BE);
+      dtTxTarget = call TimeCalc.timeElapsed(call ReferenceTime.toLocalTime(&m_t0.native), call LocalTime.get());
+      dtTxTarget += backoff;
+      if (dtTxTarget > m_dt) {
+        /* frame doesn't fit into remaining CAP */
+        uint32_t overlap = dtTxTarget - m_dt;
+        overlap = overlap + (IEEE154_aUnitBackoffPeriod - (overlap % IEEE154_aUnitBackoffPeriod));
+        backoff = overlap;
+        frame = m_txframe;
+        csma = m_csma;
+      } else {
+        /* backoff now */
+        m_state = S_TX_BACKOFF_SLOTTED;
+        call ReliableWait.waitBackoff(backoff);  /* will continue in waitBackoffDoneSlottedCsma()  */
+      }
+    }
+    if (frame != NULL) { /* frame didn't fit in the remaining CAP */
+      call CC2420Tx.unlockChipSpi();
+      call TxControl.stop();
+      call SpiResource.release();
+      m_state = S_RADIO_OFF;
+      signal SlottedCsmaCa.transmitDone(frame, csma, FALSE, backoff, ERETRY);
     }
   }
 
-  async event void ReliableWait.waitTxDone()
+  void waitBackoffDoneSlottedCsma()
   {
-    m_state = S_TX_ACTIVE;
-    call CC2420Tx.send(m_numCCA>0); // go (with or without CCA) !
+    int8_t dummy;
+    bool ccaFailure = FALSE;
+    error_t result = FAIL;
+    ieee154_txframe_t *frame = NULL;
+    ieee154_csma_t *csma = NULL;
+
+    atomic {
+      /* The CC2420 needs to be in an Rx mode for STXONCCA strobe */
+      /* Note: the receive logic of the CC2420 driver is not yet  */
+      /* started, i.e. we cannot (yet) receive any packets        */
+      call CC2420Power.rxOn();
+      m_state = S_TX_ACTIVE_SLOTTED_CSMA;
+
+      /* wait for CC2420 Rx to calibrate + CCA valid time */
+      while (call CC2420Power.rssi(&dummy) != SUCCESS)
+        ;
+
+      /* perform CCA on slot boundary (i.e. 8 symbols after backoff bounday);    */
+      /* this platform-specific command is supposed to return just in time, so   */
+      /* that the frame will be transmitted exactly on the next backoff boundary */
+      if (call ReliableWait.ccaOnBackoffBoundary(&m_t0.native)) {
+        /* first CCA succeeded */
+        if (call CC2420Tx.send(TRUE) == SUCCESS) {
+          /* frame is being sent now, do we need Rx logic ready for an ACK? */
+          checkEnableRxForACK();
+          return;
+        } else
+          ccaFailure = TRUE; /* second CCA failed */
+      } else
+        ccaFailure = TRUE; /* first CCA failed */
+
+      /* did not transmit the frame */
+      call CC2420Power.rfOff();
+      call CC2420Power.flushRxFifo(); /* we might have (accidentally) caught something */
+      m_state = S_LOAD_TXFIFO_SLOTTED;
+      if (ccaFailure) {
+        m_csma->NB += 1;
+        if (m_csma->NB > m_csma->macMaxCsmaBackoffs) {
+          /* CSMA-CA failure, we're done. The MAC may decide to retransmit. */
+          frame = m_txframe;
+          csma = m_csma;
+          result = FAIL;
+        } else {
+          /* next iteration of slotted CSMA-CA */
+          m_csma->BE += 1;
+          if (m_csma->BE > m_csma->macMaxBE)
+            m_csma->BE = m_csma->macMaxBE;
+          nextIterationSlottedCsma();
+        }
+      } else {
+        /* frame didn't fit into remaining CAP, this can only happen */
+        /* if the runtime overhead was too high. this should actually not happen.  */
+        /* (in principle the frame should have fitted, because we checked before) */
+        frame = m_txframe;
+        csma = m_csma;
+        result = ERETRY;
+      }
+    }
+    if (frame != NULL) {
+      call CC2420Tx.unlockChipSpi();
+      call TxControl.stop();
+      call SpiResource.release();
+      m_state = S_RADIO_OFF;
+      signal SlottedCsmaCa.transmitDone(frame, csma, FALSE, 0, result);
+    }
   }
 
-  async event void CC2420Tx.transmissionStarted( ieee154_txframe_t *data )
+  inline void txDoneSlottedCsmaCa(ieee154_txframe_t *frame, ieee154_csma_t *csma, 
+      bool ackPendingFlag, uint16_t remainingBackoff, error_t result)
   {
-    uint8_t frameType = data->header->mhr[0] & FC1_FRAMETYPE_MASK;
-    uint8_t token = data->headerLen;
-    signal Timestamp.transmissionStarted(frameType, data->handle, data->payload, token);
+    /* transmission completed */
+    signal SlottedCsmaCa.transmitDone(frame, csma, ackPendingFlag, remainingBackoff, result);
   }
 
-  async event void CC2420Tx.transmittedSFD(uint32_t time, ieee154_txframe_t *data)
+  /* ----------------------- Common Tx Operations ----------------------- */
+
+  void loadTxFrame(ieee154_txframe_t *frame)
   {
-    uint8_t frameType = data->header->mhr[0] & FC1_FRAMETYPE_MASK;
-    uint8_t token = data->headerLen;
-    signal Timestamp.transmittedSFD(time, frameType, data->handle, data->payload, token);
-    // ATTENTION: here we release the SPI, so we can receive a possible ACK
-    call SpiResource.release();
+    if (call SpiResource.isOwner() || call SpiResource.immediateRequest() == SUCCESS) 
+      txSpiReserved();
+    else
+      call SpiResource.request(); /* will continue in txSpiReserved() */
+  }
+
+  void txSpiReserved()
+  {
+    error_t result;
+    call CC2420Config.sync();
+    call TxControl.start();
+    result = call CC2420Tx.loadTXFIFO(m_txframe);
+    ASSERT(result == SUCCESS);
+  }
+
+  async event void CC2420Tx.loadTXFIFODone(ieee154_txframe_t *data, error_t error)
+  {
+    atomic {
+      switch (m_state)
+      {
+        case S_LOAD_TXFIFO_NO_CSMA: loadDoneRadioTx(); break;
+        case S_LOAD_TXFIFO_UNSLOTTED: nextIterationUnslottedCsma(); break;
+        case S_LOAD_TXFIFO_SLOTTED: nextIterationSlottedCsma(); break;
+        default: ASSERT(0); break;
+      }
+    }
   }
 
-  async command void Timestamp.modifyMACPayload(uint8_t token, uint8_t offset, uint8_t* buf, uint8_t len )
+  void checkEnableRxForACK()
   {
-    if (m_state == S_TX_ACTIVE)
-      call CC2420Tx.modify(offset+1+token, buf, len);
+    /* A frame is currently being transmitted, check if we  */
+    /* need the Rx logic ready for the ACK                  */
+    bool ackRequest = (m_txframe->header->mhr[MHR_INDEX_FC1] & FC1_ACK_REQUEST) ? TRUE : FALSE;
+    error_t result = SUCCESS;
+
+    if (ackRequest) {
+      /* release SpiResource and start Rx logic, so the latter  */
+      /* can take over after Tx is finished to receive the ACK */
+      call SpiResource.release();
+      result = call RxControl.start();
+    }
+    ASSERT(result == SUCCESS);
   }
 
-  async event void CC2420Tx.sendDone(ieee154_txframe_t *frame, ieee154_reftime_t *referenceTime, 
-      bool ackPendingFlag, error_t error)
+  async event void CC2420Tx.sendDone(ieee154_txframe_t *frame, 
+      ieee154_timestamp_t *timestamp, bool ackPendingFlag, error_t result)
   {
-    memcpy(&m_txReferenceTime, referenceTime, sizeof(ieee154_reftime_t));
+    m_timestamp = timestamp;
     m_ackFramePending = ackPendingFlag;
-    m_txError = error;
-    if (error == EBUSY) // CCA failure, i.e. didn't transmit
-      signalTxDone();
-    else 
-      // reset radio
-      if (call RxControl.stop() != SUCCESS) // will trigger txDoneRxControlStopped()
-        call Leds.led0On();
+    m_txResult = result;
+    if (!call SpiResource.isOwner()) {
+      /* this means an ACK was requested and during the transmission */
+      /* we released the Spi to allow the Rx part to take over */
+      ASSERT((frame->header->mhr[MHR_INDEX_FC1] & FC1_ACK_REQUEST));
+      result = call RxControl.stop();
+      ASSERT(result == SUCCESS); /* will continue in txDoneRxControlStopped() */
+    } else
+      sendDoneSpiReserved();
   }
 
   void txDoneRxControlStopped()
   {
-    // get SPI to switch radio off
+    /* get the Spi to switch radio off */
     if (call SpiResource.isOwner() || call SpiResource.immediateRequest() == SUCCESS) 
-      txDoneSpiReserved();
+      sendDoneSpiReserved();
     else
-      call SpiResource.request(); // will trigger txDoneSpiReserved()
+      call SpiResource.request(); /* will continue in sendDoneSpiReserved() */
   }
 
-  void txDoneSpiReserved() 
-  { 
-    // switch radio off
-    call CC2420Power.rfOff(); 
+  void sendDoneSpiReserved()
+  {
+    /* transmission completed, we're owning the Spi, Rx logic is disabled */
+    m_state_t state = m_state;
+    ieee154_txframe_t *frame = m_txframe;
+    ieee154_csma_t *csma = m_csma;
+
+    call CC2420Power.rfOff();
+    call CC2420Power.flushRxFifo();
+    call CC2420Tx.unlockChipSpi();
     call TxControl.stop();
-    call SpiResource.release(); // for RxControl.start to succeed
-    if (m_txError == SUCCESS)
-      signalTxDone();
-    else {
-      call TxControl.start();
-      call RxControl.start(); // will trigger txDoneRxControlStarted()
-    }
+    call SpiResource.release();
+    m_state = S_RADIO_OFF;
+
+    if (state == S_TX_ACTIVE_NO_CSMA)
+      txDoneRadioTx(frame, m_timestamp, m_txResult); 
+    else if (state == S_TX_ACTIVE_UNSLOTTED_CSMA)
+      txDoneUnslottedCsma(frame, csma, m_ackFramePending, m_txResult);
+    else if (state == S_TX_ACTIVE_SLOTTED_CSMA)
+      txDoneSlottedCsmaCa(frame, csma, m_ackFramePending, m_remainingBackoff, m_txResult);
+    else
+      ASSERT(0);
   }
 
-  void txDoneRxControlStarted()
+  async event void CC2420Tx.transmissionStarted( ieee154_txframe_t *frame )
   {
-    m_state = S_TX_DONE;
-    call SpiResource.request(); // will trigger signalTxDone()
+    uint8_t frameType = frame->header->mhr[0] & FC1_FRAMETYPE_MASK;
+    uint8_t token = frame->headerLen;
+    signal Timestamp.transmissionStarted(frameType, frame->handle, frame->payload, token);
   }
 
-  void signalTxDone() 
-  { 
-    // radio is off, Rx component is started, radio is loaded, we own the SPI
-    if (m_txError == SUCCESS)
-      m_state = S_RADIO_OFF;
-    else
-      m_state = S_TX_LOADED;
-    signal RadioTx.transmitDone(m_txdata, &m_txReferenceTime, m_ackFramePending, m_txError);
+  async event void CC2420Tx.transmittedSFD( uint32_t time, ieee154_txframe_t *frame )
+  {
+    uint8_t frameType = frame->header->mhr[0] & FC1_FRAMETYPE_MASK;
+    uint8_t token = frame->headerLen;
+    signal Timestamp.transmittedSFD( time, frameType, frame->handle, frame->payload, token );
   }
 
-  /*************/
-  /* RxControl */
-  /*************/
-
-  async event void RxControl.stopDone(error_t error)
+  async command void Timestamp.modifyMACPayload( uint8_t token, uint8_t offset, uint8_t* buf, uint8_t len )
   {
-    post rxControlStopDoneTask();
+    if (m_state == S_TX_ACTIVE_NO_CSMA || 
+        m_state == S_TX_ACTIVE_SLOTTED_CSMA ||
+        m_state == S_LOAD_TXFIFO_UNSLOTTED )
+      call CC2420Tx.modify( offset+1+token, buf, len );
   }
 
-  task void rxControlStopDoneTask()
+  async event void ReliableWait.waitBackoffDone()
   {
-    if (m_stop && m_state != S_STOPPING)
-      return;
     switch (m_state)
     {
-      case S_OFF_PENDING: offStopRxDone(); break;
-      case S_RX_PREPARED: rxStartRxDone(); break;
-      case S_TX_ACTIVE: txDoneRxControlStopped(); break;
-      case S_STOPPING: stopContinue(); break;            
-      default: // huh ?
-           call Leds.led0On(); break;
+      case S_TX_BACKOFF_SLOTTED: waitBackoffDoneSlottedCsma(); break;
+      case S_TX_BACKOFF_UNSLOTTED: waitBackoffDoneUnslottedCsma(); break;
+      default: ASSERT(0); break;
     }
   }
 
-  async event void RxControl.startDone(error_t error)
+  /* ----------------------- RxControl ----------------------- */
+
+  async event void RxControl.stopDone(error_t error)
+  {
+    post rxControlStopDoneTask();
+  }
+
+  task void rxControlStopDoneTask()
   {
     switch (m_state)
     {
-      case S_RESERVE_RX_SPI: rxStartRxDone(); break;
-      case S_LOAD_TXFIFO: continueTxPrepare(); break;
-      case S_TX_ACTIVE: txDoneRxControlStarted(); break;
-      default: // huh ?
-           call Leds.led0On(); break;
+      case S_OFF_PENDING:              offStopRxDone(); break;
+      case S_TX_ACTIVE_NO_CSMA:        /* fall through */
+      case S_TX_ACTIVE_UNSLOTTED_CSMA: /* fall through */
+      case S_TX_ACTIVE_SLOTTED_CSMA:   txDoneRxControlStopped(); break;
+      default:                         ASSERT(0); break;
     }
   }
 
-  /***********************/
-  /* SPI Bus Arbitration */
-  /***********************/
+  /* ----------------------- SPI Bus Arbitration ----------------------- */
 
   event void SpiResource.granted() 
   {
     switch (m_state)
     {
-      case S_STARTING: startReserved(); break;
-      case S_ED: edReserved(); break;
-      case S_RESERVE_RX_SPI: rxSpiReserved(); break;
-      case S_LOAD_TXFIFO: txSpiReserved(); break;
-      case S_TX_ACTIVE: txDoneSpiReserved(); break;
-      case S_STOPPING: stopReserved(); break;
-      case S_TX_DONE: signalTxDone(); break;
-      case S_OFF_PENDING: offSpiReserved(); break;
-      default: // huh ?
-           call Leds.led0On(); break;
+      case S_STARTING:                 startSpiReserved(); break;
+      case S_ED:                       edReserved(); break;
+      case S_RESERVE_RX_SPI:           rxSpiReserved(); break;
+      case S_LOAD_TXFIFO_NO_CSMA:      /* fall through */
+      case S_LOAD_TXFIFO_UNSLOTTED:    /* fall through */
+      case S_LOAD_TXFIFO_SLOTTED:      txSpiReserved(); break;
+      case S_TX_ACTIVE_NO_CSMA:        /* fall through */
+      case S_TX_ACTIVE_UNSLOTTED_CSMA: /* fall through */
+      case S_TX_ACTIVE_SLOTTED_CSMA:   sendDoneSpiReserved(); break;
+      case S_STOPPING:                 stopSpiReserved(); break;
+      case S_OFF_PENDING:              offSpiReserved(); break;
+      default:                         ASSERT(0); break;
     }
   }
 
+  async command bool CCA.getNow()
+  {
+    return call CC2420Tx.cca();
+  }
+
+  default event void SplitControl.startDone(error_t error) {}
+  default event void SplitControl.stopDone(error_t error) {}
+  
+  default async event void UnslottedCsmaCa.transmitDone(ieee154_txframe_t *frame, 
+      ieee154_csma_t *csma, bool ackPendingFlag, error_t result) {}
+  default async event void SlottedCsmaCa.transmitDone(ieee154_txframe_t *frame, ieee154_csma_t *csma, 
+      bool ackPendingFlag,  uint16_t remainingBackoff, error_t result) {}
 
-  default event void SplitControl.startDone(error_t error){}
-  default event void SplitControl.stopDone(error_t error){}
-  default async event void Timestamp.transmissionStarted(uint8_t frameType, uint8_t msduHandle, uint8_t *msdu, uint8_t token){}
-  default async event void Timestamp.transmittedSFD(uint32_t time, uint8_t frameType, uint8_t msduHandle, uint8_t *msdu, uint8_t token){}
+  default async event void Timestamp.transmissionStarted(uint8_t frameType, uint8_t msduHandle, uint8_t *msdu, uint8_t token) {}
+  default async event void Timestamp.transmittedSFD(uint32_t time, uint8_t frameType, uint8_t msduHandle, uint8_t *msdu, uint8_t token) {}
 }