]> mj.ucw.cz Git - pciutils.git/commitdiff
Rewrote the PCI Utilities. All PCI configuration space access has been
authorMartin Mares <mj@ucw.cz>
Fri, 22 Jan 1999 21:04:45 +0000 (21:04 +0000)
committerMartin Mares <mj@ucw.cz>
Fri, 5 May 2006 12:10:01 +0000 (14:10 +0200)
moved to a library which supports multiple access mechanisms: the current
/proc/bus/pci one, direct port access (needed for debugging of kernel
PCI code and as a nice side-effect this makes pciutils work with 2.0
kernels, although only for root) and reading of configuration dumps.

This has been released as version 1.99.2-alpha. For detailed description
of changes, see the ChangeLog.

Can anybody test it on non-PC architectures, please? (Especially if you
have any 64-bit card.)

26 files changed:
ChangeLog
Makefile
README
common.c [new file with mode: 0644]
filter.c [deleted file]
lib/Makefile [new file with mode: 0644]
lib/access.c [new file with mode: 0644]
lib/buffer.c [new file with mode: 0644]
lib/configure [new file with mode: 0755]
lib/dump.c [new file with mode: 0644]
lib/filter.c [new file with mode: 0644]
lib/generic.c [new file with mode: 0644]
lib/i386-ports.c [new file with mode: 0644]
lib/internal.h [new file with mode: 0644]
lib/names.c [new file with mode: 0644]
lib/pci.h [new file with mode: 0644]
lib/proc.c [new file with mode: 0644]
lib/syscalls.c [new file with mode: 0644]
lspci.8 [deleted file]
lspci.c
lspci.man [new file with mode: 0644]
names.c [deleted file]
pciutils.h
setpci.8 [deleted file]
setpci.c
setpci.man [new file with mode: 0644]

index 785a8bce35315ec75236f58bac60fbf5c72c7acd..3ead38879c9730ae6c76c6ff4227cf3ac6f33100 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,64 @@
+Fri Jan 22 19:29:31 1999  Martin Mares  <mj@albireo.ucw.cz>
+
+       * Version string is now defined in top-level Makefile, exported
+       to the configure script and also substituted to man pages.
+
+       * lspci.c (show_bases): Rewrote displaying of 64-bit addresses.
+       (show_verbose): Rewrote interrupt display logic.
+
+       * lib/i386-ports.c: Include sys/io.h only on glibc systems.
+
+       * lib/configure: Rewrote detection of Linux versions. Now it
+       works on 2.0 kernels (only with direct/dump access, of course).
+
+       * lib/internal.h: New bytesex macros using <asm/byteorder.h>
+       whenever available.
+
+       * lib/proc.c (proc_read, proc_write): Distinguish between short
+       read/write and real errors.
+
+       * lspci.c (show_htype{0,1}): Always use d->dev->rom_base_addr since
+       libpci respects buscentric mode automatically.
+
+       * lspci.c (show_hex_dump): For CardBus bridges, print out 128
+       bytes of header (the whole standard part).
+
+       * common.c: pcilib options are now all uppercase. Also moved
+       PCI access debugging option here.
+
+       * Released as 1.99.2.
+
+Wed Jan 20 22:50:35 1999  Martin Mares  <mj@albireo.ucw.cz>
+
+       * Wrote configure script and rewrote Makefiles.
+
+       * Removed few unused variables.
+
+Wed Jan 20 12:21:56 1999  Martin Mares  <mj@albireo.ucw.cz>
+
+       * common.c: Moved several functions used in both setpci and lspci
+       here. This includes parsing of libpci-related options.
+
+       * More library tweaks.
+
+       * filter.c, names.c: Moved to library.
+
+       * setpci: Rewritten to use the library.
+
+       * Released as 1.99.1.
+
+Tue Jan 19 23:00:12 1999  Martin Mares  <mj@albireo.ucw.cz>
+
+       * lspci.c (scan_devices): For cardbus bridges, read first 128
+       bytes of config space to get full standard header.
+
+       * Makefile (CFLAGS): Removed "-Wno-unused".
+
+       * Started the "New Generation" branch and introduced the
+       PCI library.
+       
+       * lspci: Rewritten to use the library.
+
 Tue Jan 19 22:24:08 1999  Martin Mares  <mj@albireo.ucw.cz>
 
        * Released as version 1.10.
index a7abb06b5a0138e690cac3048d54c1baad0924b3..ad46fffb82e7620438fb33ce2795c1eeba5be508 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,28 +1,40 @@
-# $Id: Makefile,v 1.9 1998/11/22 08:56:00 mj Exp $
+# $Id: Makefile,v 1.10 1999/01/22 21:04:46 mj Exp $
 # Makefile for Linux PCI Utilities
-# (c) 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+# (c) 1998--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
 
-ARCH=$(shell uname -m | sed -e 's/i.86/i386/' -e 's/sun4u/sparc64/' | tr 'a-z' 'A-Z')
-KERN_H=$(shell if [ ! -f pci.h ] ; then echo '-DKERNEL_PCI_H' ; fi)
 OPT=-O2 -fomit-frame-pointer
-CFLAGS=$(OPT) -Wall -W -Wno-parentheses -Wstrict-prototypes -Wno-unused -Werror -DARCH_$(ARCH) $(KERN_H)
+#OPT=-O2 -g
+CFLAGS=$(OPT) -Wall -W -Wno-parentheses -Wstrict-prototypes -Werror
+
+VERSION=1.99.2-alpha
+DATE=22 January 1999
 
 ROOT=/
 PREFIX=/usr
 
-all: lspci setpci
+export
+
+all: lib lspci setpci lspci.8 setpci.8
+
+lib: lib/config.h
+       $(MAKE) -C lib all
 
-lspci: lspci.o names.o filter.o
-setpci: setpci.o filter.o
+lib/config.h:
+       cd lib && ./configure $(PREFIX) $(VERSION)
 
-lspci.o: lspci.c pciutils.h
-names.o: names.c pciutils.h
-filter.o: filter.c pciutils.h
-setpci.o: setpci.c pciutils.h
+lspci: lspci.o common.o lib/libpci.a
+setpci: setpci.o common.o lib/libpci.a
+
+lspci.o: lspci.c pciutils.h lib/libpci.a
+setpci.o: setpci.c pciutils.h lib/libpci.a
+common.o: common.c pciutils.h lib/libpci.a
+
+%.8: %.man
+       sed <$< >$@ "s/@TODAY@/$(DATE)/;s/@VERSION@/pciutils-$(VERSION)/"
 
 clean:
        rm -f `find . -name "*~" -or -name "*.[oa]" -or -name "\#*\#" -or -name TAGS -or -name core`
-       rm -f lspci setpci pci.h
+       rm -f lspci setpci lib/config.* lib/header.h *.8
 
 install: all
        install -o root -g root -m 755 -s lspci setpci $(ROOT)/sbin
@@ -32,6 +44,8 @@ install: all
        rm -f $(ROOT)/etc/pci.ids
 
 dist: clean
-       cp /usr/src/linux/include/linux/pci.h .
+       cp /usr/src/linux/include/linux/pci.h lib/header.h
        sh -c 'X=`pwd` ; X=`basename $$X` ; cd .. ; tar czvvf /tmp/$$X.tar.gz $$X --exclude CVS --exclude tmp'
-       rm -f pci.h
+       rm -f lib/header.h
+
+.PHONY: all lib clean install dist man
diff --git a/README b/README
index d960ba19138c1100686a820e8e8cfac57a92abf8..390d6ff5e7603c311aea83de8cb4da0c48f1cc6e 100644 (file)
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-This package contains the Linux PCI Utilities, version 1.10.
+This package contains the Linux PCI Utilities, version 1.99.2-alpha.
 
 Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
 
@@ -8,6 +8,19 @@ to the terms of the GNU General Public License, either version 2 or
 policy as for the Linux kernel itself -- see /usr/src/linux/COPYING
 for details.
 
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+BIG FAT WARNING: This is an ALPHA version. The documentation is out of date
+                and the same holds for spec files and even for this README.
+                And, of course, you should expect this release to contain BUGS.
+
+WHY ALPHA?      I've split the real PCI access primitives from the rest
+                of the code and created libpci, which supports not only
+                /proc/bus/pci, but also direct hardware access and reading
+                of configuration space dumps and it's intended to work
+                on all Linux versions and even on non-Linux systems, making
+                creation of portable programs communicating with PCI devices
+                possible.
+
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
    The Linux PCI Utilities contain various utilities for dealing with the PCI
@@ -21,7 +34,9 @@ bus in Linux:
 
    See manual pages for more details.
 
-   You _NEED_ kernel 2.1.82 or newer which supports /proc/bus/pci.
+   You need kernel 2.1.82 or newer to use all functions of this package.
+For older kernels, only direct hardware access is supported and you must
+be root to use it.
 
    If you have any bug reports or suggestions, send them to the author.
 
