Fork ESP-IDF's bluetooth component
i want better sbc encoding, and no cla will stop me
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "osi/alarm.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/list.h"
|
||||
#include "esp_timer.h"
|
||||
#include "btc/btc_task.h"
|
||||
#include "btc/btc_alarm.h"
|
||||
#include "osi/mutex.h"
|
||||
#include "bt_common.h"
|
||||
|
||||
typedef struct alarm_t {
|
||||
/* timer id point to here */
|
||||
esp_timer_handle_t alarm_hdl;
|
||||
osi_alarm_callback_t cb;
|
||||
void *cb_data;
|
||||
int64_t deadline_us;
|
||||
} osi_alarm_t;
|
||||
|
||||
enum {
|
||||
ALARM_STATE_IDLE,
|
||||
ALARM_STATE_OPEN,
|
||||
};
|
||||
|
||||
static osi_mutex_t alarm_mutex;
|
||||
static int alarm_state;
|
||||
|
||||
#if (BT_BLE_DYNAMIC_ENV_MEMORY == FALSE)
|
||||
static struct alarm_t alarm_cbs[ALARM_CBS_NUM];
|
||||
#else
|
||||
static struct alarm_t *alarm_cbs;
|
||||
#endif
|
||||
|
||||
static osi_alarm_err_t alarm_free(osi_alarm_t *alarm);
|
||||
static osi_alarm_err_t alarm_set(osi_alarm_t *alarm, period_ms_t timeout, bool is_periodic);
|
||||
|
||||
int osi_alarm_create_mux(void)
|
||||
{
|
||||
if (alarm_state != ALARM_STATE_IDLE) {
|
||||
OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
|
||||
return -1;
|
||||
}
|
||||
osi_mutex_new(&alarm_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osi_alarm_delete_mux(void)
|
||||
{
|
||||
if (alarm_state != ALARM_STATE_IDLE) {
|
||||
OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
|
||||
return -1;
|
||||
}
|
||||
osi_mutex_free(&alarm_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osi_alarm_init(void)
|
||||
{
|
||||
assert(alarm_mutex != NULL);
|
||||
|
||||
osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
if (alarm_state != ALARM_STATE_IDLE) {
|
||||
OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
|
||||
goto end;
|
||||
}
|
||||
#if (BT_BLE_DYNAMIC_ENV_MEMORY == TRUE)
|
||||
if ((alarm_cbs = (osi_alarm_t *)osi_malloc(sizeof(osi_alarm_t) * ALARM_CBS_NUM)) == NULL) {
|
||||
OSI_TRACE_ERROR("%s, malloc failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(alarm_cbs, 0x00, sizeof(osi_alarm_t) * ALARM_CBS_NUM);
|
||||
alarm_state = ALARM_STATE_OPEN;
|
||||
|
||||
end:
|
||||
osi_mutex_unlock(&alarm_mutex);
|
||||
}
|
||||
|
||||
void osi_alarm_deinit(void)
|
||||
{
|
||||
assert(alarm_mutex != NULL);
|
||||
|
||||
osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
if (alarm_state != ALARM_STATE_OPEN) {
|
||||
OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ALARM_CBS_NUM; i++) {
|
||||
if (alarm_cbs[i].alarm_hdl != NULL) {
|
||||
alarm_free(&alarm_cbs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#if (BT_BLE_DYNAMIC_ENV_MEMORY == TRUE)
|
||||
osi_free(alarm_cbs);
|
||||
alarm_cbs = NULL;
|
||||
#endif
|
||||
|
||||
alarm_state = ALARM_STATE_IDLE;
|
||||
|
||||
end:
|
||||
osi_mutex_unlock(&alarm_mutex);
|
||||
}
|
||||
|
||||
static struct alarm_t *alarm_cbs_lookfor_available(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ALARM_CBS_NUM; i++) {
|
||||
if (alarm_cbs[i].alarm_hdl == NULL) { //available
|
||||
OSI_TRACE_DEBUG("%s %d %p\n", __func__, i, &alarm_cbs[i]);
|
||||
return &alarm_cbs[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void alarm_cb_handler(struct alarm_t *alarm)
|
||||
{
|
||||
OSI_TRACE_DEBUG("TimerID %p\n", alarm);
|
||||
if (alarm_state != ALARM_STATE_OPEN) {
|
||||
OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
|
||||
return;
|
||||
}
|
||||
btc_msg_t msg = {0};
|
||||
btc_alarm_args_t arg;
|
||||
msg.sig = BTC_SIG_API_CALL;
|
||||
msg.pid = BTC_PID_ALARM;
|
||||
arg.cb = alarm->cb;
|
||||
arg.cb_data = alarm->cb_data;
|
||||
btc_transfer_context(&msg, &arg, sizeof(btc_alarm_args_t), NULL, NULL);
|
||||
}
|
||||
|
||||
osi_alarm_t *osi_alarm_new(const char *alarm_name, osi_alarm_callback_t callback, void *data, period_ms_t timer_expire)
|
||||
{
|
||||
assert(alarm_mutex != NULL);
|
||||
|
||||
struct alarm_t *timer_id = NULL;
|
||||
|
||||
osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
if (alarm_state != ALARM_STATE_OPEN) {
|
||||
OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
|
||||
timer_id = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
timer_id = alarm_cbs_lookfor_available();
|
||||
|
||||
if (!timer_id) {
|
||||
OSI_TRACE_ERROR("%s alarm_cbs exhausted\n", __func__);
|
||||
timer_id = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
esp_timer_create_args_t tca = {0};
|
||||
tca.callback = (esp_timer_cb_t)alarm_cb_handler;
|
||||
tca.arg = timer_id;
|
||||
tca.dispatch_method = ESP_TIMER_TASK;
|
||||
tca.name = alarm_name;
|
||||
|
||||
timer_id->cb = callback;
|
||||
timer_id->cb_data = data;
|
||||
timer_id->deadline_us = 0;
|
||||
|
||||
esp_err_t stat = esp_timer_create(&tca, &timer_id->alarm_hdl);
|
||||
if (stat != ESP_OK) {
|
||||
OSI_TRACE_ERROR("%s failed to create timer, err 0x%x\n", __func__, stat);
|
||||
timer_id = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
osi_mutex_unlock(&alarm_mutex);
|
||||
return timer_id;
|
||||
}
|
||||
|
||||
static osi_alarm_err_t alarm_free(osi_alarm_t *alarm)
|
||||
{
|
||||
if (!alarm || alarm->alarm_hdl == NULL) {
|
||||
OSI_TRACE_ERROR("%s null\n", __func__);
|
||||
return OSI_ALARM_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_timer_stop(alarm->alarm_hdl);
|
||||
esp_err_t stat = esp_timer_delete(alarm->alarm_hdl);
|
||||
if (stat != ESP_OK) {
|
||||
OSI_TRACE_ERROR("%s failed to delete timer, err 0x%x\n", __func__, stat);
|
||||
return OSI_ALARM_ERR_FAIL;
|
||||
}
|
||||
|
||||
memset(alarm, 0, sizeof(osi_alarm_t));
|
||||
return OSI_ALARM_ERR_PASS;
|
||||
}
|
||||
|
||||
void osi_alarm_free(osi_alarm_t *alarm)
|
||||
{
|
||||
assert(alarm_mutex != NULL);
|
||||
|
||||
osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
if (alarm_state != ALARM_STATE_OPEN) {
|
||||
OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
|
||||
goto end;
|
||||
}
|
||||
alarm_free(alarm);
|
||||
|
||||
end:
|
||||
osi_mutex_unlock(&alarm_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
static osi_alarm_err_t alarm_set(osi_alarm_t *alarm, period_ms_t timeout, bool is_periodic)
|
||||
{
|
||||
assert(alarm_mutex != NULL);
|
||||
|
||||
osi_alarm_err_t ret = OSI_ALARM_ERR_PASS;
|
||||
osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
if (alarm_state != ALARM_STATE_OPEN) {
|
||||
OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
|
||||
ret = OSI_ALARM_ERR_INVALID_STATE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!alarm || alarm->alarm_hdl == NULL) {
|
||||
OSI_TRACE_ERROR("%s null\n", __func__);
|
||||
ret = OSI_ALARM_ERR_INVALID_ARG;
|
||||
goto end;
|
||||
}
|
||||
|
||||
int64_t timeout_us = 1000 * (int64_t)timeout;
|
||||
esp_err_t stat;
|
||||
if (is_periodic) {
|
||||
stat = esp_timer_start_periodic(alarm->alarm_hdl, (uint64_t)timeout_us);
|
||||
} else {
|
||||
stat = esp_timer_start_once(alarm->alarm_hdl, (uint64_t)timeout_us);
|
||||
}
|
||||
if (stat != ESP_OK) {
|
||||
OSI_TRACE_ERROR("%s failed to start timer, err 0x%x\n", __func__, stat);
|
||||
ret = OSI_ALARM_ERR_FAIL;
|
||||
goto end;
|
||||
}
|
||||
alarm->deadline_us = is_periodic ? 0 : (timeout_us + esp_timer_get_time());
|
||||
|
||||
end:
|
||||
osi_mutex_unlock(&alarm_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
osi_alarm_err_t osi_alarm_set(osi_alarm_t *alarm, period_ms_t timeout)
|
||||
{
|
||||
return alarm_set(alarm, timeout, FALSE);
|
||||
}
|
||||
|
||||
osi_alarm_err_t osi_alarm_set_periodic(osi_alarm_t *alarm, period_ms_t period)
|
||||
{
|
||||
return alarm_set(alarm, period, TRUE);
|
||||
}
|
||||
|
||||
osi_alarm_err_t osi_alarm_cancel(osi_alarm_t *alarm)
|
||||
{
|
||||
int ret = OSI_ALARM_ERR_PASS;
|
||||
osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
if (alarm_state != ALARM_STATE_OPEN) {
|
||||
OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
|
||||
ret = OSI_ALARM_ERR_INVALID_STATE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!alarm || alarm->alarm_hdl == NULL) {
|
||||
OSI_TRACE_ERROR("%s null\n", __func__);
|
||||
ret = OSI_ALARM_ERR_INVALID_ARG;
|
||||
goto end;
|
||||
}
|
||||
|
||||
esp_err_t stat = esp_timer_stop(alarm->alarm_hdl);
|
||||
if (stat != ESP_OK) {
|
||||
OSI_TRACE_DEBUG("%s failed to stop timer, err 0x%x\n", __func__, stat);
|
||||
ret = OSI_ALARM_ERR_FAIL;
|
||||
goto end;
|
||||
}
|
||||
end:
|
||||
osi_mutex_unlock(&alarm_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
period_ms_t osi_alarm_get_remaining_ms(const osi_alarm_t *alarm)
|
||||
{
|
||||
assert(alarm_mutex != NULL);
|
||||
int64_t dt_us = 0;
|
||||
|
||||
osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
dt_us = alarm->deadline_us - esp_timer_get_time();
|
||||
osi_mutex_unlock(&alarm_mutex);
|
||||
|
||||
return (dt_us > 0) ? (period_ms_t)(dt_us / 1000) : 0;
|
||||
}
|
||||
|
||||
uint32_t osi_time_get_os_boottime_ms(void)
|
||||
{
|
||||
return (uint32_t)(esp_timer_get_time() / 1000);
|
||||
}
|
||||
|
||||
bool osi_alarm_is_active(osi_alarm_t *alarm)
|
||||
{
|
||||
assert(alarm != NULL);
|
||||
|
||||
if (alarm->alarm_hdl != NULL) {
|
||||
return esp_timer_is_active(alarm->alarm_hdl);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bt_common.h"
|
||||
#include "osi/allocator.h"
|
||||
|
||||
extern void *pvPortZalloc(size_t size);
|
||||
extern void vPortFree(void *pv);
|
||||
|
||||
|
||||
#if HEAP_MEMORY_DEBUG
|
||||
|
||||
#define OSI_MEM_DBG_INFO_MAX 1024*3
|
||||
typedef struct {
|
||||
void *p;
|
||||
int size;
|
||||
const char *func;
|
||||
int line;
|
||||
} osi_mem_dbg_info_t;
|
||||
|
||||
static uint32_t mem_dbg_count = 0;
|
||||
static osi_mem_dbg_info_t mem_dbg_info[OSI_MEM_DBG_INFO_MAX];
|
||||
static uint32_t mem_dbg_current_size = 0;
|
||||
static uint32_t mem_dbg_max_size = 0;
|
||||
|
||||
#define OSI_MEM_DBG_MAX_SECTION_NUM 5
|
||||
typedef struct {
|
||||
bool used;
|
||||
uint32_t max_size;
|
||||
} osi_mem_dbg_max_size_section_t;
|
||||
static osi_mem_dbg_max_size_section_t mem_dbg_max_size_section[OSI_MEM_DBG_MAX_SECTION_NUM];
|
||||
|
||||
void osi_mem_dbg_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) {
|
||||
mem_dbg_info[i].p = NULL;
|
||||
mem_dbg_info[i].size = 0;
|
||||
mem_dbg_info[i].func = NULL;
|
||||
mem_dbg_info[i].line = 0;
|
||||
}
|
||||
mem_dbg_count = 0;
|
||||
mem_dbg_current_size = 0;
|
||||
mem_dbg_max_size = 0;
|
||||
|
||||
for (i = 0; i < OSI_MEM_DBG_MAX_SECTION_NUM; i++){
|
||||
mem_dbg_max_size_section[i].used = false;
|
||||
mem_dbg_max_size_section[i].max_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void osi_mem_dbg_record(void *p, int size, const char *func, int line)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!p || size == 0) {
|
||||
OSI_TRACE_ERROR("%s invalid !!\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) {
|
||||
if (mem_dbg_info[i].p == NULL) {
|
||||
mem_dbg_info[i].p = p;
|
||||
mem_dbg_info[i].size = size;
|
||||
mem_dbg_info[i].func = func;
|
||||
mem_dbg_info[i].line = line;
|
||||
mem_dbg_count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= OSI_MEM_DBG_INFO_MAX) {
|
||||
OSI_TRACE_ERROR("%s full %s %d !!\n", __func__, func, line);
|
||||
}
|
||||
|
||||
mem_dbg_current_size += size;
|
||||
if(mem_dbg_max_size < mem_dbg_current_size) {
|
||||
mem_dbg_max_size = mem_dbg_current_size;
|
||||
}
|
||||
|
||||
for (i = 0; i < OSI_MEM_DBG_MAX_SECTION_NUM; i++){
|
||||
if (mem_dbg_max_size_section[i].used) {
|
||||
if(mem_dbg_max_size_section[i].max_size < mem_dbg_current_size) {
|
||||
mem_dbg_max_size_section[i].max_size = mem_dbg_current_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void osi_mem_dbg_clean(void *p, const char *func, int line)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!p) {
|
||||
OSI_TRACE_ERROR("%s invalid\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) {
|
||||
if (mem_dbg_info[i].p == p) {
|
||||
mem_dbg_current_size -= mem_dbg_info[i].size;
|
||||
mem_dbg_info[i].p = NULL;
|
||||
mem_dbg_info[i].size = 0;
|
||||
mem_dbg_info[i].func = NULL;
|
||||
mem_dbg_info[i].line = 0;
|
||||
mem_dbg_count--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= OSI_MEM_DBG_INFO_MAX) {
|
||||
OSI_TRACE_ERROR("%s full %s %d !!\n", __func__, func, line);
|
||||
}
|
||||
}
|
||||
|
||||
void osi_mem_dbg_show(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) {
|
||||
if (mem_dbg_info[i].p || mem_dbg_info[i].size != 0 ) {
|
||||
OSI_TRACE_ERROR("--> p %p, s %d, f %s, l %d\n", mem_dbg_info[i].p, mem_dbg_info[i].size, mem_dbg_info[i].func, mem_dbg_info[i].line);
|
||||
}
|
||||
}
|
||||
OSI_TRACE_ERROR("--> count %d\n", mem_dbg_count);
|
||||
OSI_TRACE_ERROR("--> size %dB\n--> max size %dB\n", mem_dbg_current_size, mem_dbg_max_size);
|
||||
}
|
||||
|
||||
uint32_t osi_mem_dbg_get_max_size(void)
|
||||
{
|
||||
return mem_dbg_max_size;
|
||||
}
|
||||
|
||||
uint32_t osi_mem_dbg_get_current_size(void)
|
||||
{
|
||||
return mem_dbg_current_size;
|
||||
}
|
||||
|
||||
void osi_men_dbg_set_section_start(uint8_t index)
|
||||
{
|
||||
if (index >= OSI_MEM_DBG_MAX_SECTION_NUM) {
|
||||
OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n",
|
||||
OSI_MEM_DBG_MAX_SECTION_NUM - 1, index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem_dbg_max_size_section[index].used) {
|
||||
OSI_TRACE_WARNING("This index(%d) has been started, restart it.\n", index);
|
||||
}
|
||||
|
||||
mem_dbg_max_size_section[index].used = true;
|
||||
mem_dbg_max_size_section[index].max_size = mem_dbg_current_size;
|
||||
}
|
||||
|
||||
void osi_men_dbg_set_section_end(uint8_t index)
|
||||
{
|
||||
if (index >= OSI_MEM_DBG_MAX_SECTION_NUM) {
|
||||
OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n",
|
||||
OSI_MEM_DBG_MAX_SECTION_NUM - 1, index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mem_dbg_max_size_section[index].used) {
|
||||
OSI_TRACE_ERROR("This index(%d) has not been started.\n", index);
|
||||
return;
|
||||
}
|
||||
|
||||
mem_dbg_max_size_section[index].used = false;
|
||||
}
|
||||
|
||||
uint32_t osi_mem_dbg_get_max_size_section(uint8_t index)
|
||||
{
|
||||
if (index >= OSI_MEM_DBG_MAX_SECTION_NUM){
|
||||
OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n",
|
||||
OSI_MEM_DBG_MAX_SECTION_NUM - 1, index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mem_dbg_max_size_section[index].max_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
char *osi_strdup(const char *str)
|
||||
{
|
||||
size_t size = strlen(str) + 1; // + 1 for the null terminator
|
||||
char *new_string = (char *)osi_calloc(size);
|
||||
|
||||
if (!new_string) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(new_string, str, size);
|
||||
return new_string;
|
||||
}
|
||||
|
||||
void *osi_malloc_func(size_t size)
|
||||
{
|
||||
#if HEAP_MEMORY_DEBUG
|
||||
void *p;
|
||||
#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
|
||||
p = heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL);
|
||||
#else
|
||||
p = malloc(size);
|
||||
#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
|
||||
osi_mem_dbg_record(p, size, __func__, __LINE__);
|
||||
return p;
|
||||
#else
|
||||
#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
|
||||
return heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL);
|
||||
#else
|
||||
return malloc(size);
|
||||
#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
|
||||
#endif /* #if HEAP_MEMORY_DEBUG */
|
||||
}
|
||||
|
||||
void *osi_calloc_func(size_t size)
|
||||
{
|
||||
#if HEAP_MEMORY_DEBUG
|
||||
void *p;
|
||||
#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
|
||||
p = heap_caps_calloc_prefer(1, size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL);
|
||||
#else
|
||||
p = calloc(1, size);
|
||||
#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
|
||||
osi_mem_dbg_record(p, size, __func__, __LINE__);
|
||||
return p;
|
||||
#else
|
||||
#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
|
||||
return heap_caps_calloc_prefer(1, size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL);
|
||||
#else
|
||||
return calloc(1, size);
|
||||
#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
|
||||
#endif /* #if HEAP_MEMORY_DEBUG */
|
||||
}
|
||||
|
||||
void osi_free_func(void *ptr)
|
||||
{
|
||||
#if HEAP_MEMORY_DEBUG
|
||||
osi_mem_dbg_clean(ptr, __func__, __LINE__);
|
||||
#endif
|
||||
free(ptr);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
#include <stdint.h>
|
||||
#include "bt_common.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/buffer.h"
|
||||
|
||||
struct buffer_t {
|
||||
buffer_t *root;
|
||||
size_t refcount;
|
||||
size_t length;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
buffer_t *buffer_new(size_t size)
|
||||
{
|
||||
assert(size > 0);
|
||||
|
||||
buffer_t *buffer = osi_malloc(sizeof(buffer_t) + size);
|
||||
if (!buffer) {
|
||||
OSI_TRACE_ERROR("%s unable to allocate buffer of %zu bytes.", __func__, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer->root = buffer;
|
||||
buffer->refcount = 1;
|
||||
buffer->length = size;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
buffer_t *buffer_new_ref(const buffer_t *buf)
|
||||
{
|
||||
assert(buf != NULL);
|
||||
return buffer_new_slice(buf, buf->length);
|
||||
}
|
||||
|
||||
buffer_t *buffer_new_slice(const buffer_t *buf, size_t slice_size)
|
||||
{
|
||||
assert(buf != NULL);
|
||||
assert(slice_size > 0);
|
||||
assert(slice_size <= buf->length);
|
||||
|
||||
buffer_t *ret = osi_calloc(sizeof(buffer_t));
|
||||
if (!ret) {
|
||||
OSI_TRACE_ERROR("%s unable to allocate new buffer for slice of length %zu.", __func__, slice_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->root = buf->root;
|
||||
ret->refcount = SIZE_MAX;
|
||||
ret->length = slice_size;
|
||||
|
||||
++buf->root->refcount;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void buffer_free(buffer_t *buffer)
|
||||
{
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer->root != buffer) {
|
||||
// We're a leaf node. Delete the root node if we're the last referent.
|
||||
if (--buffer->root->refcount == 0) {
|
||||
osi_free(buffer->root);
|
||||
}
|
||||
osi_free(buffer);
|
||||
} else if (--buffer->refcount == 0) {
|
||||
// We're a root node. Roots are only deleted when their refcount goes to 0.
|
||||
osi_free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void *buffer_ptr(const buffer_t *buf)
|
||||
{
|
||||
assert(buf != NULL);
|
||||
return buf->root->data + buf->root->length - buf->length;
|
||||
}
|
||||
|
||||
size_t buffer_length(const buffer_t *buf)
|
||||
{
|
||||
assert(buf != NULL);
|
||||
return buf->length;
|
||||
}
|
||||
@@ -0,0 +1,740 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_TAG "bt_osi_config"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bt_common.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/config.h"
|
||||
#include "osi/list.h"
|
||||
|
||||
#define CONFIG_FILE_MAX_SIZE (1536)//1.5k
|
||||
#define CONFIG_FILE_DEFAULE_LENGTH (2048)
|
||||
#define CONFIG_KEY "bt_cfg_key"
|
||||
typedef struct {
|
||||
char *key;
|
||||
char *value;
|
||||
} entry_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
list_t *entries;
|
||||
} section_t;
|
||||
|
||||
struct config_t {
|
||||
list_t *sections;
|
||||
};
|
||||
|
||||
// Empty definition; this type is aliased to list_node_t.
|
||||
struct config_section_iter_t {};
|
||||
|
||||
static void config_parse(nvs_handle_t fp, config_t *config);
|
||||
|
||||
static section_t *section_new(const char *name);
|
||||
static void section_free(void *ptr);
|
||||
static section_t *section_find(const config_t *config, const char *section);
|
||||
|
||||
static entry_t *entry_new(const char *key, const char *value);
|
||||
static void entry_free(void *ptr);
|
||||
static entry_t *entry_find(const config_t *config, const char *section, const char *key);
|
||||
|
||||
config_t *config_new_empty(void)
|
||||
{
|
||||
config_t *config = osi_calloc(sizeof(config_t));
|
||||
if (!config) {
|
||||
OSI_TRACE_ERROR("%s unable to allocate memory for config_t.\n", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
config->sections = list_new(section_free);
|
||||
if (!config->sections) {
|
||||
OSI_TRACE_ERROR("%s unable to allocate list for sections.\n", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return config;
|
||||
|
||||
error:;
|
||||
config_free(config);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config_t *config_new(const char *filename)
|
||||
{
|
||||
assert(filename != NULL);
|
||||
|
||||
config_t *config = config_new_empty();
|
||||
if (!config) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t err;
|
||||
nvs_handle_t fp;
|
||||
err = nvs_open(filename, NVS_READWRITE, &fp);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_NVS_NOT_INITIALIZED) {
|
||||
OSI_TRACE_ERROR("%s: NVS not initialized. "
|
||||
"Call nvs_flash_init before initializing bluetooth.", __func__);
|
||||
} else {
|
||||
OSI_TRACE_ERROR("%s unable to open NVS namespace '%s'\n", __func__, filename);
|
||||
}
|
||||
config_free(config);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config_parse(fp, config);
|
||||
nvs_close(fp);
|
||||
return config;
|
||||
}
|
||||
|
||||
void config_free(config_t *config)
|
||||
{
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
list_free(config->sections);
|
||||
osi_free(config);
|
||||
}
|
||||
|
||||
bool config_has_section(const config_t *config, const char *section)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
|
||||
return (section_find(config, section) != NULL);
|
||||
}
|
||||
|
||||
bool config_has_key(const config_t *config, const char *section, const char *key)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
return (entry_find(config, section, key) != NULL);
|
||||
}
|
||||
|
||||
bool config_has_key_in_section(config_t *config, const char *key, char *key_value)
|
||||
{
|
||||
OSI_TRACE_DEBUG("key = %s, value = %s", key, key_value);
|
||||
for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
|
||||
const section_t *section = (const section_t *)list_node(node);
|
||||
|
||||
for (const list_node_t *node = list_begin(section->entries); node != list_end(section->entries); node = list_next(node)) {
|
||||
entry_t *entry = list_node(node);
|
||||
OSI_TRACE_DEBUG("entry->key = %s, entry->value = %s", entry->key, entry->value);
|
||||
if (!strcmp(entry->key, key) && !strcmp(entry->value, key_value)) {
|
||||
OSI_TRACE_DEBUG("%s, the irk aready in the flash.", __func__);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int config_get_int(const config_t *config, const char *section, const char *key, int def_value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
entry_t *entry = entry_find(config, section, key);
|
||||
if (!entry) {
|
||||
return def_value;
|
||||
}
|
||||
|
||||
char *endptr;
|
||||
int ret = strtol(entry->value, &endptr, 0);
|
||||
return (*endptr == '\0') ? ret : def_value;
|
||||
}
|
||||
|
||||
bool config_get_bool(const config_t *config, const char *section, const char *key, bool def_value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
entry_t *entry = entry_find(config, section, key);
|
||||
if (!entry) {
|
||||
return def_value;
|
||||
}
|
||||
|
||||
if (!strcmp(entry->value, "true")) {
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(entry->value, "false")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return def_value;
|
||||
}
|
||||
|
||||
const char *config_get_string(const config_t *config, const char *section, const char *key, const char *def_value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
entry_t *entry = entry_find(config, section, key);
|
||||
if (!entry) {
|
||||
return def_value;
|
||||
}
|
||||
|
||||
return entry->value;
|
||||
}
|
||||
|
||||
void config_set_int(config_t *config, const char *section, const char *key, int value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
char value_str[32] = { 0 };
|
||||
sprintf(value_str, "%d", value);
|
||||
config_set_string(config, section, key, value_str, false);
|
||||
}
|
||||
|
||||
void config_set_bool(config_t *config, const char *section, const char *key, bool value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
config_set_string(config, section, key, value ? "true" : "false", false);
|
||||
}
|
||||
|
||||
void config_set_string(config_t *config, const char *section, const char *key, const char *value, bool insert_back)
|
||||
{
|
||||
section_t *sec = section_find(config, section);
|
||||
if (!sec) {
|
||||
sec = section_new(section);
|
||||
if (insert_back) {
|
||||
list_append(config->sections, sec);
|
||||
} else {
|
||||
list_prepend(config->sections, sec);
|
||||
}
|
||||
}
|
||||
|
||||
for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) {
|
||||
entry_t *entry = list_node(node);
|
||||
if (!strcmp(entry->key, key)) {
|
||||
osi_free(entry->value);
|
||||
entry->value = osi_strdup(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
entry_t *entry = entry_new(key, value);
|
||||
list_append(sec->entries, entry);
|
||||
}
|
||||
|
||||
bool config_remove_section(config_t *config, const char *section)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
|
||||
section_t *sec = section_find(config, section);
|
||||
if (!sec) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return list_remove(config->sections, sec);
|
||||
}
|
||||
|
||||
bool config_update_newest_section(config_t *config, const char *section)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
|
||||
list_node_t *first_node = list_begin(config->sections);
|
||||
if (first_node == NULL) {
|
||||
return false;
|
||||
}
|
||||
section_t *first_sec = list_node(first_node);
|
||||
if (strcmp(first_sec->name, section) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
|
||||
section_t *sec = list_node(node);
|
||||
if (strcmp(sec->name, section) == 0) {
|
||||
list_delete(config->sections, sec);
|
||||
list_prepend(config->sections, sec);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_remove_key(config_t *config, const char *section, const char *key)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
bool ret;
|
||||
|
||||
section_t *sec = section_find(config, section);
|
||||
entry_t *entry = entry_find(config, section, key);
|
||||
if (!sec || !entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = list_remove(sec->entries, entry);
|
||||
if (list_length(sec->entries) == 0) {
|
||||
OSI_TRACE_DEBUG("%s remove section name:%s",__func__, section);
|
||||
ret &= config_remove_section(config, section);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const config_section_node_t *config_section_begin(const config_t *config)
|
||||
{
|
||||
assert(config != NULL);
|
||||
return (const config_section_node_t *)list_begin(config->sections);
|
||||
}
|
||||
|
||||
const config_section_node_t *config_section_end(const config_t *config)
|
||||
{
|
||||
assert(config != NULL);
|
||||
return (const config_section_node_t *)list_end(config->sections);
|
||||
}
|
||||
|
||||
const config_section_node_t *config_section_next(const config_section_node_t *node)
|
||||
{
|
||||
assert(node != NULL);
|
||||
return (const config_section_node_t *)list_next((const list_node_t *)node);
|
||||
}
|
||||
|
||||
const char *config_section_name(const config_section_node_t *node)
|
||||
{
|
||||
assert(node != NULL);
|
||||
const list_node_t *lnode = (const list_node_t *)node;
|
||||
const section_t *section = (const section_t *)list_node(lnode);
|
||||
return section->name;
|
||||
}
|
||||
|
||||
static int get_config_size(const config_t *config)
|
||||
{
|
||||
assert(config != NULL);
|
||||
|
||||
int w_len = 0, total_size = 0;
|
||||
|
||||
for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
|
||||
const section_t *section = (const section_t *)list_node(node);
|
||||
w_len = strlen(section->name) + strlen("[]\n");// format "[section->name]\n"
|
||||
total_size += w_len;
|
||||
|
||||
for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) {
|
||||
const entry_t *entry = (const entry_t *)list_node(enode);
|
||||
w_len = strlen(entry->key) + strlen(entry->value) + strlen(" = \n");// format "entry->key = entry->value\n"
|
||||
total_size += w_len;
|
||||
}
|
||||
|
||||
// Only add a separating newline if there are more sections.
|
||||
if (list_next(node) != list_end(config->sections)) {
|
||||
total_size ++; //'\n'
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
total_size ++; //'\0'
|
||||
return total_size;
|
||||
}
|
||||
|
||||
static int get_config_size_from_flash(nvs_handle_t fp)
|
||||
{
|
||||
assert(fp != 0);
|
||||
|
||||
esp_err_t err;
|
||||
const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i))
|
||||
char *keyname = osi_calloc(keyname_bufsz);
|
||||
if (!keyname){
|
||||
OSI_TRACE_ERROR("%s, malloc error\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
size_t length = CONFIG_FILE_DEFAULE_LENGTH;
|
||||
size_t total_length = 0;
|
||||
uint16_t i = 0;
|
||||
snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0);
|
||||
err = nvs_get_blob(fp, keyname, NULL, &length);
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
osi_free(keyname);
|
||||
return 0;
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
OSI_TRACE_ERROR("%s, error %d\n", __func__, err);
|
||||
osi_free(keyname);
|
||||
return 0;
|
||||
}
|
||||
total_length += length;
|
||||
while (length == CONFIG_FILE_MAX_SIZE) {
|
||||
length = CONFIG_FILE_DEFAULE_LENGTH;
|
||||
snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, ++i);
|
||||
err = nvs_get_blob(fp, keyname, NULL, &length);
|
||||
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
break;
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
OSI_TRACE_ERROR("%s, error %d\n", __func__, err);
|
||||
osi_free(keyname);
|
||||
return 0;
|
||||
}
|
||||
total_length += length;
|
||||
}
|
||||
osi_free(keyname);
|
||||
return total_length;
|
||||
}
|
||||
|
||||
bool config_save(const config_t *config, const char *filename)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(filename != NULL);
|
||||
assert(*filename != '\0');
|
||||
|
||||
esp_err_t err;
|
||||
int err_code = 0;
|
||||
nvs_handle_t fp;
|
||||
char *line = osi_calloc(1024);
|
||||
const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i))
|
||||
char *keyname = osi_calloc(keyname_bufsz);
|
||||
int config_size = get_config_size(config);
|
||||
char *buf = osi_calloc(config_size);
|
||||
if (!line || !buf || !keyname) {
|
||||
err_code |= 0x01;
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = nvs_open(filename, NVS_READWRITE, &fp);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_NVS_NOT_INITIALIZED) {
|
||||
OSI_TRACE_ERROR("%s: NVS not initialized. "
|
||||
"Call nvs_flash_init before initializing bluetooth.", __func__);
|
||||
}
|
||||
err_code |= 0x02;
|
||||
goto error;
|
||||
}
|
||||
|
||||
int w_cnt, w_cnt_total = 0;
|
||||
for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
|
||||
const section_t *section = (const section_t *)list_node(node);
|
||||
w_cnt = snprintf(line, 1024, "[%s]\n", section->name);
|
||||
if(w_cnt < 0) {
|
||||
OSI_TRACE_ERROR("snprintf error w_cnt %d.",w_cnt);
|
||||
err_code |= 0x10;
|
||||
goto error;
|
||||
}
|
||||
if(w_cnt_total + w_cnt > config_size) {
|
||||
OSI_TRACE_ERROR("%s, memcpy size (w_cnt + w_cnt_total = %d) is larger than buffer size (config_size = %d).", __func__, (w_cnt + w_cnt_total), config_size);
|
||||
err_code |= 0x20;
|
||||
goto error;
|
||||
}
|
||||
OSI_TRACE_DEBUG("section name: %s, w_cnt + w_cnt_total = %d\n", section->name, w_cnt + w_cnt_total);
|
||||
memcpy(buf + w_cnt_total, line, w_cnt);
|
||||
w_cnt_total += w_cnt;
|
||||
|
||||
for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) {
|
||||
const entry_t *entry = (const entry_t *)list_node(enode);
|
||||
OSI_TRACE_DEBUG("(key, val): (%s, %s)\n", entry->key, entry->value);
|
||||
w_cnt = snprintf(line, 1024, "%s = %s\n", entry->key, entry->value);
|
||||
if(w_cnt < 0) {
|
||||
OSI_TRACE_ERROR("snprintf error w_cnt %d.",w_cnt);
|
||||
err_code |= 0x10;
|
||||
goto error;
|
||||
}
|
||||
if(w_cnt_total + w_cnt > config_size) {
|
||||
OSI_TRACE_ERROR("%s, memcpy size (w_cnt + w_cnt_total = %d) is larger than buffer size.(config_size = %d)", __func__, (w_cnt + w_cnt_total), config_size);
|
||||
err_code |= 0x20;
|
||||
goto error;
|
||||
}
|
||||
OSI_TRACE_DEBUG("%s, w_cnt + w_cnt_total = %d", __func__, w_cnt + w_cnt_total);
|
||||
memcpy(buf + w_cnt_total, line, w_cnt);
|
||||
w_cnt_total += w_cnt;
|
||||
}
|
||||
|
||||
// Only add a separating newline if there are more sections.
|
||||
if (list_next(node) != list_end(config->sections)) {
|
||||
buf[w_cnt_total] = '\n';
|
||||
w_cnt_total += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf[w_cnt_total] = '\0';
|
||||
if (w_cnt_total < CONFIG_FILE_MAX_SIZE) {
|
||||
snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0);
|
||||
err = nvs_set_blob(fp, keyname, buf, w_cnt_total);
|
||||
if (err != ESP_OK) {
|
||||
nvs_close(fp);
|
||||
err_code |= 0x04;
|
||||
goto error;
|
||||
}
|
||||
}else {
|
||||
int count = (w_cnt_total / CONFIG_FILE_MAX_SIZE);
|
||||
assert(count <= 0xFF);
|
||||
for (uint8_t i = 0; i <= count; i++)
|
||||
{
|
||||
snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, i);
|
||||
if (i == count) {
|
||||
err = nvs_set_blob(fp, keyname, buf + i*CONFIG_FILE_MAX_SIZE, w_cnt_total - i*CONFIG_FILE_MAX_SIZE);
|
||||
OSI_TRACE_DEBUG("save keyname = %s, i = %d, %d\n", keyname, i, w_cnt_total - i*CONFIG_FILE_MAX_SIZE);
|
||||
}else {
|
||||
err = nvs_set_blob(fp, keyname, buf + i*CONFIG_FILE_MAX_SIZE, CONFIG_FILE_MAX_SIZE);
|
||||
OSI_TRACE_DEBUG("save keyname = %s, i = %d, %d\n", keyname, i, CONFIG_FILE_MAX_SIZE);
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
nvs_close(fp);
|
||||
err_code |= 0x04;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = nvs_commit(fp);
|
||||
if (err != ESP_OK) {
|
||||
nvs_close(fp);
|
||||
err_code |= 0x08;
|
||||
goto error;
|
||||
}
|
||||
|
||||
nvs_close(fp);
|
||||
osi_free(line);
|
||||
osi_free(buf);
|
||||
osi_free(keyname);
|
||||
return true;
|
||||
|
||||
error:
|
||||
if (buf) {
|
||||
osi_free(buf);
|
||||
}
|
||||
if (line) {
|
||||
osi_free(line);
|
||||
}
|
||||
if (keyname) {
|
||||
osi_free(keyname);
|
||||
}
|
||||
if (err_code) {
|
||||
OSI_TRACE_ERROR("%s, err_code: 0x%x\n", __func__, err_code);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static char *trim(char *str)
|
||||
{
|
||||
while (isspace((unsigned char)(*str))) {
|
||||
++str;
|
||||
}
|
||||
|
||||
if (!*str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
char *end_str = str + strlen(str) - 1;
|
||||
while (end_str > str && isspace((unsigned char)(*end_str))) {
|
||||
--end_str;
|
||||
}
|
||||
|
||||
end_str[1] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
static void config_parse(nvs_handle_t fp, config_t *config)
|
||||
{
|
||||
assert(fp != 0);
|
||||
assert(config != NULL);
|
||||
|
||||
esp_err_t err;
|
||||
int line_num = 0;
|
||||
int err_code = 0;
|
||||
uint16_t i = 0;
|
||||
size_t length = CONFIG_FILE_DEFAULE_LENGTH;
|
||||
size_t total_length = 0;
|
||||
char *line = osi_calloc(1024);
|
||||
char *section = osi_calloc(1024);
|
||||
const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i))
|
||||
char *keyname = osi_calloc(keyname_bufsz);
|
||||
int buf_size = get_config_size_from_flash(fp);
|
||||
char *buf = NULL;
|
||||
|
||||
if(buf_size == 0) { //First use nvs
|
||||
goto error;
|
||||
}
|
||||
buf = osi_calloc(buf_size);
|
||||
if (!line || !section || !buf || !keyname) {
|
||||
err_code |= 0x01;
|
||||
goto error;
|
||||
}
|
||||
snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0);
|
||||
err = nvs_get_blob(fp, keyname, buf, &length);
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
goto error;
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
err_code |= 0x02;
|
||||
goto error;
|
||||
}
|
||||
total_length += length;
|
||||
while (length == CONFIG_FILE_MAX_SIZE) {
|
||||
length = CONFIG_FILE_DEFAULE_LENGTH;
|
||||
snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, ++i);
|
||||
err = nvs_get_blob(fp, keyname, buf + CONFIG_FILE_MAX_SIZE * i, &length);
|
||||
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
break;
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
err_code |= 0x02;
|
||||
goto error;
|
||||
}
|
||||
total_length += length;
|
||||
}
|
||||
char *p_line_end;
|
||||
char *p_line_bgn = buf;
|
||||
strcpy(section, CONFIG_DEFAULT_SECTION);
|
||||
|
||||
while ( (p_line_bgn < buf + total_length - 1) && (p_line_end = strchr(p_line_bgn, '\n'))) {
|
||||
|
||||
// get one line
|
||||
int line_len = p_line_end - p_line_bgn;
|
||||
if (line_len > 1023) {
|
||||
OSI_TRACE_WARNING("%s exceed max line length on line %d.\n", __func__, line_num);
|
||||
break;
|
||||
}
|
||||
memcpy(line, p_line_bgn, line_len);
|
||||
line[line_len] = '\0';
|
||||
p_line_bgn = p_line_end + 1;
|
||||
char *line_ptr = trim(line);
|
||||
++line_num;
|
||||
|
||||
// Skip blank and comment lines.
|
||||
if (*line_ptr == '\0' || *line_ptr == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*line_ptr == '[') {
|
||||
size_t len = strlen(line_ptr);
|
||||
if (line_ptr[len - 1] != ']') {
|
||||
OSI_TRACE_WARNING("%s unterminated section name on line %d.\n", __func__, line_num);
|
||||
continue;
|
||||
}
|
||||
strncpy(section, line_ptr + 1, len - 2);
|
||||
section[len - 2] = '\0';
|
||||
} else {
|
||||
char *split = strchr(line_ptr, '=');
|
||||
if (!split) {
|
||||
OSI_TRACE_DEBUG("%s no key/value separator found on line %d.\n", __func__, line_num);
|
||||
continue;
|
||||
}
|
||||
*split = '\0';
|
||||
config_set_string(config, section, trim(line_ptr), trim(split + 1), true);
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
if (buf) {
|
||||
osi_free(buf);
|
||||
}
|
||||
if (line) {
|
||||
osi_free(line);
|
||||
}
|
||||
if (section) {
|
||||
osi_free(section);
|
||||
}
|
||||
if (keyname) {
|
||||
osi_free(keyname);
|
||||
}
|
||||
if (err_code) {
|
||||
OSI_TRACE_ERROR("%s returned with err code: %d\n", __func__, err_code);
|
||||
}
|
||||
}
|
||||
|
||||
static section_t *section_new(const char *name)
|
||||
{
|
||||
section_t *section = osi_calloc(sizeof(section_t));
|
||||
if (!section) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
section->name = osi_strdup(name);
|
||||
section->entries = list_new(entry_free);
|
||||
return section;
|
||||
}
|
||||
|
||||
static void section_free(void *ptr)
|
||||
{
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
section_t *section = ptr;
|
||||
osi_free(section->name);
|
||||
list_free(section->entries);
|
||||
osi_free(section);
|
||||
}
|
||||
|
||||
static section_t *section_find(const config_t *config, const char *section)
|
||||
{
|
||||
for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
|
||||
section_t *sec = list_node(node);
|
||||
if (!strcmp(sec->name, section)) {
|
||||
return sec;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static entry_t *entry_new(const char *key, const char *value)
|
||||
{
|
||||
entry_t *entry = osi_calloc(sizeof(entry_t));
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry->key = osi_strdup(key);
|
||||
entry->value = osi_strdup(value);
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void entry_free(void *ptr)
|
||||
{
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
entry_t *entry = ptr;
|
||||
osi_free(entry->key);
|
||||
osi_free(entry->value);
|
||||
osi_free(entry);
|
||||
}
|
||||
|
||||
static entry_t *entry_find(const config_t *config, const char *section, const char *key)
|
||||
{
|
||||
section_t *sec = section_find(config, section);
|
||||
if (!sec) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) {
|
||||
entry_t *entry = list_node(node);
|
||||
if (!strcmp(entry->key, key)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/pkt_queue.h"
|
||||
#include "osi/fixed_pkt_queue.h"
|
||||
#include "osi/osi.h"
|
||||
#include "osi/semaphore.h"
|
||||
|
||||
typedef struct fixed_pkt_queue_t {
|
||||
struct pkt_queue *pkt_list;
|
||||
osi_sem_t enqueue_sem;
|
||||
osi_sem_t dequeue_sem;
|
||||
size_t capacity;
|
||||
fixed_pkt_queue_cb dequeue_ready;
|
||||
} fixed_pkt_queue_t;
|
||||
|
||||
fixed_pkt_queue_t *fixed_pkt_queue_new(size_t capacity)
|
||||
{
|
||||
fixed_pkt_queue_t *ret = osi_calloc(sizeof(fixed_pkt_queue_t));
|
||||
if (!ret) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret->capacity = capacity;
|
||||
ret->pkt_list = pkt_queue_create();
|
||||
if (!ret->pkt_list) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
osi_sem_new(&ret->enqueue_sem, capacity, capacity);
|
||||
if (!ret->enqueue_sem) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
osi_sem_new(&ret->dequeue_sem, capacity, 0);
|
||||
if (!ret->dequeue_sem) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
fixed_pkt_queue_free(ret, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void fixed_pkt_queue_free(fixed_pkt_queue_t *queue, fixed_pkt_queue_free_cb free_cb)
|
||||
{
|
||||
if (queue == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
fixed_pkt_queue_unregister_dequeue(queue);
|
||||
|
||||
pkt_queue_destroy(queue->pkt_list, (pkt_queue_free_cb)free_cb);
|
||||
queue->pkt_list = NULL;
|
||||
|
||||
if (queue->enqueue_sem) {
|
||||
osi_sem_free(&queue->enqueue_sem);
|
||||
}
|
||||
if (queue->dequeue_sem) {
|
||||
osi_sem_free(&queue->dequeue_sem);
|
||||
}
|
||||
osi_free(queue);
|
||||
}
|
||||
|
||||
bool fixed_pkt_queue_is_empty(fixed_pkt_queue_t *queue)
|
||||
{
|
||||
if (queue == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return pkt_queue_is_empty(queue->pkt_list);
|
||||
}
|
||||
|
||||
size_t fixed_pkt_queue_length(fixed_pkt_queue_t *queue)
|
||||
{
|
||||
if (queue == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return pkt_queue_length(queue->pkt_list);
|
||||
}
|
||||
|
||||
size_t fixed_pkt_queue_capacity(fixed_pkt_queue_t *queue)
|
||||
{
|
||||
assert(queue != NULL);
|
||||
|
||||
return queue->capacity;
|
||||
}
|
||||
|
||||
bool fixed_pkt_queue_enqueue(fixed_pkt_queue_t *queue, pkt_linked_item_t *linked_pkt, uint32_t timeout)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
assert(queue != NULL);
|
||||
assert(linked_pkt != NULL);
|
||||
|
||||
if (osi_sem_take(&queue->enqueue_sem, timeout) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = pkt_queue_enqueue(queue->pkt_list, linked_pkt);
|
||||
|
||||
assert(ret == true);
|
||||
osi_sem_give(&queue->dequeue_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
pkt_linked_item_t *fixed_pkt_queue_dequeue(fixed_pkt_queue_t *queue, uint32_t timeout)
|
||||
{
|
||||
pkt_linked_item_t *ret = NULL;
|
||||
|
||||
assert(queue != NULL);
|
||||
|
||||
if (osi_sem_take(&queue->dequeue_sem, timeout) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
ret = pkt_queue_dequeue(queue->pkt_list);
|
||||
|
||||
osi_sem_give(&queue->enqueue_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
pkt_linked_item_t *fixed_pkt_queue_try_peek_first(fixed_pkt_queue_t *queue)
|
||||
{
|
||||
if (queue == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pkt_queue_try_peek_first(queue->pkt_list);
|
||||
}
|
||||
|
||||
void fixed_pkt_queue_register_dequeue(fixed_pkt_queue_t *queue, fixed_pkt_queue_cb ready_cb)
|
||||
{
|
||||
assert(queue != NULL);
|
||||
assert(ready_cb != NULL);
|
||||
|
||||
queue->dequeue_ready = ready_cb;
|
||||
}
|
||||
|
||||
void fixed_pkt_queue_unregister_dequeue(fixed_pkt_queue_t *queue)
|
||||
{
|
||||
assert(queue != NULL);
|
||||
|
||||
queue->dequeue_ready = NULL;
|
||||
}
|
||||
|
||||
void fixed_pkt_queue_process(fixed_pkt_queue_t *queue)
|
||||
{
|
||||
assert(queue != NULL);
|
||||
|
||||
if (queue->dequeue_ready) {
|
||||
queue->dequeue_ready(queue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/fixed_queue.h"
|
||||
#include "osi/list.h"
|
||||
#include "osi/osi.h"
|
||||
#include "osi/mutex.h"
|
||||
#include "osi/semaphore.h"
|
||||
|
||||
typedef struct fixed_queue_t {
|
||||
|
||||
list_t *list;
|
||||
osi_sem_t enqueue_sem;
|
||||
osi_sem_t dequeue_sem;
|
||||
osi_mutex_t lock;
|
||||
size_t capacity;
|
||||
|
||||
fixed_queue_cb dequeue_ready;
|
||||
} fixed_queue_t;
|
||||
|
||||
|
||||
fixed_queue_t *fixed_queue_new(size_t capacity)
|
||||
{
|
||||
fixed_queue_t *ret = osi_calloc(sizeof(fixed_queue_t));
|
||||
if (!ret) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
osi_mutex_new(&ret->lock);
|
||||
ret->capacity = capacity;
|
||||
|
||||
ret->list = list_new(NULL);
|
||||
if (!ret->list) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
osi_sem_new(&ret->enqueue_sem, capacity, capacity);
|
||||
if (!ret->enqueue_sem) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
osi_sem_new(&ret->dequeue_sem, capacity, 0);
|
||||
if (!ret->dequeue_sem) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error:;
|
||||
fixed_queue_free(ret, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void fixed_queue_free(fixed_queue_t *queue, fixed_queue_free_cb free_cb)
|
||||
{
|
||||
const list_node_t *node;
|
||||
|
||||
if (queue == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
fixed_queue_unregister_dequeue(queue);
|
||||
|
||||
if (free_cb) {
|
||||
for (node = list_begin(queue->list); node != list_end(queue->list); node = list_next(node)) {
|
||||
free_cb(list_node(node));
|
||||
}
|
||||
}
|
||||
|
||||
list_free(queue->list);
|
||||
osi_sem_free(&queue->enqueue_sem);
|
||||
osi_sem_free(&queue->dequeue_sem);
|
||||
osi_mutex_free(&queue->lock);
|
||||
osi_free(queue);
|
||||
}
|
||||
|
||||
bool fixed_queue_is_empty(fixed_queue_t *queue)
|
||||
{
|
||||
bool is_empty = false;
|
||||
|
||||
if (queue == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
is_empty = list_is_empty(queue->list);
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
|
||||
return is_empty;
|
||||
}
|
||||
|
||||
size_t fixed_queue_length(fixed_queue_t *queue)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
if (queue == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
length = list_length(queue->list);
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
|
||||
return length;
|
||||
}
|
||||
size_t fixed_queue_capacity(fixed_queue_t *queue)
|
||||
{
|
||||
assert(queue != NULL);
|
||||
|
||||
return queue->capacity;
|
||||
}
|
||||
|
||||
bool fixed_queue_enqueue(fixed_queue_t *queue, void *data, uint32_t timeout)
|
||||
{
|
||||
bool status=false; //Flag whether enqueued success
|
||||
|
||||
assert(queue != NULL);
|
||||
assert(data != NULL);
|
||||
|
||||
if (osi_sem_take(&queue->enqueue_sem, timeout) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
status = list_append(queue->list, data); //Check whether enqueued success
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
|
||||
if(status == true )
|
||||
osi_sem_give(&queue->dequeue_sem);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void *fixed_queue_dequeue(fixed_queue_t *queue, uint32_t timeout)
|
||||
{
|
||||
void *ret = NULL;
|
||||
|
||||
assert(queue != NULL);
|
||||
|
||||
if (osi_sem_take(&queue->dequeue_sem, timeout) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
ret = list_front(queue->list);
|
||||
list_remove(queue->list, ret);
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
|
||||
osi_sem_give(&queue->enqueue_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *fixed_queue_try_peek_first(fixed_queue_t *queue)
|
||||
{
|
||||
void *ret = NULL;
|
||||
|
||||
if (queue == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
ret = list_is_empty(queue->list) ? NULL : list_front(queue->list);
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *fixed_queue_try_peek_last(fixed_queue_t *queue)
|
||||
{
|
||||
void *ret = NULL;
|
||||
|
||||
if (queue == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
ret = list_is_empty(queue->list) ? NULL : list_back(queue->list);
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *fixed_queue_try_remove_from_queue(fixed_queue_t *queue, void *data)
|
||||
{
|
||||
bool removed = false;
|
||||
|
||||
if (queue == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
if (list_contains(queue->list, data) &&
|
||||
osi_sem_take(&queue->dequeue_sem, 0) == 0) {
|
||||
removed = list_remove(queue->list, data);
|
||||
assert(removed);
|
||||
}
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
|
||||
if (removed) {
|
||||
osi_sem_give(&queue->enqueue_sem);
|
||||
return data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_t *fixed_queue_get_list(fixed_queue_t *queue)
|
||||
{
|
||||
assert(queue != NULL);
|
||||
|
||||
// NOTE: This function is not thread safe, and there is no point for
|
||||
// calling osi_mutex_lock() / osi_mutex_unlock()
|
||||
return queue->list;
|
||||
}
|
||||
|
||||
void fixed_queue_register_dequeue(fixed_queue_t *queue, fixed_queue_cb ready_cb)
|
||||
{
|
||||
assert(queue != NULL);
|
||||
assert(ready_cb != NULL);
|
||||
|
||||
queue->dequeue_ready = ready_cb;
|
||||
}
|
||||
|
||||
void fixed_queue_unregister_dequeue(fixed_queue_t *queue)
|
||||
{
|
||||
assert(queue != NULL);
|
||||
|
||||
queue->dequeue_ready = NULL;
|
||||
}
|
||||
|
||||
void fixed_queue_process(fixed_queue_t *queue)
|
||||
{
|
||||
assert(queue != NULL);
|
||||
|
||||
if (queue->dequeue_ready) {
|
||||
queue->dequeue_ready(queue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "bt_common.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/future.h"
|
||||
#include "osi/osi.h"
|
||||
|
||||
void future_free(future_t *future);
|
||||
|
||||
future_t *future_new(void)
|
||||
{
|
||||
future_t *ret = osi_calloc(sizeof(future_t));
|
||||
if (!ret) {
|
||||
OSI_TRACE_ERROR("%s unable to allocate memory for return value.", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (osi_sem_new(&ret->semaphore, 1, 0) != 0) {
|
||||
OSI_TRACE_ERROR("%s unable to allocate memory for the semaphore.", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret->ready_can_be_called = true;
|
||||
return ret;
|
||||
error:;
|
||||
future_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
future_t *future_new_immediate(void *value)
|
||||
{
|
||||
future_t *ret = osi_calloc(sizeof(future_t));
|
||||
if (!ret) {
|
||||
OSI_TRACE_ERROR("%s unable to allocate memory for return value.", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret->result = value;
|
||||
ret->ready_can_be_called = false;
|
||||
return ret;
|
||||
error:;
|
||||
future_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void future_ready(future_t *future, void *value)
|
||||
{
|
||||
assert(future != NULL);
|
||||
assert(future->ready_can_be_called);
|
||||
|
||||
future->ready_can_be_called = false;
|
||||
future->result = value;
|
||||
osi_sem_give(&future->semaphore);
|
||||
}
|
||||
|
||||
void *future_await(future_t *future)
|
||||
{
|
||||
assert(future != NULL);
|
||||
|
||||
// If the future is immediate, it will not have a semaphore
|
||||
if (future->semaphore) {
|
||||
osi_sem_take(&future->semaphore, OSI_SEM_MAX_TIMEOUT);
|
||||
}
|
||||
|
||||
void *result = future->result;
|
||||
future_free(future);
|
||||
return result;
|
||||
}
|
||||
|
||||
void future_free(future_t *future)
|
||||
{
|
||||
if (!future) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (future->semaphore) {
|
||||
osi_sem_free(&future->semaphore);
|
||||
}
|
||||
|
||||
osi_free(future);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "osi/hash_functions.h"
|
||||
|
||||
hash_index_t hash_function_naive(const void *key)
|
||||
{
|
||||
return (hash_index_t)key;
|
||||
}
|
||||
|
||||
hash_index_t hash_function_integer(const void *key)
|
||||
{
|
||||
return ((hash_index_t)key) * 2654435761;
|
||||
}
|
||||
|
||||
hash_index_t hash_function_pointer(const void *key)
|
||||
{
|
||||
return ((hash_index_t)key) * 2654435761;
|
||||
}
|
||||
|
||||
hash_index_t hash_function_string(const void *key)
|
||||
{
|
||||
hash_index_t hash = 5381;
|
||||
const char *name = (const char *)key;
|
||||
size_t string_len = strlen(name);
|
||||
for (size_t i = 0; i < string_len; ++i) {
|
||||
hash = ((hash << 5) + hash ) + name[i];
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
void hash_function_blob(const unsigned char *s, unsigned int len, hash_key_t h)
|
||||
{
|
||||
size_t j;
|
||||
|
||||
while (len--) {
|
||||
j = sizeof(hash_key_t)-1;
|
||||
|
||||
while (j) {
|
||||
h[j] = ((h[j] << 7) | (h[j-1] >> 1)) + h[j];
|
||||
--j;
|
||||
}
|
||||
|
||||
h[0] = (h[0] << 7) + h[0] + *s++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "bt_common.h"
|
||||
#include "osi/list.h"
|
||||
#include "osi/hash_map.h"
|
||||
#include "osi/allocator.h"
|
||||
|
||||
struct hash_map_t;
|
||||
|
||||
typedef struct hash_map_bucket_t {
|
||||
list_t *list;
|
||||
} hash_map_bucket_t;
|
||||
|
||||
typedef struct hash_map_t {
|
||||
hash_map_bucket_t *bucket;
|
||||
size_t num_bucket;
|
||||
size_t hash_size;
|
||||
hash_index_fn hash_fn;
|
||||
key_free_fn key_fn;
|
||||
data_free_fn data_fn;
|
||||
key_equality_fn keys_are_equal;
|
||||
} hash_map_t;
|
||||
|
||||
// Hidden constructor for list, only to be used by us.
|
||||
list_t *list_new_internal(list_free_cb callback);
|
||||
|
||||
static void bucket_free_(void *data);
|
||||
static bool default_key_equality(const void *x, const void *y);
|
||||
static hash_map_entry_t *find_bucket_entry_(list_t *hash_bucket_list,
|
||||
const void *key);
|
||||
|
||||
// Hidden constructor, only to be used by the allocation tracker. Behaves the same as
|
||||
// |hash_map_new|, except you get to specify the allocator.
|
||||
hash_map_t *hash_map_new_internal(
|
||||
size_t num_bucket,
|
||||
hash_index_fn hash_fn,
|
||||
key_free_fn key_fn,
|
||||
data_free_fn data_fn,
|
||||
key_equality_fn equality_fn)
|
||||
{
|
||||
assert(hash_fn != NULL);
|
||||
assert(num_bucket > 0);
|
||||
hash_map_t *hash_map = osi_calloc(sizeof(hash_map_t));
|
||||
if (hash_map == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hash_map->hash_fn = hash_fn;
|
||||
hash_map->key_fn = key_fn;
|
||||
hash_map->data_fn = data_fn;
|
||||
hash_map->keys_are_equal = equality_fn ? equality_fn : default_key_equality;
|
||||
|
||||
hash_map->num_bucket = num_bucket;
|
||||
hash_map->bucket = osi_calloc(sizeof(hash_map_bucket_t) * num_bucket);
|
||||
if (hash_map->bucket == NULL) {
|
||||
osi_free(hash_map);
|
||||
return NULL;
|
||||
}
|
||||
return hash_map;
|
||||
}
|
||||
|
||||
hash_map_t *hash_map_new(
|
||||
size_t num_bucket,
|
||||
hash_index_fn hash_fn,
|
||||
key_free_fn key_fn,
|
||||
data_free_fn data_fn,
|
||||
key_equality_fn equality_fn)
|
||||
{
|
||||
return hash_map_new_internal(num_bucket, hash_fn, key_fn, data_fn, equality_fn);
|
||||
}
|
||||
|
||||
void hash_map_free(hash_map_t *hash_map)
|
||||
{
|
||||
if (hash_map == NULL) {
|
||||
return;
|
||||
}
|
||||
hash_map_clear(hash_map);
|
||||
osi_free(hash_map->bucket);
|
||||
osi_free(hash_map);
|
||||
}
|
||||
|
||||
/*
|
||||
bool hash_map_is_empty(const hash_map_t *hash_map) {
|
||||
assert(hash_map != NULL);
|
||||
return (hash_map->hash_size == 0);
|
||||
}
|
||||
|
||||
size_t hash_map_size(const hash_map_t *hash_map) {
|
||||
assert(hash_map != NULL);
|
||||
return hash_map->hash_size;
|
||||
}
|
||||
|
||||
size_t hash_map_num_buckets(const hash_map_t *hash_map) {
|
||||
assert(hash_map != NULL);
|
||||
return hash_map->num_bucket;
|
||||
}
|
||||
*/
|
||||
|
||||
bool hash_map_has_key(const hash_map_t *hash_map, const void *key)
|
||||
{
|
||||
assert(hash_map != NULL);
|
||||
|
||||
hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
|
||||
list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
|
||||
|
||||
hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
|
||||
return (hash_map_entry != NULL);
|
||||
}
|
||||
|
||||
bool hash_map_set(hash_map_t *hash_map, const void *key, void *data)
|
||||
{
|
||||
assert(hash_map != NULL);
|
||||
assert(data != NULL);
|
||||
|
||||
hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
|
||||
|
||||
if (hash_map->bucket[hash_key].list == NULL) {
|
||||
hash_map->bucket[hash_key].list = list_new_internal(bucket_free_);
|
||||
if (hash_map->bucket[hash_key].list == NULL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
|
||||
|
||||
hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
|
||||
|
||||
if (hash_map_entry) {
|
||||
// Calls hash_map callback to delete the hash_map_entry.
|
||||
bool rc = list_remove(hash_bucket_list, hash_map_entry);
|
||||
assert(rc == true);
|
||||
(void)rc;
|
||||
} else {
|
||||
hash_map->hash_size++;
|
||||
}
|
||||
hash_map_entry = osi_calloc(sizeof(hash_map_entry_t));
|
||||
if (hash_map_entry == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hash_map_entry->key = key;
|
||||
hash_map_entry->data = data;
|
||||
hash_map_entry->hash_map = hash_map;
|
||||
|
||||
return list_append(hash_bucket_list, hash_map_entry);
|
||||
}
|
||||
|
||||
bool hash_map_erase(hash_map_t *hash_map, const void *key)
|
||||
{
|
||||
assert(hash_map != NULL);
|
||||
|
||||
hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
|
||||
list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
|
||||
|
||||
hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
|
||||
if (hash_map_entry == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hash_map->hash_size--;
|
||||
bool remove = list_remove(hash_bucket_list, hash_map_entry);
|
||||
if(list_is_empty(hash_map->bucket[hash_key].list)) {
|
||||
list_free(hash_map->bucket[hash_key].list);
|
||||
hash_map->bucket[hash_key].list = NULL;
|
||||
}
|
||||
|
||||
return remove;
|
||||
}
|
||||
|
||||
void *hash_map_get(const hash_map_t *hash_map, const void *key)
|
||||
{
|
||||
assert(hash_map != NULL);
|
||||
|
||||
hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
|
||||
list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
|
||||
|
||||
hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
|
||||
if (hash_map_entry != NULL) {
|
||||
return hash_map_entry->data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void hash_map_clear(hash_map_t *hash_map)
|
||||
{
|
||||
assert(hash_map != NULL);
|
||||
|
||||
for (hash_index_t i = 0; i < hash_map->num_bucket; i++) {
|
||||
if (hash_map->bucket[i].list == NULL) {
|
||||
continue;
|
||||
}
|
||||
list_free(hash_map->bucket[i].list);
|
||||
hash_map->bucket[i].list = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void hash_map_foreach(hash_map_t *hash_map, hash_map_iter_cb callback, void *context)
|
||||
{
|
||||
assert(hash_map != NULL);
|
||||
assert(callback != NULL);
|
||||
|
||||
for (hash_index_t i = 0; i < hash_map->num_bucket; ++i) {
|
||||
if (hash_map->bucket[i].list == NULL) {
|
||||
continue;
|
||||
}
|
||||
for (const list_node_t *iter = list_begin(hash_map->bucket[i].list);
|
||||
iter != list_end(hash_map->bucket[i].list);
|
||||
iter = list_next(iter)) {
|
||||
hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)list_node(iter);
|
||||
if (!callback(hash_map_entry, context)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bucket_free_(void *data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)data;
|
||||
const hash_map_t *hash_map = hash_map_entry->hash_map;
|
||||
|
||||
if (hash_map->key_fn) {
|
||||
hash_map->key_fn((void *)hash_map_entry->key);
|
||||
}
|
||||
if (hash_map->data_fn) {
|
||||
hash_map->data_fn(hash_map_entry->data);
|
||||
}
|
||||
osi_free(hash_map_entry);
|
||||
}
|
||||
|
||||
static hash_map_entry_t *find_bucket_entry_(list_t *hash_bucket_list,
|
||||
const void *key)
|
||||
{
|
||||
|
||||
if (hash_bucket_list == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (const list_node_t *iter = list_begin(hash_bucket_list);
|
||||
iter != list_end(hash_bucket_list);
|
||||
iter = list_next(iter)) {
|
||||
hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)list_node(iter);
|
||||
if (hash_map_entry->hash_map->keys_are_equal(hash_map_entry->key, key)) {
|
||||
return hash_map_entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool default_key_equality(const void *x, const void *y)
|
||||
{
|
||||
return x == y;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _ALARM_H_
|
||||
#define _ALARM_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_timer.h"
|
||||
|
||||
typedef struct alarm_t osi_alarm_t;
|
||||
typedef uint64_t period_ms_t;
|
||||
typedef esp_timer_cb_t osi_alarm_callback_t;
|
||||
|
||||
typedef enum {
|
||||
OSI_ALARM_ERR_PASS = 0,
|
||||
OSI_ALARM_ERR_FAIL = -1,
|
||||
OSI_ALARM_ERR_INVALID_ARG = -2,
|
||||
OSI_ALARM_ERR_INVALID_STATE = -3,
|
||||
} osi_alarm_err_t;
|
||||
|
||||
#define ALARM_CBS_NUM 50
|
||||
#define ALARM_ID_BASE 1000
|
||||
|
||||
int osi_alarm_create_mux(void);
|
||||
int osi_alarm_delete_mux(void);
|
||||
void osi_alarm_init(void);
|
||||
void osi_alarm_deinit(void);
|
||||
|
||||
// Creates a new alarm object. The returned object must be freed by calling
|
||||
// |alarm_free|. Returns NULL on failure.
|
||||
osi_alarm_t *osi_alarm_new(const char *alarm_name, osi_alarm_callback_t callback, void *data, period_ms_t timer_expire);
|
||||
|
||||
// Frees an alarm object created by |alarm_new|. |alarm| may be NULL. If the
|
||||
// alarm is pending, it will be cancelled. It is not safe to call |alarm_free|
|
||||
// from inside the callback of |alarm|.
|
||||
void osi_alarm_free(osi_alarm_t *alarm);
|
||||
|
||||
// Sets an alarm to fire |cb| after the given |deadline|. Note that |deadline| is the
|
||||
// number of milliseconds relative to the current time. |data| is a context variable
|
||||
// for the callback and may be NULL. |cb| will be called back in the context of an
|
||||
// unspecified thread (i.e. it will not be called back in the same thread as the caller).
|
||||
// |alarm| and |cb| may not be NULL.
|
||||
osi_alarm_err_t osi_alarm_set(osi_alarm_t *alarm, period_ms_t timeout);
|
||||
|
||||
// Sets an periodic alarm to fire |cb| each given |period|.
|
||||
osi_alarm_err_t osi_alarm_set_periodic(osi_alarm_t *alarm, period_ms_t period);
|
||||
|
||||
// This function cancels the |alarm| if it was previously set. When this call
|
||||
// returns, the caller has a guarantee that the callback is not in progress and
|
||||
// will not be called if it hasn't already been called. This function is idempotent.
|
||||
// |alarm| may not be NULL.
|
||||
osi_alarm_err_t osi_alarm_cancel(osi_alarm_t *alarm);
|
||||
|
||||
// Figure out how much time until next expiration.
|
||||
// Returns 0 if not armed. |alarm| may not be NULL.
|
||||
// only for oneshot alarm, not for periodic alarm
|
||||
// TODO: Remove this function once PM timers can be re-factored
|
||||
period_ms_t osi_alarm_get_remaining_ms(const osi_alarm_t *alarm);
|
||||
|
||||
// Alarm-related state cleanup
|
||||
//void alarm_cleanup(void);
|
||||
|
||||
uint32_t osi_time_get_os_boottime_ms(void);
|
||||
|
||||
// This function returns whether the given |alarm| is active or not.
|
||||
// Return true if active, false otherwise.
|
||||
bool osi_alarm_is_active(osi_alarm_t *alarm);
|
||||
|
||||
#endif /*_ALARM_H_*/
|
||||
@@ -0,0 +1,145 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _ALLOCATOR_H_
|
||||
#define _ALLOCATOR_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
char *osi_strdup(const char *str);
|
||||
|
||||
void *osi_malloc_func(size_t size);
|
||||
void *osi_calloc_func(size_t size);
|
||||
void osi_free_func(void *ptr);
|
||||
|
||||
#if HEAP_MEMORY_DEBUG
|
||||
|
||||
void osi_mem_dbg_init(void);
|
||||
void osi_mem_dbg_record(void *p, int size, const char *func, int line);
|
||||
void osi_mem_dbg_clean(void *p, const char *func, int line);
|
||||
void osi_mem_dbg_show(void);
|
||||
uint32_t osi_mem_dbg_get_max_size(void);
|
||||
uint32_t osi_mem_dbg_get_current_size(void);
|
||||
void osi_men_dbg_set_section_start(uint8_t index);
|
||||
void osi_men_dbg_set_section_end(uint8_t index);
|
||||
uint32_t osi_mem_dbg_get_max_size_section(uint8_t index);
|
||||
|
||||
#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
|
||||
#define osi_malloc(size) \
|
||||
({ \
|
||||
void *p; \
|
||||
p = heap_caps_malloc_prefer(size, 2, \
|
||||
MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, \
|
||||
MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); \
|
||||
osi_mem_dbg_record(p, size, __func__, __LINE__); \
|
||||
(void *)p; \
|
||||
})
|
||||
|
||||
#define osi_calloc(size) \
|
||||
({ \
|
||||
void *p; \
|
||||
p = heap_caps_calloc_prefer(1, size, 2, \
|
||||
MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, \
|
||||
MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); \
|
||||
osi_mem_dbg_record(p, size, __func__, __LINE__); \
|
||||
(void *)p; \
|
||||
})
|
||||
|
||||
#else
|
||||
|
||||
#define osi_malloc(size) \
|
||||
({ \
|
||||
void *p; \
|
||||
p = malloc((size)); \
|
||||
osi_mem_dbg_record(p, size, __func__, __LINE__); \
|
||||
(void *)p; \
|
||||
})
|
||||
|
||||
#define osi_calloc(size) \
|
||||
({ \
|
||||
void *p; \
|
||||
p = calloc(1, (size)); \
|
||||
osi_mem_dbg_record(p, size, __func__, __LINE__); \
|
||||
(void *)p; \
|
||||
})
|
||||
|
||||
#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
|
||||
|
||||
|
||||
#if 0
|
||||
#define osi_malloc(size) \
|
||||
do { \
|
||||
void *p; \
|
||||
\
|
||||
#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST \
|
||||
p = heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); \
|
||||
#else \
|
||||
p = malloc((size)); \
|
||||
#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ \
|
||||
osi_mem_dbg_record(p, size, __func__, __LINE__); \
|
||||
(void *)p; \
|
||||
}while(0)
|
||||
|
||||
#define osi_calloc(size) \
|
||||
do { \
|
||||
void *p; \
|
||||
\
|
||||
#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST \
|
||||
p = heap_caps_calloc_prefer(1, size, 2, \
|
||||
MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, \
|
||||
MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); \
|
||||
#else \
|
||||
p = calloc(1, (size)); \
|
||||
#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ \
|
||||
osi_mem_dbg_record(p, size, __func__, __LINE__); \
|
||||
(void *)p; \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
#define osi_free(ptr) \
|
||||
do { \
|
||||
void *tmp_point = (void *)(ptr); \
|
||||
osi_mem_dbg_clean(tmp_point, __func__, __LINE__); \
|
||||
free(tmp_point); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
|
||||
#define osi_malloc(size) heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL)
|
||||
#define osi_calloc(size) heap_caps_calloc_prefer(1, size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL)
|
||||
#else
|
||||
#define osi_malloc(size) malloc((size))
|
||||
#define osi_calloc(size) calloc(1, (size))
|
||||
#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
|
||||
#define osi_free(p) free((p))
|
||||
|
||||
#endif /* HEAP_MEMORY_DEBUG */
|
||||
|
||||
#define FREE_AND_RESET(a) \
|
||||
do { \
|
||||
if (a) { \
|
||||
osi_free(a); \
|
||||
a = NULL; \
|
||||
} \
|
||||
}while (0)
|
||||
|
||||
|
||||
#endif /* _ALLOCATOR_H_ */
|
||||
@@ -0,0 +1,59 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _BUFFER_H_
|
||||
#define _BUFFER_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct buffer_t buffer_t;
|
||||
|
||||
// Returns a new buffer of |size| bytes. Returns NULL if a buffer could not be
|
||||
// allocated. |size| must be non-zero. The caller must release this buffer with
|
||||
// |buffer_free|.
|
||||
buffer_t *buffer_new(size_t size);
|
||||
|
||||
// Creates a new reference to the buffer |buf|. A reference is indistinguishable
|
||||
// from the original: writes to the original will be reflected in the reference
|
||||
// and vice versa. In other words, this function creates an alias to |buf|. The
|
||||
// caller must release the returned buffer with |buffer_free|. Note that releasing
|
||||
// the returned buffer does not release |buf|. |buf| must not be NULL.
|
||||
buffer_t *buffer_new_ref(const buffer_t *buf);
|
||||
|
||||
// Creates a new reference to the last |slice_size| bytes of |buf|. See
|
||||
// |buffer_new_ref| for a description of references. |slice_size| must be
|
||||
// greater than 0 and may be at most |buffer_length|
|
||||
// (0 < slice_size <= buffer_length). |buf| must not be NULL.
|
||||
buffer_t *buffer_new_slice(const buffer_t *buf, size_t slice_size);
|
||||
|
||||
// Frees a buffer object. |buf| may be NULL.
|
||||
void buffer_free(buffer_t *buf);
|
||||
|
||||
// Returns a pointer to a writeable memory region for |buf|. All references
|
||||
// and slices that share overlapping bytes will also be written to when
|
||||
// writing to the returned pointer. The caller may safely write up to
|
||||
// |buffer_length| consecutive bytes starting at the address returned by
|
||||
// this function. |buf| must not be NULL.
|
||||
void *buffer_ptr(const buffer_t *buf);
|
||||
|
||||
// Returns the length of the writeable memory region referred to by |buf|.
|
||||
// |buf| must not be NULL.
|
||||
size_t buffer_length(const buffer_t *buf);
|
||||
|
||||
#endif /*_BUFFER_H_*/
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_H__
|
||||
#define __CONFIG_H__
|
||||
|
||||
// This module implements a configuration parser. Clients can query the
|
||||
// contents of a configuration file through the interface provided here.
|
||||
// The current implementation is read-only; mutations are only kept in
|
||||
// memory. This parser supports the INI file format.
|
||||
|
||||
// Implementation notes:
|
||||
// - Key/value pairs that are not within a section are assumed to be under
|
||||
// the |CONFIG_DEFAULT_SECTION| section.
|
||||
// - Multiple sections with the same name will be merged as if they were in
|
||||
// a single section.
|
||||
// - Empty sections with no key/value pairs will be treated as if they do
|
||||
// not exist. In other words, |config_has_section| will return false for
|
||||
// empty sections.
|
||||
// - Duplicate keys in a section will overwrite previous values.
|
||||
// - All strings are case sensitive.
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// The default section name to use if a key/value pair is not defined within
|
||||
// a section.
|
||||
#define CONFIG_DEFAULT_SECTION "Global"
|
||||
|
||||
typedef struct config_t config_t;
|
||||
typedef struct config_section_node_t config_section_node_t;
|
||||
|
||||
// Creates a new config object with no entries (i.e. not backed by a file).
|
||||
// This function returns a config object or NULL on error. Clients must call
|
||||
// |config_free| on the returned handle when it is no longer required.
|
||||
config_t *config_new_empty(void);
|
||||
|
||||
// Loads the specified file and returns a handle to the config file. If there
|
||||
// was a problem loading the file or allocating memory, this function returns
|
||||
// NULL. Clients must call |config_free| on the returned handle when it is no
|
||||
// longer required. |filename| must not be NULL and must point to a readable
|
||||
// file on the filesystem.
|
||||
config_t *config_new(const char *filename);
|
||||
|
||||
// Frees resources associated with the config file. No further operations may
|
||||
// be performed on the |config| object after calling this function. |config|
|
||||
// may be NULL.
|
||||
void config_free(config_t *config);
|
||||
|
||||
// Returns true if the config file contains a section named |section|. If
|
||||
// the section has no key/value pairs in it, this function will return false.
|
||||
// |config| and |section| must not be NULL.
|
||||
bool config_has_section(const config_t *config, const char *section);
|
||||
|
||||
// Returns true if the config file has a key named |key| under |section|.
|
||||
// Returns false otherwise. |config|, |section|, and |key| must not be NULL.
|
||||
bool config_has_key(const config_t *config, const char *section, const char *key);
|
||||
|
||||
// Returns true if the config file has a key named |key| and the key_value.
|
||||
// Returns false otherwise. |config|, |key|, and |key_value| must not be NULL.
|
||||
bool config_has_key_in_section(config_t *config, const char *key, char *key_value);
|
||||
|
||||
// Returns the integral value for a given |key| in |section|. If |section|
|
||||
// or |key| do not exist, or the value cannot be fully converted to an integer,
|
||||
// this function returns |def_value|. |config|, |section|, and |key| must not
|
||||
// be NULL.
|
||||
int config_get_int(const config_t *config, const char *section, const char *key, int def_value);
|
||||
|
||||
// Returns the boolean value for a given |key| in |section|. If |section|
|
||||
// or |key| do not exist, or the value cannot be converted to a boolean, this
|
||||
// function returns |def_value|. |config|, |section|, and |key| must not be NULL.
|
||||
bool config_get_bool(const config_t *config, const char *section, const char *key, bool def_value);
|
||||
|
||||
// Returns the string value for a given |key| in |section|. If |section| or
|
||||
// |key| do not exist, this function returns |def_value|. The returned string
|
||||
// is owned by the config module and must not be freed. |config|, |section|,
|
||||
// and |key| must not be NULL. |def_value| may be NULL.
|
||||
const char *config_get_string(const config_t *config, const char *section, const char *key, const char *def_value);
|
||||
|
||||
// Sets an integral value for the |key| in |section|. If |key| or |section| do
|
||||
// not already exist, this function creates them. |config|, |section|, and |key|
|
||||
// must not be NULL.
|
||||
void config_set_int(config_t *config, const char *section, const char *key, int value);
|
||||
|
||||
// Sets a boolean value for the |key| in |section|. If |key| or |section| do
|
||||
// not already exist, this function creates them. |config|, |section|, and |key|
|
||||
// must not be NULL.
|
||||
void config_set_bool(config_t *config, const char *section, const char *key, bool value);
|
||||
|
||||
// Sets a string value for the |key| in |section|. If |key| or |section| do
|
||||
// not already exist, this function creates them. |config|, |section|, |key|, and
|
||||
// |value| must not be NULL.
|
||||
void config_set_string(config_t *config, const char *section, const char *key, const char *value, bool insert_back);
|
||||
|
||||
// Removes |section| from the |config| (and, as a result, all keys in the section).
|
||||
// Returns true if |section| was found and removed from |config|, false otherwise.
|
||||
// Neither |config| nor |section| may be NULL.
|
||||
bool config_remove_section(config_t *config, const char *section);
|
||||
|
||||
// Updates |section| to be the first section in |config|. Return true if |section| is in
|
||||
// |config| and updated successfully, false otherwise.
|
||||
// Neither |config| nor |section| may be NULL.
|
||||
bool config_update_newest_section(config_t *config, const char *section);
|
||||
|
||||
// Removes one specific |key| residing in |section| of the |config|. Returns true
|
||||
// if the section and key were found and the key was removed, false otherwise.
|
||||
// None of |config|, |section|, or |key| may be NULL.
|
||||
bool config_remove_key(config_t *config, const char *section, const char *key);
|
||||
|
||||
// Returns an iterator to the first section in the config file. If there are no
|
||||
// sections, the iterator will equal the return value of |config_section_end|.
|
||||
// The returned pointer must be treated as an opaque handle and must not be freed.
|
||||
// The iterator is invalidated on any config mutating operation. |config| may not
|
||||
// be NULL.
|
||||
const config_section_node_t *config_section_begin(const config_t *config);
|
||||
|
||||
// Returns an iterator to one past the last section in the config file. It does not
|
||||
// represent a valid section, but can be used to determine if all sections have been
|
||||
// iterated over. The returned pointer must be treated as an opaque handle and must
|
||||
// not be freed and must not be iterated on (must not call |config_section_next| on
|
||||
// it). |config| may not be NULL.
|
||||
const config_section_node_t *config_section_end(const config_t *config);
|
||||
|
||||
// Moves |iter| to the next section. If there are no more sections, |iter| will
|
||||
// equal the value of |config_section_end|. |iter| may not be NULL and must be
|
||||
// a pointer returned by either |config_section_begin| or |config_section_next|.
|
||||
const config_section_node_t *config_section_next(const config_section_node_t *iter);
|
||||
|
||||
// Returns the name of the section referred to by |iter|. The returned pointer is
|
||||
// owned by the config module and must not be freed by the caller. The pointer will
|
||||
// remain valid until |config_free| is called. |iter| may not be NULL and must not
|
||||
// equal the value returned by |config_section_end|.
|
||||
const char *config_section_name(const config_section_node_t *iter);
|
||||
|
||||
// Saves |config| to a file given by |filename|. Note that this could be a destructive
|
||||
// operation: if |filename| already exists, it will be overwritten. The config
|
||||
// module does not preserve comments or formatting so if a config file was opened
|
||||
// with |config_new| and subsequently overwritten with |config_save|, all comments
|
||||
// and special formatting in the original file will be lost. Neither |config| nor
|
||||
// |filename| may be NULL.
|
||||
bool config_save(const config_t *config, const char *filename);
|
||||
|
||||
#endif /* #ifndef __CONFIG_H__ */
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _FIXED_PKT_QUEUE_H_
|
||||
#define _FIXED_PKT_QUEUE_H_
|
||||
|
||||
|
||||
#include "osi/pkt_queue.h"
|
||||
#include "osi/semaphore.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef FIXED_PKT_QUEUE_SIZE_MAX
|
||||
#define FIXED_PKT_QUEUE_SIZE_MAX 254
|
||||
#endif
|
||||
|
||||
#define FIXED_PKT_QUEUE_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT
|
||||
|
||||
struct fixed_pkt_queue_t;
|
||||
|
||||
typedef struct fixed_pkt_queue_t fixed_pkt_queue_t;
|
||||
|
||||
typedef void (*fixed_pkt_queue_free_cb)(pkt_linked_item_t *data);
|
||||
typedef void (*fixed_pkt_queue_cb)(fixed_pkt_queue_t *queue);
|
||||
|
||||
// Creates a new fixed queue with the given |capacity|. If more elements than
|
||||
// |capacity| are added to the queue, the caller is blocked until space is
|
||||
// made available in the queue. Returns NULL on failure. The caller must free
|
||||
// the returned queue with |fixed_pkt_queue_free|.
|
||||
fixed_pkt_queue_t *fixed_pkt_queue_new(size_t capacity);
|
||||
|
||||
// Freeing a queue that is currently in use (i.e. has waiters
|
||||
// blocked on it) results in undefined behaviour.
|
||||
void fixed_pkt_queue_free(fixed_pkt_queue_t *queue, fixed_pkt_queue_free_cb free_cb);
|
||||
|
||||
// Returns a value indicating whether the given |queue| is empty. If |queue|
|
||||
// is NULL, the return value is true.
|
||||
bool fixed_pkt_queue_is_empty(fixed_pkt_queue_t *queue);
|
||||
|
||||
// Returns the length of the |queue|. If |queue| is NULL, the return value
|
||||
// is 0.
|
||||
size_t fixed_pkt_queue_length(fixed_pkt_queue_t *queue);
|
||||
|
||||
// Returns the maximum number of elements this queue may hold. |queue| may
|
||||
// not be NULL.
|
||||
size_t fixed_pkt_queue_capacity(fixed_pkt_queue_t *queue);
|
||||
|
||||
// Enqueues the given |data| into the |queue|. The caller will be blocked or immediately return or wait for timeout according to the parameter timeout.
|
||||
// If enqueue failed, it will return false, otherwise return true
|
||||
bool fixed_pkt_queue_enqueue(fixed_pkt_queue_t *queue, pkt_linked_item_t *linked_pkt, uint32_t timeout);
|
||||
|
||||
// Dequeues the next element from |queue|. If the queue is currently empty,
|
||||
// this function will block the caller until an item is enqueued or immediately return or wait for timeout according to the parameter timeout.
|
||||
// If dequeue failed, it will return NULL, otherwise return a point.
|
||||
pkt_linked_item_t *fixed_pkt_queue_dequeue(fixed_pkt_queue_t *queue, uint32_t timeout);
|
||||
|
||||
// Returns the first element from |queue|, if present, without dequeuing it.
|
||||
// This function will never block the caller. Returns NULL if there are no
|
||||
// elements in the queue or |queue| is NULL.
|
||||
pkt_linked_item_t *fixed_pkt_queue_try_peek_first(fixed_pkt_queue_t *queue);
|
||||
|
||||
// Registers |queue| with |reactor| for dequeue operations. When there is an element
|
||||
// in the queue, ready_cb will be called. The |context| parameter is passed, untouched,
|
||||
// to the callback routine. Neither |queue|, nor |reactor|, nor |read_cb| may be NULL.
|
||||
// |context| may be NULL.
|
||||
void fixed_pkt_queue_register_dequeue(fixed_pkt_queue_t *queue, fixed_pkt_queue_cb ready_cb);
|
||||
|
||||
// Unregisters the dequeue ready callback for |queue| from whichever reactor
|
||||
// it is registered with, if any. This function is idempotent.
|
||||
void fixed_pkt_queue_unregister_dequeue(fixed_pkt_queue_t *queue);
|
||||
|
||||
void fixed_pkt_queue_process(fixed_pkt_queue_t *queue);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,125 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _FIXED_QUEUE_H_
|
||||
#define _FIXED_QUEUE_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "osi/list.h"
|
||||
#include "osi/semaphore.h"
|
||||
|
||||
#ifndef QUEUE_SIZE_MAX
|
||||
#define QUEUE_SIZE_MAX 254
|
||||
#endif
|
||||
|
||||
#define FIXED_QUEUE_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT
|
||||
|
||||
struct fixed_queue_t;
|
||||
|
||||
typedef struct fixed_queue_t fixed_queue_t;
|
||||
//typedef struct reactor_t reactor_t;
|
||||
|
||||
typedef void (*fixed_queue_free_cb)(void *data);
|
||||
typedef void (*fixed_queue_cb)(fixed_queue_t *queue);
|
||||
|
||||
// Creates a new fixed queue with the given |capacity|. If more elements than
|
||||
// |capacity| are added to the queue, the caller is blocked until space is
|
||||
// made available in the queue. Returns NULL on failure. The caller must free
|
||||
// the returned queue with |fixed_queue_free|.
|
||||
fixed_queue_t *fixed_queue_new(size_t capacity);
|
||||
|
||||
// Freeing a queue that is currently in use (i.e. has waiters
|
||||
// blocked on it) results in undefined behaviour.
|
||||
void fixed_queue_free(fixed_queue_t *queue, fixed_queue_free_cb free_cb);
|
||||
|
||||
// Returns a value indicating whether the given |queue| is empty. If |queue|
|
||||
// is NULL, the return value is true.
|
||||
bool fixed_queue_is_empty(fixed_queue_t *queue);
|
||||
|
||||
// Returns the length of the |queue|. If |queue| is NULL, the return value
|
||||
// is 0.
|
||||
size_t fixed_queue_length(fixed_queue_t *queue);
|
||||
|
||||
// Returns the maximum number of elements this queue may hold. |queue| may
|
||||
// not be NULL.
|
||||
size_t fixed_queue_capacity(fixed_queue_t *queue);
|
||||
|
||||
// Enqueues the given |data| into the |queue|. The caller will be blocked or immediately return or wait for timeout according to the parameter timeout.
|
||||
// If enqueue failed, it will return false, otherwise return true
|
||||
bool fixed_queue_enqueue(fixed_queue_t *queue, void *data, uint32_t timeout);
|
||||
|
||||
// Dequeues the next element from |queue|. If the queue is currently empty,
|
||||
// this function will block the caller until an item is enqueued or immediately return or wait for timeout according to the parameter timeout.
|
||||
// If dequeue failed, it will return NULL, otherwise return a point.
|
||||
void *fixed_queue_dequeue(fixed_queue_t *queue, uint32_t timeout);
|
||||
|
||||
// Returns the first element from |queue|, if present, without dequeuing it.
|
||||
// This function will never block the caller. Returns NULL if there are no
|
||||
// elements in the queue or |queue| is NULL.
|
||||
void *fixed_queue_try_peek_first(fixed_queue_t *queue);
|
||||
|
||||
// Returns the last element from |queue|, if present, without dequeuing it.
|
||||
// This function will never block the caller. Returns NULL if there are no
|
||||
// elements in the queue or |queue| is NULL.
|
||||
void *fixed_queue_try_peek_last(fixed_queue_t *queue);
|
||||
|
||||
// Tries to remove a |data| element from the middle of the |queue|. This
|
||||
// function will never block the caller. If the queue is empty or NULL, this
|
||||
// function returns NULL immediately. |data| may not be NULL. If the |data|
|
||||
// element is found in the queue, a pointer to the removed data is returned,
|
||||
// otherwise NULL.
|
||||
void *fixed_queue_try_remove_from_queue(fixed_queue_t *queue, void *data);
|
||||
|
||||
// Returns the iterateable list with all entries in the |queue|. This function
|
||||
// will never block the caller. |queue| may not be NULL.
|
||||
//
|
||||
// NOTE: The return result of this function is not thread safe: the list could
|
||||
// be modified by another thread, and the result would be unpredictable.
|
||||
// TODO: The usage of this function should be refactored, and the function
|
||||
// itself should be removed.
|
||||
list_t *fixed_queue_get_list(fixed_queue_t *queue);
|
||||
|
||||
// This function returns a valid file descriptor. Callers may perform one
|
||||
// operation on the fd: select(2). If |select| indicates that the file
|
||||
// descriptor is readable, the caller may call |fixed_queue_enqueue| without
|
||||
// blocking. The caller must not close the returned file descriptor. |queue|
|
||||
// may not be NULL.
|
||||
//int fixed_queue_get_enqueue_fd(const fixed_queue_t *queue);
|
||||
|
||||
// This function returns a valid file descriptor. Callers may perform one
|
||||
// operation on the fd: select(2). If |select| indicates that the file
|
||||
// descriptor is readable, the caller may call |fixed_queue_dequeue| without
|
||||
// blocking. The caller must not close the returned file descriptor. |queue|
|
||||
// may not be NULL.
|
||||
//int fixed_queue_get_dequeue_fd(const fixed_queue_t *queue);
|
||||
|
||||
// Registers |queue| with |reactor| for dequeue operations. When there is an element
|
||||
// in the queue, ready_cb will be called. The |context| parameter is passed, untouched,
|
||||
// to the callback routine. Neither |queue|, nor |reactor|, nor |read_cb| may be NULL.
|
||||
// |context| may be NULL.
|
||||
void fixed_queue_register_dequeue(fixed_queue_t *queue, fixed_queue_cb ready_cb);
|
||||
|
||||
// Unregisters the dequeue ready callback for |queue| from whichever reactor
|
||||
// it is registered with, if any. This function is idempotent.
|
||||
void fixed_queue_unregister_dequeue(fixed_queue_t *queue);
|
||||
|
||||
void fixed_queue_process(fixed_queue_t *queue);
|
||||
|
||||
list_t *fixed_queue_get_list(fixed_queue_t *queue);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __FUTURE_H__
|
||||
#define __FUTURE_H__
|
||||
|
||||
#include "osi/semaphore.h"
|
||||
|
||||
struct future {
|
||||
bool ready_can_be_called;
|
||||
osi_sem_t semaphore; // NULL semaphore means immediate future
|
||||
void *result;
|
||||
};
|
||||
typedef struct future future_t;
|
||||
|
||||
#define FUTURE_SUCCESS ((void *)1)
|
||||
#define FUTURE_FAIL ((void *)0)
|
||||
|
||||
// Constructs a new future_t object. Returns NULL on failure.
|
||||
future_t *future_new(void);
|
||||
|
||||
// Constructs a new future_t object with an immediate |value|. No waiting will
|
||||
// occur in the call to |future_await| because the value is already present.
|
||||
// Returns NULL on failure.
|
||||
future_t *future_new_immediate(void *value);
|
||||
|
||||
// Signals that the |future| is ready, passing |value| back to the context
|
||||
// waiting for the result. Must only be called once for every future.
|
||||
// |future| may not be NULL.
|
||||
void future_ready(future_t *future, void *value);
|
||||
|
||||
// Waits for the |future| to be ready. Returns the value set in |future_ready|.
|
||||
// Frees the future before return. |future| may not be NULL.
|
||||
void *future_await(future_t *async_result);
|
||||
|
||||
//Free the future if this "future" is not used
|
||||
void future_free(future_t *future);
|
||||
#endif /* __FUTURE_H__ */
|
||||
@@ -0,0 +1,37 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _HASH_FUNCTIONS_H_
|
||||
#define _HASH_FUNCTIONS_H_
|
||||
|
||||
#include "osi/hash_map.h"
|
||||
|
||||
typedef unsigned char hash_key_t[4];
|
||||
|
||||
hash_index_t hash_function_naive(const void *key);
|
||||
|
||||
hash_index_t hash_function_integer(const void *key);
|
||||
|
||||
// Hashes a pointer based only on its address value
|
||||
hash_index_t hash_function_pointer(const void *key);
|
||||
|
||||
hash_index_t hash_function_string(const void *key);
|
||||
|
||||
void hash_function_blob(const unsigned char *s, unsigned int len, hash_key_t h);
|
||||
|
||||
#endif /* _HASH_FUNCTIONS_H_ */
|
||||
@@ -0,0 +1,110 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _HASH_MAP_H_
|
||||
#define _HASH_MAP_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct hash_map_t;
|
||||
typedef struct hash_map_t hash_map_t;
|
||||
|
||||
typedef struct hash_map_entry_t {
|
||||
const void *key;
|
||||
void *data;
|
||||
const hash_map_t *hash_map;
|
||||
} hash_map_entry_t;
|
||||
|
||||
typedef size_t hash_index_t;
|
||||
|
||||
// Takes a key structure and returns a hash value.
|
||||
typedef hash_index_t (*hash_index_fn)(const void *key);
|
||||
typedef bool (*hash_map_iter_cb)(hash_map_entry_t *hash_entry, void *context);
|
||||
|
||||
typedef bool (*key_equality_fn)(const void *x, const void *y);
|
||||
|
||||
typedef void (*key_free_fn)(void *data);
|
||||
typedef void (*data_free_fn)(void *data);
|
||||
|
||||
// Returns a new, empty hash_map. Returns NULL if not enough memory could be allocated
|
||||
// for the hash_map structure. The returned hash_map must be freed with |hash_map_free|.
|
||||
// The |num_bucket| specifies the number of hashable buckets for the map and must not
|
||||
// be zero. The |hash_fn| specifies a hash function to be used and must not be NULL.
|
||||
// The |key_fn| and |data_fn| are called whenever a hash_map element is removed from
|
||||
// the hash_map. They can be used to release resources held by the hash_map element,
|
||||
// e.g. memory or file descriptor. |key_fn| and |data_fn| may be NULL if no cleanup
|
||||
// is necessary on element removal. |equality_fn| is used to check for key equality.
|
||||
// If |equality_fn| is NULL, default pointer equality is used.
|
||||
hash_map_t *hash_map_new(
|
||||
size_t size,
|
||||
hash_index_fn hash_fn,
|
||||
key_free_fn key_fn,
|
||||
data_free_fn data_fn,
|
||||
key_equality_fn equality_fn);
|
||||
|
||||
// Frees the hash_map. This function accepts NULL as an argument, in which case it
|
||||
// behaves like a no-op.
|
||||
void hash_map_free(hash_map_t *hash_map);
|
||||
|
||||
// Returns true if the hash_map is empty (has no elements), false otherwise.
|
||||
// Note that a NULL |hash_map| is not the same as an empty |hash_map|. This function
|
||||
// does not accept a NULL |hash_map|.
|
||||
//bool hash_map_is_empty(const hash_map_t *hash_map);
|
||||
|
||||
// Returns the number of elements in the hash map. This function does not accept a
|
||||
// NULL |hash_map|.
|
||||
//size_t hash_map_size(const hash_map_t *hash_map);
|
||||
|
||||
// Returns the number of buckets in the hash map. This function does not accept a
|
||||
// NULL |hash_map|.
|
||||
//size_t hash_map_num_buckets(const hash_map_t *hash_map);
|
||||
|
||||
// Returns true if the hash_map has a valid entry for the presented key.
|
||||
// This function does not accept a NULL |hash_map|.
|
||||
bool hash_map_has_key(const hash_map_t *hash_map, const void *key);
|
||||
|
||||
// Returns the element indexed by |key| in the hash_map without removing it. |hash_map|
|
||||
// may not be NULL. Returns NULL if no entry indexed by |key|.
|
||||
void *hash_map_get(const hash_map_t *hash_map, const void *key);
|
||||
|
||||
// Sets the value |data| indexed by |key| into the |hash_map|. Neither |data| nor
|
||||
// |hash_map| may be NULL. This function does not make copies of |data| nor |key|
|
||||
// so the pointers must remain valid at least until the element is removed from the
|
||||
// hash_map or the hash_map is freed. Returns true if |data| could be set, false
|
||||
// otherwise (e.g. out of memory).
|
||||
bool hash_map_set(hash_map_t *hash_map, const void *key, void *data);
|
||||
|
||||
// Removes data indexed by |key| from the hash_map. |hash_map| may not be NULL.
|
||||
// If |key_fn| or |data_fn| functions were specified in |hash_map_new|, they
|
||||
// will be called back with |key| or |data| respectively. This function returns true
|
||||
// if |key| was found in the hash_map and removed, false otherwise.
|
||||
bool hash_map_erase(hash_map_t *hash_map, const void *key);
|
||||
|
||||
// Removes all elements in the hash_map. Calling this function will return the hash_map
|
||||
// to the same state it was in after |hash_map_new|. |hash_map| may not be NULL.
|
||||
void hash_map_clear(hash_map_t *hash_map);
|
||||
|
||||
// Iterates through the entire |hash_map| and calls |callback| for each data
|
||||
// element and passes through the |context| argument. If the hash_map is
|
||||
// empty, |callback| will never be called. It is not safe to mutate the
|
||||
// hash_map inside the callback. Neither |hash_map| nor |callback| may be NULL.
|
||||
// If |callback| returns false, the iteration loop will immediately exit.
|
||||
void hash_map_foreach(hash_map_t *hash_map, hash_map_iter_cb callback, void *context);
|
||||
|
||||
#endif /* _HASH_MAP_H_ */
|
||||
@@ -0,0 +1,121 @@
|
||||
#ifndef _LIST_H_
|
||||
#define _LIST_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
struct list_node_t;
|
||||
typedef struct list_node_t list_node_t;
|
||||
|
||||
struct list_t;
|
||||
typedef struct list_t list_t;
|
||||
|
||||
typedef void (*list_free_cb)(void *data);
|
||||
typedef bool (*list_iter_cb)(void *data, void *context);
|
||||
|
||||
// Returns a new, empty list. Returns NULL if not enough memory could be allocated
|
||||
// for the list structure. The returned list must be freed with |list_free|. The
|
||||
// |callback| specifies a function to be called whenever a list element is removed
|
||||
// from the list. It can be used to release resources held by the list element, e.g.
|
||||
// memory or file descriptor. |callback| may be NULL if no cleanup is necessary on
|
||||
// element removal.
|
||||
list_t *list_new(list_free_cb callback);
|
||||
|
||||
|
||||
list_node_t *list_free_node(list_t *list, list_node_t *node);
|
||||
|
||||
// similar with list_free_node, this function doesn't free the node data
|
||||
list_node_t *list_delete_node(list_t *list, list_node_t *node);
|
||||
|
||||
// Frees the list. This function accepts NULL as an argument, in which case it
|
||||
// behaves like a no-op.
|
||||
void list_free(list_t *list);
|
||||
|
||||
// Returns true if |list| is empty (has no elements), false otherwise.
|
||||
// |list| may not be NULL.
|
||||
bool list_is_empty(const list_t *list);
|
||||
|
||||
// Returns true if the list contains |data|, false otherwise.
|
||||
// |list| may not be NULL.
|
||||
bool list_contains(const list_t *list, const void *data);
|
||||
|
||||
// Returns list_node which contains |data|, NULL otherwise.
|
||||
// |list| may not be NULL.
|
||||
list_node_t *list_get_node(const list_t *list, const void *data);
|
||||
|
||||
// Returns the length of the |list|. |list| may not be NULL.
|
||||
size_t list_length(const list_t *list);
|
||||
|
||||
// Returns the first element in the list without removing it. |list| may not
|
||||
// be NULL or empty.
|
||||
void *list_front(const list_t *list);
|
||||
|
||||
// Returns the last element in the list without removing it. |list| may not
|
||||
// be NULL or empty.
|
||||
void *list_back(const list_t *list);
|
||||
list_node_t *list_back_node(const list_t *list);
|
||||
|
||||
// Inserts |data| after |prev_node| in |list|. |data|, |list|, and |prev_node|
|
||||
// may not be NULL. This function does not make a copy of |data| so the pointer
|
||||
// must remain valid at least until the element is removed from the list or the
|
||||
// list is freed. Returns true if |data| could be inserted, false otherwise
|
||||
// (e.g. out of memory).
|
||||
bool list_insert_after(list_t *list, list_node_t *prev_node, void *data);
|
||||
|
||||
// Inserts |data| at the beginning of |list|. Neither |data| nor |list| may be NULL.
|
||||
// This function does not make a copy of |data| so the pointer must remain valid
|
||||
// at least until the element is removed from the list or the list is freed.
|
||||
// Returns true if |data| could be inserted, false otherwise (e.g. out of memory).
|
||||
bool list_prepend(list_t *list, void *data);
|
||||
|
||||
// Inserts |data| at the end of |list|. Neither |data| nor |list| may be NULL.
|
||||
// This function does not make a copy of |data| so the pointer must remain valid
|
||||
// at least until the element is removed from the list or the list is freed.
|
||||
// Returns true if |data| could be inserted, false otherwise (e.g. out of memory).
|
||||
bool list_append(list_t *list, void *data);
|
||||
|
||||
// Removes |data| from the list. Neither |list| nor |data| may be NULL. If |data|
|
||||
// is inserted multiple times in the list, this function will only remove the first
|
||||
// instance. If a free function was specified in |list_new|, it will be called back
|
||||
// with |data|. This function returns true if |data| was found in the list and removed,
|
||||
// false otherwise.
|
||||
//list_node_t list_remove_node(list_t *list, list_node_t *prev_node, list_node_t *node);
|
||||
//list_node_t list_insert_node(list_t *list, list_node_t *prev_node, list_node_t *node);
|
||||
|
||||
bool list_remove(list_t *list, void *data);
|
||||
|
||||
// similar with list_remove, but do not free the node data
|
||||
bool list_delete(list_t *list, void *data);
|
||||
|
||||
// Removes all elements in the list. Calling this function will return the list to the
|
||||
// same state it was in after |list_new|. |list| may not be NULL.
|
||||
void list_clear(list_t *list);
|
||||
|
||||
// Iterates through the entire |list| and calls |callback| for each data element.
|
||||
// If the list is empty, |callback| will never be called. It is safe to mutate the
|
||||
// list inside the callback. If an element is added before the node being visited,
|
||||
// there will be no callback for the newly-inserted node. Neither |list| nor
|
||||
// |callback| may be NULL.
|
||||
list_node_t *list_foreach(const list_t *list, list_iter_cb callback, void *context);
|
||||
|
||||
// Returns an iterator to the first element in |list|. |list| may not be NULL.
|
||||
// The returned iterator is valid as long as it does not equal the value returned
|
||||
// by |list_end|.
|
||||
list_node_t *list_begin(const list_t *list);
|
||||
|
||||
// Returns an iterator that points past the end of the list. In other words,
|
||||
// this function returns the value of an invalid iterator for the given list.
|
||||
// When an iterator has the same value as what's returned by this function, you
|
||||
// may no longer call |list_next| with the iterator. |list| may not be NULL.
|
||||
list_node_t *list_end(const list_t *list);
|
||||
|
||||
// Given a valid iterator |node|, this function returns the next value for the
|
||||
// iterator. If the returned value equals the value returned by |list_end|, the
|
||||
// iterator has reached the end of the list and may no longer be used for any
|
||||
// purpose.
|
||||
list_node_t *list_next(const list_node_t *node);
|
||||
|
||||
// Returns the value stored at the location pointed to by the iterator |node|.
|
||||
// |node| must not equal the value returned by |list_end|.
|
||||
void *list_node(const list_node_t *node);
|
||||
|
||||
#endif /* _LIST_H_ */
|
||||
@@ -0,0 +1,52 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MUTEX_H__
|
||||
#define __MUTEX_H__
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "osi/semaphore.h"
|
||||
|
||||
#define OSI_MUTEX_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT
|
||||
|
||||
#define osi_mutex_valid( x ) ( ( ( *x ) == NULL) ? pdFALSE : pdTRUE )
|
||||
#define osi_mutex_set_invalid( x ) ( ( *x ) = NULL )
|
||||
|
||||
typedef SemaphoreHandle_t osi_mutex_t;
|
||||
|
||||
int osi_mutex_new(osi_mutex_t *mutex);
|
||||
|
||||
int osi_mutex_lock(osi_mutex_t *mutex, uint32_t timeout);
|
||||
|
||||
void osi_mutex_unlock(osi_mutex_t *mutex);
|
||||
|
||||
void osi_mutex_free(osi_mutex_t *mutex);
|
||||
|
||||
/* Just for a global mutex */
|
||||
int osi_mutex_global_init(void);
|
||||
|
||||
void osi_mutex_global_deinit(void);
|
||||
|
||||
void osi_mutex_global_lock(void);
|
||||
|
||||
void osi_mutex_global_unlock(void);
|
||||
|
||||
#endif /* __MUTEX_H__ */
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
#ifndef _OSI_H_
|
||||
#define _OSI_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define UNUSED_ATTR __attribute__((unused))
|
||||
|
||||
#define CONCAT(a, b) a##b
|
||||
#define COMPILE_ASSERT(x)
|
||||
|
||||
int osi_init(void);
|
||||
void osi_deinit(void);
|
||||
|
||||
#endif /*_OSI_H_*/
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _PKT_LIST_H_
|
||||
#define _PKT_LIST_H_
|
||||
|
||||
#include "sys/queue.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct pkt_queue;
|
||||
|
||||
typedef struct pkt_linked_item {
|
||||
STAILQ_ENTRY(pkt_linked_item) next;
|
||||
uint8_t data[];
|
||||
} pkt_linked_item_t;
|
||||
|
||||
#define BT_PKT_LINKED_HDR_SIZE (sizeof (pkt_linked_item_t))
|
||||
|
||||
typedef void (*pkt_queue_free_cb)(pkt_linked_item_t *item);
|
||||
|
||||
/*
|
||||
* brief: create a pkt_queue instance. pkt_queue is a wrapper class of a FIFO implemented by single linked list.
|
||||
* The enqueue and dequeue operations of the FIFO are protected against race conditions of multiple tasks
|
||||
* return: NULL if not enough memory, otherwise a valid pointer
|
||||
*/
|
||||
struct pkt_queue *pkt_queue_create(void);
|
||||
|
||||
/*
|
||||
* brief: enqueue one item to the FIFO
|
||||
* param queue: pkt_queue instance created using pkt_queue_create
|
||||
* param item: the item to be enqueued to the FIFO
|
||||
* return: true if enqueued successfully, false when the arguments passed in are invalid
|
||||
*/
|
||||
bool pkt_queue_enqueue(struct pkt_queue *queue, pkt_linked_item_t *item);
|
||||
|
||||
/*
|
||||
* brief: dequeue one item for the FIFO
|
||||
* param queue: pkt_queue instance created using pkt_queue_create
|
||||
* return: pointer of type pkt_linked_item_t dequeued, NULL if the queue is empty or upon exception
|
||||
*/
|
||||
pkt_linked_item_t *pkt_queue_dequeue(struct pkt_queue *queue);
|
||||
|
||||
/*
|
||||
* brief: get the pointer of the first item from the FIFO but not get it dequeued
|
||||
* param queue: pkt_queue instance created using pkt_queue_create
|
||||
* return: pointer of the first item in the FIFO, NULL if the FIFO is empty
|
||||
*/
|
||||
pkt_linked_item_t *pkt_queue_try_peek_first(struct pkt_queue *queue);
|
||||
|
||||
/*
|
||||
* brief: retrieve the number of items existing in the FIFO
|
||||
* param queue: pkt_queue instance created using pkt_queue_create
|
||||
* return: total number of items in the FIFO
|
||||
*/
|
||||
size_t pkt_queue_length(const struct pkt_queue *queue);
|
||||
|
||||
/*
|
||||
* brief: retrieve the status whether the FIFO is empty
|
||||
* param queue: pkt_queue instance created using pkt_queue_create
|
||||
* return: false if the FIFO is not empty, otherwise true
|
||||
*/
|
||||
bool pkt_queue_is_empty(const struct pkt_queue *queue);
|
||||
|
||||
/*
|
||||
* brief: delete the item in the FIFO one by one
|
||||
* param free_cb: destructor function for each item in the FIFO, if set to NULL, will use osi_free_func by default
|
||||
*/
|
||||
void pkt_queue_flush(struct pkt_queue *queue, pkt_queue_free_cb free_cb);
|
||||
|
||||
/*
|
||||
* brief: delete the items in the FIFO and then destroy the pkt_queue instance.
|
||||
* param free_cb: destructor function for each item in the FIFO, if set to NULL, will use osi_free_func by default
|
||||
*/
|
||||
void pkt_queue_destroy(struct pkt_queue *queue, pkt_queue_free_cb free_cb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __SEMAPHORE_H__
|
||||
#define __SEMAPHORE_H__
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#define OSI_SEM_MAX_TIMEOUT 0xffffffffUL
|
||||
|
||||
typedef SemaphoreHandle_t osi_sem_t;
|
||||
|
||||
#define osi_sem_valid( x ) ( ( ( *x ) == NULL) ? pdFALSE : pdTRUE )
|
||||
#define osi_sem_set_invalid( x ) ( ( *x ) = NULL )
|
||||
|
||||
int osi_sem_new(osi_sem_t *sem, uint32_t max_count, uint32_t init_count);
|
||||
|
||||
void osi_sem_free(osi_sem_t *sem);
|
||||
|
||||
int osi_sem_take(osi_sem_t *sem, uint32_t timeout);
|
||||
|
||||
void osi_sem_give(osi_sem_t *sem);
|
||||
|
||||
|
||||
#endif /* __SEMAPHORE_H__ */
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __THREAD_H__
|
||||
#define __THREAD_H__
|
||||
|
||||
#include "freertos/FreeRTOSConfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "osi/semaphore.h"
|
||||
#include "esp_task.h"
|
||||
#include "bt_common.h"
|
||||
|
||||
#define OSI_THREAD_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT
|
||||
|
||||
struct osi_thread;
|
||||
struct osi_event;
|
||||
|
||||
typedef struct osi_thread osi_thread_t;
|
||||
|
||||
typedef void (*osi_thread_func_t)(void *context);
|
||||
|
||||
typedef enum {
|
||||
OSI_THREAD_CORE_0 = 0,
|
||||
OSI_THREAD_CORE_1,
|
||||
OSI_THREAD_CORE_AFFINITY,
|
||||
} osi_thread_core_t;
|
||||
|
||||
/*
|
||||
* brief: Create a thread or task
|
||||
* param name: thread name
|
||||
* param stack_size: thread stack size
|
||||
* param priority: thread priority
|
||||
* param core: the CPU core which this thread run, OSI_THREAD_CORE_AFFINITY means unspecific CPU core
|
||||
* param work_queue_num: speicify queue number, the queue[0] has highest priority, and the priority is decrease by index
|
||||
* return : if create successfully, return thread handler; otherwise return NULL.
|
||||
*/
|
||||
osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priority, osi_thread_core_t core, uint8_t work_queue_num, const size_t work_queue_len[]);
|
||||
|
||||
/*
|
||||
* brief: Destroy a thread or task
|
||||
* param thread: point of thread handler
|
||||
*/
|
||||
void osi_thread_free(osi_thread_t *thread);
|
||||
|
||||
/*
|
||||
* brief: Post an msg to a thread and told the thread call the function
|
||||
* param thread: point of thread handler
|
||||
* param func: callback function that called by target thread
|
||||
* param context: argument of callback function
|
||||
* param queue_idx: the queue which the msg send to
|
||||
* param timeout: post timeout, OSI_THREAD_MAX_TIMEOUT means blocking forever, 0 means never blocking, others means block millisecond
|
||||
* return : if post successfully, return true, otherwise return false
|
||||
*/
|
||||
bool osi_thread_post(osi_thread_t *thread, osi_thread_func_t func, void *context, int queue_idx, uint32_t timeout);
|
||||
|
||||
/*
|
||||
* brief: Set the priority of thread
|
||||
* param thread: point of thread handler
|
||||
* param priority: priority
|
||||
* return : if set successfully, return true, otherwise return false
|
||||
*/
|
||||
bool osi_thread_set_priority(osi_thread_t *thread, int priority);
|
||||
|
||||
/* brief: Get thread name
|
||||
* param thread: point of thread handler
|
||||
* return: constant point of thread name
|
||||
*/
|
||||
const char *osi_thread_name(osi_thread_t *thread);
|
||||
|
||||
/* brief: Get the size of the specified queue
|
||||
* param thread: point of thread handler
|
||||
* param wq_idx: the queue index of the thread
|
||||
* return: queue size
|
||||
*/
|
||||
int osi_thread_queue_wait_size(osi_thread_t *thread, int wq_idx);
|
||||
|
||||
/*
|
||||
* brief: Create an osi_event struct and register the handler function and its argument
|
||||
* An osi_event is a kind of work that can be posted to the workqueue of osi_thread to process,
|
||||
* but the work can have at most one instance the thread workqueue before it is processed. This
|
||||
* allows the "single post, multiple data processing" jobs.
|
||||
* param func: the handler to process the job
|
||||
* param context: the argument to be passed to the handler function when the job is being processed
|
||||
* return: NULL if no memory, otherwise a valid struct pointer
|
||||
*/
|
||||
struct osi_event *osi_event_create(osi_thread_func_t func, void *context);
|
||||
|
||||
/*
|
||||
* brief: Bind an osi_event to a specific work queue for an osi_thread.
|
||||
* After binding is completed, a function call of API osi_thread_post_event will send a work
|
||||
* to the workqueue of the thread, with specified queue index.
|
||||
* param func: event: the pointer to osi_event that is created using osi_event_create
|
||||
* param thread: the pointer to osi_thread that is created using osi_thread_create
|
||||
* param queue_idx: the index of the workqueue of the specified osi_thread, with range starting from 0 to work_queue_num - 1
|
||||
* return: true if osi_event binds to the thread's workqueue successfully, otherwise false
|
||||
*/
|
||||
bool osi_event_bind(struct osi_event* event, osi_thread_t *thread, int queue_idx);
|
||||
|
||||
/*
|
||||
* brief: Destroy the osi_event struct created by osi_event_create and free the allocated memory
|
||||
* param event: the pointer to osi_event
|
||||
*/
|
||||
void osi_event_delete(struct osi_event* event);
|
||||
|
||||
/*
|
||||
* brief: try sending a work to the binded thread's workqueue, so that it can be handled by the worker thread
|
||||
* param event: pointer to osi_event, created by osi_event_create
|
||||
* param timeout: post timeout, OSI_THREAD_MAX_TIMEOUT means blocking forever, 0 means never blocking, others means block millisecond
|
||||
* return: true if the message is enqueued to the thread workqueue, otherwise failed
|
||||
* note: if the return value of function is false, it is the case that the workqueue of the thread is full, and users
|
||||
* are expected to post the event sometime later to get the work handled.
|
||||
*/
|
||||
bool osi_thread_post_event(struct osi_event *event, uint32_t timeout);
|
||||
|
||||
#endif /* __THREAD_H__ */
|
||||
@@ -0,0 +1,312 @@
|
||||
|
||||
#include "bt_common.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/list.h"
|
||||
#include "osi/osi.h"
|
||||
|
||||
struct list_node_t {
|
||||
struct list_node_t *next;
|
||||
void *data;
|
||||
};
|
||||
|
||||
typedef struct list_t {
|
||||
list_node_t *head;
|
||||
list_node_t *tail;
|
||||
size_t length;
|
||||
list_free_cb free_cb;
|
||||
} list_t;
|
||||
|
||||
//static list_node_t *list_free_node_(list_t *list, list_node_t *node);
|
||||
|
||||
// Hidden constructor, only to be used by the hash map for the allocation tracker.
|
||||
// Behaves the same as |list_new|, except you get to specify the allocator.
|
||||
list_t *list_new_internal(list_free_cb callback)
|
||||
{
|
||||
list_t *list = (list_t *) osi_calloc(sizeof(list_t));
|
||||
if (!list) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list->head = list->tail = NULL;
|
||||
list->length = 0;
|
||||
list->free_cb = callback;
|
||||
return list;
|
||||
}
|
||||
|
||||
list_t *list_new(list_free_cb callback)
|
||||
{
|
||||
return list_new_internal(callback);
|
||||
}
|
||||
|
||||
void list_free(list_t *list)
|
||||
{
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
|
||||
list_clear(list);
|
||||
osi_free(list);
|
||||
}
|
||||
|
||||
bool list_is_empty(const list_t *list)
|
||||
{
|
||||
assert(list != NULL);
|
||||
return (list->length == 0);
|
||||
}
|
||||
|
||||
bool list_contains(const list_t *list, const void *data)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(data != NULL);
|
||||
|
||||
for (const list_node_t *node = list_begin(list); node != list_end(list); node = list_next(node)) {
|
||||
if (list_node(node) == data) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
list_node_t *list_get_node(const list_t *list, const void *data)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(data != NULL);
|
||||
list_node_t *p_node_ret = NULL;
|
||||
for (list_node_t *node = list_begin(list); node != list_end(list); node = list_next(node)) {
|
||||
if (list_node(node) == data) {
|
||||
p_node_ret = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return p_node_ret;
|
||||
}
|
||||
|
||||
size_t list_length(const list_t *list)
|
||||
{
|
||||
assert(list != NULL);
|
||||
return list->length;
|
||||
}
|
||||
|
||||
void *list_front(const list_t *list)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(!list_is_empty(list));
|
||||
|
||||
return list->head->data;
|
||||
}
|
||||
|
||||
void *list_back(const list_t *list) {
|
||||
assert(list != NULL);
|
||||
assert(!list_is_empty(list));
|
||||
|
||||
return list->tail->data;
|
||||
}
|
||||
|
||||
list_node_t *list_back_node(const list_t *list) {
|
||||
assert(list != NULL);
|
||||
assert(!list_is_empty(list));
|
||||
|
||||
return list->tail;
|
||||
}
|
||||
|
||||
bool list_insert_after(list_t *list, list_node_t *prev_node, void *data) {
|
||||
assert(list != NULL);
|
||||
assert(prev_node != NULL);
|
||||
assert(data != NULL);
|
||||
list_node_t *node = (list_node_t *)osi_calloc(sizeof(list_node_t));
|
||||
if (!node) {
|
||||
OSI_TRACE_ERROR("%s osi_calloc failed.\n", __FUNCTION__ );
|
||||
return false;
|
||||
}
|
||||
node->next = prev_node->next;
|
||||
node->data = data;
|
||||
prev_node->next = node;
|
||||
if (list->tail == prev_node) {
|
||||
list->tail = node;
|
||||
}
|
||||
++list->length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool list_prepend(list_t *list, void *data)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(data != NULL);
|
||||
list_node_t *node = (list_node_t *)osi_calloc(sizeof(list_node_t));
|
||||
if (!node) {
|
||||
OSI_TRACE_ERROR("%s osi_calloc failed.\n", __FUNCTION__ );
|
||||
return false;
|
||||
}
|
||||
node->next = list->head;
|
||||
node->data = data;
|
||||
list->head = node;
|
||||
if (list->tail == NULL) {
|
||||
list->tail = list->head;
|
||||
}
|
||||
++list->length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool list_append(list_t *list, void *data)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(data != NULL);
|
||||
list_node_t *node = (list_node_t *)osi_calloc(sizeof(list_node_t));
|
||||
if (!node) {
|
||||
OSI_TRACE_ERROR("%s osi_calloc failed.\n", __FUNCTION__ );
|
||||
return false;
|
||||
}
|
||||
node->next = NULL;
|
||||
node->data = data;
|
||||
if (list->tail == NULL) {
|
||||
list->head = node;
|
||||
list->tail = node;
|
||||
} else {
|
||||
list->tail->next = node;
|
||||
list->tail = node;
|
||||
}
|
||||
++list->length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool list_remove(list_t *list, void *data)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(data != NULL);
|
||||
|
||||
if (list_is_empty(list)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (list->head->data == data) {
|
||||
list_node_t *next = list_free_node(list, list->head);
|
||||
if (list->tail == list->head) {
|
||||
list->tail = next;
|
||||
}
|
||||
list->head = next;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (list_node_t *prev = list->head, *node = list->head->next; node; prev = node, node = node->next)
|
||||
if (node->data == data) {
|
||||
prev->next = list_free_node(list, node);
|
||||
if (list->tail == node) {
|
||||
list->tail = prev;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool list_delete(list_t *list, void *data)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(data != NULL);
|
||||
|
||||
if (list_is_empty(list)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (list->head->data == data) {
|
||||
list_node_t *next = list_delete_node(list, list->head);
|
||||
if (list->tail == list->head) {
|
||||
list->tail = next;
|
||||
}
|
||||
list->head = next;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (list_node_t *prev = list->head, *node = list->head->next; node; prev = node, node = node->next)
|
||||
if (node->data == data) {
|
||||
prev->next = list_delete_node(list, node);
|
||||
if (list->tail == node) {
|
||||
list->tail = prev;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void list_clear(list_t *list)
|
||||
{
|
||||
assert(list != NULL);
|
||||
for (list_node_t *node = list->head; node; ) {
|
||||
node = list_free_node(list, node);
|
||||
}
|
||||
list->head = NULL;
|
||||
list->tail = NULL;
|
||||
list->length = 0;
|
||||
}
|
||||
|
||||
list_node_t *list_foreach(const list_t *list, list_iter_cb callback, void *context)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(callback != NULL);
|
||||
|
||||
for (list_node_t *node = list->head; node; ) {
|
||||
list_node_t *next = node->next;
|
||||
if (!callback(node->data, context)) {
|
||||
return node;
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_node_t *list_begin(const list_t *list)
|
||||
{
|
||||
assert(list != NULL);
|
||||
return list->head;
|
||||
}
|
||||
|
||||
list_node_t *list_end(UNUSED_ATTR const list_t *list)
|
||||
{
|
||||
assert(list != NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_node_t *list_next(const list_node_t *node)
|
||||
{
|
||||
assert(node != NULL);
|
||||
return node->next;
|
||||
}
|
||||
|
||||
void *list_node(const list_node_t *node)
|
||||
{
|
||||
assert(node != NULL);
|
||||
return node->data;
|
||||
}
|
||||
|
||||
list_node_t *list_free_node(list_t *list, list_node_t *node)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(node != NULL);
|
||||
|
||||
list_node_t *next = node->next;
|
||||
|
||||
if (list->free_cb) {
|
||||
list->free_cb(node->data);
|
||||
}
|
||||
osi_free(node);
|
||||
--list->length;
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
// remove the element from list but do not free the node data
|
||||
list_node_t *list_delete_node(list_t *list, list_node_t *node)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(node != NULL);
|
||||
|
||||
list_node_t *next = node->next;
|
||||
|
||||
osi_free(node);
|
||||
--list->length;
|
||||
|
||||
return next;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "osi/mutex.h"
|
||||
|
||||
|
||||
/* static section */
|
||||
static osi_mutex_t gl_mutex; /* Recursive Type */
|
||||
|
||||
|
||||
/** Create a new mutex
|
||||
* @param mutex pointer to the mutex to create
|
||||
* @return a new mutex */
|
||||
int osi_mutex_new(osi_mutex_t *mutex)
|
||||
{
|
||||
int xReturn = -1;
|
||||
|
||||
*mutex = xSemaphoreCreateMutex();
|
||||
|
||||
if (*mutex != NULL) {
|
||||
xReturn = 0;
|
||||
}
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
|
||||
/** Lock a mutex
|
||||
* @param mutex the mutex to lock */
|
||||
int osi_mutex_lock(osi_mutex_t *mutex, uint32_t timeout)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (timeout == OSI_MUTEX_MAX_TIMEOUT) {
|
||||
if (xSemaphoreTake(*mutex, portMAX_DELAY) != pdTRUE) {
|
||||
ret = -1;
|
||||
}
|
||||
} else {
|
||||
if (xSemaphoreTake(*mutex, timeout / portTICK_PERIOD_MS) != pdTRUE) {
|
||||
ret = -2;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Unlock a mutex
|
||||
* @param mutex the mutex to unlock */
|
||||
void osi_mutex_unlock(osi_mutex_t *mutex)
|
||||
{
|
||||
xSemaphoreGive(*mutex);
|
||||
}
|
||||
|
||||
/** Delete a semaphore
|
||||
* @param mutex the mutex to delete */
|
||||
void osi_mutex_free(osi_mutex_t *mutex)
|
||||
{
|
||||
vSemaphoreDelete(*mutex);
|
||||
*mutex = NULL;
|
||||
}
|
||||
|
||||
int osi_mutex_global_init(void)
|
||||
{
|
||||
gl_mutex = xSemaphoreCreateRecursiveMutex();
|
||||
if (gl_mutex == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osi_mutex_global_deinit(void)
|
||||
{
|
||||
vSemaphoreDelete(gl_mutex);
|
||||
}
|
||||
|
||||
void osi_mutex_global_lock(void)
|
||||
{
|
||||
xSemaphoreTakeRecursive(gl_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
void osi_mutex_global_unlock(void)
|
||||
{
|
||||
xSemaphoreGiveRecursive(gl_mutex);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
#include "osi/osi.h"
|
||||
#include "osi/mutex.h"
|
||||
|
||||
int osi_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (osi_mutex_global_init() != 0) {
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void osi_deinit(void)
|
||||
{
|
||||
osi_mutex_global_deinit();
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "osi/pkt_queue.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/mutex.h"
|
||||
|
||||
|
||||
STAILQ_HEAD(pkt_queue_header, pkt_linked_item);
|
||||
|
||||
struct pkt_queue {
|
||||
osi_mutex_t lock;
|
||||
size_t length;
|
||||
struct pkt_queue_header header;
|
||||
} pkt_queue_t;
|
||||
|
||||
struct pkt_queue *pkt_queue_create(void)
|
||||
{
|
||||
struct pkt_queue *queue = calloc(1, sizeof(struct pkt_queue));
|
||||
if (queue == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (osi_mutex_new(&queue->lock) != 0) {
|
||||
osi_free(queue);
|
||||
}
|
||||
struct pkt_queue_header *p = &queue->header;
|
||||
STAILQ_INIT(p);
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
static void pkt_queue_cleanup(struct pkt_queue *queue, pkt_queue_free_cb free_cb)
|
||||
{
|
||||
if (queue == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct pkt_queue_header *header = &queue->header;
|
||||
pkt_linked_item_t *item = STAILQ_FIRST(header);
|
||||
pkt_linked_item_t *tmp;
|
||||
|
||||
pkt_queue_free_cb free_func = (free_cb != NULL) ? free_cb : (pkt_queue_free_cb)osi_free_func;
|
||||
|
||||
while (item != NULL) {
|
||||
tmp = STAILQ_NEXT(item, next);
|
||||
free_func(item);
|
||||
item = tmp;
|
||||
queue->length--;
|
||||
}
|
||||
STAILQ_INIT(header);
|
||||
queue->length = 0;
|
||||
}
|
||||
|
||||
void pkt_queue_flush(struct pkt_queue *queue, pkt_queue_free_cb free_cb)
|
||||
{
|
||||
if (queue == NULL) {
|
||||
return;
|
||||
}
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
pkt_queue_cleanup(queue, free_cb);
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
}
|
||||
|
||||
void pkt_queue_destroy(struct pkt_queue *queue, pkt_queue_free_cb free_cb)
|
||||
{
|
||||
if (queue == NULL) {
|
||||
return;
|
||||
}
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
pkt_queue_cleanup(queue, free_cb);
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
|
||||
osi_mutex_free(&queue->lock);
|
||||
osi_free(queue);
|
||||
}
|
||||
|
||||
pkt_linked_item_t *pkt_queue_dequeue(struct pkt_queue *queue)
|
||||
{
|
||||
if (queue == NULL || queue->length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pkt_linked_item *item;
|
||||
struct pkt_queue_header *header;
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
header = &queue->header;
|
||||
item = STAILQ_FIRST(header);
|
||||
if (item != NULL) {
|
||||
STAILQ_REMOVE_HEAD(header, next);
|
||||
if (queue->length > 0) {
|
||||
queue->length--;
|
||||
}
|
||||
}
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
bool pkt_queue_enqueue(struct pkt_queue *queue, pkt_linked_item_t *item)
|
||||
{
|
||||
if (queue == NULL || item == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct pkt_queue_header *header;
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
header = &queue->header;
|
||||
STAILQ_INSERT_TAIL(header, item, next);
|
||||
queue->length++;
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t pkt_queue_length(const struct pkt_queue *queue)
|
||||
{
|
||||
if (queue == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return queue->length;
|
||||
}
|
||||
|
||||
bool pkt_queue_is_empty(const struct pkt_queue *queue)
|
||||
{
|
||||
return pkt_queue_length(queue) == 0;
|
||||
}
|
||||
|
||||
pkt_linked_item_t *pkt_queue_try_peek_first(struct pkt_queue *queue)
|
||||
{
|
||||
if (queue == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pkt_queue_header *header = &queue->header;
|
||||
pkt_linked_item_t *item;
|
||||
osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
item = STAILQ_FIRST(header);
|
||||
osi_mutex_unlock(&queue->lock);
|
||||
|
||||
return item;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
#include "osi/semaphore.h"
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
// Creates and returns a new semaphore. The "init_count" argument specifies
|
||||
// the initial state of the semaphore, "max_count" specifies the maximum value
|
||||
// that can be reached.
|
||||
int osi_sem_new(osi_sem_t *sem, uint32_t max_count, uint32_t init_count)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (sem) {
|
||||
*sem = xSemaphoreCreateCounting(max_count, init_count);
|
||||
if ((*sem) != NULL) {
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
// Give a semaphore
|
||||
void osi_sem_give(osi_sem_t *sem)
|
||||
{
|
||||
xSemaphoreGive(*sem);
|
||||
}
|
||||
|
||||
/*
|
||||
Blocks the thread while waiting for the semaphore to be
|
||||
signaled. If the "timeout" argument is non-zero, the thread should
|
||||
only be blocked for the specified time (measured in
|
||||
milliseconds).
|
||||
|
||||
*/
|
||||
int
|
||||
osi_sem_take(osi_sem_t *sem, uint32_t timeout)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (timeout == OSI_SEM_MAX_TIMEOUT) {
|
||||
if (xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE) {
|
||||
ret = -1;
|
||||
}
|
||||
} else {
|
||||
if (xSemaphoreTake(*sem, timeout / portTICK_PERIOD_MS) != pdTRUE) {
|
||||
ret = -2;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Deallocates a semaphore
|
||||
void osi_sem_free(osi_sem_t *sem)
|
||||
{
|
||||
vSemaphoreDelete(*sem);
|
||||
*sem = NULL;
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "osi/allocator.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "osi/semaphore.h"
|
||||
#include "osi/thread.h"
|
||||
#include "osi/mutex.h"
|
||||
|
||||
struct work_item {
|
||||
osi_thread_func_t func;
|
||||
void *context;
|
||||
};
|
||||
|
||||
struct work_queue {
|
||||
QueueHandle_t queue;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
struct osi_thread {
|
||||
TaskHandle_t thread_handle; /*!< Store the thread object */
|
||||
int thread_id; /*!< May for some OS, such as Linux */
|
||||
bool stop;
|
||||
uint8_t work_queue_num; /*!< Work queue number */
|
||||
struct work_queue **work_queues; /*!< Point to queue array, and the priority inverse array index */
|
||||
osi_sem_t work_sem;
|
||||
osi_sem_t stop_sem;
|
||||
};
|
||||
|
||||
struct osi_thread_start_arg {
|
||||
osi_thread_t *thread;
|
||||
osi_sem_t start_sem;
|
||||
int error;
|
||||
};
|
||||
|
||||
struct osi_event {
|
||||
struct work_item item;
|
||||
osi_mutex_t lock;
|
||||
uint16_t is_queued;
|
||||
uint16_t queue_idx;
|
||||
osi_thread_t *thread;
|
||||
};
|
||||
|
||||
static const size_t DEFAULT_WORK_QUEUE_CAPACITY = 100;
|
||||
|
||||
static struct work_queue *osi_work_queue_create(size_t capacity)
|
||||
{
|
||||
if (capacity == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct work_queue *wq = (struct work_queue *)osi_malloc(sizeof(struct work_queue));
|
||||
if (wq != NULL) {
|
||||
wq->queue = xQueueCreate(capacity, sizeof(struct work_item));
|
||||
if (wq->queue != 0) {
|
||||
wq->capacity = capacity;
|
||||
return wq;
|
||||
} else {
|
||||
osi_free(wq);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void osi_work_queue_delete(struct work_queue *wq)
|
||||
{
|
||||
if (wq != NULL) {
|
||||
if (wq->queue != 0) {
|
||||
vQueueDelete(wq->queue);
|
||||
}
|
||||
wq->queue = 0;
|
||||
wq->capacity = 0;
|
||||
osi_free(wq);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static bool osi_thead_work_queue_get(struct work_queue *wq, struct work_item *item)
|
||||
{
|
||||
assert (wq != NULL);
|
||||
assert (wq->queue != 0);
|
||||
assert (item != NULL);
|
||||
|
||||
if (pdTRUE == xQueueReceive(wq->queue, item, 0)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool osi_thead_work_queue_put(struct work_queue *wq, const struct work_item *item, uint32_t timeout)
|
||||
{
|
||||
assert (wq != NULL);
|
||||
assert (wq->queue != 0);
|
||||
assert (item != NULL);
|
||||
|
||||
bool ret = true;
|
||||
if (timeout == OSI_SEM_MAX_TIMEOUT) {
|
||||
if (xQueueSend(wq->queue, item, portMAX_DELAY) != pdTRUE) {
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
if (xQueueSend(wq->queue, item, timeout / portTICK_PERIOD_MS) != pdTRUE) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t osi_thead_work_queue_len(struct work_queue *wq)
|
||||
{
|
||||
assert (wq != NULL);
|
||||
assert (wq->queue != 0);
|
||||
assert (wq->capacity != 0);
|
||||
|
||||
size_t available_spaces = (size_t)uxQueueSpacesAvailable(wq->queue);
|
||||
|
||||
if (available_spaces <= wq->capacity) {
|
||||
return wq->capacity - available_spaces;
|
||||
} else {
|
||||
assert (0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void osi_thread_run(void *arg)
|
||||
{
|
||||
struct osi_thread_start_arg *start = (struct osi_thread_start_arg *)arg;
|
||||
osi_thread_t *thread = start->thread;
|
||||
|
||||
osi_sem_give(&start->start_sem);
|
||||
|
||||
while (1) {
|
||||
int idx = 0;
|
||||
|
||||
osi_sem_take(&thread->work_sem, OSI_SEM_MAX_TIMEOUT);
|
||||
|
||||
if (thread->stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
struct work_item item;
|
||||
while (!thread->stop && idx < thread->work_queue_num) {
|
||||
if (osi_thead_work_queue_get(thread->work_queues[idx], &item) == true) {
|
||||
item.func(item.context);
|
||||
idx = 0;
|
||||
continue;
|
||||
} else {
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thread->thread_handle = NULL;
|
||||
osi_sem_give(&thread->stop_sem);
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static int osi_thread_join(osi_thread_t *thread, uint32_t wait_ms)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
return osi_sem_take(&thread->stop_sem, wait_ms);
|
||||
}
|
||||
|
||||
static void osi_thread_stop(osi_thread_t *thread)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(thread != NULL);
|
||||
|
||||
//stop the thread
|
||||
thread->stop = true;
|
||||
osi_sem_give(&thread->work_sem);
|
||||
|
||||
//join
|
||||
ret = osi_thread_join(thread, 1000); //wait 1000ms
|
||||
|
||||
//if join failed, delete the task here
|
||||
if (ret != 0 && thread->thread_handle) {
|
||||
vTaskDelete(thread->thread_handle);
|
||||
}
|
||||
}
|
||||
|
||||
//in linux, the stack_size, priority and core may not be set here, the code will be ignore the arguments
|
||||
osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priority, osi_thread_core_t core, uint8_t work_queue_num, const size_t work_queue_len[])
|
||||
{
|
||||
int ret;
|
||||
struct osi_thread_start_arg start_arg = {0};
|
||||
|
||||
if (stack_size <= 0 ||
|
||||
core < OSI_THREAD_CORE_0 || core > OSI_THREAD_CORE_AFFINITY ||
|
||||
work_queue_num <= 0 || work_queue_len == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
osi_thread_t *thread = (osi_thread_t *)osi_calloc(sizeof(osi_thread_t));
|
||||
if (thread == NULL) {
|
||||
goto _err;
|
||||
}
|
||||
|
||||
thread->stop = false;
|
||||
thread->work_queues = (struct work_queue **)osi_calloc(sizeof(struct work_queue *) * work_queue_num);
|
||||
if (thread->work_queues == NULL) {
|
||||
goto _err;
|
||||
}
|
||||
thread->work_queue_num = work_queue_num;
|
||||
|
||||
for (int i = 0; i < thread->work_queue_num; i++) {
|
||||
size_t queue_len = work_queue_len[i] ? work_queue_len[i] : DEFAULT_WORK_QUEUE_CAPACITY;
|
||||
thread->work_queues[i] = osi_work_queue_create(queue_len);
|
||||
if (thread->work_queues[i] == NULL) {
|
||||
goto _err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = osi_sem_new(&thread->work_sem, 1, 0);
|
||||
if (ret != 0) {
|
||||
goto _err;
|
||||
}
|
||||
|
||||
ret = osi_sem_new(&thread->stop_sem, 1, 0);
|
||||
if (ret != 0) {
|
||||
goto _err;
|
||||
}
|
||||
|
||||
start_arg.thread = thread;
|
||||
ret = osi_sem_new(&start_arg.start_sem, 1, 0);
|
||||
if (ret != 0) {
|
||||
goto _err;
|
||||
}
|
||||
|
||||
if (xTaskCreatePinnedToCore(osi_thread_run, name, stack_size, &start_arg, priority, &thread->thread_handle, core) != pdPASS) {
|
||||
goto _err;
|
||||
}
|
||||
|
||||
osi_sem_take(&start_arg.start_sem, OSI_SEM_MAX_TIMEOUT);
|
||||
osi_sem_free(&start_arg.start_sem);
|
||||
|
||||
return thread;
|
||||
|
||||
_err:
|
||||
|
||||
if (thread) {
|
||||
if (start_arg.start_sem) {
|
||||
osi_sem_free(&start_arg.start_sem);
|
||||
}
|
||||
|
||||
if (thread->thread_handle) {
|
||||
vTaskDelete(thread->thread_handle);
|
||||
}
|
||||
|
||||
for (int i = 0; i < thread->work_queue_num; i++) {
|
||||
if (thread->work_queues[i]) {
|
||||
osi_work_queue_delete(thread->work_queues[i]);
|
||||
}
|
||||
thread->work_queues[i] = NULL;
|
||||
}
|
||||
|
||||
if (thread->work_queues) {
|
||||
osi_free(thread->work_queues);
|
||||
thread->work_queues = NULL;
|
||||
}
|
||||
|
||||
if (thread->work_sem) {
|
||||
osi_sem_free(&thread->work_sem);
|
||||
}
|
||||
|
||||
if (thread->stop_sem) {
|
||||
osi_sem_free(&thread->stop_sem);
|
||||
}
|
||||
|
||||
osi_free(thread);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void osi_thread_free(osi_thread_t *thread)
|
||||
{
|
||||
if (!thread)
|
||||
return;
|
||||
|
||||
osi_thread_stop(thread);
|
||||
|
||||
for (int i = 0; i < thread->work_queue_num; i++) {
|
||||
if (thread->work_queues[i]) {
|
||||
osi_work_queue_delete(thread->work_queues[i]);
|
||||
thread->work_queues[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (thread->work_queues) {
|
||||
osi_free(thread->work_queues);
|
||||
thread->work_queues = NULL;
|
||||
}
|
||||
|
||||
if (thread->work_sem) {
|
||||
osi_sem_free(&thread->work_sem);
|
||||
}
|
||||
|
||||
if (thread->stop_sem) {
|
||||
osi_sem_free(&thread->stop_sem);
|
||||
}
|
||||
|
||||
|
||||
osi_free(thread);
|
||||
}
|
||||
|
||||
bool osi_thread_post(osi_thread_t *thread, osi_thread_func_t func, void *context, int queue_idx, uint32_t timeout)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(func != NULL);
|
||||
|
||||
if (queue_idx >= thread->work_queue_num) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct work_item item;
|
||||
|
||||
item.func = func;
|
||||
item.context = context;
|
||||
|
||||
if (osi_thead_work_queue_put(thread->work_queues[queue_idx], &item, timeout) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
osi_sem_give(&thread->work_sem);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool osi_thread_set_priority(osi_thread_t *thread, int priority)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
|
||||
vTaskPrioritySet(thread->thread_handle, priority);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *osi_thread_name(osi_thread_t *thread)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
|
||||
return pcTaskGetName(thread->thread_handle);
|
||||
}
|
||||
|
||||
int osi_thread_queue_wait_size(osi_thread_t *thread, int wq_idx)
|
||||
{
|
||||
if (wq_idx < 0 || wq_idx >= thread->work_queue_num) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)(osi_thead_work_queue_len(thread->work_queues[wq_idx]));
|
||||
}
|
||||
|
||||
|
||||
struct osi_event *osi_event_create(osi_thread_func_t func, void *context)
|
||||
{
|
||||
struct osi_event *event = osi_calloc(sizeof(struct osi_event));
|
||||
if (event != NULL) {
|
||||
if (osi_mutex_new(&event->lock) == 0) {
|
||||
event->item.func = func;
|
||||
event->item.context = context;
|
||||
return event;
|
||||
}
|
||||
osi_free(event);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void osi_event_delete(struct osi_event* event)
|
||||
{
|
||||
if (event != NULL) {
|
||||
osi_mutex_free(&event->lock);
|
||||
memset(event, 0, sizeof(struct osi_event));
|
||||
osi_free(event);
|
||||
}
|
||||
}
|
||||
|
||||
bool osi_event_bind(struct osi_event* event, osi_thread_t *thread, int queue_idx)
|
||||
{
|
||||
if (event == NULL || event->thread != NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (thread == NULL || queue_idx >= thread->work_queue_num) {
|
||||
return false;
|
||||
}
|
||||
|
||||
event->thread = thread;
|
||||
event->queue_idx = queue_idx;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void osi_thread_generic_event_handler(void *context)
|
||||
{
|
||||
struct osi_event *event = (struct osi_event *)context;
|
||||
if (event != NULL && event->item.func != NULL) {
|
||||
osi_mutex_lock(&event->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
event->is_queued = 0;
|
||||
osi_mutex_unlock(&event->lock);
|
||||
event->item.func(event->item.context);
|
||||
}
|
||||
}
|
||||
|
||||
bool osi_thread_post_event(struct osi_event *event, uint32_t timeout)
|
||||
{
|
||||
assert(event != NULL && event->thread != NULL);
|
||||
assert(event->queue_idx >= 0 && event->queue_idx < event->thread->work_queue_num);
|
||||
bool ret = false;
|
||||
if (event->is_queued == 0) {
|
||||
uint16_t acquire_cnt = 0;
|
||||
osi_mutex_lock(&event->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
event->is_queued += 1;
|
||||
acquire_cnt = event->is_queued;
|
||||
osi_mutex_unlock(&event->lock);
|
||||
|
||||
if (acquire_cnt == 1) {
|
||||
ret = osi_thread_post(event->thread, osi_thread_generic_event_handler, event, event->queue_idx, timeout);
|
||||
if (!ret) {
|
||||
// clear "is_queued" when post failure, to allow for following event posts
|
||||
osi_mutex_lock(&event->lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
event->is_queued = 0;
|
||||
osi_mutex_unlock(&event->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user