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;
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();
}
async event void CC2420Power.startOscillatorDone()
{
- // default configuration (addresses, etc) has been written
call CC2420Power.rfOff();
+ call CC2420Power.flushRxFifo();
call CC2420Tx.unlockChipSpi();
post startDoneTask();
}
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);
}
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)
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 )
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()
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;
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) {}
}