]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lwip.git/blob - src/api/api_lib.c
sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h: Introduc...
[pes-rpp/rpp-lwip.git] / src / api / api_lib.c
1 /**
2  * @file
3  * Sequential API External 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 /* This is the part of the API that is linked with
40    the application */
41
42 #include "lwip/opt.h"
43
44 #if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
45
46 #include "lwip/api.h"
47 #include "lwip/tcpip.h"
48 #include "lwip/memp.h"
49
50 #include "lwip/ip.h"
51 #include "lwip/raw.h"
52 #include "lwip/udp.h"
53 #include "lwip/tcp.h"
54
55 #include <string.h>
56
57 /**
58  * Create a new netconn (of a specific type) that has a callback function.
59  * The corresponding pcb is also created.
60  *
61  * @param t the type of 'connection' to create (@see enum netconn_type)
62  * @param proto the IP protocol for RAW IP pcbs
63  * @param callback a function to call on status changes (RX available, TX'ed)
64  * @return a newly allocated struct netconn or
65  *         NULL on memory error
66  */
67 struct netconn*
68 netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
69 {
70   struct netconn *conn;
71   struct api_msg msg;
72
73   conn = netconn_alloc(t, callback);
74   if (conn != NULL ) {
75     msg.function = do_newconn;
76     msg.msg.msg.n.proto = proto;
77     msg.msg.conn = conn;
78     TCPIP_APIMSG(&msg);
79
80     if (conn->err != ERR_OK) {
81       LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
82       LWIP_ASSERT("conn has no mbox", conn->mbox != SYS_MBOX_NULL);
83       LWIP_ASSERT("conn has no recvmbox", conn->recvmbox != SYS_MBOX_NULL);
84       LWIP_ASSERT("conn->acceptmbox shouldn't exist", conn->acceptmbox == SYS_MBOX_NULL);
85       sys_mbox_free(conn->mbox);
86       sys_mbox_free(conn->recvmbox);
87       memp_free(MEMP_NETCONN, conn);
88       return NULL;
89     }
90   }
91   return conn;
92 }
93
94 /**
95  * Close a netconn 'connection' and free its resources.
96  * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
97  * after this returns.
98  *
99  * @param conn the netconn to delete
100  * @return ERR_OK if the connection was deleted
101  */
102 err_t
103 netconn_delete(struct netconn *conn)
104 {
105   struct api_msg msg;
106   void *mem;
107
108   /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
109   if (conn == NULL) {
110     return ERR_OK;
111   }
112
113   msg.function = do_delconn;
114   msg.msg.conn = conn;
115   tcpip_apimsg(&msg);
116
117   /* Drain the recvmbox. */
118   if (conn->recvmbox != SYS_MBOX_NULL) {
119     while (sys_mbox_tryfetch(conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
120       if (conn->type == NETCONN_TCP) {
121         if(mem != NULL) {
122           pbuf_free((struct pbuf *)mem);
123         }
124       } else {
125         netbuf_delete((struct netbuf *)mem);
126       }
127     }
128     sys_mbox_free(conn->recvmbox);
129     conn->recvmbox = SYS_MBOX_NULL;
130   }
131
132   /* Drain the acceptmbox. */
133   if (conn->acceptmbox != SYS_MBOX_NULL) {
134     while (sys_mbox_tryfetch(conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
135       netconn_delete((struct netconn *)mem);
136     }
137     sys_mbox_free(conn->acceptmbox);
138     conn->acceptmbox = SYS_MBOX_NULL;
139   }
140
141   sys_mbox_free(conn->mbox);
142   conn->mbox = SYS_MBOX_NULL;
143
144   memp_free(MEMP_NETCONN, conn);
145   return ERR_OK;
146 }
147
148 /**
149  * Get the type of a netconn (as enum netconn_type).
150  *
151  * @param conn the netconn of which to get the type
152  * @return the netconn_type of conn
153  */
154 enum netconn_type
155 netconn_type(struct netconn *conn)
156 {
157   LWIP_ERROR("netconn_type: invalid conn", (conn != NULL), return NETCONN_INVALID;);
158   return conn->type;
159 }
160
161 /**
162  * Get the local or remote IP address and port of a netconn.
163  * For RAW netconns, this returns the protocol instead of a port!
164  *
165  * @param conn the netconn to query
166  * @param addr a pointer to which to save the IP address
167  * @param port a pointer to which to save the port (or protocol for RAW)
168  * @param local 1 to get the local IP address, 0 to get the remote one
169  * @return ERR_CONN for invalid connections
170  *         ERR_OK if the information was retrieved
171  */
172 err_t
173 netconn_getaddr(struct netconn *conn, struct ip_addr *addr, u16_t *port, u8_t local)
174 {
175   struct api_msg msg;
176
177   LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
178   LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
179   LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
180
181   msg.function = do_getaddr;
182   msg.msg.conn = conn;
183   msg.msg.msg.ad.ipaddr = addr;
184   msg.msg.msg.ad.port = port;
185   msg.msg.msg.ad.local = local;
186   TCPIP_APIMSG(&msg);
187
188   return conn->err;
189 }
190
191 /**
192  * Bind a netconn to a specific local IP address and port.
193  * Binding one netconn twice might not always be checked correctly!
194  *
195  * @param conn the netconn to bind
196  * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY
197  *             to bind to all addresses)
198  * @param port the local port to bind the netconn to (not used for RAW)
199  * @return ERR_OK if bound, any other err_t on failure
200  */
201 err_t
202 netconn_bind(struct netconn *conn, struct ip_addr *addr, u16_t port)
203 {
204   struct api_msg msg;
205
206   LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
207
208   msg.function = do_bind;
209   msg.msg.conn = conn;
210   msg.msg.msg.bc.ipaddr = addr;
211   msg.msg.msg.bc.port = port;
212   TCPIP_APIMSG(&msg);
213   return conn->err;
214 }
215
216 /**
217  * Connect a netconn to a specific remote IP address and port.
218  *
219  * @param conn the netconn to connect
220  * @param addr the remote IP address to connect to
221  * @param port the remote port to connect to (no used for RAW)
222  * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
223  */
224 err_t
225 netconn_connect(struct netconn *conn, struct ip_addr *addr, u16_t port)
226 {
227   struct api_msg msg;
228
229   LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
230
231   msg.function = do_connect;
232   msg.msg.conn = conn;
233   msg.msg.msg.bc.ipaddr = addr;
234   msg.msg.msg.bc.port = port;
235   /* This is the only function which need to not block tcpip_thread */
236   tcpip_apimsg(&msg);
237   return conn->err;
238 }
239
240 /**
241  * Disconnect a netconn from its current peer (only valid for UDP netconns).
242  *
243  * @param conn the netconn to disconnect
244  * @return TODO: return value is not set here...
245  */
246 err_t
247 netconn_disconnect(struct netconn *conn)
248 {
249   struct api_msg msg;
250
251   LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
252
253   msg.function = do_disconnect;
254   msg.msg.conn = conn;
255   TCPIP_APIMSG(&msg);
256   return conn->err;
257 }
258
259 /**
260  * Set a TCP netconn into listen mode
261  *
262  * @param conn the tcp netconn to set to listen mode
263  * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
264  * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
265  *         don't return any error (yet?))
266  */
267 err_t
268 netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
269 {
270   struct api_msg msg;
271
272   /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
273   LWIP_UNUSED_ARG(backlog);
274
275   LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
276
277   msg.function = do_listen;
278   msg.msg.conn = conn;
279 #if TCP_LISTEN_BACKLOG
280   msg.msg.msg.lb.backlog = backlog;
281 #endif /* TCP_LISTEN_BACKLOG */
282   TCPIP_APIMSG(&msg);
283   return conn->err;
284 }
285
286 /**
287  * Accept a new connection on a TCP listening netconn.
288  *
289  * @param conn the TCP listen netconn
290  * @return the newly accepted netconn or NULL on timeout
291  */
292 struct netconn *
293 netconn_accept(struct netconn *conn)
294 {
295   struct netconn *newconn;
296
297   LWIP_ERROR("netconn_accept: invalid conn",       (conn != NULL),                      return NULL;);
298   LWIP_ERROR("netconn_accept: invalid acceptmbox", (conn->acceptmbox != SYS_MBOX_NULL), return NULL;);
299
300 #if LWIP_SO_RCVTIMEO
301   if (sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
302     newconn = NULL;
303   } else
304 #else
305   sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, 0);
306 #endif /* LWIP_SO_RCVTIMEO*/
307   {
308     /* Register event with callback */
309     API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
310
311 #if TCP_LISTEN_BACKLOG
312     if (newconn != NULL) {
313       /* Let the stack know that we have accepted the connection. */
314       struct api_msg msg;
315       msg.function = do_recv;
316       msg.msg.conn = conn;
317       TCPIP_APIMSG(&msg);
318     }
319 #endif /* TCP_LISTEN_BACKLOG */
320   }
321
322   return newconn;
323 }
324
325 /**
326  * Receive data (in form of a netbuf containing a packet buffer) from a netconn
327  *
328  * @param conn the netconn from which to receive data
329  * @return a new netbuf containing received data or NULL on memory error or timeout
330  */
331 struct netbuf *
332 netconn_recv(struct netconn *conn)
333 {
334   struct api_msg msg;
335   struct netbuf *buf = NULL;
336   struct pbuf *p;
337   u16_t len;
338
339   LWIP_ERROR("netconn_recv: invalid conn",  (conn != NULL), return NULL;);
340
341   if (conn->recvmbox == SYS_MBOX_NULL) {
342     /* @todo: should calling netconn_recv on a TCP listen conn be fatal (ERR_CONN)?? */
343     /* TCP listen conns don't have a recvmbox! */
344     conn->err = ERR_CONN;
345     return NULL;
346   }
347
348   if (ERR_IS_FATAL(conn->err)) {
349     return NULL;
350   }
351
352   if (conn->type == NETCONN_TCP) {
353 #if LWIP_TCP
354     if (conn->state == NETCONN_LISTEN) {
355       /* @todo: should calling netconn_recv on a TCP listen conn be fatal?? */
356       conn->err = ERR_CONN;
357       return NULL;
358     }
359
360     buf = memp_malloc(MEMP_NETBUF);
361
362     if (buf == NULL) {
363       conn->err = ERR_MEM;
364       return NULL;
365     }
366
367 #if LWIP_SO_RCVTIMEO
368     if (sys_arch_mbox_fetch(conn->recvmbox, (void *)&p, conn->recv_timeout)==SYS_ARCH_TIMEOUT) {
369       conn->err = ERR_TIMEOUT;
370       p = NULL;
371     }
372 #else
373     sys_arch_mbox_fetch(conn->recvmbox, (void *)&p, 0);
374 #endif /* LWIP_SO_RCVTIMEO*/
375
376     if (p != NULL) {
377       len = p->tot_len;
378       SYS_ARCH_DEC(conn->recv_avail, len);
379     } else {
380       len = 0;
381     }
382
383     /* Register event with callback */
384     API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
385
386     /* If we are closed, we indicate that we no longer wish to use the socket */
387     if (p == NULL) {
388       memp_free(MEMP_NETBUF, buf);
389       /* Avoid to lose any previous error code */
390       if (conn->err == ERR_OK) {
391         conn->err = ERR_CLSD;
392       }
393       return NULL;
394     }
395
396     buf->p = p;
397     buf->ptr = p;
398     buf->port = 0;
399     buf->addr = NULL;
400
401     /* Let the stack know that we have taken the data. */
402     msg.function = do_recv;
403     msg.msg.conn = conn;
404     if (buf != NULL) {
405       msg.msg.msg.r.len = buf->p->tot_len;
406     } else {
407       msg.msg.msg.r.len = 1;
408     }
409     TCPIP_APIMSG(&msg);
410 #endif /* LWIP_TCP */
411   } else {
412 #if (LWIP_UDP || LWIP_RAW)
413 #if LWIP_SO_RCVTIMEO
414     if (sys_arch_mbox_fetch(conn->recvmbox, (void *)&buf, conn->recv_timeout)==SYS_ARCH_TIMEOUT) {
415       buf = NULL;
416     }
417 #else
418     sys_arch_mbox_fetch(conn->recvmbox, (void *)&buf, 0);
419 #endif /* LWIP_SO_RCVTIMEO*/
420     if (buf!=NULL) {
421       SYS_ARCH_DEC(conn->recv_avail, buf->p->tot_len);
422       /* Register event with callback */
423       API_EVENT(conn, NETCONN_EVT_RCVMINUS, buf->p->tot_len);
424     }
425 #endif /* (LWIP_UDP || LWIP_RAW) */
426   }
427
428   LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv: received %p (err %d)\n", (void *)buf, conn->err));
429
430   return buf;
431 }
432
433 /**
434  * Send data (in form of a netbuf) to a specific remote IP address and port.
435  * Only to be used for UDP and RAW netconns (not TCP).
436  *
437  * @param conn the netconn over which to send data
438  * @param buf a netbuf containing the data to send
439  * @param addr the remote IP address to which to send the data
440  * @param port the remote port to which to send the data
441  * @return ERR_OK if data was sent, any other err_t on error
442  */
443 err_t
444 netconn_sendto(struct netconn *conn, struct netbuf *buf, struct ip_addr *addr, u16_t port)
445 {
446   if (buf != NULL) {
447     buf->addr = addr;
448     buf->port = port;
449     return netconn_send(conn, buf);
450   }
451   return ERR_VAL;
452 }
453
454 /**
455  * Send data over a UDP or RAW netconn (that is already connected).
456  *
457  * @param conn the UDP or RAW netconn over which to send data
458  * @param buf a netbuf containing the data to send
459  * @return ERR_OK if data was sent, any other err_t on error
460  */
461 err_t
462 netconn_send(struct netconn *conn, struct netbuf *buf)
463 {
464   struct api_msg msg;
465
466   LWIP_ERROR("netconn_send: invalid conn",  (conn != NULL), return ERR_ARG;);
467
468   LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %d bytes\n", buf->p->tot_len));
469   msg.function = do_send;
470   msg.msg.conn = conn;
471   msg.msg.msg.b = buf;
472   TCPIP_APIMSG(&msg);
473   return conn->err;
474 }
475
476 /**
477  * Send data over a TCP netconn.
478  *
479  * @param conn the TCP netconn over which to send data
480  * @param dataptr pointer to the application buffer that contains the data to send
481  * @param size size of the application data to send
482  * @param apiflags combination of following flags :
483  * - NETCONN_COPY (0x01) data will be copied into memory belonging to the stack
484  * - NETCONN_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent
485  * @return ERR_OK if data was sent, any other err_t on error
486  */
487 err_t
488 netconn_write(struct netconn *conn, const void *dataptr, int size, u8_t apiflags)
489 {
490   struct api_msg msg;
491
492   LWIP_ERROR("netconn_write: invalid conn",  (conn != NULL), return ERR_ARG;);
493   LWIP_ERROR("netconn_write: invalid conn->type",  (conn->type == NETCONN_TCP), return ERR_VAL;);
494
495   msg.function = do_write;
496   msg.msg.conn = conn;
497   msg.msg.msg.w.dataptr = dataptr;
498   msg.msg.msg.w.apiflags = apiflags;
499   msg.msg.msg.w.len = size;
500   /* For locking the core: this _can_ be delayed on low memory/low send buffer,
501      but if it is, this is done inside api_msg.c:do_write(), so we can use the
502      non-blocking version here. */
503   TCPIP_APIMSG(&msg);
504   return conn->err;
505 }
506
507 /**
508  * Close a TCP netconn (doesn't delete it).
509  *
510  * @param conn the TCP netconn to close
511  * @return ERR_OK if the netconn was closed, any other err_t on error
512  */
513 err_t
514 netconn_close(struct netconn *conn)
515 {
516   struct api_msg msg;
517
518   LWIP_ERROR("netconn_close: invalid conn",  (conn != NULL), return ERR_ARG;);
519
520   msg.function = do_close;
521   msg.msg.conn = conn;
522   tcpip_apimsg(&msg);
523   return conn->err;
524 }
525
526 #if LWIP_IGMP
527 /**
528  * Join multicast groups for UDP netconns.
529  *
530  * @param conn the UDP netconn for which to change multicast addresses
531  * @param multiaddr IP address of the multicast group to join or leave
532  * @param interface the IP address of the network interface on which to send
533  *                  the igmp message
534  * @param join_or_leave flag whether to send a join- or leave-message
535  * @return ERR_OK if the action was taken, any err_t on error
536  */
537 err_t
538 netconn_join_leave_group(struct netconn *conn,
539                          struct ip_addr *multiaddr,
540                          struct ip_addr *interface,
541                          enum netconn_igmp join_or_leave)
542 {
543   struct api_msg msg;
544
545   LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
546
547   msg.function = do_join_leave_group;
548   msg.msg.conn = conn;
549   msg.msg.msg.jl.multiaddr = multiaddr;
550   msg.msg.msg.jl.interface = interface;
551   msg.msg.msg.jl.join_or_leave = join_or_leave;
552   TCPIP_APIMSG(&msg);
553   return conn->err;
554 }
555 #endif /* LWIP_IGMP */
556
557 #if LWIP_DNS
558 /**
559  * Execute a DNS query, only one IP address is returned
560  *
561  * @param name a string representation of the DNS host name to query
562  * @param addr a preallocated struct ip_addr where to store the resolved IP address
563  * @return ERR_OK: resolving succeeded
564  *         ERR_MEM: memory error, try again later
565  *         ERR_ARG: dns client not initialized or invalid hostname
566  *         ERR_VAL: dns server response was invalid
567  */
568 err_t
569 netconn_gethostbyname(const char *name, struct ip_addr *addr)
570 {
571   struct dns_api_msg msg;
572   err_t err;
573   sys_sem_t sem;
574
575   LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
576   LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
577
578   sem = sys_sem_new(0);
579   if (sem == SYS_SEM_NULL) {
580     return ERR_MEM;
581   }
582
583   msg.name = name;
584   msg.addr = addr;
585   msg.err = &err;
586   msg.sem = sem;
587
588   tcpip_callback(do_gethostbyname, &msg);
589   sys_sem_wait(sem);
590   sys_sem_free(sem);
591
592   return err;
593 }
594 #endif /* LWIP_DNS*/
595
596 #endif /* LWIP_NETCONN */