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 13 14class firmware_PDTrySrc(FirmwareTest): 15 """ 16 Servo based USB PD Try.SRC protocol test. 17 18 When a PD device supports Try.SRC mode and it's enabled, it will attempt 19 to always connect as a SRC device. This test is therefore only applicable 20 if both devices support dualrole and at least one device supports Try.SRC. 21 22 Pass criteria is that when Try.SRC is enabled the device connects > 95% of 23 the time in SRC mode. When it is disabled, there must be at least 25% 24 variation in connecting as SRC and SNK. 25 """ 26 version = 1 27 28 CONNECT_ITERATIONS = 20 29 PD_DISCONNECT_TIME = 1 30 PD_CONNECT_DELAY = 4 31 SNK = 0 32 SRC = 1 33 TRYSRC_OFF_THRESHOLD = 15.0 34 TRYSRC_ON_THRESHOLD = 96.0 35 36 def _execute_connect_sequence(self, usbpd_dev, pdtester_dev, trysrc): 37 """Execute mulitple connections and track power role 38 39 This method will disconnect/connect a TypeC PD port and 40 collect the power role statistics of each connection. The time 41 delay for reconnect adds a random delay so that test to increase 42 randomness for dualrole swaps. 43 44 @param usbpd_dev: PD device object of DUT 45 @param pdtester_dev: PD device object of PDTester 46 @param trysrc: True to enable TrySrc before disconnect/connect, 47 False to disable TrySrc, None to do nothing. 48 49 @returns list with number of SNK and SRC connections 50 """ 51 stats = [0, 0] 52 random.seed() 53 # Try N disconnect/connects 54 for attempt in xrange(self.CONNECT_ITERATIONS): 55 try: 56 # Disconnect time from 1 to 2 seconds 57 disc_time = self.PD_DISCONNECT_TIME + random.random() 58 logging.info('Disconnect time = %.2f seconds', disc_time) 59 # Set the TrySrc value on DUT 60 if trysrc is not None: 61 usbpd_dev.try_src(trysrc) 62 # Force disconnect/connect 63 pdtester_dev.cc_disconnect_connect(disc_time) 64 # Wait for connection to be reestablished 65 time.sleep(self.PD_DISCONNECT_TIME + self.PD_CONNECT_DELAY) 66 # Check power role and update connection stats 67 if pdtester_dev.is_snk(): 68 stats[self.SNK] += 1; 69 logging.info('Power Role = SNK') 70 elif pdtester_dev.is_src(): 71 stats[self.SRC] += 1; 72 logging.info('Power Role = SRC') 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) 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 xrange(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 # Check to see if DUT supports Try.SRC mode 134 try_src_supported = port_pair[d_idx].try_src(True) 135 136 if not try_src_supported: 137 logging.warn('DUT does not support Try.SRC feature. ' 138 'Skip running Try.SRC-enabled test case.') 139 else: 140 # Run disconnect/connect sequence with Try.SRC enabled 141 stats_on = self._execute_connect_sequence( 142 usbpd_dev=port_pair[d_idx], 143 pdtester_dev=port_pair[p_idx], 144 trysrc=True) 145 146 # Run disconnect/connect sequence with Try.SRC disabled 147 stats_off = self._execute_connect_sequence( 148 usbpd_dev=port_pair[d_idx], 149 pdtester_dev=port_pair[p_idx], 150 trysrc=(False if try_src_supported else None)) 151 152 # Compute SNK/(SNK+SRC) ratio (percentage) for Try.SRC off case 153 total_off = float(stats_off[self.SNK] + stats_off[self.SRC]) 154 trysrc_off = float(stats_off[self.SNK]) / total_off * 100.0 155 logging.info('SNK ratio with Try.SRC disabled = %.1f%%', trysrc_off) 156 157 # When Try.SRC is off, ideally the SNK/SRC ratio will be close to 158 # 50%. However, in practice there is a wide range related to the 159 # dualrole swap timers in firmware. 160 if (trysrc_off < self.TRYSRC_OFF_THRESHOLD or 161 trysrc_off > 100 - self.TRYSRC_OFF_THRESHOLD): 162 raise error.TestFail('SRC %% = %.1f: Must be > %.1f & < %.1f' % 163 (trysrc_off, self.TRYSRC_OFF_THRESHOLD, 164 100 - self.TRYSRC_OFF_THRESHOLD)) 165 166 if try_src_supported: 167 # Compute SNK/(SNK+SRC) ratio (percentage) for Try.SRC on case 168 total_on = float(stats_on[self.SNK] + stats_on[self.SRC]) 169 trysrc_on = float(stats_on[self.SNK]) / total_on * 100.0 170 logging.info('SNK ratio with Try.SRC enabled = %.1f%%', 171 trysrc_on) 172 173 # When Try.SRC is on, the SRC/SNK, the DUT should connect in SRC 174 # mode nearly 100% of the time. 175 if trysrc_on < self.TRYSRC_ON_THRESHOLD: 176 raise error.TestFail('SRC %% = %.1f: Must be > %.1f' % 177 (trysrc_on, self.TRYSRC_ON_THRESHOLD)) 178 finally: 179 # Reenable Try.SRC mode 180 port_pair[d_idx].try_src(True) 181 # Restore the original dualrole settings 182 for side in xrange(len(port_pair)): 183 port_pair[side].drp_set(original_drp[side]) 184