diff options
-rw-r--r-- | configure.ac | 20 | ||||
-rw-r--r-- | extras/Makefile.am | 1 | ||||
-rw-r--r-- | extras/usb-db/.gitignore | 2 | ||||
-rw-r--r-- | extras/usb-db/Makefile.am | 12 | ||||
-rw-r--r-- | extras/usb-db/usb-db.c | 266 |
5 files changed, 301 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac index 2fae2ccd30..947ab80fd0 100644 --- a/configure.ac +++ b/configure.ac @@ -72,6 +72,22 @@ if test "x$enable_extras" = xyes; then PKG_CHECK_MODULES(LIBUSB, libusb >= 0.1.12) AC_SUBST(LIBUSB_CFLAGS) AC_SUBST(LIBUSB_LIBS) + + PKG_CHECK_MODULES(USBUTILS, usbutils >= 0.82) + AC_SUBST([USB_DATABASE], [$($PKG_CONFIG --variable=usbids usbutils)]) + + AC_CHECK_FILES([/usr/share/pci.ids], [pciids=/usr/share/pci.ids]) + AC_CHECK_FILES([/usr/share/hwdata/pci.ids], [pciids=/usr/share/hwdata/pci.ids]) + AC_CHECK_FILES([/usr/share/misc/pci.ids], [pciids=/usr/share/misc/pci.ids]) + AC_ARG_WITH(pci-ids-path, + AS_HELP_STRING([--pci-ids-path=DIR], [Path to pci.ids file]), + [PCI_DATABASE=${withval}], + [if test -n "$pciids" ; then + PCI_DATABASE="$pciids" + else + AC_MSG_ERROR([pci.ids not found, try --with-pci-ids-path=]) + fi]) + AC_SUBST(PCI_DATABASE) fi AM_CONDITIONAL([ENABLE_EXTRAS], [test "x$enable_extras" = xyes]) @@ -113,6 +129,7 @@ AC_CONFIG_FILES([ extras/v4l_id/Makefile extras/hid2hci/Makefile extras/udev-acl/Makefile + extras/usb-db/Makefile extras/gudev/Makefile extras/gudev/gudev-1.0.pc extras/gudev/docs/Makefile @@ -144,5 +161,8 @@ AC_MSG_RESULT([ extras: ${enable_extras} gintrospection: ${enable_introspection} + usb.ids: ${USB_DATABASE} + pci.ids: ${PCI_DATABASE} + xsltproc: ${XSLTPROC} ]) diff --git a/extras/Makefile.am b/extras/Makefile.am index e4136ad531..07bf48edf7 100644 --- a/extras/Makefile.am +++ b/extras/Makefile.am @@ -17,6 +17,7 @@ SUBDIRS = \ if ENABLE_EXTRAS SUBDIRS += \ udev-acl \ + usb-db \ hid2hci \ gudev endif diff --git a/extras/usb-db/.gitignore b/extras/usb-db/.gitignore new file mode 100644 index 0000000000..861a8382a6 --- /dev/null +++ b/extras/usb-db/.gitignore @@ -0,0 +1,2 @@ +usb-db +pci-db diff --git a/extras/usb-db/Makefile.am b/extras/usb-db/Makefile.am new file mode 100644 index 0000000000..58fd2c9370 --- /dev/null +++ b/extras/usb-db/Makefile.am @@ -0,0 +1,12 @@ +include $(top_srcdir)/Makefile.am.inc + +udevhomedir = $(udev_prefix)/lib/udev +udevhome_PROGRAMS = usb-db pci-db + +usb_db_SOURCES = usb-db.c +usb_db_CPPFLAGS = $(AM_CPPFLAGS) -DUSB_DATABASE=\"$(USB_DATABASE)\" -DBUILD_FOR_USB +usb_db_LDADD = $(top_builddir)/libudev/libudev.la + +pci_db_SOURCES = $(usb_db_SOURCES) +pci_db_CPPFLAGS = $(AM_CPPFLAGS) -DPCI_DATABASE=\"$(PCI_DATABASE)\" -DBUILD_FOR_PCI +pci_db_LDADD = $(top_builddir)/libudev/libudev.la diff --git a/extras/usb-db/usb-db.c b/extras/usb-db/usb-db.c new file mode 100644 index 0000000000..db1f843c1a --- /dev/null +++ b/extras/usb-db/usb-db.c @@ -0,0 +1,266 @@ +/*-*- linux-c -*-*/ + +/* + * Copyright (C) 2009 Lennart Poettering <lennart@poettering.net> + * + * 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: + */ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <inttypes.h> +#include <ctype.h> +#include <stdlib.h> + +#include <libudev.h> + +#if defined(BUILD_FOR_USB) +# define DATABASE USB_DATABASE +# define SUBSYSTEM "usb" +# define DEVTYPE "usb_device" +# define VENDOR_ATTR "idVendor" +# define PRODUCT_ATTR "idProduct" +#elif defined(BUILD_FOR_PCI) +# define DATABASE PCI_DATABASE +# define SUBSYSTEM "pci" +# define DEVTYPE NULL +# define VENDOR_ATTR "vendor" +# define PRODUCT_ATTR "device" +#else +# error "Are you havin' a laugh?" +#endif + +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, + 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( + 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, "r"))) { + fprintf(stderr, "Failed to open database file "DATABASE": %s\n", strerror(errno)); + return -1; + } + + for (;;) { + size_t n; + + if (line) { + free(line); + line = NULL; + } + + 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, SUBSYSTEM, DEVTYPE); +} + +int main(int argc, char*argv[]) { + + struct udev *udev = NULL; + int ret = 1; + char *sp; + struct udev_device *dev = NULL, *parent = NULL; + uint16_t vid = 0, pid = 0; + char *vendor = NULL, *product = NULL; + + if (argc < 2) { + fprintf(stderr, "Need to pass sysfs path.\n"); + goto finish; + } + + if (!(udev = udev_new())) + goto finish; + + if (asprintf(&sp, "%s/%s", udev_get_sys_path(udev), argv[1]) < 0) { + fprintf(stderr, "Failed to allocate sysfs path.\n"); + goto finish; + } + + dev = udev_device_new_from_syspath(udev, sp); + free(sp); + + if (!dev) { + fprintf(stderr, "Failed to access %s.\n", argv[1]); + goto finish; + } + + parent = find_device(dev, SUBSYSTEM, DEVTYPE); + if (!parent) { + fprintf(stderr, "Failed to find device.\n"); + goto finish; + } + + if (get_vid_pid(parent, &vid, &pid) < 0) + goto finish; + + if (lookup_vid_pid(vid, pid, &vendor, &product) < 0) + goto finish; + + if (vendor) + printf("ID_VENDOR_FROM_DATABASE=%s\n", vendor); + + if (product) + printf("ID_MODEL_FROM_DATABASE=%s\n", product); + + ret = 0; + +finish: + udev_device_unref(dev); + udev_unref(udev); + free(vendor); + free(product); + + return ret; +} |