hci early wip; refactor supervisor bluetooth.c for nrf: tested
This commit is contained in:
parent
bc4c74517a
commit
759929c24a
717
devices/ble_hci/common-hal/_bleio/Adapter.c
Normal file
717
devices/ble_hci/common-hal/_bleio/Adapter.c
Normal file
@ -0,0 +1,717 @@
|
|||||||
|
/*
|
||||||
|
* 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 "bonding.h"
|
||||||
|
|
||||||
|
#include "py/gc.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/nvm/ByteArray.h"
|
||||||
|
#include "shared-bindings/_bleio/Connection.h"
|
||||||
|
#include "shared-bindings/_bleio/ScanEntry.h"
|
||||||
|
#include "shared-bindings/time/__init__.h"
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
#ifndef BLEIO_VS_UUID_COUNT
|
||||||
|
#define BLEIO_VS_UUID_COUNT 75
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BLEIO_HVN_TX_QUEUE_SIZE
|
||||||
|
#define BLEIO_HVN_TX_QUEUE_SIZE 9
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BLEIO_CENTRAL_ROLE_COUNT
|
||||||
|
#define BLEIO_CENTRAL_ROLE_COUNT 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BLEIO_PERIPH_ROLE_COUNT
|
||||||
|
#define BLEIO_PERIPH_ROLE_COUNT 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BLEIO_ATTR_TAB_SIZE
|
||||||
|
#define BLEIO_ATTR_TAB_SIZE (BLE_GATTS_ATTR_TAB_SIZE_DEFAULT * 5)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC void get_address(bleio_adapter_obj_t *self, ble_gap_addr_t *address) {
|
||||||
|
// check_nrf_error(sd_ble_gap_addr_get(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
char default_ble_name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0, 0 , 0};
|
||||||
|
|
||||||
|
STATIC void bleio_adapter_reset_name(bleio_adapter_obj_t *self) {
|
||||||
|
uint8_t len = sizeof(default_ble_name) - 1;
|
||||||
|
|
||||||
|
ble_gap_addr_t local_address;
|
||||||
|
get_address(self, &local_address);
|
||||||
|
|
||||||
|
default_ble_name[len - 4] = nibble_to_hex_lower[local_address.addr[1] >> 4 & 0xf];
|
||||||
|
default_ble_name[len - 3] = nibble_to_hex_lower[local_address.addr[1] & 0xf];
|
||||||
|
default_ble_name[len - 2] = nibble_to_hex_lower[local_address.addr[0] >> 4 & 0xf];
|
||||||
|
default_ble_name[len - 1] = nibble_to_hex_lower[local_address.addr[0] & 0xf];
|
||||||
|
default_ble_name[len] = '\0'; // for now we add null for compatibility with C ASCIIZ strings
|
||||||
|
|
||||||
|
common_hal_bleio_adapter_set_name(self, (char*) default_ble_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_adapter_construct(bleio_adapter_obj_t *self, mcu_pin_obj_t *tx, mcu_pin_obj_t *rx, mcu_pin_obj_t *rts, mcu_pin_obj_t *cts, uint32_t baudrate, uint32_t buffer_size, mcu_pin_obj_t* spi_cs, mcu_pin_obj_t* gpio0, mcu_pin_obj_t *reset, bool reset_high) {
|
||||||
|
self->tx = tx;
|
||||||
|
self->rx = rx;
|
||||||
|
self->rts = rts;
|
||||||
|
self->cts = cts;
|
||||||
|
self->baudrate = baudrate;
|
||||||
|
self->buffer_size = buffer_size;
|
||||||
|
self->spi_cs = spi_cs;
|
||||||
|
self->gpio0 = gpio0;
|
||||||
|
self->reset = reset;
|
||||||
|
self->reset_high = reset_high;
|
||||||
|
self->enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled) {
|
||||||
|
const bool is_enabled = common_hal_bleio_adapter_get_enabled(self);
|
||||||
|
|
||||||
|
// Don't enable or disable twice
|
||||||
|
if (is_enabled == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
// Enable adapter.
|
||||||
|
|
||||||
|
// common_hal UART takes rts and cts, but is currently not implemented for many ports.
|
||||||
|
// In addition, rts and cts may be pins that are not part of the serial peripheral
|
||||||
|
// used for tx and rx, so use GPIO for them.
|
||||||
|
common_hal_busio_uart_construct(&self->hci_uart, tx, rx, NULL, NULL, NULL, false,
|
||||||
|
BLEIO_HCI_BAUDRATE, 8, PARITY_NONE, 1, 0.0f,
|
||||||
|
BLEIO_HCI_BUFFER_SIZE, NULL, false);
|
||||||
|
|
||||||
|
// RTS is output, active high
|
||||||
|
common_hal_digitalio_digitalinout_construct(&self->rts_digitalio, self->rts);
|
||||||
|
common_hal_digitalio_digitalinout_switch_to_output(&self->rts_digitalio, false, DRIVE_MODE_PUSH_PULL);
|
||||||
|
|
||||||
|
// CTS is input.
|
||||||
|
common_hal_digitalio_digitalinout_construct(&self->cts_digitalio, self->cts);
|
||||||
|
|
||||||
|
// SPI_CS and GPI0 are used to signal entering BLE mode.
|
||||||
|
// SPI_CS should be low, and GPI0 should be high
|
||||||
|
common_hal_digitalio_digitalinout_construct(&self->spi_cs_digitalio, self->spi_cs);
|
||||||
|
common_hal_digitalio_digitalinout_construct(&self->gpio0_digitalio, self->gpi0);
|
||||||
|
common_hal_digitalio_digitalinout_switch_to_output(&self->spi_cs_digitalio, false, DRIVE_MODE_PUSH_PULL);
|
||||||
|
common_hal_digitalio_digitalinout_switch_to_output(&self->gpio0_digitalio, true DRIVE_MODE_PUSH_PULL);
|
||||||
|
|
||||||
|
// RESET is output, start in non-reset state.
|
||||||
|
common_hal_digitalio_digitalinout_construct(&self->reset_digitalio, self->reset);
|
||||||
|
common_hal_digitalio_digitalinout_switch_to_output(&self->reset_digitalio,
|
||||||
|
!self->reset_high, DRIVE_MODE_PUSH_PULL);
|
||||||
|
|
||||||
|
// Adapter will enter BLE mode on reset, based on SPI_CS and GPIO0 settings.
|
||||||
|
// Reset HCI processor. Assert reset for 100ms, then wait 750ms for reset to complete.
|
||||||
|
common_hal_digitalio_digitalinout_set_value(&self->reset_digitalio, self->reset_high);
|
||||||
|
mp_hal_delay_ms(100);
|
||||||
|
common_hal_digitalio_digitalinout_set_value(&self->reset_digitalio, !self->reset_high);
|
||||||
|
mp_hal_delay_ms(750);
|
||||||
|
|
||||||
|
// After reset, set SPI_CS high.
|
||||||
|
common_hal_digitalio_digitalinout_set_value(&self->spi_cs_digitalio, true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable.
|
||||||
|
common_hal_digitalio_digitalinout_set_value(&self->reset_digitalio, self->reset_high);
|
||||||
|
mp_hal_delay_ms(100);
|
||||||
|
common_hal_digitalio_digitalinout_set_value(&self->reset_digitalio, !self->reset_high);
|
||||||
|
|
||||||
|
// Free all pins.
|
||||||
|
common_hal_busio_uart_deinit(&self->hci_uart);
|
||||||
|
common_hal_digitalio_digitalinout_deinit(&self->rts_digitalio);
|
||||||
|
common_hal_digitalio_digitalinout_deinit(&self->cts_digitalio);
|
||||||
|
common_hal_digitalio_digitalinout_deinit(&self->spi_cs_digitalio);
|
||||||
|
common_hal_digitalio_digitalinout_deinit(&self->gpi0_digitalio);
|
||||||
|
common_hal_digitalio_digitalinout_deinit(&self->reset_digitalio);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
common_hal_bleio_adapter_set_enabled(self, true);
|
||||||
|
|
||||||
|
ble_gap_addr_t local_address;
|
||||||
|
get_address(self, &local_address);
|
||||||
|
|
||||||
|
bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t);
|
||||||
|
address->base.type = &bleio_address_type;
|
||||||
|
|
||||||
|
common_hal_bleio_address_construct(address, local_address.addr, local_address.addr_type);
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_str_t* common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self) {
|
||||||
|
uint16_t len = 0;
|
||||||
|
// sd_ble_gap_device_name_get(NULL, &len);
|
||||||
|
uint8_t buf[len];
|
||||||
|
// uint32_t err_code = sd_ble_gap_device_name_get(buf, &len);
|
||||||
|
// if (err_code != NRF_SUCCESS) {
|
||||||
|
// return NULL;
|
||||||
|
// }
|
||||||
|
return mp_obj_new_str((char*) buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char* name) {
|
||||||
|
// ble_gap_conn_sec_mode_t sec;
|
||||||
|
// sec.lv = 0;
|
||||||
|
// sec.sm = 0;
|
||||||
|
// sd_ble_gap_device_name_set(&sec, (const uint8_t*) name, strlen(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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) {
|
||||||
|
// sd_ble_gap_scan_stop();
|
||||||
|
shared_module_bleio_scanresults_set_done(self->scan_results, true);
|
||||||
|
// ble_drv_remove_event_handler(scan_on_ble_evt, self->scan_results);
|
||||||
|
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) {
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The nRF SD 6.1.0 can only do one concurrent advertisement so share the advertising handle.
|
||||||
|
uint8_t adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
|
||||||
|
|
||||||
|
STATIC void check_data_fit(size_t data_len, bool connectable) {
|
||||||
|
if (data_len > BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_MAX_SUPPORTED ||
|
||||||
|
(connectable && data_len > BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED)) {
|
||||||
|
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, uint8_t *advertising_data, uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len) {
|
||||||
|
// if (self->current_advertising_data != NULL && self->current_advertising_data == self->advertising_data) {
|
||||||
|
// return NRF_ERROR_BUSY;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // If the current advertising data isn't owned by the adapter then it must be an internal
|
||||||
|
// // advertisement that we should stop.
|
||||||
|
// if (self->current_advertising_data != NULL) {
|
||||||
|
// common_hal_bleio_adapter_stop_advertising(self);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// uint32_t err_code;
|
||||||
|
// bool extended = advertising_data_len > BLE_GAP_ADV_SET_DATA_SIZE_MAX ||
|
||||||
|
// scan_response_data_len > BLE_GAP_ADV_SET_DATA_SIZE_MAX;
|
||||||
|
|
||||||
|
// uint8_t adv_type;
|
||||||
|
// if (extended) {
|
||||||
|
// if (connectable) {
|
||||||
|
// adv_type = BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED;
|
||||||
|
// } else if (scan_response_data_len > 0) {
|
||||||
|
// adv_type = BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_SCANNABLE_UNDIRECTED;
|
||||||
|
// } else {
|
||||||
|
// adv_type = BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED;
|
||||||
|
// }
|
||||||
|
// } else if (connectable) {
|
||||||
|
// adv_type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;
|
||||||
|
// } else if (scan_response_data_len > 0) {
|
||||||
|
// adv_type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_SCANNABLE_UNDIRECTED;
|
||||||
|
// } else {
|
||||||
|
// adv_type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (anonymous) {
|
||||||
|
// ble_gap_privacy_params_t privacy = {
|
||||||
|
// .privacy_mode = BLE_GAP_PRIVACY_MODE_DEVICE_PRIVACY,
|
||||||
|
// .private_addr_type = BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE,
|
||||||
|
// // Rotate the keys one second after we're scheduled to stop
|
||||||
|
// // advertising. This prevents a potential race condition where we
|
||||||
|
// // fire off a beacon with the same advertising data but a new MAC
|
||||||
|
// // address just as we tear down the connection.
|
||||||
|
// .private_addr_cycle_s = timeout + 1,
|
||||||
|
// .p_device_irk = NULL,
|
||||||
|
// };
|
||||||
|
// err_code = sd_ble_gap_privacy_set(&privacy);
|
||||||
|
// } else {
|
||||||
|
// ble_gap_privacy_params_t privacy = {
|
||||||
|
// .privacy_mode = BLE_GAP_PRIVACY_MODE_OFF,
|
||||||
|
// .private_addr_type = BLE_GAP_ADDR_TYPE_PUBLIC,
|
||||||
|
// .private_addr_cycle_s = 0,
|
||||||
|
// .p_device_irk = NULL,
|
||||||
|
// };
|
||||||
|
// err_code = sd_ble_gap_privacy_set(&privacy);
|
||||||
|
// }
|
||||||
|
// if (err_code != NRF_SUCCESS) {
|
||||||
|
// return err_code;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ble_gap_adv_params_t adv_params = {
|
||||||
|
// .interval = SEC_TO_UNITS(interval, UNIT_0_625_MS),
|
||||||
|
// .properties.type = adv_type,
|
||||||
|
// .duration = SEC_TO_UNITS(timeout, UNIT_10_MS),
|
||||||
|
// .filter_policy = BLE_GAP_ADV_FP_ANY,
|
||||||
|
// .primary_phy = BLE_GAP_PHY_1MBPS,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const ble_gap_adv_data_t ble_gap_adv_data = {
|
||||||
|
// .adv_data.p_data = advertising_data,
|
||||||
|
// .adv_data.len = advertising_data_len,
|
||||||
|
// .scan_rsp_data.p_data = scan_response_data_len > 0 ? scan_response_data : NULL,
|
||||||
|
// .scan_rsp_data.len = scan_response_data_len,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// err_code = sd_ble_gap_adv_set_configure(&adv_handle, &ble_gap_adv_data, &adv_params);
|
||||||
|
// if (err_code != NRF_SUCCESS) {
|
||||||
|
// return err_code;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ble_drv_add_event_handler(advertising_on_ble_evt, self);
|
||||||
|
|
||||||
|
// vm_used_ble = true;
|
||||||
|
// err_code = sd_ble_gap_adv_start(adv_handle, BLE_CONN_CFG_TAG_CUSTOM);
|
||||||
|
// if (err_code != NRF_SUCCESS) {
|
||||||
|
// return err_code;
|
||||||
|
// }
|
||||||
|
// self->current_advertising_data = advertising_data;
|
||||||
|
// return NRF_SUCCESS;
|
||||||
|
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) {
|
||||||
|
if (self->current_advertising_data != NULL && self->current_advertising_data == self->advertising_data) {
|
||||||
|
mp_raise_bleio_BluetoothError(translate("Already advertising."));
|
||||||
|
}
|
||||||
|
// 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 > 31 && 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) {
|
||||||
|
if (anonymous) {
|
||||||
|
// The Nordic macro is in units of 10ms. Convert to seconds.
|
||||||
|
uint32_t adv_timeout_max_secs = UNITS_TO_SEC(BLE_GAP_ADV_TIMEOUT_LIMITED_MAX, UNIT_10_MS);
|
||||||
|
uint32_t rotate_timeout_max_secs = BLE_GAP_DEFAULT_PRIVATE_ADDR_CYCLE_INTERVAL_S;
|
||||||
|
timeout = MIN(adv_timeout_max_secs, rotate_timeout_max_secs);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timeout = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (SEC_TO_UNITS(timeout, UNIT_10_MS) > BLE_GAP_ADV_TIMEOUT_LIMITED_MAX) {
|
||||||
|
mp_raise_bleio_BluetoothError(translate("Timeout is too long: Maximum timeout length is %d seconds"),
|
||||||
|
UNITS_TO_SEC(BLE_GAP_ADV_TIMEOUT_LIMITED_MAX, UNIT_10_MS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The advertising data buffers must not move, because the SoftDevice depends on them.
|
||||||
|
// So make them long-lived and reuse them onwards.
|
||||||
|
if (self->advertising_data == NULL) {
|
||||||
|
self->advertising_data = (uint8_t *) gc_alloc(BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_MAX_SUPPORTED * sizeof(uint8_t), false, true);
|
||||||
|
}
|
||||||
|
if (self->scan_response_data == NULL) {
|
||||||
|
self->scan_response_data = (uint8_t *) gc_alloc(BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_MAX_SUPPORTED * sizeof(uint8_t), false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(self->advertising_data, advertising_data_bufinfo->buf, advertising_data_bufinfo->len);
|
||||||
|
memcpy(self->scan_response_data, scan_response_data_bufinfo->buf, scan_response_data_bufinfo->len);
|
||||||
|
|
||||||
|
// check_nrf_error(_common_hal_bleio_adapter_start_advertising(self, connectable, anonymous, timeout, interval,
|
||||||
|
// self->advertising_data,
|
||||||
|
// advertising_data_bufinfo->len,
|
||||||
|
// self->scan_response_data,
|
||||||
|
// scan_response_data_bufinfo->len));
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) {
|
||||||
|
// if (adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET)
|
||||||
|
// return;
|
||||||
|
|
||||||
|
// // TODO: Don't actually stop. Switch to advertising CircuitPython if we don't already have a connection.
|
||||||
|
// const uint32_t err_code = sd_ble_gap_adv_stop(adv_handle);
|
||||||
|
// self->current_advertising_data = NULL;
|
||||||
|
|
||||||
|
// if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) {
|
||||||
|
// check_nrf_error(err_code);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self) {
|
||||||
|
return self->current_advertising_data != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) {
|
||||||
|
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
|
||||||
|
bleio_connection_internal_t *connection = &bleio_connections[i];
|
||||||
|
if (connection->conn_handle != BLE_CONN_HANDLE_INVALID) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self) {
|
||||||
|
if (self->connection_objs != NULL) {
|
||||||
|
return self->connection_objs;
|
||||||
|
}
|
||||||
|
size_t total_connected = 0;
|
||||||
|
mp_obj_t items[BLEIO_TOTAL_CONNECTION_COUNT];
|
||||||
|
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
|
||||||
|
bleio_connection_internal_t *connection = &bleio_connections[i];
|
||||||
|
if (connection->conn_handle != 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) {
|
||||||
|
bonding_erase_storage();
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
common_hal_bleio_adapter_stop_scan(adapter);
|
||||||
|
if (adapter->current_advertising_data != NULL) {
|
||||||
|
common_hal_bleio_adapter_stop_advertising(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter->connection_objs = NULL;
|
||||||
|
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
|
||||||
|
bleio_connection_internal_t *connection = &bleio_connections[i];
|
||||||
|
// Disconnect all connections 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;
|
||||||
|
}
|
||||||
|
}
|
71
devices/ble_hci/common-hal/_bleio/Adapter.h
Normal file
71
devices/ble_hci/common-hal/_bleio/Adapter.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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/Connection.h"
|
||||||
|
#include "shared-bindings/_bleio/ScanResults.h"
|
||||||
|
#include "shared-bindings/microcontroller/Pin.h"
|
||||||
|
|
||||||
|
extern bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT];
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
uint8_t* advertising_data;
|
||||||
|
uint8_t* scan_response_data;
|
||||||
|
uint8_t* current_advertising_data;
|
||||||
|
bleio_scanresults_obj_t* scan_results;
|
||||||
|
mp_obj_t name;
|
||||||
|
mp_obj_tuple_t *connection_objs;
|
||||||
|
mcu_pin_obj_t* tx;
|
||||||
|
mcu_pin_obj_t* rx;
|
||||||
|
mcu_pin_obj_t* rts;
|
||||||
|
mcu_pin_obj_t* cts;
|
||||||
|
uint32_t baudrate;
|
||||||
|
uint16_t buffer_size;
|
||||||
|
mcu_pin_obj_t* spi_cs;
|
||||||
|
mcu_pin_obj_t* gpio0;
|
||||||
|
mcu_pin_obj_t* reset;
|
||||||
|
bool reset_high;
|
||||||
|
busio_uart_obj_t hci_uart;
|
||||||
|
digitalio_digitalinout_obj_t rts_digitalio;
|
||||||
|
digitalio_digitalinout_obj_t cts_digitalio;
|
||||||
|
digitalio_digitalinout_obj_t spi_cs_digitalio;
|
||||||
|
digitalio_digitalinout_obj_t gpio0_digitalio;
|
||||||
|
digitalio_digitalinout_obj_t reset_digitalio;
|
||||||
|
bool enabled;
|
||||||
|
} bleio_adapter_obj_t;
|
||||||
|
|
||||||
|
void bleio_adapter_gc_collect(bleio_adapter_obj_t* adapter);
|
||||||
|
void bleio_adapter_reset(bleio_adapter_obj_t* adapter);
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_ADAPTER_H
|
60
devices/ble_hci/common-hal/_bleio/Attribute.c
Normal file
60
devices/ble_hci/common-hal/_bleio/Attribute.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "shared-bindings/_bleio/Attribute.h"
|
||||||
|
|
||||||
|
// Convert a _bleio security mode to a ble_gap_conn_sec_mode_t setting.
|
||||||
|
void bleio_attribute_gatts_set_security_mode(ble_gap_conn_sec_mode_t *perm, bleio_attribute_security_mode_t security_mode) {
|
||||||
|
switch (security_mode) {
|
||||||
|
case SECURITY_MODE_NO_ACCESS:
|
||||||
|
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(perm);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SECURITY_MODE_OPEN:
|
||||||
|
BLE_GAP_CONN_SEC_MODE_SET_OPEN(perm);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SECURITY_MODE_ENC_NO_MITM:
|
||||||
|
BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(perm);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SECURITY_MODE_ENC_WITH_MITM:
|
||||||
|
BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(perm);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SECURITY_MODE_LESC_ENC_WITH_MITM:
|
||||||
|
BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(perm);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SECURITY_MODE_SIGNED_NO_MITM:
|
||||||
|
BLE_GAP_CONN_SEC_MODE_SET_SIGNED_NO_MITM(perm);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SECURITY_MODE_SIGNED_WITH_MITM:
|
||||||
|
BLE_GAP_CONN_SEC_MODE_SET_SIGNED_WITH_MITM(perm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
34
devices/ble_hci/common-hal/_bleio/Attribute.h
Normal file
34
devices/ble_hci/common-hal/_bleio/Attribute.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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_ATTRIBUTE_H
|
||||||
|
#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_ATTRIBUTE_H
|
||||||
|
|
||||||
|
#include "shared-module/_bleio/Attribute.h"
|
||||||
|
|
||||||
|
extern void bleio_attribute_gatts_set_security_mode(ble_gap_conn_sec_mode_t *perm, bleio_attribute_security_mode_t security_mode);
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_ATTRIBUTE_H
|
266
devices/ble_hci/common-hal/_bleio/Characteristic.c
Normal file
266
devices/ble_hci/common-hal/_bleio/Characteristic.c
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
/*
|
||||||
|
* 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/Descriptor.h"
|
||||||
|
#include "shared-bindings/_bleio/Service.h"
|
||||||
|
|
||||||
|
#include "common-hal/_bleio/Adapter.h"
|
||||||
|
#include "common-hal/_bleio/bonding.h"
|
||||||
|
|
||||||
|
STATIC uint16_t characteristic_get_cccd(uint16_t cccd_handle, uint16_t conn_handle) {
|
||||||
|
uint16_t cccd;
|
||||||
|
ble_gatts_value_t value = {
|
||||||
|
.p_value = (uint8_t*) &cccd,
|
||||||
|
.len = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32_t err_code = sd_ble_gatts_value_get(conn_handle, cccd_handle, &value);
|
||||||
|
|
||||||
|
if (err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING) {
|
||||||
|
// CCCD is not set, so say that neither Notify nor Indicate is enabled.
|
||||||
|
cccd = 0;
|
||||||
|
} else {
|
||||||
|
check_nrf_error(err_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cccd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, uint16_t hvx_type) {
|
||||||
|
uint16_t hvx_len = bufinfo->len;
|
||||||
|
|
||||||
|
ble_gatts_hvx_params_t hvx_params = {
|
||||||
|
.handle = handle,
|
||||||
|
.type = hvx_type,
|
||||||
|
.offset = 0,
|
||||||
|
.p_len = &hvx_len,
|
||||||
|
.p_data = bufinfo->buf,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
const uint32_t err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params);
|
||||||
|
if (err_code == NRF_SUCCESS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// TX buffer is full
|
||||||
|
// We could wait for an event indicating the write is complete, but just retrying is easier.
|
||||||
|
if (err_code == NRF_ERROR_RESOURCES) {
|
||||||
|
RUN_BACKGROUND_TASKS;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some real error has occurred.
|
||||||
|
check_nrf_error(err_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
self->service = service;
|
||||||
|
self->uuid = uuid;
|
||||||
|
self->handle = BLE_GATT_HANDLE_INVALID;
|
||||||
|
self->props = props;
|
||||||
|
self->read_perm = read_perm;
|
||||||
|
self->write_perm = write_perm;
|
||||||
|
self->descriptor_list = NULL;
|
||||||
|
|
||||||
|
const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX;
|
||||||
|
if (max_length < 0 || max_length > max_length_max) {
|
||||||
|
mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"),
|
||||||
|
max_length_max, fixed_length ? "True" : "False");
|
||||||
|
}
|
||||||
|
self->max_length = max_length;
|
||||||
|
self->fixed_length = fixed_length;
|
||||||
|
|
||||||
|
if (service->is_remote) {
|
||||||
|
self->handle = handle;
|
||||||
|
} else {
|
||||||
|
common_hal_bleio_service_add_characteristic(self->service, self, initial_value_bufinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initial_value_bufinfo != NULL) {
|
||||||
|
common_hal_bleio_characteristic_set_value(self, initial_value_bufinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bleio_descriptor_obj_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) {
|
||||||
|
return self->descriptor_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self) {
|
||||||
|
return self->service;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t* buf, size_t len) {
|
||||||
|
// Do GATT operations only if this characteristic has been added to a registered service.
|
||||||
|
if (self->handle != BLE_GATT_HANDLE_INVALID) {
|
||||||
|
uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
|
||||||
|
if (common_hal_bleio_service_get_is_remote(self->service)) {
|
||||||
|
// self->value is set by evt handler.
|
||||||
|
return common_hal_bleio_gattc_read(self->handle, conn_handle, buf, len);
|
||||||
|
} else {
|
||||||
|
// conn_handle is ignored for non-system attributes.
|
||||||
|
return common_hal_bleio_gatts_read(self->handle, conn_handle, buf, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
|
||||||
|
// Last argument is true if write-no-reponse desired.
|
||||||
|
common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo,
|
||||||
|
(self->props & CHAR_PROP_WRITE_NO_RESPONSE));
|
||||||
|
} else {
|
||||||
|
// Always write the value locally even if no connections are active.
|
||||||
|
// conn_handle is ignored for non-system attributes, so we use BLE_CONN_HANDLE_INVALID.
|
||||||
|
common_hal_bleio_gatts_write(self->handle, BLE_CONN_HANDLE_INVALID, bufinfo);
|
||||||
|
// Check to see if we need to notify or indicate any active connections.
|
||||||
|
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
|
||||||
|
bleio_connection_internal_t *connection = &bleio_connections[i];
|
||||||
|
uint16_t conn_handle = connection->conn_handle;
|
||||||
|
if (connection->conn_handle == BLE_CONN_HANDLE_INVALID) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t cccd = 0;
|
||||||
|
|
||||||
|
const bool notify = self->props & CHAR_PROP_NOTIFY;
|
||||||
|
const bool indicate = self->props & CHAR_PROP_INDICATE;
|
||||||
|
if (notify | indicate) {
|
||||||
|
cccd = characteristic_get_cccd(self->cccd_handle, conn_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's possible that both notify and indicate are set.
|
||||||
|
if (notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) {
|
||||||
|
characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_NOTIFICATION);
|
||||||
|
}
|
||||||
|
if (indicate && (cccd & BLE_GATT_HVX_INDICATION)) {
|
||||||
|
characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_INDICATION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
ble_uuid_t desc_uuid;
|
||||||
|
bleio_uuid_convert_to_nrf_ble_uuid(descriptor->uuid, &desc_uuid);
|
||||||
|
|
||||||
|
ble_gatts_attr_md_t desc_attr_md = {
|
||||||
|
// Data passed is not in a permanent location and should be copied.
|
||||||
|
.vloc = BLE_GATTS_VLOC_STACK,
|
||||||
|
.vlen = !descriptor->fixed_length,
|
||||||
|
};
|
||||||
|
|
||||||
|
bleio_attribute_gatts_set_security_mode(&desc_attr_md.read_perm, descriptor->read_perm);
|
||||||
|
bleio_attribute_gatts_set_security_mode(&desc_attr_md.write_perm, descriptor->write_perm);
|
||||||
|
|
||||||
|
mp_buffer_info_t desc_value_bufinfo;
|
||||||
|
mp_get_buffer_raise(descriptor->value, &desc_value_bufinfo, MP_BUFFER_READ);
|
||||||
|
|
||||||
|
ble_gatts_attr_t desc_attr = {
|
||||||
|
.p_uuid = &desc_uuid,
|
||||||
|
.p_attr_md = &desc_attr_md,
|
||||||
|
.init_len = desc_value_bufinfo.len,
|
||||||
|
.p_value = desc_value_bufinfo.buf,
|
||||||
|
.init_offs = 0,
|
||||||
|
.max_len = descriptor->max_length,
|
||||||
|
};
|
||||||
|
|
||||||
|
check_nrf_error(sd_ble_gatts_descriptor_add(self->handle, &desc_attr, &descriptor->handle));
|
||||||
|
|
||||||
|
descriptor->next = self->descriptor_list;
|
||||||
|
self->descriptor_list = descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate) {
|
||||||
|
if (self->cccd_handle == BLE_GATT_HANDLE_INVALID) {
|
||||||
|
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 ? BLE_GATT_HVX_NOTIFICATION : 0) |
|
||||||
|
(indicate ? BLE_GATT_HVX_INDICATION : 0);
|
||||||
|
|
||||||
|
ble_gattc_write_params_t write_params = {
|
||||||
|
.write_op = BLE_GATT_OP_WRITE_REQ,
|
||||||
|
.handle = self->cccd_handle,
|
||||||
|
.p_value = (uint8_t *) &cccd_value,
|
||||||
|
.len = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params);
|
||||||
|
if (err_code == NRF_SUCCESS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write with response will return NRF_ERROR_BUSY if the response has not been received.
|
||||||
|
// Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending.
|
||||||
|
if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) {
|
||||||
|
// We could wait for an event indicating the write is complete, but just retrying is easier.
|
||||||
|
RUN_BACKGROUND_TASKS;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some real error occurred.
|
||||||
|
check_nrf_error(err_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
55
devices/ble_hci/common-hal/_bleio/Characteristic.h
Normal file
55
devices/ble_hci/common-hal/_bleio/Characteristic.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
uint16_t max_length;
|
||||||
|
bool fixed_length;
|
||||||
|
uint16_t handle;
|
||||||
|
bleio_characteristic_properties_t props;
|
||||||
|
bleio_attribute_security_mode_t read_perm;
|
||||||
|
bleio_attribute_security_mode_t write_perm;
|
||||||
|
bleio_descriptor_obj_t *descriptor_list;
|
||||||
|
uint16_t user_desc_handle;
|
||||||
|
uint16_t cccd_handle;
|
||||||
|
uint16_t sccd_handle;
|
||||||
|
} bleio_characteristic_obj_t;
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_CHARACTERISTIC_H
|
151
devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.c
Normal file
151
devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.c
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* 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 "lib/utils/interrupt_char.h"
|
||||||
|
#include "py/runtime.h"
|
||||||
|
#include "py/stream.h"
|
||||||
|
|
||||||
|
#include "shared-bindings/_bleio/__init__.h"
|
||||||
|
#include "shared-bindings/_bleio/Connection.h"
|
||||||
|
#include "supervisor/shared/tick.h"
|
||||||
|
#include "common-hal/_bleio/CharacteristicBuffer.h"
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
uint8_t is_nested_critical_region;
|
||||||
|
sd_nvic_critical_region_enter(&is_nested_critical_region);
|
||||||
|
ringbuf_put_n(&self->ringbuf, data, len);
|
||||||
|
sd_nvic_critical_region_exit(is_nested_critical_region);
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC bool characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) {
|
||||||
|
bleio_characteristic_buffer_obj_t *self = (bleio_characteristic_buffer_obj_t *) param;
|
||||||
|
switch (ble_evt->header.evt_id) {
|
||||||
|
case BLE_GATTS_EVT_WRITE: {
|
||||||
|
// A client wrote to this server characteristic.
|
||||||
|
|
||||||
|
ble_gatts_evt_write_t *evt_write = &ble_evt->evt.gatts_evt.params.write;
|
||||||
|
// Event handle must match the handle for my characteristic.
|
||||||
|
if (evt_write->handle == self->characteristic->handle) {
|
||||||
|
write_to_ringbuf(self, evt_write->data, evt_write->len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BLE_GATTC_EVT_HVX: {
|
||||||
|
// A remote service wrote to this characteristic.
|
||||||
|
|
||||||
|
ble_gattc_evt_hvx_t* evt_hvx = &ble_evt->evt.gattc_evt.params.hvx;
|
||||||
|
// Must be a notification, and event handle must match the handle for my characteristic.
|
||||||
|
if (evt_hvx->type == BLE_GATT_HVX_NOTIFICATION &&
|
||||||
|
evt_hvx->handle == self->characteristic->handle) {
|
||||||
|
write_to_ringbuf(self, evt_hvx->data, evt_hvx->len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
ble_drv_add_event_handler(characteristic_buffer_on_ble_evt, 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy received data. Lock out write interrupt handler while copying.
|
||||||
|
uint8_t is_nested_critical_region;
|
||||||
|
sd_nvic_critical_region_enter(&is_nested_critical_region);
|
||||||
|
|
||||||
|
uint32_t num_bytes_read = ringbuf_get_n(&self->ringbuf, data, len);
|
||||||
|
|
||||||
|
// Writes now OK.
|
||||||
|
sd_nvic_critical_region_exit(is_nested_critical_region);
|
||||||
|
|
||||||
|
return num_bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) {
|
||||||
|
uint8_t is_nested_critical_region;
|
||||||
|
sd_nvic_critical_region_enter(&is_nested_critical_region);
|
||||||
|
uint16_t count = ringbuf_num_filled(&self->ringbuf);
|
||||||
|
sd_nvic_critical_region_exit(is_nested_critical_region);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) {
|
||||||
|
// prevent conflict with uart irq
|
||||||
|
uint8_t is_nested_critical_region;
|
||||||
|
sd_nvic_critical_region_enter(&is_nested_critical_region);
|
||||||
|
ringbuf_clear(&self->ringbuf);
|
||||||
|
sd_nvic_critical_region_exit(is_nested_critical_region);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
ble_drv_remove_event_handler(characteristic_buffer_on_ble_evt, self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self) {
|
||||||
|
return self->characteristic != NULL &&
|
||||||
|
self->characteristic->service != NULL &&
|
||||||
|
(!self->characteristic->service->is_remote ||
|
||||||
|
(self->characteristic->service->connection != MP_OBJ_NULL &&
|
||||||
|
common_hal_bleio_connection_get_connected(self->characteristic->service->connection)));
|
||||||
|
}
|
41
devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.h
Normal file
41
devices/ble_hci/common-hal/_bleio/CharacteristicBuffer.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MICROPY_INCLUDED_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;
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_CHARACTERISTICBUFFER_H
|
768
devices/ble_hci/common-hal/_bleio/Connection.c
Normal file
768
devices/ble_hci/common-hal/_bleio/Connection.c
Normal file
@ -0,0 +1,768 @@
|
|||||||
|
/*
|
||||||
|
* 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 <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "lib/utils/interrupt_char.h"
|
||||||
|
#include "py/gc.h"
|
||||||
|
#include "py/objlist.h"
|
||||||
|
#include "py/objstr.h"
|
||||||
|
#include "py/qstr.h"
|
||||||
|
#include "py/runtime.h"
|
||||||
|
#include "shared-bindings/_bleio/__init__.h"
|
||||||
|
#include "shared-bindings/_bleio/Adapter.h"
|
||||||
|
#include "shared-bindings/_bleio/Attribute.h"
|
||||||
|
#include "shared-bindings/_bleio/Characteristic.h"
|
||||||
|
#include "shared-bindings/_bleio/Service.h"
|
||||||
|
#include "shared-bindings/_bleio/UUID.h"
|
||||||
|
#include "supervisor/shared/tick.h"
|
||||||
|
|
||||||
|
#include "common-hal/_bleio/bonding.h"
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
static bleio_service_obj_t *m_char_discovery_service;
|
||||||
|
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) {
|
||||||
|
self->remote_service_list = NULL;
|
||||||
|
|
||||||
|
self->conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||||
|
self->pair_status = PAIR_NOT_PAIRED;
|
||||||
|
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) {
|
||||||
|
sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond) {
|
||||||
|
self->pair_status = PAIR_WAITING;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return 1.25f * self->conn_params.min_conn_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the current negotiated MTU length, minus overhead.
|
||||||
|
mp_int_t common_hal_bleio_connection_get_max_packet_length(bleio_connection_internal_t *self) {
|
||||||
|
return (self->mtu == 0 ? BLE_GATT_ATT_MTU_DEFAULT : 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) {
|
||||||
|
bleio_service_obj_t* tail = connection->remote_service_list;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
service->next = tail;
|
||||||
|
tail = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection->remote_service_list = tail;
|
||||||
|
|
||||||
|
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(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(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 = 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.
|
||||||
|
const bleio_service_obj_t *service = self->remote_service_list;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bleio_service_obj_t *service = self->remote_service_list;
|
||||||
|
while (service != NULL) {
|
||||||
|
// Skip the service if it had an unknown (unregistered) UUID.
|
||||||
|
if (service->uuid == NULL) {
|
||||||
|
service = service->next;
|
||||||
|
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_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.
|
||||||
|
const bleio_descriptor_obj_t *descriptor = characteristic->descriptor_list;
|
||||||
|
next_desc_start_handle = descriptor->handle + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
service = service->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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 = service_linked_list_to_tuple(self->connection->remote_service_list);
|
||||||
|
self->connection->remote_service_list = NULL;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
90
devices/ble_hci/common-hal/_bleio/Connection.h
Normal file
90
devices/ble_hci/common-hal/_bleio/Connection.h
Normal file
@ -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 "common-hal/_bleio/bonding.h"
|
||||||
|
#include "shared-module/_bleio/Address.h"
|
||||||
|
#include "common-hal/_bleio/Service.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PAIR_NOT_PAIRED,
|
||||||
|
PAIR_WAITING,
|
||||||
|
PAIR_PAIRED,
|
||||||
|
} pair_status_t;
|
||||||
|
|
||||||
|
// We split the Connection object into two so that the internal mechanics can live outside of the
|
||||||
|
// VM. If it were one object, then we'd risk user code seeing a connection object of theirs be
|
||||||
|
// reused.
|
||||||
|
typedef struct {
|
||||||
|
uint16_t conn_handle;
|
||||||
|
bool is_central;
|
||||||
|
// Remote services discovered when this peripheral is acting as a client.
|
||||||
|
bleio_service_obj_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;
|
||||||
|
ble_drv_evt_handler_entry_t handler_entry;
|
||||||
|
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.
|
||||||
|
volatile bool do_bond_cccds;
|
||||||
|
// Request that security key info for this connection be saved.
|
||||||
|
volatile bool do_bond_keys;
|
||||||
|
// Time of setting do_bond_ccds: we delay a bit to consolidate multiple CCCD changes
|
||||||
|
// into one write. Time is currently in ticks_ms.
|
||||||
|
uint64_t do_bond_cccds_request_time;
|
||||||
|
} bleio_connection_internal_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
bleio_connection_internal_t* connection;
|
||||||
|
// The HCI disconnect reason.
|
||||||
|
uint8_t disconnect_reason;
|
||||||
|
} bleio_connection_obj_t;
|
||||||
|
|
||||||
|
bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in);
|
||||||
|
|
||||||
|
uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self);
|
||||||
|
mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t* connection);
|
||||||
|
bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle);
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_CONNECTION_H
|
97
devices/ble_hci/common-hal/_bleio/Descriptor.c
Normal file
97
devices/ble_hci/common-hal/_bleio/Descriptor.c
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
uint16_t conn_handle = bleio_connection_get_conn_handle(self->characteristic->service->connection);
|
||||||
|
if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) {
|
||||||
|
return common_hal_bleio_gattc_read(self->handle, conn_handle, buf, len);
|
||||||
|
} else {
|
||||||
|
return common_hal_bleio_gatts_read(self->handle, conn_handle, buf, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
uint16_t conn_handle = bleio_connection_get_conn_handle(self->characteristic->service->connection);
|
||||||
|
if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) {
|
||||||
|
// false means WRITE_REQ, not write-no-response
|
||||||
|
common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, false);
|
||||||
|
} else {
|
||||||
|
common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
devices/ble_hci/common-hal/_bleio/Descriptor.h
Normal file
53
devices/ble_hci/common-hal/_bleio/Descriptor.h
Normal file
@ -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
|
402
devices/ble_hci/common-hal/_bleio/PacketBuffer.c
Normal file
402
devices/ble_hci/common-hal/_bleio/PacketBuffer.c
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
/*
|
||||||
|
* 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 "lib/utils/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.
|
||||||
|
uint8_t is_nested_critical_region;
|
||||||
|
sd_nvic_critical_region_enter(&is_nested_critical_region);
|
||||||
|
// 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);
|
||||||
|
sd_nvic_critical_region_exit(is_nested_critical_region);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
uint16_t conn_handle = self->conn_handle;
|
||||||
|
uint32_t err_code;
|
||||||
|
if (self->client) {
|
||||||
|
ble_gattc_write_params_t write_params = {
|
||||||
|
.write_op = self->write_type,
|
||||||
|
.handle = self->characteristic->handle,
|
||||||
|
.p_value = self->outgoing[self->pending_index],
|
||||||
|
.len = self->pending_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
err_code = sd_ble_gattc_write(conn_handle, &write_params);
|
||||||
|
} else {
|
||||||
|
uint16_t hvx_len = self->pending_size;
|
||||||
|
|
||||||
|
ble_gatts_hvx_params_t hvx_params = {
|
||||||
|
.handle = self->characteristic->handle,
|
||||||
|
.type = self->write_type,
|
||||||
|
.offset = 0,
|
||||||
|
.p_len = &hvx_len,
|
||||||
|
.p_data = self->outgoing[self->pending_index],
|
||||||
|
};
|
||||||
|
err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params);
|
||||||
|
}
|
||||||
|
if (err_code != NRF_SUCCESS) {
|
||||||
|
// On error, simply skip updating the pending buffers so that the next HVC or WRITE
|
||||||
|
// complete event triggers another attempt.
|
||||||
|
return err_code;
|
||||||
|
}
|
||||||
|
self->pending_size = 0;
|
||||||
|
self->pending_index = (self->pending_index + 1) % 2;
|
||||||
|
self->packet_queued = true;
|
||||||
|
}
|
||||||
|
return NRF_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC bool packet_buffer_on_ble_client_evt(ble_evt_t *ble_evt, void *param) {
|
||||||
|
const uint16_t evt_id = ble_evt->header.evt_id;
|
||||||
|
// Check if this is a GATTC event so we can make sure the conn_handle is valid.
|
||||||
|
if (evt_id < BLE_GATTC_EVT_BASE || evt_id > BLE_GATTC_EVT_LAST) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t conn_handle = ble_evt->evt.gattc_evt.conn_handle;
|
||||||
|
bleio_packet_buffer_obj_t *self = (bleio_packet_buffer_obj_t *) param;
|
||||||
|
if (conn_handle != self->conn_handle) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (evt_id) {
|
||||||
|
case BLE_GATTC_EVT_HVX: {
|
||||||
|
// A remote service wrote to this characteristic.
|
||||||
|
ble_gattc_evt_hvx_t* evt_hvx = &ble_evt->evt.gattc_evt.params.hvx;
|
||||||
|
// Must be a notification, and event handle must match the handle for my characteristic.
|
||||||
|
if (evt_hvx->handle == self->characteristic->handle) {
|
||||||
|
write_to_ringbuf(self, evt_hvx->data, evt_hvx->len);
|
||||||
|
if (evt_hvx->type == BLE_GATT_HVX_INDICATION) {
|
||||||
|
sd_ble_gattc_hv_confirm(conn_handle, evt_hvx->handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE: {
|
||||||
|
queue_next_write(self);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BLE_GATTC_EVT_WRITE_RSP: {
|
||||||
|
queue_next_write(self);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC bool packet_buffer_on_ble_server_evt(ble_evt_t *ble_evt, void *param) {
|
||||||
|
bleio_packet_buffer_obj_t *self = (bleio_packet_buffer_obj_t *) param;
|
||||||
|
switch (ble_evt->header.evt_id) {
|
||||||
|
case BLE_GATTS_EVT_WRITE: {
|
||||||
|
uint16_t conn_handle = ble_evt->evt.gatts_evt.conn_handle;
|
||||||
|
// A client wrote to this server characteristic.
|
||||||
|
|
||||||
|
ble_gatts_evt_write_t *evt_write = &ble_evt->evt.gatts_evt.params.write;
|
||||||
|
|
||||||
|
// Event handle must match the handle for my characteristic.
|
||||||
|
if (evt_write->handle == self->characteristic->handle) {
|
||||||
|
if (self->conn_handle == BLE_CONN_HANDLE_INVALID) {
|
||||||
|
self->conn_handle = conn_handle;
|
||||||
|
} else if (self->conn_handle != conn_handle) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
write_to_ringbuf(self, evt_write->data, evt_write->len);
|
||||||
|
} else if (evt_write->handle == self->characteristic->cccd_handle) {
|
||||||
|
uint16_t cccd = *((uint16_t*) evt_write->data);
|
||||||
|
if (cccd & BLE_GATT_HVX_NOTIFICATION) {
|
||||||
|
self->conn_handle = conn_handle;
|
||||||
|
} else {
|
||||||
|
self->conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BLE_GAP_EVT_DISCONNECTED: {
|
||||||
|
if (self->conn_handle == ble_evt->evt.gap_evt.conn_handle) {
|
||||||
|
self->conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case BLE_GATTS_EVT_HVN_TX_COMPLETE: {
|
||||||
|
queue_next_write(self);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_packet_buffer_construct(
|
||||||
|
bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic,
|
||||||
|
size_t buffer_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) + characteristic->max_length), 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(characteristic->max_length, false);
|
||||||
|
self->outgoing[1] = m_malloc(characteristic->max_length, false);
|
||||||
|
} else {
|
||||||
|
self->outgoing[0] = NULL;
|
||||||
|
self->outgoing[1] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->client) {
|
||||||
|
ble_drv_add_event_handler(packet_buffer_on_ble_client_evt, self);
|
||||||
|
if (incoming) {
|
||||||
|
// Prefer notify if both are available.
|
||||||
|
if (incoming & CHAR_PROP_NOTIFY) {
|
||||||
|
self->write_type = BLE_GATT_HVX_NOTIFICATION;
|
||||||
|
common_hal_bleio_characteristic_set_cccd(self->characteristic, true, false);
|
||||||
|
} else {
|
||||||
|
common_hal_bleio_characteristic_set_cccd(self->characteristic, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (outgoing) {
|
||||||
|
self->write_type = BLE_GATT_OP_WRITE_REQ;
|
||||||
|
if (outgoing & CHAR_PROP_WRITE_NO_RESPONSE) {
|
||||||
|
self->write_type = BLE_GATT_OP_WRITE_CMD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ble_drv_add_event_handler(packet_buffer_on_ble_server_evt, self);
|
||||||
|
if (outgoing) {
|
||||||
|
self->write_type = BLE_GATT_HVX_INDICATION;
|
||||||
|
if (outgoing & CHAR_PROP_NOTIFY) {
|
||||||
|
self->write_type = BLE_GATT_HVX_NOTIFICATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len) {
|
||||||
|
if (ringbuf_num_filled(&self->ringbuf) < 2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy received data. Lock out write interrupt handler while copying.
|
||||||
|
uint8_t is_nested_critical_region;
|
||||||
|
sd_nvic_critical_region_enter(&is_nested_critical_region);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes now OK.
|
||||||
|
sd_nvic_critical_region_exit(is_nested_critical_region);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, 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 is_nested_critical_region;
|
||||||
|
sd_nvic_critical_region_enter(&is_nested_critical_region);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
sd_nvic_critical_region_exit(is_nested_critical_region);
|
||||||
|
|
||||||
|
// 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(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 outgoing packet length would be.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return self->characteristic->max_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
ble_drv_remove_event_handler(packet_buffer_on_ble_client_evt, self);
|
||||||
|
}
|
||||||
|
}
|
51
devices/ble_hci/common-hal/_bleio/PacketBuffer.h
Normal file
51
devices/ble_hci/common-hal/_bleio/PacketBuffer.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
uint8_t pending_index;
|
||||||
|
uint8_t write_type;
|
||||||
|
bool client;
|
||||||
|
bool packet_queued;
|
||||||
|
} bleio_packet_buffer_obj_t;
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_PACKETBUFFER_H
|
147
devices/ble_hci/common-hal/_bleio/Service.c
Normal file
147
devices/ble_hci/common-hal/_bleio/Service.c
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
|
||||||
|
* Copyright (c) 2018 Artur Pacholec
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "py/runtime.h"
|
||||||
|
#include "common-hal/_bleio/__init__.h"
|
||||||
|
#include "shared-bindings/_bleio/Characteristic.h"
|
||||||
|
#include "shared-bindings/_bleio/Descriptor.h"
|
||||||
|
#include "shared-bindings/_bleio/Service.h"
|
||||||
|
#include "shared-bindings/_bleio/Adapter.h"
|
||||||
|
|
||||||
|
uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary, mp_obj_list_t * characteristic_list) {
|
||||||
|
self->handle = 0xFFFF;
|
||||||
|
self->uuid = uuid;
|
||||||
|
self->characteristic_list = characteristic_list;
|
||||||
|
self->is_remote = false;
|
||||||
|
self->connection = NULL;
|
||||||
|
self->is_secondary = is_secondary;
|
||||||
|
|
||||||
|
ble_uuid_t nordic_uuid;
|
||||||
|
bleio_uuid_convert_to_nrf_ble_uuid(uuid, &nordic_uuid);
|
||||||
|
|
||||||
|
uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY;
|
||||||
|
if (is_secondary) {
|
||||||
|
service_type = BLE_GATTS_SRVC_TYPE_SECONDARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_used_ble = true;
|
||||||
|
|
||||||
|
return sd_ble_gatts_service_add(service_type, &nordic_uuid, &self->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary) {
|
||||||
|
check_nrf_error(_common_hal_bleio_service_construct(self, uuid, is_secondary,
|
||||||
|
mp_obj_new_list(0, NULL)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void bleio_service_from_connection(bleio_service_obj_t *self, mp_obj_t connection) {
|
||||||
|
self->handle = 0xFFFF;
|
||||||
|
self->uuid = NULL;
|
||||||
|
self->characteristic_list = mp_obj_new_list(0, NULL);
|
||||||
|
self->is_remote = true;
|
||||||
|
self->is_secondary = false;
|
||||||
|
self->connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) {
|
||||||
|
return self->uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_obj_t *self) {
|
||||||
|
return self->characteristic_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
ble_gatts_char_md_t char_md = {
|
||||||
|
.char_props.broadcast = (characteristic->props & CHAR_PROP_BROADCAST) ? 1 : 0,
|
||||||
|
.char_props.read = (characteristic->props & CHAR_PROP_READ) ? 1 : 0,
|
||||||
|
.char_props.write_wo_resp = (characteristic->props & CHAR_PROP_WRITE_NO_RESPONSE) ? 1 : 0,
|
||||||
|
.char_props.write = (characteristic->props & CHAR_PROP_WRITE) ? 1 : 0,
|
||||||
|
.char_props.notify = (characteristic->props & CHAR_PROP_NOTIFY) ? 1 : 0,
|
||||||
|
.char_props.indicate = (characteristic->props & CHAR_PROP_INDICATE) ? 1 : 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
ble_gatts_attr_md_t cccd_md = {
|
||||||
|
.vloc = BLE_GATTS_VLOC_STACK,
|
||||||
|
};
|
||||||
|
|
||||||
|
ble_uuid_t char_uuid;
|
||||||
|
bleio_uuid_convert_to_nrf_ble_uuid(characteristic->uuid, &char_uuid);
|
||||||
|
|
||||||
|
ble_gatts_attr_md_t char_attr_md = {
|
||||||
|
.vloc = BLE_GATTS_VLOC_STACK,
|
||||||
|
.vlen = !characteristic->fixed_length,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (char_md.char_props.notify || char_md.char_props.indicate) {
|
||||||
|
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
|
||||||
|
// Make CCCD write permission match characteristic read permission.
|
||||||
|
bleio_attribute_gatts_set_security_mode(&cccd_md.write_perm, characteristic->read_perm);
|
||||||
|
|
||||||
|
char_md.p_cccd_md = &cccd_md;
|
||||||
|
}
|
||||||
|
|
||||||
|
bleio_attribute_gatts_set_security_mode(&char_attr_md.read_perm, characteristic->read_perm);
|
||||||
|
bleio_attribute_gatts_set_security_mode(&char_attr_md.write_perm, characteristic->write_perm);
|
||||||
|
#if CIRCUITPY_VERBOSE_BLE
|
||||||
|
// Turn on read authorization so that we receive an event to print on every read.
|
||||||
|
char_attr_md.rd_auth = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ble_gatts_attr_t char_attr = {
|
||||||
|
.p_uuid = &char_uuid,
|
||||||
|
.p_attr_md = &char_attr_md,
|
||||||
|
.init_len = 0,
|
||||||
|
.p_value = NULL,
|
||||||
|
.init_offs = 0,
|
||||||
|
.max_len = characteristic->max_length,
|
||||||
|
};
|
||||||
|
|
||||||
|
ble_gatts_char_handles_t char_handles;
|
||||||
|
|
||||||
|
check_nrf_error(sd_ble_gatts_characteristic_add(self->handle, &char_md, &char_attr, &char_handles));
|
||||||
|
|
||||||
|
characteristic->user_desc_handle = char_handles.user_desc_handle;
|
||||||
|
characteristic->cccd_handle = char_handles.cccd_handle;
|
||||||
|
characteristic->sccd_handle = char_handles.sccd_handle;
|
||||||
|
characteristic->handle = char_handles.value_handle;
|
||||||
|
#if CIRCUITPY_VERBOSE_BLE
|
||||||
|
mp_printf(&mp_plat_print, "Char handle %x user %x cccd %x sccd %x\n", characteristic->handle, characteristic->user_desc_handle, characteristic->cccd_handle, characteristic->sccd_handle);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mp_obj_list_append(self->characteristic_list, MP_OBJ_FROM_PTR(characteristic));
|
||||||
|
}
|
54
devices/ble_hci/common-hal/_bleio/Service.h
Normal file
54
devices/ble_hci/common-hal/_bleio/Service.h
Normal file
@ -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 remote 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
|
88
devices/ble_hci/common-hal/_bleio/UUID.c
Normal file
88
devices/ble_hci/common-hal/_bleio/UUID.c
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* 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, uint32_t uuid16, const uint8_t uuid128[]) {
|
||||||
|
self->nrf_ble_uuid.uuid = uuid16;
|
||||||
|
if (uuid128 == NULL) {
|
||||||
|
self->nrf_ble_uuid.type = BLE_UUID_TYPE_BLE;
|
||||||
|
} else {
|
||||||
|
ble_uuid128_t vs_uuid;
|
||||||
|
memcpy(vs_uuid.uuid128, uuid128, sizeof(vs_uuid.uuid128));
|
||||||
|
|
||||||
|
// Register this vendor-specific UUID. Bytes 12 and 13 will be zero.
|
||||||
|
check_nrf_error(sd_ble_uuid_vs_add(&vs_uuid, &self->nrf_ble_uuid.type));
|
||||||
|
vm_used_ble = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self) {
|
||||||
|
// return self->nrf_ble_uuid.type == BLE_UUID_TYPE_BLE ? 16 : 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self) {
|
||||||
|
// return self->nrf_ble_uuid.uuid;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]) {
|
||||||
|
uint8_t length;
|
||||||
|
// check_nrf_error(sd_ble_uuid_encode(&self->nrf_ble_uuid, &length, uuid128));
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_uuid_pack_into(bleio_uuid_obj_t *self, uint8_t* buf) {
|
||||||
|
// if (self->nrf_ble_uuid.type == BLE_UUID_TYPE_BLE) {
|
||||||
|
// buf[0] = self->nrf_ble_uuid.uuid & 0xff;
|
||||||
|
// buf[1] = self->nrf_ble_uuid.uuid >> 8;
|
||||||
|
// } else {
|
||||||
|
// common_hal_bleio_uuid_get_uuid128(self, buf);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// void bleio_uuid_construct_from_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_ble_uuid) {
|
||||||
|
// if (nrf_ble_uuid->type == BLE_UUID_TYPE_UNKNOWN) {
|
||||||
|
// mp_raise_bleio_BluetoothError(translate("Unexpected nrfx uuid type"));
|
||||||
|
// }
|
||||||
|
// self->nrf_ble_uuid.uuid = nrf_ble_uuid->uuid;
|
||||||
|
// self->nrf_ble_uuid.type = nrf_ble_uuid->type;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Fill in a ble_uuid_t from my values.
|
||||||
|
// void bleio_uuid_convert_to_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_ble_uuid) {
|
||||||
|
// nrf_ble_uuid->uuid = self->nrf_ble_uuid.uuid;
|
||||||
|
// nrf_ble_uuid->type = self->nrf_ble_uuid.type;
|
||||||
|
// }
|
47
devices/ble_hci/common-hal/_bleio/UUID.h
Normal file
47
devices/ble_hci/common-hal/_bleio/UUID.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
// Use the native way of storing UUID's:
|
||||||
|
// - ble_uuid_t.uuid is a 16-bit uuid.
|
||||||
|
// - ble_uuid_t.type is BLE_UUID_TYPE_BLE if it's a 16-bit Bluetooth SIG UUID.
|
||||||
|
// or is BLE_UUID_TYPE_VENDOR_BEGIN and higher, which indexes into a table of registered
|
||||||
|
// 128-bit UUIDs.
|
||||||
|
// ble_uuid_t nrf_ble_uuid;
|
||||||
|
} bleio_uuid_obj_t;
|
||||||
|
|
||||||
|
// void bleio_uuid_construct_from_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_uuid);
|
||||||
|
// void bleio_uuid_convert_to_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_uuid);
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_UUID_H
|
246
devices/ble_hci/common-hal/_bleio/__init__.c
Normal file
246
devices/ble_hci/common-hal/_bleio/__init__.c
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
/*
|
||||||
|
* 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.h"
|
||||||
|
|
||||||
|
#include "common-hal/_bleio/__init__.h"
|
||||||
|
|
||||||
|
void check_nrf_error(uint32_t err_code) {
|
||||||
|
if (err_code == NRF_SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (err_code) {
|
||||||
|
case NRF_ERROR_TIMEOUT:
|
||||||
|
mp_raise_msg(&mp_type_TimeoutError, NULL);
|
||||||
|
return;
|
||||||
|
case BLE_ERROR_INVALID_CONN_HANDLE:
|
||||||
|
mp_raise_bleio_ConnectionError(translate("Not connected"));
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
mp_raise_bleio_BluetoothError(translate("Unknown soft device error: %04x"), err_code);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_gatt_status(uint16_t gatt_status) {
|
||||||
|
if (gatt_status == BLE_GATT_STATUS_SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (gatt_status) {
|
||||||
|
case BLE_GATT_STATUS_ATTERR_INSUF_AUTHENTICATION:
|
||||||
|
mp_raise_bleio_SecurityError(translate("Insufficient authentication"));
|
||||||
|
return;
|
||||||
|
case BLE_GATT_STATUS_ATTERR_INSUF_ENCRYPTION:
|
||||||
|
mp_raise_bleio_SecurityError(translate("Insufficient encryption"));
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
mp_raise_bleio_BluetoothError(translate("Unknown gatt error: 0x%04x"), gatt_status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn off BLE on a reset or reload.
|
||||||
|
void bleio_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);
|
||||||
|
bonding_reset();
|
||||||
|
supervisor_start_bluetooth();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The singleton _bleio.Adapter object, bound to _bleio.adapter
|
||||||
|
// It currently only has properties and no state
|
||||||
|
bleio_adapter_obj_t common_hal_bleio_adapter_obj = {
|
||||||
|
.base = {
|
||||||
|
.type = &bleio_adapter_type,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
void common_hal_bleio_check_connected(uint16_t conn_handle) {
|
||||||
|
if (conn_handle == BLE_CONN_HANDLE_INVALID) {
|
||||||
|
mp_raise_bleio_ConnectionError(translate("Not connected"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GATTS read of a Characteristic or Descriptor.
|
||||||
|
size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t* buf, size_t len) {
|
||||||
|
// conn_handle is ignored unless this is a system attribute.
|
||||||
|
// If we're not connected, that's OK, because we can still read and write the local value.
|
||||||
|
|
||||||
|
ble_gatts_value_t gatts_value = {
|
||||||
|
.p_value = buf,
|
||||||
|
.len = len,
|
||||||
|
};
|
||||||
|
|
||||||
|
check_nrf_error(sd_ble_gatts_value_get(conn_handle, handle, &gatts_value));
|
||||||
|
|
||||||
|
return gatts_value.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) {
|
||||||
|
// conn_handle is ignored unless this is a system attribute.
|
||||||
|
// If we're not connected, that's OK, because we can still read and write the local value.
|
||||||
|
|
||||||
|
ble_gatts_value_t gatts_value = {
|
||||||
|
.p_value = bufinfo->buf,
|
||||||
|
.len = bufinfo->len,
|
||||||
|
};
|
||||||
|
|
||||||
|
check_nrf_error(sd_ble_gatts_value_set(conn_handle, handle, &gatts_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t* buf;
|
||||||
|
size_t len;
|
||||||
|
size_t final_len;
|
||||||
|
uint16_t conn_handle;
|
||||||
|
volatile uint16_t status;
|
||||||
|
volatile bool done;
|
||||||
|
} read_info_t;
|
||||||
|
|
||||||
|
STATIC bool _on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) {
|
||||||
|
read_info_t* read = param;
|
||||||
|
switch (ble_evt->header.evt_id) {
|
||||||
|
|
||||||
|
// More events may be handled later, so keep this as a switch.
|
||||||
|
|
||||||
|
case BLE_GATTC_EVT_READ_RSP: {
|
||||||
|
ble_gattc_evt_t* evt = &ble_evt->evt.gattc_evt;
|
||||||
|
ble_gattc_evt_read_rsp_t *response = &evt->params.read_rsp;
|
||||||
|
if (read && evt->conn_handle == read->conn_handle) {
|
||||||
|
read->status = evt->gatt_status;
|
||||||
|
size_t len = MIN(read->len, response->len);
|
||||||
|
memcpy(read->buf, response->data, len);
|
||||||
|
read->final_len = len;
|
||||||
|
// Indicate to busy-wait loop that we've read the attribute value.
|
||||||
|
read->done = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// For debugging.
|
||||||
|
// mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id);
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t* buf, size_t len) {
|
||||||
|
common_hal_bleio_check_connected(conn_handle);
|
||||||
|
|
||||||
|
read_info_t read_info;
|
||||||
|
read_info.buf = buf;
|
||||||
|
read_info.len = len;
|
||||||
|
read_info.final_len = 0;
|
||||||
|
read_info.conn_handle = conn_handle;
|
||||||
|
// Set to true by the event handler.
|
||||||
|
read_info.done = false;
|
||||||
|
ble_drv_add_event_handler(_on_gattc_read_rsp_evt, &read_info);
|
||||||
|
|
||||||
|
uint32_t nrf_error = NRF_ERROR_BUSY;
|
||||||
|
while (nrf_error == NRF_ERROR_BUSY) {
|
||||||
|
nrf_error = sd_ble_gattc_read(conn_handle, handle, 0);
|
||||||
|
}
|
||||||
|
if (nrf_error != NRF_SUCCESS) {
|
||||||
|
ble_drv_remove_event_handler(_on_gattc_read_rsp_evt, &read_info);
|
||||||
|
check_nrf_error(nrf_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!read_info.done) {
|
||||||
|
RUN_BACKGROUND_TASKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_drv_remove_event_handler(_on_gattc_read_rsp_evt, &read_info);
|
||||||
|
check_gatt_status(read_info.status);
|
||||||
|
return read_info.final_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) {
|
||||||
|
common_hal_bleio_check_connected(conn_handle);
|
||||||
|
|
||||||
|
ble_gattc_write_params_t write_params = {
|
||||||
|
.write_op = write_no_response ? BLE_GATT_OP_WRITE_CMD: BLE_GATT_OP_WRITE_REQ,
|
||||||
|
.handle = handle,
|
||||||
|
.p_value = bufinfo->buf,
|
||||||
|
.len = bufinfo->len,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params);
|
||||||
|
if (err_code == NRF_SUCCESS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write with response will return NRF_ERROR_BUSY if the response has not been received.
|
||||||
|
// Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending.
|
||||||
|
if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) {
|
||||||
|
// We could wait for an event indicating the write is complete, but just retrying is easier.
|
||||||
|
MICROPY_VM_HOOK_LOOP;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some real error occurred.
|
||||||
|
check_nrf_error(err_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_bleio_gc_collect(void) {
|
||||||
|
bleio_adapter_gc_collect(&common_hal_bleio_adapter_obj);
|
||||||
|
}
|
50
devices/ble_hci/common-hal/_bleio/__init__.h
Normal file
50
devices/ble_hci/common-hal/_bleio/__init__.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
void bleio_reset(void);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// ble_gap_enc_key_t own_enc;
|
||||||
|
// ble_gap_enc_key_t peer_enc;
|
||||||
|
// ble_gap_id_key_t peer_id;
|
||||||
|
} bonding_keys_t;
|
||||||
|
|
||||||
|
// We assume variable length data.
|
||||||
|
// 20 bytes max (23 - 3).
|
||||||
|
#define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 3)
|
||||||
|
|
||||||
|
// These helpers raise the appropriate exceptions if the code doesn't equal success.
|
||||||
|
void check_nrf_error(uint32_t err_code);
|
||||||
|
void check_gatt_status(uint16_t gatt_status);
|
||||||
|
void check_sec_status(uint8_t sec_status);
|
||||||
|
|
||||||
|
// Track if the user code modified the BLE state to know if we need to undo it on reload.
|
||||||
|
bool vm_used_ble;
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_INIT_H
|
306
devices/ble_hci/common-hal/_bleio/bonding.c
Normal file
306
devices/ble_hci/common-hal/_bleio/bonding.c
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
/*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "shared-bindings/_bleio/__init__.h"
|
||||||
|
#include "shared-bindings/_bleio/Adapter.h"
|
||||||
|
#include "shared-bindings/nvm/ByteArray.h"
|
||||||
|
#include "supervisor/shared/tick.h"
|
||||||
|
|
||||||
|
#include "bonding.h"
|
||||||
|
|
||||||
|
// Internal flash area reserved for bonding storage.
|
||||||
|
#define BONDING_PAGES_START_ADDR CIRCUITPY_BLE_CONFIG_START_ADDR
|
||||||
|
#define BONDING_PAGES_END_ADDR (CIRCUITPY_BLE_CONFIG_START_ADDR + CIRCUITPY_BLE_CONFIG_SIZE)
|
||||||
|
|
||||||
|
// First and last four bytes are magic bytes for id and version. Data is in between.
|
||||||
|
// 'BD01'
|
||||||
|
const uint32_t BONDING_FLAG = ('1' | '0' << 8 | 'D' << 16 | 'B' << 24);
|
||||||
|
|
||||||
|
#define BONDING_DATA_START_ADDR (BONDING_PAGES_START_ADDR + sizeof(BONDING_FLAG))
|
||||||
|
#define BONDING_DATA_END_ADDR (BONDING_PAGES_END_ADDR - sizeof(BONDING_FLAG))
|
||||||
|
|
||||||
|
#define BONDING_START_FLAG_ADDR BONDING_PAGES_START_ADDR
|
||||||
|
#define BONDING_END_FLAG_ADDR BONDING_DATA_END_ADDR
|
||||||
|
|
||||||
|
// Save both system and user service info.
|
||||||
|
#define SYS_ATTR_FLAGS (BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS | BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS)
|
||||||
|
|
||||||
|
#if BONDING_DEBUG
|
||||||
|
void bonding_print_block(bonding_block_t *block) {
|
||||||
|
printf("at 0x%08lx: is_central: %1d, type: 0x%x, ediv: 0x%04x, data_length: %d\n",
|
||||||
|
(uint32_t) block, block->is_central, block->type, block->ediv, block->data_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bonding_print_keys(bonding_keys_t *keys) {
|
||||||
|
for (size_t i = 0; i < sizeof(bonding_keys_t); i ++) {
|
||||||
|
printf("%x", ((uint8_t*) keys)[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STATIC size_t compute_block_size(uint16_t data_length) {
|
||||||
|
// Round data size up to the nearest 32-bit address.
|
||||||
|
return sizeof(bonding_block_t) + ((data_length + 3) & ~0x3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bonding_erase_storage(void) {
|
||||||
|
// Erase all pages in the bonding area.
|
||||||
|
for(uint32_t page_address = BONDING_PAGES_START_ADDR;
|
||||||
|
page_address < BONDING_PAGES_END_ADDR;
|
||||||
|
page_address += FLASH_PAGE_SIZE) {
|
||||||
|
// Argument is page number, not address.
|
||||||
|
sd_flash_page_erase_sync(page_address / FLASH_PAGE_SIZE);
|
||||||
|
}
|
||||||
|
// Write marker words at the beginning and the end of the bonding area.
|
||||||
|
uint32_t flag = BONDING_FLAG;
|
||||||
|
sd_flash_write_sync((uint32_t *) BONDING_START_FLAG_ADDR, &flag, 1);
|
||||||
|
sd_flash_write_sync((uint32_t *) BONDING_END_FLAG_ADDR, &flag, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given NULL to start or block address, return the address of the next valid block.
|
||||||
|
// The last block returned is the unused block at the end.
|
||||||
|
// Return NULL if we have run off the end of the bonding space.
|
||||||
|
|
||||||
|
STATIC bonding_block_t *next_block(bonding_block_t *block) {
|
||||||
|
while (1) {
|
||||||
|
// Advance to next block.
|
||||||
|
if (block == NULL) {
|
||||||
|
return (bonding_block_t *) BONDING_DATA_START_ADDR;
|
||||||
|
} else if (block->type == BLOCK_UNUSED) {
|
||||||
|
// Already at last block (the unused block).
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to next block.
|
||||||
|
block = (bonding_block_t *) ((uint8_t *) block + compute_block_size(block->data_length));
|
||||||
|
|
||||||
|
if (block >= (bonding_block_t *) BONDING_DATA_END_ADDR) {
|
||||||
|
// Went past end of bonding space.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (block->type != BLOCK_INVALID) {
|
||||||
|
// Found an empty or a valid block.
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
// Invalid block (was erased); try again.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the block with given is_central, type and ediv value.
|
||||||
|
// If type == BLOCK_UNUSED, ediv is ignored and the the sole unused block at the end is returned.
|
||||||
|
// If not found, return NULL.
|
||||||
|
STATIC bonding_block_t *find_existing_block(bool is_central, bonding_block_type_t type, uint16_t ediv) {
|
||||||
|
bonding_block_t *block = NULL;
|
||||||
|
while (1) {
|
||||||
|
block = next_block(block);
|
||||||
|
if (block == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// If types match, and block is unused, just return it.
|
||||||
|
// Otherwise check that is_central and ediv match.
|
||||||
|
if (type == block->type) {
|
||||||
|
if (type == BLOCK_UNUSED ||
|
||||||
|
(is_central == block->is_central && ediv == block->ediv)) {
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an empty block large enough to store data_length data.
|
||||||
|
STATIC bonding_block_t* find_unused_block(uint16_t data_length) {
|
||||||
|
bonding_block_t *unused_block = find_existing_block(true, BLOCK_UNUSED, EDIV_INVALID);
|
||||||
|
// If no more room, erase all existing blocks and start over.
|
||||||
|
if (!unused_block ||
|
||||||
|
(uint8_t *) unused_block + compute_block_size(data_length) >= (uint8_t *) BONDING_DATA_END_ADDR) {
|
||||||
|
bonding_erase_storage();
|
||||||
|
unused_block = (bonding_block_t *) BONDING_DATA_START_ADDR;
|
||||||
|
}
|
||||||
|
return unused_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the header word to all 0's, to mark the block as invalid.
|
||||||
|
// We don't change data_length, so we can still skip over this block.
|
||||||
|
STATIC void invalidate_block(bonding_block_t *block) {
|
||||||
|
uint32_t zero = 0;
|
||||||
|
sd_flash_write_sync((uint32_t *) block, &zero, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write bonding block header.
|
||||||
|
STATIC void write_block_header(bonding_block_t *dest_block, bonding_block_t *source_block_header) {
|
||||||
|
sd_flash_write_sync((uint32_t *) dest_block, (uint32_t *) source_block_header, sizeof(bonding_block_t) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write variable-length data at end of bonding block.
|
||||||
|
STATIC void write_block_data(bonding_block_t *dest_block, uint8_t *data, uint16_t data_length) {
|
||||||
|
// Minimize the number of writes. Datasheet says no more than two writes per word before erasing again.
|
||||||
|
|
||||||
|
// Start writing after the current header.
|
||||||
|
uint32_t *flash_word_p = (uint32_t *) ((uint8_t *) dest_block + sizeof(bonding_block_t));
|
||||||
|
while (1) {
|
||||||
|
uint32_t word = 0xffffffff;
|
||||||
|
memcpy(&word, data, data_length >= 4 ? 4 : data_length);
|
||||||
|
sd_flash_write_sync(flash_word_p, &word, 1);
|
||||||
|
if (data_length <= 4) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data_length -= 4;
|
||||||
|
data += 4;
|
||||||
|
// Increment by word size.
|
||||||
|
flash_word_p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC void write_sys_attr_block(bleio_connection_internal_t *connection) {
|
||||||
|
uint16_t length = 0;
|
||||||
|
// First find out how big a buffer we need, then fetch the data.
|
||||||
|
if(sd_ble_gatts_sys_attr_get(connection->conn_handle, NULL, &length, SYS_ATTR_FLAGS) != NRF_SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t sys_attr[length];
|
||||||
|
if(sd_ble_gatts_sys_attr_get(connection->conn_handle, sys_attr, &length, SYS_ATTR_FLAGS) != NRF_SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is there an existing sys_attr block that matches the current sys_attr data?
|
||||||
|
bonding_block_t *existing_block =
|
||||||
|
find_existing_block(connection->is_central, BLOCK_SYS_ATTR, connection->ediv);
|
||||||
|
if (existing_block) {
|
||||||
|
if (length == existing_block->data_length &&
|
||||||
|
memcmp(sys_attr, existing_block->data, length) == 0) {
|
||||||
|
// Identical block found. No need to store again.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Data doesn't match. Invalidate block and store a new one.
|
||||||
|
invalidate_block(existing_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
bonding_block_t block_header = {
|
||||||
|
.is_central = connection->is_central,
|
||||||
|
.type = BLOCK_SYS_ATTR,
|
||||||
|
.ediv = connection->ediv,
|
||||||
|
.conn_handle = connection->conn_handle,
|
||||||
|
.data_length = length,
|
||||||
|
};
|
||||||
|
bonding_block_t *new_block = find_unused_block(length);
|
||||||
|
write_block_header(new_block, &block_header);
|
||||||
|
write_block_data(new_block, sys_attr, length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC void write_keys_block(bleio_connection_internal_t *connection) {
|
||||||
|
uint16_t const ediv = connection->is_central
|
||||||
|
? connection->bonding_keys.peer_enc.master_id.ediv
|
||||||
|
: connection->bonding_keys.own_enc.master_id.ediv;
|
||||||
|
|
||||||
|
// Is there an existing keys block that matches?
|
||||||
|
bonding_block_t *existing_block = find_existing_block(connection->is_central, BLOCK_KEYS, ediv);
|
||||||
|
if (existing_block) {
|
||||||
|
if (existing_block->data_length == sizeof(bonding_keys_t) &&
|
||||||
|
memcmp(existing_block->data, &connection->bonding_keys, sizeof(bonding_keys_t)) == 0) {
|
||||||
|
// Identical block found. No need to store again.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Data doesn't match. Invalidate block and store a new one.
|
||||||
|
invalidate_block(existing_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
bonding_block_t block_header = {
|
||||||
|
.is_central = connection->is_central,
|
||||||
|
.type = BLOCK_KEYS,
|
||||||
|
.ediv = ediv,
|
||||||
|
.conn_handle = connection->conn_handle,
|
||||||
|
.data_length = sizeof(bonding_keys_t),
|
||||||
|
};
|
||||||
|
bonding_block_t *new_block = find_unused_block(sizeof(bonding_keys_t));
|
||||||
|
write_block_header(new_block, &block_header);
|
||||||
|
write_block_data(new_block, (uint8_t *) &connection->bonding_keys, sizeof(bonding_keys_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void bonding_clear_keys(bonding_keys_t *bonding_keys) {
|
||||||
|
memset((uint8_t*) bonding_keys, 0, sizeof(bonding_keys_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void bonding_reset(void) {
|
||||||
|
if (BONDING_FLAG != *((uint32_t *) BONDING_START_FLAG_ADDR) ||
|
||||||
|
BONDING_FLAG != *((uint32_t *) BONDING_END_FLAG_ADDR)) {
|
||||||
|
bonding_erase_storage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write bonding blocks to flash. Requests have been queued during evt handlers.
|
||||||
|
void bonding_background(void) {
|
||||||
|
// A paired connection will request that its keys and CCCD values be stored.
|
||||||
|
// The CCCD store whenever a CCCD value is written.
|
||||||
|
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
|
||||||
|
bleio_connection_internal_t *connection = &bleio_connections[i];
|
||||||
|
|
||||||
|
// Wait at least one second before saving CCCD, to consolidate
|
||||||
|
// writes that involve multiple CCCDs. For instance, for HID,
|
||||||
|
// three CCCD's are set in short succession by the HID client.
|
||||||
|
if (connection->do_bond_cccds) {
|
||||||
|
uint64_t current_ticks_ms = supervisor_ticks_ms64();
|
||||||
|
if (current_ticks_ms - connection->do_bond_cccds_request_time >= 1000) {
|
||||||
|
write_sys_attr_block(connection);
|
||||||
|
connection->do_bond_cccds = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection->do_bond_keys) {
|
||||||
|
write_keys_block(connection);
|
||||||
|
connection->do_bond_keys = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bonding_load_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv) {
|
||||||
|
bonding_block_t *block = find_existing_block(is_central, BLOCK_SYS_ATTR, ediv);
|
||||||
|
if (block == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NRF_SUCCESS ==
|
||||||
|
sd_ble_gatts_sys_attr_set(conn_handle, block->data, block->data_length, SYS_ATTR_FLAGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bonding_load_keys(bool is_central, uint16_t ediv, bonding_keys_t *bonding_keys) {
|
||||||
|
bonding_block_t *block = find_existing_block(is_central, BLOCK_KEYS, ediv);
|
||||||
|
if (block == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sizeof(bonding_keys_t) != block->data_length) {
|
||||||
|
// bonding_keys_t is a fixed length, so lengths should match.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(bonding_keys, block->data, block->data_length);
|
||||||
|
return true;
|
||||||
|
}
|
83
devices/ble_hci/common-hal/_bleio/bonding.h
Normal file
83
devices/ble_hci/common-hal/_bleio/bonding.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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_BONDING_H
|
||||||
|
#define MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_BONDING_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common-hal/_bleio/__init__.h"
|
||||||
|
|
||||||
|
#define EDIV_INVALID (0xffff)
|
||||||
|
|
||||||
|
#define BONDING_DEBUG (1)
|
||||||
|
#if BONDING_DEBUG
|
||||||
|
#define BONDING_DEBUG_PRINTF(...) printf(__VA_ARGS__)
|
||||||
|
#define BONDING_DEBUG_PRINT_BLOCK(block) bonding_print_block(block)
|
||||||
|
#define BONDING_DEBUG_PRINT_KEYS(keys) bonding_print_keys(keys)
|
||||||
|
#else
|
||||||
|
#define BONDING_DEBUG_PRINTF(...)
|
||||||
|
#define BONDING_DEBUG_PRINT_BLOCK(block)
|
||||||
|
#define BONDING_DEBUG_PRINT_KEYS(keys)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Bonding data is stored in variable-length blocks consecutively in
|
||||||
|
// erased flash (all 1's). The blocks are 32-bit aligned, though the
|
||||||
|
// data may be any number of bytes. We hop through the blocks using
|
||||||
|
// the size field to find the next block. When we hit a word that is
|
||||||
|
// all 1's, we have reached the end of the blocks. We can write a new
|
||||||
|
// block there.
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BLOCK_INVALID = 0, // Ignore this block
|
||||||
|
BLOCK_KEYS = 1, // Block contains bonding keys.
|
||||||
|
BLOCK_SYS_ATTR = 2, // Block contains sys_attr values (CCCD settings, etc.).
|
||||||
|
BLOCK_UNUSED = 0xff, // Initial erased value.
|
||||||
|
} bonding_block_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool is_central: 1; // 1 if data is for a central role.
|
||||||
|
uint16_t reserved: 7; // Not currently used
|
||||||
|
bonding_block_type_t type: 8; // What kind of data is stored in.
|
||||||
|
uint16_t ediv; // ediv value; used as a lookup key.
|
||||||
|
uint16_t conn_handle; // Connection handle: used when a BLOCK_SYS_ATTR is queued to write.
|
||||||
|
// Not used as a key, etc.
|
||||||
|
uint16_t data_length; // Length of data in bytes, including ediv, not including padding.
|
||||||
|
// End of block header. 32-bit boundary here.
|
||||||
|
uint8_t data[]; // Rest of data in the block. Needs to be 32-bit aligned.
|
||||||
|
// Block is padded to 32-bit alignment.
|
||||||
|
} bonding_block_t;
|
||||||
|
|
||||||
|
void bonding_background(void);
|
||||||
|
void bonding_erase_storage(void);
|
||||||
|
void bonding_reset(void);
|
||||||
|
void bonding_clear_keys(bonding_keys_t *bonding_keys);
|
||||||
|
bool bonding_load_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv);
|
||||||
|
bool bonding_load_keys(bool is_central, uint16_t ediv, bonding_keys_t *bonding_keys);
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_BLE_HCI_COMMON_HAL_BONDING_H
|
675
devices/ble_hci/common-hal/_bleio/hci.c
Normal file
675
devices/ble_hci/common-hal/_bleio/hci.c
Normal file
@ -0,0 +1,675 @@
|
|||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
#define HCI_COMMAND_PKT 0x01
|
||||||
|
#define HCI_ACLDATA_PKT 0x02
|
||||||
|
#define HCI_EVENT_PKT 0x04
|
||||||
|
|
||||||
|
#define EVT_DISCONN_COMPLETE 0x05
|
||||||
|
#define EVT_CMD_COMPLETE 0xe
|
||||||
|
#define EVT_CMD_STATUS 0x0f
|
||||||
|
#define EVT_NUM_COMP_PKTS 0x13
|
||||||
|
#define EVT_LE_META_EVENT 0x3e
|
||||||
|
|
||||||
|
#define EVT_LE_CONN_COMPLETE 0x01
|
||||||
|
#define EVT_LE_ADVERTISING_REPORT 0x02
|
||||||
|
|
||||||
|
#define OGF_LINK_CTL 0x01
|
||||||
|
#define OGF_HOST_CTL 0x03
|
||||||
|
#define OGF_INFO_PARAM 0x04
|
||||||
|
#define OGF_STATUS_PARAM 0x05
|
||||||
|
#define OGF_LE_CTL 0x08
|
||||||
|
|
||||||
|
// OGF_LINK_CTL
|
||||||
|
#define OCF_DISCONNECT 0x0006
|
||||||
|
|
||||||
|
// OGF_HOST_CTL
|
||||||
|
#define OCF_SET_EVENT_MASK 0x0001
|
||||||
|
#define OCF_RESET 0x0003
|
||||||
|
|
||||||
|
// OGF_INFO_PARAM
|
||||||
|
#define OCF_READ_LOCAL_VERSION 0x0001
|
||||||
|
#define OCF_READ_BD_ADDR 0x0009
|
||||||
|
|
||||||
|
// OGF_STATUS_PARAM
|
||||||
|
#define OCF_READ_RSSI 0x0005
|
||||||
|
|
||||||
|
// OGF_LE_CTL
|
||||||
|
#define OCF_LE_READ_BUFFER_SIZE 0x0002
|
||||||
|
#define OCF_LE_SET_RANDOM_ADDRESS 0x0005
|
||||||
|
#define OCF_LE_SET_ADVERTISING_PARAMETERS 0x0006
|
||||||
|
#define OCF_LE_SET_ADVERTISING_DATA 0x0008
|
||||||
|
#define OCF_LE_SET_SCAN_RESPONSE_DATA 0x0009
|
||||||
|
#define OCF_LE_SET_ADVERTISE_ENABLE 0x000a
|
||||||
|
#define OCF_LE_SET_SCAN_PARAMETERS 0x000b
|
||||||
|
#define OCF_LE_SET_SCAN_ENABLE 0x000c
|
||||||
|
#define OCF_LE_CREATE_CONN 0x000d
|
||||||
|
#define OCF_LE_CANCEL_CONN 0x000e
|
||||||
|
#define OCF_LE_CONN_UPDATE 0x0013
|
||||||
|
|
||||||
|
#define HCI_OE_USER_ENDED_CONNECTION 0x13
|
||||||
|
|
||||||
|
HCIClass::HCIClass() :
|
||||||
|
_debug(NULL),
|
||||||
|
_recvIndex(0),
|
||||||
|
_pendingPkt(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
HCIClass::~HCIClass()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::begin()
|
||||||
|
{
|
||||||
|
_recvIndex = 0;
|
||||||
|
|
||||||
|
return HCITransport.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HCIClass::end()
|
||||||
|
{
|
||||||
|
HCITransport.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HCIClass::poll()
|
||||||
|
{
|
||||||
|
poll(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HCIClass::poll(unsigned long timeout)
|
||||||
|
{
|
||||||
|
#if defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_METRO_M4_AIRLIFT_LITE)
|
||||||
|
digitalWrite(NINA_RTS, LOW);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
HCITransport.wait(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (HCITransport.available()) {
|
||||||
|
byte b = HCITransport.read();
|
||||||
|
|
||||||
|
_recvBuffer[_recvIndex++] = b;
|
||||||
|
|
||||||
|
if (_recvBuffer[0] == HCI_ACLDATA_PKT) {
|
||||||
|
if (_recvIndex > 5 && _recvIndex >= (5 + (_recvBuffer[3] + (_recvBuffer[4] << 8)))) {
|
||||||
|
if (_debug) {
|
||||||
|
dumpPkt("HCI ACLDATA RX <- ", _recvIndex, _recvBuffer);
|
||||||
|
}
|
||||||
|
#if defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_METRO_M4_AIRLIFT_LITE)
|
||||||
|
digitalWrite(NINA_RTS, HIGH);
|
||||||
|
#endif
|
||||||
|
int pktLen = _recvIndex - 1;
|
||||||
|
_recvIndex = 0;
|
||||||
|
|
||||||
|
handleAclDataPkt(pktLen, &_recvBuffer[1]);
|
||||||
|
|
||||||
|
#if defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_METRO_M4_AIRLIFT_LITE)
|
||||||
|
digitalWrite(NINA_RTS, LOW);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} else if (_recvBuffer[0] == HCI_EVENT_PKT) {
|
||||||
|
if (_recvIndex > 3 && _recvIndex >= (3 + _recvBuffer[2])) {
|
||||||
|
if (_debug) {
|
||||||
|
dumpPkt("HCI EVENT RX <- ", _recvIndex, _recvBuffer);
|
||||||
|
}
|
||||||
|
#if defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_METRO_M4_AIRLIFT_LITE)
|
||||||
|
digitalWrite(NINA_RTS, HIGH);
|
||||||
|
#endif
|
||||||
|
// received full event
|
||||||
|
int pktLen = _recvIndex - 1;
|
||||||
|
_recvIndex = 0;
|
||||||
|
|
||||||
|
handleEventPkt(pktLen, &_recvBuffer[1]);
|
||||||
|
|
||||||
|
#if defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_METRO_M4_AIRLIFT_LITE)
|
||||||
|
digitalWrite(NINA_RTS, LOW);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_recvIndex = 0;
|
||||||
|
|
||||||
|
if (_debug) {
|
||||||
|
_debug->println(b, HEX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_METRO_M4_AIRLIFT_LITE)
|
||||||
|
digitalWrite(NINA_RTS, HIGH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::reset()
|
||||||
|
{
|
||||||
|
return sendCommand(OGF_HOST_CTL << 10 | OCF_RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::readLocalVersion(uint8_t& hciVer, uint16_t& hciRev, uint8_t& lmpVer, uint16_t& manufacturer, uint16_t& lmpSubVer)
|
||||||
|
{
|
||||||
|
int result = sendCommand(OGF_INFO_PARAM << 10 | OCF_READ_LOCAL_VERSION);
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
struct __attribute__ ((packed)) HCILocalVersion {
|
||||||
|
uint8_t hciVer;
|
||||||
|
uint16_t hciRev;
|
||||||
|
uint8_t lmpVer;
|
||||||
|
uint16_t manufacturer;
|
||||||
|
uint16_t lmpSubVer;
|
||||||
|
} *localVersion = (HCILocalVersion*)_cmdResponse;
|
||||||
|
|
||||||
|
hciVer = localVersion->hciVer;
|
||||||
|
hciRev = localVersion->hciRev;
|
||||||
|
lmpVer = localVersion->lmpVer;
|
||||||
|
manufacturer = localVersion->manufacturer;
|
||||||
|
lmpSubVer = localVersion->lmpSubVer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::readBdAddr(uint8_t addr[6])
|
||||||
|
{
|
||||||
|
int result = sendCommand(OGF_INFO_PARAM << 10 | OCF_READ_BD_ADDR);
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
memcpy(addr, _cmdResponse, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::readRssi(uint16_t handle)
|
||||||
|
{
|
||||||
|
int result = sendCommand(OGF_STATUS_PARAM << 10 | OCF_READ_RSSI, sizeof(handle), &handle);
|
||||||
|
int rssi = 127;
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
struct __attribute__ ((packed)) HCIReadRssi {
|
||||||
|
uint16_t handle;
|
||||||
|
int8_t rssi;
|
||||||
|
} *readRssi = (HCIReadRssi*)_cmdResponse;
|
||||||
|
|
||||||
|
if (readRssi->handle == handle) {
|
||||||
|
rssi = readRssi->rssi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rssi;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::setEventMask(uint64_t eventMask)
|
||||||
|
{
|
||||||
|
return sendCommand(OGF_HOST_CTL << 10 | OCF_SET_EVENT_MASK, sizeof(eventMask), &eventMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::readLeBufferSize(uint16_t& pktLen, uint8_t& maxPkt)
|
||||||
|
{
|
||||||
|
int result = sendCommand(OGF_LE_CTL << 10 | OCF_LE_READ_BUFFER_SIZE);
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
struct __attribute__ ((packed)) HCILeBufferSize {
|
||||||
|
uint16_t pktLen;
|
||||||
|
uint8_t maxPkt;
|
||||||
|
} *leBufferSize = (HCILeBufferSize*)_cmdResponse;
|
||||||
|
|
||||||
|
pktLen = leBufferSize->pktLen;
|
||||||
|
_maxPkt = maxPkt = leBufferSize->maxPkt;
|
||||||
|
|
||||||
|
#ifndef __AVR__
|
||||||
|
ATT.setMaxMtu(pktLen - 9); // max pkt len - ACL header size
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::leSetRandomAddress(uint8_t addr[6])
|
||||||
|
{
|
||||||
|
return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_RANDOM_ADDRESS, 6, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::leSetAdvertisingParameters(uint16_t minInterval, uint16_t maxInterval,
|
||||||
|
uint8_t advType, uint8_t ownBdaddrType,
|
||||||
|
uint8_t directBdaddrType, uint8_t directBdaddr[6],
|
||||||
|
uint8_t chanMap,
|
||||||
|
uint8_t filter)
|
||||||
|
{
|
||||||
|
struct __attribute__ ((packed)) HCILeAdvertisingParameters {
|
||||||
|
uint16_t minInterval;
|
||||||
|
uint16_t maxInterval;
|
||||||
|
uint8_t advType;
|
||||||
|
uint8_t ownBdaddrType;
|
||||||
|
uint8_t directBdaddrType;
|
||||||
|
uint8_t directBdaddr[6];
|
||||||
|
uint8_t chanMap;
|
||||||
|
uint8_t filter;
|
||||||
|
} leAdvertisingParamters;
|
||||||
|
|
||||||
|
leAdvertisingParamters.minInterval = minInterval;
|
||||||
|
leAdvertisingParamters.maxInterval = maxInterval;
|
||||||
|
leAdvertisingParamters.advType = advType;
|
||||||
|
leAdvertisingParamters.ownBdaddrType = ownBdaddrType;
|
||||||
|
leAdvertisingParamters.directBdaddrType = directBdaddrType;
|
||||||
|
memcpy(leAdvertisingParamters.directBdaddr, directBdaddr, 6);
|
||||||
|
leAdvertisingParamters.chanMap = chanMap;
|
||||||
|
leAdvertisingParamters.filter = filter;
|
||||||
|
|
||||||
|
return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_ADVERTISING_PARAMETERS, sizeof(leAdvertisingParamters), &leAdvertisingParamters);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::leSetAdvertisingData(uint8_t length, uint8_t data[])
|
||||||
|
{
|
||||||
|
struct __attribute__ ((packed)) HCILeAdvertisingData {
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t data[31];
|
||||||
|
} leAdvertisingData;
|
||||||
|
|
||||||
|
memset(&leAdvertisingData, 0, sizeof(leAdvertisingData));
|
||||||
|
leAdvertisingData.length = length;
|
||||||
|
memcpy(leAdvertisingData.data, data, length);
|
||||||
|
|
||||||
|
return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_ADVERTISING_DATA, sizeof(leAdvertisingData), &leAdvertisingData);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::leSetScanResponseData(uint8_t length, uint8_t data[])
|
||||||
|
{
|
||||||
|
struct __attribute__ ((packed)) HCILeScanResponseData {
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t data[31];
|
||||||
|
} leScanResponseData;
|
||||||
|
|
||||||
|
memset(&leScanResponseData, 0, sizeof(leScanResponseData));
|
||||||
|
leScanResponseData.length = length;
|
||||||
|
memcpy(leScanResponseData.data, data, length);
|
||||||
|
|
||||||
|
return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_SCAN_RESPONSE_DATA, sizeof(leScanResponseData), &leScanResponseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::leSetAdvertiseEnable(uint8_t enable)
|
||||||
|
{
|
||||||
|
return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_ADVERTISE_ENABLE, sizeof(enable), &enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::leSetScanParameters(uint8_t type, uint16_t interval, uint16_t window,
|
||||||
|
uint8_t ownBdaddrType, uint8_t filter)
|
||||||
|
{
|
||||||
|
struct __attribute__ ((packed)) HCILeSetScanParameters {
|
||||||
|
uint8_t type;
|
||||||
|
uint16_t interval;
|
||||||
|
uint16_t window;
|
||||||
|
uint8_t ownBdaddrType;
|
||||||
|
uint8_t filter;
|
||||||
|
} leScanParameters;
|
||||||
|
|
||||||
|
leScanParameters.type = type;
|
||||||
|
leScanParameters.interval = interval;
|
||||||
|
leScanParameters.window = window;
|
||||||
|
leScanParameters.ownBdaddrType = ownBdaddrType;
|
||||||
|
leScanParameters.filter = filter;
|
||||||
|
|
||||||
|
return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_SCAN_PARAMETERS, sizeof(leScanParameters), &leScanParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::leSetScanEnable(uint8_t enabled, uint8_t duplicates)
|
||||||
|
{
|
||||||
|
struct __attribute__ ((packed)) HCILeSetScanEnableData {
|
||||||
|
uint8_t enabled;
|
||||||
|
uint8_t duplicates;
|
||||||
|
} leScanEnableData;
|
||||||
|
|
||||||
|
leScanEnableData.enabled = enabled;
|
||||||
|
leScanEnableData.duplicates = duplicates;
|
||||||
|
|
||||||
|
return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_SCAN_ENABLE, sizeof(leScanEnableData), &leScanEnableData);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::leCreateConn(uint16_t interval, uint16_t window, uint8_t initiatorFilter,
|
||||||
|
uint8_t peerBdaddrType, uint8_t peerBdaddr[6], uint8_t ownBdaddrType,
|
||||||
|
uint16_t minInterval, uint16_t maxInterval, uint16_t latency,
|
||||||
|
uint16_t supervisionTimeout, uint16_t minCeLength, uint16_t maxCeLength)
|
||||||
|
{
|
||||||
|
struct __attribute__ ((packed)) HCILeCreateConnData {
|
||||||
|
uint16_t interval;
|
||||||
|
uint16_t window;
|
||||||
|
uint8_t initiatorFilter;
|
||||||
|
uint8_t peerBdaddrType;
|
||||||
|
uint8_t peerBdaddr[6];
|
||||||
|
uint8_t ownBdaddrType;
|
||||||
|
uint16_t minInterval;
|
||||||
|
uint16_t maxInterval;
|
||||||
|
uint16_t latency;
|
||||||
|
uint16_t supervisionTimeout;
|
||||||
|
uint16_t minCeLength;
|
||||||
|
uint16_t maxCeLength;
|
||||||
|
} leCreateConnData;
|
||||||
|
|
||||||
|
leCreateConnData.interval = interval;
|
||||||
|
leCreateConnData.window = window;
|
||||||
|
leCreateConnData.initiatorFilter = initiatorFilter;
|
||||||
|
leCreateConnData.peerBdaddrType = peerBdaddrType;
|
||||||
|
memcpy(leCreateConnData.peerBdaddr, peerBdaddr, sizeof(leCreateConnData.peerBdaddr));
|
||||||
|
leCreateConnData.ownBdaddrType = ownBdaddrType;
|
||||||
|
leCreateConnData.minInterval = minInterval;
|
||||||
|
leCreateConnData.maxInterval = maxInterval;
|
||||||
|
leCreateConnData.latency = latency;
|
||||||
|
leCreateConnData.supervisionTimeout = supervisionTimeout;
|
||||||
|
leCreateConnData.minCeLength = minCeLength;
|
||||||
|
leCreateConnData.maxCeLength = maxCeLength;
|
||||||
|
|
||||||
|
return sendCommand(OGF_LE_CTL << 10 | OCF_LE_CREATE_CONN, sizeof(leCreateConnData), &leCreateConnData);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::leCancelConn()
|
||||||
|
{
|
||||||
|
return sendCommand(OGF_LE_CTL << 10 | OCF_LE_CANCEL_CONN, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::leConnUpdate(uint16_t handle, uint16_t minInterval, uint16_t maxInterval,
|
||||||
|
uint16_t latency, uint16_t supervisionTimeout)
|
||||||
|
{
|
||||||
|
struct __attribute__ ((packed)) HCILeConnUpdateData {
|
||||||
|
uint16_t handle;
|
||||||
|
uint16_t minInterval;
|
||||||
|
uint16_t maxInterval;
|
||||||
|
uint16_t latency;
|
||||||
|
uint16_t supervisionTimeout;
|
||||||
|
uint16_t minCeLength;
|
||||||
|
uint16_t maxCeLength;
|
||||||
|
} leConnUpdateData;
|
||||||
|
|
||||||
|
leConnUpdateData.handle = handle;
|
||||||
|
leConnUpdateData.minInterval = minInterval;
|
||||||
|
leConnUpdateData.maxInterval = maxInterval;
|
||||||
|
leConnUpdateData.latency = latency;
|
||||||
|
leConnUpdateData.supervisionTimeout = supervisionTimeout;
|
||||||
|
leConnUpdateData.minCeLength = 0x0004;
|
||||||
|
leConnUpdateData.maxCeLength = 0x0006;
|
||||||
|
|
||||||
|
return sendCommand(OGF_LE_CTL << 10 | OCF_LE_CONN_UPDATE, sizeof(leConnUpdateData), &leConnUpdateData);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::sendAclPkt(uint16_t handle, uint8_t cid, uint8_t plen, void* data)
|
||||||
|
{
|
||||||
|
while (_pendingPkt >= _maxPkt) {
|
||||||
|
poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct __attribute__ ((packed)) HCIACLHdr {
|
||||||
|
uint8_t pktType;
|
||||||
|
uint16_t handle;
|
||||||
|
uint16_t dlen;
|
||||||
|
uint16_t plen;
|
||||||
|
uint16_t cid;
|
||||||
|
} aclHdr = { HCI_ACLDATA_PKT, handle, uint8_t(plen + 4), plen, cid };
|
||||||
|
|
||||||
|
uint8_t txBuffer[sizeof(aclHdr) + plen];
|
||||||
|
memcpy(txBuffer, &aclHdr, sizeof(aclHdr));
|
||||||
|
memcpy(&txBuffer[sizeof(aclHdr)], data, plen);
|
||||||
|
|
||||||
|
if (_debug) {
|
||||||
|
dumpPkt("HCI ACLDATA TX -> ", sizeof(aclHdr) + plen, txBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingPkt++;
|
||||||
|
HCITransport.write(txBuffer, sizeof(aclHdr) + plen);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::disconnect(uint16_t handle)
|
||||||
|
{
|
||||||
|
struct __attribute__ ((packed)) HCIDisconnectData {
|
||||||
|
uint16_t handle;
|
||||||
|
uint8_t reason;
|
||||||
|
} disconnectData = { handle, HCI_OE_USER_ENDED_CONNECTION };
|
||||||
|
|
||||||
|
return sendCommand(OGF_LINK_CTL << 10 | OCF_DISCONNECT, sizeof(disconnectData), &disconnectData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HCIClass::debug(Stream& stream)
|
||||||
|
{
|
||||||
|
_debug = &stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HCIClass::noDebug()
|
||||||
|
{
|
||||||
|
_debug = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HCIClass::sendCommand(uint16_t opcode, uint8_t plen, void* parameters)
|
||||||
|
{
|
||||||
|
struct __attribute__ ((packed)) {
|
||||||
|
uint8_t pktType;
|
||||||
|
uint16_t opcode;
|
||||||
|
uint8_t plen;
|
||||||
|
} pktHdr = {HCI_COMMAND_PKT, opcode, plen};
|
||||||
|
|
||||||
|
uint8_t txBuffer[sizeof(pktHdr) + plen];
|
||||||
|
memcpy(txBuffer, &pktHdr, sizeof(pktHdr));
|
||||||
|
memcpy(&txBuffer[sizeof(pktHdr)], parameters, plen);
|
||||||
|
|
||||||
|
if (_debug) {
|
||||||
|
dumpPkt("HCI COMMAND TX -> ", sizeof(pktHdr) + plen, txBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
HCITransport.write(txBuffer, sizeof(pktHdr) + plen);
|
||||||
|
|
||||||
|
_cmdCompleteOpcode = 0xffff;
|
||||||
|
_cmdCompleteStatus = -1;
|
||||||
|
|
||||||
|
for (unsigned long start = millis(); _cmdCompleteOpcode != opcode && millis() < (start + 1000);) {
|
||||||
|
poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _cmdCompleteStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HCIClass::handleAclDataPkt(uint8_t /*plen*/, uint8_t pdata[])
|
||||||
|
{
|
||||||
|
struct __attribute__ ((packed)) HCIACLHdr {
|
||||||
|
uint16_t handle;
|
||||||
|
uint16_t dlen;
|
||||||
|
uint16_t len;
|
||||||
|
uint16_t cid;
|
||||||
|
} *aclHdr = (HCIACLHdr*)pdata;
|
||||||
|
|
||||||
|
uint16_t aclFlags = (aclHdr->handle & 0xf000) >> 12;
|
||||||
|
|
||||||
|
if ((aclHdr->dlen - 4) != aclHdr->len) {
|
||||||
|
// packet is fragmented
|
||||||
|
if (aclFlags != 0x01) {
|
||||||
|
// copy into ACL buffer
|
||||||
|
memcpy(_aclPktBuffer, &_recvBuffer[1], sizeof(HCIACLHdr) + aclHdr->dlen - 4);
|
||||||
|
} else {
|
||||||
|
// copy next chunk into the buffer
|
||||||
|
HCIACLHdr* aclBufferHeader = (HCIACLHdr*)_aclPktBuffer;
|
||||||
|
|
||||||
|
memcpy(&_aclPktBuffer[sizeof(HCIACLHdr) + aclBufferHeader->dlen - 4], &_recvBuffer[1 + sizeof(aclHdr->handle) + sizeof(aclHdr->dlen)], aclHdr->dlen);
|
||||||
|
|
||||||
|
aclBufferHeader->dlen += aclHdr->dlen;
|
||||||
|
aclHdr = aclBufferHeader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((aclHdr->dlen - 4) != aclHdr->len) {
|
||||||
|
// don't have the full packet yet
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aclHdr->cid == ATT_CID) {
|
||||||
|
if (aclFlags == 0x01) {
|
||||||
|
// use buffered packet
|
||||||
|
ATT.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &_aclPktBuffer[sizeof(HCIACLHdr)]);
|
||||||
|
} else {
|
||||||
|
// use the recv buffer
|
||||||
|
ATT.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &_recvBuffer[1 + sizeof(HCIACLHdr)]);
|
||||||
|
}
|
||||||
|
} else if (aclHdr->cid == SIGNALING_CID) {
|
||||||
|
L2CAPSignaling.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &_recvBuffer[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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HCIClass::handleNumCompPkts(uint16_t /*handle*/, uint16_t numPkts)
|
||||||
|
{
|
||||||
|
if (numPkts && _pendingPkt > numPkts) {
|
||||||
|
_pendingPkt -= numPkts;
|
||||||
|
} else {
|
||||||
|
_pendingPkt = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HCIClass::handleEventPkt(uint8_t /*plen*/, uint8_t pdata[])
|
||||||
|
{
|
||||||
|
struct __attribute__ ((packed)) HCIEventHdr {
|
||||||
|
uint8_t evt;
|
||||||
|
uint8_t plen;
|
||||||
|
} *eventHdr = (HCIEventHdr*)pdata;
|
||||||
|
|
||||||
|
if (eventHdr->evt == EVT_DISCONN_COMPLETE) {
|
||||||
|
struct __attribute__ ((packed)) DisconnComplete {
|
||||||
|
uint8_t status;
|
||||||
|
uint16_t handle;
|
||||||
|
uint8_t reason;
|
||||||
|
} *disconnComplete = (DisconnComplete*)&pdata[sizeof(HCIEventHdr)];
|
||||||
|
|
||||||
|
ATT.removeConnection(disconnComplete->handle, disconnComplete->reason);
|
||||||
|
L2CAPSignaling.removeConnection(disconnComplete->handle, disconnComplete->reason);
|
||||||
|
|
||||||
|
HCI.leSetAdvertiseEnable(0x01);
|
||||||
|
} else if (eventHdr->evt == EVT_CMD_COMPLETE) {
|
||||||
|
struct __attribute__ ((packed)) CmdComplete {
|
||||||
|
uint8_t ncmd;
|
||||||
|
uint16_t opcode;
|
||||||
|
uint8_t status;
|
||||||
|
} *cmdCompleteHeader = (CmdComplete*)&pdata[sizeof(HCIEventHdr)];
|
||||||
|
|
||||||
|
_cmdCompleteOpcode = cmdCompleteHeader->opcode;
|
||||||
|
_cmdCompleteStatus = cmdCompleteHeader->status;
|
||||||
|
_cmdResponseLen = pdata[1] - sizeof(CmdComplete);
|
||||||
|
_cmdResponse = &pdata[sizeof(HCIEventHdr) + sizeof(CmdComplete)];
|
||||||
|
|
||||||
|
} else if (eventHdr->evt == EVT_CMD_STATUS) {
|
||||||
|
struct __attribute__ ((packed)) CmdStatus {
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t ncmd;
|
||||||
|
uint16_t opcode;
|
||||||
|
} *cmdStatusHeader = (CmdStatus*)&pdata[sizeof(HCIEventHdr)];
|
||||||
|
|
||||||
|
_cmdCompleteOpcode = cmdStatusHeader->opcode;
|
||||||
|
_cmdCompleteStatus = cmdStatusHeader->status;
|
||||||
|
_cmdResponseLen = 0;
|
||||||
|
} else if (eventHdr->evt == EVT_NUM_COMP_PKTS) {
|
||||||
|
uint8_t numHandles = pdata[sizeof(HCIEventHdr)];
|
||||||
|
uint16_t* data = (uint16_t*)&pdata[sizeof(HCIEventHdr) + sizeof(numHandles)];
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < numHandles; i++) {
|
||||||
|
handleNumCompPkts(data[0], data[1]);
|
||||||
|
|
||||||
|
data += 2;
|
||||||
|
}
|
||||||
|
} else if (eventHdr->evt == EVT_LE_META_EVENT) {
|
||||||
|
struct __attribute__ ((packed)) LeMetaEventHeader {
|
||||||
|
uint8_t subevent;
|
||||||
|
} *leMetaHeader = (LeMetaEventHeader*)&pdata[sizeof(HCIEventHdr)];
|
||||||
|
|
||||||
|
if (leMetaHeader->subevent == EVT_LE_CONN_COMPLETE) {
|
||||||
|
struct __attribute__ ((packed)) EvtLeConnectionComplete {
|
||||||
|
uint8_t status;
|
||||||
|
uint16_t handle;
|
||||||
|
uint8_t role;
|
||||||
|
uint8_t peerBdaddrType;
|
||||||
|
uint8_t peerBdaddr[6];
|
||||||
|
uint16_t interval;
|
||||||
|
uint16_t latency;
|
||||||
|
uint16_t supervisionTimeout;
|
||||||
|
uint8_t masterClockAccuracy;
|
||||||
|
} *leConnectionComplete = (EvtLeConnectionComplete*)&pdata[sizeof(HCIEventHdr) + sizeof(LeMetaEventHeader)];
|
||||||
|
|
||||||
|
if (leConnectionComplete->status == 0x00) {
|
||||||
|
ATT.addConnection(leConnectionComplete->handle,
|
||||||
|
leConnectionComplete->role,
|
||||||
|
leConnectionComplete->peerBdaddrType,
|
||||||
|
leConnectionComplete->peerBdaddr,
|
||||||
|
leConnectionComplete->interval,
|
||||||
|
leConnectionComplete->latency,
|
||||||
|
leConnectionComplete->supervisionTimeout,
|
||||||
|
leConnectionComplete->masterClockAccuracy);
|
||||||
|
|
||||||
|
L2CAPSignaling.addConnection(leConnectionComplete->handle,
|
||||||
|
leConnectionComplete->role,
|
||||||
|
leConnectionComplete->peerBdaddrType,
|
||||||
|
leConnectionComplete->peerBdaddr,
|
||||||
|
leConnectionComplete->interval,
|
||||||
|
leConnectionComplete->latency,
|
||||||
|
leConnectionComplete->supervisionTimeout,
|
||||||
|
leConnectionComplete->masterClockAccuracy);
|
||||||
|
}
|
||||||
|
} else if (leMetaHeader->subevent == EVT_LE_ADVERTISING_REPORT) {
|
||||||
|
struct __attribute__ ((packed)) EvtLeAdvertisingReport {
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t peerBdaddrType;
|
||||||
|
uint8_t peerBdaddr[6];
|
||||||
|
uint8_t eirLength;
|
||||||
|
uint8_t eirData[31];
|
||||||
|
} *leAdvertisingReport = (EvtLeAdvertisingReport*)&pdata[sizeof(HCIEventHdr) + sizeof(LeMetaEventHeader)];
|
||||||
|
|
||||||
|
if (leAdvertisingReport->status == 0x01) {
|
||||||
|
// last byte is RSSI
|
||||||
|
int8_t rssi = leAdvertisingReport->eirData[leAdvertisingReport->eirLength];
|
||||||
|
|
||||||
|
GAP.handleLeAdvertisingReport(leAdvertisingReport->type,
|
||||||
|
leAdvertisingReport->peerBdaddrType,
|
||||||
|
leAdvertisingReport->peerBdaddr,
|
||||||
|
leAdvertisingReport->eirLength,
|
||||||
|
leAdvertisingReport->eirData,
|
||||||
|
rssi);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HCIClass::dumpPkt(const char* prefix, uint8_t plen, uint8_t pdata[])
|
||||||
|
{
|
||||||
|
if (_debug) {
|
||||||
|
_debug->print(prefix);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < plen; i++) {
|
||||||
|
byte b = pdata[i];
|
||||||
|
|
||||||
|
if (b < 16) {
|
||||||
|
_debug->print("0");
|
||||||
|
}
|
||||||
|
|
||||||
|
_debug->print(b, HEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
_debug->println();
|
||||||
|
_debug->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
48
devices/ble_hci/supervisor/bluetooth.c
Normal file
48
devices/ble_hci/supervisor/bluetooth.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
void supervisor_bluetooth_start_advertising(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void supervisor_start_bluetooth(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FIL active_file;
|
||||||
|
volatile bool new_filename;
|
||||||
|
volatile bool run_ble_background;
|
||||||
|
bool was_connected;
|
||||||
|
|
||||||
|
void supervisor_bluetooth_background(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// This happens in an interrupt so we need to be quick.
|
||||||
|
bool supervisor_bluetooth_hook(ble_evt_t *ble_evt) {
|
||||||
|
return false;
|
||||||
|
}
|
34
devices/ble_hci/supervisor/bluetooth.h
Normal file
34
devices/ble_hci/supervisor/bluetooth.h
Normal file
@ -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
|
@ -1 +1 @@
|
|||||||
Subproject commit c0eef7b75124fc946af5f75e12d82d6d01315ab1
|
Subproject commit 039b5f3bbc3f4ba4421e581db290560d59fef625
|
@ -6,6 +6,10 @@ USB_MANUFACTURER = "Adafruit Industries LLC"
|
|||||||
CHIP_VARIANT = SAMD51J19A
|
CHIP_VARIANT = SAMD51J19A
|
||||||
CHIP_FAMILY = samd51
|
CHIP_FAMILY = samd51
|
||||||
|
|
||||||
|
# Support _bleio via the on-board ESP32 module.
|
||||||
|
CIRCUITPY_BLEIO = 1
|
||||||
|
CIRCUITPY_BLEIO_HCI = 1
|
||||||
|
|
||||||
QSPI_FLASH_FILESYSTEM = 1
|
QSPI_FLASH_FILESYSTEM = 1
|
||||||
EXTERNAL_FLASH_DEVICE_COUNT = 3
|
EXTERNAL_FLASH_DEVICE_COUNT = 3
|
||||||
EXTERNAL_FLASH_DEVICES = "S25FL116K, S25FL216K, GD25Q16C"
|
EXTERNAL_FLASH_DEVICES = "S25FL116K, S25FL216K, GD25Q16C"
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit e4161d7d6d98d78eddcccb82128856af4baf7e50
|
Subproject commit 6b531fc923d9f02b14bd731a5f584ddf716e8773
|
@ -39,6 +39,7 @@
|
|||||||
#include "py/mpstate.h"
|
#include "py/mpstate.h"
|
||||||
|
|
||||||
#include "supervisor/shared/bluetooth.h"
|
#include "supervisor/shared/bluetooth.h"
|
||||||
|
#include "supervisor/bluetooth.h"
|
||||||
|
|
||||||
nrf_nvic_state_t nrf_nvic_state = { 0 };
|
nrf_nvic_state_t nrf_nvic_state = { 0 };
|
||||||
|
|
||||||
|
81
ports/nrf/supervisor/bluetooth.c
Normal file
81
ports/nrf/supervisor/bluetooth.c
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "supervisor/shared/bluetooth.h"
|
||||||
|
#include "supervisor/bluetooth.h"
|
||||||
|
|
||||||
|
// This happens in an interrupt so we need to be quick.
|
||||||
|
bool supervisor_bluetooth_hook(ble_evt_t *ble_evt) {
|
||||||
|
#if CIRCUITPY_BLE_FILE_SERVICE
|
||||||
|
// Catch writes to filename or contents. Length is read-only.
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
switch (ble_evt->header.evt_id) {
|
||||||
|
case BLE_GAP_EVT_CONNECTED:
|
||||||
|
// We run our background task even if it wasn't us connected to because we may want to
|
||||||
|
// advertise if the user code stopped advertising.
|
||||||
|
run_ble_background = true;
|
||||||
|
break;
|
||||||
|
case BLE_GAP_EVT_DISCONNECTED:
|
||||||
|
run_ble_background = true;
|
||||||
|
break;
|
||||||
|
case BLE_GATTS_EVT_WRITE: {
|
||||||
|
// A client wrote to a characteristic.
|
||||||
|
|
||||||
|
ble_gatts_evt_write_t *evt_write = &ble_evt->evt.gatts_evt.params.write;
|
||||||
|
// Event handle must match the handle for my characteristic.
|
||||||
|
if (evt_write->handle == supervisor_ble_contents_characteristic.handle) {
|
||||||
|
// Handle events
|
||||||
|
//write_to_ringbuf(self, evt_write->data, evt_write->len);
|
||||||
|
// First packet includes a uint16_t le for length at the start.
|
||||||
|
uint16_t current_length = ((uint16_t*) current_command)[0];
|
||||||
|
memcpy(((uint8_t*) current_command) + current_offset, evt_write->data, evt_write->len);
|
||||||
|
current_offset += evt_write->len;
|
||||||
|
current_length = ((uint16_t*) current_command)[0];
|
||||||
|
if (current_offset == current_length) {
|
||||||
|
run_ble_background = true;
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
} else if (evt_write->handle == supervisor_ble_filename_characteristic.handle) {
|
||||||
|
new_filename = true;
|
||||||
|
run_ble_background = true;
|
||||||
|
done = true;
|
||||||
|
} else {
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// For debugging.
|
||||||
|
// mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return done;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
36
ports/nrf/supervisor/bluetooth.h
Normal file
36
ports/nrf/supervisor/bluetooth.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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_NRF_SUPERVISOR_BLUETOOTH_H
|
||||||
|
#define MICROPY_INCLUDED_NRF_SUPERVISOR_BLUETOOTH_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "ble.h"
|
||||||
|
|
||||||
|
bool supervisor_bluetooth_hook(ble_evt_t *ble_evt);
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_NRF_SUPERVISOR_BLUETOOTH_H
|
@ -411,7 +411,7 @@ SRC_SHARED_MODULE_ALL = \
|
|||||||
SRC_SHARED_MODULE = $(filter $(SRC_PATTERNS), $(SRC_SHARED_MODULE_ALL))
|
SRC_SHARED_MODULE = $(filter $(SRC_PATTERNS), $(SRC_SHARED_MODULE_ALL))
|
||||||
|
|
||||||
# Use the native touchio if requested. This flag is set conditionally in, say, mpconfigport.h.
|
# Use the native touchio if requested. This flag is set conditionally in, say, mpconfigport.h.
|
||||||
# The presence of common-hal/touchio/* # does not imply it's available for all chips in a port,
|
# The presence of common-hal/touchio/* does not imply it's available for all chips in a port,
|
||||||
# so there is an explicit flag. For example, SAMD21 touchio is native, but SAMD51 is not.
|
# so there is an explicit flag. For example, SAMD21 touchio is native, but SAMD51 is not.
|
||||||
ifeq ($(CIRCUITPY_TOUCHIO_USE_NATIVE),1)
|
ifeq ($(CIRCUITPY_TOUCHIO_USE_NATIVE),1)
|
||||||
SRC_COMMON_HAL_ALL += \
|
SRC_COMMON_HAL_ALL += \
|
||||||
@ -422,6 +422,14 @@ SRC_SHARED_MODULE_ALL += \
|
|||||||
touchio/TouchIn.c \
|
touchio/TouchIn.c \
|
||||||
touchio/__init__.c
|
touchio/__init__.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# If supporting _bleio via HCI, make devices/ble_hci/common-hal/_bleio be includable,
|
||||||
|
# and use C source files in devices/ble_hci/common-hal.
|
||||||
|
ifeq ($(CIRCUITPY_BLEIO_HCI),1)
|
||||||
|
INC += -I$(TOP)/devices/ble_hci
|
||||||
|
DEVICES_MODULES += $(TOP)/devices/ble_hci
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(CIRCUITPY_AUDIOMP3),1)
|
ifeq ($(CIRCUITPY_AUDIOMP3),1)
|
||||||
SRC_MOD += $(addprefix lib/mp3/src/, \
|
SRC_MOD += $(addprefix lib/mp3/src/, \
|
||||||
bitstream.c \
|
bitstream.c \
|
||||||
|
@ -79,6 +79,10 @@ CFLAGS += -DCIRCUITPY_BITBANGIO=$(CIRCUITPY_BITBANGIO)
|
|||||||
CIRCUITPY_BLEIO ?= 0
|
CIRCUITPY_BLEIO ?= 0
|
||||||
CFLAGS += -DCIRCUITPY_BLEIO=$(CIRCUITPY_BLEIO)
|
CFLAGS += -DCIRCUITPY_BLEIO=$(CIRCUITPY_BLEIO)
|
||||||
|
|
||||||
|
# _bleio can be supported on most any board via HCI
|
||||||
|
CIRCUITPY_BLEIO_HCI ?= 0
|
||||||
|
CFLAGS += -DCIRCUITPY_BLEIO_HCI=$(CIRCUITPY_BLEIO_HCI)
|
||||||
|
|
||||||
CIRCUITPY_BOARD ?= 1
|
CIRCUITPY_BOARD ?= 1
|
||||||
CFLAGS += -DCIRCUITPY_BOARD=$(CIRCUITPY_BOARD)
|
CFLAGS += -DCIRCUITPY_BOARD=$(CIRCUITPY_BOARD)
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ $(Q)$(CC) $(CFLAGS) -c -MD -o $@ $<
|
|||||||
$(RM) -f $(@:.o=.d)
|
$(RM) -f $(@:.o=.d)
|
||||||
endef
|
endef
|
||||||
|
|
||||||
vpath %.c . $(TOP) $(USER_C_MODULES)
|
vpath %.c . $(TOP) $(USER_C_MODULES) $(DEVICES_MODULES)
|
||||||
$(BUILD)/%.o: %.c
|
$(BUILD)/%.o: %.c
|
||||||
$(call compile_c)
|
$(call compile_c)
|
||||||
|
|
||||||
@ -56,8 +56,7 @@ $(BUILD)/%.o: %.c
|
|||||||
|
|
||||||
QSTR_GEN_EXTRA_CFLAGS += -I$(BUILD)/tmp
|
QSTR_GEN_EXTRA_CFLAGS += -I$(BUILD)/tmp
|
||||||
|
|
||||||
vpath %.c . $(TOP) $(USER_C_MODULES)
|
vpath %.c . $(TOP) $(USER_C_MODULES) $(DEVICES_MODULES)
|
||||||
|
|
||||||
$(BUILD)/%.pp: %.c
|
$(BUILD)/%.pp: %.c
|
||||||
$(STEPECHO) "PreProcess $<"
|
$(STEPECHO) "PreProcess $<"
|
||||||
$(Q)$(CC) $(CFLAGS) -E -Wp,-C,-dD,-dI -o $@ $<
|
$(Q)$(CC) $(CFLAGS) -E -Wp,-C,-dD,-dI -o $@ $<
|
||||||
|
@ -65,12 +65,59 @@
|
|||||||
//| connections and also initiate connections."""
|
//| connections and also initiate connections."""
|
||||||
//|
|
//|
|
||||||
|
|
||||||
//| def __init__(self, ):
|
//| def __init__(self, *, tx: Pin, rx: Pin, rts: Pin, cts: Pin, baudrate: int = 115200, buffer_size: int = 256, spi_cs: Pin, gpio0: Pin, reset: Pin, reset_high: bool):
|
||||||
//| """You cannot create an instance of `_bleio.Adapter`.
|
//| """On boards with native BLE, such as the nRf52840,
|
||||||
|
//| you cannot create an instance of `_bleio.Adapter`.
|
||||||
//| Use `_bleio.adapter` to access the sole instance available."""
|
//| Use `_bleio.adapter` to access the sole instance available."""
|
||||||
//| ...
|
|
||||||
//|
|
//|
|
||||||
|
//| On boards that do not have native BLE,
|
||||||
|
//| call `_bleio.Adapter()` once, passing it the pins used to communicate
|
||||||
|
//| with an HCI co-processor, such as an Adafruit AirLift, on or off the board.
|
||||||
|
//| The `Adapter` object will be initialized, enabled, and will be available as `_bleio.adapter`.
|
||||||
|
//| The `tx`, `rx`, `rts`, and `cs` pins are used to communicate with the HCI co-processor in HCI mode.
|
||||||
|
//| The `spi_cs` and `gpio0` pins are used to enable BLE mode
|
||||||
|
//| (usually `spi_cs` is low and `gpio0` is high to enter BLE mode).
|
||||||
|
//| The `reset` pin is used to reset the co-processor.
|
||||||
|
//| `reset_high` describes whether the reset pin is active high or active low.
|
||||||
|
//|
|
||||||
|
#if CIRCUITPY_BLEIO_HCI
|
||||||
|
STATIC mp_obj_t bleio_adapter_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||||
|
enum { ARG_tx, ARG_rx, ARG_rts, ARG_cts, ARG_baudrate, ARG_buffer_size, ARG_spi_cs, ARG_gpio0, ARG_reset, ARG_reset_high };
|
||||||
|
static const mp_arg_t allowed_args[] = {
|
||||||
|
{ MP_QSTR_tx, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
|
{ MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
|
{ MP_QSTR_rts, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
|
{ MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
|
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 115200 } },
|
||||||
|
{ MP_QSTR_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 256 } },
|
||||||
|
{ MP_QSTR_spi_cs, MP_ARG_KW_ONLY }| MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
|
{ MP_QSTR_gpio0, MP_ARG_KW_ONLY }| MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
|
{ MP_QSTR_reset, MP_ARG_KW_ONLY }| MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
|
{ MP_QSTR_reset_high, MP_ARG_KW_ONLY |MP_ARG_BOOL },
|
||||||
|
};
|
||||||
|
|
||||||
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||||
|
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||||
|
|
||||||
|
const mcu_pin_obj_t *tx = validate_obj_is_free_pin(args[ARG_tx].u_obj);
|
||||||
|
const mcu_pin_obj_t *rx = validate_obj_is_free_pin(args[ARG_rx].u_obj);
|
||||||
|
const mcu_pin_obj_t *rts = validate_obj_is_free_pin(args[ARG_rts].u_obj);
|
||||||
|
const mcu_pin_obj_t *cts = validate_obj_is_free_pin(args[ARG_cts].u_obj);
|
||||||
|
const mcu_pin_obj_t *spi_cs = validate_obj_is_free_pin(args[ARG_spi_cs].u_obj);
|
||||||
|
const mcu_pin_obj_t *gpio0 = validate_obj_is_free_pin(args[ARG_gpio0].u_obj);
|
||||||
|
const mcu_pin_obj_t *reset = validate_obj_is_free_pin(args[ARG_reset].u_obj);
|
||||||
|
const bool reset_high = args[ARG_reset_high].u_bool;
|
||||||
|
|
||||||
|
common_hal_bleio_adapter_construct(&common_hal_bleio_adapter_obj, tx, rx, rts, cts,
|
||||||
|
args[ARG_baudrate], arg[ARG_buffer_size],
|
||||||
|
spi_cs, gpio0,
|
||||||
|
reset, reset_high);
|
||||||
|
common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true);
|
||||||
|
|
||||||
|
return MP_OBJ_FROM_PTR(service);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
//|
|
||||||
//| enabled: Any = ...
|
//| enabled: Any = ...
|
||||||
//| """State of the BLE adapter."""
|
//| """State of the BLE adapter."""
|
||||||
//|
|
//|
|
||||||
@ -418,5 +465,8 @@ STATIC MP_DEFINE_CONST_DICT(bleio_adapter_locals_dict, bleio_adapter_locals_dict
|
|||||||
const mp_obj_type_t bleio_adapter_type = {
|
const mp_obj_type_t bleio_adapter_type = {
|
||||||
.base = { &mp_type_type },
|
.base = { &mp_type_type },
|
||||||
.name = MP_QSTR_Adapter,
|
.name = MP_QSTR_Adapter,
|
||||||
|
#if CIRCUITPY_BLEIO_HCI
|
||||||
|
.make_new = bleio_adapter_make_new,
|
||||||
|
#endif
|
||||||
.locals_dict = (mp_obj_t)&bleio_adapter_locals_dict,
|
.locals_dict = (mp_obj_t)&bleio_adapter_locals_dict,
|
||||||
};
|
};
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "ble_drv.h"
|
|
||||||
#include "py/objarray.h"
|
#include "py/objarray.h"
|
||||||
#include "py/objproperty.h"
|
#include "py/objproperty.h"
|
||||||
#include "py/objstr.h"
|
#include "py/objstr.h"
|
||||||
|
@ -24,6 +24,15 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if !CIRCUITPY_BLE_FILE_SERVICE
|
||||||
|
void supervisor_start_bluetooth(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void supervisor_bluetooth_background(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "extmod/vfs.h"
|
#include "extmod/vfs.h"
|
||||||
@ -41,6 +50,8 @@
|
|||||||
|
|
||||||
#include "py/mpstate.h"
|
#include "py/mpstate.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bleio_service_obj_t supervisor_ble_service;
|
bleio_service_obj_t supervisor_ble_service;
|
||||||
bleio_uuid_obj_t supervisor_ble_service_uuid;
|
bleio_uuid_obj_t supervisor_ble_service_uuid;
|
||||||
bleio_characteristic_obj_t supervisor_ble_version_characteristic;
|
bleio_characteristic_obj_t supervisor_ble_version_characteristic;
|
||||||
@ -63,10 +74,7 @@ mp_obj_t service_list_items[1];
|
|||||||
mp_obj_list_t characteristic_list;
|
mp_obj_list_t characteristic_list;
|
||||||
mp_obj_t characteristic_list_items[4];
|
mp_obj_t characteristic_list_items[4];
|
||||||
|
|
||||||
void supervisor_bluetooth_start_advertising(void) {
|
STATIC void supervisor_bluetooth_start_advertising(void) {
|
||||||
#if !CIRCUITPY_BLE_FILE_SERVICE
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj);
|
bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj);
|
||||||
if (is_connected) {
|
if (is_connected) {
|
||||||
return;
|
return;
|
||||||
@ -83,10 +91,6 @@ void supervisor_bluetooth_start_advertising(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void supervisor_start_bluetooth(void) {
|
void supervisor_start_bluetooth(void) {
|
||||||
#if !CIRCUITPY_BLE_FILE_SERVICE
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true);
|
common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true);
|
||||||
|
|
||||||
supervisor_ble_service_uuid.base.type = &bleio_uuid_type;
|
supervisor_ble_service_uuid.base.type = &bleio_uuid_type;
|
||||||
@ -177,7 +181,7 @@ volatile bool new_filename;
|
|||||||
volatile bool run_ble_background;
|
volatile bool run_ble_background;
|
||||||
bool was_connected;
|
bool was_connected;
|
||||||
|
|
||||||
void update_file_length(void) {
|
STATIC void update_file_length(void) {
|
||||||
int32_t file_length = -1;
|
int32_t file_length = -1;
|
||||||
mp_buffer_info_t bufinfo;
|
mp_buffer_info_t bufinfo;
|
||||||
bufinfo.buf = &file_length;
|
bufinfo.buf = &file_length;
|
||||||
@ -188,7 +192,7 @@ void update_file_length(void) {
|
|||||||
common_hal_bleio_characteristic_set_value(&supervisor_ble_length_characteristic, &bufinfo);
|
common_hal_bleio_characteristic_set_value(&supervisor_ble_length_characteristic, &bufinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void open_current_file(void) {
|
STATIC void open_current_file(void) {
|
||||||
if (active_file.obj.fs != 0) {
|
if (active_file.obj.fs != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -203,17 +207,15 @@ void open_current_file(void) {
|
|||||||
update_file_length();
|
update_file_length();
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_current_file(void) {
|
STATIC void close_current_file(void) {
|
||||||
f_close(&active_file);
|
f_close(&active_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t current_command[1024 / sizeof(uint32_t)];
|
uint32_t current_command[1024 / sizeof(uint32_t)];
|
||||||
volatile size_t current_offset;
|
volatile size_t current_offset;
|
||||||
|
|
||||||
|
|
||||||
void supervisor_bluetooth_background(void) {
|
void supervisor_bluetooth_background(void) {
|
||||||
#if !CIRCUITPY_BLE_FILE_SERVICE
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
if (!run_ble_background) {
|
if (!run_ble_background) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -305,54 +307,4 @@ void supervisor_bluetooth_background(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This happens in an interrupt so we need to be quick.
|
#endif // #else
|
||||||
bool supervisor_bluetooth_hook(ble_evt_t *ble_evt) {
|
|
||||||
#if !CIRCUITPY_BLE_FILE_SERVICE
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
// Catch writes to filename or contents. Length is read-only.
|
|
||||||
|
|
||||||
bool done = false;
|
|
||||||
switch (ble_evt->header.evt_id) {
|
|
||||||
case BLE_GAP_EVT_CONNECTED:
|
|
||||||
// We run our background task even if it wasn't us connected to because we may want to
|
|
||||||
// advertise if the user code stopped advertising.
|
|
||||||
run_ble_background = true;
|
|
||||||
break;
|
|
||||||
case BLE_GAP_EVT_DISCONNECTED:
|
|
||||||
run_ble_background = true;
|
|
||||||
break;
|
|
||||||
case BLE_GATTS_EVT_WRITE: {
|
|
||||||
// A client wrote to a characteristic.
|
|
||||||
|
|
||||||
ble_gatts_evt_write_t *evt_write = &ble_evt->evt.gatts_evt.params.write;
|
|
||||||
// Event handle must match the handle for my characteristic.
|
|
||||||
if (evt_write->handle == supervisor_ble_contents_characteristic.handle) {
|
|
||||||
// Handle events
|
|
||||||
//write_to_ringbuf(self, evt_write->data, evt_write->len);
|
|
||||||
// First packet includes a uint16_t le for length at the start.
|
|
||||||
uint16_t current_length = ((uint16_t*) current_command)[0];
|
|
||||||
memcpy(((uint8_t*) current_command) + current_offset, evt_write->data, evt_write->len);
|
|
||||||
current_offset += evt_write->len;
|
|
||||||
current_length = ((uint16_t*) current_command)[0];
|
|
||||||
if (current_offset == current_length) {
|
|
||||||
run_ble_background = true;
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
} else if (evt_write->handle == supervisor_ble_filename_characteristic.handle) {
|
|
||||||
new_filename = true;
|
|
||||||
run_ble_background = true;
|
|
||||||
done = true;
|
|
||||||
} else {
|
|
||||||
return done;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// For debugging.
|
|
||||||
// mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return done;
|
|
||||||
}
|
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H
|
#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H
|
||||||
|
|
||||||
void supervisor_start_bluetooth(void);
|
void supervisor_start_bluetooth(void);
|
||||||
bool supervisor_bluetooth_hook(ble_evt_t *ble_evt);
|
|
||||||
void supervisor_bluetooth_background(void);
|
void supervisor_bluetooth_background(void);
|
||||||
|
|
||||||
#endif
|
#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H
|
||||||
|
@ -33,7 +33,7 @@ endif
|
|||||||
CFLAGS += -DSPI_FLASH_FILESYSTEM=$(SPI_FLASH_FILESYSTEM)
|
CFLAGS += -DSPI_FLASH_FILESYSTEM=$(SPI_FLASH_FILESYSTEM)
|
||||||
|
|
||||||
ifeq ($(CIRCUITPY_BLEIO),1)
|
ifeq ($(CIRCUITPY_BLEIO),1)
|
||||||
SRC_SUPERVISOR += supervisor/shared/bluetooth.c
|
SRC_SUPERVISOR += supervisor/shared/bluetooth.c supervisor/bluetooth.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Choose which flash filesystem impl to use.
|
# Choose which flash filesystem impl to use.
|
||||||
|
Loading…
Reference in New Issue
Block a user