From b387c93ee007a2bde37daba8198a2108ec9704e5 Mon Sep 17 00:00:00 2001 From: anton Date: Thu, 25 Mar 2021 19:25:33 +0500 Subject: [PATCH] Extended set of command line arguments st-info: added --freq, --hot-plug and --connect-under-reset to probe st-util: added --freq --- doc/tutorial.md | 7 ++-- src/st-flash/flash.c | 11 +++--- src/st-flash/flash.h | 2 +- src/st-flash/flash_opts.c | 57 ++++++++---------------------- src/st-info/info.c | 74 +++++++++++++++++++++------------------ src/st-util/gdb-server.c | 36 ++++++++++++------- src/stlink-lib/helper.c | 14 ++++++++ src/stlink-lib/helper.h | 2 ++ src/stlink-lib/usb.c | 19 +++++----- src/stlink-lib/usb.h | 12 +++++-- 10 files changed, 122 insertions(+), 112 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 8b1220747..2403590ac 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -5,10 +5,11 @@ | Option | Tool | Description | Available
since | | --------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------- | | --flash=n[k][m] | st-flash | One can specify `--flash=128k` for example, to override the default value of 64k for the STM32F103C8T6
to assume 128k of flash being present. This option accepts decimal (128k), octal 0200k, or hex 0x80k values.
Leaving the multiplier out is equally valid, e.g.: `--flash=0x20000`. The size may be followed by an optional
"k" or "m" to multiply the given value by 1k (1024) or 1M respectively. | v1.4.0 | -| --freq=n[k][m] | st-flash | The frequency of the SWD/JTAG interface can be specified, to override the default 1800 kHz configuration.
This option solely accepts decimal values (5K or 1.8M) with the unit `Hz` being left out. Valid frequencies are:
`5K, 15K, 25K, 50K, 100K, 125K, 240K, 480K, 950K, 1200K(1.2M), 1800K(1.8M), 4000K(4M)`. | v1.6.1 | +| --freq=n[k][m] | st-info
st-flash
st-util | The frequency of the SWD/JTAG interface can be specified, to override the default 1800 kHz configuration.
This option solely accepts decimal values (5K or 1.8M) with the unit `Hz` being left out. Valid frequencies are:
`5K, 15K, 25K, 50K, 100K, 125K, 240K, 480K, 950K, 1200K, 1800K, 4000K(4M)`. | v1.6.1 | | --opt | st-flash | Optimisation can be enabled in order to skip flashing empty (0x00 or 0xff) bytes at the end of binary file.
This may cause some garbage data left after a flash operation. This option was enabled by default in earlier releases. | v1.6.1 | | --reset | st-flash | Trigger a reset both before and after flashing. The default uses the hardware reset through `NRST` pin.
The software reset is used if the hardware reset failed (`NRST` pin not connected). | v1.0.0 | -| --connect-under-reset | st-flash | Connect under reset. Option makes it possible to connect to the device before code execution. This is useful
when the target contains code that lets the device go to sleep, disables debug pins or other special code. | v1.6.1 | +| --connect-under-reset | st-info
st-flash | Connect under reset. Option makes it possible to connect to the device before code execution. This is useful
when the target contains code that lets the device go to sleep, disables debug pins or other special code. | v1.6.1 | +| --hot-plug | st-info
st-util | Connect to the target without reset. | v1.6.2 | | --probe | st-info | Display hardware information about the connected programmer and target MCU. | v1.2.0 | | --version | st-info
st-flash
st-util | Print version information. | v1.3.0 | | --help | st-flash
st-util | Print list of available commands. | | @@ -262,7 +263,7 @@ There are a few options: -m, --multi Set gdb server to extended mode. st-util will continue listening for connections after disconnect. - -n, --no-reset + -n, --no-reset, --hot-plug Do not reset board on connection. ``` diff --git a/src/st-flash/flash.c b/src/st-flash/flash.c index 447daa158..5a226d57b 100644 --- a/src/st-flash/flash.c +++ b/src/st-flash/flash.c @@ -26,9 +26,9 @@ static void cleanup(int signum) { } static void usage(void) { - puts("command line: ./st-flash [--debug] [--reset] [--connect-under-reset] [--opt] [--serial ] [--format ] [--flash=] [--freq=] [--area=] {read|write} [path] [addr] [size]"); - puts("command line: ./st-flash [--debug] [--connect-under-reset] [--freq=] [--serial ] erase"); - puts("command line: ./st-flash [--debug] [--freq=] [--serial ] reset"); + puts("command line: ./st-flash [--debug] [--reset] [--connect-under-reset] [--opt] [--serial ] [--format ] [--flash=] [--freq=] [--area=] {read|write} [path] [addr] [size]"); + puts("command line: ./st-flash [--debug] [--connect-under-reset] [--freq=] [--serial ] erase"); + puts("command line: ./st-flash [--debug] [--freq=] [--serial ] reset"); puts(" , and : Use hex format."); puts(" : Use decimal, octal or hex (prefix 0xXXX) format, optionally followed by k=KB, or m=MB (eg. --flash=128k)"); puts(" : Can be 'binary' (default) or 'ihex', although must be specified for binary format only."); @@ -52,6 +52,7 @@ int main(int ac, char** av) { uint8_t * mem = NULL; o.size = 0; + o.connect = CONNECT_NORMAL; if (flash_get_opts(&o, ac - 1, av + 1) == -1) { printf("invalid command line\n"); @@ -61,9 +62,7 @@ int main(int ac, char** av) { printf("st-flash %s\n", STLINK_VERSION); - sl = stlink_open_usb(o.log_level, - o.connect_under_reset ? 2 : 1, - (char *)o.serial, o.freq); + sl = stlink_open_usb(o.log_level, o.connect, (char *)o.serial, o.freq); if (sl == NULL) { return(-1); } diff --git a/src/st-flash/flash.h b/src/st-flash/flash.h index 8a5c2ad29..cd08db70f 100644 --- a/src/st-flash/flash.h +++ b/src/st-flash/flash.h @@ -26,7 +26,7 @@ struct flash_opts { size_t flash_size; // --flash=n[k][m] int opt; // enable empty tail data drop optimization int freq; // --freq=n[k][m] frequency of JTAG/SWD - bool connect_under_reset; + enum connect_type connect; }; #define FLASH_OPTS_INITIALIZER {0, { 0 }, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} diff --git a/src/st-flash/flash_opts.c b/src/st-flash/flash_opts.c index 7d846e425..63774ea25 100644 --- a/src/st-flash/flash_opts.c +++ b/src/st-flash/flash_opts.c @@ -2,6 +2,8 @@ #include #include +#include + #include "flash.h" static bool starts_with(const char * str, const char * prefix) { @@ -140,50 +142,21 @@ int flash_get_opts(struct flash_opts* o, int ac, char** av) { return(-1); } - } else if (strcmp(av[0], "--freq") == 0 || starts_with(av[0], "--freq=")) { - const char* freq; - - if (strcmp(av[0], "--freq") == 0) { - ac--; - av++; - - if (ac < 1) { - return(-1); - } + } else if (strcmp(av[0], "--freq") == 0) { + ac--; + av++; - freq = av[0]; - } else { - freq = av[0] + strlen("--freq="); + if (ac < 1) { + return(-1); } - if (strcmp(freq, "5K") == 0 || strcmp(freq, "5k") == 0) { - o->freq = 5; - } else if (strcmp(freq, "15K") == 0 || strcmp(freq, "15k") == 0) { - o->freq = 15; - } else if (strcmp(freq, "25K") == 0 || strcmp(freq, "25k") == 0) { - o->freq = 25; - } else if (strcmp(freq, "50K") == 0 || strcmp(freq, "50k") == 0) { - o->freq = 50; - } else if (strcmp(freq, "100K") == 0 || strcmp(freq, "100k") == 0) { - o->freq = 100; - } else if (strcmp(freq, "125K") == 0 || strcmp(freq, "125k") == 0) { - o->freq = 125; - } else if (strcmp(freq, "240K") == 0 || strcmp(freq, "240k") == 0) { - o->freq = 240; - } else if (strcmp(freq, "480K") == 0 || strcmp(freq, "480k") == 0) { - o->freq = 480; - } else if (strcmp(freq, "950K") == 0 || strcmp(freq, "950k") == 0) { - o->freq = 950; - } else if (strcmp(freq, "1200K") == 0 || strcmp(freq, "1200k") == 0 || - strcmp(freq, "1.2M") == 0 || strcmp(freq, "1.2m") == 0) { - o->freq = 1200; - } else if (strcmp(freq, "1800K") == 0 || strcmp(freq, "1800k") == 0 || - strcmp(freq, "1.8M") == 0 || strcmp(freq, "1.8m") == 0) { - o->freq = 1800; - } else if (strcmp(freq, "4000K") == 0 || strcmp(freq, "4000k") == 0 || - strcmp(freq, "4M") == 0 || strcmp(freq, "4m") == 0) { - o->freq = 4000; - } else { + 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=")) { @@ -219,7 +192,7 @@ int flash_get_opts(struct flash_opts* o, int ac, char** av) { o->flash_size = (size_t)flash_size; } } else if (strcmp(av[0], "--connect-under-reset") == 0) { - o->connect_under_reset = true; + o->connect = CONNECT_UNDER_RESET; } else { break; // non-option found diff --git a/src/st-info/info.c b/src/st-info/info.c index c1ac86373..a6b4e85c6 100644 --- a/src/st-info/info.c +++ b/src/st-info/info.c @@ -4,16 +4,17 @@ #include #include +#include static void usage(void) { puts("st-info --version"); - puts("st-info --probe"); + puts("st-info --probe [--connect-under-reset] [--hot-plug] [--freq=]"); puts("st-info --serial"); - puts("st-info --flash [--connect-under-reset]"); - puts("st-info --pagesize [--connect-under-reset]"); - puts("st-info --sram [--connect-under-reset]"); - puts("st-info --chipid [--connect-under-reset]"); - puts("st-info --descr [--connect-under-reset]"); + puts("st-info --flash [--connect-under-reset] [--hot-plug] [--freq=]"); + puts("st-info --pagesize [--connect-under-reset] [--hot-plug] [--freq=]"); + puts("st-info --sram [--connect-under-reset] [--hot-plug] [--freq=]"); + puts("st-info --chipid [--connect-under-reset] [--hot-plug] [--freq=]"); + puts("st-info --descr [--connect-under-reset] [--hot-plug] [--freq=]"); } static void stlink_print_version(stlink_t *sl) { @@ -45,11 +46,11 @@ static void stlink_print_info(stlink_t *sl) { if (params) { printf(" descr: %s\n", params->description); } } -static void stlink_probe(void) { +static void stlink_probe(enum connect_type connect, int freq) { stlink_t **stdevs; size_t size; - size = stlink_probe_usb(&stdevs); + size = stlink_probe_usb(&stdevs, connect, freq); printf("Found %u stlink programmers\n", (unsigned int)size); @@ -61,44 +62,47 @@ static void stlink_probe(void) { stlink_probe_usb_free(&stdevs, size); } -static stlink_t *stlink_open_first(bool under_reset) { +static int print_data(int ac, char **av) { stlink_t* sl = NULL; - sl = stlink_v1_open(0, 1); + enum connect_type connect = CONNECT_NORMAL; + int freq = 0; - if (sl == NULL) { - if (under_reset) { - sl = stlink_open_usb(0, 2, NULL, 0); - } else { - sl = stlink_open_usb(0, 1, NULL, 0); - } + if (strcmp(av[1], "--version") == 0) { + printf("v%s\n", STLINK_VERSION); + return(0); } - return(sl); -} + for (int i=2; i= 0) { continue; } + } + } else if (strncmp(av[i], "--freq=", 7) == 0) { + freq = arg_parse_freq(av[i] + 7); + if (freq >= 0) { continue; } + } -static int print_data(int ac, char **av) { - stlink_t* sl = NULL; - bool under_reset = false; + printf("Incorrect argument: %s\n\n", av[i]); + usage(); + return(-1); + } // probe needs all devices unclaimed if (strcmp(av[1], "--probe") == 0) { - stlink_probe(); - return(0); - } else if (strcmp(av[1], "--version") == 0) { - printf("v%s\n", STLINK_VERSION); + stlink_probe(connect, freq); return(0); } - if (ac == 3) { - if (strcmp(av[2], "--connect-under-reset") == 0) { - under_reset = true; - } else { - usage(); - return(-1); - } - } - - sl = stlink_open_first(under_reset); + // open first st-link device + sl = stlink_open_usb(0, connect, NULL, freq); if (sl == NULL) { return(-1); } diff --git a/src/st-util/gdb-server.c b/src/st-util/gdb-server.c index d7ca6d008..3dc3073e5 100644 --- a/src/st-util/gdb-server.c +++ b/src/st-util/gdb-server.c @@ -26,6 +26,7 @@ #endif #include +#include #include #include "gdb-remote.h" #include "gdb-server.h" @@ -63,7 +64,8 @@ typedef struct _st_state_t { int logging_level; int listen_port; int persistent; - int reset; + enum connect_type connect_mode; + int freq; } st_state_t; @@ -100,9 +102,9 @@ static stlink_t* do_connect(st_state_t *st) { stlink_t *sl = NULL; if (serial_specified) { - sl = stlink_open_usb(st->logging_level, st->reset, serialnumber, 0); + sl = stlink_open_usb(st->logging_level, st->connect_mode, serialnumber, st->freq); } else { - sl = stlink_open_usb(st->logging_level, st->reset, NULL, 0); + sl = stlink_open_usb(st->logging_level, st->connect_mode, NULL, st->freq); } return(sl); @@ -116,6 +118,8 @@ int parse_options(int argc, char** argv, st_state_t *st) { {"listen_port", required_argument, NULL, 'p'}, {"multi", optional_argument, NULL, 'm'}, {"no-reset", optional_argument, NULL, 'n'}, + {"hot-plug", optional_argument, NULL, 'n'}, + {"freq", optional_argument, NULL, 'F'}, {"version", no_argument, NULL, 'V'}, {"semihosting", no_argument, NULL, SEMIHOSTING_OPTION}, {"serial", required_argument, NULL, SERIAL_OPTION}, @@ -126,16 +130,16 @@ int parse_options(int argc, char** argv, st_state_t *st) { " -V, --version\t\tPrint the version\n" " -vXX, --verbose=XX\tSpecify a specific verbosity level (0..99)\n" " -v, --verbose\t\tSpecify generally verbose logging\n" - "\t\t\tChoose what version of stlink to use, (defaults to 2)\n" - " -1, --stlinkv1\tForce stlink version 1\n" " -p 4242, --listen_port=1234\n" "\t\t\tSet the gdb server listen port. " "(default port: " STRINGIFY(DEFAULT_GDB_LISTEN_PORT) ")\n" " -m, --multi\n" "\t\t\tSet gdb server to extended mode.\n" "\t\t\tst-util will continue listening for connections after disconnect.\n" - " -n, --no-reset\n" + " -n, --no-reset, --hot-plug\n" "\t\t\tDo not reset board on connection.\n" + " -F 1800K, --freq=1M\n" + "\t\t\tSet the frequency of the SWD/JTAG interface.\n" " --semihosting\n" "\t\t\tEnable semihosting support.\n" " --serial \n" @@ -160,7 +164,6 @@ int parse_options(int argc, char** argv, st_state_t *st) { exit(EXIT_SUCCESS); break; case 'v': - if (optarg) { st->logging_level = atoi(optarg); } else { @@ -170,7 +173,6 @@ int parse_options(int argc, char** argv, st_state_t *st) { break; case 'p': sscanf(optarg, "%i", &q); - if (q < 0) { fprintf(stderr, "Can't use a negative port to listen on: %d\n", q); exit(EXIT_FAILURE); @@ -178,11 +180,19 @@ int parse_options(int argc, char** argv, st_state_t *st) { st->listen_port = q; break; + case 'm': st->persistent = 1; break; case 'n': - st->reset = 0; + st->connect_mode = CONNECT_HOT_PLUG; + break; + case 'F': + st->freq = arg_parse_freq(optarg); + if (st->freq < 0) { + fprintf(stderr, "Can't parse a frequency: %s\n", optarg); + exit(EXIT_FAILURE); + } break; case 'V': printf("v%s\n", STLINK_VERSION); @@ -217,7 +227,7 @@ int main(int argc, char** argv) { // set defaults ... state.logging_level = DEFAULT_LOGGING_LEVEL; state.listen_port = DEFAULT_GDB_LISTEN_PORT; - state.reset = 1; // by default, reset board + state.connect_mode = CONNECT_NORMAL; // by default, reset board parse_options(argc, argv, &state); printf("st-util\n"); @@ -240,7 +250,7 @@ int main(int argc, char** argv) { signal(SIGSEGV, &cleanup); #endif - if (state.reset) { stlink_reset(sl); } + if (state.connect_mode != CONNECT_HOT_PLUG) { stlink_reset(sl); } DLOG("Chip ID is %#010x, Core ID is %#08x.\n", sl->chip_id, sl->core_id); @@ -1111,7 +1121,7 @@ int serve(stlink_t *sl, st_state_t *st) { stlink_force_debug(sl); - if (st->reset) { stlink_reset(sl); } + if (st->connect_mode != CONNECT_HOT_PLUG) { stlink_reset(sl); } init_code_breakpoints(sl); init_data_watchpoints(sl); @@ -1840,7 +1850,7 @@ int serve(stlink_t *sl, st_state_t *st) { connected_stlink = sl; - if (st->reset) { stlink_reset(sl); } + if (st->connect_mode != CONNECT_HOT_PLUG) { stlink_reset(sl); } ret = stlink_force_debug(sl); diff --git a/src/stlink-lib/helper.c b/src/stlink-lib/helper.c index 32d9b6c8d..1f0f71b8d 100644 --- a/src/stlink-lib/helper.c +++ b/src/stlink-lib/helper.c @@ -1,6 +1,7 @@ #include #include +#include #ifdef STLINK_HAVE_SYS_TIME_H #include @@ -13,3 +14,16 @@ unsigned time_ms() { gettimeofday(&tv, NULL); return (unsigned)(tv.tv_sec * 1000 + tv.tv_usec / 1000); } + +int arg_parse_freq(const char *str) { + char *tail; + int value = (int)strtol(str, &tail, 10); + + if ((tail[0] == 'm' || tail[0] == 'M') && tail[1] == '\0') { + value = value*1000; + } else if (((tail[0] != 'k' && tail[0] != 'K') || tail[1] != '\0') && tail[0] != '\0') { + return -1; + } + + return value; +} diff --git a/src/stlink-lib/helper.h b/src/stlink-lib/helper.h index 12c2ea2a7..96467377a 100644 --- a/src/stlink-lib/helper.h +++ b/src/stlink-lib/helper.h @@ -3,4 +3,6 @@ unsigned time_ms(); +int arg_parse_freq(const char *str); + #endif /* SYS_HELPER_H */ diff --git a/src/stlink-lib/usb.c b/src/stlink-lib/usb.c index 569815665..ce0d672c8 100644 --- a/src/stlink-lib/usb.c +++ b/src/stlink-lib/usb.c @@ -46,7 +46,7 @@ static int _stlink_match_speed_map(const uint32_t *map, unsigned int map_size, u // get abs value for comparison current_diff = (current_diff > 0) ? current_diff : -current_diff; - if ((current_diff < speed_diff) && khz >= map[i]) { + if (current_diff < speed_diff) { speed_diff = current_diff; speed_index = i; } @@ -741,6 +741,8 @@ int _stlink_usb_set_swdclk(stlink_t* sl, int clk_freq) { } return(0); + } else if (clk_freq) { + WLOG("ST-Link firmware does not support frequency setup\n"); } return(-1); @@ -1196,7 +1198,7 @@ size_t stlink_serial(struct libusb_device_handle *handle, struct libusb_device_d return strlen(serial); } -stlink_t *stlink_open_usb(enum ugly_loglevel verbose, int reset, char serial[STLINK_SERIAL_BUFFER_SIZE], int freq) { +stlink_t *stlink_open_usb(enum ugly_loglevel verbose, enum connect_type connect, char serial[STLINK_SERIAL_BUFFER_SIZE], int freq) { stlink_t* sl = NULL; struct stlink_libusb* slu = NULL; int ret = -1; @@ -1380,7 +1382,7 @@ stlink_t *stlink_open_usb(enum ugly_loglevel verbose, int reset, char serial[STL DLOG("JTAG/SWD freq set to %d\n", freq); stlink_set_swdclk(sl, freq); - if (reset == 2) { + if (connect == CONNECT_UNDER_RESET) { stlink_jtag_reset(sl, 0); if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { stlink_enter_swd_mode(sl); } @@ -1390,10 +1392,9 @@ stlink_t *stlink_open_usb(enum ugly_loglevel verbose, int reset, char serial[STL usleep(10000); } - if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { stlink_enter_swd_mode(sl); } - if (reset == 1) { + if (connect == CONNECT_NORMAL) { if ( sl->version.stlink_v > 1) { stlink_jtag_reset(sl, 2); } stlink_reset(sl); @@ -1420,7 +1421,7 @@ stlink_t *stlink_open_usb(enum ugly_loglevel verbose, int reset, char serial[STL return(NULL); } -static size_t stlink_probe_usb_devs(libusb_device **devs, stlink_t **sldevs[]) { +static size_t stlink_probe_usb_devs(libusb_device **devs, stlink_t **sldevs[], enum connect_type connect, int freq) { stlink_t **_sldevs; libusb_device *dev; int i = 0; @@ -1489,7 +1490,7 @@ static size_t stlink_probe_usb_devs(libusb_device **devs, stlink_t **sldevs[]) { if (serial_len != STLINK_SERIAL_LENGTH) { continue; } - stlink_t *sl = stlink_open_usb(0, 1, serial, 0); + stlink_t *sl = stlink_open_usb(0, connect, serial, freq); if (!sl) { ELOG("Failed to open USB device %#06x:%#06x\n", desc.idVendor, desc.idProduct); @@ -1504,7 +1505,7 @@ static size_t stlink_probe_usb_devs(libusb_device **devs, stlink_t **sldevs[]) { return(slcur); } -size_t stlink_probe_usb(stlink_t **stdevs[]) { +size_t stlink_probe_usb(stlink_t **stdevs[], enum connect_type connect, int freq) { libusb_device **devs; stlink_t **sldevs; @@ -1520,7 +1521,7 @@ size_t stlink_probe_usb(stlink_t **stdevs[]) { if (cnt < 0) { return(0); } - slcnt = stlink_probe_usb_devs(devs, &sldevs); + slcnt = stlink_probe_usb_devs(devs, &sldevs, connect, freq); libusb_free_device_list(devs, 1); libusb_exit(NULL); diff --git a/src/stlink-lib/usb.h b/src/stlink-lib/usb.h index 512370a10..f48ef225d 100644 --- a/src/stlink-lib/usb.h +++ b/src/stlink-lib/usb.h @@ -48,6 +48,12 @@ extern "C" { #define STLINK_SG_SIZE 31 #define STLINK_CMD_SIZE 16 +enum connect_type { + CONNECT_HOT_PLUG = 0, + CONNECT_NORMAL = 1, + CONNECT_UNDER_RESET = 2, +}; + struct stlink_libusb { libusb_context* libusb_ctx; libusb_device_handle* usb_handle; @@ -62,13 +68,13 @@ struct stlink_libusb { /** * Open a stlink * @param verbose Verbosity loglevel - * @param reset Reset stlink programmer + * @param connect Type of connect to target * @param serial Serial number to search for, when NULL the first stlink found is opened (binary format) * @retval NULL Error while opening the stlink * @retval !NULL Stlink found and ready to use */ -stlink_t *stlink_open_usb(enum ugly_loglevel verbose, int reset, char serial[STLINK_SERIAL_BUFFER_SIZE], int freq); -size_t stlink_probe_usb(stlink_t **stdevs[]); +stlink_t *stlink_open_usb(enum ugly_loglevel verbose, enum connect_type connect, char serial[STLINK_SERIAL_BUFFER_SIZE], int freq); +size_t stlink_probe_usb(stlink_t **stdevs[], enum connect_type connect, int freq); void stlink_probe_usb_free(stlink_t **stdevs[], size_t size); #ifdef __cplusplus