# 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_STABLE_DELAY = 3 SNK = 0 SRC = 1 TRYSRC_OFF_THRESHOLD = 15.0 TRYSRC_ON_THRESHOLD = 96.0 def _execute_connect_sequence(self, usbpd_dev, pdtester_dev, trysrc): """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 usbpd_dev: PD device object of DUT @param pdtester_dev: PD device object of PDTester @param trysrc: True to enable TrySrc before disconnect/connect, False to disable TrySrc, None to do nothing. @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 1.5 seconds disc_time = self.PD_DISCONNECT_TIME + random.random() / 2 logging.info('Disconnect time = %.2f seconds', disc_time) # Set the TrySrc value on DUT if trysrc is not None: usbpd_dev.try_src(trysrc) # Force disconnect/connect and get the connected state state = pdtester_dev.get_connected_state_after_cc_reconnect( disc_time) # Update connection stats according to the returned state if pdtester_dev.is_snk(state): stats[self.SNK] += 1 logging.info('Power Role = SNK') elif pdtester_dev.is_src(state): stats[self.SRC] += 1 logging.info('Power Role = SRC') # Wait a bit before the next iteration, in case any PR_Swap time.sleep(self.PD_STABLE_DELAY) except NotImplementedError: raise error.TestFail('TrySRC disconnect requires PDTester') logging.info('SNK = %d: SRC = %d: Total = %d', stats[0], stats[1], self.CONNECT_ITERATIONS) return stats def initialize(self, host, cmdline_args, flip_cc=False): super(firmware_PDTrySrc, self).initialize(host, cmdline_args) self.setup_pdtester(flip_cc, min_batt_level=10) # Only run in normal mode self.switcher.setup_mode('normal') # Turn off console prints, except for USBPD. self.usbpd.enable_console_channel('usbpd') 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 <-> PDTester 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.pdtester] # Backup the original dualrole settings original_drp = [None, None] port_partner = pd_device.PDPortPartner(consoles) # Identify PDTester <-> DUT PD device pair port_pair = port_partner.identify_pd_devices() if not port_pair: raise error.TestFail('No DUT to PDTester connection found!') # TODO Device pair must have PDTester so that the disconnect/connect # sequence does not affect the SRC/SNK connection. PDTester provides # a 'fakedisconnect' feature which more closely resembles unplugging # and replugging a Type C cable. for side in xrange(len(port_pair)): original_drp[side] = port_pair[side].drp_get() if port_pair[side].is_pdtester: # Identify PDTester and DUT device p_idx = side d_idx = side ^ 1 try: # Both devices must support dualrole mode for this test. for port in port_pair: try: if not port.drp_set('on'): raise error.TestFail('Could not enable DRP') except NotImplementedError: raise error.TestFail('Both devices must support DRP') # Check to see if DUT supports Try.SRC mode try_src_supported = port_pair[d_idx].try_src(True) if not try_src_supported: logging.warn('DUT does not support Try.SRC feature. ' 'Skip running Try.SRC-enabled test case.') else: # Run disconnect/connect sequence with Try.SRC enabled stats_on = self._execute_connect_sequence( usbpd_dev=port_pair[d_idx], pdtester_dev=port_pair[p_idx], trysrc=True) # Run disconnect/connect sequence with Try.SRC disabled stats_off = self._execute_connect_sequence( usbpd_dev=port_pair[d_idx], pdtester_dev=port_pair[p_idx], trysrc=(False if try_src_supported else None)) # Compute SNK/(SNK+SRC) ratio (percentage) for Try.SRC off case total_off = float(stats_off[self.SNK] + stats_off[self.SRC]) trysrc_off = float(stats_off[self.SNK]) / total_off * 100.0 logging.info('SNK ratio with Try.SRC disabled = %.1f%%', trysrc_off) # 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)) if try_src_supported: # Compute SNK/(SNK+SRC) ratio (percentage) for Try.SRC on case total_on = float(stats_on[self.SNK] + stats_on[self.SRC]) trysrc_on = float(stats_on[self.SNK]) / total_on * 100.0 logging.info('SNK ratio with Try.SRC enabled = %.1f%%', trysrc_on) # 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)) finally: # Reenable Try.SRC mode port_pair[d_idx].try_src(True) # Restore the original dualrole settings for side in xrange(len(port_pair)): port_pair[side].drp_set(original_drp[side])