Merge ~jocave/checkbox-support:add-lsusb-py-script into checkbox-support:master
- Git
- lp:~jocave/checkbox-support
- add-lsusb-py-script
- Merge into master
Proposed by
Jonathan Cave
Status: | Merged |
---|---|
Approved by: | Jonathan Cave |
Approved revision: | 477e4845272bc5df9b084c980281ca9725806a40 |
Merged at revision: | c8e97c2cfed156d2e2171a9f54dac9125ef44630 |
Proposed branch: | ~jocave/checkbox-support:add-lsusb-py-script |
Merge into: | checkbox-support:master |
Diff against target: |
566 lines (+549/-0) 2 files modified
checkbox_support/scripts/lsusb.py (+547/-0) setup.py (+2/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sylvain Pineau (community) | Approve | ||
Review via email: mp+369382@code.launchpad.net |
Commit message
Description of the change
Add lsusb.oy script and expose in setup.py. This is used by a variety of jobs and launchers so keeping just one copy here now seems sensible.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/checkbox_support/scripts/lsusb.py b/checkbox_support/scripts/lsusb.py | |||
2 | 0 | new file mode 100755 | 0 | new file mode 100755 |
3 | index 0000000..c243835 | |||
4 | --- /dev/null | |||
5 | +++ b/checkbox_support/scripts/lsusb.py | |||
6 | @@ -0,0 +1,547 @@ | |||
7 | 1 | #!/usr/bin/env python3 | ||
8 | 2 | # lsusb.py | ||
9 | 3 | # Displays your USB devices in reasonable form. | ||
10 | 4 | # (c) Kurt Garloff <garloff@suse.de>, 2/2009, GPL v2 or v3. | ||
11 | 5 | # | ||
12 | 6 | # Copyright 2016 Canonical Ltd. | ||
13 | 7 | # Sylvain Pineau <sylvain.pineau@canonical.com> | ||
14 | 8 | # | ||
15 | 9 | # Usage: See usage() | ||
16 | 10 | |||
17 | 11 | from functools import total_ordering | ||
18 | 12 | import getopt | ||
19 | 13 | import os | ||
20 | 14 | import re | ||
21 | 15 | import sys | ||
22 | 16 | |||
23 | 17 | # Global options | ||
24 | 18 | showint = False | ||
25 | 19 | showhubint = False | ||
26 | 20 | noemptyhub = False | ||
27 | 21 | nohub = False | ||
28 | 22 | warnsort = False | ||
29 | 23 | shortmode = False | ||
30 | 24 | |||
31 | 25 | prefix = "/sys/bus/usb/devices/" | ||
32 | 26 | usbids = "/usr/share/usb.ids" | ||
33 | 27 | |||
34 | 28 | esc = chr(27) | ||
35 | 29 | norm = esc + "[0;0m" | ||
36 | 30 | bold = esc + "[0;1m" | ||
37 | 31 | red = esc + "[0;31m" | ||
38 | 32 | green = esc + "[0;32m" | ||
39 | 33 | amber = esc + "[0;33m" | ||
40 | 34 | |||
41 | 35 | cols = ("", "", "", "", "") | ||
42 | 36 | |||
43 | 37 | usbvendors = [] | ||
44 | 38 | usbproducts = [] | ||
45 | 39 | usbclasses = [] | ||
46 | 40 | |||
47 | 41 | devlst = ( | ||
48 | 42 | 'host', # usb-storage | ||
49 | 43 | 'video4linux/video', # uvcvideo et al. | ||
50 | 44 | 'sound/card', # snd-usb-audio | ||
51 | 45 | 'net/', # cdc_ether, ... | ||
52 | 46 | 'input/input', # usbhid | ||
53 | 47 | 'usb:hiddev', # usb hid | ||
54 | 48 | 'bluetooth/hci', # btusb | ||
55 | 49 | 'ttyUSB', # btusb | ||
56 | 50 | 'tty/', # cdc_acm | ||
57 | 51 | 'usb:lp', # usblp | ||
58 | 52 | 'usb/', # hiddev, usblp | ||
59 | 53 | ) | ||
60 | 54 | |||
61 | 55 | |||
62 | 56 | def readattr(path, name): | ||
63 | 57 | "Read attribute from sysfs and return as string" | ||
64 | 58 | f = open(prefix + path + "/" + name) | ||
65 | 59 | return f.readline().rstrip("\n") | ||
66 | 60 | |||
67 | 61 | |||
68 | 62 | def readlink(path, name): | ||
69 | 63 | "Read symlink and return basename" | ||
70 | 64 | return os.path.basename(os.readlink(prefix + path + "/" + name)) | ||
71 | 65 | |||
72 | 66 | |||
73 | 67 | @total_ordering | ||
74 | 68 | class UsbClass: | ||
75 | 69 | "Container for USB Class/Subclass/Protocol" | ||
76 | 70 | |||
77 | 71 | def __init__(self, cl, sc, pr, str=""): | ||
78 | 72 | self.pclass = cl | ||
79 | 73 | self.subclass = sc | ||
80 | 74 | self.proto = pr | ||
81 | 75 | self.desc = str | ||
82 | 76 | |||
83 | 77 | def __repr__(self): | ||
84 | 78 | return self.desc | ||
85 | 79 | |||
86 | 80 | def __lt__(self, oth): | ||
87 | 81 | if self.pclass != oth.pclass: | ||
88 | 82 | return self.pclass - oth.pclass | ||
89 | 83 | if self.subclass != oth.subclass: | ||
90 | 84 | return self.subclass - oth.subclass | ||
91 | 85 | return self.proto - oth.proto | ||
92 | 86 | |||
93 | 87 | def __eq__(self, oth): | ||
94 | 88 | if self.pclass != oth.pclass: | ||
95 | 89 | return False | ||
96 | 90 | if self.subclass != oth.subclass: | ||
97 | 91 | return False | ||
98 | 92 | return self.proto == oth.proto | ||
99 | 93 | |||
100 | 94 | |||
101 | 95 | @total_ordering | ||
102 | 96 | class UsbVendor: | ||
103 | 97 | "Container for USB Vendors" | ||
104 | 98 | |||
105 | 99 | def __init__(self, vid, vname=""): | ||
106 | 100 | self.vid = vid | ||
107 | 101 | self.vname = vname | ||
108 | 102 | |||
109 | 103 | def __repr__(self): | ||
110 | 104 | return self.vname | ||
111 | 105 | |||
112 | 106 | def __lt__(self, oth): | ||
113 | 107 | return self.vid - oth.vid | ||
114 | 108 | |||
115 | 109 | def __eq__(self, oth): | ||
116 | 110 | return self.vid == oth.vid | ||
117 | 111 | |||
118 | 112 | |||
119 | 113 | @total_ordering | ||
120 | 114 | class UsbProduct: | ||
121 | 115 | "Container for USB VID:PID devices" | ||
122 | 116 | |||
123 | 117 | def __init__(self, vid, pid, pname=""): | ||
124 | 118 | self.vid = vid | ||
125 | 119 | self.pid = pid | ||
126 | 120 | self.pname = pname | ||
127 | 121 | |||
128 | 122 | def __repr__(self): | ||
129 | 123 | return self.pname | ||
130 | 124 | |||
131 | 125 | def __lt__(self, oth): | ||
132 | 126 | if self.vid != oth.vid: | ||
133 | 127 | return self.vid - oth.vid | ||
134 | 128 | return self.pid - oth.pid | ||
135 | 129 | |||
136 | 130 | def __eq__(self, oth): | ||
137 | 131 | if self.vid != oth.vid: | ||
138 | 132 | return False | ||
139 | 133 | return self.pid == oth.pid | ||
140 | 134 | |||
141 | 135 | |||
142 | 136 | def ishexdigit(str): | ||
143 | 137 | "return True if all digits are valid hex digits" | ||
144 | 138 | for dg in str: | ||
145 | 139 | if not dg.isdigit() and dg not in 'abcdef': | ||
146 | 140 | return False | ||
147 | 141 | return True | ||
148 | 142 | |||
149 | 143 | |||
150 | 144 | def parse_usb_ids(): | ||
151 | 145 | "Parse /usr/share/usb.ids and fill usbvendors, usbproducts, usbclasses" | ||
152 | 146 | id = 0 | ||
153 | 147 | sid = 0 | ||
154 | 148 | mode = 0 | ||
155 | 149 | strg = "" | ||
156 | 150 | cstrg = "" | ||
157 | 151 | with open(usbids, encoding="utf-8", errors='ignore') as f: | ||
158 | 152 | for ln in f: | ||
159 | 153 | if ln[0] == '#': | ||
160 | 154 | continue | ||
161 | 155 | ln = ln.rstrip('\n') | ||
162 | 156 | if len(ln) == 0: | ||
163 | 157 | continue | ||
164 | 158 | if ishexdigit(ln[0:4]): | ||
165 | 159 | mode = 0 | ||
166 | 160 | id = int(ln[:4], 16) | ||
167 | 161 | usbvendors.append(UsbVendor(id, ln[6:])) | ||
168 | 162 | continue | ||
169 | 163 | if ln[0] == '\t' and ishexdigit(ln[1:3]): | ||
170 | 164 | sid = int(ln[1:5], 16) | ||
171 | 165 | # USB devices | ||
172 | 166 | if mode == 0: | ||
173 | 167 | usbproducts.append(UsbProduct(id, sid, ln[7:])) | ||
174 | 168 | continue | ||
175 | 169 | elif mode == 1: | ||
176 | 170 | nm = ln[5:] | ||
177 | 171 | if nm != "Unused": | ||
178 | 172 | strg = cstrg + ":" + nm | ||
179 | 173 | else: | ||
180 | 174 | strg = cstrg + ":" | ||
181 | 175 | usbclasses.append(UsbClass(id, sid, -1, strg)) | ||
182 | 176 | continue | ||
183 | 177 | if ln[0] == 'C': | ||
184 | 178 | mode = 1 | ||
185 | 179 | id = int(ln[2:4], 16) | ||
186 | 180 | cstrg = ln[6:] | ||
187 | 181 | usbclasses.append(UsbClass(id, -1, -1, cstrg)) | ||
188 | 182 | continue | ||
189 | 183 | if ( | ||
190 | 184 | mode == 1 and ln[0] == '\t' and ln[1] == '\t' and | ||
191 | 185 | ishexdigit(ln[2:4]) | ||
192 | 186 | ): | ||
193 | 187 | prid = int(ln[2:4], 16) | ||
194 | 188 | usbclasses.append(UsbClass(id, sid, prid, strg + ":" + ln[6:])) | ||
195 | 189 | continue | ||
196 | 190 | mode = 2 | ||
197 | 191 | |||
198 | 192 | |||
199 | 193 | def find_usb_prod(vid, pid): | ||
200 | 194 | "Return device name from USB Vendor:Product list" | ||
201 | 195 | strg = "" | ||
202 | 196 | dev = UsbVendor(vid, "") | ||
203 | 197 | try: | ||
204 | 198 | strg = [v for v in usbvendors if v == dev][0].__repr__() | ||
205 | 199 | except IndexError: | ||
206 | 200 | return "" | ||
207 | 201 | dev = UsbProduct(vid, pid, "") | ||
208 | 202 | try: | ||
209 | 203 | strg += " " + [p for p in usbproducts if p == dev][0].__repr__() | ||
210 | 204 | except IndexError: | ||
211 | 205 | return strg | ||
212 | 206 | return strg | ||
213 | 207 | |||
214 | 208 | |||
215 | 209 | def find_usb_class(cid, sid, pid): | ||
216 | 210 | "Return USB protocol from usbclasses list" | ||
217 | 211 | if cid == 0xff and sid == 0xff and pid == 0xff: | ||
218 | 212 | return "Vendor Specific" | ||
219 | 213 | dev = UsbClass(cid, sid, pid, "") | ||
220 | 214 | try: | ||
221 | 215 | return [c for c in usbclasses if c == dev][0].__repr__() | ||
222 | 216 | except IndexError: | ||
223 | 217 | pass | ||
224 | 218 | dev = UsbClass(cid, sid, -1, "") | ||
225 | 219 | try: | ||
226 | 220 | return [c for c in usbclasses if c == dev][0].__repr__() | ||
227 | 221 | except IndexError: | ||
228 | 222 | pass | ||
229 | 223 | dev = UsbClass(cid, -1, -1, "") | ||
230 | 224 | try: | ||
231 | 225 | return [c for c in usbclasses if c == dev][0].__repr__() | ||
232 | 226 | except IndexError: | ||
233 | 227 | return "" | ||
234 | 228 | |||
235 | 229 | |||
236 | 230 | def find_storage(hostno): | ||
237 | 231 | "Return SCSI block dev names for host" | ||
238 | 232 | res = "" | ||
239 | 233 | for ent in os.listdir("/sys/class/scsi_device/"): | ||
240 | 234 | (host, bus, tgt, lun) = ent.split(":") | ||
241 | 235 | if host == hostno: | ||
242 | 236 | try: | ||
243 | 237 | path = "/sys/class/scsi_device/%s/device/block" % ent | ||
244 | 238 | for ent2 in os.listdir(path): | ||
245 | 239 | res += ent2 + " " | ||
246 | 240 | except: | ||
247 | 241 | pass | ||
248 | 242 | return res | ||
249 | 243 | |||
250 | 244 | |||
251 | 245 | def find_dev(driver, usbname): | ||
252 | 246 | "Return pseudo devname that's driven by driver" | ||
253 | 247 | res = "" | ||
254 | 248 | for nm in devlst: | ||
255 | 249 | dir = prefix + usbname | ||
256 | 250 | prep = "" | ||
257 | 251 | idx = nm.find('/') | ||
258 | 252 | if idx != -1: | ||
259 | 253 | prep = nm[:idx+1] | ||
260 | 254 | dir += "/" + nm[:idx] | ||
261 | 255 | nm = nm[idx+1:] | ||
262 | 256 | ln = len(nm) | ||
263 | 257 | try: | ||
264 | 258 | for ent in os.listdir(dir): | ||
265 | 259 | if ent[:ln] == nm: | ||
266 | 260 | res += prep+ent+" " | ||
267 | 261 | if nm == "host": | ||
268 | 262 | res += "(" + find_storage(ent[ln:])[:-1] + ")" | ||
269 | 263 | except: | ||
270 | 264 | pass | ||
271 | 265 | return res | ||
272 | 266 | |||
273 | 267 | |||
274 | 268 | class UsbInterface: | ||
275 | 269 | "Container for USB interface info" | ||
276 | 270 | |||
277 | 271 | def __init__(self, parent=None, level=1): | ||
278 | 272 | self.parent = parent | ||
279 | 273 | self.level = level | ||
280 | 274 | self.fname = "" | ||
281 | 275 | self.iclass = 0 | ||
282 | 276 | self.isclass = 0 | ||
283 | 277 | self.iproto = 0 | ||
284 | 278 | self.noep = 0 | ||
285 | 279 | self.driver = "" | ||
286 | 280 | self.devname = "" | ||
287 | 281 | self.protoname = "" | ||
288 | 282 | |||
289 | 283 | def read(self, fname): | ||
290 | 284 | fullpath = "" | ||
291 | 285 | if self.parent: | ||
292 | 286 | fullpath += self.parent.fname + "/" | ||
293 | 287 | fullpath += fname | ||
294 | 288 | self.fname = fname | ||
295 | 289 | self.iclass = int(readattr(fullpath, "bInterfaceClass"), 16) | ||
296 | 290 | self.isclass = int(readattr(fullpath, "bInterfaceSubClass"), 16) | ||
297 | 291 | self.iproto = int(readattr(fullpath, "bInterfaceProtocol"), 16) | ||
298 | 292 | self.noep = int(readattr(fullpath, "bNumEndpoints")) | ||
299 | 293 | try: | ||
300 | 294 | self.driver = readlink(fname, "driver") | ||
301 | 295 | self.devname = find_dev(self.driver, fname) | ||
302 | 296 | except: | ||
303 | 297 | pass | ||
304 | 298 | self.protoname = find_usb_class(self.iclass, self.isclass, self.iproto) | ||
305 | 299 | |||
306 | 300 | def __str__(self): | ||
307 | 301 | return "%-16s(IF) %02x:%02x:%02x %iEPs (%s) %s%s %s%s%s\n" % \ | ||
308 | 302 | (" " * self.level+self.fname, self.iclass, | ||
309 | 303 | self.isclass, self.iproto, self.noep, | ||
310 | 304 | self.protoname, | ||
311 | 305 | cols[3], self.driver, | ||
312 | 306 | cols[4], self.devname, cols[0]) | ||
313 | 307 | |||
314 | 308 | |||
315 | 309 | class UsbDevice: | ||
316 | 310 | "Container for USB device info" | ||
317 | 311 | |||
318 | 312 | def __init__(self, parent=None, level=0): | ||
319 | 313 | self.parent = parent | ||
320 | 314 | self.level = level | ||
321 | 315 | self.display_name = "" | ||
322 | 316 | self.fname = "" | ||
323 | 317 | self.busnum = 0 | ||
324 | 318 | self.devnum = 0 | ||
325 | 319 | self.iclass = 0 | ||
326 | 320 | self.isclass = 0 | ||
327 | 321 | self.iproto = 0 | ||
328 | 322 | self.vid = 0 | ||
329 | 323 | self.pid = 0 | ||
330 | 324 | self.name = "" | ||
331 | 325 | self.usbver = "" | ||
332 | 326 | self.speed = "" | ||
333 | 327 | self.maxpower = "" | ||
334 | 328 | self.noports = 0 | ||
335 | 329 | self.nointerfaces = 0 | ||
336 | 330 | self.driver = "" | ||
337 | 331 | self.devname = "" | ||
338 | 332 | self.interfaces = [] | ||
339 | 333 | self.children = [] | ||
340 | 334 | |||
341 | 335 | def read(self, fname): | ||
342 | 336 | self.fname = fname | ||
343 | 337 | self.iclass = int(readattr(fname, "bDeviceClass"), 16) | ||
344 | 338 | self.isclass = int(readattr(fname, "bDeviceSubClass"), 16) | ||
345 | 339 | self.iproto = int(readattr(fname, "bDeviceProtocol"), 16) | ||
346 | 340 | self.vid = int(readattr(fname, "idVendor"), 16) | ||
347 | 341 | self.pid = int(readattr(fname, "idProduct"), 16) | ||
348 | 342 | self.busnum = int(readattr(fname, "busnum")) | ||
349 | 343 | self.devnum = int(readattr(fname, "devnum")) | ||
350 | 344 | self.usbver = readattr(fname, "version") | ||
351 | 345 | try: | ||
352 | 346 | self.name = readattr(fname, "manufacturer") + " " \ | ||
353 | 347 | + readattr(fname, "product") | ||
354 | 348 | if self.name[:5] == "Linux": | ||
355 | 349 | rx = re.compile(r"Linux [^ ]* .hci[-_]hcd") | ||
356 | 350 | mch = rx.match(self.name) | ||
357 | 351 | if mch: | ||
358 | 352 | self.name = "Linux Foundation %.2f root hub" % float( | ||
359 | 353 | self.usbver) | ||
360 | 354 | except: | ||
361 | 355 | pass | ||
362 | 356 | if not self.name: | ||
363 | 357 | self.name = find_usb_prod(self.vid, self.pid) | ||
364 | 358 | # Some USB Card readers have a better name than Generic ... | ||
365 | 359 | if self.name[:7] == "Generic": | ||
366 | 360 | oldnm = self.name | ||
367 | 361 | self.name = find_usb_prod(self.vid, self.pid) | ||
368 | 362 | if not self.name: | ||
369 | 363 | self.name = oldnm | ||
370 | 364 | self.speed = readattr(fname, "speed") | ||
371 | 365 | self.maxpower = readattr(fname, "bMaxPower") | ||
372 | 366 | self.noports = int(readattr(fname, "maxchild")) | ||
373 | 367 | try: | ||
374 | 368 | self.nointerfaces = int(readattr(fname, "bNumInterfaces")) | ||
375 | 369 | except: | ||
376 | 370 | self.nointerfaces = 0 | ||
377 | 371 | try: | ||
378 | 372 | self.driver = readlink(fname, "driver") | ||
379 | 373 | self.devname = find_dev(self.driver, fname) | ||
380 | 374 | except: | ||
381 | 375 | pass | ||
382 | 376 | |||
383 | 377 | def readchildren(self): | ||
384 | 378 | if self.fname[0:3] == "usb": | ||
385 | 379 | fname = self.fname[3:] | ||
386 | 380 | else: | ||
387 | 381 | fname = self.fname | ||
388 | 382 | for dirent in os.listdir(prefix + self.fname): | ||
389 | 383 | if not dirent[0:1].isdigit(): | ||
390 | 384 | continue | ||
391 | 385 | if os.access(prefix + dirent + "/bInterfaceClass", os.R_OK): | ||
392 | 386 | iface = UsbInterface(self, self.level+1) | ||
393 | 387 | iface.read(dirent) | ||
394 | 388 | self.interfaces.append(iface) | ||
395 | 389 | else: | ||
396 | 390 | usbdev = UsbDevice(self, self.level+1) | ||
397 | 391 | usbdev.read(dirent) | ||
398 | 392 | usbdev.readchildren() | ||
399 | 393 | self.children.append(usbdev) | ||
400 | 394 | |||
401 | 395 | def __str__(self): | ||
402 | 396 | if self.iclass == 9: | ||
403 | 397 | col = cols[2] | ||
404 | 398 | if noemptyhub and len(self.children) == 0: | ||
405 | 399 | return "" | ||
406 | 400 | if nohub: | ||
407 | 401 | str = "" | ||
408 | 402 | else: | ||
409 | 403 | col = cols[1] | ||
410 | 404 | if not nohub or self.iclass != 9: | ||
411 | 405 | if shortmode: | ||
412 | 406 | str = "ID %04x:%04x %s" % (self.vid, self.pid, self.name) | ||
413 | 407 | else: | ||
414 | 408 | str = "Bus %03d Device %03d: ID %04x:%04x %s" % \ | ||
415 | 409 | (self.busnum, self.devnum, self.vid, self.pid, self.name) | ||
416 | 410 | str += "\n" | ||
417 | 411 | if showint: | ||
418 | 412 | for iface in self.interfaces: | ||
419 | 413 | str += iface.__str__() | ||
420 | 414 | for child in self.children: | ||
421 | 415 | str += child.__str__() | ||
422 | 416 | return str | ||
423 | 417 | |||
424 | 418 | |||
425 | 419 | def deepcopy(lst): | ||
426 | 420 | "Returns a deep copy from the list lst" | ||
427 | 421 | copy = [] | ||
428 | 422 | for item in lst: | ||
429 | 423 | copy.append(item) | ||
430 | 424 | return copy | ||
431 | 425 | |||
432 | 426 | |||
433 | 427 | def display_diff(lst1, lst2, fmtstr, args): | ||
434 | 428 | "Compare lists (same length!) and display differences" | ||
435 | 429 | for idx in range(0, len(lst1)): | ||
436 | 430 | if lst1[idx] != lst2[idx]: | ||
437 | 431 | print("Warning: " + fmtstr % args(lst2[idx])) | ||
438 | 432 | |||
439 | 433 | |||
440 | 434 | def fix_usbvend(): | ||
441 | 435 | "Sort USB vendor list and (optionally) display diffs" | ||
442 | 436 | if warnsort: | ||
443 | 437 | oldusbvend = deepcopy(usbvendors) | ||
444 | 438 | usbvendors.sort() | ||
445 | 439 | if warnsort: | ||
446 | 440 | display_diff(usbvendors, oldusbvend, "Unsorted Vendor ID %04x", | ||
447 | 441 | lambda x: (x.vid,)) | ||
448 | 442 | |||
449 | 443 | |||
450 | 444 | def fix_usbprod(): | ||
451 | 445 | "Sort USB products list" | ||
452 | 446 | if warnsort: | ||
453 | 447 | oldusbprod = deepcopy(usbproducts) | ||
454 | 448 | usbproducts.sort() | ||
455 | 449 | if warnsort: | ||
456 | 450 | display_diff(usbproducts, oldusbprod, | ||
457 | 451 | "Unsorted Vendor:Product ID %04x:%04x", | ||
458 | 452 | lambda x: (x.vid, x.pid)) | ||
459 | 453 | |||
460 | 454 | |||
461 | 455 | def fix_usbclass(): | ||
462 | 456 | "Sort USB class list" | ||
463 | 457 | if warnsort: | ||
464 | 458 | oldusbcls = deepcopy(usbclasses) | ||
465 | 459 | usbclasses.sort() | ||
466 | 460 | if warnsort: | ||
467 | 461 | display_diff(usbclasses, oldusbcls, | ||
468 | 462 | "Unsorted USB class %02x:%02x:%02x", | ||
469 | 463 | lambda x: (x.pclass, x.subclass, x.proto)) | ||
470 | 464 | |||
471 | 465 | |||
472 | 466 | def usage(): | ||
473 | 467 | "Displays usage information" | ||
474 | 468 | print("Usage: lsusb.py [options]") | ||
475 | 469 | print("Options:") | ||
476 | 470 | print(" -h display this help") | ||
477 | 471 | print(" -i display interface information") | ||
478 | 472 | print(" -I display interface information, even for hubs") | ||
479 | 473 | print(" -u suppress empty hubs") | ||
480 | 474 | print(" -U suppress all hubs") | ||
481 | 475 | print(" -c use colors") | ||
482 | 476 | print(" -s short mode") | ||
483 | 477 | print(" -w display warning if usb.ids is not sorted correctly") | ||
484 | 478 | print(" -f FILE override filename for /usr/share/usb.ids") | ||
485 | 479 | return 2 | ||
486 | 480 | |||
487 | 481 | |||
488 | 482 | def read_usb(): | ||
489 | 483 | "Read toplevel USB entries and print" | ||
490 | 484 | for dirent in os.listdir(prefix): | ||
491 | 485 | if not dirent[0:3] == "usb": | ||
492 | 486 | continue | ||
493 | 487 | usbdev = UsbDevice(None, 0) | ||
494 | 488 | usbdev.read(dirent) | ||
495 | 489 | usbdev.readchildren() | ||
496 | 490 | print(usbdev.__str__(), end="") | ||
497 | 491 | |||
498 | 492 | |||
499 | 493 | def main(): | ||
500 | 494 | "main entry point" | ||
501 | 495 | global showint, showhubint, noemptyhub, nohub, warnsort, cols, usbids, \ | ||
502 | 496 | shortmode | ||
503 | 497 | try: | ||
504 | 498 | (optlist, args) = getopt.gnu_getopt(sys.argv[1:], "hiIuUwcsf:", ("help",)) | ||
505 | 499 | except getopt.GetoptError as exc: | ||
506 | 500 | print("Error:", exc) | ||
507 | 501 | sys.exit(usage()) | ||
508 | 502 | for opt in optlist: | ||
509 | 503 | if opt[0] == "-h" or opt[0] == "--help": | ||
510 | 504 | usage() | ||
511 | 505 | sys.exit(0) | ||
512 | 506 | if opt[0] == "-i": | ||
513 | 507 | showint = True | ||
514 | 508 | continue | ||
515 | 509 | if opt[0] == "-I": | ||
516 | 510 | showint = True | ||
517 | 511 | showhubint = True | ||
518 | 512 | continue | ||
519 | 513 | if opt[0] == "-u": | ||
520 | 514 | noemptyhub = True | ||
521 | 515 | continue | ||
522 | 516 | if opt[0] == "-U": | ||
523 | 517 | noemptyhub = True | ||
524 | 518 | nohub = True | ||
525 | 519 | continue | ||
526 | 520 | if opt[0] == "-c": | ||
527 | 521 | cols = (norm, bold, red, green, amber) | ||
528 | 522 | continue | ||
529 | 523 | if opt[0] == "-w": | ||
530 | 524 | warnsort = True | ||
531 | 525 | continue | ||
532 | 526 | if opt[0] == "-f": | ||
533 | 527 | usbids = opt[1] | ||
534 | 528 | continue | ||
535 | 529 | if opt[0] == "-s": | ||
536 | 530 | shortmode = True | ||
537 | 531 | continue | ||
538 | 532 | if len(args) > 0: | ||
539 | 533 | print("Error: excess args %s ..." % args[0]) | ||
540 | 534 | sys.exit(usage()) | ||
541 | 535 | try: | ||
542 | 536 | parse_usb_ids() | ||
543 | 537 | fix_usbvend() | ||
544 | 538 | fix_usbprod() | ||
545 | 539 | fix_usbclass() | ||
546 | 540 | except: | ||
547 | 541 | print(" WARNING: Failure to read usb.ids", file=sys.stderr) | ||
548 | 542 | print(sys.exc_info(), file=sys.stderr) | ||
549 | 543 | read_usb() | ||
550 | 544 | |||
551 | 545 | # Entry point | ||
552 | 546 | if __name__ == "__main__": | ||
553 | 547 | main() | ||
554 | diff --git a/setup.py b/setup.py | |||
555 | index 4f4c992..7f115f8 100755 | |||
556 | --- a/setup.py | |||
557 | +++ b/setup.py | |||
558 | @@ -88,6 +88,8 @@ setup( | |||
559 | 88 | "checkbox_support.scripts.snap_connect:main"), | 88 | "checkbox_support.scripts.snap_connect:main"), |
560 | 89 | ("checkbox-support-eddystone_scanner=" | 89 | ("checkbox-support-eddystone_scanner=" |
561 | 90 | "checkbox_support.scripts.eddystone_scanner:main"), | 90 | "checkbox_support.scripts.eddystone_scanner:main"), |
562 | 91 | ("checkbox-support-lsusb=" | ||
563 | 92 | "checkbox_support.scripts.lsusb:main"), | ||
564 | 91 | ], | 93 | ], |
565 | 92 | }, | 94 | }, |
566 | 93 | ) | 95 | ) |
+1