1 /*
2  * Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include "pico/async_context.h"
8 
9 #include <lwip/init.h>
10 #include "lwip/timeouts.h"
11 
12 static void update_next_timeout(async_context_t *context, async_when_pending_worker_t *worker);
13 static void lwip_timeout_reached(async_context_t *context, async_at_time_worker_t *worker);
14 
15 static async_when_pending_worker_t always_pending_update_timeout_worker = {
16         .do_work = update_next_timeout
17 };
18 
19 static async_at_time_worker_t lwip_timeout_worker = {
20         .do_work = lwip_timeout_reached,
21 };
22 
lwip_timeout_reached(__unused async_context_t * context,__unused async_at_time_worker_t * worker)23 static void lwip_timeout_reached(__unused async_context_t *context, __unused async_at_time_worker_t *worker) {
24     assert(worker == &lwip_timeout_worker);
25     sys_check_timeouts();
26 }
27 
update_next_timeout(async_context_t * context,async_when_pending_worker_t * worker)28 static void update_next_timeout(async_context_t *context, async_when_pending_worker_t *worker) {
29     assert(worker == &always_pending_update_timeout_worker);
30     // we want to run on every execution of the helper to re-reflect any changes
31     // to the underlying lwIP timers which may have happened in the interim
32     // (note that worker will be called on every outermost exit of the async_context
33     // lock, and lwIP timers should not be modified whilst not holding the lock.
34     worker->work_pending = true;
35     uint32_t sleep_ms = sys_timeouts_sleeptime();
36     if (sleep_ms == SYS_TIMEOUTS_SLEEPTIME_INFINITE) {
37         lwip_timeout_worker.next_time = at_the_end_of_time;
38     } else {
39         lwip_timeout_worker.next_time = make_timeout_time_ms(sleep_ms);
40     }
41     async_context_add_at_time_worker(context, &lwip_timeout_worker);
42 }
43 
lwip_nosys_init(async_context_t * context)44 bool lwip_nosys_init(async_context_t *context) {
45     static bool done_lwip_init;
46     if (!done_lwip_init) {
47         lwip_init();
48         done_lwip_init = true;
49     }
50     // we want the worker to be called on every async helper run (starting with the next)
51     always_pending_update_timeout_worker.work_pending = true;
52     async_context_add_when_pending_worker(context, &always_pending_update_timeout_worker);
53     return true;
54 }
55 
lwip_nosys_deinit(async_context_t * context)56 void lwip_nosys_deinit(async_context_t *context) {
57     async_context_remove_at_time_worker(context, &lwip_timeout_worker);
58     async_context_remove_when_pending_worker(context, &always_pending_update_timeout_worker);
59 }
60 
61 #if NO_SYS
62 /* lwip has provision for using a mutex, when applicable */
sys_arch_protect(void)63 sys_prot_t sys_arch_protect(void) {
64     return 0;
65 }
66 
sys_arch_unprotect(__unused sys_prot_t pval)67 void sys_arch_unprotect(__unused sys_prot_t pval) {
68 }
69 
70 /* lwip needs a millisecond time source, and the TinyUSB board support code has one available */
sys_now(void)71 uint32_t sys_now(void) {
72     return to_ms_since_boot(get_absolute_time());
73 }
74 #endif