Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce an allow_duplicates flag for common USB matching options #1770

Merged
merged 3 commits into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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