]> rtime.felk.cvut.cz Git - pes-rpp/rpp-test-sw.git/blob - rpp-test-sw/commands/cmd_nc.c
Change license to MIT
[pes-rpp/rpp-test-sw.git] / rpp-test-sw / commands / cmd_nc.c
1 /*
2  * Copyright (C) 2012-2013, 2015 Czech Technical University in Prague
3  *
4  * Created on: Aug 9, 2013
5  *
6  * Authors:
7  *     - Jan Doležal
8  *
9  * Permission is hereby granted, free of charge, to any person
10  * obtaining a copy of this software and associated documentation
11  * files (the "Software"), to deal in the Software without
12  * restriction, including without limitation the rights to use,
13  * copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following
16  * conditions:
17  *
18  * The above copyright notice and this permission notice shall be
19  * included in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28  * OTHER DEALINGS IN THE SOFTWARE.
29  *
30  * File : cmd_lwip.c
31  *
32  * Abstract:
33  *      This file contains commands for LwIP test
34  *
35  */
36
37 #include "cmd_nc.h"
38
39 #ifndef DOCGEN
40
41 #include <string.h>
42 #include <ctype.h>
43
44 #include "rpp/rpp.h"
45 #include "lwip/udp.h"
46 #if !NO_SYS
47 #include "lwip/api.h" //netconn
48 #else
49 #include "lwip/timers.h" //for updating timers, when NO_SYS == 1
50 #include "lwip/tcp.h"
51 #include "lwip/tcp_impl.h"
52 #endif
53
54 #define INTERFACE_INSTANCE_NUMBER 0
55
56 #define BUF_SIZE 80
57 /* only for use in interactive mode (not thread) */
58 char in_buffer[BUF_SIZE];
59 uint8_t buff_index = 0;
60
61 /* when -p option not given, srcPort is used; when nc run repeatedly,
62  * even though netconn was removed, netconn_new() with previously used port
63  * might still cause errors like address in use, therefore in srcPort
64  * there next port NO prepared to be used */
65 uint16_t srcPort = 1025; /* initial value */
66
67 #if !NO_SYS
68 uint8_t tasks_running = 0, taskNameNum = 0;
69 boolean_t closeths; /* variable controling threads closing */
70
71 /* argument storing parameters from command line, if started as thread, they are coppied to stack of thread and cmd_do_init_nc() is acknowledged by semaphore */
72 struct nc_arg {
73         uint16_t portNO, srcPortNO;
74         ip_addr_t remoteIP;
75         xSemaphoreHandle args_coppied;
76         boolean_t thread; /* variable controling whether this instance should run as thread */
77         boolean_t udp, listen, netLoop, test, srcPortSpec;
78         err_t err;
79 };
80
81
82 /* nc task */
83 void run_nc(void *arg)
84 {
85         err_t err = ERR_OK;
86         uint8_t input = 0;
87         uint8_t tries;
88         uint32_t index;
89         uint8_t *payload;
90         struct netconn *netconn, *newconn;
91
92         newconn = NULL;
93         struct netbuf *remoteData = NULL, *localData = NULL;
94         struct pbuf *p;
95         boolean_t ncStop = FALSE;
96         boolean_t flush = FALSE;
97         struct nc_arg *nc_arg = (struct nc_arg *)arg;
98         struct netif *netif = rpp_eth_get_netif(INTERFACE_INSTANCE_NUMBER);
99
100         /* fill options from argument */
101         uint16_t portNO = nc_arg->portNO, srcPortNO = nc_arg->srcPortNO;
102         ip_addr_t remoteIP = nc_arg->remoteIP;
103         boolean_t udp = nc_arg->udp, listen = nc_arg->listen, srcPortSpec = nc_arg->srcPortSpec, netLoop = nc_arg->netLoop, test = nc_arg->test;
104         boolean_t thread = nc_arg->thread; /* variable controling whether this instance should run as thread */
105 #if DEBUG
106         uint8_t thread_inst = (taskNameNum-1); /* store number of this thread */
107 #endif
108         tasks_running++;
109
110
111         /* switch off closing of tasks/threads */
112         closeths = FALSE;
113
114
115         /*** make connection ***/
116         while (!closeths) { /* shouldn't get here twice, used only for break which leads us down to unalocating of sources */
117                 /* let nc initiator go */
118                 if (thread && /* just in case --> */ nc_arg->args_coppied != NULL && xSemaphoreGive(nc_arg->args_coppied) != pdTRUE) break;
119                 /* fill netconn struct */
120                 netconn = netconn_new(udp ? NETCONN_UDP : NETCONN_TCP);
121                 if (netconn == NULL) {
122                         err = ERR_NETCONN_NEW;
123                         break;
124                 }
125
126                 tries = 15; /* how many local ports to try */
127                 /* bind filled netconn with local interface address and source port number */
128                 while ( tries-- && (err = netconn_bind(netconn, &(netif->ip_addr), srcPortNO) ) == ERR_USE && !closeths) {
129                         if (srcPortSpec) break;  /* if port was explicitly specified (we shouldn't change user's port number just like that ;) ) */
130                         srcPort++;
131                         srcPortNO = srcPort;
132                 }
133                 if (err != ERR_OK || !tries) {
134                         newconn = netconn; /* deleting newconn is done anyway, so delete netconn this way */
135                         err = ERR_BINDING;
136                         break;
137                 }
138
139                 /* adjust recvtimeout time and tries to different values, if user shouldn't loose control over cmd processor at any time */
140                 netconn_set_recvtimeout(netconn, CONNECTING_TIMEO);
141                 tries = CONNECTING_TRIES;
142
143                 if (listen) { /* -l <PORT> option */
144                         if (!udp) { /* TCP - make connection */
145                                 /* make listening netconn */
146                                 netconn_listen(netconn);
147                                 /* accept connection to newconn */
148                                 while ( tries-- && (err = netconn_accept(netconn, &newconn)) == ERR_TIMEOUT && (thread || rpp_sci_read_nb(1, &input) != SUCCESS)) ;
149                                 if (err != ERR_OK) {
150                                         newconn = netconn;
151                                         err = ERR_CONN_ACCEPT;
152                                         break;
153                                 }
154                                 else
155                                         netconn_delete(netconn);  /* we don't need listening netconn anymore */
156                         }
157                         else
158                         {
159                                 while (tries-- && !closeths) {
160                                         /* allow user to interrupt waiting for "udp connection" - in case this is not thread */
161                                         if (!thread && (rpp_sci_read_nb(1, &input) == SUCCESS)) {
162                                                 newconn = netconn; /* deleting newconn is done anyway, so delete netconn this way */
163                                                 err = ERR_CONNECTING;
164                                                 break;
165                                         }
166
167                                         if ((err = netconn_recv(netconn, &remoteData)) == ERR_OK) { /* wait for data, from which we will determine our peer address and portNO */
168                                                 remoteIP = remoteData->addr;
169                                                 portNO = remoteData->port;
170                                                 if (test) netbuf_delete(remoteData);
171                                                 break;
172                                         }
173                                         else
174                                         {
175                                                 if (err == ERR_TIMEOUT) continue;
176                                                 else
177                                                 {
178                                                         newconn = netconn;
179                                                         break;
180                                                 }
181                                         }
182                                 }
183                                 if (!tries) {
184                                         newconn = netconn;
185                                         err = ERR_CONNECTING;
186                                         break;
187                                 }
188                         }
189                 }
190
191                 if (!listen || (listen && udp) ) { /* connect to remote node */
192                         newconn = netconn;
193                         netconn = NULL;
194                         if ( ( err = netconn_connect(newconn, &remoteIP, portNO) ) != ERR_OK )
195                                 break;
196                 }
197
198                 /* we will block for time specified on next line on netconn to receive data, if it does not come, we block for user sci input */
199                 netconn_set_recvtimeout(newconn, 50);
200
201                 if (udp && (localData = netbuf_new()) == NULL) {
202                         err = ERR_MEM;
203                         break;
204                 }
205
206                 if (test) { /* -d option; sends packets to the opposite node and doesn't receive anything */
207                         index = 0;
208                         char strbuf[13];
209                         int strlen;
210                         //netconn_set_recvtimeout(newconn, 1);
211                         while (!closeths && index++ < 4294967295) {
212                                 /* destroy all incomming data to prevent pbufs from running out */
213                                 /*while(remoteData != NULL ||  (err = netconn_recv ( newconn, &remoteData )) != ERR_TIMEOUT){
214                                     netbuf_delete(remoteData);
215                                     remoteData = NULL;
216                                    }*/
217                                 /* allow user to interrupt sending */
218                                 if (!thread && (rpp_sci_read_nb(1, &input) == SUCCESS)) break;
219                                 strlen = sprintf(strbuf,"%d\r\n",index);
220                                 if (!udp) {
221                                         if ((err = netconn_write(newconn, strbuf, strlen, NETCONN_COPY)) != ERR_OK) break;
222                                 }
223                                 else
224                                 {
225                                         payload = netbuf_alloc(localData,strlen);
226                                         if (payload != NULL) {
227                                                 netbuf_take(localData,strbuf,strlen);
228                                                 netbuf_len(localData) = strlen;
229                                                 if ((err = netconn_send(newconn, localData)) != ERR_OK) break;
230                                         }
231                                         else
232                                         {
233                                                 err = ERR_MEM;
234                                                 break;
235                                         }
236                                 }
237                         }
238                         break;
239                 }
240
241                 if (netLoop) { /* -m option */
242                         err = ERR_OK;
243                         /* send some data to opposite node so it knows its peer (this node) ; ATENTION if opposite node was not running in time this is send, then it wont get known this node's connection info */
244                         if (udp && !listen) {
245                                 payload = netbuf_alloc(localData,1);
246                                 if (payload != NULL) {
247                                         input = 0;
248                                         netbuf_take(localData,&input,1);
249                                         netbuf_len(localData) = 1;
250                                         if ((err = netconn_send(newconn, localData)) != ERR_OK) break;
251                                 }
252                                 else
253                                 {
254                                         err = ERR_MEM;
255                                         break;
256                                 }
257                         }
258                         while (!closeths) {
259                                 if (!thread && (rpp_sci_read_nb(1, &input) == SUCCESS)) break;  /* allow user to interrupt operation */
260                                 if ( remoteData != NULL ||  (err = netconn_recv( newconn, &remoteData )) != ERR_TIMEOUT ) {
261                                         if (err == ERR_OK) {
262                                                 p = remoteData->p;
263                                                 /**/
264                                                 while (p->ref > 1) /* if (p->ref > 1) p->ref = 1; */
265                                                         pbuf_free(p);
266                                                 /**/
267                                                 while (p != NULL) {
268                                                         if (!udp)
269                                                                 netconn_write(newconn, p->payload, p->len, NETCONN_COPY);  /*NETCONN_DONTBLOCK*/
270                                                         else
271                                                         {
272                                                                 payload = netbuf_alloc(localData,p->len);
273                                                                 if (payload != NULL) {
274                                                                         netbuf_take(localData,p->payload,p->len);
275                                                                         netbuf_len(localData) = p->len;
276                                                                         if ((err = netconn_send(newconn, localData)) != ERR_OK) break;
277                                                                 }
278                                                                 else
279                                                                 {
280                                                                         err = ERR_MEM;
281                                                                         break;
282                                                                 }
283                                                         }
284                                                         p = p->next;
285                                                 }
286                                                 netbuf_delete(remoteData);
287                                                 remoteData = NULL;
288                                         }
289                                         else
290                                                 break;
291                                 }
292                         }
293                         if (err == ERR_TIMEOUT) err = ERR_OK;  /* timeout is not error here, this occurs on interrupt of while cycle */
294                         break;
295                 }
296
297                 /* only active user can control this section XXX: maybe should move to init startup condition handler */
298                 if (thread) break;
299
300                 /* INTERACTIVE PART */
301
302                 /* receive remote data and print them to sci, receive sci data and write them to netconn */
303                 while (!ncStop) {
304                         if (rpp_sci_read_nb(1, &input) != SUCCESS) {
305                                 if ( remoteData != NULL || (err = netconn_recv( newconn, &remoteData )) != ERR_TIMEOUT) {
306                                         if (err == ERR_OK) {
307                                                 /* print remote data to SCI */
308                                                 p = remoteData->p;
309                                                 while (p != NULL) {
310                                                         index = 0;
311                                                         payload = p->payload;
312                                                         while (index < p->len) {
313                                                                 rpp_sci_printf("%c",payload[index]);
314                                                                 index++;
315                                                         }
316                                                         p = p->next;
317                                                 }
318                                                 netbuf_delete(remoteData);
319                                                 remoteData = NULL;
320                                         }
321                                         else
322                                                 break;  /* receive error - e.g. newconn was closed */
323                                 }
324                                 if (err == ERR_TIMEOUT) err = ERR_OK;
325                                 continue;
326                         }
327
328                         // Backspace and Delete
329                         if (input == 8 || input == 127) {
330                                 if (buff_index > 0) {
331                                         buff_index--;
332                                         rpp_sci_putc('\b');
333                                         rpp_sci_putc(' ' );
334                                         rpp_sci_putc('\b');
335                                 }
336
337                                 // Line feed or Carriage return
338                         }
339                         else if (input == 10 || input == 13) {
340                                 in_buffer[buff_index] = 13;
341                                 buff_index++;
342                                 in_buffer[buff_index] = 10;
343                                 buff_index++;
344                                 flush = TRUE;
345                                 rpp_sci_putc('\r');
346                                 rpp_sci_putc('\n');
347
348                                 // If is any printable character
349                         }
350                         else if (isprint(input) || input == 9) {
351
352                                 // Store character and increment buffer index
353                                 in_buffer[buff_index] = input;
354                                 buff_index++;
355                                 rpp_sci_putc(input);
356
357                                 // Check if buffer is full and force flush
358                                 if (buff_index == BUF_SIZE - 3)
359                                         flush = TRUE;
360                                 // All other character stops nc
361                         }
362                         else {
363                                 ncStop = TRUE;
364                                 flush = TRUE;
365                         }
366
367                         if (flush) {
368                                 if (!udp)
369                                         netconn_write(newconn, in_buffer, buff_index, NETCONN_COPY);
370                                 else
371                                 {
372                                         payload = netbuf_alloc(localData,buff_index);
373                                         if (payload != NULL) {
374                                                 netbuf_take(localData,in_buffer,buff_index);
375                                                 netbuf_len(localData) = buff_index;
376                                                 if ((err = netconn_send(newconn, localData)) != ERR_OK) break;
377                                         }
378                                         else
379                                         {
380                                                 err = ERR_MEM;
381                                                 break;
382                                         }
383                                 }
384                                 // Reset variables
385                                 buff_index = 0;
386                                 flush = FALSE;
387                         }
388                 }
389                 break;
390         }
391 #ifdef DEBUG
392         if (thread) rpp_sci_printf("NC: THREAD NO.%d FINISHED\r\n", thread_inst);
393 #endif
394         if (udp && localData != NULL) netbuf_delete(localData);
395         if (remoteData != NULL) netbuf_delete(remoteData);
396         if (newconn != NULL && !udp) netconn_close(newconn);
397         netconn_delete(newconn);
398         tasks_running--;
399         if (!thread)
400                 nc_arg->err = err;  /* only user controlled task is in front */
401         if (thread) vTaskDelete(NULL);
402 }
403 #else /* if !NO_SYS */
404 boolean_t connected;
405 boolean_t sent;
406 boolean_t ncStop;
407
408 err_t nc_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
409 {
410         uint16_t index;
411         uint8_t *payload;
412
413         if (err == ERR_ABRT) {
414                 rpp_sci_printk("recv abrt\n");
415                 return ERR_ABRT;
416         }
417         /* print remote data to SCI */
418         while (p != NULL) {
419                 index = 0;
420                 payload = p->payload;
421                 while (index < p->len) {
422                         rpp_sci_printk("%c",payload[index]);
423                         index++;
424                 }
425                 p = p->next;
426         }
427         return ERR_OK;
428 }
429
430 void nc_udp_recv_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
431 {
432         uint16_t index;
433         uint8_t *payload;
434
435         /* print remote data to SCI */
436         while (p != NULL) {
437                 index = 0;
438                 payload = p->payload;
439                 while (index < p->len) {
440                         rpp_sci_printk("%c",payload[index]);
441                         index++;
442                 }
443                 p = p->next;
444         }
445         //pbuf_free(p);
446 }
447
448 void nc_udp_listen_recv_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
449 {
450         connected = TRUE;
451         err_t err = udp_connect(pcb, addr, port);
452         LWIP_ASSERT(("nc_init: udp_listen_connect failed"), err == ERR_OK);
453         udp_recv(pcb, nc_udp_recv_callback, NULL);
454         nc_udp_recv_callback(arg, pcb, p, addr, port);
455 }
456
457 void nc_err_callback(void *arg, err_t err)
458 {
459         rpp_sci_printk("err clbck\n");
460         LWIP_DEBUGF(LWIP_DBG_ON, ("nc_err: %s", lwip_strerr(err)));
461
462         ncStop = TRUE;
463 }
464
465 err_t nc_sent_callback(void *arg, struct tcp_pcb *pcb, u16_t len)
466 {
467         sent = TRUE;
468         return ERR_OK;
469 }
470
471 err_t nc_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
472 {
473         struct tcp_pcb **newone = arg;
474
475         *newone = newpcb; /* pass accepted connection to main app loop */
476
477         connected = TRUE;
478
479         /* Set up the various callback functions */
480         tcp_recv(newpcb, nc_recv_callback);
481         tcp_err(newpcb, nc_err_callback);
482         tcp_sent(newpcb, nc_sent_callback);
483
484         return ERR_OK;
485 }
486
487 err_t nc_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err)
488 {
489
490         /* Set up the various callback functions */
491         tcp_recv(tpcb, nc_recv_callback);
492         tcp_err(tpcb, nc_err_callback);
493         tcp_sent(tpcb, nc_sent_callback);
494
495         connected = TRUE;
496         rpp_sci_printk("connected\n");
497         return ERR_OK;
498 }
499
500 err_t nc_tmirror_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
501 {
502         if (err == ERR_ABRT) {
503                 rpp_sci_printk("recv abrt\n");
504                 return ERR_ABRT;
505         }
506         while (p != NULL) {
507                 if (tcp_write(tpcb, p->payload, p->len, TCP_WRITE_FLAG_COPY) != ERR_OK) {
508                         rpp_sci_printk("error writing to newpcb - tcp_write\n");
509                         return -1;
510                 }
511                 if (tcp_output(tpcb) != ERR_OK) { /* when we want to send data immediately */
512                         rpp_sci_printk("newpcb output err - tcp\n");
513                         return -1;
514                 }
515                 p = p->next;
516         }
517         return ERR_OK;
518 }
519
520 void nc_umirror_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
521 {
522         struct pbuf *pbuf;
523
524         while (p != NULL) {
525                 pbuf = pbuf_alloc(PBUF_TRANSPORT, p->len, PBUF_RAM);
526                 if (pbuf == NULL) {
527                         rpp_sci_printk("error allocating pbuf - udp_send\n");
528                         //p = p->next;
529                         continue;
530                 }
531                 if (pbuf_take(pbuf, p->payload, p->len) != ERR_OK) {
532                         rpp_sci_printk("pbuf mem err (small pbuf) - udp_send\n");
533                         continue;
534                 }
535                 if (udp_send(pcb, pbuf) != ERR_OK) {
536                         rpp_sci_printk("error sending to pcb - udp_send\n");
537                         continue;
538                 }
539                 p = p->next;
540         }
541 }
542
543
544 #endif /* !NO_SYS */
545
546 /*
547  * this function has two operational modes, depending on LwIP settings
548  * NO_SYS == 1 - this is for testing raw API, there is only one task working with IP stack allowed when using this mode
549  * NO_SYS == 0 - this is for testing netconn API, you can let more tasks to work with IP stack
550  */
551 int cmd_do_init_nc(cmd_io_t *cmd_io, const struct cmd_des *des, char *param[])
552 {
553         if (!isPostInitialized()) {
554                 rpp_sci_printf("Eth not initialized run 'ethinit' command first.\n");
555                 return FAILURE;
556         }
557         err_t err = ERR_OK;
558         uint16_t portNO, srcPortNO = 0;
559         ip_addr_t remoteIP;
560         boolean_t help = FALSE, udp = FALSE, listen = FALSE, netLoop = FALSE, test = FALSE, srcPortSpec = FALSE;
561         uint8_t pindex;
562
563         buff_index = 0;
564         /* initialize arguments and thread name */
565 #if !NO_SYS
566         char name[5] = "nc";
567         name[4] = '\0';
568         struct nc_arg nc_arg; /* create space for handing args to nc_run() */
569         nc_arg.thread = FALSE;
570 #else
571         uint8_t input = 0;
572         boolean_t flush = FALSE;
573         uint32_t index;
574         struct netif *netif = rpp_eth_get_netif(INTERFACE_INSTANCE_NUMBER);
575         struct tcp_pcb *tpcb, *tnewpcb;
576         struct udp_pcb *pcb;
577         struct pbuf *p; /* udp send */
578 #endif
579
580         /* examine parameters */
581         for (pindex = 1; param[pindex] != 0; pindex++) {
582                 if        (strncmp((char *)param[pindex], "-h", 3) == 0) {
583                         help = TRUE;
584                         break;
585                 }
586                 else if (strncmp((char *)param[pindex], "-u", 3) == 0) {
587                         udp = TRUE;
588 #if !NO_SYS
589                 }
590                 else if (strncmp((char *)param[pindex], "-t", 3) == 0)
591                         nc_arg.thread = TRUE;
592                 else if (strncmp((char *)param[pindex], "-c", 3) == 0) {
593                         closeths = TRUE;
594                         return ERR_OK;
595 #endif
596                 }
597                 else if (strncmp((char *)param[pindex], "-l", 3) == 0 && !srcPortSpec) {
598                         listen = TRUE;
599                         srcPortNO = rpp_eth_portStrToInt((uint8_t *)param[++pindex]);
600                         if (srcPortNO == 0) {
601                                 rpp_sci_printf("E wrong portNO, portNO must follow immediately after -l option\r\n");
602                                 return BAD_PORT_NO;
603                         }
604                         srcPortSpec = TRUE;
605                 }
606                 else if (strncmp((char *)param[pindex], "-p", 3) == 0 && !srcPortSpec) {
607                         srcPortNO = rpp_eth_portStrToInt((uint8_t *)param[++pindex]);
608                         if (srcPortNO == 0) {
609                                 rpp_sci_printf("E wrong portNO, portNO must follow immediately after -p option\r\n");
610                                 return BAD_PORT_NO;
611                         }
612                         srcPortSpec = TRUE;
613                 }
614                 else if (strncmp((char *)param[pindex], "-m", 3) == 0)
615                         netLoop = TRUE;
616                 else if (strncmp((char *)param[pindex], "-d", 3) == 0)
617                         test = TRUE;
618                 else if ( (err = rpp_eth_stringToIP(&remoteIP, (uint8_t *)param[pindex])) == SUCCESS ) {
619                         portNO = rpp_eth_portStrToInt((uint8_t *)param[++pindex]);
620                         if (portNO == 0) {
621                                 rpp_sci_printf("E wrong portNO, portNO must follow immediately after IP address\r\n");
622                                 return BAD_PORT_NO;
623                         }
624                 }
625                 else {
626                         rpp_sci_printf("ERR: check option name, option combination, IP address format\r\n");
627                         help = TRUE;
628                         err = BAD_OPTION;
629                         break;
630                 }
631         }
632
633         /* TODO: additional testing of parameters mutual exclusion */
634         /* valid combinations:
635            nc <addr> <port> [-p <port>] [-u] [-t]
636            nc -l <port> [-u] [-t]
637            nc <addr> <port> -m [-p <port>] [-u] [-t]
638            nc <addr> <port> -d [-p <port>] [-u] [-t]
639            nc -l <port> -m [-u] [-t]
640            nc -l <port> -d [-u] [-t]
641          */
642
643         /* usage of command */
644         if (help || !param[1]) {
645                 rpp_sci_printf("-u udp, -t thread, -c close tasks, -l listen, -p source port, -m ping/mirror, -d test\r\n\n");
646                 rpp_sci_printf("usage:\tnc -l <port> [-u] [-m [-t] | -d [-t]]\r\n\t\tlistens on port for connection\r\n");
647                 rpp_sci_printf("\tnc <address> <port> [-p <port>] [-u] [-m [-t] | -d [-t]]\r\n\t\tconnects to the address on port NO.\r\n");
648                 rpp_sci_printf("\tnc -c\r\n\t\tdeletes all running threads.\r\n");
649                 rpp_sci_printf("opts:\t-t run as stand alone task\r\n\t-c close all stand alone tasks\r\n");
650                 rpp_sci_printf("\t-l <port> listen on given port for incomming connection\r\n\t-p <port> use as local port when connecting to remote node\r\n\t");
651                 rpp_sci_printf("\t-m send all incomming data back to sender\r\n\t-d sends many testing packets containing packet number in ASCII as data\r\n");
652                 rpp_sci_printf("Note: when using udp -u and listening -l options, first node data was received from is considered as peer for communication.\r\n");
653                 return err;
654         }
655
656         /* determine srcPortNO from predefined value if not done yet (from argument given) */
657         if (!srcPortSpec)
658                 //udp_new_port();
659                 srcPortNO = srcPort++;
660
661 #if !NO_SYS /* netconn api */
662
663         /* prepare args */
664         nc_arg.srcPortNO = srcPortNO;
665         nc_arg.portNO = portNO;
666         nc_arg.remoteIP.addr = remoteIP.addr;
667         nc_arg.udp = udp;
668         nc_arg.listen = listen;
669         nc_arg.netLoop = netLoop;
670         nc_arg.test = test;
671         nc_arg.srcPortSpec = srcPortSpec;
672
673         if (nc_arg.thread) {
674                 /* prepare semaphore */
675                 vSemaphoreCreateBinary(nc_arg.args_coppied);
676                 xSemaphoreTake(nc_arg.args_coppied, 0);
677                 /* add number to task name */
678                 taskNameNum = (taskNameNum%100);
679                 name[2] = (taskNameNum/10) + '0';
680                 name[3] = (taskNameNum%10) + '0';
681                 rpp_sci_printf("STARTING THREAD: %s\r\n", name);
682                 taskNameNum++;
683                 /* start thread */
684                 if ((err = xTaskCreate(&run_nc, name, ncTaskStackSize, &nc_arg, ncTaskPriority, NULL)) == pdPASS) {
685                         /* block on semaphore, till new thread copies arg to own stack */
686                         xSemaphoreTake( nc_arg.args_coppied, portMAX_DELAY );
687                         vSemaphoreDelete(nc_arg.args_coppied);
688                 }
689                 else {
690                         vSemaphoreDelete(nc_arg.args_coppied);
691                         return err;
692                 }
693         }
694         else
695         {
696                 taskNameNum++;
697                 run_nc(&nc_arg);
698                 return nc_arg.err;
699         }
700 #else /* raw LwIP API */
701         ncStop = FALSE;
702         connected = FALSE;
703         sent = TRUE;
704         if (udp) {
705                 pcb = udp_new();
706                 LWIP_ASSERT(("nc_init: udp_new failed"), pcb != NULL);
707                 if (pcb == NULL) return -1;
708                 err = udp_bind(pcb, &(netif->ip_addr), srcPortNO);
709                 CC_ASSERT(("nc_init: udp_bind failed: %s", lwip_strerr(err)), err == ERR_OK);
710                 if (err != ERR_OK) return -1;
711         }
712         else
713         {
714                 tpcb = tcp_new();
715                 LWIP_ASSERT(("nc_init: tcp_new failed"), tpcb != NULL);
716                 if (tpcb == NULL) return -1;
717                 err = tcp_bind(tpcb, &(netif->ip_addr), srcPortNO);
718                 CC_ASSERT(("nc_init: tcp_bind failed: %s", lwip_strerr(err)), err == ERR_OK);
719                 if (err != ERR_OK) return -1;
720         }
721
722         if (!udp) {
723                 if (listen) {
724                         tpcb = tcp_listen(tpcb);
725                         LWIP_ASSERT(("nc_init: tcp_listen failed"), tpcb != NULL);
726                         /* initialize callback arg and accept callback */
727                         tcp_arg(tpcb, &tnewpcb);
728                         tcp_accept(tpcb, nc_accept_callback);
729                 }
730                 else
731                 {
732                         tnewpcb = tpcb;
733                         tpcb = NULL;
734                         err = tcp_connect(tnewpcb, &remoteIP, portNO, nc_connected_callback);
735                         LWIP_ASSERT(("nc_init: tcp_connect failed"), err == ERR_OK);
736                 }
737         }
738         else
739         {
740                 if (listen)
741                         udp_recv(pcb, nc_udp_listen_recv_callback, NULL);
742                 else
743                 {
744                         err = udp_connect(pcb, &remoteIP, portNO);
745                         LWIP_ASSERT(("nc_init: udp_connect failed"), err == ERR_OK);
746                         //connected = TRUE;
747                         /* set udp recv callback */
748                         udp_recv(pcb, nc_udp_recv_callback, NULL);
749                 }
750         }
751
752         /* wait for connection to be established */
753         if (listen || !udp) {
754                 while (!connected) {
755                         if (rpp_sci_read_nb(1, &input) == SUCCESS) {
756                                 ncStop = TRUE;
757                                 break;
758                         }
759                         sys_check_timeouts();
760                         vTaskDelay(10); /* just because we have no other business here */
761                 }
762                 if (!udp && listen && tcp_close(tpcb) != ERR_OK) rpp_sci_printk("closing listening tcp pcb err\n");  /* close listening pcb */
763         }
764
765         /* mode handlers in loop, so we can jump out of it on error or finish and clean after us */
766         do {
767                 /*************************************** 1 mirror mode *****************************************/
768
769                 if (netLoop) { /* -m option */
770                         err = ERR_OK;
771                         /* send some data to opposite node so it knows its peer (this node) ; ATENTION if opposite node was not running in time this is send, then it wont get known this node's connection info */
772 /*          if(udp && !listen){
773                 payload = netbuf_alloc(localData,1);
774                 if(payload != NULL){
775                     input = 0;
776                     netbuf_take(localData,&input,1);
777                     netbuf_len(localData) = 1;
778                     if((err = netconn_send(newconn, localData)) != ERR_OK)break;
779                 }
780                 else
781                 {
782                     err = ERR_MEM;
783                     break;
784                 }
785             }*/
786                         /* register callback function which sends all the recevd data back to remote host */
787                         if (!udp)
788                                 tcp_recv(tnewpcb, nc_tmirror_callback);
789                         else
790                                 udp_recv(pcb, nc_umirror_callback, NULL);
791
792                         while (rpp_sci_read_nb(1, &input) != SUCCESS) {
793                                 sys_check_timeouts();
794                                 vTaskDelay(10);
795                         }
796                         break;
797                 }
798
799                 /*************************************** 2 testing mode *****************************************/
800
801                 if (test) { /* -d option; sends packets to the opposite node and doesn't receive anything */
802                         index = 0;
803                         char strbuf[13];
804                         int strlen;
805                         //netconn_set_recvtimeout(newconn, 1);
806                         while (index++ < 4294967295) {
807                                 /* allow user to interrupt sending */
808                                 if (rpp_sci_read_nb(1, &input) == SUCCESS) break;
809                                 strlen = sprintf(strbuf,"%d\r\n",index);
810                                 if (!udp) {
811                                         sys_check_timeouts();
812                                         if (tcp_write(tnewpcb, strbuf, strlen, TCP_WRITE_FLAG_COPY) != ERR_OK) {
813                                                 rpp_sci_printf("error writing to newpcb - tcp_write testing\n");
814                                                 break;
815                                         }
816                                         if (tcp_output(tnewpcb) != ERR_OK) { /* when we want to send data immediately */
817                                                 rpp_sci_printf("newpcb output err - tcp testing\n");
818                                                 break;
819                                         }
820                                 }
821                                 else
822                                 {
823                                         p = pbuf_alloc(PBUF_TRANSPORT, strlen, PBUF_RAM);
824                                         if (p == NULL) {
825                                                 rpp_sci_printf("error allocating pbuf - udp_send testing\n");
826                                                 continue;
827                                         }
828                                         if (pbuf_take(p, strbuf, strlen) != ERR_OK) {
829                                                 rpp_sci_printf("pbuf mem err (small pbuf) - udp_send testing\n");
830                                                 continue;
831                                         }
832                                         if (udp_send(pcb, p) != ERR_OK) {
833                                                 rpp_sci_printf("error sending to pcb - udp_send testing\n");
834                                                 continue;
835                                         }
836                                 }
837                         }
838                         break;
839                 }
840
841                 /*************************************** 3 interactive mode *****************************************/
842
843                 while (!ncStop) {
844                         if (!udp && tnewpcb->state != ESTABLISHED) {
845                                 ncStop = TRUE;
846                                 break;
847                         }
848                         if (rpp_sci_read_nb(1, &input) != SUCCESS) {
849                                 /* updating lwip timers here */
850                                 sys_check_timeouts();
851                                 continue;
852                         }
853
854                         // Backspace and Delete
855                         if (input == 8 || input == 127) {
856                                 if (buff_index > 0) {
857                                         buff_index--;
858                                         rpp_sci_putc('\b');
859                                         rpp_sci_putc(' ' );
860                                         rpp_sci_putc('\b');
861                                 }
862
863                                 // Line feed or Carriage return
864                         }
865                         else if (input == 10 || input == 13) {
866                                 in_buffer[buff_index] = 13;
867                                 buff_index++;
868                                 in_buffer[buff_index] = 10;
869                                 buff_index++;
870                                 flush = TRUE;
871                                 rpp_sci_putc('\r');
872                                 rpp_sci_putc('\n');
873
874                                 // If is any printable character
875                         }
876                         else if (isprint(input) || input == 9) {
877
878                                 // Store character and increment buffer index
879                                 in_buffer[buff_index] = input;
880                                 buff_index++;
881                                 rpp_sci_putc(input);
882
883                                 // Check if buffer is full and force flush
884                                 if (buff_index == BUF_SIZE - 3)
885                                         flush = TRUE;
886                                 // All other character stops nc
887                         }
888                         else {
889                                 ncStop = TRUE;
890                                 flush = TRUE;
891                         }
892
893                         if (flush) {
894                                 while (!udp && !ncStop && !sent) {
895                                         sys_check_timeouts();
896                                         vTaskDelay(10);
897                                 }
898                                 if (ncStop) break;
899                                 if (!udp) {
900                                         if (tcp_write(tnewpcb, in_buffer, buff_index, TCP_WRITE_FLAG_COPY) != ERR_OK) {
901                                                 rpp_sci_printf("error writing to newpcb - tcp_write\n");
902                                                 continue;
903                                         }
904                                         if (tcp_output(tnewpcb) != ERR_OK) { /* when we want to send data immediately */
905                                                 rpp_sci_printf("newpcb output err - tcp\n");
906                                                 continue;
907                                         }
908                                 }
909                                 else
910                                 {
911                                         p = pbuf_alloc(PBUF_TRANSPORT, buff_index, PBUF_RAM);
912                                         if (p == NULL) {
913                                                 rpp_sci_printf("error allocating pbuf - udp_send\n");
914                                                 continue;
915                                         }
916                                         if (pbuf_take(p, in_buffer, buff_index) != ERR_OK) {
917                                                 rpp_sci_printf("pbuf mem err (small pbuf) - udp_send\n");
918                                                 continue;
919                                         }
920                                         if (udp_send(pcb, p) != ERR_OK) {
921                                                 rpp_sci_printf("error sending to pcb - udp_send\n");
922                                                 continue;
923                                         }
924                                 }
925                                 // Reset variables
926                                 sent = FALSE;
927                                 buff_index = 0;
928                                 flush = FALSE;
929                         }
930                 }
931
932         } while (0);
933
934 /* close connection and clean */
935         if (!udp) {
936                 tcp_recv(tnewpcb, NULL);
937                 err = tcp_close(tnewpcb);
938                 if (err != ERR_OK) {
939                         rpp_sci_printf("newpcb closing error\n");
940                         /* closing failed, try again later */
941                         LWIP_DEBUGF(LWIP_DBG_ON, ("Error %s closing pcb=0x%08X\n", lwip_strerr(err), pcb));
942                         tcp_recv(tnewpcb, nc_recv_callback);
943                 }
944                 else {
945                         /* closing succeeded */
946                         tcp_arg(tnewpcb, NULL);
947                         tcp_sent(tnewpcb, NULL);
948                 }
949         }
950         else
951         {
952                 udp_disconnect(pcb);
953                 udp_remove(pcb);
954         }
955         buff_index = 0;
956 #endif /* if !NO_SYS */
957         return ERR_OK;
958 }
959
960 #endif  /* DOCGEN */
961
962 cmd_des_t const cmd_des_nc = {
963         0,CDESM_SPACE_SEP,
964         "ethnc","Start very simple netcat",
965
966         "### Command syntax ###\n"
967         "\n"
968         "     ethnc <IP> <PORT> [-p <PORT>] [-u] [-m [-t] | -d [-t]] [-c]\n"
969         "     ethnc -l <PORT> [-u] [-m [-t] | -d [-t]] [-c]\n"
970         "\n"
971         "### Description ###\n"
972         "\n"
973         "Netcat is a program which allows to communicate using TCP or UDP\n"
974         "protocols. First a connection is established by either:\n"
975         "\n"
976         "- connecting to a specified IP address and PORT (without option -l) or by\n"
977         "- listening for a new connection on a given PORT (with option -l).\n"
978         "\n"
979         "When no -u option is specified ethnc command works with TCP\n"
980         "connections. With -u option UDP communication is used. Listening for\n"
981         "connection on UDP means waiting for reception of any UDP datagram.\n"
982         "\n"
983         "Once the connection is established the command works in one of the\n"
984         "following modes:\n"
985         "\n"
986         "- interactive mode in which received data are forwarded to serial line and data received on serial line are sent to the connection when either new line is encountered or when internal buffer is full,\n"
987         "- sending of testing data (increasing ASCII formatted numbers) (option -d),\n"
988         "- looping of incoming data back to the connection's peer (option -m).\n"
989         "\n"
990         "Note: When trying to use a same local TCP port number multiple times\n"
991         "in a row (-l or -p options) there might be several minutes delay\n"
992         "before the port is available after closing the previous connection.\n"
993         "This situation is singled with ERROR 31.\n"
994         "\n"
995         "Other options:\n"
996         "\n"
997         "- -p specifies local port for outgoing connections.\n"
998         "- -u use UDP protocol instead of the default TCP.\n"
999         "- -t send and/or receive data in a background thread (works only with -d or -m options).\n"
1000         "- -c stop all running background tasks\n"
1001         "\n"
1002         "### Examples ###\n"
1003         "\n"
1004         "Listen for incoming TCP connection on local port 2000:\n"
1005         "     --> ethnc -l 2000\n"
1006         "\n"
1007         "Connect using TCP to address 192.168.247.15 to remote port 80 using local port 1500:\n"
1008         "     --> ethnc 192.168.247.15 80 -p 1500\n"
1009         "\n"
1010         "Send testing data to the remote node:\n"
1011         "     --> ethnc -d 192.168.247.2 1025\n"
1012         "\n"
1013         "Loop back all data coming from remote node's UDP port 1025:\n"
1014         "     --> ethnc -m -u 192.168.247.2 1025\n"
1015         "\n"
1016         "Wait for a TCP connection on local port 30000 and loop all incoming data\n"
1017         "back:\n"
1018         "     --> ethnc -l 30000 -m\n",
1019         CMD_HANDLER(cmd_do_init_nc), (void *)&cmd_list_nc
1020 };
1021
1022 /** List of commands for lwip, defined as external */
1023 cmd_des_t const *cmd_list_nc[] = {
1024         &cmd_des_nc,
1025         NULL
1026 };