1 /*
2 * Event loop based on select() loop
3 * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8 /*
9 * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
10 *
11 * SPDX-License-Identifier: Apache-2.0
12 */
13
14 #include "includes.h"
15
16 #include "common.h"
17 #include "list.h"
18 #include "eloop.h"
19 #include "esp_wifi_driver.h"
20
21 struct eloop_timeout {
22 struct dl_list list;
23 struct os_reltime time;
24 void *eloop_data;
25 void *user_data;
26 eloop_timeout_handler handler;
27 #ifdef ELOOP_DEBUG
28 char func_name[100];
29 int line;
30 #endif
31 };
32
33 struct eloop_data {
34 struct dl_list timeout;
35 ETSTimer eloop_timer;
36 bool eloop_started;
37 };
38
39 #define ELOOP_LOCK() os_mutex_lock(eloop_data_lock)
40 #define ELOOP_UNLOCK() os_mutex_unlock(eloop_data_lock)
41
42 static void *eloop_data_lock = NULL;
43
44 static struct eloop_data eloop;
45
eloop_run_wrapper(void * data)46 static int eloop_run_wrapper(void *data)
47 {
48 eloop_run();
49 return 0;
50 }
51
eloop_run_timer(void * args)52 static void eloop_run_timer(void *args)
53 {
54 /* Execute timers in pptask context to make it thread safe */
55 wifi_ipc_config_t cfg;
56
57 cfg.fn = eloop_run_wrapper;
58 cfg.arg = NULL;
59 cfg.arg_size = 0;
60 esp_wifi_ipc_internal(&cfg, false);
61 }
62
eloop_init(void)63 int eloop_init(void)
64 {
65 os_memset(&eloop, 0, sizeof(eloop));
66 dl_list_init(&eloop.timeout);
67 os_timer_disarm(&eloop.eloop_timer);
68 os_timer_setfn(&eloop.eloop_timer, (ETSTimerFunc *)eloop_run_timer, NULL);
69
70 eloop_data_lock = os_recursive_mutex_create();
71
72 if (!eloop_data_lock) {
73 wpa_printf(MSG_ERROR, "failed to create eloop data loop");
74 return -1;
75 }
76 eloop.eloop_started = true;
77
78 return 0;
79 }
80
81 #ifdef ELOOP_DEBUG
eloop_register_timeout_debug(unsigned int secs,unsigned int usecs,eloop_timeout_handler handler,void * eloop_data,void * user_data,const char * func,int line)82 int eloop_register_timeout_debug(unsigned int secs, unsigned int usecs,
83 eloop_timeout_handler handler, void *eloop_data,
84 void *user_data, const char *func, int line)
85 #else
86 int eloop_register_timeout(unsigned int secs, unsigned int usecs,
87 eloop_timeout_handler handler,
88 void *eloop_data, void *user_data)
89 #endif
90 {
91 struct eloop_timeout *timeout, *tmp;
92 os_time_t now_sec;
93 #ifdef ELOOP_DEBUG
94 int count = 0;
95 #endif
96
97 timeout = os_zalloc(sizeof(*timeout));
98 if (timeout == NULL) {
99 return -1;
100 }
101 if (os_get_reltime(&timeout->time) < 0) {
102 os_free(timeout);
103 return -1;
104 }
105 now_sec = timeout->time.sec;
106 timeout->time.sec += secs;
107 if (timeout->time.sec < now_sec) {
108 goto overflow;
109 }
110 timeout->time.usec += usecs;
111 while (timeout->time.usec >= 1000000) {
112 timeout->time.sec++;
113 timeout->time.usec -= 1000000;
114 }
115 if (timeout->time.sec < now_sec) {
116 goto overflow;
117 }
118 timeout->eloop_data = eloop_data;
119 timeout->user_data = user_data;
120 timeout->handler = handler;
121 #ifdef ELOOP_DEBUG
122 os_strlcpy(timeout->func_name, func, 100);
123 timeout->line = line;
124 #endif
125
126 /* Maintain timeouts in order of increasing time */
127 dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
128 if (os_reltime_before(&timeout->time, &tmp->time)) {
129 ELOOP_LOCK();
130 dl_list_add(tmp->list.prev, &timeout->list);
131 ELOOP_UNLOCK();
132 goto run;
133 }
134 #ifdef ELOOP_DEBUG
135 count++;
136 #endif
137 }
138 ELOOP_LOCK();
139 dl_list_add_tail(&eloop.timeout, &timeout->list);
140 ELOOP_UNLOCK();
141
142 run:
143 #ifdef ELOOP_DEBUG
144 wpa_printf(MSG_DEBUG, "ELOOP: Added one timer from %s:%d to call %p, current order=%d",
145 timeout->func_name, line, timeout->handler, count);
146 #endif
147 ELOOP_LOCK();
148 os_timer_disarm(&eloop.eloop_timer);
149 os_timer_arm(&eloop.eloop_timer, 0, 0);
150 ELOOP_UNLOCK();
151
152 return 0;
153
154 overflow:
155 /*
156 * Integer overflow - assume long enough timeout to be assumed
157 * to be infinite, i.e., the timeout would never happen.
158 */
159 wpa_printf(MSG_DEBUG,
160 "ELOOP: Too long timeout (secs=%u usecs=%u) to ever happen - ignore it",
161 secs, usecs);
162 os_free(timeout);
163 return 0;
164 }
165
timeout_exists(struct eloop_timeout * old)166 static bool timeout_exists(struct eloop_timeout *old)
167 {
168 struct eloop_timeout *timeout, *prev;
169 dl_list_for_each_safe(timeout, prev, &eloop.timeout,
170 struct eloop_timeout, list) {
171 if (old == timeout) {
172 return true;
173 }
174 }
175
176 return false;
177 }
178
eloop_remove_timeout(struct eloop_timeout * timeout)179 static void eloop_remove_timeout(struct eloop_timeout *timeout)
180 {
181 bool timeout_present = false;
182 ELOOP_LOCK();
183 /* Make sure timeout still exists(Another context may have deleted this) */
184 timeout_present = timeout_exists(timeout);
185 if (timeout_present) {
186 dl_list_del(&timeout->list);
187 }
188 ELOOP_UNLOCK();
189 if (timeout_present) {
190 os_free(timeout);
191 }
192 }
193
194 #ifdef ELOOP_DEBUG
eloop_cancel_timeout_debug(eloop_timeout_handler handler,void * eloop_data,void * user_data,const char * func,int line)195 int eloop_cancel_timeout_debug(eloop_timeout_handler handler, void *eloop_data,
196 void *user_data, const char *func, int line)
197 #else
198 int eloop_cancel_timeout(eloop_timeout_handler handler,
199 void *eloop_data, void *user_data)
200 #endif
201 {
202 struct eloop_timeout *timeout, *prev;
203 int removed = 0;
204
205 dl_list_for_each_safe(timeout, prev, &eloop.timeout,
206 struct eloop_timeout, list) {
207 if (timeout->handler == handler &&
208 (timeout->eloop_data == eloop_data ||
209 eloop_data == ELOOP_ALL_CTX) &&
210 (timeout->user_data == user_data ||
211 user_data == ELOOP_ALL_CTX)) {
212 eloop_remove_timeout(timeout);
213 removed++;
214 }
215 }
216 #ifdef ELOOP_DEBUG
217 wpa_printf(MSG_DEBUG, "ELOOP: %s:%d called to remove timer handler=%p, removed count=%d",
218 func, line, handler, removed);
219 #endif
220
221 return removed;
222 }
223
eloop_cancel_timeout_one(eloop_timeout_handler handler,void * eloop_data,void * user_data,struct os_reltime * remaining)224 int eloop_cancel_timeout_one(eloop_timeout_handler handler,
225 void *eloop_data, void *user_data,
226 struct os_reltime *remaining)
227 {
228 struct eloop_timeout *timeout, *prev;
229 int removed = 0;
230 struct os_reltime now;
231
232 os_get_reltime(&now);
233 remaining->sec = remaining->usec = 0;
234
235 dl_list_for_each_safe(timeout, prev, &eloop.timeout,
236 struct eloop_timeout, list) {
237 if (timeout->handler == handler &&
238 (timeout->eloop_data == eloop_data) &&
239 (timeout->user_data == user_data)) {
240 removed = 1;
241 if (os_reltime_before(&now, &timeout->time)) {
242 os_reltime_sub(&timeout->time, &now, remaining);
243 }
244 eloop_remove_timeout(timeout);
245 break;
246 }
247 }
248 return removed;
249 }
250
eloop_is_timeout_registered(eloop_timeout_handler handler,void * eloop_data,void * user_data)251 int eloop_is_timeout_registered(eloop_timeout_handler handler,
252 void *eloop_data, void *user_data)
253 {
254 struct eloop_timeout *tmp;
255
256 dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
257 if (tmp->handler == handler &&
258 tmp->eloop_data == eloop_data &&
259 tmp->user_data == user_data) {
260 return 1;
261 }
262 }
263
264 return 0;
265 }
266
eloop_deplete_timeout(unsigned int req_secs,unsigned int req_usecs,eloop_timeout_handler handler,void * eloop_data,void * user_data)267 int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
268 eloop_timeout_handler handler, void *eloop_data,
269 void *user_data)
270 {
271 struct os_reltime now, requested, remaining;
272 struct eloop_timeout *tmp;
273
274 dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
275 if (tmp->handler == handler &&
276 tmp->eloop_data == eloop_data &&
277 tmp->user_data == user_data) {
278 requested.sec = req_secs;
279 requested.usec = req_usecs;
280 os_get_reltime(&now);
281 os_reltime_sub(&tmp->time, &now, &remaining);
282 if (os_reltime_before(&requested, &remaining)) {
283 eloop_cancel_timeout(handler, eloop_data,
284 user_data);
285 eloop_register_timeout(requested.sec,
286 requested.usec,
287 handler, eloop_data,
288 user_data);
289 return 1;
290 }
291 return 0;
292 }
293 }
294
295 return -1;
296 }
297
eloop_replenish_timeout(unsigned int req_secs,unsigned int req_usecs,eloop_timeout_handler handler,void * eloop_data,void * user_data)298 int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
299 eloop_timeout_handler handler, void *eloop_data,
300 void *user_data)
301 {
302 struct os_reltime now, requested, remaining;
303 struct eloop_timeout *tmp;
304
305 dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
306 if (tmp->handler == handler &&
307 tmp->eloop_data == eloop_data &&
308 tmp->user_data == user_data) {
309 requested.sec = req_secs;
310 requested.usec = req_usecs;
311 os_get_reltime(&now);
312 os_reltime_sub(&tmp->time, &now, &remaining);
313 if (os_reltime_before(&remaining, &requested)) {
314 eloop_cancel_timeout(handler, eloop_data,
315 user_data);
316 eloop_register_timeout(requested.sec,
317 requested.usec,
318 handler, eloop_data,
319 user_data);
320 return 1;
321 }
322 return 0;
323 }
324 }
325
326 return -1;
327 }
328
eloop_run(void)329 void eloop_run(void)
330 {
331 struct os_reltime tv, now;
332
333 while (!dl_list_empty(&eloop.timeout)) {
334 struct eloop_timeout *timeout;
335
336 timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
337 list);
338 if (timeout) {
339 os_get_reltime(&now);
340 if (os_reltime_before(&now, &timeout->time)) {
341 /* we don't need to process it rn, do it later */
342 uint32_t ms;
343 os_reltime_sub(&timeout->time, &now, &tv);
344 ms = tv.sec * 1000 + tv.usec / 1000;
345 ELOOP_LOCK();
346 os_timer_disarm(&eloop.eloop_timer);
347 os_timer_arm(&eloop.eloop_timer, ms, 0);
348 ELOOP_UNLOCK();
349 goto out;
350 }
351 }
352
353 /* check if some registered timeouts have occurred */
354 timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
355 list);
356 if (timeout) {
357 os_get_reltime(&now);
358 if (!os_reltime_before(&now, &timeout->time)) {
359 void *eloop_data = timeout->eloop_data;
360 void *user_data = timeout->user_data;
361 eloop_timeout_handler handler =
362 timeout->handler;
363 #ifdef ELOOP_DEBUG
364 char fn_name[100] = {0};
365 int line = timeout->line;
366 os_strlcpy(fn_name, timeout->func_name, 100);
367 #endif
368 eloop_remove_timeout(timeout);
369 #ifdef ELOOP_DEBUG
370 wpa_printf(MSG_DEBUG, "ELOOP: Running timer fn:%p scheduled by %s:%d ",
371 handler, fn_name, line);
372 #endif
373 handler(eloop_data, user_data);
374 }
375 }
376 }
377 out:
378 return;
379 }
380
eloop_destroy(void)381 void eloop_destroy(void)
382 {
383 struct eloop_timeout *timeout, *prev;
384 struct os_reltime now;
385
386 if (!eloop.eloop_started) {
387 return;
388 }
389 os_get_reltime(&now);
390 dl_list_for_each_safe(timeout, prev, &eloop.timeout,
391 struct eloop_timeout, list) {
392 int sec, usec;
393 sec = timeout->time.sec - now.sec;
394 usec = timeout->time.usec - now.usec;
395 if (timeout->time.usec < now.usec) {
396 sec--;
397 usec += 1000000;
398 }
399 wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
400 "eloop_data=%p user_data=%p handler=%p",
401 sec, usec, timeout->eloop_data, timeout->user_data,
402 timeout->handler);
403 eloop_remove_timeout(timeout);
404 }
405 if (eloop_data_lock) {
406 os_mutex_delete(eloop_data_lock);
407 eloop_data_lock = NULL;
408 }
409 os_timer_disarm(&eloop.eloop_timer);
410 os_timer_done(&eloop.eloop_timer);
411 os_memset(&eloop, 0, sizeof(eloop));
412 }
413