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