toybox/toys/other/lsusb.c
<<
>>
Prefs
   1/* lsusb.c - list available USB devices
   2 *
   3 * Copyright 2013 Andre Renaud <andre@bluewatersys.com>
   4 * Copyright 2013 Isaac Dunham <ibid.ag@gmail.com>
   5
   6USE_LSUSB(NEWTOY(lsusb, "i:", TOYFLAG_USR|TOYFLAG_BIN))
   7USE_LSPCI(NEWTOY(lspci, "emkn@i:", TOYFLAG_USR|TOYFLAG_BIN))
   8
   9config LSPCI
  10  bool "lspci"
  11  default y
  12  help
  13    usage: lspci [-ekmn] [-i FILE]
  14
  15    List PCI devices.
  16
  17    -e  Extended (6 digit) class
  18    -i  ID database (default /etc/pci.ids[.gz])
  19    -k  Show kernel driver
  20    -m  Machine readable
  21    -n  Numeric output (-nn for both)
  22
  23config LSUSB
  24  bool "lsusb"
  25  default y
  26  help
  27    usage: lsusb [-i]
  28
  29    List USB hosts/devices.
  30
  31    -i  ID database (default /etc/usb.ids[.gz])
  32*/
  33
  34#define FOR_lsusb
  35#include "toys.h"
  36
  37GLOBALS(
  38  char *i;
  39  long n;
  40
  41  void *ids, *class;
  42  int count;
  43)
  44
  45struct dev_ids {
  46  struct dev_ids *next, *child;
  47  int id;
  48  char name[];
  49};
  50
  51struct scanloop {
  52  char *pattern;
  53  void *d1, *d2;
  54};
  55
  56// Common function to read uevent file under /proc for both pci and usb
  57// note that %s is omitted (because pointer is into toybuf, avoiding copy).
  58static int scan_uevent(struct dirtree *new, int len, struct scanloop *sl)
  59{
  60  int ii, count = 0;
  61  off_t flen = sizeof(toybuf);
  62  char *ss, *yy;
  63
  64  // Read data
  65  if (*new->name == '.') return 0;
  66  sprintf(toybuf, "%s/uevent", new->name);
  67  if (!readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &flen)) return 0;
  68
  69  // Loop over lines
  70  while ((flen = strcspn(ss, "\n"))) {
  71    if (ss[flen]) ss[flen++] = 0;
  72    yy = ss+flen;
  73
  74    // Try each pattern
  75    for (ii = 0; ii<len; ii++) {
  76      if (strchr(sl[ii].pattern, '%')) {
  77        if (2-!sl[ii].d2==sscanf(ss, sl[ii].pattern, sl[ii].d1, sl[ii].d2))
  78          break;
  79      } else if (strstart(&ss, sl[ii].pattern)) {
  80        *(void **)sl[ii].d1 = ss;
  81        break;
  82      }
  83    }
  84    if (ii!=len) count++;
  85    ss = yy;
  86  }
  87
  88  return count;
  89}
  90
  91static void get_names(struct dev_ids *ids, int id1, int id2,
  92  char **name1, char **name2)
  93{
  94  // Look up matching dev_ids (if any)
  95  *name1 = *name2 = "";
  96  for (; ids; ids = ids->next) {
  97    if (id1 != ids->id) continue;
  98    *name1 = ids->name;
  99    for (ids = ids->child; ids; ids = ids->next) {
 100      if (id2 != ids->id) continue;
 101      *name2 = ids->name;
 102      return;
 103    }
 104    return;
 105  }
 106}
 107
 108// Search for pci.ids or usb.ids and return parsed structure or NULL
 109struct dev_ids *parse_dev_ids(char *name, struct dev_ids **and)
 110{
 111  char *path = "/etc:/vendor:/usr/share/misc";
 112  struct string_list *sl;
 113  FILE *fp;
 114  char *s, *ss, *sss;
 115  struct dev_ids *ids = 0, *new;
 116  int fd = -1, tick = 0;
 117
 118  // Open compressed or uncompressed file
 119  sprintf(toybuf, "%s.gz", name);
 120  if ((sl = find_in_path(path, toybuf))) {
 121    signal(SIGCHLD, SIG_IGN);
 122    xpopen((char *[]){"zcat", sl->str, 0}, &fd, 1);
 123  } else if ((sl = find_in_path(path, name))) fd = xopen(sl->str,O_RDONLY);
 124  llist_traverse(sl, free);
 125  if (fd == -1) return 0;
 126  
 127  for (fp = fdopen(fd, "r"); (s = ss = xgetline(fp)); free(s)) {
 128    // TODO parse and use third level instead of skipping it here
 129    if (s[strspn(s, " \t")]=='#' || strstart(&ss, "\t\t")) continue;
 130
 131    // Switch to device class list?
 132    if (strstart(&ss, "C ") && and) {
 133      *and = ids;
 134      and = 0;
 135      tick++;
 136    } 
 137    fd = estrtol(sss = ss, &ss, 16);
 138    if (ss>sss && *ss++==' ') {
 139      while (isspace(*ss)) ss++;
 140      new = xmalloc(sizeof(*new)+strlen(ss)+1);
 141      new->child = 0;
 142      new->id = fd;
 143      strcpy(new->name, ss);
 144      if (!ids || *s!='\t') {
 145        new->next = ids;
 146        ids = new;
 147      } else {
 148        new->next = ids->child;
 149        ids->child = new;
 150      }
 151    }
 152  }
 153  fclose(fp);
 154
 155  return ids;
 156}
 157
 158static int list_usb(struct dirtree *new)
 159{
 160  int busnum = 0, devnum = 0, pid = 0, vid = 0;
 161  char *n1, *n2;
 162
 163  if (!new->parent) return DIRTREE_RECURSE;
 164  if (3 == scan_uevent(new, 3, (struct scanloop[]){{"BUSNUM=%u", &busnum, 0},
 165    {"DEVNUM=%u", &devnum, 0}, {"PRODUCT=%x/%x", &pid, &vid}}))
 166  {
 167    get_names(TT.ids, pid, vid, &n1, &n2);
 168    printf("Bus %03d Device %03d: ID %04x:%04x %s %s\n",
 169      busnum, devnum, pid, vid, n1, n2);
 170  }
 171
 172  return 0;
 173}
 174
 175void lsusb_main(void)
 176{
 177  // Parse http://www.linux-usb.org/usb.ids file (if available)
 178  TT.ids = parse_dev_ids("usb.ids", 0);
 179  dirtree_read("/sys/bus/usb/devices/", list_usb);
 180}
 181
 182#define FOR_lspci
 183#include "generated/flags.h"
 184
 185// TODO: -v
 186static int list_pci(struct dirtree *new)
 187{
 188  char *driver = 0, buf[16], *ss, *names[3];
 189  int cvd[3] = {0}, ii, revision = 0;
 190  off_t len = sizeof(toybuf);
 191
 192// Output formats: -n, -nn, -m, -nm, -nnm, -k
 193
 194  if (!new->parent) return DIRTREE_RECURSE;
 195  if (strlen(new->name)<6) return 0;
 196  TT.count = 0;
 197
 198  // Load revision
 199  sprintf(toybuf, "%s/revision", new->name);
 200  if (readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &len)) {
 201    strstart(&ss, "0x");
 202    sscanf(ss, "%x", &revision);
 203  }
 204
 205  // Load uevent data, look up names in database
 206  if (3 != scan_uevent(new, 3, (struct scanloop[]){{"DRIVER=", &driver, 0},
 207    {"PCI_CLASS=%x", cvd, 0}, {"PCI_ID=%x:%x", cvd+1, cvd+2}})) return 0;
 208  get_names(TT.class, 255&(cvd[0]>>16), 255&(cvd[0]>>8), names, names);
 209  get_names(TT.ids, cvd[1], cvd[2], names+1, names+2);
 210  if (!FLAG(e)) cvd[0] >>= 8;
 211
 212  // Output line according to flags
 213  printf("%s", new->name+5);
 214  for (ii = 0; ii<3; ii++) {
 215    sprintf(buf, "%0*x", 6-2*(ii||!FLAG(e)), cvd[ii]);
 216    if (!TT.n) printf(FLAG(m) ? " \"%s\"" : ": %s"+(ii!=1), names[ii] ? : buf);
 217    else if (TT.n==1) printf(FLAG(m) ? " \"%s\"" : (ii==2) ? "%s " : " %s:", buf);
 218    else if (!FLAG(m)) {
 219      // This one permutes the order, so do it all first time and abort loop
 220      printf(" %s [%s]: %s %s [%04x:%04x]", names[0], buf, names[1], names[2],
 221        cvd[1], cvd[2]);
 222      break;
 223    } else printf(" \"%s [%s]\"", names[ii], buf);
 224  }
 225  printf(FLAG(m) ? " -r%02x" : " (rev %02x)", revision);
 226  if (FLAG(k)) printf(FLAG(m) ? " \"%s\"" : " %s", driver);
 227  xputc('\n');
 228
 229  return 0;
 230}
 231
 232void lspci_main(void)
 233{
 234  // Parse https://pci-ids.ucw.cz/v2.2/pci.ids (if available)
 235  if (TT.n != 1) TT.class = parse_dev_ids("pci.ids", (void *)&TT.ids);
 236  dirtree_read("/sys/bus/pci/devices/", list_pci);
 237}
 238