]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/chips/xe1205/phy/XE1205PhyP.nc
Merge devel code into the trunk.
[tinyos-2.x.git] / tos / chips / xe1205 / phy / XE1205PhyP.nc
diff --git a/tos/chips/xe1205/phy/XE1205PhyP.nc b/tos/chips/xe1205/phy/XE1205PhyP.nc
new file mode 100644 (file)
index 0000000..fc150ab
--- /dev/null
@@ -0,0 +1,411 @@
+/* 
+ * Copyright (c) 2006, Ecole Polytechnique Federale de Lausanne (EPFL),
+ * Switzerland.
+ * 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 Ecole Polytechnique Federale de Lausanne (EPFL) 
+ *   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 COPYRIGHT
+ * OWNER OR 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 Henri Dubois-Ferriere
+ *
+ */
+
+#include "Timer.h"
+
+module XE1205PhyP {
+  provides interface XE1205PhyRxTx;
+
+  provides interface Init @atleastonce();
+  provides interface SplitControl @atleastonce();
+
+  uses interface Resource as SpiResourceTX;
+  uses interface Resource as SpiResourceRX;
+  uses interface Resource as SpiResourceConfig;
+
+  uses interface XE1205PhySwitch;
+  uses interface XE1205IrqConf;
+  uses interface XE1205Fifo;
+
+  uses interface GpioInterrupt as Interrupt0;
+  uses interface GpioInterrupt as Interrupt1;
+
+  uses interface Alarm<T32khz,uint16_t> as Alarm32khz16;
+#if 0
+  uses interface GeneralIO as Dpin;
+#endif
+}
+implementation {
+
+#include "xe1205debug.h"
+
+  char* txBuf = NULL;
+  uint8_t rxFrameIndex = 0;
+  uint8_t rxFrameLen = 0;
+  uint8_t nextTxLen=0;
+  uint8_t nextRxLen;
+  char rxFrame[xe1205_mtu];
+  uint8_t headerLen = 4;
+
+  uint16_t stats_rxOverruns;
+
+  typedef enum {
+    RADIO_LISTEN=0, 
+    RADIO_RX_HEADER=1, 
+    RADIO_RX_PACKET=2, 
+    RADIO_RX_PACKET_LAST=3, 
+    RADIO_TX=4,
+    RADIO_SLEEP=5, 
+    RADIO_STARTING=6
+  } phy_state_t;
+
+  phy_state_t state = RADIO_SLEEP;
+
+  void armPatternDetect();
+
+  command error_t Init.init() 
+  { 
+#if 0
+    call Dpin.makeOutput();
+#endif
+    call XE1205PhySwitch.sleepMode();
+    call XE1205PhySwitch.antennaOff();
+    return SUCCESS;
+  }
+
+  event void SpiResourceTX.granted() {  }
+  event void SpiResourceRX.granted() {  }
+  event void SpiResourceConfig.granted() { 
+    armPatternDetect();
+    call SpiResourceConfig.release();
+    atomic {
+      call Interrupt0.enableRisingEdge();
+      state = RADIO_LISTEN;
+    }
+  }
+
+
+  task void startDone() {
+    signal SplitControl.startDone(SUCCESS);
+  }
+
+  task void stopDone() {
+    signal SplitControl.stopDone(SUCCESS);
+  }
+
+  command error_t SplitControl.start() 
+  {
+    atomic state = RADIO_STARTING;
+    call XE1205PhySwitch.rxMode();
+    call XE1205PhySwitch.antennaRx();
+    call SpiResourceConfig.request();
+    post startDone();
+    return SUCCESS;
+  }
+
+  command error_t SplitControl.stop() 
+  {
+    call XE1205PhySwitch.sleepMode();
+    call XE1205PhySwitch.antennaOff();
+    atomic state = RADIO_SLEEP;
+    post stopDone();
+    return SUCCESS;
+  }
+
+  default event void SplitControl.startDone(error_t error) { }
+  default event void SplitControl.stopDone(error_t error) { }
+
+  async command bool XE1205PhyRxTx.busy() {
+    atomic return state != RADIO_LISTEN; // xxx need to deal with sleep state
+  }
+
+  void armPatternDetect() 
+  {
+    // small chance of a pattern arriving right after we arm, 
+    // and IRQ0 hasn't been enabled yet, so we would miss the interrupt
+    // xxx maybe this can also be addressed with periodic timer?
+    xe1205check(2, call XE1205IrqConf.armPatternDetector(TRUE));
+    xe1205check(1, call XE1205IrqConf.clearFifoOverrun(TRUE));  
+  }
+
+  async command void XE1205PhyRxTx.setRxHeaderLen(uint8_t l) 
+  {
+    if (l > 8) l = 8;
+    if (!l) return;
+    headerLen = l;
+  }
+
+  async command uint8_t XE1205PhyRxTx.getRxHeaderLen() {
+    return headerLen;
+  }
+
+  void computeNextRxLength() 
+  {
+    uint8_t n = rxFrameLen - rxFrameIndex; 
+    
+    // for timesync and such, we want the end of the packet to coincide with a fifofull event, 
+    // so that we know precisely when last byte was received 
+
+    if (n > 16) {
+      if (n < 32) nextRxLen = n - 16; else nextRxLen = 15;
+    } 
+    else {
+      nextRxLen = n;
+    }
+  }
+
+
+  async command error_t XE1205PhyRxTx.sendFrame(char* data, uint8_t frameLen)  __attribute__ ((noinline)) 
+  {
+    error_t status;
+   
+    if (frameLen < 6) return EINVAL;
+
+    atomic {
+      if (state == RADIO_SLEEP) return EOFF;
+      if (state != RADIO_LISTEN) return EBUSY;
+      if (frameLen == 0 || frameLen > xe1205_mtu + 7) return EINVAL; // 7 = 4 preamble + 3 sync
+      
+      call XE1205PhySwitch.txMode(); // it takes 100us to switch from rx to tx, ie less than one byte at 76kbps
+      call Interrupt0.disable();
+
+      status = call SpiResourceTX.immediateRequest();
+      xe1205check(3, status);
+      if (status != SUCCESS) {
+       call XE1205PhySwitch.rxMode(); 
+       call SpiResourceConfig.request();
+       return status;
+      }
+      call XE1205PhySwitch.antennaTx();
+      state = RADIO_TX;
+    }
+
+    status = call XE1205Fifo.write(data, frameLen);
+    // cannot happen with current SPI implementation (at least with NoDma)
+#if 0
+    if (status != SUCCESS) {
+      xe1205error(8, status);
+      call XE1205PhySwitch.rxMode(); 
+      call XE1205PhySwitch.antennaRx();
+      armPatternDetect();
+      call SpiResourceTX.release();
+      atomic {
+       call Interrupt0.enableRisingEdge();
+       state = RADIO_LISTEN;
+      }
+      return status;
+    }
+#endif
+
+    return SUCCESS;
+  }
+
+
+
+  uint16_t rxByte=0;
+
+  /**
+   * In transmit: nTxFifoEmpty. (ie after the last byte has been *read out of the fifo*)
+   * In receive: write_byte. 
+   */
+  async event void Interrupt0.fired()  __attribute__ ((noinline)) 
+  { 
+    error_t status;
+
+    switch (state) {
+
+    case RADIO_LISTEN:
+      rxByte=1;
+      state = RADIO_RX_HEADER;
+      status = call SpiResourceRX.immediateRequest();
+      xe1205check(4, status);
+      if (status != SUCCESS) {
+       state = RADIO_LISTEN;
+       call Interrupt0.disable(); // because pattern detector won't be rearmed right away
+       call SpiResourceConfig.request();
+       return;
+      }
+      return;
+
+    case RADIO_RX_HEADER:
+      rxByte++;
+      if (rxByte == 2) {
+       call Alarm32khz16.start(3000);
+      }
+
+      if (rxByte == headerLen + 1) {
+       call Interrupt0.disable();
+       xe1205check(8, call XE1205Fifo.read(rxFrame, headerLen));
+       call Interrupt1.enableRisingEdge();
+      }
+      return;
+
+    case RADIO_TX:
+
+      call Interrupt0.disable(); // avoid spurious IRQ0s from nTxFifoEmpty rebounding briefly after first byte is written.
+                                 // note that we should really wait till writedone() to re-enable either interrupt.
+      xe1205check(5, call XE1205Fifo.write(txBuf, nextTxLen));
+      return;
+
+    default:
+      return;
+    }
+  }
+
+  bool reading=FALSE;
+
+
+  /**
+   * In transmit: TxStopped. (ie after the last byte has been *sent*)
+   * In receive: Fifofull.
+   */
+  async event void Interrupt1.fired()  __attribute__ ((noinline)) 
+  { 
+    switch (state) {
+
+    case RADIO_RX_PACKET:
+      reading = TRUE;
+      xe1205check(9, call XE1205Fifo.read(&rxFrame[rxFrameIndex], nextRxLen));
+      call Interrupt1.disable(); // in case it briefly goes back to full just after we read first byte
+      rxFrameIndex += nextRxLen;
+      computeNextRxLength();
+      
+      if (nextRxLen==0) {
+       state = RADIO_RX_PACKET_LAST;
+      }
+
+      return;
+
+    case RADIO_RX_HEADER: // somehow the FIFO has filled before we finished reading the header bytes
+      call Interrupt1.disable();
+      call Alarm32khz16.stop();
+      signal XE1205PhyRxTx.rxFrameEnd(NULL, 0, FAIL);
+      armPatternDetect();
+      call SpiResourceRX.release();
+      atomic {
+       call Interrupt0.enableRisingEdge();
+       state = RADIO_LISTEN;
+      }
+      return;
+
+    case RADIO_TX:
+
+      call Interrupt1.disable();
+      call XE1205PhySwitch.rxMode(); 
+      call XE1205PhySwitch.antennaRx();
+      signal XE1205PhyRxTx.sendFrameDone();
+      armPatternDetect();
+      call SpiResourceTX.release();
+      atomic {
+       call Interrupt0.enableRisingEdge();
+       state = RADIO_LISTEN;
+      }
+      return;
+
+    default:
+      return;
+    }
+  }
+
+  async event void XE1205Fifo.readDone(error_t error) {
+    xe1205check(6, error);
+    switch(state) {
+    case RADIO_RX_HEADER:
+      rxFrameLen = signal XE1205PhyRxTx.rxFrameBegin(rxFrame, headerLen);
+      if (rxFrameLen <= headerLen) {
+       call Interrupt1.disable();
+       call Alarm32khz16.stop();
+       signal XE1205PhyRxTx.rxFrameEnd(NULL, 0, FAIL);
+       armPatternDetect();
+       call SpiResourceRX.release();
+       atomic {
+         call Interrupt0.enableRisingEdge();
+         state = RADIO_LISTEN;
+       }
+       return;
+      }
+
+      rxFrameIndex = headerLen;
+      computeNextRxLength();
+      state = RADIO_RX_PACKET;
+      return;
+
+    case RADIO_RX_PACKET_LAST:
+      call Alarm32khz16.stop();
+      signal XE1205PhyRxTx.rxFrameEnd(rxFrame, rxFrameLen + headerLen, SUCCESS);
+      armPatternDetect(); 
+      call SpiResourceRX.release();
+      atomic {
+       call Interrupt0.enableRisingEdge();
+       state = RADIO_LISTEN;
+      }
+      return;
+
+    case RADIO_RX_PACKET:
+      reading = FALSE;
+      call Interrupt1.enableRisingEdge();
+      return;
+
+    default:
+      xe1205check(10, FAIL);
+      return;
+    }
+  }
+
+  async event void XE1205Fifo.writeDone(error_t error)  __attribute__ ((noinline)) { 
+    xe1205check(7, error);
+    switch(state) {
+    case RADIO_TX:
+      txBuf = signal XE1205PhyRxTx.continueSend(&nextTxLen);
+      if (nextTxLen) {
+       call Interrupt0.enableFallingEdge();
+      } else {
+       call Interrupt0.disable();
+       call Interrupt1.enableRisingEdge();
+      }
+      return;
+    default:
+      xe1205check(11, FAIL);
+    }
+  }
+
+  async event void Alarm32khz16.fired() {
+    stats_rxOverruns++;
+    signal XE1205PhyRxTx.rxFrameEnd(NULL, 0, FAIL);
+    armPatternDetect(); 
+    call SpiResourceRX.release();
+    atomic {
+      call Interrupt0.enableRisingEdge();
+      state = RADIO_LISTEN;
+    }
+  }
+
+}
+
+
+