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,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
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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:
|
||||
Reference in New Issue
Block a user