]> sigrok.org Git - libsigrok.git/blobdiff - src/scpi/scpi_usbtmc_libusb.c
scpi: document callers' responsibility to free getters' allocated memory
[libsigrok.git] / src / scpi / scpi_usbtmc_libusb.c
index caf17e7ec77d83e347c94ac778e50c3d3425e23d..2da67030ed2495c41e9a0c3dcd56f9255d72f201 100644 (file)
@@ -17,6 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
+#include <inttypes.h>
 #include <string.h>
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
@@ -44,12 +46,11 @@ struct scpi_usbtmc_libusb {
        int response_length;
        int response_bytes_read;
        int remaining_length;
-       int rigol_ds1000;
 };
 
 /* Some USBTMC-specific enums, as defined in the USBTMC standard. */
-#define SUBCLASS_USBTMC  0x03
-#define USBTMC_USB488    0x01
+#define SUBCLASS_USBTMC 0x03
+#define USBTMC_USB488   0x01
 
 enum {
        /* USBTMC control requests */
@@ -69,6 +70,9 @@ enum {
        LOCAL_LOCKOUT               = 162,
 };
 
+/* USBTMC status codes */
+#define USBTMC_STATUS_SUCCESS      0x01
+
 /* USBTMC capabilities */
 #define USBTMC_INT_CAP_LISTEN_ONLY 0x01
 #define USBTMC_INT_CAP_TALK_ONLY   0x02
@@ -82,16 +86,39 @@ enum {
 #define USB488_DEV_CAP_SCPI        0x08
 
 /* Bulk messages constants */
-#define USBTMC_BULK_HEADER_SIZE  12
+#define USBTMC_BULK_HEADER_SIZE 12
 
 /* Bulk MsgID values */
-#define DEV_DEP_MSG_OUT         1
-#define REQUEST_DEV_DEP_MSG_IN  2
-#define DEV_DEP_MSG_IN          2
+#define DEV_DEP_MSG_OUT        1
+#define REQUEST_DEV_DEP_MSG_IN 2
+#define DEV_DEP_MSG_IN         2
 
 /* bmTransferAttributes */
-#define EOM                0x01
-#define TERM_CHAR_ENABLED  0x02
+#define EOM               0x01
+#define TERM_CHAR_ENABLED 0x02
+
+struct usbtmc_blacklist {
+       uint16_t vid;
+       uint16_t pid;
+};
+
+/* Devices that publish RL1 support, but don't support it. */
+static struct usbtmc_blacklist blacklist_remote[] = {
+       { 0x1ab1, 0x0588 }, /* Rigol DS1000 series */
+       { 0x1ab1, 0x04b0 }, /* Rigol DS2000 series */
+       { 0x1ab1, 0x04b1 }, /* Rigol DS4000 series */
+       { 0x1ab1, 0x0515 }, /* Rigol MSO5000 series */
+       { 0x0957, 0x0588 }, /* Agilent DSO1000 series (rebadged Rigol DS1000) */
+       { 0x0b21, 0xffff }, /* All Yokogawa devices */
+       { 0xf4ec, 0xffff }, /* All Siglent SDS devices */
+       ALL_ZERO
+};
+
+/* Devices that shall get reset during open(). */
+static struct usbtmc_blacklist whitelist_usb_reset[] = {
+       { 0xf4ec, 0xffff }, /* All Siglent SDS devices */
+       ALL_ZERO
+};
 
 static GSList *scpi_usbtmc_libusb_scan(struct drv_context *drvc)
 {
@@ -110,16 +137,13 @@ static GSList *scpi_usbtmc_libusb_scan(struct drv_context *drvc)
                return NULL;
        }
        for (i = 0; devlist[i]; i++) {
-               if ((ret = libusb_get_device_descriptor(devlist[i], &des)) < 0) {
-                       sr_err("Failed to get device descriptor: %s.",
-                              libusb_error_name(ret));
-                       continue;
-               }
+               libusb_get_device_descriptor(devlist[i], &des);
 
                for (confidx = 0; confidx < des.bNumConfigurations; confidx++) {
                        if ((ret = libusb_get_config_descriptor(devlist[i], confidx, &confdes)) < 0) {
-                               sr_dbg("Failed to get configuration descriptor: %s, "
-                                      "ignoring device.", libusb_error_name(ret));
+                               if (ret != LIBUSB_ERROR_NOT_FOUND)
+                                       sr_dbg("Failed to get configuration descriptor: %s, "
+                                              "ignoring device.", libusb_error_name(ret));
                                break;
                        }
                        for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) {
@@ -142,7 +166,7 @@ static GSList *scpi_usbtmc_libusb_scan(struct drv_context *drvc)
        }
        libusb_free_device_list(devlist, 1);
 
-       sr_dbg("Found %d device(s).", g_slist_length(resources));
+       /* No log message for #devices found (caller will log that). */
 
        return resources;
 }
@@ -174,18 +198,110 @@ static int scpi_usbtmc_libusb_dev_inst_new(void *priv, struct drv_context *drvc,
        return SR_OK;
 }
 
-static int scpi_usbtmc_libusb_open(void *priv)
+static int check_usbtmc_blacklist(struct usbtmc_blacklist *blacklist,
+               uint16_t vid, uint16_t pid)
 {
-       struct scpi_usbtmc_libusb *uscpi = priv;
+       int i;
+
+       for (i = 0; blacklist[i].vid; i++) {
+               if ((blacklist[i].vid == vid && blacklist[i].pid == 0xFFFF) ||
+                       (blacklist[i].vid == vid && blacklist[i].pid == pid))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static int scpi_usbtmc_remote(struct scpi_usbtmc_libusb *uscpi)
+{
+       struct sr_usb_dev_inst *usb = uscpi->usb;
+       struct libusb_device *dev;
+       struct libusb_device_descriptor des;
+       int ret;
+       uint8_t status;
+
+       if (!(uscpi->usb488_dev_cap & USB488_DEV_CAP_RL1))
+               return SR_OK;
+
+       dev = libusb_get_device(usb->devhdl);
+       libusb_get_device_descriptor(dev, &des);
+       if (check_usbtmc_blacklist(blacklist_remote, des.idVendor, des.idProduct))
+               return SR_OK;
+
+       sr_dbg("Locking out local control.");
+       ret = libusb_control_transfer(usb->devhdl, LIBUSB_ENDPOINT_IN |
+               LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+               REN_CONTROL, 1, uscpi->interface, &status, 1, TRANSFER_TIMEOUT);
+       if (ret < 0 || status != USBTMC_STATUS_SUCCESS) {
+               if (ret < 0)
+                       sr_dbg("Failed to enter REN state: %s.", libusb_error_name(ret));
+               else
+                       sr_dbg("Failed to enter REN state: USBTMC status %d.", status);
+               return SR_ERR;
+       }
+
+       ret = libusb_control_transfer(usb->devhdl, LIBUSB_ENDPOINT_IN |
+               LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+               LOCAL_LOCKOUT, 0, uscpi->interface, &status, 1,
+               TRANSFER_TIMEOUT);
+       if (ret < 0 || status != USBTMC_STATUS_SUCCESS) {
+               if (ret < 0)
+                       sr_dbg("Failed to enter local lockout state: %s.",
+                                       libusb_error_name(ret));
+               else
+                       sr_dbg("Failed to enter local lockout state: USBTMC "
+                                       "status %d.", status);
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static void scpi_usbtmc_local(struct scpi_usbtmc_libusb *uscpi)
+{
+       struct sr_usb_dev_inst *usb = uscpi->usb;
+       struct libusb_device *dev;
+       struct libusb_device_descriptor des;
+       int ret;
+       uint8_t status;
+
+       if (!(uscpi->usb488_dev_cap & USB488_DEV_CAP_RL1))
+               return;
+
+       dev = libusb_get_device(usb->devhdl);
+       libusb_get_device_descriptor(dev, &des);
+       if (check_usbtmc_blacklist(blacklist_remote, des.idVendor, des.idProduct))
+               return;
+
+       sr_dbg("Returning local control.");
+       ret = libusb_control_transfer(usb->devhdl, LIBUSB_ENDPOINT_IN |
+               LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+               GO_TO_LOCAL, 0, uscpi->interface, &status, 1, TRANSFER_TIMEOUT);
+       if (ret < 0 || status != USBTMC_STATUS_SUCCESS) {
+               if (ret < 0)
+                       sr_dbg("Failed to clear local lockout state: %s.",
+                                       libusb_error_name(ret));
+               else
+                       sr_dbg("Failed to clear local lockout state: USBTMC "
+                                       "status %d.", status);
+       }
+
+       return;
+}
+
+static int scpi_usbtmc_libusb_open(struct sr_scpi_dev_inst *scpi)
+{
+       struct scpi_usbtmc_libusb *uscpi = scpi->priv;
        struct sr_usb_dev_inst *usb = uscpi->usb;
        struct libusb_device *dev;
        struct libusb_device_descriptor des;
        struct libusb_config_descriptor *confdes;
        const struct libusb_interface_descriptor *intfdes;
        const struct libusb_endpoint_descriptor *ep;
-       int confidx, intfidx, epidx, config = 0;
+       int confidx, intfidx, epidx, config = 0, current_config;
        uint8_t capabilities[24];
        int ret, found = 0;
+       int do_reset;
 
        if (usb->devhdl)
                return SR_OK;
@@ -194,22 +310,19 @@ static int scpi_usbtmc_libusb_open(void *priv)
                return SR_ERR;
 
        dev = libusb_get_device(usb->devhdl);
-       if ((ret = libusb_get_device_descriptor(dev, &des)) < 0) {
-               sr_err("Failed to get device descriptor: %s.",
-                      libusb_error_name(ret));
-               return SR_ERR;
-       }
+       libusb_get_device_descriptor(dev, &des);
 
        for (confidx = 0; confidx < des.bNumConfigurations; confidx++) {
                if ((ret = libusb_get_config_descriptor(dev, confidx, &confdes)) < 0) {
-                       sr_dbg("Failed to get configuration descriptor: %s, "
-                              "ignoring device.", libusb_error_name(ret));
+                       if (ret != LIBUSB_ERROR_NOT_FOUND)
+                               sr_dbg("Failed to get configuration descriptor: %s, "
+                                      "ignoring device.", libusb_error_name(ret));
                        continue;
                }
                for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) {
                        intfdes = confdes->interface[intfidx].altsetting;
                        if (intfdes->bInterfaceClass    != LIBUSB_CLASS_APPLICATION ||
-                           intfdes->bInterfaceSubClass != SUBCLASS_USBTMC          ||
+                           intfdes->bInterfaceSubClass != SUBCLASS_USBTMC ||
                            intfdes->bInterfaceProtocol != USBTMC_USB488)
                                continue;
                        uscpi->interface = intfdes->bInterfaceNumber;
@@ -234,8 +347,6 @@ static int scpi_usbtmc_libusb_open(void *priv)
                                }
                        }
                        found = 1;
-                       uscpi->rigol_ds1000 = des.idVendor  == 0x1ab1 &&
-                                             des.idProduct == 0x0588;
                }
                libusb_free_config_descriptor(confdes);
                if (found)
@@ -257,10 +368,13 @@ static int scpi_usbtmc_libusb_open(void *priv)
                uscpi->detached_kernel_driver = 1;
        }
 
-       if ((ret = libusb_set_configuration(usb->devhdl, config)) < 0) {
-               sr_err("Failed to set configuration: %s.",
-                      libusb_error_name(ret));
-               return SR_ERR;
+       if (libusb_get_configuration(usb->devhdl, &current_config) == 0
+           && current_config != config) {
+               if ((ret = libusb_set_configuration(usb->devhdl, config)) < 0) {
+                       sr_err("Failed to set configuration: %s.",
+                              libusb_error_name(ret));
+                       return SR_ERR;
+               }
        }
 
        if ((ret = libusb_claim_interface(usb->devhdl, uscpi->interface)) < 0) {
@@ -269,48 +383,46 @@ static int scpi_usbtmc_libusb_open(void *priv)
                return SR_ERR;
        }
 
-       if (!uscpi->rigol_ds1000) {
-       if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_in_ep)) < 0) {
-               sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-                      uscpi->bulk_in_ep, libusb_error_name(ret));
-               return SR_ERR;
-       }
-       if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_out_ep)) < 0) {
-               sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-                      uscpi->bulk_out_ep, libusb_error_name(ret));
-               return SR_ERR;
-       }
-       if ((ret = libusb_clear_halt(usb->devhdl, uscpi->interrupt_ep)) < 0) {
-               sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-                      uscpi->interrupt_ep, libusb_error_name(ret));
-               return SR_ERR;
-       }
-       }
+       /* Optionally reset the USB device. */
+       do_reset = check_usbtmc_blacklist(whitelist_usb_reset,
+               des.idVendor, des.idProduct);
+       if (do_reset)
+               libusb_reset_device(usb->devhdl);
 
        /* Get capabilities. */
