Skip to content

Commit

Permalink
Introduce an allow_duplicates flag for common USB matching options [#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jimklimov committed Jan 5, 2023
1 parent edf1e30 commit 320d401
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 2 deletions.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ https://github.com/networkupstools/nut/milestone/8
of all USB drivers using these options to include the same description
[#1766]

- Added an `allow_duplicates` flag for common USB matching options which
may help monitor several related no-name devices (although without knowing
reliably which one is which... better than nothing) [#1756]

- Stuck drivers that do not react to `SIGTERM` quickly are now retried with
`SIGKILL` [#1424]

Expand Down
23 changes: 23 additions & 0 deletions docs/man/nut_usb_addvars.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,29 @@ connected (e.g. `device="001"` or `device="00[1-2]"`) as seen in
Note that device numbers are not guaranteed by the OS to be stable across
re-boots or device re-plugging.

*allow_duplicates*::

If you have several UPS devices which may not be uniquely identified by
the options above (e.g. only VID:PID can be discovered there), this flag
allows each driver instance where it is set to take the first match if
available, or proceed to try another.
+
Normally the driver initialization would abort at this point claiming
"Resource busy" or similar error, assuming that the otherwise properly
matched device is unique -- and some other process already handles it.
+
[WARNING]
=========
This feature is inherently non-deterministic!
The association of driver instance name to actual device
may vary between runs!

If you only care to know that *at least* one of your no-name UPSes
is online, this option can help.

If you must really know *which* one, it will not!
=========

*usb_set_altinterface =* 'bAlternateSetting'::

Force redundant call to `usb_set_altinterface()`, especially if needed
Expand Down
25 changes: 23 additions & 2 deletions drivers/libusb0.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,17 @@ void nut_usb_addvars(void)

addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name");
addvar(VAR_VALUE, "device", "Regular expression to match USB device name");

/* Warning: this feature is inherently non-deterministic!
* If you only care to know that at least one of your no-name UPSes is online,
* this option can help. If you must really know which one, it will not!
*/
addvar(VAR_FLAG, "allow_duplicates",
"If you have several UPS devices which may not be uniquely "
"identified by options above, allow each driver instance with this "
"option to take the first match if available, or try another "
"(association of driver to device may vary between runs)");

addvar(VAR_VALUE, "usb_set_altinterface", "Force redundant call to usb_set_altinterface() (value=bAlternateSetting; default=0)");

dstate_setinfo("driver.version.usb", "libusb-0.1 (or compat)");
Expand Down Expand Up @@ -379,10 +390,15 @@ static int libusb_open(usb_dev_handle **udevp,
usb_set_configuration(udev, 1);
#endif

while (usb_claim_interface(udev, usb_subdriver.hid_rep_index) < 0) {
while ((ret = usb_claim_interface(udev, usb_subdriver.hid_rep_index)) < 0) {
upsdebugx(2, "failed to claim USB device: %s",
usb_strerror());

if (ret == LIBUSB_ERROR_BUSY && testvar("allow_duplicates")) {
upsdebugx(2, "Configured to allow_duplicates so looking for another similar device");
goto next_device;
}

if (usb_detach_kernel_driver_np(udev, usb_subdriver.hid_rep_index) < 0) {
upsdebugx(2, "failed to detach kernel driver from USB device: %s",
usb_strerror());
Expand All @@ -402,7 +418,12 @@ static int libusb_open(usb_dev_handle **udevp,
usb_strerror());
}
#else
if (usb_claim_interface(udev, usb_subdriver.hid_rep_index) < 0) {
if ((ret = usb_claim_interface(udev, usb_subdriver.hid_rep_index)) < 0) {
if (ret == LIBUSB_ERROR_BUSY && testvar("allow_duplicates")) {
upsdebugx(2, "Configured to allow_duplicates so looking for another similar device");
goto next_device;
}

fatalx(EXIT_FAILURE,
"Can't claim USB device [%04x:%04x]@%d/%d: %s",
curDevice->VendorID, curDevice->ProductID,
Expand Down
21 changes: 21 additions & 0 deletions drivers/libusb1.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ void nut_usb_addvars(void)

addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name");
addvar(VAR_VALUE, "device", "Regular expression to match USB device name");

/* Warning: this feature is inherently non-deterministic!
* If you only care to know that at least one of your no-name UPSes is online,
* this option can help. If you must really know which one, it will not!
*/
addvar(VAR_FLAG, "allow_duplicates",
"If you have several UPS devices which may not be uniquely "
"identified by options above, allow each driver instance with this "
"option to take the first match if available, or try another "
"(association of driver to device may vary between runs)");

addvar(VAR_VALUE, "usb_set_altinterface", "Force redundant call to usb_set_altinterface() (value=bAlternateSetting; default=0)");

#ifdef LIBUSB_API_VERSION
Expand Down Expand Up @@ -386,6 +397,11 @@ static int nut_libusb_open(libusb_device_handle **udevp,
upsdebugx(2, "failed to claim USB device: %s",
libusb_strerror((enum libusb_error)ret));

if (ret == LIBUSB_ERROR_BUSY && testvar("allow_duplicates")) {
upsdebugx(2, "Configured to allow_duplicates so looking for another similar device");
goto next_device;
}

# ifdef HAVE_LIBUSB_DETACH_KERNEL_DRIVER
if ((ret = libusb_detach_kernel_driver(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) {
# else /* if defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) */
Expand Down Expand Up @@ -417,6 +433,11 @@ static int nut_libusb_open(libusb_device_handle **udevp,
}
#else
if ((ret = libusb_claim_interface(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS ) {
if (ret == LIBUSB_ERROR_BUSY && testvar("allow_duplicates")) {
upsdebugx(2, "Configured to allow_duplicates so looking for another similar device");
goto next_device;
}

libusb_free_config_descriptor(conf_desc);
libusb_free_device_list(devlist, 1);
fatalx(EXIT_FAILURE,
Expand Down

0 comments on commit 320d401

Please sign in to comment.