esp-idf/components/lwip/port/freertos/sys_arch.c

573 lines
13 KiB
C
Raw Normal View History

2016-08-17 11:08:22 -04:00
/*
* SPDX-FileCopyrightText: 2001-2003 Swedish Institute of Computer Science
2016-08-17 11:08:22 -04:00
*
* SPDX-License-Identifier: BSD-3-Clause
2016-08-17 11:08:22 -04:00
*
* SPDX-FileContributor: 2018-2024 Espressif Systems (Shanghai) CO LTD
2016-08-17 11:08:22 -04:00
*/
/* lwIP includes. */
#include <pthread.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
2016-08-17 11:08:22 -04:00
#include "lwip/debug.h"
#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/mem.h"
#include "lwip/stats.h"
#include "arch/sys_arch.h"
#include "arch/vfs_lwip.h"
#include "esp_log.h"
components/esp_common: added esp_macros.h that aims to hold useful macros esp_common/esp_compiler: renamed esp_macros file to a more specific one esp_common/esp_compiler: removed CONTAINER_OF macro, it was a duplicate components/freertos: placed likely macros around port and critical sections component/freertos: placed likely macros on lists module components/freertos: placed unlikely macros inside of assertion points, they likely wont fail components/freertos: added likely macros on queue modules FreeRTOS queues are one of most hot code path, because to queues itself tend to be used a lot by the applications, besides that, queues are the basic primitive to form both mutexes and semaphores, The focus here is to place likely macros inside lowest level send and receive routines, since they're common from all kobjects: semaphores, queues, mutexes and FR internals (like timer queue) components/lwip: placed likely/unlikey on net-interfaces code components/fatfs: added unlikely macros on disk drivers code components/spiffs: added unlikely macros on low level fs driver components/freertos: added likely/unlikely macros on timers and ticker freertos/event_group: placed likely/unlikely macros on hot event group code paths components/sdmmc: placed likely / unlikely macros on lower level path of sdmmc components/bt: placed unlikely macros around bt HCI functions calling components/lwip: added likely/unlikely macros on OS port code section components/freertos: fix code style on tick handler
2019-10-15 17:01:05 -04:00
#include "esp_compiler.h"
2016-08-17 11:08:22 -04:00
static const char* TAG = "lwip_arch";
2016-08-17 11:08:22 -04:00
static sys_mutex_t g_lwip_protect_mutex = NULL;
static pthread_key_t sys_thread_sem_key;
static void sys_thread_sem_free(void* data);
2016-08-17 11:08:22 -04:00
#if !LWIP_COMPAT_MUTEX
/**
* @brief Create a new mutex
*
* @param pxMutex pointer of the mutex to create
* @return ERR_OK on success, ERR_MEM when out of memory
*/
2016-08-17 11:08:22 -04:00
err_t
sys_mutex_new(sys_mutex_t *pxMutex)
{
*pxMutex = xSemaphoreCreateMutex();
if (*pxMutex == NULL) {
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_new: out of mem\r\n"));
return ERR_MEM;
2016-08-17 11:08:22 -04:00
}
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_new: m=%p\n", *pxMutex));
2016-08-17 11:08:22 -04:00
return ERR_OK;
2016-08-17 11:08:22 -04:00
}
/**
* @brief Lock a mutex
*
* @param pxMutex pointer of mutex to lock
*/
void
2016-08-17 11:08:22 -04:00
sys_mutex_lock(sys_mutex_t *pxMutex)
{
BaseType_t ret = xSemaphoreTake(*pxMutex, portMAX_DELAY);
2016-08-17 11:08:22 -04:00
LWIP_ASSERT("failed to take the mutex", ret == pdTRUE);
(void)ret;
2016-08-17 11:08:22 -04:00
}
/**
* @brief Unlock a mutex
*
* @param pxMutex pointer of mutex to unlock
*/
void
2016-08-17 11:08:22 -04:00
sys_mutex_unlock(sys_mutex_t *pxMutex)
{
BaseType_t ret = xSemaphoreGive(*pxMutex);
LWIP_ASSERT("failed to give the mutex", ret == pdTRUE);
(void)ret;
2016-08-17 11:08:22 -04:00
}
/**
* @brief Delete a mutex
*
* @param pxMutex pointer of mutex to delete
*/
2016-08-17 11:08:22 -04:00
void
sys_mutex_free(sys_mutex_t *pxMutex)
{
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_free: m=%p\n", *pxMutex));
vSemaphoreDelete(*pxMutex);
*pxMutex = NULL;
2016-08-17 11:08:22 -04:00
}
#endif /* !LWIP_COMPAT_MUTEX */
/**
* @brief Creates a new semaphore
*
* @param sem pointer of the semaphore
* @param count initial state of the semaphore
* @return err_t
*/
2016-08-17 11:08:22 -04:00
err_t
sys_sem_new(sys_sem_t *sem, u8_t count)
{
LWIP_ASSERT("initial_count invalid (neither 0 nor 1)",
(count == 0) || (count == 1));
2016-08-17 11:08:22 -04:00
*sem = xSemaphoreCreateBinary();
if (*sem == NULL) {
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_sem_new: out of mem\r\n"));
return ERR_MEM;
}
2016-08-17 11:08:22 -04:00
if (count == 1) {
BaseType_t ret = xSemaphoreGive(*sem);
LWIP_ASSERT("sys_sem_new: initial give failed", ret == pdTRUE);
(void)ret;
2016-08-17 11:08:22 -04:00
}
return ERR_OK;
2016-08-17 11:08:22 -04:00
}
/**
* @brief Signals a semaphore
*
* @param sem pointer of the semaphore
*/
void
2016-08-17 11:08:22 -04:00
sys_sem_signal(sys_sem_t *sem)
{
BaseType_t ret = xSemaphoreGive(*sem);
/* queue full is OK, this is a signal only... */
LWIP_ASSERT("sys_sem_signal: sane return value",
(ret == pdTRUE) || (ret == errQUEUE_FULL));
(void)ret;
2016-08-17 11:08:22 -04:00
}
2018-05-03 04:41:10 -04:00
/*-----------------------------------------------------------------------------------*/
// Signals a semaphore (from ISR)
int
sys_sem_signal_isr(sys_sem_t *sem)
2018-05-03 04:41:10 -04:00
{
BaseType_t woken = pdFALSE;
xSemaphoreGiveFromISR(*sem, &woken);
return woken == pdTRUE;
}
/**
* @brief Wait for a semaphore to be signaled
*
* @param sem pointer of the semaphore
* @param timeout if zero, will wait infinitely, or will wait at least for milliseconds specified by this argument
* @return SYS_ARCH_TIMEOUT when timeout, 0 otherwise
*/
u32_t
2016-08-17 11:08:22 -04:00
sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
BaseType_t ret;
2016-08-17 11:08:22 -04:00
if (!timeout) {
/* wait infinite */
ret = xSemaphoreTake(*sem, portMAX_DELAY);
LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
} else {
/* Round up the number of ticks.
* Not only we need to round up the number of ticks, but we also need to add 1.
* Indeed, this function shall wait for AT LEAST timeout, but on FreeRTOS,
* if we specify a timeout of 1 tick to `xSemaphoreTake`, it will take AT MOST
* 1 tick before triggering a timeout. Thus, we need to pass 2 ticks as a timeout
* to `xSemaphoreTake`. */
TickType_t timeout_ticks = ((timeout + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS) + 1;
ret = xSemaphoreTake(*sem, timeout_ticks);
if (ret == errQUEUE_EMPTY) {
/* timed out */
return SYS_ARCH_TIMEOUT;
2016-08-17 11:08:22 -04:00
}
LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
2016-08-17 11:08:22 -04:00
}
return 0;
2016-08-17 11:08:22 -04:00
}
/**
* @brief Delete a semaphore
*
* @param sem pointer of the semaphore to delete
*/
2016-08-17 11:08:22 -04:00
void
sys_sem_free(sys_sem_t *sem)
{
vSemaphoreDelete(*sem);
*sem = NULL;
2016-08-17 11:08:22 -04:00
}
/**
* @brief Create an empty mailbox.
*
* @param mbox pointer of the mailbox
* @param size size of the mailbox
* @return ERR_OK on success, ERR_MEM when out of memory
*/
2016-08-17 11:08:22 -04:00
err_t
sys_mbox_new(sys_mbox_t *mbox, int size)
{
*mbox = mem_malloc(sizeof(struct sys_mbox_s));
2016-08-17 11:08:22 -04:00
if (*mbox == NULL){
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new *mbox\n"));
2016-08-17 11:08:22 -04:00
return ERR_MEM;
}
(*mbox)->os_mbox = xQueueCreate(size, sizeof(void *));
if ((*mbox)->os_mbox == NULL) {
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new (*mbox)->os_mbox\n"));
2016-08-17 11:08:22 -04:00
free(*mbox);
return ERR_MEM;
}
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("new *mbox ok mbox=%p os_mbox=%p\n", *mbox, (*mbox)->os_mbox));
2016-08-17 11:08:22 -04:00
return ERR_OK;
}
/**
* @brief Send message to mailbox
*
* @param mbox pointer of the mailbox
* @param msg pointer of the message to send
*/
void
2016-08-17 11:08:22 -04:00
sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
BaseType_t ret = xQueueSendToBack((*mbox)->os_mbox, &msg, portMAX_DELAY);
LWIP_ASSERT("mbox post failed", ret == pdTRUE);
(void)ret;
2016-08-17 11:08:22 -04:00
}
/**
* @brief Try to post a message to mailbox
*
* @param mbox pointer of the mailbox
* @param msg pointer of the message to send
* @return ERR_OK on success, ERR_MEM when mailbox is full
*/
err_t
2016-08-17 11:08:22 -04:00
sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
err_t xReturn;
if (xQueueSend((*mbox)->os_mbox, &msg, 0) == pdTRUE) {
2016-08-17 11:08:22 -04:00
xReturn = ERR_OK;
} else {
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("trypost mbox=%p fail\n", (*mbox)->os_mbox));
2016-08-17 11:08:22 -04:00
xReturn = ERR_MEM;
}
return xReturn;
}
/**
* @brief Try to post a message to mailbox from ISR
*
* @param mbox pointer of the mailbox
* @param msg pointer of the message to send
* @return ERR_OK on success
* ERR_MEM when mailbox is full
* ERR_NEED_SCHED when high priority task wakes up
*/
err_t
sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
2016-08-17 11:08:22 -04:00
{
BaseType_t ret;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
2016-08-17 11:08:22 -04:00
ret = xQueueSendFromISR((*mbox)->os_mbox, &msg, &xHigherPriorityTaskWoken);
if (ret == pdTRUE) {
if (xHigherPriorityTaskWoken == pdTRUE) {
return ERR_NEED_SCHED;
}
return ERR_OK;
} else {
LWIP_ASSERT("mbox trypost failed", ret == errQUEUE_FULL);
return ERR_MEM;
2016-08-17 11:08:22 -04:00
}
}
2016-08-17 11:08:22 -04:00
/**
* @brief Fetch message from mailbox
*
* @param mbox pointer of mailbox
* @param msg pointer of the received message, could be NULL to indicate the message should be dropped
* @param timeout if zero, will wait infinitely; or will wait milliseconds specify by this argument
* @return SYS_ARCH_TIMEOUT when timeout, 0 otherwise
*/
u32_t
sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
BaseType_t ret;
void *msg_dummy;
if (msg == NULL) {
msg = &msg_dummy;
2016-08-17 11:08:22 -04:00
}
if (timeout == 0) {
/* wait infinite */
ret = xQueueReceive((*mbox)->os_mbox, &(*msg), portMAX_DELAY);
LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
} else {
TickType_t timeout_ticks = timeout / portTICK_PERIOD_MS;
ret = xQueueReceive((*mbox)->os_mbox, &(*msg), timeout_ticks);
if (ret == errQUEUE_EMPTY) {
/* timed out */
*msg = NULL;
return SYS_ARCH_TIMEOUT;
2016-08-17 11:08:22 -04:00
}
LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
2016-08-17 11:08:22 -04:00
}
return 0;
2016-08-17 11:08:22 -04:00
}
/**
* @brief try to fetch message from mailbox
*
* @param mbox pointer of mailbox
* @param msg pointer of the received message
* @return SYS_MBOX_EMPTY if mailbox is empty, 1 otherwise
*/
2016-08-17 11:08:22 -04:00
u32_t
sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
BaseType_t ret;
void *msg_dummy;
2016-08-17 11:08:22 -04:00
if (msg == NULL) {
msg = &msg_dummy;
2016-08-17 11:08:22 -04:00
}
ret = xQueueReceive((*mbox)->os_mbox, &(*msg), 0);
if (ret == errQUEUE_EMPTY) {
*msg = NULL;
return SYS_MBOX_EMPTY;
2016-08-17 11:08:22 -04:00
}
LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
2016-08-17 11:08:22 -04:00
return 0;
2016-08-17 11:08:22 -04:00
}
/**
* @brief Delete a mailbox
*
* @param mbox pointer of the mailbox to delete
*/
2016-08-17 11:08:22 -04:00
void
sys_mbox_free(sys_mbox_t *mbox)
{
if ((NULL == mbox) || (NULL == *mbox)) {
return;
}
UBaseType_t msgs_waiting = uxQueueMessagesWaiting((*mbox)->os_mbox);
LWIP_ASSERT("mbox quence not empty", msgs_waiting == 0);
2016-08-17 11:08:22 -04:00
vQueueDelete((*mbox)->os_mbox);
free(*mbox);
*mbox = NULL;
(void)msgs_waiting;
2016-08-17 11:08:22 -04:00
}
/**
* @brief Create a new thread
*
* @param name thread name
* @param thread thread function
* @param arg thread arguments
* @param stacksize stacksize of the thread
* @param prio priority of the thread
* @return thread ID
*/
2016-08-17 11:08:22 -04:00
sys_thread_t
sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
{
TaskHandle_t rtos_task;
BaseType_t ret;
2016-08-17 11:08:22 -04:00
/* LwIP's lwip_thread_fn matches FreeRTOS' TaskFunction_t, so we can pass the
thread function without adaption here. */
ret = xTaskCreatePinnedToCore(thread, name, stacksize, arg, prio, &rtos_task,
CONFIG_LWIP_TCPIP_TASK_AFFINITY);
2016-08-17 11:08:22 -04:00
2023-04-05 10:45:45 -04:00
LWIP_DEBUGF(TCPIP_DEBUG, ("new lwip task : %" U32_F ", prio:%d,stack:%d\n",
(u32_t)rtos_task, prio, stacksize));
if (ret != pdTRUE) {
2016-08-17 11:08:22 -04:00
return NULL;
}
return (sys_thread_t)rtos_task;
2016-08-17 11:08:22 -04:00
}
/**
* @brief Initialize the sys_arch layer
*
*/
2016-08-17 11:08:22 -04:00
void
sys_init(void)
{
if (!g_lwip_protect_mutex) {
if (ERR_OK != sys_mutex_new(&g_lwip_protect_mutex)) {
ESP_LOGE(TAG, "sys_init: failed to init lwip protect mutex");
}
}
// Create the pthreads key for the per-thread semaphore storage
pthread_key_create(&sys_thread_sem_key, sys_thread_sem_free);
esp_vfs_lwip_sockets_register();
2016-08-17 11:08:22 -04:00
}
/**
* @brief Get system ticks
*
* @return system tick counts
*/
u32_t
sys_jiffies(void)
{
return xTaskGetTickCount();
}
/**
* @brief Get current time, in milliseconds
*
* @return current time
*/
2016-08-17 11:08:22 -04:00
u32_t
sys_now(void)
{
return xTaskGetTickCount() * portTICK_PERIOD_MS;
2016-08-17 11:08:22 -04:00
}
/**
* @brief Protect critical region
*
* @note This function is only called during very short critical regions.
*
* @return previous protection level
*/
2016-08-17 11:08:22 -04:00
sys_prot_t
sys_arch_protect(void)
{
if (unlikely(!g_lwip_protect_mutex)) {
sys_mutex_new(&g_lwip_protect_mutex);
}
sys_mutex_lock(&g_lwip_protect_mutex);
2016-08-17 11:08:22 -04:00
return (sys_prot_t) 1;
}
/**
* @brief Unprotect critical region
*
* @param pval protection level
*/
2016-08-17 11:08:22 -04:00
void
sys_arch_unprotect(sys_prot_t pval)
{
LWIP_UNUSED_ARG(pval);
sys_mutex_unlock(&g_lwip_protect_mutex);
2016-08-17 11:08:22 -04:00
}
/*
* get per thread semaphore
*/
sys_sem_t*
sys_thread_sem_get(void)
{
sys_sem_t *sem = pthread_getspecific(sys_thread_sem_key);
if (!sem) {
sem = sys_thread_sem_init();
}
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem_get s=%p\n", sem));
return sem;
}
static void
sys_thread_sem_free(void* data) // destructor for TLS semaphore
{
sys_sem_t *sem = (sys_sem_t*)(data);
if (sem && *sem){
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem del, sem=%p\n", *sem));
vSemaphoreDelete(*sem);
}
if (sem) {
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem pointer del, sem_p=%p\n", sem));
free(sem);
}
}
sys_sem_t*
sys_thread_sem_init(void)
2016-08-17 11:08:22 -04:00
{
sys_sem_t *sem = (sys_sem_t*)mem_malloc(sizeof(sys_sem_t*));
if (!sem){
ESP_LOGE(TAG, "thread_sem_init: out of memory");
return 0;
}
*sem = xSemaphoreCreateBinary();
if (!(*sem)){
free(sem);
ESP_LOGE(TAG, "thread_sem_init: out of memory");
return 0;
}
pthread_setspecific(sys_thread_sem_key, sem);
2016-08-17 09:05:29 -04:00
return sem;
2016-08-17 11:08:22 -04:00
}
void
sys_thread_sem_deinit(void)
{
sys_sem_t *sem = pthread_getspecific(sys_thread_sem_key);
if (sem != NULL) {
sys_thread_sem_free(sem);
pthread_setspecific(sys_thread_sem_key, NULL);
}
}
void
sys_delay_ms(uint32_t ms)
2016-08-17 11:08:22 -04:00
{
vTaskDelay(ms / portTICK_PERIOD_MS);
2016-08-17 11:08:22 -04:00
}
bool
sys_thread_tcpip(sys_thread_core_lock_t type)
{
static sys_thread_t lwip_task = NULL;
#if LWIP_TCPIP_CORE_LOCKING
static sys_thread_t core_lock_holder = NULL;
#endif
switch (type) {
default:
return false;
case LWIP_CORE_IS_TCPIP_INITIALIZED:
return lwip_task != NULL;
case LWIP_CORE_MARK_TCPIP_TASK:
LWIP_ASSERT("LWIP_CORE_MARK_TCPIP_TASK: lwip_task == NULL", (lwip_task == NULL));
lwip_task = (sys_thread_t) xTaskGetCurrentTaskHandle();
return true;
#if LWIP_TCPIP_CORE_LOCKING
case LWIP_CORE_LOCK_QUERY_HOLDER:
return lwip_task ? core_lock_holder == (sys_thread_t) xTaskGetCurrentTaskHandle() : true;
case LWIP_CORE_LOCK_MARK_HOLDER:
core_lock_holder = (sys_thread_t) xTaskGetCurrentTaskHandle();
return true;
case LWIP_CORE_LOCK_UNMARK_HOLDER:
core_lock_holder = NULL;
return true;
#else
case LWIP_CORE_LOCK_QUERY_HOLDER:
return lwip_task == NULL || lwip_task == (sys_thread_t) xTaskGetCurrentTaskHandle();
#endif /* LWIP_TCPIP_CORE_LOCKING */
}
return true;
}