From abf89faae5bcf32ea3634ec351deb1d5273c5056 Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Mon, 22 May 2017 11:17:02 +0200 Subject: [PATCH 01/13] Starting DFU service introduction --- cores/arduino/Arduino.h | 4 +- cores/arduino/DFUService.cpp | 278 ++ cores/arduino/DFUService.h | 54 + cores/arduino/SoftDeviceManager.cpp | 11 + .../ble/ble_advertising/ble_advertising.c | 566 ++++ .../ble/ble_advertising/ble_advertising.h | 223 ++ .../ble/ble_services/ble_dfu/ble_dfu.c | 649 ++++ .../ble/ble_services/ble_dfu/ble_dfu.h | 239 ++ .../config/device_manager_cnfg.h | 98 + .../ble/device_manager/device_manager.h | 888 +++++ .../device_manager_peripheral.c | 2962 +++++++++++++++++ .../pstorage/config/pstorage_platform.h | 72 + .../drivers_nrf/pstorage/pstorage.c | 1572 +++++++++ .../drivers_nrf/pstorage/pstorage.h | 381 +++ .../bootloader_dfu/bootloader_types.h | 59 + .../bootloader_dfu/bootloader_util.c | 152 + .../bootloader_dfu/bootloader_util.h | 38 + .../bootloader_dfu/dfu_app_handler.c | 219 ++ .../bootloader_dfu/dfu_app_handler.h | 86 + .../libraries/bootloader_dfu/dfu_ble_svc.h | 80 + .../experimental_section_vars/section_vars.h | 263 ++ .../fstorage/config/fstorage_config.h | 59 + .../components/libraries/fstorage/fstorage.c | 494 +++ .../components/libraries/fstorage/fstorage.h | 235 ++ .../fstorage/fstorage_internal_defs.h | 135 + .../libraries/fstorage/fstorage_nosd.c | 0 cores/arduino/main.cpp | 23 +- libraries/BLE/BLEManager.cpp | 26 +- libraries/BLE/BLEManager.h | 6 +- libraries/BLE/nRF51822.cpp | 270 +- 30 files changed, 9984 insertions(+), 158 deletions(-) create mode 100644 cores/arduino/DFUService.cpp create mode 100644 cores/arduino/DFUService.h create mode 100644 cores/arduino/components/ble/ble_advertising/ble_advertising.c create mode 100644 cores/arduino/components/ble/ble_advertising/ble_advertising.h create mode 100644 cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.c create mode 100644 cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.h create mode 100644 cores/arduino/components/ble/device_manager/config/device_manager_cnfg.h create mode 100644 cores/arduino/components/ble/device_manager/device_manager.h create mode 100644 cores/arduino/components/ble/device_manager/device_manager_peripheral.c create mode 100644 cores/arduino/components/drivers_nrf/pstorage/config/pstorage_platform.h create mode 100644 cores/arduino/components/drivers_nrf/pstorage/pstorage.c create mode 100644 cores/arduino/components/drivers_nrf/pstorage/pstorage.h create mode 100644 cores/arduino/components/libraries/bootloader_dfu/bootloader_types.h create mode 100644 cores/arduino/components/libraries/bootloader_dfu/bootloader_util.c create mode 100644 cores/arduino/components/libraries/bootloader_dfu/bootloader_util.h create mode 100644 cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.c create mode 100644 cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.h create mode 100644 cores/arduino/components/libraries/bootloader_dfu/dfu_ble_svc.h create mode 100644 cores/arduino/components/libraries/experimental_section_vars/section_vars.h create mode 100644 cores/arduino/components/libraries/fstorage/config/fstorage_config.h create mode 100644 cores/arduino/components/libraries/fstorage/fstorage.c create mode 100644 cores/arduino/components/libraries/fstorage/fstorage.h create mode 100644 cores/arduino/components/libraries/fstorage/fstorage_internal_defs.h create mode 100644 cores/arduino/components/libraries/fstorage/fstorage_nosd.c diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index 70884ed..a95cf90 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -71,7 +71,7 @@ void loop( void ) ; #include "HardwareSerial.h" #include "delay.h" #include "Uart.h" - #include "SoftDeviceManager.h" + #include "SoftDeviceManager.h" #endif // __cplusplus @@ -83,7 +83,7 @@ void loop( void ) ; #include "wiring_analog.h" #include "wiring_shift.h" #include "WInterrupts.h" - +#include "DFUService.h" #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) diff --git a/cores/arduino/DFUService.cpp b/cores/arduino/DFUService.cpp new file mode 100644 index 0000000..65d26a9 --- /dev/null +++ b/cores/arduino/DFUService.cpp @@ -0,0 +1,278 @@ +/* + Copyright (c) 2016 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifdef __cplusplus +extern "C"{ +#endif + +#include "ble_dfu.h" +#include "dfu_app_handler.h" +#include "ble_advertising.h" +#include "pstorage.h" +#include "nrf_delay.h" + +#ifdef __cplusplus +} +#endif + +static ble_dfu_t m_dfus; +static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */ +static dm_application_instance_t m_app_handle; + +bool dfuService = true; + +extern void processBleEvents(ble_evt_t * p_ble_evt) __attribute__((weak)); +extern bool isPeripheralRunning() __attribute__((weak)); +extern bool isCentralRunning() __attribute__((weak)); + +void removeDfuService(bool remove){ + if(remove == true) + dfuService = false; +} + +static void on_ble_evt(ble_evt_t * p_ble_evt) +{ + switch (p_ble_evt->header.evt_id) + { + case BLE_GAP_EVT_CONNECTED: + m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + break; + + case BLE_GAP_EVT_DISCONNECTED: + m_conn_handle = BLE_CONN_HANDLE_INVALID; +//-----start advertising again here ? ------------ + ble_gap_adv_params_t advertisingParameters; + memset(&advertisingParameters, 0x00, sizeof(advertisingParameters)); + + advertisingParameters.type = BLE_GAP_ADV_TYPE_ADV_IND; + advertisingParameters.p_peer_addr = NULL; + advertisingParameters.fp = BLE_GAP_ADV_FP_ANY; + advertisingParameters.p_whitelist = NULL; + advertisingParameters.interval = (100 * 16) / 10; // advertising interval (in units of 0.625 ms) + advertisingParameters.timeout = 0; + + sd_ble_gap_adv_start(&advertisingParameters); +//------let it manage to BLE library ? ------------ + break; + + default: + // No implementation needed. + break; + } +} +#include "Arduino.h" +static void reset_prepare(void) +{ + if (m_conn_handle != BLE_CONN_HANDLE_INVALID) + { + // Disconnect from peer. + sd_ble_gap_disconnect(m_conn_handle, 0x13/*HCI_REMOTE_USER_TERMINATED_CONNECTION*/); + } + else + { + // If not connected, the device will be advertising. Hence stop the advertising. + sd_ble_gap_adv_stop(); + } + + // Add a delay to make sure that disconnect request is sent + nrf_delay_ms(500); +} + +void ble_evt_dispatch(ble_evt_t * p_ble_evt) +{ + if(dfuService){ + dm_ble_evt_handler(p_ble_evt); + ble_dfu_on_ble_evt(&m_dfus, p_ble_evt); + on_ble_evt(p_ble_evt); + } + // forward events to Arduino BLE library if used + if(processBleEvents) + processBleEvents(p_ble_evt); +} + +static void app_context_load(dm_handle_t const * p_handle) +{ + uint32_t err_code; + static uint32_t context_data; + dm_application_context_t context; + + context.len = sizeof(context_data); + context.p_data = (uint8_t *)&context_data; + + err_code = dm_application_context_get(p_handle, &context); + if (err_code == NRF_SUCCESS) + { + // Send Service Changed Indication if ATT table has changed. + if ((context_data & (DFU_APP_ATT_TABLE_CHANGED << DFU_APP_ATT_TABLE_POS)) != 0) + { + err_code = sd_ble_gatts_service_changed(m_conn_handle, 0x00C/*APP_SERVICE_HANDLE_START*/, 0xFFFF/*BLE_HANDLE_MAX*/); + if ((err_code != NRF_SUCCESS) && + (err_code != BLE_ERROR_INVALID_CONN_HANDLE) && + (err_code != NRF_ERROR_INVALID_STATE) && + (err_code != BLE_ERROR_NO_TX_PACKETS) && + (err_code != NRF_ERROR_BUSY) && + (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)) + { + //error + } + } + + err_code = dm_application_context_delete(p_handle); + } + else if (err_code == DM_NO_APP_CONTEXT) + { + // No context available. Ignore. + } + else + { + //error + } +} + + +static uint32_t device_manager_evt_handler(dm_handle_t const * p_handle, + dm_event_t const * p_event, + ret_code_t event_result) +{ + if (p_event->event_id == DM_EVT_LINK_SECURED) + { + app_context_load(p_handle); + } + + return NRF_SUCCESS; +} + +void add_dfu_service(){ + ble_gap_conn_params_t gap_conn_params; + ble_gap_conn_sec_mode_t sec_mode; + +//*** device manager init - start + + dm_init_param_t init_param = {.clear_persistent_data = false}; + dm_application_param_t register_param; + + // Initialize persistent storage module. + pstorage_init(); + dm_init(&init_param); + + memset(®ister_param.sec_param, 0, sizeof(ble_gap_sec_params_t)); + + register_param.sec_param.bond = 1; //SEC_PARAM_BOND + register_param.sec_param.mitm = 0; //SEC_PARAM_MITM + // register_param.sec_param.mitm = true; + register_param.sec_param.lesc = 0; //SEC_PARAM_LESC +// register_param.sec_param.lesc = true; + register_param.sec_param.keypress = 0; //SEC_PARAM_KEYPRESS + register_param.sec_param.io_caps = BLE_GAP_IO_CAPS_NONE;//BLE_GAP_IO_CAPS_DISPLAY_ONLY;//BLE_GAP_IO_CAPS_KEYBOARD_ONLY; + // register_param.sec_param.io_caps =BLE_GAP_IO_CAPS_DISPLAY_YESNO; + register_param.sec_param.oob = 0; //SEC_PARAM_OOB + register_param.sec_param.min_key_size = 7; //SEC_PARAM_MIN_KEY_SIZE + register_param.sec_param.max_key_size = 16; //SEC_PARAM_MAX_KEY_SIZE + register_param.evt_handler = device_manager_evt_handler; + register_param.service_type = DM_PROTOCOL_CNTXT_GATT_SRVR_ID; + + dm_register(&m_app_handle, ®ister_param); + +//*** device manager init - end + // If isPeripheralRunning is defined user is using Arduino BLE library + if(!isPeripheralRunning || !isPeripheralRunning()){ + // Advertising and connection parameters have to be set only if + // user is not using Arduino BLE library or peripheral role is not running + // to avoid overriding user's settings + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); + + const char * DEVICE_NAME = "ArduinoPRIMO_DFU"; + sd_ble_gap_device_name_set(&sec_mode, + (const uint8_t *)DEVICE_NAME, + strlen(DEVICE_NAME)); + + memset(&gap_conn_params, 0, sizeof(gap_conn_params)); + + gap_conn_params.min_conn_interval = 320; //MIN_CONN_INTERVAL + gap_conn_params.max_conn_interval = 520; //MAX_CONN_INTERVAL + gap_conn_params.slave_latency = 0; //SLAVE_LATENCY + gap_conn_params.conn_sup_timeout = 400; //CONN_SUP_TIMEOUT + + sd_ble_gap_ppcp_set(&gap_conn_params); + + // advertising + unsigned char adv_data[31] ={0}; + uint8_t len=strlen(DEVICE_NAME); + adv_data[0]=len+1; //len + adv_data[1]=0x09; //type - complete local name + memcpy((void*)&adv_data[2], DEVICE_NAME, len); + len=len+2; + sd_ble_gap_adv_data_set(adv_data, len, NULL, 0); + + // ble_advdata_t advdata; + // static ble_uuid_t m_adv_uuids[] = { + // {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}}; /**< Universally unique service identifiers. */ + + // Build advertising data struct to pass into @ref ble_advertising_init. + // memset(&advdata, 0, sizeof(advdata)); + + // advdata.name_type = BLE_ADVDATA_FULL_NAME; + // advdata.include_appearance = true; + // advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; + // advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]); + // advdata.uuids_complete.p_uuids = m_adv_uuids; + + // ble_adv_modes_config_t options = {0}; + // options.ble_adv_fast_enabled = true; //BLE_ADV_FAST_ENABLED; + // options.ble_adv_fast_interval = 40; //APP_ADV_INTERVAL; + // options.ble_adv_fast_timeout = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED; + // if(isCentralRunning && isCentralRunning()) //don't pass options in ble_advertising_init if central is running + // ble_advertising_init(&advdata, NULL, NULL, NULL, NULL); + // else + // ble_advertising_init(&advdata, NULL, &options, NULL, NULL); + } + + /** @snippet [DFU BLE Service initialization] */ + ble_dfu_init_t dfus_init; + + // Initialize the Device Firmware Update Service. + memset(&dfus_init, 0, sizeof(dfus_init)); + + dfus_init.evt_handler = dfu_app_on_dfu_evt; + dfus_init.error_handler = NULL; + dfus_init.evt_handler = dfu_app_on_dfu_evt; + dfus_init.revision = 1;//DFU_REVISION; + + ble_dfu_init(&m_dfus, &dfus_init); + + dfu_app_reset_prepare_set(reset_prepare); + dfu_app_dm_appl_instance_set(m_app_handle); + + if(!isPeripheralRunning || !isPeripheralRunning()){ + ble_gap_adv_params_t advertisingParameters; + memset(&advertisingParameters, 0x00, sizeof(advertisingParameters)); + + advertisingParameters.type = BLE_GAP_ADV_TYPE_ADV_IND; + advertisingParameters.p_peer_addr = NULL; + advertisingParameters.fp = BLE_GAP_ADV_FP_ANY; + advertisingParameters.p_whitelist = NULL; + advertisingParameters.interval = (100 * 16) / 10; // advertising interval (in units of 0.625 ms) + advertisingParameters.timeout = 0; + + sd_ble_gap_adv_start(&advertisingParameters); + } +} + +bool dfuIsEnabled(){ + return dfuService; +} \ No newline at end of file diff --git a/cores/arduino/DFUService.h b/cores/arduino/DFUService.h new file mode 100644 index 0000000..3f5e87b --- /dev/null +++ b/cores/arduino/DFUService.h @@ -0,0 +1,54 @@ +/* + Copyright (c) 2016 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _DFU_SERVICE_H +#define _DFU_SERVICE_H + + +/* + * \brief Choose if remove or not the DFU service from the sketches (enabled by default). + * + * \param remove + */ +extern void removeDfuService(bool remove); + + +/* + * \brief Indicate whether DFU service is enabled or not. + * + */ +extern bool dfuIsEnabled(); + + +/* + * \brief Add DFU service to the sketch. + * + */ +extern void add_dfu_service(); + + +/* + * \brief Receive and forward all BLE events. + * + * \param p_ble_evt + */ +extern void ble_evt_dispatch(ble_evt_t * p_ble_evt); + + +#endif /*_DFU_SERVICE_H*/ \ No newline at end of file diff --git a/cores/arduino/SoftDeviceManager.cpp b/cores/arduino/SoftDeviceManager.cpp index 0370373..f29bcb5 100644 --- a/cores/arduino/SoftDeviceManager.cpp +++ b/cores/arduino/SoftDeviceManager.cpp @@ -18,9 +18,20 @@ #include "SoftDeviceManager.h" +#ifdef __cplusplus +extern "C"{ +#endif + +#include "pstorage_platform.h" + +#ifdef __cplusplus +} +#endif + volatile static uint8_t _flashOperationPending; void socEvtHandler(uint32_t evt_id){ + pstorage_sys_event_handler(evt_id); switch(evt_id){ case NRF_EVT_FLASH_OPERATION_SUCCESS : _flashOperationPending = 0; diff --git a/cores/arduino/components/ble/ble_advertising/ble_advertising.c b/cores/arduino/components/ble/ble_advertising/ble_advertising.c new file mode 100644 index 0000000..959ecb1 --- /dev/null +++ b/cores/arduino/components/ble/ble_advertising/ble_advertising.c @@ -0,0 +1,566 @@ +/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + */ + + +#include "ble_advdata.h" +#include "ble_advertising.h" +#include "nrf_soc.h" +#include "nrf_log.h" +#include "pstorage.h" +#include "fstorage.h" +#include "sdk_common.h" + + +#define ADV_LOG(...) + +static bool m_advertising_start_pending = false; /**< Flag to keep track of ongoing operations on persistent memory. */ + +static ble_gap_addr_t m_peer_address; /**< Address of the most recently connected peer, used for direct advertising. */ +static ble_advdata_t m_advdata; /**< Used by the initialization function to set name, appearance, and UUIDs and advertising flags visible to peer devices. */ +static ble_adv_evt_t m_adv_evt; /**< Advertising event propogated to the main application. The event is either a transaction to a new advertising mode, or a request for whitelist or peer address.. */ +static ble_advertising_evt_handler_t m_evt_handler; /**< Handler for the advertising events. Can be initialized as NULL if no handling is implemented on in the main application. */ +static ble_advertising_error_handler_t m_error_handler; /**< Handler for the advertising error events. */ + +static ble_adv_mode_t m_adv_mode_current; /**< Variable to keep track of the current advertising mode. */ +static ble_adv_modes_config_t m_adv_modes_config; /**< Struct to keep track of disabled and enabled advertising modes, as well as time-outs and intervals.*/ + +static ble_gap_whitelist_t m_whitelist; /**< Struct that points to whitelisted addresses. */ +static ble_gap_addr_t * mp_whitelist_addr[BLE_GAP_WHITELIST_ADDR_MAX_COUNT]; /**< Pointer to a list of addresses. Pointed to by the whitelist */ +static ble_gap_irk_t * mp_whitelist_irk[BLE_GAP_WHITELIST_IRK_MAX_COUNT]; /**< Pointer to a list of Identity Resolving Keys (IRK). Pointed to by the whitelist */ +static bool m_whitelist_temporarily_disabled = false; /**< Flag to keep track of temporary disabling of the whitelist. */ +static bool m_whitelist_reply_expected = false; /**< Flag to verify that whitelist is only set when it is requested. */ +static bool m_peer_addr_reply_expected = false; /**< Flag to verify that peer address is only set when requested. */ + +static ble_advdata_manuf_data_t m_manuf_specific_data; /**< Manufacturer specific data structure*/ +static uint8_t m_manuf_data_array[BLE_GAP_ADV_MAX_SIZE]; /**< Array to store the Manufacturer specific data*/ +static ble_advdata_service_data_t m_service_data; /**< Service data structure. */ +static uint8_t m_service_data_array[BLE_GAP_ADV_MAX_SIZE]; /**< Array to store the service data. */ +static ble_advdata_conn_int_t m_slave_conn_int; /**< Connection interval range structure.*/ +static int8_t m_tx_power_level; /**< TX power level*/ + + +/**@brief Function for checking that the whitelist has entries. + */ +static bool whitelist_has_entries(ble_gap_whitelist_t const * whitelist) +{ + if ((whitelist->addr_count != 0) || (whitelist->irk_count != 0)) + { + return true; + } + return false; +} + + +/**@brief Function for setting the stored peer address back to zero. + */ +static void ble_advertising_peer_address_clear() +{ + memset(&m_peer_address, 0, sizeof(m_peer_address)); +} + + +/**@brief Function for checking if an address is non-zero. Used to determine if + */ +static bool peer_address_exists(uint8_t const * address) +{ + uint32_t i; + + for (i = 0; i < BLE_GAP_ADDR_LEN; i++) + { + if (address[i] != 0) + { + return true; + } + } + return false; +} + + +uint32_t ble_advertising_init(ble_advdata_t const * p_advdata, + ble_advdata_t const * p_srdata, + ble_adv_modes_config_t const * p_config, + ble_advertising_evt_handler_t const evt_handler, + ble_advertising_error_handler_t const error_handler) +{ + uint32_t err_code; + + VERIFY_PARAM_NOT_NULL(p_advdata); + VERIFY_PARAM_NOT_NULL(p_config); + + m_adv_mode_current = BLE_ADV_MODE_IDLE; + m_evt_handler = evt_handler; + m_error_handler = error_handler; + m_adv_modes_config = *p_config; + + ble_advertising_peer_address_clear(); + + // Prepare Whitelist. Address and IRK double pointers point to allocated arrays. + m_whitelist.pp_addrs = mp_whitelist_addr; + m_whitelist.pp_irks = mp_whitelist_irk; + + // Copy and set advertising data. + memset(&m_advdata, 0, sizeof(m_advdata)); + + // Copy advertising data. + m_advdata.name_type = p_advdata->name_type; + m_advdata.include_appearance = p_advdata->include_appearance; + m_advdata.flags = p_advdata->flags; + m_advdata.short_name_len = p_advdata->short_name_len; + /* + if(p_advdata->uuids_complete != NULL) + { + m_advdata.uuids_complete = p_advdata->uuids_complete; + } + */ + m_advdata.uuids_complete = p_advdata->uuids_complete; + m_advdata.uuids_more_available = p_advdata->uuids_more_available; + m_advdata.uuids_solicited = p_advdata->uuids_solicited; + + if(p_advdata->p_manuf_specific_data != NULL) + { + m_advdata.p_manuf_specific_data = &m_manuf_specific_data; + m_manuf_specific_data.data.p_data = m_manuf_data_array; + m_advdata.p_manuf_specific_data->company_identifier = + p_advdata->p_manuf_specific_data->company_identifier; + m_advdata.p_manuf_specific_data->data.size = p_advdata->p_manuf_specific_data->data.size; + + for(uint32_t i = 0; i < m_advdata.p_manuf_specific_data->data.size; i++) + { + m_manuf_data_array[i] = p_advdata->p_manuf_specific_data->data.p_data[i]; + } + } + + if(p_advdata->p_service_data_array != NULL) + { + m_service_data.data.p_data = m_service_data_array; + m_advdata.p_service_data_array = &m_service_data; + m_advdata.p_service_data_array->data.p_data = m_service_data_array; + m_advdata.p_service_data_array->data.size = p_advdata->p_service_data_array->data.size; + m_advdata.p_service_data_array->service_uuid = p_advdata->p_service_data_array->service_uuid; + + for(uint32_t i = 0; i < m_advdata.p_service_data_array->data.size; i++) + { + m_service_data_array[i] = p_advdata->p_service_data_array->data.p_data[i]; + } + + m_advdata.service_data_count = p_advdata->service_data_count; + } + + + if(p_advdata->p_slave_conn_int != NULL) + { + m_advdata.p_slave_conn_int = &m_slave_conn_int; + m_advdata.p_slave_conn_int->max_conn_interval = p_advdata->p_slave_conn_int->max_conn_interval; + m_advdata.p_slave_conn_int->min_conn_interval = p_advdata->p_slave_conn_int->min_conn_interval; + } + + if(p_advdata->p_tx_power_level != NULL) + { + m_advdata.p_tx_power_level = &m_tx_power_level; + m_advdata.p_tx_power_level = p_advdata->p_tx_power_level; + } + err_code = ble_advdata_set(&m_advdata, p_srdata); + return err_code; +} + +/** @brief Function to determine if a flash access in in progress. If it is the case, we can not +* start advertising until it is finished. attempted restart +* in @ref ble_advertising_on_sys_evt +* +* @return true if a flash access is in progress, false if not. +*/ +static bool flash_access_in_progress() +{ + uint32_t err_code; + uint32_t count = 0; + + err_code = pstorage_access_status_get(&count); + if ((err_code != NRF_ERROR_INVALID_STATE) && (err_code != NRF_SUCCESS)) + { + ADV_LOG("[ADV]: pstorage_access_status_get returned %d.\r\n", err_code); + return true; + } + + if (err_code == NRF_ERROR_INVALID_STATE) + { + err_code = fs_queued_op_count_get(&count); + if (err_code != FS_SUCCESS) + { + return false; + } + ADV_LOG("[ADV]: fs_queued_op_count_get gives count %d.\r\n", count); + } + + if(count != 0) + { + return true; + } + else + { + return false; + } +} + +uint32_t ble_advertising_start(ble_adv_mode_t advertising_mode) +{ + uint32_t err_code; + ble_gap_adv_params_t adv_params; + + m_adv_mode_current = advertising_mode; + + // Verify if there are any pending flash operations. If so, delay starting advertising until + // the flash operations are complete. + if(flash_access_in_progress()) + { + m_advertising_start_pending = true; + return NRF_SUCCESS; + } + + ADV_LOG("[ADV]: no flash operations in progress, prepare advertising.\r\n"); + // Fetch the peer address. + ble_advertising_peer_address_clear(); + + if ( ((m_adv_modes_config.ble_adv_directed_enabled) && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED)) + ||((m_adv_modes_config.ble_adv_directed_slow_enabled) && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED)) + ||((m_adv_modes_config.ble_adv_directed_slow_enabled) && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED_SLOW)) + ) + { + if (m_evt_handler != NULL) + { + m_peer_addr_reply_expected = true; + m_evt_handler(BLE_ADV_EVT_PEER_ADDR_REQUEST); + } + else + { + m_peer_addr_reply_expected = false; + } + } + + // If a mode is disabled, continue to the next mode. I.e fast instead of direct, slow instead of fast, idle instead of slow. + if ( (m_adv_mode_current == BLE_ADV_MODE_DIRECTED) + &&(!m_adv_modes_config.ble_adv_directed_enabled || !peer_address_exists(m_peer_address.addr))) + { + m_adv_mode_current = BLE_ADV_MODE_DIRECTED_SLOW; + } + if ( (m_adv_mode_current == BLE_ADV_MODE_DIRECTED_SLOW) + &&(!m_adv_modes_config.ble_adv_directed_slow_enabled || !peer_address_exists(m_peer_address.addr))) + { + m_adv_mode_current = BLE_ADV_MODE_FAST; + } + if (!m_adv_modes_config.ble_adv_fast_enabled && m_adv_mode_current == BLE_ADV_MODE_FAST) + { + m_adv_mode_current = BLE_ADV_MODE_SLOW; + } + if (!m_adv_modes_config.ble_adv_slow_enabled && m_adv_mode_current == BLE_ADV_MODE_SLOW) + { + m_adv_mode_current = BLE_ADV_MODE_IDLE; + m_adv_evt = BLE_ADV_EVT_IDLE; + } + + // Fetch the whitelist. + if ( (m_evt_handler != NULL) + && (m_adv_mode_current == BLE_ADV_MODE_FAST || m_adv_mode_current == BLE_ADV_MODE_SLOW) + && (m_adv_modes_config.ble_adv_whitelist_enabled) + && (!m_whitelist_temporarily_disabled)) + { + m_whitelist_reply_expected = true; + m_evt_handler(BLE_ADV_EVT_WHITELIST_REQUEST); + } + else + { + m_whitelist_reply_expected = false; + } + + // Initialize advertising parameters with default values. + memset(&adv_params, 0, sizeof(adv_params)); + + adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND; + adv_params.p_peer_addr = NULL; + adv_params.fp = BLE_GAP_ADV_FP_ANY; + adv_params.p_whitelist = NULL; + + // Set advertising parameters and events according to selected advertising mode. + switch (m_adv_mode_current) + { + case BLE_ADV_MODE_DIRECTED: + ADV_LOG("[ADV]: Starting direct advertisement.\r\n"); + adv_params.p_peer_addr = &m_peer_address; // Directed advertising. + adv_params.type = BLE_GAP_ADV_TYPE_ADV_DIRECT_IND; + adv_params.timeout = 0; + adv_params.interval = 0; + m_adv_evt = BLE_ADV_EVT_DIRECTED; + break; + + case BLE_ADV_MODE_DIRECTED_SLOW: + ADV_LOG("[ADV]: Starting direct advertisement.\r\n"); + adv_params.p_peer_addr = &m_peer_address; // Directed advertising. + adv_params.type = BLE_GAP_ADV_TYPE_ADV_DIRECT_IND; + adv_params.timeout = m_adv_modes_config.ble_adv_directed_slow_timeout; + adv_params.interval = m_adv_modes_config.ble_adv_directed_slow_interval; + m_adv_evt = BLE_ADV_EVT_DIRECTED_SLOW; + break; + + case BLE_ADV_MODE_FAST: + adv_params.timeout = m_adv_modes_config.ble_adv_fast_timeout; + adv_params.interval = m_adv_modes_config.ble_adv_fast_interval; + + if ( whitelist_has_entries(&m_whitelist) + && m_adv_modes_config.ble_adv_whitelist_enabled + && !m_whitelist_temporarily_disabled) + { + adv_params.fp = BLE_GAP_ADV_FP_FILTER_CONNREQ; + adv_params.p_whitelist = &m_whitelist; + m_advdata.flags = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED; + err_code = ble_advdata_set(&m_advdata, NULL); + VERIFY_SUCCESS(err_code); + + m_adv_evt = BLE_ADV_EVT_FAST_WHITELIST; + ADV_LOG("[ADV]: Starting fast advertisement with whitelist.\r\n"); + } + else + { + m_adv_evt = BLE_ADV_EVT_FAST; + ADV_LOG("[ADV]: Starting fast advertisement.\r\n"); + } + break; + + case BLE_ADV_MODE_SLOW: + adv_params.interval = m_adv_modes_config.ble_adv_slow_interval; + adv_params.timeout = m_adv_modes_config.ble_adv_slow_timeout; + + if ( whitelist_has_entries(&m_whitelist) + && m_adv_modes_config.ble_adv_whitelist_enabled + && !m_whitelist_temporarily_disabled) + { + adv_params.fp = BLE_GAP_ADV_FP_FILTER_CONNREQ; + adv_params.p_whitelist = &m_whitelist; + m_advdata.flags = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED; + err_code = ble_advdata_set(&m_advdata, NULL); + VERIFY_SUCCESS(err_code); + + m_adv_evt = BLE_ADV_EVT_SLOW_WHITELIST; + ADV_LOG("[ADV]: Starting slow advertisement with whitelist.\r\n"); + } + else + { + m_adv_evt = BLE_ADV_EVT_SLOW; + ADV_LOG("[ADV]: Starting slow advertisement.\r\n"); + } + break; + + default: + break; + } + if (m_adv_mode_current != BLE_ADV_MODE_IDLE) + { + err_code = sd_ble_gap_adv_start(&adv_params); + VERIFY_SUCCESS(err_code); + } + if (m_evt_handler != NULL) + { + m_evt_handler(m_adv_evt); + } + + return NRF_SUCCESS; +} + + +void ble_advertising_on_ble_evt(ble_evt_t const * p_ble_evt) +{ + static uint16_t current_slave_link_conn_handle = BLE_CONN_HANDLE_INVALID; + + switch (p_ble_evt->header.evt_id) + { + case BLE_GAP_EVT_CONNECTED: + if (p_ble_evt->evt.gap_evt.params.connected.role == BLE_GAP_ROLE_PERIPH) + { + current_slave_link_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + } + break; + + // Upon disconnection, whitelist will be activated and direct advertising is started. + case BLE_GAP_EVT_DISCONNECTED: + { + uint32_t err_code; + m_whitelist_temporarily_disabled = false; + + if (p_ble_evt->evt.gap_evt.conn_handle == current_slave_link_conn_handle) + { + err_code = ble_advertising_start(BLE_ADV_MODE_DIRECTED); + if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL)) + { + m_error_handler(err_code); + } + } + break; + } + // Upon time-out, the next advertising mode is started, i.e. go from fast to slow or from slow to idle. + case BLE_GAP_EVT_TIMEOUT: + if (p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_ADVERTISING) + { + switch (m_adv_mode_current) + { + case BLE_ADV_MODE_DIRECTED: + ADV_LOG("[ADV]: Timed out from directed advertising.\r\n"); + { + uint32_t err_code; + err_code = ble_advertising_start(BLE_ADV_MODE_DIRECTED_SLOW); + if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL)) + { + m_error_handler(err_code); + } + } + break; + case BLE_ADV_MODE_DIRECTED_SLOW: + ADV_LOG("[ADV]: Timed out from directed slow advertising.\r\n"); + { + uint32_t err_code; + err_code = ble_advertising_start(BLE_ADV_MODE_FAST); + if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL)) + { + m_error_handler(err_code); + } + } + break; + case BLE_ADV_MODE_FAST: + { + uint32_t err_code; + m_adv_evt = BLE_ADV_EVT_FAST; + ADV_LOG("[ADV]: Timed out from fast advertising, starting slow advertising.\r\n"); + err_code = ble_advertising_start(BLE_ADV_MODE_SLOW); + if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL)) + { + m_error_handler(err_code); + } + break; + } + case BLE_ADV_MODE_SLOW: + m_adv_evt = BLE_ADV_EVT_IDLE; + ADV_LOG("[ADV]: Timed out from slow advertising, stopping advertising.\r\n"); + if (m_evt_handler != NULL) + { + m_evt_handler(m_adv_evt); + } + break; + + default: + // No implementation needed. + break; + } + } + break; + + default: + // No implementation needed. + break; + } +} +void ble_advertising_on_sys_evt(uint32_t sys_evt) +{ + uint32_t err_code = NRF_SUCCESS; + switch (sys_evt) + { + + case NRF_EVT_FLASH_OPERATION_SUCCESS: + // Fall through. + + //When a flash operation finishes, advertising no longer needs to be pending. + case NRF_EVT_FLASH_OPERATION_ERROR: + if (m_advertising_start_pending) + { + m_advertising_start_pending = false; + err_code = ble_advertising_start(m_adv_mode_current); + if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL)) + { + m_error_handler(err_code); + } + } + break; + + default: + // No implementation needed. + break; + } +} + +uint32_t ble_advertising_peer_addr_reply(ble_gap_addr_t * p_peer_address) +{ + if(m_peer_addr_reply_expected == false) + { + return NRF_ERROR_INVALID_STATE; + } + + m_peer_address.addr_type = p_peer_address->addr_type; + + for (int i = 0; i < BLE_GAP_ADDR_LEN; i++) + { + m_peer_address.addr[i] = p_peer_address->addr[i]; + } + + m_peer_addr_reply_expected = false; + return NRF_SUCCESS; +} + + +uint32_t ble_advertising_whitelist_reply(ble_gap_whitelist_t * p_whitelist) +{ + uint32_t i; + + if(m_whitelist_reply_expected == false) + { + return NRF_ERROR_INVALID_STATE; + } + + m_whitelist.addr_count = p_whitelist->addr_count; + m_whitelist.irk_count = p_whitelist->irk_count; + + for (i = 0; i < m_whitelist.irk_count; i++) + { + mp_whitelist_irk[i] = p_whitelist->pp_irks[i]; + } + + for (i = 0; i < m_whitelist.addr_count; i++) + { + mp_whitelist_addr[i] = p_whitelist->pp_addrs[i]; + } + + m_whitelist_reply_expected = false; + return NRF_SUCCESS; +} + + +uint32_t ble_advertising_restart_without_whitelist(void) +{ + uint32_t err_code; + + if( m_adv_modes_config.ble_adv_whitelist_enabled == BLE_ADV_WHITELIST_ENABLED + && !m_whitelist_temporarily_disabled) + { + if (m_adv_mode_current != BLE_ADV_MODE_IDLE) + { + err_code = sd_ble_gap_adv_stop(); + VERIFY_SUCCESS(err_code); + } + m_whitelist_temporarily_disabled = true; + m_advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; + err_code = ble_advdata_set(&m_advdata, NULL); + VERIFY_SUCCESS(err_code); + + err_code = ble_advertising_start(m_adv_mode_current); + if ((err_code != NRF_SUCCESS) && (m_error_handler != NULL)) + { + m_error_handler(err_code); + } + } + return NRF_SUCCESS; +} + + diff --git a/cores/arduino/components/ble/ble_advertising/ble_advertising.h b/cores/arduino/components/ble/ble_advertising/ble_advertising.h new file mode 100644 index 0000000..09605e5 --- /dev/null +++ b/cores/arduino/components/ble/ble_advertising/ble_advertising.h @@ -0,0 +1,223 @@ +/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + */ + + +/**@file + * + * @defgroup ble_sdk_lib_advertising Advertising Module + * @{ + * @ingroup ble_sdk_lib + * @brief Module for handling connectable BLE advertising. + * + * @details The Advertising Module handles connectable advertising for your application. It can + * be configured with advertising modes to suit most typical use cases. + * Your main application can react to changes in advertising modes + * if an event handler is provided. + * + * @note The Advertising Module supports only applications with a single peripheral link. + * + * The application must propagate BLE stack events to this module by calling + * @ref ble_advertising_on_ble_evt() and system events by calling + * @ref ble_advertising_on_sys_evt(). + * + */ + +#ifndef BLE_ADVERTISING_H__ +#define BLE_ADVERTISING_H__ + +#include +#include "ble_gattc.h" +#include "ble.h" +#include "nrf_error.h" +#include "ble_advdata.h" + +/**@brief Advertising modes. +*/ +typedef enum +{ + BLE_ADV_MODE_IDLE, /**< Idle; no connectable advertising is ongoing.*/ + BLE_ADV_MODE_DIRECTED, /**< Directed advertising attempts to connect to the most recently disconnected peer. */ + BLE_ADV_MODE_DIRECTED_SLOW, /**< Directed advertising (low duty cycle) attempts to connect to the most recently disconnected peer. */ + BLE_ADV_MODE_FAST, /**< Fast advertising will connect to any peer device, or filter with a whitelist if one exists. */ + BLE_ADV_MODE_SLOW, /**< Slow advertising is similar to fast advertising. By default, it uses a longer advertising interval and time-out than fast advertising. However, these options are defined by the user. */ +} ble_adv_mode_t; + +/**@brief Advertising events. + * + * @details These events are propagated to the main application if a handler was provided during + * initialization of the Advertising Module. Events for modes that are not used can be + * ignored. Similarly, BLE_ADV_EVT_WHITELIST_REQUEST and BLE_ADV_EVT_PEER_ADDR_REQUEST + * can be ignored if whitelist and direct advertising is not used. + */ +typedef enum +{ + BLE_ADV_EVT_IDLE, /**< Idle; no connectable advertising is ongoing.*/ + BLE_ADV_EVT_DIRECTED, /**< Direct advertising mode has started. */ + BLE_ADV_EVT_DIRECTED_SLOW, /**< Directed advertising (low duty cycle) has started. */ + BLE_ADV_EVT_FAST, /**< Fast advertising mode has started. */ + BLE_ADV_EVT_SLOW, /**< Slow advertising mode has started.*/ + BLE_ADV_EVT_FAST_WHITELIST, /**< Fast advertising mode using the whitelist has started. */ + BLE_ADV_EVT_SLOW_WHITELIST, /**< Slow advertising mode using the whitelist has started.*/ + BLE_ADV_EVT_WHITELIST_REQUEST, /**< Request a whitelist from the main application. For whitelist advertising to work, the whitelist must be set when this event occurs. */ + BLE_ADV_EVT_PEER_ADDR_REQUEST /**< Request a peer address from the main application. For directed advertising to work, the peer address must be set when this event occurs. */ +} ble_adv_evt_t; + +/**@brief Options for the different advertisement modes. + * + * @details This structure is used to enable or disable advertising modes and to configure time-out + * periods and advertising intervals. + */ +typedef struct +{ + bool ble_adv_whitelist_enabled; /**< Enable or disable use of the whitelist. */ + bool ble_adv_directed_enabled; /**< Enable or disable direct advertising mode. */ + bool ble_adv_directed_slow_enabled; /**< Enable or disable direct advertising mode. */ + uint32_t ble_adv_directed_slow_interval; /**< Advertising interval for directed advertising. */ + uint32_t ble_adv_directed_slow_timeout; /**< Time-out (number of tries) for direct advertising. */ + bool ble_adv_fast_enabled; /**< Enable or disable fast advertising mode. */ + uint32_t ble_adv_fast_interval; /**< Advertising interval for fast advertising. */ + uint32_t ble_adv_fast_timeout; /**< Time-out (in seconds) for fast advertising. */ + bool ble_adv_slow_enabled; /**< Enable or disable slow advertising mode. */ + uint32_t ble_adv_slow_interval; /**< Advertising interval for slow advertising. */ + uint32_t ble_adv_slow_timeout; /**< Time-out (in seconds) for slow advertising. */ +}ble_adv_modes_config_t; + + +/**@brief BLE advertising event handler type. */ +typedef void (*ble_advertising_evt_handler_t) (ble_adv_evt_t const adv_evt); + +/**@brief BLE advertising error handler type. */ +typedef void (*ble_advertising_error_handler_t) (uint32_t nrf_error); + +/**@brief Initialization parameters for the Advertising Module. + * @details This structure is used to pass advertising options, advertising data, and an event handler to the Advertising Module during initialization. */ +typedef struct +{ + ble_adv_modes_config_t options; /**< Parameters for advertising modes.*/ + ble_advdata_t advdata; /**< Advertising data. */ + ble_advertising_evt_handler_t evt_handler; /**< Event handler. */ +}ble_adv_init_t; + + +/* Defines to make the mode options easier to set during advertising init.*/ +#define BLE_ADV_DIRECTED_ENABLED true +#define BLE_ADV_DIRECTED_DISABLED false + +#define BLE_ADV_DIRECTED_SLOW_ENABLED true +#define BLE_ADV_DIRECTED_SLOW_DISABLED false + +#define BLE_ADV_FAST_ENABLED true +#define BLE_ADV_FAST_DISABLED false + +#define BLE_ADV_SLOW_ENABLED true +#define BLE_ADV_SLOW_DISABLED false + +#define BLE_ADV_WHITELIST_ENABLED true +#define BLE_ADV_WHITELIST_DISABLED false + + +/**@brief Function for handling BLE events. + * + * @details This function must be called from the BLE stack event dispatcher for + * the module to handle BLE events that are relevant for the Advertising Module. + * + * @param[in] p_ble_evt BLE stack event. + */ +void ble_advertising_on_ble_evt(const ble_evt_t * const p_ble_evt); + + +/**@brief Function for handling system events. + * + * @details This function must be called to handle system events that are relevant + * for the Advertising Module. Specifically, the advertising module can not use the + * softdevice as long as there are pending writes to the flash memory. This + * event handler is designed to delay advertising until there is no flash operation. + * + * @param[in] sys_evt System event. + */ +void ble_advertising_on_sys_evt(uint32_t sys_evt); + + +/**@brief Function for initializing the Advertising Module. + * + * @details Encodes the required advertising data and passes it to the stack. + * Also builds a structure to be passed to the stack when starting advertising. + * The supplied advertising data is copied to a local structure and is manipulated + * depending on what advertising modes are started in @ref ble_advertising_start. + * + * @param[in] p_advdata Advertising data: name, appearance, discovery flags, and more. + * @param[in] p_srdata Scan response data: Supplement to advertising data. + * @param[in] p_config Select which advertising modes and intervals will be utilized. + * @param[in] evt_handler Event handler that will be called upon advertising events. + * @param[in] error_handler Error handler that will propogate internal errors to the main applications. + * + * @retval NRF_SUCCESS If initialization was successful. Otherwise, an error code is returned. + */ +uint32_t ble_advertising_init(ble_advdata_t const * p_advdata, + ble_advdata_t const * p_srdata, + ble_adv_modes_config_t const * p_config, + ble_advertising_evt_handler_t const evt_handler, + ble_advertising_error_handler_t const error_handler); + + +/**@brief Function for starting advertising. + * + * @details You can start advertising in any of the advertising modes that you enabled + * during initialization. + * + * @param[in] advertising_mode Advertising mode. + * + * @retval @ref NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval @ref NRF_ERROR_INVALID_STATE + */ +uint32_t ble_advertising_start(ble_adv_mode_t advertising_mode); + +/**@brief Function for setting the peer address. + * + * @details The peer address must be set by the application upon receiving a + * @ref BLE_ADV_EVT_PEER_ADDR_REQUEST event. Without the peer address, the directed + * advertising mode will not be run. + * + * @param[in] p_peer_addr Pointer to a peer address. + * + * @retval @ref NRF_SUCCESS Successfully stored the peer address pointer in the advertising module. + * @retval @ref NRF_ERROR_INVALID_STATE If a reply was not expected. + */ +uint32_t ble_advertising_peer_addr_reply(ble_gap_addr_t * p_peer_addr); + + +/**@brief Function for setting a whitelist. + * + * @details The whitelist must be set by the application upon receiving a + * @ref BLE_ADV_EVT_WHITELIST_REQUEST event. Without the whitelist, the whitelist + * advertising for fast and slow modes will not be run. + * + * @param[in] p_whitelist Pointer to a whitelist. + * + * @retval @ref NRF_SUCCESS Successfully stored pointers to the whitelist into the advertising module. + * @retval @ref NRF_ERROR_INVALID_STATE If a reply was not expected. + */ +uint32_t ble_advertising_whitelist_reply(ble_gap_whitelist_t * p_whitelist); + + +/**@brief Function for disabling whitelist advertising. + * + * @details This function temporarily disables whitelist advertising. + * Calling this function resets the current time-out countdown. + * + * @retval @ref NRF_SUCCESS On success, else an error message propogated from the Softdevice. + */ +uint32_t ble_advertising_restart_without_whitelist(void); +/** @} */ + +#endif // BLE_ADVERTISING_H__ + +/** @} */ diff --git a/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.c b/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.c new file mode 100644 index 0000000..635d211 --- /dev/null +++ b/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.c @@ -0,0 +1,649 @@ +/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#include "ble_dfu.h" +#include "ble_types.h" +#include "ble_gatts.h" +#include "ble_srv_common.h" +#include +#include "sdk_common.h" + +#define MAX_DFU_PKT_LEN 20 /**< Maximum length (in bytes) of the DFU Packet characteristic. */ +#define PKT_START_DFU_PARAM_LEN 2 /**< Length (in bytes) of the parameters for Packet Start DFU Request. */ +#define PKT_INIT_DFU_PARAM_LEN 2 /**< Length (in bytes) of the parameters for Packet Init DFU Request. */ +#define PKT_RCPT_NOTIF_REQ_LEN 3 /**< Length (in bytes) of the Packet Receipt Notification Request. */ +#define MAX_PKTS_RCPT_NOTIF_LEN 6 /**< Maximum length (in bytes) of the Packets Receipt Notification. */ +#define MAX_RESPONSE_LEN 7 /**< Maximum length (in bytes) of the response to a Control Point command. */ +#define MAX_NOTIF_BUFFER_LEN MAX(MAX_PKTS_RCPT_NOTIF_LEN, MAX_RESPONSE_LEN) /**< Maximum length (in bytes) of the buffer needed by DFU Service while sending notifications to peer. */ + +enum +{ + OP_CODE_START_DFU = 1, /**< Value of the Op code field for 'Start DFU' command.*/ + OP_CODE_RECEIVE_INIT = 2, /**< Value of the Op code field for 'Initialize DFU parameters' command.*/ + OP_CODE_RECEIVE_FW = 3, /**< Value of the Op code field for 'Receive firmware image' command.*/ + OP_CODE_VALIDATE = 4, /**< Value of the Op code field for 'Validate firmware' command.*/ + OP_CODE_ACTIVATE_N_RESET = 5, /**< Value of the Op code field for 'Activate & Reset' command.*/ + OP_CODE_SYS_RESET = 6, /**< Value of the Op code field for 'Reset System' command.*/ + OP_CODE_IMAGE_SIZE_REQ = 7, /**< Value of the Op code field for 'Report received image size' command.*/ + OP_CODE_PKT_RCPT_NOTIF_REQ = 8, /**< Value of the Op code field for 'Request packet receipt notification.*/ + OP_CODE_RESPONSE = 16, /**< Value of the Op code field for 'Response.*/ + OP_CODE_PKT_RCPT_NOTIF = 17 /**< Value of the Op code field for 'Packets Receipt Notification'.*/ +}; + +static bool m_is_dfu_service_initialized = false; /**< Variable to check if the DFU service was initialized by the application.*/ +static uint8_t m_notif_buffer[MAX_NOTIF_BUFFER_LEN]; /**< Buffer used for sending notifications to peer. */ + +/**@brief Function for adding DFU Packet characteristic to the BLE Stack. + * + * @param[in] p_dfu DFU Service structure. + * + * @return NRF_SUCCESS on success. Otherwise an error code. + */ +static uint32_t dfu_pkt_char_add(ble_dfu_t * const p_dfu) +{ + ble_gatts_char_md_t char_md; + ble_gatts_attr_t attr_char_value; + ble_uuid_t char_uuid; + ble_gatts_attr_md_t attr_md; + + memset(&char_md, 0, sizeof(char_md)); + + char_md.char_props.write_wo_resp = 1; + char_md.p_char_user_desc = NULL; + char_md.p_char_pf = NULL; + char_md.p_user_desc_md = NULL; + char_md.p_cccd_md = NULL; + char_md.p_sccd_md = NULL; + + char_uuid.type = p_dfu->uuid_type; + char_uuid.uuid = BLE_DFU_PKT_CHAR_UUID; + + memset(&attr_md, 0, sizeof(attr_md)); + + BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); + + attr_md.vloc = BLE_GATTS_VLOC_STACK; + attr_md.rd_auth = 0; + attr_md.wr_auth = 0; + attr_md.vlen = 1; + + memset(&attr_char_value, 0, sizeof(attr_char_value)); + + attr_char_value.p_uuid = &char_uuid; + attr_char_value.p_attr_md = &attr_md; + attr_char_value.init_len = 0; + attr_char_value.init_offs = 0; + attr_char_value.max_len = MAX_DFU_PKT_LEN; + attr_char_value.p_value = NULL; + + return sd_ble_gatts_characteristic_add(p_dfu->service_handle, + &char_md, + &attr_char_value, + &p_dfu->dfu_pkt_handles); +} + + +/**@brief Function for adding DFU Revision characteristic to the BLE Stack. + * + * @param[in] p_dfu DFU Service structure. + * + * @return NRF_SUCCESS on success. Otherwise an error code. + */ +static uint32_t dfu_rev_char_add(ble_dfu_t * const p_dfu, ble_dfu_init_t const * const p_dfu_init) +{ + ble_gatts_char_md_t char_md; + ble_gatts_attr_t attr_char_value; + ble_uuid_t char_uuid; + ble_gatts_attr_md_t attr_md; + + memset(&char_md, 0, sizeof(char_md)); + + char_md.char_props.read = 1; + char_md.p_char_user_desc = NULL; + char_md.p_char_pf = NULL; + char_md.p_user_desc_md = NULL; + char_md.p_cccd_md = NULL; + char_md.p_sccd_md = NULL; + + char_uuid.type = p_dfu->uuid_type; + char_uuid.uuid = BLE_DFU_REV_CHAR_UUID; + + memset(&attr_md, 0, sizeof(attr_md)); + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.write_perm); + + attr_md.vloc = BLE_GATTS_VLOC_STACK; + attr_md.rd_auth = 0; + attr_md.wr_auth = 0; + attr_md.vlen = 1; + + memset(&attr_char_value, 0, sizeof(attr_char_value)); + + attr_char_value.p_uuid = &char_uuid; + attr_char_value.p_attr_md = &attr_md; + attr_char_value.init_len = sizeof(uint16_t); + attr_char_value.init_offs = 0; + attr_char_value.max_len = sizeof(uint16_t); + attr_char_value.p_value = (uint8_t *)&p_dfu_init->revision; + + return sd_ble_gatts_characteristic_add(p_dfu->service_handle, + &char_md, + &attr_char_value, + &p_dfu->dfu_rev_handles); +} + + +/**@brief Function for adding DFU Control Point characteristic to the BLE Stack. + * + * @param[in] p_dfu DFU Service structure. + * + * @return NRF_SUCCESS on success. Otherwise an error code. + */ +static uint32_t dfu_ctrl_pt_add(ble_dfu_t * const p_dfu) +{ + ble_gatts_char_md_t char_md; + ble_gatts_attr_t attr_char_value; + ble_uuid_t char_uuid; + ble_gatts_attr_md_t attr_md; + + memset(&char_md, 0, sizeof(char_md)); + + char_md.char_props.write = 1; + char_md.char_props.notify = 1; + char_md.p_char_user_desc = NULL; + char_md.p_char_pf = NULL; + char_md.p_user_desc_md = NULL; + char_md.p_cccd_md = NULL; + char_md.p_sccd_md = NULL; + + char_uuid.type = p_dfu->uuid_type; + char_uuid.uuid = BLE_DFU_CTRL_PT_UUID; + + memset(&attr_md, 0, sizeof(attr_md)); + + BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); + + attr_md.vloc = BLE_GATTS_VLOC_STACK; + attr_md.rd_auth = 0; + attr_md.wr_auth = 1; + attr_md.vlen = 1; + + memset(&attr_char_value, 0, sizeof(attr_char_value)); + + attr_char_value.p_uuid = &char_uuid; + attr_char_value.p_attr_md = &attr_md; + attr_char_value.init_len = 0; + attr_char_value.init_offs = 0; + attr_char_value.max_len = BLE_L2CAP_MTU_DEF; + attr_char_value.p_value = NULL; + + return sd_ble_gatts_characteristic_add(p_dfu->service_handle, + &char_md, + &attr_char_value, + &p_dfu->dfu_ctrl_pt_handles); +} + + +/**@brief Function for handling the @ref BLE_GAP_EVT_CONNECTED event from the S110 SoftDevice. + * + * @param[in] p_dfu DFU Service Structure. + * @param[in] p_ble_evt Pointer to the event received from BLE stack. + */ +static void on_connect(ble_dfu_t * p_dfu, ble_evt_t * p_ble_evt) +{ + p_dfu->conn_handle = p_ble_evt->evt.gap_evt.conn_handle; +} + + +/**@brief Function for checking if the CCCD of DFU Control point is configured for Notification. + * + * @details This function checks if the CCCD of DFU Control Point characteristic is configured + * for Notification by the DFU Controller. + * + * @param[in] p_dfu DFU Service structure. + * + * @return True if the CCCD of DFU Control Point characteristic is configured for Notification. + * False otherwise. + */ +static bool is_cccd_configured(ble_dfu_t * p_dfu) +{ + // Check if the CCCDs are configured. + uint8_t cccd_val_buf[BLE_CCCD_VALUE_LEN]; + ble_gatts_value_t gatts_value; + + // Initialize value struct. + memset(&gatts_value, 0, sizeof(gatts_value)); + + gatts_value.len = BLE_CCCD_VALUE_LEN; + gatts_value.offset = 0; + gatts_value.p_value = cccd_val_buf; + + // Check the CCCD Value of DFU Control Point. + uint32_t err_code = sd_ble_gatts_value_get(p_dfu->conn_handle, + p_dfu->dfu_ctrl_pt_handles.cccd_handle, + &gatts_value); + if (err_code != NRF_SUCCESS) + { + if (p_dfu->error_handler != NULL) + { + p_dfu->error_handler(err_code); + } + return false; + } + + return ble_srv_is_notification_enabled(cccd_val_buf); +} + + +/**@brief Function for handling a Write event on the Control Point characteristic. + * + * @param[in] p_dfu DFU Service Structure. + * @param[in] p_ble_write_evt Pointer to the write event received from BLE stack. + * + * @return NRF_SUCCESS on successful processing of control point write. Otherwise an error code. + */ +static uint32_t on_ctrl_pt_write(ble_dfu_t * p_dfu, ble_gatts_evt_write_t * p_ble_write_evt) +{ + ble_gatts_rw_authorize_reply_params_t auth_reply; + + auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE; + auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE; + auth_reply.params.write.update = 1; + auth_reply.params.write.offset = p_ble_write_evt->offset; + auth_reply.params.write.len = p_ble_write_evt->len; + auth_reply.params.write.p_data = p_ble_write_evt->data; + + + if (!is_cccd_configured(p_dfu)) + { + // Send an error response to the peer indicating that the CCCD is improperly configured. + auth_reply.params.write.gatt_status = + BLE_GATT_STATUS_ATTERR_CPS_CCCD_CONFIG_ERROR; + + return (sd_ble_gatts_rw_authorize_reply(p_dfu->conn_handle, &auth_reply)); + + } + else + { + uint32_t err_code; + + auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS; + + err_code = (sd_ble_gatts_rw_authorize_reply(p_dfu->conn_handle, &auth_reply)); + VERIFY_SUCCESS(err_code); + } + + ble_dfu_evt_t ble_dfu_evt; + + switch (p_ble_write_evt->data[0]) + { + case OP_CODE_START_DFU: + ble_dfu_evt.ble_dfu_evt_type = BLE_DFU_START; + + if (p_ble_write_evt->len < PKT_START_DFU_PARAM_LEN) + { + return ble_dfu_response_send(p_dfu, + (ble_dfu_procedure_t) p_ble_write_evt->data[0], + BLE_DFU_RESP_VAL_OPER_FAILED); + } + + ble_dfu_evt.evt.ble_dfu_pkt_write.len = 1; + ble_dfu_evt.evt.ble_dfu_pkt_write.p_data = &(p_ble_write_evt->data[1]); + + p_dfu->evt_handler(p_dfu, &ble_dfu_evt); + break; + + case OP_CODE_RECEIVE_INIT: + ble_dfu_evt.ble_dfu_evt_type = BLE_DFU_RECEIVE_INIT_DATA; + + if (p_ble_write_evt->len < PKT_INIT_DFU_PARAM_LEN) + { + return ble_dfu_response_send(p_dfu, + (ble_dfu_procedure_t) p_ble_write_evt->data[0], + BLE_DFU_RESP_VAL_OPER_FAILED); + } + + ble_dfu_evt.evt.ble_dfu_pkt_write.len = 1; + ble_dfu_evt.evt.ble_dfu_pkt_write.p_data = &(p_ble_write_evt->data[1]); + + p_dfu->evt_handler(p_dfu, &ble_dfu_evt); + break; + + case OP_CODE_RECEIVE_FW: + ble_dfu_evt.ble_dfu_evt_type = BLE_DFU_RECEIVE_APP_DATA; + + p_dfu->evt_handler(p_dfu, &ble_dfu_evt); + break; + + case OP_CODE_VALIDATE: + ble_dfu_evt.ble_dfu_evt_type = BLE_DFU_VALIDATE; + + p_dfu->evt_handler(p_dfu, &ble_dfu_evt); + break; + + case OP_CODE_ACTIVATE_N_RESET: + ble_dfu_evt.ble_dfu_evt_type = BLE_DFU_ACTIVATE_N_RESET; + + p_dfu->evt_handler(p_dfu, &ble_dfu_evt); + break; + + case OP_CODE_SYS_RESET: + ble_dfu_evt.ble_dfu_evt_type = BLE_DFU_SYS_RESET; + + p_dfu->evt_handler(p_dfu, &ble_dfu_evt); + break; + + case OP_CODE_PKT_RCPT_NOTIF_REQ: + if (p_ble_write_evt->len < PKT_RCPT_NOTIF_REQ_LEN) + { + return (ble_dfu_response_send(p_dfu, + BLE_DFU_PKT_RCPT_REQ_PROCEDURE, + BLE_DFU_RESP_VAL_NOT_SUPPORTED)); + } + + ble_dfu_evt.evt.pkt_rcpt_notif_req.num_of_pkts = + uint16_decode(&(p_ble_write_evt->data[1])); + + if (ble_dfu_evt.evt.pkt_rcpt_notif_req.num_of_pkts == 0) + { + ble_dfu_evt.ble_dfu_evt_type = BLE_DFU_PKT_RCPT_NOTIF_DISABLED; + } + else + { + ble_dfu_evt.ble_dfu_evt_type = BLE_DFU_PKT_RCPT_NOTIF_ENABLED; + } + + p_dfu->evt_handler(p_dfu, &ble_dfu_evt); + + break; + + case OP_CODE_IMAGE_SIZE_REQ: + ble_dfu_evt.ble_dfu_evt_type = BLE_DFU_BYTES_RECEIVED_SEND; + + p_dfu->evt_handler(p_dfu, &ble_dfu_evt); + break; + + default: + // Unsupported op code. + return ble_dfu_response_send(p_dfu, + (ble_dfu_procedure_t) p_ble_write_evt->data[0], + BLE_DFU_RESP_VAL_NOT_SUPPORTED); + } + return NRF_SUCCESS; +} + + +/**@brief Function for handling the @ref BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST event from the S110 + * Stack. + * + * @param[in] p_dfu DFU Service Structure. + * @param[in] p_ble_evt Pointer to the event received from BLE stack. + */ +static void on_rw_authorize_req(ble_dfu_t * p_dfu, ble_evt_t * p_ble_evt) +{ + ble_gatts_evt_rw_authorize_request_t * p_authorize_request; + + p_authorize_request = &(p_ble_evt->evt.gatts_evt.params.authorize_request); + + if ( + (p_authorize_request->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE) + && + (p_authorize_request->request.write.handle == p_dfu->dfu_ctrl_pt_handles.value_handle) + && + (p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.op != BLE_GATTS_OP_PREP_WRITE_REQ) + && + (p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.op != BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) + && + (p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.op != BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL) + ) + { + uint32_t err_code; + + err_code = on_ctrl_pt_write(p_dfu, &(p_authorize_request->request.write)); + + if (err_code != NRF_SUCCESS && p_dfu->error_handler != NULL) + { + p_dfu->error_handler(err_code); + } + } +} + + +/**@brief Function for handling the @ref BLE_GATTS_EVT_WRITE event from the S110 SoftDevice. + * + * @param[in] p_dfu DFU Service Structure. + * @param[in] p_ble_evt Pointer to the event received from BLE stack. + */ +static void on_write(ble_dfu_t * p_dfu, ble_evt_t * p_ble_evt) +{ + if (p_ble_evt->evt.gatts_evt.params.write.handle == p_dfu->dfu_pkt_handles.value_handle) + { + // DFU Packet written + + ble_dfu_evt_t ble_dfu_evt; + + ble_dfu_evt.ble_dfu_evt_type = BLE_DFU_PACKET_WRITE; + ble_dfu_evt.evt.ble_dfu_pkt_write.len = p_ble_evt->evt.gatts_evt.params.write.len; + ble_dfu_evt.evt.ble_dfu_pkt_write.p_data = p_ble_evt->evt.gatts_evt.params.write.data; + + p_dfu->evt_handler(p_dfu, &ble_dfu_evt); + } +} + + +/**@brief Function for handling the BLE_GAP_EVT_DISCONNECTED event from the S110 SoftDevice. + * + * @param[in] p_dfu DFU Service Structure. + * @param[in] p_ble_evt Pointer to the event received from BLE stack. + */ +static void on_disconnect(ble_dfu_t * p_dfu, ble_evt_t * p_ble_evt) +{ + p_dfu->conn_handle = BLE_CONN_HANDLE_INVALID; +} + + +uint32_t ble_dfu_init(ble_dfu_t * p_dfu, ble_dfu_init_t * p_dfu_init) +{ + if ((p_dfu == NULL) || (p_dfu_init == NULL) || (p_dfu_init->evt_handler == NULL)) + { + return NRF_ERROR_NULL; + } + + p_dfu->conn_handle = BLE_CONN_HANDLE_INVALID; + + ble_uuid_t service_uuid; + uint32_t err_code; + + const ble_uuid128_t base_uuid128 = + { + { + 0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, + 0xDE, 0xEF, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00 + } + }; + + service_uuid.uuid = BLE_DFU_SERVICE_UUID; + + err_code = sd_ble_uuid_vs_add(&base_uuid128, &(service_uuid.type)); + VERIFY_SUCCESS(err_code); + + err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, + &service_uuid, + &(p_dfu->service_handle)); + VERIFY_SUCCESS(err_code); + + p_dfu->uuid_type = service_uuid.type; + + err_code = dfu_pkt_char_add(p_dfu); + VERIFY_SUCCESS(err_code); + + err_code = dfu_ctrl_pt_add(p_dfu); + VERIFY_SUCCESS(err_code); + + err_code = dfu_rev_char_add(p_dfu, p_dfu_init); + VERIFY_SUCCESS(err_code); + + p_dfu->evt_handler = p_dfu_init->evt_handler; + + if (p_dfu_init->error_handler != NULL) + { + p_dfu->error_handler = p_dfu_init->error_handler; + } + + m_is_dfu_service_initialized = true; + + return NRF_SUCCESS; +} + + +void ble_dfu_on_ble_evt(ble_dfu_t * p_dfu, ble_evt_t * p_ble_evt) +{ + if ((p_dfu == NULL) || (p_ble_evt == NULL)) + { + return; + } + + if (p_dfu->evt_handler != NULL) + { + switch (p_ble_evt->header.evt_id) + { + case BLE_GAP_EVT_CONNECTED: + on_connect(p_dfu, p_ble_evt); + break; + + case BLE_GATTS_EVT_WRITE: + on_write(p_dfu, p_ble_evt); + break; + + case BLE_GAP_EVT_DISCONNECTED: + on_disconnect(p_dfu, p_ble_evt); + break; + + case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST: + on_rw_authorize_req(p_dfu, p_ble_evt); + break; + + default: + // No implementation needed. + break; + } + } +} + + +uint32_t ble_dfu_bytes_rcvd_report(ble_dfu_t * p_dfu, uint32_t num_of_firmware_bytes_rcvd) +{ + if (p_dfu == NULL) + { + return NRF_ERROR_NULL; + } + + if ((p_dfu->conn_handle == BLE_CONN_HANDLE_INVALID) || !m_is_dfu_service_initialized) + { + return NRF_ERROR_INVALID_STATE; + } + + ble_gatts_hvx_params_t hvx_params; + uint16_t index = 0; + + // Encode the Op Code. + m_notif_buffer[index++] = OP_CODE_RESPONSE; + + // Encode the Reqest Op Code. + m_notif_buffer[index++] = OP_CODE_IMAGE_SIZE_REQ; + + // Encode the Response Value. + m_notif_buffer[index++] = (uint8_t)BLE_DFU_RESP_VAL_SUCCESS; + + index += uint32_encode(num_of_firmware_bytes_rcvd, &m_notif_buffer[index]); + + memset(&hvx_params, 0, sizeof(hvx_params)); + + hvx_params.handle = p_dfu->dfu_ctrl_pt_handles.value_handle; + hvx_params.type = BLE_GATT_HVX_NOTIFICATION; + hvx_params.offset = 0; + hvx_params.p_len = &index; + hvx_params.p_data = m_notif_buffer; + + return sd_ble_gatts_hvx(p_dfu->conn_handle, &hvx_params); +} + + +uint32_t ble_dfu_pkts_rcpt_notify(ble_dfu_t * p_dfu, uint32_t num_of_firmware_bytes_rcvd) +{ + if (p_dfu == NULL) + { + return NRF_ERROR_NULL; + } + + if ((p_dfu->conn_handle == BLE_CONN_HANDLE_INVALID) || !m_is_dfu_service_initialized) + { + return NRF_ERROR_INVALID_STATE; + } + + ble_gatts_hvx_params_t hvx_params; + uint16_t index = 0; + + m_notif_buffer[index++] = OP_CODE_PKT_RCPT_NOTIF; + + index += uint32_encode(num_of_firmware_bytes_rcvd, &m_notif_buffer[index]); + + memset(&hvx_params, 0, sizeof(hvx_params)); + + hvx_params.handle = p_dfu->dfu_ctrl_pt_handles.value_handle; + hvx_params.type = BLE_GATT_HVX_NOTIFICATION; + hvx_params.offset = 0; + hvx_params.p_len = &index; + hvx_params.p_data = m_notif_buffer; + + return sd_ble_gatts_hvx(p_dfu->conn_handle, &hvx_params); +} + + +uint32_t ble_dfu_response_send(ble_dfu_t * p_dfu, + ble_dfu_procedure_t dfu_proc, + ble_dfu_resp_val_t resp_val) +{ + if (p_dfu == NULL) + { + return NRF_ERROR_NULL; + } + + if ((p_dfu->conn_handle == BLE_CONN_HANDLE_INVALID) || !m_is_dfu_service_initialized) + { + return NRF_ERROR_INVALID_STATE; + } + + ble_gatts_hvx_params_t hvx_params; + uint16_t index = 0; + + m_notif_buffer[index++] = OP_CODE_RESPONSE; + + // Encode the Request Op code + m_notif_buffer[index++] = (uint8_t)dfu_proc; + + // Encode the Response Value. + m_notif_buffer[index++] = (uint8_t)resp_val; + + memset(&hvx_params, 0, sizeof(hvx_params)); + + hvx_params.handle = p_dfu->dfu_ctrl_pt_handles.value_handle; + hvx_params.type = BLE_GATT_HVX_NOTIFICATION; + hvx_params.offset = 0; + hvx_params.p_len = &index; + hvx_params.p_data = m_notif_buffer; + + return sd_ble_gatts_hvx(p_dfu->conn_handle, &hvx_params); +} diff --git a/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.h b/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.h new file mode 100644 index 0000000..906febc --- /dev/null +++ b/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.h @@ -0,0 +1,239 @@ +/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +/**@file + * + * @defgroup ble_sdk_srv_dfu Device Firmware Update Service + * @{ + * @ingroup ble_sdk_srv + * @brief Device Firmware Update Service + * + * @details The Device Firmware Update (DFU) service is a GATT based service that can be used for + * performing firmware updates over BLE. Note that this implementation uses vendor + * specific UUIDs for service and characteristics and is intended to demonstrate the + * firmware updates over BLE. Refer @ref bledfu_transport_bleservice and @ref + * bledfu_transport_bleprofile for more information on the service and profile respectively. + */ + +#ifndef BLE_DFU_H__ +#define BLE_DFU_H__ + +#include +#include "ble_gatts.h" +#include "ble_gap.h" +#include "ble.h" +#include "ble_srv_common.h" + +#define BLE_DFU_SERVICE_UUID 0x1530 /**< The UUID of the DFU Service. */ +#define BLE_DFU_PKT_CHAR_UUID 0x1532 /**< The UUID of the DFU Packet Characteristic. */ +#define BLE_DFU_CTRL_PT_UUID 0x1531 /**< The UUID of the DFU Control Point. */ +#define BLE_DFU_STATUS_REP_UUID 0x1533 /**< The UUID of the DFU Status Report Characteristic. */ +#define BLE_DFU_REV_CHAR_UUID 0x1534 /**< The UUID of the DFU Revision Characteristic. */ + +/**@brief DFU Event type. + * + * @details This enumeration contains the types of events that will be received from the DFU Service. + */ +typedef enum +{ + BLE_DFU_START, /**< The event indicating that the peer wants the application to prepare for a new firmware update. */ + BLE_DFU_RECEIVE_INIT_DATA, /**< The event indicating that the peer wants the application to prepare to receive init parameters. */ + BLE_DFU_RECEIVE_APP_DATA, /**< The event indicating that the peer wants the application to prepare to receive the new firmware image. */ + BLE_DFU_VALIDATE, /**< The event indicating that the peer wants the application to validate the newly received firmware image. */ + BLE_DFU_ACTIVATE_N_RESET, /**< The event indicating that the peer wants the application to undergo activate new firmware and restart with new valid application */ + BLE_DFU_SYS_RESET, /**< The event indicating that the peer wants the application to undergo a reset and start the currently valid application image.*/ + BLE_DFU_PKT_RCPT_NOTIF_ENABLED, /**< The event indicating that the peer has enabled packet receipt notifications. It is the responsibility of the application to call @ref ble_dfu_pkts_rcpt_notify each time the number of packets indicated by num_of_pkts field in @ref ble_dfu_evt_t is received.*/ + BLE_DFU_PKT_RCPT_NOTIF_DISABLED, /**< The event indicating that the peer has disabled the packet receipt notifications.*/ + BLE_DFU_PACKET_WRITE, /**< The event indicating that the peer has written a value to the 'DFU Packet' characteristic. The data received from the peer will be present in the @ref BLE_DFU_PACKET_WRITE element contained within @ref ble_dfu_evt_t.*/ + BLE_DFU_BYTES_RECEIVED_SEND /**< The event indicating that the peer is requesting for the number of bytes of firmware data last received by the application. It is the responsibility of the application to call @ref ble_dfu_pkts_rcpt_notify in response to this event. */ +} ble_dfu_evt_type_t; + +/**@brief DFU Procedure type. + * + * @details This enumeration contains the types of DFU procedures. + */ +typedef enum +{ + BLE_DFU_START_PROCEDURE = 1, /**< DFU Start procedure.*/ + BLE_DFU_INIT_PROCEDURE = 2, /**< DFU Initialization procedure.*/ + BLE_DFU_RECEIVE_APP_PROCEDURE = 3, /**< Firmware receiving procedure.*/ + BLE_DFU_VALIDATE_PROCEDURE = 4, /**< Firmware image validation procedure .*/ + BLE_DFU_PKT_RCPT_REQ_PROCEDURE = 8 /**< Packet receipt notification request procedure. */ +} ble_dfu_procedure_t; + +/**@brief DFU Response value type. + */ +typedef enum +{ + BLE_DFU_RESP_VAL_SUCCESS = 1, /**< Success.*/ + BLE_DFU_RESP_VAL_INVALID_STATE, /**< Invalid state.*/ + BLE_DFU_RESP_VAL_NOT_SUPPORTED, /**< Operation not supported.*/ + BLE_DFU_RESP_VAL_DATA_SIZE, /**< Data size exceeds limit.*/ + BLE_DFU_RESP_VAL_CRC_ERROR, /**< CRC Error.*/ + BLE_DFU_RESP_VAL_OPER_FAILED /**< Operation failed.*/ +} ble_dfu_resp_val_t; + + +/**@brief DFU Packet structure. + * + * @details This structure contains the value of the DFU Packet characteristic as written by the + * peer and the length of the value written. It will be filled by the DFU Service when the + * peer writes to the DFU Packet characteristic. + */ +typedef struct +{ + uint8_t * p_data; /**< Pointer to the received packet. This will point to a word aligned memory location.*/ + uint8_t len; /**< Length of the packet received. */ +} ble_dfu_pkt_write_t; + +/**@brief Packet receipt notification request structure. + * + * @details This structure contains the contents of the packet receipt notification request + * sent by the DFU Controller. + */ +typedef struct +{ + uint16_t num_of_pkts; /**< The number of packets of firmware data to be received by application before sending the next Packet Receipt Notification to the peer. */ +} ble_pkt_rcpt_notif_req_t; + +/**@brief DFU Event structure. + * + * @details This structure contains the event generated by the DFU Service based on the data + * received from the peer. + */ +typedef struct +{ + ble_dfu_evt_type_t ble_dfu_evt_type; /**< Type of the event.*/ + union + { + ble_dfu_pkt_write_t ble_dfu_pkt_write; /**< The DFU packet received. This field is when the @ref ble_dfu_evt_type field is set to @ref BLE_DFU_PACKET_WRITE.*/ + ble_pkt_rcpt_notif_req_t pkt_rcpt_notif_req; /**< Packet receipt notification request. This field is when the @ref ble_dfu_evt_type field is set to @ref BLE_DFU_PKT_RCPT_NOTIF_ENABLED.*/ + } evt; +} ble_dfu_evt_t; + +// Forward declaration of the ble_dfu_t type. +typedef struct ble_dfu_s ble_dfu_t; + +/**@brief DFU Service event handler type. */ +typedef void (*ble_dfu_evt_handler_t) (ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt); + +/**@brief DFU service structure. + * + * @details This structure contains status information related to the service. + */ +struct ble_dfu_s +{ + uint16_t conn_handle; /**< Handle of the current connection (as provided by the SoftDevice). This will be BLE_CONN_HANDLE_INVALID when not in a connection. */ + uint16_t revision; /**< Handle of DFU Service (as provided by the SoftDevice). */ + uint16_t service_handle; /**< Handle of DFU Service (as provided by the SoftDevice). */ + uint8_t uuid_type; /**< UUID type assigned for DFU Service by the SoftDevice. */ + ble_gatts_char_handles_t dfu_pkt_handles; /**< Handles related to the DFU Packet characteristic. */ + ble_gatts_char_handles_t dfu_ctrl_pt_handles; /**< Handles related to the DFU Control Point characteristic. */ + ble_gatts_char_handles_t dfu_status_rep_handles; /**< Handles related to the DFU Status Report characteristic. */ + ble_gatts_char_handles_t dfu_rev_handles; /**< Handles related to the DFU Revision characteristic. */ + ble_dfu_evt_handler_t evt_handler; /**< The event handler to be called when an event is to be sent to the application.*/ + ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */ +}; + +/**@brief DFU service initialization structure. + * + * @details This structure contains the initialization information for the DFU Service. The + * application needs to fill this structure and pass it to the DFU Service using the + * @ref ble_dfu_init function. + */ +typedef struct +{ + uint16_t revision; /**< Revision number to be exposed by the DFU service. */ + ble_dfu_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Device Firmware Update Service. */ + ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */ +} ble_dfu_init_t; + +/**@brief Function for handling a BLE event. + * + * @details The DFU service expects the application to call this function each time an event + * is received from the SoftDevice. This function processes the event, if it is + * relevant for the DFU service and calls the DFU event handler of the application if + * necessary. + * + * @param[in] p_dfu Pointer to the DFU service structure. + * @param[in] p_ble_evt Pointer to the event received from SoftDevice. + */ +void ble_dfu_on_ble_evt(ble_dfu_t * p_dfu, ble_evt_t * p_ble_evt); + +/**@brief Function for initializing the DFU service. + * + * @param[out] p_dfu Device Firmware Update service structure. This structure will have to be + * supplied by the application. It will be initialized by this function, + * and will later be used to identify the service instance. + * @param[in] p_dfu_init Information needed to initialize the service. + * + * @return NRF_SUCCESS if the DFU service and its characteristics were successfully added to the + * SoftDevice. Otherwise an error code. + * This function returns NRF_ERROR_NULL if the value of evt_handler in p_dfu_init + * structure provided is NULL or if the pointers supplied as input are NULL. + */ +uint32_t ble_dfu_init(ble_dfu_t * p_dfu, ble_dfu_init_t * p_dfu_init); + +/**@brief Function for sending response to a control point command. + * + * @details This function will encode a DFU Control Point response using the given input + * parameters and will send a notification of the same to the peer. + * + * @param[in] p_dfu Pointer to the DFU service structure. + * @param[in] dfu_proc Procedure for which this response is to be sent. + * @param[in] resp_val Response value. + * + * @return NRF_SUCCESS if the DFU Service has successfully requested the SoftDevice to + * send the notification. Otherwise an error code. + * This function returns NRF_ERROR_INVALID_STATE if the device is not connected to a + * peer or if the DFU service is not initialized or if the notification of the DFU + * Status Report characteristic was not enabled by the peer. It returns NRF_ERROR_NULL + * if the pointer p_dfu is NULL. + */ +uint32_t ble_dfu_response_send(ble_dfu_t * p_dfu, + ble_dfu_procedure_t dfu_proc, + ble_dfu_resp_val_t resp_val); + +/**@brief Function for notifying the peer about the number of bytes of firmware data received. + * + * @param[in] p_dfu Pointer to the DFU service structure. + * @param[in] num_of_firmware_bytes_rcvd Number of bytes. + * + * @return NRF_SUCCESS if the DFU Service has successfully requested the SoftDevice to send + * the notification. Otherwise an error code. + * This function returns NRF_ERROR_INVALID_STATE if the device is not connected to a + * peer or if the DFU service is not initialized or if the notification of the DFU + * Status Report characteristic was not enabled by the peer. It returns NRF_ERROR_NULL + * if the pointer p_dfu is NULL. + */ +uint32_t ble_dfu_bytes_rcvd_report(ble_dfu_t * p_dfu, uint32_t num_of_firmware_bytes_rcvd); + +/**@brief Function for sending Packet Receipt Notification to the peer. + * + * This function will encode the number of bytes received as input parameter into a + * notification of the control point characteristic and send it to the peer. + * + * @param[in] p_dfu Pointer to the DFU service structure. + * @param[in] num_of_firmware_bytes_rcvd Number of bytes of firmware image received. + * + * @return NRF_SUCCESS if the DFU Service has successfully requested the SoftDevice to send + * the notification. Otherwise an error code. + * This function returns NRF_ERROR_INVALID_STATE if the device is not connected to a + * peer or if the DFU service is not initialized or if the notification of the DFU + * Status Report characteristic was not enabled by the peer. It returns NRF_ERROR_NULL + * if the pointer p_dfu is NULL. + */ +uint32_t ble_dfu_pkts_rcpt_notify(ble_dfu_t * p_dfu, uint32_t num_of_firmware_bytes_rcvd); + +#endif // BLE_DFU_H__ + +/** @} */ diff --git a/cores/arduino/components/ble/device_manager/config/device_manager_cnfg.h b/cores/arduino/components/ble/device_manager/config/device_manager_cnfg.h new file mode 100644 index 0000000..03d4300 --- /dev/null +++ b/cores/arduino/components/ble/device_manager/config/device_manager_cnfg.h @@ -0,0 +1,98 @@ +/* Copyright (C) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + + /** + * @file device_manager_cnfg.h + * + * @cond + * @defgroup device_manager_cnfg Device Manager Configuration + * @ingroup device_manager + * @{ + * + * @brief Defines application specific configuration for Device Manager. + * + * @details All configurations that are specific to application have been defined + * here. Application should configuration that best suits its requirements. + */ + +#ifndef DEVICE_MANAGER_CNFG_H__ +#define DEVICE_MANAGER_CNFG_H__ + +/** + * @defgroup device_manager_inst Device Manager Instances + * @{ + */ +/** + * @brief Maximum applications that Device Manager can support. + * + * @details Maximum application that the Device Manager can support. + * Currently only one application can be supported. + * Minimum value : 1 + * Maximum value : 1 + * Dependencies : None. + */ +#define DEVICE_MANAGER_MAX_APPLICATIONS 1 + +/** + * @brief Maximum connections that Device Manager should simultaneously manage. + * + * @details Maximum connections that Device Manager should simultaneously manage. + * Minimum value : 1 + * Maximum value : Maximum links supported by SoftDevice. + * Dependencies : None. + */ +#define DEVICE_MANAGER_MAX_CONNECTIONS 1 + + +/** + * @brief Maximum bonds that Device Manager should manage. + * + * @details Maximum bonds that Device Manager should manage. + * Minimum value : 1 + * Maximum value : 254. + * Dependencies : None. + * @note In case of GAP Peripheral role, the Device Manager will accept bonding procedure + * requests from peers even if this limit is reached, but bonding information will not + * be stored. In such cases, application will be notified with DM_DEVICE_CONTEXT_FULL + * as event result at the completion of the security procedure. + */ +#define DEVICE_MANAGER_MAX_BONDS 7 + + +/** + * @brief Maximum Characteristic Client Descriptors used for GATT Server. + * + * @details Maximum Characteristic Client Descriptors used for GATT Server. + * Minimum value : 1 + * Maximum value : 254. + * Dependencies : None. + */ +#define DM_GATT_CCCD_COUNT 4 + + +/** + * @brief Size of application context. + * + * @details Size of application context that Device Manager should manage for each bonded device. + * Size had to be a multiple of word size. + * Minimum value : 4. + * Maximum value : 256. + * Dependencies : Needed only if Application Context saving is used by the application. + * @note If set to zero, its an indication that application context is not required to be managed + * by the module. + */ +#define DEVICE_MANAGER_APP_CONTEXT_SIZE 16 + +/* @} */ +/* @} */ +/** @endcond */ +#endif // DEVICE_MANAGER_CNFG_H__ + diff --git a/cores/arduino/components/ble/device_manager/device_manager.h b/cores/arduino/components/ble/device_manager/device_manager.h new file mode 100644 index 0000000..9aa465d --- /dev/null +++ b/cores/arduino/components/ble/device_manager/device_manager.h @@ -0,0 +1,888 @@ +/* Copyright (C) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +/** + * @file device_manager.h + * + * @defgroup device_manager Device Manager + * @ingroup ble_sdk_lib + * @{ + * @brief Device Manager Application Interface Abstraction. + * + * @details The Device Manager module manages Active and Bonded Peers. Management of peer includes + * book keeping of contextual information like the Security Keys, GATT + * configuration and any application specific information. + * + * Active Peers are devices which are connected, and may or may not be bonded. + * Bonded Peers are devices which are bonded, and may or may not be Active (Connected). + * Active Bonded Peer refers to a device which is connected and bonded. + * + * Paired Devices refers to peer devices that are connected and have necessary context + * establishment/exchange for the current connection session. On disconnect, + * all contextual information is flushed. For example, SMP Information Exchanged during + * pairing and GATT Configuration is not retained on disconnection. + * + * Note that this module allows management of contextual information but + * does not provide an interface for connection management. Therefore, entering connectible + * mode, connection establishment, or disconnection of a link with peer is not in scope + * of this module. + * + * For bonded peers, the contextual information is required to be retained on disconnection + * and power cycling. Persistent storage of contextual information is handled by the + * module. This module categorizes the contextual information into 3 categories: + * - Bonding Information + * Bond information is the information exchanged between local and peer device to + * establish a bond. It also includes peer identification information, + * like the peer address or the IRK or both. From here on this category of information + * is referred to as Device Context. + * - Service/Protocol Information + * Service/Protocol information is the information retained for the peer to save on one-time + * procedures like the GATT Service Discovery procedures and Service Configurations. + * It allows devices to resume data exchange on subsequent reconnection without having + * to perform initial set-up procedures each time. From here on this category is + * referred to as Service Context. + * - Application Information + * Application information is the context that the application would like to associate with + * each of the bonded device. For example, if the application chooses to rank its peers + * in order to manage them better, the rank information could be treated as + * Application Information. This storage space is provided to save the application from + * maintaining a mapping table with each Device Instance and Application Information. + * However, if the application have no use for this, it is possible to not + * use or employ this at compile time. From here on this category of information is + * referred to as Application Context. + */ + + +#ifndef DEVICE_MANAGER_H__ +#define DEVICE_MANAGER_H__ + +#include +#include +#include "sdk_common.h" +#include "ble.h" +#include "ble_gap.h" +#include "device_manager_cnfg.h" + +/** + * @defgroup dm_service_cntext_types Service/Protocol Types + * + * @brief Describes the possible types of Service/Protocol Contexts for a bonded/peer device. + * + * @details Possible Service/Protocol context per peer device. The Device Manager provides the + * functionality of persistently storing the Service/Protocol context and can automatically + * load them when needed. + * For example system attributes for a GATT Server. Based on the nature of the application, + * not all service types may be needed. The application can specify + * only the service/protocol context it wants to use at the time of registration. + * @{ + */ +#define DM_PROTOCOL_CNTXT_NONE 0x00 /**< No Service Context, this implies the application does not want to associate any service/protocol context with the peer device */ +#define DM_PROTOCOL_CNTXT_GATT_SRVR_ID 0x01 /**< GATT Server Service Context, this implies the application does associate GATT Server with the peer device and this information will be loaded when needed for a bonded device */ +#define DM_PROTOCOL_CNTXT_GATT_CLI_ID 0x02 /**< GATT Client Service Context, this implies the application does associate GATT Client with the peer device and this information will be loaded when needed for a bonded device */ +#define DM_PROTOCOL_CNTXT_ALL \ + (DM_PROTOCOL_CNTXT_GATT_SRVR_ID | DM_PROTOCOL_CNTXT_GATT_CLI_ID) /**< All Service/Protocol Context, this implies that the application wants to associate all Service/Protocol Information with the bonded device. This is configurable based on system requirements. If the application has only one type of service, this define could be altered to reflect the same. */ +/** @} */ + + +/** + * @defgroup dm_events Device Manager Events + * + * @brief This section describes the device manager events that are notified to the application. + * + * @details The Device Manager notifies the application of various asynchronous events using the + * asynchronous event notification callback. All events has been categorized into: + * a. General. + * b. Link Status. + * c. Context Management. + * + * In the callback, these events are notified along with handle that uniquely identifies: + * application instance, active instance (if applicable), device instance + * bonding instance, (if applicable) and service instance. + * Not all events are pertaining to an active connection, for example a context deletion event could occur even if the peer + * is not connected. Also, general category of events may not be pertaining to any specific peer. + * See also \ref dm_event_cb_t and \ref dm_register. + * @{ + */ +/** + * @defgroup general_events General Events + * + * @brief General or miscellaneous events. + * + * @details This category of events are general events not pertaining to a peer or context. + * + * @{ + */ +#define DM_EVT_RFU 0x00 /**< Reserved for future use, is never notified. */ +#define DM_EVT_ERROR 0x01 /**< Device Manager Event Error. */ +/** @} */ + +/** + * @defgroup link_status_events Link Status Events + * + * @brief Link Status Events. + * + * @details This category of events notify the application of the link status. Event result associated + * with the event is provided along with the event in the callback to provide more details of + * whether a procedure succeeded or failed and assist the application in decision making of + * how to proceed. For example if a DM_DEVICE_CONNECT_IND is indicated with NRF_SUCCESS + * result, the application may want to proceed with discovering and association with + * service of the peer. However, if indicated with a failure result, the application may + * want to take an alternate action such as reattempting to connect or go into a + * sleep mode. + * + * @{ + */ +#define DM_EVT_CONNECTION 0x11 /**< Indicates that link with the peer is established. */ +#define DM_EVT_DISCONNECTION 0x12 /**< Indicates that link with peer is torn down. */ +#define DM_EVT_SECURITY_SETUP 0x13 /**< Security procedure for link started indication */ +#define DM_EVT_SECURITY_SETUP_COMPLETE 0x14 /**< Security procedure for link completion indication. */ +#define DM_EVT_LINK_SECURED 0x15 /**< Indicates that link with the peer is secured. For bonded devices, subsequent reconnections with bonded peer will result only in this event when the link is secured and setup procedures will not occur unless the bonding information is either lost or deleted on either or both sides. */ +#define DM_EVT_SECURITY_SETUP_REFRESH 0x16 /**< Indicates that the security on the link was re-established. */ +/** @} */ + +/** + * @defgroup context_mgmt_events Context Management Events + * + * @brief Context Management Events. + * + * @details These events notify the application of the status of context loading and storing. + * + * @{ + */ +#define DM_EVT_DEVICE_CONTEXT_LOADED 0x21 /**< Indicates that device context for a peer is loaded. */ +#define DM_EVT_DEVICE_CONTEXT_STORED 0x22 /**< Indicates that device context is stored persistently. */ +#define DM_EVT_DEVICE_CONTEXT_DELETED 0x23 /**< Indicates that device context is deleted. */ +#define DM_EVT_SERVICE_CONTEXT_LOADED 0x31 /**< Indicates that service context for a peer is loaded. */ +#define DM_EVT_SERVICE_CONTEXT_STORED 0x32 /**< Indicates that service context is stored persistently. */ +#define DM_EVT_SERVICE_CONTEXT_DELETED 0x33 /**< Indicates that service context is deleted. */ +#define DM_EVT_APPL_CONTEXT_LOADED 0x41 /**< Indicates that application context for a peer is loaded. */ +#define DM_EVT_APPL_CONTEXT_STORED 0x42 /**< Indicates that application context is stored persistently. */ +#define DM_EVT_APPL_CONTEXT_DELETED 0x43 /**< Indicates that application context is deleted. */ +/** @} */ +/** @} */ + +#define DM_INVALID_ID 0xFF /**< Invalid instance idenitifer. */ + +/** + * @defgroup dm_data_structure Device Manager Data Types + * + * @brief This section describes all the data types exposed by the module to the application. + * @{ + */ + +/** + * @brief Application Instance. + * + * @details Application instance uniquely identifies an application. The identifier is allocated by + * the device manager when application registers with the module. The application is + * expected to identify itself with this instance identifier when initiating subsequent + * requests. Application should use the utility API \ref dm_application_instance_set in + * order to set its application instance in dm_handle_t needed for all subsequent APIs. + * See also \ref dm_register. + */ +typedef uint8_t dm_application_instance_t; + +/** + * @brief Connection Instance. + * + * @details Identifies connection instance for an active device. This instance is allocated by the + * device manager when a connection is established and is notified with DM_EVT_CONNECTION + * with the event result NRF_SUCCESS. + */ +typedef uint8_t dm_connection_instance_t; + +/** + * @brief Device Instance. + * + * @details Uniquely identifies a bonded peer device. The peer device may or may not be connected. + * In case of the central: The bonded device instance to identify the peer is allocated when bonding procedure is initiated by the central using dm_security_setup_req. + * In case of the peripheral: When the bonding procedure is successful, the DM_EVT_SECURITY_SETUP_COMPLETE event with success event result, is received. + * In case the module cannot add more bonded devices, no instance is allocated, this is indicated by an appropriate error code for the API/event as the case may be. Application can choose to disconnect the link. + */ +typedef uint8_t dm_device_instance_t; + +/** + * @brief Service Instance. + * + * @details Uniquely identifies a peer device. The peer device may or may not be connected. This + * instance is allocated by the device manager when a device is bonded and is notified + * when security procedures have been initiated. + * Security Procedures initiation is notified with DM_SECURITY_SETUP_IND with + * success event result. In case the event result indicates that the module cannot add more + * bonded devices, no instance is allocated. Application can chose to disconnect the link. + */ +typedef uint8_t dm_service_instance_t; + +/** + * @brief Service/Protocol Type Identifier. + * + * @details Uniquely identifies a service or a protocol type. Service/Protocol Type identification + * is needed as each service/protocol can have its own contextual data. + * This allows the peer to access more than one service at a time. \ref dm_service_cntext_types describes the + * list of services/protocols supported. + */ +typedef uint8_t service_type_t; + +/**@brief Device Manager Master identification and encryption information. */ +typedef struct dm_enc_key +{ + ble_gap_enc_info_t enc_info; /**< GAP encryption information. */ + ble_gap_master_id_t master_id; /**< Master identification. */ +} dm_enc_key_t; + +/** @brief Device Manager identity and address information. */ +typedef struct dm_id_key +{ + ble_gap_irk_t id_info; /**< Identity information. */ + ble_gap_addr_t id_addr_info; /**< Identity address information. */ +} dm_id_key_t; + +/** @brief Device Manager signing information. */ +typedef struct dm_sign_key +{ + ble_gap_sign_info_t sign_key; /**< GAP signing information. */ +} dm_sign_key_t; + +/** @brief Security keys. */ +typedef struct dm_sec_keyset +{ + union + { + dm_enc_key_t * p_enc_key; /**< Pointer to Device Manager encryption information structure. */ + } enc_key; + dm_id_key_t * p_id_key; /**< Identity key, or NULL. */ + dm_sign_key_t * p_sign_key; /**< Signing key, or NULL. */ +} dm_sec_keys_t; + +/** @brief Device Manager security key set. */ +typedef struct +{ + dm_sec_keys_t keys_periph; /**< Keys distributed by the device in the Peripheral role. */ + dm_sec_keys_t keys_central; /**< Keys distributed by the device in the Central role. */ +} dm_sec_keyset_t; + +/** + * @brief Device Handle used for unique identification of each peer. + * + * @details This data type is used to uniquely identify each peer device. A peer device could be + * active and/or bonded. Therefore an instance for active and bonded is provided. + * However, the application is expected to treat this is an opaque structure and use this for + * all API interactions once stored on appropriate events. + * See \ref dm_events. + */ +typedef struct device_handle +{ + dm_application_instance_t appl_id; /**< Identifies the application instances for the device that is being managed. */ + dm_connection_instance_t connection_id; /**< Identifies the active connection instance. */ + dm_device_instance_t device_id; /**< Identifies peer instance in the data base. */ + dm_service_instance_t service_id; /**< Service instance identifier. */ +} dm_handle_t; + +/** + * @brief Definition of Data Context. + * + * @details Defines contextual data format, it consists of context data length and pointer to data. + */ +typedef struct +{ + uint32_t flags; /**< Additional flags identifying data. */ + uint32_t len; /**< Length of data. */ + uint8_t * p_data; /**< Pointer to contextual data, a copy is made of the data. */ +} dm_context_t; + + +/** + * @brief Device Context. + * + * @details Defines "device context" type for a device managed by device manager. + */ +typedef dm_context_t dm_device_context_t; + +/** + * @brief Service Context. + * + * @details Service context data for a service identified by the 'service_type' field. + */ +typedef struct +{ + service_type_t service_type; /**< Identifies the service/protocol to which the context data is related. */ + dm_context_t context_data; /**< Contains length and pointer to context data */ +} dm_service_context_t; + +/** + * @brief Application context. + * + * @details The application context can be used by the application to map any application level + * information that is to be mapped with a particular peer. + * For bonded peers, this information will be stored by the bond manager persistently. + * Note that the device manager treats this information as an + * opaque block of bytes. + * Necessary APIs to get and set this context for a peer have been provided. + */ +typedef dm_context_t dm_application_context_t; + +/** + * @brief Event parameters. + * + * @details Defines event parameters for each of the events notified by the module. + */ +typedef union +{ + ble_gap_evt_t * p_gap_param; /**< All events that are triggered in device manager as a result of GAP events, like connection, disconnection and security procedures are accompanied with GAP parameters. */ + dm_application_context_t * p_app_context; /**< All events that are associated with application context procedures of store, load, and deletion have this as event parameter. */ + dm_service_context_t * p_service_context; /**< All events that are associated with service context procedures of store, load and deletion have this as event parameter. */ + dm_device_context_t * p_device_context; /**< All events that are associated with device context procedures of store, load and deletion have this as event parameter. */ +} dm_event_param_t; + +/** + * @brief Asynchronous events details notified to the application by the module. + * + * @details Defines event type along with event parameters notified to the application by the + * module. + */ +typedef struct +{ + uint8_t event_id; /**< Identifies the event. See \ref dm_events for details on event types and their significance. */ + dm_event_param_t event_param; /**< Event parameters. Can be NULL if the event does not have any parameters. */ + uint16_t event_paramlen; /**< Length of the event parameters, is zero if the event does not have any parameters. */ +} dm_event_t; + +/** + * @brief Event notification callback registered by application with the module. + * + * @details Event notification callback registered by application with the module when registering + * the module using \ref dm_register API. + * + * @param[in] p_handle Identifies the peer for which the event is being notified. + * @param[in] p_event Identifies the event, any associated parameters and parameter length. + * See \ref dm_events for details on event types and their significance. + * @param[in,out] event_result Provide additional information on the event. + * In addition to SDK error codes there is also a return value + * indicating if maximum number of connections has been reached when connecting or bonding. + * + * @retval NRF_SUCCESS on success, or a failure to indicate if it could handle the event + * successfully. There is no action taken in case application returns a failure. + */ +typedef ret_code_t (*dm_event_cb_t)(dm_handle_t const * p_handle, + dm_event_t const * p_event, + ret_code_t event_result); + +/** + * @brief Initialization Parameters. + * + * @details Indicates the application parameters. Currently this only encompasses clearing + * all persistent data. + */ +typedef struct +{ + bool clear_persistent_data; /**< Set to true in case the module should clear all persistent data. */ +} dm_init_param_t; + +/** + * @brief Application Registration Parameters. + * + * @details Parameters needed by the module when registering with it. + */ +typedef struct +{ + dm_event_cb_t evt_handler; /**< Event Handler to be registered. It will receive asynchronous notification from the module, see \ref dm_events for asynchronous events. */ + uint8_t service_type; /**< Bit mask identifying services that the application intends to support for all peers. */ + ble_gap_sec_params_t sec_param; /**< Security parameters to be used for the application. */ +} dm_application_param_t; + +/** + * @brief Defines possible security status/states. + * + * @details Defines possible security status/states of a link when requested by application using + * the \ref dm_security_status_req. + */ +typedef enum +{ + NOT_ENCRYPTED, /**< The link is not secured. */ + ENCRYPTION_IN_PROGRESS, /**< Link security is being established.*/ + ENCRYPTED /**< The link is secure.*/ +} dm_security_status_t; +/** @} */ + +/** + * @defgroup dm_api Device Module APIs + * + * @brief This section describes APIs exposed by the module. + * + * @details This section describes APIs exposed by the module. The APIs have been categorized to provide + * better and specific look up for developers. Categories are: + * - Set up APIs. + * - Context Management APIs. + * - Utility APIs. + * + * MSCs describe usage of these APIs. + * See @ref dm_msc. + * @{ + */ +/** + * @defgroup dm_setup_api Device Module Set-up APIs + * + * @brief Initialization & registration APIs that are pre-requisite for all other module procedures. + * @details This section describes the Module Initialization and Registration APIs needed to be set up by + * the application before device manager can start managing devices and device contexts + * for the application. + * + * @{ + */ + +/** + * @brief Module Initialization Routine. + * + * @details Function for initializing the module. Must called before any other APIs of the module are used. + * + * @param[in] p_init_param Initialization parameters. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * + * @note It is mandatory that pstorage is initialized before initializing this module. + */ +ret_code_t dm_init(dm_init_param_t const * p_init_param); + +/** + * @brief Function for registering the application. + * + * @details This routine is used by the application to register for asynchronous events with the + * device manager. During registration the application also indicates the services that it + * intends to support on this instance. It is possible to register multiple times with the + * device manager. At least one instance shall be registered with the device manager after + * the module has been initialized. + * Maximum number of application instances device manager can support is determined + * by DM_MAX_APPLICATIONS. + * + * All applications must be registered before initiating or accepting connections from the peer. + * + * @param[in] p_appl_param Application parameters. + * @param[out] p_appl_instance Application Instance Identifier in case registration is successful. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization. + * @retval NRF_ERROR_NO_MEM If module cannot support more applications. + * + * @note Currently only one application instance is supported by the module. + */ +ret_code_t dm_register(dm_application_instance_t * p_appl_instance, + dm_application_param_t const * p_appl_param); + +/** + * @brief Function for handling BLE events. + * + * @details BLE Event Handler for the module. This routine should be called from BLE stack event + * dispatcher for the module to work as expected. + * + * @param[in] p_ble_evt BLE stack event being dispatched to the function. + * + */ +void dm_ble_evt_handler(ble_evt_t * p_ble_evt); + +/** @} */ + + +/** + * @defgroup dm_security_api APIs to set up or read status of security on a link. + * + * @brief This section describes APIs to set up Security. These APIs require that the peer is + * connected before the procedures can be requested. + * + * @details This group allows application to request security procedures + * or get the status of the security on a link. + * @{ + */ +/** + * @brief Function for requesting setting up security on a link. + * + * @details This API initiates security procedures with a peer device. + * @note For the GAP Central role, in case peer is not bonded, request to bond/pair is + * initiated. If it is bonded, the link is re-encrypted using the existing bond information. + * For the GAP peripheral role, a Slave security request is sent. + * @details If a pairing procedure is initiated successfully, application is notified of + * @ref DM_EVT_SECURITY_SETUP_COMPLETE. A result indicating success or failure is notified along with the event. + * In case the link is re-encrypted using existing bond information, @ref DM_EVT_LINK_SECURED is + * notified to the application. + * + * @param[in] p_handle Identifies the link on which security is desired. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle is NULL. + * @retval NRF_ERROR_INVALID_ADDR If the peer is not identified by the handle provided by the application + * or if the peer is not connected when this procedure is requested. + */ +ret_code_t dm_security_setup_req(dm_handle_t * p_handle); + +/** + * @brief Function for reading the status of the security on a link. + * + * @details This API allows application to query status of security on a link. + * + * @param[in] p_handle Identifies the link on which security is desired. + * @param[out] p_status Pointer where security status is provided to the application. + * See \ref dm_security_status_t for possible statuses that can be expected. + * + * @retval NRF_SUCCESS Or appropriate error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle or p_status is NULL. + * @retval NRF_ERROR_INVALID_ADDR If peer is not identified by the handle provided by the application + * or if peer is not connected when this procedure is requested. + */ +ret_code_t dm_security_status_req(dm_handle_t const * p_handle, dm_security_status_t * p_status); + +/** + * @brief Function for creating the whitelist. + * + * @details This API allows application to create whitelist based on bonded peer devices in module + * data base. + * + * @param[in] p_handle Identifies the application requesting whitelist creation. + * @param[in,out] p_whitelist Pointer where created whitelist is provided to the application. + * + * @note 'addr_count' and 'irk_count' fields of the structure should be populated with the maximum + * number of devices that the application wishes to request in the whitelist. + * If the number of bonded devices is less than requested, the fields are updated with that number of devices. + * If the number of devices are more than requested, the module will populate the list + * with devices in the order the bond was established with the peer devices. Also, if this routine is + * called when a connection exists with one or more peer devices, + * those connected devices are not added to the whitelist. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle or p_whitelist is NULL. + */ +ret_code_t dm_whitelist_create(dm_application_instance_t const * p_handle, + ble_gap_whitelist_t * p_whitelist); + +/** @} */ + + +/** + * @defgroup dm_cntxt_mgmt_api Context Management APIs + * + * @brief Utility APIs offered by the device manager to get information about the peer if and + * when needed. + * + * @details This group of API allow the application to access information that is not required to be + * maintained by the application but may be needed. Hence it is possible to get the + * information from the module instead of mapping all the information with a device + * context. + * @{ + */ + +ret_code_t dm_device_add(dm_handle_t * p_handle, + dm_device_context_t const * p_context); + +/** + * @brief Function for deleting a peer device context and all related information from the database. + * + * @details Delete peer device context and all related information from database. If + * this API returns NRF_SUCCESS, DM_EVT_DEVICE_CONTEXT_DELETED event is notified to the + * application. Event result notified along with the event indicates success or failure + * of this procedure. + * + * @param[in] p_handle Identifies the peer device to be deleted. + * + * @retval NRF_SUCCESS on success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE In the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle is NULL. + * @retval NRF_ERROR_INVALID_ADDR If peer is not identified the handle provided by the application. + * + * @note Deleting device context results in deleting service and application context for the + * bonded device. The respective events DM_EVT_SERVICE_CONTEXT_DELETED and + * DM_EVT_APPL_CONTEXT_DELETED are not notified to the application. + */ +ret_code_t dm_device_delete(dm_handle_t const * p_handle); + +/** + * @brief Function for deleting all peer device context and all related information from the database. + * + * @details Delete peer device context and all related information from database. If + * this API returns NRF_SUCCESS, DM_EVT_DEVICE_CONTEXT_DELETED event is notified to the + * application for each device that is deleted from the data base. Event result + * notified along with the event indicates success or failure of this procedure. + * + * @param[in] p_handle Identifies application instance that is requesting + * the deletion of all bonded devices. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle is NULL. + * @retval NRF_ERROR_INVALID_ADDR If peer is not identified the handle provided by the application. + * + * @note Deleting device context results in deleting both service and application context for the + * bonded device. The respective events DM_EVT_SERVICE_CONTEXT_DELETED and + * DM_EVT_APPL_CONTEXT_DELETED are not notified to the application. + */ +ret_code_t dm_device_delete_all(dm_application_instance_t const * p_handle); + +/** + * @brief Function for setting Service Context for a peer device identified by 'p_handle' parameter. + * + * @details This API allows application to Set Service Context for a peer device identified by the + * 'p_handle' parameter. This API is useful when the Service Context cannot be requested + * from the SoftDevice, but needs to be assembled by the application or an another module. + * (or when service context is exchanged in an out of band way.) + * This API could also be used to trigger a storing of service context into persistent + * memory. If this is desired, a NULL pointer could be passed to the p_context. + * + * @param[in] p_handle Identifies peer device for which the procedure is requested. + * @param[in] p_context Service context being set. The context information includes length of + * data and pointer to the contextual data being set. The memory pointed to by + * the pointer to data is assumed to be resident when API is being called and + * can be freed or reused once the set procedure is complete. Set procedure + * completion is indicated by the event \ref DM_EVT_SERVICE_CONTEXT_STORED. + * The Event result is notified along with the event and indicates success or failure of + * this procedure. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle is NULL. + * @retval NRF_ERROR_INVALID_ADDR If the peer is not identified by the handle provided by the application. + */ +ret_code_t dm_service_context_set(dm_handle_t const * p_handle, + dm_service_context_t const * p_context); + +/** + * @brief Function for getting Service Context for a peer device identified by 'p_handle' parameter. + * + * @details Get Service Context for a peer device identified by the 'p_handle' parameter. If + * this API returns NRF_SUCCESS, DM_EVT_SERVICE_CONTEXT_LOADED event is notified to the + * application. The event result is notified along with the event indicates success or failure + * of this procedure. + * + * @param[in] p_handle Identifies peer device for which procedure is requested. + * @param[in] p_context Application context being requested. The context information includes length + * of the data and a pointer to the data. Note that requesting a 'get' + * of application does not need to provide memory, the pointer to data will be + * pointing to service data and hence no data movement is involved. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE In case API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle is NULL. + * @retval NRF_ERROR_INVALID_ADDR If the peer is not identified by the handle provided by the application. + */ +ret_code_t dm_service_context_get(dm_handle_t const * p_handle, + dm_service_context_t * p_context); + +/** + * @brief Function for deleting a Service Context for a peer device identified by the 'p_handle' parameter. + * + * @details This API allows application to delete a Service Context identified for a peer device + * identified by the 'p_handle' parameter. If this API returns NRF_SUCCESS, + * DM_EVT_SERVICE_CONTEXT_DELETED event is notified to the application. + * Event result is notified along with the event and indicates success or failure of this + * procedure. + * + * @param[in] p_handle Identifies peer device for which procedure is requested. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle is NULL. + * @retval NRF_ERROR_INVALID_ADDR If the peer is not identified by the handle provided by the application. + */ +ret_code_t dm_service_context_delete(dm_handle_t const * p_handle); + +/** + * @brief Function for setting Application Context for a peer device identified by the 'p_handle' parameter. + * + * @details This application allows the setting of the application context for the peer device identified by + * the 'p_handle'. Application context is stored persistently by the module and can be + * requested by the application at any time using the \ref dm_application_context_get + * API. Note that this procedure is permitted only for bonded devices. If the + * device is not bonded, application context cannot be set. However, it is not mandatory + * that the bonded device is connected when requesting this procedure. + * + * @param[in] p_handle Identifies peer device for which procedure is requested. + * + * @param[in] p_context Application context being set. The context information includes length of the + * data and pointer to the contextual data being set. The memory pointed to by + * the data pointer is assumed to be resident when API is being called and + * can be freed or reused once the set procedure is complete. Set procedure + * completion is notified by the event \ref DM_EVT_APPL_CONTEXT_STORED. + * The event result is notified along with the event and indicates success or + * failure of this procedure. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle and/or p_context is NULL. + * @retval NRF_ERROR_INVALID_ADDR If peer is not identified the handle provided by the application. + * + * @note The API returns FEATURE_NOT_ENABLED in case DEVICE_MANAGER_APP_CONTEXT_SIZE is set to zero. + */ +ret_code_t dm_application_context_set(dm_handle_t const * p_handle, + dm_application_context_t const * p_context); + +/** + * @brief Function for getting Application Context for a peer device identified by the 'p_handle' parameter. + * + * @details Get Application Context for a peer device identified by the 'p_handle' parameter. If + * this API returns NRF_SUCCESS, DM_EVT_APPL_CONTEXT_LOADED event is notified to the + * application. Event result notified along with the event indicates success or failure + * of this procedure. + * + * @param[in] p_handle Identifies peer device for which procedure is requested. + * @param[in] p_context Application context being requested. The context information includes + * length of data and pointer to the contextual data is provided. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle and/or p_context is NULL. + * @retval NRF_ERROR_INVALID_ADDR If the peer is not identified by the handle provided by the application. + * @retval DM_NO_APP_CONTEXT If no application context was set that can be fetched. + * + * @note The API returns FEATURE_NOT_ENABLED in case DEVICE_MANAGER_APP_CONTEXT_SIZE is set to + * zero. + */ +ret_code_t dm_application_context_get(dm_handle_t const * p_handle, + dm_application_context_t * p_context); + +/** + * @brief Function for deleting Application Context for a peer device identified by the 'p_handle' parameter. + * + * @details Delete Application Context for a peer device identified by the 'p_handle' parameter. If + * this API returns NRF_SUCCESS, DM_EVT_APPL_CONTEXT_DELETED event is notified to the + * application. The event result notified along with the event and indicates success or failure + * of this procedure. + * + * @param[in] p_handle Identifies peer device for which procedure is requested. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If the p_handle is NULL. + * @retval NRF_ERROR_INVALID_ADDR If peer is not identified the handle provided by the application. + * @retval DM_NO_APP_CONTEXT If no application context was set that can be deleted. + * + * @note The API returns FEATURE_NOT_ENABLED if the DEVICE_MANAGER_APP_CONTEXT_SIZE is set to zero. + */ +ret_code_t dm_application_context_delete(dm_handle_t const * p_handle); + +/** @} */ + + +/** + * @defgroup utility_api Utility APIs + * @{ + * @brief This section describes the utility APIs offered by the module. + * + * @details APIs defined in this section are utility or assisting/helper APIs. + */ +/** + * @brief Function for Setting/Copying Application instance to Device Manager handle. + * + * @param[in] p_appl_instance Application instance to be set. + * @param[out] p_handle Device Manager handle for which the instance is to be copied. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle and/or p_addr is NULL. + */ +ret_code_t dm_application_instance_set(dm_application_instance_t const * p_appl_instance, + dm_handle_t * p_handle); + +/** + * @brief Function for getting a peer's device address. + * + * @param[in] p_handle Identifies the peer device whose address is requested. Can not be NULL. + * @param[out] p_addr Pointer where address is to be copied. Can not be NULL. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle and/or p_addr is NULL. + * @retval NRF_ERROR_NOT_FOUND If the peer could not be identified. + */ +ret_code_t dm_peer_addr_get(dm_handle_t const * p_handle, + ble_gap_addr_t * p_addr); + +/** + * @brief Function for setting/updating a peer's device address. + * + * @param[in] p_handle Identifies the peer device whose address is requested to be set/updated. + * @param[out] p_addr Address to be set/updated. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If p_handle and/or p_addr is NULL. + * @retval NRF_ERROR_INVALID_ADDR If the peer is not identified by the handle provided by the application. + * @retval NRF_ERROR_INVALID_PARAM If this procedure is requested while connected to the peer or if the address + * type was set to BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE. + * + * @note Setting or updating a peer's device address is permitted + * only for a peer that is bonded and disconnected. + * @note Updated address is reflected only after DM_EVT_DEVICE_CONTEXT_STORED is notified to the + * application for this bonded device instance. In order to avoid abnormal behaviour, it is + * recommended to not invite/initiate connections on the updated address unless this event + * has been notified. + */ +ret_code_t dm_peer_addr_set(dm_handle_t const * p_handle, + ble_gap_addr_t const * p_addr); + +/** + * @brief Function for initializing Device Manager handle. + * + * @param[in] p_handle Device Manager handle to be initialized. + * + * @retval NRF_SUCCESS On success. + * @retval NRF_ERROR_NULL If p_handle is NULL. + * + * @note This routine is permitted before initialization of the module. + */ +ret_code_t dm_handle_initialize(dm_handle_t * p_handle); + +/** + * @brief Function for getting distributed keys for a device. + * + * @param[in] p_handle Device Manager handle identifying the peer. + * @param[out] p_key_dist Pointer to distributed keys. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_INVALID_STATE If the API is called without module initialization and/or + * application registration. + * @retval NRF_ERROR_NULL If the p_handle and/or p_key_dist pointer is NULL. + * @retval NRF_ERROR_INVALID_ADDR If the peer is not identified by the handle provided by the application. + */ +ret_code_t dm_distributed_keys_get(dm_handle_t const * p_handle, + dm_sec_keyset_t * p_key_dist); + +/** + * @brief Function for getting the corresponding dm_handle_t based on the connection handle. + * + * @param[in] conn_handle Connection handle as provided by the SoftDevice. + * @param[in,out] p_handle Pointer to the p_handle containg the application instance for the + * registered application. If the application instance is valid then + * the p_handle will be filled with requested data. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + * @retval NRF_ERROR_NULL If the p_handle pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If no p_handle is found for the provided connection handle. + */ +ret_code_t dm_handle_get(uint16_t conn_handle, dm_handle_t * p_handle); + +/** @} */ +/** @} */ +/** @} */ +#endif // DEVICE_MANAGER_H__ + diff --git a/cores/arduino/components/ble/device_manager/device_manager_peripheral.c b/cores/arduino/components/ble/device_manager/device_manager_peripheral.c new file mode 100644 index 0000000..f6657a2 --- /dev/null +++ b/cores/arduino/components/ble/device_manager/device_manager_peripheral.c @@ -0,0 +1,2962 @@ +/* Copyright (C) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#include "device_manager.h" +#include "app_trace.h" +#include "pstorage.h" +#include "ble_hci.h" +#include "app_error.h" + +#if defined ( __CC_ARM ) + #ifndef __ALIGN + #define __ALIGN(x) __align(x) /**< Forced aligment keyword for ARM Compiler */ + #endif +#elif defined ( __ICCARM__ ) + #ifndef __ALIGN + #define __ALIGN(x) /**< Forced aligment keyword for IAR Compiler */ + #endif +#elif defined ( __GNUC__ ) + #ifndef __ALIGN + #define __ALIGN(x) __attribute__((aligned(x))) /**< Forced aligment keyword for GNU Compiler */ + #endif +#endif + +#define INVALID_ADDR_TYPE 0xFF /**< Identifier for an invalid address type. */ +#define EDIV_INIT_VAL 0xFFFF /**< Initial value for diversifier. */ + +/** + * @defgroup device_manager_app_states Connection Manager Application States + * @{ + */ +#define STATE_CONTROL_PROCEDURE_IN_PROGRESS 0x01 /**< State where a security procedure is ongoing. */ +#define STATE_QUEUED_CONTROL_REQUEST 0x02 /**< State where it is known if there is any queued security request or not. */ +/** @} */ + +/** + * @defgroup device_manager_conn_inst_states Connection Manager Connection Instances States. + * @{ + */ +#define STATE_IDLE 0x01 /**< State where connection instance is free. */ +#define STATE_CONNECTED 0x02 /**< State where connection is successfully established. */ +#define STATE_PAIRING 0x04 /**< State where pairing procedure is in progress. This state is used for pairing and bonding, as pairing is needed for both. */ +#define STATE_BONDED 0x08 /**< State where device is bonded. */ +#define STATE_DISCONNECTING 0x10 /**< State where disconnection is in progress, application will be notified first, but no further active procedures on the link. */ +#define STATE_PAIRING_PENDING 0x20 /**< State where pairing request is pending on the link. */ +#define STATE_BOND_INFO_UPDATE 0x40 /**< State where information has been updated, update the flash. */ +#define STATE_LINK_ENCRYPTED 0x80 /**< State where link is encrypted. */ +/** @} */ + +/** + * @defgroup device_manager_peer_id_defines Peer Identification Information Defines. + * + * @brief These defines are used to know which of the peer identification is applicable for a peer. + * + * @details These defines are used for peer identification. Here, bit map is used because it is + * possible that the application has both IRK and address for identification. + * @{ + */ +#define UNASSIGNED 0xFF /**< Peer instance is unassigned/unused. */ +#define IRK_ENTRY 0x01 /**< Peer instance has IRK as identification information. */ +#define ADDR_ENTRY 0x02 /**< Peer instance has address as identification information. */ +#define SERVICE_CONTEXT_ENTRY 0x04 /**< Peer instance has service context set. */ +#define APP_CONTEXT_ENTRY 0x08 /**< Peer instance has an application context set. */ +/** @} */ + +/**@brief Device store state identifiers. */ +typedef enum +{ + STORE_ALL_CONTEXT, /**< Store all context. */ + FIRST_BOND_STORE, /**< Store bond. */ + UPDATE_PEER_ADDR /**< Update peer address. */ +} device_store_state_t; + +/** + * @defgroup device_manager_context_offsets Context Offsets + * @{ + * + * @brief Context offsets each of the context information in persistent memory. + * + * @details Below is a layout showing how each how the context information is stored in persistent + * memory. + * + * All Device context is stored in the flash as follows: + * +---------+---------+---------+------------------+----------------+--------------------+ + * | Block / Device ID + Layout of stored information in storage block | + * +---------+---------+---------+------------------+----------------+--------------------+ + * | Block 0 | Device 0| Peer Id | Bond Information | Service Context| Application Context| + * +---------+---------+---------+------------------+----------------+--------------------+ + * | Block 1 | Device 1| Peer Id | Bond Information | Service Context| Application Context| + * +---------+---------+---------+------------------+----------------+--------------------+ + * | ... | .... | + * +---------+---------+---------+------------------+----------------+--------------------+ + * | Block N | Device N| Peer Id | Bond Information | Service Context| Application Context| + * +---------+---------+---------+------------------+----------------+--------------------+ + * + * The following defines are used to get offset of each of the components within a block. + */ + +#define PEER_ID_STORAGE_OFFSET 0 /**< Offset at which peer id is stored in the block. */ +#define BOND_STORAGE_OFFSET PEER_ID_SIZE /**< Offset at which bond information is stored in the block. */ +#define SERVICE_STORAGE_OFFSET (BOND_STORAGE_OFFSET + BOND_SIZE) /**< Offset at which service context is stored in the block. */ +#define APP_CONTEXT_STORAGE_OFFSET (SERVICE_STORAGE_OFFSET + SERVICE_CONTEXT_SIZE) /**< Offset at which application context is stored in the block. */ +/** @} */ + +/** + * @defgroup device_manager_context_size Context size. + * @{ + * + * @brief This group defines the size of each of the context information. + */ +#define PEER_ID_SIZE (sizeof(peer_id_t)) /**< Size of peer identification information. */ +#define BOND_SIZE (sizeof(bond_context_t)) /**< Size of bond information. */ +#define DEVICE_CONTEXT_SIZE (PEER_ID_SIZE + BOND_SIZE) /**< Size of Device context, include peer identification and bond information. */ +#define GATTS_SERVICE_CONTEXT_SIZE (sizeof(dm_gatts_context_t)) /**< Size of GATTS service context. */ +#define GATTC_SERVICE_CONTEXT_SIZE (sizeof(dm_gatt_client_context_t)) /**< Size of GATTC service context. */ +#define SERVICE_CONTEXT_SIZE (GATTS_SERVICE_CONTEXT_SIZE + GATTC_SERVICE_CONTEXT_SIZE) /**< Combined size of GATTS and GATTC service contexts. */ +#define APP_CONTEXT_MIN_SIZE 4 /**< Minimum size for application context data. */ +#if (DEVICE_MANAGER_APP_CONTEXT_SIZE != 0) +#define APP_CONTEXT_SIZE (sizeof(uint32_t) + DEVICE_MANAGER_APP_CONTEXT_SIZE) /**< Size of application context including length field. */ +#else //DEVICE_MANAGER_APP_CONTEXT_SIZE +#define APP_CONTEXT_SIZE 0 /**< Size of application context. */ +#endif // DEVICE_MANAGER_APP_CONTEXT_SIZE +#define ALL_CONTEXT_SIZE (DEVICE_CONTEXT_SIZE + SERVICE_CONTEXT_SIZE + APP_CONTEXT_SIZE) /**< Size of all contexts. */ +/** @} */ + + +/** + * @defgroup device_manager_log Module's Log Macros + * + * @details Macros used for creating module logs which can be useful in understanding handling + * of events or actions on API requests. These are intended for debugging purposes and + * can be disabled by defining the DM_DISABLE_LOGS. + * + * @note That if ENABLE_DEBUG_LOG_SUPPORT is disabled, having DM_DISABLE_LOGS has no effect. + * @{ + */ +#define nDM_DISABLE_LOGS /**< Enable this macro to disable any logs from this module. */ + +#ifndef DM_DISABLE_LOGS +#define DM_LOG app_trace_log /**< Used for logging details. */ +#define DM_ERR app_trace_log /**< Used for logging errors in the module. */ +#define DM_TRC app_trace_log /**< Used for getting trace of execution in the module. */ +#define DM_DUMP app_trace_dump /**< Used for dumping octet information to get details of bond information etc. */ +#else //DM_DISABLE_LOGS +#define DM_DUMP(...) /**< Disables dumping of octet streams. */ +#define DM_LOG(...) /**< Disables detailed logs. */ +#define DM_ERR(...) /**< Disables error logs. */ +#define DM_TRC(...) /**< Disables traces. */ +#endif //DM_DISABLE_LOGS +/** @} */ + +/** + * @defgroup device_manager_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently the SDK does not use mutexes but + * framework is provided in case need arises to use an alternative architecture. + * @{ + */ +#define DM_MUTEX_LOCK() SDK_MUTEX_LOCK(m_dm_mutex) /**< Lock module using mutex. */ +#define DM_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_dm_mutex) /**< Unlock module using mutex. */ +/** @} */ + + +/** + * @defgroup device_manager_misc_defines Miscellaneous defines used across the module. + * @{ + */ +#define DM_GATT_ATTR_SIZE 6 /**< Size of each GATT attribute to be stored persistently. */ +#define DM_GATT_SERVER_ATTR_MAX_SIZE ((DM_GATT_ATTR_SIZE * DM_GATT_CCCD_COUNT) + 2) /**< Maximum size of GATT attributes to be stored.*/ +#define DM_SERVICE_CONTEXT_COUNT (DM_PROTOCOL_CNTXT_ALL + 1) /**< Maximum number of service contexts. */ +#define DM_EVT_DEVICE_CONTEXT_BASE 0x20 /**< Base for device context base. */ +#define DM_EVT_SERVICE_CONTEXT_BASE 0x30 /**< Base for service context base. */ +#define DM_EVT_APP_CONTEXT_BASE 0x40 /**< Base for application context base. */ +#define DM_LOAD_OPERATION_ID 0x01 /**< Load operation identifier. */ +#define DM_STORE_OPERATION_ID 0x02 /**< Store operation identifier. */ +#define DM_CLEAR_OPERATION_ID 0x03 /**< Clear operation identifier. */ +/** @} */ + +#define DM_GATTS_INVALID_SIZE 0xFFFFFFFF /**< Identifer for GATTS invalid size. */ + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros for verifying parameters passed to the module in the APIs. These macros + * could be mapped to nothing in the final version of the code in order to save execution + * time and program size. + * @{ + */ + +//#define DM_DISABLE_API_PARAM_CHECK /**< Macro to disable API parameters check. */ + +#undef NULL_PARAM_CHECK +#undef VERIFY_MODULE_INITIALIZED +#undef VERIFY_MODULE_INITIALIZED_VOID +#undef VERIFY_APP_REGISTERED +#undef VERIFY_APP_REGISTERED_VOID +#undef VERIFY_CONNECTION_INSTANCE +#undef VERIFY_DEVICE_INSTANCE + +#ifndef DM_DISABLE_API_PARAM_CHECK + +/**@brief Macro for verifying NULL parameters are not passed to API. + * + * @param[in] PARAM Parameter checked for NULL. + * + * @retval (NRF_ERROR_NULL | DEVICE_MANAGER_ERR_BASE) when @ref PARAM is NULL. + */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | DEVICE_MANAGER_ERR_BASE); \ + } +/**@} */ + + +/**@brief Macro for verifying module's initialization status. + * + * @retval (NRF_ERROR_INVALID_STATE | DEVICE_MANAGER_ERR_BASE) when module is not initialized. + */ +#define VERIFY_MODULE_INITIALIZED() \ + do \ + { \ + if (!m_module_initialized) \ + { \ + return (NRF_ERROR_INVALID_STATE | DEVICE_MANAGER_ERR_BASE); \ + } \ + } while (0) + + +/**@brief Macro for verifying module's initialization status. Returns in case it is not initialized. + */ +#define VERIFY_MODULE_INITIALIZED_VOID() \ + do \ + { \ + if (!m_module_initialized) \ + { \ + return; \ + } \ + } while (0) + + +/**@brief Macro for verifying that the application is registered. + * + * @param[in] X Application instance identifier. + * + * @retval (NRF_ERROR_INVALID_STATE | DEVICE_MANAGER_ERR_BASE) when module API is called without + * registering an application with the module. + */ +#define VERIFY_APP_REGISTERED(X) \ + do \ + { \ + if (((X) >= DEVICE_MANAGER_MAX_APPLICATIONS) || \ + (m_application_table[(X)].ntf_cb == NULL)) \ + { \ + return (NRF_ERROR_INVALID_STATE | DEVICE_MANAGER_ERR_BASE); \ + } \ + } while (0) + + +/**@brief Macro for verifying that the application is registered. Returns in case it is not + * registered. + * + * @param[in] X Application instance identifier. + */ +#define VERIFY_APP_REGISTERED_VOID(X) \ + do \ + { \ + if (((X) >= DEVICE_MANAGER_MAX_APPLICATIONS) || \ + (m_application_table[(X)].ntf_cb == NULL)) \ + { \ + return; \ + } \ + } while (0) + + +/**@brief Macro for verifying connection instance is allocated. + * + * @param[in] X Connection instance identifier. + * + * @retval (NRF_ERROR_INVALID_ADDR | DEVICE_MANAGER_ERR_BASE) when connection instance is not + * allocated. + */ +#define VERIFY_CONNECTION_INSTANCE(X) \ + do \ + { \ + if (((X) >= DEVICE_MANAGER_MAX_CONNECTIONS) || \ + (m_connection_table[(X)].state == STATE_IDLE)) \ + { \ + return (NRF_ERROR_INVALID_ADDR | DEVICE_MANAGER_ERR_BASE); \ + } \ + } while (0) + + +/**@brief Macro for verifying if device instance is allocated. + * + * @param[in] X Device instance identifier. + * + * @retval (NRF_ERROR_INVALID_ADDR | DEVICE_MANAGER_ERR_BASE) when device instance is not allocated. + */ +#define VERIFY_DEVICE_INSTANCE(X) \ + do \ + { \ + if (((X) >= DEVICE_MANAGER_MAX_BONDS) || \ + (m_peer_table[(X)].id_bitmap == UNASSIGNED)) \ + { \ + return (NRF_ERROR_INVALID_ADDR | DEVICE_MANAGER_ERR_BASE); \ + } \ + } while (0) + +/**@brief Macro for verifying if device is bonded and thus can store data persistantly. + * + * @param[in] X Connection instance identifier. + * + * @retval (NRF_ERROR_INVALID_STATE | DEVICE_MANAGER_ERR_BASE) when device is not bonded. + */ +#define VERIFY_DEVICE_BOND(X) \ + do \ + { \ + if ((m_connection_table[(X)].state & STATE_BONDED) != STATE_BONDED)\ + { \ + return (NRF_ERROR_INVALID_STATE | DEVICE_MANAGER_ERR_BASE); \ + } \ + } while (0) +#else +#define NULL_PARAM_CHECK(X) +#define VERIFY_MODULE_INITIALIZED() +#define VERIFY_MODULE_INITIALIZED_VOID() +#define VERIFY_APP_REGISTERED(X) +#define VERIFY_APP_REGISTERED_VOID(X) +#define VERIFY_CONNECTION_INSTANCE(X) +#define VERIFY_DEVICE_INSTANCE(X) +#endif //DM_DISABLE_API_PARAM_CHECK +/** @} */ + +#define INVALID_CONTEXT_LEN 0xFFFFFFFF /**< Identifier for invalid context length. */ +/**@brief Macro for checking that application context size is greater that minimal size. + * + * @param[in] X Size of application context. + * + * @retval (NRF_ERROR_INVALID_PARAM) when size is smaller than minimun required size. + */ +#define SIZE_CHECK_APP_CONTEXT(X) \ + if ((X) < (APP_CONTEXT_MIN_SIZE)) \ + { \ + return NRF_ERROR_INVALID_PARAM; \ + } + + +/** + * @defgroup dm_data_types Module's internal data types. + * + * @brief This section describes a module's internal data structures. + * @{ + */ +/**@brief Peer identification information. + */ +typedef struct +{ + ble_gap_id_key_t peer_id; /**< IRK and/or address of peer. */ + uint16_t ediv; /**< Peer's encrypted diversifier. */ + uint8_t id_bitmap; /**< Contains information if above field is valid. */ +} peer_id_t; + +STATIC_ASSERT(sizeof(peer_id_t) % 4 == 0); /**< Check to ensure Peer identification information is a multiple of 4. */ + +/**@brief Portion of bonding information exchanged by a device during bond creation that needs to + * be stored persistently. + * + * @note An entry is not made in this table unless device is bonded. + */ +typedef struct +{ + ble_gap_enc_key_t peer_enc_key; /**< Local LTK info, central IRK and address */ +} bond_context_t; + +STATIC_ASSERT(sizeof(bond_context_t) % 4 == 0); /**< Check to ensure bond information is a multiple of 4. */ + +/**@brief GATT Server Attributes size and data. + */ +typedef struct +{ + uint32_t flags; /**< Flags identifying the stored attributes. */ + uint32_t size; /**< Size of stored attributes. */ + uint8_t attributes[DM_GATT_SERVER_ATTR_MAX_SIZE]; /**< Array to hold the server attributes. */ +} dm_gatts_context_t; + +STATIC_ASSERT(sizeof(dm_gatts_context_t) % 4 == 0); /**< Check to ensure GATT Server Attributes size and data information is a multiple of 4. */ + +/**@brief GATT Client context information. Placeholder for now. + */ +typedef struct +{ + void * p_dummy; /**< Placeholder, currently unused. */ +} dm_gatt_client_context_t; + +STATIC_ASSERT(sizeof(dm_gatt_client_context_t) % 4 == 0); /**< Check to ensure GATT Client context information is a multiple of 4. */ +STATIC_ASSERT((DEVICE_MANAGER_APP_CONTEXT_SIZE % 4) == 0); /**< Check to ensure device manager application context information is a multiple of 4. */ + +/**@brief Connection instance definition. Maintains information with respect to an active peer. + */ +typedef struct +{ + ble_gap_addr_t peer_addr; /**< Peer identification information. This information is retained as long as the connection session exists, once disconnected, for non-bonded devices this information is not stored persistently. */ + uint16_t conn_handle; /**< Connection handle for the device. */ + uint8_t state; /**< Link state. */ + uint8_t bonded_dev_id; /**< In case the device is bonded, this points to the corresponding bonded device. This index can be used to index service and bond context as well. */ +} connection_instance_t; + +/**@brief Application instance definition. Maintains information with respect to a registered + * application. + */ +typedef struct +{ + dm_event_cb_t ntf_cb; /**< Callback registered with the application. */ + ble_gap_sec_params_t sec_param; /**< Local security parameters registered by the application. */ + uint8_t state; /**< Application state. Currently this is used only for knowing if any security procedure is in progress and/or a security procedure is pending to be requested. */ + uint8_t service; /**< Service registered by the application. */ +} application_instance_t; + +/**@brief Function for performing necessary action of storing each of the service context as + * registered by the application. + * + * @param[in] p_block_handle Storage block identifier. + * @param[in] p_handle Device handle identifying device that is stored. + * + * @retval Operation result code. + */ +typedef ret_code_t (* service_context_access_t)(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle); + +/**@brief Function for performing necessary action of applying the context information. + * + * @param[in] p_handle Device handle identifying device that is stored. + * + * @retval Operation result code. + */ +typedef ret_code_t (* service_context_apply_t)(dm_handle_t * p_handle); + +/**@brief Function for performing necessary functions of storing or updating. + * + * @param[in] p_dest Destination address where data is stored persistently. + * @param[in] p_src Source address containing data to be stored. + * @param[in] size Size of data to be stored expressed in bytes. Must be word aligned. + * @param[in] offset Offset in bytes to be applied when writing to the block. + * + * @retval Operation result code. + */ +typedef uint32_t (* storage_operation)(pstorage_handle_t * p_dest, + uint8_t * p_src, + pstorage_size_t size, + pstorage_size_t offset); +/** @} */ + +/** + * @defgroup dm_tables Module's internal tables. + * + * @brief This section describes the module's internal tables and the static global variables + * needed for its functionality. + * @{ + */ +#if (DEVICE_MANAGER_APP_CONTEXT_SIZE != 0) +static uint8_t * m_app_context_table[DEVICE_MANAGER_MAX_BONDS]; /**< Table to remember application contexts of bonded devices. */ +#endif //DEVICE_MANAGER_APP_CONTEXT_SIZE +__ALIGN(sizeof(uint32_t)) +static peer_id_t m_peer_table[DEVICE_MANAGER_MAX_BONDS] ; /**< Table to maintain bonded devices' identification information, an instance is allocated in the table when a device is bonded and freed when bond information is deleted. */ +__ALIGN(sizeof(uint32_t)) +static bond_context_t m_bond_table[DEVICE_MANAGER_MAX_CONNECTIONS]; /**< Table to maintain bond information for active peers. */ +static dm_gatts_context_t m_gatts_table[DEVICE_MANAGER_MAX_CONNECTIONS]; /**< Table for service information for active connection instances. */ +static connection_instance_t m_connection_table[DEVICE_MANAGER_MAX_CONNECTIONS]; /**< Table to maintain active peer information. An instance is allocated in the table when a new connection is established and freed on disconnection. */ +static application_instance_t m_application_table[DEVICE_MANAGER_MAX_APPLICATIONS]; /**< Table to maintain application instances. */ +static pstorage_handle_t m_storage_handle; /**< Persistent storage handle for blocks requested by the module. */ +static uint32_t m_peer_addr_update; /**< 32-bit bitmap to remember peer device address update. */ +static ble_gap_id_key_t m_local_id_info; /**< ID information of central in case resolvable address is used. */ +static bool m_module_initialized = false; /**< State indicating if module is initialized or not. */ +static uint8_t m_irk_index_table[DEVICE_MANAGER_MAX_BONDS]; /**< List maintaining IRK index list. */ + +SDK_MUTEX_DEFINE(m_dm_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ +/** @} */ + +static __INLINE ret_code_t no_service_context_store(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle); + +static __INLINE ret_code_t gatts_context_store(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle); + +static __INLINE ret_code_t gattc_context_store(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle); + +static __INLINE ret_code_t gattsc_context_store(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle); + +static __INLINE ret_code_t no_service_context_load(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle); + +static __INLINE ret_code_t gatts_context_load(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle); + +static __INLINE ret_code_t gattc_context_load(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle); + +static __INLINE ret_code_t gattsc_context_load(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle); + +static __INLINE ret_code_t no_service_context_apply(dm_handle_t * p_handle); + +static __INLINE ret_code_t gatts_context_apply(dm_handle_t * p_handle); + +static __INLINE ret_code_t gattc_context_apply(dm_handle_t * p_handle); + +static __INLINE ret_code_t gattsc_context_apply(dm_handle_t * p_handle); + + +/**< Array of function pointers based on the types of service registered. */ +const service_context_access_t m_service_context_store[DM_SERVICE_CONTEXT_COUNT] = +{ + no_service_context_store, /**< Dummy function, when there is no service context registered. */ + gatts_context_store, /**< GATT Server context store function. */ + gattc_context_store, /**< GATT Client context store function. */ + gattsc_context_store /**< GATT Server & Client context store function. */ +}; + + +/**< Array of function pointers based on the types of service registered. */ +const service_context_access_t m_service_context_load[DM_SERVICE_CONTEXT_COUNT] = +{ + no_service_context_load, /**< Dummy function, when there is no service context registered. */ + gatts_context_load, /**< GATT Server context load function. */ + gattc_context_load, /**< GATT Client context load function. */ + gattsc_context_load /**< GATT Server & Client context load function. */ +}; + + +/**< Array of function pointers based on the types of service registered. */ +const service_context_apply_t m_service_context_apply[DM_SERVICE_CONTEXT_COUNT] = +{ + no_service_context_apply, /**< Dummy function, when there is no service context registered. */ + gatts_context_apply, /**< GATT Server context apply function. */ + gattc_context_apply, /**< GATT Client context apply function. */ + gattsc_context_apply /**< GATT Server & Client context apply function. */ +}; + + +const uint32_t m_context_init_len = 0xFFFFFFFF; /**< Constant used to update the initial value for context in the flash. */ + +/**@brief Function for setting update status for the device identified by 'index'. + * + * @param[in] index Device identifier. + */ +static __INLINE void update_status_bit_set(uint32_t index) +{ + m_peer_addr_update |= (BIT_0 << index); +} + + +/**@brief Function for resetting update status for device identified by 'index'. + * + * @param[in] index Device identifier. + */ +static __INLINE void update_status_bit_reset(uint32_t index) +{ + m_peer_addr_update &= (~((uint32_t)BIT_0 << index)); +} + + +/**@brief Function for providing update status for the device identified by 'index'. + * + * @param[in] index Device identifier. + * + * @retval true if the bit is set, false otherwise. + */ +static __INLINE bool update_status_bit_is_set(uint32_t index) +{ + return ((m_peer_addr_update & (BIT_0 << index)) ? true : false); +} + + +/**@brief Function for initialiasing the application instance identified by 'index'. + * + * @param[in] index Device identifier. + */ +static __INLINE void application_instance_init(uint32_t index) +{ + DM_TRC("[DM]: Initializing Application Instance 0x%08X.\r\n", index); + + m_application_table[index].ntf_cb = NULL; + m_application_table[index].state = 0x00; + m_application_table[index].service = 0x00; +} + + +/**@brief Function for initialiasing the connection instance identified by 'index'. + * + * @param[in] index Device identifier. + */ +static __INLINE void connection_instance_init(uint32_t index) +{ + DM_TRC("[DM]: Initializing Connection Instance 0x%08X.\r\n", index); + + m_connection_table[index].state = STATE_IDLE; + m_connection_table[index].conn_handle = BLE_CONN_HANDLE_INVALID; + m_connection_table[index].bonded_dev_id = DM_INVALID_ID; + + memset(&m_connection_table[index].peer_addr, 0, sizeof (ble_gap_addr_t)); +} + + +/**@brief Function for initialiasing the peer device instance identified by 'index'. + * + * @param[in] index Device identifier. + */ +static __INLINE void peer_instance_init(uint32_t index) +{ + DM_TRC("[DM]: Initializing Peer Instance 0x%08X.\r\n", index); + + memset(m_peer_table[index].peer_id.id_addr_info.addr, 0, BLE_GAP_ADDR_LEN); + memset(m_peer_table[index].peer_id.id_info.irk, 0, BLE_GAP_SEC_KEY_LEN); + + //Initialize the address type to invalid. + m_peer_table[index].peer_id.id_addr_info.addr_type = INVALID_ADDR_TYPE; + + //Initialize the identification bit map to unassigned. + m_peer_table[index].id_bitmap = UNASSIGNED; + + // Initialize diversifier. + m_peer_table[index].ediv = EDIV_INIT_VAL; + + + //Reset the status bit. + update_status_bit_reset(index); + +#if (DEVICE_MANAGER_APP_CONTEXT_SIZE != 0) + //Initialize the application context for bond device. + m_app_context_table[index] = NULL; +#endif //DEVICE_MANAGER_APP_CONTEXT_SIZE +} + + +/**@brief Function for searching connection instance matching the connection handle and the state + * requested. + * + * @details Connection handle and state information is used to get a connection instance, it + * is possible to ignore the connection handle by using BLE_CONN_HANDLE_INVALID. + * + * @param[in] conn_handle Connection handle. + * @param[in] state Connection instance state. + * @param[out] p_instance Connection instance. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. Invalid state + * @retval NRF_ERROR_NOT_FOUND Operation failure. Not found + */ +static ret_code_t connection_instance_find(uint16_t conn_handle, + uint8_t state, + uint32_t * p_instance) +{ + ret_code_t err_code; + uint32_t index; + + err_code = NRF_ERROR_INVALID_STATE; + + for (index = 0; index < DEVICE_MANAGER_MAX_CONNECTIONS; index++) + { + //Search only based on the state. + if (state & m_connection_table[index].state) + { + //Ignore the connection handle. + if ((conn_handle == BLE_CONN_HANDLE_INVALID) || + (conn_handle == m_connection_table[index].conn_handle)) + { + //Search for matching connection handle. + (*p_instance) = index; + err_code = NRF_SUCCESS; + + break; + } + else + { + err_code = NRF_ERROR_NOT_FOUND; + } + } + } + + return err_code; +} + + +/**@brief Function for allocating device instance for a bonded device. + * + * @param[out] p_device_index Device index. + * @param[in] p_addr Peer identification information. + * + * @retval NRF_SUCCESS Operation success. + * @retval DM_DEVICE_CONTEXT_FULL Operation failure. + */ +static __INLINE ret_code_t device_instance_allocate(uint8_t * p_device_index, + ble_gap_addr_t const * p_addr) +{ + ret_code_t err_code; + uint32_t index; + + err_code = DM_DEVICE_CONTEXT_FULL; + + for (index = 0; index < DEVICE_MANAGER_MAX_BONDS; index++) + { + DM_TRC("[DM]:[DI 0x%02X]: Device type 0x%02X.\r\n", + index, m_peer_table[index].peer_id.id_addr_info.addr_type); + DM_TRC("[DM]: Device Addr 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X.\r\n", + m_peer_table[index].peer_id.id_addr_info.addr[0], + m_peer_table[index].peer_id.id_addr_info.addr[1], + m_peer_table[index].peer_id.id_addr_info.addr[2], + m_peer_table[index].peer_id.id_addr_info.addr[3], + m_peer_table[index].peer_id.id_addr_info.addr[4], + m_peer_table[index].peer_id.id_addr_info.addr[5]); + + if (m_peer_table[index].id_bitmap == UNASSIGNED) + { + if (p_addr->addr_type != BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE) + { + m_peer_table[index].id_bitmap &= (~ADDR_ENTRY); + m_peer_table[index].peer_id.id_addr_info = (*p_addr); + } + else + { + m_peer_table[index].id_bitmap &= (~IRK_ENTRY); + } + + (*p_device_index) = index; + err_code = NRF_SUCCESS; + + DM_LOG("[DM]: Allocated device instance 0x%02X\r\n", index); + + break; + } + } + + return err_code; +} + + +/**@brief Function for freeing a device instance allocated for bonded device. + * + * @param[in] device_index Device index. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + */ +static __INLINE ret_code_t device_instance_free(uint32_t device_index) +{ + ret_code_t err_code; + pstorage_handle_t block_handle; + + //Get the block handle. + err_code = pstorage_block_identifier_get(&m_storage_handle, device_index, &block_handle); + + if (err_code == NRF_SUCCESS) + { + DM_TRC("[DM]:[DI 0x%02X]: Freeing Instance.\r\n", device_index); + + //Request clearing of the block. + err_code = pstorage_clear(&block_handle, ALL_CONTEXT_SIZE); + + if (err_code == NRF_SUCCESS) + { + peer_instance_init(device_index); + } + } + + return err_code; +} + + +/**@brief Function for searching for the device in the bonded device list. + * + * @param[in] p_addr Peer identification information. + * @param[out] p_device_index Device index. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_NOT_FOUND Operation failure. + */ +static ret_code_t device_instance_find(ble_gap_addr_t const * p_addr, uint32_t * p_device_index, uint16_t ediv) +{ + ret_code_t err_code; + uint32_t index; + + err_code = NRF_ERROR_NOT_FOUND; + + if (NULL != p_addr) + { + DM_TRC("[DM]: Searching for device 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X.\r\n", + p_addr->addr[0], + p_addr->addr[1], + p_addr->addr[2], + p_addr->addr[3], + p_addr->addr[4], + p_addr->addr[5]); + } + + for (index = 0; index < DEVICE_MANAGER_MAX_BONDS; index++) + { + DM_TRC("[DM]:[DI 0x%02X]: Device type 0x%02X.\r\n", + index, m_peer_table[index].peer_id.id_addr_info.addr_type); + DM_TRC("[DM]: Device Addr 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X.\r\n", + m_peer_table[index].peer_id.id_addr_info.addr[0], + m_peer_table[index].peer_id.id_addr_info.addr[1], + m_peer_table[index].peer_id.id_addr_info.addr[2], + m_peer_table[index].peer_id.id_addr_info.addr[3], + m_peer_table[index].peer_id.id_addr_info.addr[4], + m_peer_table[index].peer_id.id_addr_info.addr[5]); + + if (((NULL == p_addr) && (ediv == m_peer_table[index].ediv)) || + ((NULL != p_addr) && (memcmp(&m_peer_table[index].peer_id.id_addr_info, p_addr, sizeof(ble_gap_addr_t)) == 0))) + { + DM_LOG("[DM]: Found device at instance 0x%02X\r\n", index); + + (*p_device_index) = index; + err_code = NRF_SUCCESS; + + break; + } + } + + return err_code; +} + + +/**@brief Function for notifying connection manager event to the application. + * + * @param[in] p_handle Device handle identifying device. + * @param[in] p_event Connection manager event details. + * @param[in] event_result Event result code. + */ +static __INLINE void app_evt_notify(dm_handle_t const * const p_handle, + dm_event_t const * const p_event, + uint32_t event_result) +{ + dm_event_cb_t app_cb = m_application_table[0].ntf_cb; + + DM_MUTEX_UNLOCK(); + + DM_TRC("[DM]: Notifying application of event 0x%02X\r\n", p_event->event_id); + + //No need to do any kind of return value processing thus can be supressed. + UNUSED_VARIABLE(app_cb(p_handle, p_event, event_result)); + + DM_MUTEX_LOCK(); +} + + +/**@brief Function for allocating instance. + * + * @details The instance identifier is provided in the 'p_instance' parameter if the routine + * succeeds. + * + * @param[out] p_instance Connection instance. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_NO_MEM Operation failure. No memory. + */ +static __INLINE uint32_t connection_instance_allocate(uint32_t * p_instance) +{ + uint32_t err_code; + + DM_TRC("[DM]: Request to allocation connection instance\r\n"); + + err_code = connection_instance_find(BLE_CONN_HANDLE_INVALID, STATE_IDLE, p_instance); + + if (err_code == NRF_SUCCESS) + { + DM_LOG("[DM]:[%02X]: Connection Instance Allocated.\r\n", (*p_instance)); + m_connection_table[*p_instance].state = STATE_CONNECTED; + } + else + { + DM_LOG("[DM]: No free connection instances available\r\n"); + err_code = NRF_ERROR_NO_MEM; + } + + return err_code; +} + + +/**@brief Function for freeing instance. Instance identifier is provided in the parameter + * 'p_instance' in case the routine succeeds. + * + * @param[in] p_instance Connection instance. + */ +static __INLINE void connection_instance_free(uint32_t const * p_instance) +{ + DM_TRC("[DM]:[CI 0x%02X]: Freeing connection instance\r\n", (*p_instance)); + + if (m_connection_table[*p_instance].state != STATE_IDLE) + { + DM_LOG("[DM]:[%02X]: Freed connection instance.\r\n", (*p_instance)); + connection_instance_init(*p_instance); + } +} + + +/**@brief Function for storage operation dummy handler. + * + * @param[in] p_dest Destination address where data is to be stored persistently. + * @param[in] p_src Source address containing data to be stored. API assumes this to be resident + * memory and no intermediate copy of data is made by the API. + * @param[in] size Size of data to be stored expressed in bytes. Should be word aligned. + * @param[in] offset Offset in bytes to be applied when writing to the block. + * For example, if within a block of 100 bytes, application wishes to + * write 20 bytes at offset of 12, then this field should be set to 12. + * Should be word aligned. + * + * @retval NRF_SUCCESS Operation success. + */ +static uint32_t storage_operation_dummy_handler(pstorage_handle_t * p_dest, + uint8_t * p_src, + pstorage_size_t size, + pstorage_size_t offset) +{ + return NRF_SUCCESS; +} + + +/**@brief Function for saving the device context persistently. + * + * @param[in] p_handle Device handle identifying device. + * @param[in] state Device store state. + */ +static __INLINE void device_context_store(dm_handle_t const * p_handle, device_store_state_t state) +{ + pstorage_handle_t block_handle; + storage_operation store_fn; + ret_code_t err_code; + + DM_LOG("[DM]: --> device_context_store\r\n"); + + err_code = pstorage_block_identifier_get(&m_storage_handle, + p_handle->device_id, + &block_handle); + + if (err_code == NRF_SUCCESS) + { + if ((STATE_BOND_INFO_UPDATE == + (m_connection_table[p_handle->connection_id].state & STATE_BOND_INFO_UPDATE)) || + (state == UPDATE_PEER_ADDR)) + { + DM_LOG("[DM]:[DI %02X]:[CI %02X]: -> Updating bonding information.\r\n", + p_handle->device_id, p_handle->connection_id); + + store_fn = pstorage_update; + } + else if (state == FIRST_BOND_STORE) + { + dbgMsg("[DM]:[DI %02X]:[CI %02X]: -> Storing bonding information.\r\n"); + dbgMsgn(p_handle->device_id); + dbgMsgn(p_handle->connection_id); +dbgMsg("\r\n"); + store_fn = pstorage_store; + } + else + { + DM_LOG("[DM]:[DI %02X]:[CI %02X]: -> No update in bonding information.\r\n", + p_handle->device_id, p_handle->connection_id); + + //No operation needed. + store_fn = storage_operation_dummy_handler; + } + + //Store the peer id. + err_code = store_fn(&block_handle, + (uint8_t *)&m_peer_table[p_handle->device_id], + PEER_ID_SIZE, + PEER_ID_STORAGE_OFFSET); +dbgMsg("bond info, src: "); dbgMsgn((uint8_t *)&m_peer_table[p_handle->device_id]); +dbgMsg(", size : "); dbgMsgn(PEER_ID_SIZE); dbgMsg(", offset : "); dbgMsgn(PEER_ID_STORAGE_OFFSET);dbgMsg("\r\n"); + if ((err_code == NRF_SUCCESS) && (state != UPDATE_PEER_ADDR)) + { + m_connection_table[p_handle->connection_id].state &= (~STATE_BOND_INFO_UPDATE); + + //Store the bond information. + err_code = store_fn(&block_handle, + (uint8_t *)&m_bond_table[p_handle->connection_id], + BOND_SIZE, + BOND_STORAGE_OFFSET); + + if (err_code != NRF_SUCCESS) + { + DM_ERR("[DM]:[0x%02X]:Failed to store bond information, reason 0x%08X\r\n", + p_handle->device_id, err_code); + } + } + + if (state != UPDATE_PEER_ADDR) + { + //Store the service information + err_code = m_service_context_store[m_application_table[p_handle->appl_id].service] + ( + &block_handle, + p_handle + ); + + if (err_code != NRF_SUCCESS) + { + //Notify application of an error event. + DM_ERR("[DM]: Failed to store service context, reason %08X\r\n", err_code); + } + } + } + + if (err_code != NRF_SUCCESS) + { + //Notify application of an error event. + DM_ERR("[DM]: Failed to store device context, reason %08X\r\n", err_code); + } +} + + +/**@brief Function for storing when there is no service registered. + * + * @param[in] p_block_handle Storage block identifier. + * @param[in] p_handle Device handle identifying device that is loaded. + * + * @retval NRF_SUCCESS + */ +static __INLINE ret_code_t no_service_context_store(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle) +{ + DM_LOG("[DM]: --> no_service_context_store\r\n"); + + return NRF_SUCCESS; +} + + +/**@brief Function for storing GATT Server context. + * + * @param[in] p_block_handle Storage block identifier. + * @param[in] p_handle Device handle identifying device that is stored. + * + * @retval NRF_SUCCESS Operation success. + */ +static __INLINE ret_code_t gatts_context_store(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle) +{ + storage_operation store_fn; + uint32_t attr_flags = BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS | BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS; + uint16_t attr_len = DM_GATT_SERVER_ATTR_MAX_SIZE; + uint8_t sys_data[DM_GATT_SERVER_ATTR_MAX_SIZE]; + + DM_LOG("[DM]: --> gatts_context_store\r\n"); + + uint32_t err_code = sd_ble_gatts_sys_attr_get( + m_connection_table[p_handle->connection_id].conn_handle, + sys_data, + &attr_len, + attr_flags); + + if (err_code == NRF_SUCCESS) + { + if (memcmp(m_gatts_table[p_handle->connection_id].attributes, sys_data, attr_len) == 0) + { + //No store operation is needed. + DM_LOG("[DM]:[0x%02X]: No change in GATTS Context information.\r\n", + p_handle->device_id); + + if ((m_connection_table[p_handle->connection_id].state & STATE_CONNECTED) != + STATE_CONNECTED) + { + DM_LOG("[DM]:[0x%02X]: Resetting GATTS for active instance.\r\n", + p_handle->connection_id); + + //Reset GATTS information for the current context. + memset(&m_gatts_table[p_handle->connection_id], 0, sizeof(dm_gatts_context_t)); + } + } + else + { + if (m_gatts_table[p_handle->connection_id].size != 0) + { + //There is data already stored in persistent memory, therefore an update is needed. + DM_LOG("[DM]:[0x%02X]: Updating stored service context\r\n", p_handle->device_id); + + store_fn = pstorage_update; + } + else + { + //Fresh write, a store is needed. + DM_LOG("[DM]:[0x%02X]: Storing service context\r\n", p_handle->device_id); + + store_fn = pstorage_store; + } + + m_gatts_table[p_handle->connection_id].flags = attr_flags; + m_gatts_table[p_handle->connection_id].size = attr_len; + memcpy(m_gatts_table[p_handle->connection_id].attributes, sys_data, attr_len); + + DM_DUMP((uint8_t *)&m_gatts_table[p_handle->connection_id], sizeof(dm_gatts_context_t)); + + DM_LOG("[DM]:[0x%02X]: GATTS Data size 0x%08X\r\n", + p_handle->device_id, + m_gatts_table[p_handle->connection_id].size); + + //Store GATTS information. + err_code = store_fn((pstorage_handle_t *)p_block_handle, + (uint8_t *)&m_gatts_table[p_handle->connection_id], + GATTS_SERVICE_CONTEXT_SIZE, + SERVICE_STORAGE_OFFSET); + + if (err_code != NRF_SUCCESS) + { + DM_ERR("[DM]:[0x%02X]:Failed to store service context, reason 0x%08X\r\n", + p_handle->device_id, + err_code); + } + else + { + DM_LOG("[DM]: Service context successfully stored.\r\n"); + } + } + } + + return NRF_SUCCESS; +} + + +/**@brief Function for storing GATT Client context. + * + * @param[in] p_block_handle Storage block identifier. + * @param[in] p_handle Device handle identifying device that is stored. + * + * @retval NRF_SUCCESS Operation success. + */ +static __INLINE ret_code_t gattc_context_store(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle) +{ + DM_LOG("[DM]: --> gattc_context_store\r\n"); + + return NRF_SUCCESS; +} + + +/**@brief Function for storing GATT Server & Client context. + * + * @param[in] p_block_handle Storage block identifier. + * @param[in] p_handle Device handle identifying device that is stored. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + */ +static __INLINE ret_code_t gattsc_context_store(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle) +{ + DM_LOG("[DM]: --> gattsc_context_store\r\n"); + + ret_code_t err_code = gatts_context_store(p_block_handle, p_handle); + + if (NRF_SUCCESS == err_code) + { + err_code = gattc_context_store(p_block_handle, p_handle); + } + + return err_code; +} + + +/**@brief Function for loading when there is no service registered. + * + * @param[in] p_block_handle Storage block identifier. + * @param[in] p_handle Device handle identifying device that is loaded. + * + * @retval NRF_SUCCESS + */ +static __INLINE ret_code_t no_service_context_load(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle) +{ + DM_LOG("[DM]: --> no_service_context_load\r\n"); + + return NRF_SUCCESS; +} + + +/**@brief Function for loading GATT Server context. + * + * @param[in] p_block_handle Storage block identifier. + * @param[in] p_handle Device handle identifying device that is loaded. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + */ +static __INLINE ret_code_t gatts_context_load(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle) +{ + DM_LOG("[DM]:[CI 0x%02X]:[DI 0x%02X]: --> gatts_context_load\r\n", + p_handle->connection_id, + p_handle->device_id); + + ret_code_t err_code = pstorage_load((uint8_t *)&m_gatts_table[p_handle->connection_id], + (pstorage_handle_t *)p_block_handle, + GATTS_SERVICE_CONTEXT_SIZE, + SERVICE_STORAGE_OFFSET); + + if (err_code == NRF_SUCCESS) + { + DM_LOG("[DM]:[%02X]:[Block ID 0x%08X]: Service context loaded, size 0x%08X\r\n", + p_handle->connection_id, + p_block_handle->block_id, + m_gatts_table[p_handle->connection_id].size); + DM_DUMP((uint8_t *)&m_gatts_table[p_handle->connection_id], sizeof(dm_gatts_context_t)); + + if (m_gatts_table[p_handle->connection_id].size == DM_GATTS_INVALID_SIZE) + { + m_gatts_table[p_handle->connection_id].size = 0; + } + } + else + { + DM_ERR("[DM]:[%02X]: Failed to load Service context, reason %08X\r\n", + p_handle->connection_id, + err_code); + } + + return err_code; +} + + +/**@brief Function for loading GATT Client context. + * + * @param[in] p_block_handle Storage block identifier. + * @param[in] p_handle Device handle identifying device that is loaded. + * + * @retval NRF_SUCCESS + */ +static __INLINE ret_code_t gattc_context_load(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle) +{ + DM_LOG("[DM]: --> gattc_context_load\r\n"); + + return NRF_SUCCESS; +} + + +/**@brief Function for loading GATT Server & Client context. + * + * @param[in] p_block_handle Storage block identifier. + * @param[in] p_handle Device handle identifying device that is loaded. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + */ +static __INLINE ret_code_t gattsc_context_load(pstorage_handle_t const * p_block_handle, + dm_handle_t const * p_handle) +{ + DM_LOG("[DM]: --> gattsc_context_load\r\n"); + + ret_code_t err_code = gatts_context_load(p_block_handle, p_handle); + + if (NRF_SUCCESS == err_code) + { + err_code = gattc_context_load(p_block_handle, p_handle); + } + + return err_code; +} + + +/**@brief Function for applying when there is no service registered. + * + * @param[in] p_handle Device handle identifying device that is applied. + * + * @retval NRF_SUCCESS + */ +static __INLINE ret_code_t no_service_context_apply(dm_handle_t * p_handle) +{ + DM_LOG("[DM]: --> no_service_context_apply\r\n"); + DM_LOG("[DM]:[CI 0x%02X]: No Service context\r\n", p_handle->connection_id); + + return NRF_SUCCESS; +} + + +/**@brief Function for applying GATT Server context. + * + * @param[in] p_handle Device handle identifying device that is applied. + * + * @retval NRF_SUCCESS On success. + * @retval DM_SERVICE_CONTEXT_NOT_APPLIED On failure. + */ +static __INLINE ret_code_t gatts_context_apply(dm_handle_t * p_handle) +{ + uint32_t err_code; + + uint8_t * p_gatts_context = NULL; + uint16_t context_len = 0; + uint32_t context_flags = 0; + + DM_LOG("[DM]: --> gatts_context_apply\r\n"); + DM_LOG("[DM]:[CI 0x%02X]: State 0x%02X, Size 0x%08X\r\n", + p_handle->connection_id, + m_connection_table[p_handle->connection_id].state, + m_gatts_table[p_handle->connection_id].size); + + if ((m_gatts_table[p_handle->connection_id].size != 0) && + ( + ((m_connection_table[p_handle->connection_id].state & STATE_LINK_ENCRYPTED) == STATE_LINK_ENCRYPTED) && + ((m_connection_table[p_handle->connection_id].state & STATE_BOND_INFO_UPDATE) + != STATE_BOND_INFO_UPDATE) + ) + ) + { + DM_LOG("[DM]: Setting stored context.\r\n"); + + p_gatts_context = &m_gatts_table[p_handle->connection_id].attributes[0]; + context_len = m_gatts_table[p_handle->connection_id].size; + context_flags = m_gatts_table[p_handle->connection_id].flags; + } + + err_code = sd_ble_gatts_sys_attr_set(m_connection_table[p_handle->connection_id].conn_handle, + p_gatts_context, + context_len, + context_flags); + + if (err_code == NRF_ERROR_INVALID_DATA) + { + // Indication that the ATT table has changed. Restore the system attributes to system + // services only and send a service changed indication if possible. + context_flags = BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS; + err_code = sd_ble_gatts_sys_attr_set(m_connection_table[p_handle->connection_id].conn_handle, + p_gatts_context, + context_len, + context_flags); + } + + if (err_code != NRF_SUCCESS) + { + DM_LOG("[DM]: Failed to set system attributes, reason 0x%08X.\r\n", err_code); + + err_code = DM_SERVICE_CONTEXT_NOT_APPLIED; + } + + if (context_flags == BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS) + { + err_code = sd_ble_gatts_service_changed(m_connection_table[p_handle->connection_id].conn_handle, + 0x000C, + 0xFFFF); + if (err_code != NRF_SUCCESS) + { + DM_LOG("[DM]: Failed to send Service Changed indication, reason 0x%08X.\r\n", err_code); + if ((err_code != BLE_ERROR_INVALID_CONN_HANDLE) && + (err_code != NRF_ERROR_INVALID_STATE) && + (err_code != BLE_ERROR_NO_TX_PACKETS) && + (err_code != NRF_ERROR_BUSY)) + { + // Those errors can be expected when sending trying to send Service Changed + // Indication if the CCCD is not set to indicate. Thus set the returning error + // code to success. + err_code = NRF_SUCCESS; + } + else + { + err_code = DM_SERVICE_CONTEXT_NOT_APPLIED; + } + } + } + + return err_code; +} + + +/**@brief Function for applying GATT Client context. + * + * @param[in] p_handle Device handle identifying device that is applied. + * + * @retval NRF_SUCCESS On success. + */ +static __INLINE ret_code_t gattc_context_apply(dm_handle_t * p_handle) +{ + DM_LOG("[DM]: --> gattc_context_apply\r\n"); + + return NRF_SUCCESS; +} + + +/**@brief Function for applying GATT Server & Client context. + * + * @param[in] p_handle Device handle identifying device that is applied. + * + * @retval NRF_SUCCESS On success, else an error code indicating reason for failure. + */ +static __INLINE ret_code_t gattsc_context_apply(dm_handle_t * p_handle) +{ + uint32_t err_code; + + DM_LOG("[DM]: --> gattsc_context_apply\r\n"); + + err_code = gatts_context_apply(p_handle); + + if (err_code == NRF_SUCCESS) + { + err_code = gattc_context_apply(p_handle); + } + + return err_code; +} + + +/**@brief Function for pstorage module callback. + * + * @param[in] p_handle Identifies module and block for which callback is received. + * @param[in] op_code Identifies the operation for which the event is notified. + * @param[in] result Identifies the result of flash access operation. + * NRF_SUCCESS implies, operation succeeded. + * @param[in] p_data Identifies the application data pointer. In case of store operation, this + * points to the resident source of application memory that application can now + * free or reuse. In case of clear, this is NULL as no application pointer is + * needed for this operation. + * @param[in] data_len Length of data provided by the application for the operation. + */ +static void dm_pstorage_cb_handler(pstorage_handle_t * p_handle, + uint8_t op_code, + uint32_t result, + uint8_t * p_data, + uint32_t data_len) +{ + VERIFY_APP_REGISTERED_VOID(0); + + if (data_len > ALL_CONTEXT_SIZE) + { + //Clearing of all bonds at initialization, no event is generated. + return; + } + + DM_MUTEX_LOCK(); + + dm_event_t dm_event; + dm_handle_t dm_handle; + dm_context_t context_data; + pstorage_handle_t block_handle; + uint32_t index_count; + uint32_t err_code; + + bool app_notify = true; + + err_code = dm_handle_initialize(&dm_handle); + APP_ERROR_CHECK(err_code); + + dm_handle.appl_id = 0; + dm_event.event_id = 0x00; + + //Construct the event which it is related to. + + //Initialize context data information and length. + context_data.p_data = p_data; + context_data.len = data_len; + + for (uint32_t index = 0; index < DEVICE_MANAGER_MAX_BONDS; index++) + { + err_code = pstorage_block_identifier_get(&m_storage_handle, index, &block_handle); + if ((err_code == NRF_SUCCESS) && + ( + (memcmp(p_handle, &block_handle, sizeof(pstorage_handle_t)) == 0) + ) + ) + { + dm_handle.device_id = index; + break; + } + } + + if (dm_handle.device_id != DM_INVALID_ID) + { + if (op_code == PSTORAGE_CLEAR_OP_CODE) + { + if (data_len == ALL_CONTEXT_SIZE) + { + dm_event.event_id = DM_EVT_DEVICE_CONTEXT_BASE; + } + else + { + dm_event.event_id = DM_EVT_APP_CONTEXT_BASE; + } + } + else + { + //Update or store operation. + //Context is identified based on the pointer value. Device context, application context + //and service context all have their own value range. + index_count = ((uint32_t)(p_data - (uint8_t *)m_peer_table)) / PEER_ID_SIZE; + + if (index_count < DEVICE_MANAGER_MAX_BONDS) + { + dm_event.event_param.p_device_context = &context_data; + + //Only the peer identification is stored, not bond information. Hence do not notify + //the application yet, unless the store operation resulted in a failure. + if ((result == NRF_SUCCESS) && + ( + (update_status_bit_is_set(dm_handle.device_id) == false) + ) + ) + { + app_notify = false; + } + else + { + //Reset update status since update is complete. + update_status_bit_reset(dm_handle.device_id); + + //Notify application of error in storing the context. + dm_event.event_id = DM_EVT_DEVICE_CONTEXT_BASE; + } + } + else + { + index_count = ((uint32_t)(p_data - (uint8_t *)m_bond_table)) / BOND_SIZE; + + if (index_count < DEVICE_MANAGER_MAX_CONNECTIONS) + { + DM_LOG("[DM]:[0x%02X]:[0x%02X]: Bond context Event\r\n", + dm_handle.device_id, + dm_handle.connection_id); + + dm_event.event_param.p_device_context = &context_data; + dm_event.event_id = DM_EVT_DEVICE_CONTEXT_BASE; + dm_handle.connection_id = index_count; + + ble_gap_sec_keyset_t keys_exchanged; + keys_exchanged.keys_peer.p_enc_key = NULL; + keys_exchanged.keys_peer.p_id_key = &m_local_id_info; + keys_exchanged.keys_own.p_enc_key = &m_bond_table[index_count].peer_enc_key; + keys_exchanged.keys_own.p_id_key = &m_peer_table[dm_handle.device_id].peer_id; + + //Context information updated to provide the keys. + context_data.p_data = (uint8_t *)&keys_exchanged; + context_data.len = sizeof(ble_gap_sec_keyset_t); + } + else + { + index_count = ((uint32_t)(p_data - (uint8_t *)m_gatts_table)) / + GATTS_SERVICE_CONTEXT_SIZE; + + if (index_count < DEVICE_MANAGER_MAX_CONNECTIONS) + { + DM_LOG("[DM]:[0x%02X]:[0x%02X]: Service context Event\r\n", + dm_handle.device_id, + dm_handle.connection_id); + + //Notify application. + dm_event.event_id = DM_EVT_SERVICE_CONTEXT_BASE; + dm_handle.connection_id = index_count; + dm_handle.service_id = DM_PROTOCOL_CNTXT_GATT_SRVR_ID; + + //Reset the service context now that it was successfully written to the + //application and the link is disconnected. + if ((m_connection_table[index_count].state & STATE_CONNECTED) != + STATE_CONNECTED) + { + DM_LOG("[DM]:[0x%02X]:[0x%02X]: Resetting bond information for " + "active instance.\r\n", + dm_handle.device_id, + dm_handle.connection_id); + + memset(&m_gatts_table[dm_handle.connection_id], + 0, + sizeof(dm_gatts_context_t)); + } + } + else + { + DM_LOG("[DM]:[0x%02X]:[0x%02X]: App context Event\r\n", + dm_handle.device_id, + dm_handle.connection_id); + + app_notify = false; + dm_event.event_id = DM_EVT_APP_CONTEXT_BASE; +#if (DEVICE_MANAGER_APP_CONTEXT_SIZE != 0) + + if (p_data == (uint8_t *)(&m_context_init_len)) + { + //Context data is deleted. + //This is a workaround to get the right event as on delete operation + //update operation is used instead of clear. + op_code = PSTORAGE_CLEAR_OP_CODE; + app_notify = true; + } + else if (m_app_context_table[dm_handle.device_id] == p_data) + { + app_notify = true; + dm_event.event_param.p_app_context = &context_data; + + //Verify if the device is connected, if yes set connection instance. + for (uint32_t index = 0; + index < DEVICE_MANAGER_MAX_CONNECTIONS; + index++) + { + if (dm_handle.device_id == m_connection_table[index].bonded_dev_id) + { + dm_handle.connection_id = index; + break; + } + } + } + else + { + //No implementation needed. + } +#endif //DEVICE_MANAGER_APP_CONTEXT_SIZE + } + } + } + } + + if (app_notify == true) + { + if (op_code == PSTORAGE_CLEAR_OP_CODE) + { + dm_event.event_id |= DM_CLEAR_OPERATION_ID; + } + else if (op_code == PSTORAGE_LOAD_OP_CODE) + { + dm_event.event_id |= DM_LOAD_OPERATION_ID; + } + else + { + dm_event.event_id |= DM_STORE_OPERATION_ID; + } + + dm_event.event_param.p_app_context = &context_data; + app_evt_notify(&dm_handle, &dm_event, result); + } + } + + DM_MUTEX_UNLOCK(); +} + + +ret_code_t dm_init(dm_init_param_t const * const p_init_param) +{ + pstorage_module_param_t param; + pstorage_handle_t block_handle; + ret_code_t err_code; + uint32_t index; + + DM_LOG("[DM]: >> dm_init.\r\n"); + + NULL_PARAM_CHECK(p_init_param); + + SDK_MUTEX_INIT(m_dm_mutex); + + DM_MUTEX_LOCK(); + + for (index = 0; index < DEVICE_MANAGER_MAX_APPLICATIONS; index++) + { + application_instance_init(index); + } + + for (index = 0; index < DEVICE_MANAGER_MAX_CONNECTIONS; index++) + { + connection_instance_init(index); + } + + memset(m_gatts_table, 0, sizeof(m_gatts_table)); + + //Initialization of all device instances. + for (index = 0; index < DEVICE_MANAGER_MAX_BONDS; index++) + { + peer_instance_init(index); + m_irk_index_table[index] = DM_INVALID_ID; + } + + //All context with respect to a particular device is stored contiguously. + param.block_size = ALL_CONTEXT_SIZE; + param.block_count = DEVICE_MANAGER_MAX_BONDS; + param.cb = dm_pstorage_cb_handler; + + err_code = pstorage_register(¶m, &m_storage_handle); + + if (err_code == NRF_SUCCESS) + { + m_module_initialized = true; + + if (p_init_param->clear_persistent_data == false) + { + DM_LOG("[DM]: Storage handle 0x%08X.\r\n", m_storage_handle.block_id); + + //Copy bonded peer device address and IRK to RAM table. + + //Bonded devices are stored in range (0,DEVICE_MANAGER_MAX_BONDS-1). The remaining + //range is for active connections that may or may not be bonded. + for (index = 0; index < DEVICE_MANAGER_MAX_BONDS; index++) + { + err_code = pstorage_block_identifier_get(&m_storage_handle, index, &block_handle); + + //Issue read request if you successfully get the block identifier. + if (err_code == NRF_SUCCESS) + { + DM_TRC("[DM]:[0x%02X]: Block handle 0x%08X.\r\n", index, block_handle.block_id); + + err_code = pstorage_load((uint8_t *)&m_peer_table[index], + &block_handle, + sizeof(peer_id_t), + 0); + + if (err_code != NRF_SUCCESS) + { + // In case a peer device could not be loaded successfully, rest of the + // initialization procedure are skipped and an error is sent to the + // application. + DM_ERR( + "[DM]: Failed to load peer device %08X from storage, reason %08X.\r\n", + index, + err_code); + + m_module_initialized = false; + break; + } + else + { + DM_TRC("[DM]:[DI 0x%02X]: Device type 0x%02X.\r\n", + index, + m_peer_table[index].peer_id.id_addr_info.addr_type); + DM_TRC("[DM]: Device Addr 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X.\r\n", + m_peer_table[index].peer_id.id_addr_info.addr[0], + m_peer_table[index].peer_id.id_addr_info.addr[1], + m_peer_table[index].peer_id.id_addr_info.addr[2], + m_peer_table[index].peer_id.id_addr_info.addr[3], + m_peer_table[index].peer_id.id_addr_info.addr[4], + m_peer_table[index].peer_id.id_addr_info.addr[5]); + } + } + else + { + //In case a peer device could not be loaded successfully, rest of the + //initialization procedure are skipped and an error is sent to the application. + DM_LOG("[DM]: Failed to get block handle for instance %08X, reason %08X.\r\n", + index, + err_code); + + m_module_initialized = false; + break; + } + } + } + else + { + err_code = pstorage_clear(&m_storage_handle, (param.block_size * param.block_count)); + DM_ERR("[DM]: Successfully requested clear of persistent data.\r\n"); + } + } + else + { + DM_ERR("[DM]: Failed to register with storage module, reason 0x%08X.\r\n", err_code); + } + + DM_MUTEX_UNLOCK(); + + DM_TRC("[DM]: << dm_init.\r\n"); + + return err_code; +} + + +ret_code_t dm_register(dm_application_instance_t * p_appl_instance, + dm_application_param_t const * p_appl_param) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_appl_instance); + NULL_PARAM_CHECK(p_appl_param); + NULL_PARAM_CHECK(p_appl_param->evt_handler); + + DM_MUTEX_LOCK(); + + DM_LOG("[DM]: >> dm_register.\r\n"); + + uint32_t err_code; + + //Verify if an application instance is available. Currently only one instance is supported. + if (m_application_table[0].ntf_cb == NULL) + { + DM_LOG("[DM]: Application Instance allocated.\r\n"); + + //Mark instance as allocated. + m_application_table[0].ntf_cb = p_appl_param->evt_handler; + m_application_table[0].sec_param = p_appl_param->sec_param; + m_application_table[0].service = p_appl_param->service_type; + + m_application_table[0].sec_param.kdist_peer.enc = 0; + m_application_table[0].sec_param.kdist_peer.id = 1; + m_application_table[0].sec_param.kdist_peer.sign = 0; + m_application_table[0].sec_param.kdist_own.enc = 1; + m_application_table[0].sec_param.kdist_own.id = 1; + m_application_table[0].sec_param.kdist_own.sign = 0; + //Populate application's instance variable with the assigned allocation instance. + *p_appl_instance = 0; + err_code = NRF_SUCCESS; + } + else + { + err_code = (NRF_ERROR_NO_MEM | DEVICE_MANAGER_ERR_BASE); + } + + DM_MUTEX_UNLOCK(); + + DM_TRC("[DM]: << dm_register.\r\n"); + + return err_code; +} + + +ret_code_t dm_security_setup_req(dm_handle_t * p_handle) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_CONNECTION_INSTANCE(p_handle->connection_id); + + DM_MUTEX_LOCK(); + + DM_LOG("[DM]: >> dm_security_setup_req\r\n"); + + uint32_t err_code = (NRF_ERROR_INVALID_STATE | DEVICE_MANAGER_ERR_BASE); + + if ((m_connection_table[p_handle->connection_id].state & STATE_CONNECTED) == STATE_CONNECTED) + { + err_code = sd_ble_gap_authenticate(m_connection_table[p_handle->connection_id].conn_handle, + &m_application_table[0].sec_param); + } + + DM_TRC("[DM]: << dm_security_setup_req, 0x%08X\r\n", err_code); + + DM_MUTEX_UNLOCK(); + + return err_code; +} + + +ret_code_t dm_security_status_req(dm_handle_t const * p_handle, + dm_security_status_t * p_status) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_status); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_CONNECTION_INSTANCE(p_handle->connection_id); + + DM_MUTEX_LOCK(); + + DM_LOG("[DM]: >> dm_security_status_req\r\n"); + + if ((m_connection_table[p_handle->connection_id].state & STATE_PAIRING) || + (m_connection_table[p_handle->connection_id].state & STATE_PAIRING_PENDING)) + { + (*p_status) = ENCRYPTION_IN_PROGRESS; + } + else if (m_connection_table[p_handle->connection_id].state & STATE_LINK_ENCRYPTED) + { + (*p_status) = ENCRYPTED; + } + else + { + (*p_status) = NOT_ENCRYPTED; + } + + DM_TRC("[DM]: << dm_security_status_req\r\n"); + + DM_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +ret_code_t dm_whitelist_create(dm_application_instance_t const * p_handle, + ble_gap_whitelist_t * p_whitelist) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_whitelist); + NULL_PARAM_CHECK(p_whitelist->pp_addrs); + NULL_PARAM_CHECK(p_whitelist->pp_irks); + VERIFY_APP_REGISTERED(*p_handle); + + DM_MUTEX_LOCK(); + + DM_LOG("[DM]: >> dm_whitelist_create\r\n"); + + uint32_t addr_count = 0; + uint32_t irk_count = 0; + bool connected = false; + + for (uint32_t index = 0; index < DEVICE_MANAGER_MAX_BONDS; index++) + { + connected = false; + + for (uint32_t c_index = 0; c_index < DEVICE_MANAGER_MAX_CONNECTIONS; c_index++) + { + if ((index == m_connection_table[c_index].bonded_dev_id) && + ((m_connection_table[c_index].state & STATE_CONNECTED) == STATE_CONNECTED)) + { + connected = true; + break; + } + } + + if (connected == false) + { + if ((irk_count < p_whitelist->irk_count) && + ((m_peer_table[index].id_bitmap & IRK_ENTRY) == 0)) + { + p_whitelist->pp_irks[irk_count] = &m_peer_table[index].peer_id.id_info; + m_irk_index_table[irk_count] = index; + irk_count++; + } + + if ((addr_count < p_whitelist->addr_count) && + (m_peer_table[index].id_bitmap & ADDR_ENTRY) == 0) + { + p_whitelist->pp_addrs[addr_count] = &m_peer_table[index].peer_id.id_addr_info; + addr_count++; + } + } + } + + p_whitelist->addr_count = addr_count; + p_whitelist->irk_count = irk_count; + + DM_LOG("[DM]: Created whitelist, number of IRK = 0x%02X, number of addr = 0x%02X\r\n", + irk_count, + addr_count); + + DM_TRC("[DM]: << dm_whitelist_create\r\n"); + + DM_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +ret_code_t dm_device_add(dm_handle_t * p_handle, + dm_device_context_t const * p_context) +{ + return (API_NOT_IMPLEMENTED | DEVICE_MANAGER_ERR_BASE); +} + + +ret_code_t dm_device_delete(dm_handle_t const * p_handle) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_DEVICE_INSTANCE(p_handle->device_id); + + DM_MUTEX_LOCK(); + + DM_TRC("[DM]: >> dm_device_delete\r\n"); + + uint32_t err_code = device_instance_free(p_handle->device_id); + + DM_TRC("[DM]: << dm_device_delete\r\n"); + + DM_MUTEX_UNLOCK(); + + return err_code; +} + + +ret_code_t dm_device_delete_all(dm_application_instance_t const * p_handle) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + VERIFY_APP_REGISTERED((*p_handle)); + + DM_MUTEX_LOCK(); + + uint32_t err_code = NRF_SUCCESS; + + DM_TRC("[DM]: >> dm_device_delete_all\r\n"); + + for (uint32_t index = 0; index < DEVICE_MANAGER_MAX_BONDS; index++) + { + if (m_peer_table[index].id_bitmap != UNASSIGNED) + { + err_code = device_instance_free(index); + } + } + + DM_TRC("[DM]: << dm_device_delete_all\r\n"); + + DM_MUTEX_UNLOCK(); + + return err_code; +} + + +ret_code_t dm_service_context_set(dm_handle_t const * p_handle, + dm_service_context_t const * p_context) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_context); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_CONNECTION_INSTANCE(p_handle->connection_id); + VERIFY_DEVICE_INSTANCE(p_handle->device_id); + + DM_MUTEX_LOCK(); + + DM_TRC("[DM]: >> dm_service_context_set\r\n"); + + if ((p_context->context_data.p_data != NULL) && + ( + (p_context->context_data.len != 0) && + (p_context->context_data.len < DM_GATT_SERVER_ATTR_MAX_SIZE) + ) + ) + { + if (p_context->service_type == DM_PROTOCOL_CNTXT_GATT_SRVR_ID) + { + memcpy(m_gatts_table[p_handle->connection_id].attributes, + p_context->context_data.p_data, + p_context->context_data.len); + } + } + + pstorage_handle_t block_handle; + uint32_t err_code = pstorage_block_identifier_get(&m_storage_handle, + p_handle->device_id, + &block_handle); + + err_code = m_service_context_store[p_context->service_type](&block_handle, p_handle); + + DM_TRC("[DM]: << dm_service_context_set\r\n"); + + DM_MUTEX_UNLOCK(); + + return err_code; +} + + +ret_code_t dm_service_context_get(dm_handle_t const * p_handle, + dm_service_context_t * p_context) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_context); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_DEVICE_INSTANCE(p_handle->device_id); + + if ((m_connection_table[p_handle->connection_id].state & STATE_CONNECTED) != STATE_CONNECTED) + { + DM_TRC("[DM]: Device must be connected to get context. \r\n"); + + return (FEATURE_NOT_ENABLED | DEVICE_MANAGER_ERR_BASE); + } + + DM_MUTEX_LOCK(); + + DM_TRC("[DM]: >> dm_service_context_get\r\n"); + + if (p_context->service_type == DM_PROTOCOL_CNTXT_GATT_SRVR_ID) + { + p_context->context_data.p_data = m_gatts_table[p_handle->connection_id].attributes; + p_context->context_data.len = m_gatts_table[p_handle->connection_id].size; + } + + pstorage_handle_t block_handle; + uint32_t err_code = pstorage_block_identifier_get(&m_storage_handle, + p_handle->device_id, + &block_handle); + + err_code = m_service_context_load[p_context->service_type](&block_handle, p_handle); + + if (p_context->service_type == DM_PROTOCOL_CNTXT_GATT_SRVR_ID) + { + p_context->context_data.p_data = m_gatts_table[p_handle->connection_id].attributes; + p_context->context_data.len = m_gatts_table[p_handle->connection_id].size; + } + + DM_TRC("[DM]: << dm_service_context_get\r\n"); + + DM_MUTEX_UNLOCK(); + + return err_code; +} + + +ret_code_t dm_service_context_delete(dm_handle_t const * p_handle) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_DEVICE_INSTANCE(p_handle->device_id); + + DM_LOG("[DM]: Context delete is not supported yet.\r\n"); + + return (API_NOT_IMPLEMENTED | DEVICE_MANAGER_ERR_BASE); +} + + +ret_code_t dm_application_context_set(dm_handle_t const * p_handle, + dm_application_context_t const * p_context) +{ +#if (DEVICE_MANAGER_APP_CONTEXT_SIZE != 0) + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_context); + NULL_PARAM_CHECK(p_context->p_data); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_DEVICE_INSTANCE(p_handle->device_id); + VERIFY_DEVICE_BOND(p_handle->connection_id); + SIZE_CHECK_APP_CONTEXT(p_context->len); + + DM_MUTEX_LOCK(); + + DM_TRC("[DM]: >> dm_application_context_set\r\n"); + + uint32_t err_code; + uint32_t context_len; + pstorage_handle_t block_handle; + + storage_operation store_fn = pstorage_store; + + err_code = pstorage_block_identifier_get(&m_storage_handle, + p_handle->device_id, + &block_handle); + + if (err_code == NRF_SUCCESS) + { + err_code = pstorage_load((uint8_t *)&context_len, + &block_handle, + sizeof(uint32_t), + APP_CONTEXT_STORAGE_OFFSET); + + if ((err_code == NRF_SUCCESS) && (context_len != INVALID_CONTEXT_LEN)) + { + //Data already exists. Need an update. + store_fn = pstorage_update; + + DM_LOG("[DM]:[DI 0x%02X]: Updating existing application context, existing len 0x%08X, " + "new length 0x%08X.\r\n", + p_handle->device_id, + context_len, + p_context->len); + } + else + { + DM_LOG("[DM]: Storing application context.\r\n"); + } + + //Store/update context length. + err_code = store_fn(&block_handle, + (uint8_t *)(&p_context->len), + sizeof(uint32_t), + APP_CONTEXT_STORAGE_OFFSET); + + if (err_code == NRF_SUCCESS) + { + //Update context data is used for application context as flash is never + //cleared if a delete of application context is called. + err_code = pstorage_update(&block_handle, + p_context->p_data, + DEVICE_MANAGER_APP_CONTEXT_SIZE, + (APP_CONTEXT_STORAGE_OFFSET + sizeof(uint32_t))); + if (err_code == NRF_SUCCESS) + { + m_app_context_table[p_handle->device_id] = p_context->p_data; + } + } + } + + DM_TRC("[DM]: << dm_application_context_set\r\n"); + + DM_MUTEX_UNLOCK(); + + return err_code; + +#else //DEVICE_MANAGER_APP_CONTEXT_SIZE + return (FEATURE_NOT_ENABLED | DEVICE_MANAGER_ERR_BASE); +#endif //DEVICE_MANAGER_APP_CONTEXT_SIZE +} + + +ret_code_t dm_application_context_get(dm_handle_t const * p_handle, + dm_application_context_t * p_context) +{ +#if (DEVICE_MANAGER_APP_CONTEXT_SIZE != 0) + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_context); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_DEVICE_INSTANCE(p_handle->device_id); + + DM_MUTEX_LOCK(); + + DM_TRC("[DM]: >> dm_application_context_get\r\n"); + + uint32_t context_len; + uint32_t err_code; + pstorage_handle_t block_handle; + + //Check if the context exists. + if (NULL == p_context->p_data) + { + p_context->p_data = m_app_context_table[p_handle->device_id]; + } + else + { + m_app_context_table[p_handle->device_id] = p_context->p_data; + } + + err_code = pstorage_block_identifier_get(&m_storage_handle, + p_handle->device_id, + &block_handle); + + if (err_code == NRF_SUCCESS) + { + err_code = pstorage_load((uint8_t *)&context_len, + &block_handle, + sizeof(uint32_t), + APP_CONTEXT_STORAGE_OFFSET); + + if ((err_code == NRF_SUCCESS) && (context_len != INVALID_CONTEXT_LEN)) + { + err_code = pstorage_load(p_context->p_data, + &block_handle, + DEVICE_MANAGER_APP_CONTEXT_SIZE, + (APP_CONTEXT_STORAGE_OFFSET + sizeof(uint32_t))); + if (err_code == NRF_SUCCESS) + { + p_context->len = context_len; + } + } + else + { + err_code = DM_NO_APP_CONTEXT; + } + } + + DM_TRC("[DM]: << dm_application_context_get\r\n"); + + DM_MUTEX_UNLOCK(); + + return err_code; + +#else //DEVICE_MANAGER_APP_CONTEXT_SIZE + return (FEATURE_NOT_ENABLED | DEVICE_MANAGER_ERR_BASE); +#endif //DEVICE_MANAGER_APP_CONTEXT_SIZE +} + + +ret_code_t dm_application_context_delete(const dm_handle_t * p_handle) +{ +#if (DEVICE_MANAGER_APP_CONTEXT_SIZE != 0) + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_DEVICE_INSTANCE(p_handle->device_id); + + DM_MUTEX_LOCK(); + + DM_TRC("[DM]: >> dm_application_context_delete\r\n"); + + uint32_t err_code; + uint32_t context_len; + pstorage_handle_t block_handle; + + err_code = pstorage_block_identifier_get(&m_storage_handle, + p_handle->device_id, + &block_handle); + + if (err_code == NRF_SUCCESS) + { + err_code = pstorage_load((uint8_t *)&context_len, + &block_handle, + sizeof(uint32_t), + APP_CONTEXT_STORAGE_OFFSET); + + if (context_len != m_context_init_len) + { + err_code = pstorage_update(&block_handle, + (uint8_t *)&m_context_init_len, + sizeof(uint32_t), + APP_CONTEXT_STORAGE_OFFSET); + + if (err_code != NRF_SUCCESS) + { + DM_ERR("[DM]: Failed to delete application context, reason 0x%08X\r\n", err_code); + } + else + { + m_app_context_table[p_handle->device_id] = NULL; + } + } + } + + DM_TRC("[DM]: << dm_application_context_delete\r\n"); + + DM_MUTEX_UNLOCK(); + + return err_code; +#else //DEVICE_MANAGER_APP_CONTEXT_SIZE + return (FEATURE_NOT_ENABLED | DEVICE_MANAGER_ERR_BASE); +#endif //DEVICE_MANAGER_APP_CONTEXT_SIZE +} + + +ret_code_t dm_application_instance_set(dm_application_instance_t const * p_appl_instance, + dm_handle_t * p_handle) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_appl_instance); + VERIFY_APP_REGISTERED((*p_appl_instance)); + + p_handle->appl_id = (*p_appl_instance); + + return NRF_SUCCESS; +} + + +uint32_t dm_handle_initialize(dm_handle_t * p_handle) +{ + NULL_PARAM_CHECK(p_handle); + + p_handle->appl_id = DM_INVALID_ID; + p_handle->connection_id = DM_INVALID_ID; + p_handle->device_id = DM_INVALID_ID; + p_handle->service_id = DM_INVALID_ID; + + return NRF_SUCCESS; +} + + +ret_code_t dm_peer_addr_set(dm_handle_t const * p_handle, + ble_gap_addr_t const * p_addr) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_addr); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_DEVICE_INSTANCE(p_handle->device_id); + + DM_MUTEX_LOCK(); + + DM_TRC("[DM]: >> dm_peer_addr_set\r\n"); + + ret_code_t err_code; + + if ((p_handle->connection_id == DM_INVALID_ID) && + (p_addr->addr_type != BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE)) + { + m_peer_table[p_handle->device_id].peer_id.id_addr_info = (*p_addr); + update_status_bit_set(p_handle->device_id); + device_context_store(p_handle, UPDATE_PEER_ADDR); + err_code = NRF_SUCCESS; + } + else + { + err_code = (NRF_ERROR_INVALID_PARAM | DEVICE_MANAGER_ERR_BASE); + } + + DM_TRC("[DM]: << dm_peer_addr_set\r\n"); + + DM_MUTEX_UNLOCK(); + + return err_code; +} + + +ret_code_t dm_peer_addr_get(dm_handle_t const * p_handle, + ble_gap_addr_t * p_addr) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_addr); + VERIFY_APP_REGISTERED(p_handle->appl_id); + + DM_MUTEX_LOCK(); + + DM_TRC("[DM]: >> dm_peer_addr_get\r\n"); + + ret_code_t err_code; + + err_code = (NRF_ERROR_NOT_FOUND | DEVICE_MANAGER_ERR_BASE); + + if (p_handle->device_id == DM_INVALID_ID) + { + if ((p_handle->connection_id != DM_INVALID_ID) && + ((m_connection_table[p_handle->connection_id].state & STATE_CONNECTED) == + STATE_CONNECTED)) + { + DM_TRC("[DM]:[CI 0x%02X]: Address get for non bonded active connection.\r\n", + p_handle->connection_id); + + (*p_addr) = m_connection_table[p_handle->connection_id].peer_addr; + err_code = NRF_SUCCESS; + } + } + else + { + if ((m_peer_table[p_handle->device_id].id_bitmap & ADDR_ENTRY) == 0) + { + DM_TRC("[DM]:[DI 0x%02X]: Address get for bonded device.\r\n", + p_handle->device_id); + + (*p_addr) = m_peer_table[p_handle->device_id].peer_id.id_addr_info; + err_code = NRF_SUCCESS; + } + } + + DM_TRC("[DM]: << dm_peer_addr_get\r\n"); + + DM_MUTEX_UNLOCK(); + + return err_code; +} + + +ret_code_t dm_distributed_keys_get(dm_handle_t const * p_handle, + dm_sec_keyset_t * p_key_dist) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_key_dist); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_DEVICE_INSTANCE(p_handle->device_id); + + DM_MUTEX_LOCK(); + + DM_TRC("[DM]: >> dm_distributed_keys_get\r\n"); + + ret_code_t err_code; + ble_gap_enc_key_t peer_enc_key; + pstorage_handle_t block_handle; + + err_code = NRF_ERROR_NOT_FOUND; + p_key_dist->keys_central.enc_key.p_enc_key = NULL; + p_key_dist->keys_central.p_id_key = (dm_id_key_t *)&m_peer_table[p_handle->device_id].peer_id; + p_key_dist->keys_central.p_sign_key = NULL; + p_key_dist->keys_periph.p_id_key = (dm_id_key_t *)&m_local_id_info; + p_key_dist->keys_periph.p_sign_key = NULL; + p_key_dist->keys_periph.enc_key.p_enc_key = (dm_enc_key_t *)&peer_enc_key; + + if ((m_peer_table[p_handle->device_id].id_bitmap & IRK_ENTRY) == 0) + { +// p_key_dist->keys_periph.p_id_key->id_addr_info.addr_type = INVALID_ADDR_TYPE; + } + + err_code = pstorage_block_identifier_get(&m_storage_handle, p_handle->device_id, &block_handle); + if (err_code == NRF_SUCCESS) + { + + err_code = pstorage_load((uint8_t *)&peer_enc_key, + &block_handle, + BOND_SIZE, + BOND_STORAGE_OFFSET); + + if (err_code == NRF_SUCCESS) + { + p_key_dist->keys_central.enc_key.p_enc_key = NULL; + p_key_dist->keys_central.p_id_key = (dm_id_key_t *)&m_peer_table[p_handle->device_id].peer_id; + p_key_dist->keys_central.p_sign_key = NULL; + p_key_dist->keys_periph.p_id_key = (dm_id_key_t *)&m_local_id_info; + p_key_dist->keys_periph.p_sign_key = NULL; + p_key_dist->keys_periph.enc_key.p_enc_key = (dm_enc_key_t *)&peer_enc_key; + } + } + + DM_TRC("[DM]: << dm_distributed_keys_get\r\n"); + + DM_MUTEX_UNLOCK(); + + return err_code; +} + + +/**@brief Function for loading bond information for a connection instance. + */ +void bond_data_load(dm_handle_t * p_handle) +{ + pstorage_handle_t block_handle; + + uint32_t err_code = pstorage_block_identifier_get(&m_storage_handle, + p_handle->device_id, + &block_handle); + if (err_code == NRF_SUCCESS) + { + dbgMsg( + "[DM]:");dbgMsgn(p_handle->connection_id); + dbgMsg(":[Block ID ");dbgMsgn(block_handle.block_id); + dbgMsg(":Loading bond information at "); + dbgMsgn(&m_bond_table[p_handle->connection_id]); + dbgMsg(", size "); dbgMsgn(BOND_SIZE); + dbgMsg(", offset ");dbgMsgn(BOND_STORAGE_OFFSET); + dbgMsg("\r\n"); + + err_code = pstorage_load((uint8_t *)&m_bond_table[p_handle->connection_id], + &block_handle, + BOND_SIZE, + BOND_STORAGE_OFFSET); + + if (err_code != NRF_SUCCESS) + { + dbgMsg("[DM]:[%02X]: Failed to load Bond information, reason %08X\r\n"); + dbgMsgn(p_handle->connection_id); + dbgMsgn(err_code); + dbgMsg("\r\n"); + } + + dbgMsg( + "[DM]:");dbgMsgn(p_handle->connection_id);dbgMsg(":Loading service context at "); + dbgMsgn(&m_gatts_table[p_handle->connection_id]);dbgMsg(", size "); + dbgMsgn(sizeof(dm_gatts_context_t));dbgMsg("offset "); + dbgMsgn(SERVICE_STORAGE_OFFSET);dbgMsg("\r\n"); + + err_code = m_service_context_load[m_application_table[0].service]( + &block_handle, + p_handle); + + if (err_code != NRF_SUCCESS) + { + dbgMsg( + "[DM]:[%02X]: Failed to load service information, reason %08X\r\n"); + dbgMsgn(p_handle->connection_id); + dbgMsgn(err_code); + dbgMsg("\r\n"); + } + } + else + { + dbgMsg("[DM]:[%02X]: Failed to get block identifier for " + "device %08X, reason %08X "); dbgMsgn(p_handle->connection_id); dbgMsgn(p_handle->device_id); dbgMsgn(err_code); + dbgMsg("\r\n"); + } +} + + +void dm_ble_evt_handler(ble_evt_t * p_ble_evt) +{ + uint32_t err_code; + uint32_t index; + uint32_t device_index = DM_INVALID_ID; + bool notify_app = false; + dm_handle_t handle; + dm_event_t event; + uint32_t event_result; + ble_gap_enc_info_t * p_enc_info = NULL; + + VERIFY_MODULE_INITIALIZED_VOID(); + VERIFY_APP_REGISTERED_VOID(0); + DM_MUTEX_LOCK(); + + err_code = dm_handle_initialize(&handle); + APP_ERROR_CHECK(err_code); + + event_result = NRF_SUCCESS; + err_code = NRF_SUCCESS; + event.event_param.p_gap_param = &p_ble_evt->evt.gap_evt; + event.event_paramlen = sizeof(ble_gap_evt_t); + handle.device_id = DM_INVALID_ID; + handle.appl_id = 0; + index = 0x00; + + if (p_ble_evt->header.evt_id != BLE_GAP_EVT_CONNECTED) + { + err_code = connection_instance_find(p_ble_evt->evt.gap_evt.conn_handle, + STATE_CONNECTED, + &index); + + if (err_code == NRF_SUCCESS) + { + handle.device_id = m_connection_table[index].bonded_dev_id; + handle.connection_id = index; + } + } + + switch (p_ble_evt->header.evt_id) + { + case BLE_GAP_EVT_CONNECTED: + dbgMsg("[DM]: Connected\r\n"); + //Allocate connection instance for a new connection. + err_code = connection_instance_allocate(&index); + + //Connection instance is successfully allocated. + if (err_code == NRF_SUCCESS) + { + //Application notification related information. + notify_app = true; + event.event_id = DM_EVT_CONNECTION; + handle.connection_id = index; + + m_connection_table[index].conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + m_connection_table[index].state = STATE_CONNECTED; + m_connection_table[index].peer_addr = + p_ble_evt->evt.gap_evt.params.connected.peer_addr; + + if (p_ble_evt->evt.gap_evt.params.connected.irk_match == 1) + { + if (m_irk_index_table[p_ble_evt->evt.gap_evt.params.connected.irk_match_idx] != DM_INVALID_ID) + { + device_index = m_irk_index_table[p_ble_evt->evt.gap_evt.params.connected.irk_match_idx]; + err_code = NRF_SUCCESS; + } + } + else + { + //Use the device address to check if the device exists in the bonded device list. + err_code = device_instance_find(&p_ble_evt->evt.gap_evt.params.connected.peer_addr, + &device_index, EDIV_INIT_VAL); + } + + if (err_code == NRF_SUCCESS) + { + m_connection_table[index].bonded_dev_id = device_index; + m_connection_table[index].state |= STATE_BONDED; + handle.device_id = device_index; + + bond_data_load(&handle); + } + } + break; + + case BLE_GAP_EVT_DISCONNECTED: + //Disconnection could be peer or self initiated hence disconnecting and connecting + //both states are permitted, however, connection handle must be known. + dbgMsg("[DM]: Disconnect Reason 0x%04X\r\n"); + dbgMsgn(p_ble_evt->evt.gap_evt.params.disconnected.reason); + dbgMsg("\r\n"); + m_connection_table[index].state &= (~STATE_CONNECTED); + + if ((m_connection_table[index].state & STATE_BONDED) == STATE_BONDED) + { + if ((m_connection_table[index].state & STATE_LINK_ENCRYPTED) == STATE_LINK_ENCRYPTED) + { + NRF_GPIO->DIRSET = (1UL << 3); //A0 + NRF_GPIO->OUTSET = (1UL << 3); //A0 + //Write bond information persistently. + device_context_store(&handle, STORE_ALL_CONTEXT); + } + } + else + { + //Free any allocated instances for devices that is not bonded. + if (handle.device_id != DM_INVALID_ID) + { + peer_instance_init(handle.device_id); + handle.device_id = DM_INVALID_ID; + } + } + + m_connection_table[index].state = STATE_DISCONNECTING; + notify_app = true; + event.event_id = DM_EVT_DISCONNECTION; + + break; + + case BLE_GAP_EVT_SEC_INFO_REQUEST: + dbgMsg("[DM]: >> BLE_GAP_EVT_SEC_INFO_REQUEST\r\n"); + + //If the device is already bonded, respond with existing info, else NULL. + if (m_connection_table[index].bonded_dev_id == DM_INVALID_ID) + {dbgMsg("device already bonded\r\n"); + //Find device based on div. + err_code = device_instance_find(NULL,&device_index, p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.ediv); + if (err_code == NRF_SUCCESS) + {dbgMsg("device found\r\n"); + //Load needed bonding information. + m_connection_table[index].bonded_dev_id = device_index; + m_connection_table[index].state |= STATE_BONDED; + handle.device_id = device_index; + bond_data_load(&handle); + } + dbgMsg("device not found\r\n"); + } + + if (m_connection_table[index].bonded_dev_id != DM_INVALID_ID) + { + p_enc_info = &m_bond_table[index].peer_enc_key.enc_info; + DM_DUMP((uint8_t *)p_enc_info, sizeof(ble_gap_enc_info_t)); + } + + err_code = sd_ble_gap_sec_info_reply(p_ble_evt->evt.gap_evt.conn_handle, + p_enc_info, + &m_peer_table[index].peer_id.id_info, + NULL); + + if (err_code != NRF_SUCCESS) + { + dbgMsg("[DM]:[CI %02X]:[DI %02X]: Security information response failed, reason " + "0x%08X\r\n"); + dbgMsgn(index); + dbgMsgn(m_connection_table[index].bonded_dev_id); + dbgMsgn(err_code); + dbgMsg("\r\n"); + } + break; + + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + dbgMsg("[DM]: >> BLE_GAP_EVT_SEC_PARAMS_REQUEST\r\n"); + + event.event_id = DM_EVT_SECURITY_SETUP; + + m_connection_table[index].state |= STATE_PAIRING; + notify_app = true; + + if (m_connection_table[index].bonded_dev_id == DM_INVALID_ID) + { + //Assign a peer index as a new bond or update existing bonds. + err_code = device_instance_allocate((uint8_t *)&device_index, + &m_connection_table[index].peer_addr); + + //Allocation successful. + if (err_code == NRF_SUCCESS) + { + dbgMsg("[DM]:[CI 0x%02X]:[DI 0x%02X]: Bonded!\r\n"); + dbgMsgn(index); + dbgMsgn(device_index); +dbgMsg("\r\n"); + handle.device_id = device_index; + m_connection_table[index].bonded_dev_id = device_index; + } + else + { + dbgMsg("[DM]: Security parameter request failed, reason 0x%08X.\r\n"); + dbgMsgn(err_code); + dbgMsg("\r\n"); + event_result = err_code; + notify_app = true; + } + + ble_gap_sec_keyset_t keys_exchanged; + + dbgMsg("[DM]: 0x%02X, 0x%02X, 0x%02X, 0x%02X\r\n"); + dbgMsgn(p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.kdist_peer.enc); + dbgMsgn(p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.kdist_own.id); + dbgMsgn(p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.kdist_peer.sign); + dbgMsgn(p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.bond); +dbgMsg("\r\n"); + keys_exchanged.keys_peer.p_enc_key = NULL; + keys_exchanged.keys_peer.p_id_key = &m_peer_table[m_connection_table[index].bonded_dev_id].peer_id; + keys_exchanged.keys_peer.p_sign_key = NULL; + keys_exchanged.keys_peer.p_pk = NULL; + keys_exchanged.keys_own.p_enc_key = &m_bond_table[index].peer_enc_key; + keys_exchanged.keys_own.p_id_key = NULL; + keys_exchanged.keys_own.p_sign_key = NULL; + keys_exchanged.keys_own.p_pk = NULL; + + err_code = sd_ble_gap_sec_params_reply(p_ble_evt->evt.gap_evt.conn_handle, + BLE_GAP_SEC_STATUS_SUCCESS, + &m_application_table[0].sec_param, + &keys_exchanged); + + if (err_code != NRF_SUCCESS) + { + dbgMsg("[DM]: Security parameter reply request failed, reason 0x%08X.\r\n"); + dbgMsgn(err_code); + dbgMsg("\r\n"); + event_result = err_code; + notify_app = false; + } + + } + else + { + //Bond/key refresh. + dbgMsg("[DM]: !!! Bond/key refresh !!!\r\n"); + //Set the update flag for bond data. + m_connection_table[index].state |= STATE_BOND_INFO_UPDATE; + event.event_id = DM_EVT_SECURITY_SETUP_REFRESH; + + err_code = sd_ble_gap_sec_params_reply(p_ble_evt->evt.gap_evt.conn_handle, + BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, + NULL, + NULL); + + if (err_code != NRF_SUCCESS) + { + dbgMsg("[DM]: Security parameter reply request failed, reason 0x%08X.\r\n");dbgMsgn(err_code); + event_result = err_code; + notify_app = false; + } + + } + break; + + case BLE_GAP_EVT_AUTH_STATUS: + { + dbgMsg("[DM]: >> BLE_GAP_EVT_AUTH_STATUS, status %08X\r\n"); + dbgMsgn(p_ble_evt->evt.gap_evt.params.auth_status.auth_status); +dbgMsg("\r\n"); + m_application_table[0].state &= (~STATE_CONTROL_PROCEDURE_IN_PROGRESS); + m_connection_table[index].state &= (~STATE_PAIRING); + event.event_id = DM_EVT_SECURITY_SETUP_COMPLETE; + notify_app = true; + + if (p_ble_evt->evt.gap_evt.params.auth_status.auth_status != BLE_GAP_SEC_STATUS_SUCCESS) + { + // In case of key refresh attempt, since this behavior is now rejected, we don't do anything here + if ((m_connection_table[index].state & STATE_BOND_INFO_UPDATE) + != STATE_BOND_INFO_UPDATE) + { + // Free the allocation as bonding failed. + ret_code_t result = device_instance_free(m_connection_table[index].bonded_dev_id); + (void) result; + event_result = p_ble_evt->evt.gap_evt.params.auth_status.auth_status; + } + } + else + { + DM_DUMP((uint8_t *)&p_ble_evt->evt.gap_evt.params.auth_status, + sizeof(ble_gap_evt_auth_status_t)); + DM_DUMP((uint8_t *)&m_bond_table[index], sizeof(bond_context_t)); + + if (p_ble_evt->evt.gap_evt.params.auth_status.bonded == 1) + { + if (handle.device_id != DM_INVALID_ID) + { + m_connection_table[index].state |= STATE_BONDED; + + //IRK and/or public address is shared, update it. + if (p_ble_evt->evt.gap_evt.params.auth_status.kdist_peer.id == 1) + { + m_peer_table[handle.device_id].id_bitmap &= (~IRK_ENTRY); + } + + if (m_connection_table[index].bonded_dev_id != DM_INVALID_ID) + { + dbgMsg("[DM]:[CI 0x%02X]:[DI 0x%02X]: Bonded!\r\n"); + dbgMsgn(index); + dbgMsgn(handle.device_id); +dbgMsg("\r\n"); + if (m_connection_table[index].peer_addr.addr_type != + BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE) + { + m_peer_table[handle.device_id].peer_id.id_addr_info = + m_connection_table[index].peer_addr; + m_peer_table[handle.device_id].id_bitmap &= (~ADDR_ENTRY); + + DM_DUMP((uint8_t *)&m_peer_table[handle.device_id].peer_id.id_addr_info, + sizeof(m_peer_table[handle.device_id].peer_id.id_addr_info)); + } + else + { + // Here we must fetch the keys from the keyset distributed. + m_peer_table[handle.device_id].ediv = m_bond_table[index].peer_enc_key.master_id.ediv; + m_peer_table[handle.device_id].id_bitmap &= (~IRK_ENTRY); + } + + device_context_store(&handle, FIRST_BOND_STORE); + } + } + } + else + { + //Pairing request, no need to touch the bonding info. + } + } + break; + } + + case BLE_GAP_EVT_CONN_SEC_UPDATE: + dbgMsg("[DM]: >> BLE_GAP_EVT_CONN_SEC_UPDATE, Mode 0x%02X, Level 0x%02X\r\n"); + dbgMsgn(p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.sm); + dbgMsgn(p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv); +dbgMsg("\r\n"); + if ((p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv == 1) && + (p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.sm == 1) && + ((m_connection_table[index].state & STATE_BONDED) == STATE_BONDED)) + { + //Lost bond case, generate a security refresh event! + memset(m_gatts_table[index].attributes, 0, DM_GATT_SERVER_ATTR_MAX_SIZE); + + event.event_id = DM_EVT_SECURITY_SETUP_REFRESH; + m_connection_table[index].state |= STATE_PAIRING_PENDING; + m_connection_table[index].state |= STATE_BOND_INFO_UPDATE; + m_application_table[0].state |= STATE_QUEUED_CONTROL_REQUEST; + } + else + { + m_connection_table[index].state |= STATE_LINK_ENCRYPTED; + event.event_id = DM_EVT_LINK_SECURED; + + //Apply service context. + err_code = m_service_context_apply[m_application_table[0].service](&handle); + + if (err_code != NRF_SUCCESS) + { + dbgMsg("[DM]:[CI 0x%02X]:[DI 0x%02X]: Failed to apply service context\r\n"); + dbgMsgn(handle.connection_id); + dbgMsgn(handle.device_id); +dbgMsg("\r\n"); + event_result = DM_SERVICE_CONTEXT_NOT_APPLIED; + } + } + event_result = NRF_SUCCESS; + notify_app = true; + + break; + + case BLE_GATTS_EVT_SYS_ATTR_MISSING: + dbgMsg("[DM]: >> BLE_GATTS_EVT_SYS_ATTR_MISSING\r\n"); + + //Apply service context. + event_result = m_service_context_apply[m_application_table[0].service](&handle); + break; + + case BLE_GAP_EVT_SEC_REQUEST: + dbgMsg("[DM]: >> BLE_GAP_EVT_SEC_REQUEST\r\n"); + + //Verify if the device is already bonded, and if it is bonded, initiate encryption. + //If the device is not bonded, an instance needs to be allocated in order to initiate + //bonding. The application have to initiate the procedure, the module will not do this + //automatically. + event.event_id = DM_EVT_SECURITY_SETUP; + notify_app = true; + + break; + + default: + break; + } + + if (notify_app) + { + app_evt_notify(&handle, &event, event_result); + + //Freeing the instance after the event is notified so the application can get the context. + if (event.event_id == DM_EVT_DISCONNECTION) + { + //Free the instance. + connection_instance_free(&index); + } + } + + UNUSED_VARIABLE(err_code); + + DM_MUTEX_UNLOCK(); +} + + +ret_code_t dm_handle_get(uint16_t conn_handle, dm_handle_t * p_handle) +{ + ret_code_t err_code; + uint32_t index; + + NULL_PARAM_CHECK(p_handle); + VERIFY_APP_REGISTERED(p_handle->appl_id); + + p_handle->device_id = DM_INVALID_ID; + + err_code = NRF_ERROR_NOT_FOUND; + + for (index = 0; index < DEVICE_MANAGER_MAX_CONNECTIONS; index++) + { + //Search for matching connection handle. + if (conn_handle == m_connection_table[index].conn_handle) + { + p_handle->connection_id = index; + p_handle->device_id = m_connection_table[index].bonded_dev_id; + + err_code = NRF_SUCCESS; + break; + } + } + return err_code; +} diff --git a/cores/arduino/components/drivers_nrf/pstorage/config/pstorage_platform.h b/cores/arduino/components/drivers_nrf/pstorage/config/pstorage_platform.h new file mode 100644 index 0000000..e792ab9 --- /dev/null +++ b/cores/arduino/components/drivers_nrf/pstorage/config/pstorage_platform.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + + /** @cond To make doxygen skip this file */ + +/** @file + * This header contains defines with respect persistent storage that are specific to + * persistent storage implementation and application use case. + */ +#ifndef PSTORAGE_PL_H__ +#define PSTORAGE_PL_H__ + +#include +#include "nrf.h" + +static __INLINE uint16_t pstorage_flash_page_size() +{ + return (uint16_t)NRF_FICR->CODEPAGESIZE; +} + +#define PSTORAGE_FLASH_PAGE_SIZE pstorage_flash_page_size() /**< Size of one flash page. */ +#define PSTORAGE_FLASH_EMPTY_MASK 0xFFFFFFFF /**< Bit mask that defines an empty address in flash. */ + +static __INLINE uint32_t pstorage_flash_page_end() +{ + uint32_t bootloader_addr = NRF_UICR->NRFFW[0]; + + return ((bootloader_addr != PSTORAGE_FLASH_EMPTY_MASK) ? + (bootloader_addr/ PSTORAGE_FLASH_PAGE_SIZE) : NRF_FICR->CODESIZE); +} + +#define PSTORAGE_FLASH_PAGE_END pstorage_flash_page_end() + +#define PSTORAGE_NUM_OF_PAGES 2 /**< Number of flash pages allocated for the pstorage module excluding the swap page, configurable based on system requirements. */ +#define PSTORAGE_MIN_BLOCK_SIZE 0x0010 /**< Minimum size of block that can be registered with the module. Should be configured based on system requirements, recommendation is not have this value to be at least size of word. */ + +#define PSTORAGE_DATA_START_ADDR ((PSTORAGE_FLASH_PAGE_END - PSTORAGE_NUM_OF_PAGES - 1) \ + * PSTORAGE_FLASH_PAGE_SIZE) /**< Start address for persistent data, configurable according to system requirements. */ +#define PSTORAGE_DATA_END_ADDR ((PSTORAGE_FLASH_PAGE_END - 1) * PSTORAGE_FLASH_PAGE_SIZE) /**< End address for persistent data, configurable according to system requirements. */ +#define PSTORAGE_SWAP_ADDR PSTORAGE_DATA_END_ADDR /**< Top-most page is used as swap area for clear and update. */ + +#define PSTORAGE_MAX_BLOCK_SIZE PSTORAGE_FLASH_PAGE_SIZE /**< Maximum size of block that can be registered with the module. Should be configured based on system requirements. And should be greater than or equal to the minimum size. */ +#define PSTORAGE_CMD_QUEUE_SIZE 30 /**< Maximum number of flash access commands that can be maintained by the module for all applications. Configurable. */ + + +/** Abstracts persistently memory block identifier. */ +typedef uint32_t pstorage_block_t; + +typedef struct +{ + uint32_t module_id; /**< Module ID.*/ + pstorage_block_t block_id; /**< Block ID.*/ +} pstorage_handle_t; + +typedef uint16_t pstorage_size_t; /** Size of length and offset fields. */ + +/**@brief Handles Flash Access Result Events. To be called in the system event dispatcher of the application. */ +void pstorage_sys_event_handler (uint32_t sys_evt); + +#endif // PSTORAGE_PL_H__ + +/** @} */ +/** @endcond */ diff --git a/cores/arduino/components/drivers_nrf/pstorage/pstorage.c b/cores/arduino/components/drivers_nrf/pstorage/pstorage.c new file mode 100644 index 0000000..a1e08b7 --- /dev/null +++ b/cores/arduino/components/drivers_nrf/pstorage/pstorage.c @@ -0,0 +1,1572 @@ +/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#include "pstorage.h" +#include +#include +#include +#include "nordic_common.h" +#include "nrf_error.h" +#include "nrf_assert.h" +#include "nrf.h" +#include "nrf_soc.h" +#include "app_util.h" +#include "app_error.h" + +#define INVALID_OPCODE 0x00 /**< Invalid op code identifier. */ +#define SOC_MAX_WRITE_SIZE PSTORAGE_FLASH_PAGE_SIZE /**< Maximum write size allowed for a single call to \ref sd_flash_write as specified in the SoC API. */ +#define RAW_MODE_APP_ID (PSTORAGE_NUM_OF_PAGES + 1) /**< Application id for raw mode. */ + +#if defined(NRF52) +#define SD_CMD_MAX_TRIES 1000 /**< Number of times to try a softdevice flash operatoion, specific for nRF52 to account for longest time of flash page erase*/ +#else +#define SD_CMD_MAX_TRIES 3 /**< Number of times to try a softdevice flash operation when the @ref NRF_EVT_FLASH_OPERATION_ERROR sys_evt is received. */ +#endif /* defined(NRF52) */ + +#define MASK_TAIL_SWAP_DONE (1 << 0) /**< Flag for checking if the tail restore area has been written to swap page. */ +#define MASK_SINGLE_PAGE_OPERATION (1 << 1) /**< Flag for checking if command is a single flash page operation. */ +#define MASK_MODULE_INITIALIZED (1 << 2) /**< Flag for checking if the module has been initialized. */ +#define MASK_FLASH_API_ERR_BUSY (1 << 3) /**< Flag for checking if flash API returned NRF_ERROR_BUSY. */ + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final code versions to save execution and size. + * + * @{ + */ + +/**@brief Check if the input pointer is NULL, if so it returns NRF_ERROR_NULL. + */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return NRF_ERROR_NULL; \ + } + +/**@brief Verifies that the module identifier supplied by the application is within permissible + * range. + */ +#define MODULE_ID_RANGE_CHECK(ID) \ + if ((((ID)->module_id) >= PSTORAGE_NUM_OF_PAGES) || \ + (m_app_table[(ID)->module_id].cb == NULL)) \ + { \ + return NRF_ERROR_INVALID_PARAM; \ + } + +/**@brief Verifies that the block identifier supplied by the application is within the permissible + * range. + */ +#define BLOCK_ID_RANGE_CHECK(ID) \ + if (((ID)->block_id) >= (m_app_table[(ID)->module_id].base_id + \ + (m_app_table[(ID)->module_id].block_count * MODULE_BLOCK_SIZE(ID)))) \ + { \ + return NRF_ERROR_INVALID_PARAM; \ + } + +/**@brief Verifies that the block size requested by the application can be supported by the module. + */ +#define BLOCK_SIZE_CHECK(X) \ + if (((X) > PSTORAGE_MAX_BLOCK_SIZE) || ((X) < PSTORAGE_MIN_BLOCK_SIZE)) \ + { \ + return NRF_ERROR_INVALID_PARAM; \ + } + +/**@brief Verifies the block size requested by the application in registration API. + */ +#define BLOCK_COUNT_CHECK(COUNT, SIZE) \ + if (((COUNT) == 0) || \ + ((m_next_page_addr + ((COUNT) *(SIZE)) > PSTORAGE_SWAP_ADDR))) \ + { \ + return NRF_ERROR_INVALID_PARAM; \ + } + +/**@brief Verifies the size parameter provided by the application in API. + */ +#define SIZE_CHECK(ID, SIZE) \ + if(((SIZE) == 0) || ((SIZE) > MODULE_BLOCK_SIZE(ID))) \ + { \ + return NRF_ERROR_INVALID_PARAM; \ + } + +/**@brief Verifies the offset parameter provided by the application in API. + */ +#define OFFSET_CHECK(ID, OFFSET, SIZE) \ + if(((SIZE) + (OFFSET)) > MODULE_BLOCK_SIZE(ID)) \ + { \ + return NRF_ERROR_INVALID_PARAM; \ + } + +#ifdef PSTORAGE_RAW_MODE_ENABLE + +/**@brief Verifies the module identifier supplied by the application. + */ +#define MODULE_RAW_HANDLE_CHECK(ID) \ + if ((((ID)->module_id) != RAW_MODE_APP_ID)) \ + { \ + return NRF_ERROR_INVALID_PARAM; \ + } + +#endif // PSTORAGE_RAW_MODE_ENABLE + +/**@} */ + + +/**@brief Verify module's initialization status. + * + * @details Verify module's initialization status. Returns NRF_ERROR_INVALID_STATE when a + * module API is called without initializing the module. + */ +#define VERIFY_MODULE_INITIALIZED() \ + do \ + { \ + if (!(m_flags & MASK_MODULE_INITIALIZED)) \ + { \ + return NRF_ERROR_INVALID_STATE; \ + } \ + } while(0) + +/**@brief Macro to fetch the block size registered for the module. */ +#define MODULE_BLOCK_SIZE(ID) (m_app_table[(ID)->module_id].block_size) + +/**@brief Main state machine of the component. */ +typedef enum +{ + STATE_IDLE, /**< State for being idle (no command execution in progress). */ + STATE_STORE, /**< State for storing data when using store/update API. */ + STATE_DATA_ERASE_WITH_SWAP, /**< State for erasing the data page when using update/clear API when use of swap page is required. */ + STATE_DATA_ERASE, /**< State for erasing the data page when using update/clear API without the need to use the swap page. */ + STATE_ERROR /**< State entered when command processing is terminated abnormally. */ +} pstorage_state_t; + +/**@brief Sub state machine contained by @ref STATE_DATA_ERASE_WITH_SWAP super state machine. */ +typedef enum +{ + STATE_ERASE_SWAP, /**< State for erasing the swap page when using the update/clear API. */ + STATE_WRITE_DATA_TO_SWAP, /**< State for writing the data page into the swap page when using update/clear API. */ + STATE_ERASE_DATA_PAGE, /**< State for erasing data page when using update/clear API. */ + STATE_RESTORE_TAIL, /**< State for restoring tail (end) of backed up data from swap to data page when using update/clear API. */ + STATE_RESTORE_HEAD, /**< State for restoring head (beginning) of backed up data from swap to data page when using update/clear API. */ + SWAP_SUB_STATE_MAX /**< Enumeration upper bound. */ +} flash_swap_sub_state_t; + +/**@brief Application registration information. + * + * @details Defines application specific information that the application needs to maintain to be able + * to process requests from each one of them. + */ +typedef struct +{ + pstorage_ntf_cb_t cb; /**< Callback registered with the module to be notified of result of flash access. */ + pstorage_block_t base_id; /**< Base block ID assigned to the module. */ + pstorage_size_t block_size; /**< Size of block for the module. */ + pstorage_size_t block_count; /**< Number of blocks requested by the application. */ +} pstorage_module_table_t; + + +#ifdef PSTORAGE_RAW_MODE_ENABLE +/**@brief Application registration information. + * + * @details Defines application specific information that the application registered for raw mode. + */ +typedef struct +{ + pstorage_ntf_cb_t cb; /**< Callback registered with the module to be notified of the result of flash access. */ +} pstorage_raw_module_table_t; +#endif // PSTORAGE_RAW_MODE_ENABLE + + +/**@brief Defines command queue element. + * + * @details Defines command queue element. Each element encapsulates needed information to process + * a flash access command. + */ +typedef struct +{ + uint8_t op_code; /**< Identifies the flash access operation being queued. Element is free if op-code is INVALID_OPCODE. */ + pstorage_size_t size; /**< Identifies the size in bytes requested for the operation. */ + pstorage_size_t offset; /**< Offset requested by the application for the access operation. */ + pstorage_handle_t storage_addr; /**< Address/Identifier for persistent memory. */ + uint8_t * p_data_addr; /**< Address/Identifier for data memory. This is assumed to be resident memory. */ +} cmd_queue_element_t; + + +/**@brief Defines command queue, an element is free if the op_code field is not invalid. + * + * @details Defines commands enqueued for flash access. At any point in time, this queue has one or + * more flash access operations pending if the count field is not zero. When the queue is + * not empty, the rp (read pointer) field points to the flash access command in progress + * or, if none is in progress, the command to be requested next. The queue implements a + * simple first in first out algorithm. Data addresses are assumed to be resident. + */ +typedef struct +{ + uint8_t rp; /**< Read pointer, pointing to flash access that is ongoing or to be requested next. */ + uint8_t count; /**< Number of elements in the queue. */ + cmd_queue_element_t cmd[PSTORAGE_CMD_QUEUE_SIZE]; /**< Array to maintain flash access operation details. */ +} cmd_queue_t; + +static cmd_queue_t m_cmd_queue; /**< Flash operation request queue. */ +static pstorage_size_t m_next_app_instance; /**< Points to the application module instance that can be allocated next. */ +static uint32_t m_next_page_addr; /**< Points to the flash address that can be allocated to a module next. This is needed as blocks of a module that can span across flash pages. */ +static pstorage_state_t m_state; /**< Main state tracking variable. */ +static flash_swap_sub_state_t m_swap_sub_state; /**< Flash swap erase when swap used state tracking variable. */ +static uint32_t m_head_word_size; /**< Head restore area size in words. */ +static uint32_t m_tail_word_size; /**< Tail restore area size in words. */ +static uint32_t m_current_page_id; /**< Variable for tracking the flash page being processed. */ +static uint32_t m_num_of_command_retries; /**< Variable for tracking flash operation retries upon flash operation failures. */ +static pstorage_module_table_t m_app_table[PSTORAGE_NUM_OF_PAGES]; /**< Registered application information table. */ +static uint32_t m_num_of_bytes_written; /**< Variable for tracking the number of bytes written by the store operation. */ +static uint32_t m_app_data_size; /**< Variable for storing the application command size parameter internally. */ +static uint32_t m_flags = 0; /**< Storage for boolean flags for state tracking. */ + +#ifdef PSTORAGE_RAW_MODE_ENABLE +static pstorage_raw_module_table_t m_raw_app_table; /**< Registered application information table for raw mode. */ +#endif // PSTORAGE_RAW_MODE_ENABLE + +// Required forward declarations. +static void cmd_process(void); +static void store_operation_execute(void); +static void app_notify(uint32_t result, cmd_queue_element_t * p_elem); +static void cmd_queue_element_init(uint32_t index); +static void cmd_queue_dequeue(void); +static void sm_state_change(pstorage_state_t new_state); +static void swap_sub_state_state_change(flash_swap_sub_state_t new_state); + +/**@brief Function for consuming a command queue element. + * + * @details Function for consuming a command queue element, which has been fully processed. + */ +static void command_queue_element_consume(void) +{ + // Initialize/free the element as it is now processed. + cmd_queue_element_init(m_cmd_queue.rp); + + // Adjust command queue state tracking variables. + --(m_cmd_queue.count); + if (++(m_cmd_queue.rp) == PSTORAGE_CMD_QUEUE_SIZE) + { + m_cmd_queue.rp = 0; + } +} + + +/**@brief Function for executing the finalization procedure for the command executed. + * + * @details Function for executing the finalization procedure for command executed, which includes + * notifying the application of command completion, consuming the command queue element, + * and changing the internal state. + */ +static void command_end_procedure_run(void) +{ + app_notify(NRF_SUCCESS, &m_cmd_queue.cmd[m_cmd_queue.rp]); + + command_queue_element_consume(); + + sm_state_change(STATE_IDLE); +} + + +/**@brief Function for idle state entry actions. + * + * @details Function for idle state entry actions, which include resetting relevant state data and + * scheduling any possible queued flash access operation. + */ +static void state_idle_entry_run(void) +{ + m_num_of_command_retries = 0; + m_num_of_bytes_written = 0; + + // Schedule any possible queued flash access operation. + cmd_queue_dequeue(); +} + + +/**@brief Function for notifying an application of command completion and transitioning to an error + * state. + * + * @param[in] result Result code of the operation for the application. + */ +static void app_notify_error_state_transit(uint32_t result) +{ + app_notify(result, &m_cmd_queue.cmd[m_cmd_queue.rp]); + sm_state_change(STATE_ERROR); +} + + +/**@brief Function for processing flash API error code. + * + * @param[in] err_code Error code from the flash API. + */ +static void flash_api_err_code_process(uint32_t err_code) +{ + switch (err_code) + { + case NRF_SUCCESS: + break; + + case NRF_ERROR_BUSY: + // Flash access operation was not accepted and must be reissued upon flash operation + // complete event. + m_flags |= MASK_FLASH_API_ERR_BUSY; + break; + + default: + // Complete the operation with appropriate result code and transit to an error state. + app_notify_error_state_transit(err_code); + break; + } +} + +/**@brief Function for writing data to flash. + * + * @param[in] p_dst Pointer to start of flash location to be written. + * @param[in] p_src Pointer to buffer with data to be written. + * @param[in] size_in_words Number of 32-bit words to write. + */ +static void flash_write(uint32_t * const p_dst, + uint32_t const * const p_src, + uint32_t size_in_words) +{ + flash_api_err_code_process(sd_flash_write(p_dst, p_src, size_in_words)); +} + + +/**@brief Function for writing data to flash upon store command. + * + * @details Function for writing data to flash upon executing store command. Data is written to + * flash in reverse order, meaning starting at the end. If the data that is to be written + * is greater than the flash page size, it will be fragmented to fit the flash page size. + */ +static void store_cmd_flash_write_execute(void) +{ + const cmd_queue_element_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + + if (p_cmd->size > SOC_MAX_WRITE_SIZE) + { + const uint32_t offset = p_cmd->size - PSTORAGE_FLASH_PAGE_SIZE; + flash_write((uint32_t *)(p_cmd->storage_addr.block_id + p_cmd->offset + offset), + (uint32_t *)(p_cmd->p_data_addr + offset), + PSTORAGE_FLASH_PAGE_SIZE / sizeof(uint32_t)); + + m_num_of_bytes_written = PSTORAGE_FLASH_PAGE_SIZE; + } + else + { + flash_write((uint32_t *)(p_cmd->storage_addr.block_id + p_cmd->offset), + (uint32_t *)(p_cmd->p_data_addr), + p_cmd->size / sizeof(uint32_t)); + + m_num_of_bytes_written = p_cmd->size; + } +} + + +/**@brief Function for store state entry action. + * + * @details Function for store state entry action, which includes writing data to a flash page. + */ +static void state_store_entry_run(void) +{ + store_cmd_flash_write_execute(); +} + + +/**@brief Function for data erase with swap state entry actions. + * + * @details Function for data erase with swap state entry actions. This includes adjusting relevant + * state and data variables and transitioning to the correct sub state. + */ +static void state_data_erase_swap_entry_run(void) +{ + m_flags &= ~MASK_TAIL_SWAP_DONE; + + const cmd_queue_element_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + const pstorage_block_t cmd_block_id = p_cmd->storage_addr.block_id; + + const uint32_t clear_start_page_id = cmd_block_id / PSTORAGE_FLASH_PAGE_SIZE; + m_current_page_id = clear_start_page_id; + + // @note: No need to include p_cmd->offset when calculating clear_end_page_id as: + // - clear API does not include offset parameter + // - update and store APIs are limited to operate on single block boundary thus the boolean + // clause ((m_head_word_size == 0) && is_more_than_one_page) below in this function will never + // evaluate as true as if is_more_than_one_page == true m_head_word_size is always != 0 + const uint32_t clear_end_page_id = (cmd_block_id + p_cmd->size - 1u) / + PSTORAGE_FLASH_PAGE_SIZE; + + if (clear_start_page_id == clear_end_page_id) + { + m_flags |= MASK_SINGLE_PAGE_OPERATION; + } + else + { + m_flags &= ~MASK_SINGLE_PAGE_OPERATION; + } + + if ((m_head_word_size == 0) && !(m_flags & MASK_SINGLE_PAGE_OPERATION)) + { + // No head restore required and clear/update area is shared by multiple flash pages, which + // means the current flash page does not have any tail area to restore. You can proceed with + // data page erase directly as no swap is needed for the current flash page. + swap_sub_state_state_change(STATE_ERASE_DATA_PAGE); + } + else + { + swap_sub_state_state_change(STATE_ERASE_SWAP); + } +} + + +/**@brief Function for erasing flash page. + * + * @param[in] page_number Page number of the page to be erased. + */ +static void flash_page_erase(uint32_t page_number) +{ + flash_api_err_code_process(sd_flash_page_erase(page_number)); +} + + +/**@brief Function for data erase state entry action. + * + * @details Function for data erase state entry action, which includes erasing the data flash page. + */ +static void state_data_erase_entry_run(void) +{ + flash_page_erase(m_current_page_id); +} + + +/**@brief Function for dispatching the correct application main state entry action. + */ +static void state_entry_action_run(void) +{ + switch (m_state) + { + case STATE_IDLE: + state_idle_entry_run(); + break; + + case STATE_STORE: + state_store_entry_run(); + break; + + case STATE_DATA_ERASE_WITH_SWAP: + state_data_erase_swap_entry_run(); + break; + + case STATE_DATA_ERASE: + state_data_erase_entry_run(); + break; + + default: + // No action needed. + break; + } +} + + +/**@brief Function for changing application main state and dispatching state entry action. + * + * @param[in] new_state New application main state to transit to. + */ +static void sm_state_change(pstorage_state_t new_state) +{ + m_state = new_state; + state_entry_action_run(); +} + + +/**@brief Function for swap erase state entry action. + * + * @details Function for swap erase state entry action, which includes erasing swap flash + * page. + */ +static void state_swap_erase_entry_run(void) +{ + flash_page_erase(PSTORAGE_SWAP_ADDR / PSTORAGE_FLASH_PAGE_SIZE); +} + + +/**@brief Function for write data to the swap state entry action. + * + * @details Function for write data to the swap state entry action, which includes writing the + * current data page to the swap flash page. + */ +static void state_write_data_swap_entry_run(void) +{ + // @note: There is room for further optimization here as there is only need to write the + // whole flash page to swap area if there is both head and tail area to be restored. In any + // other case we can omit some data from the head or end of the page as that is the clear area. + flash_write((uint32_t *)(PSTORAGE_SWAP_ADDR), + (uint32_t *)(m_current_page_id * PSTORAGE_FLASH_PAGE_SIZE), + PSTORAGE_FLASH_PAGE_SIZE / sizeof(uint32_t)); +} + + +/**@brief Function for erase data page state entry action. + * + * @details Function for erase data page state entry action, which includes erasing the data flash + * page. + */ +static void state_erase_data_page_entry_run(void) +{ + flash_page_erase(m_current_page_id); +} + + +/**@brief Function for restore tail state entry action. + * + * @details Function for restore tail state entry action, which includes writing the tail section + * back from swap to the data page. + */ +static void state_restore_tail_entry_run(void) +{ + const cmd_queue_element_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + const pstorage_block_t cmd_block_id = p_cmd->storage_addr.block_id; + + const uint32_t tail_offset = (cmd_block_id + p_cmd->size + p_cmd->offset) % + PSTORAGE_FLASH_PAGE_SIZE; + + flash_write((uint32_t *)(cmd_block_id + p_cmd->size + p_cmd->offset), + (uint32_t *)(PSTORAGE_SWAP_ADDR + tail_offset), + m_tail_word_size); +} + + +/**@brief Function for restore head state entry action. + * + * @details Function for restore head state entry action, which includes writing the head section + * back from swap to the data page. + */ +static void state_restore_head_entry_run(void) +{ + flash_write((uint32_t *)((m_current_page_id - 1u) * PSTORAGE_FLASH_PAGE_SIZE), + (uint32_t *)PSTORAGE_SWAP_ADDR, + m_head_word_size); +} + + +/**@brief Function for dispatching the correct swap sub state entry action. + */ +static void swap_sub_state_entry_action_run(void) +{ + static void (* const swap_sub_state_sm_lut[SWAP_SUB_STATE_MAX])(void) = + { + state_swap_erase_entry_run, + state_write_data_swap_entry_run, + state_erase_data_page_entry_run, + state_restore_tail_entry_run, + state_restore_head_entry_run + }; + + swap_sub_state_sm_lut[m_swap_sub_state](); +} + + +/**@brief Function for changing the swap sub state and dispatching state entry action. + * + * @param[in] new_state New swap sub state to transit to. + */ +static void swap_sub_state_state_change(flash_swap_sub_state_t new_state) +{ + m_swap_sub_state = new_state; + swap_sub_state_entry_action_run(); +} + + +/**@brief Function for initializing the command queue element. + * + * @param[in] index Index of the element to be initialized. + */ +static void cmd_queue_element_init(uint32_t index) +{ + // Internal function and checks on range of index can be avoided. + m_cmd_queue.cmd[index].op_code = INVALID_OPCODE; + m_cmd_queue.cmd[index].size = 0; + m_cmd_queue.cmd[index].storage_addr.module_id = PSTORAGE_NUM_OF_PAGES; + m_cmd_queue.cmd[index].storage_addr.block_id = 0; + m_cmd_queue.cmd[index].p_data_addr = NULL; + m_cmd_queue.cmd[index].offset = 0; +} + + +/**@brief Function for initializing the command queue. + */ +static void cmd_queue_init(void) +{ + m_cmd_queue.rp = 0; + m_cmd_queue.count = 0; + + for (uint32_t cmd_index = 0; cmd_index < PSTORAGE_CMD_QUEUE_SIZE; ++cmd_index) + { + cmd_queue_element_init(cmd_index); + } +} + + +/**@brief Function for enqueuing, and possibly dispatching, a flash access operation. + * + * @param[in] opcode Identifies the operation requested to be enqueued. + * @param[in] p_storage_addr Identifies the module and flash address on which the operation is + * requested. + * @param[in] p_data_addr Identifies the data address for flash access. + * @param[in] size Size in bytes of data requested for the access operation. + * @param[in] offset Offset within the flash memory block at which operation is requested. + * + * @retval NRF_SUCCESS Upon success. + * @retval NRF_ERROR_NO_MEM Upon failure, when no space is available in the command queue. + */ +static uint32_t cmd_queue_enqueue(uint8_t opcode, + pstorage_handle_t * p_storage_addr, + uint8_t * p_data_addr, + pstorage_size_t size, + pstorage_size_t offset) +{ + uint32_t err_code; + + if (m_cmd_queue.count != PSTORAGE_CMD_QUEUE_SIZE) + { + // Enqueue the command if it the queue is not full. + uint32_t write_index = m_cmd_queue.rp + m_cmd_queue.count; + + if (write_index >= PSTORAGE_CMD_QUEUE_SIZE) + { + write_index -= PSTORAGE_CMD_QUEUE_SIZE; + } + + m_cmd_queue.cmd[write_index].op_code = opcode; + m_cmd_queue.cmd[write_index].p_data_addr = p_data_addr; + m_cmd_queue.cmd[write_index].storage_addr = (*p_storage_addr); + m_cmd_queue.cmd[write_index].size = size; + m_cmd_queue.cmd[write_index].offset = offset; + + m_cmd_queue.count++; + + if (m_state == STATE_IDLE) + { + cmd_process(); + } + + err_code = NRF_SUCCESS; + } + else + { + err_code = NRF_ERROR_NO_MEM; + } + + return err_code; +} + + +/**@brief Function for dequeing a possible pending flash access operation. + */ +static void cmd_queue_dequeue(void) +{ + if ((m_cmd_queue.count != 0)) + { + cmd_process(); + } +} + + +/**@brief Function for notifying an application of command completion. + * + * @param[in] result Result code of the operation for the application. + * @param[in] p_elem Pointer to the command queue element for which this result was received. + */ +static void app_notify(uint32_t result, cmd_queue_element_t * p_elem) +{ + pstorage_ntf_cb_t ntf_cb; + const uint8_t op_code = p_elem->op_code; + +#ifdef PSTORAGE_RAW_MODE_ENABLE + if (p_elem->storage_addr.module_id == RAW_MODE_APP_ID) + { + ntf_cb = m_raw_app_table.cb; + } + else +#endif // PSTORAGE_RAW_MODE_ENABLE + { + ntf_cb = m_app_table[p_elem->storage_addr.module_id].cb; + } + + ntf_cb(&p_elem->storage_addr, op_code, result, p_elem->p_data_addr, m_app_data_size); +} + + +/**@brief Function for evaluating if a data page swap is required for the tail section on the + * current page. + * + * @retval true If data page swap is required. + * @retval false If data page swap is not required. + */ +static bool is_tail_data_page_swap_required(void) +{ + bool ret_value; + + // Extract id of the last page command is executed upon. + const cmd_queue_element_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + const pstorage_block_t cmd_block_id = p_cmd->storage_addr.block_id; + const uint32_t last_page_id = (cmd_block_id + p_cmd->size + p_cmd->offset - 1u) / + PSTORAGE_FLASH_PAGE_SIZE; + + // If tail section area exists and the current page is the last page then tail data page swap is + // required. + if ((m_tail_word_size != 0) && (m_current_page_id == last_page_id)) + { + ret_value = true; + } + else + { + ret_value = false; + } + + return ret_value; +} + + +/**@brief Function for performing post processing for the update and clear commands. + * + * @details Function for performing post processing for the update and clear commands, which implies + * executing the correct execution path depending on the command. + */ +static void clear_post_processing_run(void) +{ + const cmd_queue_element_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + + if (p_cmd->op_code != PSTORAGE_UPDATE_OP_CODE) + { + command_end_procedure_run(); + } + else + { + store_operation_execute(); + } +} + + +/**@brief Function for doing swap sub state exit action. + */ +static void swap_sub_sm_exit_action_run(void) +{ + clear_post_processing_run(); +} + + +/**@brief Function for evaluating if the page erase operation is required for the current page. + * + * @retval true If page erase is required. + * @retval false If page erase is not required. + */ +static bool is_page_erase_required(void) +{ + bool ret; + + const cmd_queue_element_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + const pstorage_block_t cmd_block_id = p_cmd->storage_addr.block_id; + const uint32_t id_last_page_to_be_cleared = (cmd_block_id + p_cmd->size + + p_cmd->offset - 1u) / + PSTORAGE_FLASH_PAGE_SIZE; + + // True if: + // - current page is not the last page OR + // - current page is the last page AND no tail exists + if ((m_current_page_id < id_last_page_to_be_cleared) || + ((m_current_page_id == id_last_page_to_be_cleared) && (m_tail_word_size == 0))) + { + ret = true; + } + else + { + ret = false; + } + + return ret; +} + + +/**@brief Function for reissuing the last flash operation request, which was rejected by the flash + * API, in swap sub sate. + */ +static void swap_sub_state_err_busy_process(void) +{ + // Reissue the request by doing a self transition to the current state. + m_flags &= ~MASK_FLASH_API_ERR_BUSY; + swap_sub_state_state_change(m_swap_sub_state); +} + + +/**@brief Function for doing restore head state action upon flash operation success event. + * + * @details Function for doing restore head state action upon flash operation success event, which + * includes making a state transition depending on the current state. + */ +static void head_restore_state_run(void) +{ + if (!(m_flags & MASK_FLASH_API_ERR_BUSY)) + { + if (is_tail_data_page_swap_required()) + { + // Additional data page needs to be swapped for tail section as we are clearing a block, + // which is shared between 2 flash pages. + + // Adjust variables to ensure correct state transition path is taken after the tail + // section swap has completed. + m_head_word_size = 0; + m_flags |= MASK_TAIL_SWAP_DONE; + + swap_sub_state_state_change(STATE_ERASE_SWAP); + } + else if (is_page_erase_required()) + { + // Additional page erase operation is required. + + // Adjust variable to ensure correct state transition path is taken after the additional + // page erase operation has completed. + m_head_word_size = 0; + swap_sub_state_state_change(STATE_ERASE_DATA_PAGE); + } + else if (m_tail_word_size != 0) + { + // Proceed with restoring tail from swap to data page. + swap_sub_state_state_change(STATE_RESTORE_TAIL); + } + else + { + // Swap statemachine execution end reached. + swap_sub_sm_exit_action_run(); + } + } + else + { + // As operation request was rejected by the flash API reissue the request. + swap_sub_state_err_busy_process(); + } +} + + +/**@brief Function for doing restore tail state action upon flash operation success event. + */ +static void tail_restore_state_run(void) +{ + if (!(m_flags & MASK_FLASH_API_ERR_BUSY)) + { + swap_sub_sm_exit_action_run(); + } + else + { + // As operation request was rejected by the flash API reissue the request. + swap_sub_state_err_busy_process(); + } +} + + +/**@brief Function for doing data page erase state action upon a flash operation success event. + * + * @details Function for doing data page erase state action upon a flash operation success event, + * which includes making a state transit to a new state depending on the current state. + */ +static void data_page_erase_state_run(void) +{ + if (!(m_flags & MASK_FLASH_API_ERR_BUSY)) + { + ++m_current_page_id; + + if (m_head_word_size != 0) + { + swap_sub_state_state_change(STATE_RESTORE_HEAD); + } + else if (is_page_erase_required()) + { + // Additional page erase operation is required. + swap_sub_state_state_change(STATE_ERASE_DATA_PAGE); + } + else if (m_tail_word_size != 0) + { + if (!(m_flags & MASK_TAIL_SWAP_DONE)) + { + // Tail area restore is required and we have not yet written the relevant data page + // to swap area. Start the process of writing the data page to swap. + m_flags |= MASK_TAIL_SWAP_DONE; + + swap_sub_state_state_change(STATE_ERASE_SWAP); + } + else + { + // Tail area restore is required and we have already written the relevant data page + // to swap area. Proceed by restoring the tail area. + swap_sub_state_state_change(STATE_RESTORE_TAIL); + } + } + else + { + swap_sub_sm_exit_action_run(); + } + } + else + { + // As operation request was rejected by the flash API reissue the request. + swap_sub_state_err_busy_process(); + } +} + + +/**@brief Function for doing data to swap write state action upon flash operation success event. + */ +static void data_to_swap_write_state_run(void) +{ + if (!(m_flags & MASK_FLASH_API_ERR_BUSY)) + { + // If the operation is executed only on 1 single flash page it automatically means that tail + // area is written to the swap, which we store to flags. + if (m_flags & MASK_SINGLE_PAGE_OPERATION) + { + m_flags |= MASK_TAIL_SWAP_DONE; + } + + swap_sub_state_state_change(STATE_ERASE_DATA_PAGE); + } + else + { + // As operation request was rejected by the flash API reissue the request. + swap_sub_state_err_busy_process(); + } +} + + +/**@brief Function for doing swap erase state action upon flash operation success event. + */ +static void swap_erase_state_run(void) +{ + if (!(m_flags & MASK_FLASH_API_ERR_BUSY)) + { + swap_sub_state_state_change(STATE_WRITE_DATA_TO_SWAP); + } + else + { + // As operation request was rejected by the flash API reissue the request. + swap_sub_state_err_busy_process(); + } +} + + +/**@brief Function for dispatching the correct state action for data erase with a swap composite +* state upon a flash operation success event. + */ +static void swap_sub_state_sm_run(void) +{ + static void (* const swap_sub_state_sm_lut[SWAP_SUB_STATE_MAX])(void) = + { + swap_erase_state_run, + data_to_swap_write_state_run, + data_page_erase_state_run, + tail_restore_state_run, + head_restore_state_run + }; + + swap_sub_state_sm_lut[m_swap_sub_state](); +} + + +/**@brief Function for reissuing the last flash operation request, which was rejected by the flash + * API, in main sate. + */ +static void main_state_err_busy_process(void) +{ + // Reissue the request by doing a self transition to the current state. + m_flags &= ~MASK_FLASH_API_ERR_BUSY; + sm_state_change(m_state); +} + + +/**@brief Function for doing erase state action upon flash operation success event. + * + * @details Function for doing erase state action upon flash operation success event, which includes + * making a state transition depending on the current state. + */ +static void erase_sub_state_sm_run(void) +{ + if (!(m_flags & MASK_FLASH_API_ERR_BUSY)) + { + // Clear operation request has succeeded. + ++m_current_page_id; + + if (!is_page_erase_required()) + { + clear_post_processing_run(); + } + else + { + // All required flash pages have not yet been erased, issue erase by doing a self + // transit. + sm_state_change(m_state); + } + } + else + { + // As operation request was rejected by the flash API reissue the request. + main_state_err_busy_process(); + } +} + + +/**@brief Function for doing store state action upon flash operation success event. + */ +static void store_sub_state_sm_run(void) +{ + if (!(m_flags & MASK_FLASH_API_ERR_BUSY)) + { + // As write operation request has succeeded, adjust the size tracking state information + // accordingly. + cmd_queue_element_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + p_cmd->size -= m_num_of_bytes_written; + + if (p_cmd->size == 0) + { + command_end_procedure_run(); + } + else + { + store_cmd_flash_write_execute(); + } + } + else + { + // As operation request was rejected by the flash API reissue the request. + main_state_err_busy_process(); + } +} + + +/**@brief Function for doing action upon flash operation success event. + */ +static void flash_operation_success_run(void) +{ + switch (m_state) + { + case STATE_STORE: + store_sub_state_sm_run(); + break; + + case STATE_DATA_ERASE: + erase_sub_state_sm_run(); + break; + + case STATE_DATA_ERASE_WITH_SWAP: + swap_sub_state_sm_run(); + break; + + default: + // No implementation needed. + break; + } +} + + +/**@brief Function for doing action upon flash operation failure event. + * + * @details Function for doing action upon flash operation failure event, which includes retrying + * the last operation or if retry count has been reached completing the operation with + * appropriate result code and transitioning to an error state. + * + * @note The command is not removed from the command queue, which will result to stalling of the + * command pipeline and the appropriate application recovery procedure for this is to reset + * the system by issuing @ref pstorage_init which will also result to flushing of the + * command queue. + */ +static void flash_operation_failure_run(void) +{ + if (++m_num_of_command_retries != SD_CMD_MAX_TRIES) + { + // Retry the last operation by doing a self transition to the current state. + + if (m_state != STATE_DATA_ERASE_WITH_SWAP) + { + sm_state_change(m_state); + } + else + { + swap_sub_state_state_change(m_swap_sub_state); + } + } + else + { + // Complete the operation with appropriate result code and transit to an error state. + app_notify_error_state_transit(NRF_ERROR_TIMEOUT); + } +} + + +/**@brief Function for handling flash access result events. + * + * @param[in] sys_evt System event to be handled. + */ +void pstorage_sys_event_handler(uint32_t sys_evt) +{ + if (m_state != STATE_IDLE && m_state != STATE_ERROR) + { + switch (sys_evt) + { + case NRF_EVT_FLASH_OPERATION_SUCCESS: + flash_operation_success_run(); + break; + + case NRF_EVT_FLASH_OPERATION_ERROR: + if (!(m_flags & MASK_FLASH_API_ERR_BUSY)) + { + flash_operation_failure_run(); + } + else + { + // As our last flash operation request was rejected by the flash API reissue the + // request by doing same code execution path as for flash operation sucess + // event. This will promote code reuse in the implementation. + flash_operation_success_run(); + } + break; + + default: + // No implementation needed. + break; + } + + } +} + + +/**@brief Function for calculating the tail area size in number of 32-bit words. + * + * @param[in] cmd_end_of_storage_address End of storage area within the scope of the command. + * @param[in] end_of_storage_address End of allocated storage area for the application. + */ +static void tail_word_size_calculate(pstorage_size_t cmd_end_of_storage_address, + pstorage_size_t end_of_storage_address) +{ + // Two different cases to resolve when calculating correct size for restore tail section: + // 1) End of storage area and command end area are in the same page. + // 2) End of storage area and command end area are not in the same page. + + const uint32_t end_of_storage_area_page = end_of_storage_address / + PSTORAGE_FLASH_PAGE_SIZE; + const uint32_t command_end_of_storage_area_page = cmd_end_of_storage_address / + PSTORAGE_FLASH_PAGE_SIZE; + + if (end_of_storage_area_page == command_end_of_storage_area_page) + { + //lint -e{573} suppress "Signed-unsigned mix with divide". + m_tail_word_size = (end_of_storage_address - cmd_end_of_storage_address) / sizeof(uint32_t); + } + else + { + //lint -e{573} suppress "Signed-unsigned mix with divide". + m_tail_word_size = (PSTORAGE_FLASH_PAGE_SIZE - + (cmd_end_of_storage_address % PSTORAGE_FLASH_PAGE_SIZE)) / + sizeof(uint32_t); + } +} + + +/**@brief Function for executing the clear operation. + */ +static void clear_operation_execute(void) +{ + const cmd_queue_element_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + const pstorage_block_t cmd_block_id = p_cmd->storage_addr.block_id; + + const pstorage_size_t block_size = m_app_table[p_cmd->storage_addr.module_id].block_size; + const pstorage_size_t block_count = m_app_table[p_cmd->storage_addr.module_id].block_count; + const pstorage_block_t block_base_id = m_app_table[p_cmd->storage_addr.module_id].base_id; + + const bool is_start_address_page_aligned = (cmd_block_id % PSTORAGE_FLASH_PAGE_SIZE) == 0; + + // Calculate the end (1 beyond allocated area) for complete storage area and to the area only + // within scope of this command. + const pstorage_block_t end_of_storage_address = block_base_id + (block_size * block_count); + const pstorage_block_t cmd_end_of_storage_address = cmd_block_id + p_cmd->size + p_cmd->offset; + + // Zero tail to make sure no extra erase is done erroneously. + m_tail_word_size = 0; + + // If the following is true no swap access is needed: + // - 1st logical test covers the case of: clear/update 1 complete single page. + // - 2nd logical test covers the case of: + // 1) Clear/update last allocated page and page is not full (page can't be shared between + // multiple clients so the end of the page is unused area). + // 2) Clear/update all allocated storage. + if ((is_start_address_page_aligned && (p_cmd->size == PSTORAGE_FLASH_PAGE_SIZE)) || + (is_start_address_page_aligned && (cmd_end_of_storage_address == end_of_storage_address) && + (p_cmd->offset == 0)) || (p_cmd->storage_addr.module_id == RAW_MODE_APP_ID)) + { + // Nothing to put to the swap and we can just erase the pages(s). + + m_current_page_id = cmd_block_id / PSTORAGE_FLASH_PAGE_SIZE; + + sm_state_change(STATE_DATA_ERASE); + } + else + { + // Not all the blocks for the module can be cleared, we need to use swap page for storing + // data temporarily. + + m_head_word_size = ((cmd_block_id + p_cmd->offset) % PSTORAGE_FLASH_PAGE_SIZE) / + sizeof(uint32_t); + + const bool is_cmd_end_address_page_aligned = ((cmd_end_of_storage_address % + PSTORAGE_FLASH_PAGE_SIZE) == 0); + if ((cmd_end_of_storage_address != end_of_storage_address) && + !is_cmd_end_address_page_aligned) + { + // When command area is not equal to end of the storage allocation area and not ending + // to page boundary there is a need to restore the tail area. + tail_word_size_calculate(cmd_end_of_storage_address, end_of_storage_address); + } + + sm_state_change(STATE_DATA_ERASE_WITH_SWAP); + } +} + + +/**@brief Function for executing the store operation. + */ +static void store_operation_execute(void) +{ + sm_state_change(STATE_STORE); +} + + +/**@brief Function for executing the update operation. + */ +static void update_operation_execute(void) +{ + clear_operation_execute(); +} + + +/**@brief Function for dispatching the flash access operation. + */ +static void cmd_process(void) +{ + const cmd_queue_element_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; + m_app_data_size = p_cmd->size; + + switch (p_cmd->op_code) + { + case PSTORAGE_STORE_OP_CODE: + store_operation_execute(); + break; + + case PSTORAGE_CLEAR_OP_CODE: + clear_operation_execute(); + break; + + case PSTORAGE_UPDATE_OP_CODE: + update_operation_execute(); + break; + + default: + // No action required. + break; + } +} + + +uint32_t pstorage_init(void) +{ + cmd_queue_init(); + + m_next_app_instance = 0; + m_next_page_addr = PSTORAGE_DATA_START_ADDR; + m_current_page_id = 0; + + for (uint32_t index = 0; index < PSTORAGE_NUM_OF_PAGES; index++) + { + m_app_table[index].cb = NULL; + m_app_table[index].block_size = 0; + m_app_table[index].block_count = 0; + } + +#ifdef PSTORAGE_RAW_MODE_ENABLE + m_raw_app_table.cb = NULL; +#endif //PSTORAGE_RAW_MODE_ENABLE + + m_state = STATE_IDLE; + m_num_of_command_retries = 0; + m_flags = 0; + m_num_of_bytes_written = 0; + m_flags |= MASK_MODULE_INITIALIZED; + + return NRF_SUCCESS; +} + + +uint32_t pstorage_register(pstorage_module_param_t * p_module_param, + pstorage_handle_t * p_block_id) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_module_param); + NULL_PARAM_CHECK(p_block_id); + NULL_PARAM_CHECK(p_module_param->cb); + BLOCK_SIZE_CHECK(p_module_param->block_size); + BLOCK_COUNT_CHECK(p_module_param->block_count, p_module_param->block_size); + + if (!((p_module_param->block_size % sizeof(uint32_t)) == 0)) + { + return NRF_ERROR_INVALID_PARAM; + } + + if (m_next_app_instance == PSTORAGE_NUM_OF_PAGES) + { + return NRF_ERROR_NO_MEM; + } + + p_block_id->module_id = m_next_app_instance; + p_block_id->block_id = m_next_page_addr; + + m_app_table[m_next_app_instance].base_id = p_block_id->block_id; + m_app_table[m_next_app_instance].cb = p_module_param->cb; + m_app_table[m_next_app_instance].block_size = p_module_param->block_size; + m_app_table[m_next_app_instance].block_count = p_module_param->block_count; + + // Calculate number of flash pages allocated for the device and adjust next free page address. + /*lint -save -e666 */ + const uint32_t page_count = CEIL_DIV((p_module_param->block_size * p_module_param->block_count), + PSTORAGE_FLASH_PAGE_SIZE); + /*lint -restore */ + m_next_page_addr += page_count * PSTORAGE_FLASH_PAGE_SIZE; + + ++m_next_app_instance; + + return NRF_SUCCESS; +} + + +uint32_t pstorage_block_identifier_get(pstorage_handle_t * p_base_id, + pstorage_size_t block_num, + pstorage_handle_t * p_block_id) +{ + pstorage_handle_t temp_id; + + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_base_id); + NULL_PARAM_CHECK(p_block_id); + MODULE_ID_RANGE_CHECK(p_base_id); + + temp_id = (*p_base_id); + temp_id.block_id += (block_num * MODULE_BLOCK_SIZE(p_base_id)); + + BLOCK_ID_RANGE_CHECK(&temp_id); + + (*p_block_id) = temp_id; + + return NRF_SUCCESS; +} + + +uint32_t pstorage_store(pstorage_handle_t * p_dest, + uint8_t * p_src, + pstorage_size_t size, + pstorage_size_t offset) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_src); + NULL_PARAM_CHECK(p_dest); + MODULE_ID_RANGE_CHECK(p_dest); + BLOCK_ID_RANGE_CHECK(p_dest); + SIZE_CHECK(p_dest, size); + OFFSET_CHECK(p_dest, offset, size); + + if ((!is_word_aligned(p_src)) || + (!is_word_aligned((void *)(uint32_t)offset)) || + (!is_word_aligned((uint32_t *)p_dest->block_id))) + { + return NRF_ERROR_INVALID_ADDR; + } + + return cmd_queue_enqueue(PSTORAGE_STORE_OP_CODE, p_dest, p_src, size, offset); +} + + +uint32_t pstorage_update(pstorage_handle_t * p_dest, + uint8_t * p_src, + pstorage_size_t size, + pstorage_size_t offset) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_src); + NULL_PARAM_CHECK(p_dest); + MODULE_ID_RANGE_CHECK(p_dest); + BLOCK_ID_RANGE_CHECK(p_dest); + SIZE_CHECK(p_dest, size); + OFFSET_CHECK(p_dest, offset, size); + + if ((!is_word_aligned(p_src)) || + (!is_word_aligned((void *)(uint32_t)offset)) || + (!is_word_aligned((uint32_t *)p_dest->block_id))) + { + return NRF_ERROR_INVALID_ADDR; + } + + return cmd_queue_enqueue(PSTORAGE_UPDATE_OP_CODE, p_dest, p_src, size, offset); +} + + +uint32_t pstorage_load(uint8_t * p_dest, + pstorage_handle_t * p_src, + pstorage_size_t size, + pstorage_size_t offset) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_src); + NULL_PARAM_CHECK(p_dest); + MODULE_ID_RANGE_CHECK(p_src); + BLOCK_ID_RANGE_CHECK(p_src); + SIZE_CHECK(p_src, size); + OFFSET_CHECK(p_src, offset, size); + + if ((!is_word_aligned(p_dest)) || + (!is_word_aligned((void *)(uint32_t)offset)) || + (!is_word_aligned((uint32_t *)p_src->block_id))) + { + return NRF_ERROR_INVALID_ADDR; + } + + memcpy(p_dest, (((uint8_t *)p_src->block_id) + offset), size); + + m_app_table[p_src->module_id].cb(p_src, PSTORAGE_LOAD_OP_CODE, NRF_SUCCESS, p_dest, size); + + return NRF_SUCCESS; +} + + +uint32_t pstorage_clear(pstorage_handle_t * p_dest, pstorage_size_t size) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_dest); + MODULE_ID_RANGE_CHECK(p_dest); + BLOCK_ID_RANGE_CHECK(p_dest); + + if ((!is_word_aligned((uint32_t *)p_dest->block_id))) + { + return NRF_ERROR_INVALID_ADDR; + } + + // Check is the area starting from block_id multiple of block_size. + if ( + !( + ((p_dest->block_id - m_app_table[p_dest->module_id].base_id) % + m_app_table[p_dest->module_id].block_size) == 0 + ) + ) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Check is requested size multiple of registered block size or 0. + if (((size % m_app_table[p_dest->module_id].block_size) != 0) || (size == 0)) + { + return NRF_ERROR_INVALID_PARAM; + } + + const uint32_t registered_allocation_size = m_app_table[p_dest->module_id].block_size * + m_app_table[p_dest->module_id].block_count; + + const pstorage_block_t clear_request_end_address = p_dest->block_id + size; + const pstorage_block_t allocation_end_address = m_app_table[p_dest->module_id].base_id + + registered_allocation_size; + // Check if request would lead to a buffer overrun. + if (clear_request_end_address > allocation_end_address) + { + return NRF_ERROR_INVALID_PARAM; + } + + return cmd_queue_enqueue(PSTORAGE_CLEAR_OP_CODE, p_dest, NULL, size, 0); +} + + +uint32_t pstorage_access_status_get(uint32_t * p_count) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_count); + + (*p_count) = m_cmd_queue.count; + + return NRF_SUCCESS; +} + +#ifdef PSTORAGE_RAW_MODE_ENABLE + +uint32_t pstorage_raw_register(pstorage_module_param_t * p_module_param, + pstorage_handle_t * p_block_id) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_module_param); + NULL_PARAM_CHECK(p_block_id); + NULL_PARAM_CHECK(p_module_param->cb); + + if (m_raw_app_table.cb != NULL) + { + return NRF_ERROR_NO_MEM; + } + + p_block_id->module_id = RAW_MODE_APP_ID; + m_raw_app_table.cb = p_module_param->cb; + + return NRF_SUCCESS; +} + + +uint32_t pstorage_raw_store(pstorage_handle_t * p_dest, + uint8_t * p_src, + pstorage_size_t size, + pstorage_size_t offset) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_src); + NULL_PARAM_CHECK(p_dest); + MODULE_RAW_HANDLE_CHECK(p_dest); + + if (size == 0) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Verify word alignment. + if ((!is_word_aligned(p_src)) || + (!is_word_aligned((void *)(uint32_t)size)) || + (!is_word_aligned((void *)(uint32_t)offset)) || + (!is_word_aligned((void *)(p_dest->block_id)))) + { + return NRF_ERROR_INVALID_ADDR; + } + + return cmd_queue_enqueue(PSTORAGE_STORE_OP_CODE, p_dest, p_src, size, offset); +} + + +uint32_t pstorage_raw_clear(pstorage_handle_t * p_dest, pstorage_size_t size) +{ + VERIFY_MODULE_INITIALIZED(); + NULL_PARAM_CHECK(p_dest); + MODULE_RAW_HANDLE_CHECK(p_dest); + + if ((!is_word_aligned((uint32_t *)p_dest->block_id))) + { + return NRF_ERROR_INVALID_ADDR; + } + + return cmd_queue_enqueue(PSTORAGE_CLEAR_OP_CODE, p_dest, NULL, size, 0); +} + +#endif // PSTORAGE_RAW_MODE_ENABLE diff --git a/cores/arduino/components/drivers_nrf/pstorage/pstorage.h b/cores/arduino/components/drivers_nrf/pstorage/pstorage.h new file mode 100644 index 0000000..d500599 --- /dev/null +++ b/cores/arduino/components/drivers_nrf/pstorage/pstorage.h @@ -0,0 +1,381 @@ +/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +/**@file + * + * @defgroup persistent_storage Persistent Storage Interface + * @{ + * @ingroup app_common + * @brief Abstracted flash interface. + * + * @details An abstracted interface is provided by the module to easily port the application and + * SDK modules to an alternate option. This ensures that the SDK and application are moved + * to alternate persistent storage instead of the one provided by default. + */ + +#ifndef PSTORAGE_H__ +#define PSTORAGE_H__ + +#include "pstorage_platform.h" + + +/**@defgroup ps_opcode Persistent Storage Access Operation Codes + * @{ + * @brief Persistent Storage Access Operation Codes. + * + * @details Persistent Storage Access Operation Codes are used by Persistent storage operation + * completion callback @ref pstorage_ntf_cb_t to identify the operation type requested by + * the application. + */ +#define PSTORAGE_STORE_OP_CODE 0x01 /**< Store Operation type. */ +#define PSTORAGE_LOAD_OP_CODE 0x02 /**< Load Operation type. */ +#define PSTORAGE_CLEAR_OP_CODE 0x03 /**< Clear Operation type. */ +#define PSTORAGE_UPDATE_OP_CODE 0x04 /**< Update Operation type. */ + +/**@} */ + +/**@defgroup pstorage_data_types Persistent Memory Interface Data Types + * @{ + * @brief Data Types needed for interfacing with persistent memory. + * + * @details Data Types needed for interfacing with persistent memory. + */ + +/**@brief Persistent storage operation completion callback function type. + * + * @details The persistent storage operation completion callback is used by the interface to report + * success or failure of a flash operation. Since data is not copied for a store operation, + * a callback is an indication that the resident memory can now be reused or freed. + * + * @param[in] handle Identifies the module and block for the callback that is received. + * @param[in] op_code Identifies the operation for the event that is notified. + * @param[in] result Identifies the result of a flash access operation. NRF_SUCCESS implies + * operation succeeded. + * + * @note Unmanaged (abnormal behaviour) error codes from the SoftDevice flash + * access API are forwarded as is and are expected to be handled by the + * application. For details refer to the implementation file and corresponding + * SoftDevice flash API documentation. + * + * @param[in] p_data Identifies the application data pointer. For a store operation, this points + * to the resident source of application memory that the application can now + * free or reuse. When there is a clear operation, this is NULL since no + * application pointer is needed for this operation. + * @param[in] data_len Length data the application provided for the operation. + */ +typedef void (*pstorage_ntf_cb_t)(pstorage_handle_t * p_handle, + uint8_t op_code, + uint32_t result, + uint8_t * p_data, + uint32_t data_len); + +/**@brief Struct containing module registration context. */ +typedef struct +{ + pstorage_ntf_cb_t cb; /**< Persistent storage operation completion callback function @ref pstorage_ntf_cb_t. */ + pstorage_size_t block_size; /**< Desired block size for persistent memory storage. For example, if a module has a table with 10 entries, and each entry is 64 bytes in size, + * it can request 10 blocks with a block size of 64 bytes. The module can also request one block that is 640 bytes depending + * on how it would like to access or alter the memory in persistent memory. + * The first option is preferred when it is a single entry that needs to be updated often and doesn't impact the other entries. + * The second option is preferred when table entries are not changed individually but have a common point of loading and storing + * data. */ + pstorage_size_t block_count; /** Number of blocks requested by the module; minimum values is 1. */ +} pstorage_module_param_t; + +/**@} */ + +/**@defgroup pstorage_routines Persistent Storage Access Routines + * @{ + * @brief Functions/Interface SDK modules used to persistently store data. + * + * @details Interface for the Application and SDK modules to load/store information persistently. + * Note: While implementation of each of the persistent storage access functions + * depends on the system and is specific to system/solution, the signature of the + * interface routines should not be altered. + */ + +/**@brief Function for initializing the module. + * + * @details Function for initializing the module. This function is called once before any other APIs + * of the module are used. + * + * @retval NRF_SUCCESS Operation success. + */ +uint32_t pstorage_init(void); + +/**@brief Function for registering with persistent storage interface. + * + * @param[in] p_module_param Module registration parameter. + * @param[out] p_block_id Block identifier to identify persistent memory blocks when + * registration succeeds. Application is expected to use the block IDs + * for subsequent operations on requested persistent memory. Maximum + * registrations permitted is determined by the configuration of the + * parameter PSTORAGE_NUM_OF_PAGES. If more than one memory block is + * requested, the identifier provided here is the base identifier for the + * first block and used to identify the subsequent block. The application + * uses \@ref pstorage_block_identifier_get with this base identifier and + * block number. Therefore if 10 blocks of size 64 are requested and the + * application wishes to store memory in the 6th block, it shall use + * \@ref pstorage_block_identifier_get with the base ID and provide a + * block number of 5. This way the application is only expected to + * remember the base block identifier. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. API is called without module + * initialization. + * @retval NRF_ERROR_NULL Operation failure. NULL parameter has been passed. + * @retval NRF_ERROR_INVALID_PARAM Operation failure. Invalid parameter has been passed. + * @retval NRF_ERROR_NO_MEM Operation failure. Additional registrations can't be + * supported. + */ +uint32_t pstorage_register(pstorage_module_param_t * p_module_param, + pstorage_handle_t * p_block_id); + +/**@brief Function for getting block ID with reference to base block identifier provided at the time + * of registration. + * + * @details Function to get the block ID with reference to base block identifier provided at the + * time of registration. + * If more than one memory block was requested when registering, the identifier provided + * here is the base identifier for the first block which is used to identify subsequent + * blocks. The application shall use this routine to get the block identifier, providing + * input as base identifier and block number. Therefore, if 10 blocks of size 64 are + * requested and the application wishes to store memory in the 6th block, it shall use + * \@ref pstorage_block_identifier_get with the base ID and provide a block number of 5. + * This way the application is only expected to remember the base block identifier. + * + * @param[in] p_base_id Base block ID received at the time of registration. + * @param[in] block_num Block Number, with first block numbered zero. + * @param[out] p_block_id Block identifier for the block number requested when the API succeeds. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. API is called without module + * initialization. + * @retval NRF_ERROR_NULL Operation failure. NULL parameter has been passed. + * @retval NRF_ERROR_INVALID_PARAM Operation failure. Invalid parameter has been passed. + */ +uint32_t pstorage_block_identifier_get(pstorage_handle_t * p_base_id, + pstorage_size_t block_num, + pstorage_handle_t * p_block_id); + +/**@brief Function for persistently storing data of length 'size' contained in the 'p_src' address + * in the storage module at 'p_dest' address. Equivalent to Storage Write. + * + * @param[in] p_dest Destination address where data is to be stored persistently. + * @param[in] p_src Source address containing data to be stored. API assumes this to be resident + * memory and no intermediate copy of data is made by the API. Must be word + * aligned. + * @param[in] size Size of data to be stored expressed in bytes. Must be word aligned and size + + * offset must be <= block size. + * @param[in] offset Offset in bytes to be applied when writing to the block. + * For example, if within a block of 100 bytes, the application wishes to + * write 20 bytes at an offset of 12, then this field should be set to 12. + * Must be word aligned. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. API is called without module + * initialization. + * @retval NRF_ERROR_NULL Operation failure. NULL parameter has been passed. + * @retval NRF_ERROR_INVALID_PARAM Operation failure. Invalid parameter has been passed. + * @retval NRF_ERROR_INVALID_ADDR Operation failure. Parameter is not aligned. + * @retval NRF_ERROR_NO_MEM Operation failure. No storage space available. + * + * @warning No copy of the data is made, meaning memory provided for the data source that is to + * be written to flash cannot be freed or reused by the application until this procedure + * is complete. The application is notified when the procedure is finished using the + * notification callback registered by the application. + */ +uint32_t pstorage_store(pstorage_handle_t * p_dest, + uint8_t * p_src, + pstorage_size_t size, + pstorage_size_t offset); + +/**@brief Function for updating persistently stored data of length 'size' contained in the 'p_src' + * address in the storage module at 'p_dest' address. + * + * @param[in] p_dest Destination address where data is to be updated. + * @param[in] p_src Source address containing data to be stored. API assumes this to be resident + * memory and no intermediate copy of data is made by the API. + * @param[in] size Size of data to be stored expressed in bytes. Must be word aligned and size + + * offset must be <= block size. + * @param[in] offset Offset in bytes to be applied when writing to the block. + * For example, if within a block of 100 bytes, the application wishes to + * write 20 bytes at an offset of 12 bytes, then this field should be set to 12. + * Must be word aligned. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. API is called without module + * initialization. + * @retval NRF_ERROR_NULL Operation failure. NULL parameter has been passed. + * @retval NRF_ERROR_INVALID_PARAM Operation failure. Invalid parameter has been passed. + * @retval NRF_ERROR_INVALID_ADDR Operation failure. Parameter is not aligned. + * @retval NRF_ERROR_NO_MEM Operation failure. No storage space available. + * + * @warning No copy of the data is made, meaning memory provided for the data source that is to + * be written to flash cannot be freed or reused by the application until this procedure + * is complete. The application is notified when the procedure is finished using the + * notification callback registered by the application. + */ +uint32_t pstorage_update(pstorage_handle_t * p_dest, + uint8_t * p_src, + pstorage_size_t size, + pstorage_size_t offset); + +/**@brief Function for loading persistently stored data of length 'size' from 'p_src' address + * to 'p_dest' address. Equivalent to Storage Read. + * + * @param[in] p_dest Destination address where persistently stored data is to be loaded. + * @param[in] p_src Source where data is loaded from persistent memory. + * @param[in] size Size of data to be loaded from persistent memory expressed in bytes. + * Should be word aligned. + * @param[in] offset Offset in bytes, to be applied when loading from the block. + * For example, if within a block of 100 bytes, the application wishes to + * load 20 bytes from offset of 12 bytes, then this field should be set to 12. + * Should be word aligned. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. API is called without module + * initialization. + * @retval NRF_ERROR_NULL Operation failure. NULL parameter has been passed. + * @retval NRF_ERROR_INVALID_PARAM Operation failure. Invalid parameter has been passed. + * @retval NRF_ERROR_INVALID_ADDR Operation failure. Parameter is not aligned. + * @retval NRF_ERROR_NO_MEM Operation failure. No storage space available. + */ +uint32_t pstorage_load(uint8_t * p_dest, + pstorage_handle_t * p_src, + pstorage_size_t size, + pstorage_size_t offset); + +/**@brief Function for clearing data in persistent memory. + * + * @param[in] p_base_id Base block identifier in persistent memory that needs to be cleared; + * equivalent to an Erase Operation. + * @param[in] size Size of data to be cleared from persistent memory expressed in bytes. + * This parameter is to provision for clearing of certain blocks + * of memory, or all memory blocks in a registered module. If the total size + * of the application module is used (blocks * block size) in combination with + * the identifier for the first block in the module, all blocks in the + * module will be erased. Must be multiple of block size. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. API is called without module + * initialization. + * @retval NRF_ERROR_NULL Operation failure. NULL parameter has been passed. + * @retval NRF_ERROR_INVALID_PARAM Operation failure. Invalid parameter has been passed. + * @retval NRF_ERROR_INVALID_ADDR Operation failure. Parameter is not aligned. + * @retval NRF_ERROR_NO_MEM Operation failure. No storage space available. + * + * @note Clear operations may take time. This API however, does not block until the clear + * procedure is complete. The application is notified of procedure completion using + * a notification callback registered by the application. The 'result' parameter of the + * callback indicates if the procedure was successful or not. + */ +uint32_t pstorage_clear(pstorage_handle_t * p_base_id, pstorage_size_t size); + +/**@brief Function for getting the number of pending operations with the module. + * + * @param[out] p_count Number of storage operations pending with the module. If 0, there are no + * outstanding requests. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. API is called without module + * initialization. + * @retval NRF_ERROR_NULL Operation failure. NULL parameter has been passed. + */ +uint32_t pstorage_access_status_get(uint32_t * p_count); + +#ifdef PSTORAGE_RAW_MODE_ENABLE + +/**@brief Function for registering with the persistent storage interface. + * + * @param[in] p_module_param Module registration parameter. + * @param[out] p_block_id Block identifier used to identify persistent memory blocks upon + * successful registration. The application is expected to use the block + * IDs for subsequent operations on requested persistent memory. When + * more than one memory block is requested, this identifier is the base + * identifier for the first block and used to identify subsequent blocks. + * The application shall use \@ref pstorage_block_identifier_get with + * this base identifier and block number. Therefore if 10 blocks of size + * 64 are requested and the application wishes to store memory in the 6th + * block, it shall use \@ref pstorage_block_identifier_get with the base + * ID and provide a block number of 5. Therefore, the application is only + * expected to remember the base block identifier. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. API is called without module + * initialization. + * @retval NRF_ERROR_NULL Operation failure. NULL parameter has been passed. + * @retval NRF_ERROR_INVALID_PARAM Operation failure. Invalid parameter has been passed. + * @retval NRF_ERROR_NO_MEM Operation failure. No storage space available. + */ +uint32_t pstorage_raw_register(pstorage_module_param_t * p_module_param, + pstorage_handle_t * p_block_id); + +/**@brief Function for persistently storing data of length 'size' contained in 'p_src' address in + * storage module at 'p_dest' address. Equivalent to Storage Write. + * + * @param[in] p_dest Destination address where data is to be stored persistently. + * @param[in] p_src Source address containing data to be stored. The API assumes this is resident + * memory and no intermediate copy of data is made by the API. Must be word + * aligned. + * @param[in] size Size of data to be stored expressed in bytes. Must be word aligned. + * @param[in] offset Offset in bytes to be applied when writing to the block. + * For example, if within a block of 100 bytes, the application wishes to + * write 20 bytes at an offset of 12 bytes, this field should be set to 12. + * Must be word aligned. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. API is called without module + * initialization. + * @retval NRF_ERROR_NULL Operation failure. NULL parameter has been passed. + * @retval NRF_ERROR_INVALID_PARAM Operation failure. Invalid parameter has been passed. + * @retval NRF_ERROR_INVALID_ADDR Operation failure. Parameter is not aligned. + * @retval NRF_ERROR_NO_MEM Operation failure. No storage space available. + * + * @warning No copy of the data is made, meaning memory provided for data source that is to be + * written to flash cannot be freed or reused by the application until this procedure + * is complete. The application is notified when the procedure is finished using the + * notification callback registered by the application. + */ +uint32_t pstorage_raw_store(pstorage_handle_t * p_dest, + uint8_t * p_src, + pstorage_size_t size, + pstorage_size_t offset); + +/**@brief Function for clearing data in persistent memory in raw mode. + * + * @param[in] p_dest Base block identifier in persistent memory that needs to be cleared. + * Equivalent to an Erase Operation. + * @param[in] size Size of data to be cleared from persistent memory expressed in bytes. + * Not used. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. API is called without module + * initialization. + * @retval NRF_ERROR_NULL Operation failure. NULL parameter has been passed. + * @retval NRF_ERROR_INVALID_PARAM Operation failure. Invalid parameter has been passed. + * @retval NRF_ERROR_NO_MEM Operation failure. No storage space available. + * + * @note Clear operations may take time. This API, however, does not block until the clear + * procedure is complete. The application is notified of procedure completion using + * a notification callback registered by the application. The 'result' parameter of the + * callback indicates if the procedure was successful or not. + */ +uint32_t pstorage_raw_clear(pstorage_handle_t * p_dest, pstorage_size_t size); + +#endif // PSTORAGE_RAW_MODE_ENABLE + +/**@} */ +/**@} */ + +#endif // PSTORAGE_H__ + diff --git a/cores/arduino/components/libraries/bootloader_dfu/bootloader_types.h b/cores/arduino/components/libraries/bootloader_dfu/bootloader_types.h new file mode 100644 index 0000000..da3ce2c --- /dev/null +++ b/cores/arduino/components/libraries/bootloader_dfu/bootloader_types.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +/**@file + * + * @defgroup nrf_bootloader_types Types and definitions. + * @{ + * + * @ingroup nrf_bootloader + * + * @brief Bootloader module type and definitions. + */ + +#ifndef BOOTLOADER_TYPES_H__ +#define BOOTLOADER_TYPES_H__ + +#include + +#define BOOTLOADER_DFU_START 0xB1 + +#define BOOTLOADER_SVC_APP_DATA_PTR_GET 0x02 + +/**@brief DFU Bank state code, which indicates wether the bank contains: A valid image, invalid image, or an erased flash. + */ +typedef enum +{ + BANK_VALID_APP = 0x01, + BANK_VALID_SD = 0xA5, + BANK_VALID_BOOT = 0xAA, + BANK_ERASED = 0xFE, + BANK_INVALID_APP = 0xFF, +} bootloader_bank_code_t; + +/**@brief Structure holding bootloader settings for application and bank data. + */ +typedef struct +{ + bootloader_bank_code_t bank_0; /**< Variable to store if bank 0 contains a valid application. */ + uint16_t bank_0_crc; /**< If bank is valid, this field will contain a valid CRC of the total image. */ + bootloader_bank_code_t bank_1; /**< Variable to store if bank 1 has been erased/prepared for new image. Bank 1 is only used in Banked Update scenario. */ + uint32_t bank_0_size; /**< Size of active image in bank0 if present, otherwise 0. */ + uint32_t sd_image_size; /**< Size of SoftDevice image in bank0 if bank_0 code is BANK_VALID_SD. */ + uint32_t bl_image_size; /**< Size of Bootloader image in bank0 if bank_0 code is BANK_VALID_SD. */ + uint32_t app_image_size; /**< Size of Application image in bank0 if bank_0 code is BANK_VALID_SD. */ + uint32_t sd_image_start; /**< Location in flash where SoftDevice image is stored for SoftDevice update. */ +} bootloader_settings_t; + +#endif // BOOTLOADER_TYPES_H__ + +/**@} */ diff --git a/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.c b/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.c new file mode 100644 index 0000000..8912805 --- /dev/null +++ b/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.c @@ -0,0 +1,152 @@ +/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#include "bootloader_util.h" +#include +#include + + +/** + * @brief Function for aborting current application/bootloader jump to to other app/bootloader. + * + * @details This functions will use the address provide to swap the stack pointer and then load + * the address of the reset handler to be executed. It will check current system mode + * (thread/handler) and if in thread mode it will reset into other application. + * If in handler mode \ref isr_abort will be executed to ensure correct exit of handler + * mode and jump into reset handler of other application. + * + * @param[in] start_addr Start address of other application. This address must point to the + initial stack pointer of the application. + * + * @note This function will never return but issue a reset into provided application. + */ +#if defined ( __CC_ARM ) +__asm static void bootloader_util_reset(uint32_t start_addr) +{ + LDR R5, [R0] ; Get App initial MSP for bootloader. + MSR MSP, R5 ; Set the main stack pointer to the applications MSP. + LDR R0, [R0, #0x04] ; Load Reset handler into R0. This will be first argument to branch instruction (BX). + + MOVS R4, #0xFF ; Load ones to R4. + SXTB R4, R4 ; Sign extend R4 to obtain 0xFFFFFFFF instead of 0xFF. + MRS R5, IPSR ; Load IPSR to R5 to check for handler or thread mode. + CMP R5, #0x00 ; Compare, if 0 then we are in thread mode and can continue to reset handler of bootloader. + BNE isr_abort ; If not zero we need to exit current ISR and jump to reset handler of bootloader. + + MOV LR, R4 ; Clear the link register and set to ones to ensure no return, R4 = 0xFFFFFFFF. + BX R0 ; Branch to reset handler of bootloader. + +isr_abort + ; R4 contains ones from line above. Will be popped as R12 when exiting ISR (Cleaning up the registers). + MOV R5, R4 ; Fill with ones before jumping to reset handling. We be popped as LR when exiting ISR. Ensures no return to application. + MOV R6, R0 ; Move address of reset handler to R6. Will be popped as PC when exiting ISR. Ensures the reset handler will be executed when exist ISR. + MOVS r7, #0x21 ; Move MSB reset value of xPSR to R7. Will be popped as xPSR when exiting ISR. xPSR is 0x21000000 thus MSB is 0x21. + REV r7, r7 ; Reverse byte order to put 0x21 as MSB. + PUSH {r4-r7} ; Push everything to new stack to allow interrupt handler to fetch it on exiting the ISR. + + MOVS R4, #0x00 ; Fill with zeros before jumping to reset handling. We be popped as R0 when exiting ISR (Cleaning up of the registers). + MOVS R5, #0x00 ; Fill with zeros before jumping to reset handling. We be popped as R1 when exiting ISR (Cleaning up of the registers). + MOVS R6, #0x00 ; Fill with zeros before jumping to reset handling. We be popped as R2 when exiting ISR (Cleaning up of the registers). + MOVS R7, #0x00 ; Fill with zeros before jumping to reset handling. We be popped as R3 when exiting ISR (Cleaning up of the registers). + PUSH {r4-r7} ; Push zeros (R4-R7) to stack to prepare for exiting the interrupt routine. + + MOVS R0, #0xF9 ; Move the execution return command into register, 0xFFFFFFF9. + SXTB R0, R0 ; Sign extend R0 to obtain 0xFFFFFFF9 instead of 0xF9. + BX R0 ; No return - Handler mode will be exited. Stack will be popped and execution will continue in reset handler initializing other application. + ALIGN +} +#elif defined ( __GNUC__ ) +static inline void bootloader_util_reset(uint32_t start_addr) +{ + __asm volatile( + "ldr r0, [%0]\t\n" // Get App initial MSP for bootloader. + "msr msp, r0\t\n" // Set the main stack pointer to the applications MSP. + "ldr r0, [%0, #0x04]\t\n" // Load Reset handler into R0. + + "movs r4, #0xFF\t\n" // Move ones to R4. + "sxtb r4, r4\t\n" // Sign extend R4 to obtain 0xFFFFFFFF instead of 0xFF. + + "mrs r5, IPSR\t\n" // Load IPSR to R5 to check for handler or thread mode. + "cmp r5, #0x00\t\n" // Compare, if 0 then we are in thread mode and can continue to reset handler of bootloader. + "bne isr_abort\t\n" // If not zero we need to exit current ISR and jump to reset handler of bootloader. + + "mov lr, r4\t\n" // Clear the link register and set to ones to ensure no return. + "bx r0\t\n" // Branch to reset handler of bootloader. + + "isr_abort: \t\n" + + "mov r5, r4\t\n" // Fill with ones before jumping to reset handling. Will be popped as LR when exiting ISR. Ensures no return to application. + "mov r6, r0\t\n" // Move address of reset handler to R6. Will be popped as PC when exiting ISR. Ensures the reset handler will be executed when exist ISR. + "movs r7, #0x21\t\n" // Move MSB reset value of xPSR to R7. Will be popped as xPSR when exiting ISR. xPSR is 0x21000000 thus MSB is 0x21. + "rev r7, r7\t\n" // Reverse byte order to put 0x21 as MSB. + "push {r4-r7}\t\n" // Push everything to new stack to allow interrupt handler to fetch it on exiting the ISR. + + "movs r4, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R0 when exiting ISR (Cleaning up of the registers). + "movs r5, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R1 when exiting ISR (Cleaning up of the registers). + "movs r6, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R2 when exiting ISR (Cleaning up of the registers). + "movs r7, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R3 when exiting ISR (Cleaning up of the registers). + "push {r4-r7}\t\n" // Push zeros (R4-R7) to stack to prepare for exiting the interrupt routine. + + "movs r0, #0xF9\t\n" // Move the execution return command into register, 0xFFFFFFF9. + "sxtb r0, r0\t\n" // Sign extend R0 to obtain 0xFFFFFFF9 instead of 0xF9. + "bx r0\t\n" // No return - Handler mode will be exited. Stack will be popped and execution will continue in reset handler initializing other application. + ".align\t\n" + :: "r" (start_addr) // Argument list for the gcc assembly. start_addr is %0. + : "r0", "r4", "r5", "r6", "r7" // List of register maintained manually. + ); +} +#elif defined ( __ICCARM__ ) +static inline void bootloader_util_reset(uint32_t start_addr) +{ + asm("ldr r5, [%0]\n" // Get App initial MSP for bootloader. + "msr msp, r5\n" // Set the main stack pointer to the applications MSP. + "ldr r0, [%0, #0x04]\n" // Load Reset handler into R0. + + "movs r4, #0x00\n" // Load zero into R4. + "mvns r4, r4\n" // Invert R4 to ensure it contain ones. + + "mrs r5, IPSR\n" // Load IPSR to R5 to check for handler or thread mode + "cmp r5, #0x00\n" // Compare, if 0 then we are in thread mode and can continue to reset handler of bootloader. + "bne.n isr_abort\n" // If not zero we need to exit current ISR and jump to reset handler of bootloader. + + "mov lr, r4\n" // Clear the link register and set to ones to ensure no return. + "bx r0\n" // Branch to reset handler of bootloader. + + "isr_abort: \n" + // R4 contains ones from line above. We be popped as R12 when exiting ISR (Cleaning up the registers). + "mov r5, r4\n" // Fill with ones before jumping to reset handling. Will be popped as LR when exiting ISR. Ensures no return to application. + "mov r6, r0\n" // Move address of reset handler to R6. Will be popped as PC when exiting ISR. Ensures the reset handler will be executed when exist ISR. + "movs r7, #0x21\n" // Move MSB reset value of xPSR to R7. Will be popped as xPSR when exiting ISR. xPSR is 0x21000000 thus MSB is 0x21. + "rev r7, r7\n" // Reverse byte order to put 0x21 as MSB. + "push {r4-r7}\n" // Push everything to new stack to allow interrupt handler to fetch it on exiting the ISR. + + "movs r4, #0x00\n" // Fill with zeros before jumping to reset handling. We be popped as R0 when exiting ISR (Cleaning up of the registers). + "movs r5, #0x00\n" // Fill with zeros before jumping to reset handling. We be popped as R1 when exiting ISR (Cleaning up of the registers). + "movs r6, #0x00\n" // Fill with zeros before jumping to reset handling. We be popped as R2 when exiting ISR (Cleaning up of the registers). + "movs r7, #0x00\n" // Fill with zeros before jumping to reset handling. We be popped as R3 when exiting ISR (Cleaning up of the registers). + "push {r4-r7}\n" // Push zeros (R4-R7) to stack to prepare for exiting the interrupt routine. + + "movs r0, #0x06\n" // Load 0x06 into R6 to prepare for exec return command. + "mvns r0, r0\n" // Invert 0x06 to obtain EXEC_RETURN, 0xFFFFFFF9. + "bx r0\n" // No return - Handler mode will be exited. Stack will be popped and execution will continue in reset handler initializing other application. + :: "r" (start_addr) // Argument list for the IAR assembly. start_addr is %0. + : "r0", "r4", "r5", "r6", "r7"); // List of register maintained manually. +} +#else +#error Compiler not supported. +#endif + + +void bootloader_util_app_start(uint32_t start_addr) +{ + bootloader_util_reset(start_addr); +} diff --git a/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.h b/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.h new file mode 100644 index 0000000..1e09e2d --- /dev/null +++ b/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + + /**@file + * + * @defgroup nrf_bootloader_util Bootloader util API. + * @{ + * + * @brief Bootloader util module interface. + */ + +#ifndef BOOTLOADER_UTIL_H__ +#define BOOTLOADER_UTIL_H__ + +#include +#include "bootloader_types.h" + +/**@brief Function for starting the application (or bootloader) at the provided address. + * + * @param[in] start_addr Start address. + * + * @note This function will never retrun. Instead it will reset into the application of the + * provided address. + */ +void bootloader_util_app_start(uint32_t start_addr); + +#endif // BOOTLOADER_UTIL_H__ + +/**@} */ diff --git a/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.c b/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.c new file mode 100644 index 0000000..206cbca --- /dev/null +++ b/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.c @@ -0,0 +1,219 @@ +/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#include "dfu_app_handler.h" +#include +#include "bootloader_util.h" +#include "nrf.h" +#include "nrf_sdm.h" +#include "ble_gatt.h" +#include "ble_gatts.h" +#include "app_error.h" +#include "dfu_ble_svc.h" +#include "device_manager.h" +#include "nrf_delay.h" + +#define IRQ_ENABLED 0x01 /**< Field that identifies if an interrupt is enabled. */ +#define MAX_NUMBER_INTERRUPTS 38 /**< Maximum number of interrupts available. */ + +static void dfu_app_reset_prepare(void); /**< Forward declaration of default reset handler. */ +static dfu_app_reset_prepare_t m_reset_prepare = dfu_app_reset_prepare; /**< Callback function to application to prepare for system reset. Allows application to clean up service and memory before reset. */ +static dfu_ble_peer_data_t m_peer_data; /**< Peer data to be used for data exchange when resetting into DFU mode. */ +static dm_handle_t m_dm_handle; /**< Device Manager handle with instance IDs of current BLE connection. */ + + +/**@brief Function for reset_prepare handler if the application has not registered a handler. + */ +static void dfu_app_reset_prepare(void) +{ + // Reset prepare should be handled by application. + // This function can be extended to include default handling if application does not implement + // own handler. +} + + +/**@brief Function for disabling all interrupts before jumping from bootloader to application. + */ +static void interrupts_disable(void) +{ + uint32_t interrupt_setting_mask; + uint32_t irq; + + // Disable interrupts as suggested here: + // https://devzone.nordicsemi.com/question/68419/interrupts_disable-implementation-question/ + NVIC->ICER[0] = 0xffffffff; +#ifdef NRF52 + NVIC->ICER[1] = 0xffffffff; +#endif + + // Disable SysTick interrupt to avoid hard fault inside bootloader + SysTick->CTRL&=0xFC; + SCB->ICSR|=0x2000000; + + // Fetch the current interrupt settings. + interrupt_setting_mask = NVIC->ISER[0]; + + // Loop from interrupt 0 for disabling of all interrupts. + for (irq = 0; irq < MAX_NUMBER_INTERRUPTS; irq++) + { + if (interrupt_setting_mask & (IRQ_ENABLED << irq)) + { + // The interrupt was enabled, hence disable it. + NVIC_DisableIRQ((IRQn_Type)irq); + } + } +} + + +/**@brief Function for providing peer information to DFU for re-establishing a bonded connection in + * DFU mode. + * + * @param[in] conn_handle Connection handle for the connection requesting DFU mode. + */ +static void dfu_app_peer_data_set(uint16_t conn_handle) +{ + uint32_t err_code; + dm_sec_keyset_t key_set; + uint32_t app_context_data = 0; + dm_application_context_t app_context; +// NRF_GPIO->DIRSET = (1UL << 25); + // NRF_GPIO->OUTSET = (1UL << 25); + + +/** [DFU bond sharing] */ + err_code = dm_handle_get(conn_handle, &m_dm_handle); + if (err_code == NRF_SUCCESS) + { + err_code = dm_distributed_keys_get(&m_dm_handle, &key_set); + if (err_code == NRF_SUCCESS) + { + APP_ERROR_CHECK(err_code); + m_peer_data.addr = key_set.keys_central.p_id_key->id_addr_info; + m_peer_data.irk = key_set.keys_central.p_id_key->id_info; + m_peer_data.enc_key.enc_info = key_set.keys_periph.enc_key.p_enc_key->enc_info; + m_peer_data.enc_key.master_id = key_set.keys_periph.enc_key.p_enc_key->master_id; + + err_code = dfu_ble_svc_peer_data_set(&m_peer_data); + APP_ERROR_CHECK(err_code); + + app_context_data = (DFU_APP_ATT_TABLE_CHANGED << DFU_APP_ATT_TABLE_POS); + app_context.len = sizeof(app_context_data); + app_context.p_data = (uint8_t *)&app_context_data; + app_context.flags = 0; + + err_code = dm_application_context_set(&m_dm_handle, &app_context); + if(err_code != NRF_SUCCESS){ + // NRF_GPIO->DIRSET = (1UL << 25); + // NRF_GPIO->OUTSET = (1UL << 25); + } + else{ + // NRF_GPIO->DIRSET = (1UL << 25); + // NRF_GPIO->OUTSET = (1UL << 25); + } + + APP_ERROR_CHECK(err_code); + } + else + { + // NRF_GPIO->DIRSET = (1UL << 25); + // NRF_GPIO->OUTSET = (1UL << 25); + + // Keys were not available, thus we have a non-encrypted connection. + err_code = dm_peer_addr_get(&m_dm_handle, &m_peer_data.addr); + APP_ERROR_CHECK(err_code); + + err_code = dfu_ble_svc_peer_data_set(&m_peer_data); + APP_ERROR_CHECK(err_code); + + } + } +/** [DFU bond sharing] */ +} + + +/**@brief Function for preparing the reset, disabling SoftDevice, and jumping to the bootloader. + * + * @param[in] conn_handle Connection handle for peer requesting to enter DFU mode. + */ +static void bootloader_start(uint16_t conn_handle) +{ + uint32_t err_code; + uint16_t sys_serv_attr_len = sizeof(m_peer_data.sys_serv_attr); + + err_code = sd_ble_gatts_sys_attr_get(conn_handle, + m_peer_data.sys_serv_attr, + &sys_serv_attr_len, + BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS); + if (err_code != NRF_SUCCESS) + { + // Any error at this stage means the system service attributes could not be fetched. + // This means the service changed indication cannot be sent in DFU mode, but connection + // is still possible to establish. + } + + m_reset_prepare(); + + err_code = sd_power_gpregret_set(BOOTLOADER_DFU_START); + + APP_ERROR_CHECK(err_code); + + err_code = sd_softdevice_disable(); + APP_ERROR_CHECK(err_code); + + err_code = sd_softdevice_vector_table_base_set(NRF_UICR->NRFFW[0]); + APP_ERROR_CHECK(err_code); + + dfu_app_peer_data_set(conn_handle); + + NVIC_ClearPendingIRQ(SWI2_IRQn); + interrupts_disable(); + + bootloader_util_app_start(NRF_UICR->NRFFW[0]); +} + + +void dfu_app_on_dfu_evt(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt) +{ + switch (p_evt->ble_dfu_evt_type) + { + case BLE_DFU_START: + // Starting the bootloader - will cause reset. + bootloader_start(p_dfu->conn_handle); + break; + + default: + { + // Unsupported event received from DFU Service. + // Send back BLE_DFU_RESP_VAL_NOT_SUPPORTED message to peer. + uint32_t err_code = ble_dfu_response_send(p_dfu, + BLE_DFU_START_PROCEDURE, + BLE_DFU_RESP_VAL_NOT_SUPPORTED); + APP_ERROR_CHECK(err_code); + } + break; + } +} + + +void dfu_app_reset_prepare_set(dfu_app_reset_prepare_t reset_prepare_func) +{ + m_reset_prepare = reset_prepare_func; +} + + +void dfu_app_dm_appl_instance_set(dm_application_instance_t app_instance) +{ + uint32_t err_code; + + err_code = dm_application_instance_set(&app_instance, &m_dm_handle); + APP_ERROR_CHECK(err_code); +} diff --git a/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.h b/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.h new file mode 100644 index 0000000..e923186 --- /dev/null +++ b/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +/** @file + * + * @defgroup nrf_dfu_app_handler DFU BLE packet handling in application + * @{ + * + * @brief Handling of DFU BLE packets in the application. + * + * @details This module implements the handling of DFU packets for switching + * from an application to the bootloader and start DFU mode. The DFU + * packets are transmitted over BLE. + * This module handles only the StartDFU packet, which allows a BLE + * application to expose support for the DFU Service. + * The actual DFU Service runs in a dedicated environment after a BLE + * disconnect and reset of the \nRFXX device. + * The host must reconnect and continue the update procedure with + * access to the full DFU Service. + * + * @note The application must propagate DFU events to this module by calling + * @ref dfu_app_on_dfu_evt from the @ref ble_dfu_evt_handler_t callback. + */ + +#ifndef DFU_APP_HANDLER_H__ +#define DFU_APP_HANDLER_H__ + +#include "ble_dfu.h" +#include "nrf_svc.h" +#include "bootloader_types.h" +#include "device_manager.h" + +#define DFU_APP_ATT_TABLE_POS 0 /**< Position for the ATT table changed setting. */ +#define DFU_APP_ATT_TABLE_CHANGED 1 /**< Value indicating that the ATT table might have changed. This value will be set in the application-specific context in Device Manager when entering DFU mode. */ + +/**@brief DFU application reset_prepare function. This function is a callback that allows the + * application to prepare for an upcoming application reset. + */ +typedef void (*dfu_app_reset_prepare_t)(void); + +/**@brief Function for handling events from the DFU Service. + * + * @details The application must inject this function into the DFU Service or propagate DFU events + * to the dfu_app_handler module by calling this function in the application-specific DFU event + * handler. + * + * @param[in] p_dfu Pointer to the DFU Service structure to which the include event relates. + * @param[in] p_evt Pointer to the DFU event. + */ +void dfu_app_on_dfu_evt(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt); + +/**@brief Function for registering a function to prepare a reset. + * + * @details The provided function is executed before resetting the system into bootloader/DFU + * mode. By registering this function, the caller is notified before the reset and can + * thus prepare the application for reset. For example, the application can gracefully + * disconnect any peers on BLE, turn of LEDS, ensure that all pending flash operations + * have completed, and so on. + * + * @param[in] reset_prepare_func Function to be executed before a reset. + */ +void dfu_app_reset_prepare_set(dfu_app_reset_prepare_t reset_prepare_func); + +/**@brief Function for setting the Device Manager application instance. + * + * @details This function allows to set the @ref dm_application_instance_t value that is returned by the + * Device Manager when the application registers using @ref dm_register. + * If this function is not called, it is not be possible to share bonding information + * from the application to the bootloader/DFU when entering DFU mode. + * + * @param[in] app_instance Value for the application instance in use. + */ +void dfu_app_dm_appl_instance_set(dm_application_instance_t app_instance); + +#endif // DFU_APP_HANDLER_H__ + +/** @} */ diff --git a/cores/arduino/components/libraries/bootloader_dfu/dfu_ble_svc.h b/cores/arduino/components/libraries/bootloader_dfu/dfu_ble_svc.h new file mode 100644 index 0000000..d1cc440 --- /dev/null +++ b/cores/arduino/components/libraries/bootloader_dfu/dfu_ble_svc.h @@ -0,0 +1,80 @@ +/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +/** @file + * + * @defgroup nrf_dfu_ble_svc DFU BLE SVC + * @{ + * + * @brief DFU BLE SVC in bootloader. The DFU BLE SuperVisor Calls allow an application to execute + * functions in the installed bootloader. + * + * @details This module implements handling of SuperVisor Calls in the bootloader. + * SuperVisor Calls allow for an application to execute calls into the bootloader. + * Currently, it is possible to exchange bonding information (like keys) from the + * application to a bootloader supporting DFU OTA using BLE, so the update process can be + * done through an already existing bond. + * + * @note The application must make sure that all SuperVisor Calls (SVC) are forwarded to the + * bootloader to ensure correct behavior. Forwarding of SVCs to the bootloader is + * done using the SoftDevice SVC @ref sd_softdevice_vector_table_base_set with the value + * present in @c NRF_UICR->NRFFW[0]. + */ + +#ifndef DFU_BLE_SVC_H__ +#define DFU_BLE_SVC_H__ + +#include "nrf_svc.h" +#include +#include "ble_gap.h" +#include "nrf.h" +#include "nrf_soc.h" +#include "nrf_error_sdm.h" + +#define BOOTLOADER_SVC_BASE 0x0 /**< The number of the lowest SVC number reserved for the bootloader. */ +#define SYSTEM_SERVICE_ATT_SIZE 8 /**< Size of the system service attribute length including CRC-16 at the end. */ + +/**@brief The SVC numbers used by the SVC functions in the SoC library. */ +enum BOOTLOADER_SVCS +{ + DFU_BLE_SVC_PEER_DATA_SET = BOOTLOADER_SVC_BASE, /**< SVC number for the setting of peer data call. */ + BOOTLOADER_SVC_LAST +}; + +/**@brief DFU Peer data structure. + * + * @details This structure contains peer data needed for connection to a bonded device during DFU. + * The peer data must be provided by the application to the bootloader during buttonless + * update. See @ref dfu_ble_svc_peer_data_set. It contains bond information about the + * desired DFU peer. + */ +typedef struct +{ + ble_gap_addr_t addr; /**< BLE GAP address of the device that initiated the DFU process. */ + ble_gap_irk_t irk; /**< IRK of the device that initiated the DFU process if this device uses Private Resolvable Addresses. */ + ble_gap_enc_key_t enc_key; /**< Encryption key structure containing encrypted diversifier and LTK for re-establishing the bond. */ + uint8_t sys_serv_attr[SYSTEM_SERVICE_ATT_SIZE]; /**< System service attributes for restoring of Service Changed Indication setting in DFU mode. */ +} dfu_ble_peer_data_t; + +/**@brief SVC Function for setting peer data containing address, IRK, and LTK to establish bonded + * connection in DFU mode. + * + * @param[in] p_peer_data Pointer to the peer data containing keys for the connection. + * + * @retval NRF_ERROR_NULL If a NULL pointer was provided as argument. + * @retval NRF_SUCCESS If the function completed successfully. + */ +SVCALL(DFU_BLE_SVC_PEER_DATA_SET, uint32_t, dfu_ble_svc_peer_data_set(dfu_ble_peer_data_t * p_peer_data)); + +#endif // DFU_BLE_SVC_H__ + +/** @} */ diff --git a/cores/arduino/components/libraries/experimental_section_vars/section_vars.h b/cores/arduino/components/libraries/experimental_section_vars/section_vars.h new file mode 100644 index 0000000..f971681 --- /dev/null +++ b/cores/arduino/components/libraries/experimental_section_vars/section_vars.h @@ -0,0 +1,263 @@ +/* Copyright (c) 2016 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#ifndef SECTION_VARS_H__ +#define SECTION_VARS_H__ + + +/** + * @defgroup section_vars Section variables + * @ingroup app_common + * @{ + * + * @brief Section variables. + */ + +#if defined(__ICCARM__) +// Enable IAR language extensions +#pragma language=extended +#endif + + +// Macro to delay macro expansion. +#define NRF_PRAGMA(x) _Pragma(#x) + + +/**@brief Macro to register a named section. + * + * @param[in] section_name Name of the section to register. + */ +#if defined(__CC_ARM) + +// Not required by this compiler. +#define NRF_SECTION_VARS_REGISTER_SECTION(section_name) + +#elif defined(__GNUC__) + +// Not required by this compiler. +#define NRF_SECTION_VARS_REGISTER_SECTION(section_name) + +#elif defined(__ICCARM__) + +#define NRF_SECTION_VARS_REGISTER_SECTION(section_name) NRF_PRAGMA(section = #section_name) + +#endif + + +/*lint -save -e27 */ + +/**@brief Macro to obtain the linker symbol for the beginning of a given section. + * + * @details The symbol that this macro resolves to is used to obtain a section start address. + * + * @param[in] section_name Name of the section. + */ +#if defined(__CC_ARM) + +#define NRF_SECTION_VARS_START_SYMBOL(section_name) section_name ## $$Base + +#elif defined(__GNUC__) + +#define NRF_SECTION_VARS_START_SYMBOL(section_name) __start_ ## section_name + +#elif defined(__ICCARM__) + +#define NRF_SECTION_VARS_START_SYMBOL(section_name) __section_begin(#section_name) + +#endif + + +/**@brief Macro to obtain the linker symbol for the end of a given section. + * + * @details The symbol that this macro resolves to is used to obtain a section end address. + * + * @param[in] section_name Name of the section. + */ +#if defined(__CC_ARM) + +#define NRF_SECTION_VARS_END_SYMBOL(section_name) section_name ## $$Limit + +#elif defined(__GNUC__) + +#define NRF_SECTION_VARS_END_SYMBOL(section_name) __stop_ ## section_name + +#elif defined(__ICCARM__) + +#define NRF_SECTION_VARS_END_SYMBOL(section_name) __section_end(#section_name) + +#endif + +/*lint -restore */ + + +/**@brief Macro for retrieving the length of a given section, in bytes. + * + * @param[in] section_name Name of the section. + */ +#if defined(__CC_ARM) + +#define NRF_SECTION_VARS_LENGTH(section_name) \ + ((uint32_t)&NRF_SECTION_VARS_END_SYMBOL(section_name) - (uint32_t)&NRF_SECTION_VARS_START_SYMBOL(section_name)) + +#elif defined(__GNUC__) + + #define NRF_SECTION_VARS_LENGTH(section_name) \ + ((uint32_t)&NRF_SECTION_VARS_END_SYMBOL(section_name) - (uint32_t)&NRF_SECTION_VARS_START_SYMBOL(section_name)) + +#elif defined(__ICCARM__) + + #define NRF_SECTION_VARS_LENGTH(section_name) \ + ((uint32_t)NRF_SECTION_VARS_END_SYMBOL(section_name) - (uint32_t)NRF_SECTION_VARS_START_SYMBOL(section_name)) + +#endif + + +/**@brief Macro to obtain the start address of a named section. + * + * param[in] section_name Name of the section. + */ +#if defined(__CC_ARM) + +#define NRF_SECTION_VARS_START_ADDR(section_name) (uint32_t)&NRF_SECTION_VARS_START_SYMBOL(section_name) + +#elif defined(__GNUC__) + +#define NRF_SECTION_VARS_START_ADDR(section_name) (uint32_t)&NRF_SECTION_VARS_START_SYMBOL(section_name) + +#elif defined(__ICCARM__) + +#define NRF_SECTION_VARS_START_ADDR(section_name) (uint32_t)iar_ ## section_name ## _start + +#endif + + +/*@brief Macro to obtain the end address of a named section. + * + * @param[in] section_name Name of the section. + */ +#if defined(__CC_ARM) + +#define NRF_SECTION_VARS_END_ADDR(section_name) (uint32_t)&NRF_SECTION_VARS_END_SYMBOL(section_name) + +#elif defined(__GNUC__) + +#define NRF_SECTION_VARS_END_ADDR(section_name) (uint32_t)&NRF_SECTION_VARS_END_SYMBOL(section_name) + +#elif defined(__ICCARM__) + +#define NRF_SECTION_VARS_END_ADDR(section_name) (uint32_t)iar_ ## section_name ## _end + +#endif + + +/**@brief Macro to extern a named section start and stop symbols. + * + * @note These declarations are required for GCC and Keil linkers (but not for IAR's). + * + * @param[in] type_name Name of the type stored in the section. + * @param[in] section_name Name of the section. + */ +#if defined(__CC_ARM) + +#define NRF_SECTION_VARS_REGISTER_SYMBOLS(type_name, section_name) \ + extern type_name * NRF_SECTION_VARS_START_SYMBOL(section_name); \ + extern void * NRF_SECTION_VARS_END_SYMBOL(section_name) + +#elif defined(__GNUC__) + +#define NRF_SECTION_VARS_REGISTER_SYMBOLS(type_name, section_name) \ + extern type_name * NRF_SECTION_VARS_START_SYMBOL(section_name); \ + extern void * NRF_SECTION_VARS_END_SYMBOL(section_name) + +#elif defined(__ICCARM__) + +// No symbol registration required for IAR. +#define NRF_SECTION_VARS_REGISTER_SYMBOLS(type_name, section_name) \ + extern void * iar_ ## section_name ## _start = __section_begin(#section_name); \ + extern void * iar_ ## section_name ## _end = __section_end(#section_name) + +#endif + + +/**@brief Macro to declare a variable to be placed in a named section. + * + * @details Declares a variable to be placed in a named section. This macro ensures that its symbol + * is not stripped by the linker because of optimizations. + * + * @warning The order with which variables are placed in a section is implementation dependant. + * Generally, variables are placed in a section depending on the order with which they + * are found by the linker. + * + * @warning The symbols added in the named section must be word aligned to prevent padding. + * + * @param[in] section_name Name of the section. + * @param[in] type_def Datatype of the variable to place in the given section. + */ +#if defined(__CC_ARM) + +#define NRF_SECTION_VARS_ADD(section_name, type_def) \ + static type_def __attribute__ ((section(#section_name))) __attribute__((used)) + +#elif defined(__GNUC__) + +#define NRF_SECTION_VARS_ADD(section_name, type_def) \ + static type_def __attribute__ ((section("."#section_name))) __attribute__((used)) + +#elif defined(__ICCARM__) + +#define NRF_SECTION_VARS_ADD(section_name, type_def) \ + __root type_def @ #section_name + +#endif + + +/**@brief Macro to get symbol from named section. + * + * @warning The stored symbol can only be resolved using this macro if the + * type of the data is word aligned. The operation of acquiring + * the stored symbol relies on sizeof of the stored type, no + * padding can exist in the named section in between individual + * stored items or this macro will fail. + * + * @param[in] i Index of item in section. + * @param[in] type_name Type name of item in section. + * @param[in] section_name Name of the section. + */ +#if defined(__CC_ARM) + +#define NRF_SECTION_VARS_GET(i, type_name, section_name) \ + (type_name*)(NRF_SECTION_VARS_START_ADDR(section_name) + i * sizeof(type_name)) + +#elif defined(__GNUC__) + +#define NRF_SECTION_VARS_GET(i, type_name, section_name) \ + (type_name*)(NRF_SECTION_VARS_START_ADDR(section_name) + i * sizeof(type_name)) + +#elif defined(__ICCARM__) + +#define NRF_SECTION_VARS_GET(i, type_name, section_name) \ + (type_name*)(NRF_SECTION_VARS_START_ADDR(section_name) + i * sizeof(type_name)) + +#endif + + +/**@brief Macro to get number of items in named section. + * + * @param[in] type_name Type name of item in section. + * @param[in] section_name Name of the section. + */ +#define NRF_SECTION_VARS_COUNT(type_name, section_name) \ + NRF_SECTION_VARS_LENGTH(section_name) / sizeof(type_name) + +/** @} */ + +#endif // SECTION_VARS_H__ diff --git a/cores/arduino/components/libraries/fstorage/config/fstorage_config.h b/cores/arduino/components/libraries/fstorage/config/fstorage_config.h new file mode 100644 index 0000000..71ffcad --- /dev/null +++ b/cores/arduino/components/libraries/fstorage/config/fstorage_config.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#ifndef FS_CONFIG_H__ +#define FS_CONFIG_H__ + +/** + * @defgroup fstorage_config fstorage configuration + * @ingroup fstorage + * @{ + * + * @brief fstorage configuration options. + */ + + +/**@brief Configures the size of fstorage internal queue. + * @details Increase this if there are many users, or if it is likely that many operation will be + * queued at once without waiting for the previous operations to complete. In general, + * increase the queue size if you frequently receive @ref FS_ERR_QUEUE_FULL errors when + * calling @ref fs_store or @ref fs_erase. + */ +#define FS_QUEUE_SIZE (4) + +/**@brief Configures how many times should fstorage attempt to execute an operation if + * the SoftDevice fails to schedule flash access due to high BLE activity. + * @details Increase this value if fstorage events return the @ref FS_ERR_OPERATION_TIMEOUT error + * often. + */ +#define FS_OP_MAX_RETRIES (3) + + +/**@brief Configures the maximum number of words to be written to flash in a single operation. + * If data length exceeds this value, the data will be written down in several chunks, + * as necessary. + * + * @details Tweaking this value can increase the chances of the SoftDevice being able to fit + * flash operations in between radio activity. This value is bound by the maximum number + * of words which the SoftDevice can write to flash in a single call to + * @ref sd_flash_write, which is 256 words for nRF51 ICs and 1024 words for nRF52 ICs. + */ +#if defined (NRF51) + #define FS_MAX_WRITE_SIZE_WORDS (256) +#elif defined (NRF52) + #define FS_MAX_WRITE_SIZE_WORDS (1024) +#endif + +/** @} */ + +#endif // FS_CONFIG_H__ + diff --git a/cores/arduino/components/libraries/fstorage/fstorage.c b/cores/arduino/components/libraries/fstorage/fstorage.c new file mode 100644 index 0000000..071cba5 --- /dev/null +++ b/cores/arduino/components/libraries/fstorage/fstorage.c @@ -0,0 +1,494 @@ +/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#include "fstorage.h" +#include "fstorage_config.h" +#include "fstorage_internal_defs.h" + +#include +#include +#include +#include "nrf_error.h" +#include "nrf_soc.h" + + +static uint8_t m_flags; // fstorage status flags. +static fs_op_queue_t m_queue; // Queue of requested operations. +static uint8_t m_retry_count; // Number of times the last flash operation was retried. + + +// Sends events to the application. +static void send_event(fs_op_t const * const p_op, fs_ret_t result) +{ + fs_evt_t evt; + memset(&evt, 0x00, sizeof(fs_evt_t)); + + switch (p_op->op_code) + { + case FS_OP_STORE: + evt.id = FS_EVT_STORE; + evt.store.p_data = p_op->store.p_dest; + evt.store.length_words = p_op->store.length_words; + break; + + case FS_OP_ERASE: + evt.id = FS_EVT_ERASE; + evt.erase.first_page = p_op->erase.page - p_op->erase.pages_erased; + evt.erase.last_page = p_op->erase.page; + break; + + default: + // Should not happen. + break; + } + + p_op->p_config->callback(&evt, result); +} + + +// Checks that a configuration is non-NULL and within section variable bounds. +static bool check_config(fs_config_t const * const config) +{ + if ((config != NULL) && + (FS_SECTION_VARS_START_ADDR <= (uint32_t)config) && + (FS_SECTION_VARS_END_ADDR > (uint32_t)config)) + { + return true; + } + + return false; +} + + +// Executes a store operation. +static uint32_t store_execute(fs_op_t const * const p_op) +{ + uint16_t chunk_len; + + if ((p_op->store.length_words - p_op->store.offset) < FS_MAX_WRITE_SIZE_WORDS) + { + chunk_len = p_op->store.length_words - p_op->store.offset; + } + else + { + chunk_len = FS_MAX_WRITE_SIZE_WORDS; + } + + return sd_flash_write((uint32_t*)p_op->store.p_dest + p_op->store.offset, + (uint32_t*)p_op->store.p_src + p_op->store.offset, + chunk_len); +} + + +// Executes an erase operation. +static uint32_t erase_execute(fs_op_t const * const p_op) +{ + return sd_flash_page_erase(p_op->erase.page); +} + + +// Advances the queue, wrapping around if necessary. +// If no elements are left in the queue, clears the FS_FLAG_PROCESSING flag. +static void queue_advance(void) +{ + if (--m_queue.count == 0) + { + m_flags &= ~FS_FLAG_PROCESSING; + } + + if (++m_queue.rp == FS_QUEUE_SIZE) + { + m_queue.rp = 0; + } +} + + +// Processes the current element in the queue. If the queue is empty, does nothing. +static void queue_process(void) +{ + uint32_t ret; + fs_op_t * const p_op = &m_queue.op[m_queue.rp]; + + if (m_queue.count > 0) + { + switch (p_op->op_code) + { + case FS_OP_STORE: + ret = store_execute(p_op); + break; + + case FS_OP_ERASE: + ret = erase_execute(p_op); + break; + + default: + ret = FS_ERR_INTERNAL; + break; + } + + // There is a pending flash operation which was not initiated by this module. + // Stop processing the queue and wait for a system event. + if (ret == NRF_ERROR_BUSY) + { + m_flags &= ~FS_FLAG_PROCESSING; + m_flags |= FS_FLAG_FLASH_REQ_PENDING; + } + else if (ret != NRF_SUCCESS) + { + // An error has occurred. + send_event(p_op, FS_ERR_INTERNAL); + } + else + { + // Operation is executing. + } + } +} + + +// Starts processing the queue if there are no pending flash operations, both inside and +// outside this module. Returns immediately otherwise. +static void queue_start(void) +{ + if (!(m_flags & FS_FLAG_PROCESSING) && + !(m_flags & FS_FLAG_FLASH_REQ_PENDING)) + { + m_flags |= FS_FLAG_PROCESSING; + queue_process(); + } +} + + +// Flash operation success callback handler. Keeps track of the progress of an operation. +// If it has finished, advances the queue and notifies the application. +static void on_operation_success(fs_op_t * const p_op) +{ + m_retry_count = 0; + + switch (p_op->op_code) + { + case FS_OP_STORE: + { + uint16_t chunk_len; + + if ((p_op->store.length_words - p_op->store.offset) < FS_MAX_WRITE_SIZE_WORDS) + { + chunk_len = p_op->store.length_words - p_op->store.offset; + } + else + { + chunk_len = FS_MAX_WRITE_SIZE_WORDS; + } + + p_op->store.offset += chunk_len; + + if (p_op->store.offset == p_op->store.length_words) + { + // The operation has finished. + send_event(p_op, FS_SUCCESS); + queue_advance(); + } + } + break; + + case FS_OP_ERASE: + { + p_op->erase.page++; + p_op->erase.pages_erased++; + + if (p_op->erase.pages_erased == p_op->erase.pages_to_erase) + { + send_event(p_op, FS_SUCCESS); + queue_advance(); + } + } + break; + + default: + // Should not happen. + break; + } +} + + +// Flash operation failure callback handler. If the maximum number of retries has +// been reached, notifies the application and advances the queue. +static void on_operation_failure(fs_op_t const * const p_op) +{ + if (++m_retry_count > FS_OP_MAX_RETRIES) + { + m_retry_count = 0; + + send_event(p_op, FS_ERR_OPERATION_TIMEOUT); + queue_advance(); + } +} + + +// Retrieves a pointer to the next free element in the queue. +// Additionally, increases the number of elements stored in the queue. +static bool queue_get_next_free(fs_op_t ** p_op) +{ + uint32_t idx; + + if (m_queue.count == FS_QUEUE_SIZE) + { + return false; + } + + idx = ((m_queue.rp + m_queue.count) < FS_QUEUE_SIZE) ? + (m_queue.rp + m_queue.count) : 0; + + m_queue.count++; + + // Zero the element so that unassigned fields will be zero. + memset(&m_queue.op[idx], 0x00, sizeof(fs_op_t)); + + *p_op = &m_queue.op[idx]; + + return true; +} + + +fs_ret_t fs_init(void) +{ + uint32_t const users = FS_SECTION_VARS_COUNT; + uint32_t const * p_current_end = FS_PAGE_END_ADDR; + uint32_t index_max = 0x00; + uint32_t index_last = 0xFFFFFFFF; + + if (m_flags & FS_FLAG_INITIALIZED) + { + return FS_SUCCESS; + } + + #if 0 + // Check for configurations with duplicate priority. + for (uint32_t i = 0; i < users; i++) + { + for (uint32_t j = i + 1; j < users; j++) + { + fs_config_t const * const p_config_i = FS_SECTION_VARS_GET(i); + fs_config_t const * const p_config_j = FS_SECTION_VARS_GET(j); + + if (p_config_i->page_order == p_config_j->page_order) + { + // Error. + return FS_ERR_INVALID_CFG; + } + } + } + #endif + + // Assign pages to registered users, beginning with the ones with the highest + // priority, which will be assigned pages with the highest memory address. + + for (uint32_t i = 0; i < users; i++) + { + uint8_t max_priority = 0x00; + + for (uint32_t j = 0; j < users; j++) + { + fs_config_t * const p_config = FS_SECTION_VARS_GET(j); + + // Skip the one assigned during last iteration. + if (j == index_last) + { + continue; + } + + if (p_config->priority >= max_priority) + { + max_priority = p_config->priority; + index_max = j; + } + } + + fs_config_t * const p_config = FS_SECTION_VARS_GET(index_max); + + p_config->p_end_addr = p_current_end; + p_config->p_start_addr = p_current_end - (p_config->num_pages * FS_PAGE_SIZE_WORDS); + + p_current_end = p_config->p_start_addr; + + index_last = index_max; + } + + m_flags |= FS_FLAG_INITIALIZED; + + return FS_SUCCESS; +} + + +fs_ret_t fs_store(fs_config_t const * const p_config, + uint32_t const * const p_dest, + uint32_t const * const p_src, + uint16_t const length_words) +{ + fs_op_t * p_op; + + if (!(m_flags & FS_FLAG_INITIALIZED)) + { + return FS_ERR_NOT_INITIALIZED; + } + + if (!check_config(p_config)) + { + return FS_ERR_INVALID_CFG; + } + + if ((p_src == NULL) || (p_dest == NULL)) + { + return FS_ERR_NULL_ARG; + } + + // Check that both pointers are word aligned. + if (((uint32_t)p_src & 0x03) || + ((uint32_t)p_dest & 0x03)) + { + return FS_ERR_UNALIGNED_ADDR; + } + + // Check that the operation doesn't go outside the client's memory boundaries. + if ((p_config->p_start_addr > p_dest) || + (p_config->p_end_addr < (p_dest + length_words))) + { + return FS_ERR_INVALID_ADDR; + } + + if (length_words == 0) + { + return FS_ERR_INVALID_ARG; + } + + if (!queue_get_next_free(&p_op)) + { + return FS_ERR_QUEUE_FULL; + } + + // Initialize the operation. + p_op->p_config = p_config; + p_op->op_code = FS_OP_STORE; + p_op->store.p_src = p_src; + p_op->store.p_dest = p_dest; + p_op->store.length_words = length_words; + + queue_start(); + + return FS_SUCCESS; +} + + +fs_ret_t fs_erase(fs_config_t const * const p_config, + uint32_t const * const p_page_addr, + uint16_t const num_pages) +{ + fs_op_t * p_op; + + if (!(m_flags & FS_FLAG_INITIALIZED)) + { + return FS_ERR_NOT_INITIALIZED; + } + + if (!check_config(p_config)) + { + return FS_ERR_INVALID_CFG; + } + + if (p_page_addr == NULL) + { + return FS_ERR_NULL_ARG; + } + + // Check that the page is aligned to a page boundary. + if (((uint32_t)p_page_addr % FS_PAGE_SIZE) != 0) + { + return FS_ERR_UNALIGNED_ADDR; + } + + // Check that the operation doesn't go outside the client's memory boundaries. + if ((p_page_addr < p_config->p_start_addr) || + (p_page_addr + (FS_PAGE_SIZE_WORDS * num_pages) > p_config->p_end_addr)) + { + return FS_ERR_INVALID_ADDR; + } + + if (num_pages == 0) + { + return FS_ERR_INVALID_ARG; + } + + if (!queue_get_next_free(&p_op)) + { + return FS_ERR_QUEUE_FULL; + } + + // Initialize the operation. + p_op->p_config = p_config; + p_op->op_code = FS_OP_ERASE; + p_op->erase.page = ((uint32_t)p_page_addr / FS_PAGE_SIZE); + p_op->erase.pages_to_erase = num_pages; + + queue_start(); + + return FS_SUCCESS; +} + + +fs_ret_t fs_queued_op_count_get(uint32_t * const p_op_count) +{ + if (p_op_count == NULL) + { + return FS_ERR_NULL_ARG; + } + + *p_op_count = m_queue.count; + + return FS_SUCCESS; +} + + +void fs_sys_event_handler(uint32_t sys_evt) +{ + fs_op_t * const p_op = &m_queue.op[m_queue.rp]; + + if (m_flags & FS_FLAG_PROCESSING) + { + // A flash operation was initiated by this module. Handle the result. + switch (sys_evt) + { + case NRF_EVT_FLASH_OPERATION_SUCCESS: + on_operation_success(p_op); + break; + + case NRF_EVT_FLASH_OPERATION_ERROR: + on_operation_failure(p_op); + break; + } + } + else if ((m_flags & FS_FLAG_FLASH_REQ_PENDING)) + { + // A flash operation was initiated outside this module. + // A callback which indicates that it has finished was received. + m_flags &= ~FS_FLAG_FLASH_REQ_PENDING; + + // If there are any elements left in the queue, set FS_FLAG_PROCESSING. + if (m_queue.count > 0) + { + m_flags |= FS_FLAG_PROCESSING; + } + } + + // Resume processing the queue, if necessary. + queue_process(); +} + diff --git a/cores/arduino/components/libraries/fstorage/fstorage.h b/cores/arduino/components/libraries/fstorage/fstorage.h new file mode 100644 index 0000000..df18654 --- /dev/null +++ b/cores/arduino/components/libraries/fstorage/fstorage.h @@ -0,0 +1,235 @@ +/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#ifndef FSTORAGE_H__ +#define FSTORAGE_H__ + +/** + * @defgroup fstorage fstorage + * @ingroup app_common + * @{ + * + * @brief Module which provides functionality to store data to flash and erase flash pages. + */ + +#include +#include "section_vars.h" + + +/**@brief fstorage return values. */ +typedef enum +{ + FS_SUCCESS, + FS_ERR_NOT_INITIALIZED, //!< Error. The module is not initialized. + FS_ERR_INVALID_CFG, //!< Error. Invalid fstorage configuration. + FS_ERR_NULL_ARG, //!< Error. Argument is NULL. + FS_ERR_INVALID_ARG, //!< Error. Argument contains invalid data. + FS_ERR_INVALID_ADDR, //!< Error. Address out of bounds. + FS_ERR_UNALIGNED_ADDR, //!< Error. Unaligned address. + FS_ERR_QUEUE_FULL, //!< Error. Queue is full. + FS_ERR_OPERATION_TIMEOUT, //!< Error. The operation has timed out. + FS_ERR_INTERNAL, //!< Error. Internal error. +} fs_ret_t; + + +/**@brief fstorage event IDs. */ +typedef enum +{ + FS_EVT_STORE, //!< Event for @ref fs_store. + FS_EVT_ERASE //!< Event for @ref fs_erase. +} fs_evt_id_t; + + +#if defined(__CC_ARM) + #pragma push + #pragma anon_unions +#elif defined(__ICCARM__) + #pragma language=extended +#elif defined(__GNUC__) + /* anonymous unions are enabled by default */ +#endif + +/**@brief An fstorage event. */ +typedef struct +{ + fs_evt_id_t id; //!< The event ID. + union + { + struct + { + uint32_t const * p_data; //!< Pointer to the data stored in flash. + uint16_t length_words; //!< Length of the data, in 4-byte words. + } store; + struct + { + uint16_t first_page; //!< First page erased. + uint16_t last_page; //!< Last page erased. + } erase; + }; +} fs_evt_t; + +#if defined(__CC_ARM) + #pragma pop +#elif defined(__ICCARM__) + /* leave anonymous unions enabled */ +#elif defined(__GNUC__) + /* anonymous unions are enabled by default */ +#endif + + +/**@brief fstorage event handler function prototype. + * + * @param[in] evt The event. + * @param[in] result The result of the operation. + */ +typedef void (*fs_cb_t)(fs_evt_t const * const evt, fs_ret_t result); + + +/**@brief fstorage application-specific configuration. + * + * @details Specifies the callback to invoke when an operation completes, the number of flash pages + * requested by the application and the priority with which these are to be assigned, with + * respect to other applications. Additionally, the configuration specifies the boundaries + * of the flash space assigned to an application. The configuration must be provided as an + * argument when invoking @ref fs_store and @ref fs_erase. + * + * @note The fields @p p_start_addr and @p p_end_address are set by @ref fs_init, based on the + * value of the field @p priority. + */ +typedef struct +{ + /**@brief The beginning of the flash space assigned to the application which registered this + * configuration. This field is set by @ref fs_init. + */ + uint32_t const * p_start_addr; + + /**@brief The end of the flash space assigned to the application which registered this + * configuration. This field is set by @ref fs_init. + */ + uint32_t const * p_end_addr; + + fs_cb_t const callback; //!< Callback to run when a flash operation has completed. + uint8_t const num_pages; //!< The number of flash pages requested. + + /**@brief The priority with which fstorage should assign flash pages to this application, + * with respect to other applications. Applications with higher priority will be + * assigned flash pages with a higher memory address. The highest priority is + * reserved. Must be unique among configurations. + */ + uint8_t const priority; +} fs_config_t; + + +/**@brief Macro for registering with an fstorage configuration. + * Applications which use fstorage must register with the module using this macro. + * Registering involves defining a variable which holds the configuration of fstorage + * specific to the application which invokes the macro. + * + * @details This macro places the configuration variable in a section named "fs_data" that + * fstorage uses during initialization and regular operation. + * + * @param[in] cfg_var A @e definition of a @ref fs_config_t variable. + */ +#define FS_REGISTER_CFG(cfg_var) NRF_SECTION_VARS_ADD(fs_data, cfg_var) + + +/**@brief Function for initializing the module. + * + * @details This functions assigns pages in flash according to all registered configurations. + * + * @retval FS_SUCCESS If the module was successfully initialized. + */ +fs_ret_t fs_init(void); + + +/**@brief Function for storing data in flash. + * + * @details Copies @p length_words words from @p p_src to the location pointed by @p p_dest. + * If the length of the data exceeds @ref FS_MAX_WRITE_SIZE_WORDS, the data will be + * written down in several chunks, as necessary. Only one event will be sent to the + * application upon completion. Both the source and the destination of the data must be + * word aligned. This function is asynchronous, completion is reported via an event sent + * the the callback function specified in the supplied configuration. + * + * @warning The data to be written to flash has to be kept in memory until the operation has + * terminated, i.e., an event is received. + * + * @param[in] p_config fstorage configuration registered by the application. + * @param[in] p_dest The address in flash memory where to store the data. + * @param[in] p_src Pointer to the data to store in flash. + * @param[in] length_words Length of the data to store, in words. + * + * @retval FS_SUCCESS If the operation was queued successfully. + * @retval FS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FS_ERR_INVALID_CFG If @p p_config is NULL or contains invalid data. + * @retval FS_ERR_NULL_ARG If @p p_dest or @p p_src are NULL. + * @retval FS_ERR_INVALID_ARG If @p length_words is zero. + * @retval FS_ERR_INVALID_ADDR If @p p_dest or @p p_src are outside of the flash memory + * boundaries specified in @p p_config. + * @retval FS_ERR_UNALIGNED_ADDR If @p p_dest or @p p_src are not aligned to a word boundary. + * @retval FS_ERR_QUEUE_FULL If the internal operation queue is full. + */ +fs_ret_t fs_store(fs_config_t const * const p_config, + uint32_t const * const p_dest, + uint32_t const * const p_src, + uint16_t length_words); + + +/**@brief Function for erasing flash pages. + * + * @details Starting from the page at @p p_page_addr, erases @p num_pages flash pages. + * @p p_page_addr must be aligned to a page boundary. All pages to be erased must be + * within the bounds specified in the supplied fstorage configuration. + * This function is asynchronous. Completion is reported via an event. + * + * @param[in] p_config fstorage configuration registered by the application. + * @param[in] p_page_addr Address of the page to erase. Must be aligned to a page boundary. + * @param[in] num_pages Number of pages to erase. May not be zero. + * + * @retval FS_SUCCESS If the operation was queued successfully. + * @retval FS_ERR_NOT_INITIALIZED If the module is not initialized. + * @retval FS_ERR_INVALID_CFG If @p p_config is NULL or contains invalid data. + * @retval FS_ERR_NULL_ARG If @p p_page_addr is NULL. + * @retval FS_ERR_INVALID_ARG If @p num_pages is zero. + * @retval FS_ERR_INVALID_ADDR If the operation would go beyond the flash memory boundaries + * specified in @p p_config. + * @retval FS_ERR_UNALIGNED_ADDR If @p p_page_addr is not aligned to a page boundary. + * @retval FS_ERR_QUEUE_FULL If the internal operation queue is full. + */ +fs_ret_t fs_erase(fs_config_t const * const p_config, + uint32_t const * const p_page_addr, + uint16_t num_pages); + + +/**@brief Function for retrieving the number of queued flash operations. + * + * @param[out] p_op_count The number of queued flash operations. + * + * @retval FS_SUCCESS If the number of queued operations was retrieved successfully. + * @retval FS_ERR_NULL_ARG If @p p_op_count is NULL. + */ +fs_ret_t fs_queued_op_count_get(uint32_t * const p_op_count); + + +/**@brief Function for handling system events from the SoftDevice. + * + * @details If any of the modules used by the application rely on fstorage, the application should + * dispatch system events to fstorage using this function. + * + * @param[in] sys_evt System event from the SoftDevice. + */ +void fs_sys_event_handler(uint32_t sys_evt); + + +/** @} */ + +#endif // FSTORAGE_H__ diff --git a/cores/arduino/components/libraries/fstorage/fstorage_internal_defs.h b/cores/arduino/components/libraries/fstorage/fstorage_internal_defs.h new file mode 100644 index 0000000..fc29693 --- /dev/null +++ b/cores/arduino/components/libraries/fstorage/fstorage_internal_defs.h @@ -0,0 +1,135 @@ +/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#ifndef FSTORAGE_INTERNAL_DEFS_H__ +#define FSTORAGE_INTERNAL_DEFS_H__ + +#include "nrf.h" + + +#define FS_FLAG_INITIALIZED (1 << 0) // The module has been initialized. +#define FS_FLAG_PROCESSING (1 << 1) // The module is processing flash operations. +// The module is waiting for a flash operation initiated by another module to complete. +#define FS_FLAG_FLASH_REQ_PENDING (1 << 2) + +#define FS_ERASED_WORD (0xFFFFFFFF) + +// Helper macros for section variables. +#define FS_SECTION_VARS_GET(i) NRF_SECTION_VARS_GET((i), fs_config_t, fs_data) +#define FS_SECTION_VARS_COUNT NRF_SECTION_VARS_COUNT(fs_config_t, fs_data) +#define FS_SECTION_VARS_START_ADDR NRF_SECTION_VARS_START_ADDR(fs_data) +#define FS_SECTION_VARS_END_ADDR NRF_SECTION_VARS_END_ADDR(fs_data) + + +// Register the section 'fs_data'. +//lint -save -e19 +NRF_SECTION_VARS_REGISTER_SECTION(fs_data); +//lint -restore + +// Declare symbols into the 'fs_data' section. +NRF_SECTION_VARS_REGISTER_SYMBOLS(fs_config_t, fs_data); +//lint -esym(526,fs_dataBase) +//lint -esym(526,fs_dataLimit) + + +// fstorage op-codes. +typedef enum +{ + FS_OP_NONE, // No operation. + FS_OP_STORE, // Store data. + FS_OP_ERASE // Erase one or more flash pages. +} fs_op_code_t; + + +#if defined(__CC_ARM) + #pragma push + #pragma anon_unions +#elif defined(__ICCARM__) + #pragma language=extended +#elif defined(__GNUC__) + // anonymous unions are enabled by default. +#endif + +// fstorage operation. +// Encapsulates details of a flash operation to be executed by this module. +typedef struct +{ + fs_config_t const * p_config; // Application-specific fstorage configuration. + fs_op_code_t op_code; // ID of the operation. + union + { + struct + { + uint32_t const * p_src; // Pointer to the data to be written to flash. + uint32_t const * p_dest; // Destination of the data in flash. + uint16_t length_words; // Length of the data to be written, in words. + uint16_t offset; // Write offset. + } store; + struct + { + uint16_t page; + uint16_t pages_erased; + uint16_t pages_to_erase; + } erase; + }; +} fs_op_t; + +#if defined(__CC_ARM) + #pragma pop +#elif defined(__ICCARM__) + // leave anonymous unions enabled. +#elif defined(__GNUC__) + // anonymous unions are enabled by default. +#endif + + +// Queue of requested operations. +// This queue holds flash operations requested to the module. +// The data to be written to flash must be kept in memory until the write operation +// is completed, i.e., an event indicating completion is received. +typedef struct +{ + fs_op_t op[FS_QUEUE_SIZE]; // Queue elements. + uint32_t rp; // Index of the operation being processed. + uint32_t count; // Number of elements in the queue. +} fs_op_queue_t; + + +// Size of a flash page in bytes. +#if defined (NRF51) + #define FS_PAGE_SIZE (1024) +#elif defined (NRF52) + #define FS_PAGE_SIZE (4096) +#endif + + +// Size of a flash page in words. +#define FS_PAGE_SIZE_WORDS (FS_PAGE_SIZE / sizeof(uint32_t)) + + +// Function to obtain the end of the flash space available to fstorage. +static uint32_t const * fs_flash_page_end_addr() +{ + uint32_t const bootloader_addr = NRF_UICR->NRFFW[0]; + + return (uint32_t*)((bootloader_addr != FS_ERASED_WORD) ? bootloader_addr : + NRF_FICR->CODESIZE * FS_PAGE_SIZE); +} + + +// Macro to obtain the address of the last page. +// If there is a bootloader present the bootloader address read from UICR +// will act as the page beyond the end of the available flash storage. +#define FS_PAGE_END_ADDR (fs_flash_page_end_addr()) + + +#endif //__FSTORAGE_INTERNAL_DEFS_H diff --git a/cores/arduino/components/libraries/fstorage/fstorage_nosd.c b/cores/arduino/components/libraries/fstorage/fstorage_nosd.c new file mode 100644 index 0000000..e69de29 diff --git a/cores/arduino/main.cpp b/cores/arduino/main.cpp index 4d31e90..b74a63b 100644 --- a/cores/arduino/main.cpp +++ b/cores/arduino/main.cpp @@ -20,21 +20,38 @@ #define ARDUINO_MAIN #include "Arduino.h" + /* * \brief Main entry point of Arduino application */ int main(void) { init(); - + delay(0); + SDManager.begin(); - + + // Register with the SoftDevice handler module for BLE events. + softdevice_ble_evt_handler_set(ble_evt_dispatch); + setup(); - + + if(dfuIsEnabled()) + add_dfu_service(); + for(;;){ loop(); if (serialEventRun) serialEventRun(); } return 0; +} + +extern "C" void dbgMsg(const char* msg) +{ + Serial.print(msg); +} +extern "C" void dbgMsgn(uint32_t msg) +{ + Serial.print(msg, HEX); } \ No newline at end of file diff --git a/libraries/BLE/BLEManager.cpp b/libraries/BLE/BLEManager.cpp index 9554354..aad7006 100644 --- a/libraries/BLE/BLEManager.cpp +++ b/libraries/BLE/BLEManager.cpp @@ -31,43 +31,41 @@ extern "C"{ BLEPeripheral *BLEManagerClass::_peripheralList[1] = {0}; BLECentralRole *BLEManagerClass::_centralList[1] = {0}; -bool handlerSet = false; BLEManagerClass::BLEManagerClass(){} bool BLEManagerClass::registerPeripheral(BLEPeripheral *peripheral) { _peripheralList[0] = peripheral; - if(!handlerSet){ - softdevice_ble_evt_handler_set(BLEManager.processBleEvents); - handlerSet = true; - } } bool BLEManagerClass::registerCentral(BLECentralRole *central){ _centralList[0] = central; - if(!handlerSet){ - softdevice_ble_evt_handler_set(BLEManager.processBleEvents); - handlerSet = true; - } } -void BLEManagerClass::processBleEvents(ble_evt_t *bleEvent){ +void processBleEvents(ble_evt_t *bleEvent){ ble_conn_state_on_ble_evt(bleEvent); uint16_t handler = bleEvent->evt.gap_evt.conn_handle; uint16_t role = ble_conn_state_role(handler); if(role == BLE_GAP_ROLE_PERIPH){ - if(_peripheralList[0] != 0){ - _peripheralList[0]->poll(bleEvent); + if(BLEManagerClass::_peripheralList[0] != 0){ + BLEManagerClass::_peripheralList[0]->poll(bleEvent); } } if((role == BLE_GAP_ROLE_CENTRAL) || (bleEvent->header.evt_id == BLE_GAP_EVT_ADV_REPORT)){ - if(_centralList[0] != 0){ - _centralList[0]->poll(bleEvent); + if(BLEManagerClass::_centralList[0] != 0){ + BLEManagerClass::_centralList[0]->poll(bleEvent); } } } +bool isPeripheralRunning(){ + return (BLEManagerClass::_peripheralList[0] != 0) ? true : false; +} + +bool isCentralRunning(){ + return (BLEManagerClass::_centralList[0] != 0) ? true : false; +} BLEManagerClass BLEManager; \ No newline at end of file diff --git a/libraries/BLE/BLEManager.h b/libraries/BLE/BLEManager.h index b380185..004a40c 100644 --- a/libraries/BLE/BLEManager.h +++ b/libraries/BLE/BLEManager.h @@ -33,11 +33,9 @@ class BLEManagerClass { static bool registerCentral(BLECentralRole *central); static void processBleEvents(ble_evt_t *bleEvent); - -private: + static BLEPeripheral *_peripheralList[1]; - static BLECentralRole *_centralList[1]; - static bool _handlerSet; + static BLECentralRole *_centralList[1]; }; extern BLEManagerClass BLEManager; diff --git a/libraries/BLE/nRF51822.cpp b/libraries/BLE/nRF51822.cpp index 9678125..70440d5 100644 --- a/libraries/BLE/nRF51822.cpp +++ b/libraries/BLE/nRF51822.cpp @@ -616,163 +616,163 @@ void nRF51822::poll(ble_evt_t *bleEvt) { #endif break; - case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: #ifdef NRF_51822_DEBUG - Serial.print(F("Evt Sec Params Request ")); + Serial.print(F("Evt Sec Params Request ")); #if !defined(NRF51_S130) && !defined(S110) - // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.timeout); - Serial.print(F(" ")); + // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.timeout); + Serial.print(F(" ")); #endif - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.bond); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.mitm); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.io_caps); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.oob); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.min_key_size); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.max_key_size); - Serial.println(); + Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.bond); + Serial.print(F(" ")); + Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.mitm); + Serial.print(F(" ")); + Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.io_caps); + Serial.print(F(" ")); + Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.oob); + Serial.print(F(" ")); + Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.min_key_size); + Serial.print(F(" ")); + Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.max_key_size); + Serial.println(); #endif - if (this->_bondStore && !this->_bondStore->hasData()) { - // only allow bonding if bond store exists and there is no data + if (this->_bondStore && !this->_bondStore->hasData()) { + // only allow bonding if bond store exists and there is no data - ble_gap_sec_params_t gapSecParams; + ble_gap_sec_params_t gapSecParams; - memset(&gapSecParams, 0x00, sizeof(ble_gap_sec_params_t)); + memset(&gapSecParams, 0x00, sizeof(ble_gap_sec_params_t)); #if defined(NRF5) && !defined(S110) - gapSecParams.kdist_own.enc = 1; + gapSecParams.kdist_own.enc = 1; #elif defined(NRF51_S130) - gapSecParams.kdist_periph.enc = 1; + gapSecParams.kdist_periph.enc = 1; #elif !defined(NRF5) - gapSecParams.timeout = 30; // must be 30s + gapSecParams.timeout = 30; // must be 30s #endif - gapSecParams.bond = true; - gapSecParams.lesc = (bool)this->_lesc; - gapSecParams.mitm = this->_mitm; - gapSecParams.io_caps = this->_io_caps; - gapSecParams.oob = false; - gapSecParams.min_key_size = 7; - gapSecParams.max_key_size = 16; + gapSecParams.bond = true; + gapSecParams.lesc = (bool)this->_lesc; + gapSecParams.mitm = this->_mitm; + gapSecParams.io_caps = this->_io_caps; + gapSecParams.oob = false; + gapSecParams.min_key_size = 7; + gapSecParams.max_key_size = 16; #if defined(NRF5) && !defined(S110) - ble_gap_sec_keyset_t keyset; - memset(&keyset, 0, sizeof(ble_gap_sec_keyset_t)); - if(this->_lesc > 0){ - ecc_init(); - ecc_p256_keypair_gen(this->_privateKey.pk, this->_publicKey.pk); - keyset.keys_own.p_pk=&this->_publicKey; - keyset.keys_peer.p_pk=&this->_peerKey; - } - keyset.keys_peer.p_enc_key = NULL; - keyset.keys_peer.p_id_key = NULL; - keyset.keys_peer.p_sign_key = NULL; - keyset.keys_own.p_enc_key = this->_encKey; - keyset.keys_own.p_id_key = NULL; - keyset.keys_own.p_sign_key = NULL; + ble_gap_sec_keyset_t keyset; + memset(&keyset, 0, sizeof(ble_gap_sec_keyset_t)); + if(this->_lesc > 0){ + ecc_init(); + ecc_p256_keypair_gen(this->_privateKey.pk, this->_publicKey.pk); + keyset.keys_own.p_pk=&this->_publicKey; + keyset.keys_peer.p_pk=&this->_peerKey; + } + keyset.keys_peer.p_enc_key = NULL; + keyset.keys_peer.p_id_key = NULL; + keyset.keys_peer.p_sign_key = NULL; + keyset.keys_own.p_enc_key = this->_encKey; + keyset.keys_own.p_id_key = NULL; + keyset.keys_own.p_sign_key = NULL; - sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams, &keyset); + sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams, &keyset); #elif defined(NRF51_S130) || defined(S110) - ble_gap_sec_keyset_t keyset; + ble_gap_sec_keyset_t keyset; - keyset.keys_central.p_enc_key = NULL; - keyset.keys_central.p_id_key = NULL; - keyset.keys_central.p_sign_key = NULL; - keyset.keys_periph.p_enc_key = this->_encKey; - keyset.keys_periph.p_id_key = NULL; - keyset.keys_periph.p_sign_key = NULL; + keyset.keys_central.p_enc_key = NULL; + keyset.keys_central.p_id_key = NULL; + keyset.keys_central.p_sign_key = NULL; + keyset.keys_periph.p_enc_key = this->_encKey; + keyset.keys_periph.p_id_key = NULL; + keyset.keys_periph.p_sign_key = NULL; - sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams, &keyset); -#else - sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams); -#endif - } else { -#if defined(NRF5) || defined(NRF51_S130) - sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); + sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams, &keyset); #else - sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL); + sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams); #endif - } - break; - - case BLE_GAP_EVT_SEC_INFO_REQUEST: -#ifdef NRF_51822_DEBUG - Serial.print(F("Evt Sec Info Request ")); - // Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.peer_addr); - // Serial.print(F(" ")); + } else { #if defined(NRF5) || defined(NRF51_S130) - Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.master_id.ediv); + sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); #else - Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.div); -#endif - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.enc_info); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.id_info); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.sign_info); - Serial.println(); + sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL); #endif -#if defined(NRF5) || defined(NRF51_S130) - if (this->_encKey->master_id.ediv == bleEvt->evt.gap_evt.params.sec_info_request.master_id.ediv) { - sd_ble_gap_sec_info_reply(this->_connectionHandle, &this->_encKey->enc_info, NULL, NULL); - } else { - sd_ble_gap_sec_info_reply(this->_connectionHandle, NULL, NULL, NULL); - } -#else - if (this->_authStatus->periph_keys.enc_info.div == bleEvt->evt.gap_evt.params.sec_info_request.div) { - sd_ble_gap_sec_info_reply(this->_connectionHandle, &this->_authStatus->periph_keys.enc_info, NULL); - } else { - sd_ble_gap_sec_info_reply(this->_connectionHandle, NULL, NULL); - } -#endif - break; - - case BLE_GAP_EVT_AUTH_STATUS: -#ifdef NRF_51822_DEBUG - Serial.println(F("Evt Auth Status")); - Serial.println(bleEvt->evt.gap_evt.params.auth_status.auth_status); -#endif - if (BLE_GAP_SEC_STATUS_SUCCESS == bleEvt->evt.gap_evt.params.auth_status.auth_status) { -#if !defined(NRF5) && !defined(NRF51_S130) - *this->_authStatus = bleEvt->evt.gap_evt.params.auth_status; -#endif - if (this->_bondStore) { -#ifdef NRF_51822_DEBUG - Serial.println(F("Storing bond data")); -#endif -#if defined(NRF5) || defined(NRF51_S130) - this->_bondStore->saveTempData(this->_bondData, 0, sizeof(this->_bondData)); -#else - this->_bondStore->putData(this->_authStatusBuffer, 0, sizeof(this->_authStatusBuffer)); -#endif - } - - if (this->_eventListener) { - this->_eventListener->BLEDeviceBonded(*this); - } - } - else{ - if (this->_eventListener) { - this->_eventListener->BLEMessageReceived(*this, AUTH_STATUS, bleEvt->evt.gap_evt.params.auth_status.auth_status); - } - } - break; - - case BLE_GAP_EVT_CONN_SEC_UPDATE: -#ifdef NRF_51822_DEBUG - Serial.print(F("Evt Conn Sec Update ")); - Serial.print(bleEvt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.sm); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.conn_sec_update.conn_sec.encr_key_size); - Serial.println(); -#endif - break; + } + break; + +// case BLE_GAP_EVT_SEC_INFO_REQUEST: +//#ifdef NRF_51822_DEBUG +// Serial.print(F("Evt Sec Info Request ")); +// // Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.peer_addr); +// // Serial.print(F(" ")); +//#if defined(NRF5) || defined(NRF51_S130) +// Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.master_id.ediv); +//#else +// Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.div); +//#endif +// Serial.print(F(" ")); +// Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.enc_info); +// Serial.print(F(" ")); +// Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.id_info); +// Serial.print(F(" ")); +// Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.sign_info); +// Serial.println(); +//#endif +//#if defined(NRF5) || defined(NRF51_S130) +// if (this->_encKey->master_id.ediv == bleEvt->evt.gap_evt.params.sec_info_request.master_id.ediv) { +// sd_ble_gap_sec_info_reply(this->_connectionHandle, &this->_encKey->enc_info, NULL, NULL); +// } else { +// sd_ble_gap_sec_info_reply(this->_connectionHandle, NULL, NULL, NULL); +// } +//#else +// if (this->_authStatus->periph_keys.enc_info.div == bleEvt->evt.gap_evt.params.sec_info_request.div) { +// sd_ble_gap_sec_info_reply(this->_connectionHandle, &this->_authStatus->periph_keys.enc_info, NULL); +// } else { +// sd_ble_gap_sec_info_reply(this->_connectionHandle, NULL, NULL); +// } +//#endif +// break; + +// case BLE_GAP_EVT_AUTH_STATUS: +//#ifdef NRF_51822_DEBUG +// Serial.println(F("Evt Auth Status")); +// Serial.println(bleEvt->evt.gap_evt.params.auth_status.auth_status); +//#endif +// if (BLE_GAP_SEC_STATUS_SUCCESS == bleEvt->evt.gap_evt.params.auth_status.auth_status) { +//#if !defined(NRF5) && !defined(NRF51_S130) +// *this->_authStatus = bleEvt->evt.gap_evt.params.auth_status; +//#endif +// if (this->_bondStore) { +//#ifdef NRF_51822_DEBUG +// Serial.println(F("Storing bond data")); +//#endif +//#if defined(NRF5) || defined(NRF51_S130) +// this->_bondStore->saveTempData(this->_bondData, 0, sizeof(this->_bondData)); +//#else +// this->_bondStore->putData(this->_authStatusBuffer, 0, sizeof(this->_authStatusBuffer)); +//#endif +// } + +// if (this->_eventListener) { +// this->_eventListener->BLEDeviceBonded(*this); +// } +// } +// else{ +// if (this->_eventListener) { +// this->_eventListener->BLEMessageReceived(*this, AUTH_STATUS, bleEvt->evt.gap_evt.params.auth_status.auth_status); +// } +// } +// break; + +// case BLE_GAP_EVT_CONN_SEC_UPDATE: +//#ifdef NRF_51822_DEBUG +// Serial.print(F("Evt Conn Sec Update ")); +// Serial.print(bleEvt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.sm); +// Serial.print(F(" ")); +// Serial.print(bleEvt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv); +// Serial.print(F(" ")); +// Serial.print(bleEvt->evt.gap_evt.params.conn_sec_update.conn_sec.encr_key_size); +// Serial.println(); +//#endif +// break; case BLE_GATTS_EVT_WRITE: { #ifdef NRF_51822_DEBUG From 2177e8f60d667648bc3fae70d42d100d5a21beb4 Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Thu, 25 May 2017 10:15:22 +0200 Subject: [PATCH 02/13] updates --- cores/arduino/DFUService.cpp | 47 ++++-- cores/arduino/DFUService.h | 7 + .../device_manager_peripheral.c | 22 +-- libraries/BLE/BLEBondStore.cpp | 18 +- libraries/BLE/BLEBondStore.h | 2 +- libraries/BLE/BLECentralRole.cpp | 5 +- libraries/BLE/BLEPeripheral.cpp | 9 +- libraries/BLE/nRF51822.cpp | 154 +++++++++--------- 8 files changed, 153 insertions(+), 111 deletions(-) diff --git a/cores/arduino/DFUService.cpp b/cores/arduino/DFUService.cpp index 65d26a9..7304bf6 100644 --- a/cores/arduino/DFUService.cpp +++ b/cores/arduino/DFUService.cpp @@ -25,6 +25,7 @@ extern "C"{ #include "ble_advertising.h" #include "pstorage.h" #include "nrf_delay.h" +#include "ble_conn_state.h" #ifdef __cplusplus } @@ -35,6 +36,10 @@ static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID static dm_application_instance_t m_app_handle; bool dfuService = true; +uint8_t bond = 1; +uint8_t mitm = 0; +uint8_t lesc = 0; +uint8_t io_caps = BLE_GAP_IO_CAPS_NONE; extern void processBleEvents(ble_evt_t * p_ble_evt) __attribute__((weak)); extern bool isPeripheralRunning() __attribute__((weak)); @@ -95,10 +100,16 @@ static void reset_prepare(void) void ble_evt_dispatch(ble_evt_t * p_ble_evt) { + uint16_t handler = p_ble_evt->evt.gap_evt.conn_handle; + uint16_t role = ble_conn_state_role(handler); + if(dfuService){ - dm_ble_evt_handler(p_ble_evt); - ble_dfu_on_ble_evt(&m_dfus, p_ble_evt); - on_ble_evt(p_ble_evt); + //DFU service works with peripheral role, don't forward events central related + if(role != BLE_GAP_ROLE_CENTRAL){ + dm_ble_evt_handler(p_ble_evt); + ble_dfu_on_ble_evt(&m_dfus, p_ble_evt); + on_ble_evt(p_ble_evt); + } } // forward events to Arduino BLE library if used if(processBleEvents) @@ -157,6 +168,13 @@ static uint32_t device_manager_evt_handler(dm_handle_t const * p_handle, return NRF_SUCCESS; } +void setSecParams(uint8_t Bond, uint8_t Mitm, uint8_t Lesc, uint8_t IO_caps){ + bond = Bond; + mitm = Mitm; + lesc = Lesc; + io_caps = IO_caps; +} + void add_dfu_service(){ ble_gap_conn_params_t gap_conn_params; ble_gap_conn_sec_mode_t sec_mode; @@ -172,17 +190,14 @@ void add_dfu_service(){ memset(®ister_param.sec_param, 0, sizeof(ble_gap_sec_params_t)); - register_param.sec_param.bond = 1; //SEC_PARAM_BOND - register_param.sec_param.mitm = 0; //SEC_PARAM_MITM - // register_param.sec_param.mitm = true; - register_param.sec_param.lesc = 0; //SEC_PARAM_LESC -// register_param.sec_param.lesc = true; - register_param.sec_param.keypress = 0; //SEC_PARAM_KEYPRESS - register_param.sec_param.io_caps = BLE_GAP_IO_CAPS_NONE;//BLE_GAP_IO_CAPS_DISPLAY_ONLY;//BLE_GAP_IO_CAPS_KEYBOARD_ONLY; - // register_param.sec_param.io_caps =BLE_GAP_IO_CAPS_DISPLAY_YESNO; - register_param.sec_param.oob = 0; //SEC_PARAM_OOB - register_param.sec_param.min_key_size = 7; //SEC_PARAM_MIN_KEY_SIZE - register_param.sec_param.max_key_size = 16; //SEC_PARAM_MAX_KEY_SIZE + register_param.sec_param.bond = bond; + register_param.sec_param.mitm = mitm; + register_param.sec_param.lesc = lesc; + register_param.sec_param.keypress = 0; + register_param.sec_param.io_caps = io_caps; + register_param.sec_param.oob = 0; + register_param.sec_param.min_key_size = 7; + register_param.sec_param.max_key_size = 16; register_param.evt_handler = device_manager_evt_handler; register_param.service_type = DM_PROTOCOL_CNTXT_GATT_SRVR_ID; @@ -275,4 +290,8 @@ void add_dfu_service(){ bool dfuIsEnabled(){ return dfuService; +} + +void eraseBond(){ + dm_device_delete_all(&m_app_handle); } \ No newline at end of file diff --git a/cores/arduino/DFUService.h b/cores/arduino/DFUService.h index 3f5e87b..9880e50 100644 --- a/cores/arduino/DFUService.h +++ b/cores/arduino/DFUService.h @@ -42,7 +42,14 @@ extern bool dfuIsEnabled(); */ extern void add_dfu_service(); +/* + * \brief Set parameters used in bonding procedure from external libraries. + * + * \param Bond, Mimt, Lest, IO_caps + */ +extern void setSecParams(uint8_t Bond, uint8_t Mitm, uint8_t Lesc, uint8_t IO_caps); +extern void eraseBond(); /* * \brief Receive and forward all BLE events. * diff --git a/cores/arduino/components/ble/device_manager/device_manager_peripheral.c b/cores/arduino/components/ble/device_manager/device_manager_peripheral.c index f6657a2..145a7d7 100644 --- a/cores/arduino/components/ble/device_manager/device_manager_peripheral.c +++ b/cores/arduino/components/ble/device_manager/device_manager_peripheral.c @@ -757,14 +757,14 @@ static __INLINE ret_code_t device_instance_free(uint32_t device_index) //Get the block handle. err_code = pstorage_block_identifier_get(&m_storage_handle, device_index, &block_handle); - +dbgMsg("block identification = "); dbgMsgn(err_code); dbgMsg("\r\n"); if (err_code == NRF_SUCCESS) { DM_TRC("[DM]:[DI 0x%02X]: Freeing Instance.\r\n", device_index); //Request clearing of the block. err_code = pstorage_clear(&block_handle, ALL_CONTEXT_SIZE); - +dbgMsg("storage clear = "); dbgMsgn(err_code); dbgMsg("\r\n"); if (err_code == NRF_SUCCESS) { peer_instance_init(device_index); @@ -1941,19 +1941,23 @@ ret_code_t dm_device_add(dm_handle_t * p_handle, ret_code_t dm_device_delete(dm_handle_t const * p_handle) -{ +{dbgMsg("INSIDE FUNCTION"); VERIFY_MODULE_INITIALIZED(); + dbgMsg("Module initialized passed"); NULL_PARAM_CHECK(p_handle); - VERIFY_APP_REGISTERED(p_handle->appl_id); - VERIFY_DEVICE_INSTANCE(p_handle->device_id); + dbgMsg("null param check passed"); +// VERIFY_APP_REGISTERED(p_handle->appl_id); + dbgMsg("app registered passed"); +// VERIFY_DEVICE_INSTANCE(p_handle->device_id); + dbgMsg("device instance passed"); DM_MUTEX_LOCK(); - DM_TRC("[DM]: >> dm_device_delete\r\n"); + dbgMsg("[DM]: >> dm_device_delete\r\n"); uint32_t err_code = device_instance_free(p_handle->device_id); - DM_TRC("[DM]: << dm_device_delete\r\n"); + dbgMsg("[DM]: << dm_device_delete\r\n"); DM_MUTEX_UNLOCK(); @@ -2628,9 +2632,7 @@ void dm_ble_evt_handler(ble_evt_t * p_ble_evt) { if ((m_connection_table[index].state & STATE_LINK_ENCRYPTED) == STATE_LINK_ENCRYPTED) { - NRF_GPIO->DIRSET = (1UL << 3); //A0 - NRF_GPIO->OUTSET = (1UL << 3); //A0 - //Write bond information persistently. + //Write bond information persistently. device_context_store(&handle, STORE_ALL_CONTEXT); } } diff --git a/libraries/BLE/BLEBondStore.cpp b/libraries/BLE/BLEBondStore.cpp index dfa6b1a..a05a7f4 100644 --- a/libraries/BLE/BLEBondStore.cpp +++ b/libraries/BLE/BLEBondStore.cpp @@ -18,16 +18,22 @@ #include "BLEBondStore.h" -BLEBondStore::BLEBondStore(int offset) -#if defined(__AVR__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) - : _offset(offset), -#elif defined(NRF51) || defined(NRF52) || defined(__RFduino__) - : _flashPageStartAddress((uint32_t *)(NRF_FICR->CODEPAGESIZE * (NRF_FICR->CODESIZE - 1 - (uint32_t)offset))), -#endif +BLEBondStore::BLEBondStore(int offset) : + #if defined(__AVR__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) + _offset(offset), +// #elif defined(NRF51) || defined(NRF52) || defined(__RFduino__) + // : _flashPageStartAddress((uint32_t *)(NRF_FICR->CODEPAGESIZE * (NRF_FICR->CODESIZE - 1 - (uint32_t)offset))), + #endif _tempData(NULL), _tempOffset(0), _tempLength(0) { + //by default offset is the bootloader address + //if it's null use the end of the flash + //otherwise save the data between the end of the sketch zone and the bootloader + offset = (offset != 0xFFFFFFFF) ? (offset / NRF_FICR->CODEPAGESIZE) : NRF_FICR->CODESIZE; + //save bond data at the second page before the end of the saving zone since the last page is reserved to DFU bond data (if used) + _flashPageStartAddress = (uint32_t *)(NRF_FICR->CODEPAGESIZE * (NRF_FICR->CODESIZE - 2 - (uint32_t)offset)); } bool BLEBondStore::hasData() { diff --git a/libraries/BLE/BLEBondStore.h b/libraries/BLE/BLEBondStore.h index dd92f1f..73fcbe4 100644 --- a/libraries/BLE/BLEBondStore.h +++ b/libraries/BLE/BLEBondStore.h @@ -8,7 +8,7 @@ class BLEBondStore { public: - BLEBondStore(int offset = 0); + BLEBondStore(int offset = NRF_UICR->NRFFW[0]); bool hasData(); void clearData(); diff --git a/libraries/BLE/BLECentralRole.cpp b/libraries/BLE/BLECentralRole.cpp index 80eb230..e1c3884 100644 --- a/libraries/BLE/BLECentralRole.cpp +++ b/libraries/BLE/BLECentralRole.cpp @@ -29,7 +29,7 @@ #endif -// #define BLE_CENTRAL_DEBUG +#define BLE_CENTRAL_DEBUG BLECentralRole::BLECentralRole() : @@ -1103,7 +1103,8 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ #ifdef BLE_CENTRAL_DEBUG Serial.println(F("Storing bond data")); #endif - this->_bondStore.saveTempData(this->_bondData, 0, sizeof(this->_bondData)); + this->_bondStore.putData(this->_bondData, 0, sizeof(this->_bondData)); + // this->_bondStore.saveTempData(this->_bondData, 0, sizeof(this->_bondData)); } uint8_t currentPeripheral; diff --git a/libraries/BLE/BLEPeripheral.cpp b/libraries/BLE/BLEPeripheral.cpp index 950292f..0134390 100644 --- a/libraries/BLE/BLEPeripheral.cpp +++ b/libraries/BLE/BLEPeripheral.cpp @@ -172,31 +172,37 @@ void BLEPeripheral::enableBond(BLEBondingType type){ this->_device->_mitm = true; this->_device->_io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; this->_device->_lesc = 0; + setSecParams(1, 1, 0, BLE_GAP_IO_CAPS_DISPLAY_ONLY); break; case CONFIRM_PASSKEY: this->_device->_mitm = true; this->_device->_io_caps = BLE_GAP_IO_CAPS_KEYBOARD_ONLY; this->_device->_lesc = 0; + setSecParams(1, 1, 0, BLE_GAP_IO_CAPS_KEYBOARD_ONLY); break; case LESC: this->_device->_mitm = false; this->_device->_io_caps = BLE_GAP_IO_CAPS_NONE; this->_device->_lesc = 1; + setSecParams(1, 1, 1, BLE_GAP_IO_CAPS_NONE); break; case LESC_NUM_COMPARISON: this->_device->_mitm = true; this->_device->_io_caps = BLE_GAP_IO_CAPS_DISPLAY_YESNO; this->_device->_lesc = 2; + setSecParams(1, 1, 1, BLE_GAP_IO_CAPS_DISPLAY_YESNO); break; case LESC_DISPLAY_PASSKEY: this->_device->_mitm = true; this->_device->_io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; this->_device->_lesc = 3; + setSecParams(1, 1, 1, BLE_GAP_IO_CAPS_DISPLAY_ONLY); break; case LESC_CONFIRM_PASSKEY: this->_device->_mitm = true; this->_device->_io_caps = BLE_GAP_IO_CAPS_KEYBOARD_ONLY; this->_device->_lesc = 3; + setSecParams(1, 1, 1, BLE_GAP_IO_CAPS_KEYBOARD_ONLY); break; default: @@ -206,7 +212,8 @@ void BLEPeripheral::enableBond(BLEBondingType type){ } void BLEPeripheral::clearBondStoreData() { - this->_bleBondStore.clearData(); + //this->_bleBondStore.clearData(); + eraseBond(); } void BLEPeripheral::saveBondData(){ diff --git a/libraries/BLE/nRF51822.cpp b/libraries/BLE/nRF51822.cpp index 70440d5..44f001e 100644 --- a/libraries/BLE/nRF51822.cpp +++ b/libraries/BLE/nRF51822.cpp @@ -616,87 +616,87 @@ void nRF51822::poll(ble_evt_t *bleEvt) { #endif break; - case BLE_GAP_EVT_SEC_PARAMS_REQUEST: -#ifdef NRF_51822_DEBUG - Serial.print(F("Evt Sec Params Request ")); -#if !defined(NRF51_S130) && !defined(S110) + // case BLE_GAP_EVT_SEC_PARAMS_REQUEST: +// #ifdef NRF_51822_DEBUG + // Serial.print(F("Evt Sec Params Request ")); +// #if !defined(NRF51_S130) && !defined(S110) // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.timeout); - Serial.print(F(" ")); -#endif - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.bond); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.mitm); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.io_caps); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.oob); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.min_key_size); - Serial.print(F(" ")); - Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.max_key_size); - Serial.println(); -#endif - if (this->_bondStore && !this->_bondStore->hasData()) { + // Serial.print(F(" ")); +// #endif + // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.bond); + // Serial.print(F(" ")); + // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.mitm); + // Serial.print(F(" ")); + // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.io_caps); + // Serial.print(F(" ")); + // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.oob); + // Serial.print(F(" ")); + // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.min_key_size); + // Serial.print(F(" ")); + // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.max_key_size); + // Serial.println(); +// #endif + // if (this->_bondStore && !this->_bondStore->hasData()) { // only allow bonding if bond store exists and there is no data - ble_gap_sec_params_t gapSecParams; - - memset(&gapSecParams, 0x00, sizeof(ble_gap_sec_params_t)); - -#if defined(NRF5) && !defined(S110) - gapSecParams.kdist_own.enc = 1; -#elif defined(NRF51_S130) - gapSecParams.kdist_periph.enc = 1; -#elif !defined(NRF5) - gapSecParams.timeout = 30; // must be 30s -#endif - gapSecParams.bond = true; - gapSecParams.lesc = (bool)this->_lesc; - gapSecParams.mitm = this->_mitm; - gapSecParams.io_caps = this->_io_caps; - gapSecParams.oob = false; - gapSecParams.min_key_size = 7; - gapSecParams.max_key_size = 16; - -#if defined(NRF5) && !defined(S110) - ble_gap_sec_keyset_t keyset; - memset(&keyset, 0, sizeof(ble_gap_sec_keyset_t)); - if(this->_lesc > 0){ - ecc_init(); - ecc_p256_keypair_gen(this->_privateKey.pk, this->_publicKey.pk); - keyset.keys_own.p_pk=&this->_publicKey; - keyset.keys_peer.p_pk=&this->_peerKey; - } - keyset.keys_peer.p_enc_key = NULL; - keyset.keys_peer.p_id_key = NULL; - keyset.keys_peer.p_sign_key = NULL; - keyset.keys_own.p_enc_key = this->_encKey; - keyset.keys_own.p_id_key = NULL; - keyset.keys_own.p_sign_key = NULL; + // ble_gap_sec_params_t gapSecParams; + + // memset(&gapSecParams, 0x00, sizeof(ble_gap_sec_params_t)); + +// #if defined(NRF5) && !defined(S110) + // gapSecParams.kdist_own.enc = 1; +// #elif defined(NRF51_S130) + // gapSecParams.kdist_periph.enc = 1; +// #elif !defined(NRF5) + // gapSecParams.timeout = 30; // must be 30s +// #endif + // gapSecParams.bond = true; + // gapSecParams.lesc = (bool)this->_lesc; + // gapSecParams.mitm = this->_mitm; + // gapSecParams.io_caps = this->_io_caps; + // gapSecParams.oob = false; + // gapSecParams.min_key_size = 7; + // gapSecParams.max_key_size = 16; + +// #if defined(NRF5) && !defined(S110) + // ble_gap_sec_keyset_t keyset; + // memset(&keyset, 0, sizeof(ble_gap_sec_keyset_t)); + // if(this->_lesc > 0){ + // ecc_init(); + // ecc_p256_keypair_gen(this->_privateKey.pk, this->_publicKey.pk); + // keyset.keys_own.p_pk=&this->_publicKey; + // keyset.keys_peer.p_pk=&this->_peerKey; + // } + // keyset.keys_peer.p_enc_key = NULL; + // keyset.keys_peer.p_id_key = NULL; + // keyset.keys_peer.p_sign_key = NULL; + // keyset.keys_own.p_enc_key = this->_encKey; + // keyset.keys_own.p_id_key = NULL; + // keyset.keys_own.p_sign_key = NULL; - sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams, &keyset); -#elif defined(NRF51_S130) || defined(S110) - ble_gap_sec_keyset_t keyset; - - keyset.keys_central.p_enc_key = NULL; - keyset.keys_central.p_id_key = NULL; - keyset.keys_central.p_sign_key = NULL; - keyset.keys_periph.p_enc_key = this->_encKey; - keyset.keys_periph.p_id_key = NULL; - keyset.keys_periph.p_sign_key = NULL; - - sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams, &keyset); -#else - sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams); -#endif - } else { -#if defined(NRF5) || defined(NRF51_S130) - sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); -#else - sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL); -#endif - } - break; + // sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams, &keyset); +// #elif defined(NRF51_S130) || defined(S110) + // ble_gap_sec_keyset_t keyset; + + // keyset.keys_central.p_enc_key = NULL; + // keyset.keys_central.p_id_key = NULL; + // keyset.keys_central.p_sign_key = NULL; + // keyset.keys_periph.p_enc_key = this->_encKey; + // keyset.keys_periph.p_id_key = NULL; + // keyset.keys_periph.p_sign_key = NULL; + + // sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams, &keyset); +// #else + // sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams); +// #endif + // } else { +// #if defined(NRF5) || defined(NRF51_S130) + // sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); +// #else + // sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL); +// #endif + // } + // break; // case BLE_GAP_EVT_SEC_INFO_REQUEST: //#ifdef NRF_51822_DEBUG From 85813085c9012e2f9bece07b4e90842fab88e09a Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Mon, 29 May 2017 09:01:08 +0200 Subject: [PATCH 03/13] Further developments --- cores/arduino/DFUService.cpp | 42 +++++++++--------------------- cores/arduino/DFUService.h | 1 + libraries/BLE/BLEBondStore.cpp | 14 +++++----- libraries/BLE/BLECentralRole.cpp | 30 ++++++++++++--------- libraries/BLE/BLEHIDPeripheral.cpp | 3 +++ libraries/BLE/BLEPeripheral.cpp | 6 ++--- 6 files changed, 45 insertions(+), 51 deletions(-) diff --git a/cores/arduino/DFUService.cpp b/cores/arduino/DFUService.cpp index 7304bf6..10493e6 100644 --- a/cores/arduino/DFUService.cpp +++ b/cores/arduino/DFUService.cpp @@ -80,7 +80,7 @@ static void on_ble_evt(ble_evt_t * p_ble_evt) break; } } -#include "Arduino.h" + static void reset_prepare(void) { if (m_conn_handle != BLE_CONN_HANDLE_INVALID) @@ -175,12 +175,7 @@ void setSecParams(uint8_t Bond, uint8_t Mitm, uint8_t Lesc, uint8_t IO_caps){ io_caps = IO_caps; } -void add_dfu_service(){ - ble_gap_conn_params_t gap_conn_params; - ble_gap_conn_sec_mode_t sec_mode; - -//*** device manager init - start - +void initDM(){ dm_init_param_t init_param = {.clear_persistent_data = false}; dm_application_param_t register_param; @@ -202,10 +197,18 @@ void add_dfu_service(){ register_param.service_type = DM_PROTOCOL_CNTXT_GATT_SRVR_ID; dm_register(&m_app_handle, ®ister_param); - -//*** device manager init - end + +} + +void add_dfu_service(){ + + // Initialize device manager + initDM(); // If isPeripheralRunning is defined user is using Arduino BLE library if(!isPeripheralRunning || !isPeripheralRunning()){ + ble_gap_conn_params_t gap_conn_params; + ble_gap_conn_sec_mode_t sec_mode; + // Advertising and connection parameters have to be set only if // user is not using Arduino BLE library or peripheral role is not running // to avoid overriding user's settings @@ -234,27 +237,6 @@ void add_dfu_service(){ len=len+2; sd_ble_gap_adv_data_set(adv_data, len, NULL, 0); - // ble_advdata_t advdata; - // static ble_uuid_t m_adv_uuids[] = { - // {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}}; /**< Universally unique service identifiers. */ - - // Build advertising data struct to pass into @ref ble_advertising_init. - // memset(&advdata, 0, sizeof(advdata)); - - // advdata.name_type = BLE_ADVDATA_FULL_NAME; - // advdata.include_appearance = true; - // advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; - // advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]); - // advdata.uuids_complete.p_uuids = m_adv_uuids; - - // ble_adv_modes_config_t options = {0}; - // options.ble_adv_fast_enabled = true; //BLE_ADV_FAST_ENABLED; - // options.ble_adv_fast_interval = 40; //APP_ADV_INTERVAL; - // options.ble_adv_fast_timeout = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED; - // if(isCentralRunning && isCentralRunning()) //don't pass options in ble_advertising_init if central is running - // ble_advertising_init(&advdata, NULL, NULL, NULL, NULL); - // else - // ble_advertising_init(&advdata, NULL, &options, NULL, NULL); } /** @snippet [DFU BLE Service initialization] */ diff --git a/cores/arduino/DFUService.h b/cores/arduino/DFUService.h index 9880e50..2a84130 100644 --- a/cores/arduino/DFUService.h +++ b/cores/arduino/DFUService.h @@ -50,6 +50,7 @@ extern void add_dfu_service(); extern void setSecParams(uint8_t Bond, uint8_t Mitm, uint8_t Lesc, uint8_t IO_caps); extern void eraseBond(); +extern void initDM(); /* * \brief Receive and forward all BLE events. * diff --git a/libraries/BLE/BLEBondStore.cpp b/libraries/BLE/BLEBondStore.cpp index a05a7f4..ff605cb 100644 --- a/libraries/BLE/BLEBondStore.cpp +++ b/libraries/BLE/BLEBondStore.cpp @@ -28,10 +28,12 @@ BLEBondStore::BLEBondStore(int offset) : _tempOffset(0), _tempLength(0) { - //by default offset is the bootloader address - //if it's null use the end of the flash - //otherwise save the data between the end of the sketch zone and the bootloader - offset = (offset != 0xFFFFFFFF) ? (offset / NRF_FICR->CODEPAGESIZE) : NRF_FICR->CODESIZE; + //offset takes into account the bootloader address in order to save data + //between end of the application and begin of bootloader. + // Get the bootloader starting address (in number of pages) + offset = offset / NRF_FICR->CODEPAGESIZE; + // Offset must be: end_of_flash - bootloader_address + offset = NRF_FICR->CODESIZE - offset; //save bond data at the second page before the end of the saving zone since the last page is reserved to DFU bond data (if used) _flashPageStartAddress = (uint32_t *)(NRF_FICR->CODEPAGESIZE * (NRF_FICR->CODESIZE - 2 - (uint32_t)offset)); } @@ -51,8 +53,8 @@ void BLEBondStore::clearData() { eeprom_write_byte((unsigned char *)this->_offset, 0x00); #elif defined(NRF51) || defined(NRF52) int32_t pageNo = (uint32_t)_flashPageStartAddress / NRF_FICR->CODEPAGESIZE; - - while(sd_flash_page_erase(pageNo) == NRF_ERROR_BUSY); +uint32_t err_code; + while(err_code = sd_flash_page_erase(pageNo) == NRF_ERROR_BUSY); #elif defined(__RFduino__) // turn on flash erase enable diff --git a/libraries/BLE/BLECentralRole.cpp b/libraries/BLE/BLECentralRole.cpp index e1c3884..83551a6 100644 --- a/libraries/BLE/BLECentralRole.cpp +++ b/libraries/BLE/BLECentralRole.cpp @@ -236,7 +236,7 @@ void BLECentralRole::setBondStore(BLEBondStore& bondStore){ void BLECentralRole::enableBond(BLEBondingType type){ this->_bond = true; - this->clearBondStoreData(); +// this->clearBondStoreData(); switch(type){ case DISPLAY_PASSKEY: this->_mitm = true; @@ -668,21 +668,27 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ sd_ble_gattc_primary_services_discover(this->_connectionHandle[index], 1, NULL); } if(this->_bond){ - ble_gap_sec_params_t gapSecParams; + if (this->_bondStore.hasData()){ // device already bonded + Serial.println("ALREADY BONDED"); + sd_ble_gap_encrypt(this->_connectionHandle[index], &this->_encKey->master_id, &this->_encKey->enc_info); + } + else{ // first bond. Require authentication + ble_gap_sec_params_t gapSecParams; - memset(&gapSecParams, 0x00, sizeof(ble_gap_sec_params_t)); + memset(&gapSecParams, 0x00, sizeof(ble_gap_sec_params_t)); - gapSecParams.kdist_own.enc = 1; + gapSecParams.kdist_own.enc = 1; - gapSecParams.bond = true; - gapSecParams.lesc = (bool)this->_lesc; - gapSecParams.mitm = this->_mitm; - gapSecParams.io_caps = this->_io_caps; - gapSecParams.oob = false; - gapSecParams.min_key_size = 7; - gapSecParams.max_key_size = 16; + gapSecParams.bond = true; + gapSecParams.lesc = (bool)this->_lesc; + gapSecParams.mitm = this->_mitm; + gapSecParams.io_caps = this->_io_caps; + gapSecParams.oob = false; + gapSecParams.min_key_size = 7; + gapSecParams.max_key_size = 16; - sd_ble_gap_authenticate(this->_connectionHandle[index], &gapSecParams); + sd_ble_gap_authenticate(this->_connectionHandle[index], &gapSecParams); + } } _peripheralConnected++; diff --git a/libraries/BLE/BLEHIDPeripheral.cpp b/libraries/BLE/BLEHIDPeripheral.cpp index 7c0115e..8002537 100644 --- a/libraries/BLE/BLEHIDPeripheral.cpp +++ b/libraries/BLE/BLEHIDPeripheral.cpp @@ -22,6 +22,9 @@ BLEHIDPeripheral::BLEHIDPeripheral(unsigned char req, unsigned char rdy, unsigne _numHids(0) { _instance = this; + + //initialize device manager + initDM(); } BLEHIDPeripheral::~BLEHIDPeripheral() { diff --git a/libraries/BLE/BLEPeripheral.cpp b/libraries/BLE/BLEPeripheral.cpp index 0134390..99e96d3 100644 --- a/libraries/BLE/BLEPeripheral.cpp +++ b/libraries/BLE/BLEPeripheral.cpp @@ -165,8 +165,6 @@ void BLEPeripheral::setBondStore(BLEBondStore& bondStore) { } void BLEPeripheral::enableBond(BLEBondingType type){ - this->setBondStore(this->_bleBondStore); - this->clearBondStoreData(); switch(type){ case DISPLAY_PASSKEY: this->_device->_mitm = true; @@ -212,7 +210,9 @@ void BLEPeripheral::enableBond(BLEBondingType type){ } void BLEPeripheral::clearBondStoreData() { - //this->_bleBondStore.clearData(); + // device manager can be uninitialized if this function is + // called before loop function + initDM(); eraseBond(); } From 5f1effb768572bab1919b7518bfa39f573b60b90 Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Thu, 1 Jun 2017 13:30:08 +0200 Subject: [PATCH 04/13] Updated platform.txt --- platform.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform.txt b/platform.txt index 7e3c7b5..a1d2634 100644 --- a/platform.txt +++ b/platform.txt @@ -40,7 +40,7 @@ compiler.cpp.flags=-DNRF5 -DNRF52 -DS132 -DSOFTDEVICE_PRESENT -DBLE_STACK_SUPPOR # Compile includes -compiler.nrf_api_include="-I{runtime.tools.CMSIS.path}/CMSIS/Include" "-I{runtime.platform.path}/cores/arduino/components/device" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/hal" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/clock" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/common" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/config" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/delay" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/uart" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/rng" "-I{runtime.platform.path}/cores/arduino/components/libraries/util" "-I{runtime.platform.path}/cores/arduino/components/libraries/timer" "-I{runtime.platform.path}/cores/arduino/components/libraries/trace" "-I{runtime.platform.path}/cores/arduino/components/libraries/scheduler" "-I{runtime.platform.path}/cores/arduino/components/libraries/uart" "-I{runtime.platform.path}/cores/arduino/components/libraries/softuart" "-I{runtime.platform.path}/cores/arduino/components/libraries/fifo" "-I{runtime.platform.path}/cores/arduino/components/libraries/ecc" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/ac_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/ep_oob_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/hs_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/le_oob_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/generic/message" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/generic/record" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/launchapp" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/parser/message" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/parser/record" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/text" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/uri" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib/hal_t2t" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_parser" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib" "-I{runtime.platform.path}/cores/arduino/components/ble/common" "-I{runtime.platform.path}/cores/arduino/components/softdevice/s132/headers" "-I{runtime.platform.path}/cores/arduino/components/softdevice/common/softdevice_handler" "-I{runtime.platform.path}/cores/arduino/components/toolchain" "-I{runtime.platform.path}/cores/arduino/components/toolchain/gcc" "-I{runtime.platform.path}/libraries/BLE/utility/uECC" +compiler.nrf_api_include="-I{runtime.tools.CMSIS.path}/CMSIS/Include" "-I{runtime.platform.path}/cores/arduino/components/device" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/hal" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/clock" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/common" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/config" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/delay" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/uart" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/rng" "-I{runtime.platform.path}/cores/arduino/components/libraries/util" "-I{runtime.platform.path}/cores/arduino/components/libraries/timer" "-I{runtime.platform.path}/cores/arduino/components/libraries/trace" "-I{runtime.platform.path}/cores/arduino/components/libraries/scheduler" "-I{runtime.platform.path}/cores/arduino/components/libraries/uart" "-I{runtime.platform.path}/cores/arduino/components/libraries/softuart" "-I{runtime.platform.path}/cores/arduino/components/libraries/fifo" "-I{runtime.platform.path}/cores/arduino/components/libraries/ecc" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/ac_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/ep_oob_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/hs_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/le_oob_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/generic/message" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/generic/record" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/launchapp" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/parser/message" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/parser/record" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/text" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/uri" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib/hal_t2t" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_parser" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib" "-I{runtime.platform.path}/cores/arduino/components/ble/common" "-I{runtime.platform.path}/cores/arduino/components/softdevice/s132/headers" "-I{runtime.platform.path}/cores/arduino/components/softdevice/common/softdevice_handler" "-I{runtime.platform.path}/cores/arduino/components/toolchain" "-I{runtime.platform.path}/cores/arduino/components/toolchain/gcc" "-I{runtime.platform.path}/libraries/BLE/utility/uECC" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/pstorage" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/pstorage/config" "-I{runtime.platform.path}/cores/arduino/components/libraries/fstorage" "-I{runtime.platform.path}/cores/arduino/components/libraries/fstorage/config" "-I{runtime.platform.path}/cores/arduino/components/libraries/experimental_section_vars" "-I{runtime.platform.path}/cores/arduino/components/ble/device_manager" "-I{runtime.platform.path}/cores/arduino/components/ble/device_manager/config" "-I{runtime.platform.path}/cores/arduino/components/ble/ble_services/ble_dfu" "-I{runtime.platform.path}/cores/arduino/components/libraries/bootloader_dfu" "-I{runtime.platform.path}/cores/arduino/components/ble/ble_advertising" # Create archives options compiler.ar.cmd=arm-none-eabi-ar compiler.ar.flags=rcs From 57420b218db15cf9133c359603b28b8fb6a8f56d Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Fri, 2 Jun 2017 17:55:49 +0200 Subject: [PATCH 05/13] Removed LESC bond --- libraries/BLE/BLECentralRole.cpp | 99 ++------- libraries/BLE/BLECentralRole.h | 6 - libraries/BLE/BLEDevice.cpp | 1 - libraries/BLE/BLEDevice.h | 1 - libraries/BLE/BLEPeripheral.cpp | 26 --- libraries/BLE/BLEPeripheral.h | 6 +- .../enterPasskeyCentral.ino | 2 +- .../numericComparisonCentral.ino | 133 ----------- .../numericComparison/numericComparison.ino | 116 ---------- .../Bonding/showPasskey/showPasskey.ino | 2 +- libraries/BLE/nRF51822.cpp | 206 +----------------- libraries/BLE/nRF51822.h | 4 - 12 files changed, 32 insertions(+), 570 deletions(-) delete mode 100644 libraries/BLE/examples/Central/Bonding/numericComparisonCentral/numericComparisonCentral.ino delete mode 100644 libraries/BLE/examples/Peripheral/Bonding/numericComparison/numericComparison.ino diff --git a/libraries/BLE/BLECentralRole.cpp b/libraries/BLE/BLECentralRole.cpp index 83551a6..bb07bb0 100644 --- a/libraries/BLE/BLECentralRole.cpp +++ b/libraries/BLE/BLECentralRole.cpp @@ -20,16 +20,7 @@ #include "BLECentralRole.h" #include "BLEUtil.h" -#ifdef __cplusplus - extern "C"{ -#endif -#include -#ifdef __cplusplus - } -#endif - - -#define BLE_CENTRAL_DEBUG +// #define BLE_CENTRAL_DEBUG BLECentralRole::BLECentralRole() : @@ -71,7 +62,6 @@ BLECentralRole::BLECentralRole() : _bond(false), _bondStore(), _mitm(false), - _lesc(0), _io_caps(BLE_GAP_IO_CAPS_NONE), _passkey({0,0,0,0,0,0}), _userConfirm(false) @@ -241,32 +231,10 @@ void BLECentralRole::enableBond(BLEBondingType type){ case DISPLAY_PASSKEY: this->_mitm = true; this->_io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; - this->_lesc = 0; break; case CONFIRM_PASSKEY: this->_mitm = true; this->_io_caps = BLE_GAP_IO_CAPS_KEYBOARD_ONLY; - this->_lesc = 0; - break; - case LESC: - this->_mitm = false; - this->_io_caps = BLE_GAP_IO_CAPS_NONE; - this->_lesc = 1; - break; - case LESC_NUM_COMPARISON: - this->_mitm = true; - this->_io_caps = BLE_GAP_IO_CAPS_DISPLAY_YESNO; - this->_lesc = 2; - break; - case LESC_DISPLAY_PASSKEY: - this->_mitm = true; - this->_io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; - this->_lesc = 3; - break; - case LESC_CONFIRM_PASSKEY: - this->_mitm = true; - this->_io_caps = BLE_GAP_IO_CAPS_KEYBOARD_ONLY; - this->_lesc = 3; break; default: @@ -402,10 +370,7 @@ void BLECentralRole::begin(){ if (properties & (BLERead | BLENotify | BLEIndicate)) { if (this->_bond) { - if(this->_lesc > 1) - BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&characteristicValueAttributeMetaData.read_perm); - else - BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&characteristicValueAttributeMetaData.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&characteristicValueAttributeMetaData.read_perm); } else { BLE_GAP_CONN_SEC_MODE_SET_OPEN(&characteristicValueAttributeMetaData.read_perm); } @@ -413,10 +378,7 @@ void BLECentralRole::begin(){ if (properties & (BLEWriteWithoutResponse | BLEWrite)) { if (this->_bond) { - if(this->_lesc > 1) - BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&characteristicValueAttributeMetaData.write_perm); - else - BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&characteristicValueAttributeMetaData.write_perm); + BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&characteristicValueAttributeMetaData.write_perm); } else { BLE_GAP_CONN_SEC_MODE_SET_OPEN(&characteristicValueAttributeMetaData.write_perm); } @@ -493,10 +455,7 @@ void BLECentralRole::begin(){ descriptorMetaData.vlen = (valueLength == descriptor->valueLength()) ? 0 : 1; if (this->_bond) { - if(this->_lesc > 1) - BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&descriptorMetaData.read_perm); - else - BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&descriptorMetaData.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&descriptorMetaData.read_perm); } else { BLE_GAP_CONN_SEC_MODE_SET_OPEN(&descriptorMetaData.read_perm); } @@ -669,7 +628,6 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ } if(this->_bond){ if (this->_bondStore.hasData()){ // device already bonded - Serial.println("ALREADY BONDED"); sd_ble_gap_encrypt(this->_connectionHandle[index], &this->_encKey->master_id, &this->_encKey->enc_info); } else{ // first bond. Require authentication @@ -680,7 +638,7 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ gapSecParams.kdist_own.enc = 1; gapSecParams.bond = true; - gapSecParams.lesc = (bool)this->_lesc; + gapSecParams.lesc = false; gapSecParams.mitm = this->_mitm; gapSecParams.io_caps = this->_io_caps; gapSecParams.oob = false; @@ -766,7 +724,7 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ gapSecParams.kdist_own.enc = 1; gapSecParams.bond = true; - gapSecParams.lesc = (bool)this->_lesc; + gapSecParams.lesc = false; gapSecParams.mitm = this->_mitm; gapSecParams.io_caps = this->_io_caps; gapSecParams.oob = false; @@ -775,12 +733,6 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ ble_gap_sec_keyset_t keyset; memset(&keyset, 0, sizeof(ble_gap_sec_keyset_t)); - if(this->_lesc > 0){ - ecc_init(); - ecc_p256_keypair_gen(this->_privateKey.pk, this->_publicKey.pk); - keyset.keys_own.p_pk=&this->_publicKey; - keyset.keys_peer.p_pk=&this->_peerKey; - } keyset.keys_peer.p_enc_key = NULL; keyset.keys_peer.p_id_key = NULL; @@ -928,7 +880,7 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ gapSecParams.kdist_own.enc = 1; gapSecParams.bond = true; - gapSecParams.lesc = (bool)this->_lesc; + gapSecParams.lesc = false; gapSecParams.mitm = this->_mitm; gapSecParams.io_caps = this->_io_caps; gapSecParams.oob = false; @@ -979,7 +931,7 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ gapSecParams.kdist_own.enc = 1; gapSecParams.bond = true; - gapSecParams.lesc = (bool)this->_lesc; + gapSecParams.lesc = 0; gapSecParams.mitm = this->_mitm; gapSecParams.io_caps = this->_io_caps; gapSecParams.oob = false; @@ -1110,7 +1062,6 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ Serial.println(F("Storing bond data")); #endif this->_bondStore.putData(this->_bondData, 0, sizeof(this->_bondData)); - // this->_bondStore.saveTempData(this->_bondData, 0, sizeof(this->_bondData)); } uint8_t currentPeripheral; @@ -1142,16 +1093,16 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ } } } - if(this->_lesc == 2 && this->_userConfirm){ - for(int i = 0; i < _allowedPeripherals; i++){ - if(this->_connectionHandle[i] == bleEvt->evt.gap_evt.conn_handle){ - sd_ble_gap_auth_key_reply(this->_connectionHandle[i], BLE_GAP_AUTH_KEY_TYPE_PASSKEY, NULL); - /* Due to DRGN-7235, dhkey_reply() must come after auth_key_reply() */ - sd_ble_gap_lesc_dhkey_reply(this->_connectionHandle[i], &_dhkey); - break; - } - } - } + // if(this->_lesc == 2 && this->_userConfirm){ + // for(int i = 0; i < _allowedPeripherals; i++){ + // if(this->_connectionHandle[i] == bleEvt->evt.gap_evt.conn_handle){ + // sd_ble_gap_auth_key_reply(this->_connectionHandle[i], BLE_GAP_AUTH_KEY_TYPE_PASSKEY, NULL); + // /* Due to DRGN-7235, dhkey_reply() must come after auth_key_reply() */ + // sd_ble_gap_lesc_dhkey_reply(this->_connectionHandle[i], &_dhkey); + // break; + // } + // } + // } break; case BLE_GAP_EVT_AUTH_KEY_REQUEST: @@ -1166,20 +1117,6 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ } } break; - - case BLE_GAP_EVT_LESC_DHKEY_REQUEST: - ecc_p256_shared_secret_compute(&this->_privateKey.pk[0], &bleEvt->evt.gap_evt.params.lesc_dhkey_request.p_pk_peer->pk[0], &this->_dhkey.key[0]); - if(this->_lesc == 1 || this->_lesc == 3){ - for(int i = 0; i < _allowedPeripherals; i++){ - if(this->_connectionHandle[i] == bleEvt->evt.gap_evt.conn_handle){ - sd_ble_gap_auth_key_reply(this->_connectionHandle[i], BLE_GAP_AUTH_KEY_TYPE_PASSKEY, NULL); - /* Due to DRGN-7235, dhkey_reply() must come after auth_key_reply() */ - sd_ble_gap_lesc_dhkey_reply(this->_connectionHandle[i], &_dhkey); - break; - } - } - } - break; default: #ifdef BLE_CENTRAL_DEBUG diff --git a/libraries/BLE/BLECentralRole.h b/libraries/BLE/BLECentralRole.h index e3c0162..a509355 100644 --- a/libraries/BLE/BLECentralRole.h +++ b/libraries/BLE/BLECentralRole.h @@ -174,7 +174,6 @@ class BLECentralRole : public BLECharacteristicValueChangeListener, uint8_t _bondData[((sizeof(ble_gap_enc_key_t) + 3) / 4) * 4] __attribute__ ((__aligned__(4))); ble_gap_enc_key_t* _encKey; bool _mitm; - uint8_t _lesc; uint8_t _io_caps; uint8_t _passkey[6]; bool _userConfirm; @@ -195,11 +194,6 @@ class BLECentralRole : public BLECharacteristicValueChangeListener, unsigned char _numRemoteCharacteristics; struct remoteCharacteristicInfo* _remoteCharacteristicInfo; bool _remoteRequestInProgress; - - __ALIGN(4) ble_gap_lesc_p256_pk_t _privateKey; - __ALIGN(4) ble_gap_lesc_p256_pk_t _publicKey; - __ALIGN(4) ble_gap_lesc_p256_pk_t _peerKey; - __ALIGN(4) ble_gap_lesc_dhkey_t _dhkey; char* _hci_messages[63] = {"Success", "Unknow btle command", "Unknow connection identifier", "", "", "Authentication failure", "Pin or key missing", "Memory capacity exceeded", "Connection timeout", "", "", "", "Command disallowed", "", "", "", "", "", "Invalid btle command parameters", "Remote user terminated connection", "Remote dev termination due to low resources", "Remote dev termination due to power off", "Local host terminated connection", "", "", "", "Unsupported remote feature", "", "", "", "Invalid lmp parameters", "Unspecified error", "", "", "Lmp response timeout", "", "Lmp pdu not allowed", "", "", "", "Instant passed", "Pairing with unit key unsupported", "Different transaction collision", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Controller busy", "Connection interval unacceptable", "Directed advertiser timeout", "Connection terminated due mic failure", "Connection failed to be established" }; char* _gap_sec_status[256] = {"Success", "Timeout", "Pdu invalid", "Rfu range1 begin", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Rfu range1 end", "Passkey entry failed", "Oob not available", "Auth requested", "Confirm value", "Pairing not supported", "Enc key size", "Smp cmd unsupported", "Unspecified", "Repeated attempts", "Invalid params", "Dhkey failure", "Numeric comparison failure", "Bd Edr in prog", "X trans key disallowed", "Rfu range2 begin", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Rfu range2 end"}; diff --git a/libraries/BLE/BLEDevice.cpp b/libraries/BLE/BLEDevice.cpp index 7d17419..f034204 100644 --- a/libraries/BLE/BLEDevice.cpp +++ b/libraries/BLE/BLEDevice.cpp @@ -18,7 +18,6 @@ BLEDevice::BLEDevice() : _bondStore(NULL), _eventListener(NULL), _mitm(false), - _lesc(0), _io_caps(BLE_GAP_IO_CAPS_NONE), _passkey({0,0,0,0,0,0}), _status(1), diff --git a/libraries/BLE/BLEDevice.h b/libraries/BLE/BLEDevice.h index af64dcf..2b215b3 100644 --- a/libraries/BLE/BLEDevice.h +++ b/libraries/BLE/BLEDevice.h @@ -113,7 +113,6 @@ class BLEDevice unsigned short _maximumConnectionInterval; bool _connectable; bool _mitm; - uint8_t _lesc; uint8_t _io_caps; uint8_t _passkey[6]; bool _userConfirm; diff --git a/libraries/BLE/BLEPeripheral.cpp b/libraries/BLE/BLEPeripheral.cpp index 99e96d3..ae981cc 100644 --- a/libraries/BLE/BLEPeripheral.cpp +++ b/libraries/BLE/BLEPeripheral.cpp @@ -169,39 +169,13 @@ void BLEPeripheral::enableBond(BLEBondingType type){ case DISPLAY_PASSKEY: this->_device->_mitm = true; this->_device->_io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; - this->_device->_lesc = 0; setSecParams(1, 1, 0, BLE_GAP_IO_CAPS_DISPLAY_ONLY); break; case CONFIRM_PASSKEY: this->_device->_mitm = true; this->_device->_io_caps = BLE_GAP_IO_CAPS_KEYBOARD_ONLY; - this->_device->_lesc = 0; setSecParams(1, 1, 0, BLE_GAP_IO_CAPS_KEYBOARD_ONLY); break; - case LESC: - this->_device->_mitm = false; - this->_device->_io_caps = BLE_GAP_IO_CAPS_NONE; - this->_device->_lesc = 1; - setSecParams(1, 1, 1, BLE_GAP_IO_CAPS_NONE); - break; - case LESC_NUM_COMPARISON: - this->_device->_mitm = true; - this->_device->_io_caps = BLE_GAP_IO_CAPS_DISPLAY_YESNO; - this->_device->_lesc = 2; - setSecParams(1, 1, 1, BLE_GAP_IO_CAPS_DISPLAY_YESNO); - break; - case LESC_DISPLAY_PASSKEY: - this->_device->_mitm = true; - this->_device->_io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; - this->_device->_lesc = 3; - setSecParams(1, 1, 1, BLE_GAP_IO_CAPS_DISPLAY_ONLY); - break; - case LESC_CONFIRM_PASSKEY: - this->_device->_mitm = true; - this->_device->_io_caps = BLE_GAP_IO_CAPS_KEYBOARD_ONLY; - this->_device->_lesc = 3; - setSecParams(1, 1, 1, BLE_GAP_IO_CAPS_KEYBOARD_ONLY); - break; default: break; diff --git a/libraries/BLE/BLEPeripheral.h b/libraries/BLE/BLEPeripheral.h index 492a5a0..c32fcca 100644 --- a/libraries/BLE/BLEPeripheral.h +++ b/libraries/BLE/BLEPeripheral.h @@ -55,11 +55,7 @@ enum BLEPeripheralEvent { enum BLEBondingType { JUST_WORKS = 0, DISPLAY_PASSKEY = 1, - CONFIRM_PASSKEY = 2, - LESC = 3, - LESC_NUM_COMPARISON = 4, - LESC_DISPLAY_PASSKEY = 5, - LESC_CONFIRM_PASSKEY = 6 + CONFIRM_PASSKEY = 2 }; enum BLEStatus { diff --git a/libraries/BLE/examples/Central/Bonding/enterPasskeyCentral/enterPasskeyCentral.ino b/libraries/BLE/examples/Central/Bonding/enterPasskeyCentral/enterPasskeyCentral.ino index 0046496..74dff45 100644 --- a/libraries/BLE/examples/Central/Bonding/enterPasskeyCentral/enterPasskeyCentral.ino +++ b/libraries/BLE/examples/Central/Bonding/enterPasskeyCentral/enterPasskeyCentral.ino @@ -38,7 +38,7 @@ void setup() { bleCentral.addRemoteAttribute(dummyRemoteCharacteristic); // enable bonding and set the type - bleCentral.enableBond(LESC_CONFIRM_PASSKEY); + bleCentral.enableBond(CONFIRM_PASSKEY); // assign event handlers bleCentral.setEventHandler(BLEScanReceived, receiveAdvPck); diff --git a/libraries/BLE/examples/Central/Bonding/numericComparisonCentral/numericComparisonCentral.ino b/libraries/BLE/examples/Central/Bonding/numericComparisonCentral/numericComparisonCentral.ino deleted file mode 100644 index 7dd8927..0000000 --- a/libraries/BLE/examples/Central/Bonding/numericComparisonCentral/numericComparisonCentral.ino +++ /dev/null @@ -1,133 +0,0 @@ -/* numericComparisonCentral.ino - - Written by Chiara Ruggeri (chiara@arduino.org) - - This example shows how to enable bonding features on BLECentral module. - To know all the possible bonding types please refer to the documentation. - - When passkey is displayed, press USER1_BUTTON on the board to confirm the - value. Note that the value has to be confirmed on the both of the devices - to bond them. - Use the complementary example numericComparison.ino in File->Examples->BLE->Peripheral->Bonding - to test this feature. - - In this example BLE_LED shows the status of the board. It will blink every 200 ms when the board is scanning. - It will be on when the board is connected to a peripheral. It will be off when the board is disconnected. - - This example code is in the public domain. -*/ - -#include - -#define CONFIRM_BUTTON USER1_BUTTON -// create central instance -BLECentralRole bleCentral = BLECentralRole(); - -// create remote service -BLERemoteService dummyRemoteService = BLERemoteService("19b10010e8f2537e4f6cd104768a1214"); - -// create remote characteristics -BLERemoteCharacteristic dummyRemoteCharacteristic = BLERemoteCharacteristic("19b10011e8f2537e4f6cd104768a1214", BLERead | BLEWrite); - - - -void setup() { - Serial.begin(9600); - - //initialize BLE led - pinMode(BLE_LED, OUTPUT); - - // initialize button - pinMode(CONFIRM_BUTTON, INPUT); - - // add service and characteristic - bleCentral.addRemoteAttribute(dummyRemoteService); - bleCentral.addRemoteAttribute(dummyRemoteCharacteristic); - - // enable bonding and set the type - bleCentral.enableBond(LESC_NUM_COMPARISON); - - // assign event handlers - bleCentral.setEventHandler(BLEScanReceived, receiveAdvPck); - bleCentral.setEventHandler(BLEConnected, bleCentralConnectHandler); - bleCentral.setEventHandler(BLEDisconnected, bleCentralDisconnectHandler); - bleCentral.setEventHandler(BLEPasskeyReceived, showPasskey); - bleCentral.setEventHandler(BLEBonded, bond); - - // use BLEMessage event handler to retrieve information about internal BLE status - bleCentral.setEventHandler(BLEMessage, receiveMessage); - - // begin initialization - bleCentral.begin(); - - Serial.println(F("BLE Bonding example")); -} - -void loop() { - // Handle BLE led - blinkOnScan(); -} - -void blinkOnScan(){ - // retrieve the central status in order to blink only when scanning - if(bleCentral.status() == SCANNING){ - digitalWrite(BLE_LED, LOW); - delay(200); - digitalWrite(BLE_LED, HIGH); - delay(200); - } -} - -void receiveAdvPck(BLEPeripheralPeer& peer){ - char advertisedName[31]; - byte len; - // search for a device that advertises "BONDExample" name - peer.getFieldInAdvPck(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, advertisedName, len); - if(len == 0) // field not found - peer.getFieldInAdvPck(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, advertisedName, len); - if(len != 0){ // the field was found - Serial.println(advertisedName); - if(!strcmp(advertisedName, "BONDExample")) - // Name found. Connect to the peripheral - bleCentral.connect(peer); - } -} - -void bleCentralConnectHandler(BLEPeripheralPeer& peer) { - // peer connected event handler - Serial.print("Connected event, peripheral: "); - Serial.println(peer.address()); - // turn BLE_LED on - digitalWrite(BLE_LED, HIGH); -} - -void bleCentralDisconnectHandler(BLEPeripheralPeer& peer) { - // peer disconnected event handler - Serial.print("Disconnected event, peripheral: "); - Serial.println(peer.address()); - // turn BLE_LED off - digitalWrite(BLE_LED, LOW); -} - -void showPasskey(BLEPeripheralPeer& peer) { - // passkey generated event handler - Serial.print("Press the button to confirm the received passkey: "); - Serial.println(bleCentral.getPasskey()); - - bool confirm = false; - while(confirm == false){ - if(digitalRead(CONFIRM_BUTTON) == 0) // button pressed! - confirm = true; - } - - bleCentral.confirmPasskey(true); -} - -void bond(BLEPeripheralPeer& peer) { - // central bonded event handler - Serial.println("Bonded"); -} - -void receiveMessage(int evtCode, int messageCode){ - bleCentral.printBleMessage(evtCode, messageCode); -} \ No newline at end of file diff --git a/libraries/BLE/examples/Peripheral/Bonding/numericComparison/numericComparison.ino b/libraries/BLE/examples/Peripheral/Bonding/numericComparison/numericComparison.ino deleted file mode 100644 index 7ba87cf..0000000 --- a/libraries/BLE/examples/Peripheral/Bonding/numericComparison/numericComparison.ino +++ /dev/null @@ -1,116 +0,0 @@ -/* numericComparison.ino - - Written by Chiara Ruggeri (chiara@arduino.org) - - This example shows how to enable bonding features on BLEPeripheral module. - To know all the possible bonding types please refer to the documentation. - - When passkey is displayed, press USER1_BUTTON on the board to confirm the - value. Note that the value has to be confirmed on the both of the devices - to bond them. - Use the complementary example numericComparisonCentral.ino in File->Examples->BLE->Central->Bonding - to test this feature. - In this example BLE_LED shows the status of the board. It will blink every 200 ms when the board is advertising. - It will be on when the board is connected to a central. It will be off when the board is disconnected. - - This example code is in the public domain. -*/ - -#include - -#define CONFIRM_BUTTON USER1_BUTTON - -// create peripheral instance -BLEPeripheral blePeripheral = BLEPeripheral(); - -// create service -BLEService dummyService = BLEService("19b10000e8f2537e4f6cd104768a1214"); - -// create characteristic -BLECharCharacteristic dummyCharacteristic = BLECharCharacteristic("19b10001e8f2537e4f6cd104768a1214", BLERead | BLEWrite); - -void setup() { - Serial.begin(9600); - - //initialize BLE led - pinMode(BLE_LED, OUTPUT); - - // initialize button - pinMode(CONFIRM_BUTTON, INPUT); - - // set advertised local name - blePeripheral.setLocalName("BONDExample"); - - // add service and characteristic - blePeripheral.addAttribute(dummyService); - blePeripheral.addAttribute(dummyCharacteristic); - - // enable bonding and set the type - blePeripheral.enableBond(LESC_NUM_COMPARISON); - - // assign event handlers for connected, disconnected to peripheral - blePeripheral.setEventHandler(BLEConnected, blePeripheralConnectHandler); - blePeripheral.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler); - blePeripheral.setEventHandler(BLEPasskeyReceived, showPasskey); - blePeripheral.setEventHandler(BLEBonded, bond); - - // use BLEMessage event handler to retrieve information about internal BLE status - blePeripheral.setEventHandler(BLEMessage, receiveMessage); - - // begin initialization - blePeripheral.begin(); - - Serial.println(F("BLE LED Peripheral")); -} - -void loop() { - // blink if the board is advertising - blinkOnAdv(); -} - -void blinkOnAdv(){ - // retrieve the peripheral status in order to blink only when advertising - if(blePeripheral.status() == ADVERTISING){ - digitalWrite(BLE_LED, LOW); - delay(200); - digitalWrite(BLE_LED, HIGH); - delay(200); - } -} - -void showPasskey(BLECentral& central) { - Serial.print("Press the button to confirm the received passkey: "); - Serial.println(blePeripheral.getPasskey()); - - bool confirm = false; - while(confirm == false){ - if(digitalRead(CONFIRM_BUTTON) == 0) // button pressed! - confirm = true; - } - - blePeripheral.confirmPasskey(confirm); -} - -void bond(BLECentral& central) { - Serial.println("Bonded"); -} - -void blePeripheralConnectHandler(BLECentral& central) { - // central connected event handler - Serial.print(F("Connected event, central: ")); - Serial.println(central.address()); - // turn BLE_LED on - digitalWrite(BLE_LED, HIGH); -} - -void blePeripheralDisconnectHandler(BLECentral& central) { - // central disconnected event handler - Serial.print(F("Disconnected event, central: ")); - Serial.println(central.address()); - // turn BLE_LED off - digitalWrite(BLE_LED, LOW); -} - -void receiveMessage(int evtCode, int messageCode){ - blePeripheral.printBleMessage(evtCode, messageCode); -} \ No newline at end of file diff --git a/libraries/BLE/examples/Peripheral/Bonding/showPasskey/showPasskey.ino b/libraries/BLE/examples/Peripheral/Bonding/showPasskey/showPasskey.ino index ab8b793..1ca2bc2 100644 --- a/libraries/BLE/examples/Peripheral/Bonding/showPasskey/showPasskey.ino +++ b/libraries/BLE/examples/Peripheral/Bonding/showPasskey/showPasskey.ino @@ -40,7 +40,7 @@ void setup() { blePeripheral.addAttribute(dummyCharacteristic); //enable bonding and set the type - blePeripheral.enableBond(LESC_DISPLAY_PASSKEY); + blePeripheral.enableBond(DISPLAY_PASSKEY); // assign event handlers for connected, disconnected, passkey received and bond to peripheral blePeripheral.setEventHandler(BLEConnected, blePeripheralConnectHandler); diff --git a/libraries/BLE/nRF51822.cpp b/libraries/BLE/nRF51822.cpp index 44f001e..d13d09d 100644 --- a/libraries/BLE/nRF51822.cpp +++ b/libraries/BLE/nRF51822.cpp @@ -11,15 +11,7 @@ #elif defined(NRF5) || defined(NRF51_S130) #include #include - #include - #ifdef __cplusplus - extern "C"{ - #endif - #include - #ifdef __cplusplus - } - #endif - + #include #else #include #include @@ -300,10 +292,7 @@ void nRF51822::begin(unsigned char advertisementDataSize, if (properties & (BLERead | BLENotify | BLEIndicate)) { if (this->_bondStore && !this->_bondStore->hasData()) { - if(this->_lesc > 1) - BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&characteristicValueAttributeMetaData.read_perm); - else - BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&characteristicValueAttributeMetaData.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&characteristicValueAttributeMetaData.read_perm); } else { BLE_GAP_CONN_SEC_MODE_SET_OPEN(&characteristicValueAttributeMetaData.read_perm); } @@ -311,10 +300,7 @@ void nRF51822::begin(unsigned char advertisementDataSize, if (properties & (BLEWriteWithoutResponse | BLEWrite)) { if (this->_bondStore && !this->_bondStore->hasData()) { - if(this->_lesc > 1) - BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&characteristicValueAttributeMetaData.write_perm); - else - BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&characteristicValueAttributeMetaData.write_perm); + BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&characteristicValueAttributeMetaData.write_perm); } else { BLE_GAP_CONN_SEC_MODE_SET_OPEN(&characteristicValueAttributeMetaData.write_perm); } @@ -386,10 +372,7 @@ void nRF51822::begin(unsigned char advertisementDataSize, descriptorMetaData.vlen = (valueLength == descriptor->valueLength()) ? 0 : 1; if (this->_bondStore && !this->_bondStore->hasData()) { - if(this->_lesc > 1) - BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(&descriptorMetaData.read_perm); - else - BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&descriptorMetaData.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&descriptorMetaData.read_perm); } else { BLE_GAP_CONN_SEC_MODE_SET_OPEN(&descriptorMetaData.read_perm); } @@ -616,164 +599,6 @@ void nRF51822::poll(ble_evt_t *bleEvt) { #endif break; - // case BLE_GAP_EVT_SEC_PARAMS_REQUEST: -// #ifdef NRF_51822_DEBUG - // Serial.print(F("Evt Sec Params Request ")); -// #if !defined(NRF51_S130) && !defined(S110) - // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.timeout); - // Serial.print(F(" ")); -// #endif - // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.bond); - // Serial.print(F(" ")); - // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.mitm); - // Serial.print(F(" ")); - // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.io_caps); - // Serial.print(F(" ")); - // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.oob); - // Serial.print(F(" ")); - // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.min_key_size); - // Serial.print(F(" ")); - // Serial.print(bleEvt->evt.gap_evt.params.sec_params_request.peer_params.max_key_size); - // Serial.println(); -// #endif - // if (this->_bondStore && !this->_bondStore->hasData()) { - // only allow bonding if bond store exists and there is no data - - // ble_gap_sec_params_t gapSecParams; - - // memset(&gapSecParams, 0x00, sizeof(ble_gap_sec_params_t)); - -// #if defined(NRF5) && !defined(S110) - // gapSecParams.kdist_own.enc = 1; -// #elif defined(NRF51_S130) - // gapSecParams.kdist_periph.enc = 1; -// #elif !defined(NRF5) - // gapSecParams.timeout = 30; // must be 30s -// #endif - // gapSecParams.bond = true; - // gapSecParams.lesc = (bool)this->_lesc; - // gapSecParams.mitm = this->_mitm; - // gapSecParams.io_caps = this->_io_caps; - // gapSecParams.oob = false; - // gapSecParams.min_key_size = 7; - // gapSecParams.max_key_size = 16; - -// #if defined(NRF5) && !defined(S110) - // ble_gap_sec_keyset_t keyset; - // memset(&keyset, 0, sizeof(ble_gap_sec_keyset_t)); - // if(this->_lesc > 0){ - // ecc_init(); - // ecc_p256_keypair_gen(this->_privateKey.pk, this->_publicKey.pk); - // keyset.keys_own.p_pk=&this->_publicKey; - // keyset.keys_peer.p_pk=&this->_peerKey; - // } - // keyset.keys_peer.p_enc_key = NULL; - // keyset.keys_peer.p_id_key = NULL; - // keyset.keys_peer.p_sign_key = NULL; - // keyset.keys_own.p_enc_key = this->_encKey; - // keyset.keys_own.p_id_key = NULL; - // keyset.keys_own.p_sign_key = NULL; - - // sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams, &keyset); -// #elif defined(NRF51_S130) || defined(S110) - // ble_gap_sec_keyset_t keyset; - - // keyset.keys_central.p_enc_key = NULL; - // keyset.keys_central.p_id_key = NULL; - // keyset.keys_central.p_sign_key = NULL; - // keyset.keys_periph.p_enc_key = this->_encKey; - // keyset.keys_periph.p_id_key = NULL; - // keyset.keys_periph.p_sign_key = NULL; - - // sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams, &keyset); -// #else - // sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_SUCCESS, &gapSecParams); -// #endif - // } else { -// #if defined(NRF5) || defined(NRF51_S130) - // sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); -// #else - // sd_ble_gap_sec_params_reply(this->_connectionHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL); -// #endif - // } - // break; - -// case BLE_GAP_EVT_SEC_INFO_REQUEST: -//#ifdef NRF_51822_DEBUG -// Serial.print(F("Evt Sec Info Request ")); -// // Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.peer_addr); -// // Serial.print(F(" ")); -//#if defined(NRF5) || defined(NRF51_S130) -// Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.master_id.ediv); -//#else -// Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.div); -//#endif -// Serial.print(F(" ")); -// Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.enc_info); -// Serial.print(F(" ")); -// Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.id_info); -// Serial.print(F(" ")); -// Serial.print(bleEvt->evt.gap_evt.params.sec_info_request.sign_info); -// Serial.println(); -//#endif -//#if defined(NRF5) || defined(NRF51_S130) -// if (this->_encKey->master_id.ediv == bleEvt->evt.gap_evt.params.sec_info_request.master_id.ediv) { -// sd_ble_gap_sec_info_reply(this->_connectionHandle, &this->_encKey->enc_info, NULL, NULL); -// } else { -// sd_ble_gap_sec_info_reply(this->_connectionHandle, NULL, NULL, NULL); -// } -//#else -// if (this->_authStatus->periph_keys.enc_info.div == bleEvt->evt.gap_evt.params.sec_info_request.div) { -// sd_ble_gap_sec_info_reply(this->_connectionHandle, &this->_authStatus->periph_keys.enc_info, NULL); -// } else { -// sd_ble_gap_sec_info_reply(this->_connectionHandle, NULL, NULL); -// } -//#endif -// break; - -// case BLE_GAP_EVT_AUTH_STATUS: -//#ifdef NRF_51822_DEBUG -// Serial.println(F("Evt Auth Status")); -// Serial.println(bleEvt->evt.gap_evt.params.auth_status.auth_status); -//#endif -// if (BLE_GAP_SEC_STATUS_SUCCESS == bleEvt->evt.gap_evt.params.auth_status.auth_status) { -//#if !defined(NRF5) && !defined(NRF51_S130) -// *this->_authStatus = bleEvt->evt.gap_evt.params.auth_status; -//#endif -// if (this->_bondStore) { -//#ifdef NRF_51822_DEBUG -// Serial.println(F("Storing bond data")); -//#endif -//#if defined(NRF5) || defined(NRF51_S130) -// this->_bondStore->saveTempData(this->_bondData, 0, sizeof(this->_bondData)); -//#else -// this->_bondStore->putData(this->_authStatusBuffer, 0, sizeof(this->_authStatusBuffer)); -//#endif -// } - -// if (this->_eventListener) { -// this->_eventListener->BLEDeviceBonded(*this); -// } -// } -// else{ -// if (this->_eventListener) { -// this->_eventListener->BLEMessageReceived(*this, AUTH_STATUS, bleEvt->evt.gap_evt.params.auth_status.auth_status); -// } -// } -// break; - -// case BLE_GAP_EVT_CONN_SEC_UPDATE: -//#ifdef NRF_51822_DEBUG -// Serial.print(F("Evt Conn Sec Update ")); -// Serial.print(bleEvt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.sm); -// Serial.print(F(" ")); -// Serial.print(bleEvt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv); -// Serial.print(F(" ")); -// Serial.print(bleEvt->evt.gap_evt.params.conn_sec_update.conn_sec.encr_key_size); -// Serial.println(); -//#endif -// break; - case BLE_GATTS_EVT_WRITE: { #ifdef NRF_51822_DEBUG Serial.print(F("Evt Write, handle = ")); @@ -936,7 +761,7 @@ void nRF51822::poll(ble_evt_t *bleEvt) { gapSecParams.timeout = 30; // must be 30s #endif gapSecParams.bond = true; - gapSecParams.lesc = (bool)this->_lesc; + gapSecParams.lesc = false; gapSecParams.mitm = this->_mitm; gapSecParams.io_caps = this->_io_caps; gapSecParams.oob = false; @@ -986,7 +811,7 @@ void nRF51822::poll(ble_evt_t *bleEvt) { gapSecParams.timeout = 30; // must be 30s #endif gapSecParams.bond = true; - gapSecParams.lesc = (bool)this->_lesc; + gapSecParams.lesc = false; gapSecParams.mitm = this->_mitm; gapSecParams.io_caps = this->_io_caps; gapSecParams.oob = false; @@ -1030,11 +855,11 @@ void nRF51822::poll(ble_evt_t *bleEvt) { if (this->_eventListener) { this->_eventListener->BLEDevicePasskeyReceived(*this); } - if(this->_lesc == 2 && this->_userConfirm){ - sd_ble_gap_auth_key_reply(this->_connectionHandle, BLE_GAP_AUTH_KEY_TYPE_PASSKEY, NULL); - /* Due to DRGN-7235, dhkey_reply() must come after auth_key_reply() */ - sd_ble_gap_lesc_dhkey_reply(this->_connectionHandle, &_dhkey); - } +// if(this->_lesc == 2 && this->_userConfirm){ +// sd_ble_gap_auth_key_reply(this->_connectionHandle, BLE_GAP_AUTH_KEY_TYPE_PASSKEY, NULL); +// /* Due to DRGN-7235, dhkey_reply() must come after auth_key_reply() */ +// sd_ble_gap_lesc_dhkey_reply(this->_connectionHandle, &_dhkey); +// } break; case BLE_GAP_EVT_AUTH_KEY_REQUEST: @@ -1043,15 +868,6 @@ void nRF51822::poll(ble_evt_t *bleEvt) { } break; - case BLE_GAP_EVT_LESC_DHKEY_REQUEST: - ecc_p256_shared_secret_compute(&this->_privateKey.pk[0], &bleEvt->evt.gap_evt.params.lesc_dhkey_request.p_pk_peer->pk[0], &this->_dhkey.key[0]); - if(this->_lesc == 1 || this->_lesc == 3){ - sd_ble_gap_auth_key_reply(this->_connectionHandle, BLE_GAP_AUTH_KEY_TYPE_PASSKEY, NULL); - /* Due to DRGN-7235, dhkey_reply() must come after auth_key_reply() */ - sd_ble_gap_lesc_dhkey_reply(this->_connectionHandle, &_dhkey); - } - break; - default: #ifdef NRF_51822_DEBUG Serial.print(F("bleEvt->header.evt_id = 0x")); diff --git a/libraries/BLE/nRF51822.h b/libraries/BLE/nRF51822.h index 48ea3a0..bdf3631 100644 --- a/libraries/BLE/nRF51822.h +++ b/libraries/BLE/nRF51822.h @@ -113,10 +113,6 @@ class nRF51822 : public BLEDevice struct remoteCharacteristicInfo* _remoteCharacteristicInfo; bool _remoteRequestInProgress; - __ALIGN(4) ble_gap_lesc_p256_pk_t _privateKey; - __ALIGN(4) ble_gap_lesc_p256_pk_t _publicKey; - __ALIGN(4) ble_gap_lesc_p256_pk_t _peerKey; - __ALIGN(4) ble_gap_lesc_dhkey_t _dhkey; }; #endif From 49240134cb1ace913b2be8f05fc435fa68d78885 Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Tue, 13 Jun 2017 11:20:33 +0200 Subject: [PATCH 06/13] fixed bug in retrieving role from central events. Added little fixes in BondStore class --- cores/arduino/DFUService.cpp | 3 +-- libraries/BLE/BLEBondStore.cpp | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/cores/arduino/DFUService.cpp b/cores/arduino/DFUService.cpp index 10493e6..152f4e1 100644 --- a/cores/arduino/DFUService.cpp +++ b/cores/arduino/DFUService.cpp @@ -101,8 +101,7 @@ static void reset_prepare(void) void ble_evt_dispatch(ble_evt_t * p_ble_evt) { uint16_t handler = p_ble_evt->evt.gap_evt.conn_handle; - uint16_t role = ble_conn_state_role(handler); - + uint16_t role = p_ble_evt->evt.gap_evt.params.connected.role; if(dfuService){ //DFU service works with peripheral role, don't forward events central related if(role != BLE_GAP_ROLE_CENTRAL){ diff --git a/libraries/BLE/BLEBondStore.cpp b/libraries/BLE/BLEBondStore.cpp index ff605cb..2e93692 100644 --- a/libraries/BLE/BLEBondStore.cpp +++ b/libraries/BLE/BLEBondStore.cpp @@ -18,6 +18,17 @@ #include "BLEBondStore.h" +#ifdef __cplusplus +extern "C"{ +#endif //__cplusplus + +#include "nrf_delay.h" + +#ifdef __cplusplus +} +#endif //__cplusplus + + BLEBondStore::BLEBondStore(int offset) : #if defined(__AVR__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) _offset(offset), @@ -28,10 +39,10 @@ BLEBondStore::BLEBondStore(int offset) : _tempOffset(0), _tempLength(0) { - //offset takes into account the bootloader address in order to save data - //between end of the application and begin of bootloader. - // Get the bootloader starting address (in number of pages) - offset = offset / NRF_FICR->CODEPAGESIZE; + //by default offset is the bootloader address + //if it's null use the end of the flash + //otherwise save the data between the end of the sketch zone and the bootloader + offset = (offset != 0xFFFFFFFF) ? (offset / NRF_FICR->CODEPAGESIZE) : NRF_FICR->CODESIZE; // Offset must be: end_of_flash - bootloader_address offset = NRF_FICR->CODESIZE - offset; //save bond data at the second page before the end of the saving zone since the last page is reserved to DFU bond data (if used) @@ -53,8 +64,8 @@ void BLEBondStore::clearData() { eeprom_write_byte((unsigned char *)this->_offset, 0x00); #elif defined(NRF51) || defined(NRF52) int32_t pageNo = (uint32_t)_flashPageStartAddress / NRF_FICR->CODEPAGESIZE; -uint32_t err_code; - while(err_code = sd_flash_page_erase(pageNo) == NRF_ERROR_BUSY); + while(sd_flash_page_erase(pageNo) == NRF_ERROR_BUSY); + nrf_delay_ms(100); #elif defined(__RFduino__) // turn on flash erase enable @@ -88,6 +99,7 @@ void BLEBondStore::putData(const unsigned char* data, unsigned int offset, unsig this->clearData(); while (sd_flash_write((uint32_t*)_flashPageStartAddress, (uint32_t*)data, (uint32_t)length/4) == NRF_ERROR_BUSY); + nrf_delay_ms(100); #elif defined(__RFduino__) // ignores offset this->clearData(); From 5b5781e1d5cd273abea4843ad357f77d3349d75e Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Tue, 13 Jun 2017 12:36:22 +0200 Subject: [PATCH 07/13] Added double check on events' role to avoid any issue --- cores/arduino/DFUService.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cores/arduino/DFUService.cpp b/cores/arduino/DFUService.cpp index 152f4e1..150f634 100644 --- a/cores/arduino/DFUService.cpp +++ b/cores/arduino/DFUService.cpp @@ -101,10 +101,10 @@ static void reset_prepare(void) void ble_evt_dispatch(ble_evt_t * p_ble_evt) { uint16_t handler = p_ble_evt->evt.gap_evt.conn_handle; - uint16_t role = p_ble_evt->evt.gap_evt.params.connected.role; + uint16_t role = ble_conn_state_role(handler); if(dfuService){ //DFU service works with peripheral role, don't forward events central related - if(role != BLE_GAP_ROLE_CENTRAL){ + if(role != BLE_GAP_ROLE_CENTRAL && p_ble_evt->evt.gap_evt.params.connected.role != BLE_GAP_ROLE_CENTRAL){ dm_ble_evt_handler(p_ble_evt); ble_dfu_on_ble_evt(&m_dfus, p_ble_evt); on_ble_evt(p_ble_evt); From ec4a6c8e30f256e7b7bed9647d7ff5aeca122939 Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Tue, 13 Jun 2017 13:15:48 +0200 Subject: [PATCH 08/13] Added licence in new SDK files and removed unused ECC files --- .../ble/ble_advertising/ble_advertising.c | 32 +- .../ble/ble_advertising/ble_advertising.h | 36 +- .../ble/ble_services/ble_dfu/ble_dfu.c | 31 +- .../ble/ble_services/ble_dfu/ble_dfu.h | 31 +- .../config/device_manager_cnfg.h | 30 +- .../ble/device_manager/device_manager.h | 36 +- .../device_manager_peripheral.c | 218 ++++++----- .../pstorage/config/pstorage_platform.h | 31 +- .../drivers_nrf/pstorage/pstorage.c | 31 +- .../drivers_nrf/pstorage/pstorage.h | 37 +- .../bootloader_dfu/bootloader_types.h | 31 +- .../bootloader_dfu/bootloader_util.c | 31 +- .../bootloader_dfu/bootloader_util.h | 31 +- .../bootloader_dfu/dfu_app_handler.c | 31 +- .../bootloader_dfu/dfu_app_handler.h | 31 +- .../libraries/bootloader_dfu/dfu_ble_svc.h | 31 +- cores/arduino/components/libraries/ecc/ecc.c | 143 ------- cores/arduino/components/libraries/ecc/ecc.h | 82 ---- .../experimental_section_vars/section_vars.h | 31 +- .../fstorage/config/fstorage_config.h | 31 +- .../components/libraries/fstorage/fstorage.c | 31 +- .../components/libraries/fstorage/fstorage.h | 37 +- .../fstorage/fstorage_internal_defs.h | 31 +- libraries/BLE/utility/uECC/LICENSE.txt | 21 - .../BLE/utility/uECC/micro_ecc_lib_nrf52.a | Bin 186030 -> 0 bytes libraries/BLE/utility/uECC/uECC.h | 362 ------------------ platform.txt | 4 +- 27 files changed, 607 insertions(+), 865 deletions(-) delete mode 100644 cores/arduino/components/libraries/ecc/ecc.c delete mode 100644 cores/arduino/components/libraries/ecc/ecc.h delete mode 100644 libraries/BLE/utility/uECC/LICENSE.txt delete mode 100644 libraries/BLE/utility/uECC/micro_ecc_lib_nrf52.a delete mode 100644 libraries/BLE/utility/uECC/uECC.h diff --git a/cores/arduino/components/ble/ble_advertising/ble_advertising.c b/cores/arduino/components/ble/ble_advertising/ble_advertising.c index 959ecb1..078a4cf 100644 --- a/cores/arduino/components/ble/ble_advertising/ble_advertising.c +++ b/cores/arduino/components/ble/ble_advertising/ble_advertising.c @@ -1,12 +1,30 @@ -/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ diff --git a/cores/arduino/components/ble/ble_advertising/ble_advertising.h b/cores/arduino/components/ble/ble_advertising/ble_advertising.h index 09605e5..eb0dafd 100644 --- a/cores/arduino/components/ble/ble_advertising/ble_advertising.h +++ b/cores/arduino/components/ble/ble_advertising/ble_advertising.h @@ -1,12 +1,30 @@ -/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ diff --git a/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.c b/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.c index 635d211..fd0a58a 100644 --- a/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.c +++ b/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.c @@ -1,13 +1,30 @@ -/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include "ble_dfu.h" diff --git a/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.h b/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.h index 906febc..ccc562d 100644 --- a/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.h +++ b/cores/arduino/components/ble/ble_services/ble_dfu/ble_dfu.h @@ -1,13 +1,30 @@ -/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ /**@file diff --git a/cores/arduino/components/ble/device_manager/config/device_manager_cnfg.h b/cores/arduino/components/ble/device_manager/config/device_manager_cnfg.h index 03d4300..9335a95 100644 --- a/cores/arduino/components/ble/device_manager/config/device_manager_cnfg.h +++ b/cores/arduino/components/ble/device_manager/config/device_manager_cnfg.h @@ -1,12 +1,30 @@ -/* Copyright (C) 2013 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ /** diff --git a/cores/arduino/components/ble/device_manager/device_manager.h b/cores/arduino/components/ble/device_manager/device_manager.h index 9aa465d..d19c57a 100644 --- a/cores/arduino/components/ble/device_manager/device_manager.h +++ b/cores/arduino/components/ble/device_manager/device_manager.h @@ -1,12 +1,30 @@ -/* Copyright (C) 2013 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. - * +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ /** diff --git a/cores/arduino/components/ble/device_manager/device_manager_peripheral.c b/cores/arduino/components/ble/device_manager/device_manager_peripheral.c index 145a7d7..96e2eca 100644 --- a/cores/arduino/components/ble/device_manager/device_manager_peripheral.c +++ b/cores/arduino/components/ble/device_manager/device_manager_peripheral.c @@ -1,12 +1,30 @@ -/* Copyright (C) 2013 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. - * +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include "device_manager.h" @@ -757,14 +775,14 @@ static __INLINE ret_code_t device_instance_free(uint32_t device_index) //Get the block handle. err_code = pstorage_block_identifier_get(&m_storage_handle, device_index, &block_handle); -dbgMsg("block identification = "); dbgMsgn(err_code); dbgMsg("\r\n"); + if (err_code == NRF_SUCCESS) { DM_TRC("[DM]:[DI 0x%02X]: Freeing Instance.\r\n", device_index); //Request clearing of the block. err_code = pstorage_clear(&block_handle, ALL_CONTEXT_SIZE); -dbgMsg("storage clear = "); dbgMsgn(err_code); dbgMsg("\r\n"); + if (err_code == NRF_SUCCESS) { peer_instance_init(device_index); @@ -954,10 +972,9 @@ static __INLINE void device_context_store(dm_handle_t const * p_handle, device_s } else if (state == FIRST_BOND_STORE) { - dbgMsg("[DM]:[DI %02X]:[CI %02X]: -> Storing bonding information.\r\n"); - dbgMsgn(p_handle->device_id); - dbgMsgn(p_handle->connection_id); -dbgMsg("\r\n"); + printf("[DM]:[DI %02X]:[CI %02X]: -> Storing bonding information.\r\n", + p_handle->device_id, p_handle->connection_id); + store_fn = pstorage_store; } else @@ -974,8 +991,11 @@ dbgMsg("\r\n"); (uint8_t *)&m_peer_table[p_handle->device_id], PEER_ID_SIZE, PEER_ID_STORAGE_OFFSET); -dbgMsg("bond info, src: "); dbgMsgn((uint8_t *)&m_peer_table[p_handle->device_id]); -dbgMsg(", size : "); dbgMsgn(PEER_ID_SIZE); dbgMsg(", offset : "); dbgMsgn(PEER_ID_STORAGE_OFFSET);dbgMsg("\r\n"); + + printf("bond info, src : %p, size : [DI %02X], offset : [DI %02X]\r\n", (uint8_t *)&m_peer_table[p_handle->device_id], + PEER_ID_SIZE, + PEER_ID_STORAGE_OFFSET); + if ((err_code == NRF_SUCCESS) && (state != UPDATE_PEER_ADDR)) { m_connection_table[p_handle->connection_id].state &= (~STATE_BOND_INFO_UPDATE); @@ -1941,23 +1961,19 @@ ret_code_t dm_device_add(dm_handle_t * p_handle, ret_code_t dm_device_delete(dm_handle_t const * p_handle) -{dbgMsg("INSIDE FUNCTION"); +{ VERIFY_MODULE_INITIALIZED(); - dbgMsg("Module initialized passed"); NULL_PARAM_CHECK(p_handle); - dbgMsg("null param check passed"); -// VERIFY_APP_REGISTERED(p_handle->appl_id); - dbgMsg("app registered passed"); -// VERIFY_DEVICE_INSTANCE(p_handle->device_id); - dbgMsg("device instance passed"); + VERIFY_APP_REGISTERED(p_handle->appl_id); + VERIFY_DEVICE_INSTANCE(p_handle->device_id); DM_MUTEX_LOCK(); - dbgMsg("[DM]: >> dm_device_delete\r\n"); + DM_TRC("[DM]: >> dm_device_delete\r\n"); uint32_t err_code = device_instance_free(p_handle->device_id); - dbgMsg("[DM]: << dm_device_delete\r\n"); + DM_TRC("[DM]: << dm_device_delete\r\n"); DM_MUTEX_UNLOCK(); @@ -2485,14 +2501,13 @@ void bond_data_load(dm_handle_t * p_handle) &block_handle); if (err_code == NRF_SUCCESS) { - dbgMsg( - "[DM]:");dbgMsgn(p_handle->connection_id); - dbgMsg(":[Block ID ");dbgMsgn(block_handle.block_id); - dbgMsg(":Loading bond information at "); - dbgMsgn(&m_bond_table[p_handle->connection_id]); - dbgMsg(", size "); dbgMsgn(BOND_SIZE); - dbgMsg(", offset ");dbgMsgn(BOND_STORAGE_OFFSET); - dbgMsg("\r\n"); + printf( + "[DM]:[%02X]:[Block ID 0x%08X]:Loading bond information at %p, size 0x%08X, offset 0x%08X.\r\n", + p_handle->connection_id, + block_handle.block_id, + &m_bond_table[p_handle->connection_id], + BOND_SIZE, + BOND_STORAGE_OFFSET); err_code = pstorage_load((uint8_t *)&m_bond_table[p_handle->connection_id], &block_handle, @@ -2501,17 +2516,17 @@ void bond_data_load(dm_handle_t * p_handle) if (err_code != NRF_SUCCESS) { - dbgMsg("[DM]:[%02X]: Failed to load Bond information, reason %08X\r\n"); - dbgMsgn(p_handle->connection_id); - dbgMsgn(err_code); - dbgMsg("\r\n"); + printf("[DM]:[%02X]: Failed to load Bond information, reason %08X\r\n", + p_handle->connection_id, + err_code); } - dbgMsg( - "[DM]:");dbgMsgn(p_handle->connection_id);dbgMsg(":Loading service context at "); - dbgMsgn(&m_gatts_table[p_handle->connection_id]);dbgMsg(", size "); - dbgMsgn(sizeof(dm_gatts_context_t));dbgMsg("offset "); - dbgMsgn(SERVICE_STORAGE_OFFSET);dbgMsg("\r\n"); + printf( + "[DM]:[%02X]:Loading service context at %p, size 0x%08X, offset 0x%08X.\r\n", + p_handle->connection_id, + &m_gatts_table[p_handle->connection_id], + sizeof(dm_gatts_context_t), + SERVICE_STORAGE_OFFSET); err_code = m_service_context_load[m_application_table[0].service]( &block_handle, @@ -2519,18 +2534,16 @@ void bond_data_load(dm_handle_t * p_handle) if (err_code != NRF_SUCCESS) { - dbgMsg( - "[DM]:[%02X]: Failed to load service information, reason %08X\r\n"); - dbgMsgn(p_handle->connection_id); - dbgMsgn(err_code); - dbgMsg("\r\n"); + printf( + "[DM]:[%02X]: Failed to load service information, reason %08X\r\n", + p_handle->connection_id, + err_code); } } else { - dbgMsg("[DM]:[%02X]: Failed to get block identifier for " - "device %08X, reason %08X "); dbgMsgn(p_handle->connection_id); dbgMsgn(p_handle->device_id); dbgMsgn(err_code); - dbgMsg("\r\n"); + printf("[DM]:[%02X]: Failed to get block identifier for " + "device %08X, reason %08X.\r\n", p_handle->connection_id, p_handle->device_id, err_code); } } @@ -2549,7 +2562,7 @@ void dm_ble_evt_handler(ble_evt_t * p_ble_evt) VERIFY_MODULE_INITIALIZED_VOID(); VERIFY_APP_REGISTERED_VOID(0); DM_MUTEX_LOCK(); - + err_code = dm_handle_initialize(&handle); APP_ERROR_CHECK(err_code); @@ -2577,7 +2590,11 @@ void dm_ble_evt_handler(ble_evt_t * p_ble_evt) switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: - dbgMsg("[DM]: Connected\r\n"); + printf("[DM]: Connected\r\n"); + + NRF_GPIO->DIRSET = (1UL << 28); //A2 + NRF_GPIO->OUTSET = (1UL << 28); //A2 + //Allocate connection instance for a new connection. err_code = connection_instance_allocate(&index); @@ -2595,7 +2612,7 @@ void dm_ble_evt_handler(ble_evt_t * p_ble_evt) p_ble_evt->evt.gap_evt.params.connected.peer_addr; if (p_ble_evt->evt.gap_evt.params.connected.irk_match == 1) - { + { if (m_irk_index_table[p_ble_evt->evt.gap_evt.params.connected.irk_match_idx] != DM_INVALID_ID) { device_index = m_irk_index_table[p_ble_evt->evt.gap_evt.params.connected.irk_match_idx]; @@ -2623,16 +2640,16 @@ void dm_ble_evt_handler(ble_evt_t * p_ble_evt) case BLE_GAP_EVT_DISCONNECTED: //Disconnection could be peer or self initiated hence disconnecting and connecting //both states are permitted, however, connection handle must be known. - dbgMsg("[DM]: Disconnect Reason 0x%04X\r\n"); - dbgMsgn(p_ble_evt->evt.gap_evt.params.disconnected.reason); - dbgMsg("\r\n"); + printf("[DM]: Disconnect Reason 0x%04X\r\n", + p_ble_evt->evt.gap_evt.params.disconnected.reason); + m_connection_table[index].state &= (~STATE_CONNECTED); if ((m_connection_table[index].state & STATE_BONDED) == STATE_BONDED) { if ((m_connection_table[index].state & STATE_LINK_ENCRYPTED) == STATE_LINK_ENCRYPTED) { - //Write bond information persistently. + //Write bond information persistently. device_context_store(&handle, STORE_ALL_CONTEXT); } } @@ -2653,22 +2670,21 @@ void dm_ble_evt_handler(ble_evt_t * p_ble_evt) break; case BLE_GAP_EVT_SEC_INFO_REQUEST: - dbgMsg("[DM]: >> BLE_GAP_EVT_SEC_INFO_REQUEST\r\n"); + printf("[DM]: >> BLE_GAP_EVT_SEC_INFO_REQUEST\r\n"); //If the device is already bonded, respond with existing info, else NULL. if (m_connection_table[index].bonded_dev_id == DM_INVALID_ID) - {dbgMsg("device already bonded\r\n"); + { //Find device based on div. err_code = device_instance_find(NULL,&device_index, p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.ediv); if (err_code == NRF_SUCCESS) - {dbgMsg("device found\r\n"); + { //Load needed bonding information. m_connection_table[index].bonded_dev_id = device_index; m_connection_table[index].state |= STATE_BONDED; handle.device_id = device_index; bond_data_load(&handle); - } - dbgMsg("device not found\r\n"); + } } if (m_connection_table[index].bonded_dev_id != DM_INVALID_ID) @@ -2684,17 +2700,13 @@ void dm_ble_evt_handler(ble_evt_t * p_ble_evt) if (err_code != NRF_SUCCESS) { - dbgMsg("[DM]:[CI %02X]:[DI %02X]: Security information response failed, reason " - "0x%08X\r\n"); - dbgMsgn(index); - dbgMsgn(m_connection_table[index].bonded_dev_id); - dbgMsgn(err_code); - dbgMsg("\r\n"); + DM_ERR("[DM]:[CI %02X]:[DI %02X]: Security information response failed, reason " + "0x%08X\r\n", index, m_connection_table[index].bonded_dev_id, err_code); } break; case BLE_GAP_EVT_SEC_PARAMS_REQUEST: - dbgMsg("[DM]: >> BLE_GAP_EVT_SEC_PARAMS_REQUEST\r\n"); + printf("[DM]: >> BLE_GAP_EVT_SEC_PARAMS_REQUEST\r\n"); event.event_id = DM_EVT_SECURITY_SETUP; @@ -2710,30 +2722,26 @@ void dm_ble_evt_handler(ble_evt_t * p_ble_evt) //Allocation successful. if (err_code == NRF_SUCCESS) { - dbgMsg("[DM]:[CI 0x%02X]:[DI 0x%02X]: Bonded!\r\n"); - dbgMsgn(index); - dbgMsgn(device_index); -dbgMsg("\r\n"); + printf("[DM]:[CI 0x%02X]:[DI 0x%02X]: Bonded!\r\n",index, device_index); + handle.device_id = device_index; m_connection_table[index].bonded_dev_id = device_index; } else { - dbgMsg("[DM]: Security parameter request failed, reason 0x%08X.\r\n"); - dbgMsgn(err_code); - dbgMsg("\r\n"); + printf("[DM]: Security parameter request failed, reason 0x%08X.\r\n", err_code); event_result = err_code; notify_app = true; } ble_gap_sec_keyset_t keys_exchanged; - dbgMsg("[DM]: 0x%02X, 0x%02X, 0x%02X, 0x%02X\r\n"); - dbgMsgn(p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.kdist_peer.enc); - dbgMsgn(p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.kdist_own.id); - dbgMsgn(p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.kdist_peer.sign); - dbgMsgn(p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.bond); -dbgMsg("\r\n"); + printf("[DM]: 0x%02X, 0x%02X, 0x%02X, 0x%02X\r\n", + p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.kdist_peer.enc, + p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.kdist_own.id, + p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.kdist_peer.sign, + p_ble_evt->evt.gap_evt.params.sec_params_request.peer_params.bond); + keys_exchanged.keys_peer.p_enc_key = NULL; keys_exchanged.keys_peer.p_id_key = &m_peer_table[m_connection_table[index].bonded_dev_id].peer_id; keys_exchanged.keys_peer.p_sign_key = NULL; @@ -2750,9 +2758,7 @@ dbgMsg("\r\n"); if (err_code != NRF_SUCCESS) { - dbgMsg("[DM]: Security parameter reply request failed, reason 0x%08X.\r\n"); - dbgMsgn(err_code); - dbgMsg("\r\n"); + printf("[DM]: Security parameter reply request failed, reason 0x%08X.\r\n", err_code); event_result = err_code; notify_app = false; } @@ -2761,7 +2767,7 @@ dbgMsg("\r\n"); else { //Bond/key refresh. - dbgMsg("[DM]: !!! Bond/key refresh !!!\r\n"); + printf("[DM]: !!! Bond/key refresh !!!\r\n"); //Set the update flag for bond data. m_connection_table[index].state |= STATE_BOND_INFO_UPDATE; event.event_id = DM_EVT_SECURITY_SETUP_REFRESH; @@ -2773,7 +2779,7 @@ dbgMsg("\r\n"); if (err_code != NRF_SUCCESS) { - dbgMsg("[DM]: Security parameter reply request failed, reason 0x%08X.\r\n");dbgMsgn(err_code); + printf("[DM]: Security parameter reply request failed, reason 0x%08X.\r\n", err_code); event_result = err_code; notify_app = false; } @@ -2783,9 +2789,9 @@ dbgMsg("\r\n"); case BLE_GAP_EVT_AUTH_STATUS: { - dbgMsg("[DM]: >> BLE_GAP_EVT_AUTH_STATUS, status %08X\r\n"); - dbgMsgn(p_ble_evt->evt.gap_evt.params.auth_status.auth_status); -dbgMsg("\r\n"); + printf("[DM]: >> BLE_GAP_EVT_AUTH_STATUS, status %08X\r\n", + p_ble_evt->evt.gap_evt.params.auth_status.auth_status); + m_application_table[0].state &= (~STATE_CONTROL_PROCEDURE_IN_PROGRESS); m_connection_table[index].state &= (~STATE_PAIRING); event.event_id = DM_EVT_SECURITY_SETUP_COMPLETE; @@ -2813,7 +2819,7 @@ dbgMsg("\r\n"); { if (handle.device_id != DM_INVALID_ID) { - m_connection_table[index].state |= STATE_BONDED; + m_connection_table[index].state |= STATE_BONDED; //IRK and/or public address is shared, update it. if (p_ble_evt->evt.gap_evt.params.auth_status.kdist_peer.id == 1) @@ -2823,10 +2829,10 @@ dbgMsg("\r\n"); if (m_connection_table[index].bonded_dev_id != DM_INVALID_ID) { - dbgMsg("[DM]:[CI 0x%02X]:[DI 0x%02X]: Bonded!\r\n"); - dbgMsgn(index); - dbgMsgn(handle.device_id); -dbgMsg("\r\n"); + printf("[DM]:[CI 0x%02X]:[DI 0x%02X]: Bonded!\r\n", + index, + handle.device_id); + if (m_connection_table[index].peer_addr.addr_type != BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE) { @@ -2857,10 +2863,10 @@ dbgMsg("\r\n"); } case BLE_GAP_EVT_CONN_SEC_UPDATE: - dbgMsg("[DM]: >> BLE_GAP_EVT_CONN_SEC_UPDATE, Mode 0x%02X, Level 0x%02X\r\n"); - dbgMsgn(p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.sm); - dbgMsgn(p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv); -dbgMsg("\r\n"); + printf("[DM]: >> BLE_GAP_EVT_CONN_SEC_UPDATE, Mode 0x%02X, Level 0x%02X\r\n", + p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.sm, + p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv); + if ((p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv == 1) && (p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.sm == 1) && ((m_connection_table[index].state & STATE_BONDED) == STATE_BONDED)) @@ -2883,10 +2889,10 @@ dbgMsg("\r\n"); if (err_code != NRF_SUCCESS) { - dbgMsg("[DM]:[CI 0x%02X]:[DI 0x%02X]: Failed to apply service context\r\n"); - dbgMsgn(handle.connection_id); - dbgMsgn(handle.device_id); -dbgMsg("\r\n"); + DM_ERR("[DM]:[CI 0x%02X]:[DI 0x%02X]: Failed to apply service context\r\n", + handle.connection_id, + handle.device_id); + event_result = DM_SERVICE_CONTEXT_NOT_APPLIED; } } @@ -2896,14 +2902,14 @@ dbgMsg("\r\n"); break; case BLE_GATTS_EVT_SYS_ATTR_MISSING: - dbgMsg("[DM]: >> BLE_GATTS_EVT_SYS_ATTR_MISSING\r\n"); + printf("[DM]: >> BLE_GATTS_EVT_SYS_ATTR_MISSING\r\n"); //Apply service context. event_result = m_service_context_apply[m_application_table[0].service](&handle); break; case BLE_GAP_EVT_SEC_REQUEST: - dbgMsg("[DM]: >> BLE_GAP_EVT_SEC_REQUEST\r\n"); + printf("[DM]: >> BLE_GAP_EVT_SEC_REQUEST\r\n"); //Verify if the device is already bonded, and if it is bonded, initiate encryption. //If the device is not bonded, an instance needs to be allocated in order to initiate diff --git a/cores/arduino/components/drivers_nrf/pstorage/config/pstorage_platform.h b/cores/arduino/components/drivers_nrf/pstorage/config/pstorage_platform.h index e792ab9..3fe9998 100644 --- a/cores/arduino/components/drivers_nrf/pstorage/config/pstorage_platform.h +++ b/cores/arduino/components/drivers_nrf/pstorage/config/pstorage_platform.h @@ -1,13 +1,30 @@ -/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ /** @cond To make doxygen skip this file */ diff --git a/cores/arduino/components/drivers_nrf/pstorage/pstorage.c b/cores/arduino/components/drivers_nrf/pstorage/pstorage.c index a1e08b7..070e20b 100644 --- a/cores/arduino/components/drivers_nrf/pstorage/pstorage.c +++ b/cores/arduino/components/drivers_nrf/pstorage/pstorage.c @@ -1,13 +1,30 @@ -/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include "pstorage.h" diff --git a/cores/arduino/components/drivers_nrf/pstorage/pstorage.h b/cores/arduino/components/drivers_nrf/pstorage/pstorage.h index d500599..d550d9e 100644 --- a/cores/arduino/components/drivers_nrf/pstorage/pstorage.h +++ b/cores/arduino/components/drivers_nrf/pstorage/pstorage.h @@ -1,13 +1,30 @@ -/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. - * +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ /**@file diff --git a/cores/arduino/components/libraries/bootloader_dfu/bootloader_types.h b/cores/arduino/components/libraries/bootloader_dfu/bootloader_types.h index da3ce2c..09cc01d 100644 --- a/cores/arduino/components/libraries/bootloader_dfu/bootloader_types.h +++ b/cores/arduino/components/libraries/bootloader_dfu/bootloader_types.h @@ -1,13 +1,30 @@ -/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ /**@file diff --git a/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.c b/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.c index 8912805..d2cade7 100644 --- a/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.c +++ b/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.c @@ -1,13 +1,30 @@ -/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include "bootloader_util.h" diff --git a/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.h b/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.h index 1e09e2d..fbd261a 100644 --- a/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.h +++ b/cores/arduino/components/libraries/bootloader_dfu/bootloader_util.h @@ -1,13 +1,30 @@ -/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ /**@file diff --git a/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.c b/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.c index 206cbca..e5a580d 100644 --- a/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.c +++ b/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.c @@ -1,13 +1,30 @@ -/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include "dfu_app_handler.h" diff --git a/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.h b/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.h index e923186..93e6250 100644 --- a/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.h +++ b/cores/arduino/components/libraries/bootloader_dfu/dfu_app_handler.h @@ -1,13 +1,30 @@ -/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ /** @file diff --git a/cores/arduino/components/libraries/bootloader_dfu/dfu_ble_svc.h b/cores/arduino/components/libraries/bootloader_dfu/dfu_ble_svc.h index d1cc440..f6b5e80 100644 --- a/cores/arduino/components/libraries/bootloader_dfu/dfu_ble_svc.h +++ b/cores/arduino/components/libraries/bootloader_dfu/dfu_ble_svc.h @@ -1,13 +1,30 @@ -/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ /** @file diff --git a/cores/arduino/components/libraries/ecc/ecc.c b/cores/arduino/components/libraries/ecc/ecc.c deleted file mode 100644 index 2f384cc..0000000 --- a/cores/arduino/components/libraries/ecc/ecc.c +++ /dev/null @@ -1,143 +0,0 @@ -/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of Nordic Semiconductor ASA nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @brief Elliptic Curve Cryptography Interface - * - */ - -#include -#include -#include -#include "nordic_common.h" -#include "app_timer.h" -#include "app_trace.h" -#include "app_uart.h" -#include "app_util.h" -#include "nrf_log.h" -#include "nrf_drv_rng.h" -#include "ecc.h" - -#include "uECC.h" - - -static int ecc_rng(uint8_t *dest, unsigned size) -{ - uint32_t errcode; - - errcode = nrf_drv_rng_block_rand(dest, (uint32_t) size); - - return errcode == NRF_SUCCESS ? 1 : 0; -} - -void ecc_init(void) -{ - uECC_set_rng(ecc_rng); -} - -ret_code_t ecc_p256_keypair_gen(uint8_t *p_le_sk, uint8_t *p_le_pk) -{ - const struct uECC_Curve_t * p_curve; - - if(!p_le_sk || !p_le_pk) - { - return NRF_ERROR_NULL; - } - - if(!is_word_aligned(p_le_sk) || !is_word_aligned(p_le_pk)) - { - return NRF_ERROR_INVALID_ADDR; - } - - p_curve = uECC_secp256r1(); - - int ret = uECC_make_key((uint8_t *) p_le_pk, (uint8_t *) p_le_sk, p_curve); - if(!ret) - { - return NRF_ERROR_INTERNAL; - } - - return NRF_SUCCESS; -} - -ret_code_t ecc_p256_public_key_compute(uint8_t const *p_le_sk, uint8_t *p_le_pk) -{ - const struct uECC_Curve_t * p_curve; - - if(!p_le_sk || !p_le_pk) - { - return NRF_ERROR_NULL; - } - - if(!is_word_aligned(p_le_sk) || !is_word_aligned(p_le_pk)) - { - return NRF_ERROR_INVALID_ADDR; - } - - p_curve = uECC_secp256r1(); - - NRF_LOG_PRINTF("uECC_compute_public_key\n"); - int ret = uECC_compute_public_key((uint8_t *) p_le_sk, (uint8_t *) p_le_pk, p_curve); - if(!ret) - { - return NRF_ERROR_INTERNAL; - } - - NRF_LOG_PRINTF("uECC_compute_public_key complete: %d\n", ret); - return NRF_SUCCESS; -} - -ret_code_t ecc_p256_shared_secret_compute(uint8_t const *p_le_sk, uint8_t const *p_le_pk, uint8_t *p_le_ss) -{ - const struct uECC_Curve_t * p_curve; - - if(!p_le_sk || !p_le_pk || !p_le_ss) - { - return NRF_ERROR_NULL; - } - - if(!is_word_aligned(p_le_sk) || !is_word_aligned(p_le_pk) || !is_word_aligned(p_le_ss)) - { - return NRF_ERROR_INVALID_ADDR; - } - - p_curve = uECC_secp256r1(); - - NRF_LOG_PRINTF("uECC_shared_secret\n"); - int ret = uECC_shared_secret((uint8_t *) p_le_pk, (uint8_t *) p_le_sk, p_le_ss, p_curve); - if(!ret) - { - return NRF_ERROR_INTERNAL; - } - - NRF_LOG_PRINTF("uECC_shared_secret complete: %d\n", ret); - return NRF_SUCCESS; -} - - diff --git a/cores/arduino/components/libraries/ecc/ecc.h b/cores/arduino/components/libraries/ecc/ecc.h deleted file mode 100644 index 0adca39..0000000 --- a/cores/arduino/components/libraries/ecc/ecc.h +++ /dev/null @@ -1,82 +0,0 @@ -/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of Nordic Semiconductor ASA nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @brief Elliptic Curve Cryptography Interface - * - */ - -#include -#include "nordic_common.h" -#include "nrf_error.h" -#include "sdk_errors.h" - -#define ECC_P256_SK_LEN 32 -#define ECC_P256_PK_LEN 64 - -/**@brief Initialize the ECC module. */ -void ecc_init(void); - -/**@brief Create a public/private key pair. - * - * @param[out] p_le_sk Private key. Pointer must be aligned to a 4-byte boundary. - * @param[out] p_le_pk Public key. Pointer must be aligned to a 4-byte boundary. - * - * @retval NRF_SUCCESS Key pair successfuly created. - * @retval NRF_ERROR_NULL NULL pointer provided. - * @retval NRF_ERROR_INVALID_ADDR Unaligned pointer provided. - * @retval NRF_ERROR_INTERNAL Internal error during key generation. - */ -ret_code_t ecc_p256_keypair_gen(uint8_t *p_le_sk, uint8_t* p_le_pk); - -/**@brief Create a public key from a provided private key. - * - * @param[in] p_le_sk Private key. Pointer must be aligned to a 4-byte boundary. - * @param[out] p_le_pk Public key. Pointer must be aligned to a 4-byte boundary. - * - * @retval NRF_SUCCESS Public key successfuly created. - * @retval NRF_ERROR_NULL NULL pointer provided. - * @retval NRF_ERROR_INVALID_ADDR Unaligned pointer provided. - * @retval NRF_ERROR_INTERNAL Internal error during key generation. - */ -ret_code_t ecc_p256_public_key_compute(uint8_t const *p_le_sk, uint8_t* p_le_pk); - -/**@brief Create a shared secret from a provided public/private key pair. - * - * @param[in] p_le_sk Private key. Pointer must be aligned to a 4-byte boundary. - * @param[in] p_le_pk Public key. Pointer must be aligned to a 4-byte boundary. - * @param[out] p_le_ss Shared secret. Pointer must be aligned to a 4-byte boundary. - * - * @retval NRF_SUCCESS Shared secret successfuly created. - * @retval NRF_ERROR_NULL NULL pointer provided. - * @retval NRF_ERROR_INVALID_ADDR Unaligned pointer provided. - * @retval NRF_ERROR_INTERNAL Internal error during key generation. - */ -ret_code_t ecc_p256_shared_secret_compute(uint8_t const *p_le_sk, uint8_t const * p_le_pk, uint8_t *p_le_ss); - diff --git a/cores/arduino/components/libraries/experimental_section_vars/section_vars.h b/cores/arduino/components/libraries/experimental_section_vars/section_vars.h index f971681..b2218a8 100644 --- a/cores/arduino/components/libraries/experimental_section_vars/section_vars.h +++ b/cores/arduino/components/libraries/experimental_section_vars/section_vars.h @@ -1,13 +1,30 @@ -/* Copyright (c) 2016 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #ifndef SECTION_VARS_H__ diff --git a/cores/arduino/components/libraries/fstorage/config/fstorage_config.h b/cores/arduino/components/libraries/fstorage/config/fstorage_config.h index 71ffcad..a3119c7 100644 --- a/cores/arduino/components/libraries/fstorage/config/fstorage_config.h +++ b/cores/arduino/components/libraries/fstorage/config/fstorage_config.h @@ -1,13 +1,30 @@ -/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #ifndef FS_CONFIG_H__ diff --git a/cores/arduino/components/libraries/fstorage/fstorage.c b/cores/arduino/components/libraries/fstorage/fstorage.c index 071cba5..c4ac1ba 100644 --- a/cores/arduino/components/libraries/fstorage/fstorage.c +++ b/cores/arduino/components/libraries/fstorage/fstorage.c @@ -1,13 +1,30 @@ -/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include "fstorage.h" diff --git a/cores/arduino/components/libraries/fstorage/fstorage.h b/cores/arduino/components/libraries/fstorage/fstorage.h index df18654..61f8d25 100644 --- a/cores/arduino/components/libraries/fstorage/fstorage.h +++ b/cores/arduino/components/libraries/fstorage/fstorage.h @@ -1,13 +1,30 @@ -/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. - * +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #ifndef FSTORAGE_H__ diff --git a/cores/arduino/components/libraries/fstorage/fstorage_internal_defs.h b/cores/arduino/components/libraries/fstorage/fstorage_internal_defs.h index fc29693..34be35c 100644 --- a/cores/arduino/components/libraries/fstorage/fstorage_internal_defs.h +++ b/cores/arduino/components/libraries/fstorage/fstorage_internal_defs.h @@ -1,13 +1,30 @@ -/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. +/* Copyright (c) 2010 - 2017, Nordic Semiconductor ASA All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #ifndef FSTORAGE_INTERNAL_DEFS_H__ diff --git a/libraries/BLE/utility/uECC/LICENSE.txt b/libraries/BLE/utility/uECC/LICENSE.txt deleted file mode 100644 index ab099ae..0000000 --- a/libraries/BLE/utility/uECC/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -Copyright (c) 2014, Kenneth MacKay -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libraries/BLE/utility/uECC/micro_ecc_lib_nrf52.a b/libraries/BLE/utility/uECC/micro_ecc_lib_nrf52.a deleted file mode 100644 index 80a8f5ed4269a6a8c46962ff17ae6a1a174f87e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 186030 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%0DG7pO{v->Wb1ICY2190tlG?)PZJJ}S z>8{oGJQ5{FldrsqO^o)YD#^JNKV1NN8#QfTk2jpF(Q2KcUC}~s8lRHD9zN{mZ7{44 zVN)%;l{fAqWKuLn#aB7d$(TJ~`BRa+0X~#B4}7SWt_!3wNxvxs6)OsGmeYNU@#OuL?&iqymQw`C^+x3jIyGUSNQwrY3oTL6(}( zBNQ%U1?pukY$0U1BwRIUEC>==r@8)G3zU(?Xaq+pXVNbw6hRdv>9I;vHZ;?Z#)^<% zFj)3}O*DK^ZKRelv%fwd7d+}*jTU6EUqylF&~^l*tt zOm2}jXORN3=C`jL8~#6cd?ZjEeO}~YQ4ZPn4tD5vD^%M9^*6_{4zKxBg9`ufU-)!~ zA0nsV^u8zAmS>i-%_bYQInG+?4R29Ur#7TzXv796vb04j0^~@S`;6NXh4b$)jPgZyuakc5Kq^_XS z=00j+YMS3L2EEw4`ey@G#S?77o%^DhpbhZlQb1At!z%+(Ihoa@3R~W;VBw3c4IM<%sC=$f)4UQB{tJFQ14C!d#W*$Qbj9 zsC`_;Tk1CLsnTY3`-(Q7yPn1daU?!8oo0GFZrrkVCCi>+y_qY=URAXN%0AOSI=*9u zo5u86KCe?{Z)ok^!?_3?!sE0oCJ?*RJv^%*8v~o$)}Sj&s>3ic^Vz<{4b3uoQ>8LF zs?N()xR@JNy}dst$Z1M5B}nzXwH>q1t=*d?0of#Ka;A4|HaBwbJv(95?YpW+);a(u zk7W;_&myxWP;qAvXH&+IS03z_bB>tn&a|#>-6V<11~oavAxA{IT3Xk1%Yg_e4d~YM z9MzZe`reuS>V|4;Bqf2IwubR2^xkAsA#PL92ll#(9q>+WH_{SZ~NfXRNwHRItVeA*)}mbIdO4hV03tNTF%0x)bun(t-3t> z*P5&5JUhxzpCs~iDcOpOie=!d^SXqGCVS@s4;|A)vZl@3aO4n;->H7yT;QHyIv*mY^W zApop5*@Mvkoc>ogQmG=#+ZAVV%O-sjhDE&v1>74-D=CwbrOgpFOn4V`?a(?-=XE zxI@NS_QhkX+P1*YADi%Wi<}wqZvARng_EsmUf9OD%~T3Ag-DmPjCi*$V`+r1e?GiS zF0{wl(PV9+I#KPGPY!bAIk?0<2Z>h=Zr?UIFfce!$;42=|WPcw2{rRBJ{x*CeYqzbbhg9-QWZ%J4v@whl{k9Hw%$ z$5;6S$59iwvmjB^h-0bM$!Z+hN!H0})Eoz*)p=+fhn6I9A{&=4aIHvRmlGI^3Jj-K z`@x|}K=EidM>lYq%Wcd7t#X`#t>^ee3mT-w+jfL#@TWw`CaBeCH>KBLGI*g1~ zjQEl`>}E1IDLbE%I8V%j>*}PtHH?<&aEL8s7Wv>@h8Zz~DCAC#C(D3O&pOygMMLgp z9A2(j?Olr@=Z-HPogTpnW}$}}N1K%C+0Z2OC!r(bHTuYSuA|}}hom*q7bo(Bv&T4@ z`9a{}H?)}y`5eRj1=ZD{*dISzQ$QlkJ}i%T=crSW?R~q{90Rae?Hf;gQrH70K25=> zYoxj-R`Q-MMD$v(bThNkET=%#Ibsal)#*%i(r~qPOIEkJ24Z7Jck2dTkflyk`kev@ z2o2)>v+Mbetozb~s~a+?1bst;p+SAETPDjPE|#D5gk5-?&s9!#ta_%`Kdxt@>Q!q- zRa(DHdxvL?xhX&`8nCk_Ub8De_o$xY4IS60I7ah5yt+&h4LTF&2--69lpTpHI%b27 z(Xh-^D97U2k&u3lNSxi5Wkk+In@ES@lBOJt$VR7i@}p&)rrnGw0!-4OzU~-chY8M| zaTEMT7PDu&-Y7-2k?4Pcpz4Ja<5+vDAyr*dSBFbmQVk7t zgJ*EtIq)cr&J~^2)9!e!uy?17-grR;xko9ZzK)#2IoEtkd!?3bXvW?Xfx1x)Zzyf8Ch)!=pf#u)b=IT`X*$5%|x!Mpc0R~`bxa$ ze93ryV|`sC#t$_Ob*buPZ9SNTTWvrvwK=XC;bv&LpaWiHZn|l;3^Y?p88_GNsDuA< zty5%M|HO{n9zTLNFxVF-w8@viI8olt{xs!&bxrIo1n~BxMvssTbuJ71AD;8$T&VSv}j{H{~hfW=+q8E zuqZ@elk zqc2d>>*ZF})9pc~zFA+a%h>*kv(?}s?H+3%DoHdqlGx*0uf@%7UgZM&;KfmvSiCT>`9qEMyBrJ@lGkpg-GFnWq4Xf-QFWD%aQ7->IltMW`^x@K~Ue09b>yQ zdxRvjF~Dl^DO2q9%w7WZf=mkUR6Fcn9;NU0Q#5s~?l8@TTa62pR3x@)q`V8MqD}}# zGwfEIru~T!RfjOq!K)$d{Q<&SOHM-jH&vUWE;BPyC! zba)6B+^+QNezSlgrjp>_{e?>;x9{*~$kerZ)k$299s$g%7P$rt1=7TuxG9Koy-@~L zbM0E$7u1H&foy1oys%+5szLw6c5E#g^frZvGHr5feJ*?Ctkc_Omz;NcJM;yhRNyN>`fM(zhRr5AeUO`;Fl4pa z1}^mjb86x0p=OK#DLe{-gQHpCoR$+#b80sTjw#?QaDsAbSn6O99CP7W-~?6s)v~Zd zcC)}aGfskpc5_--wTBX5C=CY&=rVyGS0&JJRUO!pnQSJdH}%EpN87V9b}|eE`c&O35$G!lu&MkINMjX^R|zNmIcK0LKg;jp+X5mSy=uN(Oe|? zp^_FN_%wO&L?X-FDfVk{&%MA2gaMVtkvnq9LT7CZ}VG8GUaoQ=ym3+$tkoSTR$f1ZR)8co1s z5;r^v0Y-=e3CZ0U;VR*$Bn4|?22%>L%*!Q`%Uv&ykdnx3hlv+7!Q!vR3Ae+iNj zI68mW+Gj`!2+-jU$*XqN%9(u@Ka_X7sG* zhY3`4AgL^w5dDafD0G-^DHk2#KwW>NB*K9KI!vIFZ#Gm}I*t^O`q6y%WL$BGWoYsv z?V;(1vob|NSIw8IV?IK*&R)Y7Kp2+wq~Ew4ZG&}$vL?4ERwfTQN;4d0Gtq<{p)47P zC1P4=FC80~2~)Vhq%0PijYupK)9U0eKQda3&uh6;CU3ElA7Vlga+fxWjTsOVilB-Z zf&S!z5}aJBEugfXn3ZKBqU4YQ!)?_#(pw3YPCOtm7;X6%9a*C-Mv+cm$5Mo8UWN2Z zlZZ+Ds1uRyA$KAnmC__)>f});B1M@y5u*sJ44OntubMv*)f8)e&tD(K_Kyub&589@ ztUE#PgqDh5t{7u3-ClC$Y`C|*J+w<*FEhx}z!CRb)L;n$^F3ML-cHu4FK@$EpWM)! z68r@z%SV>p%mh6&WqawL%iUAhJBoBnzLbJ)W{+eMg!^dKs!M|_OC)M7E3l-^B@=9A zvG!b;RW&B#uK=6%XY9+CZP|J|h`sD;Rsq#Lm&Bky-xXZ&axtb*Uspwelry7XgRCr@ zHO;zeRgAJ($J=hqkCvG{hQ1c0zacAIIMnyf>su7hVaq|+&|0QGf{L|F+-btaOs)}{ z6;O}mId^n>AHm?9U)}?){CKV2%B%R11C{#POgL$Ux30Ffx>mMT%jsTj_U*w}%=I>+ zR6}5+N#F`gxzG~uauU25%0b&e%cHs+g$`8= zeLE+yVaMMAu2$?oQHT|qsoGY|UGJ}NsVxiT^5MG*!MDT}arO5zl&jDs6>hl#s9bI~ zm*bOw_{tU2wYT&01UE`>EQ8HrliVR>?{?O=dYX^ed85y8o$_{UDCXOn4rHfG!cFny zhmjDyeC#&nyq_i@NH;WXu*pzlC z$w?+|ZGfX>*=zkmsA>;bImf+|4EPvTdpY=vj}Vs9J|}UWdm2CBDXac~T+N#bq%HGW z?2sUOpZ(UC(FM03NUfs4o0ag84zSSi^><;x@zQ0R{~XL{qNZ)gN_#*TNqV6sQ4PC`*5kq z{i=Zy@|mgzW*jFQ60l)y zX-^2r+$sfC@toB8*;aYfVr<4zUe--YMmz#3;fm%Tu2kL_aBK z9QB@YgBdv%jX@tbl+%XrG~Kr9b?jvi;Ntc@@5(3kz`X0pR=4HD=h8JdxJglkWQgf$ zg0k0;U7^{?rUnnZOi1(E|1A3jGx)MHan<9nE<11egrEN8B_tcWyP;_)1zQg{Cs;3zG2O67tn^$AJf)U^( zK3a})_b#XnoGKOtKp*1-XI)!I(zrPRAo{ZpZB*v47gPw*dd+s+q-@6x{)V^X;>OGJ zz9lX!d}ZF#j77`D3K>hv{UUcp&A@I}@3v~zA}SJzFEFG>G? zsgy7#_#0+SP-pLI-LTpxY3JbN=n#iJvPV6;R*2H>$zOd)P-Qoa`ed{932fXjy1ftI zz~=3vmNz|J__8GM#aT}>vGbl&Bz@B}lk)XwtrUh7QtuPMW(2D1$%X11y=-!B*@#Ap z`{1cGqsUZMhxFytu+=yZUr|;k1ibcydtUX}rl-A$yF(%VqJ`sw97F`U-_Hf= zcT-=QMg8!E&Rx_O@jhOQ@7ecp+6$b`o{7R{Ts191hbO9m{jxH&mxR$qs6m`L0MJ;Y z2Hyd^GWy10Ak!dt({6I40Ys>IC+vIm_fXeV%k2t*!jP7TV*|mnipLnxm>8ru?mWFx z4py*I9B%Z3V0JmGG4Nkdv573s&sfWciSNr+Zm+?+`dWSE_P%YS%li6uU~0Q^`_R}#-*hE?MsVVd9*8uwV`oj})Q-vl z^huS&)o$g+lv_D9J+N$ee5SF{tsK%{$*x4($ggDs(-1y(-Dkg#Paq18&H?+_Xy4T6 z_%MLrxou{2YZMloM6ZQcmL{q-SJBOko+RM# z$^s`=g32mj5FB1v;2M0&g;6>gF!Cg|;rtNcM{NZ0i#SF5>@qwrqQV3vGk`Y;i=hNLrl z)DbirjyeL%oaU$_Xx1Hd1kF@Gf?}V?RiAlzxtgQ$c~_8&=*d7JIC2P#1ajO#ZjD0F zcz9*){J?RtnIndo6xPTT}O%jp+@pwEzBzJN3b6_Oa-* z_`PT(s`|K4x6RRq=*u_(%Q?9!ErDas(5Hc;jD-GPZU zw2$_|QFdA8qf9x;z?hx!YyF``N9j7{ojeZk~1?O(nx58Ar%yYrW3UmxV z;~3v0YjJVvc67iBhn*iwIrefWDi`puy!63OFW3HPLFwH}J2Te=S zlsT;JNN3M0Oe}Ruh8@yLGQ)B}%_r3LC$~{Hn(|QoD0QjEn#Q{NRAX&V#G3|14zX~g z2Qsxhw3C)=Z4lt;N7lfVbAbHvyH_v))d-WaQ2UY`!cL_)mWA}?T6&{$jT}N<;Y=q0 z)pf+dvu~jfb_s4$Wa)uztJBTBocZ=v%S7HeBkW~HIHoT)GI{8zq6?=cZrLVd{mR$UMvha<&Ho1?=2#z_FM~!-Lz?CC;!ghmM)i>`} zDo?18h`q4L3!elmz;vbRCnMdN9n+JcqeeJol;`?I_Dg1G4m$_l%%)DX1osUo{lT9b z47YzOCfeR`PsHwAkOsP3)kmxOCF!8qkpw$+i9~(JOv~v=sz>L;*qaxgzRY8AsjU;H2tO*31(Uc=gXc6e|!Ja*QW@mDt}N&o8C}P2ejl9FJmop|k?` z<*;333s-kk9gJ>+a zuuxugCH}AOSTs3rka`|U*r*e_IP!>v4$jwIzrj`MrNga^X50V$T-EI`avElW4ti}( zGzKOd$?2_@salS9&Dc&Y)r7`&UbqZf?RefWqko6qp~fW=>&}K+?*Guc$IRdaUq&;Z zH^k_aa|Eec7dL12O>Otqw`4|~gR^FS9Ge4Fz1hs0fC#Kq_}>mu!5BEMV6kZ&9KPmLieA)Pp3%yeo z?eGP7KM_VhHE^scDLxpxGTOl!Vaw1ugf%hjy6CEs^7H4E6gM3c#^q{6ur~?3sbeF$ zZrCvS>Q9A*r#K%kn@YYKjeT!U$>NpA90q4oNyo+#^3l!<;hR*Qye_)3q@%55ZlKPU zNw}dMINFa2hb;r!fMaFJ{DZTuEV`=j>Vj*WcY$*&OIj{0nI9pa_493!>m0JOwq#e2 zF21sO(iX{3s@iVZ=U6F1?t$!l7@58aM*mfH(xogWH}#H zoS0FZVBYmJo-bZg$%|2EUR3kWE}LmY4$#w7G6K|S&VuzLT;Kj9@2(~kb_!;-?su+^ zcB^JFD4XX3CO{kB?fl+@_nd7|Q%QpD7OF!_2{LQTK_owqu8C$8hwS>n{&8hVKyCtZ z@?v&)Q^~#2*tim@K-$j>Y^WZrE!jFTr)2)hV^+%VT)^n301lg$^YPwP66_!CyfHkt z91hJXaghRq(dJ()KQBgN|0cXxMgG~cxfDd@;rVk;?0`fv>0rPhQ#hU+X*Rabfhc8jgVMpw_u%}2 z6-zcQ1-8fgQ9>Bir8FK%+;q>lm{JL2(+g7mYL5kBR#W_BgoHpOrOqD?A|JJBzGe5i=G|Xpj&zp}cu)GoS*5Bf=_@uTNW*wWzhA^K(QKu6u0P;wEN+aay-3l7= z)%#~c-g=oTeU$&T@sXl#uSdQ$c$j^)PT8I1Jo?1^3Itz>QgO9Gm3h3&(vr-CrFBZm$LR(qZ5Z zT5vC0a3{l!p>%}$HJ@vh_5as)CvZBI|NqC28C#YJ*_RkPxpMA>&)LR4am86j&F zN)fVzBFP$xkQAXvLZJxJ7ZHjgR4SFCsQ&NIbzaBZbISMkfBcvGk)HED*L6N;xzBy> z`#$%5&VBX&dJE!irS*U7r^8C_7Vc+lcDq^sY`*`eewEcHJYBm_f}5=uyV>>;+3$pM zlN1>&?_iLKa11#FS6dTh~+3q zKdb+`{zcXst9rH#2Df1TmTN#{y>k)sXUE-w`TIzJN7g&9dbSRNTTt%<4T!9FA)=lg z3k>Rgt-mAd4b+B{tYN_|SkG59AhO>+^;;2ilEHK@>F>ztUeR>xX_(!=CG;ODY9OjeChXFDRmC&#f9p%%HnyugJSfvSS3`ik?O^2xXoW_{R`@~ z57jHI2dEua4sOBxwF%W5qI&hz$?Dyte?h&6LiJ*^>G7faF1Q8t28HUaRlR}gWcAAF zUr=vms9p!Hq=jk^Zb7~2p?YQWr_Z15D}s7&hU#5Vy#l(AgIiE9;Y|Z)RKLzaCmHnnJ2c%9nyzNiJ&x>iv|zd=LiMJqUfF-C zS490Hmt%57J$t?h>LrBgtyMkyTq3vy^H(EOZ(hWDv18yty*8nG8}tAB{Iv|#TO2We z^&;x^3DrBHdNFDbZo&NZ4Aom3F@FuzIhenvLiKDTEgKZ0pT%*ZdK)9=uMz!hJ6Ia3 zS6206G(5Nk^S3BeZ+FD}HBska{&t4yHB!CQpcwruZV%Nvpn5SHZu{M4`WMvuDpYTo z>K(8Rl3Or;UxeyiP`#<@WA$3-Ur;Zz{;<)5*MB4Q;vrT!xCQlWgvjl0Y=J2Ed7Ra= z$6ZjbRH)tx)w9=P!7Zp)EL3lv>J3yUt7os#f_e=@^{y1s=LP>#uWqPbt{bDgS=4JA zQLj^|UWFo_cS7yK&8BO&_Mv)SfjIxXAnC)HUiwsbA#dqd-KxY1OH`vULLB~NcDp4xlcsBo#A>n zd7eEUgIh3v+e7tMs9vx=_f_X${=N*=8>@QfgJSfv_<5+_M%9baa9iH~`WMvuD^zbt zS+R;aD&&lQdws5^0)h}{;J{3{#!H9ZEp?ayR zmmCzMpT(M?dgmh6pY1<_`MWn%Z`(EMbqdwH5>fBrh9B};(?azsRM7kV;7*{Q@TP?7^;NwI8gBQ;Bl;IC@1jt>)XM+9|JxMm*Y_IzJ`435 za*ck!g!+w8zXF=C;1(=WUcFC@++L=tpKbTSE$CMz)NkQ6rrSK!Z<+c9HLPavcb8DV z71!uDAk=TwHTsPX^;>(5eltS-)~laAj)Ge-f2%|Nwy0mDppy*xy{>+dkE09P4ubv0 zc=}oW-P8*<&CGhUxawc(?FiM&TqJ!zIWeN%*-*W;*Qj?YRIhkMy-5-EGHWJnI|$}4 zwd%jmpRKIO<&BT1HzlH8OsHOgYX4rZc&J{hhO3m0xdzj2}IZfmaR8TGU2+Ofu9y0b&|vR%mdIx(m> zBUJCyQtcJ9zPUkB>qZb7|ep?Zy$M|o{?`}O9kXE1-8LiLXSrT0a_ z#;ATly$zvy&#jB{b})Z0{98S0+?*+sH(%ohw`HLMgOHynrM zOTDJ5cQGiUe!-vN`=hVw+5KkoxAec%yFFK?499b>Rd22C++@|UdawMKdQYj|t!fW$ z*Q!@wr{+(ctlq2ocdh$(o9cB7ipgu$>!N!0`oZeGrhnI}cT?_6UT?Jrw`sJlH;DwEe_2|-vr`*%9j@5Akxyg}*R zuJ-#SG=GU7X#0zp5u2{vehkfD@jX%ADmB{jzO8@3{MkZ8M|@N@c5fyx$qH}_me*E+ z`dz)ftA2$drW^D#*|oU7D|Dc_t&W{-Qqr!=O*gOpwoxPNUAZpG+pg#M;1<*ia$w}} z%-&Cb2gA+i4Byu>v# zD78o4qIIM{V|^XuiMU1Uldp{JCA`6Ew|Xn|FG}t9T5qxb`L`0mAI@m6RA~HC-qUKa z@%QOpl-eV2(YiM>POr3ktzrFlyZ)u!^tN3csr1#q?2l-Da+PuXn>^ds+4%MuSd`jr ze)HM@ce^S0!x^nlsxppW&iha;*4;kyic-6cZ=bFDH@(S>xJBzz?2O~z>>XFTjlWR; zqSS8V&(c5tc60EDGupc)H2y82_KKl)+uG7^(O#ucdnGr$^m(e3-dTS|dsRaHtAzSj z3H7fQ>R&C?9vf;8_OgDrXfH0*9v2$FdZ@j6sJ%w0y+)`#KGYr`YOfh;*GXFuw`i|c zsJ&K1tF5nuP`iB+lzxlWo;rP9+wjOew~c>ms6FzPg|*iSwc95&>9=U__E7un?*2+Y zE^&K$XZ;oJ)eH5n7iw=1YHtu~Zxm{86l!l0YHt#1Zx(8A=3P_|dpxA*pZ|&}eUxZ@ z!jN(OweYg4v-P)gNTS>)5Rv^`h5EPhg3sq|%ZtkJO3>Cv>rnsJUN!35c_dNp6NJe6 zZA1Ordd=x?pC$OOm?Qh&8R~zh7knQ7g`1zqS3)*F9YXy(cwMM(_heLtBZ}7lu2BEG z(kHIJqV7Dggw6>y*^;1IqZ7WN^Me7r^jQvwX{ZrFB>#u09 zXQ;nDOQ+wWz57G$_op}NuV}rZ%{aYYZh8x^_Ud)@FZ&}}&*~Zb_wnq+JN;*Op8o6O z#=qhQ*g$&~kbaBSH*_+N-#5Kce?{w6QO5TEuHLz;z3f#}`Yl>VfHU?VnBJ(rqP+)0 z?GJ|9hlJW~t4hB`dk=@&A5L%7U(tHil5zU>Dkl9Fts|lt+aF19)L+rwh*0~8Q2S${ z_Qyi)kB8bH54DdBwT}$7j|#Pq3bj8GYJVcsK04GsI@CTU)IKKEJ~q@oHq`!PsQt-M z`?yg1xKR7}Q2Y2$`$V^Wr4O9wEnwSSrGc5eiRn%HD_Xtn^-=n5lD9HL|BS8C-jq=P zDcDADc+USxhgJ?O7!_v$x&P_#Qz7MblxSNbj5dm%mOuV~NS`KI5Zy>)K>@?ITe z?>y6Q(eB7pWPUL{=&xvZWGH=5v}Z?l(r?l3$V_B@B|Ye`Xm?~JeNePJvJsi>)q46Z z+Iu5C=&xvZWFUP|wB9-Y#rpM!X7b8uKbd~>j{&6TXm47E_RQYa3^|K;Aw$mU-4ywz z*vsbKnIUKQp3IQ1^FGRub9jGe$T`zzMSn$m>qGgSP|mP$>Yrielrt1pvaKxr7VTw8 z5Be+m>bEug!O>ct$Xj&i(Hx!Oh>DG$BeJ`4PA^fDw7Ilb3iKC*WIL}s zpWfdjlkK~A`SfmiAUT)oU&(tcL$2aIMYe4=pI6OWLbm;WK7HflJ#tZ(<2?Jk-{z-+ z%hkOTy?``s1g1g~;~KE1zEfR3+OhyL@`R)0n*2&|}?~NqeYm9t)Ju#K6yFvLuZwc963Fgx`LW0jL{rs@EgZ3!5yu-W`Wc%zi zpPv8Ek%jUIFOxQDTmOE3%qvLtm+x_}BH7;_Mtb$h{`NJ>>zrZyC%pb-e||@Mk7meY zya^fdSTFb-(C`1G_fm#>_uSGc=BW=E*-#z4U99O-;Y-Y%hdcj{_aH`zZ)7{)@a97j6oOR`&r>1kQx()3;q?gW_4W9{= zp8a)}A?sYY^z1KZdiK{-=%k*Z{o<>}R;?b}qk2;Ho{0&GNhzsGX>n;aQWAT{)l5l< zi?3B9rDhM$y7f$R*Rda5wO7^Z)vH!d(=|Ke;uE{a)~prVJ+*sE&+0X5^+-)kjZaHV zjO`ikMhVVQ>^5Zhz%&~%u4dJw8a2`q(^6BbCv>k>Gc7iyR#Nw#@!b>kcS^15HBzhl zBkCNs!4D0zF=DIs=$_QGM)mHs;!@({G@}Wz-QyEemiOsSQs`RtyQP(8MKe0*X`kJ!{$H-fEcs~=Z2t!7GGQfhosd`e1c z&6)`bTIAH&8a=DW#wT^JQL9#>>mDpYeAQZsJ(6n1SFe>?D1uT_=OJ-%jYLd_m2X=&9{we+=8YSv7N zPjgdX6!!xPTo9<6yWkT=dQ?wKO6Xp_XIyGRYC=l&w3M{O)Y#M>sXb~WrX|*jjf*Ve zM$z3No%?Gim-$_TbC+F=uXFU1U4gR-NUR}f0vsqorcn+&yP`@9L z+RdE{$%Op@SMOqsKLP)DK-}N{7G_t6_Rl~K4fM}F4G*;QLqo%Y z^G*LT_+Kk<4eyA~Y){NiWc8+zA+!}m-0>0Tj_SGGUSH(Ub9;;|sh7$2`YDG#V5^9B zY4PdGm0wjM}d6_Tmm%QtgS>uKvAnGQMT1eVdGW z-}9`$*@X6>e^CDe`u~g?)z!7nOrk#4cfGI`q|M++BLqLzMQ2Twaats&CG83lXUI% zo|~Sj@vV-nM|&S)!(%LKxV^8)q0j7H|7ica4>|H@a-Ur0P`=*UGt(~^uI%QsP$qXy zUygj4+;;^KBe!YI{9~rx~6@j~g4Figx_p#v5Vk{+___=gBXT z-S(xK-fh#DZaW&N_tREaAO7|=l^-!RzY|Q&pL;yZ#g_G9>w1QNfJaP?@8(Z+H7gp= z(_23?ZIv(Z}Pv3W%KREQ=J$M&!I2W)iYJEtEuOam&_dg=j=K3wTZP1|AFC` zObxGS^C#W?A>I6{-bNd)I&QmA9k<@q-o=J%`Q7%T<$c_;*2`op8KtcIwX&)EE74T{ zMyBq!6jSS?FOJf29UDH?)Ohnvjkny?_OQ*=_;%i{^~<3<(zeUs{;y-pq5EmEjJlua zwN$=ea5KaEF}x4MW3JI(A4mUdof_8|UdcaB5OvT$$201ve=fJFyVNs_n1}syY@@Uj zD`)vLt#{h(6JYCS%V9Q0+do>q7gMk|_QywYG>*sT@FiS|tI@tUWc6+TYVOA`@B(_e zU#(qtnNxR#vlVv6R2+o!@D+Rux8gqh9PK<(n@$(JA0NcWa1P#}?ZNt&#EMu8TVonN zf|GD2+WB-=X9FI?vzSNQo3&TOM%WH}<0zbqcAU}1dk5`&Bg^OTcg&W>A6^qXV^186 zBXK6~!o&Ct{)(Bh`gL<-S!{vrO!ZAB+j$64pL*VKoQN}Vo~iyTaD%DkdC$zQ@8gh< znyT{+{=o3x$=7A`+Y4cFQ{z|125868Y`7giGsl>k-cx2yedm|F*wpmwd-3KL+-a)c z=j7A)BVM3An`&FX(pcZrbXuC%>$o&I)l~i7UA+Sz5C7lo;Q;Ar||`R$yC4B z$g9cklHVuq!=ns8WvcEaawa`j*nDR*HM}a`Wvc#tW$#cm|$SX~a zx5m_RZe{px{Dk30$S3hE!+#?GZmLcR-RCwxWlim*Ymjd@HD3+Mt;y}lUCDj%AyeZ& zL!N~%Fnl@rP4Zsy0rHpRZ}CS{%Xf)vZzwDmFg0IAO-=7MQ}f@{EUt4e$;qbrrI81a zhmap9k0DPXKTDoRev$ktc@_B`{187jOX_h(K5J_F-;*zqbL+K(&2M2-?WIkPSI*S( z)n<4-a&vMAQ~mnmqYNKMUPykKyqf$j`51n~@blyxdTd+WYSw(99Jvm;KDiaS3#OTxpW)=u zIG*8l9SWP?0`eO2R#WSLx2fei#PBao)%%+EKNudZeTvnw>oVAQH;@aP>K|vSPNG>( z+b8)>Q{#0ar;r~eKS6%V)Od@@uiI2X6z z*J$r)tWGuTfWvV38K@Hb(mN(;cJ}0@Z`Kiu*Bey1ZCqF zsre~`l}){ANg&rZ^=0(7udi+9z**Sv!TAyDyQ)6I(oOL9XG=@J{PGb06Q*{=Y zhqWEhz8>FY_y^>@_^GM!{x;j`GjhGwu;~}WN*HUZZW6hkc~tv4cG<=@nFOrv#-ym-=RsUo1Av}R!)BZF0cXDZ+YGC6vH#MJa&5oYejoio7at`N&1cWlSws6^7TwdbBsE zy#vF$nwm}u?L!$pg8U@;X`IgRdE{lL#($UmK6x+sFn(@oeovEsF*V*rhG&aW)^g>= zB3RPYcsG-)n;Nec!<#U?4R*%+XzxkxXKK9XOs$`}<~=&jMSj!N^f!>VlRqLKB!7+H zGW>VEuC!k-4m+5d&b?+Aebblxkg4fBLVl9`6nO@D4*6yBa`FaK^^W0>4FApSs_UNX zHK0v5uc_(WNRA=LVoiqEBR3{@G*#zeoXzlsX0rBM@65NB|WA4g+znYkW&)`b@ z3UAbV5Ubl7AHgN2?zh*>{+{=Nsrf#Lr|=iNWU7CSs=kd)jhAd{yuRe&9k?WG1;T;UWi=1K(^}L=8f0W^)$P>xW;!K7wB(E@s>hnd0Z)f<&xS!$2$Y;!< zdi}}ptcl8+?>wfKr>Lp+By7U)cH}g2e;my4QRIpEw0T(1aHVi!R-wH5BVsb zFc0f_mi()!@e3sRmNqrN70Au84^G9m@d(-%daTYZ*a9EKxwsX7z|6J%@oq3R-6E#8 z|2wfO4#6jIf;n94f&7xG@t2ZUlh={oCx1x(5BU)JEAkog&*aK={JOQVo_SRJ5poAp z^U>MVdhNyVLHG!cq*OikxohW~BvTpQe`Ij+t4% z0;X~?Q}a{CoT~eWTo>;^{XC58-_KOPC&*LCbI326S}!ZfZ<(t9p82efo0E^y{vDoY z_+_$PJjlk&jfG9kZwYb*a-6BUHBC*oF~i%CJCnO(FH`-;;A~updrVFDusK7=UC2M1 zn*K$y9b2@V%~bz<Aqz$aBaG$jiwq$s5R9P0injrfr7|KY?E} z{0H(yjB4VKR|+eeT8^4nkKrB2_hC;QNc%+cvpAFCi^(g=TgkieARedvcg)(?y@eBOb)cs{&n2DOL*9E3>QB&)o6uBa~8u?aoU2+R@ z8}dEmZsb1X0j8!i2ItWJvN>0eU-D*C)89tkOFlq8PCiBck$i!ixrN5pa@4@vP1S2i zZcT1a?n>@K?oS?SYQ83spTie$Dedo&x8WWxx*hXpQ-sShGiID2RoWtKgs4o z9gikIVrsmRO?A!?&6*>HQx0b5nJ`B7aZ*nfxa?sX#+oVrn`qF@^Si<`TWHB#$>W{mJB+cen!PNY>BzM63FopJqa0b4K=P^fnzfMJLirw)E zd=cNlqj<^Gd`EYX%e0-F$|X$Ae;INWa&_`;t1JSIwi?B zk*kv1knbUPBljT>HZ{NF$y0GQE~0%Mc^CO>{F&jGG3#A^y%?-wYWZtneTFwSb$@nb zcvrk1`_ldpd6cR79Lw+pxQzBSwC^S##3KwpL;jhZvy(qx1+a*z`sK-SSF_xSaj<6xX=YC7}CuaMW0*PEK3BY2YbbNGv?^;Ys;-^Mr?7vZOvwTs^`4)4an z_%yzQC$Y$V8tvx05-_m}beA!&9&$qft&F_t-=C2sJy!n>iACT+O-h|xFT(8#|^&o(CbO^^R&++FEuynd2`bDNvA-!-*d zx0qY>{W5YxQ+1k=+mkz!dyspP2a|`9C*Tr%gZ8&fP3Jg9CHvFA-uy)0*C3ZNcWL_| z$C>YIKS^$AeysT;cQkkCd&T7Y&F!AM+4rk6>n}4W=D~tk6iZ_{Ou*Wx!vwC5-j+G_ zaCd66cJ{#g@nL)fN8>o0VP^NQwQMdVFUD7KCBB8)WZZbWaUUK+-KDPm6rROD@Gs1g z$?vDj5xf3*QKz1`Tnu$NR+r zmoX~ZA3rM=#^P8OE1>;el})cUw!}8r!_2Sk0sG^FI2<3tXK*^sK|7zx>b!<;;CkGM zyKpalibwDyp26SoPt2tqhfOChR>xXs*VVE1=GYm#-~*<1G6V2od<37sCvh@P#Ra$s zm!th|kJVj|8*wXsh#%vB@D!fKAMqE=qJ5;*y$)}{8?gkI##pr9qp|U7V_j^5E%0u< z4^y!h4nTK)j_N%^ejJ~~i8vLX!@0Ns?e}GDzEN9&D>lbFumg6+ZrB4KLU(?VrZb8>7N?^--$?BX z$cu0du0y-dkInDi z9Ph-tFb#X-KpcWk;zXQ=c0C}Q?mS$GD{&3Jk00O>v$*Gdf#2g#XeYE=duGg!g|H-+ z!FaUav9R%)qB~DY*_|g({_Inc6-<>Zd?ROq5&%=ed8sEZq z@jcvuyYVm{!|%|pqi*$o!#~iSPo?46uk+jUV16utrLhJkU|np8EwK%D!h5j?y7RI$ z-2vpm_$a#bwbVYIJPD`cOk9eu;u>6sn{g}d!o7G1-FaP_-bwNq{1Jb_%lJ3C^Sv}) z&K&;wD~6@8JXXXySP$D`2TV1~=<{D3fP?Wdd;+K9bLh?=)A$R>ui$IA5x3xnxC{S- zpW@f}4W7pf_$Ox4Ya^TQoR|koVi~*ztDrmIOye~rKZZ}>1e}a>aRI)Lt8oKv!aev2 z9>Fj0Yy1ZPz`rm{E`PbM!@TIuOVfN8BbUPRSP_%Zowug`jmXXLPITw7sokBwCVMk{ zI6j7BaRSc3+4wTPf^Xpl+=+Ye7@oj0_#OUgJrQ2R>RxPa{4?Q z-T7_GcaYtAZpxj>ebAlvrr{5fhvO(5i_ha6d=cGwaTE{0@J_ z-|!F2q}Tekd|9v%7Q;&BEu3#BYmslo`q&sd;k}rGJ+U7S!ZA1=pT=i#Hokz1(Vf?) z`B_O`gPURlqc^Zt~h^+w*7KN}Xo5?B^1U^T3Pjj=i2ZC26w zw3v#$Z~zX*CvhT9MR#7H>dYm(^8=ONAis(B`_DGLE%+hsLU-Pv+K-aI!qfOO{)Sof z-plG;hk3C87Q<5L&MQ>^n&diI58Zi&YHvxt54+7JWj$F@I_pPD{u|2 z!=1PX58z=ujwkU)`~{cnem?TG$j@Vted_-LMBffCF$SK8j;- zJkCUSK4qMK^NG9^SK=Cc2RGw~xC{5AI}bBX-`602hd-k`A5-nw^ZDy57Zyi%UZ&b( zE!XtCc&v?e(Vd^E_KxH(n2ZnN!}u6JfzwSLbeoCu@g-b~>v1z~#e-&Do$rq)@D%=L z*4KGrm`U&5EoZ?(W4c>&cLPkDz3)2 za0l+j19%vZ<4ODlFXG=AUBI8u{AN?nKTlPbB)jufmE*~YSQi^&N3*%--HpkZiv96H z9FC9Sc$|dK;`3;~k8Sl=;2K!3iif+I1Fj1YrKe;(T;Uk z{j69Ri(_T;4(%r}0c&GpY>sWQ1NOu|I0zrY@i+;m<4l~7FX3u@3*W`}aG%*mzqyV_ z@i?BwZ}AFxh5YfdVNT441+fH{#u}J_w_^kBfSoZFd*KKih2wD&&cXTkGQNVV@h#kr zJMj}dh{x~*p2rJlzu#`_&n_Hg<~7^tdlpy>OJN19j5RO;n_(+#kM8_y)$L90hY#U! z9ED?XI?lu;W_z8dfp6fOxDmJDhqwzr$1m|Lp2NS;jxE`AuEX3|3U9)SSQTqx64t{; zco(|!z*Rqm+!OobgE$(;;S8LO%Wwt0gPZYV{11MHpW`{RqmKXKMZAnr`e4o4vtn+% z0dK;au_{)_M(EBbSDiNG_Lzq5d~&r9BoDzyaU_nzr*IxF#AUbw*W!BIfxGdj*-4*g z;%WRAFXP{swV2=Eou97pZXn-?CDEOyuJ$-`O?2n0Yj{1fJ8xaNCAlNsjlHlh4#uH4 z5vSk`oQ*Hy%eVqp;W~T=-^UN|WBd<(g{Sd*{0TiB^R)Gu8FOMDEQm!h9uqMcQ}F>D zfD>^FK8Le#DZYxU@hyB8-@|=)0FU7bJcHk%J6~SQ_Z#^yj4I(TcRnnLCD5H$ukmV= z>*D?R06vKm@l|x^&1?L($?xJe+<~9qK|GEp@f@DVKkzTiqGOeI|6PaqFgPE-0=Y8A zV2T>R*st0!w2o#$!WlitVuzCSxi-fCF$8j>YLX6BpnjT#m1!JO5wv`yP28 z9>A~h8$6E}FiQ;E0~W=SSRN~465fVQum#?W-LNP2!QuEAPR6PD7H+^#@F0GUU*b>Z zJ^FqZUO}(4KRgfS$D&vgV=*3UV_j^A9kB~0;{daZ=MBb3@Nt}i({L8f!`E>&zK!qV zUfhpI@C*Fj?CNn>=1c9HkuVLXQC@H}3` z%b26Azx>x@K`e@;u^h%@A~wTT*d9A!cTB?}I1Jr&AG91}$dhp@&cwO60$1T?+={z! zFMf(g&|Mco)BB!$1-)|qddP$Mu_%_rSajEkNYm#z92KE2dy??1v+86plxCy$jWSmi#=vfG?uE9)|j@BX7d@@e_2{%TT}1$zS4m zynvZ+_LnC+=E3|}6iZ?(#$#=)i>>iaOu?Qw00-kE_&AQoNjL}R<7@Z^zK!qVHr#=S z@F;$ZKcKrlh}P5ZI{y#?j_bU0(&K;53|r^Klujz&G)2+=AP1 z4}OA2@i@Bc#%TH%$d~bN%wEZ#zg$=n%it|o1>-Rh8(1KVL|?1DY94-Uu2a4b&1 zIXEBP^=Y&`%gJxxo466T;9lI1C-Dqk#LJkovcEp^U_mU3rLi2wVF-MUhIYs-~fCW-Sveu{W0Y6_&m?z8%|Od%PFBVNdLX?m9@S^B8$DPQ}^y0xrSj z_y)d-oA7pE%q+mr9X zt~dY(<0JSuPQhvTBEF0(a20OGt+*HW;}QG<&)|3X2mXawV*T}T9TvvoSQaZ_HLQUR zunBg=yD=H{bAImeIv9uISe$^*;B=ga3vnf`!Hu{DKg3;l01x9g_&xrLzoQrD&u3=L zkA<*2R>U~0iH)!sb~10 z!ynOppv>k!Gv>rRSOiO;yKb2JS0Kk?Jl>8CusPm=o$y{v!Jaq_N8nhTfU|HOF2-fJ z9yj7v{1Ery0X&Ik@HhMeGu80dPZrFDd9etVz_RGBi>CFIK(37qunFFQ?eHG#ifPyz zhv5hugX3{3K8K5NDZYVk;zrzpAK}M%5I@6{cm{vKpD}a1zrM0#UMzs_`fs-VkmIl> zHo+En7v6)turIpn#;M*Y@>F~dU&1B03Eg$%G~RCVK0Jg+@oW4B&*KIB3++2f_Bbkl zg)s)pVmu~dU2KSV;$3(jcE>*0ABW)x9E0QW8JvzU;ET8nSKwQ?0pG*zcnFW;*Z2*d z#|!uuM%D7yPcC%V&C~iSL@tKq@D@zOI@knTU_0!H-LMDt!$CL7OJg~#hBfe3ydB+j0JWZ5k-MV14xolV zKpudja4b&7ski_a;j6e3-@*;}K7N3o;t~7`PvfunJ7!As*Gm?>5sP39mc^Rrt|zGF zYe8;}T`>jS^#s*#D0u=-#<{ovSK(URW{%MBZ{c3tk6+}z9T#0M&9o&q2&8PLdWOx*h z<7xaBf5$&D*R2|Vs=nugg|Rr^gg0YVtd0$^3EqM2urqeSNAPic8lS}%&1ZG}W?X@* za2>vbd+-xHjK}a4p2bUe1*2>G)60hLI*^)v3Gyvi1(WbLY=<2&6?@?T9E_uJ96pWD z;%s~YSKun#V$RTYvv3#g#n13_JcVcRB3?%OZ3?TG74u>NEQX~p7UR)fpHk!3B{#=A z&|Rle?e032vNyx~;Sd~#?s}E#H-1 z=&om}@$%i~ucsnd0?T7Xtd6y?3AVs4n2ZnN!#Evh;(UAwU%}UK6TXk``k0#EgXGWg zOFWC`@G}05d2aWYGd~u?Qdk}iWxhJ-X|4YQ0q;SH{{{7dv2QOvY3kY|hs69X^huaT?CRdAJal;;XnC z-@@&<6Zhc(Jc`HhG=7V}qi59c+nhuoK>k_u~UN6d%R$ zI0NkKq7$3tYZ~{)o zxwrtA;Bs7x>v1z~#of3MkK;-F4u3>;zwqq|co09svv>|K;3fPUqnr5aB_9^V z@>mh8V=cTL8{nOI7v6{6u^0BmM{y*M!>4c>&cG$O9N)kfO> zG+x3h7~Rb8pACzcFX{aimcIwe9L~Z8xCqzcdfbd#@jv(}evV(_ zdAxvE&}-?>S6(cD#W4nBF&=B9yPmX`t2Oyf?1A@Ve|!+f;COrnr{e-#ge!3keu%qp zKf3EwYks~Ue}&)T5BL{Gwepw$dd!EVu^h%@A~wQi*a`2&KG+{0!r?d?$Ke#5hO=-U zF2bewI-w+?(7F-F34yd<@xLKU?`}@+_Q(i*PBvj;nDS?!djcAKi7fRsT!!Sv-fo;6?lk z-F3G$UV+yB`YDX1@FuK@Nmw5nV@JFjlQ9)5xAE)8;w8L-(QWjpy+KUdF#Mdpm!Aa-q9!wdSJ?`4+5#weVJKfKBi&ya&5u8urHr zaX3DPPvf&V8(+ZX_&RRDO}HNq;TQN7UcgHjb*I1lSur=>fW@#BmdA=1kBL|p8)8Si z8wcVLd={U_`S=pPf$sX;x?i@Fcj6~_5KrM*yo6UUOM8ELufw8P5-VV3tcAB?eQb=K z@Lo*8p4blu;UoAsK7-S74$jAy@fBQ;8*w}C#C>=GPvRLoj~DPV{*8G%`0J+tmPB{G za;^Vc$nLu3$_eD!=&oC?;Z4YGu>;o0g41v=F2E(Y9N)psxEJ^1 zm-scF!}I8M^w)>GzPgqxAGsj9>#A#bb#i@djCY~C-n#ldL>`V4aSA?%vv2_h*Ii#h zUWM=D2e=Op;88q|-{ALn0WV?HUF;7qH{O87uoRZZiWrZHSQi`O9oP;#V;Ag+eQ*#y zgyV4%&cy||1earQ9r$(R{dfq!#INxjp2t7%FU-=3{Rig9LRb>ZU}cQO1gwpBU_0!N zX*dLj;dq>c?)viDuAV0^z(u$g*W-J*9X~~Py?NF7lKeIPh`-<;_!s8w%>Dw4VJSR= z-{CuV`~5fLUfho-@eKZg7x6E&a}@1$-cWoLbKUC?&x?hy7*@opcpKKow%7rCVjp}2 zAIB*;4d>u|d>LQC^|%qY;SM~6NAW8>jlY`9_5K*WE`H981y-ZFlGAK_7Ry{<2Sr}107 zY;Mr+$Ya*~{G0=en;ShZ25-hnc$>LNztf4$uob%N?yEh8+#CDhNF0Mt;nTR#+@jwP z#aD49Zo>C*C+@*xcmmJhcla~@hS|IN)60eVu@JiJ^=p1AlCyg1e?Z@7FtfPJn7Hel z$5_s(&-cvhb==idr$jTq&OzkQW?7%Ttupml%X(AC*0-2CR=UH~v9o=qj)@#L z^`7{IsrNc(O}*DRZ|XJfB~!0$yi8KhaoJ7nyYiaaXB0NIEyb8xM-|LuEx(zf$w{v)rc z{X}6?`-2!$E4_lL$9Jr$$8n;m$7?-PcSbW)kH5C&MlHX&Ny~3;(R6gP?Its3$6Q$0 z)OJ)HW3Vh%z{(hl@mLQVVKZ!nolR|@x=WqOn2NoyFAhW9MXvuS9E-XO-1hw(&cb=P z%q*kpPoY+!%UX5LEw~ML;BGu@>Tz)lPv9v$Z)$tKfS2$JX1DuS+j%a`iv=*o)b?H$ zD_~_z#5!0H-R~S|yk_K9*crQEGNxiL?27|&2#&(BH~}Z)ES!f6aWSsKwYVNP;tt%6 z`|tpsz*Be@&*3FgkM}F+*>>YEI2Nbkb2tm<;X+)Dt4!^`*5Z2Hh&ymM?!yCk z7?0suJcsA;0$#!^=tcX}&y0Do0J`5}(EPdIW03B57-VJIV=*2Ru?{xFR@fFhU^1p+ zFYJp0aR?5>5jYCR;#7PNXW=|th>LL*uEq7Z5qIEj+=mD7gsJD9Q+O87;U&C+wxhQG znK3)M-+|C@_d5`>0K*GoajbxqF&5)75$j+xY=v#H116i=8K+_|?2E%p?UYC0C>)Cu za5By^_53*x7vf@EW$Jl#Ew0CnxCOW2K2y)P2k_H{urDhC6UK9>!yM0#D(2ynvVR3TDr$^`XCWVO}%4wr?zK=G6WLV@$ms zE{he+yxQMjtf|+>@tA1p^-UeDXBN@^9-EoPb$?)6v!v&BK=->BWwigdER)T0dOTn+ zQ?IN0;y_ccuZQ3;Q?DmR;3!kCx5wfHGeMtU;#4z9->=45W*t3VaG|N!EsTW>?++7-J^u{>KVtitc}mHPdweW1`th z_dnJ%_59rko0)pw&r28KintDI77?+va|E|DQruMIEalNVi=|5+n?fLbD{2kJYl}1`ybDmi*^6wd2@;Gf4pRBKXwJ(?~~~Lj{AKQnO%={d*71_ z^O}19QveH_t91WkjH&lSWwC;(7x$Gh*3|o>cuX|c>;A`jrrs|#!e-`1-T&Cu+@$*- zJDXc{|D&F!y{IFaZ}W4_r+L)#VoMLFEovcmQi#q9f{m6q+o0#jbP06WE#;HxiISpsx3%Ceh!Ik(XzJu@Khqwp#<7cQv zant)6zr&yKBL0cdnf(6l_uaI-dB`_nF)WQYV->7{Nmv&fV@qs@o$x+%zyGG`_9hR& zhwxE+0>|SNoQ|_`0lMFTQ{7j|Z{Rw77q{Y0+>3|tIDU=pci>d_C-O!76QiU2^_c^2 zz@p|Uea>#`&bh@rt?PHveye##zf(cZ!u^vIZ@@xW0&l`#yRJsoQ<&QhYGZwDiW#T_!y4CiTDg^m*%EF7hlAs_!_Rkw{bIW$6fdd9>UL24@EbfZ}3O_ z6)$5Z?w@R!8}nlkEQRH;62@Ty-i8gaIkv%$crSLx`>`Jm#^E>;pTtQx4QJvDxCmdt zmG~yUgYV&oxCi&+XZU}*yiVptk)#wu6?ldvu}#+KL)JK=rU1AF5Dd@_#H08np26?&7yKRn#w^kPcAXP%z(QC8Z^B!! z8rDSjJBxaJ*C#i{JFq?8jomN}AHYF46d%JeI1!)088{bT#HHwdmr?VzhWs{e#_hNZ zKfy!zIiAFC@JIX=FJmTqKdI?u!`zr3i(n}%hm|l66Yw@{i0=0tRlg0nBi@VM@qX-w zgK;>HME84-syB%|4QJvD=zjN6{azuj#5eIBd=EdwJ-8n~!{hiheuqEdMf?+^^+aXs z)%_l%%tLm+2dP|)TpDl2Dp&)P(ETo?#%oM&iS4ix-iJM~H@e@4)c6mPAH^qdJWj#s zI2+yXLTdb%$*;@dhk}CGaM^ z1>NsPYP_1{+E^c(;vLu?@5XMJh7aH%9Ey+O7@UaD;0&CLFXB>s4cFk?xEZ(OF8l-! z;pcb~zri2zSGC& z_#{q3_q&vu{!H=R*`A>|t_gxx( z9p=Fsu^5)do3RSkz$C1Tjj<)R!%lb~_Q2ja03X6f@d+G{Q*b)YMm_xe#~ohlb@%^n zzniF25#uls>*D`Z?|*9lzkNT@n)&F6I>h7FLl5kO!TW$=@WWV(@z2{eHRn2k%=~(7pyYVDLKLeQu}z!TZ%u zY5yGE=XGii-mm`5@c-Z2(f_OZ|F7!QW_zfQO)>obSBE&=_Vqtaw-QAx^|X%H-)WeUDw}C(eE)_C(}G{q1j8n zlVR%MNc9}Dzvrcy+Br@&bv$~BIn?v|XkFUz=Nog$5juau)K1g=-s)%__p4*Y-ha&5I*x13)%w$YWygi*nG5+{&6jkX*z#ia zGneS~Lms)z^WN2cYTs-5*j%CIHDA|pEpwIb=NqJsZqRWf zbEB5i+@$@TxrO7dXZgOA?sNBir>wiyeSV}>?L{dkn_5Ss$YV{->q4?--EDtc$lFY9 zho{IY;kHM&zgNhfNm<)vSyQ=!SzO1H$<0h{r|x?eYIpl-ZLjY86Uqy%y&T^gQg+{q zxJB!a@!j_ws(AkQf7D(!O8w(>eA-lQYihe5LLO#n`*zT%;f$5(q_%kA{NEZRqydK}FoFEsUd+CtuD>Tz|Re8JS?t3YO{dWB8h`ELK9 zoM>5(x6ZV8F_Set@(43U>y7L_hfdSup6otH?xp1?ds)=~0WF`Y`mttz)hBl`2kG-2 zvim$%kJn}76{a4yC&=z|QayeP*#5@m-yEUsm|V})n7eWvz5$H*s4?T5~h&zssGT_Jn6U)Fvpmw8t6 VZEn=|PmVFQpQ=obHMe-){{q?^?Kc1b diff --git a/libraries/BLE/utility/uECC/uECC.h b/libraries/BLE/utility/uECC/uECC.h deleted file mode 100644 index 9911763..0000000 --- a/libraries/BLE/utility/uECC/uECC.h +++ /dev/null @@ -1,362 +0,0 @@ -/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_H_ -#define _UECC_H_ - -#include - -/* Platform selection options. -If uECC_PLATFORM is not defined, the code will try to guess it based on compiler macros. -Possible values for uECC_PLATFORM are defined below: */ -#define uECC_arch_other 0 -#define uECC_x86 1 -#define uECC_x86_64 2 -#define uECC_arm 3 -#define uECC_arm_thumb 4 -#define uECC_arm_thumb2 5 -#define uECC_arm64 6 -#define uECC_avr 7 - -/* If desired, you can define uECC_WORD_SIZE as appropriate for your platform (1, 4, or 8 bytes). -If uECC_WORD_SIZE is not explicitly defined then it will be automatically set based on your -platform. */ - -/* Optimization level; trade speed for code size. - Larger values produce code that is faster but larger. - Currently supported values are 0 - 4; 0 is unusably slow for most applications. - Optimization level 4 currently only has an effect ARM platforms where more than one - curve is enabled. */ -#ifndef uECC_OPTIMIZATION_LEVEL - #define uECC_OPTIMIZATION_LEVEL 2 -#endif - -/* uECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a specific function to be -used for (scalar) squaring instead of the generic multiplication function. This can make things -faster somewhat faster, but increases the code size. */ -#ifndef uECC_SQUARE_FUNC - #define uECC_SQUARE_FUNC 0 -#endif - -/* uECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will switch to native -little-endian format for *all* arrays passed in and out of the public API. This includes public -and private keys, shared secrets, signatures and message hashes. -Using this switch reduces the amount of call stack memory used by uECC, since less intermediate -translations are required. -Note that this will *only* work on native little-endian processors and it will treat the uint8_t -arrays passed into the public API as word arrays, therefore requiring the provided byte arrays -to be word aligned on architectures that do not support unaligned accesses. */ -#ifndef uECC_VLI_NATIVE_LITTLE_ENDIAN - #define uECC_VLI_NATIVE_LITTLE_ENDIAN 0 -#endif - -/* Curve support selection. Set to 0 to remove that curve. */ -#ifndef uECC_SUPPORTS_secp160r1 - #define uECC_SUPPORTS_secp160r1 1 -#endif -#ifndef uECC_SUPPORTS_secp192r1 - #define uECC_SUPPORTS_secp192r1 1 -#endif -#ifndef uECC_SUPPORTS_secp224r1 - #define uECC_SUPPORTS_secp224r1 1 -#endif -#ifndef uECC_SUPPORTS_secp256r1 - #define uECC_SUPPORTS_secp256r1 1 -#endif -#ifndef uECC_SUPPORTS_secp256k1 - #define uECC_SUPPORTS_secp256k1 1 -#endif - -/* Specifies whether compressed point format is supported. - Set to 0 to disable point compression/decompression functions. */ -#ifndef uECC_SUPPORT_COMPRESSED_POINT - #define uECC_SUPPORT_COMPRESSED_POINT 1 -#endif - -struct uECC_Curve_t; -typedef const struct uECC_Curve_t * uECC_Curve; - -#ifdef __cplusplus -extern "C" -{ -#endif - -#if uECC_SUPPORTS_secp160r1 -uECC_Curve uECC_secp160r1(void); -#endif -#if uECC_SUPPORTS_secp192r1 -uECC_Curve uECC_secp192r1(void); -#endif -#if uECC_SUPPORTS_secp224r1 -uECC_Curve uECC_secp224r1(void); -#endif -#if uECC_SUPPORTS_secp256r1 -uECC_Curve uECC_secp256r1(void); -#endif -#if uECC_SUPPORTS_secp256k1 -uECC_Curve uECC_secp256k1(void); -#endif - -/* uECC_RNG_Function type -The RNG function should fill 'size' random bytes into 'dest'. It should return 1 if -'dest' was filled with random data, or 0 if the random data could not be generated. -The filled-in values should be either truly random, or from a cryptographically-secure PRNG. - -A correctly functioning RNG function must be set (using uECC_set_rng()) before calling -uECC_make_key() or uECC_sign(). - -Setting a correctly functioning RNG function improves the resistance to side-channel attacks -for uECC_shared_secret() and uECC_sign_deterministic(). - -A correct RNG function is set by default when building for Windows, Linux, or OS X. -If you are building on another POSIX-compliant system that supports /dev/random or /dev/urandom, -you can define uECC_POSIX to use the predefined RNG. For embedded platforms there is no predefined -RNG function; you must provide your own. -*/ -typedef int (*uECC_RNG_Function)(uint8_t *dest, unsigned size); - -/* uECC_set_rng() function. -Set the function that will be used to generate random bytes. The RNG function should -return 1 if the random data was generated, or 0 if the random data could not be generated. - -On platforms where there is no predefined RNG function (eg embedded platforms), this must -be called before uECC_make_key() or uECC_sign() are used. - -Inputs: - rng_function - The function that will be used to generate random bytes. -*/ -void uECC_set_rng(uECC_RNG_Function rng_function); - -/* uECC_get_rng() function. - -Returns the function that will be used to generate random bytes. -*/ -uECC_RNG_Function uECC_get_rng(void); - -/* uECC_curve_private_key_size() function. - -Returns the size of a private key for the curve in bytes. -*/ -int uECC_curve_private_key_size(uECC_Curve curve); - -/* uECC_curve_public_key_size() function. - -Returns the size of a public key for the curve in bytes. -*/ -int uECC_curve_public_key_size(uECC_Curve curve); - -/* uECC_make_key() function. -Create a public/private key pair. - -Outputs: - public_key - Will be filled in with the public key. Must be at least 2 * the curve size - (in bytes) long. For example, if the curve is secp256r1, public_key must be 64 - bytes long. - private_key - Will be filled in with the private key. Must be as long as the curve order; this - is typically the same as the curve size, except for secp160r1. For example, if the - curve is secp256r1, private_key must be 32 bytes long. - - For secp160r1, private_key must be 21 bytes long! Note that the first byte will - almost always be 0 (there is about a 1 in 2^80 chance of it being non-zero). - -Returns 1 if the key pair was generated successfully, 0 if an error occurred. -*/ -int uECC_make_key(uint8_t *public_key, uint8_t *private_key, uECC_Curve curve); - -/* uECC_shared_secret() function. -Compute a shared secret given your secret key and someone else's public key. -Note: It is recommended that you hash the result of uECC_shared_secret() before using it for -symmetric encryption or HMAC. - -Inputs: - public_key - The public key of the remote party. - private_key - Your private key. - -Outputs: - secret - Will be filled in with the shared secret value. Must be the same size as the - curve size; for example, if the curve is secp256r1, secret must be 32 bytes long. - -Returns 1 if the shared secret was generated successfully, 0 if an error occurred. -*/ -int uECC_shared_secret(const uint8_t *public_key, - const uint8_t *private_key, - uint8_t *secret, - uECC_Curve curve); - -#if uECC_SUPPORT_COMPRESSED_POINT -/* uECC_compress() function. -Compress a public key. - -Inputs: - public_key - The public key to compress. - -Outputs: - compressed - Will be filled in with the compressed public key. Must be at least - (curve size + 1) bytes long; for example, if the curve is secp256r1, - compressed must be 33 bytes long. -*/ -void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve); - -/* uECC_decompress() function. -Decompress a compressed public key. - -Inputs: - compressed - The compressed public key. - -Outputs: - public_key - Will be filled in with the decompressed public key. -*/ -void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve); -#endif /* uECC_SUPPORT_COMPRESSED_POINT */ - -/* uECC_valid_public_key() function. -Check to see if a public key is valid. - -Note that you are not required to check for a valid public key before using any other uECC -functions. However, you may wish to avoid spending CPU time computing a shared secret or -verifying a signature using an invalid public key. - -Inputs: - public_key - The public key to check. - -Returns 1 if the public key is valid, 0 if it is invalid. -*/ -int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve); - -/* uECC_compute_public_key() function. -Compute the corresponding public key for a private key. - -Inputs: - private_key - The private key to compute the public key for - -Outputs: - public_key - Will be filled in with the corresponding public key - -Returns 1 if the key was computed successfully, 0 if an error occurred. -*/ -int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve); - -/* uECC_sign() function. -Generate an ECDSA signature for a given hash value. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it in to -this function along with your private key. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - -Outputs: - signature - Will be filled in with the signature value. Must be at least 2 * curve size long. - For example, if the curve is secp256r1, signature must be 64 bytes long. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int uECC_sign(const uint8_t *private_key, - const uint8_t *message_hash, - unsigned hash_size, - uint8_t *signature, - uECC_Curve curve); - -/* uECC_HashContext structure. -This is used to pass in an arbitrary hash function to uECC_sign_deterministic(). -The structure will be used for multiple hash computations; each time a new hash -is computed, init_hash() will be called, followed by one or more calls to -update_hash(), and finally a call to finish_hash() to produce the resulting hash. - -The intention is that you will create a structure that includes uECC_HashContext -followed by any hash-specific data. For example: - -typedef struct SHA256_HashContext { - uECC_HashContext uECC; - SHA256_CTX ctx; -} SHA256_HashContext; - -void init_SHA256(uECC_HashContext *base) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Init(&context->ctx); -} - -void update_SHA256(uECC_HashContext *base, - const uint8_t *message, - unsigned message_size) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Update(&context->ctx, message, message_size); -} - -void finish_SHA256(uECC_HashContext *base, uint8_t *hash_result) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Final(hash_result, &context->ctx); -} - -... when signing ... -{ - uint8_t tmp[32 + 32 + 64]; - SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}}; - uECC_sign_deterministic(key, message_hash, &ctx.uECC, signature); -} -*/ -typedef struct uECC_HashContext { - void (*init_hash)(const struct uECC_HashContext *context); - void (*update_hash)(const struct uECC_HashContext *context, - const uint8_t *message, - unsigned message_size); - void (*finish_hash)(const struct uECC_HashContext *context, uint8_t *hash_result); - unsigned block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ - unsigned result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ - uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + block_size) bytes. */ -} uECC_HashContext; - -/* uECC_sign_deterministic() function. -Generate an ECDSA signature for a given hash value, using a deterministic algorithm -(see RFC 6979). You do not need to set the RNG using uECC_set_rng() before calling -this function; however, if the RNG is defined it will improve resistance to side-channel -attacks. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it to -this function along with your private key and a hash context. Note that the message_hash -does not need to be computed with the same hash function used by hash_context. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - hash_context - A hash context to use. - -Outputs: - signature - Will be filled in with the signature value. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int uECC_sign_deterministic(const uint8_t *private_key, - const uint8_t *message_hash, - unsigned hash_size, - const uECC_HashContext *hash_context, - uint8_t *signature, - uECC_Curve curve); - -/* uECC_verify() function. -Verify an ECDSA signature. - -Usage: Compute the hash of the signed data using the same hash as the signer and -pass it to this function along with the signer's public key and the signature values (r and s). - -Inputs: - public_key - The signer's public key. - message_hash - The hash of the signed data. - hash_size - The size of message_hash in bytes. - signature - The signature value. - -Returns 1 if the signature is valid, 0 if it is invalid. -*/ -int uECC_verify(const uint8_t *public_key, - const uint8_t *message_hash, - unsigned hash_size, - const uint8_t *signature, - uECC_Curve curve); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _UECC_H_ */ diff --git a/platform.txt b/platform.txt index a1d2634..280a3ae 100644 --- a/platform.txt +++ b/platform.txt @@ -40,7 +40,7 @@ compiler.cpp.flags=-DNRF5 -DNRF52 -DS132 -DSOFTDEVICE_PRESENT -DBLE_STACK_SUPPOR # Compile includes -compiler.nrf_api_include="-I{runtime.tools.CMSIS.path}/CMSIS/Include" "-I{runtime.platform.path}/cores/arduino/components/device" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/hal" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/clock" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/common" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/config" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/delay" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/uart" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/rng" "-I{runtime.platform.path}/cores/arduino/components/libraries/util" "-I{runtime.platform.path}/cores/arduino/components/libraries/timer" "-I{runtime.platform.path}/cores/arduino/components/libraries/trace" "-I{runtime.platform.path}/cores/arduino/components/libraries/scheduler" "-I{runtime.platform.path}/cores/arduino/components/libraries/uart" "-I{runtime.platform.path}/cores/arduino/components/libraries/softuart" "-I{runtime.platform.path}/cores/arduino/components/libraries/fifo" "-I{runtime.platform.path}/cores/arduino/components/libraries/ecc" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/ac_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/ep_oob_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/hs_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/le_oob_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/generic/message" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/generic/record" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/launchapp" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/parser/message" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/parser/record" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/text" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/uri" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib/hal_t2t" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_parser" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib" "-I{runtime.platform.path}/cores/arduino/components/ble/common" "-I{runtime.platform.path}/cores/arduino/components/softdevice/s132/headers" "-I{runtime.platform.path}/cores/arduino/components/softdevice/common/softdevice_handler" "-I{runtime.platform.path}/cores/arduino/components/toolchain" "-I{runtime.platform.path}/cores/arduino/components/toolchain/gcc" "-I{runtime.platform.path}/libraries/BLE/utility/uECC" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/pstorage" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/pstorage/config" "-I{runtime.platform.path}/cores/arduino/components/libraries/fstorage" "-I{runtime.platform.path}/cores/arduino/components/libraries/fstorage/config" "-I{runtime.platform.path}/cores/arduino/components/libraries/experimental_section_vars" "-I{runtime.platform.path}/cores/arduino/components/ble/device_manager" "-I{runtime.platform.path}/cores/arduino/components/ble/device_manager/config" "-I{runtime.platform.path}/cores/arduino/components/ble/ble_services/ble_dfu" "-I{runtime.platform.path}/cores/arduino/components/libraries/bootloader_dfu" "-I{runtime.platform.path}/cores/arduino/components/ble/ble_advertising" +compiler.nrf_api_include="-I{runtime.tools.CMSIS.path}/CMSIS/Include" "-I{runtime.platform.path}/cores/arduino/components/device" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/hal" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/clock" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/common" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/config" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/delay" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/uart" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/rng" "-I{runtime.platform.path}/cores/arduino/components/libraries/util" "-I{runtime.platform.path}/cores/arduino/components/libraries/timer" "-I{runtime.platform.path}/cores/arduino/components/libraries/trace" "-I{runtime.platform.path}/cores/arduino/components/libraries/scheduler" "-I{runtime.platform.path}/cores/arduino/components/libraries/uart" "-I{runtime.platform.path}/cores/arduino/components/libraries/softuart" "-I{runtime.platform.path}/cores/arduino/components/libraries/fifo" "-I{runtime.platform.path}/cores/arduino/components/libraries/ecc" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/ac_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/ep_oob_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/hs_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover/le_oob_rec" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/connection_handover" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/generic/message" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/generic/record" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/launchapp" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/parser/message" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/parser/record" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/text" "-I{runtime.platform.path}/cores/arduino/components/nfc/ndef/uri" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib/hal_t2t" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_parser" "-I{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib" "-I{runtime.platform.path}/cores/arduino/components/ble/common" "-I{runtime.platform.path}/cores/arduino/components/softdevice/s132/headers" "-I{runtime.platform.path}/cores/arduino/components/softdevice/common/softdevice_handler" "-I{runtime.platform.path}/cores/arduino/components/toolchain" "-I{runtime.platform.path}/cores/arduino/components/toolchain/gcc" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/pstorage" "-I{runtime.platform.path}/cores/arduino/components/drivers_nrf/pstorage/config" "-I{runtime.platform.path}/cores/arduino/components/libraries/fstorage" "-I{runtime.platform.path}/cores/arduino/components/libraries/fstorage/config" "-I{runtime.platform.path}/cores/arduino/components/libraries/experimental_section_vars" "-I{runtime.platform.path}/cores/arduino/components/ble/device_manager" "-I{runtime.platform.path}/cores/arduino/components/ble/device_manager/config" "-I{runtime.platform.path}/cores/arduino/components/ble/ble_services/ble_dfu" "-I{runtime.platform.path}/cores/arduino/components/libraries/bootloader_dfu" "-I{runtime.platform.path}/cores/arduino/components/ble/ble_advertising" # Create archives options compiler.ar.cmd=arm-none-eabi-ar compiler.ar.flags=rcs @@ -77,7 +77,7 @@ recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} "{archi ## Combine gc-sections, archives, and objects -recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -mcpu={build.mcu} -mthumb "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map={build.path}/{build.project_name}.map" -Wl,--cref -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -Wl,--warn-common -Wl,--start-group {object_files} "{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib/nfc_t2t_lib_gcc.a" "{runtime.platform.path}/libraries/BLE/utility/uECC/micro_ecc_lib_nrf52.a" "{build.path}/{archive_file}" -Wl,--end-group +recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -mcpu={build.mcu} -mthumb "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map={build.path}/{build.project_name}.map" -Wl,--cref -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -Wl,--warn-common -Wl,--start-group {object_files} "{runtime.platform.path}/cores/arduino/components/nfc/t2t_lib/nfc_t2t_lib_gcc.a" "{build.path}/{archive_file}" -Wl,--end-group ## Create bin From 96dd28fd14eea234289a445b8c0852b028fa33a3 Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Tue, 13 Jun 2017 13:52:53 +0200 Subject: [PATCH 09/13] Modified examples in order to reach loop function and automatically add dfu service --- libraries/PPI/examples/LowPowerBlink/LowPowerBlink.ino | 8 ++++---- libraries/PPI/examples/SenseNFC/SenseNFC.ino | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/libraries/PPI/examples/LowPowerBlink/LowPowerBlink.ino b/libraries/PPI/examples/LowPowerBlink/LowPowerBlink.ino index 6376b68..2bd7284 100644 --- a/libraries/PPI/examples/LowPowerBlink/LowPowerBlink.ino +++ b/libraries/PPI/examples/LowPowerBlink/LowPowerBlink.ino @@ -27,10 +27,10 @@ void setup() { PPI.setOutputPin(LED_BUILTIN); //bind TIMER event to PIN_TOGGLE action PPI.setShortcut(TIMER, PIN_TOGGLE); - - //put the board in low power mode to save power - LowPower.sleep(); } -void loop() {} +void loop() { + //put the board in low power mode to save power + LowPower.sleep(); +} diff --git a/libraries/PPI/examples/SenseNFC/SenseNFC.ino b/libraries/PPI/examples/SenseNFC/SenseNFC.ino index 1ab4569..25156ee 100644 --- a/libraries/PPI/examples/SenseNFC/SenseNFC.ino +++ b/libraries/PPI/examples/SenseNFC/SenseNFC.ino @@ -36,11 +36,10 @@ PPI.setShortcut(PIN_HIGH, NFC_START_SENSE); //Toggle LED pin each time an NFC field is detected PPI.setOutputPin(LED_BUILTIN); PPI.setShortcut(NFC_FIELD_DETECTED, PIN_TOGGLE); - -//Put mcu in sleep mode to save power. -LowPower.sleep(); - } -void loop() {} \ No newline at end of file +void loop() { +//Put mcu in sleep mode to save power. +LowPower.sleep(); +} \ No newline at end of file From 73b46cb1e2a5aad2bb829512ef8ff4d077b0ffa3 Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Tue, 13 Jun 2017 14:06:38 +0200 Subject: [PATCH 10/13] Added bootloader files for Primo and Primo Core --- bootloaders/primo/bootloader_primo_v1.0.0.hex | 934 ++++++++++++++++++ bootloaders/primo/dummy.txt | 1 - .../bootloader_primo_core_v1.0.0.hex | 934 ++++++++++++++++++ 3 files changed, 1868 insertions(+), 1 deletion(-) create mode 100644 bootloaders/primo/bootloader_primo_v1.0.0.hex delete mode 100644 bootloaders/primo/dummy.txt create mode 100644 bootloaders/primo_core/bootloader_primo_core_v1.0.0.hex diff --git a/bootloaders/primo/bootloader_primo_v1.0.0.hex b/bootloaders/primo/bootloader_primo_v1.0.0.hex new file mode 100644 index 0000000..01add4d --- /dev/null +++ b/bootloaders/primo/bootloader_primo_v1.0.0.hex @@ -0,0 +1,934 @@ +:020000040007F3 +:10A000001854002059A5070061A5070063A50700A3 +:10A0100065A5070067A5070069A507000000000007 +:10A0200000000000000000000000000015A4070070 +:10A030006DA50700000000006FA5070071A50700CF +:10A0400073A5070073A5070073A5070073A5070094 +:10A0500073A5070073A5070073A5070073A5070084 +:10A0600073A5070073A5070073A5070073A5070074 +:10A0700073A5070073A5070073A5070073A5070064 +:10A0800073A50700C5A6070073A5070073A5070001 +:10A09000E9A6070073A50700EDA6070073A5070052 +:10A0A00073A5070073A5070073A5070073A5070034 +:10A0B00073A5070073A50700000000000000000062 +:10A0C00073A5070073A5070073A5070073A5070014 +:10A0D00073A5070073A5070073A507000000000023 +:10A0E0000000000000000000000000000000000070 +:10A0F0000000000000000000000000000000000060 +:10A10000000000000000000000000000000000004F +:10A11000000000000000000000000000000000003F +:10A12000000000000000000000000000000000002F +:10A13000000000000000000000000000000000001F +:10A14000000000000000000000000000000000000F +:10A1500000000000000000000000000000000000FF +:10A1600000000000000000000000000000000000EF +:10A1700000000000000000000000000000000000DF +:10A1800000000000000000000000000000000000CF +:10A1900000000000000000000000000000000000BF +:10A1A00000000000000000000000000000000000AF +:10A1B000000000000000000000000000000000009F +:10A1C000000000000000000000000000000000008F +:10A1D000000000000000000000000000000000007F +:10A1E000000000000000000000000000000000006F +:10A1F000000000000000000000000000000000005F +:10A20000000000000000000000000000000000004E +:10A21000000000000000000000000000000000003E +:10A22000000000000000000000000000000000002E +:10A23000000000000000000000000000000000001E +:10A24000000000000000000000000000000000000E +:10A2500000000000000000000000000000000000FE +:10A2600000000000000000000000000000000000EE +:10A2700000000000000000000000000000000000DE +:10A2800000000000000000000000000000000000CE +:10A2900000000000000000000000000000000000BE +:10A2A00000000000000000000000000000000000AE +:10A2B000000000000000000000000000000000009E +:10A2C000000000000000000000000000000000008E +:10A2D000000000000000000000000000000000007E +:10A2E000000000000000000000000000000000006E +:10A2F000000000000000000000000000000000005E +:10A30000000000000000000000000000000000004D +:10A31000000000000000000000000000000000003D +:10A32000000000000000000000000000000000002D +:10A33000000000000000000000000000000000001D +:10A34000000000000000000000000000000000000D +:10A3500000000000000000000000000000000000FD +:10A3600000000000000000000000000000000000ED +:10A3700000000000000000000000000000000000DD +:10A3800000000000000000000000000000000000CD +:10A3900000000000000000000000000000000000BD +:10A3A00000000000000000000000000000000000AD +:10A3B000000000000000000000000000000000009D +:10A3C000000000000000000000000000000000008D +:10A3D000000000000000000000000000000000007D +:10A3E000000000000000000000000000000000006D +:10A3F000000000000000000000000000000000005D +:10A40000DFF80CD000F0ECF800480047C5CA0700A0 +:10A41000185400206FF00200704502D1EFF309815B +:10A4200001E0EFF30881886902380078004A10479C +:10A4300005A60700401E00BF00BF00BF00BF00BF51 +:10A4400000BF00BF00BF00BF00BF00BF00BF00BF14 +:10A4500000BF00BF00BF00BF00BF00BF00BF00BF04 +:10A4600000BF00BF00BF00BF00BF00BF00BF00BFF4 +:10A4700000BF00BF00BF00BF00BF00BF00BF00BFE4 +:10A4800000BF00BF00BF00BF00BF00BF00BF00BFD4 +:10A4900000BF00BF00BF00BF00BF00BF00BF00BFC4 +:10A4A00000BF00BF00BFC5D170470000401E00BF05 +:10A4B00000BF00BF00BF00BF00BF00BF00BF00BFA4 +:10A4C00000BF00BF00BF00BF00BF00BF00BF00BF94 +:10A4D00000BF00BF00BF00BF00BF00BF00BF00BF84 +:10A4E00000BF00BF00BF00BF00BF00BF00BF00BF74 +:10A4F00000BF00BF00BF00BF00BF00BF00BF00BF64 +:10A5000000BF00BF00BF00BF00BF00BF00BF00BF53 +:10A5100000BF00BF00BF00BF00BF00BF00BFC5D16C +:10A5200070470000056885F308884068FF2464B21E +:10A53000EFF30585002D01D1A646004725460646C6 +:10A5400021273FBAF0B40024002500260027F0B4EC +:10A55000F92040B2004700000648804706480047FF +:10A56000FEE7FEE7FEE7FEE7FEE7FEE7FEE7FEE7C3 +:10A57000FEE7FEE711A7070001A4070040EA010378 +:10A580009B0703D009E008C9121F08C0042AFAD2A9 +:10A5900003E011F8013B00F8013B521EF9D270476D +:10A5A000D2B201E000F8012B491EFBD27047002215 +:10A5B000F6E710B513460A4604461946FFF7F0FFC2 +:10A5C000204610BD30B504460020034600E05B1C69 +:10A5D000934203D2E05CCD5C401BF8D030BD00005C +:10A5E000064C074D06E0E06840F0010394E80700E0 +:10A5F00098471034AC42F6D3FFF706FFFCD80700AB +:10A600001CD9070010B50C4610B10120086010BD20 +:10A61000216891B11148421A814213D03C2A0ED2CE +:10A6200001223C3011F13C0111F8013900F80139E7 +:10A630001346521C3C2BF7D904E00E200BE03C22C1 +:10A64000FFF79CFF00223C21044801F045FA034932 +:10A65000891E08800020206010BD000082FF0020BD +:10A6600000F01F02012191404009800000F1E0202C +:10A67000C0F88012704700F01F020121914040098C +:10A68000800000F1E020C0F80011704700F01F02C8 +:10A69000012191404009800000F1E020C0F8001243 +:10A6A00070474907090E002806DA00F00F0000F194 +:10A6B000E02080F8141D704700F1E02080F80014BD +:10A6C0007047000007480021C0F84011C0F844114D +:10A6D000C0F84811C0F84C11C0F80011C0F80411BE +:10A6E00002F09ABF0010014002F032BF074810B5D7 +:10A6F000406830B18047002807D0BDE8104000F026 +:10A7000047BBBDE8104002F0A3B810BDB42C002038 +:10A7100070B58D480023021D0178101D0124062903 +:10A7200007D115782D0704D10578C5F30315032D3E +:10A7300002D0062904D00FE0854E844D75670EE0E7 +:10A7400015782D0708D10578C5F30315032D06D01C +:10A75000042D04D0052D02D0062909D010E04FF0B9 +:10A760008055D5F844527B4EC5F34235356002E042 +:10A7700015782D0704D10578C5F30315032D02D0F4 +:10A78000062906D011E0744D2E6826F080762E60E2 +:10A790000EE015782D0708D10578C5F30315032DB4 +:10A7A00006D0042D04D0052D02D006290AD011E0D0 +:10A7B0004FF08045C5F80C31C5F81031654D2D1F9F +:10A7C0002B6002E015782D0704D10578C5F3031539 +:10A7D000032D02D0062905D00CE05E4A0321643225 +:10A7E00011600AE015782D0704D10578C5F303152B +:10A7F000032D02D006290DD06AE0584A05211160C8 +:10A80000564978310C60554908310B603F21121DC3 +:10A81000116002E0117809075AD10078C0F30310E3 +:10A82000052855D14E4800684E4908604C48001D27 +:10A830000068091D08604A4808300068091D086062 +:10A8400047480C300068091D086045481030006812 +:10A85000091D0860424814300068091D086040481E +:10A86000183000683F49203108603D481C300068BE +:10A87000091D08603A4820300068091D0860384802 +:10A8800024300068091D0860354828300068091D1B +:10A89000086033482C300068091D086030483030AB +:10A8A00000683049403108602D4834300068091D87 +:10A8B00008602B4838300068091D086028483C3083 +:10A8C0000068091D0860264840300068091D0860BE +:10A8D00021487438016841F470010160BFF34F8F63 +:10A8E000BFF36F8F4FF01021D1F80002002803DB77 +:10A8F000D1F80402002822DA1B4A14601B480468BD +:10A90000002CFCD01524C1F800420568002DFCD0B5 +:10A91000C1F8044201680029FCD0136001680029D5 +:10A92000FCD0BFF34F8F0C48F0380168104A01F497 +:10A93000E06111430160BFF34F8F00BFFDE70E4997 +:10A940000C48086070BD0000E00F00F00DF0ADBADB +:10A9500000C007403C050040FCED00E01056004000 +:10A960000404001020C5004004E5014000E401405B +:10A970000400FA050090D003C82C002002E008C8AB +:10A98000121F08C1002AFAD170477047002001E069 +:10A9900001C1121F002AFBD170472DE9FC5F002581 +:10A9A000168807461580406B14468946A8464FF026 +:10A9B0000C0A11234FF0020B98B101784846032E80 +:10A9C00048D380F800B02288521C92B2228083546F +:10A9D0002288521C92B2228009F802102088401C62 +:10A9E00020803A6BDAB12088494600F11205B54261 +:10A9F00030D80B5420881023401C80B220800B5488 +:10AA00002088401C20800F202388155C401ECD54D8 +:10AA1000238840B25B1C23800028F5DA454697F86E +:10AA20002E0030B133462246494601F05DFF050055 +:10AA300011D197F82D0030B132462146484600F03A +:10AA40001DFC050007D1B87820B32088C846001D3A +:10AA5000B04202D95046BDE8FC9F684679DF002825 +:10AA6000F9D12188032008F8010020881921401C11 +:10AA700080B2208008F800102088401C80B220801E +:10AA800000EB0801BDF8000002F008FE0146208836 +:10AA900008442080F878B8B1228897F90310D31CB5 +:10AAA0004846B342D6D800F802B022880123521C8F +:10AAB00092B2228083542288521C92B2228009F8DA +:10AAC00002102088401C20807868B8B1218890F955 +:10AAD0000020CB1C4846B342BCD800F801B0218806 +:10AAE0000A23491C89B2218043542188491C89B218 +:10AAF000218009F801202088401C2080388950B12D +:10AB0000CDE900464B460622022107F1080002F07B +:10AB100036FE05009FD1388A50B1CDE900464B463C +:10AB20000722032107F1100002F029FE050092D14F +:10AB3000388B50B1CDE900464B461522142107F160 +:10AB4000180002F01CFE050085D1386A30B133468A +:10AB50002246494600F04CFF0500DBD1786A30B14F +:10AB600033462246494602F027F80500D2D197F82D +:10AB70002C0038B1334622464946384602F002FCE2 +:10AB80000500C7D1387838B13346224649463846A1 +:10AB900002F044F80500BDD128465CE710B5044634 +:10ABA000114890B0382180798DF83A0041F2305048 +:10ABB000ADF838006846FFF7FAFC02208DF8000077 +:10ABC00000208DF802000120ADF808000EA80390C7 +:10ABD0008DF803400021684600F0A8FA002801D053 +:10ABE00000F0D6F810B010BD643200202DE9F0431B +:10ABF000284C8BB0A078002838D118212648FFF7C0 +:10AC0000D6FCA046E078244C00254FF02809012608 +:10AC100078B304F148070095019502951022694622 +:10AC2000F81D0395FFF7CEFC20B3F81DCDE908070A +:10AC300009A8049008A806908DF814608DF81C608F +:10AC40000420FFF7ABFF25700220207204A8E0600B +:10AC5000A4F810906582104873DF08B100F098F8EE +:10AC60000E494FF40030086088F802600BB0BDE870 +:10AC7000F08307E00520FFF791FF267067602572DB +:10AC80002582E7E70620FFF789FF257065602572BA +:10AC9000DEE700008C2C00204C3200200C05005018 +:10ACA0002DE9FF410546087A0E4681070AD00322A6 +:10ACB0001146284600F012FC002801D000F068F888 +:10ACC000BDE8FF812849009001F06CFD010011D121 +:10ACD000254C7168143C009A6069FFF74FFC00989E +:10ACE00001F02CFD010005D169461F4801F036FD39 +:10ACF000010003D0284601F0B7F8E1E704200190F5 +:10AD00000098800802906069039001A801F076F82D +:10AD100007000BD0092F10D0606901F0D9FC0100A9 +:10AD200002D0284601F0A0F83946E3E7307A2169DD +:10AD3000084420616069A061C2E7307A216901445A +:10AD4000216160780028BBD06089401E0004000C9F +:10AD50006081B5D1284600F089FB08B100F018F8F1 +:10AD600020896081ACE70000A02C0020BFF34F8F4A +:10AD700005480168054A01F4E06111430160BFF331 +:10AD80004F8F00BFFDE700000CED00E00400FA0566 +:10AD90000EB500210091CDE901106A4644F2010090 +:10ADA000FFF7E4FF0EBD000038B50C46064BE26825 +:10ADB000097853F8225062680092024604F10C00B0 +:10ADC0006369A84738BD00005C31002070B50F4BA7 +:10ADD000054605201C785E78A64216D01C781878A7 +:10ADE0009E88B04201DA401C00E0002018705E88A6 +:10ADF000D86804FB06002860986800EBC4039B88B1 +:10AE00000B8050F834001060002070BD242C00200E +:10AE10002DE9F84F254C8146924660880E468142C6 +:10AE200041D800274FF6FF788DF80070684600F093 +:10AE30003FF922786078A188884201DA401C00E05E +:10AE40000020C0B2904204D19DF8000000F054F9F7 +:10AE500027E065786078884201DA401C00E0002035 +:10AE600060709DF8000000F047F9454519D0A068D2 +:10AE7000B9F1000F40F835A00DD066B16188E068E7 +:10AE8000324605FB01004946FFF778FBA06800EB5E +:10AE9000C500868002E000EBC50087800020BDE889 +:10AEA000F88F0420FBE70920F9E70000242C00209C +:10AEB0000EB504E0029ABDF804100098904702AA6B +:10AEC00001A96846FFF782FF0028F3D00EBD0000FD +:10AED00030B5CB0008339DB293074FF0000401D08A +:10AEE000072030BD044B9A602A44DA605C701C7005 +:10AEF00058809980002030BD242C0020074B9B688F +:10AF000023B12AB120B10068037C1BB1082070472F +:10AF1000072070474174426100207047342C0020A4 +:10AF200010B50446082902D00020FFF731FFD4E90C +:10AF30000010BDE8104008471CB5044ACDE90001E7 +:10AF400008216846FFF764FF1CBD000021AF070021 +:10AF50002DE9F0478046994614460F4690074FF07A +:10AF6000000634D11C4D8CB302F056F90320C5F80D +:10AF70001490287004F11800002107EB4703AC601F +:10AF800004EBC102491C167056709770506000EBBC +:10AF9000C3000329F4DBEE606E70AE701420FFF77F +:10AFA0005FFB06211420FFF77CFB1420FFF763FBF7 +:10AFB0000A49C1F8008006211120FFF772FB0748FB +:10AFC000001F006828610020BDE8F08701E007202D +:10AFD000FAE7AE60FBE70000342C002008150140C2 +:10AFE0002DE9F843DFF870900D461746D9F80810A0 +:10AFF000044651B34CB3052D02D20720BDE8F883B7 +:10B00000606910B3607C012821D0002602F084FB27 +:10B01000D9F80810804601EBC800694602F09BFB96 +:10B02000B8B1012100F8041B0C4910C0096880E880 +:10B03000E200D9F8081001EBC8009DF8001041703B +:10B040001420FFF723FB0020D8E70820D6E72E4680 +:10B05000DCE70420D2E70000342C00200415014076 +:10B06000F8B5124E0446B168E1B1DCB16069C8B10F +:10B070000027277402F050FB0546B068694600EBD4 +:10B08000C50002F068FB78B1022101704460B0682D +:10B090009DF8001000EBC50041701420FFF7F6FA90 +:10B0A0003846F8BD0820F8BD0420F8BD342C002037 +:10B0B00030B5EFF3108172B60D4A9468012324B1C4 +:10B0C0000370002900D162B630BD93604FF0E023D9 +:10B0D000D3F88051074C25401560C3F88041D3F860 +:10B0E000844154605A17C3F8842100220270E8E7B3 +:10B0F0000C340020FC06FFBC0B498A68002A11D0E2 +:10B1000000280FD1EFF3108072B60B684FF0E022E9 +:10B11000C2F800314B68C2F8043100228A6000286E +:10B1200000D162B6704700000C340020F0B50C4628 +:10B1300093B01F210726ADF80010ADF804100027CA +:10B1400050B1C17849070DD56A4602A9FFF725FC21 +:10B15000002808D102AD02E00025ADF800706CB106 +:10B16000E07810B1304613B0F0BD01AA0AA920461C +:10B17000FFF713FC0028F6D10AAA02E00022ADF87E +:10B180000470BDF80400C3B2BDF80000C1B2284687 +:10B1900072DFE8E770B5054601461C220F48FFF74D +:10B1A000EDF90F4C00262670286830B10168616007 +:10B1B0004068A060201D7ADF02E00948001D7BDFA7 +:10B1C00000280AD14FF6FF7060806670BDE87040BD +:10B1D000044A00210448FFF791BE70BD4C300020A6 +:10B1E000002C0020B9D6070084D8070070B51E4C8B +:10B1F000068800251B49E289102E15D01B4B112E05 +:10B200001CD0122E29D0502E17D130F8061F914293 +:10B2100013D14189022910D1007B10F0010F11D008 +:10B22000BDE8704000F02ABC83884B808369CB6006 +:10B23000C06908614D70002AF2D070BD4FF6FF70F2 +:10B2400048804D701868FFF70BFF0028F5D0A16902 +:10B250000029F2D0BDE8704008478268CA60C06823 +:10B260000861DDE7002C00204C30002084D8070066 +:10B2700001480068FFF7F4BE84D807007CB5054696 +:10B2800008880C460930904201D90C207CBD6846E4 +:10B2900071DF0028FAD121880820685420881B21FA +:10B2A000401C80B2208029542088401C80B220801D +:10B2B0002844DDF801100160BDF805108180208868 +:10B2C000801D80B220809DF8001001B10121295419 +:10B2D0002088401C208000207CBD0000F0B585B097 +:10B2E0000A4605002FD02888A0F57F41FF392CD0D1 +:10B2F0001748007848B3164C1020641C01272070B2 +:10B3000007206070A7700320ADF810000026E11C34 +:10B31000104602F0C8F9BDF8101000960844ADF8C8 +:10B320001000019602960396288AADF8000004A842 +:10B33000CDE902048DF80270ADF8046028886946F2 +:10B34000A6DF05B0F0BD0E20FBE70820F9E70000FE +:10B35000142C002030B585B00D46040039D0BDB3A3 +:10B360006868A8B31C4B4FF6FF7020800FCB0DF11F +:10B37000040C8CE80F0041F23050ADF8000001A839 +:10B38000811E63DF002821D1221D69460120A0DF34 +:10B3900000281BD19DF80200A071204600F08EFE0F +:10B3A000002813D1204600F0DAFC00280ED12946EF +:10B3B000204600F013FF002808D16868A062A86842 +:10B3C00000B1E062054901200870002005B030BDE1 +:10B3D000FFE70E20FAE7000088D80700142C0020B1 +:10B3E0003EB500283CD000293AD0826A002A37D0E6 +:10B3F0000A88102A23D0112A33D0502A21D0512A6A +:10B400002ED104460A4611F8060F022828D1488892 +:10B41000238A984224D1907B042821D006281FD06B +:10B4200005281DD02046891C01F032FD002817D0C8 +:10B43000E16A002914D003B0BDE83040084789888C +:10B4400011E0CA8803899A420AD108228DF80020A7 +:10B450000A7C12318DF808200191826A6946904772 +:10B460003EBD4FF6FF7101803EBD0000F0B585B0D6 +:10B470000A4605002BD02888A0F57F41FF3928D047 +:10B480001548007828B3144C1120641C0127207043 +:10B490000026ADF81070611C104602F004F9BDF8EA +:10B4A000101000960844ADF810000196029603961D +:10B4B000288AADF8000004A8CDE902048DF80270D6 +:10B4C000ADF8046028886946A6DF05B0F0BD0E20FF +:10B4D000FBE70820F9E70000142C002030B585B008 +:10B4E00028B30388A3F57F44FF3C22D0124B1B787E +:10B4F000FBB1114B10255B1C00241D7059709A7014 +:10B5000001250321ADF810100094019402940394D6 +:10B51000018AADF8001004A9CDE902138DF802509C +:10B52000ADF8044000886946A6DF05B030BD0E20A6 +:10B53000FBE70820F9E70000142C002010B50446B2 +:10B54000FFF754FE21460448FFF74AFF2046BDE8B6 +:10B55000104001F0E7BB00006432002030B51C4908 +:10B560008BB00A1D0DF1140C3CCA8CE83C00096834 +:10B57000099120B105A818DF08B1FFF709FC4FF4C5 +:10B58000F42013DF08B1FFF703FC124B48221249E5 +:10B5900009A801F077FF08B1FFF7FAFB01216A461D +:10B5A000084601F049FF08B1FFF7F2FB01208DF8D2 +:10B5B0000C00684601F036FF08B1FFF7E9FB0748C9 +:10B5C00001F07AFF002801D0FFF7E2FB0BB030BD9D +:10B5D00070D8070071D40700942E00204DD50700C5 +:10B5E00070B511DF08B1FFF7D3FB4FF0E021002069 +:10B5F000D1F800315FF0010404FA00F21A420AD0D7 +:10B6000042B202F01F0504FA05F15209920002F15C +:10B61000E022C2F88011401C2028EDD34FF44054A2 +:10B62000A06813DF08B1FFF7B3FBA068BDE8704066 +:10B6300000F010B908B5684600F00EF900980178DE +:10B64000A52904D00079AA2801D0002008BD012036 +:10B6500008BD10B500F04EFE18B900F0DBFA002866 +:10B6600013D0642001F028FB00F007FE08B1FFF7BB +:10B670008FFB00F03FFE08B1FFF78AFB00F0A4FA51 +:10B68000041E01D0FFF784FB204610BD00B587B033 +:10B690001422084901A8FEF771FF05980DF1040C6A +:10B6A00000909CE80F0000F01BF802F0CFF807B004 +:10B6B000002000BDA4D8070010B500F021FC002830 +:10B6C0000BD100F027FF044602F0C0F80349102018 +:10B6D0000860024910390860204610BD1805005066 +:10B6E0002DE9F84304460E4617461D466846DDF828 +:10B6F000208000F0B1F814F0FF024FEA14412F4C03 +:10B700002F484FF001034FF0FF0C10D0012A14D046 +:10B71000032A23D00021022A2DD0052A3FD0042A53 +:10B7200044D0062A01D104202070BDE8F88341806E +:10B730008560037080F804C00BE04180F119294452 +:10B740008160A521017080F804C0C0F81880C6602F +:10B750000EE023701A4800F065F8E6E700990A78D1 +:10B7600002704A88428089688160AA210171C6609E +:10B77000C0E90475EDE7009A1578A52D0AD005708B +:10B78000558845809268826080F804C0C16001617C +:10B790004161DEE74180816080F800C0F4E700F09D +:10B7A00095FE08B1FFF7F4FA0320BDE74180816000 +:10B7B00080F800C0009909790171CBE7542C002072 +:10B7C000683100200EB5084A07CA8DE8070001F06D +:10B7D00089FC002806D105494FF4FE2048606846E0 +:10B7E00001F0A0FC0EBD000098D80700582C0020E6 +:10B7F00038B50446684600F02FF8009800782070AD +:10B8000000984088608000988168A160017921716A +:10B81000C168E16001692161416961618069A0617C +:10B8200038BD000010B504461C21094801F050FC49 +:10B8300008B1FFF7ADFA00231C222146044801F0AD +:10B8400085FC002803D0BDE81040FFF7A1BA10BD69 +:10B85000582C0020FEF766BE0149016070470000C9 +:10B8600000F0070070B5174C0F20217801EB410163 +:10B8700004EBC1021449536912F8045F4968012DB1 +:10B880000ED0032D0BD14FF08050006980B2B3FB76 +:10B89000F0F0084428DF002801D10121A17070BD1B +:10B8A0000803516856690D1A3118926818441044FB +:10B8B000B5F5805F02D2AA0829DFECE74FF4806279 +:10B8C00029DFE8E7683000201C2C002000EB400254 +:10B8D0000548002100EBC200032201718160C0E92C +:10B8E00004218161C1607047683000202DE9F0417A +:10B8F000134C069E65780A2D1FD027783D44EDB283 +:10B900000A2D01D30A3DEDB205EB450504EBC50553 +:10B910002871AA61D1E90001C5E90401C5E902362F +:10B92000A178002021B9FFF79DFF112800D1002048 +:10B930006178491C6170BDE8F0810420FBE70000DC +:10B94000683000202DE9F05F2648C168F1B3DFF8C8 +:10B9500098B00E688146DBF800400769C078E11BAB +:10B9600021F07F45ED1CB0B9DFF880804FF48030C6 +:10B97000C8F84403C8F804031120FEF771FE112033 +:10B98000FEF779FE4FF0010AC8F800A02F20FEF75D +:10B990008DFD89F803A0B54200D23546124BE81957 +:10B9A0003C3320F07F401860DBF800105A46091B3A +:10B9B00021F07F41001B20F07F40C91C81420FD93C +:10B9C000106818602F20FEF771FD00E004E0BDE86C +:10B9D000F05F1120FEF75ABEBDE8F05F01F01CBC1D +:10B9E000BDE8F09F342C00200415014000100140F8 +:10B9F0002DE9F04117880646B81D14460D469842B9 +:10BA000002D90C20BDE8F0813188072206290AD32B +:10BA10004FF4486C4FF6FF73614501D9994202D14A +:10BA20007088062801D21046ECE7604501D998429B +:10BA3000F9D1994203D0984201D08142F3D8052030 +:10BA4000E85520881221401C80B22080295420888B +:10BA5000401C80B220804119308801F01FFE2188EF +:10BA6000084480B220804119708801F017FE2188B7 +:10BA7000084420800020C5E7FFF78AB9F8B51348CD +:10BA800000F01EFF114D0026114C0C3D30B16169D4 +:10BA9000B9B101208DF80000684611E0287820B186 +:10BAA000616971B18DF80060F6E7687858B1A168F6 +:10BAB000084800220068FFF793FA10B1A16901B1AC +:10BAC00088472E70F8BD6168F2E700000C2C00205A +:10BAD0004C30002084D8070010B5044612B11088FD +:10BAE000002211E04FF6FF70FAE7030A43EA002351 +:10BAF000A05C5840C0F30313584080EA003080B285 +:10BB0000C3B280EA4310521C8A42EED310BD00003B +:10BB100030B5164C87B01648E16801F0D9FA08B183 +:10BB2000FFF736F91248E268083800234168083008 +:10BB300001F00CFB050015D1142101A8FEF737FD1B +:10BB400000208DF804000A480DF1040C203880888C +:10BB5000ADF80600E0680490059800909CE80F009E +:10BB6000FFF7BEFD07B0284630BD0000A43100201D +:10BB7000802C0020032161F3070008B50549064B1E +:10BB8000898861F31F400ECB0094FFF7A9FD0020C8 +:10BB900008BD0000602C0020A8310020012138B52C +:10BBA00061F307000649074B898861F31F404FF492 +:10BBB00040518C680ECB0094FFF792FD002038BDF9 +:10BBC000602C0020A831002000B58BB004A8FFF73E +:10BBD0000FFE089800280CD0079860B107990A98C2 +:10BBE00008440021CDE900100898800802906846BA +:10BBF00018DF0BB000BD4FF440508068C0F5EE2256 +:10BC0000C0F5F421C2F30C02891AA1F5405100EBF2 +:10BC10005100E6E700B58BB004A8FFF7E9FD0898EE +:10BC200000280FD0079878B107990A980844032193 +:10BC300000914FF4F421CDE9011008988008039099 +:10BC4000684618DF0BB000BD4FF440508068C0F567 +:10BC5000EE22C0F5F421C2F30C02891AA1F540517D +:10BC600000EB5100E3E7000070B504000BD00022A8 +:10BC70003C210B48FFF730FF094DAD1E2988884253 +:10BC800003D00B2070BD0E2070BD3C22044920461D +:10BC9000FEF774FC2888401C2880002070BD00003E +:10BCA00082FF002070B515460A4603281FD00428DD +:10BCB00024D1134C39B1E088A0F57F41FF391DD064 +:10BCC000132176DF0DE0284600F002FD08B1FFF7F2 +:10BCD0005FF8A069A84211D1012203210948FFF7AA +:10BCE000FDFB00280AD0BDE87040FFF751B80121E4 +:10BCF000104600F0EEFF02460121EFE770BD0000A4 +:10BD00008C2C00206432002000B587B01422074933 +:10BD100001A8FEF733FC05980DF1040C00909CE897 +:10BD20000F00FFF7DDFC07B000BD0000BCD8070026 +:10BD300070471FB503230093CDE9010190080390DC +:10BD4000684618DF04B010BD1FB501230093CDE98C +:10BD5000010190080390684618DF04B010BD30B5AB +:10BD60008FB005461C216846FEF721FC00249DF893 +:10BD70000000019440F018008DF800000394049432 +:10BD800005940694A8798DF8360041F23150ADF84B +:10BD900034000C949DF83000142120F0FF008DF841 +:10BDA00030009DF8310020F00F00401C20F0F00022 +:10BDB00010308DF831009DF8320020F00600801C14 +:10BDC00020F0080040F011008DF8320007A8FEF7BF +:10BDD000EEFB0DA807900CA80890ADF824401720A2 +:10BDE000ADF82640ADF828000B94A88805F11003A3 +:10BDF00007AA6946A2DF0FB030BD000070B505008C +:10BE000004D0287A800703D0102070BD0E2070BDAA +:10BE1000144C6078032803D0042801D0052801D0F1 +:10BE2000082070BD6868E1688600A06830448842D8 +:10BE300004D94FF0FF30A0600C2070BD00F028FB4B +:10BE40000028FAD1A9683246A368206901F07EF97A +:10BE50000028F2D1A1683144A160E2689142ECD09F +:10BE6000092070BD602C002010B50A46044603214D +:10BE7000104600F02EFF024603212046FFF72EFB5E +:10BE8000002803D0BDE81040FEF782BF10BD0000BF +:10BE9000094810B54078072801D0082010BD074890 +:10BEA0000068FFF7DDF808B1FEF772FF044880680C +:10BEB000BDE8104000470000602C0020B8D8070003 +:10BEC0003432002010B50D4C6078052813D1D4E928 +:10BED000020188420FD10620607000F0D9FA0028D4 +:10BEE00008D12069E168406800F096F8002801D187 +:10BEF0000721617010BD082010BD0000602C0020DB +:10BF0000FEB51F4E1F4C06F11800002507C88DE82E +:10BF10000700A570A58004F12001684601F002F930 +:10BF200008B16570FEBD4FF440501649806820315D +:10BF3000C0F5EE224860216AA161C0F5F421C2F388 +:10BF40000C02891AA1F5405100EB51000D490E4A2F +:10BF50001831486000210A48FEF7D0FF08B1FEF70B +:10BF600017FF00224FF470113068FFF739F808B15D +:10BF7000FEF70EFF0120A56060700020FEBD0000EE +:10BF8000B8D80700602C002075C4070010B5094C14 +:10BF90000820A16801B110BD61780429FBD1A17806 +:10BFA000054800F047F810B10021A17010BD05212F +:10BFB000617010BD602C0020B43100202DE9F041EB +:10BFC000134C07466078032805D0042805D00825BF +:10BFD0002846BDE8F08104206070A06808B1082000 +:10BFE000F7E700F055FA0500F3D178688600A078ED +:10BFF0008119802901D90920EBE7064AB968104464 +:10C000003246FEF7BBFAA0783044A070E0E70000AB +:10C01000602C0020B431002010B50022FFF75CFD39 +:10C0200003490988884201D00B2010BD002010BDB3 +:10C030004032002070B500250C290ED34318018929 +:10C04000044600EB4101581A0A38C2B21948022AC4 +:10C05000027002D30A318B4201D2092070BD16480A +:10C06000FEF78CFA4FF010214FF6FF70B1F88020E8 +:10C07000824202D02388934219D1B1F882108142C2 +:10C0800002D06088884212D120894FF6FE724FF4A8 +:10C090004053A8420BD904EB45014989914204D091 +:10C0A0009E896D1CB6B2B142F3D1002070BD0B2049 +:10C0B00070BD0000882C00204032002030B58FB0C9 +:10C0C00005461C216846FEF772FA00249DF8000020 +:10C0D000019440F004008DF800000394049405944A +:10C0E0000694A8798DF8360041F23250ADF834004C +:10C0F0000C949DF83000142120F0FF008DF83000E2 +:10C100009DF8310020F00F00401C20F0F0001030AE +:10C110008DF831009DF8320020F00600801C20F0E0 +:10C12000180040F001008DF8320007A8FEF73FFA32 +:10C130000DA807900CA80890ADF824401420ADF885 +:10C140002640ADF828000B94A88805F1080307AA3B +:10C150006946A2DF0FB030BD084910B5A1F120003B +:10C16000016102214170C168203000F0B1FF002858 +:10C1700003D0BDE81040FEF70BBE10BD802C0020A0 +:10C180000E4910B5A1F118000161022141704FF470 +:10C1900040508068C0F5F421C0F5EE20C0F30C00DB +:10C1A000081AA0F540504108044800F091FF00280B +:10C1B00003D0BDE81040FEF7EBBD10BD782C002089 +:10C1C0000149486170470000602C002038B5062501 +:10C1D00065F307000094FFF783FA38BD70B58EB0A1 +:10C1E0000E4605461C216846FEF7E1F900249DF83D +:10C1F0000000019440F002008DF8000003940494C4 +:10C2000005940694A8798DF8360041F23450ADF8C3 +:10C2100034000C949DF83000142120F00F00401CD5 +:10C2200020F0F00010308DF830009DF8310020F043 +:10C23000FF008DF831009DF8320020F00600801CD0 +:10C2400020F0180040F001008DF8320007A8FEF73A +:10C25000AEF90DA807900CA808900220ADF82400B4 +:10C26000ADF82640ADF828000B96A88805F120030C +:10C2700007AA6946A2DF0EB070BD2DE9F04188B073 +:10C280006846FFF7B5FA039800282BD0069A00F508 +:10C2900080504FF48051904227D9A2F58050440835 +:10C2A0001046039A00EB440687184FF440500D46A1 +:10C2B000806801EB440890420ED204F580500422BD +:10C2C0000146FFF741FD00280CD12946042208460B +:10C2D000FFF73AFD002805D1BA1B234641463046F8 +:10C2E00000F038F808B0BDE8F081039A0698FFF72F +:10C2F0002BFDF7E730B58FB002A8FFF779FA059864 +:10C30000002817D0089A00F580504FF480519042D1 +:10C3100016D9A2F58050430810464FF44054059AB0 +:10C32000A468059D024400EB430001EB4301AC42CD +:10C3300002D20E200FB030BD121A00F00BF8F9E750 +:10C340000BAB032083E80700059880080E900BA82C +:10C3500018DFEFE72DE9F0411C4617460D4606466B +:10C36000FFF7E7FC002818D0B5F5805F07D9234612 +:10C37000291B301B1A46FFF7EDFF00280DD13A4666 +:10C3800029463046FFF7E0FC002806D13A46294608 +:10C390003046BDE8F041FFF7CCBCBDE8F0810000BD +:10C3A00070B542682A48D2E90043D2E90212C0E9D6 +:10C3B0000212C0E900430078440701D5840705D183 +:10C3C0009C0703D18C0701D1940701D0062070BDD2 +:10C3D000204D0B441A44EA60B1F5A04F1ED81C4C06 +:10C3E000C3074FF4405104F190040AD08868C0F5A7 +:10C3F000EE20904212D31848206018486060184818 +:10C4000017E08968C1F5F423C1F5EE21C1F30C01F1 +:10C41000591AA1F54051B2EB510F01D90C2070BD52 +:10C420001049216010496160800701D50F4800E084 +:10C430000F48A0606878012802D00826304670BDF9 +:10C4400000F026F80600FAD12168E8688847F5E789 +:10C45000A4310020602C002059C1070009BD07004D +:10C460009DBB070081C1070031BD070075BB0700F8 +:10C4700011BB070068B5054E01253570052565F32C +:10C4800007000094FFF72CF968BD0000602C002025 +:10C490000C4810B5007808B1082010BD0A4C20687F +:10C4A000FEF7DEFD08B1FEF773FC00224FF47011B9 +:10C4B0002068FEF795FD041E01D0FEF769FC2046BA +:10C4C00010BD0000602C0020B8D8070010B5104C3B +:10C4D00001202070E088A0F57F41FF3905D01321AD +:10C4E00076DF68B1FEF754FC0AE0A07840B174DF53 +:10C4F00008B1FEF74DFC112000F00CFC0020A070EC +:10C50000FEF7B6FE08B1FEF743FC002010BD0000A8 +:10C510008C2C002030B54A4D87B000242C702C7133 +:10C52000112000F0F0FB122000F0EDFB112000F0D4 +:10C53000F1FB122000F0EEFB424800F069FF0028FA +:10C5400075D14148FFF73CFE00F0AEF800286ED1EF +:10C550003E48FFF789FB3E4A3D49103A002868D023 +:10C56000D00608601060684671DF08B1FEF710FC65 +:10C570009DF801006946401C8DF80100002070DF25 +:10C5800008B1FEF705FC9DF8080032A120F00F006D +:10C59000401C20F0F00010308DF80800072202A89F +:10C5A0007CDF08B1FEF7F4FB00940C20ADF800002E +:10C5B00018200194ADF80200ADF804404FF4C870A3 +:10C5C000ADF8060068467ADF08B1FEF7E1FB082007 +:10C5D0000094ADF8000021480190214802901B48CA +:10C5E00069463038FEF7B6FE08B1FEF7D1FB1C21D4 +:10C5F0006846FDF7DCFF40F6CD40CDE900404FF442 +:10C600008040029003208DF80C001648ADF80E40D3 +:10C610008DF81040CDE905406846FEF7BBFD08B136 +:10C62000FEF7B6FB00F06AFEFEF7E0FA002007B066 +:10C6300030BD4FF00050086010600120E870A2E7A4 +:10C640008C2C00203DB50700A5BC070094320020CB +:10C6500018050050446675546172670089CF070061 +:10C660000DD4070079BA07000E4910B54A788C78C6 +:10C670000023A24212D0521CD2B24A70022A00D128 +:10C680004B70084A4B78183252F823200260086930 +:10C69000104420F07F400861012010BD036000209D +:10C6A00010BD0000342C002007480021074A01700B +:10C6B00002F5907002600822C0E90121C1600161A9 +:10C6C0004161816108467047AD2C0020D0320020C6 +:10C6D000F0B5194A0646916861B355691020691A88 +:10C6E00001F00701D2F800700B46012401EBC10CE8 +:10C6F00007EB8C0CB44506D1956904FA01F6754038 +:10C700000020956104E0491C01F007018D42EDD144 +:10C71000491C956901F0070104FA03F31D420AD18F +:10C720009368002B07D05B1E936053685B1C5360BB +:10C730000B46F1E70420F0BDF0330020064920232A +:10C740000A7C0968521E02F0070202EBC20203EBE8 +:10C750008202885000207047F033002070B5D0B1BD +:10C76000C9B10F4AD368C3B15B1ED36093685B1C29 +:10C7700093605369156803EBC30405EB84060660F8 +:10C78000202000EB84005B1C2858086003F00700A1 +:10C790005061002070BD0E2070BD042070BD0000EF +:10C7A000F033002010B5E9B100220A60104A536846 +:10C7B000E3B1202818D85B1E5360D068401CD060BD +:10C7C0001069136800EBC00403EB84030B600123C2 +:10C7D00083409169401C194300F00700916110618A +:10C7E000002010BD0E2010BD0C2010BD042010BD77 +:10C7F000F0330020FEB50446087A82070ED000F020 +:10C800000302C2F104030022154604E04E683554C9 +:10C81000401CC0B2521C9A42F8D30872012000900A +:10C8200048680290087A800801906846FFF7C6FBC6 +:10C8300000280BD0022100F04CFA024602212046CB +:10C84000FEF74CFE002801D0FEF7A2FAFEBD000064 +:10C85000F8B51A4C207800282ED0206928B100267F +:10C86000E06820B1002526B110E00126F8E7012597 +:10C87000F9E7684652DF052803D020B1FEF788FAB1 +:10C8800004E0012602E021690098884785B96088A4 +:10C89000ADF800006946A06861DF052803D020B12B +:10C8A000FEF776FA04E0012502E0D4E902018847A8 +:10C8B000002EDED0002DD6D0F8BD0000B42C002014 +:10C8C000054940880A88904204D34988884201D8A3 +:10C8D0000120704700207047042C0020800701D001 +:10C8E000002070470120704730B51488E51C9D4238 +:10C8F00001D90C2030BD02250D5513881C245B1C6A +:10C900009BB21380CC5413885B1C9BB2138001280C +:10C9100007D0022807D0032807D0042807D0072013 +:10C9200030BD002004E0012002E0CD5401E00320EE +:10C93000C8541088401C1080002030BD2DE9F047FD +:10C94000DFF8ACA0DAF80C009AF8004080469AF8BC +:10C950000460DAF80890DAF810703AE009EBC405E0 +:10C960002A7833E002EB42016B68521C03EBC101F1 +:10C97000AB78D2B2934200D100220B78022B02D0C6 +:10C98000032B23D11FE0D1F804C00346014603E086 +:10C99000614503D00B46C969C1B1F9E7B1B18B421A +:10C9A00005D1C06918B9134F0126BE600027D1F820 +:10C9B00000C0C969D96149B10B6863440B6005E0E7 +:10C9C0000174C06900E000210028F9D16978914222 +:10C9D000C8D1641EE4B2C1D2CAE903078AF8046070 +:10C9E000404502D00120BDE8F0870020FBE70000B1 +:10C9F000342C0020001001402DE9F04705462F4857 +:10CA0000002680460778D0F80C904BE0D8F8080054 +:10CA100000EBC70440E002D02846ED691EE0207814 +:10CA2000616800EB4002401CC0B201EBC201207003 +:10CA3000A278904200D126700A784868012A2DD148 +:10CA4000027C5ABB8A684260CA6882600A69C26016 +:10CA50004969816198F8041001B146604268D8F8CC +:10CA60001030DFF85CC0D11A21F07F41614502D25D +:10CA70008268114406E0991A826821F07F418A4257 +:10CA800002D9511A016000E006604660012186600B +:10CA90000174C66100F0A2FD002DBCD12078617840 +:10CAA0008842BCD17F1EFFB2B0D2D8F80C104945E5 +:10CAB00002D00120BDE8F0870020FBE7342C0020E5 +:10CAC000FFFF7F00344802680021B12A35D00024DE +:10CAD0004FF010204069B0F5F42F02D00020FEF78F +:10CAE00057F94FF080500069B0F5805F02D0002008 +:10CAF000FEF74EF9294B2A4A05210020FEF728FAB5 +:10CB000008B1FEF745F928492648C1F80007FEF7A5 +:10CB100059FEFEF78FFD98B1FEF79BFD08B1FEF7B9 +:10CB200037F984F00100FEF719FD00F093FBFEF7E2 +:10CB3000ADFD58B1FEF72CF908E001240160C7E70C +:10CB400084F00100FEF70AFD00F084FB174800683E +:10CB5000C00910F0010F0BD00020204304D0FEF7D5 +:10CB6000ABFD08B1FEF714F9FEF764FD10B106E065 +:10CB70000120F2E74FF440508068FEF731FDBFF32B +:10CB80004F8F0B4801680B4A01F4E06111430160CB +:10CB9000BFF34F8F00BFFDE71C05004039AF070012 +:10CBA000CC2C00200C0003001C000050100500508D +:10CBB0000CED00E00400FA0570B5144682880E46BC +:10CBC00005462088111D0144921C994201D8FE2A75 +:10CBD00001D90C2070BD521C32542088FF21401C0A +:10CBE00080B2208031542088401C80B2208081197E +:10CBF000288800F053FD2188084480B22080AA884C +:10CC000042B1A96841B13044FDF7B8FC2088A98839 +:10CC100008442080002070BD072070BDF8B50E4686 +:10CC2000017814460546012901D1687830B1208881 +:10CC3000821C9A422DD8012902D006E00720F8BDB7 +:10CC400069780144891C994223D8181A801E87B23A +:10CC5000ADF800702188B01C084469467DDF0028CB +:10CC6000EDD12878022805D1BDF80010B94201D8CD +:10CC700009210AE00821012805D16878B84202D8C4 +:10CC8000ADF8000001E0ADF80070BDF80000FE282E +:10CC900001D90C20F8BD2288401CB0542088401CCB +:10CCA00080B2208031542088401C2080BDF80010C4 +:10CCB000084420800020F8BD31B540F2E73404E09C +:10CCC000401E00902046FDF7B5FB00980028F7D1E4 +:10CCD00038BD08280BD003DC38B106280FD108E096 +:10CCE0000B280AD00C280AD105E001207047022049 +:10CCF00070470320704704207047042901D00620A4 +:10CD00007047052070470321800000F1A040C0F863 +:10CD10000017704701218140014801607047000001 +:10CD20000805005030B5574C01880022E588A7B0AF +:10CD300019290AD01EDC11293DD008DC022972D045 +:10CD4000102902D18088E080A27027B030BD4E4B00 +:10CD5000132903F1180446D01429F6D11A46058A7E +:10CD60005B8D00219D4200D1214600238088D21D89 +:10CD700086DF1BE0512968D00EDC1A297FD01B29E1 +:10CD8000E3D1007A0028E0D1A270E068401EE060A4 +:10CD90004AD0FDF72BFFD8E7522970D05529D4D1BE +:10CDA00080790028D1D11321284676DF0028CCD005 +:10CDB000FDF7EEFFC9E78020ADF894003220E06077 +:10CDC0001220FFF7A7FF012325AA01A9E088AADF07 +:10CDD00008B1FDF7DDFF207808B9FDF707FF4FF632 +:10CDE000FF70E080B1E728481022483850F8481F0B +:10CDF00005918188ADF8181080798DF81A00D91D39 +:10CE000001A8FDF7BBFB1C22214610A8FDF7B6FBCD +:10CE100001A80C901B4A10A81C320B9007AB85216F +:10CE200028467FDFC2E70CE01422184901A8FDF76D +:10CE3000A5FB059801AC009094E80F00FEF750FCAC +:10CE400083E70021284667DFB0E781790029A2D077 +:10CE5000807B042803D0062801D005289BD1022915 +:10CE60000BD001208DF884004FF4C170ADF888001C +:10CE700021A92846A8DF99E701E00220F2E700F0A7 +:10CE80004FFA93E78C2C002094320020E8D807005A +:10CE9000F0B58BB001274FF0020E05468DF80CE07F +:10CEA0008DF812700889ADF814004889ADF81600A5 +:10CEB00001F10C00002608960690ADF820E007A8C6 +:10CEC000ADF8226009900C46A98A288808AAA5DF37 +:10CED00018B1E96AC9B1884717E09DF81C0010F045 +:10CEE000010F12D0ADF81060288803A9A8DF002830 +:10CEF00012D1217B04F10D00092943D2DFE801F0B2 +:10CF0000420E1D2326282A402C0040F2FD10ADF8C9 +:10CF10001000288803A9A8DF0BB0F0BD8DF80060D1 +:10CF20000DE006221DE000BF8DF808700190AA6A8E +:10CF30006946284690470020EEE78DF80070628928 +:10CF4000022AF1D2EDE78DF800E0F0E7032012E0CD +:10CF5000042010E005200EE06089032805D203229A +:10CF600008212846FEF7BAFAD6E7B4F80D00ADF866 +:10CF7000040018B106208DF80000D8E70720FAE772 +:10CF80000920F8E70322ECE770B504460B7801208E +:10CF9000314A0A2B16D2DFE803F01F243A050D169A +:10CFA00051563D5AFEF78EFF0421FFF792FE0246CE +:10CFB000042123E0FFF78AFA08B1FDF7E9FEFEF746 +:10CFC00067FF28B970BDFFF781FA08B1FDF7E0FEF1 +:10CFD000BDE87040FFF7FAB81071486800785071EA +:10CFE00070BD02201071486800780128F8D1FEF762 +:10CFF000CDFF0221FFF76DFE024602212046FEF71B +:10D000006DFA0028ECD0BDE87040FDF7C1BE0320EA +:10D01000107170BD12792046012A07D0022A09D06A +:10D02000032AF6D1BDE87040FDF73ABEBDE8704076 +:10D0300000F04CBABDE87040FFF7DCBB5070888848 +:10D040001081508170BD00205070108170BD20464D +:10D050001169FEF743F9D4E78C2C002006480378C9 +:10D06000012B03D1012901D102210170100001D04F +:10D07000FDF78EBE70470000542C002070B5124C96 +:10D080001E4615466078012902D0032914D107E015 +:10D09000052811D163697BB13246294604200AE094 +:10D0A000022809D1094840688047032060706369FD +:10D0B00013B1324629469847280003D0BDE8704096 +:10D0C000FDF766BE70BD0000602C002034320020E9 +:10D0D00008B50B460022014603200092FEF706FC2D +:10D0E00008BD000070B50D4E00240D487460254643 +:10D0F0000470447084702846FEF7E8FB6D1C0A2D0E +:10D10000F9D3074934600020F431746041F82040BD +:10D11000401C0328FAD3002070BD00001C2C002006 +:10D1200068300020074B1A68032A08D00A600649B5 +:10D13000006841F82200521C00201A607047042049 +:10D14000704700001C2C00205C310020F8B5074619 +:10D150001D4616460C460846FFF7C0FB58B160193D +:10D16000FFF7BCFB38B13346224639460120009513 +:10D17000FEF7BCFBF8BD1020F8BD000070B5244CD4 +:10D1800006460022A078012841D100232146A37041 +:10D190000D78022E05EB450001EBC00103D0032EF4 +:10D1A00035D10D202EE01B484668761C4660896804 +:10D1B000B1EB063F15D843606078401E6070681C74 +:10D1C000C0B220700A2801D30A38207005EB450050 +:10D1D00004EBC0011046091DFDF7E6FD2846FEF7E9 +:10D1E00075FB6078002812D0A07800280FD1FEF7D8 +:10D1F00039FB00280BD0112809D0217801EB41011F +:10D2000004EBC101BDE87040091DFDF7CDBD70BD47 +:10D21000683000201C2C002070B5012670044FF0EF +:10D22000E0210025C1F88001084C4010C4F84803F3 +:10D23000C4F8080366602F20FDF738F9A660044C97 +:10D240002F202561FDF732F9E57070BD0010014017 +:10D25000342C002010B5064A14210820FDF738FEB2 +:10D26000002803D0BDE81040FDF792BD10BD0000BE +:10D27000DC2E002010B50121202803DA1D4A814050 +:10D28000114004E0402803DAA0F12002914041B1AE +:10D29000002809DA00F00F0101F1E02191F8141DD6 +:10D2A00006E042F2010010BD00F1E02191F8001407 +:10D2B0004909082914D299B1012911D004290FD0A4 +:10D2C00005290DD00C4A946800F01F0301219940F4 +:10D2D000400944B152F820300B4342F8203007E0B7 +:10D2E00042F2020010BD800000F1E020C0F8001101 +:10D2F000002010BDFC06FFBC0C340020074801785C +:10D3000021F07E0141F00101303121F080010170F6 +:10D31000072141701021817070470000A82C002067 +:10D3200070B5164C4FF6FF75E088A84201D1082071 +:10D3300070BDE178E1B1012308221149A9DF00287D +:10D34000F6D1022300221146E088A9DF0028EFD1A0 +:10D350002A460C21E088A7DFA0F54051023905D00C +:10D36000082803D0A0F540510439E1D1002070BD58 +:10D3700000231A461946A9DF70BD00008C2C00203E +:10D38000C83200202DE9F0410746806A14468846DD +:10D39000C8B3002531E000BFB96A05EB450001EBD9 +:10D3A0008006B088801CFE2802D90C20BDE8F081E0 +:10D3B0002288401C08F8020020881622401C80B2F7 +:10D3C000208008F800202088401C80B2208000EBDC +:10D3D0000801308800F062F92188084480B220807A +:10D3E000B28842B1B16871B14044FDF7C7F82088F6 +:10D3F000B188084420806D1CEDB297F82C00A8423B +:10D40000CAD80020D2E7FFE70720CFE7FDF7C0BC6E +:10D4100018B10349C860002070470E207047000013 +:10D42000B42C00201CB5034901910091694660DFCE +:10D430001CBD0000002C002070B50E4605461446A9 +:10D4400014211046FDF7B3F801202080002121614E +:10D4500021732672657205B1A072002070BD10B5EF +:10D4600011B10020FDF794FCBDE81040FFF7F0B9C2 +:10D470000021024A0846FDF7CBBC00005FD407003C +:10D48000FDF774BC30B409B18C0702D0072030BC62 +:10D490007047074C0125A16062800649636010DF78 +:10D4A0000028F4D1257030BC1620FFF7E3BE000041 +:10D4B000B42C002081D4070018B103490861002072 +:10D4C00070470E2070470000B42C002010B588B0C3 +:10D4D00004460020009001900290039019481A4AD7 +:10D4E00040798DF800001068926804906846CDE994 +:10D4F0000502087A0C2805D003221CE0FDF748FC41 +:10D5000008B010BD4868C278016862F31F610191DC +:10D51000C279416862F31F610291C17A806861F348 +:10D520001F60039004A8FEF73BFF0028E8D001210C +:10D53000FFF7CFFB024601212046FDF7CFFF002871 +:10D54000DCD1DDE78C2C0020DCD80700FFF716BE0D +:10D550002DE9F84F204C00256846D4E903B6FFF7C3 +:10D5600083F88046FFF7EAF90746A946B8F1000FAD +:10D5700025D00023009ACC46E06819E007680146F0 +:10D58000974202D9BA1A026014E0C1F800C0D1F87B +:10D590000C80D21BC8693B448A46B8F1000F07D003 +:10D5A000F71827F07F47C1E90178CAF81C500D46EB +:10D5B0000028E3D1E0602846FFF71EFA04E0284681 +:10D5C000FFF71AFA00B917B15846FEF7BBF984F80D +:10D5D0000490BDE8F88F0000342C00200D4B10B5EE +:10D5E000D96831B102680C68A24204D8A21A0A6054 +:10D5F000C161D86010BD0B4603E00B46C969121B20 +:10D6000021B10C689442F8D3A41A0C600260C16185 +:10D61000D86110BD342C00202DE9F0411D4DEC687F +:10D62000002C35D01C48002700682969B846401AEC +:10D6300020F07F462068B04215D82246361A0744AB +:10D64000E469107C68B182F810806B6933B1D2E96B +:10D650000501984728B1FDF79BFB02E0D2E90510D0 +:10D660008847002CE6D16978A878814206D1401C11 +:10D67000C0B2A870022801D185F802800548A978B7 +:10D68000183040F82170BDE8F0411420FCF7FEBFCF +:10D69000BDE8F081342C0020041501400870000A18 +:10D6A0004870022070470870020A4A70020C8A70A3 +:10D6B000000EC8700420704738B515494888A0F599 +:10D6C0007F42FF3A23D04A78124C521CD2B24A70A1 +:10D6D000237B93420AD3091D75DF002817D0A16967 +:10D6E000002914D001B0BDE83040084700254D7036 +:10D6F000217C29B13B2176DF10B1A16901B18847B6 +:10D700006169002903D08DF800506846884738BD0C +:10D71000002C00204C300020EFF30580C005C00D28 +:10D7200013D0103840B2002806DA00F00F0000F1E4 +:10D73000E02090F8140D03E000F1E02090F80004E0 +:10D740004009022803D0062803D002207047002099 +:10D7500070470120704742788378521CD2B29342BE +:10D7600000D100220378934201D1002070470A7053 +:10D770004168407800EB400001EBC00070472DE9A4 +:10D78000FC411646DDE90854CDE900541F468046A9 +:10D79000022200F00CF8002807D1CDE900543B46E6 +:10D7A00010223146404600F002F8BDE8FC812DE928 +:10D7B000FC5F0026DDE90C4B994692468E4680467A +:10D7C0002788354630E0D8F80400002200EB8500B9 +:10D7D00069460068019001A865DF002830D19DF8F6 +:10D7E000000050451FD10EB1002100E00221228827 +:10D7F00008441118594522D846B9521C90B22080CD +:10D8000009F800E02088401C20800126218801A81A +:10D8100001EB0902694665DF002811D120889DF8D7 +:10D820000010084420806D1CB8F80000A842CADC33 +:10D8300056B12088C01B401E80B2FF2802D90C20A0 +:10D84000BDE8FC9F09F807000020F9E710B5074C78 +:10D8500048DF08B1FDF79CFAFDF72AFB2078022883 +:10D8600003D0032801D00428F2D110BD542C00208D +:10D87000010000070200000000000000000000009E +:10D88000000000002C30002023D1BCEA5F78231573 +:10D89000DEEF1212000000005DD007000000000063 +:10D8A0000000000002000000000000000000000076 +:10D8B000000000000000000084310020040000008F +:10D8C0000000000000000000000000000000000058 +:10D8D0007DD00700000000000000000003000000F1 +:10D8E0000000000000000000050000000000000033 +:10D8F0000000000000000000000000001CD907002C +:10D90000002C0020CC0000007CA90700E8D907000B +:10D91000CC2C00204C2700008CA907000000000040 +:10D9200000000000000000000000000000000000F7 +:10D9300000000000000000000000000000000000E7 +:10D9400000000000000000000000000000000000D7 +:10D9500000000000000000000000000000000000C7 +:10D9600000000000000000000000000000000000B7 +:10D9700000000000000000000000000000000000A7 +:10D980000000000000000000000000000000000097 +:10D990000000000000000000000000000000000087 +:10D9A0000000000000000000000000000000FFFF79 +:10D9B0000000000032000000000000000000000035 +:10D9C0000000000000000000000000000000000057 +:10D9D0000000000000000000000000000000000047 +:08D9E000000000000090D003DC +:020000041000EA +:0410140000A0070031 +:020000041000EA +:0410180000E00700ED +:040000050007A4014B +:00000001FF diff --git a/bootloaders/primo/dummy.txt b/bootloaders/primo/dummy.txt deleted file mode 100644 index 5c3118d..0000000 --- a/bootloaders/primo/dummy.txt +++ /dev/null @@ -1 +0,0 @@ -dummy file diff --git a/bootloaders/primo_core/bootloader_primo_core_v1.0.0.hex b/bootloaders/primo_core/bootloader_primo_core_v1.0.0.hex new file mode 100644 index 0000000..4abbdc9 --- /dev/null +++ b/bootloaders/primo_core/bootloader_primo_core_v1.0.0.hex @@ -0,0 +1,934 @@ +:020000040007F3 +:10A000001854002059A5070061A5070063A50700A3 +:10A0100065A5070067A5070069A507000000000007 +:10A0200000000000000000000000000015A4070070 +:10A030006DA50700000000006FA5070071A50700CF +:10A0400073A5070073A5070073A5070073A5070094 +:10A0500073A5070073A5070073A5070073A5070084 +:10A0600073A5070073A5070073A5070073A5070074 +:10A0700073A5070073A5070073A5070073A5070064 +:10A0800073A50700C5A6070073A5070073A5070001 +:10A09000E9A6070073A50700EDA6070073A5070052 +:10A0A00073A5070073A5070073A5070073A5070034 +:10A0B00073A5070073A50700000000000000000062 +:10A0C00073A5070073A5070073A5070073A5070014 +:10A0D00073A5070073A5070073A507000000000023 +:10A0E0000000000000000000000000000000000070 +:10A0F0000000000000000000000000000000000060 +:10A10000000000000000000000000000000000004F +:10A11000000000000000000000000000000000003F +:10A12000000000000000000000000000000000002F +:10A13000000000000000000000000000000000001F +:10A14000000000000000000000000000000000000F +:10A1500000000000000000000000000000000000FF +:10A1600000000000000000000000000000000000EF +:10A1700000000000000000000000000000000000DF +:10A1800000000000000000000000000000000000CF +:10A1900000000000000000000000000000000000BF +:10A1A00000000000000000000000000000000000AF +:10A1B000000000000000000000000000000000009F +:10A1C000000000000000000000000000000000008F +:10A1D000000000000000000000000000000000007F +:10A1E000000000000000000000000000000000006F +:10A1F000000000000000000000000000000000005F +:10A20000000000000000000000000000000000004E +:10A21000000000000000000000000000000000003E +:10A22000000000000000000000000000000000002E +:10A23000000000000000000000000000000000001E +:10A24000000000000000000000000000000000000E +:10A2500000000000000000000000000000000000FE +:10A2600000000000000000000000000000000000EE +:10A2700000000000000000000000000000000000DE +:10A2800000000000000000000000000000000000CE +:10A2900000000000000000000000000000000000BE +:10A2A00000000000000000000000000000000000AE +:10A2B000000000000000000000000000000000009E +:10A2C000000000000000000000000000000000008E +:10A2D000000000000000000000000000000000007E +:10A2E000000000000000000000000000000000006E +:10A2F000000000000000000000000000000000005E +:10A30000000000000000000000000000000000004D +:10A31000000000000000000000000000000000003D +:10A32000000000000000000000000000000000002D +:10A33000000000000000000000000000000000001D +:10A34000000000000000000000000000000000000D +:10A3500000000000000000000000000000000000FD +:10A3600000000000000000000000000000000000ED +:10A3700000000000000000000000000000000000DD +:10A3800000000000000000000000000000000000CD +:10A3900000000000000000000000000000000000BD +:10A3A00000000000000000000000000000000000AD +:10A3B000000000000000000000000000000000009D +:10A3C000000000000000000000000000000000008D +:10A3D000000000000000000000000000000000007D +:10A3E000000000000000000000000000000000006D +:10A3F000000000000000000000000000000000005D +:10A40000DFF80CD000F0ECF800480047C5CA0700A0 +:10A41000185400206FF00200704502D1EFF309815B +:10A4200001E0EFF30881886902380078004A10479C +:10A4300005A60700401E00BF00BF00BF00BF00BF51 +:10A4400000BF00BF00BF00BF00BF00BF00BF00BF14 +:10A4500000BF00BF00BF00BF00BF00BF00BF00BF04 +:10A4600000BF00BF00BF00BF00BF00BF00BF00BFF4 +:10A4700000BF00BF00BF00BF00BF00BF00BF00BFE4 +:10A4800000BF00BF00BF00BF00BF00BF00BF00BFD4 +:10A4900000BF00BF00BF00BF00BF00BF00BF00BFC4 +:10A4A00000BF00BF00BFC5D170470000401E00BF05 +:10A4B00000BF00BF00BF00BF00BF00BF00BF00BFA4 +:10A4C00000BF00BF00BF00BF00BF00BF00BF00BF94 +:10A4D00000BF00BF00BF00BF00BF00BF00BF00BF84 +:10A4E00000BF00BF00BF00BF00BF00BF00BF00BF74 +:10A4F00000BF00BF00BF00BF00BF00BF00BF00BF64 +:10A5000000BF00BF00BF00BF00BF00BF00BF00BF53 +:10A5100000BF00BF00BF00BF00BF00BF00BFC5D16C +:10A5200070470000056885F308884068FF2464B21E +:10A53000EFF30585002D01D1A646004725460646C6 +:10A5400021273FBAF0B40024002500260027F0B4EC +:10A55000F92040B2004700000648804706480047FF +:10A56000FEE7FEE7FEE7FEE7FEE7FEE7FEE7FEE7C3 +:10A57000FEE7FEE711A7070001A4070040EA010378 +:10A580009B0703D009E008C9121F08C0042AFAD2A9 +:10A5900003E011F8013B00F8013B521EF9D270476D +:10A5A000D2B201E000F8012B491EFBD27047002215 +:10A5B000F6E710B513460A4604461946FFF7F0FFC2 +:10A5C000204610BD30B504460020034600E05B1C69 +:10A5D000934203D2E05CCD5C401BF8D030BD00005C +:10A5E000064C074D06E0E06840F0010394E80700E0 +:10A5F00098471034AC42F6D3FFF706FFF8D80700AF +:10A6000018D9070010B50C4610B10120086010BD24 +:10A61000216891B11148421A814213D03C2A0ED2CE +:10A6200001223C3011F13C0111F8013900F80139E7 +:10A630001346521C3C2BF7D904E00E200BE03C22C1 +:10A64000FFF79CFF00223C21044801F045FA034932 +:10A65000891E08800020206010BD000082FF0020BD +:10A6600000F01F02012191404009800000F1E0202C +:10A67000C0F88012704700F01F020121914040098C +:10A68000800000F1E020C0F80011704700F01F02C8 +:10A69000012191404009800000F1E020C0F8001243 +:10A6A00070474907090E002806DA00F00F0000F194 +:10A6B000E02080F8141D704700F1E02080F80014BD +:10A6C0007047000007480021C0F84011C0F844114D +:10A6D000C0F84811C0F84C11C0F80011C0F80411BE +:10A6E00002F098BF0010014002F030BF074810B5DB +:10A6F000406830B18047002807D0BDE8104000F026 +:10A7000047BBBDE8104002F0A3B810BDB42C002038 +:10A7100070B58D480023021D0178101D0124062903 +:10A7200007D115782D0704D10578C5F30315032D3E +:10A7300002D0062904D00FE0854E844D75670EE0E7 +:10A7400015782D0708D10578C5F30315032D06D01C +:10A75000042D04D0052D02D0062909D010E04FF0B9 +:10A760008055D5F844527B4EC5F34235356002E042 +:10A7700015782D0704D10578C5F30315032D02D0F4 +:10A78000062906D011E0744D2E6826F080762E60E2 +:10A790000EE015782D0708D10578C5F30315032DB4 +:10A7A00006D0042D04D0052D02D006290AD011E0D0 +:10A7B0004FF08045C5F80C31C5F81031654D2D1F9F +:10A7C0002B6002E015782D0704D10578C5F3031539 +:10A7D000032D02D0062905D00CE05E4A0321643225 +:10A7E00011600AE015782D0704D10578C5F303152B +:10A7F000032D02D006290DD06AE0584A05211160C8 +:10A80000564978310C60554908310B603F21121DC3 +:10A81000116002E0117809075AD10078C0F30310E3 +:10A82000052855D14E4800684E4908604C48001D27 +:10A830000068091D08604A4808300068091D086062 +:10A8400047480C300068091D086045481030006812 +:10A85000091D0860424814300068091D086040481E +:10A86000183000683F49203108603D481C300068BE +:10A87000091D08603A4820300068091D0860384802 +:10A8800024300068091D0860354828300068091D1B +:10A89000086033482C300068091D086030483030AB +:10A8A00000683049403108602D4834300068091D87 +:10A8B00008602B4838300068091D086028483C3083 +:10A8C0000068091D0860264840300068091D0860BE +:10A8D00021487438016841F470010160BFF34F8F63 +:10A8E000BFF36F8F4FF01021D1F80002002803DB77 +:10A8F000D1F80402002822DA1B4A14601B480468BD +:10A90000002CFCD01524C1F800420568002DFCD0B5 +:10A91000C1F8044201680029FCD0136001680029D5 +:10A92000FCD0BFF34F8F0C48F0380168104A01F497 +:10A93000E06111430160BFF34F8F00BFFDE70E4997 +:10A940000C48086070BD0000E00F00F00DF0ADBADB +:10A9500000C007403C050040FCED00E01056004000 +:10A960000404001020C5004004E5014000E401405B +:10A970000400FA050090D003C82C002002E008C8AB +:10A98000121F08C1002AFAD170477047002001E069 +:10A9900001C1121F002AFBD170472DE9FC5F002581 +:10A9A000168807461580406B14468946A8464FF026 +:10A9B0000C0A11234FF0020B98B101784846032E80 +:10A9C00048D380F800B02288521C92B2228083546F +:10A9D0002288521C92B2228009F802102088401C62 +:10A9E00020803A6BDAB12088494600F11205B54261 +:10A9F00030D80B5420881023401C80B220800B5488 +:10AA00002088401C20800F202388155C401ECD54D8 +:10AA1000238840B25B1C23800028F5DA454697F86E +:10AA20002E0030B133462246494601F05DFF050055 +:10AA300011D197F82D0030B132462146484600F03A +:10AA40001DFC050007D1B87820B32088C846001D3A +:10AA5000B04202D95046BDE8FC9F684679DF002825 +:10AA6000F9D12188032008F8010020881921401C11 +:10AA700080B2208008F800102088401C80B220801E +:10AA800000EB0801BDF8000002F006FE0146208838 +:10AA900008442080F878B8B1228897F90310D31CB5 +:10AAA0004846B342D6D800F802B022880123521C8F +:10AAB00092B2228083542288521C92B2228009F8DA +:10AAC00002102088401C20807868B8B1218890F955 +:10AAD0000020CB1C4846B342BCD800F801B0218806 +:10AAE0000A23491C89B2218043542188491C89B218 +:10AAF000218009F801202088401C2080388950B12D +:10AB0000CDE900464B460622022107F1080002F07B +:10AB100034FE05009FD1388A50B1CDE900464B463E +:10AB20000722032107F1100002F027FE050092D151 +:10AB3000388B50B1CDE900464B461522142107F160 +:10AB4000180002F01AFE050085D1386A30B133468C +:10AB50002246494600F04CFF0500DBD1786A30B14F +:10AB600033462246494602F025F80500D2D197F82F +:10AB70002C0038B1334622464946384602F000FCE4 +:10AB80000500C7D1387838B13346224649463846A1 +:10AB900002F042F80500BDD128465CE710B5044636 +:10ABA000114890B0382180798DF83A0041F2305048 +:10ABB000ADF838006846FFF7FAFC02208DF8000077 +:10ABC00000208DF802000120ADF808000EA80390C7 +:10ABD0008DF803400021684600F0A8FA002801D053 +:10ABE00000F0D6F810B010BD643200202DE9F0431B +:10ABF000284C8BB0A078002838D118212648FFF7C0 +:10AC0000D6FCA046E078244C00254FF02809012608 +:10AC100078B304F148070095019502951022694622 +:10AC2000F81D0395FFF7CEFC20B3F81DCDE908070A +:10AC300009A8049008A806908DF814608DF81C608F +:10AC40000420FFF7ABFF25700220207204A8E0600B +:10AC5000A4F810906582104873DF08B100F098F8EE +:10AC60000E494FF40030086088F802600BB0BDE870 +:10AC7000F08307E00520FFF791FF267067602572DB +:10AC80002582E7E70620FFF789FF257065602572BA +:10AC9000DEE700008C2C00204C3200200C05005018 +:10ACA0002DE9FF410546087A0E4681070AD00322A6 +:10ACB0001146284600F012FC002801D000F068F888 +:10ACC000BDE8FF812849009001F06CFD010011D121 +:10ACD000254C7168143C009A6069FFF74FFC00989E +:10ACE00001F02CFD010005D169461F4801F036FD39 +:10ACF000010003D0284601F0B7F8E1E704200190F5 +:10AD00000098800802906069039001A801F076F82D +:10AD100007000BD0092F10D0606901F0D9FC0100A9 +:10AD200002D0284601F0A0F83946E3E7307A2169DD +:10AD3000084420616069A061C2E7307A216901445A +:10AD4000216160780028BBD06089401E0004000C9F +:10AD50006081B5D1284600F089FB08B100F018F8F1 +:10AD600020896081ACE70000A02C0020BFF34F8F4A +:10AD700005480168054A01F4E06111430160BFF331 +:10AD80004F8F00BFFDE700000CED00E00400FA0566 +:10AD90000EB500210091CDE901106A4644F2010090 +:10ADA000FFF7E4FF0EBD000038B50C46064BE26825 +:10ADB000097853F8225062680092024604F10C00B0 +:10ADC0006369A84738BD00005C31002070B50F4BA7 +:10ADD000054605201C785E78A64216D01C781878A7 +:10ADE0009E88B04201DA401C00E0002018705E88A6 +:10ADF000D86804FB06002860986800EBC4039B88B1 +:10AE00000B8050F834001060002070BD242C00200E +:10AE10002DE9F84F254C8146924660880E468142C6 +:10AE200041D800274FF6FF788DF80070684600F093 +:10AE30003FF922786078A188884201DA401C00E05E +:10AE40000020C0B2904204D19DF8000000F054F9F7 +:10AE500027E065786078884201DA401C00E0002035 +:10AE600060709DF8000000F047F9454519D0A068D2 +:10AE7000B9F1000F40F835A00DD066B16188E068E7 +:10AE8000324605FB01004946FFF778FBA06800EB5E +:10AE9000C500868002E000EBC50087800020BDE889 +:10AEA000F88F0420FBE70920F9E70000242C00209C +:10AEB0000EB504E0029ABDF804100098904702AA6B +:10AEC00001A96846FFF782FF0028F3D00EBD0000FD +:10AED00030B5CB0008339DB293074FF0000401D08A +:10AEE000072030BD044B9A602A44DA605C701C7005 +:10AEF00058809980002030BD242C0020074B9B688F +:10AF000023B12AB120B10068037C1BB1082070472F +:10AF1000072070474174426100207047342C0020A4 +:10AF200010B50446082902D00020FFF731FFD4E90C +:10AF30000010BDE8104008471CB5044ACDE90001E7 +:10AF400008216846FFF764FF1CBD000021AF070021 +:10AF50002DE9F0478046994614460F4690074FF07A +:10AF6000000634D11C4D8CB302F054F90320C5F80F +:10AF70001490287004F11800002107EB4703AC601F +:10AF800004EBC102491C167056709770506000EBBC +:10AF9000C3000329F4DBEE606E70AE701420FFF77F +:10AFA0005FFB06211420FFF77CFB1420FFF763FBF7 +:10AFB0000A49C1F8008006211120FFF772FB0748FB +:10AFC000001F006828610020BDE8F08701E007202D +:10AFD000FAE7AE60FBE70000342C002008150140C2 +:10AFE0002DE9F843DFF870900D461746D9F80810A0 +:10AFF000044651B34CB3052D02D20720BDE8F883B7 +:10B00000606910B3607C012821D0002602F082FB29 +:10B01000D9F80810804601EBC800694602F099FB98 +:10B02000B8B1012100F8041B0C4910C0096880E880 +:10B03000E200D9F8081001EBC8009DF8001041703B +:10B040001420FFF723FB0020D8E70820D6E72E4680 +:10B05000DCE70420D2E70000342C00200415014076 +:10B06000F8B5124E0446B168E1B1DCB16069C8B10F +:10B070000027277402F04EFB0546B068694600EBD6 +:10B08000C50002F066FB78B1022101704460B0682F +:10B090009DF8001000EBC50041701420FFF7F6FA90 +:10B0A0003846F8BD0820F8BD0420F8BD342C002037 +:10B0B00030B5EFF3108172B60D4A9468012324B1C4 +:10B0C0000370002900D162B630BD93604FF0E023D9 +:10B0D000D3F88051074C25401560C3F88041D3F860 +:10B0E000844154605A17C3F8842100220270E8E7B3 +:10B0F0000C340020FC06FFBC0B498A68002A11D0E2 +:10B1000000280FD1EFF3108072B60B684FF0E022E9 +:10B11000C2F800314B68C2F8043100228A6000286E +:10B1200000D162B6704700000C340020F0B50C4628 +:10B1300093B01F210726ADF80010ADF804100027CA +:10B1400050B1C17849070DD56A4602A9FFF725FC21 +:10B15000002808D102AD02E00025ADF800706CB106 +:10B16000E07810B1304613B0F0BD01AA0AA920461C +:10B17000FFF713FC0028F6D10AAA02E00022ADF87E +:10B180000470BDF80400C3B2BDF80000C1B2284687 +:10B1900072DFE8E770B5054601461C220F48FFF74D +:10B1A000EDF90F4C00262670286830B10168616007 +:10B1B0004068A060201D7ADF02E00948001D7BDFA7 +:10B1C00000280AD14FF6FF7060806670BDE87040BD +:10B1D000044A00210448FFF791BE70BD4C300020A6 +:10B1E000002C0020B5D6070080D8070070B51E4C93 +:10B1F000068800251B49E289102E15D01B4B112E05 +:10B200001CD0122E29D0502E17D130F8061F914293 +:10B2100013D14189022910D1007B10F0010F11D008 +:10B22000BDE8704000F02ABC83884B808369CB6006 +:10B23000C06908614D70002AF2D070BD4FF6FF70F2 +:10B2400048804D701868FFF70BFF0028F5D0A16902 +:10B250000029F2D0BDE8704008478268CA60C06823 +:10B260000861DDE7002C00204C30002080D807006A +:10B2700001480068FFF7F4BE80D807007CB505469A +:10B2800008880C460930904201D90C207CBD6846E4 +:10B2900071DF0028FAD121880820685420881B21FA +:10B2A000401C80B2208029542088401C80B220801D +:10B2B0002844DDF801100160BDF805108180208868 +:10B2C000801D80B220809DF8001001B10121295419 +:10B2D0002088401C208000207CBD0000F0B585B097 +:10B2E0000A4605002FD02888A0F57F41FF392CD0D1 +:10B2F0001748007848B3164C1020641C01272070B2 +:10B3000007206070A7700320ADF810000026E11C34 +:10B31000104602F0C6F9BDF8101000960844ADF8CA +:10B320001000019602960396288AADF8000004A842 +:10B33000CDE902048DF80270ADF8046028886946F2 +:10B34000A6DF05B0F0BD0E20FBE70820F9E70000FE +:10B35000142C002030B585B00D46040039D0BDB3A3 +:10B360006868A8B31C4B4FF6FF7020800FCB0DF11F +:10B37000040C8CE80F0041F23050ADF8000001A839 +:10B38000811E63DF002821D1221D69460120A0DF34 +:10B3900000281BD19DF80200A071204600F08EFE0F +:10B3A000002813D1204600F0DAFC00280ED12946EF +:10B3B000204600F013FF002808D16868A062A86842 +:10B3C00000B1E062054901200870002005B030BDE1 +:10B3D000FFE70E20FAE7000084D80700142C0020B5 +:10B3E0003EB500283CD000293AD0826A002A37D0E6 +:10B3F0000A88102A23D0112A33D0502A21D0512A6A +:10B400002ED104460A4611F8060F022828D1488892 +:10B41000238A984224D1907B042821D006281FD06B +:10B4200005281DD02046891C01F030FD002817D0CA +:10B43000E16A002914D003B0BDE83040084789888C +:10B4400011E0CA8803899A420AD108228DF80020A7 +:10B450000A7C12318DF808200191826A6946904772 +:10B460003EBD4FF6FF7101803EBD0000F0B585B0D6 +:10B470000A4605002BD02888A0F57F41FF3928D047 +:10B480001548007828B3144C1120641C0127207043 +:10B490000026ADF81070611C104602F002F9BDF8EC +:10B4A000101000960844ADF810000196029603961D +:10B4B000288AADF8000004A8CDE902048DF80270D6 +:10B4C000ADF8046028886946A6DF05B0F0BD0E20FF +:10B4D000FBE70820F9E70000142C002030B585B008 +:10B4E00028B30388A3F57F44FF3C22D0124B1B787E +:10B4F000FBB1114B10255B1C00241D7059709A7014 +:10B5000001250321ADF810100094019402940394D6 +:10B51000018AADF8001004A9CDE902138DF802509C +:10B52000ADF8044000886946A6DF05B030BD0E20A6 +:10B53000FBE70820F9E70000142C002010B50446B2 +:10B54000FFF754FE21460448FFF74AFF2046BDE8B6 +:10B55000104001F0E5BB00006432002030B51C490A +:10B560008BB00A1D0DF1140C3CCA8CE83C00096834 +:10B57000099120B105A818DF08B1FFF709FC4FF4C5 +:10B58000F42013DF08B1FFF703FC124B48221249E5 +:10B5900009A801F075FF08B1FFF7FAFB01216A461F +:10B5A000084601F047FF08B1FFF7F2FB01208DF8D4 +:10B5B0000C00684601F034FF08B1FFF7E9FB0748CB +:10B5C00001F078FF002801D0FFF7E2FB0BB030BD9F +:10B5D0006CD807006DD40700942E002049D50700D1 +:10B5E00070B511DF08B1FFF7D3FB4FF0E021002069 +:10B5F000D1F800315FF0010404FA00F21A420AD0D7 +:10B6000042B202F01F0504FA05F15209920002F15C +:10B61000E022C2F88011401C2028EDD34FF44054A2 +:10B62000A06813DF08B1FFF7B3FBA068BDE8704066 +:10B6300000F010B908B5684600F00EF900980178DE +:10B64000A52904D00079AA2801D0002008BD012036 +:10B6500008BD10B500F04EFE18B900F0DBFA002866 +:10B6600013D0642001F026FB00F007FE08B1FFF7BD +:10B670008FFB00F03FFE08B1FFF78AFB00F0A4FA51 +:10B68000041E01D0FFF784FB204610BD00B587B033 +:10B690001422084901A8FEF771FF05980DF1040C6A +:10B6A00000909CE80F0000F01BF802F0CDF807B006 +:10B6B000002000BDA0D8070010B500F021FC002834 +:10B6C0000BD100F027FF044602F0BEF8034910201A +:10B6D0000860024910390860204610BD1805005066 +:10B6E0002DE9F84304460E4617461D466846DDF828 +:10B6F000208000F0B1F814F0FF024FEA14412F4C03 +:10B700002F484FF001034FF0FF0C10D0012A14D046 +:10B71000032A23D00021022A2DD0052A3FD0042A53 +:10B7200044D0062A01D104202070BDE8F88341806E +:10B730008560037080F804C00BE04180F119294452 +:10B740008160A521017080F804C0C0F81880C6602F +:10B750000EE023701A4800F065F8E6E700990A78D1 +:10B7600002704A88428089688160AA210171C6609E +:10B77000C0E90475EDE7009A1578A52D0AD005708B +:10B78000558845809268826080F804C0C16001617C +:10B790004161DEE74180816080F800C0F4E700F09D +:10B7A00095FE08B1FFF7F4FA0320BDE74180816000 +:10B7B00080F800C0009909790171CBE7542C002072 +:10B7C000683100200EB5084A07CA8DE8070001F06D +:10B7D00087FC002806D105494FF4FE2048606846E2 +:10B7E00001F09EFC0EBD000094D80700582C0020EC +:10B7F00038B50446684600F02FF8009800782070AD +:10B8000000984088608000988168A160017921716A +:10B81000C168E16001692161416961618069A0617C +:10B8200038BD000010B504461C21094801F04EFC4B +:10B8300008B1FFF7ADFA00231C222146044801F0AD +:10B8400083FC002803D0BDE81040FFF7A1BA10BD6B +:10B85000582C0020FEF766BE0149016070470000C9 +:10B8600000F0070070B5174C0F20217801EB410163 +:10B8700004EBC1021449536912F8045F4968012DB1 +:10B880000ED0032D0BD14FF08050006980B2B3FB76 +:10B89000F0F0084428DF002801D10121A17070BD1B +:10B8A0000803516856690D1A3118926818441044FB +:10B8B000B5F5805F02D2AA0829DFECE74FF4806279 +:10B8C00029DFE8E7683000201C2C002000EB400254 +:10B8D0000548002100EBC200032201718160C0E92C +:10B8E00004218161C1607047683000202DE9F0417A +:10B8F000134C069E65780A2D1FD027783D44EDB283 +:10B900000A2D01D30A3DEDB205EB450504EBC50553 +:10B910002871AA61D1E90001C5E90401C5E902362F +:10B92000A178002021B9FFF79DFF112800D1002048 +:10B930006178491C6170BDE8F0810420FBE70000DC +:10B94000683000202DE9F05F2648C168F1B3DFF8C8 +:10B9500098B00E688146DBF800400769C078E11BAB +:10B9600021F07F45ED1CB0B9DFF880804FF48030C6 +:10B97000C8F84403C8F804031120FEF771FE112033 +:10B98000FEF779FE4FF0010AC8F800A02F20FEF75D +:10B990008DFD89F803A0B54200D23546124BE81957 +:10B9A0003C3320F07F401860DBF800105A46091B3A +:10B9B00021F07F41001B20F07F40C91C81420FD93C +:10B9C000106818602F20FEF771FD00E004E0BDE86C +:10B9D000F05F1120FEF75ABEBDE8F05F01F01ABC1F +:10B9E000BDE8F09F342C00200415014000100140F8 +:10B9F0002DE9F04117880646B81D14460D469842B9 +:10BA000002D90C20BDE8F0813188072206290AD32B +:10BA10004FF4486C4FF6FF73614501D9994202D14A +:10BA20007088062801D21046ECE7604501D998429B +:10BA3000F9D1994203D0984201D08142F3D8052030 +:10BA4000E85520881221401C80B22080295420888B +:10BA5000401C80B220804119308801F01DFE2188F1 +:10BA6000084480B220804119708801F015FE2188B9 +:10BA7000084420800020C5E7FFF78AB9F8B51348CD +:10BA800000F01EFF114D0026114C0C3D30B16169D4 +:10BA9000B9B101208DF80000684611E0287820B186 +:10BAA000616971B18DF80060F6E7687858B1A168F6 +:10BAB000084800220068FFF793FA10B1A16901B1AC +:10BAC00088472E70F8BD6168F2E700000C2C00205A +:10BAD0004C30002080D8070010B5044612B1108801 +:10BAE000002211E04FF6FF70FAE7030A43EA002351 +:10BAF000A05C5840C0F30313584080EA003080B285 +:10BB0000C3B280EA4310521C8A42EED310BD00003B +:10BB100030B5164C87B01648E16801F0D7FA08B185 +:10BB2000FFF736F91248E268083800234168083008 +:10BB300001F00AFB050015D1142101A8FEF737FD1D +:10BB400000208DF804000A480DF1040C203880888C +:10BB5000ADF80600E0680490059800909CE80F009E +:10BB6000FFF7BEFD07B0284630BD0000A43100201D +:10BB7000802C0020032161F3070008B50549064B1E +:10BB8000898861F31F400ECB0094FFF7A9FD0020C8 +:10BB900008BD0000602C0020A8310020012138B52C +:10BBA00061F307000649074B898861F31F404FF492 +:10BBB00040518C680ECB0094FFF792FD002038BDF9 +:10BBC000602C0020A831002000B58BB004A8FFF73E +:10BBD0000FFE089800280CD0079860B107990A98C2 +:10BBE00008440021CDE900100898800802906846BA +:10BBF00018DF0BB000BD4FF440508068C0F5EE2256 +:10BC0000C0F5F421C2F30C02891AA1F5405100EBF2 +:10BC10005100E6E700B58BB004A8FFF7E9FD0898EE +:10BC200000280FD0079878B107990A980844032193 +:10BC300000914FF4F421CDE9011008988008039099 +:10BC4000684618DF0BB000BD4FF440508068C0F567 +:10BC5000EE22C0F5F421C2F30C02891AA1F540517D +:10BC600000EB5100E3E7000070B504000BD00022A8 +:10BC70003C210B48FFF730FF094DAD1E2988884253 +:10BC800003D00B2070BD0E2070BD3C22044920461D +:10BC9000FEF774FC2888401C2880002070BD00003E +:10BCA00082FF002070B515460A4603281FD00428DD +:10BCB00024D1134C39B1E088A0F57F41FF391DD064 +:10BCC000132176DF0DE0284600F002FD08B1FFF7F2 +:10BCD0005FF8A069A84211D1012203210948FFF7AA +:10BCE000FDFB00280AD0BDE87040FFF751B80121E4 +:10BCF000104600F0ECFF02460121EFE770BD0000A6 +:10BD00008C2C00206432002000B587B01422074933 +:10BD100001A8FEF733FC05980DF1040C00909CE897 +:10BD20000F00FFF7DDFC07B000BD0000B8D807002A +:10BD300070471FB503230093CDE9010190080390DC +:10BD4000684618DF04B010BD1FB501230093CDE98C +:10BD5000010190080390684618DF04B010BD30B5AB +:10BD60008FB005461C216846FEF721FC00249DF893 +:10BD70000000019440F018008DF800000394049432 +:10BD800005940694A8798DF8360041F23150ADF84B +:10BD900034000C949DF83000142120F0FF008DF841 +:10BDA00030009DF8310020F00F00401C20F0F00022 +:10BDB00010308DF831009DF8320020F00600801C14 +:10BDC00020F0080040F011008DF8320007A8FEF7BF +:10BDD000EEFB0DA807900CA80890ADF824401720A2 +:10BDE000ADF82640ADF828000B94A88805F11003A3 +:10BDF00007AA6946A2DF0FB030BD000070B505008C +:10BE000004D0287A800703D0102070BD0E2070BDAA +:10BE1000144C6078032803D0042801D0052801D0F1 +:10BE2000082070BD6868E1688600A06830448842D8 +:10BE300004D94FF0FF30A0600C2070BD00F028FB4B +:10BE40000028FAD1A9683246A368206901F07CF97C +:10BE50000028F2D1A1683144A160E2689142ECD09F +:10BE6000092070BD602C002010B50A46044603214D +:10BE7000104600F02CFF024603212046FFF72EFB60 +:10BE8000002803D0BDE81040FEF782BF10BD0000BF +:10BE9000094810B54078072801D0082010BD074890 +:10BEA0000068FFF7DDF808B1FEF772FF044880680C +:10BEB000BDE8104000470000602C0020B4D8070007 +:10BEC0003432002010B50D4C6078052813D1D4E928 +:10BED000020188420FD10620607000F0D9FA0028D4 +:10BEE00008D12069E168406800F096F8002801D187 +:10BEF0000721617010BD082010BD0000602C0020DB +:10BF0000FEB51F4E1F4C06F11800002507C88DE82E +:10BF10000700A570A58004F12001684601F000F932 +:10BF200008B16570FEBD4FF440501649806820315D +:10BF3000C0F5EE224860216AA161C0F5F421C2F388 +:10BF40000C02891AA1F5405100EB51000D490E4A2F +:10BF50001831486000210A48FEF7D0FF08B1FEF70B +:10BF600017FF00224FF470113068FFF739F808B15D +:10BF7000FEF70EFF0120A56060700020FEBD0000EE +:10BF8000B4D80700602C002075C4070010B5094C18 +:10BF90000820A16801B110BD61780429FBD1A17806 +:10BFA000054800F047F810B10021A17010BD05212F +:10BFB000617010BD602C0020B43100202DE9F041EB +:10BFC000134C07466078032805D0042805D00825BF +:10BFD0002846BDE8F08104206070A06808B1082000 +:10BFE000F7E700F055FA0500F3D178688600A078ED +:10BFF0008119802901D90920EBE7064AB968104464 +:10C000003246FEF7BBFAA0783044A070E0E70000AB +:10C01000602C0020B431002010B50022FFF75CFD39 +:10C0200003490988884201D00B2010BD002010BDB3 +:10C030004032002070B500250C290ED34318018929 +:10C04000044600EB4101581A0A38C2B21948022AC4 +:10C05000027002D30A318B4201D2092070BD16480A +:10C06000FEF78CFA4FF010214FF6FF70B1F88020E8 +:10C07000824202D02388934219D1B1F882108142C2 +:10C0800002D06088884212D120894FF6FE724FF4A8 +:10C090004053A8420BD904EB45014989914204D091 +:10C0A0009E896D1CB6B2B142F3D1002070BD0B2049 +:10C0B00070BD0000882C00204032002030B58FB0C9 +:10C0C00005461C216846FEF772FA00249DF8000020 +:10C0D000019440F004008DF800000394049405944A +:10C0E0000694A8798DF8360041F23250ADF834004C +:10C0F0000C949DF83000142120F0FF008DF83000E2 +:10C100009DF8310020F00F00401C20F0F0001030AE +:10C110008DF831009DF8320020F00600801C20F0E0 +:10C12000180040F001008DF8320007A8FEF73FFA32 +:10C130000DA807900CA80890ADF824401420ADF885 +:10C140002640ADF828000B94A88805F1080307AA3B +:10C150006946A2DF0FB030BD084910B5A1F120003B +:10C16000016102214170C168203000F0AFFF00285A +:10C1700003D0BDE81040FEF70BBE10BD802C0020A0 +:10C180000E4910B5A1F118000161022141704FF470 +:10C1900040508068C0F5F421C0F5EE20C0F30C00DB +:10C1A000081AA0F540504108044800F08FFF00280D +:10C1B00003D0BDE81040FEF7EBBD10BD782C002089 +:10C1C0000149486170470000602C002038B5062501 +:10C1D00065F307000094FFF783FA38BD70B58EB0A1 +:10C1E0000E4605461C216846FEF7E1F900249DF83D +:10C1F0000000019440F002008DF8000003940494C4 +:10C2000005940694A8798DF8360041F23450ADF8C3 +:10C2100034000C949DF83000142120F00F00401CD5 +:10C2200020F0F00010308DF830009DF8310020F043 +:10C23000FF008DF831009DF8320020F00600801CD0 +:10C2400020F0180040F001008DF8320007A8FEF73A +:10C25000AEF90DA807900CA808900220ADF82400B4 +:10C26000ADF82640ADF828000B96A88805F120030C +:10C2700007AA6946A2DF0EB070BD2DE9F04188B073 +:10C280006846FFF7B5FA039800282BD0069A00F508 +:10C2900080504FF48051904227D9A2F58050440835 +:10C2A0001046039A00EB440687184FF440500D46A1 +:10C2B000806801EB440890420ED204F580500422BD +:10C2C0000146FFF741FD00280CD12946042208460B +:10C2D000FFF73AFD002805D1BA1B234641463046F8 +:10C2E00000F038F808B0BDE8F081039A0698FFF72F +:10C2F0002BFDF7E730B58FB002A8FFF779FA059864 +:10C30000002817D0089A00F580504FF480519042D1 +:10C3100016D9A2F58050430810464FF44054059AB0 +:10C32000A468059D024400EB430001EB4301AC42CD +:10C3300002D20E200FB030BD121A00F00BF8F9E750 +:10C340000BAB032083E80700059880080E900BA82C +:10C3500018DFEFE72DE9F0411C4617460D4606466B +:10C36000FFF7E7FC002818D0B5F5805F07D9234612 +:10C37000291B301B1A46FFF7EDFF00280DD13A4666 +:10C3800029463046FFF7E0FC002806D13A46294608 +:10C390003046BDE8F041FFF7CCBCBDE8F0810000BD +:10C3A00070B542682A48D2E90043D2E90212C0E9D6 +:10C3B0000212C0E900430078440701D5840705D183 +:10C3C0009C0703D18C0701D1940701D0062070BDD2 +:10C3D000204D0B441A44EA60B1F5A04F1ED81C4C06 +:10C3E000C3074FF4405104F190040AD08868C0F5A7 +:10C3F000EE20904212D31848206018486060184818 +:10C4000017E08968C1F5F423C1F5EE21C1F30C01F1 +:10C41000591AA1F54051B2EB510F01D90C2070BD52 +:10C420001049216010496160800701D50F4800E084 +:10C430000F48A0606878012802D00826304670BDF9 +:10C4400000F026F80600FAD12168E8688847F5E789 +:10C45000A4310020602C002059C1070009BD07004D +:10C460009DBB070081C1070031BD070075BB0700F8 +:10C4700011BB070068B5054E01253570052565F32C +:10C4800007000094FFF72CF968BD0000602C002025 +:10C490000C4810B5007808B1082010BD0A4C20687F +:10C4A000FEF7DEFD08B1FEF773FC00224FF47011B9 +:10C4B0002068FEF795FD041E01D0FEF769FC2046BA +:10C4C00010BD0000602C0020B4D8070010B5104C3F +:10C4D00001202070E088A0F57F41FF3905D01321AD +:10C4E00076DF68B1FEF754FC0AE0A07840B174DF53 +:10C4F00008B1FEF74DFC112000F00AFC0020A070EE +:10C50000FEF7B6FE08B1FEF743FC002010BD0000A8 +:10C510008C2C002030B54A4D87B000242C702C7133 +:10C52000112000F0EEFB122000F0EBFB112000F0D8 +:10C53000EFFB122000F0ECFB424800F067FF002800 +:10C5400075D14148FFF73CFE00F0AEF800286ED1EF +:10C550003E48FFF789FB3E4A3D49103A002868D023 +:10C56000D00608601060684671DF08B1FEF710FC65 +:10C570009DF801006946401C8DF80100002070DF25 +:10C5800008B1FEF705FC9DF8080032A120F00F006D +:10C59000401C20F0F00010308DF80800072202A89F +:10C5A0007CDF08B1FEF7F4FB00940C20ADF800002E +:10C5B00018200194ADF80200ADF804404FF4C870A3 +:10C5C000ADF8060068467ADF08B1FEF7E1FB082007 +:10C5D0000094ADF8000021480190214802901B48CA +:10C5E00069463038FEF7B6FE08B1FEF7D1FB1C21D4 +:10C5F0006846FDF7DCFF40F6CD40CDE900404FF442 +:10C600008040029003208DF80C001648ADF80E40D3 +:10C610008DF81040CDE905406846FEF7BBFD08B136 +:10C62000FEF7B6FB00F068FEFEF7E0FA002007B068 +:10C6300030BD4FF00050086010600120E870A2E7A4 +:10C640008C2C00203DB50700A5BC070094320020CB +:10C6500018050050446675546172670085CF070065 +:10C6600009D4070079BA07000E4910B54A788C78CA +:10C670000023A24212D0521CD2B24A70022A00D128 +:10C680004B70084A4B78183252F823200260086930 +:10C69000104420F07F400861012010BD036000209D +:10C6A00010BD0000342C002007480021074A01700B +:10C6B00002F5907002600822C0E90121C1600161A9 +:10C6C0004161816108467047AD2C0020D0320020C6 +:10C6D000F0B5194A0646916861B355691020691A88 +:10C6E00001F00701D2F800700B46012401EBC10CE8 +:10C6F00007EB8C0CB44506D1956904FA01F6754038 +:10C700000020956104E0491C01F007018D42EDD144 +:10C71000491C956901F0070104FA03F31D420AD18F +:10C720009368002B07D05B1E936053685B1C5360BB +:10C730000B46F1E70420F0BDF0330020064920232A +:10C740000A7C0968521E02F0070202EBC20203EBE8 +:10C750008202885000207047F033002070B5D0B1BD +:10C76000C9B10F4AD368C3B15B1ED36093685B1C29 +:10C7700093605369156803EBC30405EB84060660F8 +:10C78000202000EB84005B1C2858086003F00700A1 +:10C790005061002070BD0E2070BD042070BD0000EF +:10C7A000F033002010B5E9B100220A60104A536846 +:10C7B000E3B1202818D85B1E5360D068401CD060BD +:10C7C0001069136800EBC00403EB84030B600123C2 +:10C7D00083409169401C194300F00700916110618A +:10C7E000002010BD0E2010BD0C2010BD042010BD77 +:10C7F000F0330020FEB50446087A82070ED000F020 +:10C800000302C2F104030022154604E04E683554C9 +:10C81000401CC0B2521C9A42F8D30872012000900A +:10C8200048680290087A800801906846FFF7C6FBC6 +:10C8300000280BD0022100F04AFA024602212046CD +:10C84000FEF74CFE002801D0FEF7A2FAFEBD000064 +:10C85000F8B51A4C207800282ED0206928B100267F +:10C86000E06820B1002526B110E00126F8E7012597 +:10C87000F9E7684652DF052803D020B1FEF788FAB1 +:10C8800004E0012602E021690098884785B96088A4 +:10C89000ADF800006946A06861DF052803D020B12B +:10C8A000FEF776FA04E0012502E0D4E902018847A8 +:10C8B000002EDED0002DD6D0F8BD0000B42C002014 +:10C8C000054940880A88904204D34988884201D8A3 +:10C8D0000120704700207047042C0020800701D001 +:10C8E000002070470120704730B51488E51C9D4238 +:10C8F00001D90C2030BD02250D5513881C245B1C6A +:10C900009BB21380CC5413885B1C9BB2138001280C +:10C9100007D0022807D0032807D0042807D0072013 +:10C9200030BD002004E0012002E0CD5401E00320EE +:10C93000C8541088401C1080002030BD2DE9F047FD +:10C94000DFF8ACA0DAF80C009AF8004080469AF8BC +:10C950000460DAF80890DAF810703AE009EBC405E0 +:10C960002A7833E002EB42016B68521C03EBC101F1 +:10C97000AB78D2B2934200D100220B78022B02D0C6 +:10C98000032B23D11FE0D1F804C00346014603E086 +:10C99000614503D00B46C969C1B1F9E7B1B18B421A +:10C9A00005D1C06918B9134F0126BE600027D1F820 +:10C9B00000C0C969D96149B10B6863440B6005E0E7 +:10C9C0000174C06900E000210028F9D16978914222 +:10C9D000C8D1641EE4B2C1D2CAE903078AF8046070 +:10C9E000404502D00120BDE8F0870020FBE70000B1 +:10C9F000342C0020001001402DE9F04705462F4857 +:10CA0000002680460778D0F80C904BE0D8F8080054 +:10CA100000EBC70440E002D02846ED691EE0207814 +:10CA2000616800EB4002401CC0B201EBC201207003 +:10CA3000A278904200D126700A784868012A2DD148 +:10CA4000027C5ABB8A684260CA6882600A69C26016 +:10CA50004969816198F8041001B146604268D8F8CC +:10CA60001030DFF85CC0D11A21F07F41614502D25D +:10CA70008268114406E0991A826821F07F418A4257 +:10CA800002D9511A016000E006604660012186600B +:10CA90000174C66100F0A0FD002DBCD12078617842 +:10CAA0008842BCD17F1EFFB2B0D2D8F80C104945E5 +:10CAB00002D00120BDE8F0870020FBE7342C0020E5 +:10CAC000FFFF7F00334802680021B12A35D00024DF +:10CAD0004FF010204069B0F5F42F02D00020FEF78F +:10CAE00057F94FF080500069B0F5805F02D0002008 +:10CAF000FEF74EF9284B294A05210020FEF728FAB7 +:10CB000008B1FEF745F927492548C1F80007FEF7A7 +:10CB100059FEFEF78FFD98B1FEF79BFD08B1FEF7B9 +:10CB200037F984F00100FEF719FD00F091FBFEF7E4 +:10CB3000ADFD58B1FEF72CF908E001240160C7E70C +:10CB400084F00100FEF70AFD00F082FB1648006841 +:10CB5000C00F0BD00020204304D0FEF7ADFD08B17C +:10CB6000FEF716F9FEF766FD10B106E00120F2E7C8 +:10CB70004FF440508068FEF733FDBFF34F8F0B48F2 +:10CB800001680B4A01F4E06111430160BFF34F8F6C +:10CB900000BFFDE71C05004039AF0700CC2C00208A +:10CBA0000C0003007C000050100500500CED00E06C +:10CBB0000400FA0570B5144682880E4605462088A2 +:10CBC000111D0144921C994201D8FE2A01D90C2062 +:10CBD00070BD521C32542088FF21401C80B220803E +:10CBE00031542088401C80B220808119288800F0B0 +:10CBF00053FD2188084480B22080AA8842B1A968E8 +:10CC000041B13044FDF7BAFC2088A988084420804F +:10CC1000002070BD072070BDF8B50E46017814469F +:10CC20000546012901D1687830B12088821C9A42DA +:10CC30002DD8012902D006E00720F8BD697801440B +:10CC4000891C994223D8181A801E87B2ADF800704B +:10CC50002188B01C084469467DDF0028EDD1287882 +:10CC6000022805D1BDF80010B94201D809210AE017 +:10CC70000821012805D16878B84202D8ADF8000033 +:10CC800001E0ADF80070BDF80000FE2801D90C20CD +:10CC9000F8BD2288401CB0542088401C80B22080FF +:10CCA00031542088401C2080BDF8001008442080AA +:10CCB0000020F8BD31B540F2E73404E0401E00909A +:10CCC0002046FDF7B7FB00980028F7D138BD0828AB +:10CCD0000BD003DC38B106280FD108E00B280AD0AE +:10CCE0000C280AD105E0012070470220704703207C +:10CCF000704704207047042901D0062070470520A2 +:10CD000070470321800000F1A040C0F80017704771 +:10CD10000121814001480160704700000805005072 +:10CD200030B5574C01880022E588A7B019290AD0F0 +:10CD30001EDC11293DD008DC022972D0102902D155 +:10CD40008088E080A27027B030BD4E4B132903F1DC +:10CD5000180446D01429F6D11A46058A5B8D0021A5 +:10CD60009D4200D1214600238088D21D86DF1BE032 +:10CD7000512968D00EDC1A297FD01B29E3D1007A13 +:10CD80000028E0D1A270E068401EE0604AD0FDF7C4 +:10CD90002DFFD8E7522970D05529D4D180790028A9 +:10CDA000D1D11321284676DF0028CCD0FDF7F0FF43 +:10CDB000C9E78020ADF894003220E0601220FFF730 +:10CDC000A7FF012325AA01A9E088AADF08B1FDF782 +:10CDD000DFFF207808B9FDF709FF4FF6FF70E0800C +:10CDE000B1E728481022483850F8481F059181883B +:10CDF000ADF8181080798DF81A00D91D01A8FDF73B +:10CE0000BDFB1C22214610A8FDF7B8FB01A80C9021 +:10CE10001B4A10A81C320B9007AB852128467FDFE8 +:10CE2000C2E70CE01422184901A8FDF7A7FB0598FA +:10CE300001AC009094E80F00FEF752FC83E700215C +:10CE4000284667DFB0E781790029A2D0807B0428DB +:10CE500003D0062801D005289BD102290BD0012040 +:10CE60008DF884004FF4C170ADF8880021A92846E0 +:10CE7000A8DF99E701E00220F2E700F04FFA93E71C +:10CE80008C2C002094320020E4D80700F0B58BB041 +:10CE900001274FF0020E05468DF80CE08DF8127058 +:10CEA0000889ADF814004889ADF8160001F10C00AE +:10CEB000002608960690ADF820E007A8ADF822609D +:10CEC00009900C46A98A288808AAA5DF18B1E96A42 +:10CED000C9B1884717E09DF81C0010F0010F12D06F +:10CEE000ADF81060288803A9A8DF002812D1217BA3 +:10CEF00004F10D00092943D2DFE801F0420E1D23A1 +:10CF000026282A402C0040F2FD10ADF81000288899 +:10CF100003A9A8DF0BB0F0BD8DF800600DE006227C +:10CF20001DE000BF8DF808700190AA6A6946284686 +:10CF300090470020EEE78DF800706289022AF1D256 +:10CF4000EDE78DF800E0F0E7032012E0042010E0A8 +:10CF500005200EE06089032805D203220821284617 +:10CF6000FEF7BCFAD6E7B4F80D00ADF8040018B12E +:10CF700006208DF80000D8E70720FAE70920F8E737 +:10CF80000322ECE770B504460B780120314A0A2BE6 +:10CF900016D2DFE803F01F243A050D1651563D5A0C +:10CFA000FEF790FF0421FFF792FE0246042123E0E2 +:10CFB000FFF78CFA08B1FDF7EBFEFEF769FF28B921 +:10CFC00070BDFFF783FA08B1FDF7E2FEBDE87040DF +:10CFD000FFF7FCB8107148680078507170BD0220EE +:10CFE0001071486800780128F8D1FEF7CFFF0221C0 +:10CFF000FFF76DFE024602212046FEF76FFA002879 +:10D00000ECD0BDE87040FDF7C3BE0320107170BDC9 +:10D0100012792046012A07D0022A09D0032AF6D124 +:10D02000BDE87040FDF73CBEBDE8704000F04CBA72 +:10D03000BDE87040FFF7DEBB5070888810815081DA +:10D0400070BD00205070108170BD20461169FEF740 +:10D0500045F9D4E78C2C002006480378012B03D136 +:10D06000012901D102210170100001D0FDF790BE0D +:10D0700070470000542C002070B5124C1E46154617 +:10D080006078012902D0032914D107E0052811D1C5 +:10D0900063697BB13246294604200AE0022809D19F +:10D0A00009484068804703206070636913B13246C5 +:10D0B00029469847280003D0BDE87040FDF768BEB8 +:10D0C00070BD0000602C00203432002008B50B46F3 +:10D0D0000022014603200092FEF708FC08BD000074 +:10D0E00070B50D4E00240D487460254604704470E0 +:10D0F00084702846FEF7EAFB6D1C0A2DF9D3074918 +:10D1000034600020F431746041F82040401C032852 +:10D11000FAD3002070BD00001C2C002068300020D5 +:10D12000074B1A68032A08D00A600649006841F8CC +:10D130002200521C00201A60704704207047000033 +:10D140001C2C00205C310020F8B507461D46164611 +:10D150000C460846FFF7C2FB58B16019FFF7BEFB4B +:10D1600038B133462246394601200095FEF7BEFB12 +:10D17000F8BD1020F8BD000070B5244C0646002212 +:10D18000A078012841D100232146A3700D78022EFA +:10D1900005EB450001EBC00103D0032E35D10D2076 +:10D1A0002EE01B484668761C46608968B1EB063F56 +:10D1B00015D843606078401E6070681CC0B2207053 +:10D1C0000A2801D30A38207005EB450004EBC001A2 +:10D1D0001046091DFDF7E8FD2846FEF777FB60784D +:10D1E000002812D0A07800280FD1FEF73BFB0028C2 +:10D1F0000BD0112809D0217801EB410104EBC101CA +:10D20000BDE87040091DFDF7CFBD70BD683000203E +:10D210001C2C002070B5012670044FF0E021002581 +:10D22000C1F88001084C4010C4F84803C4F8080352 +:10D2300066602F20FDF73AF9A660044C2F20256187 +:10D24000FDF734F9E57070BD00100140342C00206A +:10D2500010B5064A14210820FDF73AFE002803D035 +:10D26000BDE81040FDF794BD10BD0000DC2E00208D +:10D2700010B50121202803DA1D4A8140114004E045 +:10D28000402803DAA0F12002914041B1002809DAD8 +:10D2900000F00F0101F1E02191F8141D06E042F2C7 +:10D2A000010010BD00F1E02191F80014490908299E +:10D2B00014D299B1012911D004290FD005290DD01C +:10D2C0000C4A946800F01F0301219940400944B1C1 +:10D2D00052F820300B4342F8203007E042F20200BF +:10D2E00010BD800000F1E020C0F80011002010BD4A +:10D2F000FC06FFBC0C3400200748017821F07E01B9 +:10D3000041F00101303121F08001017007214170AD +:10D310001021817070470000A82C002070B5164CB9 +:10D320004FF6FF75E088A84201D1082070BDE17872 +:10D33000E1B1012308221149A9DF0028F6D1022317 +:10D3400000221146E088A9DF0028EFD12A460C21EF +:10D35000E088A7DFA0F54051023905D0082803D0A6 +:10D36000A0F540510439E1D1002070BD00231A46D8 +:10D370001946A9DF70BD00008C2C0020C8320020A7 +:10D380002DE9F0410746806A14468846C8B3002557 +:10D3900031E000BFB96A05EB450001EB8006B088BB +:10D3A000801CFE2802D90C20BDE8F0812288401C98 +:10D3B00008F8020020881622401C80B2208008F85D +:10D3C00000202088401C80B2208000EB08013088BB +:10D3D00000F062F92188084480B22080B28842B10E +:10D3E000B16871B14044FDF7C9F82088B18808449C +:10D3F00020806D1CEDB297F82C00A842CAD80020FE +:10D40000D2E7FFE70720CFE7FDF7C2BC18B1034919 +:10D41000C860002070470E2070470000B42C002028 +:10D420001CB5034901910091694660DF1CBD0000F5 +:10D43000002C002070B50E460546144614211046F7 +:10D44000FDF7B5F8012020800021216121732672AB +:10D45000657205B1A072002070BD10B511B1002039 +:10D46000FDF796FCBDE81040FFF7F2B90021024A33 +:10D470000846FDF7CDBC00005BD40700FDF776BC85 +:10D4800030B409B18C0702D0072030BC7047074C7C +:10D490000125A16062800649636010DF0028F4D195 +:10D4A000257030BC1620FFF7E3BE0000B42C00202E +:10D4B0007DD4070018B103490861002070470E2091 +:10D4C00070470000B42C002010B588B0044600203E +:10D4D000009001900290039019481A4A40798DF803 +:10D4E00000001068926804906846CDE90502087A49 +:10D4F0000C2805D003221CE0FDF74AFC08B010BD43 +:10D500004868C278016862F31F610191C27941687D +:10D5100062F31F610291C17A806861F31F6003901A +:10D5200004A8FEF73DFF0028E8D00121FFF7CFFB5C +:10D53000024601212046FDF7D1FF0028DCD1DDE7BE +:10D540008C2C0020D8D80700FFF716BE2DE9F84F25 +:10D55000204C00256846D4E903B6FFF785F88046DD +:10D56000FFF7ECF90746A946B8F1000F25D00023D4 +:10D57000009ACC46E06819E007680146974202D954 +:10D58000BA1A026014E0C1F800C0D1F80C80D21BB6 +:10D59000C8693B448A46B8F1000F07D0F71827F056 +:10D5A0007F47C1E90178CAF81C500D460028E3D135 +:10D5B000E0602846FFF720FA04E02846FFF71CFA4F +:10D5C00000B917B15846FEF7BDF984F80490BDE8DC +:10D5D000F88F0000342C00200D4B10B5D96831B104 +:10D5E00002680C68A24204D8A21A0A60C161D8601D +:10D5F00010BD0B4603E00B46C969121B21B10C6834 +:10D600009442F8D3A41A0C600260C161D86110BDC5 +:10D61000342C00202DE9F0411D4DEC68002C35D054 +:10D620001C48002700682969B846401A20F07F4648 +:10D630002068B04215D82246361A0744E469107CA7 +:10D6400068B182F810806B6933B1D2E9050198475F +:10D6500028B1FDF79DFB02E0D2E905108847002CB8 +:10D66000E6D16978A878814206D1401CC0B2A87082 +:10D67000022801D185F802800548A978183040F8C1 +:10D680002170BDE8F0411420FDF700B8BDE8F0813D +:10D69000342C0020041501400870000A4870022054 +:10D6A00070470870020A4A70020C8A70000EC87037 +:10D6B0000420704738B515494888A0F57F42FF3AE5 +:10D6C00023D04A78124C521CD2B24A70237B934228 +:10D6D0000AD3091D75DF002817D0A169002914D0CD +:10D6E00001B0BDE83040084700254D70217C29B1CC +:10D6F0003B2176DF10B1A16901B18847616900293A +:10D7000003D08DF800506846884738BD002C0020B3 +:10D710004C300020EFF30580C005C00D13D0103849 +:10D7200040B2002806DA00F00F0000F1E02090F887 +:10D73000140D03E000F1E02090F8000440090228F5 +:10D7400003D0062803D00220704700207047012034 +:10D75000704742788378521CD2B2934200D10022A3 +:10D760000378934201D1002070470A7041684078E5 +:10D7700000EB400001EBC00070472DE9FC4116466C +:10D78000DDE90854CDE900541F468046022200F02E +:10D790000CF8002807D1CDE900543B461022314651 +:10D7A000404600F002F8BDE8FC812DE9FC5F002650 +:10D7B000DDE90C4B994692468E46804627883546D1 +:10D7C00030E0D8F80400002200EB850069460068CC +:10D7D000019001A865DF002830D19DF80000504578 +:10D7E0001FD10EB1002100E0022122880844111847 +:10D7F000594522D846B9521C90B2208009F800E061 +:10D800002088401C20800126218801A801EB090204 +:10D81000694665DF002811D120889DF80010084472 +:10D8200020806D1CB8F80000A842CADC56B12088E0 +:10D83000C01B401E80B2FF2802D90C20BDE8FC9F0F +:10D8400009F807000020F9E710B5074C48DF08B1D8 +:10D85000FDF79EFAFDF72CFB2078022803D0032861 +:10D8600001D00428F2D110BD542C00200100000783 +:10D8700002000000000000000000000000000000A6 +:10D880002C30002023D1BCEA5F782315DEEF121282 +:10D890000000000059D00700000000000000000058 +:10D8A0000200000000000000000000000000000076 +:10D8B000000000008431002004000000000000008F +:10D8C00000000000000000000000000079D0070008 +:10D8D0000000000000000000030000000000000045 +:10D8E0000000000005000000000000000000000033 +:10D8F000000000000000000018D90700002C0020E4 +:10D90000CC0000007CA90700E4D90700CC2C002043 +:10D910004C2700008CA90700000000000000000058 +:10D9200000000000000000000000000000000000F7 +:10D9300000000000000000000000000000000000E7 +:10D9400000000000000000000000000000000000D7 +:10D9500000000000000000000000000000000000C7 +:10D9600000000000000000000000000000000000B7 +:10D9700000000000000000000000000000000000A7 +:10D980000000000000000000000000000000000097 +:10D990000000000000000000000000000000000087 +:10D9A00000000000000000000000FFFF0000000079 +:10D9B0003200000000000000000000000000000035 +:10D9C0000000000000000000000000000000000057 +:10D9D0000000000000000000000000000000000047 +:04D9E0000090D003E0 +:020000041000EA +:0410140000A0070031 +:020000041000EA +:0410180000E00700ED +:040000050007A4014B +:00000001FF From 8fdaccfc16ef34a23de34f272c9084249730bc45 Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Tue, 13 Jun 2017 16:06:09 +0200 Subject: [PATCH 11/13] Cleaned code up and removed unused BLEBond function --- cores/arduino/DFUService.cpp | 25 ++++++++++++------------- libraries/BLE/BLEBondStore.cpp | 27 ++------------------------- libraries/BLE/BLEBondStore.h | 7 ------- libraries/BLE/BLECentralRole.cpp | 5 ----- libraries/BLE/BLECentralRole.h | 1 - libraries/BLE/BLEPeripheral.cpp | 5 ----- libraries/BLE/BLEPeripheral.h | 2 -- 7 files changed, 14 insertions(+), 58 deletions(-) diff --git a/cores/arduino/DFUService.cpp b/cores/arduino/DFUService.cpp index 150f634..f47c420 100644 --- a/cores/arduino/DFUService.cpp +++ b/cores/arduino/DFUService.cpp @@ -60,19 +60,18 @@ static void on_ble_evt(ble_evt_t * p_ble_evt) case BLE_GAP_EVT_DISCONNECTED: m_conn_handle = BLE_CONN_HANDLE_INVALID; -//-----start advertising again here ? ------------ - ble_gap_adv_params_t advertisingParameters; - memset(&advertisingParameters, 0x00, sizeof(advertisingParameters)); - - advertisingParameters.type = BLE_GAP_ADV_TYPE_ADV_IND; - advertisingParameters.p_peer_addr = NULL; - advertisingParameters.fp = BLE_GAP_ADV_FP_ANY; - advertisingParameters.p_whitelist = NULL; - advertisingParameters.interval = (100 * 16) / 10; // advertising interval (in units of 0.625 ms) - advertisingParameters.timeout = 0; - - sd_ble_gap_adv_start(&advertisingParameters); -//------let it manage to BLE library ? ------------ + // start advertising again + ble_gap_adv_params_t advertisingParameters; + memset(&advertisingParameters, 0x00, sizeof(advertisingParameters)); + + advertisingParameters.type = BLE_GAP_ADV_TYPE_ADV_IND; + advertisingParameters.p_peer_addr = NULL; + advertisingParameters.fp = BLE_GAP_ADV_FP_ANY; + advertisingParameters.p_whitelist = NULL; + advertisingParameters.interval = (100 * 16) / 10; // advertising interval (in units of 0.625 ms) + advertisingParameters.timeout = 0; + + sd_ble_gap_adv_start(&advertisingParameters); break; default: diff --git a/libraries/BLE/BLEBondStore.cpp b/libraries/BLE/BLEBondStore.cpp index 2e93692..ad64681 100644 --- a/libraries/BLE/BLEBondStore.cpp +++ b/libraries/BLE/BLEBondStore.cpp @@ -29,15 +29,10 @@ extern "C"{ #endif //__cplusplus -BLEBondStore::BLEBondStore(int offset) : +BLEBondStore::BLEBondStore(int offset) #if defined(__AVR__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) - _offset(offset), -// #elif defined(NRF51) || defined(NRF52) || defined(__RFduino__) - // : _flashPageStartAddress((uint32_t *)(NRF_FICR->CODEPAGESIZE * (NRF_FICR->CODESIZE - 1 - (uint32_t)offset))), + : _offset(offset), #endif - _tempData(NULL), - _tempOffset(0), - _tempLength(0) { //by default offset is the bootloader address //if it's null use the end of the flash @@ -148,22 +143,4 @@ void BLEBondStore::getData(unsigned char* data, unsigned int offset, unsigned in in++; } #endif -} - -void BLEBondStore::saveTempData(const unsigned char* data, unsigned int offset, unsigned int length){ - this->_tempData = data; - this->_tempOffset = offset; - this->_tempLength = length; -} - -const unsigned char* BLEBondStore::getTempData(){ - return this->_tempData; -} - -unsigned int BLEBondStore::getTempOffset(){ - return this->_tempOffset; -} - -unsigned int BLEBondStore::getTempLength(){ - return this->_tempLength; } \ No newline at end of file diff --git a/libraries/BLE/BLEBondStore.h b/libraries/BLE/BLEBondStore.h index 73fcbe4..f126c00 100644 --- a/libraries/BLE/BLEBondStore.h +++ b/libraries/BLE/BLEBondStore.h @@ -14,10 +14,6 @@ class BLEBondStore void clearData(); void putData(const unsigned char* data, unsigned int offset, unsigned int length); void getData(unsigned char* data, unsigned int offset, unsigned int length); - void saveTempData(const unsigned char* data, unsigned int offset, unsigned int length); - const unsigned char* getTempData(); - unsigned int getTempOffset(); - unsigned int getTempLength(); private: #if defined(__AVR__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) @@ -25,9 +21,6 @@ class BLEBondStore #elif defined(NRF51)|| defined(NRF52) || defined(__RFduino__) uint32_t* _flashPageStartAddress; #endif - const unsigned char* _tempData; - unsigned int _tempOffset; - unsigned int _tempLength; }; #endif diff --git a/libraries/BLE/BLECentralRole.cpp b/libraries/BLE/BLECentralRole.cpp index bb07bb0..eb86e17 100644 --- a/libraries/BLE/BLECentralRole.cpp +++ b/libraries/BLE/BLECentralRole.cpp @@ -247,11 +247,6 @@ void BLECentralRole::clearBondStoreData() { this->_bondStore.clearData(); } -void BLECentralRole::saveBondData(){ - if(this->_bondStore.getTempData() != NULL) - this->_bondStore.putData(this->_bondStore.getTempData(), this->_bondStore.getTempOffset(), this->_bondStore.getTempLength()); -} - char * BLECentralRole::getPasskey(){ if(this->_passkey[0] != 0) return (char *)this->_passkey; diff --git a/libraries/BLE/BLECentralRole.h b/libraries/BLE/BLECentralRole.h index a509355..f170671 100644 --- a/libraries/BLE/BLECentralRole.h +++ b/libraries/BLE/BLECentralRole.h @@ -84,7 +84,6 @@ class BLECentralRole : public BLECharacteristicValueChangeListener, void setBondStore(BLEBondStore& bondStore); void enableBond(BLEBondingType type = JUST_WORKS); void clearBondStoreData(); - void saveBondData(); char *getPasskey(); void sendPasskey(char passkey[]); void confirmPasskey(bool confirm); diff --git a/libraries/BLE/BLEPeripheral.cpp b/libraries/BLE/BLEPeripheral.cpp index ae981cc..b01d2fe 100644 --- a/libraries/BLE/BLEPeripheral.cpp +++ b/libraries/BLE/BLEPeripheral.cpp @@ -190,11 +190,6 @@ void BLEPeripheral::clearBondStoreData() { eraseBond(); } -void BLEPeripheral::saveBondData(){ - if(this->_bleBondStore.getTempData() != NULL) - this->_bleBondStore.putData(this->_bleBondStore.getTempData(), this->_bleBondStore.getTempOffset(), this->_bleBondStore.getTempLength()); -} - char * BLEPeripheral::getPasskey(){ if(this->_device->_passkey[0] != 0) return (char *)this->_device->_passkey; diff --git a/libraries/BLE/BLEPeripheral.h b/libraries/BLE/BLEPeripheral.h index c32fcca..151d086 100644 --- a/libraries/BLE/BLEPeripheral.h +++ b/libraries/BLE/BLEPeripheral.h @@ -7,7 +7,6 @@ #include "Arduino.h" -#include "BLEBondStore.h" #include "BLECentral.h" #include "BLEConstantCharacteristic.h" #include "BLEDescriptor.h" @@ -95,7 +94,6 @@ class BLEPeripheral : public BLEDeviceEventListener, void setBondStore(BLEBondStore& bondStore); void enableBond(BLEBondingType type = JUST_WORKS); void clearBondStoreData(); - void saveBondData(); char *getPasskey(); void sendPasskey(char passkey[]); void confirmPasskey(bool confirm); From 4f7a6670b8e15f03f423aba3c772a562c0d07482 Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Wed, 14 Jun 2017 17:06:25 +0200 Subject: [PATCH 12/13] Forwarded BLEBond event and BLEMessage event from device manager to BLE library. Modified examples to show how to delete bond information. --- cores/arduino/DFUService.cpp | 15 ++++++++++++- cores/arduino/DFUService.h | 7 ++++++ .../device_manager_peripheral.c | 7 +++++- libraries/BLE/BLECentralRole.cpp | 22 ++++++------------- libraries/BLE/BLEManager.cpp | 4 ++++ libraries/BLE/BLEPeripheral.cpp | 7 ++++++ libraries/BLE/BLEPeripheral.h | 2 ++ .../enterPasskeyCentral.ino | 16 ++++++++++++-- .../showPasskeyCentral/showPasskeyCentral.ino | 16 ++++++++++++-- .../Bonding/enterPasskey/enterPasskey.ino | 14 ++++++++++++ .../Bonding/showPasskey/showPasskey.ino | 16 +++++++++++++- 11 files changed, 104 insertions(+), 22 deletions(-) diff --git a/cores/arduino/DFUService.cpp b/cores/arduino/DFUService.cpp index f47c420..55c3519 100644 --- a/cores/arduino/DFUService.cpp +++ b/cores/arduino/DFUService.cpp @@ -44,6 +44,7 @@ uint8_t io_caps = BLE_GAP_IO_CAPS_NONE; extern void processBleEvents(ble_evt_t * p_ble_evt) __attribute__((weak)); extern bool isPeripheralRunning() __attribute__((weak)); extern bool isCentralRunning() __attribute__((weak)); +extern void callEvtListener(uint32_t type, uint32_t code) __attribute__((weak)); void removeDfuService(bool remove){ if(remove == true) @@ -274,4 +275,16 @@ bool dfuIsEnabled(){ void eraseBond(){ dm_device_delete_all(&m_app_handle); -} \ No newline at end of file +} +#include "Arduino.h" +#ifdef __cplusplus +extern "C"{ +#endif +void forwardEvent(uint32_t type, uint32_t code){ + // if callEvtListener is defined forward events to the BLE library + if(callEvtListener) + callEvtListener(type, code); +} +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/cores/arduino/DFUService.h b/cores/arduino/DFUService.h index 2a84130..45dc477 100644 --- a/cores/arduino/DFUService.h +++ b/cores/arduino/DFUService.h @@ -58,5 +58,12 @@ extern void initDM(); */ extern void ble_evt_dispatch(ble_evt_t * p_ble_evt); +#ifdef __cplusplus +extern "C"{ +#endif +extern void forwardEvent(uint32_t type, uint32_t code); +#ifdef __cplusplus +} +#endif #endif /*_DFU_SERVICE_H*/ \ No newline at end of file diff --git a/cores/arduino/components/ble/device_manager/device_manager_peripheral.c b/cores/arduino/components/ble/device_manager/device_manager_peripheral.c index 96e2eca..2219761 100644 --- a/cores/arduino/components/ble/device_manager/device_manager_peripheral.c +++ b/cores/arduino/components/ble/device_manager/device_manager_peripheral.c @@ -2723,7 +2723,7 @@ void dm_ble_evt_handler(ble_evt_t * p_ble_evt) if (err_code == NRF_SUCCESS) { printf("[DM]:[CI 0x%02X]:[DI 0x%02X]: Bonded!\r\n",index, device_index); - + handle.device_id = device_index; m_connection_table[index].bonded_dev_id = device_index; } @@ -2808,6 +2808,8 @@ void dm_ble_evt_handler(ble_evt_t * p_ble_evt) (void) result; event_result = p_ble_evt->evt.gap_evt.params.auth_status.auth_status; } + // forward event to BLEPeripheral library + forwardEvent(2, p_ble_evt->evt.gap_evt.params.auth_status.auth_status); } else { @@ -2881,6 +2883,9 @@ void dm_ble_evt_handler(ble_evt_t * p_ble_evt) } else { + // forward event to BLEPeripheral library + forwardEvent(1, 0); + m_connection_table[index].state |= STATE_LINK_ENCRYPTED; event.event_id = DM_EVT_LINK_SECURED; diff --git a/libraries/BLE/BLECentralRole.cpp b/libraries/BLE/BLECentralRole.cpp index eb86e17..6f8daaf 100644 --- a/libraries/BLE/BLECentralRole.cpp +++ b/libraries/BLE/BLECentralRole.cpp @@ -982,6 +982,13 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ Serial.print(bleEvt->evt.gap_evt.params.conn_sec_update.conn_sec.encr_key_size); Serial.println(); #endif + if(bleEvt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.sm == 1){ + // we are bonded + eventHandler = _eventHandlers[BLEBonded]; + if (eventHandler) { + eventHandler(_node[currentPeripheral]); + } + } break; case BLE_GATTS_EVT_WRITE: { @@ -1063,11 +1070,6 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ for(currentPeripheral = 0; currentPeripheral < _allowedPeripherals; currentPeripheral++) if(this->_connectionHandle[currentPeripheral] == bleEvt->evt.gap_evt.conn_handle) break; - - eventHandler = _eventHandlers[BLEBonded]; - if (eventHandler) { - eventHandler(_node[currentPeripheral]); - } } else{ @@ -1088,16 +1090,6 @@ void BLECentralRole::poll(ble_evt_t *bleEvt){ } } } - // if(this->_lesc == 2 && this->_userConfirm){ - // for(int i = 0; i < _allowedPeripherals; i++){ - // if(this->_connectionHandle[i] == bleEvt->evt.gap_evt.conn_handle){ - // sd_ble_gap_auth_key_reply(this->_connectionHandle[i], BLE_GAP_AUTH_KEY_TYPE_PASSKEY, NULL); - // /* Due to DRGN-7235, dhkey_reply() must come after auth_key_reply() */ - // sd_ble_gap_lesc_dhkey_reply(this->_connectionHandle[i], &_dhkey); - // break; - // } - // } - // } break; case BLE_GAP_EVT_AUTH_KEY_REQUEST: diff --git a/libraries/BLE/BLEManager.cpp b/libraries/BLE/BLEManager.cpp index aad7006..22c6e03 100644 --- a/libraries/BLE/BLEManager.cpp +++ b/libraries/BLE/BLEManager.cpp @@ -68,4 +68,8 @@ bool isCentralRunning(){ return (BLEManagerClass::_centralList[0] != 0) ? true : false; } +void callEvtListener(uint32_t type, uint32_t code){ + BLEManagerClass::_peripheralList[0]->callEvtListener(type, code); +} + BLEManagerClass BLEManager; \ No newline at end of file diff --git a/libraries/BLE/BLEPeripheral.cpp b/libraries/BLE/BLEPeripheral.cpp index b01d2fe..6ab9da4 100644 --- a/libraries/BLE/BLEPeripheral.cpp +++ b/libraries/BLE/BLEPeripheral.cpp @@ -517,4 +517,11 @@ void BLEPeripheral::addFieldInPck(uint8_t type, uint8_t len, unsigned char* data *pckLen += len + 2; } +} + +void BLEPeripheral::callEvtListener(uint32_t type, uint32_t code){ + if(type == 1) // BLEBonded event + this->BLEDeviceBonded(*this->_device); + else if(type == 2) // BLEMessage event + this->BLEMessageReceived(*this->_device, AUTH_STATUS, code); } \ No newline at end of file diff --git a/libraries/BLE/BLEPeripheral.h b/libraries/BLE/BLEPeripheral.h index 151d086..21b2b01 100644 --- a/libraries/BLE/BLEPeripheral.h +++ b/libraries/BLE/BLEPeripheral.h @@ -117,6 +117,8 @@ class BLEPeripheral : public BLEDeviceEventListener, void setEventHandler(BLEPeripheralEvent event, BLEPeripheralEventHandler eventHandler); void setEventHandler(BLEPeripheralEvent event, BLEMessageEventHandler eventHandler); + void callEvtListener(uint32_t type, uint32_t code); + protected: bool characteristicValueChanged(BLECharacteristic& characteristic); bool broadcastCharacteristic(BLECharacteristic& characteristic); diff --git a/libraries/BLE/examples/Central/Bonding/enterPasskeyCentral/enterPasskeyCentral.ino b/libraries/BLE/examples/Central/Bonding/enterPasskeyCentral/enterPasskeyCentral.ino index 74dff45..7a8b19a 100644 --- a/libraries/BLE/examples/Central/Bonding/enterPasskeyCentral/enterPasskeyCentral.ino +++ b/libraries/BLE/examples/Central/Bonding/enterPasskeyCentral/enterPasskeyCentral.ino @@ -4,7 +4,11 @@ This example shows how to enable bonding features on BLECentral module. To know all the possible bonding types please refer to the documentation. - + + To delete bond information an interrupt has been attached to USER1_BUTTON (in Primo board). + If you're using a Primo Core connect a button to the board and change USER1_BUTTON with the + number of pin the button is connected to. + Use the complementary example showPasskey.ino in File->Examples->BLE->Peripheral->Bonding to test this feature. @@ -25,11 +29,13 @@ BLERemoteService dummyRemoteService = BLERemote // create remote characteristics BLERemoteCharacteristic dummyRemoteCharacteristic = BLERemoteCharacteristic("19b10011e8f2537e4f6cd104768a1214", BLERead | BLEWrite); - +int BUTTON = USER1_BUTTON; void setup() { Serial.begin(9600); + attachInterrupt(BUTTON, deleteBondInformation, LOW); + //initialize BLE led pinMode(BLE_LED, OUTPUT); @@ -125,6 +131,12 @@ void bond(BLEPeripheralPeer& peer) { Serial.println("Bonded"); } +void deleteBondInformation(){ + // button has been pressed. Delete bond + bleCentral.clearBondStoreData(); + Serial.println("Bond data cleared"); +} + void receiveMessage(int evtCode, int messageCode){ bleCentral.printBleMessage(evtCode, messageCode); } \ No newline at end of file diff --git a/libraries/BLE/examples/Central/Bonding/showPasskeyCentral/showPasskeyCentral.ino b/libraries/BLE/examples/Central/Bonding/showPasskeyCentral/showPasskeyCentral.ino index 5de20ab..0f6c229 100644 --- a/libraries/BLE/examples/Central/Bonding/showPasskeyCentral/showPasskeyCentral.ino +++ b/libraries/BLE/examples/Central/Bonding/showPasskeyCentral/showPasskeyCentral.ino @@ -4,7 +4,11 @@ This example shows how to enable bonding features on BLECentral module. To know all the possible bonding types please refer to the documentation. - + + To delete bond information an interrupt has been attached to USER1_BUTTON (in Primo board). + If you're using a Primo Core connect a button to the board and change USER1_BUTTON with the + number of pin the button is connected to. + Use the complementary example enterPasskeyBond.ino in File->Examples->BLE->Peripheral->Bonding to test this feature. @@ -25,11 +29,13 @@ BLERemoteService dummyRemoteService = BLERemote // create remote characteristics BLERemoteCharacteristic dummyRemoteCharacteristic = BLERemoteCharacteristic("19b10011e8f2537e4f6cd104768a1214", BLERead | BLEWrite); - +int BUTTON = USER1_BUTTON; void setup() { Serial.begin(9600); + attachInterrupt(BUTTON, deleteBondInformation, LOW); + //initialize BLE led pinMode(BLE_LED, OUTPUT); @@ -113,6 +119,12 @@ void bond(BLEPeripheralPeer& peer) { Serial.println("Bonded"); } +void deleteBondInformation(){ + // button has been pressed. Delete bond + bleCentral.clearBondStoreData(); + Serial.println("Bond data cleared"); +} + void receiveMessage(int evtCode, int messageCode){ bleCentral.printBleMessage(evtCode, messageCode); } \ No newline at end of file diff --git a/libraries/BLE/examples/Peripheral/Bonding/enterPasskey/enterPasskey.ino b/libraries/BLE/examples/Peripheral/Bonding/enterPasskey/enterPasskey.ino index 1608aa3..5ae974c 100644 --- a/libraries/BLE/examples/Peripheral/Bonding/enterPasskey/enterPasskey.ino +++ b/libraries/BLE/examples/Peripheral/Bonding/enterPasskey/enterPasskey.ino @@ -5,6 +5,10 @@ This example shows how to enable bonding features on BLEPeripheral module. To know all the possible bonding types please refer to the documentation. + To delete bond information an interrupt has been attached to USER1_BUTTON (in Primo board). + If you're using a Primo Core connect a button to the board and change USER1_BUTTON with the + number of pin the button is connected to. + Use the complementary example showPasskeyCentral.ino in File->Examples->BLE->Central->Bonding to test this feature. @@ -25,8 +29,12 @@ BLEService dummyService = BLEService("19b10000e8f2537e4f6 // create characteristic BLECharCharacteristic dummyCharacteristic = BLECharCharacteristic("19b10001e8f2537e4f6cd104768a1214", BLERead | BLEWrite); +int BUTTON = USER1_BUTTON; + void setup() { Serial.begin(9600); + + attachInterrupt(BUTTON, deleteBondInformation, LOW); // set advertised local name and service UUID blePeripheral.setLocalName("BONDExample"); @@ -83,6 +91,12 @@ void bond(BLECentral& central) { Serial.println("Bonded"); } +void deleteBondInformation(){ + // button has been pressed. Delete bond + blePeripheral.clearBondStoreData(); + Serial.println("Bond data cleared"); +} + void blePeripheralConnectHandler(BLECentral& central) { // central connected event handler Serial.print(F("Connected event, central: ")); diff --git a/libraries/BLE/examples/Peripheral/Bonding/showPasskey/showPasskey.ino b/libraries/BLE/examples/Peripheral/Bonding/showPasskey/showPasskey.ino index 1ca2bc2..24032a1 100644 --- a/libraries/BLE/examples/Peripheral/Bonding/showPasskey/showPasskey.ino +++ b/libraries/BLE/examples/Peripheral/Bonding/showPasskey/showPasskey.ino @@ -4,7 +4,11 @@ This example shows how to enable bonding features on BLEPeripheral module. To know all the possible bonding types please refer to the documentation. - + + To delete bond information an interrupt has been attached to USER1_BUTTON (in Primo board). + If you're using a Primo Core connect a button to the board and change USER1_BUTTON with the + number of pin the button is connected to. + Use the complementary example enterPasskeyCentral.ino in File->Examples->BLE->Central->Bonding to test this feature. @@ -25,9 +29,13 @@ BLEService dummyService = BLEService("19b10000e8f2537e4f6 // create characteristic BLECharCharacteristic dummyCharacteristic = BLECharCharacteristic("19b10001e8f2537e4f6cd104768a1214", BLERead | BLEWrite); +int BUTTON = USER1_BUTTON; + void setup() { Serial.begin(9600); + attachInterrupt(BUTTON, deleteBondInformation, LOW); + //initialize BLE led pinMode(BLE_LED, OUTPUT); @@ -83,6 +91,12 @@ void bond(BLECentral& central) { Serial.println("Bonded"); } +void deleteBondInformation(){ + // button has been pressed. Delete bond + blePeripheral.clearBondStoreData(); + Serial.println("Bond data cleared"); +} + void blePeripheralConnectHandler(BLECentral& central) { // central connected event handler Serial.print(F("Connected event, central: ")); From 47262f36af4623a46546f2ceef7ee4d1aac58183 Mon Sep 17 00:00:00 2001 From: chiararuggeri Date: Wed, 14 Jun 2017 17:21:41 +0200 Subject: [PATCH 13/13] Adjusted upload.maximum_size to take into account space taken by bootloader --- boards.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards.txt b/boards.txt index 04f4ce0..275ff6e 100644 --- a/boards.txt +++ b/boards.txt @@ -16,7 +16,7 @@ primo.pid.0=0x805a primo.upload.tool=openocd primo.upload.protocol=sam-ba -primo.upload.maximum_size=409600 +primo.upload.maximum_size=385024 primo.upload.use_1200bps_touch=false primo.upload.speed=115200 primo.upload.wait_for_upload_port=false