Add Pico W and lwIP support

This commit is contained in:
graham sanderson
2022-06-29 23:03:32 -05:00
parent 77c04e458c
commit 5e9a5e827b
36 changed files with 2915 additions and 13 deletions

View File

@ -0,0 +1,66 @@
if (PICO_CYW43_SUPPORTED) # set by BOARD=pico-w
if (TARGET cyw43_driver_picow)
message("Enabling build support for Pico W wireless.")
pico_add_impl_library(pico_cyw43_arch)
target_sources(pico_cyw43_arch INTERFACE
${CMAKE_CURRENT_LIST_DIR}/cyw43_arch.c
${CMAKE_CURRENT_LIST_DIR}/cyw43_arch_poll.c
${CMAKE_CURRENT_LIST_DIR}/cyw43_arch_threadsafe_background.c
${CMAKE_CURRENT_LIST_DIR}/cyw43_arch_freertos.c
)
target_include_directories(pico_cyw43_arch INTERFACE
${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(pico_cyw43_arch INTERFACE
pico_unique_id
cyw43_driver_picow)
if (NOT TARGET pico_lwip)
message(WARNING "lwIP is not available; Full Pico W wireless support will be unavailable too")
else()
add_library(pico_cyw43_arch_lwip_poll INTERFACE)
target_link_libraries(pico_cyw43_arch_lwip_poll INTERFACE
pico_cyw43_arch
pico_lwip
pico_lwip_nosys)
target_compile_definitions(pico_cyw43_arch_lwip_poll INTERFACE
CYW43_LWIP=1
PICO_CYW43_ARCH_POLL=1
)
add_library(pico_cyw43_arch_lwip_threadsafe_background INTERFACE)
target_link_libraries(pico_cyw43_arch_lwip_threadsafe_background INTERFACE
pico_cyw43_arch
pico_lwip
pico_lwip_nosys)
target_compile_definitions(pico_cyw43_arch_lwip_threadsafe_background INTERFACE
CYW43_LWIP=1
PICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1
)
add_library(pico_cyw43_arch_lwip_sys_freertos INTERFACE)
target_link_libraries(pico_cyw43_arch_lwip_sys_freertos INTERFACE
pico_cyw43_arch
pico_lwip
pico_lwip_contrib_freertos)
target_compile_definitions(pico_cyw43_arch_lwip_sys_freertos INTERFACE
CYW43_LWIP=1
LWIP_PROVIDE_ERRNO=1
PICO_CYW43_ARCH_FREERTOS=1
)
endif()
add_library(pico_cyw43_arch_none INTERFACE)
target_link_libraries(pico_cyw43_arch_none INTERFACE pico_cyw43_arch)
target_compile_definitions(pico_cyw43_arch_none INTERFACE
CYW43_LWIP=0
PICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1 # none still uses threadsafe_background to make gpio use easy
)
endif()
endif()
if (PICO_CYW43_DRIVER_PATH AND EXISTS "${PICO_CYW43_DRIVER_PATH}")
pico_add_doxygen(${PICO_CYW43_DRIVER_PATH}/src)
endif()

View File

@ -0,0 +1,145 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "pico/unique_id.h"
#include "cyw43.h"
#include "pico/cyw43_arch.h"
#include "cyw43_ll.h"
#include "cyw43_stats.h"
#if CYW43_ARCH_DEBUG_ENABLED
#define CYW43_ARCH_DEBUG(...) printf(__VA_ARGS__)
#else
#define CYW43_ARCH_DEBUG(...) ((void)0)
#endif
static uint32_t country_code = PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE;
void cyw43_arch_enable_sta_mode() {
assert(cyw43_is_initialized(&cyw43_state));
cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_STA, true, cyw43_arch_get_country_code());
}
void cyw43_arch_enable_ap_mode(const char *ssid, const char *password, uint32_t auth) {
assert(cyw43_is_initialized(&cyw43_state));
cyw43_wifi_ap_set_ssid(&cyw43_state, strlen(ssid), (const uint8_t *) ssid);
if (password) {
cyw43_wifi_ap_set_password(&cyw43_state, strlen(password), (const uint8_t *) password);
cyw43_wifi_ap_set_auth(&cyw43_state, auth);
} else {
cyw43_wifi_ap_set_auth(&cyw43_state, CYW43_AUTH_OPEN);
}
cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_AP, true, cyw43_arch_get_country_code());
}
#if CYW43_ARCH_DEBUG_ENABLED
// Return a string for the wireless state
static const char* status_name(int status)
{
switch (status) {
case CYW43_LINK_DOWN:
return "link down";
case CYW43_LINK_JOIN:
return "joining";
case CYW43_LINK_NOIP:
return "no ip";
case CYW43_LINK_UP:
return "link up";
case CYW43_LINK_FAIL:
return "link fail";
case CYW43_LINK_NONET:
return "network fail";
case CYW43_LINK_BADAUTH:
return "bad auth";
}
return "unknown";
}
#endif
int cyw43_arch_wifi_connect_async(const char *ssid, const char *pw, uint32_t auth) {
if (!pw) auth = CYW43_AUTH_OPEN;
// Connect to wireless
return cyw43_wifi_join(&cyw43_state, strlen(ssid), (const uint8_t *)ssid, pw ? strlen(pw) : 0, (const uint8_t *)pw, auth, NULL, CYW43_ITF_STA);
}
// Connect to wireless, return with success when an IP address has been assigned
int cyw43_arch_wifi_connect_until(const char *ssid, const char *pw, uint32_t auth, absolute_time_t until) {
int err = cyw43_arch_wifi_connect_async(ssid, pw, auth);
if (err) return err;
int status = CYW43_LINK_UP + 1;
while(status >= 0 && status != CYW43_LINK_UP) {
int new_status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
if (new_status != status) {
status = new_status;
CYW43_ARCH_DEBUG("connect status: %s\n", status_name(status));
}
// in case polling is required
cyw43_arch_poll();
best_effort_wfe_or_timeout(until);
if (time_reached(until)) {
return PICO_ERROR_TIMEOUT;
}
}
return status == CYW43_LINK_UP ? 0 : status;
}
int cyw43_arch_wifi_connect_blocking(const char *ssid, const char *pw, uint32_t auth) {
return cyw43_arch_wifi_connect_until(ssid, pw, auth, at_the_end_of_time);
}
int cyw43_arch_wifi_connect_timeout_ms(const char *ssid, const char *pw, uint32_t auth, uint32_t timeout_ms) {
return cyw43_arch_wifi_connect_until(ssid, pw, auth, make_timeout_time_ms(timeout_ms));
}
// todo maybe add an #ifdef in cyw43_driver
uint32_t storage_read_blocks(__unused uint8_t *dest, __unused uint32_t block_num, __unused uint32_t num_blocks) {
// shouldn't be used
panic_unsupported();
}
// Generate a mac address if one is not set in otp
void cyw43_hal_generate_laa_mac(__unused int idx, uint8_t buf[6]) {
CYW43_DEBUG("Warning. No mac in otp. Generating mac from board id\n");
pico_unique_board_id_t board_id;
pico_get_unique_board_id(&board_id);
memcpy(buf, &board_id.id[2], 6);
buf[0] &= (uint8_t)~0x1; // unicast
buf[0] |= 0x2; // locally administered
}
// Return mac address
void cyw43_hal_get_mac(__unused int idx, uint8_t buf[6]) {
// The mac should come from cyw43 otp.
// This is loaded into the state after the driver is initialised
// cyw43_hal_generate_laa_mac is called by the driver to generate a mac if otp is not set
memcpy(buf, cyw43_state.mac, 6);
}
uint32_t cyw43_arch_get_country_code(void) {
return country_code;
}
int cyw43_arch_init_with_country(uint32_t country) {
country_code = country;
return cyw43_arch_init();
}
void cyw43_arch_gpio_put(uint wl_gpio, bool value) {
invalid_params_if(CYW43_ARCH, wl_gpio >= CYW43_WL_GPIO_COUNT);
cyw43_gpio_set(&cyw43_state, (int)wl_gpio, value);
}
bool cyw43_arch_gpio_get(uint wl_gpio) {
invalid_params_if(CYW43_ARCH, wl_gpio >= CYW43_WL_GPIO_COUNT);
bool value = false;
cyw43_gpio_get(&cyw43_state, (int)wl_gpio, &value);
return value;
}

