+Tue Mar 31 23:11:57 1998 Martin Mares <mj@albireo.ucw.cz>
+
+ * setpci.c: Added.
+
+Sun Mar 22 15:39:08 1998 Martin Mares <mj@albireo.ucw.cz>
+
+ * lspci.8: Updated the documentation.
+
+ * lspci.c: Modified to use the new filtering mechanism (options -f and -d).
+
+ * filter.c: Introduced new generic device filter.
+
Thu Mar 19 17:03:48 1998 Martin Mares <mj@lomikel.karlin.mff.cuni.cz>
* lspci.c (grow_tree, show_tree_dev, print_it): Fixed displaying
-# $Id: Makefile,v 1.6 1998/02/09 12:32:52 mj Exp $
+# $Id: Makefile,v 1.7 1998/03/31 21:02:12 mj Exp $
# Makefile for Linux PCI Utilities
# (c) 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
all: lspci
-lspci: lspci.o names.o
+#all: lspci setpci
+
+lspci: lspci.o names.o filter.o
lspci.o: lspci.c pciutils.h
names.o: names.c pciutils.h
+filter.o: filter.c pciutils.h
+
+setpci: setpci.o filter.o
+
+setpci.o: setpci.c
clean:
rm -f `find . -name "*~" -or -name "*.[oa]" -or -name "\#*\#" -or -name TAGS -or -name core`
- rm -f lspci pci.h
+ rm -f lspci setpci pci.h
install: all
install -o root -g root -m 755 -s lspci $(PREFIX)/sbin
Release notes about new versions will be send to the list and problems with
the Linux PCI support will be probably discussed there, too.
+ You also might want to look at the pciutils web page containing release
+notes and other news: http://atrey.karlin.mff.cuni.cz/~mj/pciutils.html.
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TODO:
- - Work-around the PIIX4 ACPI crash?
-
- Better displaying of IRQ's generated by both PCI and CardBus bridges.
- Full displaying of CardBus bridge configuration. (Has anyone seen
--- /dev/null
+/*
+ * $Id: filter.c,v 1.1 1998/03/31 21:02:14 mj Exp $
+ *
+ * Linux PCI Utilities -- Device Filtering
+ *
+ * Copyright (c) 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/pci.h>
+
+#include "pciutils.h"
+
+void
+filter_init(struct pci_filter *f)
+{
+ f->bus = f->slot = f->func = -1;
+ f->vendor = f->device = -1;
+}
+
+/* Slot filter syntax: [[bus]:][slot][.[func]] */
+
+char *
+filter_parse_slot(struct pci_filter *f, char *str)
+{
+ char *colon = strchr(str, ':');
+ char *dot = strchr((colon ? colon + 1 : str), '.');
+ char *mid = str;
+ char *e;
+
+ if (colon)
+ {
+ *colon++ = 0;
+ mid = colon;
+ if (str[0] && strcmp(str, "*"))
+ {
+ long int x = strtol(str, &e, 16);
+ if ((e && *e) || (x < 0 || x >= 0xff))
+ return "Invalid bus number";
+ f->bus = x;
+ }
+ }
+ if (dot)
+ *dot++ = 0;
+ if (mid[0] && strcmp(mid, "*"))
+ {
+ long int x = strtol(str, &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;
+}
Show a tree-like diagram containing all busses, bridges, devices and connections
between them.
.TP
-.B -B <bus>
-Show only devices on specified bus.
-.TP
-.B -S <slot>
-Show only devices in specified slot number.
-.TP
-.B -F <func>
-Show only specified function number of all devices (you can mix it with
-.B -B
-and
-.B -S
-to select single device).
-.TP
-.B -V <vendor>
-Show only devices having specified vendor ID.
-.TP
-.B -D <devid>
-Show only devices having specified device ID.
+.B -s [[<bus>]:][<slot>][.[<func>]]
+Show only devices in specified bus, slot and function. Each component of the device
+address can be omitted or set as "*" meaning "any value". All numbers are
+hexadecimal. E.g., "0:" means all devices on bus 0, "0" means all functions of device 0
+on any bus, "0.3" selects third function of device 0 on all busses and ".4" shows only
+fourth function of each device.
+.TP
+.B -d [<vendor>]:[<device>]
+Show only devices with specified vendor and device ID. Both ID's are given in
+hexadecimal and may be omitted or given as "*" meaning "any value".
.TP
.B -i <file>
Use
/*
- * $Id: lspci.c,v 1.9 1998/03/19 15:56:43 mj Exp $
+ * $Id: lspci.c,v 1.10 1998/03/31 21:02:16 mj Exp $
*
* Linux PCI Utilities -- List All PCI Devices
*
static int verbose; /* Show detailed information */
static int buscentric_view; /* Show bus addresses/IRQ's instead of CPU-visible ones */
static int show_hex; /* Show contents of config space as hexadecimal numbers */
-static int bus_filter = -1; /* Bus, slot, function, vendor and device ID filtering */
-static int slot_filter = -1;
-static int func_filter = -1;
-static int vend_filter = -1;
-static int dev_filter = -1;
+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[] = "nvbxB:S:F:V:D:ti:p:m";
+static char options[] = "nvbxs:d:ti:p:m";
static char help_msg[] = "\
Usage: lspci [<switches>]\n\
\n\
--v\tBe verbose\n\
--n\tShow numeric ID's\n\
--b\tBus-centric view (PCI addresses and IRQ's instead of those seen by the CPU)\n\
--x\tShow hex-dump of config space (-xx shows full 256 bytes)\n\
--B <bus>, -S <slot>, -F <func>, -V <vendor>, -D <device> Show only selected devices\n\
--t\tShow bus tree\n\
--m\tProduce machine-readable output\n\
+-v\t\tBe verbose\n\
+-n\t\tShow numeric ID's\n\
+-b\t\tBus-centric view (PCI addresses and IRQ's instead of those seen by the CPU)\n\
+-x\t\tShow hex-dump of config space (-xx shows full 256 bytes)\n\
+-s [[<bus>]:][<slot>][.[<func>]]\tShow only devices in selected slots\n\
+-d [<vendor>]:[<device>]\tShow only selected devices\n\
+-t\t\tShow bus tree\n\
+-m\t\tProduce machine-readable output\n\
-i <file>\tUse specified ID database instead of " ETC_PCI_IDS "\n\
-p <dir>\tUse specified bus directory instead of " PROC_BUS_PCI "\n\
";
return p;
}
-/* Filtering */
-
-static inline int
-filter_out(struct device *d)
-{
- return (bus_filter >= 0 && d->bus != bus_filter ||
- slot_filter >= 0 && PCI_SLOT(d->devfn) != slot_filter ||
- func_filter >= 0 && PCI_FUNC(d->devfn) != func_filter ||
- vend_filter >= 0 && d->vendid != vend_filter ||
- dev_filter >= 0 && d->devid != dev_filter);
-}
-
/* Interface for /proc/bus/pci */
static void
d->devfn = dfn & 0xff;
d->vendid = vend >> 16U;
d->devid = vend & 0xffff;
- if (!filter_out(d))
+ if (filter_match(&filter, d->bus, d->devfn, d->vendid, d->devid))
{
*last_dev = d;
last_dev = &d->next;
main(int argc, char **argv)
{
int i;
+ char *msg;
+ filter_init(&filter);
while ((i = getopt(argc, argv, options)) != -1)
switch (i)
{
case 'b':
buscentric_view = 1;
break;
- case 'B':
- bus_filter = strtol(optarg, NULL, 16);
- break;
- case 'S':
- slot_filter = strtol(optarg, NULL, 16);
- break;
- case 'F':
- func_filter = strtol(optarg, NULL, 16);
- break;
- case 'V':
- vend_filter = strtol(optarg, NULL, 16);
+ case 's':
+ if (msg = filter_parse_slot(&filter, optarg))
+ {
+ fprintf(stderr, "lspci: -f: %s\n", msg);
+ return 1;
+ }
break;
- case 'D':
- dev_filter = strtol(optarg, NULL, 16);
+ case 'd':
+ if (msg = filter_parse_id(&filter, optarg))
+ {
+ fprintf(stderr, "lspci: -d: %s\n", msg);
+ return 1;
+ }
break;
case 'x':
show_hex++;
/*
- * $Id: pciutils.h,v 1.3 1998/02/09 12:32:56 mj Exp $
+ * $Id: pciutils.h,v 1.4 1998/03/31 21:02:18 mj Exp $
*
* Linux PCI Utilities -- Declarations
*
char *lookup_device(word, word);
char *lookup_device_full(word, word);
char *lookup_class(word);
+
+/* filter.c */
+
+struct pci_filter {
+ int bus, slot, func; /* -1 = ANY */
+ int vendor, device;
+};
+
+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);
Author: mj@atrey.karlin.mff.cuni.cz (Martin Mares)
Maintained-by: mj@atrey.karlin.mff.cuni.cz (Martin Mares)
Primary-site: atrey.karlin.mff.cuni.cz pub/local/mj/linux/pciutils-1.02.tar.gz
-Alternate-site: sunsite.unc.edu pub/Linux/system/hardware/pciutils-1.02.tar.gz
+Alternate-site: sunsite.unc.edu pub/Linux/hardware/pciutils-1.02.tar.gz
Copying-policy: GPL
End
--- /dev/null
+/*
+ * $Id: setpci.c,v 1.1 1998/03/31 21:02:20 mj Exp $
+ *
+ * Linux PCI Utilities -- Manipulate PCI Configuration Registers
+ *
+ * Copyright (c) 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "pciutils.h"
+
+static int force; /* Don't complain if no devices match */
+static int verbose; /* Verbosity level */
+
+struct device {
+ struct device *next;
+ byte bus, devfn, mark;
+ word vendid, devid;
+ int fd;
+};
+
+static struct device *first_dev;
+
+struct op {
+ struct op *next;
+ struct device **dev_vector;
+ unsigned int addr;
+ unsigned int width; /* Byte width of the access */
+ int num_values; /* Number of values to write; <0=read */
+ unsigned int values[0];
+};
+
+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;
+}
+
+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 **
+select_devices(struct pci_filter *filt)
+{
+ struct device *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))
+ cnt++;
+ a = b = xmalloc(sizeof(struct device *) * cnt);
+ for(z=first_dev; z; z=z->next)
+ if (z->mark)
+ *a++ = z;
+ *a = NULL;
+ return b;
+}
+
+static void
+exec_op(struct op *op, struct device *dev)
+{
+ char *mm[] = { NULL, "%02x", "%04x", NULL, "%08x" };
+ char *m = mm[op->width];
+
+ if (dev->fd < 0)
+ {
+ char name[64];
+ sprintf(name, PROC_BUS_PCI "/%02x/%02x.%x", dev->bus, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ dev->fd = open(name, O_RDWR ????
+ }
+
+ if (verbose)
+ printf("%02x.%02x:%x.%c ", dev->bus, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+ "?BW?L"[op->width]);
+ if (op->num_values > 0)
+ {
+ }
+ else
+ {
+ if (verbose)
+ printf("= ");
+ }
+}
+
+static void
+execute(struct op *op)
+{
+ struct device **vec = NULL;
+ struct device **pdev, *dev;
+ struct op *oops;
+
+ while (op)
+ {
+ pdev = vec = op->dev_vector;
+ while (dev = *pdev++)
+ for(oops=op; oops && oops->dev_vector == vec; oops=oops->next)
+ exec_op(oops, dev);
+ while (op && op->dev_vector == vec)
+ op = op->next;
+ }
+}
+
+static void usage(void) __attribute__((noreturn));
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+"Usage: setpci [-f] [-v] (<device>+ <reg>[=<values>]*)*\n\
+<device>: -s [[<bus>]:][<slot>][.[<func>]]\n\
+\t| -d [<vendor>]:[<device>]\n\
+<reg>: <number>[.(B|W|L)]\n\
+<values>: <value>[,<value>...]\n\
+");
+ exit(1);
+}
+
+int
+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;
+
+ argc--;
+ argv++;
+ while (argc && argv[0][0] == '-')
+ {
+ char *c = argv[0]+1;
+ char *d = c;
+ while (*c)
+ switch (*c)
+ {
+ case 'v':
+ verbose++;
+ c++;
+ break;
+ case 'f':
+ force++;
+ c++;
+ break;
+ case 0:
+ break;
+ default:
+ if (c != d)
+ usage();
+ goto next;
+ }
+ argc--;
+ argv++;
+ }
+next:
+
+ scan_devices();
+
+ while (argc)
+ {
+ char *c = argv[0];
+ char *d, *e, *f;
+ int n, i;
+ struct op *op;
+ unsigned long ll, lim;
+
+ if (*c == '-')
+ {
+ if (!c[1] || !strchr("sd", c[1]))
+ usage();
+ if (c[2])
+ d = (c[2] == '=') ? c+3 : c+2;
+ else if (argc)
+ {
+ argc--;
+ argv++;
+ d = argv[0];
+ }
+ else
+ usage();
+ if (state != STATE_GOT_FILTER)
+ {
+ filter_init(&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;
+ }
+ break;
+ case 'd':
+ if (d = filter_parse_id(&filter, d))
+ {
+ fprintf(stderr, "setpci: -d: %s\n", d);
+ return 1;
+ }
+ break;
+ default:
+ usage();
+ }
+ }
+ else if (state == STATE_INIT)
+ usage();
+ else
+ {
+ if (state == STATE_GOT_FILTER)
+ selected_devices = select_devices(&filter);
+ if (!selected_devices[0] && !force)
+ fprintf(stderr, "setpci: Warning: No devices selected for `%s'.\n", c);
+ state = STATE_GOT_OP;
+ d = strchr(c, '=');
+ if (d)
+ {
+ *d++ = 0;
+ for(e=d, n=1; *e; e++)
+ if (*e == ',')
+ n++;
+ op = xmalloc(sizeof(struct op) + n*sizeof(unsigned int));
+ }
+ else
+ {
+ n = -1;
+ op = xmalloc(sizeof(struct op));
+ }
+ op->dev_vector = selected_devices;
+ op->num_values = n;
+ e = strchr(c, '.');
+ if (e)
+ {
+ *e++ = 0;
+ if (e[1])
+ usage();
+ switch (*e & 0xdf)
+ {
+ case 'B':
+ op->width = 1; break;
+ case 'W':
+ op->width = 2; break;
+ case 'L':
+ op->width = 4; break;
+ default:
+ usage();
+ }
+ }
+ else
+ op->width = 1;
+ ll = strtol(c, &f, 16);
+ if (ll > 0x100 || ll + op->width*n > 0x100)
+ {
+ fprintf(stderr, "setpci: Register number out of range!\n");
+ return 1;
+ }
+ for(i=0; i<n; i++)
+ {
+ e = strchr(d, ',');
+ if (e)
+ *e++ = 0;
+ ll = strtoul(d, &f, 16);
+ lim = (2 << ((op->width << 3) - 1)) - 1;
+ if (f && *f ||
+ (ll > lim && ll < ~0UL - lim))
+ usage();
+ op->values[i] = ll;
+ d = e;
+ }
+ *last_op = op;
+ last_op = &op->next;
+ op->next = NULL;
+ }
+ argc--;
+ argv++;
+ }
+ if (state == STATE_INIT)
+ usage();
+
+ execute(first_op);
+
+ return 0;
+}