#include "init.h"
#include "stm32.h"
#include "parsers/parser.h"
#include "port.h"
#include "gpsystem.h" // advanced getopt: excerpt from gputils project
#define VERSION "STM32duino_0.5.1"
// global object pointers
static stm32 *stm; // one class
static Parser *parser; // either Binary or Intel-Hex
static Port *port; // either Serial or I²C
// settings
static port_options port_opts = {
NULL,
57600,"8e1",
0,
stm32::MAX_RX_FRAME,
stm32::MAX_TX_FRAME,
};
enum actions {
ACT_NONE,
ACT_READ,
ACT_WRITE,
ACT_WRITE_UNPROTECT,
ACT_READ_PROTECT,
ACT_READ_UNPROTECT,
ACT_ERASE_ONLY,
ACT_CRC
};
static actions action = ACT_NONE;
static int npages;
static int spage;
static bool no_erase = false;
static bool verify = false;
static bool init_flag = true;
static bool use_stdinout = false;
static bool force_binary = false;
static bool exec_flag = false;
static bool reset_flag = false;
static int retry = 10;
static uint32_t execute;
FILE *diag;
static char *filename;
static char *gpio_seq;
static uint32_t start_addr;
static uint32_t readwrite_len;
static const char *action2str(enum actions act) {
switch (act) {
case ACT_READ : return "memory read";
case ACT_WRITE : return "memory write";
case ACT_WRITE_UNPROTECT:return "write unprotect";
case ACT_READ_PROTECT : return "read protect";
case ACT_READ_UNPROTECT:return "read unprotect";
case ACT_ERASE_ONLY : return "flash erase";
case ACT_CRC : return "memory crc";
default : return "";
}
}
static void err_multi_action(enum actions _new) {
fprintf(stderr,
"ERROR: Invalid options !\n"
"\tCan't execute \"%s\" and \"%s\" at the same time.\n",
action2str(action), action2str(_new));
}
#ifdef _WIN32
BOOL CtrlHandler( DWORD fdwCtrlType ) {
fprintf(stderr, "\nCaught signal %lu\n",fdwCtrlType);
#else
void sighandler(int s){
fprintf(stderr, "\nCaught signal %d\n",s);
#endif
if (parser) delete parser;
if (stm) delete stm;
if (port) delete port;
exit(1);
}
static void show_help(const char*name) {
fprintf(stderr,
"Usage: %s [-bvngfhc] [-[rw] filename] {tty_device | i2c_device}\n"
" -a bus_address Bus address (for I2C port)\n"
" -b rate Baud rate (default 57600)\n"
" -m mode Serial port mode (default 8e1)\n"
" -r filename Read flash to file (or - stdout)\n"
" -w filename Write flash from file (or - stdout)\n"
" -C Compute CRC of flash content\n"
" -u Disable the flash write-protection\n"
" -j Enable the flash read-protection\n"
" -k Disable the flash read-protection\n"
" -o Erase only\n"
" -e n Only erase n pages before writing the flash\n"
" -v Verify writes\n"
" -n count Retry failed writes up to count times (default 10)\n"
" -g address Start execution at specified address (0 = flash start)\n"
" -S address[:length] Specify start address and optionally length for\n"
" read/write/erase operations\n"
" -F RX_length[:TX_length] Specify the max length of RX and TX frame\n"
" -s start_page Flash at specified page (0 = flash start)\n"
" -f Force binary parser\n"
" -h Show this help\n"
" -c Resume the connection (don't send initial INIT)\n"
" *Baud rate must be kept the same as the first init*\n"
" This is useful if the reset fails\n"
" -R Reset device at exit.\n"
" -i GPIO_string GPIO sequence to enter/exit bootloader mode\n"
" GPIO_string=[entry_seq][:[exit_seq]]\n"
" sequence=[[-]signal]&|,[sequence]\n"
"\n"
"GPIO sequence:\n"
" The following signals can appear in a sequence:\n"
" Integer number representing GPIO pin\n"
" 'dtr', 'rts' or 'brk' representing serial port signal\n"
" The sequence can use the following delimiters:\n"
" ',' adds 100 ms delay between signals\n"
" '&' adds no delay between signals\n"
" The following modifiers can be prepended to a signal:\n"
" '-' reset signal (low) instead of setting it (high)\n"
"\n"
"Examples:\n"
" Get device information:\n"
" %s /dev/ttyS0\n"
" or:\n"
" %s /dev/i2c-0\n"
"\n"
" Write with verify and then start execution:\n"
" %s -w filename -v -g 0x0 /dev/ttyS0\n"
"\n"
" Read flash to file:\n"
" %s -r filename /dev/ttyS0\n"
"\n"
" Read 100 bytes of flash from 0x1000 to stdout:\n"
" %s -r - -S 0x1000:100 /dev/ttyS0\n"
"\n"
" Start execution:\n"
" %s -g 0x0 /dev/ttyS0\n"
"\n"
" GPIO sequence:\n"
" - entry sequence: GPIO_3=low, GPIO_2=low, 100ms delay, GPIO_2=high\n"
" - exit sequence: GPIO_3=high, GPIO_2=low, 300ms delay, GPIO_2=high\n"
" %s -i '-3&-2,2:3&-2,,,2' /dev/ttyS0\n"
" GPIO sequence adding delay after port opening:\n"
" - entry sequence: delay 500ms\n"
" - exit sequence: rts=high, dtr=low, 300ms delay, GPIO_2=high\n"
" %s -R -i ',,,,,:rts&-dtr,,,2' /dev/ttyS0\n",
name,name,name,name,name,name,name,name,name);
}
static const char opts[]=
"a\1bus_address\0"
"b\1rate\0"
"m\1mode\0"
"r\1filename\0"
"w\1filename\0"
"C\0"
"u\0"
"j\0"
"k\0"
"o\0"
"e\1n\0"
"v\0"
"n\1count\0"
"g\1address\0"
"S\1address[:length]\0"
"F\1RX_length[:TX_length]\0"
"s\1start_page\0"
"f\0"
"h\0"
"c\0"
"R\0"
"i\1GPIO_string\0";
static void parse_options(void*p,char c, const char *arg) {
const char*argv0=reinterpret_cast<const char*>(p);
switch(c) {
case 'a': port_opts.bus_addr = strtoul(arg, NULL, 0); break;
case 'b': port_opts.baudRate = strtoul(arg, NULL, 0);
if (!port_opts.baudRate) {
fprintf(stderr,"Invalid baud rate\n");
exit(1);
}break;
case 'm': if (strlen(arg)!=3) {
fprintf(stderr, "Invalid serial mode\n");
exit(1);
}
memcpy(port_opts.mode,arg,3); // allowed '5','6','7','8' / 'n','o','e','0','1' / '1','2','5'
break;
case 'r':
case 'w':
if (action != ACT_NONE) {
err_multi_action((c == 'r') ? ACT_READ : ACT_WRITE);
exit(1);
}
action = (c == 'r') ? ACT_READ : ACT_WRITE;
filename = (char*)arg;
if (filename[0] == '-' && filename[1] == '\0') {
use_stdinout = true;
force_binary = true;
}
break;
case 'e':
if (readwrite_len || start_addr) {
fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n");
exit(1);
}
npages = strtoul(arg, NULL, 0);
if (npages > stm32::MAX_PAGES || npages < 0) {
fprintf(stderr, "ERROR: You need to specify a page count between 0 and 255");
exit(1);
}
if (!npages)
no_erase = true;
break;
case 'u':
if (action != ACT_NONE) {
err_multi_action(ACT_WRITE_UNPROTECT);
exit(1);
}
action = ACT_WRITE_UNPROTECT;
break;
case 'j':
if (action != ACT_NONE) {
err_multi_action(ACT_READ_PROTECT);
exit(1);
}
action = ACT_READ_PROTECT;
break;
case 'k':
if (action != ACT_NONE) {
err_multi_action(ACT_READ_UNPROTECT);
exit(1);
}
action = ACT_READ_UNPROTECT;
break;
case 'o':
if (action != ACT_NONE) {
err_multi_action(ACT_ERASE_ONLY);
exit(1);
}
action = ACT_ERASE_ONLY;
break;
case 'v':
verify = true;
break;
case 'n':
retry = strtoul(arg, NULL, 0);
break;
case 'g':
exec_flag = true;
execute = strtoul(arg, NULL, 0);
if (execute % 4 != 0) {
fprintf(stderr, "ERROR: Execution address must be word-aligned\n");
exit(1);
}
break;
case 's':
if (readwrite_len || start_addr) {
fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n");
exit(1);
}
spage = strtoul(arg, NULL, 0);
break;
case 'S':
if (spage || npages) {
fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n");
exit(1);
} else {
char *pLen;
start_addr = strtoul(arg, &pLen, 0);
if (*pLen == ':') {
pLen++;
readwrite_len = strtoul(pLen, NULL, 0);
if (readwrite_len == 0) {
fprintf(stderr, "ERROR: Invalid options, can't specify zero length\n");
exit(1);
}
}
}
break;
case 'F': {
char *pLen;
port_opts.rx_frame_max = strtoul(arg, &pLen, 0);
if (*pLen == ':') port_opts.tx_frame_max = strtoul(++pLen, NULL, 0);
if (port_opts.rx_frame_max < 0
|| port_opts.tx_frame_max < 0) {
fprintf(stderr, "ERROR: Invalid negative value for option -F\n");
exit(1);
}
if (!port_opts.rx_frame_max) port_opts.rx_frame_max = stm32::MAX_RX_FRAME;
if (!port_opts.tx_frame_max) port_opts.tx_frame_max = stm32::MAX_TX_FRAME;
if (port_opts.rx_frame_max < 20
|| port_opts.tx_frame_max < 6) {
fprintf(stderr, "ERROR: current code cannot work with small frames.\n");
fprintf(stderr, "min(RX) = 20, min(TX) = 6\n");
exit(1);
}
if (port_opts.rx_frame_max > stm32::MAX_RX_FRAME) {
fprintf(stderr, "WARNING: Ignore RX length in option -F\n");
port_opts.rx_frame_max = stm32::MAX_RX_FRAME;
}
if (port_opts.tx_frame_max > stm32::MAX_TX_FRAME) {
fprintf(stderr, "WARNING: Ignore TX length in option -F\n");
port_opts.tx_frame_max = stm32::MAX_TX_FRAME;
}
}break;
case 'f': force_binary = true; break;
case 'c': init_flag = false; break;
case 'h': show_help(argv0); exit(0);
case 'i': gpio_seq = (char*)arg; break;
case 'R': reset_flag = true; break;
case 'C': if (action != ACT_NONE) {
err_multi_action(ACT_CRC);
exit(1);
}
action = ACT_CRC;
break;
case 0: if (port_opts.device) {
fprintf(stderr, "ERROR: Invalid parameter specified\n");
show_help(argv0);
exit(1);
}
port_opts.device = arg; break;
default: {
fprintf(stderr, "ERROR: Invalid parameter specified\n");
show_help(argv0);
}
}
}
int __cdecl main(int argc, char* argv[]) {
int ret = 1;
int failed = 0;
stm32::err_t s_err;
Parser::err_t perr;
diag = stdout;
gp_getopt(argv,opts,parse_options,argv[0]);
if (!port_opts.device) port_opts.device=Port::find_com();
if (!port_opts.device) {
fprintf(stderr, "ERROR: Device not specified\n");
show_help(argv[0]);
return 1;
}
if ((action != ACT_WRITE) && verify) {
fprintf(stderr, "ERROR: Invalid usage, -v is only valid when writing\n");
show_help(argv[0]);
return 1;
}
if (action == ACT_READ && use_stdinout) {
diag = stderr;
}
// fprintf(diag, "stm32flash " VERSION "\n\n");
// fprintf(diag, "https://github.com/stm32duino/stm32flash\n\n");
#ifdef _WIN32
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE );
#else
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = sighandler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
#endif
if (action == ACT_WRITE) {
/* first try hex */
if (!force_binary) {
parser = new ParserHex;
}
if (force_binary || (perr = parser->open(filename, false))) {
if (force_binary || perr == Parser::ERR_INVALID_FILE) {
if (!force_binary) {
delete parser;
parser = 0;
}
/* now try binary */
parser = new ParserBin;
perr = parser->open(filename, 0);
}
/* if still have an error, fail */
if (perr) {
fprintf(stderr, "%s ERROR: %s\n", parser->name, Parser::errstr(perr));
if (perr == Parser::ERR_SYSTEM) perror(filename);
goto close;
}
}
fprintf(diag, "Using Parser : %s\n", parser->name);
}else{
parser = new ParserBin;
}
port=port_open(port_opts);
if (!port) {
fprintf(stderr, "Failed to open port: %s\n", port_opts.device);
goto close;
}
fprintf(diag, "Interface %s: %s\n", port->name, port->get_cfg_str());
if (init_flag && init_bl_entry(*port, gpio_seq)){
ret = 1;
fprintf(stderr, "Failed to send boot enter sequence\n");
goto close;
}
port->flush();
stm = new stm32(*port, init_flag);
if (!*stm) goto close;
fprintf(diag, "Version : 0x%02x\n", stm->bl_version);
if (port->flags & Port::F_GVR_ETX) {
fprintf(diag, "Option 1 : 0x%02x\n", stm->option1);
fprintf(diag, "Option 2 : 0x%02x\n", stm->option2);
}
fprintf(diag, "Device ID : 0x%04x (%s%s)\n", stm->pid, stm->dev->flags&F_GEN?"":"STM32",stm->dev->sname);
fprintf(diag, "- RAM : Up to %dKiB (%db reserved by bootloader)\n", (stm->dev->ram_end - 0x20000000) / 1024, stm->dev->ram_start - 0x20000000);
fprintf(diag, "- Flash : Up to %dKiB (size first sector: %dx%d)\n", (stm->dev->fl_end - stm->dev->fl_start ) / 1024, 1<<stm->dev->fl_ppsh, 1<<*stm->dev->fl_psh);
fprintf(diag, "- Option RAM : %db\n", stm->dev->opt_end - stm->dev->opt_start + 1);
fprintf(diag, "- System RAM : %dKiB\n", (stm->dev->mem_end - stm->dev->mem_start) / 1024);
{uint8_t buffer[256];
uint32_t addr, start, end, len;
int first_page, num_pages;
/*
* Cleanup addresses:
*
* Starting from options
* start_addr, readwrite_len, spage, npages
* and using device memory size, compute
* start, end, first_page, num_pages
*/
if (start_addr || readwrite_len) {
start = start_addr;
if (stm->dev->addr_in_flash(start)) end = stm->dev->fl_end;
else {
no_erase = true;
if (stm->dev->addr_in_ram(start)) end = stm->dev->ram_end;
else if (stm->dev->addr_in_opt_bytes(start)) end = stm->dev->opt_end + 1;
else if (stm->dev->addr_in_sysmem(start)) end = stm->dev->mem_end;
else { /* Unknown territory */
if (readwrite_len) end = start + readwrite_len;
else end = start + sizeof(uint32_t);
}
}
if (readwrite_len && (end > start + readwrite_len)) end = start + readwrite_len;
first_page = stm->dev->flash_addr_to_page(start);
if (!first_page && end == stm->dev->fl_end) num_pages = stm32::MASS_ERASE;
else num_pages = stm->dev->flash_addr_to_page(end,true) - first_page;
}else if (!spage && !npages) {
start = stm->dev->fl_start;
end = stm->dev->fl_end;
first_page = 0;
num_pages = stm32::MASS_ERASE;
}else{
first_page = spage;
start = stm->dev->flash_page_to_addr(first_page);
if (start > stm->dev->fl_end) {
fprintf(stderr, "Address range exceeds flash size.\n");
goto close;
}
if (npages) {
num_pages = npages;
end = stm->dev->flash_page_to_addr(first_page + num_pages);
if (end > stm->dev->fl_end) end = stm->dev->fl_end;
}else{
end = stm->dev->fl_end;
num_pages = stm->dev->flash_addr_to_page(end,true) - first_page;
}
if (!first_page && end == stm->dev->fl_end) num_pages = stm32::MASS_ERASE;
}
if (action == ACT_READ) {
unsigned int max_len = port_opts.rx_frame_max;
fprintf(diag, "Memory read\n");
perr = parser->open(filename, true);
if (perr) {
fprintf(stderr, "%s ERROR: %s\n", parser->name, Parser::errstr(perr));
if (perr == Parser::ERR_SYSTEM) perror(filename);
goto close;
}
fflush(diag);
addr = start;
while(addr < end) {
uint32_t left = end - addr;
len = max_len > left ? left : max_len;
s_err = stm->read_memory(addr, buffer, len);
if (s_err) {
fprintf(stderr, "Failed to read memory at address 0x%08x, target write-protected?\n", addr);
goto close;
}
if (parser->write(buffer, len)) {
fprintf(stderr, "Failed to write data to file\n");
goto close;
}
addr += uint32_t(len);
fprintf(diag,"\rRead address 0x%08x (%.2f%%) ",addr,
(100.0f / (float)(end - start)) * (float)(addr - start));
fflush(diag);
}
fprintf(diag,"Done.\n");
ret = 0;
goto close;
}else if (action == ACT_READ_PROTECT) {
fprintf(diag, "Read-Protecting flash\n");
/* the device automatically performs a reset after the sending the ACK */
reset_flag = false;
if (stm->readprot_memory()) {
fprintf(stderr, "Failed to read-protect flash\n");
goto close;
}
fprintf(diag,"Done.\n");
ret = 0;
}else if (action == ACT_READ_UNPROTECT) {
fprintf(diag, "Read-UnProtecting flash\n");
/* the device automatically performs a reset after the sending the ACK */
reset_flag = false;
if (stm->runprot_memory()) {
fprintf(stderr, "Failed to read-unprotect flash\n");
goto close;
}
fprintf(diag,"Done.\n");
ret = 0;
}else if (action == ACT_ERASE_ONLY) {
ret = 0;
fprintf(diag, "Erasing flash\n");
if (num_pages != stm32::MASS_ERASE &&
(start != stm->dev->flash_page_to_addr(first_page)
|| end != stm->dev->flash_page_to_addr(first_page + num_pages))) {
fprintf(stderr, "Specified start & length are invalid (must be page aligned)\n");
ret = 1;
goto close;
}
if (stm->erase_memory(first_page, num_pages)) {
fprintf(stderr, "Failed to erase memory\n");
ret = 1;
goto close;
}
ret = 0;
}else if (action == ACT_WRITE_UNPROTECT) {
fprintf(diag, "Write-unprotecting flash\n");
/* the device automatically performs a reset after the sending the ACK */
reset_flag = false;
if (stm->wunprot_memory()) {
fprintf(stderr, "Failed to write-unprotect flash\n");
goto close;
}
fprintf(diag,"Done.\n");
ret = 0;
}else if (action == ACT_WRITE) {
uint32_t offset = 0,size;
unsigned r;
unsigned max_wlen, max_rlen;
fprintf(diag, "Write to memory\n");
max_wlen = port_opts.tx_frame_max - 2; /* skip len and crc */
max_wlen &= ~3; /* 32 bit aligned */
max_rlen = port_opts.rx_frame_max;
max_rlen = max_rlen < max_wlen ? max_rlen : max_wlen;
/* Assume data from stdin is whole device */
if (use_stdinout) size = end - start;
else size = uint32_t(parser->size());
// TODO: It is possible to write to non-page boundaries, by reading out flash
// from partial pages and combining with the input data
// if ((start % stm->dev->fl_ps[i]) != 0 || (end % stm->dev->fl_ps[i]) != 0) {
// fprintf(stderr, "Specified start & length are invalid (must be page aligned)\n");
// goto close;
// }
// TODO: If writes are not page aligned, we should probably read out existing flash
// contents first, so it can be preserved and combined with new data
if (!no_erase && num_pages) {
fprintf(diag, "Erasing memory\n");
if (stm->erase_memory(first_page, num_pages)) {
fprintf(stderr, "Failed to erase memory\n");
goto close;
}
}
fflush(diag);
addr = start;
while(addr < end && offset < size) {
uint32_t left = end - addr;
unsigned len = max_wlen > left ? left : max_wlen;
len = len > size - offset ? size - offset : len;
if (parser->read(buffer, len)) goto close;
if (!len) {
if (use_stdinout) {
break;
} else {
fprintf(stderr, "Failed to read input file\n");
goto close;
}
}
again:
s_err = stm->write_memory(addr, buffer, len);
if (s_err) {
fprintf(stderr, "Failed to write memory at address 0x%08x\n", addr);
goto close;
}
if (verify) {
uint8_t*compare=new uint8_t[len];
unsigned offset=0, rlen;
while (offset < len) {
rlen = uint32_t(len) - offset;
rlen = rlen < max_rlen ? rlen : max_rlen;
s_err = stm->read_memory(addr + offset, compare + offset, rlen);
if (s_err) {
fprintf(stderr, "Failed to read memory at address 0x%08x\n", addr + offset);
goto close;
}
offset += rlen;
}
for(r = 0; r < len; ++r) if (buffer[r] != compare[r]) {
if (failed == retry) {
fprintf(stderr, "Failed to verify at address 0x%08x, expected 0x%02x and found 0x%02x\n",
(uint32_t)(addr + r),
buffer [r],
compare[r]);
goto close;
}
++failed;
goto again;
}
failed = 0;
}
addr += len;
offset += len;
fprintf(diag,"\rWrote %saddress 0x%08x (%.2f%%) ",
verify ? "and verified " : "",
addr,
(100.0f / size) * offset);
fflush(diag);
}
fprintf(diag, "Done.\n");
ret = 0;
goto close;
}else if (action == ACT_CRC) {
uint32_t crc_val = 0;
fprintf(diag, "CRC computation\n");
if (stm->crc_wrapper(start, end - start, crc_val)) {
fprintf(stderr, "Failed to read CRC\n");
goto close;
}
fprintf(diag, "CRC(0x%08x-0x%08x) = 0x%08x\n", start, end, crc_val);
ret = 0;
goto close;
}else ret = 0;
close:
if (stm && exec_flag && ret == 0) {
if (!execute) execute = stm->dev->fl_start;
fprintf(diag, "\nStarting execution at address 0x%08x... ", execute);
fflush(diag);
if (!stm->go(execute)) {
reset_flag = false;
fprintf(diag, "done.\n");
}else fprintf(diag, "failed.\n");
}
if (stm && reset_flag) {
fprintf(diag, "\nResetting device... \n");
fflush(diag);
if (init_bl_exit(*stm,*port, gpio_seq)) {
ret = 1;
fprintf(diag, "Reset failed.\n");
}else fprintf(diag, "Reset done.\n");
}else if (port) {
/* Always run exit sequence if present */
if (gpio_seq && strchr(gpio_seq, ':')) ret = gpio_bl_exit(*port, gpio_seq) || ret;
}
if (parser) delete parser;
if (stm) delete stm;
if (port) delete port;
fprintf(diag, "\n");
return ret;
}
}
EXTERN_C void __cdecl _fltused(void) {}
#ifdef UNICODE
void mainCRTStartup() {
int argc;
wchar_t**argvW=CommandLineToArgvW(GetCommandLine(),&argc);
char**argv=new char*[argc+1],**argvP=argv;
for (int i=0; i<argc; i++,argvP++) {
int len=WideCharToMultiByte(CP_UTF8,0,argvW[i],-1,0,0,0,0);
*argvP=new char[len];
WideCharToMultiByte(CP_UTF8,0,argvW[i],-1,*argvP,len,0,0);
}
*argvP=0;
LocalFree(argvW);
ExitProcess(main(argc,argv)); // argv in UTF-8 übergeben
}
#else
EXTERN_C _CRTIMP int __cdecl __getmainargs(int*,char***,char***,int);
void mainCRTStartup() {
int argc;
char**argv,**envp;
__getmainargs(&argc,&argv,&envp,TRUE); // geht irgendwie nicht in x64
ExitProcess(main(argc,argv));
}
#endif
Detected encoding: ANSI (CP1252) | 4
|
|