Skip to content

Commit

Permalink
Multiple USB-capable drivers: add ways to tune USB HID config, report…
Browse files Browse the repository at this point in the history
…, descriptor indexes and in/out endpoints [#2149]

Signed-off-by: Jim Klimov <[email protected]>
  • Loading branch information
jimklimov committed Mar 1, 2024
1 parent f2fc470 commit d9523b8
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 14 deletions.
7 changes: 7 additions & 0 deletions NEWS.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ https://github.com/networkupstools/nut/milestone/10
* Driver programs with debug tracing support via `-D` CLI option and/or
the `NUT_DEBUG_LEVEL` environment variable now check those earlier in
their life-time, so that initialization routine can be debugged. [#2259]
* Multiple USB-capable drivers got options to customize `usb_config_index`
`usb_hid_rep_index`, `usb_hid_desc_index`, `usb_hid_ep_in` and
`usb_hid_ep_out` hardware connection settings via `ups.conf` options.
This is treated as experimental, not all code paths may be actually
using such values from `struct usb_communication_subdriver_t` rather
than hard-coded defaults. Discovery of correct values is up to the
user at the moment (using `lsusb`, internet search, luck...) [#2149]

- nut-driver-enumerator (NDE) service/script:
* The optional daemon mode (primarily useful for systems which monitor
Expand Down
9 changes: 9 additions & 0 deletions conf/ups.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,15 @@ maxretry = 3
# so it is not called by default. Yet others can be composite
# devices which use a non-zero interface to represent the UPS.
#
# usb_config_index=hexnum, usb_hid_rep_index=hexnum,
# usb_hid_desc_index=hexnum, usb_hid_ep_in=hexnum, usb_hid_ep_out=hexnum:
# OPTIONAL. Force use of specific interface, endpoint, descriptor
# index etc. numbers, rather than defaulting to 0 (rarely other
# values in certain drivers for some devices known to use non-zero
# numbers). As a rule of thumb for `usb_hid_desc_index` discovery,
# you can see larger `wDescriptorLength` values (roughly 600+ bytes)
# in reports of `lsusb` or similar tools.
#
# default.<variable>: OPTIONAL. Set a default value for <variable> which is
# used in case the UPS doesn't provide a value, but which will be
# overwritten if a value is available from the UPS, e.g.:
Expand Down
15 changes: 15 additions & 0 deletions docs/man/nut_usb_addvars.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,18 @@ If you must really know *which* one, it will not!
Force redundant call to `usb_set_altinterface()`, especially if needed
for devices serving multiple USB roles where the UPS is not represented
by the interface number `0` (default).

*usb_config_index*::
*usb_hid_rep_index*::
*usb_hid_desc_index*::
*usb_hid_ep_in*::
*usb_hid_ep_out*::

Force use of specific interface, endpoint, descriptor index etc. numbers,
rather than defaulting to 0 (rarely other values in certain drivers for
some devices known to use non-zero numbers). Specified as a hexadecimal
number.
+
As a rule of thumb for `usb_hid_desc_index` discovery, you can see larger
`wDescriptorLength` values (roughly 600+ bytes) in reports of `lsusb` or
similar tools.
15 changes: 15 additions & 0 deletions docs/man/ups.conf.txt
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,21 @@ certain UPSes from working on Mac OS X. If your UPS requires explicitly setting
the alternate interface, include this flag, and email the nut-upsdev list with
details about your UPS and operating system.

*usb_config_index*::
*usb_hid_rep_index*::
*usb_hid_desc_index*::
*usb_hid_ep_in*::
*usb_hid_ep_out*::

Optional. Force use of specific interface, endpoint, descriptor index etc.
numbers, rather than defaulting to 0 (rarely other values in certain drivers
for some devices known to use non-zero numbers). Specified as a hexadecimal
number.
+
As a rule of thumb for `usb_hid_desc_index` discovery, you can see larger
`wDescriptorLength` values (roughly 600+ bytes) in reports of `lsusb` or
similar tools.

*default.<variable>*::

Optional. Set a default value for <variable> which is used in case the UPS
Expand Down
4 changes: 3 additions & 1 deletion docs/nut.dict
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
personal_ws-1.1 en 3468 utf-8
personal_ws-1.1 en 3470 utf-8
AAC
AAS
ABI
Expand Down Expand Up @@ -2177,6 +2177,7 @@ hasFeature
hb
hcd
hcl
hexnum
hg
hh
hibernate's
Expand Down Expand Up @@ -2897,6 +2898,7 @@ relicensing
remoting
renderer
renderers
repindex
repo
reportId
reposurgeon
Expand Down
18 changes: 12 additions & 6 deletions drivers/arduino-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,18 +151,24 @@ static int arduino_claim(HIDDevice_t *hd)
case POSSIBLY_SUPPORTED:
/* by default, reject, unless the productid option is given */
if (getval("productid")) {
usb->hid_ep_in=4;
usb->hid_ep_out=5;
usb->hid_rep_index = 2;
if (!getval("usb_hid_ep_in"))
usb->hid_ep_in=4;
if (!getval("usb_hid_ep_out"))
usb->hid_ep_out=5;
if (!getval("usb_hid_rep_index"))
usb->hid_rep_index = 2;
return 1;
}
possibly_supported("Arduino", hd);
return 0;

case SUPPORTED:
usb->hid_ep_in=4;
usb->hid_ep_out=5;
usb->hid_rep_index = 2;
if (!getval("usb_hid_ep_in"))
usb->hid_ep_in=4;
if (!getval("usb_hid_ep_out"))
usb->hid_ep_out=5;
if (!getval("usb_hid_rep_index"))
usb->hid_rep_index = 2;
return 1;

case NOT_SUPPORTED:
Expand Down
14 changes: 12 additions & 2 deletions drivers/libshut.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
#include "common.h" /* for xmalloc, upsdebugx prototypes */

#define SHUT_DRIVER_NAME "SHUT communication driver"
#define SHUT_DRIVER_VERSION "0.88"
#define SHUT_DRIVER_VERSION "0.89"

/* communication driver description structure */
upsdrv_info_t comm_upsdrv_info = {
Expand Down Expand Up @@ -383,6 +383,7 @@ static int libshut_open(
*/
usb_ctrl_char buf[20] __attribute__((aligned(4)));
char string[MAX_STRING_SIZE];
const char *s;
struct my_hid_descriptor *desc;
struct device_descriptor_s *dev_descriptor;

Expand All @@ -394,6 +395,14 @@ static int libshut_open(
* version is at index 1 (in which case, bcdDevice == 0x0202) */
usb_ctrl_descindex hid_desc_index = 0;

if ((s = getval("usb_hid_desc_index"))) {
unsigned short us = 0;
if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_DESCINDEX_MAX)) {
fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_desc_index", __func__);
}
hid_desc_index = (usb_ctrl_descindex)us;
}

if (!arg_device_path) {
fatalx(EXIT_FAILURE, "%s: arg_device_path=null", __func__);
}
Expand Down Expand Up @@ -537,7 +546,8 @@ static int libshut_open(
upsdebugx(2, "Device matches");

if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) {
upsdebugx(1, "Eaton device v2.02. Using full report descriptor");
upsdebugx(1, "Eaton device v2.02. Using full report descriptor");
if (!getval("usb_hid_desc_index"))
hid_desc_index = 1;
}

Expand Down
62 changes: 60 additions & 2 deletions drivers/libusb0.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
#endif

#define USB_DRIVER_NAME "USB communication driver (libusb 0.1)"
#define USB_DRIVER_VERSION "0.45"
#define USB_DRIVER_VERSION "0.47"

/* driver description structure */
upsdrv_info_t comm_upsdrv_info = {
Expand Down Expand Up @@ -94,6 +94,12 @@ void nut_usb_addvars(void)

addvar(VAR_VALUE, "usb_set_altinterface", "Force redundant call to usb_set_altinterface() (value=bAlternateSetting; default=0)");

addvar(VAR_VALUE, "usb_config_index", "Deeper tuning of USB communications for complex devices");
addvar(VAR_VALUE, "usb_hid_rep_index", "Deeper tuning of USB communications for complex devices");
addvar(VAR_VALUE, "usb_hid_desc_index", "Deeper tuning of USB communications for complex devices");
addvar(VAR_VALUE, "usb_hid_ep_in", "Deeper tuning of USB communications for complex devices");
addvar(VAR_VALUE, "usb_hid_ep_out", "Deeper tuning of USB communications for complex devices");

dstate_setinfo("driver.version.usb", "libusb-0.1 (or compat)");

upsdebugx(1, "Using USB implementation: %s", dstate_getinfo("driver.version.usb"));
Expand Down Expand Up @@ -219,6 +225,45 @@ static int libusb_open(usb_dev_handle **udevp,

struct usb_bus *busses;

static int usb_hid_number_opts_parsed = 0;
if (!usb_hid_number_opts_parsed) {
const char *s;
unsigned short us = 0;

if ((s = getval("usb_config_index"))) {
if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_CFGINDEX_MAX)) {
fatalx(EXIT_FAILURE, "%s: could not parse usb_config_index", __func__);
}
usb_subdriver.usb_config_index = (usb_ctrl_cfgindex)us;
}
if ((s = getval("usb_hid_rep_index"))) {
if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_REPINDEX_MAX)) {
fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_rep_index", __func__);
}
usb_subdriver.hid_rep_index = (usb_ctrl_repindex)us;
}
if ((s = getval("usb_hid_desc_index"))) {
if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_DESCINDEX_MAX)) {
fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_desc_index", __func__);
}
usb_subdriver.hid_desc_index = (usb_ctrl_descindex)us;
}
if ((s = getval("usb_hid_ep_in"))) {
if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_ENDPOINT_MAX)) {
fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_ep_in", __func__);
}
usb_subdriver.hid_ep_in = (usb_ctrl_endpoint)us;
}
if ((s = getval("usb_hid_ep_out"))) {
if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_ENDPOINT_MAX)) {
fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_ep_out", __func__);
}
usb_subdriver.hid_ep_out = (usb_ctrl_endpoint)us;
}

