kopia lustrzana https://github.com/stlink-org/stlink
390 wiersze
12 KiB
C
390 wiersze
12 KiB
C
/*
|
|
* File: flash_opts.c
|
|
*
|
|
* Flash Options
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <stm32.h>
|
|
#include <stlink.h>
|
|
#include "flash_opts.h"
|
|
#include "flash.h"
|
|
|
|
#include <helper.h>
|
|
|
|
static bool starts_with(const char * str, const char * prefix) {
|
|
uint32_t n = strlen(prefix);
|
|
|
|
if (strlen(str) < n) { return (false); }
|
|
|
|
return (0 == strncmp(str, prefix, n));
|
|
}
|
|
|
|
// support positive integer from 0 to UINT64_MAX
|
|
// support decimal, hexadecimal, octal, binary format like 0xff 12 1k 1M, 0b1001
|
|
// negative numbers are not supported
|
|
// return 0 if success else return -1
|
|
static int32_t get_long_integer_from_char_array (const char *const str, uint64_t *read_value) {
|
|
uint64_t value;
|
|
char *tail;
|
|
|
|
if (starts_with (str, "0x") || starts_with (str, "0X")) { // hexadecimal
|
|
value = strtoul (str + 2, &tail, 16);
|
|
} else if (starts_with (str, "0b") || starts_with (str, "0B")) { // binary
|
|
value = strtoul (str + 2, &tail, 2);
|
|
} else if (starts_with (str, "0")) { // octal
|
|
value = strtoul (str + 1, &tail, 8);
|
|
} else { // decimal
|
|
value = strtoul (str, &tail, 10);
|
|
}
|
|
|
|
if (((tail[0] == 'k') || (tail[0] == 'K')) && (tail[1] == '\0')) {
|
|
value = value * 1024;
|
|
} else if (((tail[0] == 'm') || (tail[0] == 'M')) && (tail[1] == '\0')) {
|
|
value = value * 1024 * 1024;
|
|
} else if (tail[0] == '\0') {
|
|
/* value not changed */
|
|
} else {
|
|
return (-1);
|
|
}
|
|
|
|
*read_value = value;
|
|
return (0);
|
|
}
|
|
|
|
// support positive integer from 0 to UINT32_MAX
|
|
// support decimal, hexadecimal, octal, binary format like 0xff 12 1k 1M, 0b1001
|
|
// negative numbers are not supported
|
|
// return 0 if success else return -1
|
|
static int32_t get_integer_from_char_array (const char *const str, uint32_t *read_value) {
|
|
uint64_t value;
|
|
int32_t result = get_long_integer_from_char_array (str, &value);
|
|
|
|
if (result != 0) {
|
|
return (result);
|
|
} else if (value > UINT32_MAX) {
|
|
fprintf (stderr, "*** Error: Integer greater than UINT32_MAX, cannot convert to int32_t\n");
|
|
return (-1);
|
|
} else {
|
|
*read_value = value;
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
static int32_t invalid_args(const char *expected) {
|
|
fprintf(stderr, "*** Error: Expected args for this command: %s\n", expected);
|
|
return (-1);
|
|
}
|
|
|
|
static int32_t bad_arg(const char *arg) {
|
|
fprintf(stderr, "*** Error: Invalid value for %s\n", arg);
|
|
return (-1);
|
|
}
|
|
|
|
int32_t flash_get_opts(struct flash_opts* o, int32_t ac, char** av) {
|
|
|
|
// defaults
|
|
memset(o, 0, sizeof(*o));
|
|
o->log_level = STND_LOG_LEVEL;
|
|
|
|
// options
|
|
int32_t result;
|
|
|
|
while (ac >= 1) {
|
|
if (strcmp(av[0], "--version") == 0) {
|
|
printf("v%s\n", STLINK_VERSION);
|
|
exit(EXIT_SUCCESS);
|
|
} else if (strcmp(av[0], "--debug") == 0) {
|
|
o->log_level = DEBUG_LOG_LEVEL;
|
|
} else if (strcmp(av[0], "--opt") == 0) {
|
|
o->opt = ENABLE_OPT;
|
|
} else if (strcmp(av[0], "--reset") == 0) {
|
|
o->reset = 1;
|
|
} else if (strcmp(av[0], "--serial") == 0 || starts_with(av[0], "--serial=")) {
|
|
const char * serial;
|
|
|
|
if (strcmp(av[0], "--serial") == 0) {
|
|
ac--;
|
|
av++;
|
|
|
|
if (ac < 1) { return (-1); }
|
|
|
|
serial = av[0];
|
|
} else {
|
|
serial = av[0] + strlen("--serial=");
|
|
}
|
|
|
|
memcpy(o->serial, serial, STLINK_SERIAL_BUFFER_SIZE);
|
|
|
|
} else if (strcmp(av[0], "--area") == 0 || starts_with(av[0], "--area=")) {
|
|
const char * area;
|
|
|
|
if (strcmp(av[0], "--area") == 0) {
|
|
ac--;
|
|
av++;
|
|
|
|
if (ac < 1) { return (-1); }
|
|
|
|
area = av[0];
|
|
} else {
|
|
area = av[0] + strlen("--area=");
|
|
}
|
|
|
|
if (strcmp(area, "main") == 0) {
|
|
o->area = FLASH_MAIN_MEMORY;
|
|
} else if (strcmp(area, "system") == 0) {
|
|
o->area = FLASH_SYSTEM_MEMORY;
|
|
} else if (strcmp(area, "otp") == 0) {
|
|
o->area = FLASH_OTP;
|
|
} else if (strcmp(area, "option") == 0) {
|
|
o->area = FLASH_OPTION_BYTES;
|
|
} else if (strcmp(area, "option_boot_add") == 0) {
|
|
o->area = FLASH_OPTION_BYTES_BOOT_ADD;
|
|
} else if (strcmp(area, "optcr") == 0) {
|
|
o->area = FLASH_OPTCR;
|
|
} else if (strcmp(area, "optcr1") == 0) {
|
|
o->area = FLASH_OPTCR1;
|
|
} else {
|
|
return (-1);
|
|
}
|
|
|
|
} else if (strcmp(av[0], "--freq") == 0) {
|
|
ac--;
|
|
av++;
|
|
|
|
if (ac < 1) {
|
|
return (-1);
|
|
}
|
|
|
|
o->freq = arg_parse_freq(av[0]);
|
|
if (o->freq < 0) {
|
|
return (-1);
|
|
}
|
|
} else if (starts_with(av[0], "--freq=")) {
|
|
o->freq = arg_parse_freq(av[0] + strlen("--freq="));
|
|
if (o->freq < 0) {
|
|
return (-1);
|
|
}
|
|
} else if (strcmp(av[0], "--format") == 0 || starts_with(av[0], "--format=")) {
|
|
const char * format;
|
|
|
|
if (strcmp(av[0], "--format") == 0) {
|
|
ac--;
|
|
av++;
|
|
|
|
if (ac < 1) { return (-1); }
|
|
|
|
format = av[0];
|
|
} else {
|
|
format = av[0] + strlen("--format=");
|
|
}
|
|
|
|
if (strcmp(format, "binary") == 0) {
|
|
o->format = FLASH_FORMAT_BINARY;
|
|
} else if (strcmp(format, "ihex") == 0) {
|
|
o->format = FLASH_FORMAT_IHEX;
|
|
} else {
|
|
return (bad_arg("format"));
|
|
}
|
|
} else if ( starts_with(av[0], "--flash=")) {
|
|
const char *arg = av[0] + strlen("--flash=");
|
|
|
|
uint32_t flash_size;
|
|
result = get_integer_from_char_array(arg, &flash_size);
|
|
|
|
if (result != 0) {
|
|
return (bad_arg ("--flash"));
|
|
} else {
|
|
o->flash_size = (size_t)flash_size;
|
|
}
|
|
} else if (strcmp(av[0], "--connect-under-reset") == 0) {
|
|
o->connect = CONNECT_UNDER_RESET;
|
|
} else if (strcmp(av[0], "--hot-plug") == 0) {
|
|
o->connect = CONNECT_HOT_PLUG;
|
|
} else {
|
|
break; // non-option found
|
|
|
|
}
|
|
|
|
ac--;
|
|
av++;
|
|
}
|
|
|
|
// command and (optional) device name
|
|
while (ac >= 1) {
|
|
if (strcmp(av[0], "erase") == 0) {
|
|
if (o->cmd != FLASH_CMD_NONE) { return (-1); }
|
|
o->cmd = FLASH_CMD_ERASE;
|
|
} else if (strcmp(av[0], "read") == 0) {
|
|
if (o->cmd != FLASH_CMD_NONE) { return (-1); }
|
|
|
|
o->cmd = FLASH_CMD_READ;
|
|
} else if (strcmp(av[0], "write") == 0) {
|
|
if (o->cmd != FLASH_CMD_NONE) { return (-1); }
|
|
|
|
o->cmd = FLASH_CMD_WRITE;
|
|
} else if (strcmp(av[0], "reset") == 0) {
|
|
if (o->cmd != FLASH_CMD_NONE) { return (-1); }
|
|
|
|
o->cmd = CMD_RESET;
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
ac--;
|
|
av++;
|
|
}
|
|
|
|
switch (o->cmd) {
|
|
case FLASH_CMD_NONE: // no command found
|
|
return (-1);
|
|
|
|
case FLASH_CMD_ERASE: // no more arguments expected
|
|
if (ac != 0 && ac != 2) { return (-1); }
|
|
if (ac == 2) {
|
|
uint32_t address;
|
|
result = get_integer_from_char_array(av[0], &address);
|
|
if (result != 0) {
|
|
return bad_arg ("addr");
|
|
} else {
|
|
o->addr = (stm32_addr_t) address;
|
|
}
|
|
|
|
uint32_t size;
|
|
result = get_integer_from_char_array(av[1], &size);
|
|
if (result != 0) {
|
|
return bad_arg ("size");
|
|
} else {
|
|
o->size = (size_t) size;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case FLASH_CMD_READ: // expect filename, addr and size
|
|
if ((o->area == FLASH_MAIN_MEMORY) || (o->area == FLASH_SYSTEM_MEMORY)) {
|
|
if (ac != 3) { return invalid_args("read <path> <addr> <size>"); }
|
|
|
|
o->filename = av[0];
|
|
uint32_t address;
|
|
result = get_integer_from_char_array(av[1], &address);
|
|
if (result != 0) {
|
|
return bad_arg ("addr");
|
|
} else {
|
|
o->addr = (stm32_addr_t) address;
|
|
}
|
|
|
|
uint32_t size;
|
|
result = get_integer_from_char_array(av[2], &size);
|
|
if (result != 0) {
|
|
return bad_arg ("size");
|
|
} else {
|
|
o->size = (size_t) size;
|
|
}
|
|
|
|
break;
|
|
} else if (o->area == FLASH_OTP) {
|
|
if (ac > 1 || ac ==0 ) { return invalid_args("otp read: [path]"); }
|
|
if (ac > 0) { o->filename = av[0]; }
|
|
break;
|
|
} else if (o->area == FLASH_OPTION_BYTES) {
|
|
if (ac > 2) { return invalid_args("option bytes read: [path] [size]"); }
|
|
if (ac > 0) { o->filename = av[0]; }
|
|
if (ac > 1) {
|
|
uint32_t size;
|
|
result = get_integer_from_char_array(av[1], &size);
|
|
if (result != 0) {
|
|
return bad_arg("option bytes read: invalid size");
|
|
} else {
|
|
o->size = (size_t) size;
|
|
}
|
|
}
|
|
break;
|
|
} else if (o->area == FLASH_OPTION_BYTES_BOOT_ADD) {
|
|
if (ac > 0) { return invalid_args("option bytes boot_add read"); }
|
|
break;
|
|
} else if (o->area == FLASH_OPTCR) {
|
|
if (ac > 0) { return invalid_args("option control register read"); }
|
|
break;
|
|
} else if (o->area == FLASH_OPTCR1) {
|
|
if (ac > 0) { return invalid_args("option control register 1 read"); }
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case FLASH_CMD_WRITE:
|
|
// TODO: should be boot add 0 and boot add 1 uint32
|
|
if (o->area == FLASH_OPTION_BYTES) { // expect option byte value
|
|
if (ac != 1) { return invalid_args("option byte write <value>"); }
|
|
uint32_t val;
|
|
result = get_integer_from_char_array(av[0], &val);
|
|
if (result != 0) {
|
|
return bad_arg ("val");
|
|
} else {
|
|
o->val = val;
|
|
}
|
|
} else if (o->area == FLASH_OPTION_BYTES_BOOT_ADD) { // expect option bytes boot address
|
|
if (ac != 1) { return invalid_args("option bytes boot_add write <value>"); }
|
|
|
|
uint32_t val;
|
|
result = get_integer_from_char_array(av[0], &val);
|
|
|
|
if (result != 0) {
|
|
return (bad_arg ("val"));
|
|
} else {
|
|
o->val = val;
|
|
}
|
|
} else if (o->area == FLASH_OPTCR) { // expect option control register value
|
|
if (ac != 1) { return invalid_args("option control register write <value>"); }
|
|
|
|
uint32_t val;
|
|
result = get_integer_from_char_array(av[0], &val);
|
|
|
|
if (result != 0) {
|
|
return bad_arg ("val");
|
|
} else {
|
|
o->val = val;
|
|
}
|
|
} else if (o->area == FLASH_OPTCR1) { // expect option control register 1 value
|
|
if (ac != 1) { return invalid_args("option control register 1 write <value>"); }
|
|
|
|
uint32_t val;
|
|
result = get_integer_from_char_array(av[0], &val);
|
|
if (result != 0) {
|
|
return bad_arg ("val");
|
|
} else {
|
|
o->val = val;
|
|
}
|
|
} else if (o->format == FLASH_FORMAT_BINARY) { // expect filename and addr
|
|
if (ac != 2) { return invalid_args("write <path> <addr>"); }
|
|
|
|
o->filename = av[0];
|
|
uint32_t addr;
|
|
result = get_integer_from_char_array(av[1], &addr);
|
|
|
|
if (result != 0) {
|
|
return (bad_arg ("addr"));
|
|
} else {
|
|
o->addr = (stm32_addr_t)addr;
|
|
}
|
|
} else if (o->format == FLASH_FORMAT_IHEX) { // expect filename
|
|
if (ac != 1) { return (invalid_args("write <path>")); }
|
|
|
|
o->filename = av[0];
|
|
} else {
|
|
return (-1); // should have been caught during format parsing
|
|
}
|
|
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
return (0);
|
|
}
|