]> sigrok.org Git - libsigrok.git/blobdiff - src/scpi/scpi_usbtmc_libusb.c
Constify a lot more items.
[libsigrok.git] / src / scpi / scpi_usbtmc_libusb.c
index 535ce54b3ddfab369a9476438dedab488d35c879..9f49dd1d2ea8ec2e8f2fd7446b4df1852eb232c0 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
+#include "scpi.h"
 
 #define LOG_PREFIX "scpi_usbtmc"
 
@@ -43,7 +45,6 @@ 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. */
@@ -68,6 +69,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
@@ -92,6 +96,18 @@ enum {
 #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 */
+       { 0x0957, 0x0588 },  /* Agilent DSO1000 series (rebadged Rigol DS1000) */
+       ALL_ZERO
+};
 
 static GSList *scpi_usbtmc_libusb_scan(struct drv_context *drvc)
 {
@@ -110,11 +126,7 @@ 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) {
@@ -174,16 +186,120 @@ 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)
+{
+       int i;
+
+       for (i = 0; blacklist[i].vid; i++) {
+               if (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, 1,
+                       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 scpi_usbtmc_libusb *uscpi = priv;
+       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, 1,
+                       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;
 
@@ -194,11 +310,7 @@ 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) {
@@ -234,8 +346,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 +367,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,24 +382,6 @@ 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;
-       }
-       }
-
        /* Get capabilities. */
        ret = libusb_control_transfer(usb->devhdl,
                                      LIBUSB_ENDPOINT_IN         |
@@ -312,6 +407,8 @@ static int scpi_usbtmc_libusb_open(void *priv)
               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;
 }
 
@@ -521,26 +618,16 @@ 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.",
@@ -554,8 +641,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;
 }