diff options
Diffstat (limited to 'pciusb.c')
-rw-r--r-- | pciusb.c | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/pciusb.c b/pciusb.c new file mode 100644 index 0000000..bcd901f --- /dev/null +++ b/pciusb.c @@ -0,0 +1,192 @@ +#define _GNU_SOURCE +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/utsname.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <modprobe.h> +#include <dirent.h> +#include "common.h" + +static void set_modules_from_modalias_file(struct pciusb_entry *e, char *modalias_path) { + FILE *file; + file = fopen(modalias_path, "r"); + if (file) { + char *modalias = NULL; + size_t n, size; + if (-1 == getline(&modalias, &n, file)) { + fprintf(stderr, "Unable to read modalias from %s\n", modalias_path); + fclose(file); + return; + } + fclose(file); + size = strlen(modalias); + if (size) + modalias[size-1] = 0; + + ifree(e->module); + e->module = modalias_resolve_module(modalias); + free(modalias); + } else { + fprintf(stderr, "Unable to read modalias from %s\n", modalias_path); + return; + } +} + +static void find_pci_modules_through_aliases(struct pciusb_entry *e) { + char *modalias_path; + asprintf(&modalias_path, + "/sys/bus/pci/devices/%04x:%02x:%02x.%x/modalias", + e->pci_domain, e->pci_bus, e->pci_device, e->pci_function); + set_modules_from_modalias_file(e, modalias_path); + free(modalias_path); +} + +static void find_usb_modules_through_aliases(struct pciusb_entry *e) { + char *usb_prefix, *sysfs_path; + DIR *dir; + struct dirent *dent; + + asprintf(&usb_prefix, "%d-", e->pci_bus); + /* USB port is indexed from 0 in procfs, from 1 in sysfs */ + asprintf(&sysfs_path, "/sys/bus/usb/devices/%d-%d", e->pci_bus, e->usb_port + 1); + + dir = opendir(sysfs_path); + if (!dir) { + goto end; + } + while ((dent = readdir(dir)) != NULL) { + if ((dent->d_type == DT_DIR) && + !strncmp(usb_prefix, dent->d_name, strlen(usb_prefix))) { + char *modalias_path; + asprintf(&modalias_path, "%s/%s/modalias", sysfs_path, dent->d_name); + set_modules_from_modalias_file(e, modalias_path); + free(modalias_path); + /* maybe we would need a "other_modules" field in pciusb_entry + to list modules from all USB interfaces */ + if (e->module) + break; + } + } + closedir(dir); +end: + free(sysfs_path); + free(usb_prefix); +} + +static void find_modules_through_aliases_one(const char *bus, struct pciusb_entry *e) { + if (!strcmp("pci", bus)) { + find_pci_modules_through_aliases(e); + } else if (!strcmp("usb", bus)) { + find_usb_modules_through_aliases(e); + } +} + +static void find_modules_through_aliases(const char *bus, struct pciusb_entries *entries) { + unsigned int i; + for (i = 0; i < entries->nb; i++) { + struct pciusb_entry *e = &entries->entries[i]; + + // No special case found in pcitable ? Then lookup modalias for PCI devices + if (e->module && strcmp(e->module, "unknown")) + continue; + find_modules_through_aliases_one(bus, e); + } + + modalias_cleanup(); +} + +extern int pciusb_find_modules(struct pciusb_entries *entries, const char *fpciusbtable, const descr_lookup descr_lookup, int is_pci) { + fh f; + char buf[2048]; + int line; + + f = fh_open(fpciusbtable); + + for (line = 1; fh_gets(buf, sizeof(buf) - 1, &f); line++) { + unsigned short vendor, device, subvendor, subdevice; + char *p = NULL, *q = NULL; + int offset; unsigned int i; + int nb; + if (buf[0]=='#') + continue; // skip comments + + nb = sscanf(buf, "0x%hx\t0x%hx\t0x%hx\t0x%hx\t%n", &vendor, &device, &subvendor, &subdevice, &offset); + if (nb != 4) { + nb = sscanf(buf, "0x%hx\t0x%hx\t%n", &vendor, &device, &offset); + if (nb != 2) { + fprintf(stderr, "%s %d: bad line\n", fpciusbtable, line); + continue; // skip bad line + } + } + for (i = 0; i < entries->nb; i++) { + struct pciusb_entry *e = &entries->entries[i]; + if (e->already_found) + continue; // skip since already found with sub ids + if (vendor != e->vendor || device != e->device) + continue; // main ids differ + + if (nb == 4 && !(subvendor == e->subvendor && subdevice == e->subdevice)) + continue; // subids differ + + if (!p) { // only calc text & module if not already done + p = buf + offset + 1; + q = strchr(p, '\t'); + if (!q) // no description field? + q = strchr(p, '\0') - 1; + } + if (strncmp(p, "unknown", q-p-1)) { + ifree(e->module); + e->module = strndup(p,q-p-1); + } + /* special case for buggy 0x0 usb entry */ + if (descr_lookup == LOAD && strlen(q) > 1 && 2 < strlen(q+2) && vendor != 0 && device != 0 && e->class_id != 0x90000d) { /* Hub class */ + ifree(e->text); /* usb.c set it so that we display something when usbtable doesn't refer that hw*/ + e->text = strndup(q+2, strlen(q)-4); + } + /* if subids read on pcitable line, we know that subids matches : + (see "subids differ" test above) */ + if (nb == 4) + e->already_found = 1; + } + } + fh_close(&f); + + /* If no special case in pcitable, then lookup modalias for devices */ + const char *bus = is_pci ? "pci" : "usb"; + find_modules_through_aliases(bus, entries); + + return 1; +} + +extern void pciusb_initialize(struct pciusb_entry *e) { + e->vendor = 0xffff; + e->device = 0xffff; + e->subvendor = 0xffff; + e->subdevice = 0xffff; + e->class_id = 0; + e->pci_bus = 0xff; + e->pci_device = 0xff; + e->pci_function = 0xff; + e->pci_revision = 0; + e->usb_port = 0xffff; + e->module = NULL; + e->text = NULL; + e->class = NULL; + e->already_found = 0; + e->is_pciexpress = 0; +} + +extern void pciusb_free(struct pciusb_entries *entries) { + unsigned int i; + for (i = 0; i < entries->nb; i++) { + struct pciusb_entry *e = &entries->entries[i]; + ifree(e->module); + ifree(e->text); + } + if (entries->nb) ifree(entries->entries); +} |