1c7a34993SMartin Mares /*
2c7a34993SMartin Mares * The PCI Utilities -- Show Kernel Drivers
3c7a34993SMartin Mares *
417ec7e70SMartin Mares * Copyright (c) 1997--2013 Martin Mares <[email protected]>
5c7a34993SMartin Mares *
6*61829219SMartin Mares * Can be freely distributed and used under the terms of the GNU GPL v2+.
7*61829219SMartin Mares *
8*61829219SMartin Mares * SPDX-License-Identifier: GPL-2.0-or-later
9c7a34993SMartin Mares */
10c7a34993SMartin Mares
11c7a34993SMartin Mares #include <stdio.h>
12c7a34993SMartin Mares #include <string.h>
13c7a34993SMartin Mares
14c7a34993SMartin Mares #include "lspci.h"
15c7a34993SMartin Mares
16c7a34993SMartin Mares #ifdef PCI_OS_LINUX
17c7a34993SMartin Mares
18c7a34993SMartin Mares #include <sys/utsname.h>
19c7a34993SMartin Mares
2017ec7e70SMartin Mares #ifdef PCI_USE_LIBKMOD
2117ec7e70SMartin Mares
2217ec7e70SMartin Mares #include <libkmod.h>
2317ec7e70SMartin Mares
2417ec7e70SMartin Mares static struct kmod_ctx *kmod_ctx;
2517ec7e70SMartin Mares
2617ec7e70SMartin Mares static int
show_kernel_init(void)2717ec7e70SMartin Mares show_kernel_init(void)
2817ec7e70SMartin Mares {
2917ec7e70SMartin Mares static int show_kernel_inited = -1;
3017ec7e70SMartin Mares if (show_kernel_inited >= 0)
3117ec7e70SMartin Mares return show_kernel_inited;
3217ec7e70SMartin Mares
33cc840156SVladimír Čunát kmod_ctx = kmod_new(NULL, NULL);
3417ec7e70SMartin Mares if (!kmod_ctx)
3517ec7e70SMartin Mares {
3617ec7e70SMartin Mares fprintf(stderr, "lspci: Unable to initialize libkmod context\n");
3717ec7e70SMartin Mares goto failed;
3817ec7e70SMartin Mares }
3917ec7e70SMartin Mares
4017ec7e70SMartin Mares int err;
4117ec7e70SMartin Mares if ((err = kmod_load_resources(kmod_ctx)) < 0)
4217ec7e70SMartin Mares {
4317ec7e70SMartin Mares fprintf(stderr, "lspci: Unable to load libkmod resources: error %d\n", err);
4417ec7e70SMartin Mares goto failed;
4517ec7e70SMartin Mares }
4617ec7e70SMartin Mares
4717ec7e70SMartin Mares show_kernel_inited = 1;
4817ec7e70SMartin Mares return 1;
4917ec7e70SMartin Mares
5017ec7e70SMartin Mares failed:
5117ec7e70SMartin Mares show_kernel_inited = 0;
5217ec7e70SMartin Mares return 0;
5317ec7e70SMartin Mares }
5417ec7e70SMartin Mares
5517ec7e70SMartin Mares void
show_kernel_cleanup(void)5617ec7e70SMartin Mares show_kernel_cleanup(void)
5717ec7e70SMartin Mares {
5817ec7e70SMartin Mares if (kmod_ctx)
5917ec7e70SMartin Mares kmod_unref(kmod_ctx);
6017ec7e70SMartin Mares }
6117ec7e70SMartin Mares
next_module(struct device * d)6217ec7e70SMartin Mares static const char *next_module(struct device *d)
6317ec7e70SMartin Mares {
6417ec7e70SMartin Mares static struct kmod_list *klist, *kcurrent;
6517ec7e70SMartin Mares static struct kmod_module *kmodule;
6617ec7e70SMartin Mares
6717ec7e70SMartin Mares if (kmodule)
6817ec7e70SMartin Mares {
6917ec7e70SMartin Mares kmod_module_unref(kmodule);
7017ec7e70SMartin Mares kmodule = NULL;
7117ec7e70SMartin Mares }
7217ec7e70SMartin Mares
7317ec7e70SMartin Mares if (!klist)
7417ec7e70SMartin Mares {
7517ec7e70SMartin Mares pci_fill_info(d->dev, PCI_FILL_MODULE_ALIAS);
7617ec7e70SMartin Mares if (!d->dev->module_alias)
7717ec7e70SMartin Mares return NULL;
7817ec7e70SMartin Mares int err = kmod_module_new_from_lookup(kmod_ctx, d->dev->module_alias, &klist);
7917ec7e70SMartin Mares if (err < 0)
8017ec7e70SMartin Mares {
8117ec7e70SMartin Mares fprintf(stderr, "lspci: libkmod lookup failed: error %d\n", err);
8217ec7e70SMartin Mares return NULL;
8317ec7e70SMartin Mares }
8417ec7e70SMartin Mares kcurrent = klist;
8517ec7e70SMartin Mares }
8617ec7e70SMartin Mares else
8717ec7e70SMartin Mares kcurrent = kmod_list_next(klist, kcurrent);
8817ec7e70SMartin Mares
8917ec7e70SMartin Mares if (kcurrent)
9017ec7e70SMartin Mares {
9117ec7e70SMartin Mares kmodule = kmod_module_get_module(kcurrent);
9217ec7e70SMartin Mares return kmod_module_get_name(kmodule);
9317ec7e70SMartin Mares }
9417ec7e70SMartin Mares
9517ec7e70SMartin Mares kmod_module_unref_list(klist);
9617ec7e70SMartin Mares klist = NULL;
9717ec7e70SMartin Mares return NULL;
9817ec7e70SMartin Mares }
9917ec7e70SMartin Mares
10017ec7e70SMartin Mares #else
10117ec7e70SMartin Mares
102c7a34993SMartin Mares struct pcimap_entry {
103c7a34993SMartin Mares struct pcimap_entry *next;
104c7a34993SMartin Mares unsigned int vendor, device;
105c7a34993SMartin Mares unsigned int subvendor, subdevice;
106c7a34993SMartin Mares unsigned int class, class_mask;
107c7a34993SMartin Mares char module[1];
108c7a34993SMartin Mares };
109c7a34993SMartin Mares
110c7a34993SMartin Mares static struct pcimap_entry *pcimap_head;
111c7a34993SMartin Mares
11217ec7e70SMartin Mares static int
show_kernel_init(void)11317ec7e70SMartin Mares show_kernel_init(void)
114c7a34993SMartin Mares {
115c7a34993SMartin Mares static int tried_pcimap;
116c7a34993SMartin Mares struct utsname uts;
117c7a34993SMartin Mares char *name, line[1024];
118c7a34993SMartin Mares FILE *f;
119c7a34993SMartin Mares
120c7a34993SMartin Mares if (tried_pcimap)
12117ec7e70SMartin Mares return 1;
122c7a34993SMartin Mares tried_pcimap = 1;
123c7a34993SMartin Mares
124c7a34993SMartin Mares if (name = opt_pcimap)
125c7a34993SMartin Mares {
126c7a34993SMartin Mares f = fopen(name, "r");
127c7a34993SMartin Mares if (!f)
128c7a34993SMartin Mares die("Cannot open pcimap file %s: %m", name);
129c7a34993SMartin Mares }
130c7a34993SMartin Mares else
131c7a34993SMartin Mares {
132c7a34993SMartin Mares if (uname(&uts) < 0)
133c7a34993SMartin Mares die("uname() failed: %m");
134c7a34993SMartin Mares name = alloca(64 + strlen(uts.release));
135c7a34993SMartin Mares sprintf(name, "/lib/modules/%s/modules.pcimap", uts.release);
136c7a34993SMartin Mares f = fopen(name, "r");
137c7a34993SMartin Mares if (!f)
13817ec7e70SMartin Mares return 1;
139c7a34993SMartin Mares }
140c7a34993SMartin Mares
141c7a34993SMartin Mares while (fgets(line, sizeof(line), f))
142c7a34993SMartin Mares {
143c7a34993SMartin Mares char *c = strchr(line, '\n');
144c7a34993SMartin Mares struct pcimap_entry *e;
145c7a34993SMartin Mares
146c7a34993SMartin Mares if (!c)
147c7a34993SMartin Mares die("Unterminated or too long line in %s", name);
148c7a34993SMartin Mares *c = 0;
149c7a34993SMartin Mares if (!line[0] || line[0] == '#')
150c7a34993SMartin Mares continue;
151c7a34993SMartin Mares
152c7a34993SMartin Mares c = line;
153c7a34993SMartin Mares while (*c && *c != ' ' && *c != '\t')
154c7a34993SMartin Mares c++;
155c7a34993SMartin Mares if (!*c)
156c7a34993SMartin Mares continue; /* FIXME: Emit warnings! */
157c7a34993SMartin Mares *c++ = 0;
158c7a34993SMartin Mares
159c7a34993SMartin Mares e = xmalloc(sizeof(*e) + strlen(line));
160c7a34993SMartin Mares if (sscanf(c, "%i%i%i%i%i%i",
161c7a34993SMartin Mares &e->vendor, &e->device,
162c7a34993SMartin Mares &e->subvendor, &e->subdevice,
163c7a34993SMartin Mares &e->class, &e->class_mask) != 6)
164c7a34993SMartin Mares continue;
165c7a34993SMartin Mares e->next = pcimap_head;
166c7a34993SMartin Mares pcimap_head = e;
167c7a34993SMartin Mares strcpy(e->module, line);
168c7a34993SMartin Mares }
169c7a34993SMartin Mares fclose(f);
17017ec7e70SMartin Mares
17117ec7e70SMartin Mares return 1;
172c7a34993SMartin Mares }
173c7a34993SMartin Mares
174c7a34993SMartin Mares static int
match_pcimap(struct device * d,struct pcimap_entry * e)175c7a34993SMartin Mares match_pcimap(struct device *d, struct pcimap_entry *e)
176c7a34993SMartin Mares {
177c7a34993SMartin Mares struct pci_dev *dev = d->dev;
178fb570ee3SPali Rohár unsigned int class = (((unsigned int)dev->device_class << 8) | dev->prog_if);
179c7a34993SMartin Mares
180c7a34993SMartin Mares #define MATCH(x, y) ((y) > 0xffff || (x) == (y))
181c7a34993SMartin Mares return
182c7a34993SMartin Mares MATCH(dev->vendor_id, e->vendor) &&
183c7a34993SMartin Mares MATCH(dev->device_id, e->device) &&
184fb570ee3SPali Rohár MATCH(dev->subsys_vendor_id, e->subvendor) &&
185fb570ee3SPali Rohár MATCH(dev->subsys_id, e->subdevice) &&
186c7a34993SMartin Mares (class & e->class_mask) == e->class;
187c7a34993SMartin Mares #undef MATCH
188c7a34993SMartin Mares }
189c7a34993SMartin Mares
next_module(struct device * d)19017ec7e70SMartin Mares static const char *next_module(struct device *d)
19117ec7e70SMartin Mares {
192b069b79aSMartin Mares static struct pcimap_entry *current;
19317ec7e70SMartin Mares
19417ec7e70SMartin Mares if (!current)
19517ec7e70SMartin Mares current = pcimap_head;
19617ec7e70SMartin Mares else
19717ec7e70SMartin Mares current = current->next;
19817ec7e70SMartin Mares
19917ec7e70SMartin Mares while (current)
20017ec7e70SMartin Mares {
201b069b79aSMartin Mares if (match_pcimap(d, current))
20217ec7e70SMartin Mares return current->module;
20317ec7e70SMartin Mares current = current->next;
20417ec7e70SMartin Mares }
20517ec7e70SMartin Mares
20617ec7e70SMartin Mares return NULL;
20717ec7e70SMartin Mares }
20817ec7e70SMartin Mares
20917ec7e70SMartin Mares void
show_kernel_cleanup(void)21017ec7e70SMartin Mares show_kernel_cleanup(void)
21117ec7e70SMartin Mares {
21217ec7e70SMartin Mares }
21317ec7e70SMartin Mares
21417ec7e70SMartin Mares #endif
21517ec7e70SMartin Mares
216b069b79aSMartin Mares static const char *
next_module_filtered(struct device * d)217b069b79aSMartin Mares next_module_filtered(struct device *d)
218b069b79aSMartin Mares {
219b069b79aSMartin Mares static char prev_module[256];
220b069b79aSMartin Mares const char *module;
221b069b79aSMartin Mares
222b069b79aSMartin Mares while (module = next_module(d))
223b069b79aSMartin Mares {
224b069b79aSMartin Mares if (strcmp(module, prev_module))
225b069b79aSMartin Mares {
226b069b79aSMartin Mares strncpy(prev_module, module, sizeof(prev_module));
227b069b79aSMartin Mares prev_module[sizeof(prev_module) - 1] = 0;
228b069b79aSMartin Mares return module;
229b069b79aSMartin Mares }
230b069b79aSMartin Mares }
231b069b79aSMartin Mares prev_module[0] = 0;
232b069b79aSMartin Mares return NULL;
233b069b79aSMartin Mares }
234b069b79aSMartin Mares
235c7a34993SMartin Mares void
show_kernel(struct device * d)236c7a34993SMartin Mares show_kernel(struct device *d)
237c7a34993SMartin Mares {
23817ec7e70SMartin Mares const char *driver, *module;
239c7a34993SMartin Mares
240d994587fSPali Rohár pci_fill_info(d->dev, PCI_FILL_DRIVER);
241d994587fSPali Rohár if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER))
242c7a34993SMartin Mares printf("\tKernel driver in use: %s\n", driver);
243c7a34993SMartin Mares
24417ec7e70SMartin Mares if (!show_kernel_init())
24517ec7e70SMartin Mares return;
24617ec7e70SMartin Mares
24717ec7e70SMartin Mares int cnt = 0;
248b069b79aSMartin Mares while (module = next_module_filtered(d))
24917ec7e70SMartin Mares printf("%s %s", (cnt++ ? "," : "\tKernel modules:"), module);
25017ec7e70SMartin Mares if (cnt)
251c7a34993SMartin Mares putchar('\n');
252c7a34993SMartin Mares }
253c7a34993SMartin Mares
254c7a34993SMartin Mares void
show_kernel_machine(struct device * d)255c7a34993SMartin Mares show_kernel_machine(struct device *d)
256c7a34993SMartin Mares {
25717ec7e70SMartin Mares const char *driver, *module;
258c7a34993SMartin Mares
259d994587fSPali Rohár pci_fill_info(d->dev, PCI_FILL_DRIVER);
260d994587fSPali Rohár if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER))
261c7a34993SMartin Mares printf("Driver:\t%s\n", driver);
262c7a34993SMartin Mares
26317ec7e70SMartin Mares if (!show_kernel_init())
26417ec7e70SMartin Mares return;
26517ec7e70SMartin Mares
266b069b79aSMartin Mares while (module = next_module_filtered(d))
26717ec7e70SMartin Mares printf("Module:\t%s\n", module);
268c7a34993SMartin Mares }
269c7a34993SMartin Mares
270c7a34993SMartin Mares #else
271c7a34993SMartin Mares
272c7a34993SMartin Mares void
show_kernel(struct device * d)273d994587fSPali Rohár show_kernel(struct device *d)
274c7a34993SMartin Mares {
275d994587fSPali Rohár const char *driver;
276d994587fSPali Rohár
277d994587fSPali Rohár pci_fill_info(d->dev, PCI_FILL_DRIVER);
278d994587fSPali Rohár if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER))
279d994587fSPali Rohár printf("\tDriver in use: %s\n", driver);
280c7a34993SMartin Mares }
281c7a34993SMartin Mares
282c7a34993SMartin Mares void
show_kernel_machine(struct device * d)283d994587fSPali Rohár show_kernel_machine(struct device *d)
284c7a34993SMartin Mares {
285d994587fSPali Rohár const char *driver;
286d994587fSPali Rohár
287d994587fSPali Rohár pci_fill_info(d->dev, PCI_FILL_DRIVER);
288d994587fSPali Rohár if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER))
289d994587fSPali Rohár printf("Driver:\t%s\n", driver);
290c7a34993SMartin Mares }
291c7a34993SMartin Mares
29217ec7e70SMartin Mares void
show_kernel_cleanup(void)29317ec7e70SMartin Mares show_kernel_cleanup(void)
29417ec7e70SMartin Mares {
29517ec7e70SMartin Mares }
29617ec7e70SMartin Mares
297c7a34993SMartin Mares #endif
298c7a34993SMartin Mares
299