View File

@ -0,0 +1,251 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include "pico/cyw43_arch.h"
#include "hardware/gpio.h"
#include "hardware/irq.h"
#include "hardware/sync.h"
#include "cyw43_stats.h"
#if CYW43_LWIP
#include <lwip/tcpip.h>
#endif
#if PICO_CYW43_ARCH_FREERTOS
// FreeRTOS includes
#include "FreeRTOS.h"
#include "timers.h"
#include "semphr.h"
#if NO_SYS
#error example_cyw43_arch_frertos_sys requires NO_SYS=0
#endif
#ifndef CYW43_TASK_PRIORITY
#define CYW43_TASK_PRIORITY ( tskIDLE_PRIORITY + 4)
#endif
#ifndef CYW43_SLEEP_CHECK_MS
#define CYW43_SLEEP_CHECK_MS 50 // How often to run lwip callback
#endif
#define CYW43_GPIO_IRQ_HANDLER_PRIORITY 0x40
static void signal_cyw43_task(void);
#if !LWIP_TCPIP_CORE_LOCKING_INPUT
static SemaphoreHandle_t cyw43_mutex;
#endif
static TimerHandle_t timer_handle;
static TaskHandle_t cyw43_task_handle;
static volatile bool cyw43_task_should_exit;
static SemaphoreHandle_t cyw43_worker_ran_sem;
static uint8_t cyw43_core_num;
// Called in low priority pendsv interrupt only to do lwip processing and check cyw43 sleep
static void periodic_worker(__unused TimerHandle_t handle)
{
#if CYW43_USE_STATS
static uint32_t counter;
if (counter++ % (30000 / LWIP_SYS_CHECK_MS) == 0) {
cyw43_dump_stats();
}
#endif
CYW43_STAT_INC(LWIP_RUN_COUNT);
if (cyw43_poll) {
if (cyw43_sleep > 0) {
if (--cyw43_sleep == 0) {
signal_cyw43_task();
}
}
}
}
void cyw43_await_background_or_timeout_us(uint32_t timeout_us) {
// if we are called from within an IRQ, then don't wait (we are only ever called in a polling loop)
assert(!portCHECK_IF_IN_ISR());
xSemaphoreTake(cyw43_worker_ran_sem, pdMS_TO_TICKS(timeout_us / 1000));
}
// GPIO interrupt handler to tell us there's cyw43 has work to do
static void gpio_irq_handler(void)
{
uint32_t events = gpio_get_irq_event_mask(CYW43_PIN_WL_HOST_WAKE);
if (events & GPIO_IRQ_LEVEL_HIGH) {
// As we use a high level interrupt, it will go off forever until it's serviced
// So disable the interrupt until this is done. It's re-enabled again by CYW43_POST_POLL_HOOK
// which is called at the end of cyw43_poll_func
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
signal_cyw43_task();
CYW43_STAT_INC(IRQ_COUNT);
}
}
// Low priority interrupt handler to perform background processing
static void cyw43_task(__unused void *param) {
do {
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
if (cyw43_task_should_exit) break;
cyw43_thread_enter();
if (cyw43_poll) cyw43_poll();
cyw43_thread_exit();
xSemaphoreGive(cyw43_worker_ran_sem);
__sev(); // it is possible regular code is waiting on a WFE on the other core
} while (true);
}
static void tcpip_init_done(void *param) {
xSemaphoreGive((SemaphoreHandle_t)param);
}
int cyw43_arch_init(void) {
cyw43_core_num = get_core_num();
#if configUSE_CORE_AFFINITY && configNUM_CORES > 1
TaskHandle_t task_handle = xTaskGetCurrentTaskHandle();
UBaseType_t affinity = vTaskCoreAffinityGet(task_handle);
// we must bind the main task to one core during init
vTaskCoreAffinitySet(task_handle, 1 << portGET_CORE_ID());
#endif
#if !LWIP_TCPIP_CORE_LOCKING_INPUT
cyw43_mutex = xSemaphoreCreateRecursiveMutex();
#endif
cyw43_init(&cyw43_state);
cyw43_worker_ran_sem = xSemaphoreCreateBinary();
#if CYW43_LWIP
SemaphoreHandle_t init_sem = xSemaphoreCreateBinary();
tcpip_init(tcpip_init_done, init_sem);
xSemaphoreTake(init_sem, portMAX_DELAY);
#endif
timer_handle = xTimerCreate( "cyw43_sleep_timer", // Just a text name, not used by the kernel.
pdMS_TO_TICKS(CYW43_SLEEP_CHECK_MS),
pdTRUE, // The timers will auto-reload themselves when they expire.
NULL,
periodic_worker);
if (!timer_handle) {
return PICO_ERROR_GENERIC;
}
gpio_add_raw_irq_handler_with_order_priority(IO_IRQ_BANK0, gpio_irq_handler, CYW43_GPIO_IRQ_HANDLER_PRIORITY);
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
irq_set_enabled(IO_IRQ_BANK0, true);
cyw43_task_should_exit = false;
xTaskCreate(cyw43_task, "CYW43 Worker", configMINIMAL_STACK_SIZE, NULL, CYW43_TASK_PRIORITY, &cyw43_task_handle);
#if configUSE_CORE_AFFINITY && configNUM_CORES > 1
// the cyw43 task mus tbe on the same core so it can restore IRQs
vTaskCoreAffinitySet(cyw43_task_handle, 1 << portGET_CORE_ID());
#endif
#if configUSE_CORE_AFFINITY && configNUM_CORES > 1
vTaskCoreAffinitySet(task_handle, affinity);
#endif
return PICO_OK;
}
void cyw43_arch_deinit(void) {
assert(cyw43_core_num == get_core_num());
if (timer_handle) {
xTimerDelete(timer_handle, 0);
timer_handle = 0;
}
if (cyw43_task_handle) {
cyw43_task_should_exit = true;
signal_cyw43_task();
}
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
gpio_remove_raw_irq_handler(IO_IRQ_BANK0, gpio_irq_handler);
}
void cyw43_post_poll_hook(void) {
assert(cyw43_core_num == get_core_num());
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
}
// This is called in the gpio and low_prio_irq interrupts and on either core
static void signal_cyw43_task(void) {
if (cyw43_task_handle) {
if (portCHECK_IF_IN_ISR()) {
vTaskNotifyGiveFromISR(cyw43_task_handle, NULL);
} else {
xTaskNotifyGive(cyw43_task_handle);
}
}
}
void cyw43_schedule_internal_poll_dispatch(void (*func)(void)) {
assert(func == cyw43_poll);
signal_cyw43_task();
}
static int nesting;
// Prevent background processing in pensv and access by the other core
// These methods are called in pensv context and on either core
// They can be called recursively
void cyw43_thread_enter(void) {
// Lock the other core and stop low_prio_irq running
assert(!portCHECK_IF_IN_ISR());
#if LWIP_TCPIP_CORE_LOCKING_INPUT
// we must share their mutex otherwise we can get deadlocks with two different recursive mutexes
LOCK_TCPIP_CORE();
#else
xSemaphoreTakeRecursive(cyw43_mutex, portMAX_DELAY);
#endif
nesting++;
}
#ifndef NDEBUG
void cyw43_thread_lock_check(void) {
// Lock the other core and stop low_prio_irq running
#if LWIP_TCPIP_CORE_LOCKING_INPUT
assert(xSemaphoreGetMutexHolder(lock_tcpip_core.mut) == xTaskGetCurrentTaskHandle());
#else
assert(xSemaphoreGetMutexHolder(cyw43_mutex) == xTaskGetCurrentTaskHandle());
#endif
}
#endif
// Re-enable background processing
void cyw43_thread_exit(void) {
// Run low_prio_irq if needed
--nesting;
#if LWIP_TCPIP_CORE_LOCKING_INPUT
// we must share their mutex otherwise we can get deadlocks with two different recursive mutexes
UNLOCK_TCPIP_CORE();
#else
xSemaphoreGiveRecursive(cyw43_mutex);
#endif
if (!nesting && cyw43_task_handle != xTaskGetCurrentTaskHandle())
signal_cyw43_task();
}
void cyw43_delay_ms(uint32_t ms) {
assert(!portCHECK_IF_IN_ISR());
vTaskDelay(pdMS_TO_TICKS(ms));
}
void cyw43_delay_us(uint32_t us) {
if (us >= 1000) {
cyw43_delay_ms(us / 1000);
} else {
vTaskDelay(1);
}
}
void cyw43_arch_poll() {
}
#endif

