From 5a6f456dbb8c7f20a59ac1d42c810aaf530d121f Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 18 Jan 2022 15:09:33 -0800 Subject: [PATCH] Add BLE scanning for S3 and C3. Everything else should raise NotImplementedError. First step in #5926 --- devices/ble_hci/common-hal/_bleio/__init__.h | 1 - ports/espressif/CMakeLists.txt | 2 +- ports/espressif/Makefile | 22 +- ports/espressif/common-hal/_bleio/Adapter.c | 334 ++++++++++++++++ ports/espressif/common-hal/_bleio/Adapter.h | 65 +++ ports/espressif/common-hal/_bleio/Attribute.c | 29 ++ ports/espressif/common-hal/_bleio/Attribute.h | 32 ++ .../common-hal/_bleio/Characteristic.c | 123 ++++++ .../common-hal/_bleio/Characteristic.h | 56 +++ .../common-hal/_bleio/CharacteristicBuffer.c | 92 +++++ .../common-hal/_bleio/CharacteristicBuffer.h | 41 ++ .../espressif/common-hal/_bleio/Connection.c | 130 ++++++ .../espressif/common-hal/_bleio/Connection.h | 88 +++++ .../espressif/common-hal/_bleio/Descriptor.c | 94 +++++ .../espressif/common-hal/_bleio/Descriptor.h | 52 +++ .../common-hal/_bleio/PacketBuffer.c | 273 +++++++++++++ .../common-hal/_bleio/PacketBuffer.h | 52 +++ ports/espressif/common-hal/_bleio/Service.c | 94 +++++ ports/espressif/common-hal/_bleio/Service.h | 53 +++ ports/espressif/common-hal/_bleio/UUID.c | 67 ++++ ports/espressif/common-hal/_bleio/UUID.h | 49 +++ ports/espressif/common-hal/_bleio/__init__.c | 67 ++++ ports/espressif/common-hal/_bleio/__init__.h | 42 ++ ports/espressif/common-hal/_bleio/bonding.c | 372 ++++++++++++++++++ ports/espressif/common-hal/_bleio/bonding.h | 91 +++++ .../esp-idf-config/sdkconfig-ble.defaults | 171 ++++++++ .../esp-idf-config/sdkconfig-esp32s3.defaults | 2 +- ports/espressif/mpconfigport.mk | 4 + ports/espressif/supervisor/port.c | 8 + ports/espressif/tools/update_sdkconfig.py | 25 +- ports/nrf/common-hal/_bleio/__init__.h | 1 - ports/nrf/supervisor/port.c | 2 +- py/ringbuf.c | 2 +- py/ringbuf.h | 2 +- shared-bindings/_bleio/__init__.h | 2 + shared-module/_bleio/ScanResults.c | 4 +- shared-module/_bleio/ScanResults.h | 4 +- 37 files changed, 2529 insertions(+), 19 deletions(-) create mode 100644 ports/espressif/common-hal/_bleio/Adapter.c create mode 100644 ports/espressif/common-hal/_bleio/Adapter.h create mode 100644 ports/espressif/common-hal/_bleio/Attribute.c create mode 100644 ports/espressif/common-hal/_bleio/Attribute.h create mode 100644 ports/espressif/common-hal/_bleio/Characteristic.c create mode 100644 ports/espressif/common-hal/_bleio/Characteristic.h create mode 100644 ports/espressif/common-hal/_bleio/CharacteristicBuffer.c create mode 100644 ports/espressif/common-hal/_bleio/CharacteristicBuffer.h create mode 100644 ports/espressif/common-hal/_bleio/Connection.c create mode 100644 ports/espressif/common-hal/_bleio/Connection.h create mode 100644 ports/espressif/common-hal/_bleio/Descriptor.c create mode 100644 ports/espressif/common-hal/_bleio/Descriptor.h create mode 100644 ports/espressif/common-hal/_bleio/PacketBuffer.c create mode 100644 ports/espressif/common-hal/_bleio/PacketBuffer.h create mode 100644 ports/espressif/common-hal/_bleio/Service.c create mode 100644 ports/espressif/common-hal/_bleio/Service.h create mode 100644 ports/espressif/common-hal/_bleio/UUID.c create mode 100644 ports/espressif/common-hal/_bleio/UUID.h create mode 100644 ports/espressif/common-hal/_bleio/__init__.c create mode 100644 ports/espressif/common-hal/_bleio/__init__.h create mode 100644 ports/espressif/common-hal/_bleio/bonding.c create mode 100644 ports/espressif/common-hal/_bleio/bonding.h create mode 100644 ports/espressif/esp-idf-config/sdkconfig-ble.defaults diff --git a/devices/ble_hci/common-hal/_bleio/__init__.h b/devices/ble_hci/common-hal/_bleio/__init__.h index f9caf7c5b5..5c9bbcc979 100644 --- a/devices/ble_hci/common-hal/_bleio/__init__.h +++ b/devices/ble_hci/common-hal/_bleio/__init__.h @@ -35,7 +35,6 @@ #include "hci.h" void bleio_hci_background(void); -void bleio_reset(void); typedef struct { // ble_gap_enc_key_t own_enc; diff --git a/ports/espressif/CMakeLists.txt b/ports/espressif/CMakeLists.txt index 6ad33282f8..afbb51bbad 100644 --- a/ports/espressif/CMakeLists.txt +++ b/ports/espressif/CMakeLists.txt @@ -6,7 +6,7 @@ set(ENV{IDF_PATH} ${CMAKE_SOURCE_DIR}/esp-idf) # The component list here determines what options we get in menuconfig and what the ninja file # can build. -set(COMPONENTS esptool_py soc driver log main esp-tls mbedtls esp_event esp_adc_cal esp_netif esp_wifi lwip wpa_supplicant freertos) +set(COMPONENTS esptool_py soc driver log main esp-tls mbedtls esp_event esp_adc_cal esp_netif esp_wifi lwip wpa_supplicant freertos bt) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(circuitpython) diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index acc8ec5720..877d5dd34f 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -86,6 +86,14 @@ INC += \ -isystem esp-idf \ -isystem esp-idf/components/app_update/include \ -isystem esp-idf/components/bootloader_support/include \ + -isystem esp-idf/components/bt/host/nimble/esp-hci/include \ + -isystem esp-idf/components/bt/host/nimble/nimble/nimble/host/include \ + -isystem esp-idf/components/bt/host/nimble/nimble/nimble/host/services/gap/include \ + -isystem esp-idf/components/bt/host/nimble/nimble/nimble/include \ + -isystem esp-idf/components/bt/host/nimble/nimble/nimble/host/util/include \ + -isystem esp-idf/components/bt/host/nimble/nimble/porting/nimble/include \ + -isystem esp-idf/components/bt/host/nimble/nimble/porting/npl/freertos/include \ + -isystem esp-idf/components/bt/host/nimble/port/include \ -isystem esp-idf/components/driver/include \ -isystem esp-idf/components/driver/$(IDF_TARGET)/include \ -isystem esp-idf/components/$(IDF_TARGET)/include \ @@ -133,6 +141,7 @@ endif CFLAGS += \ -DHAVE_CONFIG_H \ + -DESP_PLATFORM=1 \ -DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\" \ -DUNITY_INCLUDE_CONFIG_H -DWITH_POSIX @@ -298,8 +307,10 @@ else DEBUG_SDKCONFIG = esp-idf-config/sdkconfig-opt.defaults endif -SDKCONFIGS = esp-idf-config/sdkconfig.defaults;$(DEBUG_SDKCONFIG);$(FLASH_SDKCONFIG);$(TARGET_SDKCONFIG);boards/$(BOARD)/sdkconfig - +SDKCONFIGS := esp-idf-config/sdkconfig.defaults;$(DEBUG_SDKCONFIG);$(FLASH_SDKCONFIG);$(TARGET_SDKCONFIG);boards/$(BOARD)/sdkconfig +ifneq ($(CIRCUITPY_BLEIO),0) + SDKCONFIGS := esp-idf-config/sdkconfig-ble.defaults;$(SDKCONFIGS) +endif # create the config headers $(BUILD)/esp-idf/config/sdkconfig.h: boards/$(BOARD)/sdkconfig | $(BUILD)/esp-idf IDF_PATH=$(IDF_PATH) cmake -S . -B $(BUILD)/esp-idf -DSDKCONFIG=$(BUILD)/esp-idf/sdkconfig -DSDKCONFIG_DEFAULTS="$(SDKCONFIGS)" -DCMAKE_TOOLCHAIN_FILE=$(IDF_PATH)/tools/cmake/toolchain-$(IDF_TARGET).cmake -DIDF_TARGET=$(IDF_TARGET) -GNinja @@ -332,6 +343,12 @@ BINARY_WIFI_BLOBS = libcoexist.a libcore.a libespnow.a libmesh.a libnet80211.a l BINARY_BLOBS = esp-idf/components/esp_phy/lib/$(IDF_TARGET)/libphy.a $(addprefix esp-idf/components/esp_wifi/lib/$(IDF_TARGET)/, $(BINARY_WIFI_BLOBS)) ESP_IDF_COMPONENTS_LINK = $(IDF_TARGET_ARCH) app_update bootloader_support driver efuse esp_adc_cal esp_common esp_event esp_hw_support esp_ipc esp_netif esp_pm esp_phy esp_ringbuf esp_rom esp_system esp_timer esp-tls esp_wifi freertos hal heap log lwip mbedtls newlib nvs_flash pthread soc spi_flash vfs wpa_supplicant +ifneq ($(CIRCUITPY_BLEIO),0) + ESP_IDF_COMPONENTS_LINK += bt + BINARY_BLOBS += esp-idf/components/esp_phy/lib/$(IDF_TARGET)/libbtbb.a \ + esp-idf/components/bt/controller/lib_esp32c3_family/$(IDF_TARGET)/libbtdm_app.a +endif + ESP_IDF_COMPONENTS_EXPANDED = $(foreach component, $(ESP_IDF_COMPONENTS_LINK), $(BUILD)/esp-idf/esp-idf/$(component)/lib$(component).a) MBEDTLS_COMPONENTS_LINK = crypto tls x509 @@ -372,6 +389,7 @@ esp-idf-stamp: $(BUILD)/esp-idf/config/sdkconfig.h esp-idf/esp_system/__ldgen_output_sections.ld \ esp-idf/app_update/libapp_update.a \ esp-idf/bootloader_support/libbootloader_support.a \ + esp-idf/bt/libbt.a \ esp-idf/driver/libdriver.a \ esp-idf/efuse/libefuse.a \ esp-idf/esp_adc_cal/libesp_adc_cal.a \ diff --git a/ports/espressif/common-hal/_bleio/Adapter.c b/ports/espressif/common-hal/_bleio/Adapter.c new file mode 100644 index 0000000000..306531473a --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Adapter.c @@ -0,0 +1,334 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2016 Glenn Ruben Bakke + * Copyright (c) 2018 Artur Pacholec + * + * 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 +#include +#include +#include + +#include "py/gc.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "supervisor/shared/bluetooth/bluetooth.h" +#include "supervisor/shared/safe_mode.h" +#include "supervisor/shared/tick.h" +#include "supervisor/usb.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/nvm/ByteArray.h" +#include "shared-bindings/_bleio/Connection.h" +#include "shared-bindings/_bleio/ScanEntry.h" +#include "shared-bindings/time/__init__.h" + +#include "nimble/hci_common.h" +#include "nimble/nimble_port.h" +#include "nimble/nimble_port_freertos.h" +#include "host/ble_gap.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" + +#include "esp_nimble_hci.h" + +#include "esp_log.h" + +const char *TAG = "BLE adapter"; + +bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT]; + +// static void bluetooth_adapter_background(void *data) { +// supervisor_bluetooth_background(); +// bleio_background(); +// } + +bool ble_active = false; + +static void nimble_host_task(void *param) { + nimble_port_run(); + nimble_port_freertos_deinit(); +} + +static TaskHandle_t cp_task = NULL; + +static void _on_sync(void) { + int rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + xTaskNotifyGive(cp_task); +} + +void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled) { + const bool is_enabled = common_hal_bleio_adapter_get_enabled(self); + + // Don't enable or disable twice + if (is_enabled == enabled) { + return; + } + + if (enabled) { + esp_nimble_hci_and_controller_init(); + nimble_port_init(); + // ble_hs_cfg.reset_cb = blecent_on_reset; + ble_hs_cfg.sync_cb = _on_sync; + // ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + ble_svc_gap_device_name_set("CIRCUITPY"); + + cp_task = xTaskGetCurrentTaskHandle(); + + nimble_port_freertos_init(nimble_host_task); + // Wait for sync. + ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(200)); + } else { + nimble_port_stop(); + } +} + +bool common_hal_bleio_adapter_get_enabled(bleio_adapter_obj_t *self) { + return xTaskGetHandle("ble") != NULL; +} + +bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self) { + return NULL; +} + +bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address) { + return false; +} + +mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self) { + return NULL; +} + +void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name) { +} + +static int _scan_event(struct ble_gap_event *event, void *scan_results_in) { + bleio_scanresults_obj_t *scan_results = (bleio_scanresults_obj_t *)scan_results_in; + + if (event->type == BLE_GAP_EVENT_DISC_COMPLETE) { + shared_module_bleio_scanresults_set_done(scan_results, true); + return 0; + } + + if (event->type != BLE_GAP_EVENT_DISC && event->type != BLE_GAP_EVENT_EXT_DISC) { + ESP_LOGI(TAG, "unsupported scan event %d", event->type); + return 0; + } + + if (event->type == BLE_GAP_EVENT_DISC) { + // Legacy advertisement + struct ble_gap_disc_desc *disc = &event->disc; + bool connectable = disc->event_type == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || + disc->event_type == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND; + shared_module_bleio_scanresults_append(scan_results, + supervisor_ticks_ms64(), + connectable, + disc->event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP, + disc->rssi, + disc->addr.val, + disc->addr.type, + disc->data, + disc->length_data); + } else { + // Extended advertisement + struct ble_gap_ext_disc_desc *disc = &event->ext_disc; + shared_module_bleio_scanresults_append(scan_results, + supervisor_ticks_ms64(), + (disc->props & BLE_HCI_ADV_CONN_MASK) != 0, + (disc->props & BLE_HCI_ADV_SCAN_MASK) != 0, + disc->rssi, + disc->addr.val, + disc->addr.type, + disc->data, + disc->length_data); + } + + return 0; +} + +mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t *prefixes, + size_t prefix_length, bool extended, mp_int_t buffer_size, mp_float_t timeout, + mp_float_t interval, mp_float_t window, mp_int_t minimum_rssi, bool active) { + if (self->scan_results != NULL) { + if (!shared_module_bleio_scanresults_get_done(self->scan_results)) { + mp_raise_bleio_BluetoothError(translate("Scan already in progess. Stop with stop_scan.")); + } + self->scan_results = NULL; + } + + self->scan_results = shared_module_bleio_new_scanresults(buffer_size, prefixes, prefix_length, minimum_rssi); + // size_t max_packet_size = extended ? BLE_HCI_MAX_EXT_ADV_DATA_LEN : BLE_HCI_MAX_ADV_DATA_LEN; + + uint8_t own_addr_type; + struct ble_gap_disc_params disc_params; + int rc; + + /* Figure out address to use while advertising (no privacy for now) */ + int privacy = 0; + rc = ble_hs_id_infer_auto(privacy, &own_addr_type); + if (rc != 0) { + ESP_LOGE(TAG, "id error"); + // Error. TODO: Make function to translate into exceptions. + } + + + disc_params.filter_duplicates = 0; + disc_params.passive = !active; + disc_params.itvl = interval / 0.625; + disc_params.window = window / 0.625; + disc_params.filter_policy = 0; + disc_params.limited = 0; + + rc = ble_gap_disc(own_addr_type, timeout * 1000, &disc_params, + _scan_event, self->scan_results); + if (rc != 0) { + ESP_LOGE(TAG, "scan error %d", rc); + } + + return MP_OBJ_FROM_PTR(self->scan_results); +} + +void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self) { + if (self->scan_results == NULL) { + return; + } + ble_gap_disc_cancel(); + shared_module_bleio_scanresults_set_done(self->scan_results, true); + self->scan_results = NULL; +} + +mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) { + mp_raise_NotImplementedError(NULL); + + mp_raise_bleio_BluetoothError(translate("Failed to connect: internal error")); + + return mp_const_none; +} + +uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, + bool connectable, bool anonymous, uint32_t timeout, float interval, + const uint8_t *advertising_data, uint16_t advertising_data_len, + const uint8_t *scan_response_data, uint16_t scan_response_data_len, + mp_int_t tx_power, const bleio_address_obj_t *directed_to) { + mp_raise_NotImplementedError(NULL); + return -1; +} + + +void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, + bool anonymous, uint32_t timeout, mp_float_t interval, + mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo, + mp_int_t tx_power, const bleio_address_obj_t *directed_to) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self) { + return self->current_advertising_data != NULL; +} + +bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) { + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + if (connection->conn_handle != BLEIO_HANDLE_INVALID) { + return true; + } + } + return false; +} + +mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self) { + if (self->connection_objs != NULL) { + return self->connection_objs; + } + size_t total_connected = 0; + mp_obj_t items[BLEIO_TOTAL_CONNECTION_COUNT]; + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + if (connection->conn_handle != BLEIO_HANDLE_INVALID) { + if (connection->connection_obj == mp_const_none) { + connection->connection_obj = bleio_connection_new_from_internal(connection); + } + items[total_connected] = connection->connection_obj; + total_connected++; + } + } + self->connection_objs = mp_obj_new_tuple(total_connected, items); + return self->connection_objs; +} + +void common_hal_bleio_adapter_erase_bonding(bleio_adapter_obj_t *self) { + mp_raise_NotImplementedError(NULL); + // bonding_erase_storage(); +} + +bool common_hal_bleio_adapter_is_bonded_to_central(bleio_adapter_obj_t *self) { + mp_raise_NotImplementedError(NULL); + // return bonding_peripheral_bond_count() > 0; + return false; +} + +void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter) { + // We divide by size_t so that we can scan each 32-bit aligned value to see + // if it is a pointer. This allows us to change the structs without worrying + // about collecting new pointers. + gc_collect_root((void **)adapter, sizeof(bleio_adapter_obj_t) / (sizeof(size_t))); + gc_collect_root((void **)bleio_connections, sizeof(bleio_connections) / (sizeof(size_t))); +} + +void bleio_adapter_reset(bleio_adapter_obj_t *adapter) { + common_hal_bleio_adapter_stop_scan(adapter); + if (adapter->current_advertising_data != NULL) { + common_hal_bleio_adapter_stop_advertising(adapter); + } + + adapter->connection_objs = NULL; + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + // Disconnect all connections cleanly. + if (connection->conn_handle != BLEIO_HANDLE_INVALID) { + common_hal_bleio_connection_disconnect(connection); + } + connection->connection_obj = mp_const_none; + } + + // Wait up to 125 ms (128 ticks) for disconnect to complete. This should be + // greater than most connection intervals. + bool any_connected = false; + uint64_t start_ticks = supervisor_ticks_ms64(); + while (any_connected && supervisor_ticks_ms64() - start_ticks < 128) { + any_connected = false; + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + any_connected |= connection->conn_handle != BLEIO_HANDLE_INVALID; + } + } +} diff --git a/ports/espressif/common-hal/_bleio/Adapter.h b/ports/espressif/common-hal/_bleio/Adapter.h new file mode 100644 index 0000000000..906fc6d5fb --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Adapter.h @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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_ESPRESSIF_COMMON_HAL_BLEIO_ADAPTER_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_ADAPTER_H + +#include "py/obj.h" +#include "py/objtuple.h" + +#include "shared-bindings/_bleio/Connection.h" +#include "shared-bindings/_bleio/ScanResults.h" + +#include "supervisor/background_callback.h" + +#ifndef BLEIO_TOTAL_CONNECTION_COUNT +#define BLEIO_TOTAL_CONNECTION_COUNT 5 +#endif + +#define BLEIO_HANDLE_INVALID 0xffff + +extern bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT]; + +typedef struct { + mp_obj_base_t base; + // Pointer to buffers we maintain so that the data is long lived. + uint8_t *advertising_data; + uint8_t *scan_response_data; + // Pointer to current data. + const uint8_t *current_advertising_data; + bleio_scanresults_obj_t *scan_results; + mp_obj_t name; + mp_obj_tuple_t *connection_objs; + background_callback_t background_callback; + bool user_advertising; +} bleio_adapter_obj_t; + +void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter); +void bleio_adapter_reset(bleio_adapter_obj_t *adapter); + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_ADAPTER_H diff --git a/ports/espressif/common-hal/_bleio/Attribute.c b/ports/espressif/common-hal/_bleio/Attribute.c new file mode 100644 index 0000000000..179a3b58aa --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Attribute.c @@ -0,0 +1,29 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * + * 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 "shared-bindings/_bleio/Attribute.h" + +// Convert a _bleio security mode to a ble_gap_conn_sec_mode_t setting. diff --git a/ports/espressif/common-hal/_bleio/Attribute.h b/ports/espressif/common-hal/_bleio/Attribute.h new file mode 100644 index 0000000000..4612b6c6dd --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Attribute.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * 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_ESPRESSIF_COMMON_HAL_BLEIO_ATTRIBUTE_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_ATTRIBUTE_H + +#include "shared-module/_bleio/Attribute.h" + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_ATTRIBUTE_H diff --git a/ports/espressif/common-hal/_bleio/Characteristic.c b/ports/espressif/common-hal/_bleio/Characteristic.c new file mode 100644 index 0000000000..6c81731aa7 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Characteristic.c @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 + +#include "py/runtime.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Service.h" + +#include "common-hal/_bleio/Adapter.h" +// #include "common-hal/_bleio/bonding.h" + +void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, + uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, + bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, + mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo, + const char *user_description) { + mp_raise_NotImplementedError(NULL); + self->service = service; + self->uuid = uuid; + self->handle = BLEIO_HANDLE_INVALID; + self->props = props; + self->read_perm = read_perm; + self->write_perm = write_perm; + self->initial_value_len = 0; + self->initial_value = NULL; + if (initial_value_bufinfo != NULL) { + // Copy the initial value if it's on the heap. Otherwise it's internal and we may not be able + // to allocate. + self->initial_value_len = initial_value_bufinfo->len; + if (gc_alloc_possible()) { + if (gc_nbytes(initial_value_bufinfo->buf) > 0) { + uint8_t *initial_value = m_malloc(self->initial_value_len, false); + memcpy(initial_value, initial_value_bufinfo->buf, self->initial_value_len); + self->initial_value = initial_value; + } else { + self->initial_value = initial_value_bufinfo->buf; + } + self->descriptor_list = mp_obj_new_list(0, NULL); + } else { + self->initial_value = initial_value_bufinfo->buf; + self->descriptor_list = NULL; + } + } + + // const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX; + // if (max_length < 0 || max_length > max_length_max) { + // mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"), + // max_length_max, fixed_length ? "True" : "False"); + // } + self->max_length = max_length; + self->fixed_length = fixed_length; + + if (service->is_remote) { + self->handle = handle; + } else { + common_hal_bleio_service_add_characteristic(self->service, self, initial_value_bufinfo, user_description); + } +} + +mp_obj_tuple_t *common_hal_bleio_characteristic_get_descriptors(bleio_characteristic_obj_t *self) { + if (self->descriptor_list == NULL) { + return mp_const_empty_tuple; + } + return mp_obj_new_tuple(self->descriptor_list->len, self->descriptor_list->items); +} + +bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self) { + return self->service; +} + +size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t *buf, size_t len) { + return 0; +} + +size_t common_hal_bleio_characteristic_get_max_length(bleio_characteristic_obj_t *self) { + return self->max_length; +} + +void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { +} + +bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self) { + return self->uuid; +} + +bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self) { + return self->props; +} + +void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor) { +} + +void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate) { + +} diff --git a/ports/espressif/common-hal/_bleio/Characteristic.h b/ports/espressif/common-hal/_bleio/Characteristic.h new file mode 100644 index 0000000000..57195ea265 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Characteristic.h @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_ESPRESSIF_COMMON_HAL_BLEIO_CHARACTERISTIC_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_CHARACTERISTIC_H + +#include "shared-bindings/_bleio/Attribute.h" +#include "common-hal/_bleio/Descriptor.h" +#include "shared-module/_bleio/Characteristic.h" +#include "common-hal/_bleio/Service.h" +#include "common-hal/_bleio/UUID.h" + +typedef struct _bleio_characteristic_obj { + mp_obj_base_t base; + // Will be MP_OBJ_NULL before being assigned to a Service. + bleio_service_obj_t *service; + bleio_uuid_obj_t *uuid; + const uint8_t *initial_value; + uint16_t initial_value_len; + uint16_t max_length; + uint16_t handle; + bleio_characteristic_properties_t props; + bleio_attribute_security_mode_t read_perm; + bleio_attribute_security_mode_t write_perm; + mp_obj_list_t *descriptor_list; + uint16_t user_desc_handle; + uint16_t cccd_handle; + uint16_t sccd_handle; + bool fixed_length; +} bleio_characteristic_obj_t; + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_CHARACTERISTIC_H diff --git a/ports/espressif/common-hal/_bleio/CharacteristicBuffer.c b/ports/espressif/common-hal/_bleio/CharacteristicBuffer.c new file mode 100644 index 0000000000..dce2053282 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/CharacteristicBuffer.c @@ -0,0 +1,92 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * + * 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 +#include + +#include "shared/runtime/interrupt_char.h" +#include "py/runtime.h" +#include "py/stream.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Connection.h" +#include "supervisor/shared/tick.h" +#include "common-hal/_bleio/CharacteristicBuffer.h" +#include "shared-bindings/_bleio/CharacteristicBuffer.h" + +void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + uint8_t *buffer, size_t buffer_size, + void *static_handler_entry) { + + mp_raise_NotImplementedError(NULL); + + self->characteristic = characteristic; + self->timeout_ms = timeout * 1000; + + self->ringbuf.buf = (uint8_t *)buffer; + self->ringbuf.size = buffer_size; + self->ringbuf.iget = 0; + self->ringbuf.iput = 0; + +} + +// Assumes that timeout and buffer_size have been validated before call. +void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + size_t buffer_size) { + uint8_t *buffer = m_malloc(buffer_size, true); + _common_hal_bleio_characteristic_buffer_construct(self, characteristic, timeout, buffer, buffer_size, NULL); +} + +uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode) { + + return 0; +} + +uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) { + return 0; +} + +void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) { +} + +bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer_obj_t *self) { + return self->characteristic == NULL; +} + +void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self) { +} + +bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self) { + return self->characteristic != NULL && + self->characteristic->service != NULL && + (!self->characteristic->service->is_remote || + (self->characteristic->service->connection != MP_OBJ_NULL && + common_hal_bleio_connection_get_connected(self->characteristic->service->connection))); +} diff --git a/ports/espressif/common-hal/_bleio/CharacteristicBuffer.h b/ports/espressif/common-hal/_bleio/CharacteristicBuffer.h new file mode 100644 index 0000000000..e5247c20f7 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/CharacteristicBuffer.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * + * 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_ESPRESSIF_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H + +#include "py/ringbuf.h" +#include "shared-bindings/_bleio/Characteristic.h" + +typedef struct { + mp_obj_base_t base; + bleio_characteristic_obj_t *characteristic; + uint32_t timeout_ms; + // Ring buffer storing consecutive incoming values. + ringbuf_t ringbuf; +} bleio_characteristic_buffer_obj_t; + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H diff --git a/ports/espressif/common-hal/_bleio/Connection.c b/ports/espressif/common-hal/_bleio/Connection.c new file mode 100644 index 0000000000..647808fe57 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Connection.c @@ -0,0 +1,130 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 "shared-bindings/_bleio/Connection.h" + +#include +#include + +#include "shared/runtime/interrupt_char.h" +#include "py/gc.h" +#include "py/objlist.h" +#include "py/objstr.h" +#include "py/qstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" +#include "supervisor/shared/tick.h" + +// #include "common-hal/_bleio/bonding.h" + +#include "host/ble_att.h" + + +bool common_hal_bleio_connection_get_paired(bleio_connection_obj_t *self) { + if (self->connection == NULL) { + return false; + } + return self->connection->pair_status == PAIR_PAIRED; +} + +bool common_hal_bleio_connection_get_connected(bleio_connection_obj_t *self) { + if (self->connection == NULL) { + return false; + } + return self->connection->conn_handle != BLEIO_HANDLE_INVALID; +} + +void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self) { +} + +void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond) { + +} + +mp_float_t common_hal_bleio_connection_get_connection_interval(bleio_connection_internal_t *self) { + while (self->conn_params_updating && !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + } + return 0; +} + +// Return the current negotiated MTU length, minus overhead. +mp_int_t common_hal_bleio_connection_get_max_packet_length(bleio_connection_internal_t *self) { + return (self->mtu == 0 ? BLE_ATT_MTU_DFLT : self->mtu) - 3; +} + +void common_hal_bleio_connection_set_connection_interval(bleio_connection_internal_t *self, mp_float_t new_interval) { + self->conn_params_updating = true; +} + +mp_obj_tuple_t *common_hal_bleio_connection_discover_remote_services(bleio_connection_obj_t *self, mp_obj_t service_uuids_whitelist) { + bleio_connection_ensure_connected(self); + // Convert to a tuple and then clear the list so the callee will take ownership. + mp_obj_tuple_t *services_tuple = + mp_obj_new_tuple(self->connection->remote_service_list->len, + self->connection->remote_service_list->items); + mp_obj_list_clear(MP_OBJ_FROM_PTR(self->connection->remote_service_list)); + + return services_tuple; +} + +uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self) { + if (self == NULL || self->connection == NULL) { + return BLEIO_HANDLE_INVALID; + } + return self->connection->conn_handle; +} + +mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *internal) { + if (internal->connection_obj != mp_const_none) { + return internal->connection_obj; + } + bleio_connection_obj_t *connection = m_new_obj(bleio_connection_obj_t); + connection->base.type = &bleio_connection_type; + connection->connection = internal; + internal->connection_obj = connection; + + return MP_OBJ_FROM_PTR(connection); +} + +// Find the connection that uses the given conn_handle. Return NULL if not found. +bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle) { + bleio_connection_internal_t *connection; + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + connection = &bleio_connections[i]; + if (connection->conn_handle == conn_handle) { + return connection; + } + } + + return NULL; +} diff --git a/ports/espressif/common-hal/_bleio/Connection.h b/ports/espressif/common-hal/_bleio/Connection.h new file mode 100644 index 0000000000..2889694fbc --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Connection.h @@ -0,0 +1,88 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_ESPRESSIF_COMMON_HAL_BLEIO_CONNECTION_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_CONNECTION_H + +#include + +#include "py/obj.h" +#include "py/objlist.h" + +#include "common-hal/_bleio/__init__.h" +// #include "common-hal/_bleio/bonding.h" +#include "shared-module/_bleio/Address.h" +#include "common-hal/_bleio/Service.h" + +typedef enum { + PAIR_NOT_PAIRED, + PAIR_WAITING, + PAIR_PAIRED, +} pair_status_t; + +// We split the Connection object into two so that the internal mechanics can live outside of the +// VM. If it were one object, then we'd risk user code seeing a connection object of theirs be +// reused. +typedef struct { + uint16_t conn_handle; + bool is_central; + // Remote services discovered when this peripheral is acting as a client. + mp_obj_list_t *remote_service_list; + // The advertising data and scan response buffers are held by us, not by the SD, so we must + // maintain them and not change it. If we need to change the contents during advertising, + // there are tricks to get the SD to notice (see DevZone - TBS). + // bonding_keys_t bonding_keys; + // EDIV: Encrypted Diversifier: Identifies LTK during legacy pairing. + uint16_t ediv; + volatile pair_status_t pair_status; + uint8_t sec_status; // Internal security status. + mp_obj_t connection_obj; + volatile bool conn_params_updating; + uint16_t mtu; + // Request that CCCD values for this connection be saved, using sys_attr values. + volatile bool do_bond_cccds; + // Request that security key info for this connection be saved. + volatile bool do_bond_keys; + // Time of setting do_bond_ccds: we delay a bit to consolidate multiple CCCD changes + // into one write. Time is currently in ticks_ms. + uint64_t do_bond_cccds_request_time; +} bleio_connection_internal_t; + +typedef struct { + mp_obj_base_t base; + bleio_connection_internal_t *connection; + // The HCI disconnect reason. + uint8_t disconnect_reason; +} bleio_connection_obj_t; + +void bleio_connection_clear(bleio_connection_internal_t *self); + +uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); +mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); +bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_CONNECTION_H diff --git a/ports/espressif/common-hal/_bleio/Descriptor.c b/ports/espressif/common-hal/_bleio/Descriptor.c new file mode 100644 index 0000000000..299fb109ab --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Descriptor.c @@ -0,0 +1,94 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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 "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +#include "host/ble_att.h" + +void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { + mp_raise_NotImplementedError(NULL); + self->characteristic = characteristic; + self->uuid = uuid; + self->handle = BLEIO_HANDLE_INVALID; + self->read_perm = read_perm; + self->write_perm = write_perm; + self->initial_value = mp_obj_new_bytes(initial_value_bufinfo->buf, initial_value_bufinfo->len); + + const mp_int_t max_length_max = BLE_ATT_ATTR_MAX_LEN; + if (max_length < 0 || max_length > max_length_max) { + mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"), + max_length_max, fixed_length ? "True" : "False"); + } + self->max_length = max_length; + self->fixed_length = fixed_length; +} + +bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self) { + return self->uuid; +} + +bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self) { + return self->characteristic; +} + +size_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self, uint8_t *buf, size_t len) { + // Do GATT operations only if this descriptor has been registered + if (self->handle != BLEIO_HANDLE_INVALID) { + uint16_t conn_handle = bleio_connection_get_conn_handle(self->characteristic->service->connection); + (void)conn_handle; + } + + return 0; +} + +void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo) { + // Do GATT operations only if this descriptor has been registered. + if (self->handle != BLEIO_HANDLE_INVALID) { + // uint16_t conn_handle = bleio_connection_get_conn_handle(self->characteristic->service->connection); + if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) { + // false means WRITE_REQ, not write-no-response + // common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, false); + } else { + // Validate data length for local descriptors only. + if (self->fixed_length && bufinfo->len != self->max_length) { + mp_raise_ValueError(translate("Value length != required fixed length")); + } + if (bufinfo->len > self->max_length) { + mp_raise_ValueError(translate("Value length > max_length")); + } + + // common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo); + } + } + +} diff --git a/ports/espressif/common-hal/_bleio/Descriptor.h b/ports/espressif/common-hal/_bleio/Descriptor.h new file mode 100644 index 0000000000..4a2b298ca2 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Descriptor.h @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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_ESPRESSIF_COMMON_HAL_BLEIO_DESCRIPTOR_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_DESCRIPTOR_H + +#include "py/obj.h" + +#include "common-hal/_bleio/UUID.h" + +// Forward declare characteristic because it includes a Descriptor. +struct _bleio_characteristic_obj; + +typedef struct _bleio_descriptor_obj { + mp_obj_base_t base; + // Will be MP_OBJ_NULL before being assigned to a Characteristic. + struct _bleio_characteristic_obj *characteristic; + bleio_uuid_obj_t *uuid; + mp_obj_t initial_value; + uint16_t max_length; + bool fixed_length; + uint16_t handle; + bleio_attribute_security_mode_t read_perm; + bleio_attribute_security_mode_t write_perm; +} bleio_descriptor_obj_t; + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_DESCRIPTOR_H diff --git a/ports/espressif/common-hal/_bleio/PacketBuffer.c b/ports/espressif/common-hal/_bleio/PacketBuffer.c new file mode 100644 index 0000000000..36f9763c1c --- /dev/null +++ b/ports/espressif/common-hal/_bleio/PacketBuffer.c @@ -0,0 +1,273 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 Scott Shawcroft for Adafruit Industries + * + * 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 +#include + +#include "shared/runtime/interrupt_char.h" +#include "py/runtime.h" +#include "py/stream.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Connection.h" +#include "shared-bindings/_bleio/PacketBuffer.h" +#include "supervisor/shared/tick.h" + +#include "supervisor/shared/bluetooth/serial.h" + +#include "host/ble_att.h" + +void _common_hal_bleio_packet_buffer_construct( + bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, + uint32_t *incoming_buffer, size_t incoming_buffer_size, + uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size, + void *static_handler_entry) { + + mp_raise_NotImplementedError(NULL); + self->characteristic = characteristic; + self->client = self->characteristic->service->is_remote; + self->max_packet_size = max_packet_size; + bleio_characteristic_properties_t incoming = self->characteristic->props & (CHAR_PROP_WRITE_NO_RESPONSE | CHAR_PROP_WRITE); + bleio_characteristic_properties_t outgoing = self->characteristic->props & (CHAR_PROP_NOTIFY | CHAR_PROP_INDICATE); + + if (self->client) { + // Swap if we're the client. + bleio_characteristic_properties_t temp = incoming; + incoming = outgoing; + outgoing = temp; + self->conn_handle = bleio_connection_get_conn_handle(MP_OBJ_TO_PTR(self->characteristic->service->connection)); + } else { + self->conn_handle = BLEIO_HANDLE_INVALID; + } + + if (incoming) { + self->ringbuf.buf = (uint8_t *)incoming_buffer; + self->ringbuf.size = incoming_buffer_size; + self->ringbuf.iget = 0; + self->ringbuf.iput = 0; + } + + self->packet_queued = false; + self->pending_index = 0; + self->pending_size = 0; + self->outgoing[0] = outgoing_buffer1; + self->outgoing[1] = outgoing_buffer2; + +} + +void common_hal_bleio_packet_buffer_construct( + bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, + size_t buffer_size, size_t max_packet_size) { + + // Cap the packet size to our implementation limits. + max_packet_size = MIN(max_packet_size, BLE_ATT_ATTR_MAX_LEN - 3); + + bleio_characteristic_properties_t incoming = characteristic->props & (CHAR_PROP_WRITE_NO_RESPONSE | CHAR_PROP_WRITE); + bleio_characteristic_properties_t outgoing = characteristic->props & (CHAR_PROP_NOTIFY | CHAR_PROP_INDICATE); + if (characteristic->service->is_remote) { + // Swap if we're the client. + bleio_characteristic_properties_t temp = incoming; + incoming = outgoing; + outgoing = temp; + } + size_t incoming_buffer_size = 0; + uint32_t *incoming_buffer = NULL; + if (incoming) { + incoming_buffer_size = buffer_size * (sizeof(uint16_t) + max_packet_size); + incoming_buffer = m_malloc(incoming_buffer_size, false); + } + + uint32_t *outgoing1 = NULL; + uint32_t *outgoing2 = NULL; + if (outgoing) { + outgoing1 = m_malloc(max_packet_size, false); + outgoing2 = m_malloc(max_packet_size, false); + } + _common_hal_bleio_packet_buffer_construct(self, characteristic, + incoming_buffer, incoming_buffer_size, + outgoing1, outgoing2, max_packet_size, + NULL); +} + +mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len) { + if (ringbuf_num_filled(&self->ringbuf) < 2) { + return 0; + } + + // Copy received data. Lock out write interrupt handler while copying. + + return 0; +} + +mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, const uint8_t *data, size_t len, uint8_t *header, size_t header_len) { + if (self->outgoing[0] == NULL) { + mp_raise_bleio_BluetoothError(translate("Writes not supported on Characteristic")); + } + if (self->conn_handle == BLEIO_HANDLE_INVALID) { + return -1; + } + mp_int_t outgoing_packet_length = common_hal_bleio_packet_buffer_get_outgoing_packet_length(self); + if (outgoing_packet_length < 0) { + return -1; + } + + mp_int_t total_len = len + header_len; + if (total_len > outgoing_packet_length) { + // Supplied data will not fit in a single BLE packet. + mp_raise_ValueError_varg(translate("Total data to write is larger than %q"), MP_QSTR_outgoing_packet_length); + } + if (total_len > self->max_packet_size) { + // Supplied data will not fit in a single BLE packet. + mp_raise_ValueError_varg(translate("Total data to write is larger than %q"), MP_QSTR_max_packet_size); + } + outgoing_packet_length = MIN(outgoing_packet_length, self->max_packet_size); + + if (len + self->pending_size > (size_t)outgoing_packet_length) { + // No room to append len bytes to packet. Wait until we get a free buffer, + // and keep checking that we haven't been disconnected. + while (self->pending_size != 0 && + self->conn_handle != BLEIO_HANDLE_INVALID && + !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + } + } + if (self->conn_handle == BLEIO_HANDLE_INVALID || + mp_hal_is_interrupted()) { + return -1; + } + + size_t num_bytes_written = 0; + + uint32_t *pending = self->outgoing[self->pending_index]; + + if (self->pending_size == 0) { + memcpy(pending, header, header_len); + self->pending_size += header_len; + num_bytes_written += header_len; + } + memcpy(((uint8_t *)pending) + self->pending_size, data, len); + self->pending_size += len; + num_bytes_written += len; + + + // If no writes are queued then sneak in this data. + if (!self->packet_queued) { + } + return num_bytes_written; +} + +mp_int_t common_hal_bleio_packet_buffer_get_incoming_packet_length(bleio_packet_buffer_obj_t *self) { + // If this PacketBuffer is coming from a remote service via NOTIFY or INDICATE + // the maximum size is what can be sent in one + // BLE packet. But we must be connected to know that value. + // + // Otherwise it can be as long as the characteristic + // will permit, whether or not we're connected. + + if (self->characteristic == NULL) { + return -1; + } + + if (self->characteristic->service != NULL && + self->characteristic->service->is_remote && + (common_hal_bleio_characteristic_get_properties(self->characteristic) & + (CHAR_PROP_INDICATE | CHAR_PROP_NOTIFY))) { + // We are talking to a remote service, and data is arriving via NOTIFY or INDICATE. + if (self->conn_handle != BLEIO_HANDLE_INVALID) { + bleio_connection_internal_t *connection = bleio_conn_handle_to_connection(self->conn_handle); + if (connection) { + return common_hal_bleio_connection_get_max_packet_length(connection); + } + } + // There's no current connection, so we don't know the MTU, and + // we can't tell what the largest incoming packet length would be. + return -1; + } + return self->characteristic->max_length; +} + +mp_int_t common_hal_bleio_packet_buffer_get_outgoing_packet_length(bleio_packet_buffer_obj_t *self) { + // If we are sending data via NOTIFY or INDICATE, the maximum size + // is what can be sent in one BLE packet. But we must be connected + // to know that value. + // + // Otherwise it can be as long as the characteristic + // will permit, whether or not we're connected. + + if (self->characteristic == NULL) { + return -1; + } + + if (self->characteristic->service != NULL && + !self->characteristic->service->is_remote && + (common_hal_bleio_characteristic_get_properties(self->characteristic) & + (CHAR_PROP_INDICATE | CHAR_PROP_NOTIFY))) { + // We are sending to a client, via NOTIFY or INDICATE. + if (self->conn_handle != BLEIO_HANDLE_INVALID) { + bleio_connection_internal_t *connection = bleio_conn_handle_to_connection(self->conn_handle); + if (connection) { + return MIN(MIN(common_hal_bleio_connection_get_max_packet_length(connection), + self->max_packet_size), + self->characteristic->max_length); + } + } + // There's no current connection, so we don't know the MTU, and + // we can't tell what the largest outgoing packet length would be. + return -1; + } + // If we are talking to a remote service, we'll be bound by the MTU. (We don't actually + // know the max size of the remote characteristic.) + if (self->characteristic->service != NULL && + self->characteristic->service->is_remote) { + // We are talking to a remote service so we're writing. + if (self->conn_handle != BLEIO_HANDLE_INVALID) { + bleio_connection_internal_t *connection = bleio_conn_handle_to_connection(self->conn_handle); + if (connection) { + return MIN(common_hal_bleio_connection_get_max_packet_length(connection), + self->max_packet_size); + } + } + } + return MIN(self->characteristic->max_length, self->max_packet_size); +} + +void common_hal_bleio_packet_buffer_flush(bleio_packet_buffer_obj_t *self) { + while ((self->pending_size != 0 || + self->packet_queued) && + self->conn_handle != BLEIO_HANDLE_INVALID && + !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + } +} + +bool common_hal_bleio_packet_buffer_deinited(bleio_packet_buffer_obj_t *self) { + return self->characteristic == NULL; +} + +void common_hal_bleio_packet_buffer_deinit(bleio_packet_buffer_obj_t *self) { + if (!common_hal_bleio_packet_buffer_deinited(self)) { + } +} diff --git a/ports/espressif/common-hal/_bleio/PacketBuffer.h b/ports/espressif/common-hal/_bleio/PacketBuffer.h new file mode 100644 index 0000000000..6ecc1a69b2 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/PacketBuffer.h @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 Scott Shawcroft for Adafruit Industries + * + * 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_ESPRESSIF_COMMON_HAL_BLEIO_PACKETBUFFER_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_PACKETBUFFER_H + +#include "py/ringbuf.h" +#include "shared-bindings/_bleio/Characteristic.h" + +typedef struct { + mp_obj_base_t base; + bleio_characteristic_obj_t *characteristic; + // Ring buffer storing consecutive incoming values. + ringbuf_t ringbuf; + // Two outgoing buffers to alternate between. One will be queued for transmission by the SD and + // the other is waiting to be queued and can be extended. + uint32_t *outgoing[2]; + volatile uint16_t pending_size; + // We remember the conn_handle so we can do a NOTIFY/INDICATE to a client. + // We can find out the conn_handle on a Characteristic write or a CCCD write (but not a read). + volatile uint16_t conn_handle; + uint16_t max_packet_size; + uint8_t pending_index; + uint8_t write_type; + bool client; + bool packet_queued; +} bleio_packet_buffer_obj_t; + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_PACKETBUFFER_H diff --git a/ports/espressif/common-hal/_bleio/Service.c b/ports/espressif/common-hal/_bleio/Service.c new file mode 100644 index 0000000000..beaf930136 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Service.c @@ -0,0 +1,94 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 "common-hal/_bleio/__init__.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/Adapter.h" + +#include "host/ble_gatt.h" + +uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary, mp_obj_list_t *characteristic_list) { + self->handle = 0xFFFF; + self->uuid = uuid; + self->characteristic_list = characteristic_list; + self->is_remote = false; + self->connection = NULL; + self->is_secondary = is_secondary; + + // uint8_t service_type = BLE_GATT_SVC_TYPE_PRIMARY; + // if (is_secondary) { + // service_type = BLE_GATT_SVC_TYPE_SECONDARY; + // } + + return 0; +} + +void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary) { + mp_raise_NotImplementedError(NULL); + _common_hal_bleio_service_construct(self, uuid, is_secondary, + mp_obj_new_list(0, NULL)); +} + +void bleio_service_from_connection(bleio_service_obj_t *self, mp_obj_t connection) { + self->handle = 0xFFFF; + self->uuid = NULL; + self->characteristic_list = mp_obj_new_list(0, NULL); + self->is_remote = true; + self->is_secondary = false; + self->connection = connection; +} + +bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) { + return self->uuid; +} + +mp_obj_tuple_t *common_hal_bleio_service_get_characteristics(bleio_service_obj_t *self) { + return mp_obj_new_tuple(self->characteristic_list->len, self->characteristic_list->items); +} + +bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self) { + return self->is_remote; +} + +bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) { + return self->is_secondary; +} + +void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_buffer_info_t *initial_value_bufinfo, + const char *user_description) { + + #if CIRCUITPY_VERBOSE_BLE + mp_printf(&mp_plat_print, "Char handle %x user %x cccd %x sccd %x\n", characteristic->handle, characteristic->user_desc_handle, characteristic->cccd_handle, characteristic->sccd_handle); + #endif + + mp_obj_list_append(self->characteristic_list, MP_OBJ_FROM_PTR(characteristic)); +} diff --git a/ports/espressif/common-hal/_bleio/Service.h b/ports/espressif/common-hal/_bleio/Service.h new file mode 100644 index 0000000000..6c1688df30 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/Service.h @@ -0,0 +1,53 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_ESPRESSIF_COMMON_HAL_BLEIO_SERVICE_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_SERVICE_H + +#include "py/objlist.h" +#include "common-hal/_bleio/UUID.h" + +typedef struct bleio_service_obj { + mp_obj_base_t base; + // Handle for the local service. + uint16_t handle; + // True if created during discovery. + bool is_remote; + bool is_secondary; + bleio_uuid_obj_t *uuid; + // The connection object is set only when this is a remote service. + // A local service doesn't know the connection. + mp_obj_t connection; + mp_obj_list_t *characteristic_list; + // Range of attribute handles of this remote service. + uint16_t start_handle; + uint16_t end_handle; +} bleio_service_obj_t; + +void bleio_service_from_connection(bleio_service_obj_t *self, mp_obj_t connection); + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_SERVICE_H diff --git a/ports/espressif/common-hal/_bleio/UUID.c b/ports/espressif/common-hal/_bleio/UUID.c new file mode 100644 index 0000000000..b49c5d3992 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/UUID.c @@ -0,0 +1,67 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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 + +#include "py/runtime.h" +#include "common-hal/_bleio/UUID.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" + +// If uuid128 is NULL, this is a Bluetooth SIG 16-bit UUID. +// If uuid128 is not NULL, it's a 128-bit (16-byte) UUID, with bytes 12 and 13 zero'd out, where +// the 16-bit part goes. Those 16 bits are passed in uuid16. +void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, mp_int_t uuid16, const uint8_t uuid128[16]) { + if (uuid128 == NULL) { + ble_uuid_init_from_buf(&self->nimble_ble_uuid, (uint8_t *)&uuid16, 2); + } else { + ble_uuid_init_from_buf(&self->nimble_ble_uuid, uuid128, 16); + } +} + +uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self) { + return self->nimble_ble_uuid.u.type == BLE_UUID_TYPE_16 ? 16 : 128; +} + +uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self) { + return self->nimble_ble_uuid.u16.value; +} + +void common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]) { + memcpy(uuid128, self->nimble_ble_uuid.u128.value, 16); +} + +void common_hal_bleio_uuid_pack_into(bleio_uuid_obj_t *self, uint8_t *buf) { + if (self->nimble_ble_uuid.u.type == BLE_UUID_TYPE_16) { + buf[0] = self->nimble_ble_uuid.u16.value & 0xff; + buf[1] = self->nimble_ble_uuid.u16.value >> 8; + } else { + common_hal_bleio_uuid_get_uuid128(self, buf); + } +} diff --git a/ports/espressif/common-hal/_bleio/UUID.h b/ports/espressif/common-hal/_bleio/UUID.h new file mode 100644 index 0000000000..166088ed96 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/UUID.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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_ESPRESSIF_COMMON_HAL_BLEIO_UUID_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_UUID_H + +#include "py/obj.h" + +#include "host/ble_uuid.h" + +typedef struct { + mp_obj_base_t base; + // Use the native way of storing UUID's: + // - ble_uuid_t.uuid is a 16-bit uuid. + // - ble_uuid_t.type is BLE_UUID_TYPE_BLE if it's a 16-bit Bluetooth SIG UUID. + // or is BLE_UUID_TYPE_VENDOR_BEGIN and higher, which indexes into a table of registered + // 128-bit UUIDs. + ble_uuid_any_t nimble_ble_uuid; +} bleio_uuid_obj_t; + +void bleio_uuid_construct_from_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_uuid); +void bleio_uuid_convert_to_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_uuid); + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_UUID_H diff --git a/ports/espressif/common-hal/_bleio/__init__.c b/ports/espressif/common-hal/_bleio/__init__.c new file mode 100644 index 0000000000..bc3f954f2f --- /dev/null +++ b/ports/espressif/common-hal/_bleio/__init__.c @@ -0,0 +1,67 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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 + +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Connection.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" +#include "supervisor/shared/bluetooth/bluetooth.h" + +#include "common-hal/_bleio/__init__.h" +// #include "common-hal/_bleio/bonding.h" + +// Turn off BLE on a reset or reload. +void bleio_reset() { + // Set this explicitly to save data. + common_hal_bleio_adapter_obj.base.type = &bleio_adapter_type; + if (!common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) { + return; + } + + supervisor_stop_bluetooth(); + bleio_adapter_reset(&common_hal_bleio_adapter_obj); + common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false); + supervisor_start_bluetooth(); +} + +// The singleton _bleio.Adapter object, bound to _bleio.adapter +// It currently only has properties and no state. Inited by bleio_reset +bleio_adapter_obj_t common_hal_bleio_adapter_obj; + +void bleio_background(void) { +} + +void common_hal_bleio_gc_collect(void) { + bleio_adapter_gc_collect(&common_hal_bleio_adapter_obj); +} diff --git a/ports/espressif/common-hal/_bleio/__init__.h b/ports/espressif/common-hal/_bleio/__init__.h new file mode 100644 index 0000000000..965255907f --- /dev/null +++ b/ports/espressif/common-hal/_bleio/__init__.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * 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_ESPRESSIF_COMMON_HAL_BLEIO_INIT_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_INIT_H + +void bleio_background(void); + +// typedef struct { +// ble_gap_enc_key_t own_enc; +// ble_gap_enc_key_t peer_enc; +// ble_gap_id_key_t peer_id; +// } bonding_keys_t; + +// We assume variable length data. +// 20 bytes max (23 - 3). +#define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 3) + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_INIT_H diff --git a/ports/espressif/common-hal/_bleio/bonding.c b/ports/espressif/common-hal/_bleio/bonding.c new file mode 100644 index 0000000000..e7dac3cb33 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/bonding.c @@ -0,0 +1,372 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * + * 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 +#include +#include + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/nvm/ByteArray.h" +#include "supervisor/shared/tick.h" + +#include "bonding.h" + +// Internal flash area reserved for bonding storage. +#define BONDING_PAGES_START_ADDR CIRCUITPY_BLE_CONFIG_START_ADDR +#define BONDING_PAGES_END_ADDR (CIRCUITPY_BLE_CONFIG_START_ADDR + CIRCUITPY_BLE_CONFIG_SIZE) + +// First and last four bytes are magic bytes for id and version. Data is in between. +// 'BD01' +const uint32_t BONDING_FLAG = ('1' | '0' << 8 | 'D' << 16 | 'B' << 24); + +#define BONDING_DATA_START_ADDR (BONDING_PAGES_START_ADDR + sizeof(BONDING_FLAG)) +#define BONDING_DATA_END_ADDR (BONDING_PAGES_END_ADDR - sizeof(BONDING_FLAG)) + +#define BONDING_START_FLAG_ADDR BONDING_PAGES_START_ADDR +#define BONDING_END_FLAG_ADDR BONDING_DATA_END_ADDR + +// Save both system and user service info. +#define SYS_ATTR_FLAGS (BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS | BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS) + +#if BONDING_DEBUG +void bonding_print_block(bonding_block_t *block) { + printf("at 0x%08lx: is_central: %1d, type: 0x%x, ediv: 0x%04x, data_length: %d\n", + (uint32_t)block, block->is_central, block->type, block->ediv, block->data_length); +} + +void bonding_print_keys(bonding_keys_t *keys) { + for (size_t i = 0; i < sizeof(bonding_keys_t); i++) { + printf("%x", ((uint8_t *)keys)[i]); + } + printf("\n"); +} +#endif + +STATIC size_t compute_block_size(uint16_t data_length) { + // Round data size up to the nearest 32-bit address. + return sizeof(bonding_block_t) + ((data_length + 3) & ~0x3); +} + +void bonding_erase_storage(void) { + // Erase all pages in the bonding area. + for (uint32_t page_address = BONDING_PAGES_START_ADDR; + page_address < BONDING_PAGES_END_ADDR; + page_address += FLASH_PAGE_SIZE) { + // Argument is page number, not address. + sd_flash_page_erase_sync(page_address / FLASH_PAGE_SIZE); + } + // Write marker words at the beginning and the end of the bonding area. + uint32_t flag = BONDING_FLAG; + sd_flash_write_sync((uint32_t *)BONDING_START_FLAG_ADDR, &flag, 1); + sd_flash_write_sync((uint32_t *)BONDING_END_FLAG_ADDR, &flag, 1); +} + +// Given NULL to start or block address, return the address of the next valid block. +// The last block returned is the unused block at the end. +// Return NULL if we have run off the end of the bonding space. + +STATIC bonding_block_t *next_block(bonding_block_t *block) { + while (1) { + // Advance to next block. + if (block == NULL) { + return (bonding_block_t *)BONDING_DATA_START_ADDR; + } else if (block->type == BLOCK_UNUSED) { + // Already at last block (the unused block). + return NULL; + } + + // Advance to next block. + block = (bonding_block_t *)((uint8_t *)block + compute_block_size(block->data_length)); + + if (block >= (bonding_block_t *)BONDING_DATA_END_ADDR) { + // Went past end of bonding space. + return NULL; + } + if (block->type != BLOCK_INVALID) { + // Found an empty or a valid block. + return block; + } + // Invalid block (was erased); try again. + } +} + +// Find the block with given is_central, type and ediv value. +// If type == BLOCK_UNUSED, ediv is ignored and the the sole unused block at the end is returned. +// If not found, return NULL. +STATIC bonding_block_t *find_existing_block(bool is_central, bonding_block_type_t type, uint16_t ediv) { + bonding_block_t *block = NULL; + while (1) { + block = next_block(block); + if (block == NULL) { + return NULL; + } + // If types match, and block is unused, just return it. + // Otherwise check that is_central and ediv match. + if (type == block->type) { + if (type == BLOCK_UNUSED || + (is_central == block->is_central && ediv == block->ediv)) { + return block; + } + } + } +} + +size_t bonding_peripheral_bond_count(void) { + bonding_block_t *block = NULL; + size_t count = 0; + while (1) { + block = next_block(block); + if (block == NULL) { + return count; + } + if (block->type != BLOCK_UNUSED && block->type != BLOCK_INVALID && !block->is_central) { + count++; + } + } +} + +// Get an empty block large enough to store data_length data. +STATIC bonding_block_t *find_unused_block(uint16_t data_length) { + bonding_block_t *unused_block = find_existing_block(true, BLOCK_UNUSED, EDIV_INVALID); + // If no more room, erase all existing blocks and start over. + if (!unused_block || + (uint8_t *)unused_block + compute_block_size(data_length) >= (uint8_t *)BONDING_DATA_END_ADDR) { + bonding_erase_storage(); + unused_block = (bonding_block_t *)BONDING_DATA_START_ADDR; + } + return unused_block; +} + +// Set the header word to all 0's, to mark the block as invalid. +// We don't change data_length, so we can still skip over this block. +STATIC void invalidate_block(bonding_block_t *block) { + uint32_t zero = 0; + sd_flash_write_sync((uint32_t *)block, &zero, 1); +} + +// Write bonding block header. +STATIC void write_block_header(bonding_block_t *dest_block, bonding_block_t *source_block_header) { + sd_flash_write_sync((uint32_t *)dest_block, (uint32_t *)source_block_header, sizeof(bonding_block_t) / 4); +} + +// Write variable-length data at end of bonding block. +STATIC void write_block_data(bonding_block_t *dest_block, uint8_t *data, uint16_t data_length) { + // Minimize the number of writes. Datasheet says no more than two writes per word before erasing again. + + // Start writing after the current header. + uint32_t *flash_word_p = (uint32_t *)((uint8_t *)dest_block + sizeof(bonding_block_t)); + while (1) { + uint32_t word = 0xffffffff; + memcpy(&word, data, data_length >= 4 ? 4 : data_length); + sd_flash_write_sync(flash_word_p, &word, 1); + if (data_length <= 4) { + break; + } + data_length -= 4; + data += 4; + // Increment by word size. + flash_word_p++; + } +} + +STATIC void write_sys_attr_block(bleio_connection_internal_t *connection) { + uint16_t length = 0; + // First find out how big a buffer we need, then fetch the data. + if (sd_ble_gatts_sys_attr_get(connection->conn_handle, NULL, &length, SYS_ATTR_FLAGS) != NRF_SUCCESS) { + return; + } + uint8_t sys_attr[length]; + if (sd_ble_gatts_sys_attr_get(connection->conn_handle, sys_attr, &length, SYS_ATTR_FLAGS) != NRF_SUCCESS) { + return; + } + + // Is there an existing sys_attr block that matches the current sys_attr data? + bonding_block_t *existing_block = + find_existing_block(connection->is_central, BLOCK_SYS_ATTR, connection->ediv); + if (existing_block) { + if (length == existing_block->data_length && + memcmp(sys_attr, existing_block->data, length) == 0) { + // Identical block found. No need to store again. + return; + } + // Data doesn't match. Invalidate block and store a new one. + invalidate_block(existing_block); + } + + bonding_block_t block_header = { + .is_central = connection->is_central, + .type = BLOCK_SYS_ATTR, + .ediv = connection->ediv, + .conn_handle = connection->conn_handle, + .data_length = length, + }; + bonding_block_t *new_block = find_unused_block(length); + write_block_header(new_block, &block_header); + write_block_data(new_block, sys_attr, length); + return; +} + +STATIC void write_keys_block(bleio_connection_internal_t *connection) { + uint16_t const ediv = connection->is_central + ? connection->bonding_keys.peer_enc.master_id.ediv + : connection->bonding_keys.own_enc.master_id.ediv; + + // Is there an existing keys block that matches the ediv? + bonding_block_t *existing_block = find_existing_block(connection->is_central, BLOCK_KEYS, ediv); + if (existing_block) { + if (existing_block->data_length == sizeof(bonding_keys_t) && + memcmp(existing_block->data, &connection->bonding_keys, sizeof(bonding_keys_t)) == 0) { + // Identical block found. No need to store again. + return; + } + // Data doesn't match. Invalidate block and store a new one. + invalidate_block(existing_block); + } + // Invalidate any existing blocks that match the peer address. + existing_block = next_block(NULL); + while (existing_block != NULL) { + if (existing_block->type == BLOCK_KEYS && connection->is_central == existing_block->is_central && + existing_block->data_length == sizeof(bonding_keys_t)) { + const ble_gap_addr_t *existing_peer = &((const bonding_keys_t *)existing_block->data)->peer_id.id_addr_info; + const ble_gap_addr_t *connecting_peer = &connection->bonding_keys.peer_id.id_addr_info; + if (memcmp(existing_peer->addr, connecting_peer->addr, 6) == 0 && + memcmp(existing_block->data, &connection->bonding_keys, sizeof(bonding_keys_t)) != 0) { + // Mismatched block found. Invalidate it. + invalidate_block(existing_block); + } + } + existing_block = next_block(existing_block); + } + + bonding_block_t block_header = { + .is_central = connection->is_central, + .type = BLOCK_KEYS, + .ediv = ediv, + .conn_handle = connection->conn_handle, + .data_length = sizeof(bonding_keys_t), + }; + bonding_block_t *new_block = find_unused_block(sizeof(bonding_keys_t)); + write_block_header(new_block, &block_header); + write_block_data(new_block, (uint8_t *)&connection->bonding_keys, sizeof(bonding_keys_t)); +} + +void bonding_clear_keys(bonding_keys_t *bonding_keys) { + memset((uint8_t *)bonding_keys, 0, sizeof(bonding_keys_t)); +} + +void bonding_reset(void) { + if (BONDING_FLAG != *((uint32_t *)BONDING_START_FLAG_ADDR) || + BONDING_FLAG != *((uint32_t *)BONDING_END_FLAG_ADDR)) { + bonding_erase_storage(); + } +} + +// Write bonding blocks to flash. Requests have been queued during evt handlers. +void bonding_background(void) { + // A paired connection will request that its keys and CCCD values be stored. + // The CCCD store whenever a CCCD value is written. + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + + // Wait at least one second before saving CCCD, to consolidate + // writes that involve multiple CCCDs. For instance, for HID, + // three CCCD's are set in short succession by the HID client. + if (connection->do_bond_cccds) { + uint64_t current_ticks_ms = supervisor_ticks_ms64(); + if (current_ticks_ms - connection->do_bond_cccds_request_time >= 1000) { + write_sys_attr_block(connection); + connection->do_bond_cccds = false; + } + } + + if (connection->do_bond_keys) { + write_keys_block(connection); + connection->do_bond_keys = false; + } + } +} + +bool bonding_load_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv) { + bonding_block_t *block = find_existing_block(is_central, BLOCK_SYS_ATTR, ediv); + if (block == NULL) { + return false; + } + + return NRF_SUCCESS == + sd_ble_gatts_sys_attr_set(conn_handle, block->data, block->data_length, SYS_ATTR_FLAGS); +} + +bool bonding_load_keys(bool is_central, uint16_t ediv, bonding_keys_t *bonding_keys) { + bonding_block_t *block = find_existing_block(is_central, BLOCK_KEYS, ediv); + if (block == NULL) { + return false; + } + if (sizeof(bonding_keys_t) != block->data_length) { + // bonding_keys_t is a fixed length, so lengths should match. + return false; + } + + memcpy(bonding_keys, block->data, block->data_length); + return true; +} + +size_t bonding_load_identities(bool is_central, const ble_gap_id_key_t **keys, size_t max_length) { + bonding_block_t *block = NULL; + size_t len = 0; + while (len < max_length) { + block = next_block(block); + if (block == NULL) { + return len; + } + if (block->type != BLOCK_UNUSED && + block->type != BLOCK_INVALID && + block->is_central == is_central) { + if (sizeof(bonding_keys_t) != block->data_length) { + // bonding_keys_t is a fixed length, so lengths should match. + return len; + } + const bonding_keys_t *key_set = (const bonding_keys_t *)block->data; + keys[len] = &key_set->peer_id; + len++; + } + } + return len; +} + +const ble_gap_enc_key_t *bonding_load_peer_encryption_key(bool is_central, const ble_gap_addr_t *peer) { + bonding_block_t *block = next_block(NULL); + while (block != NULL) { + if (block->type == BLOCK_KEYS && block->is_central == is_central) { + const bonding_keys_t *key_set = (const bonding_keys_t *)block->data; + if (memcmp(key_set->peer_id.id_addr_info.addr, peer->addr, 6) == 0) { + return &key_set->peer_enc; + } + } + block = next_block(block); + } + return NULL; +} diff --git a/ports/espressif/common-hal/_bleio/bonding.h b/ports/espressif/common-hal/_bleio/bonding.h new file mode 100644 index 0000000000..9f04f10d4f --- /dev/null +++ b/ports/espressif/common-hal/_bleio/bonding.h @@ -0,0 +1,91 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * + * 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_ESPRESSIF_COMMON_HAL_BLEIO_BONDING_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_BONDING_H + +#include +#include +#include + +#include "common-hal/_bleio/__init__.h" + +#define EDIV_INVALID (0xffff) + +#define BONDING_DEBUG (1) +#if BONDING_DEBUG + #define BONDING_DEBUG_PRINTF(...) printf(__VA_ARGS__) + #define BONDING_DEBUG_PRINT_BLOCK(block) bonding_print_block(block) + #define BONDING_DEBUG_PRINT_KEYS(keys) bonding_print_keys(keys) +#else + #define BONDING_DEBUG_PRINTF(...) + #define BONDING_DEBUG_PRINT_BLOCK(block) + #define BONDING_DEBUG_PRINT_KEYS(keys) +#endif + +// Bonding data is stored in variable-length blocks consecutively in +// erased flash (all 1's). The blocks are 32-bit aligned, though the +// data may be any number of bytes. We hop through the blocks using +// the size field to find the next block. When we hit a word that is +// all 1's, we have reached the end of the blocks. We can write a new +// block there. + +typedef enum { + BLOCK_INVALID = 0, // Ignore this block + BLOCK_KEYS = 1, // Block contains bonding keys. + BLOCK_SYS_ATTR = 2, // Block contains sys_attr values (CCCD settings, etc.). + BLOCK_UNUSED = 0xff, // Initial erased value. +} bonding_block_type_t; + +typedef struct { + bool is_central : 1; // 1 if data is for a central role. + uint16_t reserved : 7; // Not currently used + bonding_block_type_t type : 8; // What kind of data is stored in. + uint16_t ediv; // ediv value; used as a lookup key. + uint16_t conn_handle; // Connection handle: used when a BLOCK_SYS_ATTR is queued to write. + // Not used as a key, etc. + uint16_t data_length; // Length of data in bytes, including ediv, not including padding. + // End of block header. 32-bit boundary here. + uint8_t data[]; // Rest of data in the block. Needs to be 32-bit aligned. + // Block is padded to 32-bit alignment. +} bonding_block_t; + +void bonding_background(void); +void bonding_erase_storage(void); +void bonding_reset(void); +void bonding_clear_keys(bonding_keys_t *bonding_keys); +bool bonding_load_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv); +bool bonding_load_keys(bool is_central, uint16_t ediv, bonding_keys_t *bonding_keys); +const ble_gap_enc_key_t *bonding_load_peer_encryption_key(bool is_central, const ble_gap_addr_t *peer); +size_t bonding_load_identities(bool is_central, const ble_gap_id_key_t **keys, size_t max_length); +size_t bonding_peripheral_bond_count(void); + +#if BONDING_DEBUG +void bonding_print_block(bonding_block_t *); +void bonding_print_keys(bonding_keys_t *); +#endif + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_BONDING_H diff --git a/ports/espressif/esp-idf-config/sdkconfig-ble.defaults b/ports/espressif/esp-idf-config/sdkconfig-ble.defaults new file mode 100644 index 0000000000..476b6a32d9 --- /dev/null +++ b/ports/espressif/esp-idf-config/sdkconfig-ble.defaults @@ -0,0 +1,171 @@ +# +# Bluetooth +# +CONFIG_BT_ENABLED=y +# end of Bluetooth + +CONFIG_BT_CTRL_MODE_EFF=1 +# +# Bluetooth controller +# +CONFIG_BT_CTRL_BLE_MAX_ACT=10 +CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=10 +CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0 +CONFIG_BT_CTRL_PINNED_TO_CORE_0=y +# CONFIG_BT_CTRL_PINNED_TO_CORE_1 is not set +CONFIG_BT_CTRL_PINNED_TO_CORE=0 +CONFIG_BT_CTRL_HCI_MODE_VHCI=y +# CONFIG_BT_CTRL_HCI_MODE_UART_H4 is not set +CONFIG_BT_CTRL_HCI_TL=1 +CONFIG_BT_CTRL_ADV_DUP_FILT_MAX=30 +# CONFIG_BT_CTRL_HW_CCA is not set +CONFIG_BT_CTRL_HW_CCA_EFF=0 +CONFIG_BT_CTRL_CE_LENGTH_TYPE_ORIG=y +# CONFIG_BT_CTRL_CE_LENGTH_TYPE_CE is not set +# CONFIG_BT_CTRL_CE_LENGTH_TYPE_SD is not set +CONFIG_BT_CTRL_CE_LENGTH_TYPE_EFF=0 +CONFIG_BT_CTRL_TX_ANTENNA_INDEX_0=y +# CONFIG_BT_CTRL_TX_ANTENNA_INDEX_1 is not set +CONFIG_BT_CTRL_TX_ANTENNA_INDEX_EFF=0 +CONFIG_BT_CTRL_RX_ANTENNA_INDEX_0=y +# CONFIG_BT_CTRL_RX_ANTENNA_INDEX_1 is not set +CONFIG_BT_CTRL_RX_ANTENNA_INDEX_EFF=0 +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N27 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N24 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N21 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N18 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N15 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N12 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N9 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N6 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N3 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N0 is not set +CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P3=y +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P6 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P9 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P12 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P15 is not set +# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P18 is not set +CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_EFF=10 +CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y +CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM=100 +CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD=20 +# CONFIG_BT_CTRL_BLE_SCAN_DUPL is not set +# CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EN is not set +CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_DIS=y +CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EFF=0 +# end of Bluetooth controller + +# CONFIG_BT_CTRL_MODEM_SLEEP is not set +# +# Bluetooth controller +# +CONFIG_BT_CTRL_SLEEP_MODE_EFF=0 +CONFIG_BT_CTRL_SLEEP_CLOCK_EFF=0 +CONFIG_BT_CTRL_HCI_TL_EFF=1 +# CONFIG_BT_CTRL_AGC_RECORRECT_EN is not set +# end of Bluetooth controller + +# CONFIG_BT_BLUEDROID_ENABLED is not set +# +# Bluetooth +# +CONFIG_BT_NIMBLE_ENABLED=y +# CONFIG_BT_CONTROLLER_ONLY is not set +# end of Bluetooth + +CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y +# +# NimBLE Options +# +# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set +# CONFIG_BT_NIMBLE_LOG_LEVEL_NONE is not set +# CONFIG_BT_NIMBLE_LOG_LEVEL_ERROR is not set +# CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING is not set +CONFIG_BT_NIMBLE_LOG_LEVEL_INFO=y +# CONFIG_BT_NIMBLE_LOG_LEVEL_DEBUG is not set +CONFIG_BT_NIMBLE_LOG_LEVEL=1 +CONFIG_BT_NIMBLE_MAX_CONNECTIONS=3 +CONFIG_BT_NIMBLE_MAX_BONDS=3 +CONFIG_BT_NIMBLE_MAX_CCCDS=8 +CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM=0 +# CONFIG_BT_NIMBLE_PINNED_TO_CORE_0 is not set +CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=y +CONFIG_BT_NIMBLE_PINNED_TO_CORE=1 +CONFIG_BT_NIMBLE_TASK_STACK_SIZE=4096 +CONFIG_BT_NIMBLE_ROLE_CENTRAL=y +CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=y +CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y +CONFIG_BT_NIMBLE_ROLE_OBSERVER=y +CONFIG_BT_NIMBLE_NVS_PERSIST=y +CONFIG_BT_NIMBLE_SM_LEGACY=y +CONFIG_BT_NIMBLE_SM_SC=y +# CONFIG_BT_NIMBLE_DEBUG is not set +# CONFIG_BT_NIMBLE_SM_SC_DEBUG_KEYS is not set +CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="nimble" +CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31 +CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=256 +CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE=0 +CONFIG_BT_NIMBLE_ACL_BUF_COUNT=20 +CONFIG_BT_NIMBLE_ACL_BUF_SIZE=255 +CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 +CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT=30 +CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT=8 +CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT=12 +# CONFIG_BT_NIMBLE_HS_FLOW_CTRL is not set +CONFIG_BT_NIMBLE_RPA_TIMEOUT=900 +# CONFIG_BT_NIMBLE_MESH is not set +CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS=y +CONFIG_BT_NIMBLE_HS_STOP_TIMEOUT_MS=2000 +# CONFIG_BT_NIMBLE_HOST_BASED_PRIVACY is not set +CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=y +CONFIG_BT_NIMBLE_MAX_CONN_REATTEMPT=3 +CONFIG_BT_NIMBLE_EXT_ADV=y +CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=1 +CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN=1650 +CONFIG_BT_NIMBLE_ENABLE_PERIODIC_ADV=y +CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=1 +# CONFIG_BT_NIMBLE_BLUFI_ENABLE is not set +CONFIG_BT_NIMBLE_USE_ESP_TIMER=y +# end of NimBLE Options + +# CONFIG_BLUEDROID_ENABLED is not set +# +# Deprecated options for backward compatibility +# +CONFIG_NIMBLE_ENABLED=y +CONFIG_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y +# CONFIG_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set +CONFIG_NIMBLE_MAX_CONNECTIONS=3 +CONFIG_NIMBLE_MAX_BONDS=3 +CONFIG_NIMBLE_MAX_CCCDS=8 +CONFIG_NIMBLE_L2CAP_COC_MAX_NUM=0 +CONFIG_NIMBLE_PINNED_TO_CORE_0=y +# CONFIG_NIMBLE_PINNED_TO_CORE_1 is not set +CONFIG_NIMBLE_PINNED_TO_CORE=0 +CONFIG_NIMBLE_TASK_STACK_SIZE=4096 +CONFIG_NIMBLE_ROLE_CENTRAL=y +CONFIG_NIMBLE_ROLE_PERIPHERAL=y +CONFIG_NIMBLE_ROLE_BROADCASTER=y +CONFIG_NIMBLE_ROLE_OBSERVER=y +CONFIG_NIMBLE_NVS_PERSIST=y +CONFIG_NIMBLE_SM_LEGACY=y +CONFIG_NIMBLE_SM_SC=y +# CONFIG_NIMBLE_DEBUG is not set +# CONFIG_NIMBLE_SM_SC_DEBUG_KEYS is not set +CONFIG_NIMBLE_SVC_GAP_DEVICE_NAME="nimble" +CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31 +CONFIG_NIMBLE_ATT_PREFERRED_MTU=256 +CONFIG_NIMBLE_SVC_GAP_APPEARANCE=0 +CONFIG_NIMBLE_ACL_BUF_COUNT=20 +CONFIG_NIMBLE_ACL_BUF_SIZE=255 +CONFIG_NIMBLE_HCI_EVT_BUF_SIZE=70 +CONFIG_NIMBLE_HCI_EVT_HI_BUF_COUNT=30 +CONFIG_NIMBLE_HCI_EVT_LO_BUF_COUNT=8 +CONFIG_NIMBLE_MSYS1_BLOCK_COUNT=12 +# CONFIG_NIMBLE_HS_FLOW_CTRL is not set +CONFIG_NIMBLE_RPA_TIMEOUT=900 +# CONFIG_NIMBLE_MESH is not set +CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS=y +CONFIG_SW_COEXIST_ENABLE=y +# end of Deprecated options for backward compatibility diff --git a/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults b/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults index e655f4ceb1..9b1c9f4263 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults @@ -14,7 +14,7 @@ CONFIG_SDK_TOOLPREFIX="xtensa-esp32s3-elf-" CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0 # end of Bootloader config -# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_80 is not set +CONFIG_BT_SOC_SUPPORT_5_0=y # # ESP32S3-Specific # diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index 0f297b9989..3705f5a93a 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -36,6 +36,8 @@ CIRCUITPY_MODULE ?= none ifeq ($(IDF_TARGET),esp32c3) CIRCUITPY_USB = 0 CIRCUITPY_ALARM = 0 +CIRCUITPY_BLEIO = 1 +CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_COUNTIO = 0 CIRCUITPY_ROTARYIO = 0 CIRCUITPY_AUDIOBUSIO = 0 @@ -45,6 +47,8 @@ CIRCUITPY_PARALLELDISPLAY = 0 CIRCUITPY_TOUCHIO ?= 1 CIRCUITPY_TOUCHIO_USE_NATIVE = 0 else ifeq ($(IDF_TARGET),esp32s3) +CIRCUITPY_BLEIO = 1 +CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_IMAGECAPTURE = 0 CIRCUITPY_PARALLELDISPLAY = 0 endif diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 5567f83c0c..11709eda40 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -70,6 +70,10 @@ #include "common-hal/audiobusio/__init__.h" #endif +#if CIRCUITPY_BLEIO +#include "shared-bindings/_bleio/__init__.h" +#endif + #if CIRCUITPY_IMAGECAPTURE #include "cam.h" #endif @@ -279,6 +283,10 @@ void reset_port(void) { watchdog_reset(); #endif + #if CIRCUITPY_BLEIO + bleio_reset(); + #endif + #if CIRCUITPY_WIFI wifi_reset(); #endif diff --git a/ports/espressif/tools/update_sdkconfig.py b/ports/espressif/tools/update_sdkconfig.py index ce174fd12a..e89f7ab00d 100644 --- a/ports/espressif/tools/update_sdkconfig.py +++ b/ports/espressif/tools/update_sdkconfig.py @@ -49,6 +49,7 @@ TARGET_SETTINGS = [ "CONFIG_TOOLPREFIX", "ESP_SLEEP_GPIO_RESET_WORKAROUND", "CONFIG_ESP_PHY_ENABLE_USB", + "CONFIG_BT_SOC_SUPPORT_5_0", ] BOARD_SETTINGS = [ @@ -65,6 +66,8 @@ FLASH_SETTINGS = [ "CONFIG_PARTITION_TABLE_FILENAME", ] +BLE_SETTINGS = ["CONFIG_BT_", "CONFIG_BLUEDROID_", "CONFIG_NIMBLE_", "CONFIG_SW_COEXIST_ENABLE"] + # boards/lilygo_ttgo_t8_s2_st7789/sdkconfig # CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y @@ -80,16 +83,17 @@ def matches_group(line, group): def add_group(lines, last_group, current_group): - if last_group != current_group: + # TODO: Properly handle nested groups + if last_group != current_group[-1]: if last_group: lines.append("# end of " + last_group) lines.append("") return None if current_group: lines.append("#") - lines.append("# " + current_group) + lines.append("# " + current_group[-1]) lines.append("#") - return current_group + return current_group[-1] return last_group @@ -120,12 +124,14 @@ def update(debug, board, update_all): opt_config = pathlib.Path("esp-idf-config/sdkconfig-opt.defaults") flash_config = pathlib.Path(f"esp-idf-config/sdkconfig-{flash}.defaults") target_config = pathlib.Path(f"esp-idf-config/sdkconfig-{target}.defaults") + ble_config = pathlib.Path(f"esp-idf-config/sdkconfig-ble.defaults") board_config = pathlib.Path(f"boards/{board}/sdkconfig") defaults = default_config.read_text().split("\n") defaults.extend(opt_config.read_text().split("\n")) defaults.extend(flash_config.read_text().split("\n")) defaults.extend(target_config.read_text().split("\n")) + defaults.extend(ble_config.read_text().split("\n")) board_settings = [] last_board_group = None @@ -135,15 +141,17 @@ def update(debug, board, update_all): last_opt_group = None target_settings = [] last_target_group = None + ble_settings = [] + last_ble_group = None default_settings = [] last_default_group = None - current_group = None + current_group = [] for line in input_config.read_text().split("\n"): if line.startswith("# ") and "CONFIG_" not in line and len(line) > 3: if line.startswith("# end of"): - current_group = None + current_group.pop() else: - current_group = line[2:] + current_group.append(line[2:]) elif (not update_all and line not in defaults) or ( update_all and matches_group(line, BOARD_SETTINGS) ): @@ -159,6 +167,9 @@ def update(debug, board, update_all): elif matches_group(line, TARGET_SETTINGS): last_target_group = add_group(target_settings, last_target_group, current_group) target_settings.append(line) + elif matches_group(line, BLE_SETTINGS): + last_ble_group = add_group(ble_settings, last_ble_group, current_group) + ble_settings.append(line) elif "CONFIG_" in line: last_default_group = add_group(default_settings, last_default_group, current_group) default_settings.append(line) @@ -167,6 +178,7 @@ def update(debug, board, update_all): add_group(opt_settings, last_opt_group, current_group) add_group(flash_settings, last_flash_group, current_group) add_group(target_settings, last_target_group, current_group) + add_group(ble_settings, last_ble_group, current_group) add_group(default_settings, last_default_group, current_group) board_config.write_text("\n".join(board_settings)) @@ -175,6 +187,7 @@ def update(debug, board, update_all): opt_config.write_text("\n".join(opt_settings)) default_config.write_text("\n".join(default_settings)) target_config.write_text("\n".join(target_settings)) + ble_config.write_text("\n".join(ble_settings)) if __name__ == "__main__": diff --git a/ports/nrf/common-hal/_bleio/__init__.h b/ports/nrf/common-hal/_bleio/__init__.h index d94267dd3c..f10001f662 100644 --- a/ports/nrf/common-hal/_bleio/__init__.h +++ b/ports/nrf/common-hal/_bleio/__init__.h @@ -28,7 +28,6 @@ #define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H void bleio_background(void); -void bleio_reset(void); typedef struct { ble_gap_enc_key_t own_enc; diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index a1eb973b83..5c1139271d 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -45,7 +45,6 @@ #include "nrf_nvic.h" #include "common-hal/microcontroller/Pin.h" -#include "common-hal/_bleio/__init__.h" #include "common-hal/alarm/time/TimeAlarm.h" #include "common-hal/analogio/AnalogIn.h" #include "common-hal/busio/I2C.h" @@ -59,6 +58,7 @@ #include "common-hal/watchdog/WatchDogTimer.h" #include "common-hal/alarm/__init__.h" +#include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/rtc/__init__.h" diff --git a/py/ringbuf.c b/py/ringbuf.c index 2003cc97d5..72e164946a 100644 --- a/py/ringbuf.c +++ b/py/ringbuf.c @@ -114,7 +114,7 @@ size_t ringbuf_num_filled(ringbuf_t *r) { // If the ring buffer fills up, not all bytes will be written. // Returns how many bytes were successfully written. -size_t ringbuf_put_n(ringbuf_t *r, uint8_t *buf, size_t bufsize) { +size_t ringbuf_put_n(ringbuf_t *r, const uint8_t *buf, size_t bufsize) { for (size_t i = 0; i < bufsize; i++) { if (ringbuf_put(r, buf[i]) < 0) { // If ringbuf is full, give up and return how many bytes diff --git a/py/ringbuf.h b/py/ringbuf.h index 6ed72dcf1a..8f7e7b1760 100644 --- a/py/ringbuf.h +++ b/py/ringbuf.h @@ -52,7 +52,7 @@ int ringbuf_put(ringbuf_t *r, uint8_t v); void ringbuf_clear(ringbuf_t *r); size_t ringbuf_num_empty(ringbuf_t *r); size_t ringbuf_num_filled(ringbuf_t *r); -size_t ringbuf_put_n(ringbuf_t *r, uint8_t *buf, size_t bufsize); +size_t ringbuf_put_n(ringbuf_t *r, const uint8_t *buf, size_t bufsize); size_t ringbuf_get_n(ringbuf_t *r, uint8_t *buf, size_t bufsize); // Note: big-endian. No-op if not enough room available for both bytes. diff --git a/shared-bindings/_bleio/__init__.h b/shared-bindings/_bleio/__init__.h index 7f251ef188..55b527cfa5 100644 --- a/shared-bindings/_bleio/__init__.h +++ b/shared-bindings/_bleio/__init__.h @@ -54,6 +54,8 @@ extern const mp_obj_type_t mp_type_bleio_BluetoothError; extern const mp_obj_type_t mp_type_bleio_RoleError; extern const mp_obj_type_t mp_type_bleio_SecurityError; +void bleio_reset(void); + extern mp_obj_t bleio_set_adapter(mp_obj_t adapter_obj); NORETURN void mp_raise_bleio_BluetoothError(const compressed_string_t *msg, ...); diff --git a/shared-module/_bleio/ScanResults.c b/shared-module/_bleio/ScanResults.c index e5b5525579..1f4dbff310 100644 --- a/shared-module/_bleio/ScanResults.c +++ b/shared-module/_bleio/ScanResults.c @@ -91,9 +91,9 @@ void shared_module_bleio_scanresults_append(bleio_scanresults_obj_t *self, bool connectable, bool scan_response, int8_t rssi, - uint8_t *peer_addr, + const uint8_t *peer_addr, uint8_t addr_type, - uint8_t *data, + const uint8_t *data, uint16_t len) { int32_t packet_size = sizeof(uint8_t) + sizeof(ticks_ms) + sizeof(rssi) + NUM_BLEIO_ADDRESS_BYTES + sizeof(addr_type) + sizeof(len) + len; diff --git a/shared-module/_bleio/ScanResults.h b/shared-module/_bleio/ScanResults.h index fa181aabee..945809c8d9 100644 --- a/shared-module/_bleio/ScanResults.h +++ b/shared-module/_bleio/ScanResults.h @@ -56,9 +56,9 @@ void shared_module_bleio_scanresults_append(bleio_scanresults_obj_t *self, bool connectable, bool scan_result, int8_t rssi, - uint8_t *peer_addr, + const uint8_t *peer_addr, uint8_t addr_type, - uint8_t *data, + const uint8_t *data, uint16_t len); #endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANRESULTS_H