| # Copyright 2016 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 |
| import random |
| import time |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.server.cros.faft.firmware_test import FirmwareTest |
| from autotest_lib.server.cros.servo import pd_device |
| |
| |
| class firmware_PDTrySrc(FirmwareTest): |
| """ |
| Servo based USB PD Try.SRC protocol test. |
| |
| When a PD device supports Try.SRC mode and it's enabled, it will attempt |
| to always connect as a SRC device. This test is therefore only applicable |
| if both devices support dualrole and at least one device supports Try.SRC. |
| |
| Pass criteria is that when Try.SRC is enabled the device connects > 95% of |
| the time in SRC mode. When it is disabled, there must be at least 25% |
| variation in connecting as SRC and SNK. |
| """ |
| version = 1 |
| |
| CONNECT_ITERATIONS = 20 |
| PD_DISCONNECT_TIME = 1 |
| PD_CONNECT_DELAY = 4 |
| SNK = 0 |
| SRC = 1 |
| TRYSRC_OFF_THRESHOLD = 15.0 |
| TRYSRC_ON_THRESHOLD = 96.0 |
| |
| def _execute_connect_sequence(self, device): |
| """Execute mulitple connections and track power role |
| |
| This method will disconnect/connect a TypeC PD port and |
| collect the power role statistics of each connection. The time |
| delay for reconnect adds a random delay so that test to increase |
| randomness for dualrole swaps. |
| |
| @param device: PD device object |
| |
| @returns list with number of SNK and SRC connections |
| """ |
| stats = [0, 0] |
| random.seed() |
| # Try N disconnect/connects |
| for attempt in xrange(self.CONNECT_ITERATIONS): |
| try: |
| # Disconnect time from 1 to 2 seconds |
| disc_time = self.PD_DISCONNECT_TIME + random.random() |
| logging.info('Disconnect time = %.2f seconds', disc_time) |
| # Force disconnect/connect |
| device.cc_disconnect_connect(disc_time) |
| # Wait for connection to be reestablished |
| time.sleep(self.PD_DISCONNECT_TIME + self.PD_CONNECT_DELAY) |
| # Check power role and update connection stats |
| if device.is_snk(): |
| stats[self.SNK] += 1; |
| logging.info('Power Role = SNK') |
| elif device.is_src(): |
| stats[self.SRC] += 1; |
| logging.info('Power Role = SRC') |
| except NotImplementedError: |
| raise error.TestFail('TrySRC disconnect requires Plankton') |
| logging.info('SNK = %d: SRC = %d: Total = %d', |
| stats[0], stats[1], self.CONNECT_ITERATIONS) |
| return stats |
| |
| def initialize(self, host, cmdline_args): |
| super(firmware_PDTrySrc, self).initialize(host, cmdline_args) |
| # Only run in normal mode |
| self.switcher.setup_mode('normal') |
| # Turn off console prints, except for USBPD. |
| self.usbpd.send_command('chan 0x08000000') |
| |
| def cleanup(self): |
| self.usbpd.send_command('chan 0xffffffff') |
| super(firmware_PDTrySrc, self).cleanup() |
| |
| def run_once(self): |
| """Execute Try.SRC PD protocol test |
| |
| 1. Verify that DUT <-> Plankton device pair exists |
| 2. Verify that DUT supports dualrole |
| 3. Verify that DUT supports Try.SRC mode |
| 4. Enable Try.SRC mode, execute disc/connect sequences |
| 5. Disable Try.SRC mode, execute disc/connect sequences |
| 6. Compute DUT SRC/SNK connect ratios for both modes |
| 7. Compare SRC connect ratio to threholds to determine pass/fail |
| """ |
| |
| # Create list of available UART consoles |
| consoles = [self.usbpd, self.plankton] |
| port_partner = pd_device.PDPortPartner(consoles) |
| # Identify Plankton <-> DUT PD device pair |
| port_pair = port_partner.identify_pd_devices() |
| if not port_pair: |
| raise error.TestFail('No DUT to Plankton connection found!') |
| |
| # TODO Device pair must have Plankton so that the disconnect/connect |
| # sequence does not affect the SRC/SNK connection. Plankton provides |
| # a 'fake_disconnect' feature which more closely resembles unplugging |
| # and replugging a Type C cable. |
| |
| # Both devices must support dualrole mode for this test. In addtion, |
| # at least one device must support Try.SRC mode. |
| for side in xrange(len(port_pair)): |
| try: |
| if not port_pair[side].drp_set('on'): |
| raise error.TestFail('Could not enable DRP') |
| except NotImplementedError: |
| raise error.TestFail('Both devices must support DRP') |
| if port_pair[side].is_plankton: |
| # Identify Plankton and DUT device |
| p_idx = side |
| d_idx = side ^ 1 |
| |
| # Make sure that DUT supports Try.SRC mode |
| if not port_pair[d_idx].try_src(True): |
| raise error.TestFail('DUT does not support Try.SRC feature') |
| # Run disconnect/connect sequence with Try.SRC enabled |
| stats_on = self._execute_connect_sequence(port_pair[p_idx]) |
| # Disable Try.SRC mode |
| port_pair[d_idx].try_src(False) |
| # Run disconnect/connect sequence with Try.SRC disabled |
| stats_off = self._execute_connect_sequence(port_pair[p_idx]) |
| # Reenable Try.SRC mode |
| port_pair[d_idx].try_src(True) |
| |
| # Compute SRC connect ratio/percent for Try.SRC on and off cases |
| total_on = float(stats_on[self.SNK] + stats_on[self.SRC]) |
| total_off = float(stats_off[self.SNK] + stats_off[self.SRC]) |
| trysrc_on = float(stats_on[self.SNK]) / total_on * 100.0 |
| trysrc_off = float(stats_off[self.SNK]) / total_off * 100.0 |
| logging.info('DUT Try.SRC on = %.1f%%: off = %.1f%%', |
| trysrc_off, trysrc_on) |
| |
| # When Try.SRC is off, ideally the SNK/SRC ratio will be close to |
| # 50%. However, in practice there is a wide range related to the |
| # dualrole swap timers in firmware. |
| if (trysrc_off < self.TRYSRC_OFF_THRESHOLD or |
| trysrc_off > 100 - self.TRYSRC_OFF_THRESHOLD): |
| raise error.TestFail('SRC %% = %.1f: Must be > %.1f & < %.1f' % |
| (trysrc_off, self.TRYSRC_OFF_THRESHOLD, |
| 100 - self.TRYSRC_OFF_THRESHOLD)) |
| # When Try.SRC is on, the SRC/SNK, the DUT should connect in SRC |
| # mode nearly 100% of the time. |
| if trysrc_on < self.TRYSRC_ON_THRESHOLD: |
| raise error.TestFail('SRC %% = %.1f: Must be > %.1f' % |
| (trysrc_on, self.TRYSRC_ON_THRESHOLD)) |
| |