184 lines
8.1 KiB
Python
184 lines
8.1 KiB
Python
# Copyright 2016 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import logging
|
|
import random
|
|
import time
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
|
|
from autotest_lib.server.cros.servo import pd_device
|
|
|
|
class firmware_PDTrySrc(FirmwareTest):
|
|
"""
|
|
Servo based USB PD Try.SRC protocol test.
|
|
|
|
When a PD device supports Try.SRC mode and it's enabled, it will attempt
|
|
to always connect as a SRC device. This test is therefore only applicable
|
|
if both devices support dualrole and at least one device supports Try.SRC.
|
|
|
|
Pass criteria is that when Try.SRC is enabled the device connects > 95% of
|
|
the time in SRC mode. When it is disabled, there must be at least 25%
|
|
variation in connecting as SRC and SNK.
|
|
"""
|
|
|
|
version = 1
|
|
CONNECT_ITERATIONS = 20
|
|
PD_DISCONNECT_TIME = 1
|
|
PD_STABLE_DELAY = 3
|
|
SNK = 0
|
|
SRC = 1
|
|
TRYSRC_OFF_THRESHOLD = 15.0
|
|
TRYSRC_ON_THRESHOLD = 96.0
|
|
|
|
def _execute_connect_sequence(self, usbpd_dev, pdtester_dev, trysrc):
|
|
"""Execute mulitple connections and track power role
|
|
|
|
This method will disconnect/connect a TypeC PD port and
|
|
collect the power role statistics of each connection. The time
|
|
delay for reconnect adds a random delay so that test to increase
|
|
randomness for dualrole swaps.
|
|
|
|
@param usbpd_dev: PD device object of DUT
|
|
@param pdtester_dev: PD device object of PDTester
|
|
@param trysrc: True to enable TrySrc before disconnect/connect,
|
|
False to disable TrySrc, None to do nothing.
|
|
|
|
@returns list with number of SNK and SRC connections
|
|
"""
|
|
stats = [0, 0]
|
|
random.seed()
|
|
# Try N disconnect/connects
|
|
for attempt in xrange(self.CONNECT_ITERATIONS):
|
|
try:
|
|
# Disconnect time from 1 to 1.5 seconds
|
|
disc_time = self.PD_DISCONNECT_TIME + random.random() / 2
|
|
logging.info('Disconnect time = %.2f seconds', disc_time)
|
|
# Set the TrySrc value on DUT
|
|
if trysrc is not None:
|
|
usbpd_dev.try_src(trysrc)
|
|
# Force disconnect/connect and get the connected state
|
|
state = pdtester_dev.get_connected_state_after_cc_reconnect(
|
|
disc_time)
|
|
# Update connection stats according to the returned state
|
|
if pdtester_dev.is_snk(state):
|
|
stats[self.SNK] += 1
|
|
logging.info('Power Role = SNK')
|
|
elif pdtester_dev.is_src(state):
|
|
stats[self.SRC] += 1
|
|
logging.info('Power Role = SRC')
|
|
# Wait a bit before the next iteration, in case any PR_Swap
|
|
time.sleep(self.PD_STABLE_DELAY)
|
|
except NotImplementedError:
|
|
raise error.TestFail('TrySRC disconnect requires PDTester')
|
|
logging.info('SNK = %d: SRC = %d: Total = %d',
|
|
stats[0], stats[1], self.CONNECT_ITERATIONS)
|
|
return stats
|
|
|
|
def initialize(self, host, cmdline_args, flip_cc=False):
|
|
super(firmware_PDTrySrc, self).initialize(host, cmdline_args)
|
|
self.setup_pdtester(flip_cc, min_batt_level=10)
|
|
# Only run in normal mode
|
|
self.switcher.setup_mode('normal')
|
|
# Turn off console prints, except for USBPD.
|
|
self.usbpd.enable_console_channel('usbpd')
|
|
|
|
def cleanup(self):
|
|
self.usbpd.send_command('chan 0xffffffff')
|
|
super(firmware_PDTrySrc, self).cleanup()
|
|
|
|
def run_once(self):
|
|
"""Execute Try.SRC PD protocol test
|
|
|
|
1. Verify that DUT <-> PDTester device pair exists
|
|
2. Verify that DUT supports dualrole
|
|
3. Verify that DUT supports Try.SRC mode
|
|
4. Enable Try.SRC mode, execute disc/connect sequences
|
|
5. Disable Try.SRC mode, execute disc/connect sequences
|
|
6. Compute DUT SRC/SNK connect ratios for both modes
|
|
7. Compare SRC connect ratio to threholds to determine pass/fail
|
|
"""
|
|
|
|
# Create list of available UART consoles
|
|
consoles = [self.usbpd, self.pdtester]
|
|
# Backup the original dualrole settings
|
|
original_drp = [None, None]
|
|
port_partner = pd_device.PDPortPartner(consoles)
|
|
# Identify PDTester <-> DUT PD device pair
|
|
port_pair = port_partner.identify_pd_devices()
|
|
if not port_pair:
|
|
raise error.TestFail('No DUT to PDTester connection found!')
|
|
|
|
# TODO Device pair must have PDTester so that the disconnect/connect
|
|
# sequence does not affect the SRC/SNK connection. PDTester provides
|
|
# a 'fakedisconnect' feature which more closely resembles unplugging
|
|
# and replugging a Type C cable.
|
|
for side in xrange(len(port_pair)):
|
|
original_drp[side] = port_pair[side].drp_get()
|
|
if port_pair[side].is_pdtester:
|
|
# Identify PDTester and DUT device
|
|
p_idx = side
|
|
d_idx = side ^ 1
|
|
|
|
try:
|
|
# Both devices must support dualrole mode for this test.
|
|
for port in port_pair:
|
|
try:
|
|
if not port.drp_set('on'):
|
|
raise error.TestFail('Could not enable DRP')
|
|
except NotImplementedError:
|
|
raise error.TestFail('Both devices must support DRP')
|
|
|
|
# Check to see if DUT supports Try.SRC mode
|
|
try_src_supported = port_pair[d_idx].try_src(True)
|
|
|
|
if not try_src_supported:
|
|
logging.warn('DUT does not support Try.SRC feature. '
|
|
'Skip running Try.SRC-enabled test case.')
|
|
else:
|
|
# Run disconnect/connect sequence with Try.SRC enabled
|
|
stats_on = self._execute_connect_sequence(
|
|
usbpd_dev=port_pair[d_idx],
|
|
pdtester_dev=port_pair[p_idx],
|
|
trysrc=True)
|
|
|
|
# Run disconnect/connect sequence with Try.SRC disabled
|
|
stats_off = self._execute_connect_sequence(
|
|
usbpd_dev=port_pair[d_idx],
|
|
pdtester_dev=port_pair[p_idx],
|
|
trysrc=(False if try_src_supported else None))
|
|
|
|
# Compute SNK/(SNK+SRC) ratio (percentage) for Try.SRC off case
|
|
total_off = float(stats_off[self.SNK] + stats_off[self.SRC])
|
|
trysrc_off = float(stats_off[self.SNK]) / total_off * 100.0
|
|
logging.info('SNK ratio with Try.SRC disabled = %.1f%%', trysrc_off)
|
|
|
|
# When Try.SRC is off, ideally the SNK/SRC ratio will be close to
|
|
# 50%. However, in practice there is a wide range related to the
|
|
# dualrole swap timers in firmware.
|
|
if (trysrc_off < self.TRYSRC_OFF_THRESHOLD or
|
|
trysrc_off > 100 - self.TRYSRC_OFF_THRESHOLD):
|
|
raise error.TestFail('SRC %% = %.1f: Must be > %.1f & < %.1f' %
|
|
(trysrc_off, self.TRYSRC_OFF_THRESHOLD,
|
|
100 - self.TRYSRC_OFF_THRESHOLD))
|
|
|
|
if try_src_supported:
|
|
# Compute SNK/(SNK+SRC) ratio (percentage) for Try.SRC on case
|
|
total_on = float(stats_on[self.SNK] + stats_on[self.SRC])
|
|
trysrc_on = float(stats_on[self.SNK]) / total_on * 100.0
|
|
logging.info('SNK ratio with Try.SRC enabled = %.1f%%',
|
|
trysrc_on)
|
|
|
|
# When Try.SRC is on, the SRC/SNK, the DUT should connect in SRC
|
|
# mode nearly 100% of the time.
|
|
if trysrc_on < self.TRYSRC_ON_THRESHOLD:
|
|
raise error.TestFail('SRC %% = %.1f: Must be > %.1f' %
|
|
(trysrc_on, self.TRYSRC_ON_THRESHOLD))
|
|
finally:
|
|
# Reenable Try.SRC mode
|
|
port_pair[d_idx].try_src(True)
|
|
# Restore the original dualrole settings
|
|
for side in xrange(len(port_pair)):
|
|
port_pair[side].drp_set(original_drp[side])
|