• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file
3  * Sequential API Main thread 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 !NO_SYS /* don't build if not configured for use in lwipopts.h */
42 
43 #include "lwip/priv/tcpip_priv.h"
44 #include "lwip/sys.h"
45 #include "lwip/memp.h"
46 #include "lwip/mem.h"
47 #include "lwip/init.h"
48 #include "lwip/ip.h"
49 #include "lwip/pbuf.h"
50 #include "lwip/etharp.h"
51 #include "netif/ethernet.h"
52 #if LWIP_LOWPOWER
53 #include "lwip/lowpower.h"
54 #endif
55 
56 #define TCPIP_MSG_VAR_REF(name)     API_VAR_REF(name)
57 #define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name)
58 #define TCPIP_MSG_VAR_ALLOC(name)   API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM)
59 #define TCPIP_MSG_VAR_FREE(name)    API_VAR_FREE(MEMP_TCPIP_MSG_API, name)
60 
61 /* global variables */
62 static tcpip_init_done_fn tcpip_init_done;
63 static void *tcpip_init_done_arg;
64 static sys_mbox_t tcpip_mbox;
65 
66 #if LWIP_TCPIP_CORE_LOCKING
67 /** The global semaphore to lock the stack. */
68 sys_mutex_t lock_tcpip_core;
69 #endif /* LWIP_TCPIP_CORE_LOCKING */
70 
71 static void tcpip_thread_handle_msg(struct tcpip_msg *msg);
72 
73 #if !LWIP_TIMERS
74 /* wait for a message with timers disabled (e.g. pass a timer-check trigger into tcpip_thread) */
75 #define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg)
76 #else /* !LWIP_TIMERS */
77 /* wait for a message, timeouts are processed while waiting */
78 #define TCPIP_MBOX_FETCH(mbox, msg) tcpip_timeouts_mbox_fetch(mbox, msg)
79 #if !LWIP_LOWPOWER
80 /**
81  * Wait (forever) for a message to arrive in an mbox.
82  * While waiting, timeouts are processed.
83  *
84  * @param mbox the mbox to fetch the message from
85  * @param msg the place to store the message
86  */
87 static void
tcpip_timeouts_mbox_fetch(sys_mbox_t * mbox,void ** msg)88 tcpip_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
89 {
90   u32_t sleeptime, res;
91 
92 again:
93   LWIP_ASSERT_CORE_LOCKED();
94 
95   sleeptime = sys_timeouts_sleeptime();
96   if (sleeptime == SYS_TIMEOUTS_SLEEPTIME_INFINITE) {
97     UNLOCK_TCPIP_CORE();
98     sys_arch_mbox_fetch(mbox, msg, 0);
99     LOCK_TCPIP_CORE();
100     return;
101   } else if (sleeptime == 0) {
102     sys_check_timeouts();
103     /* We try again to fetch a message from the mbox. */
104     goto again;
105   }
106 
107   UNLOCK_TCPIP_CORE();
108   res = sys_arch_mbox_fetch(mbox, msg, sleeptime);
109   LOCK_TCPIP_CORE();
110   if (res == SYS_ARCH_TIMEOUT) {
111     /* If a SYS_ARCH_TIMEOUT value is returned, a timeout occurred
112        before a message could be fetched. */
113     sys_check_timeouts();
114     /* We try again to fetch a message from the mbox. */
115     goto again;
116   }
117 }
118 #endif /* !LWIP_LOWPOWER */
119 #endif /* !LWIP_TIMERS */
120 
121 /**
122  * The main lwIP thread. This thread has exclusive access to lwIP core functions
123  * (unless access to them is not locked). Other threads communicate with this
124  * thread using message boxes.
125  *
126  * It also starts all the timers to make sure they are running in the right
127  * thread context.
128  *
129  * @param arg unused argument
130  */
131 static void
tcpip_thread(void * arg)132 tcpip_thread(void *arg)
133 {
134   struct tcpip_msg *msg;
135   LWIP_UNUSED_ARG(arg);
136 
137   LWIP_MARK_TCPIP_THREAD();
138 
139   LOCK_TCPIP_CORE();
140   if (tcpip_init_done != NULL) {
141     tcpip_init_done(tcpip_init_done_arg);
142   }
143 
144   while (1) {                          /* MAIN Loop */
145     LWIP_TCPIP_THREAD_ALIVE();
146     /* wait for a message, timeouts are processed while waiting */
147     TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg);
148     if (msg == NULL) {
149       LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
150       LWIP_ASSERT("tcpip_thread: invalid message", 0);
151       continue;
152     }
153     tcpip_thread_handle_msg(msg);
154   }
155 }
156 
157 /* Handle a single tcpip_msg
158  * This is in its own function for access by tests only.
159  */
160 static void
tcpip_thread_handle_msg(struct tcpip_msg * msg)161 tcpip_thread_handle_msg(struct tcpip_msg *msg)
162 {
163   switch (msg->type) {
164 #if !LWIP_TCPIP_CORE_LOCKING
165     case TCPIP_MSG_API:
166       LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
167       msg->msg.api_msg.function(msg->msg.api_msg.msg);
168       break;
169     case TCPIP_MSG_API_CALL:
170       LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
171       msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
172       sys_sem_signal(msg->msg.api_call.sem);
173       break;
174 #endif /* !LWIP_TCPIP_CORE_LOCKING */
175 
176 #if !LWIP_TCPIP_CORE_LOCKING_INPUT
177     case TCPIP_MSG_INPKT:
178       LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
179       if (msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif) != ERR_OK) {
180         pbuf_free(msg->msg.inp.p);
181       }
182       memp_free(MEMP_TCPIP_MSG_INPKT, msg);
183       break;
184 #endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
185 
186 #if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
187     case TCPIP_MSG_TIMEOUT:
188       LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
189       sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
190       memp_free(MEMP_TCPIP_MSG_API, msg);
191       break;
192     case TCPIP_MSG_UNTIMEOUT:
193       LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
194       sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
195       memp_free(MEMP_TCPIP_MSG_API, msg);
196       break;
197 #endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
198 
199     case TCPIP_MSG_CALLBACK:
200       LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
201       msg->msg.cb.function(msg->msg.cb.ctx);
202       memp_free(MEMP_TCPIP_MSG_API, msg);
203       break;
204 
205     case TCPIP_MSG_CALLBACK_STATIC:
206       LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
207       msg->msg.cb.function(msg->msg.cb.ctx);
208       break;
209 #if LWIP_LOWPOWER
210     /* just wake up thread do nothing */
211     case TCPIP_MSG_NA:
212       if (msg->msg.lowpower.type == LOW_BLOCK) {
213         LOWPOWER_SIGNAL(msg->msg.lowpower.wait_up);
214       } else {
215         memp_free(MEMP_TCPIP_MSG_LOWPOWER, msg);
216       }
217       sys_timeout_set_wake_time(LOW_TMR_DELAY);
218       break;
219 #endif
220     default:
221       LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
222       LWIP_ASSERT("tcpip_thread: invalid message", 0);
223       break;
224   }
225 }
226 
227 #if LWIP_LOWPOWER
228 /* send a na msg to wake up tcpip_thread */
229 void
tcpip_send_msg_na(enum lowpower_msg_type type)230 tcpip_send_msg_na(enum lowpower_msg_type type)
231 {
232   struct tcpip_msg *msg = NULL;
233   err_t val;
234 
235   /* is not used lowpower mode */
236   if ((type != LOW_FORCE_NON_BLOCK) && (get_lowpowper_mod() == LOW_TMR_NORMAL_MOD)) {
237     return;
238   }
239   if (sys_timeout_waiting_long() == 0) {
240     return;
241   }
242 
243   msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_LOWPOWER);
244   if (msg == NULL) {
245     LWIP_DEBUGF(LOWPOWER_DEBUG, ("tcpip_send_msg_na alloc faild\n"));
246     return;
247   }
248 
249   /* just wake up thread if nonblock */
250   msg->type = TCPIP_MSG_NA;
251   msg->msg.lowpower.type = type;
252 
253   if (type == LOW_BLOCK) {
254     LOWPOWER_SEM_NEW(msg->msg.lowpower.wait_up, val);
255     if (val != ERR_OK) {
256       LWIP_DEBUGF(LOWPOWER_DEBUG, ("alloc sem faild\n"));
257       memp_free(MEMP_TCPIP_MSG_LOWPOWER, msg);
258       return;
259     }
260   }
261 
262   if (sys_mbox_trypost(&tcpip_mbox, msg) != ERR_OK) {
263     if (type == LOW_BLOCK) {
264       LOWPOWER_SEM_FREE(msg->msg.lowpower.wait_up);
265     }
266     memp_free(MEMP_TCPIP_MSG_LOWPOWER, msg);
267     LWIP_DEBUGF(LOWPOWER_DEBUG, ("tcpip_send_msg_na post faild\n"));
268     return;
269   }
270 
271   if (type == LOW_BLOCK) {
272     LOWPOWER_SEM_WAIT(msg->msg.lowpower.wait_up);
273     LOWPOWER_SEM_FREE(msg->msg.lowpower.wait_up);
274     memp_free(MEMP_TCPIP_MSG_LOWPOWER, msg);
275   }
276 }
277 #endif /* LWIP_LOWPOWER */
278 
279 #ifdef TCPIP_THREAD_TEST
280 /** Work on queued items in single-threaded test mode */
281 int
tcpip_thread_poll_one(void)282 tcpip_thread_poll_one(void)
283 {
284   int ret = 0;
285   struct tcpip_msg *msg;
286 
287   if (sys_arch_mbox_tryfetch(&tcpip_mbox, (void **)&msg) != SYS_MBOX_EMPTY) {
288     LOCK_TCPIP_CORE();
289     if (msg != NULL) {
290       tcpip_thread_handle_msg(msg);
291       ret = 1;
292     }
293     UNLOCK_TCPIP_CORE();
294   }
295   return ret;
296 }
297 #endif
298 
299 /**
300  * Pass a received packet to tcpip_thread for input processing
301  *
302  * @param p the received packet
303  * @param inp the network interface on which the packet was received
304  * @param input_fn input function to call
305  */
306 err_t
tcpip_inpkt(struct pbuf * p,struct netif * inp,netif_input_fn input_fn)307 tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
308 {
309 #if LWIP_TCPIP_CORE_LOCKING_INPUT
310   err_t ret;
311   LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
312 #if LWIP_LOWPOWER
313   tcpip_send_msg_na(LOW_BLOCK);
314 #endif
315   LOCK_TCPIP_CORE();
316   ret = input_fn(p, inp);
317   UNLOCK_TCPIP_CORE();
318   return ret;
319 #else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
320   struct tcpip_msg *msg;
321 
322   LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
323 
324   msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
325   if (msg == NULL) {
326     return ERR_MEM;
327   }
328 
329   msg->type = TCPIP_MSG_INPKT;
330   msg->msg.inp.p = p;
331   msg->msg.inp.netif = inp;
332   msg->msg.inp.input_fn = input_fn;
333   if (sys_mbox_trypost(&tcpip_mbox, msg) != ERR_OK) {
334     memp_free(MEMP_TCPIP_MSG_INPKT, msg);
335     return ERR_MEM;
336   }
337   return ERR_OK;
338 #endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
339 }
340 
341 /**
342  * @ingroup lwip_os
343  * Pass a received packet to tcpip_thread for input processing with
344  * ethernet_input or ip_input. Don't call directly, pass to netif_add()
345  * and call netif->input().
346  *
347  * @param p the received packet, p->payload pointing to the Ethernet header or
348  *          to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
349  *          NETIF_FLAG_ETHERNET flags)
350  * @param inp the network interface on which the packet was received
351  */
352 err_t
tcpip_input(struct pbuf * p,struct netif * inp)353 tcpip_input(struct pbuf *p, struct netif *inp)
354 {
355 #if LWIP_ETHERNET
356   if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
357     return tcpip_inpkt(p, inp, ethernet_input);
358   } else
359 #endif /* LWIP_ETHERNET */
360     return tcpip_inpkt(p, inp, ip_input);
361 }
362 
363 /**
364  * @ingroup lwip_os
365  * Call a specific function in the thread context of
366  * tcpip_thread for easy access synchronization.
367  * A function called in that way may access lwIP core code
368  * without fearing concurrent access.
369  * Blocks until the request is posted.
370  * Must not be called from interrupt context!
371  *
372  * @param function the function to call
373  * @param ctx parameter passed to f
374  * @return ERR_OK if the function was called, another err_t if not
375  *
376  * @see tcpip_try_callback
377  */
378 err_t
tcpip_callback(tcpip_callback_fn function,void * ctx)379 tcpip_callback(tcpip_callback_fn function, void *ctx)
380 {
381   struct tcpip_msg *msg;
382 
383   LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
384 
385   msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
386   if (msg == NULL) {
387     return ERR_MEM;
388   }
389 
390   msg->type = TCPIP_MSG_CALLBACK;
391   msg->msg.cb.function = function;
392   msg->msg.cb.ctx = ctx;
393 
394   sys_mbox_post(&tcpip_mbox, msg);
395   return ERR_OK;
396 }
397 
398 /**
399  * @ingroup lwip_os
400  * Call a specific function in the thread context of
401  * tcpip_thread for easy access synchronization.
402  * A function called in that way may access lwIP core code
403  * without fearing concurrent access.
404  * Does NOT block when the request cannot be posted because the
405  * tcpip_mbox is full, but returns ERR_MEM instead.
406  * Can be called from interrupt context.
407  *
408  * @param function the function to call
409  * @param ctx parameter passed to f
410  * @return ERR_OK if the function was called, another err_t if not
411  *
412  * @see tcpip_callback
413  */
414 err_t
tcpip_try_callback(tcpip_callback_fn function,void * ctx)415 tcpip_try_callback(tcpip_callback_fn function, void *ctx)
416 {
417   struct tcpip_msg *msg;
418 
419   LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
420 
421   msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
422   if (msg == NULL) {
423     return ERR_MEM;
424   }
425 
426   msg->type = TCPIP_MSG_CALLBACK;
427   msg->msg.cb.function = function;
428   msg->msg.cb.ctx = ctx;
429 
430   if (sys_mbox_trypost(&tcpip_mbox, msg) != ERR_OK) {
431     memp_free(MEMP_TCPIP_MSG_API, msg);
432     return ERR_MEM;
433   }
434   return ERR_OK;
435 }
436 
437 #if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
438 /**
439  * call sys_timeout in tcpip_thread
440  *
441  * @param msecs time in milliseconds for timeout
442  * @param h function to be called on timeout
443  * @param arg argument to pass to timeout function h
444  * @return ERR_MEM on memory error, ERR_OK otherwise
445  */
446 err_t
tcpip_timeout(u32_t msecs,sys_timeout_handler h,void * arg)447 tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
448 {
449   struct tcpip_msg *msg;
450 
451   LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
452 
453   msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
454   if (msg == NULL) {
455     return ERR_MEM;
456   }
457 
458   msg->type = TCPIP_MSG_TIMEOUT;
459   msg->msg.tmo.msecs = msecs;
460   msg->msg.tmo.h = h;
461   msg->msg.tmo.arg = arg;
462   sys_mbox_post(&tcpip_mbox, msg);
463   return ERR_OK;
464 }
465 
466 /**
467  * call sys_untimeout in tcpip_thread
468  *
469  * @param h function to be called on timeout
470  * @param arg argument to pass to timeout function h
471  * @return ERR_MEM on memory error, ERR_OK otherwise
472  */
473 err_t
tcpip_untimeout(sys_timeout_handler h,void * arg)474 tcpip_untimeout(sys_timeout_handler h, void *arg)
475 {
476   struct tcpip_msg *msg;
477 
478   LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
479 
480   msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
481   if (msg == NULL) {
482     return ERR_MEM;
483   }
484 
485   msg->type = TCPIP_MSG_UNTIMEOUT;
486   msg->msg.tmo.h = h;
487   msg->msg.tmo.arg = arg;
488   sys_mbox_post(&tcpip_mbox, msg);
489   return ERR_OK;
490 }
491 #endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
492 
493 
494 /**
495  * Sends a message to TCPIP thread to call a function. Caller thread blocks on
496  * on a provided semaphore, which ist NOT automatically signalled by TCPIP thread,
497  * this has to be done by the user.
498  * It is recommended to use LWIP_TCPIP_CORE_LOCKING since this is the way
499  * with least runtime overhead.
500  *
501  * @param fn function to be called from TCPIP thread
502  * @param apimsg argument to API function
503  * @param sem semaphore to wait on
504  * @return ERR_OK if the function was called, another err_t if not
505  */
506 err_t
tcpip_send_msg_wait_sem(tcpip_callback_fn fn,void * apimsg,sys_sem_t * sem)507 tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t *sem)
508 {
509 #if LWIP_TCPIP_CORE_LOCKING
510   LWIP_UNUSED_ARG(sem);
511 #if LWIP_LOWPOWER
512   tcpip_send_msg_na(LOW_BLOCK);
513 #endif
514   LOCK_TCPIP_CORE();
515   fn(apimsg);
516   UNLOCK_TCPIP_CORE();
517   return ERR_OK;
518 #else /* LWIP_TCPIP_CORE_LOCKING */
519   TCPIP_MSG_VAR_DECLARE(msg);
520 
521   LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
522   LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
523 
524   TCPIP_MSG_VAR_ALLOC(msg);
525   TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;
526   TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;
527   TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg;
528   sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg));
529   sys_arch_sem_wait(sem, 0);
530   TCPIP_MSG_VAR_FREE(msg);
531   return ERR_OK;
532 #endif /* LWIP_TCPIP_CORE_LOCKING */
533 }
534 
535 /**
536  * Synchronously calls function in TCPIP thread and waits for its completion.
537  * It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or
538  * LWIP_NETCONN_SEM_PER_THREAD.
539  * If not, a semaphore is created and destroyed on every call which is usually
540  * an expensive/slow operation.
541  * @param fn Function to call
542  * @param call Call parameters
543  * @return Return value from tcpip_api_call_fn
544  */
545 err_t
tcpip_api_call(tcpip_api_call_fn fn,struct tcpip_api_call_data * call)546 tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call)
547 {
548 #if LWIP_TCPIP_CORE_LOCKING
549   err_t err;
550 #if LWIP_LOWPOWER
551   tcpip_send_msg_na(LOW_BLOCK);
552 #endif
553   LOCK_TCPIP_CORE();
554   err = fn(call);
555   UNLOCK_TCPIP_CORE();
556   return err;
557 #else /* LWIP_TCPIP_CORE_LOCKING */
558   TCPIP_MSG_VAR_DECLARE(msg);
559 
560 #if !LWIP_NETCONN_SEM_PER_THREAD
561   err_t err = sys_sem_new(&call->sem, 0);
562   if (err != ERR_OK) {
563     return err;
564   }
565 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
566 
567   LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
568 
569   TCPIP_MSG_VAR_ALLOC(msg);
570   TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API_CALL;
571   TCPIP_MSG_VAR_REF(msg).msg.api_call.arg = call;
572   TCPIP_MSG_VAR_REF(msg).msg.api_call.function = fn;
573 #if LWIP_NETCONN_SEM_PER_THREAD
574   TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = LWIP_NETCONN_THREAD_SEM_GET();
575 #else /* LWIP_NETCONN_SEM_PER_THREAD */
576   TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = &call->sem;
577 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
578   sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg));
579   sys_arch_sem_wait(TCPIP_MSG_VAR_REF(msg).msg.api_call.sem, 0);
580   TCPIP_MSG_VAR_FREE(msg);
581 
582 #if !LWIP_NETCONN_SEM_PER_THREAD
583   sys_sem_free(&call->sem);
584 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
585 
586   return call->err;
587 #endif /* LWIP_TCPIP_CORE_LOCKING */
588 }
589 
590 /**
591  * @ingroup lwip_os
592  * Allocate a structure for a static callback message and initialize it.
593  * The message has a special type such that lwIP never frees it.
594  * This is intended to be used to send "static" messages from interrupt context,
595  * e.g. the message is allocated once and posted several times from an IRQ
596  * using tcpip_callbackmsg_trycallback().
597  * Example usage: Trigger execution of an ethernet IRQ DPC routine in lwIP thread context.
598  *
599  * @param function the function to call
600  * @param ctx parameter passed to function
601  * @return a struct pointer to pass to tcpip_callbackmsg_trycallback().
602  *
603  * @see tcpip_callbackmsg_trycallback()
604  * @see tcpip_callbackmsg_delete()
605  */
606 struct tcpip_callback_msg *
tcpip_callbackmsg_new(tcpip_callback_fn function,void * ctx)607 tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
608 {
609   struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
610   if (msg == NULL) {
611     return NULL;
612   }
613   msg->type = TCPIP_MSG_CALLBACK_STATIC;
614   msg->msg.cb.function = function;
615   msg->msg.cb.ctx = ctx;
616   return (struct tcpip_callback_msg *)msg;
617 }
618 
619 /**
620  * @ingroup lwip_os
621  * Free a callback message allocated by tcpip_callbackmsg_new().
622  *
623  * @param msg the message to free
624  *
625  * @see tcpip_callbackmsg_new()
626  */
627 void
tcpip_callbackmsg_delete(struct tcpip_callback_msg * msg)628 tcpip_callbackmsg_delete(struct tcpip_callback_msg *msg)
629 {
630   memp_free(MEMP_TCPIP_MSG_API, msg);
631 }
632 
633 /**
634  * @ingroup lwip_os
635  * Try to post a callback-message to the tcpip_thread tcpip_mbox.
636  *
637  * @param msg pointer to the message to post
638  * @return sys_mbox_trypost() return code
639  *
640  * @see tcpip_callbackmsg_new()
641  */
642 err_t
tcpip_callbackmsg_trycallback(struct tcpip_callback_msg * msg)643 tcpip_callbackmsg_trycallback(struct tcpip_callback_msg *msg)
644 {
645   LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
646   return sys_mbox_trypost(&tcpip_mbox, msg);
647 }
648 
649 /**
650  * @ingroup lwip_os
651  * Try to post a callback-message to the tcpip_thread mbox.
652  * Same as @ref tcpip_callbackmsg_trycallback but calls sys_mbox_trypost_fromisr(),
653  * mainly to help FreeRTOS, where calls differ between task level and ISR level.
654  *
655  * @param msg pointer to the message to post
656  * @return sys_mbox_trypost_fromisr() return code (without change, so this
657  *         knowledge can be used to e.g. propagate "bool needs_scheduling")
658  *
659  * @see tcpip_callbackmsg_new()
660  */
661 err_t
tcpip_callbackmsg_trycallback_fromisr(struct tcpip_callback_msg * msg)662 tcpip_callbackmsg_trycallback_fromisr(struct tcpip_callback_msg *msg)
663 {
664   LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
665   return sys_mbox_trypost_fromisr(&tcpip_mbox, msg);
666 }
667 
668 /**
669  * @ingroup lwip_os
670  * Initialize this module:
671  * - initialize all sub modules
672  * - start the tcpip_thread
673  *
674  * @param initfunc a function to call when tcpip_thread is running and finished initializing
675  * @param arg argument to pass to initfunc
676  */
677 void
tcpip_init(tcpip_init_done_fn initfunc,void * arg)678 tcpip_init(tcpip_init_done_fn initfunc, void *arg)
679 {
680   lwip_init();
681 
682   tcpip_init_done = initfunc;
683   tcpip_init_done_arg = arg;
684   if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
685     LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
686   }
687 #if LWIP_TCPIP_CORE_LOCKING
688   if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
689     LWIP_ASSERT("failed to create lock_tcpip_core", 0);
690   }
691 #endif /* LWIP_TCPIP_CORE_LOCKING */
692 
693   sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
694 }
695 
696 /**
697  * Simple callback function used with tcpip_callback to free a pbuf
698  * (pbuf_free has a wrong signature for tcpip_callback)
699  *
700  * @param p The pbuf (chain) to be dereferenced.
701  */
702 static void
pbuf_free_int(void * p)703 pbuf_free_int(void *p)
704 {
705   struct pbuf *q = (struct pbuf *)p;
706   pbuf_free(q);
707 }
708 
709 /**
710  * A simple wrapper function that allows you to free a pbuf from interrupt context.
711  *
712  * @param p The pbuf (chain) to be dereferenced.
713  * @return ERR_OK if callback could be enqueued, an err_t if not
714  */
715 err_t
pbuf_free_callback(struct pbuf * p)716 pbuf_free_callback(struct pbuf *p)
717 {
718   return tcpip_try_callback(pbuf_free_int, p);
719 }
720 
721 /**
722  * A simple wrapper function that allows you to free heap memory from
723  * interrupt context.
724  *
725  * @param m the heap memory to free
726  * @return ERR_OK if callback could be enqueued, an err_t if not
727  */
728 err_t
mem_free_callback(void * m)729 mem_free_callback(void *m)
730 {
731   return tcpip_try_callback(mem_free, m);
732 }
733 
734 #endif /* !NO_SYS */
735