summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Halbert <halbert@halwitz.org>2020-01-03 10:24:07 -0500
committerDan Halbert <halbert@halwitz.org>2020-01-03 10:24:07 -0500
commit242d572470b20a06c5b0be5e07a84aefaa4abbe1 (patch)
tree5d1ce41796dcbbe90267b76b602d2a832bdb5709
parent6c3d555b873fb7dddfa09ff24c6630c1aae47d3d (diff)
wip
-rw-r--r--ports/atmel-samd/common-hal/nvm/ByteArray.c4
-rw-r--r--ports/nrf/common-hal/_bleio/Characteristic.c23
-rw-r--r--ports/nrf/common-hal/_bleio/Connection.c53
-rw-r--r--ports/nrf/common-hal/_bleio/Connection.h3
-rw-r--r--ports/nrf/common-hal/_bleio/bonding.c240
-rw-r--r--ports/nrf/common-hal/_bleio/bonding.h43
-rw-r--r--ports/nrf/common-hal/nvm/ByteArray.c14
-rw-r--r--ports/nrf/peripherals/nrf/nvm.c6
-rwxr-xr-xtools/preprocess_frozen_modules.py1
9 files changed, 359 insertions, 28 deletions
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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ble.h"
+#include "ble_drv.h"
+#include "shared-bindings/_bleio/__init__.h"
+#include "shared-bindings/_bleio/Adapter.h"
+#include "shared-bindings/nvm/ByteArray.h"
+
+#include "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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ble.h"
+#include "ble_drv.h"
+#include "shared-bindings/_bleio/__init__.h"
+#include "shared-bindings/_bleio/Adapter.h"
+#include "shared-bindings/nvm/ByteArray.h"
+
+#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: