diff --git a/devices/ble_hci/common-hal/_bleio/Adapter.c b/devices/ble_hci/common-hal/_bleio/Adapter.c index 17b66899a5..55e0bde017 100644 --- a/devices/ble_hci/common-hal/_bleio/Adapter.c +++ b/devices/ble_hci/common-hal/_bleio/Adapter.c @@ -31,7 +31,7 @@ #include #include -#include "hci_api.h" +#include "hci.h" #include "py/gc.h" #include "py/mphal.h" diff --git a/devices/ble_hci/common-hal/_bleio/Connection.h b/devices/ble_hci/common-hal/_bleio/Connection.h index bb0c140c55..8b9790d9ed 100644 --- a/devices/ble_hci/common-hal/_bleio/Connection.h +++ b/devices/ble_hci/common-hal/_bleio/Connection.h @@ -64,13 +64,16 @@ typedef struct { //REMOVE ble_gap_conn_params_t conn_params; volatile bool conn_params_updating; uint16_t mtu; - // Request that CCCD values for this conenction be saved, using sys_attr values. + // 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 { diff --git a/devices/ble_hci/common-hal/_bleio/__init__.h b/devices/ble_hci/common-hal/_bleio/__init__.h index 0d1c2785f7..18b4289e9a 100644 --- a/devices/ble_hci/common-hal/_bleio/__init__.h +++ b/devices/ble_hci/common-hal/_bleio/__init__.h @@ -29,7 +29,7 @@ #include -#include "hci_api.h" +#include "hci.h" void bleio_background(void); void bleio_reset(void); diff --git a/devices/ble_hci/common-hal/_bleio/att.c b/devices/ble_hci/common-hal/_bleio/att.c new file mode 100644 index 0000000000..d37012a0ad --- /dev/null +++ b/devices/ble_hci/common-hal/_bleio/att.c @@ -0,0 +1,1621 @@ +// 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 +*/ + +#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/l2cap_internal.h" + +#include "py/obj.h" +#include "common-hal/_bleio/Adapter.h" +#include "supervisor/shared/tick.h" + +enum ble_attribute_type { + BLE_TYPE_UNKNOWN = 0x0000, + BLE_TYPE_PRIMARY_SERVICE = 0x2800, + BLE_TYPE_SECONDARY_SERVICE = 0x2801, + BLE_TYPE_CHARACTERISTIC = 0x2803, + BLE_TYPE_DESCRIPTOR = 0x2900 +}; + +STATIC uint16_t max_mtu = BT_ATT_DEFAULT_LE_MTU; // 23 +STATIC unsigned long timeout = 5000; + +STATIC volatile bool cnf; + +STATIC uint16_t long_write_handle = 0x0000; +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; + +//FIX BLEDeviceEventHandler event_handlers[2]; + + +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 int send_req_wait_for_rsp(uint16_t conn_handle, int 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; + + hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, 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;) { + hci_poll_for_incoming_pkt(); + + 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 att_init(void) { + max_mtu = BT_ATT_DEFAULT_LE_MTU; + timeout = 5000; + long_write_handle = 0x0000; + 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; + } + + //FIX memset(event_handlers, 0x00, sizeof(event_handlers)); +} + +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;) { + hci_poll_for_incoming_pkt(); + + is_connected = att_address_is_connected(addr); + + if (is_connected) { + break; + } + } + + if (!is_connected) { + hci_le_cancel_conn(); + } + + return is_connected; +} + +bool att_disconnect_from_address(bt_addr_le_t *addr) { + uint16_t conn_handle = att_conn_handle(addr); + if (conn_handle == 0xffff) { + return false; + } + + hci_disconnect(conn_handle); + + hci_poll_for_incoming_pkt_timeout(timeout); + + if (!att_handle_is_connected(conn_handle)) { + return true; + } + + return false; +} + +//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_TYPE_PRIMARY_SERVICE, response_buffer); + +// if (respLength == 0) { +// return false; +// } + +// if (response_buffer[0] == BT_ATT_OP_READ_BY_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 == 0x0000) { +// reqEnd_handle = 0x0000; +// } +// } +// } else { +// reqEnd_handle = 0x0000; +// } +// } + +// 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_TYPE_CHARACTERISTIC, response_buffer); + +// if (respLength == 0) { +// return false; +// } + +// if (response_buffer[0] == BT_ATT_OP_READ_BY_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 = 23; + memcpy(&bleio_connections[peer_index].addr, peer_addr, sizeof(bleio_connections[peer_index].addr)); + + //FIX if (event_handlers[BLEConnected]) { + // event_handlers[BLEConnected](BLEDevice(peer_bdaddr_type, peer_bdaddr)); + // } +} + + +void att_remove_connection(uint16_t 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 == handle) { + peer_index = i; + } + + if (bleio_connections[i].conn_handle != 0xffff) { + peer_count++; + } + } + + if (peer_index == -1) { + // bail not found + return; + } + + //FIX BLEDevice bleDevice(bleio_connections[peer_index].address_type, bleio_connections[peer_index].address); + + if (peer_count == 1) { + //FIX + // clear CCCD values on disconnect + // for (uint16_t i = 0; i < GATT.attributeCount(); i++) { + // BLELocalAttribute* attribute = GATT.attribute(i); + + // if (attribute->type() == BLE_TYPE_CHARACTERISTIC) { + // BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + // characteristic->writeCccdValue(bleDevice, 0x0000); + // } + // } + + long_write_handle = 0x0000; + long_write_value_length = 0; + } + + //FIX + // if (event_handlers[BLEDisconnected]) { + // event_handlers[BLEDisconnected](bleDevice); + // } + + 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 = 23; + + //FIX if (bleio_connections[peer_index].device) { + //FIX delete bleio_connections[peer_index].device; + // } + //FIX bleio_connections[peer_index].device = NULL; +} + +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; +} + +//FIX +// BLERemoteDevice* att_device(uint8_t address_type, const uint8_t address[6]) { +// 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 NULL; +// } + +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) { + hci_poll_for_incoming_pkt(); + + 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 23; +} + +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 (hci_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 = 23; + + //FIX + // if (bleio_connections[i].device) { + // delete bleio_connections[i].device; + // } + // bleio_connections[i].device = NULL; + } + + return (num_disconnects > 0); +} + +// FIX +// BLEDevice att_central() { +// for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { +// if (bleio_connections[i].conn_handle == 0xffff || bleio_connections[i].role != 0x01) { +// continue; +// } + +// return BLEDevice(bleio_connections[i].address_type, bleio_connections[i].address); +// } + +// return BLEDevice(); +// } + +bool att_handle_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; + } + + //FIX This seems fishy. Why just .mtu instead of .mtu + 1 for opcode + uint8_t notification[bleio_connections[i].mtu]; + uint16_t notification_length = 0; + + notification[0] = BT_ATT_OP_NOTIFY; + notification_length++; + + memcpy(¬ification[1], &handle, sizeof(handle)); + notification_length += sizeof(handle); + + length = MIN((uint16_t)(bleio_connections[i].mtu - notification_length), (uint16_t)length); + memcpy(¬ification[notification_length], value, length); + notification_length += length; + + hci_send_acl_pkt(bleio_connections[i].conn_handle, BT_L2CAP_CID_ATT, notification_length, notification); + + num_notifications++; + } + + return (num_notifications > 0); +} + +bool att_handle_ind(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; + } + + uint8_t indication[bleio_connections[i].mtu]; + uint16_t indication_length = 0; + + indication[0] = BT_ATT_OP_INDICATE; + indication_length++; + + memcpy(&indication[1], &handle, sizeof(handle)); + indication_length += sizeof(handle); + + length = MIN((uint16_t)(bleio_connections[i].mtu - indication_length), (uint16_t)length); + memcpy(&indication[indication_length], value, length); + indication_length += length; + + cnf = false; + + hci_send_acl_pkt(bleio_connections[i].conn_handle, BT_L2CAP_CID_ATT, indication_length, indication); + + while (!cnf) { + hci_poll_for_incoming_pkt(); + + 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(req)) { + send_error(conn_handle, BT_ATT_OP_MTU_REQ, 0x0000, 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_req 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[]) { + if (dlen != sizeof(struct bt_att_exchange_mtu_rsp)) { + return; + } + + struct bt_att_exchange_mtu_rsp *rsp = (struct bt_att_exchange_mtu_rsp *) data; + + 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; + } + + //FIX + // uint8_t response[mtu]; + // uint16_t response_length; + + // response[0] = BT_ATT_OP_FIND_INFO_RSP; + // response[1] = 0x00; + // response_length = 2; + + // for (uint16_t i = (req->start_handle - 1); i < GATT.attributeCount() && i <= (req->end_handle - 1); i++) { + // BLELocalAttribute* attribute = GATT.attribute(i); + // uint16_t handle = (i + 1); + // bool is_value_handle = (attribute->type() == BLE_TYPE_CHARACTERISTIC) && (((BLELocalCharacteristic*)attribute)->valueHandle() == handle); + // int uuid_len = is_value_handle ? 2 : attribute->uuid_length(); + // size_t info_type = (uuidLen == 2) ? 0x01 : 0x02; + + // if (response[1] == 0) { + // response[1] = info_type; + // } + + // if (response[1] != info_type) { + // // different type + // break; + // } + + // // add the handle + // memcpy(&response[response_length], &handle, sizeof(handle)); + // response_length += sizeof(handle); + + // if (is_value_handle || attribute->type() == BLE_TYPE_DESCRIPTOR) { + // // add the UUID + // memcpy(&response[response_length], attribute->uuid_data(), uuid_len); + // response_length += uuidLen; + // } else { + // // add the type + // uint16_t type = attribute->type(); + + // memcpy(&response[response_length], &type, sizeof(type)); + // response_length += sizeof(type); + // } + + // if ((response_length + (2 + uuidLen)) > mtu) { + // break; + // } + // } + + // if (response_length == 2) { + // send_error(conn_handle, BT_ATT_OP_FIND_INFO_REQ, findInfoReq->start_handle, BT_ATT_ERR_ATTR_NOT_FOUND); + // } else { + // hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, response_length, response); + // } +} + +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_by_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_by_type_req->type == BLE_TYPE_PRIMARY_SERVICE) { + // for (uint16_t i = (find_by_type_req->start_handle - 1); i < GATT.attributeCount() && i <= (find_by_type_req->end_handle - 1); i++) { + // BLELocalAttribute* attribute = GATT.attribute(i); + + // if ((attribute->type() == find_by_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); + } +} + +void process_read_by_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 uuid = req->uuid[0] | (req->uuid[1] << 8); + + if (dlen != sizeof(struct bt_att_find_type_req) || + (uuid != BLE_TYPE_PRIMARY_SERVICE && + uuid != BLE_TYPE_SECONDARY_SERVICE)) { + send_error(conn_handle, BT_ATT_OP_READ_GROUP_REQ, req->start_handle, BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE); + return; + } + + uint8_t response[mtu]; + uint16_t response_length; + + response[0] = BT_ATT_OP_READ_GROUP_RSP; + response[1] = 0x00; + response_length = 2; + + // FIX + // for (uint16_t i = (readByGroupReq->start_handle - 1); i < GATT.attributeCount() && i <= (readByGroupReq->end_handle - 1); i++) { + //FIX + // BLELocalAttribute* attribute = GATT.attribute(i); + + // if (readByGroupReq->uuid != attribute->type()) { + // // not the type + // continue; + // } + + // int uuidLen = attribute->uuidLength(); + // size_t infoSize = (uuidLen == 2) ? 6 : 20; + + // if (response[1] == 0) { + // response[1] = infoSize; + // } + + // if (response[1] != infoSize) { + // // different size + // break; + // } + + // 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); + + // // add the UUID + // memcpy(&response[response_length], service->uuidData(), uuidLen); + // response_length += uuidLen; + + // if ((response_length + infoSize) > mtu) { + // break; + // } + // } + + if (response_length == 2) { + 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, response_length, response); + } +} + +int att_read_by_group_req(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, uint16_t uuid, uint8_t response_buffer[]) { + struct __packed { + struct bt_att_hdr h; + struct bt_att_read_group_req r; + } req = { { + .code = BT_ATT_OP_ERROR_RSP, + }, { + .start_handle = start_handle, + .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), (uint8_t *) &req, response_buffer); +} + +STATIC void process_read_by_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, 0x0000, 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 (dlen != sizeof(struct bt_att_read_blob_req)) { + send_error(conn_handle, BT_ATT_OP_READ_BLOB_REQ, 0x0000, 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; + } + + //FIX + (void) offset; + (void) handle; + //FIX if ((uint16_t)(handle - 1) > GATT.attributeCount()) { + // send_error(conn_handle, opcode, handle, BT_ATT_ERR_ATTR_NOT_FOUND); + // return; + // } + + uint8_t response[mtu]; + uint16_t response_length; + + response[0] = response_opcode; + response_length = 1; + + //FIX BLELocalAttribute* attribute = GATT.attribute(handle - 1); + // enum BLEAttributeType attributeType = attribute->type(); + + // if (attributeType == BLE_TYPE_PRIMARY_SERVICE) { + // if (offset) { + // send_error(conn_handle, BT_ATT_ERR_ATTR_NOT_LONG, handle, BT_ATT_ERR_INVALID_PDU); + // return; + // } + + // BLELocalService* service = (BLELocalService*)attribute; + + // // add the UUID + // uint8_t uuidLen = service->uuidLength(); + // memcpy(&response[response_length], service->uuidData(), uuidLen); + // response_length += uuidLen; + // } else if (attributeType == BLE_TYPE_CHARACTERISTIC) { + // BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + // if (characteristic->handle() == handle) { + // if (offset) { + // send_error(conn_handle, opcode, handle, BT_ATT_ERR_ATTR_NOT_LONG); + // return; + // } + + // // add the properties + // response[response_length++] = characteristic->properties(); + + // // add the value handle + // uint16_t value_handle = characteristic->value_handle(); + // memcpy(&response[response_length], &value_handle, sizeof(value_handle)); + // response_length += sizeof(value_handle); + + // // add the UUID + // uint8_t uuidLen = characteristic->uuidLength(); + // memcpy(&response[response_length], characteristic->uuidData(), uuidLen); + // response_length += uuidLen; + // } else { + // if ((characteristic->properties() & BLERead) == 0) { + // send_error(conn_handle, opcode, handle, BT_ATT_ERR_READ_NOT_PERM); + // return; + // } + + // uint16_t value_length = characteristic->value_length(); + + // if (offset >= value_length) { + // send_error(conn_handle, opcode, handle, BT_ATT_ERR_INVALID_OFFSET); + // return; + // } + + // value_length = min(mtu - response_length, value_length - offset); + + // for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + // if (bleio_connections[i].conn_handle == conn_handle) { + // // FIX characteristic->readValue(BLEDevice(bleio_connections[i].address_type, bleio_connections[i].address), offset, &response[response_length], value_length); + // response_length += value_length; + // } + // } + // } + // } else if (attributeType == BLE_TYPE_DESCRIPTOR) { + // BLELocalDescriptor* descriptor = (BLELocalDescriptor*)attribute; + + // uint16_t value_length = descriptor->valueSize(); + + // if (offset >= value_length) { + // send_error(conn_handle, opcode, handle, BT_ATT_ERR_INVALID_OFFSET); + // return; + // } + + // value_length = min(mtu - response_length, value_length - offset); + + // memcpy(&response[response_length], descriptor->value() + offset, value_length); + // response_length += value_length; + // } + + hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, response_length, response); +} + +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_by_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; + + if (dlen != sizeof(struct bt_att_read_type_req)) { + send_error(conn_handle, BT_ATT_OP_READ_TYPE_REQ, req->start_handle, BT_ATT_ERR_INVALID_PDU); + return; + } + + // uint8_t response[mtu]; + // uint16_t response_length; + + // response[0] = BT_ATT_OP_READ_TYPE_RSP; + // response[1] = 0x00; + // response_length = 2; + + // for (uint16_t i = (req->start_handle - 1); i < GATT.attributeCount() && i <= (req->end_handle - 1); i++) { + // BLELocalAttribute* attribute = GATT.attribute(i); + // uint16_t handle = (i + 1); + + // if (attribute->type() == readByTypeReq->uuid) { + // if (attribute->type() == BLE_TYPE_CHARACTERISTIC) { + // BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + // if (characteristic->value_handle() == handle) { + // // value handle, skip + // continue; + // } + + // int uuidLen = attribute->uuidLength(); + // int typeSize = (uuidLen == 2) ? 7 : 21; + + // if (response[1] == 0) { + // response[1] = typeSize; + // } + + // if (response[1] != typeSize) { + // // all done, wrong size + // break; + // } + + // // add the handle + // memcpy(&response[response_length], &handle, sizeof(handle)); + // response_length += sizeof(handle); + + // // add the properties + // response[response_length++] = characteristic->properties(); + + // // add the value handle + // uint16_t value_handle = (handle + 1); + // memcpy(&response[response_length], &value_handle, sizeof(value_handle)); + // response_length += sizeof(value_handle); + + // // add the UUID + // memcpy(&response[response_length], characteristic->uuidData(), uuidLen); + // response_length += uuidLen; + + // // skip the next handle, it's a value handle + // i++; + + // if ((response_length + typeSize) > mtu) { + // break; + // } + // } else if (attribute->type() == 0x2902) { + // BLELocalDescriptor* descriptor = (BLELocalDescriptor*)attribute; + + // // add the handle + // memcpy(&response[response_length], &handle, sizeof(handle)); + // response_length += sizeof(handle); + + // // add the value + // int valueSize = min((uint16_t)(mtu - response_length), (uint16_t)descriptor->valueSize()); + // memcpy(&response[response_length], descriptor->value(), valueSize); + // response_length += valueSize; + + // response[1] = 2 + valueSize; + + // break; // all done + // } + // } else if (attribute->type() == BLE_TYPE_CHARACTERISTIC && attribute->uuidLength() == 2 && memcmp(&readByTypeReq->uuid, attribute->uuidData(), 2) == 0) { + // BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + // // add the handle + // memcpy(&response[response_length], &handle, sizeof(handle)); + // response_length += sizeof(handle); + + // // add the value + // int value_length = min((uint16_t)(mtu - response_length), (uint16_t)characteristic->value_length()); + // memcpy(&response[response_length], characteristic->value(), value_length); + // response_length += value_length; + + // response[1] = 2 + value_length; + + // break; // all done + // } + // } + + // if (response_length == 2) { + // send_error(conn_handle, BT_ATT_OP_READ_BY_TYPE_REQ, readByTypeReq->start_handle, BT_ATT_ERR_ATTR_NOT_FOUND); + // } else { + // hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, response_length, response); + // } +} + +int att_read_by_type_req(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, uint16_t type, uint8_t response_buffer[]) { + struct __packed { + struct bt_att_hdr h; + struct bt_att_read_type_req r; + } req = { { + .code = BT_ATT_OP_READ_TYPE_REQ, + }, { + .start_handle = start_handle, + .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), (uint8_t *) &req, response_buffer); +} + +void att_read_by_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_req_or_cmd(uint16_t conn_handle, uint16_t mtu, uint8_t op, uint8_t dlen, uint8_t data[]) { + boolean 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, 0x0000, BT_ATT_ERR_INVALID_PDU); + } + return; + } + + uint16_t handle = *(uint16_t*)data; + + // if ((uint16_t)(handle - 1) > GATT.attributeCount()) { + // if (with_response) { + // send_error(conn_handle, BT_ATT_OP_WRITE_REQ, handle, BT_ATT_ERR_ATTR_NOT_FOUND); + // } + // return; + // } + + // uint8_t value_length = dlen - sizeof(handle); + // uint8_t* value = &data[sizeof(handle)]; + + // BLELocalAttribute* attribute = GATT.attribute(handle - 1); + + // if (attribute->type() == BLE_TYPE_CHARACTERISTIC) { + // BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + // if (handle != characteristic->value_handle() || + // withResponse ? ((characteristic->properties() & BLEWrite) == 0) : + // ((characteristic->properties() & BLEWriteWithoutResponse) == 0)) { + // if (withResponse) { + // send_error(conn_handle, BT_ATT_OP_WRITE_REQ, handle, BT_ATT_ERR_WRITE_NOT_PERM); + // } + // return; + // } + + // 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), value, value_length); + // break; + // } + // } + // } else if (attribute->type() == BLE_TYPE_DESCRIPTOR) { + // BLELocalDescriptor* descriptor = (BLELocalDescriptor*)attribute; + + // // only CCCD's are writable + // if (descriptor->uuidLength() != 2 || *((uint16_t*)(descriptor->uuidData())) != 0x2902) { + // if (withResponse) { + // send_error(conn_handle, BT_ATT_OP_WRITE_REQ, handle, BT_ATT_ERR_WRITE_NOT_PERM); + // } + // return; + // } + + // // get the previous handle, should be the characteristic for the CCCD + // attribute = GATT.attribute(handle - 2); + + // if (attribute->type() != BLE_TYPE_CHARACTERISTIC) { + // if (withResponse) { + // send_error(conn_handle, BT_ATT_OP_WRITE_REQ, handle, BT_ATT_ERR_WRITE_NOT_PERM); + // } + // return; + // } + + // BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + // for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + // if (bleio_connections[i].conn_handle == conn_handle) { + // //FIX characteristic->writeCccdValue(BLEDevice(bleio_connections[i].address_type, bleio_connections[i].address), *((uint16_t*)value)); + // break; + // } + // } + // } else { + // if (withResponse) { + // send_error(conn_handle, BT_ATT_OP_WRITE_REQ, handle, BT_ATT_ERR_WRITE_NOT_PERM); + // } + // return; + // } + + if (withResponse) { + uint8_t response[mtu]; + uint16_t response_length; + + response[0] = BT_ATT_OP_WRITE_RSP; + response_length = 1; + + hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, response_length, response); + } +} + +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_prep_write_req(uint16_t conn_handle, uint16_t mtu, uint8_t dlen, uint8_t data[]) { + struct __attribute__ ((packed)) PrepWriteReq { + uint16_t handle; + uint16_t offset; + } *prepWriteReq = (PrepWriteReq*)data; + + if (dlen < sizeof(PrepWriteReq)) { + send_error(conn_handle, BT_ATT_OP_PREP_WRITE_REQ, 0x0000, BT_ATT_ERR_INVALID_PDU); + return; + } + + uint16_t handle = prepWriteReq->handle; + uint16_t offset = prepWriteReq->offset; + + if ((uint16_t)(handle - 1) > GATT.attributeCount()) { + send_error(conn_handle, BT_ATT_OP_PREP_WRITE_REQ, handle, BT_ATT_ERR_ATTR_NOT_FOUND); + return; + } + + BLELocalAttribute* attribute = GATT.attribute(handle - 1); + + if (attribute->type() != BLE_TYPE_CHARACTERISTIC) { + send_error(conn_handle, BT_ATT_OP_PREP_WRITE_REQ, handle, BT_ATT_ERR_ATTR_NOT_LONG); + return; + } + + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + if (handle != characteristic->value_handle()) { + send_error(conn_handle, BT_ATT_OP_PREP_WRITE_REQ, handle, BT_ATT_ERR_ATTR_NOT_LONG); + return; + } + + if ((characteristic->properties() & BLEWrqite) == 0) { + send_error(conn_handle, BT_ATT_OP_PREP_WRITE_REQ, handle, BT_ATT_ERR_WRITE_NOT_PERM); + return; + } + + if (long_write_handle == 0) { + 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_PREP_WRITE_REQ, handle, BT_ATT_ERR_UNLIKELY); + return; + } + + uint8_t value_length = dlen - sizeof(PrepWriteReq); + uint8_t* value = &data[sizeof(PrepWriteReq)]; + + if ((offset != long_write_value_length) || ((offset + value_length) > (uint16_t)characteristic->valueSize())) { + send_error(conn_handle, BT_ATT_OP_PREP_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[]) { + if (dlen != sizeof(uint8_t)) { + send_error(conn_handle, BT_ATT_OP_EXEC_WRITE_REQ, 0x0000, BT_ATT_ERR_INVALID_PDU); + return; + } + + uint8_t flag = data[0]; + + if (long_write_handle && (flag & 0x01)) { + 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 = 0x0000; + 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_handle_notify_or_ind(uint16_t conn_handle, uint8_t opcode, uint8_t dlen, uint8_t data[]) { + if (dlen < 2) { + return; // drop + } + + struct __attribute__ ((packed)) _handleNotifyOrInd { + uint16_t handle; + } *handleNotifyOrInd = (_handleNotifyOrInd*)data; + + uint8_t handle = handleNotifyOrInd->handle; + + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + if (bleio_connections[i].conn_handle != conn_handle) { + continue; + } + + 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_HANDLE_IND) { + // send CNF for IND + + uint8_t cnf = BT_ATT_OP_HANDLE_CNF; + + hci_send_acl_pkt(conn_handle, BT_L2CAP_CID_ATT, sizeof(cnf), &cnf); + } +} + +STATIC void process_handle_cnf(uint16_t /*conn_handle*/, uint8_t /*dlen*/, uint8_t /*data*/[]) { + cnf = true; +} + +bool att_exchange_mtu(uint16_t conn_handle) { + uint8_t response_buffer[max_mtu]; + struct bt_att_exchange_mtu_req req; + req->mtu = max_mtu; + return send_req_wait_for_rsp(conn_handle, BT_ATT_OP_MTU_REQ, &req, sizeof(req), response_buffer); +} + + + +void att_set_event_handler(BLEDeviceEvent event, BLEDeviceEventHandler event_handler) { + if (event < (sizeof(event_handlers) / (sizeof(event_handlers[0])))) { + event_handlers[event] = event_handler; + } +} + +int att_read_req(uint16_t conn_handle, uint16_t handle, uint8_t response_buffer[]) { + struct __attribute__ ((packed)) { + uint8_t op; + uint16_t handle; + } read_req = { BT_ATT_OP_READ_REQ, handle }; + + return send_req_wait_for_rsp(conn_handle, &read_req, sizeof(read_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[]) { + struct __attribute__ ((packed)) { + uint8_t op; + uint16_t handle; + uint8_t data[255]; + } write_req; + + write_req.opcode = BT_ATT_OP_WRITE_REQ; + write_req.handle = handle; + memcpy(write_req.data, data, data_len); + + return send_req_wait_for_rsp(conn_handle, &write_req, 3 + data_len, response_buffer); +} + +void att_write_cmd(uint16_t conn_handle, uint16_t handle, const uint8_t* data, uint8_t data_len) { + struct bt_att_write_cmd req = { + .handle = handle, + }; + memcpy(req.value, data, data_len); + + send_req_wait_for_rsp(conn_handle, &req, data_len + sizeof(req), NULL); +} + +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 = this->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_BY_TYPE_REQ: + process_find_by_type_req(conn_handle, mtu, dlen, data); + break; + + case BT_ATT_OP_READ_BY_TYPE_REQ: + process_read_by_type_req(conn_handle, mtu, dlen, data); + break; + + case BT_ATT_OP_READ_BY_TYPE_RSP: + process_read_by_type_rsp(conn_handle, dlen, data); + break; + + case BT_ATT_OP_READ_BY_GROUP_REQ: + att_read_by_group_req(conn_handle, mtu, dlen, data); + break; + + case BT_ATT_OP_READ_BY_GROUP_RSP: + prcoess_read_by_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_PREP_WRITE_REQ: + process_prep_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_HANDLE_NOTIFY: + case BT_ATT_OP_HANDLE_IND: + process_handle_notify_or_ind(conn_handle, opcode, dlen, data); + break; + + case BT_ATT_OP_HANDLE_CNF: + process_handle_cnf(conn_handle, dlen, data); + break; + + case BT_ATT_OP_READ_MULTI_REQ: + case BT_ATT_OP_SIGNED_WRITE_CMD: + default: + send_error(conn_handle, opcode, 0x00, BT_ATT_ERR_REQ_NOT_SUPP); + break; + } +} diff --git a/devices/ble_hci/common-hal/_bleio/att.h b/devices/ble_hci/common-hal/_bleio/att.h new file mode 100644 index 0000000000..108c44929f --- /dev/null +++ b/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 +#include + +#include "hci_include/addr.h" +#include "hci_include/att.h" +#include "hci_include/att_internal.h" + +//FIX BLEDevice att_central(void); +//FIX BLERemoteDevice* att_device(uint8_t address_type, const uint8_t address[6]); +//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_all(void); +bool att_disconnect_from_address(bt_addr_le_t *addr); +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_ind(uint16_t handle, const uint8_t* value, int length); +bool att_handle_is_connected(uint16_t handle); +bool att_handle_notify(uint16_t handle, const uint8_t* value, int length); +bool att_is_connected(void); +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 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/devices/ble_hci/common-hal/_bleio/hci_api.c b/devices/ble_hci/common-hal/_bleio/hci.c similarity index 96% rename from devices/ble_hci/common-hal/_bleio/hci_api.c rename to devices/ble_hci/common-hal/_bleio/hci.c index 854f7c0a00..83aba883ad 100644 --- a/devices/ble_hci/common-hal/_bleio/hci_api.c +++ b/devices/ble_hci/common-hal/_bleio/hci.c @@ -13,13 +13,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "hci_api.h" +#include "att.h" +#include "hci.h" #include "py/obj.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 @@ -28,46 +30,18 @@ #include "common-hal/_bleio/Adapter.h" #include "shared-bindings/microcontroller/__init__.h" + // 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 -//FIX replace -#define ATT_CID 0x0004 - -#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) - -#define RX_BUFFER_SIZE (3 + 255) -#define ACL_DATA_BUFFER_SIZE (255 + 1) - #define CTS_TIMEOUT_MSECS (1000) #define RESPONSE_TIMEOUT_MSECS (1000) #define adapter (&common_hal_bleio_adapter_obj) -STATIC uint8_t rx_buffer[RX_BUFFER_SIZE]; -STATIC size_t rx_idx; - -STATIC size_t num_command_packets_allowed; -STATIC size_t max_pkt; -STATIC 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 uint8_t acl_data_buffer[ACL_DATA_BUFFER_SIZE]; -STATIC size_t acl_data_len; - -STATIC volatile bool hci_poll_in_progress = false; - -STATIC bool debug = true; - // 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. @@ -109,6 +83,36 @@ typedef struct __attribute__ ((packed)) { } 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 size_t max_pkt; +STATIC 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; + +STATIC bool debug = true; + +////////////////////////////////////////////////////////////////////// + STATIC void dump_cmd_pkt(bool tx, uint8_t pkt_len, uint8_t pkt_data[]) { if (debug) { h4_hci_cmd_pkt_t *pkt = (h4_hci_cmd_pkt_t *) pkt_data; @@ -191,15 +195,16 @@ STATIC void process_acl_data_pkt(uint8_t pkt_len, uint8_t pkt_data[]) { acl_data_len += pkt->data_len; } - acl_data_t *acl_so_far = (acl_data_t *) acl_data_buffer; - if (acl_data_len != acl_so_far->acl_data_len) { + acl_data_t *acl = (acl_data_t *) &acl_data_buffer; + if (acl_data_len != acl->acl_data_len) { // We don't have the full packet yet. return; } - // if (aclHdr->cid == ATT_CID) { - // ATT.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &rx_buffer[1 + sizeof(HCIACLHdr)]); - // } else if (aclHdr->cid == SIGNALING_CID) { + 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)) { @@ -340,6 +345,20 @@ void hci_init(void) { hci_poll_in_progress = false; } +hci_result_t hci_poll_for_incoming_pkt_timeout(uint32_t timeout_msecs) { + uint64_t start = supervisor_ticks_ms64(); + + hci_result_t result; + + while (supervisor_ticks_ms64() -start < timeout_msecs) { + result = hci_poll_for_incoming_pkt(); + RUN_BACKGROUND_TASKS; + } + + return result; +} + + hci_result_t hci_poll_for_incoming_pkt(void) { if (hci_poll_in_progress) { return HCI_OK; @@ -500,8 +519,7 @@ STATIC hci_result_t send_command(uint16_t opcode, uint8_t params_len, void* para return HCI_NO_RESPONSE; } -//FIX remove unused -STATIC int __attribute__((unused)) send_acl_pkt(uint16_t handle, uint8_t cid, void* data, uint8_t data_len) { +hci_result_t hci_send_acl_pkt(uint16_t handle, uint8_t cid, uint8_t data_len, uint8_t *data) { int result; while (pending_pkt >= max_pkt) { result = hci_poll_for_incoming_pkt(); @@ -510,7 +528,7 @@ STATIC int __attribute__((unused)) send_acl_pkt(uint16_t handle, uint8_t cid, vo } } - // data_len does not include cid. + // data_len does not include cid const size_t cid_len = sizeof_field(acl_data_t, cid); // buf_len is size of entire packet including header. const size_t buf_len = sizeof(h4_hci_acl_pkt_t) + cid_len + data_len; diff --git a/devices/ble_hci/common-hal/_bleio/hci_api.h b/devices/ble_hci/common-hal/_bleio/hci.h similarity index 93% rename from devices/ble_hci/common-hal/_bleio/hci_api.h rename to devices/ble_hci/common-hal/_bleio/hci.h index f6d96d48fe..736877ddc6 100644 --- a/devices/ble_hci/common-hal/_bleio/hci_api.h +++ b/devices/ble_hci/common-hal/_bleio/hci.h @@ -17,8 +17,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_HCI_API_H -#define MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_HCI_API_H +#ifndef MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_HCI_H +#define MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_HCI_H #include @@ -64,6 +64,7 @@ hci_result_t hci_le_set_scan_parameters(uint8_t scan_type, uint16_t interval, ui 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_poll_for_incoming_pkt_timeout(uint32_t timeout_msecs); 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); @@ -72,6 +73,7 @@ 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, uint8_t data_len, uint8_t *data); hci_result_t hci_set_evt_mask(uint64_t event_mask); -#endif // MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_HCI_API_H +#endif // MICROPY_INCLUDED_DEVICES_BLE_HCI_COMMON_HAL_BLEIO_HCI_H diff --git a/devices/ble_hci/common-hal/_bleio/hci_include/addr.h b/devices/ble_hci/common-hal/_bleio/hci_include/addr.h index 8f503b8a17..fd74a95e8d 100644 --- a/devices/ble_hci/common-hal/_bleio/hci_include/addr.h +++ b/devices/ble_hci/common-hal/_bleio/hci_include/addr.h @@ -12,6 +12,7 @@ #ifndef ZEPHYR_INCLUDE_BLUETOOTH_ADDR_H_ #define ZEPHYR_INCLUDE_BLUETOOTH_ADDR_H_ +#include #include /** diff --git a/devices/ble_hci/common-hal/_bleio/hci_include/att.h b/devices/ble_hci/common-hal/_bleio/hci_include/att.h new file mode 100644 index 0000000000..8117a48f45 --- /dev/null +++ b/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/devices/ble_hci/common-hal/_bleio/hci_include/att_internal.h b/devices/ble_hci/common-hal/_bleio/hci_include/att_internal.h new file mode 100644 index 0000000000..1c75275daa --- /dev/null +++ b/devices/ble_hci/common-hal/_bleio/hci_include/att_internal.h @@ -0,0 +1,266 @@ +// CircuitPython: Adapted from Zephyr include file. + +/* att_internal.h - Attribute protocol handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +// for __packed +#include + +#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[0]; +} __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[0]; +} __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 { + struct bt_att_handle_group list[0]; +} __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[0]; +} __packed; + +struct bt_att_data { + uint16_t handle; + uint8_t value[0]; +} __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[0]; +} __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 value[0]; +} __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 value[0]; +} __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 { + uint16_t handles[0]; +} __packed; + +/* Read Multiple Respose */ +#define BT_ATT_OP_READ_MULT_RSP 0x0f +struct bt_att_read_mult_rsp { + uint8_t value[0]; +} __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[0]; +} __packed; + +struct bt_att_group_data { + uint16_t start_handle; + uint16_t end_handle; + uint8_t value[0]; +} __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[0]; +} __packed; + +/* Write Request */ +#define BT_ATT_OP_WRITE_REQ 0x12 +struct bt_att_write_req { + uint16_t handle; + uint8_t value[0]; +} __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[0]; +} __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[0]; +} __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[0]; +} __packed; + +/* Handle Value Indication */ +#define BT_ATT_OP_INDICATE 0x1d +struct bt_att_indicate { + uint16_t handle; + uint8_t value[0]; +} __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 { + uint16_t handles[0]; +} __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[0]; +} __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[0]; +} __packed; + +/* Write Command */ +#define BT_ATT_OP_WRITE_CMD 0x52 +struct bt_att_write_cmd { + uint16_t handle; + uint8_t value[0]; +} __packed; + +/* Signed Write Command */ +#define BT_ATT_OP_SIGNED_WRITE_CMD 0xd2 +struct bt_att_signed_write_cmd { + uint16_t handle; + uint8_t value[0]; +} __packed; diff --git a/devices/ble_hci/common-hal/_bleio/hci_include/hci.h b/devices/ble_hci/common-hal/_bleio/hci_include/hci.h index 2de58b3d89..6c3a2b5bd0 100644 --- a/devices/ble_hci/common-hal/_bleio/hci_include/hci.h +++ b/devices/ble_hci/common-hal/_bleio/hci_include/hci.h @@ -1,4 +1,5 @@ // CircuitPython: Adapted from Zephyr include file. + /* hci.h - Bluetooth Host Control Interface definitions */ /* diff --git a/devices/ble_hci/common-hal/_bleio/hci_include/l2cap_internal.h b/devices/ble_hci/common-hal/_bleio/hci_include/l2cap_internal.h new file mode 100644 index 0000000000..bed311cf3c --- /dev/null +++ b/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 +// for __packed +#include + +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[0]; +} __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[0]; +} __packed; + +#define BT_L2CAP_CONF_RSP 0x05 +struct bt_l2cap_conf_rsp { + uint16_t scid; + uint16_t flags; + uint16_t result; + uint8_t data[0]; +} __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[0]; +} __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[0]; +} __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[0]; +} __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[0]; +} __packed; + +#define BT_L2CAP_ECRED_RECONF_REQ 0x19 +struct bt_l2cap_ecred_reconf_req { + uint16_t mtu; + uint16_t mps; + uint16_t scid[0]; +} __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/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 0dd31904f2..e2850e63fc 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -318,8 +318,10 @@ SRC_COMMON_HAL_ALL = \ watchdog/__init__.c \ ifeq ($(CIRCUITPY_BLEIO_HCI),1) +# Helper code for _bleio HCI. SRC_C += \ - common-hal/_bleio/hci_api.c \ + common-hal/_bleio/att.c \ + common-hal/_bleio/hci.c \ endif diff --git a/py/misc.h b/py/misc.h index 673568f226..abcc7edf74 100644 --- a/py/misc.h +++ b/py/misc.h @@ -120,6 +120,8 @@ size_t m_get_peak_bytes_allocated(void); // align ptr to the nearest multiple of "alignment" #define MP_ALIGN(ptr, alignment) (void*)(((uintptr_t)(ptr) + ((alignment) - 1)) & ~((alignment) - 1)) +#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) + /** unichar / UTF-8 *********************************************/ #if MICROPY_PY_BUILTINS_STR_UNICODE diff --git a/py/py.mk b/py/py.mk index 3cb505920c..62050519b3 100644 --- a/py/py.mk +++ b/py/py.mk @@ -19,7 +19,7 @@ endif QSTR_GLOBAL_DEPENDENCIES += $(PY_SRC)/mpconfig.h mpconfigport.h # some code is performance bottleneck and compiled with other optimization options -CSUPEROPT = -O3 +_CSUPEROPT = -O3 # this sets the config file for FatFs CFLAGS_MOD += -DFFCONF_H=\"lib/oofatfs/ffconf.h\"