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, device): 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 device: PD device object 45 46 @returns list with number of SNK and SRC connections 47 """ 48 stats = [0, 0] 49 random.seed() 50 # Try N disconnect/connects 51 for attempt in xrange(self.CONNECT_ITERATIONS): 52 try: 53 # Disconnect time from 1 to 2 seconds 54 disc_time = self.PD_DISCONNECT_TIME + random.random() 55 logging.info('Disconnect time = %.2f seconds', disc_time) 56 # Force disconnect/connect 57 device.cc_disconnect_connect(disc_time) 58 # Wait for connection to be reestablished 59 time.sleep(self.PD_DISCONNECT_TIME + self.PD_CONNECT_DELAY) 60 # Check power role and update connection stats 61 if device.is_snk(): 62 stats[self.SNK] += 1; 63 logging.info('Power Role = SNK') 64 elif device.is_src(): 65 stats[self.SRC] += 1; 66 logging.info('Power Role = SRC') 67 except NotImplementedError: 68 raise error.TestFail('TrySRC disconnect requires Plankton') 69 logging.info('SNK = %d: SRC = %d: Total = %d', 70 stats[0], stats[1], self.CONNECT_ITERATIONS) 71 return stats 72 73 def initialize(self, host, cmdline_args): 74 super(firmware_PDTrySrc, self).initialize(host, cmdline_args) 75 # Only run in normal mode 76 self.switcher.setup_mode('normal') 77 # Turn off console prints, except for USBPD. 78 self.usbpd.send_command('chan 0x08000000') 79 80 def cleanup(self): 81 self.usbpd.send_command('chan 0xffffffff') 82 super(firmware_PDTrySrc, self).cleanup() 83 84 def run_once(self): 85 """Execute Try.SRC PD protocol test 86 87 1. Verify that DUT <-> Plankton device pair exists 88 2. Verify that DUT supports dualrole 89 3. Verify that DUT supports Try.SRC mode 90 4. Enable Try.SRC mode, execute disc/connect sequences 91 5. Disable Try.SRC mode, execute disc/connect sequences 92 6. Compute DUT SRC/SNK connect ratios for both modes 93 7. Compare SRC connect ratio to threholds to determine pass/fail 94 """ 95 96 # Create list of available UART consoles 97 consoles = [self.usbpd, self.plankton] 98 port_partner = pd_device.PDPortPartner(consoles) 99 # Identify Plankton <-> DUT PD device pair 100 port_pair = port_partner.identify_pd_devices() 101 if not port_pair: 102 raise error.TestFail('No DUT to Plankton connection found!') 103 104 # TODO Device pair must have Plankton so that the disconnect/connect 105 # sequence does not affect the SRC/SNK connection. Plankton provides 106 # a 'fake_disconnect' feature which more closely resembles unplugging 107 # and replugging a Type C cable. 108 109 # Both devices must support dualrole mode for this test. In addtion, 110 # at least one device must support Try.SRC mode. 111 for side in xrange(len(port_pair)): 112 try: 113 if not port_pair[side].drp_set('on'): 114 raise error.TestFail('Could not enable DRP') 115 except NotImplementedError: 116 raise error.TestFail('Both devices must support DRP') 117 if port_pair[side].is_plankton: 118 # Identify Plankton and DUT device 119 p_idx = side 120 d_idx = side ^ 1 121 122 # Make sure that DUT supports Try.SRC mode 123 if not port_pair[d_idx].try_src(True): 124 raise error.TestFail('DUT does not support Try.SRC feature') 125 # Run disconnect/connect sequence with Try.SRC enabled 126 stats_on = self._execute_connect_sequence(port_pair[p_idx]) 127 # Disable Try.SRC mode 128 port_pair[d_idx].try_src(False) 129 # Run disconnect/connect sequence with Try.SRC disabled 130 stats_off = self._execute_connect_sequence(port_pair[p_idx]) 131 # Reenable Try.SRC mode 132 port_pair[d_idx].try_src(True) 133 134 # Compute SRC connect ratio/percent for Try.SRC on and off cases 135 total_on = float(stats_on[self.SNK] + stats_on[self.SRC]) 136 total_off = float(stats_off[self.SNK] + stats_off[self.SRC]) 137 trysrc_on = float(stats_on[self.SNK]) / total_on * 100.0 138 trysrc_off = float(stats_off[self.SNK]) / total_off * 100.0 139 logging.info('DUT Try.SRC on = %.1f%%: off = %.1f%%', 140 trysrc_off, trysrc_on) 141 142 # When Try.SRC is off, ideally the SNK/SRC ratio will be close to 143 # 50%. However, in practice there is a wide range related to the 144 # dualrole swap timers in firmware. 145 if (trysrc_off < self.TRYSRC_OFF_THRESHOLD or 146 trysrc_off > 100 - self.TRYSRC_OFF_THRESHOLD): 147 raise error.TestFail('SRC %% = %.1f: Must be > %.1f & < %.1f' % 148 (trysrc_off, self.TRYSRC_OFF_THRESHOLD, 149 100 - self.TRYSRC_OFF_THRESHOLD)) 150 # When Try.SRC is on, the SRC/SNK, the DUT should connect in SRC 151 # mode nearly 100% of the time. 152 if trysrc_on < self.TRYSRC_ON_THRESHOLD: 153 raise error.TestFail('SRC %% = %.1f: Must be > %.1f' % 154 (trysrc_on, self.TRYSRC_ON_THRESHOLD)) 155 156