tcps.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. /*
  2. * Copyright (C) 2009 by egnite GmbH
  3. * Copyright (C) 2001-2005 by egnite Software GmbH
  4. *
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 3. Neither the name of the copyright holders nor the names of
  17. * contributors may be used to endorse or promote products derived
  18. * from this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  23. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  24. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  25. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  26. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  27. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  28. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  29. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  30. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. *
  33. * For additional information see http://www.ethernut.de/
  34. *
  35. */
  36. /*!
  37. * $Id: tcps.c 4640 2012-09-24 12:05:56Z u_bonnes $
  38. */
  39. /*!
  40. * \example tcps/tcps.c
  41. *
  42. * Simple TCP server.
  43. *
  44. * \code telnet x.x.x.x \endcode
  45. *
  46. * on a command prompt, replacing x.x.x.x with the
  47. * IP address of your target board. Enter help
  48. * for a list of available commands.
  49. */
  50. /* Device specific definitions. */
  51. #include <dev/board.h>
  52. #include <dev/reset.h>
  53. #include <dev/gpio.h>
  54. /* OS specific definitions. */
  55. #include <sys/version.h>
  56. #include <sys/confnet.h>
  57. #include <sys/heap.h>
  58. #include <sys/timer.h>
  59. #include <sys/socket.h>
  60. /* Network specific definitions. */
  61. #include <arpa/inet.h>
  62. #include <net/if_var.h>
  63. #include <pro/dhcp.h>
  64. /* Standard C header files. */
  65. #include <stdlib.h>
  66. #include <stdio.h>
  67. #include <io.h>
  68. #include <string.h>
  69. #include <time.h>
  70. #include <ctype.h>
  71. /* Version of this application sample. */
  72. #define APP_VERSION "2.0.0"
  73. /* Max. size of the input line. */
  74. #define MAX_INPUT_LINE 32
  75. /* TCP server port. Telnet default is 23. */
  76. #define TCP_SERVER_PORT 23
  77. static time_t start_time;
  78. /*
  79. * Halt the application on fatal errors.
  80. */
  81. static void FatalError(char *msg)
  82. {
  83. /* Print a message ... */
  84. puts(msg);
  85. /* ... and never return. */
  86. for (;;);
  87. }
  88. /*
  89. * Extract command and up to 2 parameters from an input line.
  90. *
  91. * This helper routine splits a line into words. A pointer to the
  92. * first word is returned as a function result. A pointer to an
  93. * optional second word is set via a funtion parameter and a
  94. * pointer to the rest of the line is set via a second function
  95. * parameter.
  96. *
  97. * Example:
  98. *
  99. * If line points to "help me to get this done", then we can use
  100. *
  101. * char *cmd;
  102. * char *param1;
  103. * char *param2;
  104. *
  105. * cmd = ParseLine(line, &param1, &param2);
  106. *
  107. * On return, cmd will point to "help", param1 will point to "me"
  108. * and param 2 will point to "to get this done".
  109. *
  110. * If the line contains less than 3 words, then the second
  111. * parameter pointer is set to NULL. With one word only, also
  112. * the first parameter pointer is set to NULL. The function
  113. * result is NULL on empty lines, including lines containing
  114. * all spaces.
  115. *
  116. * Leading spaces are skipped. Trailing end of line characters
  117. * are removed.
  118. *
  119. * Note, that the original contents of the line will be
  120. * modified.
  121. */
  122. static char *ParseLine(char *line, char **pp1, char **pp2)
  123. {
  124. char *p0;
  125. char *cp;
  126. /* Initialize parameter pointers to NULL. */
  127. *pp1 = NULL;
  128. *pp2 = NULL;
  129. /* Chop off EOL. */
  130. cp = strchr(line, '\r');
  131. if (cp) {
  132. *cp = 0;
  133. }
  134. cp = strchr(line, '\n');
  135. if (cp) {
  136. *cp = 0;
  137. }
  138. /*
  139. * Parse line for command and parameters.
  140. */
  141. p0 = line;
  142. while (isspace((int)*p0)) {
  143. /* Skip leading spaces. */
  144. p0++;
  145. }
  146. if (*p0 == '\0') {
  147. /* Return NULL on empty lines. */
  148. return NULL;
  149. }
  150. cp = strchr(p0, ' ');
  151. if (cp) {
  152. *cp++ = '\0';
  153. while (isspace((int)*cp)) {
  154. /* Skip leading spaces. */
  155. cp++;
  156. }
  157. if (*cp) {
  158. /* First parameter found. */
  159. *pp1 = cp;
  160. cp = strchr(cp, ' ');
  161. } else {
  162. cp = NULL;
  163. }
  164. if (cp) {
  165. *cp++ = '\0';
  166. while (isspace((int)*cp)) {
  167. /* Skip leading spaces. */
  168. cp++;
  169. }
  170. if (*cp) {
  171. /* Remaining parameter(s) found. */
  172. *pp2 = cp;
  173. }
  174. }
  175. }
  176. /* Return pointer to command. */
  177. return p0;
  178. }
  179. /*
  180. * Process client requests.
  181. *
  182. * This function is called when a connection has been established
  183. * and returns when the connection is closed.
  184. */
  185. static void ProcessRequests(FILE * stream)
  186. {
  187. char *buff;
  188. char *cmd;
  189. size_t clen;
  190. char *p1;
  191. char *p2;
  192. /*
  193. * Allocate an input buffer. Check the result.
  194. */
  195. buff = malloc(MAX_INPUT_LINE + 1);
  196. if (buff == NULL) {
  197. return;
  198. }
  199. /*
  200. * Send a welcome banner to the new client.
  201. */
  202. fputs("200 Welcome to tcps. Type help to get help.\r\n", stream);
  203. for (;;) {
  204. /*
  205. * Flush any pending output and read in a new line.
  206. *
  207. * If you want line editing capabilities, check
  208. * http://www.ethernut.de/nutwiki/Input_Line_Editor
  209. */
  210. fflush(stream);
  211. if (fgets(buff, MAX_INPUT_LINE, stream) == NULL) {
  212. /* Probably a disconnect, return. */
  213. break;
  214. }
  215. /* Parse the input line. */
  216. cmd = ParseLine(buff, &p1, &p2);
  217. if (cmd == NULL) {
  218. /* Skip empty lines. */
  219. continue;
  220. }
  221. /* Retrieve command length for abbreviations. */
  222. clen = strlen(cmd);
  223. /*
  224. * Process memory info request.
  225. *
  226. * http://www.ethernut.de/nutwiki/Heap_Memory
  227. */
  228. if (strncmp(cmd, "heap", clen) == 0) {
  229. fprintf(stream, "210 %u bytes RAM free\r\n", (unsigned int)NutHeapAvailable());
  230. continue;
  231. }
  232. /*
  233. * Process IP address configuration.
  234. */
  235. if (strncmp(cmd, "ip", clen) == 0) {
  236. uint32_t ip = p1 ? inet_addr(p1) : (uint32_t) -1;
  237. if (ip == (uint32_t) -1) {
  238. fputs("420 Invalid or missing address\r\n", stream);
  239. } else {
  240. confnet.cdn_cip_addr = ip;
  241. if (NutNetSaveConfig()) {
  242. fputs("421 Failed to save configuration\r\n", stream);
  243. } else {
  244. fputs("220 Configuration saved\r\n", stream);
  245. }
  246. }
  247. continue;
  248. }
  249. /*
  250. * Process IP mask configuration.
  251. */
  252. if (strncmp(cmd, "mask", clen) == 0) {
  253. uint32_t mask = p1 ? inet_addr(p1) : (uint32_t) -1;
  254. if (mask == (uint32_t) -1) {
  255. fputs("430 Invalid or missing mask\r\n", stream);
  256. } else {
  257. confnet.cdn_ip_mask = mask;
  258. if (NutNetSaveConfig()) {
  259. fputs("421 Failed to save configuration\r\n", stream);
  260. } else {
  261. fputs("230 Configuration saved\r\n", stream);
  262. }
  263. }
  264. continue;
  265. }
  266. #ifndef MCU_GBA
  267. /*
  268. * Process GPIO pin status, not available on GameBoy.
  269. *
  270. * http://www.ethernut.de/nutwiki/LowLevelPortIo
  271. */
  272. if (strncmp(cmd, "pin", clen) == 0) {
  273. int bank = p1 ? atoi(p1) : 0;
  274. int bit = p2 ? atoi(p2) : 0;
  275. int state = GpioPinGet(bank, bit);
  276. fprintf(stream, "240 %d at GPIO bank %d bit %d\r\n", state, bank, bit);
  277. continue;
  278. }
  279. #endif
  280. /*
  281. * Process serial line send request.
  282. */
  283. if (strncmp(cmd, "send", clen) == 0) {
  284. if (p1) {
  285. printf("%s", p1);
  286. if (p1) {
  287. printf(" %s", p2);
  288. }
  289. }
  290. putchar('\n');
  291. fputs("250 Message sent\r\n", stream);
  292. continue;
  293. }
  294. /*
  295. * Process time info request.
  296. */
  297. if (strncmp(cmd, "uptime", clen) == 0) {
  298. fprintf(stream, "220 %ld seconds running\r\n", (long)(time(NULL) - start_time));
  299. continue;
  300. }
  301. /*
  302. * Process system reset request.
  303. *
  304. * http://www.ethernut.de/nutwiki/System_Reset
  305. */
  306. if (strncmp(cmd, "reset", clen) == 0) {
  307. fputs("910 System reset\r\n", stream);
  308. fflush(stream);
  309. NutSleep(1000);
  310. NutReset();
  311. fputs("490 System reset not implemented\r\n", stream);
  312. continue;
  313. }
  314. /*
  315. * Quit connection.
  316. */
  317. if (strncmp(cmd, "quit", clen) == 0) {
  318. fputs("900 Bye\r\n", stream);
  319. fflush(stream);
  320. break;
  321. }
  322. /*
  323. * Display help text on any unknown command.
  324. */
  325. fputs("400 List of commands follows\r\n"
  326. "h[eap] Query heap memory bytes available.\r\n"
  327. "i[p] Set IP <address>.\r\n"
  328. "m[ask] Set IP <mask>.\r\n"
  329. #ifndef MCU_GBA
  330. "p[in] Query status of GPIO pin <bank> <bit>.\r\n"
  331. #endif
  332. "r[eset] Reset system.\r\n"
  333. "s[end] Send <message> to serial port.\r\n"
  334. "u[ptime] Query number of seconds the system is running.\r\n"
  335. "q[uit] Terminates connection.\r\n"
  336. ".\r\n", stream);
  337. }
  338. free(buff);
  339. }
  340. /*
  341. * Main application routine.
  342. *
  343. * Nut/OS automatically calls this entry after initialization.
  344. */
  345. int main(void)
  346. {
  347. TCPSOCKET *sock;
  348. FILE *stream;
  349. uint32_t baud = 115200;
  350. /*
  351. * Assign stdout to the DEBUG device.
  352. */
  353. NutRegisterDevice(&DEV_CONSOLE, 0, 0);
  354. freopen(DEV_CONSOLE.dev_name, "w", stdout);
  355. _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
  356. /*
  357. * Print out our version information.
  358. */
  359. printf("\n\nNut/OS %s\n", NutVersionString());
  360. printf("TCP Server Sample %s\n", APP_VERSION);
  361. /*
  362. * Configure the network interface. It is assumed, that
  363. * we got a valid configuration in non-volatile memory.
  364. *
  365. * For alternatives see
  366. * http://www.ethernut.de/nutwiki/Network_Configuration
  367. */
  368. printf("Configure %s...", DEV_ETHER_NAME);
  369. if (NutRegisterDevice(&DEV_ETHER, 0, 0)) {
  370. FatalError("failed");
  371. }
  372. if (NutDhcpIfConfig("eth0", 0, 60000)) {
  373. FatalError("no valid network configuration");
  374. }
  375. printf("OK\nRun 'telnet %s", inet_ntoa(confnet.cdn_ip_addr));
  376. #if TCP_SERVER_PORT != 23
  377. printf(" %d", TCP_SERVER_PORT);
  378. #endif
  379. puts("' to connect to this server");
  380. /* Set the start time. */
  381. start_time = time(NULL);
  382. /*
  383. * Now loop endless for client connections.
  384. *
  385. * Note, that we are only able to serve one client at a time.
  386. * If you want to allow concurrent connections, then this
  387. * loop must run in threads. Then each thread can handle one
  388. * client. See
  389. * http://www.ethernut.de/nutwiki/Multithreading
  390. */
  391. for (;;) {
  392. /* Create a socket. */
  393. if ((sock = NutTcpCreateSocket()) != 0) {
  394. printf("Waiting for a telnet client...");
  395. /* Listen on port 23. If we return, we got a client. */
  396. if (NutTcpAccept(sock, TCP_SERVER_PORT) == 0) {
  397. puts("connected");
  398. /*
  399. * Open a stream and associate it with the socket, so
  400. * we can use standard I/O. Note, that socket streams
  401. * currently do support cooked text mode.
  402. */
  403. stream = _fdopen((int) sock, "r+b");
  404. if (stream) {
  405. /* Process client requests as long as the connection is
  406. * established.
  407. *
  408. * Note, that unplugging the network cable will not terminate
  409. * a TCP connection by default. If you need this, you may set
  410. * a receive timeout on the socket:
  411. *
  412. * uint32_t to = 5000;
  413. * NutTcpSetSockOpt(sock, SO_RCVTIMEO, &to, sizeof(to));
  414. *
  415. * See also
  416. * http://www.ethernut.de/nutwiki/Socket_Timeouts
  417. */
  418. ProcessRequests(stream);
  419. /* Close the stream. */
  420. fclose(stream);
  421. } else {
  422. puts("Assigning a stream failed");
  423. }
  424. } else {
  425. puts("failed");
  426. }
  427. /* Close our socket. */
  428. NutTcpCloseSocket(sock);
  429. puts("Disconnected");
  430. }
  431. }
  432. /* Never reached, but required to suppress compiler warning. */
  433. return 0;
  434. }