Merge pull request #397 from xor-gate/st-probe-libusb-fix
[fw/stlink] / src / stlink-usb.c
index c4c693d2b4514de39d7992aae46b32ed83e80641..36ac9687226cc40935191ac8407b1af0154ed3be 100644 (file)
@@ -15,6 +15,9 @@
 enum SCSI_Generic_Direction {SG_DXFER_TO_DEV=0, SG_DXFER_FROM_DEV=0x80};
 
 void _stlink_usb_close(stlink_t* sl) {
+    if (!sl)
+        return;
+
     struct stlink_libusb * const handle = sl->backend_data;
     // maybe we couldn't even get the usb device?
     if (handle != NULL) {
@@ -93,7 +96,7 @@ static int fill_command
     return i;
 }
 
-void _stlink_usb_version(stlink_t *sl) {
+int _stlink_usb_version(stlink_t *sl) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd  = sl->c_buf;
@@ -106,8 +109,10 @@ void _stlink_usb_version(stlink_t *sl) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
+
+    return 0;
 }
 
 int32_t _stlink_usb_target_voltage(stlink_t *sl) {
@@ -138,7 +143,7 @@ int32_t _stlink_usb_target_voltage(stlink_t *sl) {
     return voltage;
 }
 
-uint32_t _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr) {
+int _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const rdata = sl->q_buf;
     unsigned char* const cmd  = sl->c_buf;
@@ -152,12 +157,13 @@ uint32_t _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return 0;
+        return size;
     }
-    return read_uint32(rdata, 4);
+    *data = read_uint32(rdata, 4);
+    return 0;
 }
 
-void _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) {
+int _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const rdata = sl->q_buf;
     unsigned char* const cmd  = sl->c_buf;
@@ -172,37 +178,54 @@ void _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
+
+    return 0;
 }
 
-void _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
+int _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd  = sl->c_buf;
+    int i, ret;
 
-    int i = fill_command(sl, SG_DXFER_TO_DEV, len);
+    i = fill_command(sl, SG_DXFER_TO_DEV, len);
     cmd[i++] = STLINK_DEBUG_COMMAND;
     cmd[i++] = STLINK_DEBUG_WRITEMEM_32BIT;
     write_uint32(&cmd[i], addr);
     write_uint16(&cmd[i + 4], len);
-    send_only(slu, 0, cmd, slu->cmd_len);
+    ret = send_only(slu, 0, cmd, slu->cmd_len);
+    if (ret == -1)
+        return ret;
+
+    ret = send_only(slu, 1, data, len);
+    if (ret == -1)
+        return ret;
 
-    send_only(slu, 1, data, len);
+    return 0;
 }
 
-void _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) {
+int _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd  = sl->c_buf;
+    int i, ret;
 
-    int i = fill_command(sl, SG_DXFER_TO_DEV, 0);
+    i = fill_command(sl, SG_DXFER_TO_DEV, 0);
     cmd[i++] = STLINK_DEBUG_COMMAND;
     cmd[i++] = STLINK_DEBUG_WRITEMEM_8BIT;
     write_uint32(&cmd[i], addr);
     write_uint16(&cmd[i + 4], len);
-    send_only(slu, 0, cmd, slu->cmd_len);
-    send_only(slu, 1, data, len);
+    ret = send_only(slu, 0, cmd, slu->cmd_len);
+    if (ret == -1)
+        return ret;
+
+    ret = send_only(slu, 1, data, len);
+    if (ret == -1)
+        return ret;
+
+    return 0;
 }
 
 
@@ -223,7 +246,7 @@ int _stlink_usb_current_mode(stlink_t * sl) {
     return sl->q_buf[0];
 }
 
-void _stlink_usb_core_id(stlink_t * sl) {
+int _stlink_usb_core_id(stlink_t * sl) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const cmd  = sl->c_buf;
     unsigned char* const data = sl->q_buf;
@@ -237,13 +260,14 @@ void _stlink_usb_core_id(stlink_t * sl) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return -1;
     }
 
     sl->core_id = read_uint32(data, 0);
+    return 0;
 }
 
-void _stlink_usb_status(stlink_t * sl) {
+int _stlink_usb_status(stlink_t * sl) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd  = sl->c_buf;
@@ -257,12 +281,14 @@ void _stlink_usb_status(stlink_t * sl) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
     sl->q_len = (size_t) size;
+
+    return 0;
 }
 
