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