• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
6 USE_LSUSB(NEWTOY(lsusb, "i:", TOYFLAG_USR|TOYFLAG_BIN))
7 USE_LSPCI(NEWTOY(lspci, "emkn@i:", TOYFLAG_USR|TOYFLAG_BIN))
8 
9 config 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 
23 config 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 
37 GLOBALS(
38   char *i;
39   long n;
40 
41   void *ids, *class;
42   int count;
43 )
44 
45 struct dev_ids {
46   struct dev_ids *next, *child;
47   int id;
48   char name[];
49 };
50 
51 struct 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).
scan_uevent(struct dirtree * new,int len,struct scanloop * sl)58 static 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 
get_names(struct dev_ids * ids,int id1,int id2,char ** name1,char ** name2)91 static 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
parse_dev_ids(char * name,struct dev_ids ** and)109 struct 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 
list_usb(struct dirtree * new)158 static 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 
lsusb_main(void)175 void 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
list_pci(struct dirtree * new)186 static 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 
lspci_main(void)232 void 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