add files from nrf52832 bootloader project
This commit is contained in:
@ -0,0 +1,32 @@
|
||||
/* 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 memory_pool_internal Memory Pool Internal
|
||||
* @{
|
||||
* @ingroup memory_pool
|
||||
*
|
||||
* @brief Memory pool internal definitions
|
||||
*/
|
||||
|
||||
#ifndef MEM_POOL_INTERNAL_H__
|
||||
#define MEM_POOL_INTERNAL_H__
|
||||
|
||||
#define TX_BUF_SIZE 4u /**< TX buffer size in bytes. */
|
||||
#define RX_BUF_SIZE 32u /**< RX buffer size in bytes. */
|
||||
|
||||
#define RX_BUF_QUEUE_SIZE 8u /**< RX buffer element size. */
|
||||
|
||||
#endif // MEM_POOL_INTERNAL_H__
|
||||
|
||||
/** @} */
|
@ -0,0 +1,457 @@
|
||||
/* 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.h"
|
||||
#include "bootloader_types.h"
|
||||
#include "bootloader_util.h"
|
||||
#include "bootloader_settings.h"
|
||||
#include "dfu.h"
|
||||
#include "dfu_transport.h"
|
||||
#include "nrf.h"
|
||||
#include "app_error.h"
|
||||
#include "nrf_sdm.h"
|
||||
#include "nrf_mbr.h"
|
||||
#include "nordic_common.h"
|
||||
#include "crc16.h"
|
||||
#include "pstorage.h"
|
||||
#include "app_scheduler.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include "app_timer_appsh.h"
|
||||
|
||||
#define APP_TIMER_PRESCALER 0
|
||||
|
||||
#define IRQ_ENABLED 0x01 /**< Field identifying if an interrupt is enabled. */
|
||||
|
||||
#ifdef NRF52
|
||||
#define MAX_NUMBER_INTERRUPTS 39
|
||||
#else
|
||||
#define MAX_NUMBER_INTERRUPTS 32 /**< Maximum number of interrupts available. */
|
||||
#endif
|
||||
|
||||
/**@brief Enumeration for specifying current bootloader status.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
BOOTLOADER_UPDATING, /**< Bootloader status for indicating that an update is in progress. */
|
||||
BOOTLOADER_SETTINGS_SAVING, /**< Bootloader status for indicating that saving of bootloader settings is in progress. */
|
||||
BOOTLOADER_COMPLETE, /**< Bootloader status for indicating that all operations for the update procedure has completed and it is safe to reset the system. */
|
||||
BOOTLOADER_TIMEOUT, /**< Bootloader status field for indicating that a timeout has occured and current update process should be aborted. */
|
||||
BOOTLOADER_RESET, /**< Bootloader status field for indicating that a reset has been requested and current update process should be aborted. */
|
||||
} bootloader_status_t;
|
||||
|
||||
static pstorage_handle_t m_bootsettings_handle; /**< Pstorage handle to use for registration and identifying the bootloader module on subsequent calls to the pstorage module for load and store of bootloader setting in flash. */
|
||||
static bootloader_status_t m_update_status; /**< Current update status for the bootloader module to ensure correct behaviour when updating settings and when update completes. */
|
||||
|
||||
// Adafruit modification for dual transports and forced startup DFU
|
||||
extern bool is_ota(void);
|
||||
|
||||
APP_TIMER_DEF( _forced_startup_dfu_timer );
|
||||
volatile bool forced_startup_dfu_packet_received = false;
|
||||
volatile static bool _terminate_startup_dfu = false;
|
||||
|
||||
/**@brief Function for handling callbacks from pstorage module.
|
||||
*
|
||||
* @details Handles pstorage results for clear and storage operation. For detailed description of
|
||||
* the parameters provided with the callback, please refer to \ref pstorage_ntf_cb_t.
|
||||
*/
|
||||
static void pstorage_callback_handler(pstorage_handle_t * p_handle,
|
||||
uint8_t op_code,
|
||||
uint32_t result,
|
||||
uint8_t * p_data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
// If we are in BOOTLOADER_SETTINGS_SAVING state and we receive an PSTORAGE_STORE_OP_CODE
|
||||
// response then settings has been saved and update has completed.
|
||||
if ((m_update_status == BOOTLOADER_SETTINGS_SAVING) && (op_code == PSTORAGE_STORE_OP_CODE))
|
||||
{
|
||||
m_update_status = BOOTLOADER_COMPLETE;
|
||||
}
|
||||
|
||||
APP_ERROR_CHECK(result);
|
||||
}
|
||||
|
||||
// Adafruit modifcation
|
||||
static void terminate_startup_dfu(void * p_event_data, uint16_t event_size)
|
||||
{
|
||||
(void) p_event_data;
|
||||
(void) event_size;
|
||||
|
||||
_terminate_startup_dfu = true;
|
||||
}
|
||||
|
||||
/* Terminate the forced DFU mode on startup if no packets is received
|
||||
* by put an terminal handler to scheduler
|
||||
*/
|
||||
static void forced_startup_dfu_timer_handler(void * p_context)
|
||||
{
|
||||
// No packets are received within timeout, terminal and DFU mode
|
||||
// forced_startup_dfu_packet_received is set by process_dfu_packet() in dfu_transport_serial.c
|
||||
if (!forced_startup_dfu_packet_received)
|
||||
{
|
||||
app_sched_event_put(NULL, 0, terminate_startup_dfu);
|
||||
}
|
||||
}
|
||||
|
||||
/**@brief Function for waiting for events.
|
||||
*
|
||||
* @details This function will place the chip in low power mode while waiting for events from
|
||||
* the SoftDevice or other peripherals. When interrupted by an event, it will call the
|
||||
* @ref app_sched_execute function to process the received event. This function will return
|
||||
* when the final state of the firmware update is reached OR when a tear down is in
|
||||
* progress.
|
||||
*/
|
||||
static void wait_for_events(void)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
// Wait in low power state for any events.
|
||||
uint32_t err_code = sd_app_evt_wait();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
// Event received. Process it from the scheduler.
|
||||
app_sched_execute();
|
||||
|
||||
if ((m_update_status == BOOTLOADER_COMPLETE) ||
|
||||
(m_update_status == BOOTLOADER_TIMEOUT) ||
|
||||
(m_update_status == BOOTLOADER_RESET))
|
||||
{
|
||||
// When update has completed or a timeout/reset occured we will return.
|
||||
return;
|
||||
}
|
||||
|
||||
// Forced startup dfu mode timeout without any received packet
|
||||
if (_terminate_startup_dfu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool bootloader_app_is_valid(uint32_t app_addr)
|
||||
{
|
||||
const bootloader_settings_t * p_bootloader_settings;
|
||||
|
||||
// There exists an application in CODE region 1.
|
||||
if (*((uint32_t *)app_addr) == EMPTY_FLASH_MASK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
bootloader_util_settings_get(&p_bootloader_settings);
|
||||
|
||||
// The application in CODE region 1 is flagged as valid during update.
|
||||
if (p_bootloader_settings->bank_0 == BANK_VALID_APP)
|
||||
{
|
||||
uint16_t image_crc = 0;
|
||||
|
||||
// A stored crc value of 0 indicates that CRC checking is not used.
|
||||
if (p_bootloader_settings->bank_0_crc != 0)
|
||||
{
|
||||
image_crc = crc16_compute((uint8_t *)DFU_BANK_0_REGION_START,
|
||||
p_bootloader_settings->bank_0_size,
|
||||
NULL);
|
||||
}
|
||||
|
||||
success = (image_crc == p_bootloader_settings->bank_0_crc);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
static void bootloader_settings_save(bootloader_settings_t * p_settings)
|
||||
{
|
||||
uint32_t err_code = pstorage_clear(&m_bootsettings_handle, sizeof(bootloader_settings_t));
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = pstorage_store(&m_bootsettings_handle,
|
||||
(uint8_t *)p_settings,
|
||||
sizeof(bootloader_settings_t),
|
||||
0);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void bootloader_dfu_update_process(dfu_update_status_t update_status)
|
||||
{
|
||||
static bootloader_settings_t settings;
|
||||
const bootloader_settings_t * p_bootloader_settings;
|
||||
|
||||
bootloader_util_settings_get(&p_bootloader_settings);
|
||||
|
||||
if (update_status.status_code == DFU_UPDATE_APP_COMPLETE)
|
||||
{
|
||||
settings.bank_0_crc = update_status.app_crc;
|
||||
settings.bank_0_size = update_status.app_size;
|
||||
settings.bank_0 = BANK_VALID_APP;
|
||||
settings.bank_1 = BANK_INVALID_APP;
|
||||
|
||||
m_update_status = BOOTLOADER_SETTINGS_SAVING;
|
||||
bootloader_settings_save(&settings);
|
||||
}
|
||||
else if (update_status.status_code == DFU_UPDATE_SD_COMPLETE)
|
||||
{
|
||||
settings.bank_0_crc = update_status.app_crc;
|
||||
settings.bank_0_size = update_status.sd_size +
|
||||
update_status.bl_size +
|
||||
update_status.app_size;
|
||||
settings.bank_0 = BANK_VALID_SD;
|
||||
settings.bank_1 = BANK_INVALID_APP;
|
||||
settings.sd_image_size = update_status.sd_size;
|
||||
settings.bl_image_size = update_status.bl_size;
|
||||
settings.app_image_size = update_status.app_size;
|
||||
settings.sd_image_start = update_status.sd_image_start;
|
||||
|
||||
m_update_status = BOOTLOADER_SETTINGS_SAVING;
|
||||
bootloader_settings_save(&settings);
|
||||
}
|
||||
else if (update_status.status_code == DFU_UPDATE_BOOT_COMPLETE)
|
||||
{
|
||||
settings.bank_0 = p_bootloader_settings->bank_0;
|
||||
settings.bank_0_crc = p_bootloader_settings->bank_0_crc;
|
||||
settings.bank_0_size = p_bootloader_settings->bank_0_size;
|
||||
settings.bank_1 = BANK_VALID_BOOT;
|
||||
settings.sd_image_size = update_status.sd_size;
|
||||
settings.bl_image_size = update_status.bl_size;
|
||||
settings.app_image_size = update_status.app_size;
|
||||
|
||||
m_update_status = BOOTLOADER_SETTINGS_SAVING;
|
||||
bootloader_settings_save(&settings);
|
||||
}
|
||||
else if (update_status.status_code == DFU_UPDATE_SD_SWAPPED)
|
||||
{
|
||||
if (p_bootloader_settings->bank_0 == BANK_VALID_SD)
|
||||
{
|
||||
settings.bank_0_crc = 0;
|
||||
settings.bank_0_size = 0;
|
||||
settings.bank_0 = BANK_INVALID_APP;
|
||||
}
|
||||
// This handles cases where SoftDevice was not updated, hence bank0 keeps its settings.
|
||||
else
|
||||
{
|
||||
settings.bank_0 = p_bootloader_settings->bank_0;
|
||||
settings.bank_0_crc = p_bootloader_settings->bank_0_crc;
|
||||
settings.bank_0_size = p_bootloader_settings->bank_0_size;
|
||||
}
|
||||
|
||||
settings.bank_1 = BANK_INVALID_APP;
|
||||
settings.sd_image_size = 0;
|
||||
settings.bl_image_size = 0;
|
||||
settings.app_image_size = 0;
|
||||
|
||||
m_update_status = BOOTLOADER_SETTINGS_SAVING;
|
||||
bootloader_settings_save(&settings);
|
||||
}
|
||||
else if (update_status.status_code == DFU_TIMEOUT)
|
||||
{
|
||||
// Timeout has occurred. Close the connection with the DFU Controller.
|
||||
uint32_t err_code;
|
||||
if ( is_ota() )
|
||||
{
|
||||
err_code = dfu_transport_ble_close();
|
||||
}else
|
||||
{
|
||||
err_code = dfu_transport_serial_close();
|
||||
}
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
m_update_status = BOOTLOADER_TIMEOUT;
|
||||
}
|
||||
else if (update_status.status_code == DFU_BANK_0_ERASED)
|
||||
{
|
||||
settings.bank_0_crc = 0;
|
||||
settings.bank_0_size = 0;
|
||||
settings.bank_0 = BANK_INVALID_APP;
|
||||
settings.bank_1 = p_bootloader_settings->bank_1;
|
||||
|
||||
bootloader_settings_save(&settings);
|
||||
}
|
||||
else if (update_status.status_code == DFU_RESET)
|
||||
{
|
||||
m_update_status = BOOTLOADER_RESET;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No implementation needed.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t bootloader_init(void)
|
||||
{
|
||||
uint32_t err_code;
|
||||
pstorage_module_param_t storage_params = {.cb = pstorage_callback_handler};
|
||||
|
||||
err_code = pstorage_init();
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
m_bootsettings_handle.block_id = BOOTLOADER_SETTINGS_ADDRESS;
|
||||
err_code = pstorage_register(&storage_params, &m_bootsettings_handle);
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms)
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
// Clear swap if banked update is used.
|
||||
err_code = dfu_init();
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
if ( ota )
|
||||
{
|
||||
err_code = dfu_transport_ble_update_start();
|
||||
}else
|
||||
{
|
||||
// timeout_ms > 0 is forced startup DFU
|
||||
if ( timeout_ms )
|
||||
{
|
||||
forced_startup_dfu_packet_received = false;
|
||||
_terminate_startup_dfu = false;
|
||||
|
||||
(void) app_timer_create(&_forced_startup_dfu_timer, APP_TIMER_MODE_SINGLE_SHOT, forced_startup_dfu_timer_handler);
|
||||
app_timer_start(_forced_startup_dfu_timer, APP_TIMER_TICKS(timeout_ms, APP_TIMER_PRESCALER), NULL);
|
||||
}
|
||||
|
||||
err_code = dfu_transport_serial_update_start();
|
||||
}
|
||||
|
||||
wait_for_events();
|
||||
|
||||
// Close Serial transport after done
|
||||
if ( !ota ) dfu_transport_serial_close();
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**@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 = 0; // We start from first interrupt, i.e. interrupt 0.
|
||||
|
||||
// Fetch the current interrupt settings.
|
||||
interrupt_setting_mask = NVIC->ISER[0];
|
||||
|
||||
for (; irq < MAX_NUMBER_INTERRUPTS; irq++)
|
||||
{
|
||||
if (interrupt_setting_mask & (IRQ_ENABLED << irq))
|
||||
{
|
||||
// The interrupt was enabled, and hence disable it.
|
||||
NVIC_DisableIRQ((IRQn_Type)irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bootloader_app_start(uint32_t app_addr)
|
||||
{
|
||||
// If the applications CRC has been checked and passed, the magic number will be written and we
|
||||
// can start the application safely.
|
||||
uint32_t err_code = sd_softdevice_disable();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
interrupts_disable();
|
||||
|
||||
// Disable RTC1
|
||||
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
|
||||
NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk;
|
||||
NRF_RTC1->TASKS_STOP = 1;
|
||||
NRF_RTC1->TASKS_CLEAR = 1;
|
||||
|
||||
err_code = sd_softdevice_vector_table_base_set(CODE_REGION_1_START);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
bootloader_util_app_start(CODE_REGION_1_START);
|
||||
}
|
||||
|
||||
|
||||
bool bootloader_dfu_sd_in_progress(void)
|
||||
{
|
||||
const bootloader_settings_t * p_bootloader_settings;
|
||||
|
||||
bootloader_util_settings_get(&p_bootloader_settings);
|
||||
|
||||
if (p_bootloader_settings->bank_0 == BANK_VALID_SD ||
|
||||
p_bootloader_settings->bank_1 == BANK_VALID_BOOT)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
uint32_t bootloader_dfu_sd_update_continue(void)
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
if ((dfu_sd_image_validate() == NRF_SUCCESS) &&
|
||||
(dfu_bl_image_validate() == NRF_SUCCESS))
|
||||
{
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
// Ensure that flash operations are not executed within the first 100 ms seconds to allow
|
||||
// a debugger to be attached.
|
||||
nrf_delay_ms(100);
|
||||
|
||||
err_code = dfu_sd_image_swap();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = dfu_sd_image_validate();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = dfu_bl_image_swap();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t bootloader_dfu_sd_update_finalize(void)
|
||||
{
|
||||
dfu_update_status_t update_status = {DFU_UPDATE_SD_SWAPPED, };
|
||||
|
||||
bootloader_dfu_update_process(update_status);
|
||||
|
||||
wait_for_events();
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void bootloader_settings_get(bootloader_settings_t * const p_settings)
|
||||
{
|
||||
const bootloader_settings_t * p_bootloader_settings;
|
||||
|
||||
bootloader_util_settings_get(&p_bootloader_settings);
|
||||
|
||||
p_settings->bank_0 = p_bootloader_settings->bank_0;
|
||||
p_settings->bank_0_crc = p_bootloader_settings->bank_0_crc;
|
||||
p_settings->bank_0_size = p_bootloader_settings->bank_0_size;
|
||||
p_settings->bank_1 = p_bootloader_settings->bank_1;
|
||||
p_settings->sd_image_size = p_bootloader_settings->sd_image_size;
|
||||
p_settings->bl_image_size = p_bootloader_settings->bl_image_size;
|
||||
p_settings->app_image_size = p_bootloader_settings->app_image_size;
|
||||
p_settings->sd_image_start = p_bootloader_settings->sd_image_start;
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
/* 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 Bootloader API.
|
||||
* @{
|
||||
*
|
||||
* @brief Bootloader module interface.
|
||||
*/
|
||||
|
||||
#ifndef BOOTLOADER_H__
|
||||
#define BOOTLOADER_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bootloader_types.h"
|
||||
#include <dfu_types.h>
|
||||
|
||||
/**@brief Function for initializing the Bootloader.
|
||||
*
|
||||
* @retval NRF_SUCCESS If bootloader was succesfully initialized.
|
||||
*/
|
||||
uint32_t bootloader_init(void);
|
||||
|
||||
/**@brief Function for validating application region in flash.
|
||||
*
|
||||
* @param[in] app_addr Address to the region in flash where the application is stored.
|
||||
*
|
||||
* @retval true If Application region is valid.
|
||||
* @retval false If Application region is not valid.
|
||||
*/
|
||||
bool bootloader_app_is_valid(uint32_t app_addr);
|
||||
|
||||
/**@brief Function for starting the Device Firmware Update.
|
||||
*
|
||||
* @retval NRF_SUCCESS If new application image was successfully transferred.
|
||||
*/
|
||||
uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms);
|
||||
|
||||
/**@brief Function for exiting bootloader and booting into application.
|
||||
*
|
||||
* @details This function will disable SoftDevice and all interrupts before jumping to application.
|
||||
* The SoftDevice vector table base for interrupt forwarding will be set the application
|
||||
* address.
|
||||
*
|
||||
* @param[in] app_addr Address to the region where the application is stored.
|
||||
*/
|
||||
void bootloader_app_start(uint32_t app_addr);
|
||||
|
||||
/**@brief Function for retrieving the bootloader settings.
|
||||
*
|
||||
* @param[out] p_settings A copy of the current bootloader settings is returned in the structure
|
||||
* provided.
|
||||
*/
|
||||
void bootloader_settings_get(bootloader_settings_t * const p_settings);
|
||||
|
||||
/**@brief Function for processing DFU status update.
|
||||
*
|
||||
* @param[in] update_status DFU update status.
|
||||
*/
|
||||
void bootloader_dfu_update_process(dfu_update_status_t update_status);
|
||||
|
||||
/**@brief Function getting state of SoftDevice update in progress.
|
||||
* After a successfull SoftDevice transfer the system restarts in orderto disable SoftDevice
|
||||
* and complete the update.
|
||||
*
|
||||
* @retval true A SoftDevice update is in progress. This indicates that second stage
|
||||
* of a SoftDevice update procedure can be initiated.
|
||||
* @retval false No SoftDevice update is in progress.
|
||||
*/
|
||||
bool bootloader_dfu_sd_in_progress(void);
|
||||
|
||||
/**@brief Function for continuing the Device Firmware Update of a SoftDevice.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the final stage of SoftDevice update was successful.
|
||||
*/
|
||||
uint32_t bootloader_dfu_sd_update_continue(void);
|
||||
|
||||
/**@brief Function for finalizing the Device Firmware Update of a SoftDevice.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the final stage of SoftDevice update was successful.
|
||||
*/
|
||||
uint32_t bootloader_dfu_sd_update_finalize(void);
|
||||
|
||||
#endif // BOOTLOADER_H__
|
||||
|
||||
/**@} */
|
@ -0,0 +1,63 @@
|
||||
/* 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_settings.h"
|
||||
#include <stdint.h>
|
||||
#include <dfu_types.h>
|
||||
|
||||
#if defined ( __CC_ARM )
|
||||
|
||||
uint8_t m_boot_settings[CODE_PAGE_SIZE] __attribute__((at(BOOTLOADER_SETTINGS_ADDRESS))) __attribute__((used)); /**< This variable reserves a codepage for bootloader specific settings, to ensure the compiler doesn't locate any code or variables at his location. */
|
||||
uint32_t m_uicr_bootloader_start_address __attribute__((at(NRF_UICR_BOOT_START_ADDRESS))) = BOOTLOADER_REGION_START; /**< This variable makes the linker script write the bootloader start address to the UICR register. This value will be written in the HEX file and thus written to UICR when the bootloader is flashed into the chip. */
|
||||
|
||||
#elif defined ( __GNUC__ )
|
||||
|
||||
uint8_t m_boot_settings[CODE_PAGE_SIZE] __attribute__ ((section(".bootloaderSettings"))); /**< This variable reserves a codepage for bootloader specific settings, to ensure the compiler doesn't locate any code or variables at his location. */
|
||||
volatile uint32_t m_uicr_bootloader_start_address __attribute__ ((section(".uicrBootStartAddress"))) = BOOTLOADER_REGION_START; /**< This variable ensures that the linker script will write the bootloader start address to the UICR register. This value will be written in the HEX file and thus written to UICR when the bootloader is flashed into the chip. */
|
||||
|
||||
#elif defined ( __ICCARM__ )
|
||||
|
||||
__no_init uint8_t m_boot_settings[CODE_PAGE_SIZE] @ BOOTLOADER_SETTINGS_ADDRESS; /**< This variable reserves a codepage for bootloader specific settings, to ensure the compiler doesn't locate any code or variables at his location. */
|
||||
__root const uint32_t m_uicr_bootloader_start_address @ NRF_UICR_BOOT_START_ADDRESS = BOOTLOADER_REGION_START; /**< This variable ensures that the linker script will write the bootloader start address to the UICR register. This value will be written in the HEX file and thus written to UICR when the bootloader is flashed into the chip. */
|
||||
|
||||
#endif
|
||||
|
||||
#if defined ( NRF52 )
|
||||
#if defined ( __CC_ARM )
|
||||
|
||||
uint8_t m_mbr_params_page[CODE_PAGE_SIZE] __attribute__((at(BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS))) __attribute__((used)); /**< This variable reserves a codepage for mbr parameters, to ensure the compiler doesn't locate any code or variables at his location. */
|
||||
uint32_t m_uicr_mbr_params_page_address __attribute__((at(NRF_UICR_MBR_PARAMS_PAGE_ADDRESS)))
|
||||
= BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS; /**< This variable makes the linker script write the mbr parameters page address to the UICR register. This value will be written in the HEX file and thus written to the UICR when the bootloader is flashed into the chip */
|
||||
|
||||
#elif defined (__GNUC__ )
|
||||
|
||||
uint8_t m_mbr_params_page[CODE_PAGE_SIZE] __attribute__ ((section(".mbrParamsPage"))); /**< This variable reserves a codepage for mbr parameters, to ensure the compiler doesn't locate any code or variables at his location. */
|
||||
volatile uint32_t m_uicr_mbr_params_page_address __attribute__ ((section(".uicrMbrParamsPageAddress")))
|
||||
= BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS; /**< This variable makes the linker script write the mbr parameters page address to the UICR register. This value will be written in the HEX file and thus written to the UICR when the bootloader is flashed into the chip */
|
||||
|
||||
#elif defined (__ICCARM__ )
|
||||
|
||||
__no_init uint8_t m_mbr_params_page[CODE_PAGE_SIZE] @ BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS; /**< This variable reserves a codepage for bootloader specific settings, to ensure the compiler doesn't locate any code or variables at his location. */
|
||||
__root const uint32_t m_uicr_mbr_params_page_address @ NRF_UICR_MBR_PARAMS_PAGE_ADDRESS = BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS; /**< This variable ensures that the linker script will write the bootloader start address to the UICR register. This value will be written in the HEX file and thus written to UICR when the bootloader is flashed into the chip. */
|
||||
|
||||
#endif
|
||||
#endif //defined ( NRF52 )
|
||||
|
||||
|
||||
void bootloader_util_settings_get(const bootloader_settings_t ** pp_bootloader_settings)
|
||||
{
|
||||
// Read only pointer to bootloader settings in flash.
|
||||
bootloader_settings_t const * const p_bootloader_settings =
|
||||
(bootloader_settings_t *)&m_boot_settings[0];
|
||||
|
||||
*pp_bootloader_settings = p_bootloader_settings;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/* 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_bootloader_settings Bootloader settings API.
|
||||
* @{
|
||||
*
|
||||
* @brief Bootloader settings module interface.
|
||||
*/
|
||||
|
||||
#ifndef BOOTLOADER_SETTINGS_H__
|
||||
#define BOOTLOADER_SETTINGS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "bootloader_types.h"
|
||||
|
||||
/**@brief Function for getting the bootloader settings.
|
||||
*
|
||||
* @param[out] pp_bootloader_settings Bootloader settings.
|
||||
*/
|
||||
void bootloader_util_settings_get(const bootloader_settings_t ** pp_bootloader_settings);
|
||||
|
||||
#endif // BOOTLOADER_SETTINGS_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 <stdint.h>
|
||||
|
||||
#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__
|
||||
|
||||
/**@} */
|
@ -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 <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
@ -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 <stdint.h>
|
||||
#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__
|
||||
|
||||
/**@} */
|
@ -0,0 +1,134 @@
|
||||
/* 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_dfu Device Firmware Update API.
|
||||
* @{
|
||||
*
|
||||
* @brief Device Firmware Update module interface.
|
||||
*/
|
||||
|
||||
#ifndef DFU_H__
|
||||
#define DFU_H__
|
||||
|
||||
#include <dfu_types.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/**@brief DFU event callback for asynchronous calls.
|
||||
*
|
||||
* @param[in] packet Packet type for which this callback is related. START_PACKET, DATA_PACKET.
|
||||
* @param[in] result Operation result code. NRF_SUCCESS when a queued operation was successful.
|
||||
* @param[in] p_data Pointer to the data to which the operation is related.
|
||||
*/
|
||||
typedef void (*dfu_callback_t)(uint32_t packet, uint32_t result, uint8_t * p_data);
|
||||
|
||||
/**@brief Function for initializing the Device Firmware Update module.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, an error_code otherwise.
|
||||
*/
|
||||
uint32_t dfu_init(void);
|
||||
|
||||
/**@brief Function for registering a callback listener for \ref dfu_data_pkt_handle callbacks.
|
||||
*
|
||||
* @param[in] callback_handler Callback handler for receiving DFU events on completed operations
|
||||
* of DFU packets.
|
||||
*/
|
||||
void dfu_register_callback(dfu_callback_t callback_handler);
|
||||
|
||||
/**@brief Function for setting the DFU image size.
|
||||
*
|
||||
* @details Function sets the DFU image size. This function must be called when an update is started
|
||||
* in order to notify the DFU of the new image size. If multiple images are to be
|
||||
* transferred within the same update context then this function must be called with size
|
||||
* information for each image being transfered.
|
||||
* If an image type is not being transfered, e.g. SoftDevice but no Application , then the
|
||||
* image size for application must be zero.
|
||||
*
|
||||
* @param[in] p_packet Pointer to the DFU packet containing information on DFU update process to
|
||||
* be started.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, an error_code otherwise.
|
||||
*/
|
||||
uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet);
|
||||
|
||||
/**@brief Function for handling DFU data packets.
|
||||
*
|
||||
* @param[in] p_packet Pointer to the DFU packet.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, an error_code otherwise.
|
||||
*/
|
||||
uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet);
|
||||
|
||||
/**@brief Function for handling DFU init packets.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, an error_code otherwise.
|
||||
*/
|
||||
uint32_t dfu_init_pkt_handle(dfu_update_packet_t * p_packet);
|
||||
|
||||
/**@brief Function for validating a transferred image after the transfer has completed.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, an error_code otherwise.
|
||||
*/
|
||||
uint32_t dfu_image_validate(void);
|
||||
|
||||
/**@brief Function for activating the transfered image after validation has successfully completed.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, an error_code otherwise.
|
||||
*/
|
||||
uint32_t dfu_image_activate(void);
|
||||
|
||||
/**@brief Function for reseting the current update procedure and return to initial state.
|
||||
*
|
||||
* @details This function call will result in a system reset to ensure correct system behavior.
|
||||
* The reset will might be scheduled to execute at a later point in time to ensure pending
|
||||
* flash operations has completed.
|
||||
*/
|
||||
void dfu_reset(void);
|
||||
|
||||
/**@brief Function for validating that new bootloader has been correctly installed.
|
||||
*
|
||||
* @return NRF_SUCCESS if install was successful. NRF_ERROR_NULL if the images differs.
|
||||
*/
|
||||
uint32_t dfu_bl_image_validate(void);
|
||||
|
||||
/**@brief Function for validating that new SoftDevicehas been correctly installed.
|
||||
*
|
||||
* @return NRF_SUCCESS if install was successful. NRF_ERROR_NULL if the images differs.
|
||||
*/
|
||||
uint32_t dfu_sd_image_validate(void);
|
||||
|
||||
/**@brief Function for swapping existing bootloader with newly received.
|
||||
*
|
||||
* @return NRF_SUCCESS on succesfull swapping. For error code please refer to
|
||||
* \ref sd_mbr_command_copy_bl_t.
|
||||
*/
|
||||
uint32_t dfu_bl_image_swap(void);
|
||||
|
||||
/**@brief Function for swapping existing SoftDevice with newly received.
|
||||
*
|
||||
* @return NRF_SUCCESS on succesfull swapping. For error code please refer to
|
||||
* \ref sd_mbr_command_copy_sd_t.
|
||||
*/
|
||||
uint32_t dfu_sd_image_swap(void);
|
||||
|
||||
/**@brief Function for handling DFU init packet complete.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, an error_code otherwise.
|
||||
*/
|
||||
uint32_t dfu_init_pkt_complete(void);
|
||||
|
||||
#endif // DFU_H__
|
||||
|
||||
/** @} */
|
@ -0,0 +1,192 @@
|
||||
/* 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 <string.h>
|
||||
#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 32 /**< 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;
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
/** [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);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
}
|
@ -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__
|
||||
|
||||
/** @} */
|
@ -0,0 +1,87 @@
|
||||
/* 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 dfu_bank_internal Device Firmware Update internal header for bank handling in DFU.
|
||||
* @{
|
||||
*
|
||||
* @brief Device Firmware Update Bank handling module interface.
|
||||
*
|
||||
* @details This header is intended for shared definition and functions between single and dual bank
|
||||
* implementations used for DFU support. It is not supposed to be used for external access
|
||||
* to the DFU module.
|
||||
*
|
||||
*/
|
||||
#ifndef DFU_BANK_INTERNAL_H__
|
||||
#define DFU_BANK_INTERNAL_H__
|
||||
|
||||
#include <dfu_types.h>
|
||||
|
||||
/**@brief States of the DFU state machine. */
|
||||
typedef enum
|
||||
{
|
||||
DFU_STATE_INIT_ERROR, /**< State for: dfu_init(...) error. */
|
||||
DFU_STATE_IDLE, /**< State for: idle. */
|
||||
DFU_STATE_PREPARING, /**< State for: preparing, indicates that the flash is being erased and no data packets can be processed. */
|
||||
DFU_STATE_RDY, /**< State for: ready. */
|
||||
DFU_STATE_RX_INIT_PKT, /**< State for: receiving initialization packet. */
|
||||
DFU_STATE_RX_DATA_PKT, /**< State for: receiving data packet. */
|
||||
DFU_STATE_VALIDATE, /**< State for: validate. */
|
||||
DFU_STATE_WAIT_4_ACTIVATE /**< State for: waiting for dfu_image_activate(). */
|
||||
} dfu_state_t;
|
||||
|
||||
#define APP_TIMER_PRESCALER 0 /**< Value of the RTC1 PRESCALER register. */
|
||||
#define DFU_TIMEOUT_INTERVAL APP_TIMER_TICKS(300000, APP_TIMER_PRESCALER) /**< DFU timeout interval in units of timer ticks. */
|
||||
|
||||
#define IS_UPDATING_SD(START_PKT) ((START_PKT).dfu_update_mode & DFU_UPDATE_SD) /**< Macro for determining if a SoftDevice update is ongoing. */
|
||||
#define IS_UPDATING_BL(START_PKT) ((START_PKT).dfu_update_mode & DFU_UPDATE_BL) /**< Macro for determining if a Bootloader update is ongoing. */
|
||||
#define IS_UPDATING_APP(START_PKT) ((START_PKT).dfu_update_mode & DFU_UPDATE_APP) /**< Macro for determining if a Application update is ongoing. */
|
||||
#define IMAGE_WRITE_IN_PROGRESS() (m_data_received > 0) /**< Macro for determining if an image write is in progress. */
|
||||
#define IS_WORD_SIZED(SIZE) ((SIZE & (sizeof(uint32_t) - 1)) == 0) /**< Macro for checking that the provided is word sized. */
|
||||
|
||||
/**@cond NO_DOXYGEN */
|
||||
static uint32_t m_data_received; /**< Amount of received data. */
|
||||
/**@endcond */
|
||||
|
||||
/**@brief Type definition of function used for preparing of the bank before receiving of a
|
||||
* software image.
|
||||
*
|
||||
* @param[in] image_size Size of software image being received.
|
||||
*/
|
||||
typedef void (*dfu_bank_prepare_t)(uint32_t image_size);
|
||||
|
||||
/**@brief Type definition of function used for handling clear complete of the bank before
|
||||
* receiving of a software image.
|
||||
*/
|
||||
typedef void (*dfu_bank_cleared_t)(void);
|
||||
|
||||
/**@brief Type definition of function used for activating of the software image received.
|
||||
*
|
||||
* @return NRF_SUCCESS If the image has been successfully activated any other NRF_ERROR code in
|
||||
* case of a failure.
|
||||
*/
|
||||
typedef uint32_t (*dfu_bank_activate_t)(void);
|
||||
|
||||
/**@brief Structure for holding of function pointers for needed prepare and activate procedure for
|
||||
* the requested update procedure.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
dfu_bank_prepare_t prepare; /**< Function pointer to the prepare function called on start of update procedure. */
|
||||
dfu_bank_cleared_t cleared; /**< Function pointer to the cleared function called after prepare function completes. */
|
||||
dfu_bank_activate_t activate; /**< Function pointer to the activate function called on finalizing the update procedure. */
|
||||
} dfu_bank_func_t;
|
||||
|
||||
#endif // DFU_BANK_INTERNAL_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 <stdint.h>
|
||||
#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__
|
||||
|
||||
/** @} */
|
@ -0,0 +1,43 @@
|
||||
/* 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_internal DFU BLE SVC internal
|
||||
* @{
|
||||
*
|
||||
* @brief DFU BLE SVC internal functions in bootloader. The DFU BLE SuperVisor Calls allow an
|
||||
* application to execute functions in the installed bootloader. This interface provides
|
||||
* internal Bootloader DFU functions for retrieving data exchanged through SuperVisor Calls.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DFU_BLE_SVC_INTERNAL_H__
|
||||
#define DFU_BLE_SVC_INTERNAL_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "dfu_ble_svc.h"
|
||||
#include "ble_gap.h"
|
||||
|
||||
/**@brief Internal bootloader/DFU function for retrieving peer data provided from application.
|
||||
*
|
||||
* @param[out] p_peer_data Peer data set by application to be used for DFU connection.
|
||||
*
|
||||
* @retval NRF_SUCCESS If peer data is valid and can be used for connection.
|
||||
* @retval NRF_ERROR_NULL If p_peer_data is a NULL pointer.
|
||||
* @retval NRF_ERROR_INVALID_DATA If peer data is not available or invalid.
|
||||
*/
|
||||
uint32_t dfu_ble_peer_data_get(dfu_ble_peer_data_t * p_peer_data);
|
||||
|
||||
#endif // DFU_BLE_SVC_INTERNAL_H__
|
||||
|
||||
/** @} */
|
@ -0,0 +1,840 @@
|
||||
/* 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 <stddef.h>
|
||||
#include "dfu.h"
|
||||
#include <dfu_types.h>
|
||||
#include "dfu_bank_internal.h"
|
||||
#include "nrf.h"
|
||||
#include "nrf_sdm.h"
|
||||
#include "app_error.h"
|
||||
#include "app_timer.h"
|
||||
#include "bootloader.h"
|
||||
#include "bootloader_types.h"
|
||||
#include "pstorage.h"
|
||||
#include "nrf_mbr.h"
|
||||
#include "dfu_init.h"
|
||||
#include "sdk_common.h"
|
||||
|
||||
static dfu_state_t m_dfu_state; /**< Current DFU state. */
|
||||
static uint32_t m_image_size; /**< Size of the image that will be transmitted. */
|
||||
|
||||
static dfu_start_packet_t m_start_packet; /**< Start packet received for this update procedure. Contains update mode and image sizes information to be used for image transfer. */
|
||||
static uint8_t m_init_packet[128]; /**< Init packet, can hold CRC, Hash, Signed Hash and similar, for image validation, integrety check and authorization checking. */
|
||||
static uint8_t m_init_packet_length; /**< Length of init packet received. */
|
||||
static uint16_t m_image_crc; /**< Calculated CRC of the image received. */
|
||||
|
||||
APP_TIMER_DEF(m_dfu_timer_id); /**< Application timer id. */
|
||||
static bool m_dfu_timed_out = false; /**< Boolean flag value for tracking DFU timer timeout state. */
|
||||
|
||||
static pstorage_handle_t m_storage_handle_swap; /**< Pstorage handle for the swap area (bank 1). Bank used when updating an application or bootloader without SoftDevice. */
|
||||
static pstorage_handle_t m_storage_handle_app; /**< Pstorage handle for the application area (bank 0). Bank used when updating a SoftDevice w/wo bootloader. Handle also used when swapping received application from bank 1 to bank 0. */
|
||||
static pstorage_handle_t * mp_storage_handle_active; /**< Pointer to the pstorage handle for the active bank for receiving of data packets. */
|
||||
|
||||
static dfu_callback_t m_data_pkt_cb; /**< Callback from DFU Bank module for notification of asynchronous operation such as flash prepare. */
|
||||
static dfu_bank_func_t m_functions; /**< Structure holding operations for the selected update process. */
|
||||
|
||||
|
||||
/**@brief Function for handling callbacks from pstorage module.
|
||||
*
|
||||
* @details Handles pstorage results for clear and storage operation. For detailed description of
|
||||
* the parameters provided with the callback, please refer to \ref pstorage_ntf_cb_t.
|
||||
*/
|
||||
static void pstorage_callback_handler(pstorage_handle_t * p_handle,
|
||||
uint8_t op_code,
|
||||
uint32_t result,
|
||||
uint8_t * p_data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
switch (op_code)
|
||||
{
|
||||
case PSTORAGE_STORE_OP_CODE:
|
||||
if ((m_dfu_state == DFU_STATE_RX_DATA_PKT) && (m_data_pkt_cb != NULL))
|
||||
{
|
||||
m_data_pkt_cb(DATA_PACKET, result, p_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case PSTORAGE_CLEAR_OP_CODE:
|
||||
if (m_dfu_state == DFU_STATE_PREPARING)
|
||||
{
|
||||
m_functions.cleared();
|
||||
m_dfu_state = DFU_STATE_RDY;
|
||||
if (m_data_pkt_cb != NULL)
|
||||
{
|
||||
m_data_pkt_cb(START_PACKET, result, p_data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
APP_ERROR_CHECK(result);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling the DFU timeout.
|
||||
*
|
||||
* @param[in] p_context The timeout context.
|
||||
*/
|
||||
static void dfu_timeout_handler(void * p_context)
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
dfu_update_status_t update_status;
|
||||
|
||||
m_dfu_timed_out = true;
|
||||
update_status.status_code = DFU_TIMEOUT;
|
||||
|
||||
bootloader_dfu_update_process(update_status);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for restarting the DFU Timer.
|
||||
*
|
||||
* @details This function will stop and restart the DFU timer. This function will be called by the
|
||||
* functions handling any DFU packet received from the peer that is transferring a firmware
|
||||
* image.
|
||||
*/
|
||||
static uint32_t dfu_timer_restart(void)
|
||||
{
|
||||
if (m_dfu_timed_out)
|
||||
{
|
||||
// The DFU timer had already timed out.
|
||||
return NRF_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
uint32_t err_code = app_timer_stop(m_dfu_timer_id);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for preparing of flash before receiving SoftDevice image.
|
||||
*
|
||||
* @details This function will erase current application area to ensure sufficient amount of
|
||||
* storage for the SoftDevice image. Upon erase complete a callback will be done.
|
||||
* See \ref dfu_bank_prepare_t for further details.
|
||||
*/
|
||||
static void dfu_prepare_func_app_erase(uint32_t image_size)
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
mp_storage_handle_active = &m_storage_handle_app;
|
||||
|
||||
// Doing a SoftDevice update thus current application must be cleared to ensure enough space
|
||||
// for new SoftDevice.
|
||||
m_dfu_state = DFU_STATE_PREPARING;
|
||||
err_code = pstorage_clear(&m_storage_handle_app, m_image_size);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for preparing swap before receiving application or bootloader image.
|
||||
*
|
||||
* @details This function will erase current swap area to ensure flash is ready for storage of the
|
||||
* Application or Bootloader image. Upon erase complete a callback will be done.
|
||||
* See \ref dfu_bank_prepare_t for further details.
|
||||
*/
|
||||
static void dfu_prepare_func_swap_erase(uint32_t image_size)
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
mp_storage_handle_active = &m_storage_handle_swap;
|
||||
|
||||
m_dfu_state = DFU_STATE_PREPARING;
|
||||
// err_code = pstorage_clear(&m_storage_handle_swap, DFU_IMAGE_MAX_SIZE_BANKED);
|
||||
err_code = pstorage_clear(&m_storage_handle_swap, image_size);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling behaviour when clear operation has completed.
|
||||
*/
|
||||
static void dfu_cleared_func_swap(void)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling behaviour when clear operation has completed.
|
||||
*/
|
||||
static void dfu_cleared_func_app(void)
|
||||
{
|
||||
dfu_update_status_t update_status = {DFU_BANK_0_ERASED, };
|
||||
bootloader_dfu_update_process(update_status);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for calculating storage offset for receiving SoftDevice image.
|
||||
*
|
||||
* @details When a new SoftDevice is received it will be temporary stored in flash before moved to
|
||||
* address 0x0. In order to succesfully validate transfer and relocation it is important
|
||||
* that temporary image and final installed image does not ovwerlap hence an offset must
|
||||
* be calculated in case new image is larger than currently installed SoftDevice.
|
||||
*/
|
||||
uint32_t offset_calculate(uint32_t sd_image_size)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
if (m_start_packet.sd_image_size > DFU_BANK_0_REGION_START)
|
||||
{
|
||||
uint32_t page_mask = (CODE_PAGE_SIZE - 1);
|
||||
uint32_t diff = m_start_packet.sd_image_size - DFU_BANK_0_REGION_START;
|
||||
|
||||
offset = diff & ~page_mask;
|
||||
|
||||
// Align offset to next page if image size is not page sized.
|
||||
if ((diff & page_mask) > 0)
|
||||
{
|
||||
offset += CODE_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for activating received SoftDevice image.
|
||||
*
|
||||
* @note This function will not move the SoftDevice image.
|
||||
* The bootloader settings will be marked as SoftDevice update complete and the swapping of
|
||||
* current SoftDevice will occur after system reset.
|
||||
*
|
||||
* @return NRF_SUCCESS on success.
|
||||
*/
|
||||
static uint32_t dfu_activate_sd(void)
|
||||
{
|
||||
dfu_update_status_t update_status;
|
||||
|
||||
update_status.status_code = DFU_UPDATE_SD_COMPLETE;
|
||||
update_status.app_crc = m_image_crc;
|
||||
update_status.sd_image_start = DFU_BANK_0_REGION_START;
|
||||
update_status.sd_size = m_start_packet.sd_image_size;
|
||||
update_status.bl_size = m_start_packet.bl_image_size;
|
||||
update_status.app_size = m_start_packet.app_image_size;
|
||||
|
||||
bootloader_dfu_update_process(update_status);
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for activating received Application image.
|
||||
*
|
||||
* @details This function will move the received application image fram swap (bank 1) to
|
||||
* application area (bank 0).
|
||||
*
|
||||
* @return NRF_SUCCESS on success. Error code otherwise.
|
||||
*/
|
||||
static uint32_t dfu_activate_app(void)
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
// Erase BANK 0.
|
||||
err_code = pstorage_clear(&m_storage_handle_app, m_start_packet.app_image_size);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = pstorage_store(&m_storage_handle_app,
|
||||
(uint8_t *)m_storage_handle_swap.block_id,
|
||||
m_start_packet.app_image_size,
|
||||
0);
|
||||
|
||||
if (err_code == NRF_SUCCESS)
|
||||
{
|
||||
dfu_update_status_t update_status;
|
||||
|
||||
memset(&update_status, 0, sizeof(dfu_update_status_t ));
|
||||
update_status.status_code = DFU_UPDATE_APP_COMPLETE;
|
||||
update_status.app_crc = m_image_crc;
|
||||
update_status.app_size = m_start_packet.app_image_size;
|
||||
|
||||
bootloader_dfu_update_process(update_status);
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for activating received Bootloader image.
|
||||
*
|
||||
* @note This function will not move the bootloader image.
|
||||
* The bootloader settings will be marked as Bootloader update complete and the swapping of
|
||||
* current bootloader will occur after system reset.
|
||||
*
|
||||
* @return NRF_SUCCESS on success.
|
||||
*/
|
||||
static uint32_t dfu_activate_bl(void)
|
||||
{
|
||||
dfu_update_status_t update_status;
|
||||
|
||||
update_status.status_code = DFU_UPDATE_BOOT_COMPLETE;
|
||||
update_status.app_crc = m_image_crc;
|
||||
update_status.sd_size = m_start_packet.sd_image_size;
|
||||
update_status.bl_size = m_start_packet.bl_image_size;
|
||||
update_status.app_size = m_start_packet.app_image_size;
|
||||
|
||||
bootloader_dfu_update_process(update_status);
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_init(void)
|
||||
{
|
||||
uint32_t err_code;
|
||||
pstorage_module_param_t storage_module_param = {.cb = pstorage_callback_handler};
|
||||
|
||||
m_init_packet_length = 0;
|
||||
m_image_crc = 0;
|
||||
|
||||
err_code = pstorage_register(&storage_module_param, &m_storage_handle_app);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
m_dfu_state = DFU_STATE_INIT_ERROR;
|
||||
return err_code;
|
||||
}
|
||||
|
||||
m_storage_handle_app.block_id = DFU_BANK_0_REGION_START;
|
||||
m_storage_handle_swap = m_storage_handle_app;
|
||||
m_storage_handle_swap.block_id = DFU_BANK_1_REGION_START;
|
||||
|
||||
// Create the timer to monitor the activity by the peer doing the firmware update.
|
||||
err_code = app_timer_create(&m_dfu_timer_id,
|
||||
APP_TIMER_MODE_SINGLE_SHOT,
|
||||
dfu_timeout_handler);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
// Start the DFU timer.
|
||||
err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
m_data_received = 0;
|
||||
m_dfu_state = DFU_STATE_IDLE;
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void dfu_register_callback(dfu_callback_t callback_handler)
|
||||
{
|
||||
m_data_pkt_cb = callback_handler;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet)
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
m_start_packet = *(p_packet->params.start_packet);
|
||||
|
||||
// Check that the requested update procedure is supported.
|
||||
// Currently the following combinations are allowed:
|
||||
// - Application
|
||||
// - SoftDevice
|
||||
// - Bootloader
|
||||
// - SoftDevice with Bootloader
|
||||
if (IS_UPDATING_APP(m_start_packet) &&
|
||||
(IS_UPDATING_SD(m_start_packet) || IS_UPDATING_BL(m_start_packet)))
|
||||
{
|
||||
// App update is only supported independently.
|
||||
return NRF_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (!(IS_WORD_SIZED(m_start_packet.sd_image_size) &&
|
||||
IS_WORD_SIZED(m_start_packet.bl_image_size) &&
|
||||
IS_WORD_SIZED(m_start_packet.app_image_size)))
|
||||
{
|
||||
// Image_sizes are not a multiple of 4 (word size).
|
||||
return NRF_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
m_image_size = m_start_packet.sd_image_size + m_start_packet.bl_image_size +
|
||||
m_start_packet.app_image_size;
|
||||
|
||||
if (m_start_packet.bl_image_size > DFU_BL_IMAGE_MAX_SIZE)
|
||||
{
|
||||
return NRF_ERROR_DATA_SIZE;
|
||||
}
|
||||
|
||||
if (IS_UPDATING_SD(m_start_packet))
|
||||
{
|
||||
if (m_image_size > (DFU_IMAGE_MAX_SIZE_FULL))
|
||||
{
|
||||
return NRF_ERROR_DATA_SIZE;
|
||||
}
|
||||
m_functions.prepare = dfu_prepare_func_app_erase;
|
||||
m_functions.cleared = dfu_cleared_func_app;
|
||||
m_functions.activate = dfu_activate_sd;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_image_size > DFU_IMAGE_MAX_SIZE_BANKED)
|
||||
{
|
||||
return NRF_ERROR_DATA_SIZE;
|
||||
}
|
||||
|
||||
m_functions.prepare = dfu_prepare_func_swap_erase;
|
||||
m_functions.cleared = dfu_cleared_func_swap;
|
||||
if (IS_UPDATING_BL(m_start_packet))
|
||||
{
|
||||
m_functions.activate = dfu_activate_bl;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_functions.activate = dfu_activate_app;
|
||||
}
|
||||
}
|
||||
|
||||
switch (m_dfu_state)
|
||||
{
|
||||
case DFU_STATE_IDLE:
|
||||
// Valid peer activity detected. Hence restart the DFU timer.
|
||||
err_code = dfu_timer_restart();
|
||||
VERIFY_SUCCESS(err_code);
|
||||
m_functions.prepare(m_image_size);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet)
|
||||
{
|
||||
uint32_t data_length;
|
||||
uint32_t err_code;
|
||||
uint32_t * p_data;
|
||||
|
||||
VERIFY_PARAM_NOT_NULL(p_packet);
|
||||
|
||||
// Check pointer alignment.
|
||||
if (!is_word_aligned(p_packet->params.data_packet.p_data_packet))
|
||||
{
|
||||
// The p_data_packet is not word aligned address.
|
||||
return NRF_ERROR_INVALID_ADDR;
|
||||
}
|
||||
|
||||
switch (m_dfu_state)
|
||||
{
|
||||
case DFU_STATE_RDY:
|
||||
case DFU_STATE_RX_INIT_PKT:
|
||||
return NRF_ERROR_INVALID_STATE;
|
||||
|
||||
case DFU_STATE_RX_DATA_PKT:
|
||||
data_length = p_packet->params.data_packet.packet_length * sizeof(uint32_t);
|
||||
|
||||
if ((m_data_received + data_length) > m_image_size)
|
||||
{
|
||||
// The caller is trying to write more bytes into the flash than the size provided to
|
||||
// the dfu_image_size_set function. This is treated as a serious error condition and
|
||||
// an unrecoverable one. Hence point the variable mp_app_write_address to the top of
|
||||
// the flash area. This will ensure that all future application data packet writes
|
||||
// will be blocked because of the above check.
|
||||
m_data_received = 0xFFFFFFFF;
|
||||
|
||||
return NRF_ERROR_DATA_SIZE;
|
||||
}
|
||||
|
||||
// Valid peer activity detected. Hence restart the DFU timer.
|
||||
err_code = dfu_timer_restart();
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
p_data = (uint32_t *)p_packet->params.data_packet.p_data_packet;
|
||||
|
||||
err_code = pstorage_store(mp_storage_handle_active,
|
||||
(uint8_t *)p_data,
|
||||
data_length,
|
||||
m_data_received);
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
m_data_received += data_length;
|
||||
|
||||
if (m_data_received != m_image_size)
|
||||
{
|
||||
// The entire image is not received yet. More data is expected.
|
||||
err_code = NRF_ERROR_INVALID_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The entire image has been received. Return NRF_SUCCESS.
|
||||
err_code = NRF_SUCCESS;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_init_pkt_complete(void)
|
||||
{
|
||||
uint32_t err_code = NRF_ERROR_INVALID_STATE;
|
||||
|
||||
// DFU initialization has been done and a start packet has been received.
|
||||
if (IMAGE_WRITE_IN_PROGRESS())
|
||||
{
|
||||
// Image write is already in progress. Cannot handle an init packet now.
|
||||
return NRF_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (m_dfu_state == DFU_STATE_RX_INIT_PKT)
|
||||
{
|
||||
err_code = dfu_init_prevalidate(m_init_packet, m_init_packet_length, m_start_packet.dfu_update_mode);
|
||||
if (err_code == NRF_SUCCESS)
|
||||
{
|
||||
m_dfu_state = DFU_STATE_RX_DATA_PKT;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_init_packet_length = 0;
|
||||
}
|
||||
}
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_init_pkt_handle(dfu_update_packet_t * p_packet)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
uint32_t length;
|
||||
|
||||
switch (m_dfu_state)
|
||||
{
|
||||
case DFU_STATE_RDY:
|
||||
m_dfu_state = DFU_STATE_RX_INIT_PKT;
|
||||
// When receiving init packet in state ready just update and fall through this case.
|
||||
|
||||
case DFU_STATE_RX_INIT_PKT:
|
||||
// DFU initialization has been done and a start packet has been received.
|
||||
if (IMAGE_WRITE_IN_PROGRESS())
|
||||
{
|
||||
// Image write is already in progress. Cannot handle an init packet now.
|
||||
return NRF_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Valid peer activity detected. Hence restart the DFU timer.
|
||||
err_code = dfu_timer_restart();
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
length = p_packet->params.data_packet.packet_length * sizeof(uint32_t);
|
||||
if ((m_init_packet_length + length) > sizeof(m_init_packet))
|
||||
{
|
||||
return NRF_ERROR_INVALID_LENGTH;
|
||||
}
|
||||
|
||||
memcpy(&m_init_packet[m_init_packet_length],
|
||||
&p_packet->params.data_packet.p_data_packet[0],
|
||||
length);
|
||||
m_init_packet_length += length;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Either the start packet was not received or dfu_init function was not called before.
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_image_validate()
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
switch (m_dfu_state)
|
||||
{
|
||||
case DFU_STATE_RX_DATA_PKT:
|
||||
// Check if the application image write has finished.
|
||||
if (m_data_received != m_image_size)
|
||||
{
|
||||
// Image not yet fully transfered by the peer or the peer has attempted to write
|
||||
// too much data. Hence the validation should fail.
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dfu_state = DFU_STATE_VALIDATE;
|
||||
|
||||
// Valid peer activity detected. Hence restart the DFU timer.
|
||||
err_code = dfu_timer_restart();
|
||||
if (err_code == NRF_SUCCESS)
|
||||
{
|
||||
err_code = dfu_init_postvalidate((uint8_t *)mp_storage_handle_active->block_id,
|
||||
m_image_size);
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
m_dfu_state = DFU_STATE_WAIT_4_ACTIVATE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_image_activate()
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
switch (m_dfu_state)
|
||||
{
|
||||
case DFU_STATE_WAIT_4_ACTIVATE:
|
||||
|
||||
// Stop the DFU Timer because the peer activity need not be monitored any longer.
|
||||
err_code = app_timer_stop(m_dfu_timer_id);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = m_functions.activate();
|
||||
break;
|
||||
|
||||
default:
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
void dfu_reset(void)
|
||||
{
|
||||
dfu_update_status_t update_status;
|
||||
|
||||
update_status.status_code = DFU_RESET;
|
||||
|
||||
bootloader_dfu_update_process(update_status);
|
||||
}
|
||||
|
||||
|
||||
static uint32_t dfu_compare_block(uint32_t * ptr1, uint32_t * ptr2, uint32_t len)
|
||||
{
|
||||
sd_mbr_command_t sd_mbr_cmd;
|
||||
|
||||
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
|
||||
sd_mbr_cmd.params.compare.ptr1 = ptr1;
|
||||
sd_mbr_cmd.params.compare.ptr2 = ptr2;
|
||||
sd_mbr_cmd.params.compare.len = len / sizeof(uint32_t);
|
||||
|
||||
return sd_mbr_command(&sd_mbr_cmd);
|
||||
}
|
||||
|
||||
|
||||
static uint32_t dfu_copy_sd(uint32_t * src, uint32_t * dst, uint32_t len)
|
||||
{
|
||||
sd_mbr_command_t sd_mbr_cmd;
|
||||
|
||||
sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_SD;
|
||||
sd_mbr_cmd.params.copy_sd.src = src;
|
||||
sd_mbr_cmd.params.copy_sd.dst = dst;
|
||||
sd_mbr_cmd.params.copy_sd.len = len / sizeof(uint32_t);
|
||||
|
||||
return sd_mbr_command(&sd_mbr_cmd);
|
||||
}
|
||||
|
||||
|
||||
static uint32_t dfu_sd_img_block_swap(uint32_t * src,
|
||||
uint32_t * dst,
|
||||
uint32_t len,
|
||||
uint32_t block_size)
|
||||
{
|
||||
// It is neccesarry to swap the new SoftDevice in 3 rounds to ensure correct copy of data
|
||||
// and verifucation of data in case power reset occurs during write to flash.
|
||||
// To ensure the robustness of swapping the images are compared backwards till start of
|
||||
// image swap. If the back is identical everything is swapped.
|
||||
uint32_t err_code = dfu_compare_block(src, dst, len);
|
||||
if (err_code == NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
if ((uint32_t)dst > SOFTDEVICE_REGION_START)
|
||||
{
|
||||
err_code = dfu_sd_img_block_swap((uint32_t *)((uint32_t)src - block_size),
|
||||
(uint32_t *)((uint32_t)dst - block_size),
|
||||
block_size,
|
||||
block_size);
|
||||
VERIFY_SUCCESS(err_code);
|
||||
}
|
||||
|
||||
err_code = dfu_copy_sd(src, dst, len);
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
return dfu_compare_block(src, dst, len);
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_sd_image_swap(void)
|
||||
{
|
||||
bootloader_settings_t boot_settings;
|
||||
|
||||
bootloader_settings_get(&boot_settings);
|
||||
|
||||
if (boot_settings.sd_image_size == 0)
|
||||
{
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
if ((SOFTDEVICE_REGION_START + boot_settings.sd_image_size) > boot_settings.sd_image_start)
|
||||
{
|
||||
uint32_t err_code;
|
||||
uint32_t sd_start = SOFTDEVICE_REGION_START;
|
||||
uint32_t block_size = (boot_settings.sd_image_start - sd_start) / 2;
|
||||
|
||||
/* ##### FIX START ##### */
|
||||
block_size &= ~(uint32_t)(CODE_PAGE_SIZE - 1);
|
||||
/* ##### FIX END ##### */
|
||||
|
||||
uint32_t image_end = boot_settings.sd_image_start + boot_settings.sd_image_size;
|
||||
|
||||
uint32_t img_block_start = boot_settings.sd_image_start + 2 * block_size;
|
||||
uint32_t sd_block_start = sd_start + 2 * block_size;
|
||||
|
||||
if (SD_SIZE_GET(MBR_SIZE) < boot_settings.sd_image_size)
|
||||
{
|
||||
// This will clear a page thus ensuring the old image is invalidated before swapping.
|
||||
err_code = dfu_copy_sd((uint32_t *)(sd_start + block_size),
|
||||
(uint32_t *)(sd_start + block_size),
|
||||
sizeof(uint32_t));
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
err_code = dfu_copy_sd((uint32_t *)sd_start, (uint32_t *)sd_start, sizeof(uint32_t));
|
||||
VERIFY_SUCCESS(err_code);
|
||||
}
|
||||
|
||||
return dfu_sd_img_block_swap((uint32_t *)img_block_start,
|
||||
(uint32_t *)sd_block_start,
|
||||
image_end - img_block_start,
|
||||
block_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (boot_settings.sd_image_size != 0)
|
||||
{
|
||||
return dfu_copy_sd((uint32_t *)boot_settings.sd_image_start,
|
||||
(uint32_t *)SOFTDEVICE_REGION_START,
|
||||
boot_settings.sd_image_size);
|
||||
}
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_bl_image_swap(void)
|
||||
{
|
||||
bootloader_settings_t bootloader_settings;
|
||||
sd_mbr_command_t sd_mbr_cmd;
|
||||
|
||||
bootloader_settings_get(&bootloader_settings);
|
||||
|
||||
if (bootloader_settings.bl_image_size != 0)
|
||||
{
|
||||
uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ?
|
||||
DFU_BANK_1_REGION_START :
|
||||
bootloader_settings.sd_image_start +
|
||||
bootloader_settings.sd_image_size;
|
||||
|
||||
sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_BL;
|
||||
sd_mbr_cmd.params.copy_bl.bl_src = (uint32_t *)(bl_image_start);
|
||||
sd_mbr_cmd.params.copy_bl.bl_len = bootloader_settings.bl_image_size / sizeof(uint32_t);
|
||||
|
||||
return sd_mbr_command(&sd_mbr_cmd);
|
||||
}
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_bl_image_validate(void)
|
||||
{
|
||||
bootloader_settings_t bootloader_settings;
|
||||
sd_mbr_command_t sd_mbr_cmd;
|
||||
|
||||
bootloader_settings_get(&bootloader_settings);
|
||||
|
||||
if (bootloader_settings.bl_image_size != 0)
|
||||
{
|
||||
uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ?
|
||||
DFU_BANK_1_REGION_START :
|
||||
bootloader_settings.sd_image_start +
|
||||
bootloader_settings.sd_image_size;
|
||||
|
||||
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
|
||||
sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START;
|
||||
sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)(bl_image_start);
|
||||
sd_mbr_cmd.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t);
|
||||
|
||||
return sd_mbr_command(&sd_mbr_cmd);
|
||||
}
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_sd_image_validate(void)
|
||||
{
|
||||
bootloader_settings_t bootloader_settings;
|
||||
sd_mbr_command_t sd_mbr_cmd;
|
||||
|
||||
bootloader_settings_get(&bootloader_settings);
|
||||
|
||||
if (bootloader_settings.sd_image_size == 0)
|
||||
{
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
if ((SOFTDEVICE_REGION_START + bootloader_settings.sd_image_size) > bootloader_settings.sd_image_start)
|
||||
{
|
||||
uint32_t sd_start = SOFTDEVICE_REGION_START;
|
||||
uint32_t block_size = (bootloader_settings.sd_image_start - sd_start) / 2;
|
||||
uint32_t image_end = bootloader_settings.sd_image_start +
|
||||
bootloader_settings.sd_image_size;
|
||||
|
||||
uint32_t img_block_start = bootloader_settings.sd_image_start + 2 * block_size;
|
||||
uint32_t sd_block_start = sd_start + 2 * block_size;
|
||||
|
||||
if (SD_SIZE_GET(MBR_SIZE) < bootloader_settings.sd_image_size)
|
||||
{
|
||||
return NRF_ERROR_NULL;
|
||||
}
|
||||
|
||||
return dfu_sd_img_block_swap((uint32_t *)img_block_start,
|
||||
(uint32_t *)sd_block_start,
|
||||
image_end - img_block_start,
|
||||
block_size);
|
||||
}
|
||||
|
||||
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
|
||||
sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)SOFTDEVICE_REGION_START;
|
||||
sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)bootloader_settings.sd_image_start;
|
||||
sd_mbr_cmd.params.compare.len = bootloader_settings.sd_image_size / sizeof(uint32_t);
|
||||
|
||||
return sd_mbr_command(&sd_mbr_cmd);
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/* 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_init Init packet handling in DFU
|
||||
* @{
|
||||
*
|
||||
* @brief Device Firmware Update module type and function declaration for init packet handling.
|
||||
*
|
||||
* @details This header contains basic functionality for performing safety checks on software
|
||||
* updates for \nRFXX based devices. It provides a skeleton for pre-checking an init packet
|
||||
* to ensure the following image is compatible with this device. A safety check should
|
||||
* always be performed to prevent accidental flashing of unsupported applications or a
|
||||
* wrong combination of application and SoftDevice.
|
||||
* The device information contains information such as:
|
||||
* - Device type (2 bytes), for example Heart Rate. The device type is a number defined by
|
||||
* the customer. It can be located in UICR or FICR.
|
||||
* - Device revision (2 bytes), for example major revision 1, minor revision 0. The device
|
||||
* revision is a number defined by the customer. It can be located in UICR or FICR.
|
||||
* - List of SoftDevices supported by this application, for example
|
||||
* 0x0049 = S110v6_0_0
|
||||
* 0xFFFE = S110 development (any SoftDevice accepted),
|
||||
* - CRC or hash of firmware image
|
||||
*
|
||||
* @note This module does not support security features such as image signing, but the corresponding
|
||||
* implementation allows for such extensions.
|
||||
* If the init packet is signed by a trusted source, it must be decrypted before it can be
|
||||
* processed.
|
||||
*/
|
||||
|
||||
#ifndef DFU_INIT_H__
|
||||
#define DFU_INIT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "nrf.h"
|
||||
|
||||
/**@brief Structure contained in an init packet. Contains information on device type, revision, and
|
||||
* supported SoftDevices.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint16_t device_type; /**< Device type (2 bytes), for example Heart Rate. This number must be defined by the customer before production. It can be located in UICR or FICR. */
|
||||
uint16_t device_rev; /**< Device revision (2 bytes), for example major revision 1, minor revision 0. This number must be defined by the customer before production. It can be located in UICR or FICR. */
|
||||
uint32_t app_version; /**< Application version for the image software. This field allows for additional checking, for example ensuring that a downgrade is not allowed. */
|
||||
uint16_t softdevice_len; /**< Number of different SoftDevice revisions compatible with this application. The list of SoftDevice firmware IDs is defined in @ref softdevice. */
|
||||
uint16_t softdevice[1]; /**< Variable length array of SoftDevices compatible with this application. The length of the array is specified in the length field. SoftDevice firmware id 0xFFFE indicates any SoftDevice. */
|
||||
} dfu_init_packet_t;
|
||||
|
||||
/**@brief Structure holding basic device information settings.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint16_t device_type; /**< Device type (2 bytes), for example Heart Rate. This number must be defined by the customer before production. It can be located in UICR or FICR. */
|
||||
uint16_t device_rev; /**< Device revision (2 bytes), for example major revision 1, minor revision 0. This number must be defined by the customer before production. It can be located in UICR or FICR. */
|
||||
} dfu_device_info_t;
|
||||
|
||||
/** The device info offset can be modified to place the device info settings at a different location.
|
||||
* If the customer reserved UICR location is used for other application specific data, the offset
|
||||
* must be updated to avoid collision with that data.
|
||||
*/
|
||||
/** [DFU UICR DEV offset] */
|
||||
#define UICR_CUSTOMER_DEVICE_INFO_OFFSET 0x0 /**< Device info offset inside the customer UICR reserved area. Customers may change this value to place the device information in a user-preferred location. */
|
||||
/** [DFU UICR DEV offset] */
|
||||
|
||||
#define UICR_CUSTOMER_RESERVED_OFFSET 0x80 /**< Customer reserved area in the UICR. The area from UICR + 0x80 is reserved for customer usage. */
|
||||
#define DFU_DEVICE_INFO_BASE (NRF_UICR_BASE + \
|
||||
UICR_CUSTOMER_RESERVED_OFFSET + \
|
||||
UICR_CUSTOMER_DEVICE_INFO_OFFSET) /**< The device information base address inside of UICR. */
|
||||
#define DFU_DEVICE_INFO ((dfu_device_info_t *)DFU_DEVICE_INFO_BASE) /**< The memory mapped structure for device information data. */
|
||||
|
||||
#define DFU_DEVICE_TYPE_EMPTY ((uint16_t)0xFFFF) /**< Mask indicating no device type is present in UICR. 0xFFFF is default flash pattern when not written with data. */
|
||||
#define DFU_DEVICE_REVISION_EMPTY ((uint16_t)0xFFFF) /**< Mask indicating no device revision is present in UICR. 0xFFFF is default flash pattern when not written with data. */
|
||||
#define DFU_SOFTDEVICE_ANY ((uint16_t)0xFFFE) /**< Mask indicating that any SoftDevice is allowed for updating this application. Allows for easy development. Not to be used in production images. */
|
||||
|
||||
|
||||
/**@brief DFU prevalidate call for pre-checking the received init packet.
|
||||
*
|
||||
* @details Pre-validation will safety check the firmware image to be transfered in second stage.
|
||||
* The function currently checks the device type, device revision, application firmware
|
||||
* version, and supported SoftDevices. More checks should be added according to
|
||||
* customer-specific requirements.
|
||||
*
|
||||
* @param[in] p_init_data Pointer to the init packet. If the init packet is encrypted or signed,
|
||||
* it must first be decrypted before being checked.
|
||||
* @param[in] init_data_len Length of the init data.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the pre-validation succeeded, that means the image is
|
||||
* supported by the device and it is considered to come from a
|
||||
* trusted source (signing).
|
||||
* @retval NRF_ERROR_INVALID_DATA If the pre-validation failed, that means the image is not
|
||||
* supported by the device or comes from an un-trusted source
|
||||
* (signing).
|
||||
* @retval NRF_ERROR_INVALID_LENGTH If the size of the init packet is not within the limits of
|
||||
* the init packet handler.
|
||||
*/
|
||||
uint32_t dfu_init_prevalidate(uint8_t * p_init_data, uint32_t init_data_len, uint8_t image_type);
|
||||
|
||||
/**@brief DFU postvalidate call for post-checking the received image using the init packet.
|
||||
*
|
||||
* @details Post-validation can verify the integrity check the firmware image received before
|
||||
* activating the image.
|
||||
* Checks performed can be:
|
||||
* - A simple CRC as shown in the corresponding implementation of this API in the file
|
||||
* dfu_init_template.c
|
||||
* - A hash for better verification of the image.
|
||||
* - A signature to ensure the image originates from a trusted source.
|
||||
* Checks are intended to be expanded for customer-specific requirements.
|
||||
*
|
||||
* @param[in] p_image Pointer to the received image. The init data provided in the call
|
||||
* \ref dfu_init_prevalidate will be used for validating the image.
|
||||
* @param[in] image_len Length of the image data.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the post-validation succeeded, that meant the integrity of the
|
||||
* image has been verified and the image originates from a trusted
|
||||
* source (signing).
|
||||
* @retval NRF_ERROR_INVALID_DATA If the post-validation failed, that meant the post check of the
|
||||
* image failed such as the CRC is not matching the image transfered
|
||||
* or the verification of the image fails (signing).
|
||||
*/
|
||||
uint32_t dfu_init_postvalidate(uint8_t * p_image, uint32_t image_len);
|
||||
|
||||
#endif // DFU_INIT_H__
|
||||
|
||||
/**@} */
|
@ -0,0 +1,172 @@
|
||||
/* 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_init_template Template file with an DFU init packet handling example.
|
||||
* @{
|
||||
*
|
||||
* @ingroup nrf_dfu
|
||||
*
|
||||
* @brief This file contains a template on how to implement DFU init packet handling.
|
||||
*
|
||||
* @details The template shows how device type and revision can be used for a safety check of the
|
||||
* received image. It shows how validation can be performed in two stages:
|
||||
* - Stage 1: Pre-check of firmware image before transfer to ensure the firmware matches:
|
||||
* - Device Type.
|
||||
* - Device Revision.
|
||||
* Installed SoftDevice.
|
||||
* This template can be extended with additional checks according to needs.
|
||||
* For example, such a check could be the origin of the image (trusted source)
|
||||
* based on a signature scheme.
|
||||
* - Stage 2: Post-check of the image after image transfer but before installing firmware.
|
||||
* For example, such a check could be an integrity check in form of hashing or
|
||||
* verification of a signature.
|
||||
* In this template, a simple CRC check is carried out.
|
||||
* The CRC check can be replaced with other mechanisms, like signing.
|
||||
*
|
||||
* @note This module does not support security features such as image signing, but the
|
||||
* implementation allows for such extension.
|
||||
* If the init packet is signed by a trusted source, it must be decrypted before it can be
|
||||
* processed.
|
||||
*/
|
||||
|
||||
#include "dfu_init.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <dfu_types.h>
|
||||
#include "nrf_error.h"
|
||||
#include "crc16.h"
|
||||
|
||||
// ADAFRUIT
|
||||
// All firmware init data must has Device Type ADAFRUIT_DEVICE_TYPE
|
||||
// SD + Bootloader upgrade must have ADAFRUIT_SD_UNLOCK_CODE in Device Revision
|
||||
#define ADAFRUIT_DEVICE_TYPE 0x0052 // for nrf52
|
||||
#define ADAFRUIT_SD_UNLOCK_CODE 0xADAF
|
||||
|
||||
#define DFU_INIT_PACKET_EXT_LENGTH_MIN 2 //< Minimum length of the extended init packet. The extended init packet may contain a CRC, a HASH, or other data. This value must be changed according to the requirements of the system. The template uses a minimum value of two in order to hold a CRC. */
|
||||
#define DFU_INIT_PACKET_EXT_LENGTH_MAX 10 //< Maximum length of the extended init packet. The extended init packet may contain a CRC, a HASH, or other data. This value must be changed according to the requirements of the system. The template uses a maximum value of 10 in order to hold a CRC and any padded data on transport layer without overflow. */
|
||||
|
||||
static uint8_t m_extended_packet[DFU_INIT_PACKET_EXT_LENGTH_MAX]; //< Data array for storage of the extended data received. The extended data follows the normal init data of type \ref dfu_init_packet_t. Extended data can be used for a CRC, hash, signature, or other data. */
|
||||
static uint8_t m_extended_packet_length; //< Length of the extended data received with init packet. */
|
||||
|
||||
|
||||
uint32_t dfu_init_prevalidate(uint8_t * p_init_data, uint32_t init_data_len, uint8_t image_type)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
|
||||
// In order to support signing or encryption then any init packet decryption function / library
|
||||
// should be called from here or implemented at this location.
|
||||
|
||||
// Length check to ensure valid data are parsed.
|
||||
if (init_data_len < sizeof(dfu_init_packet_t))
|
||||
{
|
||||
return NRF_ERROR_INVALID_LENGTH;
|
||||
}
|
||||
|
||||
// Current template uses clear text data so they can be casted for pre-check.
|
||||
dfu_init_packet_t * p_init_packet = (dfu_init_packet_t *)p_init_data;
|
||||
|
||||
m_extended_packet_length = ((uint32_t)p_init_data + init_data_len) -
|
||||
(uint32_t)&p_init_packet->softdevice[p_init_packet->softdevice_len];
|
||||
if (m_extended_packet_length < DFU_INIT_PACKET_EXT_LENGTH_MIN)
|
||||
{
|
||||
return NRF_ERROR_INVALID_LENGTH;
|
||||
}
|
||||
|
||||
if (((uint32_t)p_init_data + init_data_len) <
|
||||
(uint32_t)&p_init_packet->softdevice[p_init_packet->softdevice_len])
|
||||
{
|
||||
return NRF_ERROR_INVALID_LENGTH;
|
||||
}
|
||||
|
||||
memcpy(m_extended_packet,
|
||||
&p_init_packet->softdevice[p_init_packet->softdevice_len],
|
||||
m_extended_packet_length);
|
||||
|
||||
/** [DFU init application version] */
|
||||
// To support application versioning, this check should be updated.
|
||||
// This template allows for any application to be installed. However,
|
||||
// customers can place a revision number at the bottom of the application
|
||||
// to be verified by the bootloader. This can be done at a location
|
||||
// relative to the application, for example the application start
|
||||
// address + 0x0100.
|
||||
/** [DFU init application version] */
|
||||
|
||||
// First check to verify the image to be transfered matches the device type.
|
||||
// If no Device type is present in DFU_DEVICE_INFO then any image will be accepted.
|
||||
// if ((DFU_DEVICE_INFO->device_type != DFU_DEVICE_TYPE_EMPTY) &&
|
||||
// (p_init_packet->device_type != DFU_DEVICE_INFO->device_type))
|
||||
// {
|
||||
// return NRF_ERROR_INVALID_DATA;
|
||||
// }
|
||||
|
||||
// Second check to verify the image to be transfered matches the device revision.
|
||||
// If no Device revision is present in DFU_DEVICE_INFO then any image will be accepted.
|
||||
// if ((DFU_DEVICE_INFO->device_rev != DFU_DEVICE_REVISION_EMPTY) &&
|
||||
// (p_init_packet->device_rev != DFU_DEVICE_INFO->device_rev))
|
||||
|
||||
if ( p_init_packet->device_type != ADAFRUIT_DEVICE_TYPE )
|
||||
{
|
||||
return NRF_ERROR_FORBIDDEN;
|
||||
}
|
||||
|
||||
// Adafruit unlock code must match to upgrade SoftDevice and/or Bootloader
|
||||
if ( image_type & (DFU_UPDATE_SD | DFU_UPDATE_BL) )
|
||||
{
|
||||
if (p_init_packet->device_rev != ADAFRUIT_SD_UNLOCK_CODE)
|
||||
{
|
||||
return NRF_ERROR_FORBIDDEN;
|
||||
}
|
||||
}
|
||||
|
||||
// Third check: Check the array of supported SoftDevices by this application.
|
||||
// If the installed SoftDevice does not match any SoftDevice in the list then an
|
||||
// error is returned.
|
||||
while (i < p_init_packet->softdevice_len)
|
||||
{
|
||||
if (p_init_packet->softdevice[i] == DFU_SOFTDEVICE_ANY ||
|
||||
p_init_packet->softdevice[i++] == SD_FWID_GET(MBR_SIZE))
|
||||
{
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
// No matching SoftDevice found - Return NRF_ERROR_INVALID_DATA.
|
||||
return NRF_ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_init_postvalidate(uint8_t * p_image, uint32_t image_len)
|
||||
{
|
||||
uint16_t image_crc;
|
||||
uint16_t received_crc;
|
||||
|
||||
// In order to support hashing (and signing) then the (decrypted) hash should be fetched and
|
||||
// the corresponding hash should be calculated over the image at this location.
|
||||
// If hashing (or signing) is added to the system then the CRC validation should be removed.
|
||||
|
||||
// calculate CRC from active block.
|
||||
image_crc = crc16_compute(p_image, image_len, NULL);
|
||||
|
||||
// Decode the received CRC from extended data.
|
||||
received_crc = uint16_decode((uint8_t *)&m_extended_packet[0]);
|
||||
|
||||
// Compare the received and calculated CRC.
|
||||
if (image_crc != received_crc)
|
||||
{
|
||||
return NRF_ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
@ -0,0 +1,788 @@
|
||||
/* 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 <stddef.h>
|
||||
#include "dfu.h"
|
||||
#include <dfu_types.h>
|
||||
#include "dfu_bank_internal.h"
|
||||
#include "nrf.h"
|
||||
#include "nrf_sdm.h"
|
||||
#include "app_error.h"
|
||||
#include "app_timer.h"
|
||||
#include "bootloader.h"
|
||||
#include "bootloader_types.h"
|
||||
#include "pstorage.h"
|
||||
#include "nrf_mbr.h"
|
||||
#include "dfu_init.h"
|
||||
#include "sdk_common.h"
|
||||
|
||||
static dfu_state_t m_dfu_state; /**< Current DFU state. */
|
||||
static uint32_t m_image_size; /**< Size of the image that will be transmitted. */
|
||||
|
||||
static dfu_start_packet_t m_start_packet; /**< Start packet received for this update procedure. Contains update mode and image sizes information to be used for image transfer. */
|
||||
static uint8_t m_init_packet[64]; /**< Init packet, can hold CRC, Hash, Signed Hash and similar, for image validation, integrety check and authorization checking. */
|
||||
static uint8_t m_init_packet_length; /**< Length of init packet received. */
|
||||
static uint16_t m_image_crc; /**< Calculated CRC of the image received. */
|
||||
|
||||
APP_TIMER_DEF(m_dfu_timer_id); /**< Application timer id. */
|
||||
static bool m_dfu_timed_out = false; /**< Boolean flag value for tracking DFU timer timeout state. */
|
||||
|
||||
static pstorage_handle_t m_storage_handle_app; /**< Pstorage handle for the application area (bank 0). Bank used when updating a SoftDevice w/wo bootloader. Handle also used when swapping received application from bank 1 to bank 0. */
|
||||
static pstorage_handle_t * mp_storage_handle_active; /**< Pointer to the pstorage handle for the active bank for receiving of data packets. */
|
||||
|
||||
static dfu_callback_t m_data_pkt_cb; /**< Callback from DFU Bank module for notification of asynchronous operation such as flash prepare. */
|
||||
static dfu_bank_func_t m_functions; /**< Structure holding operations for the selected update process. */
|
||||
|
||||
|
||||
/**@brief Function for handling callbacks from pstorage module.
|
||||
*
|
||||
* @details Handles pstorage results for clear and storage operation. For detailed description of
|
||||
* the parameters provided with the callback, please refer to \ref pstorage_ntf_cb_t.
|
||||
*/
|
||||
static void pstorage_callback_handler(pstorage_handle_t * p_handle,
|
||||
uint8_t op_code,
|
||||
uint32_t result,
|
||||
uint8_t * p_data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
switch (op_code)
|
||||
{
|
||||
case PSTORAGE_STORE_OP_CODE:
|
||||
if ((m_dfu_state == DFU_STATE_RX_DATA_PKT) && (m_data_pkt_cb != NULL))
|
||||
{
|
||||
m_data_pkt_cb(DATA_PACKET, result, p_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case PSTORAGE_CLEAR_OP_CODE:
|
||||
if (m_dfu_state == DFU_STATE_PREPARING)
|
||||
{
|
||||
m_functions.cleared();
|
||||
m_dfu_state = DFU_STATE_RDY;
|
||||
if (m_data_pkt_cb != NULL)
|
||||
{
|
||||
m_data_pkt_cb(START_PACKET, result, p_data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
APP_ERROR_CHECK(result);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling the DFU timeout.
|
||||
*
|
||||
* @param[in] p_context The timeout context.
|
||||
*/
|
||||
static void dfu_timeout_handler(void * p_context)
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
dfu_update_status_t update_status;
|
||||
|
||||
m_dfu_timed_out = true;
|
||||
update_status.status_code = DFU_TIMEOUT;
|
||||
|
||||
bootloader_dfu_update_process(update_status);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for restarting the DFU Timer.
|
||||
*
|
||||
* @details This function will stop and restart the DFU timer. This function will be called by the
|
||||
* functions handling any DFU packet received from the peer that is transferring a firmware
|
||||
* image.
|
||||
*/
|
||||
static uint32_t dfu_timer_restart(void)
|
||||
{
|
||||
if (m_dfu_timed_out)
|
||||
{
|
||||
// The DFU timer had already timed out.
|
||||
return NRF_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
uint32_t err_code = app_timer_stop(m_dfu_timer_id);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for preparing of flash before receiving SoftDevice image.
|
||||
*
|
||||
* @details This function will erase current application area to ensure sufficient amount of
|
||||
* storage for the SoftDevice image. Upon erase complete a callback will be done.
|
||||
* See \ref dfu_bank_prepare_t for further details.
|
||||
*/
|
||||
static void dfu_prepare_func_app_erase(uint32_t image_size)
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
mp_storage_handle_active = &m_storage_handle_app;
|
||||
|
||||
// Doing a SoftDevice update thus current application must be cleared to ensure enough space
|
||||
// for new SoftDevice.
|
||||
m_dfu_state = DFU_STATE_PREPARING;
|
||||
err_code = pstorage_clear(&m_storage_handle_app, m_image_size);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling behaviour when clear operation has completed.
|
||||
*/
|
||||
static void dfu_cleared_func_app(void)
|
||||
{
|
||||
dfu_update_status_t update_status = {DFU_BANK_0_ERASED, };
|
||||
bootloader_dfu_update_process(update_status);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for calculating storage offset for receiving SoftDevice image.
|
||||
*
|
||||
* @details When a new SoftDevice is received it will be temporary stored in flash before moved to
|
||||
* address 0x0. In order to succesfully validate transfer and relocation it is important
|
||||
* that temporary image and final installed image does not ovwerlap hence an offset must
|
||||
* be calculated in case new image is larger than currently installed SoftDevice.
|
||||
*/
|
||||
uint32_t offset_calculate(uint32_t sd_image_size)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
if (m_start_packet.sd_image_size > DFU_BANK_0_REGION_START)
|
||||
{
|
||||
uint32_t page_mask = (CODE_PAGE_SIZE - 1);
|
||||
uint32_t diff = m_start_packet.sd_image_size - DFU_BANK_0_REGION_START;
|
||||
|
||||
offset = diff & ~page_mask;
|
||||
|
||||
// Align offset to next page if image size is not page sized.
|
||||
if ((diff & page_mask) > 0)
|
||||
{
|
||||
offset += CODE_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for activating received SoftDevice image.
|
||||
*
|
||||
* @note This function will not move the SoftDevice image.
|
||||
* The bootloader settings will be marked as SoftDevice update complete and the swapping of
|
||||
* current SoftDevice will occur after system reset.
|
||||
*
|
||||
* @return NRF_SUCCESS on success.
|
||||
*/
|
||||
static uint32_t dfu_activate_sd(void)
|
||||
{
|
||||
dfu_update_status_t update_status;
|
||||
|
||||
update_status.status_code = DFU_UPDATE_SD_COMPLETE;
|
||||
update_status.app_crc = m_image_crc;
|
||||
update_status.sd_image_start = DFU_BANK_0_REGION_START;
|
||||
update_status.sd_size = m_start_packet.sd_image_size;
|
||||
update_status.bl_size = m_start_packet.bl_image_size;
|
||||
update_status.app_size = m_start_packet.app_image_size;
|
||||
|
||||
bootloader_dfu_update_process(update_status);
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for activating received Application image.
|
||||
*
|
||||
* @details This function will move the received application image fram swap (bank 1) to
|
||||
* application area (bank 0).
|
||||
*
|
||||
* @return NRF_SUCCESS on success. Error code otherwise.
|
||||
*/
|
||||
static uint32_t dfu_activate_app(void)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
dfu_update_status_t update_status;
|
||||
|
||||
memset(&update_status, 0, sizeof(dfu_update_status_t ));
|
||||
update_status.status_code = DFU_UPDATE_APP_COMPLETE;
|
||||
update_status.app_crc = m_image_crc;
|
||||
update_status.app_size = m_start_packet.app_image_size;
|
||||
|
||||
bootloader_dfu_update_process(update_status);
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for activating received Bootloader image.
|
||||
*
|
||||
* @note This function will not move the bootloader image.
|
||||
* The bootloader settings will be marked as Bootloader update complete and the swapping of
|
||||
* current bootloader will occur after system reset.
|
||||
*
|
||||
* @return NRF_SUCCESS on success.
|
||||
*/
|
||||
static uint32_t dfu_activate_bl(void)
|
||||
{
|
||||
dfu_update_status_t update_status;
|
||||
|
||||
update_status.status_code = DFU_UPDATE_BOOT_COMPLETE;
|
||||
update_status.app_crc = m_image_crc;
|
||||
update_status.sd_size = m_start_packet.sd_image_size;
|
||||
update_status.bl_size = m_start_packet.bl_image_size;
|
||||
update_status.app_size = m_start_packet.app_image_size;
|
||||
|
||||
bootloader_dfu_update_process(update_status);
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_init(void)
|
||||
{
|
||||
uint32_t err_code;
|
||||
pstorage_module_param_t storage_module_param = {.cb = pstorage_callback_handler};
|
||||
|
||||
m_init_packet_length = 0;
|
||||
m_image_crc = 0;
|
||||
|
||||
err_code = pstorage_register(&storage_module_param, &m_storage_handle_app);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
m_dfu_state = DFU_STATE_INIT_ERROR;
|
||||
return err_code;
|
||||
}
|
||||
|
||||
m_storage_handle_app.block_id = DFU_BANK_0_REGION_START;
|
||||
|
||||
// Create the timer to monitor the activity by the peer doing the firmware update.
|
||||
err_code = app_timer_create(&m_dfu_timer_id,
|
||||
APP_TIMER_MODE_SINGLE_SHOT,
|
||||
dfu_timeout_handler);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
// Start the DFU timer.
|
||||
err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
m_data_received = 0;
|
||||
m_dfu_state = DFU_STATE_IDLE;
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void dfu_register_callback(dfu_callback_t callback_handler)
|
||||
{
|
||||
m_data_pkt_cb = callback_handler;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet)
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
m_start_packet = *(p_packet->params.start_packet);
|
||||
|
||||
// Check that the requested update procedure is supported.
|
||||
// Currently the following combinations are allowed:
|
||||
// - Application
|
||||
// - SoftDevice
|
||||
// - Bootloader
|
||||
// - SoftDevice with Bootloader
|
||||
if (IS_UPDATING_APP(m_start_packet) &&
|
||||
(IS_UPDATING_SD(m_start_packet) || IS_UPDATING_BL(m_start_packet)))
|
||||
{
|
||||
// App update is only supported independently.
|
||||
return NRF_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (!(IS_WORD_SIZED(m_start_packet.sd_image_size) &&
|
||||
IS_WORD_SIZED(m_start_packet.bl_image_size) &&
|
||||
IS_WORD_SIZED(m_start_packet.app_image_size)))
|
||||
{
|
||||
// Image_sizes are not a multiple of 4 (word size).
|
||||
return NRF_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
m_image_size = m_start_packet.sd_image_size + m_start_packet.bl_image_size +
|
||||
m_start_packet.app_image_size;
|
||||
|
||||
if (m_start_packet.bl_image_size > DFU_BL_IMAGE_MAX_SIZE)
|
||||
{
|
||||
return NRF_ERROR_DATA_SIZE;
|
||||
}
|
||||
|
||||
if (m_image_size > (DFU_IMAGE_MAX_SIZE_FULL))
|
||||
{
|
||||
return NRF_ERROR_DATA_SIZE;
|
||||
}
|
||||
m_functions.prepare = dfu_prepare_func_app_erase;
|
||||
m_functions.cleared = dfu_cleared_func_app;
|
||||
|
||||
if (IS_UPDATING_SD(m_start_packet))
|
||||
{
|
||||
m_functions.activate = dfu_activate_sd;
|
||||
}
|
||||
else if (IS_UPDATING_BL(m_start_packet))
|
||||
{
|
||||
m_functions.activate = dfu_activate_bl;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_functions.activate = dfu_activate_app;
|
||||
}
|
||||
|
||||
switch (m_dfu_state)
|
||||
{
|
||||
case DFU_STATE_IDLE:
|
||||
// Valid peer activity detected. Hence restart the DFU timer.
|
||||
err_code = dfu_timer_restart();
|
||||
VERIFY_SUCCESS(err_code);
|
||||
m_functions.prepare(m_image_size);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet)
|
||||
{
|
||||
uint32_t data_length;
|
||||
uint32_t err_code;
|
||||
uint32_t * p_data;
|
||||
|
||||
VERIFY_PARAM_NOT_NULL(p_packet);
|
||||
|
||||
// Check pointer alignment.
|
||||
if (!is_word_aligned(p_packet->params.data_packet.p_data_packet))
|
||||
{
|
||||
// The p_data_packet is not word aligned address.
|
||||
return NRF_ERROR_INVALID_ADDR;
|
||||
}
|
||||
|
||||
switch (m_dfu_state)
|
||||
{
|
||||
case DFU_STATE_RDY:
|
||||
case DFU_STATE_RX_INIT_PKT:
|
||||
return NRF_ERROR_INVALID_STATE;
|
||||
|
||||
case DFU_STATE_RX_DATA_PKT:
|
||||
data_length = p_packet->params.data_packet.packet_length * sizeof(uint32_t);
|
||||
|
||||
if ((m_data_received + data_length) > m_image_size)
|
||||
{
|
||||
// The caller is trying to write more bytes into the flash than the size provided to
|
||||
// the dfu_image_size_set function. This is treated as a serious error condition and
|
||||
// an unrecoverable one. Hence point the variable mp_app_write_address to the top of
|
||||
// the flash area. This will ensure that all future application data packet writes
|
||||
// will be blocked because of the above check.
|
||||
m_data_received = 0xFFFFFFFF;
|
||||
|
||||
return NRF_ERROR_DATA_SIZE;
|
||||
}
|
||||
|
||||
// Valid peer activity detected. Hence restart the DFU timer.
|
||||
err_code = dfu_timer_restart();
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
p_data = (uint32_t *)p_packet->params.data_packet.p_data_packet;
|
||||
|
||||
err_code = pstorage_store(mp_storage_handle_active,
|
||||
(uint8_t *)p_data,
|
||||
data_length,
|
||||
m_data_received);
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
m_data_received += data_length;
|
||||
|
||||
if (m_data_received != m_image_size)
|
||||
{
|
||||
// The entire image is not received yet. More data is expected.
|
||||
err_code = NRF_ERROR_INVALID_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The entire image has been received. Return NRF_SUCCESS.
|
||||
err_code = NRF_SUCCESS;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_init_pkt_complete(void)
|
||||
{
|
||||
uint32_t err_code = NRF_ERROR_INVALID_STATE;
|
||||
|
||||
// DFU initialization has been done and a start packet has been received.
|
||||
if (IMAGE_WRITE_IN_PROGRESS())
|
||||
{
|
||||
// Image write is already in progress. Cannot handle an init packet now.
|
||||
return NRF_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (m_dfu_state == DFU_STATE_RX_INIT_PKT)
|
||||
{
|
||||
err_code = dfu_init_prevalidate(m_init_packet, m_init_packet_length, m_start_packet.dfu_update_mode);
|
||||
if (err_code == NRF_SUCCESS)
|
||||
{
|
||||
m_dfu_state = DFU_STATE_RX_DATA_PKT;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_init_packet_length = 0;
|
||||
}
|
||||
}
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_init_pkt_handle(dfu_update_packet_t * p_packet)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
uint32_t length;
|
||||
|
||||
switch (m_dfu_state)
|
||||
{
|
||||
case DFU_STATE_RDY:
|
||||
m_dfu_state = DFU_STATE_RX_INIT_PKT;
|
||||
// When receiving init packet in state ready just update and fall through this case.
|
||||
|
||||
case DFU_STATE_RX_INIT_PKT:
|
||||
// DFU initialization has been done and a start packet has been received.
|
||||
if (IMAGE_WRITE_IN_PROGRESS())
|
||||
{
|
||||
// Image write is already in progress. Cannot handle an init packet now.
|
||||
return NRF_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Valid peer activity detected. Hence restart the DFU timer.
|
||||
err_code = dfu_timer_restart();
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
length = p_packet->params.data_packet.packet_length * sizeof(uint32_t);
|
||||
if ((m_init_packet_length + length) > sizeof(m_init_packet))
|
||||
{
|
||||
return NRF_ERROR_INVALID_LENGTH;
|
||||
}
|
||||
|
||||
memcpy(&m_init_packet[m_init_packet_length],
|
||||
&p_packet->params.data_packet.p_data_packet[0],
|
||||
length);
|
||||
m_init_packet_length += length;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Either the start packet was not received or dfu_init function was not called before.
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_image_validate()
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
switch (m_dfu_state)
|
||||
{
|
||||
case DFU_STATE_RX_DATA_PKT:
|
||||
// Check if the application image write has finished.
|
||||
if (m_data_received != m_image_size)
|
||||
{
|
||||
// Image not yet fully transfered by the peer or the peer has attempted to write
|
||||
// too much data. Hence the validation should fail.
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dfu_state = DFU_STATE_VALIDATE;
|
||||
|
||||
// Valid peer activity detected. Hence restart the DFU timer.
|
||||
err_code = dfu_timer_restart();
|
||||
if (err_code == NRF_SUCCESS)
|
||||
{
|
||||
err_code = dfu_init_postvalidate((uint8_t *)mp_storage_handle_active->block_id,
|
||||
m_image_size);
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
m_dfu_state = DFU_STATE_WAIT_4_ACTIVATE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_image_activate()
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
switch (m_dfu_state)
|
||||
{
|
||||
case DFU_STATE_WAIT_4_ACTIVATE:
|
||||
|
||||
// Stop the DFU Timer because the peer activity need not be monitored any longer.
|
||||
err_code = app_timer_stop(m_dfu_timer_id);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = m_functions.activate();
|
||||
break;
|
||||
|
||||
default:
|
||||
err_code = NRF_ERROR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
void dfu_reset(void)
|
||||
{
|
||||
dfu_update_status_t update_status;
|
||||
|
||||
update_status.status_code = DFU_RESET;
|
||||
|
||||
bootloader_dfu_update_process(update_status);
|
||||
}
|
||||
|
||||
|
||||
static uint32_t dfu_compare_block(uint32_t * ptr1, uint32_t * ptr2, uint32_t len)
|
||||
{
|
||||
sd_mbr_command_t sd_mbr_cmd;
|
||||
|
||||
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
|
||||
sd_mbr_cmd.params.compare.ptr1 = ptr1;
|
||||
sd_mbr_cmd.params.compare.ptr2 = ptr2;
|
||||
sd_mbr_cmd.params.compare.len = len / sizeof(uint32_t);
|
||||
|
||||
return sd_mbr_command(&sd_mbr_cmd);
|
||||
}
|
||||
|
||||
|
||||
static uint32_t dfu_copy_sd(uint32_t * src, uint32_t * dst, uint32_t len)
|
||||
{
|
||||
sd_mbr_command_t sd_mbr_cmd;
|
||||
|
||||
sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_SD;
|
||||
sd_mbr_cmd.params.copy_sd.src = src;
|
||||
sd_mbr_cmd.params.copy_sd.dst = dst;
|
||||
sd_mbr_cmd.params.copy_sd.len = len / sizeof(uint32_t);
|
||||
|
||||
return sd_mbr_command(&sd_mbr_cmd);
|
||||
}
|
||||
|
||||
|
||||
static uint32_t dfu_sd_img_block_swap(uint32_t * src,
|
||||
uint32_t * dst,
|
||||
uint32_t len,
|
||||
uint32_t block_size)
|
||||
{
|
||||
// It is neccesarry to swap the new SoftDevice in 3 rounds to ensure correct copy of data
|
||||
// and verifucation of data in case power reset occurs during write to flash.
|
||||
// To ensure the robustness of swapping the images are compared backwards till start of
|
||||
// image swap. If the back is identical everything is swapped.
|
||||
uint32_t err_code = dfu_compare_block(src, dst, len);
|
||||
if (err_code == NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
if ((uint32_t)dst > SOFTDEVICE_REGION_START)
|
||||
{
|
||||
err_code = dfu_sd_img_block_swap((uint32_t *)((uint32_t)src - block_size),
|
||||
(uint32_t *)((uint32_t)dst - block_size),
|
||||
block_size,
|
||||
block_size);
|
||||
VERIFY_SUCCESS(err_code);
|
||||
}
|
||||
|
||||
err_code = dfu_copy_sd(src, dst, len);
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
return dfu_compare_block(src, dst, len);
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_sd_image_swap(void)
|
||||
{
|
||||
bootloader_settings_t boot_settings;
|
||||
|
||||
bootloader_settings_get(&boot_settings);
|
||||
|
||||
if (boot_settings.sd_image_size == 0)
|
||||
{
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
if ((SOFTDEVICE_REGION_START + boot_settings.sd_image_size) > boot_settings.sd_image_start)
|
||||
{
|
||||
uint32_t err_code;
|
||||
uint32_t sd_start = SOFTDEVICE_REGION_START;
|
||||
uint32_t block_size = (boot_settings.sd_image_start - sd_start) / 2;
|
||||
|
||||
/* ##### FIX START ##### */
|
||||
block_size &= ~(uint32_t)(CODE_PAGE_SIZE - 1);
|
||||
/* ##### FIX END ##### */
|
||||
|
||||
uint32_t image_end = boot_settings.sd_image_start + boot_settings.sd_image_size;
|
||||
|
||||
uint32_t img_block_start = boot_settings.sd_image_start + 2 * block_size;
|
||||
uint32_t sd_block_start = sd_start + 2 * block_size;
|
||||
|
||||
if (SD_SIZE_GET(MBR_SIZE) < boot_settings.sd_image_size)
|
||||
{
|
||||
// This will clear a page thus ensuring the old image is invalidated before swapping.
|
||||
err_code = dfu_copy_sd((uint32_t *)(sd_start + block_size),
|
||||
(uint32_t *)(sd_start + block_size),
|
||||
sizeof(uint32_t));
|
||||
VERIFY_SUCCESS(err_code);
|
||||
|
||||
err_code = dfu_copy_sd((uint32_t *)sd_start, (uint32_t *)sd_start, sizeof(uint32_t));
|
||||
VERIFY_SUCCESS(err_code);
|
||||
}
|
||||
|
||||
return dfu_sd_img_block_swap((uint32_t *)img_block_start,
|
||||
(uint32_t *)sd_block_start,
|
||||
image_end - img_block_start,
|
||||
block_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (boot_settings.sd_image_size != 0)
|
||||
{
|
||||
return dfu_copy_sd((uint32_t *)boot_settings.sd_image_start,
|
||||
(uint32_t *)SOFTDEVICE_REGION_START,
|
||||
boot_settings.sd_image_size);
|
||||
}
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_bl_image_swap(void)
|
||||
{
|
||||
bootloader_settings_t bootloader_settings;
|
||||
sd_mbr_command_t sd_mbr_cmd;
|
||||
|
||||
bootloader_settings_get(&bootloader_settings);
|
||||
|
||||
if (bootloader_settings.bl_image_size != 0)
|
||||
{
|
||||
uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ?
|
||||
DFU_BANK_0_REGION_START :
|
||||
bootloader_settings.sd_image_start +
|
||||
bootloader_settings.sd_image_size;
|
||||
|
||||
sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_BL;
|
||||
sd_mbr_cmd.params.copy_bl.bl_src = (uint32_t *)(bl_image_start);
|
||||
sd_mbr_cmd.params.copy_bl.bl_len = bootloader_settings.bl_image_size / sizeof(uint32_t);
|
||||
|
||||
return sd_mbr_command(&sd_mbr_cmd);
|
||||
}
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_bl_image_validate(void)
|
||||
{
|
||||
bootloader_settings_t bootloader_settings;
|
||||
sd_mbr_command_t sd_mbr_cmd;
|
||||
|
||||
bootloader_settings_get(&bootloader_settings);
|
||||
|
||||
if (bootloader_settings.bl_image_size != 0)
|
||||
{
|
||||
uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ?
|
||||
DFU_BANK_0_REGION_START :
|
||||
bootloader_settings.sd_image_start +
|
||||
bootloader_settings.sd_image_size;
|
||||
|
||||
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
|
||||
sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START;
|
||||
sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)(bl_image_start);
|
||||
sd_mbr_cmd.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t);
|
||||
|
||||
return sd_mbr_command(&sd_mbr_cmd);
|
||||
}
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_sd_image_validate(void)
|
||||
{
|
||||
bootloader_settings_t bootloader_settings;
|
||||
sd_mbr_command_t sd_mbr_cmd;
|
||||
|
||||
bootloader_settings_get(&bootloader_settings);
|
||||
|
||||
if (bootloader_settings.sd_image_size == 0)
|
||||
{
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
if ((SOFTDEVICE_REGION_START + bootloader_settings.sd_image_size) > bootloader_settings.sd_image_start)
|
||||
{
|
||||
uint32_t sd_start = SOFTDEVICE_REGION_START;
|
||||
uint32_t block_size = (bootloader_settings.sd_image_start - sd_start) / 2;
|
||||
uint32_t image_end = bootloader_settings.sd_image_start +
|
||||
bootloader_settings.sd_image_size;
|
||||
|
||||
uint32_t img_block_start = bootloader_settings.sd_image_start + 2 * block_size;
|
||||
uint32_t sd_block_start = sd_start + 2 * block_size;
|
||||
|
||||
if (SD_SIZE_GET(MBR_SIZE) < bootloader_settings.sd_image_size)
|
||||
{
|
||||
return NRF_ERROR_NULL;
|
||||
}
|
||||
|
||||
return dfu_sd_img_block_swap((uint32_t *)img_block_start,
|
||||
(uint32_t *)sd_block_start,
|
||||
image_end - img_block_start,
|
||||
block_size);
|
||||
}
|
||||
|
||||
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
|
||||
sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)SOFTDEVICE_REGION_START;
|
||||
sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)bootloader_settings.sd_image_start;
|
||||
sd_mbr_cmd.params.compare.len = bootloader_settings.sd_image_size / sizeof(uint32_t);
|
||||
|
||||
return sd_mbr_command(&sd_mbr_cmd);
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/* 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_dfu_transport DFU transport API.
|
||||
* @{
|
||||
*
|
||||
* @brief DFU transport module interface.
|
||||
*/
|
||||
|
||||
#ifndef DFU_TRANSPORT_H__
|
||||
#define DFU_TRANSPORT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**@brief Function for starting the update of Device Firmware.
|
||||
*
|
||||
* @retval NRF_SUCCESS Operation success.
|
||||
*/
|
||||
uint32_t dfu_transport_serial_update_start(void);
|
||||
|
||||
/**@brief Function for closing the transport layer.
|
||||
*
|
||||
* @retval NRF_SUCCESS Operation success.
|
||||
*/
|
||||
uint32_t dfu_transport_serial_close(void);
|
||||
|
||||
|
||||
uint32_t dfu_transport_ble_update_start(void);
|
||||
uint32_t dfu_transport_ble_close();
|
||||
|
||||
#endif // DFU_TRANSPORT_H__
|
||||
|
||||
/**@} */
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,314 @@
|
||||
/* 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 "dfu_transport.h"
|
||||
#include <stddef.h>
|
||||
#include "dfu.h"
|
||||
#include <dfu_types.h>
|
||||
#include "app_error.h"
|
||||
#include "app_util.h"
|
||||
#include "hci_transport.h"
|
||||
#include "app_timer.h"
|
||||
#include "app_scheduler.h"
|
||||
|
||||
#define MAX_BUFFERS 4u /**< Maximum number of buffers that can be received queued without being consumed. */
|
||||
|
||||
/**
|
||||
* defgroup Data Packet Queue Access Operation Macros
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Provides status showing if the queue is full or not. */
|
||||
#define DATA_QUEUE_FULL() \
|
||||
(((MAX_BUFFERS -1) == m_data_queue.count) ? true : false)
|
||||
|
||||
/** Provides status showing if the queue is empty or not */
|
||||
#define DATA_QUEUE_EMPTY() \
|
||||
((0 == m_data_queue.count) ? true : false)
|
||||
|
||||
/** Initializes an element of the data queue. */
|
||||
#define DATA_QUEUE_ELEMENT_INIT(i) \
|
||||
m_data_queue.data_packet[(i)].packet_type = INVALID_PACKET
|
||||
|
||||
/** Sets the packet type of an element of the data queue. */
|
||||
#define DATA_QUEUE_ELEMENT_SET_PTYPE(i, t) \
|
||||
m_data_queue.data_packet[(i)].packet_type = (t)
|
||||
|
||||
/** Copies a data packet pointer of an element of the data queue. */
|
||||
#define DATA_QUEUE_ELEMENT_COPY_PDATA(i, dp) \
|
||||
m_data_queue.data_packet[(i)].params.data_packet.p_data_packet = (uint32_t *)(dp)
|
||||
|
||||
/** Sets the packet length of an element in the data queue. */
|
||||
#define DATA_QUEUE_ELEMENT_SET_PLEN(i, l) \
|
||||
m_data_queue.data_packet[(i)].params.data_packet.packet_length = (l)
|
||||
|
||||
/** Gets a data packet pointer of an element in the data queue. */
|
||||
#define DATA_QUEUE_ELEMENT_GET_PDATA(i) \
|
||||
(m_data_queue.data_packet[(i)].params.data_packet.p_data_packet)
|
||||
|
||||
/** Gets the packet type of an element in the data queue. */
|
||||
#define DATA_QUEUE_ELEMENT_GET_PTYPE(i) \
|
||||
m_data_queue.data_packet[(i)].packet_type
|
||||
|
||||
/* @} */
|
||||
|
||||
/** Abstracts data packet queue */
|
||||
typedef struct
|
||||
{
|
||||
dfu_update_packet_t data_packet[MAX_BUFFERS]; /**< Bootloader data packets used when processing data from the UART. */
|
||||
volatile uint8_t count; /**< Counter to maintain number of elements in the queue. */
|
||||
} dfu_data_queue_t;
|
||||
|
||||
static dfu_data_queue_t m_data_queue; /**< Received-data packet queue. */
|
||||
|
||||
/**@brief Initializes an element of the data buffer queue.
|
||||
*
|
||||
* @param[in] element_index index of the element.
|
||||
*/
|
||||
static void data_queue_element_init (uint8_t element_index)
|
||||
{
|
||||
DATA_QUEUE_ELEMENT_INIT(element_index);
|
||||
DATA_QUEUE_ELEMENT_SET_PTYPE(element_index, INVALID_PACKET);
|
||||
DATA_QUEUE_ELEMENT_COPY_PDATA(element_index, NULL);
|
||||
DATA_QUEUE_ELEMENT_SET_PLEN(element_index, 0);
|
||||
}
|
||||
|
||||
/** Initializes data buffer queue */
|
||||
static void data_queue_init(void)
|
||||
{
|
||||
uint32_t index;
|
||||
|
||||
m_data_queue.count = 0;
|
||||
|
||||
for (index = 0; index < MAX_BUFFERS; index++)
|
||||
{
|
||||
data_queue_element_init(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**@brief Function for freeing an element.
|
||||
*
|
||||
* @param[in] element_index index of the element.
|
||||
*/
|
||||
static uint32_t data_queue_element_free(uint8_t element_index)
|
||||
{
|
||||
uint8_t * p_data;
|
||||
uint32_t retval;
|
||||
|
||||
retval = NRF_ERROR_INVALID_PARAM;
|
||||
|
||||
if (MAX_BUFFERS > element_index)
|
||||
{
|
||||
p_data = (uint8_t *)DATA_QUEUE_ELEMENT_GET_PDATA(element_index);
|
||||
if (INVALID_PACKET != DATA_QUEUE_ELEMENT_GET_PTYPE(element_index))
|
||||
{
|
||||
m_data_queue.count--;
|
||||
data_queue_element_init (element_index);
|
||||
retval = hci_transport_rx_pkt_consume((p_data - 4));
|
||||
APP_ERROR_CHECK(retval);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NRF_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
/**@brief Function for Allocating element.
|
||||
*
|
||||
* @param[in] packet_type packet type.
|
||||
* @param[out] p_element_index index of the element.
|
||||
*/
|
||||
static uint32_t data_queue_element_alloc(uint8_t * p_element_index, uint8_t packet_type)
|
||||
{
|
||||
uint32_t retval;
|
||||
uint32_t index;
|
||||
|
||||
retval = NRF_ERROR_NO_MEM;
|
||||
|
||||
if (INVALID_PACKET == packet_type)
|
||||
{
|
||||
retval = NRF_ERROR_INVALID_PARAM;
|
||||
}
|
||||
else if (true == DATA_QUEUE_FULL())
|
||||
{
|
||||
retval = NRF_ERROR_NO_MEM;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (index = 0; index < MAX_BUFFERS; index++)
|
||||
{
|
||||
if (INVALID_PACKET == DATA_QUEUE_ELEMENT_GET_PTYPE(index))
|
||||
{
|
||||
// Found a free element: allocate, and end search.
|
||||
*p_element_index = index;
|
||||
DATA_QUEUE_ELEMENT_SET_PTYPE(index, packet_type);
|
||||
retval = NRF_SUCCESS;
|
||||
m_data_queue.count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Flush everything on disconnect or stop.
|
||||
static void data_queue_flush(void)
|
||||
{
|
||||
uint32_t index;
|
||||
|
||||
for (index = 0; index < MAX_BUFFERS; index++)
|
||||
{
|
||||
// In this case it does not matter if free succeeded or not as data packets are being flushed because DFU Trnsport was closed
|
||||
(void)data_queue_element_free(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling the callback events from the dfu module.
|
||||
* Callbacks are expected when \ref dfu_data_pkt_handle has been executed.
|
||||
*
|
||||
* @param[in] packet Packet type for which this callback is related. START_PACKET, DATA_PACKET.
|
||||
* @param[in] result Operation result code. NRF_SUCCESS when a queued operation was successful.
|
||||
* @param[in] p_data Pointer to the data to which the operation is related.
|
||||
*/
|
||||
static void dfu_cb_handler(uint32_t packet, uint32_t result, uint8_t * p_data)
|
||||
{
|
||||
APP_ERROR_CHECK(result);
|
||||
}
|
||||
|
||||
|
||||
static void process_dfu_packet(void * p_event_data, uint16_t event_size)
|
||||
{
|
||||
uint32_t retval;
|
||||
uint32_t index;
|
||||
dfu_update_packet_t * packet;
|
||||
|
||||
// Adafruit modification for startup dfu
|
||||
extern bool forced_startup_dfu_packet_received;
|
||||
forced_startup_dfu_packet_received = true;
|
||||
|
||||
while (false == DATA_QUEUE_EMPTY())
|
||||
{
|
||||
// Fetch the element to be processed.
|
||||
for (index = 0; index < MAX_BUFFERS ; index++)
|
||||
{
|
||||
packet = &m_data_queue.data_packet[index];
|
||||
if (INVALID_PACKET != packet->packet_type)
|
||||
{
|
||||
extern void blinky_fast_set(bool isFast);
|
||||
switch (DATA_QUEUE_ELEMENT_GET_PTYPE(index))
|
||||
{
|
||||
case DATA_PACKET:
|
||||
(void)dfu_data_pkt_handle(packet);
|
||||
break;
|
||||
|
||||
case START_PACKET:
|
||||
packet->params.start_packet =
|
||||
(dfu_start_packet_t*)packet->params.data_packet.p_data_packet;
|
||||
retval = dfu_start_pkt_handle(packet);
|
||||
APP_ERROR_CHECK(retval);
|
||||
break;
|
||||
|
||||
case INIT_PACKET:
|
||||
(void)dfu_init_pkt_handle(packet);
|
||||
retval = dfu_init_pkt_complete();
|
||||
APP_ERROR_CHECK(retval);
|
||||
|
||||
blinky_fast_set(true);
|
||||
break;
|
||||
|
||||
case STOP_DATA_PACKET:
|
||||
(void)dfu_image_validate();
|
||||
(void)dfu_image_activate();
|
||||
|
||||
blinky_fast_set(false);
|
||||
|
||||
// Break the loop by returning.
|
||||
return;
|
||||
|
||||
default:
|
||||
// No implementation needed.
|
||||
break;
|
||||
}
|
||||
|
||||
// Free the processed element.
|
||||
retval = data_queue_element_free(index);
|
||||
APP_ERROR_CHECK(retval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rpc_transport_event_handler(hci_transport_evt_t event)
|
||||
{
|
||||
uint32_t retval;
|
||||
uint16_t rpc_cmd_length_read = 0;
|
||||
uint8_t * p_rpc_cmd_buffer = NULL;
|
||||
uint8_t element_index;
|
||||
|
||||
retval = hci_transport_rx_pkt_extract(&p_rpc_cmd_buffer, &rpc_cmd_length_read);
|
||||
if (NRF_SUCCESS == retval)
|
||||
{
|
||||
// Verify if the data queue can buffer the packet.
|
||||
retval = data_queue_element_alloc(&element_index, p_rpc_cmd_buffer[0]);
|
||||
if (NRF_SUCCESS == retval)
|
||||
{
|
||||
//subtract 1 since we are interested in payload length and not the type field.
|
||||
DATA_QUEUE_ELEMENT_SET_PLEN(element_index,(rpc_cmd_length_read / sizeof(uint32_t)) - 1);
|
||||
DATA_QUEUE_ELEMENT_COPY_PDATA(element_index, &p_rpc_cmd_buffer[4]);
|
||||
retval = app_sched_event_put(NULL, 0, process_dfu_packet);
|
||||
}
|
||||
}
|
||||
|
||||
if (p_rpc_cmd_buffer != NULL && NRF_SUCCESS != retval)
|
||||
{
|
||||
// Free the packet that could not be processed.
|
||||
retval = hci_transport_rx_pkt_consume(p_rpc_cmd_buffer);
|
||||
APP_ERROR_CHECK(retval);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_transport_serial_update_start(void)
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
// Initialize data buffer queue.
|
||||
data_queue_init();
|
||||
|
||||
dfu_register_callback(dfu_cb_handler);
|
||||
|
||||
// Open transport layer.
|
||||
err_code = hci_transport_open();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
// Register callback to be run when commands have been received by the transport layer.
|
||||
err_code = hci_transport_evt_handler_reg(rpc_transport_event_handler);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint32_t dfu_transport_serial_close(void)
|
||||
{
|
||||
// Remove all buffered packets.
|
||||
data_queue_flush();
|
||||
|
||||
return hci_transport_close();
|
||||
}
|
||||
|
@ -0,0 +1,168 @@
|
||||
/* 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_dfu_types Types and definitions.
|
||||
* @{
|
||||
*
|
||||
* @ingroup nrf_dfu
|
||||
*
|
||||
* @brief Device Firmware Update module type and definitions.
|
||||
*/
|
||||
|
||||
#ifndef DFU_TYPES_H__
|
||||
#define DFU_TYPES_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "nrf_sdm.h"
|
||||
#include "nrf_mbr.h"
|
||||
#include "nrf.h"
|
||||
#include "app_util.h"
|
||||
|
||||
#define NRF_UICR_BOOT_START_ADDRESS (NRF_UICR_BASE + 0x14) /**< Register where the bootloader start address is stored in the UICR register. */
|
||||
|
||||
#if defined(NRF52)
|
||||
#define NRF_UICR_MBR_PARAMS_PAGE_ADDRESS (NRF_UICR_BASE + 0x18) /**< Register where the mbr params page is stored in the UICR register. (Only in use in nRF52 MBR).*/
|
||||
#endif
|
||||
|
||||
#define CODE_REGION_1_START SD_SIZE_GET(MBR_SIZE) /**< This field should correspond to the size of Code Region 0, (which is identical to Start of Code Region 1), found in UICR.CLEN0 register. This value is used for compile safety, as the linker will fail if application expands into bootloader. Runtime, the bootloader will use the value found in UICR.CLEN0. */
|
||||
#define SOFTDEVICE_REGION_START MBR_SIZE /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
|
||||
|
||||
#ifdef NRF51
|
||||
|
||||
#define CODE_PAGE_SIZE 0x0400 /**< Size of a flash codepage. Used for size of the reserved flash space in the bootloader region. Will be runtime checked against NRF_UICR->CODEPAGESIZE to ensure the region is correct. */
|
||||
|
||||
#ifdef SIGNING
|
||||
|
||||
#define BOOTLOADER_REGION_START 0x00039C00 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
|
||||
#define BOOTLOADER_SETTINGS_ADDRESS 0x0003D800 /**< The field specifies the page location of the bootloader settings address. */
|
||||
|
||||
#else
|
||||
|
||||
#define BOOTLOADER_REGION_START 0x0003C000 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
|
||||
#define BOOTLOADER_SETTINGS_ADDRESS 0x0003FC00 /**< The field specifies the page location of the bootloader settings address. */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#elif defined(NRF52)
|
||||
|
||||
#define BOOTLOADER_REGION_START 0x00074000 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
|
||||
#define BOOTLOADER_SETTINGS_ADDRESS 0x0007F000 /**< The field specifies the page location of the bootloader settings address. */
|
||||
#define BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS 0x0007E000 /**< The field specifies the page location of the mbr params page address. */
|
||||
|
||||
#define CODE_PAGE_SIZE 0x1000 /**< Size of a flash codepage. Used for size of the reserved flash space in the bootloader region. Will be runtime checked against NRF_UICR->CODEPAGESIZE to ensure the region is correct. */
|
||||
|
||||
#else
|
||||
|
||||
#error No target defined
|
||||
|
||||
#endif
|
||||
|
||||
#define DFU_REGION_TOTAL_SIZE (BOOTLOADER_REGION_START - CODE_REGION_1_START) /**< Total size of the region between SD and Bootloader. */
|
||||
|
||||
#ifndef DFU_APP_DATA_RESERVED
|
||||
#define DFU_APP_DATA_RESERVED CODE_PAGE_SIZE * 7 /**< Size of Application Data that must be preserved between application updates. This value must be a multiple of page size. Page size is 0x400 (1024d) bytes, thus this value must be 0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, etc. */
|
||||
#endif
|
||||
|
||||
#define DFU_IMAGE_MAX_SIZE_FULL (DFU_REGION_TOTAL_SIZE - DFU_APP_DATA_RESERVED) /**< Maximum size of an application, excluding save data from the application. */
|
||||
|
||||
#define DFU_IMAGE_MAX_SIZE_BANKED (((DFU_IMAGE_MAX_SIZE_FULL) - \
|
||||
(DFU_IMAGE_MAX_SIZE_FULL % (2 * CODE_PAGE_SIZE)))/2) /**< Maximum size of an application, excluding save data from the application. */
|
||||
|
||||
#define DFU_BL_IMAGE_MAX_SIZE (BOOTLOADER_SETTINGS_ADDRESS - BOOTLOADER_REGION_START) /**< Maximum size of a bootloader, excluding save data from the current bootloader. */
|
||||
|
||||
#define DFU_BANK_0_REGION_START CODE_REGION_1_START /**< Bank 0 region start. */
|
||||
#define DFU_BANK_1_REGION_START (DFU_BANK_0_REGION_START + DFU_IMAGE_MAX_SIZE_BANKED) /**< Bank 1 region start. */
|
||||
|
||||
#define EMPTY_FLASH_MASK 0xFFFFFFFF /**< Bit mask that defines an empty address in flash. */
|
||||
|
||||
#define INVALID_PACKET 0x00 /**< Invalid packet identifies. */
|
||||
#define INIT_PACKET 0x01 /**< Packet identifies for initialization packet. */
|
||||
#define STOP_INIT_PACKET 0x02 /**< Packet identifies for stop initialization packet. Used when complete init packet has been received so that the init packet can be used for pre validaiton. */
|
||||
#define START_PACKET 0x03 /**< Packet identifies for the Data Start Packet. */
|
||||
#define DATA_PACKET 0x04 /**< Packet identifies for a Data Packet. */
|
||||
#define STOP_DATA_PACKET 0x05 /**< Packet identifies for the Data Stop Packet. */
|
||||
|
||||
#define DFU_UPDATE_SD 0x01 /**< Bit field indicating update of SoftDevice is ongoing. */
|
||||
#define DFU_UPDATE_BL 0x02 /**< Bit field indicating update of bootloader is ongoing. */
|
||||
#define DFU_UPDATE_APP 0x04 /**< Bit field indicating update of application is ongoing. */
|
||||
|
||||
#define DFU_INIT_RX 0x00 /**< Op Code identifies for receiving init packet. */
|
||||
#define DFU_INIT_COMPLETE 0x01 /**< Op Code identifies for transmission complete of init packet. */
|
||||
|
||||
// Safe guard to ensure during compile time that the DFU_APP_DATA_RESERVED is a multiple of page size.
|
||||
STATIC_ASSERT((((DFU_APP_DATA_RESERVED) & (CODE_PAGE_SIZE - 1)) == 0x00));
|
||||
|
||||
/**@brief Structure holding a start packet containing update mode and image sizes.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t dfu_update_mode; /**< Packet type, used to identify the content of the received packet referenced by data packet. */
|
||||
uint32_t sd_image_size; /**< Size of the SoftDevice image to be transferred. Zero if no SoftDevice image will be transfered. */
|
||||
uint32_t bl_image_size; /**< Size of the Bootloader image to be transferred. Zero if no Bootloader image will be transfered. */
|
||||
uint32_t app_image_size; /**< Size of the application image to be transmitted. Zero if no Bootloader image will be transfered. */
|
||||
} dfu_start_packet_t;
|
||||
|
||||
/**@brief Structure holding a bootloader init/data packet received.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint32_t packet_length; /**< Packet length of the data packet. Each data is word size, meaning length of 4 is 4 words, not bytes. */
|
||||
uint32_t * p_data_packet; /**< Data Packet received. Each data is a word size entry. */
|
||||
} dfu_data_packet_t;
|
||||
|
||||
/**@brief Structure for holding dfu update packet. Packet type indicate the type of packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint32_t packet_type; /**< Packet type, used to identify the content of the received packet referenced by data packet. */
|
||||
union
|
||||
{
|
||||
dfu_data_packet_t data_packet; /**< Used when packet type is INIT_PACKET or DATA_PACKET. Packet contains data received for init or data. */
|
||||
dfu_start_packet_t * start_packet; /**< Used when packet type is START_DATA_PACKET. Will contain information on software to be updtaed, i.e. SoftDevice, Bootloader and/or Application along with image sizes. */
|
||||
} params;
|
||||
} dfu_update_packet_t;
|
||||
|
||||
/**@brief DFU status error codes.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
DFU_UPDATE_APP_COMPLETE, /**< Status update of application complete.*/
|
||||
DFU_UPDATE_SD_COMPLETE, /**< Status update of SoftDevice update complete. Note that this solely indicates that a new SoftDevice has been received and stored in bank 0 and 1. */
|
||||
DFU_UPDATE_SD_SWAPPED, /**< Status update of SoftDevice update complete. Note that this solely indicates that a new SoftDevice has been received and stored in bank 0 and 1. */
|
||||
DFU_UPDATE_BOOT_COMPLETE, /**< Status update complete.*/
|
||||
DFU_BANK_0_ERASED, /**< Status bank 0 erased.*/
|
||||
DFU_TIMEOUT, /**< Status timeout.*/
|
||||
DFU_RESET /**< Status Reset to indicate current update procedure has been aborted and system should reset. */
|
||||
} dfu_update_status_code_t;
|
||||
|
||||
/**@brief Structure holding DFU complete event.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
dfu_update_status_code_t status_code; /**< Device Firmware Update status. */
|
||||
uint16_t app_crc; /**< CRC of the recieved application. */
|
||||
uint32_t sd_size; /**< Size of the recieved SoftDevice. */
|
||||
uint32_t bl_size; /**< Size of the recieved BootLoader. */
|
||||
uint32_t app_size; /**< Size of the recieved Application. */
|
||||
uint32_t sd_image_start; /**< Location in flash where the received SoftDevice image is stored. */
|
||||
} dfu_update_status_t;
|
||||
|
||||
/**@brief Update complete handler type. */
|
||||
typedef void (*dfu_complete_handler_t)(dfu_update_status_t dfu_update_status);
|
||||
|
||||
#endif // DFU_TYPES_H__
|
||||
|
||||
/**@} */
|
@ -0,0 +1,32 @@
|
||||
/* 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 memory_pool_internal Memory Pool Internal
|
||||
* @{
|
||||
* @ingroup memory_pool
|
||||
*
|
||||
* @brief Memory pool internal definitions
|
||||
*/
|
||||
|
||||
#ifndef MEM_POOL_INTERNAL_H__
|
||||
#define MEM_POOL_INTERNAL_H__
|
||||
|
||||
#define TX_BUF_SIZE 32u /**< TX buffer size in bytes. */
|
||||
#define RX_BUF_SIZE 600u /**< RX buffer size in bytes. */
|
||||
|
||||
#define RX_BUF_QUEUE_SIZE 2u /**< RX buffer element size. */
|
||||
|
||||
#endif // MEM_POOL_INTERNAL_H__
|
||||
|
||||
/** @} */
|
@ -0,0 +1,45 @@
|
||||
/* 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_bootloader_hci_congfig HCI Transport Layer Configuration
|
||||
* @{
|
||||
* @ingroup dfu_bootloader_api
|
||||
* @brief Definition of HCI Transport Layer configurable parameters
|
||||
*/
|
||||
|
||||
#ifndef HCI_TRANSPORT_CONFIG_H__
|
||||
#define HCI_TRANSPORT_CONFIG_H__
|
||||
|
||||
#include "boards.h" /**< Default include for boards.h which means that default pin numbers will be used for RX, TX, CTS, and RTS on the UART. Other pin number can be used if desired. */
|
||||
|
||||
/** This section covers configurable parameters for the HCI Transport SLIP layer. */
|
||||
#define HCI_SLIP_UART_RX_PIN_NUMBER RX_PIN_NUMBER /**< Defines the UART RX pin number. The default pin for the board is chosen, but can be overwritten. */
|
||||
|
||||
#define HCI_SLIP_UART_TX_PIN_NUMBER TX_PIN_NUMBER /**< Defines the UART TX pin number. The default pin for the board is chosen, but can be overwritten. */
|
||||
|
||||
#define HCI_SLIP_UART_RTS_PIN_NUMBER RTS_PIN_NUMBER /**< Defines the UART RTS pin number. The default pin for the board is chosen, but can be overwritten. */
|
||||
|
||||
#define HCI_SLIP_UART_CTS_PIN_NUMBER CTS_PIN_NUMBER /**< Defines the UART CTS pin number. The default pin for the board is chosen, but can be overwritten. */
|
||||
|
||||
#define HCI_SLIP_UART_MODE APP_UART_FLOW_CONTROL_DISABLED /**< Defines the UART mode to be used. Use UART Low Power with Flow Control - Valid values are defined in \ref app_uart_flow_control_t. For further information on the UART Low Power mode, please refer to: \ref app_uart . */
|
||||
|
||||
#define HCI_SLIP_UART_BAUDRATE UART_BAUDRATE_BAUDRATE_Baud115200 // UART_BAUDRATE_BAUDRATE_Baud38400 /**< Defines the UART Baud rate. Default is 38400 baud. */
|
||||
|
||||
/** This section covers configurable parameters for the HCI Transport layer that are used for calculating correct value for the retransmission timer timeout. */
|
||||
#define MAX_PACKET_SIZE_IN_BITS 8000u /**< Maximum size of a single application packet in bits. */
|
||||
#define USED_BAUD_RATE 115200u /**< The used uart baudrate. */
|
||||
|
||||
#endif // HCI_TRANSPORT_CONFIG_H__
|
||||
|
||||
/** @} */
|
Reference in New Issue
Block a user