]> mj.ucw.cz Git - pciutils.git/commitdiff
PCIutils 1.03.
authorMartin Mares <mj@ucw.cz>
Sun, 19 Apr 1998 11:02:25 +0000 (11:02 +0000)
committerMartin Mares <mj@ucw.cz>
Fri, 5 May 2006 12:09:50 +0000 (14:09 +0200)
- setpci now works and has a (well, a bit confusing) man page.

- lspci now understands the expansion ROM base addresses as reported
  by new kernels.

ChangeLog
Makefile
README
lspci.8
lspci.c
pciutils.lsm
setpci.8 [new file with mode: 0644]
setpci.c

index 5b34d8e3a50213e1ebc7a26c59878a4619a24989..b8bb1ff81d4b7fa535c5ffb5a381b6291a7e9626 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Sun Apr 19 11:14:25 1998  Martin Mares  <mj@albireo.ucw.cz>
+
+       * setpci.8: Written.
+
+       * setpci.c: Finished.
+
+       * lspci.c: Now able to fetch expansion ROM base from kernel device list
+       and print it if not in buscentric mode.
+
 Tue Mar 31 23:11:57 1998  Martin Mares  <mj@albireo.ucw.cz>
 
        * setpci.c: Added.
index bb58e3640573f3de5b3ae21259710ac387c13a0a..e4ab6307c316c90cae42f0e736535cb5824e3a70 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.7 1998/03/31 21:02:12 mj Exp $
+# $Id: Makefile,v 1.8 1998/04/19 11:02:25 mj Exp $
 # Makefile for Linux PCI Utilities
 # (c) 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
 
@@ -10,28 +10,24 @@ CFLAGS=$(OPT) -Wall -W -Wno-parentheses -Wstrict-prototypes -Wno-unused -Werror
 PREFIX=/
 MANPREFIX=/usr
 
-all: lspci
-
-#all: lspci setpci
+all: lspci setpci
 
 lspci: lspci.o names.o filter.o
+setpci: setpci.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
+setpci.o: setpci.c pciutils.h
 
 clean:
        rm -f `find . -name "*~" -or -name "*.[oa]" -or -name "\#*\#" -or -name TAGS -or -name core`
        rm -f lspci setpci pci.h
 
 install: all
-       install -o root -g root -m 755 -s lspci $(PREFIX)/sbin
+       install -o root -g root -m 755 -s lspci setpci $(PREFIX)/sbin
        install -o root -g root -m 644 pci.ids $(PREFIX)/etc
-       install -o root -g root -m 644 lspci.8 $(MANPREFIX)/man/man8
+       install -o root -g root -m 644 lspci.8 setpci.8 $(MANPREFIX)/man/man8
 
 dist: clean
        cp /usr/src/linux/include/linux/pci.h .
diff --git a/README b/README
index dfe0cbfca4bd8cfd201feb9b906123d96d028de4..971b7cc59254112d7bbd5aaf190802264984eea3 100644 (file)
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-This package contains the Linux PCI Utilities, version 1.02.
+This package contains the Linux PCI Utilities, version 1.03.
 
 Copyright (c) 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
 
@@ -10,10 +10,16 @@ for details.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-   The Linux PCI Utilities currently contain only one utility -- lspci,
-indented for displaying of detailed information about all PCI busses and
-devices in the system, replacing the original /proc/pci interface. See
-the manual page (lspci.8) for more details.
+   The Linux PCI Utilities contain various utilities for dealing with the PCI
+bus in Linux:
+
+       - lspci: displays detailed information about all PCI busses and devices
+         in the system, replacing the original /proc/pci interface.
+
+       - setpci: allows to read from and write to PCI device configuration
+         registers. For example, you can adjust the latency timers with it.
+
+   See manual pages for more details.
 
    If you have any bug reports or suggestions, send them to the author.
 
diff --git a/lspci.8 b/lspci.8
index 3ccedb427af9785ea39274c32f951b27fcb21df2..5601554bed750b030c19411ea9339bf5202fa91d 100644 (file)
--- a/lspci.8
+++ b/lspci.8
@@ -1,4 +1,4 @@
-.TH lspci 8 "15 February 98" "pciutils-1.02" "Linux PCI Utilities"
+.TH lspci 8 "19 April 98" "pciutils-1.03" "Linux PCI Utilities"
 .IX lspci
 .SH NAME
 lspci \- list all PCI devices
