diff options
Diffstat (limited to 'ls-kernel.c')
-rw-r--r-- | ls-kernel.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/ls-kernel.c b/ls-kernel.c new file mode 100644 index 0000000..c939c52 --- /dev/null +++ b/ls-kernel.c @@ -0,0 +1,298 @@ +/* + * The PCI Utilities -- Show Kernel Drivers + * + * Copyright (c) 1997--2013 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL v2+. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <stdio.h> +#include <string.h> + +#include "lspci.h" + +#ifdef PCI_OS_LINUX + +#include <sys/utsname.h> + +#ifdef PCI_USE_LIBKMOD + +#include <libkmod.h> + +static struct kmod_ctx *kmod_ctx; + +static int +show_kernel_init(void) +{ + static int show_kernel_inited = -1; + if (show_kernel_inited >= 0) + return show_kernel_inited; + + kmod_ctx = kmod_new(NULL, NULL); + if (!kmod_ctx) + { + fprintf(stderr, "lspci: Unable to initialize libkmod context\n"); + goto failed; + } + + int err; + if ((err = kmod_load_resources(kmod_ctx)) < 0) + { + fprintf(stderr, "lspci: Unable to load libkmod resources: error %d\n", err); + goto failed; + } + + show_kernel_inited = 1; + return 1; + +failed: + show_kernel_inited = 0; + return 0; +} + +void +show_kernel_cleanup(void) +{ + if (kmod_ctx) + kmod_unref(kmod_ctx); +} + +static const char *next_module(struct device *d) +{ + static struct kmod_list *klist, *kcurrent; + static struct kmod_module *kmodule; + + if (kmodule) + { + kmod_module_unref(kmodule); + kmodule = NULL; + } + + if (!klist) + { + pci_fill_info(d->dev, PCI_FILL_MODULE_ALIAS); + if (!d->dev->module_alias) + return NULL; + int err = kmod_module_new_from_lookup(kmod_ctx, d->dev->module_alias, &klist); + if (err < 0) + { + fprintf(stderr, "lspci: libkmod lookup failed: error %d\n", err); + return NULL; + } + kcurrent = klist; + } + else + kcurrent = kmod_list_next(klist, kcurrent); + + if (kcurrent) + { + kmodule = kmod_module_get_module(kcurrent); + return kmod_module_get_name(kmodule); + } + + kmod_module_unref_list(klist); + klist = NULL; + return NULL; +} + +#else + +struct pcimap_entry { + struct pcimap_entry *next; + unsigned int vendor, device; + unsigned int subvendor, subdevice; + unsigned int class, class_mask; + char module[1]; +}; + +static struct pcimap_entry *pcimap_head; + +static int +show_kernel_init(void) +{ + static int tried_pcimap; + struct utsname uts; + char *name, line[1024]; + FILE *f; + + if (tried_pcimap) + return 1; + tried_pcimap = 1; + + if (name = opt_pcimap) + { + f = fopen(name, "r"); + if (!f) + die("Cannot open pcimap file %s: %m", name); + } + else + { + if (uname(&uts) < 0) + die("uname() failed: %m"); + name = alloca(64 + strlen(uts.release)); + sprintf(name, "/lib/modules/%s/modules.pcimap", uts.release); + f = fopen(name, "r"); + if (!f) + return 1; + } + + while (fgets(line, sizeof(line), f)) + { + char *c = strchr(line, '\n'); + struct pcimap_entry *e; + + if (!c) + die("Unterminated or too long line in %s", name); + *c = 0; + if (!line[0] || line[0] == '#') + continue; + + c = line; + while (*c && *c != ' ' && *c != '\t') + c++; + if (!*c) + continue; /* FIXME: Emit warnings! */ + *c++ = 0; + + e = xmalloc(sizeof(*e) + strlen(line)); + if (sscanf(c, "%i%i%i%i%i%i", + &e->vendor, &e->device, + &e->subvendor, &e->subdevice, + &e->class, &e->class_mask) != 6) + continue; + e->next = pcimap_head; + pcimap_head = e; + strcpy(e->module, line); + } + fclose(f); + + return 1; +} + +static int +match_pcimap(struct device *d, struct pcimap_entry *e) +{ + struct pci_dev *dev = d->dev; + unsigned int class = (((unsigned int)dev->device_class << 8) | dev->prog_if); + +#define MATCH(x, y) ((y) > 0xffff || (x) == (y)) + return + MATCH(dev->vendor_id, e->vendor) && + MATCH(dev->device_id, e->device) && + MATCH(dev->subsys_vendor_id, e->subvendor) && + MATCH(dev->subsys_id, e->subdevice) && + (class & e->class_mask) == e->class; +#undef MATCH +} + +static const char *next_module(struct device *d) +{ + static struct pcimap_entry *current; + + if (!current) + current = pcimap_head; + else + current = current->next; + + while (current) + { + if (match_pcimap(d, current)) + return current->module; + current = current->next; + } + + return NULL; +} + +void +show_kernel_cleanup(void) +{ +} + +#endif + +static const char * +next_module_filtered(struct device *d) +{ + static char prev_module[256]; + const char *module; + + while (module = next_module(d)) + { + if (strcmp(module, prev_module)) + { + strncpy(prev_module, module, sizeof(prev_module)); + prev_module[sizeof(prev_module) - 1] = 0; + return module; + } + } + prev_module[0] = 0; + return NULL; +} + +void +show_kernel(struct device *d) +{ + const char *driver, *module; + + pci_fill_info(d->dev, PCI_FILL_DRIVER); + if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) + printf("\tKernel driver in use: %s\n", driver); + + if (!show_kernel_init()) + return; + + int cnt = 0; + while (module = next_module_filtered(d)) + printf("%s %s", (cnt++ ? "," : "\tKernel modules:"), module); + if (cnt) + putchar('\n'); +} + +void +show_kernel_machine(struct device *d) +{ + const char *driver, *module; + + pci_fill_info(d->dev, PCI_FILL_DRIVER); + if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) + printf("Driver:\t%s\n", driver); + + if (!show_kernel_init()) + return; + + while (module = next_module_filtered(d)) + printf("Module:\t%s\n", module); +} + +#else + +void +show_kernel(struct device *d) +{ + const char *driver; + + pci_fill_info(d->dev, PCI_FILL_DRIVER); + if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) + printf("\tDriver in use: %s\n", driver); +} + +void +show_kernel_machine(struct device *d) +{ + const char *driver; + + pci_fill_info(d->dev, PCI_FILL_DRIVER); + if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) + printf("Driver:\t%s\n", driver); +} + +void +show_kernel_cleanup(void) +{ +} + +#endif + |