1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
|
--- pciutils-2.2.0/lspci.c
+++ pciutils-dsd/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.0/Makefile
+++ pciutils-dsd/Makefile
@@ -33,7 +33,7 @@ RELEASE=
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
@@ -43,9 +43,11 @@ force:
lib/config.h:
cd lib && ./configure $(IDSDIR) $(VERSION) $(HOST) $(RELEASE)
+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)
@@ -66,15 +68,15 @@ distclean: clean
install: all
# -c is ignored on Linux, but required on FreeBSD
$(DIRINSTALL) -m 755 $(SBINDIR) $(IDSDIR) $(MANDIR)/man8
- $(INSTALL) -c -m 755 -s lspci setpci $(SBINDIR)
+ $(INSTALL) -c -m 755 -s lspci setpci pcimodules $(SBINDIR)
$(INSTALL) -c -m 755 update-pciids $(SBINDIR)
$(INSTALL) -c -m 644 pci.ids $(IDSDIR)
- $(INSTALL) -c -m 644 lspci.8 setpci.8 update-pciids.8 $(MANDIR)/man8
+ $(INSTALL) -c -m 644 lspci.8 setpci.8 update-pciids.8 pcimodules.8 $(MANDIR)/man8
uninstall: all
- rm -f $(SBINDIR)/lspci $(SBINDIR)/setpci $(SBINDIR)/update-pciids
+ rm -f $(SBINDIR)/lspci $(SBINDIR)/setpci $(SBINDIR)/update-pciids $(SBINDIR)/pcimodules
rm -f $(IDSDIR)/pci.ids
- rm -f $(MANDIR)/man8/lspci.8 $(MANDIR)/man8/setpci.8 $(MANDIR)/man8/update-pciids.8
+ rm -f $(MANDIR)/man8/lspci.8 $(MANDIR)/man8/setpci.8 $(MANDIR)/man8/update-pciids.8 $(MANDIR)/man8/pcimodules.8
get-ids:
cp ~/tree/pciids/pci.ids pci.ids
--- pciutils-2.2.0/pcimodules.c
+++ pciutils-dsd/pcimodules.c
@@ -0,0 +1,183 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#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;
+
+#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.*/
+
+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.0/pcimodules.man
+++ pciutils-dsd/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/<kernel-version>/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 <mj@suse.cz>.
+
+.SH AUTHOR
+.B pcimodules
+was written by Adam J. Richter <adam@yggdrasil.com>, based on public
+domain example code by Martin Mares <mj@suse.cz>.
+
+.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).
|