X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Fdcttech-usbrelay%2Fapi.c;h=deeb234a95eb2e72e640e6a3d0bcc8e2b9e328a6;hb=77ee3168d0ae88c92a127999d890fff0511ca31e;hp=38d2512fcaeaf47b99ddd633317747ff450753fb;hpb=0498ef4e42b70e873167e2e5ff8520b692514df0;p=libsigrok.git diff --git a/src/hardware/dcttech-usbrelay/api.c b/src/hardware/dcttech-usbrelay/api.c index 38d2512f..deeb234a 100644 --- a/src/hardware/dcttech-usbrelay/api.c +++ b/src/hardware/dcttech-usbrelay/api.c @@ -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 "." 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;