-       ret = libusb_control_transfer(usb->devhdl,
-                                     LIBUSB_ENDPOINT_IN         |
-                                     LIBUSB_REQUEST_TYPE_CLASS  |
-                                     LIBUSB_RECIPIENT_INTERFACE,
-                                     GET_CAPABILITIES, 0,
-                                     uscpi->interface,
-                                     capabilities, sizeof(capabilities),
-                                     TRANSFER_TIMEOUT);
+       ret = libusb_control_transfer(usb->devhdl, LIBUSB_ENDPOINT_IN |
+               LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+               GET_CAPABILITIES, 0, uscpi->interface, capabilities,
+               sizeof(capabilities), TRANSFER_TIMEOUT);
        if (ret == sizeof(capabilities)) {
                uscpi->usbtmc_int_cap = capabilities[ 4];
                uscpi->usbtmc_dev_cap = capabilities[ 5];
                uscpi->usb488_dev_cap = capabilities[15];
        }
        sr_dbg("Device capabilities: %s%s%s%s%s, %s, %s",
-              uscpi->usb488_dev_cap & USB488_DEV_CAP_SCPI       ? "SCPI, "    : "",
-              uscpi->usbtmc_dev_cap & USBTMC_DEV_CAP_TERMCHAR   ? "TermChar, ": "",
-              uscpi->usbtmc_int_cap & USBTMC_INT_CAP_LISTEN_ONLY? "L3, " :
-              uscpi->usbtmc_int_cap & USBTMC_INT_CAP_TALK_ONLY  ? ""     : "L4, ",
-              uscpi->usbtmc_int_cap & USBTMC_INT_CAP_TALK_ONLY  ? "T5, " :
-              uscpi->usbtmc_int_cap & USBTMC_INT_CAP_LISTEN_ONLY? ""     : "T6, ",
-              uscpi->usb488_dev_cap & USB488_DEV_CAP_SR1        ? "SR1"  : "SR0",
-              uscpi->usb488_dev_cap & USB488_DEV_CAP_RL1        ? "RL1"  : "RL0",
-              uscpi->usb488_dev_cap & USB488_DEV_CAP_DT1        ? "DT1"  : "DT0");
+              uscpi->usb488_dev_cap & USB488_DEV_CAP_SCPI        ? "SCPI, "    : "",
+              uscpi->usbtmc_dev_cap & USBTMC_DEV_CAP_TERMCHAR    ? "TermChar, ": "",
+              uscpi->usbtmc_int_cap & USBTMC_INT_CAP_LISTEN_ONLY ? "L3, " :
+              uscpi->usbtmc_int_cap & USBTMC_INT_CAP_TALK_ONLY   ? ""     : "L4, ",
+              uscpi->usbtmc_int_cap & USBTMC_INT_CAP_TALK_ONLY   ? "T5, " :
+              uscpi->usbtmc_int_cap & USBTMC_INT_CAP_LISTEN_ONLY ? ""     : "T6, ",
+              uscpi->usb488_dev_cap & USB488_DEV_CAP_SR1         ? "SR1"  : "SR0",
+              uscpi->usb488_dev_cap & USB488_DEV_CAP_RL1         ? "RL1"  : "RL0",
+              uscpi->usb488_dev_cap & USB488_DEV_CAP_DT1         ? "DT1"  : "DT0");
+
+       scpi_usbtmc_remote(uscpi);
+
+       return SR_OK;
+}
+
+static int scpi_usbtmc_libusb_connection_id(struct sr_scpi_dev_inst *scpi,
+               char **connection_id)
+{
+       struct scpi_usbtmc_libusb *uscpi = scpi->priv;
+       struct sr_usb_dev_inst *usb = uscpi->usb;
+
+       *connection_id = g_strdup_printf("%s/%" PRIu8 ".%" PRIu8 "",
+               scpi->prefix, usb->bus, usb->address);
 
        return SR_OK;
 }
