X-Git-Url: https://oss.titaniummirror.com/gitweb/?a=blobdiff_plain;f=tos%2Fchips%2Fcc2420_tkn154%2FCC2420TKN154P.nc;h=67c42c2c3f777403dae975e3fdfadd60ecbbaec8;hb=280de4fedba9a7080138600e623cdadf22bf728a;hp=73e0a7d7a1c04bc220948ccb144e4e9236b09240;hpb=cfc8b8448d14883d4d47e0852797366ef0c13a75;p=tinyos-2.x.git diff --git a/tos/chips/cc2420_tkn154/CC2420TKN154P.nc b/tos/chips/cc2420_tkn154/CC2420TKN154P.nc index 73e0a7d7..67c42c2c 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,78 +75,83 @@ 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_WAIT, + S_RX_PENDING, S_RECEIVING, S_OFF_PENDING, - S_LOAD_TXFIFO, - S_TX_LOADED, - S_TX_WAIT, - 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_txframe; - norace error_t m_txError; - norace ieee154_reftime_t m_txReferenceTime; + 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; - norace uint8_t m_numCCA; - ieee154_reftime_t *m_t0Tx; - uint32_t m_dtMax; - uint32_t m_dt; - norace ieee154_csma_t *m_csmaParams; - 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(); + /* function prototypes */ uint8_t dBmToPA_LEVEL(int dbm); void txDoneRxControlStopped(); void rxSpiReserved(); void txSpiReserved(); - void txDoneSpiReserved(); - void finishTx(); - void stopContinue(); + void sendDoneSpiReserved(); void offSpiReserved(); void offStopRxDone(); - uint16_t generateRandomBackoff(uint8_t BE); - void randomDelayUnslottedCsmaCa(); - void randomDelaySlottedCsmaCa(bool resume, uint16_t remainingBackoff); - void sendDone(ieee154_reftime_t *referenceTime, bool ackPendingFlag, error_t error); - + uint16_t getRandomBackoff(uint8_t BE); + void loadTxFrame(ieee154_txframe_t *frame); + void checkEnableRxForACK(); -/* ----------------------- StdControl Operations ----------------------- */ + /* ----------------------- StdControl Operations ----------------------- */ command error_t SplitControl.start() { 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(); } @@ -154,7 +162,6 @@ module CC2420TKN154P async event void CC2420Power.startOscillatorDone() { - // default configuration (addresses, etc) has been written call CC2420Power.rfOff(); call CC2420Power.flushRxFifo(); call CC2420Tx.unlockChipSpi(); @@ -172,7 +179,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); } @@ -182,61 +188,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(); // spin - this should not happen, because the caller has switched radio off - else - stopContinue(); - } - - void stopContinue() - { - call SpiResource.request(); + /* 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 stopReserved() - { - // we own the SPI bus - atomic { - 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); - } - } + /* ----------------------- Helpers / PIB Updates ----------------------- */ - uint16_t generateRandomBackoff(uint8_t BE) + /* Returns a random number [0,(2^BE) - 1] (uniform distr.) */ + /* multiplied by backoff period time (in symbols) */ + uint16_t getRandomBackoff(uint8_t BE) { - // return random number from [0,(2^BE) - 1] (uniform distr.) uint16_t res = call Random.rand16(); uint16_t mask = 0xFFFF; mask <<= BE; mask = ~mask; res &= mask; - return res; + 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) @@ -274,10 +265,10 @@ 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: @@ -291,76 +282,63 @@ 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 busy wait?) - 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(); - call CC2420Power.flushRxFifo(); - m_state = S_RADIO_OFF; - call SpiResource.release(); - signal EnergyDetection.done(SUCCESS, maxEnergy); + m_edStartTime = call LocalTime.get(); + post energyDetectionTask(); } -/* ----------------------- Transceiver Off ----------------------- */ - - task void spinOffTask() + task void energyDetectionTask() { - uint8_t i; - call Leds.led2On(); call Leds.led1On(); - for (i=0; i<65500U; i++) ; - call Leds.led2Off(); call Leds.led1Off(); - for (i=0; i<65500U; i++) ; - call RadioOff.off(); + 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() { atomic { if (m_state == S_RADIO_OFF) return EALREADY; - if (m_state == S_RX_WAIT || m_state == S_TX_WAIT){ - post spinOffTask(); - return SUCCESS; - } 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(); + ASSERT(call RxControl.stop() == SUCCESS); return SUCCESS; } @@ -369,7 +347,7 @@ module CC2420TKN154P if (call SpiResource.immediateRequest() == SUCCESS) offSpiReserved(); else - call SpiResource.request(); // will trigger offSpiReserved() + call SpiResource.request(); /* will continue in offSpiReserved() */ } void offSpiReserved() @@ -388,72 +366,51 @@ 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) { 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){ - m_state = S_RADIO_OFF; - call Leds.led0On(); - return FAIL; - } else { - if (call SpiResource.immediateRequest() == SUCCESS) // will trigger rxSpiReserved() - rxSpiReserved(); - else - call SpiResource.request(); - } + m_t0.regular = t0; + m_dt = dt; + ASSERT(call RxControl.start() == SUCCESS); + if (call SpiResource.immediateRequest() == SUCCESS) + rxSpiReserved(); + else + call SpiResource.request(); /* will continue in rxSpiReserved() */ return SUCCESS; } void rxSpiReserved() { - call CC2420Config.sync(); // put PIB changes into operation - call TxControl.stop(); - call TxControl.start(); // for timestamping (SFD interrupt) - 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) - { + 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; - } - m_state = S_RX_WAIT; - if (t0 != NULL) - call ReliableWait.waitRx(t0, dt); // will signal waitRxDone() in time - else + m_state = S_RX_PENDING; + 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() { atomic { - if (call CC2420Power.rxOn() != SUCCESS) - call Leds.led0On(); m_state = S_RECEIVING; + ASSERT(call CC2420Power.rxOn() == SUCCESS); } call CC2420Tx.lockChipSpi(); - call SpiResource.release(); + call SpiResource.release(); + signal RadioRx.enableRxDone(); } - event message_t* CC2420Rx.received(message_t *frame, 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(frame, timestamp); @@ -462,341 +419,413 @@ module CC2420TKN154P } 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 ) { + 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; - m_txframe = frame; - m_state = S_LOAD_TXFIFO; + m_state = S_LOAD_TXFIFO_NO_CSMA; } - if (call SpiResource.isOwner() || call SpiResource.immediateRequest() == SUCCESS) - txSpiReserved(); - else - call SpiResource.request(); // will trigger txSpiReserved() + m_txframe = frame; + if( t0 == NULL ) + m_dt = 0; + else { + memcpy( &m_t0.native, t0, sizeof(ieee154_timestamp_t) ); + m_dt = dt; + } + loadTxFrame(frame); /* will continue in loadDoneRadioTx() */ return SUCCESS; } - void txSpiReserved() - { - call CC2420Config.sync(); - call TxControl.start(); - if (call CC2420Tx.loadTXFIFO(m_txframe) != SUCCESS) - call Leds.led0On(); - } - - async event void CC2420Tx.loadTXFIFODone(ieee154_txframe_t *data, error_t error) - { - if (m_state != S_LOAD_TXFIFO || error != SUCCESS) - call Leds.led0On(); - m_state = S_TX_LOADED; - signal RadioTx.loadDone(); // we keep owning the SPI - } - - async command ieee154_txframe_t* RadioTx.getLoadedFrame() + void loadDoneRadioTx() { - if (m_state == S_TX_LOADED) - return m_txframe; - else - return NULL; - } - - async command error_t RadioTx.transmit(ieee154_reftime_t *t0, uint32_t dt) - { - // transmit without CCA + /* frame was loaded into TXFIFO */ atomic { - if (m_state != S_TX_LOADED) - return FAIL; - m_numCCA = 0; - m_state = S_TX_WAIT; - if (t0 != NULL) - call ReliableWait.waitTx(t0, dt); // will signal waitTxDone() in time - else + 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 */ } - return SUCCESS; } - void checkEnableRxForACK() + async event void ReliableWait.waitTxDone() { - // the packet is currently being transmitted, check if we need the receive logic ready - bool ackRequest = (m_txframe->header->mhr[MHR_INDEX_FC1] & FC1_ACK_REQUEST) ? TRUE : FALSE; - if (ackRequest){ - // ATTENTION: here the SpiResource is released if ACK is expected - // (so Rx part of the driver can take over) - call SpiResource.release(); - if (call RxControl.start() != SUCCESS) - call Leds.led0On(); + atomic { + ASSERT(m_state == S_TX_PENDING); + m_state = S_TX_ACTIVE_NO_CSMA; + ASSERT(call CC2420Tx.send(FALSE) == SUCCESS); /* transmit without CCA, this must succeed */ + checkEnableRxForACK(); } } - async event void ReliableWait.waitTxDone() + inline void txDoneRadioTx(ieee154_txframe_t *frame, ieee154_timestamp_t *timestamp, error_t result) { - atomic { - m_state = S_TX_ACTIVE; - if (call CC2420Tx.send(FALSE) == SUCCESS) // transmit without CCA, this must succeed - checkEnableRxForACK(); - else - call Leds.led0On(); - } + /* transmission completed */ + signal RadioTx.transmitDone(frame, timestamp, result); } - async command error_t RadioTx.transmitUnslottedCsmaCa(ieee154_csma_t *csmaParams) + /* ----------------------- UnslottedCsmaCa ----------------------- */ + + async command error_t UnslottedCsmaCa.transmit(ieee154_txframe_t *frame, ieee154_csma_t *csma) { - // transmit with single CCA + 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_TX_LOADED) + if( m_state != S_RADIO_OFF ) return FAIL; - m_csmaParams = csmaParams; - m_numCCA = 1; - randomDelayUnslottedCsmaCa(); + m_state = S_LOAD_TXFIFO_UNSLOTTED; } + m_txframe = frame; + m_csma = csma; + loadTxFrame(frame); /* will continue in nextIterationUnslottedCsma() */ return SUCCESS; } - void randomDelayUnslottedCsmaCa() + void nextIterationUnslottedCsma() { - // wait random delay (unslotted CSMA-CA) - uint16_t dtTx = generateRandomBackoff(m_csmaParams->BE) * 20; - call ReferenceTime.getNow(m_t0Tx, 0); - m_state = S_TX_WAIT; - call ReliableWait.waitBackoff(m_t0Tx, dtTx); + /* 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() */ } - void waitBackoffUnslottedCsmaCaDone() + void waitBackoffDoneUnslottedCsma() { + /* backoff finished, try to transmit now */ int8_t dummy; + ieee154_txframe_t *frame = NULL; + ieee154_csma_t *csma = NULL; + atomic { - // CC2420 needs to be in an Rx state for STXONCCA strobe - // note: the receive logic of the CC2420 driver is not yet started, - // i.e. we will not (yet) receive any packets + /* 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; - // wait for CC2420 Rx to calibrate + CCA valid time + m_state = S_TX_ACTIVE_UNSLOTTED_CSMA; + + /* wait for CC2420 Rx to calibrate + CCA valid time */ while (call CC2420Power.rssi(&dummy) != SUCCESS) ; - // call ReliableWait.busyWait(40); - // transmit with single CCA (STXONCCA strobe) - if (call CC2420Tx.send(TRUE) == 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 + /* channel is busy */ call CC2420Power.rfOff(); - call CC2420Power.flushRxFifo(); // we might have (accidentally) caught something during CCA - m_state = S_TX_LOADED; - m_csmaParams->NB += 1; - if (m_csmaParams->NB > m_csmaParams->macMaxCsmaBackoffs){ - // CSMA-CA failure, note: we keep owning the SPI, - // our state is back to S_TX_LOADED, the MAC may try to retransmit - signal RadioTx.transmitUnslottedCsmaCaDone(m_txframe, FALSE, m_csmaParams, FAIL); + /* 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 { - // next iteration of unslotted CSMA-CA - m_csmaParams->BE += 1; - if (m_csmaParams->BE > m_csmaParams->macMaxBE) - m_csmaParams->BE = m_csmaParams->macMaxBE; - randomDelayUnslottedCsmaCa(); + /* 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(); } } } + 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); } - async command error_t RadioTx.transmitSlottedCsmaCa(ieee154_reftime_t *slot0Time, uint32_t dtMax, - bool resume, uint16_t remainingBackoff, ieee154_csma_t *csmaParams) + /* ----------------------- 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) { - // slotted CSMA-CA requires very exact timing (transmission on - // 320 us backoff boundary), even if we have a sufficiently precise and - // accurate clock the CC2420 is not the right radio for - // this task because it is accessed over SPI. The code below relies on - // platform-specific busy-wait functions that must be adjusted - // (through measurements) such that they meet the timing constraints + 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_TX_LOADED) + if( m_state != S_RADIO_OFF ) return FAIL; - m_csmaParams = csmaParams; - m_numCCA = 2; - m_t0Tx = slot0Time; - m_dtMax = dtMax; - randomDelaySlottedCsmaCa(resume, remainingBackoff); + 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; } - void randomDelaySlottedCsmaCa(bool resume, uint16_t remainingBackoff) + void nextIterationSlottedCsma() { - uint16_t dtTx; + uint32_t dtTxTarget; + uint16_t backoff; + ieee154_txframe_t *frame = NULL; + ieee154_csma_t *csma = NULL; + atomic { - dtTx = call TimeCalc.timeElapsed(call ReferenceTime.toLocalTime(m_t0Tx), call LocalTime.get()); - dtTx += (20 - (dtTx % 20)); // round to backoff boundary - if (resume) - dtTx += remainingBackoff; - else - dtTx = dtTx + (generateRandomBackoff(m_csmaParams->BE) * 20); - dtTx += 40; // two backoff periods for the two CCA, the actual tx is scheduled for = m_t0Tx + dtTx - if (dtTx > m_dtMax){ - uint16_t remaining = dtTx - m_dtMax; - if (remaining >= 40) - remaining -= 40; // substract the two CCA (they don't count for the backoff) - else - remaining = 0; - signal RadioTx.transmitSlottedCsmaCaDone(m_txframe, NULL, FALSE, remaining, m_csmaParams, ERETRY); + 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 { - m_state = S_TX_WAIT; - call ReliableWait.waitBackoff(m_t0Tx, dtTx); + /* 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); + } } - void waitBackoffSlottedCsmaCaDone() + void waitBackoffDoneSlottedCsma() { - bool cca; - uint16_t dtTx=0; int8_t dummy; + bool ccaFailure = FALSE; + error_t result = FAIL; + ieee154_txframe_t *frame = NULL; + ieee154_csma_t *csma = NULL; + atomic { - // CC2420 needs to be in an Rx state for STXONCCA strobe - // note: the receive logic of the CC2420 driver is not yet started, - // i.e. we will not (yet) receive any packets + /* 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; - // wait for CC2420 Rx to calibrate + CCA valid time + 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 (or rather 8 symbols after) - call ReliableWait.busyWaitSlotBoundaryCCA(m_t0Tx, &dtTx); // platform-specific implementation - cca = call CC2420Tx.cca(); - if (cca && dtTx <= m_dtMax){ - // Tx in following slot (STXONCCA) - call ReliableWait.busyWaitSlotBoundaryTx(m_t0Tx, dtTx+20); // platform-specific implementation - if (call CC2420Tx.send(TRUE) == 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 - cca = FALSE; - } - // did not transmit the frame + 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_TX_LOADED; - if (dtTx > m_dtMax) - // frame didn't fit into remaining CAP, this can only - // be because we couldn't meet the time-constraints - // (in principle the frame should have fitted) - signal RadioTx.transmitSlottedCsmaCaDone(m_txframe, NULL, FALSE, 0, m_csmaParams, ERETRY); - else { - // CCA failed - m_csmaParams->NB += 1; - if (m_csmaParams->NB > m_csmaParams->macMaxCsmaBackoffs){ - // CSMA-CA failure, note: we keep owning the SPI - signal RadioTx.transmitSlottedCsmaCaDone(m_txframe, NULL, FALSE, 0, m_csmaParams, FAIL); + 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_csmaParams->BE += 1; - if (m_csmaParams->BE > m_csmaParams->macMaxBE) - m_csmaParams->BE = m_csmaParams->macMaxBE; - randomDelaySlottedCsmaCa(FALSE, 0); + /* 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 ReliableWait.waitBackoffDone() + inline void txDoneSlottedCsmaCa(ieee154_txframe_t *frame, ieee154_csma_t *csma, + bool ackPendingFlag, uint16_t remainingBackoff, error_t result) { - if (m_numCCA == 1) - waitBackoffUnslottedCsmaCaDone(); + /* transmission completed */ + signal SlottedCsmaCa.transmitDone(frame, csma, ackPendingFlag, remainingBackoff, result); + } + + /* ----------------------- Common Tx Operations ----------------------- */ + + void loadTxFrame(ieee154_txframe_t *frame) + { + if (call SpiResource.isOwner() || call SpiResource.immediateRequest() == SUCCESS) + txSpiReserved(); else - waitBackoffSlottedCsmaCaDone(); + call SpiResource.request(); /* will continue in txSpiReserved() */ } - async event void CC2420Tx.transmissionStarted( ieee154_txframe_t *frame ) + void txSpiReserved() { - uint8_t frameType = frame->header->mhr[0] & FC1_FRAMETYPE_MASK; - uint8_t token = frame->headerLen; - signal Timestamp.transmissionStarted(frameType, frame->handle, frame->payload, token); + call CC2420Config.sync(); + call TxControl.start(); + ASSERT(call CC2420Tx.loadTXFIFO(m_txframe) == SUCCESS); } - async event void CC2420Tx.transmittedSFD(uint32_t time, ieee154_txframe_t *frame) + async event void CC2420Tx.loadTXFIFODone(ieee154_txframe_t *data, error_t error) { - 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); + 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; + 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(); + ASSERT(call RxControl.start() == 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) { - if (!call SpiResource.isOwner()){ - // this can only happen if an ack was requested and we gave up the SPI - bool wasAckRequested = (frame->header->mhr[MHR_INDEX_FC1] & FC1_ACK_REQUEST) ? TRUE : FALSE; - if (!wasAckRequested) - call Leds.led0On(); // internal error! - memcpy(&m_txReferenceTime, referenceTime, sizeof(ieee154_reftime_t)); - m_ackFramePending = ackPendingFlag; - m_txError = error; - if (call RxControl.stop() != SUCCESS) // will trigger txDoneRxControlStopped() - call Leds.led0On(); + m_timestamp = timestamp; + m_ackFramePending = ackPendingFlag; + 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)); + ASSERT(call RxControl.stop() == SUCCESS); /* will continue in txDoneRxControlStopped() */ } else - sendDone(referenceTime, ackPendingFlag, error); + 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() - } - - void txDoneSpiReserved() - { - sendDone(&m_txReferenceTime, m_ackFramePending, m_txError); + call SpiResource.request(); /* will continue in sendDoneSpiReserved() */ } - void sendDone(ieee154_reftime_t *referenceTime, bool ackPendingFlag, error_t error) + void sendDoneSpiReserved() { - uint8_t numCCA = m_numCCA; - // transmission complete, we're owning the SPI, Rx logic is disabled + /* 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(); - switch (error) + call CC2420Tx.unlockChipSpi(); + call TxControl.stop(); + 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); + } + + async event void CC2420Tx.transmissionStarted( ieee154_txframe_t *frame ) + { + uint8_t frameType = frame->header->mhr[0] & FC1_FRAMETYPE_MASK; + uint8_t token = frame->headerLen; + signal Timestamp.transmissionStarted(frameType, frame->handle, frame->payload, token); + } + + 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 ); + } + + async command void Timestamp.modifyMACPayload( uint8_t token, uint8_t offset, uint8_t* buf, uint8_t len ) + { + 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 ); + } + + async event void ReliableWait.waitBackoffDone() + { + switch (m_state) { - case SUCCESS: - m_state = S_RADIO_OFF; - break; - case ENOACK: - m_state = S_TX_LOADED; - break; - default: - call Leds.led0On(); // internal error! - break; + case S_TX_BACKOFF_SLOTTED: waitBackoffDoneSlottedCsma(); break; + case S_TX_BACKOFF_UNSLOTTED: waitBackoffDoneUnslottedCsma(); break; + default: ASSERT(0); break; } - if (error == SUCCESS){ - call CC2420Tx.unlockChipSpi(); - call TxControl.stop(); - call SpiResource.release(); - m_state = S_RADIO_OFF; - } else - m_state = S_TX_LOADED; - if (numCCA == 0) - signal RadioTx.transmitDone(m_txframe, referenceTime); - else if (numCCA == 1) - signal RadioTx.transmitUnslottedCsmaCaDone(m_txframe, ackPendingFlag, m_csmaParams, error); - else - signal RadioTx.transmitSlottedCsmaCaDone(m_txframe, referenceTime, ackPendingFlag, 0, m_csmaParams, error); } -/* ----------------------- RxControl ----------------------- */ + /* ----------------------- RxControl ----------------------- */ async event void RxControl.stopDone(error_t error) { @@ -805,40 +834,51 @@ module CC2420TKN154P task void rxControlStopDoneTask() { - if (m_stop && m_state != S_STOPPING) - return; switch (m_state) { - case S_OFF_PENDING: offStopRxDone(); break; - case S_TX_ACTIVE: txDoneRxControlStopped(); break; - case S_STOPPING: stopContinue(); 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_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) {} }