X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=lspci.c;h=0515fcd6ad8d89c64bcc54fdf5d61da73163285b;hb=c953c3094d13235ad97b41cd5b59df1309d9367b;hp=035cbbba8f34ef8d45e58d40999ac0ac40725f91;hpb=a387042e9c12d5e924dfb19e54253db41980937a;p=pciutils.git diff --git a/lspci.c b/lspci.c index 035cbbb..0515fcd 100644 --- a/lspci.c +++ b/lspci.c @@ -1,7 +1,7 @@ /* * The PCI Utilities -- List All PCI Devices * - * Copyright (c) 1997--2007 Martin Mares + * Copyright (c) 1997--2008 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -12,6 +12,7 @@ #include #include +#define PCIUTILS_LSPCI #include "pciutils.h" /* Options */ @@ -24,28 +25,54 @@ static int opt_tree; /* Show bus tree */ static int opt_machine; /* Generate machine-readable output */ static int opt_map_mode; /* Bus mapping mode enabled */ static int opt_domains; /* Show domain numbers (0=disabled, 1=auto-detected, 2=requested) */ +static int opt_kernel; /* Show kernel drivers */ +static int opt_query_dns; /* Query the DNS (0=disabled, 1=enabled, 2=refresh cache) */ +static int opt_query_all; /* Query the DNS for all entries */ +static char *opt_pcimap; /* Override path to Linux modules.pcimap */ const char program_name[] = "lspci"; -static char options[] = "nvbxs:d:ti:mgMD" GENERIC_OPTIONS ; - -static char help_msg[] = "\ -Usage: lspci []\n\ -\n\ --v\t\tBe verbose\n\ --n\t\tShow numeric ID's\n\ --nn\t\tShow both textual and numeric ID's (names & numbers)\n\ --b\t\tBus-centric view (PCI addresses and IRQ's instead of those seen by the CPU)\n\ --x\t\tShow hex-dump of the standard portion of config space\n\ --xxx\t\tShow hex-dump of the whole config space (dangerous; root only)\n\ --xxxx\t\tShow hex-dump of the 4096-byte extended config space (root only)\n\ --s [[[[]:]]:][][.[]]\tShow only devices in selected slots\n\ --d []:[]\tShow only selected devices\n\ --t\t\tShow bus tree\n\ --m\t\tProduce machine-readable output\n\ --i \tUse specified ID database instead of %s\n\ --D\t\tAlways show domain numbers\n\ --M\t\tEnable `bus mapping' mode (dangerous; root only)\n" +static char options[] = "nvbxs:d:ti:mgp:qkMDQ" GENERIC_OPTIONS ; + +static char help_msg[] = +"Usage: lspci []\n" +"\n" +"Basic display modes:\n" +"-mm\t\tProduce machine-readable output (single -m for an obsolete format)\n" +"-t\t\tShow bus tree\n" +"\n" +"Display options:\n" +"-v\t\tBe verbose (-vv for very verbose)\n" +#ifdef PCI_OS_LINUX +"-k\t\tShow kernel drivers handling each device\n" +#endif +"-x\t\tShow hex-dump of the standard part of the config space\n" +"-xxx\t\tShow hex-dump of the whole config space (dangerous; root only)\n" +"-xxxx\t\tShow hex-dump of the 4096-byte extended config space (root only)\n" +"-b\t\tBus-centric view (addresses and IRQ's as seen by the bus)\n" +"-D\t\tAlways show domain numbers\n" +"\n" +"Resolving of device ID's to names:\n" +"-n\t\tShow numeric ID's\n" +"-nn\t\tShow both textual and numeric ID's (names & numbers)\n" +#ifdef PCI_USE_DNS +"-q\t\tQuery the PCI ID database for unknown ID's via DNS\n" +"-qq\t\tAs above, but re-query locally cached entries\n" +"-Q\t\tQuery the PCI ID database for all ID's via DNS\n" +#endif +"\n" +"Selection of devices:\n" +"-s [[[[]:]]:][][.[]]\tShow only devices in selected slots\n" +"-d []:[]\t\t\tShow only devices with specified ID's\n" +"\n" +"Other options:\n" +"-i \tUse specified ID database instead of %s\n" +#ifdef PCI_OS_LINUX +"-p \tLook up kernel modules in a given file instead of default modules.pcimap\n" +#endif +"-M\t\tEnable `bus mapping' mode (dangerous; root only)\n" +"\n" +"PCI access options:\n" GENERIC_HELP ; @@ -295,6 +322,25 @@ show_terse(struct device *d) putchar('\n'); } +static void +get_subid(struct device *d, word *subvp, word *subdp) +{ + byte htype = get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f; + + if (htype == PCI_HEADER_TYPE_NORMAL) + { + *subvp = get_conf_word(d, PCI_SUBSYSTEM_VENDOR_ID); + *subdp = get_conf_word(d, PCI_SUBSYSTEM_ID); + } + else if (htype == PCI_HEADER_TYPE_CARDBUS && d->config_cached >= 128) + { + *subvp = get_conf_word(d, PCI_CB_SUBSYSTEM_VENDOR_ID); + *subdp = get_conf_word(d, PCI_CB_SUBSYSTEM_ID); + } + else + *subvp = *subdp = 0xffff; +} + /*** Capabilities ***/ static void @@ -1436,6 +1482,12 @@ show_caps(struct device *d) case PCI_CAP_ID_MSIX: cap_msix(d, where, cap); break; + case PCI_CAP_ID_SATA: + printf("SATA HBA \n"); + break; + case PCI_CAP_ID_AF: + printf("PCIe advanced features \n"); + break; default: printf("#%02x [%04x]\n", id, cap); } @@ -1446,6 +1498,188 @@ show_caps(struct device *d) show_ext_caps(d); } +/*** Kernel drivers ***/ + +#ifdef PCI_OS_LINUX + +#include + +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 void +load_pcimap(void) +{ + static int tried_pcimap; + struct utsname uts; + char *name, line[1024]; + FILE *f; + + if (tried_pcimap) + return; + 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; + } + + 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); +} + +static int +match_pcimap(struct device *d, struct pcimap_entry *e) +{ + struct pci_dev *dev = d->dev; + unsigned int class = get_conf_long(d, PCI_REVISION_ID) >> 8; + word subv, subd; + +#define MATCH(x, y) ((y) > 0xffff || (x) == (y)) + get_subid(d, &subv, &subd); + return + MATCH(dev->vendor_id, e->vendor) && + MATCH(dev->device_id, e->device) && + MATCH(subv, e->subvendor) && + MATCH(subd, e->subdevice) && + (class & e->class_mask) == e->class; +#undef MATCH +} + +#define DRIVER_BUF_SIZE 1024 + +static char * +find_driver(struct device *d, char *buf) +{ + struct pci_dev *dev = d->dev; + char name[1024], *drv, *base; + int n; + + if (dev->access->method != PCI_ACCESS_SYS_BUS_PCI) + return NULL; + + base = pci_get_param(dev->access, "sysfs.path"); + if (!base || !base[0]) + return NULL; + + n = snprintf(name, sizeof(name), "%s/devices/%04x:%02x:%02x.%d/driver", + base, dev->domain, dev->bus, dev->dev, dev->func); + if (n < 0 || n >= (int)sizeof(name)) + die("show_driver: sysfs device name too long, why?"); + + n = readlink(name, buf, DRIVER_BUF_SIZE); + if (n < 0) + return NULL; + if (n >= DRIVER_BUF_SIZE) + return ""; + buf[n] = 0; + + if (drv = strrchr(buf, '/')) + return drv+1; + else + return buf; +} + +static void +show_kernel(struct device *d) +{ + char buf[DRIVER_BUF_SIZE]; + char *driver; + struct pcimap_entry *e, *last = NULL; + + if (driver = find_driver(d, buf)) + printf("\tKernel driver in use: %s\n", driver); + + load_pcimap(); + for (e=pcimap_head; e; e=e->next) + if (match_pcimap(d, e) && (!last || strcmp(last->module, e->module))) + { + printf("%s %s", (last ? "," : "\tKernel modules:"), e->module); + last = e; + } + if (last) + putchar('\n'); +} + +static void +show_kernel_machine(struct device *d) +{ + char buf[DRIVER_BUF_SIZE]; + char *driver; + struct pcimap_entry *e, *last = NULL; + + if (driver = find_driver(d, buf)) + printf("Driver:\t%s\n", driver); + + load_pcimap(); + for (e=pcimap_head; e; e=e->next) + if (match_pcimap(d, e) && (!last || strcmp(last->module, e->module))) + { + printf("Module:\t%s\n", e->module); + last = e; + } +} + +#else + +static void +show_kernel(struct device *d UNUSED) +{ +} + +static void +show_kernel_machine(struct device *d UNUSED) +{ +} + +#endif + /*** Verbose output ***/ static void @@ -1779,7 +2013,7 @@ show_verbose(struct device *d) byte max_lat, min_gnt; byte int_pin = get_conf_byte(d, PCI_INTERRUPT_PIN); unsigned int irq = p->irq; - word subsys_v = 0, subsys_d = 0; + word subsys_v, subsys_d; char ssnamebuf[256]; show_terse(d); @@ -1791,8 +2025,6 @@ show_verbose(struct device *d) printf("\t!!! Invalid class %04x for header type %02x\n", class, htype); max_lat = get_conf_byte(d, PCI_MAX_LAT); min_gnt = get_conf_byte(d, PCI_MIN_GNT); - subsys_v = get_conf_word(d, PCI_SUBSYSTEM_VENDOR_ID); - subsys_d = get_conf_word(d, PCI_SUBSYSTEM_ID); break; case PCI_HEADER_TYPE_BRIDGE: if ((class >> 8) != PCI_BASE_CLASS_BRIDGE) @@ -1803,17 +2035,13 @@ show_verbose(struct device *d) if ((class >> 8) != PCI_BASE_CLASS_BRIDGE) printf("\t!!! Invalid class %04x for header type %02x\n", class, htype); min_gnt = max_lat = 0; - if (d->config_cached >= 128) - { - subsys_v = get_conf_word(d, PCI_CB_SUBSYSTEM_VENDOR_ID); - subsys_d = get_conf_word(d, PCI_CB_SUBSYSTEM_ID); - } break; default: printf("\t!!! Unknown header type %02x\n", htype); return; } + get_subid(d, &subsys_v, &subsys_d); if (subsys_v && subsys_v != 0xffff) printf("\tSubsystem: %s\n", pci_lookup_name(pacc, ssnamebuf, sizeof(ssnamebuf), @@ -1962,23 +2190,10 @@ show_machine(struct device *d) { struct pci_dev *p = d->dev; int c; - word sv_id=0, sd_id=0; + word sv_id, sd_id; char classbuf[128], vendbuf[128], devbuf[128], svbuf[128], sdbuf[128]; - switch (get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f) - { - case PCI_HEADER_TYPE_NORMAL: - sv_id = get_conf_word(d, PCI_SUBSYSTEM_VENDOR_ID); - sd_id = get_conf_word(d, PCI_SUBSYSTEM_ID); - break; - case PCI_HEADER_TYPE_CARDBUS: - if (d->config_cached >= 128) - { - sv_id = get_conf_word(d, PCI_CB_SUBSYSTEM_VENDOR_ID); - sd_id = get_conf_word(d, PCI_CB_SUBSYSTEM_ID); - } - break; - } + get_subid(d, &sv_id, &sd_id); if (verbose) { @@ -2002,6 +2217,8 @@ show_machine(struct device *d) printf("Rev:\t%02x\n", c); if (c = get_conf_byte(d, PCI_CLASS_PROG)) printf("ProgIf:\t%02x\n", c); + if (opt_kernel) + show_kernel_machine(d); } else { @@ -2031,10 +2248,15 @@ show_device(struct device *d) { if (opt_machine) show_machine(d); - else if (verbose) - show_verbose(d); else - show_terse(d); + { + if (verbose) + show_verbose(d); + else + show_terse(d); + if (opt_kernel || verbose) + show_kernel(d); + } if (opt_hex) show_hex_dump(d); if (verbose || opt_hex) @@ -2516,12 +2738,32 @@ main(int argc, char **argv) case 'm': opt_machine++; break; + case 'p': + opt_pcimap = optarg; + break; +#ifdef PCI_OS_LINUX + case 'k': + opt_kernel++; + break; +#endif case 'M': opt_map_mode++; break; case 'D': opt_domains = 2; break; +#ifdef PCI_USE_DNS + case 'q': + opt_query_dns++; + break; + case 'Q': + opt_query_all = 1; + break; +#else + case 'q': + case 'Q': + die("DNS queries are not available in this version"); +#endif default: if (parse_generic_option(i, pacc, optarg)) break; @@ -2532,6 +2774,15 @@ main(int argc, char **argv) if (optind < argc) goto bad; + if (opt_query_dns) + { + pacc->id_lookup_mode |= PCI_LOOKUP_NETWORK; + if (opt_query_dns > 1) + pacc->id_lookup_mode |= PCI_LOOKUP_REFRESH_CACHE; + } + if (opt_query_all) + pacc->id_lookup_mode |= PCI_LOOKUP_NETWORK | PCI_LOOKUP_SKIP_LOCAL; + pci_init(pacc); if (opt_map_mode) map_the_bus();