@@ -337,14 +449,14 @@ static void usbtmc_bulk_out_header_write(void *header, uint8_t MsgID,
                                          uint8_t bmTransferAttributes,
                                          char TermChar)
 {
-         W8(header+ 0, MsgID);
-         W8(header+ 1, bTag);
-         W8(header+ 2, ~bTag);
-         W8(header+ 3, 0);
-       WL32(header+ 4, TransferSize);
-         W8(header+ 8, bmTransferAttributes);
-         W8(header+ 9, TermChar);
-       WL16(header+10, 0);
+         W8(header +  0, MsgID);
+         W8(header +  1, bTag);
+         W8(header +  2, ~bTag);
+         W8(header +  3, 0);
+       WL32(header +  4, TransferSize);
+         W8(header +  8, bmTransferAttributes);
+         W8(header +  9, TermChar);
+       WL16(header + 10, 0);
 }
 
 static int usbtmc_bulk_in_header_read(void *header, uint8_t MsgID,
@@ -352,14 +464,15 @@ static int usbtmc_bulk_in_header_read(void *header, uint8_t MsgID,
                                       int32_t *TransferSize,
                                       uint8_t *bmTransferAttributes)
 {
-       if (R8(header+0) != MsgID ||
-           R8(header+1) != bTag  ||
-           R8(header+2) != (unsigned char)~bTag)
+       if (R8(header + 0) != MsgID ||
+           R8(header + 1) != bTag  ||
+           R8(header + 2) != (unsigned char)~bTag)
                return SR_ERR;
        if (TransferSize)
-               *TransferSize = RL32(header+4);
+               *TransferSize = RL32(header + 4);
        if (bmTransferAttributes)
-               *bmTransferAttributes = R8(header+8);
+               *bmTransferAttributes = R8(header + 8);
+
        return SR_OK;
 }
 
@@ -370,23 +483,23 @@ static int scpi_usbtmc_bulkout(struct scpi_usbtmc_libusb *uscpi,
        struct sr_usb_dev_inst *usb = uscpi->usb;
        int padded_size, ret, transferred;
 
-       if (data && size+USBTMC_BULK_HEADER_SIZE+3 > (int)sizeof(uscpi->buffer)) {
+       if (data && (size + USBTMC_BULK_HEADER_SIZE + 3) > (int)sizeof(uscpi->buffer)) {
                sr_err("USBTMC bulk out transfer is too big.");
                return SR_ERR;
        }
 
        uscpi->bTag++;
-       uscpi->bTag += !uscpi->bTag;  /* bTag == 0 is invalid so avoid it. */
+       uscpi->bTag += !uscpi->bTag; /* bTag == 0 is invalid so avoid it. */
 
        usbtmc_bulk_out_header_write(uscpi->buffer, msg_id, uscpi->bTag,
                                     size, transfer_attributes, 0);
        if (data)
-               memcpy(uscpi->buffer+USBTMC_BULK_HEADER_SIZE, data, size);
+               memcpy(uscpi->buffer + USBTMC_BULK_HEADER_SIZE, data, size);
        else
                size = 0;
        size += USBTMC_BULK_HEADER_SIZE;
        padded_size = (size + 3) & ~0x3;
-       memset(uscpi->buffer+size, 0, padded_size - size);
+       memset(uscpi->buffer + size, 0, padded_size - size);
 
        ret = libusb_bulk_transfer(usb->devhdl, uscpi->bulk_out_ep,
                                   uscpi->buffer, padded_size, &transferred,
@@ -411,14 +524,36 @@ static int scpi_usbtmc_bulkin_start(struct scpi_usbtmc_libusb *uscpi,
                                     uint8_t *transfer_attributes)
 {
        struct sr_usb_dev_inst *usb = uscpi->usb;
-       int ret, transferred, message_size;
+       int ret, transferred, message_size, tries;
+
+       for (tries = 0; ; tries++) {
+               ret = libusb_bulk_transfer(usb->devhdl, uscpi->bulk_in_ep, data,
+                                          size, &transferred,
+                                          TRANSFER_TIMEOUT);
+               if (ret < 0) {
+                       sr_err("USBTMC bulk in transfer error: %s.",
+                              libusb_error_name(ret));
+                       return SR_ERR;
+               }
 
-       ret = libusb_bulk_transfer(usb->devhdl, uscpi->bulk_in_ep, data, size,
-                                  &transferred, TRANSFER_TIMEOUT);
-       if (ret < 0) {
-               sr_err("USBTMC bulk in transfer error: %s.",
-                      libusb_error_name(ret));
-               return SR_ERR;
+               if (transferred == 0 && tries < 1) {
+                       /*
+                        * The DEV_DEP_MSG_IN message is empty, and the TMC
+                        * spec says it should at least contain a header.
+                        * The Rigol DS1054Z seems to do this sometimes, and
+                        * it follows up with a valid message.  Give the device
+                        * one more chance to send a header.
+                        */
+                       sr_warn("USBTMC bulk in start was empty; retrying\n");
+                       continue;
+               }
+
+               if (transferred < USBTMC_BULK_HEADER_SIZE) {
+                       sr_err("USBTMC bulk in returned too little data: %d/%d bytes\n", transferred, size);
+                       return SR_ERR;
+               }
+
+               break;
        }
 
        if (usbtmc_bulk_in_header_read(data, msg_id, uscpi->bTag, &message_size,
@@ -521,31 +656,21 @@ static int scpi_usbtmc_libusb_read_complete(void *priv)
               uscpi->bulkin_attributes & EOM;
 }
 
-static int scpi_usbtmc_libusb_close(void *priv)
+static int scpi_usbtmc_libusb_close(struct sr_scpi_dev_inst *scpi)
 {
-       int ret;
-       struct scpi_usbtmc_libusb *uscpi = priv;
+       struct scpi_usbtmc_libusb *uscpi = scpi->priv;
        struct sr_usb_dev_inst *usb = uscpi->usb;
+       int ret;
 
        if (!usb->devhdl)
                return SR_ERR;
 
-       if (!uscpi->rigol_ds1000) {
-       if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_in_ep)) < 0)
-               sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-                      uscpi->bulk_in_ep, libusb_error_name(ret));
-       if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_out_ep)) < 0)
-               sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-                      uscpi->bulk_out_ep, libusb_error_name(ret));
-       if ((ret = libusb_clear_halt(usb->devhdl, uscpi->interrupt_ep)) < 0)
-               sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-                      uscpi->interrupt_ep, libusb_error_name(ret));
-       }
+       scpi_usbtmc_local(uscpi);
 
        if ((ret = libusb_release_interface(usb->devhdl, uscpi->interface)) < 0)
                sr_err("Failed to release interface: %s.",
                       libusb_error_name(ret));