View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include "hardware/gpio.h"
#include "hardware/irq.h"
#include "pico/sem.h"
#include "pico/cyw43_arch.h"
#include "cyw43_stats.h"
#if PICO_CYW43_ARCH_POLL
#include <lwip/init.h>
#include "lwip/timeouts.h"
#if CYW43_LWIP && !NO_SYS
#error PICO_CYW43_ARCH_POLL requires lwIP NO_SYS=1
#endif
#define CYW43_GPIO_IRQ_HANDLER_PRIORITY 0x40
#ifndef NDEBUG
uint8_t cyw43_core_num;
#endif
bool cyw43_poll_required;
// GPIO interrupt handler to tell us there's cyw43 has work to do
static void gpio_irq_handler(void)
{
uint32_t events = gpio_get_irq_event_mask(CYW43_PIN_WL_HOST_WAKE);
if (events & GPIO_IRQ_LEVEL_HIGH) {
// As we use a high level interrupt, it will go off forever until it's serviced
// So disable the interrupt until this is done. It's re-enabled again by CYW43_POST_POLL_HOOK
// which is called at the end of cyw43_poll_func
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
// also clear the force bit which we use to programmatically cause this handler to fire (on the right core)
io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ?
&iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
hw_clear_bits(&irq_ctrl_base->intf[CYW43_PIN_WL_HOST_WAKE/8], GPIO_IRQ_LEVEL_HIGH << (4 * (CYW43_PIN_WL_HOST_WAKE & 7)));
cyw43_schedule_internal_poll_dispatch(cyw43_poll);
CYW43_STAT_INC(IRQ_COUNT);
}
}
void cyw43_post_poll_hook(void) {
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
}
int cyw43_arch_init(void) {
#ifndef NDEBUG
cyw43_core_num = (uint8_t)get_core_num();
#endif
cyw43_init(&cyw43_state);
static bool done_lwip_init;
if (!done_lwip_init) {
lwip_init();
done_lwip_init = true;
}
gpio_add_raw_irq_handler_with_order_priority(IO_IRQ_BANK0, gpio_irq_handler, CYW43_GPIO_IRQ_HANDLER_PRIORITY);
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
irq_set_enabled(IO_IRQ_BANK0, true);
return 0;
}
void cyw43_arch_deinit(void) {
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
gpio_remove_raw_irq_handler(IO_IRQ_BANK0, gpio_irq_handler);
cyw43_deinit(&cyw43_state);
}
void cyw43_schedule_internal_poll_dispatch(__unused void (*func)(void)) {
cyw43_poll_required = true;
}
void cyw43_arch_poll(void)
{
CYW43_STAT_INC(LWIP_RUN_COUNT);
sys_check_timeouts();
if (cyw43_poll) {
if (cyw43_sleep > 0) {
// todo check this; but we don't want to advance too quickly
static absolute_time_t last_poll_time;
absolute_time_t current = get_absolute_time();
if (absolute_time_diff_us(last_poll_time, current) > 1000) {
if (--cyw43_sleep == 0) {
cyw43_poll_required = 1;
}
last_poll_time = current;
}
}
// todo graham i removed this because otherwise polling can do nothing during connect.
// in the polling only case, the caller is responsible for throttling how often they call anyway.
// The alternative would be to have the call to this function from the init set the poll_required flag first
// if (cyw43_poll_required) {
cyw43_poll();
// cyw43_poll_required = false;
// }
}
}
#ifndef NDEBUG
void cyw43_thread_check() {
if (__get_current_exception() || get_core_num() != cyw43_core_num) {
panic("cyw43_thread_lock_check failed");
}
}
#endif
#endif

View File

