From 76a5dd90ffc7587a6e7082dbe570c77571edece7 Mon Sep 17 00:00:00 2001 From: a_cunha Date: Mon, 11 Feb 2008 17:41:25 +0000 Subject: [PATCH] *** empty log message *** --- tos/lib/net/zigbee/cc2420/AddressFilter.nc | 20 + tos/lib/net/zigbee/cc2420/CC2420ControlC.nc | 107 ++ tos/lib/net/zigbee/cc2420/CC2420ControlP.nc | 525 +++++++++ tos/lib/net/zigbee/cc2420/CC2420ReceiveC.nc | 84 ++ tos/lib/net/zigbee/cc2420/CC2420ReceiveP.nc | 1005 +++++++++++++++++ tos/lib/net/zigbee/cc2420/CC2420SpiP.nc | 377 +++++++ tos/lib/net/zigbee/cc2420/CC2420TransmitC.nc | 90 ++ tos/lib/net/zigbee/cc2420/CC2420TransmitP.nc | 254 +++++ tos/lib/net/zigbee/cc2420/Receiveframe.nc | 16 + tos/lib/net/zigbee/cc2420/Sendframe.nc | 24 + tos/lib/net/zigbee/cc2420/readme.txt | 8 + tos/lib/net/zigbee/ieee802154/readme.txt | 12 + .../ieee802154/timerasyncTDBS/TimerAsync.nc | 92 ++ .../ieee802154/timerasyncTDBS/TimerAsyncC.nc | 30 + .../ieee802154/timerasyncTDBS/TimerAsyncM.nc | 436 +++++++ tos/lib/net/zigbee/readme.txt | 109 ++ 16 files changed, 3189 insertions(+) create mode 100644 tos/lib/net/zigbee/cc2420/AddressFilter.nc create mode 100644 tos/lib/net/zigbee/cc2420/CC2420ControlC.nc create mode 100644 tos/lib/net/zigbee/cc2420/CC2420ControlP.nc create mode 100644 tos/lib/net/zigbee/cc2420/CC2420ReceiveC.nc create mode 100644 tos/lib/net/zigbee/cc2420/CC2420ReceiveP.nc create mode 100644 tos/lib/net/zigbee/cc2420/CC2420SpiP.nc create mode 100644 tos/lib/net/zigbee/cc2420/CC2420TransmitC.nc create mode 100644 tos/lib/net/zigbee/cc2420/CC2420TransmitP.nc create mode 100644 tos/lib/net/zigbee/cc2420/Receiveframe.nc create mode 100644 tos/lib/net/zigbee/cc2420/Sendframe.nc create mode 100644 tos/lib/net/zigbee/cc2420/readme.txt create mode 100644 tos/lib/net/zigbee/ieee802154/readme.txt create mode 100644 tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsync.nc create mode 100644 tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsyncC.nc create mode 100644 tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsyncM.nc create mode 100644 tos/lib/net/zigbee/readme.txt diff --git a/tos/lib/net/zigbee/cc2420/AddressFilter.nc b/tos/lib/net/zigbee/cc2420/AddressFilter.nc new file mode 100644 index 00000000..850da9a7 --- /dev/null +++ b/tos/lib/net/zigbee/cc2420/AddressFilter.nc @@ -0,0 +1,20 @@ +/** + * + * @author André Cunha + * @version 1.0 + */ + +interface AddressFilter { + + + command error_t set_address(uint16_t mac_short_address, uint32_t mac_extended0, uint32_t mac_extended1); + + + command error_t set_coord_address(uint16_t mac_coord_address, uint16_t mac_panid); + + + command error_t enable_address_decode(uint8_t enable); + + +} + diff --git a/tos/lib/net/zigbee/cc2420/CC2420ControlC.nc b/tos/lib/net/zigbee/cc2420/CC2420ControlC.nc new file mode 100644 index 00000000..72ca3891 --- /dev/null +++ b/tos/lib/net/zigbee/cc2420/CC2420ControlC.nc @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2005-2006 Arch Rock 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 Arch Rock 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 + * ARCHED ROCK 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 + */ + +/** + * Implementation for configuring a ChipCon CC2420 radio. + * + * @author Jonathan Hui + * @version $Revision$ $Date$ + */ + +#include "CC2420.h" +#include "IEEE802154.h" + +configuration CC2420ControlC { + + provides interface Resource; + provides interface CC2420Config; + provides interface CC2420Power; + provides interface Read as ReadRssi; + + // provides interface Read_rssi; + +} + +implementation { + + components CC2420ControlP; + Resource = CC2420ControlP; + CC2420Config = CC2420ControlP; + CC2420Power = CC2420ControlP; + ReadRssi = CC2420ControlP; + + //Read_rssi = CC2420ControlP; + + components MainC; + MainC.SoftwareInit -> CC2420ControlP; + + //components CC2420ActiveMessageC; + //CC2420ControlP.AMPacket -> CC2420ActiveMessageC; + + components AlarmMultiplexC as Alarm; + CC2420ControlP.StartupTimer -> Alarm; + + components HplCC2420PinsC as Pins; + CC2420ControlP.CSN -> Pins.CSN; + CC2420ControlP.RSTN -> Pins.RSTN; + CC2420ControlP.VREN -> Pins.VREN; + + components HplCC2420InterruptsC as Interrupts; + CC2420ControlP.InterruptCCA -> Interrupts.InterruptCCA; + + components new CC2420SpiC() as Spi; + CC2420ControlP.SpiResource -> Spi; + CC2420ControlP.SRXON -> Spi.SRXON; + CC2420ControlP.SRFOFF -> Spi.SRFOFF; + CC2420ControlP.SXOSCON -> Spi.SXOSCON; + CC2420ControlP.SXOSCOFF -> Spi.SXOSCOFF; + CC2420ControlP.FSCTRL -> Spi.FSCTRL; + CC2420ControlP.IOCFG0 -> Spi.IOCFG0; + CC2420ControlP.IOCFG1 -> Spi.IOCFG1; + CC2420ControlP.MDMCTRL0 -> Spi.MDMCTRL0; + CC2420ControlP.MDMCTRL1 -> Spi.MDMCTRL1; + CC2420ControlP.PANID -> Spi.PANID; + CC2420ControlP.RXCTRL1 -> Spi.RXCTRL1; + CC2420ControlP.RSSI -> Spi.RSSI; + + + + components new CC2420SpiC() as SyncSpiC; + CC2420ControlP.SyncResource -> SyncSpiC; + + components new CC2420SpiC() as RssiResource; + CC2420ControlP.RssiResource -> RssiResource; + + //components ActiveMessageAddressC; + //CC2420ControlP.ActiveMessageAddress -> ActiveMessageAddressC; + +} + diff --git a/tos/lib/net/zigbee/cc2420/CC2420ControlP.nc b/tos/lib/net/zigbee/cc2420/CC2420ControlP.nc new file mode 100644 index 00000000..a969c3ff --- /dev/null +++ b/tos/lib/net/zigbee/cc2420/CC2420ControlP.nc @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2005-2006 Arch Rock 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 Arch Rock 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 + * ARCHED ROCK 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 Jonathan Hui + * @author David Moss + * @author Urs Hunkeler (ReadRssi implementation) + * @version $Revision$ $Date$ + */ + +#include "Timer.h" + +module CC2420ControlP { + + provides interface Init; + provides interface Resource; + provides interface CC2420Config; + provides interface CC2420Power; + provides interface Read as ReadRssi; + + // provides interface Read_rssi; + + uses interface Alarm as StartupTimer; + uses interface GeneralIO as CSN; + uses interface GeneralIO as RSTN; + uses interface GeneralIO as VREN; + uses interface GpioInterrupt as InterruptCCA; + //uses interface ActiveMessageAddress; + + uses interface CC2420Ram as PANID; + uses interface CC2420Register as FSCTRL; + uses interface CC2420Register as IOCFG0; + uses interface CC2420Register as IOCFG1; + 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; + +} + +implementation { + + typedef enum { + S_VREG_STOPPED, + S_VREG_STARTING, + S_VREG_STARTED, + S_XOSC_STARTING, + S_XOSC_STARTED, + } cc2420_control_state_t; + + uint8_t m_channel; + + uint8_t m_tx_power; + + uint16_t m_pan; + + uint16_t m_short_addr; + + bool m_sync_busy; + + bool autoAckEnabled; + + bool hwAutoAckDefault; + + bool addressRecognition; + + norace cc2420_control_state_t m_state = S_VREG_STOPPED; + + /***************** Prototypes ****************/ + + void writeFsctrl(); + void writeMdmctrl0(); + void writeId(); + + task void sync(); + task void syncDone(); + + /***************** Init Commands ****************/ + command error_t Init.init() { + call CSN.makeOutput(); + call RSTN.makeOutput(); + call VREN.makeOutput(); + + //m_short_addr = call ActiveMessageAddress.amAddress(); + // m_pan = call ActiveMessageAddress.amGroup(); + m_tx_power = CC2420_DEF_RFPOWER; + + //m_channel = CC2420_DEF_CHANNEL; + m_channel = LOGICAL_CHANNEL; + + +//#if defined(CC2420_NO_ACKNOWLEDGEMENTS) + autoAckEnabled = FALSE; +//#else +// autoAckEnabled = TRUE; +//#endif + +//#if defined(CC2420_HW_ACKNOWLEDGEMENTS) +// hwAutoAckDefault = TRUE; +//#else + hwAutoAckDefault = FALSE; +//#endif + +//#if defined(CC2420_NO_ADDRESS_RECOGNITION) + addressRecognition = FALSE; +//#else +// addressRecognition = TRUE; +//#endif + + return SUCCESS; + } + + /***************** Resource Commands ****************/ + async command error_t Resource.immediateRequest() { + error_t error = call SpiResource.immediateRequest(); + if ( error == SUCCESS ) { + call CSN.clr(); + } + return error; + } + + async command error_t Resource.request() { + return call SpiResource.request(); + } + + async command uint8_t Resource.isOwner() { + return call SpiResource.isOwner(); + } + + async command error_t Resource.release() { + atomic { + call CSN.set(); + return call SpiResource.release(); + } + } + + /***************** CC2420Power Commands ****************/ + async command error_t CC2420Power.startVReg() { + atomic { + if ( m_state != S_VREG_STOPPED ) { + return FAIL; + } + m_state = S_VREG_STARTING; + } + call VREN.set(); + call StartupTimer.start( CC2420_TIME_VREN ); + return SUCCESS; + } + + async command error_t CC2420Power.stopVReg() { + m_state = S_VREG_STOPPED; + call RSTN.clr(); + call VREN.clr(); + call RSTN.set(); + return SUCCESS; + } + + async command error_t CC2420Power.startOscillator() { + atomic { + //if ( m_state != S_VREG_STARTED ) { + // return FAIL; + //} + + //m_state = S_XOSC_STARTING; + + /* + call IOCFG1.write( CC2420_SFDMUX_XOSC16M_STABLE << + CC2420_IOCFG1_CCAMUX ); + */ + call IOCFG1.write(0x0018); + + call InterruptCCA.enableRisingEdge(); + call SXOSCON.strobe(); + /* + call IOCFG0.write( ( 1 << CC2420_IOCFG0_FIFOP_POLARITY ) | + ( 127 << CC2420_IOCFG0_FIFOP_THR ) ); + */ + call IOCFG0.write(0x027F); + + + writeFsctrl(); + + + //call FSCTRL.write(0x4192); + //initial set of frequency channel + //call FSCTRL.write( ( 1 << CC2420_FSCTRL_LOCK_THR ) | ( ( (LOGICAL_CHANNEL - 11)*5+357) << CC2420_FSCTRL_FREQ ) ); + + //call MDMCTRL0.write(0x0AE2); + call MDMCTRL0.write(0x02E2); + + //CHANGE + writeMdmctrl0(); + //call RXCTRL1.write(0x0A56); + + /* + call RXCTRL1.write( ( 1 << CC2420_RXCTRL1_RXBPF_LOCUR ) | + ( 1 << CC2420_RXCTRL1_LOW_LOWGAIN ) | + ( 1 << CC2420_RXCTRL1_HIGH_HGM ) | + ( 1 << CC2420_RXCTRL1_LNA_CAP_ARRAY ) | + ( 1 << CC2420_RXCTRL1_RXMIX_TAIL ) | + ( 1 << CC2420_RXCTRL1_RXMIX_VCM ) | + ( 2 << CC2420_RXCTRL1_RXMIX_CURRENT ) ); + */ + } + return SUCCESS; + } + + + async command error_t CC2420Power.stopOscillator() { + atomic { + if ( m_state != S_XOSC_STARTED ) { + return FAIL; + } + m_state = S_VREG_STARTED; + call SXOSCOFF.strobe(); + } + return SUCCESS; + } + + async command error_t CC2420Power.rxOn() { + atomic { + if ( m_state != S_XOSC_STARTED ) { + return FAIL; + } + call SRXON.strobe(); + } + return SUCCESS; + } + + async command error_t CC2420Power.rfOff() { + atomic { + if ( m_state != S_XOSC_STARTED ) { + return FAIL; + } + call SRFOFF.strobe(); + } + return SUCCESS; + } + + + /***************** CC2420Config Commands ****************/ + command uint8_t CC2420Config.getChannel() { + atomic return m_channel; + } + + command void CC2420Config.setChannel( uint8_t channel ) { + atomic m_channel = channel; + + } + + async command uint16_t CC2420Config.getShortAddr() { + atomic return m_short_addr; + } + + command void CC2420Config.setShortAddr( uint16_t addr ) { + atomic m_short_addr = addr; + } + + async command uint16_t CC2420Config.getPanAddr() { + atomic return m_pan; + } + + command void CC2420Config.setPanAddr( uint16_t pan ) { + atomic m_pan = pan; + } + + /** + * Sync must be called to commit software parameters configured on + * the microcontroller (through the CC2420Config interface) to the + * CC2420 radio chip. + */ + command error_t CC2420Config.sync() { + atomic { + if ( m_sync_busy ) { + return FAIL; + } + + m_sync_busy = TRUE; + if ( m_state == S_XOSC_STARTED ) { + call SyncResource.request(); + } else { + post syncDone(); + } + } + return SUCCESS; + } + + /** + * @param on TRUE to turn address recognition on, FALSE to turn it off + */ + command void CC2420Config.setAddressRecognition(bool on) { + atomic addressRecognition = on; + } + + /** + * @return TRUE if address recognition is enabled + */ + async command bool CC2420Config.isAddressRecognitionEnabled() { + atomic return addressRecognition; + } + + + /** + * Sync must be called for acknowledgement changes to take effect + * @param enableAutoAck TRUE to enable auto acknowledgements + * @param hwAutoAck TRUE to default to hardware auto acks, FALSE to + * default to software auto acknowledgements + */ + command void CC2420Config.setAutoAck(bool enableAutoAck, bool hwAutoAck) { + autoAckEnabled = enableAutoAck; + hwAutoAckDefault = hwAutoAck; + } + + /** + * @return TRUE if hardware auto acks are the default, FALSE if software + * acks are the default + */ + async command bool CC2420Config.isHwAutoAckDefault() { + atomic return hwAutoAckDefault; + } + + /** + * @return TRUE if auto acks are enabled + */ + async command bool CC2420Config.isAutoAckEnabled() { + atomic return autoAckEnabled; + } + + /***************** ReadRssi Commands ****************/ + command error_t ReadRssi.read() { + return call RssiResource.request(); + } + + /***************** Spi Resources Events ****************/ + event void SyncResource.granted() { + //possibly this is on the sync of the new configuration of the transceiver //CHECK + + call CSN.clr(); + call SRFOFF.strobe(); + + writeFsctrl(); + //CHANGE + writeMdmctrl0(); + writeId(); + call CSN.set(); + call CSN.clr(); + call SRXON.strobe(); + call CSN.set(); + call SyncResource.release(); + post syncDone(); + } + + 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); + + //signal Read_rssi.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() { + m_state = S_XOSC_STARTED; + call InterruptCCA.disable(); + call IOCFG1.write( 0 ); + writeId(); + call CSN.set(); + call CSN.clr(); + signal CC2420Power.startOscillatorDone(); + } + + /***************** ActiveMessageAddress Events ****************/ + /* + async event void ActiveMessageAddress.changed() { + atomic { + m_short_addr = call ActiveMessageAddress.amAddress(); + m_pan = call ActiveMessageAddress.amGroup(); + } + + post sync(); + } + */ + /***************** Tasks ****************/ + /** + * Attempt to synchronize our current settings with the CC2420 + */ + task void sync() { + call CC2420Config.sync(); + } + + task void syncDone() { + atomic m_sync_busy = FALSE; + signal CC2420Config.syncDone( SUCCESS ); + } + + + /***************** Functions ****************/ + /** + * Write teh FSCTRL register + */ + void writeFsctrl() { + uint8_t channel; + + atomic { + channel = m_channel; + } + + call FSCTRL.write( ( 1 << CC2420_FSCTRL_LOCK_THR ) | + ( ( (channel - 11)*5+357 ) << CC2420_FSCTRL_FREQ ) ); + /* + call FSCTRL.write( ( 1 << CC2420_FSCTRL_LOCK_THR ) | + ( channel << CC2420_FSCTRL_FREQ ) ); + */ + } + + /** + * Write the MDMCTRL0 register + */ + void writeMdmctrl0() { + atomic { + call MDMCTRL0.write( ( 1 << CC2420_MDMCTRL0_RESERVED_FRAME_MODE ) | + ( addressRecognition << CC2420_MDMCTRL0_ADR_DECODE ) | + ( 2 << CC2420_MDMCTRL0_CCA_HYST ) | + ( 3 << CC2420_MDMCTRL0_CCA_MOD ) | + ( 1 << CC2420_MDMCTRL0_AUTOCRC ) | + ( (autoAckEnabled && hwAutoAckDefault) << CC2420_MDMCTRL0_AUTOACK ) | + ( 0 << CC2420_MDMCTRL0_AUTOACK ) | + ( 2 << CC2420_MDMCTRL0_PREAMBLE_LENGTH ) ); + } + // Jon Green: + // MDMCTRL1.CORR_THR is defaulted to 20 instead of 0 like the datasheet says + // If we add in changes to MDMCTRL1, be sure to include this fix. + } + + /** + * Write the PANID register + */ + void writeId() { + nxle_uint16_t id[ 2 ]; + + atomic { + id[ 0 ] = m_pan; + id[ 1 ] = m_short_addr; + } + + call PANID.write(0, (uint8_t*)&id, sizeof(id)); + } + + + + /***************** Defaults ****************/ + default event void CC2420Config.syncDone( error_t error ) { + } + + default event void ReadRssi.readDone(error_t error, uint16_t data) { + } + + /* + + command error_t Read_rssi.read() { + return call RssiResource.request(); + } + */ + +} diff --git a/tos/lib/net/zigbee/cc2420/CC2420ReceiveC.nc b/tos/lib/net/zigbee/cc2420/CC2420ReceiveC.nc new file mode 100644 index 00000000..90a5433d --- /dev/null +++ b/tos/lib/net/zigbee/cc2420/CC2420ReceiveC.nc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2005-2006 Arch Rock 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 Arch Rock 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 + * ARCHED ROCK 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 + */ + +/** + * Implementation of the receive path for the ChipCon CC2420 radio. + * + * @author Jonathan Hui + * @version $Revision$ $Date$ + */ + + + +configuration CC2420ReceiveC { + + provides interface StdControl; + + provides interface Receiveframe; + + provides interface AddressFilter; + +} + +implementation { + components MainC; + components CC2420ReceiveP; + + components new CC2420SpiC() as Spi; + components CC2420ControlC; + + components HplCC2420PinsC as Pins; + components HplCC2420InterruptsC as InterruptsC; + + components LedsC as Leds; + CC2420ReceiveP.Leds -> Leds; + + StdControl = CC2420ReceiveP; + + Receiveframe = CC2420ReceiveP; + + AddressFilter = 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.CC2420Config -> CC2420ControlC; + +} diff --git a/tos/lib/net/zigbee/cc2420/CC2420ReceiveP.nc b/tos/lib/net/zigbee/cc2420/CC2420ReceiveP.nc new file mode 100644 index 00000000..14cb939a --- /dev/null +++ b/tos/lib/net/zigbee/cc2420/CC2420ReceiveP.nc @@ -0,0 +1,1005 @@ +/* + * Copyright (c) 2005-2006 Arch Rock 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 Arch Rock 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 + * ARCHED ROCK 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 Jonathan Hui + * @author David Moss + * @author Jung Il Choi + * @version $Revision$ $Date$ + */ + +#include "printfUART.h" +#include "frame_format.h" +#include "mac_func.h" + +module CC2420ReceiveP { + + provides interface Init; + provides interface StdControl; + //provides interface CC2420Receive; + //provides interface Receive; + //provides interface ReceiveIndicator as PacketIndicator; + + provides interface Receiveframe; + + provides interface AddressFilter; + + + + uses interface GeneralIO as CSN; + uses interface GeneralIO as FIFO; + uses interface GeneralIO as FIFOP; + uses interface GpioInterrupt as InterruptFIFOP; + + uses interface Resource as SpiResource; + uses interface CC2420Fifo as RXFIFO; + uses interface CC2420Strobe as SACK; + uses interface CC2420Strobe as SFLUSHRX; + //uses interface CC2420Packet; + //uses interface CC2420PacketBody; + uses interface CC2420Config; + + uses interface Leds; + + + + + +} + +implementation { + +typedef enum{ + S_STOPPED =0, + S_STARTED=1, + S_RX_LENGTH=2, + S_RX_FC=3, //FC - FRAME CONTROL + S_RX_ADDR=4, + S_RX_PAYLOAD=5, + S_RX_DISCARD=6, +}cc2420_receive_state_t; + + +/* + typedef enum { + S_STOPPED, + S_STARTED, + S_RX_LENGTH, + S_RX_FCF, + S_RX_PAYLOAD, + } cc2420_receive_state_t; + */ + enum { + RXFIFO_SIZE = 128, + TIMESTAMP_QUEUE_SIZE = 8, + SACK_HEADER_LENGTH = 7, + }; + + //uint16_t m_timestamp_queue[ TIMESTAMP_QUEUE_SIZE ]; + + //uint8_t m_timestamp_head; + + //uint8_t m_timestamp_size; + + /** Number of packets we missed because we were doing something else */ + uint8_t m_missed_packets; + + /** TRUE if we are receiving a valid packet into the stack */ + bool receivingPacket; + + /** The length of the frame we're currently receiving */ + norace uint8_t rxFrameLength; + + //number of bytes left in the FIFO Buffer + norace uint8_t m_bytes_left; + + //norace message_t* m_p_rx_buf; + + //message_t m_rx_buf; + + //already used + //cc2420_receive_state_t m_state; + + + norace MPDU rxmpdu; + MPDU *rxmpdu_ptr; + + + cc2420_receive_state_t m_state; + + + uint8_t rssi; + + + uint8_t receive_count=0; + + + +/*******************************************/ +/**** ADDRESS DECODE VARIABLES ****/ +/*******************************************/ + //address verification frame control variables + //frame control variables + uint8_t source_address=0; + uint8_t destination_address=0; + + //address verification structure pointers + dest_short *dest_short_ptr; + dest_long *dest_long_ptr; + + source_short *source_short_ptr; + source_long *source_long_ptr; + + beacon_addr_short *beacon_addr_short_ptr; + + uint8_t address_decode = 1; + + //address verification variables + uint16_t ver_macCoordShortAddress = 0x0000; + uint16_t ver_macShortAddress = 0xffff; + + uint32_t ver_aExtendedAddress0=0x00000000; + uint32_t ver_aExtendedAddress1=0x00000000; + + uint16_t ver_macPANId=0xffff; + + + + /***************** Prototypes ****************/ + void reset_state(); + void beginReceive(); + void receive(); + void waitForNextPacket(); + void flush(); + + // task void receiveDone_task(); + + /***************** Init Commands ****************/ + command error_t Init.init() { + //m_p_rx_buf = &m_rx_buf; + + rxmpdu_ptr = &rxmpdu; + + printfUART_init(); + + return SUCCESS; + } + + /***************** StdControl ****************/ + command error_t StdControl.start() { + + atomic { + reset_state(); + m_state = S_STARTED; + atomic receivingPacket = FALSE; + call InterruptFIFOP.enableFallingEdge(); + } + + + + return SUCCESS; + } + + command error_t StdControl.stop() { + atomic { + m_state = S_STOPPED; + reset_state(); + call CSN.set(); + call InterruptFIFOP.disable(); + } + return SUCCESS; + } + + + + + command error_t AddressFilter.set_address(uint16_t mac_short_address, uint32_t mac_extended0, uint32_t mac_extended1) + { + + ver_macShortAddress = mac_short_address; + + ver_aExtendedAddress0=mac_extended0; + ver_aExtendedAddress1=mac_extended1; + + address_decode = 1; + + //printfUART("sa %i %x %x %x %x\n",address_decode,ver_macShortAddress,ver_aExtendedAddress0,ver_aExtendedAddress1); + + + return SUCCESS; + } + + + command error_t AddressFilter.set_coord_address(uint16_t mac_coord_address, uint16_t mac_panid) + { + + ver_macCoordShortAddress = mac_coord_address; + ver_macPANId = mac_panid; + + //printfUART("sca %i %x %x\n",address_decode,ver_macCoordShortAddress,ver_macPANId); + + return SUCCESS; + } + + + command error_t AddressFilter.enable_address_decode(uint8_t enable) + { + + address_decode = enable; + + //printfUART("ead %i\n",address_decode); + + return SUCCESS; + } + + + + + /***************** Receive Commands ****************/ + /* + command void* Receive.getPayload(message_t* m, uint8_t* len) { + + if (len != NULL) { + *len = ((uint8_t*) (call CC2420PacketBody.getHeader( m_p_rx_buf )))[0]; + } + return m->data; + } + + command uint8_t Receive.payloadLength(message_t* m) { + uint8_t* buf = (uint8_t*)(call CC2420PacketBody.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 ); + m_timestamp_queue[ tail ] = time; + m_timestamp_size++; + } + + } + + async command void CC2420Receive.sfd_dropped() { + + if ( m_timestamp_size ) { + m_timestamp_size--; + } + + } +*/ + /***************** PacketIndicator Commands ****************/ + /* + command bool PacketIndicator.isReceiving() { + bool receiving; + atomic { + receiving = receivingPacket; + } + return receiving; + } + + */ + /***************** InterruptFIFOP Events ****************/ + async event void InterruptFIFOP.fired() { + + ////printfUART("Int %i\n",m_state); +//call Leds.led1Toggle(); + //call Leds.led2Toggle(); + + + if ( m_state == S_STARTED ) { + + + beginReceive(); + + + /* + if(call SpiResource.isOwner()) { + receive(); + + } else if (call SpiResource.immediateRequest() == SUCCESS) { + receive(); + + } else { + call SpiResource.request(); + } +*/ + } else { + m_missed_packets++; + } + + } + + + /***************** SpiResource Events ****************/ + event void SpiResource.granted() { + receive(); + } + + /***************** RXFIFO Events ****************/ + /** + * We received some bytes from the SPI bus. Process them in the context + * of the state we're in. Remember the length byte is not part of the length + */ + async event void RXFIFO.readDone( uint8_t* rx_buf, uint8_t rx_len,error_t error ) { + + + //int i; + /* + uint8_t len; + + //uint8_t rssi; + //uint8_t lqi; + int i; + + len = rx_buf[0]; + + rssi= 255 - rx_buf[len-1]; + + //lqi = rssi & 0x7f; + */ + /* + + //printfUART("r d %i %i\n", len, rssi); + + // len = rx_buf[0]; + //rssi=rx_buf[len-2]; + + + for (i=0;i<40;i++) + { + //printfUART("r %i %x\n",i,rx_buf[i]); + } + + + + + //signal Receiveframe.receive((uint8_t*)rxmpdu_ptr, rssi); + + receive_count++; + + if (receive_count == 2) + { flush(); + receive_count =0; + } + */ + + atomic{ + //my code + switch(m_state){ + + case S_RX_LENGTH: + + rxFrameLength = rx_buf[0]; + + m_state = S_RX_FC; + + //verify print + ////printfUART("LEN %x %x %i %i\n",rxFrameLength,rxmpdu_ptr->length,m_state,m_bytes_left); + + //printfUART("r%i %i %i\n",rxmpdu_ptr->seq_num,rxFrameLength,MAC_PACKET_SIZE); + + if ( rxFrameLength + 1 > m_bytes_left ) + { + // Length of this packet is bigger than the RXFIFO, flush it out. + //printfUART("pkt too big\n",""); + flush(); + + } + else + { + if ( !call FIFO.get() && !call FIFOP.get() ) + { + //printfUART("RED left %x\n",m_bytes_left); + + m_bytes_left -= rxFrameLength + 1; + } + + //if(rxFrameLength <= MAC_PACKET_SIZE) + //{ + if(rxFrameLength > 0) + { + //verify read length and read the frame control field (2 bytes) + if(rxFrameLength > 2) { + // This packet has an FCF byte plus at least one more byte to read + //call RXFIFO.continueRead(buf + 1, SACK_HEADER_LENGTH); + + ////printfUART("LEN OK\n",""); + //read frame control + sequence number + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 1, 3); + } + else + { + // This is really a bad packet, skip FCF and get it out of here. + //m_state = S_RX_PAYLOAD; + + m_state = S_RX_DISCARD; + //printfUART("bad len\n",""); + + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 1, rxFrameLength); + return; + } + } + else + { + // Length == 0; start reading the next packet + atomic receivingPacket = FALSE; + call CSN.set(); + call SpiResource.release(); + waitForNextPacket(); + } + + //} + //else + //{ + // Length is too large; we have to flush the entire Rx FIFO + // //printfUART("pkt too large\n",""); + // flush(); + + // return; + //} + } + break; + case S_RX_FC: + + //verify print + ////printfUART("FC %x %x %x %i\n",rxmpdu_ptr->frame_control1,rxmpdu_ptr->frame_control2,rxmpdu_ptr->seq_num, m_state); + + if ((rxmpdu_ptr->frame_control1 & 0x7) == TYPE_ACK) + { + m_state = S_RX_PAYLOAD; + + //printfUART("r ack \n",""); + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr+4,2); + return; + } + + + if (address_decode == 1) + { + m_state = S_RX_ADDR; + + destination_address=get_fc2_dest_addr(rxmpdu_ptr->frame_control2); + + if (destination_address > 1) + { + switch(destination_address) + { + case SHORT_ADDRESS: + //read the short address + destination PAN identifier + ////printfUART("s ad",""); + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 4, 6); + break; + + case LONG_ADDRESS: + //read the long address + destination PAN identifier + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 4, 12); + break; + } + } + else + { + //destination address fields not present + m_state = S_RX_PAYLOAD; + //it is not possible to do the address decoding, there is no destination address fields + //send the full packet up + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 4, rxmpdu_ptr->length- 3); + + } + } + else + { + //address decode is not activated + m_state = S_RX_PAYLOAD; + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 4, rxmpdu_ptr->length - 3); + } + + + break; + case S_RX_ADDR: + m_state = S_RX_PAYLOAD; + + + switch ((rxmpdu_ptr->frame_control1 & 0x7)) + { + case TYPE_BEACON: + ////printfUART("RB \n",""); + + beacon_addr_short_ptr = (beacon_addr_short *) &rxmpdu_ptr->data[0]; + + ////printfUART("pb %x %x %x %x\n",rxmpdu_ptr->seq_num, beacon_addr_short_ptr->destination_PAN_identifier,beacon_addr_short_ptr->destination_address, beacon_addr_short_ptr->source_address); + + /* + for (i=0;i<6;i++) + { + //printfUART("r %i %x %x %x\n",i,rxmpdu_ptr->data[i],rx_buf[i],rx_buf[i+4]); + } + */ + + //printfUART("RB %x %x \n",ver_macCoordShortAddress,ver_macShortAddress); + + + //avoid VERIFY static assignment of coordinator parent + if (beacon_addr_short_ptr->source_address != ver_macCoordShortAddress) + { + //printfUART("bad bec %x %x\n", beacon_addr_short_ptr->source_address,ver_macCoordShortAddress); + + m_state = S_RX_DISCARD; + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 10, rxmpdu_ptr->length - 9); + return; + } + /* + if (ver_macShortAddress != 0xffff) + { + if ( beacon_addr_short_ptr->source_address != ver_macShortAddress) + { + //printfUART("pb %x %x\n", beacon_addr_short_ptr->source_address,ver_macShortAddress); + + m_state = S_RX_DISCARD; + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 10, rxmpdu_ptr->length - 9); + return; + } + } + */ + break; + case TYPE_DATA: + case TYPE_CMD: + + //VALIDATION OF DESTINATION ADDRESSES - NOT TO OVERLOAD THE PROCESSOR + if (destination_address > 1) + { + switch(destination_address) + { + case SHORT_ADDRESS: + dest_short_ptr = (dest_short *) &rxmpdu_ptr->data[0]; + + if ( dest_short_ptr->destination_address != 0xffff && dest_short_ptr->destination_address != ver_macShortAddress) + { + //printfUART("nsm %x %x\n", dest_short_ptr->destination_address,ver_macShortAddress); + + m_state = S_RX_DISCARD; + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 10, rxmpdu_ptr->length - 9); + return; + } + + //If a destination PAN identifier is included in the frame, it shall match macPANId or shall be the + //broadcast PAN identifier (0 x ffff). + if(dest_short_ptr->destination_PAN_identifier != 0xffff && dest_short_ptr->destination_PAN_identifier != ver_macPANId ) + { + //printfUART("wsP %x %x \n", dest_short_ptr->destination_PAN_identifier,ver_macPANId); + + m_state = S_RX_DISCARD; + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 10, rxmpdu_ptr->length - 9); + return; + } + + break; + + case LONG_ADDRESS: + + dest_long_ptr = (dest_long *) &rxmpdu_ptr->data[0]; + /* + if ( dest_long_ptr->destination_address0 !=ver_aExtendedAddress0 && dest_long_ptr->destination_address1 !=ver_aExtendedAddress1 ) + { + //printfUART("nlm %x %x \n",dest_long_ptr->destination_address0,dest_long_ptr->destination_address1); + + m_state = S_RX_DISCARD; + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 16, rxmpdu_ptr->length - 15); + return; + } + + + //If a destination PAN identifier is included in the frame, it shall match macPANId or shall be the + //broadcast PAN identifier (0 x ffff). + if(dest_long_ptr->destination_PAN_identifier != 0xffff && dest_long_ptr->destination_PAN_identifier != ver_macPANId ) + { + //printfUART("wLP %x %x\n", dest_long_ptr->destination_PAN_identifier,ver_macPANId); + + m_state = S_RX_DISCARD; + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 16, rxmpdu_ptr->length - 15); + return; + } + */ + break; + } + } + + break; + + case TYPE_ACK: + + //printfUART("error ack \n",""); + //call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr); + + return; + + break; + + + } + + //read the remaining packet + switch(destination_address) + { + case SHORT_ADDRESS: + ////printfUART("as %i\n", (rxmpdu_ptr->length - 9)); + + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 10, rxmpdu_ptr->length - 9); //7 + break; + + case LONG_ADDRESS: + ////printfUART("al %i\n", (rxmpdu_ptr->length - 15)); + call RXFIFO.continueRead((uint8_t*)rxmpdu_ptr + 16, rxmpdu_ptr->length - 15); + break; + } + + break; + + + case S_RX_PAYLOAD: + call CSN.set(); + //signal Receiveframe.receive((uint8_t*)rxmpdu_ptr, rssi); + + + //rssi= 255 - rx_buf[len-1]; + + /* + for (i=6;i<12;i++) + { + //printfUART("p %i %x %x\n",i,rxmpdu_ptr->data[i],rx_buf[i-6]); + } + */ + + if(!m_missed_packets) { + // Release the SPI only if there are no more frames to download + call SpiResource.release(); + } + + rssi = 255 - rxmpdu_ptr->data[rxmpdu_ptr->length-4]; + + //printfUART("pay %i %x %i\n",rxmpdu_ptr->seq_num, rssi,m_missed_packets); + + signal Receiveframe.receive((uint8_t*)rxmpdu_ptr, rssi); + + + if (m_missed_packets == 0) + { + flush(); + } + else + { + waitForNextPacket(); + } + + break; + + + case S_RX_DISCARD: + atomic receivingPacket = FALSE; + call CSN.set(); + call SpiResource.release(); + if (m_missed_packets == 0) + { + flush(); + } + else + { + waitForNextPacket(); + } + + break; + + default: + atomic receivingPacket = FALSE; + call CSN.set(); + call SpiResource.release(); + break; + + + } + + } + + + /* + cc2420_header_t* header = call CC2420PacketBody.getHeader( m_p_rx_buf ); + cc2420__t* metadata = call CC2420PacketBody.getMetadata( m_p_rx_buf ); + uint8_t* buf = (uint8_t*) header; + rxFrameLength = buf[ 0 ]; + + switch( m_state ) { + + case S_RX_LENGTH: + m_state = S_RX_FCF; + if ( rxFrameLength + 1 > m_bytes_left ) { + // Length of this packet is bigger than the RXFIFO, flush it out. + flush(); + + } else { + if ( !call FIFO.get() && !call FIFOP.get() ) { + m_bytes_left -= rxFrameLength + 1; + } + + if(rxFrameLength <= MAC_PACKET_SIZE) { + if(rxFrameLength > 0) { + if(rxFrameLength > SACK_HEADER_LENGTH) { + // This packet has an FCF byte plus at least one more byte to read + call RXFIFO.continueRead(buf + 1, SACK_HEADER_LENGTH); + + } else { + // This is really a bad packet, skip FCF and get it out of here. + m_state = S_RX_PAYLOAD; + call RXFIFO.continueRead(buf + 1, rxFrameLength); + } + + } else { + // Length == 0; start reading the next packet + atomic receivingPacket = FALSE; + 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_FCF: + m_state = S_RX_PAYLOAD; + + /* + * The destination address check here is not completely optimized. If you + * are seeing issues with dropped acknowledgements, try removing + * the address check and decreasing SACK_HEADER_LENGTH to 2. + * The length byte and the FCF byte are the only two bytes required + * to know that the packet is valid and requested an ack. The destination + * address is useful when we want to sniff packets from other transmitters + * while acknowledging packets that were destined for our local address. + *//* + if(call CC2420Config.isAutoAckEnabled() && !call CC2420Config.isHwAutoAckDefault()) { + if (((( header->fcf >> IEEE154_FCF_ACK_REQ ) & 0x01) == 1) + && (header->dest == call CC2420Config.getShortAddr()) + && ((( header->fcf >> IEEE154_FCF_FRAME_TYPE ) & 7) == IEEE154_TYPE_DATA)) { + // CSn flippage cuts off our FIFO; SACK and begin reading again + call CSN.set(); + call CSN.clr(); + call SACK.strobe(); + call CSN.set(); + call CSN.clr(); + call RXFIFO.beginRead(buf + 1 + SACK_HEADER_LENGTH, + rxFrameLength - SACK_HEADER_LENGTH); + return; + } + + + } + + // Didn't flip CSn, we're ok to continue reading. + call RXFIFO.continueRead(buf + 1 + SACK_HEADER_LENGTH, + rxFrameLength - SACK_HEADER_LENGTH); + break; + + case S_RX_PAYLOAD: + call CSN.set(); + + if(!m_missed_packets) { + // Release the SPI only if there are no more frames to download + call SpiResource.release(); + } + + if ( m_timestamp_size ) { + if ( rxFrameLength > 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; + } + + // We may have received an ack that should be processed by Transmit + // buf[rxFrameLength] >> 7 checks the CRC + if ( ( buf[ rxFrameLength ] >> 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; + } + } + + waitForNextPacket(); + break; + + default: + atomic receivingPacket = FALSE; + call CSN.set(); + call SpiResource.release(); + break; + + } + */ + } + + 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_metadata_t* metadata = call CC2420PacketBody.getMetadata( m_p_rx_buf ); + uint8_t* buf = (uint8_t*) call CC2420PacketBody.getHeader( m_p_rx_buf );; + + metadata->crc = buf[ rxFrameLength ] >> 7; + metadata->rssi = buf[ rxFrameLength - 1 ]; + metadata->lqi = buf[ rxFrameLength ] & 0x7f; + m_p_rx_buf = signal Receive.receive( m_p_rx_buf, m_p_rx_buf->data, + rxFrameLength ); + + atomic receivingPacket = FALSE; + waitForNextPacket(); + } + */ + /****************** CC2420Config Events ****************/ + event void CC2420Config.syncDone( error_t error ) { + } + + /****************** Functions ****************/ + /** + * Attempt to acquire the SPI bus to receive a packet. + */ + + void beginReceive() { + + m_state = S_RX_LENGTH; + + atomic receivingPacket = TRUE; + + ////printfUART("br %i\n",m_state); + + if(call SpiResource.isOwner()) { + receive(); + + } else if (call SpiResource.immediateRequest() == SUCCESS) { + receive(); + + } else { + call SpiResource.request(); + } + } + + /** + * Flush out the Rx FIFO + */ + void flush() { + //printfUART("f %i\n",m_state); + 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 Leds.led2Toggle(); + + call CSN.clr(); + + //call RXFIFO.beginRead( (uint8_t*)(call CC2420PacketBody.getHeader( m_p_rx_buf )), 1 ); + + //call RXFIFO.beginRead ((uint8_t*)rxmpdu_ptr,100); + + //my old + call RXFIFO.beginRead((uint8_t*)rxmpdu_ptr,1); + + + + + } + + + /** + * 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 ) { + call SpiResource.release(); + return; + } + + //printfUART("wn %i %i\n",m_state,m_missed_packets ); + + + atomic receivingPacket = FALSE; + + if ( ( m_missed_packets && call FIFO.get() ) || !call FIFOP.get() ) { + // 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; + + //printfUART("wnP %i\n",m_state); + + m_missed_packets = 0; + call SpiResource.release(); + } + } + } + + /** + * Reset this component + */ + + void reset_state() { + m_bytes_left = RXFIFO_SIZE; + atomic receivingPacket = FALSE; + //m_timestamp_head = 0; + //m_timestamp_size = 0; + m_missed_packets = 0; + } + +} diff --git a/tos/lib/net/zigbee/cc2420/CC2420SpiP.nc b/tos/lib/net/zigbee/cc2420/CC2420SpiP.nc new file mode 100644 index 00000000..d6112a8b --- /dev/null +++ b/tos/lib/net/zigbee/cc2420/CC2420SpiP.nc @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2005-2006 Arch Rock 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 Arch Rock 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 + * ARCHED ROCK 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 Jonathan Hui + * @author David Moss + * @author Roman Lim + * @version $Revision$ $Date$ + */ + +module CC2420SpiP { + + provides { + interface ChipSpiResource; + interface Resource[ uint8_t id ]; + interface CC2420Fifo as Fifo[ uint8_t id ]; + interface CC2420Ram as Ram[ uint16_t id ]; + interface CC2420Register as Reg[ uint8_t id ]; + interface CC2420Strobe as Strobe[ uint8_t id ]; + } + + uses { + interface Resource as SpiResource; + interface SpiByte; + interface SpiPacket; + interface State as WorkingState; + interface Leds; + } +} + +implementation { + + enum { + RESOURCE_COUNT = uniqueCount( "CC2420Spi.Resource" ), + NO_HOLDER = 0xFF, + }; + + /** WorkingStates */ + enum { + S_IDLE, + S_BUSY, + }; + + /** Address to read/write on the CC2420, also maintains caller's client id */ + norace uint16_t m_addr; + + /** Each bit represents a client ID that is requesting SPI bus access */ + uint8_t m_requests = 0; + + /** The current client that owns the SPI bus */ + uint8_t m_holder = NO_HOLDER; + + /** TRUE if it is safe to release the SPI bus after all users say ok */ + bool release; + + /***************** Prototypes ****************/ + error_t attemptRelease(); + task void grant(); + + /***************** ChipSpiResource Commands ****************/ + /** + * Abort the release of the SPI bus. This must be called only with the + * releasing() event + */ + async command void ChipSpiResource.abortRelease() { + atomic release = FALSE; + } + + /** + * Release the SPI bus if there are no objections + */ + async command error_t ChipSpiResource.attemptRelease() { + return attemptRelease(); + } + + /***************** Resource Commands *****************/ + async command error_t Resource.request[ uint8_t id ]() { + + atomic { + if ( call WorkingState.requestState(S_BUSY) == SUCCESS ) { + m_holder = id; + if(call SpiResource.isOwner()) { + post grant(); + + } else { + call SpiResource.request(); + } + + } else { + m_requests |= 1 << id; + } + } + return SUCCESS; + } + + async command error_t Resource.immediateRequest[ uint8_t id ]() { + error_t error; + + atomic { + if ( call WorkingState.requestState(S_BUSY) != SUCCESS ) { + return EBUSY; + } + + + if(call SpiResource.isOwner()) { + m_holder = id; + error = SUCCESS; + + } else if ((error = call SpiResource.immediateRequest()) == SUCCESS ) { + m_holder = id; + + } else { + call WorkingState.toIdle(); + } + } + return error; + } + + async command error_t Resource.release[ uint8_t id ]() { + uint8_t i; + atomic { + if ( m_holder != id ) { + return FAIL; + } + + m_holder = NO_HOLDER; + if ( !m_requests ) { + call WorkingState.toIdle(); + attemptRelease(); + + } else { + for ( i = m_holder + 1; ; i++ ) { + i %= RESOURCE_COUNT; + + if ( m_requests & ( 1 << i ) ) { + m_holder = i; + m_requests &= ~( 1 << i ); + post grant(); + return SUCCESS; + } + } + } + } + + return SUCCESS; + } + + async command uint8_t Resource.isOwner[ uint8_t id ]() { + atomic return (m_holder == id); + } + + + /***************** SpiResource Events ****************/ + event void SpiResource.granted() { + post grant(); + } + + /***************** Fifo Commands ****************/ + async command cc2420_status_t Fifo.beginRead[ uint8_t addr ]( uint8_t* data, + uint8_t len ) { + + cc2420_status_t status = 0; + + atomic { + if(call WorkingState.isIdle()) { + return status; + } + } + + m_addr = addr | 0x40; + + status = call SpiByte.write( m_addr ); + call Fifo.continueRead[ addr ]( data, len ); + + return status; + + } + + async command error_t Fifo.continueRead[ uint8_t addr ]( uint8_t* data, + uint8_t len ) { + return call SpiPacket.send( NULL, data, len ); + } + + async command cc2420_status_t Fifo.write[ uint8_t addr ]( uint8_t* data, + uint8_t len ) { + + uint8_t status = 0; + + atomic { + if(call WorkingState.isIdle()) { + return status; + } + } + + m_addr = addr; + + status = call SpiByte.write( m_addr ); + call SpiPacket.send( data, NULL, len ); + + return status; + + } + + /***************** RAM Commands ****************/ + async command cc2420_status_t Ram.read[ uint16_t addr ]( uint8_t offset, + uint8_t* data, + uint8_t len ) { + + cc2420_status_t status = 0; + + atomic { + if(call WorkingState.isIdle()) { + return status; + } + } + + addr += offset; + + call SpiByte.write( addr | 0x80 ); + status = call SpiByte.write( ( ( addr >> 1 ) & 0xc0 ) | 0x20 ); + for ( ; len; len-- ) { + *data++ = call SpiByte.write( 0 ); + } + + return status; + + } + + + async command cc2420_status_t Ram.write[ uint16_t addr ]( uint8_t offset, + uint8_t* data, + uint8_t len ) { + + cc2420_status_t status = 0; + + atomic { + if(call WorkingState.isIdle()) { + return status; + } + } + + addr += offset; + + call SpiByte.write( addr | 0x80 ); + call SpiByte.write( ( addr >> 1 ) & 0xc0 ); + for ( ; len; len-- ) { + status = call SpiByte.write( *data++ ); + } + + return status; + + } + + /***************** Register Commands ****************/ + async command cc2420_status_t Reg.read[ uint8_t addr ]( uint16_t* data ) { + + cc2420_status_t status = 0; + + atomic { + if(call WorkingState.isIdle()) { + return status; + } + } + + status = call SpiByte.write( addr | 0x40 ); + *data = (uint16_t)call SpiByte.write( 0 ) << 8; + *data |= call SpiByte.write( 0 ); + + return status; + + } + + async command cc2420_status_t Reg.write[ uint8_t addr ]( uint16_t data ) { + atomic { + if(call WorkingState.isIdle()) { + return 0; + } + } + call SpiByte.write( addr ); + call SpiByte.write( data >> 8 ); + return call SpiByte.write( data & 0xff ); + } + + + /***************** Strobe Commands ****************/ + async command cc2420_status_t Strobe.strobe[ uint8_t addr ]() { + atomic { + if(call WorkingState.isIdle()) { + return 0; + } + } + + return call SpiByte.write( addr ); + } + + /***************** SpiPacket Events ****************/ + async event void SpiPacket.sendDone( uint8_t* tx_buf, uint8_t* rx_buf, + uint16_t len, error_t error ) { + if ( m_addr & 0x40 ) { + signal Fifo.readDone[ m_addr & ~0x40 ]( rx_buf, len, error ); + } else { + signal Fifo.writeDone[ m_addr ]( tx_buf, len, error ); + } + } + + /***************** Functions ****************/ + error_t attemptRelease() { + + atomic{ + if(m_requests > 0 + || m_holder != NO_HOLDER + || !call WorkingState.isIdle()) { + return FAIL; + } + } + atomic release = TRUE; + signal ChipSpiResource.releasing(); + atomic { + if(release) { + call SpiResource.release(); + return SUCCESS; + } + } + + return EBUSY; + } + + task void grant() { + uint8_t holder; + atomic { + holder = m_holder; + } + signal Resource.granted[ holder ](); + } + + /***************** Defaults ****************/ + default event void Resource.granted[ uint8_t id ]() { + } + + default async event void Fifo.readDone[ uint8_t addr ]( uint8_t* rx_buf, uint8_t rx_len, error_t error ) { + } + + default async event void Fifo.writeDone[ uint8_t addr ]( uint8_t* tx_buf, uint8_t tx_len, error_t error ) { + } + + default async event void ChipSpiResource.releasing() { + } + +} diff --git a/tos/lib/net/zigbee/cc2420/CC2420TransmitC.nc b/tos/lib/net/zigbee/cc2420/CC2420TransmitC.nc new file mode 100644 index 00000000..be02c32a --- /dev/null +++ b/tos/lib/net/zigbee/cc2420/CC2420TransmitC.nc @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2005-2006 Arch Rock 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 Arch Rock 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 + * ARCHED ROCK 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 + */ + +/** + * Implementation of the transmit path for the ChipCon CC2420 radio. + * + * @author Jonathan Hui + * @version $Revision$ $Date$ + */ + +#include "IEEE802154.h" + +configuration CC2420TransmitC { + + provides { + interface StdControl; + + interface ReceiveIndicator as EnergyIndicator; + interface ReceiveIndicator as ByteIndicator; + + interface Sendframe; + } +} + +implementation { + + components CC2420TransmitP; + StdControl = CC2420TransmitP; + + EnergyIndicator = CC2420TransmitP.EnergyIndicator; + ByteIndicator = CC2420TransmitP.ByteIndicator; + + + Sendframe = CC2420TransmitP; + + components MainC; + MainC.SoftwareInit -> CC2420TransmitP; + + + components HplCC2420PinsC as Pins; + CC2420TransmitP.CCA -> Pins.CCA; + CC2420TransmitP.CSN -> Pins.CSN; + CC2420TransmitP.SFD -> Pins.SFD; + + components HplCC2420InterruptsC as Interrupts; + CC2420TransmitP.CaptureSFD -> Interrupts.CaptureSFD; + + components new CC2420SpiC() as Spi; + CC2420TransmitP.SpiResource -> Spi; + CC2420TransmitP.ChipSpiResource -> Spi; + CC2420TransmitP.SNOP -> Spi.SNOP; + CC2420TransmitP.STXON -> Spi.STXON; + CC2420TransmitP.STXONCCA -> Spi.STXONCCA; + CC2420TransmitP.SFLUSHTX -> Spi.SFLUSHTX; + CC2420TransmitP.TXCTRL -> Spi.TXCTRL; + CC2420TransmitP.TXFIFO -> Spi.TXFIFO; + CC2420TransmitP.TXFIFO_RAM -> Spi.TXFIFO_RAM; + CC2420TransmitP.MDMCTRL1 -> Spi.MDMCTRL1; + + components LedsC; + CC2420TransmitP.Leds -> LedsC; +} diff --git a/tos/lib/net/zigbee/cc2420/CC2420TransmitP.nc b/tos/lib/net/zigbee/cc2420/CC2420TransmitP.nc new file mode 100644 index 00000000..98d64f27 --- /dev/null +++ b/tos/lib/net/zigbee/cc2420/CC2420TransmitP.nc @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2005-2006 Arch Rock 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 Arch Rock 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 + * ARCHED ROCK 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 Jonathan Hui + * @author David Moss + * @author Jung Il Choi Initial SACK implementation + * @version $Revision$ $Date$ + */ + +module CC2420TransmitP { + + provides interface Init; + provides interface StdControl; + + provides interface Sendframe; + + provides interface ReceiveIndicator as EnergyIndicator; + provides interface ReceiveIndicator as ByteIndicator; + + uses interface GpioCapture as CaptureSFD; + uses interface GeneralIO as CCA; + uses interface GeneralIO as CSN; + uses interface GeneralIO as SFD; + + uses interface Resource as SpiResource; + uses interface ChipSpiResource; + uses interface CC2420Fifo as TXFIFO; + uses interface CC2420Ram as TXFIFO_RAM; + uses interface CC2420Register as TXCTRL; + uses interface CC2420Strobe as SNOP; + uses interface CC2420Strobe as STXON; + uses interface CC2420Strobe as STXONCCA; + uses interface CC2420Strobe as SFLUSHTX; + uses interface CC2420Register as MDMCTRL1; + + + uses interface Leds; +} + +implementation { + + /** Byte reception/transmission indicator */ + bool sfdHigh; + + + /***************** Prototypes ****************/ + + void attemptSend(); + + error_t acquireSpiResource(); + error_t releaseSpiResource(); + void signalDone( error_t err ); + + + /***************** Init Commands *****************/ + command error_t Init.init() { + call CCA.makeInput(); + call CSN.makeOutput(); + call SFD.makeInput(); + return SUCCESS; + } + + /***************** StdControl Commands ****************/ + command error_t StdControl.start() { + atomic { + call CaptureSFD.captureRisingEdge(); + } + + return SUCCESS; + } + + command error_t StdControl.stop() { + atomic { + + call CaptureSFD.disable(); + call SpiResource.release(); // REMOVE + call CSN.set(); + } + return SUCCESS; + } + + +/**************** Send Commands ****************/ + + async command error_t Sendframe.send(uint8_t* frame, uint8_t frame_length) + { + //printfUART("Send Command\n", ""); + + if ( acquireSpiResource() == SUCCESS ) { + call CSN.clr(); + + call TXCTRL.write( ( 2 << CC2420_TXCTRL_TXMIXBUF_CUR ) | + ( 3 << CC2420_TXCTRL_PA_CURRENT ) | + ( 1 << CC2420_TXCTRL_RESERVED ) | + ( (CC2420_DEF_RFPOWER & 0x1F) << CC2420_TXCTRL_PA_LEVEL ) ); + + + call TXFIFO.write( (uint8_t*)frame, frame_length); + + } + return SUCCESS; + } + + + /***************** Indicator Commands ****************/ + command bool EnergyIndicator.isReceiving() { + return !(call CCA.get()); + } + + command bool ByteIndicator.isReceiving() { + bool high; + atomic high = sfdHigh; + return high; + } + + /** + * 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 ) { + + } + + /***************** ChipSpiResource Events ****************/ + async event void ChipSpiResource.releasing() { + } + + + /***************** SpiResource Events ****************/ + event void SpiResource.granted() { + + attemptSend(); + + } + + /***************** 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 ) + { + + call CSN.set(); + attemptSend(); + + } + + + async event void TXFIFO.readDone( uint8_t* tx_buf, uint8_t tx_len,error_t error ) { + } + + + /***************** Functions ****************/ + + + /** + * 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() { + + + + //printfUART("attempt Send Command\n", ""); + + atomic { + + + call CSN.clr(); + + call STXON.strobe(); + + call CSN.set(); + + releaseSpiResource(); + } + + + signalDone(SUCCESS); + + } + + error_t acquireSpiResource() { + error_t error = call SpiResource.immediateRequest(); + + //printfUART("acquire spi\n", ""); + if ( error != SUCCESS ) { + call SpiResource.request(); + } + return error; + } + + error_t releaseSpiResource() { + call SpiResource.release(); + return SUCCESS; + } + + + void signalDone( error_t err ) { + + call ChipSpiResource.attemptRelease(); + signal Sendframe.sendDone( err ); + } + +} + diff --git a/tos/lib/net/zigbee/cc2420/Receiveframe.nc b/tos/lib/net/zigbee/cc2420/Receiveframe.nc new file mode 100644 index 00000000..74831804 --- /dev/null +++ b/tos/lib/net/zigbee/cc2420/Receiveframe.nc @@ -0,0 +1,16 @@ +/** + * Low-level abstraction for the receive path implementaiton of the + * ChipCon CC2420 radio. + * + * @author + * @version + */ + +interface Receiveframe { + + /** + * Signal that a message has been received + */ + async event void receive(uint8_t* frame, uint8_t rssi); + +} diff --git a/tos/lib/net/zigbee/cc2420/Sendframe.nc b/tos/lib/net/zigbee/cc2420/Sendframe.nc new file mode 100644 index 00000000..365dcbed --- /dev/null +++ b/tos/lib/net/zigbee/cc2420/Sendframe.nc @@ -0,0 +1,24 @@ +/** + * Low-level abstraction for the transmit path implementaiton of the + * ChipCon CC2420 radio. + * + * @author + * @version + */ + +interface Sendframe { + + /** + * Send a message + + * @return SUCCESS if the request was accepted, FAIL otherwise. + */ + async command error_t send(uint8_t* frame, uint8_t frame_length); + + /** + * Signal that a message has been sent + */ + async event void sendDone(error_t error ); + +} + diff --git a/tos/lib/net/zigbee/cc2420/readme.txt b/tos/lib/net/zigbee/cc2420/readme.txt new file mode 100644 index 00000000..115427de --- /dev/null +++ b/tos/lib/net/zigbee/cc2420/readme.txt @@ -0,0 +1,8 @@ +Title: ZigBee +Author: André Cunha - IPP-HURRAY! http://www.open-zb.net +---------------------------------------------- + + +Notes: +------ +This folder contains the CC2420 modified files and other additional files. \ No newline at end of file diff --git a/tos/lib/net/zigbee/ieee802154/readme.txt b/tos/lib/net/zigbee/ieee802154/readme.txt new file mode 100644 index 00000000..db38221a --- /dev/null +++ b/tos/lib/net/zigbee/ieee802154/readme.txt @@ -0,0 +1,12 @@ +Title: ZigBee +Author: André Cunha - IPP-HURRAY! http://www.open-zb.net +---------------------------------------------- + + +Notes: +------ +This folder contains the main implementation files. + +Note that the IEEE 802.15.4 MAC is implemented in the mac and timerasync folders and the IEEE 802.15.4 +with the Zigbee Network layer including the Cluster-tree formation with the TDBS mechanism (refer to http://www.open-zb.net/publications/hurray-tr-070102.pdf) +is implemented in the macTDBS and timerasyncTDBS \ No newline at end of file diff --git a/tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsync.nc b/tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsync.nc new file mode 100644 index 00000000..6b3554c8 --- /dev/null +++ b/tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsync.nc @@ -0,0 +1,92 @@ +/* + * @author IPP HURRAY http://www.hurray.isep.ipp.pt/art-wise + * @author Andre Cunha + * + */ + + interface TimerAsync + { + + async command error_t start(); + + async command error_t stop(); + + async command error_t reset(); + + /***********************************FIRED EVENTS COMMANDS******************************/ + //time before BI + async event error_t before_bi_fired(); + + async event error_t sd_fired(); + + async event error_t bi_fired(); + + //backoff fired + async event error_t backoff_fired(); + + //backoff boundary fired + async event error_t time_slot_fired(); + + async event error_t before_time_slot_fired(); + + async event error_t sfd_fired(); + + /***********************************INIT/RESET COMMANDS******************************/ + + async command error_t set_bi_sd(uint32_t bi_symbols,uint32_t sd_symbols); + + async command error_t set_backoff_symbols(uint8_t symbols); + + async command error_t set_enable_backoffs(bool enable_backoffs); + + async command uint8_t reset_start(uint32_t start_ticks); + + async command error_t reset_process_frame_tick_counter(); + + /*****************************************************************************/ + + async command error_t set_timers_enable(uint8_t timer); + + /***********************************GET COMMANDS******************************/ + async command uint32_t get_total_tick_counter(); + + async command uint32_t get_current_number_backoff(); + + async command uint32_t get_time_slot_backoff_periods(); + + async command uint32_t get_current_time_slot(); + + async command uint32_t get_current_number_backoff_on_time_slot(); + + async command uint32_t get_process_frame_tick_counter(); + + async command uint32_t get_time_slot_ticks(); + + async command uint32_t get_current_ticks(); + + async command uint32_t get_sd_ticks(); + + async command uint32_t get_bi_ticks(); + + async command uint32_t get_backoff_ticks(); + + + /*****************************************************************************/ + //TDBS IMPLEMENTATION + /*****************************************************************************/ + + //track beacon interfaces + + async command error_t set_track_beacon(uint8_t track); + + async command error_t set_track_beacon_start_ticks(uint32_t parent_offset_symbols,uint32_t duration_symbols,uint32_t transmission_delay); + + + async event error_t before_start_track_beacon_fired(); + + async event error_t start_track_beacon_fired(); + + async event error_t end_track_beacon_fired(); + + } + diff --git a/tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsyncC.nc b/tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsyncC.nc new file mode 100644 index 00000000..32490333 --- /dev/null +++ b/tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsyncC.nc @@ -0,0 +1,30 @@ +/* + * @author IPP HURRAY http://www.hurray.isep.ipp.pt/art-wise + * @author Andre Cunha + * + */ + + //TIMER ASYNC TELOSB + + +configuration TimerAsyncC +{ + //provides interface StdControl; + provides interface TimerAsync; +} +implementation +{ + + components LedsC; + components TimerAsyncM; + + components new Alarm32khz32C() as Alarm; + + //StdControl = TimerAsyncM; + TimerAsync = TimerAsyncM; + + TimerAsyncM.Leds -> LedsC; + + TimerAsyncM.AsyncTimer -> Alarm; + +} diff --git a/tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsyncM.nc b/tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsyncM.nc new file mode 100644 index 00000000..30b2141f --- /dev/null +++ b/tos/lib/net/zigbee/ieee802154/timerasyncTDBS/TimerAsyncM.nc @@ -0,0 +1,436 @@ +/* + * @author IPP HURRAY http://www.hurray.isep.ipp.pt/art-wise + * @author Andre Cunha + * + */ + +#define BEFORE_BI_INTERVAL 100 +#define BEFORE_BB_INTERVAL 5 + +#define SO_EQUAL_BO_DIFFERENCE 2 + +//#define SYMBOL_DIVISION 4 + +//temporary +#define NUMBER_TIME_SLOTS 16 + +//TDBS Implementation +#define BEFORE_TRACK_BEACON 40 + +module TimerAsyncM { + + provides interface TimerAsync; + + uses interface Leds; + + uses interface Alarm as AsyncTimer; + + + +} +implementation +{ + +uint32_t ticks_counter; + +//BEACON INTERVAL VARIABLES +uint32_t bi_ticks; +uint32_t bi_backoff_periods; +uint32_t before_bi_ticks; +uint32_t sd_ticks; + +//number of backoff periods +uint32_t time_slot_backoff_periods; + +//number of ticks in the timeslot +uint32_t time_slot_ticks; +uint32_t before_time_slot_ticks; +uint32_t time_slot_tick_next_fire; + +//BACKOFF VARIABLES +uint32_t backoff_symbols; + +//number of ticks in the backoff +uint32_t backoff_ticks = 5; + +//COUNTER VARIABLES +uint32_t backoff_ticks_counter=0; + +//give the current time slot number +uint8_t current_time_slot=0; +//counts the current number of time slots of each time slot +uint32_t current_number_backoff_on_time_slot=0; +//count the total number of backoffs +uint32_t current_number_backoff = 0; + +//OTHER +bool backoffs=0; +bool enable_backoffs=0; + +uint8_t previous_sfd=0; +uint8_t current_sfd = 0; + +uint32_t process_frame_tick_counter=0; + +uint32_t total_tick_counter=0; + +uint8_t timers_enable=0x01; + + +//TDBS Implementation + +uint32_t start_track_beacon_ticks=0; +uint32_t end_track_beacon_ticks=0; + +uint8_t track_beacon=0; + + +async command error_t TimerAsync.start() +{ + +call AsyncTimer.start(10); + +return SUCCESS; + +} + +async command error_t TimerAsync.stop() +{ + +return SUCCESS; + +} + +/*RESET the tick counter, */ +async command error_t TimerAsync.reset() +{ + atomic ticks_counter = 0; + call AsyncTimer.start(10); + return SUCCESS; +} + +async command error_t TimerAsync.set_bi_sd(uint32_t bi_symbols,uint32_t sd_symbols) +{ + +atomic{ + time_slot_backoff_periods = (sd_symbols / NUMBER_TIME_SLOTS) / backoff_symbols; + time_slot_ticks = time_slot_backoff_periods * backoff_ticks; + time_slot_tick_next_fire = time_slot_ticks; + before_time_slot_ticks = time_slot_ticks - BEFORE_BB_INTERVAL; + sd_ticks = time_slot_ticks * NUMBER_TIME_SLOTS; + + if (bi_symbols == sd_symbols ) + { + //in order not to have the same time for both BI and SI + sd_ticks = sd_ticks - SO_EQUAL_BO_DIFFERENCE; + } + + bi_backoff_periods = bi_symbols/ backoff_symbols; + bi_ticks = bi_backoff_periods * backoff_ticks; + + before_bi_ticks = bi_ticks - BEFORE_BI_INTERVAL; + + /* + printfUART("bi_ticks %i\n", bi_ticks); + printfUART("sd_ticks %i\n", sd_ticks); + printfUART("time_slot_ticks %i\n", time_slot_ticks); + */ + } +return SUCCESS; +} + + +async command error_t TimerAsync.set_backoff_symbols(uint8_t Backoff_Duration_Symbols) +{ + + atomic + { + backoff_symbols = Backoff_Duration_Symbols; + backoff_ticks = 1; + } + + return SUCCESS; +} + + +async command error_t TimerAsync.set_enable_backoffs(bool enable) +{ + atomic enable_backoffs = enable; + return SUCCESS; +} + + + +async event void AsyncTimer.fired() { + +atomic{ + + if(timers_enable==0x01) + { + + ticks_counter++; + process_frame_tick_counter++; + + total_tick_counter++; + + if (ticks_counter == before_bi_ticks) + { + signal TimerAsync.before_bi_fired(); + } + + if (ticks_counter == bi_ticks) + { + //printfUART("bi%d\n", ticks_counter); + ticks_counter = 0; + current_time_slot=0; + backoff_ticks_counter=0; + time_slot_tick_next_fire=time_slot_ticks; + backoffs=1; + enable_backoffs = 1; + current_number_backoff =0; + signal TimerAsync.bi_fired(); + } + + if(ticks_counter == sd_ticks) + { + backoffs=0; + signal TimerAsync.sd_fired(); + } + + if ((enable_backoffs == 1) && (backoffs == 1)) + { + backoff_ticks_counter++; + + if (backoff_ticks_counter == backoff_ticks) + { + + backoff_ticks_counter=0; + current_number_backoff ++; + current_number_backoff_on_time_slot++; + signal TimerAsync.backoff_fired(); + } + + //before time slot boundary + if(ticks_counter == before_time_slot_ticks) + { + signal TimerAsync.before_time_slot_fired(); + } + + //time slot fired + if (ticks_counter == time_slot_tick_next_fire) + { + time_slot_tick_next_fire = time_slot_tick_next_fire + time_slot_ticks; + before_time_slot_ticks = time_slot_tick_next_fire - BEFORE_BB_INTERVAL; + backoff_ticks_counter=0; + current_number_backoff_on_time_slot=0; + current_time_slot++; + + if ((current_time_slot > 0) && (current_time_slot < 16) ) + signal TimerAsync.time_slot_fired(); + + + + } + } + + + //will only fires when the node is in the inactive period(backoffs==0) and is tracking the beacon + //TDBS Implementation + if(track_beacon == 1) + { + + if(ticks_counter == (start_track_beacon_ticks - BEFORE_TRACK_BEACON)) + { + //backoff_ticks_counter=0; + signal TimerAsync.before_start_track_beacon_fired(); + } + + if(ticks_counter == start_track_beacon_ticks) + { + //backoff_ticks_counter=0; + signal TimerAsync.start_track_beacon_fired(); + } + + backoff_ticks_counter++; + if(backoff_ticks_counter==backoff_ticks) + { + backoff_ticks_counter=0; + signal TimerAsync.backoff_fired(); + } + + if(ticks_counter == end_track_beacon_ticks) + { + signal TimerAsync.end_track_beacon_fired(); + } + } + } + + call AsyncTimer.start(10); + + } +} + + +async command error_t TimerAsync.set_timers_enable(uint8_t timer) +{ + + atomic timers_enable = timer; + //printfUART("te%i\n", timers_enable); + + +return SUCCESS; +} + +async command error_t TimerAsync.reset_process_frame_tick_counter() +{ +atomic process_frame_tick_counter=0; + +return SUCCESS; +} + + + +/*RESET the tick counter, to the start ticks */ + +async command uint8_t TimerAsync.reset_start(uint32_t start_ticks) +{ + //ticks_counter =0; + //ticks_counter = start_ticks; + + current_time_slot = start_ticks / time_slot_ticks; + + if (current_time_slot == 0) + { + time_slot_tick_next_fire= time_slot_ticks; + current_number_backoff = start_ticks / backoff_ticks; + current_number_backoff_on_time_slot = current_number_backoff; + } + else + { + time_slot_tick_next_fire= ((current_time_slot+1) * time_slot_ticks); + current_number_backoff = start_ticks / backoff_ticks; + current_number_backoff_on_time_slot = current_number_backoff - (current_time_slot * time_slot_backoff_periods); + } + + backoff_ticks_counter=0; + backoffs=1; + //on_sync = 1; + + total_tick_counter = total_tick_counter + start_ticks; + ticks_counter = start_ticks; + +/* + printfUART("bi_ticks %i\n", bi_ticks); + printfUART("sd_ticks %i\n", sd_ticks); + printfUART("time_slot_ticks %i\n", time_slot_ticks); + printfUART("total_tick_counter %i\n", total_tick_counter); + printfUART("ticks_counter %i\n", ticks_counter); + printfUART("current_time_slot %i\n", current_time_slot); +*/ + + + + return current_time_slot; + + } + +/***********************************SET COMMANDS******************************/ + +/***********************************GET COMMANDS******************************/ +/*get current clock ticks*/ + +async command uint32_t TimerAsync.get_current_ticks() +{ + return ticks_counter; +} +/*get current sd ticks*/ +async command uint32_t TimerAsync.get_sd_ticks() +{ + return time_slot_ticks * NUMBER_TIME_SLOTS; +} +/*get current clock ticks*/ +async command uint32_t TimerAsync.get_bi_ticks() +{ + return bi_ticks; +} +/*get current backoff ticks*/ +async command uint32_t TimerAsync.get_backoff_ticks() +{ + return backoff_ticks; +} +/*get current time slot ticks*/ +async command uint32_t TimerAsync.get_time_slot_ticks() +{ + return time_slot_ticks; +} + +/*get current backoff ticks*/ +async command uint32_t TimerAsync.get_current_number_backoff() +{ +return current_number_backoff; +} + +async command uint32_t TimerAsync.get_time_slot_backoff_periods() +{ +return time_slot_backoff_periods; +} + +async command uint32_t TimerAsync.get_current_time_slot() +{ +return current_time_slot; +} + + +async command uint32_t TimerAsync.get_current_number_backoff_on_time_slot() +{ + +return current_number_backoff_on_time_slot; + +} + +async command uint32_t TimerAsync.get_total_tick_counter() +{ +return total_tick_counter; +} +async command uint32_t TimerAsync.get_process_frame_tick_counter() +{ + //printfUART("%d\n", process_frame_tick_counter); + +return process_frame_tick_counter; +} + + +//TDBS Implementation +async command error_t TimerAsync.set_track_beacon(uint8_t track) +{ + atomic track_beacon = track; + +return SUCCESS; +} + +async command error_t TimerAsync.set_track_beacon_start_ticks(uint32_t parent_offset_symbols,uint32_t duration_symbols,uint32_t transmission_delay) +{ + +atomic{ + + start_track_beacon_ticks = bi_ticks - ((parent_offset_symbols / backoff_symbols)*backoff_ticks); + + end_track_beacon_ticks = start_track_beacon_ticks + ((duration_symbols / backoff_symbols)*backoff_ticks); + + //verify, the node must synchronyze with the parent beacon offset + ticks_counter = (start_track_beacon_ticks - transmission_delay); + + + //printfUART("start_track_beacon_ticks %i\n", start_track_beacon_ticks); + //printfUART("end_track_beacon_ticks %i\n", end_track_beacon_ticks); + + + } +return SUCCESS; +} + + + + + +} diff --git a/tos/lib/net/zigbee/readme.txt b/tos/lib/net/zigbee/readme.txt new file mode 100644 index 00000000..2c9c5cf5 --- /dev/null +++ b/tos/lib/net/zigbee/readme.txt @@ -0,0 +1,109 @@ +Title: open-zb protocol stack implementation for TinyOS v2.0 +Author: André Cunha - IPP-HURRAY! http://www.open-zb.net +---------------------------------------------- + +Implementation of the ZigBee and the beacon-enabled mode of the IEEE 802.15.4. + +This project is divided in two, the beacon-enabled mode of the IEEE 802.15.4 without any network level +and supporting the synchronized star topology, tested in the MicaZ and TelosB motes and the beacon-enabled +mode of the IEEE 802.15.4 with the ZigBee network layer supporting the Cluster-tree topology. + +The current version of the implementation of the IEEE 802.15.4 beacon enabled mode supports the following functionalities: + +-CSMA/CA algorithm – slotted version; +-GTS Mechanism; +-Indirect transmission mechanism; +-Direct / Indirect / GTS Data Transmission; +-Beacon Management; +-Frame construction – Short Addressing Fields only and extended addressing +fields in the association request; +-Association/Disassociation Mechanism; +-MAC PIB Management; +-Frame Reception Conditions; +-ED and PASSIVE channel scan; + +The following functionalities are not implemented or tested in the current +version of the implementation +-Unslotted version CSMA/CA; +-Extended Address Fields of the Frames; +-IntraPAN Address Fields of the Frames; +-Active and Orphan channel Scan; +-Orphan Devices; +-Frame Reception Conditions (Verify Conditions); +-Security – Out of the scope of this implementation; + +The current version of the ZigBee Network Layer, besides the above functionalities, supports + +-Creation of the Cluster-tree topology (statically defined) using the TDBS (refer to http://www.open-zb.net/publications/hurray-tr-070510.pdf) +-Cluster-tree routing protocol +-Address Assignment +-NWL PIB management + + +Notes: +------ + +The implementation files is organized as follows: + +folders: + +app +- IEEE 802.15.4 test applications + Contains 4 test applications + -AssociationExample - uses the associates with the Coordinator, transmits data messages and dissasociates + -DataSendExample - data transmission example + -GTSManagementExample - Uses the GTS functions to allocate a GTS slot in the Coordinator, sends GTS messages and deallocate the GTS slot + -SimpleRoutingExample - evolution of the DataSendExample used to demonstrate a simple netork layer where two nodes use the Coordinator to route messages +Notes: +The objective of these examples is to +provide a demonstration/testing of the protocol functionalities and allowing a simple +understanding of the implementation functions. +All the examples have a configuration file associated (eg .h) located in +the application folder. The configuration include the type of device (eg +COORDINATOR or END_DEVICE), the logical channel, the beacon order, the +superframe order, the pan id and the device depth in the network (by default all the end +devices have a depth of 1 and the coordinator a depth of 0). + +- ZigBee Network Layer with the TDBS + + -Test_APL +This application uses the interfaces provided by the NWKM component and currently is customized +to use with the TELOSB mote due to the interfacing with the mote user button. The TELOSB mote needs to +“warmup” before entering into normal operational behaviour, so, the user button is used to start the mote +operation either by starting to send beacons, in the case of the ZigBee Coordinator, or to associate to a +network in the case of ZigBee Routers or End Devices. +In order to test the cluster-tree approach we have forced the association to a specific parent device by +assigning some static parameters to the device. These parameters are located in the nwk_const.h file under +the lib.nwk and are the following: +-TYPE_DEVICE – selecting the role of the device in the network; +-DEVICE_DEPTH – selecting the depth of the device in the network. This parameter in be used in +computing the cskip functions used for the address assignment and for the tree-routing. This value +will also be used to select the appropriate parent selected for the association. +Depending of the selected depth the device will select the statically defined parent. The parent values are +assigned in the NLME_NETWORK_DISCOVERY.request primitive.The parents addresses (short address and +extended address) are defined in the following variables: +Activated when the device depth is 0x01 +-D1_PAN_EXT0 0x00000001 +-D1_PAN_EXT1 0x00000001 +-D1_PAN_SHORT 0x0000 +Activated when the device depth is 0x02 +-D2_PAN_EXT0 0x00000002 +-D2_PAN_EXT1 0x00000002 +-D2_PAN_SHORT 0x0001 +Activated when the device depth is 0x03 +-D3_PAN_EXT0 0x00000003 +-D3_PAN_EXT1 0x00000003 +-D3_PAN_SHORT 0x0002 +Activated when the device depth is 0x04 +-D4_PAN_EXT0 0x00000006 +-D4_PAN_EXT1 0x00000006 +-D4_PAN_SHORT 0x0022 +In order for a cluster-tree to work properly there is a need to schedule the beacon frames. This is +done by assigning a time offset to each routers. The device assigned as a ZigBee +Coordinator will accept the negotiation requests for beacon transmission. Upon the reception of these +messages the ZC will execute the process_beacon_scheduling function that already has an offset list for each +device (based on the short address). This function can be replaced with a scheduling algorithm. + +More details in http://www.open-zb.net/publications/hurray-tr-070510.pdf + + -- 2.39.2