diff options
-rw-r--r-- | apps/ChangeLog.txt | 3 | ||||
-rw-r--r-- | apps/examples/cc3000/Makefile | 15 | ||||
-rw-r--r-- | apps/examples/cc3000/cc3000basic.c | 470 | ||||
-rw-r--r-- | apps/examples/cc3000/shell.c | 226 | ||||
-rw-r--r-- | apps/examples/cc3000/shell.h | 83 | ||||
-rw-r--r-- | apps/examples/cc3000/telnetd.h | 115 | ||||
-rw-r--r-- | apps/examples/cc3000/telnetd_driver.c | 808 | ||||
-rw-r--r-- | apps/examples/telnetd/shell.c | 1 | ||||
-rw-r--r-- | apps/netutils/telnetd/telnetd_driver.c | 2 |
9 files changed, 1522 insertions, 201 deletions
diff --git a/apps/ChangeLog.txt b/apps/ChangeLog.txt index 79341e8c4..df258de8c 100644 --- a/apps/ChangeLog.txt +++ b/apps/ChangeLog.txt @@ -688,3 +688,6 @@ of CC3000 logic by David Sidrane (2013-10-16). * apps/examples/random: Add a simple test that dumps values from /dev/random (2013-10-20). + * apps/examples/cc3000: Extensions and enhancements from David + Sidrane (2013-10-23). + diff --git a/apps/examples/cc3000/Makefile b/apps/examples/cc3000/Makefile index 41d8c4acb..524c4f4f6 100644 --- a/apps/examples/cc3000/Makefile +++ b/apps/examples/cc3000/Makefile @@ -1,5 +1,5 @@ ############################################################################ -# apps/examples/hello/Makefile +# apps/examples/cc3000/Makefile # # Copyright (C) 2008, 2010-2013 Gregory Nutt. All rights reserved. # Author: Gregory Nutt <gnutt@nuttx.org> @@ -41,12 +41,16 @@ include $(APPDIR)/Make.defs APPNAME = c3b PRIORITY = SCHED_PRIORITY_DEFAULT -STACKSIZE = 4096 +STACKSIZE = 2048 + +APPNAME1 = shell +PRIORITY1 = SCHED_PRIORITY_DEFAULT +STACKSIZE1 = 2048 # Hello, World! Example ASRCS = -CSRCS = cc3000basic.c board.c +CSRCS = cc3000basic.c board.c shell.c telnetd_driver.c AOBJS = $(ASRCS:.S=$(OBJEXT)) COBJS = $(CSRCS:.c=$(OBJEXT)) @@ -68,7 +72,7 @@ ROOTDEPPATH = --dep-path . # Common build -VPATH = +VPATH = all: .built .PHONY: clean depend distclean @@ -86,8 +90,9 @@ $(COBJS): %$(OBJEXT): %.c ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) $(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat: $(DEPCONFIG) Makefile $(call REGISTER,$(APPNAME),$(PRIORITY),$(STACKSIZE),$(APPNAME)_main) + $(call REGISTER,$(APPNAME1),$(PRIORITY1),$(STACKSIZE1),$(APPNAME1)_main) -context: $(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat +context: $(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat $(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat else context: endif diff --git a/apps/examples/cc3000/cc3000basic.c b/apps/examples/cc3000/cc3000basic.c index 0a0142654..9a355f9e5 100644 --- a/apps/examples/cc3000/cc3000basic.c +++ b/apps/examples/cc3000/cc3000basic.c @@ -6,6 +6,10 @@ * Version 1.0.1b * * Copyright (C) 2013 Chris Magagna - cmagagna@yahoo.com +* Port to nuttx: +* Alan Carvalho de Assis <acassis@gmail.com> +* David Sidrane <david_s5@nscdg.com> +* * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -81,6 +85,7 @@ Arduino pin -----> 560 Ohm --+--> 1K Ohm -----> GND ****************************************************************************/ +#include <nuttx/config.h> #include "board.h" #include <stdio.h> #include <string.h> @@ -88,16 +93,18 @@ Arduino pin -----> 560 Ohm --+--> 1K Ohm -----> GND #include <stdlib.h> #include <stdbool.h> #include <sys/time.h> +#include <arpa/inet.h> #include <nuttx/wireless/cc3000/nvmem.h> #include <nuttx/wireless/cc3000/include/sys/socket.h> #include <nuttx/wireless/cc3000/wlan.h> #include <nuttx/wireless/cc3000/hci.h> #include <nuttx/wireless/cc3000/security.h> #include <nuttx/wireless/cc3000/netapp.h> +#include "shell.h" void Initialize(void); void helpme(void); -void execute(int cmd); +int execute(int cmd); void ShowBufferSize(void); void ShowFreeRAM(void); void Blinker(void); @@ -112,11 +119,56 @@ void ShowInformation(void); // function Blinker() flashes this LED. It's not required for actual use. #define BLINKER_LED 6 +#define MS_PER_SEC 1000 +#define US_PER_MS 1000 +#define US_PER_SEC 1000000 uint8_t isInitialized = false; +bool wait(long timeoutMs, volatile unsigned long *what, volatile unsigned long is) +{ + long t_ms; + struct timeval end, start; + + gettimeofday(&start, NULL); + + while (*what != is) + { + Blinker(); + usleep(10*US_PER_MS); + gettimeofday(&end, NULL); + t_ms = ((end.tv_sec - start.tv_sec) * MS_PER_SEC) + ((end.tv_usec - start.tv_usec) / US_PER_MS) ; + if (t_ms > timeoutMs) + { + return false; + } + } + + return true; +} + +bool wait_on(long timeoutMs, volatile unsigned long *what, volatile unsigned long is,char * msg) +{ + printf(msg); + printf("..."); + fflush(stdout); + bool ret = wait(timeoutMs,what,is); + if (!ret) + { + printf(" FAILED:Timeout!\n"); + } + else + { + printf(" Succeed\n"); + } + + fflush(stdout); + return ret; +} + void AsyncEventPrint(void) { + printf("\n"); switch(lastAsyncEvent) { printf("CC3000 Async event: Simple config done\n"); @@ -161,26 +213,29 @@ void AsyncEventPrint(void) void helpme(void) { printf("\n+-------------------------------------------+\n"); - printf("| Arduino CC3000 Demo Program |\n"); + printf("| Nuttx CC3000 Demo Program |\n"); printf("+-------------------------------------------+\n\n"); - printf(" 1 - Initialize the CC3000\n"); - printf(" 2 - Show RX & TX buffer sizes, & free RAM\n"); - printf(" 3 - Start Smart Config\n"); - printf(" 4 - Manually connect to AP\n"); - printf(" 5 - Manually add connection profile\n"); - printf(" 6 - List access points\n"); - printf(" 7 - Show CC3000 information\n"); - printf("\n Type 1-7 to select above option or (q/Q) to quit: "); + printf(" 01 - Initialize the CC3000\n"); + printf(" 02 - Show RX & TX buffer sizes, & free RAM\n"); + printf(" 03 - Start Smart Config\n"); + printf(" 04 - Manually connect to AP\n"); + printf(" 05 - Manually add connection profile\n"); + printf(" 06 - List access points\n"); + printf(" 07 - Show CC3000 information\n"); + printf(" 08 - Telnet\n"); + printf("\n Type 01-07 to select above option: "); } -void execute(int cmd) +int execute(int cmd) { + int ret = 0; if (asyncNotificationWaiting) { asyncNotificationWaiting = false; AsyncEventPrint(); } + printf("\n"); switch(cmd) { case '1': @@ -205,17 +260,24 @@ void execute(int cmd) case '7': ShowInformation(); break; + case '8': + shell_main(0, 0); + break; + case 'q': + case 'Q': + ret = 1; + break; default: printf("**Unknown command \"%d\" **\n", cmd); break; } - return; + return ret; } void Initialize(void) { - uint8_t fancyBuffer[MAC_ADDR_LEN], i = 0; + uint8_t fancyBuffer[MAC_ADDR_LEN]; if (isInitialized) { @@ -285,7 +347,6 @@ void Blinker(void) { } - /* Smart Config is TI's way to let you connect your device to your WiFi network without needing a keyboard and display to enter the network name, password, @@ -312,74 +373,77 @@ char simpleConfigPrefix[] = {'T', 'T', 'T'}; // that name into their phone or tablet when they run Smart Config. char device_name[] = "CC3000"; -void StartSmartConfig(void) { +void StartSmartConfig(void) +{ long rval; - double t_us; - struct timeval end, start; - if (!isInitialized) { - printf("CC3000 not initialized; can't run Smart Config.\n"); - return; - } + if (!isInitialized) + { + printf("CC3000 not initialized; can't run Smart Config.\n"); + return; + } - printf("Starting Smart Config...\n"); + printf("Starting Smart Config\n"); - printf(" Disabling auto-connect policy...\n"); - if ((rval = wlan_ioctl_set_connection_policy(DISABLE, DISABLE, DISABLE))!=0) { - printf(" Setting auto connection policy failed, error: %X\n", rval); - return; - } + printf(" Disabling auto-connect policy..."); + if ((rval = wlan_ioctl_set_connection_policy(DISABLE, DISABLE, DISABLE)) !=0 ) + { + printf(" Failed!\n Setting auto connection policy failed, error: %X\n", rval); + return; + } - printf(" Deleting all existing profiles...\n"); - if ((rval = wlan_ioctl_del_profile(255))!=0) { - printf(" Deleting all profiles failed, error: %X\n", rval); - return; - } - - printf(" Waiting until disconnected...\n"); - while (ulCC3000Connected == 1) { - Blinker(); - } - - printf(" Setting smart config prefix...\n"); - if ((rval = wlan_smart_config_set_prefix(simpleConfigPrefix))!=0) { - printf(" Setting smart config prefix failed, error: %X", rval); - return; - } + printf(" Succeed\n"); + printf(" Deleting all existing profiles..."); + fflush(stdout); + + if ((rval = wlan_ioctl_del_profile(255)) !=0 ) + { + printf(" Failed!\n Deleting all profiles failed, error: %X\n", rval); + return; + } + + printf(" Succeed\n"); + wait_on(20*MS_PER_SEC, &ulCC3000Connected, 0, " Waiting until disconnected"); + printf(" Setting smart config prefix..."); + fflush(stdout); + + if ((rval = wlan_smart_config_set_prefix(simpleConfigPrefix)) !=0 ) + { + printf(" Failed!\n Setting smart config prefix failed, error: %X", rval); + return; + } + + printf(" Succeed\n"); printf(" Starting smart config..."); - if ((rval = wlan_smart_config_start(0))!=0) { - printf(" Starting smart config failed, error: %X\n", rval); - return; - } - - // Wait for Smartconfig process complete, or 30 seconds, whichever - // comes first. The Uno isn't seeing the HCI_EVNT_WLAN_ASYNC_SIMPLE_CONFIG_DONE - // event and I can't figure out why (it works fine on the Teensy) so my - // temporary workaround is I just stop waiting after a while - gettimeofday(&start, NULL); - while (ulSmartConfigFinished == 0) - { - Blinker(); - gettimeofday(&end, NULL); - t_us = ((end.tv_sec - start.tv_sec) * 1000*1000) + (end.tv_usec - start.tv_usec) ; - if (t_us > 3000000) //3s + fflush(stdout); + + if ((rval = wlan_smart_config_start(0)) !=0 ) + { + printf(" Failed!\n Starting smart config failed, error: %X\n", rval); + return; + } + + printf(" Succeed\n"); + + if (!wait_on(30*MS_PER_SEC, &ulSmartConfigFinished, 1, " Waiting on Starting smart config done")) { printf(" Timed out waiting for Smart Config to finish. Hopefully it did anyway\n"); - break; } - } - printf(" Smart Config packet seen!\n"); + printf(" Smart Config packet %s!\n",ulSmartConfigFinished ? "seen" : "NOT seen"); - printf(" Enabling auto-connect policy...\n"); - if ((rval=wlan_ioctl_set_connection_policy(DISABLE, DISABLE, ENABLE))!=0) - { - printf(" Setting auto connection policy failed, error: %X\n", rval); - return; - } + printf(" Enabling auto-connect policy..."); + fflush(stdout); + if ((rval=wlan_ioctl_set_connection_policy(DISABLE, DISABLE, ENABLE)) !=0 ) + { + printf(" Failed!\n Setting auto connection policy failed, error: %X\n", rval); + return; + } + printf(" Succeed\n"); printf(" Stopping CC3000...\n"); + fflush(stdout); wlan_stop(); // no error returned here, so nothing to check printf(" Pausing for 2 seconds...\n"); @@ -388,27 +452,29 @@ void StartSmartConfig(void) { printf(" Restarting CC3000... \n"); wlan_start(0); // no error returned here, so nothing to check - printf(" Waiting for connection to AP...\n"); - while (ulCC3000Connected!=1) - { - Blinker(); - } + if (!wait_on(20*MS_PER_SEC, &ulCC3000Connected, 1, " Waiting for connection to AP")) + { + printf(" Timed out waiting for connection to AP\n"); + return; + } - printf(" Waiting for IP address from DHCP...\n"); - while (ulCC3000DHCP!=1) - { - Blinker(); - } + if (!wait_on(15*MS_PER_SEC, &ulCC3000DHCP, 1, " Waiting for IP address from DHCP")) + { + printf(" Timed out waiting for IP address from DHCP\n"); + return; + } printf(" Sending mDNS broadcast to signal we're done with Smart Config...\n"); + fflush(stdout); mdnsAdvertiser(1,device_name,strlen(device_name)); // The API documentation says mdnsAdvertiser() // is supposed to return 0 on success and SOC_ERROR on failure, but it looks like // what it actually returns is the socket number it used. So we ignore it. - printf(" Smart Config finished!\n"); + printf(" Smart Config finished Successfully!\n"); + ShowInformation(); + fflush(stdout); } - /* This is an example of how you'd connect the CC3000 to an AP without using Smart Config or a stored profile. @@ -417,17 +483,17 @@ void StartSmartConfig(void) { always going to connect to your network this way you wouldn't need it. */ -void ManualConnect(void) { - +void ManualConnect(void) +{ char ssidName[] = "YourAP"; char AP_KEY[] = "yourpass"; uint8_t rval; if (!isInitialized) - { - printf("CC3000 not initialized; can't run manual connect.\n"); - return; - } + { + printf("CC3000 not initialized; can't run manual connect.\n"); + return; + } printf("Starting manual connect...\n"); @@ -437,11 +503,7 @@ void ManualConnect(void) { printf(" Deleting all existing profiles...\n"); rval = wlan_ioctl_del_profile(255); - printf(" Waiting until disconnected...\n"); - while (ulCC3000Connected == 1) - { - usleep(100000); //delay 100ms in busy wait - } + wait_on(15*MS_PER_SEC, &ulCC3000Connected, 0, " Waiting until disconnected"); printf(" Manually connecting...\n"); @@ -450,6 +512,7 @@ void ManualConnect(void) { // Parameter 3 is the MAC adddress of the AP. All the TI examples // use NULL. I suppose you would want to specify this // if you were security paranoid. + rval = wlan_connect(WLAN_SEC_WPA2, ssidName, strlen(ssidName), @@ -457,12 +520,14 @@ void ManualConnect(void) { (uint8_t *)AP_KEY, strlen(AP_KEY)); - if (rval==0) { - printf(" Manual connect success.\n"); - } - else { - printf(" Unusual return value: %d\n", rval); - } + if (rval==0) + { + printf(" Manual connect success.\n"); + } + else + { + printf(" Unusual return value: %d\n", rval); + } } /* @@ -483,16 +548,17 @@ void ManualConnect(void) { manage multiple profiles you'll need to do that yourself. */ -void ManualAddProfile(void) { +void ManualAddProfile(void) +{ char ssidName[] = "YourAP"; char AP_KEY[] = "yourpass"; uint8_t rval; if (!isInitialized) - { - printf("CC3000 not initialized; can't run manual add profile."); - return; - } + { + printf("CC3000 not initialized; can't run manual add profile."); + return; + } printf("Starting manual add profile...\n"); @@ -513,34 +579,33 @@ void ManualAddProfile(void) { strlen(AP_KEY) // WPA security key length ); - if (rval!=255) { - - // This code is lifted from http://e2e.ti.com/support/low_power_rf/f/851/p/180859/672551.aspx; - // the actual API documentation on wlan_add_profile doesn't specify any of this.... - - printf(" Manual add profile success, stored in profile: %d\n", rval); + if (rval!=255) + { + // This code is lifted from http://e2e.ti.com/support/low_power_rf/f/851/p/180859/672551.aspx; + // the actual API documentation on wlan_add_profile doesn't specify any of this.... - printf(" Enabling auto connection...\n"); - wlan_ioctl_set_connection_policy(DISABLE, DISABLE, ENABLE); + printf(" Manual add profile success, stored in profile: %d\n", rval); - printf(" Stopping CC3000...\n"); - wlan_stop(); + printf(" Enabling auto connection...\n"); + wlan_ioctl_set_connection_policy(DISABLE, DISABLE, ENABLE); - printf(" Stopping for 5 seconds...\n"); - usleep(5000000); + printf(" Stopping CC3000...\n"); + wlan_stop(); - printf(" Restarting CC3000...\n"); - wlan_start(0); + printf(" Stopping for 5 seconds...\n"); + usleep(5000000); - printf(" Manual add profile done!"); + printf(" Restarting CC3000...\n"); + wlan_start(0); - } - else { - printf(" Manual add profile failured (all profiles full?)."); - } + printf(" Manual add profile done!"); + } + else + { + printf(" Manual add profile failured (all profiles full?)."); + } } - /* The call wlan_ioctl_get_scan_results returns this structure. I couldn't find it in the TI library so it's defined here. It's 50 bytes with @@ -582,7 +647,8 @@ void ManualAddProfile(void) { bssid - 6 bytes - the MAC address of this AP */ -typedef struct scanResults { +typedef struct scanResults +{ unsigned long numNetworksFound; unsigned long results; unsigned isValid:1; @@ -592,29 +658,32 @@ typedef struct scanResults { uint16_t frameTime; uint8_t ssid_name[32]; uint8_t bssid[6]; - } scanResults; +} scanResults; #define NUM_CHANNELS 16 -void ListAccessPoints(void) { +void ListAccessPoints(void) +{ unsigned long aiIntervalList[NUM_CHANNELS]; uint8_t rval; scanResults sr; int apCounter, i; char localB[33]; - if (!isInitialized) { - printf("CC3000 not initialized; can't list access points.\n"); - return; - } + if (!isInitialized) + { + printf("CC3000 not initialized; can't list access points.\n"); + return; + } printf("List visible access points\n"); printf(" Setting scan paramters...\n"); - for (i=0; i<NUM_CHANNELS; i++) { - aiIntervalList[i] = 2000; - } + for (i=0; i<NUM_CHANNELS; i++) + { + aiIntervalList[i] = 2000; + } rval = wlan_ioctl_set_scan_params( 1000, // enable start application scan @@ -627,13 +696,15 @@ void ListAccessPoints(void) { 205, // probe TX power aiIntervalList // table of scan intervals per channel ); - if (rval!=0) { - printf(" Got back unusual result from wlan_ioctl_set_scan_params, can't continue: %d\n", rval); - return; - } - printf(" Sleeping 5 seconds to let the CC3000 discover APs...\n"); - usleep(5000000); + if (rval!=0) + { + printf(" Got back unusual result from wlan_ioctl_set_scan_params, can't continue: %d\n", rval); + return; + } + + //printf(" Sleeping 5 seconds to let the CC3000 discover APs...\n"); + //usleep(5000000); printf(" Getting AP count...\n"); @@ -641,56 +712,62 @@ void ListAccessPoints(void) { // actual # of APs currently seen. Get that # then loop through and print // out what's found. - if ((rval=wlan_ioctl_get_scan_results(2000, (uint8_t *)&sr))!=0) { - printf(" Got back unusual result from wlan_ioctl_get scan results, can't continue: %d\n", rval); - return; - } + if ((rval=wlan_ioctl_get_scan_results(2000, (uint8_t *)&sr))!=0) + { + printf(" Got back unusual result from wlan_ioctl_get scan results, can't continue: %d\n", rval); + return; + } apCounter = sr.numNetworksFound; printf(" Number of APs found: %d\n", apCounter); - do { - if (sr.isValid) { - printf(" \n"); - switch(sr.securityMode) { - case WLAN_SEC_UNSEC: // 0 - printf("OPEN "); - break; - case WLAN_SEC_WEP: // 1 - printf("WEP "); - break; - case WLAN_SEC_WPA: // 2 - printf("WPA "); - break; - case WLAN_SEC_WPA2: // 3 - printf("WPA2 "); - break; + do + { + if (sr.isValid) + { + printf(" "); + switch(sr.securityMode) + { + case WLAN_SEC_UNSEC: // 0 + printf("OPEN "); + break; + case WLAN_SEC_WEP: // 1 + printf("WEP "); + break; + case WLAN_SEC_WPA: // 2 + printf("WPA "); + break; + case WLAN_SEC_WPA2: // 3 + printf("WPA2 "); + break; + } + + sprintf(localB, "%3d ", sr.rssi); + printf("%s", localB); + memset(localB, 0, 33); + memcpy(localB, sr.ssid_name, sr.ssidLength); + printf("%s\n", localB); } - sprintf(localB, "%3d ", sr.rssi); - printf("%s", localB); - memset(localB, 0, 33); - memcpy(localB, sr.ssid_name, sr.ssidLength); - printf("%s", localB); - } - if (--apCounter>0) { - if ((rval=wlan_ioctl_get_scan_results(2000, (uint8_t *)&sr))!=0) { - printf(" Got back unusual result from wlan_ioctl_get scan, can't continue: %d\n", rval); - return; - } + if (--apCounter>0) + { + if ((rval=wlan_ioctl_get_scan_results(2000, (uint8_t *)&sr)) !=0 ) + { + printf(" Got back unusual result from wlan_ioctl_get scan, can't continue: %d\n", rval); + return; + } + } } - } while (apCounter>0); + while (apCounter>0); printf(" Access Point list finished.\n"); } - - -void PrintIPBytes(uint8_t *ipBytes) { +void PrintIPBytes(uint8_t *ipBytes) +{ printf("%d.%d.%d.%d\n", ipBytes[3], ipBytes[2], ipBytes[1], ipBytes[0]); } - /* All the data in all the fields from netapp_ipconfig() are reversed, e.g. an IP address is read via bytes 3,2,1,0 instead of bytes @@ -701,16 +778,17 @@ void PrintIPBytes(uint8_t *ipBytes) { the right order etc. */ -void ShowInformation(void) { - +void ShowInformation(void) +{ tNetappIpconfigRetArgs inf; char localB[33]; int i; - if (!isInitialized) { - printf("CC3000 not initialized; can't get information.\n"); - return; - } + if (!isInitialized) + { + printf("CC3000 not initialized; can't get information.\n"); + return; + } printf("CC3000 information:\n"); @@ -732,33 +810,35 @@ void ShowInformation(void) { PrintIPBytes(inf.aucDNSServer); printf(" MAC address: "); - for (i=(MAC_ADDR_LEN-1); i>=0; i--) { - if (i!=(MAC_ADDR_LEN-1)) { - printf(":"); + for (i=(MAC_ADDR_LEN-1); i>=0; i--) + { + if (i!=(MAC_ADDR_LEN-1)) + { + printf(":"); + } + printf("%X", inf.uaMacAddr[i]); } - printf("%X", inf.uaMacAddr[i]); - } + printf("\n"); - memset(localB, 0, 32); - memcpy(localB, inf.uaSSID, 32); + memset(localB, 0, sizeof(localB)); + strncpy(localB, (char*)inf.uaSSID,sizeof(localB)); - printf(" Connected to SSID: %d\n", localB); + printf(" Connected to SSID: %s\n", localB); } int c3b_main(int argc, char *argv[]) { char ch='0'; - while(ch != 'q' && ch != 'Q') - { - helpme(); + do + { + helpme(); - ch = getchar(); + ch = getchar(); - execute(ch); - } + } + while(execute(ch) == 0); return 0; } - diff --git a/apps/examples/cc3000/shell.c b/apps/examples/cc3000/shell.c new file mode 100644 index 000000000..590c8b12c --- /dev/null +++ b/apps/examples/cc3000/shell.c @@ -0,0 +1,226 @@ +/**************************************************************************** + * apps/examples/cc3000/shell.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * This is a leverage of similar logic from uIP: + * + * Author: Adam Dunkels <adam@sics.se> + * 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. 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. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <net/if.h> +#include <arpa/inet.h> + +#include <apps/netutils/telnetd.h> +#include <apps/netutils/uiplib.h> + +#include "shell.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ptentry_s +{ + FAR const char *commandstr; + void (*pfunc)(int argc, char **argv); +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void shell_help(int argc, char **argv); +static void shell_quit(int argc, char **argv); +static void shell_unknown(int argc, char **argv); +static void shell_parse(FAR char *line, int len); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct ptentry_s g_parsetab[] = +{ + {"help", shell_help}, + {"exit", shell_quit}, + {"?", shell_help}, + {NULL, shell_unknown} +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: shell_help + ****************************************************************************/ + +static void shell_help(int argc, char **argv) +{ + printf("Available commands:\n"); + printf(" help, ? - show help\n"); + printf(" exit - exit shell\n"); +} + +/**************************************************************************** + * Name: shell_help + ****************************************************************************/ + +static void shell_unknown(int argc, char **argv) +{ + if (argv[0]) + { + printf("Unknown command: %s\n", argv[0]); + } +} + +/**************************************************************************** + * Name: shell_quit + ****************************************************************************/ + +static void shell_quit(int argc, char **argv) +{ + printf("Bye!\n"); + exit(0); +} + +/**************************************************************************** + * Name: shell_parse + ****************************************************************************/ + +static void shell_parse(FAR char *line, int len) +{ + struct ptentry_s *entry; + FAR char *cmd; + FAR char *saveptr; + + /* Get the command from the beginning the line */ + + cmd = strtok_r(line, " \t\n\r\f\v", &saveptr); + if (cmd) + { + /* Now find the matching command in the command table */ + + for (entry = g_parsetab; entry->commandstr != NULL; entry++) + { + if (strncmp(entry->commandstr, cmd, strlen(entry->commandstr)) == 0) + { + break; + } + } + + entry->pfunc(1, &cmd); + } +} + +/**************************************************************************** + * Name: shell_session + ****************************************************************************/ + +int shell_session(int argc, char *argv[]) +{ + char line[128]; + + printf("CC3000 command shell -- NuttX style\n"); + printf("Type '?' and return for help\n"); + + for(;;) + { + printf(SHELL_PROMPT); + fflush(stdout); + + if (fgets(line, 128, stdin) == NULL) + { + break; + } + + shell_parse(line, 128); + } + + return 0; +} + +/**************************************************************************** + * Name: shell_netinit + ****************************************************************************/ + +static void shell_netinit(void) +{ + +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int shell_main(int argc, char *argv[]) +{ + struct telnetd_config_s config; + int ret; + + /* Configure the network */ + + printf("shell_main: Initializing the network\n"); + shell_netinit(); + + /* Configure the telnet daemon */ + + config.d_port = HTONS(23); + config.d_priority = CONFIG_EXAMPLES_TELNETD_DAEMONPRIO; + config.d_stacksize = CONFIG_EXAMPLES_TELNETD_DAEMONSTACKSIZE; + config.t_priority = CONFIG_EXAMPLES_TELNETD_CLIENTPRIO; + config.t_stacksize = CONFIG_EXAMPLES_TELNETD_CLIENTSTACKSIZE; + config.t_entry = shell_session; + + /* Start the telnet daemon */ + + printf("shell_main: Starting the Telnet daemon\n"); + ret = telnetd_start(&config); + if (ret < 0) + { + printf("Failed to tart the Telnet daemon\n"); + } + + printf("shell_main: Exiting\n"); + return 0; +} diff --git a/apps/examples/cc3000/shell.h b/apps/examples/cc3000/shell.h new file mode 100644 index 000000000..ce2ca4f3c --- /dev/null +++ b/apps/examples/cc3000/shell.h @@ -0,0 +1,83 @@ +/**************************************************************************** + * apps/examples/cc3000/shell.h + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * 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. + * + ****************************************************************************/ + +#ifndef __APPS_EXAMPLES_CC3000_SHELL_H +#define __APPS_EXAMPLES_CC3000_SHELL_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ +/* CONFIG_EXAMPLES_TELNETD_DAEMONPRIO - Priority of the Telnet daemon. + * Default: SCHED_PRIORITY_DEFAULT + * CONFIG_EXAMPLES_TELNETD_DAEMONSTACKSIZE - Stack size allocated for the + * Telnet daemon. Default: 2048 + * CONFIG_EXAMPLES_TELNETD_CLIENTPRIO- Priority of the Telnet client. + * Default: SCHED_PRIORITY_DEFAULT + * CONFIG_EXAMPLES_TELNETD_CLIENTSTACKSIZE - Stack size allocated for the + * Telnet client. Default: 2048 + * CONFIG_EXAMPLES_TELNETD_NOMAC - If the hardware has no MAC address of its + * own, define this =y to provide a bogus address for testing. + * CONFIG_EXAMPLES_TELNETD_IPADDR - The target IP address. Default 10.0.0.2 + * CONFIG_EXAMPLES_TELNETD_DRIPADDR - The default router address. Default + * 10.0.0.1 + * CONFIG_EXAMPLES_TELNETD_NETMASK - The network mask. Default: 255.255.255.0 + */ + +#ifndef CONFIG_EXAMPLES_TELNETD_DAEMONPRIO +# define CONFIG_EXAMPLES_TELNETD_DAEMONPRIO SCHED_PRIORITY_DEFAULT +#endif + +#ifndef CONFIG_EXAMPLES_TELNETD_DAEMONSTACKSIZE +# define CONFIG_EXAMPLES_TELNETD_DAEMONSTACKSIZE 768 +#endif + +#ifndef CONFIG_EXAMPLES_TELNETD_CLIENTPRIO +# define CONFIG_EXAMPLES_TELNETD_CLIENTPRIO SCHED_PRIORITY_DEFAULT +#endif + +#ifndef CONFIG_EXAMPLES_TELNETD_CLIENTSTACKSIZE +# define CONFIG_EXAMPLES_TELNETD_CLIENTSTACKSIZE 786 +#endif + +/* Other definitions ********************************************************/ + +#define SHELL_PROMPT "CC3000 1.0> " + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int shell_main(int argc, char *argv[]); + +#endif /* __APPS_EXAMPLES_CC3000_SHELL_H */ diff --git a/apps/examples/cc3000/telnetd.h b/apps/examples/cc3000/telnetd.h new file mode 100644 index 000000000..d4858864d --- /dev/null +++ b/apps/examples/cc3000/telnetd.h @@ -0,0 +1,115 @@ +/**************************************************************************** + * apps/examples/cc3000/telnetd.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * 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 Gregory Nutt nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __APPS_EXAMPLES_CC3000_TELNETD_H +#define __APPS_EXAMPLES_CC3000_TELNETD_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This structure represents the overall state of one telnet daemon instance + * (Yes, multiple telnet daemons are supported). + */ + +struct telnetd_s +{ + int port; /* The port to listen on (in network byte order) */ + int priority; /* The execution priority of the spawned task, */ + int stacksize; /* The stack size needed by the spawned task */ + main_t entry; /* The entrypoint of the task to spawn when a new + * connection is accepted. */ +}; + +/* This structure is used to passed information to telnet daemon when it + * started. It contains global information visable to all telnet daemons. + */ + +struct telnetd_common_s +{ + uint8_t ndaemons; /* The total number of daemons running */ + sem_t startsem; /* Enforces one-at-a-time startup */ + sem_t exclsem; /* Enforces exclusive access to 'minor' */ + FAR struct telnetd_s *daemon; /* Describes the new daemon */ + int minor; /* The next minor number to use */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* This structure is used to passed information to telnet daemon when it + * started. It contains global information visable to all telnet daemons. + */ + +extern struct telnetd_common_s g_telnetdcommon; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: telnetd_driver + * + * Description: + * Create a character driver to "wrap" the telnet session. This function + * will select and return a unique path for the new telnet device. + * + * Parameters: + * sd - The socket descriptor that represents the new telnet connection. + * daemon - A pointer to the structure representing the overall state of + * this instance of the telnet daemon. + * + * Return: + * An allocated string represent the full path to the created driver. The + * receiver of the string must de-allocate this memory when it is no longer + * needed. NULL is returned on a failure. + * + ****************************************************************************/ + +FAR char *telnetd_driver(long sd, FAR struct telnetd_s *daemon); + +#endif /* __APPS_EXAMPLES_CC3000_TELNETD_H */ + diff --git a/apps/examples/cc3000/telnetd_driver.c b/apps/examples/cc3000/telnetd_driver.c new file mode 100644 index 000000000..c7d5c86ca --- /dev/null +++ b/apps/examples/cc3000/telnetd_driver.c @@ -0,0 +1,808 @@ +/**************************************************************************** + * apps/examples/cc3000/telnetd_driver.c + * + * Copyright (C) 2007, 2009, 2011-2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * This is a leverage of similar logic from uIP which has a compatible BSD + * license: + * + * Author: Adam Dunkels <adam@sics.se> + * 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. Neither the name of the Institute, NuttX 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <nuttx/wireless/cc3000/include/sys/socket.h> + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <poll.h> +#include <errno.h> +#include <nuttx/fs/fs.h> +#include <debug.h> + +#include <nuttx/net/net.h> + +#include <apps/netutils/telnetd.h> + +#include "telnetd.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Telnet protocol stuff ****************************************************/ + +#define ISO_nl 0x0a +#define ISO_cr 0x0d + +#define TELNET_IAC 255 +#define TELNET_WILL 251 +#define TELNET_WONT 252 +#define TELNET_DO 253 +#define TELNET_DONT 254 + +/* Device stuff *************************************************************/ + +#define TELNETD_DEVFMT "/dev/telnetd%d" + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* The state of the telnet parser */ + +enum telnetd_state_e +{ + STATE_NORMAL = 0, + STATE_IAC, + STATE_WILL, + STATE_WONT, + STATE_DO, + STATE_DONT +}; + +/* This structure describes the internal state of the driver */ + +struct telnetd_dev_s +{ + sem_t td_exclsem; /* Enforces mutually exclusive access */ + uint8_t td_state; /* (See telnetd_state_e) */ + uint8_t td_pending; /* Number of valid, pending bytes in the rxbuffer */ + uint8_t td_offset; /* Offset to the valid, pending bytes in the rxbuffer */ + uint8_t td_crefs; /* The number of open references to the session */ + int td_minor; /* Minor device number */ + long td_psock; /* A clone of the internal socket structure */ + char td_rxbuffer[CONFIG_TELNETD_RXBUFFER_SIZE]; + char td_txbuffer[CONFIG_TELNETD_TXBUFFER_SIZE]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* Support functions */ + +#ifdef CONFIG_TELNETD_DUMPBUFFER +static inline void telnetd_dumpbuffer(FAR const char *msg, + FAR const char *buffer, unsigned int nbytes); +#else +# define telnetd_dumpbuffer(msg,buffer,nbytes) +#endif +static void telnetd_getchar(FAR struct telnetd_dev_s *priv, uint8_t ch, + FAR char *dest, int *nread); +static ssize_t telnetd_receive(FAR struct telnetd_dev_s *priv, + FAR const char *src, size_t srclen, FAR char *dest, + size_t destlen); +static bool telnetd_putchar(FAR struct telnetd_dev_s *priv, uint8_t ch, + int *nwritten); +static void telnetd_sendopt(FAR struct telnetd_dev_s *priv, uint8_t option, + uint8_t value); + +/* Character driver methods */ + +static int telnetd_open(FAR struct file *filep); +static int telnetd_close(FAR struct file *filep); +static ssize_t telnetd_read(FAR struct file *, FAR char *, size_t); +static ssize_t telnetd_write(FAR struct file *, FAR const char *, size_t); +static int telnetd_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_telnetdfops = +{ + telnetd_open, /* open */ + telnetd_close, /* close */ + telnetd_read, /* read */ + telnetd_write, /* write */ + 0, /* seek */ + telnetd_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , 0 /* poll */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: telnetd_dumpbuffer + * + * Description: + * Dump a buffer of data (debug only) + * + ****************************************************************************/ + +#ifdef CONFIG_TELNETD_DUMPBUFFER +static inline void telnetd_dumpbuffer(FAR const char *msg, + FAR const char *buffer, + unsigned int nbytes) +{ + /* CONFIG_DEBUG, CONFIG_DEBUG_VERBOSE, and CONFIG_DEBUG_NET have to be + * defined or the following does nothing. + */ + + nvdbgdumpbuffer(msg, (FAR const uint8_t*)buffer, nbytes); +} +#endif + +/**************************************************************************** + * Name: telnetd_getchar + * + * Description: + * Get another character for the user received buffer from the RX buffer + * + ****************************************************************************/ + +static void telnetd_getchar(FAR struct telnetd_dev_s *priv, uint8_t ch, + FAR char *dest, int *nread) +{ + register int index; + + /* Ignore carriage returns */ + + if (ch != ISO_cr) + { + /* Add all other characters to the destination buffer */ + + index = *nread; + dest[index++] = ch; + *nread = index; + } +} + +/**************************************************************************** + * Name: telnetd_receive + * + * Description: + * Process a received telenet buffer + * + ****************************************************************************/ + +static ssize_t telnetd_receive(FAR struct telnetd_dev_s *priv, FAR const char *src, + size_t srclen, FAR char *dest, size_t destlen) +{ + int nread; + uint8_t ch; + + nllvdbg("srclen: %d destlen: %d\n", srclen, destlen); + + for (nread = 0; srclen > 0 && nread < destlen; srclen--) + { + ch = *src++; + nllvdbg("ch=%02x state=%d\n", ch, priv->td_state); + + switch (priv->td_state) + { + case STATE_IAC: + if (ch == TELNET_IAC) + { + telnetd_getchar(priv, ch, dest, &nread); + priv->td_state = STATE_NORMAL; + } + else + { + switch (ch) + { + case TELNET_WILL: + priv->td_state = STATE_WILL; + break; + + case TELNET_WONT: + priv->td_state = STATE_WONT; + break; + + case TELNET_DO: + priv->td_state = STATE_DO; + break; + + case TELNET_DONT: + priv->td_state = STATE_DONT; + break; + + default: + priv->td_state = STATE_NORMAL; + break; + } + } + break; + + case STATE_WILL: + /* Reply with a DONT */ + + telnetd_sendopt(priv, TELNET_DONT, ch); + priv->td_state = STATE_NORMAL; + break; + + case STATE_WONT: + /* Reply with a DONT */ + + telnetd_sendopt(priv, TELNET_DONT, ch); + priv->td_state = STATE_NORMAL; + break; + + case STATE_DO: + /* Reply with a WONT */ + + telnetd_sendopt(priv, TELNET_WONT, ch); + priv->td_state = STATE_NORMAL; + break; + + case STATE_DONT: + /* Reply with a WONT */ + + telnetd_sendopt(priv, TELNET_WONT, ch); + priv->td_state = STATE_NORMAL; + break; + + case STATE_NORMAL: + if (ch == TELNET_IAC) + { + priv->td_state = STATE_IAC; + } + else + { + telnetd_getchar(priv, ch, dest, &nread); + } + break; + } + } + + /* We get here if (1) all of the received bytes have been processed, or + * (2) if the user's buffer has become full. + */ + + if (srclen > 0) + { + /* Remember where we left off. These bytes will be returned the next + * time that telnetd_read() is called. + */ + + priv->td_pending = srclen; + priv->td_offset = (src - priv->td_rxbuffer); + } + else + { + /* All of the received bytes were consumed */ + + priv->td_pending = 0; + priv->td_offset = 0; + } + + return nread; +} + +/**************************************************************************** + * Name: telnetd_putchar + * + * Description: + * Put another character from the user buffer to the TX buffer. + * + ****************************************************************************/ + +static bool telnetd_putchar(FAR struct telnetd_dev_s *priv, uint8_t ch, + int *nread) +{ + register int index; + bool ret = false; + + /* Ignore carriage returns (we will put these in automatically as necesary) */ + + if (ch != ISO_cr) + { + /* Add all other characters to the destination buffer */ + + index = *nread; + priv->td_txbuffer[index++] = ch; + + /* Check for line feeds */ + + if (ch == ISO_nl) + { + /* Now add the carriage return */ + + priv->td_txbuffer[index++] = ISO_cr; + priv->td_txbuffer[index++] = '\0'; + + /* End of line */ + + ret = true; + } + + *nread = index; + } + + return ret; +} + +/**************************************************************************** + * Name: telnetd_sendopt + * + * Description: + * Send the telnet option bytes + * + ****************************************************************************/ + +static void telnetd_sendopt(FAR struct telnetd_dev_s *priv, uint8_t option, + uint8_t value) +{ + uint8_t optbuf[4]; + optbuf[0] = TELNET_IAC; + optbuf[1] = option; + optbuf[2] = value; + optbuf[3] = 0; + + telnetd_dumpbuffer("Send optbuf", optbuf, 4); + if (send(priv->td_psock, optbuf, 4, 0) < 0) + { + nlldbg("Failed to send TELNET_IAC\n"); + } +} + +/**************************************************************************** + * Name: telnetd_open + ****************************************************************************/ + +static int telnetd_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct telnetd_dev_s *priv = inode->i_private; + int tmp; + int ret; + + nllvdbg("td_crefs: %d\n", priv->td_crefs); + + /* O_NONBLOCK is not supported */ + + if (filep->f_oflags & O_NONBLOCK) + { + ret = -ENOSYS; + goto errout; + } + + /* Get exclusive access to the device structures */ + + ret = sem_wait(&priv->td_exclsem); + if (ret < 0) + { + ret = -errno; + goto errout; + } + + /* Increment the count of references to the device. If this the first + * time that the driver has been opened for this device, then initialize + * the device. + */ + + tmp = priv->td_crefs + 1; + if (tmp > 255) + { + /* More than 255 opens; uint8_t would overflow to zero */ + + ret = -EMFILE; + goto errout_with_sem; + } + + /* Save the new open count on success */ + + priv->td_crefs = tmp; + ret = OK; + +errout_with_sem: + sem_post(&priv->td_exclsem); + +errout: + return ret; +} + +/**************************************************************************** + * Name: telnetd_close + ****************************************************************************/ + +static int telnetd_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct telnetd_dev_s *priv = inode->i_private; + FAR char *devpath; + int ret; + + nllvdbg("td_crefs: %d\n", priv->td_crefs); + + /* Get exclusive access to the device structures */ + + ret = sem_wait(&priv->td_exclsem); + if (ret < 0) + { + ret = -errno; + goto errout; + } + + /* Decrement the references to the driver. If the reference count will + * decrement to 0, then uninitialize the driver. + */ + + if (priv->td_crefs > 1) + { + /* Just decrement the reference count and release the semaphore */ + + priv->td_crefs--; + sem_post(&priv->td_exclsem); + } + else + { + /* Re-create the path to the driver. */ + + sched_lock(); + ret = asprintf(&devpath, TELNETD_DEVFMT, priv->td_minor); + if (ret < 0) + { + nlldbg("Failed to allocate the driver path\n"); + } + else + { + /* Unregister the character driver */ + + ret = unregister_driver(devpath); + if (ret < 0) + { + nlldbg("Failed to unregister the driver %s: %d\n", ret); + } + + free(devpath); + } + + /* Close the socket */ + + closesocket(priv->td_psock); + + /* Release the driver memory. What if there are threads waiting on + * td_exclsem? They will never be awakened! How could this happen? + * crefs == 1 so there are no other open references to the driver. + * But this could have if someone were trying to re-open the driver + * after every other thread has closed it. That really should not + * happen in the intended usage model. + */ + + DEBUGASSERT(priv->td_exclsem.semcount == 0); + sem_destroy(&priv->td_exclsem); + free(priv); + sched_unlock(); + } + + ret = OK; + +errout: + return ret; +} + +/**************************************************************************** + * Name: telnetd_read + ****************************************************************************/ + +static ssize_t telnetd_read(FAR struct file *filep, FAR char *buffer, size_t len) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct telnetd_dev_s *priv = inode->i_private; + ssize_t ret; + + nllvdbg("len: %d\n", len); + + /* First, handle the case where there are still valid bytes left in the + * I/O buffer from the last time that read was called. NOTE: Much of + * what we read may be protocol stuff and may not correspond to user + * data. Hence we need the loop and we need may need to call psock_recv() + * multiple times in order to get data that the client is interested in. + */ + + do + { + if (priv->td_pending > 0) + { + /* Process the buffered telnet data */ + + FAR const char *src = &priv->td_rxbuffer[priv->td_offset]; + ret = telnetd_receive(priv, src, priv->td_pending, buffer, len); + } + + /* Read a buffer of data from the telnet client */ + + else + { + ret = recv(priv->td_psock, priv->td_rxbuffer, + CONFIG_TELNETD_RXBUFFER_SIZE, 0); + + /* Did we receive anything? */ + + if (ret > 0) + { + /* Yes.. Process the newly received telnet data */ + + telnetd_dumpbuffer("Received buffer", priv->td_rxbuffer, ret); + ret = telnetd_receive(priv, priv->td_rxbuffer, ret, buffer, len); + } + + /* Otherwise the peer closed the connection (ret == 0) or an error + * occurred (ret < 0). + */ + + else + { + break; + } + } + } + while (ret == 0); + + /* Return: + * + * ret > 0: The number of characters copied into the user buffer by + * telnetd_receive(). + * ret <= 0: Loss of connection or error events reported by recv(). + */ + + return ret; +} + +/**************************************************************************** + * Name: telnetd_write + ****************************************************************************/ + +static ssize_t telnetd_write(FAR struct file *filep, FAR const char *buffer, size_t len) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct telnetd_dev_s *priv = inode->i_private; + FAR const char *src = buffer; + ssize_t nsent; + ssize_t ret; + int ncopied; + char ch; + bool eol; + + nllvdbg("len: %d\n", len); + + /* Process each character from the user buffer */ + + for (nsent = 0, ncopied = 0; nsent < len; nsent++) + { + /* Get the next character from the user buffer */ + + ch = *src++; + + /* Add the character to the TX buffer */ + + eol = telnetd_putchar(priv, ch, &ncopied); + + /* Was that the end of a line? Or is the buffer too full to hold the + * next largest character sequence ("\r\n\0")? + */ + + if (eol || ncopied > CONFIG_TELNETD_TXBUFFER_SIZE-3) + { + /* Yes... send the data now */ + + ret = send(priv->td_psock, priv->td_txbuffer, ncopied, 0); + if (ret < 0) + { + nlldbg("psock_send failed '%s': %d\n", priv->td_txbuffer, ret); + return ret; + } + + /* Reset the index to the beginning of the TX buffer. */ + + ncopied = 0; + } + } + + /* Send anything remaining in the TX buffer */ + + if (ncopied > 0) + { + ret = send(priv->td_psock, priv->td_txbuffer, ncopied, 0); + if (ret < 0) + { + nlldbg("psock_send failed '%s': %d\n", priv->td_txbuffer, ret); + return ret; + } + } + + /* Notice that we don't actually return the number of bytes sent, but + * rather, the number of bytes that the caller asked us to send. We may + * have sent more bytes (because of CR-LF expansion and because of NULL + * termination). But it confuses some logic if you report that you sent + * more than you were requested to. + */ + + return len; +} + +/**************************************************************************** + * Name: telnetd_poll + ****************************************************************************/ + +static int telnetd_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ +#if 0 /* No ioctl commands are yet supported */ + struct inode *inode = filep->f_inode; + struct cdcacm_dev_s *priv = inode->i_private; + int ret = OK; + + switch (cmd) + { + /* Add ioctl commands here */ + + default: + ret = -ENOTTY; + break; + } + + return ret; +#else + return -ENOTTY; +#endif +} + +/**************************************************************************** + * Name: telnetd_poll + ****************************************************************************/ + +#if 0 /* Not used by this driver */ +static int telnetd_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct telnetd_dev_s *priv = inode->i_private; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: telnetd_driver + * + * Description: + * Create a character driver to "wrap" the telnet session. This function + * will select and return a unique path for the new telnet device. + * + * Parameters: + * sd - The socket descriptor that represents the new telnet connection. + * daemon - A pointer to the structure representing the overall state of + * this instance of the telnet daemon. + * + * Return: + * An allocated string represent the full path to the created driver. The + * receiver of the string must de-allocate this memory when it is no longer + * needed. NULL is returned on a failure. + * + ****************************************************************************/ + +FAR char *telnetd_driver(long sd, FAR struct telnetd_s *daemon) +{ + FAR struct telnetd_dev_s *priv; + FAR char *devpath = NULL; + int ret; + + /* Allocate instance data for this driver */ + + priv = (FAR struct telnetd_dev_s*)malloc(sizeof(struct telnetd_dev_s)); + if (!priv) + { + nlldbg("Failed to allocate the driver data structure\n"); + return NULL; + } + + /* Initialize the allocated driver instance */ + + sem_init(&priv->td_exclsem, 0, 1); + + priv->td_state = STATE_NORMAL; + priv->td_crefs = 0; + priv->td_pending = 0; + priv->td_offset = 0; + priv->td_psock = sd; + + + /* Allocation a unique minor device number of the telnet drvier */ + + do + { + ret = sem_wait(&g_telnetdcommon.exclsem); + if (ret < 0 && errno != -EINTR) + { + goto errout_with_dev; + } + } + while (ret < 0); + + priv->td_minor = g_telnetdcommon.minor; + g_telnetdcommon.minor++; + sem_post(&g_telnetdcommon.exclsem); + + /* Create a path and name for the driver. */ + + ret = asprintf(&devpath, TELNETD_DEVFMT, priv->td_minor); + if (ret < 0) + { + nlldbg("Failed to allocate the driver path\n"); + goto errout_with_dev; + } + + /* Register the driver */ + + ret = register_driver(devpath, &g_telnetdfops, 0666, priv); + if (ret < 0) + { + nlldbg("Failed to register the driver %s: %d\n", devpath, ret); + goto errout_with_devpath; + } + + /* Return the path to the new telnet driver */ + + return devpath; + +errout_with_devpath: + free(devpath); +errout_with_dev: + free(priv); + return NULL; +} diff --git a/apps/examples/telnetd/shell.c b/apps/examples/telnetd/shell.c index 3058daa39..c26b1e168 100644 --- a/apps/examples/telnetd/shell.c +++ b/apps/examples/telnetd/shell.c @@ -45,6 +45,7 @@ #include <string.h> #include <net/if.h> +#include <arpa/inet.h> #include <apps/netutils/telnetd.h> #include <apps/netutils/uiplib.h> diff --git a/apps/netutils/telnetd/telnetd_driver.c b/apps/netutils/telnetd/telnetd_driver.c index 1b04bfbe5..9128ad09b 100644 --- a/apps/netutils/telnetd/telnetd_driver.c +++ b/apps/netutils/telnetd/telnetd_driver.c @@ -121,7 +121,7 @@ struct telnetd_dev_s #ifdef CONFIG_TELNETD_DUMPBUFFER static inline void telnetd_dumpbuffer(FAR const char *msg, - FAR const char *buffer, unsigned int nbytes) + FAR const char *buffer, unsigned int nbytes); #else # define telnetd_dumpbuffer(msg,buffer,nbytes) #endif |