1# Copyright 2016 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 6import random 7import time 8 9from autotest_lib.client.common_lib import error 10from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 11from autotest_lib.server.cros.servo import pd_device 12 13class firmware_PDTrySrc(FirmwareTest): 14 """ 15 Servo based USB PD Try.SRC protocol test. 16 17 When a PD device supports Try.SRC mode and it's enabled, it will attempt 18 to always connect as a SRC device. This test is therefore only applicable 19 if both devices support dualrole and at least one device supports Try.SRC. 20 21 Pass criteria is that when Try.SRC is enabled the device connects > 95% of 22 the time in SRC mode. When it is disabled, there must be at least 25% 23 variation in connecting as SRC and SNK. 24 """ 25 26 version = 1 27 CONNECT_ITERATIONS = 20 28 PD_DISCONNECT_TIME = 1 29 PD_STABLE_DELAY = 3 30 SNK = 0 31 SRC = 1 32 TRYSRC_OFF_THRESHOLD = 15.0 33 TRYSRC_ON_THRESHOLD = 96.0 34 35 def _execute_connect_sequence(self, usbpd_dev, pdtester_dev, trysrc): 36 """Execute mulitple connections and track power role 37 38 This method will disconnect/connect a TypeC PD port and 39 collect the power role statistics of each connection. The time 40 delay for reconnect adds a random delay so that test to increase 41 randomness for dualrole swaps. 42 43 @param usbpd_dev: PD device object of DUT 44 @param pdtester_dev: PD device object of PDTester 45 @param trysrc: True to enable TrySrc before disconnect/connect, 46 False to disable TrySrc, None to do nothing. 47 48 @returns list with number of SNK and SRC connections 49 """ 50 stats = [0, 0] 51 random.seed() 52 # Try N disconnect/connects 53 for attempt in range(self.CONNECT_ITERATIONS): 54 try: 55 # Disconnect time from 1 to 1.5 seconds 56 disc_time = self.PD_DISCONNECT_TIME + random.random() / 2 57 logging.info('Disconnect time = %.2f seconds', disc_time) 58 # Set the TrySrc value on DUT 59 if trysrc is not None: 60 usbpd_dev.try_src(trysrc) 61 # Force disconnect/connect and get the connected state 62 state = pdtester_dev.get_connected_state_after_cc_reconnect( 63 disc_time) 64 # Update connection stats according to the returned state 65 if pdtester_dev.is_snk(state): 66 stats[self.SNK] += 1 67 logging.info('Power Role = SNK') 68 elif pdtester_dev.is_src(state): 69 stats[self.SRC] += 1 70 logging.info('Power Role = SRC') 71 # Wait a bit before the next iteration, in case any PR_Swap 72 time.sleep(self.PD_STABLE_DELAY) 73 except NotImplementedError: 74 raise error.TestFail('TrySRC disconnect requires PDTester') 75 logging.info('SNK = %d: SRC = %d: Total = %d', 76 stats[0], stats[1], self.CONNECT_ITERATIONS) 77 return stats 78 79 def initialize(self, host, cmdline_args, flip_cc=False): 80 super(firmware_PDTrySrc, self).initialize(host, cmdline_args) 81 self.setup_pdtester(flip_cc, min_batt_level=10) 82 # Only run in normal mode 83 self.switcher.setup_mode('normal') 84 # Turn off console prints, except for USBPD. 85 self.usbpd.enable_console_channel('usbpd') 86 87 def cleanup(self): 88 self.usbpd.send_command('chan 0xffffffff') 89 super(firmware_PDTrySrc, self).cleanup() 90 91 def run_once(self): 92 """Execute Try.SRC PD protocol test 93 94 1. Verify that DUT <-> PDTester device pair exists 95 2. Verify that DUT supports dualrole 96 3. Verify that DUT supports Try.SRC mode 97 4. Enable Try.SRC mode, execute disc/connect sequences 98 5. Disable Try.SRC mode, execute disc/connect sequences 99 6. Compute DUT SRC/SNK connect ratios for both modes 100 7. Compare SRC connect ratio to threholds to determine pass/fail 101 """ 102 103 # Create list of available UART consoles 104 consoles = [self.usbpd, self.pdtester] 105 # Backup the original dualrole settings 106 original_drp = [None, None] 107 port_partner = pd_device.PDPortPartner(consoles) 108 # Identify PDTester <-> DUT PD device pair 109 port_pair = port_partner.identify_pd_devices() 110 if not port_pair: 111 raise error.TestFail('No DUT to PDTester connection found!') 112 113 # TODO Device pair must have PDTester so that the disconnect/connect 114 # sequence does not affect the SRC/SNK connection. PDTester provides 115 # a 'fakedisconnect' feature which more closely resembles unplugging 116 # and replugging a Type C cable. 117 for side in range(len(port_pair)): 118 original_drp[side] = port_pair[side].drp_get() 119 if port_pair[side].is_pdtester: 120 # Identify PDTester and DUT device 121 p_idx = side 122 d_idx = side ^ 1 123 124 try: 125 # Both devices must support dualrole mode for this test. 126 for port in port_pair: 127 try: 128 if not port.drp_set('on'): 129 raise error.TestFail('Could not enable DRP') 130 except NotImplementedError: 131 raise error.TestFail('Both devices must support DRP') 132 133 # Setting DRP on ServoV4 ('usbc_action drp') triggers reconnect 134 # Wait some time to ensure that no operation will occur during test 135 time.sleep(port_pair[p_idx].utils.CONNECT_TIME) 136 137 # Check to see if DUT supports Try.SRC mode 138 try_src_supported = port_pair[d_idx].try_src(True) 139 140 if not try_src_supported: 141 logging.warning('DUT does not support Try.SRC feature. ' 142 'Skip running Try.SRC-enabled test case.') 143 else: 144 # Run disconnect/connect sequence with Try.SRC enabled 145 stats_on = self._execute_connect_sequence( 146 usbpd_dev=port_pair[d_idx], 147 pdtester_dev=port_pair[p_idx], 148 trysrc=True) 149 150 # Run disconnect/connect sequence with Try.SRC disabled 151 stats_off = self._execute_connect_sequence( 152 usbpd_dev=port_pair[d_idx], 153 pdtester_dev=port_pair[p_idx], 154 trysrc=(False if try_src_supported else None)) 155 156 # Compute SNK/(SNK+SRC) ratio (percentage) for Try.SRC off case 157 total_off = float(stats_off[self.SNK] + stats_off[self.SRC]) 158 trysrc_off = float(stats_off[self.SNK]) / total_off * 100.0 159 logging.info('SNK ratio with Try.SRC disabled = %.1f%%', trysrc_off) 160 161 # When Try.SRC is off, ideally the SNK/SRC ratio will be close to 162 # 50%. However, in practice there is a wide range related to the 163 # dualrole swap timers in firmware. 164 if (trysrc_off < self.TRYSRC_OFF_THRESHOLD or 165 trysrc_off > 100 - self.TRYSRC_OFF_THRESHOLD): 166 raise error.TestFail('SRC %% = %.1f: Must be > %.1f & < %.1f' % 167 (trysrc_off, self.TRYSRC_OFF_THRESHOLD, 168 100 - self.TRYSRC_OFF_THRESHOLD)) 169 170 if try_src_supported: 171 # Compute SNK/(SNK+SRC) ratio (percentage) for Try.SRC on case 172 total_on = float(stats_on[self.SNK] + stats_on[self.SRC]) 173 trysrc_on = float(stats_on[self.SNK]) / total_on * 100.0 174 logging.info('SNK ratio with Try.SRC enabled = %.1f%%', 175 trysrc_on) 176 177 # When Try.SRC is on, the SRC/SNK, the DUT should connect in SRC 178 # mode nearly 100% of the time. 179 if trysrc_on < self.TRYSRC_ON_THRESHOLD: 180 raise error.TestFail('SRC %% = %.1f: Must be > %.1f' % 181 (trysrc_on, self.TRYSRC_ON_THRESHOLD)) 182 finally: 183 # Reenable Try.SRC mode 184 port_pair[d_idx].try_src(True) 185 # Restore the original dualrole settings 186 for side in range(len(port_pair)): 187 port_pair[side].drp_set(original_drp[side]) 188