From 121da1045cafe46d32ab8ecf87a98fe19430462c Mon Sep 17 00:00:00 2001 From: rincon Date: Thu, 12 Apr 2007 17:11:11 +0000 Subject: [PATCH] Update of the default CC2420 to the low power listening stack. The "acknowledgement" LPL version is the default, while the continuous-modulation "no-acknowledgement" LPL layer is still considered experimental. RSSI can be directly read from the radio through CC2420ConrolC. --- tos/chips/cc2420/CC2420.h | 49 +- tos/chips/cc2420/CC2420AckLpl.h | 102 +++ tos/chips/cc2420/CC2420AckLplC.nc | 98 +++ tos/chips/cc2420/CC2420AckLplP.nc | 509 ++++++++++++++ tos/chips/cc2420/CC2420ActiveMessageC.nc | 61 +- tos/chips/cc2420/CC2420ActiveMessageP.nc | 24 +- tos/chips/cc2420/CC2420Cca.nc | 46 ++ tos/chips/cc2420/CC2420Config.nc | 2 +- tos/chips/cc2420/CC2420ControlC.nc | 22 +- tos/chips/cc2420/CC2420ControlP.nc | 170 +++-- tos/chips/cc2420/CC2420CsmaC.nc | 27 +- tos/chips/cc2420/CC2420CsmaP.nc | 207 ++++-- tos/chips/cc2420/CC2420DutyCycle.nc | 67 ++ tos/chips/cc2420/CC2420DutyCycleC.nc | 88 +++ tos/chips/cc2420/CC2420DutyCycleP.nc | 311 +++++++++ tos/chips/cc2420/CC2420LplDummyC.nc | 66 ++ tos/chips/cc2420/CC2420LplDummyP.nc | 84 +++ tos/chips/cc2420/CC2420NoAckLpl.h | 97 +++ tos/chips/cc2420/CC2420NoAckLplC.nc | 109 +++ tos/chips/cc2420/CC2420NoAckLplP.nc | 552 +++++++++++++++ tos/chips/cc2420/CC2420Packet.nc | 12 +- tos/chips/cc2420/CC2420PacketC.nc | 22 +- tos/chips/cc2420/CC2420ReceiveC.nc | 16 +- tos/chips/cc2420/CC2420ReceiveP.nc | 303 +++++--- tos/chips/cc2420/CC2420SpiC.nc | 10 +- tos/chips/cc2420/CC2420SpiImplP.nc | 116 ++- tos/chips/cc2420/CC2420TinyosNetworkC.nc | 92 +++ tos/chips/cc2420/CC2420TinyosNetworkP.nc | 116 +++ tos/chips/cc2420/CC2420Transmit.nc | 26 +- tos/chips/cc2420/CC2420TransmitC.nc | 42 +- tos/chips/cc2420/CC2420TransmitP.nc | 854 ++++++++++++++++------- tos/chips/cc2420/PacketLink.nc | 72 ++ tos/chips/cc2420/PacketLinkC.nc | 70 ++ tos/chips/cc2420/PacketLinkDummyC.nc | 59 ++ tos/chips/cc2420/PacketLinkDummyP.nc | 92 +++ tos/chips/cc2420/PacketLinkP.nc | 228 ++++++ tos/chips/cc2420/README.txt | 45 ++ tos/chips/cc2420/RadioBackoff.nc | 96 +++ tos/chips/cc2420/UniqueReceiveC.nc | 65 ++ tos/chips/cc2420/UniqueReceiveP.nc | 181 +++++ tos/chips/cc2420/UniqueSendC.nc | 65 ++ tos/chips/cc2420/UniqueSendP.nc | 110 +++ tos/interfaces/LowPowerListening.nc | 130 ++++ 43 files changed, 4886 insertions(+), 627 deletions(-) create mode 100644 tos/chips/cc2420/CC2420AckLpl.h create mode 100644 tos/chips/cc2420/CC2420AckLplC.nc create mode 100644 tos/chips/cc2420/CC2420AckLplP.nc create mode 100644 tos/chips/cc2420/CC2420Cca.nc create mode 100644 tos/chips/cc2420/CC2420DutyCycle.nc create mode 100644 tos/chips/cc2420/CC2420DutyCycleC.nc create mode 100644 tos/chips/cc2420/CC2420DutyCycleP.nc create mode 100644 tos/chips/cc2420/CC2420LplDummyC.nc create mode 100644 tos/chips/cc2420/CC2420LplDummyP.nc create mode 100644 tos/chips/cc2420/CC2420NoAckLpl.h create mode 100644 tos/chips/cc2420/CC2420NoAckLplC.nc create mode 100644 tos/chips/cc2420/CC2420NoAckLplP.nc create mode 100644 tos/chips/cc2420/CC2420TinyosNetworkC.nc create mode 100644 tos/chips/cc2420/CC2420TinyosNetworkP.nc create mode 100644 tos/chips/cc2420/PacketLink.nc create mode 100644 tos/chips/cc2420/PacketLinkC.nc create mode 100644 tos/chips/cc2420/PacketLinkDummyC.nc create mode 100644 tos/chips/cc2420/PacketLinkDummyP.nc create mode 100644 tos/chips/cc2420/PacketLinkP.nc create mode 100644 tos/chips/cc2420/README.txt create mode 100644 tos/chips/cc2420/RadioBackoff.nc create mode 100644 tos/chips/cc2420/UniqueReceiveC.nc create mode 100644 tos/chips/cc2420/UniqueReceiveP.nc create mode 100644 tos/chips/cc2420/UniqueSendC.nc create mode 100644 tos/chips/cc2420/UniqueSendP.nc create mode 100644 tos/interfaces/LowPowerListening.nc diff --git a/tos/chips/cc2420/CC2420.h b/tos/chips/cc2420/CC2420.h index 788b700c..91665102 100644 --- a/tos/chips/cc2420/CC2420.h +++ b/tos/chips/cc2420/CC2420.h @@ -29,16 +29,19 @@ * OF THE POSSIBILITY OF SUCH DAMAGE * * @author Jonathan Hui + * @author David Moss * @version $Revision$ $Date$ */ #ifndef __CC2420_H__ #define __CC2420_H__ -//#include "message.h" - typedef uint8_t cc2420_status_t; +/** + * CC2420 header. An I-frame (interoperability frame) header has an + * extra network byte specified by 6LowPAN + */ typedef nx_struct cc2420_header_t { nxle_uint8_t length; nxle_uint16_t fcf; @@ -46,12 +49,25 @@ typedef nx_struct cc2420_header_t { nxle_uint16_t destpan; nxle_uint16_t dest; nxle_uint16_t src; + + /** I-Frame 6LowPAN interoperability byte */ +#ifdef CC2420_IFRAME_TYPE + nxle_uint8_t network; +#endif + nxle_uint8_t type; } cc2420_header_t; - + +/** + * CC2420 Packet Footer + */ typedef nx_struct cc2420_footer_t { } cc2420_footer_t; +/** + * CC2420 Packet metadata. Contains extra information about the message + * that will not be transmitted + */ typedef nx_struct cc2420_metadata_t { nx_uint8_t tx_power; nx_uint8_t rssi; @@ -59,13 +75,23 @@ typedef nx_struct cc2420_metadata_t { nx_bool crc; nx_bool ack; nx_uint16_t time; + nx_uint16_t rxInterval; + + /** Packet Link Metadata */ +#ifdef PACKET_LINK + nx_uint16_t maxRetries; + nx_uint16_t retryDelay; +#endif + } cc2420_metadata_t; + typedef nx_struct cc2420_packet_t { cc2420_header_t packet; nx_uint8_t data[]; } cc2420_packet_t; + #ifndef TOSH_DATA_LENGTH #define TOSH_DATA_LENGTH 28 #endif @@ -78,6 +104,22 @@ typedef nx_struct cc2420_packet_t { #define CC2420_DEF_RFPOWER 31 #endif +/** + * Ideally, your receive history size should be equal to the number of + * RF neighbors your node will have + */ +#ifndef RECEIVE_HISTORY_SIZE +#define RECEIVE_HISTORY_SIZE 4 +#endif + +/** + * The 6LowPAN ID has yet to be defined for a TinyOS network. + */ +#ifndef TINYOS_6LOWPAN_NETWORK_ID +#define TINYOS_6LOWPAN_NETWORK_ID 0x0 +#endif + + enum { // size of the header not including the length byte MAC_HEADER_SIZE = sizeof( cc2420_header_t ) - 1, @@ -92,6 +134,7 @@ enum cc2420_enums { CC2420_TIME_VREN = 20, // jiffies CC2420_TIME_SYMBOL = 2, // 2 symbols / jiffy CC2420_BACKOFF_PERIOD = ( 20 / CC2420_TIME_SYMBOL ), // symbols + CC2420_MIN_BACKOFF = ( 20 / CC2420_TIME_SYMBOL ), // platform specific? CC2420_ACK_WAIT_DELAY = 128, // jiffies }; diff --git a/tos/chips/cc2420/CC2420AckLpl.h b/tos/chips/cc2420/CC2420AckLpl.h new file mode 100644 index 00000000..0ff05f0e --- /dev/null +++ b/tos/chips/cc2420/CC2420AckLpl.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + + /** + * @author David Moss + */ +#ifndef CC2420ACKLPL_H +#define CC2420ACKLPL_H + +/** + * Low Power Listening Send States + */ +typedef enum { + S_LPL_NOT_SENDING, // DEFAULT + S_LPL_FIRST_MESSAGE, // 1. Sending the first message + S_LPL_SENDING, // 2. Sending all other messages + S_LPL_CLEAN_UP, // 3. Clean up the transmission +} lpl_sendstate_t; + + +/** + * Amount of time, in milliseconds, to keep the radio on after + * a successful receive addressed to this node + * You don't want this too fast, or the off timer can accidentally + * fire due to delays in the system. The radio would shut off and + * possibly need to turn back on again immediately, which can lock up + * the CC2420 if it's in the middle of doing something. + */ +#ifndef DELAY_AFTER_RECEIVE +#define DELAY_AFTER_RECEIVE 100 +#endif + +/** + * This is a measured value of the time in ms the radio is actually on + * We round this up to err on the side of better performance ratios + */ +#ifndef DUTY_ON_TIME +#define DUTY_ON_TIME 5 +#endif + +/** + * The maximum number of CCA checks performed on each wakeup. + * If there are too few, the receiver may wake up between messages + * and not detect the transmitter. + * + * The on-time had to increase from the original version to allow multiple + * transmitters to co-exist. This is due to using ack's, which then requires us + * to extend the backoff period. In networks that transmit frequently, possibly + * with multiple transmitters, this power scheme makes sense. + * + * In networks that transmit very infrequently or without multiple transmitters, + * it makes more sense to go with no acks and no backoffs and make the + * receive check as short as possible. + */ +#ifndef MAX_LPL_CCA_CHECKS + +#if defined(PLATFORM_TELOSB) || defined(PLATFORM_TMOTE) +#define MAX_LPL_CCA_CHECKS 500 +#else +#define MAX_LPL_CCA_CHECKS 400 +#endif + +#endif + +/** + * The minimum number of samples that must be taken in CC2420DutyCycleP + * that show the channel is not clear before a detection event is issued + */ +#ifndef MIN_SAMPLES_BEFORE_DETECT +#define MIN_SAMPLES_BEFORE_DETECT 3 +#endif + +#endif + diff --git a/tos/chips/cc2420/CC2420AckLplC.nc b/tos/chips/cc2420/CC2420AckLplC.nc new file mode 100644 index 00000000..a737bd59 --- /dev/null +++ b/tos/chips/cc2420/CC2420AckLplC.nc @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Low Power Listening for the CC2420 + * @author David Moss + */ + + +#include "CC2420AckLpl.h" +#warning "*** USING ACK LOW POWER LISTENING LAYER" + +configuration CC2420AckLplC { + provides { + interface LowPowerListening; + interface Send; + interface Receive; + interface SplitControl; + interface State as SendState; + } + + uses { + interface Send as SubSend; + interface Receive as SubReceive; + interface SplitControl as SubControl; + } +} + +implementation { + components MainC, + CC2420AckLplP, + CC2420DutyCycleC, + CC2420ActiveMessageC, + CC2420CsmaC, + CC2420TransmitC, + CC2420PacketC, + RandomC, + LedsC, + new StateC() as SendStateC, + new StateC() as RadioStateC, + new TimerMilliC() as OffTimerC, + new TimerMilliC() as SendDoneTimerC; + + LowPowerListening = CC2420AckLplP; + Send = CC2420AckLplP; + Receive = CC2420AckLplP; + SplitControl = CC2420DutyCycleC; + SendState = SendStateC; + + SubControl = CC2420AckLplP.SubControl; + SubReceive = CC2420AckLplP.SubReceive; + SubSend = CC2420AckLplP.SubSend; + + + MainC.SoftwareInit -> CC2420AckLplP; + + CC2420AckLplP.Random -> RandomC; + CC2420AckLplP.SendState -> SendStateC; + CC2420AckLplP.RadioState -> RadioStateC; + CC2420AckLplP.SplitControlState -> CC2420DutyCycleC; + CC2420AckLplP.OffTimer -> OffTimerC; + CC2420AckLplP.SendDoneTimer -> SendDoneTimerC; + CC2420AckLplP.CC2420DutyCycle -> CC2420DutyCycleC; + CC2420AckLplP.Resend -> CC2420TransmitC; + CC2420AckLplP.PacketAcknowledgements -> CC2420ActiveMessageC; + CC2420AckLplP.AMPacket -> CC2420ActiveMessageC; + CC2420AckLplP.CC2420Packet -> CC2420PacketC; + CC2420AckLplP.Leds -> LedsC; + +} diff --git a/tos/chips/cc2420/CC2420AckLplP.nc b/tos/chips/cc2420/CC2420AckLplP.nc new file mode 100644 index 00000000..e35ca475 --- /dev/null +++ b/tos/chips/cc2420/CC2420AckLplP.nc @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Low Power Listening for the CC2420. This component is responsible for + * delivery of an LPL packet, and for turning off the radio when the radio + * has run out of tasks. + * + * The CC2420DutyCycle component is responsible for duty cycling the radio + * and performing receive detections. + * + * @author David Moss + */ + +#include "CC2420AckLpl.h" + +module CC2420AckLplP { + provides { + interface Init; + interface LowPowerListening; + interface Send; + interface Receive; + } + + uses { + interface Send as SubSend; + interface CC2420Transmit as Resend; + interface Receive as SubReceive; + interface AMPacket; + interface SplitControl as SubControl; + interface CC2420DutyCycle; + interface CC2420Packet; + interface PacketAcknowledgements; + interface State as SendState; + interface State as RadioState; + interface State as SplitControlState; + interface Random; + interface Timer as OffTimer; + interface Timer as SendDoneTimer; + interface Leds; + } +} + +implementation { + + /** The message currently being sent */ + message_t *currentSendMsg; + + /** The length of the current send message */ + uint8_t currentSendLen; + + /** TRUE if the radio is duty cycling and not always on */ + bool dutyCycling; + + /** + * Radio State + */ + enum { + S_OFF, + S_ON, + }; + + /** + * Send States + */ + enum { + S_IDLE, + S_SENDING, + }; + + enum { + ONE_MESSAGE = 0, + }; + + /***************** Prototypes ***************/ + task void send(); + task void resend(); + task void startRadio(); + task void stopRadio(); + + void initializeSend(); + void startOffTimer(); + uint16_t getActualDutyCycle(uint16_t dutyCycle); + + /***************** Init Commands ***************/ + command error_t Init.init() { + dutyCycling = FALSE; + return SUCCESS; + } + + /***************** LowPowerListening Commands ***************/ + /** + * Set this this node's radio sleep interval, in milliseconds. + * Once every interval, the node will sleep and perform an Rx check + * on the radio. Setting the sleep interval to 0 will keep the radio + * always on. + * + * This is the equivalent of setting the local duty cycle rate. + * + * @param sleepIntervalMs the length of this node's Rx check interval, in [ms] + */ + command void LowPowerListening.setLocalSleepInterval( + uint16_t sleepIntervalMs) { + call CC2420DutyCycle.setSleepInterval(sleepIntervalMs); + } + + /** + * @return the local node's sleep interval, in [ms] + */ + command uint16_t LowPowerListening.getLocalSleepInterval() { + return call CC2420DutyCycle.getSleepInterval(); + } + + /** + * Set this node's radio duty cycle rate, in units of [percentage*100]. + * For example, to get a 0.05% duty cycle, + * + * call LowPowerListening.setDutyCycle(5); + * + * + * For a 100% duty cycle (always on), + * + * call LowPowerListening.setDutyCycle(10000); + * + * + * This is the equivalent of setting the local sleep interval explicitly. + * + * @param dutyCycle The duty cycle percentage, in units of [percentage*100] + */ + command void LowPowerListening.setLocalDutyCycle(uint16_t dutyCycle) { + call CC2420DutyCycle.setSleepInterval( + call LowPowerListening.dutyCycleToSleepInterval(dutyCycle)); + } + + /** + * @return this node's radio duty cycle rate, in units of [percentage*100] + */ + command uint16_t LowPowerListening.getLocalDutyCycle() { + return call LowPowerListening.sleepIntervalToDutyCycle( + call CC2420DutyCycle.getSleepInterval()); + } + + + /** + * Configure this outgoing message so it can be transmitted to a neighbor mote + * with the specified Rx sleep interval. + * @param msg Pointer to the message that will be sent + * @param sleepInterval The receiving node's sleep interval, in [ms] + */ + command void LowPowerListening.setRxSleepInterval(message_t *msg, + uint16_t sleepIntervalMs) { + (call CC2420Packet.getMetadata(msg))->rxInterval = sleepIntervalMs; + } + + /** + * @return the destination node's sleep interval configured in this message + */ + command uint16_t LowPowerListening.getRxSleepInterval(message_t *msg) { + return (call CC2420Packet.getMetadata(msg))->rxInterval; + } + + /** + * Configure this outgoing message so it can be transmitted to a neighbor mote + * with the specified Rx duty cycle rate. + * Duty cycle is in units of [percentage*100], i.e. 0.25% duty cycle = 25. + * + * @param msg Pointer to the message that will be sent + * @param dutyCycle The duty cycle of the receiving mote, in units of + * [percentage*100] + */ + command void LowPowerListening.setRxDutyCycle(message_t *msg, + uint16_t dutyCycle) { + (call CC2420Packet.getMetadata(msg))->rxInterval = + call LowPowerListening.dutyCycleToSleepInterval(dutyCycle); + } + + + /** + * @return the destination node's duty cycle configured in this message + * in units of [percentage*100] + */ + command uint16_t LowPowerListening.getRxDutyCycle(message_t *msg) { + return call LowPowerListening.sleepIntervalToDutyCycle( + (call CC2420Packet.getMetadata(msg))->rxInterval); + } + + /** + * Convert a duty cycle, in units of [percentage*100], to + * the sleep interval of the mote in milliseconds + * @param dutyCycle The duty cycle in units of [percentage*100] + * @return The equivalent sleep interval, in units of [ms] + */ + command uint16_t LowPowerListening.dutyCycleToSleepInterval( + uint16_t dutyCycle) { + dutyCycle = getActualDutyCycle(dutyCycle); + + if(dutyCycle == 10000) { + return 0; + } + + return (DUTY_ON_TIME * (10000 - dutyCycle)) / dutyCycle; + } + + /** + * Convert a sleep interval, in units of [ms], to a duty cycle + * in units of [percentage*100] + * @param sleepInterval The sleep interval in units of [ms] + * @return The duty cycle in units of [percentage*100] + */ + command uint16_t LowPowerListening.sleepIntervalToDutyCycle( + uint16_t sleepInterval) { + if(sleepInterval == 0) { + return 10000; + } + + return getActualDutyCycle((DUTY_ON_TIME * 10000) + / (sleepInterval + DUTY_ON_TIME)); + } + + + /***************** Send Commands ***************/ + /** + * Each call to this send command gives the message a single + * DSN that does not change for every copy of the message + * sent out. For messages that are not acknowledged, such as + * a broadcast address message, the receiving end does not + * signal receive() more than once for that message. + */ + command error_t Send.send(message_t *msg, uint8_t len) { + if(call SplitControlState.getState() == S_OFF) { + // Everything is off right now, start SplitControl and try again + return EOFF; + } + + if(call SendState.requestState(S_LPL_SENDING) == SUCCESS) { + currentSendMsg = msg; + currentSendLen = len; + + // In case our off timer is running... + call OffTimer.stop(); + call SendDoneTimer.stop(); + + if(call RadioState.getState() == S_ON) { + initializeSend(); + return SUCCESS; + + } else { + post startRadio(); + } + + return SUCCESS; + } + + return FAIL; + } + + command error_t Send.cancel(message_t *msg) { + if(currentSendMsg == msg) { + call SendState.toIdle(); + call SendDoneTimer.stop(); + startOffTimer(); + return call SubSend.cancel(msg); + } + + return FAIL; + } + + + command uint8_t Send.maxPayloadLength() { + return call SubSend.maxPayloadLength(); + } + + command void *Send.getPayload(message_t* msg) { + return call SubSend.getPayload(msg); + } + + /***************** Receive Commands ***************/ + command void *Receive.getPayload(message_t* msg, uint8_t* len) { + return call SubReceive.getPayload(msg, len); + } + + command uint8_t Receive.payloadLength(message_t* msg) { + return call SubReceive.payloadLength(msg); + } + + + /***************** DutyCycle Events ***************/ + /** + * A transmitter was detected. You must now take action to + * turn the radio off when the transaction is complete. + */ + event void CC2420DutyCycle.detected() { + // At this point, the duty cycling has been disabled temporary + // and it will be this component's job to turn the radio back off + // Wait long enough to see if we actually receive a packet, which is + // just a little longer in case there is more than one lpl transmitter on + // the channel. + + if(call SendState.isIdle()) { + startOffTimer(); + } + } + + + /***************** SubControl Events ***************/ + event void SubControl.startDone(error_t error) { + if(!error) { + call RadioState.forceState(S_ON); + + if(call SendState.getState() == S_LPL_FIRST_MESSAGE + || call SendState.getState() == S_LPL_SENDING) { + initializeSend(); + } + } + } + + event void SubControl.stopDone(error_t error) { + if(!error) { + call RadioState.forceState(S_OFF); + + if(call SendState.getState() == S_LPL_FIRST_MESSAGE + || call SendState.getState() == S_LPL_SENDING) { + // We're in the middle of sending a message; start the radio back up + post startRadio(); + + } else { + call OffTimer.stop(); + call SendDoneTimer.stop(); + } + } + } + + /***************** SubSend Events ***************/ + event void SubSend.sendDone(message_t* msg, error_t error) { + + switch(call SendState.getState()) { + case S_LPL_SENDING: + if(call SendDoneTimer.isRunning()) { + if(!call PacketAcknowledgements.wasAcked(msg)) { + post resend(); + return; + } + } + break; + + case S_LPL_CLEAN_UP: + /** + * We include this state so upper layers can't send a different message + * before the last message gets done sending + */ + break; + + default: + break; + } + + call SendState.toIdle(); + call SendDoneTimer.stop(); + startOffTimer(); + signal Send.sendDone(msg, error); + } + + /***************** SubReceive Events ***************/ + /** + * If the received message is new, we signal the receive event and + * start the off timer. If the last message we received had the same + * DSN as this message, then the chances are pretty good + * that this message should be ignored, especially if the destination address + * as the broadcast address + */ + event message_t *SubReceive.receive(message_t* msg, void* payload, + uint8_t len) { + + call CC2420DutyCycle.forceDetected(); + startOffTimer(); + return signal Receive.receive(msg, payload, len); + } + + /***************** Timer Events ****************/ + event void OffTimer.fired() { + /* + * Only stop the radio if the radio is supposed to be off permanently + * or if the duty cycle is on and our sleep interval is not 0 + */ + if(call SplitControlState.getState() == S_OFF + || (call CC2420DutyCycle.getSleepInterval() > 0 + && call SplitControlState.getState() == S_ON + && call SendState.getState() == S_LPL_NOT_SENDING)) { + post stopRadio(); + } + } + + /** + * When this timer is running, that means we're sending repeating messages + * to a node that is receive check duty cycling. + */ + event void SendDoneTimer.fired() { + if(call SendState.getState() == S_LPL_SENDING) { + // The next time SubSend.sendDone is signaled, send is complete. + call SendState.forceState(S_LPL_CLEAN_UP); + } + } + + /***************** Resend Events ****************/ + /** + * Signal that a message has been sent + * + * @param p_msg message to send. + * @param error notifaction of how the operation went. + */ + async event void Resend.sendDone( message_t* p_msg, error_t error ) { + // This is actually caught by SubSend.sendDone + } + + + /***************** Tasks ***************/ + task void send() { + if(call SubSend.send(currentSendMsg, currentSendLen) != SUCCESS) { + post send(); + } + } + + task void resend() { + if(call Resend.resend(TRUE) != SUCCESS) { + post resend(); + } + } + + task void startRadio() { + if(call SubControl.start() != SUCCESS) { + post startRadio(); + } + } + + task void stopRadio() { + if(call SendState.getState() == S_LPL_NOT_SENDING) { + if(call SubControl.stop() != SUCCESS) { + post stopRadio(); + } + } + } + + /***************** Functions ***************/ + void initializeSend() { + if(call LowPowerListening.getRxSleepInterval(currentSendMsg) + > ONE_MESSAGE) { + + if(call AMPacket.destination(currentSendMsg) == AM_BROADCAST_ADDR) { + call PacketAcknowledgements.noAck(currentSendMsg); + } else { + // Send it repetitively within our transmit window + call PacketAcknowledgements.requestAck(currentSendMsg); + } + + call SendDoneTimer.startOneShot( + call LowPowerListening.getRxSleepInterval(currentSendMsg) + 20); + } + + post send(); + } + + + void startOffTimer() { + call OffTimer.startOneShot(DELAY_AFTER_RECEIVE); + } + + /** + * Check the bounds on a given duty cycle + * We're never over 100%, and we're never at 0% + */ + uint16_t getActualDutyCycle(uint16_t dutyCycle) { + if(dutyCycle > 10000) { + return 10000; + } else if(dutyCycle == 0) { + return 1; + } + + return dutyCycle; + } +} + diff --git a/tos/chips/cc2420/CC2420ActiveMessageC.nc b/tos/chips/cc2420/CC2420ActiveMessageC.nc index ef78e86e..31e90766 100644 --- a/tos/chips/cc2420/CC2420ActiveMessageC.nc +++ b/tos/chips/cc2420/CC2420ActiveMessageC.nc @@ -24,12 +24,13 @@ /** * The Active Message layer for the CC2420 radio. This configuration * just layers the AM dispatch (CC2420ActiveMessageM) on top of the - * underlying CC2420 radio packet (CC2420CsmaRadioC), which is + * underlying CC2420 radio packet (CC2420CsmaCsmaCC), which is * inherently an AM packet (acknowledgements based on AM destination * addr and group). Note that snooping may not work, due to CC2420 * early packet rejection if acknowledgements are enabled. * * @author Philip Levis + * @author David Moss * @version $Revision$ $Date$ */ @@ -45,30 +46,66 @@ configuration CC2420ActiveMessageC { interface Packet; interface CC2420Packet; interface PacketAcknowledgements; + interface RadioBackoff[am_id_t amId]; + interface LowPowerListening; + interface PacketLink; } } implementation { components CC2420ActiveMessageP as AM; - components CC2420CsmaC as Radio; + components CC2420CsmaC as CsmaC; components ActiveMessageAddressC as Address; + components UniqueSendC; + components UniqueReceiveC; + components CC2420TinyosNetworkC; + components CC2420PacketC; - SplitControl = Radio; - Packet = AM; +#if defined(LOW_POWER_LISTENING) || defined(ACK_LOW_POWER_LISTENING) + components CC2420AckLplC as LplC; +#elif defined(NOACK_LOW_POWER_LISTENING) + components CC2420NoAckLplC as LplC; +#else + components CC2420LplDummyC as LplC; +#endif +#if defined(PACKET_LINK) + components PacketLinkC as LinkC; +#else + components PacketLinkDummyC as LinkC; +#endif + + + RadioBackoff = CsmaC; + Packet = AM; AMSend = AM; Receive = AM.Receive; Snoop = AM.Snoop; AMPacket = AM; - - AM.SubSend -> Radio.Send; - AM.SubReceive -> Radio.Receive; - AM.amAddress -> Address; - Radio.AMPacket -> AM; - - components CC2420PacketC; + PacketLink = LinkC; + LowPowerListening = LplC; CC2420Packet = CC2420PacketC; PacketAcknowledgements = CC2420PacketC; + + + // SplitControl Layers + SplitControl = LplC; + LplC.SubControl -> CsmaC; + + // Send Layers + AM.SubSend -> UniqueSendC; + UniqueSendC.SubSend -> LinkC; + LinkC.SubSend -> LplC.Send; + LplC.SubSend -> CC2420TinyosNetworkC.Send; + CC2420TinyosNetworkC.SubSend -> CsmaC; + + // Receive Layers + AM.SubReceive -> LplC; + LplC.SubReceive -> UniqueReceiveC.Receive; + UniqueReceiveC.SubReceive -> CC2420TinyosNetworkC.Receive; + CC2420TinyosNetworkC.SubReceive -> CsmaC; - + AM.amAddress -> Address; + AM.CC2420Packet -> CC2420PacketC; + } diff --git a/tos/chips/cc2420/CC2420ActiveMessageP.nc b/tos/chips/cc2420/CC2420ActiveMessageP.nc index 6f6694fa..45a245f4 100644 --- a/tos/chips/cc2420/CC2420ActiveMessageP.nc +++ b/tos/chips/cc2420/CC2420ActiveMessageP.nc @@ -31,6 +31,8 @@ * @author Philip Levis * @version $Revision$ $Date$ */ + +#include "CC2420.h" module CC2420ActiveMessageP { provides { @@ -43,6 +45,7 @@ module CC2420ActiveMessageP { uses { interface Send as SubSend; interface Receive as SubReceive; + interface CC2420Packet; command am_addr_t amAddress(); } } @@ -52,14 +55,11 @@ implementation { CC2420_SIZE = MAC_HEADER_SIZE + MAC_FOOTER_SIZE, }; - cc2420_header_t* getHeader( message_t* msg ) { - return (cc2420_header_t*)( msg->data - sizeof(cc2420_header_t) ); - } command error_t AMSend.send[am_id_t id](am_addr_t addr, message_t* msg, uint8_t len) { - cc2420_header_t* header = getHeader( msg ); + cc2420_header_t* header = call CC2420Packet.getHeader( msg ); header->type = id; header->dest = addr; header->destpan = TOS_AM_GROUP; @@ -115,22 +115,22 @@ implementation { } command am_addr_t AMPacket.destination(message_t* amsg) { - cc2420_header_t* header = getHeader(amsg); + cc2420_header_t* header = call CC2420Packet.getHeader(amsg); return header->dest; } command am_addr_t AMPacket.source(message_t* amsg) { - cc2420_header_t* header = getHeader(amsg); + cc2420_header_t* header = call CC2420Packet.getHeader(amsg); return header->src; } command void AMPacket.setDestination(message_t* amsg, am_addr_t addr) { - cc2420_header_t* header = getHeader(amsg); + cc2420_header_t* header = call CC2420Packet.getHeader(amsg); header->dest = addr; } command void AMPacket.setSource(message_t* amsg, am_addr_t addr) { - cc2420_header_t* header = getHeader(amsg); + cc2420_header_t* header = call CC2420Packet.getHeader(amsg); header->src = addr; } @@ -140,12 +140,12 @@ implementation { } command am_id_t AMPacket.type(message_t* amsg) { - cc2420_header_t* header = getHeader(amsg); + cc2420_header_t* header = call CC2420Packet.getHeader(amsg); return header->type; } command void AMPacket.setType(message_t* amsg, am_id_t type) { - cc2420_header_t* header = getHeader(amsg); + cc2420_header_t* header = call CC2420Packet.getHeader(amsg); header->type = type; } @@ -165,12 +165,12 @@ implementation { command void Packet.clear(message_t* msg) {} command uint8_t Packet.payloadLength(message_t* msg) { - return getHeader(msg)->length - CC2420_SIZE; + return (call CC2420Packet.getHeader(msg))->length - CC2420_SIZE; } command void Packet.setPayloadLength(message_t* msg, uint8_t len) { - getHeader(msg)->length = len + CC2420_SIZE; + (call CC2420Packet.getHeader(msg))->length = len + CC2420_SIZE; } command uint8_t Packet.maxPayloadLength() { diff --git a/tos/chips/cc2420/CC2420Cca.nc b/tos/chips/cc2420/CC2420Cca.nc new file mode 100644 index 00000000..059c9fef --- /dev/null +++ b/tos/chips/cc2420/CC2420Cca.nc @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Interface to obtain a CCA reading from the CC2420 radio + * to determine if a neighbor is transmitting + * @author David Moss + */ + +interface CC2420Cca { + + /** + * @return TRUE if the CCA pin shows a clear channel + */ + command bool isChannelClear(); + +} + diff --git a/tos/chips/cc2420/CC2420Config.nc b/tos/chips/cc2420/CC2420Config.nc index 2d935888..90308c38 100644 --- a/tos/chips/cc2420/CC2420Config.nc +++ b/tos/chips/cc2420/CC2420Config.nc @@ -53,7 +53,7 @@ interface CC2420Config { event void syncDone( error_t error ); /** - * Change the channel of the radio. + * Change the channel of the radio, between 11 and 26 */ command uint8_t getChannel(); command void setChannel( uint8_t channel ); diff --git a/tos/chips/cc2420/CC2420ControlC.nc b/tos/chips/cc2420/CC2420ControlC.nc index 04de72ef..1657bcf9 100644 --- a/tos/chips/cc2420/CC2420ControlC.nc +++ b/tos/chips/cc2420/CC2420ControlC.nc @@ -41,24 +41,27 @@ configuration CC2420ControlC { - provides interface Init; provides interface Resource; provides interface CC2420Config; provides interface CC2420Power; - - uses interface AMPacket; - + provides interface Read as ReadRssi; + } implementation { components CC2420ControlP; - Init = CC2420ControlP; Resource = CC2420ControlP; CC2420Config = CC2420ControlP; CC2420Power = CC2420ControlP; - AMPacket = CC2420ControlP; + ReadRssi = CC2420ControlP; + components MainC; + MainC.SoftwareInit -> CC2420ControlP; + + components CC2420ActiveMessageC; + CC2420ControlP.AMPacket -> CC2420ActiveMessageC; + components AlarmMultiplexC as Alarm; CC2420ControlP.StartupTimer -> Alarm; @@ -81,14 +84,15 @@ implementation { CC2420ControlP.IOCFG1 -> Spi.IOCFG1; CC2420ControlP.MDMCTRL0 -> Spi.MDMCTRL0; CC2420ControlP.MDMCTRL1 -> Spi.MDMCTRL1; - CC2420ControlP.RXCTRL1 -> Spi.RXCTRL1; CC2420ControlP.PANID -> Spi.PANID; + CC2420ControlP.RXCTRL1 -> Spi.RXCTRL1; + CC2420ControlP.RSSI -> Spi.RSSI; components new CC2420SpiC() as SyncSpiC; CC2420ControlP.SyncResource -> SyncSpiC; - components LedsC as Leds; - CC2420ControlP.Leds -> Leds; + components new CC2420SpiC() as RssiResource; + CC2420ControlP.RssiResource -> RssiResource; } diff --git a/tos/chips/cc2420/CC2420ControlP.nc b/tos/chips/cc2420/CC2420ControlP.nc index 333d3ca7..1dae5a2d 100644 --- a/tos/chips/cc2420/CC2420ControlP.nc +++ b/tos/chips/cc2420/CC2420ControlP.nc @@ -31,6 +31,7 @@ /** * @author Jonathan Hui + * @author Urs Hunkeler (ReadRssi implementation) * @version $Revision$ $Date$ */ @@ -42,6 +43,7 @@ module CC2420ControlP { provides interface Resource; provides interface CC2420Config; provides interface CC2420Power; + provides interface Read as ReadRssi; uses interface Alarm as StartupTimer; uses interface GeneralIO as CSN; @@ -49,7 +51,6 @@ module CC2420ControlP { uses interface GeneralIO as VREN; uses interface GpioInterrupt as InterruptCCA; - uses interface Resource as SpiResource; uses interface CC2420Ram as PANID; uses interface CC2420Register as FSCTRL; uses interface CC2420Register as IOCFG0; @@ -57,12 +58,15 @@ module CC2420ControlP { uses interface CC2420Register as MDMCTRL0; uses interface CC2420Register as MDMCTRL1; uses interface CC2420Register as RXCTRL1; + uses interface CC2420Register as RSSI; uses interface CC2420Strobe as SRXON; uses interface CC2420Strobe as SRFOFF; uses interface CC2420Strobe as SXOSCOFF; uses interface CC2420Strobe as SXOSCON; uses interface AMPacket; - + + uses interface Resource as SpiResource; + uses interface Resource as RssiResource; uses interface Resource as SyncResource; uses interface Leds; @@ -80,14 +84,21 @@ implementation { } cc2420_control_state_t; uint8_t m_channel = CC2420_DEF_CHANNEL; + uint8_t m_tx_power = CC2420_DEF_RFPOWER; + uint16_t m_pan = TOS_AM_GROUP; + uint16_t m_short_addr; + bool m_sync_busy; - task void syncDone_task(); - + norace cc2420_control_state_t m_state = S_VREG_STOPPED; + + /***************** Prototypes ****************/ + task void syncDone_task(); + /***************** Init Commands ****************/ command error_t Init.init() { call CSN.makeOutput(); call RSTN.makeOutput(); @@ -96,10 +107,12 @@ implementation { return SUCCESS; } + /***************** Resource Commands ****************/ async command error_t Resource.immediateRequest() { error_t error = call SpiResource.immediateRequest(); - if ( error == SUCCESS ) + if ( error == SUCCESS ) { call CSN.clr(); + } return error; } @@ -118,15 +131,12 @@ implementation { } } - event void SpiResource.granted() { - call CSN.clr(); - signal Resource.granted(); - } - + /***************** CC2420Power Commands ****************/ async command error_t CC2420Power.startVReg() { atomic { - if ( m_state != S_VREG_STOPPED ) - return FAIL; + if ( m_state != S_VREG_STOPPED ) { + return FAIL; + } m_state = S_VREG_STARTING; } call VREN.set(); @@ -134,15 +144,6 @@ implementation { return SUCCESS; } - async event void StartupTimer.fired() { - if ( m_state == S_VREG_STARTING ) { - m_state = S_VREG_STARTED; - call RSTN.clr(); - call RSTN.set(); - signal CC2420Power.startVRegDone(); - } - } - async command error_t CC2420Power.stopVReg() { m_state = S_VREG_STOPPED; call RSTN.clr(); @@ -153,26 +154,32 @@ implementation { async command error_t CC2420Power.startOscillator() { atomic { - if ( m_state != S_VREG_STARTED ) - return FAIL; - + if ( m_state != S_VREG_STARTED ) { + return FAIL; + } + m_state = S_XOSC_STARTING; call IOCFG1.write( CC2420_SFDMUX_XOSC16M_STABLE << - CC2420_IOCFG1_CCAMUX ); + CC2420_IOCFG1_CCAMUX ); + call InterruptCCA.enableRisingEdge(); call SXOSCON.strobe(); + call IOCFG0.write( ( 1 << CC2420_IOCFG0_FIFOP_POLARITY ) | - ( 127 << CC2420_IOCFG0_FIFOP_THR ) ); + ( 127 << CC2420_IOCFG0_FIFOP_THR ) ); + call FSCTRL.write( ( 1 << CC2420_FSCTRL_LOCK_THR ) | - ( ( (m_channel - 11)*5+357 ) - << CC2420_FSCTRL_FREQ ) ); + ( ( (m_channel - 11)*5+357 ) + << CC2420_FSCTRL_FREQ ) ); + call MDMCTRL0.write( ( 1 << CC2420_MDMCTRL0_RESERVED_FRAME_MODE ) | - ( 1 << CC2420_MDMCTRL0_ADR_DECODE ) | - ( 2 << CC2420_MDMCTRL0_CCA_HYST ) | - ( 3 << CC2420_MDMCTRL0_CCA_MOD ) | - ( 1 << CC2420_MDMCTRL0_AUTOCRC ) | - ( 1 << CC2420_MDMCTRL0_AUTOACK ) | - ( 2 << CC2420_MDMCTRL0_PREAMBLE_LENGTH ) ); + ( 1 << CC2420_MDMCTRL0_ADR_DECODE ) | + ( 2 << CC2420_MDMCTRL0_CCA_HYST ) | + ( 3 << CC2420_MDMCTRL0_CCA_MOD ) | + ( 1 << CC2420_MDMCTRL0_AUTOCRC ) | + ( 0 << CC2420_MDMCTRL0_AUTOACK ) | // we now SACK + ( 2 << CC2420_MDMCTRL0_PREAMBLE_LENGTH ) ); + call RXCTRL1.write( ( 1 << CC2420_RXCTRL1_RXBPF_LOCUR ) | ( 1 << CC2420_RXCTRL1_LOW_LOWGAIN ) | ( 1 << CC2420_RXCTRL1_HIGH_HGM ) | @@ -184,23 +191,12 @@ implementation { return SUCCESS; } - async event void InterruptCCA.fired() { - nxle_uint16_t id[ 2 ]; - m_state = S_XOSC_STARTED; - id[ 0 ] = m_pan; - id[ 1 ] = m_short_addr; - call InterruptCCA.disable(); - call IOCFG1.write( 0 ); - call PANID.write( 0, (uint8_t*)&id, 4 ); - call CSN.set(); - call CSN.clr(); - signal CC2420Power.startOscillatorDone(); - } async command error_t CC2420Power.stopOscillator() { atomic { - if ( m_state != S_XOSC_STARTED ) - return FAIL; + if ( m_state != S_XOSC_STARTED ) { + return FAIL; + } m_state = S_VREG_STARTED; call SXOSCOFF.strobe(); } @@ -209,8 +205,9 @@ implementation { async command error_t CC2420Power.rxOn() { atomic { - if ( m_state != S_XOSC_STARTED ) - return FAIL; + if ( m_state != S_XOSC_STARTED ) { + return FAIL; + } call SRXON.strobe(); } return SUCCESS; @@ -218,13 +215,16 @@ implementation { async command error_t CC2420Power.rfOff() { atomic { - if ( m_state != S_XOSC_STARTED ) - return FAIL; + if ( m_state != S_XOSC_STARTED ) { + return FAIL; + } call SRFOFF.strobe(); } return SUCCESS; } + + /***************** CC2420Config Commands ****************/ command uint8_t CC2420Config.getChannel() { atomic return m_channel; } @@ -251,17 +251,26 @@ implementation { command error_t CC2420Config.sync() { atomic { - if ( m_sync_busy ) + if ( m_sync_busy ) { return FAIL; + } + m_sync_busy = TRUE; - if ( m_state == S_XOSC_STARTED ) + if ( m_state == S_XOSC_STARTED ) { call SyncResource.request(); - else + } else { post syncDone_task(); + } } return SUCCESS; } + /***************** ReadRssi Commands ****************/ + command error_t ReadRssi.read() { + return call RssiResource.request(); + } + + /***************** Spi Resources Events ****************/ event void SyncResource.granted() { nxle_uint16_t id[ 2 ]; @@ -276,7 +285,7 @@ implementation { call CSN.clr(); call SRFOFF.strobe(); call FSCTRL.write( ( 1 << CC2420_FSCTRL_LOCK_THR ) | - ( ( (channel - 11)*5+357 ) << CC2420_FSCTRL_FREQ ) ); + ( ( (channel - 11)*5+357 ) << CC2420_FSCTRL_FREQ ) ); call PANID.write( 0, (uint8_t*)id, sizeof( id ) ); call CSN.set(); call CSN.clr(); @@ -288,11 +297,58 @@ implementation { } + event void SpiResource.granted() { + call CSN.clr(); + signal Resource.granted(); + } + + event void RssiResource.granted() { + uint16_t data; + call CSN.clr(); + call RSSI.read(&data); + call CSN.set(); + + call RssiResource.release(); + data += 0x7f; + data &= 0x00ff; + signal ReadRssi.readDone(SUCCESS, data); + } + + /***************** StartupTimer Events ****************/ + async event void StartupTimer.fired() { + if ( m_state == S_VREG_STARTING ) { + m_state = S_VREG_STARTED; + call RSTN.clr(); + call RSTN.set(); + signal CC2420Power.startVRegDone(); + } + } + + /***************** InterruptCCA Events ****************/ + async event void InterruptCCA.fired() { + nxle_uint16_t id[ 2 ]; + m_state = S_XOSC_STARTED; + id[ 0 ] = m_pan; + id[ 1 ] = m_short_addr; + call InterruptCCA.disable(); + call IOCFG1.write( 0 ); + call PANID.write( 0, (uint8_t*)&id, 4 ); + call CSN.set(); + call CSN.clr(); + signal CC2420Power.startOscillatorDone(); + } + + /***************** Tasks ****************/ task void syncDone_task() { atomic m_sync_busy = FALSE; signal CC2420Config.syncDone( SUCCESS ); } - default event void CC2420Config.syncDone( error_t error ) {} + /***************** Defaults ****************/ + default event void CC2420Config.syncDone( error_t error ) { + } + default event void ReadRssi.readDone(error_t error, uint16_t data) { + } + } diff --git a/tos/chips/cc2420/CC2420CsmaC.nc b/tos/chips/cc2420/CC2420CsmaC.nc index 34620ba8..5f052a5c 100644 --- a/tos/chips/cc2420/CC2420CsmaC.nc +++ b/tos/chips/cc2420/CC2420CsmaC.nc @@ -42,46 +42,45 @@ configuration CC2420CsmaC { provides interface SplitControl; - provides interface Send; provides interface Receive; - - uses interface AMPacket; + provides interface RadioBackoff[am_id_t amId]; } implementation { components CC2420CsmaP as CsmaP; - + RadioBackoff = CsmaP; SplitControl = CsmaP; Send = CsmaP; - AMPacket = CsmaP; + components MainC; + MainC.SoftwareInit -> CsmaP; + + components CC2420ActiveMessageC; + CsmaP.AMPacket -> CC2420ActiveMessageC; + components CC2420ControlC; - AMPacket = CC2420ControlC; CsmaP.Resource -> CC2420ControlC; CsmaP.CC2420Power -> CC2420ControlC; components CC2420TransmitC; - CsmaP.SubControl -> CC2420TransmitC; CsmaP.CC2420Transmit -> CC2420TransmitC; - CsmaP.CsmaBackoff -> CC2420TransmitC; + CsmaP.SubBackoff -> CC2420TransmitC; components CC2420ReceiveC; Receive = CC2420ReceiveC; CsmaP.SubControl -> CC2420ReceiveC; + components CC2420PacketC; + CsmaP.CC2420Packet -> CC2420PacketC; + components RandomC; CsmaP.Random -> RandomC; components LedsC as Leds; CsmaP.Leds -> Leds; - - components MainC; - MainC.SoftwareInit -> CsmaP; - MainC.SoftwareInit -> CC2420ControlC; - MainC.SoftwareInit -> CC2420TransmitC; - MainC.SoftwareInit -> CC2420ReceiveC; + } diff --git a/tos/chips/cc2420/CC2420CsmaP.nc b/tos/chips/cc2420/CC2420CsmaP.nc index ee9e245d..f0382f5a 100644 --- a/tos/chips/cc2420/CC2420CsmaP.nc +++ b/tos/chips/cc2420/CC2420CsmaP.nc @@ -39,15 +39,17 @@ module CC2420CsmaP { provides interface Init; provides interface SplitControl; provides interface Send; + provides interface RadioBackoff[am_id_t amId]; uses interface Resource; uses interface CC2420Power; - uses interface AsyncStdControl as SubControl; + uses interface StdControl as SubControl; uses interface CC2420Transmit; - uses interface CsmaBackoff; + uses interface RadioBackoff as SubBackoff; uses interface Random; uses interface AMPacket; uses interface Leds; + uses interface CC2420Packet; } @@ -63,102 +65,67 @@ implementation { }; message_t* m_msg; + uint8_t m_state = S_PREINIT; - uint8_t m_dsn; + error_t sendErr = SUCCESS; + /** TRUE if we are to use CCA when sending the current packet */ + norace bool ccaOn; + + /****************** Prototypes ****************/ + task void startDone_task(); task void startDone_task(); task void stopDone_task(); task void sendDone_task(); - cc2420_header_t* getHeader( message_t* msg ) { - return (cc2420_header_t*)( msg->data - sizeof( cc2420_header_t ) ); - } - - cc2420_metadata_t* getMetadata( message_t* msg ) { - return (cc2420_metadata_t*)msg->metadata; - } - + /***************** Init Commands ****************/ command error_t Init.init() { - - if ( m_state != S_PREINIT ) + if ( m_state != S_PREINIT ) { return FAIL; - + } m_state = S_STOPPED; - return SUCCESS; - } + /***************** SplitControl Commands ****************/ command error_t SplitControl.start() { - - if ( m_state != S_STOPPED ) + if ( m_state != S_STOPPED ) { return FAIL; + } m_state = S_STARTING; - - m_dsn = call Random.rand16(); call CC2420Power.startVReg(); - return SUCCESS; - - } - - async event void CC2420Power.startVRegDone() { - call Resource.request(); - } - - event void Resource.granted() { - call CC2420Power.startOscillator(); - } - - async event void CC2420Power.startOscillatorDone() { - call SubControl.start(); - call CC2420Power.rxOn(); - call Resource.release(); - post startDone_task(); - } - - task void startDone_task() { - m_state = S_STARTED; - signal SplitControl.startDone( SUCCESS ); } command error_t SplitControl.stop() { - - if ( m_state != S_STARTED ) + if ( m_state != S_STARTED ) { return FAIL; + } m_state = S_STOPPING; - call SubControl.stop(); call CC2420Power.stopVReg(); post stopDone_task(); - return SUCCESS; - - } - - task void stopDone_task() { - m_state = S_STOPPED; - signal SplitControl.stopDone( SUCCESS ); } + /***************** Send Commands ****************/ command error_t Send.cancel( message_t* p_msg ) { - return FAIL; + return call CC2420Transmit.cancel(); } command error_t Send.send( message_t* p_msg, uint8_t len ) { - cc2420_header_t* header = getHeader( p_msg ); - cc2420_metadata_t* metadata = getMetadata( p_msg ); + cc2420_header_t* header = call CC2420Packet.getHeader( p_msg ); + cc2420_metadata_t* metadata = call CC2420Packet.getMetadata( p_msg ); atomic { if ( m_state != S_STARTED ) return FAIL; m_state = S_TRANSMIT; m_msg = p_msg; - header->dsn = ++m_dsn; } header->length = len; @@ -172,9 +139,11 @@ implementation { metadata->rssi = 0; metadata->lqi = 0; metadata->time = 0; - - call CC2420Transmit.sendCCA( m_msg ); - + + ccaOn = TRUE; + signal RadioBackoff.requestCca[((cc2420_header_t*)(m_msg->data - + sizeof(cc2420_header_t)))->type](m_msg); + call CC2420Transmit.send( m_msg, ccaOn ); return SUCCESS; } @@ -187,19 +156,89 @@ implementation { return TOSH_DATA_LENGTH; } - async event uint16_t CsmaBackoff.initial( message_t* m ) { - return ( call Random.rand16() & 0x1f ) + 1; + /**************** RadioBackoff Commands ****************/ + /** + * Must be called within a requestInitialBackoff event + * @param backoffTime the amount of time in some unspecified units to backoff + */ + async command void RadioBackoff.setInitialBackoff[am_id_t amId](uint16_t backoffTime) { + call SubBackoff.setInitialBackoff(backoffTime); } - - async event uint16_t CsmaBackoff.congestion( message_t* m ) { - return ( call Random.rand16() & 0x7 ) + 1; + + /** + * Must be called within a requestCongestionBackoff event + * @param backoffTime the amount of time in some unspecified units to backoff + */ + async command void RadioBackoff.setCongestionBackoff[am_id_t amId](uint16_t backoffTime) { + call SubBackoff.setCongestionBackoff(backoffTime); + } + + /** + * Must be called within a requestLplBackoff event + * @param backoffTime the amount of time in some unspecified units to backoff + */ + async command void RadioBackoff.setLplBackoff[am_id_t amId](uint16_t backoffTime) { + call SubBackoff.setLplBackoff(backoffTime); + } + + /** + * Enable CCA for the outbound packet. Must be called within a requestCca + * event + * @param ccaOn TRUE to enable CCA, which is the default. + */ + async command void RadioBackoff.setCca[am_id_t amId](bool useCca) { + ccaOn = useCca; } + + /**************** Events ****************/ async event void CC2420Transmit.sendDone( message_t* p_msg, error_t err ) { atomic sendErr = err; post sendDone_task(); } + async event void CC2420Power.startVRegDone() { + call Resource.request(); + } + + event void Resource.granted() { + call CC2420Power.startOscillator(); + } + + async event void CC2420Power.startOscillatorDone() { + post startDone_task(); + } + + /***************** SubBackoff Events ****************/ + async event void SubBackoff.requestInitialBackoff(message_t *msg) { + call SubBackoff.setInitialBackoff ( call Random.rand16() + % (0x1F * CC2420_BACKOFF_PERIOD) + CC2420_MIN_BACKOFF); + + signal RadioBackoff.requestInitialBackoff[((cc2420_header_t*)(msg->data - + sizeof(cc2420_header_t)))->type](msg); + } + + async event void SubBackoff.requestCongestionBackoff(message_t *msg) { + call SubBackoff.setCongestionBackoff( call Random.rand16() + % (0x7 * CC2420_BACKOFF_PERIOD) + CC2420_MIN_BACKOFF); + + signal RadioBackoff.requestCongestionBackoff[((cc2420_header_t*)(msg->data - + sizeof(cc2420_header_t)))->type](msg); + } + + async event void SubBackoff.requestLplBackoff(message_t *msg) { + call SubBackoff.setLplBackoff(call Random.rand16() % 10); + + signal RadioBackoff.requestLplBackoff[((cc2420_header_t*)(msg->data - + sizeof(cc2420_header_t)))->type](msg); + } + + async event void SubBackoff.requestCca(message_t *msg) { + // Lower layers than this do not configure the CCA settings + } + + + /***************** Tasks ****************/ task void sendDone_task() { error_t packetErr; atomic packetErr = sendErr; @@ -207,5 +246,45 @@ implementation { signal Send.sendDone( m_msg, packetErr ); } + task void startDone_task() { + call SubControl.start(); + call CC2420Power.rxOn(); + call Resource.release(); + m_state = S_STARTED; + signal SplitControl.startDone( SUCCESS ); + } + + task void stopDone_task() { + m_state = S_STOPPED; + signal SplitControl.stopDone( SUCCESS ); + } + + + /***************** Functions ****************/ + + + /***************** Defaults ***************/ + default event void SplitControl.startDone(error_t error) { + } + + default event void SplitControl.stopDone(error_t error) { + } + + + default async event void RadioBackoff.requestInitialBackoff[am_id_t amId]( + message_t *msg) { + } + + default async event void RadioBackoff.requestCongestionBackoff[am_id_t amId]( + message_t *msg) { + } + + default async event void RadioBackoff.requestLplBackoff[am_id_t amId]( + message_t *msg) { + } + + default async event void RadioBackoff.requestCca[am_id_t amId]( + message_t *msg) { + } } diff --git a/tos/chips/cc2420/CC2420DutyCycle.nc b/tos/chips/cc2420/CC2420DutyCycle.nc new file mode 100644 index 00000000..30c75f30 --- /dev/null +++ b/tos/chips/cc2420/CC2420DutyCycle.nc @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Manage the CC2420's duty cycle and power + * @author David Moss + */ + +interface CC2420DutyCycle { + + /** + * Set the sleep interval, in binary milliseconds + * @param sleepIntervalMs the sleep interval in [ms] + */ + command void setSleepInterval(uint16_t sleepIntervalMs); + + /** + * @return the sleep interval in [ms] + */ + command uint16_t getSleepInterval(); + + /** + * Sometimes the radio kicks on and an Rx interrupt causes the CCA + * checking for-loop to stop doing its job. The upper layers receive + * a message, but the for-loop CCA check never got the detect because it + * got cut off. So, LPL can override the duty cycler and leave the radio in + * its current state. When the radio turns back off again, the duty cycling + * continues + */ + command void forceDetected(); + + /** + * A transmitter was detected. You must now take action to + * turn the radio off when the transaction is complete. + */ + event void detected(); + +} + diff --git a/tos/chips/cc2420/CC2420DutyCycleC.nc b/tos/chips/cc2420/CC2420DutyCycleC.nc new file mode 100644 index 00000000..1c7ec375 --- /dev/null +++ b/tos/chips/cc2420/CC2420DutyCycleC.nc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Use this component to duty cycle the radio. When a message is heard, + * disable DutyCycling. + * + * @author David Moss dmm@rincon.com + */ + +configuration CC2420DutyCycleC { + provides { + interface CC2420DutyCycle; + interface SplitControl; + interface State as SplitControlState; + } +} + +implementation { + components MainC, + CC2420DutyCycleP, + CC2420TransmitC, + CC2420CsmaC, + LedsC, + new StateC() as RadioPowerStateC, + new StateC() as DutyCycleStateC, + new StateC() as CheckStateC, + new StateC() as SplitControlStateC, + new TimerMilliC() as OnTimerC, + new TimerMilliC() as CheckTimerC, + RandomC; + +#if defined(LOW_POWER_LISTENING) || defined(ACK_LOW_POWER_LISTENING) + components CC2420AckLplC as LplC; +#elif defined(NOACK_LOW_POWER_LISTENING) + components CC2420NoAckLplC as LplC; +#else + components CC2420LplDummyC as LplC; +#endif + + CC2420DutyCycle = CC2420DutyCycleP; + SplitControl = CC2420DutyCycleP; + SplitControlState = SplitControlStateC; + + MainC.SoftwareInit -> CC2420DutyCycleP; + + CC2420DutyCycleP.Random -> RandomC; + CC2420DutyCycleP.CC2420Cca -> CC2420TransmitC; + CC2420DutyCycleP.SubControl -> CC2420CsmaC; + CC2420DutyCycleP.SendState -> LplC; + CC2420DutyCycleP.RadioPowerState -> RadioPowerStateC; + CC2420DutyCycleP.DutyCycleState -> DutyCycleStateC; + CC2420DutyCycleP.SplitControlState -> SplitControlStateC; + CC2420DutyCycleP.CheckState -> CheckStateC; + CC2420DutyCycleP.OnTimer -> OnTimerC; + CC2420DutyCycleP.Leds -> LedsC; + +} + + diff --git a/tos/chips/cc2420/CC2420DutyCycleP.nc b/tos/chips/cc2420/CC2420DutyCycleP.nc new file mode 100644 index 00000000..6162b60c --- /dev/null +++ b/tos/chips/cc2420/CC2420DutyCycleP.nc @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Module to duty cycle the radio on and off, performing CCA receive checks. + * When a carrier is sensed, this will leave the radio on. It is then up + * to higher layers to turn the radio off again. Once the radio is turned + * off, this module will automatically continue duty cycling and looking for + * a modulated signal. + * + * @author David Moss + */ + +module CC2420DutyCycleP { + provides { + interface CC2420DutyCycle; + interface Init; + interface SplitControl; + } + + uses { + interface Timer as OnTimer; + interface SplitControl as SubControl; + interface State as RadioPowerState; + interface State as DutyCycleState; + interface State as SplitControlState; + interface State as CheckState; + interface State as SendState; + interface Leds; + interface CC2420Cca; + interface Random; + } +} + +implementation { + + /** The current period of the duty cycle, equivalent of wakeup interval */ + uint16_t sleepInterval; + + /** The number of times the CCA has been sampled in this wakeup period */ + uint16_t ccaChecks; + + /** TRUE if we get an Rx interrupt that stops our CCA checking loop */ + bool detectionForced; + + /** + * Radio Power, Check State, and Duty Cycling State + */ + enum { + S_OFF, // off by default + S_ON, + }; + + + /***************** Prototypes ****************/ + task void stopRadio(); + task void startRadio(); + task void getCca(); + + /***************** Init Commands ****************/ + command error_t Init.init() { + sleepInterval = 0; + return SUCCESS; + } + + /***************** CC2420DutyCycle Commands ****************/ + /** + * Set the sleep interval, in binary milliseconds + * @param sleepIntervalMs the sleep interval in [ms] + */ + command void CC2420DutyCycle.setSleepInterval(uint16_t sleepIntervalMs) { + if (!sleepInterval && sleepIntervalMs) { + // We were always on, now lets duty cycle + call DutyCycleState.forceState(S_ON); + call CheckState.toIdle(); + post stopRadio(); // Might want to delay turning off the radio + } + + detectionForced = FALSE; + sleepInterval = sleepIntervalMs; + + if(sleepInterval == 0 && call DutyCycleState.getState() == S_ON) { + call DutyCycleState.forceState(S_OFF); + call CheckState.toIdle(); + + /* + * Leave the radio on permanently if sleepInterval == 0 and the radio is + * supposed to be enabled + */ + if(call RadioPowerState.getState() == S_OFF) { + call SubControl.start(); + } + } + } + + /** + * @return the sleep interval in [ms] + */ + command uint16_t CC2420DutyCycle.getSleepInterval() { + return sleepInterval; + } + + command void CC2420DutyCycle.forceDetected() { + detectionForced = TRUE; + } + + /***************** SplitControl Commands ****************/ + command error_t SplitControl.start() { + call SplitControlState.forceState(S_ON); + + if(sleepInterval > 0) { + // Begin duty cycling + call DutyCycleState.forceState(S_ON); + call CheckState.toIdle(); + post stopRadio(); + signal SplitControl.startDone(SUCCESS); + + } else { + call DutyCycleState.forceState(S_OFF); + call CheckState.toIdle(); + + /* + * Leave the radio on permanently if sleepInterval == 0 and the radio is + * supposed to be enabled + */ + if(call RadioPowerState.getState() == S_OFF) { + call SubControl.start(); + // Here, SplitControl.startDone is signaled on SubControl.startDone + + } else { + // Radio is already on + signal SplitControl.startDone(SUCCESS); + } + } + + return SUCCESS; + } + + command error_t SplitControl.stop() { + call SplitControlState.forceState(S_OFF); + call DutyCycleState.forceState(S_OFF); + call CheckState.toIdle(); + return call SubControl.stop(); + + /* + * SubControl.stopDone signals SplitControl.stopDone when + * DutyCycleState is S_OFF + */ + } + + /***************** Timer Events ****************/ + event void OnTimer.fired() { + if(call DutyCycleState.getState() == S_ON) { + if(call RadioPowerState.getState() == S_OFF) { + call CheckState.forceState(S_ON); + ccaChecks = 0; + + /* + * The MicaZ, running on an external oscillator I think, and + * returning the microcontroller out of a sleep state to immediately + * perform an ADC conversion, sucks. The first ADC conversion out + * of a sleep state lasts about a second. We don't want the radio + * on that long. Like the CC1000 RSSI pulse check implementation + * done in the Rincon CC1000Radio stack, we will perform + * a single ADC conversion and then flip on the radio to check + * the channel. + */ + post getCca(); + + } else { + // Someone else turned on the radio, try again in awhile + call OnTimer.startOneShot(sleepInterval); + } + } + } + + /***************** SubControl Events ****************/ + event void SubControl.startDone(error_t error) { + if(call DutyCycleState.getState() == S_ON && error) { + // My responsibility to try again + post startRadio(); + return; + } + + call RadioPowerState.forceState(S_ON); + //call Leds.led2On(); + + if(call DutyCycleState.getState() == S_ON) { + if(call CheckState.getState() == S_ON) { + post getCca(); + } + + } else { + // Must have turned the radio on manually + signal SplitControl.startDone(SUCCESS); + } + } + + event void SubControl.stopDone(error_t error) { + if(error && call DutyCycleState.getState() == S_ON) { + // My responsibility to try again + post stopRadio(); + return; + } + + detectionForced = FALSE; + call RadioPowerState.forceState(S_OFF); + //call Leds.led2Off(); + + if(call DutyCycleState.getState() == S_ON) { + call OnTimer.startOneShot(sleepInterval); + + } else { + // Must have turned off the radio manually + signal SplitControl.stopDone(error); + } + + } + + + /***************** Tasks ****************/ + task void stopRadio() { + if(call DutyCycleState.getState() == S_ON && !detectionForced) { + if(call SubControl.stop() != SUCCESS) { + // Already stopped? + call OnTimer.startOneShot(sleepInterval); + } + } + } + + task void startRadio() { + if(call DutyCycleState.getState() == S_ON) { + if(call SubControl.start() != SUCCESS) { + post startRadio(); + } + } + } + + + task void getCca() { + uint8_t detects = 0; + if(call DutyCycleState.getState() == S_ON) { + + ccaChecks++; + if(ccaChecks == 1) { + // Microcontroller is ready, turn on the radio and sample a few times + post startRadio(); + return; + } + + atomic { + for( ; ccaChecks < MAX_LPL_CCA_CHECKS && call SendState.isIdle(); ccaChecks++) { + if(!call CC2420Cca.isChannelClear() || detectionForced) { + detects++; + if(detects > MIN_SAMPLES_BEFORE_DETECT) { + signal CC2420DutyCycle.detected(); + return; + } + // Leave the radio on for upper layers to perform some transaction + } + } + } + + call CheckState.toIdle(); + if(call SendState.isIdle()) { + post stopRadio(); + } + } + } + + /**************** Defaults ****************/ + default event void CC2420DutyCycle.detected() { + } + + + default event void SplitControl.startDone(error_t error) { + } + + default event void SplitControl.stopDone(error_t error) { + } +} + + diff --git a/tos/chips/cc2420/CC2420LplDummyC.nc b/tos/chips/cc2420/CC2420LplDummyC.nc new file mode 100644 index 00000000..b8ef1e60 --- /dev/null +++ b/tos/chips/cc2420/CC2420LplDummyC.nc @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Dummy low power listening interface used when LowPowerListening is not + * compiled in with the application. + * Sleep interval is always 0, and duty cycle is always 100% + * @author David Moss + */ + +configuration CC2420LplDummyC { + provides { + interface Send; + interface Receive; + interface LowPowerListening; + interface SplitControl; + interface State as SendState; + } + + uses { + interface Send as SubSend; + interface Receive as SubReceive; + interface SplitControl as SubControl; + } +} + +implementation { + components CC2420LplDummyP; + components new StateC(); + + Send = SubSend; + Receive = SubReceive; + SplitControl = SubControl; + LowPowerListening = CC2420LplDummyP; + SendState = StateC; + +} + diff --git a/tos/chips/cc2420/CC2420LplDummyP.nc b/tos/chips/cc2420/CC2420LplDummyP.nc new file mode 100644 index 00000000..97bdd0c6 --- /dev/null +++ b/tos/chips/cc2420/CC2420LplDummyP.nc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Dummy low power listening interface used when LowPowerListening is not + * compiled in with the application. + * Sleep interval is always 0, and duty cycle is always 100% + * @author David Moss + */ + +module CC2420LplDummyP { + provides { + interface LowPowerListening; + } +} + +implementation { + + command void LowPowerListening.setLocalSleepInterval(uint16_t sleepIntervalMs) { + } + + command uint16_t LowPowerListening.getLocalSleepInterval() { + return 0; + } + + command void LowPowerListening.setLocalDutyCycle(uint16_t dutyCycle) { + } + + command uint16_t LowPowerListening.getLocalDutyCycle() { + return 10000; + } + + command void LowPowerListening.setRxSleepInterval(message_t *msg, uint16_t sleepIntervalMs) { + } + + command uint16_t LowPowerListening.getRxSleepInterval(message_t *msg) { + return 0; + } + + command void LowPowerListening.setRxDutyCycle(message_t *msg, uint16_t dutyCycle) { + } + + command uint16_t LowPowerListening.getRxDutyCycle(message_t *msg) { + return 10000; + } + + command uint16_t LowPowerListening.dutyCycleToSleepInterval(uint16_t dutyCycle) { + return 0; + } + + command uint16_t LowPowerListening.sleepIntervalToDutyCycle(uint16_t sleepInterval) { + return 10000; + } + +} + diff --git a/tos/chips/cc2420/CC2420NoAckLpl.h b/tos/chips/cc2420/CC2420NoAckLpl.h new file mode 100644 index 00000000..7d00c500 --- /dev/null +++ b/tos/chips/cc2420/CC2420NoAckLpl.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + + /** + * @author David Moss + */ +#ifndef CC2420NOACKLPL_H +#define CC2420NOACKLPL_H + +/** + * Low Power Listening Send States + */ +typedef enum { + S_LPL_NOT_SENDING, // DEFAULT + S_LPL_FIRST_MESSAGE, // 1. Initial backoffs, no acks, full CCA + S_LPL_LAST_MESSAGE, // 3. No backoffs, acknowledgement request, no CCA +} lpl_sendstate_t; + +/** + * Amount of time, in milliseconds, to keep the radio on after + * a successful receive addressed to this node + * You don't want this too fast, or the off timer can accidentally + * fire due to delays in the system. The radio would shut off and + * possibly need to turn back on again immediately, which can lock up + * the CC2420 if it's in the middle of doing something. + */ +#ifndef DELAY_AFTER_RECEIVE +#define DELAY_AFTER_RECEIVE 100 +#endif + + +/** + * This is a measured value of the time in ms the radio is actually on + * We round this up to err on the side of better performance ratios + */ +#ifndef DUTY_ON_TIME +#define DUTY_ON_TIME 1 +#endif + +/** + * The maximum number of CCA checks performed on each wakeup. + * The value is relative to the speed of transmission and speed of the + * microcontroller executing the receive check loop. If the transmission + * is ultimately back-to-back without break or the microcontroller + * is slow, we can do less samples. If the microcontroller is very + * fast, we must do more samples. Keep in mind the datasheet also + * specifies that the CCA pin is valid after 8 symbol periods of + * the radio being on. + */ +#ifndef MAX_LPL_CCA_CHECKS + +#if defined(PLATFORM_TELOSB) +#define MAX_LPL_CCA_CHECKS 12 +#else +#define MAX_LPL_CCA_CHECKS 12 +#endif + +#endif + +/** + * The minimum number of samples that must be taken in CC2420DutyCycleP + * that show the channel is not clear before a detection event is issued + */ +#ifndef MIN_SAMPLES_BEFORE_DETECT +#define MIN_SAMPLES_BEFORE_DETECT 1 +#endif + +#endif + diff --git a/tos/chips/cc2420/CC2420NoAckLplC.nc b/tos/chips/cc2420/CC2420NoAckLplC.nc new file mode 100644 index 00000000..e733b985 --- /dev/null +++ b/tos/chips/cc2420/CC2420NoAckLplC.nc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Low Power Listening for the CC2420 + * This is experimental because: + * > Acknowledgements really don't work at all, even on the last message + * which is supposed to return an acknowledgement from the receiver. + * > The CRC is not yet manually calculated, so the entire continuous + * modulation doesn't contain any useful information. + * By adding in the CRC calcuation functionality to TransmitP, + * we can better support mobile nodes that walk out of range and + * lossy connections. We could also put receivers back to sleep that + * the "preamble" is not destined for, because the actual preamble + * would have an address associated with it. + * + * @author David Moss + */ + +#include "CC2420NoAckLpl.h" +#warning "*** USING EXPERIMENTAL NO-ACK LOW POWER LISTENING LAYER" + +configuration CC2420NoAckLplC { + provides { + interface LowPowerListening; + interface Send; + interface Receive; + interface SplitControl; + interface State as SendState; + } + + uses { + interface Send as SubSend; + interface Receive as SubReceive; + interface SplitControl as SubControl; + } +} + +implementation { + components MainC, + CC2420NoAckLplP, + CC2420DutyCycleC, + CC2420ActiveMessageC, + CC2420CsmaC, + CC2420TransmitC, + CC2420PacketC, + RandomC, + LedsC, + new StateC() as SendStateC, + new StateC() as RadioStateC, + new TimerMilliC() as OffTimerC; + + LowPowerListening = CC2420NoAckLplP; + Send = CC2420NoAckLplP; + Receive = CC2420NoAckLplP; + SplitControl = CC2420DutyCycleC; + SendState = SendStateC; + + SubControl = CC2420NoAckLplP.SubControl; + SubReceive = CC2420NoAckLplP.SubReceive; + SubSend = CC2420NoAckLplP.SubSend; + + + MainC.SoftwareInit -> CC2420NoAckLplP; + + CC2420NoAckLplP.Random -> RandomC; + CC2420NoAckLplP.SendState -> SendStateC; + CC2420NoAckLplP.RadioState -> RadioStateC; + CC2420NoAckLplP.SplitControlState -> CC2420DutyCycleC; + CC2420NoAckLplP.CC2420Cca -> CC2420TransmitC; + CC2420NoAckLplP.OffTimer -> OffTimerC; + CC2420NoAckLplP.CC2420DutyCycle -> CC2420DutyCycleC; + CC2420NoAckLplP.Resend -> CC2420TransmitC; + CC2420NoAckLplP.PacketAcknowledgements -> CC2420ActiveMessageC; + CC2420NoAckLplP.AMPacket -> CC2420ActiveMessageC; + CC2420NoAckLplP.CC2420Packet -> CC2420PacketC; + CC2420NoAckLplP.RadioBackoff -> CC2420CsmaC; + CC2420NoAckLplP.Leds -> LedsC; + +} + diff --git a/tos/chips/cc2420/CC2420NoAckLplP.nc b/tos/chips/cc2420/CC2420NoAckLplP.nc new file mode 100644 index 00000000..8657af1c --- /dev/null +++ b/tos/chips/cc2420/CC2420NoAckLplP.nc @@ -0,0 +1,552 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Low Power Listening for the CC2420. This component is responsible for + * delivery of an LPL packet, and for turning off the radio when the radio + * has run out of tasks. + * + * The CC2420DutyCycle component is responsible for duty cycling the radio + * and performing receive detections. + * + * @author David Moss + */ + +#include "CC2420NoAckLpl.h" + +module CC2420NoAckLplP { + provides { + interface Init; + interface LowPowerListening; + interface Send; + interface Receive; + } + + uses { + interface Send as SubSend; + interface CC2420Transmit as Resend; + interface Receive as SubReceive; + interface AMPacket; + interface CC2420Cca; + interface SplitControl as SubControl; + interface CC2420DutyCycle; + interface CC2420Packet; + interface PacketAcknowledgements; + interface RadioBackoff[am_id_t amId]; + interface State as SendState; + interface State as RadioState; + interface State as SplitControlState; + interface Random; + interface Timer as OffTimer; + interface Leds; + } +} + +implementation { + + /** The message currently being sent */ + message_t *currentSendMsg; + + /** The length of the current send message */ + uint8_t currentSendLen; + + /** TRUE if the radio is duty cycling and not always on */ + bool dutyCycling; + + /** TRUE if we have received a message after the last detect */ + bool receivedMsg; + + + /** + * Radio State + */ + enum { + S_OFF, + S_ON, + }; + + /** + * Send States + */ + enum { + S_IDLE, + S_SENDING, + }; + + enum { + ONE_MESSAGE = 0, + }; + + + /***************** Prototypes ***************/ + task void send(); + task void resend(); + task void startRadio(); + task void stopRadio(); + task void detectTxDone(); + + void initializeSend(); + void startOffTimer(); + uint16_t getActualDutyCycle(uint16_t dutyCycle); + + /***************** Init Commands ***************/ + command error_t Init.init() { + dutyCycling = FALSE; + return SUCCESS; + } + + /***************** LowPowerListening Commands ***************/ + /** + * Set this this node's radio sleep interval, in milliseconds. + * Once every interval, the node will sleep and perform an Rx check + * on the radio. Setting the sleep interval to 0 will keep the radio + * always on. + * + * This is the equivalent of setting the local duty cycle rate. + * + * @param sleepIntervalMs the length of this node's Rx check interval, in [ms] + */ + command void LowPowerListening.setLocalSleepInterval( + uint16_t sleepIntervalMs) { + call CC2420DutyCycle.setSleepInterval(sleepIntervalMs); + } + + /** + * @return the local node's sleep interval, in [ms] + */ + command uint16_t LowPowerListening.getLocalSleepInterval() { + return call CC2420DutyCycle.getSleepInterval(); + } + + /** + * Set this node's radio duty cycle rate, in units of [percentage*100]. + * For example, to get a 0.05% duty cycle, + * + * call LowPowerListening.setDutyCycle(5); + * + * + * For a 100% duty cycle (always on), + * + * call LowPowerListening.setDutyCycle(10000); + * + * + * This is the equivalent of setting the local sleep interval explicitly. + * + * @param dutyCycle The duty cycle percentage, in units of [percentage*100] + */ + command void LowPowerListening.setLocalDutyCycle(uint16_t dutyCycle) { + call CC2420DutyCycle.setSleepInterval( + call LowPowerListening.dutyCycleToSleepInterval(dutyCycle)); + } + + /** + * @return this node's radio duty cycle rate, in units of [percentage*100] + */ + command uint16_t LowPowerListening.getLocalDutyCycle() { + return call LowPowerListening.sleepIntervalToDutyCycle( + call CC2420DutyCycle.getSleepInterval()); + } + + + /** + * Configure this outgoing message so it can be transmitted to a neighbor mote + * with the specified Rx sleep interval. + * @param msg Pointer to the message that will be sent + * @param sleepInterval The receiving node's sleep interval, in [ms] + */ + command void LowPowerListening.setRxSleepInterval(message_t *msg, + uint16_t sleepIntervalMs) { + (call CC2420Packet.getMetadata(msg))->rxInterval = sleepIntervalMs; + } + + /** + * @return the destination node's sleep interval configured in this message + */ + command uint16_t LowPowerListening.getRxSleepInterval(message_t *msg) { + return (call CC2420Packet.getMetadata(msg))->rxInterval; + } + + /** + * Configure this outgoing message so it can be transmitted to a neighbor mote + * with the specified Rx duty cycle rate. + * Duty cycle is in units of [percentage*100], i.e. 0.25% duty cycle = 25. + * + * @param msg Pointer to the message that will be sent + * @param dutyCycle The duty cycle of the receiving mote, in units of + * [percentage*100] + */ + command void LowPowerListening.setRxDutyCycle(message_t *msg, + uint16_t dutyCycle) { + (call CC2420Packet.getMetadata(msg))->rxInterval = + call LowPowerListening.dutyCycleToSleepInterval(dutyCycle); + } + + + /** + * @return the destination node's duty cycle configured in this message + * in units of [percentage*100] + */ + command uint16_t LowPowerListening.getRxDutyCycle(message_t *msg) { + return call LowPowerListening.sleepIntervalToDutyCycle( + (call CC2420Packet.getMetadata(msg))->rxInterval); + } + + /** + * Convert a duty cycle, in units of [percentage*100], to + * the sleep interval of the mote in milliseconds + * @param dutyCycle The duty cycle in units of [percentage*100] + * @return The equivalent sleep interval, in units of [ms] + */ + command uint16_t LowPowerListening.dutyCycleToSleepInterval( + uint16_t dutyCycle) { + dutyCycle = getActualDutyCycle(dutyCycle); + + if(dutyCycle == 10000) { + return 0; + } + + return (DUTY_ON_TIME * (10000 - dutyCycle)) / dutyCycle; + } + + /** + * Convert a sleep interval, in units of [ms], to a duty cycle + * in units of [percentage*100] + * @param sleepInterval The sleep interval in units of [ms] + * @return The duty cycle in units of [percentage*100] + */ + command uint16_t LowPowerListening.sleepIntervalToDutyCycle( + uint16_t sleepInterval) { + if(sleepInterval == 0) { + return 10000; + } + + return getActualDutyCycle((DUTY_ON_TIME * 10000) + / (sleepInterval + DUTY_ON_TIME)); + } + + + /***************** Send Commands ***************/ + /** + * Each call to this send command gives the message a single + * DSN that does not change for every copy of the message + * sent out. For messages that are not acknowledged, such as + * a broadcast address message, the receiving end does not + * signal receive() more than once for that message. + */ + command error_t Send.send(message_t *msg, uint8_t len) { + if(call SplitControlState.getState() == S_OFF) { + // Everything is off right now, start SplitControl and try again + return EOFF; + } + + if(call SendState.requestState(S_LPL_FIRST_MESSAGE) == SUCCESS) { + currentSendMsg = msg; + currentSendLen = len; + + // In case our off timer is running... + call OffTimer.stop(); + + if(call RadioState.getState() == S_ON) { + initializeSend(); + return SUCCESS; + + } else { + post startRadio(); + } + + return SUCCESS; + } + + return FAIL; + } + + command error_t Send.cancel(message_t *msg) { + if(currentSendMsg == msg) { + call SendState.toIdle(); + startOffTimer(); + return call SubSend.cancel(msg); + } + + return FAIL; + } + + + command uint8_t Send.maxPayloadLength() { + return call SubSend.maxPayloadLength(); + } + + command void *Send.getPayload(message_t* msg) { + return call SubSend.getPayload(msg); + } + + /***************** Receive Commands ***************/ + command void *Receive.getPayload(message_t* msg, uint8_t* len) { + return call SubReceive.getPayload(msg, len); + } + + command uint8_t Receive.payloadLength(message_t* msg) { + return call SubReceive.payloadLength(msg); + } + + + /***************** DutyCycle Events ***************/ + /** + * A transmitter was detected. You must now take action to + * turn the radio off when the transaction is complete. + */ + event void CC2420DutyCycle.detected() { + // At this point, the duty cycling has been disabled temporary + // and it will be this component's job to turn the radio back off + // Wait long enough to see if we actually receive a packet + receivedMsg = FALSE; + post detectTxDone(); + } + + + /***************** SubControl Events ***************/ + event void SubControl.startDone(error_t error) { + if(!error) { + call RadioState.forceState(S_ON); + + if(call SendState.getState() == S_LPL_FIRST_MESSAGE) { + initializeSend(); + } + } + } + + event void SubControl.stopDone(error_t error) { + if(!error) { + call RadioState.forceState(S_OFF); + + if(call SendState.getState() == S_LPL_FIRST_MESSAGE) { + // We're in the middle of sending a message; start the radio back up + post startRadio(); + + } else { + call OffTimer.stop(); + } + } + } + + /***************** SubSend Events ***************/ + event void SubSend.sendDone(message_t* msg, error_t error) { + switch(call SendState.getState()) { + case S_LPL_FIRST_MESSAGE: + call SendState.forceState(S_LPL_LAST_MESSAGE); + call PacketAcknowledgements.requestAck(msg); + post send(); + return; + + case S_LPL_LAST_MESSAGE: + // Execute past the switch statement + break; + + default: + // Execute past the switch statement + break; + } + + call SendState.toIdle(); + startOffTimer(); + signal Send.sendDone(msg, error); + } + + /***************** SubReceive Events ***************/ + /** + * If the received message is new, we signal the receive event and + * start the off timer. If the last message we received had the same + * DSN as this message, then the chances are pretty good + * that this message should be ignored, especially if the destination address + * as the broadcast address + */ + event message_t *SubReceive.receive(message_t* msg, void* payload, + uint8_t len) { + + /** + * Now wait for the next moment the channel is free before passing the + * packet up, and keep the radio on that whole time. That's when our + * transmitter is done transmitting, LPL or not. + */ + + call CC2420DutyCycle.forceDetected(); + receivedMsg = TRUE; + startOffTimer(); + return signal Receive.receive(msg, payload, len); + } + + /***************** Timer Events ****************/ + event void OffTimer.fired() { + /* + * Only stop the radio if the radio is supposed to be off permanently + * or if the duty cycle is on and our sleep interval is not 0 + */ + if(call SplitControlState.getState() == S_OFF + || (call CC2420DutyCycle.getSleepInterval() > 0 + && call SplitControlState.getState() == S_ON + && call SendState.getState() == S_LPL_NOT_SENDING)) { + post stopRadio(); + } + } + + /***************** RadioBackoff Events ****************/ + /** + * Request for input on the initial backoff + * Reply using setInitialBackoff(..) + * @param msg pointer to the message being sent + */ + async event void RadioBackoff.requestInitialBackoff[am_id_t amId](message_t *msg) { + } + + /** + * Request for input on the congestion backoff + * Reply using setCongestionBackoff(..) + * @param msg pointer to the message being sent + */ + async event void RadioBackoff.requestCongestionBackoff[am_id_t amId](message_t *msg) { + } + + /** + * Request for input on the low power listening backoff + * This should be somewhat random, but as short as possible + * Reply using setLplBackoff(..) + * @param msg pointer to the message being sent + */ + async event void RadioBackoff.requestLplBackoff[am_id_t amId](message_t *msg) { + } + + /** + * Request for input on whether or not to use CCA on the outbound packet. + * Replies should come in the form of setCca(..) + * @param msg pointer to the message being sent + */ + async event void RadioBackoff.requestCca[am_id_t amId](message_t *msg) { + if(call SendState.getState() == S_LPL_FIRST_MESSAGE) { + call RadioBackoff.setCca[amId](TRUE); + + } else if(call SendState.getState() == S_LPL_LAST_MESSAGE) { + call RadioBackoff.setCca[amId](FALSE); + } + } + + + /***************** Resend Events ****************/ + /** + * Signal that a message has been sent + * + * @param p_msg message to send. + * @param error notifaction of how the operation went. + */ + async event void Resend.sendDone( message_t* p_msg, error_t error ) { + // This is actually caught by SubSend.sendDone + } + + + /***************** Tasks ***************/ + task void send() { + if(call SubSend.send(currentSendMsg, currentSendLen) != SUCCESS) { + post send(); + } + } + + task void resend() { + if(call Resend.resend(FALSE) != SUCCESS) { + post resend(); + } + } + + task void startRadio() { + if(call SubControl.start() != SUCCESS) { + post startRadio(); + } + } + + task void stopRadio() { + if(call SendState.getState() == S_LPL_NOT_SENDING) { + if(call SubControl.stop() != SUCCESS) { + post stopRadio(); + } + } + } + + /** + * Don't start the off-timer until the end of the transmitter's transmission. + */ + task void detectTxDone() { + uint16_t clearChannelSamples; + + if(call SendState.getState() != S_LPL_FIRST_MESSAGE + && call SendState.getState() != S_LPL_LAST_MESSAGE + && !receivedMsg) { + for(clearChannelSamples = 0; clearChannelSamples < MAX_LPL_CCA_CHECKS * 8; clearChannelSamples++) { + // In one straight shot, sample the channel repetitively and verify that + // the transmitter is done transmitting + if(!call CC2420Cca.isChannelClear()) { + // Nope, start over from the beginning. + post detectTxDone(); + return; + } + } + + // Transmitter done, turn the radio off in a few... + startOffTimer(); + } + } + + /***************** Functions ***************/ + void initializeSend() { + if(call LowPowerListening.getRxSleepInterval(currentSendMsg) + > ONE_MESSAGE) { + call PacketAcknowledgements.noAck(currentSendMsg); + } + + post send(); + } + + + void startOffTimer() { + call OffTimer.startOneShot(DELAY_AFTER_RECEIVE); + } + + /** + * Check the bounds on a given duty cycle + * We're never over 100%, and we're never at 0% + */ + uint16_t getActualDutyCycle(uint16_t dutyCycle) { + if(dutyCycle > 10000) { + return 10000; + } else if(dutyCycle == 0) { + return 1; + } + + return dutyCycle; + } +} + diff --git a/tos/chips/cc2420/CC2420Packet.nc b/tos/chips/cc2420/CC2420Packet.nc index 52ce70d7..2c955978 100644 --- a/tos/chips/cc2420/CC2420Packet.nc +++ b/tos/chips/cc2420/CC2420Packet.nc @@ -31,7 +31,8 @@ /** * @author Jonathan Hui - * @version $Revision$ $Date$ + * @author David Moss + * @author Chad Metcalf */ #include "message.h" @@ -70,4 +71,13 @@ interface CC2420Packet { */ async command uint8_t getLqi( message_t* p_msg ); + /** + * @return pointer to the cc2420_header_t of the given message + */ + async command cc2420_header_t *getHeader(message_t *msg); + + /** + * @return pointer to the cc2420_metadata_t of the given message + */ + async command cc2420_metadata_t *getMetadata(message_t *msg); } diff --git a/tos/chips/cc2420/CC2420PacketC.nc b/tos/chips/cc2420/CC2420PacketC.nc index e0640233..9241185f 100644 --- a/tos/chips/cc2420/CC2420PacketC.nc +++ b/tos/chips/cc2420/CC2420PacketC.nc @@ -31,10 +31,12 @@ /** * @author Jonathan Hui - * @version $Revision$ $Date$ + * @author David Moss + * @author Chad Metcalf */ #include "IEEE802154.h" +#include "message.h" module CC2420PacketC { @@ -45,44 +47,44 @@ module CC2420PacketC { implementation { - cc2420_header_t* getHeader( message_t* msg ) { + async command cc2420_header_t *CC2420Packet.getHeader( message_t* msg ) { return (cc2420_header_t*)( msg->data - sizeof( cc2420_header_t ) ); } - cc2420_metadata_t* getMetadata( message_t* msg ) { + async command cc2420_metadata_t *CC2420Packet.getMetadata( message_t* msg ) { return (cc2420_metadata_t*)msg->metadata; } async command error_t Acks.requestAck( message_t* p_msg ) { - getHeader( p_msg )->fcf |= 1 << IEEE154_FCF_ACK_REQ; + (call CC2420Packet.getHeader( p_msg ))->fcf |= 1 << IEEE154_FCF_ACK_REQ; return SUCCESS; } async command error_t Acks.noAck( message_t* p_msg ) { - getHeader( p_msg )->fcf &= ~(1 << IEEE154_FCF_ACK_REQ); + (call CC2420Packet.getHeader( p_msg ))->fcf &= ~(1 << IEEE154_FCF_ACK_REQ); return SUCCESS; } async command bool Acks.wasAcked( message_t* p_msg ) { - return getMetadata( p_msg )->ack; + return (call CC2420Packet.getMetadata( p_msg ))->ack; } async command void CC2420Packet.setPower( message_t* p_msg, uint8_t power ) { if ( power > 31 ) power = 31; - getMetadata( p_msg )->tx_power = power; + (call CC2420Packet.getMetadata( p_msg ))->tx_power = power; } async command uint8_t CC2420Packet.getPower( message_t* p_msg ) { - return getMetadata( p_msg )->tx_power; + return (call CC2420Packet.getMetadata( p_msg ))->tx_power; } async command int8_t CC2420Packet.getRssi( message_t* p_msg ) { - return getMetadata( p_msg )->rssi; + return (call CC2420Packet.getMetadata( p_msg ))->rssi; } async command error_t CC2420Packet.getLqi( message_t* p_msg ) { - return getMetadata( p_msg )->lqi; + return (call CC2420Packet.getMetadata( p_msg ))->lqi; } } diff --git a/tos/chips/cc2420/CC2420ReceiveC.nc b/tos/chips/cc2420/CC2420ReceiveC.nc index b1762e88..452aad29 100644 --- a/tos/chips/cc2420/CC2420ReceiveC.nc +++ b/tos/chips/cc2420/CC2420ReceiveC.nc @@ -38,16 +38,17 @@ configuration CC2420ReceiveC { - provides interface Init; - provides interface AsyncStdControl; + provides interface StdControl; provides interface CC2420Receive; provides interface Receive; } implementation { - + components MainC; components CC2420ReceiveP; + components CC2420PacketC; + components ActiveMessageAddressC; components new CC2420SpiC() as Spi; components HplCC2420PinsC as Pins; @@ -56,18 +57,21 @@ implementation { components LedsC as Leds; CC2420ReceiveP.Leds -> Leds; - Init = CC2420ReceiveP; - AsyncStdControl = CC2420ReceiveP; + StdControl = CC2420ReceiveP; CC2420Receive = CC2420ReceiveP; Receive = CC2420ReceiveP; + MainC.SoftwareInit -> CC2420ReceiveP; + CC2420ReceiveP.CSN -> Pins.CSN; CC2420ReceiveP.FIFO -> Pins.FIFO; CC2420ReceiveP.FIFOP -> Pins.FIFOP; CC2420ReceiveP.InterruptFIFOP -> InterruptsC.InterruptFIFOP; - CC2420ReceiveP.SpiResource -> Spi; CC2420ReceiveP.RXFIFO -> Spi.RXFIFO; CC2420ReceiveP.SFLUSHRX -> Spi.SFLUSHRX; + CC2420ReceiveP.SACK -> Spi.SACK; + CC2420ReceiveP.CC2420Packet -> CC2420PacketC; + CC2420ReceiveP.amAddress -> ActiveMessageAddressC; } diff --git a/tos/chips/cc2420/CC2420ReceiveP.nc b/tos/chips/cc2420/CC2420ReceiveP.nc index c318e1d8..96df09e7 100644 --- a/tos/chips/cc2420/CC2420ReceiveP.nc +++ b/tos/chips/cc2420/CC2420ReceiveP.nc @@ -31,13 +31,15 @@ /** * @author Jonathan Hui + * @author David Moss + * @author Jung Il Choi * @version $Revision$ $Date$ */ module CC2420ReceiveP { provides interface Init; - provides interface AsyncStdControl; + provides interface StdControl; provides interface CC2420Receive; provides interface Receive; @@ -50,9 +52,10 @@ module CC2420ReceiveP { uses interface CC2420Fifo as RXFIFO; uses interface CC2420Strobe as SACK; uses interface CC2420Strobe as SFLUSHRX; - + uses interface CC2420Packet; + uses interface Leds; - + uses async command am_addr_t amAddress(); } implementation { @@ -70,100 +73,129 @@ implementation { }; uint16_t m_timestamp_queue[ TIMESTAMP_QUEUE_SIZE ]; - uint8_t m_timestamp_head, m_timestamp_size; + + uint8_t m_timestamp_head; + + uint8_t m_timestamp_size; + uint8_t m_missed_packets; + bool fallingEdgeEnabled; + norace uint8_t m_bytes_left; + norace message_t* m_p_rx_buf; message_t m_rx_buf; + cc2420_receive_state_t m_state; - + + /***************** Prototypes ****************/ + void reset_state(); void beginReceive(); void receive(); void waitForNextPacket(); - task void receiveDone_task(); - - cc2420_header_t* getHeader( message_t* msg ) { - return (cc2420_header_t*)( msg->data - sizeof( cc2420_header_t ) ); - } + void flush(); - cc2420_metadata_t* getMetadata( message_t* msg ) { - return (cc2420_metadata_t*)msg->metadata; - } + task void receiveDone_task(); + /***************** Init Commands ****************/ command error_t Init.init() { + fallingEdgeEnabled = FALSE; m_p_rx_buf = &m_rx_buf; return SUCCESS; } - void reset_state() { - m_bytes_left = RXFIFO_SIZE; - m_timestamp_head = m_timestamp_size = 0; - m_missed_packets = 0; - } - - async command error_t AsyncStdControl.start() { + /***************** StdControl ****************/ + command error_t StdControl.start() { atomic { reset_state(); m_state = S_STARTED; + + // Warning: MicaZ problems have been encountered with the following line + // after it follows a .disable command call InterruptFIFOP.enableFallingEdge(); } return SUCCESS; } - async command error_t AsyncStdControl.stop() { + + + command error_t StdControl.stop() { atomic { m_state = S_STOPPED; + reset_state(); + + call SpiResource.release(); + + // Warning: MicaZ problems have been encountered with the following line + // followed by a re-enable. The re-enable doesn't occur. call InterruptFIFOP.disable(); } return SUCCESS; } + /***************** Receive Commands ****************/ + command void* Receive.getPayload(message_t* m, uint8_t* len) { + if (len != NULL) { + *len = ((uint8_t*) (call CC2420Packet.getHeader( m_p_rx_buf )))[0]; + } + return m->data; + } + + command uint8_t Receive.payloadLength(message_t* m) { + uint8_t* buf = (uint8_t*)(call CC2420Packet.getHeader( m_p_rx_buf )); + return buf[0]; + } + + + /***************** CC2420Receive Commands ****************/ + /** + * Start frame delimiter signifies the beginning/end of a packet + * See the CC2420 datasheet for details. + */ async command void CC2420Receive.sfd( uint16_t time ) { if ( m_timestamp_size < TIMESTAMP_QUEUE_SIZE ) { uint8_t tail = ( ( m_timestamp_head + m_timestamp_size ) % - TIMESTAMP_QUEUE_SIZE ); + TIMESTAMP_QUEUE_SIZE ); m_timestamp_queue[ tail ] = time; m_timestamp_size++; } } async command void CC2420Receive.sfd_dropped() { - if ( m_timestamp_size ) + if ( m_timestamp_size ) { m_timestamp_size--; + } } + + /***************** InterruptFIFOP Events ****************/ async event void InterruptFIFOP.fired() { - if ( m_state == S_STARTED ) + if ( m_state == S_STARTED ) { beginReceive(); - else + + } else { m_missed_packets++; + } } - void beginReceive() { - m_state = S_RX_HEADER; - if ( call SpiResource.immediateRequest() == SUCCESS ) - receive(); - else - call SpiResource.request(); - } + /***************** SpiResource Events ****************/ event void SpiResource.granted() { receive(); } - - void receive() { - call CSN.clr(); - call RXFIFO.beginRead( (uint8_t*)getHeader( m_p_rx_buf ), 1 ); - } - + + /***************** RXFIFO Events ****************/ + /** + * We received some bytes from the SPI bus. Process them in the context + * of the state we're in + */ async event void RXFIFO.readDone( uint8_t* rx_buf, uint8_t rx_len, - error_t error ) { - - cc2420_header_t* header = getHeader( m_p_rx_buf ); - cc2420_metadata_t* metadata = getMetadata( m_p_rx_buf ); - uint8_t* buf = (uint8_t*)header; + error_t error ) { + cc2420_header_t* header = call CC2420Packet.getHeader( m_p_rx_buf ); + cc2420_metadata_t* metadata = call CC2420Packet.getMetadata( m_p_rx_buf ); + uint8_t* buf = (uint8_t*) header; uint8_t length = buf[ 0 ]; switch( m_state ) { @@ -171,47 +203,67 @@ implementation { case S_RX_HEADER: m_state = S_RX_PAYLOAD; if ( length + 1 > m_bytes_left ) { - reset_state(); - call CSN.set(); - call CSN.clr(); - call SFLUSHRX.strobe(); - call SFLUSHRX.strobe(); - call CSN.set(); - call SpiResource.release(); - waitForNextPacket(); - } - else { - if ( !call FIFO.get() && !call FIFOP.get() ) - m_bytes_left -= length + 1; - call RXFIFO.continueRead( (length <= MAC_PACKET_SIZE) ? buf + 1 : NULL, - length ); + flush(); + + } else { + if ( !call FIFO.get() && !call FIFOP.get() ) { + m_bytes_left -= length + 1; + } + + if(length <= MAC_PACKET_SIZE) { + if(length > 0) { + // Length is in bounds; read in this packet + call RXFIFO.continueRead((uint8_t*)(call CC2420Packet.getHeader( + m_p_rx_buf )) + 1, length); + + } else { + // Length == 0; start reading the next packet + call CSN.set(); + call SpiResource.release(); + waitForNextPacket(); + } + + } else { + // Length is too large; we have to flush the entire Rx FIFO + flush(); + } } break; case S_RX_PAYLOAD: - call CSN.set(); + +#ifndef CC2420_NO_ACKNOWLEDGEMENTS + // Sorry about the preprocessing stuff. BaseStation depends on it. + if (((( header->fcf >> IEEE154_FCF_ACK_REQ ) & 0x01) == 1) + && (header->dest == call amAddress()) + && ((( header->fcf >> IEEE154_FCF_FRAME_TYPE ) & 7) == IEEE154_TYPE_DATA)) { + call CSN.clr(); + call SACK.strobe(); + call CSN.set(); + } +#endif + call SpiResource.release(); if ( m_timestamp_size ) { - if ( length > 10 ) { - metadata->time = m_timestamp_queue[ m_timestamp_head ]; - m_timestamp_head = ( m_timestamp_head + 1 ) % TIMESTAMP_QUEUE_SIZE; - m_timestamp_size--; - } - } - else { - metadata->time = 0xffff; + if ( length > 10 ) { + metadata->time = m_timestamp_queue[ m_timestamp_head ]; + m_timestamp_head = ( m_timestamp_head + 1 ) % TIMESTAMP_QUEUE_SIZE; + m_timestamp_size--; + } + } else { + metadata->time = 0xffff; } - // pass packet up if crc is good + // We may have received an ack that should be processed by Transmit if ( ( buf[ length ] >> 7 ) && rx_buf ) { - uint8_t type = ( header->fcf >> IEEE154_FCF_FRAME_TYPE ) & 7; - signal CC2420Receive.receive( type, m_p_rx_buf ); - if ( type == IEEE154_TYPE_DATA ) { - post receiveDone_task(); - return; - } + uint8_t type = ( header->fcf >> IEEE154_FCF_FRAME_TYPE ) & 7; + signal CC2420Receive.receive( type, m_p_rx_buf ); + if ( type == IEEE154_TYPE_DATA ) { + post receiveDone_task(); + return; + } } waitForNextPacket(); @@ -226,10 +278,17 @@ implementation { } + async event void RXFIFO.writeDone( uint8_t* tx_buf, uint8_t tx_len, error_t error ) { + } + + /***************** Tasks *****************/ + /** + * Fill in metadata details, pass the packet up the stack, and + * get the next packet. + */ task void receiveDone_task() { - - cc2420_header_t* header = getHeader( m_p_rx_buf ); - cc2420_metadata_t* metadata = getMetadata( m_p_rx_buf ); + cc2420_header_t* header = call CC2420Packet.getHeader( m_p_rx_buf ); + cc2420_metadata_t* metadata = call CC2420Packet.getMetadata( m_p_rx_buf ); uint8_t* buf = (uint8_t*)header; uint8_t length = buf[ 0 ]; @@ -237,42 +296,86 @@ implementation { metadata->rssi = buf[ length - 1 ]; metadata->lqi = buf[ length ] & 0x7f; m_p_rx_buf = signal Receive.receive( m_p_rx_buf, m_p_rx_buf->data, - length ); + length ); waitForNextPacket(); + } + + /****************** Functions ****************/ + /** + * Attempt to acquire the SPI bus to receive a packet. + */ + void beginReceive() { + m_state = S_RX_HEADER; + + if ( call SpiResource.immediateRequest() == SUCCESS ) { + receive(); + } else { + call SpiResource.request(); + } + } + + /** + * Flush out the Rx FIFO + */ + void flush() { + reset_state(); + call CSN.set(); + call CSN.clr(); + call SFLUSHRX.strobe(); + call SFLUSHRX.strobe(); + call CSN.set(); + call SpiResource.release(); + waitForNextPacket(); + } + + /** + * The first byte of each packet is the length byte. Read in that single + * byte, and then read in the rest of the packet. The CC2420 could contain + * multiple packets that have been buffered up, so if something goes wrong, + * we necessarily want to flush out the FIFO unless we have to. + */ + void receive() { + call CSN.clr(); + call RXFIFO.beginRead( (uint8_t*)(call CC2420Packet.getHeader( m_p_rx_buf )), 1 ); } - void waitForNextPacket() { + /** + * Determine if there's a packet ready to go, or if we should do nothing + * until the next packet arrives + */ + void waitForNextPacket() { atomic { - if ( m_state == S_STOPPED ) - return; + if ( m_state == S_STOPPED ) { + return; + } if ( ( m_missed_packets && call FIFO.get() ) || !call FIFOP.get() ) { - if ( m_missed_packets ) - m_missed_packets--; - beginReceive(); - } - else { - m_state = S_STARTED; - m_missed_packets = 0; + // A new packet is buffered up and ready to go + if ( m_missed_packets ) { + m_missed_packets--; + } + + beginReceive(); + + } else { + // Wait for the next packet to arrive + m_state = S_STARTED; + m_missed_packets = 0; } - } - - } - - command void* Receive.getPayload(message_t* m, uint8_t* len) { - if (len != NULL) { - *len = TOSH_DATA_LENGTH; } - return m->data; } - - command uint8_t Receive.payloadLength(message_t* m) { - uint8_t* buf = (uint8_t*)getHeader( m_p_rx_buf ); - return buf[0]; + + /** + * Reset this component + */ + void reset_state() { + m_bytes_left = RXFIFO_SIZE; + m_timestamp_head = 0; + m_timestamp_size = 0; + m_missed_packets = 0; } - async event void RXFIFO.writeDone( uint8_t* tx_buf, uint8_t tx_len, error_t error ) {} } diff --git a/tos/chips/cc2420/CC2420SpiC.nc b/tos/chips/cc2420/CC2420SpiC.nc index 8d54674b..97885031 100644 --- a/tos/chips/cc2420/CC2420SpiC.nc +++ b/tos/chips/cc2420/CC2420SpiC.nc @@ -50,6 +50,8 @@ generic configuration CC2420SpiC() { provides interface CC2420Strobe as STXONCCA; provides interface CC2420Strobe as SXOSCON; provides interface CC2420Strobe as SXOSCOFF; + + provides interface CC2420Strobe as SACK; // registers provides interface CC2420Register as FSCTRL; @@ -57,8 +59,9 @@ generic configuration CC2420SpiC() { provides interface CC2420Register as IOCFG1; provides interface CC2420Register as MDMCTRL0; provides interface CC2420Register as MDMCTRL1; - provides interface CC2420Register as RXCTRL1; provides interface CC2420Register as TXCTRL; + provides interface CC2420Register as RXCTRL1; + provides interface CC2420Register as RSSI; // ram provides interface CC2420Ram as IEEEADR; @@ -93,6 +96,8 @@ implementation { STXONCCA = Spi.Strobe[ CC2420_STXONCCA ]; SXOSCON = Spi.Strobe[ CC2420_SXOSCON ]; SXOSCOFF = Spi.Strobe[ CC2420_SXOSCOFF ]; + + SACK = Spi.Strobe[ CC2420_SACK ]; // registers FSCTRL = Spi.Reg[ CC2420_FSCTRL ]; @@ -100,8 +105,9 @@ implementation { IOCFG1 = Spi.Reg[ CC2420_IOCFG1 ]; MDMCTRL0 = Spi.Reg[ CC2420_MDMCTRL0 ]; MDMCTRL1 = Spi.Reg[ CC2420_MDMCTRL1 ]; - RXCTRL1 = Spi.Reg[ CC2420_RXCTRL1 ]; TXCTRL = Spi.Reg[ CC2420_TXCTRL ]; + RXCTRL1 = Spi.Reg[ CC2420_RXCTRL1 ]; + RSSI = Spi.Reg[ CC2420_RSSI ]; // ram IEEEADR = Spi.Ram[ CC2420_RAM_IEEEADR ]; diff --git a/tos/chips/cc2420/CC2420SpiImplP.nc b/tos/chips/cc2420/CC2420SpiImplP.nc index 14a76006..4bc4540d 100644 --- a/tos/chips/cc2420/CC2420SpiImplP.nc +++ b/tos/chips/cc2420/CC2420SpiImplP.nc @@ -67,11 +67,11 @@ implementation { async command error_t Resource.request[ uint8_t id ]() { atomic { if ( m_resource_busy ) - m_requests |= 1 << id; + m_requests |= 1 << id; else { - m_holder = id; - m_resource_busy = TRUE; - call SpiResource.request(); + m_holder = id; + m_resource_busy = TRUE; + call SpiResource.request(); } } return SUCCESS; @@ -81,11 +81,11 @@ implementation { error_t error; atomic { if ( m_resource_busy ) - return EBUSY; + return EBUSY; error = call SpiResource.immediateRequest(); if ( error == SUCCESS ) { - m_holder = id; - m_resource_busy = TRUE; + m_holder = id; + m_resource_busy = TRUE; } } return error; @@ -94,24 +94,27 @@ implementation { async command error_t Resource.release[ uint8_t id ]() { uint8_t i; atomic { - if ( m_holder != id ) - return FAIL; + if ( m_holder != id ) { + return FAIL; + } + m_holder = NO_HOLDER; call SpiResource.release(); if ( !m_requests ) { - m_resource_busy = FALSE; - } - else { - for ( i = m_holder + 1; ; i++ ) { - if ( i >= RESOURCE_COUNT ) - i = 0; - if ( m_requests & ( 1 << i ) ) { - m_holder = i; - m_requests &= ~( 1 << i ); - call SpiResource.request(); - return SUCCESS; - } - } + m_resource_busy = FALSE; + } else { + for ( i = m_holder + 1; ; i++ ) { + if ( i >= RESOURCE_COUNT ) { + i = 0; + } + + if ( m_requests & ( 1 << i ) ) { + m_holder = i; + m_requests &= ~( 1 << i ); + call SpiResource.request(); + return SUCCESS; + } + } } return SUCCESS; } @@ -128,12 +131,18 @@ implementation { } async command cc2420_status_t Fifo.beginRead[ uint8_t addr ]( uint8_t* data, - uint8_t len ) { + uint8_t len ) { - cc2420_status_t status; + cc2420_status_t status = 0; + + atomic { + if(!m_resource_busy) { + return status; + } + } m_addr = addr | 0x40; - + status = call SpiByte.write( m_addr ); call Fifo.continueRead[ addr ]( data, len ); @@ -142,16 +151,22 @@ implementation { } async command error_t Fifo.continueRead[ uint8_t addr ]( uint8_t* data, - uint8_t len ) { + uint8_t len ) { call SpiPacket.send( NULL, data, len ); return SUCCESS; } async command cc2420_status_t Fifo.write[ uint8_t addr ]( uint8_t* data, - uint8_t len ) { - - uint8_t status; + uint8_t len ) { + uint8_t status = 0; + + atomic { + if(!m_resource_busy) { + return status; + } + } + m_addr = addr; status = call SpiByte.write( m_addr ); @@ -162,11 +177,17 @@ implementation { } async command cc2420_status_t Ram.read[ uint16_t addr ]( uint8_t offset, - uint8_t* data, - uint8_t len ) { + uint8_t* data, + uint8_t len ) { - cc2420_status_t status; + cc2420_status_t status = 0; + atomic { + if(!m_resource_busy) { + return status; + } + } + addr += offset; call SpiByte.write( addr | 0x80 ); @@ -179,7 +200,7 @@ implementation { } async event void SpiPacket.sendDone( uint8_t* tx_buf, uint8_t* rx_buf, - uint16_t len, error_t error ) { + uint16_t len, error_t error ) { if ( m_addr & 0x40 ) signal Fifo.readDone[ m_addr & ~0x40 ]( rx_buf, len, error ); else @@ -187,11 +208,17 @@ implementation { } async command cc2420_status_t Ram.write[ uint16_t addr ]( uint8_t offset, - uint8_t* data, - uint8_t len ) { + uint8_t* data, + uint8_t len ) { cc2420_status_t status = 0; + atomic { + if(!m_resource_busy) { + return status; + } + } + addr += offset; call SpiByte.write( addr | 0x80 ); @@ -205,7 +232,13 @@ implementation { async command cc2420_status_t Reg.read[ uint8_t addr ]( uint16_t* data ) { - cc2420_status_t status; + cc2420_status_t status = 0; + + atomic { + if(!m_resource_busy) { + return status; + } + } status = call SpiByte.write( addr | 0x40 ); *data = (uint16_t)call SpiByte.write( 0 ) << 8; @@ -216,6 +249,11 @@ implementation { } async command cc2420_status_t Reg.write[ uint8_t addr ]( uint16_t data ) { + atomic { + if(!m_resource_busy) { + return 0; + } + } call SpiByte.write( addr ); call SpiByte.write( data >> 8 ); @@ -224,6 +262,12 @@ implementation { } async command cc2420_status_t Strobe.strobe[ uint8_t addr ]() { + atomic { + if(!m_resource_busy) { + return 0; + } + } + return call SpiByte.write( addr ); } diff --git a/tos/chips/cc2420/CC2420TinyosNetworkC.nc b/tos/chips/cc2420/CC2420TinyosNetworkC.nc new file mode 100644 index 00000000..6abf1eac --- /dev/null +++ b/tos/chips/cc2420/CC2420TinyosNetworkC.nc @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Original TinyOS T-Frames use a packet header that is not compatible with + * other 6LowPAN networks. They do not include the network byte + * responsible for identifying the packing as being sourced from a TinyOS + * network. + * + * TinyOS I-Frames are interoperability packets that do include a network + * byte as defined by 6LowPAN specifications. The I-Frame header type is + * the default packet header used in TinyOS networks. + * + * Since either packet header is acceptable, this layer must do some + * preprocessing (sorry) to figure out whether or not it needs to include + * the functionality to process I-frames. If I-Frames are used, then + * the network byte is added on the way out and checked on the way in. + * If the packet came from a network different from a TinyOS network, the + * user may access it through the DispatchP's NonTinyosReceive[] Receive + * interface and process it in a different radio stack. + * + * If T-Frames are used instead, this layer is simply pass-through wiring to the + * layer beneath. + * + * Define "CC2420_IFRAME_TYPE" to use the interoperability frame and + * this layer + * + * @author David Moss + */ + +#include "CC2420.h" + +configuration CC2420TinyosNetworkC { + provides { + interface Send; + interface Receive; + } + + uses { + interface Receive as SubReceive; + interface Send as SubSend; + } +} + +implementation { + +#ifdef CC2420_IFRAME_TYPE + components CC2420TinyosNetworkP; + components CC2420PacketC; + + CC2420TinyosNetworkP.Send = Send; + CC2420TinyosNetworkP.Receive = Receive; + CC2420TinyosNetworkP.SubSend = SubSend; + CC2420TinyosNetworkP.SubReceive = SubReceive; + + CC2420TinyosNetworkP.CC2420Packet -> CC2420PacketC; + +#else + Send = SubSend; + Receive = SubReceive; +#endif + +} + diff --git a/tos/chips/cc2420/CC2420TinyosNetworkP.nc b/tos/chips/cc2420/CC2420TinyosNetworkP.nc new file mode 100644 index 00000000..18b3bc95 --- /dev/null +++ b/tos/chips/cc2420/CC2420TinyosNetworkP.nc @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Fills in the network ID byte for outgoing packets for compatibility with + * other 6LowPAN networks. Filters incoming packets that are not + * TinyOS network compatible. Provides the 6LowpanSnoop interface to + * sniff for packets that were not originated from TinyOS. + * + * @author David Moss + */ + +#include "CC2420.h" + +module CC2420TinyosNetworkP { + provides { + interface Send; + interface Receive; + + interface Receive as NonTinyosReceive[uint8_t networkId]; + } + + uses { + interface Send as SubSend; + interface Receive as SubReceive; + interface CC2420Packet; + } +} + +implementation { + + /***************** Send Commands ****************/ + command error_t Send.send(message_t* msg, uint8_t len) { + (call CC2420Packet.getHeader(msg))->network = TINYOS_6LOWPAN_NETWORK_ID; + return call SubSend.send(msg, len); + } + + command error_t Send.cancel(message_t* msg) { + return call SubSend.cancel(msg); + } + + command uint8_t Send.maxPayloadLength() { + return call SubSend.maxPayloadLength(); + } + + command void* Send.getPayload(message_t* msg) { + return call SubSend.getPayload(msg); + } + + /***************** Receive Commands ****************/ + command void* Receive.getPayload(message_t* msg, uint8_t* len) { + return call SubReceive.getPayload(msg, len); + } + + command uint8_t Receive.payloadLength(message_t* msg) { + return call SubReceive.payloadLength(msg); + } + + /***************** NonTinyosReceive Commands ****************/ + command void* NonTinyosReceive.getPayload[uint8_t networkId](message_t* msg, uint8_t* len) { + return call SubReceive.getPayload(msg, len); + } + + command uint8_t NonTinyosReceive.payloadLength[uint8_t networkId](message_t* msg) { + return call SubReceive.payloadLength(msg); + } + + /***************** SubSend Events *****************/ + event void SubSend.sendDone(message_t* msg, error_t error) { + signal Send.sendDone(msg, error); + } + + /***************** SubReceive Events ***************/ + event message_t *SubReceive.receive(message_t *msg, void *payload, uint8_t len) { + if((call CC2420Packet.getHeader(msg))->network == TINYOS_6LOWPAN_NETWORK_ID) { + return signal Receive.receive(msg, payload, len); + + } else { + return signal NonTinyosReceive.receive[(call CC2420Packet.getHeader(msg))->network](msg, payload, len); + } + } + + /***************** Defaults ****************/ + default event message_t *NonTinyosReceive.receive[uint8_t networkId](message_t *msg, void *payload, uint8_t len) { + return msg; + } + +} diff --git a/tos/chips/cc2420/CC2420Transmit.nc b/tos/chips/cc2420/CC2420Transmit.nc index e556f77a..d30a84b6 100644 --- a/tos/chips/cc2420/CC2420Transmit.nc +++ b/tos/chips/cc2420/CC2420Transmit.nc @@ -40,34 +40,20 @@ interface CC2420Transmit { /** - * Send a message with CCA enabled. + * Send a message * * @param p_msg message to send. + * @param useCca TRUE if this Tx should use clear channel assessments * @return SUCCESS if the request was accepted, FAIL otherwise. */ - async command error_t sendCCA( message_t* p_msg ); + async command error_t send( message_t* p_msg, bool useCca ); /** - * Send a message with CCA disabled. - * - * @param p_msg message to send. - * @return SUCCESS if the request was accepted, FAIL otherwise. - */ - async command error_t send( message_t* p_msg ); - - /** - * Send the previous message again with CCA enabled. - * - * @return SUCCESS if the request was accepted, FAIL otherwise. - */ - async command error_t resendCCA(); - - /** - * Send the previous message again with CCA disabled. - * + * Send the previous message again + * @param useCca TRUE if this re-Tx should use clear channel assessments * @return SUCCESS if the request was accepted, FAIL otherwise. */ - async command error_t resend(); + async command error_t resend(bool useCca); /** * Cancel sending of the message. diff --git a/tos/chips/cc2420/CC2420TransmitC.nc b/tos/chips/cc2420/CC2420TransmitC.nc index 5e076608..12405d42 100644 --- a/tos/chips/cc2420/CC2420TransmitC.nc +++ b/tos/chips/cc2420/CC2420TransmitC.nc @@ -36,26 +36,32 @@ * @version $Revision$ $Date$ */ -configuration CC2420TransmitC { +#include "IEEE802154.h" - provides interface Init; - provides interface AsyncStdControl; - provides interface CC2420Transmit; - provides interface CsmaBackoff; - provides interface RadioTimeStamping; +configuration CC2420TransmitC { + provides { + interface StdControl; + interface CC2420Transmit; + interface RadioBackoff; + interface RadioTimeStamping; + interface CC2420Cca; + } } implementation { components CC2420TransmitP; - Init = Alarm; - Init = CC2420TransmitP; - AsyncStdControl = CC2420TransmitP; + StdControl = CC2420TransmitP; CC2420Transmit = CC2420TransmitP; - CsmaBackoff = CC2420TransmitP; + RadioBackoff = CC2420TransmitP; RadioTimeStamping = CC2420TransmitP; + CC2420Cca = CC2420TransmitP; + components MainC; + MainC.SoftwareInit -> CC2420TransmitP; + MainC.SoftwareInit -> Alarm; + components AlarmMultiplexC as Alarm; CC2420TransmitP.BackoffTimer -> Alarm; @@ -76,11 +82,17 @@ implementation { CC2420TransmitP.TXCTRL -> Spi.TXCTRL; CC2420TransmitP.TXFIFO -> Spi.TXFIFO; CC2420TransmitP.TXFIFO_RAM -> Spi.TXFIFO_RAM; - + CC2420TransmitP.MDMCTRL1 -> Spi.MDMCTRL1; + components CC2420ReceiveC; CC2420TransmitP.CC2420Receive -> CC2420ReceiveC; - - components LedsC as Leds; - CC2420TransmitP.Leds -> Leds; - + + components new TimerMilliC() as LplDisableTimerC; + CC2420TransmitP.LplDisableTimer -> LplDisableTimerC; + + components CC2420PacketC; + CC2420TransmitP.CC2420Packet -> CC2420PacketC; + + components LedsC; + CC2420TransmitP.Leds -> LedsC; } diff --git a/tos/chips/cc2420/CC2420TransmitP.nc b/tos/chips/cc2420/CC2420TransmitP.nc index 9793949b..b1a43f60 100644 --- a/tos/chips/cc2420/CC2420TransmitP.nc +++ b/tos/chips/cc2420/CC2420TransmitP.nc @@ -31,18 +31,25 @@ /** * @author Jonathan Hui + * @author David Moss + * @author Jung Il Choi * @version $Revision$ $Date$ */ +#include "CC2420.h" +#include "crc.h" + module CC2420TransmitP { provides interface Init; - provides interface AsyncStdControl; + provides interface StdControl; provides interface CC2420Transmit as Send; - provides interface CsmaBackoff; + provides interface RadioBackoff; provides interface RadioTimeStamping as TimeStamp; - + provides interface CC2420Cca; + uses interface Alarm as BackoffTimer; + uses interface CC2420Packet; uses interface GpioCapture as CaptureSFD; uses interface GeneralIO as CCA; uses interface GeneralIO as CSN; @@ -56,10 +63,11 @@ module CC2420TransmitP { uses interface CC2420Strobe as STXON; uses interface CC2420Strobe as STXONCCA; uses interface CC2420Strobe as SFLUSHTX; + uses interface CC2420Register as MDMCTRL1; + uses interface Timer as LplDisableTimer; uses interface CC2420Receive; uses interface Leds; - } implementation { @@ -73,7 +81,9 @@ implementation { S_SFD, S_EFD, S_ACK_WAIT, - S_CANCEL, + S_LOAD_CANCEL, + S_TX_CANCEL, + S_CCA_CANCEL, } cc2420_transmit_state_t; // This specifies how many jiffies the stack should wait after a @@ -84,48 +94,49 @@ implementation { CC2420_ABORT_PERIOD = 320 }; - norace message_t* m_msg; + norace message_t *m_msg; + norace bool m_cca; + norace uint8_t m_tx_power; + cc2420_transmit_state_t m_state = S_STOPPED; + bool m_receiving = FALSE; + uint16_t m_prev_time; + + bool signalSendDone; + + /** TRUE if the current lpl tx is taking place with continuous modulation */ + norace bool continuousModulation; + + /** Total CCA checks that showed no activity before the NoAck LPL send */ + norace int8_t totalCcaChecks; + + /** The initial backoff period */ + norace uint16_t myInitialBackoff; + + /** The congestion backoff period */ + norace uint16_t myCongestionBackoff; + + /** The low power listening backoff period */ + norace uint16_t myLplBackoff; + + /***************** Prototypes ****************/ + error_t send( message_t *p_msg, bool cca ); + error_t resend( bool cca ); void loadTXFIFO(); void attemptSend(); - - cc2420_header_t* getHeader( message_t* msg ) { - return (cc2420_header_t*)( msg->data - sizeof( cc2420_header_t ) ); - } - - cc2420_metadata_t* getMetadata( message_t* msg ) { - return (cc2420_metadata_t*)msg->metadata; - } - - void startBackoffTimer(uint16_t time) { - call BackoffTimer.start(time); - } - - void stopBackoffTimer() { - call BackoffTimer.stop(); - } - - error_t acquireSpiResource() { - error_t error = call SpiResource.immediateRequest(); - if ( error != SUCCESS ) - call SpiResource.request(); - return error; - } - - error_t releaseSpiResource() { - return call SpiResource.release(); - } - - void signalDone( error_t err ) { - atomic m_state = S_STARTED; - signal Send.sendDone( m_msg, err ); - } - + void congestionBackoff(); + error_t acquireSpiResource(); + error_t releaseSpiResource(); + void signalDone( error_t err ); + + task void startLplTimer(); + + /***************** Init Commands *****************/ command error_t Init.init() { call CCA.makeInput(); call CSN.makeOutput(); @@ -133,7 +144,8 @@ implementation { return SUCCESS; } - async command error_t AsyncStdControl.start() { + /***************** StdControl Commands ****************/ + command error_t StdControl.start() { atomic { call CaptureSFD.captureRisingEdge(); m_state = S_STARTED; @@ -143,308 +155,620 @@ implementation { return SUCCESS; } - async command error_t AsyncStdControl.stop() { + command error_t StdControl.stop() { atomic { m_state = S_STOPPED; - stopBackoffTimer(); + call BackoffTimer.stop(); call CaptureSFD.disable(); + call SpiResource.release(); } return SUCCESS; } - error_t send( message_t* p_msg, bool cca ) { + /**************** Send Commands ****************/ + async command error_t Send.send( message_t* p_msg, bool useCca ) { + return send( p_msg, useCca ); + } + + async command error_t Send.resend(bool useCca) { + return resend( useCca ); + } + + async command error_t Send.cancel() { atomic { - if ( m_state != S_STARTED ) - return FAIL; - m_state = S_LOAD; - m_cca = cca; - m_msg = p_msg; + signalSendDone = FALSE; + switch( m_state ) { + case S_LOAD: + m_state = S_LOAD_CANCEL; + break; + + case S_SAMPLE_CCA: + m_state = S_CCA_CANCEL; + break; + + case S_BEGIN_TRANSMIT: + m_state = S_TX_CANCEL; + break; + + default: + // cancel not allowed while radio is busy transmitting + return FAIL; + } } - if ( acquireSpiResource() == SUCCESS ) - loadTXFIFO(); - return SUCCESS; - } - async command error_t Send.sendCCA( message_t* p_msg ) { - return send( p_msg, TRUE ); + async command error_t Send.modify( uint8_t offset, uint8_t* buf, + uint8_t len ) { + call CSN.clr(); + call TXFIFO_RAM.write( offset, buf, len ); + call CSN.set(); + return SUCCESS; } - async command error_t Send.send( message_t* p_msg ) { - return send( p_msg, FALSE ); + /***************** RadioBackoff Commands ****************/ + /** + * Must be called within a requestInitialBackoff event + * @param backoffTime the amount of time in some unspecified units to backoff + */ + async command void RadioBackoff.setInitialBackoff(uint16_t backoffTime) { + myInitialBackoff = backoffTime + 1; } - - error_t resend( bool cca ) { - - atomic { - if ( m_state != S_STARTED ) - return FAIL; - m_cca = cca; - m_state = cca ? S_SAMPLE_CCA : S_BEGIN_TRANSMIT; - } - - if ( m_cca ) { - startBackoffTimer( signal CsmaBackoff.initial( m_msg ) * - CC2420_BACKOFF_PERIOD ); - } - else if ( acquireSpiResource() == SUCCESS ) { - attemptSend(); - } - - return SUCCESS; + /** + * Must be called within a requestCongestionBackoff event + * @param backoffTime the amount of time in some unspecified units to backoff + */ + async command void RadioBackoff.setCongestionBackoff(uint16_t backoffTime) { + myCongestionBackoff = backoffTime + 1; } - async command error_t Send.resendCCA() { - return resend( TRUE ); + /** + * Must be called within a requestLplBackoff event + * @param backoffTime the amount of time in some unspecified units to backoff + */ + async command void RadioBackoff.setLplBackoff(uint16_t backoffTime) { + myLplBackoff = backoffTime + 1; } - - async command error_t Send.resend() { - return resend( FALSE ); + + async command void RadioBackoff.setCca(bool useCca) { } + + + + /***************** CC2420Cca Commands ****************/ + /** + * @return TRUE if the CCA pin shows a clear channel + */ + command bool CC2420Cca.isChannelClear() { + return call CCA.get(); + } + - async command error_t Send.cancel() { - - stopBackoffTimer(); - + /** + * The CaptureSFD event is actually an interrupt from the capture pin + * which is connected to timing circuitry and timer modules. This + * type of interrupt allows us to see what time (being some relative value) + * the event occurred, and lets us accurately timestamp our packets. This + * allows higher levels in our system to synchronize with other nodes. + * + * Because the SFD events can occur so quickly, and the interrupts go + * in both directions, we set up the interrupt but check the SFD pin to + * determine if that interrupt condition has already been met - meaning, + * we should fall through and continue executing code where that interrupt + * would have picked up and executed had our microcontroller been fast enough. + */ + async event void CaptureSFD.captured( uint16_t time ) { atomic { switch( m_state ) { - case S_LOAD: - m_state = S_CANCEL; - break; - case S_SAMPLE_CCA: case S_BEGIN_TRANSMIT: - m_state = S_STARTED; - break; + + case S_SFD: + call CaptureSFD.captureFallingEdge(); + signal TimeStamp.transmittedSFD( time, m_msg ); + releaseSpiResource(); + call BackoffTimer.stop(); + m_state = S_EFD; + if ( ( ( (call CC2420Packet.getHeader( m_msg ))->fcf >> IEEE154_FCF_FRAME_TYPE ) & 7 ) == IEEE154_TYPE_DATA ) { + (call CC2420Packet.getMetadata( m_msg ))->time = time; + } + + if ( call SFD.get() ) { + break; + } + /** Fall Through because the next interrupt was already received */ + + case S_EFD: + call CaptureSFD.captureRisingEdge(); + if ( (call CC2420Packet.getHeader( m_msg ))->fcf & ( 1 << IEEE154_FCF_ACK_REQ ) ) { + m_state = S_ACK_WAIT; + call BackoffTimer.start( CC2420_ACK_WAIT_DELAY ); + } else { + +#ifdef NOACK_LOW_POWER_LISTENING + if(continuousModulation) { + // Wait for the LplDisableTimer to fire before running signalDone() + return; + } +#endif + signalDone(SUCCESS); + } + + if ( !call SFD.get() ) { + break; + } + /** Fall Through because the next interrupt was already received */ + default: - // cancel not allowed while radio is busy transmitting - return FAIL; + if ( !m_receiving ) { + call CaptureSFD.captureFallingEdge(); + signal TimeStamp.receivedSFD( time ); + call CC2420Receive.sfd( time ); + m_receiving = TRUE; + m_prev_time = time; + if ( call SFD.get() ) { + // wait for the next interrupt before moving on + return; + } + } + + call CaptureSFD.captureRisingEdge(); + m_receiving = FALSE; + if ( time - m_prev_time < 10 ) { + call CC2420Receive.sfd_dropped(); + } + break; + } } + } - return SUCCESS; + /***************** CC2420Receive Events ****************/ + /** + * If the packet we just received was an ack that we were expecting, + * our send is complete. + */ + async event void CC2420Receive.receive( uint8_t type, message_t* ack_msg ) { + cc2420_header_t* ack_header; + cc2420_header_t* msg_header; + cc2420_metadata_t* msg_metadata; + uint8_t* ack_buf; + uint8_t length; + if ( type == IEEE154_TYPE_ACK ) { + ack_header = call CC2420Packet.getHeader( ack_msg ); + msg_header = call CC2420Packet.getHeader( m_msg ); + msg_metadata = call CC2420Packet.getMetadata( m_msg ); + ack_buf = (uint8_t *) ack_header; + length = ack_header->length; + + if ( m_state == S_ACK_WAIT && + msg_header->dsn == ack_header->dsn ) { + call BackoffTimer.stop(); + msg_metadata->ack = TRUE; + msg_metadata->rssi = ack_buf[ length - 1 ]; + msg_metadata->lqi = ack_buf[ length ] & 0x7f; + signalDone(SUCCESS); + } + } } - void loadTXFIFO() { - cc2420_header_t* header = getHeader( m_msg ); - uint8_t tx_power = getMetadata( m_msg )->tx_power; - if ( !tx_power ) - tx_power = CC2420_DEF_RFPOWER; - call CSN.clr(); - if ( m_tx_power != tx_power ) - call TXCTRL.write( ( 2 << CC2420_TXCTRL_TXMIXBUF_CUR ) | - ( 3 << CC2420_TXCTRL_PA_CURRENT ) | - ( 1 << CC2420_TXCTRL_RESERVED ) | - ( tx_power << CC2420_TXCTRL_PA_LEVEL ) ); - m_tx_power = tx_power; - call TXFIFO.write( (uint8_t*)header, header->length - 1 ); + /***************** SpiResource Events ****************/ + event void SpiResource.granted() { + uint8_t cur_state; + + atomic { + cur_state = m_state; + } + + switch( cur_state ) { + case S_LOAD: + loadTXFIFO(); + break; + + case S_BEGIN_TRANSMIT: + attemptSend(); + break; + + case S_LOAD_CANCEL: + case S_CCA_CANCEL: + case S_TX_CANCEL: + call CSN.clr(); + call SFLUSHTX.strobe(); + call CSN.set(); + releaseSpiResource(); + atomic { + if (signalSendDone) { + signalDone(ECANCEL); + } else { + m_state = S_STARTED; + } + } + break; + + default: + releaseSpiResource(); + break; + } } + /***************** TXFIFO Events ****************/ + /** + * The TXFIFO is used to load packets into the transmit buffer on the + * chip + */ async event void TXFIFO.writeDone( uint8_t* tx_buf, uint8_t tx_len, - error_t error ) { + error_t error ) { call CSN.set(); - - if ( m_state == S_CANCEL ) { - m_state = S_STARTED; - } - else if ( !m_cca ) { - m_state = S_BEGIN_TRANSMIT; + if ( m_state == S_LOAD_CANCEL ) { + atomic { + call CSN.clr(); + call SFLUSHTX.strobe(); + call CSN.set(); + } + releaseSpiResource(); + if (signalSendDone) { + signalDone(ECANCEL); + } else { + m_state = S_STARTED; + } + + } else if ( !m_cca ) { + atomic { + if (m_state == S_LOAD_CANCEL) { + m_state = S_TX_CANCEL; + } else { + m_state = S_BEGIN_TRANSMIT; + } + } attemptSend(); - } - else { + + } else { releaseSpiResource(); - m_state = S_SAMPLE_CCA; - startBackoffTimer( signal CsmaBackoff.initial( m_msg ) * - CC2420_BACKOFF_PERIOD ); + atomic { + if (m_state == S_LOAD_CANCEL) { + m_state = S_CCA_CANCEL; + } else { + m_state = S_SAMPLE_CCA; + } + } + + signal RadioBackoff.requestInitialBackoff(m_msg); + call BackoffTimer.start(myInitialBackoff); } - } - void congestionBackoff() { - atomic { - uint16_t time = signal CsmaBackoff.congestion( m_msg ); - if ( time ) - startBackoffTimer( time * CC2420_BACKOFF_PERIOD ); - else - m_state = S_STARTED; - } + + async event void TXFIFO.readDone( uint8_t* tx_buf, uint8_t tx_len, + error_t error ) { } - + + + /***************** Timer Events ****************/ + /** + * The backoff timer is mainly used to wait for a moment before trying + * to send a packet again. But we also use it to timeout the wait for + * an acknowledgement, and timeout the wait for an SFD interrupt when + * we should have gotten one. + */ async event void BackoffTimer.fired() { - atomic { switch( m_state ) { - - case S_SAMPLE_CCA : - // sample CCA and wait a little longer if free, just in case we - // sampled during the ack turn-around window - if ( call CCA.get() ) { - m_state = S_BEGIN_TRANSMIT; - startBackoffTimer( CC2420_TIME_ACK_TURNAROUND ); - } - else { - congestionBackoff(); - } - break; - - case S_BEGIN_TRANSMIT : - if ( acquireSpiResource() == SUCCESS ) - attemptSend(); - break; - - case S_ACK_WAIT : - signalDone( SUCCESS ); - break; - -#ifdef PLATFORM_MICAZ + + case S_SAMPLE_CCA : + // sample CCA and wait a little longer if free, just in case we + // sampled during the ack turn-around window + if ( call CCA.get() ) { + m_state = S_BEGIN_TRANSMIT; + call BackoffTimer.start( CC2420_TIME_ACK_TURNAROUND ); + + } else { + congestionBackoff(); + } + break; + + case S_CCA_CANCEL: + m_state = S_TX_CANCEL; + /** Fall Through */ + + case S_BEGIN_TRANSMIT: + case S_TX_CANCEL: + if ( acquireSpiResource() == SUCCESS ) { + attemptSend(); + } + break; + + case S_ACK_WAIT: + signalDone( SUCCESS ); + break; + case S_SFD: - // We didn't receive an SFD interrupt within CC2420_ABORT_PERIOD - // jiffies. Assume something is wrong. - call SFLUSHTX.strobe(); - call CaptureSFD.disable(); - call CaptureSFD.captureRisingEdge(); - signalDone( ERETRY ); - break; -#endif + // We didn't receive an SFD interrupt within CC2420_ABORT_PERIOD + // jiffies. Assume something is wrong. + call SFLUSHTX.strobe(); + call CaptureSFD.captureRisingEdge(); + releaseSpiResource(); + signalDone( ERETRY ); + break; + default: - break; + break; + } + } + } + + event void LplDisableTimer.fired() { + call MDMCTRL1.write(0 << CC2420_MDMCTRL1_TX_MODE); + signalDone( SUCCESS ); + } + + /***************** Functions ****************/ + /** + * Set up a message to be sent. First load it into the outbound tx buffer + * on the chip, then attempt to send it. + * @param *p_msg Pointer to the message that needs to be sent + * @param cca TRUE if this transmit should use clear channel assessment + */ + error_t send( message_t* p_msg, bool cca ) { + atomic { + if (m_state == S_LOAD_CANCEL + || m_state == S_CCA_CANCEL + || m_state == S_TX_CANCEL) { + return ECANCEL; + } + + if ( m_state != S_STARTED ) { + return FAIL; } + + m_state = S_LOAD; + m_cca = cca; + m_msg = p_msg; + totalCcaChecks = 0; + } + + if ( acquireSpiResource() == SUCCESS ) { + loadTXFIFO(); } + return SUCCESS; } - void attemptSend() { + /** + * Resend a packet that already exists in the outbound tx buffer on the + * chip + * @param cca TRUE if this transmit should use clear channel assessment + */ + error_t resend( bool cca ) { + atomic { + if (m_state == S_LOAD_CANCEL + || m_state == S_CCA_CANCEL + || m_state == S_TX_CANCEL) { + return ECANCEL; + } + + if ( m_state != S_STARTED ) { + return FAIL; + } + + m_cca = cca; + m_state = cca ? S_SAMPLE_CCA : S_BEGIN_TRANSMIT; + totalCcaChecks = 0; + } + + if(m_cca) { + if((call CC2420Packet.getMetadata( m_msg ))->rxInterval > 0) { + signal RadioBackoff.requestLplBackoff(m_msg); + call BackoffTimer.start( myLplBackoff ); + + } else { + signal RadioBackoff.requestInitialBackoff(m_msg); + call BackoffTimer.start( myInitialBackoff ); + } + + } else if ( acquireSpiResource() == SUCCESS ) { + attemptSend(); + } + + return SUCCESS; + } + + /** + * Attempt to send the packet we have loaded into the tx buffer on + * the radio chip. The STXONCCA will send the packet immediately if + * the channel is clear. If we're not concerned about whether or not + * the channel is clear (i.e. m_cca == FALSE), then STXON will send the + * packet without checking for a clear channel. + * + * If the packet didn't get sent, then congestion == TRUE. In that case, + * we reset the backoff timer and try again in a moment. + * + * If the packet got sent, we should expect an SFD interrupt to take + * over, signifying the packet is getting sent. + */ + void attemptSend() { uint8_t status; bool congestion = TRUE; + + continuousModulation = FALSE; + +#ifdef NOACK_LOW_POWER_LISTENING + // When the message is being sent with LPL, CCA implies this is the first + // message. Send it using the continuous modulation delivery + continuousModulation = m_cca + && (call CC2420Packet.getMetadata( m_msg ))->rxInterval > 0; +#endif - call CSN.clr(); + atomic { + if (m_state == S_TX_CANCEL) { + call SFLUSHTX.strobe(); + releaseSpiResource(); + call CSN.set(); + if (signalSendDone) { + signalDone(ECANCEL); + } else { + m_state = S_STARTED; + } + return; + } + + + call CSN.clr(); + + if(continuousModulation) { + /* + * We do a complex backoff like this because the last message of a + * continuous modulation delivery is currently reloaded into the + * SPI bus and sent off without CCA. This leaves a small quiet gap + * between the continuous modulation and its last message that other + * transmitters are guaranteed to hit. + * + * In the future, when the CRC is built into the continuous delivery + * message, this complex backoff may not be needed. Alternatively, + * directly accessing the TXFIFO RAM and adjusting the FCF to + * request an acknowledgement, and then resend without CCA will + * decrease the quiet gap. + */ + if(totalCcaChecks < 20) { + if(call CCA.get()) { + // Extended backoff + totalCcaChecks++; + } else { + // Saw another transmitter, start over + totalCcaChecks = 0; + } + + call CSN.set(); + releaseSpiResource(); + signal RadioBackoff.requestInitialBackoff(m_msg); + call BackoffTimer.start( myInitialBackoff ); + return; + } + + call MDMCTRL1.write(2 << CC2420_MDMCTRL1_TX_MODE); + } else { + call MDMCTRL1.write(0 << CC2420_MDMCTRL1_TX_MODE); + } - status = m_cca ? call STXONCCA.strobe() : call STXON.strobe(); - if ( !( status & CC2420_STATUS_TX_ACTIVE ) ) { - status = call SNOP.strobe(); - if ( status & CC2420_STATUS_TX_ACTIVE ) - congestion = FALSE; + status = m_cca ? call STXONCCA.strobe() : call STXON.strobe(); + if ( !( status & CC2420_STATUS_TX_ACTIVE ) ) { + status = call SNOP.strobe(); + if ( status & CC2420_STATUS_TX_ACTIVE ) { + congestion = FALSE; + + if(continuousModulation) { + post startLplTimer(); + } + } + } + + m_state = congestion ? S_SAMPLE_CCA : S_SFD; + call CSN.set(); } - atomic m_state = congestion ? S_SAMPLE_CCA : S_SFD; - call CSN.set(); - if ( congestion ) { + totalCcaChecks = 0; releaseSpiResource(); congestionBackoff(); + } else { + call BackoffTimer.start(CC2420_ABORT_PERIOD); } -#ifdef PLATFORM_MICAZ - else { - startBackoffTimer(CC2420_ABORT_PERIOD); - } -#endif - } - - async command error_t Send.modify( uint8_t offset, uint8_t* buf, - uint8_t len ) { - call CSN.clr(); - call TXFIFO_RAM.write( offset, buf, len ); - call CSN.set(); - return SUCCESS; - } - - async event void CaptureSFD.captured( uint16_t time ) { - + + + /** + * Congestion Backoff + */ + void congestionBackoff() { atomic { - switch( m_state ) { - - case S_SFD: - call CaptureSFD.captureFallingEdge(); - signal TimeStamp.transmittedSFD( time, m_msg ); - releaseSpiResource(); - stopBackoffTimer(); - m_state = S_EFD; - if ( ( ( getHeader( m_msg )->fcf >> IEEE154_FCF_FRAME_TYPE ) & 7 ) == - IEEE154_TYPE_DATA ) - getMetadata( m_msg )->time = time; - if ( call SFD.get() ) - break; - - case S_EFD: - call CaptureSFD.captureRisingEdge(); - if ( getHeader( m_msg )->fcf & ( 1 << IEEE154_FCF_ACK_REQ ) ) { - m_state = S_ACK_WAIT; - startBackoffTimer( CC2420_ACK_WAIT_DELAY ); - } - else { - signalDone(SUCCESS); - } - if ( !call SFD.get() ) - break; - - default: - if ( !m_receiving ) { - call CaptureSFD.captureFallingEdge(); - signal TimeStamp.receivedSFD( time ); - call CC2420Receive.sfd( time ); - m_receiving = TRUE; - m_prev_time = time; - if ( call SFD.get() ) - return; - } - if ( m_receiving ) { - call CaptureSFD.captureRisingEdge(); - m_receiving = FALSE; - if ( time - m_prev_time < 10 ) - call CC2420Receive.sfd_dropped(); - } - break; - + if((call CC2420Packet.getMetadata(m_msg))->rxInterval > 0) { + signal RadioBackoff.requestLplBackoff(m_msg); + call BackoffTimer.start(myLplBackoff); + + } else { + signal RadioBackoff.requestCongestionBackoff(m_msg); + call BackoffTimer.start(myCongestionBackoff); } } } - - async event void CC2420Receive.receive( uint8_t type, message_t* ack_msg ) { - - if ( type == IEEE154_TYPE_ACK ) { - cc2420_header_t* ack_header = getHeader( ack_msg ); - cc2420_header_t* msg_header = getHeader( m_msg ); - cc2420_metadata_t* msg_metadata = getMetadata( m_msg ); - uint8_t* ack_buf = (uint8_t*)ack_header; - uint8_t length = ack_header->length; - - if ( m_state == S_ACK_WAIT && - msg_header->dsn == ack_header->dsn ) { - stopBackoffTimer(); - msg_metadata->ack = TRUE; - msg_metadata->rssi = ack_buf[ length - 1 ]; - msg_metadata->lqi = ack_buf[ length ] & 0x7f; - signalDone(SUCCESS); - } + + error_t acquireSpiResource() { + error_t error = call SpiResource.immediateRequest(); + if ( error != SUCCESS ) { + call SpiResource.request(); } - + return error; } - event void SpiResource.granted() { + error_t releaseSpiResource() { + call SpiResource.release(); + return SUCCESS; + } - uint8_t cur_state; - atomic { - cur_state = m_state; + /** + * Setup the packet transmission power and load the tx fifo buffer on + * the chip with our outbound packet. + * + * Warning: the tx_power metadata might not be initialized and + * could be a value other than 0 on boot. Verification is needed here + * to make sure the value won't overstep its bounds in the TXCTRL register + * and is transmitting at max power by default. + * + * It should be possible to manually calculate the packet's CRC here and + * tack it onto the end of the header + payload when loading into the TXFIFO, + * so the continuous modulation low power listening strategy will continually + * deliver valid packets. This would increase receive reliability for + * mobile nodes and lossy connections. The crcByte() function should use + * the same CRC polynomial as the CC2420's AUTOCRC functionality. + */ + void loadTXFIFO() { + cc2420_header_t* header = call CC2420Packet.getHeader( m_msg ); + uint8_t tx_power = (call CC2420Packet.getMetadata( m_msg ))->tx_power; + + if ( !tx_power ) { + // If our packet's tx_power wasn't configured to anything but 0, + // send it using the default RF power. This assumes the + // packet's metadata is all set to 0's on boot. + + tx_power = CC2420_DEF_RFPOWER; } - - switch( cur_state ) { - case S_LOAD: loadTXFIFO(); break; - case S_BEGIN_TRANSMIT: attemptSend(); break; - default: releaseSpiResource(); break; + + call CSN.clr(); + + if ( m_tx_power != tx_power ) { + call TXCTRL.write( ( 2 << CC2420_TXCTRL_TXMIXBUF_CUR ) | + ( 3 << CC2420_TXCTRL_PA_CURRENT ) | + ( 1 << CC2420_TXCTRL_RESERVED ) | + ( (tx_power & 0x1F) << CC2420_TXCTRL_PA_LEVEL ) ); } - + + m_tx_power = tx_power; + + call TXFIFO.write( (uint8_t*)header, header->length - 1 ); + } + + void signalDone( error_t err ) { + atomic m_state = S_STARTED; + signal Send.sendDone( m_msg, err ); + } + + + + /***************** Tasks ****************/ + task void startLplTimer() { + call LplDisableTimer.startOneShot((call CC2420Packet.getMetadata( m_msg ))->rxInterval + 10); } - async event void TXFIFO.readDone( uint8_t* tx_buf, uint8_t tx_len, error_t error ) {} - - default async event void TimeStamp.transmittedSFD( uint16_t time, message_t* p_msg ) {} - default async event void TimeStamp.receivedSFD( uint16_t time ) {} + /***************** Defaults ****************/ + default async event void TimeStamp.transmittedSFD( uint16_t time, message_t* p_msg ) { + } + + default async event void TimeStamp.receivedSFD( uint16_t time ) { + } } + diff --git a/tos/chips/cc2420/PacketLink.nc b/tos/chips/cc2420/PacketLink.nc new file mode 100644 index 00000000..0b8e54ae --- /dev/null +++ b/tos/chips/cc2420/PacketLink.nc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * @author David Moss + * @author Jon Wyant + */ + +interface PacketLink { + + /** + * Set the maximum number of times attempt message delivery + * Default is 0 + * @param msg + * @param maxRetries the maximum number of attempts to deliver + * the message + */ + command void setRetries(message_t *msg, uint16_t maxRetries); + + /** + * Set a delay between each retry attempt + * @param msg + * @param retryDelay the delay betweeen retry attempts, in milliseconds + */ + command void setRetryDelay(message_t *msg, uint16_t retryDelay); + + /** + * @return the maximum number of retry attempts for this message + */ + command uint16_t getRetries(message_t *msg); + + /** + * @return the delay between retry attempts in ms for this message + */ + command uint16_t getRetryDelay(message_t *msg); + + /** + * @return TRUE if the message was delivered. + */ + command bool wasDelivered(message_t *msg); + +} + + diff --git a/tos/chips/cc2420/PacketLinkC.nc b/tos/chips/cc2420/PacketLinkC.nc new file mode 100644 index 00000000..2b5280c6 --- /dev/null +++ b/tos/chips/cc2420/PacketLinkC.nc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Reliable Packet Link Functionality + * @author David Moss + * @author Jon Wyant + */ + +#warning "*** USING PACKET LINK LAYER" + +configuration PacketLinkC { + provides { + interface Send; + interface PacketLink; + } + + uses { + interface Send as SubSend; + } +} + +implementation { + + components PacketLinkP, + ActiveMessageC, + CC2420PacketC, + RandomC, + new StateC() as SendStateC, + new TimerMilliC() as DelayTimerC; + + PacketLink = PacketLinkP; + Send = PacketLinkP.Send; + SubSend = PacketLinkP.SubSend; + + PacketLinkP.SendState -> SendStateC; + PacketLinkP.DelayTimer -> DelayTimerC; + PacketLinkP.PacketAcknowledgements -> ActiveMessageC; + PacketLinkP.AMPacket -> ActiveMessageC; + PacketLinkP.CC2420Packet -> CC2420PacketC; + +} diff --git a/tos/chips/cc2420/PacketLinkDummyC.nc b/tos/chips/cc2420/PacketLinkDummyC.nc new file mode 100644 index 00000000..2ea159ce --- /dev/null +++ b/tos/chips/cc2420/PacketLinkDummyC.nc @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Dummy configuration for PacketLink Layer + * @author David Moss + * @author Jon Wyant + */ + +configuration PacketLinkDummyC { + provides { + interface Send; + interface PacketLink; + } + + uses { + interface Send as SubSend; + } +} + +implementation { + components PacketLinkDummyP, + ActiveMessageC; + + PacketLink = PacketLinkDummyP; + Send = SubSend; + + PacketLinkDummyP.PacketAcknowledgements -> ActiveMessageC; + +} + diff --git a/tos/chips/cc2420/PacketLinkDummyP.nc b/tos/chips/cc2420/PacketLinkDummyP.nc new file mode 100644 index 00000000..1688da67 --- /dev/null +++ b/tos/chips/cc2420/PacketLinkDummyP.nc @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Dummy module for Packet Link layer + * @author David Moss + * @author Jon Wyant + */ + +module PacketLinkDummyP { + provides { + interface PacketLink; + } + + uses { + interface PacketAcknowledgements; + } +} + +implementation { + + /***************** PacketLink Commands ***************/ + /** + * Set the maximum number of times attempt message delivery + * Default is 0 + * @param msg + * @param maxRetries the maximum number of attempts to deliver + * the message + */ + command void PacketLink.setRetries(message_t *msg, uint16_t maxRetries) { + } + + /** + * Set a delay between each retry attempt + * @param msg + * @param retryDelay the delay betweeen retry attempts, in milliseconds + */ + command void PacketLink.setRetryDelay(message_t *msg, uint16_t retryDelay) { + } + + /** + * @return the maximum number of retry attempts for this message + */ + command uint16_t PacketLink.getRetries(message_t *msg) { + return 0; + } + + /** + * @return the delay between retry attempts in ms for this message + */ + command uint16_t PacketLink.getRetryDelay(message_t *msg) { + return 0; + } + + /** + * @return TRUE if the message was delivered. + * This should always be TRUE if the message was sent to the + * AM_BROADCAST_ADDR + */ + command bool PacketLink.wasDelivered(message_t *msg) { + return call PacketAcknowledgements.wasAcked(msg); + } +} + diff --git a/tos/chips/cc2420/PacketLinkP.nc b/tos/chips/cc2420/PacketLinkP.nc new file mode 100644 index 00000000..cccd212b --- /dev/null +++ b/tos/chips/cc2420/PacketLinkP.nc @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Reliable Packet Link Functionality + * @author David Moss + * @author Jon Wyant + */ + +#include "CC2420.h" + +module PacketLinkP { + provides { + interface Send; + interface PacketLink; + } + + uses { + interface Send as SubSend; + interface State as SendState; + interface PacketAcknowledgements; + interface Timer as DelayTimer; + interface AMPacket; + interface CC2420Packet; + } +} + +implementation { + + /** The message currently being sent */ + message_t *currentSendMsg; + + /** Length of the current send message */ + uint8_t currentSendLen; + + /** The length of the current send message */ + uint16_t totalRetries; + + + /** + * Send States + */ + enum { + S_IDLE, + S_SENDING, + }; + + + /***************** Prototypes ***************/ + task void send(); + void signalDone(error_t error); + + /***************** PacketLink Commands ***************/ + /** + * Set the maximum number of times attempt message delivery + * Default is 0 + * @param msg + * @param maxRetries the maximum number of attempts to deliver + * the message + */ + command void PacketLink.setRetries(message_t *msg, uint16_t maxRetries) { + (call CC2420Packet.getMetadata(msg))->maxRetries = maxRetries; + } + + /** + * Set a delay between each retry attempt + * @param msg + * @param retryDelay the delay betweeen retry attempts, in milliseconds + */ + command void PacketLink.setRetryDelay(message_t *msg, uint16_t retryDelay) { + (call CC2420Packet.getMetadata(msg))->retryDelay = retryDelay; + } + + /** + * @return the maximum number of retry attempts for this message + */ + command uint16_t PacketLink.getRetries(message_t *msg) { + return (call CC2420Packet.getMetadata(msg))->maxRetries; + } + + /** + * @return the delay between retry attempts in ms for this message + */ + command uint16_t PacketLink.getRetryDelay(message_t *msg) { + return (call CC2420Packet.getMetadata(msg))->retryDelay; + } + + /** + * @return TRUE if the message was delivered. + */ + command bool PacketLink.wasDelivered(message_t *msg) { + return call PacketAcknowledgements.wasAcked(msg); + } + + /***************** Send Commands ***************/ + /** + * Each call to this send command gives the message a single + * DSN that does not change for every copy of the message + * sent out. For messages that are not acknowledged, such as + * a broadcast address message, the receiving end does not + * signal receive() more than once for that message. + */ + command error_t Send.send(message_t *msg, uint8_t len) { + error_t error; + if(call SendState.requestState(S_SENDING) == SUCCESS) { + + currentSendMsg = msg; + currentSendLen = len; + totalRetries = 0; + + if(call PacketLink.getRetries(msg) > 0) { + call PacketAcknowledgements.requestAck(msg); + } + + if((error = call SubSend.send(msg, len)) != SUCCESS) { + call SendState.toIdle(); + } + + return error; + } + + return EBUSY; + } + + command error_t Send.cancel(message_t *msg) { + if(currentSendMsg == msg) { + call SendState.toIdle(); + return call SubSend.cancel(msg); + } + + return FAIL; + } + + + command uint8_t Send.maxPayloadLength() { + return call SubSend.maxPayloadLength(); + } + + command void *Send.getPayload(message_t* msg) { + return call SubSend.getPayload(msg); + } + + + /***************** SubSend Events ***************/ + event void SubSend.sendDone(message_t* msg, error_t error) { + if(call SendState.getState() == S_SENDING) { + totalRetries++; + if(call PacketAcknowledgements.wasAcked(msg)) { + signalDone(SUCCESS); + return; + + } else if(totalRetries < call PacketLink.getRetries(currentSendMsg)) { + + if(call PacketLink.getRetryDelay(currentSendMsg) > 0) { + // Resend after some delay + call DelayTimer.startOneShot(call PacketLink.getRetryDelay(currentSendMsg)); + + } else { + // Resend immediately + post send(); + } + + return; + } + } + + signalDone(error); + } + + + /***************** Timer Events ****************/ + /** + * When this timer is running, that means we're sending repeating messages + * to a node that is receive check duty cycling. + */ + event void DelayTimer.fired() { + if(call SendState.getState() == S_SENDING) { + post send(); + } + } + + /***************** Tasks ***************/ + task void send() { + if(call PacketLink.getRetries(currentSendMsg) > 0) { + call PacketAcknowledgements.requestAck(currentSendMsg); + } + + if(call SubSend.send(currentSendMsg, currentSendLen) != SUCCESS) { + post send(); + } + } + + /***************** Functions ***************/ + void signalDone(error_t error) { + call DelayTimer.stop(); + call SendState.toIdle(); + signal Send.sendDone(currentSendMsg, error); + } +} + diff --git a/tos/chips/cc2420/README.txt b/tos/chips/cc2420/README.txt new file mode 100644 index 00000000..518940d3 --- /dev/null +++ b/tos/chips/cc2420/README.txt @@ -0,0 +1,45 @@ + +CC2420 Release Notes 4/11/07 + +Known Issues +__________________________________________ + > LPL Lockups when the node is also accessing the USART + > NoAck LPL doesn't ack at the end properly, and also isn't + finished being implemented. The CRC of the packet needs to + manually be loaded into TXFIFO before continuous modulation. + + +Low Power Listening Schemes and Preprocessor Variables +__________________________________________ +There are two low power listening schemes. +The default is called "AckLpl", because it inserts acknowledgement gaps and +short backoffs during the packet retransmission process. +This allows the transmitter to stop transmitting early, but increases the +power consumption per receive check. + +The second is called "NoAckLpl", because it does not insert acknowledgement +gaps or backoffs in the retransmission process, so the receive checks are +shorter but the transmissions are longer. This is more experimental than +the Ack LPL version. + +To compile in the default Ack LPL version, #define the preprocessor variable: + + LOW_POWER_LISTENING + -or- + ACK_LOW_POWER_LISTENING + + +To compile in the NoAck LPL version, #define: + + NOACK_LOW_POWER_LISTENING + + +To compile in the PacketLink layer, #define: + + PACKET_LINK + + +To remove SACK auto-acknowledgements, #define: + + CC2420_NO_ACKNOWLEDGEMENTS + diff --git a/tos/chips/cc2420/RadioBackoff.nc b/tos/chips/cc2420/RadioBackoff.nc new file mode 100644 index 00000000..98d63826 --- /dev/null +++ b/tos/chips/cc2420/RadioBackoff.nc @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Interface to request and specify backoff periods for messages + * @author David Moss + */ + +interface RadioBackoff { + + /** + * Must be called within a requestInitialBackoff event + * @param backoffTime the amount of time in some unspecified units to backoff + */ + async command void setInitialBackoff(uint16_t backoffTime); + + /** + * Must be called within a requestCongestionBackoff event + * @param backoffTime the amount of time in some unspecified units to backoff + */ + async command void setCongestionBackoff(uint16_t backoffTime); + + + /** + * Must be called within a requestLplBackoff event + * @param backoffTime the amount of time in some unspecified units to backoff + */ + async command void setLplBackoff(uint16_t backoffTime); + + + /** + * Enable CCA for the outbound packet. Must be called within a requestCca + * event + * @param ccaOn TRUE to enable CCA, which is the default. + */ + async command void setCca(bool ccaOn); + + + /** + * Request for input on the initial backoff + * Reply using setInitialBackoff(..) + * @param msg pointer to the message being sent + */ + async event void requestInitialBackoff(message_t *msg); + + /** + * Request for input on the congestion backoff + * Reply using setCongestionBackoff(..) + * @param msg pointer to the message being sent + */ + async event void requestCongestionBackoff(message_t *msg); + + /** + * Request for input on the low power listening backoff + * This should be somewhat random, but as short as possible + * Reply using setLplBackoff(..) + * @param msg pointer to the message being sent + */ + async event void requestLplBackoff(message_t *msg); + + /** + * Request for input on whether or not to use CCA on the outbound packet. + * Replies should come in the form of setCca(..) + * @param msg pointer to the message being sent + */ + async event void requestCca(message_t *msg); +} + diff --git a/tos/chips/cc2420/UniqueReceiveC.nc b/tos/chips/cc2420/UniqueReceiveC.nc new file mode 100644 index 00000000..2a8dd9ac --- /dev/null +++ b/tos/chips/cc2420/UniqueReceiveC.nc @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * This layer keeps a history of the past RECEIVE_HISTORY_SIZE received messages + * If the source address and dsn number of a newly received message matches + * our recent history, we drop the message because we've already seen it. + * This should sit at the bottom of the stack + * @author David Moss + */ + +configuration UniqueReceiveC { + provides { + interface Receive; + interface Receive as DuplicateReceive; + } + + uses { + interface Receive as SubReceive; + } +} + +implementation { + components UniqueReceiveP, + CC2420PacketC, + MainC; + + Receive = UniqueReceiveP.Receive; + DuplicateReceive = UniqueReceiveP.DuplicateReceive; + SubReceive = UniqueReceiveP.SubReceive; + + MainC.SoftwareInit -> UniqueReceiveP; + + UniqueReceiveP.CC2420Packet -> CC2420PacketC; + +} + diff --git a/tos/chips/cc2420/UniqueReceiveP.nc b/tos/chips/cc2420/UniqueReceiveP.nc new file mode 100644 index 00000000..fb63ec68 --- /dev/null +++ b/tos/chips/cc2420/UniqueReceiveP.nc @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * This layer keeps a history of the past RECEIVE_HISTORY_SIZE received messages + * If the source address and dsn number of a newly received message matches + * our recent history, we drop the message because we've already seen it. + * @author David Moss + */ + +#include "CC2420.h" + +module UniqueReceiveP { + provides { + interface Receive; + interface Receive as DuplicateReceive; + interface Init; + } + + uses { + interface Receive as SubReceive; + interface CC2420Packet; + } +} + +implementation { + + struct { + am_addr_t source; + uint8_t dsn; + } receivedMessages[RECEIVE_HISTORY_SIZE]; + + uint8_t writeIndex = 0; + + /** History element containing info on a source previously received from */ + uint8_t recycleSourceElement; + + enum { + INVALID_ELEMENT = 0xFF, + }; + + /***************** Init Commands *****************/ + command error_t Init.init() { + int i; + for(i = 0; i < RECEIVE_HISTORY_SIZE; i++) { + receivedMessages[i].source = (am_addr_t) 0xFFFF; + receivedMessages[i].dsn = 0; + } + return SUCCESS; + } + + /***************** Prototypes Commands ***************/ + bool hasSeen(uint16_t msgSource, uint8_t msgDsn); + void insert(uint16_t msgSource, uint8_t msgDsn); + + /***************** Receive Commands ***************/ + command void *Receive.getPayload(message_t* msg, uint8_t* len) { + return call SubReceive.getPayload(msg, len); + } + + command uint8_t Receive.payloadLength(message_t* msg) { + return call SubReceive.payloadLength(msg); + } + + + /***************** DuplicateReceive Commands ****************/ + command void *DuplicateReceive.getPayload(message_t* msg, uint8_t* len) { + return call SubReceive.getPayload(msg, len); + } + + command uint8_t DuplicateReceive.payloadLength(message_t* msg) { + return call SubReceive.payloadLength(msg); + } + + /***************** SubReceive Events *****************/ + event message_t *SubReceive.receive(message_t* msg, void* payload, + uint8_t len) { + uint16_t msgSource = (call CC2420Packet.getHeader(msg))->src; + uint8_t msgDsn = (call CC2420Packet.getHeader(msg))->dsn; + + if(hasSeen(msgSource, msgDsn)) { + return signal DuplicateReceive.receive(msg, payload, len); + + } else { + insert(msgSource, msgDsn); + return signal Receive.receive(msg, payload, len); + } + } + + /****************** Functions ****************/ + /** + * This function does two things: + * 1. It loops through our entire receive history and detects if we've + * seen this DSN before from the given source (duplicate packet) + * 2. It detects if we've seen messages from this source before, so we know + * where to update our history if it turns out this is a new message. + * + * The global recycleSourceElement variable stores the location of the next insert + * if we've received a packet from that source before. Otherwise, it's up + * to the insert() function to decide who to kick out of our history. + */ + bool hasSeen(uint16_t msgSource, uint8_t msgDsn) { + int i; + recycleSourceElement = INVALID_ELEMENT; + + atomic { + for(i = 0; i < RECEIVE_HISTORY_SIZE; i++) { + if(receivedMessages[i].source == msgSource) { + if(receivedMessages[i].dsn == msgDsn) { + // Only exit this loop if we found a duplicate packet + return TRUE; + } + + recycleSourceElement = i; + } + } + } + + return FALSE; + } + + /** + * Insert the message into the history. If we received a message from this + * source before, insert it into the same location as last time and verify + * that the "writeIndex" is not pointing to that location. Otherwise, + * insert it into the "writeIndex" location. + */ + void insert(uint16_t msgSource, uint8_t msgDsn) { + uint8_t element = recycleSourceElement; + bool increment = FALSE; + + atomic { + if(element == INVALID_ELEMENT || writeIndex == element) { + // Use the writeIndex element to insert this new message into + element = writeIndex; + increment = TRUE; + } + + receivedMessages[element].source = msgSource; + receivedMessages[element].dsn = msgDsn; + if(increment) { + writeIndex++; + writeIndex %= RECEIVE_HISTORY_SIZE; + } + } + } + + /***************** Defaults ****************/ + default event message_t *DuplicateReceive.receive(message_t *msg, void *payload, uint8_t len) { + return msg; + } +} + diff --git a/tos/chips/cc2420/UniqueSendC.nc b/tos/chips/cc2420/UniqueSendC.nc new file mode 100644 index 00000000..a2cbcefd --- /dev/null +++ b/tos/chips/cc2420/UniqueSendC.nc @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Generate a unique dsn byte for this outgoing packet + * This should sit at the top of the stack + * @author David Moss + */ + +configuration UniqueSendC { + provides { + interface Send; + } + + uses { + interface Send as SubSend; + } +} + +implementation { + components UniqueSendP, + new StateC(), + RandomC, + CC2420PacketC, + MainC; + + Send = UniqueSendP.Send; + SubSend = UniqueSendP.SubSend; + + MainC.SoftwareInit -> UniqueSendP; + + UniqueSendP.State -> StateC; + UniqueSendP.Random -> RandomC; + UniqueSendP.CC2420Packet -> CC2420PacketC; + +} + diff --git a/tos/chips/cc2420/UniqueSendP.nc b/tos/chips/cc2420/UniqueSendP.nc new file mode 100644 index 00000000..74df958a --- /dev/null +++ b/tos/chips/cc2420/UniqueSendP.nc @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * This layer is responsible for supplying a unique data sequence number (dsn) + * to each outgoing message. + * @author David Moss + */ + +module UniqueSendP { + provides { + interface Send; + interface Init; + } + + uses { + interface Send as SubSend; + interface State; + interface Random; + interface CC2420Packet; + } +} + +implementation { + + uint8_t localSendId; + + enum { + S_IDLE, + S_SENDING, + }; + + /***************** Init Commands ****************/ + command error_t Init.init() { + localSendId = call Random.rand16(); + return SUCCESS; + } + + /***************** Send Commands ****************/ + /** + * Each call to this send command gives the message a single + * DSN that does not change for every copy of the message + * sent out. For messages that are not acknowledged, such as + * a broadcast address message, the receiving end does not + * signal receive() more than once for that message. + */ + command error_t Send.send(message_t *msg, uint8_t len) { + error_t error; + if(call State.requestState(S_SENDING) == SUCCESS) { + (call CC2420Packet.getHeader(msg))->dsn = localSendId++; + + if((error = call SubSend.send(msg, len)) != SUCCESS) { + call State.toIdle(); + } + + return error; + } + + return EBUSY; + } + + command error_t Send.cancel(message_t *msg) { + return call SubSend.cancel(msg); + } + + + command uint8_t Send.maxPayloadLength() { + return call SubSend.maxPayloadLength(); + } + + command void *Send.getPayload(message_t* msg) { + return call SubSend.getPayload(msg); + } + + /***************** SubSend Events ****************/ + event void SubSend.sendDone(message_t *msg, error_t error) { + call State.toIdle(); + signal Send.sendDone(msg, error); + } + +} + diff --git a/tos/interfaces/LowPowerListening.nc b/tos/interfaces/LowPowerListening.nc new file mode 100644 index 00000000..0326907d --- /dev/null +++ b/tos/interfaces/LowPowerListening.nc @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2005-2006 Rincon Research Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the Rincon Research Corporation nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * RINCON RESEARCH OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE + */ + +/** + * Low Power Listening interface + * + * @author Jonathan Hui + * @author David Moss + */ + +#include "message.h" + +interface LowPowerListening { + + /** + * Set this this node's radio sleep interval, in milliseconds. + * Once every interval, the node will sleep and perform an Rx check + * on the radio. Setting the sleep interval to 0 will keep the radio + * always on. + * + * This is the equivalent of setting the local duty cycle rate. + * + * @param sleepIntervalMs the length of this node's Rx check interval, in [ms] + */ + command void setLocalSleepInterval(uint16_t sleepIntervalMs); + + /** + * @return the local node's sleep interval, in [ms] + */ + command uint16_t getLocalSleepInterval(); + + /** + * Set this node's radio duty cycle rate, in units of [percentage*100]. + * For example, to get a 0.05% duty cycle, + * + * call LowPowerListening.setDutyCycle(5); + * + * + * For a 100% duty cycle (always on), + * + * call LowPowerListening.setDutyCycle(10000); + * + * + * This is the equivalent of setting the local sleep interval explicitly. + * + * @param dutyCycle The duty cycle percentage, in units of [percentage*100] + */ + command void setLocalDutyCycle(uint16_t dutyCycle); + + /** + * @return this node's radio duty cycle rate, in units of [percentage*100] + */ + command uint16_t getLocalDutyCycle(); + + + /** + * Configure this outgoing message so it can be transmitted to a neighbor mote + * with the specified Rx sleep interval. + * @param msg Pointer to the message that will be sent + * @param sleepInterval The receiving node's sleep interval, in [ms] + */ + command void setRxSleepInterval(message_t *msg, uint16_t sleepIntervalMs); + + /** + * @return the destination node's sleep interval configured in this message + */ + command uint16_t getRxSleepInterval(message_t *msg); + + /** + * Configure this outgoing message so it can be transmitted to a neighbor mote + * with the specified Rx duty cycle rate. + * Duty cycle is in units of [percentage*100], i.e. 0.25% duty cycle = 25. + * + * @param msg Pointer to the message that will be sent + * @param dutyCycle The duty cycle of the receiving mote, in units of + * [percentage*100] + */ + command void setRxDutyCycle(message_t *msg, uint16_t dutyCycle); + + /** + * @return the destination node's duty cycle configured in this message + * in units of [percentage*100] + */ + command uint16_t getRxDutyCycle(message_t *msg); + + /** + * Convert a duty cycle, in units of [percentage*100], to + * the sleep interval of the mote in milliseconds + * @param dutyCycle The duty cycle in units of [percentage*100] + * @return The equivalent sleep interval, in units of [ms] + */ + command uint16_t dutyCycleToSleepInterval(uint16_t dutyCycle); + + /** + * Convert a sleep interval, in units of [ms], to a duty cycle + * in units of [percentage*100] + * @param sleepInterval The sleep interval in units of [ms] + * @return The duty cycle in units of [percentage*100] + */ + command uint16_t sleepIntervalToDutyCycle(uint16_t sleepInterval); + +} -- 2.39.2