@@ -36,5 +51,5 @@ notes and other news: http://atrey.karlin.mff.cuni.cz/~mj/pciutils.html.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 TODO:
-
-       - Better displaying of IRQ's generated by both PCI and CardBus bridges.
+       - lspci: "scan hard" function
+       - lib: "syscall" access method
diff --git a/common.c b/common.c
new file mode 100644 (file)
index 0000000..b1bceb7
--- /dev/null
+++ b/common.c
@@ -0,0 +1,79 @@
+/*
+ *     $Id: common.c,v 1.1 1999/01/22 21:04:50 mj Exp $
+ *
+ *     Linux PCI Utilities -- Common Functions
+ *
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include "pciutils.h"
+
+void __attribute__((noreturn))
+die(char *msg, ...)
+{
+  va_list args;
+
+  va_start(args, msg);
+  fputs("lspci: ", stderr);
+  vfprintf(stderr, msg, args);
+  fputc('\n', stderr);
+  exit(1);
+}
+
+void *
+xmalloc(unsigned int howmuch)
+{
+  void *p = malloc(howmuch);
+  if (!p)
+    die("Unable to allocate %d bytes of memory", howmuch);
+  return p;
+}
+
+int
+parse_generic_option(int i, struct pci_access *pacc, char *optarg)
+{
+  switch (i)
+    {
+#ifdef 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 HAVE_PM_INTEL_CONF
+    case 'H':
+      if (!strcmp(optarg, "1"))
+       pacc->method = PCI_ACCESS_I386_TYPE1;
+      else if (!strcmp(optarg, "2"))
+       pacc->method = PCI_ACCESS_I386_TYPE2;
+      else
+       die("Unknown hardware configuration type %s", optarg);
+      break;
+#endif
+#ifdef HAVE_PM_SYSCALLS
+    case 'S':
+      pacc->method = PCI_ACCESS_SYSCALLS;
+      break;
+#endif
+#ifdef HAVE_PM_DUMP
+    case 'F':
+      pacc->method_params[PCI_ACCESS_DUMP] = optarg;
+      pacc->method = PCI_ACCESS_DUMP;
+      break;
+#endif
+    case 'G':
+      pacc->debugging++;
+      break;
+    default:
+      return 0;
+    }
+  return 1;
+}
diff --git a/filter.c b/filter.c
deleted file mode 100644 (file)
index e557cdd..0000000
--- a/filter.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- *     $Id: filter.c,v 1.2 1998/06/08 07:51:45 mj Exp $
- *
- *     Linux PCI Utilities -- Device Filtering
- *
- *     Copyright (c) 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
- *
- *     Can be freely distributed and used under the terms of the GNU GPL.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "pciutils.h"
-
-void
-filter_init(struct pci_filter *f)
-{
-  f->bus = f->slot = f->func = -1;
-  f->vendor = f->device = -1;
-}
-
-/* Slot filter syntax: [[bus]:][slot][.[func]] */
-
-char *
-filter_parse_slot(struct pci_filter *f, char *str)
-{
-  char *colon = strchr(str, ':');
-  char *dot = strchr((colon ? colon + 1 : str), '.');
-  char *mid = str;
-  char *e;
-
-  if (colon)
-    {
-      *colon++ = 0;
-      mid = colon;
-      if (str[0] && strcmp(str, "*"))
-       {
-         long int x = strtol(str, &e, 16);
-         if ((e && *e) || (x < 0 || x >= 0xff))
-           return "Invalid bus number";
-         f->bus = x;
-       }
-    }
-  if (dot)
-    *dot++ = 0;
-  if (mid[0] && strcmp(mid, "*"))
-    {
-      long int x = strtol(mid, &e, 16);
-      if ((e && *e) || (x < 0 || x >= 0x1f))
-       return "Invalid slot number";
-      f->slot = x;
-    }
-  if (dot && dot[0] && strcmp(dot, "*"))
-    {
-      long int x = strtol(dot, &e, 16);
-      if ((e && *e) || (x < 0 || x >= 7))
-       return "Invalid function number";
-      f->func = x;
-    }
-  return NULL;
-}
-
-/* ID filter syntax: [vendor]:[device] */
-
-char *
-filter_parse_id(struct pci_filter *f, char *str)
-{
-  char *s, *e;
-
-  if (!*str)
-    return NULL;
-  s = strchr(str, ':');
-  if (!s)
-    return "':' expected";
-  *s++ = 0;
-  if (str[0] && strcmp(str, "*"))
-    {
-      long int x = strtol(str, &e, 16);
-      if ((e && *e) || (x < 0 || x >= 0xffff))
-       return "Invalid vendor ID";
-      f->vendor = x;
-    }
-  if (s[0] && strcmp(s, "*"))
-    {
-      long int x = strtol(s, &e, 16);
-      if ((e && *e) || (x < 0 || x >= 0xffff))
-       return "Invalid device ID";
-      f->device = x;
-    }
-  return NULL;
-}
-
-int
-filter_match(struct pci_filter *f, byte bus, byte devfn, word vendid, word devid)
-{
-  if ((f->bus >= 0 && f->bus != bus) ||
-      (f->slot >= 0 && f->slot != PCI_SLOT(devfn)) ||
-      (f->func >= 0 && f->func != PCI_FUNC(devfn)) ||
-      (f->device >= 0 && f->device != devid) ||
-      (f->vendor >= 0 && f->vendor != vendid))
-    return 0;
-  return 1;
-}
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644 (file)
index 0000000..6138fd9
--- /dev/null
@@ -0,0 +1,44 @@
+# $Id: Makefile,v 1.1 1999/01/22 21:05:10 mj Exp $
+# Makefile for The PCI Library
+# (c) 1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+
+include config.mk
+
+OBJS=access.o generic.o dump.o names.o filter.o buffer.o
+INCL=internal.h pci.h config.h
+
+ifdef HAVE_PM_LINUX_PROC
+OBJS += proc.o
+endif
+
+ifdef HAVE_PM_INTEL_CONF
+OBJS += i386-ports.o
+endif
+
+ifdef HAVE_PM_DUMP
+OBJS += dump.o
+endif
+
+ifdef HAVE_PM_SYSCALLS
+OBJS += syscalls.o
+endif
+
+ifdef HAVE_OWN_HEADER_H
+INCL += header.h
+endif
+
+all: libpci.a
+
+libpci.a: $(OBJS)
+       rm -f $@
+       ar rcs $@ $^
+       ranlib $@
+
+access.o: access.c $(INCL)
+i386-ports.o: i386-ports.c $(INCL)
+proc.o: proc.c $(INCL)
+generic.o: generic.c $(INCL)
+syscalls.o: syscalls.c $(INCL)
+dump.o: dump.c $(INCL)
+names.o: names.c $(INCL)
+filter.o: filter.c $(INCL)
diff --git a/lib/access.c b/lib/access.c
new file mode 100644 (file)
index 0000000..cd7ce12
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ *     $Id: access.c,v 1.1 1999/01/22 21:05:12 mj Exp $
+ *
+ *     The PCI Library -- User Access
+ *
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.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 HAVE_PM_LINUX_PROC
+  &pm_linux_proc,
+#else
+  NULL,
+#endif
+#ifdef HAVE_PM_SYSCALLS
+  &pm_syscalls,
+#else
+  NULL,
+#endif
+#ifdef HAVE_PM_INTEL_CONF
+  &pm_intel_conf1,
+  &pm_intel_conf2,
+#else
+  NULL,
+  NULL,
+#endif
+#ifdef 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;
+
+  bzero(a, sizeof(*a));
+  a->id_file_name = PATH_PCI_IDS;
+  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 * UNUSED msg, ...)
+{
+}
+
+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];
+               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_mfree(a);
+}
+
+void
+pci_scan_bus(struct pci_access *a)
+{
+  a->methods->scan(a);
+}
+
+struct pci_dev *
+pci_alloc_dev(struct pci_access *a)
+{
+  struct pci_dev *d = pci_malloc(a, sizeof(struct pci_dev));
+
+  bzero(d, sizeof(*d));
+  d->access = a;
+  d->methods = a->methods;
+  if (d->methods->init_dev)
+    d->methods->init_dev(d);
+  return d;
+}
+
+int
+pci_link_dev(struct pci_access *a, struct pci_dev *d)
+{
+  d->next = a->devices;
+  a->devices = d;
+
+  return 1;
+}
+
+struct pci_dev *
+pci_get_dev(struct pci_access *a, int bus, int dev, int func)
+{
+  struct pci_dev *d = pci_alloc_dev(a);
+
+  d->bus = bus;
+  d->dev = dev;
+  d->func = func;
+  return d;
+}
+
+void pci_free_dev(struct pci_dev *d)
+{
+  if (d->methods->cleanup_dev)
+    d->methods->cleanup_dev(d);
+  pci_mfree(d);
+}
+
+static inline void
+pci_read_data(struct pci_dev *d, void *buf, int pos, int len)
+{
+  if (pos & (len-1))
+    d->access->error("Unaligned read: pos=%02x, len=%d", pos, len);
+  if (!d->methods->read(d, pos, buf, len))
+    memset(buf, 0xff, len);
+}
+
+byte
+pci_read_byte(struct pci_dev *d, int pos)
+{
+  byte buf;
+  pci_read_data(d, &buf, pos, 1);
+  return buf;
+}
+
+word
+pci_read_word(struct pci_dev *d, int pos)
+{
+  word buf;
+  pci_read_data(d, &buf, pos, 2);
+  return le16_to_cpu(buf);
+}
+
+u32
+pci_read_long(struct pci_dev *d, int pos)
+{
+  u32 buf;
+  pci_read_data(d, &buf, pos, 4);
+  return le32_to_cpu(buf);
+}
+
+int
+pci_read_block(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  return d->methods->read(d, pos, buf, len);
+}
+
+static inline int
+pci_write_data(struct pci_dev *d, void *buf, int pos, int len)
+{
+  if (pos & (len-1))
+    d->access->error("Unaligned write: pos=%02x,len=%d", pos, len);
+  return d->methods->write(d, pos, buf, len);
+}
+
+int
+pci_write_byte(struct pci_dev *d, int pos, byte data)
+{
+  return pci_write_data(d, &data, pos, 1);
+}
+
+int
+pci_write_word(struct pci_dev *d, int pos, word data)
+{
+  word buf = cpu_to_le16(data);
+  return pci_write_data(d, &buf, pos, 2);
+}
+
+int
+pci_write_long(struct pci_dev *d, int pos, u32 data)
+{
+  u32 buf = cpu_to_le32(data);
+  return pci_write_data(d, &buf, pos, 4);
+}
+
+int
+pci_write_block(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  return d->methods->write(d, pos, buf, len);
+}
+
+void
+pci_fill_info(struct pci_dev *d, int flags)
+{
+  if (flags & PCI_FILL_RESCAN)
+    {
+      flags &= ~PCI_FILL_RESCAN;
+      d->known_fields = 0;
+    }
+  if (flags & ~d->known_fields)
+    d->methods->fill_info(d, flags & ~d->known_fields);
+  d->known_fields |= flags;
+}
diff --git a/lib/buffer.c b/lib/buffer.c
new file mode 100644 (file)
index 0000000..9e62802
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *     $Id: buffer.c,v 1.1 1999/01/22 21:05:14 mj Exp $
+ *
+ *     The PCI Library -- Buffered Access
+ *
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.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 int
+buff_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  memcpy(buf, (byte *)d->aux + pos, len);
+  return 1;
+}
+
+static int
+buff_write(struct pci_dev *d, int UNUSED pos, byte * UNUSED buf, int UNUSED len)
+{
+  d->access->error("buffer: Writing to configuration space not supported.");
+  return 0;
+}
+
+static struct pci_methods pm_buffer = {
+  "Buffer",
+  NULL,                                        /* config */
+  NULL,                                        /* Shall not be called */
+  NULL,                                        /* No init nor cleanup */
+  NULL,
+  NULL,                                        /* No scanning */
+  pci_generic_fill_info,
+  buff_read,
+  buff_write,
+  NULL,                                        /* init_dev */
+  NULL                                 /* cleanup_dev */
+};
+
+void
+pci_setup_buffer(struct pci_dev *d, byte *buf)
+{
+  if (d->methods->cleanup_dev)
+    d->methods->cleanup_dev(d);
+  d->methods = &pm_buffer;
+  d->aux = buf;
+}
diff --git a/lib/configure b/lib/configure
new file mode 100755 (executable)
index 0000000..eb72fb1
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+echo -n "Configuring libpci for your system..."
+prefix=${1:-/usr}
+version=${2:-0.0}
+sys=`uname -s`
+rel=`uname -r`
+cpu=`uname -m | sed 's/^i.86$/i386/;s/^sun4u$/sparc64/'`
+echo "$sys/$cpu $rel"
+if [ "$sys" != Linux ] ; then
+       echo "libpci currently supports only Linux"
+       exit 1
+fi
+echo -n "Looking for access methods..."
+c=config.h
+echo >$c "#define ARCH_`echo $cpu | tr 'a-z' 'A-Z'`"
+case $rel in
+       2.[1-9]*|[3-9]*)        echo -n " proc"
+                       echo >>$c '#define HAVE_PM_LINUX_PROC'
+                       echo >>$c '#define HAVE_LINUX_BYTEORDER_H'
+                       echo >>$c '#define PATH_PROC_BUS_PCI "/proc/bus/pci"'
+                       ok=1
+                       ;;
+esac
+case $cpu in
+       i386)           echo -n " i386-ports"
+                       echo >>$c '#define HAVE_PM_INTEL_CONF'
+                       ok=1
+                       ;;
+       sparc)          echo -n " syscalls"
+                       echo >>$c '#define HAVE_PM_SYSCALLS'
+                       ok=1
+                       ;;
+       alpha|sparc64)  echo >>$c '#define HAVE_64BIT_LONG_INT'
+#                      echo -n " syscalls"
+#                      echo >>$c '#define HAVE_PM_SYSCALLS'
+#                      ok=1
+                       ;;
+esac
+echo >>$c '#define HAVE_PM_DUMP'
+echo " dump"
+if [ -z "$ok" ] ; then
+       echo "WARNING: No real configuration access method is available."
+fi
+echo >>$c "#define PATH_PCI_IDS \"$prefix/share/pci.ids\""
+if [ -f header.h ] ; then
+       echo >>$c '#define HAVE_OWN_HEADER_H'
+fi
+echo >>$c "#define PCILIB_VERSION \"$version\""
+sed '/^#define [^ ]*$/!d;s/^#define \(.*\)/\1=1/' <$c >config.mk
diff --git a/lib/dump.c b/lib/dump.c
new file mode 100644 (file)
index 0000000..5aa8323
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ *     $Id: dump.c,v 1.1 1999/01/22 21:05:20 mj Exp $
+ *
+ *     The PCI Library -- Reading of Bus Dumps
+ *
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+
+#include "internal.h"
+
+static int
+dump_detect(struct pci_access *a)
+{
+  return !!a->method_params[PCI_ACCESS_DUMP];
+}
+
+static void
+dump_init(struct pci_access *a)
+{
+  char *name = a->method_params[PCI_ACCESS_DUMP];
+  FILE *f;
+  char buf[256];
+  struct pci_dev *dev = NULL;
+  int len, bn, dn, fn, i, j;
+
+  if (!a)
+    a->error("dump: File name not given.");
+  if (!(f = fopen(name, "r")))
+    a->error("dump: Cannot open %s: %s", name, strerror(errno));
+  while (fgets(buf, sizeof(buf)-1, f))
+    {
+      char *z = strchr(buf, '\n');
+      if (!z)
+       a->error("dump: line too long or unterminated");
+      *z-- = 0;
+      if (z >= buf && *z == '\r')
+       *z-- = 0;
+      len = z - buf + 1;
+      if (len >= 8 && buf[2] == ':' && buf[5] == '.' && buf[7] == ' ' &&
+         sscanf(buf, "%x:%x.%d ", &bn, &dn, &fn) == 3)
+       {
+         dev = pci_get_dev(a, bn, dn, fn);
+         dev->aux = pci_malloc(a, 256);
+         memset(dev->aux, 0xff, 256);
+         pci_link_dev(a, dev);
+       }
+      else if (!len)
+       dev = NULL;
+      else if (dev && len >= 51 && buf[2] == ':' && buf[3] == ' ' &&
+              sscanf(buf, "%x: ", &i) == 1)
+       {
+         z = buf+3;
+         while (isspace(z[0]) && isxdigit(z[1]) && isxdigit(z[2]))
+           {
+             z++;
+             if (sscanf(z, "%x", &j) != 1 || i >= 256)
+               a->error("dump: Malformed line");
+             ((byte *) dev->aux)[i++] = j;
+             z += 2;
+           }
+       }
+    }
+}
+
+static void
+dump_cleanup(struct pci_access * UNUSED a)
+{
+}
+
+static void
+dump_scan(struct pci_access * UNUSED a)
+{
+}
+
+static int
+dump_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  if (!d->aux)
+    {
+      struct pci_dev *e = d->access->devices;
+      while (e && (e->bus != d->bus || e->dev != d->dev || e->func != d->func))
+       e = e->next;
+      if (e)
+       d = e;
+      else
+       return 0;
+    }
+  memcpy(buf, (byte *) d->aux + pos, len);
+  return 1;
+}
+
+static int
+dump_write(struct pci_dev * UNUSED d, int UNUSED pos, byte * UNUSED buf, int UNUSED len)
+{
+  d->access->error("Writing to dump files is not supported.");
+  return 0;
+}
+
+static void
+dump_cleanup_dev(struct pci_dev *d)
+{
+  if (d->aux)
+    {
+      pci_mfree(d->aux);
+      d->aux = NULL;
+    }
+}
+
+struct pci_methods pm_dump = {
+  "dump",
+  NULL,                                        /* config */
+  dump_detect,
+  dump_init,
+  dump_cleanup,
+  dump_scan,
+  pci_generic_fill_info,
+  dump_read,
+  dump_write,
+  NULL,                                        /* init_dev */
+  dump_cleanup_dev
+};
diff --git a/lib/filter.c b/lib/filter.c
new file mode 100644 (file)
index 0000000..299af29
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *     $Id: filter.c,v 1.1 1999/01/22 21:05:22 mj Exp $
+ *
+ *     Linux PCI Library -- Device Filtering
+ *
+ *     Copyright (c) 1998--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "internal.h"
+
+void
+pci_filter_init(struct pci_access * UNUSED a, struct pci_filter *f)
+{
+  f->bus = f->slot = f->func = -1;
+  f->vendor = f->device = -1;
+}
+
+/* Slot filter syntax: [[bus]:][slot][.[func]] */
+
+char *
+pci_filter_parse_slot(struct pci_filter *f, char *str)
+{
+  char *colon = strchr(str, ':');
+  char *dot = strchr((colon ? colon + 1 : str), '.');
+  char *mid = str;
+  char *e;
+
+  if (colon)
+    {
+      *colon++ = 0;
+      mid = colon;
+      if (str[0] && strcmp(str, "*"))
+       {
+         long int x = strtol(str, &e, 16);
+         if ((e && *e) || (x < 0 || x >= 0xff))
+           return "Invalid bus number";
+         f->bus = x;
+       }
+    }
+  if (dot)
+    *dot++ = 0;
+  if (mid[0] && strcmp(mid, "*"))
+    {
+      long int x = strtol(mid, &e, 16);
+      if ((e && *e) || (x < 0 || x >= 0x1f))
+       return "Invalid slot number";
+      f->slot = x;
+    }
+  if (dot && dot[0] && strcmp(dot, "*"))
+    {
+      long int x = strtol(dot, &e, 16);
+      if ((e && *e) || (x < 0 || x >= 7))
+       return "Invalid function number";
+      f->func = x;
+    }
+  return NULL;
+}
+
+/* ID filter syntax: [vendor]:[device] */
+
+char *
+pci_filter_parse_id(struct pci_filter *f, char *str)
+{
+  char *s, *e;
+
+  if (!*str)
+    return NULL;
+  s = strchr(str, ':');
+  if (!s)
+    return "':' expected";
+  *s++ = 0;
+  if (str[0] && strcmp(str, "*"))
+    {
+      long int x = strtol(str, &e, 16);
+      if ((e && *e) || (x < 0 || x >= 0xffff))
+       return "Invalid vendor ID";
+      f->vendor = x;
+    }
+  if (s[0] && strcmp(s, "*"))
+    {
+      long int x = strtol(s, &e, 16);
+      if ((e && *e) || (x < 0 || x >= 0xffff))
+       return "Invalid device ID";
+      f->device = x;
+    }
+  return NULL;
+}
+
+int
+pci_filter_match(struct pci_filter *f, struct pci_dev *d)
+{
+  if ((f->bus >= 0 && f->bus != d->bus) ||
+      (f->slot >= 0 && f->slot != d->dev) ||
+      (f->func >= 0 && f->func != d->func))
+    return 0;
+  if (f->device >= 0 || f->vendor >= 0)
+    {
+      pci_fill_info(d, PCI_FILL_IDENT);
+      if ((f->device >= 0 && f->device != d->device_id) ||
+         (f->vendor >= 0 && f->vendor != d->vendor_id))
+       return 0;
+    }
+  return 1;
+}
diff --git a/lib/generic.c b/lib/generic.c
new file mode 100644 (file)
index 0000000..2ac30bd
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ *     $Id: generic.c,v 1.1 1999/01/22 21:05:24 mj Exp $
+ *
+ *     The PCI Library -- Generic Direct Access Functions
+ *
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <string.h>
+
+#include "internal.h"
+
+static void
+pci_generic_scan_bus(struct pci_access *a, byte *busmap, int bus)
+{
+  int dev, multi, ht;
+  struct pci_dev *t = pci_alloc_dev(a);
+
+  a->debug("Scanning bus %02x for devices...\n", bus);
+  if (busmap[bus])
+    {
+      a->warning("Bus %02x seen twice (firmware bug). Ignored.", bus);
+      return;
+    }
+  busmap[bus] = 1;
+  t->bus = bus;
+  for(dev=0; dev<32; dev++)
+    {
+      t->dev = dev;
+      multi = 0;
+      for(t->func=0; t->func<8; t->func++)
+       {
+         u32 vd = pci_read_long(t, PCI_VENDOR_ID);
+         struct pci_dev *d;
+
+         if (!vd || vd == 0xffffffff)
+           break;
+         ht = pci_read_byte(t, PCI_HEADER_TYPE);
+         if (!t->func)
+           multi = ht & 0x80;
+         ht &= 0x7f;
+         d = pci_alloc_dev(a);
+         d->bus = t->bus;
+         d->dev = t->dev;
+         d->func = t->func;
+         d->vendor_id = vd & 0xffff;
+         d->device_id = vd >> 16U;
+         d->known_fields = PCI_FILL_IDENT;
+         d->hdrtype = ht;
+         pci_link_dev(a, d);
+         switch (ht)
+           {
+           case PCI_HEADER_TYPE_NORMAL:
+             break;
+           case PCI_HEADER_TYPE_BRIDGE:
+           case PCI_HEADER_TYPE_CARDBUS:
+             pci_generic_scan_bus(a, busmap, pci_read_byte(t, PCI_SECONDARY_BUS));
+             break;
+           default:
+             a->debug("Device %02x:%02x.%d has unknown header type %02x.\n", d->bus, d->dev, d->func);
+           }
+         if (!multi)
+           break;
+       }
+    }
+}
+
+void
+pci_generic_scan(struct pci_access *a)
+{
+  byte busmap[256];
+
+  bzero(busmap, sizeof(busmap));
+  pci_generic_scan_bus(a, busmap, 0);
+}
+
+void
+pci_generic_fill_info(struct pci_dev *d, int flags)
+{
+  struct pci_access *a = d->access;
+
+  if (flags & PCI_FILL_IDENT)
+    {
+      d->vendor_id = pci_read_word(d, PCI_VENDOR_ID);
+      d->device_id = pci_read_word(d, PCI_DEVICE_ID);
+    }
+  if (flags & PCI_FILL_IRQ)
+    d->irq = pci_read_byte(d, PCI_INTERRUPT_LINE);
+  if (flags & PCI_FILL_BASES)
+    {
+      int cnt = 0, i;
+      bzero(d->base_addr, sizeof(d->base_addr));
+      switch (d->hdrtype)
+       {
+       case PCI_HEADER_TYPE_NORMAL:
+         cnt = 6;
+         break;
+       case PCI_HEADER_TYPE_BRIDGE:
+         cnt = 2;
+         break;
+       case PCI_HEADER_TYPE_CARDBUS:
+         cnt = 1;
+         break;
+       }
+      if (cnt)
+       {
+         u16 cmd = pci_read_word(d, PCI_COMMAND);
+         for(i=0; i<cnt; i++)
+           {
+             u32 x = pci_read_long(d, PCI_BASE_ADDRESS_0 + i*4);
+             if (!x || x == (u32) ~0)
+               continue;
+             d->base_addr[i] = x;
+             if (x & PCI_BASE_ADDRESS_SPACE_IO)
+               {
+                 if (!(cmd & PCI_COMMAND_IO))
+                   d->base_addr[i] = 0;
+               }
+             else if (cmd & PCI_COMMAND_MEMORY)
+               {
+                 if ((x & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64)
+                   {
+                     if (i >= cnt-1)
+                       a->warning("%02x:%02x.%d: Invalid 64-bit address seen.", d->bus, d->dev, d->func);
+                     else
+                       {
+                         u32 y = pci_read_long(d, PCI_BASE_ADDRESS_0 + (++i)*4);
+#ifdef HAVE_64BIT_LONG_INT
+                         d->base_addr[i-1] |= ((unsigned long) y) << 32;
+#else
+                         if (y)
+                           {
+                             a->warning("%02x:%02x.%d 64-bit device address ignored.", d->bus, d->dev, d->func);
+                             d->base_addr[i-1] = 0;
+                           }
+#endif
+                       }
+                   }
+               }
+             else
+               d->base_addr[i] = 0;
+           }
+       }
+    }
+  if (flags & PCI_FILL_ROM_BASE)
+    {
+      int reg = 0;
+      d->rom_base_addr = 0;
+      switch (d->hdrtype)
+       {
+       case PCI_HEADER_TYPE_NORMAL:
+         reg = PCI_ROM_ADDRESS;
+         break;
+       case PCI_HEADER_TYPE_BRIDGE:
+         reg = PCI_ROM_ADDRESS1;
+         break;
+       }
+      if (reg)
+       {
+         u32 a = pci_read_long(d, reg);
+         if (a & PCI_ROM_ADDRESS_ENABLE)
+           d->rom_base_addr = a;
+       }
+    }
+}
+
+static int
+pci_generic_block_op(struct pci_dev *d, int pos, byte *buf, int len,
+                int (*r)(struct pci_dev *d, int pos, byte *buf, int len))
+{
+  if ((pos & 1) && len >= 1)
+    {
+      if (!r(d, pos, buf, 1))
+       return 0;
+      pos++; buf++; len--;
+    }
+  if ((pos & 3) && len >= 2)
+    {
+      if (!r(d, pos, buf, 2))
+       return 0;
+      pos += 2; buf += 2; len -= 2;
+    }
+  while (len >= 4)
+    {
+      if (!r(d, pos, buf, 4))
+       return 0;
+      pos += 4; buf += 4; len -= 4;
+    }
+  if (len >= 2)
+    {
+      if (!r(d, pos, buf, 2))
+       return 0;
+      pos += 2; buf += 2; len -= 2;
+    }
+  if (len && !r(d, pos, buf, 1))
+    return 0;
+  return 1;
+}
+
+int
+pci_generic_block_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  return pci_generic_block_op(d, pos, buf, len, d->access->methods->read);
+}
+
+int
+pci_generic_block_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  return pci_generic_block_op(d, pos, buf, len, d->access->methods->write);
+}
diff --git a/lib/i386-ports.c b/lib/i386-ports.c
new file mode 100644 (file)
index 0000000..0e1acae
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ *     $Id: i386-ports.c,v 1.1 1999/01/22 21:05:26 mj Exp $
+ *
+ *     The PCI Library -- Direct Configuration access via i386 Ports
+ *
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <asm/io.h>
+#include <unistd.h>
+
+#ifdef __GLIBC__
+#include <sys/io.h>
+#endif
+
+#include "internal.h"
+
+static int intel_iopl_set = -1;
+
+static int
+intel_setup_io(void)
+{
+  if (intel_iopl_set < 0)
+    intel_iopl_set = (iopl(3) < 0) ? 0 : 1;
+  return intel_iopl_set;
+}
+
+static void
+conf12_init(struct pci_access *a)
+{
+  if (!intel_setup_io())
+    a->error("You need to be root to have access to I/O ports.");
+}
+
+static void
+conf12_cleanup(struct pci_access * UNUSED a)
+{
+  iopl(3);
+  intel_iopl_set = -1;
+}
+
+/*
+ * Before we decide to use direct hardware access mechanisms, we try to do some
+ * trivial checks to ensure it at least _seems_ to be working -- we just test
+ * whether bus 00 contains a host bridge (this is similar to checking
+ * techniques used in XFree86, but ours should be more reliable since we
+ * attempt to make use of direct access hints provided by the PCI BIOS).
+ *
+ * This should be close to trivial, but it isn't, because there are buggy
+ * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
+ */
+
+static int
+intel_sanity_check(struct pci_access *a, struct pci_methods *m)
+{
+  struct pci_dev d;
+
+  a->debug("...sanity check");
+  d.bus = 0;
+  d.func = 0;
+  for(d.dev = 0; d.dev < 32; d.dev++)
+    {
+      u16 class, vendor;
+      if (m->read(&d, PCI_CLASS_DEVICE, (byte *) &class, sizeof(class)) &&
+         (class == cpu_to_le16(PCI_CLASS_BRIDGE_HOST) || class == cpu_to_le16(PCI_CLASS_DISPLAY_VGA)) ||
+         m->read(&d, PCI_VENDOR_ID, (byte *) &vendor, sizeof(vendor)) &&
+         (vendor == cpu_to_le16(PCI_VENDOR_ID_INTEL) || vendor == cpu_to_le16(PCI_VENDOR_ID_COMPAQ)))
+       {
+         a->debug("...outside the Asylum at 0/%02x/0", d.dev);
+         return 1;
+       }
+    }
+  a->debug("...insane");
+  return 0;
+}
+
+/*
+ *     Configuration type 1
+ */
+
+#define CONFIG_CMD(bus, device_fn, where)   (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
+
+static int
+conf1_detect(struct pci_access *a)
+{
+  unsigned int tmp;
+  int res = 0;
+
+  if (!intel_setup_io())
+    {
+      a->debug("...no I/O permission");
+      return 0;
+    }
+  outb (0x01, 0xCFB);
+  tmp = inl (0xCF8);
+  outl (0x80000000, 0xCF8);
+  if (inl (0xCF8) == 0x80000000)
+    res = 1;
+  outl (tmp, 0xCF8);
+  if (res)
+    res = intel_sanity_check(a, &pm_intel_conf1);
+  return res;
+}
+
+static int
+conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  int addr = 0xcfc + (pos&3);
+  outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8);
+
+  switch (len)
+    {
+    case 1:
+      buf[0] = inb(addr);
+      break;
+    case 2:
+      ((u16 *) buf)[0] = cpu_to_le16(inw(addr));
+      break;
+    case 4:
+      ((u32 *) buf)[0] = cpu_to_le32(inl(addr));
+      break;
+    default:
+      return pci_generic_block_read(d, pos, buf, len);
+    }
+  return 1;
+}
+
+static int
+conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  int addr = 0xcfc + (pos&3);
+  outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8);
+
+  switch (len)
+    {
+    case 1:
+      outb(buf[0], addr);
+      break;
+    case 2:
+      outw(le16_to_cpu(((u16 *) buf)[0]), addr);
+      break;
+    case 4:
+      outl(le32_to_cpu(((u32 *) buf)[0]), addr);
+      break;
+    default:
+      return pci_generic_block_write(d, pos, buf, len);
+    }
+  return 1;
+}
+
+/*
+ *     Configuration type 2. Obsolete and brain-damaged, but existing.
+ */
+
+static int
+conf2_detect(struct pci_access *a)
+{
+  if (!intel_setup_io())
+    {
+      a->debug("...no I/O permission");
+      return 0;
+    }
+
+  /* This is ugly and tends to produce false positives. Beware. */
+
+  outb(0x00, 0xCFB);
+  outb(0x00, 0xCF8);
+  outb(0x00, 0xCFA);
+  if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00)
+    return intel_sanity_check(a, &pm_intel_conf2);
+  else
+    return 0;
+}
+
+static int
+conf2_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  int addr = 0xc000 | (d->dev << 8) | pos;
+
+  if (d->dev >= 16)
+    /* conf2 supports only 16 devices per bus */
+    return 0;
+  outb((d->func << 1) | 0xf0, 0xcf8);
+  outb(d->bus, 0xcfa);
+  switch (len)
+    {
+    case 1:
+      buf[0] = inb(addr);
+      break;
+    case 2:
+      ((u16 *) buf)[0] = cpu_to_le16(inw(addr));
+      break;
+    case 4:
+      ((u32 *) buf)[0] = cpu_to_le32(inl(addr));
+      break;
+    default:
+      outb(0, 0xcf8);
+      return pci_generic_block_read(d, pos, buf, len);
+    }
+  outb(0, 0xcf8);
+  return 1;
+}
+
+static int
+conf2_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  int addr = 0xc000 | (d->dev << 8) | pos;
+
+  if (d->dev >= 16)
+    d->access->error("conf2_write: only first 16 devices exist.");
+  outb((d->func << 1) | 0xf0, 0xcf8);
+  outb(d->bus, 0xcfa);
+  switch (len)
+    {
+    case 1:
+      outb(buf[0], addr);
+      break;
+    case 2:
+      outw(le16_to_cpu(* (u16 *) buf), addr);
+      break;
+    case 4:
+      outl(le32_to_cpu(* (u32 *) buf), addr);
+      break;
+    default:
+      outb(0, 0xcf8);
+      return pci_generic_block_write(d, pos, buf, len);
+    }
+  outb(0, 0xcf8);
+  return 1;
+}
+
+struct pci_methods pm_intel_conf1 = {
+  "Intel-conf1",
+  NULL,                                        /* config */
+  conf1_detect,
+  conf12_init,
+  conf12_cleanup,
+  pci_generic_scan,
+  pci_generic_fill_info,
+  conf1_read,
+  conf1_write,
+  NULL,                                        /* init_dev */
+  NULL                                 /* cleanup_dev */
+};
+
+struct pci_methods pm_intel_conf2 = {
+  "Intel-conf2",
+  NULL,                                        /* config */
+  conf2_detect,
+  conf12_init,
+  conf12_cleanup,
+  pci_generic_scan,
+  pci_generic_fill_info,
+  conf2_read,
+  conf2_write,
+  NULL,                                        /* init_dev */
+  NULL                                 /* cleanup_dev */
+};
diff --git a/lib/internal.h b/lib/internal.h
new file mode 100644 (file)
index 0000000..bd80ee3
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *     $Id: internal.h,v 1.1 1999/01/22 21:05:29 mj Exp $
+ *
+ *     The PCI Library -- Internal Include File
+ *
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "pci.h"
+
+#ifdef HAVE_PM_LINUX_BYTEORDER_H
+
+#include <asm/byteorder.h>
+#define cpu_to_le16 __cpu_to_le16
+#define cpu_to_le32 __cpu_to_le32
+#define le16_to_cpu __le16_to_cpu
+#define le32_to_cpu __le32_to_cpu
+
+#else
+
+#include <endian.h>
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_le16 swab16
+#define cpu_to_le32 swab32
+#define le16_to_cpu swab16
+#define le32_to_cpu swab32
+
+static inline word swab16(word w)
+{
+  return (w << 8) | ((w >> 8) & 0xff);
+}
+
+static inline u32 swab32(u32 w)
+{
+  return ((w & 0xff000000) >> 24) |
+         ((w & 0x00ff0000) >> 8) |
+         ((w & 0x0000ff00) << 8)  |
+         ((w & 0x000000ff) << 24);
+}
+#else
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#define le16_to_cpu(x) (x)
+#define le32_to_cpu(x) (x)
+#endif
+
+#endif
+
+struct pci_methods {
+  char *name;
+  void (*config)(struct pci_access *);
+  int (*detect)(struct pci_access *);
+  void (*init)(struct pci_access *);
+  void (*cleanup)(struct pci_access *);
+  void (*scan)(struct pci_access *);
+  void (*fill_info)(struct pci_dev *, int flags);
+  int (*read)(struct pci_dev *, int pos, byte *buf, int len);
+  int (*write)(struct pci_dev *, int pos, byte *buf, int len);
+  void (*init_dev)(struct pci_dev *);
+  void (*cleanup_dev)(struct pci_dev *);
+};
+
+void pci_generic_scan(struct pci_access *);
+void pci_generic_fill_info(struct pci_dev *, int flags);
+int pci_generic_block_read(struct pci_dev *, int pos, byte *buf, int len);
+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 *);
+
+struct pci_dev *pci_alloc_dev(struct pci_access *);
+int pci_link_dev(struct pci_access *, struct pci_dev *);
+
+extern struct pci_methods pm_intel_conf1, pm_intel_conf2, pm_linux_proc,
+  pm_syscalls, pm_dump;
+
+#define UNUSED __attribute__((unused))
diff --git a/lib/names.c b/lib/names.c
new file mode 100644 (file)
index 0000000..e18e47f
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ *     $Id: names.c,v 1.1 1999/01/22 21:05:33 mj Exp $
+ *
+ *     The PCI Library -- ID to Name Translation
+ *
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "internal.h"
+
+struct nl_entry {
+  struct nl_entry *next;
+  word id1, id2;
+  int cat;
+  byte *name;
+};
+
+#define NL_VENDOR 0
+#define NL_DEVICE 1
+#define NL_CLASS 2
+#define NL_SUBCLASS 3
+#define NL_SUBSYSTEM_VENDOR 4
+#define NL_SUBSYSTEM_DEVICE 5
+
+#define HASH_SIZE 1024
+
+static inline unsigned int nl_calc_hash(int cat, int id1, int id2)
+{
+  unsigned int h;
+
+  h = id1 ^ id2 ^ (cat << 5);
+  h += (h >> 6);
+  return h & (HASH_SIZE-1);
+}
+
+static struct nl_entry *nl_lookup(struct pci_access *a, int num, int cat, int id1, int id2)
+{
+  unsigned int h;
+  struct nl_entry *n;
+
+  if (num)
+    return NULL;
+  h = nl_calc_hash(cat, id1, id2);
+  n = a->nl_hash[h];
+  while (n && (n->id1 != id1 || n->id2 != id2 || n->cat != cat))
+    n = n->next;
+  return n;
+}
+
+static int nl_add(struct pci_access *a, int cat, int id1, int id2, byte *text)
+{
+  unsigned int h = nl_calc_hash(cat, id1, id2);
+  struct nl_entry *n = a->nl_hash[h];
+
+  while (n && (n->id1 != id1 || n->id2 != id2 || n->cat != cat))
+    n = n->next;
+  if (n)
+    return 1;
+  n = pci_malloc(a, sizeof(struct nl_entry));
+  n->id1 = id1;
+  n->id2 = id2;
+  n->cat = cat;
+  n->name = text;
+  n->next = a->nl_hash[h];
+  a->nl_hash[h] = n;
+  return 0;
+}
+
+static void
+err_name_list(struct pci_access *a, char *msg)
+{
+  a->error("%s: %s: %s\n", a->id_file_name, msg, strerror(errno));
+}
+
+static void
+parse_name_list(struct pci_access *a)
+{
+  byte *p = a->nl_list;
+  byte *q, *r;
+  int lino = 0;
+  unsigned int id1=0, id2=0;
+  int cat, last_cat = -1;
+
+  while (*p)
+    {
+      lino++;
+      q = p;
+      while (*p && *p != '\n')
+       {
+         if (*p == '#')
+           {
+             *p++ = 0;
+             while (*p && *p != '\n')
+               p++;
+             break;
+           }
+         if (*p == '\t')
+           *p = ' ';
+         p++;
+       }
+      if (*p == '\n')
+       *p++ = 0;
+      if (!*q)
+       continue;
+      r = p;
+      while (r > q && r[-1] == ' ')
+       *--r = 0;
+      r = q;
+      while (*q == ' ')
+       q++;
+      if (r == q)
+       {
+         if (q[0] == 'C' && q[1] == ' ')
+           {
+             if (strlen(q+2) < 3 ||
+                 q[4] != ' ' ||
+                 sscanf(q+2, "%x", &id1) != 1)
+               goto parserr;
+             cat = last_cat = NL_CLASS;
+           }
+         else if (q[0] == 'S' && q[1] == ' ')
+           {
+             if (strlen(q+2) < 5 ||
+                 q[6] != ' ' ||
+                 sscanf(q+2, "%x", &id1) != 1)
+               goto parserr;
+             cat = last_cat = NL_SUBSYSTEM_VENDOR;
+             q += 2;
+           }
+         else
+           {
+             if (strlen(q) < 5 ||
+                 q[4] != ' ' ||
+                 sscanf(q, "%x", &id1) != 1)
+               goto parserr;
+             cat = last_cat = NL_VENDOR;
+           }
+         id2 = 0;
+       }
+      else
+       {
+         if (sscanf(q, "%x", &id2) != 1)
+           goto parserr;
+         if (last_cat < 0)
+           goto parserr;
+         if (last_cat == NL_CLASS)
+           cat = NL_SUBCLASS;
+         else
+           cat = last_cat+1;
+       }
+      q += 4;
+      while (*q == ' ')
+       q++;
+      if (!*q)
+       goto parserr;
+      if (nl_add(a, cat, id1, id2, q))
+       a->error("%s, line %d: duplicate entry", a->id_file_name, lino);
+    }
+  return;
+
+parserr:
+  a->error("%s, line %d: parse error", a->id_file_name, lino);
+}
+
+static void
+load_name_list(struct pci_access *a)
+{
+  int fd;
+  struct stat st;
+
+  fd = open(a->id_file_name, O_RDONLY);
+  if (fd < 0)
+    {
+      a->numeric_ids = 1;
+      return;
+    }
+  if (fstat(fd, &st) < 0)
+    err_name_list(a, "stat");
+  a->nl_list = pci_malloc(a, st.st_size + 1);
+  if (read(fd, a->nl_list, st.st_size) != st.st_size)
+    err_name_list(a, "read");
+  a->nl_list[st.st_size] = 0;
+  a->nl_hash = pci_malloc(a, sizeof(struct nl_entry *) * HASH_SIZE);
+  bzero(a->nl_hash, sizeof(struct nl_entry *) * HASH_SIZE);
+  parse_name_list(a);
+  close(fd);
+}
+
+void
+pci_free_name_list(struct pci_access *a)
+{
+  pci_mfree(a->nl_list);
+  a->nl_list = NULL;
+  pci_mfree(a->nl_hash);
+  a->nl_hash = NULL;
+}
+
+static int
+compound_name(struct pci_access *a, int num, char *buf, int size, int cat, int v, int i)
+{
+  if (!num)
+    {
+      struct nl_entry *e, *e2;
+
+      e = nl_lookup(a, 0, cat, v, 0);
+      e2 = nl_lookup(a, 0, cat+1, v, i);
+      if (!e)
+       return snprintf(buf, size, "Unknown device %04x:%04x", v, i);
+      else if (!e2)
+       return snprintf(buf, size, "%s: Unknown device %04x", e->name, i);
+      else
+       return snprintf(buf, size, "%s %s", e->name, e2->name);
+    }
+  else
+    return snprintf(buf, size, "%04x:%04x", v, i);
+}
+
+char *
+pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, u32 arg1, u32 arg2)
+{
+  int num = a->numeric_ids;
+  int res;
+  struct nl_entry *n;
+
+  if (flags & PCI_LOOKUP_NUMERIC)
+    {
+      flags &= PCI_LOOKUP_NUMERIC;
+      num = 1;
+    }
+  if (!a->nl_hash && !num)
+    {
+      load_name_list(a);
+      num = a->numeric_ids;
+    }
+  switch (flags)
+    {
+    case PCI_LOOKUP_VENDOR:
+      if (n = nl_lookup(a, num, NL_VENDOR, arg1, 0))
+       return n->name;
+      else
+       res = snprintf(buf, size, "%04x", arg1);
+      break;
+    case PCI_LOOKUP_DEVICE:
+      if (n = nl_lookup(a, num, NL_DEVICE, arg1, arg2))
+       return n->name;
+      else
+       res = snprintf(buf, size, "%04x", arg1);
+      break;
+    case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE:
+      res = compound_name(a, num, buf, size, NL_VENDOR, arg1, arg2);
+      break;
+    case PCI_LOOKUP_VENDOR | PCI_LOOKUP_SUBSYSTEM:
+      if (n = nl_lookup(a, num, NL_SUBSYSTEM_VENDOR, arg1, 0))
+       return n->name;
+      else
+       res = snprintf(buf, size, "%04x", arg1);
+      break;
+    case PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM:
+      if (n = nl_lookup(a, num, NL_SUBSYSTEM_DEVICE, arg1, arg2))
+       return n->name;
+      else
+       res = snprintf(buf, size, "%04x", arg1);
+      break;
+    case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM:
+      res = compound_name(a, num, buf, size, NL_SUBSYSTEM_VENDOR, arg1, arg2);
+      break;
+    case PCI_LOOKUP_CLASS:
+      if (n = nl_lookup(a, num, NL_SUBCLASS, arg1 >> 8, arg1 & 0xff))
+       return n->name;
+      else if (n = nl_lookup(a, num, NL_CLASS, arg1, 0))
+       res = snprintf(buf, size, "%s [%04x]", n->name, arg1);
+      else
+       res = snprintf(buf, size, "Class %04x", arg1);
+      break;
+    default:
+      return "<pci_lookup_name: invalid request>";
+    }
+  return (res == size) ? "<too-large>" : buf;
+}
diff --git a/lib/pci.h b/lib/pci.h
new file mode 100644 (file)
index 0000000..80ae5a1
--- /dev/null
+++ b/lib/pci.h
@@ -0,0 +1,158 @@
+/*
+ *     $Id: pci.h,v 1.1 1999/01/22 21:05:34 mj Exp $
+ *
+ *     The PCI Library
+ *
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _PCI_LIB_H
+#define _PCI_LIB_H
+
+#include "config.h"
+
+#ifdef HAVE_OWN_HEADER_H
+#include "header.h"
+#else
+#include <linux/pci.h>
+#endif
+
+/*
+ *     Types
+ */
+
+#include <linux/types.h>
+
+typedef __u8 byte;
+typedef __u8 u8;
+typedef __u16 word;
+typedef __u16 u16;
+typedef __u32 u32;
+
+/*
+ *     PCI Access Structure
+ */
+
+struct pci_methods;
+struct nl_entry;
+
+#define PCI_ACCESS_AUTO                        0       /* Autodetection (params: none) */
+#define PCI_ACCESS_PROC_BUS_PCI                1       /* Linux /proc/bus/pci (params: path) */
+#define PCI_ACCESS_SYSCALLS            2       /* pciconfig_read() syscalls (params: none) */
+#define PCI_ACCESS_I386_TYPE1          3       /* i386 ports, type 1 (params: none) */
+#define PCI_ACCESS_I386_TYPE2          4       /* i386 ports, type 2 (params: none) */
+#define PCI_ACCESS_DUMP                        5       /* Dump file (params: filename) */
+#define PCI_ACCESS_MAX                 6
+
+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 */
+  char *id_file_name;                  /* Name of ID list file */
+  int numeric_ids;                     /* Don't resolve device IDs to names */
+  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 */
+
+  struct pci_dev *devices;             /* Devices found on this bus */
+
+  /* Fields used internally: */
+  struct pci_methods *methods;
+  char *nl_list;                       /* Name list cache */
+  struct nl_entry **nl_hash;
+  int fd;                              /* proc: fd */
+  int fd_rw;                           /* proc: fd opened read-write */
+  struct pci_dev *cached_dev;          /* proc: device the fd is for */
+};
+
+/* Initialize PCI access */
+struct pci_access *pci_alloc(void);
+void pci_init(struct pci_access *);
+void pci_cleanup(struct pci_access *);
+
+/* Scanning of devices */
+void pci_scan_bus(struct pci_access *acc);
+struct pci_dev *pci_get_dev(struct pci_access *acc, int bus, int dev, int func); /* Raw access to specified device */
+void pci_free_dev(struct pci_dev *);
+
+/*
+ *     Devices
+ */
+
+struct pci_dev {
+  struct pci_dev *next;                        /* Next device in the chain */
+  word bus;                            /* Higher byte can select host bridges */
+  byte dev, func;                      /* Device and function */
+
+  /* These fields are set by pci_fill_info() */
+  word vendor_id, device_id;           /* Identity of the device */
+  int irq;                             /* IRQ number */
+  unsigned long base_addr[6];          /* Base addresses */
+  unsigned long rom_base_addr;         /* Expansion ROM base address */
+
+  /* Fields used internally: */
+  struct pci_access *access;
+  struct pci_methods *methods;
+  int known_fields;                    /* Set of info fields that is already known */
+  int hdrtype;                         /* Direct methods: header type */
+  void *aux;                           /* Auxillary data */
+};
+
+byte pci_read_byte(struct pci_dev *, int pos); /* Access to configuration space */
+word 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, byte *buf, int len);
+int pci_write_byte(struct pci_dev *, int pos, byte data);
+int pci_write_word(struct pci_dev *, int pos, word data);
+int pci_write_long(struct pci_dev *, int pos, u32 data);
+int pci_write_block(struct pci_dev *, int pos, byte *buf, int len);
+
+void pci_fill_info(struct pci_dev *, int flags); /* Fill in device information */
+
+#define PCI_FILL_IDENT         1
+#define PCI_FILL_IRQ           2
+#define PCI_FILL_BASES         4
+#define PCI_FILL_ROM_BASE      8
+#define PCI_FILL_RESCAN                0x10000
+
+/*
+ *     Buffered access
+ */
+
+void pci_setup_buffer(struct pci_dev *, byte *buf);
+
+/*
+ *     Filters
+ */
+
+struct pci_filter {
+  int bus, slot, func;                 /* -1 = ANY */
+  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 *);
+
+/*
+ *     Device names
+ */
+
+char *pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, u32 arg1, u32 arg2);
+void pci_free_name_list(struct pci_access *a);
+
+#define PCI_LOOKUP_VENDOR 1
+#define PCI_LOOKUP_DEVICE 2
+#define PCI_LOOKUP_CLASS 4
+#define PCI_LOOKUP_SUBSYSTEM 8
+#define PCI_LOOKUP_NUMERIC 0x10000
+
+#endif
diff --git a/lib/proc.c b/lib/proc.c
new file mode 100644 (file)
index 0000000..f5ae883
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ *     $Id: proc.c,v 1.1 1999/01/22 21:05:39 mj Exp $
+ *
+ *     The PCI Library -- Configuration Access via /proc/bus/pci
+ *
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include "internal.h"
+
+#include <asm/unistd.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
+#include <syscall-list.h>
+#endif
+
+/*
+ * As libc doesn't support pread/pwrite yet, we have to call them directly
+ * or use lseek/read/write instead.
+ */
+#if !(defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0)
+
+#if defined(__GLIBC__) && !(defined(__powerpc__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
+#ifndef SYS_pread
+#define SYS_pread __NR_pread
+#endif
+static int
+pread(unsigned int fd, void *buf, size_t size, loff_t where)
+{
+  return syscall(SYS_pread, fd, buf, size, where);
+}
+
+#ifndef SYS_pwrite
+#define SYS_pwrite __NR_pwrite
+#endif
+static int
+pwrite(unsigned int fd, void *buf, size_t size, loff_t where)
+{
+  return syscall(SYS_pwrite, fd, buf, size, where);
+}
+#else
+static _syscall4(int, pread, unsigned int, fd, void *, buf, size_t, size, loff_t, where);
+static _syscall4(int, pwrite, unsigned int, fd, void *, buf, size_t, size, loff_t, where);
+#endif
+
+#endif
+
+static void
+proc_config(struct pci_access *a)
+{
+  a->method_params[PCI_ACCESS_PROC_BUS_PCI] = PATH_PROC_BUS_PCI;
+}
+
+static int
+proc_detect(struct pci_access *a)
+{
+  char *name = a->method_params[PCI_ACCESS_PROC_BUS_PCI];
+
+  if (access(name, R_OK))
+    {
+      a->warning("Cannot open %s", name);
+      return 0;
+    }
+  a->debug("...using %s", name);
+  return 1;
+}
+
+static void
+proc_init(struct pci_access *a)
+{
+  a->fd = -1;
+}
+
+static void
+proc_cleanup(struct pci_access *a)
+{
+  if (a->fd >= 0)
+    {
+      close(a->fd);
+      a->fd = -1;
+    }
+}
+
+static void
+proc_scan(struct pci_access *a)
+{
+  FILE *f;
+  char buf[256];
+
+  if (snprintf(buf, sizeof(buf), "%s/devices", a->method_params[PCI_ACCESS_PROC_BUS_PCI]) == sizeof(buf))
+    a->error("File name too long");
+  f = fopen(buf, "r");
+  if (!f)
+    a->error("Cannot open %s", buf);
+  while (fgets(buf, sizeof(buf)-1, f))
+    {
+      struct pci_dev *d = pci_alloc_dev(a);
+      unsigned int dfn, vend;
+
+      sscanf(buf, "%x %x %x %lx %lx %lx %lx %lx %lx %lx",
+            &dfn,
+            &vend,
+            &d->irq,
+            &d->base_addr[0],
+            &d->base_addr[1],
+            &d->base_addr[2],
+            &d->base_addr[3],
+            &d->base_addr[4],
+            &d->base_addr[5],
+            &d->rom_base_addr);
+      d->bus = dfn >> 8U;
+      d->dev = PCI_SLOT(dfn & 0xff);
+      d->func = PCI_FUNC(dfn & 0xff);
+      d->vendor_id = vend >> 16U;
+      d->device_id = vend & 0xffff;
+      d->known_fields = a->buscentric ? PCI_FILL_IDENT
+                                     : (PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE);
+      pci_link_dev(a, d);
+    }
+  fclose(f);
+}
+
+static int
+proc_setup(struct pci_dev *d, int rw)
+{
+  struct pci_access *a = d->access;
+
+  if (a->cached_dev != d || a->fd_rw < rw)
+    {
+      char buf[256];
+      if (a->fd >= 0)
+       close(a->fd);
+      if (snprintf(buf, sizeof(buf), "%s/%02x/%02x.%d", a->method_params[PCI_ACCESS_PROC_BUS_PCI],
+                  d->bus, d->dev, d->func) == sizeof(buf))
+       a->error("File name too long");
+      a->fd_rw = a->writeable || rw;
+      a->fd = open(buf, a->fd_rw ? O_RDWR : O_RDONLY);
+      if (a->fd < 0)
+       a->warning("Cannot open %s", buf);
+      a->cached_dev = d;
+    }
+  return a->fd;
+}
+
+static int
+proc_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  int fd = proc_setup(d, 0);
+  int res;
+
+  if (fd < 0)
+    return 0;
+  res = pread(fd, buf, len, pos);
+  if (res < 0)
+    {
+      d->access->warning("proc_read: read failed: %s", strerror(errno));
+      return 0;
+    }
+  else if (res != len)
+    {
+      d->access->warning("proc_read: tried to read %d bytes at %d, but got only %d", len, pos, res);
+      return 0;
+    }
+  return 1;
+}
+
+static int
+proc_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  int fd = proc_setup(d, 1);
+  int res;
+
+  if (fd < 0)
+    return 0;
+  res = pwrite(fd, buf, len, pos);
+  if (res < 0)
+    {
+      d->access->warning("proc_write: write failed: %s", strerror(errno));
+      return 0;
+    }
+  else if (res != len)
+    {
+      d->access->warning("proc_write: tried to write %d bytes at %d, but got only %d", len, pos, res);
+      return 0;
+    }
+  return 1;
+}
+
+static void
+proc_cleanup_dev(struct pci_dev *d)
+{
+  if (d->access->cached_dev == d)
+    d->access->cached_dev = NULL;
+}
+
+struct pci_methods pm_linux_proc = {
+  "/proc/bus/pci",
+  proc_config,
+  proc_detect,
+  proc_init,
+  proc_cleanup,
+  proc_scan,
+  pci_generic_fill_info,
+  proc_read,
+  proc_write,
+  NULL,                                        /* init_dev */
+  proc_cleanup_dev
+};
diff --git a/lib/syscalls.c b/lib/syscalls.c
new file mode 100644 (file)
index 0000000..250e622
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *     $Id: syscalls.c,v 1.1 1999/01/22 21:05:42 mj Exp $
+ *
+ *     The PCI Library -- Configuration Access via Syscalls
+ *
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "internal.h"
+
+static int
+sysc_detect(struct pci_access *a)
+{
+  return 0;
+}
+
+static void
+sysc_init(struct pci_access *a)
+{
+}
+
+static void
+sysc_cleanup(struct pci_access *a)
+{
+}
+
+static int
+sysc_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  return 0;
+}
+
+static int
+sysc_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  return 0;
+}
+
+struct pci_methods pm_syscalls = {
+  "syscalls",
+  NULL,                                        /* config */
+  sysc_detect,
+  sysc_init,
+  sysc_cleanup,
+  pci_generic_scan,
+  pci_generic_fill_info,
+  sysc_read,
+  sysc_write,
+  NULL,                                        /* init_dev */
+  NULL                                 /* cleanup_dev */
+};
diff --git a/lspci.8 b/lspci.8
deleted file mode 100644 (file)
index 4b6c2f4..0000000
--- a/lspci.8
+++ /dev/null
@@ -1,98 +0,0 @@
-.TH lspci 8 "19 January 1999" "pciutils-1.10" "Linux PCI Utilities"
-.IX lspci
-.SH NAME
-lspci \- list all PCI devices
-.SH SYNOPSIS
-.B lspci
-.RB [ options ]
-.SH DESCRIPTION
-.B lspci
-is a utility for displaying information about all PCI busses in the system and
-all devices connected to them. It requires Linux kernel 2.1.82 or newer and
-supersedes the original /proc/pci interface found in earlier kernels.
-
-If you are going to report bugs in PCI device drivers or in
-.I lspci
-itself, please include output of "lspci -vvx".
-
-.SH OPTIONS
-.TP
-.B -v
-Tells
-.I lspci
-to be verbose and display detailed information about all devices.
-.TP
-.B -vv
-Tells
-.I lspci
-to be very verbose and display even more information (actually everything the
-PCI device is able to tell). The exact meaning of these data is not explained
-in this manual page, if you want to know more, consult
-.B /usr/include/linux/pci.h
-or the PCI specs.
-.TP
-.B -n
-Show PCI vendor and device codes as numbers instead of looking them up in the
-PCI ID database.
-.TP
-.B -x
-Show hexadecimal dump of first 64 bytes of the PCI configuration space (the standard
-header). Useful for debugging of drivers and
-.I lspci
-itself.
-.TP
-.B -xxx
-Show hexadecimal dump of whole PCI configuration space. Available only for root
-as several PCI devices
-.B crash
-when you try to read undefined portions of the config space (this behaviour probably
-doesn't violate the PCI standard, but it's at least very stupid).
-.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 busses, bridges, devices and connections
-between them.
-.TP
-.B -s [[<bus>]:][<slot>][.[<func>]]
-Show only devices in specified bus, slot and function. Each component of the device
-address can be omitted or set as "*" meaning "any value". All numbers are
-hexadecimal.  E.g., "0:" means all devices on bus 0, "0" means all functions of device 0
-on any bus, "0.3" selects third function of device 0 on all busses and ".4" shows only
-fourth function of each device.
-.TP
-.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 "*" meaning "any value".
-.TP
-.B -i <file>
-Use
-.B
-<file>
-as PCI ID database instead of /usr/share/pci.ids.
-.TP
-.B -p <dir>
-Use
-.B <dir>
-as directory containing PCI bus information instead of /proc/bus/pci.
-.TP
-.B -m
-Dump PCI device data in machine readable form (both normal and verbose format supported)
-for easy parsing by scripts.
-
-.SH FILES
-.TP
-.B /usr/share/pci.ids
-A list of all known PCI ID's (vendors, devices, classes and subclasses).
-.TP
-.B /proc/bus/pci
-An interface to PCI bus configuration space provided by the kernel. Contains
-per-bus subdirectories with per-card config space files and a
-.I
-devices
-file containing a list of all PCI devices.
-
-.SH AUTHOR
-The Linux PCI Utilities are maintained by Martin Mares <mj@atrey.karlin.mff.cuni.cz>.
diff --git a/lspci.c b/lspci.c
index 2480f89ee11bc98222e14fccd7c85bdb8d1ea4ab..2b1c5e11bd1ed57b8a8c0d037a72c2043e845859 100644 (file)
--- a/lspci.c
+++ b/lspci.c
@@ -1,5 +1,5 @@
 /*
- *     $Id: lspci.c,v 1.18 1999/01/19 21:24:38 mj Exp $
+ *     $Id: lspci.c,v 1.19 1999/01/22 21:04:54 mj Exp $
  *
  *     Linux PCI Utilities -- List All PCI Devices
  *
@@ -11,7 +11,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <fcntl.h>
+#include <stdarg.h>
 #include <unistd.h>
 
 #include "pciutils.h"
@@ -24,9 +24,8 @@ static int show_hex;                  /* Show contents of config space as hexadecimal numbers *
 static struct pci_filter filter;       /* Device filter */
 static int show_tree;                  /* Show bus tree */
 static int machine_readable;           /* Generate machine-readable output */
