From e528d3488dfffc7e55c3eba0739fbebcdf5e2370 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Sat, 19 Apr 2025 09:36:24 +0200 Subject: [PATCH] cpu/esp32: add BLE support for the ESP32-H2 --- cpu/esp32/Makefile.dep | 9 +++ cpu/esp32/Makefile.features | 10 +-- cpu/esp32/Makefile.include | 16 ++++- cpu/esp32/esp-ble-nimble/esp_ble_nimble.c | 76 +++++++++++++++++++++-- cpu/esp32/include/irq_arch.h | 1 + cpu/esp32/irq_arch.c | 16 ++++- 6 files changed, 113 insertions(+), 15 deletions(-) diff --git a/cpu/esp32/Makefile.dep b/cpu/esp32/Makefile.dep index fc62880bde..43a41b7c9a 100644 --- a/cpu/esp32/Makefile.dep +++ b/cpu/esp32/Makefile.dep @@ -27,6 +27,9 @@ ifneq (,$(filter esp_ble,$(USEMODULE))) ifeq (esp32,$(CPU_FAM)) FEATURES_REQUIRED += esp_ble_esp32 USEPKG += esp32_sdk_lib_bt_esp32 + else ifeq (esp32h2,$(CPU_FAM)) + FEATURES_REQUIRED += esp_ble_esp32h2 + USEPKG += esp32_sdk_lib_bt_esp32h2 else ifneq (,$(filter esp32c3 esp32s3,$(CPU_FAM))) FEATURES_REQUIRED += esp_ble_esp32c3 USEPKG += esp32_sdk_lib_bt_esp32c3 @@ -161,9 +164,15 @@ endif ifneq (,$(filter nimble,$(USEPKG))) USEMODULE += esp_ble USEMODULE += esp_ble_nimble + USEMODULE += esp_idf_nvs_flash USEMODULE += nimble_host USEMODULE += nimble_transport_hci_h4 USEMODULE += ztimer_msec + ifeq (esp32h2,$(CPU_FAM)) + # TODO for the moment, we use tinycrypt for encryption + # USEPKG += esp32_sdk_mbedtls + USEPKG += esp32_sdk_lib_coexist + endif endif ifneq (,$(filter periph_adc,$(USEMODULE))) diff --git a/cpu/esp32/Makefile.features b/cpu/esp32/Makefile.features index f21e21a6e7..7c409aa3db 100644 --- a/cpu/esp32/Makefile.features +++ b/cpu/esp32/Makefile.features @@ -45,17 +45,19 @@ ifneq (esp32h2,$(CPU_FAM)) FEATURES_PROVIDED += esp_wifi_enterprise endif -ifeq (esp32,$(CPU_FAM)) +ifneq (,$(filter esp32 esp32c3 esp32h2 esp32s3,$(CPU_FAM))) FEATURES_PROVIDED += ble_nimble FEATURES_PROVIDED += ble_nimble_netif FEATURES_PROVIDED += esp_ble +endif + +ifeq (esp32,$(CPU_FAM)) FEATURES_PROVIDED += esp_ble_esp32 +else ifeq (esp32h2,$(CPU_FAM)) + FEATURES_PROVIDED += esp_ble_esp32h2 else ifneq (,$(filter esp32c3 esp32s3,$(CPU_FAM))) FEATURES_PROVIDED += ble_adv_ext - FEATURES_PROVIDED += ble_nimble - FEATURES_PROVIDED += ble_nimble_netif FEATURES_PROVIDED += ble_phy_2mbit - FEATURES_PROVIDED += esp_ble FEATURES_PROVIDED += esp_ble_esp32c3 endif diff --git a/cpu/esp32/Makefile.include b/cpu/esp32/Makefile.include index 317bb61f65..a52802c057 100644 --- a/cpu/esp32/Makefile.include +++ b/cpu/esp32/Makefile.include @@ -184,6 +184,9 @@ ifneq (,$(filter esp_ble,$(USEMODULE))) INCLUDES += -I$(ESP32_SDK_DIR)/components/bt/include/esp32c3/include endif ifeq (esp32h2,$(CPU_FAM)) + CFLAGS += -Ddefault_RNG_defined=0 + CFLAGS += -DNIMBLE_OS_MSYS_INIT_IN_CONTROLLER=1 + CFLAGS += -DNIMBLE_G_MSYS_POOL_LIST_IN_CONTROLLER=1 INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_hw_support/port/$(CPU_FAM)/private_include endif endif @@ -211,6 +214,7 @@ endif ifneq (,$(filter esp_idf_nvs_flash,$(USEMODULE))) INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_partition/include + INCLUDES += -I$(ESP32_SDK_DIR)/components/nvs_flash/include CFLAGS += -DMBEDTLS_CIPHER_MODE_XTS endif @@ -401,12 +405,18 @@ endif # Libraries needed when using esp_ble ifneq (,$(filter esp_ble,$(USEMODULE))) LINKFLAGS += -L$(ESP32_SDK_LIB_PHY_DIR)/$(CPU_FAM) - LINKFLAGS += -L$(ESP32_SDK_LIB_BT_DIR)/$(CPU_FAM) - ARCHIVES += -lbtdm_app ARCHIVES += -lphy -lstdc++ + ifneq (,$(filter esp32 esp32c3 esp32s3,$(CPU_FAM))) + LINKFLAGS += -L$(ESP32_SDK_LIB_BT_DIR)/$(CPU_FAM) + ARCHIVES += -lbtdm_app + else ifeq (esp32h2,$(CPU_FAM)) + LINKFLAGS += -L$(ESP32_SDK_LIB_COEXIST_DIR)/$(CPU_FAM) + LINKFLAGS += -L$(ESP32_SDK_LIB_BT_DIR) + ARCHIVES += -lble_app -lcoexist + endif ifeq (esp32,$(CPU_FAM)) ARCHIVES += -lrtc - else ifneq (,$(filter esp32c3 esp32s3,$(CPU_FAM))) + else ifneq (,$(filter esp32c3 esp32h2 esp32s3,$(CPU_FAM))) ARCHIVES += -lbtbb endif endif diff --git a/cpu/esp32/esp-ble-nimble/esp_ble_nimble.c b/cpu/esp32/esp-ble-nimble/esp_ble_nimble.c index 2373477cb0..510b816b54 100644 --- a/cpu/esp32/esp-ble-nimble/esp_ble_nimble.c +++ b/cpu/esp32/esp-ble-nimble/esp_ble_nimble.c @@ -22,14 +22,16 @@ #include "log.h" #include "esp_bt.h" #include "mutex.h" +#include "nimble_riot.h" #include "od.h" #include "host/ble_hs.h" #include "nimble/hci_common.h" -#include "nimble/nimble_port.h" #include "nimble/transport/hci_h4.h" #include "sysinit/sysinit.h" +#include "nvs_flash.h" + #define ENABLE_DEBUG 0 #include "debug.h" @@ -45,6 +47,23 @@ #define BLE_VHCI_TIMEOUT_MS 2000 +#if CPU_FAM_ESP32 || CPU_FAM_ESP32S3 || CPU_FAM_ESP32C3 +/* On ESP32, ESP32-S3 and ESP32-C3, the BT Controller calls the + * notify_host_send_available callback function when it becomes ready to + * receive commands from the host. It can be used to unlock a mutex that + * is used to synchronize the access to the BT Controller by different + * threads. */ +# define BT_CTRL_SUPPORTS_READY_CB 1 +#elif CPU_FAM_ESP32C2 || CPU_FAM_ESP32C6 || CPU_FAM_ESP32H2 +/* On other ESP32x variants like ESP32-C2, ESP32-C6 and ESP32-H2, + * this callback function is not used and we have to use another mechanism. */ +# define BT_CTRL_SUPPORTS_READY_CB 0 +# define BT_CTRL_WAIT_READY_CYCLES 10 +# define BT_CTRL_WAIT_READY_INTERVALL_MS 10 +#else +# error "Platform implementation is missing" +#endif + /* Definition of UART H4 packet types */ enum { BLE_HCI_UART_H4_NONE = 0x00, @@ -56,19 +75,30 @@ enum { static const char *LOG_TAG = "esp_nimble"; +#if BT_CTRL_SUPPORTS_READY_CB static mutex_t _esp_vhci_semaphore = MUTEX_INIT; +#endif static struct hci_h4_sm _esp_h4sm; static void _ble_vhci_controller_ready_cb(void) { + DEBUG("%s\n", __func__); + +#if BT_CTRL_SUPPORTS_READY_CB mutex_unlock(&_esp_vhci_semaphore); +#endif } static int _ble_vhci_packet_received_cb(uint8_t *data, uint16_t len) { + DEBUG("%s: data=%p len=%u\n", __func__, data, len); + /* process the HCI H4 formatted packet and call ble_transport_to_hs_* */ - len = hci_h4_sm_rx(&_esp_h4sm, data, len); + if (nimble_port_initialized) { + len = hci_h4_sm_rx(&_esp_h4sm, data, len); + } + return 0; } @@ -81,6 +111,9 @@ static inline int _ble_transport_to_ll(uint8_t *packet, uint16_t len) { uint8_t rc = 0; + DEBUG("%s: controller status=%d\n", __func__, esp_bt_controller_get_status()); + +#if BT_CTRL_SUPPORTS_READY_CB /* check whether the controller is ready to accept packets */ if (!esp_vhci_host_check_send_available()) { LOG_TAG_DEBUG(LOG_TAG, "Controller not ready to accept packets"); @@ -94,6 +127,23 @@ static inline int _ble_transport_to_ll(uint8_t *packet, uint16_t len) else { rc = BLE_HS_ETIMEOUT_HCI; } +#else + unsigned i = 0; + for (; i < BT_CTRL_WAIT_READY_CYCLES; i++) { + if (esp_vhci_host_check_send_available()) { + break; + } + ztimer_sleep(ZTIMER_MSEC, BT_CTRL_WAIT_READY_INTERVALL_MS); + } + if (i < BT_CTRL_WAIT_READY_CYCLES) { + esp_vhci_host_send_packet(packet, len); + } + else { + LOG_TAG_DEBUG(LOG_TAG, "Controller not ready to accept packets"); + rc = BLE_HS_ETIMEOUT_HCI; + } +#endif + return rc; } @@ -113,8 +163,8 @@ int ble_transport_to_ll_cmd_impl(void *buf) packet[0] = BLE_HCI_UART_H4_CMD; /* first byte is the packet indicator */ memcpy(packet + 1, cmd, len - 1); + DEBUG("%s: CMD host to ctrl\n", __func__); if (ENABLE_DEBUG && IS_USED(MODULE_OD)) { - printf("CMD host to ctrl:\n"); od_hex_dump(packet + 1, len - 1, OD_WIDTH_DEFAULT); } @@ -147,8 +197,8 @@ int ble_transport_to_ll_acl_impl(struct os_mbuf *om) packet[0] = BLE_HCI_UART_H4_ACL; len++; + DEBUG("%s: ACL host to ctrl\n", __func__); if (ENABLE_DEBUG && IS_USED(MODULE_OD)) { - printf("ACL host to ctrl:\n"); od_hex_dump(packet + 1, (om->om_len < 32) ? om->om_len : 32, OD_WIDTH_DEFAULT); } @@ -169,17 +219,19 @@ static int _esp_hci_h4_frame_cb(uint8_t pkt_type, void *data) { int rc = 0; + DEBUG("%s: pkt_type=%d data=%p\n", __func__, pkt_type, data); + switch (pkt_type) { case HCI_H4_ACL: + DEBUG("%s: ACL ctrl to host\n", __func__); if (ENABLE_DEBUG && IS_USED(MODULE_OD)) { - printf("ACL ctrl to host:\n"); od_hex_dump((uint8_t *)data, 4, OD_WIDTH_DEFAULT); } rc = ble_transport_to_hs_acl(data); break; case HCI_H4_EVT: + DEBUG("%s: EVT ctrl to host\n", __func__); if (ENABLE_DEBUG && IS_USED(MODULE_OD)) { - printf("EVT ctrl to host:\n"); od_hex_dump((uint8_t *)data, ((uint8_t *)data)[1] + 2, OD_WIDTH_DEFAULT); } rc = ble_transport_to_hs_evt(data); @@ -197,6 +249,12 @@ void esp_ble_nimble_init(void) esp_err_t ret; esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + if (IS_ACTIVE(CONFIG_ESP_WIFI_NVS_ENABLED) && !IS_USED(MODULE_ESP_WIFI_ANY)) { + if (nvs_flash_init() != ESP_OK) { + LOG_ERROR("nfs_flash_init failed\n"); + } + } + /* TODO: BLE mode only used, the memory for BT Classic could be released if ((ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);) != ESP_OK) { LOG_TAG_ERROR(LOG_TAG, @@ -206,17 +264,23 @@ void esp_ble_nimble_init(void) } */ + DEBUG("%s: ctrl status=%d\n", __func__, esp_bt_controller_get_status()); + /* init and enable the Bluetooth LE controller */ if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { LOG_TAG_ERROR(LOG_TAG, "Bluetooth controller initialize failed: %d", ret); assert(0); } + DEBUG("%s: ctrl status=%d\n", __func__, esp_bt_controller_get_status()); + if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) { LOG_TAG_ERROR(LOG_TAG, "Bluetooth controller enable failed: %d", ret); assert(0); } + DEBUG("%s: ctrl status=%d\n", __func__, esp_bt_controller_get_status()); + /* register callbacks from Bluetooth LE controller */ if ((ret = esp_vhci_host_register_callback(&vhci_host_cb)) != ESP_OK) { assert(0); diff --git a/cpu/esp32/include/irq_arch.h b/cpu/esp32/include/irq_arch.h index c0caf2e1ab..481adceaa4 100644 --- a/cpu/esp32/include/irq_arch.h +++ b/cpu/esp32/include/irq_arch.h @@ -49,6 +49,7 @@ extern "C" { #endif #define CPU_INUM_GPIO 2 /**< Level interrupt with low priority 1 */ #define CPU_INUM_BLE 5 /**< Level interrupt with low priority 1 */ +#define CPU_INUM_BT_MAC 8 /**< Level interrupt with low priority 1 */ #define CPU_INUM_RTT 9 /**< Level interrupt with low priority 1 */ #define CPU_INUM_SERIAL_JTAG 10 /**< Edge interrupt with low priority 1 */ #define CPU_INUM_I2C 12 /**< Level interrupt with low priority 1 */ diff --git a/cpu/esp32/irq_arch.c b/cpu/esp32/irq_arch.c index 56d214ff09..891436cdfc 100644 --- a/cpu/esp32/irq_arch.c +++ b/cpu/esp32/irq_arch.c @@ -45,7 +45,7 @@ typedef struct intr_handle_data_t { uint8_t level; } intr_handle_data_t; -/* TODO change to a clearer approach */ +/* TODO change to a clearer and more dynamic approach */ static const struct intr_handle_data_t _irq_data_table[] = { #ifndef __XTENSA__ { ETS_FROM_CPU_INTR0_SOURCE, CPU_INUM_SOFTWARE, 1 }, @@ -81,8 +81,10 @@ static const struct intr_handle_data_t _irq_data_table[] = { #endif #if defined(SOC_BLE_SUPPORTED) # if defined(CPU_FAM_ESP32) || defined(CPU_FAM_ESP32S3) || defined(CPU_FAM_ESP32C3) - { ETS_RWBLE_INTR_SOURCE, CPU_INUM_BLE, 2 }, + { ETS_RWBLE_INTR_SOURCE, CPU_INUM_BLE, 1 }, # elif defined(CPU_FAM_ESP32H2) + { ETS_LP_BLE_TIMER_INTR_SOURCE, CPU_INUM_BLE, 1 }, + { ETS_BT_MAC_INTR_SOURCE, CPU_INUM_BT_MAC, 1 }, # else # error "Platform implementation is missing" # endif @@ -193,12 +195,22 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, #ifdef SOC_CPU_HAS_FLEXIBLE_INTC /* set interrupt level given by flags */ esp_cpu_intr_set_priority(_irq_data_table[i].intr, esp_intr_flags_to_level(flags)); + esp_cpu_intr_set_type(_irq_data_table[i].intr, + flags & ESP_INTR_FLAG_EDGE ? ESP_CPU_INTR_TYPE_EDGE + : ESP_CPU_INTR_TYPE_LEVEL); +#endif + +#if SOC_INT_PLIC_SUPPORTED + RV_CLEAR_CSR(mideleg, BIT(_irq_data_table[i].intr)); #endif /* enable the interrupt if ESP_INTR_FLAG_INTRDISABLED is not set */ if ((flags & ESP_INTR_FLAG_INTRDISABLED) == 0) { esp_cpu_intr_enable(BIT(_irq_data_table[i].intr)); } + else { + esp_cpu_intr_disable(BIT(_irq_data_table[i].intr)); + } if (ret_handle) { *((intr_handle_t *)ret_handle) = (const intr_handle_t)&_irq_data_table[i];