Fork ESP-IDF's bluetooth component

i want better sbc encoding, and no cla will stop me
This commit is contained in:
jacqueline
2024-03-28 14:32:49 +11:00
parent 239e6d8950
commit ee29c25b29
1761 changed files with 737738 additions and 0 deletions
+446
View File
@@ -0,0 +1,446 @@
choice BTDM_CTRL_MODE
prompt "Bluetooth controller mode (BR/EDR/BLE/DUALMODE)"
help
Specify the bluetooth controller mode (BR/EDR, BLE or dual mode).
config BTDM_CTRL_MODE_BLE_ONLY
bool "BLE Only"
config BTDM_CTRL_MODE_BR_EDR_ONLY
bool "BR/EDR Only"
config BTDM_CTRL_MODE_BTDM
bool "Bluetooth Dual Mode"
endchoice
config BTDM_CTRL_BLE_MAX_CONN
int "BLE Max Connections"
depends on BTDM_CTRL_MODE_BLE_ONLY || BTDM_CTRL_MODE_BTDM
default 3
range 1 9
help
BLE maximum connections of bluetooth controller.
Each connection uses 1KB static DRAM whenever the BT controller is enabled.
config BTDM_CTRL_BR_EDR_MAX_ACL_CONN
int "BR/EDR ACL Max Connections"
depends on BTDM_CTRL_MODE_BR_EDR_ONLY || BTDM_CTRL_MODE_BTDM
default 2
range 1 7
help
BR/EDR ACL maximum connections of bluetooth controller.
Each connection uses 1.2 KB DRAM whenever the BT controller is enabled.
config BTDM_CTRL_BR_EDR_MAX_SYNC_CONN
int "BR/EDR Sync(SCO/eSCO) Max Connections"
depends on BTDM_CTRL_MODE_BR_EDR_ONLY || BTDM_CTRL_MODE_BTDM
default 0
range 0 3
help
BR/EDR Synchronize maximum connections of bluetooth controller.
Each connection uses 2 KB DRAM whenever the BT controller is enabled.
choice BTDM_CTRL_BR_EDR_SCO_DATA_PATH
prompt "BR/EDR Sync(SCO/eSCO) default data path"
depends on BTDM_CTRL_MODE_BR_EDR_ONLY || BTDM_CTRL_MODE_BTDM
default BTDM_CTRL_BR_EDR_SCO_DATA_PATH_PCM
help
SCO data path, i.e. HCI or PCM.
SCO data can be sent/received through HCI synchronous packets, or the data
can be routed to on-chip PCM module on ESP32. PCM input/output signals can
be "matrixed" to GPIOs. The default data path can also be set using API
"esp_bredr_sco_datapath_set"
config BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI
bool "HCI"
config BTDM_CTRL_BR_EDR_SCO_DATA_PATH_PCM
bool "PCM"
endchoice
config BTDM_CTRL_BR_EDR_SCO_DATA_PATH_EFF
int
default 0 if BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI
default 1 if BTDM_CTRL_BR_EDR_SCO_DATA_PATH_PCM
default 0
menuconfig BTDM_CTRL_PCM_ROLE_EDGE_CONFIG
bool "PCM Signal Config (Role and Polar)"
depends on BTDM_CTRL_BR_EDR_SCO_DATA_PATH_PCM
default y
choice BTDM_CTRL_PCM_ROLE
prompt "PCM Role"
depends on BTDM_CTRL_PCM_ROLE_EDGE_CONFIG
help
PCM role can be configured as PCM master or PCM slave
config BTDM_CTRL_PCM_ROLE_MASTER
bool "PCM Master"
config BTDM_CTRL_PCM_ROLE_SLAVE
bool "PCM Slave"
endchoice
choice BTDM_CTRL_PCM_POLAR
prompt "PCM Polar"
depends on BTDM_CTRL_PCM_ROLE_EDGE_CONFIG
help
PCM polarity can be configured as Falling Edge or Rising Edge
config BTDM_CTRL_PCM_POLAR_FALLING_EDGE
bool "Falling Edge"
config BTDM_CTRL_PCM_POLAR_RISING_EDGE
bool "Rising Edge"
endchoice
config BTDM_CTRL_PCM_ROLE_EFF
int
default 0 if BTDM_CTRL_PCM_ROLE_MASTER
default 1 if BTDM_CTRL_PCM_ROLE_SLAVE
default 0
config BTDM_CTRL_PCM_POLAR_EFF
int
default 0 if BTDM_CTRL_PCM_POLAR_FALLING_EDGE
default 1 if BTDM_CTRL_PCM_POLAR_RISING_EDGE
default 0
config BTDM_CTRL_AUTO_LATENCY
bool "Auto latency"
depends on BTDM_CTRL_MODE_BTDM
default n
help
BLE auto latency, used to enhance classic BT performance
while classic BT and BLE are enabled at the same time.
config BTDM_CTRL_AUTO_LATENCY_EFF
bool
default BTDM_CTRL_AUTO_LATENCY if BTDM_CTRL_MODE_BTDM
default n
config BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT
bool "Legacy Authentication Vendor Specific Event Enable"
depends on BTDM_CTRL_MODE_BR_EDR_ONLY || BTDM_CTRL_MODE_BTDM
default y
help
To protect from BIAS attack during Legacy authentication,
Legacy authentication Vendor specific event should be enabled
config BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF
bool
default BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT if BTDM_CTRL_MODE_BR_EDR_ONLY || BTDM_CTRL_MODE_BTDM
default 0
config BTDM_CTRL_BLE_MAX_CONN_EFF
int
default BTDM_CTRL_BLE_MAX_CONN if BTDM_CTRL_MODE_BLE_ONLY || BTDM_CTRL_MODE_BTDM
default 0
config BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF
int
default BTDM_CTRL_BR_EDR_MAX_ACL_CONN if BTDM_CTRL_MODE_BR_EDR_ONLY || BTDM_CTRL_MODE_BTDM
default 0
config BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF
int
default BTDM_CTRL_BR_EDR_MAX_SYNC_CONN if BTDM_CTRL_MODE_BR_EDR_ONLY || BTDM_CTRL_MODE_BTDM
default 0
choice BTDM_CTRL_PINNED_TO_CORE_CHOICE
prompt "The cpu core which bluetooth controller run"
depends on !FREERTOS_UNICORE
help
Specify the cpu core to run bluetooth controller.
Can not specify no-affinity.
config BTDM_CTRL_PINNED_TO_CORE_0
bool "Core 0 (PRO CPU)"
config BTDM_CTRL_PINNED_TO_CORE_1
bool "Core 1 (APP CPU)"
depends on !FREERTOS_UNICORE
endchoice
config BTDM_CTRL_PINNED_TO_CORE
int
default 0 if BTDM_CTRL_PINNED_TO_CORE_0
default 1 if BTDM_CTRL_PINNED_TO_CORE_1
default 0
choice BTDM_CTRL_HCI_MODE_CHOICE
prompt "HCI mode"
help
Speicify HCI mode as VHCI or UART(H4)
config BTDM_CTRL_HCI_MODE_VHCI
bool "VHCI"
help
Normal option. Mostly, choose this VHCI when bluetooth host run on ESP32, too.
config BTDM_CTRL_HCI_MODE_UART_H4
bool "UART(H4)"
help
If use external bluetooth host which run on other hardware and use UART as the HCI interface,
choose this option.
endchoice
menu "HCI UART(H4) Options"
visible if BTDM_CTRL_HCI_MODE_UART_H4
config BTDM_CTRL_HCI_UART_NO
int "UART Number for HCI"
depends on BTDM_CTRL_HCI_MODE_UART_H4
range 1 2
default 1
help
Uart number for HCI. The available uart is UART1 and UART2.
config BTDM_CTRL_HCI_UART_BAUDRATE
int "UART Baudrate for HCI"
depends on BTDM_CTRL_HCI_MODE_UART_H4
range 115200 921600
default 921600
help
UART Baudrate for HCI. Please use standard baudrate.
config BTDM_CTRL_HCI_UART_FLOW_CTRL_EN
bool "Enable UART flow control"
depends on BTDM_CTRL_HCI_MODE_UART_H4
default y
endmenu
menu "MODEM SLEEP Options"
config BTDM_CTRL_MODEM_SLEEP
bool "Bluetooth modem sleep"
default y
help
Enable/disable bluetooth controller low power mode.
choice BTDM_CTRL_MODEM_SLEEP_MODE
prompt "Bluetooth Modem sleep mode"
depends on BTDM_CTRL_MODEM_SLEEP
help
To select which strategy to use for modem sleep
config BTDM_CTRL_MODEM_SLEEP_MODE_ORIG
bool "ORIG Mode(sleep with low power clock)"
help
ORIG mode is a bluetooth sleep mode that can be used for dual mode controller. In this mode,
bluetooth controller sleeps between BR/EDR frames and BLE events. A low power clock is used to
maintain bluetooth reference clock.
config BTDM_CTRL_MODEM_SLEEP_MODE_EVED
bool "EVED Mode(For internal test only)"
help
EVED mode is for BLE only and is only for internal test. Do not use it for production. this
mode is not compatible with DFS nor light sleep
endchoice
choice BTDM_CTRL_LOW_POWER_CLOCK
prompt "Bluetooth low power clock"
depends on BTDM_CTRL_MODEM_SLEEP_MODE_ORIG
help
Select the low power clock source for bluetooth controller. Bluetooth low power clock is
the clock source to maintain time in sleep mode.
- "Main crystal" option provides good accuracy and can support Dynamic Frequency Scaling
to be used with Bluetooth modem sleep. Light sleep is not supported.
- "External 32kHz crystal" option allows user to use a 32.768kHz crystal as Bluetooth low
power clock. This option is allowed as long as External 32kHz crystal is configured as
the system RTC clock source. This option provides good accuracy and supports Bluetooth
modem sleep to be used alongside Dynamic Frequency Scaling or light sleep.
config BTDM_CTRL_LPCLK_SEL_MAIN_XTAL
bool "Main crystal"
help
Main crystal can be used as low power clock for bluetooth modem sleep. If this option is
selected, bluetooth modem sleep can work under Dynamic Frequency Scaling(DFS) enabled, but
cannot work when light sleep is enabled. Main crystal has a good performance in accuracy as
the bluetooth low power clock source.
config BTDM_CTRL_LPCLK_SEL_EXT_32K_XTAL
bool "External 32kHz crystal"
depends on RTC_CLK_SRC_EXT_CRYS
help
External 32kHz crystal has a nominal frequency of 32.768kHz and provides good frequency
stability. If used as Bluetooth low power clock, External 32kHz can support Bluetooth
modem sleep to be used with both DFS and light sleep.
endchoice
endmenu
choice BTDM_BLE_SLEEP_CLOCK_ACCURACY
prompt "BLE Sleep Clock Accuracy"
depends on BTDM_CTRL_MODE_BLE_ONLY || BTDM_CTRL_MODE_BTDM
default BTDM_BLE_DEFAULT_SCA_250PPM
help
BLE Sleep Clock Accuracy(SCA) for the local device is used to estimate window widening in BLE
connection events. With a lower level of clock accuracy(e.g. 500ppm over 250ppm), the slave
needs a larger RX window to synchronize with master in each anchor point, thus resulting in an
increase of power consumption but a higher level of robustness in keeping connected. According
to the requirements of Bluetooth Core specification 4.2, the worst-case accuracy of Classic
Bluetooth low power oscialltor(LPO) is +/-250ppm in STANDBY and in low power modes such as
sniff. For BLE the worst-case SCA is +/-500ppm.
- "151ppm to 250ppm" option is the default value for Bluetooth Dual mode
- "251ppm to 500ppm" option can be used in BLE only mode when using external 32kHz crystal as
low power clock. This option is provided in case that BLE sleep clock has a lower level of
accuracy, or other error sources contribute to the inaccurate timing during sleep.
config BTDM_BLE_DEFAULT_SCA_500PPM
bool "251ppm to 500ppm"
depends on BTDM_CTRL_LPCLK_SEL_EXT_32K_XTAL && BTDM_CTRL_MODE_BLE_ONLY
config BTDM_BLE_DEFAULT_SCA_250PPM
bool "151ppm to 250ppm"
endchoice
config BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF
int
default 0 if BTDM_BLE_DEFAULT_SCA_500PPM
default 1 if BTDM_BLE_DEFAULT_SCA_250PPM
default 1
config BTDM_BLE_SCAN_DUPL
bool "BLE Scan Duplicate Options"
depends on (BTDM_CTRL_MODE_BTDM || BTDM_CTRL_MODE_BLE_ONLY)
default y
help
This select enables parameters setting of BLE scan duplicate.
choice BTDM_SCAN_DUPL_TYPE
prompt "Scan Duplicate Type"
default BTDM_SCAN_DUPL_TYPE_DEVICE
depends on BTDM_BLE_SCAN_DUPL
help
Scan duplicate have three ways. one is "Scan Duplicate By Device Address", This way is to use
advertiser address filtering. The adv packet of the same address is only allowed to be reported once.
Another way is "Scan Duplicate By Device Address And Advertising Data". This way is to use advertising
data and device address filtering. All different adv packets with the same address are allowed to be
reported. The last way is "Scan Duplicate By Advertising Data". This way is to use advertising data
filtering. All same advertising data only allow to be reported once even though they are from
different devices.
config BTDM_SCAN_DUPL_TYPE_DEVICE
bool "Scan Duplicate By Device Address"
help
This way is to use advertiser address filtering. The adv packet of the same address is only
allowed to be reported once
config BTDM_SCAN_DUPL_TYPE_DATA
bool "Scan Duplicate By Advertising Data"
help
This way is to use advertising data filtering. All same advertising data only allow to be reported
once even though they are from different devices.
config BTDM_SCAN_DUPL_TYPE_DATA_DEVICE
bool "Scan Duplicate By Device Address And Advertising Data"
help
This way is to use advertising data and device address filtering. All different adv packets with
the same address are allowed to be reported.
endchoice
config BTDM_SCAN_DUPL_TYPE
int
depends on BTDM_BLE_SCAN_DUPL
default 0 if BTDM_SCAN_DUPL_TYPE_DEVICE
default 1 if BTDM_SCAN_DUPL_TYPE_DATA
default 2 if BTDM_SCAN_DUPL_TYPE_DATA_DEVICE
default 0
config BTDM_SCAN_DUPL_CACHE_SIZE
int "Maximum number of devices in scan duplicate filter"
depends on BTDM_BLE_SCAN_DUPL
range 10 1000
default 100
help
Maximum number of devices which can be recorded in scan duplicate filter.
When the maximum amount of device in the filter is reached, the oldest device will be refreshed.
config BTDM_SCAN_DUPL_CACHE_REFRESH_PERIOD
int "Duplicate scan list refresh period (seconds)"
depends on BTDM_BLE_SCAN_DUPL
range 0 1000
default 0
help
If the period value is non-zero, the controller will periodically clear the device information
stored in the scan duuplicate filter. If it is 0, the scan duuplicate filter will not be cleared
until the scanning is disabled. Duplicate advertisements for this period should not be sent to the
Host in advertising report events.
There are two scenarios where the ADV packet will be repeatedly reported:
1. The duplicate scan cache is full, the controller will delete the oldest device information and
add new device information.
2. When the refresh period is up, the controller will clear all device information and start filtering
again.
config BTDM_BLE_MESH_SCAN_DUPL_EN
bool "Special duplicate scan mechanism for BLE Mesh scan"
depends on BTDM_BLE_SCAN_DUPL
default n
help
This enables the BLE scan duplicate for special BLE Mesh scan.
config BTDM_MESH_DUPL_SCAN_CACHE_SIZE
int "Maximum number of Mesh adv packets in scan duplicate filter"
depends on BTDM_BLE_MESH_SCAN_DUPL_EN
range 10 1000
default 100
help
Maximum number of adv packets which can be recorded in duplicate scan cache for BLE Mesh.
When the maximum amount of device in the filter is reached, the cache will be refreshed.
config BTDM_CTRL_FULL_SCAN_SUPPORTED
bool "BLE full scan feature supported"
depends on BTDM_CTRL_MODE_BLE_ONLY || BTDM_CTRL_MODE_BTDM
default y
help
The full scan function is mainly used to provide BLE scan performance.
This is required for scenes with high scan performance requirements, such as BLE Mesh scenes.
config BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP
bool "BLE adv report flow control supported"
depends on (BTDM_CTRL_MODE_BTDM || BTDM_CTRL_MODE_BLE_ONLY)
default y
help
The function is mainly used to enable flow control for advertising reports. When it is enabled,
advertising reports will be discarded by the controller if the number of unprocessed advertising
reports exceeds the size of BLE adv report flow control.
config BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM
int "BLE adv report flow control number"
depends on BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP
range 50 1000
default 100
help
The number of unprocessed advertising report that Bluedroid can save.If you set
`BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM` to a small value, this may cause adv packets lost.
If you set `BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM` to a large value, Bluedroid may cache a
lot of adv packets and this may cause system memory run out. For example, if you set
it to 50, the maximum memory consumed by host is 35 * 50 bytes. Please set
`BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM` according to your system free memory and handle adv
packets as fast as possible, otherwise it will cause adv packets lost.
config BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD
int "BLE adv lost event threshold value"
depends on BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP
range 1 1000
default 20
help
When adv report flow control is enabled, The ADV lost event will be generated when the number
of ADV packets lost in the controller reaches this threshold. It is better to set a larger value.
If you set `BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD` to a small value or printf every adv lost event, it
may cause adv packets lost more.
config BTDM_RESERVE_DRAM
hex
default 0xdb5c if BT_ENABLED
default 0
config BTDM_CTRL_HLI
bool "High level interrupt"
depends on BT_ENABLED
default y
help
Using Level 4 interrupt for Bluetooth.
File diff suppressed because it is too large Load Diff
+297
View File
@@ -0,0 +1,297 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#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"
#if CONFIG_BTDM_CTRL_HLI
#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];
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) {
/* no handler found, it is OK in this case. */
}
}
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;
static 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) {
/* Failed to send to downstream queue, it is OK in this case. */
}
}
}
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(struct hli_queue_t) + 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);
}
#endif /* CONFIG_BTDM_CTRL_HLI */
+167
View File
@@ -0,0 +1,167 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "esp_intr_alloc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#ifdef __cplusplus
extern "C" {
#endif
#if CONFIG_BTDM_CTRL_HLI
/*** Queues ***/
struct hli_queue_t
{
size_t elem_size;
char* begin;
char* end;
const char* bufend;
QueueHandle_t downstream;
int flags;
char buf[0];
};
/**
* @brief Register a high level interrupt function
*
* @param handler interrupt handler function
* @param arg argument to pass to the interrupt handler
* @param intr_reg address of the peripheral register containing the interrupt status,
* or value 0 to get the status from CPU INTERRUPT register
* @param intr_mask mask of the interrupt, in the interrupt status register
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM if too many handlers are registered
*/
esp_err_t hli_intr_register(intr_handler_t handler, void* arg, uint32_t intr_reg, uint32_t intr_mask);
/**
* @brief Mask all interrupts (including high level ones) on the current CPU
*
* @return uint32_t interrupt status, pass it to hli_intr_restore
*/
uint32_t hli_intr_disable(void);
/**
* @brief Re-enable interrupts
*
* @param state value returned by hli_intr_disable
*/
void hli_intr_restore(uint32_t state);
/**
* @brief Type of a hli queue
*/
typedef struct hli_queue_t* hli_queue_handle_t;
/**
* @brief Initialize hli_queue module. Must be called once before using hli queue APIs.
*/
void hli_queue_setup(void);
/**
* @brief Shutdown hli_queue module.
*/
void hli_queue_shutdown(void);
/**
* @brief Create a hli queue, wrapping a FreeRTOS queue
*
* This queue can be used from high level interrupts,
* but **ONLY ON THE CPU WHERE hli_queue_setup WAS CALLED**. Values sent to this
* queue are automatically forwarded to "downstream" FreeRTOS queue using a level 3
* software interrupt.
*
* @param nelem number of elements in the queue
* @param elem_size size of one element; must match element size of a downstream queue
* @param downstream FreeRTOS queue to send the values to
* @return hli_queue_handle_t handle of the created queue, or NULL on failure
*/
hli_queue_handle_t hli_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream);
/**
* @brief Create a customer hli queue, wrapping a FreeRTOS queue
*
* This queue can be used from high level interrupts,
* but **ONLY ON THE CPU WHERE hli_queue_setup WAS CALLED**. Values sent to this
* queue are automatically forwarded to "downstream" FreeRTOS queue using a level 3
* software interrupt.
*
* @param nelem number of elements in the queue
* @param elem_size size of one element; must match element size of a downstream queue
* @param downstream FreeRTOS queue to send the values to
* @return hli_queue_handle_t handle of the created queue, or NULL on failure
*/
hli_queue_handle_t hli_customer_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream);
/**
* @brief Create a hli queue, wrapping a FreeRTOS semaphore
*
* See notes on hli_queue_create.
*
* @param max_count maximum semaphore count
* @param downstream FreeRTOS semaphore to forward the calls to
* @return hli_queue_handle_t handle of the created queue, or NULL on failure
*/
hli_queue_handle_t hli_semaphore_create(size_t max_count, SemaphoreHandle_t downstream);
/**
* @brief Delete a hli queue
*
* Make sure noone is using the queue before deleting it.
*
* @param queue handle returned by hli_queue_create or hli_semaphore_create
*/
void hli_queue_delete(hli_queue_handle_t queue);
/**
* @brief Get one element from a hli queue
*
* Usually not used, values get sent to a downstream FreeRTOS queue automatically.
* However if downstream queue is NULL, this API can be used to get values from a hli queue.
*
* @param queue handle of a queue
* @param out pointer where to store the element
* @return true if the element was successfully read from the queue
*/
bool hli_queue_get(hli_queue_handle_t queue, void* out);
/**
* @brief Put one element into a hli queue
*
* This puts copies an element into the queue and raises a software interrupt (level 3).
* In the interrupt, the value is copied to a FreeRTOS "downstream" queue.
*
* Note that if the value does not fit into a downstream queue, no error is returned,
* and the value is lost.
*
* @param queue handle of a queue
* @param data pointer to the element to be sent
* @return true if data was placed into the hli queue successfully
*/
bool hli_queue_put(hli_queue_handle_t queue, const void* data);
/**
* @brief "Give" a semaphore wrapped by a hli queue
*
* @param queue handle returned by hli_semaphore_create
* @return true if the event was sent to a hli queue successfully
*/
bool hli_semaphore_give(hli_queue_handle_t queue);
#endif /* CONFIG_BTDM_CTRL_HLI */
#ifdef __cplusplus
}
#endif
+267
View File
@@ -0,0 +1,267 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <xtensa/coreasm.h>
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>
#include "xtensa_context.h"
#include "sdkconfig.h"
#include "soc/soc.h"
#if CONFIG_BTDM_CTRL_HLI
/* Interrupt stack size, for C code.
* TODO: reduce and make configurable.
*/
#define L4_INTR_STACK_SIZE 4096
/* Save area for the CPU state:
* - 64 words for the general purpose registers
* - 7 words for some of the special registers:
* - WINDOWBASE, WINDOWSTART only WINDOWSTART is truly needed
* - SAR, LBEG, LEND, LCOUNT since the C code might use these
* - EPC1 since the C code might cause window overflow exceptions
* This is not laid out as standard exception frame structure
* for simplicity of the save/restore code.
*/
#define REG_FILE_SIZE (64 * 4)
#define SPECREG_OFFSET REG_FILE_SIZE
#define SPECREG_SIZE (7 * 4)
#define REG_SAVE_AREA_SIZE (SPECREG_OFFSET + SPECREG_SIZE)
.data
_l4_intr_stack:
.space L4_INTR_STACK_SIZE
_l4_save_ctx:
.space REG_SAVE_AREA_SIZE
.section .iram1,"ax"
.global xt_highint4
.type xt_highint4,@function
.align 4
xt_highint4:
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
/*
Here, Timer2 is used to count a little time(50us).
The subsequent dram0 write operation is blocked due to live lock, which will
cause timer2 to timeout and trigger a level 5 interrupt.
*/
rsr.ccount a0
addmi a0, a0, (CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ*50)
wsr a0, CCOMPARE2
/* Enable Timer 2 interrupt */
rsr a0, INTENABLE
extui a0, a0, 16, 1
bnez a0, 1f
movi a0, 0
xsr a0, INTENABLE /* disable all interrupts */
/* And a0 with (1 << 16) for Timer 2 interrupt mask */
addmi a0, a0, (1<<14)
addmi a0, a0, (1<<14)
addmi a0, a0, (1<<14)
addmi a0, a0, (1<<14)
wsr a0, INTENABLE /* Enable Timer 2 */
1:
#endif
movi a0, _l4_save_ctx
/* save 4 lower registers */
s32i a1, a0, 4
s32i a2, a0, 8
s32i a3, a0, 12
rsr a2, EXCSAVE_4 /* holds the value of a0 */
s32i a2, a0, 0
/* Save special registers */
addi a0, a0, SPECREG_OFFSET
rsr a2, WINDOWBASE
s32i a2, a0, 0
rsr a2, WINDOWSTART
s32i a2, a0, 4
rsr a2, SAR
s32i a2, a0, 8
rsr a2, LBEG
s32i a2, a0, 12
rsr a2, LEND
s32i a2, a0, 16
rsr a2, LCOUNT
s32i a2, a0, 20
rsr a2, EPC1
s32i a2, a0, 24
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
movi a0, 0
xsr a0, INTENABLE /* disable all interrupts */
movi a2, ~(1<<16)
and a0, a2, a0
wsr a0, INTENABLE
#endif
/* disable exception mode, window overflow */
movi a0, PS_INTLEVEL(5) | PS_EXCM
wsr a0, PS
rsync
/* Save the remaining physical registers.
* 4 registers are already saved, which leaves 60 registers to save.
* (FIXME: consider the case when the CPU is configured with physical 32 registers)
* These 60 registers are saved in 5 iterations, 12 registers at a time.
*/
movi a1, 5
movi a3, _l4_save_ctx + 4 * 4
/* This is repeated 5 times, each time the window is shifted by 12 registers.
* We come here with a1 = downcounter, a3 = save pointer, a2 and a0 unused.
*/
1:
s32i a4, a3, 0
s32i a5, a3, 4
s32i a6, a3, 8
s32i a7, a3, 12
s32i a8, a3, 16
s32i a9, a3, 20
s32i a10, a3, 24
s32i a11, a3, 28
s32i a12, a3, 32
s32i a13, a3, 36
s32i a14, a3, 40
s32i a15, a3, 44
/* We are about to rotate the window, so that a12-a15 will become the new a0-a3.
* Copy a0-a3 to a12-15 to still have access to these values.
* At the same time we can decrement the counter and adjust the save area pointer
*/
/* a0 is constant (_l4_save_ctx), no need to copy */
addi a13, a1, -1 /* copy and decrement the downcounter */
/* a2 is scratch so no need to copy */
addi a15, a3, 48 /* copy and adjust the save area pointer */
beqz a13, 2f /* have saved all registers ? */
rotw 3 /* rotate the window and go back */
j 1b
/* the loop is complete */
2:
rotw 4 /* this brings us back to the original window */
/* a0 still points to _l4_save_ctx */
/* Can clear WINDOWSTART now, all registers are saved */
rsr a2, WINDOWBASE
/* WINDOWSTART = (1 << WINDOWBASE) */
movi a3, 1
ssl a2
sll a3, a3
wsr a3, WINDOWSTART
_highint4_stack_switch:
movi a0, 0
movi sp, _l4_intr_stack + L4_INTR_STACK_SIZE - 16
s32e a0, sp, -12 /* For GDB: set null SP */
s32e a0, sp, -16 /* For GDB: set null PC */
movi a0, _highint4_stack_switch /* For GDB: cosmetics, for the frame where stack switch happened */
/* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
movi a6, PS_INTLEVEL(4) | PS_UM | PS_WOE
wsr a6, PS
rsync
/* Call C handler */
mov a6, sp
call4 hli_c_handler
l32e sp, sp, -12 /* switch back to the original stack */
/* Done with C handler; re-enable exception mode, disabling window overflow */
movi a2, PS_INTLEVEL(5) | PS_EXCM /* TOCHECK */
wsr a2, PS
rsync
/* Restore the special registers.
* WINDOWSTART will be restored near the end.
*/
movi a0, _l4_save_ctx + SPECREG_OFFSET
l32i a2, a0, 8
wsr a2, SAR
l32i a2, a0, 12
wsr a2, LBEG
l32i a2, a0, 16
wsr a2, LEND
l32i a2, a0, 20
wsr a2, LCOUNT
l32i a2, a0, 24
wsr a2, EPC1
/* Restoring the physical registers.
* This is the reverse to the saving process above.
*/
/* Rotate back to the final window, then start loading 12 registers at a time,
* in 5 iterations.
* Again, a1 is the downcounter and a3 is the save area pointer.
* After each rotation, a1 and a3 are copied from a13 and a15.
* To simplify the loop, we put the initial values into a13 and a15.
*/
rotw -4
movi a15, _l4_save_ctx + 64 * 4 /* point to the end of the save area */
movi a13, 5
1:
/* Copy a1 and a3 from their previous location,
* at the same time decrementing and adjusting the save area pointer.
*/
addi a1, a13, -1
addi a3, a15, -48
/* Load 12 registers */
l32i a4, a3, 0
l32i a5, a3, 4
l32i a6, a3, 8
l32i a7, a3, 12
l32i a8, a3, 16
l32i a9, a3, 20
l32i a10, a3, 24
l32i a11, a3, 28 /* ensure PS and EPC written */
l32i a12, a3, 32
l32i a13, a3, 36
l32i a14, a3, 40
l32i a15, a3, 44
/* Done with the loop? */
beqz a1, 2f
/* If no, rotate the window and repeat */
rotw -3
j 1b
2:
/* Done with the loop. Only 4 registers (a0-a3 in the original window) remain
* to be restored. Also need to restore WINDOWSTART, since all the general
* registers are now in place.
*/
movi a0, _l4_save_ctx
l32i a2, a0, SPECREG_OFFSET + 4
wsr a2, WINDOWSTART
l32i a1, a0, 4
l32i a2, a0, 8
l32i a3, a0, 12
rsr a0, EXCSAVE_4 /* holds the value of a0 before the interrupt handler */
/* Return from the interrupt, restoring PS from EPS_4 */
rfi 4
#endif /* CONFIG_BTDM_CTRL_HLI */
/* The linker has no reason to link in this file; all symbols it exports are already defined
(weakly!) in the default int handler. Define a symbol here so we can use it to have the
linker inspect this anyway. */
.global ld_include_hli_vectors_bt
ld_include_hli_vectors_bt: