-
Notifications
You must be signed in to change notification settings - Fork 14
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 (<); + 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"); + } +}