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