diff --git a/lspci.c b/lspci.c
index f59ce9121e601d2a878e148c44edf1beb0a162aa..90b2e581000127ba18e0d77678810fd99a9936c2 100644 (file)
--- a/lspci.c
+++ b/lspci.c
@@ -1,5 +1,5 @@
 /*
- *     $Id: lspci.c,v 1.10 1998/03/31 21:02:16 mj Exp $
+ *     $Id: lspci.c,v 1.11 1998/04/19 11:02:27 mj Exp $
  *
  *     Linux PCI Utilities -- List All PCI Devices
  *
@@ -58,7 +58,7 @@ struct device {
   byte bus, devfn;
   word vendid, devid;
   unsigned int kernel_irq;
-  unsigned long kernel_base_addr[6];
+  unsigned long kernel_base_addr[6], kernel_rom_base_addr;
   byte config[256];
 };
 
@@ -98,7 +98,8 @@ scan_dev_list(void)
       struct device *d = xmalloc(sizeof(struct device));
       unsigned int dfn, vend;
 
-      sscanf(line, "%x %x %x %lx %lx %lx %lx %lx %lx",
+      bzero(d, sizeof(*d));
+      sscanf(line, "%x %x %x %lx %lx %lx %lx %lx %lx %lx",
             &dfn,
             &vend,
             &d->kernel_irq,
@@ -107,7 +108,8 @@ scan_dev_list(void)
             &d->kernel_base_addr[2],
             &d->kernel_base_addr[3],
             &d->kernel_base_addr[4],
-            &d->kernel_base_addr[5]);
+            &d->kernel_base_addr[5],
+            &d->kernel_rom_base_addr);
       d->bus = dfn >> 8U;
       d->devfn = dfn & 0xff;
       d->vendid = vend >> 16U;
@@ -313,16 +315,13 @@ show_bases(struct device *d, int cnt)
 static void
 show_htype0(struct device *d)
 {
-  u32 rom = get_conf_long(d, PCI_ROM_ADDRESS);
+  unsigned long rom = buscentric_view ? get_conf_long(d, PCI_ROM_ADDRESS) : d->kernel_rom_base_addr;
 
   show_bases(d, 6);
 
   if (rom & 1)
-    {
-      word cmd = get_conf_word(d, PCI_COMMAND);
-      printf("\tExpansion ROM at %08x%s\n", rom & ~0xfff,
-            (cmd & PCI_COMMAND_MEMORY) ? "" : " [disabled]");
-    }
+    printf("\tExpansion ROM at %08lx%s\n", rom & PCI_ROM_ADDRESS_MASK,
+          (rom & PCI_ROM_ADDRESS_ENABLE) ? "" : " [disabled]");
 }
 
 static void
@@ -337,7 +336,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;
-  u32 rom = get_conf_long(d, PCI_ROM_ADDRESS1);
+  unsigned long rom = buscentric_view ? get_conf_long(d, PCI_ROM_ADDRESS) : d->kernel_rom_base_addr;
   word brc = get_conf_word(d, PCI_BRIDGE_CONTROL);
 
   show_bases(d, 2);
@@ -394,11 +393,8 @@ show_htype1(struct device *d)
     printf("\tSecondary status: SERR\n");
 
   if (rom & 1)
-    {
-      word cmd = get_conf_word(d, PCI_COMMAND);
-      printf("\tExpansion ROM at %08x%s\n", rom & ~0xfff,
-            (cmd & PCI_COMMAND_MEMORY) ? "" : " [disabled]");
-    }
+    printf("\tExpansion ROM at %08lx%s\n", rom & PCI_ROM_ADDRESS_MASK,
+          (rom & PCI_ROM_ADDRESS_ENABLE) ? "" : " [disabled]");
 
   if (verbose > 1)
     printf("\tBridgeCtl: Parity%c SERR%c NoISA%c VGA%c MAbort%c >Reset%c FastB2B%c\n",
index 63d6f3a9ca1609cd3d6613e3defe7fd0179cb665..a6c7639bfb18c5f765d007947808665de8d76a43 100644 (file)
@@ -1,15 +1,15 @@
 Begin3
 Title:          Linux PCI Utilities
-Version:        1.02
-Entered-date:   980215
+Version:        1.03
+Entered-date:   980419
 Description:    This package contains various utilities for inspecting and
                setting of devices connected to the PCI bus. Requires
                kernel version 2.1.82 or newer (supporting the /proc/bus/pci
                interface).
-Keywords:       kernel, pci, proc, lspci
+Keywords:       kernel, pci, proc, lspci, setpci
 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/hardware/pciutils-1.02.tar.gz
+Primary-site:   atrey.karlin.mff.cuni.cz pub/linux/pci/pciutils-1.03.tar.gz
+Alternate-site: sunsite.unc.edu pub/Linux/hardware/pciutils-1.03.tar.gz
 Copying-policy: GPL
 End
diff --git a/setpci.8 b/setpci.8
new file mode 100644 (file)
index 0000000..836cfb2
--- /dev/null
+++ b/setpci.8
@@ -0,0 +1,161 @@
+.TH setpci 8 "19 April 98" "pciutils-1.03" "Linux PCI Utilities"
+.IX setpci
+.SH NAME
+lspci \- configure PCI devices
+.SH SYNOPSIS
+.B setpci
+.RB [ options ]
+.B devices
+.BR operations ...
+
+.SH DESCRIPTION
+.PP
+.B setpci
+is a utility for querying and configuring PCI devices. It requires Linux kernel 2.1.82
+or newer as it uses the /proc/bus/pci interface.
+.PP
+All numbers are entered in hexadecimal notation.
+
+.SH OPTIONS
+.TP
+.B -v
+Tells
+.I setpci
+to be verbose and display detailed information about configuration space accesses.
+.TP
+.B -f
+Tells
+.I lspci
+not to complain when there's nothing to do (when no devices are selected).
+This option is intended for use in widely-distributed configuration scripts
+where it's uncertain whether the device in question is present in the machine
+or not.
+.TP
+.B -D
+`Demo mode' -- simulate configuration space accesses instead of really doing them.
+It's useful to try
+.B setpci -vD
+to see what your complex sequence of
+.B setpci
+operations does before you actually execute it.
+
+.SH DEVICE SELECTION
+.PP
+Before each sequence of operations you need to select which devices you wish that
+operation to affect.
+.TP
+.B -s [[<bus>]:][<slot>][.[<func>]]
+Select devices in specified bus, slot and function. Each component of the device
+address can be omitted or set as "*" meaning "any value". All numbers are
+hexadecimal.  E.g., "0:" means all devices on bus 0, "0" means all functions of device 0
+on any bus, "0.3" selects third function of device 0 on all busses and ".4" selects only
+fourth function of each device.
+.TP
+.B -d [<vendor>]:[<device>]
+Select devices with specified vendor and device ID. Both ID's are given in
+hexadecimal and may be omitted or given as "*" meaning "any value".
+
+.SH OPERATIONS
+.PP
+To query value of a configuration register, just name it (either by typing its name or
+by typing register address with optional
+.BR .B ,
+.B .W
+or
+.B .L
+suffix specifying register width as byte, word or longword).
+.PP
+To set a register, write
+.BR reg = values
+where
+.B reg
+is the same you would use to query the register and
+.B values
+is a comma-separated list of values you want to write starting with the given
+address.
+
+.SH REGISTER NAMES
+.PP
+.B setpci
+knows the following configuration register names. See PCI bus specs for their precise
+meaning or consult
+.B /usr/include/linux/pci.h
+for few comments.
+.PP
+.nf
+VENDOR_ID
+DEVICE_ID
+COMMAND
+STATUS
+REVISION
+CLASS_PROG
+CLASS_DEVICE
+CACHE_LINE_SIZE
+LATENCY_TIMER
+HEADER_TYPE
+BIST
+BASE_ADDRESS_0
+BASE_ADDRESS_1
+BASE_ADDRESS_2
+BASE_ADDRESS_3
+BASE_ADDRESS_4
+BASE_ADDRESS_5
+CARDBUS_CIS
+SUBSYSTEM_VENDOR_ID
+SUBSYSTEM_ID
+ROM_ADDRESS
+INTERRUPT_LINE
+INTERRUPT_PIN
+MIN_GNT
+MAX_LAT
+PRIMARY_BUS
+SECONDARY_BUS
+SUBORDINATE_BUS
+SEC_LATENCY_TIMER
+IO_BASE
+IO_LIMIT
+SEC_STATUS
+MEMORY_BASE
+MEMORY_LIMIT
+PREF_MEMORY_BASE
+PREF_MEMORY_LIMIT
+PREF_BASE_UPPER32
+PREF_LIMIT_UPPER32
+IO_BASE_UPPER16
+IO_LIMIT_UPPER16
+BRIDGE_ROM_ADDRESS
+BRIDGE_CONTROL
+CB_CARDBUS_BASE
+CB_CAPABILITIES
+CB_SEC_STATUS
+CB_BUS_NUMBER
+CB_CARDBUS_NUMBER
+CB_SUBORDINATE_BUS
+CB_CARDBUS_LATENCY
+CB_MEMORY_BASE_0
+CB_MEMORY_LIMIT_0
+CB_MEMORY_BASE_1
+CB_MEMORY_LIMIT_1
+CB_IO_BASE_0
+CB_IO_BASE_0_HI
+CB_IO_LIMIT_0
+CB_IO_LIMIT_0_HI
+CB_IO_BASE_1
+CB_IO_BASE_1_HI
+CB_IO_LIMIT_1
+CB_IO_LIMIT_1_HI
+CB_SUBSYSTEM_VENDOR_ID
+CB_SUBSYSTEM_ID
+CB_LEGACY_MODE_BASE
+
+.SH EXAMPLES
+.PP
+`setpci -d *:* latency_timer=40' sets the latency timer to 64 (40 hexadecimal).
+.PP
+`setpci -s 0 device_id vendor_id' lists ID's of devices in slot 0 in all busses.
+.PP
+`setpci -s 12:3.4 34.l=1,2,3' writes longword 1 to register 34, 2 to register 35
+and 3 to register 35 of device at bus 12, slot 3, function 4.
+
+.SH AUTHOR
+The Linux PCI Utilities are maintained by Martin Mares <mj@atrey.karlin.mff.cuni.cz>.
index 189b96a984f3ae8200e0547b73eb76f54bf1d2e1..b13ddb4b4f8bb118647090e599fba12681167c31 100644 (file)
--- a/setpci.c
+++ b/setpci.c
@@ -1,5 +1,5 @@
 /*
- *     $Id: setpci.c,v 1.1 1998/03/31 21:02:20 mj Exp $
+ *     $Id: setpci.c,v 1.2 1998/04/19 11:02:29 mj Exp $
  *
  *     Linux PCI Utilities -- Manipulate PCI Configuration Registers
  *
 #include <stdlib.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <errno.h>
+#include <asm/unistd.h>
+#include <asm/byteorder.h>
 
 #include "pciutils.h"
 
 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;
+  int fd, need_write;
 };
 
 static struct device *first_dev;
@@ -51,6 +55,13 @@ xmalloc(unsigned int howmuch)
   return p;
 }
 
+/*
+ * As libc doesn't support pread/pwrite yet, we have to call them directly
+ * or use lseek/read/write instead.
+ */
+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);
+
 static void
 scan_devices(void)
 {
@@ -103,25 +114,88 @@ exec_op(struct op *op, struct device *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 (dev->fd < 0)
+  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));
-      dev->fd = open(name, O_RDWR ????
+      if ((dev->fd = open(name, dev->need_write ? O_RDWR : O_RDONLY)) < 0)
+       {
+         perror(name);
+         exit(1);
+       }
     }
 
   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)
