• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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