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 _, val = self.ec.send_command_get_output( 83 '%sget %s' % (('gpio', 'ioex')[is_ioex], gpio_name), 84 ['([01])[^\n\r]*\s%s' % gpio_name])[0] 85 return val == '1' 86 87 def get_port_count(self): 88 """Get the number of USB ports.""" 89 for cnt in xrange(10): 90 try: 91 self.__check_usb_enabled(cnt) 92 except error.TestFail: 93 logging.info("Found %d USB ports", cnt) 94 return cnt 95 # Limit reached. Probably something went wrong. 96 raise error.TestFail("Unexpected error while trying to determine " + 97 "number of USB ports") 98 99 100 def check_power_off_mode(self): 101 """Shutdown the system and check USB ports are disabled.""" 102 self.run_shutdown_cmd() 103 self.wait_for('shutdown', 'Checking that all USB-A ports are disabled') 104 # Check that all USB-A ports are disabled 105 for idx in xrange(self._port_count): 106 if self.__check_usb_enabled(idx): 107 raise error.TestFail( 108 'Not all USB-A ports are disabled after shutdown') 109 self.servo.power_short_press() 110 111 112 def run_once(self): 113 """Execute the main body of the test. 114 """ 115 self._smart_usb_charge = ( 116 'smart_usb_charge' in self.faft_config.ec_capability) 117 self._port_count = self.get_port_count() 118 119 if self._port_count == 0: 120 raise error.TestNAError("No USB-A port; nothing needs to be tested") 121 122 if self.servo.main_device_is_ccd(): 123 logging.info("Using CCD, ignore checking USB port connection.") 124 elif (self.servo.has_control('servo_v4_type') and 125 self.servo.get('servo_v4_type') == 'type-c'): 126 logging.info("Using type-c servo, ignore checking USB port connection.") 127 elif (self.servo.has_control('servo_v4_type') and 128 self.servo.get('servo_v4_type') != 'type-c'): 129 # When only one USB-A port control is available, turning off the 130 # USB-A port disconnects the network connection from the DUT. 131 raise error.TestNAError("Only one USB-A port control; servo v4 type-C required") 132 else: 133 logging.info("Turn off all USB ports and then turn them on again.") 134 self.switcher.mode_aware_reboot( 135 'custom', self.fake_reboot_by_usb_mode_change) 136 137 logging.info("Check USB ports are disabled when powered off.") 138 self.switcher.mode_aware_reboot('custom', self.check_power_off_mode) 139