]> rtime.felk.cvut.cz Git - pes-rpp/rpp-test-sw.git/commitdiff
Merge branch 'master' into personal/vajnamar/merge-fixes
authorMartin Vajnar <martin.vajnar@gmail.com>
Thu, 30 Jul 2015 12:26:59 +0000 (14:26 +0200)
committerMartin Vajnar <martin.vajnar@gmail.com>
Thu, 30 Jul 2015 12:26:59 +0000 (14:26 +0200)
rpp-lib
rpp-test-sw/Makefile.var
rpp-test-sw/commands/cmd.c
rpp-test-sw/commands/cmd_echoserver.c [new file with mode: 0644]
rpp-test-sw/commands/cmd_echoserver.h [new file with mode: 0644]

diff --git a/rpp-lib b/rpp-lib
index 038aadd53b522e5bf269131fb6cba29c22180398..054ee07126b1a4be43ed6d77261bf20c89fd3433 160000 (submodule)
--- a/rpp-lib
+++ b/rpp-lib
@@ -1 +1 @@
-Subproject commit 038aadd53b522e5bf269131fb6cba29c22180398
+Subproject commit 054ee07126b1a4be43ed6d77261bf20c89fd3433
index 8ce88a525f14334122743fc17be1f10291320702..c33ba4d0d8ca8242c64801346a2f6de3520d8280 100644 (file)
@@ -30,7 +30,8 @@ SOURCES_tms570_rpp = \
        commands/cmd_netstats.c \
        commands/cmd_sdram.c \
        commands/cmd_spi.c \
-       commands/cmd_vbat.c
+       commands/cmd_vbat.c \
+       commands/cmd_echoserver.c
 
 
 SOURCES += $(SOURCES_$(TARGET))
index 5a01bb6f62b4849c2af7c715951debe816f67e1e..b02c1e5b245c1d15c6d41329769187ffd324daf1 100644 (file)
@@ -36,6 +36,7 @@
 #include "cmd_lin.h"
 #include "cmd_lout.h"
 #include "cmd_nc.h"
+#include "cmd_echoserver.h"
 #include "cmd_netstats.h"
 #include "cmd_sdram.h"
 #include "cmd_spi.h"
