Browse Source

extmod/modbluetooth: Refactor stack/hci/driver/port bindings.

Previously the interaction between the different layers of the Bluetooth
stack was different on each port and each stack.  This commit defines
common interfaces between them and implements them for cyw43, btstack,
nimble, stm32, unix.
pull/6405/head
Jim Mussared 4 years ago
committed by Damien George
parent
commit
ed14435a8e
  1. 14
      drivers/cyw43/cywbt.c
  2. 11
      extmod/btstack/btstack.mk
  3. 1
      extmod/btstack/btstack_config.h
  4. 94
      extmod/btstack/btstack_hci_uart.c
  5. 21
      extmod/btstack/btstack_hci_uart.h
  6. 5
      extmod/btstack/modbluetooth_btstack.c
  7. 1
      extmod/btstack/modbluetooth_btstack.h
  8. 38
      extmod/mpbthci.c
  9. 25
      extmod/mpbthci.h
  10. 25
      extmod/nimble/hal/hal_uart.c
  11. 31
      extmod/nimble/hal/hal_uart.h
  12. 45
      extmod/nimble/logcfg/logcfg.h
  13. 122
      extmod/nimble/modbluetooth_nimble.c
  14. 18
      extmod/nimble/modbluetooth_nimble.h
  15. 13
      extmod/nimble/nimble.mk
  16. 64
      extmod/nimble/nimble/nimble_npl_os.c
  17. 19
      extmod/nimble/nimble/nimble_npl_os.h
  18. 5
      extmod/nimble/syscfg/syscfg.h
  19. 2
      ports/esp32/Makefile
  20. 30
      ports/esp32/mpnimbleport.c
  21. 7
      ports/stm32/Makefile
  22. 97
      ports/stm32/mpbthciport.c
  23. 107
      ports/stm32/mpbtstackport.c
  24. 34
      ports/stm32/mpbtstackport.h
  25. 21
      ports/stm32/mpconfigport.h
  26. 61
      ports/stm32/mpnimbleport.c
  27. 34
      ports/stm32/mpnimbleport.h
  28. 9
      ports/unix/Makefile
  29. 39
      ports/unix/mpbtstackport.h
  30. 92
      ports/unix/mpbtstackport_common.c
  31. 101
      ports/unix/mpbtstackport_usb.c
  32. 4
      ports/unix/mpconfigport.h

14
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;

11
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 += \

1
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, ...

94
ports/stm32/btstack.c → 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

21
extmod/nimble/nimble/nimble_hci_uart.h → 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

5
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;

1
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,
};

38
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

25
extmod/modbluetooth_hci.h → 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

25
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

31
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

45
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

122
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
mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_STOPPING;
// 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
ble_hs_stop(&ble_hs_shutdown_stop_listener, ble_hs_shutdown_stop_cb, NULL);
DEBUG_printf("mp_bluetooth_deinit: starting port shutdown\n");
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;

18
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

13
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
CFLAGS_MOD += -DMICROPY_BLUETOOTH_NIMBLE=1
EXTMOD_SRC_C += $(NIMBLE_EXTMOD_DIR)/modbluetooth_nimble.c
NIMBLE_EXTMOD_DIR = extmod/nimble
CFLAGS_MOD += -DMICROPY_BLUETOOTH_NIMBLE=1
# 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

64
extmod/nimble/nimble/npl_os.c → 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);
}

19
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 <stdint.h>
// --- 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

5
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)

2
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 \

30
ports/esp32/nimble.c → 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

7
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)

97
ports/stm32/modbluetooth_hci.c → 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

107
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

34
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

21
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)

61
ports/stm32/nimble.c → 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;
}
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();
}
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();
void mp_bluetooth_nimble_port_postinit(void) {
// 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_port_deinit(void) {
mp_bluetooth_hci_controller_deactivate();
}
// --- Port-specific helpers for the generic NimBLE bindings. -----------------
void mp_bluetooth_nimble_port_start(void) {
ble_hs_start();
void mp_bluetooth_nimble_hci_uart_wfi(void) {
__WFI();
}
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();
}
uint32_t mp_bluetooth_nimble_hci_uart_enter_critical(void) {
MICROPY_PY_BLUETOOTH_ENTER
return atomic_state;
}
void mp_bluetooth_nimble_hci_uart_tx_strn(const char *str, uint len) {
mp_bluetooth_hci_uart_write((const uint8_t *)str, len);
void mp_bluetooth_nimble_hci_uart_exit_critical(uint32_t atomic_state) {
MICROPY_PY_BLUETOOTH_EXIT
}
#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE

34
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

9
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)

39
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

92
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

101
ports/unix/btstack_usb.c → 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

4
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

Loading…
Cancel
Save