/* usb-common.c - common useful USB functions Copyright (C) 2008 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common.h" #include "usb-common.h" int is_usb_device_supported(usb_device_id_t *usb_device_id_list, USBDevice_t *device) { int retval = NOT_SUPPORTED; usb_device_id_t *usbdev; for (usbdev = usb_device_id_list; (usbdev->vendorID != 0 || usbdev->productID != 0 || usbdev->fun != NULL); usbdev++ ) { if (usbdev->vendorID != device->VendorID) { continue; } /* flag as possibly supported if we see a known vendor */ retval = POSSIBLY_SUPPORTED; if (usbdev->productID != device->ProductID) { continue; } /* call the specific handler, if it exists */ if (usbdev->fun != NULL) { (*usbdev->fun)(device); } return SUPPORTED; } return retval; } /* ---------------------------------------------------------------------- */ /* matchers */ /* helper function: version of strcmp that tolerates NULL * pointers. NULL is considered to come before all other strings * alphabetically. */ static int strcmp_null(char *s1, char *s2) { if (s1 == NULL && s2 == NULL) { return 0; } if (s1 == NULL) { return -1; } if (s2 == NULL) { return 1; } return strcmp(s1, s2); } /* private callback function for exact matches */ static int match_function_exact(USBDevice_t *hd, void *privdata) { USBDevice_t *data = (USBDevice_t *)privdata; upsdebugx(3, "%s: matching a device...", __func__); if (hd->VendorID != data->VendorID) { upsdebugx(2, "%s: failed match of %s: %4x != %4x", __func__, "VendorID", hd->VendorID, data->VendorID); return 0; } if (hd->ProductID != data->ProductID) { upsdebugx(2, "%s: failed match of %s: %4x != %4x", __func__, "ProductID", hd->ProductID, data->ProductID); return 0; } if (strcmp_null(hd->Vendor, data->Vendor) != 0) { upsdebugx(2, "%s: failed match of %s: %s != %s", __func__, "Vendor", hd->Vendor, data->Vendor); return 0; } if (strcmp_null(hd->Product, data->Product) != 0) { upsdebugx(2, "%s: failed match of %s: %s != %s", __func__, "Product", hd->Product, data->Product); return 0; } if (strcmp_null(hd->Serial, data->Serial) != 0) { upsdebugx(2, "%s: failed match of %s: %s != %s", __func__, "Serial", hd->Serial, data->Serial); return 0; } #ifdef DEBUG_EXACT_MATCH_BUS if (strcmp_null(hd->Bus, data->Bus) != 0) { upsdebugx(2, "%s: failed match of %s: %s != %s", __func__, "Bus", hd->Bus, data->Bus); return 0; } #endif #ifdef DEBUG_EXACT_MATCH_DEVICE if (strcmp_null(hd->Device, data->Device) != 0) { upsdebugx(2, "%s: failed match of %s: %s != %s", __func__, "Device", hd->Device, data->Device); return 0; } #endif return 1; } /* constructor: create an exact matcher that matches the device. * On success, return 0 and store the matcher in *matcher. On * error, return -1 with errno set */ int USBNewExactMatcher(USBDeviceMatcher_t **matcher, USBDevice_t *hd) { USBDeviceMatcher_t *m; USBDevice_t *data; m = malloc(sizeof(*m)); if (!m) { return -1; } data = calloc(1, sizeof(*data)); if (!data) { free(m); return -1; } m->match_function = &match_function_exact; m->privdata = (void *)data; m->next = NULL; data->VendorID = hd->VendorID; data->ProductID = hd->ProductID; data->Vendor = hd->Vendor ? strdup(hd->Vendor) : NULL; data->Product = hd->Product ? strdup(hd->Product) : NULL; data->Serial = hd->Serial ? strdup(hd->Serial) : NULL; #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; } /* destructor: free matcher previously created with USBNewExactMatcher */ void USBFreeExactMatcher(USBDeviceMatcher_t *matcher) { USBDevice_t *data; if (!matcher) { return; } data = (USBDevice_t *)matcher->privdata; free(data->Vendor); free(data->Product); free(data->Serial); #ifdef DEBUG_EXACT_MATCH_BUS free(data->Bus); #endif #ifdef DEBUG_EXACT_MATCH_DEVICE free(data->Device); #endif free(data); free(matcher); } /* Private function for compiling a regular expression. On success, * store the compiled regular expression (or NULL) in *compiled, and * return 0. On error with errno set, return -1. If the supplied * regular expression is unparseable, return -2 (an error message can * then be retrieved with regerror(3)). Note that *compiled will be an * allocated value, and must be freed with regfree(), then free(), see * regex(3). As a special case, if regex==NULL, then set * *compiled=NULL (regular expression NULL is intended to match * anything). */ static int compile_regex(regex_t **compiled, char *regex, int cflags) { int r; regex_t *preg; if (regex == NULL) { *compiled = NULL; return 0; } preg = malloc(sizeof(*preg)); if (!preg) { return -1; } r = regcomp(preg, regex, cflags); if (r) { free(preg); return -2; } *compiled = preg; return 0; } /* Private function for regular expression matching. Check if the * entire string str (minus any initial and trailing whitespace) * matches the compiled regular expression preg. Return 1 if it * matches, 0 if not. Return -1 on error with errno set. Special * cases: if preg==NULL, it matches everything (no contraint). If * str==NULL, then it is treated as "". */ static int match_regex(regex_t *preg, char *str) { int r; size_t len = 0; char *string; regmatch_t match; if (!preg) { return 1; } if (!str) { string = xstrdup(""); } else { /* skip leading whitespace */ for (len = 0; len < strlen(str); len++) { if (!strchr(" \t\n", str[len])) { break; } } string = xstrdup(str+len); /* skip trailing whitespace */ for (len = strlen(string); len > 0; len--) { if (!strchr(" \t\n", string[len-1])) { break; } } string[len] = '\0'; } /* test the regular expression */ r = regexec(preg, string, 1, &match, 0); free(string); if (r) { return 0; } /* check that the match is the entire string */ if ((match.rm_so != 0) || (match.rm_eo != (int)len)) { return 0; } return 1; } /* Private function, similar to match_regex, but the argument being * matched is a (hexadecimal) number, rather than a string. It is * converted to a 4-digit hexadecimal string. */ static int match_regex_hex(regex_t *preg, int n) { char buf[10]; snprintf(buf, sizeof(buf), "%04x", n); return match_regex(preg, buf); } /* private data type: hold a set of compiled regular expressions. */ typedef struct regex_matcher_data_s { regex_t *regex[7]; } regex_matcher_data_t; /* private callback function for regex matches */ static int match_function_regex(USBDevice_t *hd, void *privdata) { regex_matcher_data_t *data = (regex_matcher_data_t *)privdata; int r; upsdebugx(3, "%s: matching a device...", __func__); r = match_regex_hex(data->regex[0], hd->VendorID); if (r != 1) { /* upsdebugx(2, "%s: failed match of %s: %4x !~ %s", __func__, "VendorID", hd->VendorID, data->regex[0]); */ upsdebugx(2, "%s: failed match of %s: %4x", __func__, "VendorID", hd->VendorID); return r; } r = match_regex_hex(data->regex[1], hd->ProductID); if (r != 1) { /* upsdebugx(2, "%s: failed match of %s: %4x !~ %s", __func__, "ProductID", hd->ProductID, data->regex[1]); */ upsdebugx(2, "%s: failed match of %s: %4x", __func__, "ProductID", hd->ProductID); return r; } r = match_regex(data->regex[2], hd->Vendor); if (r != 1) { /* upsdebugx(2, "%s: failed match of %s: %s !~ %s", __func__, "Vendor", hd->Vendor, data->regex[2]); */ upsdebugx(2, "%s: failed match of %s: %s", __func__, "Vendor", hd->Vendor); return r; } r = match_regex(data->regex[3], hd->Product); if (r != 1) { /* upsdebugx(2, "%s: failed match of %s: %s !~ %s", __func__, "Product", hd->Product, data->regex[3]); */ upsdebugx(2, "%s: failed match of %s: %s", __func__, "Product", hd->Product); return r; } r = match_regex(data->regex[4], hd->Serial); if (r != 1) { /* upsdebugx(2, "%s: failed match of %s: %s !~ %s", __func__, "Serial", hd->Serial, data->regex[4]); */ upsdebugx(2, "%s: failed match of %s: %s", __func__, "Serial", hd->Serial); return r; } r = match_regex(data->regex[5], hd->Bus); if (r != 1) { /* upsdebugx(2, "%s: failed match of %s: %s !~ %s", __func__, "Bus", hd->Bus, data->regex[5]); */ upsdebugx(2, "%s: failed match of %s: %s", __func__, "Bus", hd->Bus); return r; } r = match_regex(data->regex[6], hd->Device); if (r != 1) { /* upsdebugx(2, "%s: failed match of %s: %s !~ %s", __func__, "Device", hd->Device, data->regex[6]); */ upsdebugx(2, "%s: failed match of %s: %s", __func__, "Device", hd->Device); return r; } return 1; } /* constructor: create a regular expression matcher. This matcher is * based on seven regular expression strings in regex_array[0..6], * corresponding to: vendorid, productid, vendor, product, serial, * 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--7 to indicate that the regular expression regex_array[i-1] was * ill-formed (an error message can then be retrieved with * regerror(3)). */ int USBNewRegexMatcher(USBDeviceMatcher_t **matcher, char **regex, int cflags) { int r, i; USBDeviceMatcher_t *m; regex_matcher_data_t *data; m = malloc(sizeof(*m)); if (!m) { return -1; } data = calloc(1, sizeof(*data)); if (!data) { free(m); return -1; } m->match_function = &match_function_regex; m->privdata = (void *)data; m->next = NULL; for (i=0; i<7; i++) { r = compile_regex(&data->regex[i], regex[i], cflags); if (r == -2) { r = i+1; } if (r) { USBFreeRegexMatcher(m); return r; } } *matcher = m; return 0; } void USBFreeRegexMatcher(USBDeviceMatcher_t *matcher) { int i; regex_matcher_data_t *data; if (!matcher) { return; } data = (regex_matcher_data_t *)matcher->privdata; for (i = 0; i < 7; i++) { if (!data->regex[i]) { continue; } regfree(data->regex[i]); free(data->regex[i]); } free(data); free(matcher); } void warn_if_bad_usb_port_filename(const char *fn) { /* USB drivers ignore the 'port' setting - log a notice * if it is not "auto". Note: per se, ignoring the port * (or internally the device_path variable from main.c) * is currently not a bug and is actually documented at * docs/config-notes.txt; however it is not something * evident to users during troubleshooting a device. * Documentation and common practice recommend port=auto * so here we warn during driver start if it has some * other value and users might think it is honoured. */ if (!fn) { upslogx(LOG_WARNING, "WARNING: %s(): port argument was not " "specified to the driver", __func__); return; } if (!strcmp(fn, "auto")) return; upslogx(LOG_WARNING, "WARNING: %s(): port argument specified to\n" " the driver is \"%s\" but USB drivers do " "not use it and rely on\n" " libusb walking all devices and matching " "their identification metadata.\n" " NUT documentation recommends port=\"auto\" " "for USB devices to avoid confusion.", __func__, fn); return; }