diff --git a/NEWS.adoc b/NEWS.adoc index d42c2375fa..f4d8929bfc 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -158,6 +158,18 @@ https://github.com/networkupstools/nut/milestone/11 reading an empty string or getting a success code `0` from libusb. This difference should now be better logged, and not into syslog. [#2399] + - USB drivers can benefit from a new `nut_usb_get_string()` method which + can do a fallback `en_US` query for devices which report a broken "langid" + language identifier value. This notably manifested in inability to query + device Manufacturer, Model and Serial Number values with some buggy device + firmware or hardware. [PR #2604, issues #1925, #414] + * Currently this was tested to fix certain device discovery with `usbhid-ups`; + should also apply out of the box to same discovery logic in `blazer_usb`, + `nutdrv_qx`, `riello_usb` and `tripplite_usb` drivers. + * More work may be needed for other USB-capable drivers (`richcomm_usb`, + `nutdrv_atcl_usb`) and for general code to collect string readings and + other data points, and for `nut-scanner`. + - Introduced a new driver concept for interaction with OS-reported hardware monitoring readings. Currently instantiated as `hwmon_ina219` specifically made for Texas Instruments INA219 chip as exposed in the Linux "hwmon" diff --git a/drivers/blazer_usb.c b/drivers/blazer_usb.c index c0b091ab3d..fb0f75a2d5 100644 --- a/drivers/blazer_usb.c +++ b/drivers/blazer_usb.c @@ -37,7 +37,7 @@ #endif #define DRIVER_NAME "Megatec/Q1 protocol USB driver" -#define DRIVER_VERSION "0.19" +#define DRIVER_VERSION "0.20" /* driver description structure */ upsdrv_info_t upsdrv_info = { diff --git a/drivers/libusb0.c b/drivers/libusb0.c index c201d3d96f..2cc617a239 100644 --- a/drivers/libusb0.c +++ b/drivers/libusb0.c @@ -50,7 +50,6 @@ upsdrv_info_t comm_upsdrv_info = { }; #define MAX_REPORT_SIZE 0x1800 -#define MAX_RETRY 3 #if (!HAVE_STRCASESTR) && (HAVE_STRSTR && HAVE_STRLWR && HAVE_STRDUP) /* Only used in this file of all NUT codebase, so not in str.{c,h} @@ -60,7 +59,7 @@ upsdrv_info_t comm_upsdrv_info = { static char *strcasestr(const char *haystack, const char *needle); #endif -static void libusb_close(usb_dev_handle *udev); +static void nut_libusb_close(usb_dev_handle *udev); /*! Add USB-related driver variables with addvar() and dstate_setinfo(). * This removes some code duplication across the USB drivers. @@ -155,7 +154,7 @@ static inline int matches(USBDeviceMatcher_t *matcher, USBDevice_t *device) { * devices from working on Mac OS X (presumably the OS is already setting * altinterface to 0). */ -static int nut_usb_set_altinterface(usb_dev_handle *udev) +static int nut_libusb_set_altinterface(usb_dev_handle *udev) { int altinterface = 0, ret = 0; char *alt_string, *endp = NULL; @@ -197,13 +196,15 @@ static int nut_usb_set_altinterface(usb_dev_handle *udev) * is accepted, or < 1 if not. If it isn't accepted, the next device * (if any) will be tried, until there are no more devices left. */ -static int libusb_open(usb_dev_handle **udevp, +static int nut_libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDeviceMatcher_t *matcher, int (*callback)(usb_dev_handle *udev, USBDevice_t *hd, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen) ) { +#ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP int retries; +#endif usb_ctrl_charbufsize rdlen1, rdlen2; /* report descriptor length, method 1+2 */ USBDeviceMatcher_t *m; struct usb_device *dev; @@ -313,7 +314,7 @@ static int libusb_open(usb_dev_handle **udevp, #ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ /* Causes a double free corruption in linux if device is detached! */ - libusb_close(*udevp); + nut_libusb_close(*udevp); #endif upsdebugx(3, "usb_busses=%p", (void*)usb_busses); @@ -396,44 +397,32 @@ static int libusb_open(usb_dev_handle **udevp, #endif if (dev->descriptor.iManufacturer) { - retries = MAX_RETRY; - while (retries > 0) { - ret = usb_get_string_simple(udev, dev->descriptor.iManufacturer, - string, sizeof(string)); - if (ret > 0) { - curDevice->Vendor = xstrdup(string); - break; - } - retries--; - upsdebugx(1, "%s get iManufacturer failed, retrying...", __func__); + ret = nut_usb_get_string(udev, dev->descriptor.iManufacturer, + string, sizeof(string)); + if (ret > 0) { + curDevice->Vendor = xstrdup(string); + } else { + upsdebugx(1, "%s: get Manufacturer string failed", __func__); } } if (dev->descriptor.iProduct) { - retries = MAX_RETRY; - while (retries > 0) { - ret = usb_get_string_simple(udev, dev->descriptor.iProduct, - string, sizeof(string)); - if (ret > 0) { - curDevice->Product = xstrdup(string); - break; - } - retries--; - upsdebugx(1, "%s get iProduct failed, retrying...", __func__); + ret = nut_usb_get_string(udev, dev->descriptor.iProduct, + string, sizeof(string)); + if (ret > 0) { + curDevice->Product = xstrdup(string); + } else { + upsdebugx(1, "%s: get Product string failed", __func__); } } if (dev->descriptor.iSerialNumber) { - retries = MAX_RETRY; - while (retries > 0) { - ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, - string, sizeof(string)); - if (ret > 0) { - curDevice->Serial = xstrdup(string); - break; - } - retries--; - upsdebugx(1, "%s get iSerialNumber failed, retrying...", __func__); + ret = nut_usb_get_string(udev, dev->descriptor.iSerialNumber, + string, sizeof(string)); + if (ret > 0) { + curDevice->Serial = xstrdup(string); + } else { + upsdebugx(1, "%s: get Serial Number string failed", __func__); } } @@ -489,11 +478,10 @@ static int libusb_open(usb_dev_handle **udevp, /* this method requires at least libusb 0.1.8: * it force device claiming by unbinding * attached driver... From libhid */ - retries = MAX_RETRY; #ifdef WIN32 usb_set_configuration(udev, 1); #endif - + retries = 3; while ((ret = usb_claim_interface(udev, usb_subdriver.hid_rep_index)) < 0) { upsdebugx(2, "failed to claim USB device: %s", usb_strerror()); @@ -540,59 +528,7 @@ static int libusb_open(usb_dev_handle **udevp, #endif /* if_claimed = 1; */ - nut_usb_set_altinterface(udev); - - /* If libusb failed to identify the device strings earlier, - * can we do that after claiming the interface? Just try... - * Note that we succeeded so far, meaning these strings were - * not among matching criteria. But they can be important for - * our drivers (e.g. per-model tweaks) and pretty reporting - * of certain `device.*` and/or `ups.*` data points. - */ - if (!curDevice->Vendor) { - retries = MAX_RETRY; - while (retries > 0) { - ret = usb_get_string_simple(udev, dev->descriptor.iManufacturer, - string, sizeof(string)); - if (ret > 0) { - curDevice->Vendor = xstrdup(string); - break; - } - retries--; - upsdebugx(1, "%s get iManufacturer failed, retrying...", __func__); - } - upsdebugx(2, "- Manufacturer: %s", curDevice->Vendor ? curDevice->Vendor : "unknown"); - } - - if (!curDevice->Product) { - retries = MAX_RETRY; - while (retries > 0) { - ret = usb_get_string_simple(udev, dev->descriptor.iProduct, - string, sizeof(string)); - if (ret > 0) { - curDevice->Product = xstrdup(string); - break; - } - retries--; - upsdebugx(1, "%s get iProduct failed, retrying...", __func__); - } - upsdebugx(2, "- Product: %s", curDevice->Product ? curDevice->Product : "unknown"); - } - - if (!curDevice->Serial) { - retries = MAX_RETRY; - while (retries > 0) { - ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, - string, sizeof(string)); - if (ret > 0) { - curDevice->Serial = xstrdup(string); - break; - } - retries--; - upsdebugx(1, "%s get iSerialNumber failed, retrying...", __func__); - } - upsdebugx(2, "- Serial Number: %s", curDevice->Serial ? curDevice->Serial : "unknown"); - } + nut_libusb_set_altinterface(udev); /* Did the driver provide a callback method for any further * device acceptance checks (e.g. when same ID is supported @@ -803,7 +739,7 @@ static int libusb_open(usb_dev_handle **udevp, * Error handler for usb_get/set_* functions. Return value > 0 success, * 0 unknown or temporary failure (ignored), < 0 permanent failure (reconnect) */ -static int libusb_strerror(const int ret, const char *desc) +static int nut_libusb_strerror(const int ret, const char *desc) { if (ret > 0) { return ret; @@ -850,10 +786,10 @@ static int libusb_strerror(const int ret, const char *desc) */ /* Expected evaluated types for the API: - * static int libusb_get_report(usb_dev_handle *udev, + * static int nut_libusb_get_report(usb_dev_handle *udev, * int ReportId, unsigned char *raw_buf, int ReportSize) */ -static int libusb_get_report( +static int nut_libusb_get_report( usb_dev_handle *udev, usb_ctrl_repindex ReportId, usb_ctrl_charbuf raw_buf, @@ -861,7 +797,7 @@ static int libusb_get_report( { int ret; - upsdebugx(4, "Entering libusb_get_report"); + upsdebugx(4, "Entering nut_libusb_get_report"); if (!udev) { return 0; @@ -883,14 +819,14 @@ static int libusb_get_report( return 0; } - return libusb_strerror(ret, __func__); + return nut_libusb_strerror(ret, __func__); } /* Expected evaluated types for the API: - * static int libusb_set_report(usb_dev_handle *udev, + * static int nut_libusb_set_report(usb_dev_handle *udev, * int ReportId, unsigned char *raw_buf, int ReportSize) */ -static int libusb_set_report( +static int nut_libusb_set_report( usb_dev_handle *udev, usb_ctrl_repindex ReportId, usb_ctrl_charbuf raw_buf, @@ -918,14 +854,14 @@ static int libusb_set_report( return 0; } - return libusb_strerror(ret, __func__); + return nut_libusb_strerror(ret, __func__); } /* Expected evaluated types for the API: - * static int libusb_get_string(usb_dev_handle *udev, - * int StringIdx, char *buf, size_t buflen) + * static int nut_libusb_get_string(usb_dev_handle *udev, + * int StringIdx, char *buf, int buflen) */ -static int libusb_get_string( +static int nut_libusb_get_string( usb_dev_handle *udev, usb_ctrl_strindex StringIdx, char *buf, @@ -933,33 +869,18 @@ static int libusb_get_string( { int ret; -#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 -#endif -#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS -# pragma GCC diagnostic ignored "-Wtype-limits" -#endif -#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE -# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" -#endif -#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE -# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" -#endif /* - * usb.h:int usb_get_string_simple(usb_dev_handle *dev, int index, + * usb.h:int nut_usb_get_string(usb_dev_handle *dev, int index, * usb.h- char *buf, size_t buflen); */ if (!udev - || StringIdx < 0 || (uintmax_t)StringIdx > INT_MAX - || buflen < 0 || (uintmax_t)buflen > (uintmax_t)SIZE_MAX + || StringIdx < 1 || StringIdx > 255 + || buflen < 1 ) { -#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 return -1; } - ret = usb_get_string_simple(udev, StringIdx, buf, (size_t)buflen); + ret = nut_usb_get_string(udev, StringIdx, buf, (size_t)buflen); #ifdef WIN32 errno = -ret; @@ -969,22 +890,22 @@ static int libusb_get_string( * logging below - also tends to happen */ if (ret == 0) { size_t len = strlen(buf); - upsdebugx(2, "%s: usb_get_string_simple() returned " + upsdebugx(2, "%s: nut_usb_get_string() returned " "0 (might be just success code), " "actual buf length is %" PRIuSIZE, __func__, len); /* if (len) */ return len; - /* else may log "libusb_get_string: Success" and return 0 below */ + /* else may log "nut_libusb_get_string: Success" and return 0 below */ } - return libusb_strerror(ret, __func__); + return nut_libusb_strerror(ret, __func__); } /* Expected evaluated types for the API: - * static int libusb_get_interrupt(usb_dev_handle *udev, + * static int nut_libusb_get_interrupt(usb_dev_handle *udev, * unsigned char *buf, int bufsize, int timeout) */ -static int libusb_get_interrupt( +static int nut_libusb_get_interrupt( usb_dev_handle *udev, usb_ctrl_charbuf buf, usb_ctrl_charbufsize bufsize, @@ -1008,10 +929,10 @@ static int libusb_get_interrupt( ret = usb_clear_halt(udev, 0x81); } - return libusb_strerror(ret, __func__); + return nut_libusb_strerror(ret, __func__); } -static void libusb_close(usb_dev_handle *udev) +static void nut_libusb_close(usb_dev_handle *udev) { if (!udev) { return; @@ -1058,12 +979,12 @@ static char *strcasestr(const char *haystack, const char *needle) { usb_communication_subdriver_t usb_subdriver = { USB_DRIVER_NAME, USB_DRIVER_VERSION, - libusb_open, - libusb_close, - libusb_get_report, - libusb_set_report, - libusb_get_string, - libusb_get_interrupt, + nut_libusb_open, + nut_libusb_close, + nut_libusb_get_report, + nut_libusb_set_report, + nut_libusb_get_string, + nut_libusb_get_interrupt, LIBUSB_DEFAULT_CONF_INDEX, LIBUSB_DEFAULT_INTERFACE, LIBUSB_DEFAULT_DESC_INDEX, diff --git a/drivers/libusb1.c b/drivers/libusb1.c index 2f325f30f1..6035f65cb9 100644 --- a/drivers/libusb1.c +++ b/drivers/libusb1.c @@ -45,7 +45,6 @@ upsdrv_info_t comm_upsdrv_info = { }; #define MAX_REPORT_SIZE 0x1800 -#define MAX_RETRY 3 static void nut_libusb_close(libusb_device_handle *udev); @@ -120,7 +119,7 @@ static inline int matches(USBDeviceMatcher_t *matcher, USBDevice_t *device) { * devices from working on Mac OS X (presumably the OS is already setting * altinterface to 0). */ -static int nut_usb_set_altinterface(libusb_device_handle *udev) +static int nut_libusb_set_altinterface(libusb_device_handle *udev) { int altinterface = 0, ret = 0; char *alt_string, *endp = NULL; @@ -167,7 +166,9 @@ static int nut_libusb_open(libusb_device_handle **udevp, USBDevice_t *hd, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen) ) { +#if (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER) || (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) int retries; +#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 */ @@ -390,56 +391,44 @@ static int nut_libusb_open(libusb_device_handle **udevp, curDevice->bcdDevice = dev_desc.bcdDevice; if (dev_desc.iManufacturer) { - retries = MAX_RETRY; - while (retries > 0) { - ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iManufacturer, - (unsigned char*)string, sizeof(string)); - if (ret > 0) { - curDevice->Vendor = strdup(string); - if (curDevice->Vendor == NULL) { - libusb_free_device_list(devlist, 1); - fatal_with_errno(EXIT_FAILURE, "Out of memory"); - } - break; + ret = nut_usb_get_string(udev, dev_desc.iManufacturer, + string, sizeof(string)); + if (ret > 0) { + curDevice->Vendor = strdup(string); + if (curDevice->Vendor == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); } - retries--; - upsdebugx(1, "%s get iManufacturer failed, retrying...", __func__); + } else { + upsdebugx(1, "%s: get Manufacturer string failed", __func__); } } if (dev_desc.iProduct) { - retries = MAX_RETRY; - while (retries > 0) { - ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iProduct, - (unsigned char*)string, sizeof(string)); - if (ret > 0) { - curDevice->Product = strdup(string); - if (curDevice->Product == NULL) { - libusb_free_device_list(devlist, 1); - fatal_with_errno(EXIT_FAILURE, "Out of memory"); - } - break; + ret = nut_usb_get_string(udev, dev_desc.iProduct, + string, sizeof(string)); + if (ret > 0) { + curDevice->Product = strdup(string); + if (curDevice->Product == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); } - retries--; - upsdebugx(1, "%s get iProduct failed, retrying...", __func__); + } else { + upsdebugx(1, "%s: get Product string failed", __func__); } } if (dev_desc.iSerialNumber) { - retries = MAX_RETRY; - while (retries > 0) { - ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iSerialNumber, - (unsigned char*)string, sizeof(string)); - if (ret > 0) { - curDevice->Serial = strdup(string); - if (curDevice->Serial == NULL) { - libusb_free_device_list(devlist, 1); - fatal_with_errno(EXIT_FAILURE, "Out of memory"); - } - break; + ret = nut_usb_get_string(udev, dev_desc.iSerialNumber, + string, sizeof(string)); + if (ret > 0) { + curDevice->Serial = strdup(string); + if (curDevice->Serial == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); } - retries--; - upsdebugx(1, "%s get iSerialNumber failed, retrying...", __func__); + } else { + upsdebugx(1, "%s: get Serial Number string failed", __func__); } } @@ -532,12 +521,12 @@ static int nut_libusb_open(libusb_device_handle **udevp, #if (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER) || (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) /* Then, try the explicit detach method. * This function is available on FreeBSD 10.1-10.3 */ - retries = MAX_RETRY; #ifdef WIN32 /* TODO: Align with libusb1 - initially from Windows branch made against libusb0 */ libusb_set_configuration(udev, 1); #endif + retries = 3; 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)); @@ -599,71 +588,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, upsdebugx(2, "Claimed interface %d successfully", usb_subdriver.hid_rep_index); - nut_usb_set_altinterface(udev); - - /* If libusb failed to identify the device strings earlier, - * can we do that after claiming the interface? Just try... - * Note that we succeeded so far, meaning these strings were - * not among matching criteria. But they can be important for - * our drivers (e.g. per-model tweaks) and pretty reporting - * of certain `device.*` and/or `ups.*` data points. - */ - if (!curDevice->Vendor) { - retries = MAX_RETRY; - while (retries > 0) { - ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iManufacturer, - (unsigned char*)string, sizeof(string)); - if (ret > 0) { - curDevice->Vendor = strdup(string); - if (curDevice->Vendor == NULL) { - libusb_free_device_list(devlist, 1); - fatal_with_errno(EXIT_FAILURE, "Out of memory"); - } - break; - } - retries--; - upsdebugx(1, "%s get iManufacturer failed, retrying...", __func__); - } - upsdebugx(2, "- Manufacturer: %s", curDevice->Vendor ? curDevice->Vendor : "unknown"); - } - - if (!curDevice->Product) { - retries = MAX_RETRY; - while (retries > 0) { - ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iProduct, - (unsigned char*)string, sizeof(string)); - if (ret > 0) { - curDevice->Product = strdup(string); - if (curDevice->Product == NULL) { - libusb_free_device_list(devlist, 1); - fatal_with_errno(EXIT_FAILURE, "Out of memory"); - } - break; - } - retries--; - upsdebugx(1, "%s get iProduct failed, retrying...", __func__); - } - upsdebugx(2, "- Product: %s", curDevice->Product ? curDevice->Product : "unknown"); - } - - if (!curDevice->Serial) { - retries = MAX_RETRY; - while (retries > 0) { - ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iSerialNumber, - (unsigned char*)string, sizeof(string)); - if (ret > 0) { - curDevice->Serial = strdup(string); - if (curDevice->Serial == NULL) { - libusb_free_device_list(devlist, 1); - fatal_with_errno(EXIT_FAILURE, "Out of memory"); - } - break; - } - retries--; - upsdebugx(1, "%s get iSerialNumber failed, retrying...", __func__); - } - upsdebugx(2, "- Serial Number: %s", curDevice->Serial ? curDevice->Serial : "unknown"); - } + nut_libusb_set_altinterface(udev); /* Did the driver provide a callback method for any further * device acceptance checks (e.g. when same ID is supported @@ -1056,7 +981,7 @@ static int nut_libusb_set_report( /* Expected evaluated types for the API: * static int nut_libusb_get_string(libusb_device_handle *udev, - * int StringIdx, char *buf, int buflen) + * uint8_t StringIdx, unsigned char *buf, uint16_t buflen) */ static int nut_libusb_get_string( libusb_device_handle *udev, @@ -1066,36 +991,18 @@ static int nut_libusb_get_string( { int ret; -#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 -#endif -#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS -# pragma GCC diagnostic ignored "-Wtype-limits" -#endif -#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE -# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" -#endif -#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE -# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" -#endif - if (!udev - || StringIdx < 0 || (uintmax_t)StringIdx > UINT8_MAX - || buflen < 0 || (uintmax_t)buflen > (uintmax_t)INT_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 + if (!udev || StringIdx < 1 || buflen < 1) { return -1; } - ret = libusb_get_string_descriptor_ascii(udev, (uint8_t)StringIdx, - (unsigned char*)buf, (int)buflen); + ret = nut_usb_get_string(udev, (uint8_t)StringIdx, + buf, (int)buflen); /** 0 can be seen as an empty string, or as LIBUSB_SUCCESS for * logging below - also tends to happen */ if (ret == 0) { size_t len = strlen(buf); - upsdebugx(2, "%s: libusb_get_string_descriptor_ascii() returned " + upsdebugx(2, "%s: nut_usb_get_string() returned " "0 (might be just LIBUSB_SUCCESS code), " "actual buf length is %" PRIuSIZE, __func__, len); /* if (len) */ diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 4332be5097..3bc1d8675a 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -58,7 +58,7 @@ # define DRIVER_NAME "Generic Q* Serial driver" #endif /* QX_USB */ -#define DRIVER_VERSION "0.36" +#define DRIVER_VERSION "0.37" #ifdef QX_SERIAL # include "serial.h" diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c index 32a46b5618..fdc7de8246 100644 --- a/drivers/riello_usb.c +++ b/drivers/riello_usb.c @@ -36,7 +36,7 @@ #include "riello.h" #define DRIVER_NAME "Riello USB driver" -#define DRIVER_VERSION "0.12" +#define DRIVER_VERSION "0.13" #define DEFAULT_OFFDELAY 5 /*!< seconds (max 0xFF) */ #define DEFAULT_BOOTDELAY 5 /*!< seconds (max 0xFF) */ diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index cab7b14ca1..d441e783a4 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -137,7 +137,7 @@ #include "usb-common.h" #define DRIVER_NAME "Tripp Lite OMNIVS / SMARTPRO driver" -#define DRIVER_VERSION "0.37" +#define DRIVER_VERSION "0.38" /* driver description structure */ upsdrv_info_t upsdrv_info = { diff --git a/drivers/usb-common.c b/drivers/usb-common.c index e46d415176..8256c95069 100644 --- a/drivers/usb-common.c +++ b/drivers/usb-common.c @@ -410,3 +410,85 @@ void warn_if_bad_usb_port_filename(const char *fn) { __func__, fn); return; } + +/* Retries were introduced for "Tripp Lite" devices, see + * https://github.com/networkupstools/nut/issues/414 + */ +#define MAX_STRING_DESC_TRIES 3 + +/* API neutral, handles retries */ +static int nut_usb_get_string_descriptor( + usb_dev_handle *udev, + int StringIdx, + int langid, + char *buf, + size_t buflen) +{ + int ret = -1; + int tries = MAX_STRING_DESC_TRIES; + + while (tries--) { + ret = usb_get_string(udev, (usb_ctrl_strindex)StringIdx, langid, (usb_ctrl_charbuf)buf, buflen); + if (ret >= 0) { + break; + } else if (tries) { + upsdebugx(1, "%s: string descriptor %d request failed, retrying...", __func__, StringIdx); + usleep(50000); /* 50 ms, might help in some cases */ + } + } + return ret; +} + +/* API neutral, assumes en_US if langid descriptor is broken */ +int nut_usb_get_string( + usb_dev_handle *udev, + int StringIdx, + char *buf, + size_t buflen) +{ + int ret; + char buffer[255]; + int langid; + int len; + int i; + + if (!udev || StringIdx < 1 || StringIdx > 255) { + return -1; + } + + /* request langid descriptor */ + ret = nut_usb_get_string_descriptor(udev, 0, 0, buffer, 4); + if (ret < 0) + return ret; + + if (ret == 4 && buffer[0] >= 4 && buffer[1] == USB_DT_STRING) { + langid = buffer[2] | (buffer[3] << 8); + } else { + upsdebugx(1, "%s: Broken language identifier, assuming en_US", __func__); + langid = 0x0409; + } + + /* retrieve string in preferred language */ + ret = nut_usb_get_string_descriptor(udev, StringIdx, langid, buffer, sizeof(buffer)); + if (ret < 0) { +#ifdef WIN32 + /* only for libusb0 ? */ + errno = -ret; +#endif + return ret; + } + + /* translate simple UTF-16LE to 8-bit */ + len = ret < (int)buflen ? ret : (int)buflen; + len = len / 2 - 1; // 16-bit characters, without header + len = len < (int)buflen - 1 ? len : (int)buflen - 1; // reserve for null terminator + for (i = 0; i < len; i++) { + if (buffer[2 + i * 2 + 1] == 0) + buf[i] = buffer[2 + i * 2]; + else + buf[i] = '?'; // not decoded + } + buf[i] = '\0'; + + return len; +} diff --git a/drivers/usb-common.h b/drivers/usb-common.h index 7178e44a07..b71bed8a06 100644 --- a/drivers/usb-common.h +++ b/drivers/usb-common.h @@ -560,4 +560,9 @@ void nut_usb_addvars(void); * here, to use in several USB-capable drivers. */ void warn_if_bad_usb_port_filename(const char *fn); +/* Retrieves string descriptor from device, decoded to 8-bit string, + * applies retries on failures, and assumes en_US in case the + * langid descriptor is invalid. */ +int nut_usb_get_string(usb_dev_handle *udev, int StringIdx, char *buf, size_t buflen); + #endif /* NUT_USB_COMMON_H */ diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index b8ff6662a5..b5968d0a02 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -29,7 +29,7 @@ */ #define DRIVER_NAME "Generic HID driver" -#define DRIVER_VERSION "0.56" +#define DRIVER_VERSION "0.57" #define HU_VAR_WAITBEFORERECONNECT "waitbeforereconnect"