diff --git a/drivers/cyw43/cywbt.c b/drivers/cyw43/cywbt.c index 79318f4483..defe670683 100644 --- a/drivers/cyw43/cywbt.c +++ b/drivers/cyw43/cywbt.c @@ -31,13 +31,19 @@ #include "py/mphal.h" #include "pin_static_af.h" #include "uart.h" -#include "extmod/modbluetooth_hci.h" +#include "extmod/mpbthci.h" #if MICROPY_PY_NETWORK_CYW43 extern const char fw_4343WA1_7_45_98_50_start; #define CYWBT_FW_ADDR (&fw_4343WA1_7_45_98_50_start + 749 * 512 + 29 * 256) +// Provided by the port. +extern pyb_uart_obj_t mp_bluetooth_hci_uart_obj; + +// Provided by the port, and also possibly shared with the stack. +extern uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + /******************************************************************************/ // CYW BT HCI low-level driver @@ -168,10 +174,6 @@ int mp_bluetooth_hci_controller_init(void) { mp_hal_pin_config(pyb_pin_WL_GPIO_4, MP_HAL_PIN_MODE_OUTPUT, MP_HAL_PIN_PULL_NONE, 0); // RF-switch power mp_hal_pin_high(pyb_pin_WL_GPIO_4); // Turn the RF-switch on - return 0; -} - -int mp_bluetooth_hci_controller_activate(void) { uint8_t buf[256]; mp_hal_pin_low(pyb_pin_BT_REG_ON); @@ -219,7 +221,7 @@ int mp_bluetooth_hci_controller_activate(void) { return 0; } -int mp_bluetooth_hci_controller_deactivate(void) { +int mp_bluetooth_hci_controller_deinit(void) { mp_hal_pin_low(pyb_pin_BT_REG_ON); return 0; diff --git a/extmod/btstack/btstack.mk b/extmod/btstack/btstack.mk index dd96e63379..3084601b85 100644 --- a/extmod/btstack/btstack.mk +++ b/extmod/btstack/btstack.mk @@ -38,6 +38,17 @@ CFLAGS += $(shell pkg-config libusb-1.0 --cflags) LDFLAGS += $(shell pkg-config libusb-1.0 --libs) endif +ifeq ($(MICROPY_BLUETOOTH_BTSTACK_H4),1) +SRC_BTSTACK += \ + lib/btstack/src/hci_transport_h4.c \ + lib/btstack/chipset/zephyr/btstack_chipset_zephyr.c + +EXTMOD_SRC_C += \ + extmod/btstack/btstack_hci_uart.c \ + +CFLAGS_MOD += -DMICROPY_BLUETOOTH_BTSTACK_H4=1 +endif + ifeq ($(MICROPY_BLUETOOTH_BTSTACK_ENABLE_CLASSIC),1) include $(BTSTACK_DIR)/src/classic/Makefile.inc SRC_BTSTACK += \ diff --git a/extmod/btstack/btstack_config.h b/extmod/btstack/btstack_config.h index f420f47a5b..e56a84f94a 100644 --- a/extmod/btstack/btstack_config.h +++ b/extmod/btstack/btstack_config.h @@ -8,6 +8,7 @@ // #define ENABLE_CLASSIC #define ENABLE_LE_DATA_CHANNELS // #define ENABLE_LOG_INFO +// #define ENABLE_LOG_DEBUG #define ENABLE_LOG_ERROR // BTstack configuration. buffers, sizes, ... diff --git a/ports/stm32/btstack.c b/extmod/btstack/btstack_hci_uart.c similarity index 67% rename from ports/stm32/btstack.c rename to extmod/btstack/btstack_hci_uart.c index cbb15a86cb..5c96e02dc0 100644 --- a/ports/stm32/btstack.c +++ b/extmod/btstack/btstack_hci_uart.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2020 Damien P. George + * Copyright (c) 2020 Jim Mussared * * 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,12 +32,14 @@ #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" +#include "extmod/mpbthci.h" +#include "extmod/btstack/btstack_hci_uart.h" + +#include "mpbtstackport.h" + +// Implements a btstack btstack_uart_block_t on top of the mphciuart.h +// interface to an HCI UART provided by the port. // We pass the bytes directly to the UART during a send, but then notify btstack in the next poll. STATIC bool send_done; @@ -48,42 +51,29 @@ 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) { + (void)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); + // Set up the UART peripheral, attach IRQ and power up the HCI controller. + // We haven't been told the baud rate yet, so defer that until btstack_uart_set_baudrate. + mp_bluetooth_hci_uart_init(MICROPY_HW_BLE_UART_ID, 0); + mp_bluetooth_hci_controller_init(); 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) { + mp_bluetooth_hci_controller_deinit(); return 0; } @@ -101,10 +91,12 @@ STATIC int btstack_uart_set_baudrate(uint32_t baudrate) { } STATIC int btstack_uart_set_parity(int parity) { + (void)parity; return 0; } STATIC int btstack_uart_set_flowcontrol(int flowcontrol) { + (void)flowcontrol; return 0; } @@ -123,14 +115,16 @@ STATIC int btstack_uart_get_supported_sleep_modes(void) { } STATIC void btstack_uart_set_sleep(btstack_uart_sleep_mode_t sleep_mode) { + (void)sleep_mode; // printf("btstack_uart_set_sleep %u\n", sleep_mode); } STATIC void btstack_uart_set_wakeup_handler(void (*wakeup_handler)(void)) { + (void)wakeup_handler; // printf("btstack_uart_set_wakeup_handler\n"); } -STATIC const btstack_uart_block_t btstack_uart_block = { +const btstack_uart_block_t mp_bluetooth_btstack_hci_uart_block = { &btstack_uart_init, &btstack_uart_open, &btstack_uart_close, @@ -146,15 +140,9 @@ STATIC const btstack_uart_block_t btstack_uart_block = { &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, -}; +void mp_bluetooth_btstack_hci_uart_process(void) { + bool host_wake = mp_bluetooth_hci_controller_woken(); -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; @@ -176,48 +164,10 @@ STATIC void btstack_uart_process(void) { } } } -} -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 diff --git a/extmod/nimble/nimble/nimble_hci_uart.h b/extmod/btstack/btstack_hci_uart.h similarity index 66% rename from extmod/nimble/nimble/nimble_hci_uart.h rename to extmod/btstack/btstack_hci_uart.h index 646a12dac1..8011e587de 100644 --- a/extmod/nimble/nimble/nimble_hci_uart.h +++ b/extmod/btstack/btstack_hci_uart.h @@ -3,7 +3,8 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Damien P. George + * Copyright (c) 2020 Damien P. George + * Copyright (c) 2020 Jim Mussared * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,18 +24,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_NIMBLE_HCI_UART_H -#define MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_NIMBLE_HCI_UART_H -// Extensions to extmod/modbluetooth_hci.h specific to NimBLE. +#ifndef MICROPY_INCLUDED_EXTMOD_BTSTACK_HCI_UART_H +#define MICROPY_INCLUDED_EXTMOD_BTSTACK_HCI_UART_H -#include "extmod/nimble/hal/hal_uart.h" +#include "lib/btstack/src/btstack.h" -// Helpers called from ports. -void mp_bluetooth_nimble_hci_uart_process(void); +// --- Used by the port to create the HCI transport --------------------------- +extern const btstack_uart_block_t mp_bluetooth_btstack_hci_uart_block; -// 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); +// --- Called by the MicroPython port when UART data is available ------------- +void mp_bluetooth_btstack_hci_uart_process(void); -#endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_NIMBLE_HCI_UART_H +#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 9aef7e6879..03ec12b734 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -53,6 +53,8 @@ STATIC const uint16_t BTSTACK_GAP_DEVICE_NAME_HANDLE = 3; volatile int mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; +#define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV + STATIC int btstack_error_to_errno(int err) { DEBUG_printf(" --> btstack error: %d\n", err); if (err == ERROR_CODE_SUCCESS) { @@ -300,6 +302,9 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t if (state == HCI_STATE_WORKING) { // Signal that initialisation has completed. mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_ACTIVE; + } else if (state == HCI_STATE_HALTING) { + // Signal that de-initialisation has begun. + mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_HALTING; } else if (state == HCI_STATE_OFF) { // Signal that de-initialisation has completed. mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; diff --git a/extmod/btstack/modbluetooth_btstack.h b/extmod/btstack/modbluetooth_btstack.h index 2fad86f226..7890bbfae2 100644 --- a/extmod/btstack/modbluetooth_btstack.h +++ b/extmod/btstack/modbluetooth_btstack.h @@ -56,6 +56,7 @@ enum { MP_BLUETOOTH_BTSTACK_STATE_OFF, MP_BLUETOOTH_BTSTACK_STATE_STARTING, MP_BLUETOOTH_BTSTACK_STATE_ACTIVE, + MP_BLUETOOTH_BTSTACK_STATE_HALTING, MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT, }; diff --git a/extmod/mpbthci.c b/extmod/mpbthci.c new file mode 100644 index 0000000000..79a8654242 --- /dev/null +++ b/extmod/mpbthci.c @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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. + */ + +// Default definitions in case a controller doesn't implement this. + +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH + +#define DEBUG_printf(...) // printf(__VA_ARGS__) + +#include "extmod/mpbthci.h" + + +#endif // MICROPY_PY_BLUETOOTH diff --git a/extmod/modbluetooth_hci.h b/extmod/mpbthci.h similarity index 71% rename from extmod/modbluetooth_hci.h rename to extmod/mpbthci.h index d6b432d224..acb5b832ba 100644 --- a/extmod/modbluetooth_hci.h +++ b/extmod/mpbthci.h @@ -24,15 +24,15 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_HCI_H -#define MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_HCI_H +#ifndef MICROPY_INCLUDED_EXTMOD_MPBTHCI_H +#define MICROPY_INCLUDED_EXTMOD_MPBTHCI_H -#include "uart.h" +// --- Optionally can be implemented by the driver. --------------------------- -// Optionally can be implemented by the driver. +// Start/stop the HCI controller. +// Requires the UART to this HCI controller is available. int mp_bluetooth_hci_controller_init(void); -int mp_bluetooth_hci_controller_activate(void); -int mp_bluetooth_hci_controller_deactivate(void); +int mp_bluetooth_hci_controller_deinit(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); @@ -41,16 +41,11 @@ 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); +// --- Bindings that need to be implemented by the port. ---------------------- +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate); +int mp_bluetooth_hci_uart_deinit(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 +#endif // MICROPY_INCLUDED_EXTMOD_MPBTHCI_H diff --git a/extmod/nimble/hal/hal_uart.c b/extmod/nimble/hal/hal_uart.c index fba82b8304..c6d0850fea 100644 --- a/extmod/nimble/hal/hal_uart.c +++ b/extmod/nimble/hal/hal_uart.c @@ -26,11 +26,9 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "pin_static_af.h" #include "nimble/ble.h" #include "extmod/nimble/hal/hal_uart.h" -#include "extmod/modbluetooth_hci.h" -#include "extmod/nimble/nimble/nimble_hci_uart.h" +#include "extmod/mpbthci.h" #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE @@ -39,6 +37,9 @@ static void *hal_uart_tx_arg; static hal_uart_rx_cb_t hal_uart_rx_cb; static void *hal_uart_rx_arg; +// Provided by the port, and also possibly shared with the driver. +extern uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + 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) { hal_uart_tx_cb = tx_cb; hal_uart_tx_arg = tx_arg; @@ -48,9 +49,7 @@ 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) { - mp_bluetooth_hci_uart_init(port); - mp_bluetooth_hci_uart_set_baudrate(baudrate); - return mp_bluetooth_hci_uart_activate(); + return mp_bluetooth_hci_uart_init(port, baudrate); } void hal_uart_start_tx(uint32_t port) { @@ -71,7 +70,7 @@ void hal_uart_start_tx(uint32_t port) { printf("\n"); #endif - mp_bluetooth_nimble_hci_uart_tx_strn((void*)mp_bluetooth_hci_cmd_buf, len); + mp_bluetooth_hci_uart_write(mp_bluetooth_hci_cmd_buf, len); } int hal_uart_close(uint32_t port) { @@ -79,7 +78,17 @@ int hal_uart_close(uint32_t port) { } void mp_bluetooth_nimble_hci_uart_process(void) { - mp_bluetooth_nimble_hci_uart_rx(hal_uart_rx_cb, hal_uart_rx_arg); + bool host_wake = mp_bluetooth_hci_controller_woken(); + + int chr; + while ((chr = mp_bluetooth_hci_uart_readchar()) >= 0) { + // printf("UART RX: %02x\n", data); + hal_uart_rx_cb(hal_uart_rx_arg, chr); + } + + if (host_wake) { + mp_bluetooth_hci_controller_sleep_maybe(); + } } #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/extmod/nimble/hal/hal_uart.h b/extmod/nimble/hal/hal_uart.h index 0ef04bc81e..1ff1c17436 100644 --- a/extmod/nimble/hal/hal_uart.h +++ b/extmod/nimble/hal/hal_uart.h @@ -1,3 +1,29 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_NIMBLE_HAL_HAL_UART_H #define MICROPY_INCLUDED_EXTMOD_NIMBLE_HAL_HAL_UART_H @@ -10,10 +36,13 @@ 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. +// --- 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); int hal_uart_close(uint32_t port); +// --- Called by the MicroPython port when UART data is available ------------- +void mp_bluetooth_nimble_hci_uart_process(void); + #endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_HAL_HAL_UART_H diff --git a/extmod/nimble/logcfg/logcfg.h b/extmod/nimble/logcfg/logcfg.h new file mode 100644 index 0000000000..e9023dae65 --- /dev/null +++ b/extmod/nimble/logcfg/logcfg.h @@ -0,0 +1,45 @@ +/** + * This file was generated by Apache newt version: 1.8.0-dev + */ + +#ifndef MICROPY_INCLUDED_EXTMOD_NIMBLE_LOGCFG_LOGCFG_H +#define MICROPY_INCLUDED_EXTMOD_NIMBLE_LOGCFG_LOGCFG_H + +#include "py/mphal.h" +#include "modlog/modlog.h" +#include "log_common/log_common.h" + +#define MICROPY_PY_BLUETOOTH_DIAGNOSTIC_LOGGING (1) + +#if MICROPY_PY_BLUETOOTH_DIAGNOSTIC_LOGGING +#define DFLT_LOG_DEBUG(...) MODLOG_DEBUG(4, __VA_ARGS__) +#else +#define DFLT_LOG_DEBUG(...) IGNORE(__VA_ARGS__) +#endif + +#if MICROPY_PY_BLUETOOTH_DIAGNOSTIC_LOGGING > 1 +#define BLE_HS_LOG_DEBUG(...) MODLOG_DEBUG(4, __VA_ARGS__) +#else +#define BLE_HS_LOG_DEBUG(...) IGNORE(__VA_ARGS__) +#endif + +#define BLE_HS_LOG_INFO(...) MODLOG_INFO(4, __VA_ARGS__) +#define BLE_HS_LOG_WARN(...) MODLOG_WARN(4, __VA_ARGS__) +#define BLE_HS_LOG_ERROR(...) MODLOG_ERROR(4, __VA_ARGS__) +#define BLE_HS_LOG_CRITICAL(...) MODLOG_CRITICAL(4, __VA_ARGS__) +#define BLE_HS_LOG_DISABLED(...) MODLOG_DISABLED(4, __VA_ARGS__) + +#define DFLT_LOG_INFO(...) MODLOG_INFO(0, __VA_ARGS__) +#define DFLT_LOG_WARN(...) MODLOG_WARN(0, __VA_ARGS__) +#define DFLT_LOG_ERROR(...) MODLOG_ERROR(0, __VA_ARGS__) +#define DFLT_LOG_CRITICAL(...) MODLOG_CRITICAL(0, __VA_ARGS__) +#define DFLT_LOG_DISABLED(...) MODLOG_DISABLED(0, __VA_ARGS__) + +#define MFG_LOG_DEBUG(...) IGNORE(__VA_ARGS__) +#define MFG_LOG_INFO(...) IGNORE(__VA_ARGS__) +#define MFG_LOG_WARN(...) IGNORE(__VA_ARGS__) +#define MFG_LOG_ERROR(...) IGNORE(__VA_ARGS__) +#define MFG_LOG_CRITICAL(...) IGNORE(__VA_ARGS__) +#define MFG_LOG_DISABLED(...) MODLOG_DISABLED(128, __VA_ARGS__) + +#endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_LOGCFG_LOGCFG_H diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 36bfa6cccf..61ba3a3aba 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -33,6 +33,7 @@ #include "extmod/nimble/modbluetooth_nimble.h" #include "extmod/modbluetooth.h" +#include "extmod/mpbthci.h" #include "host/ble_hs.h" #include "host/util/util.h" @@ -64,10 +65,11 @@ STATIC int8_t ble_hs_err_to_errno_table[] = { }; STATIC int ble_hs_err_to_errno(int err) { + DEBUG_printf("ble_hs_err_to_errno: %d\n", err); if (!err) { return 0; } - if (0 <= err && err < MP_ARRAY_SIZE(ble_hs_err_to_errno_table) && ble_hs_err_to_errno_table[err]) { + if (err >= 0 && (unsigned)err < MP_ARRAY_SIZE(ble_hs_err_to_errno_table) && ble_hs_err_to_errno_table[err]) { return ble_hs_err_to_errno_table[err]; } else { return MP_EIO; @@ -150,6 +152,12 @@ STATIC void sync_cb(void) { int rc; ble_addr_t addr; + DEBUG_printf("sync_cb: state=%d\n", mp_bluetooth_nimble_ble_state); + + if (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC) { + return; + } + rc = ble_hs_util_ensure_addr(0); // prefer public address if (rc != 0) { // https://mynewt.apache.org/latest/tutorials/ble/eddystone.html#configure-the-nimble-stack-with-an-address @@ -264,11 +272,67 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { return 0; } +#if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY + +// On ports such as ESP32 where we only implement the bindings, then +// the port must provide these functions. +// But for STM32 / Unix-H4, we provide a default implementation of the +// port-specific functionality. +// TODO: In the future if a port ever needs to customise these functions +// then investigate using MP_WEAK or splitting them out to another .c file. + +#include "transport/uart/ble_hci_uart.h" + +void mp_bluetooth_nimble_port_hci_init(void) { + DEBUG_printf("mp_bluetooth_nimble_port_hci_init (nimble default)\n"); + // This calls mp_bluetooth_hci_uart_init (via ble_hci_uart_init --> hal_uart_config --> mp_bluetooth_hci_uart_init). + ble_hci_uart_init(); + mp_bluetooth_hci_controller_init(); +} + +void mp_bluetooth_nimble_port_hci_deinit(void) { + DEBUG_printf("mp_bluetooth_nimble_port_hci_deinit (nimble default)\n"); + mp_bluetooth_hci_controller_deinit(); + mp_bluetooth_hci_uart_deinit(); +} + +void mp_bluetooth_nimble_port_start(void) { + DEBUG_printf("mp_bluetooth_nimble_port_start (nimble default)\n"); + // By default, assume port is already running its own background task (e.g. SysTick on STM32). + // ESP32 runs a FreeRTOS task, Unix has a thread. +} + +// Called when the host stop procedure has completed. +STATIC void ble_hs_shutdown_stop_cb(int status, void *arg) { + (void)status; + (void)arg; + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; +} + +STATIC struct ble_hs_stop_listener ble_hs_shutdown_stop_listener; + +void mp_bluetooth_nimble_port_shutdown(void) { + DEBUG_printf("mp_bluetooth_nimble_port_shutdown (nimble default)\n"); + // By default, just call ble_hs_stop directly and wait for the stack to stop. + + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_STOPPING; + + ble_hs_stop(&ble_hs_shutdown_stop_listener, ble_hs_shutdown_stop_cb, NULL); + + while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { + MICROPY_EVENT_POLL_HOOK + } +} + +#endif // !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY + int mp_bluetooth_init(void) { DEBUG_printf("mp_bluetooth_init\n"); // Clean up if necessary. mp_bluetooth_deinit(); + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_STARTING; + ble_hs_cfg.reset_cb = reset_cb; ble_hs_cfg.sync_cb = sync_cb; ble_hs_cfg.gatts_register_cb = gatts_register_cb; @@ -277,56 +341,70 @@ int mp_bluetooth_init(void) { MP_STATE_PORT(bluetooth_nimble_root_pointers) = m_new0(mp_bluetooth_nimble_root_pointers_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; + #if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY + // Dereference any previous NimBLE mallocs. + MP_STATE_PORT(bluetooth_nimble_memory) = NULL; + #endif - mp_bluetooth_nimble_port_preinit(); + // Allow port (ESP32) to override NimBLE's HCI init. + // Otherwise default implementation above calls ble_hci_uart_init(). + mp_bluetooth_nimble_port_hci_init(); + + // Initialise NimBLE memory and data structures. nimble_port_init(); - mp_bluetooth_nimble_port_postinit(); // By default, just register the default gap/gatt service. ble_svc_gap_init(); ble_svc_gatt_init(); + // Make sure that the HCI UART and event handling task is running. mp_bluetooth_nimble_port_start(); + // Static initialization is complete, can start processing events. + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC; + // Wait for sync callback while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE) { MICROPY_EVENT_POLL_HOOK } + DEBUG_printf("mp_bluetooth_init: ready\n"); return 0; } -// Called when the host stop procedure has completed. -STATIC void ble_hs_shutdown_stop_cb(int status, void *arg) { - mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; -} - -STATIC struct ble_hs_stop_listener ble_hs_shutdown_stop_listener; - void mp_bluetooth_deinit(void) { DEBUG_printf("mp_bluetooth_deinit\n"); if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { return; } - mp_bluetooth_gap_advertise_stop(); - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - mp_bluetooth_gap_scan_stop(); - #endif + // Must call ble_hs_stop() in a port-specific way to stop the background + // task. Default implementation provided above. + if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE) { + mp_bluetooth_gap_advertise_stop(); + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + mp_bluetooth_gap_scan_stop(); + #endif - mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_STOPPING; + DEBUG_printf("mp_bluetooth_deinit: starting port shutdown\n"); - ble_hs_stop(&ble_hs_shutdown_stop_listener, ble_hs_shutdown_stop_cb, NULL); - - while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { - MICROPY_EVENT_POLL_HOOK + mp_bluetooth_nimble_port_shutdown(); + assert(mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF); + } else { + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; } - mp_bluetooth_nimble_port_deinit(); + // Shutdown the HCI controller. + mp_bluetooth_nimble_port_hci_deinit(); MP_STATE_PORT(bluetooth_nimble_root_pointers) = NULL; + + #if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY + // Dereference any previous NimBLE mallocs. + MP_STATE_PORT(bluetooth_nimble_memory) = NULL; + #endif + DEBUG_printf("mp_bluetooth_deinit: shut down\n"); } @@ -485,7 +563,7 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { if (!append) { // Unref any previous service definitions. - for (int i = 0; i < MP_STATE_PORT(bluetooth_nimble_root_pointers)->n_services; ++i) { + for (size_t i = 0; i < MP_STATE_PORT(bluetooth_nimble_root_pointers)->n_services; ++i) { MP_STATE_PORT(bluetooth_nimble_root_pointers)->services[i] = NULL; } MP_STATE_PORT(bluetooth_nimble_root_pointers)->n_services = 0; diff --git a/extmod/nimble/modbluetooth_nimble.h b/extmod/nimble/modbluetooth_nimble.h index f44e1d69dc..7e401781e7 100644 --- a/extmod/nimble/modbluetooth_nimble.h +++ b/extmod/nimble/modbluetooth_nimble.h @@ -43,15 +43,27 @@ typedef struct _mp_bluetooth_nimble_root_pointers_t { enum { MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF, MP_BLUETOOTH_NIMBLE_BLE_STATE_STARTING, + MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC, MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE, MP_BLUETOOTH_NIMBLE_BLE_STATE_STOPPING, }; extern volatile int mp_bluetooth_nimble_ble_state; -void mp_bluetooth_nimble_port_preinit(void); -void mp_bluetooth_nimble_port_postinit(void); -void mp_bluetooth_nimble_port_deinit(void); +// --- Optionally provided by the MicroPython port. --------------------------- +// (default implementations provided by modbluetooth_nimble.c) + +// Tell the port to init the UART and start the HCI controller. +void mp_bluetooth_nimble_port_hci_init(void); + +// Tell the port to deinit the UART and shutdown the HCI controller. +void mp_bluetooth_nimble_port_hci_deinit(void); + +// Tell the port to run its background task (i.e. poll the UART and pump events). void mp_bluetooth_nimble_port_start(void); +// Tell the port to stop its background task. +void mp_bluetooth_nimble_port_shutdown(void); + + #endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_MODBLUETOOTH_NIMBLE_H diff --git a/extmod/nimble/nimble.mk b/extmod/nimble/nimble.mk index 4e1642c98a..f8a68bc6f5 100644 --- a/extmod/nimble/nimble.mk +++ b/extmod/nimble/nimble.mk @@ -2,16 +2,19 @@ ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) -EXTMOD_SRC_C += extmod/nimble/modbluetooth_nimble.c +EXTMOD_DIR = extmod +NIMBLE_EXTMOD_DIR = $(EXTMOD_DIR)/nimble + +EXTMOD_SRC_C += $(NIMBLE_EXTMOD_DIR)/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 +CFLAGS_MOD += -DMICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY=$(MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY) + ifeq ($(MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY),0) NIMBLE_LIB_DIR = lib/mynewt-nimble @@ -82,7 +85,7 @@ LIB_SRC_C += $(addprefix $(NIMBLE_LIB_DIR)/, \ ) EXTMOD_SRC_C += $(addprefix $(NIMBLE_EXTMOD_DIR)/, \ - nimble/npl_os.c \ + nimble/nimble_npl_os.c \ hal/hal_uart.c \ ) @@ -98,7 +101,7 @@ INC += -I$(TOP)/$(NIMBLE_LIB_DIR)/nimble/include INC += -I$(TOP)/$(NIMBLE_LIB_DIR)/nimble/transport/uart/include 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 +$(BUILD)/$(NIMBLE_LIB_DIR)/%.o: CFLAGS += -Wno-maybe-uninitialized -Wno-pointer-arith -Wno-unused-but-set-variable -Wno-format -Wno-sign-compare endif diff --git a/extmod/nimble/nimble/npl_os.c b/extmod/nimble/nimble/nimble_npl_os.c similarity index 85% rename from extmod/nimble/nimble/npl_os.c rename to extmod/nimble/nimble/nimble_npl_os.c index 20c2604054..ba3031a8ff 100644 --- a/extmod/nimble/nimble/npl_os.c +++ b/extmod/nimble/nimble/nimble_npl_os.c @@ -29,16 +29,18 @@ #include "py/runtime.h" #include "nimble/ble.h" #include "nimble/nimble_npl.h" -#include "nimble/nimble_hci_uart.h" +#include "extmod/nimble/hal/hal_uart.h" -#define DEBUG_OS_printf(...) //printf(__VA_ARGS__) -#define DEBUG_MALLOC_printf(...) //printf(__VA_ARGS__) -#define DEBUG_EVENT_printf(...) //printf(__VA_ARGS__) -#define DEBUG_MUTEX_printf(...) //printf(__VA_ARGS__) -#define DEBUG_SEM_printf(...) //printf(__VA_ARGS__) -#define DEBUG_CALLOUT_printf(...) //printf(__VA_ARGS__) -#define DEBUG_TIME_printf(...) //printf(__VA_ARGS__) -#define DEBUG_CRIT_printf(...) //printf(__VA_ARGS__) +#include "extmod/modbluetooth.h" + +#define DEBUG_OS_printf(...) // printf(__VA_ARGS__) +#define DEBUG_MALLOC_printf(...) // printf(__VA_ARGS__) +#define DEBUG_EVENT_printf(...) // printf(__VA_ARGS__) +#define DEBUG_MUTEX_printf(...) // printf(__VA_ARGS__) +#define DEBUG_SEM_printf(...) // printf(__VA_ARGS__) +#define DEBUG_CALLOUT_printf(...) // printf(__VA_ARGS__) +#define DEBUG_TIME_printf(...) // printf(__VA_ARGS__) +#define DEBUG_CRIT_printf(...) // printf(__VA_ARGS__) bool ble_npl_os_started(void) { DEBUG_OS_printf("ble_npl_os_started\n"); @@ -138,7 +140,7 @@ int nimble_sprintf(char *str, const char *fmt, ...) { struct ble_npl_eventq *global_eventq = NULL; -void os_eventq_run_all(void) { +void mp_bluetooth_nimble_os_eventq_run_all(void) { for (struct ble_npl_eventq *evq = global_eventq; evq != NULL; evq = evq->nextq) { while (evq->head != NULL) { struct ble_npl_event *ev = evq->head; @@ -238,23 +240,39 @@ ble_npl_error_t ble_npl_sem_init(struct ble_npl_sem *sem, uint16_t tokens) { ble_npl_error_t ble_npl_sem_pend(struct ble_npl_sem *sem, ble_npl_time_t timeout) { DEBUG_SEM_printf("ble_npl_sem_pend(%p, %u) count=%u\n", sem, (uint)timeout, (uint)sem->count); + + // This is called by NimBLE to synchronously wait for an HCI ACK. The + // corresponding ble_npl_sem_release is called directly by the UART rx + // handler (i.e. hal_uart_rx_cb in extmod/nimble/hal/hal_uart.c). + + // So this implementation just polls the UART until either the semaphore + // is released, or the timeout occurs. + 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. + // This can be called either from code running in NimBLE's "task" + // (i.e. PENDSV) or directly by application code, so for the + // latter case, prevent the "task" from running while we poll the + // UART directly. MICROPY_PY_BLUETOOTH_ENTER mp_bluetooth_nimble_hci_uart_process(); MICROPY_PY_BLUETOOTH_EXIT + if (sem->count != 0) { break; } - __WFI(); + + // Because we're polling, might as well wait for a UART IRQ indicating + // more data available. + mp_bluetooth_nimble_hci_uart_wfi(); } + if (sem->count == 0) { - printf("timeout\n"); + printf("NimBLE: HCI ACK timeout\n"); return BLE_NPL_TIMEOUT; } + DEBUG_SEM_printf("got response in %u ms\n", (int)(mp_hal_ticks_ms() - t0)); } sem->count -= 1; @@ -277,7 +295,7 @@ uint16_t ble_npl_sem_get_count(struct ble_npl_sem *sem) { static struct ble_npl_callout *global_callout = NULL; -void os_callout_process(void) { +void mp_bluetooth_nimble_os_callout_process(void) { uint32_t tnow = mp_hal_ticks_ms(); for (struct ble_npl_callout *c = global_callout; c != NULL; c = c->nextc) { if (!c->active) { @@ -386,12 +404,24 @@ void ble_npl_time_delay(ble_npl_time_t ticks) { /******************************************************************************/ // CRITICAL +// This is used anywhere NimBLE modifies global data structures. +// We need to protect between: +// - A MicroPython VM thread. +// - The NimBLE "task" (e.g. PENDSV on STM32, pthread on Unix). +// On STM32, by disabling PENDSV, we ensure that either: +// - If we're in the NimBLE task, we're exclusive anyway. +// - If we're in a VM thread, we can't be interrupted by the NimBLE task, or switched to another thread. +// On Unix, there's a global mutex. + +// TODO: Both ports currently use MICROPY_PY_BLUETOOTH_ENTER in their implementation, +// maybe this doesn't need to be port-specific? + uint32_t ble_npl_hw_enter_critical(void) { DEBUG_CRIT_printf("ble_npl_hw_enter_critical()\n"); - return raise_irq_pri(15); + return mp_bluetooth_nimble_hci_uart_enter_critical(); } void ble_npl_hw_exit_critical(uint32_t ctx) { DEBUG_CRIT_printf("ble_npl_hw_exit_critical(%u)\n", (uint)ctx); - restore_irq_pri(ctx); + mp_bluetooth_nimble_hci_uart_exit_critical(ctx); } diff --git a/extmod/nimble/nimble/nimble_npl_os.h b/extmod/nimble/nimble/nimble_npl_os.h index 3d886fedb5..5542520661 100644 --- a/extmod/nimble/nimble/nimble_npl_os.h +++ b/extmod/nimble/nimble/nimble_npl_os.h @@ -24,11 +24,15 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_STM32_NIMBLE_NIMBLE_NPL_OS_H -#define MICROPY_INCLUDED_STM32_NIMBLE_NIMBLE_NPL_OS_H +#ifndef MICROPY_INCLUDED_STM32_NIMBLE_NIMBLE_NIMBLE_NPL_OS_H +#define MICROPY_INCLUDED_STM32_NIMBLE_NIMBLE_NIMBLE_NPL_OS_H + +// This is included by nimble/nimble_npl.h -- include that rather than this file directly. #include +// --- Configuration of NimBLE data structures -------------------------------- + #define BLE_NPL_OS_ALIGNMENT (4) #define BLE_NPL_TIME_FOREVER (0xffffffff) @@ -63,4 +67,15 @@ struct ble_npl_sem { volatile uint16_t count; }; +// --- Called by the MicroPython port ----------------------------------------- + +void mp_bluetooth_nimble_os_eventq_run_all(void); +void mp_bluetooth_nimble_os_callout_process(void); + +// --- Must be provided by the MicroPython port ------------------------------- + +void mp_bluetooth_nimble_hci_uart_wfi(void); +uint32_t mp_bluetooth_nimble_hci_uart_enter_critical(void); +void mp_bluetooth_nimble_hci_uart_exit_critical(uint32_t atomic_state); + #endif // MICROPY_INCLUDED_STM32_NIMBLE_NIMBLE_NPL_OS_H diff --git a/extmod/nimble/syscfg/syscfg.h b/extmod/nimble/syscfg/syscfg.h index 6ac420f35e..8a6f2338be 100644 --- a/extmod/nimble/syscfg/syscfg.h +++ b/extmod/nimble/syscfg/syscfg.h @@ -2,12 +2,14 @@ #define MICROPY_INCLUDED_EXTMOD_NIMBLE_SYSCFG_H #include "py/mphal.h" -#include "uart.h" + +#include "mpnimbleport.h" void *nimble_malloc(size_t size); void nimble_free(void *ptr); void *nimble_realloc(void *ptr, size_t size); +// Redirect NimBLE malloc to the GC heap. #define malloc(size) nimble_malloc(size) #define free(ptr) nimble_free(ptr) #define realloc(ptr, size) nimble_realloc(ptr, size) @@ -88,6 +90,7 @@ int nimble_sprintf(char *str, const char *fmt, ...); #define MYNEWT_VAL_BLE_GATT_WRITE_NO_RSP (MYNEWT_VAL_BLE_ROLE_CENTRAL) #define MYNEWT_VAL_BLE_GATT_WRITE_RELIABLE (MYNEWT_VAL_BLE_ROLE_CENTRAL) #define MYNEWT_VAL_BLE_HOST (1) +#define MYNEWT_VAL_BLE_HS_AUTO_START (1) #define MYNEWT_VAL_BLE_HS_DEBUG (0) #define MYNEWT_VAL_BLE_HS_FLOW_CTRL (0) #define MYNEWT_VAL_BLE_HS_FLOW_CTRL_ITVL (1000) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 2cbe9f6be5..dcf4110cfc 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -341,7 +341,7 @@ SRC_C = \ modnetwork.c \ network_lan.c \ network_ppp.c \ - nimble.c \ + mpnimbleport.c \ modsocket.c \ modesp.c \ esp32_partition.c \ diff --git a/ports/esp32/nimble.c b/ports/esp32/mpnimbleport.c similarity index 66% rename from ports/esp32/nimble.c rename to ports/esp32/mpnimbleport.c index 16829732c3..a58fcbdbf4 100644 --- a/ports/esp32/nimble.c +++ b/ports/esp32/mpnimbleport.c @@ -30,28 +30,48 @@ #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE +#define DEBUG_printf(...) // printf("nimble (esp32): " __VA_ARGS__) + #include "esp_nimble_hci.h" #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" +#include "extmod/nimble/modbluetooth_nimble.h" + STATIC void ble_host_task(void *param) { + DEBUG_printf("ble_host_task\n"); nimble_port_run(); // This function will return only when nimble_port_stop() is executed. nimble_port_freertos_deinit(); } -void mp_bluetooth_nimble_port_preinit(void) { +void mp_bluetooth_nimble_port_hci_init(void) { + DEBUG_printf("mp_bluetooth_nimble_port_hci_init\n"); esp_nimble_hci_and_controller_init(); } -void mp_bluetooth_nimble_port_postinit(void) { -} +void mp_bluetooth_nimble_port_hci_deinit(void) { + DEBUG_printf("mp_bluetooth_nimble_port_hci_deinit\n"); -void mp_bluetooth_nimble_port_deinit(void) { - nimble_port_stop(); + esp_nimble_hci_and_controller_deinit(); } void mp_bluetooth_nimble_port_start(void) { + DEBUG_printf("mp_bluetooth_nimble_port_start\n"); nimble_port_freertos_init(ble_host_task); } +void mp_bluetooth_nimble_port_shutdown(void) { + DEBUG_printf("mp_bluetooth_nimble_port_shutdown\n"); + + // Despite the name, these is an ESP32-specific (no other NimBLE ports have these functions). + // Calls ble_hs_stop() and waits for stack shutdown. + nimble_port_stop(); + + // Shuts down the event queue. + nimble_port_deinit(); + + // Mark stack as shutdown. + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; +} + #endif diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index e7d2e2abc2..7b68299e04 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -480,7 +480,7 @@ endif ifeq ($(MICROPY_PY_BLUETOOTH),1) -SRC_C += modbluetooth_hci.c +SRC_C += mpbthciport.c ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) @@ -490,12 +490,13 @@ endif ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) include $(TOP)/extmod/nimble/nimble.mk -SRC_C += nimble.c +SRC_C += mpnimbleport.c endif ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) +MICROPY_BLUETOOTH_BTSTACK_H4 ?= 1 include $(TOP)/extmod/btstack/btstack.mk -SRC_C += btstack.c +SRC_C += mpbtstackport.c endif ifeq ($(MICROPY_PY_NETWORK_CYW43),1) diff --git a/ports/stm32/modbluetooth_hci.c b/ports/stm32/mpbthciport.c similarity index 75% rename from ports/stm32/modbluetooth_hci.c rename to ports/stm32/mpbthciport.c index 54c60adc01..71a9e4fc7f 100644 --- a/ports/stm32/modbluetooth_hci.c +++ b/ports/stm32/mpbthciport.c @@ -26,17 +26,20 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "extmod/modbluetooth_hci.h" +#include "extmod/mpbthci.h" #include "systick.h" #include "pendsv.h" +#include "lib/utils/mpirq.h" #include "py/obj.h" #if MICROPY_PY_BLUETOOTH +#define DEBUG_printf(...) // printf(__VA_ARGS__) + uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; -// Must be provided by the stack bindings. +// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c). extern void mp_bluetooth_hci_poll(void); // Hook for pendsv poller to run this periodically every 128ms @@ -61,34 +64,19 @@ 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; -} - -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) { +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { (void)port; - return 0; -} - -int mp_bluetooth_hci_uart_activate(void) { + (void)baudrate; rfcore_ble_init(); hci_uart_rx_buf_cur = 0; hci_uart_rx_buf_len = 0; return 0; } +int mp_bluetooth_hci_uart_deinit(void) { + return 0; +} + int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { (void)baudrate; return 0; @@ -140,13 +128,16 @@ mp_irq_obj_t mp_bluetooth_hci_uart_irq_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. + // DEBUG_printf("mp_uart_interrupt\n"); + // New HCI data, schedule mp_bluetooth_hci_poll via PENDSV 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) { +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { + DEBUG_printf("mp_bluetooth_hci_uart_init (stm32)\n"); + // 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; @@ -154,16 +145,29 @@ int mp_bluetooth_hci_uart_init(uint32_t port) { 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; + + // This also initialises the UART. + mp_bluetooth_hci_uart_set_baudrate(baudrate); + + return 0; +} + +int mp_bluetooth_hci_uart_deinit(void) { + DEBUG_printf("mp_bluetooth_hci_uart_deinit (stm32)\n"); + // TODO: deinit mp_bluetooth_hci_uart_obj + return 0; } int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { + DEBUG_printf("mp_bluetooth_hci_uart_set_baudrate(%lu) (stm32)\n", baudrate); + if (!baudrate) { + return -1; + } + 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) { // Add IRQ handler for IDLE (i.e. packet finished). uart_irq_config(&mp_bluetooth_hci_uart_obj, false); mp_irq_init(&mp_bluetooth_hci_uart_irq_obj, &uart_irq_methods, MP_OBJ_FROM_PTR(&mp_bluetooth_hci_uart_obj)); @@ -173,13 +177,12 @@ int mp_bluetooth_hci_uart_activate(void) { mp_bluetooth_hci_uart_irq_obj.ishard = true; uart_irq_config(&mp_bluetooth_hci_uart_obj, true); - 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) { + // DEBUG_printf("mp_bluetooth_hci_uart_write (stm32)\n"); + mp_bluetooth_hci_controller_wakeup(); uart_tx_strn(&mp_bluetooth_hci_uart_obj, (void *)buf, len); return 0; @@ -188,7 +191,10 @@ int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { // 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) { + // DEBUG_printf("mp_bluetooth_hci_uart_readchar (stm32)\n"); + if (uart_rx_any(&mp_bluetooth_hci_uart_obj)) { + // DEBUG_printf("... available\n"); return uart_rx_char(&mp_bluetooth_hci_uart_obj); } else { return -1; @@ -197,4 +203,33 @@ int mp_bluetooth_hci_uart_readchar(void) { #endif // defined(STM32WB) +// Default (weak) implementation of the HCI controller interface. +// A driver (e.g. cywbt43.c) can override these for controller-specific +// functionality (i.e. power management). + +MP_WEAK int mp_bluetooth_hci_controller_init(void) { + DEBUG_printf("mp_bluetooth_hci_controller_init (default)\n"); + return 0; +} + +MP_WEAK int mp_bluetooth_hci_controller_deinit(void) { + DEBUG_printf("mp_bluetooth_hci_controller_deinit (default)\n"); + return 0; +} + +MP_WEAK int mp_bluetooth_hci_controller_sleep_maybe(void) { + DEBUG_printf("mp_bluetooth_hci_controller_sleep_maybe (default)\n"); + return 0; +} + +MP_WEAK bool mp_bluetooth_hci_controller_woken(void) { + DEBUG_printf("mp_bluetooth_hci_controller_woken (default)\n"); + return true; +} + +MP_WEAK int mp_bluetooth_hci_controller_wakeup(void) { + DEBUG_printf("mp_bluetooth_hci_controller_wakeup (default)\n"); + return 0; +} + #endif // MICROPY_PY_BLUETOOTH diff --git a/ports/stm32/mpbtstackport.c b/ports/stm32/mpbtstackport.c new file mode 100644 index 0000000000..a031a6a151 --- /dev/null +++ b/ports/stm32/mpbtstackport.c @@ -0,0 +1,107 @@ +/* + * 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/mpbthci.h" +#include "extmod/btstack/btstack_hci_uart.h" +#include "extmod/btstack/modbluetooth_btstack.h" + +// 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 const hci_transport_config_uart_t hci_transport_config_uart = { + HCI_TRANSPORT_CONFIG_UART, + MICROPY_HW_BLE_UART_BAUDRATE, + 3000000, + 0, + NULL, +}; + +void mp_bluetooth_hci_poll(void) { + if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_OFF) { + return; + } + + // Process uart data. + if (mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_HALTING) { + mp_bluetooth_btstack_hci_uart_process(); + } + + // 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(&mp_bluetooth_btstack_hci_uart_block); + hci_init(transport, &hci_transport_config_uart); + + // TODO: Probably not necessary for BCM (we have our own firmware loader in the cyw43 driver), + // 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); + hci_close(); +} + +void mp_bluetooth_btstack_port_start(void) { + hci_power_control(HCI_POWER_ON); +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/ports/stm32/mpbtstackport.h b/ports/stm32/mpbtstackport.h new file mode 100644 index 0000000000..110f921d9a --- /dev/null +++ b/ports/stm32/mpbtstackport.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_STM32_BTSTACK_PORT_H +#define MICROPY_INCLUDED_STM32_BTSTACK_PORT_H + +// To allow MICROPY_HW_BLE_UART_ID to be resolved. + +#include "uart.h" + +#endif // MICROPY_INCLUDED_STM32_BTSTACK_PORT_H diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 14a0589895..e1e1491d64 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -298,6 +298,7 @@ extern const struct _mp_obj_module_t mp_module_onewire; #if MICROPY_BLUETOOTH_NIMBLE struct _mp_bluetooth_nimble_root_pointers_t; +struct _mp_bluetooth_nimble_malloc_t; #define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE void **bluetooth_nimble_memory; struct _mp_bluetooth_nimble_root_pointers_t *bluetooth_nimble_root_pointers; #else #define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE @@ -410,14 +411,20 @@ static inline mp_uint_t disable_irq(void) { #define MICROPY_THREAD_YIELD() #endif -// The LwIP interface must run at a raised IRQ priority -#define MICROPY_PY_LWIP_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); -#define MICROPY_PY_LWIP_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); -#define MICROPY_PY_LWIP_EXIT restore_irq_pri(atomic_state); +// For regular code that wants to prevent "background tasks" from running. +// These background tasks (LWIP, Bluetooth) run in PENDSV context. +#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_PENDSV_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state); -// Bluetooth calls must run at a raised IRQ priority -#define MICROPY_PY_BLUETOOTH_ENTER MICROPY_PY_LWIP_ENTER -#define MICROPY_PY_BLUETOOTH_EXIT MICROPY_PY_LWIP_EXIT +// Prevent the "LWIP task" from running. +#define MICROPY_PY_LWIP_ENTER MICROPY_PY_PENDSV_ENTER +#define MICROPY_PY_LWIP_REENTER MICROPY_PY_PENDSV_REENTER +#define MICROPY_PY_LWIP_EXIT MICROPY_PY_PENDSV_EXIT + +// Prevent the "Bluetooth task" from running (either NimBLE or btstack). +#define MICROPY_PY_BLUETOOTH_ENTER MICROPY_PY_PENDSV_ENTER +#define MICROPY_PY_BLUETOOTH_EXIT MICROPY_PY_PENDSV_EXIT // We need an implementation of the log2 function which is not a macro #define MP_NEED_LOG2 (1) diff --git a/ports/stm32/nimble.c b/ports/stm32/mpnimbleport.c similarity index 56% rename from ports/stm32/nimble.c rename to ports/stm32/mpnimbleport.c index 0d349585fb..1d7c095139 100644 --- a/ports/stm32/nimble.c +++ b/ports/stm32/mpnimbleport.c @@ -31,58 +31,41 @@ #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE -#include "transport/uart/ble_hci_uart.h" #include "host/ble_hs.h" +#include "nimble/nimble_npl.h" -#include "extmod/modbluetooth_hci.h" +#include "extmod/mpbthci.h" #include "extmod/nimble/modbluetooth_nimble.h" -#include "extmod/nimble/nimble/nimble_hci_uart.h" - -extern void os_eventq_run_all(void); -extern void os_callout_process(void); +#include "extmod/nimble/hal/hal_uart.h" +// This implements the Nimble "background task". It's called at PENDSV +// priority, either every 128ms or whenever there's UART data available. +// Because it's called via PENDSV, you can implicitly consider that it +// is surrounded by MICROPY_PY_BLUETOOTH_ENTER / MICROPY_PY_BLUETOOTH_EXIT. void mp_bluetooth_hci_poll(void) { - if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { - return; - } + if (mp_bluetooth_nimble_ble_state >= MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC) { + // Ask NimBLE to process UART data. + mp_bluetooth_nimble_hci_uart_process(); - mp_bluetooth_nimble_hci_uart_process(); - os_callout_process(); - os_eventq_run_all(); -} - -void mp_bluetooth_nimble_port_preinit(void) { - MP_STATE_PORT(bluetooth_nimble_memory) = NULL; - ble_hci_uart_init(); -} - -void mp_bluetooth_nimble_port_postinit(void) { -} - -void mp_bluetooth_nimble_port_deinit(void) { - mp_bluetooth_hci_controller_deactivate(); -} - -void mp_bluetooth_nimble_port_start(void) { - ble_hs_start(); -} - -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(); - - int chr; - while ((chr = mp_bluetooth_hci_uart_readchar()) >= 0) { - // printf("UART RX: %02x\n", data); - rx_cb(rx_arg, chr); - } - - if (host_wake) { - mp_bluetooth_hci_controller_sleep_maybe(); + // Run pending background operations and events, but only after HCI sync. + mp_bluetooth_nimble_os_callout_process(); + mp_bluetooth_nimble_os_eventq_run_all(); } } -void mp_bluetooth_nimble_hci_uart_tx_strn(const char *str, uint len) { - mp_bluetooth_hci_uart_write((const uint8_t *)str, len); +// --- Port-specific helpers for the generic NimBLE bindings. ----------------- + +void mp_bluetooth_nimble_hci_uart_wfi(void) { + __WFI(); +} + +uint32_t mp_bluetooth_nimble_hci_uart_enter_critical(void) { + MICROPY_PY_BLUETOOTH_ENTER + return atomic_state; +} + +void mp_bluetooth_nimble_hci_uart_exit_critical(uint32_t atomic_state) { + MICROPY_PY_BLUETOOTH_EXIT } #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/ports/stm32/mpnimbleport.h b/ports/stm32/mpnimbleport.h new file mode 100644 index 0000000000..f3ee597183 --- /dev/null +++ b/ports/stm32/mpnimbleport.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_STM32_NIMBLE_PORT_H +#define MICROPY_INCLUDED_STM32_NIMBLE_PORT_H + +// To allow MICROPY_HW_BLE_UART_ID to be resolved. + +#include "uart.h" + +#endif // MICROPY_INCLUDED_STM32_NIMBLE_PORT_H diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 3e2fa63a16..a9ba5edfad 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -140,18 +140,19 @@ ifeq ($(HAVE_LIBUSB),1) CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH=1 CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1 -CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK=1 +# Runs in a thread, cannot make calls into the VM. +CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK=0 MICROPY_BLUETOOTH_BTSTACK ?= 1 MICROPY_BLUETOOTH_BTSTACK_USB ?= 1 ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) GIT_SUBMODULES += lib/btstack - include $(TOP)/extmod/btstack/btstack.mk endif endif + endif ifeq ($(MICROPY_PY_FFI),1) @@ -198,7 +199,9 @@ SRC_C = \ alloc.c \ coverage.c \ fatfs_port.c \ - btstack_usb.c \ + mpbtstackport_common.c \ + mpbtstackport_usb.c \ + mpnimbleport.c \ $(SRC_MOD) \ $(wildcard $(VARIANT_DIR)/*.c) diff --git a/ports/unix/mpbtstackport.h b/ports/unix/mpbtstackport.h new file mode 100644 index 0000000000..29433cc7fb --- /dev/null +++ b/ports/unix/mpbtstackport.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_UNIX_BTSTACK_PORT_H +#define MICROPY_INCLUDED_UNIX_BTSTACK_PORT_H + +#define MICROPY_HW_BLE_UART_ID (0) +#define MICROPY_HW_BLE_UART_BAUDRATE (1000000) + +bool mp_bluetooth_hci_poll(void); + +#if MICROPY_BLUETOOTH_BTSTACK_USB +void mp_bluetooth_btstack_port_init_usb(void); +#endif + +#endif // MICROPY_INCLUDED_UNIX_BTSTACK_PORT_H diff --git a/ports/unix/mpbtstackport_common.c b/ports/unix/mpbtstackport_common.c new file mode 100644 index 0000000000..402cf95283 --- /dev/null +++ b/ports/unix/mpbtstackport_common.c @@ -0,0 +1,92 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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/btstack/modbluetooth_btstack.h" + +#include "mpbtstackport.h" + +// Called by the UART polling thread in mpbthciport.c, or by the USB polling thread in mpbtstackport_usb.c. +bool mp_bluetooth_hci_poll(void) { + if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_HALTING) { + // Pretend like we're running in IRQ context (i.e. other things can't be running at the same time). + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + btstack_run_loop_embedded_execute_once(); + MICROPY_END_ATOMIC_SECTION(atomic_state); + + return true; + } + + return false; +} + +// 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(); +} + +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); + + #if MICROPY_BLUETOOTH_BTSTACK_USB + mp_bluetooth_btstack_port_init_usb(); + #endif +} + +void mp_hal_get_mac(int idx, uint8_t buf[6]) { +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/ports/unix/btstack_usb.c b/ports/unix/mpbtstackport_usb.c similarity index 54% rename from ports/unix/btstack_usb.c rename to ports/unix/mpbtstackport_usb.c index ab6a49f39a..28d2c8c543 100644 --- a/ports/unix/btstack_usb.c +++ b/ports/unix/mpbtstackport_usb.c @@ -31,7 +31,7 @@ #include "py/mperrno.h" #include "py/mphal.h" -#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_USB #include "lib/btstack/src/btstack.h" #include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" @@ -40,76 +40,15 @@ #include "extmod/btstack/modbluetooth_btstack.h" +#include "mpbtstackport.h" + #if !MICROPY_PY_THREAD #error Unix btstack requires MICROPY_PY_THREAD #endif STATIC const useconds_t USB_POLL_INTERVAL_US = 1000; -STATIC const uint8_t read_static_address_command_complete_prefix[] = { 0x0e, 0x1b, 0x01, 0x09, 0xfc }; - -STATIC uint8_t local_addr[6] = {0}; -STATIC uint8_t static_address[6] = {0}; -STATIC volatile bool have_addr = false; -STATIC bool using_static_address = false; - -STATIC btstack_packet_callback_registration_t hci_event_callback_registration; - -STATIC void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - if (packet_type != HCI_EVENT_PACKET) { - return; - } - switch (hci_event_packet_get_type(packet)) { - case BTSTACK_EVENT_STATE: - if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) { - return; - } - gap_local_bd_addr(local_addr); - if (using_static_address) { - memcpy(local_addr, static_address, sizeof(local_addr)); - } - have_addr = true; - break; - case HCI_EVENT_COMMAND_COMPLETE: - if (memcmp(packet, read_static_address_command_complete_prefix, sizeof(read_static_address_command_complete_prefix)) == 0) { - reverse_48(&packet[7], static_address); - gap_random_address_set(static_address); - using_static_address = true; - have_addr = true; - } - break; - default: - break; - } -} - -// 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(); -} - -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(); - } - +void mp_bluetooth_btstack_port_init_usb(void) { // MICROPYBTUSB can be a ':'' or '-' separated port list. char *path = getenv("MICROPYBTUSB"); if (path != NULL) { @@ -128,11 +67,7 @@ void mp_bluetooth_btstack_port_init(void) { hci_transport_usb_set_path(usb_path_len, usb_path); } - // hci_dump_open(NULL, HCI_DUMP_STDOUT); hci_init(hci_transport_usb_instance(), NULL); - - hci_event_callback_registration.callback = &packet_handler; - hci_add_event_handler(&hci_event_callback_registration); } STATIC pthread_t bstack_thread_id; @@ -142,9 +77,12 @@ void mp_bluetooth_btstack_port_deinit(void) { // Wait for the poll loop to terminate when the state is set to OFF. pthread_join(bstack_thread_id, NULL); - have_addr = false; } + +// Provided by mpbstackport_common.c. +extern bool mp_bluetooth_hci_poll(void); + STATIC void *btstack_thread(void *arg) { (void)arg; hci_power_control(HCI_POWER_ON); @@ -155,19 +93,15 @@ STATIC void *btstack_thread(void *arg) { // in modbluetooth_btstack.c setting the state back to OFF. // Or, if a timeout results in it being set to TIMEOUT. - while (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { - // Pretend like we're running in IRQ context (i.e. other things can't be running at the same time). - mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); - btstack_run_loop_embedded_execute_once(); - MICROPY_END_ATOMIC_SECTION(atomic_state); + while (true) { + if (!mp_bluetooth_hci_poll()) { + break; + } // The USB transport schedules events to the run loop at 1ms intervals, // and the implementation currently polls rather than selects. usleep(USB_POLL_INTERVAL_US); } - - hci_close(); - return NULL; } @@ -179,13 +113,4 @@ void mp_bluetooth_btstack_port_start(void) { pthread_create(&bstack_thread_id, &attr, &btstack_thread, NULL); } -void mp_hal_get_mac(int idx, uint8_t buf[6]) { - if (idx == MP_HAL_MAC_BDADDR) { - if (!have_addr) { - mp_raise_OSError(MP_ENODEV); - } - memcpy(buf, local_addr, sizeof(local_addr)); - } -} - -#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_USB diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 08605842fd..9e24ab10cd 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -309,9 +309,11 @@ void mp_unix_mark_exec(void); #define MP_STATE_PORT MP_STATE_VM -#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK +#if MICROPY_PY_BLUETOOTH +#if MICROPY_BLUETOOTH_BTSTACK struct _mp_bluetooth_btstack_root_pointers_t; #define MICROPY_BLUETOOTH_ROOT_POINTERS struct _mp_bluetooth_btstack_root_pointers_t *bluetooth_btstack_root_pointers; +#endif #else #define MICROPY_BLUETOOTH_ROOT_POINTERS #endif