Skip to content

Commit

Permalink
Merge pull request networkupstools#2054 from jimklimov/issue-2043
Browse files Browse the repository at this point in the history
Allow USB device matching by `busport` value
  • Loading branch information
jimklimov authored Sep 16, 2023
2 parents 484ed6d + 573f93b commit 6d992c7
Show file tree
Hide file tree
Showing 17 changed files with 305 additions and 43 deletions.
5 changes: 5 additions & 0 deletions NEWS.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,11 @@ as part of https://github.com/networkupstools/nut/issues/1410 solution.
of all USB drivers using these options to include the same description
[#1766]

- Added a "busport" USB matching option (if supported by the hardware, OS and
libusb on the particular deployment, it should allow to specify physical
port numbers on an USB hub, rather than logical "device" enumeration values,
and in turn -- this should be less volatile across reboots etc.) [#2043]

- 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]
Expand Down
22 changes: 17 additions & 5 deletions docs/man/nut_usb_addvars.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,30 @@ Examples:

Select a UPS on a specific USB bus or group of buses. The argument is
a regular expression that must match the bus name where the UPS is
connected (e.g. `bus="002"` or `bus="00[2-3]"`) as seen in
`/proc/bus/usb/devices` or *lsusb(8)*; including leading zeroes.
connected (e.g. `bus="002"` or `bus="00[2-3]"`) as seen on Linux in
`/sys/bus/usb/devices` or *lsusb(8)*; including leading zeroes.

*device =* 'regex'::

Select a UPS on a specific USB device or group of devices. The argument is
a regular expression that must match the device name where the UPS is
connected (e.g. `device="001"` or `device="00[1-2]"`) as seen in
`/proc/bus/usb/devices` or *lsusb(8)*; including leading zeroes.
Note that device numbers are not guaranteed by the OS to be stable across
connected (e.g. `device="001"` or `device="00[1-2]"`) as seen on Linux
in `/sys/bus/usb/devices` or *lsusb(8)*; including leading zeroes.
+
NOTE: device numbers are not guaranteed by the OS to be stable across
re-boots or device re-plugging.

*busport =* 'regex'::

If supported by the hardware, OS and libusb on the particular deployment,
this option should allow to specify physical port numbers on an USB hub,
rather than logical `device` enumeration values, and in turn -- this should
be less volatile across reboots or re-plugging.
+
NOTE: this option is not practically supported by some NUT builds
(it should be ignored with a warning then), and not by all systems
that NUT can run on.

*allow_duplicates*::

If you have several UPS devices which may not be uniquely identified by
Expand Down
3 changes: 2 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 3222 utf-8
personal_ws-1.1 en 3223 utf-8
AAS
ABI
ACFAIL
Expand Down Expand Up @@ -1639,6 +1639,7 @@ bugfixes
buildbots
builddir
bullseye
busport
busybox
bv
bypassvolts
Expand Down
14 changes: 12 additions & 2 deletions drivers/blazer_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
#endif

#define DRIVER_NAME "Megatec/Q1 protocol USB driver"
#define DRIVER_VERSION "0.16"
#define DRIVER_VERSION "0.17"

/* driver description structure */
upsdrv_info_t upsdrv_info = {
Expand Down Expand Up @@ -597,7 +597,7 @@ void upsdrv_initups(void)
#ifndef TESTING
int ret, langid;
char tbuf[255]; /* Some devices choke on size > 255 */
char *regex_array[7];
char *regex_array[USBMATCHER_REGEXP_ARRAY_LIMIT];
char *subdrv = getval("subdriver");

warn_if_bad_usb_port_filename(device_path);
Expand All @@ -609,6 +609,13 @@ void upsdrv_initups(void)
regex_array[4] = getval("serial");
regex_array[5] = getval("bus");
regex_array[6] = getval("device");
#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
regex_array[7] = getval("busport");
# else
if (getval("busport")) {
upslogx(LOG_WARNING, "\"busport\" is configured for the device, but is not actually handled by current build combination of NUT and libusb (ignored)");
}
# endif

/* check for language ID workaround (#1) */
if (getval("langid_fix")) {
Expand Down Expand Up @@ -725,5 +732,8 @@ void upsdrv_cleanup(void)
free(usbdevice.Serial);
free(usbdevice.Bus);
free(usbdevice.Device);
#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
free(usbdevice.BusPort);
# endif
#endif /* TESTING */
}
22 changes: 20 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.87"
#define SHUT_DRIVER_VERSION "0.88"

/* communication driver description structure */
upsdrv_info_t comm_upsdrv_info = {
Expand Down Expand Up @@ -468,21 +468,36 @@ static int libshut_open(
free(curDevice->Serial);
free(curDevice->Bus);
free(curDevice->Device);
#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
free(curDevice->BusPort);
#endif
memset(curDevice, '\0', sizeof(*curDevice));

curDevice->VendorID = dev_descriptor->idVendor;
curDevice->ProductID = dev_descriptor->idProduct;
curDevice->Bus = strdup("serial");
curDevice->Device = strdup(arg_device_path);
#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
curDevice->BusPort = (char *)malloc(4);
if (curDevice->BusPort == NULL) {
fatal_with_errno(EXIT_FAILURE, "Out of memory");
}
upsdebugx(2, "%s: NOTE: BusPort is always zero with libshut", __func__);
sprintf(curDevice->BusPort, "%03d", 0);
#endif

curDevice->bcdDevice = dev_descriptor->bcdDevice;
curDevice->Vendor = strdup("Eaton");
curDevice->Vendor = NULL;
if (dev_descriptor->iManufacturer) {
ret = shut_get_string_simple(*arg_upsfd, dev_descriptor->iManufacturer,
string, MAX_STRING_SIZE);
if (ret > 0) {
curDevice->Vendor = strdup(string);
}
}
if (curDevice->Vendor == NULL) {
curDevice->Vendor = strdup("Eaton");
}

/* ensure iProduct retrieval */
if (dev_descriptor->iProduct) {
Expand Down Expand Up @@ -514,6 +529,9 @@ static int libshut_open(
upsdebugx(2, "- Product: %s", curDevice->Product);
upsdebugx(2, "- Serial Number: %s", curDevice->Serial);
upsdebugx(2, "- Bus: %s", curDevice->Bus);
#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
upsdebugx(2, "- Bus Port: %s", curDevice->BusPort ? curDevice->BusPort : "unknown");
#endif
upsdebugx(2, "- Device: %s", curDevice->Device ? curDevice->Device : "unknown");
upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice);
upsdebugx(2, "Device matches");
Expand Down
5 changes: 4 additions & 1 deletion drivers/libshut.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ typedef int usb_ctrl_timeout_msec; /* in milliseconds */

/*!
* SHUTDevice_t: Describe a SHUT device. This structure contains exactly
* the 5 pieces of information by which a SHUT device identifies
* the 5 or more pieces of information by which a SHUT device identifies
* itself, so it serves as a kind of "fingerprint" of the device. This
* information must be matched exactly when reopening a device, and
* therefore must not be "improved" or updated by a client
Expand All @@ -132,6 +132,9 @@ typedef struct SHUTDevice_s {
char* Bus; /*!< Bus name, e.g. "003" */
uint16_t bcdDevice; /*!< Device release number */
char *Device; /*!< Device name on the bus, e.g. "001" */
#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
char *BusPort; /*!< Port name, e.g. "001" */
#endif
} SHUTDevice_t;

/*!
Expand Down
22 changes: 21 additions & 1 deletion 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.44"
#define USB_DRIVER_VERSION "0.45"

/* driver description structure */
upsdrv_info_t comm_upsdrv_info = {
Expand Down Expand Up @@ -76,6 +76,11 @@ 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");
/* Not supported by libusb0, but let's not crash config
* parsing on unknown keywords due to such nuances! :) */
addvar(VAR_VALUE, "busport", "Regular expression to match USB bus port name"
" (tolerated but ignored in this build)"
);

/* Warning: this feature is inherently non-deterministic!
* If you only care to know that at least one of your no-name UPSes is online,
Expand Down Expand Up @@ -287,6 +292,9 @@ static int libusb_open(usb_dev_handle **udevp,
free(curDevice->Serial);
free(curDevice->Bus);
free(curDevice->Device);
#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
free(curDevice->BusPort);
#endif
memset(curDevice, '\0', sizeof(*curDevice));

/* Keep the list of items in sync with those matched by
Expand All @@ -298,6 +306,15 @@ static int libusb_open(usb_dev_handle **udevp,
curDevice->Device = xstrdup(dev->filename);
curDevice->bcdDevice = dev->descriptor.bcdDevice;

#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
curDevice->BusPort = (char *)malloc(4);
if (curDevice->BusPort == NULL) {
fatal_with_errno(EXIT_FAILURE, "Out of memory");
}
upsdebugx(2, "%s: NOTE: BusPort is always zero with libusb0", __func__);
sprintf(curDevice->BusPort, "%03d", 0);
#endif

if (dev->descriptor.iManufacturer) {
retries = MAX_RETRY;
while (retries > 0) {
Expand Down Expand Up @@ -347,6 +364,9 @@ static int libusb_open(usb_dev_handle **udevp,
upsdebugx(2, "- Serial Number: %s", curDevice->Serial ? curDevice->Serial : "unknown");
upsdebugx(2, "- Bus: %s", curDevice->Bus ? curDevice->Bus : "unknown");
upsdebugx(2, "- Device: %s", curDevice->Device ? curDevice->Device : "unknown");
#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
upsdebugx(2, "- Bus Port: %s", curDevice->BusPort ? curDevice->BusPort : "unknown");
#endif
upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice);

/* FIXME: extend to Eaton OEMs (HP, IBM, ...) */
Expand Down
35 changes: 34 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.45"
#define USB_DRIVER_VERSION "0.46"

/* driver description structure */
upsdrv_info_t comm_upsdrv_info = {
Expand Down Expand Up @@ -66,6 +66,15 @@ 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");
addvar(VAR_VALUE, "busport", "Regular expression to match USB bus port name"
#if (!defined WITH_USB_BUSPORT) || (!WITH_USB_BUSPORT)
/* Not supported by this version of libusb1,
* but let's not crash config parsing on
* unknown keywords due to such nuances! :)
*/
" (tolerated but ignored in this build)"
#endif
);

/* Warning: this feature is inherently non-deterministic!
* If you only care to know that at least one of your no-name UPSes is online,
Expand Down Expand Up @@ -163,6 +172,9 @@ static int nut_libusb_open(libusb_device_handle **udevp,
const struct libusb_interface_descriptor *if_desc;
libusb_device_handle *udev;
uint8_t bus_num, device_addr;
#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
uint8_t bus_port;
#endif
int ret, res;
unsigned char buf[20];
const unsigned char *p;
Expand Down Expand Up @@ -237,6 +249,9 @@ static int nut_libusb_open(libusb_device_handle **udevp,
free(curDevice->Serial);
free(curDevice->Bus);
free(curDevice->Device);
#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
free(curDevice->BusPort);
#endif
memset(curDevice, '\0', sizeof(*curDevice));

/* Keep the list of items in sync with those matched by
Expand Down Expand Up @@ -278,6 +293,21 @@ static int nut_libusb_open(libusb_device_handle **udevp,
}
}

#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
bus_port = libusb_get_port_number(device);
curDevice->BusPort = (char *)malloc(4);
if (curDevice->BusPort == NULL) {
libusb_free_device_list(devlist, 1);
fatal_with_errno(EXIT_FAILURE, "Out of memory");
}
if (bus_port > 0) {
sprintf(curDevice->BusPort, "%03d", bus_port);
} else {
upsdebugx(1, "%s: invalid libusb bus number %i",
__func__, bus_port);
}
#endif

curDevice->VendorID = dev_desc.idVendor;
curDevice->ProductID = dev_desc.idProduct;
curDevice->bcdDevice = dev_desc.bcdDevice;
Expand Down Expand Up @@ -342,6 +372,9 @@ static int nut_libusb_open(libusb_device_handle **udevp,
upsdebugx(2, "- Product: %s", curDevice->Product ? curDevice->Product : "unknown");
upsdebugx(2, "- Serial Number: %s", curDevice->Serial ? curDevice->Serial : "unknown");
upsdebugx(2, "- Bus: %s", curDevice->Bus ? curDevice->Bus : "unknown");
#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT)
upsdebugx(2, "- Bus Port: %s", curDevice->BusPort ? curDevice->BusPort : "unknown");
#endif
upsdebugx(2, "- Device: %s", curDevice->Device ? curDevice->Device : "unknown");
upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice);

Expand Down
Loading

0 comments on commit 6d992c7

Please sign in to comment.