]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/chips/cc1000/CC1000SendReceiveP.nc
Merge devel code into the trunk.
[tinyos-2.x.git] / tos / chips / cc1000 / CC1000SendReceiveP.nc
diff --git a/tos/chips/cc1000/CC1000SendReceiveP.nc b/tos/chips/cc1000/CC1000SendReceiveP.nc
new file mode 100644 (file)
index 0000000..ec2e851
--- /dev/null
@@ -0,0 +1,677 @@
+// $Id$
+
+/*                                                                     tab:4
+ * "Copyright (c) 2000-2005 The Regents of the University  of California.  
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
+ *
+ * Copyright (c) 2002-2005 Intel Corporation
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached INTEL-LICENSE     
+ * file. If you do not find these files, copies can be found by writing to
+ * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, 
+ * 94704.  Attention:  Intel License Inquiry.
+ */
+#include "message.h"
+#include "crc.h"
+#include "CC1000Const.h"
+#include "Timer.h"
+
+/**
+ * A rewrite of the low-power-listening CC1000 radio stack.
+ * This file contains the send and receive logic for the CC1000 radio.
+ * It does not do any media-access control. It requests the channel
+ * via the ready-to-send event (rts) and starts transmission on reception
+ * of the clear-to-send command (cts). It listens for packets if the
+ * listen() command is called, and stops listening when off() is called.
+ * <p>
+ * This code has some degree of platform-independence, via the
+ * CC1000Control, RSSIADC and SpiByteFifo interfaces which must be provided
+ * by the platform. However, these interfaces may still reflect some
+ * particularities of the mica2 hardware implementation.
+ *
+ * @author Philip Buonadonna
+ * @author Jaein Jeong
+ * @author Joe Polastre
+ * @author David Gay
+ */
+  
+module CC1000SendReceiveP {
+  provides {
+    interface Init;
+    interface StdControl;
+    interface Send;
+    interface Receive;
+    interface RadioTimeStamping;
+    interface Packet;
+    interface ByteRadio;
+    interface PacketAcknowledgements;
+  }
+  uses {
+    //interface PowerManagement;
+    interface CC1000Control;
+    interface HplCC1000Spi;
+
+    interface ReadNow<uint16_t> as RssiRx;
+    async command am_addr_t amAddress();
+  }
+}
+implementation 
+{
+  enum {
+    OFF_STATE,
+
+    INACTIVE_STATE,            /* Not listening, but will accept sends */
+
+    LISTEN_STATE,              /* Listening for packets */
+
+    /* Reception states */
+    SYNC_STATE,
+    RX_STATE,
+    RECEIVED_STATE,
+    SENDING_ACK,
+
+    /* Transmission states */
+    TXPREAMBLE_STATE,
+    TXSYNC_STATE,
+    TXDATA_STATE,
+    TXCRC_STATE,
+    TXFLUSH_STATE,
+    TXWAITFORACK_STATE,
+    TXREADACK_STATE,
+    TXDONE_STATE,
+  };
+
+  enum {
+    SYNC_BYTE1 =       0x33,
+    SYNC_BYTE2 =       0xcc,
+    SYNC_WORD =                SYNC_BYTE1 << 8 | SYNC_BYTE2,
+    ACK_BYTE1 =                0xba,
+    ACK_BYTE2 =                0x83,
+    ACK_WORD =                 ACK_BYTE1 << 8 | ACK_BYTE2,
+    ACK_LENGTH =       16,
+    MAX_ACK_WAIT =     18
+  };
+
+  uint8_t radioState;
+  struct {
+    uint8_t ack : 1;           /* acks enabled? */
+    uint8_t txBusy : 1;                /* send pending? */
+    uint8_t invert : 1;                /* data inverted? (see cc1000 datasheet) */
+    uint8_t rxBitOffset : 3;   /* bit-offset of received bytes */
+  } f; // f for flags
+  uint16_t count;
+  uint16_t runningCrc;
+
+  uint16_t rxShiftBuf;
+  message_t rxBuf;
+  message_t *rxBufPtr = &rxBuf;
+
+  uint16_t preambleLength;
+  message_t *txBufPtr;
+  uint8_t nextTxByte;
+
+  const_uint8_t ackCode[5] = { 0xab, ACK_BYTE1, ACK_BYTE2, 0xaa, 0xaa };
+
+  /* Packet structure accessor functions. Note that everything is
+   * relative to the data field. */
+  cc1000_header_t *getHeader(message_t *amsg) {
+    return (cc1000_header_t *)(amsg->data - sizeof(cc1000_header_t));
+  }
+
+  cc1000_footer_t *getFooter(message_t *amsg) {
+    return (cc1000_footer_t *)(amsg->footer);
+  }
+  
+  cc1000_metadata_t *getMetadata(message_t *amsg) {
+    return (cc1000_metadata_t *)((uint8_t *)amsg->footer + sizeof(cc1000_footer_t));
+  }
+  
+  /* State transition functions */
+  /*----------------------------*/
+
+  void enterOffState() {
+    radioState = OFF_STATE;
+  }
+
+  void enterInactiveState() {
+    radioState = INACTIVE_STATE;
+  }
+
+  void enterListenState() {
+    radioState = LISTEN_STATE;
+    count = 0;
+  }
+
+  void enterSyncState() {
+    radioState = SYNC_STATE;
+    count = 0;
+    rxShiftBuf = 0;
+  }
+
+  void enterRxState() {
+    cc1000_header_t *header = getHeader(rxBufPtr);
+    radioState = RX_STATE;
+    header->length = sizeof rxBufPtr->data;
+    count = sizeof(message_header_t) - sizeof(cc1000_header_t);
+    runningCrc = 0;
+  }
+
+  void enterReceivedState() {
+    radioState = RECEIVED_STATE;
+  }
+
+  void enterAckState() {
+    radioState = SENDING_ACK;
+    count = 0;
+  }
+
+  void enterTxPreambleState() {
+    radioState = TXPREAMBLE_STATE;
+    count = 0;
+    runningCrc = 0;
+    nextTxByte = 0xaa;
+  }
+
+  void enterTxSyncState() {
+    radioState = TXSYNC_STATE;
+  }
+
+  void enterTxDataState() {
+    radioState = TXDATA_STATE;
+    // The count increment happens before the first byte is read from the
+    // packet, so we subtract one from the real packet start point to
+    // compensate.
+    count = (sizeof(message_header_t) - sizeof(cc1000_header_t)) -1; 
+  }
+
+  void enterTxCrcState() {
+    radioState = TXCRC_STATE;
+  }
+    
+  void enterTxFlushState() {
+    radioState = TXFLUSH_STATE;
+    count = 0;
+  }
+    
+  void enterTxWaitForAckState() {
+    radioState = TXWAITFORACK_STATE;
+    count = 0;
+  }
+    
+  void enterTxReadAckState() {
+    radioState = TXREADACK_STATE;
+    rxShiftBuf = 0;
+    count = 0;
+  }
+    
+  void enterTxDoneState() {
+    radioState = TXDONE_STATE;
+  }
+
+  command error_t Init.init() {
+    f.ack = TRUE; /* We always ack, for now at least */
+    call HplCC1000Spi.initSlave();
+    return SUCCESS;
+  }
+
+  command error_t StdControl.start() {
+    atomic 
+      {
+       enterInactiveState();
+       f.txBusy = FALSE;
+       f.invert = call CC1000Control.getLOStatus();
+      }
+    return SUCCESS;
+  }
+
+  command error_t StdControl.stop() {
+    atomic enterOffState();
+    return SUCCESS;
+  }
+
+  /* Send side. Outside requests, SPI handlers for each state */
+  /*----------------------------------------------------------*/
+
+  command error_t Send.send(message_t *msg, uint8_t len) {
+    atomic
+      {
+       if (f.txBusy || radioState == OFF_STATE)
+         return FAIL;
+       else {
+         cc1000_header_t *header = getHeader(msg);
+
+         f.txBusy = TRUE;
+         header->length = len;
+         txBufPtr = msg;
+       }
+      }
+    signal ByteRadio.rts();
+
+    return SUCCESS;
+  }
+
+  async command void ByteRadio.cts() {
+    /* We're set to go! Start with our exciting preamble... */
+    enterTxPreambleState();
+    call HplCC1000Spi.writeByte(0xaa);
+    call CC1000Control.txMode();
+    call HplCC1000Spi.txMode();
+  }
+
+  command error_t Send.cancel(message_t *msg) {
+    /* We simply ignore cancellations. */
+    return FAIL;
+  }
+
+  void sendNextByte() {
+    call HplCC1000Spi.writeByte(nextTxByte);
+    count++;
+  }
+
+  void txPreamble() {
+    sendNextByte();
+    if (count >= preambleLength)
+      {
+       nextTxByte = SYNC_BYTE1;
+       enterTxSyncState();
+      }
+  }
+
+  void txSync() {
+    sendNextByte();
+    nextTxByte = SYNC_BYTE2;
+    enterTxDataState();
+    signal RadioTimeStamping.transmittedSFD(0, txBufPtr); 
+  }
+
+  void txData() {
+    cc1000_header_t *txHeader = getHeader(txBufPtr);
+    sendNextByte();
+    
+    if (count < txHeader->length + sizeof(message_header_t))
+      {
+       nextTxByte = ((uint8_t *)txBufPtr)[count];
+       runningCrc = crcByte(runningCrc, nextTxByte);
+      }
+    else
+      {
+       nextTxByte = runningCrc;
+       enterTxCrcState();
+      }
+  }
+
+  void txCrc() {
+    sendNextByte();
+    nextTxByte = runningCrc >> 8;
+    enterTxFlushState();
+  }
+
+  void txFlush() {
+    sendNextByte();
+    if (count > 3)
+      if (f.ack)
+       enterTxWaitForAckState();
+      else
+       {
+         call HplCC1000Spi.rxMode();
+         call CC1000Control.rxMode();
+         enterTxDoneState();
+       }
+  }
+
+  void txWaitForAck() {
+    sendNextByte();
+    if (count == 1)
+      {
+       call HplCC1000Spi.rxMode();
+       call CC1000Control.rxMode();
+      }
+    else if (count > 3)
+      enterTxReadAckState();
+  }
+
+  void txReadAck(uint8_t in) {
+    uint8_t i;
+
+    sendNextByte();
+
+    for (i = 0; i < 8; i ++)
+      {
+       rxShiftBuf <<= 1;
+       if (in & 0x80)
+         rxShiftBuf |=  0x1;
+       in <<= 1;
+
+       if (rxShiftBuf == ACK_WORD)
+         {
+           getMetadata(txBufPtr)->ack = 1;
+           enterTxDoneState();
+           return;
+         }
+      }
+    if (count >= MAX_ACK_WAIT)
+      {
+       getMetadata(txBufPtr)->ack = 0;
+       enterTxDoneState();
+      }
+  }
+
+  task void signalPacketSent() {
+    message_t *pBuf;
+
+    atomic
+      {
+       pBuf = txBufPtr;
+       f.txBusy = FALSE;
+       enterListenState();
+      }
+    signal Send.sendDone(pBuf, SUCCESS);
+  }
+
+  void txDone() {
+    post signalPacketSent();
+    signal ByteRadio.sendDone();
+  }
+
+  /* Receive */
+  /*---------*/
+
+  void packetReceived();
+  void packetReceiveDone();
+
+  async command void ByteRadio.listen() {
+    enterListenState();
+    call CC1000Control.rxMode();
+    call HplCC1000Spi.rxMode();
+    call HplCC1000Spi.enableIntr();
+  }
+
+  async command void ByteRadio.off() {
+    enterInactiveState();
+    call HplCC1000Spi.disableIntr();
+  }
+
+  void listenData(uint8_t in) {
+    bool preamble = in == 0xaa || in == 0x55;
+
+    // Look for enough preamble bytes
+    if (preamble)
+      {
+       count++;
+       if (count > CC1K_ValidPrecursor)
+         enterSyncState();
+      }
+    else
+      count = 0;
+
+    signal ByteRadio.idleByte(preamble);
+  }
+
+  void syncData(uint8_t in) {
+    // draw in the preamble bytes and look for a sync byte
+    // save the data in a short with last byte received as msbyte
+    //    and current byte received as the lsbyte.
+    // use a bit shift compare to find the byte boundary for the sync byte
+    // retain the shift value and use it to collect all of the packet data
+    // check for data inversion, and restore proper polarity 
+    // XXX-PB: Don't do this.
+
+    if (in == 0xaa || in == 0x55)
+      // It is actually possible to have the LAST BIT of the incoming
+      // data be part of the Sync Byte.  SO, we need to store that
+      // However, the next byte should definitely not have this pattern.
+      // XXX-PB: Do we need to check for excessive preamble?
+      rxShiftBuf = in << 8;
+    else if (count++ == 0)
+      rxShiftBuf |= in;
+    else if (count <= 6)
+      {
+       // TODO: Modify to be tolerant of bad bits in the preamble...
+       uint16_t tmp;
+       uint8_t i;
+
+       // bit shift the data in with previous sample to find sync
+       tmp = rxShiftBuf;
+       rxShiftBuf = rxShiftBuf << 8 | in;
+
+       for(i = 0; i < 8; i++)
+         {
+           tmp <<= 1;
+           if (in & 0x80)
+             tmp  |=  0x1;
+           in <<= 1;
+           // check for sync bytes
+           if (tmp == SYNC_WORD)
+             {
+               enterRxState();
+               signal ByteRadio.rx();
+               f.rxBitOffset = 7 - i;
+               signal RadioTimeStamping.receivedSFD(0);
+               call RssiRx.read();
+             }
+         }
+      }
+    else // We didn't find it after a reasonable number of tries, so....
+      enterListenState();
+  }
+  
+  async event void RssiRx.readDone(error_t result, uint16_t data) {
+    cc1000_metadata_t *rxMetadata = getMetadata(rxBufPtr);
+
+    if (result != SUCCESS)
+      rxMetadata->strength = 0;
+    else
+      rxMetadata->strength = data;
+  }
+
+  void rxData(uint8_t in) {
+    uint8_t nextByte;
+    cc1000_header_t *rxHeader = getHeader(rxBufPtr);
+
+    // rxLength is the offset into a message_t at which the packet
+    // data ends: it is NOT equal to the number of bytes received,
+    // as there may be padding in the message_t before the packet.
+    uint8_t rxLength = rxHeader->length + offsetof(message_t, data);
+
+    // Reject invalid length packets
+    if (rxLength > TOSH_DATA_LENGTH + offsetof(message_t, data))
+      {
+       // The packet's screwed up, so just dump it
+       enterListenState();
+       signal ByteRadio.rxDone();
+       return;
+      }
+
+    rxShiftBuf = rxShiftBuf << 8 | in;
+    nextByte = rxShiftBuf >> f.rxBitOffset;
+    ((uint8_t *)rxBufPtr)[count++] = nextByte;
+
+    if (count <= rxLength)
+      runningCrc = crcByte(runningCrc, nextByte);
+
+    // Jump to CRC when we reach the end of data
+    if (count == rxLength) {
+      count = offsetof(message_t, footer) + offsetof(cc1000_footer_t, crc);
+    }
+
+    if (count == (offsetof(message_t, footer) + sizeof(cc1000_footer_t)))
+      packetReceived();
+  }
+
+  void packetReceived() {
+    cc1000_footer_t *rxFooter = getFooter(rxBufPtr);
+    cc1000_header_t *rxHeader = getHeader(rxBufPtr);
+    // Packet filtering based on bad CRC's is done at higher layers.
+    // So sayeth the TOS weenies.
+    rxFooter->crc = (rxFooter->crc == runningCrc);
+
+    if (f.ack &&
+       rxFooter->crc &&
+       rxHeader->addr == call amAddress())
+      {
+       enterAckState();
+       call CC1000Control.txMode();
+       call HplCC1000Spi.txMode();
+       call HplCC1000Spi.writeByte(0xaa);
+      }
+    else
+      packetReceiveDone();
+  }
+
+  void ackData(uint8_t in) {
+    if (++count >= ACK_LENGTH)
+      { 
+       call CC1000Control.rxMode();
+       call HplCC1000Spi.rxMode();
+       packetReceiveDone();
+      }
+    else if (count >= ACK_LENGTH - sizeof ackCode)
+      call HplCC1000Spi.writeByte(read_uint8_t(&ackCode[count + sizeof ackCode - ACK_LENGTH]));
+  }
+
+  task void signalPacketReceived() {
+    message_t *pBuf;
+    cc1000_header_t *pHeader;
+    atomic
+      {
+       if (radioState != RECEIVED_STATE)
+         return;
+
+       pBuf = rxBufPtr;
+      }
+    pHeader = getHeader(pBuf);
+    pBuf = signal Receive.receive(pBuf, pBuf->data, pHeader->length);
+    atomic
+      {
+       if (pBuf) 
+         rxBufPtr = pBuf;
+       if (radioState == RECEIVED_STATE) // receiver might've done something
+         enterListenState();
+       signal ByteRadio.rxDone();
+      }
+  }
+
+  void packetReceiveDone() {
+    post signalPacketReceived();
+    enterReceivedState();
+  }
+
+  async event void HplCC1000Spi.dataReady(uint8_t data) {
+    if (f.invert)
+      data = ~data;
+
+    switch (radioState)
+      {
+      default: break;
+      case TXPREAMBLE_STATE: txPreamble(); break;
+      case TXSYNC_STATE: txSync(); break;
+      case TXDATA_STATE: txData(); break;
+      case TXCRC_STATE: txCrc(); break;
+      case TXFLUSH_STATE: txFlush(); break;
+      case TXWAITFORACK_STATE: txWaitForAck(); break;
+      case TXREADACK_STATE: txReadAck(data); break;
+      case TXDONE_STATE: txDone(); break;
+
+      case LISTEN_STATE: listenData(data); break;
+      case SYNC_STATE: syncData(data); break;
+      case RX_STATE: rxData(data); break;
+      case SENDING_ACK: ackData(data); break;
+      }
+  }
+
+  /* Interaction with rest of stack */
+  /*--------------------------------*/
+
+  async command void ByteRadio.setPreambleLength(uint16_t bytes) {
+    atomic preambleLength = bytes;
+  }
+
+  async command uint16_t ByteRadio.getPreambleLength() {
+    atomic return preambleLength;
+  }
+
+  async command message_t *ByteRadio.getTxMessage() {
+    return txBufPtr;
+  }
+
+  async command bool ByteRadio.syncing() {
+    return radioState == SYNC_STATE;
+  }
+
+  /* Abstract packet layout */
+
+  command void Packet.clear(message_t *msg) {
+    memset(msg, 0, sizeof(message_t));
+  }
+
+  command uint8_t Packet.payloadLength(message_t *msg) {
+    cc1000_header_t *header = getHeader(msg);
+    return header->length;
+  }
+  command void Packet.setPayloadLength(message_t *msg, uint8_t len) {
+    getHeader(msg)->length  = len;
+  }
+  
+  command uint8_t Packet.maxPayloadLength() {
+    return TOSH_DATA_LENGTH;
+  }
+
+  command void* Packet.getPayload(message_t *msg, uint8_t *len) {
+    if (len != NULL) {
+      cc1000_header_t *header = getHeader(msg);
+
+      *len = header->length;
+    }
+    return (void*)msg->data;
+  }
+
+  async command error_t PacketAcknowledgements.requestAck(message_t *msg) {
+    return SUCCESS;            /* We always ack. */
+  }
+
+  async command error_t PacketAcknowledgements.noAck(message_t *msg) {
+    return FAIL;               /* We always ack */
+  }
+
+  command void* Receive.getPayload(message_t *m, uint8_t *len) {
+    return call Packet.getPayload(m, len);
+  }
+
+  command uint8_t Receive.payloadLength(message_t *m) {
+    return call Packet.payloadLength(m);
+  }
+
+  command uint8_t Send.maxPayloadLength() {
+    return call Packet.maxPayloadLength();
+  }
+
+  command void* Send.getPayload(message_t *m) {
+    return call Packet.getPayload(m, NULL);
+  }
+
+  async command bool PacketAcknowledgements.wasAcked(message_t *msg) {
+    return getMetadata(msg)->ack;
+  }
+  // Default events for radio send/receive coordinators do nothing.
+  // Be very careful using these, or you'll break the stack.
+  default async event void RadioTimeStamping.transmittedSFD(uint16_t time, message_t *msgBuff) { }
+  default async event void RadioTimeStamping.receivedSFD(uint16_t time) { }
+}