--- pciutils-2.2.4/lspci.c +++ pciutils-2.2.4/lspci.c @@ -113,8 +113,10 @@ scan_device(struct pci_dev *p) d->config = xmalloc(64); d->present = xmalloc(64); memset(d->present, 1, 64); - if (!pci_read_block(p, 0, d->config, 64)) - die("Unable to read the standard configuration space header"); + if (!pci_read_block(p, 0, d->config, 64)) { + fprintf(stderr, "Unable to read the standard configuration space header"); + return NULL; + } if ((d->config[PCI_HEADER_TYPE] & 0x7f) == PCI_HEADER_TYPE_CARDBUS) { /* For cardbus bridges, we need to fetch 64 bytes more to get the --- pciutils-2.2.4/Makefile +++ pciutils-2.2.4/Makefile @@ -24,7 +24,7 @@ export -all: $(PCILIB) lspci setpci lspci.8 setpci.8 update-pciids update-pciids.8 $(PCI_IDS) +all: $(PCILIB) lspci setpci pcimodules lspci.8 setpci.8 update-pciids update-pciids.8 pcimodules.8 $(PCI_IDS) $(PCILIB): $(PCIINC) force $(MAKE) -C lib all @@ -34,9 +34,11 @@ lib/config.h lib/config.mk: cd lib && ./configure "$(IDSDIR)" "$(VERSION)" "$(HOST)" "$(RELEASE)" "$(ZLIB)" +pcimodules: pcimodules.o common.o $(PCILIB) lspci: lspci.o common.o $(PCILIB) setpci: setpci.o common.o $(PCILIB) +pcimodules.o: pcimodules.c pciutils.h $(PCIINC) lspci.o: lspci.c pciutils.h $(PCIINC) setpci.o: setpci.c pciutils.h $(PCIINC) common.o: common.c pciutils.h $(PCIINC) @@ -58,15 +60,15 @@ install: all # -c is ignored on Linux, but required on FreeBSD $(DIRINSTALL) -m 755 $(DESTDIR)$(SBINDIR) $(DESTDIR)$(IDSDIR) $(DESTDIR)$(MANDIR)/man8 - $(INSTALL) -c -m 755 -s lspci setpci $(DESTDIR)$(SBINDIR) + $(INSTALL) -c -m 755 lspci setpci pcimodules $(DESTDIR)$(SBINDIR) $(INSTALL) -c -m 755 update-pciids $(DESTDIR)$(SBINDIR) $(INSTALL) -c -m 644 $(PCI_IDS) $(DESTDIR)$(IDSDIR) - $(INSTALL) -c -m 644 lspci.8 setpci.8 update-pciids.8 $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -c -m 644 lspci.8 setpci.8 update-pciids.8 pcimodules.8 $(DESTDIR)$(MANDIR)/man8 uninstall: all - rm -f $(DESTDIR)$(SBINDIR)/lspci $(DESTDIR)$(SBINDIR)/setpci $(DESTDIR)$(SBINDIR)/update-pciids + rm -f $(DESTDIR)$(SBINDIR)/lspci $(DESTDIR)$(SBINDIR)/setpci $(DESTDIR)$(SBINDIR)/update-pciids $(DESTDIR)$(SBINDIR)/pcimodules rm -f $(DESTDIR)$(IDSDIR)/$(PCI_IDS) - rm -f $(DESTDIR)$(MANDIR)/man8/lspci.8 $(DESTDIR)$(MANDIR)/man8/setpci.8 $(DESTDIR)$(MANDIR)/man8/update-pciids.8 + rm -f $(DESTDIR)$(MANDIR)/man8/lspci.8 $(DESTDIR)$(MANDIR)/man8/setpci.8 $(DESTDIR)$(MANDIR)/man8/update-pciids.8 $(DESTDIR)$(MANDIR)/man8/pcimodules.8 pci.ids.gz: pci.ids gzip -9 <$< >$@ --- pciutils-2.2.4/pcimodules.c +++ pciutils-2.2.4/pcimodules.c @@ -0,0 +1,184 @@ +/* + * pcimodules: Load all kernel modules for PCI device currently + * plugged into any PCI slot. + * + * Copyright 2000 Yggdrasil Computing, Incorporated + * This file may be copied under the terms and conditions of version + * two of the GNU General Public License, as published by the Free + * Software Foundation (Cambridge, Massachusetts, USA). + * + * This file is based on pciutils/lib/example.c, which has the following + * authorship and copyright statement: + * + * Written by Martin Mares and put to public domain. You can do + * with it anything you want, but I don't give you any warranty. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define _GNU_SOURCE +#include + +#include "pciutils.h" + +#define MODDIR "/lib/modules" +#define PCIMAP "modules.pcimap" + +#define LINELENGTH 8000 + +#define DEVICE_ANY 0xffffffff +#define VENDOR_ANY 0xffffffff + +#include "lib/pci.h" + +struct pcimap_entry { + unsigned int vendor, subsys_vendor, dev, subsys_dev, class, class_mask; + char *module; + struct pcimap_entry *next; +}; + +static struct pcimap_entry *pcimap_list = NULL; + +const char program_name[] = "pcimodules"; + +#define OPT_STRING "h" +static struct option long_options[] = { + {"class", required_argument, NULL, 'c'}, + {"classmask", required_argument, NULL, 'm'}, + {"help", no_argument, NULL, 'h'}, + { 0, 0, 0, 0} +}; + +static unsigned long desired_class; +static unsigned long desired_classmask; /* Default is 0: accept all classes.*/ + +static void +read_pcimap(void) +{ + struct utsname utsname; + char filename[MAXPATHLEN]; + FILE *pcimap_file; + char line[LINELENGTH]; + struct pcimap_entry *entry; + unsigned int driver_data; + char *prevmodule = ""; + char module[LINELENGTH]; + + if (uname(&utsname) < 0) { + perror("uname"); + exit(1); + } + sprintf(filename, "%s/%s/%s", MODDIR, utsname.release, PCIMAP); + if ((pcimap_file = fopen(filename, "r")) == NULL) { + perror(filename); + exit(1); + } + + while(fgets(line, LINELENGTH, pcimap_file) != NULL) { + if (line[0] == '#') + continue; + + entry = xmalloc(sizeof(struct pcimap_entry)); + + if (sscanf(line, "%s 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + module, + &entry->vendor, &entry->dev, + &entry->subsys_vendor, &entry->subsys_dev, + &entry->class, &entry->class_mask, + &driver_data) != 8) { + fprintf (stderr, + "modules.pcimap unparsable line: %s.\n", line); + free(entry); + continue; + } + + /* Optimize memory allocation a bit, in case someday we + have Linux systems with ~100,000 modules. It also + allows us to just compare pointers to avoid trying + to load a module twice. */ + if (strcmp(module, prevmodule) != 0) { + prevmodule = xmalloc(strlen(module)+1); + strcpy(prevmodule, module); + } + entry->module = prevmodule; + entry->next = pcimap_list; + pcimap_list = entry; + } + fclose(pcimap_file); +} + +/* Return a filled in pci_access->dev tree, with the device classes + stored in dev->aux. +*/ +static void +match_pci_modules(void) +{ + struct pci_access *pacc; + struct pci_dev *dev; + unsigned int class, subsys_dev, subsys_vendor; + struct pcimap_entry *map; + const char *prevmodule = ""; + + pacc = pci_alloc(); /* Get the pci_access structure */ + /* Set all options you want -- here we stick with the defaults */ + pci_init(pacc); /* Initialize the PCI library */ + pci_scan_bus(pacc); /* We want to get the list of devices */ + for(dev=pacc->devices; dev; dev=dev->next) { + pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES); + class = (pci_read_word(dev, PCI_CLASS_DEVICE) << 8) + | pci_read_byte(dev, PCI_CLASS_PROG); + subsys_dev = pci_read_word(dev, PCI_SUBSYSTEM_ID); + subsys_vendor = pci_read_word(dev,PCI_SUBSYSTEM_VENDOR_ID); + for(map = pcimap_list; map != NULL; map = map->next) { + if (((map->class ^ class) & map->class_mask) == 0 && + ((desired_class ^ class) & desired_classmask)==0 && + (map->dev == DEVICE_ANY || + map->dev == dev->device_id) && + (map->vendor == VENDOR_ANY || + map->vendor == dev->vendor_id) && + (map->subsys_dev == DEVICE_ANY || + map->subsys_dev == subsys_dev) && + (map->subsys_vendor == VENDOR_ANY || + map->subsys_vendor == subsys_vendor) && + prevmodule != map->module) { + printf("%s\n", map->module); + prevmodule = map->module; + } + } + + } + pci_cleanup(pacc); +} + +int +main (int argc, char **argv) +{ + int opt_index = 0; + int opt; + + while ((opt = getopt_long(argc, argv, OPT_STRING, long_options, + &opt_index)) != -1) { + switch(opt) { + case 'c': + desired_class = strtol(optarg, NULL, 0); + break; + case 'm': + desired_classmask = strtol(optarg, NULL, 0); + break; + case 'h': + printf ("Usage: pcimodules [--help]\n" + " Lists kernel modules corresponding to PCI devices currently plugged" + " into the computer.\n"); + } + } + + read_pcimap(); + match_pci_modules(); + return 0; +} --- pciutils-2.2.4/pcimodules.man +++ pciutils-2.2.4/pcimodules.man @@ -0,0 +1,92 @@ +.TH pcimodules 8 "@TODAY@" "@VERSION@" "Linux PCI Utilities" +.IX pcimodules +.SH NAME +pcimodules \- List kernel driver modules available for all currently plugged +in PCI devices +.SH SYNOPSIS +.B pcimodules +.RB [ --class class_id ] +.RB [ --classmask mask ] +.RB [ --help ] +.SH DESCRIPTION +.B pcimodules +lists all driver modules for all currently plugged in PCI devices. +.B pcimodules +should be run at boot time, and whenever a PCI device is "hot plugged" +into the system. This can be done by the following Bourne shell syntax: +.IP + for module in $(pcimodules) ; do +.IP + modprobe -s -k "$module" +.IP + done +.PP +When a PCI device is removed from the system, the Linux kernel will +decrement a usage count on PCI driver module. If this count drops +to zero (i.e., there are no PCI drivers), then the +.B modprobe -r +process that is normally configured to run from cron every few minutes +will eventually remove the unneeded module. +.PP +The --class and --classmask arguments can be used to limit the search +to certain classes of PCI devices. This is useful, for example, to +generate a list of ethernet card drivers to be loaded when the kernel +has indicated that it is trying to resolve an unknown network interface. +.PP +Modules are listed in the order in which the PCI devices are physically +arranged so that the computer owner can arrange things like having scsi +device 0 be on a controller that is not alphabetically the first scsi +controller. +.SH OPTIONS +.TP +.B --class class --classmask mask +.PP +--class and --classmask limit the search to PCI +cards in particular classes. These arguments are always used together. +The arguments to --class and --classmask +can be given as hexadecimal numbers by prefixing a leading "0x". +Note that the classes used by pcimodules are in "Linux" format, +meaning the class value that you see with lspci would be shifted +left eight bits, with the new low eight bits programming interface ID. +An examples of how to use class and classmask is provided below. +.B --help, -h +Print a help message and exit. +.SH EXAMPLES +.TP +pcimodules +lists all modules corresponding to currently plugged in PCI devices. +.TP +pcimodules --class 0x200000 --classmask 0xffff00 +lists all modules corresponding to currently plugged in ethernet PCI devices. +.SH FILES +.TP +.B /lib/modules//modules.pcimap +This file is automatically generated by +.B depmod, +and used by +.B pcimodules +to determine which modules correspond to which PCI ID's. +.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 lspci (8) + +.SH MAINTAINER +The Linux PCI Utilities are maintained by Martin Mares . + +.SH AUTHOR +.B pcimodules +was written by Adam J. Richter , based on public +domain example code by Martin Mares . + +.SH COPYRIGHT +.B pcimodules +is copyright 2000, Yggdrasil Computing, Incorporated, and may +be copied under the terms and conditions of version 2 of the GNU +General Public License as published by the Free Software Foundation +(Cambrige, Massachusetts, United States of America).