diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c --- a/lib/libusb/libusb10.c +++ b/lib/libusb/libusb10.c @@ -670,6 +670,7 @@ struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; int i; int j; + int ok; ctx = GET_CONTEXT(ctx); if (ctx == NULL) @@ -693,8 +694,12 @@ */ if (pdesc->idVendor == vendor_id && pdesc->idProduct == product_id) { - libusb_open(devs[j], &pdev); - break; + ok = libusb_open(devs[j], &pdev); + /* + * If this fails, try the next one. + */ + if (ok == 0) + break; } } diff --git a/lib/libusb/libusb20.h b/lib/libusb/libusb20.h --- a/lib/libusb/libusb20.h +++ b/lib/libusb/libusb20.h @@ -173,6 +173,14 @@ LIBUSB20_POWER_RESUME, }; +/** \ingroup misc + * libusb20_dev_open_with_flags values + */ +enum libusb20_open_flags { + LIBUSB20_OPEN_SHARED = 0, + LIBUSB20_OPEN_EXCLUSIVE = 1, +}; + struct usb_device_info; struct libusb20_transfer; struct libusb20_backend; @@ -252,6 +260,8 @@ int libusb20_dev_get_stats(struct libusb20_device *pdev, struct libusb20_device_stats *pstat); int libusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t iface_index); int libusb20_dev_open(struct libusb20_device *pdev, uint16_t transfer_max); +int libusb20_dev_open_exclusive(struct libusb20_device *pdev, uint16_t transfer_max); +int libusb20_dev_open_with_flags(struct libusb20_device *pdev, uint16_t transfer_max, int flags); int libusb20_dev_process(struct libusb20_device *pdev); int libusb20_dev_request_sync(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags); int libusb20_dev_req_string_sync(struct libusb20_device *pdev, uint8_t index, uint16_t langid, void *ptr, uint16_t len); diff --git a/lib/libusb/libusb20.c b/lib/libusb/libusb20.c --- a/lib/libusb/libusb20.c +++ b/lib/libusb/libusb20.c @@ -657,8 +657,9 @@ return (error); } +/* Only flag implemented is LIBUSB20_OPEN_EXCLUSIVE. */ int -libusb20_dev_open(struct libusb20_device *pdev, uint16_t nTransferMax) +libusb20_dev_open_with_flags(struct libusb20_device *pdev, uint16_t nTransferMax, int flags) { struct libusb20_transfer *xfer; uint32_t size; @@ -691,7 +692,7 @@ /* set "nTransfer" early */ pdev->nTransfer = nTransferMax; - error = pdev->beMethods->open_device(pdev, nTransferMax); + error = pdev->beMethods->open_device(pdev, nTransferMax, flags); if (error) { if (pdev->pTransfer != NULL) { @@ -707,6 +708,12 @@ return (error); } +int +libusb20_dev_open(struct libusb20_device *pdev, uint16_t nTransferMax) +{ + return libusb20_dev_open_with_flags(pdev, nTransferMax, LIBUSB20_OPEN_EXCLUSIVE); +} + int libusb20_dev_reset(struct libusb20_device *pdev) { diff --git a/lib/libusb/libusb20_dev_open.3 b/lib/libusb/libusb20_dev_open.3 new file mode 100644 --- /dev/null +++ b/lib/libusb/libusb20_dev_open.3 @@ -0,0 +1,110 @@ +.\" +.\" Copyright (c) 2025 Rick Parrish +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.Dd December 14, 2025 +.Dt LIBUSB20 3 +.Os +.Sh NAME +.Nm libusb20_dev_open +.Nd open a USB device to retrieve descriptors and initiate transfers +.Sh SYNOPSIS +.Lb libusb +.In libusb20.h +.Ft int +.Fn libusb20_dev_open "struct libusb20_device *pdev" "uin16_t transfer_max" +.Ft int +.Fn libusb20_dev_open_with_flags "struct libusb20_device *pdev" "uin16_t transfer_max" "int flags" +.Sh DESCRIPTION +The +.Nm +and +.Fn libusb20_dev_open_with_flags +functions open a USB device to retrieve descriptors and initiate transfers. +.Nm +accepts a pointer as +.Fa pdev +to a +.Xr libusb20_device 3 +obtained from +.Xr libusb20_be_device_foreach 3 . +A zero for +.Fa transfer_max +limits the device to only control transfers. +.Fn libusb20_dev_open_with_flags +also accepts a +.Fa flags +argument which may be LIBUSB20_OPEN_SHARED or LIBUSB20_OPEN_EXCLUSIVE. +Calling +.Nm +is the same as calling +.Fn libusb20_dev_open_with_flags +with +.Fa flags +set to LIBUSB20_OPEN_EXCLUSIVE. +Call +.Xr libusb20_dev_close 3 +to free resources taken by the open device handle. +.Sh ERRORS +.Nm +and +.Fn libusb20_dev_open_with_flags +return one of the following to report success or failure: +.Bl -tag -width "LIBUSB20_ERROR_NO_DEVICE" -compact +.It Er LIBUSB20_SUCCESS +The operation succeeds. +.It Er LIBUSB20_ERROR_BUSY +The device is in use elsewhere. +.It Er LIBUSB20_ERROR_ACCESS +A permissions issue. +.It Er LIBUSB20_ERROR_NO_DEVICE +The device detached. +.It Er LIBUSB20_ERROR_NO_MEM +This library could not allocate memory. +.It More errors in +.Xr libusb20 3 +and +.In libusb20.h +.El +.Sh EXAMPLES +.Nm +.Bd -literal + #include + struct libusb20_backend *be = libusb20_be_alloc_default(); + struct libusb20_device *device = NULL; + while ( (device = libusb20_be_device_foreach(be, device)) != NULL ) { + code = libusb20_dev_open(device, 4); + if (code == LIBUSB20_SUCCESS) { + /* do something */ + libusb20_dev_close(device); + } + else if (code == LIBUSB20_ERROR_BUSY) { + printf("Device already in use.\n"); + } + } + libusb20_be_free(be); +.Ed +.Fn libusb20_dev_open_with_flags +.Bd -literal + #include + int code; + struct libusb20_backend *be = libusb20_be_alloc_default(); + struct libusb20_device *device = NULL; + while ( (device = libusb20_be_device_foreach(be, device)) != NULL ) { + code = libusb20_dev_open_with_flags(device, 4, LIBUSB20_OPEN_EXCLUSIVE); + if (code == LIBUSB20_SUCCESS) { + /* do something */ + libusb20_dev_close(device); + } + else if (code == LIBUSB20_ERROR_BUSY) { + printf("Device already in use.\n"); + } + } + libusb20_be_free(be); +.Ed +.Pg +.Sh SEE ALSO +.Xr libusb20_be_device_foreach 3 , +.Xr libusb20_dev_close 3 , +.Xr libusb20 3 diff --git a/lib/libusb/libusb20_int.h b/lib/libusb/libusb20_int.h --- a/lib/libusb/libusb20_int.h +++ b/lib/libusb/libusb20_int.h @@ -53,7 +53,7 @@ typedef int (libusb20_dev_get_info_t)(struct libusb20_device *pdev, struct usb_device_info *pinfo); typedef int (libusb20_dev_get_iface_desc_t)(struct libusb20_device *pdev, uint8_t iface_index, char *buf, uint8_t len); typedef int (libusb20_init_backend_t)(struct libusb20_backend *pbe); -typedef int (libusb20_open_device_t)(struct libusb20_device *pdev, uint16_t transfer_count_max); +typedef int (libusb20_open_device_t)(struct libusb20_device *pdev, uint16_t transfer_count_max, int flags); typedef void (libusb20_exit_backend_t)(struct libusb20_backend *pbe); typedef int (libusb20_root_set_template_t)(struct libusb20_backend *pbe, int temp); typedef int (libusb20_root_get_template_t)(struct libusb20_backend *pbe, int *ptemp); diff --git a/lib/libusb/libusb20_ugen20.c b/lib/libusb/libusb20_ugen20.c --- a/lib/libusb/libusb20_ugen20.c +++ b/lib/libusb/libusb20_ugen20.c @@ -375,13 +375,13 @@ } static int -ugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer) +ugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer, int flags) { uint32_t plugtime; char buf[64]; int f; int g; - int error; + int error, arg; snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u", pdev->bus_number, pdev->device_address); @@ -409,6 +409,13 @@ error = LIBUSB20_ERROR_NO_DEVICE; goto done; } + arg = ((flags & LIBUSB20_OPEN_EXCLUSIVE) != 0) ? LOCK_EX|LOCK_NB : LOCK_SH|LOCK_NB; + if (flock(f, arg) != 0) { + close(f); + close(g); + error = LIBUSB20_ERROR_BUSY; + goto done; + } /* need to set this before "tr_renew()" */ pdev->file = f; pdev->file_ctrl = g;