Files
SDK_GD32W51x/NSPE/WIFI_IOT/wifi/wifi_eloop.c
2023-05-18 18:53:00 +08:00

454 lines
12 KiB
C

/*
* Event loop based on select() loop
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
/*============================ INCLUDES ======================================*/
#include "wrapper_os.h"
#include "wifi_management.h"
#ifdef CONFIG_WIFI_MANAGEMENT_TASK
/*============================ MACROS ========================================*/
/*============================ MACRO FUNCTIONS ===============================*/
/*============================ TYPES =========================================*/
struct eloop_event {
void *eloop_data;
void *user_data;
eloop_event_handler handler;
eloop_event_t event;
};
struct eloop_timeout {
struct list_head list;
uint32_t time;
void *eloop_data;
void *user_data;
eloop_timeout_handler handler;
};
struct eloop_data {
size_t event_count;
struct eloop_event *events;
struct list_head timeout;
int pending_terminate;
int terminate;
};
/*============================ GLOBAL VARIABLES ==============================*/
/*============================ LOCAL VARIABLES ===============================*/
static struct eloop_data eloop;
static struct eloop_event eloop_predefined_events[] = WIFI_MANAGEMENT_INSTALL_STATIC_EVENTS();
/*============================ PROTOTYPES ====================================*/
/*============================ IMPLEMENTATION ================================*/
/*!
\brief initialize global event loop data(This function must be called before any other eloop_* function.)
\param[in] none
\param[out] none
\retval 0 on success, -1 on failure
*/
int eloop_init(void)
{
COMPILE_TIME_ASSERT(sizeof(eloop_message_t) == sizeof(void *));
sys_memset(&eloop, 0, sizeof(eloop));
INIT_DLIST_HEAD(&eloop.timeout);
return 0;
}
/*!
\brief register handler for generic events
\param[in] event: event to wait (eloop implementation specific)
\param[in] handler: callback function to be called when event is triggered
\param[in] eloop_data: callback context data (eloop_data)
\param[in] user_data: callback context data (user_data)
\param[out] none
\retval 0 on success, -1 on failure
*/
int eloop_event_register(eloop_event_t event,
eloop_event_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_event *tmp;
if (eloop.terminate)
return -1;
tmp = sys_realloc(eloop.events,
(eloop.event_count + 1) * sizeof(struct eloop_event));
if (tmp == NULL)
return -1;
tmp[eloop.event_count].eloop_data = eloop_data;
tmp[eloop.event_count].user_data = user_data;
tmp[eloop.event_count].handler = handler;
tmp[eloop.event_count].event = event;
eloop.event_count++;
eloop.events = tmp;
return 0;
}
/*!
\brief unregister handler for a generic event, Unregister a generic event notifier that was previously registered with eloop_event_register().
\param[in] event: event to cancel (eloop implementation specific)
\param[out] none
\retval none
*/
void eloop_event_unregister(eloop_event_t event)
{
size_t i;
if (eloop.terminate)
return;
if (eloop.events == NULL || eloop.event_count == 0)
return;
for (i = 0; i < eloop.event_count; i++) {
if (eloop.events[i].event == event)
break;
}
if (i == eloop.event_count)
return;
if (i != eloop.event_count - 1) {
sys_memmove(&eloop.events[i], &eloop.events[i + 1],
(eloop.event_count - i - 1) *
sizeof(struct eloop_event));
}
eloop.event_count--;
}
/*!
\brief send an event to the event loop
\param[in] event: event to signal
\param[out] none
\retval 0 on success, -1 on failure or event queue full
*/
int eloop_event_send(eloop_event_t event)
{
eloop_message_t message = {0};
if (eloop.terminate)
return -1;
message.event = event;
return sys_task_post(&wifi_mgmt_task_tcb, &message, 0);
}
/*!
\brief send a message to the event loop
\param[in] message: message to signal
\param[out] none
\retval 0 on success, -1 on failure or event queue full
*/
int eloop_message_send(eloop_message_t message)
{
if (eloop.terminate)
return -1;
return sys_task_post(&wifi_mgmt_task_tcb, &message, 0);
}
/*!
\brief dispatch event
\param[in] message: message to signal
\param[out] none
\retval none
*/
static void eloop_event_dispatch(eloop_message_t message)
{
int i;
eloop_event_t event = message.event;
if (event == ELOOP_EVENT_WAKEUP)
return;
if (event == ELOOP_EVENT_TERMINATE) {
eloop.terminate = 1;
return;
}
for (i = 0; i < ARRAY_SIZE(eloop_predefined_events); i++) {
if (eloop_predefined_events[i].event == ELOOP_EVENT_ALL ||
eloop_predefined_events[i].event == event) {
eloop_predefined_events[i].handler(
eloop_predefined_events[i].eloop_data,
&message);
}
}
for (i = 0; i < eloop.event_count; i++) {
if (eloop.events[i].event == event) {
eloop.events[i].handler(
eloop.events[i].eloop_data,
eloop.events[i].user_data);
}
}
}
/*!
\brief register timeout
\param[in] msecs: number of milliseconds to the timeout
\param[in] handler: callback function to be called when timeout occurs
\param[in] eloop_data: callback context data (eloop_ctx)
\param[in] user_data: callback context data (sock_ctx)
\param[out] none
\retval 0 on success, -1 on failure
*/
int eloop_timeout_register(unsigned int msecs,
eloop_timeout_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *tmp;
SYS_SR_ALLOC();
if (eloop.terminate)
return -1;
timeout = sys_zalloc(sizeof(*timeout));
if (timeout == NULL)
return -1;
timeout->time = sys_current_time_get();
timeout->time += msecs;
timeout->eloop_data = eloop_data;
timeout->user_data = user_data;
timeout->handler = handler;
SYS_CRITICAL_ENTER();
/* Maintain timeouts in order of increasing time */
list_for_each_entry(tmp, &eloop.timeout, struct eloop_timeout, list) {
if (timeout->time < tmp->time) {
list_add(&timeout->list, tmp->list.prev);
goto exit;
}
}
list_add_tail(&timeout->list, &eloop.timeout);
exit:
SYS_CRITICAL_EXIT();
return 0;
}
/*!
\brief remove eloop timeout
\param[in] timeout: pointer to the eloop_timeout struction need to remove
\param[out] none
\retval none
*/
static void eloop_timeout_remove(struct eloop_timeout *timeout)
{
list_del(&timeout->list);
sys_mfree(timeout);
}
/*!
\brief cancel timeouts
\param[in] handler: matching callback function
\param[in] eloop_data: matching eloop_data or %ELOOP_ALL_CTX to match all
\param[in] user_data: matching user_data or %ELOOP_ALL_CTX to match all
\param[out] none
\retval number of cancelled timeouts(0x00000000-0xffffffff)
*/
int eloop_timeout_cancel(eloop_timeout_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *prev;
int removed = 0;
SYS_SR_ALLOC();
if (eloop.terminate)
return -1;
SYS_CRITICAL_ENTER();
list_for_each_entry_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_timeout_remove(timeout);
removed++;
}
}
SYS_CRITICAL_EXIT();
return removed;
}
/*!
\brief check if a timeout is already registered
\param[in] handler: matching callback function
\param[in] eloop_data: matching eloop_data
\param[in] user_data: matching user_data
\param[out] none
\retval 1 if the timeout is registered, 0 if the timeout is not registered(0x00000000-0xffffffff)
*/
int eloop_is_timeout_registered(eloop_timeout_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_timeout *tmp;
SYS_SR_ALLOC();
SYS_CRITICAL_ENTER();
list_for_each_entry(tmp, &eloop.timeout, struct eloop_timeout, list) {
if (tmp->handler == handler &&
tmp->eloop_data == eloop_data &&
tmp->user_data == user_data)
SYS_CRITICAL_EXIT();
return 1;
}
SYS_CRITICAL_EXIT();
return 0;
}
/*!
\brief handle eloop timeout
\param[in] none
\param[out] none
\retval none
*/
static void eloop_timeout_handle()
{
struct eloop_timeout *timeout;
uint32_t now;
SYS_SR_ALLOC();
SYS_CRITICAL_ENTER();
timeout = list_first_entry(&eloop.timeout, struct eloop_timeout, list);
if (timeout) {
now = sys_current_time_get();
if (SYS_TIME_AFTER_EQ(now, timeout->time)) {
void *eloop_data = timeout->eloop_data;
void *user_data = timeout->user_data;
eloop_timeout_handler handler =
timeout->handler;
eloop_timeout_remove(timeout);
SYS_CRITICAL_EXIT();
handler(eloop_data, user_data);
return;
}
}
SYS_CRITICAL_EXIT();
}
/*!
\brief start the event loop
\param[in] none
\param[out] none
\retval none
*/
void eloop_run(void)
{
uint32_t now;
uint32_t remain;
eloop_message_t message;
struct eloop_timeout *timeout;
SYS_SR_ALLOC();
while (!eloop.terminate) {
remain = 0;
SYS_CRITICAL_ENTER();
timeout = list_first_entry(&eloop.timeout, struct eloop_timeout, list);
if (timeout) {
now = sys_current_time_get();
if (SYS_TIME_BEFORE(now, timeout->time)) {
remain = timeout->time - now;
}
}
SYS_CRITICAL_EXIT();
if (timeout && remain == 0) {
sys_yield();
} else {
if (sys_task_wait(timeout ? remain : 0, &message) == OS_OK) {
eloop_event_dispatch(message);
}
}
eloop_timeout_handle();
}
}
/*!
\brief terminate event loop
\param[in] none
\param[out] none
\retval none
*/
void eloop_terminate(void)
{
eloop_event_send(ELOOP_EVENT_TERMINATE);
}
/*!
\brief free any resources allocated for the event loop
\param[in] none
\param[out] none
\retval none
*/
void eloop_destroy(void)
{
struct eloop_timeout *timeout, *prev;
uint32_t now;
SYS_SR_ALLOC();
/* terminated by eloop_terminate, all events have been rejected since then */
sys_task_msg_flush(&wifi_mgmt_task_tcb);
now = sys_current_time_get();
SYS_CRITICAL_ENTER();
list_for_each_entry_safe(timeout, prev, &eloop.timeout,
struct eloop_timeout, list) {
wifi_sm_printf(WIFI_SM_INFO, "ELOOP: remaining timeout: %u "
"eloop_data=%p user_data=%p handler=%p",
timeout->time - now, timeout->eloop_data, timeout->user_data,
timeout->handler);
eloop_timeout_remove(timeout);
}
SYS_CRITICAL_EXIT();
sys_mfree(eloop.events);
}
/*!
\brief check whether event loop has been terminated
\param[in] none
\param[out] none
\retval 1: event loop terminate, 0: event loop still running
*/
int eloop_terminated(void)
{
return eloop.terminate;
}
#endif /* CONFIG_WIFI_MANAGEMENT_TASK */