From 242d572470b20a06c5b0be5e07a84aefaa4abbe1 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 3 Jan 2020 10:24:07 -0500 Subject: wip --- ports/atmel-samd/common-hal/nvm/ByteArray.c | 4 +- ports/nrf/common-hal/_bleio/Characteristic.c | 23 +++ ports/nrf/common-hal/_bleio/Connection.c | 53 ++++-- ports/nrf/common-hal/_bleio/Connection.h | 3 +- ports/nrf/common-hal/_bleio/bonding.c | 240 +++++++++++++++++++++++++++ ports/nrf/common-hal/_bleio/bonding.h | 43 +++++ ports/nrf/common-hal/nvm/ByteArray.c | 14 +- ports/nrf/peripherals/nrf/nvm.c | 6 + tools/preprocess_frozen_modules.py | 1 + 9 files changed, 359 insertions(+), 28 deletions(-) create mode 100644 ports/nrf/common-hal/_bleio/bonding.c create mode 100644 ports/nrf/common-hal/_bleio/bonding.h diff --git a/ports/atmel-samd/common-hal/nvm/ByteArray.c b/ports/atmel-samd/common-hal/nvm/ByteArray.c index 12a127f53..ac896a9d5 100644 --- a/ports/atmel-samd/common-hal/nvm/ByteArray.c +++ b/ports/atmel-samd/common-hal/nvm/ByteArray.c @@ -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. struct flash_descriptor desc; 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(); - return true; + return status; } // NVM memory is memory mapped so reading it is easy. diff --git a/ports/nrf/common-hal/_bleio/Characteristic.c b/ports/nrf/common-hal/_bleio/Characteristic.c index 9f1255e32..f80c52ae4 100644 --- a/ports/nrf/common-hal/_bleio/Characteristic.c +++ b/ports/nrf/common-hal/_bleio/Characteristic.c @@ -82,6 +82,27 @@ STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_ } } +STATIC bool characteristic_on_ble_evt(ble_evt_t *ble_evt, void *param) { + bleio_characteristic_obj_t *self = (bleio_characteristic_obj_t *) param; + switch (ble_evt->header.evt_id) { + case BLE_GATTS_EVT_WRITE: { + // A client wrote to this server characteristic. + // If we are bonded, stored the CCCD value. + if (self->service != MP_OBJ_NULL) { + bleio_connection_obj_t *connection = self->service->connection; + uint16_t conn_handle = bleio_connection_get_conn_handle(connection); + if (conn_handle != BLE_CONN_HANDLE_INVALID && + connection->pairing_status == PAIR_PAIRED && + ble_evt->gatts_evt.params.write.handle == self->cccd_handle) { + bonding_save_cccd_later(connection->is_central, conn_handle, connection->ediv); + } + break; + } + } + + return true; +} + void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { self->service = service; self->uuid = uuid; @@ -108,6 +129,8 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, if (initial_value_bufinfo != NULL) { common_hal_bleio_characteristic_set_value(self, initial_value_bufinfo); } + + ble_drv_add_event_handler(characteristic_on_ble_evt, self); } bleio_descriptor_obj_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) { diff --git a/ports/nrf/common-hal/_bleio/Connection.c b/ports/nrf/common-hal/_bleio/Connection.c index e0b50901a..725a9a11a 100644 --- a/ports/nrf/common-hal/_bleio/Connection.c +++ b/ports/nrf/common-hal/_bleio/Connection.c @@ -171,6 +171,15 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { break; } case BLE_GAP_EVT_SEC_PARAMS_REQUEST: { + // First time pairing. + // 1. Either we or peer initiate the process + // 2. Peer asks for security parameters using BLE_GAP_EVT_SEC_PARAMS_REQUEST. + // 3. Pair Key exchange ("just works" implemented now; TODO key pairing) + // 4. Connection is secured: BLE_GAP_EVT_CONN_SEC_UPDATE + // 5. Long-term Keys exchanged: BLE_GAP_EVT_AUTH_STATUS + + bonding_clear_keys(&self->bonding_keys); + self->ediv = EDIV_INVALID; ble_gap_sec_keyset_t keyset = { .keys_own = { .p_enc_key = &self->bonding_keys.own_enc, @@ -188,7 +197,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, - &pairing_sec_params, &keyset); + self->is_central ? NULL : &pairing_sec_params, + &keyset); break; } @@ -202,8 +212,9 @@ 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; self->sec_status = status->auth_status; if (status->auth_status == BLE_GAP_SEC_STATUS_SUCCESS) { - // TODO _ediv = bonding_keys->own_enc.master_id.ediv; + self->ediv = bonding_keys->own_enc.master_id.ediv; self->pair_status = PAIR_PAIRED; + bonding_save_keys(self->is_central, self->conn_handle, &self->bonding_keys); } else { self->pair_status = PAIR_NOT_PAIRED; } @@ -216,14 +227,17 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { // - Else return NULL --> Initiate key exchange ble_gap_evt_sec_info_request_t* sec_info_request = &ble_evt->evt.gap_evt.params.sec_info_request; (void) sec_info_request; - //if ( bond_load_keys(_role, sec_req->master_id.ediv, &bkeys) ) { - //sd_ble_gap_sec_info_reply(_conn_hdl, &bkeys.own_enc.enc_info, &bkeys.peer_id.id_info, NULL); - // - //_ediv = bkeys.own_enc.master_id.ediv; - // } else { + bond_keys bond_keys_t; + if ( bonding_load_keys(self->is_central, sec_info_request->master_id.ediv, &self->bonding_keys) ) { + sd_ble_gap_sec_info_reply(self->conn_handle + &self->bonding_keys.own_enc.enc_info, + &self->bonding_keys.peer_id.id_info, + NULL); + self->ediv = bond_keys.own_enc.master_id.ediv; + } else { sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL); - // } - break; + } + break; } case BLE_GAP_EVT_CONN_SEC_UPDATE: { // 0x1a @@ -235,17 +249,23 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { // mode >=1 and/or level >=1 means encryption is set up self->pair_status = PAIR_NOT_PAIRED; } else { - //if ( !bond_load_cccd(_role, _conn_hdl, _ediv) ) { - if (true) { // TODO: no bonding yet - // Initialize system attributes fresh. - sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); - } + uint8_t *sys_attr; + uint16_t sys_attr_len; + if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv, sys_attr, sys_attr_len)) { + sd_ble_gatts_sys_attr_set(self->conn_handle, sys_attr, sys_attr_len, SVC_CONTEXT_FLAG); // Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS SUCCESS. self->ediv = self->bonding_keys.own_enc.master_id.ediv; + } else { + // No matching bonding found, so use fresh system attributes. + sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); + } } break; } + case BLE_GATTS_EVT_WRITE: { + if (self->pair_status == PAIR_PAIRED) && + default: return false; @@ -258,8 +278,7 @@ void bleio_connection_clear(bleio_connection_internal_t *self) { self->conn_handle = BLE_CONN_HANDLE_INVALID; self->pair_status = PAIR_NOT_PAIRED; - - memset(&self->bonding_keys, 0, sizeof(self->bonding_keys)); + bonding_clear_keys(self); } bool common_hal_bleio_connection_get_paired(bleio_connection_obj_t *self) { @@ -480,7 +499,7 @@ STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, bleio default: // TODO: sd_ble_gattc_descriptors_discover() can return things that are not descriptors, // so ignore those. - // https://devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors + // htts:p//devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors break; } diff --git a/ports/nrf/common-hal/_bleio/Connection.h b/ports/nrf/common-hal/_bleio/Connection.h index 1474a0a6c..daa7f6446 100644 --- a/ports/nrf/common-hal/_bleio/Connection.h +++ b/ports/nrf/common-hal/_bleio/Connection.h @@ -36,6 +36,7 @@ #include "py/objlist.h" #include "common-hal/_bleio/__init__.h" +#include "common-hal/_bleio/bonding.h" #include "shared-module/_bleio/Address.h" #include "common-hal/_bleio/Service.h" @@ -59,7 +60,7 @@ typedef struct { // EDIV: Encrypted Diversifier: Identifies LTK during legacy pairing. bonding_keys_t bonding_keys; uint16_t ediv; - pair_status_t pair_status; + volatile pair_status_t pair_status; uint8_t sec_status; // Internal security status. mp_obj_t connection_obj; ble_drv_evt_handler_entry_t handler_entry; diff --git a/ports/nrf/common-hal/_bleio/bonding.c b/ports/nrf/common-hal/_bleio/bonding.c new file mode 100644 index 000000000..c717d0816 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/bonding.c @@ -0,0 +1,240 @@ +/* + * 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 +#include +#include + +#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 "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. Start data after that. +// '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); + +#define BONDING_DATA_START_ADDR (BONDING_DATA_START_ADDR + sizeof(BONDING_START_BYTES)) +#define BONDING_DATA_END_ADDR (BONDING_PAGES_END_ADDR - sizeof(BONDING_END_BYTES)) + +#define BONDING_START_FLAG_ADDR BONDING_DATA_START_ADDR +#define BONDING_END_FLAG_ADDR BONDING_DATA_END_ADDR + +// 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. + +typdef 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 { + 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. + 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) { + // 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) { + nrf_nvmc_page_erase(page_address); + } + // 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_END_ADDR, BONDING_END_FLAG_ADDR); + // First unused block is at the beginning. + bonding_unused_block = 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. +// 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 *bonding_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_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 + bonding_block_word_size(block->data_length)); + + } + if (block >= (bonding_block_t *) BONDING_DATA_END_ADDR) { + // Went past end of bonding space. + return NULL; + } + if (block.valid) { + // Found an empty or a valid block. + return block; + } + // Invalid block (was erased); try again. + } +} + +// 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 *bonding_find_block(bonding_block_type_t type, uint16_t ediv) { + bonding_block_t *block = NULL; + while (1) { + block = bonding_next_block(block); + if (block == NULL) { + return NULL; + } + if (block.type == BLOCK_UNUSED) { + return block; + } + if (type == block.type && ediv = block.ediv) { + return 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 bonding_invalidate_block(bonding_block_t *block) { + nrf_nvmc_write_word((uint32_t) bonding_unused_block, 0x00000000); +} + +// Try to write a new block. If no room, erase everything and start again. +// TODO: could do garbage collection instead. +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. + return false; + } + + // No more room. Erase all existing bonding info and start over. + if (bonding_unused_block == NULL || bonding_unused_block + block_size >= BONDING_DATA_END_ADDR) { + bonding_erase_storage(); + } + + bonding_block_t block_without_data; + block_without_data.valid = 1; + block_without_data.type = type; + block_without_data.ediv = ediv; + block_without_data.data_length = data_length; + + // Write header data. + nrf_nvmc_write_words((uint32_t) bonding_unused_block, (uint32_t *) &block_without_data, + sizeof(block_without_data) / 4); + + // Write variable-length data. + // 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); + while (1) { + uint32_t word = 0xffffffff; + memcpy(&word, data, data_length >= 4 ? 4 : data_length); + nrf_nvmc_write_word(word_p, word); + if (data_length <= 4) { + break; + } + data_length -= 4; + word_p++; + } + return true; +} + + + +bool bonding_load_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv, + uint8_t **sys_attr, uint16_t *sys_attr_len) { + bonding_block_t *block = bonding_find_matching_block(BLOCK_SYS_ATTR, ediv); + if (block) { + *sys_attr = block.data; + *sys_attr_len = block.data_length; + return true; + } else { + return false; + } +} + +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); + if (block) { + memcpy(bonding_keys, block.data, block.data_length); + return true; + } else { + return false; + } +} + +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) { + // save in id role/ediv + // sys_attr + +bool bonding_save_keys(bool is_central, uint16_t conn_handle, bonding_keys_t *bonding_keys) { + // save in id role/ediv: + // bonding keys + // peer name, or if no name, then human-readable address +} diff --git a/ports/nrf/common-hal/_bleio/bonding.h b/ports/nrf/common-hal/_bleio/bonding.h new file mode 100644 index 000000000..906d9d194 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/bonding.h @@ -0,0 +1,43 @@ +/* + * 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 +#include +#include + +#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" + +#define EDIV_INVALID (0xffff) + +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); +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); +bool bonding_save_keys(bool is_central, uint16_t conn_handle, bonding_keys_t *bonding_keys); diff --git a/ports/nrf/common-hal/nvm/ByteArray.c b/ports/nrf/common-hal/nvm/ByteArray.c index 7d733ce7b..6b2f04a61 100644 --- a/ports/nrf/common-hal/nvm/ByteArray.c +++ b/ports/nrf/common-hal/nvm/ByteArray.c @@ -36,21 +36,17 @@ uint32_t common_hal_nvm_bytearray_get_length(nvm_bytearray_obj_t *self) { 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 // it since we can only clear a whole page at a time. - bool status; 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 { uint8_t buffer[FLASH_PAGE_SIZE]; memcpy(buffer, (uint8_t *)page_addr, FLASH_PAGE_SIZE); memcpy(buffer + offset, bytes, len); - status = nrf_nvm_safe_flash_page_write(page_addr, buffer); - } - if (!status) { - mp_raise_OSError_msg(translate("Flash write failed")); + return nrf_nvm_safe_flash_page_write(page_addr, buffer); } } @@ -63,7 +59,9 @@ bool common_hal_nvm_bytearray_set_bytes(nvm_bytearray_obj_t *self, while (len) { 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; values += write_len; page_addr += FLASH_PAGE_SIZE; diff --git a/ports/nrf/peripherals/nrf/nvm.c b/ports/nrf/peripherals/nrf/nvm.c index 7118b9c87..d13775d4d 100644 --- a/ports/nrf/peripherals/nrf/nvm.c +++ b/ports/nrf/peripherals/nrf/nvm.c @@ -50,6 +50,12 @@ STATIC sd_flash_operation_status_t sd_flash_operation_wait_until_done(void) { } #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) { #ifdef BLUETOOTH_SD uint8_t sd_en = 0; diff --git a/tools/preprocess_frozen_modules.py b/tools/preprocess_frozen_modules.py index 2199e9d39..90ea49ca4 100755 --- a/tools/preprocess_frozen_modules.py +++ b/tools/preprocess_frozen_modules.py @@ -47,6 +47,7 @@ def copy_and_process(in_dir, out_dir): output_file_path = Path(out_dir, input_file_path.relative_to(in_dir)) if file.endswith(".py"): + print(file) if not output_file_path.parent.exists(): output_file_path.parent.mkdir(parents=True) with input_file_path.open("r") as input, output_file_path.open("w") as output: -- cgit v1.2.3 From 390337b9a518eaa4416458bce9f4a39b60b23372 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 5 Jan 2020 23:33:42 -0500 Subject: wip; compiles --- ports/nrf/Makefile | 3 +- ports/nrf/background.c | 2 + ports/nrf/common-hal/_bleio/Adapter.c | 2 + ports/nrf/common-hal/_bleio/Characteristic.c | 9 +- ports/nrf/common-hal/_bleio/Connection.c | 25 +-- ports/nrf/common-hal/_bleio/__init__.h | 6 +- ports/nrf/common-hal/_bleio/bonding.c | 297 ++++++++++++++++++--------- ports/nrf/common-hal/_bleio/bonding.h | 54 ++++- ports/nrf/mpconfigport.h | 5 +- 9 files changed, 274 insertions(+), 129 deletions(-) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index e6070fc32..92985e4e9 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -73,8 +73,6 @@ INC += -I$(BUILD) INC += -I$(BUILD)/genhdr INC += -I./../../lib/cmsis/inc INC += -I./boards/$(BOARD) -INC += -I./modules/ubluepy -INC += -I./modules/ble INC += -I./nrfx INC += -I./nrfx/hal INC += -I./nrfx/mdk @@ -156,6 +154,7 @@ SRC_C += \ boards/$(BOARD)/pins.c \ device/$(MCU_VARIANT)/startup_$(MCU_SUB_VARIANT).c \ bluetooth/ble_drv.c \ + common-hal/_bleio/bonding.c \ lib/libc/string0.c \ lib/mp-readline/readline.c \ lib/oofatfs/ff.c \ diff --git a/ports/nrf/background.c b/ports/nrf/background.c index 629967b3d..966c56e0b 100644 --- a/ports/nrf/background.c +++ b/ports/nrf/background.c @@ -43,6 +43,7 @@ #if CIRCUITPY_BLEIO #include "supervisor/shared/bluetooth.h" +#include "common-hal/_bleio/bonding.h" #endif static bool running_background_tasks = false; @@ -68,6 +69,7 @@ void run_background_tasks(void) { #if CIRCUITPY_BLEIO supervisor_bluetooth_background(); + bonding_background(); #endif #if CIRCUITPY_DISPLAYIO diff --git a/ports/nrf/common-hal/_bleio/Adapter.c b/ports/nrf/common-hal/_bleio/Adapter.c index cee40a5a0..12d509073 100644 --- a/ports/nrf/common-hal/_bleio/Adapter.c +++ b/ports/nrf/common-hal/_bleio/Adapter.c @@ -32,6 +32,7 @@ #include "ble.h" #include "ble_drv.h" +#include "bonding.h" #include "nrfx_power.h" #include "nrf_nvic.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]; connection->connection_obj = mp_const_none; } + bonding_reset(); } diff --git a/ports/nrf/common-hal/_bleio/Characteristic.c b/ports/nrf/common-hal/_bleio/Characteristic.c index f80c52ae4..81639898f 100644 --- a/ports/nrf/common-hal/_bleio/Characteristic.c +++ b/ports/nrf/common-hal/_bleio/Characteristic.c @@ -33,6 +33,7 @@ #include "shared-bindings/_bleio/Service.h" #include "common-hal/_bleio/Adapter.h" +#include "common-hal/_bleio/bonding.h" STATIC uint16_t characteristic_get_cccd(uint16_t cccd_handle, uint16_t conn_handle) { uint16_t cccd; @@ -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; uint16_t conn_handle = bleio_connection_get_conn_handle(connection); if (conn_handle != BLE_CONN_HANDLE_INVALID && - connection->pairing_status == PAIR_PAIRED && - ble_evt->gatts_evt.params.write.handle == self->cccd_handle) { - bonding_save_cccd_later(connection->is_central, conn_handle, connection->ediv); + common_hal_bleio_connection_get_paired(connection) && + ble_evt->evt.gatts_evt.params.write.handle == self->cccd_handle) { + bonding_save_cccd_info( + connection->connection->is_central, conn_handle, connection->connection->ediv); + } } break; } diff --git a/ports/nrf/common-hal/_bleio/Connection.c b/ports/nrf/common-hal/_bleio/Connection.c index 725a9a11a..6c49823c2 100644 --- a/ports/nrf/common-hal/_bleio/Connection.c +++ b/ports/nrf/common-hal/_bleio/Connection.c @@ -47,6 +47,8 @@ #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/UUID.h" +#include "common-hal/_bleio/bonding.h" + #define BLE_ADV_LENGTH_FIELD_SIZE 1 #define BLE_ADV_AD_TYPE_FIELD_SIZE 1 #define BLE_AD_TYPE_FLAGS_DATA_SIZE 1 @@ -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; self->sec_status = status->auth_status; 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; bonding_save_keys(self->is_central, self->conn_handle, &self->bonding_keys); } else { @@ -227,13 +229,12 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { // - Else return NULL --> Initiate key exchange ble_gap_evt_sec_info_request_t* sec_info_request = &ble_evt->evt.gap_evt.params.sec_info_request; (void) sec_info_request; - bond_keys bond_keys_t; 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.peer_id.id_info, NULL); - self->ediv = bond_keys.own_enc.master_id.ediv; + self->ediv = self->bonding_keys.own_enc.master_id.ediv; } else { 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 self->pair_status = PAIR_NOT_PAIRED; } else { - uint8_t *sys_attr; - uint16_t sys_attr_len; - if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv, sys_attr, sys_attr_len)) { - sd_ble_gatts_sys_attr_set(self->conn_handle, sys_attr, sys_attr_len, SVC_CONTEXT_FLAG); - // Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS SUCCESS. - self->ediv = self->bonding_keys.own_enc.master_id.ediv; + // Does an sd_ble_gatts_sys_attr_set() with the stored values. + if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv)) { + // Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS SUCCESS. + self->ediv = self->bonding_keys.own_enc.master_id.ediv; } else { // No matching bonding found, so use fresh system attributes. 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; } - case BLE_GATTS_EVT_WRITE: { - if (self->pair_status == PAIR_PAIRED) && - - default: return false; } @@ -278,7 +273,7 @@ void bleio_connection_clear(bleio_connection_internal_t *self) { self->conn_handle = BLE_CONN_HANDLE_INVALID; 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) { diff --git a/ports/nrf/common-hal/_bleio/__init__.h b/ports/nrf/common-hal/_bleio/__init__.h index ecacf7c85..e216795fc 100644 --- a/ports/nrf/common-hal/_bleio/__init__.h +++ b/ports/nrf/common-hal/_bleio/__init__.h @@ -30,9 +30,9 @@ void bleio_reset(void); typedef struct { - ble_gap_enc_key_t own_enc; - ble_gap_enc_key_t peer_enc; - ble_gap_id_key_t peer_id; + ble_gap_enc_key_t own_enc; + ble_gap_enc_key_t peer_enc; + ble_gap_id_key_t peer_id; } bonding_keys_t; // We assume variable length data. diff --git a/ports/nrf/common-hal/_bleio/bonding.c b/ports/nrf/common-hal/_bleio/bonding.c index c717d0816..1675ade43 100644 --- a/ports/nrf/common-hal/_bleio/bonding.c +++ b/ports/nrf/common-hal/_bleio/bonding.c @@ -34,6 +34,9 @@ #include "shared-bindings/_bleio/Adapter.h" #include "shared-bindings/nvm/ByteArray.h" +#include "nrf_nvmc.h" +#include "sd_mutex.h" + #include "bonding.h" // 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. // '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' -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_END_ADDR (BONDING_PAGES_END_ADDR - sizeof(BONDING_END_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_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 -// 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. - -typdef 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 { - 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. - return sizeof(bonding_block_t) + ((data_length + 3) & 0x3); +// 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) +STATIC bonding_block_t *bonding_unused_block = NULL; +nrf_mutex_t queued_bonding_block_list_mutex; -void bonding_clear_keys(bonding_keys_t *bonding_keys) { - memset(bonding_keys, 0, sizeof(bonding_keys)); +STATIC inline 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); } -STATIC void bonding_erase_storage(void) { +STATIC void erase_bonding_storage(void) { // Erase all pages in the bonding area. + BONDING_DEBUG_PRINTF("erase_bonding_storage()\n"); for(uint32_t page_address = BONDING_PAGES_START_ADDR; page_address < BONDING_PAGES_END_ADDR; - page_address += FLASH_PAGE) { + page_address += FLASH_PAGE_SIZE) { nrf_nvmc_page_erase(page_address); } // 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_END_ADDR, BONDING_END_FLAG_ADDR); // First unused block is at the beginning. - bonding_unused_block = 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); - } + bonding_unused_block = (bonding_block_t *) BONDING_DATA_START_ADDR; } // 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 *bonding_next_block(bonding_block_t *block) { +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_DATA_START_ADDR; + 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 + 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) { // Went past end of bonding space. return NULL; } - if (block.valid) { + if (block->type != BLOCK_INVALID) { // Found an empty or a valid 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. // 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 *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; while (1) { - block = bonding_next_block(block); + block = next_block(block); if (block == NULL) { return NULL; } - if (block.type == BLOCK_UNUSED) { + if (block->type == BLOCK_UNUSED) { return block; } - if (type == block.type && ediv = block.ediv) { + if (is_central == block->is_central && + type == block->type && + ediv == block->ediv) { 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. // We don't change data_length, so we can still skip over this block. -STATIC void bonding_invalidate_block(bonding_block_t *block) { - nrf_nvmc_write_word((uint32_t) bonding_unused_block, 0x00000000); +STATIC void invalidate_block(bonding_block_t *block) { + 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. -// TODO: could do garbage collection instead. -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) { +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) { + if (compute_block_size(data_length) > BONDING_DATA_END_ADDR - BONDING_DATA_START_ADDR) { // Ridiculous size. - return false; + return; + } + + queued_bonding_block_list_elt_t* queued_elt = + m_malloc_maybe(sizeof(queued_bonding_block_list_elt_t) + data_length, false); + + if (!queued_elt) { + // Failed to allocate. Not much we can do, since this might be during an evt handler. + return; } - // No more room. Erase all existing bonding info and start over. - if (bonding_unused_block == NULL || bonding_unused_block + block_size >= BONDING_DATA_END_ADDR) { - bonding_erase_storage(); + // 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; + if (data && data_length != 0) { + memcpy(&queued_elt->bonding_block.data, data, data_length); } +} - bonding_block_t block_without_data; - block_without_data.valid = 1; - block_without_data.type = type; - block_without_data.ediv = ediv; - block_without_data.data_length = data_length; +// 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(); + } - // Write header data. - nrf_nvmc_write_words((uint32_t) bonding_unused_block, (uint32_t *) &block_without_data, - sizeof(block_without_data) / 4); + nrf_nvmc_write_words((uint32_t) bonding_unused_block, (uint32_t *) block, sizeof(bonding_block_t) / 4); +} - // Write variable-length data. +// 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. - 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) { uint32_t word = 0xffffffff; 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) { break; } 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; -bool bonding_load_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv, - uint8_t **sys_attr, uint16_t *sys_attr_len) { - bonding_block_t *block = bonding_find_matching_block(BLOCK_SYS_ATTR, ediv); - if (block) { - *sys_attr = block.data; - *sys_attr_len = block.data_length; - return true; + write_block_header(block); + write_block_data((uint8_t *) bonding_keys, sizeof(bonding_keys_t)); + return true; +} + + +void bonding_clear_keys(bonding_keys_t *bonding_keys) { + memset((uint8_t*) bonding_keys, 0, sizeof(bonding_keys_t)); +} + +void bonding_reset(void) { + BONDING_DEBUG_PRINTF("bonding_reset()\n"); + 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(); } 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; } + + 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) { - bonding_block_t *block = bonding_find_matching_block(BLOCK_SYS_ATTR, ediv); - if (block) { - memcpy(bonding_keys, block.data, block.data_length); - return true; - } else { + bonding_block_t *block = find_block(is_central, BLOCK_SYS_ATTR, ediv); + if (block == NULL) { + BONDING_DEBUG_PRINTF("bonding_load_keys(): block not found\n"); + 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) { - // save in id role/ediv - // sys_attr +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); +} -bool bonding_save_keys(bool is_central, uint16_t conn_handle, bonding_keys_t *bonding_keys) { - // save in id role/ediv: - // bonding keys - // peer name, or if no name, then human-readable address +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; + queue_write_block(is_central, BLOCK_KEYS, ediv, conn_handle, (uint8_t *) bonding_keys, sizeof(bonding_keys_t)); } diff --git a/ports/nrf/common-hal/_bleio/bonding.h b/ports/nrf/common-hal/_bleio/bonding.h index 906d9d194..1b0083865 100644 --- a/ports/nrf/common-hal/_bleio/bonding.h +++ b/ports/nrf/common-hal/_bleio/bonding.h @@ -24,20 +24,64 @@ * THE SOFTWARE. */ +#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_BONDING_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_BONDING_H + #include #include #include #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 "common-hal/_bleio/__init__.h" #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); 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_save_cccd_info_later(bool is_central, uint16_t conn_handle, uint16_t ediv, uint8_t *sys_attr, uint16_t sys_attr_len); -bool bonding_save_keys(bool is_central, uint16_t conn_handle, bonding_keys_t *bonding_keys); +void bonding_save_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv); +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 diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index cb25870dc..14f7632fd 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -29,6 +29,7 @@ #define NRF5_MPCONFIGPORT_H__ #include "ble_drv.h" +#include "common-hal/_bleio/bonding.h" #include "nrf_mbr.h" // for MBR_SIZE #include "nrf_sdm.h" // for SD_FLASH_SIZE @@ -152,10 +153,10 @@ #endif - - #define MICROPY_PORT_ROOT_POINTERS \ CIRCUITPY_COMMON_ROOT_POINTERS \ 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__ -- cgit v1.2.3 From 9c167af17a4dd8670a457ab25bda4bba1d9eaa6e Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 9 Jan 2020 17:40:02 -0500 Subject: wip; redid flash writing to be compatible with SD --- ports/nrf/common-hal/_bleio/Adapter.c | 1 - ports/nrf/common-hal/_bleio/Characteristic.c | 8 ++++- ports/nrf/common-hal/_bleio/Characteristic.h | 1 + ports/nrf/common-hal/_bleio/__init__.c | 1 + ports/nrf/common-hal/_bleio/bonding.c | 34 ++++++++++-------- ports/nrf/peripherals/nrf/nvm.c | 45 +++++++++++++++++++---- ports/nrf/peripherals/nrf/nvm.h | 5 +++ ports/nrf/sd.c | 54 ++++++++++++++++++++++++++++ ports/nrf/sd.h | 46 ++++++++++++++++++++++++ shared-bindings/_bleio/Characteristic.c | 12 ++++++- shared-bindings/_bleio/Characteristic.h | 1 + 11 files changed, 185 insertions(+), 23 deletions(-) create mode 100644 ports/nrf/sd.c create mode 100644 ports/nrf/sd.h diff --git a/ports/nrf/common-hal/_bleio/Adapter.c b/ports/nrf/common-hal/_bleio/Adapter.c index 12d509073..05cd0260b 100644 --- a/ports/nrf/common-hal/_bleio/Adapter.c +++ b/ports/nrf/common-hal/_bleio/Adapter.c @@ -670,5 +670,4 @@ void bleio_adapter_reset(bleio_adapter_obj_t* adapter) { bleio_connection_internal_t *connection = &connections[i]; connection->connection_obj = mp_const_none; } - bonding_reset(); } diff --git a/ports/nrf/common-hal/_bleio/Characteristic.c b/ports/nrf/common-hal/_bleio/Characteristic.c index 81639898f..a87cd6e18 100644 --- a/ports/nrf/common-hal/_bleio/Characteristic.c +++ b/ports/nrf/common-hal/_bleio/Characteristic.c @@ -133,7 +133,8 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, common_hal_bleio_characteristic_set_value(self, initial_value_bufinfo); } - ble_drv_add_event_handler(characteristic_on_ble_evt, self); + self->handler_entry.next = NULL; +//////////////// ble_drv_add_event_handler_entry(&self->handler_entry, characteristic_on_ble_evt, self); } bleio_descriptor_obj_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) { @@ -287,3 +288,8 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, } } + +void common_hal_bleio_characteristic_del(bleio_characteristic_obj_t *self) { + // Remove from event handler list, since the evt handler entry is built-in and not a heap object. + ble_drv_remove_event_handler(characteristic_on_ble_evt, self); +} diff --git a/ports/nrf/common-hal/_bleio/Characteristic.h b/ports/nrf/common-hal/_bleio/Characteristic.h index bb8f28495..5759321aa 100644 --- a/ports/nrf/common-hal/_bleio/Characteristic.h +++ b/ports/nrf/common-hal/_bleio/Characteristic.h @@ -47,6 +47,7 @@ typedef struct _bleio_characteristic_obj { bleio_attribute_security_mode_t read_perm; bleio_attribute_security_mode_t write_perm; bleio_descriptor_obj_t *descriptor_list; + ble_drv_evt_handler_entry_t handler_entry; uint16_t user_desc_handle; uint16_t cccd_handle; uint16_t sccd_handle; diff --git a/ports/nrf/common-hal/_bleio/__init__.c b/ports/nrf/common-hal/_bleio/__init__.c index 4b2780ded..b9fbaa7c3 100644 --- a/ports/nrf/common-hal/_bleio/__init__.c +++ b/ports/nrf/common-hal/_bleio/__init__.c @@ -94,6 +94,7 @@ void bleio_reset() { common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false); } supervisor_start_bluetooth(); + bonding_reset(); } // The singleton _bleio.Adapter object, bound to _bleio.adapter diff --git a/ports/nrf/common-hal/_bleio/bonding.c b/ports/nrf/common-hal/_bleio/bonding.c index 1675ade43..1175f72e4 100644 --- a/ports/nrf/common-hal/_bleio/bonding.c +++ b/ports/nrf/common-hal/_bleio/bonding.c @@ -34,7 +34,7 @@ #include "shared-bindings/_bleio/Adapter.h" #include "shared-bindings/nvm/ByteArray.h" -#include "nrf_nvmc.h" +#include "nrf_soc.h" #include "sd_mutex.h" #include "bonding.h" @@ -68,15 +68,17 @@ STATIC inline size_t compute_block_size(uint16_t data_length) { STATIC void erase_bonding_storage(void) { // Erase all pages in the bonding area. - BONDING_DEBUG_PRINTF("erase_bonding_storage()\n"); for(uint32_t page_address = BONDING_PAGES_START_ADDR; page_address < BONDING_PAGES_END_ADDR; page_address += FLASH_PAGE_SIZE) { - nrf_nvmc_page_erase(page_address); + // 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. - nrf_nvmc_write_word(BONDING_DATA_START_ADDR, BONDING_START_FLAG_ADDR); - nrf_nvmc_write_word(BONDING_DATA_END_ADDR, BONDING_END_FLAG_ADDR); + 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); // First unused block is at the beginning. bonding_unused_block = (bonding_block_t *) BONDING_DATA_START_ADDR; } @@ -135,7 +137,8 @@ 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"); - nrf_nvmc_write_word((uint32_t) block, 0x00000000); + uint32_t zero = 0; + sd_flash_write_sync((uint32_t *) block, &zero, 1); } 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) { @@ -178,7 +181,7 @@ STATIC void write_block_header(bonding_block_t *block) { erase_bonding_storage(); } - nrf_nvmc_write_words((uint32_t) bonding_unused_block, (uint32_t *) block, sizeof(bonding_block_t) / 4); + sd_flash_write_sync((uint32_t *) bonding_unused_block, (uint32_t *) block, sizeof(bonding_block_t) / 4); } // Write variable-length data at end of bonding block. @@ -190,7 +193,7 @@ STATIC void write_block_data(uint8_t *data, uint16_t data_length) { while (1) { uint32_t word = 0xffffffff; memcpy(&word, data, data_length >= 4 ? 4 : data_length); - nrf_nvmc_write_word((uint32_t) flash_word_p, word); + sd_flash_write_sync(flash_word_p, &word, 1); if (data_length <= 4) { break; } @@ -242,8 +245,9 @@ void bonding_clear_keys(bonding_keys_t *bonding_keys) { memset((uint8_t*) bonding_keys, 0, sizeof(bonding_keys_t)); } +// Call only when SD is enabled. void bonding_reset(void) { - BONDING_DEBUG_PRINTF("bonding_reset()\n"); + 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)) { @@ -256,18 +260,20 @@ void bonding_reset(void) { // 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) { - + uint8_t sd_en = 0; + (void) sd_softdevice_is_enabled(&sd_en); + if (!sd_en) { + return; + } // 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); + bonding_block_t *block = (bonding_block_t *) MP_STATE_VM(queued_bonding_block_list); if (block) { - // Remove written block from front of list. + // Remove block from 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; } diff --git a/ports/nrf/peripherals/nrf/nvm.c b/ports/nrf/peripherals/nrf/nvm.c index d13775d4d..2417ec66a 100644 --- a/ports/nrf/peripherals/nrf/nvm.c +++ b/ports/nrf/peripherals/nrf/nvm.c @@ -38,29 +38,62 @@ #include "ble_drv.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) { sd_flash_operation_status = SD_FLASH_OPERATION_IN_PROGRESS; } STATIC sd_flash_operation_status_t sd_flash_operation_wait_until_done(void) { - while (sd_flash_operation_status == SD_FLASH_OPERATION_IN_PROGRESS) { - sd_app_evt_wait(); + // 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) { + sd_app_evt_wait(); + } + } 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 // 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. +// writes to a page.g bool nrf_nvm_safe_flash_page_write(uint32_t page_addr, uint8_t *data) { #ifdef BLUETOOTH_SD - uint8_t sd_en = 0; - (void) sd_softdevice_is_enabled(&sd_en); - if (sd_en) { + if (sd_is_enabled()) { uint32_t err_code; sd_flash_operation_status_t status; diff --git a/ports/nrf/peripherals/nrf/nvm.h b/ports/nrf/peripherals/nrf/nvm.h index e54e953df..8ba95773d 100644 --- a/ports/nrf/peripherals/nrf/nvm.h +++ b/ports/nrf/peripherals/nrf/nvm.h @@ -27,4 +27,9 @@ #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); diff --git a/ports/nrf/sd.c b/ports/nrf/sd.c new file mode 100644 index 000000000..b3162e6af --- /dev/null +++ b/ports/nrf/sd.c @@ -0,0 +1,54 @@ +/* + * 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 "py/mpconfig.h" +#include "py/runtime.h" +#include "nrf_soc.h" + +void sd_mutex_acquire_check(nrf_mutex_t* p_mutex) { + uint32_t err_code = sd_mutex_acquire(p_mutex); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to acquire mutex, err 0x%04x"), err_code); + } +} + +void sd_mutex_acquire_wait(nrf_mutex_t* p_mutex) { + while (sd_mutex_acquire(p_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) { + RUN_BACKGROUND_TASKS; + } +} + +void sd_mutex_acquire_wait_no_vm(nrf_mutex_t* p_mutex) { + while (sd_mutex_acquire(p_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) { + } +} + +void sd_mutex_release_check(nrf_mutex_t* p_mutex) { + uint32_t err_code = sd_mutex_release(p_mutex); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to release mutex, err 0x%04x"), err_code); + } +} diff --git a/ports/nrf/sd.h b/ports/nrf/sd.h new file mode 100644 index 000000000..ca4691720 --- /dev/null +++ b/ports/nrf/sd.h @@ -0,0 +1,46 @@ +/* + * 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_SD_MUTEX_H +#define MICROPY_INCLUDED_NRF_SD_MUTEX_H + +#include "nrf_soc.h" + +// Helpers for common usage of nrf_mutex. + +// Try to acquire a mutex right now. Raise exception if we can't get it. +void sd_mutex_acquire_check(nrf_mutex_t* p_mutex); + +// Wait for a mutex to become available. Run VM background tasks while waiting. +void sd_mutex_acquire_wait(nrf_mutex_t* p_mutex); + +// Wait for a mutex to become available.. Block VM while waiting. +void sd_mutex_acquire_wait_no_vm(nrf_mutex_t* p_mutex); + +// Release a mutex, and raise exception on error. +void sd_mutex_release_check(nrf_mutex_t* p_mutex); + +#endif // MICROPY_INCLUDED_NRF_SD_MUTEX_H diff --git a/shared-bindings/_bleio/Characteristic.c b/shared-bindings/_bleio/Characteristic.c index 7fcf274da..0434368f2 100644 --- a/shared-bindings/_bleio/Characteristic.c +++ b/shared-bindings/_bleio/Characteristic.c @@ -128,7 +128,8 @@ STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ } mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); + // There may be some cleanup needed when a characteristic is gc'd, so enable finaliser. + bleio_characteristic_obj_t *characteristic = m_new_obj_with_finaliser(bleio_characteristic_obj_t); characteristic->base.type = &bleio_characteristic_type; // Range checking on max_length arg is done by the common_hal layer, because @@ -292,8 +293,17 @@ 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); +// Cleanup on gc. +STATIC mp_obj_t bleio_characteristic_del(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_bleio_characteristic_del(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_del_obj, bleio_characteristic_del); + STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&bleio_characteristic_del_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_uuid), MP_ROM_PTR(&bleio_characteristic_uuid_obj) }, diff --git a/shared-bindings/_bleio/Characteristic.h b/shared-bindings/_bleio/Characteristic.h index c4356fd4b..60ab57572 100644 --- a/shared-bindings/_bleio/Characteristic.h +++ b/shared-bindings/_bleio/Characteristic.h @@ -45,5 +45,6 @@ extern bleio_descriptor_obj_t *common_hal_bleio_characteristic_get_descriptor_li extern bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self); extern void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor); extern void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate); +extern void common_hal_bleio_characteristic_del(bleio_characteristic_obj_t *self); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H -- cgit v1.2.3 From 346ce3b73b532d2776e660e0e92c5d79c699880e Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 10 Jan 2020 23:55:45 -0500 Subject: wip: HID bonding works! --- ports/nrf/common-hal/_bleio/Adapter.c | 5 + ports/nrf/common-hal/_bleio/Connection.c | 44 +++++++-- ports/nrf/common-hal/_bleio/bonding.c | 158 ++++++++++++++++++------------- ports/nrf/common-hal/_bleio/bonding.h | 13 ++- ports/nrf/mpconfigport.h | 2 +- shared-bindings/_bleio/Adapter.c | 25 +++-- shared-bindings/_bleio/Adapter.h | 14 +-- 7 files changed, 170 insertions(+), 91 deletions(-) diff --git a/ports/nrf/common-hal/_bleio/Adapter.c b/ports/nrf/common-hal/_bleio/Adapter.c index 05cd0260b..8c732e73b 100644 --- a/ports/nrf/common-hal/_bleio/Adapter.c +++ b/ports/nrf/common-hal/_bleio/Adapter.c @@ -224,6 +224,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); connection->conn_handle = BLE_CONN_HANDLE_INVALID; + connection->pair_status = PAIR_NOT_PAIRED; if (connection->connection_obj != mp_const_none) { bleio_connection_obj_t* obj = connection->connection_obj; obj->connection = NULL; @@ -657,6 +658,10 @@ mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self) { return self->connection_objs; } +void common_hal_bleio_adapter_erase_bonding(bleio_adapter_obj_t *self) { + bonding_erase_storage(); +} + void bleio_adapter_gc_collect(bleio_adapter_obj_t* adapter) { gc_collect_root((void**)adapter, sizeof(bleio_adapter_obj_t) / sizeof(size_t)); gc_collect_root((void**)connections, sizeof(connections) / sizeof(size_t)); diff --git a/ports/nrf/common-hal/_bleio/Connection.c b/ports/nrf/common-hal/_bleio/Connection.c index 6c49823c2..664805b66 100644 --- a/ports/nrf/common-hal/_bleio/Connection.c +++ b/ports/nrf/common-hal/_bleio/Connection.c @@ -54,7 +54,7 @@ #define BLE_AD_TYPE_FLAGS_DATA_SIZE 1 static const ble_gap_sec_params_t pairing_sec_params = { - .bond = 0, + .bond = 1, .mitm = 0, .lesc = 0, .keypress = 0, @@ -66,6 +66,13 @@ static const ble_gap_sec_params_t pairing_sec_params = { .kdist_peer = { .enc = 1, .id = 1}, }; +#define CONNECTION_DEBUG (1) +#if CONNECTION_DEBUG + #define CONNECTION_DEBUG_PRINTF(...) printf(__VA_ARGS__) +#else + #define CONNECTION_DEBUG_PRINTF(...) +#endif + static volatile bool m_discovery_in_process; static volatile bool m_discovery_successful; @@ -86,7 +93,10 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { switch (ble_evt->header.evt_id) { case BLE_GAP_EVT_DISCONNECTED: + CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_DISCONNECTED\n"); + // Adapter.c does the work for this event. break; + case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { ble_gap_phys_t const phys = { .rx_phys = BLE_GAP_PHY_AUTO, @@ -173,10 +183,11 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { break; } case BLE_GAP_EVT_SEC_PARAMS_REQUEST: { + CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_SEC_PARAMS_REQUEST\n"); // 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 key pairing) + // 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 @@ -205,43 +216,55 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { } case BLE_GAP_EVT_LESC_DHKEY_REQUEST: + CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_LESC_DHKEY_REQUEST\n"); // TODO for LESC pairing: // sd_ble_gap_lesc_dhkey_reply(...); break; case BLE_GAP_EVT_AUTH_STATUS: { // 0x19 + CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_AUTH_STATUS\n"); // Pairing process completed ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status; self->sec_status = status->auth_status; if (status->auth_status == BLE_GAP_SEC_STATUS_SUCCESS) { self->ediv = self->bonding_keys.own_enc.master_id.ediv; self->pair_status = PAIR_PAIRED; + CONNECTION_DEBUG_PRINTF("PAIR_PAIRED\n"); bonding_save_keys(self->is_central, self->conn_handle, &self->bonding_keys); } else { + // Inform busy-waiter pairing has failed. self->pair_status = PAIR_NOT_PAIRED; + CONNECTION_DEBUG_PRINTF("PAIR_NOT_PAIRED\n"); } break; } case BLE_GAP_EVT_SEC_INFO_REQUEST: { // 0x14 + CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_SEC_INFO_REQUEST\n"); // Peer asks for the stored keys. // - load key and return if bonded previously. // - Else return NULL --> Initiate key exchange ble_gap_evt_sec_info_request_t* sec_info_request = &ble_evt->evt.gap_evt.params.sec_info_request; (void) sec_info_request; if ( bonding_load_keys(self->is_central, sec_info_request->master_id.ediv, &self->bonding_keys) ) { - sd_ble_gap_sec_info_reply(self->conn_handle, - &self->bonding_keys.own_enc.enc_info, - &self->bonding_keys.peer_id.id_info, - NULL); + CONNECTION_DEBUG_PRINTF("keys found\n"); + uint32_t err_code = sd_ble_gap_sec_info_reply( + self->conn_handle, + &self->bonding_keys.own_enc.enc_info, + &self->bonding_keys.peer_id.id_info, + NULL); + CONNECTION_DEBUG_PRINTF("sd_ble_gap_sec_info_reply err_code: %0lx\n", err_code); self->ediv = self->bonding_keys.own_enc.master_id.ediv; } else { + CONNECTION_DEBUG_PRINTF("keys not found\n"); + // We don't have stored keys. Ask for keys. sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL); } break; } case BLE_GAP_EVT_CONN_SEC_UPDATE: { // 0x1a + CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_CONN_SEC_UPDATE\n"); ble_gap_conn_sec_t* conn_sec = &ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec; if (conn_sec->sec_mode.sm <= 1 && conn_sec->sec_mode.lv <= 1) { // Security setup did not succeed: @@ -250,12 +273,13 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { // mode >=1 and/or level >=1 means encryption is set up self->pair_status = PAIR_NOT_PAIRED; } else { - // Does an sd_ble_gatts_sys_attr_set() with the stored values. if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv)) { - // Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS SUCCESS. - self->ediv = self->bonding_keys.own_enc.master_id.ediv; + // Did an sd_ble_gatts_sys_attr_set() with the stored sys_attr values. + // Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS with BLE_GAP_SEC_STATUS_SUCCESS. + CONNECTION_DEBUG_PRINTF("did bonding_load_cccd_info()\n"); } else { // No matching bonding found, so use fresh system attributes. + CONNECTION_DEBUG_PRINTF("bonding_load_cccd_info() failed\n"); sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); } } @@ -263,8 +287,10 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { } default: + CONNECTION_DEBUG_PRINTF("Unhandled event: %04x\n", ble_evt->header.evt_id); return false; } + CONNECTION_DEBUG_PRINTF("Handled event: %04x\n", ble_evt->header.evt_id); return true; } diff --git a/ports/nrf/common-hal/_bleio/bonding.c b/ports/nrf/common-hal/_bleio/bonding.c index 1175f72e4..0476d722b 100644 --- a/ports/nrf/common-hal/_bleio/bonding.c +++ b/ports/nrf/common-hal/_bleio/bonding.c @@ -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)); } diff --git a/ports/nrf/common-hal/_bleio/bonding.h b/ports/nrf/common-hal/_bleio/bonding.h index 1b0083865..35347af2a 100644 --- a/ports/nrf/common-hal/_bleio/bonding.h +++ b/ports/nrf/common-hal/_bleio/bonding.h @@ -40,8 +40,12 @@ #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. @@ -71,12 +75,13 @@ typedef struct { } 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; +typedef struct _queued_bonding_block_entry_t { + struct _queued_bonding_block_entry_t *next; + bonding_block_t block; // variable length, based on data_length. +} queued_bonding_block_entry_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); diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 14f7632fd..a480ec1e0 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -156,7 +156,7 @@ #define MICROPY_PORT_ROOT_POINTERS \ CIRCUITPY_COMMON_ROOT_POINTERS \ ble_drv_evt_handler_entry_t* ble_drv_evt_handler_entries; \ - queued_bonding_block_list_elt_t* queued_bonding_block_list; \ + queued_bonding_block_entry_t* queued_bonding_block_entries; \ #endif // NRF5_MPCONFIGPORT_H__ diff --git a/shared-bindings/_bleio/Adapter.c b/shared-bindings/_bleio/Adapter.c index 5d3211b0c..500055cf3 100644 --- a/shared-bindings/_bleio/Adapter.c +++ b/shared-bindings/_bleio/Adapter.c @@ -360,22 +360,35 @@ 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); +//| .. 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[] = { { MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&bleio_adapter_enabled_obj) }, { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_adapter_address_obj) }, - { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_adapter_name_obj) }, + { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_adapter_name_obj) }, - { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_adapter_start_advertising_obj) }, - { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_adapter_stop_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_adapter_start_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_adapter_stop_advertising_obj) }, { MP_ROM_QSTR(MP_QSTR_start_scan), MP_ROM_PTR(&bleio_adapter_start_scan_obj) }, - { MP_ROM_QSTR(MP_QSTR_stop_scan), MP_ROM_PTR(&bleio_adapter_stop_scan_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_scan), MP_ROM_PTR(&bleio_adapter_stop_scan_obj) }, - { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_adapter_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_adapter_connect_obj) }, - { 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_erase_bonding), MP_ROM_PTR(&bleio_adapter_erase_bonding_obj) }, }; STATIC MP_DEFINE_CONST_DICT(bleio_adapter_locals_dict, bleio_adapter_locals_dict_table); diff --git a/shared-bindings/_bleio/Adapter.h b/shared-bindings/_bleio/Adapter.h index 4340d82c1..9b20a461a 100644 --- a/shared-bindings/_bleio/Adapter.h +++ b/shared-bindings/_bleio/Adapter.h @@ -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 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); -void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self); +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); +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); -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 bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self); +extern mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self); +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 -- cgit v1.2.3 From 9e7f8743c2725c1818033e348e8d542ec7539045 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 12 Jan 2020 23:32:51 -0500 Subject: fix CCCD bonding store; avoid excessive bonding writes --- ports/nrf/common-hal/_bleio/Characteristic.c | 31 ------ ports/nrf/common-hal/_bleio/Characteristic.h | 1 - ports/nrf/common-hal/_bleio/Connection.c | 18 ++- ports/nrf/common-hal/_bleio/bonding.c | 161 ++++++++++++++++----------- py/gc.c | 4 + py/gc.h | 2 + shared-bindings/_bleio/Characteristic.c | 13 +-- shared-bindings/_bleio/Characteristic.h | 1 - 8 files changed, 117 insertions(+), 114 deletions(-) diff --git a/ports/nrf/common-hal/_bleio/Characteristic.c b/ports/nrf/common-hal/_bleio/Characteristic.c index a87cd6e18..4d5cab5c5 100644 --- a/ports/nrf/common-hal/_bleio/Characteristic.c +++ b/ports/nrf/common-hal/_bleio/Characteristic.c @@ -83,29 +83,6 @@ STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_ } } -STATIC bool characteristic_on_ble_evt(ble_evt_t *ble_evt, void *param) { - bleio_characteristic_obj_t *self = (bleio_characteristic_obj_t *) param; - switch (ble_evt->header.evt_id) { - case BLE_GATTS_EVT_WRITE: { - // A client wrote to this server characteristic. - // If we are bonded, stored the CCCD value. - if (self->service != MP_OBJ_NULL) { - bleio_connection_obj_t *connection = self->service->connection; - uint16_t conn_handle = bleio_connection_get_conn_handle(connection); - if (conn_handle != BLE_CONN_HANDLE_INVALID && - common_hal_bleio_connection_get_paired(connection) && - ble_evt->evt.gatts_evt.params.write.handle == self->cccd_handle) { - bonding_save_cccd_info( - connection->connection->is_central, conn_handle, connection->connection->ediv); - } - } - break; - } - } - - return true; -} - void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { self->service = service; self->uuid = uuid; @@ -132,9 +109,6 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, if (initial_value_bufinfo != NULL) { common_hal_bleio_characteristic_set_value(self, initial_value_bufinfo); } - - self->handler_entry.next = NULL; -//////////////// ble_drv_add_event_handler_entry(&self->handler_entry, characteristic_on_ble_evt, self); } bleio_descriptor_obj_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) { @@ -288,8 +262,3 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, } } - -void common_hal_bleio_characteristic_del(bleio_characteristic_obj_t *self) { - // Remove from event handler list, since the evt handler entry is built-in and not a heap object. - ble_drv_remove_event_handler(characteristic_on_ble_evt, self); -} diff --git a/ports/nrf/common-hal/_bleio/Characteristic.h b/ports/nrf/common-hal/_bleio/Characteristic.h index 5759321aa..bb8f28495 100644 --- a/ports/nrf/common-hal/_bleio/Characteristic.h +++ b/ports/nrf/common-hal/_bleio/Characteristic.h @@ -47,7 +47,6 @@ typedef struct _bleio_characteristic_obj { bleio_attribute_security_mode_t read_perm; bleio_attribute_security_mode_t write_perm; bleio_descriptor_obj_t *descriptor_list; - ble_drv_evt_handler_entry_t handler_entry; uint16_t user_desc_handle; uint16_t cccd_handle; uint16_t sccd_handle; diff --git a/ports/nrf/common-hal/_bleio/Connection.c b/ports/nrf/common-hal/_bleio/Connection.c index 664805b66..b9a9cf2c4 100644 --- a/ports/nrf/common-hal/_bleio/Connection.c +++ b/ports/nrf/common-hal/_bleio/Connection.c @@ -123,6 +123,17 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { 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) { + bonding_save_cccd_info(self->is_central, self->conn_handle, self->ediv); + } + break; + case BLE_GATTS_EVT_SYS_ATTR_MISSING: sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); break; @@ -223,7 +234,7 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { case BLE_GAP_EVT_AUTH_STATUS: { // 0x19 CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_AUTH_STATUS\n"); - // Pairing process completed + // Key exchange completed. ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status; self->sec_status = status->auth_status; if (status->auth_status == BLE_GAP_SEC_STATUS_SUCCESS) { @@ -264,8 +275,10 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { } case BLE_GAP_EVT_CONN_SEC_UPDATE: { // 0x1a - CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_CONN_SEC_UPDATE\n"); + // 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; + CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_CONN_SEC_UPDATE, sm: %d, lv: %d\n", + conn_sec->sec_mode.sm, conn_sec->sec_mode.lv); if (conn_sec->sec_mode.sm <= 1 && conn_sec->sec_mode.lv <= 1) { // Security setup did not succeed: // mode 0, level 0 means no access @@ -282,6 +295,7 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { CONNECTION_DEBUG_PRINTF("bonding_load_cccd_info() failed\n"); sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); } + self->pair_status = PAIR_PAIRED; } break; } diff --git a/ports/nrf/common-hal/_bleio/bonding.c b/ports/nrf/common-hal/_bleio/bonding.c index 0476d722b..56f77af85 100644 --- a/ports/nrf/common-hal/_bleio/bonding.c +++ b/ports/nrf/common-hal/_bleio/bonding.c @@ -33,6 +33,7 @@ #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 "sd_mutex.h" @@ -56,8 +57,8 @@ const uint32_t BONDING_FLAG = ('1' | '0' << 8 | 'D' << 16 | 'B' << 24); // 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) -STATIC bonding_block_t *bonding_unused_block = NULL; -nrf_mutex_t queued_bonding_block_entries_mutex; +STATIC nrf_mutex_t queued_bonding_block_entries_mutex; +STATIC uint64_t block_queued_at_ticks_ms = 0; #if BONDING_DEBUG void bonding_print_block(bonding_block_t *block) { @@ -79,6 +80,7 @@ STATIC size_t compute_block_size(uint16_t data_length) { } void bonding_erase_storage(void) { + BONDING_DEBUG_PRINTF("bonding_erase_storage()\n"); // Erase all pages in the bonding area. for(uint32_t page_address = BONDING_PAGES_START_ADDR; page_address < BONDING_PAGES_END_ADDR; @@ -90,8 +92,6 @@ void bonding_erase_storage(void) { 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; } // Given NULL to start or block address, return the address of the next valid block. @@ -122,18 +122,16 @@ STATIC bonding_block_t *next_block(bonding_block_t *block) { } } -// Find the block with given type and ediv value. +// 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_block_with_keys(bool is_central, bonding_block_type_t type, uint16_t ediv) { +STATIC bonding_block_t *find_candidate_block(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; } - BONDING_DEBUG_PRINT_BLOCK(block); if (block->type == BLOCK_INVALID) { // Skip discarded blocks. continue; @@ -149,11 +147,22 @@ STATIC bonding_block_t *find_block_with_keys(bool is_central, bonding_block_type } } +// 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_candidate_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) { BONDING_DEBUG_PRINTF("invalidate_block()\n"); - BONDING_DEBUG_PRINT_BLOCK(block); uint32_t zero = 0; sd_flash_write_sync((uint32_t *) block, &zero, 1); } @@ -164,6 +173,10 @@ STATIC void queue_write_block(bool is_central, bonding_block_type_t type, uint16 return; } + // No heap available, so never mind. This might be called between VM instantiations. + if (!gc_alloc_possible()) { + return; + } queued_bonding_block_entry_t* queued_entry = m_malloc_maybe(sizeof(queued_bonding_block_entry_t) + data_length, false); @@ -181,31 +194,35 @@ STATIC void queue_write_block(bool is_central, bonding_block_type_t type, uint16 memcpy(&queued_entry->block.data, data, data_length); } + // Note: blocks are added in LIFO order, for simplicity and speed. + // The assumption is that there won't be stale blocks on the + // list. The sys_attr blocks don't contain sys_attr data, just a + // request to store the latest value. The key blocks are assumed + // not to be superseded quickly. If this assumption becomes + // invalid, the queue should be changed to FIFO. + // 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); + + // Remember when we last queued a block, so we avoid excesive + // sys_attr writes. + block_queued_at_ticks_ms = supervisor_ticks_ms64(); } // 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) { - bonding_erase_storage(); - } - - sd_flash_write_sync((uint32_t *) bonding_unused_block, (uint32_t *) block, sizeof(bonding_block_t) / 4); +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(uint8_t *data, uint16_t data_length) { +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 *) bonding_unused_block + sizeof(bonding_block_t)); + 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); @@ -218,43 +235,68 @@ STATIC void write_block_data(uint8_t *data, uint16_t data_length) { // 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"); +STATIC void write_sys_attr_block(bonding_block_t *block) { 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; + return; } 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; + return; } // Now we know the data size. block->data_length = length; - write_block_header(block); - write_block_data(sys_attr, length); - return true; + + // Is there an existing sys_attr block that matches the current sys_attr data? + bonding_block_t *candidate_block = find_candidate_block(block->is_central, block->type, block->ediv); + if (candidate_block) { + if (length == candidate_block->data_length && + memcmp(sys_attr, candidate_block->data, block->data_length) == 0) { + BONDING_DEBUG_PRINTF("Identical sys_attr block already stored.\n"); + // Identical block found. No need to store again. + return; + } + // Data doesn't match. Invalidate block and store a new one. + invalidate_block(candidate_block); + } + + bonding_block_t *new_block = find_unused_block(length); + write_block_header(new_block, block); + write_block_data(new_block, sys_attr, length); + return; } -STATIC bool write_keys_block(bonding_block_t *block) { - BONDING_DEBUG_PRINTF("write_keys_block()\n"); +STATIC void write_keys_block(bonding_block_t *block) { if (block->data_length != sizeof(bonding_keys_t)) { - return false; + // Bad length. + return; + } + + // Is there an existing keys block that matches? + bonding_block_t *candidate_block = find_candidate_block(block->is_central, block->type, block->ediv); + if (candidate_block) { + if (block->data_length == candidate_block->data_length && + memcmp(block->data, candidate_block->data, block->data_length) == 0) { + BONDING_DEBUG_PRINTF("Identical keys block already stored.\n"); + // Identical block found. No need to store again. + return; + } + // Data doesn't match. Invalidate block and store a new one. + invalidate_block(candidate_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; + bonding_block_t *new_block = find_unused_block(sizeof(bonding_keys_t)); + write_block_header(new_block, block); + write_block_data(new_block, (uint8_t *) bonding_keys, sizeof(bonding_keys_t)); } @@ -269,8 +311,6 @@ void bonding_reset(void) { 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_with_keys(true, BLOCK_UNUSED, EDIV_INVALID); } } @@ -282,6 +322,19 @@ void bonding_background(void) { if (!sd_en) { return; } + + if (block_queued_at_ticks_ms == 0) { + // No writes have been queued yet. + return; + } + + // Wait at least one second before writing a block, to consolidate writes + // that will be duplicates. + uint64_t current_ticks_ms = supervisor_ticks_ms64(); + if (current_ticks_ms - block_queued_at_ticks_ms < 1000) { + return; + } + // Get block at front of list. bonding_block_t *block = NULL; sd_mutex_acquire_wait(&queued_bonding_block_entries_mutex); @@ -292,27 +345,10 @@ void bonding_background(void) { } sd_mutex_release(&queued_bonding_block_entries_mutex); if (!block) { + // List is empty. return; } - // Is there an existing block whose keys match? - 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(): 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_with_keys\n"); - invalidate_block(block_with_keys); - } - switch (block->type) { case BLOCK_SYS_ATTR: write_sys_attr_block(block); @@ -323,27 +359,23 @@ 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_with_keys(is_central, BLOCK_SYS_ATTR, ediv); + bonding_block_t *block = find_candidate_block(is_central, BLOCK_SYS_ATTR, ediv); if (block == NULL) { - 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, 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_with_keys(is_central, BLOCK_KEYS, ediv); + bonding_block_t *block = find_candidate_block(is_central, BLOCK_KEYS, ediv); if (block == NULL) { - BONDING_DEBUG_PRINTF("bonding_load_keys(): block not found, ediv: %04x\n", ediv); return false; } if (sizeof(bonding_keys_t) != block->data_length) { @@ -351,15 +383,12 @@ 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, 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) { - BONDING_DEBUG_PRINTF("bonding_save_cccd_info: is_central: %d, conn_handle: %04x, ediv: %04x\n", - is_central, conn_handle, ediv); + BONDING_DEBUG_PRINTF("bonding_save_cccd_info()\n"); queue_write_block(is_central, BLOCK_SYS_ATTR, ediv, conn_handle, NULL, 0); } @@ -367,7 +396,5 @@ void bonding_save_keys(bool is_central, uint16_t conn_handle, bonding_keys_t *bo 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)); } diff --git a/py/gc.c b/py/gc.c index 5e631f5ed..fef67f61b 100755 --- a/py/gc.c +++ b/py/gc.c @@ -464,6 +464,10 @@ void gc_info(gc_info_t *info) { 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 // 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) { diff --git a/py/gc.h b/py/gc.h index cd63ba94c..2a0811f4e 100644 --- a/py/gc.h +++ b/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_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); // Use this function to sweep the whole heap and run all finalisers diff --git a/shared-bindings/_bleio/Characteristic.c b/shared-bindings/_bleio/Characteristic.c index 0434368f2..2b1dc1f78 100644 --- a/shared-bindings/_bleio/Characteristic.c +++ b/shared-bindings/_bleio/Characteristic.c @@ -128,8 +128,7 @@ STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ } mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - // There may be some cleanup needed when a characteristic is gc'd, so enable finaliser. - bleio_characteristic_obj_t *characteristic = m_new_obj_with_finaliser(bleio_characteristic_obj_t); + bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); characteristic->base.type = &bleio_characteristic_type; // Range checking on max_length arg is done by the common_hal layer, because @@ -293,17 +292,7 @@ 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); -// Cleanup on gc. -STATIC mp_obj_t bleio_characteristic_del(mp_obj_t self_in) { - bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_bleio_characteristic_del(self); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_del_obj, bleio_characteristic_del); - - STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&bleio_characteristic_del_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_uuid), MP_ROM_PTR(&bleio_characteristic_uuid_obj) }, diff --git a/shared-bindings/_bleio/Characteristic.h b/shared-bindings/_bleio/Characteristic.h index 60ab57572..c4356fd4b 100644 --- a/shared-bindings/_bleio/Characteristic.h +++ b/shared-bindings/_bleio/Characteristic.h @@ -45,6 +45,5 @@ extern bleio_descriptor_obj_t *common_hal_bleio_characteristic_get_descriptor_li extern bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self); extern void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor); extern void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate); -extern void common_hal_bleio_characteristic_del(bleio_characteristic_obj_t *self); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H -- cgit v1.2.3 From 4ad004f24eb79a382f1e6230f8ff02784d469d87 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 13 Jan 2020 17:52:32 -0500 Subject: put bonding to-do flags into Connection objects instead of using a heap-allocated queue --- ports/nrf/common-hal/_bleio/Connection.c | 32 ++--- ports/nrf/common-hal/_bleio/Connection.h | 9 +- ports/nrf/common-hal/_bleio/bonding.c | 204 +++++++++---------------------- ports/nrf/common-hal/_bleio/bonding.h | 21 ++-- ports/nrf/mpconfigport.h | 1 - 5 files changed, 84 insertions(+), 183 deletions(-) diff --git a/ports/nrf/common-hal/_bleio/Connection.c b/ports/nrf/common-hal/_bleio/Connection.c index b9a9cf2c4..ac5c7ecba 100644 --- a/ports/nrf/common-hal/_bleio/Connection.c +++ b/ports/nrf/common-hal/_bleio/Connection.c @@ -46,6 +46,7 @@ #include "shared-bindings/_bleio/Characteristic.h" #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/UUID.h" +#include "supervisor/shared/tick.h" #include "common-hal/_bleio/bonding.h" @@ -93,7 +94,6 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { switch (ble_evt->header.evt_id) { case BLE_GAP_EVT_DISCONNECTED: - CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_DISCONNECTED\n"); // Adapter.c does the work for this event. break; @@ -130,7 +130,12 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { 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) { - bonding_save_cccd_info(self->is_central, self->conn_handle, self->ediv); + // + // 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; @@ -194,7 +199,6 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { break; } case BLE_GAP_EVT_SEC_PARAMS_REQUEST: { - CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_SEC_PARAMS_REQUEST\n"); // First time pairing. // 1. Either we or peer initiate the process // 2. Peer asks for security parameters using BLE_GAP_EVT_SEC_PARAMS_REQUEST. @@ -227,47 +231,40 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { } case BLE_GAP_EVT_LESC_DHKEY_REQUEST: - CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_LESC_DHKEY_REQUEST\n"); // TODO for LESC pairing: // sd_ble_gap_lesc_dhkey_reply(...); break; case BLE_GAP_EVT_AUTH_STATUS: { // 0x19 - CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_AUTH_STATUS\n"); // Key exchange completed. ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status; self->sec_status = status->auth_status; if (status->auth_status == BLE_GAP_SEC_STATUS_SUCCESS) { self->ediv = self->bonding_keys.own_enc.master_id.ediv; self->pair_status = PAIR_PAIRED; - CONNECTION_DEBUG_PRINTF("PAIR_PAIRED\n"); - bonding_save_keys(self->is_central, self->conn_handle, &self->bonding_keys); + // Save keys in bonding area at next opportunity. + self->do_bond_keys = true; } else { // Inform busy-waiter pairing has failed. self->pair_status = PAIR_NOT_PAIRED; - CONNECTION_DEBUG_PRINTF("PAIR_NOT_PAIRED\n"); } break; } case BLE_GAP_EVT_SEC_INFO_REQUEST: { // 0x14 - CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_SEC_INFO_REQUEST\n"); // Peer asks for the stored keys. // - load key and return if bonded previously. // - Else return NULL --> Initiate key exchange ble_gap_evt_sec_info_request_t* sec_info_request = &ble_evt->evt.gap_evt.params.sec_info_request; (void) sec_info_request; if ( bonding_load_keys(self->is_central, sec_info_request->master_id.ediv, &self->bonding_keys) ) { - CONNECTION_DEBUG_PRINTF("keys found\n"); - uint32_t err_code = sd_ble_gap_sec_info_reply( + sd_ble_gap_sec_info_reply( self->conn_handle, &self->bonding_keys.own_enc.enc_info, &self->bonding_keys.peer_id.id_info, NULL); - CONNECTION_DEBUG_PRINTF("sd_ble_gap_sec_info_reply err_code: %0lx\n", err_code); self->ediv = self->bonding_keys.own_enc.master_id.ediv; } else { - CONNECTION_DEBUG_PRINTF("keys not found\n"); // We don't have stored keys. Ask for keys. sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL); } @@ -277,8 +274,6 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { 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; - CONNECTION_DEBUG_PRINTF("BLE_GAP_EVT_CONN_SEC_UPDATE, sm: %d, lv: %d\n", - conn_sec->sec_mode.sm, conn_sec->sec_mode.lv); if (conn_sec->sec_mode.sm <= 1 && conn_sec->sec_mode.lv <= 1) { // Security setup did not succeed: // mode 0, level 0 means no access @@ -289,10 +284,8 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv)) { // Did an sd_ble_gatts_sys_attr_set() with the stored sys_attr values. // Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS with BLE_GAP_SEC_STATUS_SUCCESS. - CONNECTION_DEBUG_PRINTF("did bonding_load_cccd_info()\n"); } else { // No matching bonding found, so use fresh system attributes. - CONNECTION_DEBUG_PRINTF("bonding_load_cccd_info() failed\n"); sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); } self->pair_status = PAIR_PAIRED; @@ -301,10 +294,8 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { } default: - CONNECTION_DEBUG_PRINTF("Unhandled event: %04x\n", ble_evt->header.evt_id); return false; } - CONNECTION_DEBUG_PRINTF("Handled event: %04x\n", ble_evt->header.evt_id); return true; } @@ -590,8 +581,7 @@ STATIC bool discovery_on_ble_evt(ble_evt_t *ble_evt, mp_obj_t payload) { break; default: - // For debugging. - // mp_printf(&mp_plat_print, "Unhandled discovery event: 0x%04x\n", ble_evt->header.evt_id); + // CONNECTION_DEBUG_PRINTF(&mp_plat_print, "Unhandled discovery event: 0x%04x\n", ble_evt->header.evt_id); return false; break; } diff --git a/ports/nrf/common-hal/_bleio/Connection.h b/ports/nrf/common-hal/_bleio/Connection.h index daa7f6446..a9e82dd39 100644 --- a/ports/nrf/common-hal/_bleio/Connection.h +++ b/ports/nrf/common-hal/_bleio/Connection.h @@ -57,8 +57,8 @@ typedef struct { // The advertising data and scan response buffers are held by us, not by the SD, so we must // maintain them and not change it. If we need to change the contents during advertising, // there are tricks to get the SD to notice (see DevZone - TBS). - // EDIV: Encrypted Diversifier: Identifies LTK during legacy pairing. bonding_keys_t bonding_keys; + // EDIV: Encrypted Diversifier: Identifies LTK during legacy pairing. uint16_t ediv; volatile pair_status_t pair_status; uint8_t sec_status; // Internal security status. @@ -66,6 +66,13 @@ typedef struct { ble_drv_evt_handler_entry_t handler_entry; ble_gap_conn_params_t conn_params; volatile bool conn_params_updating; + // Request that CCCD info for this connection be saved. + volatile bool do_bond_cccds; + // Request that security key info for this connection be saved. + volatile bool do_bond_keys; + // Time of setting do_bond_ccds: we delay a bit to consolidate multiple CCCD changes + // into one write. Time is currently in ticks_ms. + uint64_t do_bond_cccds_request_time; } bleio_connection_internal_t; typedef struct { diff --git a/ports/nrf/common-hal/_bleio/bonding.c b/ports/nrf/common-hal/_bleio/bonding.c index 56f77af85..eb30ee8b1 100644 --- a/ports/nrf/common-hal/_bleio/bonding.c +++ b/ports/nrf/common-hal/_bleio/bonding.c @@ -36,7 +36,6 @@ #include "supervisor/shared/tick.h" #include "nrf_soc.h" -#include "sd_mutex.h" #include "bonding.h" @@ -57,9 +56,6 @@ const uint32_t BONDING_FLAG = ('1' | '0' << 8 | 'D' << 16 | 'B' << 24); // 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) -STATIC nrf_mutex_t queued_bonding_block_entries_mutex; -STATIC uint64_t block_queued_at_ticks_ms = 0; - #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", @@ -80,7 +76,6 @@ STATIC size_t compute_block_size(uint16_t data_length) { } void bonding_erase_storage(void) { - BONDING_DEBUG_PRINTF("bonding_erase_storage()\n"); // Erase all pages in the bonding area. for(uint32_t page_address = BONDING_PAGES_START_ADDR; page_address < BONDING_PAGES_END_ADDR; @@ -125,7 +120,7 @@ STATIC bonding_block_t *next_block(bonding_block_t *block) { // 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_candidate_block(bool is_central, bonding_block_type_t type, uint16_t ediv) { +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); @@ -149,7 +144,7 @@ STATIC bonding_block_t *find_candidate_block(bool is_central, bonding_block_type // 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_candidate_block(true, BLOCK_UNUSED, EDIV_INVALID); + 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) { @@ -162,56 +157,10 @@ STATIC bonding_block_t* find_unused_block(uint16_t data_length) { // 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) { - BONDING_DEBUG_PRINTF("invalidate_block()\n"); uint32_t zero = 0; sd_flash_write_sync((uint32_t *) block, &zero, 1); } -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) { - if (compute_block_size(data_length) > BONDING_DATA_END_ADDR - BONDING_DATA_START_ADDR) { - // Ridiculous size. - return; - } - - // No heap available, so never mind. This might be called between VM instantiations. - if (!gc_alloc_possible()) { - return; - } - queued_bonding_block_entry_t* queued_entry = - m_malloc_maybe(sizeof(queued_bonding_block_entry_t) + data_length, false); - - if (!queued_entry) { - // Failed to allocate. Not much we can do, since this might be during an evt handler. - return; - } - - 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_entry->block.data, data, data_length); - } - - // Note: blocks are added in LIFO order, for simplicity and speed. - // The assumption is that there won't be stale blocks on the - // list. The sys_attr blocks don't contain sys_attr data, just a - // request to store the latest value. The key blocks are assumed - // not to be superseded quickly. If this assumption becomes - // invalid, the queue should be changed to FIFO. - - // 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); - - // Remember when we last queued a block, so we avoid excesive - // sys_attr writes. - block_queued_at_ticks_ms = supervisor_ticks_ms64(); -} - // 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); @@ -237,134 +186,109 @@ STATIC void write_block_data(bonding_block_t *dest_block, uint8_t *data, uint16_ } } -STATIC void write_sys_attr_block(bonding_block_t *block) { +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(block->conn_handle, NULL, &length, SYS_ATTR_FLAGS) != NRF_SUCCESS) { + 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(block->conn_handle, sys_attr, &length, SYS_ATTR_FLAGS) != NRF_SUCCESS) { + if(sd_ble_gatts_sys_attr_get(connection->conn_handle, sys_attr, &length, SYS_ATTR_FLAGS) != NRF_SUCCESS) { return; } - // Now we know the data size. - block->data_length = length; - // Is there an existing sys_attr block that matches the current sys_attr data? - bonding_block_t *candidate_block = find_candidate_block(block->is_central, block->type, block->ediv); - if (candidate_block) { - if (length == candidate_block->data_length && - memcmp(sys_attr, candidate_block->data, block->data_length) == 0) { - BONDING_DEBUG_PRINTF("Identical sys_attr block already stored.\n"); + 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(candidate_block); + 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); + write_block_header(new_block, &block_header); write_block_data(new_block, sys_attr, length); return; } -STATIC void write_keys_block(bonding_block_t *block) { - if (block->data_length != sizeof(bonding_keys_t)) { - // Bad 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 *candidate_block = find_candidate_block(block->is_central, block->type, block->ediv); - if (candidate_block) { - if (block->data_length == candidate_block->data_length && - memcmp(block->data, candidate_block->data, block->data_length) == 0) { - BONDING_DEBUG_PRINTF("Identical keys block already stored.\n"); + 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(candidate_block); + invalidate_block(existing_block); } - 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; + 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); - write_block_data(new_block, (uint8_t *) bonding_keys, 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)); } -// Call only when SD is enabled. void bonding_reset(void) { - 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(); } } -// Write bonding blocks to flash. These have been queued during event handlers. -// We do one at a time, on each background call. +// Write bonding blocks to flash. Requests have been queued during evt handlers. void bonding_background(void) { - uint8_t sd_en = 0; - (void) sd_softdevice_is_enabled(&sd_en); - if (!sd_en) { - return; - } - - if (block_queued_at_ticks_ms == 0) { - // No writes have been queued yet. - return; - } - - // Wait at least one second before writing a block, to consolidate writes - // that will be duplicates. - uint64_t current_ticks_ms = supervisor_ticks_ms64(); - if (current_ticks_ms - block_queued_at_ticks_ms < 1000) { - return; - } - - // Get block at front of list. - 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_entries_mutex); - if (!block) { - // List is empty. - return; - } - - switch (block->type) { - case BLOCK_SYS_ATTR: - write_sys_attr_block(block); - break; - - case BLOCK_KEYS: - write_keys_block(block); - break; + // 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 = &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; + } - default: - break; + 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_candidate_block(is_central, BLOCK_SYS_ATTR, ediv); + bonding_block_t *block = find_existing_block(is_central, BLOCK_SYS_ATTR, ediv); if (block == NULL) { return false; } @@ -374,7 +298,7 @@ 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) { - bonding_block_t *block = find_candidate_block(is_central, BLOCK_KEYS, ediv); + bonding_block_t *block = find_existing_block(is_central, BLOCK_KEYS, ediv); if (block == NULL) { return false; } @@ -386,15 +310,3 @@ bool bonding_load_keys(bool is_central, uint16_t ediv, bonding_keys_t *bonding_k memcpy(bonding_keys, block->data, block->data_length); return true; } - -void bonding_save_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv) { - BONDING_DEBUG_PRINTF("bonding_save_cccd_info()\n"); - 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; - queue_write_block(is_central, BLOCK_KEYS, ediv, conn_handle, (uint8_t *) bonding_keys, sizeof(bonding_keys_t)); -} diff --git a/ports/nrf/common-hal/_bleio/bonding.h b/ports/nrf/common-hal/_bleio/bonding.h index 35347af2a..cb8e7c427 100644 --- a/ports/nrf/common-hal/_bleio/bonding.h +++ b/ports/nrf/common-hal/_bleio/bonding.h @@ -48,11 +48,12 @@ #define BONDING_DEBUG_PRINT_KEYS(keys) #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. +// 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 @@ -69,24 +70,16 @@ typedef struct { 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. + // 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; -// Bonding blocks that need to be written are stored in a linked list. -typedef struct _queued_bonding_block_entry_t { - struct _queued_bonding_block_entry_t *next; - bonding_block_t block; // variable length, based on data_length. -} queued_bonding_block_entry_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); -void bonding_save_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv); -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 diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index a480ec1e0..70b9ab63e 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -156,7 +156,6 @@ #define MICROPY_PORT_ROOT_POINTERS \ CIRCUITPY_COMMON_ROOT_POINTERS \ ble_drv_evt_handler_entry_t* ble_drv_evt_handler_entries; \ - queued_bonding_block_entry_t* queued_bonding_block_entries; \ #endif // NRF5_MPCONFIGPORT_H__ -- cgit v1.2.3 From 0367ba7495e209e38ff80934affe9e7fb3f04cff Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 13 Jan 2020 18:29:34 -0500 Subject: fix some typos and leftovers --- locale/circuitpython.pot | 22 ++++++------- ports/nrf/common-hal/_bleio/Connection.c | 3 +- ports/nrf/common-hal/_bleio/Connection.h | 1 + ports/nrf/mpconfigport.h | 1 - ports/nrf/sd.c | 54 -------------------------------- ports/nrf/sd.h | 46 --------------------------- tools/preprocess_frozen_modules.py | 1 - 7 files changed, 11 insertions(+), 117 deletions(-) delete mode 100644 ports/nrf/sd.c delete mode 100644 ports/nrf/sd.h diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 085101ff1..a60d95aa2 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\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" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -513,21 +513,21 @@ msgstr "" msgid "Could not initialize UART" msgstr "" -#: shared-module/audiomp3/MP3File.c +#: shared-module/audiomp3/MP3Decoder.c msgid "Couldn't allocate decoder" msgstr "" #: 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" msgstr "" -#: shared-module/audiomp3/MP3File.c +#: shared-module/audiomp3/MP3Decoder.c msgid "Couldn't allocate input buffer" msgstr "" #: 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" msgstr "" @@ -620,7 +620,7 @@ msgstr "" msgid "Failed sending command." msgstr "" -#: ports/nrf/sd_mutex.c +#: ports/nrf/sd.c ports/nrf/sd_mutex.c #, c-format msgid "Failed to acquire mutex, err 0x%04x" msgstr "" @@ -643,11 +643,11 @@ msgstr "" msgid "Failed to connect: timeout" msgstr "" -#: shared-module/audiomp3/MP3File.c +#: shared-module/audiomp3/MP3Decoder.c msgid "Failed to parse MP3 file" msgstr "" -#: ports/nrf/sd_mutex.c +#: ports/nrf/sd.c ports/nrf/sd_mutex.c #, c-format msgid "Failed to release mutex, err 0x%04x" msgstr "" @@ -660,10 +660,6 @@ msgstr "" msgid "File exists" msgstr "" -#: ports/nrf/common-hal/nvm/ByteArray.c -msgid "Flash write failed" -msgstr "" - #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c msgid "Frequency captured is above capability. Capture Paused." msgstr "" @@ -1776,7 +1772,7 @@ msgstr "" msgid "extra positional arguments given" 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 msgid "file must be a file opened in byte mode" msgstr "" diff --git a/ports/nrf/common-hal/_bleio/Connection.c b/ports/nrf/common-hal/_bleio/Connection.c index b8fa77cd3..96e8b8fbe 100644 --- a/ports/nrf/common-hal/_bleio/Connection.c +++ b/ports/nrf/common-hal/_bleio/Connection.c @@ -308,7 +308,6 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { } else { if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv)) { // Did an sd_ble_gatts_sys_attr_set() with the stored sys_attr values. - // Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS with BLE_GAP_SEC_STATUS_SUCCESS. } else { // No matching bonding found, so use fresh system attributes. sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); @@ -553,7 +552,7 @@ STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, bleio default: // TODO: sd_ble_gattc_descriptors_discover() can return things that are not descriptors, // so ignore those. - // htts:p//devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors + // https://devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors break; } diff --git a/ports/nrf/common-hal/_bleio/Connection.h b/ports/nrf/common-hal/_bleio/Connection.h index 1f40cc644..282e0c4b5 100644 --- a/ports/nrf/common-hal/_bleio/Connection.h +++ b/ports/nrf/common-hal/_bleio/Connection.h @@ -67,6 +67,7 @@ typedef struct { ble_gap_conn_params_t conn_params; volatile bool conn_params_updating; uint16_t mtu; + // Request that CCCD values for this conenction be saved, using sys_attr values. volatile bool do_bond_cccds; // Request that security key info for this connection be saved. volatile bool do_bond_keys; diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 70b9ab63e..b6635965d 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -29,7 +29,6 @@ #define NRF5_MPCONFIGPORT_H__ #include "ble_drv.h" -#include "common-hal/_bleio/bonding.h" #include "nrf_mbr.h" // for MBR_SIZE #include "nrf_sdm.h" // for SD_FLASH_SIZE diff --git a/ports/nrf/sd.c b/ports/nrf/sd.c deleted file mode 100644 index b3162e6af..000000000 --- a/ports/nrf/sd.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 "py/mpconfig.h" -#include "py/runtime.h" -#include "nrf_soc.h" - -void sd_mutex_acquire_check(nrf_mutex_t* p_mutex) { - uint32_t err_code = sd_mutex_acquire(p_mutex); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to acquire mutex, err 0x%04x"), err_code); - } -} - -void sd_mutex_acquire_wait(nrf_mutex_t* p_mutex) { - while (sd_mutex_acquire(p_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) { - RUN_BACKGROUND_TASKS; - } -} - -void sd_mutex_acquire_wait_no_vm(nrf_mutex_t* p_mutex) { - while (sd_mutex_acquire(p_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) { - } -} - -void sd_mutex_release_check(nrf_mutex_t* p_mutex) { - uint32_t err_code = sd_mutex_release(p_mutex); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to release mutex, err 0x%04x"), err_code); - } -} diff --git a/ports/nrf/sd.h b/ports/nrf/sd.h deleted file mode 100644 index ca4691720..000000000 --- a/ports/nrf/sd.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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_SD_MUTEX_H -#define MICROPY_INCLUDED_NRF_SD_MUTEX_H - -#include "nrf_soc.h" - -// Helpers for common usage of nrf_mutex. - -// Try to acquire a mutex right now. Raise exception if we can't get it. -void sd_mutex_acquire_check(nrf_mutex_t* p_mutex); - -// Wait for a mutex to become available. Run VM background tasks while waiting. -void sd_mutex_acquire_wait(nrf_mutex_t* p_mutex); - -// Wait for a mutex to become available.. Block VM while waiting. -void sd_mutex_acquire_wait_no_vm(nrf_mutex_t* p_mutex); - -// Release a mutex, and raise exception on error. -void sd_mutex_release_check(nrf_mutex_t* p_mutex); - -#endif // MICROPY_INCLUDED_NRF_SD_MUTEX_H diff --git a/tools/preprocess_frozen_modules.py b/tools/preprocess_frozen_modules.py index 974ccecd1..7ae20c4d6 100755 --- a/tools/preprocess_frozen_modules.py +++ b/tools/preprocess_frozen_modules.py @@ -48,7 +48,6 @@ def copy_and_process(in_dir, out_dir): output_file_path = Path(out_dir, input_file_path.relative_to(in_dir)) if file.endswith(".py"): - print(file) if not output_file_path.parent.exists(): output_file_path.parent.mkdir(parents=True) with input_file_path.open("r") as input, output_file_path.open("w") as output: -- cgit v1.2.3 From 85dc4089b9b2d721a3e4bfc68e252c0dda450137 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 14 Jan 2020 17:23:16 -0500 Subject: address review comments --- ports/nrf/common-hal/_bleio/bonding.c | 5 +---- ports/nrf/peripherals/nrf/nvm.c | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ports/nrf/common-hal/_bleio/bonding.c b/ports/nrf/common-hal/_bleio/bonding.c index 7ba589623..9716b3988 100644 --- a/ports/nrf/common-hal/_bleio/bonding.c +++ b/ports/nrf/common-hal/_bleio/bonding.c @@ -92,6 +92,7 @@ void bonding_erase_storage(void) { // 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. @@ -127,10 +128,6 @@ STATIC bonding_block_t *find_existing_block(bool is_central, bonding_block_type_ if (block == NULL) { return NULL; } - if (block->type == BLOCK_INVALID) { - // Skip discarded blocks. - continue; - } // If types match, and block is unused, just return it. // Otherwise check that is_central and ediv match. if (type == block->type) { diff --git a/ports/nrf/peripherals/nrf/nvm.c b/ports/nrf/peripherals/nrf/nvm.c index e8fcc9008..63b168f14 100644 --- a/ports/nrf/peripherals/nrf/nvm.c +++ b/ports/nrf/peripherals/nrf/nvm.c @@ -89,7 +89,7 @@ bool sd_flash_write_sync(uint32_t *dest_words, uint32_t* src_words, uint32_t num // 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.g +// writes to a page. bool nrf_nvm_safe_flash_page_write(uint32_t page_addr, uint8_t *data) { #ifdef BLUETOOTH_SD -- cgit v1.2.3