I needed a "device" argument for USB matching in order to indepedently match two identical UPSen attached to the same bus. I'll attach a patch. Is that the preferred way of proposing such a change? The changes to usb-common.[hc] and libusb.c are straightforward (sans the DEBUG_EXACT_MATCH_DEVICE, which I modeled after DEBUG_EXACT_MATCH_BUS, whatever that is used for). The changes to the various USB drivers are mechanical. Maybe some #define for the regex array size (or even seven more #defines for the magical index values) would be useful. The line numbers in nutdrv.qx.c will not match yours; I've a much larger change (new driver) in there. No changes to documentation yet. -------------- next part -------------- diff -ru /tmp/nut-2.7.4/drivers/usb-common.h ./usb-common.h --- /tmp/nut-2.7.4/drivers/usb-common.h 2015-12-29 13:08:34.000000000 +0100 +++ ./usb-common.h 2021-01-29 15:41:13.000000000 +0100 @@ -30,7 +30,7 @@ /*! * USBDevice_t: Describe a USB device. This structure contains exactly - * the 5 pieces of information by which a USB device identifies + * the pieces of information by which a USB 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 @@ -44,6 +44,7 @@ char *Product; /*!< Device's Product Name */ char *Serial; /*!< Product serial number */ char *Bus; /*!< Bus name, e.g. "003" */ + char *Device; /*!< Device name, e.g. "001" */ uint16_t bcdDevice; /*!< Device release number */ } USBDevice_t; diff -ru /tmp/nut-2.7.4/drivers/usb-common.c ./usb-common.c --- /tmp/nut-2.7.4/drivers/usb-common.c 2015-12-29 09:42:34.000000000 +0100 +++ ./usb-common.c 2021-01-29 15:40:26.000000000 +0100 @@ -103,6 +103,11 @@ return 0; } #endif +#ifdef DEBUG_EXACT_MATCH_DEVICE + if (strcmp_null(hd->Device, data->Device) != 0) { + return 0; + } +#endif return 1; } @@ -138,6 +143,9 @@ #ifdef DEBUG_EXACT_MATCH_BUS data->Bus = hd->Bus ? strdup(hd->Bus) : NULL; #endif +#ifdef DEBUG_EXACT_MATCH_DEVICE + data->Device = hd->Device ? strdup(hd->Device) : NULL; +#endif *matcher = m; return 0; @@ -160,6 +168,9 @@ #ifdef DEBUG_EXACT_MATCH_BUS free(data->Bus); #endif +#ifdef DEBUG_EXACT_MATCH_DEVICE + free(data->Device); +#endif free(data); free(matcher); } @@ -271,7 +282,7 @@ /* private data type: hold a set of compiled regular expressions. */ typedef struct regex_matcher_data_s { - regex_t *regex[6]; + regex_t *regex[7]; } regex_matcher_data_t; /* private callback function for regex matches */ @@ -309,18 +320,23 @@ if (r != 1) { return r; } + + r = match_regex(data->regex[6], hd->Device); + if (r != 1) { + return r; + } return 1; } /* constructor: create a regular expression matcher. This matcher is - * based on six regular expression strings in regex_array[0..5], + * based on seven regular expression strings in regex_array[0..6], * corresponding to: vendorid, productid, vendor, product, serial, - * bus. Any of these strings can be NULL, which matches + * bus, device. Any of these strings can be NULL, which matches * everything. Cflags are as in regcomp(3). Typical values for cflags * are REG_ICASE (case insensitive matching) and REG_EXTENDED (use * extended regular expressions). On success, return 0 and store the * matcher in *matcher. On error, return -1 with errno set, or return - * i=1--6 to indicate that the regular expression regex_array[i-1] was + * i=1--7 to indicate that the regular expression regex_array[i-1] was * ill-formed (an error message can then be retrieved with * regerror(3)). */ @@ -345,7 +361,7 @@ m->privdata = (void *)data; m->next = NULL; - for (i=0; i<6; i++) { + for (i=0; i<7; i++) { r = compile_regex(&data->regex[i], regex[i], cflags); if (r == -2) { r = i+1; @@ -372,7 +388,7 @@ data = (regex_matcher_data_t *)matcher->privdata; - for (i = 0; i < 6; i++) { + for (i = 0; i < 7; i++) { if (!data->regex[i]) { continue; } diff -ru /tmp/nut-2.7.4/drivers/libusb.c ./libusb.c --- /tmp/nut-2.7.4/drivers/libusb.c 2015-12-29 13:08:34.000000000 +0100 +++ ./libusb.c 2021-01-29 15:31:33.000000000 +0100 @@ -63,6 +63,7 @@ addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)"); 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, "usb_set_altinterface", "Force redundant call to usb_set_altinterface() (value=bAlternateSetting; default=0)"); } @@ -198,11 +199,13 @@ free(curDevice->Product); free(curDevice->Serial); free(curDevice->Bus); + free(curDevice->Device); memset(curDevice, '\0', sizeof(*curDevice)); curDevice->VendorID = dev->descriptor.idVendor; curDevice->ProductID = dev->descriptor.idProduct; curDevice->Bus = strdup(bus->dirname); + curDevice->Device = strdup(dev->filename); curDevice->bcdDevice = dev->descriptor.bcdDevice; if (dev->descriptor.iManufacturer) { @@ -235,6 +238,7 @@ 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"); + upsdebugx(2, "- Device: %s", curDevice->Device ? curDevice->Device : "unknown"); upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice); if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { diff -ru /tmp/nut-2.7.4/drivers/blazer_usb.c ./blazer_usb.c --- /tmp/nut-2.7.4/drivers/blazer_usb.c 2015-12-29 13:08:34.000000000 +0100 +++ ./blazer_usb.c 2021-01-29 15:20:42.000000000 +0100 @@ -522,7 +522,7 @@ int ret, langid; char tbuf[255]; /* Some devices choke on size > 255 */ - char *regex_array[6]; + char *regex_array[7]; char *subdrv = getval("subdriver"); @@ -532,6 +532,7 @@ regex_array[3] = getval("product"); regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); + regex_array[6] = getval("device"); /* check for language ID workaround (#1) */ if (getval("langid_fix")) { diff -ru /tmp/nut-2.7.4/drivers/nutdrv_qx.c ./nutdrv_qx.c --- /tmp/nut-2.7.4/drivers/nutdrv_qx.c 2016-03-08 13:01:11.000000000 +0100 +++ ./nutdrv_qx.c 2021-02-01 14:58:02.000000000 +0100 @@ -1930,7 +2006,7 @@ int ret, langid; char tbuf[255]; /* Some devices choke on size > 255 */ - char *regex_array[6]; + char *regex_array[7]; char *subdrv = getval("subdriver"); @@ -1940,6 +2016,7 @@ regex_array[3] = getval("product"); regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); + regex_array[6] = getval("device"); /* Check for language ID workaround (#1) */ if (getval("langid_fix")) { diff -ru /tmp/nut-2.7.4/drivers/riello_usb.c ./riello_usb.c --- /tmp/nut-2.7.4/drivers/riello_usb.c 2015-12-29 13:08:34.000000000 +0100 +++ ./riello_usb.c 2021-01-29 15:21:07.000000000 +0100 @@ -793,7 +793,7 @@ }; int ret; - char *regex_array[6]; + char *regex_array[7]; char *subdrv = getval("subdriver"); @@ -803,6 +803,7 @@ regex_array[3] = getval("product"); regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); + regex_array[6] = getval("device"); /* pick up the subdriver name if set explicitly */ if (subdrv) { diff -ru /tmp/nut-2.7.4/drivers/tripplite_usb.c ./tripplite_usb.c --- /tmp/nut-2.7.4/drivers/tripplite_usb.c 2015-12-29 13:08:34.000000000 +0100 +++ ./tripplite_usb.c 2021-01-29 15:21:26.000000000 +0100 @@ -1429,7 +1429,7 @@ */ void upsdrv_initups(void) { - char *regex_array[6]; + char *regex_array[7]; char *value; int r; @@ -1440,6 +1440,7 @@ regex_array[3] = getval("product"); /* product string */ regex_array[4] = getval("serial"); /* probably won't see this */ regex_array[5] = getval("bus"); + regex_array[6] = getval("device"); r = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); if (r==-1) { diff -ru /tmp/nut-2.7.4/drivers/usbhid-ups.c ./usbhid-ups.c --- /tmp/nut-2.7.4/drivers/usbhid-ups.c 2015-12-29 13:08:34.000000000 +0100 +++ ./usbhid-ups.c 2021-01-29 15:21:44.000000000 +0100 @@ -920,7 +920,7 @@ subdriver_matcher = device_path; #else - char *regex_array[6]; + char *regex_array[7]; upsdebugx(1, "upsdrv_initups..."); @@ -943,6 +943,7 @@ regex_array[3] = getval("product"); regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); + regex_array[6] = getval("device"); ret = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); switch(ret)