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