diff options
author | Martin Mares <mj@ucw.cz> | 2008-02-20 12:18:19 +0100 |
---|---|---|
committer | Martin Mares <mj@ucw.cz> | 2008-02-20 12:18:19 +0100 |
commit | a2bf30a4fd407c55e8172e2f6fd158725ccb90b6 (patch) | |
tree | 971b143dc78a2b88ac43e01e2914ba23b95fcd57 | |
parent | daf1afbf693d83566863e6dc5447e0bebdd1c679 (diff) | |
parent | 4d59f9e543c291e1454c638e32cbe94af66b11b8 (diff) | |
download | pciutils-network.tar.gz |
Merge with git+ssh://git.ucw.cz/home/mj/GIT/pciutils.git#networknetwork
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | ChangeLog | 74 | ||||
-rw-r--r-- | Makefile | 80 | ||||
-rw-r--r-- | README | 44 | ||||
-rw-r--r-- | common.c | 61 | ||||
-rw-r--r-- | example.c (renamed from lib/example.c) | 13 | ||||
-rw-r--r-- | lib/Makefile | 48 | ||||
-rw-r--r-- | lib/access.c | 177 | ||||
-rw-r--r-- | lib/aix-device.c | 11 | ||||
-rwxr-xr-x | lib/configure | 108 | ||||
-rw-r--r-- | lib/dump.c | 16 | ||||
-rw-r--r-- | lib/fbsd-device.c | 9 | ||||
-rw-r--r-- | lib/i386-ports.c | 6 | ||||
-rw-r--r-- | lib/init.c | 224 | ||||
-rw-r--r-- | lib/internal.h | 12 | ||||
-rw-r--r-- | lib/libpci.pc.in | 2 | ||||
-rw-r--r-- | lib/libpci.ver | 12 | ||||
-rw-r--r-- | lib/names-cache.c | 203 | ||||
-rw-r--r-- | lib/names-hash.c | 126 | ||||
-rw-r--r-- | lib/names-net.c | 239 | ||||
-rw-r--r-- | lib/names-parse.c | 251 | ||||
-rw-r--r-- | lib/names.c | 640 | ||||
-rw-r--r-- | lib/names.h | 69 | ||||
-rw-r--r-- | lib/nbsd-libpci.c | 9 | ||||
-rw-r--r-- | lib/obsd-device.c | 9 | ||||
-rw-r--r-- | lib/params.c | 88 | ||||
-rw-r--r-- | lib/pci.h | 107 | ||||
-rw-r--r-- | lib/proc.c | 11 | ||||
-rw-r--r-- | lib/sysfs.c | 9 | ||||
-rw-r--r-- | lib/types.h | 10 | ||||
-rw-r--r-- | lspci.c | 97 | ||||
-rw-r--r-- | lspci.man | 180 | ||||
-rwxr-xr-x | maint/gen-zone | 4 | ||||
-rwxr-xr-x | maint/release | 3 | ||||
-rw-r--r-- | pcilib.man | 109 | ||||
-rw-r--r-- | pciutils.h | 24 | ||||
-rw-r--r-- | setpci.c | 19 | ||||
-rw-r--r-- | setpci.man | 54 |
38 files changed, 2008 insertions, 1154 deletions
@@ -1,7 +1,9 @@ *.a *.o -*.8 +*.so +*.[0-9] lspci setpci +example update-pciids pci.ids.gz @@ -1,5 +1,78 @@ +2008-02-20 Martin Mares <mj@ucw.cz> + + * Released as 2.99.1-alpha2. + + * Changed the default domain for the DNS resolver. + +2008-02-18 Martin Mares <mj@ucw.cz> + + * Released as 2.99.1-alpha1. + + * The makefile system has been reworked. All configuration settings + are now passed to the configure script in environment variables, + allowing for easy tweaking in the top-level Makefile. All control + knobs are now described in the README. + + * The libpci can be built as a shared library with properly restricted + symbol exports. Use `make SHARED=yes' to enable that or `make SHARED=local' + for a local testing build (with hardwired paths to the library, so that + it does not need installation). + + * The example program has been moved from lib/example.c to the top-level + directory, because it should be built exactly as the other utilities + are. It has been also improved slightly to educate better. + + * The i386-ports method is enabled on Linux/x86_64 as well. + +2008-02-13 Martin Mares <mj@ucw.cz> + + * Released as 2.2.10-net2. + + * Support for resolving of PCI ID's using DNS together with a local + cache of resolved entries has been added. See the `-q' and `-Q' options + of lspci. + + * The library now has a generic system of settable parameters, which + also include settings of the DNS resolver and cache. An `-O' option + has been added to lspci and setpci to allow setting of these options. + + * Configuration of the access methods are now specified by the new + parameter system, replacing the pci_access->method_params array. + + * Access methods now also have sensible names and help texts and it + is possible to look up method ID by a name. + + * An `-A' switch has been added to both lspci and setpci, allowing to + select an arbitrary access method. The `-P' switch (configure proc + backend) has been removed as it is no longer needed and I do not know + any its user. + + * Several source files have been split for better maintainability + (most notably the resolving of ID's). + + * Man pages and help texts have been updated. A new man page `pcilib(7)' + has been added and description of library options has been moved there. + + * When an unknown device ID is encountered, we print `Device <id>' + instead of `Unknown device <id>'. It uses less space and it also + should reduce the number of inexperienced users complaining that + the device is not supported by the OS. To lookup up OS drivers, + use the `-k' option. + + * PCI_LIB_VERSION has been bumped to 0x020299. + + * Makefile: stripping of the binaries during installation can be + overridden by the STRIP variable. + + * lib/types.h: We use the integer types from <stdint.h> if the + compiler claims C99 support. + 2008-02-11 Martin Mares <mj@ucw.cz> + * Released as 2.2.10. + + * lspci.c, setpci.c: Cleaned up the list of options. + * lib/names.c: Fix displaying of errors reported by zlib. Previously, the buffer containing the error message had been deallocated by gzclose() before the message was printed. @@ -148,7 +221,6 @@ * lspci.c (show_express): Added PCI/PCI-X to PCI-Express Bridge type. Patch by Mark Glines. ->>>>>>> main 2007-02-09 Martin Mares <mj@ucw.cz> * pci.ids: Updated to the current snapshot of the database. @@ -4,9 +4,26 @@ OPT=-O2 CFLAGS=$(OPT) -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -VERSION=2.2.9-net1 -DATE=2008-02-11 +VERSION=2.99.1-alpha2 +DATE=2008-02-18 +# Host OS and release (override if you are cross-compiling) +HOST= +RELEASE= + +# Support for compressed pci.ids (yes/no, default: detect) +ZLIB= + +# Support for resolving ID's by DNS (yes/no, default: detect) +DNS= + +# Build libpci as a shared library (yes/no; or local for testing; requires GCC) +SHARED=no + +# ABI version suffix in the name of the shared library +ABI_VERSION=.2.99 + +# Installation directories PREFIX=/usr/local SBINDIR=$(PREFIX)/sbin SHAREDIR=$(PREFIX)/share @@ -15,34 +32,36 @@ MANDIR:=$(shell if [ -d $(PREFIX)/share/man ] ; then echo $(PREFIX)/share/man ; INCDIR=$(PREFIX)/include LIBDIR=$(PREFIX)/lib PKGCFDIR=$(LIBDIR)/pkgconfig + +# Commands INSTALL=install DIRINSTALL=install -d +STRIP=-s AR=ar RANLIB=ranlib -PCILIB=lib/libpci.a -PCILIBPC=lib/libpci.pc -PCIINC=lib/config.h lib/header.h lib/pci.h lib/types.h lib/sysdep.h -PCIINC_INS=lib/config.h lib/header.h lib/pci.h lib/types.h + +# Base name of the library (overriden on NetBSD, which has its own libpci) +LIBNAME=libpci -include lib/config.mk -HOST= -RELEASE= +PCIINC=lib/config.h lib/header.h lib/pci.h lib/types.h lib/sysdep.h +PCIINC_INS=lib/config.h lib/header.h lib/pci.h lib/types.h export -all: $(PCILIB) lspci setpci lspci.8 setpci.8 update-pciids update-pciids.8 $(PCI_IDS) +all: lib/$(PCILIB) lspci setpci example lspci.8 setpci.8 pcilib.7 update-pciids update-pciids.8 $(PCI_IDS) -$(PCILIB): $(PCIINC) force +lib/$(PCILIB): $(PCIINC) force $(MAKE) -C lib all force: lib/config.h lib/config.mk: - cd lib && ./configure "$(IDSDIR)" "$(VERSION)" "$(HOST)" "$(RELEASE)" "$(ZLIB)" + cd lib && ./configure -lspci: lspci.o common.o $(PCILIB) /usr/lib/libresolv.a -setpci: setpci.o common.o $(PCILIB) /usr/lib/libresolv.a +lspci: lspci.o common.o lib/$(PCILIB) +setpci: setpci.o common.o lib/$(PCILIB) lspci.o: lspci.c pciutils.h $(PCIINC) setpci.o: setpci.c pciutils.h $(PCIINC) @@ -52,39 +71,56 @@ update-pciids: update-pciids.sh sed <$< >$@ "s@^DEST=.*@DEST=$(IDSDIR)/$(PCI_IDS)@;s@^PCI_COMPRESSED_IDS=.*@PCI_COMPRESSED_IDS=$(PCI_COMPRESSED_IDS)@" chmod +x $@ +# The example of use of libpci +example: example.o lib/$(PCILIB) +example.o: example.c $(PCIINC) + %: %.o $(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@ -%.8: %.man +%.8 %.7: %.man M=`echo $(DATE) | sed 's/-01-/-January-/;s/-02-/-February-/;s/-03-/-March-/;s/-04-/-April-/;s/-05-/-May-/;s/-06-/-June-/;s/-07-/-July-/;s/-08-/-August-/;s/-09-/-September-/;s/-10-/-October-/;s/-11-/-November-/;s/-12-/-December-/;s/\(.*\)-\(.*\)-\(.*\)/\3 \2 \1/'` ; sed <$< >$@ "s/@TODAY@/$$M/;s/@VERSION@/pciutils-$(VERSION)/;s#@IDSDIR@#$(IDSDIR)#" clean: rm -f `find . -name "*~" -o -name "*.[oa]" -o -name "\#*\#" -o -name TAGS -o -name core -o -name "*.orig"` - rm -f update-pciids lspci setpci lib/config.* lib/example *.8 pci.ids.* lib/*.pc + rm -f update-pciids lspci setpci example lib/config.* *.[78] pci.ids.* lib/*.pc lib/*.so lib/*.so.* rm -rf maint/dist distclean: clean install: all # -c is ignored on Linux, but required on FreeBSD - $(DIRINSTALL) -m 755 $(DESTDIR)$(SBINDIR) $(DESTDIR)$(IDSDIR) $(DESTDIR)$(MANDIR)/man8 - $(INSTALL) -c -m 755 -s lspci setpci $(DESTDIR)$(SBINDIR) + $(DIRINSTALL) -m 755 $(DESTDIR)$(SBINDIR) $(DESTDIR)$(IDSDIR) $(DESTDIR)$(MANDIR)/man8 $(DESTDIR)$(MANDIR)/man7 + $(INSTALL) -c -m 755 $(STRIP) lspci setpci $(DESTDIR)$(SBINDIR) $(INSTALL) -c -m 755 update-pciids $(DESTDIR)$(SBINDIR) $(INSTALL) -c -m 644 $(PCI_IDS) $(DESTDIR)$(IDSDIR) $(INSTALL) -c -m 644 lspci.8 setpci.8 update-pciids.8 $(DESTDIR)$(MANDIR)/man8 - -install-lib: $(PCIINC_INS) $(PCILIB) $(PCILIBPC) + $(INSTALL) -c -m 644 pcilib.7 $(DESTDIR)$(MANDIR)/man7 +ifeq ($(SHARED),yes) + $(DIRINSTALL) -m 755 $(DESTDIR)$(LIBDIR) + $(INSTALL) -c -m 644 lib/$(PCILIB) $(DESTDIR)$(LIBDIR) + ln -sf $(PCILIB) $(DESTDIR)$(LIBDIR)/$(LIBNAME).so$(ABI_VERSION) +endif + +install-lib: $(PCIINC_INS) lib/$(PCILIB) lib/$(PCILIBPC) $(DIRINSTALL) -m 755 $(DESTDIR)$(INCDIR)/pci $(DESTDIR)$(LIBDIR) $(DESTDIR)$(PKGCFDIR) $(INSTALL) -c -m 644 $(PCIINC_INS) $(DESTDIR)$(INCDIR)/pci - $(INSTALL) -c -m 644 $(PCILIB) $(DESTDIR)$(LIBDIR) - $(INSTALL) -c -m 644 $(PCILIBPC) $(DESTDIR)$(PKGCFDIR) + $(INSTALL) -c -m 644 lib/$(PCILIB) $(DESTDIR)$(LIBDIR) + $(INSTALL) -c -m 644 lib/$(PCILIBPC) $(DESTDIR)$(PKGCFDIR) +ifeq ($(SHARED),yes) + ln -sf $(LIBNAME).so$(ABI_VERSION) $(DESTDIR)$(LIBDIR)/$(LIBNAME).so +endif uninstall: all rm -f $(DESTDIR)$(SBINDIR)/lspci $(DESTDIR)$(SBINDIR)/setpci $(DESTDIR)$(SBINDIR)/update-pciids rm -f $(DESTDIR)$(IDSDIR)/$(PCI_IDS) rm -f $(DESTDIR)$(MANDIR)/man8/lspci.8 $(DESTDIR)$(MANDIR)/man8/setpci.8 $(DESTDIR)$(MANDIR)/man8/update-pciids.8 + rm -f $(DESTDIR)$(MANDIR)/man7/pcilib.7 +ifeq ($(SHARED),yes) + rm -f $(DESTDIR)$(LIBDIR)/$(PCILIB) $(DESTDIR)$(LIBDIR)/$(LIBNAME).so$(ABI_VERSION) +endif pci.ids.gz: pci.ids gzip -9 <$< >$@ -.PHONY: all clean distclean install uninstall force +.PHONY: all clean distclean install install-lib uninstall force @@ -1,6 +1,6 @@ This package contains the PCI Utilities, version @VERSION@. -Copyright (c) 1997--2007 Martin Mares <mj@ucw.cz> +Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> All files in this package can be freely distributed and used according to the terms of the GNU General Public License, either version 2 or @@ -43,6 +43,7 @@ The utilities include: (See manual pages for more details) 2. Compiling and (un)installing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Just run "make" to compile the package and then "make install" to install it. +Please note that GNU make is needed on most platforms. If you want to change the default installation location, please override the PREFIX variable specified in the Makefile -- e.g., you can use @@ -52,12 +53,31 @@ will allow you to install to a different directory from the one you intend to eventually run it from. This is useful for people who are packaging pciutils to install on other computers. -The configure script will automatically enable support for a compressed -pci.ids if you have zlib installed. You can override its guess by using -"make ZLIB=no" or "make ZLIB=yes". If compressed support is enabled, -pciutils will use pci.ids.gz in preference to pci.ids, even if the -pci.ids file is newer. If the pci.ids.gz file is missing, it will use -pci.ids instead. +There are several options which can be set in the Makefile or overridden +when running make: + + ZLIB=yes/no Enable support for compressed pci.ids (requires zlib). + If it is enabled, pciutils will use pci.ids.gz in preference to + pci.ids, even if the pci.ids file is newer. If the pci.ids.gz + file is missing, it will use pci.ids instead. If you do not + specify this option, the configure script will try to guess + automatically based on the presence of zlib. + + DNS=yes/no Enable support for querying the central database of PCI ID's + using DNS. Requires libresolv (which is available on most + systems as a part of the standard libraries) and tries to + autodetect its presence if the option is not specified. + + SHARED=yes/ Build libpci as a shared library. Requires GCC 4.0 or newer. + no/local The ABI of the shared library is intended to remain backward + compatible for a long time (we use symbol versioning to achieve + that, like GNU libc does). The value `local' includes the + right directory name in the binaries, so the utilities can be + run without installation. This is not recommended for any + production builds. + +"make install-lib" installs the library together with its header files +for use by other programs. When you are bored of dumping PCI registers, just use "make uninstall". @@ -71,6 +91,9 @@ If you are missing names for any of your devices or you just want to stay on the bleeding edge, download the most recent pci.ids file from http://pciids.sf.net/ (e.g., by running the update-ids utility). +Alternatively, you can use `lspci -q' to query the central database +for new entries via network. + If your devices still appear as unknown, please send us their ID's and names, the detailed instructions for submissions are listed on the sf.net web page. @@ -91,9 +114,10 @@ There is also a public GIT tree at: 5. Using the library ~~~~~~~~~~~~~~~~~~~~ -There is still no documentation for the library, if you want to use it -in your programs, please follow the comments in lib/pci.h and in the -example program lib/example.c. +So far, there is only a little documentation for the library except for the +general introduction in the pcilib(7) man page. If you want to use the +library in your programs, please follow the comments in lib/pci.h and in +the example program example.c. 6. Feedback @@ -1,7 +1,7 @@ /* * The PCI Utilities -- Common Functions * - * Copyright (c) 1997--2006 Martin Mares <mj@ucw.cz> + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -44,17 +44,56 @@ xrealloc(void *ptr, unsigned int howmuch) return p; } +static void +set_pci_method(struct pci_access *pacc, char *arg) +{ + char *name; + int i; + + if (!strcmp(arg, "help")) + { + printf("Known PCI access methods:\n\n"); + for (i=0; name = pci_get_method_name(i); i++) + if (name[0]) + printf("%s\n", name); + exit(0); + } + else + { + i = pci_lookup_method(arg); + if (i < 0) + die("No such PCI access method: %s (see `-A help' for a list)", arg); + pacc->method = i; + } +} + +static void +set_pci_option(struct pci_access *pacc, char *arg) +{ + if (!strcmp(arg, "help")) + { + struct pci_param *p; + printf("Known PCI access parameters:\n\n"); + for (p=NULL; p=pci_walk_params(pacc, p);) + printf("%-20s %s (%s)\n", p->param, p->help, p->value); + exit(0); + } + else + { + char *sep = strchr(arg, '='); + if (!sep) + die("Invalid PCI access parameter syntax: %s", arg); + *sep++ = 0; + if (pci_set_param(pacc, arg, sep) < 0) + die("Unrecognized PCI access parameter: %s (see `-O help' for a list)", arg); + } +} + int parse_generic_option(int i, struct pci_access *pacc, char *optarg) { switch (i) { -#ifdef PCI_HAVE_PM_LINUX_PROC - case 'P': - pacc->method_params[PCI_ACCESS_PROC_BUS_PCI] = optarg; - pacc->method = PCI_ACCESS_PROC_BUS_PCI; - break; -#endif #ifdef PCI_HAVE_PM_INTEL_CONF case 'H': if (!strcmp(optarg, "1")) @@ -67,13 +106,19 @@ parse_generic_option(int i, struct pci_access *pacc, char *optarg) #endif #ifdef PCI_HAVE_PM_DUMP case 'F': - pacc->method_params[PCI_ACCESS_DUMP] = optarg; + pci_set_param(pacc, "dump.name", optarg); pacc->method = PCI_ACCESS_DUMP; break; #endif + case 'A': + set_pci_method(pacc, optarg); + break; case 'G': pacc->debugging++; break; + case 'O': + set_pci_option(pacc, optarg); + break; default: return 0; } diff --git a/lib/example.c b/example.c index e7b5b66..9b24951 100644 --- a/lib/example.c +++ b/example.c @@ -7,13 +7,14 @@ #include <stdio.h> -#include "pci.h" +#include "lib/pci.h" int main(void) { struct pci_access *pacc; struct pci_dev *dev; unsigned int c; + char namebuf[1024], *name; pacc = pci_alloc(); /* Get the pci_access structure */ /* Set all options you want -- here we stick with the defaults */ @@ -23,9 +24,13 @@ int main(void) { pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); /* Fill in header info we need */ c = pci_read_byte(dev, PCI_INTERRUPT_PIN); /* Read config register directly */ - printf("%02x:%02x.%d vendor=%04x device=%04x class=%04x irq=%d (pin %d) base0=%lx\n", - dev->bus, dev->dev, dev->func, dev->vendor_id, dev->device_id, - dev->device_class, dev->irq, c, dev->base_addr[0]); + printf("%04x:%02x:%02x.%d vendor=%04x device=%04x class=%04x irq=%d (pin %d) base0=%lx", + dev->domain, dev->bus, dev->dev, dev->func, dev->vendor_id, dev->device_id, + dev->device_class, dev->irq, c, (long) dev->base_addr[0]); + + /* Look up and print the full name of the device */ + name = pci_lookup_name(pacc, namebuf, sizeof(namebuf), PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id); + printf(" (%s)\n", name); } pci_cleanup(pacc); /* Close everything */ return 0; diff --git a/lib/Makefile b/lib/Makefile index 2afd886..acbd420 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,32 +1,29 @@ # Makefile for The PCI Library -# (c) 1999--2007 Martin Mares <mj@ucw.cz> +# (c) 1999--2008 Martin Mares <mj@ucw.cz> -include config.mk +# Expects to be invoked from the top-level Makefile and uses lots of its variables. -OBJS=access.o generic.o dump.o names.o filter.o +OBJS=init access generic dump names filter names-hash names-parse names-net names-cache params INCL=internal.h pci.h config.h header.h sysdep.h types.h -PCILIB=libpci.a -PCILIBPC=libpci.pc - ifdef PCI_HAVE_PM_LINUX_SYSFS -OBJS += sysfs.o +OBJS += sysfs endif ifdef PCI_HAVE_PM_LINUX_PROC -OBJS += proc.o +OBJS += proc endif ifdef PCI_HAVE_PM_INTEL_CONF -OBJS += i386-ports.o +OBJS += i386-ports endif ifdef PCI_HAVE_PM_DUMP -OBJS += dump.o +OBJS += dump endif ifdef PCI_HAVE_PM_FBSD_DEVICE -OBJS += fbsd-device.o +OBJS += fbsd-device CFLAGS += -I/usr/src/sys ifdef FREEBSD_SYS CFLAGS += -I${FREEBSD_SYS} @@ -34,34 +31,41 @@ endif endif ifdef PCI_HAVE_PM_OBSD_DEVICE -OBJS += obsd-device.o +OBJS += obsd-device endif ifdef PCI_HAVE_PM_AIX_DEVICE -OBJS += aix-device.o +OBJS += aix-device endif ifdef PCI_HAVE_PM_NBSD_LIBPCI -OBJS += nbsd-libpci.o -PCILIB=libpciutils.a +OBJS += nbsd-libpci endif all: $(PCILIB) $(PCILIBPC) -$(PCILIB): $(OBJS) +ifeq ($(SHARED),no) +$(PCILIB): $(addsuffix .o,$(OBJS)) rm -f $@ $(AR) rcs $@ $^ $(RANLIB) $@ +else +CFLAGS += -fPIC -fvisibility=hidden +$(PCILIB): $(addsuffix .o,$(OBJS)) + $(CC) -shared $(SONAME) -Wl,--version-script=libpci.ver -o $@ $^ $(LIB_LDLIBS) +endif -$(PCILIBPC): $(PCILIBPC).in +$(PCILIBPC): libpci.pc.in sed <$< >$@ -e 's,@PREFIX@,$(PREFIX),' \ -e 's,@INCDIR@,$(INCDIR),' \ -e 's,@LIBDIR@,$(LIBDIR),' \ -e 's,@IDSDIR@,$(IDSDIR),' \ -e 's,@VERSION@,$(VERSION),' \ - -e 's,@LIBZ@,$(LIBZ),' + -e 's,@LDLIBS@,$(LDLIBS),' +init.o: init.c $(INCL) access.o: access.c $(INCL) +params.o: params.c $(INCL) i386-ports.o: i386-ports.c $(INCL) i386-io-hurd.h i386-io-linux.h i386-io-sunos.h i386-io-windows.h proc.o: proc.c $(INCL) pread.h sysfs.o: sysfs.c $(INCL) pread.h @@ -71,8 +75,10 @@ obsd-device.o: obsd-device.c $(INCL) fbsd-device.o: fbsd-device.c $(INCL) aix-device.o: aix-device.c $(INCL) dump.o: dump.c $(INCL) -names.o: names.c $(INCL) +names.o: names.c $(INCL) names.h +names-cache.o: names-cache.c $(INCL) names.h +names-hash.o: names-hash.c $(INCL) names.h +names-net.o: names-net.c $(INCL) names.h +names-parse.o: names-parse.c $(INCL) names.h filter.o: filter.c $(INCL) nbsd-libpci.o: nbsd-libpci.c $(INCL) - -example: example.c $(PCILIB) diff --git a/lib/access.c b/lib/access.c index 01bdc24..d9032a7 100644 --- a/lib/access.c +++ b/lib/access.c @@ -13,183 +13,6 @@ #include "internal.h" -static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = { - NULL, -#ifdef PCI_HAVE_PM_LINUX_SYSFS - &pm_linux_sysfs, -#else - NULL, -#endif -#ifdef PCI_HAVE_PM_LINUX_PROC - &pm_linux_proc, -#else - NULL, -#endif -#ifdef PCI_HAVE_PM_INTEL_CONF - &pm_intel_conf1, - &pm_intel_conf2, -#else - NULL, - NULL, -#endif -#ifdef PCI_HAVE_PM_FBSD_DEVICE - &pm_fbsd_device, -#else - NULL, -#endif -#ifdef PCI_HAVE_PM_AIX_DEVICE - &pm_aix_device, -#else - NULL, -#endif -#ifdef PCI_HAVE_PM_NBSD_LIBPCI - &pm_nbsd_libpci, -#else - NULL, -#endif -#ifdef PCI_HAVE_PM_OBSD_DEVICE - &pm_obsd_device, -#else - NULL, -#endif -#ifdef PCI_HAVE_PM_DUMP - &pm_dump, -#else - NULL, -#endif -}; - -struct pci_access * -pci_alloc(void) -{ - struct pci_access *a = malloc(sizeof(struct pci_access)); - int i; - - memset(a, 0, sizeof(*a)); - pci_set_name_list_path(a, PCI_PATH_IDS_DIR "/" PCI_IDS, 0); - pci_set_net_domain(a, PCI_ID_DOMAIN, 0); - a->id_lookup_mode = PCI_LOOKUP_CACHE; - for(i=0; i<PCI_ACCESS_MAX; i++) - if (pci_methods[i] && pci_methods[i]->config) - pci_methods[i]->config(a); - return a; -} - -void * -pci_malloc(struct pci_access *a, int size) -{ - void *x = malloc(size); - - if (!x) - a->error("Out of memory (allocation of %d bytes failed)", size); - return x; -} - -void -pci_mfree(void *x) -{ - if (x) - free(x); -} - -static void -pci_generic_error(char *msg, ...) -{ - va_list args; - - va_start(args, msg); - fputs("pcilib: ", stderr); - vfprintf(stderr, msg, args); - fputc('\n', stderr); - exit(1); -} - -static void -pci_generic_warn(char *msg, ...) -{ - va_list args; - - va_start(args, msg); - fputs("pcilib: ", stderr); - vfprintf(stderr, msg, args); - fputc('\n', stderr); -} - -static void -pci_generic_debug(char *msg, ...) -{ - va_list args; - - va_start(args, msg); - vfprintf(stdout, msg, args); - va_end(args); -} - -static void -pci_null_debug(char *msg UNUSED, ...) -{ -} - -void -pci_init(struct pci_access *a) -{ - if (!a->error) - a->error = pci_generic_error; - if (!a->warning) - a->warning = pci_generic_warn; - if (!a->debug) - a->debug = pci_generic_debug; - if (!a->debugging) - a->debug = pci_null_debug; - - if (a->method) - { - if (a->method >= PCI_ACCESS_MAX || !pci_methods[a->method]) - a->error("This access method is not supported."); - a->methods = pci_methods[a->method]; - } - else - { - unsigned int i; - for(i=0; i<PCI_ACCESS_MAX; i++) - if (pci_methods[i]) - { - a->debug("Trying method %d...", i); - if (pci_methods[i]->detect(a)) - { - a->debug("...OK\n"); - a->methods = pci_methods[i]; - a->method = i; - break; - } - a->debug("...No.\n"); - } - if (!a->methods) - a->error("Cannot find any working access method."); - } - a->debug("Decided to use %s\n", a->methods->name); - a->methods->init(a); -} - -void -pci_cleanup(struct pci_access *a) -{ - struct pci_dev *d, *e; - - for(d=a->devices; d; d=e) - { - e = d->next; - pci_free_dev(d); - } - if (a->methods) - a->methods->cleanup(a); - pci_free_name_list(a); - pci_set_name_list_path(a, NULL, 0); - pci_set_net_domain(a, NULL, 0); - pci_set_id_cache(a, NULL, 0); - pci_mfree(a); -} - void pci_scan_bus(struct pci_access *a) { diff --git a/lib/aix-device.c b/lib/aix-device.c index e02c98d..cb0a974 100644 --- a/lib/aix-device.c +++ b/lib/aix-device.c @@ -122,12 +122,6 @@ aix_bus_number(char *name) /* Method entries */ -static void -aix_config(struct pci_access *a) -{ - a->method_params[PCI_ACCESS_AIX_DEVICE] = NULL; -} - static int aix_detect(struct pci_access *a) { @@ -265,8 +259,9 @@ aix_write(struct pci_dev *d, int pos, byte *buf, int len) } struct pci_methods pm_aix_device = { - "AIX-device", - aix_config, + "aix-device", + "AIX /dev/pci[0-n]", + NULL, aix_detect, aix_init, aix_cleanup, diff --git a/lib/configure b/lib/configure index b368c98..d855a3c 100755 --- a/lib/configure +++ b/lib/configure @@ -1,4 +1,6 @@ #!/bin/sh +# Configuration script for the PCI library +# (c) 1998--2008 Martin Mares <mj@ucw.cz> echo_n() { if [ -n "$BASH" ] @@ -9,37 +11,40 @@ echo_n() { fi } -echo_n "Configuring libpci for your system..." -idsdir=${1:-/usr/share} -version=${2:-0.0} -sys=`uname -s` -rel=`uname -r` -if [ "$sys" = "AIX" -a -x /usr/bin/oslevel -a -x /usr/sbin/lsattr ] -then - rel=`/usr/bin/oslevel` - proc=`/usr/sbin/lsdev -C -c processor -S available -F name | head -1` - cpu=`/usr/sbin/lsattr -F value -l $proc -a type | sed 's/_.*//'` -else - cpu=`uname -m | sed 's/^i.86$/i386/;s/^sun4u$/sparc64/;s/^i86pc$/i386/'` +if [ -z "$VERSION" -o -z "$IDSDIR" ] ; then + echo >&2 "Please run the configure script from the top-level Makefile" + exit 1 fi -if [ "$sys" = "GNU/kFreeBSD" -o "$sys" = "DragonFly" ] -then - sys=freebsd + +echo_n "Configuring libpci for your system..." +if [ -z "$HOST" ] ; then + sys=`uname -s` + rel=`uname -r` + if [ "$sys" = "AIX" -a -x /usr/bin/oslevel -a -x /usr/sbin/lsattr ] + then + rel=`/usr/bin/oslevel` + proc=`/usr/sbin/lsdev -C -c processor -S available -F name | head -1` + cpu=`/usr/sbin/lsattr -F value -l $proc -a type | sed 's/_.*//'` + else + cpu=`uname -m | sed 's/^i.86$/i386/;s/^sun4u$/sparc64/;s/^i86pc$/i386/'` + fi + if [ "$sys" = "GNU/kFreeBSD" -o "$sys" = "DragonFly" ] + then + sys=freebsd + fi + HOST=${3:-$cpu-$sys} fi -host=${3:-$cpu-$sys} # CAVEAT: tr on Solaris is a bit weird and the extra [] is otherwise harmless. -host=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)$/\1--\2/' | tr '[A-Z]' '[a-z]'` +host=`echo $HOST | sed 's/^\([^-]*\)-\([^-]*\)$/\1--\2/' | tr '[A-Z]' '[a-z]'` cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` sys=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` -rel=${4:-$rel} echo " $host $rel" -zlib=$5 c=config.h m=config.mk echo >$c "#define PCI_ARCH_`echo $cpu | tr '[a-z]' '[A-Z]'`" echo >>$c "#define PCI_OS_`echo $sys | tr '[a-z]' '[A-Z]'`" -rm -f $m +echo >$m 'WITH_LIBS=' echo_n "Looking for access methods..." @@ -52,11 +57,12 @@ case $sys in echo >>$c '#define PCI_PATH_PROC_BUS_PCI "/proc/bus/pci"' echo >>$c '#define PCI_PATH_SYS_BUS_PCI "/sys/bus/pci"' case $cpu in - i386) echo_n " i386-ports" + i386|x86_64) echo_n " i386-ports" echo >>$c '#define PCI_HAVE_PM_INTEL_CONF' ;; esac echo >>$c '#define PCI_HAVE_64BIT_ADDRESS' + echo >>$c '#define PCI_USE_DNS' ;; sunos) case $cpu in @@ -64,7 +70,7 @@ case $sys in echo >>$c "#define PCI_HAVE_PM_INTEL_CONF" ;; *) - echo " The PCI library is does not support Solaris for this architecture: $cpu" + echo " The PCI library does not support Solaris for this architecture: $cpu" exit 1 ;; esac @@ -91,8 +97,8 @@ case $sys in echo_n " nbsd-libpci" echo >>$c '#define PCI_HAVE_PM_NBSD_LIBPCI' echo >>$c '#define PCI_PATH_NBSD_DEVICE "/dev/pci0"' - echo >>$m 'PCILIB=lib/libpciutils.a' - echo >>$m 'LDLIBS+=-lpci' + echo >>$m 'LIBNAME=libpciutils' + echo >>$m 'WITH_LIBS+=-lpci' ;; gnu) echo_n " i386-ports" @@ -108,26 +114,58 @@ echo >>$c '#define PCI_HAVE_PM_DUMP' echo " dump" echo_n "Checking for zlib support... " -if [ "$zlib" = yes -o "$zlib" = no ] ; then - echo "$zlib (set manually)" +if [ "$ZLIB" = yes -o "$ZLIB" = no ] ; then + echo "$ZLIB (set manually)" else - if [ -f /usr/include/zlib.h ] ; then - zlib=yes + if [ -f /usr/include/zlib.h -o -f /usr/local/include/zlib.h ] ; then + ZLIB=yes else - zlib=no + ZLIB=no fi - echo "$zlib (auto-detected)" + echo "$ZLIB (auto-detected)" fi -if [ "$zlib" = yes ] ; then +if [ "$ZLIB" = yes ] ; then echo >>$c '#define PCI_COMPRESSED_IDS' echo >>$c '#define PCI_IDS "pci.ids.gz"' echo >>$m 'LIBZ=-lz' - echo >>$m 'LDLIBS+=$(LIBZ)' + echo >>$m 'WITH_LIBS+=$(LIBZ)' else echo >>$c '#define PCI_IDS "pci.ids"' fi -echo >>$c "#define PCI_PATH_IDS_DIR \"$idsdir\"" -echo >>$c "#define PCI_ID_DOMAIN \"pci-id.ucw.cz\"" +echo >>$c "#define PCI_PATH_IDS_DIR \"$IDSDIR\"" + +echo_n "Checking for DNS support... " +if [ "$DNS" = yes -o "$DNS" = no ] ; then + echo "$DNS (set manually)" +else + if [ -f /usr/include/resolv.h ] ; then + DNS=yes + else + DNS=no + fi + echo "$DNS (auto-detected)" +fi +if [ "$DNS" = yes ] ; then + echo >>$c "#define PCI_USE_DNS" + echo >>$c "#define PCI_ID_DOMAIN \"pci.id.ucw.cz\"" + echo >>$m 'WITH_LIBS+=-lresolv' +fi + +echo "Checking whether to build a shared library... $SHARED (set manually)" +if [ "$SHARED" = no ] ; then + echo >>$m 'PCILIB=$(LIBNAME).a' + echo >>$m 'LDLIBS=$(WITH_LIBS)' + echo >>$m 'LIB_LDLIBS=' +else + echo >>$m 'PCILIB=$(LIBNAME).so.$(VERSION)' + # We link the dependencies _to_ the library, so we do not need explicit deps in .pc + echo >>$m 'LDLIBS=' + echo >>$m 'LIB_LDLIBS=$(WITH_LIBS)' + if [ "$SHARED" = yes ] ; then + echo >>$m 'SONAME=-Wl,-soname,$(LIBNAME).so$(ABI_VERSION)' + fi +fi +echo >>$m 'PCILIBPC=$(LIBNAME).pc' -echo >>$c "#define PCILIB_VERSION \"$version\"" +echo >>$c "#define PCILIB_VERSION \"$VERSION\"" sed '/"/{s/^#define \([^ ]*\) "\(.*\)"$/\1=\2/;p;d;};s/^#define \(.*\)/\1=1/' <$c >>$m @@ -1,7 +1,7 @@ /* * The PCI Library -- Reading of Bus Dumps * - * Copyright (c) 1997--2005 Martin Mares <mj@ucw.cz> + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -18,10 +18,17 @@ struct dump_data { byte data[1]; }; +static void +dump_config(struct pci_access *a) +{ + pci_define_param(a, "dump.name", "", "Name of the bus dump file to read from"); +} + static int dump_detect(struct pci_access *a) { - return !!a->method_params[PCI_ACCESS_DUMP]; + char *name = pci_get_param(a, "dump.name"); + return name && name[0]; } static void @@ -49,7 +56,7 @@ dump_validate(char *s, char *fmt) static void dump_init(struct pci_access *a) { - char *name = a->method_params[PCI_ACCESS_DUMP]; + char *name = pci_get_param(a, "dump.name"); FILE *f; char buf[256]; struct pci_dev *dev = NULL; @@ -157,7 +164,8 @@ dump_cleanup_dev(struct pci_dev *d) struct pci_methods pm_dump = { "dump", - NULL, /* config */ + "Reading of register dumps (set the `dump.name' parameter)", + dump_config, dump_detect, dump_init, dump_cleanup, diff --git a/lib/fbsd-device.c b/lib/fbsd-device.c index 875480d..742c641 100644 --- a/lib/fbsd-device.c +++ b/lib/fbsd-device.c @@ -33,13 +33,13 @@ static void fbsd_config(struct pci_access *a) { - a->method_params[PCI_ACCESS_FBSD_DEVICE] = PCI_PATH_FBSD_DEVICE; + pci_define_param(a, "fbsd.path", PCI_PATH_FBSD_DEVICE, "Path to the FreeBSD PCI device"); } static int fbsd_detect(struct pci_access *a) { - char *name = a->method_params[PCI_ACCESS_FBSD_DEVICE]; + char *name = pci_get_param(a, "fbsd.path"); if (access(name, R_OK)) { @@ -53,7 +53,7 @@ fbsd_detect(struct pci_access *a) static void fbsd_init(struct pci_access *a) { - char *name = a->method_params[PCI_ACCESS_FBSD_DEVICE]; + char *name = pci_get_param(a, "fbsd.path"); a->fd = open(name, O_RDWR, 0); if (a->fd < 0) @@ -154,7 +154,8 @@ fbsd_write(struct pci_dev *d, int pos, byte *buf, int len) } struct pci_methods pm_fbsd_device = { - "FreeBSD-device", + "fbsd-device", + "FreeBSD /dev/pci device", fbsd_config, fbsd_detect, fbsd_init, diff --git a/lib/i386-ports.c b/lib/i386-ports.c index 4b51c88..041e47f 100644 --- a/lib/i386-ports.c +++ b/lib/i386-ports.c @@ -253,7 +253,8 @@ conf2_write(struct pci_dev *d, int pos, byte *buf, int len) } struct pci_methods pm_intel_conf1 = { - "Intel-conf1", + "intel-conf1", + "Raw I/O port access using Intel conf1 interface", NULL, /* config */ conf1_detect, conf12_init, @@ -267,7 +268,8 @@ struct pci_methods pm_intel_conf1 = { }; struct pci_methods pm_intel_conf2 = { - "Intel-conf2", + "intel-conf2", + "Raw I/O port access using Intel conf2 interface", NULL, /* config */ conf2_detect, conf12_init, diff --git a/lib/init.c b/lib/init.c new file mode 100644 index 0000000..be45f81 --- /dev/null +++ b/lib/init.c @@ -0,0 +1,224 @@ +/* + * The PCI Library -- Initialization and related things + * + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> + +#include "internal.h" + +static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = { + NULL, +#ifdef PCI_HAVE_PM_LINUX_SYSFS + &pm_linux_sysfs, +#else + NULL, +#endif +#ifdef PCI_HAVE_PM_LINUX_PROC + &pm_linux_proc, +#else + NULL, +#endif +#ifdef PCI_HAVE_PM_INTEL_CONF + &pm_intel_conf1, + &pm_intel_conf2, +#else + NULL, + NULL, +#endif +#ifdef PCI_HAVE_PM_FBSD_DEVICE + &pm_fbsd_device, +#else + NULL, +#endif +#ifdef PCI_HAVE_PM_AIX_DEVICE + &pm_aix_device, +#else + NULL, +#endif +#ifdef PCI_HAVE_PM_NBSD_LIBPCI + &pm_nbsd_libpci, +#else + NULL, +#endif +#ifdef PCI_HAVE_PM_OBSD_DEVICE + &pm_obsd_device, +#else + NULL, +#endif +#ifdef PCI_HAVE_PM_DUMP + &pm_dump, +#else + NULL, +#endif +}; + +void * +pci_malloc(struct pci_access *a, int size) +{ + void *x = malloc(size); + + if (!x) + a->error("Out of memory (allocation of %d bytes failed)", size); + return x; +} + +void +pci_mfree(void *x) +{ + if (x) + free(x); +} + +char * +pci_strdup(struct pci_access *a, char *s) +{ + int len = strlen(s) + 1; + char *t = pci_malloc(a, len); + memcpy(t, s, len); + return t; +} + +static void +pci_generic_error(char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fputs("pcilib: ", stderr); + vfprintf(stderr, msg, args); + fputc('\n', stderr); + exit(1); +} + +static void +pci_generic_warn(char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fputs("pcilib: ", stderr); + vfprintf(stderr, msg, args); + fputc('\n', stderr); +} + +static void +pci_generic_debug(char *msg, ...) +{ + va_list args; + + va_start(args, msg); + vfprintf(stdout, msg, args); + va_end(args); +} + +static void +pci_null_debug(char *msg UNUSED, ...) +{ +} + +int +pci_lookup_method(char *name) +{ + int i; + + for (i=0; i<PCI_ACCESS_MAX; i++) + if (pci_methods[i] && !strcmp(pci_methods[i]->name, name)) + return i; + return -1; +} + +char * +pci_get_method_name(int index) +{ + if (index < 0 || index >= PCI_ACCESS_MAX) + return NULL; + else if (!pci_methods[index]) + return ""; + else + return pci_methods[index]->name; +} + +struct pci_access * +pci_alloc(void) +{ + struct pci_access *a = malloc(sizeof(struct pci_access)); + int i; + + memset(a, 0, sizeof(*a)); + pci_set_name_list_path(a, PCI_PATH_IDS_DIR "/" PCI_IDS, 0); +#ifdef PCI_USE_DNS + pci_define_param(a, "net.domain", PCI_ID_DOMAIN, "DNS domain used for resolving of ID's"); + pci_define_param(a, "net.cache_name", "~/.pciids-cache", "Name of the ID cache file"); + a->id_lookup_mode = PCI_LOOKUP_CACHE; +#endif + for(i=0; i<PCI_ACCESS_MAX; i++) + if (pci_methods[i] && pci_methods[i]->config) + pci_methods[i]->config(a); + return a; +} + +void +pci_init(struct pci_access *a) +{ + if (!a->error) + a->error = pci_generic_error; + if (!a->warning) + a->warning = pci_generic_warn; + if (!a->debug) + a->debug = pci_generic_debug; + if (!a->debugging) + a->debug = pci_null_debug; + + if (a->method) + { + if (a->method >= PCI_ACCESS_MAX || !pci_methods[a->method]) + a->error("This access method is not supported."); + a->methods = pci_methods[a->method]; + } + else + { + unsigned int i; + for(i=0; i<PCI_ACCESS_MAX; i++) + if (pci_methods[i]) + { + a->debug("Trying method %d...", i); + if (pci_methods[i]->detect(a)) + { + a->debug("...OK\n"); + a->methods = pci_methods[i]; + a->method = i; + break; + } + a->debug("...No.\n"); + } + if (!a->methods) + a->error("Cannot find any working access method."); + } + a->debug("Decided to use %s\n", a->methods->name); + a->methods->init(a); +} + +void +pci_cleanup(struct pci_access *a) +{ + struct pci_dev *d, *e; + + for(d=a->devices; d; d=e) + { + e = d->next; + pci_free_dev(d); + } + if (a->methods) + a->methods->cleanup(a); + pci_free_name_list(a); + pci_free_params(a); + pci_set_name_list_path(a, NULL, 0); + pci_mfree(a); +} diff --git a/lib/internal.h b/lib/internal.h index 925eac1..81b2b83 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -1,16 +1,21 @@ /* * The PCI Library -- Internal Stuff * - * Copyright (c) 1997--2004 Martin Mares <mj@ucw.cz> + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */ +#ifdef __GNUC__ +#define PCI_ABI __attribute__((visibility("default"))) +#endif + #include "pci.h" #include "sysdep.h" struct pci_methods { char *name; + char *help; void (*config)(struct pci_access *); int (*detect)(struct pci_access *); void (*init)(struct pci_access *); @@ -31,10 +36,15 @@ int pci_generic_block_write(struct pci_dev *, int pos, byte *buf, int len); void *pci_malloc(struct pci_access *, int); void pci_mfree(void *); +char *pci_strdup(struct pci_access *a, char *s); struct pci_dev *pci_alloc_dev(struct pci_access *); int pci_link_dev(struct pci_access *, struct pci_dev *); +void pci_define_param(struct pci_access *acc, char *param, char *val, char *help); +int pci_set_param_internal(struct pci_access *acc, char *param, char *val, int copy); +void pci_free_params(struct pci_access *acc); + extern struct pci_methods pm_intel_conf1, pm_intel_conf2, pm_linux_proc, pm_fbsd_device, pm_aix_device, pm_nbsd_libpci, pm_obsd_device, pm_dump, pm_linux_sysfs; diff --git a/lib/libpci.pc.in b/lib/libpci.pc.in index 3e47698..f43f498 100644 --- a/lib/libpci.pc.in +++ b/lib/libpci.pc.in @@ -6,5 +6,5 @@ idsdir=@IDSDIR@ Name: libpci Description: libpci Version: @VERSION@ -Libs: -L${libdir} -lpci @LIBZ@ +Libs: -L${libdir} -lpci @LDLIBS@ Cflags: -I${includedir} diff --git a/lib/libpci.ver b/lib/libpci.ver new file mode 100644 index 0000000..192bce7 --- /dev/null +++ b/lib/libpci.ver @@ -0,0 +1,12 @@ +/* Version script for the libpci */ + +/* + * Visibility declarations in the source take precedence over this script, + * so we can boldly declare pci_* as public and still keep the internal + * functions properly hidden. + */ + +LIBPCI_2.99 { + global: pci_*; + local: *; +}; diff --git a/lib/names-cache.c b/lib/names-cache.c new file mode 100644 index 0000000..059311a --- /dev/null +++ b/lib/names-cache.c @@ -0,0 +1,203 @@ +/* + * The PCI Library -- ID to Name Cache + * + * Copyright (c) 2008 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <pwd.h> +#include <unistd.h> + +#include "internal.h" +#include "names.h" + +#ifdef PCI_USE_DNS + +static const char cache_version[] = "#PCI-CACHE-1.0"; + +static char *get_cache_name(struct pci_access *a) +{ + char *name, *buf; + + name = pci_get_param(a, "net.cache_name"); + if (!name || !name[0]) + return NULL; + if (strncmp(name, "~/", 2)) + return name; + + uid_t uid = getuid(); + struct passwd *pw = getpwuid(uid); + if (!pw) + return name; + + buf = pci_malloc(a, strlen(pw->pw_dir) + strlen(name+1) + 1); + sprintf(buf, "%s%s", pw->pw_dir, name+1); + pci_set_param_internal(a, "net.cache_name", buf, 0); + return buf; +} + +int +pci_id_cache_load(struct pci_access *a, int flags) +{ + char *name; + char line[MAX_LINE]; + FILE *f; + int lino; + + a->id_cache_status = 1; + name = get_cache_name(a); + if (!name) + return 0; + a->debug("Using cache %s\n", name); + if (flags & PCI_LOOKUP_REFRESH_CACHE) + { + a->debug("Not loading cache, will refresh everything\n"); + a->id_cache_status = 2; + return 0; + } + + f = fopen(name, "rb"); + if (!f) + { + a->debug("Cache file does not exist\n"); + return 0; + } + /* FIXME: Compare timestamp with the pci.ids file? */ + + lino = 0; + while (fgets(line, sizeof(line), f)) + { + char *p = strchr(line, '\n'); + lino++; + if (p) + { + *p = 0; + if (lino == 1) + { + if (strcmp(line, cache_version)) + { + a->debug("Unrecognized cache version %s, ignoring\n", line); + break; + } + continue; + } + else + { + int cat, id1, id2, id3, id4, cnt; + if (sscanf(line, "%d%x%x%x%x%n", &cat, &id1, &id2, &id3, &id4, &cnt) >= 5) + { + p = line + cnt; + while (*p && *p == ' ') + p++; + pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_CACHE); + continue; + } + } + } + a->warning("Malformed cache file %s (line %d), ignoring", name, lino); + break; + } + + if (ferror(f)) + a->warning("Error while reading %s", name); + fclose(f); + return 1; +} + +void +pci_id_cache_flush(struct pci_access *a) +{ + int orig_status = a->id_cache_status; + FILE *f; + unsigned int h; + struct id_entry *e, *e2; + char hostname[256], *tmpname, *name; + int this_pid; + + a->id_cache_status = 0; + if (orig_status < 2) + return; + name = get_cache_name(a); + if (!name) + return; + + this_pid = getpid(); + if (gethostname(hostname, sizeof(hostname)) < 0) + hostname[0] = 0; + else + hostname[sizeof(hostname)-1] = 0; + tmpname = pci_malloc(a, strlen(name) + strlen(hostname) + 64); + sprintf(tmpname, "%s.tmp-%s-%d", name, hostname, this_pid); + + f = fopen(tmpname, "wb"); + if (!f) + { + a->warning("Cannot write to %s: %s", name, strerror(errno)); + pci_mfree(tmpname); + return; + } + a->debug("Writing cache to %s\n", name); + fprintf(f, "%s\n", cache_version); + + for (h=0; h<HASH_SIZE; h++) + for (e=a->id_hash[h]; e; e=e->next) + if (e->src == SRC_CACHE || e->src == SRC_NET) + { + /* Negative entries are not written */ + if (!e->name[0]) + continue; + + /* Verify that every entry is written at most once */ + for (e2=a->id_hash[h]; e2 != e; e2=e2->next) + if ((e2->src == SRC_CACHE || e2->src == SRC_NET) && + e2->cat == e->cat && + e2->id12 == e->id12 && e2->id34 == e->id34) + break; + if (e2 == e) + fprintf(f, "%d %x %x %x %x %s\n", + e->cat, + pair_first(e->id12), pair_second(e->id12), + pair_first(e->id34), pair_second(e->id34), + e->name); + } + + fflush(f); + if (ferror(f)) + a->warning("Error writing %s", name); + fclose(f); + + if (rename(tmpname, name) < 0) + { + a->warning("Cannot rename %s to %s: %s", tmpname, name, strerror(errno)); + unlink(tmpname); + } + pci_mfree(tmpname); +} + +#else + +int pci_id_cache_load(struct pci_access *a UNUSED, int flags UNUSED) +{ + a->id_cache_status = 1; + return 0; +} + +void pci_id_cache_flush(struct pci_access *a) +{ + a->id_cache_status = 0; +} + +#endif + +void +pci_id_cache_dirty(struct pci_access *a) +{ + if (a->id_cache_status >= 1) + a->id_cache_status = 2; +} diff --git a/lib/names-hash.c b/lib/names-hash.c new file mode 100644 index 0000000..33f3c11 --- /dev/null +++ b/lib/names-hash.c @@ -0,0 +1,126 @@ +/* + * The PCI Library -- ID to Name Hash + * + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <string.h> + +#include "internal.h" +#include "names.h" + +struct id_bucket { + struct id_bucket *next; + unsigned int full; +}; + +#ifdef __GNUC__ +#define BUCKET_ALIGNMENT __alignof__(struct id_bucket) +#else +union id_align { + struct id_bucket *next; + unsigned int full; +}; +#define BUCKET_ALIGNMENT sizeof(union id_align) +#endif +#define BUCKET_ALIGN(n) ((n)+BUCKET_ALIGNMENT-(n)%BUCKET_ALIGNMENT) + +static void *id_alloc(struct pci_access *a, unsigned int size) +{ + struct id_bucket *buck = a->current_id_bucket; + unsigned int pos; + + if (!a->id_hash) + { + a->id_hash = pci_malloc(a, sizeof(struct id_entry *) * HASH_SIZE); + memset(a->id_hash, 0, sizeof(struct id_entry *) * HASH_SIZE); + } + + if (!buck || buck->full + size > BUCKET_SIZE) + { + buck = pci_malloc(a, BUCKET_SIZE); + buck->next = a->current_id_bucket; + a->current_id_bucket = buck; + buck->full = BUCKET_ALIGN(sizeof(struct id_bucket)); + } + pos = buck->full; + buck->full = BUCKET_ALIGN(buck->full + size); + return (byte *)buck + pos; +} + +static inline unsigned int id_hash(int cat, u32 id12, u32 id34) +{ + unsigned int h; + + h = id12 ^ (id34 << 3) ^ (cat << 5); + return h % HASH_SIZE; +} + +int +pci_id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src) +{ + u32 id12 = id_pair(id1, id2); + u32 id34 = id_pair(id3, id4); + unsigned int h = id_hash(cat, id12, id34); + struct id_entry *n = a->id_hash ? a->id_hash[h] : NULL; + int len = strlen(text); + + while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat)) + n = n->next; + if (n) + return 1; + n = id_alloc(a, sizeof(struct id_entry) + len); + n->id12 = id12; + n->id34 = id34; + n->cat = cat; + n->src = src; + memcpy(n->name, text, len+1); + n->next = a->id_hash[h]; + a->id_hash[h] = n; + return 0; +} + +char +*pci_id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4) +{ + struct id_entry *n, *best; + u32 id12 = id_pair(id1, id2); + u32 id34 = id_pair(id3, id4); + + if (a->id_hash) + { + n = a->id_hash[id_hash(cat, id12, id34)]; + best = NULL; + for (; n; n=n->next) + { + if (n->id12 != id12 || n->id34 != id34 || n->cat != cat) + continue; + if (n->src == SRC_LOCAL && (flags & PCI_LOOKUP_SKIP_LOCAL)) + continue; + if (n->src == SRC_NET && !(flags & PCI_LOOKUP_NETWORK)) + continue; + if (n->src == SRC_CACHE && !(flags & PCI_LOOKUP_CACHE)) + continue; + if (!best || best->src < n->src) + best = n; + } + if (best) + return best->name; + } + return NULL; +} + +void +pci_id_hash_free(struct pci_access *a) +{ + pci_mfree(a->id_hash); + a->id_hash = NULL; + while (a->current_id_bucket) + { + struct id_bucket *buck = a->current_id_bucket; + a->current_id_bucket = buck->next; + pci_mfree(buck); + } +} diff --git a/lib/names-net.c b/lib/names-net.c new file mode 100644 index 0000000..d149491 --- /dev/null +++ b/lib/names-net.c @@ -0,0 +1,239 @@ +/* + * The PCI Library -- Resolving ID's via DNS + * + * Copyright (c) 2007--2008 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <string.h> +#include <stdlib.h> + +#include "internal.h" +#include "names.h" + +#ifdef PCI_USE_DNS + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> + +/* + * Unfortunately, there are no portable functions for DNS RR parsing, + * so we will do the bit shuffling with our own bare hands. + */ + +#define GET16(x) do { if (p+2 > end) goto err; x = (p[0] << 8) | p[1]; p += 2; } while (0) +#define GET32(x) do { if (p+4 > end) goto err; x = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; p += 4; } while (0) + +enum dns_section { + DNS_SEC_QUESTION, + DNS_SEC_ANSWER, + DNS_SEC_AUTHORITY, + DNS_SEC_ADDITIONAL, + DNS_NUM_SECTIONS +}; + +struct dns_state { + u16 counts[DNS_NUM_SECTIONS]; + byte *sections[DNS_NUM_SECTIONS+1]; + byte *sec_ptr, *sec_end; + + /* Result of dns_parse_rr(): */ + u16 rr_type; + u16 rr_class; + u32 rr_ttl; + u16 rr_len; + byte *rr_data; +}; + +static byte * +dns_skip_name(byte *p, byte *end) +{ + while (p < end) + { + unsigned int x = *p++; + if (!x) + return p; + switch (x & 0xc0) + { + case 0: /* Uncompressed: x = length */ + p += x; + break; + case 0xc0: /* Indirection: 1 byte more for offset */ + p++; + return (p < end) ? p : NULL; + default: /* RFU */ + return NULL; + } + } + return NULL; +} + +static int +dns_parse_packet(struct dns_state *s, byte *p, unsigned int plen) +{ + byte *end = p + plen; + unsigned int i, j, x, len; + +#if 0 + /* Dump the packet */ + for (i=0; i<plen; i++) + { + if (!(i%16)) printf("%04x:", i); + printf(" %02x", p[i]); + if ((i%16)==15 || i==plen-1) putchar('\n'); + } +#endif + + GET32(x); /* ID and flags are ignored */ + for (i=0; i<DNS_NUM_SECTIONS; i++) + GET16(s->counts[i]); + for (i=0; i<DNS_NUM_SECTIONS; i++) + { + s->sections[i] = p; + for (j=0; j < s->counts[i]; j++) + { + p = dns_skip_name(p, end); /* Name */ + if (!p) + goto err; + GET32(x); /* Type and class */ + if (i != DNS_SEC_QUESTION) + { + GET32(x); /* TTL */ + GET16(len); /* Length of data */ + p += len; + if (p > end) + goto err; + } + } + } + s->sections[i] = p; + return 0; + +err: + return -1; +} + +static void +dns_init_section(struct dns_state *s, int i) +{ + s->sec_ptr = s->sections[i]; + s->sec_end = s->sections[i+1]; +} + +static int +dns_parse_rr(struct dns_state *s) +{ + byte *p = s->sec_ptr; + byte *end = s->sec_end; + + if (p == end) + return 0; + p = dns_skip_name(p, end); + if (!p) + goto err; + GET16(s->rr_type); + GET16(s->rr_class); + GET32(s->rr_ttl); + GET16(s->rr_len); + s->rr_data = p; + s->sec_ptr = p + s->rr_len; + return 1; + +err: + return -1; +} + +char +*pci_id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4) +{ + static int resolver_inited; + char name[256], dnsname[256], txt[256], *domain; + byte answer[4096]; + const byte *data; + int res, j, dlen; + struct dns_state ds; + + domain = pci_get_param(a, "net.domain"); + if (!domain || !domain[0]) + return NULL; + + switch (cat) + { + case ID_VENDOR: + sprintf(name, "%04x", id1); + break; + case ID_DEVICE: + sprintf(name, "%04x.%04x", id2, id1); + break; + case ID_SUBSYSTEM: + sprintf(name, "%04x.%04x.%04x.%04x", id4, id3, id2, id1); + break; + case ID_GEN_SUBSYSTEM: + sprintf(name, "%04x.%04x.s", id2, id1); + break; + case ID_CLASS: + sprintf(name, "%02x.c", id1); + break; + case ID_SUBCLASS: + sprintf(name, "%02x.%02x.c", id2, id1); + break; + case ID_PROGIF: + sprintf(name, "%02x.%02x.%02x.c", id3, id2, id1); + break; + default: + return NULL; + } + sprintf(dnsname, "%s.%s", name, domain); + + a->debug("Resolving %s\n", dnsname); + if (!resolver_inited) + { + resolver_inited = 1; + res_init(); + } + res = res_query(dnsname, ns_c_in, ns_t_txt, answer, sizeof(answer)); + if (res < 0) + { + a->debug("\tfailed, h_errno=%d\n", _res.res_h_errno); + return NULL; + } + if (dns_parse_packet(&ds, answer, res) < 0) + { + a->debug("\tMalformed DNS packet received\n"); + return NULL; + } + dns_init_section(&ds, DNS_SEC_ANSWER); + while (dns_parse_rr(&ds) > 0) + { + if (ds.rr_class != ns_c_in || ds.rr_type != ns_t_txt) + { + a->debug("\tUnexpected RR in answer: class %d, type %d\n", ds.rr_class, ds.rr_type); + continue; + } + data = ds.rr_data; + dlen = ds.rr_len; + j = 0; + while (j < dlen && j+1+data[j] <= dlen) + { + memcpy(txt, &data[j+1], data[j]); + txt[data[j]] = 0; + j += 1+data[j]; + a->debug("\t\"%s\"\n", txt); + if (txt[0] == 'i' && txt[1] == '=') + return strdup(txt+2); + } + } + + return NULL; +} + +#else + +char *pci_id_net_lookup(struct pci_access *a UNUSED, int cat UNUSED, int id1 UNUSED, int id2 UNUSED, int id3 UNUSED, int id4 UNUSED) +{ + return NULL; +} + +#endif diff --git a/lib/names-parse.c b/lib/names-parse.c new file mode 100644 index 0000000..58ab578 --- /dev/null +++ b/lib/names-parse.c @@ -0,0 +1,251 @@ +/* + * The PCI Library -- Parsing of the ID list + * + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "internal.h" +#include "names.h" + +#ifdef PCI_COMPRESSED_IDS +#include <zlib.h> +typedef gzFile pci_file; +#define pci_gets(f, l, s) gzgets(f, l, s) +#define pci_eof(f) gzeof(f) + +static pci_file pci_open(struct pci_access *a) +{ + pci_file result; + size_t len; + char *new_name; + + result = gzopen(a->id_file_name, "rb"); + if (result) + return result; + len = strlen(a->id_file_name); + if (len >= 3 && memcmp(a->id_file_name + len - 3, ".gz", 3) != 0) + return result; + new_name = malloc(len - 2); + memcpy(new_name, a->id_file_name, len - 3); + new_name[len - 3] = 0; + pci_set_name_list_path(a, new_name, 1); + return gzopen(a->id_file_name, "rb"); +} + +#define pci_close(f) gzclose(f) +#define PCI_ERROR(f, err) \ + if (!err) { \ + int errnum; \ + gzerror(f, &errnum); \ + if (errnum >= 0) err = NULL; \ + else if (errnum == Z_ERRNO) err = "I/O error"; \ + else err = zError(errnum); \ + } +#else +typedef FILE * pci_file; +#define pci_gets(f, l, s) fgets(l, s, f) +#define pci_eof(f) feof(f) +#define pci_open(a) fopen(a->id_file_name, "r") +#define pci_close(f) fclose(f) +#define PCI_ERROR(f, err) if (!err && ferror(f)) err = "I/O error"; +#endif + +static int id_hex(char *p, int cnt) +{ + int x = 0; + while (cnt--) + { + x <<= 4; + if (*p >= '0' && *p <= '9') + x += (*p - '0'); + else if (*p >= 'a' && *p <= 'f') + x += (*p - 'a' + 10); + else if (*p >= 'A' && *p <= 'F') + x += (*p - 'A' + 10); + else + return -1; + p++; + } + return x; +} + +static inline int id_white_p(int c) +{ + return (c == ' ') || (c == '\t'); +} + + +static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino) +{ + char line[MAX_LINE]; + char *p; + int id1=0, id2=0, id3=0, id4=0; + int cat = -1; + int nest; + static const char parse_error[] = "Parse error"; + + *lino = 0; + while (pci_gets(f, line, sizeof(line))) + { + (*lino)++; + p = line; + while (*p && *p != '\n' && *p != '\r') + p++; + if (!*p && !pci_eof(f)) + return "Line too long"; + *p = 0; + if (p > line && (p[-1] == ' ' || p[-1] == '\t')) + *--p = 0; + + p = line; + while (id_white_p(*p)) + p++; + if (!*p || *p == '#') + continue; + + p = line; + while (*p == '\t') + p++; + nest = p - line; + + if (!nest) /* Top-level entries */ + { + if (p[0] == 'C' && p[1] == ' ') /* Class block */ + { + if ((id1 = id_hex(p+2, 2)) < 0 || !id_white_p(p[4])) + return parse_error; + cat = ID_CLASS; + p += 5; + } + else if (p[0] == 'S' && p[1] == ' ') + { /* Generic subsystem block */ + if ((id1 = id_hex(p+2, 4)) < 0 || p[6]) + return parse_error; + if (!pci_id_lookup(a, 0, ID_VENDOR, id1, 0, 0, 0)) + return "Vendor does not exist"; + cat = ID_GEN_SUBSYSTEM; + continue; + } + else if (p[0] >= 'A' && p[0] <= 'Z' && p[1] == ' ') + { /* Unrecognized block (RFU) */ + cat = ID_UNKNOWN; + continue; + } + else /* Vendor ID */ + { + if ((id1 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) + return parse_error; + cat = ID_VENDOR; + p += 5; + } + id2 = id3 = id4 = 0; + } + else if (cat == ID_UNKNOWN) /* Nested entries in RFU blocks are skipped */ + continue; + else if (nest == 1) /* Nesting level 1 */ + switch (cat) + { + case ID_VENDOR: + case ID_DEVICE: + case ID_SUBSYSTEM: + if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) + return parse_error; + p += 5; + cat = ID_DEVICE; + id3 = id4 = 0; + break; + case ID_GEN_SUBSYSTEM: + if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) + return parse_error; + p += 5; + id3 = id4 = 0; + break; + case ID_CLASS: + case ID_SUBCLASS: + case ID_PROGIF: + if ((id2 = id_hex(p, 2)) < 0 || !id_white_p(p[2])) + return parse_error; + p += 3; + cat = ID_SUBCLASS; + id3 = id4 = 0; + break; + default: + return parse_error; + } + else if (nest == 2) /* Nesting level 2 */ + switch (cat) + { + case ID_DEVICE: + case ID_SUBSYSTEM: + if ((id3 = id_hex(p, 4)) < 0 || !id_white_p(p[4]) || (id4 = id_hex(p+5, 4)) < 0 || !id_white_p(p[9])) + return parse_error; + p += 10; + cat = ID_SUBSYSTEM; + break; + case ID_CLASS: + case ID_SUBCLASS: + case ID_PROGIF: + if ((id3 = id_hex(p, 2)) < 0 || !id_white_p(p[2])) + return parse_error; + p += 3; + cat = ID_PROGIF; + id4 = 0; + break; + default: + return parse_error; + } + else /* Nesting level 3 or more */ + return parse_error; + while (id_white_p(*p)) + p++; + if (!*p) + return parse_error; + if (pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_LOCAL)) + return "Duplicate entry"; + } + return NULL; +} + +int +pci_load_name_list(struct pci_access *a) +{ + pci_file f; + int lino; + const char *err; + + pci_free_name_list(a); + a->id_load_failed = 1; + if (!(f = pci_open(a))) + return 0; + err = id_parse_list(a, f, &lino); + PCI_ERROR(f, err); + pci_close(f); + if (err) + a->error("%s at %s, line %d\n", err, a->id_file_name, lino); + a->id_load_failed = 0; + return 1; +} + +void +pci_free_name_list(struct pci_access *a) +{ + pci_id_cache_flush(a); + pci_id_hash_free(a); + a->id_load_failed = 0; +} + +void +pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed) +{ + if (a->free_id_name) + free(a->id_file_name); + a->id_file_name = name; + a->free_id_name = to_be_freed; +} diff --git a/lib/names.c b/lib/names.c index 30b6b6e..26de128 100644 --- a/lib/names.c +++ b/lib/names.c @@ -7,415 +7,17 @@ */ #include <stdio.h> -#include <stdlib.h> #include <stdarg.h> #include <string.h> -#include <errno.h> -#include <netinet/in.h> -#include <arpa/nameser.h> -#include <resolv.h> -#include <sys/types.h> -#include <pwd.h> -#include <unistd.h> #include "internal.h" - -#ifdef PCI_COMPRESSED_IDS -#include <zlib.h> -typedef gzFile pci_file; -#define pci_gets(f, l, s) gzgets(f, l, s) -#define pci_eof(f) gzeof(f) - -static pci_file pci_open(struct pci_access *a) -{ - pci_file result; - size_t len; - char *new_name; - - result = gzopen(a->id_file_name, "rb"); - if (result) - return result; - len = strlen(a->id_file_name); - if (len >= 3 && memcmp(a->id_file_name + len - 3, ".gz", 3) != 0) - return result; - new_name = malloc(len - 2); - memcpy(new_name, a->id_file_name, len - 3); - new_name[len - 3] = 0; - pci_set_name_list_path(a, new_name, 1); - return gzopen(a->id_file_name, "rb"); -} - -#define pci_close(f) gzclose(f) -#define PCI_ERROR(f, err) \ - if (!err) { \ - int errnum; \ - gzerror(f, &errnum); \ - if (errnum >= 0) err = NULL; \ - else if (errnum == Z_ERRNO) err = "I/O error"; \ - else err = zError(errnum); \ - } -#else -typedef FILE * pci_file; -#define pci_gets(f, l, s) fgets(l, s, f) -#define pci_eof(f) feof(f) -#define pci_open(a) fopen(a->id_file_name, "r") -#define pci_close(f) fclose(f) -#define PCI_ERROR(f, err) if (!err && ferror(f)) err = "I/O error"; -#endif - -struct id_entry { - struct id_entry *next; - u32 id12, id34; - byte cat; - byte src; - char name[1]; -}; - -enum id_entry_type { - ID_UNKNOWN, - ID_VENDOR, - ID_DEVICE, - ID_SUBSYSTEM, - ID_GEN_SUBSYSTEM, - ID_CLASS, - ID_SUBCLASS, - ID_PROGIF -}; - -enum id_entry_src { - SRC_UNKNOWN, - SRC_CACHE, - SRC_NET, - SRC_LOCAL, -}; - -struct id_bucket { - struct id_bucket *next; - unsigned int full; -}; - -#define MAX_LINE 1024 -#define BUCKET_SIZE 8192 -#define HASH_SIZE 4099 - -#ifdef __GNUC__ -#define BUCKET_ALIGNMENT __alignof__(struct id_bucket) -#else -union id_align { - struct id_bucket *next; - unsigned int full; -}; -#define BUCKET_ALIGNMENT sizeof(union id_align) -#endif -#define BUCKET_ALIGN(n) ((n)+BUCKET_ALIGNMENT-(n)%BUCKET_ALIGNMENT) - -static void *id_alloc(struct pci_access *a, unsigned int size) -{ - struct id_bucket *buck = a->current_id_bucket; - unsigned int pos; - - if (!a->id_hash) - { - a->id_hash = pci_malloc(a, sizeof(struct id_entry *) * HASH_SIZE); - memset(a->id_hash, 0, sizeof(struct id_entry *) * HASH_SIZE); - } - - if (!buck || buck->full + size > BUCKET_SIZE) - { - buck = pci_malloc(a, BUCKET_SIZE); - buck->next = a->current_id_bucket; - a->current_id_bucket = buck; - buck->full = BUCKET_ALIGN(sizeof(struct id_bucket)); - } - pos = buck->full; - buck->full = BUCKET_ALIGN(buck->full + size); - return (byte *)buck + pos; -} - -static inline u32 id_pair(unsigned int x, unsigned int y) -{ - return ((x << 16) | y); -} - -static inline unsigned int pair_first(unsigned int x) -{ - return (x >> 16) & 0xffff; -} - -static inline unsigned int pair_second(unsigned int x) -{ - return x & 0xffff; -} - -static inline unsigned int id_hash(int cat, u32 id12, u32 id34) -{ - unsigned int h; - - h = id12 ^ (id34 << 3) ^ (cat << 5); - return h % HASH_SIZE; -} - -static int id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src) -{ - u32 id12 = id_pair(id1, id2); - u32 id34 = id_pair(id3, id4); - unsigned int h = id_hash(cat, id12, id34); - struct id_entry *n = a->id_hash ? a->id_hash[h] : NULL; - int len = strlen(text); - - while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat)) - n = n->next; - if (n) - return 1; - n = id_alloc(a, sizeof(struct id_entry) + len); - n->id12 = id12; - n->id34 = id34; - n->cat = cat; - n->src = src; - memcpy(n->name, text, len+1); - n->next = a->id_hash[h]; - a->id_hash[h] = n; - return 0; -} - -static char *id_lookup_raw(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4) -{ - struct id_entry *n, *best; - u32 id12 = id_pair(id1, id2); - u32 id34 = id_pair(id3, id4); - - if (a->id_hash) - { - n = a->id_hash[id_hash(cat, id12, id34)]; - best = NULL; - for (; n; n=n->next) - { - if (n->id12 != id12 || n->id34 != id34 || n->cat != cat) - continue; - if (n->src == SRC_LOCAL && (flags & PCI_LOOKUP_SKIP_LOCAL)) - continue; - if (n->src == SRC_NET && !(flags & PCI_LOOKUP_NETWORK)) - continue; - if (n->src == SRC_CACHE && !(flags & PCI_LOOKUP_CACHE)) - continue; - if (!best || best->src < n->src) - best = n; - } - if (best) - return best->name; - } - return NULL; -} - -static const char cache_version[] = "#PCI-CACHE-1.0"; - -static int -pci_id_cache_load(struct pci_access *a, int flags) -{ - char *name; - char line[MAX_LINE]; - const char default_name[] = "/.pciids-cache"; - FILE *f; - int lino; - - a->id_cache_status = 1; - if (!a->id_cache_file) - { - /* Construct the default ID cache name */ - uid_t uid = getuid(); - struct passwd *pw = getpwuid(uid); - if (!pw) - return 0; - name = pci_malloc(a, strlen(pw->pw_dir) + sizeof(default_name)); - sprintf(name, "%s%s", pw->pw_dir, default_name); - pci_set_id_cache(a, name, 1); - } - a->debug("Using cache %s\n", a->id_cache_file); - if (flags & PCI_LOOKUP_REFRESH_CACHE) - { - a->debug("Not loading cache, will refresh everything\n"); - a->id_cache_status = 2; - return 0; - } - - f = fopen(a->id_cache_file, "rb"); - if (!f) - { - a->debug("Cache file does not exist\n"); - return 0; - } - /* FIXME: Compare timestamp with the pci.ids file? */ - - lino = 0; - while (fgets(line, sizeof(line), f)) - { - char *p = strchr(line, '\n'); - lino++; - if (p) - { - *p = 0; - if (lino == 1) - { - if (strcmp(line, cache_version)) - { - a->debug("Unrecognized cache version %s, ignoring\n", line); - break; - } - continue; - } - else - { - int cat, id1, id2, id3, id4, cnt; - if (sscanf(line, "%d%x%x%x%x%n", &cat, &id1, &id2, &id3, &id4, &cnt) >= 5) - { - p = line + cnt; - while (*p && *p == ' ') - p++; - id_insert(a, cat, id1, id2, id3, id4, p, SRC_CACHE); - continue; - } - } - } - a->warning("Malformed cache file %s (line %d), ignoring", a->id_cache_file, lino); - break; - } - - if (ferror(f)) - a->warning("Error while reading %s", a->id_cache_file); - fclose(f); - return 1; -} - -static void -pci_id_cache_dirty(struct pci_access *a) -{ - if (a->id_cache_status >= 1) - a->id_cache_status = 2; -} - -void -pci_id_cache_flush(struct pci_access *a) -{ - int orig_status = a->id_cache_status; - FILE *f; - unsigned int h; - struct id_entry *e, *e2; - - a->id_cache_status = 0; - if (orig_status < 2) - return; - if (!a->id_cache_file) - return; - f = fopen(a->id_cache_file, "wb"); - if (!f) - { - a->warning("Cannot write %s: %s", a->id_cache_file, strerror(errno)); - return; - } - a->debug("Writing cache to %s\n", a->id_cache_file); - fprintf(f, "%s\n", cache_version); - - for (h=0; h<HASH_SIZE; h++) - for (e=a->id_hash[h]; e; e=e->next) - if (e->src == SRC_CACHE || e->src == SRC_NET) - { - /* Verify that every entry is written at most once */ - for (e2=a->id_hash[h]; e2 != e; e2=e2->next) - if ((e2->src == SRC_CACHE || e2->src == SRC_NET) && - e2->cat == e->cat && - e2->id12 == e->id12 && e2->id34 == e->id34) - break; - if (e2 == e) - fprintf(f, "%d %x %x %x %x %s\n", - e->cat, - pair_first(e->id12), pair_second(e->id12), - pair_first(e->id34), pair_second(e->id34), - e->name); - } - - fflush(f); - if (ferror(f)) - a->warning("Error writing %s", a->id_cache_file); - fclose(f); -} - -static char *id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4) -{ - char name[256], dnsname[256], txt[256]; - byte answer[4096]; - const byte *data; - int res, i, j, dlen; - ns_msg m; - ns_rr rr; - - switch (cat) - { - case ID_VENDOR: - sprintf(name, "%04x", id1); - break; - case ID_DEVICE: - sprintf(name, "%04x.%04x", id2, id1); - break; - case ID_SUBSYSTEM: - sprintf(name, "%04x.%04x.%04x.%04x", id4, id3, id2, id1); - break; - case ID_GEN_SUBSYSTEM: - sprintf(name, "%04x.%04x.s", id2, id1); - break; - case ID_CLASS: - sprintf(name, "%02x.c", id1); - break; - case ID_SUBCLASS: - sprintf(name, "%02x.%02x.c", id2, id1); - break; - case ID_PROGIF: - sprintf(name, "%02x.%02x.%02x.c", id3, id2, id1); - break; - default: - return NULL; - } - sprintf(dnsname, "%s.%s", name, a->id_domain); - - a->debug("Resolving %s\n", dnsname); - res_init(); - res = res_query(dnsname, ns_c_in, ns_t_txt, answer, sizeof(answer)); - if (res < 0) - { - a->debug("\tfailed, h_errno=%d\n", _res.res_h_errno); - return NULL; - } - if (ns_initparse(answer, res, &m) < 0) - { - a->debug("\tinitparse failed\n"); - return NULL; - } - for (i=0; ns_parserr(&m, ns_s_an, i, &rr) >= 0; i++) - { - a->debug("\tanswer %d (class %d, type %d)\n", i, ns_rr_class(rr), ns_rr_type(rr)); - if (ns_rr_class(rr) != ns_c_in || ns_rr_type(rr) != ns_t_txt) - continue; - data = ns_rr_rdata(rr); - dlen = ns_rr_rdlen(rr); - j = 0; - while (j < dlen && j+1+data[j] <= dlen) - { - memcpy(txt, &data[j+1], data[j]); - txt[data[j]] = 0; - j += 1+data[j]; - a->debug("\t\t%s\n", txt); - if (txt[0] == 'i' && txt[1] == '=') - return strdup(txt+2); - } - } - - return NULL; -} +#include "names.h" static char *id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4) { char *name; - while (!(name = id_lookup_raw(a, flags, cat, id1, id2, id3, id4))) + while (!(name = pci_id_lookup(a, flags, cat, id1, id2, id3, id4))) { if ((flags & PCI_LOOKUP_CACHE) && !a->id_cache_status) { @@ -424,14 +26,14 @@ static char *id_lookup(struct pci_access *a, int flags, int cat, int id1, int id } if (flags & PCI_LOOKUP_NETWORK) { - if (name = id_net_lookup(a, cat, id1, id2, id3, id4)) + if (name = pci_id_net_lookup(a, cat, id1, id2, id3, id4)) { - id_insert(a, cat, id1, id2, id3, id4, name, SRC_NET); - free(name); + pci_id_insert(a, cat, id1, id2, id3, id4, name, SRC_NET); + pci_mfree(name); pci_id_cache_dirty(a); } else - id_insert(a, cat, id1, id2, id3, id4, "", SRC_NET); /* FIXME: Check that negative caching works */ + pci_id_insert(a, cat, id1, id2, id3, id4, "", SRC_NET); /* We want to iterate the lookup to get the allocated ID entry from the hash */ continue; } @@ -440,196 +42,6 @@ static char *id_lookup(struct pci_access *a, int flags, int cat, int id1, int id return (name[0] ? name : NULL); } -static int id_hex(char *p, int cnt) -{ - int x = 0; - while (cnt--) - { - x <<= 4; - if (*p >= '0' && *p <= '9') - x += (*p - '0'); - else if (*p >= 'a' && *p <= 'f') - x += (*p - 'a' + 10); - else if (*p >= 'A' && *p <= 'F') - x += (*p - 'A' + 10); - else - return -1; - p++; - } - return x; -} - -static inline int id_white_p(int c) -{ - return (c == ' ') || (c == '\t'); -} - -static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino) -{ - char line[MAX_LINE]; - char *p; - int id1=0, id2=0, id3=0, id4=0; - int cat = -1; - int nest; - static const char parse_error[] = "Parse error"; - - *lino = 0; - while (pci_gets(f, line, sizeof(line))) - { - (*lino)++; - p = line; - while (*p && *p != '\n' && *p != '\r') - p++; - if (!*p && !pci_eof(f)) - return "Line too long"; - *p = 0; - if (p > line && (p[-1] == ' ' || p[-1] == '\t')) - *--p = 0; - - p = line; - while (id_white_p(*p)) - p++; - if (!*p || *p == '#') - continue; - - p = line; - while (*p == '\t') - p++; - nest = p - line; - - if (!nest) /* Top-level entries */ - { - if (p[0] == 'C' && p[1] == ' ') /* Class block */ - { - if ((id1 = id_hex(p+2, 2)) < 0 || !id_white_p(p[4])) - return parse_error; - cat = ID_CLASS; - p += 5; - } - else if (p[0] == 'S' && p[1] == ' ') - { /* Generic subsystem block */ - if ((id1 = id_hex(p+2, 4)) < 0 || p[6]) - return parse_error; - if (!id_lookup(a, 0, ID_VENDOR, id1, 0, 0, 0)) - return "Vendor does not exist"; - cat = ID_GEN_SUBSYSTEM; - continue; - } - else if (p[0] >= 'A' && p[0] <= 'Z' && p[1] == ' ') - { /* Unrecognized block (RFU) */ - cat = ID_UNKNOWN; - continue; - } - else /* Vendor ID */ - { - if ((id1 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) - return parse_error; - cat = ID_VENDOR; - p += 5; - } - id2 = id3 = id4 = 0; - } - else if (cat == ID_UNKNOWN) /* Nested entries in RFU blocks are skipped */ - continue; - else if (nest == 1) /* Nesting level 1 */ - switch (cat) - { - case ID_VENDOR: - case ID_DEVICE: - case ID_SUBSYSTEM: - if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) - return parse_error; - p += 5; - cat = ID_DEVICE; - id3 = id4 = 0; - break; - case ID_GEN_SUBSYSTEM: - if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) - return parse_error; - p += 5; - id3 = id4 = 0; - break; - case ID_CLASS: - case ID_SUBCLASS: - case ID_PROGIF: - if ((id2 = id_hex(p, 2)) < 0 || !id_white_p(p[2])) - return parse_error; - p += 3; - cat = ID_SUBCLASS; - id3 = id4 = 0; - break; - default: - return parse_error; - } - else if (nest == 2) /* Nesting level 2 */ - switch (cat) - { - case ID_DEVICE: - case ID_SUBSYSTEM: - if ((id3 = id_hex(p, 4)) < 0 || !id_white_p(p[4]) || (id4 = id_hex(p+5, 4)) < 0 || !id_white_p(p[9])) - return parse_error; - p += 10; - cat = ID_SUBSYSTEM; - break; - case ID_CLASS: - case ID_SUBCLASS: - case ID_PROGIF: - if ((id3 = id_hex(p, 2)) < 0 || !id_white_p(p[2])) - return parse_error; - p += 3; - cat = ID_PROGIF; - id4 = 0; - break; - default: - return parse_error; - } - else /* Nesting level 3 or more */ - return parse_error; - while (id_white_p(*p)) - p++; - if (!*p) - return parse_error; - if (id_insert(a, cat, id1, id2, id3, id4, p, SRC_LOCAL)) - return "Duplicate entry"; - } - return NULL; -} - -int -pci_load_name_list(struct pci_access *a) -{ - pci_file f; - int lino; - const char *err; - - pci_free_name_list(a); - a->id_load_failed = 1; - if (!(f = pci_open(a))) - return 0; - err = id_parse_list(a, f, &lino); - PCI_ERROR(f, err); - pci_close(f); - if (err) - a->error("%s at %s, line %d\n", err, a->id_file_name, lino); - a->id_load_failed = 0; - return 1; -} - -void -pci_free_name_list(struct pci_access *a) -{ - pci_id_cache_flush(a); - pci_mfree(a->id_hash); - a->id_hash = NULL; - a->id_cache_status = 0; - while (a->current_id_bucket) - { - struct id_bucket *buck = a->current_id_bucket; - a->current_id_bucket = buck->next; - pci_mfree(buck); - } -} - static char * id_lookup_subsys(struct pci_access *a, int flags, int iv, int id, int isv, int isd) { @@ -676,18 +88,18 @@ format_name_pair(char *buf, int size, int flags, char *v, char *d, char *num) if (v && d) res = snprintf(buf, size, "%s %s [%s]", v, d, num); else if (!v) - res = snprintf(buf, size, "Unknown device [%s]", num); + res = snprintf(buf, size, "Device [%s]", num); else /* v && !d */ - res = snprintf(buf, size, "%s Unknown device [%s]", v, num); + res = snprintf(buf, size, "%s Device [%s]", v, num); } else { if (v && d) res = snprintf(buf, size, "%s %s", v, d); else if (!v) - res = snprintf(buf, size, "Unknown device %s", num); + res = snprintf(buf, size, "Device %s", num); else /* v && !d */ - res = snprintf(buf, size, "%s Unknown device %s", v, num+5); + res = snprintf(buf, size, "%s Device %s", v, num+5); } if (res < 0 || res >= size) return "<pci_lookup_name: buffer too small>"; @@ -724,12 +136,12 @@ pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) case PCI_LOOKUP_VENDOR: iv = va_arg(args, int); sprintf(numbuf, "%04x", iv); - return format_name(buf, size, flags, id_lookup(a, flags, ID_VENDOR, iv, 0, 0, 0), numbuf, "Unknown vendor"); + return format_name(buf, size, flags, id_lookup(a, flags, ID_VENDOR, iv, 0, 0, 0), numbuf, "Vendor"); case PCI_LOOKUP_DEVICE: iv = va_arg(args, int); id = va_arg(args, int); sprintf(numbuf, "%04x", id); - return format_name(buf, size, flags, id_lookup(a, flags, ID_DEVICE, iv, id, 0, 0), numbuf, "Unknown device"); + return format_name(buf, size, flags, id_lookup(a, flags, ID_DEVICE, iv, id, 0, 0), numbuf, "Device"); case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE: iv = va_arg(args, int); id = va_arg(args, int); @@ -748,7 +160,7 @@ pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) isv = va_arg(args, int); isd = va_arg(args, int); sprintf(numbuf, "%04x", isd); - return format_name(buf, size, flags, id_lookup_subsys(a, flags, iv, id, isv, isd), numbuf, "Unknown device"); + return format_name(buf, size, flags, id_lookup_subsys(a, flags, iv, id, isv, isd), numbuf, "Device"); case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM: iv = va_arg(args, int); id = va_arg(args, int); @@ -767,7 +179,7 @@ pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) if (!(flags & PCI_LOOKUP_NUMERIC)) /* Include full class number */ flags |= PCI_LOOKUP_MIXED; } - return format_name(buf, size, flags, cls, numbuf, ((flags & PCI_LOOKUP_MIXED) ? "Unknown class" : "Class")); + return format_name(buf, size, flags, cls, numbuf, "Class"); case PCI_LOOKUP_PROGIF: icls = va_arg(args, int); ipif = va_arg(args, int); @@ -791,27 +203,3 @@ pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) return "<pci_lookup_name: invalid request>"; } } - -void pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed) -{ - if (a->free_id_name) - free(a->id_file_name); - a->id_file_name = name; - a->free_id_name = to_be_freed; -} - -void pci_set_net_domain(struct pci_access *a, char *name, int to_be_freed) -{ - if (a->free_id_domain) - free(a->id_domain); - a->id_domain = name; - a->free_id_domain = to_be_freed; -} - -void pci_set_id_cache(struct pci_access *a, char *name, int to_be_freed) -{ - if (a->free_id_cache_file) - free(a->id_cache_file); - a->id_cache_file = name; - a->free_id_cache_file = to_be_freed; -} diff --git a/lib/names.h b/lib/names.h new file mode 100644 index 0000000..81c373f --- /dev/null +++ b/lib/names.h @@ -0,0 +1,69 @@ +/* + * The PCI Library -- ID to Name Translation + * + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#define MAX_LINE 1024 + +/* names-hash.c */ + +struct id_entry { + struct id_entry *next; + u32 id12, id34; + byte cat; + byte src; + char name[1]; +}; + +enum id_entry_type { + ID_UNKNOWN, + ID_VENDOR, + ID_DEVICE, + ID_SUBSYSTEM, + ID_GEN_SUBSYSTEM, + ID_CLASS, + ID_SUBCLASS, + ID_PROGIF +}; + +enum id_entry_src { + SRC_UNKNOWN, + SRC_CACHE, + SRC_NET, + SRC_LOCAL, +}; + +#define BUCKET_SIZE 8192 +#define HASH_SIZE 4099 + +static inline u32 id_pair(unsigned int x, unsigned int y) +{ + return ((x << 16) | y); +} + +static inline unsigned int pair_first(unsigned int x) +{ + return (x >> 16) & 0xffff; +} + +static inline unsigned int pair_second(unsigned int x) +{ + return x & 0xffff; +} + +int pci_id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src); +char *pci_id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4); + +/* names-cache.c */ + +int pci_id_cache_load(struct pci_access *a, int flags); +void pci_id_cache_dirty(struct pci_access *a); +void pci_id_cache_flush(struct pci_access *a); +void pci_id_hash_free(struct pci_access *a); + +/* names-dns.c */ + +char *pci_id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4); diff --git a/lib/nbsd-libpci.c b/lib/nbsd-libpci.c index 81600a0..2d24c4c 100644 --- a/lib/nbsd-libpci.c +++ b/lib/nbsd-libpci.c @@ -25,13 +25,13 @@ static void nbsd_config(struct pci_access *a) { - a->method_params[PCI_ACCESS_NBSD_LIBPCI] = PCI_PATH_NBSD_DEVICE; + pci_define_param(a, "nbsd.path", PCI_PATH_NBSD_DEVICE, "Path to the NetBSD PCI device"); } static int nbsd_detect(struct pci_access *a) { - char *name = a->method_params[PCI_ACCESS_NBSD_LIBPCI]; + char *name = pci_get_param(a, "nbsd.path"); if (access(name, R_OK)) { @@ -48,7 +48,7 @@ nbsd_detect(struct pci_access *a) static void nbsd_init(struct pci_access *a) { - char *name = a->method_params[PCI_ACCESS_NBSD_LIBPCI]; + char *name = pci_get_param(a, "nbsd.path"); int mode = a->writeable ? O_RDWR : O_RDONLY; a->fd = open(name, mode, 0); @@ -141,7 +141,8 @@ nbsd_write(struct pci_dev *d, int pos, byte *buf, int len) } struct pci_methods pm_nbsd_libpci = { - "NetBSD-libpci", + "nbsd-libpci", + "NetBSD libpci", nbsd_config, nbsd_detect, nbsd_init, diff --git a/lib/obsd-device.c b/lib/obsd-device.c index 721f2f5..62395b3 100644 --- a/lib/obsd-device.c +++ b/lib/obsd-device.c @@ -19,13 +19,13 @@ static void obsd_config(struct pci_access *a) { - a->method_params[PCI_ACCESS_OBSD_DEVICE] = PCI_PATH_OBSD_DEVICE; + pci_define_param(a, "obsd.path", PCI_PATH_OBSD_DEVICE, "Path to the OpenBSD PCI device"); } static int obsd_detect(struct pci_access *a) { - char *name = a->method_params[PCI_ACCESS_OBSD_DEVICE]; + char *name = pci_get_param(a, "obsd.path"); if (access(name, R_OK)) { @@ -39,7 +39,7 @@ obsd_detect(struct pci_access *a) static void obsd_init(struct pci_access *a) { - char *name = a->method_params[PCI_ACCESS_OBSD_DEVICE]; + char *name = pci_get_param(a, "obsd.path"); a->fd = open(name, O_RDWR, 0); if (a->fd < 0) @@ -136,7 +136,8 @@ obsd_write(struct pci_dev *d, int pos, byte *buf, int len) } struct pci_methods pm_obsd_device = { - "OpenBSD-device", + "obsd-device", + "/dev/pci on OpenBSD", obsd_config, obsd_detect, obsd_init, diff --git a/lib/params.c b/lib/params.c new file mode 100644 index 0000000..0e6edbb --- /dev/null +++ b/lib/params.c @@ -0,0 +1,88 @@ +/* + * The PCI Library -- Parameters + * + * Copyright (c) 2008 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "internal.h" + +char * +pci_get_param(struct pci_access *acc, char *param) +{ + struct pci_param *p; + + for (p=acc->params; p; p=p->next) + if (!strcmp(p->param, param)) + return p->value; + return NULL; +} + +void +pci_define_param(struct pci_access *acc, char *param, char *value, char *help) +{ + struct pci_param *p = pci_malloc(acc, sizeof(*p)); + + p->next = acc->params; + acc->params = p; + p->param = param; + p->value = value; + p->value_malloced = 0; + p->help = help; +} + +int +pci_set_param_internal(struct pci_access *acc, char *param, char *value, int copy) +{ + struct pci_param *p; + + for (p=acc->params; p; p=p->next) + if (!strcmp(p->param, param)) + { + if (p->value_malloced) + pci_mfree(p->value); + p->value_malloced = copy; + if (copy) + p->value = pci_strdup(acc, value); + else + p->value = value; + return 0; + } + return -1; +} + +int +pci_set_param(struct pci_access *acc, char *param, char *value) +{ + return pci_set_param_internal(acc, param, value, 1); +} + +void +pci_free_params(struct pci_access *acc) +{ + struct pci_param *p; + + while (p = acc->params) + { + acc->params = p->next; + if (p->value_malloced) + pci_mfree(p->value); + pci_mfree(p); + } +} + +struct pci_param * +pci_walk_params(struct pci_access *acc, struct pci_param *prev) +{ + /* So far, the params form a simple linked list, but this can change in the future */ + if (!prev) + return acc->params; + else + return prev->next; +} + @@ -13,7 +13,11 @@ #include "header.h" #include "types.h" -#define PCI_LIB_VERSION 0x020204 /* FIXME: Update */ +#define PCI_LIB_VERSION 0x029901 + +#ifndef PCI_ABI +#define PCI_ABI +#endif /* * PCI Access Structure @@ -23,23 +27,22 @@ struct pci_methods; enum pci_access_type { /* Known access methods, remember to update access.c as well */ - PCI_ACCESS_AUTO, /* Autodetection (params: none) */ - PCI_ACCESS_SYS_BUS_PCI, /* Linux /sys/bus/pci (params: path) */ - PCI_ACCESS_PROC_BUS_PCI, /* Linux /proc/bus/pci (params: path) */ - PCI_ACCESS_I386_TYPE1, /* i386 ports, type 1 (params: none) */ - PCI_ACCESS_I386_TYPE2, /* i386 ports, type 2 (params: none) */ - PCI_ACCESS_FBSD_DEVICE, /* FreeBSD /dev/pci (params: path) */ + PCI_ACCESS_AUTO, /* Autodetection */ + PCI_ACCESS_SYS_BUS_PCI, /* Linux /sys/bus/pci */ + PCI_ACCESS_PROC_BUS_PCI, /* Linux /proc/bus/pci */ + PCI_ACCESS_I386_TYPE1, /* i386 ports, type 1 */ + PCI_ACCESS_I386_TYPE2, /* i386 ports, type 2 */ + PCI_ACCESS_FBSD_DEVICE, /* FreeBSD /dev/pci */ PCI_ACCESS_AIX_DEVICE, /* /dev/pci0, /dev/bus0, etc. */ PCI_ACCESS_NBSD_LIBPCI, /* NetBSD libpci */ PCI_ACCESS_OBSD_DEVICE, /* OpenBSD /dev/pci */ - PCI_ACCESS_DUMP, /* Dump file (params: filename) */ + PCI_ACCESS_DUMP, /* Dump file */ PCI_ACCESS_MAX }; struct pci_access { /* Options you can change: */ unsigned int method; /* Access method */ - char *method_params[PCI_ACCESS_MAX]; /* Parameters for the methods */ int writeable; /* Open in read/write mode */ int buscentric; /* Bus-centric view of the world */ @@ -48,23 +51,20 @@ struct pci_access { int numeric_ids; /* Enforce PCI_LOOKUP_NUMERIC (>1 => PCI_LOOKUP_MIXED) */ unsigned int id_lookup_mode; /* pci_lookup_mode flags which are set automatically */ - /* Default: PCI_LOOKUP_CACHE */ - char *id_domain; /* DNS domain used for the lookups (use pci_set_net_domain()) */ - int free_id_domain; /* Set if id_domain is malloced */ - char *id_cache_file; /* Name of the ID cache file (use pci_set_net_cache()) */ - int free_id_cache_file; /* Set if id_cache_file is malloced */ + /* Default: PCI_LOOKUP_CACHE */ int debugging; /* Turn on debugging messages */ /* Functions you can override: */ - void (*error)(char *msg, ...); /* Write error message and quit */ - void (*warning)(char *msg, ...); /* Write a warning message */ - void (*debug)(char *msg, ...); /* Write a debugging message */ + void (*error)(char *msg, ...) PCI_PRINTF(1,2); /* Write error message and quit */ + void (*warning)(char *msg, ...) PCI_PRINTF(1,2); /* Write a warning message */ + void (*debug)(char *msg, ...) PCI_PRINTF(1,2); /* Write a debugging message */ struct pci_dev *devices; /* Devices found on this bus */ /* Fields used internally: */ struct pci_methods *methods; + struct pci_param *params; struct id_entry **id_hash; /* names.c */ struct id_bucket *current_id_bucket; int id_load_failed; @@ -76,14 +76,35 @@ struct pci_access { }; /* Initialize PCI access */ -struct pci_access *pci_alloc(void); -void pci_init(struct pci_access *); -void pci_cleanup(struct pci_access *); +struct pci_access *pci_alloc(void) PCI_ABI; +void pci_init(struct pci_access *) PCI_ABI; +void pci_cleanup(struct pci_access *) PCI_ABI; /* Scanning of devices */ -void pci_scan_bus(struct pci_access *acc); -struct pci_dev *pci_get_dev(struct pci_access *acc, int domain, int bus, int dev, int func); /* Raw access to specified device */ -void pci_free_dev(struct pci_dev *); +void pci_scan_bus(struct pci_access *acc) PCI_ABI; +struct pci_dev *pci_get_dev(struct pci_access *acc, int domain, int bus, int dev, int func) PCI_ABI; /* Raw access to specified device */ +void pci_free_dev(struct pci_dev *) PCI_ABI; + +/* Names of access methods */ +int pci_lookup_method(char *name) PCI_ABI; /* Returns -1 if not found */ +char *pci_get_method_name(int index) PCI_ABI; /* Returns "" if unavailable, NULL if index out of range */ + +/* + * Named parameters + */ + +struct pci_param { + struct pci_param *next; /* Please use pci_walk_params() for traversing the list */ + char *param; /* Name of the parameter */ + char *value; /* Value of the parameter */ + int value_malloced; /* used internally */ + char *help; /* Explanation of the parameter */ +}; + +char *pci_get_param(struct pci_access *acc, char *param) PCI_ABI; +int pci_set_param(struct pci_access *acc, char *param, char *value) PCI_ABI; /* 0 on success, -1 if no such parameter */ +/* To traverse the list, call pci_walk_params repeatedly, first with prev=NULL, and do not modify the parameters during traversal. */ +struct pci_param *pci_walk_params(struct pci_access *acc, struct pci_param *prev) PCI_ABI; /* * Devices @@ -116,16 +137,16 @@ struct pci_dev { #define PCI_ADDR_IO_MASK (~(pciaddr_t) 0x3) #define PCI_ADDR_MEM_MASK (~(pciaddr_t) 0xf) -u8 pci_read_byte(struct pci_dev *, int pos); /* Access to configuration space */ -u16 pci_read_word(struct pci_dev *, int pos); -u32 pci_read_long(struct pci_dev *, int pos); -int pci_read_block(struct pci_dev *, int pos, u8 *buf, int len); -int pci_write_byte(struct pci_dev *, int pos, u8 data); -int pci_write_word(struct pci_dev *, int pos, u16 data); -int pci_write_long(struct pci_dev *, int pos, u32 data); -int pci_write_block(struct pci_dev *, int pos, u8 *buf, int len); +u8 pci_read_byte(struct pci_dev *, int pos) PCI_ABI; /* Access to configuration space */ +u16 pci_read_word(struct pci_dev *, int pos) PCI_ABI; +u32 pci_read_long(struct pci_dev *, int pos) PCI_ABI; +int pci_read_block(struct pci_dev *, int pos, u8 *buf, int len) PCI_ABI; +int pci_write_byte(struct pci_dev *, int pos, u8 data) PCI_ABI; +int pci_write_word(struct pci_dev *, int pos, u16 data) PCI_ABI; +int pci_write_long(struct pci_dev *, int pos, u32 data) PCI_ABI; +int pci_write_block(struct pci_dev *, int pos, u8 *buf, int len) PCI_ABI; -int pci_fill_info(struct pci_dev *, int flags); /* Fill in device information */ +int pci_fill_info(struct pci_dev *, int flags) PCI_ABI; /* Fill in device information */ #define PCI_FILL_IDENT 1 #define PCI_FILL_IRQ 2 @@ -135,7 +156,7 @@ int pci_fill_info(struct pci_dev *, int flags); /* Fill in device information */ #define PCI_FILL_CLASS 32 #define PCI_FILL_RESCAN 0x10000 -void pci_setup_cache(struct pci_dev *, u8 *cache, int len); +void pci_setup_cache(struct pci_dev *, u8 *cache, int len) PCI_ABI; /* * Filters @@ -146,10 +167,10 @@ struct pci_filter { int vendor, device; }; -void pci_filter_init(struct pci_access *, struct pci_filter *); -char *pci_filter_parse_slot(struct pci_filter *, char *); -char *pci_filter_parse_id(struct pci_filter *, char *); -int pci_filter_match(struct pci_filter *, struct pci_dev *); +void pci_filter_init(struct pci_access *, struct pci_filter *) PCI_ABI; +char *pci_filter_parse_slot(struct pci_filter *, char *) PCI_ABI; +char *pci_filter_parse_id(struct pci_filter *, char *) PCI_ABI; +int pci_filter_match(struct pci_filter *, struct pci_dev *) PCI_ABI; /* * Conversion of PCI ID's to names (according to the pci.ids file) @@ -167,14 +188,12 @@ int pci_filter_match(struct pci_filter *, struct pci_dev *); * PROGIF (classID, progif) -> programming interface */ -char *pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...); +char *pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) PCI_ABI; -int pci_load_name_list(struct pci_access *a); /* Called automatically by pci_lookup_*() when needed; returns success */ -void pci_free_name_list(struct pci_access *a); /* Called automatically by pci_cleanup() */ -void pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed); -void pci_set_net_domain(struct pci_access *a, char *name, int to_be_freed); -void pci_set_id_cache(struct pci_access *a, char *name, int to_be_freed); -void pci_id_cache_flush(struct pci_access *a); +int pci_load_name_list(struct pci_access *a) PCI_ABI; /* Called automatically by pci_lookup_*() when needed; returns success */ +void pci_free_name_list(struct pci_access *a) PCI_ABI; /* Called automatically by pci_cleanup() */ +void pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed) PCI_ABI; +void pci_id_cache_flush(struct pci_access *a) PCI_ABI; enum pci_lookup_mode { PCI_LOOKUP_VENDOR = 1, /* Vendor name (args: vendorID) */ @@ -21,13 +21,13 @@ static void proc_config(struct pci_access *a) { - a->method_params[PCI_ACCESS_PROC_BUS_PCI] = PCI_PATH_PROC_BUS_PCI; + pci_define_param(a, "proc.path", PCI_PATH_PROC_BUS_PCI, "Path to the procfs bus tree"); } static int proc_detect(struct pci_access *a) { - char *name = a->method_params[PCI_ACCESS_PROC_BUS_PCI]; + char *name = pci_get_param(a, "proc.path"); if (access(name, R_OK)) { @@ -60,7 +60,7 @@ proc_scan(struct pci_access *a) FILE *f; char buf[512]; - if (snprintf(buf, sizeof(buf), "%s/devices", a->method_params[PCI_ACCESS_PROC_BUS_PCI]) == sizeof(buf)) + if (snprintf(buf, sizeof(buf), "%s/devices", pci_get_param(a, "proc.path")) == sizeof(buf)) a->error("File name too long"); f = fopen(buf, "r"); if (!f) @@ -124,7 +124,7 @@ proc_setup(struct pci_dev *d, int rw) if (a->fd >= 0) close(a->fd); e = snprintf(buf, sizeof(buf), "%s/%02x/%02x.%d", - a->method_params[PCI_ACCESS_PROC_BUS_PCI], + pci_get_param(a, "proc.path"), d->bus, d->dev, d->func); if (e < 0 || e >= (int) sizeof(buf)) a->error("File name too long"); @@ -187,7 +187,8 @@ proc_cleanup_dev(struct pci_dev *d) } struct pci_methods pm_linux_proc = { - "Linux-proc", + "linux-proc", + "The proc file system on Linux", proc_config, proc_detect, proc_init, diff --git a/lib/sysfs.c b/lib/sysfs.c index a6cbd57..ea386fa 100644 --- a/lib/sysfs.c +++ b/lib/sysfs.c @@ -2,7 +2,7 @@ * The PCI Library -- Configuration Access via /sys/bus/pci * * Copyright (c) 2003 Matthew Wilcox <willy@fc.hp.com> - * Copyright (c) 1997--2003 Martin Mares <mj@ucw.cz> + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -25,13 +25,13 @@ static void sysfs_config(struct pci_access *a) { - a->method_params[PCI_ACCESS_SYS_BUS_PCI] = PCI_PATH_SYS_BUS_PCI; + pci_define_param(a, "sysfs.path", PCI_PATH_SYS_BUS_PCI, "Path to the sysfs device tree"); } static inline char * sysfs_name(struct pci_access *a) { - return a->method_params[PCI_ACCESS_SYS_BUS_PCI]; + return pci_get_param(a, "sysfs.path"); } static int @@ -250,7 +250,8 @@ static void sysfs_cleanup_dev(struct pci_dev *d) } struct pci_methods pm_linux_sysfs = { - "Linux-sysfs", + "linux-sysfs", + "The sys filesystem on Linux", sysfs_config, sysfs_detect, sysfs_init, diff --git a/lib/types.h b/lib/types.h index f7ab470..3e0e5c3 100644 --- a/lib/types.h +++ b/lib/types.h @@ -1,7 +1,7 @@ /* * The PCI Library -- Types and Format Strings * - * Copyright (c) 1997--2007 Martin Mares <mj@ucw.cz> + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -15,7 +15,7 @@ typedef BYTE u8; typedef WORD u16; typedef DWORD u32; -#elif defined(PCI_HAVE_STDINT_H) +#elif defined(PCI_HAVE_STDINT_H) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) #include <stdint.h> typedef uint8_t u8; typedef uint16_t u16; @@ -57,3 +57,9 @@ typedef u32 pciaddr_t; #else #define PCIIRQ_FMT "%d" #endif + +#ifdef __GNUC__ +#define PCI_PRINTF(x,y) __attribute__((format(printf, x, y))) +#else +#define PCI_PRINTF(x,y) +#endif @@ -12,6 +12,7 @@ #include <stdarg.h> #include <unistd.h> +#define PCIUTILS_LSPCI #include "pciutils.h" /* Options */ @@ -25,35 +26,53 @@ static int opt_machine; /* Generate machine-readable output */ static int opt_map_mode; /* Bus mapping mode enabled */ static int opt_domains; /* Show domain numbers (0=disabled, 1=auto-detected, 2=requested) */ static int opt_kernel; /* Show kernel drivers */ +static int opt_query_dns; /* Query the DNS (0=disabled, 1=enabled, 2=refresh cache) */ +static int opt_query_all; /* Query the DNS for all entries */ static char *opt_pcimap; /* Override path to Linux modules.pcimap */ const char program_name[] = "lspci"; -static char options[] = "nvbxs:d:ti:mgp:kMD" GENERIC_OPTIONS ; - -static char help_msg[] = "\ -Usage: lspci [<switches>]\n\ -\n\ --v\t\tBe verbose\n\ --n\t\tShow numeric ID's\n\ --nn\t\tShow both textual and numeric ID's (names & numbers)\n\ --b\t\tBus-centric view (PCI addresses and IRQ's instead of those seen by the CPU)\n\ --x\t\tShow hex-dump of the standard portion of config space\n\ --xxx\t\tShow hex-dump of the whole config space (dangerous; root only)\n\ --xxxx\t\tShow hex-dump of the 4096-byte extended config space (root only)\n\ --s [[[[<domain>]:]<bus>]:][<slot>][.[<func>]]\tShow only devices in selected slots\n\ --d [<vendor>]:[<device>]\tShow only selected devices\n\ --t\t\tShow bus tree\n\ --m\t\tProduce machine-readable output\n\ --i <file>\tUse specified ID database instead of %s\n" +static char options[] = "nvbxs:d:ti:mgp:qkMDQ" GENERIC_OPTIONS ; + +static char help_msg[] = +"Usage: lspci [<switches>]\n" +"\n" +"Basic display modes:\n" +"-mm\t\tProduce machine-readable output (single -m for an obsolete format)\n" +"-t\t\tShow bus tree\n" +"\n" +"Display options:\n" +"-v\t\tBe verbose (-vv for very verbose)\n" #ifdef PCI_OS_LINUX -"\ --k\t\tShow kernel drivers handling each device\n\ --p <file>\tLook up kernel modules in a given file instead of default modules.pcimap\n" +"-k\t\tShow kernel drivers handling each device\n" #endif -"\ --D\t\tAlways show domain numbers\n\ --M\t\tEnable `bus mapping' mode (dangerous; root only)\n" +"-x\t\tShow hex-dump of the standard part of the config space\n" +"-xxx\t\tShow hex-dump of the whole config space (dangerous; root only)\n" +"-xxxx\t\tShow hex-dump of the 4096-byte extended config space (root only)\n" +"-b\t\tBus-centric view (addresses and IRQ's as seen by the bus)\n" +"-D\t\tAlways show domain numbers\n" +"\n" +"Resolving of device ID's to names:\n" +"-n\t\tShow numeric ID's\n" +"-nn\t\tShow both textual and numeric ID's (names & numbers)\n" +#ifdef PCI_USE_DNS +"-q\t\tQuery the PCI ID database for unknown ID's via DNS\n" +"-qq\t\tAs above, but re-query locally cached entries\n" +"-Q\t\tQuery the PCI ID database for all ID's via DNS\n" +#endif +"\n" +"Selection of devices:\n" +"-s [[[[<domain>]:]<bus>]:][<slot>][.[<func>]]\tShow only devices in selected slots\n" +"-d [<vendor>]:[<device>]\t\t\tShow only devices with specified ID's\n" +"\n" +"Other options:\n" +"-i <file>\tUse specified ID database instead of %s\n" +#ifdef PCI_OS_LINUX +"-p <file>\tLook up kernel modules in a given file instead of default modules.pcimap\n" +#endif +"-M\t\tEnable `bus mapping' mode (dangerous; root only)\n" +"\n" +"PCI access options:\n" GENERIC_HELP ; @@ -1579,13 +1598,16 @@ static char * find_driver(struct device *d, char *buf) { struct pci_dev *dev = d->dev; - char *base = dev->access->method_params[PCI_ACCESS_SYS_BUS_PCI]; - char name[1024], *drv; + char name[1024], *drv, *base; int n; if (dev->access->method != PCI_ACCESS_SYS_BUS_PCI) return NULL; + base = pci_get_param(dev->access, "sysfs.path"); + if (!base || !base[0]) + return NULL; + n = snprintf(name, sizeof(name), "%s/devices/%04x:%02x:%02x.%d/driver", base, dev->domain, dev->bus, dev->dev, dev->func); if (n < 0 || n >= (int)sizeof(name)) @@ -2719,15 +2741,29 @@ main(int argc, char **argv) case 'p': opt_pcimap = optarg; break; +#ifdef PCI_OS_LINUX case 'k': opt_kernel++; break; +#endif case 'M': opt_map_mode++; break; case 'D': opt_domains = 2; break; +#ifdef PCI_USE_DNS + case 'q': + opt_query_dns++; + break; + case 'Q': + opt_query_all = 1; + break; +#else + case 'q': + case 'Q': + die("DNS queries are not available in this version"); +#endif default: if (parse_generic_option(i, pacc, optarg)) break; @@ -2738,8 +2774,15 @@ main(int argc, char **argv) if (optind < argc) goto bad; - /* FIXME */ - pacc->id_lookup_mode |= PCI_LOOKUP_NETWORK; + if (opt_query_dns) + { + pacc->id_lookup_mode |= PCI_LOOKUP_NETWORK; + if (opt_query_dns > 1) + pacc->id_lookup_mode |= PCI_LOOKUP_REFRESH_CACHE; + } + if (opt_query_all) + pacc->id_lookup_mode |= PCI_LOOKUP_NETWORK | PCI_LOOKUP_SKIP_LOCAL; + pci_init(pacc); if (opt_map_mode) map_the_bus(); @@ -7,8 +7,8 @@ lspci \- list all PCI devices .RB [ options ] .SH DESCRIPTION .B lspci -is a utility for displaying information about all PCI buses in the system and -all devices connected to them. +is a utility for displaying information about PCI buses in the system and +devices connected to them. By default, it shows a brief list of devices. Use the options described below to request either a more verbose output or output intended for @@ -19,8 +19,8 @@ If you are going to report bugs in PCI device drivers or in itself, please include output of "lspci -vvx" or even better "lspci -vvxxx" (however, see below for possible caveats). -Some parts of the output, especially in the highly verbose modes, is probably -intelligible only to experienced PCI hackers. For the exact definitions of +Some parts of the output, especially in the highly verbose modes, are probably +intelligible only to experienced PCI hackers. For exact definitions of the fields, please consult either the PCI specifications or the .B header.h and @@ -38,6 +38,22 @@ information with text. .SH OPTIONS + +.SS Basic display modes +.TP +.B -m +Dump PCI device data in a backward-compatible machine readable form. +See below for details. +.TP +.B -mm +Dump PCI device data in a machine readable form for easy parsing by scripts. +See below for details. +.TP +.B -t +Show a tree-like diagram containing all buses, bridges, devices and connections +between them. + +.SS Display options .TP .B -v Be verbose and display detailed information about all devices. @@ -50,12 +66,12 @@ useful. Be even more verbose and display everything we are able to parse, even if it doesn't look interesting at all (e.g., undefined memory regions). .TP -.B -n -Show PCI vendor and device codes as numbers instead of looking them up in the -PCI ID list. -.TP -.B -nn -Show PCI vendor and device codes as both numbers and names. +.B -k +Show kernel drivers handling each device and also kernel modules capable of handling it. +Turned on by default when +.B -v +is given in the normal mode of output. +(Currently works only on Linux with kernel 2.6 or newer.) .TP .B -x Show hexadecimal dump of the standard part of the configuration space (the first @@ -73,20 +89,43 @@ devices are rare, so you needn't worry much. Show hexadecimal dump of the extended (4096-byte) PCI configuration space available on PCI-X 2.0 and PCI Express buses. .TP -.B -k -Show kernel drivers handling each device and also kernel modules capable of handling it. -Turned on by default when -.B -v -is given in the normal mode of output. -(Currently works only on Linux with kernel 2.6 or newer.) -.TP .B -b Bus-centric view. Show all IRQ numbers and addresses as seen by the cards on the PCI bus instead of as seen by the kernel. .TP -.B -t -Show a tree-like diagram containing all buses, bridges, devices and connections -between them. +.B -D +Always show PCI domain numbers. By default, lspci suppresses them on machines which +have only domain 0. + +.SS Options to control resolving ID's to names +.TP +.B -n +Show PCI vendor and device codes as numbers instead of looking them up in the +PCI ID list. +.TP +.B -nn +Show PCI vendor and device codes as both numbers and names. +.TP +.B -q +Use DNS to query the central PCI ID database if a device is not found in the local +.B pci.ids +file. If the DNS query succeeds, the result is cached in +.B ~/.pciids-cache +and it is recognized in subsequent runs even if +.B -q +is not given any more. Please use this switch inside automated scripts only +with caution to avoid overloading the database servers. +.TP +.B -qq +Same as +.BR -q , +but the local cache is reset. +.TP +.B -Q +Query the central database even for entries which are recognized locally. +Use this if you suspect that the displayed entry is wrong. + +.SS Options for selection of devices .TP .B -s [[[[<domain>]:]<bus>]:][<slot>][.[<func>]] Show only devices in the specified domain (in case your machine has several host bridges, @@ -100,6 +139,8 @@ the fourth function of each device. .B -d [<vendor>]:[<device>] Show only devices with specified vendor and device ID. Both ID's are given in hexadecimal and may be omitted or given as "*", both meaning "any value". + +.SS Other options .TP .B -i <file> Use @@ -115,18 +156,6 @@ as the map of PCI ID's handled by kernel modules. By default, lspci uses .RI /lib/modules/ kernel_version /modules.pcimap. Applies only to Linux systems with recent enough module tools. .TP -.B -m -Dump PCI device data in a backward-compatible machine readable form. -See below for details. -.TP -.B -mm -Dump PCI device data in a machine readable form for easy parsing by scripts. -See below for details. -.TP -.B -D -Always show PCI domain numbers. By default, lspci suppresses them on machines which -have only domain 0. -.TP .B -M Invoke bus mapping mode which performs a thorough scan of all PCI devices, including those behind misconfigured bridges etc. This option is available only to root and it @@ -139,73 +168,36 @@ Shows .I lspci version. This option should be used stand-alone. -.SH PCILIB AND ITS OPTIONS -The PCI utilities use PCILIB (a portable library providing platform-independent -functions for PCI configuration space access) to talk to the PCI cards. It supports -the following access methods: - +.SS PCI access options +.PP +The PCI utilities use the PCI library to talk to PCI devices (see +\fBpcilib\fP(7) for details). You can use the following options to +influence its behavior: .TP -.B linux_sysfs -The -.B /sys -filesystem on Linux 2.6 and newer. The standard header of the config space is available -to all users, the rest only to root. Supports extended configuration space, PCI domains -and information on attached kernel drivers. -.TP -.B linux_proc -The -.B /proc/bus/pci -interface supported by Linux 2.1 and newer. The standard header of the config space is available -to all users, the rest only to root. -.TP -.B intel_conf1 -Direct hardware access via Intel configuration mechanism 1. Available on i386 and compatibles -on Linux, Solaris/x86, GNU Hurd and Windows. Requires root privileges. -.TP -.B intel_conf2 -Direct hardware access via Intel configuration mechanism 2. Available on i386 and compatibles -on Linux, Solaris/x86 and GNU Hurd. Requires root privileges. Warning: This method -is able to address only first 16 devices on any bus and it seems to be very -unreliable in many cases. -.TP -.B fbsd_device -The -.B /dev/pci -device on FreeBSD. Requires root privileges. -.TP -.B obsd_device -The -.B /dev/pci -device on OpenBSD. Requires root privileges. -.TP -.B nbsd_libpci -The -.B /dev/pci0 -device on NetBSD accessed using the local libpci library. -.TP -.B aix_device -Access method used on AIX. Requires root privileges. - -.P -By default, PCILIB uses the first available access method and displays no debugging -messages, but you can use the following switches to control its behavior: - +.B -A <method> +The library supports a variety of methods to access the PCI hardware. +By default, it uses the first access method available, but you can use +this option to override this decision. See \fB-A help\fP for a list of +available methods and their descriptions. .TP -.B -P <dir> -Force use of the linux_proc access method, using -.B <dir> -instead of /proc/bus/pci. +.B -O <param>=<value> +The behavior of the library is controlled by several named parameters. +This option allows to set the value of any of the parameters. Use \fB-O help\fP +for a list of known parameters and their default values. .TP .B -H1 Use direct hardware access via Intel configuration mechanism 1. +(This is a shorthand for \fB-A intel-conf1\fP.) .TP .B -H2 Use direct hardware access via Intel configuration mechanism 2. +(This is a shorthand for \fB-A intel-conf2\fP.) .TP .B -F <file> -Extract all information from given file containing output of lspci -x. This is very -useful for analysis of user-supplied bug reports, because you can display the -hardware configuration in any way you want without disturbing the user with +Instead of accessing real hardware, read the list of devices and values of their +configuration registers from the given file produced by an earlier run of lspci -x. +This is very useful for analysis of user-supplied bug reports, because you can display +the hardware configuration in any way you want without disturbing the user with requests for more dumps. .TP .B -G @@ -333,11 +325,8 @@ utility to download the most recent version. .B @IDSDIR@/pci.ids.gz If lspci is compiled with support for compression, this file is tried before pci.ids. .TP -.B /proc/bus/pci -An interface to PCI bus configuration space provided by the post-2.1.82 Linux -kernels. Contains per-bus subdirectories with per-card config space files and a -.I devices -file containing a list of all PCI devices. +.B ~/.pciids-cache +All ID's found in the DNS query mode are cached in this file. .SH BUGS @@ -354,7 +343,8 @@ back-end. .SH SEE ALSO .BR setpci (8), -.BR update-pciids (8) +.BR update-pciids (8), +.BR pcilib (7) .SH AUTHOR The PCI Utilities are maintained by Martin Mares <mj@ucw.cz>. diff --git a/maint/gen-zone b/maint/gen-zone index 57d7982..b3591f8 100755 --- a/maint/gen-zone +++ b/maint/gen-zone @@ -47,6 +47,6 @@ sub esc($) { foreach my $i (keys %ids) { my $j = join(".", reverse split(/[: ]/, $i)); - print "$j\tTXT \"i=", esc($ids{$i}), "\"\n"; - # print "$j\tTXT \"c=", esc($comments{$i}), "\"\n" + print "$j.pci\tTXT \"i=", esc($ids{$i}), "\"\n"; + # print "$j.pci\tTXT \"c=", esc($comments{$i}), "\"\n" } diff --git a/maint/release b/maint/release index 63b6be0..7c4ac51 100755 --- a/maint/release +++ b/maint/release @@ -14,6 +14,9 @@ while (<X>) { } print "API version is $apiver ... <confirm> "; <STDIN>; +print "Updating public GIT tree\n"; +`cg-push public`; die if $?; + my $r = new UCW::Release("pciutils"); my $ver = $r->GetVersionFromFile("Makefile", "VERSION=(.*)"); $r->GetVersionsFromChangelog("ChangeLog", "Released as (.*)\."); diff --git a/pcilib.man b/pcilib.man new file mode 100644 index 0000000..6cf5682 --- /dev/null +++ b/pcilib.man @@ -0,0 +1,109 @@ +.TH pcilib 7 "@TODAY@" "@VERSION@" "The PCI Utilities" +.IX pcilib +.SH NAME +pcilib \- a library for accessing PCI devices + +.SH DESCRIPTION + +The PCI library (also known as \fIpcilib\fP and \fIlibpci\fP) is a portable library +for accessing PCI devices and their configuration space. + +.SH ACCESS METHODS + +.PP +The library supports a variety of methods to access the configuration space +on different operating systems. By default, the first matching method in this +list is used, but you can specify override the decision (see the \fB-A\fP switch +of \fIlspci\fP). + +.TP +.B linux-sysfs +The +.B /sys +filesystem on Linux 2.6 and newer. The standard header of the config space is available +to all users, the rest only to root. Supports extended configuration space, PCI domains +and information on attached kernel drivers. +.TP +.B linux-proc +The +.B /proc/bus/pci +interface supported by Linux 2.1 and newer. The standard header of the config space is available +to all users, the rest only to root. +.TP +.B intel-conf1 +Direct hardware access via Intel configuration mechanism 1. Available on i386 and compatibles +on Linux, Solaris/x86, GNU Hurd and Windows. Requires root privileges. +.TP +.B intel-conf2 +Direct hardware access via Intel configuration mechanism 2. Available on i386 and compatibles +on Linux, Solaris/x86, GNU Hurd and Windows. Requires root privileges. Warning: This method +is able to address only the first 16 devices on any bus and it seems to be very +unreliable in many cases. +.TP +.B fbsd-device +The +.B /dev/pci +device on FreeBSD. Requires root privileges. +.TP +.B aix-device +Access method used on AIX. Requires root privileges. +.TP +.B nbsd-libpci +The +.B /dev/pci0 +device on NetBSD accessed using the local libpci library. +.TP +.B obsd-device +The +.B /dev/pci +device on OpenBSD. Requires root privileges. +.TP +.B dump +Read the contents of configuration registers from a file specified in the +.B dump.name +parameter. The format corresponds to the output of \fIlspci\fP \fB-x\fP. + +.SH PARAMETERS + +.PP +The library is controlled by several parameters. They should have sensible default +values, but in case you want to do something unusual (or even something weird), +you can override them (see the \fB-O\fP switch of \fIlspci\fP). + +.SS Parameters of specific access methods + +.TP +.B dump.name +Name of the bus dump file to read from. +.TP +.B fbsd.path +Path to the FreeBSD PCI device. +.TP +.B nbsd.path +Path to the NetBSD PCI device. +.TP +.B obsd.path +Path to the OpenBSD PCI device. +.TP +.B proc.path +Path to the procfs bus tree. +.TP +.B sysfs.path +Path to the sysfs device tree. + +.SS Parameters for resolving of ID's via DNS +.TP +.B net.domain +DNS domain containing the ID database. +.TP +.B net.cache_name +Name of the file used for caching of resolved ID's. + +.SH SEE ALSO + +.BR lspci (8), +.BR setpci (8), +.BR update-pciids (8) + +.SH AUTHOR +The PCI Utilities are maintained by Martin Mares <mj@ucw.cz>. @@ -1,7 +1,7 @@ /* * The PCI Utilities -- Declarations * - * Copyright (c) 1997--2004 Martin Mares <mj@ucw.cz> + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -17,18 +17,11 @@ extern const char program_name[]; -void die(char *msg, ...) NONRET; +void die(char *msg, ...) NONRET PCI_PRINTF(1,2); void *xmalloc(unsigned int howmuch); void *xrealloc(void *ptr, unsigned int howmuch); int parse_generic_option(int i, struct pci_access *pacc, char *optarg); -#ifdef PCI_HAVE_PM_LINUX_PROC -#define GENOPT_PROC "P:" -#define GENHELP_PROC "-P <dir>\tUse specified directory instead of " PCI_PATH_PROC_BUS_PCI "\n" -#else -#define GENOPT_PROC -#define GENHELP_PROC -#endif #ifdef PCI_HAVE_PM_INTEL_CONF #define GENOPT_INTEL "H:" #define GENHELP_INTEL "-H <mode>\tUse direct hardware access (<mode> = 1 or 2)\n" @@ -36,14 +29,17 @@ int parse_generic_option(int i, struct pci_access *pacc, char *optarg); #define GENOPT_INTEL #define GENHELP_INTEL #endif -#ifdef PCI_HAVE_PM_DUMP +#if defined(PCI_HAVE_PM_DUMP) && !defined(PCIUTILS_SETPCI) #define GENOPT_DUMP "F:" -#define GENHELP_DUMP "-F <file>\tRead configuration data from given file\n" +#define GENHELP_DUMP "-F <file>\tRead PCI configuration dump from a given file\n" #else #define GENOPT_DUMP #define GENHELP_DUMP #endif -#define GENERIC_OPTIONS "G" GENOPT_PROC GENOPT_INTEL GENOPT_DUMP -#define GENERIC_HELP GENHELP_PROC GENHELP_INTEL GENHELP_DUMP \ - "-G\t\tEnable PCI access debugging\n" +#define GENERIC_OPTIONS "A:GO:" GENOPT_INTEL GENOPT_DUMP +#define GENERIC_HELP \ + "-A <method>\tUse the specified PCI access method (see `-A help' for a list)\n" \ + "-O <par>=<val>\tSet PCI access parameter (see `-O help' for a list)\n" \ + "-G\t\tEnable PCI access debugging\n" \ + GENHELP_INTEL GENHELP_DUMP @@ -1,7 +1,7 @@ /* * The PCI Utilities -- Manipulate PCI Configuration Registers * - * Copyright (c) 1998--2006 Martin Mares <mj@ucw.cz> + * Copyright (c) 1998--2008 Martin Mares <mj@ucw.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -12,6 +12,7 @@ #include <stdarg.h> #include <unistd.h> +#define PCIUTILS_SETPCI #include "pciutils.h" static int force; /* Don't complain if no devices match */ @@ -246,7 +247,7 @@ static const struct reg_name pci_reg_names[] = { { 0x00, 0, NULL } }; -static void NONRET +static void NONRET PCI_PRINTF(1,2) usage(char *msg, ...) { va_list args; @@ -258,11 +259,17 @@ usage(char *msg, ...) fprintf(stderr, "\n\n"); } fprintf(stderr, -"Usage: setpci [<options>] (<device>+ <reg>[=<values>]*)*\n\ --f\t\tDon't complain if there's nothing to do\n\ --v\t\tBe verbose\n\ --D\t\tList changes, don't commit them\n" +"Usage: setpci [<options>] (<device>+ <reg>[=<values>]*)*\n" +"\n" +"General options:\n" +"-f\t\tDon't complain if there's nothing to do\n" +"-v\t\tBe verbose\n" +"-D\t\tList changes, don't commit them\n" +"\n" +"PCI access options:\n" GENERIC_HELP +"\n" +"Setting commands:\n" "<device>:\t-s [[[<domain>]:][<bus>]:][<slot>][.[<func>]]\n" "\t|\t-d [<vendor>]:[<device>]\n" "<reg>:\t\t<number>[.(B|W|L)]\n" @@ -22,6 +22,8 @@ Please see for details on access rights. .SH OPTIONS + +.SS General options .TP .B -v Tells @@ -49,6 +51,34 @@ Shows .I setpci version. This option should be used stand-alone. +.SS PCI access options +.PP +The PCI utilities use the PCI library to talk to PCI devices (see +\fBpcilib\fP(7) for details). You can use the following options to +influence its behavior: +.TP +.B -A <method> +The library supports a variety of methods to access the PCI hardware. +By default, it uses the first access method available, but you can use +this option to override this decision. See \fB-A help\fP for a list of +available methods and their descriptions. +.TP +.B -O <param>=<value> +The behavior of the library is controlled by several named parameters. +This option allows to set the value of any of the parameters. Use \fB-O help\fP +for a list of known parameters and their default values. +.TP +.B -H1 +Use direct hardware access via Intel configuration mechanism 1. +(This is a shorthand for \fB-A intel-conf1\fP.) +.TP +.B -H2 +Use direct hardware access via Intel configuration mechanism 2. +(This is a shorthand for \fB-A intel-conf2\fP.) +.TP +.B -G +Increase debug level of the library. + .SH DEVICE SELECTION .PP Before each sequence of operations you need to select which devices you wish that @@ -169,29 +199,9 @@ CB_SUBSYSTEM_VENDOR_ID CB_SUBSYSTEM_ID CB_LEGACY_MODE_BASE -.SH PCILIB OPTIONS -The PCI utilities use PCILIB (a portable library providing platform-independent -functions for PCI configuration space access) to talk to the PCI cards. Please -see -.BR lspci(8) -for a list of switches controlling behavior of the library. - -.SH EXAMPLES -.PP -`setpci -d *:* latency_timer=40' sets the latency timer to 64 (40 hexadecimal). -.PP -`setpci -s 0 device_id vendor_id' lists ID's of devices in slot 0 in all buses. -.PP -`setpci -s 12:3.4 3c.l=1,2,3' writes longword 1 to register 3c, 2 to register 3d -and 3 to register 3e of device at bus 12, slot 3, function 4. -.PP -`setpci -s 13:8.4 40.b=50:d0,04:0c,ff' works on bus 13, device 8, function -4: turns bit 7 off and bits 6 and 4 on in the byte register 40; turns -bit 3 off and bit 2 on in the byte register 41; sets byte register -42 to ff. - .SH SEE ALSO -.BR lspci (8) +.BR lspci (8), +.BR pcilib (7) .SH AUTHOR The PCI Utilities are maintained by Martin Mares <mj@ucw.cz>. |