Nut/OS  4.10.3
API Reference
tcps.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009 by egnite GmbH
00003  * Copyright (C) 2001-2005 by egnite Software GmbH
00004  *
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  * 3. Neither the name of the copyright holders nor the names of
00017  *    contributors may be used to endorse or promote products derived
00018  *    from this software without specific prior written permission.
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00027  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00028  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00029  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00030  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00031  * SUCH DAMAGE.
00032  *
00033  * For additional information see http://www.ethernut.de/
00034  *
00035  */
00036 
00053 /* Device specific definitions. */
00054 #include <dev/board.h>
00055 #include <dev/reset.h>
00056 #include <dev/gpio.h>
00057 
00058 /* OS specific definitions. */
00059 #include <sys/version.h>
00060 #include <sys/confnet.h>
00061 #include <sys/heap.h>
00062 #include <sys/timer.h>
00063 #include <sys/socket.h>
00064 
00065 /* Network specific definitions. */
00066 #include <arpa/inet.h>
00067 #include <net/if_var.h>
00068 #include <pro/dhcp.h>
00069 
00070 /* Standard C header files. */
00071 #include <stdlib.h>
00072 #include <stdio.h>
00073 #include <io.h>
00074 #include <string.h>
00075 #include <time.h>
00076 #include <ctype.h>
00077 
00078 /* Version of this application sample. */
00079 #define APP_VERSION     "2.0.0"
00080 
00081 /* Max. size of the input line. */
00082 #define MAX_INPUT_LINE  32
00083 
00084 /* TCP server port. Telnet default is 23. */
00085 #define TCP_SERVER_PORT 23
00086 
00087 static time_t start_time;
00088 
00089 /*
00090  * Halt the application on fatal errors.
00091  */
00092 static void FatalError(char *msg)
00093 {
00094     /* Print a message ... */
00095     puts(msg);
00096     /* ... and never return. */
00097     for (;;);
00098 }
00099 
00100 /*
00101  * Extract command and up to 2 parameters from an input line.
00102  *
00103  * This helper routine splits a line into words. A pointer to the
00104  * first word is returned as a function result. A pointer to an
00105  * optional second word is set via a funtion parameter and a
00106  * pointer to the rest of the line is set via a second function
00107  * parameter.
00108  *
00109  * Example:
00110  *
00111  * If line points to "help me to get this done", then we can use
00112  *
00113  * char *cmd;
00114  * char *param1;
00115  * char *param2;
00116  *
00117  * cmd = ParseLine(line, &param1, &param2);
00118  *
00119  * On return, cmd will point to "help", param1 will point to "me"
00120  * and param 2 will point to "to get this done".
00121  *
00122  * If the line contains less than 3 words, then the second
00123  * parameter pointer is set to NULL. With one word only, also
00124  * the first parameter pointer is set to NULL. The function 
00125  * result is NULL on empty lines, including lines containing 
00126  * all spaces.
00127  *
00128  * Leading spaces are skipped. Trailing end of line characters
00129  * are removed.
00130  *
00131  * Note, that the original contents of the line will be
00132  * modified.
00133  */
00134 static char *ParseLine(char *line, char **pp1, char **pp2)
00135 {
00136     char *p0;
00137     char *cp;
00138 
00139     /* Initialize parameter pointers to NULL. */
00140     *pp1 = NULL;
00141     *pp2 = NULL;
00142 
00143     /* Chop off EOL. */
00144     cp = strchr(line, '\r');
00145     if (cp) {
00146         *cp = 0;
00147     }
00148     cp = strchr(line, '\n');
00149     if (cp) {
00150         *cp = 0;
00151     }
00152 
00153     /*
00154      * Parse line for command and parameters.
00155      */
00156     p0 = line;
00157     while (isspace((int)*p0)) {
00158         /* Skip leading spaces. */
00159         p0++;
00160     }
00161     if (*p0 == '\0') {
00162         /* Return NULL on empty lines. */
00163         return NULL;
00164     }
00165     cp = strchr(p0, ' ');
00166     if (cp) {
00167         *cp++ = '\0';
00168         while (isspace((int)*cp)) {
00169             /* Skip leading spaces. */
00170             cp++;
00171         }
00172         if (*cp) {
00173             /* First parameter found. */
00174             *pp1 = cp;
00175             cp = strchr(cp, ' ');
00176         } else {
00177             cp = NULL;
00178         }
00179         if (cp) {
00180             *cp++ = '\0';
00181             while (isspace((int)*cp)) {
00182                 /* Skip leading spaces. */
00183                 cp++;
00184             }
00185             if (*cp) {
00186                 /* Remaining parameter(s) found. */
00187                 *pp2 = cp;
00188             }
00189         }
00190     }
00191     /* Return pointer to command. */
00192     return p0;
00193 }
00194 
00195 /*
00196  * Process client requests.
00197  *
00198  * This function is called when a connection has been established
00199  * and returns when the connection is closed.
00200  */
00201 static void ProcessRequests(FILE * stream)
00202 {
00203     char *buff;
00204     char *cmd;
00205     size_t clen;
00206     char *p1;
00207     char *p2;
00208 
00209     /*
00210      * Allocate an input buffer. Check the result.
00211      */
00212     buff = malloc(MAX_INPUT_LINE + 1);
00213     if (buff == NULL) {
00214         return;
00215     }
00216 
00217     /*
00218      * Send a welcome banner to the new client.
00219      */
00220     fputs("200 Welcome to tcps. Type help to get help.\r\n", stream);
00221     for (;;) {
00222 
00223         /* 
00224          * Flush any pending output and read in a new line. 
00225          *
00226          * If you want line editing capabilities, check
00227          * http://www.ethernut.de/nutwiki/Input_Line_Editor
00228          */
00229         fflush(stream);
00230         if (fgets(buff, MAX_INPUT_LINE, stream) == NULL) {
00231             /* Probably a disconnect, return. */
00232             break;
00233         }
00234         /* Parse the input line. */
00235         cmd = ParseLine(buff, &p1, &p2);
00236         if (cmd == NULL) {
00237             /* Skip empty lines. */
00238             continue;
00239         }
00240         /* Retrieve command length for abbreviations. */
00241         clen = strlen(cmd);
00242 
00243         /*
00244          * Process memory info request.
00245          *
00246          * http://www.ethernut.de/nutwiki/Heap_Memory
00247          */
00248         if (strncmp(cmd, "heap", clen) == 0) {
00249             fprintf(stream, "210 %u bytes RAM free\r\n", (unsigned int)NutHeapAvailable());
00250             continue;
00251         }
00252 
00253         /*
00254          * Process IP address configuration.
00255          */
00256         if (strncmp(cmd, "ip", clen) == 0) {
00257             uint32_t ip = p1 ? inet_addr(p1) : (uint32_t) -1;
00258 
00259             if (ip == (uint32_t) -1) {
00260                 fputs("420 Invalid or missing address\r\n", stream);
00261             } else {
00262                 confnet.cdn_cip_addr = ip;
00263                 if (NutNetSaveConfig()) {
00264                     fputs("421 Failed to save configuration\r\n", stream);
00265                 } else {
00266                     fputs("220 Configuration saved\r\n", stream);
00267                 }
00268             }
00269             continue;
00270         }
00271 
00272         /*
00273          * Process IP mask configuration.
00274          */
00275         if (strncmp(cmd, "mask", clen) == 0) {
00276             uint32_t mask = p1 ? inet_addr(p1) : (uint32_t) -1;
00277 
00278             if (mask == (uint32_t) -1) {
00279                 fputs("430 Invalid or missing mask\r\n", stream);
00280             } else {
00281                 confnet.cdn_ip_mask = mask;
00282                 if (NutNetSaveConfig()) {
00283                     fputs("421 Failed to save configuration\r\n", stream);
00284                 } else {
00285                     fputs("230 Configuration saved\r\n", stream);
00286                 }
00287             }
00288             continue;
00289         }
00290 
00291 #ifndef MCU_GBA
00292         /*
00293          * Process GPIO pin status, not available on GameBoy.
00294          *
00295          * http://www.ethernut.de/nutwiki/LowLevelPortIo
00296          */
00297         if (strncmp(cmd, "pin", clen) == 0) {
00298             int bank = p1 ? atoi(p1) : 0;
00299             int bit = p2 ? atoi(p2) : 0;
00300             int state = GpioPinGet(bank, bit);
00301 
00302             fprintf(stream, "240 %d at GPIO bank %d bit %d\r\n", state, bank, bit);
00303             continue;
00304         }
00305 #endif
00306 
00307         /*
00308          * Process serial line send request.
00309          */
00310         if (strncmp(cmd, "send", clen) == 0) {
00311             if (p1) {
00312                 printf("%s", p1);
00313                 if (p1) {
00314                     printf(" %s", p2);
00315                 }
00316             }
00317             putchar('\n');
00318             fputs("250 Message sent\r\n", stream);
00319             continue;
00320         }
00321 
00322         /*
00323          * Process time info request.
00324          */
00325         if (strncmp(cmd, "uptime", clen) == 0) {
00326             fprintf(stream, "220 %ld seconds running\r\n", (long)(time(NULL) - start_time));
00327             continue;
00328         }
00329 
00330         /*
00331          * Process system reset request.
00332          *
00333          * http://www.ethernut.de/nutwiki/System_Reset
00334          */
00335         if (strncmp(cmd, "reset", clen) == 0) {
00336             fputs("910 System reset\r\n", stream);
00337             fflush(stream);
00338             NutSleep(1000);
00339             NutReset();
00340             fputs("490 System reset not implemented\r\n", stream);
00341             continue;
00342         }
00343 
00344         /*
00345          * Quit connection.
00346          */
00347         if (strncmp(cmd, "quit", clen) == 0) {
00348             fputs("900 Bye\r\n", stream);
00349             fflush(stream);
00350             break;
00351         }
00352 
00353         /*
00354          * Display help text on any unknown command.
00355          */
00356         fputs("400 List of commands follows\r\n"
00357               "h[eap]    Query heap memory bytes available.\r\n"
00358               "i[p]      Set IP <address>.\r\n"
00359               "m[ask]    Set IP <mask>.\r\n"
00360 #ifndef MCU_GBA
00361               "p[in]     Query status of GPIO pin <bank> <bit>.\r\n"
00362 #endif
00363               "r[eset]   Reset system.\r\n"
00364               "s[end]    Send <message> to serial port.\r\n"
00365               "u[ptime]  Query number of seconds the system is running.\r\n"
00366               "q[uit]    Terminates connection.\r\n" 
00367               ".\r\n", stream);
00368     }
00369     free(buff);
00370 }
00371 
00372 /*
00373  * Main application routine. 
00374  *
00375  * Nut/OS automatically calls this entry after initialization.
00376  */
00377 int main(void)
00378 {
00379     TCPSOCKET *sock;
00380     FILE *stream;
00381     uint32_t baud = 115200;
00382 
00383     /*
00384      * Assign stdout to the DEBUG device.
00385      */
00386     NutRegisterDevice(&DEV_CONSOLE, 0, 0);
00387     freopen(DEV_CONSOLE_NAME, "w", stdout);
00388     _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
00389 
00390     /*
00391      * Print out our version information.
00392      */
00393     printf("\n\nNut/OS %s\n", NutVersionString());
00394     printf("TCP Server Sample %s\n", APP_VERSION);
00395 
00396     /*
00397      * Configure the network interface. It is assumed, that 
00398      * we got a valid configuration in non-volatile memory.
00399      *
00400      * For alternatives see
00401      * http://www.ethernut.de/nutwiki/Network_Configuration
00402      */
00403     printf("Configure %s...", DEV_ETHER_NAME);
00404     if (NutRegisterDevice(&DEV_ETHER, 0, 0)) {
00405         FatalError("failed");
00406     }
00407     if (NutDhcpIfConfig("eth0", 0, 60000)) {
00408         FatalError("no valid network configuration");
00409     }
00410     printf("OK\nRun 'telnet %s", inet_ntoa(confnet.cdn_ip_addr));
00411 #if TCP_SERVER_PORT != 23
00412     printf(" %d", TCP_SERVER_PORT);
00413 #endif
00414     puts("' to connect to this server");
00415 
00416     /* Set the start time. */
00417     start_time = time(NULL);
00418 
00419     /*
00420      * Now loop endless for client connections.
00421      *
00422      * Note, that we are only able to serve one client at a time.
00423      * If you want to allow concurrent connections, then this
00424      * loop must run in threads. Then each thread can handle one
00425      * client. See
00426      * http://www.ethernut.de/nutwiki/Multithreading
00427      */
00428     for (;;) {
00429         /* Create a socket. */
00430         if ((sock = NutTcpCreateSocket()) != 0) {
00431             printf("Waiting for a telnet client...");
00432             /* Listen on port 23. If we return, we got a client. */
00433             if (NutTcpAccept(sock, TCP_SERVER_PORT) == 0) {
00434                 puts("connected");
00435 
00436                 /*
00437                  * Open a stream and associate it with the socket, so 
00438                  * we can use standard I/O. Note, that socket streams
00439                  * currently do support cooked text mode.
00440                  */
00441                 stream = _fdopen((int) sock, "r+b");
00442                 if (stream) {
00443                     /* Process client requests as long as the connection is
00444                      * established.
00445                      *
00446                      * Note, that unplugging the network cable will not terminate
00447                      * a TCP connection by default. If you need this, you may set
00448                      * a receive timeout on the socket:
00449                      *
00450                      * uint32_t to = 5000;
00451                      * NutTcpSetSockOpt(sock, SO_RCVTIMEO, &to, sizeof(to));
00452                      *
00453                      * See also
00454                      * http://www.ethernut.de/nutwiki/Socket_Timeouts
00455                      */
00456                     ProcessRequests(stream);
00457                     /* Close the stream. */
00458                     fclose(stream);
00459                 } else {
00460                     puts("Assigning a stream failed");
00461                 }
00462             } else {
00463                 puts("failed");
00464             }
00465 
00466             /* Close our socket. */
00467             NutTcpCloseSocket(sock);
00468             puts("Disconnected");
00469         }
00470     }
00471     /* Never reached, but required to suppress compiler warning. */
00472     return 0;
00473 }