-static char *pci_dir = PROC_BUS_PCI;
 
-static char options[] = "nvbxs:d:ti:p:m";
+static char options[] = "nvbxs:d:ti:mg" GENERIC_OPTIONS ;
 
 static char help_msg[] = "\
 Usage: lspci [<switches>]\n\
@@ -39,11 +38,15 @@ Usage: lspci [<switches>]\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 " ETC_PCI_IDS "\n\
--p <dir>\tUse specified bus directory instead of " PROC_BUS_PCI "\n\
-";
+-i <file>\tUse specified ID database instead of %s\n"
+GENERIC_HELP
+;
 
-/* Format strings used for IRQ numbers */
+/* Communication with libpci */
+
+static struct pci_access *pacc;
+
+/* Format strings used for IRQ numbers and memory addresses */
 
 #ifdef ARCH_SPARC64
 #define IRQ_FORMAT "%08x"
@@ -51,124 +54,54 @@ Usage: lspci [<switches>]\n\
 #define IRQ_FORMAT "%d"
 #endif
 
+#ifdef HAVE_64BIT_LONG_INT
+#define LONG_FORMAT "%016lx"
+#else
+#define LONG_FORMAT "%08lx"
+#endif
+
 /* Our view of the PCI bus */
 
 struct device {
   struct device *next;
-  byte bus, devfn;
-  word vendid, devid;
-  unsigned int kernel_irq;
-  unsigned long kernel_base_addr[6], kernel_rom_base_addr;
+  struct pci_dev *dev;
+  int config_cnt;
   byte config[256];
 };
 
