|
|
|
@ -43,14 +43,12 @@
|
|
|
|
|
#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. Start data after that.
|
|
|
|
|
// First and last four bytes are magic bytes for id and version. Data is in between.
|
|
|
|
|
// 'BD01'
|
|
|
|
|
const uint32_t BONDING_START_FLAG = ('1' | '0' << 8 | 'D' << 16 | 'B' << 24);
|
|
|
|
|
// 'ED01'
|
|
|
|
|
const uint32_t BONDING_END_FLAG = ('1' | '0' << 8 | 'D' << 16 | 'E' << 24);
|
|
|
|
|
const uint32_t BONDING_FLAG = ('1' | '0' << 8 | 'D' << 16 | 'B' << 24);
|
|
|
|
|
|
|
|
|
|
#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_FLAG))
|
|
|
|
|
#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
|
|
|
|
@ -59,14 +57,28 @@ const uint32_t BONDING_END_FLAG = ('1' | '0' << 8 | 'D' << 16 | 'E' << 24);
|
|
|
|
|
#define SYS_ATTR_FLAGS (BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS | BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS)
|
|
|
|
|
|
|
|
|
|
STATIC bonding_block_t *bonding_unused_block = NULL;
|
|
|
|
|
nrf_mutex_t queued_bonding_block_list_mutex;
|
|
|
|
|
nrf_mutex_t queued_bonding_block_entries_mutex;
|
|
|
|
|
|
|
|
|
|
STATIC inline size_t compute_block_size(uint16_t data_length) {
|
|
|
|
|
#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);
|
|
|
|
|
return sizeof(bonding_block_t) + ((data_length + 3) & ~0x3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
STATIC void erase_bonding_storage(void) {
|
|
|
|
|
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;
|
|
|
|
@ -75,10 +87,9 @@ STATIC void erase_bonding_storage(void) {
|
|
|
|
|
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_START_FLAG;
|
|
|
|
|
sd_flash_write_sync((uint32_t *) BONDING_DATA_START_ADDR, &flag, 1);
|
|
|
|
|
flag = BONDING_END_FLAG;
|
|
|
|
|
sd_flash_write_sync((uint32_t *) BONDING_DATA_END_ADDR, &flag, 1);
|
|
|
|
|
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);
|
|
|
|
|
// First unused block is at the beginning.
|
|
|
|
|
bonding_unused_block = (bonding_block_t *) BONDING_DATA_START_ADDR;
|
|
|
|
|
}
|
|
|
|
@ -90,7 +101,6 @@ STATIC bonding_block_t *next_block(bonding_block_t *block) {
|
|
|
|
|
while (1) {
|
|
|
|
|
// Advance to next block.
|
|
|
|
|
if (block == NULL) {
|
|
|
|
|
// Return first block (which might be unused if block list is empty).
|
|
|
|
|
return (bonding_block_t *) BONDING_DATA_START_ADDR;
|
|
|
|
|
} else if (block->type == BLOCK_UNUSED) {
|
|
|
|
|
// Already at last block (the unused block).
|
|
|
|
@ -115,20 +125,26 @@ STATIC bonding_block_t *next_block(bonding_block_t *block) {
|
|
|
|
|
// 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 not found, return NULL.
|
|
|
|
|
STATIC bonding_block_t *find_block(bool is_central, bonding_block_type_t type, uint16_t ediv) {
|
|
|
|
|
STATIC bonding_block_t *find_block_with_keys(bool is_central, bonding_block_type_t type, uint16_t ediv) {
|
|
|
|
|
bonding_block_t *block = NULL;
|
|
|
|
|
BONDING_DEBUG_PRINTF("find_block_with_keys(): looking through blocks:\n");
|
|
|
|
|
while (1) {
|
|
|
|
|
block = next_block(block);
|
|
|
|
|
if (block == NULL) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
if (block->type == BLOCK_UNUSED) {
|
|
|
|
|
return block;
|
|
|
|
|
BONDING_DEBUG_PRINT_BLOCK(block);
|
|
|
|
|
if (block->type == BLOCK_INVALID) {
|
|
|
|
|
// Skip discarded blocks.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (is_central == block->is_central &&
|
|
|
|
|
type == block->type &&
|
|
|
|
|
ediv == block->ediv) {
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -137,6 +153,7 @@ STATIC bonding_block_t *find_block(bool is_central, bonding_block_type_t type, u
|
|
|
|
|
// We don't change data_length, so we can still skip over this block.
|
|
|
|
|
STATIC void invalidate_block(bonding_block_t *block) {
|
|
|
|
|
BONDING_DEBUG_PRINTF("invalidate_block()\n");
|
|
|
|
|
BONDING_DEBUG_PRINT_BLOCK(block);
|
|
|
|
|
uint32_t zero = 0;
|
|
|
|
|
sd_flash_write_sync((uint32_t *) block, &zero, 1);
|
|
|
|
|
}
|
|
|
|
@ -147,29 +164,28 @@ STATIC void queue_write_block(bool is_central, bonding_block_type_t type, uint16
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
queued_bonding_block_list_elt_t* queued_elt =
|
|
|
|
|
m_malloc_maybe(sizeof(queued_bonding_block_list_elt_t) + data_length, false);
|
|
|
|
|
queued_bonding_block_entry_t* queued_entry =
|
|
|
|
|
m_malloc_maybe(sizeof(queued_bonding_block_entry_t) + data_length, false);
|
|
|
|
|
|
|
|
|
|
if (!queued_elt) {
|
|
|
|
|
if (!queued_entry) {
|
|
|
|
|
// Failed to allocate. Not much we can do, since this might be during an evt handler.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add this new element to the front of the list.
|
|
|
|
|
sd_mutex_acquire_wait(&queued_bonding_block_list_mutex);
|
|
|
|
|
queued_elt->next_queued_block = MP_STATE_VM(queued_bonding_block_list);
|
|
|
|
|
MP_STATE_VM(queued_bonding_block_list) = queued_elt;
|
|
|
|
|
sd_mutex_release(&queued_bonding_block_list_mutex);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
queued_elt->bonding_block.is_central = is_central;
|
|
|
|
|
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;
|
|
|
|
|
queued_entry->block.is_central = is_central;
|
|
|
|
|
queued_entry->block.type = type;
|
|
|
|
|
queued_entry->block.ediv = ediv;
|
|
|
|
|
queued_entry->block.conn_handle = conn_handle;
|
|
|
|
|
queued_entry->block.data_length = data_length;
|
|
|
|
|
if (data && data_length != 0) {
|
|
|
|
|
memcpy(&queued_elt->bonding_block.data, data, data_length);
|
|
|
|
|
memcpy(&queued_entry->block.data, data, data_length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add this new element to the front of the list.
|
|
|
|
|
sd_mutex_acquire_wait(&queued_bonding_block_entries_mutex);
|
|
|
|
|
queued_entry->next = MP_STATE_VM(queued_bonding_block_entries);
|
|
|
|
|
MP_STATE_VM(queued_bonding_block_entries) = queued_entry;
|
|
|
|
|
sd_mutex_release(&queued_bonding_block_entries_mutex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write bonding block header.
|
|
|
|
@ -178,7 +194,7 @@ STATIC void write_block_header(bonding_block_t *block) {
|
|
|
|
|
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();
|
|
|
|
|
bonding_erase_storage();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sd_flash_write_sync((uint32_t *) bonding_unused_block, (uint32_t *) block, sizeof(bonding_block_t) / 4);
|
|
|
|
@ -198,6 +214,7 @@ STATIC void write_block_data(uint8_t *data, uint16_t data_length) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
data_length -= 4;
|
|
|
|
|
data += 4;
|
|
|
|
|
// Increment by word size.
|
|
|
|
|
flash_word_p++;
|
|
|
|
|
}
|
|
|
|
@ -231,10 +248,10 @@ STATIC bool write_keys_block(bonding_block_t *block) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bonding_keys_t *bonding_keys = (bonding_keys_t *) block->data;
|
|
|
|
|
BONDING_DEBUG_PRINT_KEYS(bonding_keys);
|
|
|
|
|
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;
|
|
|
|
@ -247,13 +264,13 @@ void bonding_clear_keys(bonding_keys_t *bonding_keys) {
|
|
|
|
|
|
|
|
|
|
// Call only when SD is enabled.
|
|
|
|
|
void bonding_reset(void) {
|
|
|
|
|
MP_STATE_VM(queued_bonding_block_list) = NULL;
|
|
|
|
|
sd_mutex_new(&queued_bonding_block_list_mutex);
|
|
|
|
|
if (BONDING_START_FLAG != *((uint32_t *) BONDING_START_FLAG_ADDR) ||
|
|
|
|
|
BONDING_END_FLAG != *((uint32_t *) BONDING_END_FLAG_ADDR)) {
|
|
|
|
|
erase_bonding_storage();
|
|
|
|
|
MP_STATE_VM(queued_bonding_block_entries) = NULL;
|
|
|
|
|
sd_mutex_new(&queued_bonding_block_entries_mutex);
|
|
|
|
|
if (BONDING_FLAG != *((uint32_t *) BONDING_START_FLAG_ADDR) ||
|
|
|
|
|
BONDING_FLAG != *((uint32_t *) BONDING_END_FLAG_ADDR)) {
|
|
|
|
|
bonding_erase_storage();
|
|
|
|
|
} else {
|
|
|
|
|
bonding_unused_block = find_block(true, BLOCK_UNUSED, EDIV_INVALID);
|
|
|
|
|
bonding_unused_block = find_block_with_keys(true, BLOCK_UNUSED, EDIV_INVALID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -266,29 +283,34 @@ void bonding_background(void) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Get block at front of list.
|
|
|
|
|
sd_mutex_acquire_wait(&queued_bonding_block_list_mutex);
|
|
|
|
|
bonding_block_t *block = (bonding_block_t *) MP_STATE_VM(queued_bonding_block_list);
|
|
|
|
|
if (block) {
|
|
|
|
|
// Remove block from list.
|
|
|
|
|
MP_STATE_VM(queued_bonding_block_list) = MP_STATE_VM(queued_bonding_block_list)->next_queued_block;
|
|
|
|
|
bonding_block_t *block = NULL;
|
|
|
|
|
sd_mutex_acquire_wait(&queued_bonding_block_entries_mutex);
|
|
|
|
|
if (MP_STATE_VM(queued_bonding_block_entries)) {
|
|
|
|
|
block = &(MP_STATE_VM(queued_bonding_block_entries)->block);
|
|
|
|
|
// Remove entry from list.
|
|
|
|
|
MP_STATE_VM(queued_bonding_block_entries) = MP_STATE_VM(queued_bonding_block_entries)->next;
|
|
|
|
|
}
|
|
|
|
|
sd_mutex_release(&queued_bonding_block_list_mutex);
|
|
|
|
|
sd_mutex_release(&queued_bonding_block_entries_mutex);
|
|
|
|
|
if (!block) {
|
|
|
|
|
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) {
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_background(): processing queued block:\n");
|
|
|
|
|
BONDING_DEBUG_PRINT_BLOCK(block);
|
|
|
|
|
bonding_block_t *block_with_keys = find_block_with_keys(block->is_central, block->type, block->ediv);
|
|
|
|
|
if (block_with_keys) {
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_background(): block with same keys found:\n");
|
|
|
|
|
BONDING_DEBUG_PRINT_BLOCK(block_with_keys);
|
|
|
|
|
if (block->data_length == block_with_keys->data_length &&
|
|
|
|
|
memcmp(block->data, block_with_keys->data, block->data_length) == 0) {
|
|
|
|
|
// Identical block found. No need to store again.
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_background(): identical block found\n");
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_background(): block is identical to block_with_keys\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);
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_background(): invalidating block_with_keys\n");
|
|
|
|
|
invalidate_block(block_with_keys);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (block->type) {
|
|
|
|
@ -301,26 +323,27 @@ void bonding_background(void) {
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
BONDING_DEBUG_PRINTF("unknown block type: %x\n", block->type);
|
|
|
|
|
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);
|
|
|
|
|
bonding_block_t *block = find_block_with_keys(is_central, BLOCK_SYS_ATTR, ediv);
|
|
|
|
|
if (block == NULL) {
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_load_cccd_info(): block not found\n");
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_load_cccd_info(): block not found, ediv: %04x\n", ediv);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_load_cccd_info(): block found\n");
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_load_cccd_info(): block found, ediv: %04x\n", ediv);
|
|
|
|
|
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_block(is_central, BLOCK_SYS_ATTR, ediv);
|
|
|
|
|
bonding_block_t *block = find_block_with_keys(is_central, BLOCK_KEYS, ediv);
|
|
|
|
|
if (block == NULL) {
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_load_keys(): block not found\n");
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_load_keys(): block not found, ediv: %04x\n", ediv);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (sizeof(bonding_keys_t) != block->data_length) {
|
|
|
|
@ -328,18 +351,23 @@ bool bonding_load_keys(bool is_central, uint16_t ediv, bonding_keys_t *bonding_k
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_load_keys(): block found\n");
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_load_keys(): block found, ediv: %04x\n", ediv);
|
|
|
|
|
memcpy(bonding_keys, block->data, block->data_length);
|
|
|
|
|
BONDING_DEBUG_PRINT_KEYS(bonding_keys);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void bonding_save_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv) {
|
|
|
|
|
queue_write_block(is_central, ediv, conn_handle, BLOCK_SYS_ATTR, NULL, 0);
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_save_cccd_info: is_central: %d, conn_handle: %04x, ediv: %04x\n",
|
|
|
|
|
is_central, conn_handle, ediv);
|
|
|
|
|
queue_write_block(is_central, BLOCK_SYS_ATTR, ediv, conn_handle, NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void bonding_save_keys(bool is_central, uint16_t conn_handle, bonding_keys_t *bonding_keys) {
|
|
|
|
|
uint16_t const ediv = is_central
|
|
|
|
|
? bonding_keys->peer_enc.master_id.ediv
|
|
|
|
|
: bonding_keys->own_enc.master_id.ediv;
|
|
|
|
|
BONDING_DEBUG_PRINTF("bonding_save_keys: is_central: %d, conn_handle: %04x, ediv: %04x\n",
|
|
|
|
|
is_central, conn_handle, ediv);
|
|
|
|
|
queue_write_block(is_central, BLOCK_KEYS, ediv, conn_handle, (uint8_t *) bonding_keys, sizeof(bonding_keys_t));
|
|
|
|
|
}
|
|
|
|
|