diff --git a/iio-private.h b/iio-private.h index 71a46afef..9cc50a5c5 100644 --- a/iio-private.h +++ b/iio-private.h @@ -249,7 +249,7 @@ struct iio_context * serial_create_context_from_uri(const char *uri); int local_context_scan(struct iio_scan_result *scan_result); -int usb_context_scan(struct iio_scan_result *scan_result); +int usb_context_scan(struct iio_scan_result *scan_result, const char *args); int dnssd_context_scan(struct iio_scan_result *scan_result); diff --git a/iio.h b/iio.h index 07a1a8004..7442a9f3b 100644 --- a/iio.h +++ b/iio.h @@ -244,10 +244,14 @@ enum iio_event_direction { * * NOTE: Libiio version 0.20 and above can handle multiple * strings, for instance "local:usb:", "ip:usb:", "local:usb:ip:", and - * require a colon as the delimiter. If NULL, the local, USB and IP - * backends will be scanned. + * require a colon as the delimiter. * Libiio version 0.24 and above prefer a comma instead of colon as the - * delimiter. */ + * delimiter, and handle specifying backend-specific information. For + * instance, "local,usb=0456:*" will scan the local backend and limit + * scans on USB to vendor ID 0x0456, and accept all product IDs. The + * "usb=0456:b673" string would limit the scan to the device with this + * particular VID/PID. Both IDs are expected in hexadecimal, no 0x + * prefix needed. */ __api __check_ret struct iio_scan_context * iio_create_scan_context( const char *backend, unsigned int flags); diff --git a/scan.c b/scan.c index 4e9e72ebc..33f2ce2cf 100644 --- a/scan.c +++ b/scan.c @@ -42,8 +42,10 @@ ssize_t iio_scan_context_get_info_list(struct iio_scan_context *ctx, /* Since tokens are all null terminated, it's safe to use strcmp on them */ if (WITH_LOCAL_BACKEND && !strcmp(token, "local")) { ret = local_context_scan(&scan_result); - } else if (WITH_USB_BACKEND && !strcmp(token, "usb")) { - ret = usb_context_scan(&scan_result); + } else if (WITH_USB_BACKEND && (!strcmp(token, "usb") || + !strncmp(token, "usb=", sizeof("usb=") - 1))) { + token = token[3] == '=' ? token + 4 : NULL; + ret = usb_context_scan(&scan_result, token); } else if (HAVE_DNS_SD && !strcmp(token, "ip")) { ret = dnssd_context_scan(&scan_result); } else { @@ -108,6 +110,7 @@ struct iio_scan_context * iio_create_scan_context( const char *backend, unsigned int flags) { struct iio_scan_context *ctx; + char *ptr, *ptr2; unsigned int i, len; /* "flags" must be zero for now */ @@ -135,6 +138,18 @@ struct iio_scan_context * iio_create_scan_context( for (i = 0; i < len; i++) if (ctx->backendopts[i] == ':') ctx->backendopts[i] = ','; + + /* The only place where a colon is accepted is in the usb arguments: + * usb=vid:pid */ + for (ptr = strstr(ctx->backendopts, "usb="); ptr; + ptr = strstr(ptr, "usb=")) { + ptr += sizeof("usb="); + strtoul(ptr, &ptr2, 16); + + /* The USB backend will take care of errors */ + if (ptr2 != ptr && *ptr2 == ',') + *ptr2 = ':'; + } } return ctx; diff --git a/usb.c b/usb.c index 97a4496b9..e12fb332f 100644 --- a/usb.c +++ b/usb.c @@ -1200,14 +1200,58 @@ static int usb_fill_context_info(struct iio_context_info *info, return 0; } -int usb_context_scan(struct iio_scan_result *scan_result) +static int parse_vid_pid(const char *vid_pid, uint16_t *vid, uint16_t *pid) +{ + unsigned long val; + char *ptr; + + /* + * vid_pid string must be either: + * - NULL: scan everything, + * - "vid:*": scan all devices with the given VID, + * - "vid:pid": scan the device with the given VID/PID. + * IDs are given in hexadecimal, and the 0x prefix is not required. + */ + + *vid = 0; + *pid = 0; + + if (!vid_pid) + return 0; + + val = strtoul(vid_pid, &ptr, 16); + if (ptr == vid_pid || val > 0xFFFF || *ptr != ':') + return -EINVAL; + + *vid = (uint16_t) val; + + vid_pid = ptr + 1; + + if (*vid_pid == '*') + return vid_pid[1] == '\0' ? 0 : -EINVAL; + + val = strtoul(vid_pid, &ptr, 16); + if (ptr == vid_pid || val > 0xFFFF || *ptr != '\0') + return -EINVAL; + + *pid = (uint16_t) val; + + return 0; +} + +int usb_context_scan(struct iio_scan_result *scan_result, const char *args) { struct iio_context_info *info; libusb_device **device_list; libusb_context *ctx; + uint16_t vid, pid; unsigned int i; int ret; + ret = parse_vid_pid(args, &vid, &pid); + if (ret) + return ret; + ret = libusb_init(&ctx); if (ret < 0) return -(int) libusb_to_errno(ret); @@ -1222,6 +1266,21 @@ int usb_context_scan(struct iio_scan_result *scan_result) struct libusb_device_handle *hdl; struct libusb_device *dev = device_list[i]; unsigned int intrfc = 0; + struct libusb_device_descriptor device_descriptor; + + /* If we are given a pid or vid, use that to qualify for things, + * this avoids open/closing random devices & potentially locking + * (blocking them) from other applications + */ + if(vid || pid) { + ret = libusb_get_device_descriptor(dev, &device_descriptor); + if (ret) + continue; + if (vid && vid != device_descriptor.idVendor) + continue; + if (pid && pid != device_descriptor.idProduct) + continue; + } ret = libusb_open(dev, &hdl); if (ret)