diff options
Diffstat (limited to 'src/udev/udev-builtin-hwdb.c')
-rw-r--r-- | src/udev/udev-builtin-hwdb.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c new file mode 100644 index 0000000000..aa996f375d --- /dev/null +++ b/src/udev/udev-builtin-hwdb.c @@ -0,0 +1,247 @@ +/* + * usb-db, pci-db - lookup vendor/product database + * + * Copyright (C) 2009 Lennart Poettering <lennart@poettering.net> + * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <inttypes.h> +#include <ctype.h> +#include <stdlib.h> + +#include "udev.h" + +static int get_id_attr( + struct udev_device *parent, + const char *name, + uint16_t *value) { + + const char *t; + unsigned u; + + if (!(t = udev_device_get_sysattr_value(parent, name))) { + fprintf(stderr, "%s lacks %s.\n", udev_device_get_syspath(parent), name); + return -1; + } + + if (!strncmp(t, "0x", 2)) + t += 2; + + if (sscanf(t, "%04x", &u) != 1 || u > 0xFFFFU) { + fprintf(stderr, "Failed to parse %s on %s.\n", name, udev_device_get_syspath(parent)); + return -1; + } + + *value = (uint16_t) u; + return 0; +} + +static int get_vid_pid( + struct udev_device *parent, + const char *vendor_attr, + const char *product_attr, + uint16_t *vid, + uint16_t *pid) { + + if (get_id_attr(parent, vendor_attr, vid) < 0) + return -1; + else if (*vid <= 0) { + fprintf(stderr, "Invalid vendor id.\n"); + return -1; + } + + if (get_id_attr(parent, product_attr, pid) < 0) + return -1; + + return 0; +} + +static void rstrip(char *n) { + size_t i; + + for (i = strlen(n); i > 0 && isspace(n[i-1]); i--) + n[i-1] = 0; +} + +#define HEXCHARS "0123456789abcdefABCDEF" +#define WHITESPACE " \t\n\r" +static int lookup_vid_pid(const char *database, + uint16_t vid, uint16_t pid, + char **vendor, char **product) +{ + + FILE *f; + int ret = -1; + int found_vendor = 0; + char *line = NULL; + + *vendor = *product = NULL; + + if (!(f = fopen(database, "rme"))) { + fprintf(stderr, "Failed to open database file '%s': %s\n", database, strerror(errno)); + return -1; + } + + for (;;) { + size_t n; + + if (getline(&line, &n, f) < 0) + break; + + rstrip(line); + + if (line[0] == '#' || line[0] == 0) + continue; + + if (strspn(line, HEXCHARS) == 4) { + unsigned u; + + if (found_vendor) + break; + + if (sscanf(line, "%04x", &u) == 1 && u == vid) { + char *t; + + t = line+4; + t += strspn(t, WHITESPACE); + + if (!(*vendor = strdup(t))) { + fprintf(stderr, "Out of memory.\n"); + goto finish; + } + + found_vendor = 1; + } + + continue; + } + + if (found_vendor && line[0] == '\t' && strspn(line+1, HEXCHARS) == 4) { + unsigned u; + + if (sscanf(line+1, "%04x", &u) == 1 && u == pid) { + char *t; + + t = line+5; + t += strspn(t, WHITESPACE); + + if (!(*product = strdup(t))) { + fprintf(stderr, "Out of memory.\n"); + goto finish; + } + + break; + } + } + } + + ret = 0; + +finish: + free(line); + fclose(f); + + if (ret < 0) { + free(*product); + free(*vendor); + + *product = *vendor = NULL; + } + + return ret; +} + +static struct udev_device *find_device(struct udev_device *dev, const char *subsys, const char *devtype) +{ + const char *str; + + str = udev_device_get_subsystem(dev); + if (str == NULL) + goto try_parent; + if (strcmp(str, subsys) != 0) + goto try_parent; + + if (devtype != NULL) { + str = udev_device_get_devtype(dev); + if (str == NULL) + goto try_parent; + if (strcmp(str, devtype) != 0) + goto try_parent; + } + return dev; +try_parent: + return udev_device_get_parent_with_subsystem_devtype(dev, subsys, devtype); +} + + +static int builtin_db(struct udev_device *dev, bool test, + const char *database, + const char *vendor_attr, const char *product_attr, + const char *subsys, const char *devtype) +{ + struct udev_device *parent; + uint16_t vid = 0, pid = 0; + char *vendor = NULL, *product = NULL; + + parent = find_device(dev, subsys, devtype); + if (!parent) { + fprintf(stderr, "Failed to find device.\n"); + goto finish; + } + + if (get_vid_pid(parent, vendor_attr, product_attr, &vid, &pid) < 0) + goto finish; + + if (lookup_vid_pid(database, vid, pid, &vendor, &product) < 0) + goto finish; + + if (vendor) + udev_builtin_add_property(dev, test, "ID_VENDOR_FROM_DATABASE", vendor); + if (product) + udev_builtin_add_property(dev, test, "ID_MODEL_FROM_DATABASE", product); + +finish: + free(vendor); + free(product); + return 0; +} + +static int builtin_usb_db(struct udev_device *dev, int argc, char *argv[], bool test) +{ + return builtin_db(dev, test, USB_DATABASE, "idVendor", "idProduct", "usb", "usb_device"); +} + +static int builtin_pci_db(struct udev_device *dev, int argc, char *argv[], bool test) +{ + return builtin_db(dev, test, PCI_DATABASE, "vendor", "device", "pci", NULL); +} + +const struct udev_builtin udev_builtin_usb_db = { + .name = "usb-db", + .cmd = builtin_usb_db, + .help = "USB vendor/product database", + .run_once = true, +}; + +const struct udev_builtin udev_builtin_pci_db = { + .name = "pci-db", + .cmd = builtin_pci_db, + .help = "PCI vendor/product database", + .run_once = true, +}; |