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