From 9b442bfd118bbf5d49a032cc82beea4b0b3830e6 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Sat, 3 Sep 2011 13:29:33 -0700 Subject: [PATCH] add a free netio server --- kernel/netio.c | 349 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 328 insertions(+), 21 deletions(-) diff --git a/kernel/netio.c b/kernel/netio.c index 166bbc18..96ee53aa 100644 --- a/kernel/netio.c +++ b/kernel/netio.c @@ -1,50 +1,356 @@ -/* - * this example is derived from the netio example of the contrib-1.4.0 +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. * - * see http://download.savannah.gnu.org/releases/lwip/ */ - #include -#include #if defined(CONFIG_LWIP) #include + +#if LWIP_TCP #include +/* + * This implements a netio server. + * The client sends a command word (4 bytes) then a data length word (4 bytes). + * If the command is "receive", the server is to consume "data length" bytes into + * a circular buffer until the first byte is non-zero, then it is to consume + * another command/data pair. + * If the command is "send", the server is to send "data length" bytes from a circular + * buffer with the first byte being zero, until "some time" (6 seconds in the + * current netio126.zip download) has passed and then send one final buffer with + * the first byte being non-zero. Then it is to consume another command/data pair. + */ + /* See http://www.nwlab.net/art/netio/netio.html to get the netio tool */ -#if LWIP_TCP -static err_t netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +/* implementation options */ +#define NETIO_BUF_SIZE (4 * 1024) +#define NETIO_USE_STATIC_BUF 0 + +/* NetIO server state definition */ +#define NETIO_STATE_WAIT_FOR_CMD 0 +#define NETIO_STATE_RECV_DATA 1 +#define NETIO_STATE_SEND_DATA 2 +#define NETIO_STATE_SEND_DATA_LAST 3 +#define NETIO_STATE_DONE 4 + +struct netio_state { + u32_t state; + u32_t cmd; + u32_t data_len; + u32_t cntr; + u8_t * buf_ptr; + u32_t buf_pos; + u32_t first_byte; + u32_t time_stamp; +}; + +/* NetIO command protocol definition */ +#define NETIO_CMD_QUIT 0 +#define NETIO_CMD_C2S 1 +#define NETIO_CMD_S2C 2 +#define NETIO_CMD_RES 3 + +static err_t netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); + +static void +netio_close(void *arg, struct tcp_pcb *pcb) { + err_t err; - LWIP_UNUSED_ARG(arg); + struct netio_state *ns = arg; + ns->state = NETIO_STATE_DONE; + tcp_recv(pcb, NULL); + err = tcp_close(pcb); - if (err == ERR_OK && p != NULL) { - tcp_recved(pcb, p->tot_len); - pbuf_free(p); + if (err != ERR_OK) { + /* closing failed, try again later */ + tcp_recv(pcb, netio_recv); } else { - pbuf_free(p); + /* closing succeeded */ +#if NETIO_USE_STATIC_BUF != 1 + if(ns->buf_ptr != NULL){ + mem_free(ns->buf_ptr); + } +#endif + tcp_arg(pcb, NULL); + tcp_poll(pcb, NULL, 0); + tcp_sent(pcb, NULL); + if (arg != NULL) { + mem_free(arg); + } + } +} + +static err_t +netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct netio_state *ns = arg; + u8_t * data_ptr; + u32_t data_cntr; + struct pbuf *q = p; + u16_t len; + + if (p != NULL) { + tcp_recved(pcb, p->tot_len); } - if (err == ERR_OK && p == NULL) { - tcp_arg(pcb, NULL); - tcp_sent(pcb, NULL); - tcp_recv(pcb, NULL); - tcp_close(pcb); + if (err == ERR_OK && q != NULL) { + + while (q != NULL) { + data_cntr = q->len; + data_ptr = q->payload; + while (data_cntr--) { + if (ns->state == NETIO_STATE_DONE){ + netio_close(ns, pcb); + break; + } else if (ns->state == NETIO_STATE_WAIT_FOR_CMD) { + if (ns->cntr < 4) { + /* build up the CMD field */ + ns->cmd <<= 8; + ns->cmd |= *data_ptr++; + ns->cntr++; + } else if (ns->cntr < 8) { + /* build up the DATA field */ + ns->data_len <<= 8; + ns->data_len |= *data_ptr++; + ns->cntr++; + + if (ns->cntr == 8) { + /* now we have full command and data words */ + ns->cntr = 0; + ns->buf_pos = 0; + ns->buf_ptr[0] = 0; + if (ns->cmd == NETIO_CMD_C2S) { + ns->state = NETIO_STATE_RECV_DATA; + } else if (ns->cmd == NETIO_CMD_S2C) { + ns->state = NETIO_STATE_SEND_DATA; + /* start timer */ + ns->time_stamp = get_clock_tick(); + /* send first round of data */ + + len = tcp_sndbuf(pcb); + len = LWIP_MIN(len, ns->data_len - ns->cntr); + len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos); + + do { + err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY); + if (err == ERR_MEM) { + len /= 2; + } + } while ((err == ERR_MEM) && (len > 1)); + + ns->buf_pos += len; + ns->cntr += len; + + } else { + /* unrecognized command, punt */ + ns->cntr = 0; + ns->buf_pos = 0; + ns->buf_ptr[0] = 0; + netio_close(ns, pcb); + break; + } + } + } else { + /* in trouble... shouldn't be in this state! */ + kputs("netio is in trouble\n"); + } + + } else if (ns->state == NETIO_STATE_RECV_DATA) { + + if(ns->cntr == 0){ + /* save the first byte of this new round of data + * this will not match ns->buf_ptr[0] in the case that + * NETIO_BUF_SIZE is less than ns->data_len. + */ + ns->first_byte = *data_ptr; + } + + ns->buf_ptr[ns->buf_pos++] = *data_ptr++; + ns->cntr++; + + if (ns->buf_pos == NETIO_BUF_SIZE) { + /* circularize the buffer */ + ns->buf_pos = 0; + } + + if(ns->cntr == ns->data_len){ + ns->cntr = 0; + if (ns->first_byte != 0) { + /* if this last round did not start with 0, + * go look for another command */ + ns->state = NETIO_STATE_WAIT_FOR_CMD; + ns->data_len = 0; + ns->cmd = 0; + /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */ + } else { + /* stay here and wait on more data */ + } + } + + } else if (ns->state == NETIO_STATE_SEND_DATA + || ns->state == NETIO_STATE_SEND_DATA_LAST) { + /* I don't think this should happen... */ + kputs("I don't think this should happen...\n"); + } else { + /* done / quit */ + netio_close(ns, pcb); + break; + } /* end of ns->state condition */ + } /* end of while data still in this pbuf */ + + q = q->next; + } + + pbuf_free(p); + + } else { + + /* error or closed by other side */ + if (p != NULL) { + pbuf_free(p); + } + + /* close the connection */ + netio_close(ns, pcb); + + } + return ERR_OK; + +} + +static err_t +netio_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct netio_state *ns = arg; + err_t err = ERR_OK; + + if (ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA) { + /* done with this round of sending */ + ns->buf_pos = 0; + ns->cntr = 0; + + /* check if timer expired */ + if (get_clock_tick() - ns->time_stamp > 600) { + ns->buf_ptr[0] = 1; + ns->state = NETIO_STATE_SEND_DATA_LAST; + } else { + ns->buf_ptr[0] = 0; + } + } + + if(ns->state == NETIO_STATE_SEND_DATA_LAST || ns->state == NETIO_STATE_SEND_DATA){ + len = tcp_sndbuf(pcb); + len = LWIP_MIN(len, ns->data_len - ns->cntr); + len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos); + + if(ns->cntr < ns->data_len){ + do { + err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY); + if (err == ERR_MEM) { + len /= 2; + } + } while ((err == ERR_MEM) && (len > 1)); + + ns->buf_pos += len; + if(ns->buf_pos >= NETIO_BUF_SIZE){ + ns->buf_pos = 0; + } + + ns->cntr += len; + } + } + + if(ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA_LAST){ + /* we have buffered up all our data to send this last round, go look for a command */ + ns->state = NETIO_STATE_WAIT_FOR_CMD; + ns->cntr = 0; + /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */ } return ERR_OK; } -static err_t netio_accept(void *arg, struct tcp_pcb *pcb, err_t err) +static err_t +netio_poll(void *arg, struct tcp_pcb *pcb) { - LWIP_UNUSED_ARG(arg); + struct netio_state * ns = arg; + + if(ns->state == NETIO_STATE_SEND_DATA) { + + } else if(ns->state == NETIO_STATE_DONE){ + netio_close(ns, pcb); + } + + return ERR_OK; + +} + +#if NETIO_USE_STATIC_BUF == 1 +static u8_t netio_buf[NETIO_BUF_SIZE]; +#endif + +static err_t +netio_accept(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct netio_state * ns; + LWIP_UNUSED_ARG(err); - tcp_arg(pcb, NULL); - tcp_sent(pcb, NULL); + ns = mem_malloc(sizeof(struct netio_state)); + + if(ns == NULL){ + return ERR_MEM; + } + + ns->state = NETIO_STATE_WAIT_FOR_CMD; + ns->data_len = 0; + ns->cmd = 0; + ns->cntr = 0; + ns->buf_pos = 0; +#if NETIO_USE_STATIC_BUF == 1 + ns->buf_ptr = netio_buf; +#else + ns->buf_ptr = mem_malloc(NETIO_BUF_SIZE); + + if(ns->buf_ptr == NULL){ + mem_free(ns); + return ERR_MEM; + } +#endif + + ns->buf_ptr[0] = 0; + + tcp_arg(pcb, ns); + tcp_sent(pcb, netio_sent); tcp_recv(pcb, netio_recv); + tcp_poll(pcb, netio_poll, 4); /* every 2 seconds */ + //tcp_nagle_disable(pcb); return ERR_OK; } @@ -57,6 +363,7 @@ void netio_init(void) pcb = tcp_listen(pcb); tcp_accept(pcb, netio_accept); } + #endif /* LWIP_TCP */ #endif