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