@ -0,0 +1,319 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include "pico/cyw43_arch.h"
#include "pico/mutex.h"
#include "pico/sem.h"
#include "hardware/gpio.h"
#include "hardware/irq.h"
#include "cyw43_stats.h"
#if CYW43_LWIP
#include <lwip/init.h>
#include "lwip/timeouts.h"
#endif
// note same code
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND && CYW43_LWIP && !NO_SYS
#error PICO_CYW43_ARCH_THREADSAFE_BACKGROUND requires lwIP NO_SYS=1
#endif
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND && CYW43_LWIP && MEM_LIBC_MALLOC
#error MEM_LIBC_MALLOC is incompatible with PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
#endif
// todo right now we are now always doing a cyw43_dispatch along with a lwip one when hopping cores in low_prio_irq_schedule_dispatch
#ifndef CYW43_SLEEP_CHECK_MS
#define CYW43_SLEEP_CHECK_MS 50 // How often to run lwip callback
#endif
static alarm_id_t periodic_alarm = -1;
static inline uint recursive_mutex_enter_count(recursive_mutex_t *mutex) {
return mutex->enter_count;
}
static inline lock_owner_id_t recursive_mutex_owner(recursive_mutex_t *mutex) {
return mutex->owner;
}
#define CYW43_GPIO_IRQ_HANDLER_PRIORITY 0x40
enum {
CYW43_DISPATCH_SLOT_CYW43 = 0,
CYW43_DISPATCH_SLOT_ADAPTER,
CYW43_DISPATCH_SLOT_ENUM_COUNT
};
#ifndef CYW43_DISPATCH_SLOT_COUNT
#define CYW43_DISPATCH_SLOT_COUNT CYW43_DISPATCH_SLOT_ENUM_COUNT
#endif
typedef void (*low_prio_irq_dispatch_t)(void);
static void low_prio_irq_schedule_dispatch(size_t slot, low_prio_irq_dispatch_t f);
static uint8_t cyw43_core_num;
#ifndef NDEBUG
static bool in_low_priority_irq;
#endif
static uint8_t low_priority_irq_num;
static bool low_priority_irq_missed;
static low_prio_irq_dispatch_t low_priority_irq_dispatch_slots[CYW43_DISPATCH_SLOT_COUNT];
static recursive_mutex_t cyw43_mutex;
semaphore_t cyw43_irq_sem;
// Called in low priority pendsv interrupt only to do lwip processing and check cyw43 sleep
static void periodic_worker(void)
{
#if CYW43_USE_STATS
static uint32_t counter;
if (counter++ % (30000 / LWIP_SYS_CHECK_MS) == 0) {
cyw43_dump_stats();
}
#endif
CYW43_STAT_INC(LWIP_RUN_COUNT);
#if CYW43_LWIP
sys_check_timeouts();
#endif
if (cyw43_poll) {
if (cyw43_sleep > 0) {
if (--cyw43_sleep == 0) {
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
}
}
}
}
// Regular callback to get lwip to check for timeouts
static int64_t periodic_alarm_handler(__unused alarm_id_t id, __unused void *user_data)
{
// Do lwip processing in low priority pendsv interrupt
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_ADAPTER, periodic_worker);
return CYW43_SLEEP_CHECK_MS * 1000;
}
void cyw43_await_background_or_timeout_us(uint32_t timeout_us) {
// if we are called from within an IRQ, then don't wait (we are only ever called in a polling loop)
if (!__get_current_exception()) {
sem_acquire_timeout_us(&cyw43_irq_sem, timeout_us);
}
}
// GPIO interrupt handler to tell us there's cyw43 has work to do
static void gpio_irq_handler(void)
{
uint32_t events = gpio_get_irq_event_mask(CYW43_PIN_WL_HOST_WAKE);
if (events & GPIO_IRQ_LEVEL_HIGH) {
// As we use a high level interrupt, it will go off forever until it's serviced
// So disable the interrupt until this is done. It's re-enabled again by CYW43_POST_POLL_HOOK
// which is called at the end of cyw43_poll_func
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
// also clear the force bit which we use to progratically cause this handler to fire (on the right core)
io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ?
&iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
hw_clear_bits(&irq_ctrl_base->intf[CYW43_PIN_WL_HOST_WAKE/8], GPIO_IRQ_LEVEL_HIGH << (4 * (CYW43_PIN_WL_HOST_WAKE & 7)));
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
CYW43_STAT_INC(IRQ_COUNT);
}
}
// Low priority interrupt handler to perform background processing
static void low_priority_irq_handler(void) {
assert(cyw43_core_num == get_core_num());
if (recursive_mutex_try_enter(&cyw43_mutex, NULL)) {
if (recursive_mutex_enter_count(&cyw43_mutex) != 1) {
low_priority_irq_missed = true;
CYW43_STAT_INC(PENDSV_DISABLED_COUNT);
} else {
CYW43_STAT_INC(PENDSV_RUN_COUNT);
#ifndef NDEBUG
in_low_priority_irq = true;
#endif
for (size_t i = 0; i < count_of(low_priority_irq_dispatch_slots); i++) {
if (low_priority_irq_dispatch_slots[i] != NULL) {
low_prio_irq_dispatch_t f = low_priority_irq_dispatch_slots[i];
low_priority_irq_dispatch_slots[i] = NULL;
f();
}
}
#ifndef NDEBUG
in_low_priority_irq = false;
#endif
}
recursive_mutex_exit(&cyw43_mutex);
} else {
CYW43_STAT_INC(PENDSV_DISABLED_COUNT);
low_priority_irq_missed = true;
}
sem_release(&cyw43_irq_sem);
}
static bool low_prio_irq_init(uint8_t priority) {
assert(get_core_num() == cyw43_core_num);
int irq = user_irq_claim_unused(false);
if (irq < 0) return false;
low_priority_irq_num = (uint8_t) irq;
irq_set_exclusive_handler(low_priority_irq_num, low_priority_irq_handler);
irq_set_enabled(low_priority_irq_num, true);
irq_set_priority(low_priority_irq_num, priority);
return true;
}
static void low_prio_irq_deinit(void) {
if (low_priority_irq_num > 0) {
irq_set_enabled(low_priority_irq_num, false);
irq_remove_handler(low_priority_irq_num, low_priority_irq_handler);
user_irq_unclaim(low_priority_irq_num);
low_priority_irq_num = 0;
}
}
int cyw43_arch_init(void) {
cyw43_core_num = get_core_num();
recursive_mutex_init(&cyw43_mutex);
cyw43_init(&cyw43_state);
sem_init(&cyw43_irq_sem, 0, 1);
// Start regular lwip callback to handle timeouts
periodic_alarm = add_alarm_in_us(CYW43_SLEEP_CHECK_MS * 1000, periodic_alarm_handler, NULL, true);
if (periodic_alarm < 0) {
return PICO_ERROR_GENERIC;
}
gpio_add_raw_irq_handler_with_order_priority(IO_IRQ_BANK0, gpio_irq_handler, CYW43_GPIO_IRQ_HANDLER_PRIORITY);
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
irq_set_enabled(IO_IRQ_BANK0, true);
#if CYW43_LWIP
lwip_init();
#endif
// start low priority handler (no background work is done before this)
bool ok = low_prio_irq_init(PICO_LOWEST_IRQ_PRIORITY);
if (!ok) {
cyw43_arch_deinit();
return PICO_ERROR_GENERIC;
}
return PICO_OK;
}
void cyw43_arch_deinit(void) {
if (periodic_alarm >= 0) {
cancel_alarm(periodic_alarm);
periodic_alarm = -1;
}
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
gpio_remove_raw_irq_handler(IO_IRQ_BANK0, gpio_irq_handler);
low_prio_irq_deinit();
}
void cyw43_post_poll_hook(void) {
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
}
// This is called in the gpio and low_prio_irq interrupts and on either core
static void low_prio_irq_schedule_dispatch(size_t slot, low_prio_irq_dispatch_t f) {
assert(slot < count_of(low_priority_irq_dispatch_slots));
low_priority_irq_dispatch_slots[slot] = f;
if (cyw43_core_num == get_core_num()) {
//on same core, can dispatch directly
irq_set_pending(low_priority_irq_num);
} else {
// on wrong core, so force via GPIO IRQ which itself calls this method for the CYW43 slot.
// since the CYW43 slot always uses the same function, this is fine with the addition of an
// extra (but harmless) CYW43 slot call when another SLOT is invoked.
// We could do better, but would have to track why the IRQ was called.
io_irq_ctrl_hw_t *irq_ctrl_base = cyw43_core_num ?
&iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
hw_set_bits(&irq_ctrl_base->intf[CYW43_PIN_WL_HOST_WAKE/8], GPIO_IRQ_LEVEL_HIGH << (4 * (CYW43_PIN_WL_HOST_WAKE & 7)));
}
}
void cyw43_schedule_internal_poll_dispatch(void (*func)(void)) {
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, func);
}
// Prevent background processing in pensv and access by the other core
// These methods are called in pensv context and on either core
// They can be called recursively
void cyw43_thread_enter(void) {
// Lock the other core and stop low_prio_irq running
recursive_mutex_enter_blocking(&cyw43_mutex);
}
#ifndef NDEBUG
void cyw43_thread_lock_check(void) {
// Lock the other core and stop low_prio_irq running
if (recursive_mutex_enter_count(&cyw43_mutex) < 1 || recursive_mutex_owner(&cyw43_mutex) != lock_get_caller_owner_id()) {
panic("cyw43_thread_lock_check failed");
}
}
#endif
// Re-enable background processing
void cyw43_thread_exit(void) {
// Run low_prio_irq if needed
if (1 == recursive_mutex_enter_count(&cyw43_mutex)) {
// note the outer release of the mutex is not via cyw43_exit in the low_priority_irq case (it is a direct mutex exit)
assert(!in_low_priority_irq);
// if (low_priority_irq_missed) {
// low_priority_irq_missed = false;
if (low_priority_irq_dispatch_slots[CYW43_DISPATCH_SLOT_CYW43]) {
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
}
// }
}
recursive_mutex_exit(&cyw43_mutex);
}
static void cyw43_delay_until(absolute_time_t until) {
// sleep can be called in IRQs, so there's not much we can do there
if (__get_current_exception()) {
busy_wait_until(until);
} else {
sleep_until(until);
}
}
void cyw43_delay_ms(uint32_t ms) {
cyw43_delay_until(make_timeout_time_ms(ms));
}
void cyw43_delay_us(uint32_t us) {
cyw43_delay_until(make_timeout_time_us(us));
}
void cyw43_arch_poll() {
// should not be necessary
// if (cyw43_poll) {
// low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
// }
}
#if !CYW43_LWIP
static void no_lwip_fail() {
panic("You cannot use IP with pico_cyw43_arch_none");
}
void cyw43_cb_tcpip_init(cyw43_t *self, int itf) {
}
void cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) {
}
void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {
no_lwip_fail();
}
void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {
no_lwip_fail();
}
void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) {
no_lwip_fail();
}
#endif
#endif

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// This header is included by cyw43_driver to setup its environment
#ifndef _CYW43_CONFIGPORT_H
#define _CYW43_CONFIGPORT_H
#include "pico.h"
#ifdef PICO_CYW43_ARCH_HEADER
#include __XSTRING(PICO_CYW43_ARCH_HEADER)
#else
#if PICO_CYW43_ARCH_POLL
#include "pico/cyw43_arch/arch_poll.h"
#elif PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
#include "pico/cyw43_arch/arch_threadsafe_background.h"
#elif PICO_CYW43_ARCH_FREERTOS
#include "pico/cyw43_arch/arch_freertos.h"
#else
#error must specify support pico_cyw43_arch architecture type or set PICO_CYW43_ARCH_HEADER
#endif
#endif
#ifndef CYW43_HOST_NAME
#define CYW43_HOST_NAME "PicoW"
#endif
#ifndef CYW43_GPIO
#define CYW43_GPIO 1
#endif
#ifndef CYW43_LOGIC_DEBUG
#define CYW43_LOGIC_DEBUG 0
#endif
#ifndef CYW43_USE_OTP_MAC
#define CYW43_USE_OTP_MAC 1
#endif
#ifndef CYW43_NO_NETUTILS
#define CYW43_NO_NETUTILS 1
#endif
#ifndef CYW43_IOCTL_TIMEOUT_US
#define CYW43_IOCTL_TIMEOUT_US 1000000
#endif
#ifndef CYW43_USE_STATS
#define CYW43_USE_STATS 0
#endif
// todo should this be user settable?
#ifndef CYW43_HAL_MAC_WLAN0
#define CYW43_HAL_MAC_WLAN0 0
#endif
#ifndef STATIC
#define STATIC static
#endif
#ifndef CYW43_USE_SPI
#define CYW43_USE_SPI 1
#endif
#ifndef CYW43_SPI_PIO
#define CYW43_SPI_PIO 1
#endif
#endif