-    {
-    }
+    printf("%02x:%02x.%x:%02x", dev->bus, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), op->addr);
+  if (op->num_values >= 0)
+    for(i=0; i<op->num_values; i++)
+      {
+       if (verbose)
+         {
+           putchar(' ');
+           printf(m, op->values[i]);
+         }
+       if (demo_mode)
+         continue;
+       switch (op->width)
+         {
+         case 1:
+           x8 = op->values[i];
+           i = pwrite(dev->fd, &x8, 1, op->addr);
+           break;
+         case 2:
+           x16 = __cpu_to_le16(op->values[i]);
+           i = pwrite(dev->fd, &x16, 2, op->addr);
+           break;
+         default:
+           x32 = __cpu_to_le32(op->values[i]);
+           i = pwrite(dev->fd, &x32, 4, op->addr);
+           break;
+         }
+       if (i != (int) op->width)
+         {
+           fprintf(stderr, "Error writing to %02x:%02x.%d: %m", dev->bus, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+           exit(1);
+         }
+      }
   else
     {
       if (verbose)
-       printf("= ");
+       printf(" = ");
+      if (!demo_mode)
+       {
+         switch (op->width)
+           {
+           case 1:
+             i = pread(dev->fd, &x8, 1, op->addr);
+             x = x8;
+             break;
+           case 2:
+             i = pread(dev->fd, &x16, 2, op->addr);
+             x = __le16_to_cpu(x16);
+             break;
+           default:
+             i = pread(dev->fd, &x32, 4, op->addr);
+             x = __le32_to_cpu(x32);
+             break;
+           }
+         if (i != (int) op->width)
+           {
+             fprintf(stderr, "Error reading from %02x:%02x.%d: %m", dev->bus, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+             exit(1);
+           }
+         printf(m, x);
+       }
+      else
+       putchar('?');
     }
+  putchar('\n');
 }
 
 static void
@@ -142,17 +216,112 @@ 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;
+       }
+      op = op->next;
+    }
+}
+
+struct reg_name {
+  int offset;
+  int width;
+  char *name;
+};
+
+static struct reg_name pci_reg_names[] = {
+  { 0x00, 2, "VENDOR_ID", },
+  { 0x02, 2, "DEVICE_ID", },
+  { 0x04, 2, "COMMAND", },
+  { 0x06, 2, "STATUS", },
+  { 0x08, 1, "REVISION", },
+  { 0x09, 1, "CLASS_PROG", },
+  { 0x0a, 2, "CLASS_DEVICE", },
+  { 0x0c, 1, "CACHE_LINE_SIZE", },
+  { 0x0d, 1, "LATENCY_TIMER", },
+  { 0x0e, 1, "HEADER_TYPE", },
+  { 0x0f, 1, "BIST", },
+  { 0x10, 4, "BASE_ADDRESS_0", },
+  { 0x14, 4, "BASE_ADDRESS_1", },
+  { 0x18, 4, "BASE_ADDRESS_2", },
+  { 0x1c, 4, "BASE_ADDRESS_3", },
+  { 0x20, 4, "BASE_ADDRESS_4", },
+  { 0x24, 4, "BASE_ADDRESS_5", },
+  { 0x28, 4, "CARDBUS_CIS", },
+  { 0x2c, 4, "SUBSYSTEM_VENDOR_ID", },
+  { 0x2e, 2, "SUBSYSTEM_ID", },
+  { 0x30, 4, "ROM_ADDRESS", },
+  { 0x3c, 1, "INTERRUPT_LINE", },
+  { 0x3d, 1, "INTERRUPT_PIN", },
+  { 0x3e, 1, "MIN_GNT", },
+  { 0x3f, 1, "MAX_LAT", },
+  { 0x18, 1, "PRIMARY_BUS", },
+  { 0x19, 1, "SECONDARY_BUS", },
+  { 0x1a, 1, "SUBORDINATE_BUS", },
+  { 0x1b, 1, "SEC_LATENCY_TIMER", },
+  { 0x1c, 1, "IO_BASE", },
+  { 0x1d, 1, "IO_LIMIT", },
+  { 0x1e, 2, "SEC_STATUS", },
+  { 0x20, 2, "MEMORY_BASE", },
+  { 0x22, 2, "MEMORY_LIMIT", },
+  { 0x24, 2, "PREF_MEMORY_BASE", },
+  { 0x26, 2, "PREF_MEMORY_LIMIT", },
+  { 0x28, 4, "PREF_BASE_UPPER32", },
+  { 0x2c, 4, "PREF_LIMIT_UPPER32", },
+  { 0x30, 2, "IO_BASE_UPPER16", },
+  { 0x32, 2, "IO_LIMIT_UPPER16", },
+  { 0x38, 4, "BRIDGE_ROM_ADDRESS", },
+  { 0x3e, 2, "BRIDGE_CONTROL", },
+  { 0x10, 4, "CB_CARDBUS_BASE", },
+  { 0x14, 2, "CB_CAPABILITIES", },
+  { 0x16, 2, "CB_SEC_STATUS", },
+  { 0x18, 1, "CB_BUS_NUMBER", },
+  { 0x19, 1, "CB_CARDBUS_NUMBER", },
+  { 0x1a, 1, "CB_SUBORDINATE_BUS", },
+  { 0x1b, 1, "CB_CARDBUS_LATENCY", },
+  { 0x1c, 4, "CB_MEMORY_BASE_0", },
+  { 0x20, 4, "CB_MEMORY_LIMIT_0", },
+  { 0x24, 4, "CB_MEMORY_BASE_1", },
+  { 0x28, 4, "CB_MEMORY_LIMIT_1", },
+  { 0x2c, 2, "CB_IO_BASE_0", },
+  { 0x2e, 2, "CB_IO_BASE_0_HI", },
+  { 0x30, 2, "CB_IO_LIMIT_0", },
+  { 0x32, 2, "CB_IO_LIMIT_0_HI", },
+  { 0x34, 2, "CB_IO_BASE_1", },
+  { 0x36, 2, "CB_IO_BASE_1_HI", },
+  { 0x38, 2, "CB_IO_LIMIT_1", },
+  { 0x3a, 2, "CB_IO_LIMIT_1_HI", },
+  { 0x40, 2, "CB_SUBSYSTEM_VENDOR_ID", },
+  { 0x42, 2, "CB_SUBSYSTEM_ID", },
+  { 0x44, 4, "CB_LEGACY_MODE_BASE", },
+  { 0x00, 0, NULL }
+};
+
 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\
