diff --git a/NanodeUIP.cpp b/NanodeUIP.cpp index aec8343..4e77df8 100644 --- a/NanodeUIP.cpp +++ b/NanodeUIP.cpp @@ -1,5 +1,7 @@ #include "NanodeUIP.h" +#include +#include #include #include "uip.h" @@ -8,12 +10,15 @@ #include "dhcpc.h" #include "resolv.h" -void nanode_log(char *msg); void nanode_log(char *msg) { Serial.println(msg); } +void nanode_log_P(PGM_P msg) { + printf_P(PSTR("%lu: %S\r\n"),millis(),msg); +} + void dhcpc_configured(const struct dhcpc_state *s) { uip_sethostaddr(s->ipaddr); uip_setnetmask(s->netmask); @@ -93,14 +98,14 @@ void NanodeUIP::set_nameserver_addr(byte a, byte b, byte c, byte d) { // Requires a buffer of at least 18 bytes to format into void NanodeUIP::get_mac_str(char *buf) { - sprintf(buf,"%02X:%02X:%02X:%02X:%02X:%02X", + sprintf_P(buf,PSTR("%02X:%02X:%02X:%02X:%02X:%02X"), uip_ethaddr.addr[0], uip_ethaddr.addr[1], uip_ethaddr.addr[2], uip_ethaddr.addr[3], uip_ethaddr.addr[4], uip_ethaddr.addr[5]); } // Requires a buffer of at least 16 bytes to format into void NanodeUIP::format_ipaddr(char *buf,uint16_t *addr) { - sprintf(buf,"%d.%d.%d.%d",addr[0]&0xff,addr[0]>>8, + sprintf_P(buf,PSTR("%d.%d.%d.%d"),addr[0]&0xff,addr[0]>>8, addr[1]&0xff,addr[1]>>8); } diff --git a/NanodeUIP.h b/NanodeUIP.h index 5d424b7..a0f9526 100644 --- a/NanodeUIP.h +++ b/NanodeUIP.h @@ -6,11 +6,14 @@ #else #include // Arduino 0022 #endif +#include #include "uip.h" #include "timer.h" extern void resolv_conf(const uint16_t *dnsserver); +extern void nanode_log(char *msg); +extern void nanode_log_P(PGM_P msg); #define DHCP_STATUS_OK 1 #define DHCP_STATUS_DOWN 0 diff --git a/examples/telnet_server/Jamfile b/examples/telnet_server/Jamfile new file mode 100644 index 0000000..9f7a3bd --- /dev/null +++ b/examples/telnet_server/Jamfile @@ -0,0 +1,224 @@ +# (1) Project Information + +PROJECT_LIBS = SPI NanodeUIP NanodeUNIO ; +PROJECT_DIRS = $(PWD) ; + +# (2) Board Information + +UPLOAD_PROTOCOL ?= arduino ; +UPLOAD_SPEED ?= 57600 ; +MCU ?= atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. + +if $(OS) = MACOSX +{ + AVR_BIN = /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN = /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; +} + +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; + +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +AR = $(AVR_BIN)/avr-ar rcs ; +RANLIB = ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE = $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; + +# Search everywhere for headers + +HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; + +# Output locations + +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; + +# +# Custom rules +# + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +actions Link bind NEEDLIBS +{ + $(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(NEEDLIBS) $(LINKLIBS) $(>) +} + +rule Library +{ + LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule Upload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; +} + +actions UploadAction +{ + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +rule Arduino +{ + LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; + #LinkLibraries $(<) : core libs ; + Main $(<) : $(>) ; + Hex $(<:B).hex : $(<) ; + for _p in $(PORTS) + { + Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; + } +} + +# +# Targets +# + +# Grab everything from the core directory +#Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +#Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# Main output executable +Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/examples/telnet_server/memb.c b/examples/telnet_server/memb.c new file mode 100644 index 0000000..777b52f --- /dev/null +++ b/examples/telnet_server/memb.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: memb.c,v 1.1 2006/06/12 08:21:43 adam Exp $ + */ + +/** + * \addtogroup memb + * @{ + */ + + /** + * \file + * Memory block allocation routines. + * \author Adam Dunkels + */ +#include + +#include "memb.h" + +/*---------------------------------------------------------------------------*/ +void +memb_init(struct memb_blocks *m) +{ + memset(m->count, 0, m->num); + memset(m->mem, 0, m->size * m->num); +} +/*---------------------------------------------------------------------------*/ +void * +memb_alloc(struct memb_blocks *m) +{ + int i; + + for(i = 0; i < m->num; ++i) { + if(m->count[i] == 0) { + /* If this block was unused, we increase the reference count to + indicate that it now is used and return a pointer to the + memory block. */ + ++(m->count[i]); + return (void *)((char *)m->mem + (i * m->size)); + } + } + + /* No free block was found, so we return NULL to indicate failure to + allocate block. */ + return NULL; +} +/*---------------------------------------------------------------------------*/ +char +memb_free(struct memb_blocks *m, void *ptr) +{ + int i; + char *ptr2; + + /* Walk through the list of blocks and try to find the block to + which the pointer "ptr" points to. */ + ptr2 = (char *)m->mem; + for(i = 0; i < m->num; ++i) { + + if(ptr2 == (char *)ptr) { + /* We've found to block to which "ptr" points so we decrease the + reference count and return the new value of it. */ + if(m->count[i] > 0) { + /* Make sure that we don't deallocate free memory. */ + --(m->count[i]); + } + return m->count[i]; + } + ptr2 += m->size; + } + return -1; +} +/*---------------------------------------------------------------------------*/ + +/** @} */ diff --git a/examples/telnet_server/memb.h b/examples/telnet_server/memb.h new file mode 100644 index 0000000..b725ebe --- /dev/null +++ b/examples/telnet_server/memb.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: memb.h,v 1.1 2006/06/12 08:21:43 adam Exp $ + */ + +/** + * \defgroup memb Memory block management functions + * + * The memory block allocation routines provide a simple yet powerful + * set of functions for managing a set of memory blocks of fixed + * size. A set of memory blocks is statically declared with the + * MEMB() macro. Memory blocks are allocated from the declared + * memory by the memb_alloc() function, and are deallocated with the + * memb_free() function. + * + * \note Because of namespace clashes only one MEMB() can be + * declared per C module, and the name scope of a MEMB() memory + * block is local to each C module. + * + * The following example shows how to declare and use a memory block + * called "cmem" which has 8 chunks of memory with each memory chunk + * being 20 bytes large. + * + * @{ + */ + + +/** + * \file + * Memory block allocation routines. + * \author + * Adam Dunkels + * + */ + +#ifndef __MEMB_H__ +#define __MEMB_H__ + +/* + * Here we define a C preprocessing macro for concatenating to + * strings. We need use two macros in order to allow concatenation of + * two #defined macros. + */ +#define MEMB_CONCAT2(s1, s2) s1##s2 +#define MEMB_CONCAT(s1, s2) MEMB_CONCAT2(s1, s2) + +/** + * Declare a memory block. + * + * This macro is used to staticall declare a block of memory that can + * be used by the block allocation functions. The macro statically + * declares a C array with a size that matches the specified number of + * blocks and their individual sizes. + * + * Example: + \code +MEMB(connections, sizeof(struct connection), 16); + \endcode + * + * \param name The name of the memory block (later used with + * memb_init(), memb_alloc() and memb_free()). + * + * \param size The size of each memory chunk, in bytes. + * + * \param num The total number of memory chunks in the block. + * + */ +#define MEMB(name, structure, num) \ + static char MEMB_CONCAT(name,_memb_count)[num]; \ + static structure MEMB_CONCAT(name,_memb_mem)[num]; \ + static struct memb_blocks name = {sizeof(structure), num, \ + MEMB_CONCAT(name,_memb_count), \ + (void *)MEMB_CONCAT(name,_memb_mem)} + +struct memb_blocks { + unsigned short size; + unsigned short num; + char *count; + void *mem; +}; + +/** + * Initialize a memory block that was declared with MEMB(). + * + * \param m A memory block previosly declared with MEMB(). + */ +void memb_init(struct memb_blocks *m); + +/** + * Allocate a memory block from a block of memory declared with MEMB(). + * + * \param m A memory block previosly declared with MEMB(). + */ +void *memb_alloc(struct memb_blocks *m); + +/** + * Deallocate a memory block from a memory block previously declared + * with MEMB(). + * + * \param m m A memory block previosly declared with MEMB(). + * + * \param ptr A pointer to the memory block that is to be deallocated. + * + * \return The new reference count for the memory block (should be 0 + * if successfully deallocated) or -1 if the pointer "ptr" did not + * point to a legal memory block. + */ +char memb_free(struct memb_blocks *m, void *ptr); + +/** @} */ + +#endif /* __MEMB_H__ */ diff --git a/examples/telnet_server/printf.h b/examples/telnet_server/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/examples/telnet_server/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/examples/telnet_server/shell.cpp b/examples/telnet_server/shell.cpp new file mode 100644 index 0000000..1a4de98 --- /dev/null +++ b/examples/telnet_server/shell.cpp @@ -0,0 +1,146 @@ + /* + * Copyright (c) 2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: shell.c,v 1.1 2006/06/07 09:43:54 adam Exp $ + * + */ +#include +#include "Arduino.h" +#include "NanodeUIP.h" +#include "shell.h" + +struct ptentry { + PGM_P commandstr; + void (* pfunc)(const char *str); +}; + +#ifndef SHELL_PROMPT +#define SHELL_PROMPT "NanodeUIP> " +#endif + +/*---------------------------------------------------------------------------*/ +void +parse(const register char *str, struct ptentry *t) +{ + // RAM space to copy an entry down from progmem + struct ptentry entry; + + // Copy down the first entry + memcpy_P(&entry,t,sizeof(entry)); + + // Keep looking at entries until the commandstr is empty + while ( entry.commandstr != NULL ) + { + // If the command matches + if(strncmp_P(str, entry.commandstr, strlen_P(entry.commandstr)) == 0) { + // We've found our goal, stop! + break; + } + // Copy down the next entry + memcpy_P(&entry,++t,sizeof(entry)); + } + + entry.pfunc(str); +} + +extern char* __brkval; +extern char __heap_start; +extern int errno; + +/*---------------------------------------------------------------------------*/ +static void +show_memory(const char *str) +{ + char buf[25]; + sprintf_P(buf,PSTR("SP = 0x%x"),SP); + shell_output(buf,NULL); + sprintf_P(buf,PSTR("__brkval = %p"),__brkval?__brkval:&__heap_start); + shell_output(buf,NULL); + sprintf_P(buf,PSTR("Free memory = %u bytes"),SP-(__brkval?(uint16_t)__brkval:(uint16_t)&__heap_start)); + shell_output(buf,NULL); +} + +/*---------------------------------------------------------------------------*/ +static void +help(const char *) +{ + // TEXT HERE CAN ONLY BE 30 chars / output! based on telnetd.h + shell_output_P(PSTR("Available commands:"), NULL); + shell_output_P(PSTR("help, ? - show help"), NULL); + shell_output_P(PSTR("exit - exit shell"), NULL); + shell_output_P(PSTR("mem - show memory"), NULL); +} +/*---------------------------------------------------------------------------*/ +static void +unknown_command(const char *str) +{ + if(strlen(str) > 0) { + shell_output_P_1st(PSTR("Unknown command: "), str); + } +} +/*---------------------------------------------------------------------------*/ + +const char stats_str_p[] PROGMEM = "stats"; +const char conn_str_p[] PROGMEM = "conn"; +const char help_str_p[] PROGMEM = "help"; +const char exit_str_p[] PROGMEM = "exit"; +const char q_str_p[] PROGMEM = "?"; +const char mem_str_p[] PROGMEM = "mem"; + +static struct ptentry parsetab[] PROGMEM = + {{stats_str_p, help}, + {conn_str_p, help}, + {help_str_p, help}, + {exit_str_p, shell_quit}, + {q_str_p, help}, + {mem_str_p, show_memory}, + {NULL, unknown_command}}; +/*---------------------------------------------------------------------------*/ +void +shell_init(void) +{ +} +/*---------------------------------------------------------------------------*/ +void +shell_start(void) +{ + shell_output_P(PSTR("uIP command shell"), NULL); + shell_output_P(PSTR("Type '?' and return for help"), NULL); + shell_prompt_P(PSTR(SHELL_PROMPT)); +} +/*---------------------------------------------------------------------------*/ +void +shell_input(char *cmd) +{ + parse(cmd, parsetab); + shell_prompt_P(PSTR(SHELL_PROMPT)); +} +/*---------------------------------------------------------------------------*/ +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/telnet_server/shell.h b/examples/telnet_server/shell.h new file mode 100644 index 0000000..4c2b6db --- /dev/null +++ b/examples/telnet_server/shell.h @@ -0,0 +1,108 @@ +/** + * \file + * Interface for the Contiki shell. + * \author Adam Dunkels + * + * Some of the functions declared in this file must be implemented as + * a shell back-end in the architecture specific files of a Contiki + * port. + */ + + +/* + * Copyright (c) 2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This file is part of the Contiki desktop OS. + * + * $Id: shell.h,v 1.1 2006/06/07 09:43:54 adam Exp $ + * + */ +#ifndef __SHELL_H__ +#define __SHELL_H__ + +/** + * Initialize the shell. + * + * Called when the shell front-end process starts. This function may + * be used to start listening for signals. + */ +void shell_init(void); + +/** + * Start the shell back-end. + * + * Called by the front-end when a new shell is started. + */ +void shell_start(void); + +/** + * Process a shell command. + * + * This function will be called by the shell GUI / telnet server whan + * a command has been entered that should be processed by the shell + * back-end. + * + * \param command The command to be processed. + */ +void shell_input(char *command); + +/** + * Quit the shell. + * + */ +void shell_quit(const char *); + + +/** + * Print a string to the shell window. + * + * This function is implemented by the shell GUI / telnet server and + * can be called by the shell back-end to output a string in the + * shell window. The string is automatically appended with a linebreak. + * + * \param str1 The first half of the string to be output. + * \param str2 The second half of the string to be output. + */ +void shell_output(const char *str1, const char *str2); +void shell_output_P(PGM_P str1, PGM_P str2); +void shell_output_P_1st(PGM_P str1, const char* str2); + +/** + * Print a prompt to the shell window. + * + * This function can be used by the shell back-end to print out a + * prompt to the shell window. + * + * \param prompt The prompt to be printed. + * + */ +void shell_prompt(const char *prompt); +void shell_prompt_P(PGM_P prompt); + +#endif /* __SHELL_H__ */ +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/telnet_server/telnet.cpp b/examples/telnet_server/telnet.cpp new file mode 100644 index 0000000..1e0742a --- /dev/null +++ b/examples/telnet_server/telnet.cpp @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2003, Adam Dunkelps-> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This file is part of the uIP TCP/IP stack + * + * $Id: telnetd.c,v 1.2 2006/06/07 09:43:54 adam Exp $ + * + */ + +#include "NanodeUIP.h" +#include "Arduino.h" +#include "telnetd.h" +#include "shell.h" + +#include + +#define ISO_nl 0x0a +#define ISO_cr 0x0d + +#define STATE_NORMAL 0 +#define STATE_IAC 1 +#define STATE_WILL 2 +#define STATE_WONT 3 +#define STATE_DO 4 +#define STATE_DONT 5 +#define STATE_CLOSE 6 + +static struct telnetd_state* ps; + +#define TELNET_IAC 255 +#define TELNET_WILL 251 +#define TELNET_WONT 252 +#define TELNET_DO 253 +#define TELNET_DONT 254 +/*---------------------------------------------------------------------------*/ +static char * +alloc_line(void) +{ + return reinterpret_cast(memb_alloc(&(ps->linemem))); +} +/*---------------------------------------------------------------------------*/ +static void +dealloc_line(char *line) +{ + memb_free(&(ps->linemem), line); +} +/*---------------------------------------------------------------------------*/ +void +shell_quit(const char *) +{ + ps->state = STATE_CLOSE; +} +/*---------------------------------------------------------------------------*/ +static void +sendline(char *line) +{ + static unsigned int i; + + for(i = 0; i < TELNETD_CONF_NUMLINES; ++i) { + if(ps->lines[i] == NULL) { + ps->lines[i] = line; + break; + } + } + if(i == TELNETD_CONF_NUMLINES) { + dealloc_line(line); + } +} +/*---------------------------------------------------------------------------*/ +void +shell_prompt(const char *str) +{ + char *line; + line = alloc_line(); + if(line != NULL) { + strncpy(line, str, TELNETD_CONF_LINELEN); + /* petsciiconv_toascii(line, TELNETD_CONF_LINELEN);*/ + sendline(line); + } +} +/*---------------------------------------------------------------------------*/ +void +shell_prompt_P(PGM_P str) +{ + char *line; + line = alloc_line(); + if(line != NULL) { + strncpy_P(line, str, TELNETD_CONF_LINELEN); + /* petsciiconv_toascii(line, TELNETD_CONF_LINELEN);*/ + sendline(line); + } +} +/*---------------------------------------------------------------------------*/ +void +shell_output(const char *str1, const char *str2) +{ + static unsigned len; + char *line; + + line = alloc_line(); + if(line != NULL) { + len = strlen(str1); + strncpy(line, str1, TELNETD_CONF_LINELEN); + if(str2 && len < TELNETD_CONF_LINELEN) { + strncpy(line + len, str2, TELNETD_CONF_LINELEN - len); + } + len = strlen(line); + if(len < TELNETD_CONF_LINELEN - 2) { + line[len] = ISO_cr; + line[len+1] = ISO_nl; + line[len+2] = 0; + } + /* petsciiconv_toascii(line, TELNETD_CONF_LINELEN);*/ + sendline(line); + } +} +/*---------------------------------------------------------------------------*/ +void +shell_output_P(PGM_P str1, PGM_P str2) +{ + static unsigned len; + char *line; + + line = alloc_line(); + if(line != NULL) { + len = strlen_P(str1); + strncpy_P(line, str1, TELNETD_CONF_LINELEN); + if(str2 && len < TELNETD_CONF_LINELEN) { + strncpy_P(line + len, str2, TELNETD_CONF_LINELEN - len); + } + len = strlen(line); + if(len < TELNETD_CONF_LINELEN - 2) { + line[len] = ISO_cr; + line[len+1] = ISO_nl; + line[len+2] = 0; + } + /* petsciiconv_toascii(line, TELNETD_CONF_LINELEN);*/ + sendline(line); + } +} +/*---------------------------------------------------------------------------*/ +void +shell_output_P_1st(PGM_P str1, const char* str2) +{ + static unsigned len; + char *line; + + line = alloc_line(); + if(line != NULL) { + len = strlen_P(str1); + strncpy_P(line, str1, TELNETD_CONF_LINELEN); + if(str2 && len < TELNETD_CONF_LINELEN) { + strncpy(line + len, str2, TELNETD_CONF_LINELEN - len); + } + len = strlen(line); + if(len < TELNETD_CONF_LINELEN - 2) { + line[len] = ISO_cr; + line[len+1] = ISO_nl; + line[len+2] = 0; + } + /* petsciiconv_toascii(line, TELNETD_CONF_LINELEN);*/ + sendline(line); + } +} +/*---------------------------------------------------------------------------*/ +void +telnetd_init(void) +{ + uip_listen(HTONS(23),telnetd_appcall); + shell_init(); +} +/*---------------------------------------------------------------------------*/ +static void +acked(void) +{ + static unsigned int i; + + while(ps->numsent > 0) { + dealloc_line(ps->lines[0]); + for(i = 1; i < TELNETD_CONF_NUMLINES; ++i) { + ps->lines[i - 1] = ps->lines[i]; + } + ps->lines[TELNETD_CONF_NUMLINES - 1] = NULL; + --ps->numsent; + } +} +/*---------------------------------------------------------------------------*/ +static void +senddata(void) +{ + static char *bufptr, *lineptr; + static int buflen, linelen; + + bufptr = reinterpret_cast(uip_appdata); + buflen = 0; + for(ps->numsent = 0; ps->numsent < TELNETD_CONF_NUMLINES && + ps->lines[ps->numsent] != NULL ; ++ps->numsent) { + lineptr = ps->lines[ps->numsent]; + linelen = strlen(lineptr); + if(linelen > TELNETD_CONF_LINELEN) { + linelen = TELNETD_CONF_LINELEN; + } + if(buflen + linelen < uip_mss()) { + memcpy(bufptr, lineptr, linelen); + bufptr += linelen; + buflen += linelen; + } else { + break; + } + } + uip_send(uip_appdata, buflen); +} +/*---------------------------------------------------------------------------*/ +static void +closed(void) +{ + static unsigned int i; + + for(i = 0; i < TELNETD_CONF_NUMLINES; ++i) { + if(ps->lines[i] != NULL) { + dealloc_line(ps->lines[i]); + } + } +} +/*---------------------------------------------------------------------------*/ +static void +get_char(u8_t c) +{ + if(c == ISO_cr) { + return; + } + + ps->buf[(int)ps->bufptr] = c; + if(ps->buf[(int)ps->bufptr] == ISO_nl || + ps->bufptr == sizeof(ps->buf) - 1) { + if(ps->bufptr > 0) { + ps->buf[(int)ps->bufptr] = 0; + /* petsciiconv_topetscii(ps->buf, TELNETD_CONF_LINELEN);*/ + } + if((ps->bufptr + 1) < sizeof(ps->buf)) { + ps->buf[(int)ps->bufptr +1] = 0; + } + shell_input(ps->buf); + ps->bufptr = 0; + } else { + ++ps->bufptr; + } +} +/*---------------------------------------------------------------------------*/ +static void +sendopt(u8_t option, u8_t value) +{ + char *line; + line = alloc_line(); + if(line != NULL) { + line[0] = TELNET_IAC; + line[1] = option; + line[2] = value; + line[3] = 0; + sendline(line); + } +} +/*---------------------------------------------------------------------------*/ +static void +newdata(void) +{ + u16_t len; + u8_t c; + char *dataptr; + + + len = uip_datalen(); + dataptr = (char *)uip_appdata; + + while(len > 0 && ps->bufptr < sizeof(ps->buf)) { + c = *dataptr; + ++dataptr; + --len; + switch(ps->state) { + case STATE_IAC: + if(c == TELNET_IAC) { + get_char(c); + ps->state = STATE_NORMAL; + } else { + switch(c) { + case TELNET_WILL: + ps->state = STATE_WILL; + break; + case TELNET_WONT: + ps->state = STATE_WONT; + break; + case TELNET_DO: + ps->state = STATE_DO; + break; + case TELNET_DONT: + ps->state = STATE_DONT; + break; + default: + ps->state = STATE_NORMAL; + break; + } + } + break; + case STATE_WILL: + /* Reply with a DONT */ + sendopt(TELNET_DONT, c); + ps->state = STATE_NORMAL; + break; + + case STATE_WONT: + /* Reply with a DONT */ + sendopt(TELNET_DONT, c); + ps->state = STATE_NORMAL; + break; + case STATE_DO: + /* Reply with a WONT */ + sendopt(TELNET_WONT, c); + ps->state = STATE_NORMAL; + break; + case STATE_DONT: + /* Reply with a WONT */ + sendopt(TELNET_WONT, c); + ps->state = STATE_NORMAL; + break; + case STATE_NORMAL: + if(c == TELNET_IAC) { + ps->state = STATE_IAC; + } else { + get_char(c); + } + break; + } + + + } + +} +/*---------------------------------------------------------------------------*/ +void +telnetd_appcall(void) +{ + ps = reinterpret_cast(&(uip_conn->appstate)); + static unsigned int i; + if(uip_connected()) { + /* tcp_markconn(uip_conn, &s);*/ + for(i = 0; i < TELNETD_CONF_NUMLINES; ++i) { + ps->lines[i] = NULL; + } + ps->bufptr = 0; + ps->state = STATE_NORMAL; + + ps->linemem.size = sizeof(telnetd_line); + ps->linemem.num = TELNETD_CONF_NUMLINES; + ps->linemem.count = ps->linemem_memb_count; + ps->linemem.mem = ps->linemem_memb_mem; + memb_init(&(ps->linemem)); + + shell_start(); + } + + if(ps->state == STATE_CLOSE) { + ps->state = STATE_NORMAL; + uip_close(); + return; + } + + if(uip_closed() || + uip_aborted() || + uip_timedout()) { + closed(); + } + + if(uip_acked()) { + acked(); + } + + if(uip_newdata()) { + newdata(); + } + + if(uip_rexmit() || + uip_newdata() || + uip_acked() || + uip_connected() || + uip_poll()) { + senddata(); + } + ps = NULL; +} +/*---------------------------------------------------------------------------*/ +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/telnet_server/telnet_server.pde b/examples/telnet_server/telnet_server.pde new file mode 100644 index 0000000..565442d --- /dev/null +++ b/examples/telnet_server/telnet_server.pde @@ -0,0 +1,40 @@ +#include +#include +#include +#include "telnetd.h" +#include "printf.h" + +void dhcp_status(int s,const uint16_t *) { + char buf[20]="IP:"; + if (s==DHCP_STATUS_OK) { + uip.get_ip_addr_str(buf+3); + Serial.println(buf); + + nanode_log_P(PSTR("Bringing up telnet server...")); + telnetd_init(); + } +} + +void setup() { + char buf[20]; + byte macaddr[6]; + NanodeUNIO unio(NANODE_MAC_DEVICE); + + Serial.begin(38400); + printf_begin(); + printf_P(PSTR(__FILE__"\r\n")); + + unio.read(macaddr,NANODE_MAC_ADDRESS,6); + uip.init(macaddr); + uip.get_mac_str(buf); + Serial.println(buf); + uip.wait_for_link(); + nanode_log_P(PSTR("Link is up")); + uip.start_dhcp(dhcp_status); + nanode_log_P(PSTR("setup() done")); +} + +void loop() { + uip.poll(); +} +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/telnet_server/telnetd.h b/examples/telnet_server/telnetd.h new file mode 100644 index 0000000..8a8fa7b --- /dev/null +++ b/examples/telnet_server/telnetd.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This file is part of the uIP TCP/IP stack + * + * $Id: telnetd.h,v 1.2 2006/06/07 09:43:54 adam Exp $ + * + */ +#ifndef __TELNETD_H__ +#define __TELNETD_H__ + +#include "uipopt.h" +extern "C" +{ +#include "memb.h" +} + +void telnetd_appcall(void); +void telnetd_init(void); + +// Be sure to adjust TCP_APP_STATE_SIZE if you change any of these! +#ifndef TELNETD_CONF_LINELEN +#define TELNETD_CONF_LINELEN 30 +#endif +#ifndef TELNETD_CONF_NUMLINES +// orig value #define TELNETD_CONF_NUMLINES 16 +#define TELNETD_CONF_NUMLINES 5 +#endif + +struct telnetd_line { + char line[TELNETD_CONF_LINELEN]; +}; + +struct telnetd_state { + char *lines[TELNETD_CONF_NUMLINES]; + char buf[TELNETD_CONF_LINELEN]; + u8_t bufptr; + u8_t numsent; + u8_t state; + + char linemem_memb_count[TELNETD_CONF_NUMLINES]; + telnetd_line linemem_memb_mem[TELNETD_CONF_NUMLINES]; + memb_blocks linemem; +}; + +#endif /* __TELNETD_H__ */ +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/telnet_server/test.ex b/examples/telnet_server/test.ex new file mode 100644 index 0000000..caa886c --- /dev/null +++ b/examples/telnet_server/test.ex @@ -0,0 +1,14 @@ +#!/usr/bin/expect + +spawn telnet 192.168.1.118 +expect "NanodeUIP>" +send "mem\r" +expect "NanodeUIP>" +send "help\r" +expect "NanodeUIP>" +send "garbage\r" +expect "NanodeUIP>" +send "exit\r" +expect "NanodeUIP>" + + diff --git a/platform.cpp b/platform.cpp index 197c9e0..c2e328d 100644 --- a/platform.cpp +++ b/platform.cpp @@ -1,3 +1,4 @@ +#include #include "clock.h" #include "uip-conf.h" @@ -14,7 +15,11 @@ clock_time_t clock_time(void) { void nullproc(void) {} extern void nanode_log(char *msg); +extern void nanode_log_P(PGM_P msg); void uip_log(char *msg) { nanode_log(msg); } +void uip_log_P(PGM_P msg) { + nanode_log_P(msg); +} diff --git a/uip-conf.h b/uip-conf.h index 9ba773c..a387bae 100644 --- a/uip-conf.h +++ b/uip-conf.h @@ -137,7 +137,7 @@ typedef unsigned short uip_stats_t; /*#include "webclient.h"*/ /* XXX must figure out a better way of doing this! */ -#define TCP_APP_STATE_SIZE 150 +#define TCP_APP_STATE_SIZE 220 typedef void tcp_appcall_fn(void); typedef void udp_appcall_fn(void); diff --git a/uip.cpp b/uip.cpp index fe39330..8e577a9 100644 --- a/uip.cpp +++ b/uip.cpp @@ -240,9 +240,10 @@ struct uip_stats uip_stat; #endif /* UIP_STATISTICS == 1 */ #if UIP_LOGGING == 1 +#include #include -void uip_log(char *msg); -#define UIP_LOG(m) uip_log(m) +void uip_log_P(PGM_P msg); +#define UIP_LOG(m) uip_log_P(PSTR(m)) #else #define UIP_LOG(m) #endif /* UIP_LOGGING == 1 */ diff --git a/uipopt.h b/uipopt.h index b1ef8e5..07343ed 100644 --- a/uipopt.h +++ b/uipopt.h @@ -67,6 +67,7 @@ #define UIP_BIG_ENDIAN 1234 #endif /* UIP_BIG_ENDIAN */ +#include #include "uip-conf.h" /*------------------------------------------------------------------------------*/ @@ -432,6 +433,7 @@ * is called by uIP whenever a log message is generated. */ void uip_log(char *msg); +void uip_log_P(PGM_P msg); /** * The link level header length.