Skip to content

Commit

Permalink
usb: when scanning provide option to qualify USB vendor ID or product ID
Browse files Browse the repository at this point in the history
When we do a USB scan, we open each device looking for a string "IIO",
and if it is not, then we close the device. The archirecture of libusb
is that when we are doing this, other applications are locked out from
using that device. This is known to cause some user problems on other
devices (not IIO) - most notibly HackRF and USRP.

This change adds the ability to restricting scans to certain vendors, or
specific product IDs - which is what most purpose build software is
looking for. (GNU Radio doesn"t want to open every device, only the ones
it knows about).

This should allow us to play nicer with others. Examples:
 usb,local
 local,usb=0456:b673,usb=0456:b672
 usb=0456:*

VENDOR ID and PRODUCT ID are hexadecimal numbers (no prefix needed),
"*" (match any). By default (no vid:pid provided) all devices are opened
and checked (which is the previous behavour).

Signed-off-by: Robin Getz <[email protected]>
Signed-off-by: Paul Cercueil <[email protected]>
  • Loading branch information
rgetz authored and pcercuei committed Feb 17, 2022
1 parent f272886 commit d8c87b5
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 7 deletions.
2 changes: 1 addition & 1 deletion iio-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
10 changes: 7 additions & 3 deletions iio.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,14 @@ enum iio_event_direction {
*
* <b>NOTE:</b> 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);

Expand Down
19 changes: 17 additions & 2 deletions scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down
61 changes: 60 additions & 1 deletion usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
Expand Down

0 comments on commit d8c87b5

Please sign in to comment.