diff --git a/NEWS b/NEWS index 8bb53f3e64..a640603a71 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,