From 2209f8d2cb9ae1b6c268532e99a374533eb8fa78 Mon Sep 17 00:00:00 2001 From: "R. Steve McKown" Date: Sun, 19 Sep 2010 15:53:50 -0600 Subject: [PATCH] Add SharedSplitControlC, for N clients needing shared access to something. --- tos/system/SharedSplitControlC.nc | 66 +++++++ tos/system/SharedSplitControlP.nc | 296 ++++++++++++++++++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 tos/system/SharedSplitControlC.nc create mode 100644 tos/system/SharedSplitControlP.nc diff --git a/tos/system/SharedSplitControlC.nc b/tos/system/SharedSplitControlC.nc new file mode 100644 index 00000000..a5254762 --- /dev/null +++ b/tos/system/SharedSplitControlC.nc @@ -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 + */ + +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 index 00000000..a4cc7ce9 --- /dev/null +++ b/tos/system/SharedSplitControlP.nc @@ -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 + */ + +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; + 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) {} +} -- 2.39.2