mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
d1db2df316
components/os: Move ETS_T1_WDT_INUM, ETS_CACHEERR_INUM and ETS_DPORT_INUM to l5 interrupt components/os: high level interrupt(5) components/os: hli_api: meta queue: fix out of bounds access, check for overflow components/os: hli: don't spill registers, instead save them to a separate region Level 4 interrupt has a chance of preempting a window overflow or underflow exception. Therefore it is not possible to use standard context save functions, as the SP on entry to Level 4 interrupt may be invalid (e.g. in WindowUnderflow4). Instead, mask window overflows and save the entire general purpose register file, plus some of the special registers. Then clear WindowStart, allowing the C handler to execute without spilling the old windows. On exit from the interrupt handler, do everything in reverse. components/bt: using high level interrupt in lc components/os: Add DRAM_ATTR to avoid feature `Allow .bss segment placed in external memory` components/bt: optimize code structure components/os: Modify the BT assert process to adapt to coredump and HLI components/os: Disable exception mode after saving special registers To store some registers first, avoid stuck due to live lock after disabling exception mode components/os: using dport instead of AHB in BT to fix live lock components/bt: Fix hli queue send error components/bt: Fix CI fail # Conflicts: # components/bt/CMakeLists.txt # components/bt/component.mk # components/bt/controller/bt.c # components/bt/controller/lib # components/esp_common/src/int_wdt.c # components/esp_system/port/soc/esp32/dport_panic_highint_hdl.S # components/soc/esp32/include/soc/soc.h
295 lines
8.8 KiB
C
295 lines
8.8 KiB
C
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
|
// All rights reserved.
|
|
|
|
#include <string.h>
|
|
#include "esp_log.h"
|
|
#include "esp_heap_caps.h"
|
|
#include "xtensa/core-macros.h"
|
|
#include "soc/dport_reg.h"
|
|
#include "hli_api.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/queue.h"
|
|
|
|
|
|
#define HLI_MAX_HANDLERS 4
|
|
|
|
typedef struct {
|
|
intr_handler_t handler;
|
|
void* arg;
|
|
uint32_t intr_reg;
|
|
uint32_t intr_mask;
|
|
} hli_handler_info_t;
|
|
|
|
typedef struct {
|
|
#define CUSTOMER_TYPE_REQUEST (0)
|
|
#define CUSTOMER_TYPE_RELEASE (1)
|
|
struct {
|
|
uint32_t cb_type;
|
|
union {
|
|
int (* request)(uint32_t, uint32_t, uint32_t);
|
|
int (* release)(uint32_t);
|
|
} cb;
|
|
} customer_cb;
|
|
uint32_t arg0, arg1, arg2;
|
|
} customer_swisr_t;
|
|
|
|
static void IRAM_ATTR customer_swisr_handle(customer_swisr_t *cus_swisr)
|
|
{
|
|
if (cus_swisr->customer_cb.cb_type == CUSTOMER_TYPE_REQUEST) {
|
|
if (cus_swisr->customer_cb.cb.request != NULL) {
|
|
cus_swisr->customer_cb.cb.request(cus_swisr->arg0, cus_swisr->arg1, cus_swisr->arg2);
|
|
}
|
|
} else if(cus_swisr->customer_cb.cb_type == CUSTOMER_TYPE_RELEASE) {
|
|
if (cus_swisr->customer_cb.cb.release != NULL) {
|
|
cus_swisr->customer_cb.cb.release(cus_swisr->arg0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static DRAM_ATTR hli_handler_info_t s_hli_handlers[HLI_MAX_HANDLERS];
|
|
// static const char* TAG = "hli_queue";
|
|
|
|
esp_err_t hli_intr_register(intr_handler_t handler, void* arg, uint32_t intr_reg, uint32_t intr_mask)
|
|
{
|
|
for (hli_handler_info_t* hip = s_hli_handlers;
|
|
hip < s_hli_handlers + HLI_MAX_HANDLERS;
|
|
++hip) {
|
|
if (hip->handler == NULL) {
|
|
hip->arg = arg;
|
|
hip->intr_reg = intr_reg;
|
|
hip->intr_mask = intr_mask;
|
|
hip->handler = handler; /* set last, indicates the entry as valid */
|
|
return ESP_OK;
|
|
}
|
|
}
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
|
|
void IRAM_ATTR hli_c_handler(void)
|
|
{
|
|
bool handled = false;
|
|
/* Iterate over registered interrupt handlers,
|
|
* and check if the expected mask is present in the interrupt status register.
|
|
*/
|
|
for (hli_handler_info_t* hip = s_hli_handlers;
|
|
hip < s_hli_handlers + HLI_MAX_HANDLERS;
|
|
++hip) {
|
|
if (hip->handler == NULL) {
|
|
continue;
|
|
}
|
|
uint32_t reg = hip->intr_reg;
|
|
uint32_t val;
|
|
if (reg == 0) { /* special case for CPU internal interrupts */
|
|
val = XTHAL_GET_INTERRUPT();
|
|
} else {
|
|
/* "reg" might not be in DPORT, but this will work in any case */
|
|
val = DPORT_REG_READ(reg);
|
|
}
|
|
if ((val & hip->intr_mask) != 0) {
|
|
handled = true;
|
|
(*hip->handler)(hip->arg);
|
|
}
|
|
}
|
|
if (!handled) {
|
|
// esp_rom_printf(DRAM_STR("hli_c_handler: no handler found!\n"));
|
|
// abort();
|
|
}
|
|
}
|
|
|
|
uint32_t IRAM_ATTR hli_intr_disable(void)
|
|
{
|
|
// disable level 4 and below
|
|
return XTOS_SET_INTLEVEL(XCHAL_DEBUGLEVEL - 2);
|
|
}
|
|
|
|
void IRAM_ATTR hli_intr_restore(uint32_t state)
|
|
{
|
|
XTOS_RESTORE_JUST_INTLEVEL(state);
|
|
}
|
|
|
|
#define HLI_META_QUEUE_SIZE 16
|
|
#define HLI_QUEUE_MAX_ELEM_SIZE 32
|
|
#define HLI_QUEUE_SW_INT_NUM 29
|
|
|
|
#define HLI_QUEUE_FLAG_SEMAPHORE BIT(0)
|
|
#define HLI_QUEUE_FLAG_CUSTOMER BIT(1)
|
|
|
|
static DRAM_ATTR struct hli_queue_t *s_meta_queue_ptr = NULL;
|
|
intr_handle_t ret_handle;
|
|
|
|
static inline char* IRAM_ATTR wrap_ptr(hli_queue_handle_t queue, char *ptr)
|
|
{
|
|
return (ptr == queue->bufend) ? queue->buf : ptr;
|
|
}
|
|
|
|
static inline bool IRAM_ATTR queue_empty(hli_queue_handle_t queue)
|
|
{
|
|
return queue->begin == queue->end;
|
|
}
|
|
|
|
static inline bool IRAM_ATTR queue_full(hli_queue_handle_t queue)
|
|
{
|
|
return wrap_ptr(queue, queue->end + queue->elem_size) == queue->begin;
|
|
}
|
|
|
|
static void IRAM_ATTR queue_isr_handler(void* arg)
|
|
{
|
|
int do_yield = pdFALSE;
|
|
XTHAL_SET_INTCLEAR(BIT(HLI_QUEUE_SW_INT_NUM));
|
|
hli_queue_handle_t queue;
|
|
|
|
while (hli_queue_get(s_meta_queue_ptr, &queue)) {
|
|
static DRAM_ATTR char scratch[HLI_QUEUE_MAX_ELEM_SIZE];
|
|
while (hli_queue_get(queue, scratch)) {
|
|
int res = pdPASS;
|
|
if ((queue->flags & HLI_QUEUE_FLAG_CUSTOMER) != 0) {
|
|
customer_swisr_handle((customer_swisr_t *)scratch);
|
|
} else if ((queue->flags & HLI_QUEUE_FLAG_SEMAPHORE) != 0) {
|
|
res = xSemaphoreGiveFromISR((SemaphoreHandle_t) queue->downstream, &do_yield);
|
|
} else {
|
|
res = xQueueSendFromISR(queue->downstream, scratch, &do_yield);
|
|
}
|
|
if (res == pdFAIL) {
|
|
// ESP_EARLY_LOGE(TAG, "Failed to send to %s %p", (queue->flags & HLI_QUEUE_FLAG_SEMAPHORE) == 0 ? "queue" : "semaphore", queue->downstream);
|
|
}
|
|
}
|
|
}
|
|
if (do_yield) {
|
|
portYIELD_FROM_ISR();
|
|
}
|
|
}
|
|
|
|
/* Notify the level 3 handler that an element is added to the given hli queue.
|
|
* Do this by placing the queue handle onto s_meta_queue, and raising a SW interrupt.
|
|
*
|
|
* This function must be called with HL interrupts disabled!
|
|
*/
|
|
static void IRAM_ATTR queue_signal(hli_queue_handle_t queue)
|
|
{
|
|
/* See if the queue is already in s_meta_queue, before adding */
|
|
bool found = false;
|
|
const hli_queue_handle_t *end = (hli_queue_handle_t*) s_meta_queue_ptr->end;
|
|
hli_queue_handle_t *item = (hli_queue_handle_t*) s_meta_queue_ptr->begin;
|
|
for (;item != end; item = (hli_queue_handle_t*) wrap_ptr(s_meta_queue_ptr, (char*) (item + 1))) {
|
|
if (*item == queue) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
bool res = hli_queue_put(s_meta_queue_ptr, &queue);
|
|
if (!res) {
|
|
esp_rom_printf(DRAM_STR("Fatal error in queue_signal: s_meta_queue full\n"));
|
|
abort();
|
|
}
|
|
XTHAL_SET_INTSET(BIT(HLI_QUEUE_SW_INT_NUM));
|
|
}
|
|
}
|
|
|
|
static void queue_init(hli_queue_handle_t queue, size_t buf_size, size_t elem_size, QueueHandle_t downstream)
|
|
{
|
|
queue->elem_size = elem_size;
|
|
queue->begin = queue->buf;
|
|
queue->end = queue->buf;
|
|
queue->bufend = queue->buf + buf_size;
|
|
queue->downstream = downstream;
|
|
queue->flags = 0;
|
|
}
|
|
|
|
void hli_queue_setup(void)
|
|
{
|
|
if (s_meta_queue_ptr == NULL) {
|
|
s_meta_queue_ptr = hli_queue_create(HLI_META_QUEUE_SIZE, sizeof(void*), NULL);
|
|
ESP_ERROR_CHECK(esp_intr_alloc(ETS_INTERNAL_SW1_INTR_SOURCE, ESP_INTR_FLAG_IRAM, queue_isr_handler, NULL, &ret_handle));
|
|
xt_ints_on(BIT(HLI_QUEUE_SW_INT_NUM));
|
|
}
|
|
}
|
|
|
|
void hli_queue_shutdown(void)
|
|
{
|
|
if (s_meta_queue_ptr != NULL) {
|
|
hli_queue_delete(s_meta_queue_ptr);
|
|
s_meta_queue_ptr = NULL;
|
|
esp_intr_free(ret_handle);
|
|
xt_ints_off(BIT(HLI_QUEUE_SW_INT_NUM));
|
|
}
|
|
}
|
|
|
|
hli_queue_handle_t hli_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream)
|
|
{
|
|
const size_t buf_elem = nelem + 1;
|
|
if (elem_size > HLI_QUEUE_MAX_ELEM_SIZE) {
|
|
return NULL;
|
|
}
|
|
size_t buf_size = buf_elem * elem_size;
|
|
hli_queue_handle_t res = (hli_queue_handle_t) heap_caps_malloc(sizeof(*res) + buf_size,
|
|
MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
queue_init(res, buf_size, elem_size, downstream);
|
|
return res;
|
|
}
|
|
|
|
hli_queue_handle_t hli_customer_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream)
|
|
{
|
|
hli_queue_handle_t res = hli_queue_create(nelem, elem_size, (QueueHandle_t) downstream);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
res->flags |= HLI_QUEUE_FLAG_CUSTOMER;
|
|
return res;
|
|
}
|
|
|
|
hli_queue_handle_t hli_semaphore_create(size_t max_count, SemaphoreHandle_t downstream)
|
|
{
|
|
const size_t elem_size = 1;
|
|
hli_queue_handle_t res = hli_queue_create(max_count, elem_size, (QueueHandle_t) downstream);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
res->flags |= HLI_QUEUE_FLAG_SEMAPHORE;
|
|
return res;
|
|
}
|
|
|
|
void hli_queue_delete(hli_queue_handle_t queue)
|
|
{
|
|
free(queue);
|
|
}
|
|
|
|
bool IRAM_ATTR hli_queue_get(hli_queue_handle_t queue, void* out)
|
|
{
|
|
uint32_t int_state = hli_intr_disable();
|
|
bool res = false;
|
|
if (!queue_empty(queue)) {
|
|
memcpy(out, queue->begin, queue->elem_size);
|
|
queue->begin = wrap_ptr(queue, queue->begin + queue->elem_size);
|
|
res = true;
|
|
}
|
|
hli_intr_restore(int_state);
|
|
return res;
|
|
}
|
|
|
|
bool IRAM_ATTR hli_queue_put(hli_queue_handle_t queue, const void* data)
|
|
{
|
|
uint32_t int_state = hli_intr_disable();
|
|
bool res = false;
|
|
bool was_empty = queue_empty(queue);
|
|
if (!queue_full(queue)) {
|
|
memcpy(queue->end, data, queue->elem_size);
|
|
queue->end = wrap_ptr(queue, queue->end + queue->elem_size);
|
|
if (was_empty && queue != s_meta_queue_ptr) {
|
|
queue_signal(queue);
|
|
}
|
|
res = true;
|
|
}
|
|
hli_intr_restore(int_state);
|
|
return res;
|
|
}
|
|
|
|
bool IRAM_ATTR hli_semaphore_give(hli_queue_handle_t queue)
|
|
{
|
|
uint8_t data = 0;
|
|
return hli_queue_put(queue, &data);
|
|
}
|