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