wip; compiles
This commit is contained in:
parent
242d572470
commit
390337b9a5
|
@ -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
|
||||||
|
@ -156,6 +154,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"
|
||||||
|
@ -669,4 +670,5 @@ void bleio_adapter_reset(bleio_adapter_obj_t* adapter) {
|
||||||
bleio_connection_internal_t *connection = &connections[i];
|
bleio_connection_internal_t *connection = &connections[i];
|
||||||
connection->connection_obj = mp_const_none;
|
connection->connection_obj = mp_const_none;
|
||||||
}
|
}
|
||||||
|
bonding_reset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -92,9 +93,11 @@ STATIC bool characteristic_on_ble_evt(ble_evt_t *ble_evt, void *param) {
|
||||||
bleio_connection_obj_t *connection = self->service->connection;
|
bleio_connection_obj_t *connection = self->service->connection;
|
||||||
uint16_t conn_handle = bleio_connection_get_conn_handle(connection);
|
uint16_t conn_handle = bleio_connection_get_conn_handle(connection);
|
||||||
if (conn_handle != BLE_CONN_HANDLE_INVALID &&
|
if (conn_handle != BLE_CONN_HANDLE_INVALID &&
|
||||||
connection->pairing_status == PAIR_PAIRED &&
|
common_hal_bleio_connection_get_paired(connection) &&
|
||||||
ble_evt->gatts_evt.params.write.handle == self->cccd_handle) {
|
ble_evt->evt.gatts_evt.params.write.handle == self->cccd_handle) {
|
||||||
bonding_save_cccd_later(connection->is_central, conn_handle, connection->ediv);
|
bonding_save_cccd_info(
|
||||||
|
connection->connection->is_central, conn_handle, connection->connection->ediv);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@
|
||||||
#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 "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
|
||||||
|
@ -212,7 +214,7 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
||||||
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) {
|
||||||
self->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;
|
||||||
bonding_save_keys(self->is_central, self->conn_handle, &self->bonding_keys);
|
bonding_save_keys(self->is_central, self->conn_handle, &self->bonding_keys);
|
||||||
} else {
|
} else {
|
||||||
|
@ -227,13 +229,12 @@ 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;
|
||||||
bond_keys bond_keys_t;
|
|
||||||
if ( bonding_load_keys(self->is_central, sec_info_request->master_id.ediv, &self->bonding_keys) ) {
|
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
|
sd_ble_gap_sec_info_reply(self->conn_handle,
|
||||||
&self->bonding_keys.own_enc.enc_info,
|
&self->bonding_keys.own_enc.enc_info,
|
||||||
&self->bonding_keys.peer_id.id_info,
|
&self->bonding_keys.peer_id.id_info,
|
||||||
NULL);
|
NULL);
|
||||||
self->ediv = bond_keys.own_enc.master_id.ediv;
|
self->ediv = self->bonding_keys.own_enc.master_id.ediv;
|
||||||
} else {
|
} else {
|
||||||
sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL);
|
sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
@ -249,12 +250,10 @@ 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 {
|
||||||
uint8_t *sys_attr;
|
// Does an sd_ble_gatts_sys_attr_set() with the stored values.
|
||||||
uint16_t sys_attr_len;
|
if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv)) {
|
||||||
if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv, sys_attr, sys_attr_len)) {
|
// Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS SUCCESS.
|
||||||
sd_ble_gatts_sys_attr_set(self->conn_handle, sys_attr, sys_attr_len, SVC_CONTEXT_FLAG);
|
self->ediv = self->bonding_keys.own_enc.master_id.ediv;
|
||||||
// Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS SUCCESS.
|
|
||||||
self->ediv = self->bonding_keys.own_enc.master_id.ediv;
|
|
||||||
} else {
|
} else {
|
||||||
// No matching bonding found, so use fresh system attributes.
|
// 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);
|
||||||
|
@ -263,10 +262,6 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case BLE_GATTS_EVT_WRITE: {
|
|
||||||
if (self->pair_status == PAIR_PAIRED) &&
|
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -278,7 +273,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_clear_keys(&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) {
|
||||||
|
|
|
@ -30,9 +30,9 @@
|
||||||
void bleio_reset(void);
|
void bleio_reset(void);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ble_gap_enc_key_t own_enc;
|
ble_gap_enc_key_t own_enc;
|
||||||
ble_gap_enc_key_t peer_enc;
|
ble_gap_enc_key_t peer_enc;
|
||||||
ble_gap_id_key_t peer_id;
|
ble_gap_id_key_t peer_id;
|
||||||
} bonding_keys_t;
|
} bonding_keys_t;
|
||||||
|
|
||||||
// We assume variable length data.
|
// We assume variable length data.
|
||||||
|
|
|
@ -34,6 +34,9 @@
|
||||||
#include "shared-bindings/_bleio/Adapter.h"
|
#include "shared-bindings/_bleio/Adapter.h"
|
||||||
#include "shared-bindings/nvm/ByteArray.h"
|
#include "shared-bindings/nvm/ByteArray.h"
|
||||||
|
|
||||||
|
#include "nrf_nvmc.h"
|
||||||
|
#include "sd_mutex.h"
|
||||||
|
|
||||||
#include "bonding.h"
|
#include "bonding.h"
|
||||||
|
|
||||||
// Internal flash area reserved for bonding storage.
|
// Internal flash area reserved for bonding storage.
|
||||||
|
@ -42,97 +45,64 @@
|
||||||
|
|
||||||
// First and last four bytes are magic bytes for id and version. Start data after that.
|
// First and last four bytes are magic bytes for id and version. Start data after that.
|
||||||
// 'BD01'
|
// 'BD01'
|
||||||
const uint32_t BONDING_START_FLAG ('1' | '0' << 8 | 'D' << 16 | 'B' << 24);
|
const uint32_t BONDING_START_FLAG = ('1' | '0' << 8 | 'D' << 16 | 'B' << 24);
|
||||||
// 'ED01'
|
// 'ED01'
|
||||||
const uint32_t BONDING_END_FLAG ('1' | '0' << 8 | 'D' << 16 | 'E' << 24);
|
const uint32_t BONDING_END_FLAG = ('1' | '0' << 8 | 'D' << 16 | 'E' << 24);
|
||||||
|
|
||||||
#define BONDING_DATA_START_ADDR (BONDING_DATA_START_ADDR + sizeof(BONDING_START_BYTES))
|
#define BONDING_DATA_START_ADDR (BONDING_PAGES_START_ADDR + sizeof(BONDING_START_FLAG))
|
||||||
#define BONDING_DATA_END_ADDR (BONDING_PAGES_END_ADDR - sizeof(BONDING_END_BYTES))
|
#define BONDING_DATA_END_ADDR (BONDING_PAGES_END_ADDR - sizeof(BONDING_END_FLAG))
|
||||||
|
|
||||||
#define BONDING_START_FLAG_ADDR BONDING_DATA_START_ADDR
|
#define BONDING_START_FLAG_ADDR BONDING_PAGES_START_ADDR
|
||||||
#define BONDING_END_FLAG_ADDR BONDING_DATA_END_ADDR
|
#define BONDING_END_FLAG_ADDR BONDING_DATA_END_ADDR
|
||||||
|
|
||||||
// Bonding data is stored in variable-length blocks consecutively in erased flash.
|
// Save both system and user service info.
|
||||||
// The blocks are 32-bit aligned, though the data may be any number of bytes.
|
#define SYS_ATTR_FLAGS (BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS | BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS)
|
||||||
// You can hop through the blocks using the size field to find the next block.
|
|
||||||
// When you hit a word that is all one's, you have reached the end of the blocks.
|
|
||||||
// You can write a new block there.
|
|
||||||
|
|
||||||
typdef enum {
|
STATIC bonding_block_t *bonding_unused_block = NULL;
|
||||||
BLOCK_INVALID = 0, // Ignore this block
|
nrf_mutex_t queued_bonding_block_list_mutex;
|
||||||
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 {
|
STATIC inline size_t compute_block_size(uint16_t data_length) {
|
||||||
uint16_t 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.
|
|
||||||
uint32_t data_length; // Length of data in bytes, including ediv, not including padding.
|
|
||||||
// data_length only needs to be 16 bits, but easier to write a word at a time.
|
|
||||||
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;
|
|
||||||
|
|
||||||
STATIC inline size_t bonding_block_size(uint16_t data_length) {
|
|
||||||
// Round data size up to the nearest 32-bit address.
|
// Round data size up to the nearest 32-bit address.
|
||||||
return sizeof(bonding_block_t) + ((data_length + 3) & 0x3);
|
return sizeof(bonding_block_t) + ((data_length + 3) & 0x3);
|
||||||
|
|
||||||
|
|
||||||
void bonding_clear_keys(bonding_keys_t *bonding_keys) {
|
|
||||||
memset(bonding_keys, 0, sizeof(bonding_keys));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC void bonding_erase_storage(void) {
|
STATIC void erase_bonding_storage(void) {
|
||||||
// Erase all pages in the bonding area.
|
// Erase all pages in the bonding area.
|
||||||
|
BONDING_DEBUG_PRINTF("erase_bonding_storage()\n");
|
||||||
for(uint32_t page_address = BONDING_PAGES_START_ADDR;
|
for(uint32_t page_address = BONDING_PAGES_START_ADDR;
|
||||||
page_address < BONDING_PAGES_END_ADDR;
|
page_address < BONDING_PAGES_END_ADDR;
|
||||||
page_address += FLASH_PAGE) {
|
page_address += FLASH_PAGE_SIZE) {
|
||||||
nrf_nvmc_page_erase(page_address);
|
nrf_nvmc_page_erase(page_address);
|
||||||
}
|
}
|
||||||
// Write marker words at the beginning and the end of the bonding area.
|
// Write marker words at the beginning and the end of the bonding area.
|
||||||
nrf_nvmc_write_word(BONDING_DATA_START_ADDR, BONDING_START_FLAG_ADDR);
|
nrf_nvmc_write_word(BONDING_DATA_START_ADDR, BONDING_START_FLAG_ADDR);
|
||||||
nrf_nvmc_write_word(BONDING_DATA_END_ADDR, BONDING_END_FLAG_ADDR);
|
nrf_nvmc_write_word(BONDING_DATA_END_ADDR, BONDING_END_FLAG_ADDR);
|
||||||
// First unused block is at the beginning.
|
// First unused block is at the beginning.
|
||||||
bonding_unused_block = BONDING_DATA_START_ADDR;
|
bonding_unused_block = (bonding_block_t *) BONDING_DATA_START_ADDR;
|
||||||
}
|
|
||||||
|
|
||||||
STATIC bonding_block_t *bonding_unused_block = NULL;
|
|
||||||
|
|
||||||
void bonding_init(void) {
|
|
||||||
if (BONDING_START_BYTES != *((uint32_t *) BONDING_START_FLAG_ADDR) ||
|
|
||||||
BONDING_END_BYTES != *((uint32_t *) BONDING_END_FLAG_ADDR)) {
|
|
||||||
bonding_erase_storage();
|
|
||||||
} else {
|
|
||||||
bonding_unused_block = bonding_find_block(BLOCK_UNUSED, EDIV_INVALID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given NULL to start or block address, return the address of the next valid block.
|
// 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.
|
// The last block returned is the unused block at the end.
|
||||||
// Return NULL if we have run off the end of the bonding space.
|
// Return NULL if we have run off the end of the bonding space.
|
||||||
STATIC bonding_block_t *bonding_next_block(bonding_block_t *block) {
|
STATIC bonding_block_t *next_block(bonding_block_t *block) {
|
||||||
while (1) {
|
while (1) {
|
||||||
// Advance to next block.
|
// Advance to next block.
|
||||||
if (block == NULL) {
|
if (block == NULL) {
|
||||||
// Return first block (which might be unused if block list is empty).
|
// Return first block (which might be unused if block list is empty).
|
||||||
return BONDING_DATA_START_ADDR;
|
return (bonding_block_t *) BONDING_DATA_START_ADDR;
|
||||||
} else if (block->type == BLOCK_UNUSED) {
|
} else if (block->type == BLOCK_UNUSED) {
|
||||||
// Already at last block (the unused block).
|
// Already at last block (the unused block).
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance to next block.
|
// Advance to next block.
|
||||||
block += (bonding_block_t *) ((uint8_t *) block + bonding_block_word_size(block->data_length));
|
block = (bonding_block_t *) ((uint8_t *) block + compute_block_size(block->data_length));
|
||||||
|
|
||||||
}
|
|
||||||
if (block >= (bonding_block_t *) BONDING_DATA_END_ADDR) {
|
if (block >= (bonding_block_t *) BONDING_DATA_END_ADDR) {
|
||||||
// Went past end of bonding space.
|
// Went past end of bonding space.
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (block.valid) {
|
if (block->type != BLOCK_INVALID) {
|
||||||
// Found an empty or a valid block.
|
// Found an empty or a valid block.
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
@ -143,17 +113,19 @@ STATIC bonding_block_t *bonding_next_block(bonding_block_t *block) {
|
||||||
// Find the block with given type and ediv value.
|
// Find the block with given type and ediv value.
|
||||||
// If type == BLOCK_UNUSED, ediv is ignored and the the sole unused block at the end is returned.
|
// If type == BLOCK_UNUSED, ediv is ignored and the the sole unused block at the end is returned.
|
||||||
// If not found, return NULL.
|
// If not found, return NULL.
|
||||||
STATIC bonding_block_t *bonding_find_block(bonding_block_type_t type, uint16_t ediv) {
|
STATIC bonding_block_t *find_block(bool is_central, bonding_block_type_t type, uint16_t ediv) {
|
||||||
bonding_block_t *block = NULL;
|
bonding_block_t *block = NULL;
|
||||||
while (1) {
|
while (1) {
|
||||||
block = bonding_next_block(block);
|
block = next_block(block);
|
||||||
if (block == NULL) {
|
if (block == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (block.type == BLOCK_UNUSED) {
|
if (block->type == BLOCK_UNUSED) {
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
if (type == block.type && ediv = block.ediv) {
|
if (is_central == block->is_central &&
|
||||||
|
type == block->type &&
|
||||||
|
ediv == block->ediv) {
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,80 +133,207 @@ STATIC bonding_block_t *bonding_find_block(bonding_block_type_t type, uint16_t e
|
||||||
|
|
||||||
// Set the header word to all 0's, to mark the block as invalid.
|
// 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.
|
// We don't change data_length, so we can still skip over this block.
|
||||||
STATIC void bonding_invalidate_block(bonding_block_t *block) {
|
STATIC void invalidate_block(bonding_block_t *block) {
|
||||||
nrf_nvmc_write_word((uint32_t) bonding_unused_block, 0x00000000);
|
BONDING_DEBUG_PRINTF("invalidate_block()\n");
|
||||||
|
nrf_nvmc_write_word((uint32_t) block, 0x00000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to write a new block. If no room, erase everything and start again.
|
STATIC void queue_write_block(bool is_central, bonding_block_type_t type, uint16_t ediv, uint16_t conn_handle, uint8_t *data, uint16_t data_length) {
|
||||||
// TODO: could do garbage collection instead.
|
if (compute_block_size(data_length) > BONDING_DATA_END_ADDR - BONDING_DATA_START_ADDR) {
|
||||||
STATIC bool bonding_write_block(bonding_block_type_t type, uint16_t ediv, uint8_t *data, uint16_t data_length) {
|
|
||||||
size_t block_size = bonding_block_word_size(data_length);
|
|
||||||
if (block_size > BONDING_DATA_END_ADDR - BONDING_DATA_START_ADDR) {
|
|
||||||
// Ridiculous size.
|
// Ridiculous size.
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No more room. Erase all existing bonding info and start over.
|
queued_bonding_block_list_elt_t* queued_elt =
|
||||||
if (bonding_unused_block == NULL || bonding_unused_block + block_size >= BONDING_DATA_END_ADDR) {
|
m_malloc_maybe(sizeof(queued_bonding_block_list_elt_t) + data_length, false);
|
||||||
bonding_erase_storage();
|
|
||||||
|
if (!queued_elt) {
|
||||||
|
// Failed to allocate. Not much we can do, since this might be during an evt handler.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bonding_block_t block_without_data;
|
// Add this new element to the front of the list.
|
||||||
block_without_data.valid = 1;
|
sd_mutex_acquire_wait(&queued_bonding_block_list_mutex);
|
||||||
block_without_data.type = type;
|
queued_elt->next_queued_block = MP_STATE_VM(queued_bonding_block_list);
|
||||||
block_without_data.ediv = ediv;
|
MP_STATE_VM(queued_bonding_block_list) = queued_elt;
|
||||||
block_without_data.data_length = data_length;
|
sd_mutex_release(&queued_bonding_block_list_mutex);
|
||||||
|
|
||||||
// Write header data.
|
//
|
||||||
nrf_nvmc_write_words((uint32_t) bonding_unused_block, (uint32_t *) &block_without_data,
|
queued_elt->bonding_block.is_central = is_central;
|
||||||
sizeof(block_without_data) / 4);
|
queued_elt->bonding_block.type = type;
|
||||||
|
queued_elt->bonding_block.ediv = ediv;
|
||||||
|
queued_elt->bonding_block.conn_handle = conn_handle;
|
||||||
|
queued_elt->bonding_block.data_length = data_length;
|
||||||
|
if (data && data_length != 0) {
|
||||||
|
memcpy(&queued_elt->bonding_block.data, data, data_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write variable-length data.
|
// Write bonding block header.
|
||||||
|
STATIC void write_block_header(bonding_block_t *block) {
|
||||||
|
// If no more room, erase all existing blocks and start over.
|
||||||
|
if (bonding_unused_block == NULL ||
|
||||||
|
(uint8_t *) bonding_unused_block + compute_block_size(block->data_length) >=
|
||||||
|
(uint8_t *)BONDING_DATA_END_ADDR) {
|
||||||
|
erase_bonding_storage();
|
||||||
|
}
|
||||||
|
|
||||||
|
nrf_nvmc_write_words((uint32_t) bonding_unused_block, (uint32_t *) block, sizeof(bonding_block_t) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write variable-length data at end of bonding block.
|
||||||
|
STATIC void write_block_data(uint8_t *data, uint16_t data_length) {
|
||||||
// Minimize the number of writes. Datasheet says no more than two writes per word before erasing again.
|
// Minimize the number of writes. Datasheet says no more than two writes per word before erasing again.
|
||||||
uint32_t *word_p = (uint32_t) bonding_unused_block + sizeof(block_without_data);
|
|
||||||
|
// Start writing after the current header.
|
||||||
|
uint32_t *flash_word_p = (uint32_t *) ((uint8_t *) bonding_unused_block + sizeof(bonding_block_t));
|
||||||
while (1) {
|
while (1) {
|
||||||
uint32_t word = 0xffffffff;
|
uint32_t word = 0xffffffff;
|
||||||
memcpy(&word, data, data_length >= 4 ? 4 : data_length);
|
memcpy(&word, data, data_length >= 4 ? 4 : data_length);
|
||||||
nrf_nvmc_write_word(word_p, word);
|
nrf_nvmc_write_word((uint32_t) flash_word_p, word);
|
||||||
if (data_length <= 4) {
|
if (data_length <= 4) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
data_length -= 4;
|
data_length -= 4;
|
||||||
word_p++;
|
// Increment by word size.
|
||||||
|
flash_word_p++;
|
||||||
}
|
}
|
||||||
|
bonding_unused_block = (bonding_block_t *) flash_word_p;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC bool write_sys_attr_block(bonding_block_t *block) {
|
||||||
|
BONDING_DEBUG_PRINTF("write_sys_attr_block()\n");
|
||||||
|
uint16_t length = 0;
|
||||||
|
// First find out how big a buffer we need, then fetch the data.
|
||||||
|
if(sd_ble_gatts_sys_attr_get(block->conn_handle, NULL, &length, SYS_ATTR_FLAGS) != NRF_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t sys_attr[length];
|
||||||
|
if(sd_ble_gatts_sys_attr_get(block->conn_handle, sys_attr, &length, SYS_ATTR_FLAGS) != NRF_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we know the data size.
|
||||||
|
block->data_length = length;
|
||||||
|
write_block_header(block);
|
||||||
|
write_block_data(sys_attr, length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC bool write_keys_block(bonding_block_t *block) {
|
||||||
|
BONDING_DEBUG_PRINTF("write_keys_block()\n");
|
||||||
|
if (block->data_length != sizeof(bonding_keys_t)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bonding_keys_t *bonding_keys = (bonding_keys_t *) block->data;
|
||||||
|
block->ediv = block->is_central
|
||||||
|
? bonding_keys->peer_enc.master_id.ediv
|
||||||
|
: bonding_keys->own_enc.master_id.ediv;
|
||||||
|
|
||||||
|
write_block_header(block);
|
||||||
|
write_block_data((uint8_t *) bonding_keys, sizeof(bonding_keys_t));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void bonding_clear_keys(bonding_keys_t *bonding_keys) {
|
||||||
|
memset((uint8_t*) bonding_keys, 0, sizeof(bonding_keys_t));
|
||||||
|
}
|
||||||
|
|
||||||
bool bonding_load_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv,
|
void bonding_reset(void) {
|
||||||
uint8_t **sys_attr, uint16_t *sys_attr_len) {
|
BONDING_DEBUG_PRINTF("bonding_reset()\n");
|
||||||
bonding_block_t *block = bonding_find_matching_block(BLOCK_SYS_ATTR, ediv);
|
sd_mutex_new(&queued_bonding_block_list_mutex);
|
||||||
if (block) {
|
if (BONDING_START_FLAG != *((uint32_t *) BONDING_START_FLAG_ADDR) ||
|
||||||
*sys_attr = block.data;
|
BONDING_END_FLAG != *((uint32_t *) BONDING_END_FLAG_ADDR)) {
|
||||||
*sys_attr_len = block.data_length;
|
erase_bonding_storage();
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
|
bonding_unused_block = find_block(true, BLOCK_UNUSED, EDIV_INVALID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write bonding blocks to flash. These have been queued during event handlers.
|
||||||
|
// We do one at a time, on each background call.
|
||||||
|
void bonding_background(void) {
|
||||||
|
|
||||||
|
// Get block at front of list.
|
||||||
|
sd_mutex_acquire_wait(&queued_bonding_block_list_mutex);
|
||||||
|
bonding_block_t *block = &(MP_STATE_VM(queued_bonding_block_list)->bonding_block);
|
||||||
|
if (block) {
|
||||||
|
// Remove written block from front of list.
|
||||||
|
MP_STATE_VM(queued_bonding_block_list) = MP_STATE_VM(queued_bonding_block_list)->next_queued_block;
|
||||||
|
}
|
||||||
|
sd_mutex_release(&queued_bonding_block_list_mutex);
|
||||||
|
|
||||||
|
if (!block) {
|
||||||
|
// No blocks in queue.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is there an existing block whose keys match?
|
||||||
|
bonding_block_t *matching_block = find_block(block->is_central, block->type, block->ediv);
|
||||||
|
if (matching_block) {
|
||||||
|
if (block->data_length == matching_block->data_length &&
|
||||||
|
memcmp(block->data, matching_block->data, block->data_length) == 0) {
|
||||||
|
// Identical block found. No need to store again.
|
||||||
|
BONDING_DEBUG_PRINTF("bonding_background(): identical block found\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Block keys match but data doesn't. Invalidate block and store a new one.
|
||||||
|
BONDING_DEBUG_PRINTF("bonding_background(): invalidating block\n");
|
||||||
|
invalidate_block(matching_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (block->type) {
|
||||||
|
case BLOCK_SYS_ATTR:
|
||||||
|
write_sys_attr_block(block);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BLOCK_KEYS:
|
||||||
|
write_keys_block(block);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bonding_load_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv) {
|
||||||
|
bonding_block_t *block = find_block(is_central, BLOCK_SYS_ATTR, ediv);
|
||||||
|
if (block == NULL) {
|
||||||
|
BONDING_DEBUG_PRINTF("bonding_load_cccd_info(): block not found\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BONDING_DEBUG_PRINTF("bonding_load_cccd_info(): block found\n");
|
||||||
|
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) {
|
bool bonding_load_keys(bool is_central, uint16_t ediv, bonding_keys_t *bonding_keys) {
|
||||||
bonding_block_t *block = bonding_find_matching_block(BLOCK_SYS_ATTR, ediv);
|
bonding_block_t *block = find_block(is_central, BLOCK_SYS_ATTR, ediv);
|
||||||
if (block) {
|
if (block == NULL) {
|
||||||
memcpy(bonding_keys, block.data, block.data_length);
|
BONDING_DEBUG_PRINTF("bonding_load_keys(): block not found\n");
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (sizeof(bonding_keys_t) != block->data_length) {
|
||||||
|
// bonding_keys_t is a fixed length, so lengths should match.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BONDING_DEBUG_PRINTF("bonding_load_keys(): block found\n");
|
||||||
|
memcpy(bonding_keys, block->data, block->data_length);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bonding_save_cccd_info_later(bool is_central, uint16_t conn_handle, uint16_t ediv, uint8_t *sys_attr, uint16_t sys_attr_len) {
|
void bonding_save_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv) {
|
||||||
// save in id role/ediv
|
queue_write_block(is_central, ediv, conn_handle, BLOCK_SYS_ATTR, NULL, 0);
|
||||||
// sys_attr
|
}
|
||||||
|
|
||||||
bool bonding_save_keys(bool is_central, uint16_t conn_handle, bonding_keys_t *bonding_keys) {
|
void bonding_save_keys(bool is_central, uint16_t conn_handle, bonding_keys_t *bonding_keys) {
|
||||||
// save in id role/ediv:
|
uint16_t const ediv = is_central
|
||||||
// bonding keys
|
? bonding_keys->peer_enc.master_id.ediv
|
||||||
// peer name, or if no name, then human-readable address
|
: bonding_keys->own_enc.master_id.ediv;
|
||||||
|
queue_write_block(is_central, BLOCK_KEYS, ediv, conn_handle, (uint8_t *) bonding_keys, sizeof(bonding_keys_t));
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,20 +24,64 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_BONDING_H
|
||||||
|
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_BONDING_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "ble.h"
|
#include "ble.h"
|
||||||
#include "ble_drv.h"
|
#include "ble_drv.h"
|
||||||
#include "shared-bindings/_bleio/__init__.h"
|
#include "common-hal/_bleio/__init__.h"
|
||||||
#include "shared-bindings/_bleio/Adapter.h"
|
|
||||||
#include "shared-bindings/nvm/ByteArray.h"
|
|
||||||
|
|
||||||
#define EDIV_INVALID (0xffff)
|
#define EDIV_INVALID (0xffff)
|
||||||
|
|
||||||
|
#define BONDING_DEBUG (1)
|
||||||
|
#if BONDING_DEBUG
|
||||||
|
#define BONDING_DEBUG_PRINTF(...) printf(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define BONDING_DEBUG_PRINTF(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Bonding data is stored in variable-length blocks consecutively in erased flash.
|
||||||
|
// The blocks are 32-bit aligned, though the data may be any number of bytes.
|
||||||
|
// You can hop through the blocks using the size field to find the next block.
|
||||||
|
// When you hit a word that is all one's, you have reached the end of the blocks.
|
||||||
|
// You 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.
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Bonding blocks that need to be written are stored in a linked list.
|
||||||
|
typedef struct _queued_bonding_block_list_elt_t {
|
||||||
|
struct _queued_bonding_block_list_elt_t *next_queued_block;
|
||||||
|
bonding_block_t bonding_block; // variable length, based on data_length.
|
||||||
|
} queued_bonding_block_list_elt_t;
|
||||||
|
|
||||||
|
void bonding_background(void);
|
||||||
|
void bonding_reset(void);
|
||||||
void bonding_clear_keys(bonding_keys_t *bonding_keys);
|
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_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);
|
bool bonding_load_keys(bool is_central, uint16_t ediv, bonding_keys_t *bonding_keys);
|
||||||
bool bonding_save_cccd_info_later(bool is_central, uint16_t conn_handle, uint16_t ediv, uint8_t *sys_attr, uint16_t sys_attr_len);
|
void bonding_save_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv);
|
||||||
bool bonding_save_keys(bool is_central, uint16_t conn_handle, bonding_keys_t *bonding_keys);
|
void bonding_save_keys(bool is_central, uint16_t conn_handle, bonding_keys_t *bonding_keys);
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_BONDING_H
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#define NRF5_MPCONFIGPORT_H__
|
#define NRF5_MPCONFIGPORT_H__
|
||||||
|
|
||||||
#include "ble_drv.h"
|
#include "ble_drv.h"
|
||||||
|
#include "common-hal/_bleio/bonding.h"
|
||||||
|
|
||||||
#include "nrf_mbr.h" // for MBR_SIZE
|
#include "nrf_mbr.h" // for MBR_SIZE
|
||||||
#include "nrf_sdm.h" // for SD_FLASH_SIZE
|
#include "nrf_sdm.h" // for SD_FLASH_SIZE
|
||||||
|
@ -152,10 +153,10 @@
|
||||||
#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; \
|
||||||
|
queued_bonding_block_list_elt_t* queued_bonding_block_list; \
|
||||||
|
|
||||||
|
|
||||||
#endif // NRF5_MPCONFIGPORT_H__
|
#endif // NRF5_MPCONFIGPORT_H__
|
||||||
|
|
Loading…
Reference in New Issue