Add new async_context abstraction and refactor cyw43_arch to use it (#1177)

* Extract all poll/threadsafe_background/freertos from cyw43_arch into new abstraction async_context:
* provides support for asynchronous events (timers/IRQ notifications) to be handled in a safe context.
* now guarantees all callbacks happen on a single core.
* is reusable by multiple different libraries (stdio_usb can now be ported to this but hasn't been yet).
* supports multiple independent instances (independent instances will not block each other).
* cyw43_arch libraries cleaned up to use the new abstraction. Note each distinct cyw43_arch type is now a very thin layer that creates the right type of context and adds cyw43_driver and lwip support as appropriate.

Additionally,

* Add new pico_time and hardware_alarm APIs
* Add from_us_since_boot()
* Add alarm_pool_create_with_unused_hardware_alarm()
* Add alarm_pool_add_alarm_at_force_in_context()
* Add hardware_alarm_claim_unused()
* Add hardware_alarm_force_irq()
* Added panic_compact() and some minor comment cleanup; moved FIRST_USER_IRQ define to platform_defs.h
This commit is contained in:
Graham Sanderson
2023-01-24 12:01:24 -06:00
committed by GitHub
parent c578422528
commit a540ca905a
47 changed files with 2694 additions and 883 deletions

View File

@ -6,10 +6,10 @@ endif ()
set(LWIP_TEST_PATH "src/Filelists.cmake")
if (NOT PICO_LWIP_PATH)
set(PICO_LWIP_PATH ${PROJECT_SOURCE_DIR}/lib/lwip)
# if (NOT EXISTS ${PICO_LWIP_PATH}/${LWIP_TEST_PATH})
# message(WARNING "LWIP submodule has not been initialized; Pico W wireless support will be unavailable
if (PICO_CYW43_SUPPORTED AND NOT EXISTS ${PICO_LWIP_PATH}/${LWIP_TEST_PATH})
message(WARNING "LWIP submodule has not been initialized; Pico W wireless support will be unavailable
#hint: try 'git submodule update --init' from your SDK directory (${PICO_SDK_PATH}).")
# endif()
endif()
elseif (NOT EXISTS ${PICO_LWIP_PATH}/${LWIP_TEST_PATH})
message(WARNING "PICO_LWIP_PATH specified but content not present.")
endif()
@ -46,7 +46,7 @@ if (EXISTS ${PICO_LWIP_PATH}/${LWIP_TEST_PATH})
${PICO_LWIP_PATH}/src/core/tcp_out.c
${PICO_LWIP_PATH}/src/core/timeouts.c
${PICO_LWIP_PATH}/src/core/udp.c
${CMAKE_CURRENT_LIST_DIR}/random.c
${CMAKE_CURRENT_LIST_DIR}/lwip_random.c
)
target_include_directories(pico_lwip_core INTERFACE
${PICO_LWIP_PATH}/src/include)
@ -269,11 +269,12 @@ if (EXISTS ${PICO_LWIP_PATH}/${LWIP_TEST_PATH})
# our nosys impl
add_library(pico_lwip_nosys INTERFACE)
target_sources(pico_lwip_nosys INTERFACE
${CMAKE_CURRENT_LIST_DIR}/nosys.c
${CMAKE_CURRENT_LIST_DIR}/lwip_nosys.c
)
target_link_libraries(pico_lwip_nosys INTERFACE
pico_lwip_arch)
pico_async_context_base
pico_lwip_arch
pico_lwip)
if (NOT PICO_LWIP_CONTRIB_PATH)
set(PICO_LWIP_CONTRIB_PATH ${PICO_LWIP_PATH}/contrib)
@ -291,5 +292,14 @@ if (EXISTS ${PICO_LWIP_PATH}/${LWIP_TEST_PATH})
target_link_libraries(pico_lwip_contrib_freertos INTERFACE
pico_lwip_arch)
add_library(pico_lwip_freertos INTERFACE)
target_sources(pico_lwip_freertos INTERFACE
${CMAKE_CURRENT_LIST_DIR}/lwip_freertos.c
)
target_link_libraries(pico_lwip_freertos INTERFACE
pico_async_context_base
pico_lwip
pico_lwip_contrib_freertos)
pico_promote_common_scope_vars()
endif()

View File

@ -34,9 +34,18 @@
#include <sys/time.h>
#ifndef PICO_LWIP_CUSTOM_LOCK_TCPIP_CORE
#define PICO_LWIP_CUSTOM_LOCK_TCPIP_CORE 1
#endif
#if NO_SYS
// todo really we should just not allow SYS_LIGHTWEIGHT_PROT for nosys mode (it doesn't do anything anyway)
typedef int sys_prot_t;
#elif PICO_LWIP_CUSTOM_LOCK_TCPIP_CORE
void pico_lwip_custom_lock_tcpip_core(void);
void pico_lwip_custom_unlock_tcpip_core(void);
#define LOCK_TCPIP_CORE() pico_lwip_custom_lock_tcpip_core()
#define UNLOCK_TCPIP_CORE() pico_lwip_custom_unlock_tcpip_core()
#endif
/* define compiler specific symbols */
@ -76,8 +85,14 @@ typedef int sys_prot_t;
#define LWIP_PLATFORM_ASSERT(x) panic(x)
#endif
unsigned int pico_lwip_rand(void);
#ifndef LWIP_RAND
#ifdef __cplusplus
extern "C" {
#endif
unsigned int pico_lwip_rand(void);
#ifdef __cplusplus
}
#endif
// Use ROSC based random number generation, more for the fact that rand() may not be seeded, than anything else
#define LWIP_RAND pico_lwip_rand
#endif

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_LWIP_FREERTOS_H
#define _PICO_LWIP_FREERTOS_H
#include "pico.h"
#include "pico/async_context.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \brief Initializes lwIP (NO_SYS=0 mode) support support for FreeRTOS using the provided async_context
* \ingroup pico_lwip
*
* If the initialization succeeds, \ref lwip_freertos_deinit() can be called to shutdown lwIP support
*
* \param context the async_context instance that provides the abstraction for handling asynchronous work. Note in general
* this would be an \ref async_context_freertos instance, though it doesn't have to be.
*
* \return true if the initialization succeeded
*/
bool lwip_freertos_init(async_context_t *context);
/*! \brief De-initialize lwIP (NO_SYS=0 mode) support for FreeRTOS
* \ingroup pico_lwip
*
* Note that since lwIP may only be initialized once, and doesn't itself provide a shutdown mechanism, lwIP
* itself may still consume resources.
*
* It is however safe to call \ref lwip_freertos_init again later.
*
* \param context the async_context the lwip_freertos support was added to via \ref lwip_freertos_init
*/
void lwip_freertos_deinit(async_context_t *context);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_LWIP_NOSYS_H
#define _PICO_LWIP_NOSYS_H
#include "pico.h"
#include "pico/async_context.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \brief Initializes lwIP (NO_SYS=1 mode) support support using the provided async_context
* \ingroup pico_lwip
*
* If the initialization succeeds, \ref lwip_nosys_deinit() can be called to shutdown lwIP support
*
* \param context the async_context instance that provides the abstraction for handling asynchronous work.
* \return true if the initialization succeeded
*/
bool lwip_nosys_init(async_context_t *context);
/*! \brief De-initialize lwIP (NO_SYS=1 mode) support
* \ingroup pico_lwip
*
* Note that since lwIP may only be initialized once, and doesn't itself provide a shutdown mechanism, lwIP
* itself may still consume resources
*
* It is however safe to call \ref lwip_nosys_init again later.
*
* \param context the async_context the lwip_nosys support was added to via \ref lwip_nosys_init
*/
void lwip_nosys_deinit(async_context_t *context);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// todo graham #ifdef for LWIP inclusion?
#include "pico/async_context.h"
#include "pico/time.h"
#include "lwip/tcpip.h"
#include "lwip/timeouts.h"
#include "FreeRTOS.h"
#include "semphr.h"
#if NO_SYS
#error lwip_freertos_async_context_bindings requires NO_SYS=0
#endif
static async_context_t * volatile lwip_context;
// lwIP tcpip_task cannot be shutdown, so we block it when we are de-initialized.
static SemaphoreHandle_t tcpip_task_blocker;
static void tcpip_init_done(void *param) {
xSemaphoreGive((SemaphoreHandle_t)param);
}
bool lwip_freertos_init(async_context_t *context) {
assert(!lwip_context);
lwip_context = context;
static bool done_lwip_init;
if (!done_lwip_init) {
done_lwip_init = true;
SemaphoreHandle_t init_sem = xSemaphoreCreateBinary();
tcpip_task_blocker = xSemaphoreCreateBinary();
tcpip_init(tcpip_init_done, init_sem);
xSemaphoreTake(init_sem, portMAX_DELAY);
vSemaphoreDelete(init_sem);
} else {
xSemaphoreGive(tcpip_task_blocker);
}
return true;
}
static uint32_t clear_lwip_context(__unused void *param) {
lwip_context = NULL;
return 0;
}
void lwip_freertos_deinit(__unused async_context_t *context) {
// clear the lwip context under lock as lwIP may still be running in tcpip_task
async_context_execute_sync(context, clear_lwip_context, NULL);
}
void pico_lwip_custom_lock_tcpip_core(void) {
while (!lwip_context) {
xSemaphoreTake(tcpip_task_blocker, portMAX_DELAY);
}
async_context_acquire_lock_blocking(lwip_context);
}
void pico_lwip_custom_unlock_tcpip_core(void) {
async_context_release_lock(lwip_context);
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/async_context.h"
#include <lwip/init.h>
#include "lwip/timeouts.h"
static void update_next_timeout(async_context_t *context, async_when_pending_worker_t *worker);
static void lwip_timeout_reached(async_context_t *context, async_at_time_worker_t *worker);
static async_when_pending_worker_t always_pending_update_timeout_worker = {
.do_work = update_next_timeout
};
static async_at_time_worker_t lwip_timeout_worker = {
.do_work = lwip_timeout_reached,
};
static void lwip_timeout_reached(__unused async_context_t *context, __unused async_at_time_worker_t *worker) {
assert(worker == &lwip_timeout_worker);
sys_check_timeouts();
}
static void update_next_timeout(async_context_t *context, async_when_pending_worker_t *worker) {
assert(worker == &always_pending_update_timeout_worker);
// we want to run on every execution of the helper to re-reflect any changes
// to the underlying lwIP timers which may have happened in the interim
// (note that worker will be called on every outermost exit of the async_context
// lock, and lwIP timers should not be modified whilst not holding the lock.
worker->work_pending = true;
uint32_t sleep_ms = sys_timeouts_sleeptime();
if (sleep_ms == SYS_TIMEOUTS_SLEEPTIME_INFINITE) {
lwip_timeout_worker.next_time = at_the_end_of_time;
} else {
lwip_timeout_worker.next_time = make_timeout_time_ms(sleep_ms);
}
async_context_add_at_time_worker(context, &lwip_timeout_worker);
}
bool lwip_nosys_init(async_context_t *context) {
static bool done_lwip_init;
if (!done_lwip_init) {
lwip_init();
done_lwip_init = true;
}
// we want the worker to be called on every async helper run (starting with the next)
always_pending_update_timeout_worker.work_pending = true;
async_context_add_when_pending_worker(context, &always_pending_update_timeout_worker);
return true;
}
void lwip_nosys_deinit(async_context_t *context) {
async_context_remove_at_time_worker(context, &lwip_timeout_worker);
async_context_remove_when_pending_worker(context, &always_pending_update_timeout_worker);
}
#if NO_SYS
/* lwip has provision for using a mutex, when applicable */
sys_prot_t sys_arch_protect(void) {
return 0;
}
void sys_arch_unprotect(__unused sys_prot_t pval) {
}
/* lwip needs a millisecond time source, and the TinyUSB board support code has one available */
uint32_t sys_now(void) {
return to_ms_since_boot(get_absolute_time());
}
#endif

View File

@ -1,26 +0,0 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "lwip/init.h"
#include "pico/time.h"
#if NO_SYS
/* lwip has provision for using a mutex, when applicable */
sys_prot_t sys_arch_protect(void) {
return 0;
}
void sys_arch_unprotect(sys_prot_t pval) {
(void) pval;
}
/* lwip needs a millisecond time source, and the TinyUSB board support code has one available */
uint32_t sys_now(void) {
return to_ms_since_boot(get_absolute_time());
}
#endif