X-Git-Url: https://oss.titaniummirror.com/gitweb/?p=tinyos-2.x.git;a=blobdiff_plain;f=tos%2Fchips%2Fcc2420_tkn154%2FCC2420TKN154P.nc;h=d6a144b97c14a6e64a372c0b91f82b11efb31ad7;hp=2546253eddd3f02c50205dc94ec298c5aa8fec80;hb=e9bfab607e051bae6afb47b44892ce37541d1b44;hpb=adf1de6c009d13b7b52e68535c63b28f59c97400 diff --git a/tos/chips/cc2420_tkn154/CC2420TKN154P.nc b/tos/chips/cc2420_tkn154/CC2420TKN154P.nc index 2546253e..d6a144b9 100644 --- a/tos/chips/cc2420_tkn154/CC2420TKN154P.nc +++ b/tos/chips/cc2420_tkn154/CC2420TKN154P.nc @@ -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 as RadioPromiscuousMode; interface Timestamp; + interface GetNow as CCA; } uses { interface Notify as PIBUpdate[uint8_t attributeID]; interface LocalTime 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) {} }