aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/devices
diff options
context:
space:
mode:
Diffstat (limited to 'circuitpython/devices')
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Adapter.c982
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Adapter.h99
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Attribute.c49
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Attribute.h35
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Characteristic.c232
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Characteristic.h63
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.c105
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.h43
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Connection.c772
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Connection.h90
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Descriptor.c114
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Descriptor.h53
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/PacketBuffer.c268
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/PacketBuffer.h54
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Service.c130
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/Service.h54
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/UUID.c75
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/UUID.h58
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/__init__.c117
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/__init__.h61
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/att.c1795
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/att.h57
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci.c805
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci.h81
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci_debug.c549
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/NOTE.txt2
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/addr.h95
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/att.h41
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/att_internal.h273
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci.h1772
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_err.h92
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_raw.h152
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_vs.h379
-rw-r--r--circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/l2cap_internal.h230
-rw-r--r--circuitpython/devices/ble_hci/supervisor/bluetooth.c29
-rw-r--r--circuitpython/devices/ble_hci/supervisor/bluetooth.h34
36 files changed, 9840 insertions, 0 deletions
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/Adapter.c b/circuitpython/devices/ble_hci/common-hal/_bleio/Adapter.c
new file mode 100644
index 0000000..839519d
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Adapter.c
@@ -0,0 +1,982 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 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 <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "hci.h"
+
+#include "py/gc.h"
+#include "py/mphal.h"
+#include "py/objstr.h"
+#include "py/runtime.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/_bleio/Characteristic.h"
+#include "shared-bindings/_bleio/Service.h"
+#include "shared-bindings/_bleio/Connection.h"
+#include "shared-bindings/_bleio/ScanEntry.h"
+#include "shared-bindings/time/__init__.h"
+
+#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION))
+#define SEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000000) / (RESOLUTION))
+#define UNITS_TO_SEC(TIME, RESOLUTION) (((TIME)*(RESOLUTION)) / 1000000)
+// 0.625 msecs (625 usecs)
+#define ADV_INTERVAL_UNIT_FLOAT_SECS (0.000625)
+// Microseconds is the base unit. The macros above know that.
+#define UNIT_0_625_MS (625)
+#define UNIT_1_25_MS (1250)
+#define UNIT_10_MS (10000)
+
+#define MAX_ADVERTISEMENT_SIZE (31)
+
+// TODO make this settable from Python.
+#define DEFAULT_TX_POWER 0 // 0 dBm
+#define MAX_ANONYMOUS_ADV_TIMEOUT_SECS (60 * 15)
+#define MAX_LIMITED_DISCOVERABLE_ADV_TIMEOUT_SECS (180)
+
+#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS)
+#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS)
+#define BLE_SLAVE_LATENCY 0
+#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)
+
+bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT];
+
+STATIC void add_generic_services(bleio_adapter_obj_t *adapter) {
+ // Create Generic Access UUID, Service, and Characteristics.
+
+ // Generic Access Service setup.
+
+ bleio_uuid_obj_t *generic_access_service_uuid = m_new_obj(bleio_uuid_obj_t);
+ generic_access_service_uuid->base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(generic_access_service_uuid, 0x1800, NULL);
+
+ bleio_uuid_obj_t *device_name_characteristic_uuid = m_new_obj(bleio_uuid_obj_t);
+ device_name_characteristic_uuid->base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(device_name_characteristic_uuid, 0x2A00, NULL);
+
+ bleio_uuid_obj_t *appearance_characteristic_uuid = m_new_obj(bleio_uuid_obj_t);
+ appearance_characteristic_uuid->base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(appearance_characteristic_uuid, 0x2A01, NULL);
+
+ // Not implemented:
+ // Peripheral Preferred Connection Parameters
+ // Central Address Resolution
+
+ bleio_service_obj_t *generic_access_service = m_new_obj(bleio_service_obj_t);
+ generic_access_service->base.type = &bleio_service_type;
+ common_hal_bleio_service_construct(generic_access_service, generic_access_service_uuid, false);
+
+ adapter->device_name_characteristic = m_new_obj(bleio_characteristic_obj_t);
+ adapter->device_name_characteristic->base.type = &bleio_characteristic_type;
+
+ char generic_name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 'n', 'n', 'n', 'n' };
+ mp_buffer_info_t generic_name_bufinfo = {
+ .buf = generic_name,
+ .len = sizeof(generic_name),
+ };
+
+ // Will be added to service by constructor.
+ common_hal_bleio_characteristic_construct(
+ adapter->device_name_characteristic,
+ generic_access_service,
+ BLE_GATT_HANDLE_INVALID,
+ device_name_characteristic_uuid,
+ CHAR_PROP_READ,
+ SECURITY_MODE_OPEN,
+ SECURITY_MODE_NO_ACCESS,
+ 248, // max length, from Bluetooth spec
+ false, // not fixed length
+ &generic_name_bufinfo,
+ NULL
+ );
+
+ uint16_t zero_16 = 0;
+ mp_buffer_info_t zero_16_value = {
+ .buf = &zero_16,
+ .len = sizeof(zero_16),
+ };
+
+ adapter->appearance_characteristic = m_new_obj(bleio_characteristic_obj_t);
+ adapter->appearance_characteristic->base.type = &bleio_characteristic_type;
+
+ common_hal_bleio_characteristic_construct(
+ adapter->appearance_characteristic,
+ generic_access_service,
+ BLE_GATT_HANDLE_INVALID,
+ appearance_characteristic_uuid,
+ CHAR_PROP_READ,
+ SECURITY_MODE_OPEN,
+ SECURITY_MODE_NO_ACCESS,
+ 2, // max length, from Bluetooth spec
+ true, // fixed length
+ &zero_16_value,
+ NULL
+ );
+
+ // Generic Attribute Service setup.
+
+ bleio_uuid_obj_t *generic_attribute_service_uuid = m_new_obj(bleio_uuid_obj_t);
+ generic_attribute_service_uuid->base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(generic_attribute_service_uuid, 0x1801, NULL);
+
+ bleio_uuid_obj_t *service_changed_characteristic_uuid = m_new_obj(bleio_uuid_obj_t);
+ service_changed_characteristic_uuid->base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(service_changed_characteristic_uuid, 0x2A05, NULL);
+
+ bleio_service_obj_t *generic_attribute_service = m_new_obj(bleio_service_obj_t);
+ generic_attribute_service->base.type = &bleio_service_type;
+ common_hal_bleio_service_construct(generic_attribute_service, generic_attribute_service_uuid, false);
+
+ adapter->service_changed_characteristic = m_new_obj(bleio_characteristic_obj_t);
+ adapter->service_changed_characteristic->base.type = &bleio_characteristic_type;
+
+ uint32_t zero_32 = 0;
+ mp_buffer_info_t zero_32_value = {
+ .buf = &zero_32,
+ .len = sizeof(zero_32),
+ };
+
+ common_hal_bleio_characteristic_construct(
+ adapter->service_changed_characteristic,
+ generic_attribute_service,
+ BLE_GATT_HANDLE_INVALID,
+ service_changed_characteristic_uuid,
+ CHAR_PROP_INDICATE,
+ SECURITY_MODE_OPEN,
+ SECURITY_MODE_NO_ACCESS,
+ 4, // max length, from Bluetooth spec
+ true, // fixed length
+ &zero_32_value,
+ NULL
+ );
+}
+
+
+STATIC void check_enabled(bleio_adapter_obj_t *adapter) {
+ if (!common_hal_bleio_adapter_get_enabled(adapter)) {
+ mp_raise_bleio_BluetoothError(translate("Adapter not enabled"));
+ }
+}
+
+// STATIC bool adapter_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
+// bleio_adapter_obj_t *self = (bleio_adapter_obj_t*)self_in;
+
+// // For debugging.
+// // mp_printf(&mp_plat_print, "Adapter event: 0x%04x\n", ble_evt->header.evt_id);
+
+// switch (ble_evt->header.evt_id) {
+// case BLE_GAP_EVT_CONNECTED: {
+// // Find an empty connection. One must always be available because the SD has the same
+// // total connection limit.
+// bleio_connection_internal_t *connection;
+// for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+// connection = &bleio_connections[i];
+// if (connection->conn_handle == BLE_CONN_HANDLE_INVALID) {
+// break;
+// }
+// }
+
+// // Central has connected.
+// ble_gap_evt_connected_t* connected = &ble_evt->evt.gap_evt.params.connected;
+
+// connection->conn_handle = ble_evt->evt.gap_evt.conn_handle;
+// connection->connection_obj = mp_const_none;
+// connection->pair_status = PAIR_NOT_PAIRED;
+// connection->mtu = 0;
+
+// ble_drv_add_event_handler_entry(&connection->handler_entry, connection_on_ble_evt, connection);
+// self->connection_objs = NULL;
+
+// // Save the current connection parameters.
+// memcpy(&connection->conn_params, &connected->conn_params, sizeof(ble_gap_conn_params_t));
+
+// #if CIRCUITPY_VERBOSE_BLE
+// ble_gap_conn_params_t *cp = &connected->conn_params;
+// mp_printf(&mp_plat_print, "conn params: min_ci %d max_ci %d s_l %d sup_timeout %d\n", cp->min_conn_interval, cp->max_conn_interval, cp->slave_latency, cp->conn_sup_timeout);
+// #endif
+
+// // See if connection interval set by Central is out of range.
+// // If so, negotiate our preferred range.
+// ble_gap_conn_params_t conn_params;
+// sd_ble_gap_ppcp_get(&conn_params);
+// if (conn_params.min_conn_interval < connected->conn_params.min_conn_interval ||
+// conn_params.min_conn_interval > connected->conn_params.max_conn_interval) {
+// sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, &conn_params);
+// }
+// self->current_advertising_data = NULL;
+// break;
+// }
+// case BLE_GAP_EVT_DISCONNECTED: {
+// // Find the connection that was disconnected.
+// bleio_connection_internal_t *connection;
+// for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+// connection = &bleio_connections[i];
+// if (connection->conn_handle == ble_evt->evt.gap_evt.conn_handle) {
+// break;
+// }
+// }
+// ble_drv_remove_event_handler(connection_on_ble_evt, connection);
+// connection->conn_handle = BLE_CONN_HANDLE_INVALID;
+// connection->pair_status = PAIR_NOT_PAIRED;
+// if (connection->connection_obj != mp_const_none) {
+// bleio_connection_obj_t* obj = connection->connection_obj;
+// obj->connection = NULL;
+// obj->disconnect_reason = ble_evt->evt.gap_evt.params.disconnected.reason;
+// }
+// self->connection_objs = NULL;
+
+// break;
+// }
+
+// case BLE_GAP_EVT_ADV_SET_TERMINATED:
+// self->current_advertising_data = NULL;
+// break;
+
+// default:
+// // For debugging.
+// // mp_printf(&mp_plat_print, "Unhandled adapter event: 0x%04x\n", ble_evt->header.evt_id);
+// return false;
+// break;
+// }
+// return true;
+// }
+
+char default_ble_name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0, 0};
+
+// Get various values and limits set by the adapter.
+// Set event mask.
+STATIC void bleio_adapter_hci_init(bleio_adapter_obj_t *self) {
+
+ const size_t len = sizeof(default_ble_name);
+
+ bt_addr_t addr;
+ hci_check_error(hci_read_bd_addr(&addr));
+
+ default_ble_name[len - 4] = nibble_to_hex_lower[addr.val[1] >> 4 & 0xf];
+ default_ble_name[len - 3] = nibble_to_hex_lower[addr.val[1] & 0xf];
+ default_ble_name[len - 2] = nibble_to_hex_lower[addr.val[0] >> 4 & 0xf];
+ default_ble_name[len - 1] = nibble_to_hex_lower[addr.val[0] & 0xf];
+ self->name = mp_obj_new_str(default_ble_name, len);
+
+ // Get version information.
+ if (hci_read_local_version(&self->hci_version, &self->hci_revision, &self->lmp_version,
+ &self->manufacturer, &self->lmp_subversion) != HCI_OK) {
+ mp_raise_bleio_BluetoothError(translate("Could not read HCI version"));
+ }
+ // Get supported features.
+ if (hci_le_read_local_supported_features(self->features) != HCI_OK) {
+ mp_raise_bleio_BluetoothError(translate("Could not read BLE features"));
+ }
+
+ // Enabled desired events.
+ // Most importantly, includes:
+ // BT_EVT_MASK_LE_META_EVENT BT_EVT_BIT(61)
+ if (hci_set_event_mask(0x3FFFFFFFFFFFFFFF) != HCI_OK) {
+ mp_raise_bleio_BluetoothError(translate("Could not set event mask"));
+ }
+ // The default events for LE are:
+ // BT_EVT_MASK_LE_CONN_COMPLETE, BT_EVT_MASK_LE_ADVERTISING_REPORT,
+ // BT_EVT_MASK_LE_CONN_UPDATE_COMPLETE, BT_EVT_MASK_LE_REMOTE_FEAT_COMPLETE
+ // BT_EVT_MASK_LE_LTK_REQUEST.
+ // That's all we need right now, so we don't bother to set the LE event mask.
+
+ // Get ACL buffer info.
+ uint16_t le_max_len;
+ uint8_t le_max_num;
+ if (hci_le_read_buffer_size(&le_max_len, &le_max_num) == HCI_OK) {
+ self->max_acl_buffer_len = le_max_len;
+ self->max_acl_num_buffers = le_max_num;
+ } else {
+ // LE Read Buffer Size not available; use the general Read Buffer Size.
+ uint16_t acl_max_len;
+ uint8_t sco_max_len;
+ uint16_t acl_max_num;
+ uint16_t sco_max_num;
+ if (hci_read_buffer_size(&acl_max_len, &sco_max_len, &acl_max_num, &sco_max_num) != HCI_OK) {
+ mp_raise_bleio_BluetoothError(translate("Could not read BLE buffer info"));
+ }
+ self->max_acl_buffer_len = acl_max_len;
+ self->max_acl_num_buffers = acl_max_num;
+ }
+
+ // Get max advertising length if extended advertising is supported.
+ if (BT_FEAT_LE_EXT_ADV(self->features)) {
+ uint16_t max_adv_data_len;
+ if (hci_le_read_maximum_advertising_data_length(&max_adv_data_len) != HCI_OK) {
+ mp_raise_bleio_BluetoothError(translate("Could not get max advertising length"));
+ }
+ self->max_adv_data_len = max_adv_data_len;
+ } else {
+ self->max_adv_data_len = MAX_ADVERTISEMENT_SIZE;
+ }
+}
+
+void common_hal_bleio_adapter_construct_hci_uart(bleio_adapter_obj_t *self, busio_uart_obj_t *uart, digitalio_digitalinout_obj_t *rts, digitalio_digitalinout_obj_t *cts) {
+ self->allocated = true;
+ self->hci_uart = uart;
+ self->rts_digitalinout = rts;
+ self->cts_digitalinout = cts;
+
+ // Advertising-related fields are initialized by common_hal_bleio_adapter_set_enabled().
+ self->enabled = false;
+
+ common_hal_bleio_adapter_set_enabled(self, true);
+ bleio_adapter_hci_init(self);
+ common_hal_bleio_adapter_set_name(self, default_ble_name);
+}
+
+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;
+ }
+
+ self->enabled = enabled;
+
+ // We must poll for input from the HCI adapter.
+ // TODO Can we instead trigger an interrupt on UART input traffic?
+ if (enabled) {
+ supervisor_enable_tick();
+ } else {
+ supervisor_disable_tick();
+ }
+
+ // Enabling or disabling: stop any current activity; reset to known state.
+ hci_reset();
+ self->now_advertising = false;
+ self->extended_advertising = false;
+ self->circuitpython_advertising = false;
+ self->advertising_timeout_msecs = 0;
+
+ if (enabled) {
+ // Reset list of known attributes.
+ // Indices into the list are handles. Handle 0x0000 designates an invalid handle,
+ // so store None there to skip it.
+ self->attributes = mp_obj_new_list(0, NULL);
+ bleio_adapter_add_attribute(self, mp_const_none);
+ add_generic_services(self);
+ }
+}
+
+bool common_hal_bleio_adapter_get_enabled(bleio_adapter_obj_t *self) {
+ return self->enabled;
+}
+
+bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self) {
+ check_enabled(self);
+
+ bt_addr_t addr;
+ hci_check_error(hci_read_bd_addr(&addr));
+
+ bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t);
+ address->base.type = &bleio_address_type;
+
+ common_hal_bleio_address_construct(address, addr.val, BT_ADDR_LE_PUBLIC);
+ return address;
+}
+
+bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address) {
+ mp_buffer_info_t bufinfo;
+ if (!mp_get_buffer(address->bytes, &bufinfo, MP_BUFFER_READ)) {
+ return false;
+ }
+ return hci_le_set_random_address(bufinfo.buf) == HCI_OK;
+}
+
+mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self) {
+ return self->name;
+}
+
+void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name) {
+ self->name = mp_obj_new_str(name, strlen(name));
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(self->name, &bufinfo, MP_BUFFER_READ);
+ bleio_characteristic_set_local_value(self->device_name_characteristic, &bufinfo);
+}
+
+
+// STATIC bool scan_on_ble_evt(ble_evt_t *ble_evt, void *scan_results_in) {
+// bleio_scanresults_obj_t *scan_results = (bleio_scanresults_obj_t*)scan_results_in;
+
+// if (ble_evt->header.evt_id == BLE_GAP_EVT_TIMEOUT &&
+// ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_SCAN) {
+// shared_module_bleio_scanresults_set_done(scan_results, true);
+// ble_drv_remove_event_handler(scan_on_ble_evt, scan_results);
+// return true;
+// }
+
+// if (ble_evt->header.evt_id != BLE_GAP_EVT_ADV_REPORT) {
+// return false;
+// }
+// ble_gap_evt_adv_report_t *report = &ble_evt->evt.gap_evt.params.adv_report;
+
+// shared_module_bleio_scanresults_append(scan_results,
+// supervisor_ticks_ms64(),
+// report->type.connectable,
+// report->type.scan_response,
+// report->rssi,
+// report->peer_addr.addr,
+// report->peer_addr.addr_type,
+// report->data.p_data,
+// report->data.len);
+
+// const uint32_t err_code = sd_ble_gap_scan_start(NULL, scan_results->common_hal_data);
+// if (err_code != NRF_SUCCESS) {
+// // TODO: Pass the error into the scan results so it can throw an exception.
+// scan_results->done = true;
+// }
+// return true;
+// }
+
+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) {
+ // TODO
+ mp_raise_NotImplementedError(NULL);
+ check_enabled(self);
+
+ 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_GAP_SCAN_BUFFER_EXTENDED_MAX_SUPPORTED : BLE_GAP_SCAN_BUFFER_MAX;
+ // uint8_t *raw_data = m_malloc(sizeof(ble_data_t) + max_packet_size, false);
+ // ble_data_t * sd_data = (ble_data_t *) raw_data;
+ // self->scan_results->common_hal_data = sd_data;
+ // sd_data->len = max_packet_size;
+ // sd_data->p_data = raw_data + sizeof(ble_data_t);
+
+ // ble_drv_add_event_handler(scan_on_ble_evt, self->scan_results);
+
+ // uint32_t nrf_timeout = SEC_TO_UNITS(timeout, UNIT_10_MS);
+ // if (timeout <= 0.0001) {
+ // nrf_timeout = BLE_GAP_SCAN_TIMEOUT_UNLIMITED;
+ // }
+
+ // ble_gap_scan_params_t scan_params = {
+ // .extended = extended,
+ // .interval = SEC_TO_UNITS(interval, UNIT_0_625_MS),
+ // .timeout = nrf_timeout,
+ // .window = SEC_TO_UNITS(window, UNIT_0_625_MS),
+ // .scan_phys = BLE_GAP_PHY_1MBPS,
+ // .active = active
+ // };
+ // uint32_t err_code;
+ // vm_used_ble = true;
+ // err_code = sd_ble_gap_scan_start(&scan_params, sd_data);
+
+ // if (err_code != NRF_SUCCESS) {
+ // self->scan_results = NULL;
+ // ble_drv_remove_event_handler(scan_on_ble_evt, self->scan_results);
+ // check_nrf_error(err_code);
+ // }
+
+ return MP_OBJ_FROM_PTR(self->scan_results);
+}
+
+void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self) {
+ // TODO
+ mp_raise_NotImplementedError(NULL);
+ check_enabled(self);
+
+ // If not already scanning, no problem.
+ if (hci_le_set_scan_enable(BT_HCI_LE_SCAN_DISABLE, BT_HCI_LE_SCAN_FILTER_DUP_DISABLE) == HCI_OK) {
+ shared_module_bleio_scanresults_set_done(self->scan_results, true);
+ self->scan_results = NULL;
+ }
+}
+
+// typedef struct {
+// uint16_t conn_handle;
+// volatile bool done;
+// } connect_info_t;
+
+// STATIC bool connect_on_ble_evt(ble_evt_t *ble_evt, void *info_in) {
+// connect_info_t *info = (connect_info_t*)info_in;
+
+// switch (ble_evt->header.evt_id) {
+// case BLE_GAP_EVT_CONNECTED:
+// info->conn_handle = ble_evt->evt.gap_evt.conn_handle;
+// info->done = true;
+
+// break;
+
+// case BLE_GAP_EVT_TIMEOUT:
+// // Handle will be invalid.
+// info->done = true;
+// break;
+// default:
+// // For debugging.
+// // mp_printf(&mp_plat_print, "Unhandled central event: 0x%04x\n", ble_evt->header.evt_id);
+// return false;
+// break;
+// }
+// return true;
+// }
+
+mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) {
+ // TODO
+ mp_raise_NotImplementedError(NULL);
+
+ check_enabled(self);
+
+ // ble_gap_addr_t addr;
+
+ // addr.addr_type = address->type;
+ // mp_buffer_info_t address_buf_info;
+ // mp_get_buffer_raise(address->bytes, &address_buf_info, MP_BUFFER_READ);
+ // memcpy(addr.addr, (uint8_t *) address_buf_info.buf, NUM_BLEIO_ADDRESS_BYTES);
+
+ // ble_gap_scan_params_t scan_params = {
+ // .interval = MSEC_TO_UNITS(100, UNIT_0_625_MS),
+ // .window = MSEC_TO_UNITS(100, UNIT_0_625_MS),
+ // .scan_phys = BLE_GAP_PHY_1MBPS,
+ // // timeout of 0 means no timeout
+ // .timeout = SEC_TO_UNITS(timeout, UNIT_10_MS),
+ // };
+
+ // ble_gap_conn_params_t conn_params = {
+ // .conn_sup_timeout = MSEC_TO_UNITS(4000, UNIT_10_MS),
+ // .min_conn_interval = MSEC_TO_UNITS(15, UNIT_1_25_MS),
+ // .max_conn_interval = MSEC_TO_UNITS(300, UNIT_1_25_MS),
+ // .slave_latency = 0, // number of conn events
+ // };
+
+ // connect_info_t event_info;
+ // ble_drv_add_event_handler(connect_on_ble_evt, &event_info);
+ // event_info.done = false;
+
+ vm_used_ble = true;
+ // uint32_t err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params, BLE_CONN_CFG_TAG_CUSTOM);
+
+ // if (err_code != NRF_SUCCESS) {
+ // ble_drv_remove_event_handler(connect_on_ble_evt, &event_info);
+ // check_nrf_error(err_code);
+ // }
+
+ // while (!event_info.done) {
+ // RUN_BACKGROUND_TASKS;
+ // }
+
+ // ble_drv_remove_event_handler(connect_on_ble_evt, &event_info);
+
+ // uint16_t conn_handle = event_info.conn_handle;
+ // if (conn_handle == BLE_CONN_HANDLE_INVALID) {
+ // mp_raise_bleio_BluetoothError(translate("Failed to connect: timeout"));
+ // }
+
+ // // Negotiate for better PHY, larger MTU and data lengths since we are the central. These are
+ // // nice-to-haves so ignore any errors.
+ // ble_gap_phys_t const phys = {
+ // .rx_phys = BLE_GAP_PHY_AUTO,
+ // .tx_phys = BLE_GAP_PHY_AUTO,
+ // };
+ // sd_ble_gap_phy_update(conn_handle, &phys);
+ // sd_ble_gattc_exchange_mtu_request(conn_handle, BLE_GATTS_VAR_ATTR_LEN_MAX);
+ // sd_ble_gap_data_length_update(conn_handle, NULL, NULL);
+
+ // Make the connection object and return it.
+ // for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ // bleio_connection_internal_t *connection = &bleio_connections[i];
+ // if (connection->conn_handle == conn_handle) {
+ // return bleio_connection_new_from_internal(connection);
+ // }
+ // }
+
+ mp_raise_bleio_BluetoothError(translate("Failed to connect: internal error"));
+
+ return mp_const_none;
+}
+
+STATIC void check_data_fit(size_t data_len, bool connectable) {
+ if (data_len > MAX_ADVERTISEMENT_SIZE) {
+ mp_raise_ValueError(translate("Data too large for advertisement packet"));
+ }
+}
+
+// STATIC bool advertising_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
+// bleio_adapter_obj_t *self = (bleio_adapter_obj_t*)self_in;
+
+// switch (ble_evt->header.evt_id) {
+// case BLE_GAP_EVT_ADV_SET_TERMINATED:
+// common_hal_bleio_adapter_stop_advertising(self);
+// ble_drv_remove_event_handler(advertising_on_ble_evt, self_in);
+// break;
+
+// default:
+// // For debugging.
+// // mp_printf(&mp_plat_print, "Unhandled advertising event: 0x%04x\n", ble_evt->header.evt_id);
+// return false;
+// break;
+// }
+// return true;
+// }
+
+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) {
+ check_enabled(self);
+
+ if (self->now_advertising) {
+ if (self->circuitpython_advertising) {
+ common_hal_bleio_adapter_stop_advertising(self);
+ } else {
+ // User-requested advertising.
+ // TODO allow multiple advertisements.
+ // Already advertising. Can't advertise twice.
+ return 1;
+ }
+ }
+
+ // Peer address, for directed advertising
+ bt_addr_le_t peer_addr = { 0 };
+
+ // Copy peer address, if supplied.
+ if (directed_to) {
+ mp_buffer_info_t bufinfo;
+ if (mp_get_buffer(directed_to->bytes, &bufinfo, MP_BUFFER_READ)) {
+ peer_addr.type = directed_to->type;
+ memcpy(&peer_addr.a.val, bufinfo.buf, sizeof(peer_addr.a.val));
+ }
+ }
+
+ bool extended =
+ advertising_data_len > self->max_adv_data_len || scan_response_data_len > self->max_adv_data_len;
+
+ if (extended) {
+ if (!BT_FEAT_LE_EXT_ADV(self->features)) {
+ mp_raise_bleio_BluetoothError(translate("Data length needs extended advertising, but this adapter does not support it"));
+ }
+
+ uint16_t props = 0;
+ if (connectable) {
+ props |= BT_HCI_LE_ADV_PROP_CONN;
+ }
+ if (scan_response_data_len > 0) {
+ props |= BT_HCI_LE_ADV_PROP_SCAN;
+ }
+
+ // Advertising interval.
+ uint32_t interval_units = SEC_TO_UNITS(interval, UNIT_0_625_MS);
+
+ hci_check_error(
+ hci_le_set_extended_advertising_parameters(
+ 0, // handle
+ props, // adv properties
+ interval_units, // min interval
+ interval_units, // max interval
+ 0b111, // channel map: channels 37, 38, 39
+ anonymous ? BT_ADDR_LE_RANDOM : BT_ADDR_LE_PUBLIC,
+ &peer_addr, // peer_addr,
+ 0x00, // filter policy: no filter
+ DEFAULT_TX_POWER,
+ BT_HCI_LE_EXT_SCAN_PHY_1M, // Secondary PHY to use
+ 0x00, // AUX_ADV_IND shall be sent prior to next adv event
+ BT_HCI_LE_EXT_SCAN_PHY_1M, // Secondary PHY to use
+ 0x00, // Advertising SID
+ 0x00 // Scan req notify disable
+ ));
+
+ // We can use the duration mechanism provided, instead of our own.
+ self->advertising_timeout_msecs = 0;
+
+ uint8_t handle[1] = { 0 };
+ uint16_t duration_10msec[1] = { timeout * 100 };
+ uint8_t max_ext_adv_evts[1] = { 0 };
+ hci_check_error(
+ hci_le_set_extended_advertising_enable(
+ BT_HCI_LE_ADV_ENABLE,
+ 1, // one advertising set.
+ handle,
+ duration_10msec,
+ max_ext_adv_evts
+ ));
+
+ self->extended_advertising = true;
+ } else {
+ // Legacy advertising (not extended).
+
+ uint8_t adv_type;
+ if (connectable) {
+ // Connectable, scannable, undirected.
+ adv_type = BT_HCI_ADV_IND;
+ } else if (scan_response_data_len > 0) {
+ // Unconnectable, scannable, undirected.
+ adv_type = BT_HCI_ADV_SCAN_IND;
+ } else {
+ // Unconnectable, unscannable, undirected.
+ adv_type = BT_HCI_ADV_NONCONN_IND;
+ }
+
+ // Advertising interval.
+ uint16_t interval_units = SEC_TO_UNITS(interval, UNIT_0_625_MS);
+
+ hci_check_error(
+ hci_le_set_advertising_parameters(
+ interval_units, // min interval
+ interval_units, // max interval
+ adv_type,
+ anonymous ? BT_ADDR_LE_RANDOM : BT_ADDR_LE_PUBLIC,
+ &peer_addr,
+ 0b111, // channel map: channels 37, 38, 39
+ 0x00 // filter policy: no filter
+ ));
+
+ // The HCI commands expect MAX_ADVERTISEMENT_SIZE (31)octets,
+ // even though the actual data length may be shorter.
+ uint8_t full_data[MAX_ADVERTISEMENT_SIZE] = { 0 };
+ memcpy(full_data, advertising_data, MIN(sizeof(full_data), advertising_data_len));
+ hci_check_error(hci_le_set_advertising_data(advertising_data_len, full_data));
+ memset(full_data, 0, sizeof(full_data));
+ if (scan_response_data_len > 0) {
+ memcpy(full_data, scan_response_data, MIN(sizeof(full_data), scan_response_data_len));
+ hci_check_error(hci_le_set_scan_response_data(scan_response_data_len, full_data));
+ }
+
+ // No duration mechanism is provided for legacy advertising, so we need to do our own.
+ self->advertising_timeout_msecs = timeout * 1000;
+ self->advertising_start_ticks = supervisor_ticks_ms64();
+
+ // Start advertising.
+ hci_check_error(hci_le_set_advertising_enable(BT_HCI_LE_ADV_ENABLE));
+ self->extended_advertising = false;
+ } // end legacy advertising setup
+
+ vm_used_ble = true;
+ self->now_advertising = true;
+ return 0;
+}
+
+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) {
+ check_enabled(self);
+
+ // interval value has already been validated.
+
+ check_data_fit(advertising_data_bufinfo->len, connectable);
+ check_data_fit(scan_response_data_bufinfo->len, connectable);
+
+ if (advertising_data_bufinfo->len > MAX_ADVERTISEMENT_SIZE && scan_response_data_bufinfo->len > 0) {
+ mp_raise_bleio_BluetoothError(translate("Extended advertisements with scan response not supported."));
+ }
+
+ // Anonymous mode requires a timeout so that we don't continue to broadcast
+ // the same data while cycling the MAC address -- otherwise, what's the
+ // point of randomizing the MAC address?
+ if (timeout == 0 && anonymous) {
+ timeout = MAX_ANONYMOUS_ADV_TIMEOUT_SECS;
+ } else {
+ if (timeout > MAX_LIMITED_DISCOVERABLE_ADV_TIMEOUT_SECS) {
+ mp_raise_bleio_BluetoothError(translate("Timeout is too long: Maximum timeout length is %d seconds"),
+ MAX_LIMITED_DISCOVERABLE_ADV_TIMEOUT_SECS);
+ }
+ }
+
+ if (tx_power != 0) {
+ mp_raise_NotImplementedError(translate("Only tx_power=0 supported"));
+ }
+
+ const uint32_t result = _common_hal_bleio_adapter_start_advertising(
+ self, connectable, anonymous, timeout, interval,
+ advertising_data_bufinfo->buf,
+ advertising_data_bufinfo->len,
+ scan_response_data_bufinfo->buf,
+ scan_response_data_bufinfo->len,
+ tx_power, directed_to);
+
+ if (result) {
+ mp_raise_bleio_BluetoothError(translate("Already advertising"));
+ }
+ self->circuitpython_advertising = false;
+}
+
+void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) {
+ check_enabled(self);
+
+ self->now_advertising = false;
+ self->extended_advertising = false;
+ self->circuitpython_advertising = false;
+
+ int result = hci_le_set_advertising_enable(BT_HCI_LE_ADV_DISABLE);
+ // OK if we're already stopped. There seems to be an ESP32 HCI bug:
+ // If advertising is already off, then LE_SET_ADV_ENABLE does not return a response.
+ if (result != HCI_RESPONSE_TIMEOUT) {
+ hci_check_error(result);
+ }
+
+ // TODO startup CircuitPython advertising again.
+}
+
+// Note that something stopped advertising, such as a connection happening.
+// Don't ask the adapter to stop.
+void bleio_adapter_advertising_was_stopped(bleio_adapter_obj_t *self) {
+ self->now_advertising = false;
+ self->extended_advertising = false;
+ self->circuitpython_advertising = false;
+}
+
+bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self) {
+ check_enabled(self);
+
+ return self->now_advertising;
+}
+
+bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) {
+ check_enabled(self);
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ bleio_connection_internal_t *connection = &bleio_connections[i];
+ if (connection->conn_handle != BLE_CONN_HANDLE_INVALID) {
+ return true;
+ }
+ }
+ return false;
+}
+
+mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self) {
+ check_enabled(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 != BLE_CONN_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) {
+ // TODO
+ mp_raise_NotImplementedError(NULL);
+ check_enabled(self);
+
+ // FIX bonding_erase_storage();
+}
+
+uint16_t bleio_adapter_add_attribute(bleio_adapter_obj_t *adapter, mp_obj_t *attribute) {
+ check_enabled(adapter);
+
+ // The handle is the index of this attribute in the attributes list.
+ uint16_t handle = (uint16_t)adapter->attributes->len;
+ mp_obj_list_append(adapter->attributes, attribute);
+
+ if (mp_obj_is_type(attribute, &bleio_service_type)) {
+ adapter->last_added_service_handle = handle;
+ }
+ if (mp_obj_is_type(attribute, &bleio_characteristic_type)) {
+ adapter->last_added_characteristic_handle = handle;
+ }
+
+ return handle;
+}
+
+mp_obj_t *bleio_adapter_get_attribute(bleio_adapter_obj_t *adapter, uint16_t handle) {
+ check_enabled(adapter);
+
+ if (handle == 0 || handle >= adapter->attributes->len) {
+ return mp_const_none;
+ }
+ return adapter->attributes->items[handle];
+}
+
+uint16_t bleio_adapter_max_attribute_handle(bleio_adapter_obj_t *adapter) {
+ check_enabled(adapter);
+
+ return adapter->attributes->len - 1;
+}
+
+
+void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter) {
+ 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) {
+
+ if (!common_hal_bleio_adapter_get_enabled(adapter)) {
+ return;
+ }
+
+ // Adapter will be reset.
+ common_hal_bleio_adapter_set_enabled(adapter, false);
+
+ 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 with Python state cleanly. Keep any supervisor-only connections.
+ if (connection->connection_obj != mp_const_none &&
+ connection->conn_handle != BLE_CONN_HANDLE_INVALID) {
+ common_hal_bleio_connection_disconnect(connection);
+ }
+ connection->connection_obj = mp_const_none;
+ }
+
+}
+
+void bleio_adapter_background(bleio_adapter_obj_t *adapter) {
+ if (!common_hal_bleio_adapter_get_enabled(adapter)) {
+ return;
+ }
+
+ if (adapter->advertising_timeout_msecs > 0 &&
+ supervisor_ticks_ms64() - adapter->advertising_start_ticks > adapter->advertising_timeout_msecs) {
+ adapter->advertising_timeout_msecs = 0;
+ common_hal_bleio_adapter_stop_advertising(adapter);
+ }
+
+ hci_result_t result = hci_poll_for_incoming_pkt();
+ if (result != HCI_OK) {
+ mp_printf(&mp_plat_print, "bad hci_poll_for_incoming_pkt() result in background: %d\n", result);
+ }
+}
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/Adapter.h b/circuitpython/devices/ble_hci/common-hal/_bleio/Adapter.h
new file mode 100644
index 0000000..48d4f2e
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Adapter.h
@@ -0,0 +1,99 @@
+/*
+ * 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_BLE_HCI_COMMON_HAL_ADAPTER_H
+#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_ADAPTER_H
+
+#include "py/obj.h"
+#include "py/objtuple.h"
+
+#include "shared-bindings/_bleio/Characteristic.h"
+#include "shared-bindings/_bleio/Connection.h"
+#include "shared-bindings/_bleio/ScanResults.h"
+#include "shared-bindings/busio/UART.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+
+#ifndef BLEIO_TOTAL_CONNECTION_COUNT
+#define BLEIO_TOTAL_CONNECTION_COUNT 5
+#endif
+
+extern bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT];
+
+typedef struct _bleio_adapter_obj_t {
+ mp_obj_base_t base;
+ bleio_scanresults_obj_t *scan_results;
+ mp_obj_t name;
+ mp_obj_tuple_t *connection_objs;
+ busio_uart_obj_t *hci_uart;
+ digitalio_digitalinout_obj_t *rts_digitalinout;
+ digitalio_digitalinout_obj_t *cts_digitalinout;
+ bool allocated; // True when in use.
+ bool now_advertising;
+ bool extended_advertising;
+ bool circuitpython_advertising;
+ bool enabled;
+
+ // HCI adapter version info.
+ uint8_t hci_version;
+ uint8_t lmp_version;
+ uint16_t hci_revision;
+ uint16_t manufacturer;
+ uint16_t lmp_subversion;
+
+ // Used to monitor advertising timeout for legacy avertising.
+ uint64_t advertising_start_ticks;
+ uint64_t advertising_timeout_msecs; // If zero, do not check.
+
+ // Generic services characteristics.
+ bleio_characteristic_obj_t *device_name_characteristic;
+ bleio_characteristic_obj_t *appearance_characteristic;
+ bleio_characteristic_obj_t *service_changed_characteristic;
+
+ uint16_t max_acl_buffer_len;
+ uint16_t max_acl_num_buffers;
+ uint16_t max_adv_data_len;
+ uint8_t features[8]; // Supported BLE features.
+
+ // All the local attributes for this device. The index into the list
+ // corresponds to the handle.
+ mp_obj_list_t *attributes;
+ // Handle for last added service. Characteristics can only be added immediately after
+ // the service they belong to. This vets that.
+ uint16_t last_added_service_handle;
+ uint16_t last_added_characteristic_handle;
+} bleio_adapter_obj_t;
+
+uint16_t bleio_adapter_add_attribute(bleio_adapter_obj_t *adapter, mp_obj_t *attribute);
+void bleio_adapter_advertising_was_stopped(bleio_adapter_obj_t *self);
+mp_obj_t *bleio_adapter_get_attribute(bleio_adapter_obj_t *adapter, uint16_t handle);
+uint16_t bleio_adapter_max_attribute_handle(bleio_adapter_obj_t *adapter);
+void bleio_adapter_background(bleio_adapter_obj_t *adapter);
+void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter);
+void bleio_adapter_reset(bleio_adapter_obj_t *adapter);
+
+#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_ADAPTER_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/Attribute.c b/circuitpython/devices/ble_hci/common-hal/_bleio/Attribute.c
new file mode 100644
index 0000000..b1b31d6
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Attribute.c
@@ -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
+ *
+ * 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/Attribute.h"
+#include "shared-bindings/_bleio/Characteristic.h"
+#include "shared-bindings/_bleio/Descriptor.h"
+#include "shared-bindings/_bleio/Service.h"
+
+
+bleio_uuid_obj_t *bleio_attribute_get_uuid(mp_obj_t *attribute) {
+ if (mp_obj_is_type(attribute, &bleio_characteristic_type)) {
+ bleio_characteristic_obj_t *characteristic = MP_OBJ_TO_PTR(attribute);
+ return characteristic->uuid;
+ }
+ if (mp_obj_is_type(attribute, &bleio_descriptor_type)) {
+ bleio_descriptor_obj_t *descriptor = MP_OBJ_TO_PTR(attribute);
+ return descriptor->uuid;
+ }
+ if (mp_obj_is_type(attribute, &bleio_service_type)) {
+ bleio_service_obj_t *service = MP_OBJ_TO_PTR(attribute);
+ return service->uuid;
+ }
+ mp_raise_RuntimeError(translate("Invalid BLE attribute"));
+}
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/Attribute.h b/circuitpython/devices/ble_hci/common-hal/_bleio/Attribute.h
new file mode 100644
index 0000000..b8702ca
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Attribute.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 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_BLE_HCI_COMMON_HAL_ATTRIBUTE_H
+#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_ATTRIBUTE_H
+
+#include "shared-module/_bleio/Attribute.h"
+#include "shared-bindings/_bleio/UUID.h"
+
+bleio_uuid_obj_t *bleio_attribute_get_uuid(mp_obj_t *attribute);
+
+#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_ATTRIBUTE_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/Characteristic.c b/circuitpython/devices/ble_hci/common-hal/_bleio/Characteristic.c
new file mode 100644
index 0000000..e2f9cad
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Characteristic.c
@@ -0,0 +1,232 @@
+/*
+ * 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 "py/runtime.h"
+
+#include "shared-bindings/_bleio/__init__.h"
+#include "shared-bindings/_bleio/Characteristic.h"
+#include "shared-bindings/_bleio/CharacteristicBuffer.h"
+#include "shared-bindings/_bleio/Descriptor.h"
+#include "shared-bindings/_bleio/PacketBuffer.h"
+#include "shared-bindings/_bleio/Service.h"
+
+#include "common-hal/_bleio/Adapter.h"
+#include "common-hal/_bleio/att.h"
+
+#define CCCD_NOTIFY 0x1
+#define CCCD_INDICATE 0x2
+
+
+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) {
+ self->service = service;
+ self->uuid = uuid;
+ self->decl_handle = BLE_GATT_HANDLE_INVALID;
+ self->handle = BLE_GATT_HANDLE_INVALID;
+ self->props = props;
+ self->read_perm = read_perm;
+ self->write_perm = write_perm;
+ self->descriptor_list = mp_obj_new_list(0, NULL);
+ self->observer = mp_const_none;
+ self->user_desc = NULL;
+ self->cccd = NULL;
+ self->sccd = NULL;
+ self->value = mp_obj_new_bytes(initial_value_bufinfo->buf, initial_value_bufinfo->len);
+
+ const mp_int_t max_length_max = 512;
+ if (max_length < 0 || max_length > max_length_max) {
+ mp_raise_ValueError(translate("max_length must be <= 512"));
+ }
+ 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) {
+ 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_max_length(bleio_characteristic_obj_t *self) {
+ return self->max_length;
+}
+
+size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t *buf, size_t len) {
+ // Do GATT operations only if this characteristic has been added to a registered service.
+ if (self->handle != BLE_GATT_HANDLE_INVALID) {
+ // FIX uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
+ if (common_hal_bleio_service_get_is_remote(self->service)) {
+ // FIX read remote chars
+ // uint8_t rsp[MAX(len, 512)];
+ // FIX improve att_read_req to write into our requested buffer.
+ // return att_read_req(conn_handle, self->handle, rsp);
+ return 0; // FIX
+ } else {
+ mp_buffer_info_t bufinfo;
+ if (!mp_get_buffer(self->value, &bufinfo, MP_BUFFER_READ)) {
+ return 0;
+ }
+ const size_t actual_length = MIN(len, bufinfo.len);
+ memcpy(buf, bufinfo.buf, actual_length);
+ return actual_length;
+ }
+ }
+
+ return 0;
+}
+
+void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) {
+ 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"));
+ }
+
+ // Do GATT operations only if this characteristic has been added to a registered service.
+ if (self->handle != BLE_GATT_HANDLE_INVALID) {
+ if (common_hal_bleio_service_get_is_remote(self->service)) {
+ // FIX uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
+ if (self->props & CHAR_PROP_WRITE) {
+ // FIX writing remote chars
+ // uint8_t rsp[sizeof(bt_att_error_rsp)];
+ // att_write_req(conn_handle, self->handle, bufinfo->buf, bufinfo->len, rsp);
+ } else if (self->props & CHAR_PROP_WRITE_NO_RESPONSE) {
+ // att_write_cmd(conn_handle, self->handle, bufinfo->buff, bufinfo->len);
+ } else {
+ mp_raise_bleio_BluetoothError(translate("Characteristic not writable"));
+ }
+ } else {
+ // Always write the value locally even if no connections are active.
+ bleio_characteristic_set_local_value(self, bufinfo);
+ // Notify or indicate all active connections.
+
+ uint16_t cccd_value = 0;
+ mp_buffer_info_t cccd_bufinfo = {
+ .buf = &cccd_value,
+ .len = sizeof(cccd_value),
+ };
+
+ const bool notify = self->props & CHAR_PROP_NOTIFY;
+ const bool indicate = self->props & CHAR_PROP_INDICATE;
+ // Read the CCCD value, if there is one.
+ if ((notify | indicate) && self->cccd != NULL) {
+ common_hal_bleio_descriptor_get_value(self->cccd, cccd_bufinfo.buf, cccd_bufinfo.len);
+ }
+
+ // It's possible that both notify and indicate are set.
+ if (notify && (cccd_value & CCCD_NOTIFY)) {
+ att_notify(self->handle, bufinfo->buf, MIN(bufinfo->len, self->max_length));
+ }
+ if (indicate && (cccd_value & CCCD_INDICATE)) {
+ att_indicate(self->handle, bufinfo->buf, MIN(bufinfo->len, self->max_length));
+
+ }
+ }
+ }
+}
+
+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) {
+ if (self->handle != common_hal_bleio_adapter_obj.last_added_characteristic_handle) {
+ mp_raise_bleio_BluetoothError(
+ translate("Descriptor can only be added to most recently added characteristic"));
+ }
+
+ descriptor->handle = bleio_adapter_add_attribute(&common_hal_bleio_adapter_obj, MP_OBJ_TO_PTR(descriptor));
+ // Include this descriptor in the service handle's range.
+ self->service->end_handle = descriptor->handle;
+
+ mp_obj_list_append(MP_OBJ_FROM_PTR(self->descriptor_list),
+ MP_OBJ_FROM_PTR(descriptor));
+}
+
+void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate) {
+ if (self->cccd == NULL) {
+ mp_raise_bleio_BluetoothError(translate("No CCCD for this Characteristic"));
+ }
+
+ if (!common_hal_bleio_service_get_is_remote(self->service)) {
+ mp_raise_bleio_RoleError(translate("Can't set CCCD on local Characteristic"));
+ }
+
+ const uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
+ common_hal_bleio_check_connected(conn_handle);
+
+ uint16_t cccd_value =
+ (notify ? CCCD_NOTIFY : 0) |
+ (indicate ? CCCD_INDICATE : 0);
+
+ // FIX do remote
+ (void)cccd_value;
+ // uint8_t rsp[sizeof(bt_att_error_rsp)];
+ // if (att_write_req(conn_handle, self->cccd->handle, &cccd_value, sizeof(cccd_value)) == 0) {
+ // mp_raise_bleio_BluetoothError(translate("Could not write CCCD"));
+ // }
+}
+
+bool bleio_characteristic_set_local_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) {
+ if (self->fixed_length && bufinfo->len != self->max_length) {
+ return false;
+ }
+ if (bufinfo->len > self->max_length) {
+ return false;
+ }
+
+ self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len);
+
+ if (mp_obj_is_type(self->observer, &bleio_characteristic_buffer_type)) {
+ bleio_characteristic_buffer_update(MP_OBJ_FROM_PTR(self->observer), bufinfo);
+ } else if (mp_obj_is_type(self->observer, &bleio_packet_buffer_type)) {
+ bleio_packet_buffer_update(MP_OBJ_FROM_PTR(self->observer), bufinfo);
+ } else {
+ return false;
+ }
+ return true;
+}
+
+void bleio_characteristic_set_observer(bleio_characteristic_obj_t *self, mp_obj_t observer) {
+ self->observer = observer;
+}
+
+void bleio_characteristic_clear_observer(bleio_characteristic_obj_t *self) {
+ self->observer = mp_const_none;
+}
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/Characteristic.h b/circuitpython/devices/ble_hci/common-hal/_bleio/Characteristic.h
new file mode 100644
index 0000000..6d5a125
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Characteristic.h
@@ -0,0 +1,63 @@
+/*
+ * 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_BLE_HCI_COMMON_HAL_CHARACTERISTIC_H
+#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_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;
+ mp_obj_t value;
+ mp_obj_t observer;
+ mp_obj_list_t *descriptor_list;
+ uint16_t max_length;
+ bool fixed_length;
+ uint16_t decl_handle;
+ uint16_t handle; // Should be decl_handle+1.
+ bleio_characteristic_properties_t props;
+ bleio_attribute_security_mode_t read_perm;
+ bleio_attribute_security_mode_t write_perm;
+ bleio_descriptor_obj_t *descriptor_linked_list;
+ bleio_descriptor_obj_t *user_desc;
+ bleio_descriptor_obj_t *cccd;
+ bleio_descriptor_obj_t *sccd;
+} bleio_characteristic_obj_t;
+
+bool bleio_characteristic_set_local_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo);
+
+void bleio_characteristic_set_observer(bleio_characteristic_obj_t *self, mp_obj_t observer);
+void bleio_characteristic_clear_observer(bleio_characteristic_obj_t *self);
+
+#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_CHARACTERISTIC_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.c b/circuitpython/devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.c
new file mode 100644
index 0000000..0d87f03
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.c
@@ -0,0 +1,105 @@
+/*
+ * 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 <string.h>
+#include <stdio.h>
+
+#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/CharacteristicBuffer.h"
+#include "supervisor/shared/tick.h"
+#include "common-hal/_bleio/CharacteristicBuffer.h"
+
+// Push all the data onto the ring buffer. When the buffer is full, new bytes will be dropped.
+STATIC void write_to_ringbuf(bleio_characteristic_buffer_obj_t *self, uint8_t *data, uint16_t len) {
+ ringbuf_put_n(&self->ringbuf, data, len);
+}
+
+void bleio_characteristic_buffer_update(bleio_characteristic_buffer_obj_t *self, mp_buffer_info_t *bufinfo) {
+ write_to_ringbuf(self, bufinfo->buf, bufinfo->len);
+}
+
+// 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) {
+
+ self->characteristic = characteristic;
+ self->timeout_ms = timeout * 1000;
+ // This is a macro.
+ // true means long-lived, so it won't be moved.
+ ringbuf_alloc(&self->ringbuf, buffer_size, true);
+
+ bleio_characteristic_set_observer(characteristic, self);
+}
+
+uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode) {
+ uint64_t start_ticks = supervisor_ticks_ms64();
+
+ // Wait for all bytes received or timeout
+ while ((ringbuf_num_filled(&self->ringbuf) < len) && (supervisor_ticks_ms64() - start_ticks < self->timeout_ms)) {
+ RUN_BACKGROUND_TASKS;
+ // Allow user to break out of a timeout with a KeyboardInterrupt.
+ if (mp_hal_is_interrupted()) {
+ return 0;
+ }
+ }
+
+ uint32_t num_bytes_read = ringbuf_get_n(&self->ringbuf, data, len);
+ return num_bytes_read;
+}
+
+uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) {
+ uint16_t count = ringbuf_num_filled(&self->ringbuf);
+ return count;
+}
+
+void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) {
+ ringbuf_clear(&self->ringbuf);
+}
+
+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) {
+ if (!common_hal_bleio_characteristic_buffer_deinited(self)) {
+ bleio_characteristic_clear_observer(self->characteristic);
+ }
+}
+
+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/circuitpython/devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.h b/circuitpython/devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.h
new file mode 100644
index 0000000..107e580
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * 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_BLE_HCI_COMMON_HAL_CHARACTERISTICBUFFER_H
+#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_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;
+
+void bleio_characteristic_buffer_update(bleio_characteristic_buffer_obj_t *self, mp_buffer_info_t *bufinfo);
+
+#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_CHARACTERISTICBUFFER_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/Connection.c b/circuitpython/devices/ble_hci/common-hal/_bleio/Connection.c
new file mode 100644
index 0000000..9531231
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Connection.c
@@ -0,0 +1,772 @@
+/*
+ * 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 "att.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#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"
+
+#define BLE_ADV_LENGTH_FIELD_SIZE 1
+#define BLE_ADV_AD_TYPE_FIELD_SIZE 1
+#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1
+
+// static const ble_gap_sec_params_t pairing_sec_params = {
+// .bond = 1,
+// .mitm = 0,
+// .lesc = 0,
+// .keypress = 0,
+// .oob = 0,
+// .io_caps = BLE_GAP_IO_CAPS_NONE,
+// .min_key_size = 7,
+// .max_key_size = 16,
+// .kdist_own = { .enc = 1, .id = 1},
+// .kdist_peer = { .enc = 1, .id = 1},
+// };
+
+#define CONNECTION_DEBUG (1)
+#if CONNECTION_DEBUG
+ #define CONNECTION_DEBUG_PRINTF(...) printf(__VA_ARGS__)
+#else
+ #define CONNECTION_DEBUG_PRINTF(...)
+#endif
+
+static volatile bool m_discovery_in_process;
+static volatile bool m_discovery_successful;
+
+// FIX static bleio_service_obj_t *m_char_discovery_service;
+// FIX static bleio_characteristic_obj_t *m_desc_discovery_characteristic;
+
+// bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
+// bleio_connection_internal_t *self = (bleio_connection_internal_t*)self_in;
+
+// if (BLE_GAP_EVT_BASE <= ble_evt->header.evt_id && ble_evt->header.evt_id <= BLE_GAP_EVT_LAST &&
+// ble_evt->evt.gap_evt.conn_handle != self->conn_handle) {
+// return false;
+// }
+// if (BLE_GATTS_EVT_BASE <= ble_evt->header.evt_id && ble_evt->header.evt_id <= BLE_GATTS_EVT_LAST &&
+// ble_evt->evt.gatts_evt.conn_handle != self->conn_handle) {
+// return false;
+// }
+
+// switch (ble_evt->header.evt_id) {
+// case BLE_GAP_EVT_DISCONNECTED:
+// // Adapter.c does the work for this event.
+// break;
+
+// case BLE_GAP_EVT_PHY_UPDATE_REQUEST: {
+// ble_gap_phys_t const phys = {
+// .rx_phys = BLE_GAP_PHY_AUTO,
+// .tx_phys = BLE_GAP_PHY_AUTO,
+// };
+// sd_ble_gap_phy_update(ble_evt->evt.gap_evt.conn_handle, &phys);
+// break;
+// }
+
+// case BLE_GAP_EVT_PHY_UPDATE: { // 0x22
+// break;
+// }
+
+// case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST:
+// // SoftDevice will respond to a length update request.
+// sd_ble_gap_data_length_update(self->conn_handle, NULL, NULL);
+// break;
+
+// case BLE_GAP_EVT_DATA_LENGTH_UPDATE: { // 0x24
+// break;
+// }
+
+// case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: {
+// ble_gatts_evt_exchange_mtu_request_t *request =
+// &ble_evt->evt.gatts_evt.params.exchange_mtu_request;
+
+// uint16_t new_mtu = BLE_GATTS_VAR_ATTR_LEN_MAX;
+// if (request->client_rx_mtu < new_mtu) {
+// new_mtu = request->client_rx_mtu;
+// }
+// if (new_mtu < BLE_GATT_ATT_MTU_DEFAULT) {
+// new_mtu = BLE_GATT_ATT_MTU_DEFAULT;
+// }
+// if (self->mtu > 0) {
+// new_mtu = self->mtu;
+// }
+
+// self->mtu = new_mtu;
+// sd_ble_gatts_exchange_mtu_reply(self->conn_handle, new_mtu);
+// break;
+// }
+
+
+// case BLE_GATTC_EVT_EXCHANGE_MTU_RSP: {
+// ble_gattc_evt_exchange_mtu_rsp_t *response =
+// &ble_evt->evt.gattc_evt.params.exchange_mtu_rsp;
+
+// self->mtu = response->server_rx_mtu;
+// break;
+// }
+
+// case BLE_GATTS_EVT_WRITE:
+// // A client wrote a value.
+// // If we are bonded and it's a CCCD (UUID 0x2902), store the CCCD value.
+// if (self->conn_handle != BLE_CONN_HANDLE_INVALID &&
+// self->pair_status == PAIR_PAIRED &&
+// ble_evt->evt.gatts_evt.params.write.uuid.type == BLE_UUID_TYPE_BLE &&
+// ble_evt->evt.gatts_evt.params.write.uuid.uuid == 0x2902) {
+// //
+// // Save sys_attr data (CCCD state) in bonding area at
+// // next opportunity, but also remember time of this
+// // request, so we can consolidate closely-spaced requests.
+// self->do_bond_cccds = true;
+// self->do_bond_cccds_request_time = supervisor_ticks_ms64();
+// }
+// // Return false so other handlers get this event as well.
+// return false;
+
+// case BLE_GATTS_EVT_SYS_ATTR_MISSING:
+// sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
+// break;
+
+// #if CIRCUITPY_VERBOSE_BLE
+// // Use read authorization to snoop on all reads when doing verbose debugging.
+// case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST: {
+
+// ble_gatts_evt_rw_authorize_request_t *request =
+// &ble_evt->evt.gatts_evt.params.authorize_request;
+
+// mp_printf(&mp_plat_print, "Read %x offset %d ", request->request.read.handle, request->request.read.offset);
+// uint8_t value_bytes[22];
+// ble_gatts_value_t value;
+// value.offset = request->request.read.offset;
+// value.len = 22;
+// value.p_value = value_bytes;
+
+// sd_ble_gatts_value_get(self->conn_handle, request->request.read.handle, &value);
+// size_t len = value.len;
+// if (len > 22) {
+// len = 22;
+// }
+// for (uint8_t i = 0; i < len; i++) {
+// mp_printf(&mp_plat_print, " %02x", value_bytes[i]);
+// }
+// mp_printf(&mp_plat_print, "\n");
+// ble_gatts_rw_authorize_reply_params_t reply;
+// reply.type = request->type;
+// reply.params.read.gatt_status = BLE_GATT_STATUS_SUCCESS;
+// reply.params.read.update = false;
+// reply.params.read.offset = request->request.read.offset;
+// sd_ble_gatts_rw_authorize_reply(self->conn_handle, &reply);
+// break;
+// }
+// #endif
+
+// case BLE_GATTS_EVT_HVN_TX_COMPLETE: // Capture this for now. 0x55
+// break;
+// case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: {
+// self->conn_params_updating = true;
+// ble_gap_evt_conn_param_update_request_t *request =
+// &ble_evt->evt.gap_evt.params.conn_param_update_request;
+// sd_ble_gap_conn_param_update(self->conn_handle, &request->conn_params);
+// break;
+// }
+// case BLE_GAP_EVT_CONN_PARAM_UPDATE: { // 0x12
+// ble_gap_evt_conn_param_update_t *result =
+// &ble_evt->evt.gap_evt.params.conn_param_update;
+
+// #if CIRCUITPY_VERBOSE_BLE
+// ble_gap_conn_params_t *cp = &ble_evt->evt.gap_evt.params.conn_param_update.conn_params;
+// mp_printf(&mp_plat_print, "conn params updated: min_ci %d max_ci %d s_l %d sup_timeout %d\n", cp->min_conn_interval, cp->max_conn_interval, cp->slave_latency, cp->conn_sup_timeout);
+// #endif
+
+// memcpy(&self->conn_params, &result->conn_params, sizeof(ble_gap_conn_params_t));
+// self->conn_params_updating = false;
+// break;
+// }
+// case BLE_GAP_EVT_SEC_PARAMS_REQUEST: {
+// // First time pairing.
+// // 1. Either we or peer initiate the process
+// // 2. Peer asks for security parameters using BLE_GAP_EVT_SEC_PARAMS_REQUEST.
+// // 3. Pair Key exchange ("just works" implemented now; TODO: out-of-band key pairing)
+// // 4. Connection is secured: BLE_GAP_EVT_CONN_SEC_UPDATE
+// // 5. Long-term Keys exchanged: BLE_GAP_EVT_AUTH_STATUS
+
+// bonding_clear_keys(&self->bonding_keys);
+// self->ediv = EDIV_INVALID;
+// ble_gap_sec_keyset_t keyset = {
+// .keys_own = {
+// .p_enc_key = &self->bonding_keys.own_enc,
+// .p_id_key = NULL,
+// .p_sign_key = NULL,
+// .p_pk = NULL
+// },
+
+// .keys_peer = {
+// .p_enc_key = &self->bonding_keys.peer_enc,
+// .p_id_key = &self->bonding_keys.peer_id,
+// .p_sign_key = NULL,
+// .p_pk = NULL
+// }
+// };
+
+// sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_SUCCESS,
+// self->is_central ? NULL : &pairing_sec_params,
+// &keyset);
+// break;
+// }
+
+// case BLE_GAP_EVT_LESC_DHKEY_REQUEST:
+// // TODO for LESC pairing:
+// // sd_ble_gap_lesc_dhkey_reply(...);
+// break;
+
+// case BLE_GAP_EVT_AUTH_STATUS: { // 0x19
+// // Key exchange completed.
+// ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status;
+// self->sec_status = status->auth_status;
+// if (status->auth_status == BLE_GAP_SEC_STATUS_SUCCESS) {
+// self->ediv = self->bonding_keys.own_enc.master_id.ediv;
+// self->pair_status = PAIR_PAIRED;
+// // Save keys in bonding area at next opportunity.
+// self->do_bond_keys = true;
+// } else {
+// // Inform busy-waiter pairing has failed.
+// self->pair_status = PAIR_NOT_PAIRED;
+// }
+// break;
+// }
+
+// case BLE_GAP_EVT_SEC_INFO_REQUEST: { // 0x14
+// // Peer asks for the stored keys.
+// // - load key and return if bonded previously.
+// // - Else return NULL --> Initiate key exchange
+// ble_gap_evt_sec_info_request_t* sec_info_request = &ble_evt->evt.gap_evt.params.sec_info_request;
+// (void) sec_info_request;
+// if ( bonding_load_keys(self->is_central, sec_info_request->master_id.ediv, &self->bonding_keys) ) {
+// sd_ble_gap_sec_info_reply(
+// self->conn_handle,
+// &self->bonding_keys.own_enc.enc_info,
+// &self->bonding_keys.peer_id.id_info,
+// NULL);
+// self->ediv = self->bonding_keys.own_enc.master_id.ediv;
+// } else {
+// // We don't have stored keys. Ask for keys.
+// sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL);
+// }
+// break;
+// }
+
+// case BLE_GAP_EVT_CONN_SEC_UPDATE: { // 0x1a
+// // We get this both on first-time pairing and on subsequent pairings using stored keys.
+// ble_gap_conn_sec_t* conn_sec = &ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec;
+// if (conn_sec->sec_mode.sm <= 1 && conn_sec->sec_mode.lv <= 1) {
+// // Security setup did not succeed:
+// // mode 0, level 0 means no access
+// // mode 1, level 1 means open link
+// // mode >=1 and/or level >=1 means encryption is set up
+// self->pair_status = PAIR_NOT_PAIRED;
+// } else {
+// if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv)) {
+// // Did an sd_ble_gatts_sys_attr_set() with the stored sys_attr values.
+// } else {
+// // No matching bonding found, so use fresh system attributes.
+// sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
+// }
+// self->pair_status = PAIR_PAIRED;
+// }
+// break;
+// }
+
+// default:
+// return false;
+// }
+// return true;
+// }
+
+void bleio_connection_clear(bleio_connection_internal_t *self) {
+ mp_obj_list_clear(MP_OBJ_FROM_PTR(self->remote_service_list));
+
+ self->conn_handle = BLE_CONN_HANDLE_INVALID;
+ self->pair_status = PAIR_NOT_PAIRED;
+ self->is_central = false;
+ // FIX bonding_clear_keys(&self->bonding_keys);
+}
+
+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 != BLE_CONN_HANDLE_INVALID;
+}
+
+void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self) {
+ hci_disconnect(self->conn_handle);
+}
+
+void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond) {
+ self->pair_status = PAIR_WAITING;
+
+ // FIX check_nrf_error(sd_ble_gap_authenticate(self->conn_handle, &pairing_sec_params));
+
+ while (self->pair_status == PAIR_WAITING && !mp_hal_is_interrupted()) {
+ RUN_BACKGROUND_TASKS;
+ }
+ if (mp_hal_is_interrupted()) {
+ return;
+ }
+ // FIX check_sec_status(self->sec_status);
+}
+
+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;
+ }
+ // FIX return 1.25f * self->conn_params.min_conn_interval;
+ return 0.0f;
+}
+
+// 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 ? BT_ATT_DEFAULT_LE_MTU : 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;
+ // uint16_t interval = new_interval / 1.25f;
+ // self->conn_params.min_conn_interval = interval;
+ // self->conn_params.max_conn_interval = interval;
+ // uint32_t status = NRF_ERROR_BUSY;
+ // while (status == NRF_ERROR_BUSY) {
+ // status = sd_ble_gap_conn_param_update(self->conn_handle, &self->conn_params);
+ // RUN_BACKGROUND_TASKS;
+ // }
+ // check_nrf_error(status);
+}
+
+// service_uuid may be NULL, to discover all services.
+// STATIC bool discover_next_services(bleio_connection_internal_t* connection, uint16_t start_handle, ble_uuid_t *service_uuid) {
+// m_discovery_successful = false;
+// m_discovery_in_process = true;
+
+// uint32_t nrf_err = NRF_ERROR_BUSY;
+// while (nrf_err == NRF_ERROR_BUSY) {
+// nrf_err = sd_ble_gattc_primary_services_discover(connection->conn_handle, start_handle, service_uuid);
+// }
+// check_nrf_error(nrf_err);
+
+// // Wait for a discovery event.
+// while (m_discovery_in_process) {
+// MICROPY_VM_HOOK_LOOP;
+// }
+// return m_discovery_successful;
+// }
+
+// STATIC bool discover_next_characteristics(bleio_connection_internal_t* connection, bleio_service_obj_t *service, uint16_t start_handle) {
+// m_char_discovery_service = service;
+
+// ble_gattc_handle_range_t handle_range;
+// handle_range.start_handle = start_handle;
+// handle_range.end_handle = service->end_handle;
+
+// m_discovery_successful = false;
+// m_discovery_in_process = true;
+
+// uint32_t err_code = sd_ble_gattc_characteristics_discover(connection->conn_handle, &handle_range);
+// if (err_code != NRF_SUCCESS) {
+// return false;
+// }
+
+// // Wait for a discovery event.
+// while (m_discovery_in_process) {
+// MICROPY_VM_HOOK_LOOP;
+// }
+// return m_discovery_successful;
+// }
+
+// STATIC bool discover_next_descriptors(bleio_connection_internal_t* connection, bleio_characteristic_obj_t *characteristic, uint16_t start_handle, uint16_t end_handle) {
+// m_desc_discovery_characteristic = characteristic;
+
+// ble_gattc_handle_range_t handle_range;
+// handle_range.start_handle = start_handle;
+// handle_range.end_handle = end_handle;
+
+// m_discovery_successful = false;
+// m_discovery_in_process = true;
+
+// uint32_t err_code = sd_ble_gattc_descriptors_discover(connection->conn_handle, &handle_range);
+// if (err_code != NRF_SUCCESS) {
+// return false;
+// }
+
+// // Wait for a discovery event.
+// while (m_discovery_in_process) {
+// MICROPY_VM_HOOK_LOOP;
+// }
+// return m_discovery_successful;
+// }
+
+// STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response, bleio_connection_internal_t* connection) {
+// for (size_t i = 0; i < response->count; ++i) {
+// ble_gattc_service_t *gattc_service = &response->services[i];
+
+// bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t);
+// service->base.type = &bleio_service_type;
+
+// // Initialize several fields at once.
+// bleio_service_from_connection(service, bleio_connection_new_from_internal(connection));
+
+// service->is_remote = true;
+// service->start_handle = gattc_service->handle_range.start_handle;
+// service->end_handle = gattc_service->handle_range.end_handle;
+// service->handle = gattc_service->handle_range.start_handle;
+
+// if (gattc_service->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
+// // Known service UUID.
+// bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t);
+// uuid->base.type = &bleio_uuid_type;
+// bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_service->uuid);
+// service->uuid = uuid;
+// } else {
+// // The discovery response contained a 128-bit UUID that has not yet been registered with the
+// // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
+// // For now, just set the UUID to NULL.
+// service->uuid = NULL;
+// }
+//
+// mp_obj_list_append(MP_OBJ_FROM_PTR(connection->remote_service_list),
+// MP_OBJ_FROM_PTR(service));
+// }
+//
+// if (response->count > 0) {
+// m_discovery_successful = true;
+// }
+// m_discovery_in_process = false;
+// }
+
+// STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, bleio_connection_internal_t* connection) {
+// for (size_t i = 0; i < response->count; ++i) {
+// ble_gattc_char_t *gattc_char = &response->chars[i];
+
+// bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t);
+// characteristic->base.type = &bleio_characteristic_type;
+
+// bleio_uuid_obj_t *uuid = NULL;
+
+// if (gattc_char->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
+// // Known characteristic UUID.
+// uuid = m_new_obj(bleio_uuid_obj_t);
+// uuid->base.type = &bleio_uuid_type;
+// bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_char->uuid);
+// } else {
+// // The discovery response contained a 128-bit UUID that has not yet been registered with the
+// // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
+// // For now, just leave the UUID as NULL.
+// }
+
+// bleio_characteristic_properties_t props =
+// (gattc_char->char_props.broadcast ? CHAR_PROP_BROADCAST : 0) |
+// (gattc_char->char_props.indicate ? CHAR_PROP_INDICATE : 0) |
+// (gattc_char->char_props.notify ? CHAR_PROP_NOTIFY : 0) |
+// (gattc_char->char_props.read ? CHAR_PROP_READ : 0) |
+// (gattc_char->char_props.write ? CHAR_PROP_WRITE : 0) |
+// (gattc_char->char_props.write_wo_resp ? CHAR_PROP_WRITE_NO_RESPONSE : 0);
+
+// // Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler.
+// common_hal_bleio_characteristic_construct(
+// characteristic, m_char_discovery_service, gattc_char->handle_value, uuid,
+// props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN,
+// GATT_MAX_DATA_LENGTH, false, // max_length, fixed_length: values may not matter for gattc
+// NULL);
+
+// mp_obj_list_append(MP_OBJ_FROM_PTR(m_char_discovery_service->characteristic_list),
+// MP_OBJ_FROM_PTR(characteristic));
+// }
+
+// if (response->count > 0) {
+// m_discovery_successful = true;
+// }
+// m_discovery_in_process = false;
+// }
+
+// STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, bleio_connection_internal_t* connection) {
+// for (size_t i = 0; i < response->count; ++i) {
+// ble_gattc_desc_t *gattc_desc = &response->descs[i];
+
+// // Remember handles for certain well-known descriptors.
+// switch (gattc_desc->uuid.uuid) {
+// case BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG:
+// m_desc_discovery_characteristic->cccd_handle = gattc_desc->handle;
+// break;
+
+// case BLE_UUID_DESCRIPTOR_SERVER_CHAR_CONFIG:
+// m_desc_discovery_characteristic->sccd_handle = gattc_desc->handle;
+// break;
+
+// case BLE_UUID_DESCRIPTOR_CHAR_USER_DESC:
+// m_desc_discovery_characteristic->user_desc_handle = gattc_desc->handle;
+// break;
+
+// default:
+// // TODO: sd_ble_gattc_descriptors_discover() can return things that are not descriptors,
+// // so ignore those.
+// // https://devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors
+// break;
+// }
+
+// bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t);
+// descriptor->base.type = &bleio_descriptor_type;
+
+// bleio_uuid_obj_t *uuid = NULL;
+
+// if (gattc_desc->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
+// // Known descriptor UUID.
+// uuid = m_new_obj(bleio_uuid_obj_t);
+// uuid->base.type = &bleio_uuid_type;
+// bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_desc->uuid);
+// } else {
+// // The discovery response contained a 128-bit UUID that has not yet been registered with the
+// // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
+// // For now, just leave the UUID as NULL.
+// }
+
+// common_hal_bleio_descriptor_construct(
+// descriptor, m_desc_discovery_characteristic, uuid,
+// SECURITY_MODE_OPEN, SECURITY_MODE_OPEN,
+// GATT_MAX_DATA_LENGTH, false, mp_const_empty_bytes);
+// descriptor->handle = gattc_desc->handle;
+
+// mp_obj_list_append(MP_OBJ_FROM_PTR(m_desc_discovery_characteristic->descriptor_list),
+// MP_OBJ_FROM_PTR(descriptor));
+// }
+
+// if (response->count > 0) {
+// m_discovery_successful = true;
+// }
+// m_discovery_in_process = false;
+// }
+
+// STATIC bool discovery_on_ble_evt(ble_evt_t *ble_evt, mp_obj_t payload) {
+// bleio_connection_internal_t* connection = MP_OBJ_TO_PTR(payload);
+// switch (ble_evt->header.evt_id) {
+// case BLE_GAP_EVT_DISCONNECTED:
+// m_discovery_successful = false;
+// m_discovery_in_process = false;
+// break;
+
+// case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP:
+// on_primary_srv_discovery_rsp(&ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp, connection);
+// break;
+
+// case BLE_GATTC_EVT_CHAR_DISC_RSP:
+// on_char_discovery_rsp(&ble_evt->evt.gattc_evt.params.char_disc_rsp, connection);
+// break;
+
+// case BLE_GATTC_EVT_DESC_DISC_RSP:
+// on_desc_discovery_rsp(&ble_evt->evt.gattc_evt.params.desc_disc_rsp, connection);
+// break;
+
+// default:
+// // CONNECTION_DEBUG_PRINTF(&mp_plat_print, "Unhandled discovery event: 0x%04x\n", ble_evt->header.evt_id);
+// return false;
+// break;
+// }
+// return true;
+// }
+
+// STATIC void discover_remote_services(bleio_connection_internal_t *self, mp_obj_t service_uuids_whitelist) {
+// ble_drv_add_event_handler(discovery_on_ble_evt, self);
+
+// // Start over with an empty list.
+// self->remote_service_list = mp_obj_new_list(0, NULL);
+
+
+// if (service_uuids_whitelist == mp_const_none) {
+// // List of service UUID's not given, so discover all available services.
+
+// uint16_t next_service_start_handle = BLE_GATT_HANDLE_START;
+
+// while (discover_next_services(self, next_service_start_handle, MP_OBJ_NULL)) {
+// // discover_next_services() appends to remote_services_list.
+
+// // Get the most recently discovered service, and then ask for services
+// // whose handles start after the last attribute handle inside that service.
+// // There must be at least one if discover_next_services() returned true.
+// const bleio_service_obj_t *service =
+// self->remote_service_list->items[self->remote_service_list->len - 1];
+// next_service_start_handle = service->end_handle + 1;
+// }
+// } else {
+// mp_obj_iter_buf_t iter_buf;
+// mp_obj_t iterable = mp_getiter(service_uuids_whitelist, &iter_buf);
+// mp_obj_t uuid_obj;
+// while ((uuid_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
+// if (!mp_obj_is_type(uuid_obj, &bleio_uuid_type)) {
+// mp_raise_TypeError(translate("non-UUID found in service_uuids_whitelist"));
+// }
+// bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj);
+
+// ble_uuid_t nrf_uuid;
+// bleio_uuid_convert_to_nrf_ble_uuid(uuid, &nrf_uuid);
+
+// // Service might or might not be discovered; that's ok. Caller has to check
+// // Central.remote_services to find out.
+// // We only need to call this once for each service to discover.
+// discover_next_services(self, BLE_GATT_HANDLE_START, &nrf_uuid);
+// }
+// }
+
+
+// for (size_t i = 0; i < self->remote_service_list->len; i++) {
+// bleio_service_obj_t *service = MP_OBJ_TO_PTR(self->remote_service_list->items[i]);
+// // Skip the service if it had an unknown (unregistered) UUID.
+// if (service->uuid == NULL) {
+// continue;
+// }
+
+// uint16_t next_char_start_handle = service->start_handle;
+
+// // Stop when we go past the end of the range of handles for this service or
+// // discovery call returns nothing.
+// // discover_next_characteristics() appends to the characteristic_list.
+// while (next_char_start_handle <= service->end_handle &&
+// discover_next_characteristics(self, service, next_char_start_handle)) {
+
+
+// // Get the most recently discovered characteristic, and then ask for characteristics
+// // whose handles start after the last attribute handle inside that characteristic.
+// const bleio_characteristic_obj_t *characteristic =
+// MP_OBJ_TO_PTR(service->characteristic_list->items[service->characteristic_list->len - 1]);
+
+// next_char_start_handle = characteristic->handle + 1;
+// }
+
+// // Got characteristics for this service. Now discover descriptors for each characteristic.
+// size_t char_list_len = service->characteristic_list->len;
+// for (size_t char_idx = 0; char_idx < char_list_len; ++char_idx) {
+// bleio_characteristic_obj_t *characteristic =
+// MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx]);
+// const bool last_characteristic = char_idx == char_list_len - 1;
+// bleio_characteristic_obj_t *next_characteristic = last_characteristic
+// ? NULL
+// : MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx + 1]);
+
+// // Skip the characteristic if it had an unknown (unregistered) UUID.
+// if (characteristic->uuid == NULL) {
+// continue;
+// }
+
+// uint16_t next_desc_start_handle = characteristic->handle + 1;
+
+// // Don't run past the end of this service or the beginning of the next characteristic.
+// uint16_t next_desc_end_handle = next_characteristic == NULL
+// ? service->end_handle
+// : next_characteristic->handle - 1;
+
+// // Stop when we go past the end of the range of handles for this service or
+// // discovery call returns nothing.
+// // discover_next_descriptors() appends to the descriptor_linked_list.
+// while (next_desc_start_handle <= service->end_handle &&
+// next_desc_start_handle <= next_desc_end_handle &&
+// discover_next_descriptors(self, characteristic,
+// next_desc_start_handle, next_desc_end_handle)) {
+// // Get the most recently discovered descriptor, and then ask for descriptors
+// // whose handles start after that descriptor's handle.
+// // There must be at least one if discover_next_descriptors() returned true.
+// const bleio_descriptor_obj_t *descriptor =
+// characteristic->descriptor_list->items[characteristic->descriptor_list->len - 1];
+// next_desc_start_handle = descriptor->handle + 1;
+// }
+// }
+// }
+
+// // This event handler is no longer needed.
+// ble_drv_remove_event_handler(discovery_on_ble_evt, self);
+
+// }
+
+mp_obj_tuple_t *common_hal_bleio_connection_discover_remote_services(bleio_connection_obj_t *self, mp_obj_t service_uuids_whitelist) {
+ // FIX discover_remote_services(self->connection, 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 BLE_CONN_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/circuitpython/devices/ble_hci/common-hal/_bleio/Connection.h b/circuitpython/devices/ble_hci/common-hal/_bleio/Connection.h
new file mode 100644
index 0000000..185a8b3
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Connection.h
@@ -0,0 +1,90 @@
+/*
+ * 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_BLE_HCI_COMMON_HAL_CONNECTION_H
+#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_CONNECTION_H
+
+#include <stdbool.h>
+
+#include "py/obj.h"
+#include "py/objlist.h"
+
+#include "common-hal/_bleio/__init__.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;
+ // REMOVE ble_gap_conn_params_t conn_params;
+ 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;
+ // FIX from att.c
+ uint8_t role;
+ bt_addr_le_t addr;
+} 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;
+
+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);
+void bleio_connection_clear(bleio_connection_internal_t *self);
+
+#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_CONNECTION_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/Descriptor.c b/circuitpython/devices/ble_hci/common-hal/_bleio/Descriptor.c
new file mode 100644
index 0000000..444d57f
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Descriptor.c
@@ -0,0 +1,114 @@
+/*
+ * 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"
+
+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) {
+ self->characteristic = characteristic;
+ self->uuid = uuid;
+ self->handle = BLE_GATT_HANDLE_INVALID;
+ self->read_perm = read_perm;
+ self->write_perm = write_perm;
+ self->value = mp_obj_new_bytes(initial_value_bufinfo->buf, initial_value_bufinfo->len);
+
+ 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;
+
+ common_hal_bleio_descriptor_set_value(self, initial_value_bufinfo);
+}
+
+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 != BLE_GATT_HANDLE_INVALID) {
+ if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) {
+ // uint16_t conn_handle = bleio_connection_get_conn_handle(self->characteristic->service->connection);
+ // FIX have att_read_req fill in a buffer
+ // uint8_t rsp[MAX(len, 512)];
+ // return att_read_req(conn_handle, self->handle, rsp, len);
+ return 0;
+ } else {
+ mp_buffer_info_t bufinfo;
+ if (!mp_get_buffer(self->value, &bufinfo, MP_BUFFER_READ)) {
+ return 0;
+ }
+ const size_t actual_length = MIN(len, bufinfo.len);
+ memcpy(buf, bufinfo.buf, actual_length);
+ return actual_length;
+ }
+ }
+
+ return 0;
+}
+
+void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo) {
+ 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"));
+ }
+
+ self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len);
+
+ // Do GATT operations only if this descriptor has been registered.
+ if (self->handle != BLE_GATT_HANDLE_INVALID) {
+ if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) {
+ // FIX
+ // uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
+ // att_write_req(conn_handle, self->handle, bufinfo->buf, bufinfo->len, rsp);
+ } else {
+ // Always write the value locally even if no connections are active.
+ if (self->fixed_length && bufinfo->len != self->max_length) {
+ return;
+ }
+ if (bufinfo->len > self->max_length) {
+ return;
+ }
+
+ self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len);
+ }
+ }
+}
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/Descriptor.h b/circuitpython/devices/ble_hci/common-hal/_bleio/Descriptor.h
new file mode 100644
index 0000000..ffa0c17
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Descriptor.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
+ * 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_BLE_HCI_COMMON_HAL_DESCRIPTOR_H
+#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_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 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;
+ struct _bleio_descriptor_obj *next;
+} bleio_descriptor_obj_t;
+
+#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_DESCRIPTOR_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/PacketBuffer.c b/circuitpython/devices/ble_hci/common-hal/_bleio/PacketBuffer.c
new file mode 100644
index 0000000..7380d7e
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/PacketBuffer.c
@@ -0,0 +1,268 @@
+/*
+ * 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 <string.h>
+#include <stdio.h>
+
+#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"
+
+STATIC void write_to_ringbuf(bleio_packet_buffer_obj_t *self, uint8_t *data, uint16_t len) {
+ if (len + sizeof(uint16_t) > ringbuf_capacity(&self->ringbuf)) {
+ // This shouldn't happen.
+ return;
+ }
+ // Push all the data onto the ring buffer.
+ // Make room for the new value by dropping the oldest packets first.
+ while (ringbuf_capacity(&self->ringbuf) - ringbuf_num_filled(&self->ringbuf) < len + sizeof(uint16_t)) {
+ uint16_t packet_length;
+ ringbuf_get_n(&self->ringbuf, (uint8_t *)&packet_length, sizeof(uint16_t));
+ for (uint16_t i = 0; i < packet_length; i++) {
+ ringbuf_get(&self->ringbuf);
+ }
+ // set an overflow flag?
+ }
+ ringbuf_put_n(&self->ringbuf, (uint8_t *)&len, sizeof(uint16_t));
+ ringbuf_put_n(&self->ringbuf, data, len);
+}
+
+STATIC uint32_t queue_next_write(bleio_packet_buffer_obj_t *self) {
+ // Queue up the next outgoing buffer. We use two, one that has been passed to the SD for
+ // transmission (when packet_queued is true) and the other is `pending` and can still be
+ // modified. By primarily appending to the `pending` buffer we can reduce the protocol overhead
+ // of the lower level link and ATT layers.
+ self->packet_queued = false;
+ if (self->pending_size > 0) {
+ mp_buffer_info_t bufinfo = {
+ .buf = self->outgoing[self->pending_index],
+ .len = self->pending_size,
+ };
+ common_hal_bleio_characteristic_set_value(self->characteristic, &bufinfo);
+
+ self->pending_size = 0;
+ self->pending_index = (self->pending_index + 1) % 2;
+ self->packet_queued = true;
+ }
+ return 0;
+}
+
+void bleio_packet_buffer_update(bleio_packet_buffer_obj_t *self, mp_buffer_info_t *bufinfo) {
+ write_to_ringbuf(self, bufinfo->buf, bufinfo->len);
+}
+
+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) {
+
+ self->characteristic = characteristic;
+ self->client = self->characteristic->service->is_remote;
+ 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 = BLE_CONN_HANDLE_INVALID;
+ }
+
+ if (incoming) {
+ if (!ringbuf_alloc(&self->ringbuf, buffer_size * (sizeof(uint16_t) + max_packet_size), false)) {
+ mp_raise_ValueError(translate("Buffer too large and unable to allocate"));
+ }
+ }
+
+ if (outgoing) {
+ self->packet_queued = false;
+ self->pending_index = 0;
+ self->pending_size = 0;
+ self->outgoing[0] = m_malloc(max_packet_size, false);
+ self->outgoing[1] = m_malloc(max_packet_size, false);
+ } else {
+ self->outgoing[0] = NULL;
+ self->outgoing[1] = NULL;
+ }
+ self->max_packet_size = max_packet_size;
+
+ bleio_characteristic_set_observer(self->characteristic, self);
+}
+
+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.
+ // Get packet length, which is in first two bytes of packet.
+ uint16_t packet_length;
+ ringbuf_get_n(&self->ringbuf, (uint8_t *)&packet_length, sizeof(uint16_t));
+
+ mp_int_t ret;
+ if (packet_length > len) {
+ // Packet is longer than requested. Return negative of overrun value.
+ ret = len - packet_length;
+ // Discard the packet if it's too large. Don't fill data.
+ while (packet_length--) {
+ (void)ringbuf_get(&self->ringbuf);
+ }
+ } else {
+ // Read as much as possible, but might be shorter than len.
+ ringbuf_get_n(&self->ringbuf, data, packet_length);
+ ret = packet_length;
+ }
+
+ return ret;
+}
+
+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 == BLE_CONN_HANDLE_INVALID) {
+ return -1;
+ }
+ uint16_t outgoing_packet_length = common_hal_bleio_packet_buffer_get_outgoing_packet_length(self);
+
+ if (len + header_len > outgoing_packet_length) {
+ // Supplied data will not fit in a single BLE packet.
+ mp_raise_ValueError(translate("Total data to write is larger than outgoing_packet_length"));
+ }
+
+ if (len + self->pending_size > 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 != BLE_CONN_HANDLE_INVALID) {
+ RUN_BACKGROUND_TASKS;
+ }
+ }
+ if (self->conn_handle == BLE_CONN_HANDLE_INVALID) {
+ return -1;
+ }
+
+ size_t num_bytes_written = 0;
+
+ uint8_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(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) {
+ queue_next_write(self);
+ }
+ 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 != BLE_CONN_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->characteristic->max_length);
+ }
+ }
+ // 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 != BLE_CONN_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;
+ }
+ return MIN(self->characteristic->max_length, self->max_packet_size);
+}
+
+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)) {
+ bleio_characteristic_clear_observer(self->characteristic);
+ }
+}
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/PacketBuffer.h b/circuitpython/devices/ble_hci/common-hal/_bleio/PacketBuffer.h
new file mode 100644
index 0000000..3447174
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/PacketBuffer.h
@@ -0,0 +1,54 @@
+/*
+ * 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_BLE_HCI_COMMON_HAL_PACKETBUFFER_H
+#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_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.
+ uint8_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;
+
+void bleio_packet_buffer_update(bleio_packet_buffer_obj_t *self, mp_buffer_info_t *bufinfo);
+
+#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_PACKETBUFFER_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/Service.c b/circuitpython/devices/ble_hci/common-hal/_bleio/Service.c
new file mode 100644
index 0000000..11b7efd
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Service.c
@@ -0,0 +1,130 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 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 "shared-bindings/_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"
+
+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->uuid = uuid;
+ self->characteristic_list = characteristic_list;
+ self->is_remote = false;
+ self->connection = NULL;
+ self->is_secondary = is_secondary;
+
+ vm_used_ble = true;
+
+ self->handle = bleio_adapter_add_attribute(&common_hal_bleio_adapter_obj, MP_OBJ_TO_PTR(self));
+ self->start_handle = self->handle;
+ self->end_handle = self->handle;
+ if (self->handle == BLE_GATT_HANDLE_INVALID) {
+ return 1;
+ }
+ return 0;
+}
+
+void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary) {
+ if (_common_hal_bleio_service_construct(self, uuid, is_secondary,
+ mp_obj_new_list(0, NULL)) != 0) {
+ mp_raise_RuntimeError(translate("Failed to add service"));
+ }
+}
+
+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 (self->handle != common_hal_bleio_adapter_obj.last_added_service_handle) {
+ mp_raise_bleio_BluetoothError(
+ translate("Characteristic can only be added to most recently added service"));
+ }
+ characteristic->decl_handle = bleio_adapter_add_attribute(
+ &common_hal_bleio_adapter_obj, MP_OBJ_TO_PTR(characteristic));
+ // This is the value handle.
+ characteristic->handle = bleio_adapter_add_attribute(
+ &common_hal_bleio_adapter_obj, MP_OBJ_TO_PTR(characteristic));
+
+ self->end_handle = characteristic->handle;
+
+ if (characteristic->props & (CHAR_PROP_NOTIFY | CHAR_PROP_INDICATE)) {
+ // We need a CCCD if this characteristic is doing notify or indicate.
+ bleio_descriptor_obj_t *cccd = m_new_obj(bleio_descriptor_obj_t);
+ cccd->base.type = &bleio_descriptor_type;
+
+ uint16_t zero = 0;
+ mp_buffer_info_t zero_cccd_value = {
+ .buf = &zero,
+ .len = sizeof(zero),
+ };
+
+ common_hal_bleio_descriptor_construct(
+ cccd,
+ characteristic,
+ &cccd_uuid, // 0x2902
+ SECURITY_MODE_OPEN, // CCCD read perm
+ characteristic->read_perm, // Make CCCD write perm match characteristic read perm.
+ 2, // 2 bytes
+ true, // fixed length
+ &zero_cccd_value // Initial value is 0.
+ );
+
+ // Adds CCCD to attribute table, and also extends self->end_handle to include the CCCD.
+ common_hal_bleio_characteristic_add_descriptor(characteristic, cccd);
+ characteristic->cccd = cccd;
+ }
+
+ mp_obj_list_append(self->characteristic_list, MP_OBJ_FROM_PTR(characteristic));
+}
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/Service.h b/circuitpython/devices/ble_hci/common-hal/_bleio/Service.h
new file mode 100644
index 0000000..dce6f13
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/Service.h
@@ -0,0 +1,54 @@
+/*
+ * 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_BLE_HCI_COMMON_HAL_SERVICE_H
+#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_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 service.
+ uint16_t start_handle;
+ uint16_t end_handle;
+ struct bleio_service_obj *next;
+} bleio_service_obj_t;
+
+void bleio_service_from_connection(bleio_service_obj_t *self, mp_obj_t connection);
+
+#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_SERVICE_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/UUID.c b/circuitpython/devices/ble_hci/common-hal/_bleio/UUID.c
new file mode 100644
index 0000000..f12d1e7
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/UUID.c
@@ -0,0 +1,75 @@
+/*
+ * 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 <string.h>
+
+#include "py/runtime.h"
+#include "common-hal/_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]) {
+ self->size = uuid128 == NULL ? 16 : 128;
+ self->uuid16 = uuid16;
+ if (uuid128) {
+ memcpy(self->uuid128, uuid128, 16);
+ self->uuid128[12] = uuid16 & 0xff;
+ self->uuid128[13] = uuid16 >> 8;
+ } else {
+ memset(self->uuid128, 0, 16);
+ }
+}
+
+uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self) {
+ return self->size;
+}
+
+uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self) {
+ return self->uuid16;
+}
+
+void common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]) {
+ memcpy(uuid128, self->uuid128, 16);
+}
+
+void common_hal_bleio_uuid_pack_into(bleio_uuid_obj_t *self, uint8_t *buf) {
+ if (self->size == 16) {
+ buf[0] = self->uuid16 & 0xff;
+ buf[1] = self->uuid16 >> 8;
+ } else {
+ common_hal_bleio_uuid_get_uuid128(self, buf);
+ }
+}
+
+// Return a uui16 only if this is a standard uuid. Otherwise return BLE_UUID_UNKNOWN.
+uint16_t bleio_uuid_get_uuid16_or_unknown(bleio_uuid_obj_t *uuid) {
+ return uuid->size == 16 ? uuid->uuid16 : BLE_UUID_UNKNOWN;
+}
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/UUID.h b/circuitpython/devices/ble_hci/common-hal/_bleio/UUID.h
new file mode 100644
index 0000000..59e09f7
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/UUID.h
@@ -0,0 +1,58 @@
+/*
+ * 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_BLE_HCI_COMMON_HAL_UUID_H
+#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_UUID_H
+
+#include "py/obj.h"
+
+// Types returned by attribute table lookups. These are UUIDs.
+typedef enum {
+ BLE_UUID_UNKNOWN = 0x0000,
+ BLE_UUID_SERVICE_PRIMARY = 0x2800,
+ BLE_UUID_SERVICE_SECONDARY = 0x2801,
+ BLE_UUID_SERVICE_INCLUDE = 0x2802,// not yet implemented by us
+ BLE_UUID_CHARACTERISTIC = 0x2803,
+ BLE_UUID_CHAR_EXTENDED_PROPS = 0x2900,// not yet implemented by us
+ BLE_UUID_CHAR_USER_DESC = 0x2901,// not yet implemented by us
+ BLE_UUID_CCCD = 0x2902,
+ BLE_UUID_SCCD = 0x2903,// not yet implemented by us
+ BLE_UUID_CHAR_PRESENTATION_FMT = 0x2904, // not yet implemented by us
+ BLE_UUID_CHAR_AGGREGATE_FMT = 0x2905,// not yet implemented by us
+} ble_standard_uuid;
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t size;
+ uint16_t uuid16;
+ uint8_t uuid128[16];
+} bleio_uuid_obj_t;
+
+uint16_t bleio_uuid_get_uuid16_or_unknown(bleio_uuid_obj_t *uuid);
+
+#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_UUID_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/__init__.c b/circuitpython/devices/ble_hci/common-hal/_bleio/__init__.c
new file mode 100644
index 0000000..a22bcfb
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/__init__.c
@@ -0,0 +1,117 @@
+/*
+ * 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 <string.h>
+
+#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"
+
+// UUID shared by all cccd's.
+bleio_uuid_obj_t cccd_uuid;
+
+bool vm_used_ble;
+
+// void check_sec_status(uint8_t sec_status) {
+// if (sec_status == BLE_GAP_SEC_STATUS_SUCCESS) {
+// return;
+// }
+
+// switch (sec_status) {
+// case BLE_GAP_SEC_STATUS_UNSPECIFIED:
+// mp_raise_bleio_SecurityError(translate("Unspecified issue. Can be that the pairing prompt on the other device was declined or ignored."));
+// return;
+// default:
+// mp_raise_bleio_SecurityError(translate("Unknown security error: 0x%04x"), sec_status);
+// }
+// }
+
+void bleio_user_reset() {
+ // HCI doesn't support the BLE workflow so just do a full reset.
+ bleio_reset();
+}
+
+// Turn off BLE on a reset or reload.
+void bleio_reset() {
+ // Create a UUID object for all CCCD's.
+ cccd_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&cccd_uuid, BLE_UUID_CCCD, NULL);
+
+ bleio_hci_reset();
+
+ if (!common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) {
+ return;
+ }
+ bleio_adapter_reset(&common_hal_bleio_adapter_obj);
+ if (!vm_used_ble) {
+ // No user-code BLE operations were done, so we can maintain the supervisor state.
+ return;
+ }
+ common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false);
+ common_hal_bleio_adapter_obj.allocated = false;
+
+ bleio_set_adapter(mp_const_none);
+
+ // FIX bonding_reset();
+ supervisor_start_bluetooth();
+}
+
+// The singleton _bleio.Adapter object, bound to _bleio.adapter
+bleio_adapter_obj_t common_hal_bleio_adapter_obj = {
+ .base = {
+ .type = &bleio_adapter_type,
+ },
+};
+
+bleio_adapter_obj_t *common_hal_bleio_allocate_adapter_or_raise(void) {
+ if (common_hal_bleio_adapter_obj.allocated) {
+ mp_raise_RuntimeError(translate("Too many Adapters"));
+ }
+ return &common_hal_bleio_adapter_obj;
+}
+
+void common_hal_bleio_check_connected(uint16_t conn_handle) {
+ if (conn_handle == BLE_CONN_HANDLE_INVALID) {
+ mp_raise_ConnectionError(translate("Not connected"));
+ }
+}
+
+void common_hal_bleio_gc_collect(void) {
+ bleio_adapter_gc_collect(&common_hal_bleio_adapter_obj);
+}
+
+
+void bleio_hci_background(void) {
+ bleio_adapter_background(&common_hal_bleio_adapter_obj);
+}
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/__init__.h b/circuitpython/devices/ble_hci/common-hal/_bleio/__init__.h
new file mode 100644
index 0000000..5c9bbcc
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/__init__.h
@@ -0,0 +1,61 @@
+/*
+ * 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_BLE_HCI_COMMON_HAL_INIT_H
+#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_INIT_H
+
+#include <stdbool.h>
+
+#include "shared-bindings/_bleio/UUID.h"
+
+#include "att.h"
+#include "hci.h"
+
+void bleio_hci_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 (BT_ATT_DEFAULT_LE_MTU - 3)
+
+// FIX
+#define BLE_GATT_HANDLE_INVALID 0x0000
+#define BLE_CONN_HANDLE_INVALID 0xFFFF
+#define BLE_GATTS_FIX_ATTR_LEN_MAX (510) /**< Maximum length for fixed length Attribute Values. */
+#define BLE_GATTS_VAR_ATTR_LEN_MAX (512) /**< Maximum length for variable length Attribute Values. */
+
+// Track if the user code modified the BLE state to know if we need to undo it on reload.
+extern bool vm_used_ble;
+
+// UUID shared by all CCCD's.
+extern bleio_uuid_obj_t cccd_uuid;
+
+#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_INIT_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/att.c b/circuitpython/devices/ble_hci/common-hal/_bleio/att.c
new file mode 100644
index 0000000..abfe742
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/att.c
@@ -0,0 +1,1795 @@
+// Derived from ArduinoBLE.
+// Copyright 2020 Dan Halbert for Adafruit Industries
+
+// Some functions here are unused now, but may be used in the future.
+// Don't warn or error about this, and depend on the compiler & linker to
+// eliminate the associated code.
+#pragma GCC diagnostic ignored "-Wunused"
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+/*
+ This file is part of the ArduinoBLE library.
+ Copyright (c) 2018 Arduino SA. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "hci.h"
+#include "att.h"
+
+// Zephyr include files to define HCI communication values and structs.
+// #include "hci_include/hci.h"
+// #include "hci_include/hci_err.h"
+#include "hci_include/att_internal.h"
+#include "hci_include/l2cap_internal.h"
+
+#include "py/obj.h"
+#include "common-hal/_bleio/Adapter.h"
+#include "common-hal/_bleio/Attribute.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 "shared-bindings/_bleio/UUID.h"
+#include "supervisor/shared/tick.h"
+
+STATIC uint16_t max_mtu = BT_ATT_DEFAULT_LE_MTU; // 23
+STATIC unsigned long timeout = 5000;
+
+STATIC volatile bool confirm;
+
+STATIC uint16_t long_write_handle = BLE_GATT_HANDLE_INVALID;
+STATIC uint8_t *long_write_value = NULL;
+STATIC uint16_t long_write_value_length = 0;
+
+// When we send a request, fill this struct with info about the expected response.
+// We check this against the actual received response.
+STATIC struct {
+ uint16_t conn_handle; // Expected handle.
+ uint8_t opcode; // Expected RSP opcode.
+ uint8_t *buffer; // Pointer to response packet
+ uint8_t length; // Length of response packet.
+} expected_rsp;
+
+// A characteristic declaration has this data, in this order:
+// See Bluetooth v5.1 spec, section 3.3.1 Characteristic declaration.
+typedef struct __packed {
+ uint8_t properties;
+ uint16_t value_handle;
+ uint8_t uuid[]; // 2 or 16 bytes
+} characteristic_declaration_t;
+
+STATIC uint8_t bleio_properties_to_ble_spec_properties(uint8_t bleio_properties) {
+ uint8_t ble_spec_properties = 0;
+ if (bleio_properties & CHAR_PROP_BROADCAST) {
+ ble_spec_properties |= BT_GATT_CHRC_BROADCAST;
+ }
+ if (bleio_properties & CHAR_PROP_INDICATE) {
+ ble_spec_properties |= BT_GATT_CHRC_INDICATE;
+ }
+ if (bleio_properties & CHAR_PROP_NOTIFY) {
+ ble_spec_properties |= BT_GATT_CHRC_NOTIFY;
+ }
+ if (bleio_properties & CHAR_PROP_READ) {
+ ble_spec_properties |= BT_GATT_CHRC_READ;
+ }
+ if (bleio_properties & CHAR_PROP_WRITE) {
+ ble_spec_properties |= BT_GATT_CHRC_WRITE;
+ }
+ if (bleio_properties & CHAR_PROP_WRITE_NO_RESPONSE) {
+ ble_spec_properties |= BT_GATT_CHRC_WRITE_WITHOUT_RESP;
+ }
+
+ return ble_spec_properties;
+}
+
+// FIX not currently used; reenable when used.
+#if 0
+STATIC uint8_t ble_spec_properties_to_bleio_properties(uint8_t ble_spec_properties) {
+ uint8_t bleio_properties = 0;
+ if (ble_spec_properties & BT_GATT_CHRC_BROADCAST) {
+ bleio_properties |= CHAR_PROP_BROADCAST;
+ }
+ if (ble_spec_properties & BT_GATT_CHRC_INDICATE) {
+ bleio_properties |= CHAR_PROP_INDICATE;
+ }
+ if (ble_spec_properties & BT_GATT_CHRC_NOTIFY) {
+ bleio_properties |= CHAR_PROP_NOTIFY;
+ }
+ if (ble_spec_properties & BT_GATT_CHRC_READ) {
+ bleio_properties |= CHAR_PROP_READ;
+ }
+ if (ble_spec_properties & BT_GATT_CHRC_WRITE) {
+ bleio_properties |= CHAR_PROP_WRITE;
+ }
+ if (ble_spec_properties & BT_GATT_CHRC_WRITE_WITHOUT_RESP) {
+ bleio_properties |= CHAR_PROP_WRITE_NO_RESPONSE;
+ }
+
+ return bleio_properties;
+}
+#endif // #if 0
+
+STATIC void send_error(uint16_t conn_handle, uint8_t opcode, uint16_t handle, uint8_t code) {
+ struct __packed {
+ struct bt_att_hdr h;
+ struct bt_att_error_rsp r;
+ } rsp = { {
+ .code = BT_ATT_OP_ERROR_RSP,
+ }, {
+ .request = opcode,
+ }};
+
+ hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, sizeof(rsp), (uint8_t *)&rsp);
+}
+
+STATIC void send_req(uint16_t conn_handle, size_t request_length, uint8_t *request_buffer) {
+ hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, request_length, request_buffer);
+}
+
+STATIC int send_req_wait_for_rsp(uint16_t conn_handle, size_t request_length, uint8_t *request_buffer, uint8_t response_buffer[]) {
+ // We expect a particular kind of response after this request.
+ expected_rsp.conn_handle = conn_handle;
+ // The response opcode is the request opcode + 1.
+ expected_rsp.opcode = request_buffer[0] + 1;
+ expected_rsp.buffer = response_buffer;
+ expected_rsp.length = 0;
+
+ send_req(conn_handle, request_length, request_buffer);
+
+ if (response_buffer == NULL) {
+ // not expecting a response.
+ return 0;
+ }
+
+ for (uint64_t start = supervisor_ticks_ms64(); supervisor_ticks_ms64() - start < timeout;) {
+ // RUN_BACKGROUND_TASKS includes hci_poll_for_incoming_pkt();
+ RUN_BACKGROUND_TASKS;
+
+ if (!att_handle_is_connected(conn_handle)) {
+ break;
+ }
+
+ if (expected_rsp.length != 0) {
+ expected_rsp.conn_handle = 0xffff;
+ return expected_rsp.length;
+ }
+ }
+
+ expected_rsp.conn_handle = 0xffff;
+ return 0;
+}
+
+// If a response matches what is in expected_rsp, copy the rest of it into the buffer.
+STATIC void check_and_save_expected_rsp(uint16_t conn_handle, uint8_t opcode, uint8_t dlen, uint8_t data[]) {
+ if (conn_handle == expected_rsp.conn_handle && expected_rsp.opcode == opcode) {
+ expected_rsp.buffer[0] = opcode;
+ memcpy(&expected_rsp.buffer[1], data, dlen);
+ expected_rsp.length = dlen + 1;
+ }
+}
+
+void bleio_att_reset(void) {
+ max_mtu = BT_ATT_DEFAULT_LE_MTU;
+ timeout = 5000;
+ long_write_handle = BLE_GATT_HANDLE_INVALID;
+ long_write_value = NULL;
+ long_write_value_length = 0;
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ bleio_connections[i].conn_handle = BLE_CONN_HANDLE_INVALID;
+ bleio_connections[i].role = 0x00;
+ bleio_connections[i].addr.type = 0;
+ memset(bleio_connections[i].addr.a.val, 0, sizeof_field(bt_addr_t, val));
+ bleio_connections[i].mtu = BT_ATT_DEFAULT_LE_MTU;
+ }
+}
+
+bool att_connect_to_address(bt_addr_le_t *addr) {
+ // FIX
+ if (hci_le_create_conn(0x0060, 0x0030, 0x00, addr, 0x00,
+ 0x0006, 0x000c, 0x0000, 0x00c8, 0x0004, 0x0006) != 0) {
+ return false;
+ }
+
+ bool is_connected = false;
+
+ for (uint64_t start = supervisor_ticks_ms64(); supervisor_ticks_ms64() - start < timeout;) {
+ // RUN_BACKGROUND_TASKS includes hci_poll_for_incoming_pkt();
+ RUN_BACKGROUND_TASKS;
+
+ is_connected = att_address_is_connected(addr);
+
+ if (is_connected) {
+ break;
+ }
+ }
+
+ if (!is_connected) {
+ hci_le_cancel_conn();
+ }
+
+ return is_connected;
+}
+
+bool att_disconnect(uint16_t conn_handle) {
+ if (conn_handle == BLE_CONN_HANDLE_INVALID) {
+ return false;
+ }
+
+ hci_disconnect(conn_handle);
+
+ // Confirm we're now disconnected.
+ return !att_handle_is_connected(conn_handle);
+}
+
+// FIX
+// STATIC bool discover_services(uint16_t conn_handle, BLERemoteDevice* device, const char* serviceUuidFilter) {
+// uint16_t reqStart_handle = 0x0001;
+// uint16_t reqEnd_handle = 0xffff;
+
+// uint8_t response_buffer[max_mtu];
+
+// BLEUuid serviceUuid(serviceUuidFilter);
+
+// while (reqEnd_handle == 0xffff) {
+// int respLength = readByGroupReq(conn_handle, reqStart_handle, reqEnd_handle, BLE_UUID_SERVICE_PRIMARY, response_buffer);
+
+// if (respLength == 0) {
+// return false;
+// }
+
+// if (response_buffer[0] == BT_ATT_OP_READ_GROUP_RSP) {
+// uint16_t lengthPerService = response_buffer[1];
+// uint8_t uuidLen = lengthPerService - 4;
+
+// for (size_t i = 2; i < respLength; i += lengthPerService) {
+// struct __attribute__ ((packed)) RawService {
+// uint16_t start_handle;
+// uint16_t end_handle;
+// uint8_t uuid[16];
+// } *rawService = (RawService*)&response_buffer[i];
+
+// if (serviceUuidFilter == NULL ||
+// (uuidLen == serviceUuid.length() && memcmp(rawService->uuid, serviceUuid.data(), uuidLen) == 0)) {
+
+// BLERemoteService* service = new BLERemoteService(rawService->uuid, uuidLen,
+// rawService->start_handle,
+// rawService->end_handle);
+
+// if (service == NULL) {
+// return false;
+// }
+
+// device->addService(service);
+
+// }
+
+// reqStart_handle = rawService->end_handle + 1;
+
+// if (reqStart_handle == BLE_GATT_HANDLE_INVALID) {
+// reqEnd_handle = BLE_GATT_HANDLE_INVALID;
+// }
+// }
+// } else {
+// reqEnd_handle = BLE_GATT_HANDLE_INVALID;
+// }
+// }
+
+// return true;
+// }
+
+// STATIC bool discover_characteristics(uint16_t conn_handle, BLERemoteDevice* device) {
+// uint16_t reqStart_handle = 0x0001;
+// uint16_t reqEnd_handle = 0xffff;
+
+// uint8_t response_buffer[max_mtu];
+
+// int serviceCount = device->serviceCount();
+
+// for (size_t i = 0; i < serviceCount; i++) {
+// BLERemoteService* service = device->service(i);
+
+// reqStart_handle = service->start_handle();
+// reqEnd_handle = service->end_handle();
+
+// while (1) {
+// int respLength = readByTypeReq(conn_handle, reqStart_handle, reqEnd_handle, BLE_UUID_CHARACTERISTIC, response_buffer);
+
+// if (respLength == 0) {
+// return false;
+// }
+
+// if (response_buffer[0] == BT_ATT_OP_READ_TYPE_RSP) {
+// uint16_t lengthPerCharacteristic = response_buffer[1];
+// uint8_t uuidLen = lengthPerCharacteristic - 5;
+
+// for (size_t i = 2; i < respLength; i += lengthPerCharacteristic) {
+// struct __attribute__ ((packed)) RawCharacteristic {
+// uint16_t start_handle;
+// uint8_t properties;
+// uint16_t value_handle;
+// uint8_t uuid[16];
+// } *rawCharacteristic = (RawCharacteristic*)&response_buffer[i];
+
+// BLERemoteCharacteristic* characteristic = new BLERemoteCharacteristic(rawCharacteristic->uuid, uuidLen,
+// conn_handle,
+// rawCharacteristic->start_handle,
+// rawCharacteristic->properties,
+// rawCharacteristic->value_handle);
+
+// if (characteristic == NULL) {
+// return false;
+// }
+
+// service->addCharacteristic(characteristic);
+
+// reqStart_handle = rawCharacteristic->value_handle + 1;
+// }
+// } else {
+// break;
+// }
+// }
+// }
+
+// return true;
+// }
+
+// STATIC bool discover_descriptors(uint16_t conn_handle, BLERemoteDevice* device) {
+// uint16_t reqStart_handle = 0x0001;
+// uint16_t reqEnd_handle = 0xffff;
+
+// uint8_t response_buffer[max_mtu];
+
+// int serviceCount = device->serviceCount();
+
+// for (size_t i = 0; i < serviceCount; i++) {
+// BLERemoteService* service = device->service(i);
+
+// uint16_t serviceEnd_handle = service->end_handle();
+
+// int characteristicCount = service->characteristicCount();
+
+// for (int j = 0; j < characteristicCount; j++) {
+// BLERemoteCharacteristic* characteristic = service->characteristic(j);
+// BLERemoteCharacteristic* nextCharacteristic = (j == (characteristicCount - 1)) ? NULL : service->characteristic(j);
+
+// reqStart_handle = characteristic->value_handle() + 1;
+// reqEnd_handle = nextCharacteristic ? nextCharacteristic->value_handle() : serviceEnd_handle;
+
+// if (reqStart_handle > reqEnd_handle) {
+// continue;
+// }
+
+// while (1) {
+// int respLength = findInfoReq(conn_handle, reqStart_handle, reqEnd_handle, response_buffer);
+
+// if (respLength == 0) {
+// return false;
+// }
+
+// if (response_buffer[0] == BT_ATT_OP_FIND_INFO_RSP) {
+// uint16_t lengthPerDescriptor = response_buffer[1] * 4;
+// uint8_t uuidLen = 2;
+
+// for (size_t i = 2; i < respLength; i += lengthPerDescriptor) {
+// struct __attribute__ ((packed)) RawDescriptor {
+// uint16_t handle;
+// uint8_t uuid[16];
+// } *rawDescriptor = (RawDescriptor*)&response_buffer[i];
+
+// BLERemoteDescriptor* descriptor = new BLERemoteDescriptor(rawDescriptor->uuid, uuidLen,
+// conn_handle,
+// rawDescriptor->handle);
+
+// if (descriptor == NULL) {
+// return false;
+// }
+
+// characteristic->addDescriptor(descriptor);
+
+// reqStart_handle = rawDescriptor->handle + 1;
+// }
+// } else {
+// break;
+// }
+// }
+// }
+// }
+
+// return true;
+// }
+
+bool att_discover_attributes(bt_addr_le_t *addr, const char *service_uuid_filter) {
+ uint16_t conn_handle = att_conn_handle(addr);
+ if (conn_handle == 0xffff) {
+ return false;
+ }
+
+ // send MTU request
+ if (!att_exchange_mtu(conn_handle)) {
+ return false;
+ }
+
+ // find the device entry for the peeer
+ // FIX BLERemoteDevice* device = NULL;
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ // if (bleio_connections[i].conn_handle == conn_handle) {
+ // //FIX if (bleio_connections[i].device == NULL) {
+ // //FIX
+ // //bleio_connections[i].device = new BLERemoteDevice();
+ // //}
+
+ // //device = bleio_connections[i].device;
+
+ // break;
+ // }
+ // }
+
+ // //FIX if (device == NULL) {
+ // // return false;
+ // // }
+
+ // if (service_uuid_filter == NULL) {
+ // // clear existing services
+ // //FIX device->clear_services();
+ // } else {
+ // //FIX int service_count = device->service_count();
+
+ // for (size_t i = 0; i < service_count; i++) {
+ // //FIX BLERemoteService* service = device->service(i);
+
+ // if (strcasecmp(service->uuid(), service_uuid_filter) == 0) {
+ // // found an existing service with same UUID
+ // return true;
+ // }
+ // }
+ }
+
+ // discover services
+ // FIX
+ // if (!att_discover_services(conn_handle, device, service_uuid_filter)) {
+ // return false;
+ // }
+
+ // // discover characteristics
+ // if (!discover_characteristics(conn_handle, device)) {
+ // return false;
+ // }
+
+ // // discover descriptors396
+ // if (!discover_descriptors(conn_handle, device)) {
+ // return false;
+ // }
+
+ return true;
+}
+
+void att_set_max_mtu(uint16_t max_mtu_in) {
+ max_mtu = max_mtu_in;
+}
+
+void att_set_timeout(unsigned long timeout_in) {
+ timeout = timeout_in;
+}
+
+void att_add_connection(uint16_t handle, uint8_t role, bt_addr_le_t *peer_addr, uint16_t interval, uint16_t latency, uint16_t supervision_timeout, uint8_t master_clock_accuracy) {
+ (void)interval;
+ (void)latency;
+ (void)supervision_timeout;
+ (void)master_clock_accuracy;
+
+ int peer_index = -1;
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle == 0xffff) {
+ peer_index = i;
+ break;
+ }
+ }
+
+ if (peer_index == -1) {
+ // bail, no space
+ return;
+ }
+
+ bleio_connections[peer_index].conn_handle = handle;
+ bleio_connections[peer_index].role = role;
+ bleio_connections[peer_index].mtu = BT_ATT_DEFAULT_LE_MTU;
+ memcpy(&bleio_connections[peer_index].addr, peer_addr, sizeof(bleio_connections[peer_index].addr));
+}
+
+
+void att_remove_connection(uint16_t conn_handle, uint8_t reason) {
+ (void)reason;
+ int peer_index = -1;
+ int peer_count = 0;
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle == conn_handle) {
+ peer_index = i;
+ }
+
+ if (bleio_connections[i].conn_handle != 0xffff) {
+ peer_count++;
+ }
+ }
+
+ if (peer_index == -1) {
+ // Peer not found
+ return;
+ }
+
+ if (peer_count == 1) {
+
+ // Clear CCCD values on disconnect.
+ size_t max_attribute_handle = bleio_adapter_max_attribute_handle(&common_hal_bleio_adapter_obj);
+ for (size_t handle = 1; handle <= max_attribute_handle; handle++) {
+ mp_obj_t attribute_obj = bleio_adapter_get_attribute(&common_hal_bleio_adapter_obj, handle);
+
+ uint16_t zero = 0;
+ mp_buffer_info_t zero_cccd_value = {
+ .buf = &zero,
+ .len = sizeof(zero),
+ };
+
+ if (mp_obj_is_type(attribute_obj, &bleio_descriptor_type)) {
+ bleio_descriptor_obj_t *descriptor = MP_OBJ_TO_PTR(attribute_obj);
+ if (bleio_uuid_get_uuid16_or_unknown(descriptor->uuid) == BLE_UUID_CCCD) {
+ common_hal_bleio_descriptor_set_value(descriptor, &zero_cccd_value);
+ }
+ }
+ }
+
+ long_write_handle = BLE_GATT_HANDLE_INVALID;
+ long_write_value_length = 0;
+ }
+
+ bleio_connections[peer_index].conn_handle = 0xffff;
+ bleio_connections[peer_index].role = 0x00;
+ memset(&bleio_connections[peer_index].addr, 0x00, sizeof(bleio_connections[peer_index].addr));
+ bleio_connections[peer_index].mtu = BT_ATT_DEFAULT_LE_MTU;
+}
+
+uint16_t att_conn_handle(bt_addr_le_t *addr) {
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].addr.type == addr->type &&
+ memcmp(&bleio_connections[i].addr.a.val, addr->a.val, sizeof(addr->a.val)) == 0) {
+ return bleio_connections[i].conn_handle;
+ }
+ }
+
+ return 0xffff;
+}
+
+bool att_is_connected(void) {
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle != 0xffff) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool att_address_is_connected(bt_addr_le_t *addr) {
+ return att_conn_handle(addr) != 0xffff;
+}
+
+bool att_handle_is_connected(uint16_t handle) {
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle == handle) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+uint16_t att_mtu(uint16_t handle) {
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle == handle) {
+ return bleio_connections[i].mtu;
+ }
+ }
+
+ return BT_ATT_DEFAULT_LE_MTU;
+}
+
+bool att_disconnect_all(void) {
+ int num_disconnects = 0;
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle == 0xffff) {
+ continue;
+ }
+
+ if (att_disconnect(bleio_connections[i].conn_handle) != 0) {
+ continue;
+ }
+
+ num_disconnects++;
+
+ bleio_connections[i].conn_handle = 0xffff;
+ bleio_connections[i].role = 0x00;
+ bleio_connections[i].addr.type = 0;
+ memset(bleio_connections[i].addr.a.val, 0, sizeof(bleio_connections[i].addr.a.val));
+ bleio_connections[i].mtu = BT_ATT_DEFAULT_LE_MTU;
+ }
+
+ return num_disconnects > 0;
+}
+
+bool att_notify(uint16_t handle, const uint8_t *value, int length) {
+ int num_notifications = 0;
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle == 0xffff) {
+ continue;
+ }
+
+ typedef struct __packed {
+ struct bt_att_hdr hdr;
+ struct bt_att_notify ntf;
+ } notify_t;
+
+ size_t allowed_length = MIN((uint16_t)(bleio_connections[i].mtu - sizeof(notify_t)), (uint16_t)length);
+
+ uint8_t notify_bytes[sizeof(notify_t) + allowed_length];
+ notify_t *notify = (notify_t *)notify_bytes;
+ notify->hdr.code = BT_ATT_OP_NOTIFY;
+ ;
+ notify->ntf.handle = handle;
+ memcpy(notify->ntf.value, value, allowed_length);
+ hci_send_acl_pkt(bleio_connections[i].conn_handle, BT_L2CAP_CID_ATT,
+ sizeof(notify_bytes), notify_bytes);
+
+ num_notifications++;
+ }
+
+ return num_notifications > 0;
+}
+
+bool att_indicate(uint16_t handle, const uint8_t *value, int length) {
+ int num_indications = 0;
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle == 0xffff) {
+ continue;
+ }
+
+ typedef struct __packed {
+ struct bt_att_hdr hdr;
+ struct bt_att_indicate ind;
+ } indicate_t;
+
+ size_t allowed_length = MIN((uint16_t)(bleio_connections[i].mtu - sizeof(indicate_t)), (uint16_t)length);
+
+ uint8_t indicate_bytes[sizeof(indicate_t) + allowed_length];
+ indicate_t *indicate = (indicate_t *)indicate_bytes;
+ indicate->hdr.code = BT_ATT_OP_INDICATE;
+ ;
+ indicate->ind.handle = handle;
+ memcpy(indicate->ind.value, value, allowed_length);
+
+ confirm = false;
+
+ hci_send_acl_pkt(bleio_connections[i].conn_handle, BT_L2CAP_CID_ATT,
+ sizeof(indicate_bytes), indicate_bytes);
+
+ while (!confirm) {
+ // RUN_BACKGROUND_TASKS includes hci_poll_for_incoming_pkt();
+ RUN_BACKGROUND_TASKS;
+
+ if (!att_address_is_connected(&bleio_connections[i].addr)) {
+ break;
+ }
+ }
+
+ num_indications++;
+ }
+
+ return num_indications > 0;
+}
+
+STATIC void process_error(uint16_t conn_handle, uint8_t dlen, uint8_t data[]) {
+ struct bt_att_error_rsp *rsp = (struct bt_att_error_rsp *)data;
+
+ if (dlen != sizeof(struct bt_att_error_rsp)) {
+ // Incorrect size; ignore.
+ return;
+ }
+
+ // expected_rsp.opcode is an RSP opcode. Does it match the REQ opcode in this response?
+ if (expected_rsp.conn_handle == conn_handle && (expected_rsp.opcode - 1) == rsp->request) {
+ expected_rsp.buffer[0] = BT_ATT_OP_ERROR_RSP;
+ memcpy(&expected_rsp.buffer[1], data, dlen);
+ expected_rsp.length = dlen + 1;
+ }
+}
+
+STATIC void process_mtu_req(uint16_t conn_handle, uint8_t dlen, uint8_t data[]) {
+ struct bt_att_exchange_mtu_req *req = (struct bt_att_exchange_mtu_req *)data;
+
+ if (dlen != sizeof(struct bt_att_exchange_mtu_req)) {
+ send_error(conn_handle, BT_ATT_OP_MTU_REQ, BLE_GATT_HANDLE_INVALID, BT_ATT_ERR_INVALID_PDU);
+ return;
+ }
+
+ uint16_t mtu = req->mtu;
+
+ if (mtu > max_mtu) {
+ mtu = max_mtu;
+ }
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle == conn_handle) {
+ bleio_connections[i].mtu = mtu;
+ break;
+ }
+ }
+
+ struct __packed {
+ struct bt_att_hdr h;
+ struct bt_att_exchange_mtu_rsp r;
+ } rsp = { {
+ .code = BT_ATT_OP_MTU_RSP,
+ }, {
+ .mtu = mtu,
+ }};
+
+ hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, sizeof(rsp), (uint8_t *)&rsp);
+}
+
+STATIC void process_mtu_rsp(uint16_t conn_handle, uint8_t dlen, uint8_t data[]) {
+ struct bt_att_exchange_mtu_rsp *rsp = (struct bt_att_exchange_mtu_rsp *)data;
+
+ if (dlen != sizeof(struct bt_att_exchange_mtu_rsp)) {
+ return;
+ }
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle == conn_handle) {
+ bleio_connections[i].mtu = rsp->mtu;
+ break;
+ }
+ }
+
+ check_and_save_expected_rsp(conn_handle, BT_ATT_OP_MTU_RSP, dlen, data);
+}
+
+STATIC void process_find_info_req(uint16_t conn_handle, uint16_t mtu, uint8_t dlen, uint8_t data[]) {
+ struct bt_att_find_info_req *req = (struct bt_att_find_info_req *)data;
+
+ if (dlen != sizeof(struct bt_att_find_info_req)) {
+ send_error(conn_handle, BT_ATT_OP_FIND_INFO_REQ, req->start_handle, BT_ATT_ERR_INVALID_PDU);
+ return;
+ }
+
+ typedef struct __packed {
+ struct bt_att_hdr h;
+ struct bt_att_find_info_rsp r;
+ } rsp_t;
+
+ uint8_t rsp_bytes[mtu];
+ rsp_t *rsp = (rsp_t *)rsp_bytes;
+ rsp->h.code = BT_ATT_OP_FIND_INFO_RSP;
+
+ // Keeps track of total length of the response.
+ size_t rsp_length = sizeof(rsp_t);
+
+ bool no_data = true;
+
+ // All the data chunks must have uuid's that are the same size.
+ // Keep track of the first one to make sure.
+ size_t sizeof_first_uuid = 0;
+
+ const uint16_t max_attribute_handle = bleio_adapter_max_attribute_handle(&common_hal_bleio_adapter_obj);
+ for (uint16_t handle = req->start_handle;
+ handle <= max_attribute_handle && handle <= req->end_handle;
+ handle++) {
+
+ mp_obj_t *attribute_obj = bleio_adapter_get_attribute(&common_hal_bleio_adapter_obj, handle);
+
+ // Fetch the uuid for the given attribute, which might be a characteristic or a descriptor.
+ bleio_uuid_obj_t *uuid;
+
+ if (mp_obj_is_type(attribute_obj, &bleio_characteristic_type)) {
+ bleio_characteristic_obj_t *characteristic = MP_OBJ_TO_PTR(attribute_obj);
+ if (characteristic->handle != handle) {
+ // If the handles don't match, this is the characteristic definition attribute.
+ // Skip it. We want the characteristic value attribute.
+ continue;
+ }
+ uuid = characteristic->uuid;
+
+ } else {
+ uuid = bleio_attribute_get_uuid(attribute_obj);
+ }
+
+ const uint32_t sizeof_uuid = common_hal_bleio_uuid_get_size(uuid) / 8;
+ if (sizeof_first_uuid == 0) {
+ sizeof_first_uuid = sizeof_uuid;
+ // All the uuids in the response will be the same size.
+ rsp->r.format = sizeof_uuid == 2 ? BT_ATT_INFO_16 : BT_ATT_INFO_128;
+ }
+
+ if (sizeof_uuid != sizeof_first_uuid) {
+ // Previous UUID was a different size. We can't mix sizes.
+ // Stop and send what we have so far.
+ break;
+ }
+
+ if (rsp_length + sizeof_uuid > mtu) {
+ // No remaining room in response for this uuid.
+ break;
+ }
+
+ if (sizeof_uuid == 2) {
+ struct bt_att_info_16 *info_16 = (struct bt_att_info_16 *)&rsp_bytes[rsp_length];
+ info_16->handle = handle;
+ info_16->uuid = common_hal_bleio_uuid_get_uuid16(uuid);
+
+ rsp_length += sizeof(struct bt_att_info_16);
+ } else {
+ struct bt_att_info_128 *info_128 = (struct bt_att_info_128 *)&rsp_bytes[rsp_length];
+ info_128->handle = handle;
+ common_hal_bleio_uuid_get_uuid128(uuid, info_128->uuid);
+
+ rsp_length += sizeof(struct bt_att_info_128);
+ }
+
+ no_data = false;
+ } // end for
+
+
+ if (no_data) {
+ send_error(conn_handle, BT_ATT_OP_FIND_INFO_REQ, req->start_handle, BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
+ } else {
+ hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, rsp_length, rsp_bytes);
+ }
+}
+
+static int att_find_info_req(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, uint8_t response_buffer[]) {
+ struct __packed req {
+ struct bt_att_hdr h;
+ struct bt_att_find_info_req r;
+ } req = { {
+ .code = BT_ATT_OP_FIND_INFO_REQ,
+ }, {
+ .start_handle = start_handle,
+ .end_handle = end_handle,
+ }};
+
+ return send_req_wait_for_rsp(conn_handle, sizeof(req), (uint8_t *)&req, response_buffer);
+}
+
+STATIC void process_find_info_rsp(uint16_t conn_handle, uint8_t dlen, uint8_t data[]) {
+ if (dlen < 2) {
+ return; // invalid, drop
+ }
+
+ check_and_save_expected_rsp(conn_handle, BT_ATT_OP_FIND_INFO_RSP, dlen, data);
+}
+
+STATIC void process_find_type_req(uint16_t conn_handle, uint16_t mtu, uint8_t dlen, uint8_t data[]) {
+ struct bt_att_find_type_req *req = (struct bt_att_find_type_req *)data;
+
+ if (dlen < sizeof(struct bt_att_find_type_req)) {
+ send_error(conn_handle, BT_ATT_OP_FIND_TYPE_RSP, req->start_handle, BT_ATT_ERR_INVALID_PDU);
+ return;
+ }
+
+ uint8_t response[mtu];
+ uint16_t response_length;
+
+ response[0] = BT_ATT_OP_FIND_TYPE_RSP;
+ response_length = 1;
+
+ // FIX
+ // if (find_type_req->type == BLE_UUID_SERVICE_PRIMARY) {
+ // for (uint16_t i = (find_type_req->start_handle - 1); i < GATT.attributeCount() && i <= (find_type_req->end_handle - 1); i++) {
+ // BLELocalAttribute* attribute = GATT.attribute(i);
+
+ // if ((attribute->type() == find_type_req->type) && (attribute->uuidLength() == value_length) && memcmp(attribute->uuidData(), value, value_length) == 0) {
+ // BLELocalService* service = (BLELocalService*)attribute;
+
+ // // add the start handle
+ // uint16_t start_handle = service->start_handle();
+ // memcpy(&response[response_length], &start_handle, sizeof(start_handle));
+ // response_length += sizeof(start_handle);
+
+ // // add the end handle
+ // uint16_t end_handle = service->end_handle();
+ // memcpy(&response[response_length], &end_handle, sizeof(end_handle));
+ // response_length += sizeof(end_handle);
+ // }
+
+ // if ((response_length + 4) > mtu) {
+ // break;
+ // }
+ // }
+ // }
+
+ if (response_length == 1) {
+ send_error(conn_handle, BT_ATT_OP_FIND_TYPE_RSP, req->start_handle, BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
+ } else {
+ hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, response_length, response);
+ }
+}
+
+static void process_read_group_req(uint16_t conn_handle, uint16_t mtu, uint8_t dlen, uint8_t data[]) {
+ struct bt_att_read_group_req *req = (struct bt_att_read_group_req *)data;
+ uint16_t type_uuid = req->uuid[0] | (req->uuid[1] << 8);
+
+ // We only support returning services for BT_ATT_OP_READ_GROUP_REQ, which is typically used
+ // for service discovery.
+ if (dlen != sizeof(struct bt_att_read_group_req) + sizeof(type_uuid) ||
+ (type_uuid != BLE_UUID_SERVICE_PRIMARY &&
+ type_uuid != BLE_UUID_SERVICE_SECONDARY)) {
+ send_error(conn_handle, BT_ATT_OP_READ_GROUP_REQ, req->start_handle, BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE);
+ return;
+ }
+
+ typedef struct __packed {
+ struct bt_att_hdr h;
+ struct bt_att_read_group_rsp r;
+ } rsp_t;
+
+ uint8_t rsp_bytes[mtu];
+ rsp_t *rsp = (rsp_t *)rsp_bytes;
+ rsp->h.code = BT_ATT_OP_READ_GROUP_RSP;
+ rsp->r.len = 0;
+
+ // Keeps track of total length of the response.
+ size_t rsp_length = sizeof(rsp_t);
+
+ bool no_data = true;
+
+ // All the data chunks must have uuid's that are the same size.
+ // Keep track of the first one to make sure.
+ size_t sizeof_first_service_uuid = 0;
+
+ // Size of a single bt_att_group_data chunk. Start with the intial size, and
+ // add the uuid size in the loop below.
+ size_t data_length = sizeof(struct bt_att_group_data);
+
+ const uint16_t max_attribute_handle = bleio_adapter_max_attribute_handle(&common_hal_bleio_adapter_obj);
+ for (uint16_t handle = req->start_handle;
+ handle <= max_attribute_handle && handle <= req->end_handle;
+ handle++) {
+
+ if (rsp_length + data_length > mtu) {
+ // The next possible bt_att_group_data chunk won't fit. The response is full.
+ break;
+ }
+
+ mp_obj_t *attribute_obj = bleio_adapter_get_attribute(&common_hal_bleio_adapter_obj, handle);
+ if (mp_obj_is_type(attribute_obj, &bleio_service_type)) {
+ bleio_service_obj_t *service = MP_OBJ_TO_PTR(attribute_obj);
+
+ // Is this a 16-bit or a 128-bit uuid? It must match in size with any previous attribute
+ // in this transmission.
+ const uint32_t sizeof_service_uuid = common_hal_bleio_uuid_get_size(service->uuid) / 8;
+ if (sizeof_first_service_uuid == 0) {
+ sizeof_first_service_uuid = sizeof_service_uuid;
+ data_length += sizeof_service_uuid;
+ } else if (sizeof_first_service_uuid != sizeof_service_uuid) {
+ // Mismatched sizes, which can't be in the same batch.
+ // Transmit just what we have so far in this batch.
+ break;
+ }
+
+ // Pass the length of ONE bt_att_group_data chunk.
+ // There may be multiple chunks in this transmission.
+ rsp->r.len = data_length;
+
+ struct bt_att_group_data *group_data = (struct bt_att_group_data *)&rsp_bytes[rsp_length];
+
+ group_data->start_handle = service->start_handle;
+ group_data->end_handle = service->end_handle;
+ common_hal_bleio_uuid_pack_into(service->uuid, group_data->value);
+
+ rsp_length += data_length;
+ no_data = false;
+ }
+ }
+
+ if (no_data) {
+ send_error(conn_handle, BT_ATT_OP_READ_GROUP_REQ, req->start_handle, BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
+ } else {
+ hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, rsp_length, rsp_bytes);
+ }
+}
+
+static int att_read_group_req(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, uint16_t uuid, uint8_t response_buffer[]) {
+
+ typedef struct __packed {
+ struct bt_att_hdr h;
+ struct bt_att_read_group_req r;
+ } req_t;
+
+ uint8_t req_bytes[sizeof(req_t) + sizeof(uuid)];
+ req_t *req = (req_t *)req_bytes;
+
+ req->h.code = BT_ATT_OP_READ_GROUP_REQ;
+ req->r.start_handle = start_handle;
+ req->r.end_handle = end_handle;
+ req->r.uuid[0] = uuid & 0xff;
+ req->r.uuid[1] = uuid >> 8;
+
+ return send_req_wait_for_rsp(conn_handle, sizeof(req_bytes), req_bytes, response_buffer);
+}
+
+STATIC void process_read_group_rsp(uint16_t conn_handle, uint8_t dlen, uint8_t data[]) {
+ if (dlen < 2) {
+ return; // invalid, drop
+ }
+
+ check_and_save_expected_rsp(conn_handle, BT_ATT_OP_READ_GROUP_RSP, dlen, data);
+}
+
+STATIC void process_read_or_read_blob_req(uint16_t conn_handle, uint16_t mtu, uint8_t opcode, uint8_t dlen, uint8_t data[]) {
+ uint16_t handle;
+ uint16_t offset = 0;
+ uint8_t response_opcode;
+
+ if (opcode == BT_ATT_OP_READ_REQ) {
+ if (dlen != sizeof(struct bt_att_read_req)) {
+ send_error(conn_handle, BT_ATT_OP_READ_REQ, BLE_GATT_HANDLE_INVALID, BT_ATT_ERR_INVALID_PDU);
+ return;
+ }
+
+ struct bt_att_read_req *req = (struct bt_att_read_req *)data;
+ handle = req->handle;
+ response_opcode = BT_ATT_OP_READ_RSP;
+
+ } else if (opcode == BT_ATT_OP_READ_BLOB_REQ) {
+ if (dlen != sizeof(struct bt_att_read_blob_req)) {
+ send_error(conn_handle, BT_ATT_OP_READ_BLOB_REQ, BLE_GATT_HANDLE_INVALID, BT_ATT_ERR_INVALID_PDU);
+ return;
+ }
+
+ struct bt_att_read_blob_req *req = (struct bt_att_read_blob_req *)data;
+ handle = req->handle;
+ offset = req->offset;
+ response_opcode = BT_ATT_OP_READ_BLOB_RSP;
+ } else {
+ return;
+ }
+
+
+ if (handle > bleio_adapter_max_attribute_handle(&common_hal_bleio_adapter_obj)) {
+ send_error(conn_handle, opcode, handle, BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
+ return;
+ }
+
+ typedef struct __packed {
+ struct bt_att_hdr h;
+ struct bt_att_read_rsp r; // Same as bt_att_read_blob_rsp.
+ } rsp_t;
+
+ uint8_t rsp_bytes[mtu];
+ rsp_t *rsp = (rsp_t *)rsp_bytes;
+ rsp->h.code = response_opcode;
+
+ // Keeps track of total length of the response.
+ size_t rsp_length = sizeof(rsp_t);
+
+ mp_obj_t *attribute_obj = bleio_adapter_get_attribute(&common_hal_bleio_adapter_obj, handle);
+ if (mp_obj_is_type(attribute_obj, &bleio_service_type)) {
+ if (offset) {
+ send_error(conn_handle, BT_ATT_ERR_ATTRIBUTE_NOT_LONG, handle, BT_ATT_ERR_INVALID_PDU);
+ return;
+ }
+
+ bleio_service_obj_t *service = MP_OBJ_TO_PTR(attribute_obj);
+ const uint32_t sizeof_service_uuid = common_hal_bleio_uuid_get_size(service->uuid) / 8;
+
+ common_hal_bleio_uuid_pack_into(service->uuid, rsp->r.value);
+ rsp_length += sizeof_service_uuid;
+
+ } else if (mp_obj_is_type(attribute_obj, &bleio_characteristic_type)) {
+ bleio_characteristic_obj_t *characteristic = MP_OBJ_TO_PTR(attribute_obj);
+ if (characteristic->decl_handle == handle) {
+ // Read characteristic declaration. Return properties, value handle, and uuid.
+ if (offset) {
+ send_error(conn_handle, opcode, handle, BT_ATT_ERR_ATTRIBUTE_NOT_LONG);
+ return;
+ }
+
+ characteristic_declaration_t *char_decl = (characteristic_declaration_t *)rsp->r.value;
+
+ // Convert from the bleio properties bit values to the BLE spec properties bit values.
+ // They are not the same :(.
+ char_decl->properties = bleio_properties_to_ble_spec_properties(characteristic->props);
+ char_decl->value_handle = characteristic->handle;
+
+ const uint32_t sizeof_char_uuid = common_hal_bleio_uuid_get_size(characteristic->uuid) / 8;
+ common_hal_bleio_uuid_pack_into(characteristic->uuid, char_decl->uuid);
+ rsp_length += sizeof_char_uuid;
+
+ } else {
+ // Read characteristic value.
+
+ if ((characteristic->props & CHAR_PROP_READ) == 0) {
+ send_error(conn_handle, opcode, handle, BT_ATT_ERR_READ_NOT_PERMITTED);
+ return;
+ }
+
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer(characteristic->value, &bufinfo, MP_BUFFER_READ);
+
+ if (offset >= bufinfo.len) {
+ send_error(conn_handle, opcode, handle, BT_ATT_ERR_INVALID_OFFSET);
+ return;
+ }
+
+ size_t value_length = MIN(mtu - rsp_length, bufinfo.len - offset);
+ memcpy(rsp->r.value, bufinfo.buf + offset, value_length);
+ rsp_length += value_length;
+ }
+ } else if (mp_obj_is_type(attribute_obj, &bleio_descriptor_type)) {
+ bleio_descriptor_obj_t *descriptor = MP_OBJ_TO_PTR(attribute_obj);
+
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer(descriptor->value, &bufinfo, MP_BUFFER_READ);
+
+ if (offset >= bufinfo.len) {
+ send_error(conn_handle, opcode, handle, BT_ATT_ERR_INVALID_OFFSET);
+ return;
+ }
+
+ size_t value_length = MIN(mtu - rsp_length, bufinfo.len - offset);
+ memcpy(rsp->r.value, bufinfo.buf + offset, value_length);
+ rsp_length += value_length;
+ }
+
+ hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, rsp_length, rsp_bytes);
+}
+
+STATIC void process_read_rsp(uint16_t conn_handle, uint8_t dlen, uint8_t data[]) {
+ check_and_save_expected_rsp(conn_handle, BT_ATT_OP_READ_RSP, dlen, data);
+}
+
+STATIC void process_read_type_req(uint16_t conn_handle, uint16_t mtu, uint8_t dlen, uint8_t data[]) {
+ struct bt_att_read_type_req *req = (struct bt_att_read_type_req *)data;
+ uint16_t type_uuid = req->uuid[0] | (req->uuid[1] << 8);
+
+ if (dlen != sizeof(struct bt_att_read_type_req) + sizeof(type_uuid)) {
+ send_error(conn_handle, BT_ATT_OP_READ_TYPE_REQ, req->start_handle, BT_ATT_ERR_INVALID_PDU);
+ return;
+ }
+
+ typedef struct __packed {
+ struct bt_att_hdr h;
+ struct bt_att_read_type_rsp r;
+ } rsp_t;
+
+ uint8_t rsp_bytes[mtu];
+ rsp_t *rsp = (rsp_t *)rsp_bytes;
+ rsp->h.code = BT_ATT_OP_READ_TYPE_RSP;
+ rsp->r.len = 0;
+
+ // Keeps track of total length of the response.
+ size_t rsp_length = sizeof(rsp_t);
+
+ bool no_data = true;
+
+ // All the data chunks must have uuid's that are the same size.
+ // Keep track of the first one to make sure.
+ size_t sizeof_first_uuid = 0;
+
+ // Size of a single bt_att_data chunk. Start with the initial size, and
+ // add the uuid size and other data sizes in the loop below.
+ size_t data_length = sizeof(struct bt_att_data);
+
+ const uint16_t max_attribute_handle = bleio_adapter_max_attribute_handle(&common_hal_bleio_adapter_obj);
+ for (uint16_t handle = req->start_handle;
+ handle <= max_attribute_handle && handle <= req->end_handle;
+ handle++) {
+
+ if (rsp_length + data_length > mtu) {
+ // The next possible bt_att_data chunk won't fit. The response is full.
+ break;
+ }
+
+ mp_obj_t *attribute_obj = bleio_adapter_get_attribute(&common_hal_bleio_adapter_obj, handle);
+
+ if (type_uuid == BLE_UUID_CHARACTERISTIC &&
+ mp_obj_is_type(attribute_obj, &bleio_characteristic_type)) {
+ // Request is for characteristic declarations.
+ bleio_characteristic_obj_t *characteristic = MP_OBJ_TO_PTR(attribute_obj);
+
+ if (characteristic->handle == handle) {
+ // If the characteristic's handle is this attribute's handle, skip it:
+ // it's the attribute for characteristic value. We want to return the declaration
+ // handle attribute instead. (It will probably get skipped below, by the
+ // handle++).
+ continue;
+ }
+
+ // Is this a 16-bit or a 128-bit uuid? It must match in size with any previous attribute
+ // in this transmission.
+ const uint32_t sizeof_uuid = common_hal_bleio_uuid_get_size(characteristic->uuid) / 8;
+ if (sizeof_first_uuid == 0) {
+ sizeof_first_uuid = sizeof_uuid;
+ data_length += sizeof_uuid;
+ data_length += sizeof(characteristic_declaration_t);
+ } else if (sizeof_first_uuid != sizeof_uuid) {
+ // Mismatched sizes, which can't be in the same batch.
+ // Transmit just what we have so far in this batch.
+ break;
+ }
+
+ // Pass the length of ONE bt_att_data chunk.
+ // There may be multiple chunks in this transmission.
+ rsp->r.len = data_length;
+
+ struct bt_att_data *att_data = (struct bt_att_data *)&rsp_bytes[rsp_length];
+
+ att_data->handle = characteristic->decl_handle;
+
+ characteristic_declaration_t *char_decl = (characteristic_declaration_t *)att_data->value;
+
+ // Convert from the bleio properties bit values to the BLE spec properties bit values.
+ // They are not the same :(.
+ char_decl->properties = bleio_properties_to_ble_spec_properties(characteristic->props);
+ char_decl->value_handle = characteristic->handle;
+ common_hal_bleio_uuid_pack_into(characteristic->uuid, char_decl->uuid);
+
+ // We know the next handle will be the characteristic value handle, so skip it.
+ handle++;
+
+ rsp_length += data_length;
+ no_data = false;
+
+ } else if (mp_obj_is_type(attribute_obj, &bleio_descriptor_type)) {
+ // See if request is for a descriptor value with a 16-bit UUID, such as the CCCD.
+ bleio_descriptor_obj_t *descriptor = MP_OBJ_TO_PTR(attribute_obj);
+ if (bleio_uuid_get_uuid16_or_unknown(descriptor->uuid) == type_uuid) {
+ struct bt_att_data *att_data = (struct bt_att_data *)&rsp_bytes[rsp_length];
+
+ att_data->handle = handle;
+
+ mp_buffer_info_t bufinfo;
+ if (!mp_get_buffer(descriptor->value, &bufinfo, MP_BUFFER_READ)) {
+ break;
+ }
+ uint16_t value_size = MIN(mtu - rsp_length, bufinfo.len);
+ memcpy(att_data->value, bufinfo.buf, value_size);
+ rsp_length += value_size;
+
+ // Only return one descriptor value.
+ no_data = false;
+ break;
+ }
+
+ } else if (mp_obj_is_type(attribute_obj, &bleio_characteristic_type)) {
+ // See if request is for a characteristic value with a 16-bit UUID.
+ bleio_characteristic_obj_t *characteristic = MP_OBJ_TO_PTR(attribute_obj);
+ if (bleio_uuid_get_uuid16_or_unknown(characteristic->uuid) == type_uuid) {
+
+ struct bt_att_data *att_data = (struct bt_att_data *)&rsp_bytes[rsp_length];
+
+ att_data->handle = handle;
+
+ mp_buffer_info_t bufinfo;
+ if (!mp_get_buffer(characteristic->value, &bufinfo, MP_BUFFER_READ)) {
+ // This shouldn't happen. There should be a buf in characteristic->value.
+ break;
+ }
+ uint16_t value_size = MIN(mtu - rsp_length, bufinfo.len);
+ memcpy(att_data->value, bufinfo.buf, value_size);
+ rsp_length += value_size;
+
+ // Only return one characteristic value.
+ no_data = false;
+ break;
+ }
+ }
+ } // end for loop
+
+ if (no_data) {
+ send_error(conn_handle, BT_ATT_OP_READ_TYPE_REQ,
+ req->start_handle, BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
+ } else {
+ hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, rsp_length, rsp_bytes);
+ }
+}
+
+static int att_read_type_req(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, uint16_t type, uint8_t response_buffer[]) {
+ typedef struct __packed {
+ struct bt_att_hdr h;
+ struct bt_att_read_type_req r;
+ } req_t;
+
+ uint8_t req_bytes[sizeof(req_t) + sizeof(type)];
+ req_t *req = (req_t *)req_bytes;
+
+ req->h.code = BT_ATT_OP_READ_TYPE_REQ;
+ req->r.start_handle = start_handle;
+ req->r.end_handle = end_handle;
+ req->r.uuid[0] = type & 0xff;
+ req->r.uuid[1] = type >> 8;
+
+ return send_req_wait_for_rsp(conn_handle, sizeof(req_bytes), req_bytes, response_buffer);
+}
+
+STATIC void process_read_type_rsp(uint16_t conn_handle, uint8_t dlen, uint8_t data[]) {
+ if (dlen < 1) {
+ return; // invalid, drop
+ }
+
+ check_and_save_expected_rsp(conn_handle, BT_ATT_OP_READ_TYPE_RSP, dlen, data);
+}
+
+// Handles BT_ATT_OP_WRITE_REQ or BT_ATT_OP_WRITE_
+STATIC void process_write_req_or_cmd(uint16_t conn_handle, uint16_t mtu, uint8_t op, uint8_t dlen, uint8_t data[]) {
+ // struct bt_att_write_cmd is identical, so don't bother to split code paths based on opcode.
+ struct bt_att_write_req *req = (struct bt_att_write_req *)data;
+
+ bool with_response = (op == BT_ATT_OP_WRITE_REQ);
+
+ if (dlen < sizeof(struct bt_att_write_req)) {
+ if (with_response) {
+ send_error(conn_handle, BT_ATT_OP_WRITE_REQ, BLE_GATT_HANDLE_INVALID, BT_ATT_ERR_INVALID_PDU);
+ }
+ return;
+ }
+
+ if (req->handle > bleio_adapter_max_attribute_handle(&common_hal_bleio_adapter_obj)) {
+ if (with_response) {
+ send_error(conn_handle, BT_ATT_OP_WRITE_REQ, req->handle, BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
+ }
+ return;
+ }
+
+ size_t value_length = dlen - sizeof(struct bt_att_write_req);
+
+ mp_buffer_info_t bufinfo;
+ bufinfo.buf = req->value;
+ bufinfo.len = value_length;
+
+ mp_obj_t attribute_obj = bleio_adapter_get_attribute(&common_hal_bleio_adapter_obj, req->handle);
+
+ if (mp_obj_is_type(attribute_obj, &bleio_characteristic_type)) {
+ bleio_characteristic_obj_t *characteristic = MP_OBJ_TO_PTR(attribute_obj);
+
+ // Don't write the characteristic declaration.
+ // Also, this must be a writable characteristic.
+ if (req->handle != characteristic->handle ||
+ (with_response
+ ? (characteristic->props & CHAR_PROP_WRITE) == 0
+ : (characteristic->props & CHAR_PROP_WRITE_NO_RESPONSE) == 0)) {
+ if (with_response) {
+ send_error(conn_handle, BT_ATT_OP_WRITE_REQ, req->handle, BT_ATT_ERR_WRITE_NOT_PERMITTED);
+ }
+ return;
+ }
+
+ // Just change the local value. Don't fire off notifications, etc.
+ bleio_characteristic_set_local_value(characteristic, &bufinfo);
+
+ } else if (mp_obj_is_type(attribute_obj, &bleio_descriptor_type)) {
+ bleio_descriptor_obj_t *descriptor = MP_OBJ_TO_PTR(attribute_obj);
+ // Only CCCD's are writable.
+ if (bleio_uuid_get_uuid16_or_unknown(descriptor->uuid) != BLE_UUID_CCCD) {
+ if (with_response) {
+ send_error(conn_handle, BT_ATT_OP_WRITE_REQ, req->handle, BT_ATT_ERR_WRITE_NOT_PERMITTED);
+ }
+ return;
+ }
+
+ common_hal_bleio_descriptor_set_value(descriptor, &bufinfo);
+ }
+
+ if (with_response) {
+ // There's no data in the response. We just indicate the write happened.
+ struct bt_att_hdr rsp = {
+ .code = BT_ATT_OP_WRITE_RSP,
+ };
+
+ hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, sizeof(rsp), (uint8_t *)&rsp);
+ }
+}
+
+STATIC void process_write_rsp(uint16_t conn_handle, uint8_t dlen, uint8_t data[]) {
+ if (dlen != 0) {
+ return; // drop
+ }
+
+ check_and_save_expected_rsp(conn_handle, BT_ATT_OP_WRITE_RSP, dlen, data);
+}
+
+STATIC void process_prepare_write_req(uint16_t conn_handle, uint16_t mtu, uint8_t dlen, uint8_t data[]) {
+ struct bt_att_prepare_write_req *req = (struct bt_att_prepare_write_req *)data;
+
+ if (dlen < sizeof(struct bt_att_prepare_write_req)) {
+ send_error(conn_handle, BT_ATT_OP_PREPARE_WRITE_REQ, BLE_GATT_HANDLE_INVALID, BT_ATT_ERR_INVALID_PDU);
+ return;
+ }
+
+ uint16_t handle = req->handle;
+ uint16_t offset = req->offset;
+ (void)offset;
+
+ if (handle > bleio_adapter_max_attribute_handle(&common_hal_bleio_adapter_obj)) {
+ send_error(conn_handle, BT_ATT_OP_PREPARE_WRITE_REQ, handle, BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
+ return;
+ }
+
+ mp_obj_t *attribute = bleio_adapter_get_attribute(&common_hal_bleio_adapter_obj, handle);
+
+ if (!mp_obj_is_type(attribute, &bleio_characteristic_type)) {
+ send_error(conn_handle, BT_ATT_OP_PREPARE_WRITE_REQ, handle, BT_ATT_ERR_ATTRIBUTE_NOT_LONG);
+ return;
+ }
+
+ bleio_characteristic_obj_t *characteristic = MP_OBJ_TO_PTR(attribute);
+
+ if (handle != characteristic->handle) {
+ send_error(conn_handle, BT_ATT_OP_PREPARE_WRITE_REQ, handle, BT_ATT_ERR_ATTRIBUTE_NOT_LONG);
+ return;
+ }
+
+ if ((characteristic->props & CHAR_PROP_WRITE) == 0) {
+ send_error(conn_handle, BT_ATT_OP_PREPARE_WRITE_REQ, handle, BT_ATT_ERR_WRITE_NOT_PERMITTED);
+ return;
+ }
+
+ // FIX if (long_write_handle == BLE_GATT_HANDLE_INVALID)
+ // int valueSize = characteristic->valueSize();
+
+ // long_write_value = (uint8_t*)realloc(long_write_value, valueSize);
+ // long_write_value_length = 0;
+ // long_write_handle = handle;
+
+ // memset(long_write_value, 0x00, valueSize);
+ // } else if (long_write_handle != handle) {
+ // send_error(conn_handle, BT_ATT_OP_PREPARE_WRITE_REQ, handle, BT_ATT_ERR_UNLIKELY);
+ // return;
+ // }
+
+ // uint8_t value_length = dlen - sizeof(struct bt_att_prepare_write_req);
+ // uint8_t* value = &data[sizeof(struct bt_att_prepare_write_req)];
+
+ // if ((offset != long_write_value_length) || ((offset + value_length) > (uint16_t)characteristic->valueSize())) {
+ // send_error(conn_handle, BT_ATT_OP_PREPARE_WRITE_REQ, handle, BT_ATT_ERR_INVALID_OFFSET);
+ // return;
+ // }
+
+ // memcpy(long_write_value + offset, value, value_length);
+ // long_write_value_length += value_length;
+
+ // uint8_t response[mtu];
+ // uint16_t response_length;
+
+ // response[0] = BT_ATT_OP_PREP_WRITE_RSP;
+ // memcpy(&response[1], data, dlen);
+ // response_length = dlen + 1;
+
+ // hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, response_length, response);
+}
+
+STATIC void process_exec_write_req(uint16_t conn_handle, uint16_t mtu, uint8_t dlen, uint8_t data[]) {
+ struct bt_att_exec_write_req *req = (struct bt_att_exec_write_req *)data;
+
+ if (dlen != sizeof(struct bt_att_exec_write_req)) {
+ send_error(conn_handle, BT_ATT_OP_EXEC_WRITE_REQ, BLE_GATT_HANDLE_INVALID, BT_ATT_ERR_INVALID_PDU);
+ return;
+ }
+
+ if (long_write_handle && (req->flags & 0x01)) {
+ // FIX BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)GATT.attribute(long_write_handle - 1);
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle == conn_handle) {
+ // FIX characteristic->writeValue(BLEDevice(bleio_connections[i].address_type, bleio_connections[i].address), long_write_value, long_write_value_length);
+ break;
+ }
+ }
+ }
+
+ long_write_handle = BLE_GATT_HANDLE_INVALID;
+ long_write_value_length = 0;
+
+ uint8_t response[mtu];
+ uint16_t response_length;
+
+ response[0] = BT_ATT_OP_EXEC_WRITE_RSP;
+ response_length = 1;
+
+ hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, response_length, response);
+}
+
+STATIC void process_notify_or_indicate(uint16_t conn_handle, uint8_t opcode, uint8_t dlen, uint8_t data[]) {
+ if (dlen < 2) {
+ return; // drop
+ }
+
+ // struct bt_att_notify and bt_att_indicate are identical.
+ // FIXunused struct bt_att_notify *req = (struct bt_att_notify *) data;
+
+ // FIXunused uint8_t handle = req->handle;
+
+ for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
+ if (bleio_connections[i].conn_handle != conn_handle) {
+ continue;
+ }
+
+ // FIX BLERemoteDevice* device = bleio_connections[i].device;
+
+ // if (!device) {
+ // break;
+ // }
+
+ // int serviceCount = device->serviceCount();
+
+ // for (size_t i = 0; i < serviceCount; i++) {
+ // BLERemoteService* s = device->service(i);
+
+ // if (s->start_handle() < handle && s->end_handle() >= handle) {
+ // int characteristicCount = s->characteristicCount();
+
+ // for (int j = 0; j < characteristicCount; j++) {
+ // BLERemoteCharacteristic* c = s->characteristic(j);
+
+ // if (c->value_handle() == handle) {
+ // //FIX c->writeValue(BLEDevice(bleio_connections[i].address_type, bleio_connections[i].address), &data[2], dlen - 2);
+ // }
+ // }
+
+ // break;
+ // }
+ // }
+ }
+
+ if (opcode == BT_ATT_OP_INDICATE) {
+ // send CONFIRM for INDICATE
+
+ uint8_t op_confirm = BT_ATT_OP_CONFIRM;
+
+ hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, sizeof(op_confirm), &op_confirm);
+ }
+}
+
+STATIC void process_confirm(uint16_t conn_handle, uint8_t dlen, uint8_t data[]) {
+ (void)conn_handle;
+ (void)dlen;
+ (void)data;
+
+ confirm = true;
+}
+
+bool att_exchange_mtu(uint16_t conn_handle) {
+ uint8_t response_buffer[max_mtu];
+ struct bt_att_exchange_mtu_req req = {
+ .mtu = max_mtu,
+ };
+ return send_req_wait_for_rsp(conn_handle, sizeof(req), (uint8_t *)&req, response_buffer);
+}
+
+int att_read_req(uint16_t conn_handle, uint16_t handle, uint8_t response_buffer[]) {
+ struct __packed {
+ struct bt_att_hdr h;
+ struct bt_att_read_req r;
+ } req = { {
+ .code = BT_ATT_OP_READ_REQ,
+ }, {
+ .handle = handle,
+ }};
+
+ return send_req_wait_for_rsp(conn_handle, sizeof(req), (uint8_t *)&req, response_buffer);
+}
+
+int att_write_req(uint16_t conn_handle, uint16_t handle, const uint8_t *data, uint8_t data_len, uint8_t response_buffer[]) {
+ typedef struct __packed {
+ struct bt_att_hdr h;
+ struct bt_att_write_req r;
+ } req_t;
+
+ uint8_t req_bytes[sizeof(req_t) + data_len];
+ req_t *req = (req_t *)req_bytes;
+ req->h.code = BT_ATT_OP_WRITE_REQ;
+ req->r.handle = handle;
+ memcpy(req->r.value, data, data_len);
+
+ return send_req_wait_for_rsp(conn_handle, sizeof(req_bytes), req_bytes, response_buffer);
+}
+
+void att_write_cmd(uint16_t conn_handle, uint16_t handle, const uint8_t *data, uint8_t data_len) {
+ typedef struct __packed {
+ struct bt_att_hdr h;
+ struct bt_att_write_cmd r;
+ } cmd_t;
+
+ uint8_t cmd_bytes[sizeof(cmd_t) + data_len];
+ cmd_t *cmd = (cmd_t *)cmd_bytes;
+ cmd->h.code = BT_ATT_OP_WRITE_CMD;
+ cmd->r.handle = handle;
+ memcpy(cmd->r.value, data, data_len);
+
+ send_req(conn_handle, sizeof(cmd_bytes), cmd_bytes);
+}
+
+void att_process_data(uint16_t conn_handle, uint8_t dlen, uint8_t data[]) {
+ // Opcode is a single byte at the front of the data.
+ uint8_t opcode = data[0];
+
+ // Skip over opcode.
+ dlen--;
+ data++;
+
+ uint16_t mtu = att_mtu(conn_handle);
+
+ switch (opcode) {
+ case BT_ATT_OP_ERROR_RSP:
+ process_error(conn_handle, dlen, data);
+ break;
+
+ case BT_ATT_OP_MTU_REQ:
+ process_mtu_req(conn_handle, dlen, data);
+ break;
+
+ case BT_ATT_OP_MTU_RSP:
+ process_mtu_rsp(conn_handle, dlen, data);
+ break;
+
+ case BT_ATT_OP_FIND_INFO_REQ:
+ process_find_info_req(conn_handle, mtu, dlen, data);
+ break;
+
+ case BT_ATT_OP_FIND_INFO_RSP:
+ process_find_info_rsp(conn_handle, dlen, data);
+ break;
+
+ case BT_ATT_OP_FIND_TYPE_REQ:
+ process_find_type_req(conn_handle, mtu, dlen, data);
+ break;
+
+ case BT_ATT_OP_READ_TYPE_REQ:
+ process_read_type_req(conn_handle, mtu, dlen, data);
+ break;
+
+ case BT_ATT_OP_READ_TYPE_RSP:
+ process_read_type_rsp(conn_handle, dlen, data);
+ break;
+
+ case BT_ATT_OP_READ_GROUP_REQ:
+ process_read_group_req(conn_handle, mtu, dlen, data);
+ break;
+
+ case BT_ATT_OP_READ_GROUP_RSP:
+ process_read_group_rsp(conn_handle, dlen, data);
+ break;
+
+ case BT_ATT_OP_READ_REQ:
+ case BT_ATT_OP_READ_BLOB_REQ:
+ process_read_or_read_blob_req(conn_handle, mtu, opcode, dlen, data);
+ break;
+
+ case BT_ATT_OP_READ_RSP:
+ process_read_rsp(conn_handle, dlen, data);
+ break;
+
+ case BT_ATT_OP_WRITE_REQ:
+ case BT_ATT_OP_WRITE_CMD:
+ process_write_req_or_cmd(conn_handle, mtu, opcode, dlen, data);
+ break;
+
+ case BT_ATT_OP_WRITE_RSP:
+ process_write_rsp(conn_handle, dlen, data);
+ break;
+
+ case BT_ATT_OP_PREPARE_WRITE_REQ:
+ process_prepare_write_req(conn_handle, mtu, dlen, data);
+ break;
+
+ case BT_ATT_OP_EXEC_WRITE_REQ:
+ process_exec_write_req(conn_handle, mtu, dlen, data);
+ break;
+
+ case BT_ATT_OP_NOTIFY:
+ case BT_ATT_OP_INDICATE:
+ process_notify_or_indicate(conn_handle, opcode, dlen, data);
+ break;
+
+ case BT_ATT_OP_CONFIRM:
+ process_confirm(conn_handle, dlen, data);
+ break;
+
+ case BT_ATT_OP_READ_MULT_REQ:
+ case BT_ATT_OP_SIGNED_WRITE_CMD:
+ default:
+ send_error(conn_handle, opcode, 0x00, BT_ATT_ERR_NOT_SUPPORTED);
+ break;
+ }
+}
+
+// FIX Do we need all of these?
+static void check_att_err(uint8_t err) {
+ const compressed_string_t *msg = NULL;
+ switch (err) {
+ case 0:
+ return;
+ case BT_ATT_ERR_INVALID_HANDLE:
+ msg = translate("Invalid handle");
+ break;
+ case BT_ATT_ERR_READ_NOT_PERMITTED:
+ msg = translate("Read not permitted");
+ break;
+ case BT_ATT_ERR_WRITE_NOT_PERMITTED:
+ msg = translate("Write not permitted");
+ break;
+ case BT_ATT_ERR_INVALID_PDU:
+ msg = translate("Invalid PDU");
+ break;
+ case BT_ATT_ERR_NOT_SUPPORTED:
+ msg = translate("Not supported");
+ break;
+ case BT_ATT_ERR_INVALID_OFFSET:
+ msg = translate("Invalid offset");
+ break;
+ case BT_ATT_ERR_PREPARE_QUEUE_FULL:
+ msg = translate("Prepare queue full");
+ break;
+ case BT_ATT_ERR_ATTRIBUTE_NOT_FOUND:
+ msg = translate("Attribute not found");
+ break;
+ case BT_ATT_ERR_ATTRIBUTE_NOT_LONG:
+ msg = translate("Attribute not long");
+ break;
+ case BT_ATT_ERR_ENCRYPTION_KEY_SIZE:
+ msg = translate("Encryption key size");
+ break;
+ case BT_ATT_ERR_INVALID_ATTRIBUTE_LEN:
+ msg = translate("Invalid attribute length");
+ break;
+ case BT_ATT_ERR_UNLIKELY:
+ msg = translate("Unlikely");
+ break;
+ case BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE:
+ msg = translate("Unsupported group type");
+ break;
+ case BT_ATT_ERR_INSUFFICIENT_RESOURCES:
+ msg = translate("Insufficient resources");
+ break;
+ case BT_ATT_ERR_DB_OUT_OF_SYNC:
+ msg = translate("DB out of sync");
+ break;
+ case BT_ATT_ERR_VALUE_NOT_ALLOWED:
+ msg = translate("Value not allowed");
+ break;
+ }
+ if (msg) {
+ mp_raise_bleio_BluetoothError(msg);
+ }
+
+ switch (err) {
+ case BT_ATT_ERR_AUTHENTICATION:
+ msg = translate("Insufficient authentication");
+ break;
+ case BT_ATT_ERR_INSUFFICIENT_ENCRYPTION:
+ msg = translate("Insufficient encryption");
+ break;
+ }
+ if (msg) {
+ mp_raise_bleio_SecurityError(msg);
+ }
+
+ mp_raise_bleio_BluetoothError(translate("Unknown ATT error: 0x%02x"), err);
+}
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/att.h b/circuitpython/devices/ble_hci/common-hal/_bleio/att.h
new file mode 100644
index 0000000..e8fdd53
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/att.h
@@ -0,0 +1,57 @@
+// Derived from ArduinoBLE.
+// Copyright 2020 Dan Halbert for Adafruit Industries
+
+/*
+ This file is part of the ArduinoBLE library.
+ Copyright (c) 2018 Arduino SA. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_ATT_H
+#define MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_ATT_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "hci_include/addr.h"
+#include "hci_include/att.h"
+#include "hci_include/att_internal.h"
+
+void bleio_att_reset(void);
+
+// FIX void att_set_event_handler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler);
+bool att_address_is_connected(bt_addr_le_t *addr);
+bool att_connect_to_address(bt_addr_le_t *addr);
+bool att_disconnect(uint16_t conn_handle);
+bool att_disconnect_all(void);
+bool att_discover_attributes(bt_addr_le_t *addr, const char *service_uuid_filter);
+bool att_exchange_mtu(uint16_t conn_handle);
+bool att_handle_is_connected(uint16_t handle);
+bool att_indicate(uint16_t handle, const uint8_t *value, int length);
+bool att_is_connected(void);
+bool att_notify(uint16_t handle, const uint8_t *value, int length);
+int att_read_req(uint16_t conn_handle, uint16_t handle, uint8_t response_buffer[]);
+int att_write_req(uint16_t conn_handle, uint16_t handle, const uint8_t *data, uint8_t data_len, uint8_t response_buffer[]);
+uint16_t att_conn_handle(bt_addr_le_t *addr);
+uint16_t att_mtu(uint16_t handle);
+void att_add_connection(uint16_t handle, uint8_t role, bt_addr_le_t *peer_addr, uint16_t interval, uint16_t latency, uint16_t supervision_timeout, uint8_t master_clock_accuracy);
+void att_process_data(uint16_t conn_handle, uint8_t dlen, uint8_t data[]);
+void att_remove_connection(uint16_t conn_handle, uint8_t reason);
+void att_set_max_mtu(uint16_t max_mtu);
+void att_set_timeout(unsigned long timeout);
+void att_write_cmd(uint16_t conn_handle, uint16_t handle, const uint8_t *data, uint8_t data_len);
+
+#endif // MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_ATT_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci.c b/circuitpython/devices/ble_hci/common-hal/_bleio/hci.c
new file mode 100644
index 0000000..ec16635
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci.c
@@ -0,0 +1,805 @@
+// This file is derived from the ArduinoBLE library. Its header is below.
+/*
+ This file is part of the ArduinoBLE library.
+ Copyright (c) 2018 Arduino SA. All rights reserved.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "att.h"
+#include "hci.h"
+
+#include "py/obj.h"
+#include "py/mperrno.h"
+#include "py/runtime.h"
+
+// Zephyr include files to define HCI communication values and structs.
+#include "hci_include/hci.h"
+#include "hci_include/hci_err.h"
+#include "hci_include/l2cap_internal.h"
+
+#include <string.h>
+
+#include "py/mphal.h" // *****************************
+#include "supervisor/shared/tick.h"
+#include "shared-bindings/_bleio/__init__.h"
+#include "common-hal/_bleio/Adapter.h"
+#include "shared-bindings/microcontroller/__init__.h"
+
+// Set to 1 for extensive HCI packet logging.
+#define HCI_DEBUG 0
+
+// HCI H4 protocol packet types: first byte in the packet.
+#define H4_CMD 0x01
+#define H4_ACL 0x02
+#define H4_SCO 0x03
+#define H4_EVT 0x04
+
+#define CTS_TIMEOUT_MSECS (1000)
+#define RESPONSE_TIMEOUT_MSECS (1000)
+
+// These are the headers of the full packets that are sent over the serial interface.
+// They all have a one-byte type-field at the front, one of the H4_xxx packet types.
+
+typedef struct __attribute__ ((packed)) {
+ uint8_t pkt_type;
+ uint16_t opcode;
+ uint8_t param_len;
+ uint8_t params[];
+} h4_hci_cmd_pkt_t;
+
+#define ACL_DATA_PB_FIRST_NON_FLUSH 0
+#define ACL_DATA_PB_MIDDLE 1
+#define ACL_DATA_PB_FIRST_FLUSH 2
+#define ACL_DATA_PB_FULL 3
+
+typedef struct __attribute__ ((packed)) {
+ uint8_t pkt_type;
+ uint16_t handle : 12;
+ uint8_t pb : 2; // Packet boundary flag: ACL_DATA_PB values.
+ uint8_t bc : 2; // Broadcast flag: always 0b00 for BLE.
+ uint16_t data_len; // length of data[] in this packet.
+ uint8_t data[];
+} h4_hci_acl_pkt_t;
+
+// The ACL data in an h4_hci_acl_pkt_t may be fragmented across
+// multiple ACL_DATA packets, and need to be recombined. This is the
+// structure of the combined packet or the first fragment.
+typedef struct __attribute__ ((packed)) {
+ uint16_t acl_data_len; // Length of acl_data. Does not include this header.
+ uint16_t cid; // Channel ID.
+ uint8_t acl_data[]; // Length is acl_data_len of full packet.
+} acl_data_t;
+
+typedef struct __attribute__ ((packed)) {
+ uint8_t pkt_type;
+ uint8_t evt;
+ uint8_t param_len;
+ uint8_t params[];
+} h4_hci_evt_pkt_t;
+
+
+//////////////////////////////////////////////////////////////////////
+// Static storage:
+
+// FIX size
+#define RX_BUFFER_SIZE (3 + 255)
+#define ACL_DATA_BUFFER_SIZE (255)
+
+STATIC uint8_t rx_buffer[RX_BUFFER_SIZE];
+STATIC size_t rx_idx;
+
+STATIC uint8_t acl_data_buffer[ACL_DATA_BUFFER_SIZE];
+STATIC size_t acl_data_len;
+
+STATIC size_t num_command_packets_allowed;
+STATIC volatile size_t pending_pkt;
+
+// Results from parsing a command response packet.
+STATIC bool cmd_response_received;
+STATIC uint16_t cmd_response_opcode;
+STATIC uint8_t cmd_response_status;
+STATIC size_t cmd_response_len;
+STATIC uint8_t *cmd_response_data;
+
+STATIC volatile bool hci_poll_in_progress = false;
+
+
+//////////////////////////////////////////////////////////////////////
+
+#if HCI_DEBUG
+#include "hci_debug.c"
+#endif // HCI_DEBUG
+
+STATIC void process_acl_data_pkt(uint8_t pkt_len, uint8_t pkt_data[]) {
+ h4_hci_acl_pkt_t *pkt = (h4_hci_acl_pkt_t *)pkt_data;
+
+ if (pkt->pb != ACL_DATA_PB_MIDDLE) {
+ // This is the start of a fragmented acl_data packet or is a full packet.
+ memcpy(acl_data_buffer, pkt->data, pkt->data_len);
+ acl_data_len = pkt->data_len;
+ } else {
+ // This is a middle or end fragment of acl data.
+ // Append to the accumulated data so far.
+ memcpy(&acl_data_buffer[acl_data_len], pkt->data, pkt->data_len);
+ acl_data_len += pkt->data_len;
+ }
+
+ acl_data_t *acl = (acl_data_t *)&acl_data_buffer;
+ if (acl_data_len != sizeof(acl) + acl->acl_data_len) {
+ // We don't have the full packet yet.
+ return;
+ }
+
+ if (acl->cid == BT_L2CAP_CID_ATT) {
+ att_process_data(pkt->handle, acl->acl_data_len, acl->acl_data);
+ }
+ // } else if (aclHdr->cid == BT_L2CAP_CID_LE_SIG) {
+ // L2CAPSignaling.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &rx_buffer[1 + sizeof(HCIACLHdr)]);
+ // } else {
+ // struct __attribute__ ((packed)) {
+ // uint8_t op;
+ // uint8_t id;
+ // uint16_t length;
+ // uint16_t reason;
+ // uint16_t localCid;
+ // uint16_t remoteCid;
+ // } l2capRejectCid= { 0x01, 0x00, 0x006, 0x0002, aclHdr->cid, 0x0000 };
+
+ // sendAclPkt(aclHdr->handle & 0x0fff, 0x0005, sizeof(l2capRejectCid), &l2capRejectCid);
+ // }
+}
+
+// Process number of completed packets. Reduce number of pending packets by reported
+// number of completed.
+STATIC void process_num_comp_pkts(uint16_t handle, uint16_t num_pkts) {
+ if (num_pkts && pending_pkt > num_pkts) {
+ pending_pkt -= num_pkts;
+ } else {
+ pending_pkt = 0;
+ }
+}
+
+STATIC void process_evt_pkt(size_t pkt_len, uint8_t pkt_data[]) {
+ h4_hci_evt_pkt_t *pkt = (h4_hci_evt_pkt_t *)pkt_data;
+
+ switch (pkt->evt) {
+ case BT_HCI_EVT_DISCONN_COMPLETE: {
+ struct bt_hci_evt_disconn_complete *disconn_complete =
+ (struct bt_hci_evt_disconn_complete *)pkt->params;
+ (void)disconn_complete;
+
+ att_remove_connection(disconn_complete->handle, disconn_complete->reason);
+ // FIX L2CAPSignaling.removeConnection(disconn_complete->handle, disconn_complete->reason);
+ break;
+ }
+
+ case BT_HCI_EVT_CMD_COMPLETE: {
+ struct cmd_complete_with_status {
+ struct bt_hci_evt_cmd_complete cmd_complete;
+ struct bt_hci_evt_cc_status cc_status;
+ } __packed;
+
+ struct cmd_complete_with_status *evt = (struct cmd_complete_with_status *)pkt->params;
+
+ num_command_packets_allowed = evt->cmd_complete.ncmd;
+
+ cmd_response_received = true;
+ cmd_response_opcode = evt->cmd_complete.opcode;
+ cmd_response_status = evt->cc_status.status;
+ // All the bytes following cmd_complete, -including- the status byte, which is
+ // included in all the _bt_hci_rp_* structs.
+ cmd_response_data = (uint8_t *)&evt->cc_status;
+ // Includes status byte.
+ cmd_response_len = pkt->param_len - sizeof_field(struct cmd_complete_with_status, cmd_complete);
+
+ break;
+ }
+
+ case BT_HCI_EVT_CMD_STATUS: {
+ struct bt_hci_evt_cmd_status *evt = (struct bt_hci_evt_cmd_status *)pkt->params;
+
+ num_command_packets_allowed = evt->ncmd;
+
+ cmd_response_received = true;
+ cmd_response_opcode = evt->opcode;
+ cmd_response_status = evt->status;
+ cmd_response_data = NULL;
+ cmd_response_len = 0;
+
+ break;
+ }
+
+ case BT_HCI_EVT_NUM_COMPLETED_PACKETS: {
+ struct bt_hci_evt_num_completed_packets *evt =
+ (struct bt_hci_evt_num_completed_packets *)pkt->params;
+
+ // Start at zero-th pair: (conn handle, num completed packets).
+ struct bt_hci_handle_count *handle_and_count = &(evt->h[0]);
+ for (uint8_t i = 0; i < evt->num_handles; i++) {
+ process_num_comp_pkts(handle_and_count->handle, handle_and_count->count);
+ handle_and_count++;
+ }
+ break;
+ }
+
+ case BT_HCI_EVT_LE_META_EVENT: {
+ struct bt_hci_evt_le_meta_event *meta_evt = (struct bt_hci_evt_le_meta_event *)pkt->params;
+ uint8_t *le_evt = pkt->params + sizeof (struct bt_hci_evt_le_meta_event);
+
+ if (meta_evt->subevent == BT_HCI_EVT_LE_CONN_COMPLETE) {
+ // Advertising stops when connection occurs.
+ // We don't tell the adapter to stop, because stopping advertising
+ // when it's already stopped seems to exercise a bug in the ESP32 HCI code:
+ // It doesn't return a response.
+ bleio_adapter_advertising_was_stopped(&common_hal_bleio_adapter_obj);
+
+ struct bt_hci_evt_le_conn_complete *le_conn_complete =
+ (struct bt_hci_evt_le_conn_complete *)le_evt;
+
+ if (le_conn_complete->status == BT_HCI_ERR_SUCCESS) {
+ att_add_connection(
+ le_conn_complete->handle,
+ le_conn_complete->role,
+ &le_conn_complete->peer_addr,
+ le_conn_complete->interval,
+ le_conn_complete->latency,
+ le_conn_complete->supv_timeout,
+ le_conn_complete->clock_accuracy);
+
+ }
+ } else if (meta_evt->subevent == BT_HCI_EVT_LE_ADVERTISING_REPORT) {
+ struct bt_hci_evt_le_advertising_info *le_advertising_info =
+ (struct bt_hci_evt_le_advertising_info *)le_evt;
+ if (le_advertising_info->evt_type == BT_HCI_ADV_DIRECT_IND) {
+ // FIX
+ // last byte is RSSI
+ // GAP.handleLeAdvertisingReport(leAdvertisingReport->type,
+ // leAdvertisingReport->peerBdaddrType,
+ // leAdvertisingReport->peerBdaddr,
+ // leAdvertisingReport->eirLength,
+ // leAdvertisingReport->eirData,
+ // rssi); //FIX, don't separate
+
+ }
+ }
+ break;
+ }
+
+ default:
+ #if HCI_DEBUG
+ mp_printf(&mp_plat_print, "process_evt_pkt: Unknown event: %02x\n");
+ #endif
+ break;
+ }
+}
+
+void bleio_hci_reset(void) {
+ rx_idx = 0;
+ pending_pkt = 0;
+ hci_poll_in_progress = false;
+ bleio_att_reset();
+}
+
+hci_result_t hci_poll_for_incoming_pkt(void) {
+ common_hal_mcu_disable_interrupts();
+ if (hci_poll_in_progress) {
+ common_hal_mcu_enable_interrupts();
+ return HCI_OK;
+ }
+ hci_poll_in_progress = true;
+ common_hal_mcu_enable_interrupts();
+
+ // Assert RTS low to say we're ready to read data.
+ common_hal_digitalio_digitalinout_set_value(common_hal_bleio_adapter_obj.rts_digitalinout, false);
+
+ int errcode = 0;
+ bool packet_is_complete = false;
+
+ // Read bytes until we run out. There may be more than one packet in the input buffer.
+ while (!packet_is_complete &&
+ common_hal_busio_uart_rx_characters_available(common_hal_bleio_adapter_obj.hci_uart) > 0) {
+
+ // Read just one character a a time, so we don't accidentally get part of a second
+ // packet.
+ size_t num_read =
+ common_hal_busio_uart_read(common_hal_bleio_adapter_obj.hci_uart, rx_buffer + rx_idx, 1, &errcode);
+ if (num_read == 0) {
+ return HCI_OK;
+ }
+ if (errcode) {
+ if (errcode == EAGAIN) {
+ continue;
+ }
+ hci_poll_in_progress = false;
+ mp_printf(&mp_plat_print, "HCI_READ_ERROR, errcode: %x\n", errcode);
+ return HCI_READ_ERROR;
+ }
+ rx_idx++;
+ if (rx_idx >= sizeof(rx_buffer)) {
+ // Incoming packet is too large. Should not happen.
+ return HCI_READ_ERROR;
+ }
+
+ switch (rx_buffer[0]) {
+ case H4_ACL:
+ if (rx_idx >= sizeof(h4_hci_acl_pkt_t)) {
+ const size_t total_len =
+ sizeof(h4_hci_acl_pkt_t) + ((h4_hci_acl_pkt_t *)rx_buffer)->data_len;
+ if (rx_idx == total_len) {
+ packet_is_complete = true;
+ }
+ if (rx_idx > total_len) {
+ return HCI_PACKET_SIZE_ERROR;
+ }
+ }
+ break;
+
+ case H4_EVT:
+ if (rx_idx >= sizeof(h4_hci_evt_pkt_t)) {
+ const size_t total_len =
+ sizeof(h4_hci_evt_pkt_t) + ((h4_hci_evt_pkt_t *)rx_buffer)->param_len;
+ if (rx_idx == total_len) {
+ packet_is_complete = true;
+ }
+ if (rx_idx > total_len) {
+ return HCI_PACKET_SIZE_ERROR;
+ }
+ }
+ break;
+
+ default:
+ // Unknown or bad packet type. Start over.
+ rx_idx = 0;
+ break;
+ }
+ } // end while
+
+ if (packet_is_complete) {
+ // Stop incoming data while processing packet.
+ common_hal_digitalio_digitalinout_set_value(common_hal_bleio_adapter_obj.rts_digitalinout, true);
+ size_t pkt_len = rx_idx;
+
+ // Reset for next packet.
+ rx_idx = 0;
+ packet_is_complete = false;
+
+ switch (rx_buffer[0]) {
+ case H4_ACL:
+ #if HCI_DEBUG
+ dump_acl_pkt(false, pkt_len, rx_buffer);
+ #endif
+ process_acl_data_pkt(pkt_len, rx_buffer);
+ break;
+
+ case H4_EVT:
+ #if HCI_DEBUG
+ dump_evt_pkt(false, pkt_len, rx_buffer);
+ #endif
+ process_evt_pkt(pkt_len, rx_buffer);
+ break;
+
+ default:
+ #if HCI_DEBUG
+ mp_printf(&mp_plat_print, "Unknown HCI packet type: %d\n", rx_buffer[0]);
+ #endif
+ break;
+ }
+
+ // Let incoming bytes flow again.
+ common_hal_digitalio_digitalinout_set_value(common_hal_bleio_adapter_obj.rts_digitalinout, false);
+ }
+
+ // All done with this batch. Hold off receiving bytes until we're ready again.
+ ///common_hal_digitalio_digitalinout_set_value(common_hal_bleio_adapter_obj.rts_digitalinout, true);
+ hci_poll_in_progress = false;
+ return HCI_OK;
+}
+
+
+STATIC hci_result_t write_pkt(uint8_t *buffer, size_t len) {
+ // Wait for CTS to go low before writing to HCI adapter.
+ uint64_t start = supervisor_ticks_ms64();
+
+ while (common_hal_digitalio_digitalinout_get_value(common_hal_bleio_adapter_obj.cts_digitalinout)) {
+ RUN_BACKGROUND_TASKS;
+ if (supervisor_ticks_ms64() - start > CTS_TIMEOUT_MSECS) {
+ return HCI_WRITE_TIMEOUT;
+ }
+ }
+
+ int errcode = 0;
+ common_hal_busio_uart_write(common_hal_bleio_adapter_obj.hci_uart, buffer, len, &errcode);
+ if (errcode) {
+ return HCI_WRITE_ERROR;
+ }
+
+ return HCI_OK;
+}
+
+STATIC hci_result_t send_command(uint16_t opcode, uint8_t params_len, void *params) {
+ uint8_t cmd_pkt_len = sizeof(h4_hci_cmd_pkt_t) + params_len;
+ uint8_t tx_buffer[cmd_pkt_len];
+
+ // cmd header is at the beginning of tx_buffer
+ h4_hci_cmd_pkt_t *cmd_pkt = (h4_hci_cmd_pkt_t *)tx_buffer;
+ cmd_pkt->pkt_type = H4_CMD;
+ cmd_pkt->opcode = opcode;
+ cmd_pkt->param_len = params_len;
+
+ memcpy(cmd_pkt->params, params, params_len);
+
+ #if HCI_DEBUG
+ dump_cmd_pkt(true, sizeof(tx_buffer), tx_buffer);
+ #endif
+
+ int result = write_pkt(tx_buffer, cmd_pkt_len);
+ if (result != HCI_OK) {
+ return result;
+ }
+
+ cmd_response_received = false;
+
+ // Wait for a response. Note that other packets may be received that are not
+ // command responses.
+ uint64_t start = supervisor_ticks_ms64();
+ while (supervisor_ticks_ms64() - start < RESPONSE_TIMEOUT_MSECS) {
+ // RUN_BACKGROUND_TASKS includes hci_poll_for_incoming_pkt();
+ RUN_BACKGROUND_TASKS;
+ if (cmd_response_received && cmd_response_opcode == opcode) {
+ // If this is definitely a response to the command that was sent,
+ // return the status value, which will will be
+ // BT_HCI_ERR_SUCCESS (0x00) if the command succeeded,
+ // or a BT_HCI_ERR_x value (> 0x00) if there was a problem.
+ return cmd_response_status;
+ }
+ }
+
+ // No I/O error, but no response sent back in time.
+ return HCI_RESPONSE_TIMEOUT;
+}
+
+hci_result_t hci_send_acl_pkt(uint16_t handle, uint8_t cid, uint16_t data_len, uint8_t *data) {
+ // Wait for all backlogged packets to finish.
+ while (pending_pkt >= common_hal_bleio_adapter_obj.max_acl_num_buffers) {
+ // RUN_BACKGROUND_TASKS includes hci_poll_for_incoming_pkt();
+ RUN_BACKGROUND_TASKS;
+ }
+
+ // buf_len is size of entire packet including header.
+ const size_t buf_len = sizeof(h4_hci_acl_pkt_t) + sizeof(acl_data_t) + data_len;
+ uint8_t tx_buffer[buf_len];
+
+ h4_hci_acl_pkt_t *acl_pkt = (h4_hci_acl_pkt_t *)tx_buffer;
+ acl_data_t *acl_data = (acl_data_t *)acl_pkt->data;
+ acl_pkt->pkt_type = H4_ACL;
+ acl_pkt->handle = handle;
+ acl_pkt->pb = ACL_DATA_PB_FIRST_FLUSH;
+ acl_pkt->bc = 0;
+ acl_pkt->data_len = (uint16_t)(sizeof(acl_data_t) + data_len);
+ acl_data->acl_data_len = data_len;
+ acl_data->cid = cid;
+
+ memcpy(&acl_data->acl_data, data, data_len);
+
+ #if HCI_DEBUG
+ dump_acl_pkt(true, buf_len, tx_buffer);
+ #endif
+
+ pending_pkt++;
+
+ int errcode = 0;
+ common_hal_busio_uart_write(common_hal_bleio_adapter_obj.hci_uart, tx_buffer, buf_len, &errcode);
+ if (errcode) {
+ return HCI_WRITE_ERROR;
+ }
+ return HCI_OK;
+}
+
+hci_result_t hci_reset(void) {
+ return send_command(BT_HCI_OP_RESET, 0, NULL);
+}
+
+hci_result_t hci_read_local_version(uint8_t *hci_version, uint16_t *hci_revision, uint8_t *lmp_version, uint16_t *manufacturer, uint16_t *lmp_subversion) {
+ hci_result_t result = send_command(BT_HCI_OP_READ_LOCAL_VERSION_INFO, 0, NULL);
+ if (result == HCI_OK) {
+ struct bt_hci_rp_read_local_version_info *response =
+ (struct bt_hci_rp_read_local_version_info *)cmd_response_data;
+ *hci_version = response->hci_version;
+ *hci_revision = response->hci_revision;
+ *lmp_version = response->lmp_version;
+ *manufacturer = response->manufacturer;
+ *lmp_subversion = response->lmp_subversion;
+ }
+
+ return result;
+}
+
+hci_result_t hci_read_bd_addr(bt_addr_t *addr) {
+ int result = send_command(BT_HCI_OP_READ_BD_ADDR, 0, NULL);
+ if (result == HCI_OK) {
+ struct bt_hci_rp_read_bd_addr *response = (struct bt_hci_rp_read_bd_addr *)cmd_response_data;
+ memcpy(addr->val, response->bdaddr.val, sizeof_field(bt_addr_t, val));
+ }
+
+ return result;
+}
+
+hci_result_t hci_read_rssi(uint16_t handle, int *rssi) {
+ int result = send_command(BT_HCI_OP_READ_RSSI, sizeof(handle), &handle);
+ if (result == HCI_OK) {
+ struct bt_hci_rp_read_rssi *response = (struct bt_hci_rp_read_rssi *)cmd_response_data;
+ *rssi = response->rssi;
+ }
+
+ return result;
+}
+
+hci_result_t hci_set_event_mask(uint64_t event_mask) {
+ return send_command(BT_HCI_OP_SET_EVENT_MASK, sizeof(event_mask), &event_mask);
+}
+
+hci_result_t hci_le_read_buffer_size(uint16_t *le_max_len, uint8_t *le_max_num) {
+ int result = send_command(BT_HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
+ if (result == HCI_OK) {
+ struct bt_hci_rp_le_read_buffer_size *response =
+ (struct bt_hci_rp_le_read_buffer_size *)cmd_response_data;
+ *le_max_len = response->le_max_len;
+ *le_max_num = response->le_max_num;
+ }
+
+ return result;
+}
+
+hci_result_t hci_read_buffer_size(uint16_t *acl_max_len, uint8_t *sco_max_len, uint16_t *acl_max_num, uint16_t *sco_max_num) {
+ int result = send_command(BT_HCI_OP_READ_BUFFER_SIZE, 0, NULL);
+ if (result == HCI_OK) {
+ struct bt_hci_rp_read_buffer_size *response =
+ (struct bt_hci_rp_read_buffer_size *)cmd_response_data;
+ *acl_max_len = response->acl_max_len;
+ *sco_max_len = response->sco_max_len;
+ *acl_max_num = response->acl_max_num;
+ *sco_max_num = response->sco_max_num;
+ }
+
+ return result;
+}
+
+hci_result_t hci_le_set_random_address(uint8_t addr[6]) {
+ return send_command(BT_HCI_OP_LE_SET_RANDOM_ADDRESS, 6, addr);
+}
+
+hci_result_t hci_le_set_advertising_parameters(uint16_t min_interval, uint16_t max_interval, uint8_t type, uint8_t own_addr_type, bt_addr_le_t *direct_addr, uint8_t channel_map, uint8_t filter_policy) {
+ struct bt_hci_cp_le_set_adv_param params = {
+ .min_interval = min_interval,
+ .max_interval = max_interval,
+ .type = type,
+ .own_addr_type = own_addr_type,
+ // .direct_addr set below.
+ .channel_map = channel_map,
+ .filter_policy = filter_policy,
+ };
+ params.direct_addr.type = direct_addr->type;
+ memcpy(params.direct_addr.a.val, direct_addr->a.val, sizeof(params.direct_addr.a.val));
+
+ return send_command(BT_HCI_OP_LE_SET_ADV_PARAM, sizeof(params), &params);
+}
+
+hci_result_t hci_le_set_extended_advertising_parameters(uint8_t handle, uint16_t props, uint32_t prim_min_interval, uint32_t prim_max_interval, uint8_t prim_channel_map, uint8_t own_addr_type, bt_addr_le_t *peer_addr, uint8_t filter_policy, int8_t tx_power, uint8_t prim_adv_phy, uint8_t sec_adv_max_skip, uint8_t sec_adv_phy, uint8_t sid, uint8_t scan_req_notify_enable) {
+ struct bt_hci_cp_le_set_ext_adv_param params = {
+ .handle = handle,
+ .props = props,
+ // .prim_min_interval and .prim_max_interval set below
+ .prim_channel_map = prim_channel_map,
+ .own_addr_type = own_addr_type,
+ // .peer_addr set below.
+ .tx_power = tx_power,
+ .sec_adv_max_skip = sec_adv_max_skip,
+ .sec_adv_phy = sec_adv_phy,
+ .sid = sid,
+ .scan_req_notify_enable = scan_req_notify_enable,
+ };
+ // Assumes little-endian.
+ memcpy(params.prim_min_interval, (void *)&prim_min_interval,
+ sizeof_field(struct bt_hci_cp_le_set_ext_adv_param, prim_min_interval));
+ memcpy(params.prim_max_interval, (void *)&prim_max_interval,
+ sizeof_field(struct bt_hci_cp_le_set_ext_adv_param, prim_max_interval));
+ memcpy(params.peer_addr.a.val, peer_addr->a.val, sizeof_field(bt_addr_le_t, a.val));
+ return send_command(BT_HCI_OP_LE_SET_EXT_ADV_PARAM, sizeof(params), &params);
+}
+
+hci_result_t hci_le_read_maximum_advertising_data_length(uint16_t *max_adv_data_len) {
+ int result = send_command(BT_HCI_OP_LE_READ_MAX_ADV_DATA_LEN, 0, NULL);
+ if (result == HCI_OK) {
+ struct bt_hci_rp_le_read_max_adv_data_len *response =
+ (struct bt_hci_rp_le_read_max_adv_data_len *)cmd_response_data;
+ *max_adv_data_len = response->max_adv_data_len;
+ }
+
+ return result;
+}
+
+hci_result_t hci_le_read_local_supported_features(uint8_t features[8]) {
+ int result = send_command(BT_HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL);
+ if (result == HCI_OK) {
+ struct bt_hci_rp_le_read_local_features *response =
+ (struct bt_hci_rp_le_read_local_features *)cmd_response_data;
+ memcpy(features, response->features,
+ sizeof_field(struct bt_hci_rp_le_read_local_features, features));
+ }
+
+ return result;
+}
+
+hci_result_t hci_le_set_advertising_data(uint8_t len, uint8_t data[]) {
+ struct bt_hci_cp_le_set_adv_data params = {
+ // Zero out unused data bytes.
+ .data = { 0 },
+ };
+
+ params.len = len;
+ memcpy(params.data, data, len);
+
+ // All data bytes are sent even if some are unused.
+ return send_command(BT_HCI_OP_LE_SET_ADV_DATA, sizeof(params), &params);
+}
+
+hci_result_t hci_le_set_extended_advertising_data(uint8_t handle, uint8_t op, uint8_t frag_pref, uint8_t len, uint8_t data[]) {
+ const uint8_t max_len = sizeof_field(struct bt_hci_cp_le_set_ext_adv_data, data);
+ uint8_t valid_len = MIN(len, max_len);
+ struct bt_hci_cp_le_set_ext_adv_data params = {
+ .handle = handle,
+ .op = op,
+ .frag_pref = frag_pref,
+ .len = valid_len,
+ };
+ memcpy(params.data, data, valid_len);
+ return send_command(BT_HCI_OP_LE_SET_EXT_ADV_DATA, sizeof(params) - (max_len - valid_len), &params);
+}
+
+
+hci_result_t hci_le_set_scan_response_data(uint8_t len, uint8_t data[]) {
+ struct bt_hci_cp_le_set_scan_rsp_data params = {
+ // Zero out unused data bytes.
+ .data = { 0 },
+ };
+ params.len = len;
+ memcpy(params.data, data, len);
+
+ // All data bytes are sent even if some are unused.
+ return send_command(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(params), &params);
+}
+
+hci_result_t hci_le_set_advertising_enable(uint8_t enable) {
+ return send_command(BT_HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
+hci_result_t hci_le_set_extended_advertising_enable(uint8_t enable, uint8_t set_num, uint8_t handle[], uint16_t duration[], uint8_t max_ext_adv_evts[]) {
+ uint8_t params[sizeof(struct bt_hci_cp_le_set_ext_adv_enable) +
+ set_num * (sizeof(struct bt_hci_ext_adv_set))];
+ struct bt_hci_cp_le_set_ext_adv_enable *params_p = (struct bt_hci_cp_le_set_ext_adv_enable *)&params;
+ params_p->enable = enable;
+ params_p->set_num = set_num;
+ for (size_t i = 0; i < set_num; i++) {
+ params_p->s[i].handle = handle[i];
+ params_p->s[i].duration = duration[i];
+ params_p->s[i].max_ext_adv_evts = max_ext_adv_evts[i];
+ }
+
+ return send_command(BT_HCI_OP_LE_SET_EXT_ADV_ENABLE, sizeof(params), &params);
+}
+
+hci_result_t hci_le_set_scan_parameters(uint8_t scan_type, uint16_t interval, uint16_t window, uint8_t addr_type, uint8_t filter_policy) {
+ struct bt_hci_cp_le_set_scan_param params = {
+ .scan_type = scan_type,
+ .interval = interval,
+ .window = window,
+ .addr_type = addr_type,
+ .filter_policy = filter_policy,
+ };
+
+ return send_command(BT_HCI_OP_LE_SET_SCAN_PARAM, sizeof(params), &params);
+}
+
+hci_result_t hci_le_set_scan_enable(uint8_t enable, uint8_t filter_dup) {
+ struct bt_hci_cp_le_set_scan_enable params = {
+ .enable = enable,
+ .filter_dup = filter_dup,
+ };
+
+ return send_command(BT_HCI_OP_LE_SET_SCAN_ENABLE, sizeof(params), &params);
+}
+
+hci_result_t hci_le_create_conn(uint16_t scan_interval, uint16_t scan_window, uint8_t filter_policy, bt_addr_le_t *peer_addr, uint8_t own_addr_type, uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency, uint16_t supervision_timeout, uint16_t min_ce_len, uint16_t max_ce_len) {
+ struct bt_hci_cp_le_create_conn params = {
+ .scan_interval = scan_interval,
+ .scan_window = scan_window,
+ .filter_policy = filter_policy,
+ // .peer_addr is set below
+ .own_addr_type = own_addr_type,
+ .conn_interval_min = conn_interval_min,
+ .conn_interval_max = conn_interval_max,
+ .conn_latency = conn_latency,
+ .supervision_timeout = supervision_timeout,
+ .min_ce_len = min_ce_len,
+ .max_ce_len = max_ce_len,
+ };
+ params.peer_addr.type = peer_addr->type;
+ memcpy(params.peer_addr.a.val, peer_addr->a.val, sizeof(params.peer_addr.a.val));
+
+ return send_command(BT_HCI_OP_LE_CREATE_CONN, sizeof(params), &params);
+}
+
+hci_result_t hci_le_cancel_conn(void) {
+ return send_command(BT_HCI_OP_CONNECT_CANCEL, 0, NULL);
+}
+
+hci_result_t hci_le_conn_update(uint16_t handle, uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency, uint16_t supervision_timeout) {
+ struct hci_cp_le_conn_update params = {
+ .handle = handle,
+ .conn_interval_min = conn_interval_min,
+ .conn_interval_max = conn_interval_max,
+ .conn_latency = conn_latency,
+ .supervision_timeout = supervision_timeout,
+ .min_ce_len = 4,
+ .max_ce_len = 6,
+ };
+
+ return send_command(BT_HCI_OP_LE_CONN_UPDATE, sizeof(params), &params);
+}
+
+hci_result_t hci_disconnect(uint16_t handle) {
+ struct bt_hci_cp_disconnect params = {
+ .handle = handle,
+ .reason = BT_HCI_ERR_REMOTE_USER_TERM_CONN,
+ };
+
+ return send_command(BT_HCI_OP_DISCONNECT, sizeof(params), &params);
+}
+
+void hci_check_error(hci_result_t result) {
+ switch (result) {
+ case HCI_OK:
+ return;
+
+ case HCI_RESPONSE_TIMEOUT:
+ mp_raise_bleio_BluetoothError(translate("Timeout waiting for HCI response"));
+ return;
+
+ case HCI_WRITE_TIMEOUT:
+ mp_raise_bleio_BluetoothError(translate("Timeout waiting to write HCI request"));
+ return;
+
+ case HCI_READ_ERROR:
+ mp_raise_bleio_BluetoothError(translate("Error reading from HCI adapter"));
+ return;
+
+ case HCI_WRITE_ERROR:
+ mp_raise_bleio_BluetoothError(translate("Error writing to HCI adapter"));
+ return;
+
+ case HCI_PACKET_SIZE_ERROR:
+ mp_raise_RuntimeError(translate("HCI packet size mismatch"));
+ return;
+
+ case HCI_ATT_ERROR:
+ mp_raise_RuntimeError(translate("Error in ATT protocol code"));
+ return;
+
+ default:
+ // Should be an HCI status error, > 0.
+ if (result > 0) {
+ mp_raise_bleio_BluetoothError(translate("HCI status error: %02x"), result);
+ } else {
+ mp_raise_bleio_BluetoothError(translate("Unknown hci_result_t: %d"), result);
+ }
+ return;
+ }
+}
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci.h b/circuitpython/devices/ble_hci/common-hal/_bleio/hci.h
new file mode 100644
index 0000000..c9fd239
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci.h
@@ -0,0 +1,81 @@
+/*
+ This file is part of the ArduinoBLE library.
+ Copyright (c) 2018 Arduino SA. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_HCI_H
+#define MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_HCI_H
+
+#include <stdint.h>
+
+#include "common-hal/_bleio/hci_include/hci.h"
+#include "common-hal/_bleio/hci_include/hci_err.h"
+
+// Incomplete forward declaration to get around mutually-dependent include files.
+typedef struct _bleio_adapter_obj_t bleio_adapter_obj_t;
+
+// An hci_result_t is one of the HCI_x values below,
+// or it is > 0 and is an HCI command status value (see hci_include/hci_err.h)
+typedef int hci_result_t;
+#define HCI_OK (0)
+#define HCI_RESPONSE_TIMEOUT (-1)
+#define HCI_WRITE_TIMEOUT (-2)
+#define HCI_READ_ERROR (-3)
+#define HCI_WRITE_ERROR (-4)
+#define HCI_ATT_ERROR (-5)
+#define HCI_PACKET_SIZE_ERROR (-6)
+
+extern void bleio_hci_reset(void);
+
+void hci_check_error(hci_result_t result);
+
+hci_result_t hci_disconnect(uint16_t handle);
+
+hci_result_t hci_le_cancel_conn(void);
+hci_result_t hci_le_conn_update(uint16_t handle, uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency, uint16_t supervision_timeout);
+hci_result_t hci_le_create_conn(uint16_t scan_interval, uint16_t scan_window, uint8_t filter_policy, bt_addr_le_t *peer_addr, uint8_t own_addr_type, uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency, uint16_t supervision_timeout, uint16_t min_ce_len, uint16_t max_ce_len);
+
+hci_result_t hci_le_read_buffer_size(uint16_t *le_max_len, uint8_t *le_max_num);
+hci_result_t hci_le_read_maximum_advertising_data_length(uint16_t *max_adv_data_len);
+hci_result_t hci_le_read_local_supported_features(uint8_t features[8]);
+
+hci_result_t hci_le_set_advertising_data(uint8_t length, uint8_t data[]);
+hci_result_t hci_le_set_advertising_enable(uint8_t enable);
+hci_result_t hci_le_set_advertising_parameters(uint16_t min_interval, uint16_t max_interval, uint8_t type, uint8_t own_addr_type, bt_addr_le_t *direct_addr, uint8_t channel_map, uint8_t filter_policy);
+
+hci_result_t hci_le_set_extended_advertising_data(uint8_t handle, uint8_t op, uint8_t frag_pref, uint8_t len, uint8_t data[]);
+hci_result_t hci_le_set_extended_advertising_enable(uint8_t enable, uint8_t set_num, uint8_t handle[], uint16_t duration[], uint8_t max_ext_adv_evts[]);
+hci_result_t hci_le_set_extended_advertising_parameters(uint8_t handle, uint16_t props, uint32_t prim_min_interval, uint32_t prim_max_interval, uint8_t prim_channel_map, uint8_t own_addr_type, bt_addr_le_t *peer_addr, uint8_t filter_policy, int8_t tx_power, uint8_t prim_adv_phy, uint8_t sec_adv_max_skip, uint8_t sec_adv_phy, uint8_t sid, uint8_t scan_req_notify_enable);
+
+hci_result_t hci_le_set_random_address(uint8_t addr[6]);
+hci_result_t hci_le_set_scan_enable(uint8_t enable, uint8_t filter_dup);
+hci_result_t hci_le_set_scan_parameters(uint8_t scan_type, uint16_t interval, uint16_t window, uint8_t addr_type, uint8_t filter_policy);
+hci_result_t hci_le_set_scan_response_data(uint8_t length, uint8_t data[]);
+
+hci_result_t hci_poll_for_incoming_pkt(void);
+
+hci_result_t hci_read_bd_addr(bt_addr_t *addr);
+hci_result_t hci_read_buffer_size(uint16_t *acl_max_len, uint8_t *sco_max_len, uint16_t *acl_max_num, uint16_t *sco_max_num);
+hci_result_t hci_read_local_version(uint8_t *hci_version, uint16_t *hci_revision, uint8_t *lmp_version, uint16_t *manufacturer, uint16_t *lmp_subversion);
+hci_result_t hci_read_rssi(uint16_t handle, int *rssi);
+
+hci_result_t hci_reset(void);
+
+hci_result_t hci_send_acl_pkt(uint16_t handle, uint8_t cid, uint16_t data_len, uint8_t *data);
+hci_result_t hci_set_event_mask(uint64_t event_mask);
+
+#endif // MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_HCI_H
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci_debug.c b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_debug.c
new file mode 100644
index 0000000..5e57142
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_debug.c
@@ -0,0 +1,549 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 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.
+ */
+
+// This file is #include'd in hci.c when HCI_DEBUG is non-zero.
+
+STATIC const char *att_opcode_name(uint16_t opcode) {
+ switch (opcode) {
+ case BT_ATT_OP_ERROR_RSP:
+ return "ERROR_RSP";
+ case BT_ATT_OP_MTU_REQ:
+ return "MTU_REQ";
+ case BT_ATT_OP_MTU_RSP:
+ return "MTU_RSP";
+ case BT_ATT_OP_FIND_INFO_REQ:
+ return "FIND_INFO_REQ";
+ case BT_ATT_OP_FIND_INFO_RSP:
+ return "FIND_INFO_RSP";
+ case BT_ATT_OP_FIND_TYPE_REQ:
+ return "FIND_TYPE_REQ";
+ case BT_ATT_OP_FIND_TYPE_RSP:
+ return "FIND_TYPE_RSP";
+ case BT_ATT_OP_READ_TYPE_REQ:
+ return "READ_TYPE_REQ";
+ case BT_ATT_OP_READ_TYPE_RSP:
+ return "READ_TYPE_RSP";
+ case BT_ATT_OP_READ_REQ:
+ return "READ_REQ";
+ case BT_ATT_OP_READ_RSP:
+ return "READ_RSP";
+ case BT_ATT_OP_READ_BLOB_REQ:
+ return "READ_BLOB_REQ";
+ case BT_ATT_OP_READ_BLOB_RSP:
+ return "READ_BLOB_RSP";
+ case BT_ATT_OP_READ_MULT_REQ:
+ return "READ_MULT_REQ";
+ case BT_ATT_OP_READ_MULT_RSP:
+ return "READ_MULT_RSP";
+ case BT_ATT_OP_READ_GROUP_REQ:
+ return "READ_GROUP_REQ";
+ case BT_ATT_OP_READ_GROUP_RSP:
+ return "READ_GROUP_RSP";
+ case BT_ATT_OP_WRITE_REQ:
+ return "WRITE_REQ";
+ case BT_ATT_OP_WRITE_RSP:
+ return "WRITE_RSP";
+ case BT_ATT_OP_PREPARE_WRITE_REQ:
+ return "PREPARE_WRITE_REQ";
+ case BT_ATT_OP_PREPARE_WRITE_RSP:
+ return "PREPARE_WRITE_RSP";
+ case BT_ATT_OP_EXEC_WRITE_REQ:
+ return "EXEC_WRITE_REQ";
+ case BT_ATT_OP_EXEC_WRITE_RSP:
+ return "EXEC_WRITE_RSP";
+ case BT_ATT_OP_NOTIFY:
+ return "NOTIFY";
+ case BT_ATT_OP_INDICATE:
+ return "INDICATE";
+ case BT_ATT_OP_CONFIRM:
+ return "CONFIRM";
+ case BT_ATT_OP_READ_MULT_VL_REQ:
+ return "READ_MULT_VL_REQ";
+ case BT_ATT_OP_READ_MULT_VL_RSP:
+ return "READ_MULT_VL_RSP";
+ case BT_ATT_OP_NOTIFY_MULT:
+ return "NOTIFY_MULT";
+ case BT_ATT_OP_WRITE_CMD:
+ return "WRITE_CMD";
+ case BT_ATT_OP_SIGNED_WRITE_CMD:
+ return "SIGNED_WRITE_CMD";
+ default:
+ return "";
+ }
+}
+
+STATIC const char *hci_evt_name(uint8_t evt) {
+ switch (evt) {
+ case BT_HCI_EVT_UNKNOWN:
+ return "UNKNOWN";
+ case BT_HCI_EVT_VENDOR:
+ return "VENDOR";
+ case BT_HCI_EVT_INQUIRY_COMPLETE:
+ return "INQUIRY_COMPLETE";
+ case BT_HCI_EVT_CONN_COMPLETE:
+ return "CONN_COMPLETE";
+ case BT_HCI_EVT_CONN_REQUEST:
+ return "CONN_REQUEST";
+ case BT_HCI_EVT_DISCONN_COMPLETE:
+ return "DISCONN_COMPLETE";
+ case BT_HCI_EVT_AUTH_COMPLETE:
+ return "AUTH_COMPLETE";
+ case BT_HCI_EVT_REMOTE_NAME_REQ_COMPLETE:
+ return "REMOTE_NAME_REQ_COMPLETE";
+ case BT_HCI_EVT_ENCRYPT_CHANGE:
+ return "ENCRYPT_CHANGE";
+ case BT_HCI_EVT_REMOTE_FEATURES:
+ return "REMOTE_FEATURES";
+ case BT_HCI_EVT_REMOTE_VERSION_INFO:
+ return "REMOTE_VERSION_INFO";
+ case BT_HCI_EVT_CMD_COMPLETE:
+ return "CMD_COMPLETE";
+ case BT_HCI_EVT_CMD_STATUS:
+ return "CMD_STATUS";
+ case BT_HCI_EVT_ROLE_CHANGE:
+ return "ROLE_CHANGE";
+ case BT_HCI_EVT_NUM_COMPLETED_PACKETS:
+ return "NUM_COMPLETED_PACKETS";
+ case BT_HCI_EVT_PIN_CODE_REQ:
+ return "PIN_CODE_REQ";
+ case BT_HCI_EVT_LINK_KEY_REQ:
+ return "LINK_KEY_REQ";
+ case BT_HCI_EVT_LINK_KEY_NOTIFY:
+ return "LINK_KEY_NOTIFY";
+ case BT_HCI_EVT_DATA_BUF_OVERFLOW:
+ return "DATA_BUF_OVERFLOW";
+ case BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI:
+ return "INQUIRY_RESULT_WITH_RSSI";
+ case BT_HCI_EVT_REMOTE_EXT_FEATURES:
+ return "REMOTE_EXT_FEATURES";
+ case BT_HCI_EVT_SYNC_CONN_COMPLETE:
+ return "SYNC_CONN_COMPLETE";
+ case BT_HCI_EVT_EXTENDED_INQUIRY_RESULT:
+ return "EXTENDED_INQUIRY_RESULT";
+ case BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE:
+ return "ENCRYPT_KEY_REFRESH_COMPLETE";
+ case BT_HCI_EVT_IO_CAPA_REQ:
+ return "IO_CAPA_REQ";
+ case BT_HCI_EVT_IO_CAPA_RESP:
+ return "IO_CAPA_RESP";
+ case BT_HCI_EVT_USER_CONFIRM_REQ:
+ return "USER_CONFIRM_REQ";
+ case BT_HCI_EVT_USER_PASSKEY_REQ:
+ return "USER_PASSKEY_REQ";
+ case BT_HCI_EVT_SSP_COMPLETE:
+ return "SSP_COMPLETE";
+ case BT_HCI_EVT_USER_PASSKEY_NOTIFY:
+ return "USER_PASSKEY_NOTIFY";
+ case BT_HCI_EVT_LE_META_EVENT:
+ return "LE_META_EVENT";
+ case BT_HCI_EVT_AUTH_PAYLOAD_TIMEOUT_EXP:
+ return "AUTH_PAYLOAD_TIMEOUT_EXP";
+ default:
+ return "";
+ }
+}
+
+STATIC const char *hci_evt_le_name(uint8_t evt_le) {
+ switch (evt_le) {
+ case BT_HCI_EVT_LE_CONN_COMPLETE:
+ return "LE_CONN_COMPLETE";
+ case BT_HCI_EVT_LE_ADVERTISING_REPORT:
+ return "LE_ADVERTISING_REPORT";
+ case BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE:
+ return "LE_CONN_UPDATE_COMPLETE";
+ case BT_HCI_EVT_LE_LTK_REQUEST:
+ return "LE_LTK_REQUEST";
+ case BT_HCI_EVT_LE_CONN_PARAM_REQ:
+ return "LE_CONN_PARAM_REQ";
+ case BT_HCI_EVT_LE_DATA_LEN_CHANGE:
+ return "LE_DATA_LEN_CHANGE";
+ case BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE:
+ return "LE_P256_PUBLIC_KEY_COMPLETE";
+ case BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE:
+ return "LE_GENERATE_DHKEY_COMPLETE";
+ case BT_HCI_EVT_LE_ENH_CONN_COMPLETE:
+ return "LE_ENH_CONN_COMPLETE";
+ case BT_HCI_EVT_LE_DIRECT_ADV_REPORT:
+ return "LE_DIRECT_ADV_REPORT";
+ case BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE:
+ return "LE_PHY_UPDATE_COMPLETE";
+ case BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT:
+ return "LE_EXT_ADVERTISING_REPORT";
+ case BT_HCI_EVT_LE_PER_ADV_SYNC_ESTABLISHED:
+ return "LE_PER_ADV_SYNC_ESTABLISHED";
+ case BT_HCI_EVT_LE_PER_ADVERTISING_REPORT:
+ return "LE_PER_ADVERTISING_REPORT";
+ case BT_HCI_EVT_LE_PER_ADV_SYNC_LOST:
+ return "LE_PER_ADV_SYNC_LOST";
+ case BT_HCI_EVT_LE_SCAN_TIMEOUT:
+ return "LE_SCAN_TIMEOUT";
+ case BT_HCI_EVT_LE_ADV_SET_TERMINATED:
+ return "LE_ADV_SET_TERMINATED";
+ case BT_HCI_EVT_LE_SCAN_REQ_RECEIVED:
+ return "LE_SCAN_REQ_RECEIVED";
+ case BT_HCI_EVT_LE_CHAN_SEL_ALGO:
+ return "LE_CHAN_SEL_ALGO";
+ default:
+ return "";
+ }
+}
+
+STATIC const char *hci_opcode_name(uint16_t opcode) {
+ switch (opcode) {
+ case BT_OP_NOP:
+ return "NOP";
+ case BT_HCI_OP_INQUIRY:
+ return "INQUIRY";
+ case BT_HCI_OP_INQUIRY_CANCEL:
+ return "INQUIRY_CANCEL";
+ case BT_HCI_OP_CONNECT:
+ return "CONNECT";
+ case BT_HCI_OP_DISCONNECT:
+ return "DISCONNECT";
+ case BT_HCI_OP_CONNECT_CANCEL:
+ return "CONNECT_CANCEL";
+ case BT_HCI_OP_ACCEPT_CONN_REQ:
+ return "ACCEPT_CONN_REQ";
+ case BT_HCI_OP_SETUP_SYNC_CONN:
+ return "SETUP_SYNC_CONN";
+ case BT_HCI_OP_ACCEPT_SYNC_CONN_REQ:
+ return "ACCEPT_SYNC_CONN_REQ";
+ case BT_HCI_OP_REJECT_CONN_REQ:
+ return "REJECT_CONN_REQ";
+ case BT_HCI_OP_LINK_KEY_REPLY:
+ return "LINK_KEY_REPLY";
+ case BT_HCI_OP_LINK_KEY_NEG_REPLY:
+ return "LINK_KEY_NEG_REPLY";
+ case BT_HCI_OP_PIN_CODE_REPLY:
+ return "PIN_CODE_REPLY";
+ case BT_HCI_OP_PIN_CODE_NEG_REPLY:
+ return "PIN_CODE_NEG_REPLY";
+ case BT_HCI_OP_AUTH_REQUESTED:
+ return "AUTH_REQUESTED";
+ case BT_HCI_OP_SET_CONN_ENCRYPT:
+ return "SET_CONN_ENCRYPT";
+ case BT_HCI_OP_REMOTE_NAME_REQUEST:
+ return "REMOTE_NAME_REQUEST";
+ case BT_HCI_OP_REMOTE_NAME_CANCEL:
+ return "REMOTE_NAME_CANCEL";
+ case BT_HCI_OP_READ_REMOTE_FEATURES:
+ return "READ_REMOTE_FEATURES";
+ case BT_HCI_OP_READ_REMOTE_EXT_FEATURES:
+ return "READ_REMOTE_EXT_FEATURES";
+ case BT_HCI_OP_READ_REMOTE_VERSION_INFO:
+ return "READ_REMOTE_VERSION_INFO";
+ case BT_HCI_OP_IO_CAPABILITY_REPLY:
+ return "IO_CAPABILITY_REPLY";
+ case BT_HCI_OP_USER_CONFIRM_REPLY:
+ return "USER_CONFIRM_REPLY";
+ case BT_HCI_OP_USER_CONFIRM_NEG_REPLY:
+ return "USER_CONFIRM_NEG_REPLY";
+ case BT_HCI_OP_USER_PASSKEY_REPLY:
+ return "USER_PASSKEY_REPLY";
+ case BT_HCI_OP_USER_PASSKEY_NEG_REPLY:
+ return "USER_PASSKEY_NEG_REPLY";
+ case BT_HCI_OP_IO_CAPABILITY_NEG_REPLY:
+ return "IO_CAPABILITY_NEG_REPLY";
+ case BT_HCI_OP_SET_EVENT_MASK:
+ return "SET_EVENT_MASK";
+ case BT_HCI_OP_RESET:
+ return "RESET";
+ case BT_HCI_OP_WRITE_LOCAL_NAME:
+ return "WRITE_LOCAL_NAME";
+ case BT_HCI_OP_WRITE_PAGE_TIMEOUT:
+ return "WRITE_PAGE_TIMEOUT";
+ case BT_HCI_OP_WRITE_SCAN_ENABLE:
+ return "WRITE_SCAN_ENABLE";
+ case BT_HCI_OP_READ_TX_POWER_LEVEL:
+ return "READ_TX_POWER_LEVEL";
+ case BT_HCI_OP_SET_CTL_TO_HOST_FLOW:
+ return "SET_CTL_TO_HOST_FLOW";
+ case BT_HCI_OP_HOST_BUFFER_SIZE:
+ return "HOST_BUFFER_SIZE";
+ case BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS:
+ return "HOST_NUM_COMPLETED_PACKETS";
+ case BT_HCI_OP_WRITE_INQUIRY_MODE:
+ return "WRITE_INQUIRY_MODE";
+ case BT_HCI_OP_WRITE_SSP_MODE:
+ return "WRITE_SSP_MODE";
+ case BT_HCI_OP_SET_EVENT_MASK_PAGE_2:
+ return "SET_EVENT_MASK_PAGE_2";
+ case BT_HCI_OP_LE_WRITE_LE_HOST_SUPP:
+ return "LE_WRITE_LE_HOST_SUPP";
+ case BT_HCI_OP_WRITE_SC_HOST_SUPP:
+ return "WRITE_SC_HOST_SUPP";
+ case BT_HCI_OP_READ_AUTH_PAYLOAD_TIMEOUT:
+ return "READ_AUTH_PAYLOAD_TIMEOUT";
+ case BT_HCI_OP_WRITE_AUTH_PAYLOAD_TIMEOUT:
+ return "WRITE_AUTH_PAYLOAD_TIMEOUT";
+ case BT_HCI_OP_READ_LOCAL_VERSION_INFO:
+ return "READ_LOCAL_VERSION_INFO";
+ case BT_HCI_OP_READ_SUPPORTED_COMMANDS:
+ return "READ_SUPPORTED_COMMANDS";
+ case BT_HCI_OP_READ_LOCAL_EXT_FEATURES:
+ return "READ_LOCAL_EXT_FEATURES";
+ case BT_HCI_OP_READ_LOCAL_FEATURES:
+ return "READ_LOCAL_FEATURES";
+ case BT_HCI_OP_READ_BUFFER_SIZE:
+ return "READ_BUFFER_SIZE";
+ case BT_HCI_OP_READ_BD_ADDR:
+ return "READ_BD_ADDR";
+ case BT_HCI_OP_READ_RSSI:
+ return "READ_RSSI";
+ case BT_HCI_OP_READ_ENCRYPTION_KEY_SIZE:
+ return "READ_ENCRYPTION_KEY_SIZE";
+ case BT_HCI_OP_LE_SET_EVENT_MASK:
+ return "LE_SET_EVENT_MASK";
+ case BT_HCI_OP_LE_READ_BUFFER_SIZE:
+ return "LE_READ_BUFFER_SIZE";
+ case BT_HCI_OP_LE_READ_LOCAL_FEATURES:
+ return "LE_READ_LOCAL_FEATURES";
+ case BT_HCI_OP_LE_SET_RANDOM_ADDRESS:
+ return "LE_SET_RANDOM_ADDRESS";
+ case BT_HCI_OP_LE_SET_ADV_PARAM:
+ return "LE_SET_ADV_PARAM";
+ case BT_HCI_OP_LE_READ_ADV_CHAN_TX_POWER:
+ return "LE_READ_ADV_CHAN_TX_POWER";
+ case BT_HCI_OP_LE_SET_ADV_DATA:
+ return "LE_SET_ADV_DATA";
+ case BT_HCI_OP_LE_SET_SCAN_RSP_DATA:
+ return "LE_SET_SCAN_RSP_DATA";
+ case BT_HCI_OP_LE_SET_ADV_ENABLE:
+ return "LE_SET_ADV_ENABLE";
+ case BT_HCI_OP_LE_SET_SCAN_PARAM:
+ return "LE_SET_SCAN_PARAM";
+ case BT_HCI_OP_LE_SET_SCAN_ENABLE:
+ return "LE_SET_SCAN_ENABLE";
+ case BT_HCI_OP_LE_CREATE_CONN:
+ return "LE_CREATE_CONN";
+ case BT_HCI_OP_LE_CREATE_CONN_CANCEL:
+ return "LE_CREATE_CONN_CANCEL";
+ case BT_HCI_OP_LE_READ_WL_SIZE:
+ return "LE_READ_WL_SIZE";
+ case BT_HCI_OP_LE_CLEAR_WL:
+ return "LE_CLEAR_WL";
+ case BT_HCI_OP_LE_ADD_DEV_TO_WL:
+ return "LE_ADD_DEV_TO_WL";
+ case BT_HCI_OP_LE_REM_DEV_FROM_WL:
+ return "LE_REM_DEV_FROM_WL";
+ case BT_HCI_OP_LE_CONN_UPDATE:
+ return "LE_CONN_UPDATE";
+ case BT_HCI_OP_LE_SET_HOST_CHAN_CLASSIF:
+ return "LE_SET_HOST_CHAN_CLASSIF";
+ case BT_HCI_OP_LE_READ_CHAN_MAP:
+ return "LE_READ_CHAN_MAP";
+ case BT_HCI_OP_LE_READ_REMOTE_FEATURES:
+ return "LE_READ_REMOTE_FEATURES";
+ case BT_HCI_OP_LE_ENCRYPT:
+ return "LE_ENCRYPT";
+ case BT_HCI_OP_LE_RAND:
+ return "LE_RAND";
+ case BT_HCI_OP_LE_START_ENCRYPTION:
+ return "LE_START_ENCRYPTION";
+ case BT_HCI_OP_LE_LTK_REQ_REPLY:
+ return "LE_LTK_REQ_REPLY";
+ case BT_HCI_OP_LE_LTK_REQ_NEG_REPLY:
+ return "LE_LTK_REQ_NEG_REPLY";
+ case BT_HCI_OP_LE_READ_SUPP_STATES:
+ return "LE_READ_SUPP_STATES";
+ case BT_HCI_OP_LE_RX_TEST:
+ return "LE_RX_TEST";
+ case BT_HCI_OP_LE_TX_TEST:
+ return "LE_TX_TEST";
+ case BT_HCI_OP_LE_TEST_END:
+ return "LE_TEST_END";
+ case BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY:
+ return "LE_CONN_PARAM_REQ_REPLY";
+ case BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY:
+ return "LE_CONN_PARAM_REQ_NEG_REPLY";
+ case BT_HCI_OP_LE_SET_DATA_LEN:
+ return "LE_SET_DATA_LEN";
+ case BT_HCI_OP_LE_READ_DEFAULT_DATA_LEN:
+ return "LE_READ_DEFAULT_DATA_LEN";
+ case BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN:
+ return "LE_WRITE_DEFAULT_DATA_LEN";
+ case BT_HCI_OP_LE_P256_PUBLIC_KEY:
+ return "LE_P256_PUBLIC_KEY";
+ case BT_HCI_OP_LE_GENERATE_DHKEY:
+ return "LE_GENERATE_DHKEY";
+ case BT_HCI_OP_LE_ADD_DEV_TO_RL:
+ return "LE_ADD_DEV_TO_RL";
+ case BT_HCI_OP_LE_REM_DEV_FROM_RL:
+ return "LE_REM_DEV_FROM_RL";
+ case BT_HCI_OP_LE_CLEAR_RL:
+ return "LE_CLEAR_RL";
+ case BT_HCI_OP_LE_READ_RL_SIZE:
+ return "LE_READ_RL_SIZE";
+ case BT_HCI_OP_LE_READ_PEER_RPA:
+ return "LE_READ_PEER_RPA";
+ case BT_HCI_OP_LE_READ_LOCAL_RPA:
+ return "LE_READ_LOCAL_RPA";
+ case BT_HCI_OP_LE_SET_ADDR_RES_ENABLE:
+ return "LE_SET_ADDR_RES_ENABLE";
+ case BT_HCI_OP_LE_SET_RPA_TIMEOUT:
+ return "LE_SET_RPA_TIMEOUT";
+ case BT_HCI_OP_LE_READ_MAX_DATA_LEN:
+ return "LE_READ_MAX_DATA_LEN";
+ case BT_HCI_OP_LE_READ_PHY:
+ return "LE_READ_PHY";
+ case BT_HCI_OP_LE_SET_DEFAULT_PHY:
+ return "LE_SET_DEFAULT_PHY";
+ case BT_HCI_OP_LE_SET_PHY:
+ return "LE_SET_PHY";
+ case BT_HCI_OP_LE_ENH_RX_TEST:
+ return "LE_ENH_RX_TEST";
+ case BT_HCI_OP_LE_ENH_TX_TEST:
+ return "LE_ENH_TX_TEST";
+ case BT_HCI_OP_LE_SET_ADV_SET_RANDOM_ADDR:
+ return "LE_SET_ADV_SET_RANDOM_ADDR";
+ case BT_HCI_OP_LE_SET_EXT_ADV_PARAM:
+ return "LE_SET_EXT_ADV_PARAM";
+ case BT_HCI_OP_LE_SET_EXT_ADV_DATA:
+ return "LE_SET_EXT_ADV_DATA";
+ case BT_HCI_OP_LE_SET_EXT_SCAN_RSP_DATA:
+ return "LE_SET_EXT_SCAN_RSP_DATA";
+ case BT_HCI_OP_LE_SET_EXT_ADV_ENABLE:
+ return "LE_SET_EXT_ADV_ENABLE";
+ case BT_HCI_OP_LE_READ_MAX_ADV_DATA_LEN:
+ return "LE_READ_MAX_ADV_DATA_LEN";
+ case BT_HCI_OP_LE_READ_NUM_ADV_SETS:
+ return "LE_READ_NUM_ADV_SETS";
+ case BT_HCI_OP_LE_REMOVE_ADV_SET:
+ return "LE_REMOVE_ADV_SET";
+ case BT_HCI_OP_CLEAR_ADV_SETS:
+ return "CLEAR_ADV_SETS";
+ case BT_HCI_OP_LE_SET_PER_ADV_PARAM:
+ return "LE_SET_PER_ADV_PARAM";
+ case BT_HCI_OP_LE_SET_PER_ADV_DATA:
+ return "LE_SET_PER_ADV_DATA";
+ case BT_HCI_OP_LE_SET_PER_ADV_ENABLE:
+ return "LE_SET_PER_ADV_ENABLE";
+ case BT_HCI_OP_LE_SET_EXT_SCAN_PARAM:
+ return "LE_SET_EXT_SCAN_PARAM";
+ case BT_HCI_OP_LE_SET_EXT_SCAN_ENABLE:
+ return "LE_SET_EXT_SCAN_ENABLE";
+ case BT_HCI_OP_LE_EXT_CREATE_CONN:
+ return "LE_EXT_CREATE_CONN";
+ case BT_HCI_OP_LE_PER_ADV_CREATE_SYNC:
+ return "LE_PER_ADV_CREATE_SYNC";
+ case BT_HCI_OP_LE_PER_ADV_CREATE_SYNC_CANCEL:
+ return "LE_PER_ADV_CREATE_SYNC_CANCEL";
+ case BT_HCI_OP_LE_PER_ADV_TERMINATE_SYNC:
+ return "LE_PER_ADV_TERMINATE_SYNC";
+ case BT_HCI_OP_LE_ADD_DEV_TO_PER_ADV_LIST:
+ return "LE_ADD_DEV_TO_PER_ADV_LIST";
+ case BT_HCI_OP_LE_REM_DEV_FROM_PER_ADV_LIST:
+ return "LE_REM_DEV_FROM_PER_ADV_LIST";
+ case BT_HCI_OP_LE_CLEAR_PER_ADV_LIST:
+ return "LE_CLEAR_PER_ADV_LIST";
+ case BT_HCI_OP_LE_READ_PER_ADV_LIST_SIZE:
+ return "LE_READ_PER_ADV_LIST_SIZE";
+ case BT_HCI_OP_LE_READ_TX_POWER:
+ return "LE_READ_TX_POWER";
+ case BT_HCI_OP_LE_READ_RF_PATH_COMP:
+ return "LE_READ_RF_PATH_COMP";
+ case BT_HCI_OP_LE_WRITE_RF_PATH_COMP:
+ return "LE_WRITE_RF_PATH_COMP";
+ case BT_HCI_OP_LE_SET_PRIVACY_MODE:
+ return "LE_SET_PRIVACY_MODE";
+ default:
+ return "";
+ }
+}
+
+
+STATIC void dump_cmd_pkt(bool tx, uint8_t pkt_len, uint8_t pkt_data[]) {
+ h4_hci_cmd_pkt_t *pkt = (h4_hci_cmd_pkt_t *)pkt_data;
+ mp_printf(&mp_plat_print,
+ "%s HCI COMMAND (%x) op: %s (%04x), len: %d, data: ",
+ tx ? "TX->" : "RX<-",
+ pkt->pkt_type,
+ hci_opcode_name(pkt->opcode), pkt->opcode, pkt->param_len);
+ for (size_t i = 0; i < pkt->param_len; i++) {
+ mp_printf(&mp_plat_print, "%02x ", pkt->params[i]);
+ }
+ if (pkt_len != sizeof(h4_hci_cmd_pkt_t) + pkt->param_len) {
+ mp_printf(&mp_plat_print, " LENGTH MISMATCH, pkt_len: %d", pkt_len);
+ }
+ mp_printf(&mp_plat_print, "\n");
+}
+
+STATIC void dump_acl_pkt(bool tx, uint8_t pkt_len, uint8_t pkt_data[]) {
+ h4_hci_acl_pkt_t *pkt = (h4_hci_acl_pkt_t *)pkt_data;
+ acl_data_t *acl = (acl_data_t *)pkt->data;
+
+ mp_printf(&mp_plat_print,
+ "%s HCI ACLDATA (%x) ",
+ tx ? "TX->" : "RX<-", pkt->pkt_type);
+
+ if (pkt->pb != ACL_DATA_PB_MIDDLE && acl->cid == BT_L2CAP_CID_ATT) {
+ // This is the start of a fragmented acl_data packet or is a full packet,
+ // and is an ATT protocol packet.
+ mp_printf(&mp_plat_print, "att: %s (%02x), ", att_opcode_name(acl->acl_data[0]), acl->acl_data[0]);
+ }
+
+ mp_printf(&mp_plat_print,
+ "handle: %04x, pb: %d, bc: %d, data_len: %d, ",
+ pkt->handle, pkt->pb, pkt->bc, pkt->data_len);
+
+ if (pkt->pb != ACL_DATA_PB_MIDDLE) {
+ // This is the start of a fragmented acl_data packet or is a full packet.
+ mp_printf(&mp_plat_print,
+ "acl data_len: %d, cid: %04x, data: ",
+ acl->acl_data_len, acl->cid);
+ for (size_t i = 0; i < acl->acl_data_len; i++) {
+ mp_printf(&mp_plat_print, "%02x ", acl->acl_data[i]);
+ }
+ } else {
+ for (size_t i = 0; i < pkt->data_len; i++) {
+ mp_printf(&mp_plat_print, "more data: %02x ", pkt->data[i]);
+ }
+ }
+
+ if (pkt_len != sizeof(h4_hci_acl_pkt_t) + pkt->data_len) {
+ mp_printf(&mp_plat_print, " LENGTH MISMATCH, pkt_len: %d", pkt_len);
+ }
+ mp_printf(&mp_plat_print, "\n");
+}
+
+STATIC void dump_evt_pkt(bool tx, uint8_t pkt_len, uint8_t pkt_data[]) {
+ h4_hci_evt_pkt_t *pkt = (h4_hci_evt_pkt_t *)pkt_data;
+ mp_printf(&mp_plat_print,
+ "%s HCI EVENT (%x) evt: %s (%02x), param_len: %d, data: ",
+ tx ? "TX->" : "RX<-",
+ pkt->pkt_type,
+ pkt->evt == BT_HCI_EVT_LE_META_EVENT
+ ? hci_evt_le_name(pkt->params[0])
+ : hci_evt_name(pkt->evt),
+ pkt->evt, pkt->param_len);
+ for (size_t i = 0; i < pkt->param_len; i++) {
+ mp_printf(&mp_plat_print, "%02x ", pkt->params[i]);
+ }
+ if (pkt_len != sizeof(h4_hci_evt_pkt_t) + pkt->param_len) {
+ mp_printf(&mp_plat_print, " LENGTH MISMATCH, pkt_len: %d", pkt_len);
+ }
+ mp_printf(&mp_plat_print, "\n");
+}
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/NOTE.txt b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/NOTE.txt
new file mode 100644
index 0000000..ac34c81
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/NOTE.txt
@@ -0,0 +1,2 @@
+The HCI-related include files here were copied from the Zephyr project, from this commit:
+https://github.com/zephyrproject-rtos/zephyr/tree/0a87f9359edf1ec1c169626df3e19c2b4a4e9646/include/bluetooth
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/addr.h b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/addr.h
new file mode 100644
index 0000000..44f057e
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/addr.h
@@ -0,0 +1,95 @@
+// CircuitPython: Adapted from Zephyer include files.
+/** @file
+ * @brief Bluetooth device address definitions and utilities.
+ */
+
+/*
+ * Copyright (c) 2019 Nordic Semiconductor ASA
+ * Copyright 2020 Dan Halbert for Adafruit Industries
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef ZEPHYR_INCLUDE_BLUETOOTH_ADDR_H_
+#define ZEPHYR_INCLUDE_BLUETOOTH_ADDR_H_
+
+#include <stdbool.h>
+#include <string.h>
+
+/**
+ * @brief Bluetooth device address definitions and utilities.
+ * @defgroup bt_addr Device Address
+ * @ingroup bluetooth
+ * @{
+ */
+
+#define BT_ADDR_LE_PUBLIC 0x00
+#define BT_ADDR_LE_RANDOM 0x01
+#define BT_ADDR_LE_PUBLIC_ID 0x02
+#define BT_ADDR_LE_RANDOM_ID 0x03
+
+/** Bluetooth Device Address */
+typedef struct {
+ uint8_t val[6];
+} bt_addr_t;
+
+/** Bluetooth LE Device Address */
+typedef struct {
+ uint8_t type;
+ bt_addr_t a;
+} bt_addr_le_t;
+
+#define BT_ADDR_ANY ((bt_addr_t[]) { { { 0, 0, 0, 0, 0, 0 } } })
+#define BT_ADDR_NONE ((bt_addr_t[]) { { \
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } })
+#define BT_ADDR_LE_ANY ((bt_addr_le_t[]) { { 0, { { 0, 0, 0, 0, 0, 0 } } } })
+#define BT_ADDR_LE_NONE ((bt_addr_le_t[]) { { 0, \
+ { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } } })
+
+static inline int bt_addr_cmp(const bt_addr_t *a, const bt_addr_t *b) {
+ return memcmp(a, b, sizeof(*a));
+}
+
+static inline int bt_addr_le_cmp(const bt_addr_le_t *a, const bt_addr_le_t *b) {
+ return memcmp(a, b, sizeof(*a));
+}
+
+static inline void bt_addr_copy(bt_addr_t *dst, const bt_addr_t *src) {
+ memcpy(dst, src, sizeof(*dst));
+}
+
+static inline void bt_addr_le_copy(bt_addr_le_t *dst, const bt_addr_le_t *src) {
+ memcpy(dst, src, sizeof(*dst));
+}
+
+#define BT_ADDR_IS_RPA(a) (((a)->val[5] & 0xc0) == 0x40)
+#define BT_ADDR_IS_NRPA(a) (((a)->val[5] & 0xc0) == 0x00)
+#define BT_ADDR_IS_STATIC(a) (((a)->val[5] & 0xc0) == 0xc0)
+
+#define BT_ADDR_SET_RPA(a) ((a)->val[5] = (((a)->val[5] & 0x3f) | 0x40))
+#define BT_ADDR_SET_NRPA(a) ((a)->val[5] &= 0x3f)
+#define BT_ADDR_SET_STATIC(a) ((a)->val[5] |= 0xc0)
+
+int bt_addr_le_create_nrpa(bt_addr_le_t *addr);
+int bt_addr_le_create_static(bt_addr_le_t *addr);
+
+static inline bool bt_addr_le_is_rpa(const bt_addr_le_t *addr) {
+ if (addr->type != BT_ADDR_LE_RANDOM) {
+ return false;
+ }
+
+ return BT_ADDR_IS_RPA(&addr->a);
+}
+
+static inline bool bt_addr_le_is_identity(const bt_addr_le_t *addr) {
+ if (addr->type == BT_ADDR_LE_PUBLIC) {
+ return true;
+ }
+
+ return BT_ADDR_IS_STATIC(&addr->a);
+}
+
+/**
+ * @}
+ */
+
+#endif /* ZEPHYR_INCLUDE_BLUETOOTH_ADDR_H_ */
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/att.h b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/att.h
new file mode 100644
index 0000000..f292ceb
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/att.h
@@ -0,0 +1,41 @@
+// CircuitPython: Adapted from Zephyr include file.
+/** @file
+ * @brief Attribute Protocol handling.
+ */
+
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef ZEPHYR_INCLUDE_BLUETOOTH_ATT_H_
+#define ZEPHYR_INCLUDE_BLUETOOTH_ATT_H_
+
+/* Error codes for Error response PDU */
+#define BT_ATT_ERR_INVALID_HANDLE 0x01
+#define BT_ATT_ERR_READ_NOT_PERMITTED 0x02
+#define BT_ATT_ERR_WRITE_NOT_PERMITTED 0x03
+#define BT_ATT_ERR_INVALID_PDU 0x04
+#define BT_ATT_ERR_AUTHENTICATION 0x05
+#define BT_ATT_ERR_NOT_SUPPORTED 0x06
+#define BT_ATT_ERR_INVALID_OFFSET 0x07
+#define BT_ATT_ERR_AUTHORIZATION 0x08
+#define BT_ATT_ERR_PREPARE_QUEUE_FULL 0x09
+#define BT_ATT_ERR_ATTRIBUTE_NOT_FOUND 0x0a
+#define BT_ATT_ERR_ATTRIBUTE_NOT_LONG 0x0b
+#define BT_ATT_ERR_ENCRYPTION_KEY_SIZE 0x0c
+#define BT_ATT_ERR_INVALID_ATTRIBUTE_LEN 0x0d
+#define BT_ATT_ERR_UNLIKELY 0x0e
+#define BT_ATT_ERR_INSUFFICIENT_ENCRYPTION 0x0f
+#define BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE 0x10
+#define BT_ATT_ERR_INSUFFICIENT_RESOURCES 0x11
+#define BT_ATT_ERR_DB_OUT_OF_SYNC 0x12
+#define BT_ATT_ERR_VALUE_NOT_ALLOWED 0x13
+
+/* Common Profile Error Codes (from CSS) */
+#define BT_ATT_ERR_WRITE_REQ_REJECTED 0xfc
+#define BT_ATT_ERR_CCC_IMPROPER_CONF 0xfd
+#define BT_ATT_ERR_PROCEDURE_IN_PROGRESS 0xfe
+#define BT_ATT_ERR_OUT_OF_RANGE 0xff
+
+#endif /* ZEPHYR_INCLUDE_BLUETOOTH_ATT_H_ */
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/att_internal.h b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/att_internal.h
new file mode 100644
index 0000000..af8695d
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/att_internal.h
@@ -0,0 +1,273 @@
+// CircuitPython: Adapted from Zephyr include file.
+
+/* att_internal.h - Attribute protocol handling */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include <stdbool.h>
+// for __packed
+#include <sys/cdefs.h>
+
+#define BT_EATT_PSM 0x27
+#define BT_ATT_DEFAULT_LE_MTU 23
+#define BT_ATT_TIMEOUT K_SECONDS(30)
+
+// FIX #if BT_L2CAP_RX_MTU < CONFIG_BT_L2CAP_TX_MTU
+// #define BT_ATT_MTU BT_L2CAP_RX_MTU
+// #else
+// #define BT_ATT_MTU CONFIG_BT_L2CAP_TX_MTU
+// #endif
+
+struct bt_att_hdr {
+ uint8_t code;
+} __packed;
+
+#define BT_ATT_OP_ERROR_RSP 0x01
+struct bt_att_error_rsp {
+ uint8_t request;
+ uint16_t handle;
+ uint8_t error;
+} __packed;
+
+#define BT_ATT_OP_MTU_REQ 0x02
+struct bt_att_exchange_mtu_req {
+ uint16_t mtu;
+} __packed;
+
+#define BT_ATT_OP_MTU_RSP 0x03
+struct bt_att_exchange_mtu_rsp {
+ uint16_t mtu;
+} __packed;
+
+/* Find Information Request */
+#define BT_ATT_OP_FIND_INFO_REQ 0x04
+struct bt_att_find_info_req {
+ uint16_t start_handle;
+ uint16_t end_handle;
+} __packed;
+
+/* Format field values for BT_ATT_OP_FIND_INFO_RSP */
+#define BT_ATT_INFO_16 0x01
+#define BT_ATT_INFO_128 0x02
+
+struct bt_att_info_16 {
+ uint16_t handle;
+ uint16_t uuid;
+} __packed;
+
+struct bt_att_info_128 {
+ uint16_t handle;
+ uint8_t uuid[16];
+} __packed;
+
+/* Find Information Response */
+#define BT_ATT_OP_FIND_INFO_RSP 0x05
+struct bt_att_find_info_rsp {
+ uint8_t format;
+ uint8_t info[];
+} __packed;
+
+/* Find By Type Value Request */
+#define BT_ATT_OP_FIND_TYPE_REQ 0x06
+struct bt_att_find_type_req {
+ uint16_t start_handle;
+ uint16_t end_handle;
+ uint16_t type;
+ uint8_t value[];
+} __packed;
+
+struct bt_att_handle_group {
+ uint16_t start_handle;
+ uint16_t end_handle;
+} __packed;
+
+/* Find By Type Value Response */
+#define BT_ATT_OP_FIND_TYPE_RSP 0x07
+struct bt_att_find_type_rsp {
+ uint8_t _dummy[0];
+ struct bt_att_handle_group list[];
+} __packed;
+
+/* Read By Type Request */
+#define BT_ATT_OP_READ_TYPE_REQ 0x08
+struct bt_att_read_type_req {
+ uint16_t start_handle;
+ uint16_t end_handle;
+ uint8_t uuid[];
+} __packed;
+
+struct bt_att_data {
+ uint16_t handle;
+ uint8_t value[];
+} __packed;
+
+/* Read By Type Response */
+#define BT_ATT_OP_READ_TYPE_RSP 0x09
+struct bt_att_read_type_rsp {
+ uint8_t len;
+ struct bt_att_data data[];
+} __packed;
+
+/* Read Request */
+#define BT_ATT_OP_READ_REQ 0x0a
+struct bt_att_read_req {
+ uint16_t handle;
+} __packed;
+
+/* Read Response */
+#define BT_ATT_OP_READ_RSP 0x0b
+struct bt_att_read_rsp {
+ uint8_t _dummy[0];
+ uint8_t value[];
+} __packed;
+
+/* Read Blob Request */
+#define BT_ATT_OP_READ_BLOB_REQ 0x0c
+struct bt_att_read_blob_req {
+ uint16_t handle;
+ uint16_t offset;
+} __packed;
+
+/* Read Blob Response */
+#define BT_ATT_OP_READ_BLOB_RSP 0x0d
+struct bt_att_read_blob_rsp {
+ uint8_t _dummy[0];
+ uint8_t value[];
+} __packed;
+
+/* Read Multiple Request */
+#define BT_ATT_READ_MULT_MIN_LEN_REQ 0x04
+
+#define BT_ATT_OP_READ_MULT_REQ 0x0e
+struct bt_att_read_mult_req {
+ uint8_t _dummy[0];
+ uint16_t handles[];
+} __packed;
+
+/* Read Multiple Respose */
+#define BT_ATT_OP_READ_MULT_RSP 0x0f
+struct bt_att_read_mult_rsp {
+ uint8_t _dummy[0];
+ uint8_t value[];
+} __packed;
+
+/* Read by Group Type Request */
+#define BT_ATT_OP_READ_GROUP_REQ 0x10
+struct bt_att_read_group_req {
+ uint16_t start_handle;
+ uint16_t end_handle;
+ uint8_t uuid[];
+} __packed;
+
+struct bt_att_group_data {
+ uint16_t start_handle;
+ uint16_t end_handle;
+ uint8_t value[];
+} __packed;
+
+/* Read by Group Type Response */
+#define BT_ATT_OP_READ_GROUP_RSP 0x11
+struct bt_att_read_group_rsp {
+ uint8_t len;
+ struct bt_att_group_data data[];
+} __packed;
+
+/* Write Request */
+#define BT_ATT_OP_WRITE_REQ 0x12
+struct bt_att_write_req {
+ uint16_t handle;
+ uint8_t value[];
+} __packed;
+
+/* Write Response */
+#define BT_ATT_OP_WRITE_RSP 0x13
+
+/* Prepare Write Request */
+#define BT_ATT_OP_PREPARE_WRITE_REQ 0x16
+struct bt_att_prepare_write_req {
+ uint16_t handle;
+ uint16_t offset;
+ uint8_t value[];
+} __packed;
+
+/* Prepare Write Respond */
+#define BT_ATT_OP_PREPARE_WRITE_RSP 0x17
+struct bt_att_prepare_write_rsp {
+ uint16_t handle;
+ uint16_t offset;
+ uint8_t value[];
+} __packed;
+
+/* Execute Write Request */
+#define BT_ATT_FLAG_CANCEL 0x00
+#define BT_ATT_FLAG_EXEC 0x01
+
+#define BT_ATT_OP_EXEC_WRITE_REQ 0x18
+struct bt_att_exec_write_req {
+ uint8_t flags;
+} __packed;
+
+/* Execute Write Response */
+#define BT_ATT_OP_EXEC_WRITE_RSP 0x19
+
+/* Handle Value Notification */
+#define BT_ATT_OP_NOTIFY 0x1b
+struct bt_att_notify {
+ uint16_t handle;
+ uint8_t value[];
+} __packed;
+
+/* Handle Value Indication */
+#define BT_ATT_OP_INDICATE 0x1d
+struct bt_att_indicate {
+ uint16_t handle;
+ uint8_t value[];
+} __packed;
+
+/* Handle Value Confirm */
+#define BT_ATT_OP_CONFIRM 0x1e
+
+struct bt_att_signature {
+ uint8_t value[12];
+} __packed;
+
+#define BT_ATT_OP_READ_MULT_VL_REQ 0x20
+struct bt_att_read_mult_vl_req {
+ uint8_t _dummy[0];
+ uint16_t handles[];
+} __packed;
+
+/* Read Multiple Respose */
+#define BT_ATT_OP_READ_MULT_VL_RSP 0x21
+struct bt_att_read_mult_vl_rsp {
+ uint16_t len;
+ uint8_t value[];
+} __packed;
+
+/* Handle Multiple Value Notification */
+#define BT_ATT_OP_NOTIFY_MULT 0x23
+struct bt_att_notify_mult {
+ uint16_t handle;
+ uint16_t len;
+ uint8_t value[];
+} __packed;
+
+/* Write Command */
+#define BT_ATT_OP_WRITE_CMD 0x52
+struct bt_att_write_cmd {
+ uint16_t handle;
+ uint8_t value[];
+} __packed;
+
+/* Signed Write Command */
+#define BT_ATT_OP_SIGNED_WRITE_CMD 0xd2
+struct bt_att_signed_write_cmd {
+ uint16_t handle;
+ uint8_t value[];
+} __packed;
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci.h b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci.h
new file mode 100644
index 0000000..b5f9506
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci.h
@@ -0,0 +1,1772 @@
+// CircuitPython: Adapted from Zephyr include file.
+
+/* hci.h - Bluetooth Host Control Interface definitions */
+
+/*
+ * Copyright 2020 Dan Halbert for Adafruit Industries
+ * Copyright (c) 2015-2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef ZEPHYR_INCLUDE_BLUETOOTH_HCI_H_
+#define ZEPHYR_INCLUDE_BLUETOOTH_HCI_H_
+
+#include <stdbool.h>
+// for __packed
+#include <sys/cdefs.h>
+
+#include "addr.h"
+
+// ESP32S2 build environment defines this already.
+#ifndef BIT
+#define BIT(n) (1UL << (n))
+#endif
+
+/* Special own address types for LL privacy (used in adv & scan parameters) */
+#define BT_HCI_OWN_ADDR_RPA_OR_PUBLIC 0x02
+#define BT_HCI_OWN_ADDR_RPA_OR_RANDOM 0x03
+#define BT_HCI_OWN_ADDR_RPA_MASK 0x02
+
+#define BT_HCI_PEER_ADDR_RPA_UNRESOLVED 0xfe
+#define BT_HCI_PEER_ADDR_ANONYMOUS 0xff
+
+#define BT_ENC_KEY_SIZE_MIN 0x07
+#define BT_ENC_KEY_SIZE_MAX 0x10
+
+struct bt_hci_evt_hdr {
+ uint8_t evt;
+ uint8_t len;
+} __packed;
+#define BT_HCI_EVT_HDR_SIZE 2
+
+#define BT_ACL_START_NO_FLUSH 0x00
+#define BT_ACL_CONT 0x01
+#define BT_ACL_START 0x02
+#define BT_ACL_COMPLETE 0x03
+
+#define BT_ACL_POINT_TO_POINT 0x00
+#define BT_ACL_BROADCAST 0x01
+
+#define bt_acl_handle(h) ((h) & BIT_MASK(12))
+#define bt_acl_flags(h) ((h) >> 12)
+#define bt_acl_flags_pb(f) ((f) & BIT_MASK(2))
+#define bt_acl_flags_bc(f) ((f) >> 2)
+#define bt_acl_handle_pack(h, f) ((h) | ((f) << 12))
+
+struct bt_hci_acl_hdr {
+ uint16_t handle;
+ uint16_t len;
+} __packed;
+#define BT_HCI_ACL_HDR_SIZE 4
+
+struct bt_hci_cmd_hdr {
+ uint16_t opcode;
+ uint8_t param_len;
+} __packed;
+#define BT_HCI_CMD_HDR_SIZE 3
+
+/* Supported Commands */
+#define BT_CMD_TEST(cmd, octet, bit) (cmd[octet] & BIT(bit))
+#define BT_CMD_LE_STATES(cmd) BT_CMD_TEST(cmd, 28, 3)
+
+#define BT_FEAT_TEST(feat, page, octet, bit) (feat[page][octet] & BIT(bit))
+
+#define BT_FEAT_BREDR(feat) !BT_FEAT_TEST(feat, 0, 4, 5)
+#define BT_FEAT_LE(feat) BT_FEAT_TEST(feat, 0, 4, 6)
+#define BT_FEAT_EXT_FEATURES(feat) BT_FEAT_TEST(feat, 0, 7, 7)
+#define BT_FEAT_HOST_SSP(feat) BT_FEAT_TEST(feat, 1, 0, 0)
+#define BT_FEAT_SC(feat) BT_FEAT_TEST(feat, 2, 1, 0)
+
+#define BT_FEAT_LMP_ESCO_CAPABLE(feat) BT_FEAT_TEST(feat, 0, 3, 7)
+#define BT_FEAT_HV2_PKT(feat) BT_FEAT_TEST(feat, 0, 1, 4)
+#define BT_FEAT_HV3_PKT(feat) BT_FEAT_TEST(feat, 0, 1, 5)
+#define BT_FEAT_EV4_PKT(feat) BT_FEAT_TEST(feat, 0, 4, 0)
+#define BT_FEAT_EV5_PKT(feat) BT_FEAT_TEST(feat, 0, 4, 1)
+#define BT_FEAT_2EV3_PKT(feat) BT_FEAT_TEST(feat, 0, 5, 5)
+#define BT_FEAT_3EV3_PKT(feat) BT_FEAT_TEST(feat, 0, 5, 6)
+#define BT_FEAT_3SLOT_PKT(feat) BT_FEAT_TEST(feat, 0, 5, 7)
+
+/* LE features */
+#define BT_LE_FEAT_BIT_ENC 0
+#define BT_LE_FEAT_BIT_CONN_PARAM_REQ 1
+#define BT_LE_FEAT_BIT_EXT_REJ_IND 2
+#define BT_LE_FEAT_BIT_SLAVE_FEAT_REQ 3
+#define BT_LE_FEAT_BIT_PING 4
+#define BT_LE_FEAT_BIT_DLE 5
+#define BT_LE_FEAT_BIT_PRIVACY 6
+#define BT_LE_FEAT_BIT_EXT_SCAN 7
+#define BT_LE_FEAT_BIT_PHY_2M 8
+#define BT_LE_FEAT_BIT_SMI_TX 9
+#define BT_LE_FEAT_BIT_SMI_RX 10
+#define BT_LE_FEAT_BIT_PHY_CODED 11
+#define BT_LE_FEAT_BIT_EXT_ADV 12
+#define BT_LE_FEAT_BIT_PER_ADV 13
+#define BT_LE_FEAT_BIT_CHAN_SEL_ALGO_2 14
+#define BT_LE_FEAT_BIT_PWR_CLASS_1 15
+#define BT_LE_FEAT_BIT_MIN_USED_CHAN_PROC 16
+#define BT_LE_FEAT_BIT_CONN_CTE_REQ 17
+#define BT_LE_FEAT_BIT_CONN_CTE_RESP 18
+#define BT_LE_FEAT_BIT_CONNECTIONLESS_CTE_TX 19
+#define BT_LE_FEAT_BIT_CONNECTIONLESS_CTE_RX 20
+#define BT_LE_FEAT_BIT_ANT_SWITCH_TX_AOD 21
+#define BT_LE_FEAT_BIT_ANT_SWITCH_RX_AOA 22
+#define BT_LE_FEAT_BIT_RX_CTE 23
+#define BT_LE_FEAT_BIT_PERIODIC_SYNC_XFER_SEND 24
+#define BT_LE_FEAT_BIT_PERIODIC_SYNC_XFER_RECV 25
+#define BT_LE_FEAT_BIT_SCA_UPDATE 26
+#define BT_LE_FEAT_BIT_REMOTE_PUB_KEY_VALIDATE 27
+#define BT_LE_FEAT_BIT_CIS_MASTER 28
+#define BT_LE_FEAT_BIT_CIS_SLAVE 29
+#define BT_LE_FEAT_BIT_ISO_BROADCASTER 30
+#define BT_LE_FEAT_BIT_SYNC_RECEIVER 31
+#define BT_LE_FEAT_BIT_ISO_CHANNELS 32
+#define BT_LE_FEAT_BIT_PWR_CTRL_REQ 33
+#define BT_LE_FEAT_BIT_PWR_CHG_IND 34
+#define BT_LE_FEAT_BIT_PATH_LOSS_MONITOR 35
+
+#define BT_LE_FEAT_TEST(feat, n) (feat[(n) >> 3] & \
+ BIT((n) & 7))
+
+#define BT_FEAT_LE_ENCR(feat) BT_LE_FEAT_TEST(feat, \
+ BT_LE_FEAT_BIT_ENC)
+#define BT_FEAT_LE_CONN_PARAM_REQ_PROC(feat) BT_LE_FEAT_TEST(feat, \
+ BT_LE_FEAT_BIT_CONN_PARAM_REQ)
+#define BT_FEAT_LE_SLAVE_FEATURE_XCHG(feat) BT_LE_FEAT_TEST(feat, \
+ BT_LE_FEAT_BIT_SLAVE_FEAT_REQ)
+#define BT_FEAT_LE_DLE(feat) BT_LE_FEAT_TEST(feat, \
+ BT_LE_FEAT_BIT_DLE)
+#define BT_FEAT_LE_PHY_2M(feat) BT_LE_FEAT_TEST(feat, \
+ BT_LE_FEAT_BIT_PHY_2M)
+#define BT_FEAT_LE_PHY_CODED(feat) BT_LE_FEAT_TEST(feat, \
+ BT_LE_FEAT_BIT_PHY_CODED)
+#define BT_FEAT_LE_PRIVACY(feat) BT_LE_FEAT_TEST(feat, \
+ BT_LE_FEAT_BIT_PRIVACY)
+#define BT_FEAT_LE_EXT_ADV(feat) BT_LE_FEAT_TEST(feat, \
+ BT_LE_FEAT_BIT_EXT_ADV)
+
+/* LE States */
+#define BT_LE_STATES_SLAVE_CONN_ADV(states) (states & 0x0000004000000000)
+
+/* Bonding/authentication types */
+#define BT_HCI_NO_BONDING 0x00
+#define BT_HCI_NO_BONDING_MITM 0x01
+#define BT_HCI_DEDICATED_BONDING 0x02
+#define BT_HCI_DEDICATED_BONDING_MITM 0x03
+#define BT_HCI_GENERAL_BONDING 0x04
+#define BT_HCI_GENERAL_BONDING_MITM 0x05
+
+/*
+ * MITM protection is enabled in SSP authentication requirements octet when
+ * LSB bit is set.
+ */
+#define BT_MITM 0x01
+
+/* I/O capabilities */
+#define BT_IO_DISPLAY_ONLY 0x00
+#define BT_IO_DISPLAY_YESNO 0x01
+#define BT_IO_KEYBOARD_ONLY 0x02
+#define BT_IO_NO_INPUT_OUTPUT 0x03
+
+/* SCO packet types */
+#define HCI_PKT_TYPE_HV1 0x0020
+#define HCI_PKT_TYPE_HV2 0x0040
+#define HCI_PKT_TYPE_HV3 0x0080
+
+/* eSCO packet types */
+#define HCI_PKT_TYPE_ESCO_HV1 0x0001
+#define HCI_PKT_TYPE_ESCO_HV2 0x0002
+#define HCI_PKT_TYPE_ESCO_HV3 0x0004
+#define HCI_PKT_TYPE_ESCO_EV3 0x0008
+#define HCI_PKT_TYPE_ESCO_EV4 0x0010
+#define HCI_PKT_TYPE_ESCO_EV5 0x0020
+#define HCI_PKT_TYPE_ESCO_2EV3 0x0040
+#define HCI_PKT_TYPE_ESCO_3EV3 0x0080
+#define HCI_PKT_TYPE_ESCO_2EV5 0x0100
+#define HCI_PKT_TYPE_ESCO_3EV5 0x0200
+
+
+#define ESCO_PKT_MASK (HCI_PKT_TYPE_ESCO_HV1 | \
+ HCI_PKT_TYPE_ESCO_HV2 | \
+ HCI_PKT_TYPE_ESCO_HV3)
+#define SCO_PKT_MASK (HCI_PKT_TYPE_HV1 | \
+ HCI_PKT_TYPE_HV2 | \
+ HCI_PKT_TYPE_HV3)
+#define EDR_ESCO_PKT_MASK (HCI_PKT_TYPE_ESCO_2EV3 | \
+ HCI_PKT_TYPE_ESCO_3EV3 | \
+ HCI_PKT_TYPE_ESCO_2EV5 | \
+ HCI_PKT_TYPE_ESCO_3EV5)
+
+/* HCI BR/EDR link types */
+#define BT_HCI_SCO 0x00
+#define BT_HCI_ACL 0x01
+#define BT_HCI_ESCO 0x02
+
+/* OpCode Group Fields */
+#define BT_OGF_LINK_CTRL 0x01
+#define BT_OGF_BASEBAND 0x03
+#define BT_OGF_INFO 0x04
+#define BT_OGF_STATUS 0x05
+#define BT_OGF_LE 0x08
+#define BT_OGF_VS 0x3f
+
+/* Construct OpCode from OGF and OCF */
+#define BT_OP(ogf, ocf) ((ocf) | ((ogf) << 10))
+
+/* Invalid opcode */
+#define BT_OP_NOP 0x0000
+
+/* Obtain OGF from OpCode */
+#define BT_OGF(opcode) (((opcode) >> 10) & BIT_MASK(6))
+/* Obtain OCF from OpCode */
+#define BT_OCF(opcode) ((opcode) & BIT_MASK(10))
+
+#define BT_HCI_OP_INQUIRY BT_OP(BT_OGF_LINK_CTRL, 0x0001)
+struct bt_hci_op_inquiry {
+ uint8_t lap[3];
+ uint8_t length;
+ uint8_t num_rsp;
+} __packed;
+
+#define BT_HCI_OP_INQUIRY_CANCEL BT_OP(BT_OGF_LINK_CTRL, 0x0002)
+
+#define BT_HCI_OP_CONNECT BT_OP(BT_OGF_LINK_CTRL, 0x0005)
+struct bt_hci_cp_connect {
+ bt_addr_t bdaddr;
+ uint16_t packet_type;
+ uint8_t pscan_rep_mode;
+ uint8_t reserved;
+ uint16_t clock_offset;
+ uint8_t allow_role_switch;
+} __packed;
+
+#define BT_HCI_OP_DISCONNECT BT_OP(BT_OGF_LINK_CTRL, 0x0006)
+struct bt_hci_cp_disconnect {
+ uint16_t handle;
+ uint8_t reason;
+} __packed;
+
+#define BT_HCI_OP_CONNECT_CANCEL BT_OP(BT_OGF_LINK_CTRL, 0x0008)
+struct bt_hci_cp_connect_cancel {
+ bt_addr_t bdaddr;
+} __packed;
+struct bt_hci_rp_connect_cancel {
+ uint8_t status;
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_OP_ACCEPT_CONN_REQ BT_OP(BT_OGF_LINK_CTRL, 0x0009)
+struct bt_hci_cp_accept_conn_req {
+ bt_addr_t bdaddr;
+ uint8_t role;
+} __packed;
+
+#define BT_HCI_OP_SETUP_SYNC_CONN BT_OP(BT_OGF_LINK_CTRL, 0x0028)
+struct bt_hci_cp_setup_sync_conn {
+ uint16_t handle;
+ uint32_t tx_bandwidth;
+ uint32_t rx_bandwidth;
+ uint16_t max_latency;
+ uint16_t content_format;
+ uint8_t retrans_effort;
+ uint16_t pkt_type;
+} __packed;
+
+#define BT_HCI_OP_ACCEPT_SYNC_CONN_REQ BT_OP(BT_OGF_LINK_CTRL, 0x0029)
+struct bt_hci_cp_accept_sync_conn_req {
+ bt_addr_t bdaddr;
+ uint32_t tx_bandwidth;
+ uint32_t rx_bandwidth;
+ uint16_t max_latency;
+ uint16_t content_format;
+ uint8_t retrans_effort;
+ uint16_t pkt_type;
+} __packed;
+
+#define BT_HCI_OP_REJECT_CONN_REQ BT_OP(BT_OGF_LINK_CTRL, 0x000a)
+struct bt_hci_cp_reject_conn_req {
+ bt_addr_t bdaddr;
+ uint8_t reason;
+} __packed;
+
+#define BT_HCI_OP_LINK_KEY_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000b)
+struct bt_hci_cp_link_key_reply {
+ bt_addr_t bdaddr;
+ uint8_t link_key[16];
+} __packed;
+
+#define BT_HCI_OP_LINK_KEY_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000c)
+struct bt_hci_cp_link_key_neg_reply {
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_OP_PIN_CODE_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000d)
+struct bt_hci_cp_pin_code_reply {
+ bt_addr_t bdaddr;
+ uint8_t pin_len;
+ uint8_t pin_code[16];
+} __packed;
+struct bt_hci_rp_pin_code_reply {
+ uint8_t status;
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_OP_PIN_CODE_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000e)
+struct bt_hci_cp_pin_code_neg_reply {
+ bt_addr_t bdaddr;
+} __packed;
+struct bt_hci_rp_pin_code_neg_reply {
+ uint8_t status;
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_OP_AUTH_REQUESTED BT_OP(BT_OGF_LINK_CTRL, 0x0011)
+struct bt_hci_cp_auth_requested {
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_OP_SET_CONN_ENCRYPT BT_OP(BT_OGF_LINK_CTRL, 0x0013)
+struct bt_hci_cp_set_conn_encrypt {
+ uint16_t handle;
+ uint8_t encrypt;
+} __packed;
+
+#define BT_HCI_OP_REMOTE_NAME_REQUEST BT_OP(BT_OGF_LINK_CTRL, 0x0019)
+struct bt_hci_cp_remote_name_request {
+ bt_addr_t bdaddr;
+ uint8_t pscan_rep_mode;
+ uint8_t reserved;
+ uint16_t clock_offset;
+} __packed;
+
+#define BT_HCI_OP_REMOTE_NAME_CANCEL BT_OP(BT_OGF_LINK_CTRL, 0x001a)
+struct bt_hci_cp_remote_name_cancel {
+ bt_addr_t bdaddr;
+} __packed;
+struct bt_hci_rp_remote_name_cancel {
+ uint8_t status;
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_OP_READ_REMOTE_FEATURES BT_OP(BT_OGF_LINK_CTRL, 0x001b)
+struct bt_hci_cp_read_remote_features {
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_OP_READ_REMOTE_EXT_FEATURES BT_OP(BT_OGF_LINK_CTRL, 0x001c)
+struct bt_hci_cp_read_remote_ext_features {
+ uint16_t handle;
+ uint8_t page;
+} __packed;
+
+#define BT_HCI_OP_READ_REMOTE_VERSION_INFO BT_OP(BT_OGF_LINK_CTRL, 0x001d)
+struct bt_hci_cp_read_remote_version_info {
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_OP_IO_CAPABILITY_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002b)
+struct bt_hci_cp_io_capability_reply {
+ bt_addr_t bdaddr;
+ uint8_t capability;
+ uint8_t oob_data;
+ uint8_t authentication;
+} __packed;
+
+#define BT_HCI_OP_USER_CONFIRM_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002c)
+#define BT_HCI_OP_USER_CONFIRM_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002d)
+struct bt_hci_cp_user_confirm_reply {
+ bt_addr_t bdaddr;
+} __packed;
+struct bt_hci_rp_user_confirm_reply {
+ uint8_t status;
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_OP_USER_PASSKEY_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002e)
+struct bt_hci_cp_user_passkey_reply {
+ bt_addr_t bdaddr;
+ uint32_t passkey;
+} __packed;
+
+#define BT_HCI_OP_USER_PASSKEY_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002f)
+struct bt_hci_cp_user_passkey_neg_reply {
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_OP_IO_CAPABILITY_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x0034)
+struct bt_hci_cp_io_capability_neg_reply {
+ bt_addr_t bdaddr;
+ uint8_t reason;
+} __packed;
+
+#define BT_HCI_OP_SET_EVENT_MASK BT_OP(BT_OGF_BASEBAND, 0x0001)
+struct bt_hci_cp_set_event_mask {
+ uint8_t events[8];
+} __packed;
+
+#define BT_HCI_OP_RESET BT_OP(BT_OGF_BASEBAND, 0x0003)
+
+#define BT_HCI_OP_WRITE_LOCAL_NAME BT_OP(BT_OGF_BASEBAND, 0x0013)
+struct bt_hci_write_local_name {
+ uint8_t local_name[248];
+} __packed;
+
+#define BT_HCI_OP_WRITE_PAGE_TIMEOUT BT_OP(BT_OGF_BASEBAND, 0x0018)
+
+#define BT_HCI_OP_WRITE_SCAN_ENABLE BT_OP(BT_OGF_BASEBAND, 0x001a)
+#define BT_BREDR_SCAN_DISABLED 0x00
+#define BT_BREDR_SCAN_INQUIRY 0x01
+#define BT_BREDR_SCAN_PAGE 0x02
+
+#define BT_TX_POWER_LEVEL_CURRENT 0x00
+#define BT_TX_POWER_LEVEL_MAX 0x01
+#define BT_HCI_OP_READ_TX_POWER_LEVEL BT_OP(BT_OGF_BASEBAND, 0x002d)
+struct bt_hci_cp_read_tx_power_level {
+ uint16_t handle;
+ uint8_t type;
+} __packed;
+
+struct bt_hci_rp_read_tx_power_level {
+ uint8_t status;
+ uint16_t handle;
+ int8_t tx_power_level;
+} __packed;
+
+#define BT_HCI_CTL_TO_HOST_FLOW_DISABLE 0x00
+#define BT_HCI_CTL_TO_HOST_FLOW_ENABLE 0x01
+#define BT_HCI_OP_SET_CTL_TO_HOST_FLOW BT_OP(BT_OGF_BASEBAND, 0x0031)
+struct bt_hci_cp_set_ctl_to_host_flow {
+ uint8_t flow_enable;
+} __packed;
+
+#define BT_HCI_OP_HOST_BUFFER_SIZE BT_OP(BT_OGF_BASEBAND, 0x0033)
+struct bt_hci_cp_host_buffer_size {
+ uint16_t acl_mtu;
+ uint8_t sco_mtu;
+ uint16_t acl_pkts;
+ uint16_t sco_pkts;
+} __packed;
+
+struct bt_hci_handle_count {
+ uint16_t handle;
+ uint16_t count;
+} __packed;
+
+#define BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS BT_OP(BT_OGF_BASEBAND, 0x0035)
+struct bt_hci_cp_host_num_completed_packets {
+ uint8_t num_handles;
+ struct bt_hci_handle_count h[];
+} __packed;
+
+#define BT_HCI_OP_WRITE_INQUIRY_MODE BT_OP(BT_OGF_BASEBAND, 0x0045)
+struct bt_hci_cp_write_inquiry_mode {
+ uint8_t mode;
+} __packed;
+
+#define BT_HCI_OP_WRITE_SSP_MODE BT_OP(BT_OGF_BASEBAND, 0x0056)
+struct bt_hci_cp_write_ssp_mode {
+ uint8_t mode;
+} __packed;
+
+#define BT_HCI_OP_SET_EVENT_MASK_PAGE_2 BT_OP(BT_OGF_BASEBAND, 0x0063)
+struct bt_hci_cp_set_event_mask_page_2 {
+ uint8_t events_page_2[8];
+} __packed;
+
+#define BT_HCI_OP_LE_WRITE_LE_HOST_SUPP BT_OP(BT_OGF_BASEBAND, 0x006d)
+struct bt_hci_cp_write_le_host_supp {
+ uint8_t le;
+ uint8_t simul;
+} __packed;
+
+#define BT_HCI_OP_WRITE_SC_HOST_SUPP BT_OP(BT_OGF_BASEBAND, 0x007a)
+struct bt_hci_cp_write_sc_host_supp {
+ uint8_t sc_support;
+} __packed;
+
+#define BT_HCI_OP_READ_AUTH_PAYLOAD_TIMEOUT BT_OP(BT_OGF_BASEBAND, 0x007b)
+struct bt_hci_cp_read_auth_payload_timeout {
+ uint16_t handle;
+} __packed;
+
+struct bt_hci_rp_read_auth_payload_timeout {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t auth_payload_timeout;
+} __packed;
+
+#define BT_HCI_OP_WRITE_AUTH_PAYLOAD_TIMEOUT BT_OP(BT_OGF_BASEBAND, 0x007c)
+struct bt_hci_cp_write_auth_payload_timeout {
+ uint16_t handle;
+ uint16_t auth_payload_timeout;
+} __packed;
+
+struct bt_hci_rp_write_auth_payload_timeout {
+ uint8_t status;
+ uint16_t handle;
+} __packed;
+
+/* HCI version from Assigned Numbers */
+#define BT_HCI_VERSION_1_0B 0
+#define BT_HCI_VERSION_1_1 1
+#define BT_HCI_VERSION_1_2 2
+#define BT_HCI_VERSION_2_0 3
+#define BT_HCI_VERSION_2_1 4
+#define BT_HCI_VERSION_3_0 5
+#define BT_HCI_VERSION_4_0 6
+#define BT_HCI_VERSION_4_1 7
+#define BT_HCI_VERSION_4_2 8
+#define BT_HCI_VERSION_5_0 9
+#define BT_HCI_VERSION_5_1 10
+#define BT_HCI_VERSION_5_2 11
+
+#define BT_HCI_OP_READ_LOCAL_VERSION_INFO BT_OP(BT_OGF_INFO, 0x0001)
+struct bt_hci_rp_read_local_version_info {
+ uint8_t status;
+ uint8_t hci_version;
+ uint16_t hci_revision;
+ uint8_t lmp_version;
+ uint16_t manufacturer;
+ uint16_t lmp_subversion;
+} __packed;
+
+#define BT_HCI_OP_READ_SUPPORTED_COMMANDS BT_OP(BT_OGF_INFO, 0x0002)
+struct bt_hci_rp_read_supported_commands {
+ uint8_t status;
+ uint8_t commands[64];
+} __packed;
+
+#define BT_HCI_OP_READ_LOCAL_EXT_FEATURES BT_OP(BT_OGF_INFO, 0x0004)
+struct bt_hci_cp_read_local_ext_features {
+ uint8_t page;
+};
+struct bt_hci_rp_read_local_ext_features {
+ uint8_t status;
+ uint8_t page;
+ uint8_t max_page;
+ uint8_t ext_features[8];
+} __packed;
+
+#define BT_HCI_OP_READ_LOCAL_FEATURES BT_OP(BT_OGF_INFO, 0x0003)
+struct bt_hci_rp_read_local_features {
+ uint8_t status;
+ uint8_t features[8];
+} __packed;
+
+#define BT_HCI_OP_READ_BUFFER_SIZE BT_OP(BT_OGF_INFO, 0x0005)
+struct bt_hci_rp_read_buffer_size {
+ uint8_t status;
+ uint16_t acl_max_len;
+ uint8_t sco_max_len;
+ uint16_t acl_max_num;
+ uint16_t sco_max_num;
+} __packed;
+
+#define BT_HCI_OP_READ_BD_ADDR BT_OP(BT_OGF_INFO, 0x0009)
+struct bt_hci_rp_read_bd_addr {
+ uint8_t status;
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_OP_READ_RSSI BT_OP(BT_OGF_STATUS, 0x0005)
+struct bt_hci_cp_read_rssi {
+ uint16_t handle;
+} __packed;
+struct bt_hci_rp_read_rssi {
+ uint8_t status;
+ uint16_t handle;
+ int8_t rssi;
+} __packed;
+
+#define BT_HCI_ENCRYPTION_KEY_SIZE_MIN 7
+#define BT_HCI_ENCRYPTION_KEY_SIZE_MAX 16
+
+#define BT_HCI_OP_READ_ENCRYPTION_KEY_SIZE BT_OP(BT_OGF_STATUS, 0x0008)
+struct bt_hci_cp_read_encryption_key_size {
+ uint16_t handle;
+} __packed;
+struct bt_hci_rp_read_encryption_key_size {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t key_size;
+} __packed;
+
+/* BLE */
+
+#define BT_HCI_OP_LE_SET_EVENT_MASK BT_OP(BT_OGF_LE, 0x0001)
+struct bt_hci_cp_le_set_event_mask {
+ uint8_t events[8];
+} __packed;
+
+#define BT_HCI_OP_LE_READ_BUFFER_SIZE BT_OP(BT_OGF_LE, 0x0002)
+struct bt_hci_rp_le_read_buffer_size {
+ uint8_t status;
+ uint16_t le_max_len;
+ uint8_t le_max_num;
+} __packed;
+
+#define BT_HCI_OP_LE_READ_LOCAL_FEATURES BT_OP(BT_OGF_LE, 0x0003)
+struct bt_hci_rp_le_read_local_features {
+ uint8_t status;
+ uint8_t features[8];
+} __packed;
+
+#define BT_HCI_OP_LE_SET_RANDOM_ADDRESS BT_OP(BT_OGF_LE, 0x0005)
+struct bt_hci_cp_le_set_random_address {
+ bt_addr_t bdaddr;
+} __packed;
+
+/* LE Advertising Types (LE Advertising Parameters Set)*/
+#define BT_LE_ADV_IND (__DEPRECATED_MACRO 0x00)
+#define BT_LE_ADV_DIRECT_IND (__DEPRECATED_MACRO 0x01)
+#define BT_LE_ADV_SCAN_IND (__DEPRECATED_MACRO 0x02)
+#define BT_LE_ADV_NONCONN_IND (__DEPRECATED_MACRO 0x03)
+#define BT_LE_ADV_DIRECT_IND_LOW_DUTY (__DEPRECATED_MACRO 0x04)
+/* LE Advertising PDU Types. */
+#define BT_LE_ADV_SCAN_RSP (__DEPRECATED_MACRO 0x04)
+
+#define BT_HCI_ADV_IND 0x00
+#define BT_HCI_ADV_DIRECT_IND 0x01
+#define BT_HCI_ADV_SCAN_IND 0x02
+#define BT_HCI_ADV_NONCONN_IND 0x03
+#define BT_HCI_ADV_DIRECT_IND_LOW_DUTY 0x04
+#define BT_HCI_ADV_SCAN_RSP 0x04
+
+#define BT_LE_ADV_FP_NO_WHITELIST 0x00
+#define BT_LE_ADV_FP_WHITELIST_SCAN_REQ 0x01
+#define BT_LE_ADV_FP_WHITELIST_CONN_IND 0x02
+#define BT_LE_ADV_FP_WHITELIST_BOTH 0x03
+
+#define BT_HCI_OP_LE_SET_ADV_PARAM BT_OP(BT_OGF_LE, 0x0006)
+struct bt_hci_cp_le_set_adv_param {
+ uint16_t min_interval;
+ uint16_t max_interval;
+ uint8_t type;
+ uint8_t own_addr_type;
+ bt_addr_le_t direct_addr;
+ uint8_t channel_map;
+ uint8_t filter_policy;
+} __packed;
+
+#define BT_HCI_OP_LE_READ_ADV_CHAN_TX_POWER BT_OP(BT_OGF_LE, 0x0007)
+struct bt_hci_rp_le_read_chan_tx_power {
+ uint8_t status;
+ int8_t tx_power_level;
+} __packed;
+
+#define BT_HCI_OP_LE_SET_ADV_DATA BT_OP(BT_OGF_LE, 0x0008)
+struct bt_hci_cp_le_set_adv_data {
+ uint8_t len;
+ uint8_t data[31];
+} __packed;
+
+#define BT_HCI_OP_LE_SET_SCAN_RSP_DATA BT_OP(BT_OGF_LE, 0x0009)
+struct bt_hci_cp_le_set_scan_rsp_data {
+ uint8_t len;
+ uint8_t data[31];
+} __packed;
+
+#define BT_HCI_LE_ADV_DISABLE 0x00
+#define BT_HCI_LE_ADV_ENABLE 0x01
+
+#define BT_HCI_OP_LE_SET_ADV_ENABLE BT_OP(BT_OGF_LE, 0x000a)
+struct bt_hci_cp_le_set_adv_enable {
+ uint8_t enable;
+} __packed;
+
+/* Scan types */
+#define BT_HCI_OP_LE_SET_SCAN_PARAM BT_OP(BT_OGF_LE, 0x000b)
+#define BT_HCI_LE_SCAN_PASSIVE 0x00
+#define BT_HCI_LE_SCAN_ACTIVE 0x01
+
+#define BT_HCI_LE_SCAN_FP_NO_WHITELIST 0x00
+#define BT_HCI_LE_SCAN_FP_USE_WHITELIST 0x01
+
+struct bt_hci_cp_le_set_scan_param {
+ uint8_t scan_type;
+ uint16_t interval;
+ uint16_t window;
+ uint8_t addr_type;
+ uint8_t filter_policy;
+} __packed;
+
+#define BT_HCI_OP_LE_SET_SCAN_ENABLE BT_OP(BT_OGF_LE, 0x000c)
+
+#define BT_HCI_LE_SCAN_DISABLE 0x00
+#define BT_HCI_LE_SCAN_ENABLE 0x01
+
+#define BT_HCI_LE_SCAN_FILTER_DUP_DISABLE 0x00
+#define BT_HCI_LE_SCAN_FILTER_DUP_ENABLE 0x01
+
+struct bt_hci_cp_le_set_scan_enable {
+ uint8_t enable;
+ uint8_t filter_dup;
+} __packed;
+
+#define BT_HCI_OP_LE_CREATE_CONN BT_OP(BT_OGF_LE, 0x000d)
+
+#define BT_HCI_LE_CREATE_CONN_FP_DIRECT 0x00
+#define BT_HCI_LE_CREATE_CONN_FP_WHITELIST 0x01
+
+struct bt_hci_cp_le_create_conn {
+ uint16_t scan_interval;
+ uint16_t scan_window;
+ uint8_t filter_policy;
+ bt_addr_le_t peer_addr;
+ uint8_t own_addr_type;
+ uint16_t conn_interval_min;
+ uint16_t conn_interval_max;
+ uint16_t conn_latency;
+ uint16_t supervision_timeout;
+ uint16_t min_ce_len;
+ uint16_t max_ce_len;
+} __packed;
+
+#define BT_HCI_OP_LE_CREATE_CONN_CANCEL BT_OP(BT_OGF_LE, 0x000e)
+
+#define BT_HCI_OP_LE_READ_WL_SIZE BT_OP(BT_OGF_LE, 0x000f)
+struct bt_hci_rp_le_read_wl_size {
+ uint8_t status;
+ uint8_t wl_size;
+} __packed;
+
+#define BT_HCI_OP_LE_CLEAR_WL BT_OP(BT_OGF_LE, 0x0010)
+
+#define BT_HCI_OP_LE_ADD_DEV_TO_WL BT_OP(BT_OGF_LE, 0x0011)
+struct bt_hci_cp_le_add_dev_to_wl {
+ bt_addr_le_t addr;
+} __packed;
+
+#define BT_HCI_OP_LE_REM_DEV_FROM_WL BT_OP(BT_OGF_LE, 0x0012)
+struct bt_hci_cp_le_rem_dev_from_wl {
+ bt_addr_le_t addr;
+} __packed;
+
+#define BT_HCI_OP_LE_CONN_UPDATE BT_OP(BT_OGF_LE, 0x0013)
+struct hci_cp_le_conn_update {
+ uint16_t handle;
+ uint16_t conn_interval_min;
+ uint16_t conn_interval_max;
+ uint16_t conn_latency;
+ uint16_t supervision_timeout;
+ uint16_t min_ce_len;
+ uint16_t max_ce_len;
+} __packed;
+
+#define BT_HCI_OP_LE_SET_HOST_CHAN_CLASSIF BT_OP(BT_OGF_LE, 0x0014)
+struct bt_hci_cp_le_set_host_chan_classif {
+ uint8_t ch_map[5];
+} __packed;
+
+#define BT_HCI_OP_LE_READ_CHAN_MAP BT_OP(BT_OGF_LE, 0x0015)
+struct bt_hci_cp_le_read_chan_map {
+ uint16_t handle;
+} __packed;
+struct bt_hci_rp_le_read_chan_map {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t ch_map[5];
+} __packed;
+
+#define BT_HCI_OP_LE_READ_REMOTE_FEATURES BT_OP(BT_OGF_LE, 0x0016)
+struct bt_hci_cp_le_read_remote_features {
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_OP_LE_ENCRYPT BT_OP(BT_OGF_LE, 0x0017)
+struct bt_hci_cp_le_encrypt {
+ uint8_t key[16];
+ uint8_t plaintext[16];
+} __packed;
+struct bt_hci_rp_le_encrypt {
+ uint8_t status;
+ uint8_t enc_data[16];
+} __packed;
+
+#define BT_HCI_OP_LE_RAND BT_OP(BT_OGF_LE, 0x0018)
+struct bt_hci_rp_le_rand {
+ uint8_t status;
+ uint8_t rand[8];
+} __packed;
+
+#define BT_HCI_OP_LE_START_ENCRYPTION BT_OP(BT_OGF_LE, 0x0019)
+struct bt_hci_cp_le_start_encryption {
+ uint16_t handle;
+ uint64_t rand;
+ uint16_t ediv;
+ uint8_t ltk[16];
+} __packed;
+
+#define BT_HCI_OP_LE_LTK_REQ_REPLY BT_OP(BT_OGF_LE, 0x001a)
+struct bt_hci_cp_le_ltk_req_reply {
+ uint16_t handle;
+ uint8_t ltk[16];
+} __packed;
+struct bt_hci_rp_le_ltk_req_reply {
+ uint8_t status;
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_OP_LE_LTK_REQ_NEG_REPLY BT_OP(BT_OGF_LE, 0x001b)
+struct bt_hci_cp_le_ltk_req_neg_reply {
+ uint16_t handle;
+} __packed;
+struct bt_hci_rp_le_ltk_req_neg_reply {
+ uint8_t status;
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_OP_LE_READ_SUPP_STATES BT_OP(BT_OGF_LE, 0x001c)
+struct bt_hci_rp_le_read_supp_states {
+ uint8_t status;
+ uint8_t le_states[8];
+} __packed;
+
+#define BT_HCI_OP_LE_RX_TEST BT_OP(BT_OGF_LE, 0x001d)
+struct bt_hci_cp_le_rx_test {
+ uint8_t rx_ch;
+} __packed;
+
+#define BT_HCI_OP_LE_TX_TEST BT_OP(BT_OGF_LE, 0x001e)
+struct bt_hci_cp_le_tx_test {
+ uint8_t tx_ch;
+ uint8_t test_data_len;
+ uint8_t pkt_payload;
+} __packed;
+
+#define BT_HCI_OP_LE_TEST_END BT_OP(BT_OGF_LE, 0x001f)
+struct bt_hci_rp_le_test_end {
+ uint8_t status;
+ uint16_t rx_pkt_count;
+} __packed;
+
+#define BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY BT_OP(BT_OGF_LE, 0x0020)
+struct bt_hci_cp_le_conn_param_req_reply {
+ uint16_t handle;
+ uint16_t interval_min;
+ uint16_t interval_max;
+ uint16_t latency;
+ uint16_t timeout;
+ uint16_t min_ce_len;
+ uint16_t max_ce_len;
+} __packed;
+struct bt_hci_rp_le_conn_param_req_reply {
+ uint8_t status;
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY BT_OP(BT_OGF_LE, 0x0021)
+struct bt_hci_cp_le_conn_param_req_neg_reply {
+ uint16_t handle;
+ uint8_t reason;
+} __packed;
+struct bt_hci_rp_le_conn_param_req_neg_reply {
+ uint8_t status;
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_OP_LE_SET_DATA_LEN BT_OP(BT_OGF_LE, 0x0022)
+struct bt_hci_cp_le_set_data_len {
+ uint16_t handle;
+ uint16_t tx_octets;
+ uint16_t tx_time;
+} __packed;
+struct bt_hci_rp_le_set_data_len {
+ uint8_t status;
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_OP_LE_READ_DEFAULT_DATA_LEN BT_OP(BT_OGF_LE, 0x0023)
+struct bt_hci_rp_le_read_default_data_len {
+ uint8_t status;
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+} __packed;
+
+#define BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN BT_OP(BT_OGF_LE, 0x0024)
+struct bt_hci_cp_le_write_default_data_len {
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+} __packed;
+
+#define BT_HCI_OP_LE_P256_PUBLIC_KEY BT_OP(BT_OGF_LE, 0x0025)
+
+#define BT_HCI_OP_LE_GENERATE_DHKEY BT_OP(BT_OGF_LE, 0x0026)
+struct bt_hci_cp_le_generate_dhkey {
+ uint8_t key[64];
+} __packed;
+
+#define BT_HCI_OP_LE_ADD_DEV_TO_RL BT_OP(BT_OGF_LE, 0x0027)
+struct bt_hci_cp_le_add_dev_to_rl {
+ bt_addr_le_t peer_id_addr;
+ uint8_t peer_irk[16];
+ uint8_t local_irk[16];
+} __packed;
+
+#define BT_HCI_OP_LE_REM_DEV_FROM_RL BT_OP(BT_OGF_LE, 0x0028)
+struct bt_hci_cp_le_rem_dev_from_rl {
+ bt_addr_le_t peer_id_addr;
+} __packed;
+
+#define BT_HCI_OP_LE_CLEAR_RL BT_OP(BT_OGF_LE, 0x0029)
+
+#define BT_HCI_OP_LE_READ_RL_SIZE BT_OP(BT_OGF_LE, 0x002a)
+struct bt_hci_rp_le_read_rl_size {
+ uint8_t status;
+ uint8_t rl_size;
+} __packed;
+
+#define BT_HCI_OP_LE_READ_PEER_RPA BT_OP(BT_OGF_LE, 0x002b)
+struct bt_hci_cp_le_read_peer_rpa {
+ bt_addr_le_t peer_id_addr;
+} __packed;
+struct bt_hci_rp_le_read_peer_rpa {
+ uint8_t status;
+ bt_addr_t peer_rpa;
+} __packed;
+
+#define BT_HCI_OP_LE_READ_LOCAL_RPA BT_OP(BT_OGF_LE, 0x002c)
+struct bt_hci_cp_le_read_local_rpa {
+ bt_addr_le_t peer_id_addr;
+} __packed;
+struct bt_hci_rp_le_read_local_rpa {
+ uint8_t status;
+ bt_addr_t local_rpa;
+} __packed;
+
+#define BT_HCI_ADDR_RES_DISABLE 0x00
+#define BT_HCI_ADDR_RES_ENABLE 0x01
+
+#define BT_HCI_OP_LE_SET_ADDR_RES_ENABLE BT_OP(BT_OGF_LE, 0x002d)
+struct bt_hci_cp_le_set_addr_res_enable {
+ uint8_t enable;
+} __packed;
+
+#define BT_HCI_OP_LE_SET_RPA_TIMEOUT BT_OP(BT_OGF_LE, 0x002e)
+struct bt_hci_cp_le_set_rpa_timeout {
+ uint16_t rpa_timeout;
+} __packed;
+
+#define BT_HCI_OP_LE_READ_MAX_DATA_LEN BT_OP(BT_OGF_LE, 0x002f)
+struct bt_hci_rp_le_read_max_data_len {
+ uint8_t status;
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+ uint16_t max_rx_octets;
+ uint16_t max_rx_time;
+} __packed;
+
+#define BT_HCI_LE_PHY_1M 0x01
+#define BT_HCI_LE_PHY_2M 0x02
+#define BT_HCI_LE_PHY_CODED 0x03
+
+#define BT_HCI_OP_LE_READ_PHY BT_OP(BT_OGF_LE, 0x0030)
+struct bt_hci_cp_le_read_phy {
+ uint16_t handle;
+} __packed;
+struct bt_hci_rp_le_read_phy {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t tx_phy;
+ uint8_t rx_phy;
+} __packed;
+
+#define BT_HCI_LE_PHY_TX_ANY BIT(0)
+#define BT_HCI_LE_PHY_RX_ANY BIT(1)
+
+#define BT_HCI_LE_PHY_PREFER_1M BIT(0)
+#define BT_HCI_LE_PHY_PREFER_2M BIT(1)
+#define BT_HCI_LE_PHY_PREFER_CODED BIT(2)
+
+#define BT_HCI_OP_LE_SET_DEFAULT_PHY BT_OP(BT_OGF_LE, 0x0031)
+struct bt_hci_cp_le_set_default_phy {
+ uint8_t all_phys;
+ uint8_t tx_phys;
+ uint8_t rx_phys;
+} __packed;
+
+#define BT_HCI_LE_PHY_CODED_ANY 0x00
+#define BT_HCI_LE_PHY_CODED_S2 0x01
+#define BT_HCI_LE_PHY_CODED_S8 0x02
+
+#define BT_HCI_OP_LE_SET_PHY BT_OP(BT_OGF_LE, 0x0032)
+struct bt_hci_cp_le_set_phy {
+ uint16_t handle;
+ uint8_t all_phys;
+ uint8_t tx_phys;
+ uint8_t rx_phys;
+ uint16_t phy_opts;
+} __packed;
+
+#define BT_HCI_LE_MOD_INDEX_STANDARD 0x00
+#define BT_HCI_LE_MOD_INDEX_STABLE 0x01
+
+#define BT_HCI_OP_LE_ENH_RX_TEST BT_OP(BT_OGF_LE, 0x0033)
+struct bt_hci_cp_le_enh_rx_test {
+ uint8_t rx_ch;
+ uint8_t phy;
+ uint8_t mod_index;
+} __packed;
+
+/* Extends BT_HCI_LE_PHY */
+#define BT_HCI_LE_TX_PHY_CODED_S8 0x03
+#define BT_HCI_LE_TX_PHY_CODED_S2 0x04
+
+#define BT_HCI_OP_LE_ENH_TX_TEST BT_OP(BT_OGF_LE, 0x0034)
+struct bt_hci_cp_le_enh_tx_test {
+ uint8_t tx_ch;
+ uint8_t test_data_len;
+ uint8_t pkt_payload;
+ uint8_t phy;
+} __packed;
+
+#define BT_HCI_OP_LE_SET_ADV_SET_RANDOM_ADDR BT_OP(BT_OGF_LE, 0x0035)
+struct bt_hci_cp_le_set_adv_set_random_addr {
+ uint8_t handle;
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_LE_ADV_PROP_CONN BIT(0)
+#define BT_HCI_LE_ADV_PROP_SCAN BIT(1)
+#define BT_HCI_LE_ADV_PROP_DIRECT BIT(2)
+#define BT_HCI_LE_ADV_PROP_HI_DC_CONN BIT(3)
+#define BT_HCI_LE_ADV_PROP_LEGACY BIT(4)
+#define BT_HCI_LE_ADV_PROP_ANON BIT(5)
+#define BT_HCI_LE_ADV_PROP_TX_POWER BIT(6)
+
+#define BT_HCI_LE_ADV_SCAN_REQ_ENABLE 1
+#define BT_HCI_LE_ADV_SCAN_REQ_DISABLE 0
+
+#define BT_HCI_LE_ADV_TX_POWER_NO_PREF 0x7F
+
+#define BT_HCI_OP_LE_SET_EXT_ADV_PARAM BT_OP(BT_OGF_LE, 0x0036)
+struct bt_hci_cp_le_set_ext_adv_param {
+ uint8_t handle;
+ uint16_t props;
+ uint8_t prim_min_interval[3];
+ uint8_t prim_max_interval[3];
+ uint8_t prim_channel_map;
+ uint8_t own_addr_type;
+ bt_addr_le_t peer_addr;
+ uint8_t filter_policy;
+ int8_t tx_power;
+ uint8_t prim_adv_phy;
+ uint8_t sec_adv_max_skip;
+ uint8_t sec_adv_phy;
+ uint8_t sid;
+ uint8_t scan_req_notify_enable;
+} __packed;
+struct bt_hci_rp_le_set_ext_adv_param {
+ uint8_t status;
+ int8_t tx_power;
+} __packed;
+
+#define BT_HCI_LE_EXT_ADV_OP_INTERM_FRAG 0x00
+#define BT_HCI_LE_EXT_ADV_OP_FIRST_FRAG 0x01
+#define BT_HCI_LE_EXT_ADV_OP_LAST_FRAG 0x02
+#define BT_HCI_LE_EXT_ADV_OP_COMPLETE_DATA 0x03
+#define BT_HCI_LE_EXT_ADV_OP_UNCHANGED_DATA 0x04
+
+#define BT_HCI_LE_EXT_ADV_FRAG_ENABLED 0x00
+#define BT_HCI_LE_EXT_ADV_FRAG_DISABLED 0x01
+
+#define BT_HCI_LE_EXT_ADV_FRAG_MAX_LEN 251
+
+#define BT_HCI_OP_LE_SET_EXT_ADV_DATA BT_OP(BT_OGF_LE, 0x0037)
+struct bt_hci_cp_le_set_ext_adv_data {
+ uint8_t handle;
+ uint8_t op;
+ uint8_t frag_pref;
+ uint8_t len;
+ uint8_t data[251];
+} __packed;
+
+#define BT_HCI_OP_LE_SET_EXT_SCAN_RSP_DATA BT_OP(BT_OGF_LE, 0x0038)
+struct bt_hci_cp_le_set_ext_scan_rsp_data {
+ uint8_t handle;
+ uint8_t op;
+ uint8_t frag_pref;
+ uint8_t len;
+ uint8_t data[251];
+} __packed;
+
+#define BT_HCI_OP_LE_SET_EXT_ADV_ENABLE BT_OP(BT_OGF_LE, 0x0039)
+struct bt_hci_ext_adv_set {
+ uint8_t handle;
+ uint16_t duration;
+ uint8_t max_ext_adv_evts;
+} __packed;
+
+struct bt_hci_cp_le_set_ext_adv_enable {
+ uint8_t enable;
+ uint8_t set_num;
+ struct bt_hci_ext_adv_set s[];
+} __packed;
+
+#define BT_HCI_OP_LE_READ_MAX_ADV_DATA_LEN BT_OP(BT_OGF_LE, 0x003a)
+struct bt_hci_rp_le_read_max_adv_data_len {
+ uint8_t status;
+ uint16_t max_adv_data_len;
+} __packed;
+
+#define BT_HCI_OP_LE_READ_NUM_ADV_SETS BT_OP(BT_OGF_LE, 0x003b)
+struct bt_hci_rp_le_read_num_adv_sets {
+ uint8_t status;
+ uint8_t num_sets;
+} __packed;
+
+#define BT_HCI_OP_LE_REMOVE_ADV_SET BT_OP(BT_OGF_LE, 0x003c)
+struct bt_hci_cp_le_remove_adv_set {
+ uint8_t handle;
+} __packed;
+
+#define BT_HCI_OP_CLEAR_ADV_SETS BT_OP(BT_OGF_LE, 0x003d)
+
+#define BT_HCI_OP_LE_SET_PER_ADV_PARAM BT_OP(BT_OGF_LE, 0x003e)
+struct bt_hci_cp_le_set_per_adv_param {
+ uint8_t handle;
+ uint16_t min_interval;
+ uint16_t max_interval;
+ uint16_t props;
+} __packed;
+
+#define BT_HCI_OP_LE_SET_PER_ADV_DATA BT_OP(BT_OGF_LE, 0x003f)
+struct bt_hci_cp_le_set_per_adv_data {
+ uint8_t handle;
+ uint8_t op;
+ uint8_t len;
+ uint8_t data[251];
+} __packed;
+
+#define BT_HCI_OP_LE_SET_PER_ADV_ENABLE BT_OP(BT_OGF_LE, 0x0040)
+struct bt_hci_cp_le_set_per_adv_enable {
+ uint8_t enable;
+ uint8_t handle;
+} __packed;
+
+#define BT_HCI_OP_LE_SET_EXT_SCAN_PARAM BT_OP(BT_OGF_LE, 0x0041)
+struct bt_hci_ext_scan_phy {
+ uint8_t type;
+ uint16_t interval;
+ uint16_t window;
+} __packed;
+
+#define BT_HCI_LE_EXT_SCAN_PHY_1M BIT(0)
+#define BT_HCI_LE_EXT_SCAN_PHY_2M BIT(1)
+#define BT_HCI_LE_EXT_SCAN_PHY_CODED BIT(2)
+
+struct bt_hci_cp_le_set_ext_scan_param {
+ uint8_t own_addr_type;
+ uint8_t filter_policy;
+ uint8_t phys;
+ struct bt_hci_ext_scan_phy p[];
+} __packed;
+
+/* Extends BT_HCI_LE_SCAN_FILTER_DUP */
+#define BT_HCI_LE_EXT_SCAN_FILTER_DUP_ENABLE_RESET 0x02
+
+#define BT_HCI_OP_LE_SET_EXT_SCAN_ENABLE BT_OP(BT_OGF_LE, 0x0042)
+struct bt_hci_cp_le_set_ext_scan_enable {
+ uint8_t enable;
+ uint8_t filter_dup;
+ uint16_t duration;
+ uint16_t period;
+} __packed;
+
+#define BT_HCI_OP_LE_EXT_CREATE_CONN BT_OP(BT_OGF_LE, 0x0043)
+struct bt_hci_ext_conn_phy {
+ uint16_t scan_interval;
+ uint16_t scan_window;
+ uint16_t conn_interval_min;
+ uint16_t conn_interval_max;
+ uint16_t conn_latency;
+ uint16_t supervision_timeout;
+ uint16_t min_ce_len;
+ uint16_t max_ce_len;
+} __packed;
+
+struct bt_hci_cp_le_ext_create_conn {
+ uint8_t filter_policy;
+ uint8_t own_addr_type;
+ bt_addr_le_t peer_addr;
+ uint8_t phys;
+ struct bt_hci_ext_conn_phy p[];
+} __packed;
+
+#define BT_HCI_OP_LE_PER_ADV_CREATE_SYNC BT_OP(BT_OGF_LE, 0x0044)
+struct bt_hci_cp_le_per_adv_create_sync {
+ uint8_t filter_policy;
+ uint8_t sid;
+ bt_addr_le_t addr;
+ uint16_t skip;
+ uint16_t sync_timeout;
+ uint8_t unused;
+} __packed;
+
+#define BT_HCI_OP_LE_PER_ADV_CREATE_SYNC_CANCEL BT_OP(BT_OGF_LE, 0x0045)
+
+#define BT_HCI_OP_LE_PER_ADV_TERMINATE_SYNC BT_OP(BT_OGF_LE, 0x0046)
+struct bt_hci_cp_le_per_adv_terminate_sync {
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_OP_LE_ADD_DEV_TO_PER_ADV_LIST BT_OP(BT_OGF_LE, 0x0047)
+struct bt_hci_cp_le_add_dev_to_per_adv_list {
+ bt_addr_le_t addr;
+ uint8_t sid;
+} __packed;
+
+#define BT_HCI_OP_LE_REM_DEV_FROM_PER_ADV_LIST BT_OP(BT_OGF_LE, 0x0048)
+struct bt_hci_cp_le_rem_dev_from_per_adv_list {
+ bt_addr_le_t addr;
+ uint8_t sid;
+} __packed;
+
+#define BT_HCI_OP_LE_CLEAR_PER_ADV_LIST BT_OP(BT_OGF_LE, 0x0049)
+
+#define BT_HCI_OP_LE_READ_PER_ADV_LIST_SIZE BT_OP(BT_OGF_LE, 0x004a)
+struct bt_hci_rp_le_read_per_adv_list_size {
+ uint8_t status;
+ uint8_t list_size;
+} __packed;
+
+#define BT_HCI_OP_LE_READ_TX_POWER BT_OP(BT_OGF_LE, 0x004b)
+struct bt_hci_rp_le_read_tx_power {
+ uint8_t status;
+ int8_t min_tx_power;
+ int8_t max_tx_power;
+} __packed;
+
+#define BT_HCI_OP_LE_READ_RF_PATH_COMP BT_OP(BT_OGF_LE, 0x004c)
+struct bt_hci_rp_le_read_rf_path_comp {
+ uint8_t status;
+ int16_t tx_path_comp;
+ int16_t rx_path_comp;
+} __packed;
+
+#define BT_HCI_OP_LE_WRITE_RF_PATH_COMP BT_OP(BT_OGF_LE, 0x004d)
+struct bt_hci_cp_le_write_rf_path_comp {
+ int16_t tx_path_comp;
+ int16_t rx_path_comp;
+} __packed;
+
+#define BT_HCI_LE_PRIVACY_MODE_NETWORK 0x00
+#define BT_HCI_LE_PRIVACY_MODE_DEVICE 0x01
+
+#define BT_HCI_OP_LE_SET_PRIVACY_MODE BT_OP(BT_OGF_LE, 0x004e)
+struct bt_hci_cp_le_set_privacy_mode {
+ bt_addr_le_t id_addr;
+ uint8_t mode;
+} __packed;
+
+/* Event definitions */
+
+#define BT_HCI_EVT_UNKNOWN 0x00
+#define BT_HCI_EVT_VENDOR 0xff
+
+#define BT_HCI_EVT_INQUIRY_COMPLETE 0x01
+struct bt_hci_evt_inquiry_complete {
+ uint8_t status;
+} __packed;
+
+#define BT_HCI_EVT_CONN_COMPLETE 0x03
+struct bt_hci_evt_conn_complete {
+ uint8_t status;
+ uint16_t handle;
+ bt_addr_t bdaddr;
+ uint8_t link_type;
+ uint8_t encr_enabled;
+} __packed;
+
+#define BT_HCI_EVT_CONN_REQUEST 0x04
+struct bt_hci_evt_conn_request {
+ bt_addr_t bdaddr;
+ uint8_t dev_class[3];
+ uint8_t link_type;
+} __packed;
+
+#define BT_HCI_EVT_DISCONN_COMPLETE 0x05
+struct bt_hci_evt_disconn_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t reason;
+} __packed;
+
+#define BT_HCI_EVT_AUTH_COMPLETE 0x06
+struct bt_hci_evt_auth_complete {
+ uint8_t status;
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_EVT_REMOTE_NAME_REQ_COMPLETE 0x07
+struct bt_hci_evt_remote_name_req_complete {
+ uint8_t status;
+ bt_addr_t bdaddr;
+ uint8_t name[248];
+} __packed;
+
+#define BT_HCI_EVT_ENCRYPT_CHANGE 0x08
+struct bt_hci_evt_encrypt_change {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t encrypt;
+} __packed;
+
+#define BT_HCI_EVT_REMOTE_FEATURES 0x0b
+struct bt_hci_evt_remote_features {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t features[8];
+} __packed;
+
+#define BT_HCI_EVT_REMOTE_VERSION_INFO 0x0c
+struct bt_hci_evt_remote_version_info {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t version;
+ uint16_t manufacturer;
+ uint16_t subversion;
+} __packed;
+
+#define BT_HCI_EVT_CMD_COMPLETE 0x0e
+struct bt_hci_evt_cmd_complete {
+ uint8_t ncmd;
+ uint16_t opcode;
+} __packed;
+
+struct bt_hci_evt_cc_status {
+ uint8_t status;
+} __packed;
+
+#define BT_HCI_EVT_CMD_STATUS 0x0f
+struct bt_hci_evt_cmd_status {
+ uint8_t status;
+ uint8_t ncmd;
+ uint16_t opcode;
+} __packed;
+
+#define BT_HCI_EVT_ROLE_CHANGE 0x12
+struct bt_hci_evt_role_change {
+ uint8_t status;
+ bt_addr_t bdaddr;
+ uint8_t role;
+} __packed;
+
+#define BT_HCI_EVT_NUM_COMPLETED_PACKETS 0x13
+struct bt_hci_evt_num_completed_packets {
+ uint8_t num_handles;
+ struct bt_hci_handle_count h[];
+} __packed;
+
+#define BT_HCI_EVT_PIN_CODE_REQ 0x16
+struct bt_hci_evt_pin_code_req {
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_EVT_LINK_KEY_REQ 0x17
+struct bt_hci_evt_link_key_req {
+ bt_addr_t bdaddr;
+} __packed;
+
+/* Link Key types */
+#define BT_LK_COMBINATION 0x00
+#define BT_LK_LOCAL_UNIT 0x01
+#define BT_LK_REMOTE_UNIT 0x02
+#define BT_LK_DEBUG_COMBINATION 0x03
+#define BT_LK_UNAUTH_COMBINATION_P192 0x04
+#define BT_LK_AUTH_COMBINATION_P192 0x05
+#define BT_LK_CHANGED_COMBINATION 0x06
+#define BT_LK_UNAUTH_COMBINATION_P256 0x07
+#define BT_LK_AUTH_COMBINATION_P256 0x08
+
+#define BT_HCI_EVT_LINK_KEY_NOTIFY 0x18
+struct bt_hci_evt_link_key_notify {
+ bt_addr_t bdaddr;
+ uint8_t link_key[16];
+ uint8_t key_type;
+} __packed;
+
+/* Overflow link types */
+#define BT_OVERFLOW_LINK_SYNCH 0x00
+#define BT_OVERFLOW_LINK_ACL 0x01
+
+#define BT_HCI_EVT_DATA_BUF_OVERFLOW 0x1a
+struct bt_hci_evt_data_buf_overflow {
+ uint8_t link_type;
+} __packed;
+
+#define BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI 0x22
+struct bt_hci_evt_inquiry_result_with_rssi {
+ bt_addr_t addr;
+ uint8_t pscan_rep_mode;
+ uint8_t reserved;
+ uint8_t cod[3];
+ uint16_t clock_offset;
+ int8_t rssi;
+} __packed;
+
+#define BT_HCI_EVT_REMOTE_EXT_FEATURES 0x23
+struct bt_hci_evt_remote_ext_features {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t page;
+ uint8_t max_page;
+ uint8_t features[8];
+} __packed;
+
+#define BT_HCI_EVT_SYNC_CONN_COMPLETE 0x2c
+struct bt_hci_evt_sync_conn_complete {
+ uint8_t status;
+ uint16_t handle;
+ bt_addr_t bdaddr;
+ uint8_t link_type;
+ uint8_t tx_interval;
+ uint8_t retansmission_window;
+ uint16_t rx_pkt_length;
+ uint16_t tx_pkt_length;
+ uint8_t air_mode;
+} __packed;
+
+#define BT_HCI_EVT_EXTENDED_INQUIRY_RESULT 0x2f
+struct bt_hci_evt_extended_inquiry_result {
+ uint8_t num_reports;
+ bt_addr_t addr;
+ uint8_t pscan_rep_mode;
+ uint8_t reserved;
+ uint8_t cod[3];
+ uint16_t clock_offset;
+ int8_t rssi;
+ uint8_t eir[240];
+} __packed;
+
+#define BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE 0x30
+struct bt_hci_evt_encrypt_key_refresh_complete {
+ uint8_t status;
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_EVT_IO_CAPA_REQ 0x31
+struct bt_hci_evt_io_capa_req {
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_EVT_IO_CAPA_RESP 0x32
+struct bt_hci_evt_io_capa_resp {
+ bt_addr_t bdaddr;
+ uint8_t capability;
+ uint8_t oob_data;
+ uint8_t authentication;
+} __packed;
+
+#define BT_HCI_EVT_USER_CONFIRM_REQ 0x33
+struct bt_hci_evt_user_confirm_req {
+ bt_addr_t bdaddr;
+ uint32_t passkey;
+} __packed;
+
+#define BT_HCI_EVT_USER_PASSKEY_REQ 0x34
+struct bt_hci_evt_user_passkey_req {
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_EVT_SSP_COMPLETE 0x36
+struct bt_hci_evt_ssp_complete {
+ uint8_t status;
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_EVT_USER_PASSKEY_NOTIFY 0x3b
+struct bt_hci_evt_user_passkey_notify {
+ bt_addr_t bdaddr;
+ uint32_t passkey;
+} __packed;
+
+#define BT_HCI_EVT_LE_META_EVENT 0x3e
+struct bt_hci_evt_le_meta_event {
+ uint8_t subevent;
+} __packed;
+
+#define BT_HCI_EVT_AUTH_PAYLOAD_TIMEOUT_EXP 0x57
+struct bt_hci_evt_auth_payload_timeout_exp {
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_ROLE_MASTER 0x00
+#define BT_HCI_ROLE_SLAVE 0x01
+
+#define BT_HCI_EVT_LE_CONN_COMPLETE 0x01
+struct bt_hci_evt_le_conn_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t role;
+ bt_addr_le_t peer_addr;
+ uint16_t interval;
+ uint16_t latency;
+ uint16_t supv_timeout;
+ uint8_t clock_accuracy;
+} __packed;
+
+#define BT_HCI_EVT_LE_ADVERTISING_REPORT 0x02
+struct bt_hci_evt_le_advertising_info {
+ uint8_t evt_type;
+ bt_addr_le_t addr;
+ uint8_t length;
+ uint8_t data[];
+} __packed;
+struct bt_hci_evt_le_advertising_report {
+ uint8_t num_reports;
+ struct bt_hci_evt_le_advertising_info adv_info[];
+} __packed;
+
+#define BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE 0x03
+struct bt_hci_evt_le_conn_update_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t interval;
+ uint16_t latency;
+ uint16_t supv_timeout;
+} __packed;
+
+#define BT_HCI_EV_LE_REMOTE_FEAT_COMPLETE 0x04
+struct bt_hci_evt_le_remote_feat_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t features[8];
+} __packed;
+
+#define BT_HCI_EVT_LE_LTK_REQUEST 0x05
+struct bt_hci_evt_le_ltk_request {
+ uint16_t handle;
+ uint64_t rand;
+ uint16_t ediv;
+} __packed;
+
+#define BT_HCI_EVT_LE_CONN_PARAM_REQ 0x06
+struct bt_hci_evt_le_conn_param_req {
+ uint16_t handle;
+ uint16_t interval_min;
+ uint16_t interval_max;
+ uint16_t latency;
+ uint16_t timeout;
+} __packed;
+
+#define BT_HCI_EVT_LE_DATA_LEN_CHANGE 0x07
+struct bt_hci_evt_le_data_len_change {
+ uint16_t handle;
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+ uint16_t max_rx_octets;
+ uint16_t max_rx_time;
+} __packed;
+
+#define BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE 0x08
+struct bt_hci_evt_le_p256_public_key_complete {
+ uint8_t status;
+ uint8_t key[64];
+} __packed;
+
+#define BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE 0x09
+struct bt_hci_evt_le_generate_dhkey_complete {
+ uint8_t status;
+ uint8_t dhkey[32];
+} __packed;
+
+#define BT_HCI_EVT_LE_ENH_CONN_COMPLETE 0x0a
+struct bt_hci_evt_le_enh_conn_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t role;
+ bt_addr_le_t peer_addr;
+ bt_addr_t local_rpa;
+ bt_addr_t peer_rpa;
+ uint16_t interval;
+ uint16_t latency;
+ uint16_t supv_timeout;
+ uint8_t clock_accuracy;
+} __packed;
+
+#define BT_HCI_EVT_LE_DIRECT_ADV_REPORT 0x0b
+struct bt_hci_evt_le_direct_adv_info {
+ uint8_t evt_type;
+ bt_addr_le_t addr;
+ bt_addr_le_t dir_addr;
+ int8_t rssi;
+} __packed;
+struct bt_hci_evt_le_direct_adv_report {
+ uint8_t num_reports;
+ struct bt_hci_evt_le_direct_adv_info direct_adv_info[];
+} __packed;
+
+#define BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE 0x0c
+struct bt_hci_evt_le_phy_update_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t tx_phy;
+ uint8_t rx_phy;
+} __packed;
+
+#define BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT 0x0d
+
+#define BT_HCI_LE_ADV_EVT_TYPE_CONN BIT(0)
+#define BT_HCI_LE_ADV_EVT_TYPE_SCAN BIT(1)
+#define BT_HCI_LE_ADV_EVT_TYPE_DIRECT BIT(2)
+#define BT_HCI_LE_ADV_EVT_TYPE_SCAN_RSP BIT(3)
+#define BT_HCI_LE_ADV_EVT_TYPE_LEGACY BIT(4)
+
+#define BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS(ev_type) (((ev_type) >> 5) & 0x03)
+#define BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE 0
+#define BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_PARTIAL 1
+#define BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE 2
+
+struct bt_hci_evt_le_ext_advertising_info {
+ uint16_t evt_type;
+ bt_addr_le_t addr;
+ uint8_t prim_phy;
+ uint8_t sec_phy;
+ uint8_t sid;
+ int8_t tx_power;
+ int8_t rssi;
+ uint16_t interval;
+ bt_addr_le_t direct_addr;
+ uint8_t length;
+ uint8_t data[];
+} __packed;
+struct bt_hci_evt_le_ext_advertising_report {
+ uint8_t num_reports;
+ struct bt_hci_evt_le_ext_advertising_info adv_info[];
+} __packed;
+
+#define BT_HCI_EVT_LE_PER_ADV_SYNC_ESTABLISHED 0x0e
+struct bt_hci_evt_le_per_adv_sync_established {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t sid;
+ bt_addr_le_t adv_addr;
+ uint8_t phy;
+ uint16_t interval;
+ uint8_t clock_accuracy;
+} __packed;
+
+#define BT_HCI_EVT_LE_PER_ADVERTISING_REPORT 0x0f
+struct bt_hci_evt_le_per_advertising_report {
+ uint16_t handle;
+ int8_t tx_power;
+ int8_t rssi;
+ uint8_t unused;
+ uint8_t data_status;
+ uint8_t length;
+ uint8_t data[];
+} __packed;
+
+#define BT_HCI_EVT_LE_PER_ADV_SYNC_LOST 0x10
+struct bt_hci_evt_le_per_adv_sync_lost {
+ uint16_t handle;
+} __packed;
+
+#define BT_HCI_EVT_LE_SCAN_TIMEOUT 0x11
+
+#define BT_HCI_EVT_LE_ADV_SET_TERMINATED 0x12
+struct bt_hci_evt_le_adv_set_terminated {
+ uint8_t status;
+ uint8_t adv_handle;
+ uint16_t conn_handle;
+ uint8_t num_completed_ext_adv_evts;
+} __packed;
+
+#define BT_HCI_EVT_LE_SCAN_REQ_RECEIVED 0x13
+struct bt_hci_evt_le_scan_req_received {
+ uint8_t handle;
+ bt_addr_le_t addr;
+} __packed;
+
+#define BT_HCI_LE_CHAN_SEL_ALGO_1 0x00
+#define BT_HCI_LE_CHAN_SEL_ALGO_2 0x01
+
+#define BT_HCI_EVT_LE_CHAN_SEL_ALGO 0x14
+struct bt_hci_evt_le_chan_sel_algo {
+ uint16_t handle;
+ uint8_t chan_sel_algo;
+} __packed;
+
+/* Event mask bits */
+
+#define BT_EVT_BIT(n) (1ULL << (n))
+
+#define BT_EVT_MASK_INQUIRY_COMPLETE BT_EVT_BIT(0)
+#define BT_EVT_MASK_CONN_COMPLETE BT_EVT_BIT(2)
+#define BT_EVT_MASK_CONN_REQUEST BT_EVT_BIT(3)
+#define BT_EVT_MASK_DISCONN_COMPLETE BT_EVT_BIT(4)
+#define BT_EVT_MASK_AUTH_COMPLETE BT_EVT_BIT(5)
+#define BT_EVT_MASK_REMOTE_NAME_REQ_COMPLETE BT_EVT_BIT(6)
+#define BT_EVT_MASK_ENCRYPT_CHANGE BT_EVT_BIT(7)
+#define BT_EVT_MASK_REMOTE_FEATURES BT_EVT_BIT(10)
+#define BT_EVT_MASK_REMOTE_VERSION_INFO BT_EVT_BIT(11)
+#define BT_EVT_MASK_HARDWARE_ERROR BT_EVT_BIT(15)
+#define BT_EVT_MASK_ROLE_CHANGE BT_EVT_BIT(17)
+#define BT_EVT_MASK_PIN_CODE_REQ BT_EVT_BIT(21)
+#define BT_EVT_MASK_LINK_KEY_REQ BT_EVT_BIT(22)
+#define BT_EVT_MASK_LINK_KEY_NOTIFY BT_EVT_BIT(23)
+#define BT_EVT_MASK_DATA_BUFFER_OVERFLOW BT_EVT_BIT(25)
+#define BT_EVT_MASK_INQUIRY_RESULT_WITH_RSSI BT_EVT_BIT(33)
+#define BT_EVT_MASK_REMOTE_EXT_FEATURES BT_EVT_BIT(34)
+#define BT_EVT_MASK_SYNC_CONN_COMPLETE BT_EVT_BIT(43)
+#define BT_EVT_MASK_EXTENDED_INQUIRY_RESULT BT_EVT_BIT(46)
+#define BT_EVT_MASK_ENCRYPT_KEY_REFRESH_COMPLETE BT_EVT_BIT(47)
+#define BT_EVT_MASK_IO_CAPA_REQ BT_EVT_BIT(48)
+#define BT_EVT_MASK_IO_CAPA_RESP BT_EVT_BIT(49)
+#define BT_EVT_MASK_USER_CONFIRM_REQ BT_EVT_BIT(50)
+#define BT_EVT_MASK_USER_PASSKEY_REQ BT_EVT_BIT(51)
+#define BT_EVT_MASK_SSP_COMPLETE BT_EVT_BIT(53)
+#define BT_EVT_MASK_USER_PASSKEY_NOTIFY BT_EVT_BIT(58)
+#define BT_EVT_MASK_LE_META_EVENT BT_EVT_BIT(61)
+
+/* Page 2 */
+#define BT_EVT_MASK_PHY_LINK_COMPLETE BT_EVT_BIT(0)
+#define BT_EVT_MASK_CH_SELECTED_COMPLETE BT_EVT_BIT(1)
+#define BT_EVT_MASK_DISCONN_PHY_LINK_COMPLETE BT_EVT_BIT(2)
+#define BT_EVT_MASK_PHY_LINK_LOSS_EARLY_WARN BT_EVT_BIT(3)
+#define BT_EVT_MASK_PHY_LINK_RECOVERY BT_EVT_BIT(4)
+#define BT_EVT_MASK_LOG_LINK_COMPLETE BT_EVT_BIT(5)
+#define BT_EVT_MASK_DISCONN_LOG_LINK_COMPLETE BT_EVT_BIT(6)
+#define BT_EVT_MASK_FLOW_SPEC_MODIFY_COMPLETE BT_EVT_BIT(7)
+#define BT_EVT_MASK_NUM_COMPLETE_DATA_BLOCKS BT_EVT_BIT(8)
+#define BT_EVT_MASK_AMP_START_TEST BT_EVT_BIT(9)
+#define BT_EVT_MASK_AMP_TEST_END BT_EVT_BIT(10)
+#define BT_EVT_MASK_AMP_RX_REPORT BT_EVT_BIT(11)
+#define BT_EVT_MASK_AMP_SR_MODE_CHANGE_COMPLETE BT_EVT_BIT(12)
+#define BT_EVT_MASK_AMP_STATUS_CHANGE BT_EVT_BIT(13)
+#define BT_EVT_MASK_TRIGG_CLOCK_CAPTURE BT_EVT_BIT(14)
+#define BT_EVT_MASK_SYNCH_TRAIN_COMPLETE BT_EVT_BIT(15)
+#define BT_EVT_MASK_SYNCH_TRAIN_RX BT_EVT_BIT(16)
+#define BT_EVT_MASK_CL_SLAVE_BC_RX BT_EVT_BIT(17)
+#define BT_EVT_MASK_CL_SLAVE_BC_TIMEOUT BT_EVT_BIT(18)
+#define BT_EVT_MASK_TRUNC_PAGE_COMPLETE BT_EVT_BIT(19)
+#define BT_EVT_MASK_SLAVE_PAGE_RSP_TIMEOUT BT_EVT_BIT(20)
+#define BT_EVT_MASK_CL_SLAVE_BC_CH_MAP_CHANGE BT_EVT_BIT(21)
+#define BT_EVT_MASK_INQUIRY_RSP_NOT BT_EVT_BIT(22)
+#define BT_EVT_MASK_AUTH_PAYLOAD_TIMEOUT_EXP BT_EVT_BIT(23)
+#define BT_EVT_MASK_SAM_STATUS_CHANGE BT_EVT_BIT(24)
+
+#define BT_EVT_MASK_LE_CONN_COMPLETE BT_EVT_BIT(0)
+#define BT_EVT_MASK_LE_ADVERTISING_REPORT BT_EVT_BIT(1)
+#define BT_EVT_MASK_LE_CONN_UPDATE_COMPLETE BT_EVT_BIT(2)
+#define BT_EVT_MASK_LE_REMOTE_FEAT_COMPLETE BT_EVT_BIT(3)
+#define BT_EVT_MASK_LE_LTK_REQUEST BT_EVT_BIT(4)
+#define BT_EVT_MASK_LE_CONN_PARAM_REQ BT_EVT_BIT(5)
+#define BT_EVT_MASK_LE_DATA_LEN_CHANGE BT_EVT_BIT(6)
+#define BT_EVT_MASK_LE_P256_PUBLIC_KEY_COMPLETE BT_EVT_BIT(7)
+#define BT_EVT_MASK_LE_GENERATE_DHKEY_COMPLETE BT_EVT_BIT(8)
+#define BT_EVT_MASK_LE_ENH_CONN_COMPLETE BT_EVT_BIT(9)
+#define BT_EVT_MASK_LE_DIRECT_ADV_REPORT BT_EVT_BIT(10)
+#define BT_EVT_MASK_LE_PHY_UPDATE_COMPLETE BT_EVT_BIT(11)
+#define BT_EVT_MASK_LE_EXT_ADVERTISING_REPORT BT_EVT_BIT(12)
+#define BT_EVT_MASK_LE_PER_ADV_SYNC_ESTABLISHED BT_EVT_BIT(13)
+#define BT_EVT_MASK_LE_PER_ADVERTISING_REPORT BT_EVT_BIT(14)
+#define BT_EVT_MASK_LE_PER_ADV_SYNC_LOST BT_EVT_BIT(15)
+#define BT_EVT_MASK_LE_SCAN_TIMEOUT BT_EVT_BIT(16)
+#define BT_EVT_MASK_LE_ADV_SET_TERMINATED BT_EVT_BIT(17)
+#define BT_EVT_MASK_LE_SCAN_REQ_RECEIVED BT_EVT_BIT(18)
+#define BT_EVT_MASK_LE_CHAN_SEL_ALGO BT_EVT_BIT(19)
+
+//
+
+#endif /* ZEPHYR_INCLUDE_BLUETOOTH_HCI_H_ */
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_err.h b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_err.h
new file mode 100644
index 0000000..476d3f4
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_err.h
@@ -0,0 +1,92 @@
+/** @file
+ * @brief Bluetooth Host Control Interface status codes.
+ */
+
+/*
+ * Copyright (c) 2019 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef ZEPHYR_INCLUDE_BLUETOOTH_HCI_STATUS_H_
+#define ZEPHYR_INCLUDE_BLUETOOTH_HCI_STATUS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** HCI Error Codes, BT Core Spec v5.2 [Vol 1, Part F]. */
+#define BT_HCI_ERR_SUCCESS 0x00
+#define BT_HCI_ERR_UNKNOWN_CMD 0x01
+#define BT_HCI_ERR_UNKNOWN_CONN_ID 0x02
+#define BT_HCI_ERR_HW_FAILURE 0x03
+#define BT_HCI_ERR_PAGE_TIMEOUT 0x04
+#define BT_HCI_ERR_AUTH_FAIL 0x05
+#define BT_HCI_ERR_PIN_OR_KEY_MISSING 0x06
+#define BT_HCI_ERR_MEM_CAPACITY_EXCEEDED 0x07
+#define BT_HCI_ERR_CONN_TIMEOUT 0x08
+#define BT_HCI_ERR_CONN_LIMIT_EXCEEDED 0x09
+#define BT_HCI_ERR_SYNC_CONN_LIMIT_EXCEEDED 0x0a
+#define BT_HCI_ERR_CONN_ALREADY_EXISTS 0x0b
+#define BT_HCI_ERR_CMD_DISALLOWED 0x0c
+#define BT_HCI_ERR_INSUFFICIENT_RESOURCES 0x0d
+#define BT_HCI_ERR_INSUFFICIENT_SECURITY 0x0e
+#define BT_HCI_ERR_BD_ADDR_UNACCEPTABLE 0x0f
+#define BT_HCI_ERR_CONN_ACCEPT_TIMEOUT 0x10
+#define BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL 0x11
+#define BT_HCI_ERR_INVALID_PARAM 0x12
+#define BT_HCI_ERR_REMOTE_USER_TERM_CONN 0x13
+#define BT_HCI_ERR_REMOTE_LOW_RESOURCES 0x14
+#define BT_HCI_ERR_REMOTE_POWER_OFF 0x15
+#define BT_HCI_ERR_LOCALHOST_TERM_CONN 0x16
+#define BT_HCI_ERR_REPEATED_ATTEMPTS 0x17
+#define BT_HCI_ERR_PAIRING_NOT_ALLOWED 0x18
+#define BT_HCI_ERR_UNKNOWN_LMP_PDU 0x19
+#define BT_HCI_ERR_UNSUPP_REMOTE_FEATURE 0x1a
+#define BT_HCI_ERR_SCO_OFFSET_REJECTED 0x1b
+#define BT_HCI_ERR_SCO_INTERVAL_REJECTED 0x1c
+#define BT_HCI_ERR_SCO_AIR_MODE_REJECTED 0x1d
+#define BT_HCI_ERR_INVALID_LL_PARAM 0x1e
+#define BT_HCI_ERR_UNSPECIFIED 0x1f
+#define BT_HCI_ERR_UNSUPP_LL_PARAM_VAL 0x20
+#define BT_HCI_ERR_ROLE_CHANGE_NOT_ALLOWED 0x21
+#define BT_HCI_ERR_LL_RESP_TIMEOUT 0x22
+#define BT_HCI_ERR_LL_PROC_COLLISION 0x23
+#define BT_HCI_ERR_LMP_PDU_NOT_ALLOWED 0x24
+#define BT_HCI_ERR_ENC_MODE_NOT_ACCEPTABLE 0x25
+#define BT_HCI_ERR_LINK_KEY_CANNOT_BE_CHANGED 0x26
+#define BT_HCI_ERR_REQUESTED_QOS_NOT_SUPPORTED 0x27
+#define BT_HCI_ERR_INSTANT_PASSED 0x28
+#define BT_HCI_ERR_PAIRING_NOT_SUPPORTED 0x29
+#define BT_HCI_ERR_DIFF_TRANS_COLLISION 0x2a
+#define BT_HCI_ERR_QOS_UNACCEPTABLE_PARAM 0x2c
+#define BT_HCI_ERR_QOS_REJECTED 0x2d
+#define BT_HCI_ERR_CHAN_ASSESS_NOT_SUPPORTED 0x2e
+#define BT_HCI_ERR_INSUFF_SECURITY 0x2f
+#define BT_HCI_ERR_PARAM_OUT_OF_MANDATORY_RANGE 0x30
+#define BT_HCI_ERR_ROLE_SWITCH_PENDING 0x32
+#define BT_HCI_ERR_RESERVED_SLOT_VIOLATION 0x34
+#define BT_HCI_ERR_ROLE_SWITCH_FAILED 0x35
+#define BT_HCI_ERR_EXT_INQ_RESP_TOO_LARGE 0x36
+#define BT_HCI_ERR_SIMPLE_PAIR_NOT_SUPP_BY_HOST 0x37
+#define BT_HCI_ERR_HOST_BUSY_PAIRING 0x38
+#define BT_HCI_ERR_CONN_REJECTED_DUE_TO_NO_CHAN 0x39
+#define BT_HCI_ERR_CONTROLLER_BUSY 0x3a
+#define BT_HCI_ERR_UNACCEPT_CONN_PARAM 0x3b
+#define BT_HCI_ERR_ADV_TIMEOUT 0x3c
+#define BT_HCI_ERR_TERM_DUE_TO_MIC_FAIL 0x3d
+#define BT_HCI_ERR_CONN_FAIL_TO_ESTAB 0x3e
+#define BT_HCI_ERR_MAC_CONN_FAILED 0x3f
+#define BT_HCI_ERR_CLOCK_ADJUST_REJECTED 0x40
+#define BT_HCI_ERR_SUBMAP_NOT_DEFINED 0x41
+#define BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER 0x42
+#define BT_HCI_ERR_LIMIT_REACHED 0x43
+#define BT_HCI_ERR_OP_CANCELLED_BY_HOST 0x44
+#define BT_HCI_ERR_PACKET_TOO_LONG 0x45
+
+#define BT_HCI_ERR_AUTHENTICATION_FAIL __DEPRECATED_MACRO BT_HCI_ERR_AUTH_FAIL
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZEPHYR_INCLUDE_BLUETOOTH_HCI_STATUS_H_ */
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_raw.h b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_raw.h
new file mode 100644
index 0000000..8fb5564
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_raw.h
@@ -0,0 +1,152 @@
+/** @file
+ * @brief Bluetooth HCI RAW channel handling
+ */
+
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef ZEPHYR_INCLUDE_BLUETOOTH_HCI_RAW_H_
+#define ZEPHYR_INCLUDE_BLUETOOTH_HCI_RAW_H_
+
+/**
+ * @brief HCI RAW channel
+ * @defgroup hci_raw HCI RAW channel
+ * @ingroup bluetooth
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(CONFIG_BT_CTLR_TX_BUFFER_SIZE)
+#define BT_L2CAP_MTU (CONFIG_BT_CTLR_TX_BUFFER_SIZE - BT_L2CAP_HDR_SIZE)
+#else
+#define BT_L2CAP_MTU 65 /* 64-byte public key + opcode */
+#endif /* CONFIG_BT_CTLR */
+
+/** Data size needed for ACL buffers */
+#define BT_BUF_ACL_SIZE BT_L2CAP_BUF_SIZE(BT_L2CAP_MTU)
+
+#if defined(CONFIG_BT_CTLR_TX_BUFFERS)
+#define BT_HCI_ACL_COUNT CONFIG_BT_CTLR_TX_BUFFERS
+#else
+#define BT_HCI_ACL_COUNT 6
+#endif
+
+#define BT_BUF_TX_SIZE MAX(BT_BUF_RX_SIZE, BT_BUF_ACL_SIZE)
+
+/** @brief Send packet to the Bluetooth controller
+ *
+ * Send packet to the Bluetooth controller. Caller needs to
+ * implement netbuf pool.
+ *
+ * @param buf netbuf packet to be send
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_send(struct net_buf *buf);
+
+enum {
+ /** Passthrough mode
+ *
+ * While in this mode the buffers are passed as is between the stack
+ * and the driver.
+ */
+ BT_HCI_RAW_MODE_PASSTHROUGH = 0x00,
+
+ /** H:4 mode
+ *
+ * While in this mode H:4 headers will added into the buffers
+ * according to the buffer type when coming from the stack and will be
+ * removed and used to set the buffer type.
+ */
+ BT_HCI_RAW_MODE_H4 = 0x01,
+};
+
+/** @brief Set Bluetooth RAW channel mode
+ *
+ * Set access mode of Bluetooth RAW channel.
+ *
+ * @param mode Access mode.
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_hci_raw_set_mode(uint8_t mode);
+
+/** @brief Get Bluetooth RAW channel mode
+ *
+ * Get access mode of Bluetooth RAW channel.
+ *
+ * @return Access mode.
+ */
+uint8_t bt_hci_raw_get_mode(void);
+
+#define BT_HCI_ERR_EXT_HANDLED 0xff
+
+/** Helper macro to define a command extension
+ *
+ * @param _op Opcode of the command.
+ * @param _min_len Minimal length of the command.
+ * @param _func Handler function to be called.
+ */
+#define BT_HCI_RAW_CMD_EXT(_op, _min_len, _func) \
+ { \
+ .op = _op, \
+ .min_len = _min_len, \
+ .func = _func, \
+ }
+
+struct bt_hci_raw_cmd_ext {
+ /** Opcode of the command */
+ uint16_t op;
+
+ /** Minimal length of the command */
+ size_t min_len;
+
+ /** Handler function.
+ *
+ * Handler function to be called when a command is intercepted.
+ *
+ * @param buf Buffer containing the command.
+ *
+ * @return HCI Status code or BT_HCI_ERR_EXT_HANDLED if command has
+ * been handled already and a response has been sent as oppose to
+ * BT_HCI_ERR_SUCCESS which just indicates that the command can be
+ * sent to the controller to be processed.
+ */
+ uint8_t (*func)(struct net_buf *buf);
+};
+
+/** @brief Register Bluetooth RAW command extension table
+ *
+ * Register Bluetooth RAW channel command extension table, opcodes in this
+ * table are intercepted to sent to the handler function.
+ *
+ * @param cmds Pointer to the command extension table.
+ * @param size Size of the command extension table.
+ */
+void bt_hci_raw_cmd_ext_register(struct bt_hci_raw_cmd_ext *cmds, size_t size);
+
+/** @brief Enable Bluetooth RAW channel:
+ *
+ * Enable Bluetooth RAW HCI channel.
+ *
+ * @param rx_queue netbuf queue where HCI packets received from the Bluetooth
+ * controller are to be queued. The queue is defined in the caller while
+ * the available buffers pools are handled in the stack.
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_enable_raw(struct k_fifo *rx_queue);
+
+#ifdef __cplusplus
+}
+#endif
+/**
+ * @}
+ */
+
+#endif /* ZEPHYR_INCLUDE_BLUETOOTH_HCI_RAW_H_ */
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_vs.h b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_vs.h
new file mode 100644
index 0000000..02452a7
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/hci_vs.h
@@ -0,0 +1,379 @@
+/* hci_vs.h - Bluetooth Host Control Interface Vendor Specific definitions */
+
+/*
+ * Copyright (c) 2017-2018 Nordic Semiconductor ASA
+ * Copyright (c) 2015-2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef ZEPHYR_INCLUDE_BLUETOOTH_HCI_VS_H_
+#define ZEPHYR_INCLUDE_BLUETOOTH_HCI_VS_H_
+
+#include <bluetooth/hci.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BT_VS_CMD_BIT_VERSION 0
+#define BT_VS_CMD_BIT_SUP_CMD 1
+#define BT_VS_CMD_BIT_SUP_FEAT 2
+#define BT_VS_CMD_BIT_SET_EVT_MASK 3
+#define BT_VS_CMD_BIT_RESET 4
+#define BT_VS_CMD_BIT_WRITE_BDADDR 5
+#define BT_VS_CMD_BIT_SET_TRACE_ENABLE 6
+#define BT_VS_CMD_BIT_READ_BUILD_INFO 7
+#define BT_VS_CMD_BIT_READ_STATIC_ADDRS 8
+#define BT_VS_CMD_BIT_READ_KEY_ROOTS 9
+#define BT_VS_CMD_BIT_READ_CHIP_TEMP 10
+#define BT_VS_CMD_BIT_READ_HOST_STACK_CMD 11
+#define BT_VS_CMD_BIT_SET_SCAN_REP_ENABLE 12
+#define BT_VS_CMD_BIT_WRITE_TX_POWER 13
+#define BT_VS_CMD_BIT_READ_TX_POWER 14
+
+#define BT_VS_CMD_SUP_FEAT(cmd) BT_LE_FEAT_TEST(cmd, \
+ BT_VS_CMD_BIT_SUP_FEAT)
+#define BT_VS_CMD_READ_STATIC_ADDRS(cmd) BT_LE_FEAT_TEST(cmd, \
+ BT_VS_CMD_BIT_READ_STATIC_ADDRS)
+#define BT_VS_CMD_READ_KEY_ROOTS(cmd) BT_LE_FEAT_TEST(cmd, \
+ BT_VS_CMD_BIT_READ_KEY_ROOTS)
+
+#define BT_HCI_VS_HW_PLAT_INTEL 0x0001
+#define BT_HCI_VS_HW_PLAT_NORDIC 0x0002
+#define BT_HCI_VS_HW_PLAT_NXP 0x0003
+
+#define BT_HCI_VS_HW_VAR_NORDIC_NRF51X 0x0001
+#define BT_HCI_VS_HW_VAR_NORDIC_NRF52X 0x0002
+#define BT_HCI_VS_HW_VAR_NORDIC_NRF53X 0x0003
+
+#define BT_HCI_VS_FW_VAR_STANDARD_CTLR 0x0001
+#define BT_HCI_VS_FW_VAR_VS_CTLR 0x0002
+#define BT_HCI_VS_FW_VAR_FW_LOADER 0x0003
+#define BT_HCI_VS_FW_VAR_RESCUE_IMG 0x0004
+#define BT_HCI_OP_VS_READ_VERSION_INFO BT_OP(BT_OGF_VS, 0x0001)
+struct bt_hci_rp_vs_read_version_info {
+ uint8_t status;
+ uint16_t hw_platform;
+ uint16_t hw_variant;
+ uint8_t fw_variant;
+ uint8_t fw_version;
+ uint16_t fw_revision;
+ uint32_t fw_build;
+} __packed;
+
+#define BT_HCI_OP_VS_READ_SUPPORTED_COMMANDS BT_OP(BT_OGF_VS, 0x0002)
+struct bt_hci_rp_vs_read_supported_commands {
+ uint8_t status;
+ uint8_t commands[64];
+} __packed;
+
+#define BT_HCI_OP_VS_READ_SUPPORTED_FEATURES BT_OP(BT_OGF_VS, 0x0003)
+struct bt_hci_rp_vs_read_supported_features {
+ uint8_t status;
+ uint8_t features[8];
+} __packed;
+
+#define BT_HCI_OP_VS_SET_EVENT_MASK BT_OP(BT_OGF_VS, 0x0004)
+struct bt_hci_cp_vs_set_event_mask {
+ uint8_t event_mask[8];
+} __packed;
+
+#define BT_HCI_VS_RESET_SOFT 0x00
+#define BT_HCI_VS_RESET_HARD 0x01
+#define BT_HCI_OP_VS_RESET BT_OP(BT_OGF_VS, 0x0005)
+struct bt_hci_cp_vs_reset {
+ uint8_t type;
+} __packed;
+
+#define BT_HCI_OP_VS_WRITE_BD_ADDR BT_OP(BT_OGF_VS, 0x0006)
+struct bt_hci_cp_vs_write_bd_addr {
+ bt_addr_t bdaddr;
+} __packed;
+
+#define BT_HCI_VS_TRACE_DISABLED 0x00
+#define BT_HCI_VS_TRACE_ENABLED 0x01
+
+#define BT_HCI_VS_TRACE_HCI_EVTS 0x00
+#define BT_HCI_VS_TRACE_VDC 0x01
+#define BT_HCI_OP_VS_SET_TRACE_ENABLE BT_OP(BT_OGF_VS, 0x0007)
+struct bt_hci_cp_vs_set_trace_enable {
+ uint8_t enable;
+ uint8_t type;
+} __packed;
+
+#define BT_HCI_OP_VS_READ_BUILD_INFO BT_OP(BT_OGF_VS, 0x0008)
+struct bt_hci_rp_vs_read_build_info {
+ uint8_t status;
+ uint8_t info[];
+} __packed;
+
+struct bt_hci_vs_static_addr {
+ bt_addr_t bdaddr;
+ uint8_t ir[16];
+} __packed;
+
+#define BT_HCI_OP_VS_READ_STATIC_ADDRS BT_OP(BT_OGF_VS, 0x0009)
+struct bt_hci_rp_vs_read_static_addrs {
+ uint8_t status;
+ uint8_t num_addrs;
+ struct bt_hci_vs_static_addr a[];
+} __packed;
+
+#define BT_HCI_OP_VS_READ_KEY_HIERARCHY_ROOTS BT_OP(BT_OGF_VS, 0x000a)
+struct bt_hci_rp_vs_read_key_hierarchy_roots {
+ uint8_t status;
+ uint8_t ir[16];
+ uint8_t er[16];
+} __packed;
+
+#define BT_HCI_OP_VS_READ_CHIP_TEMP BT_OP(BT_OGF_VS, 0x000b)
+struct bt_hci_rp_vs_read_chip_temp {
+ uint8_t status;
+ int8_t temps;
+} __packed;
+
+struct bt_hci_vs_cmd {
+ uint16_t vendor_id;
+ uint16_t opcode_base;
+} __packed;
+
+#define BT_HCI_VS_VID_ANDROID 0x0001
+#define BT_HCI_VS_VID_MICROSOFT 0x0002
+#define BT_HCI_OP_VS_READ_HOST_STACK_CMDS BT_OP(BT_OGF_VS, 0x000c)
+struct bt_hci_rp_vs_read_host_stack_cmds {
+ uint8_t status;
+ uint8_t num_cmds;
+ struct bt_hci_vs_cmd c[];
+} __packed;
+
+#define BT_HCI_VS_SCAN_REQ_REPORTS_DISABLED 0x00
+#define BT_HCI_VS_SCAN_REQ_REPORTS_ENABLED 0x01
+#define BT_HCI_OP_VS_SET_SCAN_REQ_REPORTS BT_OP(BT_OGF_VS, 0x000d)
+struct bt_hci_cp_vs_set_scan_req_reports {
+ uint8_t enable;
+} __packed;
+
+#define BT_HCI_VS_LL_HANDLE_TYPE_ADV 0x00
+#define BT_HCI_VS_LL_HANDLE_TYPE_SCAN 0x01
+#define BT_HCI_VS_LL_HANDLE_TYPE_CONN 0x02
+#define BT_HCI_VS_LL_TX_POWER_LEVEL_NO_PREF 0x7F
+#define BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL BT_OP(BT_OGF_VS, 0x000e)
+struct bt_hci_cp_vs_write_tx_power_level {
+ uint8_t handle_type;
+ uint16_t handle;
+ int8_t tx_power_level;
+} __packed;
+
+struct bt_hci_rp_vs_write_tx_power_level {
+ uint8_t status;
+ uint8_t handle_type;
+ uint16_t handle;
+ int8_t selected_tx_power;
+} __packed;
+
+#define BT_HCI_OP_VS_READ_TX_POWER_LEVEL BT_OP(BT_OGF_VS, 0x000f)
+struct bt_hci_cp_vs_read_tx_power_level {
+ uint8_t handle_type;
+ uint16_t handle;
+} __packed;
+
+struct bt_hci_rp_vs_read_tx_power_level {
+ uint8_t status;
+ uint8_t handle_type;
+ uint16_t handle;
+ int8_t tx_power_level;
+} __packed;
+
+#define BT_HCI_OP_VS_READ_USB_TRANSPORT_MODE BT_OP(BT_OGF_VS, 0x0010)
+
+struct bt_hci_rp_vs_read_usb_transport_mode {
+ uint8_t status;
+ uint8_t num_supported_modes;
+ uint8_t supported_mode[];
+} __packed;
+
+#define BT_HCI_VS_USB_H2_MODE 0x00
+#define BT_HCI_VS_USB_H4_MODE 0x01
+
+#define BT_HCI_OP_VS_SET_USB_TRANSPORT_MODE BT_OP(BT_OGF_VS, 0x0011)
+
+struct bt_hci_cp_vs_set_usb_transport_mode {
+ uint8_t mode;
+} __packed;
+
+/* Events */
+
+struct bt_hci_evt_vs {
+ uint8_t subevent;
+} __packed;
+
+#define BT_HCI_EVT_VS_FATAL_ERROR 0x02
+struct bt_hci_evt_vs_fatal_error {
+ uint64_t pc;
+ uint8_t err_info[];
+} __packed;
+
+#define BT_HCI_VS_TRACE_LMP_TX 0x01
+#define BT_HCI_VS_TRACE_LMP_RX 0x02
+#define BT_HCI_VS_TRACE_LLCP_TX 0x03
+#define BT_HCI_VS_TRACE_LLCP_RX 0x04
+#define BT_HCI_VS_TRACE_LE_CONN_IND 0x05
+#define BT_HCI_EVT_VS_TRACE_INFO 0x03
+struct bt_hci_evt_vs_trace_info {
+ uint8_t type;
+ uint8_t data[];
+} __packed;
+
+#define BT_HCI_EVT_VS_SCAN_REQ_RX 0x04
+struct bt_hci_evt_vs_scan_req_rx {
+ bt_addr_le_t addr;
+ int8_t rssi;
+} __packed;
+
+/* Event mask bits */
+
+#define BT_EVT_MASK_VS_FATAL_ERROR BT_EVT_BIT(1)
+#define BT_EVT_MASK_VS_TRACE_INFO BT_EVT_BIT(2)
+#define BT_EVT_MASK_VS_SCAN_REQ_RX BT_EVT_BIT(3)
+
+/* Mesh HCI commands */
+#define BT_HCI_MESH_REVISION 0x01
+
+#define BT_HCI_OP_VS_MESH BT_OP(BT_OGF_VS, 0x0042)
+#define BT_HCI_MESH_EVT_PREFIX 0xF0
+
+struct bt_hci_cp_mesh {
+ uint8_t opcode;
+} __packed;
+
+#define BT_HCI_OC_MESH_GET_OPTS 0x00
+struct bt_hci_rp_mesh_get_opts {
+ uint8_t status;
+ uint8_t opcode;
+ uint8_t revision;
+ uint8_t ch_map;
+ int8_t min_tx_power;
+ int8_t max_tx_power;
+ uint8_t max_scan_filter;
+ uint8_t max_filter_pattern;
+ uint8_t max_adv_slot;
+ uint8_t max_tx_window;
+ uint8_t evt_prefix_len;
+ uint8_t evt_prefix;
+} __packed;
+
+#define BT_HCI_MESH_PATTERN_LEN_MAX 0x0f
+
+#define BT_HCI_OC_MESH_SET_SCAN_FILTER 0x01
+struct bt_hci_mesh_pattern {
+ uint8_t pattern_len;
+ uint8_t pattern[];
+} __packed;
+
+struct bt_hci_cp_mesh_set_scan_filter {
+ uint8_t scan_filter;
+ uint8_t filter_dup;
+ uint8_t num_patterns;
+ struct bt_hci_mesh_pattern patterns[];
+} __packed;
+struct bt_hci_rp_mesh_set_scan_filter {
+ uint8_t status;
+ uint8_t opcode;
+ uint8_t scan_filter;
+} __packed;
+
+#define BT_HCI_OC_MESH_ADVERTISE 0x02
+struct bt_hci_cp_mesh_advertise {
+ uint8_t adv_slot;
+ uint8_t own_addr_type;
+ bt_addr_t random_addr;
+ uint8_t ch_map;
+ int8_t tx_power;
+ uint8_t min_tx_delay;
+ uint8_t max_tx_delay;
+ uint8_t retx_count;
+ uint8_t retx_interval;
+ uint8_t scan_delay;
+ uint16_t scan_duration;
+ uint8_t scan_filter;
+ uint8_t data_len;
+ uint8_t data[31];
+} __packed;
+struct bt_hci_rp_mesh_advertise {
+ uint8_t status;
+ uint8_t opcode;
+ uint8_t adv_slot;
+} __packed;
+
+#define BT_HCI_OC_MESH_ADVERTISE_TIMED 0x03
+struct bt_hci_cp_mesh_advertise_timed {
+ uint8_t adv_slot;
+ uint8_t own_addr_type;
+ bt_addr_t random_addr;
+ uint8_t ch_map;
+ int8_t tx_power;
+ uint8_t retx_count;
+ uint8_t retx_interval;
+ uint32_t instant;
+ uint16_t tx_delay;
+ uint16_t tx_window;
+ uint8_t data_len;
+ uint8_t data[31];
+} __packed;
+struct bt_hci_rp_mesh_advertise_timed {
+ uint8_t status;
+ uint8_t opcode;
+ uint8_t adv_slot;
+} __packed;
+
+#define BT_HCI_OC_MESH_ADVERTISE_CANCEL 0x04
+struct bt_hci_cp_mesh_advertise_cancel {
+ uint8_t adv_slot;
+} __packed;
+struct bt_hci_rp_mesh_advertise_cancel {
+ uint8_t status;
+ uint8_t opcode;
+ uint8_t adv_slot;
+} __packed;
+
+#define BT_HCI_OC_MESH_SET_SCANNING 0x05
+struct bt_hci_cp_mesh_set_scanning {
+ uint8_t enable;
+ uint8_t ch_map;
+ uint8_t scan_filter;
+} __packed;
+struct bt_hci_rp_mesh_set_scanning {
+ uint8_t status;
+ uint8_t opcode;
+} __packed;
+
+/* Events */
+struct bt_hci_evt_mesh {
+ uint8_t prefix;
+ uint8_t subevent;
+} __packed;
+
+#define BT_HCI_EVT_MESH_ADV_COMPLETE 0x00
+struct bt_hci_evt_mesh_adv_complete {
+ uint8_t adv_slot;
+} __packed;
+
+#define BT_HCI_EVT_MESH_SCANNING_REPORT 0x01
+struct bt_hci_evt_mesh_scan_report {
+ bt_addr_le_t addr;
+ uint8_t chan;
+ int8_t rssi;
+ uint32_t instant;
+ uint8_t data_len;
+ uint8_t data[];
+} __packed;
+struct bt_hci_evt_mesh_scanning_report {
+ uint8_t num_reports;
+ struct bt_hci_evt_mesh_scan_report reports[];
+} __packed;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZEPHYR_INCLUDE_BLUETOOTH_HCI_VS_H_ */
diff --git a/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/l2cap_internal.h b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/l2cap_internal.h
new file mode 100644
index 0000000..b2e7fb4
--- /dev/null
+++ b/circuitpython/devices/ble_hci/common-hal/_bleio/hci_include/l2cap_internal.h
@@ -0,0 +1,230 @@
+// CircuitPython: Adapted from Zephyr include file.
+
+/** @file
+ * @brief Internal APIs for Bluetooth L2CAP handling.
+ */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdbool.h>
+// for __packed
+#include <string.h>
+
+enum l2cap_conn_list_action {
+ BT_L2CAP_CHAN_LOOKUP,
+ BT_L2CAP_CHAN_DETACH,
+};
+
+#define BT_L2CAP_CID_BR_SIG 0x0001
+#define BT_L2CAP_CID_ATT 0x0004
+#define BT_L2CAP_CID_LE_SIG 0x0005
+#define BT_L2CAP_CID_SMP 0x0006
+#define BT_L2CAP_CID_BR_SMP 0x0007
+
+#define BT_L2CAP_PSM_RFCOMM 0x0003
+
+struct bt_l2cap_hdr {
+ uint16_t len;
+ uint16_t cid;
+} __packed;
+
+struct bt_l2cap_sig_hdr {
+ uint8_t code;
+ uint8_t ident;
+ uint16_t len;
+} __packed;
+
+#define BT_L2CAP_REJ_NOT_UNDERSTOOD 0x0000
+#define BT_L2CAP_REJ_MTU_EXCEEDED 0x0001
+#define BT_L2CAP_REJ_INVALID_CID 0x0002
+
+#define BT_L2CAP_CMD_REJECT 0x01
+struct bt_l2cap_cmd_reject {
+ uint16_t reason;
+ uint8_t data[];
+} __packed;
+
+struct bt_l2cap_cmd_reject_cid_data {
+ uint16_t scid;
+ uint16_t dcid;
+} __packed;
+
+#define BT_L2CAP_CONN_REQ 0x02
+struct bt_l2cap_conn_req {
+ uint16_t psm;
+ uint16_t scid;
+} __packed;
+
+/* command statuses in reposnse */
+#define BT_L2CAP_CS_NO_INFO 0x0000
+#define BT_L2CAP_CS_AUTHEN_PEND 0x0001
+
+/* valid results in conn response on BR/EDR */
+#define BT_L2CAP_BR_SUCCESS 0x0000
+#define BT_L2CAP_BR_PENDING 0x0001
+#define BT_L2CAP_BR_ERR_PSM_NOT_SUPP 0x0002
+#define BT_L2CAP_BR_ERR_SEC_BLOCK 0x0003
+#define BT_L2CAP_BR_ERR_NO_RESOURCES 0x0004
+#define BT_L2CAP_BR_ERR_INVALID_SCID 0x0006
+#define BT_L2CAP_BR_ERR_SCID_IN_USE 0x0007
+
+#define BT_L2CAP_CONN_RSP 0x03
+struct bt_l2cap_conn_rsp {
+ uint16_t dcid;
+ uint16_t scid;
+ uint16_t result;
+ uint16_t status;
+} __packed;
+
+#define BT_L2CAP_CONF_SUCCESS 0x0000
+#define BT_L2CAP_CONF_UNACCEPT 0x0001
+#define BT_L2CAP_CONF_REJECT 0x0002
+
+#define BT_L2CAP_CONF_REQ 0x04
+struct bt_l2cap_conf_req {
+ uint16_t dcid;
+ uint16_t flags;
+ uint8_t data[];
+} __packed;
+
+#define BT_L2CAP_CONF_RSP 0x05
+struct bt_l2cap_conf_rsp {
+ uint16_t scid;
+ uint16_t flags;
+ uint16_t result;
+ uint8_t data[];
+} __packed;
+
+/* Option type used by MTU config request data */
+#define BT_L2CAP_CONF_OPT_MTU 0x01
+/* Options bits selecting most significant bit (hint) in type field */
+#define BT_L2CAP_CONF_HINT 0x80
+#define BT_L2CAP_CONF_MASK 0x7f
+
+struct bt_l2cap_conf_opt {
+ uint8_t type;
+ uint8_t len;
+ uint8_t data[];
+} __packed;
+
+#define BT_L2CAP_DISCONN_REQ 0x06
+struct bt_l2cap_disconn_req {
+ uint16_t dcid;
+ uint16_t scid;
+} __packed;
+
+#define BT_L2CAP_DISCONN_RSP 0x07
+struct bt_l2cap_disconn_rsp {
+ uint16_t dcid;
+ uint16_t scid;
+} __packed;
+
+#define BT_L2CAP_INFO_FEAT_MASK 0x0002
+#define BT_L2CAP_INFO_FIXED_CHAN 0x0003
+
+#define BT_L2CAP_INFO_REQ 0x0a
+struct bt_l2cap_info_req {
+ uint16_t type;
+} __packed;
+
+/* info result */
+#define BT_L2CAP_INFO_SUCCESS 0x0000
+#define BT_L2CAP_INFO_NOTSUPP 0x0001
+
+#define BT_L2CAP_INFO_RSP 0x0b
+struct bt_l2cap_info_rsp {
+ uint16_t type;
+ uint16_t result;
+ uint8_t data[];
+} __packed;
+
+#define BT_L2CAP_CONN_PARAM_REQ 0x12
+struct bt_l2cap_conn_param_req {
+ uint16_t min_interval;
+ uint16_t max_interval;
+ uint16_t latency;
+ uint16_t timeout;
+} __packed;
+
+#define BT_L2CAP_CONN_PARAM_ACCEPTED 0x0000
+#define BT_L2CAP_CONN_PARAM_REJECTED 0x0001
+
+#define BT_L2CAP_CONN_PARAM_RSP 0x13
+struct bt_l2cap_conn_param_rsp {
+ uint16_t result;
+} __packed;
+
+#define BT_L2CAP_LE_CONN_REQ 0x14
+struct bt_l2cap_le_conn_req {
+ uint16_t psm;
+ uint16_t scid;
+ uint16_t mtu;
+ uint16_t mps;
+ uint16_t credits;
+} __packed;
+
+/* valid results in conn response on LE */
+#define BT_L2CAP_LE_SUCCESS 0x0000
+#define BT_L2CAP_LE_ERR_PSM_NOT_SUPP 0x0002
+#define BT_L2CAP_LE_ERR_NO_RESOURCES 0x0004
+#define BT_L2CAP_LE_ERR_AUTHENTICATION 0x0005
+#define BT_L2CAP_LE_ERR_AUTHORIZATION 0x0006
+#define BT_L2CAP_LE_ERR_KEY_SIZE 0x0007
+#define BT_L2CAP_LE_ERR_ENCRYPTION 0x0008
+#define BT_L2CAP_LE_ERR_INVALID_SCID 0x0009
+#define BT_L2CAP_LE_ERR_SCID_IN_USE 0x000A
+#define BT_L2CAP_LE_ERR_UNACCEPT_PARAMS 0x000B
+#define BT_L2CAP_LE_ERR_INVALID_PARAMS 0x000C
+
+#define BT_L2CAP_LE_CONN_RSP 0x15
+struct bt_l2cap_le_conn_rsp {
+ uint16_t dcid;
+ uint16_t mtu;
+ uint16_t mps;
+ uint16_t credits;
+ uint16_t result;
+} __packed;
+
+#define BT_L2CAP_LE_CREDITS 0x16
+struct bt_l2cap_le_credits {
+ uint16_t cid;
+ uint16_t credits;
+} __packed;
+
+#define BT_L2CAP_ECRED_CONN_REQ 0x17
+struct bt_l2cap_ecred_conn_req {
+ uint16_t psm;
+ uint16_t mtu;
+ uint16_t mps;
+ uint16_t credits;
+ uint16_t scid[];
+} __packed;
+
+#define BT_L2CAP_ECRED_CONN_RSP 0x18
+struct bt_l2cap_ecred_conn_rsp {
+ uint16_t mtu;
+ uint16_t mps;
+ uint16_t credits;
+ uint16_t result;
+ uint16_t dcid[];
+} __packed;
+
+#define BT_L2CAP_ECRED_RECONF_REQ 0x19
+struct bt_l2cap_ecred_reconf_req {
+ uint16_t mtu;
+ uint16_t mps;
+ uint16_t scid[];
+} __packed;
+
+#define BT_L2CAP_RECONF_SUCCESS 0x0000
+#define BT_L2CAP_RECONF_INVALID_MTU 0x0001
+#define BT_L2CAP_RECONF_INVALID_MPS 0x0002
+
+#define BT_L2CAP_ECRED_RECONF_RSP 0x1a
+struct bt_l2cap_ecred_reconf_rsp {
+ uint16_t result;
+} __packed;
diff --git a/circuitpython/devices/ble_hci/supervisor/bluetooth.c b/circuitpython/devices/ble_hci/supervisor/bluetooth.c
new file mode 100644
index 0000000..83a4672
--- /dev/null
+++ b/circuitpython/devices/ble_hci/supervisor/bluetooth.c
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 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.
+ */
+
+#if CIRCUITPY_BLE_FILE_SERVICE
+#error CIRCUITPY_BLE_FILE_SERVICE not implemented for CIRCUITPY_BLEIO_HCI
+#endif
diff --git a/circuitpython/devices/ble_hci/supervisor/bluetooth.h b/circuitpython/devices/ble_hci/supervisor/bluetooth.h
new file mode 100644
index 0000000..0364582
--- /dev/null
+++ b/circuitpython/devices/ble_hci/supervisor/bluetooth.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 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_DEVICE_BLE_HCI_SUPERVISOR_BLUETOOTH_H
+#define MICROPY_INCLUDED_DEVICE_BLE_HCI_SUPERVISOR_BLUETOOTH_H
+
+void supervisor_start_bluetooth(void);
+bool supervisor_bluetooth_hook(ble_evt_t *ble_evt);
+void supervisor_bluetooth_background(void);
+
+#endif // MICROPY_INCLUDED_DEVICE_BLE_HCI_SUPERVISOR_BLUETOOTH_H