usb_hid_number_opts_parsed = 1;
}

/* libusb base init */
usb_init();
usb_find_busses();
Expand Down Expand Up @@ -374,7 +419,8 @@ static int libusb_open(usb_dev_handle **udevp,

/* FIXME: extend to Eaton OEMs (HP, IBM, ...) */
if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) {
usb_subdriver.hid_desc_index = 1;
if (!getval("usb_hid_desc_index"))
usb_subdriver.hid_desc_index = 1;
}

upsdebugx(2, "Trying to match device");
Expand Down Expand Up @@ -618,6 +664,18 @@ static int libusb_open(usb_dev_handle **udevp,
"Report descriptor retrieved (Reportlen = %"
PRI_NUT_USB_CTRL_CHARBUFSIZE ")", rdlen);
upsdebugx(2, "Found HID device");

upsdebugx(3, "Using default, detected or customized USB HID numbers: "
"usb_config_index=%d usb_hid_rep_index=%d "
"usb_hid_desc_index=%d "
"usb_hid_ep_in=%d usb_hid_ep_out=%d",
usb_subdriver.usb_config_index,
usb_subdriver.hid_rep_index,
usb_subdriver.hid_desc_index,
usb_subdriver.hid_ep_in,
usb_subdriver.hid_ep_out
);

