summaryrefslogtreecommitdiff
path: root/contrib/apps/tcpecho_raw/tcpecho_raw.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/apps/tcpecho_raw/tcpecho_raw.c')
-rw-r--r--contrib/apps/tcpecho_raw/tcpecho_raw.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/contrib/apps/tcpecho_raw/tcpecho_raw.c b/contrib/apps/tcpecho_raw/tcpecho_raw.c
new file mode 100644
index 00000000000..5a71d84b55e
--- /dev/null
+++ b/contrib/apps/tcpecho_raw/tcpecho_raw.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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 and a contribution to the lwIP TCP/IP stack.
+ *
+ * Credits go to Adam Dunkels (and the current maintainers) of this software.
+ *
+ * Christiaan Simons rewrote this file to get a more stable echo example.
+ */
+
+/**
+ * @file
+ * TCP echo server example using raw API.
+ *
+ * Echos all bytes sent by connecting client,
+ * and passively closes when client is done.
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+#include "lwip/tcp.h"
+#include "tcpecho_raw.h"
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+static struct tcp_pcb *tcpecho_raw_pcb;
+
+enum tcpecho_raw_states
+{
+ ES_NONE = 0,
+ ES_ACCEPTED,
+ ES_RECEIVED,
+ ES_CLOSING
+};
+
+struct tcpecho_raw_state
+{
+ u8_t state;
+ u8_t retries;
+ struct tcp_pcb *pcb;
+ /* pbuf (chain) to recycle */
+ struct pbuf *p;
+};
+
+static void
+tcpecho_raw_free(struct tcpecho_raw_state *es)
+{
+ if (es != NULL) {
+ if (es->p) {
+ /* free the buffer chain if present */
+ pbuf_free(es->p);
+ }
+
+ mem_free(es);
+ }
+}
+
+static void
+tcpecho_raw_close(struct tcp_pcb *tpcb, struct tcpecho_raw_state *es)
+{
+ tcp_arg(tpcb, NULL);
+ tcp_sent(tpcb, NULL);
+ tcp_recv(tpcb, NULL);
+ tcp_err(tpcb, NULL);
+ tcp_poll(tpcb, NULL, 0);
+
+ tcpecho_raw_free(es);
+
+ tcp_close(tpcb);
+}
+
+static void
+tcpecho_raw_send(struct tcp_pcb *tpcb, struct tcpecho_raw_state *es)
+{
+ struct pbuf *ptr;
+ err_t wr_err = ERR_OK;
+
+ while ((wr_err == ERR_OK) &&
+ (es->p != NULL) &&
+ (es->p->len <= tcp_sndbuf(tpcb))) {
+ ptr = es->p;
+
+ /* enqueue data for transmission */
+ wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1);
+ if (wr_err == ERR_OK) {
+ u16_t plen;
+
+ plen = ptr->len;
+ /* continue with next pbuf in chain (if any) */
+ es->p = ptr->next;
+ if(es->p != NULL) {
+ /* new reference! */
+ pbuf_ref(es->p);
+ }
+ /* chop first pbuf from chain */
+ pbuf_free(ptr);
+ /* we can read more data now */
+ tcp_recved(tpcb, plen);
+ } else if(wr_err == ERR_MEM) {
+ /* we are low on memory, try later / harder, defer to poll */
+ es->p = ptr;
+ } else {
+ /* other problem ?? */
+ }
+ }
+}
+
+static void
+tcpecho_raw_error(void *arg, err_t err)
+{
+ struct tcpecho_raw_state *es;
+
+ LWIP_UNUSED_ARG(err);
+
+ es = (struct tcpecho_raw_state *)arg;
+
+ tcpecho_raw_free(es);
+}
+
+static err_t
+tcpecho_raw_poll(void *arg, struct tcp_pcb *tpcb)
+{
+ err_t ret_err;
+ struct tcpecho_raw_state *es;
+
+ es = (struct tcpecho_raw_state *)arg;
+ if (es != NULL) {
+ if (es->p != NULL) {
+ /* there is a remaining pbuf (chain) */
+ tcpecho_raw_send(tpcb, es);
+ } else {
+ /* no remaining pbuf (chain) */
+ if(es->state == ES_CLOSING) {
+ tcpecho_raw_close(tpcb, es);
+ }
+ }
+ ret_err = ERR_OK;
+ } else {
+ /* nothing to be done */
+ tcp_abort(tpcb);
+ ret_err = ERR_ABRT;
+ }
+ return ret_err;
+}
+
+static err_t
+tcpecho_raw_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+ struct tcpecho_raw_state *es;
+
+ LWIP_UNUSED_ARG(len);
+
+ es = (struct tcpecho_raw_state *)arg;
+ es->retries = 0;
+
+ if(es->p != NULL) {
+ /* still got pbufs to send */
+ tcp_sent(tpcb, tcpecho_raw_sent);
+ tcpecho_raw_send(tpcb, es);
+ } else {
+ /* no more pbufs to send */
+ if(es->state == ES_CLOSING) {
+ tcpecho_raw_close(tpcb, es);
+ }
+ }
+ return ERR_OK;
+}
+
+static err_t
+tcpecho_raw_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
+{
+ struct tcpecho_raw_state *es;
+ err_t ret_err;
+
+ LWIP_ASSERT("arg != NULL",arg != NULL);
+ es = (struct tcpecho_raw_state *)arg;
+ if (p == NULL) {
+ /* remote host closed connection */
+ es->state = ES_CLOSING;
+ if(es->p == NULL) {
+ /* we're done sending, close it */
+ tcpecho_raw_close(tpcb, es);
+ } else {
+ /* we're not done yet */
+ tcpecho_raw_send(tpcb, es);
+ }
+ ret_err = ERR_OK;
+ } else if(err != ERR_OK) {
+ /* cleanup, for unknown reason */
+ LWIP_ASSERT("no pbuf expected here", p == NULL);
+ ret_err = err;
+ }
+ else if(es->state == ES_ACCEPTED) {
+ /* first data chunk in p->payload */
+ es->state = ES_RECEIVED;
+ /* store reference to incoming pbuf (chain) */
+ es->p = p;
+ tcpecho_raw_send(tpcb, es);
+ ret_err = ERR_OK;
+ } else if (es->state == ES_RECEIVED) {
+ /* read some more data */
+ if(es->p == NULL) {
+ es->p = p;
+ tcpecho_raw_send(tpcb, es);
+ } else {
+ struct pbuf *ptr;
+
+ /* chain pbufs to the end of what we recv'ed previously */
+ ptr = es->p;
+ pbuf_cat(ptr,p);
+ }
+ ret_err = ERR_OK;
+ } else {
+ /* unknown es->state, trash data */
+ tcp_recved(tpcb, p->tot_len);
+ pbuf_free(p);
+ ret_err = ERR_OK;
+ }
+ return ret_err;
+}
+
+static err_t
+tcpecho_raw_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+ err_t ret_err;
+ struct tcpecho_raw_state *es;
+
+ LWIP_UNUSED_ARG(arg);
+ if ((err != ERR_OK) || (newpcb == NULL)) {
+ return ERR_VAL;
+ }
+
+ /* Unless this pcb should have NORMAL priority, set its priority now.
+ When running out of pcbs, low priority pcbs can be aborted to create
+ new pcbs of higher priority. */
+ tcp_setprio(newpcb, TCP_PRIO_MIN);
+
+ es = (struct tcpecho_raw_state *)mem_malloc(sizeof(struct tcpecho_raw_state));
+ if (es != NULL) {
+ es->state = ES_ACCEPTED;
+ es->pcb = newpcb;
+ es->retries = 0;
+ es->p = NULL;
+ /* pass newly allocated es to our callbacks */
+ tcp_arg(newpcb, es);
+ tcp_recv(newpcb, tcpecho_raw_recv);
+ tcp_err(newpcb, tcpecho_raw_error);
+ tcp_poll(newpcb, tcpecho_raw_poll, 0);
+ tcp_sent(newpcb, tcpecho_raw_sent);
+ ret_err = ERR_OK;
+ } else {
+ ret_err = ERR_MEM;
+ }
+ return ret_err;
+}
+
+void
+tcpecho_raw_init(void)
+{
+ tcpecho_raw_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
+ if (tcpecho_raw_pcb != NULL) {
+ err_t err;
+
+ err = tcp_bind(tcpecho_raw_pcb, IP_ANY_TYPE, 7);
+ if (err == ERR_OK) {
+ tcpecho_raw_pcb = tcp_listen(tcpecho_raw_pcb);
+ tcp_accept(tcpecho_raw_pcb, tcpecho_raw_accept);
+ } else {
+ /* abort? output diagnostic? */
+ }
+ } else {
+ /* abort? output diagnostic? */
+ }
+}
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */