• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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