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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy