blob: 9c6a01c25e2aa9d9bd50aa5e05bb80f84be4d95b [file] [log] [blame]
# Copyright 2014 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.
"""USB type-C CC line polarity check and operation flip test w/ Plankton-Raiden.
Description
-----------
This test flips the polarity bit of the USB type-C. Usually, the test will come
along with other USB type-C test, for example, CC1/CC2 USB performance
(``removable_storage.py``).
Automated test is unstable, you may have to retry. The Plankton-Raiden board
guesses the polarity bit everytime ``SetDeviceEngaged()``. Then it tries to
flip the CC polarity to the another side. We have found that we need to charge
the DUT in order to flip, and because of the charge action, the Plankton-Raiden
board guesses the logical polarity bit again (you can find that the polarity is
switching between CC1 and CC2 back-and-forth) and thus the flipping is unstable.
Test Procedure
--------------
This test can be tested manualy with the help from operator or automatically.
For normal USB type-C cable, this is a manual test with the help from operator:
1. Check USB type-C cable connected direction is right by CC polarity
2. Show operation instruction for cable flipping to test another CC line.
For double CC cable, this is an automated test:
- This test can flip CC automatically or you can set Arg
``double_cc_flip_target`` as 'CC1' or 'CC2' to indicate the final CC
position.
- If test scheme can guarantee double CC cable connection is not twisted,
that is, Plankton CC1 is connected to DUT CC1, then it can set Arg
``double_cc_quick_check`` as True to accelerate the test.
Dependency
----------
- For manual test, you need a normal USB type-C cable to connect with
Plankton-Raiden board.
- For automated test, you need a double CC cable to connection with
Plankton-Raiden board.
Examples
--------
To manual test with a dummy BFTFixture by asking operator to flip the cable,
add this in test list::
{
"pytest_name": "plankton_cc_flip_check",
"args": {
"bft_fixture": {
"class_name":
"cros.factory.test.fixture.dummy_bft_fixture.DummyBFTFixture",
"params": {}
},
"ask_flip_operation": true,
"usb_c_index": 0,
"state_src_ready": "SNK_READY"
}
}
Automated test with a dolphin BFTFixture and flipping the polarity to CC1::
{
"pytest_name": "plankton_cc_flip_check",
"args": {
"bft_fixture": {
"class_name":
"cros.factory.test.fixture.dummy_bft_fixture.DummyBFTFixture",
"params": {}
},
"double_cc_flip_target": "CC1",
"usb_c_index": 1,
"state_src_ready": "SNK_READY"
}
}
"""
import logging
import factory_common # pylint: disable=unused-import
from cros.factory.device import device_utils
from cros.factory.test import session
from cros.factory.test.fixture import bft_fixture
from cros.factory.test.i18n import _
from cros.factory.test import test_case
from cros.factory.test import test_ui
from cros.factory.utils.arg_utils import Arg
from cros.factory.utils import sync_utils
_CC_UNCONNECT = 'UNCONNECTED'
class PlanktonCCFlipCheck(test_case.TestCase):
"""Plankton USB type-C CC line polarity check and operation flip test."""
ARGS = [
Arg('bft_fixture', dict, bft_fixture.TEST_ARG_HELP),
Arg('adb_remote_test', bool, 'Run test against remote ADB target.',
default=False),
Arg('usb_c_index', int, 'Index of DUT USB_C port'),
Arg('original_enabled_cc', str, 'Set "CC1" or "CC2" if you want to check '
'what CC pin is enabled now. There is no check if it is not set.',
default=None),
Arg('ask_flip_operation', bool,
'Determine whether to ask operator to flip cable.',
default=False),
Arg('double_cc_flip_target', str,
'If using double CC cable, set either "CC1" or "CC2" for the target '
'to flip. Flip anyway if this is not set.',
default=None),
Arg('double_cc_quick_check', bool,
'If using double CC cable, set True if you guarantee CC pair is not '
'reversed. CC polarity in Plankton side implies DUT side.',
default=False),
Arg('timeout_secs', int,
'Timeout seconds for operation, set 0 for operator pressing enter '
'key to finish operation.',
default=0),
Arg('state_src_ready', (int, str), 'State number of pd state SRC_READY.',
default=22),
Arg('wait_dut_reconnect_secs', int,
'Wait DUT to reconnect for n seconds after CC flip. This is required '
'if remote DUT might be disconnected a while after CC flip, e.g. DUT '
'has no battery and will reboot on CC flip. If n equals to 0, will '
'wait forever.', default=5),
Arg('init_cc_state_retry_times', int, 'Retry times for init CC state.',
default=3)
]
def setUp(self):
self._dut = device_utils.CreateDUTInterface()
self.ui.ToggleTemplateClass('font-large', True)
self._bft_fixture = bft_fixture.CreateBFTFixture(**self.args.bft_fixture)
self._adb_remote_test = self.args.adb_remote_test
self._double_cc_quick_check = (
self._bft_fixture.IsDoubleCCCable() and self.args.double_cc_quick_check)
if (not self._bft_fixture.IsParallelTest() and
not self._double_cc_quick_check):
# No preparation is required for parallel test.
if self._adb_remote_test:
# For remote test, keep adb connection enabled.
self._bft_fixture.SetDeviceEngaged('ADB_HOST', engage=True)
else:
self._bft_fixture.SetDeviceEngaged('USB3', engage=True)
self._bft_fixture.SetFakeDisconnection(1)
self.Sleep(1)
self._polarity = self.GetCCPolarityWithRetry(
self.args.init_cc_state_retry_times)
logging.info('Initial polarity: %s', self._polarity)
def GetCCPolarity(self):
"""Gets enabled CC line for USB_C port arg.usb_c_index.
Returns:
'CC1' or 'CC2', or _CC_UNCONNECT if it doesn't detect SRC_READY.
"""
if not self._dut.IsReady():
self.ui.SetState(_('Wait DUT to reconnect'))
session.console.info(
'Lose connection to DUT, waiting for DUT to reconnect')
sync_utils.WaitFor(lambda: self._dut.Call(['true']) == 0,
self.args.wait_dut_reconnect_secs,
poll_interval=1)
# For double CC cable, if we guarantee CC pair is not reversed, polarity in
# Plankton side implies DUT side.
if self._double_cc_quick_check:
return self._bft_fixture.GetPDState()['polarity']
port_status = self._dut.usb_c.GetPDStatus(self.args.usb_c_index)
# For newer version EC, port_status[state] will return string instead of
# state number.
if self._adb_remote_test or self._bft_fixture.IsParallelTest():
# For remote or parallel test, just feedback polarity.
return port_status['polarity']
if (port_status['state'] == self.args.state_src_ready or
port_status['state'] == 'SRC_READY'):
return port_status['polarity']
logging.info('Detected port state is not state_src_ready (expect: %s '
'or SRC_READY, got: %s).',
self.args.state_src_ready, port_status['state'])
return _CC_UNCONNECT
def CheckCCPolarityWithRetry(self, expected_polarity, retry_times):
"""Check the CC Polarity.
It will retry by retry_times argument to let PD do negotiate.
Args:
expected_polarity: expected polarity.
retry_times: retry times.
Returns:
'CC1' or 'CC2', or _CC_UNCONNECT
"""
# We may need some time for PD negotiate and settle down
retry_times_left = retry_times
polarity = self.GetCCPolarity()
while retry_times_left != 0 and polarity != expected_polarity:
self.Sleep(1)
polarity = self.GetCCPolarity()
logging.info('[%d]Poll polarity %s', retry_times_left, polarity)
retry_times_left -= 1
return polarity
def GetCCPolarityWithRetry(self, retry_times):
"""Get the CC Polarity.
It will retry by retry_times argument to let PD do negotiate.
Args:
retry_times: retry times.
Returns:
'CC1' or 'CC2', or _CC_UNCONNECT
"""
# We may need some time for PD negotiate and settle down
retry_times_left = retry_times
polarity = self.GetCCPolarity()
while retry_times_left != 0 and polarity == _CC_UNCONNECT:
self.Sleep(1)
polarity = self.GetCCPolarity()
logging.info('[%d]Poll polarity %s', retry_times_left, polarity)
retry_times_left -= 1
return polarity
def tearDown(self):
self._bft_fixture.Disconnect()
def runTest(self):
if (self.args.original_enabled_cc is not None and
self._polarity != self.args.original_enabled_cc and
not self._bft_fixture.IsDoubleCCCable()):
self.fail('Original polarity is wrong (expect: %s, got: %s). '
'Does Raiden cable connect in correct direction?' %
(self.args.original_enabled_cc, self._polarity))
if self.args.ask_flip_operation:
self.ui.SetState(_('Flip USB type-C cable and plug in again...'))
if self.args.timeout_secs == 0:
self.ui.SetState(_('And press Enter key to continue...'), append=True)
self.ui.WaitKeysOnce(test_ui.ENTER_KEY)
polarity = self.GetCCPolarity()
if polarity == self._polarity or polarity == _CC_UNCONNECT:
self.FailTask(
'DUT does not detect cable flipped. Was it really flipped?')
else:
# Start countdown timer.
self.ui.StartFailingCountdownTimer(self.args.timeout_secs)
while True:
self.Sleep(0.5)
polarity = self.GetCCPolarity()
if polarity != self._polarity and polarity != _CC_UNCONNECT:
return
elif (self._bft_fixture.IsDoubleCCCable() and
(not self.args.double_cc_flip_target or
self._polarity != self.args.double_cc_flip_target)):
if self.args.timeout_secs:
self.ui.StartFailingCountdownTimer(self.args.timeout_secs)
session.console.info('Double CC test, doing CC flip...')
# TODO(yllin): Remove this if solve the plankton firmware issue
def charge_check_flip():
self._bft_fixture.SetDeviceEngaged('CHARGE_5V', True)
self.Sleep(2)
new_polarity = self.GetCCPolarityWithRetry(5)
if new_polarity != self._polarity:
return
self._bft_fixture.SetMuxFlip(0)
self.Sleep(2)
charge_check_flip()
if self._adb_remote_test and not self._double_cc_quick_check:
# For remote test, keep adb connection enabled.
self._bft_fixture.SetDeviceEngaged('ADB_HOST', engage=True)
new_polarity = self.CheckCCPolarityWithRetry(self._polarity, 5)
if new_polarity == self._polarity:
self.FailTask('Unexpected polarity')