From e15d562c2e54c2defb1b656315783a596cfc477c Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Mon, 21 Feb 2022 02:01:15 -0600 Subject: [PATCH] Merge lspci.c with lsusb.c to share infrastructure. The lspci database loading code should work now, and it can be stored .gz, and the output is closer to the host version. --- toys/other/lspci.c | 125 -------------------------- toys/other/lsusb.c | 213 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 168 insertions(+), 170 deletions(-) delete mode 100644 toys/other/lspci.c diff --git a/toys/other/lspci.c b/toys/other/lspci.c deleted file mode 100644 index 1d3c99ee..00000000 --- a/toys/other/lspci.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * lspci - written by Isaac Dunham - -USE_LSPCI(NEWTOY(lspci, "emkn"USE_LSPCI_TEXT("@i:"), TOYFLAG_USR|TOYFLAG_BIN)) - -config LSPCI - bool "lspci" - default y - help - usage: lspci [-ekm] - - List PCI devices. - - -e Print all 6 digits in class - -k Print kernel driver - -m Machine readable format - -config LSPCI_TEXT - bool "lspci readable output" - depends on LSPCI - default y - help - usage: lspci [-n] [-i FILE] - - -n Numeric output (repeat for readable and numeric) - -i PCI ID database (default /usr/share/misc/pci.ids) - -*/ - -#define FOR_lspci -#include "toys.h" - -GLOBALS( - char *i; - long n; - - FILE *db; -) - -static int do_lspci(struct dirtree *new) -{ - char *p = toybuf, *vendor = toybuf+9, *device = toybuf+18, - driver[256], *vbig = 0, *dbig = 0, **fields; - int dirfd; - - if (!new->parent) return DIRTREE_RECURSE; - - // Parse data out of /proc - - if (-1 == (dirfd = openat(dirtree_parentfd(new), new->name, O_RDONLY))) - return 0; - - *driver = 0; - if (FLAG(k)) readlinkat0(dirfd, "driver", driver, sizeof(driver)); - - for (fields = (char*[]){"class", "vendor", "device", 0}; *fields; fields++) { - int fd, size = 6 + 2*(FLAG(e) && p == toybuf); - *p = 0; - - if (-1 == (fd = openat(dirfd, *fields, O_RDONLY))) { - close(dirfd); - return 0; - } - xreadall(fd, p, size); - memmove(p, p+2, size -= 2); - p[size] = 0; - close(fd); - p += 9; - } - - close(dirfd); - - // Lookup/display data from pci.ids? - - if (CFG_LSPCI_TEXT && TT.db) { - if (TT.n != 1) { - char *s; - - fseek(TT.db, 0, SEEK_SET); - while (!vbig || !dbig) { - s = p; - if (!fgets(s, sizeof(toybuf)-(p-toybuf)-1, TT.db)) break; - while (isspace(*s)) s++; - if (*s == '#') continue; - if (vbig && s == p) break; - if (strstart(&s, vbig ? device : vendor)) { - if (vbig) dbig = s+2; - else vbig = s+2; - s += strlen(s); - s[-1] = 0; // trim ending newline - p = s + 1; - } - } - } - - if (TT.n > 1) { - printf(FLAG(m) ? "%s, \"%s\" \"%s [%s]\" \"%s [%s]\"" - : "%s Class %s: %s [%s] %s [%s]", - new->name+5, toybuf, vbig ? vbig : "", vendor, - dbig ? dbig : "", device); - - goto driver; - } - } - - printf(FLAG(m) ? "%s \"%s\" \"%s\" \"%s\"" : "%s Class %s: %s:%s", - new->name+5, toybuf, vbig ? vbig : vendor, dbig ? dbig : device); - -driver: - if (*driver) printf(FLAG(m) ? " \"%s\"" : " %s", basename(driver)); - xputc('\n'); - - return 0; -} - -void lspci_main(void) -{ - if (CFG_LSPCI_TEXT && TT.n != 1) { - if (!TT.i) TT.i = CFG_TOYBOX_ON_ANDROID ? "/vendor/pci.ids" : - "/usr/share/misc/pci.ids"; - if (!(TT.db = fopen(TT.i, "r"))) perror_msg("%s", TT.i); - } - - dirtree_read("/sys/bus/pci/devices", do_lspci); -} diff --git a/toys/other/lsusb.c b/toys/other/lsusb.c index 30325c34..8d2aa130 100644 --- a/toys/other/lsusb.c +++ b/toys/other/lsusb.c @@ -1,23 +1,45 @@ /* lsusb.c - list available USB devices * * Copyright 2013 Andre Renaud + * Copyright 2013 Isaac Dunham -USE_LSUSB(NEWTOY(lsusb, NULL, TOYFLAG_USR|TOYFLAG_BIN)) +USE_LSUSB(NEWTOY(lsusb, "i:", TOYFLAG_USR|TOYFLAG_BIN)) +USE_LSPCI(NEWTOY(lspci, "emkn@i:", TOYFLAG_USR|TOYFLAG_BIN)) + +config LSPCI + bool "lspci" + default y + help + usage: lspci [-ekmn] [-i FILE] + + List PCI devices. + + -e Extended (6 digit) class + -i ID database (default /etc/pci.ids[.gz]) + -k Show kernel driver + -m Machine readable + -n Numeric output (-nn for both) config LSUSB bool "lsusb" default y help - usage: lsusb + usage: lsusb [-i] List USB hosts/devices. + + -i ID database (default /etc/usb.ids) */ #define FOR_lsusb #include "toys.h" GLOBALS( - void *ids; + char *i; + long n; + + void *ids, *class; + int count; ) struct dev_ids { @@ -26,54 +48,72 @@ struct dev_ids { char name[]; }; -static int list_device(struct dirtree *new) -{ - FILE *file; - char *name; - struct dev_ids *ids; - int busnum = 0, devnum = 0, pid = 0, vid = 0, count = 0; +struct scanloop { + char *pattern; + void *d1, *d2; +}; - if (!new->parent) return DIRTREE_RECURSE; - if (new->name[0] == '.') return 0; - - // Read data from proc file - sprintf(toybuf, "%s/uevent", name = dirtree_path(new, 0)); - if (!(file = fopen(toybuf, "r"))) return 0; - free(name); - while (fgets(toybuf, sizeof(toybuf), file)) - if (sscanf(toybuf, "BUSNUM=%u\n", &busnum) - || sscanf(toybuf, "DEVNUM=%u\n", &devnum) - || sscanf(toybuf, "PRODUCT=%x/%x/", &pid, &vid)) count++; - fclose(file); - - // Output with any matching ids data - if (count == 3) { - printf("Bus %03d Device %03d: ID %04x:%04x", busnum, devnum, pid, vid); - for (ids = TT.ids; ids; ids = ids->next) { - if (pid != ids->id) continue; - printf("%s", ids->name); - for (ids = ids->child; ids; ids = ids->next) { - if (vid != ids->id) continue; - printf("%s", ids->name); +// Common function to read uevent file under /proc for both pci and usb +// note that %s is omitted (because pointer is into toybuf, avoiding copy). +static int scan_uevent(struct dirtree *new, int len, struct scanloop *sl) +{ + int ii, count = 0; + off_t flen = sizeof(toybuf); + char *ss, *yy; + + // Read data + if (*new->name == '.') return 0; + sprintf(toybuf, "%s/uevent", new->name); + if (!readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &flen)) return 0; + + // Loop over lines + while ((flen = strcspn(ss, "\n"))) { + if (ss[flen]) ss[flen++] = 0; + yy = ss+flen; + + // Try each pattern + for (ii = 0; iinext) { + if (id1 != ids->id) continue; + *name1 = ids->name; + for (ids = ids->child; ids; ids = ids->next) { + if (id2 != ids->id) continue; + *name2 = ids->name; + return; + } + return; + } +} + +// Search for pci.ids or usb.ids and return parsed structure or NULL +struct dev_ids *parse_dev_ids(char *name, struct dev_ids **and) { char *path = "/etc:/vendor:/usr/share/misc"; struct string_list *sl; FILE *fp; - char *s, *ss; + char *s, *ss, *sss; struct dev_ids *ids = 0, *new; - int fd = -1; + int fd = -1, tick = 0; // Open compressed or uncompressed file sprintf(toybuf, "%s.gz", name); @@ -81,13 +121,22 @@ struct dev_ids *parse_dev_ids(char *name) signal(SIGCHLD, SIG_IGN); xpopen((char *[]){"zcat", sl->str, 0}, &fd, 1); } else if ((sl = find_in_path(path, name))) fd = xopen(sl->str,O_RDONLY); - free(sl); + llist_traverse(sl, free); if (fd == -1) return 0; - + for (fp = fdopen(fd, "r"); (s = ss = xgetline(fp)); free(s)) { + // TODO parse and use third level instead of skipping it here if (s[strspn(s, " \t")]=='#' || strstart(&ss, "\t\t")) continue; - fd = estrtol(s, &ss, 16); - if (ss == s+4+(*s=='\t') && *ss++==' ') { + + // Switch to device class list? + if (strstart(&ss, "C ") && and) { + *and = ids; + and = 0; + tick++; + } + fd = estrtol(sss = ss, &ss, 16); + if (ss>sss && *ss++==' ') { + while (isspace(*ss)) ss++; new = xmalloc(sizeof(*new)+strlen(ss)+1); new->child = 0; new->id = fd; @@ -106,9 +155,83 @@ struct dev_ids *parse_dev_ids(char *name) return ids; } +static int list_usb(struct dirtree *new) +{ + int busnum = 0, devnum = 0, pid = 0, vid = 0; + char *n1, *n2; + + if (!new->parent) return DIRTREE_RECURSE; + if (3 != scan_uevent(new, 3, (struct scanloop[]){{"BUSNUM=%u", &busnum, 0}, + {"DEVNUM=%u", &devnum, 0}, {"PRODUCT=%x/%x", &pid, &vid}})) + { + get_names(TT.ids, pid, vid, &n1, &n2); + printf("Bus %03d Device %03d: ID %04x:%04x%s%s\n", + busnum, devnum, pid, vid, n1, n2); + } + + return 0; +} + void lsusb_main(void) { - // Parse http://www.linux-usb.org/usb.ids file (if available) - TT.ids = parse_dev_ids("usb.ids"); - dirtree_read("/sys/bus/usb/devices/", list_device); + // Parse http://www.linux-usb.org/usb.ids file (if available) + TT.ids = parse_dev_ids("usb.ids", 0); + dirtree_read("/sys/bus/usb/devices/", list_usb); +} + +#define FOR_lspci +#include "generated/flags.h" + +// TODO: -v +static int list_pci(struct dirtree *new) +{ + char *driver = 0, buf[16], *ss, *names[3]; + int cvd[3] = {0}, ii, revision = 0; + off_t len = sizeof(toybuf); + +// Output formats: -n, -nn, -m, -nm, -nnm, -k + + if (!new->parent) return DIRTREE_RECURSE; + if (strlen(new->name)<6) return 0; + TT.count = 0; + + // Load revision + sprintf(toybuf, "%s/revision", new->name); + if (readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &len)) { + strstart(&ss, "0x"); + sscanf(ss, "%x", &revision); + } + + // Load uevent data, look up names in database + if (3 != scan_uevent(new, 3, (struct scanloop[]){{"DRIVER=", &driver, 0}, + {"PCI_CLASS=%x", cvd, 0}, {"PCI_ID=%x:%x", cvd+1, cvd+2}})) return 0; + get_names(TT.class, 255&(cvd[0]>>16), 255&(cvd[0]>>8), names, names); + get_names(TT.ids, cvd[1], cvd[2], names+1, names+2); + if (!FLAG(e)) cvd[0] >>= 8; + + // Output line according to flags + printf("%s", new->name+5); + for (ii = 0; ii<3; ii++) { + sprintf(buf, "%0*x", 6-2*(ii||!FLAG(e)), cvd[ii]); + if (!TT.n) printf(FLAG(m) ? " \"%s\"" : ": %s"+(ii!=1), names[ii] ? : buf); + else if (TT.n==1) printf(FLAG(m) ? " \"%s\"" : (ii==2) ? "%s " : " %s:", buf); + else if (!FLAG(m)) { + // This one permutes the order, so do it all first time and abort loop + printf(" %s [%s]: %s %s [%04x:%04x]", names[0], buf, names[1], names[2], + cvd[1], cvd[2]); + break; + } else printf(" \"%s [%s]\"", names[ii], buf); + } + printf(FLAG(m) ? " -r%02x" : " (rev %02x)", revision); + if (FLAG(k)) printf(FLAG(m) ? " \"%s\"" : " %s", driver); + xputc('\n'); + + return 0; +} + +void lspci_main(void) +{ + // Parse https://pci-ids.ucw.cz/v2.2/pci.ids (if available) + if (TT.n != 1) TT.class = parse_dev_ids("pci.ids", (void *)&TT.ids); + dirtree_read("/sys/bus/pci/devices/", list_pci); } -- 2.39.2