From 56e59eb0368dec9e0306bdb52020d6fa5fe6e1a0 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Thu, 11 Aug 2022 12:46:41 +0200 Subject: [PATCH] cpu/esp32: add NimBLE support for ESP32 --- cpu/esp32/Kconfig.common | 1 + cpu/esp32/Kconfig.esp32 | 2 + cpu/esp32/Makefile | 4 + cpu/esp32/Makefile.dep | 8 + cpu/esp32/Makefile.features | 1 + cpu/esp32/Makefile.include | 1 + cpu/esp32/doc.txt | 19 +- cpu/esp32/esp-ble-nimble/Kconfig | 22 ++ cpu/esp32/esp-ble-nimble/Makefile | 3 + cpu/esp32/esp-ble-nimble/doc.txt | 17 ++ cpu/esp32/esp-ble-nimble/esp_ble_nimble.c | 204 ++++++++++++++++++ .../include/esp_ble_nimble/syscfg/syscfg.h | 41 ++++ sys/auto_init/auto_init.c | 5 + sys/auto_init/include/auto_init_priorities.h | 6 + 14 files changed, 332 insertions(+), 2 deletions(-) create mode 100644 cpu/esp32/esp-ble-nimble/Kconfig create mode 100644 cpu/esp32/esp-ble-nimble/Makefile create mode 100644 cpu/esp32/esp-ble-nimble/doc.txt create mode 100644 cpu/esp32/esp-ble-nimble/esp_ble_nimble.c create mode 100644 cpu/esp32/include/esp_ble_nimble/syscfg/syscfg.h diff --git a/cpu/esp32/Kconfig.common b/cpu/esp32/Kconfig.common index cd4ab2448d..43907cc846 100644 --- a/cpu/esp32/Kconfig.common +++ b/cpu/esp32/Kconfig.common @@ -31,6 +31,7 @@ config MODULE_ESP_SPI_RAM Say y to use external SPI RAM connected through the SPI interface. rsource "bootloader/Kconfig" +rsource "esp-ble-nimble/Kconfig" rsource "esp-idf/Kconfig" rsource "esp-idf-api/Kconfig" rsource "periph/Kconfig" diff --git a/cpu/esp32/Kconfig.esp32 b/cpu/esp32/Kconfig.esp32 index de478800db..f8e5f0db7c 100644 --- a/cpu/esp32/Kconfig.esp32 +++ b/cpu/esp32/Kconfig.esp32 @@ -18,6 +18,8 @@ config CPU_FAM_ESP32 select CPU_CORE_XTENSA_LX6 select HAS_ARCH_ESP32 select HAS_CPU_ESP32 + select HAS_BLE_NIMBLE + select HAS_BLE_NIMBLE_NETIF select HAS_ESP_BLE select HAS_ESP_BLE_ESP32 select HAS_ESP_HW_COUNTER diff --git a/cpu/esp32/Makefile b/cpu/esp32/Makefile index a75705dd87..c0885a57ba 100644 --- a/cpu/esp32/Makefile +++ b/cpu/esp32/Makefile @@ -13,6 +13,10 @@ ifneq (, $(filter esp_bootloader, $(USEMODULE))) DIRS += bootloader endif +ifneq (, $(filter esp_ble_nimble, $(USEMODULE))) + DIRS += esp-ble-nimble +endif + ifneq (, $(filter esp_eth, $(USEMODULE))) DIRS += esp-eth endif diff --git a/cpu/esp32/Makefile.dep b/cpu/esp32/Makefile.dep index 8435b4ddd5..2992395d42 100644 --- a/cpu/esp32/Makefile.dep +++ b/cpu/esp32/Makefile.dep @@ -131,6 +131,14 @@ ifneq (,$(filter mtd,$(USEMODULE))) USEMODULE += esp_idf_spi_flash endif +ifneq (,$(filter nimble,$(USEPKG))) + USEMODULE += esp_ble + USEMODULE += esp_ble_nimble + USEMODULE += nimble_host + USEMODULE += nimble_transport_hci_h4 + USEMODULE += ztimer_msec +endif + ifneq (,$(filter periph_adc,$(USEMODULE))) USEMODULE += esp_idf_adc endif diff --git a/cpu/esp32/Makefile.features b/cpu/esp32/Makefile.features index 11459aa417..5b4ee17c9c 100644 --- a/cpu/esp32/Makefile.features +++ b/cpu/esp32/Makefile.features @@ -25,6 +25,7 @@ endif ifeq (esp32,$(CPU_FAM)) FEATURES_PROVIDED += ble_nimble + FEATURES_PROVIDED += ble_nimble_netif FEATURES_PROVIDED += esp_ble FEATURES_PROVIDED += esp_ble_esp32 endif diff --git a/cpu/esp32/Makefile.include b/cpu/esp32/Makefile.include index a318ff6196..a810093989 100644 --- a/cpu/esp32/Makefile.include +++ b/cpu/esp32/Makefile.include @@ -86,6 +86,7 @@ ifneq (,$(filter esp_ble,$(USEMODULE))) endif ifneq (,$(filter esp_ble_nimble,$(USEMODULE))) + INCLUDES += -I$(RIOTCPU)/$(CPU)/include/esp_ble_nimble INCLUDES += $(NIMIBASE)/nimble/transport/common/hci_h4/include endif diff --git a/cpu/esp32/doc.txt b/cpu/esp32/doc.txt index e2e3cc53dd..d543b39f85 100644 --- a/cpu/esp32/doc.txt +++ b/cpu/esp32/doc.txt @@ -55,6 +55,7 @@ This document describes the RIOT implementation for supported variants 2. [WiFi Network Interface](#esp32_wifi_network_interface) 3. [WiFi SoftAP Network Interface](#esp32_wifi_ap_network_interface) 4. [ESP-NOW Network Interface](#esp32_esp_now_network_interface) + 4. [Bluetooth Interface](#esp32_esp_bluetooth_interface) 5. [Other Network Devices](#esp32_other_network_devices) 10. [Application-Specific Configurations](#esp32_application_specific_configurations) 1. [Make Variable `CFLAGS`](#esp32_config_make_variable) @@ -204,7 +205,7 @@ The key features of ESP32 are: | SPIs | 4 | yes (2) | | UARTs | 3 | yes | | WiFi | IEEE 802.11 b/g/n built in | yes | -| Bluetooth | v4.2 BR/EDR and BLE | no | +| Bluetooth | v4.2 BR/EDR and BLE | yes | | Ethernet | MAC interface with dedicated DMA and IEEE 1588 support | yes | | CAN | version 2.0 | yes | | IR | up to 8 channels TX/RX | no | @@ -335,7 +336,6 @@ at the moment: - Only **one core** (the PRO CPU) is used because RIOT does not support running multiple threads simultaneously. -- **Bluetooth** cannot be used at the moment. - **Flash encryption** is not yet supported. [Back to table of contents](#esp32_toc) @@ -1691,6 +1691,21 @@ the channel of the AP asvalue for the parameter 'ESP_NOW_CHANNEL'. [Back to table of contents](#esp32_toc) +## Bluetooth Network Interface {#esp32_esp_bluetooth_interface} + +The following ESP32x SoC variants (families) integrate a Bluetooth Link +Controller and a Bluetooth baseband system: + +- ESP32 supports Bluetooth v4.2 BR/EDR and Bluetooth LE +- ESP32-C3, ESP32-S3 support Bluetooth 5 and Bluetooth mesh + +The Bluetooth interface can be used with the Bluetooth host implementation +of the NimBLE package. Use one of the `nimble_*` modules for different +applications to enable the Bluetooth interface and the NimBLE host +implementation. Please refer to the NimBle package documentation for details. + +[Back to table of contents](#esp32_toc) + ## Other Network Devices {#esp32_other_network_devices} RIOT provides a number of driver modules for different types of network diff --git a/cpu/esp32/esp-ble-nimble/Kconfig b/cpu/esp32/esp-ble-nimble/Kconfig new file mode 100644 index 0000000000..3bbdc3b778 --- /dev/null +++ b/cpu/esp32/esp-ble-nimble/Kconfig @@ -0,0 +1,22 @@ +# Copyright (c) 2022 HAW Hamburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +config MODULE_ESP_BLE_NIMBLE + bool "ESP32 Bluetooth LE HCI for NimBLE host" + depends on TEST_KCONFIG + depends on CPU_FAM_ESP32 + depends on HAS_ESP_BLE + default y if TEST_KCONFIG && MODULE_NIMBLE + select MODULE_ESP_BLE + select MODULE_NIMBLE_HOST + select MODULE_NIMBLE_TRANSPORT_HCI_H4 + select MODULE_ZTIMER_MSEC + +config HAS_ESP_BLE + bool + help + Indicates that a ESP32 Buetooth LE controller. diff --git a/cpu/esp32/esp-ble-nimble/Makefile b/cpu/esp32/esp-ble-nimble/Makefile new file mode 100644 index 0000000000..d0e911aebe --- /dev/null +++ b/cpu/esp32/esp-ble-nimble/Makefile @@ -0,0 +1,3 @@ +MODULE=esp_ble_nimble + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/esp32/esp-ble-nimble/doc.txt b/cpu/esp32/esp-ble-nimble/doc.txt new file mode 100644 index 0000000000..207a5a3420 --- /dev/null +++ b/cpu/esp32/esp-ble-nimble/doc.txt @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2022 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup cpu_esp32_esp_ble_nimble ESP32 Bluetooth LE HCI for NimBLE host + * @ingroup cpu_esp32 + * @brief ESP32 Bluetooth LE HCI implementation for NimBLE host + * + * This module realizes the HCI for NimBLE host using the ESP32 Bluetooth controller + * + * @author Gunar Schorcht + */ diff --git a/cpu/esp32/esp-ble-nimble/esp_ble_nimble.c b/cpu/esp32/esp-ble-nimble/esp_ble_nimble.c new file mode 100644 index 0000000000..3b0ed29089 --- /dev/null +++ b/cpu/esp32/esp-ble-nimble/esp_ble_nimble.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2022 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32_esp_ble_nimble + * @{ + * + * @file + * @brief Implementation of the Bluetooth LE Host Controller Interface + * + * ESP32x SoC Bluetooth LE controller uses the uses UART H4 transport protocol. + * + * @author Gunar Schorcht + * @} + */ + +#include "log.h" +#include "esp_bt.h" +#include "mutex.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" + +/* The size of an HCI command packet is defined as POOL_CMD_SIZE + * in `./nimble/transport/src/transport.c.`. Since there is no defined + * symbol in the header files, we have to define it here. According to + * the BLE specification, controllers must accept HCI command packets + * with up to 255 bytes of data excluding the header. */ +#define BLE_HCI_CMD_SIZE (255 + BLE_HCI_CMD_HDR_LEN) + +#define BLE_HCI_CMD_HDR_LEN (3) +#define BLE_HCI_EVT_HDR_LEN (2) + +#define BLE_VHCI_TIMEOUT_MS 2000 + +/* Definition of UART H4 packet types */ +enum { + BLE_HCI_UART_H4_NONE = 0x00, + BLE_HCI_UART_H4_CMD = 0x01, + BLE_HCI_UART_H4_ACL = 0x02, + BLE_HCI_UART_H4_SCO = 0x03, + BLE_HCI_UART_H4_EVT = 0x04 +}; + +static const char *LOG_TAG = "esp_nimble"; + +static mutex_t _esp_vhci_semaphore = MUTEX_INIT; + +static struct hci_h4_sm _esp_h4sm; + +static void _ble_vhci_controller_ready_cb(void) +{ + mutex_unlock(&_esp_vhci_semaphore); +} + +static int _ble_vhci_packet_received_cb(uint8_t *data, uint16_t len) +{ + /* process the HCI H4 formatted packet and call ble_transport_to_hs_* */ + len = hci_h4_sm_rx(&_esp_h4sm, data, len); + return 0; +} + +static const esp_vhci_host_callback_t vhci_host_cb = { + .notify_host_send_available = _ble_vhci_controller_ready_cb, + .notify_host_recv = _ble_vhci_packet_received_cb, +}; + +static inline int _ble_transport_to_ll(uint8_t *packet, uint16_t len) +{ + uint8_t rc = 0; + + /* 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"); + } + + /* take the semaphore with timeout and send the packet to the controller */ + if (ztimer_mutex_lock_timeout(ZTIMER_MSEC, &_esp_vhci_semaphore, + BLE_VHCI_TIMEOUT_MS) == 0) { + esp_vhci_host_send_packet(packet, len); + } + else { + rc = BLE_HS_ETIMEOUT_HCI; + } + return rc; +} + +int ble_transport_to_ll_cmd_impl(void *buf) +{ + uint16_t len; + uint8_t rc = 0; + uint8_t packet[BLE_HCI_CMD_SIZE + 1]; + uint8_t* cmd = buf; + + assert(cmd != NULL); + + /* Prepare the HCI H4 formatted packet. HCI H4 uses one byte HCI packet + * indicator in front of the HCI command packet. */ + + len = BLE_HCI_CMD_HDR_LEN + cmd[2] + 1; /* overall length */ + packet[0] = BLE_HCI_UART_H4_CMD; /* first byte is the packet indicator */ + memcpy(packet + 1, cmd, len - 1); + + /* send the packet */ + rc = _ble_transport_to_ll(packet, len); + + /* release the packet buffer */ + ble_transport_free(buf); + + return rc; +} + +int ble_transport_to_ll_acl_impl(struct os_mbuf *om) +{ + uint16_t len = 0; + uint8_t rc = 0; + uint8_t packet[MYNEWT_VAL(BLE_TRANSPORT_ACL_SIZE) + 1]; + + assert(om != NULL); + + /* If this packet is zero length, just free it */ + if (OS_MBUF_PKTLEN(om) == 0) { + os_mbuf_free_chain(om); + return 0; + } + + /* Prepare the HCI H4 formatted packet. HCI H4 uses one byte HCI packet + indicator in front of the HCI command packet. */ + + packet[0] = BLE_HCI_UART_H4_ACL; + len++; + + os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &packet[1]); + len += OS_MBUF_PKTLEN(om); + + /* send the packet */ + rc = _ble_transport_to_ll(packet, len); + + /* release the mbuf */ + os_mbuf_free_chain(om); + + return rc; +} + +static int _esp_hci_h4_frame_cb(uint8_t pkt_type, void *data) +{ + int rc = 0; + + switch (pkt_type) { + case HCI_H4_ACL: + rc = ble_transport_to_hs_acl(data); + break; + case HCI_H4_EVT: + rc = ble_transport_to_hs_evt(data); + break; + default: + assert(0); + break; + } + + return rc; +} + +void esp_ble_nimble_init(void) +{ + esp_err_t ret; + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + + /* 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, + "Bluetooth controller release classic bt memory failed: %s", + esp_err_to_name(ret)); + assert(0); + } + */ + + /* 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); + } + + 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); + } + + /* register callbacks from Bluetooth LE controller */ + if ((ret = esp_vhci_host_register_callback(&vhci_host_cb)) != ESP_OK) { + assert(0); + } + + /* init HCI H4 processing */ + hci_h4_sm_init(&_esp_h4sm, &hci_h4_allocs_from_ll, _esp_hci_h4_frame_cb); +} diff --git a/cpu/esp32/include/esp_ble_nimble/syscfg/syscfg.h b/cpu/esp32/include/esp_ble_nimble/syscfg/syscfg.h new file mode 100644 index 0000000000..5ee86db9d3 --- /dev/null +++ b/cpu/esp32/include/esp_ble_nimble/syscfg/syscfg.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32_esp_ble_nimble + * @{ + * + * @file + * @brief NimBLE configuration for ESP32x SoCs + * + * @author Gunar Schorcht + */ + + +#ifndef ESP_BLE_NIMBLE_SYSCFG_SYSCFG_H +#define ESP_BLE_NIMBLE_SYSCFG_SYSCFG_H + +#if !DOXYGEN + +/* disable native transport mechanism and enable the custom transport mechanism */ +#define MYNEWT_VAL_BLE_TRANSPORT_LL__custom (1) +#define MYNEWT_VAL_BLE_TRANSPORT_LL__native (0) + +#include_next "syscfg/syscfg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !DOXYGEN */ +#endif /* ESP_BLE_NIMBLE_SYSCFG_SYSCFG_H */ +/** @} */ diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 89a10ac866..4ec21d3dd4 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -195,6 +195,11 @@ extern void asymcute_handler_run(void); AUTO_INIT(asymcute_handler_run, AUTO_INIT_PRIO_MOD_ASYMCUTE); #endif +#if IS_USED(MODULE_ESP_BLE_NIMBLE) +extern void esp_ble_nimble_init(void); +AUTO_INIT(esp_ble_nimble_init, + AUTO_INIT_PRIO_MOD_ESP_BLE_NIMBLE); +#endif #if IS_USED(MODULE_NIMBLE) extern void nimble_riot_init(void); AUTO_INIT(nimble_riot_init, diff --git a/sys/auto_init/include/auto_init_priorities.h b/sys/auto_init/include/auto_init_priorities.h index 773916b2e5..8dd90b5ce3 100644 --- a/sys/auto_init/include/auto_init_priorities.h +++ b/sys/auto_init/include/auto_init_priorities.h @@ -209,6 +209,12 @@ extern "C" { */ #define AUTO_INIT_PRIO_MOD_ASYMCUTE 1310 #endif +#ifndef AUTO_INIT_PRIO_MOD_ESP_BLE_NIMBLE +/** + * @brief ESP BLE NimBLE priority + */ +#define AUTO_INIT_PRIO_MOD_ESP_BLE_NIMBLE 1319 +#endif #ifndef AUTO_INIT_PRIO_MOD_NIMBLE /** * @brief NimBLE priority