Commit 796b06c2 authored by Kay Sievers's avatar Kay Sievers
Browse files

udev: add hardware database support

parent 59bb9d9a
......@@ -80,6 +80,7 @@ systempresetdir=$(rootprefix)/lib/systemd/system-preset
udevlibexecdir=$(rootprefix)/lib/udev
udevhomedir = $(udevlibexecdir)
udevrulesdir = $(udevlibexecdir)/rules.d
udevhwdbdir = $(udevlibexecdir)/hwdb.d
# And these are the special ones for /
rootprefix=@rootprefix@
......@@ -1758,6 +1759,7 @@ man/systemd-udevd-kernel.socket.8: man/systemd-udevd.service.8
udev-confdirs:
-$(MKDIR_P) $(DESTDIR)$(sysconfdir)/udev/rules.d
-$(MKDIR_P) $(DESTDIR)$(sysconfdir)/udev/hwdb.d
INSTALL_DATA_HOOKS += udev-confdirs
......@@ -1777,6 +1779,10 @@ dist_udevrules_DATA += \
rules/80-drivers.rules \
rules/95-udev-late.rules
dist_udevhwdb_DATA = \
hwdb/20-pci-vendor-product.hwdb \
hwdb/20-usb-vendor-product.hwdb
udevconfdir = $(sysconfdir)/udev
dist_udevconf_DATA = \
src/udev/udev.conf
......@@ -1824,6 +1830,7 @@ noinst_LTLIBRARIES += \
libudev_core_la_SOURCES = \
src/udev/udev.h \
src/udev/udev-hwdb.h \
src/udev/udev-event.c \
src/udev/udev-watch.c \
src/udev/udev-node.c \
......@@ -1854,8 +1861,7 @@ libudev_core_la_LIBADD = \
libudev_core_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
-DFIRMWARE_PATH="$(FIRMWARE_PATH)" \
-DUSB_DATABASE=\"$(USB_DATABASE)\" -DPCI_DATABASE=\"$(PCI_DATABASE)\"
-DFIRMWARE_PATH="$(FIRMWARE_PATH)"
if HAVE_ACL
libudev_core_la_SOURCES += \
......@@ -1878,6 +1884,7 @@ udevadm_SOURCES = \
src/udev/udevadm-info.c \
src/udev/udevadm-control.c \
src/udev/udevadm-monitor.c \
src/udev/udevadm-hwdb.c \
src/udev/udevadm-settle.c \
src/udev/udevadm-trigger.c \
src/udev/udevadm-test.c \
......@@ -3951,6 +3958,7 @@ distclean-local: $(DISTCLEAN_LOCAL_HOOKS)
clean-local:
rm -rf $(abs_srcdir)/install-tree
rm -f $(abs_srcdir)/hwdb/usb.ids $(abs_srcdir)/hwdb/pci.ids
DISTCHECK_CONFIGURE_FLAGS = \
--with-sysvinit-path=$$dc_install_base/$(sysvinitdir) \
......@@ -3967,6 +3975,12 @@ DISTCHECK_CONFIGURE_FLAGS += \
--enable-gtk-doc
endif
hwdb-update:
( cd hwdb && \
wget -N http://www.linux-usb.org/usb.ids && \
wget -N http://pciids.sourceforge.net/v2.2/pci.ids && \
./ids-update.pl )
upload: all distcheck
cp -v systemd-$(VERSION).tar.xz /home/lennart/git.fedora/systemd/
scp systemd-$(VERSION).tar.xz fdo:/srv/www.freedesktop.org/www/software/systemd/
......
......@@ -448,6 +448,7 @@ Features:
* GC unreferenced jobs (such as .device jobs)
* write blog stories about:
- hwdb: what belongs into it, lsusb
- enabling dbus services
- status update
- how to make changes to sysctl and sysfs attributes
......
......@@ -567,33 +567,6 @@ if test "x$enable_coredump" != "xno"; then
fi
AM_CONDITIONAL(ENABLE_COREDUMP, [test "$have_coredump" = "yes"])
# ------------------------------------------------------------------------------
AC_ARG_WITH(usb-ids-path,
[AS_HELP_STRING([--with-usb-ids-path=DIR], [Path to usb.ids file])],
[USB_DATABASE=${withval}],
[if test -n "$usbids" ; then
USB_DATABASE="$usbids"
else
PKG_CHECK_MODULES(USBUTILS, usbutils >= 0.82)
AC_SUBST([USB_DATABASE], [$($PKG_CONFIG --variable=usbids usbutils)])
fi])
AC_MSG_CHECKING([for USB database location])
AC_MSG_RESULT([$USB_DATABASE])
AC_SUBST(USB_DATABASE)
AC_ARG_WITH(pci-ids-path,
[AS_HELP_STRING([--with-pci-ids-path=DIR], [Path to pci.ids file])],
[PCI_DATABASE=${withval}],
[if test -n "$pciids" ; then
PCI_DATABASE="$pciids"
else
PKG_CHECK_MODULES(LIBPCI, libpci >= 3)
AC_SUBST([PCI_DATABASE], [$($PKG_CONFIG --variable=idsdir libpci)/pci.ids])
fi])
AC_MSG_CHECKING([for PCI database location])
AC_MSG_RESULT([$PCI_DATABASE])
AC_SUBST(PCI_DATABASE)
# ------------------------------------------------------------------------------
AC_ARG_WITH(firmware-path,
AS_HELP_STRING([--with-firmware-path=DIR[[[:DIR[...]]]]],
......@@ -878,8 +851,6 @@ AC_MSG_RESULT([
localed: ${have_localed}
coredump: ${have_coredump}
firmware path: ${FIRMWARE_PATH}
usb.ids: ${USB_DATABASE}
pci.ids: ${PCI_DATABASE}
gudev: ${enable_gudev}
gintrospection: ${enable_introspection}
keymap: ${enable_keymap}
......
/pci.ids
/usb.ids
This diff is collapsed.
This diff is collapsed.
#!/usr/bin/perl
use strict;
use warnings;
my $vendor;
open(IN, "<", "usb.ids");
open(OUT, ">", "20-usb-vendor-product.hwdb");
print(OUT "# This file is part of systemd.\n" .
"#\n" .
"# Data imported and updated from: http://www.linux-usb.org/usb.ids\n");
while (my $line = <IN>) {
$line =~ s/\s+$//;
$line =~ m/^([0-9a-f]{4})\s*(.*)$/;
if (defined $1) {
$vendor = uc $1;
my $text = $2;
print(OUT "\n");
print(OUT "usb:v" . $vendor . "*\n");
print(OUT " ID_VENDOR_FROM_DATABASE=" . $text . "\n");
next;
}
$line =~ m/^\t([0-9a-f]{4})\s*(.*)$/;
if (defined $1) {
my $product = uc $1;
my $text = $2;
print(OUT "\n");
print(OUT "usb:v" . $vendor . "p" . $product . "*\n");
print(OUT " ID_PRODUCT_FROM_DATABASE=" . $text . "\n");
}
}
close(INP);
close(OUTP);
my $device;
open(IN, "<", "pci.ids");
open(OUT, ">", "20-pci-vendor-product.hwdb");
print(OUT "# This file is part of systemd.\n" .
"#\n" .
"# Data imported and updated from: http://pciids.sourceforge.net/v2.2/pci.ids\n");
while (my $line = <IN>) {
$line =~ s/\s+$//;
$line =~ m/^([0-9a-f]{4})\s*(.*)$/;
if (defined $1) {
$vendor = uc $1;
my $text = $2;
print(OUT "\n");
print(OUT "pci:v0000" . $vendor . "*\n");
print(OUT " ID_VENDOR_FROM_DATABASE=" . $text . "\n");
next;
}
$line =~ m/^\t([0-9a-f]{4})\s*(.*)$/;
if (defined $1) {
$device = uc $1;
my $text = $2;
print(OUT "\n");
print(OUT "pci:v0000" . $vendor . "d0000" . $device . "*\n");
print(OUT " ID_PRODUCT_FROM_DATABASE=" . $text . "\n");
next;
}
$line =~ m/^\t\t([0-9a-f]{4})\s*([0-9a-f]{4})\s*(.*)$/;
if (defined $1) {
my $sub_vendor = uc $1;
my $sub_device = uc $2;
my $text = $3;
print(OUT "\n");
print(OUT "pci:v0000" . $vendor . "d0000" . $device . "sv0000" . $sub_vendor . "sd0000" . $sub_device . "*\n");
print(OUT " ID_PRODUCT_FROM_DATABASE=" . $text . "\n");
}
}
close(INP);
close(OUTP);
......@@ -43,7 +43,7 @@ SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", GROUP="video"
# 'libusb' device nodes
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664"
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id"
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb"
# printer
KERNEL=="parport[0-9]*", GROUP="lp"
......
......@@ -4,11 +4,11 @@ ACTION=="remove", GOTO="net_end"
SUBSYSTEM!="net", GOTO="net_end"
SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id"
SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db"
SUBSYSTEMS=="usb", IMPORT{builtin}="hwdb"
SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}"
SUBSYSTEMS=="usb", GOTO="net_end"
SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db"
IMPORT{builtin}="hwdb"
SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
LABEL="net_end"
......@@ -3,12 +3,12 @@
ACTION=="remove", GOTO="tty_end"
SUBSYSTEM!="tty", GOTO="tty_end"
IMPORT{builtin}="hwdb"
SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id"
SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db"
SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}"
SUBSYSTEMS=="usb", GOTO="tty_end"
SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db"
SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
LABEL="tty_end"
......@@ -37,8 +37,8 @@ KERNEL!="card*", GOTO="sound_end"
ENV{SOUND_INITIALIZED}="1"
IMPORT{builtin}="hwdb"
SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db"
SUBSYSTEMS=="usb", GOTO="skip_pci"
SUBSYSTEMS=="firewire", ATTRS{vendor_name}=="?*", ATTRS{model_name}=="?*", \
......@@ -46,10 +46,7 @@ SUBSYSTEMS=="firewire", ATTRS{vendor_name}=="?*", ATTRS{model_name}=="?*", \
SUBSYSTEMS=="firewire", ATTRS{guid}=="?*", ENV{ID_ID}="firewire-$attr{guid}"
SUBSYSTEMS=="firewire", GOTO="skip_pci"
SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db"
SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
LABEL="skip_pci"
ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}-$attr{id}"
......
......@@ -1365,7 +1365,7 @@ int main(int argc, char *argv[]) {
for (j = optind; j < argc; j++) {
char *fragment;
fragment = resolve_fragment(argv[j], (const char**) conf_file_dirs);
fragment = resolve_fragment(argv[j], (const char **)conf_file_dirs);
if (!fragment) {
log_error("Failed to find a %s file: %m", argv[j]);
r = EXIT_FAILURE;
......@@ -1379,8 +1379,7 @@ int main(int argc, char *argv[]) {
} else {
char **files, **f;
r = conf_files_list_strv(&files, ".conf",
(const char **) conf_file_dirs);
r = conf_files_list_strv(&files, ".conf", (const char **)conf_file_dirs);
if (r < 0) {
log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
r = EXIT_FAILURE;
......
/*
* 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/>.
*/
/***
This file is part of systemd.
Copyright 2012 Kay Sievers <kay.sievers@vrfy.org>
Copyright 2008 Alan Jenkins <alan.christopher.jenkins@googlemail.com>
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdio.h>
#include <errno.h>
......@@ -24,224 +24,352 @@
#include <inttypes.h>
#include <ctype.h>
#include <stdlib.h>
#include <fnmatch.h>
#include <getopt.h>
#include <sys/mman.h>
#include "udev.h"
#include "udev-hwdb.h"
static int get_id_attr(
struct udev_device *parent,
const char *name,
uint16_t *value) {
struct linebuf {
char bytes[LINE_MAX];
size_t size;
size_t len;
};
const char *t;
unsigned u;
static void linebuf_init(struct linebuf *buf) {
buf->size = 0;
buf->len = 0;
}
if (!(t = udev_device_get_sysattr_value(parent, name))) {
fprintf(stderr, "%s lacks %s.\n", udev_device_get_syspath(parent), name);
return -1;
}
static const char *linebuf_get(struct linebuf *buf) {
if (buf->len + 1 >= sizeof(buf->bytes))
return NULL;
buf->bytes[buf->len] = '\0';
return buf->bytes;
}
static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
if (buf->len + len >= sizeof(buf->bytes))
return false;
memcpy(buf->bytes + buf->len, s, len);
buf->len += len;
return true;
}
if (startswith(t, "0x"))
t += 2;
static bool linebuf_add_char(struct linebuf *buf, char c)
{
if (buf->len + 1 >= sizeof(buf->bytes))
return false;
buf->bytes[buf->len++] = c;
return true;
}
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;
}
static void linebuf_rem(struct linebuf *buf, size_t count) {
assert(buf->len >= count);
buf->len -= count;
}
*value = (uint16_t) u;
return 0;
static void linebuf_rem_char(struct linebuf *buf) {
linebuf_rem(buf, 1);
}
static int get_vid_pid(
struct udev_device *parent,
const char *vendor_attr,
const char *product_attr,
uint16_t *vid,
uint16_t *pid) {
struct trie_f {
struct udev_device *dev;
bool test;
FILE *f;
uint64_t file_time_usec;
union {
struct trie_header_f *head;
const char *map;
};
size_t map_size;
};
if (get_id_attr(parent, vendor_attr, vid) < 0)
return -1;
else if (*vid <= 0) {
fprintf(stderr, "Invalid vendor id.\n");
return -1;
}
static const struct trie_child_entry_f *trie_node_children(struct trie_f *trie, const struct trie_node_f *node) {
return (const struct trie_child_entry_f *)((const char *)node + le64toh(trie->head->node_size));
}
if (get_id_attr(parent, product_attr, pid) < 0)
return -1;
static const struct trie_value_entry_f *trie_node_values(struct trie_f *trie, const struct trie_node_f *node) {
const char *base = (const char *)node;
return 0;
base += le64toh(trie->head->node_size);
base += node->children_count * le64toh(trie->head->child_entry_size);
return (const struct trie_value_entry_f *)base;
}
static void rstrip(char *n) {
size_t i;
static const struct trie_node_f *trie_node_from_off(struct trie_f *trie, le64_t off) {
return (const struct trie_node_f *)(trie->map + le64toh(off));
}
for (i = strlen(n); i > 0 && isspace(n[i-1]); i--)
n[i-1] = 0;
static const char *trie_string(struct trie_f *trie, le64_t off) {
return trie->map + le64toh(off);
}
#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)
{
static int trie_children_cmp_f(const void *v1, const void *v2) {
const struct trie_child_entry_f *n1 = v1;
const struct trie_child_entry_f *n2 = v2;
FILE *f;
int ret = -1;
int found_vendor = 0;
char *line = NULL;
return n1->c - n2->c;
}
*vendor = *product = NULL;
static const struct trie_node_f *node_lookup_f(struct trie_f *trie, const struct trie_node_f *node, uint8_t c) {
struct trie_child_entry_f *child;
struct trie_child_entry_f search;
if (!(f = fopen(database, "rme"))) {
fprintf(stderr, "Failed to open database file '%s': %s\n", database, strerror(errno));
return -1;
}
search.c = c;
child = bsearch(&search, trie_node_children(trie, node), node->children_count,
le64toh(trie->head->child_entry_size), trie_children_cmp_f);
if (child)
return trie_node_from_off(trie, child->child_off);
return NULL;
}
for (;;) {
size_t n;
static void trie_fnmatch_f(struct trie_f *trie, const struct trie_node_f *node, size_t p,
struct linebuf *buf, const char *search,
void (*cb)(struct trie_f *trie, const char *key, const char *value)) {
size_t len;
size_t i;
const char *prefix;
if (getline(&line, &n, f) < 0)
break;
prefix = trie_string(trie, node->prefix_off);
len = strlen(prefix + p);
linebuf_add(buf, prefix + p, len);
rstrip(line);
for (i = 0; i < node->children_count; i++) {
const struct trie_child_entry_f *child = &trie_node_children(trie, node)[i];
if (line[0] == '#' || line[0] == 0)
continue;
linebuf_add_char(buf, child->c);
trie_fnmatch_f(trie, trie_node_from_off(trie, child->child_off), 0, buf, search, cb);
linebuf_rem_char(buf);
}
if (strspn(line, HEXCHARS) == 4) {
unsigned u;
if (node->values_count && fnmatch(linebuf_get(buf), search, 0) == 0)
for (i = 0; i < node->values_count; i++)
cb(trie, trie_string(trie, trie_node_values(trie, node)[i].key_off),
trie_string(trie, trie_node_values(trie, node)[i].value_off));
if (found_vendor)
break;
linebuf_rem(buf, len);
}
if (sscanf(line, "%04x", &u) == 1 && u == vid) {
char *t;
static void trie_search_f(struct trie_f *trie, const char *search,
void (*cb)(struct trie_f *trie, const char *key, const char *value)) {
struct linebuf buf;
const struct trie_node_f *node;
size_t i = 0;
t = line+4;
t += strspn(t, WHITESPACE);
linebuf_init(&buf);
if (!(*vendor = strdup(t))) {
fprintf(stderr, "Out of memory.\n");
goto finish;
}
node = trie_node_from_off(trie, trie->head->nodes_root_off);
while (node) {
const struct trie_node_f *child;
size_t p = 0;
found_vendor = 1;
}
if (node->prefix_off) {
uint8_t c;
continue;
for (; (c = trie_string(trie, node->prefix_off)[p]); p++) {
if (c == '*' || c == '?' || c == '[') {
trie_fnmatch_f(trie, node, p, &buf, search + i + p, cb);
return;
}
if (c != search[i + p])
return;
}
i += p;
}
if (found_vendor && line[0] == '\t' && strspn(line+1, HEXCHARS) == 4) {
unsigned u;
child = node_lookup_f(trie, node, '*');
if (child) {
linebuf_add_char(&buf, '*');
trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
linebuf_rem_char(&buf);
}
if (sscanf(line+1, "%04x", &u) == 1 && u == pid) {
char *t;
child = node_lookup_f(trie, node, '?');
if (child) {
linebuf_add_char(&buf, '?');
trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
linebuf_rem_char(&buf);
}
t = line+5;
t += strspn(t, WHITESPACE);
child = node_lookup_f(trie, node, '[');
if (child) {
linebuf_add_char(&buf, '[');
trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
linebuf_rem_char(&buf);
}
if (!(*product = strdup(t))) {
fprintf(stderr, "Out of memory.\n");
goto finish;
}
if (search[i] == '\0') {
size_t n;