fflush(stdout);

return rdlen;
Expand Down
60 changes: 59 additions & 1 deletion drivers/libusb1.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
#include "nut_stdint.h"

#define USB_DRIVER_NAME "USB communication driver (libusb 1.0)"
#define USB_DRIVER_VERSION "0.46"
#define USB_DRIVER_VERSION "0.47"

/* driver description structure */
upsdrv_info_t comm_upsdrv_info = {
Expand Down Expand Up @@ -88,6 +88,12 @@ void nut_usb_addvars(void)

addvar(VAR_VALUE, "usb_set_altinterface", "Force redundant call to usb_set_altinterface() (value=bAlternateSetting; default=0)");

addvar(VAR_VALUE, "usb_config_index", "Deeper tuning of USB communications for complex devices");
addvar(VAR_VALUE, "usb_hid_rep_index", "Deeper tuning of USB communications for complex devices");
addvar(VAR_VALUE, "usb_hid_desc_index", "Deeper tuning of USB communications for complex devices");
addvar(VAR_VALUE, "usb_hid_ep_in", "Deeper tuning of USB communications for complex devices");
addvar(VAR_VALUE, "usb_hid_ep_out", "Deeper tuning of USB communications for complex devices");

#ifdef LIBUSB_API_VERSION
dstate_setinfo("driver.version.usb", "libusb-%u.%u.%u (API: 0x%x)", v->major, v->minor, v->micro, LIBUSB_API_VERSION);
#else /* no LIBUSB_API_VERSION */
Expand Down Expand Up @@ -189,6 +195,45 @@ static int nut_libusb_open(libusb_device_handle **udevp,
unsigned char rdbuf[MAX_REPORT_SIZE];
int32_t rdlen;

static int usb_hid_number_opts_parsed = 0;
if (!usb_hid_number_opts_parsed) {
const char *s;
unsigned short us = 0;

if ((s = getval("usb_config_index"))) {
if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_CFGINDEX_MAX)) {
fatalx(EXIT_FAILURE, "%s: could not parse usb_config_index", __func__);
}
usb_subdriver.usb_config_index = (usb_ctrl_cfgindex)us;
}
if ((s = getval("usb_hid_rep_index"))) {
if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_REPINDEX_MAX)) {
fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_rep_index", __func__);
}
usb_subdriver.hid_rep_index = (usb_ctrl_repindex)us;
}
if ((s = getval("usb_hid_desc_index"))) {
if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_DESCINDEX_MAX)) {
fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_desc_index", __func__);
}
usb_subdriver.hid_desc_index = (usb_ctrl_descindex)us;
}
if ((s = getval("usb_hid_ep_in"))) {
if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_ENDPOINT_MAX)) {
fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_ep_in", __func__);
}
usb_subdriver.hid_ep_in = (usb_ctrl_endpoint)us;
}
if ((s = getval("usb_hid_ep_out"))) {
if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_ENDPOINT_MAX)) {
fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_ep_out", __func__);
}
usb_subdriver.hid_ep_out = (usb_ctrl_endpoint)us;
}

