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