From aba6f43544b17d2f16e69ca19f162c6c19748fc7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 27 Oct 2025 00:56:08 +0100 Subject: [PATCH 1/2] drivers/libusb{0,1}.c, NEWS.adoc: if the tried "rdlen" did not succeed, fall back to the other value we had in mind [#3136] In libusb1.c also fix printout of rdlen variables from %d to %PRIi32 Signed-off-by: Jim Klimov --- NEWS.adoc | 4 ++ drivers/libusb0.c | 122 ++++++++++++++++++++++--------------- drivers/libusb1.c | 150 +++++++++++++++++++++++++++------------------- 3 files changed, 166 insertions(+), 110 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 367eb36e77..2c5d51eb66 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -49,6 +49,10 @@ https://github.com/networkupstools/nut/milestone/12 stable name as a default implementation). Drivers united by `main.c` framework introduced a `upsdrv_tweak_prognames()` hook method to let them manipulate the array of `prognames[]` aliases. [PR #3101] + * `libusb0` and `libusb1` integrations now try both "Resource Descriptor + Length" values they could identify on some devices: sometimes the first + (or otherwise preferred) option may be not the correct one, so try the + other too. [issue #3136] - `asem`, `bestfortress`, `bestuferrups`, `bicker_ser`, `everups`, `metasys`, `masterguard`, `mge-utalk`, `oneac`, `phoenixcontact_modbus`, `pijuice`, diff --git a/drivers/libusb0.c b/drivers/libusb0.c index 22bf95da79..aa29c1685b 100644 --- a/drivers/libusb0.c +++ b/drivers/libusb0.c @@ -38,7 +38,7 @@ #endif /* WIN32 */ #define USB_DRIVER_NAME "USB communication driver (libusb 0.1)" -#define USB_DRIVER_VERSION "0.50" +#define USB_DRIVER_VERSION "0.51" /* driver description structure */ upsdrv_info_t comm_upsdrv_info = { @@ -219,7 +219,7 @@ static int nut_libusb_open(usb_dev_handle **udevp, #ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP int retries; #endif - usb_ctrl_charbufsize rdlen1, rdlen2; /* report descriptor length, method 1+2 */ + usb_ctrl_charbufsize rdlen1, rdlen2, rdlens[2]; /* report descriptor length, method 1+2, then an array to iterate them (if both) in chosen order */ USBDeviceMatcher_t *m; struct usb_device *dev; struct usb_bus *bus; @@ -231,13 +231,14 @@ static int nut_libusb_open(usb_dev_handle **udevp, usb_ctrl_char *p; char string[256]; int i; + size_t j; int count_open_EACCESS = 0; int count_open_errors = 0; int count_open_attempts = 0; /* report descriptor */ usb_ctrl_char rdbuf[MAX_REPORT_SIZE]; - usb_ctrl_charbufsize rdlen; + usb_ctrl_charbufsize rdlen = -1; struct usb_bus *busses; @@ -625,13 +626,15 @@ static int nut_libusb_open(usb_dev_handle **udevp, the maximum of the two values instead. */ if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { upsdebugx(1, "Eaton device v2.02. Using full report descriptor"); - rdlen = rdlen1; + rdlens[0] = rdlen1; + rdlens[1] = rdlen2; } else { - rdlen = rdlen2 >= 0 ? rdlen2 : rdlen1; + rdlens[0] = rdlen2 >= 0 ? rdlen2 : rdlen1; + rdlens[1] = rdlen2 >= 0 ? rdlen1 : rdlen2; } - if (rdlen < 0) { + if (rdlen1 < 0 && rdlen2 < 0) { upsdebugx(2, "Unable to retrieve any HID descriptor"); goto next_device; } @@ -640,61 +643,82 @@ static int nut_libusb_open(usb_dev_handle **udevp, "(Reportlen = %" PRI_NUT_USB_CTRL_CHARBUFSIZE " vs. %" PRI_NUT_USB_CTRL_CHARBUFSIZE ")", rdlen1, rdlen2); + } else { + if (rdlen1 == rdlen2) { + rdlens[1] = -1; + } } - upsdebugx(2, - "HID descriptor length %" PRI_NUT_USB_CTRL_CHARBUFSIZE, - rdlen); + for (j = 0; j < sizeof(rdlens); j++) { + rdlen = rdlens[j]; + if (rdlen < 0) + continue; - if ((uintmax_t)rdlen > sizeof(rdbuf)) { upsdebugx(2, - "HID descriptor too long %" PRI_NUT_USB_CTRL_CHARBUFSIZE - " (max %" PRIuSIZE ")", - rdlen, sizeof(rdbuf)); - goto next_device; - } + "Trying HID descriptor length %" PRI_NUT_USB_CTRL_CHARBUFSIZE, + rdlen); + + if ((uintmax_t)rdlen > sizeof(rdbuf)) { + upsdebugx(2, + "HID descriptor too long %" PRI_NUT_USB_CTRL_CHARBUFSIZE + " (max %" PRIuSIZE ")", + rdlen, sizeof(rdbuf)); + continue; + /*goto next_device;*/ + } - /* Note: rdlen is safe to cast to unsigned below, - * since the <0 case was ruled out above */ - /* res = usb_get_descriptor(udev, USB_DT_REPORT, hid_desc_index, bigbuf, rdlen); */ - res = usb_control_msg(udev, - USB_ENDPOINT_IN + 1, - USB_REQ_GET_DESCRIPTOR, - (USB_DT_REPORT << 8) + usb_subdriver.hid_desc_index, - usb_subdriver.hid_rep_index, - rdbuf, rdlen, USB_TIMEOUT); + /* Note: rdlen is safe to cast to unsigned below, + * since the <0 case was ruled out above */ + /* res = usb_get_descriptor(udev, USB_DT_REPORT, hid_desc_index, bigbuf, rdlen); */ + res = usb_control_msg(udev, + USB_ENDPOINT_IN + 1, + USB_REQ_GET_DESCRIPTOR, + (USB_DT_REPORT << 8) + usb_subdriver.hid_desc_index, + usb_subdriver.hid_rep_index, + rdbuf, rdlen, USB_TIMEOUT); - if (res < 0) - { - upsdebug_with_errno(2, "Unable to get Report descriptor"); - goto next_device; - } + if (res < 0) + { + upsdebug_with_errno(2, "Unable to get Report descriptor"); + continue; + /*goto next_device;*/ + } - if (res < rdlen) - { + if (res < rdlen) + { #ifndef WIN32 - upsdebugx(2, "Warning: report descriptor too short " - "(expected %" PRI_NUT_USB_CTRL_CHARBUFSIZE - ", got %d)", rdlen, res); + upsdebugx(2, "Warning: report descriptor too short " + "(expected %" PRI_NUT_USB_CTRL_CHARBUFSIZE + ", got %d)", rdlen, res); #else /* WIN32 */ - /* https://github.com/networkupstools/nut/issues/1690#issuecomment-1455206002 */ - upsdebugx(0, "Warning: report descriptor too short " - "(expected %" PRI_NUT_USB_CTRL_CHARBUFSIZE - ", got %d)", rdlen, res); - upsdebugx(0, "Please check your Windows Device Manager: " - "perhaps the UPS was recognized by default OS\n" - "driver such as HID UPS Battery (hidbatt.sys, " - "hidusb.sys or similar). It could have been\n" - "\"restored\" by Windows Update. You can try " - "https://zadig.akeo.ie/ to handle it with\n" - "either WinUSB, libusb0.sys or libusbK.sys."); + /* https://github.com/networkupstools/nut/issues/1690#issuecomment-1455206002 */ + upsdebugx(0, "Warning: report descriptor too short " + "(expected %" PRI_NUT_USB_CTRL_CHARBUFSIZE + ", got %d)", rdlen, res); + upsdebugx(0, "Please check your Windows Device Manager: " + "perhaps the UPS was recognized by default OS\n" + "driver such as HID UPS Battery (hidbatt.sys, " + "hidusb.sys or similar). It could have been\n" + "\"restored\" by Windows Update. You can try " + "https://zadig.akeo.ie/ to handle it with\n" + "either WinUSB, libusb0.sys or libusbK.sys."); #endif /* WIN32 */ - rdlen = res; /* correct rdlen if necessary */ + rdlen = res; /* correct rdlen if necessary */ + } + + res = callback(udev, curDevice, rdbuf, rdlen); + if (res < 1) { + upsdebugx(2, "Caller doesn't like this device (or rdlen is wrong)"); + continue; + /*goto next_device;*/ + } + + /* We found it... or at least something that did not complain */ + break; } - res = callback(udev, curDevice, rdbuf, rdlen); - if (res < 1) { - upsdebugx(2, "Caller doesn't like this device"); + if (j >= sizeof(rdlens)) { + /* Ended the loop without success */ goto next_device; } diff --git a/drivers/libusb1.c b/drivers/libusb1.c index 2ce00dbf3c..71eab6ff7e 100644 --- a/drivers/libusb1.c +++ b/drivers/libusb1.c @@ -33,7 +33,7 @@ #include "nut_stdint.h" #define USB_DRIVER_NAME "USB communication driver (libusb 1.0)" -#define USB_DRIVER_VERSION "0.50" +#define USB_DRIVER_VERSION "0.51" /* driver description structure */ upsdrv_info_t comm_upsdrv_info = { @@ -185,7 +185,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, #endif /* libusb-1.0 usb_ctrl_charbufsize is uint16_t and we * want the rdlen vars signed - so taking a wider type */ - int32_t rdlen1, rdlen2; /* report descriptor length, method 1+2 */ + int32_t rdlen1, rdlen2, rdlens[2]; /* report descriptor length, method 1+2, then an array to iterate them (if both) in chosen order */ USBDeviceMatcher_t *m; libusb_device **devlist; ssize_t devcount = 0; @@ -203,13 +203,14 @@ static int nut_libusb_open(libusb_device_handle **udevp, const unsigned char *p; char string[256]; int i; + size_t j; int count_open_EACCESS = 0; int count_open_errors = 0; int count_open_attempts = 0; /* report descriptor */ unsigned char rdbuf[MAX_REPORT_SIZE]; - int32_t rdlen; + int32_t rdlen = -1; static int usb_hid_number_opts_parsed = 0; if (!usb_hid_number_opts_parsed) { @@ -653,7 +654,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, if (rdlen1 < -1) { upsdebugx(2, "Warning: HID descriptor, method 1 failed"); } - upsdebugx(3, "HID descriptor length (method 1) %d", rdlen1); + upsdebugx(3, "HID descriptor length (method 1) %" PRIi32, rdlen1); /* SECOND METHOD: find HID descriptor among "extra" bytes of interface descriptor, i.e., bytes tucked onto the end of @@ -683,7 +684,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, if (rdlen2 < -1) { upsdebugx(2, "Warning: HID descriptor, method 2 failed"); } - upsdebugx(3, "HID descriptor length (method 2) %d", rdlen2); + upsdebugx(3, "HID descriptor length (method 2) %" PRIi32, rdlen2); /* when available, always choose the second value, as it seems to be more reliable (it is the one reported e.g. by @@ -691,28 +692,41 @@ static int nut_libusb_open(libusb_device_handle **udevp, the maximum of the two values instead. */ if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { upsdebugx(1, "Eaton device v2.02. Using full report descriptor"); - rdlen = rdlen1; + rdlens[0] = rdlen1; + rdlens[1] = rdlen2; } else { - rdlen = rdlen2 >= 0 ? rdlen2 : rdlen1; + rdlens[0] = rdlen2 >= 0 ? rdlen2 : rdlen1; + rdlens[1] = rdlen2 >= 0 ? rdlen1 : rdlen2; } - if (rdlen < 0) { + if (rdlen1 < 0 && rdlen2 < 0) { upsdebugx(2, "Unable to retrieve any HID descriptor"); goto next_device; } if (rdlen1 >= 0 && rdlen2 >= 0 && rdlen1 != rdlen2) { upsdebugx(2, "Warning: two different HID descriptors retrieved " - "(Reportlen = %d vs. %d)", rdlen1, rdlen2); + "(Reportlen = %" PRIi32 " vs. %" PRIi32 ")", + rdlen1, rdlen2); + } else { + if (rdlen1 == rdlen2) { + rdlens[1] = -1; + } } - upsdebugx(2, "HID descriptor length %d", rdlen); + for (j = 0; j < sizeof(rdlens); j++) { + rdlen = rdlens[j]; + if (rdlen < 0) + continue; - if (rdlen > (int)sizeof(rdbuf)) { - upsdebugx(2, "HID descriptor too long %d (max %d)", - rdlen, (int)sizeof(rdbuf)); - goto next_device; - } + upsdebugx(2, "Trying HID descriptor length %" PRIi32, rdlen); + + if (rdlen > (int)sizeof(rdbuf)) { + upsdebugx(2, "HID descriptor too long %" PRIi32 " (max %d)", + rdlen, (int)sizeof(rdbuf)); + continue; + /*goto next_device;*/ + } #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) # pragma GCC diagnostic push @@ -726,67 +740,81 @@ static int nut_libusb_open(libusb_device_handle **udevp, #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE # pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" #endif - if ((uintmax_t)rdlen > UINT16_MAX) { + if ((uintmax_t)rdlen > UINT16_MAX) { #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) # pragma GCC diagnostic pop #endif - upsdebugx(2, "HID descriptor too long %d (max %" PRIuMAX ")", - rdlen, (uintmax_t)UINT16_MAX); - goto next_device; - } + upsdebugx(2, "HID descriptor too long %" PRIi32 + " (max %" PRIuMAX ")", + rdlen, (uintmax_t)UINT16_MAX); + continue; + /*goto next_device;*/ + } - /* libusb0: USB_ENDPOINT_IN + 1 */ - res = libusb_control_transfer(udev, - LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_STANDARD|LIBUSB_RECIPIENT_INTERFACE, - LIBUSB_REQUEST_GET_DESCRIPTOR, - (LIBUSB_DT_REPORT << 8) + usb_subdriver.hid_desc_index, - usb_subdriver.hid_rep_index, - rdbuf, (uint16_t)rdlen, USB_TIMEOUT); + /* libusb0: USB_ENDPOINT_IN + 1 */ + res = libusb_control_transfer(udev, + LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_STANDARD|LIBUSB_RECIPIENT_INTERFACE, + LIBUSB_REQUEST_GET_DESCRIPTOR, + (LIBUSB_DT_REPORT << 8) + usb_subdriver.hid_desc_index, + usb_subdriver.hid_rep_index, + rdbuf, (uint16_t)rdlen, USB_TIMEOUT); - if (res < 0) - { - upsdebug_with_errno(2, "Unable to get Report descriptor"); - goto next_device; - } + if (res < 0) + { + upsdebug_with_errno(2, "Unable to get Report descriptor"); + continue; + /*goto next_device;*/ + } - if (res < rdlen) - { + if (res < rdlen) + { #ifndef WIN32 - upsdebugx(2, "Warning: report descriptor too short " - "(expected %d, got %d)", rdlen, res); + upsdebugx(2, "Warning: report descriptor too short " + "(expected %" PRIi32 ", got %d)", rdlen, res); #else /* WIN32 */ - /* https://github.com/networkupstools/nut/issues/1690#issuecomment-1455206002 */ - upsdebugx(0, "Warning: report descriptor too short " - "(expected %d, got %d)", rdlen, res); - upsdebugx(0, "Please check your Windows Device Manager: " - "perhaps the UPS was recognized by default OS\n" - "driver such as HID UPS Battery (hidbatt.sys, " - "hidusb.sys or similar). It could have been\n" - "\"restored\" by Windows Update. You can try " - "https://zadig.akeo.ie/ to handle it with\n" - "either WinUSB, libusb0.sys or libusbK.sys."); + /* https://github.com/networkupstools/nut/issues/1690#issuecomment-1455206002 */ + upsdebugx(0, "Warning: report descriptor too short " + "(expected %" PRIi32 ", got %d)", rdlen, res); + upsdebugx(0, "Please check your Windows Device Manager: " + "perhaps the UPS was recognized by default OS\n" + "driver such as HID UPS Battery (hidbatt.sys, " + "hidusb.sys or similar). It could have been\n" + "\"restored\" by Windows Update. You can try " + "https://zadig.akeo.ie/ to handle it with\n" + "either WinUSB, libusb0.sys or libusbK.sys."); #endif /* WIN32 */ - rdlen = res; /* correct rdlen if necessary */ - } + rdlen = res; /* correct rdlen if necessary */ + } - if (rdlen < USB_CTRL_CHARBUFSIZE_MIN - || (uintmax_t)rdlen > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX - ) { - upsdebugx(2, - "Report descriptor length is out of range on this device: " - "should be %" PRIdMAX " < %d < %" PRIuMAX, - (intmax_t)USB_CTRL_CHARBUFSIZE_MIN, rdlen, - (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); - goto next_device; + if (rdlen < USB_CTRL_CHARBUFSIZE_MIN + || (uintmax_t)rdlen > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX + ) { + upsdebugx(2, + "Report descriptor length is out of range on this device: " + "should be %" PRIdMAX " < %" PRIi32 " < %" PRIuMAX, + (intmax_t)USB_CTRL_CHARBUFSIZE_MIN, rdlen, + (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); + goto next_device; + } + + res = callback(udev, curDevice, rdbuf, (usb_ctrl_charbufsize)rdlen); + if (res < 1) { + upsdebugx(2, "Caller doesn't like this device"); + continue; + /*goto next_device;*/ + } + + /* We found it... or at least something that did not complain */ + break; } - res = callback(udev, curDevice, rdbuf, (usb_ctrl_charbufsize)rdlen); - if (res < 1) { - upsdebugx(2, "Caller doesn't like this device"); + if (j >= sizeof(rdlens)) { + /* Ended the loop without success */ goto next_device; } - upsdebugx(2, "Report descriptor retrieved (Reportlen = %d)", rdlen); + upsdebugx(2, "Report descriptor retrieved (Reportlen = %" PRIi32 ")", + rdlen); upsdebugx(2, "Found HID device"); upsdebugx(3, "Using default, detected or customized USB HID numbers: " From b9635371a77bfae4a208fbac8d3f8d9d98d455b1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 30 Oct 2025 15:26:21 +0100 Subject: [PATCH 2/2] drivers/libusb{0,1}.c: nut_libusb_open(): drop commented-away "goto next_device" now that we have a loop and "continue" to try another rdlen [#3136] Signed-off-by: Jim Klimov --- drivers/libusb0.c | 3 --- drivers/libusb1.c | 4 ---- 2 files changed, 7 deletions(-) diff --git a/drivers/libusb0.c b/drivers/libusb0.c index aa29c1685b..be1ae8bc0a 100644 --- a/drivers/libusb0.c +++ b/drivers/libusb0.c @@ -664,7 +664,6 @@ static int nut_libusb_open(usb_dev_handle **udevp, " (max %" PRIuSIZE ")", rdlen, sizeof(rdbuf)); continue; - /*goto next_device;*/ } /* Note: rdlen is safe to cast to unsigned below, @@ -681,7 +680,6 @@ static int nut_libusb_open(usb_dev_handle **udevp, { upsdebug_with_errno(2, "Unable to get Report descriptor"); continue; - /*goto next_device;*/ } if (res < rdlen) @@ -710,7 +708,6 @@ static int nut_libusb_open(usb_dev_handle **udevp, if (res < 1) { upsdebugx(2, "Caller doesn't like this device (or rdlen is wrong)"); continue; - /*goto next_device;*/ } /* We found it... or at least something that did not complain */ diff --git a/drivers/libusb1.c b/drivers/libusb1.c index 71eab6ff7e..7cd170d3ca 100644 --- a/drivers/libusb1.c +++ b/drivers/libusb1.c @@ -725,7 +725,6 @@ static int nut_libusb_open(libusb_device_handle **udevp, upsdebugx(2, "HID descriptor too long %" PRIi32 " (max %d)", rdlen, (int)sizeof(rdbuf)); continue; - /*goto next_device;*/ } #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) @@ -748,7 +747,6 @@ static int nut_libusb_open(libusb_device_handle **udevp, " (max %" PRIuMAX ")", rdlen, (uintmax_t)UINT16_MAX); continue; - /*goto next_device;*/ } /* libusb0: USB_ENDPOINT_IN + 1 */ @@ -763,7 +761,6 @@ static int nut_libusb_open(libusb_device_handle **udevp, { upsdebug_with_errno(2, "Unable to get Report descriptor"); continue; - /*goto next_device;*/ } if (res < rdlen) @@ -801,7 +798,6 @@ static int nut_libusb_open(libusb_device_handle **udevp, if (res < 1) { upsdebugx(2, "Caller doesn't like this device"); continue; - /*goto next_device;*/ } /* We found it... or at least something that did not complain */