1 # Copyright 2015 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import logging 6 import re 7 8 from devil.utils import cmd_helper 9 10 _COULDNT_OPEN_ERROR_RE = re.compile(r'Couldn\'t open device.*') 11 _INDENTATION_RE = re.compile(r'^( *)') 12 _LSUSB_BUS_DEVICE_RE = re.compile(r'^Bus (\d{3}) Device (\d{3}): (.*)') 13 _LSUSB_ENTRY_RE = re.compile(r'^ *([^ ]+) +([^ ]+) *([^ ].*)?$') 14 _LSUSB_GROUP_RE = re.compile(r'^ *([^ ]+.*):$') 15 16 17 def _lsusbv_on_device(bus_id, dev_id): 18 """Calls lsusb -v on device.""" 19 _, raw_output = cmd_helper.GetCmdStatusAndOutputWithTimeout( 20 ['lsusb', '-v', '-s', '%s:%s' % (bus_id, dev_id)], timeout=10) 21 22 device = {'bus': bus_id, 'device': dev_id} 23 depth_stack = [device] 24 25 # TODO(jbudorick): Add documentation for parsing. 26 for line in raw_output.splitlines(): 27 # Ignore blank lines. 28 if not line: 29 continue 30 # Filter out error mesage about opening device. 31 if _COULDNT_OPEN_ERROR_RE.match(line): 32 continue 33 # Find start of device information. 34 m = _LSUSB_BUS_DEVICE_RE.match(line) 35 if m: 36 if m.group(1) != bus_id: 37 logging.warning( 38 'Expected bus_id value: %r, seen %r', bus_id, m.group(1)) 39 if m.group(2) != dev_id: 40 logging.warning( 41 'Expected dev_id value: %r, seen %r', dev_id, m.group(2)) 42 device['desc'] = m.group(3) 43 continue 44 45 indent_match = _INDENTATION_RE.match(line) 46 if not indent_match: 47 continue 48 49 depth = 1 + len(indent_match.group(1)) / 2 50 if depth > len(depth_stack): 51 logging.error( 52 'lsusb parsing error: unexpected indentation: "%s"', line) 53 continue 54 55 while depth < len(depth_stack): 56 depth_stack.pop() 57 58 cur = depth_stack[-1] 59 60 m = _LSUSB_GROUP_RE.match(line) 61 if m: 62 new_group = {} 63 cur[m.group(1)] = new_group 64 depth_stack.append(new_group) 65 continue 66 67 m = _LSUSB_ENTRY_RE.match(line) 68 if m: 69 new_entry = { 70 '_value': m.group(2), 71 '_desc': m.group(3), 72 } 73 cur[m.group(1)] = new_entry 74 depth_stack.append(new_entry) 75 continue 76 77 logging.error('lsusb parsing error: unrecognized line: "%s"', line) 78 79 return device 80 81 def lsusb(): 82 """Call lsusb and return the parsed output.""" 83 _, lsusb_list_output = cmd_helper.GetCmdStatusAndOutputWithTimeout( 84 ['lsusb'], timeout=10) 85 devices = [] 86 for line in lsusb_list_output.splitlines(): 87 m = _LSUSB_BUS_DEVICE_RE.match(line) 88 if m: 89 bus_num = m.group(1) 90 dev_num = m.group(2) 91 try: 92 devices.append(_lsusbv_on_device(bus_num, dev_num)) 93 except cmd_helper.TimeoutError: 94 # Will be blacklisted if it is in expected device file, but times out. 95 logging.info('lsusb -v %s:%s timed out.', bus_num, dev_num) 96 return devices 97 98 def raw_lsusb(): 99 return cmd_helper.GetCmdOutput(['lsusb']) 100 101 def get_lsusb_serial(device): 102 try: 103 return device['Device Descriptor']['iSerial']['_desc'] 104 except KeyError: 105 return None 106 107 def get_android_devices(): 108 return [serial for serial in (get_lsusb_serial(d) for d in lsusb()) 109 if serial] 110