usb_hid_number_opts_parsed = 1;
}

/* libusb base init */
if (libusb_init(NULL) < 0) {
libusb_exit(NULL);
Expand Down Expand Up @@ -381,6 +426,7 @@ static int nut_libusb_open(libusb_device_handle **udevp,

/* FIXME: extend to Eaton OEMs (HP, IBM, ...) */
if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) {
if (!getval("usb_hid_desc_index"))
usb_subdriver.hid_desc_index = 1;
}

Expand Down Expand Up @@ -702,6 +748,18 @@ static int nut_libusb_open(libusb_device_handle **udevp,

upsdebugx(2, "Report descriptor retrieved (Reportlen = %d)", rdlen);
upsdebugx(2, "Found HID device");

upsdebugx(3, "Using default, detected or customized USB HID numbers: "
"usb_config_index=%d usb_hid_rep_index=%d "
"usb_hid_desc_index=%d "
"usb_hid_ep_in=%d usb_hid_ep_out=%d",
usb_subdriver.usb_config_index,
usb_subdriver.hid_rep_index,
usb_subdriver.hid_desc_index,
usb_subdriver.hid_ep_in,
usb_subdriver.hid_ep_out
);

fflush(stdout);
libusb_free_device_list(devlist, 1);

Expand Down
5 changes: 5 additions & 0 deletions drivers/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,11 @@ void storeval(const char *var, char *val)
|| !strcmp(var, "device")
|| !strcmp(var, "busport")
|| !strcmp(var, "usb_set_altinterface")
|| !strcmp(var, "usb_config_index")
|| !strcmp(var, "usb_hid_rep_index")
|| !strcmp(var, "usb_hid_desc_index")
|| !strcmp(var, "usb_hid_ep_in")
|| !strcmp(var, "usb_hid_ep_out")
|| !strcmp(var, "allow_duplicates")
|| !strcmp(var, "langid_fix")
|| !strcmp(var, "noscanlangid")
Expand Down
6 changes: 4 additions & 2 deletions drivers/powervar-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,16 @@ static int powervar_claim(HIDDevice_t *hd)
case POSSIBLY_SUPPORTED:
/* by default, reject, unless the productid option is given */
if (getval("productid")) {
usb->hid_rep_index = 1;
if (!getval("usb_hid_rep_index"))
usb->hid_rep_index = 1;
return 1;
}
possibly_supported("Powervar", hd);
return 0;

case SUPPORTED:
usb->hid_rep_index = 1;
if (!getval("usb_hid_rep_index"))
usb->hid_rep_index = 1;
return 1;

case NOT_SUPPORTED:
Expand Down
2 changes: 2 additions & 0 deletions tools/nut-scanner/scan_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,8 @@ nutscan_device_t * nutscan_scan_usb(nutscan_usb_t * scanopts)
}
#endif /* WITH_LIBUSB_1_0 */

/* FIXME: Detect and suggest HID index, interface number, etc. */

if (scanopts->report_bcdDevice) {
/* Not currently matched by drivers, hence commented
* for now even if requested via scanopts */
Expand Down

0 comments on commit d9523b8

Please sign in to comment.