Skip to content

lib shell

jmattsson edited this page Jan 19, 2012 · 3 revisions

Patch for lib/shell for future reference.

GitHub's wiki is really ticking me off... it shows up fine on the preview, but hit Save and it all shrinks up to a single-line. /facepalm

diff --git a/apps/tests/TestSerialShell/BusyShellCmdC.nc b/apps/tests/TestSerialShell/BusyShellCmdC.nc
new file mode 100644==
index 0000000..2c3b3eb
--- /dev/null
+++ b/apps/tests/TestSerialShell/BusyShellCmdC.nc
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+module BusyShellCmdC
+{
+  provides interface ShellExecute;
+  uses interface ShellOutput;
+}
+implementation
+{
+  command error_t ShellExecute.execute (uint8_t argc, const char *argv[])
+  {
+    return EBUSY;
+  }
+
+  command void ShellExecute.abort () {}
+
+  event void ShellOutput.outputDone () {}
+}
diff --git a/apps/tests/TestSerialShell/CancelmeShellCmdC.nc b/apps/tests/TestSerialShell/CancelmeShellCmdC.nc
new file mode 100644
index 0000000..cfaf7f1
--- /dev/null
+++ b/apps/tests/TestSerialShell/CancelmeShellCmdC.nc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+module CancelmeShellCmdC
+{
+  provides interface ShellExecute;
+  uses interface ShellOutput;
+}
+implementation
+{
+  command error_t ShellExecute.execute (uint8_t argc, const char *argv[])
+  {
+    return SUCCESS;
+  }
+
+  command void ShellExecute.abort ()
+  {
+    signal ShellExecute.executeDone (ECANCEL);
+  }
+
+  event void ShellOutput.outputDone () {}
+}
diff --git a/apps/tests/TestSerialShell/Makefile b/apps/tests/TestSerialShell/Makefile
new file mode 100644
index 0000000..a31f900
--- /dev/null
+++ b/apps/tests/TestSerialShell/Makefile
@@ -0,0 +1,5 @@
+COMPONENT=TestSerialShellC
+
+CFLAGS += -I$(TOSDIR)/lib/shell -I$(TOSDIR)/lib/shell/preconf
+
+include $(MAKERULES)
diff --git a/apps/tests/TestSerialShell/README b/apps/tests/TestSerialShell/README
new file mode 100644
index 0000000..7b0c661
--- /dev/null
+++ b/apps/tests/TestSerialShell/README
@@ -0,0 +1,4 @@
+A small application demonstrating how to use lib/shell to put together
+a serial command line shell easily.
+
+Also see tos/lib/shell for examples on how to implement actual shell commands.
diff --git a/apps/tests/TestSerialShell/TestSerialShellC.nc b/apps/tests/TestSerialShell/TestSerialShellC.nc
new file mode 100644
index 0000000..912a86f
--- /dev/null
+++ b/apps/tests/TestSerialShell/TestSerialShellC.nc
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+#include "ShellCommand.h"
+
+configuration TestSerialShellC
+{
+}
+implementation
+{
+  components TestSerialShellP as App, MainC, PlatformSerialC;
+  App.Boot -> MainC;
+  App.UartStream -> PlatformSerialC;
+
+  // Pre-configured serial commands
+  components HelpSerialCmdC, UptimeSerialCmdC;
+
+
+  // Custom commands with hand-wiring. Note naming of PlatformSerialShellC.
+  components PlatformSerialShellC as SerialShell;
+  components BusyShellCmdC, CancelmeShellCmdC;
+
+  WIRE_SHELL_COMMAND("busy", BusyShellCmdC, SerialShell);
+  WIRE_SHELL_COMMAND("cancelme", CancelmeShellCmdC, SerialShell);
+}
diff --git a/apps/tests/TestSerialShell/TestSerialShellP.nc b/apps/tests/TestSerialShell/TestSerialShellP.nc
new file mode 100644
index 0000000..816ac9d
--- /dev/null
+++ b/apps/tests/TestSerialShell/TestSerialShellP.nc
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+module TestSerialShellP
+{
+  uses interface Boot;
+  uses interface UartStream;
+}
+implementation
+{
+  event void Boot.booted ()
+  {
+    const char msg[] = "TestSerialShell Booted\r\n";
+    call UartStream.send ((uint8_t *)msg, sizeof(msg) -1);
+  }
+
+  async event void UartStream.receivedByte (uint8_t byte) {}
+  async event void UartStream.sendDone (uint8_t *buf, uint16_t len, error_t res) {}
+  async event void UartStream.receiveDone (uint8_t *buf, uint16_t len, error_t res) {}
+}
diff --git a/apps/tests/TestVariantPool/Makefile b/apps/tests/TestVariantPool/Makefile
new file mode 100644
index 0000000..cdf021e
--- /dev/null
+++ b/apps/tests/TestVariantPool/Makefile
@@ -0,0 +1,4 @@
+COMPONENT=TestAppC
+PFLAGS += -I$(TOSDIR)/lib/printf
+#CFLAGS += -DVARIANT_POOL_DEBUG
+include $(MAKERULES)
diff --git a/apps/tests/TestVariantPool/TestAppC.nc b/apps/tests/TestVariantPool/TestAppC.nc
new file mode 100644
index 0000000..1f12281
--- /dev/null
+++ b/apps/tests/TestVariantPool/TestAppC.nc
@@ -0,0 +1,12 @@
+configuration TestAppC {
+} implementation {
+  components TestP;
+  components MainC;
+
+  TestP.Boot -> MainC;
+  
+  components new VariantPoolC (64) as VPool;
+  TestP.VPool -> VPool;
+
+#include <unittest/config_impl.h>
+}
diff --git a/apps/tests/TestVariantPool/TestP.nc b/apps/tests/TestVariantPool/TestP.nc
new file mode 100644
index 0000000..4062f79
--- /dev/null
+++ b/apps/tests/TestVariantPool/TestP.nc
@@ -0,0 +1,76 @@
+#include <stdio.h>
+
+module TestP {
+  uses interface Boot;
+  uses interface VariantPool as VPool;
+#include <unittest/module_spec.h>
+} implementation {
+#include <unittest/module_impl.h>
+
+#define POOL_SIZE (64 - 2)
+
+  void assert_available (size_t size)
+  {
+    void *t;
+    size_t lt = 0;
+    t = call VPool.reserve (&lt);
+    ASSERT_TRUE(t != NULL);
+    ASSERT_TRUE(lt = size);
+    call VPool.release (t);
+  }
+
+  event void Boot.booted () {
+    void *a1, *a2, *a3;
+    size_t l1, l2, l3;
+
+    // Reserve full pool
+    a1 = call VPool.reserve (&l1);
+    ASSERT_TRUE(a1 != NULL);
+    ASSERT_EQUAL(l1, POOL_SIZE);
+
+    // Reserve from empty pool
+    l2 = 0xfade;
+    a2 = call VPool.reserve (&l2);
+    ASSERT_TRUE(!a2);
+    ASSERT_EQUAL(l2, 0xfade); // a failed reserve should not touch len ptr
+
+    // Alloc from empty pool
+    a2 = call VPool.alloc (1);
+    ASSERT_TRUE(!a2);
+    ASSERT_EQUAL(l2, 0xfade); // a failed alloc should not touch len ptr
+
+    // Reduce reservation
+    l1 = 4;
+    call VPool.reduce (a1, l1);
+    a2 = call VPool.reserve (&l2);
+    ASSERT_TRUE(a2 != NULL);
+    ASSERT_EQUAL(l2, POOL_SIZE - l1 - 2);
+
+    // Alloc from empty pool
+    l3 = 1;
+    a3 = call VPool.alloc (l3);
+    ASSERT_TRUE(!a3);
+
+    // Reduce reservation
+    l2 = 15;
+    call VPool.reduce (a2, l2);
+
+    // Alloc from pool
+    l3 = 34;
+    a3 = call VPool.alloc (l3);
+    ASSERT_TRUE(a3 != NULL);
+
+    // Release in order a3, a1, a2 to exercise most merge scenarios
+    call VPool.release (a3);
+    assert_available (POOL_SIZE - l1 - l2 - 2*2);
+
+    call VPool.release (a1);
+    // a1 was only a small unmerged chunk, should have no impact on reserve
+    assert_available (POOL_SIZE - l1 - l2 - 2*2);
+
+    call VPool.release (a2);
+    assert_available (POOL_SIZE);
+
+    ALL_TESTS_PASSED();
+  }
+}
diff --git a/tos/interfaces/VariantPool.nc b/tos/interfaces/VariantPool.nc
new file mode 100644
index 0000000..e0be2a4
--- /dev/null
+++ b/tos/interfaces/VariantPool.nc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+/**
+ * The VariantPool is an alternative to Pool and malloc(). It provides access
+ * to a compile-time bounded memory area (unlike malloc), but unlike Pool
+ * it allows allocations of varying sizes.
+ *
+ * Additionally, it provides the capability for allocations of unknown size
+ * via the reserve() function. Calling reserve() allocates as much contiguous
+ * memory in the pool that it can, and returns it and the actual size
+ * allocated. When the desired size is learnt, the allocation can (and should)
+ * then be reduced to said size by calling reduce(). This releases the excess
+ * memory back into the pool. A typical use case for this is for formatting
+ * buffers, where temporary memory is desired for short periods of time.
+ *
+ * Any memory obtained from alloc() or reserve() MUST be released back to the
+ * pool through release(). Calling reduce(p, 0) is NOT a substitute.
+ */
+interface VariantPool
+{
+  /**
+   * Allocates memory of the requested size from the pool, if possible.
+   * Any memory allocated MUST Be returned to the pool by calling release().
+   *
+   * @param len The length (in bytes) of memory requested.
+   * @return A pointer to the allocated memory, or zero if the allocation
+   *  request could not be satisfied.
+   */
+  command void *alloc (size_t len);
+
+  /**
+   * Allocates as much contiguous memory from the pool as it can, possibly
+   * the entirety of the pool. Memory allocated this way SHOULD be reduced
+   * to the minimum required as soon as said minimum is discovered. This
+   * SHOULD happen before the current task completes (as doing otherwise
+   * might leave other tasks starved).
+   *
+   * @param actual_len A pointer to a length field where the actual allocation
+   *  length will be stored. Only updated if memory is actually reserved.
+   * @return A pointer to the allocated memory, or zero if the request could
+   *  not be satisfied.
+   */
+  command void *reserve (size_t *actual_len);
+
+  /**
+   * Reduces previously reserved (or allocated) memory to a smaller size,
+   * returning the excess memory to the pool.
+   *
+   * @param p Pointer to memory previously obtained from alloc() or reserve().
+   * @param newlen The size to reduce the allocation to. It is not possible
+   *  to increase the allocation by supplying a larger size than originally
+   *  allocated.
+   */
+  command void reduce (void *p, size_t newlen);
+
+  /**
+   * Returns previously allocated memory to the pool.
+   *
+   * @param p The memory to return to the pool. MUST have been previously
+   *  returned from alloc() or reserve().
+   */
+  command void release (void *p);
+}
diff --git a/tos/lib/shell/CommandLineParser.nc b/tos/lib/shell/CommandLineParser.nc
new file mode 100644
index 0000000..4b3daa6
--- /dev/null
+++ b/tos/lib/shell/CommandLineParser.nc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+interface CommandLineParser
+{
+  /**
+   * Parses the contents of a zero-terminated character buffer, splitting it
+   * into arguments and building up an argc/argv pair.
+   *
+   * @param str Pointer to the string to parse. MUST be zero-terminated.
+   * @param argc In: contains the length of the argv array.
+   *   Out: number of entries used in argv.
+   * @param argv The argv array, to receive the argument pointers.
+   * @return SUCCESS if the string could be parsed.
+   */
+  command error_t parse (char *str, uint8_t *argc, char *argv[]);
+}
diff --git a/tos/lib/shell/HelpShellCmdC.nc b/tos/lib/shell/HelpShellCmdC.nc
new file mode 100644
index 0000000..48bdddf
--- /dev/null
+++ b/tos/lib/shell/HelpShellCmdC.nc
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+generic configuration HelpShellCmdC()
+{
+  provides interface ShellExecute;
+  uses interface ShellCommand as CommandList[uint8_t id];
+  uses interface ShellOutput;
+}
+implementation
+{
+  components new HelpShellCmdP() as Cmd, ScratchAreaC;
+
+  Cmd.Scratch -> ScratchAreaC;
+
+  ShellExecute = Cmd;
+  ShellOutput = Cmd;
+  CommandList = Cmd;
+}
diff --git a/tos/lib/shell/HelpShellCmdP.nc b/tos/lib/shell/HelpShellCmdP.nc
new file mode 100644
index 0000000..0a8ac24
--- /dev/null
+++ b/tos/lib/shell/HelpShellCmdP.nc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+#include <stdio.h>
+
+generic module HelpShellCmdP()
+{
+  provides interface ShellExecute;
+  uses interface ShellOutput;
+  uses interface ShellCommand[uint8_t id];
+  uses interface VariantPool as Scratch;
+}
+implementation
+{
+  char *buf = 0;
+
+  command error_t ShellExecute.execute (uint8_t argc, const char *argv[])
+  {
+    size_t len = 0;
+    uint8_t id = 0;
+    char *p;
+    int ret;
+
+    if (buf)
+      return EBUSY;
+
+    buf = p = call Scratch.reserve (&len);
+
+    while (len)
+    {
+      const char *str = call ShellCommand.getCommandString[id++] ();
+      if (!str || !*str)
+        break;
+
+      ret = snprintf (p, len, "%s\r\n", str);
+      if (ret <= 0 || (ret > len))
+        break;
+
+      len -= ret;
+      p += ret;
+    }
+
+    call Scratch.reduce (buf, p - buf);
+    return call ShellOutput.output (buf, p - buf);
+  }
+
+  command void ShellExecute.abort () {}
+
+  event void ShellOutput.outputDone ()
+  {
+    call Scratch.release (buf);
+    buf = 0;
+    signal ShellExecute.executeDone (SUCCESS);
+  }
+}
diff --git a/tos/lib/shell/README b/tos/lib/shell/README
new file mode 100644
index 0000000..060ce48
--- /dev/null
+++ b/tos/lib/shell/README
@@ -0,0 +1,57 @@
+The lib/shell components provide a (hopefully) convenient way of implementing
+simple command shells.
+
+It was inspired by lib/net/blip/shell, but differs in the following ways:
+
+  * Command execution is split-phase. Long running commands are easy to
+    implement, as executeDone() must be signalled to allow the next command
+    to be processed by the shell.
+
+  * Commands may be interrupted/aborted. Long running commands can thus
+    stop reposting themselves, and signal early completion (ECANCEL).
+
+  * Command output is split-phase. Commands wishing to return lots of
+    information can simply wait to be told when it's possible to output
+    the next chunk of data. Obtaining the maximum chunk of output possible
+    in each go is also supported, allowing commands to optimize their
+    output (e.g. to maximize payload/headers ratio for network shells).
+
+  * Support for different types of shells are possible, e.g. serial, UDP.
+    A command does not need to know what type of shell it will be used with.
+
+  * With care, commands can be shared across multiple shells (just be
+    careful to either support reentrancy or disallow it altogether).
+
+  * The output interface is separate from the command interface, and can be
+    wired to support diagnostics/snooping of command activity, or even
+    support blind terminals.
+
+  * Command line parsing is delegated to its own module, allowing for easy
+    extension of features if so desired (environment vars, loops, etc).
+
+  * Limited interactive line editing supported in the SerialShellC.
+    Non-interactive shell types (e.g. UDP-based ones) don't need this,
+    and don't incur the overhead of this support.
+
+Of course, there are some downsides:
+
+  * Increased wiring requirements. As there can be more than one shell,
+    commands cannot auto-wire themselves to The One True Shell.
+    To alleviate this, the macros in ShellCommand.h abstract away the
+    intricacies, and pre-configured components can be provided for the
+    typical use case (e.g. single UDP shell or serial interface using
+    PlatformSerialC).
+
+  * To support multiple concurrent shells, the commands must be written
+    to handle reentrancy. For long-running/posting commands, this involves
+    keeping extra state around, or alternatively they can simply detect
+    attempts at reentrancy and return EBUSY.
+
+  * If the output is fanned out, a lot of care must be taken to not send
+    multiple outputDone() signals, or to reference the output string after
+    an outputDone() event has been signalled. Diagnostic/snoop wiring
+    should be passive (not signal outputDone()), and copy the output into
+    its own buffers straight away.
+
+For an example of how to make use of lib/shell, have a look at the
+TestSerialShell application in apps/tests/TestSerialShell.
diff --git a/tos/lib/shell/SerialShellC.nc b/tos/lib/shell/SerialShellC.nc
new file mode 100644
index 0000000..1024850
--- /dev/null
+++ b/tos/lib/shell/SerialShellC.nc
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+generic module SerialShellC(uint8_t num_cmds)
+{
+  provides interface Init;
+  provides interface ShellOutput[uint8_t id];
+  provides interface ShellCommand as CommandList[uint8_t id];
+
+  uses interface ShellCommand[uint8_t id];
+  uses interface ShellExecute[uint8_t id];
+  uses interface CommandLineParser;
+  uses interface UartStream;
+}
+implementation
+{
+  enum { NO_CMD = 0xff };
+  enum { ABORT_KEY = 0x03 }; // ctrl-c
+
+  typedef enum {
+    PROMPT_BARE, PROMPT_OK, PROMPT_BUSY, PROMPT_FAIL, PROMPT_ABORT,
+    PROMPT_UNKNOWN, PROMPT_SYNTAX
+  } prompt_t;
+
+  prompt_t new_prompt;
+
+  uint8_t cur_cmd = NO_CMD;
+  bool printing_prompt = FALSE;
+  volatile bool buffer_locked = FALSE;
+
+  #ifndef SERIAL_SHELL_BUFFER_SIZE
+  #define SERIAL_SHELL_BUFFER_SIZE 80
+  #endif
+
+  char recv_buf[SERIAL_SHELL_BUFFER_SIZE] = { 0 };
+  char *rx = recv_buf;
+
+  #ifndef SERIAL_SHELL_MAX_ARGS
+  #define SERIAL_SHELL_MAX_ARGS 9
+  #endif
+
+  char *argv[SERIAL_SHELL_MAX_ARGS];
+
+  void print_prompt (prompt_t type)
+  {
+    static const char *prompts[] = {
+      "# ",
+      "OK\r\n# ",
+      "BUSY\r\n# ",
+      "FAILED\r\n# ",
+      "^C\r\n# ",
+      "UNKNOWN\r\n# ",
+      "SYNTAX ERROR\r\n# "
+    };
+    const char *prompt = prompts[type];
+
+    printing_prompt =
+      (call UartStream.send ((uint8_t *)prompt, strlen (prompt)) == SUCCESS) ?
+        TRUE : FALSE;
+  }
+
+  inline prompt_t error_to_prompt (error_t result)
+  {
+    switch (result)
+    {
+      case SUCCESS: return PROMPT_OK;
+      case EBUSY: return PROMPT_BUSY;
+      case ECANCEL: return PROMPT_ABORT;
+      default: return PROMPT_FAIL;
+    }
+  }
+
+  task void cmd_exit ()
+  {
+    print_prompt (new_prompt);
+    if (!printing_prompt)
+    {
+      post cmd_exit ();
+      return;
+    }
+  }
+
+  command error_t Init.init ()
+  {
+    return call UartStream.enableReceiveInterrupt ();
+  }
+
+ 
+  command error_t ShellOutput.output[uint8_t id] (const char *str, size_t len)
+  {
+    if (id != cur_cmd)
+      return SUCCESS; // Note: use SUCCESS to work with ecombine nicely
+    else
+      return call UartStream.send ((uint8_t *)str, len);
+  }
+
+  command size_t ShellOutput.limit[uint8_t id] ()
+  {
+    // We don't need to buffer/packetize the output, so no real limit
+    return (size_t)-1;
+  }
+
+  event void ShellExecute.executeDone[uint8_t id] (error_t result)
+  {
+    if (id != cur_cmd)
+      return; // not our execute
+
+    new_prompt = error_to_prompt (result);
+    post cmd_exit ();
+  }
+
+
+  task void abort_requested ()
+  {
+    if (cur_cmd != NO_CMD)
+      call ShellExecute.abort[cur_cmd] ();
+    else
+      print_prompt (PROMPT_ABORT);
+  }
+
+  task void output_done ()
+  {
+    if (printing_prompt)
+    {
+      printing_prompt = FALSE;
+      cur_cmd = NO_CMD;
+      atomic buffer_locked = FALSE;
+    }
+    else
+      if (cur_cmd != NO_CMD)
+        signal ShellOutput.outputDone[cur_cmd] ();
+  }
+
+  task void parse_command ()
+  {
+    uint8_t argc = SERIAL_SHELL_MAX_ARGS;
+    error_t res =
+      call CommandLineParser.parse (recv_buf, &argc, argv);
+
+    if (res == SUCCESS)
+    {
+      uint8_t i;
+
+      if (!argc || !*argv[0]) // no command, just reprint prompt
+      {
+        new_prompt = PROMPT_BARE;
+        goto no_run;
+      }
+
+      for (i = 0; i < num_cmds; ++i)
+      {
+        if (strcmp (argv[0], call ShellCommand.getCommandString[i] ()) == 0)
+          break;
+      }
+
+      if (i == num_cmds)
+        new_prompt = PROMPT_UNKNOWN; // command not found
+      else
+      {
+        error_t result;
+        cur_cmd = i;
+        result = call ShellExecute.execute[i] (argc, (const char **)argv);
+        if (result == SUCCESS)
+          return; // now running a command
+        new_prompt = error_to_prompt (result);
+      }
+    }
+    else
+      new_prompt = PROMPT_SYNTAX;
+
+  no_run:
+    post cmd_exit ();
+  }
+
+
+  async event void UartStream.sendDone (uint8_t *buf, uint16_t len, error_t res)
+  {
+    post output_done ();
+  }
+
+  async event void UartStream.receiveDone (uint8_t *buf, uint16_t len, error_t res) {}
+
+  async event void UartStream.receivedByte (uint8_t byte)
+  {
+    if (byte == ABORT_KEY)
+    {
+      rx = recv_buf;
+      post abort_requested ();
+      return;
+    }
+
+    if (buffer_locked)
+      return;
+
+    if (byte < ' ' || byte == 0x7f)
+    {
+      switch (byte)
+      {
+        case 0x08: // Backspace
+        case 0x7f: // Delete
+          if (rx > recv_buf)
+            --rx;
+          break;
+        case '\r':
+        case '\n':
+          *rx = 0;
+          rx = recv_buf;
+          buffer_locked = TRUE;
+          post parse_command ();
+          break;
+        default: break;
+      }
+      return;
+    }
+
+    if (rx < (recv_buf + sizeof (recv_buf) -1))
+      *rx++ = byte;
+  }
+
+  command const char *CommandList.getCommandString[uint8_t id] ()
+  {
+    return call ShellCommand.getCommandString[id] ();
+  }
+
+  default command const char *ShellCommand.getCommandString[uint8_t id] ()
+  {
+    return "";
+  }
+
+  default command error_t ShellExecute.execute[uint8_t id] (uint8_t argc, const char *argv_[])
+  {
+    return FAIL;
+  }
+
+  default command void ShellExecute.abort[uint8_t id] () {}
+  default event void ShellOutput.outputDone[uint8_t id] () {}
+}
diff --git a/tos/lib/shell/ShellCommand.h b/tos/lib/shell/ShellCommand.h
new file mode 100644
index 0000000..b35b6cb
--- /dev/null
+++ b/tos/lib/shell/ShellCommand.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+#ifndef _SHELL_COMMAND_H_
+#define _SHELL_COMMAND_H_
+
+#define WIRE_SHELL_COMMAND(cmdstring, component, shell) \
+    enum { SHELL_CMD_ ## component = unique("shell.cmd." #shell) }; \
+    components new ShellCommandC (cmdstring) as ShellCommand_ ## component; \
+    shell.ShellCommand[SHELL_CMD_ ## component] -> ShellCommand_ ## component; \
+    shell.ShellExecute[SHELL_CMD_ ## component] -> component; \
+    shell.ShellOutput[SHELL_CMD_ ## component] <- component
+
+#define NUM_COMMANDS_FOR_SHELL(shell) uniqueCount("shell.cmd." #shell)
+
+#endif
diff --git a/tos/lib/shell/ShellCommand.nc b/tos/lib/shell/ShellCommand.nc
new file mode 100644
index 0000000..38396e3
--- /dev/null
+++ b/tos/lib/shell/ShellCommand.nc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+interface ShellCommand
+{
+  command const char *getCommandString ();
+}
diff --git a/tos/lib/shell/ShellCommandC.nc b/tos/lib/shell/ShellCommandC.nc
new file mode 100644
index 0000000..65d3c13
--- /dev/null
+++ b/tos/lib/shell/ShellCommandC.nc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+generic module ShellCommandC(const char commandString[])
+{
+  provides interface ShellCommand;
+}
+implementation
+{
+  command const char *ShellCommand.getCommandString ()
+  {
+    return commandString;
+  }
+}
diff --git a/tos/lib/shell/ShellExecute.nc b/tos/lib/shell/ShellExecute.nc
new file mode 100644
index 0000000..ee36c86
--- /dev/null
+++ b/tos/lib/shell/ShellExecute.nc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+interface ShellExecute
+{
+  command error_t execute (uint8_t argc, const char *argv[]);
+
+  // note: must still signal executeDone()
+  command void abort ();
+
+  event void executeDone (error_t result);
+}
diff --git a/tos/lib/shell/ShellOutput.nc b/tos/lib/shell/ShellOutput.nc
new file mode 100644
index 0000000..3d8b034
--- /dev/null
+++ b/tos/lib/shell/ShellOutput.nc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+interface ShellOutput
+{
+  command error_t output (const char *str, size_t len);
+
+  // note: only signalled if outputn returned SUCCESS
+  event void outputDone ();
+
+  // maximum length output which can be successfully submitted
+  command size_t limit ();
+}
diff --git a/tos/lib/shell/SimpleCommandParserC.nc b/tos/lib/shell/SimpleCommandParserC.nc
new file mode 100644
index 0000000..5604e83
--- /dev/null
+++ b/tos/lib/shell/SimpleCommandParserC.nc
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+/**
+ * A simple command line parser, supporting double-quotes and backslash
+ * escapes. Only the <space> character is considered a delimiter.
+ */
+generic module SimpleCommandParserC()
+{
+  provides interface CommandLineParser;
+}
+implementation
+{
+
+  command error_t CommandLineParser.parse (char *str, uint8_t *argc, char *argv[])
+  {
+    uint8_t max_args = *argc;
+    char *p;
+    bool escaped = FALSE, inquote = FALSE, inspace = TRUE;
+
+    *argc = 0;
+
+    for (p = str; *p && *argc < max_args; ++p)
+    {
+      if (escaped)
+      {
+        escaped = FALSE;
+        continue;
+      }
+      if (*p == ' ' && !inquote && !escaped)
+      {
+        inspace = TRUE;
+        *p = 0;
+        continue;
+      }
+      if (inspace && !escaped && *p != ' ')
+      {
+        inspace = FALSE;
+        argv[(*argc)++] = p;
+        continue;
+      }
+      if (*p == '\\')
+      {
+        escaped = TRUE;
+        continue;
+      }
+      if (*p == '"')
+      {
+        inquote = TRUE;
+        continue;
+      }
+    }
+
+    if (escaped || inquote)
+      return EINVAL;
+    else if (*p)
+      return ENOMEM;
+    else
+      return SUCCESS;
+  }
+
+}
diff --git a/tos/lib/shell/UptimeShellCmdC.nc b/tos/lib/shell/UptimeShellCmdC.nc
new file mode 100644
index 0000000..a1f67d0
--- /dev/null
+++ b/tos/lib/shell/UptimeShellCmdC.nc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+configuration UptimeShellCmdC
+{
+  provides interface ShellExecute;
+  uses interface ShellOutput;
+}
+implementation
+{
+  components UptimeShellCmdP as Cmd, LocalTimeMilliC, ScratchAreaC;
+
+  Cmd.LocalTime -> LocalTimeMilliC;
+  Cmd.Scratch -> ScratchAreaC;
+
+  ShellExecute = Cmd;
+  ShellOutput = Cmd;
+}
diff --git a/tos/lib/shell/UptimeShellCmdP.nc b/tos/lib/shell/UptimeShellCmdP.nc
new file mode 100644
index 0000000..6e2e505
--- /dev/null
+++ b/tos/lib/shell/UptimeShellCmdP.nc
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+#include <stdio.h>
+
+module UptimeShellCmdP
+{
+  provides interface ShellExecute;
+  uses interface ShellOutput;
+  uses interface LocalTime<TMilli>;
+  uses interface VariantPool as Scratch;
+}
+implementation
+{
+  char *buf = 0;
+
+  command error_t ShellExecute.execute (uint8_t argc, const char *argv[])
+  {
+    size_t len = 0;
+    int ret;
+
+    if (buf)
+      return EBUSY;
+
+    buf = call Scratch.reserve (&len);
+    ret = snprintf (buf, len, "Uptime: %lus\r\n", call LocalTime.get () >> 10);
+    if (ret <= 0)
+    {
+      call Scratch.release (buf);
+      return FAIL;
+    }
+
+    call Scratch.reduce (buf, ret);
+    return call ShellOutput.output (buf, ret);
+  }
+
+  command void ShellExecute.abort () {}
+
+  event void ShellOutput.outputDone ()
+  {
+    call Scratch.release (buf);
+    buf = 0;
+    signal ShellExecute.executeDone (SUCCESS);
+  }
+}
diff --git a/tos/lib/shell/preconf/HelpSerialCmdC.nc b/tos/lib/shell/preconf/HelpSerialCmdC.nc
new file mode 100644
index 0000000..4308dc9
--- /dev/null
+++ b/tos/lib/shell/preconf/HelpSerialCmdC.nc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+#include "ShellCommand.h"
+
+configuration HelpSerialCmdC
+{
+}
+implementation
+{
+  components PlatformSerialShellC as SerialShell, new HelpShellCmdC();
+  WIRE_SHELL_COMMAND("help", HelpShellCmdC, SerialShell);
+  HelpShellCmdC.CommandList -> SerialShell;
+}
diff --git a/tos/lib/shell/preconf/PlatformSerialShellC.nc b/tos/lib/shell/preconf/PlatformSerialShellC.nc
new file mode 100644
index 0000000..857ad02
--- /dev/null
+++ b/tos/lib/shell/preconf/PlatformSerialShellC.nc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+/**
+ * Provides a pre-wired serial shell, using PlatformSerialC.
+ *
+ * The name to register commands against is SerialShell, e.g.:
+ *   components PlatformSerialShellC as SerialShell;
+ *   WIRE_SHELL_COMMAND("mycmd", MyCmdC, SerialShell);
+ */
+configuration PlatformSerialShellC
+{
+  provides interface ShellOutput[uint8_t id];
+  provides interface ShellCommand as CommandList[uint8_t id];
+
+  uses interface ShellCommand[uint8_t id];
+  uses interface ShellExecute[uint8_t id];
+}
+implementation
+{
+  components
+    new SerialShellC(NUM_COMMANDS_FOR_SHELL(SerialShell)) as SerialShell,
+    new SimpleCommandParserC(),
+    PlatformSerialC,
+    MainC;
+
+  SerialShell.UartStream -> PlatformSerialC;
+  SerialShell.CommandLineParser -> SimpleCommandParserC;
+  SerialShell.Init <- MainC.SoftwareInit;
+
+  ShellOutput = SerialShell.ShellOutput;
+  CommandList = SerialShell.CommandList;
+
+  ShellCommand = SerialShell.ShellCommand;
+  ShellExecute = SerialShell.ShellExecute;
+}
diff --git a/tos/lib/shell/preconf/UptimeSerialCmdC.nc b/tos/lib/shell/preconf/UptimeSerialCmdC.nc
new file mode 100644
index 0000000..73a6735
--- /dev/null
+++ b/tos/lib/shell/preconf/UptimeSerialCmdC.nc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+#include "ShellCommand.h"
+
+configuration UptimeSerialCmdC
+{
+}
+implementation
+{
+  components PlatformSerialShellC as SerialShell, UptimeShellCmdC;
+  WIRE_SHELL_COMMAND("uptime", UptimeShellCmdC, SerialShell);
+}
diff --git a/tos/system/ScratchAreaC.nc b/tos/system/ScratchAreaC.nc
new file mode 100644
index 0000000..f59073d
--- /dev/null
+++ b/tos/system/ScratchAreaC.nc
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+configuration ScratchAreaC
+{
+  provides interface VariantPool;
+}
+implementation
+{
+  components new VariantPoolC(128) as Scratch;
+  VariantPool = Scratch;
+}
diff --git a/tos/system/VariantPoolC.nc b/tos/system/VariantPoolC.nc
new file mode 100644
index 0000000..05ecf58
--- /dev/null
+++ b/tos/system/VariantPoolC.nc
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2012 Johny Mattsson
+ * 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 copyright holders 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 HOLDER 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.
+ */
+
+generic module VariantPoolC(size_t pool_size)
+{
+  provides interface VariantPool;
+}
+implementation
+{
+  typedef struct list_node
+  {
+    size_t len;  // space available to user (i.e. excluding this field)
+    struct list_node *next;  // link field, also start of user data when alloc'd
+  } list_node_t;
+
+  union {
+    list_node_t first_node;
+    char raw[pool_size];
+  } pool = { { pool_size - sizeof (size_t), 0 } };
+
+  list_node_t *free_list = &pool.first_node;
+
+
+#ifdef VARIANT_POOL_DEBUG
+  void dump_free_list (const char *where)
+  {
+    list_node_t *node = free_list;
+    printf("free_list[%s]: ", where);
+    for (; node; node = node->next)
+    {
+      printf("%u@%p->%p ", node->len, node, node->next);
+    }
+    printf("\r\n");
+  }
+#else
+  #define dump_free_list(x)
+#endif
+
+
+  inline bool can_split (list_node_t *node, size_t newlen)
+  {
+    size_t min_size = sizeof (list_node_t *);
+    return (newlen < node->len) &&
+           (newlen > min_size) &&
+           (node->len - newlen) > min_size;
+  }
+
+  inline void *node_to_data (list_node_t *node)
+  {
+    return &node->next;
+  }
+
+  inline list_node_t *data_to_node (void *p)
+  {
+    return (list_node_t *)(((char *)p) - sizeof (size_t));
+  }
+
+  inline void *end_of_node (list_node_t *node)
+  {
+    return (char *)node + node->len + sizeof (size_t);
+  }
+
+  void unlink (list_node_t *node)
+  {
+    list_node_t **where = &free_list;
+
+    while (*where)
+    {
+      list_node_t *cur = *where;
+      if (cur == node)
+      {
+        *where = cur->next;
+        return;
+      }
+      else
+        where = &cur->next;
+    }
+  }
+
+  void merge_with_next (list_node_t *node)
+  {
+    if (node && end_of_node (node) == node->next)
+    {
+      node->len += node->next->len + sizeof (size_t);
+      node->next = node->next->next;
+    }
+  }
+
+  void link (list_node_t *node)
+  {
+    list_node_t **where = &free_list, *prev = 0;
+
+    while (*where && node > *where)
+    {
+      prev = *where;
+      where = &(*where)->next;
+    }
+
+    node->next = *where;
+    *where = node;
+
+    merge_with_next (node);
+    merge_with_next (prev);
+  }
+
+  command void *VariantPool.alloc (size_t len)
+  {
+    list_node_t *cur, *best;
+
+    if (!free_list)
+      return 0;
+
+    dump_free_list("alloc-in");
+
+    best = free_list;
+    for (cur = free_list->next; cur; cur = cur->next)
+    {
+      if (cur->len >= len && cur->len < best->len)
+        best = cur;
+    }
+
+    if (best->len < len)
+      return 0;
+
+    unlink (best);
+
+    // don't return unnecessarily large chunks
+    call VariantPool.reduce (&best->next, len);
+
+    dump_free_list("alloc-out");
+
+    return node_to_data (best);
+  }
+
+  command void *VariantPool.reserve (size_t *actual_len)
+  {
+    list_node_t *cur, *best;
+    if (!actual_len || !free_list)
+      return 0;
+
+    dump_free_list("reserve-in");
+
+    best = free_list;
+    for (cur = free_list->next; cur; cur = cur->next)
+    {
+      if (cur->len > best->len)
+        best = cur;
+    }
+
+    unlink (best);
+
+    dump_free_list("reserve-out");
+
+    *actual_len = best->len;
+    return node_to_data (best);
+  }
+
+  command void VariantPool.reduce (void *p, size_t newlen)
+  {
+    list_node_t *node = data_to_node (p);
+
+    dump_free_list("reduce-in");
+
+    // if we can't split this block, reduce is a no-op
+    if (can_split (node, newlen))
+    {
+      list_node_t *fragment = (list_node_t *)((char *)p + newlen);
+      fragment->len = node->len - newlen - sizeof (size_t);
+      node->len = newlen;
+      call VariantPool.release (node_to_data (fragment));
+    }
+
+    dump_free_list("reduce-out");
+  }
+
+  command void VariantPool.release (void *p)
+  {
+    list_node_t *node = data_to_node (p);
+
+    dump_free_list("release-in");
+
+    if (node)
+      link (node);
+
+    dump_free_list("release-out");
+  }
+}
Clone this wiki locally