+"Usage: setpci [-fvD] (<device>+ <reg>[=<values>]*)*\n\
+-f\t\tDon't complain if there's nothing to do\n\
+-v\t\tBe verbose\n\
+-D\t\tList changes, don't commit them\n\
+<device>:\t-s [[<bus>]:][<slot>][.[<func>]]\n\
+\t|\t-d [<vendor>]:[<device>]\n\
+<reg>:\t\t<number>[.(B|W|L)]\n\
+     |\t\t<name>\n\
+<values>:\t<value>[,<value>...]\n\
 ");
   exit(1);
 }
@@ -181,6 +350,10 @@ main(int argc, char **argv)
            force++;
            c++;
            break;
+         case 'D':
+           demo_mode++;
+           c++;
+           break;
          case 0:
            break;
          default:
@@ -255,6 +428,8 @@ next:
          if (d)
            {
              *d++ = 0;
+             if (!*d)
+               usage();
              for(e=d, n=1; *e; e++)
                if (*e == ',')
                  n++;
@@ -288,11 +463,28 @@ next:
          else
            op->width = 1;
          ll = strtol(c, &f, 16);
-         if (ll > 0x100 || ll + op->width*n > 0x100)
+         if (f && *f)
+           {
+             struct reg_name *r;
+             for(r = pci_reg_names; r->name; r++)
+               if (!strcasecmp(r->name, c))
+                 break;
+             if (!r->name || e)
+               usage();
+             ll = r->offset;
+             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;
            }
+         if (ll & (op->width - 1))
+           {
+             fprintf(stderr, "setpci: Unaligned register address!\n");
+             return 1;
+           }
+         op->addr = ll;
          for(i=0; i<n; i++)
            {
              e = strchr(d, ',');
@@ -316,6 +508,7 @@ next:
   if (state == STATE_INIT)
     usage();
 
+  scan_ops(first_op);
   execute(first_op);
 
   return 0;