1 /* 2 * The PCI Utilities -- Show Kernel Drivers 3 * 4 * Copyright (c) 1997--2013 Martin Mares <[email protected]> 5 * 6 * Can be freely distributed and used under the terms of the GNU GPL v2+. 7 * 8 * SPDX-License-Identifier: GPL-2.0-or-later 9 */ 10 11 #include <stdio.h> 12 #include <string.h> 13 14 #include "lspci.h" 15 16 #ifdef PCI_OS_LINUX 17 18 #include <sys/utsname.h> 19 20 #ifdef PCI_USE_LIBKMOD 21 22 #include <libkmod.h> 23 24 static struct kmod_ctx *kmod_ctx; 25 26 static int 27 show_kernel_init(void) 28 { 29 static int show_kernel_inited = -1; 30 if (show_kernel_inited >= 0) 31 return show_kernel_inited; 32 33 kmod_ctx = kmod_new(NULL, NULL); 34 if (!kmod_ctx) 35 { 36 fprintf(stderr, "lspci: Unable to initialize libkmod context\n"); 37 goto failed; 38 } 39 40 int err; 41 if ((err = kmod_load_resources(kmod_ctx)) < 0) 42 { 43 fprintf(stderr, "lspci: Unable to load libkmod resources: error %d\n", err); 44 goto failed; 45 } 46 47 show_kernel_inited = 1; 48 return 1; 49 50 failed: 51 show_kernel_inited = 0; 52 return 0; 53 } 54 55 void 56 show_kernel_cleanup(void) 57 { 58 if (kmod_ctx) 59 kmod_unref(kmod_ctx); 60 } 61 62 static const char *next_module(struct device *d) 63 { 64 static struct kmod_list *klist, *kcurrent; 65 static struct kmod_module *kmodule; 66 67 if (kmodule) 68 { 69 kmod_module_unref(kmodule); 70 kmodule = NULL; 71 } 72 73 if (!klist) 74 { 75 pci_fill_info(d->dev, PCI_FILL_MODULE_ALIAS); 76 if (!d->dev->module_alias) 77 return NULL; 78 int err = kmod_module_new_from_lookup(kmod_ctx, d->dev->module_alias, &klist); 79 if (err < 0) 80 { 81 fprintf(stderr, "lspci: libkmod lookup failed: error %d\n", err); 82 return NULL; 83 } 84 kcurrent = klist; 85 } 86 else 87 kcurrent = kmod_list_next(klist, kcurrent); 88 89 if (kcurrent) 90 { 91 kmodule = kmod_module_get_module(kcurrent); 92 return kmod_module_get_name(kmodule); 93 } 94 95 kmod_module_unref_list(klist); 96 klist = NULL; 97 return NULL; 98 } 99 100 #else 101 102 struct pcimap_entry { 103 struct pcimap_entry *next; 104 unsigned int vendor, device; 105 unsigned int subvendor, subdevice; 106 unsigned int class, class_mask; 107 char module[1]; 108 }; 109 110 static struct pcimap_entry *pcimap_head; 111 112 static int 113 show_kernel_init(void) 114 { 115 static int tried_pcimap; 116 struct utsname uts; 117 char *name, line[1024]; 118 FILE *f; 119 120 if (tried_pcimap) 121 return 1; 122 tried_pcimap = 1; 123 124 if (name = opt_pcimap) 125 { 126 f = fopen(name, "r"); 127 if (!f) 128 die("Cannot open pcimap file %s: %m", name); 129 } 130 else 131 { 132 if (uname(&uts) < 0) 133 die("uname() failed: %m"); 134 name = alloca(64 + strlen(uts.release)); 135 sprintf(name, "/lib/modules/%s/modules.pcimap", uts.release); 136 f = fopen(name, "r"); 137 if (!f) 138 return 1; 139 } 140 141 while (fgets(line, sizeof(line), f)) 142 { 143 char *c = strchr(line, '\n'); 144 struct pcimap_entry *e; 145 146 if (!c) 147 die("Unterminated or too long line in %s", name); 148 *c = 0; 149 if (!line[0] || line[0] == '#') 150 continue; 151 152 c = line; 153 while (*c && *c != ' ' && *c != '\t') 154 c++; 155 if (!*c) 156 continue; /* FIXME: Emit warnings! */ 157 *c++ = 0; 158 159 e = xmalloc(sizeof(*e) + strlen(line)); 160 if (sscanf(c, "%i%i%i%i%i%i", 161 &e->vendor, &e->device, 162 &e->subvendor, &e->subdevice, 163 &e->class, &e->class_mask) != 6) 164 continue; 165 e->next = pcimap_head; 166 pcimap_head = e; 167 strcpy(e->module, line); 168 } 169 fclose(f); 170 171 return 1; 172 } 173 174 static int 175 match_pcimap(struct device *d, struct pcimap_entry *e) 176 { 177 struct pci_dev *dev = d->dev; 178 unsigned int class = (((unsigned int)dev->device_class << 8) | dev->prog_if); 179 180 #define MATCH(x, y) ((y) > 0xffff || (x) == (y)) 181 return 182 MATCH(dev->vendor_id, e->vendor) && 183 MATCH(dev->device_id, e->device) && 184 MATCH(dev->subsys_vendor_id, e->subvendor) && 185 MATCH(dev->subsys_id, e->subdevice) && 186 (class & e->class_mask) == e->class; 187 #undef MATCH 188 } 189 190 static const char *next_module(struct device *d) 191 { 192 static struct pcimap_entry *current; 193 194 if (!current) 195 current = pcimap_head; 196 else 197 current = current->next; 198 199 while (current) 200 { 201 if (match_pcimap(d, current)) 202 return current->module; 203 current = current->next; 204 } 205 206 return NULL; 207 } 208 209 void 210 show_kernel_cleanup(void) 211 { 212 } 213 214 #endif 215 216 static const char * 217 next_module_filtered(struct device *d) 218 { 219 static char prev_module[256]; 220 const char *module; 221 222 while (module = next_module(d)) 223 { 224 if (strcmp(module, prev_module)) 225 { 226 strncpy(prev_module, module, sizeof(prev_module)); 227 prev_module[sizeof(prev_module) - 1] = 0; 228 return module; 229 } 230 } 231 prev_module[0] = 0; 232 return NULL; 233 } 234 235 void 236 show_kernel(struct device *d) 237 { 238 const char *driver, *module; 239 240 pci_fill_info(d->dev, PCI_FILL_DRIVER); 241 if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) 242 printf("\tKernel driver in use: %s\n", driver); 243 244 if (!show_kernel_init()) 245 return; 246 247 int cnt = 0; 248 while (module = next_module_filtered(d)) 249 printf("%s %s", (cnt++ ? "," : "\tKernel modules:"), module); 250 if (cnt) 251 putchar('\n'); 252 } 253 254 void 255 show_kernel_machine(struct device *d) 256 { 257 const char *driver, *module; 258 259 pci_fill_info(d->dev, PCI_FILL_DRIVER); 260 if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) 261 printf("Driver:\t%s\n", driver); 262 263 if (!show_kernel_init()) 264 return; 265 266 while (module = next_module_filtered(d)) 267 printf("Module:\t%s\n", module); 268 } 269 270 #else 271 272 void 273 show_kernel(struct device *d) 274 { 275 const char *driver; 276 277 pci_fill_info(d->dev, PCI_FILL_DRIVER); 278 if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) 279 printf("\tDriver in use: %s\n", driver); 280 } 281 282 void 283 show_kernel_machine(struct device *d) 284 { 285 const char *driver; 286 287 pci_fill_info(d->dev, PCI_FILL_DRIVER); 288 if (driver = pci_get_string_property(d->dev, PCI_FILL_DRIVER)) 289 printf("Driver:\t%s\n", driver); 290 } 291 292 void 293 show_kernel_cleanup(void) 294 { 295 } 296 297 #endif 298 299