2018-02-07 16:32:49 +00:00
/* 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 .
*
*/
2018-02-15 09:27:51 +00:00
# include "sdk_common.h"
2018-02-07 16:32:49 +00:00
# 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"
2018-02-15 09:27:51 +00:00
# include "app_timer.h"
2018-02-07 16:32:49 +00:00
2018-04-09 11:52:46 +00:00
# include "tusb.h"
2018-02-07 16:32:49 +00:00
# define APP_TIMER_PRESCALER 0
# define IRQ_ENABLED 0x01 /**< Field identifying if an interrupt is enabled. */
2018-05-25 14:33:47 +00:00
/**< Maximum number of interrupts available. */
2018-04-16 10:29:24 +00:00
# if defined(NRF52832_XXAA)
2018-02-07 16:32:49 +00:00
# define MAX_NUMBER_INTERRUPTS 39
2018-04-16 10:29:24 +00:00
# elif defined(NRF52840_XXAA)
2018-05-25 14:33:47 +00:00
# define MAX_NUMBER_INTERRUPTS 48
2018-02-07 16:32:49 +00:00
# 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.
2018-04-16 11:42:12 +00:00
// uint32_t err_code = sd_app_evt_wait();
// APP_ERROR_CHECK(err_code);
2018-02-07 16:32:49 +00:00
// Event received. Process it from the scheduler.
app_sched_execute ( ) ;
2018-04-09 11:52:46 +00:00
// USB stack
tusb_task ( ) ;
2018-06-23 06:24:21 +00:00
// Send out cdc's data
2018-07-14 17:02:44 +00:00
tud_cdc_write_flush ( ) ;
2018-06-23 06:24:21 +00:00
2018-02-07 16:32:49 +00:00
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 ) ;
2018-02-15 09:27:51 +00:00
app_timer_start ( _forced_startup_dfu_timer , APP_TIMER_TICKS ( timeout_ms ) , NULL ) ;
2018-02-07 16:32:49 +00:00
}
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 ( ) ;
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 ;
}