BLE HID WIP: works everywhere except iOS; fixed a bunch of bugs; pretend bonding
This commit is contained in:
parent
1d973a0803
commit
76446f634b
|
@ -40,6 +40,11 @@
|
|||
#include "shared-bindings/bleio/Adapter.h"
|
||||
#include "shared-bindings/bleio/Address.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)
|
||||
|
||||
STATIC void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) {
|
||||
mp_raise_msg_varg(&mp_type_AssertionError,
|
||||
translate("Soft device assert, id: 0x%08lX, pc: 0x%08lX"), id, pc);
|
||||
|
@ -97,6 +102,16 @@ STATIC uint32_t ble_stack_enable(void) {
|
|||
return err_code;
|
||||
|
||||
err_code = sd_ble_enable(&app_ram_start);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
return err_code;
|
||||
|
||||
ble_gap_conn_params_t gap_conn_params = {
|
||||
.min_conn_interval = BLE_MIN_CONN_INTERVAL,
|
||||
.max_conn_interval = BLE_MAX_CONN_INTERVAL,
|
||||
.slave_latency = BLE_SLAVE_LATENCY,
|
||||
.conn_sup_timeout = BLE_CONN_SUP_TIMEOUT,
|
||||
};
|
||||
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H
|
||||
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H
|
||||
|
||||
// Nothing yet.
|
||||
#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_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H
|
||||
|
|
|
@ -38,22 +38,18 @@
|
|||
#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/Peripheral.h"
|
||||
#include "shared-bindings/bleio/Service.h"
|
||||
#include "shared-bindings/bleio/UUID.h"
|
||||
|
||||
#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS)
|
||||
#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(300, UNIT_0_625_MS)
|
||||
#define BLE_SLAVE_LATENCY 0
|
||||
#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)
|
||||
|
||||
#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 = 0, // TODO: add bonding
|
||||
.bond = 1,
|
||||
.mitm = 0,
|
||||
.lesc = 0,
|
||||
.keypress = 0,
|
||||
|
@ -75,15 +71,23 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||
bleio_peripheral_obj_t *self = (bleio_peripheral_obj_t*)self_in;
|
||||
|
||||
// For debugging.
|
||||
// mp_printf(&mp_plat_print, "Peripheral event: 0x%04x\n", ble_evt->header.evt_id);
|
||||
mp_printf(&mp_plat_print, "Peripheral event: 0x%04x\n", ble_evt->header.evt_id);
|
||||
|
||||
switch (ble_evt->header.evt_id) {
|
||||
case BLE_GAP_EVT_CONNECTED: {
|
||||
// Central has connected.
|
||||
ble_gap_conn_params_t conn_params;
|
||||
ble_gap_evt_connected_t* connected = &ble_evt->evt.gap_evt.params.connected;
|
||||
|
||||
self->conn_handle = ble_evt->evt.gap_evt.conn_handle;
|
||||
|
||||
// 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);
|
||||
sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, &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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -102,21 +106,16 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||
}
|
||||
|
||||
case BLE_GAP_EVT_ADV_SET_TERMINATED:
|
||||
// Someday may handle timeouts or limit reached.
|
||||
// TODO: Someday may handle timeouts or limit reached.
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: {
|
||||
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_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_GATTS_EVT_EXCHANGE_MTU_REQUEST: {
|
||||
// We only handle MTU of size BLE_GATT_ATT_MTU_DEFAULT.
|
||||
sd_ble_gatts_exchange_mtu_reply(self->conn_handle, BLE_GATT_ATT_MTU_DEFAULT);
|
||||
break;
|
||||
}
|
||||
|
@ -125,9 +124,27 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||
sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
|
||||
sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &pairing_sec_params, NULL);
|
||||
case BLE_GAP_EVT_SEC_PARAMS_REQUEST: {
|
||||
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,
|
||||
&pairing_sec_params, &keyset);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVT_LESC_DHKEY_REQUEST:
|
||||
// TODO for LESC pairing:
|
||||
|
@ -138,15 +155,44 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||
// Pairing process completed
|
||||
ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status;
|
||||
if (BLE_GAP_SEC_STATUS_SUCCESS == status->auth_status) {
|
||||
// mp_printf(&mp_plat_print, "Pairing succeeded, status: 0x%04x\n", status->auth_status);
|
||||
// TODO _ediv = bonding_keys->own_enc.master_id.ediv;
|
||||
mp_printf(&mp_plat_print, "Pairing succeeded, status: 0x%04x\n", status->auth_status);
|
||||
self->pair_status = PAIR_PAIRED;
|
||||
} else {
|
||||
// mp_printf(&mp_plat_print, "Pairing failed, status: 0x%04x\n", status->auth_status);
|
||||
mp_printf(&mp_plat_print, "Pairing failed, status: 0x%04x\n", status->auth_status);
|
||||
self->pair_status = PAIR_NOT_PAIRED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVT_SEC_INFO_REQUEST: {
|
||||
// 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 ( bond_load_keys(_role, sec_req->master_id.ediv, &bkeys) ) {
|
||||
//sd_ble_gap_sec_info_reply(_conn_hdl, &bkeys.own_enc.enc_info, &bkeys.peer_id.id_info, NULL);
|
||||
//
|
||||
//_ediv = bkeys.own_enc.master_id.ediv;
|
||||
// } else {
|
||||
sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL);
|
||||
// }
|
||||
break;
|
||||
}
|
||||
|
||||
// case BLE_GATTS_EVT_WRITE: {
|
||||
// ble_gatts_evt_write_t* write = &ble_evt->evt.gatts_evt.params.write;
|
||||
// mp_printf(&mp_plat_print, "GATTS write uuid %x\n", write->uuid.uuid);
|
||||
// break;
|
||||
// }
|
||||
|
||||
// case BLE_GATTS_EVT_HVN_TX_COMPLETE: {
|
||||
// ble_gatts_evt_hvn_tx_complete_t* complete = &ble_evt->evt.gatts_evt.params.hvn_tx_complete;
|
||||
// mp_printf(&mp_plat_print, "HVN TX COMPLETE finished write num %d\n", complete->count);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case BLE_GAP_EVT_CONN_SEC_UPDATE: {
|
||||
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) {
|
||||
|
@ -156,11 +202,13 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||
// mode >=1 and/or level >=1 means encryption is set up
|
||||
self->pair_status = PAIR_NOT_PAIRED;
|
||||
} else {
|
||||
// TODO: see Bluefruit lib
|
||||
// if ( !bond_load_cccd(_role, _conn_hdl, _ediv) ) {
|
||||
// sd_ble_gatts_sys_attr_set(_conn_hdl, NULL, 0, 0);
|
||||
// }
|
||||
self->pair_status = PAIR_PAIRED;
|
||||
//if ( !bond_load_cccd(_role, _conn_hdl, _ediv) ) {
|
||||
if (true) { // TODO: no bonding yet
|
||||
// Initialize system attributes fresh.
|
||||
sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
|
||||
}
|
||||
// Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS SUCCESS.
|
||||
self->ediv = self->bonding_keys.own_enc.master_id.ediv;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -168,7 +216,7 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||
|
||||
default:
|
||||
// For debugging.
|
||||
// mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id);
|
||||
mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -185,6 +233,8 @@ void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_
|
|||
self->adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
|
||||
self->pair_status = PAIR_NOT_PAIRED;
|
||||
|
||||
memset(&self->bonding_keys, 0, sizeof(self->bonding_keys));
|
||||
|
||||
// Add all the services.
|
||||
|
||||
for (size_t service_idx = 0; service_idx < services_list->len; ++service_idx) {
|
||||
|
@ -237,9 +287,9 @@ void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *self,
|
|||
|
||||
GET_STR_DATA_LEN(self->name, name_data, name_len);
|
||||
if (name_len > 0) {
|
||||
// Set device name, and make it available to anyone.
|
||||
ble_gap_conn_sec_mode_t sec_mode;
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
|
||||
|
||||
err_code = sd_ble_gap_device_name_set(&sec_mode, name_data, name_len);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
mp_raise_OSError_msg_varg(translate("Failed to set device name, err 0x%04x"), err_code);
|
||||
|
@ -322,10 +372,12 @@ void common_hal_bleio_peripheral_pair(bleio_peripheral_obj_t *self) {
|
|||
}
|
||||
|
||||
while (self->pair_status == PAIR_WAITING) {
|
||||
MICROPY_VM_HOOK_LOOP;
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
|
||||
if (self->pair_status == PAIR_NOT_PAIRED) {
|
||||
mp_raise_OSError_msg(translate("Failed to pair"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "py/obj.h"
|
||||
#include "py/objlist.h"
|
||||
|
||||
#include "common-hal/bleio/__init__.h"
|
||||
#include "shared-module/bleio/Address.h"
|
||||
|
||||
typedef enum {
|
||||
|
@ -54,6 +55,9 @@ typedef struct {
|
|||
// 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).
|
||||
// EDIV: Encrypted Diversifier: Identifies LTK during legacy pairing.
|
||||
bonding_keys_t bonding_keys;
|
||||
uint16_t ediv;
|
||||
uint8_t* advertising_data;
|
||||
uint8_t* scan_response_data;
|
||||
uint8_t adv_handle;
|
||||
|
|
|
@ -89,13 +89,6 @@ void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self)
|
|||
.vloc = BLE_GATTS_VLOC_STACK,
|
||||
};
|
||||
|
||||
if (char_md.char_props.notify || char_md.char_props.indicate) {
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
|
||||
|
||||
char_md.p_cccd_md = &cccd_md;
|
||||
}
|
||||
|
||||
ble_uuid_t char_uuid;
|
||||
bleio_uuid_convert_to_nrf_ble_uuid(characteristic->uuid, &char_uuid);
|
||||
|
||||
|
@ -104,8 +97,16 @@ void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self)
|
|||
.vlen = !characteristic->fixed_length,
|
||||
};
|
||||
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&char_attr_md.read_perm);
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&char_attr_md.write_perm);
|
||||
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);
|
||||
|
||||
mp_buffer_info_t char_value_bufinfo;
|
||||
mp_get_buffer_raise(characteristic->value, &char_value_bufinfo, MP_BUFFER_READ);
|
||||
|
@ -146,8 +147,8 @@ void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self)
|
|||
.vlen = !descriptor->fixed_length,
|
||||
};
|
||||
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&desc_attr_md.read_perm);
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&desc_attr_md.write_perm);
|
||||
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);
|
||||
|
|
|
@ -29,6 +29,12 @@
|
|||
|
||||
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)
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "py/obj.h"
|
||||
|
||||
#include "common-hal/bleio/Attribute.h"
|
||||
#include "shared-module/bleio/Attribute.h"
|
||||
|
||||
extern const mp_obj_type_t bleio_attribute_type;
|
||||
|
|
|
@ -44,11 +44,12 @@
|
|||
|
||||
#include "common-hal/bleio/Peripheral.h"
|
||||
|
||||
#define ADV_INTERVAL_DEFAULT (1.0f)
|
||||
#define ADV_INTERVAL_MIN (0.0020f)
|
||||
#define ADV_INTERVAL_MIN_STRING "0.0020"
|
||||
#define ADV_INTERVAL_MAX (10.24f)
|
||||
#define ADV_INTERVAL_MAX_STRING "10.24"
|
||||
// 20ms is recommended by Apple
|
||||
#define ADV_INTERVAL_DEFAULT (0.1f)
|
||||
|
||||
//| .. currentmodule:: bleio
|
||||
//|
|
||||
|
@ -183,7 +184,7 @@ const mp_obj_property_t bleio_peripheral_name_obj = {
|
|||
(mp_obj_t)&mp_const_none_obj },
|
||||
};
|
||||
|
||||
//| .. method:: start_advertising(data, *, scan_response=None, connectable=True, interval=1)
|
||||
//| .. method:: start_advertising(data, *, scan_response=None, connectable=True, interval=0.1)
|
||||
//|
|
||||
//| Starts advertising the peripheral. The peripheral's name and
|
||||
//| services are included in the advertisement packets.
|
||||
|
@ -217,7 +218,7 @@ STATIC mp_obj_t bleio_peripheral_start_advertising(mp_uint_t n_args, const mp_ob
|
|||
}
|
||||
|
||||
if (args[ARG_interval].u_obj == MP_OBJ_NULL) {
|
||||
args[ARG_interval].u_obj = mp_obj_new_float(1.0F);
|
||||
args[ARG_interval].u_obj = mp_obj_new_float(ADV_INTERVAL_DEFAULT);
|
||||
}
|
||||
|
||||
const mp_float_t interval = mp_obj_float_get(args[ARG_interval].u_obj);
|
||||
|
|
|
@ -31,9 +31,9 @@
|
|||
|
||||
#include "py/objlist.h"
|
||||
|
||||
#include "shared-bindings/bleio/__init__.h"
|
||||
#include "shared-bindings/bleio/Adapter.h"
|
||||
|
||||
#include "common-hal/bleio/__init__.h"
|
||||
#include "common-hal/bleio/Adapter.h"
|
||||
|
||||
extern const super_adapter_obj_t common_hal_bleio_adapter_obj;
|
||||
|
|
Loading…
Reference in New Issue