Logo Search packages:      
Sourcecode: nut version File versions

usb-common.c

/* usb-common.c - common useful USB functions

   Copyright (C) 2008  Arnaud Quette <arnaud.quette@gmail.com>

   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, int dev_VendorID, int dev_ProductID)
{
      int retval = NOT_SUPPORTED;
      usb_device_id_t *usbdev;

      for (usbdev = usb_device_id_list; usbdev->vendorID != -1; usbdev++) {

            if (usbdev->vendorID != dev_VendorID) {
                  continue;
            }

            /* flag as possibly supported if we see a known vendor */
            retval = POSSIBLY_SUPPORTED;

            if (usbdev->productID != dev_ProductID) {
                  continue;
            }

            /* call the specific handler, if it exists */
            if (usbdev->fun != NULL) {
                  (*usbdev->fun)();
            }

            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;
      
      if (hd->VendorID != data->VendorID) {
            return 0;
      }

      if (hd->ProductID != data->ProductID) {
            return 0;
      }

      if (strcmp_null(hd->Vendor, data->Vendor) != 0) {
            return 0;
      }

      if (strcmp_null(hd->Product, data->Product) != 0) {
            return 0;
      }

      if (strcmp_null(hd->Serial, data->Serial) != 0) {
            return 0;
      }
#ifdef DEBUG_EXACT_MATCH_BUS
      if (strcmp_null(hd->Bus, data->Bus) != 0) {
            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 (!matcher) {
            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
      *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
      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;
      char  *string;
      regmatch_t  match;

      if (!preg) {
            return 1;
      }

      if (!str) {
            str = "";
      }

      /* skip leading whitespace */
      for (len = 0; len < strlen(str); len++) {

            if (!strchr(" \t\n", str[len])) {
                  break;
            }
      }

      string = strdup(str+len);
      if (!string) {
            return -1;
      }

      /* 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[6];
} 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;
      
      r = match_regex_hex(data->regex[0], hd->VendorID);
      if (r != 1) {
            return r;
      }

      r = match_regex_hex(data->regex[1], hd->ProductID);
      if (r != 1) {
            return r;
      }

      r = match_regex(data->regex[2], hd->Vendor);
      if (r != 1) {
            return r;
      }

      r = match_regex(data->regex[3], hd->Product);
      if (r != 1) {
            return r;
      }

      r = match_regex(data->regex[4], hd->Serial);
      if (r != 1) {
            return r;
      }

      r = match_regex(data->regex[5], hd->Bus);
      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],
 * corresponding to: vendorid, productid, vendor, product, serial,
 * bus. 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
 * 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<6; 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 < 6; i++) {
            if (!data->regex[i]) {
                  continue;
            }

            regfree(data->regex[i]);
            free(data->regex[i]);
      }

      free(data);
      free(matcher);
}

Generated by  Doxygen 1.6.0   Back to index