From: Martin Mares Date: Fri, 22 Jan 1999 21:04:45 +0000 (+0000) Subject: Rewrote the PCI Utilities. All PCI configuration space access has been X-Git-Tag: v3.0.0~303 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=727ce158868ed101006ecc5d3dd3faede927165c;p=pciutils.git Rewrote the PCI Utilities. All PCI configuration space access has been 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.) --- diff --git a/ChangeLog b/ChangeLog index 785a8bc..3ead388 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,64 @@ +Fri Jan 22 19:29:31 1999 Martin Mares + + * 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 + 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 + + * Wrote configure script and rewrote Makefiles. + + * Removed few unused variables. + +Wed Jan 20 12:21:56 1999 Martin Mares + + * 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 + + * 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 * Released as version 1.10. diff --git a/Makefile b/Makefile index a7abb06..ad46fff 100644 --- 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 +# (c) 1998--1999 Martin Mares -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 d960ba1..390d6ff 100644 --- 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 @@ -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 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 + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include +#include + +#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 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 - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include -#include -#include - -#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 index 0000000..6138fd9 --- /dev/null +++ b/lib/Makefile @@ -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 + +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 index 0000000..cd7ce12 --- /dev/null +++ b/lib/access.c @@ -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 + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include + +#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; iconfig) + 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; idebug("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 index 0000000..9e62802 --- /dev/null +++ b/lib/buffer.c @@ -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 + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include + +#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 index 0000000..eb72fb1 --- /dev/null +++ b/lib/configure @@ -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 index 0000000..5aa8323 --- /dev/null +++ b/lib/dump.c @@ -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 + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include + +#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 index 0000000..299af29 --- /dev/null +++ b/lib/filter.c @@ -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 + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include + +#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 index 0000000..2ac30bd --- /dev/null +++ b/lib/generic.c @@ -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 + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include + +#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; ibase_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 index 0000000..0e1acae --- /dev/null +++ b/lib/i386-ports.c @@ -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 + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include + +#ifdef __GLIBC__ +#include +#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 index 0000000..bd80ee3 --- /dev/null +++ b/lib/internal.h @@ -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 + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "pci.h" + +#ifdef HAVE_PM_LINUX_BYTEORDER_H + +#include +#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 +#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 index 0000000..e18e47f --- /dev/null +++ b/lib/names.c @@ -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 + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 ""; + } + return (res == size) ? "" : buf; +} diff --git a/lib/pci.h b/lib/pci.h new file mode 100644 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 + * + * 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 +#endif + +/* + * Types + */ + +#include + +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 index 0000000..f5ae883 --- /dev/null +++ b/lib/proc.c @@ -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 + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#include +#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1 +#include +#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 index 0000000..250e622 --- /dev/null +++ b/lib/syscalls.c @@ -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 + * + * 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 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 [[]:][][.[]] -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 []:[] -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 -Use -.B - -as PCI ID database instead of /usr/share/pci.ids. -.TP -.B -p -Use -.B -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 . diff --git a/lspci.c b/lspci.c index 2480f89..2b1c5e1 100644 --- 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 #include #include -#include +#include #include #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 []\n\ @@ -39,11 +38,15 @@ Usage: lspci []\n\ -d []:[]\tShow only selected devices\n\ -t\t\tShow bus tree\n\ -m\t\tProduce machine-readable output\n\ --i \tUse specified ID database instead of " ETC_PCI_IDS "\n\ --p \tUse specified bus directory instead of " PROC_BUS_PCI "\n\ -"; +-i \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 []\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(""); else printf(""); 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("\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(""); + done = 1; + } } + } + if (!done) + { + if (a) + printf(LONG_FORMAT, a); else - { - printf("????????"); - x64 = 1; - } + printf(((flg & PCI_BASE_ADDRESS_MEM_MASK) || z) ? "" : ""); } - if (x64 || a) - printf("%08lx", a); - else - printf(""); 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; iconfig_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 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 [[]:][][.[]] +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 []:[] +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 +Use +.B + +as PCI ID database instead of /usr/share/pci.ids. +.TP +.B -p +Use +.B +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 +Use Linux 2.1 style configuration access to directory +.B +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 +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 . diff --git a/names.c b/names.c deleted file mode 100644 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 - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/pciutils.h b/pciutils.h index 4e26108..d223353 100644 --- a/pciutils.h +++ b/pciutils.h @@ -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 + * Copyright (c) 1997--1999 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ -#include +#include "lib/pci.h" -#ifdef KERNEL_PCI_H -#include -#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 \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 \tUse direct hardware access ( = 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 \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 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 [[]:][][.[]] -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 []:[] -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 . diff --git a/setpci.c b/setpci.c index 2cbcfef..a3bfb63 100644 --- 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 #include #include -#include +#include #include -#include -#include - -#include -#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1 -#include -#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; inum_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] (+ [=]*)*\n\ +"Usage: setpci [] (+ [=]*)*\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\ -:\t-s [[]:][][.[]]\n\ +-D\t\tList changes, don't commit them\n" +GENERIC_HELP +":\t-s [[]:][][.[]]\n\ \t|\t-d []:[]\n\ :\t\t[.(B|W|L)]\n\ |\t\t\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]:][][.[]] +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 []:[] +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 +Use Linux 2.1 style configuration access to directory +.B +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 +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 .