Home | History | Annotate | Download | only in firmware_PDTrySrc
      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 
      5 import logging
      6 import random
      7 import time
      8 
      9 from autotest_lib.client.common_lib import error
     10 from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
     11 from autotest_lib.server.cros.servo import pd_device
     12 
     13 
     14 class 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