Skip to content

Commit 3a116a3

Browse files
committed
BMDebug: do not pop up "file open" dialog before initialization is complete; search PATH for arm-none-eabi-gdb.
BMFlash: option to also run Tcl script on failed downloads. Handle commands that were renamed in firmware 1.9. Fix memory leaks found by ValGrind.
1 parent a9ca67d commit 3a116a3

35 files changed

+1021
-403
lines changed

‎source/Makefile.linux‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ endif
7878
OBJLIST_BMDEBUG = bmdebug.o armdisasm.o bmcommon.o bmp-scan.o bmp-script.o \
7979
demangle.o dwarf.o elf.o guidriver.o mcu-info.o memdump.o \
8080
minIni.o nuklear_mousepointer.o nuklear_splitter.o nuklear_style.o \
81-
nuklear_tooltip.o rs232.o serialmon.o specialfolder.o svd-support.o \
82-
swotrace.o tcpip.o xmltractor.o decodectf.o parsetsdl.o \
81+
nuklear_tooltip.o pathsearch.o rs232.o serialmon.o specialfolder.o \
82+
svd-support.o swotrace.o tcpip.o xmltractor.o decodectf.o parsetsdl.o \
8383
findfont.o lodepng.o nuklear.o nuklear_glfw_gl2.o noc_file_dialog.o
8484

8585
OBJLIST_BMFLASH = bmflash.o bmcommon.o bmp-scan.o bmp-script.o bmp-support.o \
@@ -201,6 +201,8 @@ nuklear_tooltip.o : nuklear_tooltip.c
201201

202202
parsetsdl.o : parsetsdl.c
203203

204+
pathsearch.o : pathsearch.c
205+
204206
rs232.o : rs232.c
205207

206208
serialmon.o : serialmon.c

‎source/bmdebug.c‎

Lines changed: 258 additions & 160 deletions
Large diffs are not rendered by default.

‎source/bmdebug.rc‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* GDB front-end with specific support for the Black Magic Probe.
33
* This utility is built with Nuklear for a cross-platform GUI.
44
*
5-
* Copyright 2019-2022 CompuPhase
5+
* Copyright 2019-2023 CompuPhase
66
*
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
@@ -33,7 +33,7 @@ AppIcon ICON "res/icon_debug.ico"
3333
#define BUILD SVNREV_NUM
3434
#define VERSIONSTR SVNREV_STR
3535
#define VERSIONNAME "bmdebug.exe\0"
36-
#define VERSIONDESCRIPTION "GDB front-end\0"
36+
#define VERSIONDESCRIPTION "BMDebug: GDB front-end\0"
3737
#define VERSIONCOMPANYNAME "CompuPhase\0"
3838
#define VERSIONPRODUCTNAME "bmdebug\0"
3939
#define VERSIONCOPYRIGHT "Copyright \251 CompuPhase 2019-2022\0"

‎source/bmflash.c‎

Lines changed: 103 additions & 32 deletions
Large diffs are not rendered by default.

‎source/bmflash.rc‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* the Black Magic Probe on a system. This utility is built with Nuklear for a
44
* cross-platform GUI.
55
*
6-
* Copyright 2019-202 CompuPhase
6+
* Copyright 2019-2023 CompuPhase
77
*
88
* Licensed under the Apache License, Version 2.0 (the "License");
99
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ AppIcon ICON "res/icon_download.ico"
3434
#define BUILD SVNREV_NUM
3535
#define VERSIONSTR SVNREV_STR
3636
#define VERSIONNAME "bmflash.exe\0"
37-
#define VERSIONDESCRIPTION "Flash programming tool\0"
37+
#define VERSIONDESCRIPTION "BMFlash: Flash programming tool\0"
3838
#define VERSIONCOMPANYNAME "CompuPhase\0"
3939
#define VERSIONPRODUCTNAME "bmflash\0"
4040
#define VERSIONCOPYRIGHT "Copyright \251 CompuPhase 2019-2022\0"

‎source/bmp-scan.c‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,19 @@ int get_bmp_count(void)
428428
return idx;
429429
}
430430

