2018-12-11 16:43:40 +00:00
/**
* \ file
2019-01-09 17:01:13 +00:00
* \ brief USB DFU bootloader implementation ( DFU mode )
2018-12-11 16:43:40 +00:00
*
2019-01-09 17:01:13 +00:00
* Copyright ( c ) 2018 - 2019 sysmocom - s . f . m . c . GmbH , Author : Kevin Redon < kredon @ sysmocom . de >
2018-12-11 16:43:40 +00:00
*
2019-01-09 17:01:13 +00:00
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
2018-12-11 16:43:40 +00:00
*
2019-01-09 17:01:13 +00:00
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
2018-12-11 16:43:40 +00:00
*
2019-01-09 17:01:13 +00:00
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2018-12-11 16:43:40 +00:00
*/
2020-01-27 13:20:44 +00:00
# include <errno.h>
2018-12-11 16:43:40 +00:00
# include "atmel_start.h"
# include "atmel_start_pins.h"
2019-01-09 17:01:13 +00:00
/** Start address of the application in flash
* \ remark must be initialized by check_bootloader
*/
static uint32_t * application_start_address ;
2019-02-14 17:35:33 +00:00
/** Location of the DFU magic value to force starting DFU */
2019-02-14 23:59:18 +00:00
static volatile uint32_t * dfu_magic = ( uint32_t * ) HSRAM_ADDR ; // magic value should be written at start of RAM
2019-02-14 17:35:33 +00:00
2019-01-09 17:01:13 +00:00
/** Check if the bootloader is valid
* \ return true if the bootloader is valid and can be run
* \ remark initializes application_start_address
*/
static bool check_bootloader ( void )
{
if ( hri_nvmctrl_read_STATUS_BOOTPROT_bf ( FLASH_0 . dev . hw ) > 15 ) { // ensure BOOTPROT setting is valid
return false ;
}
application_start_address = ( uint32_t * ) ( ( 15 - hri_nvmctrl_read_STATUS_BOOTPROT_bf ( FLASH_0 . dev . hw ) ) * 8192 ) ; // calculate bootloader size to know start address of the application (e.g. after the bootloader)
if ( 0 = = application_start_address ) { // no space has been reserved for the bootloader
return false ;
}
return true ;
}
/** Check if starting the bootloader is forced
* \ return true of the DFU bootloader should be started
*/
static bool check_force_dfu ( void )
{
2019-02-14 23:59:18 +00:00
if ( 0x44465521 = = * dfu_magic ) { // check for the magic value which can be set by the main application
* dfu_magic = 0 ; // erase value so we don't stay in the DFU bootloader upon reset
2019-02-14 17:35:33 +00:00
return true ;
}
if ( 0 = = gpio_get_pin_level ( BUTTON_FORCE_DFU ) ) { // signal is low when button is pressed
return true ;
}
return false ;
2019-01-09 17:01:13 +00:00
}
/** Check if the application is valid
* \ return true if the application is valid and can be started
* \ warning application_start_address must be initialized
*/
static bool check_application ( void )
{
/* the application starts with the vector table
* the first entry in the vector table is the initial stack pointer ( SP ) address
* the stack will be placed in RAM which begins at 0x2000 0000 , and there is up to 256 KB of RAM ( 0x40000 ) .
* if the SP is not in this range ( e . g . flash has been erased ) there is no valid application
* the second entry in the vector table is the reset address , corresponding to the application start
*/
2019-02-15 09:10:38 +00:00
return ( HSRAM_ADDR = = ( ( * application_start_address ) & 0xFFF80000 ) ) ;
2019-01-09 17:01:13 +00:00
}
/** Start the application
* \ warning application_start_address must be initialized
*/
static void start_application ( void )
{
__set_MSP ( * application_start_address ) ; // re-base the Stack Pointer
SCB - > VTOR = ( ( uint32_t ) application_start_address & SCB_VTOR_TBLOFF_Msk ) ; // re-base the vector table base address
asm ( " bx %0 " : : " r " ( * ( application_start_address + 1 ) ) ) ; // jump to application Reset Handler in the application */
}
2020-01-27 13:20:44 +00:00
# if defined(SYSMOOCTSIM)
/* Section 9.6 of SAMD5x/E5x Family Data Sheet */
static int get_chip_unique_serial ( uint8_t * out , size_t len )
{
uint32_t * out32 = ( uint32_t * ) out ;
if ( len < 16 )
return - EINVAL ;
out32 [ 0 ] = * ( uint32_t * ) 0x008061fc ;
out32 [ 1 ] = * ( uint32_t * ) 0x00806010 ;
out32 [ 2 ] = * ( uint32_t * ) 0x00806014 ;
out32 [ 3 ] = * ( uint32_t * ) 0x00806018 ;
return 0 ;
}
/* same as get_chip_unique_serial but in hex-string format */
static int get_chip_unique_serial_str ( char * out , size_t len )
{
uint8_t buf [ 16 ] ;
int rc ;
if ( len < 16 * 2 + 1 )
return - EINVAL ;
rc = get_chip_unique_serial ( buf , sizeof ( buf ) ) ;
if ( rc < 0 )
return rc ;
for ( int i = 0 ; i < sizeof ( buf ) ; i + + )
sprintf ( & out [ i * 2 ] , " %02x " , buf [ i ] ) ;
return 0 ;
}
static int str_to_usb_desc ( char * in , uint8_t in_sz , uint8_t * out , uint8_t out_sz ) {
if ( 2 + in_sz * 2 < out_sz )
return - 1 ;
memset ( out , 0 , out_sz ) ;
out [ 0 ] = out_sz ;
out [ 1 ] = 0x3 ;
for ( int i = 2 ; i < out_sz ; i + = 2 )
out [ i ] = in [ ( i > > 1 ) - 1 ] ;
return 0 ;
}
char sernr_buf [ 16 * 2 + 1 ] ;
//unicode for descriptor
uint8_t sernr_buf_descr [ 1 + 1 + 16 * 2 * 2 ] ;
# endif
2019-01-09 17:01:13 +00:00
2018-12-11 16:43:40 +00:00
int main ( void )
{
2019-01-09 17:01:13 +00:00
atmel_start_init ( ) ; // initialise system
2020-01-27 13:20:44 +00:00
# if defined(SYSMOOCTSIM)
get_chip_unique_serial_str ( sernr_buf , sizeof ( sernr_buf ) ) ;
str_to_usb_desc ( sernr_buf , sizeof ( sernr_buf ) , sernr_buf_descr , sizeof ( sernr_buf_descr ) ) ;
# endif
2019-01-09 17:01:13 +00:00
if ( ! check_bootloader ( ) ) { // check bootloader
// blink the LED to tell the user we don't know where the application starts
while ( true ) {
gpio_set_pin_level ( LED_SYSTEM , false ) ;
delay_ms ( 500 ) ;
gpio_set_pin_level ( LED_SYSTEM , true ) ;
delay_ms ( 500 ) ;
}
}
2019-01-16 17:14:41 +00:00
if ( ! check_force_dfu ( ) & & check_application ( ) ) { // application is valid
2019-01-09 17:01:13 +00:00
start_application ( ) ; // start application
} else {
2019-02-14 17:59:32 +00:00
if ( ! check_application ( ) ) { // if the application is corrupted the start DFU start should be dfuERROR
dfu_state = USB_DFU_STATE_DFU_ERROR ;
}
2019-01-09 17:01:13 +00:00
usb_dfu ( ) ; // start DFU bootloader
}
2018-12-11 16:43:40 +00:00
}