2 * cmd_echoserver.c -- Simple echoserver
4 * Copyright (C) 2014 Czech Technical University in Prague
6 * This echoserver is capable of running multiple instances each running
7 * in the background thread.
8 * Each instance listens on a predefined port, when a connection is
9 * established and some data are received, the same data are then sent
11 * There is support for basic thread management -- it is possible to
12 * either list all the background threads or kill them all.
14 * The code uses LwIP netconn API.
17 * es_ prefix is used as the "echoserver" specific identifier
20 #include "cmd_echoserver.h"
29 #include "lwip/api.h" /* netconn */
31 #include "os/FreeRTOS.h"
34 /* Ethernet interface instance nr. */
35 #define INTERFACE_INSTANCE_NUMBER 0
37 /* Number of statically allocated structs es_thread_context.
38 * This limits the number of concurrently running threads.
40 #define MAX_THREADS_CNT 12
42 /* Run in the foreground, in the context of the command-processor thread */
43 #define ES_COMMAND_RUN_FOREGROUND 1
44 /* Create new background thread */
45 #define ES_COMMAND_NEW_THREAD 2
46 /* List all the background ES threads */
47 #define ES_COMMAND_LIST_THREADS 3
48 /* Kill all the background ES threads */
49 #define ES_COMMAND_KILL_ALL_THREADS 4
51 /* Forward declarations */
52 void es_print_help(void);
54 struct es_thread_context {
55 /* Thread handle; Useful when killing all the threads */
58 /* listening connection */
59 struct netconn *conn_listen;
60 /* used for the netconn_accept() */
63 #define ES_THREAD_TERMINATED 0
64 #define ES_THREAD_RUNNING 1
65 /* Status of the thread represented with this
66 * particular struct es_thread_context
71 * Options passed when executed from the command processor
74 /* Number of the port used to listen to */
79 } es_threads[MAX_THREADS_CNT];
81 static int es_thread_handle_find_first_free(void)
85 for (i = 0; i < MAX_THREADS_CNT; i++) {
86 if (es_threads[i].status == ES_THREAD_TERMINATED)
93 static void es_thread_terminate(struct es_thread_context *thread_context)
95 int err = thread_context->err;
97 /* FIXME maybe not necessary at all */
98 thread_context->port_no = 0;
100 thread_context->err = 0;
101 thread_context->status = ES_THREAD_TERMINATED;
103 if (thread_context->conn_listen) {
104 netconn_close(thread_context->conn_listen);
105 netconn_delete(thread_context->conn_listen);
106 thread_context->conn_listen = NULL;
108 if (thread_context->conn) {
109 netconn_close(thread_context->conn);
110 netconn_delete(thread_context->conn);
111 thread_context->conn = NULL;
114 rpp_sci_printf("Terminating thread %s retcode: %d\n",
115 pcTaskGetTaskName(thread_context->handle), err);
116 vTaskDelete(thread_context->handle);
120 static void es_logic(void *data)
122 struct es_thread_context *thread_context = (struct es_thread_context*)data;
125 thread_context->conn_listen = netconn_new(NETCONN_TCP);
126 if (thread_context->conn_listen == NULL) {
127 rpp_sci_printf("netconn_new() failed\n");
128 thread_context->err = ERR_MEM;
131 err = netconn_bind(thread_context->conn_listen, NULL, thread_context->port_no);
133 thread_context->err = err;
137 err = netconn_listen(thread_context->conn_listen);
139 thread_context->err = err;
143 while (1) { /* keep-alive */
144 err = netconn_accept(thread_context->conn_listen, &thread_context->conn);
146 struct netbuf *netbuf;
150 rpp_sci_printf("accepted new connection %p\n",
151 thread_context->conn);
152 while ((err = netconn_recv(thread_context->conn, &netbuf)) == ERR_OK) {
153 /*printf("Recved\n");*/
155 netbuf_data(netbuf, &data, &len);
156 //rpp_sci_printf("netbuf len: %d\n", (unsigned int)len);
157 err = netconn_write(thread_context->conn,
158 data, len, NETCONN_COPY);
159 /* FIXME NOCOPY or COPY? What if we free the netbuf before its
160 payload is transmitted? */
163 "tcpecho: netconn_write: error \"%s\" (%d)\n",
164 lwip_strerr(err), err);
165 break; /* FIXME is this necessary?
166 Is it possible to continue? */
168 } while (netbuf_next(netbuf) >= 0);
169 netbuf_delete(netbuf);
171 rpp_sci_printf("Got EOF, looping\n");
172 netconn_close(thread_context->conn);
173 netconn_delete(thread_context->conn);
174 /* Necessary for generic resource freeing */
175 thread_context->conn = NULL;
177 thread_context->err = err;
183 netconn_close(thread_context->conn_listen);
184 netconn_delete(thread_context->conn_listen);
185 /* Necessary for generic resource freeing */
186 thread_context->conn_listen = NULL;
190 static void es_thread(void *data)
192 struct es_thread_context *thread_context = (struct es_thread_context*)data;
194 /* Run the echoserver logic */
197 /* It is mandatory to destroy the thread when leaving;
198 * Be aware of the fact that we are terminating ourselves --
199 * in our thread context */
200 es_thread_terminate(thread_context);
202 /* We should never ever get here */
205 int cmd_do_init_es(cmd_io_t *cmd_io, const struct cmd_des *des, char *param[])
207 struct netif *netif = rpp_eth_get_netif(INTERFACE_INSTANCE_NUMBER);
208 struct es_thread_context *thread_context;
209 unsigned char thread_name[5] = "es"; /* thread name, used for debugging only */
213 int command = ES_COMMAND_RUN_FOREGROUND;
214 unsigned int port_no = 0;
217 if (!isPostInitialized()) {
218 rpp_sci_printf("Eth not initialized run 'ethinit' command first.\n");
222 /* Parse the "command line" arguments */
223 for (pindex = 1; param[pindex] != 0; pindex++) {
224 if (strncmp(param[pindex], "-c", 3) == 0) {
225 if (command != ES_COMMAND_RUN_FOREGROUND) {
226 rpp_sci_printf("More than single command are used\n");
229 command = ES_COMMAND_KILL_ALL_THREADS;
230 } else if (strncmp(param[pindex], "-l", 3) == 0) {
231 if (command != ES_COMMAND_RUN_FOREGROUND) {
232 rpp_sci_printf("More than single command are used\n");
235 command = ES_COMMAND_LIST_THREADS;
236 } else if (strncmp(param[pindex], "-t", 3) == 0) {
237 if (command != ES_COMMAND_RUN_FOREGROUND) {
238 rpp_sci_printf("More than single command are used\n");
241 command = ES_COMMAND_NEW_THREAD;
242 } else if (strncmp(param[pindex], "-p", 3) == 0) {
243 /* The validity (!= 0) check is done later */
244 port_no = rpp_eth_portStrToInt((uint8_t *)param[++pindex]);
251 /* Port number is mandatory for interactive or background-thread mode */
252 if (command == ES_COMMAND_RUN_FOREGROUND || command == ES_COMMAND_NEW_THREAD)
256 rpp_sci_printf("Port number not set\n");
260 /* Is there any free preallocated thread handle? */
261 thread_nr = es_thread_handle_find_first_free();
263 rpp_sci_printf("Unable to create new thread."
264 "Maximum number of simultaneously running threads is reached\n");
267 thread_context = &es_threads[thread_nr];
268 thread_context->port_no = port_no;
271 /* Execute the particular command */
272 if (command == ES_COMMAND_NEW_THREAD) {
273 unsigned int thread_nr_tmp;
274 thread_nr_tmp = (thread_nr % 100);
275 thread_name[2] = (thread_nr_tmp/10) + '0';
276 thread_name[3] = (thread_nr_tmp%10) + '0';
277 thread_name[4] = '\0';
278 rpp_sci_printf("Starting thread: %s\n", thread_name);
279 err = xTaskCreate(es_thread,
284 &thread_context->handle);
286 rpp_sci_printf("xTaskCreate() failed\n");
289 thread_context->status = ES_THREAD_RUNNING;
292 } else if (command == ES_COMMAND_RUN_FOREGROUND) {
293 /* Run in the foreground */
294 es_logic((void *)thread_context);
295 } else if (command == ES_COMMAND_LIST_THREADS) {
296 for (i = 0; i < MAX_THREADS_CNT; i++) {
297 if (es_threads[i].status == ES_THREAD_RUNNING) {
298 rpp_sci_printf("Thread %s exists state: %s\n",
299 pcTaskGetTaskName(es_threads[i].handle),
303 } else if (command == ES_COMMAND_KILL_ALL_THREADS) {
304 for (i = 0; i < MAX_THREADS_CNT; i++) {
305 if (es_threads[i].status == ES_THREAD_RUNNING) {
306 es_thread_terminate(&es_threads[i]);
315 #define ES_COMMAND_NAME "ethes"
316 cmd_des_t const cmd_des_es = {
318 ES_COMMAND_NAME, "Start very simple TCP echoserver",
320 ES_COMMAND_NAME " -p NUMBER [-l|-c|-t]\n\n"
321 " -p\tdefine port number to listen to\n"
322 "Optional command:\n"
323 " -l\tlist all running threads\n"
324 " -c\tkill all running threads\n"
325 " -t\tcreate new background thread\n"
326 "If no command is given, the application is run in interactive mode"
327 " (in foreground)\n",
328 CMD_HANDLER(cmd_do_init_es), (void *)&cmd_list_es
332 void es_print_help(void)
334 rpp_sci_printf("%s\n", cmd_des_es.long_help);
338 /** List of commands for lwip, defined as external */
339 cmd_des_t const *cmd_list_es[] = {