Merge pull request #2510 from dhalbert/bonding-nvm
nrf: Add bonding to BLE pairing support
This commit is contained in:
commit
8b61333937
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-01-07 14:31-0800\n"
|
"POT-Creation-Date: 2020-01-13 18:15-0500\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -513,21 +513,21 @@ msgstr ""
|
|||||||
msgid "Could not initialize UART"
|
msgid "Could not initialize UART"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-module/audiomp3/MP3File.c
|
#: shared-module/audiomp3/MP3Decoder.c
|
||||||
msgid "Couldn't allocate decoder"
|
msgid "Couldn't allocate decoder"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-module/audiocore/WaveFile.c shared-module/audiomixer/Mixer.c
|
#: shared-module/audiocore/WaveFile.c shared-module/audiomixer/Mixer.c
|
||||||
#: shared-module/audiomp3/MP3File.c
|
#: shared-module/audiomp3/MP3Decoder.c
|
||||||
msgid "Couldn't allocate first buffer"
|
msgid "Couldn't allocate first buffer"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-module/audiomp3/MP3File.c
|
#: shared-module/audiomp3/MP3Decoder.c
|
||||||
msgid "Couldn't allocate input buffer"
|
msgid "Couldn't allocate input buffer"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-module/audiocore/WaveFile.c shared-module/audiomixer/Mixer.c
|
#: shared-module/audiocore/WaveFile.c shared-module/audiomixer/Mixer.c
|
||||||
#: shared-module/audiomp3/MP3File.c
|
#: shared-module/audiomp3/MP3Decoder.c
|
||||||
msgid "Couldn't allocate second buffer"
|
msgid "Couldn't allocate second buffer"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -620,7 +620,7 @@ msgstr ""
|
|||||||
msgid "Failed sending command."
|
msgid "Failed sending command."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ports/nrf/sd_mutex.c
|
#: ports/nrf/sd.c ports/nrf/sd_mutex.c
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Failed to acquire mutex, err 0x%04x"
|
msgid "Failed to acquire mutex, err 0x%04x"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -643,11 +643,11 @@ msgstr ""
|
|||||||
msgid "Failed to connect: timeout"
|
msgid "Failed to connect: timeout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-module/audiomp3/MP3File.c
|
#: shared-module/audiomp3/MP3Decoder.c
|
||||||
msgid "Failed to parse MP3 file"
|
msgid "Failed to parse MP3 file"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ports/nrf/sd_mutex.c
|
#: ports/nrf/sd.c ports/nrf/sd_mutex.c
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Failed to release mutex, err 0x%04x"
|
msgid "Failed to release mutex, err 0x%04x"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -660,10 +660,6 @@ msgstr ""
|
|||||||
msgid "File exists"
|
msgid "File exists"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ports/nrf/common-hal/nvm/ByteArray.c
|
|
||||||
msgid "Flash write failed"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c
|
#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c
|
||||||
msgid "Frequency captured is above capability. Capture Paused."
|
msgid "Frequency captured is above capability. Capture Paused."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -1776,7 +1772,7 @@ msgstr ""
|
|||||||
msgid "extra positional arguments given"
|
msgid "extra positional arguments given"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3File.c
|
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
|
||||||
#: shared-bindings/displayio/OnDiskBitmap.c
|
#: shared-bindings/displayio/OnDiskBitmap.c
|
||||||
msgid "file must be a file opened in byte mode"
|
msgid "file must be a file opened in byte mode"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -43,9 +43,9 @@ bool common_hal_nvm_bytearray_set_bytes(nvm_bytearray_obj_t *self,
|
|||||||
// whenever we need it instead of storing it long term.
|
// whenever we need it instead of storing it long term.
|
||||||
struct flash_descriptor desc;
|
struct flash_descriptor desc;
|
||||||
desc.dev.hw = NVMCTRL;
|
desc.dev.hw = NVMCTRL;
|
||||||
flash_write(&desc, (uint32_t) self->start_address + start_index, values, len);
|
bool status = flash_write(&desc, (uint32_t) self->start_address + start_index, values, len) == ERR_NONE;
|
||||||
assert_heap_ok();
|
assert_heap_ok();
|
||||||
return true;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NVM memory is memory mapped so reading it is easy.
|
// NVM memory is memory mapped so reading it is easy.
|
||||||
|
@ -73,8 +73,6 @@ INC += -I$(BUILD)
|
|||||||
INC += -I$(BUILD)/genhdr
|
INC += -I$(BUILD)/genhdr
|
||||||
INC += -I./../../lib/cmsis/inc
|
INC += -I./../../lib/cmsis/inc
|
||||||
INC += -I./boards/$(BOARD)
|
INC += -I./boards/$(BOARD)
|
||||||
INC += -I./modules/ubluepy
|
|
||||||
INC += -I./modules/ble
|
|
||||||
INC += -I./nrfx
|
INC += -I./nrfx
|
||||||
INC += -I./nrfx/hal
|
INC += -I./nrfx/hal
|
||||||
INC += -I./nrfx/mdk
|
INC += -I./nrfx/mdk
|
||||||
@ -157,6 +155,7 @@ SRC_C += \
|
|||||||
boards/$(BOARD)/pins.c \
|
boards/$(BOARD)/pins.c \
|
||||||
device/$(MCU_VARIANT)/startup_$(MCU_SUB_VARIANT).c \
|
device/$(MCU_VARIANT)/startup_$(MCU_SUB_VARIANT).c \
|
||||||
bluetooth/ble_drv.c \
|
bluetooth/ble_drv.c \
|
||||||
|
common-hal/_bleio/bonding.c \
|
||||||
lib/libc/string0.c \
|
lib/libc/string0.c \
|
||||||
lib/mp-readline/readline.c \
|
lib/mp-readline/readline.c \
|
||||||
lib/oofatfs/ff.c \
|
lib/oofatfs/ff.c \
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
|
|
||||||
#if CIRCUITPY_BLEIO
|
#if CIRCUITPY_BLEIO
|
||||||
#include "supervisor/shared/bluetooth.h"
|
#include "supervisor/shared/bluetooth.h"
|
||||||
|
#include "common-hal/_bleio/bonding.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool running_background_tasks = false;
|
static bool running_background_tasks = false;
|
||||||
@ -68,6 +69,7 @@ void run_background_tasks(void) {
|
|||||||
|
|
||||||
#if CIRCUITPY_BLEIO
|
#if CIRCUITPY_BLEIO
|
||||||
supervisor_bluetooth_background();
|
supervisor_bluetooth_background();
|
||||||
|
bonding_background();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if CIRCUITPY_DISPLAYIO
|
#if CIRCUITPY_DISPLAYIO
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include "ble.h"
|
#include "ble.h"
|
||||||
#include "ble_drv.h"
|
#include "ble_drv.h"
|
||||||
|
#include "bonding.h"
|
||||||
#include "nrfx_power.h"
|
#include "nrfx_power.h"
|
||||||
#include "nrf_nvic.h"
|
#include "nrf_nvic.h"
|
||||||
#include "nrf_sdm.h"
|
#include "nrf_sdm.h"
|
||||||
@ -248,6 +249,7 @@ STATIC bool adapter_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||||||
}
|
}
|
||||||
ble_drv_remove_event_handler(connection_on_ble_evt, connection);
|
ble_drv_remove_event_handler(connection_on_ble_evt, connection);
|
||||||
connection->conn_handle = BLE_CONN_HANDLE_INVALID;
|
connection->conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||||
|
connection->pair_status = PAIR_NOT_PAIRED;
|
||||||
if (connection->connection_obj != mp_const_none) {
|
if (connection->connection_obj != mp_const_none) {
|
||||||
bleio_connection_obj_t* obj = connection->connection_obj;
|
bleio_connection_obj_t* obj = connection->connection_obj;
|
||||||
obj->connection = NULL;
|
obj->connection = NULL;
|
||||||
@ -692,6 +694,10 @@ mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self) {
|
|||||||
return self->connection_objs;
|
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) {
|
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**)adapter, sizeof(bleio_adapter_obj_t) / sizeof(size_t));
|
||||||
gc_collect_root((void**)bleio_connections, sizeof(bleio_connections) / sizeof(size_t));
|
gc_collect_root((void**)bleio_connections, sizeof(bleio_connections) / sizeof(size_t));
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "shared-bindings/_bleio/Service.h"
|
#include "shared-bindings/_bleio/Service.h"
|
||||||
|
|
||||||
#include "common-hal/_bleio/Adapter.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) {
|
STATIC uint16_t characteristic_get_cccd(uint16_t cccd_handle, uint16_t conn_handle) {
|
||||||
uint16_t cccd;
|
uint16_t cccd;
|
||||||
|
@ -46,13 +46,16 @@
|
|||||||
#include "shared-bindings/_bleio/Characteristic.h"
|
#include "shared-bindings/_bleio/Characteristic.h"
|
||||||
#include "shared-bindings/_bleio/Service.h"
|
#include "shared-bindings/_bleio/Service.h"
|
||||||
#include "shared-bindings/_bleio/UUID.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_LENGTH_FIELD_SIZE 1
|
||||||
#define BLE_ADV_AD_TYPE_FIELD_SIZE 1
|
#define BLE_ADV_AD_TYPE_FIELD_SIZE 1
|
||||||
#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1
|
#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1
|
||||||
|
|
||||||
static const ble_gap_sec_params_t pairing_sec_params = {
|
static const ble_gap_sec_params_t pairing_sec_params = {
|
||||||
.bond = 0,
|
.bond = 1,
|
||||||
.mitm = 0,
|
.mitm = 0,
|
||||||
.lesc = 0,
|
.lesc = 0,
|
||||||
.keypress = 0,
|
.keypress = 0,
|
||||||
@ -64,6 +67,13 @@ static const ble_gap_sec_params_t pairing_sec_params = {
|
|||||||
.kdist_peer = { .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_in_process;
|
||||||
static volatile bool m_discovery_successful;
|
static volatile bool m_discovery_successful;
|
||||||
|
|
||||||
@ -84,6 +94,7 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||||||
|
|
||||||
switch (ble_evt->header.evt_id) {
|
switch (ble_evt->header.evt_id) {
|
||||||
case BLE_GAP_EVT_DISCONNECTED:
|
case BLE_GAP_EVT_DISCONNECTED:
|
||||||
|
// Adapter.c does the work for this event.
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BLE_GAP_EVT_PHY_UPDATE_REQUEST: {
|
case BLE_GAP_EVT_PHY_UPDATE_REQUEST: {
|
||||||
@ -137,6 +148,22 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||||||
break;
|
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();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case BLE_GATTS_EVT_SYS_ATTR_MISSING:
|
case BLE_GATTS_EVT_SYS_ATTR_MISSING:
|
||||||
sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
|
sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
|
||||||
break;
|
break;
|
||||||
@ -197,6 +224,15 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BLE_GAP_EVT_SEC_PARAMS_REQUEST: {
|
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 = {
|
ble_gap_sec_keyset_t keyset = {
|
||||||
.keys_own = {
|
.keys_own = {
|
||||||
.p_enc_key = &self->bonding_keys.own_enc,
|
.p_enc_key = &self->bonding_keys.own_enc,
|
||||||
@ -214,7 +250,8 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_SUCCESS,
|
sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_SUCCESS,
|
||||||
&pairing_sec_params, &keyset);
|
self->is_central ? NULL : &pairing_sec_params,
|
||||||
|
&keyset);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,13 +261,16 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BLE_GAP_EVT_AUTH_STATUS: { // 0x19
|
case BLE_GAP_EVT_AUTH_STATUS: { // 0x19
|
||||||
// Pairing process completed
|
// Key exchange completed.
|
||||||
ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status;
|
ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status;
|
||||||
self->sec_status = status->auth_status;
|
self->sec_status = status->auth_status;
|
||||||
if (status->auth_status == BLE_GAP_SEC_STATUS_SUCCESS) {
|
if (status->auth_status == BLE_GAP_SEC_STATUS_SUCCESS) {
|
||||||
// TODO _ediv = bonding_keys->own_enc.master_id.ediv;
|
self->ediv = self->bonding_keys.own_enc.master_id.ediv;
|
||||||
self->pair_status = PAIR_PAIRED;
|
self->pair_status = PAIR_PAIRED;
|
||||||
|
// Save keys in bonding area at next opportunity.
|
||||||
|
self->do_bond_keys = true;
|
||||||
} else {
|
} else {
|
||||||
|
// Inform busy-waiter pairing has failed.
|
||||||
self->pair_status = PAIR_NOT_PAIRED;
|
self->pair_status = PAIR_NOT_PAIRED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -242,17 +282,22 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||||||
// - Else return NULL --> Initiate key exchange
|
// - 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;
|
ble_gap_evt_sec_info_request_t* sec_info_request = &ble_evt->evt.gap_evt.params.sec_info_request;
|
||||||
(void) sec_info_request;
|
(void) sec_info_request;
|
||||||
//if ( bond_load_keys(_role, sec_req->master_id.ediv, &bkeys) ) {
|
if ( bonding_load_keys(self->is_central, sec_info_request->master_id.ediv, &self->bonding_keys) ) {
|
||||||
//sd_ble_gap_sec_info_reply(_conn_hdl, &bkeys.own_enc.enc_info, &bkeys.peer_id.id_info, NULL);
|
sd_ble_gap_sec_info_reply(
|
||||||
//
|
self->conn_handle,
|
||||||
//_ediv = bkeys.own_enc.master_id.ediv;
|
&self->bonding_keys.own_enc.enc_info,
|
||||||
// } else {
|
&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);
|
sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL);
|
||||||
// }
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case BLE_GAP_EVT_CONN_SEC_UPDATE: { // 0x1a
|
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;
|
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) {
|
if (conn_sec->sec_mode.sm <= 1 && conn_sec->sec_mode.lv <= 1) {
|
||||||
// Security setup did not succeed:
|
// Security setup did not succeed:
|
||||||
@ -261,18 +306,17 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
|||||||
// mode >=1 and/or level >=1 means encryption is set up
|
// mode >=1 and/or level >=1 means encryption is set up
|
||||||
self->pair_status = PAIR_NOT_PAIRED;
|
self->pair_status = PAIR_NOT_PAIRED;
|
||||||
} else {
|
} else {
|
||||||
//if ( !bond_load_cccd(_role, _conn_hdl, _ediv) ) {
|
if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv)) {
|
||||||
if (true) { // TODO: no bonding yet
|
// Did an sd_ble_gatts_sys_attr_set() with the stored sys_attr values.
|
||||||
// Initialize system attributes fresh.
|
} else {
|
||||||
|
// No matching bonding found, so use fresh system attributes.
|
||||||
sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
|
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->pair_status = PAIR_PAIRED;
|
||||||
self->ediv = self->bonding_keys.own_enc.master_id.ediv;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -284,8 +328,7 @@ void bleio_connection_clear(bleio_connection_internal_t *self) {
|
|||||||
|
|
||||||
self->conn_handle = BLE_CONN_HANDLE_INVALID;
|
self->conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||||
self->pair_status = PAIR_NOT_PAIRED;
|
self->pair_status = PAIR_NOT_PAIRED;
|
||||||
|
bonding_clear_keys(&self->bonding_keys);
|
||||||
memset(&self->bonding_keys, 0, sizeof(self->bonding_keys));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool common_hal_bleio_connection_get_paired(bleio_connection_obj_t *self) {
|
bool common_hal_bleio_connection_get_paired(bleio_connection_obj_t *self) {
|
||||||
@ -565,8 +608,7 @@ STATIC bool discovery_on_ble_evt(ble_evt_t *ble_evt, mp_obj_t payload) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// For debugging.
|
// CONNECTION_DEBUG_PRINTF(&mp_plat_print, "Unhandled discovery event: 0x%04x\n", ble_evt->header.evt_id);
|
||||||
// mp_printf(&mp_plat_print, "Unhandled discovery event: 0x%04x\n", ble_evt->header.evt_id);
|
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "py/objlist.h"
|
#include "py/objlist.h"
|
||||||
|
|
||||||
#include "common-hal/_bleio/__init__.h"
|
#include "common-hal/_bleio/__init__.h"
|
||||||
|
#include "common-hal/_bleio/bonding.h"
|
||||||
#include "shared-module/_bleio/Address.h"
|
#include "shared-module/_bleio/Address.h"
|
||||||
#include "common-hal/_bleio/Service.h"
|
#include "common-hal/_bleio/Service.h"
|
||||||
|
|
||||||
@ -56,16 +57,23 @@ typedef struct {
|
|||||||
// The advertising data and scan response buffers are held by us, not by the SD, so we must
|
// 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,
|
// 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).
|
// 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;
|
bonding_keys_t bonding_keys;
|
||||||
|
// EDIV: Encrypted Diversifier: Identifies LTK during legacy pairing.
|
||||||
uint16_t ediv;
|
uint16_t ediv;
|
||||||
pair_status_t pair_status;
|
volatile pair_status_t pair_status;
|
||||||
uint8_t sec_status; // Internal security status.
|
uint8_t sec_status; // Internal security status.
|
||||||
mp_obj_t connection_obj;
|
mp_obj_t connection_obj;
|
||||||
ble_drv_evt_handler_entry_t handler_entry;
|
ble_drv_evt_handler_entry_t handler_entry;
|
||||||
ble_gap_conn_params_t conn_params;
|
ble_gap_conn_params_t conn_params;
|
||||||
volatile bool conn_params_updating;
|
volatile bool conn_params_updating;
|
||||||
uint16_t mtu;
|
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;
|
} bleio_connection_internal_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -97,6 +97,7 @@ void bleio_reset() {
|
|||||||
common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false);
|
common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false);
|
||||||
}
|
}
|
||||||
supervisor_start_bluetooth();
|
supervisor_start_bluetooth();
|
||||||
|
bonding_reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The singleton _bleio.Adapter object, bound to _bleio.adapter
|
// The singleton _bleio.Adapter object, bound to _bleio.adapter
|
||||||
|
309
ports/nrf/common-hal/_bleio/bonding.c
Normal file
309
ports/nrf/common-hal/_bleio/bonding.c
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ble.h"
|
||||||
|
#include "ble_drv.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 "nrf_soc.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];
|
||||||
|
|
||||||
|
uint64_t current_ticks_ms = supervisor_ticks_ms64();
|
||||||
|
// 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 &&
|
||||||
|
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;
|
||||||
|
}
|
85
ports/nrf/common-hal/_bleio/bonding.h
Normal file
85
ports/nrf/common-hal/_bleio/bonding.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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_NRF_COMMON_HAL_BLEIO_BONDING_H
|
||||||
|
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_BONDING_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "ble.h"
|
||||||
|
#include "ble_drv.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_NRF_COMMON_HAL_BLEIO_BONDING_H
|
@ -36,21 +36,17 @@ uint32_t common_hal_nvm_bytearray_get_length(nvm_bytearray_obj_t *self) {
|
|||||||
return self->len;
|
return self->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_page(uint32_t page_addr, uint32_t offset, uint32_t len, uint8_t *bytes) {
|
static bool write_page(uint32_t page_addr, uint32_t offset, uint32_t len, uint8_t *bytes) {
|
||||||
// Write a whole page to flash, buffering it first and then erasing and rewriting
|
// Write a whole page to flash, buffering it first and then erasing and rewriting
|
||||||
// it since we can only clear a whole page at a time.
|
// it since we can only clear a whole page at a time.
|
||||||
|
|
||||||
bool status;
|
|
||||||
if (offset == 0 && len == FLASH_PAGE_SIZE) {
|
if (offset == 0 && len == FLASH_PAGE_SIZE) {
|
||||||
status = nrf_nvm_safe_flash_page_write(page_addr, bytes);
|
return nrf_nvm_safe_flash_page_write(page_addr, bytes);
|
||||||
} else {
|
} else {
|
||||||
uint8_t buffer[FLASH_PAGE_SIZE];
|
uint8_t buffer[FLASH_PAGE_SIZE];
|
||||||
memcpy(buffer, (uint8_t *)page_addr, FLASH_PAGE_SIZE);
|
memcpy(buffer, (uint8_t *)page_addr, FLASH_PAGE_SIZE);
|
||||||
memcpy(buffer + offset, bytes, len);
|
memcpy(buffer + offset, bytes, len);
|
||||||
status = nrf_nvm_safe_flash_page_write(page_addr, buffer);
|
return nrf_nvm_safe_flash_page_write(page_addr, buffer);
|
||||||
}
|
|
||||||
if (!status) {
|
|
||||||
mp_raise_OSError_msg(translate("Flash write failed"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +59,9 @@ bool common_hal_nvm_bytearray_set_bytes(nvm_bytearray_obj_t *self,
|
|||||||
|
|
||||||
while (len) {
|
while (len) {
|
||||||
uint32_t write_len = MIN(len, FLASH_PAGE_SIZE - offset);
|
uint32_t write_len = MIN(len, FLASH_PAGE_SIZE - offset);
|
||||||
write_page(page_addr, offset, write_len, values);
|
if (!write_page(page_addr, offset, write_len, values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
len -= write_len;
|
len -= write_len;
|
||||||
values += write_len;
|
values += write_len;
|
||||||
page_addr += FLASH_PAGE_SIZE;
|
page_addr += FLASH_PAGE_SIZE;
|
||||||
|
@ -152,10 +152,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define MICROPY_PORT_ROOT_POINTERS \
|
#define MICROPY_PORT_ROOT_POINTERS \
|
||||||
CIRCUITPY_COMMON_ROOT_POINTERS \
|
CIRCUITPY_COMMON_ROOT_POINTERS \
|
||||||
ble_drv_evt_handler_entry_t* ble_drv_evt_handler_entries; \
|
ble_drv_evt_handler_entry_t* ble_drv_evt_handler_entries; \
|
||||||
|
|
||||||
|
|
||||||
#endif // NRF5_MPCONFIGPORT_H__
|
#endif // NRF5_MPCONFIGPORT_H__
|
||||||
|
@ -38,23 +38,62 @@
|
|||||||
#include "ble_drv.h"
|
#include "ble_drv.h"
|
||||||
#include "nrf_sdm.h"
|
#include "nrf_sdm.h"
|
||||||
|
|
||||||
|
STATIC bool sd_is_enabled(void) {
|
||||||
|
uint8_t sd_en = 0;
|
||||||
|
(void) sd_softdevice_is_enabled(&sd_en);
|
||||||
|
return sd_en;
|
||||||
|
}
|
||||||
|
|
||||||
STATIC void sd_flash_operation_start(void) {
|
STATIC void sd_flash_operation_start(void) {
|
||||||
sd_flash_operation_status = SD_FLASH_OPERATION_IN_PROGRESS;
|
sd_flash_operation_status = SD_FLASH_OPERATION_IN_PROGRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC sd_flash_operation_status_t sd_flash_operation_wait_until_done(void) {
|
STATIC sd_flash_operation_status_t sd_flash_operation_wait_until_done(void) {
|
||||||
|
// If the SD is not enabled, no events are generated, so just return immediately.
|
||||||
|
if (sd_is_enabled()) {
|
||||||
while (sd_flash_operation_status == SD_FLASH_OPERATION_IN_PROGRESS) {
|
while (sd_flash_operation_status == SD_FLASH_OPERATION_IN_PROGRESS) {
|
||||||
sd_app_evt_wait();
|
sd_app_evt_wait();
|
||||||
}
|
}
|
||||||
return sd_flash_operation_status;
|
} else {
|
||||||
|
sd_flash_operation_status = SD_FLASH_OPERATION_DONE;
|
||||||
}
|
}
|
||||||
|
return sd_flash_operation_status;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sd_flash_page_erase_sync(uint32_t page_number) {
|
||||||
|
sd_flash_operation_start();
|
||||||
|
if (sd_flash_page_erase(page_number) != NRF_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sd_flash_operation_wait_until_done() == SD_FLASH_OPERATION_ERROR) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sd_flash_write_sync(uint32_t *dest_words, uint32_t* src_words, uint32_t num_words) {
|
||||||
|
sd_flash_operation_start();
|
||||||
|
if (sd_flash_write(dest_words, src_words, num_words) != NRF_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sd_flash_operation_wait_until_done() == SD_FLASH_OPERATION_ERROR) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// The nRF52840 datasheet specifies a maximum of two writes to a flash
|
||||||
|
// location before an erase is necessary, even if the write is all
|
||||||
|
// ones (erased state). So we can't avoid erases even if the page
|
||||||
|
// appears to be already erased (all ones), unless we keep track of
|
||||||
|
// writes to a page.
|
||||||
|
|
||||||
bool nrf_nvm_safe_flash_page_write(uint32_t page_addr, uint8_t *data) {
|
bool nrf_nvm_safe_flash_page_write(uint32_t page_addr, uint8_t *data) {
|
||||||
#ifdef BLUETOOTH_SD
|
#ifdef BLUETOOTH_SD
|
||||||
uint8_t sd_en = 0;
|
if (sd_is_enabled()) {
|
||||||
(void) sd_softdevice_is_enabled(&sd_en);
|
|
||||||
if (sd_en) {
|
|
||||||
uint32_t err_code;
|
uint32_t err_code;
|
||||||
sd_flash_operation_status_t status;
|
sd_flash_operation_status_t status;
|
||||||
|
|
||||||
|
@ -27,4 +27,9 @@
|
|||||||
|
|
||||||
#define FLASH_PAGE_SIZE (4096)
|
#define FLASH_PAGE_SIZE (4096)
|
||||||
|
|
||||||
|
#if BLUETOOTH_SD
|
||||||
|
bool sd_flash_page_erase_sync(uint32_t page_number);
|
||||||
|
bool sd_flash_write_sync(uint32_t *dest_words, uint32_t* src_words, uint32_t num_words);
|
||||||
|
#endif
|
||||||
|
|
||||||
bool nrf_nvm_safe_flash_page_write(uint32_t page_addr, uint8_t *data);
|
bool nrf_nvm_safe_flash_page_write(uint32_t page_addr, uint8_t *data);
|
||||||
|
4
py/gc.c
4
py/gc.c
@ -464,6 +464,10 @@ void gc_info(gc_info_t *info) {
|
|||||||
GC_EXIT();
|
GC_EXIT();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool gc_alloc_possible(void) {
|
||||||
|
return MP_STATE_MEM(gc_pool_start) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
// We place long lived objects at the end of the heap rather than the start. This reduces
|
// We place long lived objects at the end of the heap rather than the start. This reduces
|
||||||
// fragmentation by localizing the heap churn to one portion of memory (the start of the heap.)
|
// fragmentation by localizing the heap churn to one portion of memory (the start of the heap.)
|
||||||
void *gc_alloc(size_t n_bytes, bool has_finaliser, bool long_lived) {
|
void *gc_alloc(size_t n_bytes, bool has_finaliser, bool long_lived) {
|
||||||
|
2
py/gc.h
2
py/gc.h
@ -58,6 +58,8 @@ void gc_collect_ptr(void *ptr);
|
|||||||
void gc_collect_root(void **ptrs, size_t len);
|
void gc_collect_root(void **ptrs, size_t len);
|
||||||
void gc_collect_end(void);
|
void gc_collect_end(void);
|
||||||
|
|
||||||
|
// Is the gc heap available?
|
||||||
|
bool gc_alloc_possible(void);
|
||||||
void *gc_alloc(size_t n_bytes, bool has_finaliser, bool long_lived);
|
void *gc_alloc(size_t n_bytes, bool has_finaliser, bool long_lived);
|
||||||
|
|
||||||
// Use this function to sweep the whole heap and run all finalisers
|
// Use this function to sweep the whole heap and run all finalisers
|
||||||
|
@ -360,6 +360,17 @@ STATIC mp_obj_t bleio_adapter_connect(mp_uint_t n_args, const mp_obj_t *pos_args
|
|||||||
}
|
}
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_connect_obj, 2, bleio_adapter_connect);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_connect_obj, 2, bleio_adapter_connect);
|
||||||
|
|
||||||
|
//| .. method:: erase_bonding()
|
||||||
|
//|
|
||||||
|
//| Erase all bonding information stored in flash memory.
|
||||||
|
STATIC mp_obj_t bleio_adapter_erase_bonding(mp_obj_t self_in) {
|
||||||
|
bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
|
||||||
|
common_hal_bleio_adapter_erase_bonding(self);
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_erase_bonding_obj, bleio_adapter_erase_bonding);
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t bleio_adapter_locals_dict_table[] = {
|
STATIC const mp_rom_map_elem_t bleio_adapter_locals_dict_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&bleio_adapter_enabled_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&bleio_adapter_enabled_obj) },
|
||||||
@ -376,6 +387,8 @@ STATIC const mp_rom_map_elem_t bleio_adapter_locals_dict_table[] = {
|
|||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_adapter_connected_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_adapter_connected_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_connections), MP_ROM_PTR(&bleio_adapter_connections_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_connections), MP_ROM_PTR(&bleio_adapter_connections_obj) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_erase_bonding), MP_ROM_PTR(&bleio_adapter_erase_bonding_obj) },
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC MP_DEFINE_CONST_DICT(bleio_adapter_locals_dict, bleio_adapter_locals_dict_table);
|
STATIC MP_DEFINE_CONST_DICT(bleio_adapter_locals_dict, bleio_adapter_locals_dict_table);
|
||||||
|
@ -48,13 +48,15 @@ extern void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const c
|
|||||||
extern uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, float interval, uint8_t *advertising_data, uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len);
|
extern uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, float interval, uint8_t *advertising_data, uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len);
|
||||||
|
|
||||||
extern void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, mp_float_t interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo);
|
extern void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, mp_float_t interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo);
|
||||||
void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self);
|
extern void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self);
|
||||||
|
|
||||||
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);
|
extern 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);
|
||||||
void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self);
|
extern void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self);
|
||||||
|
|
||||||
bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self);
|
extern bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self);
|
||||||
mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self);
|
extern mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self);
|
||||||
mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout);
|
extern mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout);
|
||||||
|
|
||||||
|
extern void common_hal_bleio_adapter_erase_bonding(bleio_adapter_obj_t *self);
|
||||||
|
|
||||||
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H
|
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H
|
||||||
|
@ -292,7 +292,6 @@ STATIC mp_obj_t bleio_characteristic_set_cccd(mp_uint_t n_args, const mp_obj_t *
|
|||||||
}
|
}
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_set_cccd_obj, 1, bleio_characteristic_set_cccd);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_set_cccd_obj, 1, bleio_characteristic_set_cccd);
|
||||||
|
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = {
|
STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR_add_to_service), MP_ROM_PTR(&bleio_characteristic_add_to_service_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_add_to_service), MP_ROM_PTR(&bleio_characteristic_add_to_service_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&bleio_characteristic_properties_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&bleio_characteristic_properties_obj) },
|
||||||
|
Loading…
Reference in New Issue
Block a user