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

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy