diff --git a/ports/nrf/common-hal/_bleio/Adapter.c b/ports/nrf/common-hal/_bleio/Adapter.c index 12d5090737..05cd0260b2 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 81639898fc..a87cd6e186 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 bb8f28495e..5759321aa2 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 4b2780dedc..b9fbaa7c33 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 1675ade43d..1175f72e47 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 d13775d4dd..2417ec66a9 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 e54e953dfc..8ba95773d6 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 0000000000..b3162e6af9 --- /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 0000000000..ca46917205 --- /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 7fcf274da0..0434368f24 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 c4356fd4b9..60ab575724 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