-void _stlink_usb_force_debug(stlink_t *sl) {
+int _stlink_usb_force_debug(stlink_t *sl) {
     struct stlink_libusb *slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd  = sl->c_buf;
@@ -275,11 +301,13 @@ void _stlink_usb_force_debug(stlink_t *sl) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
+
+    return 0;
 }
 
-void _stlink_usb_enter_swd_mode(stlink_t * sl) {
+int _stlink_usb_enter_swd_mode(stlink_t * sl) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const cmd  = sl->c_buf;
     ssize_t size;
@@ -293,11 +321,13 @@ void _stlink_usb_enter_swd_mode(stlink_t * sl) {
     size = send_only(slu, 1, cmd, slu->cmd_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
+
+    return 0;
 }
 
-void _stlink_usb_exit_dfu_mode(stlink_t* sl) {
+int _stlink_usb_exit_dfu_mode(stlink_t* sl) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const cmd = sl->c_buf;
     ssize_t size;
@@ -309,15 +339,17 @@ void _stlink_usb_exit_dfu_mode(stlink_t* sl) {
     size = send_only(slu, 1, cmd, slu->cmd_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
+
+    return 0;
 }
 
 /**
  * TODO - not convinced this does anything...
  * @param sl
  */
-void _stlink_usb_reset(stlink_t * sl) {
+int _stlink_usb_reset(stlink_t * sl) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd = sl->c_buf;
@@ -331,12 +363,14 @@ void _stlink_usb_reset(stlink_t * sl) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
+
+    return 0;
 }
 
 
-void _stlink_usb_jtag_reset(stlink_t * sl, int value) {
+int _stlink_usb_jtag_reset(stlink_t * sl, int value) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd = sl->c_buf;
@@ -351,12 +385,14 @@ void _stlink_usb_jtag_reset(stlink_t * sl, int value) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
+
+    return 0;
 }
 
 
-void _stlink_usb_step(stlink_t* sl) {
+int _stlink_usb_step(stlink_t* sl) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd = sl->c_buf;
@@ -370,15 +406,17 @@ void _stlink_usb_step(stlink_t* sl) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
+
+    return 0;
 }
 
 /**
  * This seems to do a good job of restarting things from the beginning?
  * @param sl
  */
-void _stlink_usb_run(stlink_t* sl) {
+int _stlink_usb_run(stlink_t* sl) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd = sl->c_buf;
@@ -392,11 +430,13 @@ void _stlink_usb_run(stlink_t* sl) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
+
+    return 0;
 }
 
-void _stlink_usb_exit_debug_mode(stlink_t *sl) {
+int _stlink_usb_exit_debug_mode(stlink_t *sl) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const cmd = sl->c_buf;
     ssize_t size;
@@ -408,11 +448,13 @@ void _stlink_usb_exit_debug_mode(stlink_t *sl) {
     size = send_only(slu, 1, cmd, slu->cmd_len);
     if (size == -1) {
         printf("[!] send_only\n");
-        return;
+        return size;
     }
+
+    return 0;
 }
 
-void _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
+int _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd = sl->c_buf;
@@ -427,15 +469,16 @@ void _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
 
     sl->q_len = (size_t) size;
 
     stlink_print_data(sl);
+    return 0;
 }
 
-void _stlink_usb_read_all_regs(stlink_t *sl, reg *regp) {
+int _stlink_usb_read_all_regs(stlink_t *sl, reg *regp) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const cmd = sl->c_buf;
     unsigned char* const data = sl->q_buf;
@@ -448,7 +491,7 @@ void _stlink_usb_read_all_regs(stlink_t *sl, reg *regp) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
     sl->q_len = (size_t) size;
     stlink_print_data(sl);
@@ -460,16 +503,18 @@ void _stlink_usb_read_all_regs(stlink_t *sl, reg *regp) {
     regp->rw         = read_uint32(sl->q_buf, 76);
     regp->rw2        = read_uint32(sl->q_buf, 80);
     if (sl->verbose < 2)
-        return;
+        return 0;
 
     DLOG("xpsr       = 0x%08x\n", read_uint32(sl->q_buf, 64));
     DLOG("main_sp    = 0x%08x\n", read_uint32(sl->q_buf, 68));
     DLOG("process_sp = 0x%08x\n", read_uint32(sl->q_buf, 72));
     DLOG("rw         = 0x%08x\n", read_uint32(sl->q_buf, 76));
     DLOG("rw2        = 0x%08x\n", read_uint32(sl->q_buf, 80));
+
+    return 0;
 }
 
-void _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) {
+int _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd  = sl->c_buf;
@@ -484,7 +529,7 @@ void _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
     sl->q_len = (size_t) size;
     stlink_print_data(sl);
@@ -510,19 +555,27 @@ void _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) {
     default:
         regp->r[r_idx] = r;
     }
+
+    return 0;
 }
 
 /* See section C1.6 of the ARMv7-M Architecture Reference Manual */
-void _stlink_usb_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) {
+int _stlink_usb_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) {
     uint32_t r;
+    int ret;
 
     sl->q_buf[0] = (unsigned char) r_idx;
     for (int i = 1; i < 4; i++) {
         sl->q_buf[i] = 0;
     }
 
-    _stlink_usb_write_mem32(sl, DCRSR, 4);
+    ret = _stlink_usb_write_mem32(sl, DCRSR, 4);
+    if (ret == -1)
+        return ret;
+
     _stlink_usb_read_mem32(sl, DCRDR, 4);
+    if (ret == -1)
+        return ret;
 
     r = read_uint32(sl->q_buf, 0);
     DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r);
@@ -541,22 +594,39 @@ void _stlink_usb_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) {
         regp->s[r_idx - 0x40] = r;
         break;
     }
+
+    return 0;
 }
 
-void _stlink_usb_read_all_unsupported_regs(stlink_t *sl, reg *regp) {
-    _stlink_usb_read_unsupported_reg(sl, 0x14, regp);
-    _stlink_usb_read_unsupported_reg(sl, 0x21, regp);
+int _stlink_usb_read_all_unsupported_regs(stlink_t *sl, reg *regp) {
+    int ret;
+
+    ret = _stlink_usb_read_unsupported_reg(sl, 0x14, regp);
+    if (ret == -1)
+        return ret;
+
+    ret = _stlink_usb_read_unsupported_reg(sl, 0x21, regp);
+    if (ret == -1)
+        return ret;
 
     for (int i = 0; i < 32; i++) {
-        _stlink_usb_read_unsupported_reg(sl, 0x40+i, regp);
+        ret = _stlink_usb_read_unsupported_reg(sl, 0x40+i, regp);
+        if (ret == -1)
+            return ret;
     }
+
+    return 0;
 }
 
 /* See section C1.6 of the ARMv7-M Architecture Reference Manual */
-void _stlink_usb_write_unsupported_reg(stlink_t *sl, uint32_t val, int r_idx, reg *regp) {
+int _stlink_usb_write_unsupported_reg(stlink_t *sl, uint32_t val, int r_idx, reg *regp) {
+    int ret;
+
     if (r_idx >= 0x1C && r_idx <= 0x1F) { /* primask, basepri, faultmask, or control */
         /* These are held in the same register */
-        _stlink_usb_read_unsupported_reg(sl, 0x14, regp);
+        ret = _stlink_usb_read_unsupported_reg(sl, 0x14, regp);
+        if (ret == -1)
+            return ret;
 
         val = (uint8_t) (val>>24);
 
@@ -580,17 +650,19 @@ void _stlink_usb_write_unsupported_reg(stlink_t *sl, uint32_t val, int r_idx, re
 
     write_uint32(sl->q_buf, val);
 
-    _stlink_usb_write_mem32(sl, DCRDR, 4);
+    ret = _stlink_usb_write_mem32(sl, DCRDR, 4);
+    if (ret == -1)
+        return ret;
 
     sl->q_buf[0] = (unsigned char) r_idx;
     sl->q_buf[1] = 0;
     sl->q_buf[2] = 0x01;
     sl->q_buf[3] = 0;
 
-    _stlink_usb_write_mem32(sl, DCRSR, 4);
+    return _stlink_usb_write_mem32(sl, DCRSR, 4);
 }
 
-void _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) {
+int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) {
     struct stlink_libusb * const slu = sl->backend_data;
     unsigned char* const data = sl->q_buf;
     unsigned char* const cmd  = sl->c_buf;
@@ -605,10 +677,12 @@ void _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) {
     size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len);
     if (size == -1) {
         printf("[!] send_recv\n");
-        return;
+        return size;
     }
     sl->q_len = (size_t) size;
     stlink_print_data(sl);
+
+    return 0;
 }
 
 stlink_backend_t _stlink_usb_backend = {
@@ -647,12 +721,10 @@ stlink_t* stlink_open_usb(const int verbose, int reset, char *p_usb_iserial) {
     int error = -1;
     int config;
 
-    sl = malloc(sizeof (stlink_t));
-    slu = malloc(sizeof (struct stlink_libusb));
-    if (sl == NULL) goto on_error;
-    if (slu == NULL) goto on_error;
-    memset(sl, 0, sizeof (stlink_t));
-    memset(slu, 0, sizeof (struct stlink_libusb));
+    sl = calloc(1, sizeof (stlink_t));
+    slu = calloc(1, sizeof (struct stlink_libusb));
+    if (sl == NULL) goto on_malloc_error;
+    if (slu == NULL) goto on_malloc_error;
 
     ugly_init(verbose);
     sl->backend = &_stlink_usb_backend;
@@ -689,12 +761,11 @@ stlink_t* stlink_open_usb(const int verbose, int reset, char *p_usb_iserial) {
             if ((libusb_get_bus_number(list[cnt])!=devBus) || (libusb_get_device_address(list[cnt])!=devAddr)) continue;
         if ( (desc.idProduct == USB_STLINK_32L_PID) || (desc.idProduct == USB_STLINK_NUCLEO_PID) ){
             if ((p_usb_iserial != NULL)){
-                unsigned char buffer[13];
                 struct libusb_device_handle* handle;
                 libusb_open(list[cnt], &handle);
-                libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buffer, 13);
+                libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, (unsigned char *)sl->serial, sizeof(sl->serial));
                 libusb_close(handle);
-                if (memcmp(p_usb_iserial,&buffer,12) == 0){
+                if (memcmp(p_usb_iserial,&sl->serial, sizeof(sl->serial) - 1) == 0){
                     break;
                 }else{
                     continue;
@@ -716,7 +787,7 @@ stlink_t* stlink_open_usb(const int verbose, int reset, char *p_usb_iserial) {
         int error = libusb_open(list[cnt], &slu->usb_handle);
         if( error !=0 ) {
             WLOG("Error %d (%s) opening ST-Link/V2 device %03d:%03d\n", 
-               error, strerror (errno), libusb_get_bus_number(list[cnt]), libusb_get_device_address(list[cnt]));
+        error, strerror (errno), libusb_get_bus_number(list[cnt]), libusb_get_device_address(list[cnt]));
             goto on_error;
         }
     }
@@ -796,8 +867,120 @@ on_libusb_error:
 on_error:
     if( slu->libusb_ctx)
         libusb_exit(slu->libusb_ctx);
+on_malloc_error:
     if (sl != NULL) free(sl);
     if (slu != NULL) free(slu);
     return NULL;
 }
 
+static size_t stlink_probe_usb_devs(libusb_device **devs, stlink_t **sldevs[]) {
+    stlink_t **_sldevs;
+    libusb_device *dev;
+    int i = 0;
+    int ret = 0;
+    size_t slcnt = 0;
+    size_t slcur = 0;
+
+    /* Count stlink */
+    while ((dev = devs[i++]) != NULL) {
+        struct libusb_device_descriptor desc;
+        int r = libusb_get_device_descriptor(dev, &desc);
+        if (r < 0) {
+            WLOG("failed to get libusb device descriptor\n");
+            break;
+        }
+
+        if (desc.idProduct != USB_STLINK_32L_PID &&
+            desc.idProduct != USB_STLINK_NUCLEO_PID)
+            continue;
+
+        slcnt++;
+    }
+
+    /* Allocate list of pointers */
+    _sldevs = calloc(slcnt, sizeof(stlink_t *));
+    if (!_sldevs) {
+        *sldevs = NULL;
+        return 0;
+    }
+
+    /* Open stlinks and attach to list */
+    i = 0;
+    while ((dev = devs[i++]) != NULL) {
+        struct libusb_device_descriptor desc;
+        ret = libusb_get_device_descriptor(dev, &desc);
+        if (ret < 0) {
+            WLOG("failed to get libusb device descriptor\n");
+            break;
+        }
+
+        if (desc.idProduct != USB_STLINK_32L_PID &&
+            desc.idProduct != USB_STLINK_NUCLEO_PID)
+            continue;
+
+        struct libusb_device_handle* handle;
+        char serial[13];
+        memset(serial, 0, sizeof(serial));
+
+        ret = libusb_open(dev, &handle);
+        if (ret < 0) {
+            WLOG("failed to get libusb device descriptor\n");
+            break;
+        }
+        libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, (unsigned char *)&serial, sizeof(serial));
+        libusb_close(handle);
+
+        stlink_t *sl = NULL;
+        sl = stlink_open_usb(0, 1, serial);
+        if (!sl)
+            continue;
+
+        _sldevs[slcur] = sl;
+        slcur++;
+    }
+
+    /* Something went wrong */
+    if (ret < 0) {
+        free(_sldevs);
+        *sldevs = NULL;
+        return 0;
+    }
+
+    *sldevs = _sldevs;
+    return slcnt;
+}
+
+size_t stlink_probe_usb(stlink_t **stdevs[]) {
+    libusb_device **devs;
+    stlink_t **sldevs;
+
+    size_t slcnt = 0;
+    int r;
+    ssize_t cnt;
+
+    r = libusb_init(NULL);
+    if (r < 0)
+        return 0;
+
+    cnt = libusb_get_device_list(NULL, &devs);
+    if (cnt < 0)
+        return 0;
+
+    slcnt = stlink_probe_usb_devs(devs, &sldevs);
+    libusb_free_device_list(devs, 1);
+
+    libusb_exit(NULL);
+
+    *stdevs = sldevs;
+    return slcnt;
+}
+
+void stlink_probe_usb_free(stlink_t ***stdevs, size_t size) {
+    if (stdevs == NULL || *stdevs == NULL || size == 0)
+        return;
+
+    for (size_t n = 0; n < size; n++)
+        stlink_close((*stdevs)[n]);
+    free(*stdevs);
+    *stdevs = NULL;
+}