431+
/** check_versionstring() returns the hardware version of the debug probe.
432+
*
433+
* \param string [in] The string with the (purported) version information,
434+
* as returned by the "monitor version" command.
435+
*
436+
* \return The probe type/hardware version.
437+
*
438+
* \note Firmware version is ambiguous: the format has changed a few times, and
439+
* for firmware built from mainline (as well as firmware release 1.7),
440+
* the "version number" is the git hash. A more reiable way to determine
441+
* the firmware version is to parse the result of "monitor help" for
442+
* commands introduced at a specific version.
443+
*/
431444
int check_versionstring(const char *string)
432445
{
433446
if (strncmp(string, "Black Magic Probe", 17) == 0) {

‎source/bmp-support.c‎

Lines changed: 185 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -307,11 +307,9 @@ int bmp_break(void)
307307
* The name of the driver for the MCU (that the Black Magic Probe uses) is
308308
* returned.
309309
*
310-
* \param tpwr Set to 1 to power up the voltage-sense pin, 0 to
311-
* power-down, or 2 to optionally power this pin if the
312-
* initial scan returns a power of 0.0V.
313-
* \param connect_srst Set to 1 to let the Black Magic Probe keep the target
314-
* MCU in reset while scanning and attaching.
310+
* \param autopower If set, and if the swdp_scan command returns 0V power,
311+
* the "tpwr" command is given, before the swdp_scan
312+
* command is retried.
315313
* \param name Will be set to the name of the driver for the MCU (the
316314
* MCU series name) on output. This parameter may be NULL.
317315
* \param namelength The maximum length of the name, including the \0 byte.
@@ -322,10 +320,10 @@ int bmp_break(void)
322320
* \param archlength The maximum length of the architecture name, including
323321
* the \0 byte.
324322
*
325-
* \return 1 on success, 0 on failure. Status and error messages are passed via
326-
* the callback.
323+
* \return true on success, false on failure. Status and error messages are
324+
* passed via the callback.
327325
*/
328-
int bmp_attach(int tpwr, int connect_srst, char *name, size_t namelength, char *arch, size_t archlength)
326+
bool bmp_attach(bool autopower, char *name, size_t namelength, char *arch, size_t archlength)
329327
{
330328
char buffer[512];
331329
size_t size;
@@ -338,39 +336,33 @@ int bmp_attach(int tpwr, int connect_srst, char *name, size_t namelength, char *
338336

339337
if (!bmp_isopen()) {
340338
notice(BMPERR_ATTACHFAIL, "No connection to debug probe");
341-
return 0;
339+
return false;
342340
}
343341

344342
restart:
345-
if (connect_srst != 0) {
346-
if (!bmp_monitor("connect_srst enable"))
347-
notice(BMPERR_MONITORCMD, "Setting connect-with-reset option failed");
348-
}
349-
if (tpwr == 1) {
350-
if (bmp_monitor("tpwr enable")) {
351-
/* give the micro-controller a bit of time to start up, before issuing
352-
the swdp_scan command */
353-
# if defined _WIN32
354-
Sleep(100);
355-
# else
356-
usleep(100 * 1000);
357-
# endif
358-
} else {
359-
notice(BMPERR_MONITORCMD, "Power to target failed");
360-
}
361-
}
362343
gdbrsp_xmit("qRcmd,swdp_scan", -1);
363344
for ( ;; ) {
364345
size = gdbrsp_recv(buffer, sizearray(buffer), 1000);
365346
if (size > 2 && buffer[0] == 'o') {
366347
const char *ptr;
367348
buffer[size] = '\0';
368349
/* parse the string */
369-
if (tpwr == 2 && strchr(buffer, '\n') != NULL && (ptr = strstr(buffer + 1, "voltage:")) != NULL) {
350+
if (autopower && strchr(buffer, '\n') != NULL && (ptr = strstr(buffer + 1, "voltage:")) != NULL) {
370351
double voltage = strtod(ptr + 8, (char**)&ptr);
371352
if (*ptr == 'V' && voltage < 0.1) {
372353
notice(BMPSTAT_NOTICE, "Note: powering target");
373-
tpwr = 1;
354+
if (bmp_monitor("tpwr enable")) {
355+
/* give the micro-controller a bit of time to start up, before issuing
356+
the swdp_scan command */
357+
# if defined _WIN32
358+
Sleep(100);
359+
# else
360+
usleep(100 * 1000);
361+
# endif
362+
} else {
363+
notice(BMPERR_MONITORCMD, "Power to target failed");
364+
}
365+
autopower = false; /* do not drop in this case again */
374366
goto restart;
375367
}
376368
}
@@ -394,7 +386,7 @@ int bmp_attach(int tpwr, int connect_srst, char *name, size_t namelength, char *
394386
notice(BMPSTAT_NOTICE, buffer + 1); /* skip the 'o' at the start */
395387
} else if (size != 2 || memcmp(buffer, "OK", size) != 0) {
396388
/* error message was already given by an "output"-response */
397-
return 0;
389+
return false;
398390
} else {
399391
break; /* OK was received */
400392
}
@@ -407,7 +399,7 @@ int bmp_attach(int tpwr, int connect_srst, char *name, size_t namelength, char *
407399
|| (size >= 3 && buffer[0] == 'T' && isxdigit(buffer[1]) && isxdigit(buffer[2]));
408400
if (!ok) {
409401
notice(BMPERR_ATTACHFAIL, "Attach failed");
410-
return 0;
402+
return false;
411403
}
412404
notice(BMPSTAT_NOTICE, "Attached to target 1");
413405

@@ -443,26 +435,26 @@ int bmp_attach(int tpwr, int connect_srst, char *name, size_t namelength, char *
443435
if (FlashRgnCount == 0)
444436
notice(BMPERR_NOFLASH, "No Flash memory record");
445437

446-
return 1;
438+
return true;
447439
}
448440

449-
int bmp_detach(int powerdown)
441+
bool bmp_detach(bool powerdown)
450442
{
451-
int result = 0;
443+
bool result = false;
452444

453445
if (bmp_isopen()) {
454446
char buffer[100];
455447
size_t size;
456-
result = 1;
448+
result = true;
457449
/* detach */
458450
gdbrsp_xmit("D", -1);
459451
size = gdbrsp_recv(buffer, sizearray(buffer), 1000);
460452
if (size != 2 || memcmp(buffer, "OK", size) != 0)
461-
result = 0;
453+
result = false;
462454
/* optionally disable power */
463455
if (powerdown) {
464456
if (!bmp_monitor("tpwr disable"))
465-
result = 0;
457+
result = false;
466458
}
467459
}
468460

@@ -516,6 +508,163 @@ int bmp_checkversionstring(void)
516508
return probe;
517509
}
518510

511+
/** bmp_get_monitor_cmds() collects the list of "monitor" commands. These are
512+
* probe-dependent and target-dependent (plus probe firmware version
513+
* dependent).
514+
*
515+
* When this function is called after connecting to the probe (but before
516+
* attaching to the target), it returns only the probe-dependent commands.
517+
*
518+
* \return A pointer to a dynamically allocated string, which contains the
519+
* commands separated by a space. The returned list must be freed with
520+
* free().
521+
*/
522+
const char *bmp_get_monitor_cmds(void)
523+
{
524+
if (!bmp_isopen())
525+
return NULL;
526+
527+
int count = 0;
528+
int listsize = 4;
529+
char **list = malloc(listsize * sizeof(char*));
530+
if (list == NULL)
531+
return NULL;
532+
memset(list, 0, listsize * sizeof(char*));
533+
534+
char line[512];
535+
memset(line, 0, sizeof line);
536+
537+
gdbrsp_xmit("qRcmd,help",-1);
538+
for (;;) {
539+
char buffer[512];
540+
size_t size = gdbrsp_recv(buffer, sizearray(buffer) - 1, 1000);
541+
if (size > 0) {
542+
assert(size < sizearray(buffer));
543+
buffer[size] = '\0';
544+
char *ptr;
545+
if (buffer[0] == 'o') {
546+
if (line[0] == 'o')
547+
strlcat(line, buffer + 1, sizearray(line));
548+
else
549+
strlcpy(line, buffer, sizearray(line));
550+
if (strchr(line, '\n') != NULL) {
551+
/* get only the command (strip summary) */
552+
char *ptr = strstr(line, "--");
553+
if (ptr != NULL) {
554+
while (ptr > line && *(ptr - 1) <= ' ')
555+
ptr -= 1;
556+
*ptr = '\0';
557+
/* check whether to grow the list */
558+
if (count + 1 >= listsize) {
559+
int newsize = 2 * listsize;
560+
char **newlist = malloc(newsize * sizeof(char*));
561+
if (newlist != NULL) {
562+
memset(newlist, 0, newsize * sizeof(char*));
563+
memcpy(newlist, list, count * sizeof(char*));
564+
free((void*)list);
565+
list = newlist;
566+
listsize = newsize;
567+
}
568+
}
569+
if (count + 1 < listsize) {
570+
ptr = line + 1; /* skip 'o' that starts the line of the reply */
571+
while (*ptr != '\0' && *ptr <= ' ')
572+
ptr++; /* skip whitespace too */
573+
list[count]=strdup(ptr);
574+
count++;
575+
}
576+
}
577+
memset(line, 0, sizeof line);
578+
}
579+
} else if ((ptr = strchr(buffer, 'o')) != NULL) {
580+
strlcpy(line, ptr, sizearray(line));
581+
} else if (size == 2 && memcmp(buffer, "OK", size) == 0) {
582+
/* end response found -> done */
583+
break;
584+
}
585+
} else {
586+
/* no new data arrived within the time-out, assume failure */
587+
break;
588+
}
589+
}
590+
591+
/* sort the retrieved list (insertion sort) */
592+
for (int i = 1; i < count; i++) {
593+
char *key = list[i];
594+
int j;
595+
for (j = i; j > 0 && strcmp(list[j - 1], key) > 0; j--)
596+
list[j] = list[j - 1];
597+
list[j] = key;
598+
}
599+
600+
/* build a string from the list */
601+
size_t total_length = 0;
602+
for (int idx = 0; idx < count; idx++) {
603+
assert(list[idx] != NULL);
604+
total_length += strlen(list[idx]) + 1; /* +1 for space between words, or for final '\0' */
605+
}
606+
char *buffer = malloc(total_length * sizeof(char));
607+
if (buffer != NULL) {
608+
*buffer = '\0';
609+
for (int idx = 0; idx < count; idx++) {
610+
assert(list[idx] != NULL);
611+
strcat(buffer, list[idx]);
612+
if (idx + 1 < count)
613+
strcat(buffer, " ");
614+
}
615+
}
616+
617+
/* clean up */
618+
for (int idx = 0; idx < count; idx++) {
619+
assert(list[idx] != NULL);
620+
free((void*)list[idx]);
621+
}
622+
free(list);
623+
624+
return (const char*)buffer;
625+
}
626+
627+
/** bmp_expand_monitor_cmd() finds the complete command from a prefix.
628+
* \param buffer [out] Will contain the complete command.
629+
* \param bufsize The size of the output buffer.
630+
* \param name [in] The prefix to complete.
631+
* \param list [in] A string with all commands (separated by spaces).
632+
* \return true on success, false if the prefix does not match any command.
633+
*/
634+
bool bmp_expand_monitor_cmd(char *buffer, size_t bufsize, const char *name, const char *list)
635+
{
636+
assert(buffer != NULL && bufsize != 0);
637+
assert(name != NULL);
638+
assert(list != NULL);
639+
size_t name_len = strlen(name);
640+
641+
buffer[0] = '\0';
642+
const char *head = list;
643+
while (*head != '\0') {
644+
/* the assumption is that the list of commands is "wekk-formed": no leading
645+
or trailing spaces and the tokens separated by a single space */
646+
assert(*head > ' ');
647+
const char *tail = strchr(head, ' ');
648+
if (tail == NULL)
649+
tail = head + strlen(head);
650+
size_t token_len = tail - head;
651+
if (token_len >= name_len && strncmp(name, head, name_len) == 0) {
652+
/* match, copy the token */
653+
assert(token_len < bufsize);
654+
if (bufsize <= token_len)
655+
token_len = bufsize - 1;
656+
strncpy(buffer, head, token_len);
657+
buffer[token_len] = '\0';
658+
return true;
659+
}
660+
head = tail;
661+
while (*head != '\0' && *head <= ' ')
662+
head++;
663+
}
664+
665+
return false;
666+
}
667+
519668
/** bmp_monitor() executes a "monitor" command and returns whether the reply
520669
* indicates success. This is suitable for simple monitor commands, that do
521670
* not require analysis of the reply strings sent by the device (other than

‎source/bmp-support.h‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,13 @@ bool bmp_isopen(void);
5757
void bmp_sethandle(HCOM *hcom);
5858
HCOM *bmp_comport(void);
5959

60-
int bmp_checkversionstring(void);
6160
int bmp_is_ip_address(const char *address);
61+
int bmp_checkversionstring(void);
62+
const char *bmp_get_monitor_cmds(void);
63+
bool bmp_expand_monitor_cmd(char *buffer, size_t bufsize, const char *name, const char *list);
6264

63-
int bmp_attach(int tpwr, int connect_srst, char *name, size_t namelength, char *arch, size_t archlength);
64-
int bmp_detach(int powerdown);
65+
bool bmp_attach(bool autopower, char *name, size_t namelength, char *arch, size_t archlength);
66+
bool bmp_detach(bool powerdown);
6567

6668
int bmp_monitor(const char *command);
6769
int bmp_fullerase(void);

0 commit comments

Comments
 (0)