]> oss.titaniummirror.com Git - tinyos-2.x.git/commitdiff
Add SharedSplitControlC, for N clients needing shared access to something.
authorR. Steve McKown <rsmckown@gmail.com>
Sun, 19 Sep 2010 21:53:50 +0000 (15:53 -0600)
committerR. Steve McKown <rsmckown@gmail.com>
Mon, 9 Jul 2012 16:34:43 +0000 (10:34 -0600)
tos/system/SharedSplitControlC.nc [new file with mode: 0644]
tos/system/SharedSplitControlP.nc [new file with mode: 0644]

diff --git a/tos/system/SharedSplitControlC.nc b/tos/system/SharedSplitControlC.nc
new file mode 100644 (file)
index 0000000..a525476
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2010, Titanium Mirror, Inc.
+ * 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 Technische Universität Berlin 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.
+ */
+
+/**
+ * Allows multiple clients, who all need to have some underlying component
+ * started, to share the control of a SplitControl (MasterControl) interface.
+ * Any client successfully starting with SplitControl.start() / startDone()
+ * means the MasterControl state is on/started.  That state is retained until
+ * the last client successfully completes SplitControl.stop() / stopDone(),
+ * at which time the MasterControl state will be off/stopped.
+ *
+ * @param p_deferStopTime is the number of mibbiseconds the implementation
+ * waits after all clients have completed stop operations before the device
+ * wired to MasterControl is actually stopped.  Ths allows implementations to
+ * control 'flapping' of the state of the underlying device.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+generic configuration SharedSplitControlC(char resourceName[],
+    uint16_t p_deferStopTime) {
+  provides interface SplitControl[uint8_t id];
+  uses interface SplitControl as MasterControl;
+}
+implementation {
+  enum { CLIENTS = uniqueCount(resourceName) };
+
+  components new SharedSplitControlP(CLIENTS, p_deferStopTime);
+  SplitControl = SharedSplitControlP;
+  MasterControl = SharedSplitControlP;
+
+  components MainC;
+  MainC.SoftwareInit -> SharedSplitControlP;
+
+  components new TimerMilliC();
+  SharedSplitControlP.Timer -> TimerMilliC;
+
+  components new StateC();
+  SharedSplitControlP.State -> StateC;
+}
diff --git a/tos/system/SharedSplitControlP.nc b/tos/system/SharedSplitControlP.nc
new file mode 100644 (file)
index 0000000..a4cc7ce
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2010, Titanium Mirror, Inc.
+ * 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 Technische Universität Berlin 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.
+ */
+
+/**
+ * Allows multiple clients, who all need to have some underlying component
+ * started, to share the control of a SplitControl (MasterControl) interface.
+ * Any client successfully starting with SplitControl.start() / startDone()
+ * means the MasterControl state is on/started.  That state is retained until
+ * the last client successfully completes SplitControl.stop() / stopDone(),
+ * at which time the MasterControl state will be off/stopped.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+generic module SharedSplitControlP(uint8_t p_clients,
+    uint16_t p_deferStopTime) {
+  provides {
+    interface Init;
+    interface SplitControl[uint8_t id];
+  }
+  uses {
+    interface SplitControl as MasterControl;
+    interface Timer<TMilli>;
+    interface State;
+  }
+}
+implementation {
+  enum {
+    /* MasterControl states */
+    S_IDLE = 0,
+    S_START,
+    S_ON,
+    S_STOPWAIT,
+    S_STOP,
+
+    /* S_STOPWAIT is special, for the master only.  After all clients have
+     * stopped, the master could be stopped.  But, we leave it on in the
+     * S_STOPWAIT state for a little bit before actually stopping it, so that
+     * we can reduce start/stop thrashing in our application.  This behavior
+     * is controlled by the p_deferStopTime parameter.
+     */
+  };
+
+  uint8_t m_state[p_clients];
+
+  command error_t Init.init()
+  {
+    return SUCCESS;
+  }
+
+  task void signalStarts()
+  {
+    unsigned id;
+
+    for (id = 0; id < p_clients; id++) {
+      if (m_state[id] == S_START) {
+       m_state[id] = S_ON;
+       signal SplitControl.startDone[id](SUCCESS);
+      }
+    }
+  }
+
+  command error_t SplitControl.start[uint8_t id]()
+  {
+    error_t error;
+
+    if (id >= p_clients)
+      return FAIL;
+
+    switch (m_state[id]) {
+      case S_IDLE:
+       switch (call State.getState()) {
+         case S_IDLE:
+           error = call MasterControl.start();
+           if (error == SUCCESS) {
+             call State.forceState(S_START);
+             m_state[id] = S_START;
+           }
+           return error;
+           break;
+         case S_START:
+           m_state[id] = S_START;
+           return SUCCESS;
+           break;
+         case S_STOPWAIT:
+           call Timer.stop();
+           call State.forceState(S_ON);
+           /* fall through */
+         case S_ON:
+           m_state[id] = S_START;
+           post signalStarts();
+           return SUCCESS;
+           break;
+         case S_STOP:
+           /* Master will restart as soon as its stop is done */
+           m_state[id] = S_START;
+           return SUCCESS;
+           break;
+         default:
+           /* Should never get here */
+           return FAIL;
+       }
+       break;
+      case S_START:
+       return SUCCESS;
+       break;
+      case S_ON:
+       return EALREADY;
+       break;
+      case S_STOP:
+      default:
+       return EBUSY;
+       break;
+    }
+  }
+
+  event void MasterControl.startDone(error_t error)
+  {
+    unsigned id;
+    unsigned newState = (error == SUCCESS) ? S_ON : S_IDLE;
+
+    /* Master is starting, implying >= 1 client starting and all others are off.
+     * Master and all starting clients transition to the on state if the start
+     * was successful, or fall back to the idle (off) state if not.
+     */
+    call State.forceState(newState);
+    for (id = 0; id < p_clients; id++) {
+      if (m_state[id] == S_START) {
+       m_state[id] = newState;
+       signal SplitControl.startDone[id](error);
+      }
+    }
+  }
+
+  task void signalStops()
+  {
+    unsigned id;
+    bool allOff = TRUE;
+
+    for (id = 0; id < p_clients; id++) {
+      if (m_state[id] == S_STOP) {
+       m_state[id] = S_IDLE;
+       signal SplitControl.stopDone[id](SUCCESS);
+      } else if (m_state[id] != S_IDLE)
+       allOff = FALSE;
+    }
+    if (allOff) {
+      /* All clients are off.  We can stop the master, but we might wait a bit
+       * before stopping the master.
+       */
+      if (p_deferStopTime) {
+       call State.forceState(S_STOPWAIT);
+       call Timer.startOneShot(p_deferStopTime);
+      } else {
+       if (call MasterControl.stop() == SUCCESS)
+         call State.forceState(S_STOP);
+      }
+    }
+  }
+
+  command error_t SplitControl.stop[uint8_t id]()
+  {
+    if (id >= p_clients)
+      return FAIL;
+
+    switch (m_state[id]) {
+      case S_IDLE:
+       return EALREADY;
+       break;
+      case S_ON:
+       switch (call State.getState()) {
+         case S_ON:
+           /* fall through */
+         case S_STOPWAIT:
+         case S_IDLE:
+           /* Invalid condition.  If the master is off, then all clients
+            * should also be off.  Go ahead signal the client to stop.
+            */
+           m_state[id] = S_STOP;
+           post signalStops();
+           return SUCCESS;
+           break;
+         case S_START:
+         case S_STOP:
+         default:
+           /* Invalid condition.  For the master to be starting, all clients
+            * must either be off or starting.  For the master to be stopping,
+            * no client can be on.  We can generally correct this invalid
+            * condition by setting the client to S_STOP and dealing with it
+            * in MasterControl.startDone() or stopDone() (respectively).
+            */
+           m_state[id] = S_STOP;
+           return SUCCESS;
+           break;
+       }
+       break;
+      case S_STOP:
+       return SUCCESS;
+       break;
+      case S_START:
+      default:
+       return EBUSY;
+       break;
+    }
+    /* If we get here, we had an invalid state combination */
+  }
+
+  event void Timer.fired()
+  {
+    error_t error = call MasterControl.stop();
+
+    if (error == SUCCESS)
+      call State.forceState(S_STOP);
+    else {
+      unsigned id;
+
+      /* If we failed to start, we need to signal any starting clients */
+      for (id = 0; id < p_clients; id++) {
+       if (m_state[id] == S_START) {
+         m_state[id] = S_ON;
+         signal SplitControl.startDone[id](SUCCESS);
+       }
+      }
+    }
+  }
+
+  event void MasterControl.stopDone(error_t error)
+  {
+    unsigned id;
+    unsigned newState = (error == SUCCESS) ? S_IDLE : S_ON;
+    bool starts = FALSE;
+
+    /* Master is stopping, implying >= 1 client stopping.  Other clients may
+     * be in any other state.  Master and all stopping clients transition to
+     * the off state if the start was successful, or fall back to the on state
+     * if not.
+     */
+    call State.forceState(newState);
+    for (id = 0; id < p_clients; id++) {
+      if (m_state[id] == S_STOP) {
+       m_state[id] = newState;
+       signal SplitControl.stopDone[id](error);
+      } else if (m_state[id] == S_START)
+       starts = TRUE;
+    }
+
+    /* While the master was stopping, some clients that were off may have
+     * issued a start.  It is possible that we need to start the master again.
+     * If the master start() fails, all clients in start get kicked back to
+     * off, in error.
+     */
+    if (starts) {
+      error = call MasterControl.start();
+
+      if (error == SUCCESS)
+       call State.forceState(S_START);
+      else {
+       for (id = 0; id < p_clients; id++) {
+         if (m_state[id] == S_START) {
+           m_state[id] = S_IDLE;
+           signal SplitControl.startDone[id](error);
+         }
+       }
+      }
+    }
+  }
+
+  default event void SplitControl.startDone[uint8_t id](error_t error) {}
+  default event void SplitControl.stopDone[uint8_t id](error_t error) {}
+}