1# Copyright 2019 The Chromium OS 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"""A test to verify the muxable USB port on servo works with a stick.""" 6 7import logging 8import time 9 10from autotest_lib.client.common_lib import error 11from autotest_lib.server import test 12 13class servo_USBMuxVerification(test.test): 14 """Test to expose mux to both servo and dut host as well as power off.""" 15 version = 1 16 17 # The file names to find the stick's vid, pid, serial. 18 STICK_ID_FILE_NAMES = ('idVendor', 'idProduct', 'serial') 19 20 def _validate_state(self): 21 """Validate the current state of the mux and the visibility of the stick. 22 23 Given the mux direction, and the power state, validates that the stick 24 is showing up on the host it is expected to show up, and not on the 25 host(s) it is not. 26 27 Raises: 28 error.TestFail: if the USB stick is visible when it should not be or if 29 the USB stick is not visible when it should be. 30 """ 31 # A small sleep count to ensure everyone is in the right state and the 32 # caches values/sysfs files are all updated. 33 time.sleep(2) 34 host_map = {'dut': self.dut_host, 35 'servo': self.servo_host} 36 direction = self.servo.get('image_usbkey_direction') 37 pwr = self.servo.get('image_usbkey_pwr') 38 expectation = {} 39 expectation['dut'] = expectation['servo'] = True 40 if pwr == 'off': 41 expectation['dut'] = expectation['servo'] = False 42 else: 43 if direction == 'dut_sees_usbkey': 44 expectation['servo'] = False 45 elif direction == 'servo_sees_usbkey': 46 expectation['dut'] = False 47 else: 48 raise error.TestNA('Unknown servo usbkey direction %s' % direction) 49 for host in expectation: 50 if expectation[host] and not self.is_usb_stick_visible(host_map[host]): 51 raise error.TestFail ('Usb stick not visible on %s side even though ' 52 'it should be.' % host) 53 if not expectation[host] and self.is_usb_stick_visible(host_map[host]): 54 raise error.TestFail ('Usb stick visible on %s side even though it ' 55 'should not be.' % host) 56 57 def is_usb_stick_visible(self, host): 58 """Check whether the stick is visible on |host|. 59 60 On |host| check whether a usb device exists that has the 61 (vid, pid, serial) that was originally read out from the stick. 62 63 Args: 64 host: dut or servo host to inspect 65 66 Returns: 67 True if the stick is visible on |host|, False otherwise 68 69 Raises: 70 error.TestError: if more than one usb device have the vid, pid, serial 71 that was originally identified by the init sequence 72 """ 73 usb_dirs = [] 74 for value, fname in zip(self.stick_id, self.STICK_ID_FILE_NAMES): 75 fs = host.run('grep -lr %s $(find /sys/bus/usb/devices/*/ -maxdepth 1 ' 76 '-name %s)' % (value, fname), 77 ignore_status=True).stdout.strip().split() 78 # Remove the file name to have the usb sysfs dir 79 dirnames = ['/'.join(f.split('/')[:-1]) for f in fs] 80 usb_dirs.append(set(dirnames)) 81 common_usb_dev = usb_dirs.pop() 82 while usb_dirs: 83 # This will only leave the usb device dirs that share the same 84 # vid, pid, serial. Ideally, that's 1 - the stick, or 0, if the stick 85 # is not visible 86 common_usb_dev = common_usb_dev & usb_dirs.pop() 87 if len(common_usb_dev) > 1: 88 raise error.TestError('More than one usb device detected on host %s ' 89 'with vid:0x%s pid:0x%s serial:%s.' 90 % ((host.hostname,) + self.stick_id)) 91 return len(common_usb_dev) == 1 92 93 def get_usb_stick_id(self): 94 """Helper to retrieve usb stick's vid, pid, and serial. 95 96 Returns: 97 (vid, pid, serial) tuple of the stick 98 99 Raises: 100 error.TestFail: if the usb stick cannot be found or if reading 101 any of the idVendor, idProduct, serial files fails. 102 """ 103 # Getting the stick id by pointing it to the servo host. 104 self.servo.set('image_usbkey_direction', 'servo_sees_usbkey') 105 # Get the usb sysfs directory of the stick 106 symbolic_dev_name = self.servo.get('image_usbkey_dev') 107 if not symbolic_dev_name: 108 raise error.TestFail('No usb stick dev file found.') 109 # This will get just the sdx portion of /dev/sdx 110 path_cmd = 'realpath /sys/block/%s' % symbolic_dev_name.split('/')[-1] 111 # This sed command essentially anchors the path on the usb device's 112 # interface's pattern i.e. the bus-port.port...:x.x pattern. Then 113 # it removes anything beyond it and it itself, leaving the usb 114 # device file's sysfs directory that houses the ID files. 115 sed_cmd = r"sed -nr 's|/[0-9]+\-[1-9.]+\:[0-9.]+/.*$|/|p'" 116 cmd = r'%s | %s' % (path_cmd, sed_cmd) 117 logging.debug('Using cmd: %s over ssh to see the stick\'s usb sysfs ' 118 'device file directory.', cmd) 119 dev_dir = self.servo_host.run(cmd).stdout.strip() 120 # Get vid, pid, serial 121 if not dev_dir: 122 raise error.TestFail('Failed to find the usb stick usb sysfs device ' 123 'directory on the servo host.') 124 id_elems = [] 125 for id_file in self.STICK_ID_FILE_NAMES: 126 cmd = 'sudo cat %s%s' % (dev_dir, id_file) 127 try: 128 elem = self.servo_host.run(cmd).stdout.strip() 129 if not elem: 130 raise error.TestFail('Device id file %s not found.' % id_file) 131 except (error.AutoservRunError, error.TestFail) as e: 132 raise error.TestFail('Failed to get %s file for dev %s' % (id_file, 133 dev_dir)) 134 id_elems.append(elem) 135 return tuple(id_elems) 136 137 def initialize(self, host): 138 """Initialize test by extracting usb stick information.""" 139 self.dut_host = host 140 self.servo_host = host._servo_host 141 self.servo = host.servo 142 self._original_pwr = self.servo.get('image_usbkey_pwr') 143 self._original_direction = self.servo.get('image_usbkey_direction') 144 self.servo.set('image_usbkey_pwr', 'on') 145 logging.info('Turning the stick to the servo host.') 146 # Stick id is a (vid, pid, serial) tuple for the stick. 147 self.stick_id = self.get_usb_stick_id() 148 149 def run_once(self, host): 150 """Run through the test cases. 151 152 - Facing the servo host 153 - power off 154 - Facing the DUT 155 - power off 156 """ 157 self.servo.set('image_usbkey_direction', 'servo_sees_usbkey') 158 # Sleep for 2s to let the device properly enumerate. 159 self._validate_state() 160 logging.info('Turning the power to the stick off.') 161 self.servo.set('image_usbkey_pwr', 'off') 162 self._validate_state() 163 logging.info('Turning the power to the stick on.') 164 self.servo.set('image_usbkey_pwr', 'on') 165 logging.info('Turning the stick to the dut host.') 166 self.servo.set('image_usbkey_direction', 'dut_sees_usbkey') 167 # Sleep for 2s to let the device properly enumerate. 168 self._validate_state() 169 logging.info('Turning the power to the stick off.') 170 self.servo.set('image_usbkey_pwr', 'off') 171 self._validate_state() 172 173 def cleanup(self, host): 174 """Restore usb mux to its original state.""" 175 self.servo.set('image_usbkey_pwr', self._original_pwr) 176 self.servo.set('image_usbkey_direction', self._original_direction) 177