]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/chips/msp430/usci/Msp430SpiP.nc
Fix race conditions when SPI peripheral is unconfigured during activity.
[tinyos-2.x.git] / tos / chips / msp430 / usci / Msp430SpiP.nc
index 922b7cff2bd79b802af4cc7b3b9802918d9c52f2..b230f607cab60ca2035626a302686ff5932f2b27 100644 (file)
@@ -32,6 +32,8 @@
  *
  * TODO: Implement error checking via UCxxSTAT
  *
+ * NOTE: Define NO_REN_ON_SPI to disable PxREN bits when SPI is acquired.
+ *
  * @author R. Steve McKown <rsmckown@gmail.com>
  */
  
@@ -57,10 +59,13 @@ implementation {
     BLOCKSIZE_DEFAULT = 64,
 
     /* Bit positions in m_pins */
-    PINS_STE = 1,
+    PINS_STE = 0,
     PINS_SOMI,
     PINS_SIMO,
     PINS_CLK,
+#ifdef NO_REN_ON_SPI
+    PINS_RENADDR,      /* This gets added to store the PxREN bit */
+#endif
   };
 
   uint8_t m_pins;
@@ -107,6 +112,26 @@ implementation {
 
       /* Configure pins for SPI, saving prior pin states */
       m_pins = 0;
+#ifdef NO_REN_ON_SPI
+      /* - First save off and disable PxREN bits */
+      if (is4pin() && call STE.isRen()) {
+       m_pins |= (1 << (PINS_STE + PINS_RENADDR));
+       call STE.disableRen();
+      }
+      if (call SOMI.isRen()) {
+       m_pins |= (1 << (PINS_SOMI + PINS_RENADDR));
+       call SOMI.disableRen();
+      }
+      if (call SIMO.isRen()) {
+       m_pins |= (1 << (PINS_SIMO + PINS_RENADDR));
+       call SIMO.disableRen();
+      }
+      if (call CLK.isRen()) {
+       m_pins |= (1 << (PINS_CLK + PINS_RENADDR));
+       call CLK.disableRen();
+      }
+#endif
+      /* - Then save off IOFunc state and enable ModuleFunc */
       if (is4pin() && call STE.isIOFunc()) {
        m_pins |= (1 << PINS_STE);
        call STE.selectModuleFunc();
@@ -133,17 +158,31 @@ implementation {
     }
   }
 
+  task void signalSendDone()
+  {
+    atomic {
+      uint16_t len = m_len;
+      m_len = 0;
+      signal SpiPacket.sendDone(m_txBuf, m_rxBuf, len, SUCCESS);
+    }
+  }
+
   async command void ResourceConfigure.unconfigure()
   {
     atomic {
       /* Disable the device */
       call Registers.setCtl1(UCSWRST);
 
+      /* Ensure SpiPacket.sendDone is posted if a trx was in progress */
+      if (m_len)
+       post signalSendDone();
+
       /* Clear interrupts and interrupt flags.  We only used Rx */
       call Registers.clrIeRx();
       call Registers.clrIfgRx();
 
       /* Restore pins to their pre-configure state */
+      /* - First restore IOFunc states */
       if (is4pin() && (m_pins & (1 << PINS_STE)))
        call STE.selectIOFunc();
       if (m_pins & (1 << PINS_SIMO))
@@ -152,6 +191,17 @@ implementation {
        call SOMI.selectIOFunc();
       if (m_pins & (1 << PINS_CLK))
        call CLK.selectIOFunc();
+      /* - Then restore PxREN bits */
+#ifdef NO_REN_ON_SPI
+      if (is4pin() && (m_pins & (1 << (PINS_STE + PINS_RENADDR))))
+       call STE.enableRen();
+      if (m_pins & (1 << (PINS_SIMO + PINS_RENADDR)))
+       call SIMO.enableRen();
+      if (m_pins & (1 << (PINS_SOMI + PINS_RENADDR)))
+       call SOMI.enableRen();
+      if (m_pins & (1 << (PINS_CLK + PINS_RENADDR)))
+       call CLK.enableRen();
+#endif
     }
   }
 
@@ -167,13 +217,51 @@ implementation {
     }
   }
 
-  void sendData()
+  bool waitOnRx()
+  {
+    if (call Registers.getCtl0(UCMST)) {
+      while (!call Registers.getIfgRx() && !call Registers.getCtl1(UCSWRST));
+      return TRUE;
+    } else {
+      /* If SPI slave, the host could quit clocking any time, so we need a
+       * timeout.
+       */
+      unsigned i = 0;
+
+      while (++i) {
+       if (call Registers.getIfgRx())
+         return TRUE;
+       if (call Registers.getCtl1(UCSWRST))
+         return FALSE;
+      }
+      return FALSE;
+    }
+  }
+
+  bool waitOnTx()
+  {
+    if (call Registers.getCtl0(UCMST)) {
+      while (!call Registers.getIfgTx() && !call Registers.getCtl1(UCSWRST));
+      return TRUE;
+    } else {
+      /* If SPI slave, the host could quit clocking any time, so we need a
+       * timeout.
+       */
+      unsigned i = 0;
+
+      while (++i) {
+       if (call Registers.getIfgTx())
+         return TRUE;
+       if (call Registers.getCtl1(UCSWRST))
+         return FALSE;
+      }
+      return FALSE;
+    }
+  }
+
+  /* Return FALSE if we are in reset, so callers can clean up as appropriate. */
+  bool sendData()
   {
-    /* We don't need to check Registers.getIfgTx() (aks UCxxTXIFG), as
-     * sendData() is only called after peripheral init or receipt of the rx()
-     * interrupt.  SPI on msp430 guarantees UCxxTXIFG is asserted in both of
-     * these cases.
-     */
     atomic {
       uint16_t end = m_pos + (blockSize ? blockSize : BLOCKSIZE_DEFAULT);
       uint8_t tmp;
@@ -182,12 +270,14 @@ implementation {
        end = m_len;
       call Registers.setTxbuf(m_txBuf ? m_txBuf[m_pos] : 0);
       while (++m_pos < end) {
-       while (!call Registers.getIfgRx() && !call Registers.getCtl1(UCSWRST));
+       waitOnRx();
        tmp = call Registers.getRxbuf();
        if (m_rxBuf)
-         m_rxBuf[m_pos - 1] = call Registers.getRxbuf();
+         m_rxBuf[m_pos - 1] = tmp;
+       waitOnTx();
        call Registers.setTxbuf(m_txBuf ? m_txBuf[m_pos] : 0);
       }
+      return call Registers.getCtl1(UCSWRST) ? FALSE : TRUE;
     }
   }
 
@@ -202,34 +292,30 @@ implementation {
        m_rxBuf = rxBuf;
        m_len = len;
        m_pos = 0;
-       call Registers.setIeRx();
-       sendData();
-       return SUCCESS;
+       if (sendData()) {
+         call Registers.setIeRx();
+         return SUCCESS;
+       } else {
+         m_len = 0;
+         return FAIL;
+       }
       }
     }
   }
 
   async event void Interrupts.tx() {}
 
-  task void signalSendDone()
-  {
-    atomic {
-      m_len = 0;
-      signal SpiPacket.sendDone(m_txBuf, m_rxBuf, m_len, SUCCESS);
-    }
-  }
-
   async event void Interrupts.rx(uint8_t byte)
   {
     if (m_rxBuf)
       m_rxBuf[m_pos - 1] = byte;
 
-    if (m_pos < m_len)
-      sendData();
-    else {
-      call Registers.clrIeRx();
-      post signalSendDone(); /* Don't signal from ISR context */
+    if (m_pos < m_len) {
+      if (sendData())
+       return;
     }
+    call Registers.clrIeRx();
+    post signalSendDone(); /* Don't signal from ISR context */
   }
 
   default async event void SpiPacket.sendDone(uint8_t*, uint8_t*, uint16_t,