1# Copyright (c) 2012 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 5import logging 6 7from autotest_lib.client.common_lib import error 8from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 9 10 11class firmware_ECUsbPorts(FirmwareTest): 12 """ 13 Servo based EC USB port control test. 14 """ 15 version = 1 16 17 18 # Delay for remote shell command call to return 19 RPC_DELAY = 1 20 21 # Delay between turning off and on USB ports 22 REBOOT_DELAY = 6 23 24 # USB charge modes, copied from ec/include/usb_charge.h 25 USB_CHARGE_MODE_DISABLED = 0 26 USB_CHARGE_MODE_SDP2 = 1 27 USB_CHARGE_MODE_CDP = 2 28 USB_CHARGE_MODE_DCP_SHORT = 3 29 USB_CHARGE_MODE_ENABLED = 4 30 31 def initialize(self, host, cmdline_args): 32 super(firmware_ECUsbPorts, self).initialize(host, cmdline_args) 33 # Don't bother if there is no Chrome EC. 34 if not self.check_ec_capability(['usb']): 35 raise error.TestNAError("Nothing needs to be tested on this device") 36 # Only run in normal mode 37 self.switcher.setup_mode('normal') 38 self.ec.send_command("chan 0") 39 40 41 def cleanup(self): 42 try: 43 self.ec.send_command("chan 0xffffffff") 44 except Exception as e: 45 logging.error("Caught exception: %s", str(e)) 46 super(firmware_ECUsbPorts, self).cleanup() 47 48 49 def fake_reboot_by_usb_mode_change(self): 50 """ 51 Turn off USB ports and also kill FAFT client so that this acts like a 52 reboot. If USB ports cannot be turned off or on, reboot step would 53 fail. 54 """ 55 for_all_ports_cmd = ('id=0; while [ $id -lt %d ];' + 56 'do ectool usbchargemode "$id" %d;' + 57 'id=$((id+1)); sleep 0.5; done') 58 # Port disable - same for smart and dumb ports. 59 ports_off_cmd = for_all_ports_cmd % (self._port_count, 60 self.USB_CHARGE_MODE_DISABLED) 61 # Port enable - different command based on smart/dumb port. 62 port_enable_param = (self.USB_CHARGE_MODE_SDP2 63 if self._smart_usb_charge else self.USB_CHARGE_MODE_ENABLED) 64 ports_on_cmd = for_all_ports_cmd % (self._port_count, port_enable_param) 65 cmd = ("sleep %d; %s; sleep %d; %s" % 66 (self.RPC_DELAY, ports_off_cmd, self.REBOOT_DELAY, 67 ports_on_cmd)) 68 block = False 69 self.faft_client.system.run_shell_command(cmd, block) 70 self.faft_client.disconnect() 71 72 def __check_usb_enabled(self, idx): 73 """Returns True if USB-A enable signal is high for a given index""" 74 is_ioex = False 75 gpio_name = 'USB%d_ENABLE' % (idx + 1) 76 if self.faft_config.custom_usb_enable_pins: 77 if idx >= len(self.faft_config.custom_usb_enable_pins): 78 raise error.TestFail('No USB enable for index %d' % idx) 79 is_ioex = self.faft_config.custom_usb_enable_pins[idx].get( 80 'ioex', False) 81 gpio_name = self.faft_config.custom_usb_enable_pins[idx]['name'] 82 # change the unicode to ascii 83 gpio_name = str(gpio_name) 84 _, val = self.ec.send_command_get_output( 85 '%sget %s' % (('gpio', 'ioex')[is_ioex], gpio_name), 86 ['(?i)([01])[^\n\r]*\s%s' % gpio_name])[0] 87 return val == '1' 88 89 def probe_port_count(self): 90 """Probe the EC's gpio pins to determine the number of USB-A ports""" 91 for cnt in range(10): 92 try: 93 self.__check_usb_enabled(cnt) 94 except error.TestFail: 95 # Enforce that zero ports are specified explicitly. Without 96 # this, the TEST_NA result tends to get ignored until final 97 # FSI signoff, at which point it becomes an emergency when 98 # someone notices this device does indeed have USB-A ports. 99 if cnt == 0: 100 raise error.TestFail( 101 "No USB A ports could be found. If this device has " 102 "no USB-A ports, specify usb_a_port_count: 0 in your " 103 "fw-testing-configs") 104 105 logging.info("Found %d USB ports via probe", cnt) 106 return cnt 107 # Limit reached. Probably something went wrong. 108 raise error.TestFail("Unexpected error while trying to determine " + 109 "number of USB ports") 110 111 def get_port_count(self): 112 """Get the number of USB ports.""" 113 114 # Prefer an explicit count. 115 count = self.faft_config.usb_a_port_count 116 if count is not None: 117 logging.info("%d USB ports specified via config", count) 118 # Allow -1 as an escape hatch back to dynamic probing for 119 # devices whose USB-A port counts may vary by SKU. 120 if count == -1: 121 try: 122 count = self.probe_port_count() 123 except error.TestFail: 124 count = 0 125 126 return count 127 128 # Next use the custom enable as a proxy, if it's non-zero. 129 if self.faft_config.custom_usb_enable_pins: 130 count = len(self.faft_config.custom_usb_enable_pins) 131 logging.info("%d USB ports counted by pins", count) 132 return count 133 134 # Finally, fall back to probing if unspecified. 135 return self.probe_port_count() 136 137 def check_power_off_mode(self): 138 """Shutdown the system and check USB ports are disabled.""" 139 self.run_shutdown_cmd() 140 self.wait_for('shutdown', 'Checking that all USB-A ports are disabled') 141 # Check that all USB-A ports are disabled 142 for idx in range(self._port_count): 143 if self.__check_usb_enabled(idx): 144 raise error.TestFail( 145 'Not all USB-A ports are disabled after shutdown') 146 self.servo.power_short_press() 147 148 149 def run_once(self): 150 """Execute the main body of the test. 151 """ 152 self._smart_usb_charge = ( 153 'smart_usb_charge' in self.faft_config.ec_capability) 154 self._port_count = self.get_port_count() 155 156 if self._port_count == 0: 157 raise error.TestNAError("No USB-A port; nothing needs to be tested") 158 159 if self.servo.main_device_is_ccd(): 160 logging.info("Using CCD, ignore checking USB port connection.") 161 elif self.servo.is_servo_v4_type_c(): 162 logging.info("Using type-c servo, ignore checking USB port connection.") 163 elif self.servo.get_servo_v4_type() is not None: 164 # When only one USB-A port control is available, turning off the 165 # USB-A port disconnects the network connection from the DUT. 166 raise error.TestNAError("Only one USB-A port control; servo v4 type-C required") 167 else: 168 logging.info("Turn off all USB ports and then turn them on again.") 169 self.switcher.mode_aware_reboot( 170 'custom', self.fake_reboot_by_usb_mode_change) 171 172 logging.info("Check USB ports are disabled when powered off.") 173 self.switcher.mode_aware_reboot('custom', self.check_power_off_mode) 174