diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index ceb1b0175f..f96f9d833e 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -1,4 +1,5 @@ set(srcs "port/os_xtensa.c" + "port/eloop.c" "src/ap/ap_config.c" "src/ap/ieee802_1x.c" "src/ap/wpa_auth.c" diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c b/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c index 19509e751f..0ad024d057 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c @@ -6,6 +6,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "rsn_supp/wpa.h" #include "rsn_supp/wpa_i.h" #include "common/eapol_common.h" @@ -260,6 +261,7 @@ int esp_supplicant_init(void) esp_wifi_register_wpa3_cb(wpa_cb); ret = esp_supplicant_common_init(wpa_cb); + eloop_init(); if (ret != 0) { return ret; @@ -277,5 +279,6 @@ int esp_supplicant_init(void) int esp_supplicant_deinit(void) { esp_supplicant_common_deinit(); + eloop_destroy(); return esp_wifi_unregister_wpa_cb_internal(); } diff --git a/components/wpa_supplicant/include/utils/wpa_debug.h b/components/wpa_supplicant/include/utils/wpa_debug.h index 855f23a579..a870385771 100644 --- a/components/wpa_supplicant/include/utils/wpa_debug.h +++ b/components/wpa_supplicant/include/utils/wpa_debug.h @@ -196,14 +196,4 @@ PRINTF_FORMAT(3, 4); typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt, size_t len); -typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); - -int eloop_cancel_timeout(eloop_timeout_handler handler, - void *eloop_data, void *user_data); - -int eloop_register_timeout(unsigned int secs, unsigned int usecs, - eloop_timeout_handler handler, - void *eloop_data, void *user_data); - - #endif /* WPA_DEBUG_H */ diff --git a/components/wpa_supplicant/port/eloop.c b/components/wpa_supplicant/port/eloop.c new file mode 100644 index 0000000000..07a3eebc17 --- /dev/null +++ b/components/wpa_supplicant/port/eloop.c @@ -0,0 +1,329 @@ +/* + * Event loop based on select() loop + * Copyright (c) 2002-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "includes.h" + +#include "common.h" +#include "list.h" +#include "eloop.h" +#include "esp_wifi.h" + +struct eloop_timeout { + struct dl_list list; + struct os_reltime time; + void *eloop_data; + void *user_data; + eloop_timeout_handler handler; +}; + +struct eloop_data { + struct dl_list timeout; + ETSTimer eloop_timer; + bool eloop_started; +}; + +#define ELOOP_LOCK() xSemaphoreTakeRecursive(eloop_data_lock, portMAX_DELAY) +#define ELOOP_UNLOCK() xSemaphoreGiveRecursive(eloop_data_lock) + +static void *eloop_data_lock = NULL; + +static struct eloop_data eloop; + +int eloop_init(void) +{ + os_memset(&eloop, 0, sizeof(eloop)); + dl_list_init(&eloop.timeout); + ets_timer_disarm(&eloop.eloop_timer); + ets_timer_setfn(&eloop.eloop_timer, (ETSTimerFunc *)eloop_run, NULL); + + eloop_data_lock = xSemaphoreCreateRecursiveMutex(); + + if (!eloop_data_lock) { + wpa_printf(MSG_ERROR, "failed to create eloop data loop"); + return -1; + } + eloop.eloop_started = true; + + return 0; +} + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *tmp; + os_time_t now_sec; + + timeout = os_zalloc(sizeof(*timeout)); + if (timeout == NULL) + return -1; + if (os_get_reltime(&timeout->time) < 0) { + os_free(timeout); + return -1; + } + now_sec = timeout->time.sec; + timeout->time.sec += secs; + if (timeout->time.sec < now_sec) + goto overflow; + timeout->time.usec += usecs; + while (timeout->time.usec >= 1000000) { + timeout->time.sec++; + timeout->time.usec -= 1000000; + } + if (timeout->time.sec < now_sec) + goto overflow; + timeout->eloop_data = eloop_data; + timeout->user_data = user_data; + timeout->handler = handler; + + /* Maintain timeouts in order of increasing time */ + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (os_reltime_before(&timeout->time, &tmp->time)) { + ELOOP_LOCK(); + dl_list_add(tmp->list.prev, &timeout->list); + ELOOP_UNLOCK(); + goto run; + } + } + ELOOP_LOCK(); + dl_list_add_tail(&eloop.timeout, &timeout->list); + ELOOP_UNLOCK(); + +run: + ets_timer_disarm(&eloop.eloop_timer); + ets_timer_arm(&eloop.eloop_timer, 0, 0); + + return 0; + +overflow: + /* + * Integer overflow - assume long enough timeout to be assumed + * to be infinite, i.e., the timeout would never happen. + */ + wpa_printf(MSG_DEBUG, + "ELOOP: Too long timeout (secs=%u usecs=%u) to ever happen - ignore it", + secs,usecs); + os_free(timeout); + return 0; +} + + +static void eloop_remove_timeout(struct eloop_timeout *timeout) +{ + ELOOP_LOCK(); + dl_list_del(&timeout->list); + ELOOP_UNLOCK(); + os_free(timeout); +} + + +int eloop_cancel_timeout(eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *prev; + int removed = 0; + + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data || + eloop_data == ELOOP_ALL_CTX) && + (timeout->user_data == user_data || + user_data == ELOOP_ALL_CTX)) { + eloop_remove_timeout(timeout); + removed++; + } + } + + return removed; +} + + +int eloop_cancel_timeout_one(eloop_timeout_handler handler, + void *eloop_data, void *user_data, + struct os_reltime *remaining) +{ + struct eloop_timeout *timeout, *prev; + int removed = 0; + struct os_reltime now; + + os_get_reltime(&now); + remaining->sec = remaining->usec = 0; + + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data) && + (timeout->user_data == user_data)) { + removed = 1; + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, remaining); + eloop_remove_timeout(timeout); + break; + } + } + return removed; +} + + +int eloop_is_timeout_registered(eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *tmp; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) + return 1; + } + + return 0; +} + + +int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + struct eloop_timeout *tmp; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&requested, &remaining)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + + +int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + struct eloop_timeout *tmp; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&remaining, &requested)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + +void eloop_run(void) +{ + struct os_reltime tv, now; + + while (!dl_list_empty(&eloop.timeout)) { + struct eloop_timeout *timeout; + + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_reltime(&now); + if (os_reltime_before(&now, &timeout->time)) { + /* we don't need to process it rn, do it later */ + uint32_t ms; + os_reltime_sub(&timeout->time, &now, &tv); + ms = tv.sec * 1000 + tv.usec / 1000; + ets_timer_disarm(&eloop.eloop_timer); + ets_timer_arm(&eloop.eloop_timer, ms, 0); + goto out; + } + } + + /* check if some registered timeouts have occurred */ + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_reltime(&now); + if (!os_reltime_before(&now, &timeout->time)) { + void *eloop_data = timeout->eloop_data; + void *user_data = timeout->user_data; + eloop_timeout_handler handler = + timeout->handler; + eloop_remove_timeout(timeout); + handler(eloop_data, user_data); + } + } + } +out: + return; +} + +void eloop_destroy(void) +{ + struct eloop_timeout *timeout, *prev; + struct os_reltime now; + + if (!eloop.eloop_started) { + return; + } + os_get_reltime(&now); + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + int sec, usec; + sec = timeout->time.sec - now.sec; + usec = timeout->time.usec - now.usec; + if (timeout->time.usec < now.usec) { + sec--; + usec += 1000000; + } + wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d " + "eloop_data=%p user_data=%p handler=%p", + sec, usec, timeout->eloop_data, timeout->user_data, + timeout->handler); + eloop_remove_timeout(timeout); + } + if (eloop_data_lock) { + vSemaphoreDelete(eloop_data_lock); + eloop_data_lock = NULL; + } + ets_timer_disarm(&eloop.eloop_timer); + os_memset(&eloop, 0, sizeof(eloop)); +} diff --git a/components/wpa_supplicant/src/ap/wpa_auth.c b/components/wpa_supplicant/src/ap/wpa_auth.c index be6e5c3857..78269bb7ae 100644 --- a/components/wpa_supplicant/src/ap/wpa_auth.c +++ b/components/wpa_supplicant/src/ap/wpa_auth.c @@ -8,6 +8,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "utils/state_machine.h" #include "common/ieee802_11_defs.h" #include "ap/wpa_auth.h" @@ -48,9 +49,6 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; -static const u32 eapol_key_timeout_first = 100; /* ms */ -static const u32 eapol_key_timeout_subseq = 1000; /* ms */ -static const u32 eapol_key_timeout_first_group = 500; /* ms */ #define WPA_SM_MAX_INDEX 16 static void *s_sm_table[WPA_SM_MAX_INDEX]; @@ -1129,7 +1127,6 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *kde, size_t kde_len, int keyidx, int encr) { - int timeout_ms; int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; int ctr; @@ -1140,17 +1137,8 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, keyidx, encr, 0); ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; - if (ctr == 1 && wpa_auth->conf.tx_status) - timeout_ms = pairwise ? eapol_key_timeout_first : - eapol_key_timeout_first_group; - else - timeout_ms = eapol_key_timeout_subseq; if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) sm->pending_1_of_4_timeout = 1; - wpa_printf( MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " - "counter %d)\n", timeout_ms, ctr); - eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, - wpa_send_eapol_timeout, wpa_auth, sm); ets_timer_disarm(&sm->resend_eapol); ets_timer_setfn(&sm->resend_eapol, (ETSTimerFunc *)resend_eapol_handle, (void*)(sm->index)); ets_timer_arm(&sm->resend_eapol, 1000, 0); diff --git a/components/wpa_supplicant/src/common/rrm.c b/components/wpa_supplicant/src/common/rrm.c index 8fae4c3eb4..dc7a2aec25 100644 --- a/components/wpa_supplicant/src/common/rrm.c +++ b/components/wpa_supplicant/src/common/rrm.c @@ -10,6 +10,7 @@ #include "utils/common.h" #include "utils/bitfield.h" +#include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wpa_supplicant_i.h" diff --git a/components/wpa_supplicant/src/rsn_supp/wpa.c b/components/wpa_supplicant/src/rsn_supp/wpa.c index 8b1cb67d3d..b44c406ec9 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa.c +++ b/components/wpa_supplicant/src/rsn_supp/wpa.c @@ -15,6 +15,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "rsn_supp/wpa.h" #include "rsn_supp/pmksa_cache.h" #include "rsn_supp/wpa_i.h" diff --git a/components/wpa_supplicant/src/utils/eloop.h b/components/wpa_supplicant/src/utils/eloop.h new file mode 100644 index 0000000000..04ee6d1837 --- /dev/null +++ b/components/wpa_supplicant/src/utils/eloop.h @@ -0,0 +1,367 @@ +/* + * Event loop + * Copyright (c) 2002-2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file defines an event loop interface that supports processing events + * from registered timeouts (i.e., do something after N seconds), sockets + * (e.g., a new packet available for reading), and signals. eloop.c is an + * implementation of this interface using select() and sockets. This is + * suitable for most UNIX/POSIX systems. When porting to other operating + * systems, it may be necessary to replace that implementation with OS specific + * mechanisms. + */ + +#ifndef ELOOP_H +#define ELOOP_H + +/** + * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts + */ +#define ELOOP_ALL_CTX (void *) -1 + +/** + * eloop_event_type - eloop socket event type for eloop_register_sock() + * @EVENT_TYPE_READ: Socket has data available for reading + * @EVENT_TYPE_WRITE: Socket has room for new data to be written + * @EVENT_TYPE_EXCEPTION: An exception has been reported + */ +typedef enum { + EVENT_TYPE_READ = 0, + EVENT_TYPE_WRITE, + EVENT_TYPE_EXCEPTION +} eloop_event_type; + +/** + * eloop_sock_handler - eloop socket event callback type + * @sock: File descriptor number for the socket + * @eloop_ctx: Registered callback context data (eloop_data) + * @sock_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx); + +/** + * eloop_event_handler - eloop generic event callback type + * @eloop_ctx: Registered callback context data (eloop_data) + * @user_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_event_handler)(void *eloop_ctx, void *user_ctx); + +/** + * eloop_timeout_handler - eloop timeout event callback type + * @eloop_ctx: Registered callback context data (eloop_data) + * @user_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx); + +/** + * eloop_signal_handler - eloop signal event callback type + * @sig: Signal number + * @signal_ctx: Registered callback context data (user_data from + * eloop_register_signal(), eloop_register_signal_terminate(), or + * eloop_register_signal_reconfig() call) + */ +typedef void (*eloop_signal_handler)(int sig, void *signal_ctx); + +/** + * eloop_init() - Initialize global event loop data + * Returns: 0 on success, -1 on failure + * + * This function must be called before any other eloop_* function. + */ +int eloop_init(void); + +/** + * eloop_register_read_sock - Register handler for read events + * @sock: File descriptor number for the socket + * @handler: Callback function to be called when data is available for reading + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a read socket notifier for the given file descriptor. The handler + * function will be called whenever data is available for reading from the + * socket. The handler function is responsible for clearing the event after + * having processed it in order to avoid eloop from calling the handler again + * for the same event. + */ +int eloop_register_read_sock(int sock, eloop_sock_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_unregister_read_sock - Unregister handler for read events + * @sock: File descriptor number for the socket + * + * Unregister a read socket notifier that was previously registered with + * eloop_register_read_sock(). + */ +void eloop_unregister_read_sock(int sock); + +/** + * eloop_register_sock - Register handler for socket events + * @sock: File descriptor number for the socket + * @type: Type of event to wait for + * @handler: Callback function to be called when the event is triggered + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register an event notifier for the given socket's file descriptor. The + * handler function will be called whenever the that event is triggered for the + * socket. The handler function is responsible for clearing the event after + * having processed it in order to avoid eloop from calling the handler again + * for the same event. + */ +int eloop_register_sock(int sock, eloop_event_type type, + eloop_sock_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_unregister_sock - Unregister handler for socket events + * @sock: File descriptor number for the socket + * @type: Type of event for which sock was registered + * + * Unregister a socket event notifier that was previously registered with + * eloop_register_sock(). + */ +void eloop_unregister_sock(int sock, eloop_event_type type); + +/** + * eloop_register_event - Register handler for generic events + * @event: Event to wait (eloop implementation specific) + * @event_size: Size of event data + * @handler: Callback function to be called when event is triggered + * @eloop_data: Callback context data (eloop_data) + * @user_data: Callback context data (user_data) + * Returns: 0 on success, -1 on failure + * + * Register an event handler for the given event. This function is used to + * register eloop implementation specific events which are mainly targeted for + * operating system specific code (driver interface and l2_packet) since the + * portable code will not be able to use such an OS-specific call. The handler + * function will be called whenever the event is triggered. The handler + * function is responsible for clearing the event after having processed it in + * order to avoid eloop from calling the handler again for the same event. + * + * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE + * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable, + * and they would call this function with eloop_register_event(h, sizeof(h), + * ...). + */ +int eloop_register_event(void *event, size_t event_size, + eloop_event_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_unregister_event - Unregister handler for a generic event + * @event: Event to cancel (eloop implementation specific) + * @event_size: Size of event data + * + * Unregister a generic event notifier that was previously registered with + * eloop_register_event(). + */ +void eloop_unregister_event(void *event, size_t event_size); + +/** + * eloop_register_timeout - Register timeout + * @secs: Number of seconds to the timeout + * @usecs: Number of microseconds to the timeout + * @handler: Callback function to be called when timeout occurs + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a timeout that will cause the handler function to be called after + * given time. + */ +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_cancel_timeout - Cancel timeouts + * @handler: Matching callback function + * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all + * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all + * Returns: Number of cancelled timeouts + * + * Cancel matching timeouts registered with + * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for + * cancelling all timeouts regardless of eloop_data/user_data. + */ +int eloop_cancel_timeout(eloop_timeout_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_cancel_timeout_one - Cancel a single timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * @remaining: Time left on the cancelled timer + * Returns: Number of cancelled timeouts + * + * Cancel matching timeout registered with + * eloop_register_timeout() and return the remaining time left. + */ +int eloop_cancel_timeout_one(eloop_timeout_handler handler, + void *eloop_data, void *user_data, + struct os_reltime *remaining); + +/** + * eloop_is_timeout_registered - Check if a timeout is already registered + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is registered, 0 if the timeout is not registered + * + * Determine if a matching timeout is registered + * with eloop_register_timeout(). + */ +int eloop_is_timeout_registered(eloop_timeout_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_deplete_timeout - Deplete a timeout that is already registered + * @req_secs: Requested number of seconds to the timeout + * @req_usecs: Requested number of microseconds to the timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is depleted, 0 if no change is made, -1 if no + * timeout matched + * + * Find a registered matching timeout. If found, + * deplete the timeout if remaining time is more than the requested time. + */ +int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data); + +/** + * eloop_replenish_timeout - Replenish a timeout that is already registered + * @req_secs: Requested number of seconds to the timeout + * @req_usecs: Requested number of microseconds to the timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is replenished, 0 if no change is made, -1 if no + * timeout matched + * + * Find a registered matching timeout. If found, + * replenish the timeout if remaining time is less than the requested time. + */ +int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data); + +/** + * eloop_register_signal - Register handler for signals + * @sig: Signal number (e.g., SIGHUP) + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a signal is received. + * The callback function is actually called only after the system signal + * handler has returned. This means that the normal limits for sighandlers + * (i.e., only "safe functions" allowed) do not apply for the registered + * callback. + */ +int eloop_register_signal(int sig, eloop_signal_handler handler, + void *user_data); + +/** + * eloop_register_signal_terminate - Register handler for terminate signals + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a process termination + * signal is received. The callback function is actually called only after the + * system signal handler has returned. This means that the normal limits for + * sighandlers (i.e., only "safe functions" allowed) do not apply for the + * registered callback. + * + * This function is a more portable version of eloop_register_signal() since + * the knowledge of exact details of the signals is hidden in eloop + * implementation. In case of operating systems using signal(), this function + * registers handlers for SIGINT and SIGTERM. + */ +int eloop_register_signal_terminate(eloop_signal_handler handler, + void *user_data); + +/** + * eloop_register_signal_reconfig - Register handler for reconfig signals + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a reconfiguration / + * hangup signal is received. The callback function is actually called only + * after the system signal handler has returned. This means that the normal + * limits for sighandlers (i.e., only "safe functions" allowed) do not apply + * for the registered callback. + * + * This function is a more portable version of eloop_register_signal() since + * the knowledge of exact details of the signals is hidden in eloop + * implementation. In case of operating systems using signal(), this function + * registers a handler for SIGHUP. + */ +int eloop_register_signal_reconfig(eloop_signal_handler handler, + void *user_data); + +/** + * eloop_sock_requeue - Requeue sockets + * + * Requeue sockets after forking because some implementations require this, + * such as epoll and kqueue. + */ +int eloop_sock_requeue(void); + +/** + * eloop_run - Start the event loop + * + * Start the event loop and continue running as long as there are any + * registered event handlers. This function is run after event loop has been + * initialized with event_init() and one or more events have been registered. + */ +void eloop_run(void); + +/** + * eloop_terminate - Terminate event loop + * + * Terminate event loop even if there are registered events. This can be used + * to request the program to be terminated cleanly. + */ +void eloop_terminate(void); + +/** + * eloop_destroy - Free any resources allocated for the event loop + * + * After calling eloop_destroy(), other eloop_* functions must not be called + * before re-running eloop_init(). + */ +void eloop_destroy(void); + +/** + * eloop_terminated - Check whether event loop has been terminated + * Returns: 1 = event loop terminate, 0 = event loop still running + * + * This function can be used to check whether eloop_terminate() has been called + * to request termination of the event loop. This is normally used to abort + * operations that may still be queued to be run when eloop_terminate() was + * called. + */ +int eloop_terminated(void); + +/** + * eloop_wait_for_read_sock - Wait for a single reader + * @sock: File descriptor number for the socket + * + * Do a blocking wait for a single read socket. + */ +void eloop_wait_for_read_sock(int sock); + +#endif /* ELOOP_H */ diff --git a/components/wpa_supplicant/src/utils/wpa_debug.c b/components/wpa_supplicant/src/utils/wpa_debug.c index 3190ffa5df..ca75f74837 100644 --- a/components/wpa_supplicant/src/utils/wpa_debug.c +++ b/components/wpa_supplicant/src/utils/wpa_debug.c @@ -112,16 +112,4 @@ void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len) } #endif -int eloop_cancel_timeout(eloop_timeout_handler handler, - void *eloop_data, void *user_data) -{ - return 0; -} - -int eloop_register_timeout(unsigned int secs, unsigned int usecs, - eloop_timeout_handler handler, - void *eloop_data, void *user_data) -{ - return 0; -} #endif // ESP_SUPPLICANT diff --git a/components/wpa_supplicant/test/test_eloop.c b/components/wpa_supplicant/test/test_eloop.c new file mode 100644 index 0000000000..f25847f3d5 --- /dev/null +++ b/components/wpa_supplicant/test/test_eloop.c @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "string.h" +#include "esp_system.h" +#include "unity.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_wifi_types.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "../esp_supplicant/src/esp_wifi_driver.h" +#include "esp_log.h" +#include "test_utils.h" +#include "memory_checks.h" + +uint32_t timeouts_usec[6] = { 10000, 1000, 10000, 5000, 15000, 1000 }; +uint32_t timeouts_sec[6] = { 10, 1, 10, 5, 15, 1 }; +int executed_order[6]; +int t; +struct os_reltime ts; + +/* there is only single instance of esp_timer so no need of protection */ +void callback(void *a, void *b) +{ + int *i = a; + struct os_time age, now; + + os_get_reltime(&now); + os_time_sub(&now, &ts, &age); + + /* let's give 5 ms offset for this small block */ + if ((age.sec - timeouts_sec[*i]) || age.usec - timeouts_usec[*i] > 5000) { + executed_order[t] = -1; + } else { + executed_order[t] = *i; + } + t++; + + ESP_LOGI("Eloop Test", "timer ran after %lu sec and %lu usec of scheduled time", age.sec - timeouts_sec[*i], age.usec - timeouts_usec[*i]); + +} + +/* Check if eloop runs its timers correctly & in correct order */ +TEST_CASE("Test eloop timers run", "[eloop]") +{ + int execution_order[6] = {1, 5, 3, 0, 2, 4}; + int index[6] = {0,1,2,3,4,5}; + + eloop_init(); + os_get_reltime(&ts); + for (int i = 0; i < 6; i++) { + eloop_register_timeout(timeouts_sec[i], timeouts_usec[i], + callback, &index[i], NULL); + } + + /* wait for all timers to run */ + os_sleep(20, 0); + + /* check the execution order, this will also check whether they were fired at correct time */ + TEST_ASSERT(memcmp(execution_order, executed_order, 6*sizeof(int)) == 0); + eloop_destroy(); +}