View File

@ -0,0 +1,328 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_CYW43_ARCH_H
#define _PICO_CYW43_ARCH_H
#include "pico.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "cyw43.h"
#include "cyw43_country.h"
/**
* \defgroup cyw43_driver cyw43_driver
* \ingroup pico_cyw43_arch
* \brief Driver used for Pico W wireless
*/
/**
* \defgroup cyw43_ll cyw43_ll
* \ingroup cyw43_driver
* \brief Low Level CYW43 driver interface
*/
/** \file pico/cyw43_arch.h
* \defgroup pico_cyw43_arch pico_cyw43_arch
*
* Architecture for integrating the CYW43 driver (for the wireless on Pico W) and lwIP (for TCP/IP stack) into the SDK. It is also necessary for accessing the on-board LED on Pico W
*
* Both the low level \c cyw43_driver and the lwIP stack require periodic servicing, and have limitations
* on whether they can be called from multiple cores/threads.
*
* \c pico_cyw43_arch attempts to abstract these complications into several behavioral groups:
*
* * \em 'poll' - This not multi-core/IRQ safe, and requires the user to call \ref cyw43_arch_poll periodically from their main loop
* * \em 'thread_safe_background' - This is multi-core/thread/task safe, and maintenance of the driver and TCP/IP stack is handled automatically in the background
*
* As of right now, lwIP is the only supported TCP/IP stack, however the use of \c pico_cyw43_arch is intended to be independent of
* the particular TCP/IP stack used (and possibly Bluetooth stack used) in the future. For this reason, the integration of lwIP
* is handled in the base (\c pico_cyw43_arch) library based on the #define \ref CYW43_LWIP used by the \c cyw43_driver.
*
* Whilst you can use the \c pico_cyw43_arch library directly and specify \ref CYW$#_LWIP (and other defines) yourself, several
* other libraries are made available to the build which aggregate the defines and other dependencies for you:
*
* * \b pico_cyw43_arch_lwip_poll - For using the RAW lwIP API (in `NO_SYS=1` mode) without any background processing or multi-core/thread safety.
*
* The user must call \ref pico_cyw43_poll periodically from their main loop.
*
* This wrapper library:
* - Sets \c CYW43_LWIP=1 to enable lwIP support in \c pico_cyw43_arch and \c cyw43_driver.
* - Sets \c PICO_CYW43_ARCH_POLL=1 to select the polling behavior.
* - Adds the \c pico_lwip as a dependency to pull in lwIP.
*
* * \b pico_cyw43_arch_lwip_threadsafe_background - For using the RAW lwIP API (in `NO_SYS=1` mode) with multi-core/thread safety, and automatic servicing of the \c cyw43_driver and
* lwIP in background.
*
* Calls into the \c cyw43_driver high level API (cyw43.h) may be made from either core or from lwIP callbacks, however calls into lwIP (which
* is not thread-safe) other than those made from lwIP callbacks, must be bracketed with \ref cyw43_arch_lwip_begin and \ref cyw43_arch_lwip_end. It is fine to bracket
* calls made from within lwIP callbacks too; you just don't have to.
*
* \note lwIP callbacks happen in a (low priority) IRQ context (similar to an alarm callback), so care should be taken when interacting
* with other code.
*
* This wrapper library:
* - Sets \c CYW43_LWIP=1 to enable lwIP support in \c pico_cyw43_arch and \c cyw43_driver
* - Sets \c PICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1 to select the thread-safe/non-polling behavior.
* - Adds the pico_lwip as a dependency to pull in lwIP.
*
*
* This library \em can also be used under the RP2040 port of FreeRTOS with lwIP in `NO_SYS=1` mode (allowing you to call \c cyw43_driver APIs
* from any task, and to call lwIP from lwIP callbacks, or from any task if you bracket the calls with \ref cyw43_arch_lwip_begin and \ref cyw43_arch_lwip_end. Again, you should be
* careful about what you do in lwIP callbacks, as you cannot call most FreeRTOS APIs from within an IRQ context. Unless you have good reason, you should probably
* use the full FreeRTOS integration (with `NO_SYS=0`) provided by \c pico_cyw43_arch_lwip_sys_freertos.
*
* * \b pico_cyw43_arch_lwip_sys_freertos - For using the full lwIP API including blocking sockets in OS (`NO_SYS=0`) mode, along with with multi-core/task/thread safety, and automatic servicing of the \c cyw43_driver and
* the lwIP stack.
*
* This wrapper library:
* - Sets \c CYW43_LWIP=1 to enable lwIP support in \c pico_cyw43_arch and \c cyw43_driver.
* - Sets \c PICO_CYW43_ARCH_FREERTOS=1 to select the NO_SYS=0 lwip/FreeRTOS integration
* - Sets \c LWIP_PROVIDE_ERRNO=1 to provide error numbers needed for compilation without an OS
* - Adds the \c pico_lwip as a dependency to pull in lwIP.
* - Adds the lwIP/FreeRTOS code from lwip-contrib (in the contrib directory of lwIP)
*
* Calls into the \c cyw43_driver high level API (cyw43.h) may be made from any task or from lwIP callbacks, but not from IRQs. Calls into the lwIP RAW API (which is not thread safe)
* must be bracketed with \ref cyw43_arch_lwip_begin and \ref cyw43_arch_lwip_end. It is fine to bracket calls made from within lwIP callbacks too; you just don't have to.
*
* \note this wrapper library requires you to link FreeRTOS functionality with your application yourself.
*
* * \b pico_cyw43_arch_none - If you do not need the TCP/IP stack but wish to use the on-board LED.
*
* This wrapper library:
* - Sets \c CYW43_LWIP=0 to disable lwIP support in \c pico_cyw43_arch and \c cyw43_driver
*/
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_CYW43_ARCH, Enable/disable assertions in the pico_cyw43_arch module, type=bool, default=0, group=pico_cyw43_arch
#ifndef PARAM_ASSERTIONS_ENABLED_CYW43_ARCH
#define PARAM_ASSERTIONS_ENABLED_CYW43_ARCH 0
#endif
// PICO_CONFIG: CYW43_ARCH_DEBUG_ENABLED, Enable/disable some debugging output in the pico_cyw43_arch module, type=bool, default=1 in debug builds, group=pico_cyw43_arch
#ifndef CYW43_ARCH_DEBUG_ENABLED
#ifndef NDEBUG
#define CYW43_ARCH_DEBUG_ENABLED 1
#else
#define CYW43_ARCH_DEBUG_ENABLED 0
#endif
#endif
// PICO_CONFIG: PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE, Default country code for the cyw43 wireless driver, default=CYW43_COUNTRY_WORLDWIDE, group=pico_cyw43_arch
#ifndef PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE
#define PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE CYW43_COUNTRY_WORLDWIDE
#endif
/*!
* \brief Initialize the CYW43 architecture
* \ingroup pico_cyw43_arch
*
* This method initializes the `cyw43_driver` code and initializes the lwIP stack (if it
* was enabled at build time). This method must be called prior to using any other \c pico_cyw43_arch,
* \cyw43_driver or lwIP functions.
*
* \note this method initializes wireless with a country code of \c PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE
* which defaults to \c CYW43_COUNTRY_WORLDWIDE. Worldwide settings may not give the best performance; consider
* setting PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE to a different value or calling \ref cyw43_arch_init_with_country
* \return 0 if the initialization is successful, an error code otherwise \see pico_error_codes
*/
int cyw43_arch_init(void);
/*!
* \brief Initialize the CYW43 architecture for use in a specific country
* \ingroup pico_cyw43_arch
*
* This method initializes the `cyw43_driver` code and initializes the lwIP stack (if it
* was enabled at build time). This method must be called prior to using any other \c pico_cyw43_arch,
* \cyw43_driver or lwIP functions.
*
* \param country the country code to use (see \ref CYW43_COUNTRY_)
* \return 0 if the initialization is successful, an error code otherwise \see pico_error_codes
*/
int cyw43_arch_init_with_country(uint32_t country);
/*!
* \brief Enables Wi-Fi STA (Station) mode.
* \ingroup pico_cyw43_arch
*
* This enables the Wi-Fi in \emStation mode such that connections can be made to other Wi-Fi Access Points
*/
void cyw43_arch_enable_sta_mode(void);
/*!
* \brief Enables Wi-Fi AP (Access point) mode.
* \ingroup pico_cyw43_arch
*
* This enables the Wi-Fi in \em Access \em Point mode such that connections can be made to the device by other Wi-Fi clients
* \param ssid the name for the access point
* \param password the password to use or NULL for no password.
* \param auth the authorization type to use when the password is enabled. Values are \ref CYW43_AUTH_WPA_TKIP_PSK,
* \ref CYW43_AUTH_WPA2_AES_PSK, or \ref CYW43_AUTH_WPA2_MIXED_PSK (see \ref CYW43_AUTH_)
*/
void cyw43_arch_enable_ap_mode(const char *ssid, const char *password, uint32_t auth);
/*!
* \brief De-initialize the CYW43 architecture
* \ingroup pico_cyw43_arch
*
* This method de-initializes the `cyw43_driver` code and de-initializes the lwIP stack (if it
* was enabled at build time). Note this method should always be called from the same core (or RTOS
* task, depending on the environment) as \ref cyw43_arch_init.
*/
void cyw43_arch_deinit(void);
/*!
* \brief Attempt to connect to a wireless access point, blocking until the network is joined or a failure is detected.
* \ingroup pico_cyw43_arch
*
* \param ssid the network name to connect to
* \param password the network password or NULL if there is no password required
* \param auth the authorization type to use when the password is enabled. Values are \ref CYW43_AUTH_WPA_TKIP_PSK,
* \ref CYW43_AUTH_WPA2_AES_PSK, or \ref CYW43_AUTH_WPA2_MIXED_PSK (see \ref CYW43_AUTH_)
*
* \return 0 if the initialization is successful, an error code otherwise \see pico_error_codes
*/
int cyw43_arch_wifi_connect_blocking(const char *ssid, const char *pw, uint32_t auth);
/*!
* \brief Attempt to connect to a wireless access point, blocking until the network is joined, a failure is detected or a timeout occurs
* \ingroup pico_cyw43_arch
*
* \param ssid the network name to connect to
* \param password the network password or NULL if there is no password required
* \param auth the authorization type to use when the password is enabled. Values are \ref CYW43_AUTH_WPA_TKIP_PSK,
* \ref CYW43_AUTH_WPA2_AES_PSK, or \ref CYW43_AUTH_WPA2_MIXED_PSK (see \ref CYW43_AUTH_)
*
* \return 0 if the initialization is successful, an error code otherwise \see pico_error_codes
*/
int cyw43_arch_wifi_connect_timeout_ms(const char *ssid, const char *pw, uint32_t auth, uint32_t timeout);
/*!
* \brief Start attempting to connect to a wireless access point
* \ingroup pico_cyw43_arch
*
* This method tells the CYW43 driver to start connecting to an access point. You should subsequently check the
* status by calling \ref cyw43_wifi_link_status.
*
* \param ssid the network name to connect to
* \param password the network password or NULL if there is no password required
* \param auth the authorization type to use when the password is enabled. Values are \ref CYW43_AUTH_WPA_TKIP_PSK,
* \ref CYW43_AUTH_WPA2_AES_PSK, or \ref CYW43_AUTH_WPA2_MIXED_PSK (see \ref CYW43_AUTH_)
*
* \return 0 if the scan was started successfully, an error code otherwise \see pico_error_codes
*/
int cyw43_arch_wifi_connect_async(const char *ssid, const char *pw, uint32_t auth);
/*!
* \brief Return the country code used to initialize cyw43_arch
* \ingroup pico_cyw43_arch
*
* \return the country code (see \ref CYW43_COUNTRY_)
*/
uint32_t cyw43_arch_get_country_code(void);
/*!
* \brief Set a GPIO pin on the wireless chip to a given value
* \ingroup pico_cyw43_arch
* \note this method does not check for errors setting the GPIO. You can use the lower level \ref cyw43_gpio_set instead if you wish
* to check for errors.
*
* \param wl_gpio the GPIO number on the wireless chip
* \param value true to set the GPIO, false to clear it.
*/
void cyw43_arch_gpio_put(uint wl_gpio, bool value);
/*!
* \brief Read the value of a GPIO pin on the wireless chip
* \ingroup pico_cyw43_arch
* \note this method does not check for errors setting the GPIO. You can use the lower level \ref cyw43_gpio_get instead if you wish
* to check for errors.
*
* \param wl_gpio the GPIO number on the wireless chip
* \return true if the GPIO is high, false otherwise
*/
bool cyw43_arch_gpio_get(uint wl_gpio);
/*!
* \brief Perform any processing required by the \c cyw43_driver or the TCP/IP stack
* \ingroup pico_cyw43_arch
*
* This method must be called periodically from the main loop when using a
* \em polling style \c pico_cyw43_arch (e.g. \c pico_cyw43_arch_lwip_poll ). It
* may be called in other styles, but it is unnecessary to do so.
*/
void cyw43_arch_poll(void);
/*!
* \fn cyw43_arch_lwip_begin
* \brief Acquire any locks required to call into lwIP
* \ingroup pico_cyw43_arch
*
* The lwIP API is not thread safe. You should surround calls into the lwIP API
* with calls to this method and \ref cyw43_arch_lwip_end. Note these calls are not
* necessary (but harmless) when you are calling back into the lwIP API from an lwIP callback.
* If you are using single-core polling only (pico_cyw43_arch_poll) then these calls are no-ops
* anyway it is good practice to call them anyway where they are necessary.
*
* \sa cyw43_arch_lwip_end
* \sa cyw43_arch_lwip_protect
*/
/*!
* \fn void cyw43_arch_lwip_end(void)
* \brief Release any locks required for calling into lwIP
* \ingroup pico_cyw43_arch
*
* The lwIP API is not thread safe. You should surround calls into the lwIP API
* with calls to \ref cyw43_arch_lwip_begin and this method. Note these calls are not
* necessary (but harmless) when you are calling back into the lwIP API from an lwIP callback.
* If you are using single-core polling only (pico_cyw43_arch_poll) then these calls are no-ops
* anyway it is good practice to call them anyway where they are necessary.
*
* \sa cyw43_arch_lwip_begin
* \sa cyw43_arch_lwip_protect
*/
/*!
* \fn int cyw43_arch_lwip_protect(int (*func)(void *param), void *param)
* \brief sad Release any locks required for calling into lwIP
* \ingroup pico_cyw43_arch
*
* The lwIP API is not thread safe. You can use this method to wrap a function
* with any locking required to call into the lwIP API. If you are using
* single-core polling only (pico_cyw43_arch_poll) then there are no
* locks to required, but it is still good practice to use this function.
*
* \param func the function ta call with any required locks held
* \param param parameter to pass to \c func
* \return the return value from \c func
* \sa cyw43_arch_lwip_begin
* \sa cyw43_arch_lwip_end
*/
/*!
* \fn void cyw43_arch_lwip_check(void)
* \brief Checks the caller has any locks required for calling into lwIP
* \ingroup pico_cyw43_arch
*
* The lwIP API is not thread safe. You should surround calls into the lwIP API
* with calls to \ref cyw43_arch_lwip_begin and this method. Note these calls are not
* necessary (but harmless) when you are calling back into the lwIP API from an lwIP callback.
*
* This method will assert in debug mode, if the above conditions are not met (i.e. it is not safe to
* call into the lwIP API)
*
* \sa cyw43_arch_lwip_begin
* \sa cyw43_arch_lwip_protect
*/
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_CYW43_ARCH_ARCH_COMMON_H
#define _PICO_CYW43_ARCH_ARCH_COMMON_H
#include "pico.h"
#include "pico/time.h"
#include "hardware/gpio.h"
#include "pico/error.h"
#ifdef __cplusplus
extern "C" {
#endif
// Note, these are negated, because cyw43_driver negates them before returning!
#define CYW43_EPERM (-PICO_ERROR_NOT_PERMITTED) // Operation not permitted
#define CYW43_EIO (-PICO_ERROR_IO) // I/O error
#define CYW43_EINVAL (-PICO_ERROR_INVALID_ARG) // Invalid argument
#define CYW43_ETIMEDOUT (-PICO_ERROR_TIMEOUT) // Connection timed out
#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT
#define cyw43_hal_pin_obj_t uint
// get the number of elements in a fixed-size array
#define CYW43_ARRAY_SIZE(a) count_of(a)
static inline uint32_t cyw43_hal_ticks_us(void) {
return time_us_32();
}
static inline uint32_t cyw43_hal_ticks_ms(void) {
return to_ms_since_boot(get_absolute_time());
}
static inline int cyw43_hal_pin_read(cyw43_hal_pin_obj_t pin) {
return gpio_get(pin);
}
static inline void cyw43_hal_pin_low(cyw43_hal_pin_obj_t pin) {
gpio_clr_mask(1 << pin);
}
static inline void cyw43_hal_pin_high(cyw43_hal_pin_obj_t pin) {
gpio_set_mask(1 << pin);
}
#define CYW43_HAL_PIN_MODE_INPUT (GPIO_IN)
#define CYW43_HAL_PIN_MODE_OUTPUT (GPIO_OUT)
#define CYW43_HAL_PIN_PULL_NONE (0)
#define CYW43_HAL_PIN_PULL_UP (1)
#define CYW43_HAL_PIN_PULL_DOWN (2)
static inline void cyw43_hal_pin_config(cyw43_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, __unused uint32_t alt) {
assert((mode == CYW43_HAL_PIN_MODE_INPUT || mode == CYW43_HAL_PIN_MODE_OUTPUT) && alt == 0);
gpio_set_dir(pin, mode);
gpio_set_pulls(pin, pull == CYW43_HAL_PIN_PULL_UP, pull == CYW43_HAL_PIN_PULL_DOWN);
}
void cyw43_hal_get_mac(int idx, uint8_t buf[6]);
void cyw43_hal_generate_laa_mac(int idx, uint8_t buf[6]);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _EXAMPLE_CYW43_ARCH_ARCH_FREERTOS_SYS_H
#define _EXAMPLE_CYW43_ARCH_ARCH_FREERTOS_SYS_H
#include "pico/cyw43_arch/arch_common.h"
#ifdef __cplusplus
extern "C" {
#endif
void cyw43_thread_enter(void);
void cyw43_thread_exit(void);
#define CYW43_THREAD_ENTER cyw43_thread_enter();
#define CYW43_THREAD_EXIT cyw43_thread_exit();
#ifndef NDEBUG
void cyw43_thread_lock_check(void);
#define cyw43_arch_lwip_check() cyw43_thread_lock_check()
#define CYW43_THREAD_LOCK_CHECK cyw43_arch_lwip_check();
#else
#define cyw43_arch_lwip_check() ((void)0)
#define CYW43_THREAD_LOCK_CHECK
#endif
void cyw43_await_background_or_timeout_us(uint32_t timeout_us);
// todo not 100% sure about the timeouts here; MP uses __WFI which will always wakeup periodically
#define CYW43_SDPCM_SEND_COMMON_WAIT cyw43_await_background_or_timeout_us(1000);
#define CYW43_DO_IOCTL_WAIT cyw43_await_background_or_timeout_us(1000);
void cyw43_delay_ms(uint32_t ms);
void cyw43_delay_us(uint32_t us);
void cyw43_schedule_internal_poll_dispatch(void (*func)(void));
void cyw43_post_poll_hook(void);
#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook();
static inline void cyw43_arch_lwip_begin(void) {
cyw43_thread_enter();
}
static inline void cyw43_arch_lwip_end(void) {
cyw43_thread_exit();
}
static inline int cyw43_arch_lwip_protect(int (*func)(void *param), void *param) {
cyw43_arch_lwip_begin();
int rc = func(param);
cyw43_arch_lwip_end();
return rc;
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_CYW43_ARCH_ARCH_POLL_H
#define _PICO_CYW43_ARCH_ARCH_POLL_H
#include "pico/cyw43_arch/arch_common.h"
#include <assert.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CYW43_THREAD_ENTER
#define CYW43_THREAD_EXIT
#ifndef NDEBUG
void cyw43_thread_check(void);
#define cyw43_arch_lwip_check() cyw43_thread_check()
#define CYW43_THREAD_LOCK_CHECK cyw43_arch_lwip_check();
#else
#define cyw43_arch_lwip_check() ((void)0)
#define CYW43_THREAD_LOCK_CHECK
#endif
#define CYW43_SDPCM_SEND_COMMON_WAIT cyw43_poll_required = true;
#define CYW43_DO_IOCTL_WAIT cyw43_poll_required = true;
#define cyw43_delay_ms sleep_ms
#define cyw43_delay_us sleep_us
void cyw43_schedule_internal_poll_dispatch(void (*func)(void));
void cyw43_post_poll_hook(void);
extern bool cyw43_poll_required;
#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook();
#endif
#ifndef DOXYGEN_GENERATION // multiple definitions in separate headers seems to confused doxygen
#define cyw43_arch_lwip_begin() ((void)0)
#define cyw43_arch_lwip_end() ((void)0)
static inline int cyw43_arch_lwip_protect(int (*func)(void *param), void *param) {
return func(param);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_CYW43_ARCH_ARCH_THREADSAFE_BACKGROUND_H
#define _PICO_CYW43_ARCH_ARCH_THREADSAFE_BACKGROUND_H
#include "pico/cyw43_arch/arch_common.h"
#ifdef __cplusplus
extern "C" {
#endif
void cyw43_thread_enter(void);
void cyw43_thread_exit(void);
#define CYW43_THREAD_ENTER cyw43_thread_enter();
#define CYW43_THREAD_EXIT cyw43_thread_exit();
#ifndef NDEBUG
void cyw43_thread_lock_check(void);
#define cyw43_arch_lwip_check() cyw43_thread_lock_check()
#define CYW43_THREAD_LOCK_CHECK cyw43_arch_lwip_check();
#else
#define cyw43_arch_lwip_check() ((void)0)
#define CYW43_THREAD_LOCK_CHECK
#endif
void cyw43_await_background_or_timeout_us(uint32_t timeout_us);
// todo not 100% sure about the timeouts here; MP uses __WFI which will always wakeup periodically
#define CYW43_SDPCM_SEND_COMMON_WAIT cyw43_await_background_or_timeout_us(1000);
#define CYW43_DO_IOCTL_WAIT cyw43_await_background_or_timeout_us(1000);
void cyw43_delay_ms(uint32_t ms);
void cyw43_delay_us(uint32_t us);
void cyw43_schedule_internal_poll_dispatch(void (*func)(void));
void cyw43_post_poll_hook(void);
#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook();
static inline void cyw43_arch_lwip_begin(void) {
cyw43_thread_enter();
}
static inline void cyw43_arch_lwip_end(void) {
cyw43_thread_exit();
}
static inline int cyw43_arch_lwip_protect(int (*func)(void *param), void *param) {
cyw43_arch_lwip_begin();
int rc = func(param);
cyw43_arch_lwip_end();
return rc;
}
#ifdef __cplusplus
}
#endif
#endif