-       
+
        if (uscpi->detached_kernel_driver) {
                if ((ret = libusb_attach_kernel_driver(usb->devhdl,
                                                uscpi->interface)) < 0)
@@ -554,8 +679,7 @@ static int scpi_usbtmc_libusb_close(void *priv)
 
                uscpi->detached_kernel_driver = 0;
        }
-       libusb_close(usb->devhdl);
-       usb->devhdl = NULL;
+       sr_usb_close(usb);
 
        return SR_OK;
 }
@@ -569,10 +693,12 @@ static void scpi_usbtmc_libusb_free(void *priv)
 SR_PRIV const struct sr_scpi_dev_inst scpi_usbtmc_libusb_dev = {
        .name          = "USBTMC",
        .prefix        = "usbtmc",
+       .transport     = SCPI_TRANSPORT_USBTMC,
        .priv_size     = sizeof(struct scpi_usbtmc_libusb),
        .scan          = scpi_usbtmc_libusb_scan,
        .dev_inst_new  = scpi_usbtmc_libusb_dev_inst_new,
        .open          = scpi_usbtmc_libusb_open,
+       .connection_id = scpi_usbtmc_libusb_connection_id,
        .source_add    = scpi_usbtmc_libusb_source_add,
        .source_remove = scpi_usbtmc_libusb_source_remove,
        .send          = scpi_usbtmc_libusb_send,