@@ -159,6 +160,7 @@ cmd_des_t const *cmd_list_main[] = {
        CMD_DES_INCLUDE_SUBLIST(cmd_list_lout),
        CMD_DES_INCLUDE_SUBLIST(cmd_list_motor_example),
        CMD_DES_INCLUDE_SUBLIST(cmd_list_nc),
+       CMD_DES_INCLUDE_SUBLIST(cmd_list_es),
        CMD_DES_INCLUDE_SUBLIST(cmd_list_netstats),
 #endif
        CMD_DES_INCLUDE_SUBLIST(cmd_list_pin),
diff --git a/rpp-test-sw/commands/cmd_echoserver.c b/rpp-test-sw/commands/cmd_echoserver.c
new file mode 100644 (file)
index 0000000..eadbc39
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * cmd_echoserver.c -- Simple echoserver
+ *
+ * Copyright (C) 2014 Czech Technical University in Prague
+ *
+ * This echoserver is capable of running multiple instances each running
+ * in the background thread.
+ * Each instance listens on a predefined port, when a connection is
+ * established and some data are received, the same data are then sent
+ * back to the client.
+ * There is support for basic thread management -- it is possible to
+ * either list all the background threads or kill them all.
+ *
+ * The code uses LwIP netconn API.
+ *
+ * Nomenclature:
+ *     es_ prefix is used as the "echoserver" specific identifier
+ */
+
+#include "cmd_echoserver.h"
+
+#ifndef DOCGEN
+
+#include <string.h>
+#include <ctype.h>
+
+#include "rpp/rpp.h"
+#include "lwip/udp.h"
+#include "lwip/api.h" /* netconn */
+
+#include "os/FreeRTOS.h"
+#include "os/task.h"
+
+/* Ethernet interface instance nr. */
+#define INTERFACE_INSTANCE_NUMBER      0
+
+/* Number of statically allocated structs es_thread_context.
+ * This limits the number of concurrently running threads.
+ */
+#define MAX_THREADS_CNT                        12
+
+/* Run in the foreground, in the context of the command-processor thread */
+#define ES_COMMAND_RUN_FOREGROUND      1
+/* Create new background thread */
+#define ES_COMMAND_NEW_THREAD          2
+/* List all the background ES threads */
+#define ES_COMMAND_LIST_THREADS                3
+/* Kill all the background ES threads */
+#define ES_COMMAND_KILL_ALL_THREADS    4
+
+/* Forward declarations */
+void es_print_help(void);
+
+struct es_thread_context {
+       /* Thread handle; Useful when killing all the threads */
+       xTaskHandle handle;
+
+       /* listening connection */
+       struct netconn *conn_listen;
+       /* used for the netconn_accept() */
+       struct netconn *conn;
+
+#define ES_THREAD_TERMINATED           0
+#define ES_THREAD_RUNNING              1
+       /* Status of the thread represented with this
+        * particular struct es_thread_context
+        */
+       int status;
+
+       /*
+        * Options passed when executed from the command processor
+        */
+
+       /* Number of the port used to listen to */
+       unsigned int port_no;
+
+       /* Return code */
+       int err;
+} es_threads[MAX_THREADS_CNT];
+
+static int es_thread_handle_find_first_free(void)
+{
+       int i;
+
+       for (i = 0; i < MAX_THREADS_CNT; i++) {
+               if (es_threads[i].status == ES_THREAD_TERMINATED)
+                       return i;
+       }
+
+       return -1;
+}
+
+static void es_thread_terminate(struct es_thread_context *thread_context)
+{
+       int err = thread_context->err;
+
+       /* FIXME maybe not necessary at all */
+       thread_context->port_no = 0;
+
+       thread_context->err = 0;
+       thread_context->status = ES_THREAD_TERMINATED;
+
+       if (thread_context->conn_listen) {
+               netconn_close(thread_context->conn_listen);
+               netconn_delete(thread_context->conn_listen);
+               thread_context->conn_listen = NULL;
+       }
+       if (thread_context->conn) {
+               netconn_close(thread_context->conn);
+               netconn_delete(thread_context->conn);
+               thread_context->conn = NULL;
+       }
+
+       rpp_sci_printf("Terminating thread %s retcode: %d\n",
+                       pcTaskGetTaskName(thread_context->handle), err);
+       vTaskDelete(thread_context->handle);
+}
+
+
+static void es_logic(cmd_io_t *cmd_io, void *data)
+{
+       struct es_thread_context *thread_context = (struct es_thread_context*)data;
+       err_t err = ERR_OK;
+
+       thread_context->conn_listen = netconn_new(NETCONN_TCP);
+       if (thread_context->conn_listen == NULL) {
+               rpp_sci_printf("netconn_new() failed\n");
+               thread_context->err = ERR_MEM;
+               return;
+       }
+       err = netconn_bind(thread_context->conn_listen, NULL, thread_context->port_no);
+       if (err != ERR_OK) {
+               thread_context->err = err;
+               goto leave_clean;
+       }
+
+       err = netconn_listen(thread_context->conn_listen);
+       if (err != ERR_OK) {
+               thread_context->err = err;
+               goto leave_clean;
+       }
+
+       while (cmd_io == NULL || cmd_io->getc(cmd_io) < 0) { /* keep-alive */
+               err = netconn_accept(thread_context->conn_listen, &thread_context->conn);
+               if (err == ERR_OK) {
+                       struct netbuf *netbuf;
+                       void *data;
+                       u16_t len;
+
+                       rpp_sci_printf("accepted new connection %p\n",
+                               thread_context->conn);
+                       while ((err = netconn_recv(thread_context->conn, &netbuf)) == ERR_OK) {
+                               /*printf("Recved\n");*/
+                               do {
+                                       netbuf_data(netbuf, &data, &len);
+                                       //rpp_sci_printf("netbuf len: %d\n", (unsigned int)len);
+                                       err = netconn_write(thread_context->conn,
+                                                           data, len, NETCONN_COPY);
+                                       /* FIXME NOCOPY or COPY? What if we free the netbuf before its
+                                          payload is transmitted? */
+                                       if (err != ERR_OK) {
+                                               rpp_sci_printf(
+                                                       "tcpecho: netconn_write: error \"%s\" (%d)\n",
+                                                       lwip_strerr(err), err);
+                                                       break; /* FIXME is this necessary?
+                                                               Is it possible to continue? */
+                                       }
+                               } while (netbuf_next(netbuf) >= 0);
+                               netbuf_delete(netbuf);
+                       }
+                       rpp_sci_printf("Got EOF, looping\n");
+                       netconn_close(thread_context->conn);
+                       netconn_delete(thread_context->conn);
+                       /* Necessary for generic resource freeing */
+                       thread_context->conn = NULL;
+               } else {
+                       thread_context->err = err;
+                       return;
+               }
+       }
+
+leave_clean:
+       netconn_close(thread_context->conn_listen);
+       netconn_delete(thread_context->conn_listen);
+       /* Necessary for generic resource freeing */
+       thread_context->conn_listen = NULL;
+       return;
+}
+
+static void es_thread(void *data)
+{
+       struct es_thread_context *thread_context = (struct es_thread_context*)data;
+
+       /* Run the echoserver logic */
+       es_logic(NULL, data);
+
+       /* It is mandatory to destroy the thread when leaving;
+        * Be aware of the fact that we are terminating ourselves --
+        * in our thread context */
+       es_thread_terminate(thread_context);
+
+       /* We should never ever get here */
+}
+
+int cmd_do_init_es(cmd_io_t *cmd_io, const struct cmd_des *des, char *param[])
+{
+       struct netif *netif = rpp_eth_get_netif(INTERFACE_INSTANCE_NUMBER);
+       struct es_thread_context *thread_context;
+       unsigned char thread_name[5] = "es"; /* thread name, used for debugging only */
+       err_t err = ERR_OK;
+       uint8_t pindex;
+       int thread_nr;
+       int command = ES_COMMAND_RUN_FOREGROUND;
+       unsigned int port_no = 0;
+       int i;
+
+       if (!isPostInitialized()) {
+               rpp_sci_printf("Eth not initialized run 'ethinit' command first.\n");
+               return FAILURE;
+       }
+
+       /* Parse the "command line" arguments */
+       for (pindex = 1; param[pindex] != 0; pindex++) {
+               if (strncmp(param[pindex], "-c", 3) == 0) {
+                       if (command != ES_COMMAND_RUN_FOREGROUND) {
+                               rpp_sci_printf("More than single command are used\n");
+                               return FAILURE;
+                       }
+                       command = ES_COMMAND_KILL_ALL_THREADS;
+               } else if (strncmp(param[pindex], "-l", 3) == 0) {
+                       if (command != ES_COMMAND_RUN_FOREGROUND) {
+                               rpp_sci_printf("More than single command are used\n");
+                               return FAILURE;
+                       }
+                       command = ES_COMMAND_LIST_THREADS;
+               } else if (strncmp(param[pindex], "-t", 3) == 0) {
+                       if (command != ES_COMMAND_RUN_FOREGROUND) {
+                               rpp_sci_printf("More than single command are used\n");
+                               return FAILURE;
+                       }
+                       command = ES_COMMAND_NEW_THREAD;
+               } else if (strncmp(param[pindex], "-p", 3) == 0) {
+                       /* The validity (!= 0) check is done later */
+                       port_no = rpp_eth_portStrToInt((uint8_t *)param[++pindex]);
+               } else {
+                       es_print_help();
+                       return FAILURE;
+               }
+       }
+
+       /* Port number is mandatory for interactive or background-thread mode */
+       if (command == ES_COMMAND_RUN_FOREGROUND || command == ES_COMMAND_NEW_THREAD)
+       {
+               if (!port_no)
+               {
+                       rpp_sci_printf("Port number not set\n");
+                       return FAILURE;
+               }
+
+               /* Is there any free preallocated thread handle? */
+               thread_nr = es_thread_handle_find_first_free();
+               if (thread_nr < 0) {
+                       rpp_sci_printf("Unable to create new thread."
+                               "Maximum number of simultaneously running threads is reached\n");
+                       return FAILURE;
+               }
+               thread_context = &es_threads[thread_nr];
+               thread_context->port_no = port_no;
+       }
+
+       /* Execute the particular command */
+       if (command == ES_COMMAND_NEW_THREAD) {
+               unsigned int thread_nr_tmp;
+               thread_nr_tmp = (thread_nr % 100);
+               thread_name[2] = (thread_nr_tmp/10) + '0';
+               thread_name[3] = (thread_nr_tmp%10) + '0';
+               thread_name[4] = '\0';
+               rpp_sci_printf("Starting thread: %s\n", thread_name);
+               err = xTaskCreate(es_thread,
+                                 thread_name,
+                                 esTaskStackSize,
+                                 thread_context,
+                                 esTaskPriority,
+                                 &thread_context->handle);
+               if (err != pdPASS) {
+                       rpp_sci_printf("xTaskCreate() failed\n");
+                       return FAILURE;
+               } else {
+                       thread_context->status = ES_THREAD_RUNNING;
+               }
+
+       } else if (command == ES_COMMAND_RUN_FOREGROUND) {
+               /* Run in the foreground */
+               es_logic(cmd_io, (void *)thread_context);
+       } else if (command == ES_COMMAND_LIST_THREADS) {
+               for (i = 0; i < MAX_THREADS_CNT; i++) {
+                       if (es_threads[i].status == ES_THREAD_RUNNING) {
+                               rpp_sci_printf("Thread %s exists state: %s\n",
+                                       pcTaskGetTaskName(es_threads[i].handle),
+                                       "unknown");
+                       }
+               }
+       } else if (command == ES_COMMAND_KILL_ALL_THREADS) {
+               for (i = 0; i < MAX_THREADS_CNT; i++) {
+                       if (es_threads[i].status == ES_THREAD_RUNNING) {
+                               es_thread_terminate(&es_threads[i]);
+                       }
+               }
+       }
+       return ERR_OK;
+}
+
+#endif  /* DOCGEN */
+
+#define ES_COMMAND_NAME                                "ethes"
+cmd_des_t const cmd_des_es = {
+       0, CDESM_SPACE_SEP,
+       ES_COMMAND_NAME, "Start very simple TCP echoserver",
+       "Syntax:\n"
+       ES_COMMAND_NAME " -p NUMBER [-l|-c|-t]\n\n"
+       "    -p\tdefine port number to listen to\n"
+       "Optional command:\n"
+       "    -l\tlist all running threads\n"
+       "    -c\tkill all running threads\n"
+       "    -t\tcreate new background thread\n"
+       "If no command is given, the application is run in interactive mode"
+       " (in foreground)\n",
+       CMD_HANDLER(cmd_do_init_es), (void *)&cmd_list_es
+};
+
+#ifndef DOCGEN
+void es_print_help(void)
+{
+       rpp_sci_printf("%s\n", cmd_des_es.long_help);
+}
+#endif  /* DOCGEN */
+
+/** List of commands for lwip, defined as external */
+cmd_des_t const *cmd_list_es[] = {
+       &cmd_des_es,
+       NULL
+};
diff --git a/rpp-test-sw/commands/cmd_echoserver.h b/rpp-test-sw/commands/cmd_echoserver.h
new file mode 100644 (file)
index 0000000..3df8432
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _CMD_ES_H_
+#define _CMD_ES_H_
+
+#define esTaskStackSize                        500
+#define esTaskPriority                 0
+
+/* return values */
+#define BAD_IP_ADDR                    -21
+#define BAD_PORT_NO                    -22
+#define BAD_OPTION                     -23
+#define ERR_NETCONN_NEW                        -30
+#define ERR_BINDING                    -31
+#define ERR_CONN_ACCEPT                        -32
+#define ERR_CONNECTING                 -33
+#define ERR_SENDING                    -34
+#define ERR_RECEIVING                  -35
+
+#include "cmdproc.h"
+
+extern cmd_des_t const *cmd_list_es[];
+
+#endif /* _CMD_ES_H_ */