| # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.server.cros.faft.firmware_test import FirmwareTest |
| |
| |
| class firmware_ECUsbPorts(FirmwareTest): |
| """ |
| Servo based EC USB port control test. |
| """ |
| version = 1 |
| |
| |
| # Delay for remote shell command call to return |
| RPC_DELAY = 1 |
| |
| # Delay between turning off and on USB ports |
| REBOOT_DELAY = 6 |
| |
| # Timeout range for waiting system to shutdown |
| SHUTDOWN_TIMEOUT = 10 |
| |
| # USB charge modes, copied from ec/include/usb_charge.h |
| USB_CHARGE_MODE_DISABLED = 0 |
| USB_CHARGE_MODE_SDP2 = 1 |
| USB_CHARGE_MODE_CDP = 2 |
| USB_CHARGE_MODE_DCP_SHORT = 3 |
| USB_CHARGE_MODE_ENABLED = 4 |
| |
| def initialize(self, host, cmdline_args): |
| super(firmware_ECUsbPorts, self).initialize(host, cmdline_args) |
| # Don't bother if there is no Chrome EC. |
| if not self.check_ec_capability(['usb']): |
| raise error.TestNAError("Nothing needs to be tested on this device") |
| # Only run in normal mode |
| self.switcher.setup_mode('normal') |
| self.ec.send_command("chan 0") |
| |
| |
| def cleanup(self): |
| try: |
| self.ec.send_command("chan 0xffffffff") |
| except Exception as e: |
| logging.error("Caught exception: %s", str(e)) |
| super(firmware_ECUsbPorts, self).cleanup() |
| |
| |
| def fake_reboot_by_usb_mode_change(self): |
| """ |
| Turn off USB ports and also kill FAFT client so that this acts like a |
| reboot. If USB ports cannot be turned off or on, reboot step would |
| fail. |
| """ |
| for_all_ports_cmd = ('id=0; while [ $id -lt %d ];' + |
| 'do ectool usbchargemode "$id" %d;' + |
| 'id=$((id+1)); sleep 0.5; done') |
| # Port disable - same for smart and dumb ports. |
| ports_off_cmd = for_all_ports_cmd % (self._port_count, |
| self.USB_CHARGE_MODE_DISABLED) |
| # Port enable - different command based on smart/dumb port. |
| port_enable_param = (self.USB_CHARGE_MODE_SDP2 |
| if self._smart_usb_charge else self.USB_CHARGE_MODE_ENABLED) |
| ports_on_cmd = for_all_ports_cmd % (self._port_count, port_enable_param) |
| cmd = ("(sleep %d; %s; sleep %d; %s)&" % |
| (self.RPC_DELAY, ports_off_cmd, |
| self.REBOOT_DELAY, |
| ports_on_cmd)) |
| self.faft_client.System.RunShellCommand(cmd) |
| self.faft_client.disconnect() |
| |
| def __get_usb_enable_name(self, idx): |
| """Returns the USB enable signal name for a given index""" |
| if hasattr(self.faft_config, 'custom_usb_enable_names'): |
| if idx >= len(self.faft_config.custom_usb_enable_names): |
| raise error.TestFail('No USB enable for index %d' % idx) |
| return self.faft_config.custom_usb_enable_names[idx] |
| else: |
| return "USB%d_ENABLE" % (idx + 1) |
| |
| def get_port_count(self): |
| """Get the number of USB ports.""" |
| cnt = 0 |
| limit = 10 |
| while limit > 0: |
| try: |
| gpio_name = self.__get_usb_enable_name(cnt) |
| self.ec.send_command_get_output( |
| "gpioget %s" % gpio_name, |
| ["[01].\s*%s" % gpio_name]) |
| cnt = cnt + 1 |
| limit = limit - 1 |
| except error.TestFail: |
| logging.info("Found %d USB ports", cnt) |
| return cnt |
| |
| # Limit reached. Probably something went wrong. |
| raise error.TestFail("Unexpected error while trying to determine " + |
| "number of USB ports") |
| |
| |
| def wait_port_disabled(self, port_count, timeout): |
| """ |
| Wait for all USB ports to be disabled. |
| |
| Args: |
| @param port_count: Number of USB ports. |
| @param timeout: Timeout range. |
| """ |
| logging.info('Waiting for %d USB ports to be disabled.', port_count) |
| while timeout > 0: |
| try: |
| timeout = timeout - 1 |
| for idx in xrange(0, port_count): |
| gpio_name = self.__get_usb_enable_name(idx) |
| self.ec.send_command_get_output( |
| "gpioget %s" % gpio_name, |
| ["0.\s*%s" % gpio_name]) |
| return True |
| except error.TestFail: |
| # USB ports not disabled. Retry. |
| pass |
| return False |
| |
| |
| def check_power_off_mode(self): |
| """Shutdown the system and check USB ports are disabled.""" |
| self._failed = False |
| self.faft_client.System.RunShellCommand("shutdown -P now") |
| self.switcher.wait_for_client_offline() |
| if not self.wait_port_disabled(self._port_count, self.SHUTDOWN_TIMEOUT): |
| logging.info("Fails to wait for USB port disabled") |
| self._failed = True |
| self.servo.power_short_press() |
| |
| |
| def check_failure(self): |
| """Returns true if failure has been encountered.""" |
| return not self._failed |
| |
| |
| def run_once(self): |
| """Execute the main body of the test. |
| """ |
| self._smart_usb_charge = ( |
| 'smart_usb_charge' in self.faft_config.ec_capability) |
| self._port_count = self.get_port_count() |
| |
| if self._port_count == 0: |
| raise error.TestNAError("No USB-A port; nothing needs to be tested") |
| |
| if self.servo.running_through_ccd(): |
| logging.info("Using CCD, ignore checking USB port connection.") |
| else: |
| logging.info("Turn off all USB ports and then turn them on again.") |
| self.switcher.mode_aware_reboot( |
| 'custom', self.fake_reboot_by_usb_mode_change) |
| |
| logging.info("Check USB ports are disabled when powered off.") |
| self.switcher.mode_aware_reboot('custom', self.check_power_off_mode) |
| |
| logging.info("Check if failure occurred.") |
| self.check_state(self.check_failure) |