-static struct device *first_dev, **last_dev = &first_dev;
-
-/* Miscellaneous routines */
-
-void *
-xmalloc(unsigned int howmuch)
-{
-  void *p = malloc(howmuch);
-  if (!p)
-    {
-      fprintf(stderr, "lspci: Unable to allocate %d bytes of memory\n", howmuch);
-      exit(1);
-    }
-  return p;
-}
-
-/* Interface for /proc/bus/pci */
-
-static void
-scan_dev_list(void)
-{
-  FILE *f;
-  byte line[256];
-  byte name[256];
-
-  sprintf(name, "%s/devices", pci_dir);
-  if (! (f = fopen(name, "r")))
-    {
-      perror(name);
-      exit(1);
-    }
-  while (fgets(line, sizeof(line), f))
-    {
-      struct device *d = xmalloc(sizeof(struct device));
-      unsigned int dfn, vend;
-
-      bzero(d, sizeof(*d));
-      sscanf(line, "%x %x %x %lx %lx %lx %lx %lx %lx %lx",
-            &dfn,
-            &vend,
-            &d->kernel_irq,
-            &d->kernel_base_addr[0],
-            &d->kernel_base_addr[1],
-            &d->kernel_base_addr[2],
-            &d->kernel_base_addr[3],
-            &d->kernel_base_addr[4],
-            &d->kernel_base_addr[5],
-            &d->kernel_rom_base_addr);
-      d->bus = dfn >> 8U;
-      d->devfn = dfn & 0xff;
-      d->vendid = vend >> 16U;
-      d->devid = vend & 0xffff;
-      if (filter_match(&filter, d->bus, d->devfn, d->vendid, d->devid))
-       {
-         *last_dev = d;
-         last_dev = &d->next;
-         d->next = NULL;
-       }
-    }
-  fclose(f);
-}
-
-static inline void
-make_proc_pci_name(struct device *d, char *p)
-{
-  sprintf(p, "%s/%02x/%02x.%x",
-         pci_dir, d->bus, PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
-}
+static struct device *first_dev;
 
 static void
-scan_config(void)
+scan_devices(void)
 {
   struct device *d;
-  char name[64];
-  int fd, res;
   int how_much = (show_hex > 2) ? 256 : 64;
+  struct pci_dev *p;
 
-  for(d=first_dev; d; d=d->next)
+  pci_scan_bus(pacc);
+  for(p=pacc->devices; p; p=p->next)
     {
-      make_proc_pci_name(d, name);
-      if ((fd = open(name, O_RDONLY)) < 0)
-       {
-         fprintf(stderr, "lspci: Unable to open %s: %m\n", name);
-         exit(1);
-       }
-      res = read(fd, d->config, how_much);
-      if (res < 0)
-       {
-         fprintf(stderr, "lspci: Error reading %s: %m\n", name);
-         exit(1);
-       }
-      if (res != how_much)
+      if (!pci_filter_match(&filter, p))
+       continue;
+      d = xmalloc(sizeof(struct device));
+      d->next = first_dev;
+      first_dev = d;
+      d->dev = p;
+      if (!pci_read_block(p, 0, d->config, how_much))
+       die("Unable to read %d bytes of configuration space.", how_much);
+      if (how_much < 128 && (d->config[PCI_HEADER_TYPE] & 0x7f) == PCI_HEADER_TYPE_CARDBUS)
        {
-         fprintf(stderr, "lspci: Only %d bytes of config space available to you\n", res);
-         exit(1);
+         /* For cardbus bridges, we need to fetch 64 bytes more to get the full standard header... */
+         if (!pci_read_block(p, 0, d->config+64, 64))
+           die("Unable to read cardbus bridge extension data.");
+         how_much = 128;
        }
-      close(fd);
+      d->config_cnt = how_much;
+      pci_setup_buffer(p, d->config);
+      pci_fill_info(p, PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE);
     }
 }
 
-static void
-scan_proc(void)
-{
-  scan_dev_list();
-  scan_config();
-}
-
 /* Config space accesses */
 
 static inline byte
@@ -197,16 +130,20 @@ get_conf_long(struct device *d, unsigned int pos)
 static int
 compare_them(const void *A, const void *B)
 {
-  const struct device *a = *(const struct device **)A;
-  const struct device *b = *(const struct device **)B;
+  const struct pci_dev *a = (*(const struct device **)A)->dev;
+  const struct pci_dev *b = (*(const struct device **)B)->dev;
 
   if (a->bus < b->bus)
     return -1;
   if (a->bus > b->bus)
     return 1;
-  if (a->devfn < b->devfn)
+  if (a->dev < b->dev)
+    return -1;
+  if (a->dev > b->dev)
+    return 1;
+  if (a->func < b->func)
     return -1;
-  if (a->devfn > b->devfn)
+  if (a->func > b->func)
     return 1;
   return 0;
 }
@@ -214,7 +151,7 @@ compare_them(const void *A, const void *B)
 static void
 sort_them(void)
 {
-  struct device **index, **h;
+  struct device **index, **h, **last_dev;
   int cnt;
   struct device *d;
 
@@ -242,13 +179,19 @@ static void
 show_terse(struct device *d)
 {
   int c;
+  struct pci_dev *p = d->dev;
+  byte classbuf[128], devbuf[128];
 
   printf("%02x:%02x.%x %s: %s",
-        d->bus,
-        PCI_SLOT(d->devfn),
-        PCI_FUNC(d->devfn),
-        lookup_class(get_conf_word(d, PCI_CLASS_DEVICE)),
-        lookup_device_full(d->vendid, d->devid));
+        p->bus,
+        p->dev,
+        p->func,
+        pci_lookup_name(pacc, classbuf, sizeof(classbuf),
+                        PCI_LOOKUP_CLASS,
+                        get_conf_word(d, PCI_CLASS_DEVICE), 0),
+        pci_lookup_name(pacc, devbuf, sizeof(devbuf),
+                        PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
+                        p->vendor_id, p->device_id));
   if (c = get_conf_byte(d, PCI_REVISION_ID))
     printf(" (rev %02x)", c);
   if (verbose && (c = get_conf_byte(d, PCI_CLASS_PROG)))
@@ -259,6 +202,7 @@ show_terse(struct device *d)
 static void
 show_bases(struct device *d, int cnt)
 {
+  struct pci_dev *p = d->dev;
   word cmd = get_conf_word(d, PCI_COMMAND);
   int i;
 
@@ -266,26 +210,28 @@ show_bases(struct device *d, int cnt)
     {
       unsigned long pos;
       unsigned int flg = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i);
-      if (buscentric_view)
-       pos = flg;
-      else
-       {
-         pos = d->kernel_base_addr[i];
-         if (!pos)
-           continue;
-       }
-      if (pos == 0xffffffff)
+      pos = p->base_addr[i];
+      if (flg == 0xffffffff)
+       flg = 0;
+      if (!pos && !flg)
        continue;
       if (verbose > 1)
        printf("\tRegion %d: ", i);
       else
        putchar('\t');
+      if (pos && !flg)                 /* Reported by the OS, but not by the device */
+       {
+         printf("[virtual] ");
+         flg = pos;
+       }
       if (flg & PCI_BASE_ADDRESS_SPACE_IO)
        {
          unsigned long a = pos & PCI_BASE_ADDRESS_IO_MASK;
          printf("I/O ports at ");
          if (a)
            printf("%04lx", a);
+         else if (flg & PCI_BASE_ADDRESS_IO_MASK)
+           printf("<ignored>");
          else
            printf("<unassigned>");
          if (!(cmd & PCI_COMMAND_IO))
@@ -295,30 +241,38 @@ show_bases(struct device *d, int cnt)
        {
          int t = flg & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
          unsigned long a = pos & PCI_BASE_ADDRESS_MEM_MASK;
-         int x64 = 0;
+         int done = 0;
+         u32 z = 0;
+
          printf("Memory at ");
          if (t == PCI_BASE_ADDRESS_MEM_TYPE_64)
            {
-             if (i < cnt - 1)
+             if (i >= cnt - 1)
+               {
+                 printf("<invalid-64bit-slot>\n");
+                 done = 1;
+               }
+             else
                {
-                 u32 z;
                  i++;
                  z = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i);
                  if (buscentric_view)
-                   printf("%08x", z);
-                 if (z)
-                   x64 = 1;
+                   {
+                     if (a || z)
+                       printf("%08x%08lx", z, a);
+                     else
+                       printf("<unassigned>");
+                     done = 1;
+                   }
                }
+           }
+         if (!done)
+           {
+             if (a)
+               printf(LONG_FORMAT, a);
              else
-               {
-                 printf("????????");
-                 x64 = 1;
-               }
+               printf(((flg & PCI_BASE_ADDRESS_MEM_MASK) || z) ? "<ignored>" : "<unassigned>");
            }
-         if (x64 || a)
-           printf("%08lx", a);
-         else
-           printf("<unassigned>");
          printf(" (%s, %sprefetchable)",
                 (t == PCI_BASE_ADDRESS_MEM_TYPE_32) ? "32-bit" :
                 (t == PCI_BASE_ADDRESS_MEM_TYPE_64) ? "64-bit" :
@@ -334,10 +288,9 @@ show_bases(struct device *d, int cnt)
 static void
 show_htype0(struct device *d)
 {
-  unsigned long rom = buscentric_view ? get_conf_long(d, PCI_ROM_ADDRESS) : d->kernel_rom_base_addr;
+  unsigned long rom = d->dev->rom_base_addr;
 
   show_bases(d, 6);
-
   if (rom & 1)
     printf("\tExpansion ROM at %08lx%s\n", rom & PCI_ROM_ADDRESS_MASK,
           (rom & PCI_ROM_ADDRESS_ENABLE) ? "" : " [disabled]");
@@ -346,6 +299,7 @@ show_htype0(struct device *d)
 static void
 show_htype1(struct device *d)
 {
+  struct pci_dev *p = d->dev;
   u32 io_base = get_conf_byte(d, PCI_IO_BASE);
   u32 io_limit = get_conf_byte(d, PCI_IO_LIMIT);
   u32 io_type = io_base & PCI_IO_RANGE_TYPE_MASK;
@@ -355,7 +309,7 @@ show_htype1(struct device *d)
   u32 pref_base = get_conf_word(d, PCI_PREF_MEMORY_BASE);
   u32 pref_limit = get_conf_word(d, PCI_PREF_MEMORY_LIMIT);
   u32 pref_type = pref_base & PCI_PREF_RANGE_TYPE_MASK;
-  unsigned long rom = buscentric_view ? get_conf_long(d, PCI_ROM_ADDRESS) : d->kernel_rom_base_addr;
+  unsigned long rom = p->rom_base_addr;
   word brc = get_conf_word(d, PCI_BRIDGE_CONTROL);
 
   show_bases(d, 2);
@@ -487,6 +441,7 @@ show_htype2(struct device *d)
 static void
 show_verbose(struct device *d)
 {
+  struct pci_dev *p = d->dev;
   word status = get_conf_word(d, PCI_STATUS);
   word cmd = get_conf_word(d, PCI_COMMAND);
   word class = get_conf_word(d, PCI_CLASS_DEVICE);
@@ -496,9 +451,9 @@ show_verbose(struct device *d)
   byte cache_line = get_conf_byte(d, PCI_CACHE_LINE_SIZE);
   byte max_lat, min_gnt;
   byte int_pin = get_conf_byte(d, PCI_INTERRUPT_PIN);
-  byte int_line = get_conf_byte(d, PCI_INTERRUPT_LINE);
-  unsigned int irq;
+  unsigned int irq = p->irq;
   word subsys_v, subsys_d;
+  char ssnamebuf[256];
 
   show_terse(d);
 
@@ -519,7 +474,7 @@ show_verbose(struct device *d)
     case PCI_HEADER_TYPE_BRIDGE:
       if (class != PCI_CLASS_BRIDGE_PCI)
        goto badhdr;
-      irq = int_line = int_pin = min_gnt = max_lat = 0;
+      irq = int_pin = min_gnt = max_lat = 0;
       subsys_v = subsys_d = 0;
       break;
     case PCI_HEADER_TYPE_CARDBUS:
@@ -534,13 +489,11 @@ show_verbose(struct device *d)
       return;
     }
 
-  if (buscentric_view)
-    irq = int_line;
-  else
-    irq = d->kernel_irq;
-
   if (verbose && subsys_v && subsys_v != 0xffff)
-    printf("\tSubsystem: %s\n", lookup_subsys_device_full(subsys_v, subsys_d));
+    printf("\tSubsystem: %s\n",
+          pci_lookup_name(pacc, ssnamebuf, sizeof(ssnamebuf),
+                          PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
+                          subsys_v, subsys_d));
 
   if (verbose > 1)
     {
@@ -580,8 +533,9 @@ show_verbose(struct device *d)
            printf(", cache line size %02x", cache_line);
          putchar('\n');
        }
-      if (int_pin)
-       printf("\tInterrupt: pin %c routed to IRQ " IRQ_FORMAT "\n", 'A' + int_pin - 1, irq);
+      if (int_pin || irq)
+       printf("\tInterrupt: pin %c routed to IRQ " IRQ_FORMAT "\n",
+              (int_pin ? 'A' + int_pin - 1 : '?'), irq);
     }
   else
     {
@@ -604,11 +558,8 @@ show_verbose(struct device *d)
             ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_FAST) ? "fast" : "??");
       if (cmd & PCI_COMMAND_MASTER)
        printf(", latency %d", latency);
-      if (int_pin)
-       if (d->kernel_irq)
-         printf(", IRQ " IRQ_FORMAT, irq);
-       else
-         printf(", IRQ ?");
+      if (irq)
+       printf(", IRQ " IRQ_FORMAT, irq);
       putchar('\n');
     }
 
@@ -638,9 +589,8 @@ static void
 show_hex_dump(struct device *d)
 {
   int i;
-  int limit = (show_hex > 2) ? 256 : 64;
 
-  for(i=0; i<limit; i++)
+  for(i=0; i<d->config_cnt; i++)
     {
       if (! (i & 15))
        printf("%02x:", i);
@@ -653,8 +603,10 @@ show_hex_dump(struct device *d)
 static void
 show_machine(struct device *d)
 {
+  struct pci_dev *p = d->dev;
   int c;
   word sv_id=0, sd_id=0;
+  char classbuf[128], vendbuf[128], devbuf[128], svbuf[128], sdbuf[128];
 
   switch (get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f)
     {
@@ -670,14 +622,19 @@ show_machine(struct device *d)
 
   if (verbose)
     {
-      printf("Device:\t%02x:%02x.%x\n", d->bus, PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
-      printf("Class:\t%s\n", lookup_class(get_conf_word(d, PCI_CLASS_DEVICE)));
-      printf("Vendor:\t%s\n", lookup_vendor(d->vendid));
-      printf("Device:\t%s\n", lookup_device(d->vendid, d->devid));
+      printf("Device:\t%02x:%02x.%x\n", p->bus, p->dev, p->func);
+      printf("Class:\t%s\n",
+            pci_lookup_name(pacc, classbuf, sizeof(classbuf), PCI_LOOKUP_CLASS, get_conf_word(d, PCI_CLASS_DEVICE), 0));
+      printf("Vendor:\t%s\n",
+            pci_lookup_name(pacc, vendbuf, sizeof(vendbuf), PCI_LOOKUP_VENDOR, p->vendor_id, p->device_id));
+      printf("Device:\t%s\n",
+            pci_lookup_name(pacc, devbuf, sizeof(devbuf), PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id));
       if (sv_id && sv_id != 0xffff)
        {
-         printf("SVendor:\t%s\n", lookup_subsys_vendor(sv_id));
-         printf("SDevice:\t%s\n", lookup_subsys_device(sv_id, sd_id));
+         printf("SVendor:\t%s\n",
+                pci_lookup_name(pacc, svbuf, sizeof(svbuf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR, sv_id, sd_id));
+         printf("SDevice:\t%s\n",
+                pci_lookup_name(pacc, sdbuf, sizeof(sdbuf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE, sv_id, sd_id));
        }
       if (c = get_conf_byte(d, PCI_REVISION_ID))
        printf("Rev:\t%02x\n", c);
@@ -686,17 +643,22 @@ show_machine(struct device *d)
     }
   else
     {
-      printf("%02x:%02x.%x ", d->bus, PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
+      printf("%02x:%02x.%x ", p->bus, p->dev, p->func);
       printf("\"%s\" \"%s\" \"%s\"",
-            lookup_class(get_conf_word(d, PCI_CLASS_DEVICE)),
-            lookup_vendor(d->vendid),
-            lookup_device(d->vendid, d->devid));
+            pci_lookup_name(pacc, classbuf, sizeof(classbuf), PCI_LOOKUP_CLASS,
+                            get_conf_word(d, PCI_CLASS_DEVICE), 0),
+            pci_lookup_name(pacc, vendbuf, sizeof(vendbuf), PCI_LOOKUP_VENDOR,
+                            p->vendor_id, p->device_id),
+            pci_lookup_name(pacc, devbuf, sizeof(devbuf), PCI_LOOKUP_DEVICE,
+                            p->vendor_id, p->device_id));
       if (c = get_conf_byte(d, PCI_REVISION_ID))
        printf(" -r%02x", c);
       if (c = get_conf_byte(d, PCI_CLASS_PROG))
        printf(" -p%02x", c);
       if (sv_id && sv_id != 0xffff)
-       printf(" \"%s\" \"%s\"", lookup_subsys_vendor(sv_id), lookup_subsys_device(sv_id, sd_id));
+       printf(" \"%s\" \"%s\"",
+              pci_lookup_name(pacc, svbuf, sizeof(svbuf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR, sv_id, sd_id),
+              pci_lookup_name(pacc, sdbuf, sizeof(sdbuf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE, sv_id, sd_id));
       else
        printf(" \"\" \"\"");
       putchar('\n');
@@ -769,15 +731,16 @@ new_bus(struct bridge *b, unsigned int n)
 static void
 insert_dev(struct device *d, struct bridge *b)
 {
+  struct pci_dev *p = d->dev;
   struct bus *bus;
 
-  if (! (bus = find_bus(b, d->bus)))
+  if (! (bus = find_bus(b, p->bus)))
     {
       struct bridge *c;
       for(c=b->child; c; c=c->next)
-       if (c->secondary <= d->bus && d->bus <= c->subordinate)
+       if (c->secondary <= p->bus && p->bus <= c->subordinate)
          return insert_dev(d, c);
-      bus = new_bus(b, d->bus);
+      bus = new_bus(b, p->bus);
     }
   /* Simple insertion at the end _does_ guarantee the correct order as the
    * original device list was sorted by (bus, devfn) lexicographically
@@ -877,9 +840,11 @@ static void show_tree_bridge(struct bridge *, byte *, byte *);
 static void
 show_tree_dev(struct device *d, byte *line, byte *p)
 {
+  struct pci_dev *q = d->dev;
   struct bridge *b;
+  char namebuf[256];
 
-  p += sprintf(p, "%02x.%x", PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
+  p += sprintf(p, "%02x.%x", q->dev, q->func);
   for(b=&host_bridge; b; b=b->chain)
     if (b->br_dev == d)
       {
@@ -891,7 +856,10 @@ show_tree_dev(struct device *d, byte *line, byte *p)
         return;
       }
   if (verbose)
-    p += sprintf(p, "  %s", lookup_device_full(d->vendid, d->devid));
+    p += sprintf(p, "  %s",
+                pci_lookup_name(pacc, namebuf, sizeof(namebuf),
+                                PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
+                                q->vendor_id, q->device_id));
   print_it(line, p);
 }
 
@@ -970,32 +938,31 @@ main(int argc, char **argv)
       puts("lspci version " PCIUTILS_VERSION);
       return 0;
     }
-  filter_init(&filter);
+
+  pacc = pci_alloc();
+  pacc->error = die;
+  pci_filter_init(pacc, &filter);
+
   while ((i = getopt(argc, argv, options)) != -1)
     switch (i)
       {
       case 'n':
-       show_numeric_ids = 1;
+       pacc->numeric_ids = 1;
        break;
       case 'v':
        verbose++;
        break;
       case 'b':
+       pacc->buscentric = 1;
        buscentric_view = 1;
        break;
       case 's':
-       if (msg = filter_parse_slot(&filter, optarg))
-         {
-           fprintf(stderr, "lspci: -f: %s\n", msg);
-           return 1;
-         }
+       if (msg = pci_filter_parse_slot(&filter, optarg))
+         die("-f: %s", msg);
        break;
       case 'd':
-       if (msg = filter_parse_id(&filter, optarg))
-         {
-           fprintf(stderr, "lspci: -d: %s\n", msg);
-           return 1;
-         }
+       if (msg = pci_filter_parse_id(&filter, optarg))
+         die("-d: %s", msg);
        break;
       case 'x':
        show_hex++;
@@ -1004,28 +971,29 @@ main(int argc, char **argv)
        show_tree++;
        break;
       case 'i':
-       pci_ids = optarg;
-       break;
-      case 'p':
-       pci_dir = optarg;
+       pacc->id_file_name = optarg;
        break;
       case 'm':
        machine_readable++;
        break;
       default:
+       if (parse_generic_option(i, pacc, optarg))
+         break;
       bad:
-       fprintf(stderr, help_msg);
+       fprintf(stderr, help_msg, pacc->id_file_name);
        return 1;
       }
   if (optind < argc)
     goto bad;
 
-  scan_proc();
+  pci_init(pacc);
+  scan_devices();
   sort_them();
   if (show_tree)
     show_forest();
   else
     show();
+  pci_cleanup(pacc);
 
   return 0;
 }
diff --git a/lspci.man b/lspci.man
new file mode 100644 (file)
index 0000000..6021d92
--- /dev/null
+++ b/lspci.man
@@ -0,0 +1,138 @@
+.TH lspci 8 "@TODAY@" "@VERSION@" "Linux PCI Utilities"
+.IX lspci
+.SH NAME
+lspci \- list all PCI devices
+.SH SYNOPSIS
+.B lspci
+.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.
+
+To make use of all the features of this program, you need to have Linux kernel
+2.1.82 or newer which supports the /proc/bus/pci interface. With older kernels,
+the PCI utilities have to use direct hardware access which is available
+only to root and it suffers from numerous race conditions and other problems.
+
+If you are going to report bugs in PCI device drivers or in
+.I lspci
+itself, please include output of "lspci -vvx".
+
+.SH OPTIONS
+.TP
+.B -v
+Tells
+.I lspci
+to be verbose and display detailed information about all devices.
+.TP
+.B -vv
+Tells
+.I lspci
+to be very verbose and display even more information (actually everything the
+PCI device is able to tell). The exact meaning of these data is not explained
+in this manual page, if you want to know more, consult
+.B /usr/include/linux/pci.h
+or the PCI specs.
+.TP
+.B -n
+Show PCI vendor and device codes as numbers instead of looking them up in the
+PCI ID database.
+.TP
+.B -x
+Show hexadecimal dump of first 64 bytes of the PCI configuration space (the standard
+header). Useful for debugging of drivers and
+.I lspci
+itself.
+.TP
+.B -xxx
+Show hexadecimal dump of whole PCI configuration space. Available only for root
+as several PCI devices
+.B crash
+when you try to read undefined portions of the config space (this behaviour probably
+doesn't violate the PCI standard, but it's at least very stupid).
+.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.
+.TP
+.B -s [[<bus>]:][<slot>][.[<func>]]
+Show only devices in specified bus, slot and function. Each component of the device
+address can be omitted or set as "*" meaning "any value". All numbers are
+hexadecimal.  E.g., "0:" means all devices on bus 0, "0" means all functions of device 0
+on any bus, "0.3" selects third function of device 0 on all buses and ".4" shows only
+fourth function of each device.
+.TP
+.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 "*" meaning "any value".
+.TP
+.B -i <file>
+Use
+.B
+<file>
+as PCI ID database instead of /usr/share/pci.ids.
+.TP
+.B -p <dir>
+Use
+.B <dir>
+as directory containing PCI bus information instead of /proc/bus/pci.
+.TP
+.B -m
+Dump PCI device data in machine readable form (both normal and verbose format supported)
+for easy parsing by scripts.
+
+.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. The following
+options control parameters of the library, especially what access method it uses.
+By default, PCILIB uses the first available access method and displays no debugging
+messages. Each switch is accompanied by a list of hardware/software configurations
+it's supported in.
+
+.TP
+.B -P <dir>
+Use Linux 2.1 style configuration access to directory
+.B <dir>
+instead of /proc/bus/pci. (Linux 2.1 or newer only)
+.TP
+.B -H1
+Use direct hardware access via Intel configuration mechanism 1. (i386 and compatible only)
+.TP
+.B -H2
+Use direct hardware access via Intel configuration mechanism 2. Warning: This method
+is able to address only first 16 devices on any bus and it seems to be very
+unrealiable in many cases. (i386 and compatible only)
+.TP
+.B -S
+Use PCI access syscalls. (Linux on Alpha and UltraSparc only)
+.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
+requests for more dumps. (All systems)
+.TP
+.B -G
+Increase debug level of the library. (All systems)
+
+.SH FILES
+.TP
+.B /usr/share/pci.ids
+A list of all known PCI ID's (vendors, devices, classes and subclasses).
+.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.
+
+.SH SEE ALSO
+.BR setpci (8)
+
+.SH AUTHOR
+The Linux PCI Utilities are maintained by Martin Mares <mj@atrey.karlin.mff.cuni.cz>.
diff --git a/names.c b/names.c
deleted file mode 100644 (file)
index 5067dec..0000000
--- a/names.c
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- *     $Id: names.c,v 1.6 1998/07/17 08:57:16 mj Exp $
- *
- *     Linux PCI Utilities -- Device ID to Name Translation
- *
- *     Copyright (c) 1997, 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
- *
- *     Can be freely distributed and used under the terms of the GNU GPL.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
-
-#include "pciutils.h"
-
-int show_numeric_ids;
-
-char *pci_ids = ETC_PCI_IDS;
-
-static byte *name_list;
-static int name_list_loaded;
-
-struct nl_entry {
-  struct nl_entry *next;
-  word id1, id2;
-  int cat;
-  byte *name;
-};
-
-#define NL_VENDOR 0
-#define NL_DEVICE 1
-#define NL_CLASS 2
-#define NL_SUBCLASS 3
-#define NL_SUBSYSTEM_VENDOR 4
-#define NL_SUBSYSTEM_DEVICE 5
-
-#define HASH_SIZE 1024
-
-static struct nl_entry *nl_hash[HASH_SIZE];
-
-static inline unsigned int nl_calc_hash(int cat, int id1, int id2)
-{
-  unsigned int h;
-
-  h = id1 ^ id2 ^ (cat << 5);
-  h += (h >> 6);
-  return h & (HASH_SIZE-1);
-}
-
-static struct nl_entry *nl_lookup(int cat, int id1, int id2)
-{
-  unsigned int h = nl_calc_hash(cat, id1, id2);
-  struct nl_entry *n = nl_hash[h];
-
-  while (n && (n->id1 != id1 || n->id2 != id2 || n->cat != cat))
-    n = n->next;
-  return n;
-}
-
-static int nl_add(int cat, int id1, int id2, byte *text)
-{
-  unsigned int h = nl_calc_hash(cat, id1, id2);
-  struct nl_entry *n = nl_hash[h];
-
-  while (n && (n->id1 != id1 || n->id2 != id2 || n->cat != cat))
-    n = n->next;
-  if (n)
-    return 1;
-  n = xmalloc(sizeof(struct nl_entry));
-  n->id1 = id1;
-  n->id2 = id2;
-  n->cat = cat;
-  n->name = text;
-  n->next = nl_hash[h];
-  nl_hash[h] = n;
-  return 0;
-}
-
-static void
-err_name_list(char *msg)
-{
-  fprintf(stderr, "%s: %s: %m\n", pci_ids, msg);
-  exit(1);
-}
-
-static void
-parse_name_list(void)
-{
-  byte *p = name_list;
-  byte *q, *r;
-  int lino = 0;
-  unsigned int id1=0, id2=0;
-  int cat, last_cat = -1;
-
-  while (*p)
-    {
-      lino++;
-      q = p;
-      while (*p && *p != '\n')
-       {
-         if (*p == '#')
-           {
-             *p++ = 0;
-             while (*p && *p != '\n')
-               p++;
-             break;
-           }
-         if (*p == '\t')
-           *p = ' ';
-         p++;
-       }
-      if (*p == '\n')
-       *p++ = 0;
-      if (!*q)
-       continue;
-      r = p;
-      while (r > q && r[-1] == ' ')
-       *--r = 0;
-      r = q;
-      while (*q == ' ')
-       q++;
-      if (r == q)
-       {
-         if (q[0] == 'C' && q[1] == ' ')
-           {
-             if (strlen(q+2) < 3 ||
-                 q[4] != ' ' ||
-                 sscanf(q+2, "%x", &id1) != 1)
-               goto parserr;
-             cat = last_cat = NL_CLASS;
-           }
-         else if (q[0] == 'S' && q[1] == ' ')
-           {
-             if (strlen(q+2) < 5 ||
-                 q[6] != ' ' ||
-                 sscanf(q+2, "%x", &id1) != 1)
-               goto parserr;
-             cat = last_cat = NL_SUBSYSTEM_VENDOR;
-             q += 2;
-           }
-         else
-           {
-             if (strlen(q) < 5 ||
-                 q[4] != ' ' ||
-                 sscanf(q, "%x", &id1) != 1)
-               goto parserr;
-             cat = last_cat = NL_VENDOR;
-           }
-         id2 = 0;
-       }
-      else
-       {
-         if (sscanf(q, "%x", &id2) != 1)
-           goto parserr;
-         if (last_cat < 0)
-           goto parserr;
-         if (last_cat == NL_CLASS)
-           cat = NL_SUBCLASS;
-         else
-           cat = last_cat+1;
-       }
-      q += 4;
-      while (*q == ' ')
-       q++;
-      if (!*q)
-       goto parserr;
-      if (nl_add(cat, id1, id2, q))
-       {
-         fprintf(stderr, "%s, line %d: duplicate entry\n", pci_ids, lino);
-         exit(1);
-       }
-    }
-  return;
-
-parserr:
-  fprintf(stderr, "%s, line %d: parse error\n", pci_ids, lino);
-  exit(1);
-}
-
-static void
-load_name_list(void)
-{
-  int fd;
-  struct stat st;
-
-  fd = open(pci_ids, O_RDONLY);
-  if (fd < 0)
-    {
-      show_numeric_ids = 1;
-      return;
-    }
-  if (fstat(fd, &st) < 0)
-    err_name_list("stat");
-  name_list = xmalloc(st.st_size + 1);
-  if (read(fd, name_list, st.st_size) != st.st_size)
-    err_name_list("read");
-  name_list[st.st_size] = 0;
-  parse_name_list();
-  close(fd);
-  name_list_loaded = 1;
-}
-
-char *
-do_lookup_vendor(int cat, word i)
-{
-  static char vendbuf[6];
-
-  if (!show_numeric_ids && !name_list_loaded)
-    load_name_list();
-  if (!show_numeric_ids)
-    {
-      struct nl_entry *e;
-
-      e = nl_lookup(cat, i, 0);
-      if (e)
-       return e->name;
-    }
-  sprintf(vendbuf, "%04x", i);
-  return vendbuf;
-}
-
-char *
-do_lookup_device(int cat, word v, word i)
-{
-  static char devbuf[6];
-
-  if (!show_numeric_ids && !name_list_loaded)
-    load_name_list();
-  if (!show_numeric_ids)
-    {
-      struct nl_entry *e;
-
-      e = nl_lookup(cat, v, i);
-      if (e)
-       return e->name;
-    }
-  sprintf(devbuf, "%04x", i);
-  return devbuf;
-}
-
-char *
-do_lookup_device_full(int cat, word v, word i)
-{
-  static char fullbuf[256];
-
-  if (!show_numeric_ids && !name_list_loaded)
-    load_name_list();
-  if (!show_numeric_ids)
-    {
-      struct nl_entry *e, *e2;
-
-      e = nl_lookup(cat, v, 0);
-      e2 = nl_lookup(cat+1, v, i);
-      if (!e)
-       sprintf(fullbuf, "Unknown device %04x:%04x", v, i);
-      else if (!e2)
-       sprintf(fullbuf, "%s: Unknown device %04x", e->name, i);
-      else
-       sprintf(fullbuf, "%s %s", e->name, e2->name);
-    }
-  else
-    sprintf(fullbuf, "%04x:%04x", v, i);
-  return fullbuf;
-}
-
-char *
-lookup_vendor(word i)
-{
-  return do_lookup_vendor(NL_VENDOR, i);
-}
-
-char *
-lookup_subsys_vendor(word i)
-{
-  return do_lookup_vendor(NL_SUBSYSTEM_VENDOR, i);
-}
-
-char *
-lookup_device(word i, word v)
-{
-  return do_lookup_device(NL_DEVICE, v, i);
-}
-
-char *
-lookup_subsys_device(word v, word i)
-{
-  return do_lookup_device(NL_SUBSYSTEM_DEVICE, v, i);
-}
-
-char *
-lookup_device_full(word v, word i)
-{
-  return do_lookup_device_full(NL_VENDOR, v, i);
-}
-
-char *
-lookup_subsys_device_full(word v, word i)
-{
-  return do_lookup_device_full(NL_SUBSYSTEM_VENDOR, v, i);
-}
-
-char *
-lookup_class(word c)
-{
-  static char classbuf[80];
-
-  if (!show_numeric_ids && !name_list_loaded)
-    load_name_list();
-  if (!show_numeric_ids)
-    {
-      struct nl_entry *e;
-
-      e = nl_lookup(NL_SUBCLASS, c >> 8, c & 0xff);
-      if (e)
-       return e->name;
-      e = nl_lookup(NL_CLASS, c, 0);
-      if (e)
-       sprintf(classbuf, "%s [%04x]", e->name, c);
-      else
-       sprintf(classbuf, "Unknown class [%04x]", c);
-    }
-  else
-    sprintf(classbuf, "Class %04x", c);
-  return classbuf;
-}
index 4e2610884752ea6222c6ef5691863de006bcfefc..d2233538716121d69fd18d6b6d0a0c4b417f4812 100644 (file)
@@ -1,57 +1,50 @@
 /*
- *     $Id: pciutils.h,v 1.11 1999/01/19 21:24:42 mj Exp $
+ *     $Id: pciutils.h,v 1.12 1999/01/22 21:04:59 mj Exp $
  *
  *     Linux PCI Utilities -- Declarations
  *
- *     Copyright (c) 1997, 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *     Copyright (c) 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 
-#include <linux/types.h>
+#include "lib/pci.h"
 
-#ifdef KERNEL_PCI_H
-#include <linux/pci.h>
-#else
-#include "pci.h"
-#endif
-
-#define PCIUTILS_VERSION "1.10"
-
-#define PROC_BUS_PCI "/proc/bus/pci"
-#define ETC_PCI_IDS "/usr/share/pci.ids"
-
-/* Types */
-
-typedef __u8 byte;
-typedef __u16 word;
-typedef __u32 u32;
-
-/* lspci.c */
-
-void *xmalloc(unsigned int);
-
-/* names.c */
-
-extern int show_numeric_ids;
-extern char *pci_ids;
+#define PCIUTILS_VERSION PCILIB_VERSION
 
-char *lookup_vendor(word);
-char *lookup_device(word, word);
-char *lookup_device_full(word, word);
-char *lookup_class(word);
-char *lookup_subsys_vendor(word);
-char *lookup_subsys_device(word, word);
-char *lookup_subsys_device_full(word, word);
+void __attribute__((noreturn)) die(char *msg, ...);
+void *xmalloc(unsigned int howmuch);
+int parse_generic_option(int i, struct pci_access *pacc, char *optarg);
 
-/* filter.c */
-
-struct pci_filter {
-  int bus, slot, func;                 /* -1 = ANY */
-  int vendor, device;
-};
+#ifdef HAVE_PM_LINUX_PROC
+#define GENOPT_PROC "P:"
+#define GENHELP_PROC "-P <dir>\tUse specified directory instead of " PATH_PROC_BUS_PCI "\n"
+#else
+#define GENOPT_PROC
+#define GENHELP_PROC
+#endif
+#ifdef HAVE_PM_INTEL_CONF
+#define GENOPT_INTEL "H:"
+#define GENHELP_INTEL "-H <mode>\tUse direct hardware access (<mode> = 1 or 2)\n"
+#else
+#define GENOPT_INTEL
+#define GENHELP_INTEL
+#endif
+#ifdef HAVE_PM_SYSCALLS
+#define GENOPT_SYSCALLS "S"
+#define GENHELP_SYSCALLS "-S\t\tUse direct hardware access via syscalls\n"
+#else
+#define GENOPT_SYSCALLS
+#define GENHELP_SYSCALLS
+#endif
+#ifdef HAVE_PM_DUMP
+#define GENOPT_DUMP "F:"
+#define GENHELP_DUMP "-F <file>\tRead configuration data from given file\n"
+#else
+#define GENOPT_DUMP
+#define GENHELP_DUMP
+#endif
 
-void filter_init(struct pci_filter *);
-char *filter_parse_slot(struct pci_filter *, char *);
-char *filter_parse_id(struct pci_filter *, char *);
-int filter_match(struct pci_filter *, byte bus, byte devfn, word vendid, word devid);
+#define GENERIC_OPTIONS "G" GENOPT_PROC GENOPT_INTEL GENOPT_SYSCALLS GENOPT_DUMP
+#define GENERIC_HELP GENHELP_PROC GENHELP_INTEL GENHELP_SYSCALLS GENHELP_DUMP \
+       "-G\t\tEnable PCI access debugging\n"
diff --git a/setpci.8 b/setpci.8
deleted file mode 100644 (file)
index 5c622aa..0000000
--- a/setpci.8
+++ /dev/null
@@ -1,161 +0,0 @@
-.TH setpci 8 "19 January 1999" "pciutils-1.10" "Linux PCI Utilities"
-.IX setpci
-.SH NAME
-setpci \- configure PCI devices
-.SH SYNOPSIS
-.B setpci
-.RB [ options ]
-.B devices
-.BR operations ...
-
-.SH DESCRIPTION
-.PP
-.B setpci
-is a utility for querying and configuring PCI devices. It requires Linux kernel 2.1.82
-or newer as it uses the /proc/bus/pci interface.
-.PP
-All numbers are entered in hexadecimal notation.
-
-.SH OPTIONS
-.TP
-.B -v
-Tells
-.I setpci
-to be verbose and display detailed information about configuration space accesses.
-.TP
-.B -f
-Tells
-.I lspci
-not to complain when there's nothing to do (when no devices are selected).
-This option is intended for use in widely-distributed configuration scripts
-where it's uncertain whether the device in question is present in the machine
-or not.
-.TP
-.B -D
-`Demo mode' -- simulate configuration space accesses instead of really doing them.
-It's useful to try
-.B setpci -vD
-to see what your complex sequence of
-.B setpci
-operations does before you actually execute it.
-
-.SH DEVICE SELECTION
-.PP
-Before each sequence of operations you need to select which devices you wish that
-operation to affect.
-.TP
-.B -s [[<bus>]:][<slot>][.[<func>]]
-Select devices in specified bus, slot and function. Each component of the device
-address can be omitted or set as "*" meaning "any value". All numbers are
-hexadecimal.  E.g., "0:" means all devices on bus 0, "0" means all functions of device 0
-on any bus, "0.3" selects third function of device 0 on all busses and ".4" selects only
-fourth function of each device.
-.TP
-.B -d [<vendor>]:[<device>]
-Select devices with specified vendor and device ID. Both ID's are given in
-hexadecimal and may be omitted or given as "*" meaning "any value".
-
-.SH OPERATIONS
-.PP
-To query value of a configuration register, just name it (either by typing its name or
-by typing register address with optional
-.BR .B ,
-.B .W
-or
-.B .L
-suffix specifying register width as byte, word or longword).
-.PP
-To set a register, write
-.BR reg = values
-where
-.B reg
-is the same you would use to query the register and
-.B values
-is a comma-separated list of values you want to write starting with the given
-address.
-
-.SH REGISTER NAMES
-.PP
-.B setpci
-knows the following configuration register names. See PCI bus specs for their precise
-meaning or consult
-.B /usr/include/linux/pci.h
-for few comments.
-.PP
-.nf
-VENDOR_ID
-DEVICE_ID
-COMMAND
-STATUS
-REVISION
-CLASS_PROG
-CLASS_DEVICE
-CACHE_LINE_SIZE
-LATENCY_TIMER
-HEADER_TYPE
-BIST
-BASE_ADDRESS_0
-BASE_ADDRESS_1
-BASE_ADDRESS_2
-BASE_ADDRESS_3
-BASE_ADDRESS_4
-BASE_ADDRESS_5
-CARDBUS_CIS
-SUBSYSTEM_VENDOR_ID
-SUBSYSTEM_ID
-ROM_ADDRESS
-INTERRUPT_LINE
-INTERRUPT_PIN
-MIN_GNT
-MAX_LAT
-PRIMARY_BUS
-SECONDARY_BUS
-SUBORDINATE_BUS
-SEC_LATENCY_TIMER
-IO_BASE
-IO_LIMIT
-SEC_STATUS
-MEMORY_BASE
-MEMORY_LIMIT
-PREF_MEMORY_BASE
-PREF_MEMORY_LIMIT
-PREF_BASE_UPPER32
-PREF_LIMIT_UPPER32
-IO_BASE_UPPER16
-IO_LIMIT_UPPER16
-BRIDGE_ROM_ADDRESS
-BRIDGE_CONTROL
-CB_CARDBUS_BASE
-CB_CAPABILITIES
-CB_SEC_STATUS
-CB_BUS_NUMBER
-CB_CARDBUS_NUMBER
-CB_SUBORDINATE_BUS
-CB_CARDBUS_LATENCY
-CB_MEMORY_BASE_0
-CB_MEMORY_LIMIT_0
-CB_MEMORY_BASE_1
-CB_MEMORY_LIMIT_1
-CB_IO_BASE_0
-CB_IO_BASE_0_HI
-CB_IO_LIMIT_0
-CB_IO_LIMIT_0_HI
-CB_IO_BASE_1
-CB_IO_BASE_1_HI
-CB_IO_LIMIT_1
-CB_IO_LIMIT_1_HI
-CB_SUBSYSTEM_VENDOR_ID
-CB_SUBSYSTEM_ID
-CB_LEGACY_MODE_BASE
-
-.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 busses.
-.PP
-`setpci -s 12:3.4 34.l=1,2,3' writes longword 1 to register 34, 2 to register 35
-and 3 to register 35 of device at bus 12, slot 3, function 4.
-
-.SH AUTHOR
-The Linux PCI Utilities are maintained by Martin Mares <mj@atrey.karlin.mff.cuni.cz>.
index 2cbcfefff95cae3ff4757d566623ff18a45ba986..a3bfb63d545a1d441acdfaecf96c8fd409ea858d 100644 (file)
--- a/setpci.c
+++ b/setpci.c
@@ -1,5 +1,5 @@
 /*
- *     $Id: setpci.c,v 1.8 1998/10/24 13:39:20 mj Exp $
+ *     $Id: setpci.c,v 1.9 1999/01/22 21:05:02 mj Exp $
  *
  *     Linux PCI Utilities -- Manipulate PCI Configuration Registers
  *
@@ -8,20 +8,11 @@
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 
-#define _GNU_SOURCE
-
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <fcntl.h>
+#include <stdarg.h>
 #include <unistd.h>
-#include <errno.h>
-#include <asm/byteorder.h>
-
-#include <asm/unistd.h>
-#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
-#include <syscall-list.h>
-#endif
 
 #include "pciutils.h"
 
@@ -29,18 +20,11 @@ static int force;                   /* Don't complain if no devices match */
 static int verbose;                    /* Verbosity level */
 static int demo_mode;                  /* Only show */
 
-struct device {
-  struct device *next;
-  byte bus, devfn, mark;
-  word vendid, devid;
-  int fd, need_write;
-};
-
-static struct device *first_dev;
+static struct pci_access *pacc;
 
 struct op {
   struct op *next;
-  struct device **dev_vector;
+  struct pci_dev **dev_vector;
   unsigned int addr;
   unsigned int width;                  /* Byte width of the access */
   int num_values;                      /* Number of values to write; <0=read */
@@ -49,120 +33,33 @@ struct op {
 
 static struct op *first_op, **last_op = &first_op;
 
-void *
-xmalloc(unsigned int howmuch)
-{
-  void *p = malloc(howmuch);
-  if (!p)
-    {
-      fprintf(stderr, "setpci: Unable to allocate %d bytes of memory\n", howmuch);
-      exit(1);
-    }
-  return p;
-}
-
-/*
- * As libc doesn't support pread/pwrite yet, we have to call them directly
- * or use lseek/read/write instead.
- */
-#if !(defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0)
-
-#if defined(__GLIBC__) && !(defined(__powerpc__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
-#ifndef SYS_pread
-#define SYS_pread __NR_pread
-#endif
-static int
-pread(unsigned int fd, void *buf, size_t size, loff_t where)
-{
-  return syscall(SYS_pread, fd, buf, size, where);
-}
-
-#ifndef SYS_pwrite
-#define SYS_pwrite __NR_pwrite
-#endif
-static int
-pwrite(unsigned int fd, void *buf, size_t size, loff_t where)
-{
-  return syscall(SYS_pwrite, fd, buf, size, where);
-}
-#else
-static _syscall4(int, pread, unsigned int, fd, void *, buf, size_t, size, loff_t, where);
-static _syscall4(int, pwrite, unsigned int, fd, void *, buf, size_t, size, loff_t, where);
-#endif
-
-#endif
-
-static void
-scan_devices(void)
-{
-  struct device **last = &first_dev;
-  byte line[256];
-  FILE *f;
-
-  if (!(f = fopen(PROC_BUS_PCI "/devices", "r")))
-    {
-      perror(PROC_BUS_PCI "/devices");
-      exit(1);
-    }
-  while (fgets(line, sizeof(line), f))
-    {
-      struct device *d = xmalloc(sizeof(struct device));
-      unsigned int dfn, vend;
-
-      sscanf(line, "%x %x", &dfn, &vend);
-      d->bus = dfn >> 8U;
-      d->devfn = dfn & 0xff;
-      d->vendid = vend >> 16U;
-      d->devid = vend & 0xffff;
-      d->fd = -1;
-      *last = d;
-      last = &d->next;
-    }
-  fclose(f);
-  *last = NULL;
-}
-
-static struct device **
+static struct pci_dev **
 select_devices(struct pci_filter *filt)
 {
-  struct device *z, **a, **b;
+  struct pci_dev *z, **a, **b;
   int cnt = 1;
 
-  for(z=first_dev; z; z=z->next)
-    if (z->mark = filter_match(filt, z->bus, z->devfn, z->vendid, z->devid))
+  for(z=pacc->devices; z; z=z->next)
+    if (pci_filter_match(filt, z))
       cnt++;
   a = b = xmalloc(sizeof(struct device *) * cnt);
-  for(z=first_dev; z; z=z->next)
-    if (z->mark)
+  for(z=pacc->devices; z; z=z->next)
+    if (pci_filter_match(filt, z))
       *a++ = z;
   *a = NULL;
   return b;
 }
 
 static void
-exec_op(struct op *op, struct device *dev)
+exec_op(struct op *op, struct pci_dev *dev)
 {
   char *mm[] = { NULL, "%02x", "%04x", NULL, "%08x" };
   char *m = mm[op->width];
   unsigned int x;
   int i;
-  __u32 x32;
-  __u16 x16;
-  __u8 x8;
-
-  if (!demo_mode && dev->fd < 0)
-    {
-      char name[64];
-      sprintf(name, PROC_BUS_PCI "/%02x/%02x.%x", dev->bus, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
-      if ((dev->fd = open(name, dev->need_write ? O_RDWR : O_RDONLY)) < 0)
-       {
-         perror(name);
-         exit(1);
-       }
-    }
 
   if (verbose)
-    printf("%02x:%02x.%x:%02x", dev->bus, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), op->addr);
+    printf("%02x:%02x.%x:%02x", dev->bus, dev->dev, dev->func, op->addr);
   if (op->num_values >= 0)
     for(i=0; i<op->num_values; i++)
       {
@@ -176,23 +73,15 @@ exec_op(struct op *op, struct device *dev)
        switch (op->width)
          {
          case 1:
-           x8 = op->values[i];
-           i = pwrite(dev->fd, &x8, 1, op->addr);
+           pci_write_byte(dev, op->addr, op->values[i]);
            break;
          case 2:
-           x16 = __cpu_to_le16(op->values[i]);
-           i = pwrite(dev->fd, &x16, 2, op->addr);
+           pci_write_word(dev, op->addr, op->values[i]);
            break;
          default:
-           x32 = __cpu_to_le32(op->values[i]);
-           i = pwrite(dev->fd, &x32, 4, op->addr);
+           pci_write_long(dev, op->addr, op->values[i]);
            break;
          }
-       if (i != (int) op->width)
-         {
-           fprintf(stderr, "Error writing to %02x:%02x.%d: %m\n", dev->bus, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
-           exit(1);
-         }
       }
   else
     {
@@ -203,23 +92,15 @@ exec_op(struct op *op, struct device *dev)
          switch (op->width)
            {
            case 1:
-             i = pread(dev->fd, &x8, 1, op->addr);
-             x = x8;
+             x = pci_read_byte(dev, op->addr);
              break;
            case 2:
-             i = pread(dev->fd, &x16, 2, op->addr);
-             x = __le16_to_cpu(x16);
+             x = pci_read_word(dev, op->addr);
              break;
            default:
-             i = pread(dev->fd, &x32, 4, op->addr);
-             x = __le32_to_cpu(x32);
+             x = pci_read_long(dev, op->addr);
              break;
            }
-         if (i != (int) op->width)
-           {
-             fprintf(stderr, "Error reading from %02x:%02x.%d: %m\n", dev->bus, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
-             exit(1);
-           }
          printf(m, x);
        }
       else
@@ -231,8 +112,8 @@ exec_op(struct op *op, struct device *dev)
 static void
 execute(struct op *op)
 {
-  struct device **vec = NULL;
-  struct device **pdev, *dev;
+  struct pci_dev **vec = NULL;
+  struct pci_dev **pdev, *dev;
   struct op *oops;
 
   while (op)
@@ -249,16 +130,10 @@ execute(struct op *op)
 static void
 scan_ops(struct op *op)
 {
-  struct device **pdev, *dev;
-
   while (op)
     {
       if (op->num_values >= 0)
-       {
-         pdev = op->dev_vector;
-         while (dev = *pdev++)
-           dev->need_write = 1;
-       }
+       pacc->writeable = 1;
       op = op->next;
     }
 }
@@ -343,11 +218,12 @@ static void
 usage(void)
 {
   fprintf(stderr,
-"Usage: setpci [-fvD] (<device>+ <reg>[=<values>]*)*\n\
+"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\
-<device>:\t-s [[<bus>]:][<slot>][.[<func>]]\n\
+-D\t\tList changes, don't commit them\n"
+GENERIC_HELP
+"<device>:\t-s [[<bus>]:][<slot>][.[<func>]]\n\
 \t|\t-d [<vendor>]:[<device>]\n\
 <reg>:\t\t<number>[.(B|W|L)]\n\
      |\t\t<name>\n\
@@ -361,7 +237,8 @@ main(int argc, char **argv)
 {
   enum { STATE_INIT, STATE_GOT_FILTER, STATE_GOT_OP } state = STATE_INIT;
   struct pci_filter filter;
-  struct device **selected_devices = NULL;
+  struct pci_dev **selected_devices = NULL;
+  char *opts = GENERIC_OPTIONS ;
 
   if (argc == 2 && !strcmp(argv[1], "--version"))
     {
@@ -370,10 +247,15 @@ main(int argc, char **argv)
     }
   argc--;
   argv++;
+
+  pacc = pci_alloc();
+  pacc->error = die;
+
   while (argc && argv[0][0] == '-')
     {
       char *c = argv[0]+1;
       char *d = c;
+      char *e;
       while (*c)
        switch (*c)
          {
@@ -392,16 +274,42 @@ main(int argc, char **argv)
          case 0:
            break;
          default:
-           if (c != d)
-             usage();
-           goto next;
+           if (e = strchr(opts, *c))
+             {
+               char *arg;
+               c++;
+               if (e[1] == ':')
+                 {
+                   if (*c)
+                     arg = c;
+                   else if (argc > 1)
+                     {
+                       arg = argv[1];
+                       argc--; argv++;
+                     }
+                   else
+                     usage();
+                   c = "";
+                 }
+               else
+                 arg = NULL;
+               if (!parse_generic_option(*e, pacc, arg))
+                 usage();
+             }
+           else
+             {
+               if (c != d)
+                 usage();
+               goto next;
+             }
          }
       argc--;
       argv++;
     }
 next:
 
-  scan_devices();
+  pci_init(pacc);
+  pci_scan_bus(pacc);
 
   while (argc)
     {
@@ -427,24 +335,18 @@ next:
            usage();
          if (state != STATE_GOT_FILTER)
            {
-             filter_init(&filter);
+             pci_filter_init(pacc, &filter);
              state = STATE_GOT_FILTER;
            }
          switch (c[1])
            {
            case 's':
-             if (d = filter_parse_slot(&filter, d))
-               {
-                 fprintf(stderr, "setpci: -s: %s\n", d);
-                 return 1;
-               }
+             if (d = pci_filter_parse_slot(&filter, d))
+               die("-s: %s", d);
              break;
            case 'd':
-             if (d = filter_parse_id(&filter, d))
-               {
-                 fprintf(stderr, "setpci: -d: %s\n", d);
-                 return 1;
-               }
+             if (d = pci_filter_parse_id(&filter, d))
+               die("-d: %s", d);
              break;
            default:
              usage();
@@ -510,15 +412,9 @@ next:
              op->width = r->width;
            }
          if (ll > 0x100 || ll + op->width*((n < 0) ? 1 : n) > 0x100)
-           {
-             fprintf(stderr, "setpci: Register number out of range!\n");
-             return 1;
-           }
+           die("Register number out of range!");
          if (ll & (op->width - 1))
-           {
-             fprintf(stderr, "setpci: Unaligned register address!\n");
-             return 1;
-           }
+           die("Unaligned register address!");
          op->addr = ll;
          for(i=0; i<n; i++)
            {
diff --git a/setpci.man b/setpci.man
new file mode 100644 (file)
index 0000000..865e9b6
--- /dev/null
@@ -0,0 +1,202 @@
+.TH setpci 8 "@TODAY@" "@VERSION@" "Linux PCI Utilities"
+.IX setpci
+.SH NAME
+setpci \- configure PCI devices
+.SH SYNOPSIS
+.B setpci
+.RB [ options ]
+.B devices
+.BR operations ...
+
+.SH DESCRIPTION
+.PP
+.B setpci
+is a utility for querying and configuring PCI devices.
+
+To make use of all the features of this program, you need to have Linux kernel
+2.1.82 or newer which supports the /proc/bus/pci interface. With older kernels,
+the PCI utilities have to use direct hardware access which is available
+only to root and it suffers from numerous race conditions and other problems.
+
+All numbers are entered in hexadecimal notation.
+
+.SH OPTIONS
+.TP
+.B -v
+Tells
+.I setpci
+to be verbose and display detailed information about configuration space accesses.
+.TP
+.B -f
+Tells
+.I setpci
+not to complain when there's nothing to do (when no devices are selected).
+This option is intended for use in widely-distributed configuration scripts
+where it's uncertain whether the device in question is present in the machine
+or not.
+.TP
+.B -D
+`Demo mode' -- simulate configuration space accesses instead of really doing them.
+It's useful to try
+.B setpci -vD
+to see what your complex sequence of
+.B setpci
+operations does before you actually execute it.
+
+.SH DEVICE SELECTION
+.PP
+Before each sequence of operations you need to select which devices you wish that
+operation to affect.
+.TP
+.B -s [[<bus>]:][<slot>][.[<func>]]
+Select devices in specified bus, slot and function. Each component of the device
+address can be omitted or set as "*" meaning "any value". All numbers are
+hexadecimal.  E.g., "0:" means all devices on bus 0, "0" means all functions of device 0
+on any bus, "0.3" selects third function of device 0 on all busses and ".4" selects only
+fourth function of each device.
+.TP
+.B -d [<vendor>]:[<device>]
+Select devices with specified vendor and device ID. Both ID's are given in
+hexadecimal and may be omitted or given as "*" meaning "any value".
+
+.SH OPERATIONS
+.PP
+To query value of a configuration register, just name it (either by typing its name or
+by typing register address with optional
+.BR .B ,
+.B .W
+or
+.B .L
+suffix specifying register width as byte, word or longword).
+.PP
+To set a register, write
+.BR reg = values
+where
+.B reg
+is the same you would use to query the register and
+.B values
+is a comma-separated list of values you want to write starting with the given
+address.
+
+.SH REGISTER NAMES
+.PP
+.B setpci
+knows the following configuration register names. See PCI bus specs for their precise
+meaning or consult
+.B /usr/include/linux/pci.h
+for few comments.
+.PP
+.nf
+VENDOR_ID
+DEVICE_ID
+COMMAND
+STATUS
+REVISION
+CLASS_PROG
+CLASS_DEVICE
+CACHE_LINE_SIZE
+LATENCY_TIMER
+HEADER_TYPE
+BIST
+BASE_ADDRESS_0
+BASE_ADDRESS_1
+BASE_ADDRESS_2
+BASE_ADDRESS_3
+BASE_ADDRESS_4
+BASE_ADDRESS_5
+CARDBUS_CIS
+SUBSYSTEM_VENDOR_ID
+SUBSYSTEM_ID
+ROM_ADDRESS
+INTERRUPT_LINE
+INTERRUPT_PIN
+MIN_GNT
+MAX_LAT
+PRIMARY_BUS
+SECONDARY_BUS
+SUBORDINATE_BUS
+SEC_LATENCY_TIMER
+IO_BASE
+IO_LIMIT
+SEC_STATUS
+MEMORY_BASE
+MEMORY_LIMIT
+PREF_MEMORY_BASE
+PREF_MEMORY_LIMIT
+PREF_BASE_UPPER32
+PREF_LIMIT_UPPER32
+IO_BASE_UPPER16
+IO_LIMIT_UPPER16
+BRIDGE_ROM_ADDRESS
+BRIDGE_CONTROL
+CB_CARDBUS_BASE
+CB_CAPABILITIES
+CB_SEC_STATUS
+CB_BUS_NUMBER
+CB_CARDBUS_NUMBER
+CB_SUBORDINATE_BUS
+CB_CARDBUS_LATENCY
+CB_MEMORY_BASE_0
+CB_MEMORY_LIMIT_0
+CB_MEMORY_BASE_1
+CB_MEMORY_LIMIT_1
+CB_IO_BASE_0
+CB_IO_BASE_0_HI
+CB_IO_LIMIT_0
+CB_IO_LIMIT_0_HI
+CB_IO_BASE_1
+CB_IO_BASE_1_HI
+CB_IO_LIMIT_1
+CB_IO_LIMIT_1_HI
+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. The following
+options control parameters of the library, especially what access method it uses.
+By default, PCILIB uses the first available access method and displays no debugging
+messages. Each switch is accompanied by a list of hardware/software configurations
+it's supported in.
+
+.TP
+.B -P <dir>
+Use Linux 2.1 style configuration access to directory
+.B <dir>
+instead of /proc/bus/pci. (Linux 2.1 or newer only)
+.TP
+.B -H1
+Use direct hardware access via Intel configuration mechanism 1. (i386 and compatible only)
+.TP
+.B -H2
+Use direct hardware access via Intel configuration mechanism 2. Warning: This method
+is able to address only first 16 devices on any bus and it seems to be very
+unrealiable in many cases. (i386 and compatible only)
+.TP
+.B -S
+Use PCI access syscalls. (Linux on Alpha and UltraSparc only)
+.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
+requests for more dumps. (All systems)
+.TP
+.B -G
+Increase debug level of the library. (All systems)
+
+.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 busses.
+.PP
+`setpci -s 12:3.4 34.l=1,2,3' writes longword 1 to register 34, 2 to register 35
+and 3 to register 35 of device at bus 12, slot 3, function 4.
+
+.SH SEE ALSO
+.BR lspci (8)
+
+.SH AUTHOR
+The Linux PCI Utilities are maintained by Martin Mares <mj@atrey.karlin.mff.cuni.cz>.