]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/dcttech-usbrelay/api.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / hardware / dcttech-usbrelay / api.c
index 38d2512fcaeaf47b99ddd633317747ff450753fb..deeb234a95eb2e72e640e6a3d0bcc8e2b9e328a6 100644 (file)
@@ -45,6 +45,7 @@ static const uint32_t devopts_cg[] = {
 static struct sr_dev_driver dcttech_usbrelay_driver_info;
 
 static struct sr_dev_inst *probe_device_common(const char *path,
+       uint16_t vid, uint16_t pid, const char *want_serno,
        const wchar_t *vendor, const wchar_t *product)
 {
        char nonws[16], *s, *endp;
@@ -62,6 +63,7 @@ static struct sr_dev_inst *probe_device_common(const char *path,
        struct channel_group_context *cgc;
        size_t idx, nr;
        struct sr_channel_group *cg;
+       char cg_name[24];
 
        /*
         * Get relay count from product string. Weak condition,
@@ -80,7 +82,10 @@ static struct sr_dev_inst *probe_device_common(const char *path,
        sr_info("Relay count %lu from product string %s.", relay_count, nonws);
 
        /* Open device, need to communicate to identify. */
-       hid = hid_open_path(path);
+       if (vid && pid)
+               hid = hid_open(vid, pid, NULL);
+       else
+               hid = hid_open_path(path);
        if (!hid) {
                sr_err("Cannot open %s.", path);
                return NULL;
@@ -124,6 +129,12 @@ static struct sr_dev_inst *probe_device_common(const char *path,
        sr_info("HID report data: serial number %s, relay state 0x%02x.",
                serno, curr_state);
 
+       /* Optionally filter by serial number. */
+       if (want_serno && *want_serno && strcmp(serno, want_serno) != 0) {
+               sr_dbg("Serial number does not match user spec. Skipping.");
+               return NULL;
+       }
+
        /* Create a device instance. */
        sdi = g_malloc0(sizeof(*sdi));
        sdi->vendor = g_strdup_printf("%ls", vendor);
@@ -137,50 +148,81 @@ static struct sr_dev_inst *probe_device_common(const char *path,
        devc = g_malloc0(sizeof(*devc));
        sdi->priv = devc;
        devc->hid_path = g_strdup(path);
+       devc->usb_vid = vid;
+       devc->usb_pid = pid;
        devc->relay_count = relay_count;
        devc->relay_mask = (1U << relay_count) - 1;
        for (idx = 0; idx < devc->relay_count; idx++) {
                nr = idx + 1;
-               cg = g_malloc0(sizeof(*cg));
-               cg->name = g_strdup_printf("R%zu", nr);
+               snprintf(cg_name, sizeof(cg_name), "R%zu", nr);
                cgc = g_malloc0(sizeof(*cgc));
-               cg->priv = cgc;
                cgc->number = nr;
-               sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+               cg = sr_channel_group_new(sdi, cg_name, cgc);
+               (void)cg;
        }
 
        return sdi;
 }
 
-static struct sr_dev_inst *probe_device_enum(struct hid_device_info *dev)
+static struct sr_dev_inst *probe_device_enum(struct hid_device_info *dev,
+       const char *want_serno)
 {
-       return probe_device_common(dev->path,
+       return probe_device_common(dev->path, 0, 0, want_serno,
                dev->manufacturer_string, dev->product_string);
 }
 
-static struct sr_dev_inst *probe_device_path(const char *path)
+static struct sr_dev_inst *probe_device_conn(const char *path)
 {
+       char vid_pid[12];
+       uint16_t vid, pid;
+       const char *s;
+       char *endp;
+       unsigned long num;
        hid_device *dev;
        gboolean ok;
        int ret;
        wchar_t vendor[32], product[32];
 
        /*
-        * TODO Accept different types of conn= specs? Either paths that
-        * hidapi(3) can open. Or bus.addr specs that we can check for
-        * during USB enumeration and derive a hidapi(3) compatible path
-        * from? Is some "unescaping" desirable for platforms which have
-        * colons in hidapi(3) paths that collide with how conn= specs
-        * are passed in sigrok? This would be the place to translate
-        * the 'path' to a canonical format.
+        * The hidapi(3) library's API strives for maximum portability,
+        * thus won't provide ways of getting a path from alternative
+        * presentations like VID:PID pairs, bus.addr specs, etc. The
+        * typical V-USB setup neither provides reliable serial numbers
+        * (that USB enumeration would cover). So this driver's support
+        * for conn= specs beyond Unix style path names is limited, too.
+        * This implementation tries "VID.PID" then assumes "path". The
+        * inability to even get the path for a successfully opened HID
+        * results in redundancy across the places which open devices.
         */
 
-       dev = hid_open_path(path);
+       /* Check for "<vid>.<pid>" specs. */
+       vid = pid = 0;
+       s = path;
+       ret = sr_atoul_base(s, &num, &endp, 16);
+       if (ret == SR_OK && endp && endp == s + 4 && *endp == '.' && num) {
+               vid = num;
+               s = ++endp;
+       }
+       ret = sr_atoul_base(s, &num, &endp, 16);
+       if (ret == SR_OK && endp && endp == s + 4 && *endp == '\0' && num) {
+               pid = num;
+               s = ++endp;
+       }
+       if (vid && pid) {
+               snprintf(vid_pid, sizeof(vid_pid), "%04x.%04x", vid, pid);
+               path = vid_pid;
+               sr_dbg("Using VID.PID %s.", path);
+       }
+
+       /* Open the device, get vendor and product strings. */
+       if (vid && pid)
+               dev = hid_open(vid, pid, NULL);
+       else
+               dev = hid_open_path(path);
        if (!dev) {
                sr_err("Cannot open %s.", path);
                return NULL;
        }
-
        ok = TRUE;
        ret = hid_get_manufacturer_string(dev, vendor, ARRAY_SIZE(vendor));
        if (ret != 0)
@@ -196,7 +238,7 @@ static struct sr_dev_inst *probe_device_path(const char *path)
        if (!ok)
                return NULL;
 
-       return probe_device_common(path, vendor, product);
+       return probe_device_common(path, vid, pid, NULL, vendor, product);
 }
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
@@ -204,6 +246,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        const char *conn;
        GSList *devices;
        struct drv_context *drvc;
+       char want_serno[SERNO_LENGTH + 1];
        struct hid_device_info *devs, *curdev;
        wchar_t *ws;
        char nonws[32];
@@ -222,17 +265,28 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        /*
         * The firmware is V-USB based. The USB VID:PID identification
         * is shared across several projects. Need to inspect the vendor
-        * and product _strings_ to actually identify the device. The
-        * USB serial number need not be present nor reliable. The HID
-        * report content will carry the board's serial number.
+        * and product _strings_ to actually identify the device.
+        *
+        * The USB serial number need not be present nor reliable. The
+        * HID report content will carry the board's serial number.
+        * When users specify "sn=..." connection strings, then run a
+        * regular USB enumation, and filter the result set by serial
+        * numbers which only become available with HID reports.
         *
-        * When conn= was specified, then have HIDAPI open _this_ device
-        * and skip the enumeration. Which allows users to specify paths
-        * that need not match the enumeration's details.
+        * When other connection strings were specified, then have
+        * HIDAPI open _this_ device and skip the enumeration. Which
+        * allows users to specify paths that need not match the
+        * enumeration's details.
         */
+       memset(want_serno, 0, sizeof(want_serno));
+       if (conn && g_str_has_prefix(conn, "sn=")) {
+               conn += strlen("sn=");
+               snprintf(want_serno, sizeof(want_serno), "%s", conn);
+               conn = NULL;
+       }
        if (conn) {
                sr_info("Checking HID path %s.", conn);
-               sdi = probe_device_path(conn);
+               sdi = probe_device_conn(conn);
                if (!sdi)
                        sr_warn("Failed to communicate to %s.", conn);
                else
@@ -268,11 +322,9 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
                /* Identify device by communicating to it. */
                sr_info("Checking HID path %s.", curdev->path);
-               sdi = probe_device_enum(curdev);
-               if (!sdi) {
-                       sr_warn("Failed to communicate to %s.", curdev->path);
+               sdi = probe_device_enum(curdev, want_serno);
+               if (!sdi)
                        continue;
-               }
                devices = g_slist_append(devices, sdi);
        }
        hid_free_enumeration(devs);
@@ -291,7 +343,10 @@ static int dev_open(struct sr_dev_inst *sdi)
                devc->hid_dev = NULL;
        }
 
-       devc->hid_dev = hid_open_path(devc->hid_path);
+       if (devc->usb_vid && devc->usb_pid)
+               devc->hid_dev = hid_open(devc->usb_vid, devc->usb_pid, NULL);
+       else
+               devc->hid_dev = hid_open_path(devc->hid_path);
        if (!devc->hid_dev)
                return SR_ERR_IO;