+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.
-# $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
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
-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>
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
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.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TODO:
-
- - Better displaying of IRQ's generated by both PCI and CardBus bridges.
+ - lspci: "scan hard" function
+ - lib: "syscall" access method
--- /dev/null
+/*
+ * $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;
+}
--- /dev/null
+# $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)
--- /dev/null
+/*
+ * $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;
+}
--- /dev/null
+/*
+ * $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;
+}
--- /dev/null
+#!/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
--- /dev/null
+/*
+ * $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
+};
/*
- * $Id: filter.c,v 1.2 1998/06/08 07:51:45 mj Exp $
+ * $Id: filter.c,v 1.1 1999/01/22 21:05:22 mj Exp $
*
- * Linux PCI Utilities -- Device Filtering
+ * Linux PCI Library -- Device Filtering
*
- * Copyright (c) 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "pciutils.h"
+#include "internal.h"
void
-filter_init(struct pci_filter *f)
+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 *
-filter_parse_slot(struct pci_filter *f, char *str)
+pci_filter_parse_slot(struct pci_filter *f, char *str)
{
char *colon = strchr(str, ':');
char *dot = strchr((colon ? colon + 1 : str), '.');
/* ID filter syntax: [vendor]:[device] */
char *
-filter_parse_id(struct pci_filter *f, char *str)
+pci_filter_parse_id(struct pci_filter *f, char *str)
{
char *s, *e;
}
int
-filter_match(struct pci_filter *f, byte bus, byte devfn, word vendid, word devid)
+pci_filter_match(struct pci_filter *f, struct pci_dev *d)
{
- 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))
+ 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;
}
--- /dev/null
+/*
+ * $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);
+}
--- /dev/null
+/*
+ * $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 */
+};
--- /dev/null
+/*
+ * $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))
--- /dev/null
+/*
+ * $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;
+}
--- /dev/null
+/*
+ * $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
--- /dev/null
+/*
+ * $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
+};
--- /dev/null
+/*
+ * $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 */
+};
/*
- * $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
*
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
-#include <fcntl.h>
+#include <stdarg.h>
#include <unistd.h>
#include "pciutils.h"
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\
-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"
#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
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;
}
static void
sort_them(void)
{
- struct device **index, **h;
+ struct device **index, **h, **last_dev;
int cnt;
struct device *d;
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)))
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;
{
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))
{
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" :
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]");
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;
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);
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);
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);
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:
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)
{
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
{
((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');
}
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);
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)
{
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);
}
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');
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
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)
{
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);
}
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++;
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;
}
-.TH lspci 8 "19 January 1999" "pciutils-1.10" "Linux PCI Utilities"
+.TH lspci 8 "@TODAY@" "@VERSION@" "Linux PCI Utilities"
.IX lspci
.SH NAME
lspci \- list all PCI devices
.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.
+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
PCI bus instead of as seen by the kernel.
.TP
.B -t
-Show a tree-like diagram containing all busses, bridges, devices and connections
+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 busses and ".4" shows only
+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>]
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 kernel. Contains
-per-bus subdirectories with per-card config space files and a
-.I
-devices
+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>.
+++ /dev/null
-/*
- * $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;
-}
/*
- * $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"
/*
- * $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
*
* 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"
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 */
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++)
{
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
{
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
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)
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;
}
}
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\
{
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"))
{
}
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)
{
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)
{
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();
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++)
{
-.TH setpci 8 "19 January 1999" "pciutils-1.10" "Linux PCI Utilities"
+.TH setpci 8 "@TODAY@" "@VERSION@" "Linux PCI Utilities"
.IX setpci
.SH NAME
setpci \- configure PCI devices
.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
+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 -f
Tells
-.I lspci
+.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
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).
`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>.