Permalink
Cannot retrieve contributors at this time
| /* | |
| * dfu-programmer | |
| * | |
| * $Id$ | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 2 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * This program 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 General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include <stdio.h> | |
| #include <stdint.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "dfu-bool.h" | |
| #include "config.h" | |
| #include "commands.h" | |
| #include "arguments.h" | |
| #include "intel_hex.h" | |
| #include "stm32.h" | |
| #include "atmel.h" | |
| #include "util.h" | |
| #define COMMAND_DEBUG_THRESHOLD 40 | |
| #define DEBUG(...) dfu_debug( __FILE__, __FUNCTION__, __LINE__, \ | |
| COMMAND_DEBUG_THRESHOLD, __VA_ARGS__ ) | |
| static int security_bit_state; | |
| // ________ P R O T O T Y P E S _______________________________ | |
| static int32_t execute_validate( dfu_device_t *device, | |
| intel_buffer_out_t *bout, | |
| uint8_t mem_segment, | |
| dfu_bool quiet ); | |
| /* provide an out buffer to validate and whether this is from | |
| * flash or eeprom data sections, also wether you want it quiet | |
| */ | |
| // ________ F U N C T I O N S _______________________________ | |
| static void security_check( dfu_device_t *device ) { | |
| if( ADC_AVR32 == device->type ) { | |
| // Get security bit state for AVR32. | |
| security_bit_state = atmel_getsecure( device ); | |
| DEBUG( "Security bit check returned %d.\n", security_bit_state ); | |
| } else { | |
| // Security bit not present or not testable. | |
| security_bit_state = ATMEL_SECURE_OFF; | |
| } | |
| } | |
| static void security_message( void ) { | |
| if( security_bit_state > ATMEL_SECURE_OFF ) { | |
| fprintf( stderr, "The security bit %s set.\n" | |
| "Erase the device to clear temporarily.\n", | |
| (ATMEL_SECURE_ON == security_bit_state) ? "is" : "may be" ); | |
| } | |
| } | |
| static int32_t execute_erase( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| int32_t result = SUCCESS; | |
| if( !(GRP_STM32 & args->device_type) && !args->com_erase_data.force ) { | |
| if( 0 == atmel_blank_check( device, args->flash_address_bottom, | |
| args->flash_address_top, | |
| args->quiet ) ) { | |
| if ( !args->quiet ) { | |
| fprintf( stderr, "Chip already blank, to force erase use --force.\n"); | |
| } | |
| return SUCCESS; | |
| } | |
| } | |
| DEBUG( "erase 0x%X bytes.\n", | |
| (args->flash_address_top - args->flash_address_bottom) ); | |
| if( GRP_STM32 & args->device_type ) { | |
| result = stm32_erase_flash( device, args->quiet ); | |
| } else { | |
| result = atmel_erase_flash( device, ATMEL_ERASE_ALL, args->quiet ); | |
| } | |
| if( 0 != result ) { | |
| return result; | |
| } | |
| if( !(GRP_STM32 & args->device_type) && | |
| !args->com_erase_data.suppress_validation ) { | |
| result = atmel_blank_check( device, args->flash_address_bottom, | |
| args->flash_address_top, | |
| args->quiet ); | |
| } | |
| return result; | |
| } | |
| static int32_t execute_setsecure( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| int32_t result; | |
| if( ADC_AVR32 != args->device_type ) { | |
| DEBUG( "target doesn't support security bit set.\n" ); | |
| fprintf( stderr, "Operation not supported on %s\n", | |
| args->device_type_string ); | |
| return ARGUMENT_ERROR; | |
| } | |
| result = atmel_secure( device ); | |
| if( result < 0 ) { | |
| DEBUG( "Error while setting security bit. (%d)\n", result ); | |
| fprintf( stderr, "Error setting security bit.\n" ); | |
| return UNSPECIFIED_ERROR; | |
| } | |
| return SUCCESS; | |
| } | |
| // TODO : split this into a new command (no file is needed) - also general | |
| // format of this program is that only 1 command is run at a time.. caveat is | |
| // that if program sets a section in memory to '\0' and serialize sets it | |
| // otherwise, the secion will end up '\0' unless a page erase is used.. so may | |
| // need to keep this part of the flash command, but specify that serialize data | |
| // 'wins' over data from the hex file | |
| static int32_t serialize_memory_image( intel_buffer_out_t *bout, | |
| struct programmer_arguments *args ) { | |
| uint32_t target_offset = 0; | |
| if( args->command == com_user ) | |
| target_offset = ATMEL_USER_PAGE_OFFSET; | |
| else if( args->device_type & GRP_STM32 ) | |
| target_offset = STM32_FLASH_OFFSET; | |
| if ( NULL != args->com_flash_data.serial_data ) { | |
| int16_t *serial_data = args->com_flash_data.serial_data; | |
| uint32_t length = args->com_flash_data.serial_length; | |
| uint32_t offset = args->com_flash_data.serial_offset; | |
| uint32_t i; | |
| for( i=0; i < length; ++i ) { | |
| if ( 0 != intel_process_data(bout, serial_data[i], | |
| target_offset, offset + i) ) { | |
| return BUFFER_INIT_ERROR; | |
| } | |
| } | |
| } | |
| return SUCCESS; | |
| } | |
| static int32_t execute_validate( dfu_device_t *device, | |
| intel_buffer_out_t *bout, | |
| uint8_t mem_segment, | |
| const dfu_bool quiet ) { | |
| int32_t retval = UNSPECIFIED_ERROR; | |
| int32_t result; // result of fcn calls | |
| intel_buffer_in_t buin; // buffer in for storing read mem | |
| if( 0 != intel_init_buffer_in(&buin, bout->info.total_size, | |
| bout->info.page_size ) ) { | |
| DEBUG("ERROR initializing a buffer.\n"); | |
| retval = BUFFER_INIT_ERROR; | |
| goto error; | |
| } | |
| buin.info.data_start = bout->info.valid_start; | |
| buin.info.data_end = bout->info.valid_end; | |
| if( device->type & GRP_STM32 ) { | |
| result = stm32_read_flash( device, &buin, mem_segment, quiet ); | |
| } else { | |
| result = atmel_read_flash( device, &buin, mem_segment, quiet ); | |
| } | |
| if( 0 != result ) { | |
| DEBUG("ERROR: could not read memory, err %d.\n", result); | |
| retval = FLASH_READ_ERROR; | |
| goto error; | |
| } | |
| if( 0 != (result = intel_validate_buffer( &buin, bout, quiet )) ) { | |
| if( result < 0 ) { | |
| retval = VALIDATION_ERROR_IN_REGION; | |
| } else { | |
| retval = VALIDATION_ERROR_OUTSIDE_REGION; | |
| } | |
| goto error; | |
| } | |
| retval = SUCCESS; | |
| error: | |
| if( !quiet && SUCCESS != retval ) fprintf( stderr, "FAIL\n" ); | |
| if( NULL != buin.data ) { | |
| free( buin.data ); | |
| buin.data = NULL; | |
| } | |
| return retval; | |
| } | |
| static void print_flash_usage( intel_buffer_info_t *info ) { | |
| fprintf( stderr, | |
| "0x%X bytes written into 0x%X bytes memory (%.02f%%).\n", | |
| info->data_end - info->data_start + 1, | |
| info->valid_end - info->valid_start + 1, | |
| ((float) (100 * (info->data_end - info->data_start + 1)) / | |
| (float) (info->valid_end - info->valid_start + 1)) ) ; | |
| } | |
| static int32_t execute_hex2bin( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| int32_t retval = -1; | |
| uint32_t i; | |
| intel_buffer_out_t bout; | |
| size_t memory_size; | |
| size_t page_size; | |
| uint32_t target_offset = 0; | |
| memory_size = args->memory_address_top + 1; | |
| page_size = args->flash_page_size; | |
| // ----------------- CONVERT HEX FILE TO BINARY ------------------------- | |
| if( 0 != intel_init_buffer_out(&bout, memory_size, page_size) ) { | |
| DEBUG("ERROR initializing a buffer.\n"); | |
| goto error; | |
| } | |
| if( 0!= intel_hex_to_buffer( args->com_convert_data.file, &bout, | |
| target_offset, args->quiet ) ) { | |
| DEBUG( "Something went wrong with creating the memory image.\n" ); | |
| goto error; | |
| } | |
| if( !args->quiet ) | |
| fprintf( stderr, "Dumping 0x%X bytes from address offset 0x%X.\n", | |
| bout.info.data_end + 1, target_offset ); | |
| for( i = 0; i <= bout.info.data_end; i++ ) { | |
| fprintf( stdout, "%c", bout.data[i] <= 0xFF ? bout.data[i] & 0xFF : 0xFF); | |
| } | |
| fflush( stdout ); | |
| retval = 0; | |
| error: | |
| if( NULL != bout.data ) { | |
| free( bout.data ); | |
| bout.data = NULL; | |
| } | |
| return retval; | |
| } | |
| static int32_t execute_bin2hex( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| int32_t retval = -1; // return value for this fcn | |
| intel_buffer_in_t buin; // buffer in for storing read mem | |
| enum atmel_memory_unit_enum mem_segment = args->com_convert_data.segment; | |
| size_t mem_size = 0; | |
| size_t page_size = 0; | |
| uint32_t target_offset = 0; // address offset on the target device | |
| // NOTE: target_offset may not be set appropriately for device | |
| // classes other than ADC_AVR32 | |
| FILE *fp = NULL; | |
| char *filename = args->com_convert_data.file; | |
| if( ADC_AVR32 == args->device_type ) { | |
| target_offset = 0x80000000; | |
| } | |
| switch( mem_segment ) { | |
| case mem_flash: | |
| mem_size = args->memory_address_top + 1; | |
| page_size = args->flash_page_size; | |
| break; | |
| case mem_eeprom: | |
| mem_size = args->eeprom_memory_size; | |
| page_size = args->eeprom_page_size; | |
| break; | |
| case mem_user: | |
| mem_size = args->flash_page_size; | |
| page_size = args->flash_page_size; | |
| target_offset = 0x80800000; | |
| break; | |
| default: | |
| fprintf( stderr, "Dump not currenlty supported for this memory.\n" ); | |
| goto error; | |
| } | |
| if( 0 != intel_init_buffer_in(&buin, mem_size, page_size) ) { | |
| DEBUG("ERROR initializing a buffer.\n"); | |
| goto error; | |
| } | |
| if( mem_segment == mem_flash ) { | |
| buin.info.data_start = args->flash_address_bottom; | |
| buin.info.data_end = args->flash_address_top; | |
| } | |
| if( NULL == filename ) { | |
| if( !args->quiet ) fprintf( stderr, "Invalid filename.\n" ); | |
| retval = -2; | |
| goto error; | |
| } | |
| if( 0 == strcmp("STDIN", filename) ) { | |
| fp = stdin; | |
| } else { | |
| fp = fopen( filename, "r" ); | |
| if( NULL == fp ) { | |
| if( !args->quiet ) fprintf( stderr, "Error opening %s\n", filename ); | |
| retval = -3; | |
| goto error; | |
| } | |
| } | |
| buin.info.data_end = fread(buin.data, 1, buin.info.total_size, fp); | |
| if( buin.info.data_end == 0 ) { | |
| if( !args->quiet ) fprintf( stderr, "ERROR: no bytes read\n" ); | |
| retval = -4; | |
| goto error; | |
| } | |
| if( !args->quiet ) | |
| fprintf( stderr, "Read 0x%X bytes, making hex with address offset 0x%X.\n", | |
| buin.info.data_end + 1, target_offset ); | |
| retval = intel_hex_from_buffer( &buin, args->com_convert_data.force, target_offset ); | |
| error: | |
| if( NULL != buin.data ) { | |
| free( buin.data ); | |
| buin.data = NULL; | |
| } | |
| return retval; | |
| } | |
| static int32_t execute_flash( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| int32_t retval = UNSPECIFIED_ERROR; | |
| int32_t result; | |
| uint32_t i; | |
| intel_buffer_out_t bout; | |
| size_t memory_size; | |
| size_t page_size; | |
| enum atmel_memory_unit_enum mem_type = args->com_flash_data.segment; | |
| uint32_t target_offset = 0; | |
| /* assign the correct memory size */ | |
| switch ( mem_type ) { | |
| case mem_flash: | |
| if( args->device_type & GRP_STM32 ) { | |
| target_offset = STM32_FLASH_OFFSET; | |
| } | |
| memory_size = args->memory_address_top + 1; | |
| page_size = args->flash_page_size; | |
| break; | |
| case mem_eeprom: | |
| if( 0 == args->eeprom_memory_size ) { | |
| fprintf( stderr, "This device has no eeprom.\n" ); | |
| return ARGUMENT_ERROR; | |
| } | |
| memory_size = args->eeprom_memory_size; | |
| page_size = args->eeprom_page_size; | |
| break; | |
| case mem_user: | |
| memory_size = args->flash_page_size; | |
| page_size = args->flash_page_size; | |
| mem_type = mem_user; | |
| target_offset = ATMEL_USER_PAGE_OFFSET; | |
| if( args->device_type != ADC_AVR32 ){ | |
| fprintf(stderr, "Flash User only implemented for ADC_AVR32 devices.\n"); | |
| retval = ARGUMENT_ERROR; | |
| goto error; | |
| } | |
| break; | |
| default: | |
| DEBUG("Unknown memory type %d\n", mem_type); | |
| return ARGUMENT_ERROR; | |
| } | |
| // ----------------- CONVERT HEX FILE TO BINARY ------------------------- | |
| if( 0 != intel_init_buffer_out(&bout, memory_size, page_size) ) { | |
| DEBUG("ERROR initializing a buffer.\n"); | |
| retval = BUFFER_INIT_ERROR; | |
| goto error; | |
| } | |
| result = intel_hex_to_buffer( args->com_flash_data.file, &bout, | |
| target_offset, args->quiet ); | |
| if ( result < 0 ) { | |
| DEBUG( "Something went wrong with creating the memory image.\n" ); | |
| retval = BUFFER_INIT_ERROR; | |
| goto error; | |
| } else if ( result > 0 ) { | |
| DEBUG( "WARNING: File contains 0x%X bytes outside target memory.\n", | |
| result ); | |
| if( mem_type == mem_flash ) { | |
| DEBUG( "There may be data in the user page (offset %#X).\n", | |
| ATMEL_USER_PAGE_OFFSET ); | |
| DEBUG( "Inspect the hex file or try flash-user.\n" ); | |
| } | |
| if( !args->quiet ) { | |
| fprintf( stderr, | |
| "WARNING: 0x%X bytes are outside target memory,\n", result ); | |
| fprintf( stderr, " and will not be written.\n" ); | |
| } | |
| } | |
| // TODO : consider accepting a string to flash to the user page as well as a hex | |
| // file.. this would be easier than using serialize and could return the address | |
| // location of the start of the string (to be used in the program file) | |
| if (0 != serialize_memory_image( &bout, args )) { | |
| retval = BUFFER_INIT_ERROR; | |
| goto error; | |
| } | |
| if( mem_type == mem_flash ) { | |
| bout.info.valid_start = args->flash_address_bottom; | |
| bout.info.valid_end = args->flash_address_top; | |
| // check that there isn't anything overlapping the bootloader | |
| for( i = args->bootloader_bottom; i <= args->bootloader_top; i++) { | |
| if( bout.data[i] <= UINT8_MAX ) { | |
| if( true == args->suppressbootloader ) { | |
| //If we're ignoring the bootloader, don't write to it | |
| bout.data[i] = UINT16_MAX; | |
| } else { | |
| fprintf( stderr, "Bootloader and code overlap.\n" ); | |
| fprintf( stderr, "Use --suppress-bootloader-mem to ignore\n" ); | |
| retval = BUFFER_INIT_ERROR; | |
| goto error; | |
| } | |
| } | |
| } | |
| } else if ( mem_type == mem_user ) { | |
| // check here about overwriting? | |
| if ( bout.info.data_start == UINT32_MAX ) { | |
| fprintf( stderr, | |
| "ERROR: No data to write into the user page.\n" ); | |
| retval = BUFFER_INIT_ERROR; | |
| goto error; | |
| } else { | |
| DEBUG("Hex file contains %u bytes to write.\n", | |
| bout.info.data_end - bout.info.data_start + 1 ); | |
| } | |
| if ( !(args->com_flash_data.force) ) { | |
| /* depending on the version of the bootloader, there could be | |
| * configuration values in the last word or last two words of the | |
| * user page. If these are overwritten the device may not start. | |
| * A warning should be issued before these values can be changed. */ | |
| fprintf( stderr, | |
| "ERROR: --force flag is required to write user page.\n" ); | |
| fprintf( stderr, | |
| " Last word(s) in user page contain configuration data.\n"); | |
| fprintf( stderr, | |
| " The user page is erased whenever any data is written.\n"); | |
| fprintf( stderr, | |
| " Without valid config. device always resets in bootloader.\n"); | |
| fprintf( stderr, | |
| " Use dump-user to obtain valid configuration words.\n"); | |
| retval = ARGUMENT_ERROR; | |
| goto error; | |
| // TODO : implement so this error only appers when data overlaps the | |
| // bootloader configuration words. This would require reading the user | |
| // page to add that data to the buffer, and also should include | |
| // checking the bootloader version to make sure the right number of | |
| // words are blocked / written. | |
| // ----------- the below for loop is not currently in use ----------- | |
| for ( i = bout.info.total_size - 8; i < bout.info.total_size; i++ ) { | |
| if ( -1 != bout.data[i] ) { | |
| fprintf( stderr, | |
| "ERROR: data overlap with bootloader configuration word(s).\n" ); | |
| DEBUG( "At position %d, value is %d.\n", i, bout.data[i] ); | |
| fprintf( stderr, | |
| "ERROR: use the --force-config flag to write the data.\n" ); | |
| retval = ARGUMENT_ERROR; | |
| goto error; | |
| } | |
| } | |
| } | |
| } | |
| // ------------------ WRITE PROGRAM DATA ------------------------------- | |
| if( mem_type == mem_user ) { | |
| result = atmel_user( device, &bout ); | |
| } else { | |
| if( args->device_type & GRP_STM32 ) { | |
| result = stm32_write_flash( device, &bout, | |
| mem_type == mem_eeprom ? true : false, | |
| args->com_flash_data.force, args->quiet ); | |
| } else { | |
| result = atmel_flash(device, &bout, | |
| mem_type == mem_eeprom ? true : false, | |
| args->com_flash_data.force, args->quiet); | |
| } | |
| } | |
| if( 0 != result ) { | |
| DEBUG( "Error writing %s data. (err %d)\n", "memory", result ); | |
| retval = FLASH_WRITE_ERROR; | |
| goto error; | |
| } | |
| // ------------------ VALIDATE PROGRAM ------------------------------ | |
| if( 0 == args->com_flash_data.suppress_validation ) { | |
| if( 0 != ( retval = execute_validate(device, &bout, mem_type, args->quiet)) ) { | |
| fprintf( stderr, "Memory did not validate. Did you erase?\n" ); | |
| goto error; | |
| } else if ( 0 == args->quiet ) { | |
| print_flash_usage( &bout.info ); | |
| } | |
| } else if( 0 == args->quiet ) { | |
| print_flash_usage( &bout.info ); | |
| } | |
| retval = SUCCESS; | |
| error: | |
| if( NULL != bout.data ) { | |
| free( bout.data ); | |
| bout.data = NULL; | |
| } | |
| return retval; | |
| } | |
| static int32_t execute_getfuse( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| atmel_avr32_fuses_t info; | |
| char *message = NULL; | |
| int32_t value = 0; | |
| int32_t status; | |
| /* only ADC_AVR32 seems to support fuse operation */ | |
| if( !(ADC_AVR32 & args->device_type) ) { | |
| DEBUG( "target doesn't support fuse set operation.\n" ); | |
| fprintf( stderr, "target doesn't support fuse set operation.\n" ); | |
| return ARGUMENT_ERROR; | |
| } | |
| /* Check AVR32 security bit in order to provide a better error message. */ | |
| security_check( device ); | |
| if( args->device_type & GRP_STM32 ) { | |
| fprintf( stderr, "Operation not supported on %s.\n", | |
| args->device_type_string ); | |
| return ARGUMENT_ERROR; | |
| } else { | |
| status = atmel_read_fuses( device, &info ); | |
| } | |
| if( 0 != status ) { | |
| DEBUG( "Error reading %s config information.\n", | |
| args->device_type_string ); | |
| fprintf( stderr, "Error reading %s config information.\n", | |
| args->device_type_string ); | |
| security_message(); | |
| return status; | |
| } | |
| switch( args->com_getfuse_data.name ) { | |
| case get_lock: | |
| value = info.lock; | |
| message = "Locked regions"; | |
| break; | |
| case get_epfl: | |
| value = info.epfl; | |
| message = "External Privileged Fetch Lock"; | |
| break; | |
| case get_bootprot: | |
| value = info.bootprot; | |
| message = "Bootloader protected area"; | |
| break; | |
| case get_bodlevel: | |
| value = info.bodlevel; | |
| message = "Brown-out detector trigger level"; | |
| break; | |
| case get_bodhyst: | |
| value = info.bodhyst; | |
| message = "BOD Hysteresis enable"; | |
| break; | |
| case get_boden: | |
| value = info.boden; | |
| message = "BOD Enable"; | |
| break; | |
| case get_isp_bod_en: | |
| value = info.isp_bod_en; | |
| message = "ISP BOD enable"; | |
| break; | |
| case get_isp_io_cond_en: | |
| value = info.isp_io_cond_en; | |
| message = "ISP IO condition enable"; | |
| break; | |
| case get_isp_force: | |
| value = info.isp_force; | |
| message = "ISP Force"; | |
| break; | |
| } | |
| fprintf( stdout, "%s%s0x%02x (%d)\n", | |
| ((0 == args->quiet) ? message : ""), | |
| ((0 == args->quiet) ? ": " : ""), | |
| value, value ); | |
| return 0; | |
| } | |
| static int32_t execute_get( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| atmel_device_info_t info; | |
| char *message = NULL; | |
| int16_t value = 0; | |
| int32_t status; | |
| int32_t controller_error = 0; | |
| /* Check AVR32 security bit in order to provide a better error message. */ | |
| security_check( device ); | |
| if( args->device_type & GRP_STM32 ) { | |
| fprintf( stderr, "Operation not supported on %s.\n", | |
| args->device_type_string ); | |
| return -1; | |
| } else { | |
| status = atmel_read_config( device, &info ); | |
| } | |
| if( 0 != status ) { | |
| DEBUG( "Error reading %s config information.\n", | |
| args->device_type_string ); | |
| fprintf( stderr, "Error reading %s config information.\n", | |
| args->device_type_string ); | |
| security_message(); | |
| return status; | |
| } | |
| switch( args->com_get_data.name ) { | |
| case get_bootloader: | |
| value = info.bootloaderVersion; | |
| message = "Bootloader Version"; | |
| break; | |
| case get_ID1: | |
| value = info.bootID1; | |
| message = "Device boot ID 1"; | |
| break; | |
| case get_ID2: | |
| value = info.bootID2; | |
| message = "Device boot ID 2"; | |
| break; | |
| case get_BSB: | |
| value = info.bsb; | |
| message = "Boot Status Byte"; | |
| if( ADC_8051 != args->device_type ) { | |
| controller_error = 1; | |
| } | |
| break; | |
| case get_SBV: | |
| value = info.sbv; | |
| message = "Software Boot Vector"; | |
| if( ADC_8051 != args->device_type ) { | |
| controller_error = 1; | |
| } | |
| break; | |
| case get_SSB: | |
| value = info.ssb; | |
| message = "Software Security Byte"; | |
| if( ADC_8051 != args->device_type ) { | |
| controller_error = 1; | |
| } | |
| break; | |
| case get_EB: | |
| value = info.eb; | |
| message = "Extra Byte"; | |
| if( ADC_8051 != args->device_type ) { | |
| controller_error = 1; | |
| } | |
| break; | |
| case get_manufacturer: | |
| value = info.manufacturerCode; | |
| message = "Manufacturer Code"; | |
| break; | |
| case get_family: | |
| value = info.familyCode; | |
| message = "Family Code"; | |
| break; | |
| case get_product_name: | |
| value = info.productName; | |
| message = "Product Name"; | |
| break; | |
| case get_product_rev: | |
| value = info.productRevision; | |
| message = "Product Revision"; | |
| break; | |
| case get_HSB: | |
| value = info.hsb; | |
| message = "Hardware Security Byte"; | |
| if( ADC_8051 != args->device_type ) { | |
| controller_error = 1; | |
| } | |
| break; | |
| } | |
| if( 0 != controller_error ) { | |
| DEBUG( "%s requires 8051 based controller\n", message ); | |
| fprintf( stderr, "%s requires 8051 based controller\n", | |
| message ); | |
| return -1; | |
| } | |
| if( value < 0 ) { | |
| fprintf( stderr, "The requested device info is unavailable.\n" ); | |
| return -2; | |
| } | |
| fprintf( stdout, "%s%s0x%02x (%d)\n", | |
| ((0 == args->quiet) ? message : ""), | |
| ((0 == args->quiet) ? ": " : ""), | |
| value, value ); | |
| return 0; | |
| } | |
| static int32_t execute_dump( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| int32_t i = 0; | |
| int32_t retval = UNSPECIFIED_ERROR; | |
| int32_t result; // result of fcn calls | |
| intel_buffer_in_t buin; // buffer in for storing read mem | |
| enum atmel_memory_unit_enum mem_segment = args->com_read_data.segment; | |
| size_t mem_size = 0; | |
| size_t page_size = 0; | |
| uint32_t target_offset = 0; // address offset on the target device | |
| // NOTE: target_offset may not be set appropriately for device | |
| // classes other than ADC_AVR32 | |
| switch( mem_segment ) { | |
| case mem_flash: | |
| mem_size = args->memory_address_top + 1; | |
| page_size = args->flash_page_size; | |
| if( ADC_AVR32 == args->device_type ) { | |
| target_offset = 0x80000000; | |
| } else if( GRP_STM32 & args->device_type ) { | |
| target_offset = STM32_FLASH_OFFSET; | |
| } | |
| break; | |
| case mem_eeprom: | |
| mem_size = args->eeprom_memory_size; | |
| page_size = args->eeprom_page_size; | |
| break; | |
| case mem_user: | |
| mem_size = args->flash_page_size; | |
| page_size = args->flash_page_size; | |
| target_offset = 0x80800000; | |
| break; | |
| default: | |
| fprintf( stderr, "Dump not currenlty supported for this memory.\n" ); | |
| retval = ARGUMENT_ERROR; | |
| goto error; | |
| } | |
| if( 0 != intel_init_buffer_in(&buin, mem_size, page_size) ) { | |
| DEBUG("ERROR initializing a buffer.\n"); | |
| retval = BUFFER_INIT_ERROR; | |
| goto error; | |
| } | |
| if( mem_segment == mem_flash ) { | |
| buin.info.data_start = args->flash_address_bottom; | |
| buin.info.data_end = args->flash_address_top; | |
| } | |
| if( args->device_type & GRP_STM32 ) { | |
| result = stm32_read_flash(device, &buin, mem_segment, args->quiet); | |
| } else { | |
| /* Check AVR32 security bit in order to provide a better error message */ | |
| security_check( device ); // avr32 has no eeprom, but OK | |
| result = atmel_read_flash(device, &buin, mem_segment, args->quiet); | |
| } | |
| if( 0 != result ) { | |
| DEBUG("ERROR: could not read memory, err %d.\n", result); | |
| security_message(); | |
| retval = FLASH_READ_ERROR; | |
| goto error; | |
| } | |
| // determine first & last page with non-blank data | |
| if( args->com_read_data.force ) { | |
| buin.info.data_start = 0; | |
| } else { | |
| // find first page with data | |
| for( i = buin.info.data_start; i < buin.info.data_end; i++ ) { | |
| if( buin.data[i] != 0xFF ) break; | |
| if( i / buin.info.page_size > | |
| buin.info.data_start / buin.info.page_size ) { | |
| // i has just jumpped to a different page than buin.data_start | |
| buin.info.data_start = i; | |
| } | |
| } | |
| if( i == buin.info.data_end ) { | |
| if( !args->quiet ) | |
| fprintf( stderr, | |
| "Memory is blank, returning a single blank page.\n" | |
| "Use --force to return the entire memory regardless.\n"); | |
| buin.info.data_start = 0; | |
| buin.info.data_end = buin.info.page_size - 1; | |
| } else { // find last page with data | |
| for( i = buin.info.data_end; i > buin.info.data_start; i-- ) { | |
| if( buin.data[i] !=0xFF ) break; | |
| if( i / buin.info.page_size < | |
| buin.info.data_end / buin.info.page_size ) { | |
| buin.info.data_end = i; | |
| } | |
| } | |
| } | |
| } | |
| if( args->com_read_data.bin ) { | |
| if( !args->quiet ) | |
| fprintf( stderr, "Dumping 0x%X bytes from address offset 0x%X.\n", | |
| buin.info.data_end + 1, target_offset ); | |
| for( i = 0; i <= buin.info.data_end; i++ ) { | |
| fprintf( stdout, "%c", buin.data[i] ); | |
| } | |
| } else { | |
| if( !args->quiet ) | |
| fprintf( stderr, "Dumping 0x%X bytes from address offset 0x%X.\n", | |
| buin.info.data_end - buin.info.data_start + 1, | |
| target_offset + buin.info.data_start ); | |
| intel_hex_from_buffer( &buin, | |
| args->com_read_data.force, target_offset ); | |
| } | |
| fflush( stdout ); | |
| retval = SUCCESS; | |
| error: | |
| if( NULL != buin.data ) { | |
| free( buin.data ); | |
| buin.data = NULL; | |
| } | |
| return retval; | |
| } | |
| static int32_t execute_setfuse( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| int32_t value = args->com_setfuse_data.value; | |
| int32_t name = args->com_setfuse_data.name; | |
| /* only ADC_AVR32 seems to support fuse operation */ | |
| if( !(ADC_AVR32 & args->device_type) || (GRP_STM32 & args->device_type) ) { | |
| fprintf( stderr, "Operation not supported on %s\n", | |
| args->device_type_string ); | |
| DEBUG( "target doesn't support fuse set operation.\n" ); | |
| return -1; | |
| } | |
| /* Check AVR32 security bit in order to provide a better error message. */ | |
| security_check( device ); | |
| if( 0 != atmel_set_fuse(device, name, value) ) { | |
| DEBUG( "Fuse set failed.\n" ); | |
| fprintf( stderr, "Fuse set failed.\n" ); | |
| security_message(); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| static int32_t execute_configure( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| int32_t value = args->com_configure_data.value; | |
| int32_t name = args->com_configure_data.name; | |
| if( ADC_8051 != args->device_type ) { | |
| fprintf( stderr, "Operation not supported on %s\n", | |
| args->device_type_string ); | |
| DEBUG( "target doesn't support configure operation.\n" ); | |
| return -1; | |
| } | |
| if( (0xff & value) != value ) { | |
| DEBUG( "Value to configure must be in range 0-255.\n" ); | |
| fprintf( stderr, "Value to configure must be in range 0-255.\n" ); | |
| return -1; | |
| } | |
| if( 0 != atmel_set_config(device, name, value) ) | |
| { | |
| DEBUG( "Configuration set failed.\n" ); | |
| fprintf( stderr, "Configuration set failed.\n" ); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| static int32_t execute_launch( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| if( args->device_type & GRP_STM32 ) { | |
| return stm32_start_app( device, args->quiet ); | |
| } else if( args->com_launch_config.noreset ) { | |
| return atmel_start_app_noreset( device ); | |
| } else { | |
| return atmel_start_app_reset( device ); | |
| } | |
| } | |
| int32_t execute_command( dfu_device_t *device, | |
| struct programmer_arguments *args ) { | |
| device->type = args->device_type; | |
| switch( args->command ) { | |
| case com_erase: | |
| return execute_erase( device, args ); | |
| case com_bin2hex: | |
| return execute_bin2hex( device, args ); | |
| case com_hex2bin: | |
| return execute_hex2bin( device, args ); | |
| case com_flash: | |
| return execute_flash( device, args ); | |
| case com_eflash: | |
| args->com_flash_data.segment = mem_eeprom; | |
| args->command = com_launch; | |
| return execute_flash( device, args ); | |
| case com_user: | |
| args->com_flash_data.segment = mem_user; | |
| args->command = com_launch; | |
| return execute_flash( device, args ); | |
| case com_start_app: | |
| args->com_launch_config.noreset = true; | |
| case com_reset: | |
| args->command = com_launch; | |
| case com_launch: | |
| return execute_launch( device, args ); | |
| case com_get: | |
| return execute_get( device, args ); | |
| case com_getfuse: | |
| return execute_getfuse( device, args ); | |
| case com_dump: | |
| args->command = com_read; | |
| args->com_read_data.force = true; | |
| args->com_read_data.bin = 1; | |
| return execute_dump( device, args ); | |
| case com_edump: | |
| args->com_read_data.segment = mem_eeprom; | |
| args->com_read_data.force = true; | |
| args->command = com_read; | |
| args->com_read_data.bin = 1; | |
| return execute_dump( device, args ); | |
| case com_udump: | |
| args->com_read_data.segment = mem_eeprom; | |
| args->com_read_data.force = true; | |
| args->command = com_read; | |
| args->com_read_data.bin = 1; | |
| return execute_dump( device, args ); | |
| case com_read: | |
| return execute_dump( device, args ); | |
| case com_configure: | |
| return execute_configure( device, args ); | |
| case com_setfuse: | |
| return execute_setfuse( device, args ); | |
| case com_setsecure: | |
| return execute_setsecure( device, args ); | |
| default: | |
| fprintf( stderr, "Not supported at this time.\n" ); | |
| } | |
| return ARGUMENT_ERROR; | |
| } |