From 44aa5b220073830e3cc1477c803cac7acabfcc17 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:23:44 +1100 Subject: [PATCH 01/15] stm32/modnetwork: Remove redundant call to nimble_poll in lwip poll. The bluetooth stack has its own dedicated polling function, see mod_bluetooth_nimble_poll_wrapper(). --- ports/stm32/modnetwork.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ports/stm32/modnetwork.c b/ports/stm32/modnetwork.c index 8f10612d8c4da..4419bf47684ab 100644 --- a/ports/stm32/modnetwork.c +++ b/ports/stm32/modnetwork.c @@ -63,11 +63,6 @@ STATIC void pyb_lwip_poll(void) { // Run the lwIP internal updates sys_check_timeouts(); - - #if MICROPY_BLUETOOTH_NIMBLE - extern void nimble_poll(void); - nimble_poll(); - #endif } void mod_network_lwip_poll_wrapper(uint32_t ticks_ms) { From c44d52f33e4ec626c7f6293323300baaeb55e856 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:27:30 +1100 Subject: [PATCH 02/15] extmod/modbluetooth_nimble: Move nimble specific code, factor nimble.mk. Move extmod/modbluetooth_nimble.* to extmod/nimble. And move common Makefile lines to extmod/nimble/nimble.mk (which was previously only used by stm32). This allows (upcoming) btstack to follow a similar structure. Work done in collaboration with Jim Mussared aka @jimmo. --- extmod/{ => nimble}/modbluetooth_nimble.c | 4 ++-- extmod/{ => nimble}/modbluetooth_nimble.h | 6 +++--- extmod/nimble/nimble.mk | 19 +++++++++++++++---- ports/esp32/Makefile | 14 +++++++------- ports/stm32/Makefile | 6 +++++- ports/stm32/nimble.c | 2 +- 6 files changed, 33 insertions(+), 18 deletions(-) rename extmod/{ => nimble}/modbluetooth_nimble.c (99%) rename extmod/{ => nimble}/modbluetooth_nimble.h (91%) diff --git a/extmod/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c similarity index 99% rename from extmod/modbluetooth_nimble.c rename to extmod/nimble/modbluetooth_nimble.c index aa1b032f27a05..f7602c35fd1f2 100644 --- a/extmod/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -31,8 +31,8 @@ #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE -#include "modbluetooth_nimble.h" -#include "modbluetooth.h" +#include "extmod/nimble/modbluetooth_nimble.h" +#include "extmod/modbluetooth.h" #include "host/ble_hs.h" #include "host/util/util.h" diff --git a/extmod/modbluetooth_nimble.h b/extmod/nimble/modbluetooth_nimble.h similarity index 91% rename from extmod/modbluetooth_nimble.h rename to extmod/nimble/modbluetooth_nimble.h index e2474a6ee50b9..c70c3bc90ae63 100644 --- a/extmod/modbluetooth_nimble.h +++ b/extmod/nimble/modbluetooth_nimble.h @@ -24,8 +24,8 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_NIMBLE_H -#define MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_NIMBLE_H +#ifndef MICROPY_INCLUDED_EXTMOD_NIMBLE_MODBLUETOOTH_NIMBLE_H +#define MICROPY_INCLUDED_EXTMOD_NIMBLE_MODBLUETOOTH_NIMBLE_H #define MP_BLUETOOTH_NIMBLE_MAX_SERVICES (8) @@ -51,4 +51,4 @@ void mp_bluetooth_nimble_port_postinit(void); void mp_bluetooth_nimble_port_deinit(void); void mp_bluetooth_nimble_port_start(void); -#endif // MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_NIMBLE_H +#endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_MODBLUETOOTH_NIMBLE_H diff --git a/extmod/nimble/nimble.mk b/extmod/nimble/nimble.mk index e554e9ff0619f..4d2c6637a4f39 100644 --- a/extmod/nimble/nimble.mk +++ b/extmod/nimble/nimble.mk @@ -1,10 +1,21 @@ -# Makefile directives for Apache mynewt nimble BLE component +# Makefile directives for Apache Mynewt NimBLE component ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) -NIMBLE_LIB_DIR = lib/mynewt-nimble +EXTMOD_SRC_C += extmod/nimble/modbluetooth_nimble.c + +CFLAGS_MOD += -DMICROPY_BLUETOOTH_NIMBLE=1 + NIMBLE_EXTMOD_DIR = extmod/nimble +# Use NimBLE from the submodule in lib/mynewt-nimble by default, +# allowing a port to use their own system version (e.g. ESP32). +MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY ?= 0 + +ifeq ($(MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY),0) + +NIMBLE_LIB_DIR = lib/mynewt-nimble + SRC_LIB += $(addprefix $(NIMBLE_LIB_DIR)/, \ $(addprefix ext/tinycrypt/src/, \ aes_encrypt.c \ @@ -76,8 +87,6 @@ EXTMOD_SRC_C += $(addprefix $(NIMBLE_EXTMOD_DIR)/, \ nimble/hci_uart.c \ ) -CFLAGS_MOD += -DMICROPY_BLUETOOTH_NIMBLE=1 - INC += -I$(TOP)/$(NIMBLE_EXTMOD_DIR) INC += -I$(TOP)/$(NIMBLE_LIB_DIR) INC += -I$(TOP)/$(NIMBLE_LIB_DIR)/ext/tinycrypt/include @@ -93,3 +102,5 @@ INC += -I$(TOP)/$(NIMBLE_LIB_DIR)/porting/nimble/include $(BUILD)/$(NIMBLE_LIB_DIR)/%.o: CFLAGS += -Wno-maybe-uninitialized -Wno-pointer-arith -Wno-unused-but-set-variable -Wno-format endif + +endif diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index fe4787b7ae338..7033a3dd26c27 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -118,7 +118,12 @@ endif MICROPY_PY_BLUETOOTH ?= 1 ifeq ($(MICROPY_PY_BLUETOOTH),1) SDKCONFIG += boards/sdkconfig.ble -MICROPY_BLUETOOTH_NIMBLE = 1 + +# Use NimBLE on ESP32. +MICROPY_BLUETOOTH_NIMBLE ?= 1 +# Use Nimble bindings, but ESP32 IDF provides the Nimble library. +MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY = 1 +include $(TOP)/extmod/nimble/nimble.mk endif # include sdkconfig to get needed configuration values @@ -244,10 +249,6 @@ INC_NEWLIB += -I$(ESPCOMP)/newlib/include ifeq ($(MICROPY_PY_BLUETOOTH),1) CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH=1 CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1 - -ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) -CFLAGS_MOD += -DMICROPY_BLUETOOTH_NIMBLE=1 -endif endif # these flags are common to C and C++ compilation @@ -354,9 +355,8 @@ SRC_C = \ $(wildcard $(BOARD_DIR)/*.c) \ $(SRC_MOD) -EXTMOD_SRC_C = $(addprefix extmod/,\ +EXTMOD_SRC_C += $(addprefix extmod/,\ modonewire.c \ - modbluetooth_nimble.c \ ) LIB_SRC_C = $(addprefix lib/,\ diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 11db9dfbabf0b..235b251644fb6 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -458,14 +458,18 @@ CFLAGS_MOD += -DMBEDTLS_CONFIG_FILE='"mbedtls/mbedtls_config.h"' SRC_MOD += mbedtls/mbedtls_port.c endif +ifeq ($(MICROPY_PY_BLUETOOTH),1) + ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) include $(TOP)/extmod/nimble/nimble.mk SRC_C += nimble.c SRC_C += nimble_hci_uart.c -EXTMOD_SRC_C += extmod/modbluetooth_nimble.c +endif + ifeq ($(MICROPY_PY_NETWORK_CYW43),1) DRIVERS_SRC_C += drivers/cyw43/cywbt.c endif + endif OBJ = diff --git a/ports/stm32/nimble.c b/ports/stm32/nimble.c index b8fdc8f8866ef..b21b2bede7e90 100644 --- a/ports/stm32/nimble.c +++ b/ports/stm32/nimble.c @@ -36,7 +36,7 @@ #include "transport/uart/ble_hci_uart.h" #include "host/ble_hs.h" -#include "extmod/modbluetooth_nimble.h" +#include "extmod/nimble/modbluetooth_nimble.h" extern void nimble_uart_process(void); extern void os_eventq_run_all(void); From 894c550c866211c9f176875d40c15fcf3bf74149 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:35:07 +1100 Subject: [PATCH 03/15] stm32: Refactor bluetooth stack/hci/driver bindings. This makes a cleaner separation between the: driver, HCI UART and BT stack. Also updated the naming to be more consistent (mp_bluetooth_hci_*). Work done in collaboration with Jim Mussared aka @jimmo. --- drivers/cyw43/cywbt.c | 100 +++++++--- .../cywbt.h => extmod/modbluetooth_hci.h | 34 +++- .../{nimble/hci_uart.c => hal/hal_uart.c} | 27 ++- extmod/nimble/hal/hal_uart.h | 1 + extmod/nimble/nimble.mk | 2 +- .../nimble/{hci_uart.h => nimble_hci_uart.h} | 23 +-- extmod/nimble/nimble/npl_os.c | 4 +- ports/stm32/Makefile | 3 +- ports/stm32/main.c | 8 +- ports/stm32/modbluetooth_hci.c | 158 ++++++++++++++++ ports/stm32/nimble.c | 59 ++++-- ports/stm32/nimble_hci_uart.c | 176 ------------------ ports/stm32/pendsv.h | 4 +- ports/stm32/systick.h | 4 +- 14 files changed, 338 insertions(+), 265 deletions(-) rename drivers/cyw43/cywbt.h => extmod/modbluetooth_hci.h (50%) rename extmod/nimble/{nimble/hci_uart.c => hal/hal_uart.c} (78%) rename extmod/nimble/nimble/{hci_uart.h => nimble_hci_uart.h} (70%) create mode 100644 ports/stm32/modbluetooth_hci.c delete mode 100644 ports/stm32/nimble_hci_uart.c diff --git a/drivers/cyw43/cywbt.c b/drivers/cyw43/cywbt.c index fae661608dc36..79318f4483ba4 100644 --- a/drivers/cyw43/cywbt.c +++ b/drivers/cyw43/cywbt.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Damien P. George + * Copyright (c) 2019-2020 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,8 +31,7 @@ #include "py/mphal.h" #include "pin_static_af.h" #include "uart.h" -#include "cywbt.h" -#include "nimble/hci_uart.h" +#include "extmod/modbluetooth_hci.h" #if MICROPY_PY_NETWORK_CYW43 @@ -42,7 +41,7 @@ extern const char fw_4343WA1_7_45_98_50_start; /******************************************************************************/ // CYW BT HCI low-level driver -static void cywbt_wait_cts_low(void) { +STATIC void cywbt_wait_cts_low(void) { mp_hal_pin_config(pyb_pin_BT_CTS, MP_HAL_PIN_MODE_INPUT, MP_HAL_PIN_PULL_UP, 0); for (int i = 0; i < 200; ++i) { if (mp_hal_pin_read(pyb_pin_BT_CTS) == 0) { @@ -53,13 +52,13 @@ static void cywbt_wait_cts_low(void) { mp_hal_pin_config_alt_static(pyb_pin_BT_CTS, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_USART6_CTS); } -static int cywbt_hci_cmd_raw(size_t len, uint8_t *buf) { - uart_tx_strn(&bt_hci_uart_obj, (void*)buf, len); +STATIC int cywbt_hci_cmd_raw(size_t len, uint8_t *buf) { + uart_tx_strn(&mp_bluetooth_hci_uart_obj, (void*)buf, len); for (int i = 0; i < 6; ++i) { - while (!uart_rx_any(&bt_hci_uart_obj)) { + while (!uart_rx_any(&mp_bluetooth_hci_uart_obj)) { MICROPY_EVENT_POLL_HOOK } - buf[i] = uart_rx_char(&bt_hci_uart_obj); + buf[i] = uart_rx_char(&mp_bluetooth_hci_uart_obj); } // expect a comand complete event (event 0x0e) @@ -76,17 +75,17 @@ static int cywbt_hci_cmd_raw(size_t len, uint8_t *buf) { int sz = buf[2] - 3; for (int i = 0; i < sz; ++i) { - while (!uart_rx_any(&bt_hci_uart_obj)) { + while (!uart_rx_any(&mp_bluetooth_hci_uart_obj)) { MICROPY_EVENT_POLL_HOOK } - buf[i] = uart_rx_char(&bt_hci_uart_obj); + buf[i] = uart_rx_char(&mp_bluetooth_hci_uart_obj); } return 0; } -static int cywbt_hci_cmd(int ogf, int ocf, size_t param_len, const uint8_t *param_buf) { - uint8_t *buf = bt_hci_cmd_buf; +STATIC int cywbt_hci_cmd(int ogf, int ocf, size_t param_len, const uint8_t *param_buf) { + uint8_t *buf = mp_bluetooth_hci_cmd_buf; buf[0] = 0x01; buf[1] = ocf; buf[2] = ogf << 2 | ocf >> 8; @@ -97,19 +96,19 @@ static int cywbt_hci_cmd(int ogf, int ocf, size_t param_len, const uint8_t *para return cywbt_hci_cmd_raw(4 + param_len, buf); } -static void put_le16(uint8_t *buf, uint16_t val) { +STATIC void put_le16(uint8_t *buf, uint16_t val) { buf[0] = val; buf[1] = val >> 8; } -static void put_le32(uint8_t *buf, uint32_t val) { +STATIC void put_le32(uint8_t *buf, uint32_t val) { buf[0] = val; buf[1] = val >> 8; buf[2] = val >> 16; buf[3] = val >> 24; } -static int cywbt_set_baudrate(uint32_t baudrate) { +STATIC int cywbt_set_baudrate(uint32_t baudrate) { uint8_t buf[6]; put_le16(buf, 0); put_le32(buf + 2, baudrate); @@ -117,12 +116,12 @@ static int cywbt_set_baudrate(uint32_t baudrate) { } // download firmware -static int cywbt_download_firmware(const uint8_t *firmware) { +STATIC int cywbt_download_firmware(const uint8_t *firmware) { cywbt_hci_cmd(0x3f, 0x2e, 0, NULL); bool last_packet = false; while (!last_packet) { - uint8_t *buf = bt_hci_cmd_buf; + uint8_t *buf = mp_bluetooth_hci_cmd_buf; memcpy(buf + 1, firmware, 3); firmware += 3; last_packet = buf[1] == 0x4e; @@ -149,15 +148,15 @@ static int cywbt_download_firmware(const uint8_t *firmware) { cywbt_wait_cts_low(); mp_hal_pin_config(pyb_pin_WL_GPIO_1, MP_HAL_PIN_MODE_INPUT, MP_HAL_PIN_PULL_DOWN, 0); // Select chip antenna (could also select external) - nimble_hci_uart_set_baudrate(115200); + mp_bluetooth_hci_uart_set_baudrate(115200); cywbt_set_baudrate(3000000); - nimble_hci_uart_set_baudrate(3000000); + mp_bluetooth_hci_uart_set_baudrate(3000000); return 0; } -int cywbt_init(void) { - // This is called from Nimble via hal_uart_config which will have already initialized the UART. +int mp_bluetooth_hci_controller_init(void) { + // This is called immediately after the UART is initialised during stack initialisation. mp_hal_pin_output(pyb_pin_BT_REG_ON); mp_hal_pin_low(pyb_pin_BT_REG_ON); @@ -172,11 +171,11 @@ int cywbt_init(void) { return 0; } -int cywbt_activate(void) { +int mp_bluetooth_hci_controller_activate(void) { uint8_t buf[256]; mp_hal_pin_low(pyb_pin_BT_REG_ON); - nimble_hci_uart_set_baudrate(115200); + mp_bluetooth_hci_uart_set_baudrate(115200); mp_hal_delay_ms(100); mp_hal_pin_high(pyb_pin_BT_REG_ON); cywbt_wait_cts_low(); @@ -186,7 +185,7 @@ int cywbt_activate(void) { // Change baudrate cywbt_set_baudrate(3000000); - nimble_hci_uart_set_baudrate(3000000); + mp_bluetooth_hci_uart_set_baudrate(3000000); cywbt_download_firmware((const uint8_t*)CYWBT_FW_ADDR); @@ -220,4 +219,57 @@ int cywbt_activate(void) { return 0; } +int mp_bluetooth_hci_controller_deactivate(void) { + mp_hal_pin_low(pyb_pin_BT_REG_ON); + + return 0; +} + +#ifdef pyb_pin_BT_DEV_WAKE +STATIC uint32_t bt_sleep_ticks; +#endif + +int mp_bluetooth_hci_controller_sleep_maybe(void) { + #ifdef pyb_pin_BT_DEV_WAKE + if (mp_hal_pin_read(pyb_pin_BT_DEV_WAKE) == 0) { + if (mp_hal_ticks_ms() - bt_sleep_ticks > 500) { + mp_hal_pin_high(pyb_pin_BT_DEV_WAKE); // let sleep + } + } + #endif + return 0; +} + +bool mp_bluetooth_hci_controller_woken(void) { + #ifdef pyb_pin_BT_HOST_WAKE + bool host_wake = mp_hal_pin_read(pyb_pin_BT_HOST_WAKE); + /* + // this is just for info/tracing purposes + static bool last_host_wake = false; + if (host_wake != last_host_wake) { + printf("HOST_WAKE change %d -> %d\n", last_host_wake, host_wake); + last_host_wake = host_wake; + } + */ + return host_wake; + #else + return true; + #endif +} + +int mp_bluetooth_hci_controller_wakeup(void) { + #ifdef pyb_pin_BT_DEV_WAKE + bt_sleep_ticks = mp_hal_ticks_ms(); + + if (mp_hal_pin_read(pyb_pin_BT_DEV_WAKE) == 1) { + mp_hal_pin_low(pyb_pin_BT_DEV_WAKE); // wake up + // Use delay_us rather than delay_ms to prevent running the scheduler (which + // might result in more BLE operations). + mp_hal_delay_us(5000); // can't go lower than this + } + #endif + + return 0; +} + #endif diff --git a/drivers/cyw43/cywbt.h b/extmod/modbluetooth_hci.h similarity index 50% rename from drivers/cyw43/cywbt.h rename to extmod/modbluetooth_hci.h index 9809d7f41a5b2..6d44761a4efbe 100644 --- a/drivers/cyw43/cywbt.h +++ b/extmod/modbluetooth_hci.h @@ -23,13 +23,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_DRIVERS_CYW43_CYWBT_H -#define MICROPY_INCLUDED_DRIVERS_CYW43_CYWBT_H -extern uint8_t bt_hci_cmd_buf[4 + 256]; -extern pyb_uart_obj_t bt_hci_uart_obj; +#ifndef MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_HCI_H +#define MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_HCI_H -int cywbt_init(void); -int cywbt_activate(void); +#include "uart.h" -#endif // MICROPY_INCLUDED_DRIVERS_CYW43_CYWBT_H +// Optionally can be implemented by the driver. +int mp_bluetooth_hci_controller_init(void); +int mp_bluetooth_hci_controller_activate(void); +int mp_bluetooth_hci_controller_deactivate(void); + +// Tell the controller to go to sleep (e.g. on RX if we don't think we're expecting anything more). +int mp_bluetooth_hci_controller_sleep_maybe(void); +// True if the controller woke us up. +bool mp_bluetooth_hci_controller_woken(void); +// Wake up the controller (e.g. we're about to TX). +int mp_bluetooth_hci_controller_wakeup(void); + +// Storage and bindings that need to be implemented by the port. +// These are used by the stack bindings (e.g. nimble/hal_uart.c) +// as well as potentially the driver (e.g. cywbt.c). +extern uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; +extern pyb_uart_obj_t mp_bluetooth_hci_uart_obj; + +int mp_bluetooth_hci_uart_init(uint32_t port); +int mp_bluetooth_hci_uart_activate(void); +int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate); +int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len); + +#endif // MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_HCI_H diff --git a/extmod/nimble/nimble/hci_uart.c b/extmod/nimble/hal/hal_uart.c similarity index 78% rename from extmod/nimble/nimble/hci_uart.c rename to extmod/nimble/hal/hal_uart.c index b4ac4e738828e..fba82b830438a 100644 --- a/extmod/nimble/nimble/hci_uart.c +++ b/extmod/nimble/hal/hal_uart.c @@ -28,14 +28,11 @@ #include "py/mphal.h" #include "pin_static_af.h" #include "nimble/ble.h" -#include "hal/hal_uart.h" -#include "hci_uart.h" +#include "extmod/nimble/hal/hal_uart.h" +#include "extmod/modbluetooth_hci.h" +#include "extmod/nimble/nimble/nimble_hci_uart.h" -#if MICROPY_BLUETOOTH_NIMBLE - -/******************************************************************************/ -// Bindings Uart to Nimble -uint8_t bt_hci_cmd_buf[4 + 256]; +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE static hal_uart_tx_cb_t hal_uart_tx_cb; static void *hal_uart_tx_arg; @@ -51,9 +48,9 @@ int hal_uart_init_cbs(uint32_t port, hal_uart_tx_cb_t tx_cb, void *tx_arg, hal_u } int hal_uart_config(uint32_t port, uint32_t baudrate, uint32_t bits, uint32_t stop, uint32_t parity, uint32_t flow) { - nimble_hci_uart_configure(port); - nimble_hci_uart_set_baudrate(baudrate); - return nimble_hci_uart_activate(); + mp_bluetooth_hci_uart_init(port); + mp_bluetooth_hci_uart_set_baudrate(baudrate); + return mp_bluetooth_hci_uart_activate(); } void hal_uart_start_tx(uint32_t port) { @@ -63,7 +60,7 @@ void hal_uart_start_tx(uint32_t port) { if (data == -1) { break; } - bt_hci_cmd_buf[len++] = data; + mp_bluetooth_hci_cmd_buf[len++] = data; } #if 0 @@ -74,15 +71,15 @@ void hal_uart_start_tx(uint32_t port) { printf("\n"); #endif - nimble_hci_uart_tx_strn((void*)bt_hci_cmd_buf, len); + mp_bluetooth_nimble_hci_uart_tx_strn((void*)mp_bluetooth_hci_cmd_buf, len); } int hal_uart_close(uint32_t port) { return 0; // success } -void nimble_uart_process(void) { - nimble_hci_uart_rx(hal_uart_rx_cb, hal_uart_rx_arg); +void mp_bluetooth_nimble_hci_uart_process(void) { + mp_bluetooth_nimble_hci_uart_rx(hal_uart_rx_cb, hal_uart_rx_arg); } -#endif // MICROPY_BLUETOOTH_NIMBLE +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/extmod/nimble/hal/hal_uart.h b/extmod/nimble/hal/hal_uart.h index 294dfb50dd451..0ef04bc81e026 100644 --- a/extmod/nimble/hal/hal_uart.h +++ b/extmod/nimble/hal/hal_uart.h @@ -10,6 +10,7 @@ typedef int (*hal_uart_tx_cb_t)(void *arg); typedef int (*hal_uart_rx_cb_t)(void *arg, uint8_t data); +// Called by NimBLE, implemented in hal_uart.c. int hal_uart_init_cbs(uint32_t port, hal_uart_tx_cb_t tx_cb, void *tx_arg, hal_uart_rx_cb_t rx_cb, void *rx_arg); int hal_uart_config(uint32_t port, uint32_t baud, uint32_t bits, uint32_t stop, uint32_t parity, uint32_t flow); void hal_uart_start_tx(uint32_t port); diff --git a/extmod/nimble/nimble.mk b/extmod/nimble/nimble.mk index 4d2c6637a4f39..60dbe60b0a24d 100644 --- a/extmod/nimble/nimble.mk +++ b/extmod/nimble/nimble.mk @@ -84,7 +84,7 @@ SRC_LIB += $(addprefix $(NIMBLE_LIB_DIR)/, \ EXTMOD_SRC_C += $(addprefix $(NIMBLE_EXTMOD_DIR)/, \ nimble/npl_os.c \ - nimble/hci_uart.c \ + hal/hal_uart.c \ ) INC += -I$(TOP)/$(NIMBLE_EXTMOD_DIR) diff --git a/extmod/nimble/nimble/hci_uart.h b/extmod/nimble/nimble/nimble_hci_uart.h similarity index 70% rename from extmod/nimble/nimble/hci_uart.h rename to extmod/nimble/nimble/nimble_hci_uart.h index 3d4a2a9835f17..646a12dac1460 100644 --- a/extmod/nimble/nimble/hci_uart.h +++ b/extmod/nimble/nimble/nimble_hci_uart.h @@ -23,21 +23,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_HCI_UART_H -#define MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_HCI_UART_H +#ifndef MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_NIMBLE_HCI_UART_H +#define MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_NIMBLE_HCI_UART_H -#include "extmod/nimble/hal/hal_uart.h" - -// To be implemented by the port. +// Extensions to extmod/modbluetooth_hci.h specific to NimBLE. -int nimble_hci_uart_configure(uint32_t port); - -// This will default to MICROPY_HW_BLE_UART_BAUDRATE, but can be updated later. -int nimble_hci_uart_set_baudrate(uint32_t baudrate); +#include "extmod/nimble/hal/hal_uart.h" -int nimble_hci_uart_activate(void); +// Helpers called from ports. +void mp_bluetooth_nimble_hci_uart_process(void); -void nimble_hci_uart_rx(hal_uart_rx_cb_t rx_cb, void *rx_arg); -void nimble_hci_uart_tx_strn(const char *str, uint len); +// Must be provided by the port. +void mp_bluetooth_nimble_hci_uart_rx(hal_uart_rx_cb_t rx_cb, void *rx_arg); +void mp_bluetooth_nimble_hci_uart_tx_strn(const char *str, uint len); -#endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_HCI_UART_H +#endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_NIMBLE_HCI_UART_H diff --git a/extmod/nimble/nimble/npl_os.c b/extmod/nimble/nimble/npl_os.c index 4875d2d1ad45c..57ab689e6b6c1 100644 --- a/extmod/nimble/nimble/npl_os.c +++ b/extmod/nimble/nimble/npl_os.c @@ -29,6 +29,7 @@ #include "py/runtime.h" #include "nimble/ble.h" #include "nimble/nimble_npl.h" +#include "nimble/nimble_hci_uart.h" #define DEBUG_OS_printf(...) //printf(__VA_ARGS__) #define DEBUG_MALLOC_printf(...) //printf(__VA_ARGS__) @@ -234,8 +235,7 @@ ble_npl_error_t ble_npl_sem_pend(struct ble_npl_sem *sem, ble_npl_time_t timeout if (sem->count == 0) { uint32_t t0 = mp_hal_ticks_ms(); while (sem->count == 0 && mp_hal_ticks_ms() - t0 < timeout) { - extern void nimble_uart_process(void); - nimble_uart_process(); + mp_bluetooth_nimble_hci_uart_process(); if (sem->count != 0) { break; } diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 235b251644fb6..fab2f621b22a2 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -460,10 +460,11 @@ endif ifeq ($(MICROPY_PY_BLUETOOTH),1) +SRC_C += modbluetooth_hci.c + ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) include $(TOP)/extmod/nimble/nimble.mk SRC_C += nimble.c -SRC_C += nimble_hci_uart.c endif ifeq ($(MICROPY_PY_NETWORK_CYW43),1) diff --git a/ports/stm32/main.c b/ports/stm32/main.c index e221df0d0fa74..78973f41184d7 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -49,7 +49,7 @@ #include "drivers/cyw43/cyw43.h" #endif -#if MICROPY_BLUETOOTH_NIMBLE +#if MICROPY_PY_BLUETOOTH #include "extmod/modbluetooth.h" #endif @@ -539,9 +539,9 @@ void stm32_main(uint32_t reset_mode) { #endif systick_enable_dispatch(SYSTICK_DISPATCH_LWIP, mod_network_lwip_poll_wrapper); #endif - #if MICROPY_BLUETOOTH_NIMBLE - extern void mod_bluetooth_nimble_poll_wrapper(uint32_t ticks_ms); - systick_enable_dispatch(SYSTICK_DISPATCH_NIMBLE, mod_bluetooth_nimble_poll_wrapper); + #if MICROPY_PY_BLUETOOTH + extern void mp_bluetooth_hci_poll_wrapper(uint32_t ticks_ms); + systick_enable_dispatch(SYSTICK_DISPATCH_BLUETOOTH_HCI, mp_bluetooth_hci_poll_wrapper); #endif #if MICROPY_PY_NETWORK_CYW43 diff --git a/ports/stm32/modbluetooth_hci.c b/ports/stm32/modbluetooth_hci.c new file mode 100644 index 0000000000000..c6937b2b2a169 --- /dev/null +++ b/ports/stm32/modbluetooth_hci.c @@ -0,0 +1,158 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2020 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mphal.h" +#include "extmod/modbluetooth_hci.h" +#include "systick.h" +#include "pendsv.h" + +#include "py/obj.h" + +#if MICROPY_PY_BLUETOOTH + +uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + +// Must be provided by the stack bindings. +extern void mp_bluetooth_hci_poll(void); + +// Hook for pendsv poller to run this periodically every 128ms +#define BLUETOOTH_HCI_TICK(tick) (((tick) & ~(SYSTICK_DISPATCH_NUM_SLOTS - 1) & 0x7f) == 0) + +// Called periodically (systick) or directly (e.g. uart irq). +void mp_bluetooth_hci_poll_wrapper(uint32_t ticks_ms) { + if (ticks_ms == 0 || BLUETOOTH_HCI_TICK(ticks_ms)) { + pendsv_schedule_dispatch(PENDSV_DISPATCH_BLUETOOTH_HCI, mp_bluetooth_hci_poll); + } +} + +#if defined(STM32WB) + +/******************************************************************************/ +// HCI over IPCC + +#include "rfcore.h" + +int mp_bluetooth_hci_controller_deactivate(void) { + return 0; +} + +int mp_bluetooth_hci_controller_sleep_maybe(void) { + return 0; +} + +bool mp_bluetooth_hci_controller_woken(void) { + return true; +} + +int mp_bluetooth_hci_controller_wakeup(void) { + return 0; +} + +int mp_bluetooth_hci_uart_init(uint32_t port) { + (void)port; + return 0; +} + +int mp_bluetooth_hci_uart_activate(void) { + rfcore_ble_init(); + return 0; +} + +int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { + (void)baudrate; + return 0; +} + +int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { + MICROPY_PY_BLUETOOTH_ENTER + rfcore_ble_hci_cmd(len, (const uint8_t *)buf); + MICROPY_PY_BLUETOOTH_EXIT + return 0; +} + +#else + +/******************************************************************************/ +// HCI over UART + +#include "pendsv.h" +#include "uart.h" + +pyb_uart_obj_t mp_bluetooth_hci_uart_obj; + +static uint8_t hci_uart_rxbuf[512]; + +mp_obj_t mp_uart_interrupt(mp_obj_t self_in) { + // New HCI data, schedule mp_bluetooth_hci_poll to make the stack handle it. + mp_bluetooth_hci_poll_wrapper(0); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_uart_interrupt_obj, mp_uart_interrupt); + +int mp_bluetooth_hci_uart_init(uint32_t port) { + // bits (8), stop (1), parity (none) and flow (rts/cts) are assumed to match MYNEWT_VAL_BLE_HCI_UART_ constants in syscfg.h. + mp_bluetooth_hci_uart_obj.base.type = &pyb_uart_type; + mp_bluetooth_hci_uart_obj.uart_id = port; + mp_bluetooth_hci_uart_obj.is_static = true; + mp_bluetooth_hci_uart_obj.timeout = 2; + mp_bluetooth_hci_uart_obj.timeout_char = 2; + MP_STATE_PORT(pyb_uart_obj_all)[mp_bluetooth_hci_uart_obj.uart_id - 1] = &mp_bluetooth_hci_uart_obj; + return 0; +} + +int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { + uart_init(&mp_bluetooth_hci_uart_obj, baudrate, UART_WORDLENGTH_8B, UART_PARITY_NONE, UART_STOPBITS_1, UART_HWCONTROL_RTS | UART_HWCONTROL_CTS); + uart_set_rxbuf(&mp_bluetooth_hci_uart_obj, sizeof(hci_uart_rxbuf), hci_uart_rxbuf); + return 0; +} + +int mp_bluetooth_hci_uart_activate(void) { + // Interrupt on RX chunk received (idle) + // Trigger stack poll when this happens + mp_obj_t uart_irq_fn = mp_load_attr(MP_OBJ_FROM_PTR(&mp_bluetooth_hci_uart_obj), MP_QSTR_irq); + mp_obj_t uargs[] = { + MP_OBJ_FROM_PTR(&mp_uart_interrupt_obj), + MP_OBJ_NEW_SMALL_INT(UART_FLAG_IDLE), + mp_const_true, + }; + mp_call_function_n_kw(uart_irq_fn, 3, 0, uargs); + + mp_bluetooth_hci_controller_init(); + mp_bluetooth_hci_controller_activate(); + + return 0; +} + +int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { + mp_bluetooth_hci_controller_wakeup(); + uart_tx_strn(&mp_bluetooth_hci_uart_obj, (void *)buf, len); + return 0; +} + +#endif // defined(STM32WB) + +#endif // MICROPY_PY_BLUETOOTH diff --git a/ports/stm32/nimble.c b/ports/stm32/nimble.c index b21b2bede7e90..ff78a0a1a3d55 100644 --- a/ports/stm32/nimble.c +++ b/ports/stm32/nimble.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2019 Jim Mussared + * Copyright (c) 2020 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,37 +31,26 @@ #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE -#include "systick.h" -#include "pendsv.h" - #include "transport/uart/ble_hci_uart.h" #include "host/ble_hs.h" +#include "extmod/modbluetooth_hci.h" #include "extmod/nimble/modbluetooth_nimble.h" +#include "extmod/nimble/nimble/nimble_hci_uart.h" -extern void nimble_uart_process(void); extern void os_eventq_run_all(void); extern void os_callout_process(void); -// Hook for pendsv poller to run this periodically every 128ms -#define NIMBLE_TICK(tick) (((tick) & ~(SYSTICK_DISPATCH_NUM_SLOTS - 1) & 0x7f) == 0) - -void nimble_poll(void) { +void mp_bluetooth_hci_poll(void) { if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { return; } - nimble_uart_process(); + mp_bluetooth_nimble_hci_uart_process(); os_callout_process(); os_eventq_run_all(); } -void mod_bluetooth_nimble_poll_wrapper(uint32_t ticks_ms) { - if (NIMBLE_TICK(ticks_ms)) { - pendsv_schedule_dispatch(PENDSV_DISPATCH_NIMBLE, nimble_poll); - } -} - void mp_bluetooth_nimble_port_preinit(void) { MP_STATE_PORT(bluetooth_nimble_memory) = NULL; ble_hci_uart_init(); @@ -70,13 +60,46 @@ void mp_bluetooth_nimble_port_postinit(void) { } void mp_bluetooth_nimble_port_deinit(void) { - #ifdef pyb_pin_BT_REG_ON - mp_hal_pin_low(pyb_pin_BT_REG_ON); - #endif + mp_bluetooth_hci_controller_deactivate(); } void mp_bluetooth_nimble_port_start(void) { ble_hs_start(); } +#if defined(STM32WB) + +#include "rfcore.h" + +void mp_bluetooth_nimble_hci_uart_rx(hal_uart_rx_cb_t rx_cb, void *rx_arg) { + // Protect in case it's called from ble_npl_sem_pend at thread-level + MICROPY_PY_LWIP_ENTER + rfcore_ble_check_msg(rx_cb, rx_arg); + MICROPY_PY_LWIP_EXIT +} + +#else + +#include "uart.h" + +void mp_bluetooth_nimble_hci_uart_rx(hal_uart_rx_cb_t rx_cb, void *rx_arg) { + bool host_wake = mp_bluetooth_hci_controller_woken(); + + while (uart_rx_any(&mp_bluetooth_hci_uart_obj)) { + uint8_t data = uart_rx_char(&mp_bluetooth_hci_uart_obj); + //printf("UART RX: %02x\n", data); + rx_cb(rx_arg, data); + } + + if (host_wake) { + mp_bluetooth_hci_controller_sleep_maybe(); + } +} + +#endif // defined(STM32WB) + +void mp_bluetooth_nimble_hci_uart_tx_strn(const char *str, uint len) { + mp_bluetooth_hci_uart_write((const uint8_t *)str, len); +} + #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/ports/stm32/nimble_hci_uart.c b/ports/stm32/nimble_hci_uart.c deleted file mode 100644 index 11608558780b6..0000000000000 --- a/ports/stm32/nimble_hci_uart.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018-2019 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "py/runtime.h" -#include "py/mphal.h" -#include "extmod/nimble/nimble/hci_uart.h" - -#if MICROPY_BLUETOOTH_NIMBLE - -#if defined(STM32WB) - -/******************************************************************************/ -// HCI over IPCC - -#include "rfcore.h" - -int nimble_hci_uart_configure(uint32_t port) { - (void)port; - return 0; -} - -int nimble_hci_uart_set_baudrate(uint32_t baudrate) { - (void)baudrate; - return 0; -} - -int nimble_hci_uart_activate(void) { - rfcore_ble_init(); - return 0; -} - -void nimble_hci_uart_rx(hal_uart_rx_cb_t rx_cb, void *rx_arg) { - // Protect in case it's called from ble_npl_sem_pend at thread-level - MICROPY_PY_LWIP_ENTER - rfcore_ble_check_msg(rx_cb, rx_arg); - MICROPY_PY_LWIP_EXIT -} - -void nimble_hci_uart_tx_strn(const char *str, uint len) { - MICROPY_PY_LWIP_ENTER - rfcore_ble_hci_cmd(len, (const uint8_t *)str); - MICROPY_PY_LWIP_EXIT -} - -#else - -/******************************************************************************/ -// HCI over UART - -#include "pendsv.h" -#include "uart.h" -#include "drivers/cyw43/cywbt.h" - -pyb_uart_obj_t bt_hci_uart_obj; -static uint8_t hci_uart_rxbuf[512]; - -#ifdef pyb_pin_BT_DEV_WAKE -static uint32_t bt_sleep_ticks; -#endif - -extern void nimble_poll(void); - -mp_obj_t mp_uart_interrupt(mp_obj_t self_in) { - pendsv_schedule_dispatch(PENDSV_DISPATCH_NIMBLE, nimble_poll); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mp_uart_interrupt_obj, mp_uart_interrupt); - -int nimble_hci_uart_set_baudrate(uint32_t baudrate) { - uart_init(&bt_hci_uart_obj, baudrate, UART_WORDLENGTH_8B, UART_PARITY_NONE, UART_STOPBITS_1, UART_HWCONTROL_RTS | UART_HWCONTROL_CTS); - uart_set_rxbuf(&bt_hci_uart_obj, sizeof(hci_uart_rxbuf), hci_uart_rxbuf); - return 0; -} - -int nimble_hci_uart_configure(uint32_t port) { - // bits (8), stop (1), parity (none) and flow (rts/cts) are assumed to match MYNEWT_VAL_BLE_HCI_UART_ constants in syscfg.h. - bt_hci_uart_obj.base.type = &pyb_uart_type; - bt_hci_uart_obj.uart_id = port; - bt_hci_uart_obj.is_static = true; - bt_hci_uart_obj.timeout = 2; - bt_hci_uart_obj.timeout_char = 2; - MP_STATE_PORT(pyb_uart_obj_all)[bt_hci_uart_obj.uart_id - 1] = &bt_hci_uart_obj; - return 0; -} - -int nimble_hci_uart_activate(void) { - // Interrupt on RX chunk received (idle) - // Trigger nimble poll when this happens - mp_obj_t uart_irq_fn = mp_load_attr(MP_OBJ_FROM_PTR(&bt_hci_uart_obj), MP_QSTR_irq); - mp_obj_t uargs[] = { - MP_OBJ_FROM_PTR(&mp_uart_interrupt_obj), - MP_OBJ_NEW_SMALL_INT(UART_FLAG_IDLE), - mp_const_true, - }; - mp_call_function_n_kw(uart_irq_fn, 3, 0, uargs); - - #if MICROPY_PY_NETWORK_CYW43 - cywbt_init(); - cywbt_activate(); - #endif - - return 0; -} - -void nimble_hci_uart_rx(hal_uart_rx_cb_t rx_cb, void *rx_arg) { - #ifdef pyb_pin_BT_HOST_WAKE - int host_wake = 0; - host_wake = mp_hal_pin_read(pyb_pin_BT_HOST_WAKE); - /* - // this is just for info/tracing purposes - static int last_host_wake = 0; - if (host_wake != last_host_wake) { - printf("HOST_WAKE change %d -> %d\n", last_host_wake, host_wake); - last_host_wake = host_wake; - } - */ - #endif - - while (uart_rx_any(&bt_hci_uart_obj)) { - uint8_t data = uart_rx_char(&bt_hci_uart_obj); - //printf("UART RX: %02x\n", data); - rx_cb(rx_arg, data); - } - - #ifdef pyb_pin_BT_DEV_WAKE - if (host_wake == 1 && mp_hal_pin_read(pyb_pin_BT_DEV_WAKE) == 0) { - if (mp_hal_ticks_ms() - bt_sleep_ticks > 500) { - //printf("BT SLEEP\n"); - mp_hal_pin_high(pyb_pin_BT_DEV_WAKE); // let sleep - } - } - #endif -} - -void nimble_hci_uart_tx_strn(const char *str, uint len) { - #ifdef pyb_pin_BT_DEV_WAKE - bt_sleep_ticks = mp_hal_ticks_ms(); - - if (mp_hal_pin_read(pyb_pin_BT_DEV_WAKE) == 1) { - //printf("BT WAKE for TX\n"); - mp_hal_pin_low(pyb_pin_BT_DEV_WAKE); // wake up - // Use delay_us rather than delay_ms to prevent running the scheduler (which - // might result in more BLE operations). - mp_hal_delay_us(5000); // can't go lower than this - } - #endif - - uart_tx_strn(&bt_hci_uart_obj, str, len); -} - -#endif // defined(STM32WB) - -#endif // MICROPY_BLUETOOTH_NIMBLE diff --git a/ports/stm32/pendsv.h b/ports/stm32/pendsv.h index dd3f8f2cb38b8..585f81e8bd441 100644 --- a/ports/stm32/pendsv.h +++ b/ports/stm32/pendsv.h @@ -34,8 +34,8 @@ enum { PENDSV_DISPATCH_CYW43, #endif #endif - #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE - PENDSV_DISPATCH_NIMBLE, + #if MICROPY_PY_BLUETOOTH + PENDSV_DISPATCH_BLUETOOTH_HCI, #endif PENDSV_DISPATCH_MAX }; diff --git a/ports/stm32/systick.h b/ports/stm32/systick.h index a70f03e176a30..6aef3f2e4e731 100644 --- a/ports/stm32/systick.h +++ b/ports/stm32/systick.h @@ -37,8 +37,8 @@ enum { #if MICROPY_PY_NETWORK && MICROPY_PY_LWIP SYSTICK_DISPATCH_LWIP, #endif - #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE - SYSTICK_DISPATCH_NIMBLE, + #if MICROPY_PY_BLUETOOTH + SYSTICK_DISPATCH_BLUETOOTH_HCI, #endif SYSTICK_DISPATCH_MAX }; From 0ac06a510af8290e2c5e3a1f43c87d232e460b9b Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:37:32 +1100 Subject: [PATCH 04/15] extmod/modbluetooth: Extract out gatts_db functionality from nimble. For use by other stacks, if they need it. Work done in collaboration with Jim Mussared aka @jimmo. --- extmod/modbluetooth.c | 62 +++++++++++++++++++++ extmod/modbluetooth.h | 36 ++++++++++++- extmod/nimble/modbluetooth_nimble.c | 83 +++++------------------------ extmod/nimble/modbluetooth_nimble.h | 4 +- 4 files changed, 113 insertions(+), 72 deletions(-) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 1d8a73848fef2..75fbeedb508b6 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -1088,4 +1088,66 @@ bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_han } #endif +void mp_bluetooth_gatts_db_create_entry(mp_gatts_db_t db, uint16_t handle, size_t len) { + mp_map_elem_t *elem = mp_map_lookup(db, MP_OBJ_NEW_SMALL_INT(handle), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + mp_bluetooth_gatts_db_entry_t *entry = m_new(mp_bluetooth_gatts_db_entry_t, 1); + entry->data = m_new(uint8_t, len); + entry->data_alloc = len; + entry->data_len = 0; + entry->append = false; + elem->value = MP_OBJ_FROM_PTR(entry); +} + +mp_bluetooth_gatts_db_entry_t *mp_bluetooth_gatts_db_lookup(mp_gatts_db_t db, uint16_t handle) { + mp_map_elem_t *elem = mp_map_lookup(db, MP_OBJ_NEW_SMALL_INT(handle), MP_MAP_LOOKUP); + if (!elem) { + return NULL; + } + return MP_OBJ_TO_PTR(elem->value); +} + +int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, uint8_t **value, size_t *value_len) { + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle); + if (!entry) { + return MP_EINVAL; + } + + *value = entry->data; + *value_len = entry->data_len; + if (entry->append) { + entry->data_len = 0; + } + + return 0; +} + +int mp_bluetooth_gatts_db_write(mp_gatts_db_t db, uint16_t handle, const uint8_t *value, size_t value_len) { + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle); + if (!entry) { + return MP_EINVAL; + } + + if (value_len > entry->data_alloc) { + entry->data = m_new(uint8_t, value_len); + entry->data_alloc = value_len; + } + + memcpy(entry->data, value, value_len); + entry->data_len = value_len; + + return 0; +} + +int mp_bluetooth_gatts_db_resize(mp_gatts_db_t db, uint16_t handle, size_t len, bool append) { + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle); + if (!entry) { + return MP_EINVAL; + } + entry->data = m_renew(uint8_t, entry->data, entry->data_alloc, len); + entry->data_alloc = len; + entry->data_len = 0; + entry->append = append; + return 0; +} + #endif // MICROPY_PY_BLUETOOTH diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index 5e4e2c25a68bb..661130802a319 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -54,10 +54,12 @@ #endif // Common constants. -#ifndef MP_BLUETOOTH_MAX_ATTR_SIZE -#define MP_BLUETOOTH_MAX_ATTR_SIZE (20) +#ifndef MP_BLUETOOTH_DEFAULT_ATTR_LEN +#define MP_BLUETOOTH_DEFAULT_ATTR_LEN (20) #endif +#define MP_BLUETOOTH_CCCB_LEN (2) + // Advertisement packet lengths #define MP_BLUETOOTH_GAP_ADV_MAX_LEN (32) @@ -267,4 +269,34 @@ void mp_bluetooth_gattc_on_data_available_end(void); void mp_bluetooth_gattc_on_write_status(uint16_t conn_handle, uint16_t value_handle, uint16_t status); #endif +// For stacks that don't manage attribute value data (currently all of them), helpers +// to store this in a map, keyed by value handle. + +typedef struct { + // Pointer to heap-allocated data. + uint8_t *data; + // Allocated size of data. + size_t data_alloc; + // Current bytes in use. + size_t data_len; + // Whether new writes append or replace existing data (default false). + bool append; +} mp_bluetooth_gatts_db_entry_t; + +typedef mp_map_t *mp_gatts_db_t; + +STATIC inline void mp_bluetooth_gatts_db_create(mp_gatts_db_t *db) { + *db = m_new(mp_map_t, 1); +} + +STATIC inline void mp_bluetooth_gatts_db_reset(mp_gatts_db_t db) { + mp_map_init(db, 0); +} + +void mp_bluetooth_gatts_db_create_entry(mp_gatts_db_t db, uint16_t handle, size_t len); +mp_bluetooth_gatts_db_entry_t *mp_bluetooth_gatts_db_lookup(mp_gatts_db_t db, uint16_t handle); +int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, uint8_t **value, size_t *value_len); +int mp_bluetooth_gatts_db_write(mp_gatts_db_t db, uint16_t handle, const uint8_t *value, size_t value_len); +int mp_bluetooth_gatts_db_resize(mp_gatts_db_t db, uint16_t handle, size_t len, bool append); + #endif // MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_H diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index f7602c35fd1f2..8ef7c6f31d809 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -153,17 +153,6 @@ STATIC ble_addr_t create_nimble_addr(uint8_t addr_type, const uint8_t *addr) { #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -typedef struct { - // Pointer to heap-allocated data. - uint8_t *data; - // Allocated size of data. - size_t data_alloc; - // Current bytes in use. - size_t data_len; - // Whether new writes append or replace existing data (default false). - bool append; -} gatts_db_entry_t; - volatile int mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; STATIC void reset_cb(int reason) { @@ -195,8 +184,8 @@ STATIC void sync_cb(void) { assert(rc == 0); } - if (MP_BLUETOOTH_MAX_ATTR_SIZE > 20) { - rc = ble_att_set_preferred_mtu(MP_BLUETOOTH_MAX_ATTR_SIZE + 3); + if (MP_BLUETOOTH_DEFAULT_ATTR_LEN > 20) { + rc = ble_att_set_preferred_mtu(MP_BLUETOOTH_DEFAULT_ATTR_LEN + 3); assert(rc == 0); } @@ -205,16 +194,6 @@ STATIC void sync_cb(void) { mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE; } -STATIC void create_gatts_db_entry(uint16_t handle) { - mp_map_elem_t *elem = mp_map_lookup(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, MP_OBJ_NEW_SMALL_INT(handle), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); - gatts_db_entry_t *entry = m_new(gatts_db_entry_t, 1); - entry->data = m_new(uint8_t, MP_BLUETOOTH_MAX_ATTR_SIZE); - entry->data_alloc = MP_BLUETOOTH_MAX_ATTR_SIZE; - entry->data_len = 0; - entry->append = false; - elem->value = MP_OBJ_FROM_PTR(entry); -} - STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) { switch (ctxt->op) { case BLE_GATT_REGISTER_OP_SVC: @@ -232,7 +211,7 @@ STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) { // Allocate the gatts_db storage for this characteristic. // Although this function is a callback, it's called synchronously from ble_hs_sched_start/ble_gatts_start, so safe to allocate. - create_gatts_db_entry(ctxt->chr.val_handle); + mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, ctxt->chr.val_handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN); break; case BLE_GATT_REGISTER_OP_DSC: @@ -241,7 +220,7 @@ STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) { DEBUG_EVENT_printf("gatts_register_cb: dsc uuid=%p handle=%d\n", &ctxt->dsc.dsc_def->uuid, ctxt->dsc.handle); // See above, safe to alloc. - create_gatts_db_entry(ctxt->dsc.handle); + mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, ctxt->dsc.handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN); // Unlike characteristics, we have to manually provide a way to get the handle back to the register method. *((uint16_t *)ctxt->dsc.dsc_def->arg) = ctxt->dsc.handle; @@ -291,7 +270,7 @@ int mp_bluetooth_init(void) { ble_hs_cfg.store_status_cb = ble_store_util_status_rr; MP_STATE_PORT(bluetooth_nimble_root_pointers) = m_new0(mp_bluetooth_nimble_root_pointers_t, 1); - MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db = m_new(mp_map_t, 1); + mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db); mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_STARTING; @@ -408,8 +387,7 @@ void mp_bluetooth_gap_advertise_stop(void) { static int characteristic_access_cb(uint16_t conn_handle, uint16_t value_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { DEBUG_EVENT_printf("characteristic_access_cb: conn_handle=%u value_handle=%u op=%u\n", conn_handle, value_handle, ctxt->op); - mp_map_elem_t *elem; - gatts_db_entry_t *entry; + mp_bluetooth_gatts_db_entry_t *entry; switch (ctxt->op) { case BLE_GATT_ACCESS_OP_READ_CHR: case BLE_GATT_ACCESS_OP_READ_DSC: @@ -420,22 +398,20 @@ static int characteristic_access_cb(uint16_t conn_handle, uint16_t value_handle, } #endif - elem = mp_map_lookup(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, MP_OBJ_NEW_SMALL_INT(value_handle), MP_MAP_LOOKUP); - if (!elem) { + entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, value_handle); + if (!entry) { return BLE_ATT_ERR_ATTR_NOT_FOUND; } - entry = MP_OBJ_TO_PTR(elem->value); os_mbuf_append(ctxt->om, entry->data, entry->data_len); return 0; case BLE_GATT_ACCESS_OP_WRITE_CHR: case BLE_GATT_ACCESS_OP_WRITE_DSC: - elem = mp_map_lookup(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, MP_OBJ_NEW_SMALL_INT(value_handle), MP_MAP_LOOKUP); - if (!elem) { + entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, value_handle); + if (!entry) { return BLE_ATT_ERR_ATTR_NOT_FOUND; } - entry = MP_OBJ_TO_PTR(elem->value); size_t offset = 0; if (entry->append) { @@ -458,7 +434,7 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { } // Reset the gatt characteristic value db. - mp_map_init(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, 0); + mp_bluetooth_gatts_db_reset(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db); // By default, just register the default gap service. ble_svc_gap_init(); @@ -551,33 +527,11 @@ int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { } int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { - mp_map_elem_t *elem = mp_map_lookup(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, MP_OBJ_NEW_SMALL_INT(value_handle), MP_MAP_LOOKUP); - if (!elem) { - return MP_EINVAL; - } - gatts_db_entry_t *entry = MP_OBJ_TO_PTR(elem->value); - *value = entry->data; - *value_len = entry->data_len; - if (entry->append) { - entry->data_len = 0; - } - return 0; + return mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, value_handle, value, value_len); } int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len) { - mp_map_elem_t *elem = mp_map_lookup(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, MP_OBJ_NEW_SMALL_INT(value_handle), MP_MAP_LOOKUP); - if (!elem) { - return MP_EINVAL; - } - gatts_db_entry_t *entry = MP_OBJ_TO_PTR(elem->value); - if (value_len > entry->data_alloc) { - entry->data = m_new(uint8_t, value_len); - entry->data_alloc = value_len; - } - - memcpy(entry->data, value, value_len); - entry->data_len = value_len; - return 0; + return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, value_handle, value, value_len); } // TODO: Could use ble_gatts_chr_updated to send to all subscribed centrals. @@ -602,16 +556,7 @@ int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { } int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { - mp_map_elem_t *elem = mp_map_lookup(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, MP_OBJ_NEW_SMALL_INT(value_handle), MP_MAP_LOOKUP); - if (!elem) { - return MP_EINVAL; - } - gatts_db_entry_t *entry = MP_OBJ_TO_PTR(elem->value); - entry->data = m_renew(uint8_t, entry->data, entry->data_alloc, len); - entry->data_alloc = len; - entry->data_len = 0; - entry->append = append; - return 0; + return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, value_handle, len, append); } #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE diff --git a/extmod/nimble/modbluetooth_nimble.h b/extmod/nimble/modbluetooth_nimble.h index c70c3bc90ae63..745ff968255dc 100644 --- a/extmod/nimble/modbluetooth_nimble.h +++ b/extmod/nimble/modbluetooth_nimble.h @@ -27,11 +27,13 @@ #ifndef MICROPY_INCLUDED_EXTMOD_NIMBLE_MODBLUETOOTH_NIMBLE_H #define MICROPY_INCLUDED_EXTMOD_NIMBLE_MODBLUETOOTH_NIMBLE_H +#include "extmod/modbluetooth.h" + #define MP_BLUETOOTH_NIMBLE_MAX_SERVICES (8) typedef struct _mp_bluetooth_nimble_root_pointers_t { // Characteristic (and descriptor) value storage. - mp_map_t *gatts_db; + mp_gatts_db_t gatts_db; // Pending service definitions. size_t n_services; From d35fefe30ac70a5f1c74e85f06ddbad2177f808a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:39:50 +1100 Subject: [PATCH 05/15] lib: Add BlueKitchen BTstack submodule. --- .gitmodules | 3 +++ lib/btstack | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/btstack diff --git a/.gitmodules b/.gitmodules index 3880ea60a0df2..9d07e70449d47 100644 --- a/.gitmodules +++ b/.gitmodules @@ -30,3 +30,6 @@ [submodule "lib/mynewt-nimble"] path = lib/mynewt-nimble url = https://github.com/apache/mynewt-nimble.git +[submodule "lib/btstack"] + path = lib/btstack + url = https://github.com/bluekitchen/btstack.git diff --git a/lib/btstack b/lib/btstack new file mode 160000 index 0000000000000..a6121b51b229b --- /dev/null +++ b/lib/btstack @@ -0,0 +1 @@ +Subproject commit a6121b51b229b883431c4a1600b0f46db31d32dc From fbefcef1df377e9205392f8656ae70eab00d9559 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:40:55 +1100 Subject: [PATCH 06/15] extmod/btstack: Add empty modbluetooth implementation. Work done in collaboration with Jim Mussared aka @jimmo. --- extmod/btstack/btstack.mk | 48 ++++++++++ extmod/btstack/btstack_config.h | 44 +++++++++ extmod/btstack/modbluetooth_btstack.c | 128 ++++++++++++++++++++++++++ extmod/btstack/modbluetooth_btstack.h | 30 ++++++ 4 files changed, 250 insertions(+) create mode 100644 extmod/btstack/btstack.mk create mode 100644 extmod/btstack/btstack_config.h create mode 100644 extmod/btstack/modbluetooth_btstack.c create mode 100644 extmod/btstack/modbluetooth_btstack.h diff --git a/extmod/btstack/btstack.mk b/extmod/btstack/btstack.mk new file mode 100644 index 0000000000000..0a6876b22762e --- /dev/null +++ b/extmod/btstack/btstack.mk @@ -0,0 +1,48 @@ +# Makefile directives for BlueKitchen BTstack + +ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) + +BTSTACK_EXTMOD_DIR = extmod/btstack + +EXTMOD_SRC_C += extmod/btstack/modbluetooth_btstack.c + +INC += -I$(TOP)/$(BTSTACK_EXTMOD_DIR) + +CFLAGS_MOD += -DMICROPY_BLUETOOTH_BTSTACK=1 + +BTSTACK_DIR = $(TOP)/lib/btstack + +include $(BTSTACK_DIR)/src/Makefile.inc +include $(BTSTACK_DIR)/src/ble/Makefile.inc + +INC += -I$(BTSTACK_DIR)/src +INC += -I$(BTSTACK_DIR)/3rd-party/bluedroid/decoder/include +INC += -I$(BTSTACK_DIR)/3rd-party/bluedroid/encoder/include +INC += -I$(BTSTACK_DIR)/3rd-party/md5 +INC += -I$(BTSTACK_DIR)/3rd-party/yxml + +SRC_BTSTACK = \ + $(addprefix lib/btstack/src/, $(SRC_FILES)) \ + $(addprefix lib/btstack/src/ble/, $(filter-out %_tlv.c, $(SRC_BLE_FILES))) \ + lib/btstack/platform/embedded/btstack_run_loop_embedded.c \ + +ifeq ($MICROPY_BLUETOOTH_BTSTACK_ENABLE_CLASSIC,1) +include $(BTSTACK_DIR)/src/classic/Makefile.inc +SRC_BTSTACK += \ + $(addprefix lib/btstack/src/classic/, $(SRC_CLASSIC_FILES)) +endif + +SRC_LIB += $(SRC_BTSTACK) + +#$(BUILD)/lib/btstack/src/classic/btstack_link_key_db_static.o: CFLAGS += -Wno-error=pointer-arith + +# Incorrect %u, should be %lu. +$(BUILD)/lib/btstack/src/classic/a2dp_source.o: CFLAGS += -Wno-error=format= +$(BUILD)/lib/btstack/src/classic/btstack_sbc_decoder_bluedroid.o: CFLAGS += -Wno-error=format= +$(BUILD)/lib/btstack/src/classic/btstack_link_key_db_tlv.o: CFLAGS += -Wno-error=format= +$(BUILD)/lib/btstack/src/classic/goep_client.o: CFLAGS += -Wno-error=format= +$(BUILD)/lib/btstack/src/ble/le_device_db_tlv.o: CFLAGS += -Wno-error=format= + + + +endif diff --git a/extmod/btstack/btstack_config.h b/extmod/btstack/btstack_config.h new file mode 100644 index 0000000000000..0976bbe7284df --- /dev/null +++ b/extmod/btstack/btstack_config.h @@ -0,0 +1,44 @@ +#ifndef MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H +#define MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H + +// BTstack features that can be enabled +#define ENABLE_BLE +#define ENABLE_LE_PERIPHERAL +#define ENABLE_LE_CENTRAL +// #define ENABLE_CLASSIC +#define ENABLE_LE_DATA_CHANNELS +// #define ENABLE_LOG_INFO +#define ENABLE_LOG_ERROR + +// BTstack configuration. buffers, sizes, ... +#define HCI_ACL_PAYLOAD_SIZE 1021 +#define MAX_NR_GATT_CLIENTS 1 +#define MAX_NR_HCI_CONNECTIONS 1 +#define MAX_NR_L2CAP_SERVICES 3 +#define MAX_NR_L2CAP_CHANNELS 3 +#define MAX_NR_RFCOMM_MULTIPLEXERS 1 +#define MAX_NR_RFCOMM_SERVICES 1 +#define MAX_NR_RFCOMM_CHANNELS 1 +#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 2 +#define MAX_NR_BNEP_SERVICES 1 +#define MAX_NR_BNEP_CHANNELS 1 +#define MAX_NR_HFP_CONNECTIONS 1 +#define MAX_NR_WHITELIST_ENTRIES 1 +#define MAX_NR_SM_LOOKUP_ENTRIES 3 +#define MAX_NR_SERVICE_RECORD_ITEMS 1 +#define MAX_NR_AVDTP_STREAM_ENDPOINTS 1 +#define MAX_NR_AVDTP_CONNECTIONS 1 +#define MAX_NR_AVRCP_CONNECTIONS 1 + +#define MAX_NR_LE_DEVICE_DB_ENTRIES 4 + +// Link Key DB and LE Device DB using TLV on top of Flash Sector interface +// #define NVM_NUM_DEVICE_DB_ENTRIES 16 + +// We don't give btstack a malloc, so use a fixed-size ATT DB. +#define MAX_ATT_DB_SIZE 512 + +// BTstack HAL configuration +#define HAVE_EMBEDDED_TIME_MS + +#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c new file mode 100644 index 0000000000000..73f28ffad39ba --- /dev/null +++ b/extmod/btstack/modbluetooth_btstack.c @@ -0,0 +1,128 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK + +#include "extmod/btstack/modbluetooth_btstack.h" +#include "extmod/modbluetooth.h" + +int mp_bluetooth_init(void) { + return 0; +} + +void mp_bluetooth_deinit(void) { +} + +bool mp_bluetooth_is_enabled(void) { + return false; +} + +void mp_bluetooth_get_device_addr(uint8_t *addr) { + mp_hal_get_mac(MP_HAL_MAC_BDADDR, addr); +} + +int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) { + return 0; +} + +void mp_bluetooth_gap_advertise_stop(void) { +} + +int mp_bluetooth_gatts_register_service_begin(bool append) { + return 0; +} + +int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, mp_obj_bluetooth_uuid_t **characteristic_uuids, uint8_t *characteristic_flags, mp_obj_bluetooth_uuid_t **descriptor_uuids, uint8_t *descriptor_flags, uint8_t *num_descriptors, uint16_t *handles, size_t num_characteristics) { + return 0; +} + +int mp_bluetooth_gatts_register_service_end(void) { + return 0; +} + +int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { + return 0; +} + +int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len) { + return 0; +} + +int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { + return 0; +} + +int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len) { + return 0; +} + +int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { + return 0; +} + +int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { + return 0; +} + +int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { + return 0; +} + +#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us) { + return 0; +} + +int mp_bluetooth_gap_scan_stop(void) { + return 0; +} + +int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms) { + return 0; +} + +int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle) { + return 0; +} + +int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { + return 0; +} + +int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { + return 0; +} + +int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { + return 0; +} + +int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) { + return 0; +} +#endif + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/extmod/btstack/modbluetooth_btstack.h b/extmod/btstack/modbluetooth_btstack.h new file mode 100644 index 0000000000000..f58d3074807cc --- /dev/null +++ b/extmod/btstack/modbluetooth_btstack.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H +#define MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H + +#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H From 0e95815bfbf9e0c1ca87437536c848e14f5dc6cf Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:41:34 +1100 Subject: [PATCH 07/15] stm32: Add bindings for BTstack implementation. Work done in collaboration with Jim Mussared aka @jimmo. --- ports/stm32/Makefile | 11 +++ ports/stm32/btstack.c | 222 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 ports/stm32/btstack.c diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index fab2f621b22a2..2dc740561816f 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -462,11 +462,22 @@ ifeq ($(MICROPY_PY_BLUETOOTH),1) SRC_C += modbluetooth_hci.c +ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) +ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) +$(error Cannot enable both NimBLE and BTstack at the same time) +endif +endif + ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) include $(TOP)/extmod/nimble/nimble.mk SRC_C += nimble.c endif +ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) +include $(TOP)/extmod/btstack/btstack.mk +SRC_C += btstack.c +endif + ifeq ($(MICROPY_PY_NETWORK_CYW43),1) DRIVERS_SRC_C += drivers/cyw43/cywbt.c endif diff --git a/ports/stm32/btstack.c b/ports/stm32/btstack.c new file mode 100644 index 0000000000000..f7669d1fc6447 --- /dev/null +++ b/ports/stm32/btstack.c @@ -0,0 +1,222 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK + +#include "lib/btstack/src/btstack.h" +#include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" +#include "lib/btstack/platform/embedded/hal_cpu.h" +#include "lib/btstack/platform/embedded/hal_time_ms.h" + +#include "extmod/modbluetooth_hci.h" +#include "extmod/btstack/modbluetooth_btstack.h" + +// We pass the bytes directly to the UART during a send, but then notify btstack in the next poll. +STATIC bool send_done; +STATIC void (*send_handler)(void); + +// btstack issues a read of len bytes, and gives us a buffer to asynchronously fill up. +STATIC uint8_t *recv_buf; +STATIC size_t recv_len; +STATIC size_t recv_idx; +STATIC void (*recv_handler)(void); + +// The IRQ functionality in btstack_run_loop_embedded.c is not used, so the +// following three functions are empty. + +void hal_cpu_disable_irqs(void) { +} + +void hal_cpu_enable_irqs(void) { +} + +void hal_cpu_enable_irqs_and_sleep(void) { +} + +uint32_t hal_time_ms(void) { + return mp_hal_ticks_ms(); +} + +STATIC int btstack_uart_init(const btstack_uart_config_t *uart_config) { + send_done = false; + recv_len = 0; + recv_idx = 0; + recv_handler = NULL; + send_handler = NULL; + + // Set up the UART periperhal. + mp_bluetooth_hci_uart_init(MICROPY_HW_BLE_UART_ID); + + return 0; +} + +STATIC int btstack_uart_open(void) { + // Attach IRQ and power up the HCI controller. + mp_bluetooth_hci_uart_activate(); + return 0; +} + +STATIC int btstack_uart_close(void) { + return 0; +} + +STATIC void btstack_uart_set_block_received(void (*block_handler)(void)) { + recv_handler = block_handler; +} + +STATIC void btstack_uart_set_block_sent(void (*block_handler)(void)) { + send_handler = block_handler; +} + +STATIC int btstack_uart_set_baudrate(uint32_t baudrate) { + mp_bluetooth_hci_uart_set_baudrate(baudrate); + return 0; +} + +STATIC int btstack_uart_set_parity(int parity) { + return 0; +} + +STATIC int btstack_uart_set_flowcontrol(int flowcontrol) { + return 0; +} + +STATIC void btstack_uart_receive_block(uint8_t *buf, uint16_t len) { + recv_buf = buf; + recv_len = len; +} + +STATIC void btstack_uart_send_block(const uint8_t *buf, uint16_t len) { + mp_bluetooth_hci_uart_write(buf, len); + send_done = true; +} + +STATIC int btstack_uart_get_supported_sleep_modes(void) { + return 0; +} + +STATIC void btstack_uart_set_sleep(btstack_uart_sleep_mode_t sleep_mode) { + // printf("btstack_uart_set_sleep %u\n", sleep_mode); +} + +STATIC void btstack_uart_set_wakeup_handler(void (*wakeup_handler)(void)) { + // printf("btstack_uart_set_wakeup_handler\n"); +} + +STATIC const btstack_uart_block_t btstack_uart_block = { + &btstack_uart_init, + &btstack_uart_open, + &btstack_uart_close, + &btstack_uart_set_block_received, + &btstack_uart_set_block_sent, + &btstack_uart_set_baudrate, + &btstack_uart_set_parity, + &btstack_uart_set_flowcontrol, + &btstack_uart_receive_block, + &btstack_uart_send_block, + &btstack_uart_get_supported_sleep_modes, + &btstack_uart_set_sleep, + &btstack_uart_set_wakeup_handler, +}; + +STATIC const hci_transport_config_uart_t hci_transport_config_uart = { + HCI_TRANSPORT_CONFIG_UART, + MICROPY_HW_BLE_UART_BAUDRATE, + 3000000, + 0, + NULL, +}; + +STATIC void btstack_uart_process(void) { + if (send_done) { + // If we'd done a TX in the last interval, notify btstack that it's complete. + send_done = false; + if (send_handler) { + send_handler(); + } + } + + // Append any new bytes to the recv buffer, notifying bstack if we've got + // the number of bytes it was looking for. + while (uart_rx_any(&mp_bluetooth_hci_uart_obj) && recv_idx < recv_len) { + recv_buf[recv_idx++] = uart_rx_char(&mp_bluetooth_hci_uart_obj); + if (recv_idx == recv_len) { + recv_idx = 0; + recv_len = 0; + if (recv_handler) { + recv_handler(); + } + } + } +} + +void mp_bluetooth_hci_poll(void) { + if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_OFF) { + return; + } + + // Process uart data. + bool host_wake = mp_bluetooth_hci_controller_woken(); + btstack_uart_process(); + if (host_wake) { + mp_bluetooth_hci_controller_sleep_maybe(); + } + + // Call the BTstack run loop. + btstack_run_loop_embedded_execute_once(); +} + +void mp_bluetooth_btstack_port_init(void) { + static bool run_loop_init = false; + if (!run_loop_init) { + run_loop_init = true; + btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); + } else { + btstack_run_loop_embedded_get_instance()->init(); + } + + // hci_dump_open(NULL, HCI_DUMP_STDOUT); + const hci_transport_t *transport = hci_transport_h4_instance(&btstack_uart_block); + hci_init(transport, &hci_transport_config_uart); + + // TODO: Probably not necessary for BCM (we have our own firmware loader), + // but might be worth investigating for other controllers in the future. + // hci_set_chipset(btstack_chipset_bcm_instance()); +} + +void mp_bluetooth_btstack_port_deinit(void) { + hci_power_control(HCI_POWER_OFF); +} + +void mp_bluetooth_btstack_port_start(void) { + hci_power_control(HCI_POWER_ON); +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK From 51f8591097cc58bfcb0449fccb5048db7e5473dc Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:42:15 +1100 Subject: [PATCH 08/15] stm32/boards/PYBD: Allow building with BTstack (via make command line). Work done in collaboration with Jim Mussared aka @jimmo. --- ports/stm32/boards/PYBD_SF2/mpconfigboard.mk | 5 +++-- ports/stm32/boards/PYBD_SF3/mpconfigboard.mk | 5 +++-- ports/stm32/boards/PYBD_SF6/mpconfigboard.mk | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ports/stm32/boards/PYBD_SF2/mpconfigboard.mk b/ports/stm32/boards/PYBD_SF2/mpconfigboard.mk index 69407345bd13a..ead8bb8c07cf3 100644 --- a/ports/stm32/boards/PYBD_SF2/mpconfigboard.mk +++ b/ports/stm32/boards/PYBD_SF2/mpconfigboard.mk @@ -10,8 +10,9 @@ TEXT0_SECTIONS = .isr_vector .text .data TEXT1_SECTIONS = .text_ext # MicroPython settings -MICROPY_PY_BLUETOOTH = 1 -MICROPY_BLUETOOTH_NIMBLE = 1 +MICROPY_PY_BLUETOOTH ?= 1 +MICROPY_BLUETOOTH_NIMBLE ?= 1 +MICROPY_BLUETOOTH_BTSTACK ?= 0 MICROPY_PY_LWIP = 1 MICROPY_PY_NETWORK_CYW43 = 1 MICROPY_PY_USSL = 1 diff --git a/ports/stm32/boards/PYBD_SF3/mpconfigboard.mk b/ports/stm32/boards/PYBD_SF3/mpconfigboard.mk index f6d6e955b616b..119ec8f825b42 100644 --- a/ports/stm32/boards/PYBD_SF3/mpconfigboard.mk +++ b/ports/stm32/boards/PYBD_SF3/mpconfigboard.mk @@ -10,8 +10,9 @@ TEXT0_SECTIONS = .isr_vector .text .data TEXT1_SECTIONS = .text_ext # MicroPython settings -MICROPY_PY_BLUETOOTH = 1 -MICROPY_BLUETOOTH_NIMBLE = 1 +MICROPY_PY_BLUETOOTH ?= 1 +MICROPY_BLUETOOTH_NIMBLE ?= 1 +MICROPY_BLUETOOTH_BTSTACK ?= 0 MICROPY_PY_LWIP = 1 MICROPY_PY_NETWORK_CYW43 = 1 MICROPY_PY_USSL = 1 diff --git a/ports/stm32/boards/PYBD_SF6/mpconfigboard.mk b/ports/stm32/boards/PYBD_SF6/mpconfigboard.mk index e33d62d86a931..ddc176e9cd422 100644 --- a/ports/stm32/boards/PYBD_SF6/mpconfigboard.mk +++ b/ports/stm32/boards/PYBD_SF6/mpconfigboard.mk @@ -7,8 +7,9 @@ LD_FILES = boards/PYBD_SF6/f767.ld TEXT0_ADDR = 0x08008000 # MicroPython settings -MICROPY_PY_BLUETOOTH = 1 -MICROPY_BLUETOOTH_NIMBLE = 1 +MICROPY_PY_BLUETOOTH ?= 1 +MICROPY_BLUETOOTH_NIMBLE ?= 1 +MICROPY_BLUETOOTH_BTSTACK ?= 0 MICROPY_PY_LWIP = 1 MICROPY_PY_NETWORK_CYW43 = 1 MICROPY_PY_USSL = 1 From 0674917bc53d0a7346715d2af508a074ebf26c24 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:43:01 +1100 Subject: [PATCH 09/15] extmod/btstack: Implement advertising. Work done in collaboration with Jim Mussared aka @jimmo. --- extmod/btstack/modbluetooth_btstack.c | 68 ++++++++++++++++++++++++++- extmod/btstack/modbluetooth_btstack.h | 19 ++++++++ ports/stm32/mpconfigport.h | 10 +++- 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 73f28ffad39ba..3e3852e5c2e39 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -24,20 +24,52 @@ * THE SOFTWARE. */ +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" + #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK #include "extmod/btstack/modbluetooth_btstack.h" #include "extmod/modbluetooth.h" +#include "lib/btstack/src/btstack.h" + +volatile int mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; + int mp_bluetooth_init(void) { + btstack_memory_init(); + + MP_STATE_PORT(bluetooth_btstack_root_pointers) = m_new0(mp_bluetooth_btstack_root_pointers_t, 1); + + mp_bluetooth_btstack_port_init(); + mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_STARTING; + + l2cap_init(); + le_device_db_init(); + sm_init(); + + // Set blank ER/IR keys to suppress BTstack warning. + // TODO handle this correctly. + sm_key_t dummy_key; + memset(dummy_key, 0, sizeof(dummy_key)); + sm_set_er(dummy_key); + sm_set_ir(dummy_key); + + mp_bluetooth_btstack_port_start(); + mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_ACTIVE; return 0; } void mp_bluetooth_deinit(void) { + mp_bluetooth_btstack_port_deinit(); + mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; + + MP_STATE_PORT(bluetooth_btstack_root_pointers) = NULL; } bool mp_bluetooth_is_enabled(void) { - return false; + return mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE; } void mp_bluetooth_get_device_addr(uint8_t *addr) { @@ -45,10 +77,44 @@ void mp_bluetooth_get_device_addr(uint8_t *addr) { } int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) { + uint16_t adv_int_min = interval_us / 625; + uint16_t adv_int_max = interval_us / 625; + uint8_t adv_type = connectable ? 0 : 2; + bd_addr_t null_addr = {0}; + + uint8_t direct_address_type = 0; + uint8_t channel_map = 0x07; // Use all three broadcast channels. + uint8_t filter_policy = 0x00; // None. + + gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, direct_address_type, null_addr, channel_map, filter_policy); + + // Copy the adv_data and sr_data into a persistent buffer (which is findable via the btstack root pointers). + size_t total_bytes = adv_data_len + sr_data_len; + if (total_bytes > MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data_alloc) { + // Resize if necessary. + MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data = m_new(uint8_t, total_bytes); + MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data_alloc = total_bytes; + } + uint8_t *data = MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data; + + if (adv_data) { + memcpy(data, (uint8_t *)adv_data, adv_data_len); + gap_advertisements_set_data(adv_data_len, data); + data += adv_data_len; + } + if (sr_data) { + memcpy(data, (uint8_t *)sr_data, sr_data_len); + gap_scan_response_set_data(sr_data_len, data); + } + + gap_advertisements_enable(true); return 0; } void mp_bluetooth_gap_advertise_stop(void) { + gap_advertisements_enable(false); + MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data_alloc = 0; + MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data = NULL; } int mp_bluetooth_gatts_register_service_begin(bool append) { diff --git a/extmod/btstack/modbluetooth_btstack.h b/extmod/btstack/modbluetooth_btstack.h index f58d3074807cc..9e8c6ad3fbc2b 100644 --- a/extmod/btstack/modbluetooth_btstack.h +++ b/extmod/btstack/modbluetooth_btstack.h @@ -27,4 +27,23 @@ #ifndef MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H #define MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H +typedef struct _mp_bluetooth_btstack_root_pointers_t { + // This stores both the advertising data and the scan response data, concatenated together. + uint8_t *adv_data; + // Total length of both. + size_t adv_data_alloc; +} mp_bluetooth_btstack_root_pointers_t; + +enum { + MP_BLUETOOTH_BTSTACK_STATE_OFF, + MP_BLUETOOTH_BTSTACK_STATE_STARTING, + MP_BLUETOOTH_BTSTACK_STATE_ACTIVE, +}; + +extern volatile int mp_bluetooth_btstack_state; + +void mp_bluetooth_btstack_port_init(void); +void mp_bluetooth_btstack_port_deinit(void); +void mp_bluetooth_btstack_port_start(void); + #endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index ada4ce5af80a1..95442b6476b5b 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -269,6 +269,13 @@ struct _mp_bluetooth_nimble_root_pointers_t; #define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE #endif +#if MICROPY_BLUETOOTH_BTSTACK +struct _mp_bluetooth_btstack_root_pointers_t; +#define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_BTSTACK void **bluetooth_nimble_memory; struct _mp_bluetooth_btstack_root_pointers_t *bluetooth_btstack_root_pointers; +#else +#define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_BTSTACK +#endif + #define MICROPY_PORT_ROOT_POINTERS \ const char *readline_hist[8]; \ \ @@ -301,7 +308,8 @@ struct _mp_bluetooth_nimble_root_pointers_t; mp_obj_list_t mod_network_nic_list; \ \ MICROPY_PORT_ROOT_POINTER_MBEDTLS \ - MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE \ + MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE \ + MICROPY_PORT_ROOT_POINTER_BLUETOOTH_BTSTACK \ // type definitions for the specific machine From 86c26db3ff40b87a8d058a67ac5c71f71a3818b6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:43:28 +1100 Subject: [PATCH 10/15] extmod/btstack: Implement service registration. Work done in collaboration with Jim Mussared aka @jimmo. --- extmod/btstack/modbluetooth_btstack.c | 122 ++++++++++++++++++++++++++ extmod/modbluetooth.h | 1 + 2 files changed, 123 insertions(+) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 3e3852e5c2e39..e7f9014048c48 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -37,6 +37,41 @@ volatile int mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; +static btstack_packet_callback_registration_t hci_event_callback_registration; + +STATIC void att_server_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + // printf("att_server_packet_handler: packet_type: %u, channel: %u, packet: %p, size: %u\n", packet_type, channel, packet, size); + if (packet_type == HCI_EVENT_PACKET) { + uint8_t event_type = hci_event_packet_get_type(packet); + if (event_type == ATT_EVENT_CONNECTED) { + printf(" --> att connected\n"); + } else if (event_type == ATT_EVENT_DISCONNECTED) { + printf(" --> att disconnected\n"); + } else if (event_type == HCI_EVENT_LE_META) { + printf(" --> att le meta\n"); + } else if (event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { + printf(" --> att disconnect complete\n"); + } else { + printf(" --> att type: unknown (%u)\n", event_type); + } + } +} + +STATIC void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + // printf("hci_packet_handler: packet_type: %u, channel: %u, packet: %p, size: %u\n", packet_type, channel, packet, size); + if (packet_type == HCI_EVENT_PACKET) { + uint8_t event_type = hci_event_packet_get_type(packet); + if (event_type == HCI_EVENT_TRANSPORT_PACKET_SENT || event_type == HCI_EVENT_COMMAND_COMPLETE) { + } else if (event_type == BTSTACK_EVENT_NR_CONNECTIONS_CHANGED) { + printf(" --> hci type: # conns changed\n"); + } else if (event_type == HCI_EVENT_VENDOR_SPECIFIC) { + printf(" --> hci type: vendor specific\n"); + } else { + printf(" --> hci type: unknown (%u)\n", event_type); + } + } +} + int mp_bluetooth_init(void) { btstack_memory_init(); @@ -58,6 +93,15 @@ int mp_bluetooth_init(void) { mp_bluetooth_btstack_port_start(); mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_ACTIVE; + + // Register for HCI events. + memset(&hci_event_callback_registration, 0, sizeof(btstack_packet_callback_registration_t)); + hci_event_callback_registration.callback = &hci_packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + // Register for ATT event. + att_server_register_packet_handler(&att_server_packet_handler); + return 0; } @@ -118,14 +162,92 @@ void mp_bluetooth_gap_advertise_stop(void) { } int mp_bluetooth_gatts_register_service_begin(bool append) { + if (!append) { + // This will reset the DB. + // Becase the DB is statically allocated, there's no problem with just re-initing it. + // Note this would be a memory leak if we enabled HAVE_MALLOC (there's no API to free the existing db). + att_db_util_init(); + + att_db_util_add_service_uuid16(GAP_SERVICE_UUID); + att_db_util_add_characteristic_uuid16(GAP_DEVICE_NAME_UUID, ATT_PROPERTY_READ, ATT_SECURITY_NONE, ATT_SECURITY_NONE, (uint8_t *)"MPY BTSTACK", 11); + + att_db_util_add_service_uuid16(0x1801); + att_db_util_add_characteristic_uuid16(0x2a05, ATT_PROPERTY_READ, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0); + + } + + return 0; +} + +STATIC uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { + printf("btstack: att_read_callback (handle: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, offset, buffer, buffer_size); + return 0; +} + +STATIC int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { + printf("btstack: att_write_callback (handle: %u, mode: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, transaction_mode, offset, buffer, buffer_size); return 0; } +// Note: modbluetooth UUIDs store their data in LE. +STATIC inline uint16_t get_uuid16(const mp_obj_bluetooth_uuid_t *uuid) { + return (uuid->data[1] << 8) | uuid->data[0]; +} + int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, mp_obj_bluetooth_uuid_t **characteristic_uuids, uint8_t *characteristic_flags, mp_obj_bluetooth_uuid_t **descriptor_uuids, uint8_t *descriptor_flags, uint8_t *num_descriptors, uint16_t *handles, size_t num_characteristics) { + // TODO: It's not clear which endian btstack needs for uuids. + + uint16_t service_handle; + if (service_uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { + service_handle = att_db_util_add_service_uuid16(get_uuid16(service_uuid)); + } else if (service_uuid->type == MP_BLUETOOTH_UUID_TYPE_128) { + service_handle = att_db_util_add_service_uuid128(service_uuid->data); + } else { + return MP_EINVAL; + } + + printf("Registered service with handle %u\n", service_handle); + + size_t handle_index = 0; + size_t descriptor_index = 0; + + for (size_t i = 0; i < num_characteristics; ++i) { + uint16_t props = characteristic_flags[i] | ATT_PROPERTY_DYNAMIC; + uint16_t read_permission = ATT_SECURITY_NONE; + uint16_t write_permission = ATT_SECURITY_NONE; + if (characteristic_uuids[i]->type == MP_BLUETOOTH_UUID_TYPE_16) { + handles[handle_index] = att_db_util_add_characteristic_uuid16(get_uuid16(characteristic_uuids[i]), props, read_permission, write_permission, NULL, 0); + } else if (characteristic_uuids[i]->type == MP_BLUETOOTH_UUID_TYPE_128) { + handles[handle_index] = att_db_util_add_characteristic_uuid128(characteristic_uuids[i]->data, props, read_permission, write_permission, NULL, 0); + } else { + return MP_EINVAL; + } + printf("Registered char with handle %u\n", handles[handle_index]); + ++handle_index; + + for (size_t j = 0; j < num_descriptors[i]; ++j) { + uint16_t props = descriptor_flags[descriptor_index] | ATT_PROPERTY_DYNAMIC; + uint16_t read_permission = ATT_SECURITY_NONE; + uint16_t write_permission = ATT_SECURITY_NONE; + + if (descriptor_uuids[descriptor_index]->type == MP_BLUETOOTH_UUID_TYPE_16) { + handles[handle_index] = att_db_util_add_descriptor_uuid16(get_uuid16(descriptor_uuids[descriptor_index]), props, read_permission, write_permission, NULL, 0); + } else if (descriptor_uuids[descriptor_index]->type == MP_BLUETOOTH_UUID_TYPE_128) { + handles[handle_index] = att_db_util_add_descriptor_uuid128(descriptor_uuids[descriptor_index]->data, props, read_permission, write_permission, NULL, 0); + } else { + return MP_EINVAL; + } + printf("Registered desc with handle %u\n", handles[handle_index]); + ++descriptor_index; + ++handle_index; + } + } + return 0; } int mp_bluetooth_gatts_register_service_end(void) { + att_server_init(att_db_util_get_address(), &att_read_callback, &att_write_callback); return 0; } diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index 661130802a319..ed280e3897d31 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -63,6 +63,7 @@ // Advertisement packet lengths #define MP_BLUETOOTH_GAP_ADV_MAX_LEN (32) +// These match the spec values for these flags so can be passed directly to the stack. #define MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ (1 << 1) #define MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE (1 << 3) #define MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY (1 << 4) From 372e5a280e666f988757a8945c66693a19c40d32 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:43:52 +1100 Subject: [PATCH 11/15] extmod/btstack: Implement gatts_db for btstack. Work done in collaboration with Jim Mussared aka @jimmo. --- extmod/btstack/modbluetooth_btstack.c | 148 ++++++++++++++++++++------ extmod/btstack/modbluetooth_btstack.h | 5 + 2 files changed, 122 insertions(+), 31 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index e7f9014048c48..f285f3e45ce93 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -35,47 +35,69 @@ #include "lib/btstack/src/btstack.h" +#define DEBUG_EVENT_printf(...) //printf(__VA_ARGS__) + volatile int mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; static btstack_packet_callback_registration_t hci_event_callback_registration; +STATIC int btstack_error_to_errno(int err) { + if (err == ERROR_CODE_SUCCESS) { + return 0; + } else if (err == BTSTACK_ACL_BUFFERS_FULL) { + return MP_ENOMEM; + } else { + return MP_EINVAL; + } +} + STATIC void att_server_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - // printf("att_server_packet_handler: packet_type: %u, channel: %u, packet: %p, size: %u\n", packet_type, channel, packet, size); + DEBUG_EVENT_printf("att_server_packet_handler: packet_type: %u, channel: %u, packet: %p, size: %u\n", packet_type, channel, packet, size); if (packet_type == HCI_EVENT_PACKET) { uint8_t event_type = hci_event_packet_get_type(packet); if (event_type == ATT_EVENT_CONNECTED) { - printf(" --> att connected\n"); + DEBUG_EVENT_printf(" --> att connected\n"); + uint16_t conn_handle = att_event_connected_get_handle(packet); + uint8_t addr_type = att_event_connected_get_address_type(packet); + bd_addr_t addr; + att_event_connected_get_address(packet, addr); + mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_CONNECT, conn_handle, addr_type, addr); } else if (event_type == ATT_EVENT_DISCONNECTED) { - printf(" --> att disconnected\n"); + DEBUG_EVENT_printf(" --> att disconnected\n"); + uint16_t conn_handle = att_event_disconnected_get_handle(packet); + bd_addr_t addr = {0}; + mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, conn_handle, 0, addr); } else if (event_type == HCI_EVENT_LE_META) { - printf(" --> att le meta\n"); + DEBUG_EVENT_printf(" --> att le meta\n"); } else if (event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { - printf(" --> att disconnect complete\n"); + DEBUG_EVENT_printf(" --> att disconnect complete\n"); } else { - printf(" --> att type: unknown (%u)\n", event_type); + DEBUG_EVENT_printf(" --> att type: unknown (%u)\n", event_type); } } } STATIC void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - // printf("hci_packet_handler: packet_type: %u, channel: %u, packet: %p, size: %u\n", packet_type, channel, packet, size); + DEBUG_EVENT_printf("hci_packet_handler: packet_type: %u, channel: %u, packet: %p, size: %u\n", packet_type, channel, packet, size); if (packet_type == HCI_EVENT_PACKET) { uint8_t event_type = hci_event_packet_get_type(packet); if (event_type == HCI_EVENT_TRANSPORT_PACKET_SENT || event_type == HCI_EVENT_COMMAND_COMPLETE) { } else if (event_type == BTSTACK_EVENT_NR_CONNECTIONS_CHANGED) { - printf(" --> hci type: # conns changed\n"); + DEBUG_EVENT_printf(" --> hci type: # conns changed\n"); } else if (event_type == HCI_EVENT_VENDOR_SPECIFIC) { - printf(" --> hci type: vendor specific\n"); + DEBUG_EVENT_printf(" --> hci type: vendor specific\n"); } else { - printf(" --> hci type: unknown (%u)\n", event_type); + DEBUG_EVENT_printf(" --> hci type: unknown (%u)\n", event_type); } } } int mp_bluetooth_init(void) { + DEBUG_EVENT_printf("mp_bluetooth_init\n"); btstack_memory_init(); MP_STATE_PORT(bluetooth_btstack_root_pointers) = m_new0(mp_bluetooth_btstack_root_pointers_t, 1); + mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db); mp_bluetooth_btstack_port_init(); mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_STARTING; @@ -106,6 +128,7 @@ int mp_bluetooth_init(void) { } void mp_bluetooth_deinit(void) { + DEBUG_EVENT_printf("mp_bluetooth_deinit\n"); mp_bluetooth_btstack_port_deinit(); mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; @@ -121,6 +144,7 @@ void mp_bluetooth_get_device_addr(uint8_t *addr) { } int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) { + DEBUG_EVENT_printf("mp_bluetooth_gap_advertise_start\n"); uint16_t adv_int_min = interval_us / 625; uint16_t adv_int_max = interval_us / 625; uint8_t adv_type = connectable ? 0 : 2; @@ -156,12 +180,14 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons } void mp_bluetooth_gap_advertise_stop(void) { + DEBUG_EVENT_printf("mp_bluetooth_gap_advertise_stop\n"); gap_advertisements_enable(false); MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data_alloc = 0; MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data = NULL; } int mp_bluetooth_gatts_register_service_begin(bool append) { + DEBUG_EVENT_printf("mp_bluetooth_gatts_register_service_begin\n"); if (!append) { // This will reset the DB. // Becase the DB is statically allocated, there's no problem with just re-initing it. @@ -173,43 +199,68 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { att_db_util_add_service_uuid16(0x1801); att_db_util_add_characteristic_uuid16(0x2a05, ATT_PROPERTY_READ, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0); - } return 0; } STATIC uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { - printf("btstack: att_read_callback (handle: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, offset, buffer, buffer_size); - return 0; + DEBUG_EVENT_printf("btstack: att_read_callback (handle: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, offset, buffer, buffer_size); + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, att_handle); + if (!entry) { + DEBUG_EVENT_printf("btstack: att_read_callback handle not found\n"); + return 0; // TODO: Find status code for not-found. + } + + return att_read_callback_handle_blob(entry->data, entry->data_len, offset, buffer, buffer_size); } STATIC int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { - printf("btstack: att_write_callback (handle: %u, mode: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, transaction_mode, offset, buffer, buffer_size); + DEBUG_EVENT_printf("btstack: att_write_callback (handle: %u, mode: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, transaction_mode, offset, buffer, buffer_size); + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, att_handle); + if (!entry) { + DEBUG_EVENT_printf("btstack: att_write_callback handle not found\n"); + return 0; // TODO: Find status code for not-found. + } + + // TODO: Use `offset` arg. + size_t append_offset = 0; + if (entry->append) { + append_offset = entry->data_len; + } + entry->data_len = MIN(entry->data_alloc, buffer_size + append_offset); + memcpy(entry->data + append_offset, buffer, entry->data_len - append_offset); + + mp_bluetooth_gatts_on_write(connection_handle, att_handle); + return 0; } -// Note: modbluetooth UUIDs store their data in LE. STATIC inline uint16_t get_uuid16(const mp_obj_bluetooth_uuid_t *uuid) { return (uuid->data[1] << 8) | uuid->data[0]; } int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, mp_obj_bluetooth_uuid_t **characteristic_uuids, uint8_t *characteristic_flags, mp_obj_bluetooth_uuid_t **descriptor_uuids, uint8_t *descriptor_flags, uint8_t *num_descriptors, uint16_t *handles, size_t num_characteristics) { - // TODO: It's not clear which endian btstack needs for uuids. + DEBUG_EVENT_printf("mp_bluetooth_gatts_register_service\n"); + // Note: btstack expects BE UUIDs (which it immediately convertes to LE). + // So we have to convert all our modbluetooth LE UUIDs to BE just for the att_db_util_add_* methods (using get_uuid16 above, and reverse_128 from btstackutil.h). + + // TODO: btstack's att_db_util_add_* methods have no bounds checking or validation. + // Need some way to prevent additional services being added if we're out of space in the static buffer. - uint16_t service_handle; if (service_uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { - service_handle = att_db_util_add_service_uuid16(get_uuid16(service_uuid)); + att_db_util_add_service_uuid16(get_uuid16(service_uuid)); } else if (service_uuid->type == MP_BLUETOOTH_UUID_TYPE_128) { - service_handle = att_db_util_add_service_uuid128(service_uuid->data); + uint8_t buffer[16]; + reverse_128(service_uuid->data, buffer); + att_db_util_add_service_uuid128(buffer); } else { return MP_EINVAL; } - printf("Registered service with handle %u\n", service_handle); - size_t handle_index = 0; size_t descriptor_index = 0; + static uint8_t cccb_buf[2] = {0}; for (size_t i = 0; i < num_characteristics; ++i) { uint16_t props = characteristic_flags[i] | ATT_PROPERTY_DYNAMIC; @@ -218,11 +269,20 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m if (characteristic_uuids[i]->type == MP_BLUETOOTH_UUID_TYPE_16) { handles[handle_index] = att_db_util_add_characteristic_uuid16(get_uuid16(characteristic_uuids[i]), props, read_permission, write_permission, NULL, 0); } else if (characteristic_uuids[i]->type == MP_BLUETOOTH_UUID_TYPE_128) { - handles[handle_index] = att_db_util_add_characteristic_uuid128(characteristic_uuids[i]->data, props, read_permission, write_permission, NULL, 0); + uint8_t buffer[16]; + reverse_128(characteristic_uuids[i]->data, buffer); + handles[handle_index] = att_db_util_add_characteristic_uuid128(buffer, props, read_permission, write_permission, NULL, 0); } else { return MP_EINVAL; } - printf("Registered char with handle %u\n", handles[handle_index]); + mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index], MP_BLUETOOTH_DEFAULT_ATTR_LEN); + // If a NOTIFY or INDICATE characteristic is added, then we need to manage a value for the CCCB. + if (props & (ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE)) { + // btstack creates the CCCB as the next handle. + mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index] + 1, MP_BLUETOOTH_CCCB_LEN); + mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index] + 1, cccb_buf, sizeof(cccb_buf)); + } + DEBUG_EVENT_printf("Registered char with handle %u\n", handles[handle_index]); ++handle_index; for (size_t j = 0; j < num_descriptors[i]; ++j) { @@ -233,11 +293,14 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m if (descriptor_uuids[descriptor_index]->type == MP_BLUETOOTH_UUID_TYPE_16) { handles[handle_index] = att_db_util_add_descriptor_uuid16(get_uuid16(descriptor_uuids[descriptor_index]), props, read_permission, write_permission, NULL, 0); } else if (descriptor_uuids[descriptor_index]->type == MP_BLUETOOTH_UUID_TYPE_128) { - handles[handle_index] = att_db_util_add_descriptor_uuid128(descriptor_uuids[descriptor_index]->data, props, read_permission, write_permission, NULL, 0); + uint8_t buffer[16]; + reverse_128(descriptor_uuids[descriptor_index]->data, buffer); + handles[handle_index] = att_db_util_add_descriptor_uuid128(buffer, props, read_permission, write_permission, NULL, 0); } else { return MP_EINVAL; } - printf("Registered desc with handle %u\n", handles[handle_index]); + mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index], MP_BLUETOOTH_DEFAULT_ATTR_LEN); + DEBUG_EVENT_printf("Registered desc with handle %u\n", handles[handle_index]); ++descriptor_index; ++handle_index; } @@ -247,68 +310,91 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m } int mp_bluetooth_gatts_register_service_end(void) { + DEBUG_EVENT_printf("mp_bluetooth_gatts_register_service_end\n"); att_server_init(att_db_util_get_address(), &att_read_callback, &att_write_callback); return 0; } int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { - return 0; + DEBUG_EVENT_printf("mp_bluetooth_gatts_read\n"); + return mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); } int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len) { - return 0; + DEBUG_EVENT_printf("mp_bluetooth_gatts_write\n"); + return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); } int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { - return 0; + DEBUG_EVENT_printf("mp_bluetooth_gatts_notify\n"); + // Note: btstack doesn't appear to support sending a notification without a value, so include the stored value. + uint8_t *data = NULL; + size_t len = 0; + mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len); + return mp_bluetooth_gatts_notify_send(conn_handle, value_handle, data, &len); } int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len) { - return 0; + DEBUG_EVENT_printf("mp_bluetooth_gatts_notify_send\n"); + // TODO: We need to use att_server_request_to_send_notification here as the stack may not be ready to send a notification. + int err = att_server_notify(conn_handle, value_handle, value, *value_len); + return btstack_error_to_errno(err); } int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { - return 0; + DEBUG_EVENT_printf("mp_bluetooth_gatts_indicate\n"); + return btstack_error_to_errno(att_server_indicate(conn_handle, value_handle, NULL, 0)); } int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { - return 0; + DEBUG_EVENT_printf("mp_bluetooth_gatts_set_buffer\n"); + return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, len, append); } int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { + DEBUG_EVENT_printf("mp_bluetooth_gap_disconnect\n"); + gap_disconnect(conn_handle); return 0; } #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us) { + DEBUG_EVENT_printf("mp_bluetooth_gap_scan_start\n"); return 0; } int mp_bluetooth_gap_scan_stop(void) { + DEBUG_EVENT_printf("mp_bluetooth_gap_scan_stop\n"); return 0; } int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms) { + DEBUG_EVENT_printf("mp_bluetooth_gap_peripheral_connect\n"); return 0; } int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle) { + DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_primary_services\n"); return 0; } int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { + DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_characteristics\n"); return 0; } int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { + DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_descriptors\n"); return 0; } int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { + DEBUG_EVENT_printf("mp_bluetooth_gattc_read\n"); return 0; } int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) { + DEBUG_EVENT_printf("mp_bluetooth_gattc_write\n"); return 0; } #endif diff --git a/extmod/btstack/modbluetooth_btstack.h b/extmod/btstack/modbluetooth_btstack.h index 9e8c6ad3fbc2b..50e690fd4cd4e 100644 --- a/extmod/btstack/modbluetooth_btstack.h +++ b/extmod/btstack/modbluetooth_btstack.h @@ -27,11 +27,16 @@ #ifndef MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H #define MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H +#include "extmod/modbluetooth.h" + typedef struct _mp_bluetooth_btstack_root_pointers_t { // This stores both the advertising data and the scan response data, concatenated together. uint8_t *adv_data; // Total length of both. size_t adv_data_alloc; + + // Characteristic (and descriptor) value storage. + mp_gatts_db_t gatts_db; } mp_bluetooth_btstack_root_pointers_t; enum { From 018ce122ca6c073c0a9ed11b5ab4ca2f5e629350 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:44:13 +1100 Subject: [PATCH 12/15] extmod/btstack: Implement scan and gatt client, connect and disconnect. Work done in collaboration with Jim Mussared aka @jimmo. --- extmod/btstack/modbluetooth_btstack.c | 249 +++++++++++++++++++++----- 1 file changed, 206 insertions(+), 43 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index f285f3e45ce93..001d6102253ed 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -39,7 +39,7 @@ volatile int mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; -static btstack_packet_callback_registration_t hci_event_callback_registration; +STATIC btstack_packet_callback_registration_t hci_event_callback_registration; STATIC int btstack_error_to_errno(int err) { if (err == ERROR_CODE_SUCCESS) { @@ -51,46 +51,146 @@ STATIC int btstack_error_to_errno(int err) { } } -STATIC void att_server_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - DEBUG_EVENT_printf("att_server_packet_handler: packet_type: %u, channel: %u, packet: %p, size: %u\n", packet_type, channel, packet, size); - if (packet_type == HCI_EVENT_PACKET) { - uint8_t event_type = hci_event_packet_get_type(packet); - if (event_type == ATT_EVENT_CONNECTED) { - DEBUG_EVENT_printf(" --> att connected\n"); - uint16_t conn_handle = att_event_connected_get_handle(packet); - uint8_t addr_type = att_event_connected_get_address_type(packet); +STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uuid128) { + mp_obj_bluetooth_uuid_t result; + if (uuid16 != 0) { + result.data[0] = uuid16 & 0xff; + result.data[1] = (uuid16 >> 8) & 0xff; + result.type = MP_BLUETOOTH_UUID_TYPE_16; + } else { + reverse_128(uuid128, result.data); + result.type = MP_BLUETOOTH_UUID_TYPE_128; + } + return result; +} + +STATIC void btstack_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + DEBUG_EVENT_printf("btstack_packet_handler(packet_type=%u, channel=%u, packet=%p, size=%u)\n", packet_type, channel, packet, size); + if (packet_type != HCI_EVENT_PACKET) { + return; + } + + uint8_t event_type = hci_event_packet_get_type(packet); + if (event_type == ATT_EVENT_CONNECTED) { + DEBUG_EVENT_printf(" --> att connected\n"); + } else if (event_type == ATT_EVENT_DISCONNECTED) { + DEBUG_EVENT_printf(" --> att disconnected\n"); + } else if (event_type == HCI_EVENT_LE_META) { + DEBUG_EVENT_printf(" --> hci le meta\n"); + if (hci_event_le_meta_get_subevent_code(packet) == HCI_SUBEVENT_LE_CONNECTION_COMPLETE) { + uint16_t conn_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); + uint8_t addr_type = hci_subevent_le_connection_complete_get_peer_address_type(packet); bd_addr_t addr; - att_event_connected_get_address(packet, addr); - mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_CONNECT, conn_handle, addr_type, addr); - } else if (event_type == ATT_EVENT_DISCONNECTED) { - DEBUG_EVENT_printf(" --> att disconnected\n"); - uint16_t conn_handle = att_event_disconnected_get_handle(packet); - bd_addr_t addr = {0}; - mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, conn_handle, 0, addr); - } else if (event_type == HCI_EVENT_LE_META) { - DEBUG_EVENT_printf(" --> att le meta\n"); - } else if (event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { - DEBUG_EVENT_printf(" --> att disconnect complete\n"); + hci_subevent_le_connection_complete_get_peer_address(packet, addr); + uint16_t irq_event; + if (hci_subevent_le_connection_complete_get_role(packet) == 0) { + // Master role. + irq_event = MP_BLUETOOTH_IRQ_PERIPHERAL_CONNECT; + } else { + // Slave role. + irq_event = MP_BLUETOOTH_IRQ_CENTRAL_CONNECT; + } + mp_bluetooth_gap_on_connected_disconnected(irq_event, conn_handle, addr_type, addr); + } + } else if (event_type == BTSTACK_EVENT_STATE) { + DEBUG_EVENT_printf(" --> btstack event state 0x%02x\n", btstack_event_state_get_state(packet)); + } else if (event_type == HCI_EVENT_TRANSPORT_PACKET_SENT) { + DEBUG_EVENT_printf(" --> hci transport packet set\n"); + } else if (event_type == HCI_EVENT_COMMAND_COMPLETE) { + DEBUG_EVENT_printf(" --> hci command complete\n"); + } else if (event_type == HCI_EVENT_COMMAND_STATUS) { + DEBUG_EVENT_printf(" --> hci command status\n"); + } else if (event_type == HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS) { + DEBUG_EVENT_printf(" --> hci number of completed packets\n"); + } else if (event_type == BTSTACK_EVENT_NR_CONNECTIONS_CHANGED) { + DEBUG_EVENT_printf(" --> btstack # conns changed\n"); + } else if (event_type == HCI_EVENT_VENDOR_SPECIFIC) { + DEBUG_EVENT_printf(" --> hci vendor specific\n"); + } else if (event_type == GAP_EVENT_ADVERTISING_REPORT) { + DEBUG_EVENT_printf(" --> gap advertising report\n"); + bd_addr_t address; + gap_event_advertising_report_get_address(packet, address); + uint8_t adv_event_type = gap_event_advertising_report_get_advertising_event_type(packet); + uint8_t address_type = gap_event_advertising_report_get_address_type(packet); + int8_t rssi = gap_event_advertising_report_get_rssi(packet); + uint8_t length = gap_event_advertising_report_get_data_length(packet); + const uint8_t *data = gap_event_advertising_report_get_data(packet); + bool connectable = adv_event_type == 0 || adv_event_type == 1; + if (adv_event_type <= 2) { + mp_bluetooth_gap_on_scan_result(address_type, address, connectable, rssi, data, length); + } else if (adv_event_type == 4) { + // TODO: Scan response. + } + } else if (event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { + DEBUG_EVENT_printf(" --> hci disconnect complete\n"); + uint16_t conn_handle = hci_event_disconnection_complete_get_connection_handle(packet); + const hci_connection_t *conn = hci_connection_for_handle(conn_handle); + uint16_t irq_event; + if (conn == NULL || conn->role == 0) { + // Master role. + irq_event = MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT; } else { - DEBUG_EVENT_printf(" --> att type: unknown (%u)\n", event_type); + // Slave role. + irq_event = MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT; } + uint8_t addr[6] = {0}; + mp_bluetooth_gap_on_connected_disconnected(irq_event, conn_handle, 0xff, addr); + } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { + DEBUG_EVENT_printf(" --> gatt query complete\n"); + } else if (event_type == GATT_EVENT_SERVICE_QUERY_RESULT) { + DEBUG_EVENT_printf(" --> gatt service query result\n"); + uint16_t conn_handle = gatt_event_service_query_result_get_handle(packet); + gatt_client_service_t service; + gatt_event_service_query_result_get_service(packet, &service); + mp_obj_bluetooth_uuid_t service_uuid = create_mp_uuid(service.uuid16, service.uuid128); + mp_bluetooth_gattc_on_primary_service_result(conn_handle, service.start_group_handle, service.end_group_handle, &service_uuid); + } else if (event_type == GATT_EVENT_CHARACTERISTIC_QUERY_RESULT) { + DEBUG_EVENT_printf(" --> gatt characteristic query result\n"); + uint16_t conn_handle = gatt_event_characteristic_query_result_get_handle(packet); + gatt_client_characteristic_t characteristic; + gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); + mp_obj_bluetooth_uuid_t characteristic_uuid = create_mp_uuid(characteristic.uuid16, characteristic.uuid128); + mp_bluetooth_gattc_on_characteristic_result(conn_handle, characteristic.start_handle, characteristic.value_handle, characteristic.properties, &characteristic_uuid); + } else if (event_type == GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT) { + DEBUG_EVENT_printf(" --> gatt descriptor query result\n"); + uint16_t conn_handle = gatt_event_all_characteristic_descriptors_query_result_get_handle(packet); + gatt_client_characteristic_descriptor_t descriptor; + gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &descriptor); + mp_obj_bluetooth_uuid_t descriptor_uuid = create_mp_uuid(descriptor.uuid16, descriptor.uuid128); + mp_bluetooth_gattc_on_descriptor_result(conn_handle, descriptor.handle, &descriptor_uuid); + } else if (event_type == GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) { + DEBUG_EVENT_printf(" --> gatt characteristic value query result\n"); + uint16_t conn_handle = gatt_event_characteristic_value_query_result_get_handle(packet); + uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet); + uint16_t len = gatt_event_characteristic_value_query_result_get_value_length(packet); + const uint8_t *data = gatt_event_characteristic_value_query_result_get_value(packet); + MICROPY_PY_BLUETOOTH_ENTER + len = mp_bluetooth_gattc_on_data_available_start(MP_BLUETOOTH_IRQ_GATTC_READ_RESULT, conn_handle, value_handle, len); + mp_bluetooth_gattc_on_data_available_chunk(data, len); + mp_bluetooth_gattc_on_data_available_end(); + MICROPY_PY_BLUETOOTH_EXIT + } else { + DEBUG_EVENT_printf(" --> hci event type: unknown (0x%02x)\n", event_type); } } -STATIC void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - DEBUG_EVENT_printf("hci_packet_handler: packet_type: %u, channel: %u, packet: %p, size: %u\n", packet_type, channel, packet, size); - if (packet_type == HCI_EVENT_PACKET) { - uint8_t event_type = hci_event_packet_get_type(packet); - if (event_type == HCI_EVENT_TRANSPORT_PACKET_SENT || event_type == HCI_EVENT_COMMAND_COMPLETE) { - } else if (event_type == BTSTACK_EVENT_NR_CONNECTIONS_CHANGED) { - DEBUG_EVENT_printf(" --> hci type: # conns changed\n"); - } else if (event_type == HCI_EVENT_VENDOR_SPECIFIC) { - DEBUG_EVENT_printf(" --> hci type: vendor specific\n"); - } else { - DEBUG_EVENT_printf(" --> hci type: unknown (%u)\n", event_type); - } +#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +STATIC void btstack_packet_handler_write_with_response(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + DEBUG_EVENT_printf("btstack_packet_handler_write_with_response(packet_type=%u, channel=%u, packet=%p, size=%u)\n", packet_type, channel, packet, size); + if (packet_type != HCI_EVENT_PACKET) { + return; + } + + uint8_t event_type = hci_event_packet_get_type(packet); + if (event_type == GATT_EVENT_QUERY_COMPLETE) { + DEBUG_EVENT_printf(" --> gatt query complete\n"); + uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); + uint8_t status = gatt_event_query_complete_get_att_status(packet); + // TODO there is no value_handle to pass here + mp_bluetooth_gattc_on_write_status(conn_handle, 0, status); } } +#endif int mp_bluetooth_init(void) { DEBUG_EVENT_printf("mp_bluetooth_init\n"); @@ -113,17 +213,18 @@ int mp_bluetooth_init(void) { sm_set_er(dummy_key); sm_set_ir(dummy_key); + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + gatt_client_init(); + #endif + mp_bluetooth_btstack_port_start(); mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_ACTIVE; // Register for HCI events. memset(&hci_event_callback_registration, 0, sizeof(btstack_packet_callback_registration_t)); - hci_event_callback_registration.callback = &hci_packet_handler; + hci_event_callback_registration.callback = &btstack_packet_handler; hci_add_event_handler(&hci_event_callback_registration); - // Register for ATT event. - att_server_register_packet_handler(&att_server_packet_handler); - return 0; } @@ -358,44 +459,106 @@ int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { } #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +STATIC btstack_timer_source_t scan_duration_timeout; + +STATIC void hci_initialization_timeout_handler(btstack_timer_source_t *ds) { + mp_bluetooth_gap_scan_stop(); +} + int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us) { DEBUG_EVENT_printf("mp_bluetooth_gap_scan_start\n"); + + btstack_run_loop_set_timer(&scan_duration_timeout, duration_ms); + btstack_run_loop_set_timer_handler(&scan_duration_timeout, hci_initialization_timeout_handler); + btstack_run_loop_add_timer(&scan_duration_timeout); + + // 0 = passive scan (we don't handle scan response). + gap_set_scan_parameters(0, interval_us / 625, window_us / 625); + gap_start_scan(); + return 0; } int mp_bluetooth_gap_scan_stop(void) { DEBUG_EVENT_printf("mp_bluetooth_gap_scan_stop\n"); + btstack_run_loop_remove_timer(&scan_duration_timeout); + gap_stop_scan(); + mp_bluetooth_gap_on_scan_complete(); return 0; } int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms) { DEBUG_EVENT_printf("mp_bluetooth_gap_peripheral_connect\n"); - return 0; + + uint16_t conn_scan_interval = 60000 / 625; + uint16_t conn_scan_window = 30000 / 625; + uint16_t conn_interval_min = 10000 / 1250; + uint16_t conn_interval_max = 30000 / 1250; + uint16_t conn_latency = 4; + uint16_t supervision_timeout = duration_ms / 10; // default = 720 + uint16_t min_ce_length = 10000 / 625; + uint16_t max_ce_length = 30000 / 625; + + gap_set_connection_parameters(conn_scan_interval, conn_scan_window, conn_interval_min, conn_interval_max, conn_latency, supervision_timeout, min_ce_length, max_ce_length); + + bd_addr_t btstack_addr; + memcpy(btstack_addr, addr, BD_ADDR_LEN); + return btstack_error_to_errno(gap_connect(btstack_addr, addr_type)); } int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle) { DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_primary_services\n"); - return 0; + return btstack_error_to_errno(gatt_client_discover_primary_services(&btstack_packet_handler, conn_handle)); } int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_characteristics\n"); - return 0; + + gatt_client_service_t service = { + // Only start/end handles needed for gatt_client_discover_characteristics_for_service. + .start_group_handle = start_handle, + .end_group_handle = end_handle, + .uuid16 = 0, + .uuid128 = {0}, + }; + return btstack_error_to_errno(gatt_client_discover_characteristics_for_service(&btstack_packet_handler, conn_handle, &service)); } int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_descriptors\n"); - return 0; + gatt_client_characteristic_t characteristic = { + // Only start/end handles needed for gatt_client_discover_characteristic_descriptors. + .start_handle = start_handle, + .value_handle = 0, + .end_handle = end_handle, + .properties = 0, + .uuid16 = 0, + .uuid128 = {0}, + }; + return btstack_error_to_errno(gatt_client_discover_characteristic_descriptors(&btstack_packet_handler, conn_handle, &characteristic)); } int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { DEBUG_EVENT_printf("mp_bluetooth_gattc_read\n"); - return 0; + return btstack_error_to_errno(gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler, conn_handle, value_handle)); } int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) { DEBUG_EVENT_printf("mp_bluetooth_gattc_write\n"); - return 0; + + // TODO the below gatt_client functions do not copy the data and require it to be valid + // until the write is done, so there should be some kind of buffering done here. + + if (mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) { + // TODO need to call gatt_client_request_can_write_without_response_event then do + // the actual write on the callback from that. + return btstack_error_to_errno(gatt_client_write_value_of_characteristic_without_response(conn_handle, value_handle, *value_len, (uint8_t *)value)); + } + if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) { + return btstack_error_to_errno(gatt_client_write_value_of_characteristic(&btstack_packet_handler_write_with_response, conn_handle, value_handle, *value_len, (uint8_t *)value)); + } + + return MP_EINVAL; } #endif From d7259f6b1c746d352c658bf94a4e0d4c92712ba5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 6 Mar 2020 12:37:37 +1100 Subject: [PATCH 13/15] extmod/btstack: Implement notifications/indications for GATT clients. Work done in collaboration with Jim Mussared aka @jimmo. --- extmod/btstack/modbluetooth_btstack.c | 37 ++++++++++++++++++++++++++- extmod/btstack/modbluetooth_btstack.h | 11 ++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 001d6102253ed..f5ae40a3a0b50 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -135,6 +135,7 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint16_t channel, uint8_ } uint8_t addr[6] = {0}; mp_bluetooth_gap_on_connected_disconnected(irq_event, conn_handle, 0xff, addr); + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { DEBUG_EVENT_printf(" --> gatt query complete\n"); } else if (event_type == GATT_EVENT_SERVICE_QUERY_RESULT) { @@ -169,6 +170,25 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint16_t channel, uint8_ mp_bluetooth_gattc_on_data_available_chunk(data, len); mp_bluetooth_gattc_on_data_available_end(); MICROPY_PY_BLUETOOTH_EXIT + } else if (event_type == GATT_EVENT_NOTIFICATION) { + DEBUG_EVENT_printf(" --> gatt notification\n"); + uint16_t conn_handle = gatt_event_notification_get_handle(packet); + uint16_t value_handle = gatt_event_notification_get_value_handle(packet); + uint16_t len = gatt_event_notification_get_value_length(packet); + const uint8_t *data = gatt_event_notification_get_value(packet); + len = mp_bluetooth_gattc_on_data_available_start(MP_BLUETOOTH_IRQ_GATTC_NOTIFY, conn_handle, value_handle, len); + mp_bluetooth_gattc_on_data_available_chunk(data, len); + mp_bluetooth_gattc_on_data_available_end(); + } else if (event_type == GATT_EVENT_INDICATION) { + DEBUG_EVENT_printf(" --> gatt indication\n"); + uint16_t conn_handle = gatt_event_indication_get_handle(packet); + uint16_t value_handle = gatt_event_indication_get_value_handle(packet); + uint16_t len = gatt_event_indication_get_value_length(packet); + const uint8_t *data = gatt_event_indication_get_value(packet); + len = mp_bluetooth_gattc_on_data_available_start(MP_BLUETOOTH_IRQ_GATTC_INDICATE, conn_handle, value_handle, len); + mp_bluetooth_gattc_on_data_available_chunk(data, len); + mp_bluetooth_gattc_on_data_available_end(); + #endif } else { DEBUG_EVENT_printf(" --> hci event type: unknown (0x%02x)\n", event_type); } @@ -225,11 +245,26 @@ int mp_bluetooth_init(void) { hci_event_callback_registration.callback = &btstack_packet_handler; hci_add_event_handler(&hci_event_callback_registration); + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + // Enable GATT_EVENT_NOTIFICATION/GATT_EVENT_INDICATION for all connections and handles. + gatt_client_listen_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification, &btstack_packet_handler, GATT_CLIENT_ANY_CONNECTION, NULL); + #endif + return 0; } void mp_bluetooth_deinit(void) { DEBUG_EVENT_printf("mp_bluetooth_deinit\n"); + + if (!MP_STATE_PORT(bluetooth_btstack_root_pointers)) { + return; + } + + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + // Remove our registration for notify/indicate. + gatt_client_stop_listening_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification); + #endif + mp_bluetooth_btstack_port_deinit(); mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; @@ -560,6 +595,6 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const return MP_EINVAL; } -#endif +#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/extmod/btstack/modbluetooth_btstack.h b/extmod/btstack/modbluetooth_btstack.h index 50e690fd4cd4e..ba1d9591dc3a1 100644 --- a/extmod/btstack/modbluetooth_btstack.h +++ b/extmod/btstack/modbluetooth_btstack.h @@ -27,8 +27,12 @@ #ifndef MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H #define MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK + #include "extmod/modbluetooth.h" +#include "lib/btstack/src/btstack.h" + typedef struct _mp_bluetooth_btstack_root_pointers_t { // This stores both the advertising data and the scan response data, concatenated together. uint8_t *adv_data; @@ -37,6 +41,11 @@ typedef struct _mp_bluetooth_btstack_root_pointers_t { // Characteristic (and descriptor) value storage. mp_gatts_db_t gatts_db; + + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + // Registration for notify/indicate events. + gatt_client_notification_t notification; + #endif } mp_bluetooth_btstack_root_pointers_t; enum { @@ -51,4 +60,6 @@ void mp_bluetooth_btstack_port_init(void); void mp_bluetooth_btstack_port_deinit(void); void mp_bluetooth_btstack_port_start(void); +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK + #endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H From e965363b6b66785ff088c4e998a7e80783bdd97c Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 10 Mar 2020 01:47:16 +1100 Subject: [PATCH 14/15] stm32: Refactor Bluetooth HCI RX to be independent of transport layer. Now all HCI specific code (eg UART vs WB55 internal messaging) is confined to modbluetooth_hci.c. --- extmod/modbluetooth_hci.h | 1 + extmod/nimble/nimble/npl_os.c | 4 ++++ ports/stm32/btstack.c | 5 ++-- ports/stm32/modbluetooth_hci.c | 42 ++++++++++++++++++++++++++++++++++ ports/stm32/nimble.c | 23 +++---------------- ports/stm32/rfcore.c | 15 +++++------- ports/stm32/rfcore.h | 2 +- 7 files changed, 60 insertions(+), 32 deletions(-) diff --git a/extmod/modbluetooth_hci.h b/extmod/modbluetooth_hci.h index 6d44761a4efbe..d6b432d224710 100644 --- a/extmod/modbluetooth_hci.h +++ b/extmod/modbluetooth_hci.h @@ -50,6 +50,7 @@ extern pyb_uart_obj_t mp_bluetooth_hci_uart_obj; int mp_bluetooth_hci_uart_init(uint32_t port); int mp_bluetooth_hci_uart_activate(void); int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate); +int mp_bluetooth_hci_uart_readchar(void); int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len); #endif // MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_HCI_H diff --git a/extmod/nimble/nimble/npl_os.c b/extmod/nimble/nimble/npl_os.c index 57ab689e6b6c1..620dcb0aec13c 100644 --- a/extmod/nimble/nimble/npl_os.c +++ b/extmod/nimble/nimble/npl_os.c @@ -235,7 +235,11 @@ ble_npl_error_t ble_npl_sem_pend(struct ble_npl_sem *sem, ble_npl_time_t timeout if (sem->count == 0) { uint32_t t0 = mp_hal_ticks_ms(); while (sem->count == 0 && mp_hal_ticks_ms() - t0 < timeout) { + // This function may be called at thread-level, so execute + // mp_bluetooth_nimble_hci_uart_process at raised priority. + MICROPY_PY_BLUETOOTH_ENTER mp_bluetooth_nimble_hci_uart_process(); + MICROPY_PY_BLUETOOTH_EXIT if (sem->count != 0) { break; } diff --git a/ports/stm32/btstack.c b/ports/stm32/btstack.c index f7669d1fc6447..cbb15a86cb972 100644 --- a/ports/stm32/btstack.c +++ b/ports/stm32/btstack.c @@ -165,8 +165,9 @@ STATIC void btstack_uart_process(void) { // Append any new bytes to the recv buffer, notifying bstack if we've got // the number of bytes it was looking for. - while (uart_rx_any(&mp_bluetooth_hci_uart_obj) && recv_idx < recv_len) { - recv_buf[recv_idx++] = uart_rx_char(&mp_bluetooth_hci_uart_obj); + int chr; + while (recv_idx < recv_len && (chr = mp_bluetooth_hci_uart_readchar()) >= 0) { + recv_buf[recv_idx++] = chr; if (recv_idx == recv_len) { recv_idx = 0; recv_len = 0; diff --git a/ports/stm32/modbluetooth_hci.c b/ports/stm32/modbluetooth_hci.c index c6937b2b2a169..4e016eae8451c 100644 --- a/ports/stm32/modbluetooth_hci.c +++ b/ports/stm32/modbluetooth_hci.c @@ -54,8 +54,13 @@ void mp_bluetooth_hci_poll_wrapper(uint32_t ticks_ms) { /******************************************************************************/ // HCI over IPCC +#include #include "rfcore.h" +STATIC uint16_t hci_uart_rx_buf_cur; +STATIC uint16_t hci_uart_rx_buf_len; +STATIC uint8_t hci_uart_rx_buf_data[256]; + int mp_bluetooth_hci_controller_deactivate(void) { return 0; } @@ -79,6 +84,8 @@ int mp_bluetooth_hci_uart_init(uint32_t port) { int mp_bluetooth_hci_uart_activate(void) { rfcore_ble_init(); + hci_uart_rx_buf_cur = 0; + hci_uart_rx_buf_len = 0; return 0; } @@ -94,6 +101,31 @@ int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { return 0; } +// Callback to copy data into local hci_uart_rx_buf_data buffer for subsequent use. +STATIC int mp_bluetooth_hci_uart_msg_cb(void *env, const uint8_t *buf, size_t len) { + (void)env; + if (hci_uart_rx_buf_len + len > MP_ARRAY_SIZE(hci_uart_rx_buf_data)) { + len = MP_ARRAY_SIZE(hci_uart_rx_buf_data) - hci_uart_rx_buf_len; + } + memcpy(hci_uart_rx_buf_data + hci_uart_rx_buf_len, buf, len); + hci_uart_rx_buf_len += len; + return 0; +} + +int mp_bluetooth_hci_uart_readchar(void) { + if (hci_uart_rx_buf_cur >= hci_uart_rx_buf_len) { + hci_uart_rx_buf_cur = 0; + hci_uart_rx_buf_len = 0; + rfcore_ble_check_msg(mp_bluetooth_hci_uart_msg_cb, NULL); + } + + if (hci_uart_rx_buf_cur < hci_uart_rx_buf_len) { + return hci_uart_rx_buf_data[hci_uart_rx_buf_cur++]; + } else { + return -1; + } +} + #else /******************************************************************************/ @@ -153,6 +185,16 @@ int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { return 0; } +// This function expects the controller to be in the wake state via a previous call +// to mp_bluetooth_hci_controller_woken. +int mp_bluetooth_hci_uart_readchar(void) { + if (uart_rx_any(&mp_bluetooth_hci_uart_obj)) { + return uart_rx_char(&mp_bluetooth_hci_uart_obj); + } else { + return -1; + } +} + #endif // defined(STM32WB) #endif // MICROPY_PY_BLUETOOTH diff --git a/ports/stm32/nimble.c b/ports/stm32/nimble.c index ff78a0a1a3d55..1f4aa4b2ce6cb 100644 --- a/ports/stm32/nimble.c +++ b/ports/stm32/nimble.c @@ -67,28 +67,13 @@ void mp_bluetooth_nimble_port_start(void) { ble_hs_start(); } -#if defined(STM32WB) - -#include "rfcore.h" - -void mp_bluetooth_nimble_hci_uart_rx(hal_uart_rx_cb_t rx_cb, void *rx_arg) { - // Protect in case it's called from ble_npl_sem_pend at thread-level - MICROPY_PY_LWIP_ENTER - rfcore_ble_check_msg(rx_cb, rx_arg); - MICROPY_PY_LWIP_EXIT -} - -#else - -#include "uart.h" - void mp_bluetooth_nimble_hci_uart_rx(hal_uart_rx_cb_t rx_cb, void *rx_arg) { bool host_wake = mp_bluetooth_hci_controller_woken(); - while (uart_rx_any(&mp_bluetooth_hci_uart_obj)) { - uint8_t data = uart_rx_char(&mp_bluetooth_hci_uart_obj); + int chr; + while ((chr = mp_bluetooth_hci_uart_readchar()) >= 0) { //printf("UART RX: %02x\n", data); - rx_cb(rx_arg, data); + rx_cb(rx_arg, chr); } if (host_wake) { @@ -96,8 +81,6 @@ void mp_bluetooth_nimble_hci_uart_rx(hal_uart_rx_cb_t rx_cb, void *rx_arg) { } } -#endif // defined(STM32WB) - void mp_bluetooth_nimble_hci_uart_tx_strn(const char *str, uint len) { mp_bluetooth_hci_uart_write((const uint8_t *)str, len); } diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 8221527658982..9b513839c8b43 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -56,7 +56,7 @@ typedef struct _tl_list_node_t { } tl_list_node_t; typedef struct _parse_hci_info_t { - int (*cb_fun)(void *, uint8_t); + int (*cb_fun)(void *, const uint8_t *, size_t); void *cb_env; bool was_hci_reset_evt; } parse_hci_info_t; @@ -190,9 +190,7 @@ STATIC void tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { // Standard BT HCI ACL packet kind = "HCI_ACL"; if (parse != NULL) { - for (size_t i = 0; i < len; ++i) { - parse->cb_fun(parse->cb_env, buf[i]); - } + parse->cb_fun(parse->cb_env, buf, len); } break; } @@ -205,12 +203,11 @@ STATIC void tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { len -= 1; fix = true; } - for (size_t i = 0; i < len; ++i) { - parse->cb_fun(parse->cb_env, buf[i]); - } + parse->cb_fun(parse->cb_env, buf, len); if (fix) { len += 1; - parse->cb_fun(parse->cb_env, 0x00); // success + uint8_t data = 0x00; // success + parse->cb_fun(parse->cb_env, &data, 1); } // Check for successful HCI_Reset event parse->was_hci_reset_evt = buf[1] == 0x0e && buf[2] == 0x04 && buf[3] == 0x01 @@ -403,7 +400,7 @@ void rfcore_ble_hci_cmd(size_t len, const uint8_t *src) { IPCC->C1SCR = ch << 16; } -void rfcore_ble_check_msg(int (*cb)(void *, uint8_t), void *env) { +void rfcore_ble_check_msg(int (*cb)(void *, const uint8_t *, size_t), void *env) { parse_hci_info_t parse = { cb, env, false }; tl_check_msg(&ipcc_mem_ble_evt_queue, IPCC_CH_BLE, &parse); diff --git a/ports/stm32/rfcore.h b/ports/stm32/rfcore.h index 8a04075732559..138c438f12eae 100644 --- a/ports/stm32/rfcore.h +++ b/ports/stm32/rfcore.h @@ -32,6 +32,6 @@ void rfcore_init(void); void rfcore_ble_init(void); void rfcore_ble_hci_cmd(size_t len, const uint8_t *src); -void rfcore_ble_check_msg(int (*cb)(void *, uint8_t), void *env); +void rfcore_ble_check_msg(int (*cb)(void *, const uint8_t *, size_t), void *env); #endif // MICROPY_INCLUDED_STM32_RFCORE_H From ecee6f2877aa5e1a9c486fdde5d47c3de5dee092 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Feb 2020 14:46:17 +1100 Subject: [PATCH 15/15] travis: Build stm32 PYBD_SF6 with BTstack as bluetooth stack. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e7fd13fd55962..2a993009d53e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,10 +53,11 @@ jobs: script: - make ${MAKEOPTS} -C mpy-cross - make ${MAKEOPTS} -C ports/stm32 submodules + - git submodule update --init lib/btstack - make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_F091RC - make ${MAKEOPTS} -C ports/stm32 BOARD=PYBV11 MICROPY_PY_WIZNET5K=5200 MICROPY_PY_CC3K=1 - make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF2 - - make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF6 NANBOX=1 + - make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF6 NANBOX=1 MICROPY_BLUETOOTH_NIMBLE=0 MICROPY_BLUETOOTH_BTSTACK=1 - make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_H743ZI CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' - make ${MAKEOPTS} -C ports/stm32 BOARD=B_L072Z_LRWAN1 - make ${MAKEOPTS} -C ports/stm32 BOARD=STM32L476DISC