]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/chips/cc2420/CC2420TransmitP.nc
Update of the default CC2420 to the low power listening stack. The "acknowledgement...
[tinyos-2.x.git] / tos / chips / cc2420 / CC2420TransmitP.nc
index 9793949b57782028da323f7cf16166c95f503b0a..b1a43f60b0acb6bba5e0e8c4e54df3178d262c36 100644 (file)
 
 /**
  * @author Jonathan Hui <jhui@archrock.com>
+ * @author David Moss
+ * @author Jung Il Choi
  * @version $Revision$ $Date$
  */
 
+#include "CC2420.h"
+#include "crc.h"
+
 module CC2420TransmitP {
 
   provides interface Init;
-  provides interface AsyncStdControl;
+  provides interface StdControl;
   provides interface CC2420Transmit as Send;
-  provides interface CsmaBackoff;
+  provides interface RadioBackoff;
   provides interface RadioTimeStamping as TimeStamp;
-
+  provides interface CC2420Cca;
+  
   uses interface Alarm<T32khz,uint32_t> as BackoffTimer;
+  uses interface CC2420Packet;
   uses interface GpioCapture as CaptureSFD;
   uses interface GeneralIO as CCA;
   uses interface GeneralIO as CSN;
@@ -56,10 +63,11 @@ module CC2420TransmitP {
   uses interface CC2420Strobe as STXON;
   uses interface CC2420Strobe as STXONCCA;
   uses interface CC2420Strobe as SFLUSHTX;
+  uses interface CC2420Register as MDMCTRL1;
 
+  uses interface Timer<TMilli> as LplDisableTimer;
   uses interface CC2420Receive;
   uses interface Leds;
-
 }
 
 implementation {
@@ -73,7 +81,9 @@ implementation {
     S_SFD,
     S_EFD,
     S_ACK_WAIT,
-    S_CANCEL,
+    S_LOAD_CANCEL,
+    S_TX_CANCEL,
+    S_CCA_CANCEL,
   } cc2420_transmit_state_t;
 
   // This specifies how many jiffies the stack should wait after a
@@ -84,48 +94,49 @@ implementation {
     CC2420_ABORT_PERIOD = 320
   };
   
-  norace message_t* m_msg;
+  norace message_t *m_msg;
+  
   norace bool m_cca;
+  
   norace uint8_t m_tx_power;
+  
   cc2420_transmit_state_t m_state = S_STOPPED;
+  
   bool m_receiving = FALSE;
+  
   uint16_t m_prev_time;
+  
+  bool signalSendDone;
+  
+  /** TRUE if the current lpl tx is taking place with continuous modulation */
+  norace bool continuousModulation;
+  
+  /** Total CCA checks that showed no activity before the NoAck LPL send */
+  norace int8_t totalCcaChecks;
+  
+  /** The initial backoff period */
+  norace uint16_t myInitialBackoff;
+  
+  /** The congestion backoff period */
+  norace uint16_t myCongestionBackoff;
+  
+  /** The low power listening backoff period */
+  norace uint16_t myLplBackoff;
+  
 
+  /***************** Prototypes ****************/
+  error_t send( message_t *p_msg, bool cca );
+  error_t resend( bool cca );
   void loadTXFIFO();
   void attemptSend();
-
-  cc2420_header_t* getHeader( message_t* msg ) {
-    return (cc2420_header_t*)( msg->data - sizeof( cc2420_header_t ) );
-  }
-
-  cc2420_metadata_t* getMetadata( message_t* msg ) {
-    return (cc2420_metadata_t*)msg->metadata;
-  }
-
-  void startBackoffTimer(uint16_t time) {
-    call BackoffTimer.start(time);
-  }
-
-  void stopBackoffTimer() {
-    call BackoffTimer.stop();
-  }
-
-  error_t acquireSpiResource() {
-    error_t error = call SpiResource.immediateRequest();
-    if ( error != SUCCESS )
-      call SpiResource.request();
-    return error;
-  }
-
-  error_t releaseSpiResource() {
-    return call SpiResource.release();
-  }
-
-  void signalDone( error_t err ) {
-    atomic m_state = S_STARTED;
-    signal Send.sendDone( m_msg, err );
-  }
-
+  void congestionBackoff();
+  error_t acquireSpiResource();
+  error_t releaseSpiResource();
+  void signalDone( error_t err );
+  
+  task void startLplTimer();
+  
+  /***************** Init Commands *****************/
   command error_t Init.init() {
     call CCA.makeInput();
     call CSN.makeOutput();
@@ -133,7 +144,8 @@ implementation {
     return SUCCESS;
   }
 
-  async command error_t AsyncStdControl.start() {
+  /***************** StdControl Commands ****************/
+  command error_t StdControl.start() {
     atomic {
       call CaptureSFD.captureRisingEdge();
       m_state = S_STARTED;
@@ -143,308 +155,620 @@ implementation {
     return SUCCESS;
   }
 
-  async command error_t AsyncStdControl.stop() {
+  command error_t StdControl.stop() {
     atomic {
       m_state = S_STOPPED;
-      stopBackoffTimer();
+      call BackoffTimer.stop();
       call CaptureSFD.disable();
+      call SpiResource.release();
     }
     return SUCCESS;
   }
 
-  error_t send( message_t* p_msg, bool cca ) {
 
+  /**************** Send Commands ****************/
+  async command error_t Send.send( message_t* p_msg, bool useCca ) {
+    return send( p_msg, useCca );
+  }
+
+  async command error_t Send.resend(bool useCca) {
+    return resend( useCca );
+  }
+
+  async command error_t Send.cancel() {
     atomic {
-      if ( m_state != S_STARTED )
-       return FAIL;
-      m_state = S_LOAD;
-      m_cca = cca;
-      m_msg = p_msg;
+      signalSendDone = FALSE;
+      switch( m_state ) {
+      case S_LOAD:
+        m_state = S_LOAD_CANCEL;
+        break;
+        
+      case S_SAMPLE_CCA:
+        m_state = S_CCA_CANCEL;
+        break;
+        
+      case S_BEGIN_TRANSMIT:
+        m_state = S_TX_CANCEL;
+        break;
+        
+      default:
+        // cancel not allowed while radio is busy transmitting
+        return FAIL;
+      }
     }
 
-    if ( acquireSpiResource() == SUCCESS )
-      loadTXFIFO();
-
     return SUCCESS;
-
   }
 
-  async command error_t Send.sendCCA( message_t* p_msg ) {
-    return send( p_msg, TRUE );
+  async command error_t Send.modify( uint8_t offset, uint8_t* buf, 
+                                     uint8_t len ) {
+    call CSN.clr();
+    call TXFIFO_RAM.write( offset, buf, len );
+    call CSN.set();
+    return SUCCESS;
   }
 
-  async command error_t Send.send( message_t* p_msg ) {
-    return send( p_msg, FALSE );
+  /***************** RadioBackoff Commands ****************/
+  /**
+   * Must be called within a requestInitialBackoff event
+   * @param backoffTime the amount of time in some unspecified units to backoff
+   */
+  async command void RadioBackoff.setInitialBackoff(uint16_t backoffTime) {
+    myInitialBackoff = backoffTime + 1;
   }
-
-  error_t resend( bool cca ) {
-
-    atomic {
-      if ( m_state != S_STARTED )
-       return FAIL;
-      m_cca = cca;
-      m_state = cca ? S_SAMPLE_CCA : S_BEGIN_TRANSMIT;
-    }
-
-    if ( m_cca ) {
-      startBackoffTimer( signal CsmaBackoff.initial( m_msg ) * 
-                              CC2420_BACKOFF_PERIOD );
-    }
-    else if ( acquireSpiResource() == SUCCESS ) {
-      attemptSend();
-    }
-    
-    return SUCCESS;
   
+  /**
+   * Must be called within a requestCongestionBackoff event
+   * @param backoffTime the amount of time in some unspecified units to backoff
+   */
+  async command void RadioBackoff.setCongestionBackoff(uint16_t backoffTime) {
+    myCongestionBackoff = backoffTime + 1;
   }
   
-  async command error_t Send.resendCCA() {
-    return resend( TRUE );
+  /**
+   * Must be called within a requestLplBackoff event
+   * @param backoffTime the amount of time in some unspecified units to backoff
+   */
+  async command void RadioBackoff.setLplBackoff(uint16_t backoffTime) {
+    myLplBackoff = backoffTime + 1;
   }
-
-  async command error_t Send.resend() {
-    return resend( FALSE );
+  
+  async command void RadioBackoff.setCca(bool useCca) {
   }
+  
+  
+  
+  /***************** CC2420Cca Commands ****************/
+  /**
+   * @return TRUE if the CCA pin shows a clear channel
+   */
+  command bool CC2420Cca.isChannelClear() {
+    return call CCA.get();
+  }
+  
 
-  async command error_t Send.cancel() {
-
-    stopBackoffTimer();
-
+  /**
+   * The CaptureSFD event is actually an interrupt from the capture pin
+   * which is connected to timing circuitry and timer modules.  This
+   * type of interrupt allows us to see what time (being some relative value)
+   * the event occurred, and lets us accurately timestamp our packets.  This
+   * allows higher levels in our system to synchronize with other nodes.
+   *
+   * Because the SFD events can occur so quickly, and the interrupts go
+   * in both directions, we set up the interrupt but check the SFD pin to
+   * determine if that interrupt condition has already been met - meaning,
+   * we should fall through and continue executing code where that interrupt
+   * would have picked up and executed had our microcontroller been fast enough.
+   */
+  async event void CaptureSFD.captured( uint16_t time ) {
     atomic {
       switch( m_state ) {
-      case S_LOAD:
-       m_state = S_CANCEL;
-       break;
-      case S_SAMPLE_CCA: case S_BEGIN_TRANSMIT:
-       m_state = S_STARTED;
-       break;
+        
+      case S_SFD:
+        call CaptureSFD.captureFallingEdge();
+        signal TimeStamp.transmittedSFD( time, m_msg );
+        releaseSpiResource();
+        call BackoffTimer.stop();
+        m_state = S_EFD;
+        if ( ( ( (call CC2420Packet.getHeader( m_msg ))->fcf >> IEEE154_FCF_FRAME_TYPE ) & 7 ) == IEEE154_TYPE_DATA ) {
+          (call CC2420Packet.getMetadata( m_msg ))->time = time;
+        }
+        
+        if ( call SFD.get() ) {
+          break;
+        }
+        /** Fall Through because the next interrupt was already received */
+        
+      case S_EFD:
+        call CaptureSFD.captureRisingEdge();
+        if ( (call CC2420Packet.getHeader( m_msg ))->fcf & ( 1 << IEEE154_FCF_ACK_REQ ) ) {
+          m_state = S_ACK_WAIT;
+          call BackoffTimer.start( CC2420_ACK_WAIT_DELAY );
+        } else {
+        
+#ifdef NOACK_LOW_POWER_LISTENING
+          if(continuousModulation) {
+            // Wait for the LplDisableTimer to fire before running signalDone()
+            return;
+          }
+#endif
+          signalDone(SUCCESS);
+        }
+        
+        if ( !call SFD.get() ) {
+          break;
+        }
+        /** Fall Through because the next interrupt was already received */
+        
       default:
-       // cancel not allowed while radio is busy transmitting
-       return FAIL;
+        if ( !m_receiving ) {
+          call CaptureSFD.captureFallingEdge();
+          signal TimeStamp.receivedSFD( time );
+          call CC2420Receive.sfd( time );
+          m_receiving = TRUE;
+          m_prev_time = time;
+          if ( call SFD.get() ) {
+            // wait for the next interrupt before moving on
+            return;
+          }
+        }
+        
+        call CaptureSFD.captureRisingEdge();
+        m_receiving = FALSE;
+        if ( time - m_prev_time < 10 ) {
+          call CC2420Receive.sfd_dropped();
+        }
+        break;
+      
       }
     }
+  }
 
-    return SUCCESS;
+  /***************** CC2420Receive Events ****************/
+  /**
+   * If the packet we just received was an ack that we were expecting,
+   * our send is complete.
+   */
+  async event void CC2420Receive.receive( uint8_t type, message_t* ack_msg ) {
+    cc2420_header_t* ack_header;
+    cc2420_header_t* msg_header;
+    cc2420_metadata_t* msg_metadata;
+    uint8_t* ack_buf;
+    uint8_t length;
 
+    if ( type == IEEE154_TYPE_ACK ) {
+      ack_header = call CC2420Packet.getHeader( ack_msg );
+      msg_header = call CC2420Packet.getHeader( m_msg );
+      msg_metadata = call CC2420Packet.getMetadata( m_msg );
+      ack_buf = (uint8_t *) ack_header;
+      length = ack_header->length;
+      
+      if ( m_state == S_ACK_WAIT &&
+           msg_header->dsn == ack_header->dsn ) {
+        call BackoffTimer.stop();
+        msg_metadata->ack = TRUE;
+        msg_metadata->rssi = ack_buf[ length - 1 ];
+        msg_metadata->lqi = ack_buf[ length ] & 0x7f;
+        signalDone(SUCCESS);
+      }
+    }
   }
 
-  void loadTXFIFO() {
-    cc2420_header_t* header = getHeader( m_msg );
-    uint8_t tx_power = getMetadata( m_msg )->tx_power;
-    if ( !tx_power )
-      tx_power = CC2420_DEF_RFPOWER;
-    call CSN.clr();
-    if ( m_tx_power != tx_power )
-      call TXCTRL.write( ( 2 << CC2420_TXCTRL_TXMIXBUF_CUR ) |
-                        ( 3 << CC2420_TXCTRL_PA_CURRENT ) |
-                        ( 1 << CC2420_TXCTRL_RESERVED ) |
-                        ( tx_power << CC2420_TXCTRL_PA_LEVEL ) );
-    m_tx_power = tx_power;
-    call TXFIFO.write( (uint8_t*)header, header->length - 1 );
+  /***************** SpiResource Events ****************/
+  event void SpiResource.granted() {
+    uint8_t cur_state;
+
+    atomic {
+      cur_state = m_state;
+    }
+
+    switch( cur_state ) {
+    case S_LOAD:
+      loadTXFIFO();
+      break;
+      
+    case S_BEGIN_TRANSMIT:
+      attemptSend();
+      break;
+      
+    case S_LOAD_CANCEL:
+    case S_CCA_CANCEL:
+    case S_TX_CANCEL:
+      call CSN.clr();
+      call SFLUSHTX.strobe();
+      call CSN.set();
+      releaseSpiResource();
+      atomic {
+        if (signalSendDone) {
+          signalDone(ECANCEL);
+        } else {
+          m_state = S_STARTED;
+        }
+      }
+      break;
+      
+    default:
+      releaseSpiResource();
+      break;
+    }
   }
   
+  /***************** TXFIFO Events ****************/
+  /**
+   * The TXFIFO is used to load packets into the transmit buffer on the
+   * chip
+   */
   async event void TXFIFO.writeDone( uint8_t* tx_buf, uint8_t tx_len,
-                                    error_t error ) {
+                                     error_t error ) {
 
     call CSN.set();
-
-    if ( m_state == S_CANCEL ) {
-      m_state = S_STARTED;
-    }
-    else if ( !m_cca ) {
-      m_state = S_BEGIN_TRANSMIT;
+    if ( m_state == S_LOAD_CANCEL ) {
+      atomic {
+        call CSN.clr();
+        call SFLUSHTX.strobe();
+        call CSN.set();
+      }
+      releaseSpiResource();
+      if (signalSendDone) {
+        signalDone(ECANCEL);
+      } else {
+        m_state = S_STARTED;
+      }
+      
+    } else if ( !m_cca ) {
+      atomic {
+        if (m_state == S_LOAD_CANCEL) {
+          m_state = S_TX_CANCEL;
+        } else {
+          m_state = S_BEGIN_TRANSMIT;
+        }
+      }
       attemptSend();
-    }
-    else {
+      
+    else {
       releaseSpiResource();
-      m_state = S_SAMPLE_CCA;
-      startBackoffTimer( signal CsmaBackoff.initial( m_msg ) * 
-                        CC2420_BACKOFF_PERIOD );
+      atomic {
+        if (m_state == S_LOAD_CANCEL) {
+          m_state = S_CCA_CANCEL;
+        } else {
+          m_state = S_SAMPLE_CCA;
+        }
+      }
+      
+      signal RadioBackoff.requestInitialBackoff(m_msg);
+      call BackoffTimer.start(myInitialBackoff);
     }
-
   }
 
-  void congestionBackoff() {
-    atomic {
-      uint16_t time = signal CsmaBackoff.congestion( m_msg );
-      if ( time )
-       startBackoffTimer( time * CC2420_BACKOFF_PERIOD );
-      else
-       m_state = S_STARTED;
-    }
+  
+  async event void TXFIFO.readDone( uint8_t* tx_buf, uint8_t tx_len, 
+      error_t error ) {
   }
-
+  
+  
+  /***************** Timer Events ****************/
+  /**
+   * The backoff timer is mainly used to wait for a moment before trying
+   * to send a packet again. But we also use it to timeout the wait for
+   * an acknowledgement, and timeout the wait for an SFD interrupt when
+   * we should have gotten one.
+   */
   async event void BackoffTimer.fired() {
-
     atomic {
       switch( m_state ) {
-       
-      case S_SAMPLE_CCA :
-       // sample CCA and wait a little longer if free, just in case we
-       // sampled during the ack turn-around window
-       if ( call CCA.get() ) {
-         m_state = S_BEGIN_TRANSMIT;
-         startBackoffTimer( CC2420_TIME_ACK_TURNAROUND );
-       }
-       else {
-         congestionBackoff();
-       }
-       break;
-       
-      case S_BEGIN_TRANSMIT :
-       if ( acquireSpiResource() == SUCCESS )
-         attemptSend();
-       break;
-       
-      case S_ACK_WAIT :
-       signalDone( SUCCESS );
-       break;
-       
-#ifdef PLATFORM_MICAZ
+        
+      case S_SAMPLE_CCA : 
+        // sample CCA and wait a little longer if free, just in case we
+        // sampled during the ack turn-around window
+        if ( call CCA.get() ) {
+          m_state = S_BEGIN_TRANSMIT;
+          call BackoffTimer.start( CC2420_TIME_ACK_TURNAROUND );
+          
+        } else {
+          congestionBackoff();
+        }
+        break;
+        
+      case S_CCA_CANCEL:
+        m_state = S_TX_CANCEL;
+        /** Fall Through */
+        
+      case S_BEGIN_TRANSMIT:
+      case S_TX_CANCEL:
+        if ( acquireSpiResource() == SUCCESS ) {
+          attemptSend();
+        }
+        break;
+        
+      case S_ACK_WAIT:
+        signalDone( SUCCESS );
+        break;
+
       case S_SFD:
-       // We didn't receive an SFD interrupt within CC2420_ABORT_PERIOD
-       // jiffies. Assume something is wrong.
-       call SFLUSHTX.strobe();
-       call CaptureSFD.disable();
-       call CaptureSFD.captureRisingEdge();
-       signalDone( ERETRY );
-       break;
-#endif
+        // We didn't receive an SFD interrupt within CC2420_ABORT_PERIOD
+        // jiffies. Assume something is wrong.
+        call SFLUSHTX.strobe();
+        call CaptureSFD.captureRisingEdge();
+        releaseSpiResource();
+        signalDone( ERETRY );
+        break;
+
       default:
-       break;
+        break;
+      }
+    }
+  }
+  
+  event void LplDisableTimer.fired() {
+    call MDMCTRL1.write(0 << CC2420_MDMCTRL1_TX_MODE);
+    signalDone( SUCCESS );
+  }
+    
+  /***************** Functions ****************/
+  /**
+   * Set up a message to be sent. First load it into the outbound tx buffer
+   * on the chip, then attempt to send it.
+   * @param *p_msg Pointer to the message that needs to be sent
+   * @param cca TRUE if this transmit should use clear channel assessment
+   */
+  error_t send( message_t* p_msg, bool cca ) {
+    atomic {
+      if (m_state == S_LOAD_CANCEL
+          || m_state == S_CCA_CANCEL
+          || m_state == S_TX_CANCEL) {
+        return ECANCEL;
+      }
+      
+      if ( m_state != S_STARTED ) {
+        return FAIL;
       }
+      
+      m_state = S_LOAD;
+      m_cca = cca;
+      m_msg = p_msg;
+      totalCcaChecks = 0;
+    }
+    
+    if ( acquireSpiResource() == SUCCESS ) {
+      loadTXFIFO();
     }
 
+    return SUCCESS;
   }
   
-  void attemptSend() {
+  /**
+   * Resend a packet that already exists in the outbound tx buffer on the
+   * chip
+   * @param cca TRUE if this transmit should use clear channel assessment
+   */
+  error_t resend( bool cca ) {
 
+    atomic {
+      if (m_state == S_LOAD_CANCEL
+          || m_state == S_CCA_CANCEL
+          || m_state == S_TX_CANCEL) {
+        return ECANCEL;
+      }
+      
+      if ( m_state != S_STARTED ) {
+        return FAIL;
+      }
+      
+      m_cca = cca;
+      m_state = cca ? S_SAMPLE_CCA : S_BEGIN_TRANSMIT;
+      totalCcaChecks = 0;
+    }
+    
+    if(m_cca) {
+      if((call CC2420Packet.getMetadata( m_msg ))->rxInterval > 0) {
+        signal RadioBackoff.requestLplBackoff(m_msg);
+        call BackoffTimer.start( myLplBackoff );
+        
+      } else {
+        signal RadioBackoff.requestInitialBackoff(m_msg);
+        call BackoffTimer.start( myInitialBackoff );
+      }
+      
+    } else if ( acquireSpiResource() == SUCCESS ) {
+      attemptSend();
+    }
+    
+    return SUCCESS;
+  }
+  
+  /**
+   * Attempt to send the packet we have loaded into the tx buffer on 
+   * the radio chip.  The STXONCCA will send the packet immediately if
+   * the channel is clear.  If we're not concerned about whether or not
+   * the channel is clear (i.e. m_cca == FALSE), then STXON will send the
+   * packet without checking for a clear channel.
+   *
+   * If the packet didn't get sent, then congestion == TRUE.  In that case,
+   * we reset the backoff timer and try again in a moment.
+   *
+   * If the packet got sent, we should expect an SFD interrupt to take
+   * over, signifying the packet is getting sent.
+   */
+  void attemptSend() {
     uint8_t status;
     bool congestion = TRUE;
+    
+    continuousModulation = FALSE;
+    
+#ifdef NOACK_LOW_POWER_LISTENING
+      // When the message is being sent with LPL, CCA implies this is the first
+      // message.  Send it using the continuous modulation delivery
+      continuousModulation = m_cca 
+          && (call CC2420Packet.getMetadata( m_msg ))->rxInterval > 0;
+#endif
 
-    call CSN.clr();
+    atomic {
+      if (m_state == S_TX_CANCEL) {
+        call SFLUSHTX.strobe();
+        releaseSpiResource();
+        call CSN.set();
+        if (signalSendDone) {
+          signalDone(ECANCEL);
+        } else {
+          m_state = S_STARTED;
+        }
+        return;
+      }
+      
+      
+      call CSN.clr();
+      
+      if(continuousModulation) {
+        /*
+         * We do a complex backoff like this because the last message of a
+         * continuous modulation delivery is currently reloaded into the 
+         * SPI bus and sent off without CCA.  This leaves a small quiet gap
+         * between the continuous modulation and its last message that other
+         * transmitters are guaranteed to hit.
+         *
+         * In the future, when the CRC is built into the continuous delivery
+         * message, this complex backoff may not be needed.  Alternatively,
+         * directly accessing the TXFIFO RAM and adjusting the FCF to 
+         * request an acknowledgement, and then resend without CCA will 
+         * decrease the quiet gap.
+         */
+        if(totalCcaChecks < 20) {
+          if(call CCA.get()) {
+            // Extended backoff
+            totalCcaChecks++;
+          } else {
+            // Saw another transmitter, start over
+            totalCcaChecks = 0;
+          }
+          
+          call CSN.set();
+          releaseSpiResource();
+          signal RadioBackoff.requestInitialBackoff(m_msg);       
+          call BackoffTimer.start( myInitialBackoff );
+          return;
+        }
+        
+        call MDMCTRL1.write(2 << CC2420_MDMCTRL1_TX_MODE);
+      } else {
+        call MDMCTRL1.write(0 << CC2420_MDMCTRL1_TX_MODE);
+      }
 
-    status = m_cca ? call STXONCCA.strobe() : call STXON.strobe();
-    if ( !( status & CC2420_STATUS_TX_ACTIVE ) ) {
-      status = call SNOP.strobe();
-      if ( status & CC2420_STATUS_TX_ACTIVE )
-       congestion = FALSE;
+      status = m_cca ? call STXONCCA.strobe() : call STXON.strobe();
+      if ( !( status & CC2420_STATUS_TX_ACTIVE ) ) {
+        status = call SNOP.strobe();
+        if ( status & CC2420_STATUS_TX_ACTIVE ) {
+          congestion = FALSE;
+          
+          if(continuousModulation) {
+            post startLplTimer();
+          }
+        }
+      }
+      
+      m_state = congestion ? S_SAMPLE_CCA : S_SFD;
+      call CSN.set();
     }
-    atomic m_state = congestion ? S_SAMPLE_CCA : S_SFD;
     
-    call CSN.set();
-
     if ( congestion ) {
+      totalCcaChecks = 0;
       releaseSpiResource();
       congestionBackoff();
+    } else {
+      call BackoffTimer.start(CC2420_ABORT_PERIOD);
     }
-#ifdef PLATFORM_MICAZ
-    else {
-      startBackoffTimer(CC2420_ABORT_PERIOD);
-    }
-#endif
-
   }
-
-  async command error_t Send.modify( uint8_t offset, uint8_t* buf, 
-                                    uint8_t len ) {
-    call CSN.clr();
-    call TXFIFO_RAM.write( offset, buf, len );
-    call CSN.set();
-    return SUCCESS;
-  }
-
-  async event void CaptureSFD.captured( uint16_t time ) {
-
+  
+  
+  /**  
+   * Congestion Backoff
+   */
+  void congestionBackoff() {
     atomic {
-      switch( m_state ) {
-       
-      case S_SFD:
-       call CaptureSFD.captureFallingEdge();
-       signal TimeStamp.transmittedSFD( time, m_msg );
-       releaseSpiResource();
-       stopBackoffTimer();
-       m_state = S_EFD;
-       if ( ( ( getHeader( m_msg )->fcf >> IEEE154_FCF_FRAME_TYPE ) & 7 ) == 
-            IEEE154_TYPE_DATA )
-         getMetadata( m_msg )->time = time;
-       if ( call SFD.get() )
-         break;
-       
-      case S_EFD:
-       call CaptureSFD.captureRisingEdge();
-       if ( getHeader( m_msg )->fcf & ( 1 << IEEE154_FCF_ACK_REQ ) ) {
-         m_state = S_ACK_WAIT;
-         startBackoffTimer( CC2420_ACK_WAIT_DELAY );
-       }
-       else {
-         signalDone(SUCCESS);
-       }
-       if ( !call SFD.get() )
-         break;
-       
-      default:
-       if ( !m_receiving ) {
-         call CaptureSFD.captureFallingEdge();
-         signal TimeStamp.receivedSFD( time );
-         call CC2420Receive.sfd( time );
-         m_receiving = TRUE;
-         m_prev_time = time;
-         if ( call SFD.get() )
-           return;
-       }
-       if ( m_receiving ) {
-         call CaptureSFD.captureRisingEdge();
-         m_receiving = FALSE;
-         if ( time - m_prev_time < 10 )
-           call CC2420Receive.sfd_dropped();
-       }
-       break;
-      
+      if((call CC2420Packet.getMetadata(m_msg))->rxInterval > 0) {
+        signal RadioBackoff.requestLplBackoff(m_msg);
+        call BackoffTimer.start(myLplBackoff);
+        
+      } else {
+        signal RadioBackoff.requestCongestionBackoff(m_msg);
+        call BackoffTimer.start(myCongestionBackoff);
       }
     }
   }
-
-  async event void CC2420Receive.receive( uint8_t type, message_t* ack_msg ) {
-
-    if ( type == IEEE154_TYPE_ACK ) {
-      cc2420_header_t* ack_header = getHeader( ack_msg );
-      cc2420_header_t* msg_header = getHeader( m_msg );
-      cc2420_metadata_t* msg_metadata = getMetadata( m_msg );
-      uint8_t* ack_buf = (uint8_t*)ack_header;
-      uint8_t length = ack_header->length;
-      
-      if ( m_state == S_ACK_WAIT &&
-          msg_header->dsn == ack_header->dsn ) {
-       stopBackoffTimer();
-       msg_metadata->ack = TRUE;
-       msg_metadata->rssi = ack_buf[ length - 1 ];
-       msg_metadata->lqi = ack_buf[ length ] & 0x7f;
-       signalDone(SUCCESS);
-      }
+  
+  error_t acquireSpiResource() {
+    error_t error = call SpiResource.immediateRequest();
+    if ( error != SUCCESS ) {
+      call SpiResource.request();
     }
-
+    return error;
   }
 
-  event void SpiResource.granted() {
+  error_t releaseSpiResource() {
+    call SpiResource.release();
+    return SUCCESS;
+  }
 
-    uint8_t cur_state;
 
-    atomic {
-      cur_state = m_state;
+  /** 
+   * Setup the packet transmission power and load the tx fifo buffer on
+   * the chip with our outbound packet.  
+   *
+   * Warning: the tx_power metadata might not be initialized and
+   * could be a value other than 0 on boot.  Verification is needed here
+   * to make sure the value won't overstep its bounds in the TXCTRL register
+   * and is transmitting at max power by default.
+   *
+   * It should be possible to manually calculate the packet's CRC here and
+   * tack it onto the end of the header + payload when loading into the TXFIFO,
+   * so the continuous modulation low power listening strategy will continually
+   * deliver valid packets.  This would increase receive reliability for
+   * mobile nodes and lossy connections.  The crcByte() function should use
+   * the same CRC polynomial as the CC2420's AUTOCRC functionality.
+   */
+  void loadTXFIFO() {
+    cc2420_header_t* header = call CC2420Packet.getHeader( m_msg );
+    uint8_t tx_power = (call CC2420Packet.getMetadata( m_msg ))->tx_power;
+    
+    if ( !tx_power ) {
+      // If our packet's tx_power wasn't configured to anything but 0,
+      // send it using the default RF power.  This assumes the
+      // packet's metadata is all set to 0's on boot.
+      
+      tx_power = CC2420_DEF_RFPOWER;
     }
-
-    switch( cur_state ) {
-    case S_LOAD: loadTXFIFO(); break;
-    case S_BEGIN_TRANSMIT: attemptSend(); break;
-    default: releaseSpiResource(); break;
+    
+    call CSN.clr();
+    
+    if ( m_tx_power != tx_power ) {
+      call TXCTRL.write( ( 2 << CC2420_TXCTRL_TXMIXBUF_CUR ) |
+                         ( 3 << CC2420_TXCTRL_PA_CURRENT ) |
+                         ( 1 << CC2420_TXCTRL_RESERVED ) |
+                         ( (tx_power & 0x1F) << CC2420_TXCTRL_PA_LEVEL ) );
     }
-
+    
+    m_tx_power = tx_power;
+    
+    call TXFIFO.write( (uint8_t*)header, header->length - 1 );
+  }
+  
+  void signalDone( error_t err ) {
+    atomic m_state = S_STARTED;
+    signal Send.sendDone( m_msg, err );
+  }
+  
+  
+  
+  /***************** Tasks ****************/
+  task void startLplTimer() {
+    call LplDisableTimer.startOneShot((call CC2420Packet.getMetadata( m_msg ))->rxInterval + 10);
   }
 
-  async event void TXFIFO.readDone( uint8_t* tx_buf, uint8_t tx_len, error_t error ) {}
-
-  default async event void TimeStamp.transmittedSFD( uint16_t time, message_t* p_msg ) {}
-  default async event void TimeStamp.receivedSFD( uint16_t time ) {}
+  /***************** Defaults ****************/
+  default async event void TimeStamp.transmittedSFD( uint16_t time, message_t* p_msg ) {
+  }
+  
+  default async event void TimeStamp.receivedSFD( uint16_t time ) {
+  }
 
 }
+