]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lwip.git/blob - src/api/api_msg.c
ccb4c0dd64cbe8b09d4f949e93caa7369aeafd23
[pes-rpp/rpp-lwip.git] / src / api / api_msg.c
1 /**
2  * @file
3  * Sequential API Internal module
4  *
5  */
6
7 /*
8  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
9  * All rights reserved. 
10  * 
11  * Redistribution and use in source and binary forms, with or without modification, 
12  * are permitted provided that the following conditions are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright notice,
15  *    this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright notice,
17  *    this list of conditions and the following disclaimer in the documentation
18  *    and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission. 
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
23  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
24  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
25  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
27  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
30  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
31  * OF SUCH DAMAGE.
32  *
33  * This file is part of the lwIP TCP/IP stack.
34  * 
35  * Author: Adam Dunkels <adam@sics.se>
36  *
37  */
38
39 #include "lwip/opt.h"
40
41 #if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
42
43 #include "lwip/api_msg.h"
44
45 #include "lwip/ip.h"
46 #include "lwip/udp.h"
47 #include "lwip/tcp.h"
48 #include "lwip/raw.h"
49
50 #include "lwip/memp.h"
51 #include "lwip/tcpip.h"
52 #include "lwip/igmp.h"
53 #include "lwip/dns.h"
54
55 /* forward declarations */
56 #if LWIP_TCP
57 static err_t do_writemore(struct netconn *conn);
58 static void do_close_internal(struct netconn *conn);
59 #endif
60
61 #if LWIP_RAW
62 /**
63  * Receive callback function for RAW netconns.
64  * Doesn't 'eat' the packet, only references it and sends it to
65  * conn->recvmbox
66  *
67  * @see raw.h (struct raw_pcb.recv) for parameters and return value
68  */
69 static u8_t
70 recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
71     struct ip_addr *addr)
72 {
73   struct netbuf *buf;
74   struct netconn *conn;
75 #if LWIP_SO_RCVBUF
76   int recv_avail;
77 #endif /* LWIP_SO_RCVBUF */
78
79   conn = arg;
80
81 #if LWIP_SO_RCVBUF
82   SYS_ARCH_GET(conn->recv_avail, recv_avail);
83   if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL) &&
84       ((recv_avail + (int)(p->tot_len)) <= conn->recv_bufsize)) {
85 #else  /* LWIP_SO_RCVBUF */
86   if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL)) {
87 #endif /* LWIP_SO_RCVBUF */
88     buf = memp_malloc(MEMP_NETBUF);
89     if (buf == NULL) {
90       return 0;
91     }
92     pbuf_ref(p);
93     buf->p = p;
94     buf->ptr = p;
95     buf->addr = addr;
96     buf->port = pcb->protocol;
97
98     SYS_ARCH_INC(conn->recv_avail, p->tot_len);
99     /* Register event with callback */
100     API_EVENT(conn, NETCONN_EVT_RCVPLUS, p->tot_len);
101     if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) {
102       netbuf_delete(buf);
103     }
104   }
105
106   return 0; /* do not eat the packet */
107 }
108 #endif /* LWIP_RAW*/
109
110 #if LWIP_UDP
111 /**
112  * Receive callback function for UDP netconns.
113  * Posts the packet to conn->recvmbox or deletes it on memory error.
114  *
115  * @see udp.h (struct udp_pcb.recv) for parameters
116  */
117 static void
118 recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
119    struct ip_addr *addr, u16_t port)
120 {
121   struct netbuf *buf;
122   struct netconn *conn;
123 #if LWIP_SO_RCVBUF
124   int recv_avail;
125 #endif /* LWIP_SO_RCVBUF */
126
127   LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
128   LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
129   LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
130   conn = arg;
131   LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
132
133 #if LWIP_SO_RCVBUF
134   SYS_ARCH_GET(conn->recv_avail, recv_avail);
135   if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL) ||
136       ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
137 #else  /* LWIP_SO_RCVBUF */
138   if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) {
139 #endif /* LWIP_SO_RCVBUF */
140     pbuf_free(p);
141     return;
142   }
143
144   buf = memp_malloc(MEMP_NETBUF);
145   if (buf == NULL) {
146     pbuf_free(p);
147     return;
148   } else {
149     buf->p = p;
150     buf->ptr = p;
151     buf->addr = addr;
152     buf->port = port;
153   }
154
155   SYS_ARCH_INC(conn->recv_avail, p->tot_len);
156   /* Register event with callback */
157   API_EVENT(conn, NETCONN_EVT_RCVPLUS, p->tot_len);
158   if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) {
159     netbuf_delete(buf);
160     return;
161   }
162 }
163 #endif /* LWIP_UDP */
164
165 #if LWIP_TCP
166 /**
167  * Receive callback function for TCP netconns.
168  * Posts the packet to conn->recvmbox or deletes it on memory error.
169  *
170  * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
171  */
172 static err_t
173 recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
174 {
175   struct netconn *conn;
176   u16_t len;
177
178   LWIP_UNUSED_ARG(pcb);
179   LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
180   LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
181   conn = arg;
182   LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
183
184   if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) {
185     pbuf_free(p);
186     return ERR_VAL;
187   }
188
189   conn->err = err;
190   if (p != NULL) {
191     len = p->tot_len;
192     SYS_ARCH_INC(conn->recv_avail, len);
193   } else {
194     len = 0;
195   }
196   /* Register event with callback */
197   API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
198   sys_mbox_post(conn->recvmbox, p);
199
200   return ERR_OK;
201 }
202
203 /**
204  * Poll callback function for TCP netconns.
205  * Wakes up an application thread that waits for a connection to close
206  * or data to be sent. The application thread then takes the
207  * appropriate action to go on.
208  *
209  * Signals the conn->sem.
210  * netconn_close waits for conn->sem if closing failed.
211  *
212  * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
213  */
214 static err_t
215 poll_tcp(void *arg, struct tcp_pcb *pcb)
216 {
217   struct netconn *conn = arg;
218
219   LWIP_UNUSED_ARG(pcb);
220   LWIP_ASSERT("conn != NULL", (conn != NULL));
221
222   if (conn->state == NETCONN_WRITE) {
223     do_writemore(conn);
224   } else if (conn->state == NETCONN_CLOSE) {
225     do_close_internal(conn);
226   }
227
228   return ERR_OK;
229 }
230
231 /**
232  * Sent callback function for TCP netconns.
233  * Signals the conn->sem and calls API_EVENT.
234  * netconn_write waits for conn->sem if send buffer is low.
235  *
236  * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
237  */
238 static err_t
239 sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
240 {
241   struct netconn *conn = arg;
242
243   LWIP_UNUSED_ARG(pcb);
244   LWIP_ASSERT("conn != NULL", (conn != NULL));
245
246   if (conn->state == NETCONN_WRITE) {
247     LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
248     do_writemore(conn);
249   } else if (conn->state == NETCONN_CLOSE) {
250     do_close_internal(conn);
251   }
252
253   if (conn) {
254     if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT)) {
255       API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
256     }
257   }
258   
259   return ERR_OK;
260 }
261
262 /**
263  * Error callback function for TCP netconns.
264  * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
265  * The application thread has then to decide what to do.
266  *
267  * @see tcp.h (struct tcp_pcb.err) for parameters
268  */
269 static void
270 err_tcp(void *arg, err_t err)
271 {
272   struct netconn *conn;
273
274   conn = arg;
275   LWIP_ASSERT("conn != NULL", (conn != NULL));
276
277   conn->pcb.tcp = NULL;
278
279   conn->err = err;
280   if (conn->recvmbox != SYS_MBOX_NULL) {
281     /* Register event with callback */
282     API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
283     sys_mbox_post(conn->recvmbox, NULL);
284   }
285   if (conn->op_completed != SYS_SEM_NULL && conn->state == NETCONN_CONNECT) {
286     conn->state = NETCONN_NONE;
287     sys_sem_signal(conn->op_completed);
288   }
289   if (conn->acceptmbox != SYS_MBOX_NULL) {
290     /* Register event with callback */
291     API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
292     sys_mbox_post(conn->acceptmbox, NULL);
293   }
294   if ((conn->state == NETCONN_WRITE) || (conn->state == NETCONN_CLOSE)) {
295     /* calling do_writemore/do_close_internal is not necessary
296        since the pcb has already been deleted! */
297     conn->state = NETCONN_NONE;
298     /* wake up the waiting task */
299     sys_sem_signal(conn->op_completed);
300   }
301 }
302
303 /**
304  * Setup a tcp_pcb with the correct callback function pointers
305  * and their arguments.
306  *
307  * @param conn the TCP netconn to setup
308  */
309 static void
310 setup_tcp(struct netconn *conn)
311 {
312   struct tcp_pcb *pcb;
313
314   pcb = conn->pcb.tcp;
315   tcp_arg(pcb, conn);
316   tcp_recv(pcb, recv_tcp);
317   tcp_sent(pcb, sent_tcp);
318   tcp_poll(pcb, poll_tcp, 4);
319   tcp_err(pcb, err_tcp);
320 }
321
322 /**
323  * Accept callback function for TCP netconns.
324  * Allocates a new netconn and posts that to conn->acceptmbox.
325  *
326  * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
327  */
328 static err_t
329 accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
330 {
331   struct netconn *newconn;
332   struct netconn *conn;
333
334 #if API_MSG_DEBUG
335 #if TCP_DEBUG
336   tcp_debug_print_state(newpcb->state);
337 #endif /* TCP_DEBUG */
338 #endif /* API_MSG_DEBUG */
339   conn = (struct netconn *)arg;
340
341   LWIP_ERROR("accept_function: invalid conn->acceptmbox",
342              conn->acceptmbox != SYS_MBOX_NULL, return ERR_VAL;);
343
344   /* We have to set the callback here even though
345    * the new socket is unknown. conn->socket is marked as -1. */
346   newconn = netconn_alloc(conn->type, conn->callback);
347   if (newconn == NULL) {
348     return ERR_MEM;
349   }
350   newconn->pcb.tcp = newpcb;
351   setup_tcp(newconn);
352   newconn->err = err;
353   /* Register event with callback */
354   API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
355
356   if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) {
357     /* When returning != ERR_OK, the connection is aborted in tcp_process(),
358        so do nothing here! */
359     newconn->pcb.tcp = NULL;
360     netconn_free(newconn);
361     return ERR_MEM;
362   }
363   return ERR_OK;
364 }
365 #endif /* LWIP_TCP */
366
367 /**
368  * Create a new pcb of a specific type.
369  * Called from do_newconn().
370  *
371  * @param msg the api_msg_msg describing the connection type
372  * @return msg->conn->err, but the return value is currently ignored
373  */
374 static err_t
375 pcb_new(struct api_msg_msg *msg)
376 {
377    msg->conn->err = ERR_OK;
378
379    LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
380
381    /* Allocate a PCB for this connection */
382    switch(NETCONNTYPE_GROUP(msg->conn->type)) {
383 #if LWIP_RAW
384    case NETCONN_RAW:
385      msg->conn->pcb.raw = raw_new(msg->msg.n.proto);
386      if(msg->conn->pcb.raw == NULL) {
387        msg->conn->err = ERR_MEM;
388        break;
389      }
390      raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
391      break;
392 #endif /* LWIP_RAW */
393 #if LWIP_UDP
394    case NETCONN_UDP:
395      msg->conn->pcb.udp = udp_new();
396      if(msg->conn->pcb.udp == NULL) {
397        msg->conn->err = ERR_MEM;
398        break;
399      }
400 #if LWIP_UDPLITE
401      if (msg->conn->type==NETCONN_UDPLITE) {
402        udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
403      }
404 #endif /* LWIP_UDPLITE */
405      if (msg->conn->type==NETCONN_UDPNOCHKSUM) {
406        udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
407      }
408      udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
409      break;
410 #endif /* LWIP_UDP */
411 #if LWIP_TCP
412    case NETCONN_TCP:
413      msg->conn->pcb.tcp = tcp_new();
414      if(msg->conn->pcb.tcp == NULL) {
415        msg->conn->err = ERR_MEM;
416        break;
417      }
418      setup_tcp(msg->conn);
419      break;
420 #endif /* LWIP_TCP */
421    default:
422      /* Unsupported netconn type, e.g. protocol disabled */
423      msg->conn->err = ERR_VAL;
424      break;
425    }
426
427   return msg->conn->err;
428 }
429
430 /**
431  * Create a new pcb of a specific type inside a netconn.
432  * Called from netconn_new_with_proto_and_callback.
433  *
434  * @param msg the api_msg_msg describing the connection type
435  */
436 void
437 do_newconn(struct api_msg_msg *msg)
438 {
439    if(msg->conn->pcb.tcp == NULL) {
440      pcb_new(msg);
441    }
442    /* Else? This "new" connection already has a PCB allocated. */
443    /* Is this an error condition? Should it be deleted? */
444    /* We currently just are happy and return. */
445
446    TCPIP_APIMSG_ACK(msg);
447 }
448
449 /**
450  * Create a new netconn (of a specific type) that has a callback function.
451  * The corresponding pcb is NOT created!
452  *
453  * @param t the type of 'connection' to create (@see enum netconn_type)
454  * @param proto the IP protocol for RAW IP pcbs
455  * @param callback a function to call on status changes (RX available, TX'ed)
456  * @return a newly allocated struct netconn or
457  *         NULL on memory error
458  */
459 struct netconn*
460 netconn_alloc(enum netconn_type t, netconn_callback callback)
461 {
462   struct netconn *conn;
463   int size;
464
465   conn = memp_malloc(MEMP_NETCONN);
466   if (conn == NULL) {
467     return NULL;
468   }
469
470   conn->err = ERR_OK;
471   conn->type = t;
472   conn->pcb.tcp = NULL;
473
474 #if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \
475     (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE)
476   size = DEFAULT_RAW_RECVMBOX_SIZE;
477 #else
478   switch(NETCONNTYPE_GROUP(t)) {
479 #if LWIP_RAW
480   case NETCONN_RAW:
481     size = DEFAULT_RAW_RECVMBOX_SIZE;
482     break;
483 #endif /* LWIP_RAW */
484 #if LWIP_UDP
485   case NETCONN_UDP:
486     size = DEFAULT_UDP_RECVMBOX_SIZE;
487     break;
488 #endif /* LWIP_UDP */
489 #if LWIP_TCP
490   case NETCONN_TCP:
491     size = DEFAULT_TCP_RECVMBOX_SIZE;
492     break;
493 #endif /* LWIP_TCP */
494   default:
495     LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
496     break;
497   }
498 #endif
499
500   if ((conn->op_completed = sys_sem_new(0)) == SYS_SEM_NULL) {
501     memp_free(MEMP_NETCONN, conn);
502     return NULL;
503   }
504   if ((conn->recvmbox = sys_mbox_new(size)) == SYS_MBOX_NULL) {
505     sys_sem_free(conn->op_completed);
506     memp_free(MEMP_NETCONN, conn);
507     return NULL;
508   }
509
510   conn->acceptmbox   = SYS_MBOX_NULL;
511   conn->state        = NETCONN_NONE;
512   /* initialize socket to -1 since 0 is a valid socket */
513   conn->socket       = -1;
514   conn->callback     = callback;
515   conn->recv_avail   = 0;
516 #if LWIP_SO_RCVTIMEO
517   conn->recv_timeout = 0;
518 #endif /* LWIP_SO_RCVTIMEO */
519 #if LWIP_SO_RCVBUF
520   conn->recv_bufsize = INT_MAX;
521 #endif /* LWIP_SO_RCVBUF */
522   return conn;
523 }
524
525 /**
526  * Delete a netconn and all its resources.
527  * The pcb is NOT freed (since we might not be in the right thread context do this).
528  *
529  * @param conn the netconn to free
530  */
531 void
532 netconn_free(struct netconn *conn)
533 {
534   void *mem;
535   LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
536
537   /* Drain the recvmbox. */
538   if (conn->recvmbox != SYS_MBOX_NULL) {
539     while (sys_mbox_tryfetch(conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
540       if (conn->type == NETCONN_TCP) {
541         if(mem != NULL) {
542           pbuf_free((struct pbuf *)mem);
543         }
544       } else {
545         netbuf_delete((struct netbuf *)mem);
546       }
547     }
548     sys_mbox_free(conn->recvmbox);
549     conn->recvmbox = SYS_MBOX_NULL;
550   }
551
552   /* Drain the acceptmbox. */
553   if (conn->acceptmbox != SYS_MBOX_NULL) {
554     while (sys_mbox_tryfetch(conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
555       netconn_delete((struct netconn *)mem);
556     }
557     sys_mbox_free(conn->acceptmbox);
558     conn->acceptmbox = SYS_MBOX_NULL;
559   }
560
561   sys_sem_free(conn->op_completed);
562   conn->op_completed = SYS_SEM_NULL;
563
564   memp_free(MEMP_NETCONN, conn);
565 }
566
567 #if LWIP_TCP
568 /**
569  * Internal helper function to close a TCP netconn: since this sometimes
570  * doesn't work at the first attempt, this function is called from multiple
571  * places.
572  *
573  * @param conn the TCP netconn to close
574  */
575 static void
576 do_close_internal(struct netconn *conn)
577 {
578   err_t err;
579
580   LWIP_ASSERT("invalid conn", (conn != NULL));
581   LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP));
582   LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
583   LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
584
585   /* Set back some callback pointers */
586   if (conn->pcb.tcp->state == LISTEN) {
587     tcp_arg(conn->pcb.tcp, NULL);
588     tcp_accept(conn->pcb.tcp, NULL);
589   } else {
590     tcp_recv(conn->pcb.tcp, NULL);
591   }
592   /* Try to close the connection */
593   err = tcp_close(conn->pcb.tcp);
594   if (err == ERR_OK) {
595     /* Closing succeeded */
596     conn->state = NETCONN_NONE;
597     /* Set back some callback pointers as conn is going away */
598     tcp_err(conn->pcb.tcp, NULL);
599     tcp_poll(conn->pcb.tcp, NULL, 4);
600     tcp_sent(conn->pcb.tcp, NULL);
601     tcp_recv(conn->pcb.tcp, NULL);
602     tcp_arg(conn->pcb.tcp, NULL);
603     conn->pcb.tcp = NULL;
604     conn->err = ERR_OK;
605     /* Trigger select() in socket layer. This send should something else so the
606        errorfd is set, not the read and write fd! */
607     API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
608     API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
609     /* wake up the application task */
610     sys_sem_signal(conn->op_completed);
611   }
612   /* If closing didn't succeed, we get called again either
613      from poll_tcp or from sent_tcp */
614 }
615 #endif /* LWIP_TCP */
616
617 /**
618  * Delete the pcb inside a netconn.
619  * Called from netconn_delete.
620  *
621  * @param msg the api_msg_msg pointing to the connection
622  */
623 void
624 do_delconn(struct api_msg_msg *msg)
625 {
626   if (msg->conn->pcb.tcp != NULL) {
627     switch (NETCONNTYPE_GROUP(msg->conn->type)) {
628 #if LWIP_RAW
629     case NETCONN_RAW:
630       raw_remove(msg->conn->pcb.raw);
631       break;
632 #endif /* LWIP_RAW */
633 #if LWIP_UDP
634     case NETCONN_UDP:
635       msg->conn->pcb.udp->recv_arg = NULL;
636       udp_remove(msg->conn->pcb.udp);
637       break;
638 #endif /* LWIP_UDP */
639 #if LWIP_TCP
640     case NETCONN_TCP:
641       msg->conn->state = NETCONN_CLOSE;
642       do_close_internal(msg->conn);
643       /* API_EVENT is called inside do_close_internal, before releasing
644          the application thread, so we can return at this point! */
645       return;
646 #endif /* LWIP_TCP */
647     default:
648       break;
649     }
650   }
651   /* tcp netconns don't come here! */
652
653   /* Trigger select() in socket layer. This send should something else so the
654      errorfd is set, not the read and write fd! */
655   API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
656   API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
657
658   if (msg->conn->op_completed != SYS_SEM_NULL) {
659     sys_sem_signal(msg->conn->op_completed);
660   }
661 }
662
663 /**
664  * Bind a pcb contained in a netconn
665  * Called from netconn_bind.
666  *
667  * @param msg the api_msg_msg pointing to the connection and containing
668  *            the IP address and port to bind to
669  */
670 void
671 do_bind(struct api_msg_msg *msg)
672 {
673   if (!ERR_IS_FATAL(msg->conn->err)) {
674     if (msg->conn->pcb.tcp != NULL) {
675       switch (NETCONNTYPE_GROUP(msg->conn->type)) {
676 #if LWIP_RAW
677       case NETCONN_RAW:
678         msg->conn->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
679         break;
680 #endif /* LWIP_RAW */
681 #if LWIP_UDP
682       case NETCONN_UDP:
683         msg->conn->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
684         break;
685 #endif /* LWIP_UDP */
686 #if LWIP_TCP
687       case NETCONN_TCP:
688         msg->conn->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port);
689         break;
690 #endif /* LWIP_TCP */
691       default:
692         break;
693       }
694     } else {
695       /* msg->conn->pcb is NULL */
696       msg->conn->err = ERR_VAL;
697     }
698   }
699   TCPIP_APIMSG_ACK(msg);
700 }
701
702 #if LWIP_TCP
703 /**
704  * TCP callback function if a connection (opened by tcp_connect/do_connect) has
705  * been established (or reset by the remote host).
706  *
707  * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
708  */
709 static err_t
710 do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
711 {
712   struct netconn *conn;
713
714   LWIP_UNUSED_ARG(pcb);
715
716   conn = arg;
717
718   if (conn == NULL) {
719     return ERR_VAL;
720   }
721
722   conn->err = err;
723   if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) {
724     setup_tcp(conn);
725   }
726   conn->state = NETCONN_NONE;
727   sys_sem_signal(conn->op_completed);
728   return ERR_OK;
729 }
730 #endif /* LWIP_TCP */
731
732 /**
733  * Connect a pcb contained inside a netconn
734  * Called from netconn_connect.
735  *
736  * @param msg the api_msg_msg pointing to the connection and containing
737  *            the IP address and port to connect to
738  */
739 void
740 do_connect(struct api_msg_msg *msg)
741 {
742   if (msg->conn->pcb.tcp == NULL) {
743     sys_sem_signal(msg->conn->op_completed);
744     return;
745   }
746
747   switch (NETCONNTYPE_GROUP(msg->conn->type)) {
748 #if LWIP_RAW
749   case NETCONN_RAW:
750     msg->conn->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
751     sys_sem_signal(msg->conn->op_completed);
752     break;
753 #endif /* LWIP_RAW */
754 #if LWIP_UDP
755   case NETCONN_UDP:
756     msg->conn->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
757     sys_sem_signal(msg->conn->op_completed);
758     break;
759 #endif /* LWIP_UDP */
760 #if LWIP_TCP
761   case NETCONN_TCP:
762     msg->conn->state = NETCONN_CONNECT;
763     setup_tcp(msg->conn);
764     msg->conn->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port,
765                                  do_connected);
766     /* sys_sem_signal() is called from do_connected (or err_tcp()),
767      * when the connection is established! */
768     break;
769 #endif /* LWIP_TCP */
770   default:
771     break;
772   }
773 }
774
775 /**
776  * Connect a pcb contained inside a netconn
777  * Only used for UDP netconns.
778  * Called from netconn_disconnect.
779  *
780  * @param msg the api_msg_msg pointing to the connection to disconnect
781  */
782 void
783 do_disconnect(struct api_msg_msg *msg)
784 {
785 #if LWIP_UDP
786   if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
787     udp_disconnect(msg->conn->pcb.udp);
788   }
789 #endif /* LWIP_UDP */
790   TCPIP_APIMSG_ACK(msg);
791 }
792
793 /**
794  * Set a TCP pcb contained in a netconn into listen mode
795  * Called from netconn_listen.
796  *
797  * @param msg the api_msg_msg pointing to the connection
798  */
799 void
800 do_listen(struct api_msg_msg *msg)
801 {
802 #if LWIP_TCP
803   if (!ERR_IS_FATAL(msg->conn->err)) {
804     if (msg->conn->pcb.tcp != NULL) {
805       if (msg->conn->type == NETCONN_TCP) {
806         if (msg->conn->pcb.tcp->state == CLOSED) {
807 #if TCP_LISTEN_BACKLOG
808           struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
809 #else  /* TCP_LISTEN_BACKLOG */
810           struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp);
811 #endif /* TCP_LISTEN_BACKLOG */
812           if (lpcb == NULL) {
813             msg->conn->err = ERR_MEM;
814           } else {
815             /* delete the recvmbox and allocate the acceptmbox */
816             if (msg->conn->recvmbox != SYS_MBOX_NULL) {
817               /** @todo: should we drain the recvmbox here? */
818               sys_mbox_free(msg->conn->recvmbox);
819               msg->conn->recvmbox = NULL;
820             }
821             if (msg->conn->acceptmbox == SYS_MBOX_NULL) {
822               if ((msg->conn->acceptmbox = sys_mbox_new(DEFAULT_ACCEPTMBOX_SIZE)) == SYS_MBOX_NULL) {
823                 msg->conn->err = ERR_MEM;
824               }
825             }
826             if (msg->conn->err == ERR_OK) {
827               msg->conn->state = NETCONN_LISTEN;
828               msg->conn->pcb.tcp = lpcb;
829               tcp_arg(msg->conn->pcb.tcp, msg->conn);
830               tcp_accept(msg->conn->pcb.tcp, accept_function);
831             }
832           }
833         } else {
834           msg->conn->err = ERR_CONN;
835         }
836       }
837     }
838   }
839 #endif /* LWIP_TCP */
840   TCPIP_APIMSG_ACK(msg);
841 }
842
843 /**
844  * Send some data on a RAW or UDP pcb contained in a netconn
845  * Called from netconn_send
846  *
847  * @param msg the api_msg_msg pointing to the connection
848  */
849 void
850 do_send(struct api_msg_msg *msg)
851 {
852   if (!ERR_IS_FATAL(msg->conn->err)) {
853     if (msg->conn->pcb.tcp != NULL) {
854       switch (NETCONNTYPE_GROUP(msg->conn->type)) {
855 #if LWIP_RAW
856       case NETCONN_RAW:
857         if (msg->msg.b->addr == NULL) {
858           msg->conn->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
859         } else {
860           msg->conn->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, msg->msg.b->addr);
861         }
862         break;
863 #endif
864 #if LWIP_UDP
865       case NETCONN_UDP:
866         if (msg->msg.b->addr == NULL) {
867           msg->conn->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
868         } else {
869           msg->conn->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, msg->msg.b->addr, msg->msg.b->port);
870         }
871         break;
872 #endif /* LWIP_UDP */
873       default:
874         break;
875       }
876     }
877   }
878   TCPIP_APIMSG_ACK(msg);
879 }
880
881 /**
882  * Recv some data from a RAW or UDP pcb contained in a netconn
883  * Called from netconn_recv
884  *
885  * @param msg the api_msg_msg pointing to the connection
886  */
887 void
888 do_recv(struct api_msg_msg *msg)
889 {
890 #if LWIP_TCP
891   if (!ERR_IS_FATAL(msg->conn->err)) {
892     if (msg->conn->pcb.tcp != NULL) {
893       if (msg->conn->type == NETCONN_TCP) {
894 #if TCP_LISTEN_BACKLOG
895         if (msg->conn->pcb.tcp->state == LISTEN) {
896           tcp_accepted(msg->conn->pcb.tcp);
897         } else
898 #endif /* TCP_LISTEN_BACKLOG */
899         {
900           tcp_recved(msg->conn->pcb.tcp, msg->msg.r.len);
901         }
902       }
903     }
904   }
905 #endif /* LWIP_TCP */
906   TCPIP_APIMSG_ACK(msg);
907 }
908
909 #if LWIP_TCP
910 /**
911  * See if more data needs to be written from a previous call to netconn_write.
912  * Called initially from do_write. If the first call can't send all data
913  * (because of low memory or empty send-buffer), this function is called again
914  * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
915  * blocking application thread (waiting in netconn_write) is released.
916  *
917  * @param conn netconn (that is currently in state NETCONN_WRITE) to process
918  * @return ERR_OK
919  *         ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
920  */
921 static err_t
922 do_writemore(struct netconn *conn)
923 {
924   err_t err;
925   void *dataptr;
926   u16_t len, available;
927   u8_t write_finished = 0;
928
929   LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
930
931   dataptr = (u8_t*)conn->write_msg->msg.w.dataptr + conn->write_offset;
932   if ((conn->write_msg->msg.w.len - conn->write_offset > 0xffff)) { /* max_u16_t */
933     len = 0xffff;
934 #if LWIP_TCPIP_CORE_LOCKING
935     conn->write_delayed = 1;
936 #endif
937   } else {
938     len = conn->write_msg->msg.w.len - conn->write_offset;
939   }
940   available = tcp_sndbuf(conn->pcb.tcp);
941   if (available < len) {
942     /* don't try to write more than sendbuf */
943     len = available;
944 #if LWIP_TCPIP_CORE_LOCKING
945     conn->write_delayed = 1;
946 #endif
947   }
948
949   err = tcp_write(conn->pcb.tcp, dataptr, len, conn->write_msg->msg.w.apiflags);
950   LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->write_msg->msg.w.len));
951   if (err == ERR_OK) {
952     conn->write_offset += len;
953     if (conn->write_offset == conn->write_msg->msg.w.len) {
954       /* everything was written */
955       write_finished = 1;
956       conn->write_msg = NULL;
957       conn->write_offset = 0;
958     }
959     err = tcp_output_nagle(conn->pcb.tcp);
960     conn->err = err;
961     if ((err == ERR_OK) && (tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT)) {
962       API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
963     }
964   } else if (err == ERR_MEM) {
965     /* tcp_enqueue returned ERR_MEM, try tcp_output anyway */
966     err = tcp_output(conn->pcb.tcp);
967
968 #if LWIP_TCPIP_CORE_LOCKING
969     conn->write_delayed = 1;
970 #endif
971   } else {
972     /* if ERR_MEM, we wait for sent_tcp or poll_tcp to be called
973        on other errors we don't try writing any more */
974     conn->err = err;
975     write_finished = 1;
976   }
977
978   if (write_finished) {
979     /* everything was written: set back connection state
980        and back to application task */
981     conn->state = NETCONN_NONE;
982 #if LWIP_TCPIP_CORE_LOCKING
983     if (conn->write_delayed != 0)
984 #endif
985     {
986       sys_sem_signal(conn->op_completed);
987     }
988   }
989 #if LWIP_TCPIP_CORE_LOCKING
990   else
991     return ERR_MEM;
992 #endif
993   return ERR_OK;
994 }
995 #endif /* LWIP_TCP */
996
997 /**
998  * Send some data on a TCP pcb contained in a netconn
999  * Called from netconn_write
1000  *
1001  * @param msg the api_msg_msg pointing to the connection
1002  */
1003 void
1004 do_write(struct api_msg_msg *msg)
1005 {
1006   if (!ERR_IS_FATAL(msg->conn->err)) {
1007     if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) {
1008 #if LWIP_TCP
1009       msg->conn->state = NETCONN_WRITE;
1010       /* set all the variables used by do_writemore */
1011       msg->conn->write_msg = msg;
1012       msg->conn->write_offset = 0;
1013 #if LWIP_TCPIP_CORE_LOCKING
1014       msg->conn->write_delayed = 0;
1015       if (do_writemore(msg->conn) != ERR_OK) {
1016         LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
1017         UNLOCK_TCPIP_CORE();
1018         sys_arch_sem_wait(msg->conn->op_completed, 0);
1019         LOCK_TCPIP_CORE();
1020         LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
1021       }
1022 #else
1023       do_writemore(msg->conn);
1024 #endif
1025       /* for both cases: if do_writemore was called, don't ACK the APIMSG! */
1026       return;
1027 #endif /* LWIP_TCP */
1028 #if (LWIP_UDP || LWIP_RAW)
1029     } else {
1030       msg->conn->err = ERR_VAL;
1031 #endif /* (LWIP_UDP || LWIP_RAW) */
1032     }
1033   }
1034   TCPIP_APIMSG_ACK(msg);
1035 }
1036
1037 /**
1038  * Return a connection's local or remote address
1039  * Called from netconn_getaddr
1040  *
1041  * @param msg the api_msg_msg pointing to the connection
1042  */
1043 void
1044 do_getaddr(struct api_msg_msg *msg)
1045 {
1046   if (msg->conn->pcb.ip != NULL) {
1047     *(msg->msg.ad.ipaddr) = (msg->msg.ad.local?msg->conn->pcb.ip->local_ip:msg->conn->pcb.ip->remote_ip);
1048     
1049     switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1050 #if LWIP_RAW
1051     case NETCONN_RAW:
1052       if (msg->msg.ad.local) {
1053         *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
1054       } else {
1055         /* return an error as connecting is only a helper for upper layers */
1056         msg->conn->err = ERR_CONN;
1057       }
1058       break;
1059 #endif /* LWIP_RAW */
1060 #if LWIP_UDP
1061     case NETCONN_UDP:
1062       if (msg->msg.ad.local) {
1063         *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
1064       } else {
1065         if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
1066           msg->conn->err = ERR_CONN;
1067         } else {
1068           *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
1069         }
1070       }
1071       break;
1072 #endif /* LWIP_UDP */
1073 #if LWIP_TCP
1074     case NETCONN_TCP:
1075       *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port);
1076       break;
1077 #endif /* LWIP_TCP */
1078     }
1079   } else {
1080     msg->conn->err = ERR_CONN;
1081   }
1082   TCPIP_APIMSG_ACK(msg);
1083 }
1084
1085 /**
1086  * Close a TCP pcb contained in a netconn
1087  * Called from netconn_close
1088  *
1089  * @param msg the api_msg_msg pointing to the connection
1090  */
1091 void
1092 do_close(struct api_msg_msg *msg)
1093 {
1094 #if LWIP_TCP
1095   if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) {
1096       msg->conn->state = NETCONN_CLOSE;
1097       do_close_internal(msg->conn);
1098       /* for tcp netconns, do_close_internal ACKs the message */
1099   } else
1100 #endif /* LWIP_TCP */
1101   {
1102     msg->conn->err = ERR_VAL;
1103     TCPIP_APIMSG_ACK(msg);
1104   }
1105 }
1106
1107 #if LWIP_IGMP
1108 /**
1109  * Join multicast groups for UDP netconns.
1110  * Called from netconn_join_leave_group
1111  *
1112  * @param msg the api_msg_msg pointing to the connection
1113  */
1114 void
1115 do_join_leave_group(struct api_msg_msg *msg)
1116
1117   if (!ERR_IS_FATAL(msg->conn->err)) {
1118     if (msg->conn->pcb.tcp != NULL) {
1119       if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
1120 #if LWIP_UDP
1121         if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
1122           msg->conn->err = igmp_joingroup(msg->msg.jl.interface, msg->msg.jl.multiaddr);
1123         } else {
1124           msg->conn->err = igmp_leavegroup(msg->msg.jl.interface, msg->msg.jl.multiaddr);
1125         }
1126 #endif /* LWIP_UDP */
1127 #if (LWIP_TCP || LWIP_RAW)
1128       } else {
1129         msg->conn->err = ERR_VAL;
1130 #endif /* (LWIP_TCP || LWIP_RAW) */
1131       }
1132     }
1133   }
1134   TCPIP_APIMSG_ACK(msg);
1135 }
1136 #endif /* LWIP_IGMP */
1137
1138 #if LWIP_DNS
1139 /**
1140  * Callback function that is called when DNS name is resolved
1141  * (or on timeout). A waiting application thread is waked up by
1142  * signaling the semaphore.
1143  */
1144 static void
1145 do_dns_found(const char *name, struct ip_addr *ipaddr, void *arg)
1146 {
1147   struct dns_api_msg *msg = (struct dns_api_msg*)arg;
1148
1149   LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0);
1150
1151   if (ipaddr == NULL) {
1152     /* timeout or memory error */
1153     *msg->err = ERR_VAL;
1154   } else {
1155     /* address was resolved */
1156     *msg->err = ERR_OK;
1157     *msg->addr = *ipaddr;
1158   }
1159   /* wake up the application task waiting in netconn_gethostbyname */
1160   sys_sem_signal(msg->sem);
1161 }
1162
1163 /**
1164  * Execute a DNS query
1165  * Called from netconn_gethostbyname
1166  *
1167  * @param arg the dns_api_msg pointing to the query
1168  */
1169 void
1170 do_gethostbyname(void *arg)
1171 {
1172   struct dns_api_msg *msg = (struct dns_api_msg*)arg;
1173
1174   *msg->err = dns_gethostbyname(msg->name, msg->addr, do_dns_found, msg);
1175   if (*msg->err != ERR_INPROGRESS) {
1176     /* on error or immediate success, wake up the application
1177      * task waiting in netconn_gethostbyname */
1178     sys_sem_signal(msg->sem);
1179   }
1180 }
1181 #endif /* LWIP_DNS */
1182
1183 #endif /* LWIP_NETCONN */
1184