From edf1e3006ea59380d3bc0575f7e29169de6cdc3c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Jan 2023 00:26:43 +0100 Subject: [PATCH 1/2] drivers/libusb{0,1}.c, drivers/usb-common.c: update comments in device matcher code --- drivers/libusb0.c | 5 ++++- drivers/libusb1.c | 15 ++++++++++----- drivers/usb-common.c | 3 +++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/libusb0.c b/drivers/libusb0.c index eef5dd2d51..acd475299d 100644 --- a/drivers/libusb0.c +++ b/drivers/libusb0.c @@ -363,6 +363,9 @@ static int libusb_open(usb_dev_handle **udevp, goto next_device; } } + + /* If we got here, none of the matchers said + * that the device is not what we want. */ upsdebugx(2, "Device matches"); /* Now we have matched the device we wanted. Claim it. */ @@ -375,8 +378,8 @@ static int libusb_open(usb_dev_handle **udevp, #ifdef WIN32 usb_set_configuration(udev, 1); #endif - while (usb_claim_interface(udev, usb_subdriver.hid_rep_index) < 0) { + while (usb_claim_interface(udev, usb_subdriver.hid_rep_index) < 0) { upsdebugx(2, "failed to claim USB device: %s", usb_strerror()); diff --git a/drivers/libusb1.c b/drivers/libusb1.c index 6627ebc83d..5ba241a3fc 100644 --- a/drivers/libusb1.c +++ b/drivers/libusb1.c @@ -331,8 +331,10 @@ static int nut_libusb_open(libusb_device_handle **udevp, goto next_device; } } - upsdebugx(2, "Device matches"); + /* If we got here, none of the matchers said + * that the device is not what we want. */ + upsdebugx(2, "Device matches"); upsdebugx(2, "Reading first configuration descriptor"); ret = libusb_get_config_descriptor(device, @@ -379,20 +381,23 @@ static int nut_libusb_open(libusb_device_handle **udevp, /* TODO: Align with libusb1 - initially from Windows branch made against libusb0 */ libusb_set_configuration(udev, 1); #endif + while ((ret = libusb_claim_interface(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { upsdebugx(2, "failed to claim USB device: %s", libusb_strerror((enum libusb_error)ret)); # ifdef HAVE_LIBUSB_DETACH_KERNEL_DRIVER - if ((ret = libusb_detach_kernel_driver(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { + if ((ret = libusb_detach_kernel_driver(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { # else /* if defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) */ - if ((ret = libusb_detach_kernel_driver_np(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { + if ((ret = libusb_detach_kernel_driver_np(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { # endif - if (ret == LIBUSB_ERROR_NOT_FOUND) + if (ret == LIBUSB_ERROR_NOT_FOUND) { + /* logged as "Entity not found" if this persists */ upsdebugx(2, "Kernel driver already detached"); - else + } else { upsdebugx(1, "failed to detach kernel driver from USB device: %s", libusb_strerror((enum libusb_error)ret)); + } } else { upsdebugx(2, "detached kernel driver from USB device..."); } diff --git a/drivers/usb-common.c b/drivers/usb-common.c index 5f2dc168d1..a45a621168 100644 --- a/drivers/usb-common.c +++ b/drivers/usb-common.c @@ -311,6 +311,9 @@ static int match_function_regex(USBDevice_t *hd, void *privdata) upsdebugx(3, "%s: matching a device...", __func__); + /* NOTE: Here and below the "detailed" logging is commented away + * because data->regex[] elements are not strings anymore! */ + r = match_regex_hex(data->regex[0], hd->VendorID); if (r != 1) { /* From 320d401e48dcf0950016d8dcab5f2b34bfa9424c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Jan 2023 02:32:18 +0100 Subject: [PATCH 2/2] Introduce an `allow_duplicates` flag for common USB matching options [#1756] --- NEWS | 4 ++++ docs/man/nut_usb_addvars.txt | 23 +++++++++++++++++++++++ drivers/libusb0.c | 25 +++++++++++++++++++++++-- drivers/libusb1.c | 21 +++++++++++++++++++++ 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 9afce2500f..45596a56b3 100644 --- a/NEWS +++ b/NEWS @@ -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] diff --git a/docs/man/nut_usb_addvars.txt b/docs/man/nut_usb_addvars.txt index 3f6b3f5ccf..0ad8547fa2 100644 --- a/docs/man/nut_usb_addvars.txt +++ b/docs/man/nut_usb_addvars.txt @@ -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 diff --git a/drivers/libusb0.c b/drivers/libusb0.c index acd475299d..8d8c9e1bf0 100644 --- a/drivers/libusb0.c +++ b/drivers/libusb0.c @@ -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)"); @@ -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()); @@ -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, diff --git a/drivers/libusb1.c b/drivers/libusb1.c index 5ba241a3fc..80154a99b4 100644 --- a/drivers/libusb1.c +++ b/drivers/libusb1.c @@ -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 @@ -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) */ @@ -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,