blob: 5b943b1749bda29db5e2483dc20ea427bf723d42 [file] [log] [blame]
# Lint as: python2, python3
# 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.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import re
import logging
from six.moves import range
import time
from autotest_lib.client.common_lib import error
from autotest_lib.server.cros.servo import pd_console
class PDDevice(object):
"""Base clase for all PD devices
This class provides a set of APIs for expected Type C PD required actions
in TypeC FAFT tests. The base class is specific for Type C devices that
do not have any console access.
"""
def is_src(self, state=None):
"""Checks if the port is connected as a source
"""
raise NotImplementedError(
'is_src should be implemented in derived class')
def is_snk(self, state=None):
"""Checks if the port is connected as a sink
@returns None
"""
raise NotImplementedError(
'is_snk should be implemented in derived class')
def is_connected(self, state=None):
"""Checks if the port is connected
@returns True if in a connected state, False otherwise
"""
return self.is_src(state) or self.is_snk(state)
def is_disconnected(self, state=None):
"""Checks if the port is disconnected
"""
raise NotImplementedError(
'is_disconnected should be implemented in derived class')
def is_ufp(self):
"""Checks if data role is UFP
"""
raise NotImplementedError(
'is_ufp should be implemented in derived class')
def is_dfp(self):
"""Checks if data role is DFP
"""
raise NotImplementedError(
'is_dfp should be implemented in derived class')
def is_drp(self):
"""Checks if dual role mode is supported
"""
raise NotImplementedError(
'is_drp should be implemented in derived class')
def dr_swap(self):
"""Attempts a data role swap
"""
raise NotImplementedError(
'dr_swap should be implemented in derived class')
def pr_swap(self):
"""Attempts a power role swap
"""
raise NotImplementedError(
'pr_swap should be implemented in derived class')
def vbus_request(self, voltage):
"""Requests a specific VBUS voltage from SRC
@param voltage: requested voltage level (5, 12, 20) in volts
"""
raise NotImplementedError(
'vbus_request should be implemented in derived class')
def soft_reset(self):
"""Initates a PD soft reset sequence
"""
raise NotImplementedError(
'soft_reset should be implemented in derived class')
def hard_reset(self):
"""Initates a PD hard reset sequence
"""
raise NotImplementedError(
'hard_reset should be implemented in derived class')
def drp_set(self, mode):
"""Sets dualrole mode
@param mode: desired dual role setting (on, off, snk, src)
"""
raise NotImplementedError(
'drp_set should be implemented in derived class')
def drp_get(self):
"""Gets dualrole mode
@returns one of the modes (on, off, snk, src)
"""
raise NotImplementedError(
'drp_set should be implemented in derived class')
def drp_disconnect_connect(self, disc_time_sec):
"""Force PD disconnect/connect via drp settings
@param disc_time_sec: Time in seconds between disconnect and reconnect
"""
raise NotImplementedError(
'drp_disconnect_connect should be implemented in derived class'
)
def cc_disconnect_connect(self, disc_time_sec):
"""Force PD disconnect/connect
@param disc_time_sec: Time in seconds between disconnect and reconnect
"""
raise NotImplementedError(
'cc_disconnect_connect should be implemented in derived class')
def get_connected_state_after_cc_reconnect(self, disc_time_sec):
"""Get the connected state after disconnect/reconnect
@param disc_time_sec: Time in seconds for disconnect period.
@returns: The connected PD state.
"""
raise NotImplementedError(
'get_connected_state_after_cc_reconnect should be implemented'
'in derived class')
class PDConsoleDevice(PDDevice):
"""Class for PD devices that have console access
This class contains methods for common PD actions for any PD device which
has UART console access. It inherits the PD device base class. In addition,
it stores both the UART console and port for the PD device.
"""
def __init__(self, console, port, utils):
"""Initialization method
@param console: UART console object
@param port: USB PD port number
"""
# Save UART console
self.console = console
# Instantiate PD utilities used by methods in this class
self.utils = utils
# Save the PD port number for this device
self.port = port
# Not a PDTester device
self.is_pdtester = False
def get_pd_state(self):
"""Get the state of the PD port"""
return self.utils.get_pd_state(self.port)
def get_pd_role(self):
"""Get the current PD power role (source or sink)
@returns: current pd state
"""
return self.utils.get_pd_role(self.port)
def is_pd_flag_set(self, key):
"""Test a bit in PD protocol state flags
The flag word contains various PD protocol state information.
This method allows for a specific flag to be tested.
@param key: dict key to retrieve the flag bit mapping
@returns True if the bit to be tested is set
"""
return self.utils.is_pd_flag_set(self.port, key)
def is_src(self, state=None):
"""Checks if the port is connected as a source.
The "state" argument allows the caller to get_pd_state() once, and then
evaluate multiple conditions without re-getting the state.
@param state: the state to check (None to get current state)
@returns True if connected as SRC, False otherwise
"""
return self.utils.is_src_connected(self.port, state)
def is_snk(self, state=None):
"""Checks if the port is connected as a sink
The "state" argument allows the caller to get_pd_state() once, and then
evaluate multiple conditions without re-getting the state.
@param state: the state to check (None to get current state)
@returns True if connected as SNK, False otherwise
"""
return self.utils.is_snk_connected(self.port, state)
def is_connected(self, state=None):
"""Checks if the port is connected
The "state" argument allows the caller to get_pd_state() once, and then
evaluate multiple conditions without re-getting the state.
@param state: the state to check (None to get current state)
@returns True if in a connected state, False otherwise
"""
return self.is_snk(state) or self.is_src(state)
def is_disconnected(self, state=None):
"""Checks if the port is disconnected
@returns True if in a disconnected state, False otherwise
"""
return self.utils.is_disconnected(self.port, state)
def __repr__(self):
"""String representation of the object"""
return "<%s %r port %s>" % (
self.__class__.__name__, self.console.name, self.port)
def is_drp(self):
"""Checks if dual role mode is supported
@returns True if dual role mode is 'on', False otherwise
"""
return self.utils.is_pd_dual_role_enabled(self.port)
def drp_disconnect_connect(self, disc_time_sec):
"""Disconnect/reconnect using drp mode settings
A PD console device doesn't have an explicit connect/disconnect
command. Instead, the dualrole mode setting is used to force
disconnects in devices which support this feature. To disconnect,
force the dualrole mode to be the opposite role of the current
connected state.
@param disc_time_sec: time in seconds to wait to reconnect
@returns True if device disconnects, then returns to a connected
state. False if either step fails.
"""
# Dualrole mode must be supported
if self.is_drp() is False:
logging.warn('Device not DRP capable, unabled to force disconnect')
return False
# Force state will be the opposite of current connect state
if self.is_src():
drp_mode = 'snk'
swap_state = self.utils.get_snk_connect_states()
else:
drp_mode = 'src'
swap_state = self.utils.get_src_connect_states()
# Force disconnect
self.drp_set(drp_mode)
# Wait for disconnect time
time.sleep(disc_time_sec)
# Verify that the device is disconnected
disconnect = self.is_disconnected()
# If the other device is dualrole, then forcing dualrole mode will
# only cause the disconnect to appear momentarily and reconnect
# in the power role forced by the drp_set() call. For this case,
# the role swap verifies that a disconnect/connect sequence occurred.
if disconnect == False:
time.sleep(self.utils.CONNECT_TIME)
# Connected, verify if power role swap has occurred
if self.utils.get_pd_state(self.port) in swap_state:
# Restore default dualrole mode
self.drp_set('on')
# Restore orignal power role
connect = self.pr_swap()
if connect == False:
logging.warn('DRP on both devices, 2nd power swap failed')
return connect
# Restore default dualrole mode
self.drp_set('on')
# Allow enough time for protocol state machine
time.sleep(self.utils.CONNECT_TIME)
# Check if connected
connect = self.is_connected()
logging.info('Disconnect = %r, Connect = %r', disconnect, connect)
return bool(disconnect and connect)
def drp_set(self, mode):
"""Sets dualrole mode
@param mode: desired dual role setting (on, off, snk, src)
@returns True is set was successful, False otherwise
"""
# Set desired dualrole mode
self.utils.set_pd_dualrole(self.port, mode)
# Get current setting
current = self.utils.get_pd_dualrole(self.port)
# Verify that setting is correct
return bool(mode == current)
def drp_get(self):
"""Gets dualrole mode
@returns one of the modes (on, off, snk, src)
"""
return self.utils.get_pd_dualrole(self.port)
def try_src(self, enable):
"""Enables/Disables Try.SRC PD protocol setting
@param enable: True to enable, False to disable
@returns True is setting was successful, False if feature not
supported by the device, or not set as desired.
"""
# Create Try.SRC pd command
cmd = 'pd trysrc %d' % int(enable)
# TCPMv1 indicates Try.SRC is on by returning 'on'
# TCPMv2 indicates Try.SRC is on by returning 'Forced ON'
on_vals = ('on', 'Forced ON')
# TCPMv1 indicates Try.SRC is off by returning 'off'
# TCPMv2 indicates Try.SRC is off by returning 'Forced OFF'
off_vals = ('off', 'Forced OFF')
# Try.SRC on/off is output, if supported feature
regex = ['Try\.SRC\s(%s)|(Parameter)' % ('|'.join(on_vals + off_vals))]
m = self.utils.send_pd_command_get_output(cmd, regex)
# Determine if Try.SRC feature is supported
if 'Try.SRC' not in m[0][0]:
logging.warn('Try.SRC not supported on this PD device')
return False
# TrySRC is supported on this PD device, verify setting.
trysrc_val = m[0][1]
logging.info('Try.SRC mode = %s', trysrc_val)
if enable:
vals = on_vals
else:
vals = off_vals
return trysrc_val in vals
def soft_reset(self):
"""Initates a PD soft reset sequence
To verify that a soft reset sequence was initiated, the
reply message is checked to verify that the reset command
was acknowledged by its port pair. The connect state should
be same as it was prior to issuing the reset command.
@returns True if the port pair acknowledges the the reset message
and if following the command, the device returns to the same
connected state. False otherwise.
"""
RESET_DELAY = 0.5
cmd = 'pd %d soft' % self.port
state_before = self.utils.get_pd_state(self.port)
reply = self.utils.send_pd_command_get_reply_msg(cmd)
if reply != self.utils.PD_CONTROL_MSG_DICT['Accept']:
return False
time.sleep(RESET_DELAY)
state_after = self.utils.get_pd_state(self.port)
return state_before == state_after
def hard_reset(self):
"""Initates a PD hard reset sequence
To verify that a hard reset sequence was initiated, the
console ouput is scanned for HARD RST TX. In addition, the connect
state should be same as it was prior to issuing the reset command.
@returns True if the port pair acknowledges that hard reset was
initiated and if following the command, the device returns to the same
connected state. False otherwise.
"""
RESET_DELAY = 1.0
cmd = 'pd %d hard' % self.port
state_before = self.utils.get_pd_state(self.port)
self.utils.enable_pd_console_debug()
try:
tcpmv1_pattern = '.*(HARD\sRST\sTX)'
tcpmv2_pattern = '.*(PE_SNK_Hard_Reset)|.*(PE_SRC_Hard_Reset)'
pattern = '|'.join((tcpmv1_pattern, tcpmv2_pattern))
self.utils.send_pd_command_get_output(cmd, [pattern])
except error.TestFail:
logging.warn('HARD RST TX not found')
return False
finally:
self.utils.disable_pd_console_debug()
time.sleep(RESET_DELAY)
state_after = self.utils.get_pd_state(self.port)
return state_before == state_after
def pr_swap(self):
"""Attempts a power role swap
In order to attempt a power role swap the device must be
connected and support dualrole mode. Once these two criteria
are checked a power role command is issued. Following a delay
to allow for a reconnection the new power role is checked
against the power role prior to issuing the command.
@returns True if the device has swapped power roles, False otherwise.
"""
# Get starting state
if not self.is_drp():
logging.warn('Dualrole Mode not enabled!')
return False
if self.is_connected() == False:
logging.warn('PD contract not established!')
return False
current_pr = self.utils.get_pd_state(self.port)
swap_cmd = 'pd %d swap power' % self.port
self.utils.send_pd_command(swap_cmd)
time.sleep(self.utils.CONNECT_TIME)
new_pr = self.utils.get_pd_state(self.port)
logging.info('Power swap: %s -> %s', current_pr, new_pr)
if self.is_connected() == False:
logging.warn('Device not connected following PR swap attempt.')
return False
return current_pr != new_pr
class PDTesterDevice(PDConsoleDevice):
"""Class for PDTester devices
This class contains methods for PD funtions which are unique to the
PDTester board, e.g. Plankton or Servo v4. It inherits all the methods
for PD console devices.
"""
def __init__(self, console, port, utils):
"""Initialization method
@param console: UART console for this device
@param port: USB PD port number
"""
# Instantiate the PD console object
super(PDTesterDevice, self).__init__(console, port, utils)
# Indicate this is PDTester device
self.is_pdtester = True
def _toggle_pdtester_drp(self):
"""Issue 'usbc_action drp' PDTester command
@returns value of drp_enable in PDTester FW
"""
drp_cmd = 'usbc_action drp'
drp_re = ['DRP\s=\s(\d)']
# Send DRP toggle command to PDTester and get value of 'drp_enable'
m = self.utils.send_pd_command_get_output(drp_cmd, drp_re)
return int(m[0][1])
def _enable_pdtester_drp(self):
"""Enable DRP mode on PDTester
DRP mode can only be toggled and is not able to be explicitly
enabled/disabled via the console. Therefore, this method will
toggle DRP mode until the console reply indicates that this
mode is enabled. The toggle happens a maximum of two times
in case this is called when it's already enabled.
@returns True when DRP mode is enabled, False if not successful
"""
for attempt in range(2):
if self._toggle_pdtester_drp() == True:
logging.info('PDTester DRP mode enabled')
return True
logging.error('PDTester DRP mode set failure')
return False
def _verify_state_sequence(self, states_list, console_log):
"""Compare PD state transitions to expected values
@param states_list: list of expected PD state transitions
@param console_log: console output which contains state names
@returns True if the sequence matches, False otherwise
"""
# For each state in the expected state transiton table, build
# the regexp and search for it in the state transition log.
for state in states_list:
state_regx = r'C{0}\s+[\w]+:?\s({1})'.format(self.port,
state)
if re.search(state_regx, console_log) is None:
return False
return True
def cc_disconnect_connect(self, disc_time_sec):
"""Disconnect/reconnect using PDTester
PDTester supports a feature which simulates a USB Type C disconnect
and reconnect.
@param disc_time_sec: Time in seconds for disconnect period.
"""
DISC_DELAY = 100
disc_cmd = 'fakedisconnect %d %d' % (DISC_DELAY,
disc_time_sec * 1000)
self.utils.send_pd_command(disc_cmd)
def get_connected_state_after_cc_reconnect(self, disc_time_sec):
"""Get the connected state after disconnect/reconnect using PDTester
PDTester supports a feature which simulates a USB Type C disconnect
and reconnect. It returns the first connected state (either source or
sink) after reconnect.
@param disc_time_sec: Time in seconds for disconnect period.
@returns: The connected PD state.
"""
DISC_DELAY = 100
disc_cmd = 'fakedisconnect %d %d' % (DISC_DELAY, disc_time_sec * 1000)
src_connected_tuple = self.utils.get_src_connect_states()
snk_connected_tuple = self.utils.get_snk_connect_states()
connected_exp = '|'.join(src_connected_tuple + snk_connected_tuple)
reply_exp = ['(.*)(C%d)\s+[\w]+:?\s(%s)' % (self.port, connected_exp)]
m = self.utils.send_pd_command_get_output(disc_cmd, reply_exp)
return m[0][3]
def drp_disconnect_connect(self, disc_time_sec):
"""Disconnect/reconnect using PDTester
Utilize PDTester disconnect/connect utility and verify
that both disconnect and reconnect actions were successful.
@param disc_time_sec: Time in seconds for disconnect period.
@returns True if device disconnects, then returns to a connected
state. False if either step fails.
"""
self.cc_disconnect_connect(disc_time_sec)
time.sleep(disc_time_sec / 2)
disconnect = self.is_disconnected()
time.sleep(disc_time_sec / 2 + self.utils.CONNECT_TIME)
connect = self.is_connected()
return disconnect and connect
def drp_set(self, mode):
"""Sets dualrole mode
@param mode: desired dual role setting (on, off, snk, src)
@returns True if dualrole mode matches the requested value or
is successfully set to that value. False, otherwise.
"""
# Get current value of dualrole
drp = self.utils.get_pd_dualrole(self.port)
if drp == mode:
return True
if mode == 'on':
# Setting dpr_enable on PDTester will set dualrole mode to on
return self._enable_pdtester_drp()
else:
# If desired setting is other than 'on', need to ensure that
# drp mode on PDTester is disabled.
if drp == 'on':
# This will turn off drp_enable flag and set dualmode to 'off'
return self._toggle_pdtester_drp()
# With drp_enable flag off, can set to desired setting
return self.utils.set_pd_dualrole(self.port, mode)
def _reset(self, cmd, states_list):
"""Initates a PD reset sequence
PDTester device has state names available on the console. When
a soft reset is issued the console log is extracted and then
compared against the expected state transisitons.
@param cmd: reset type (soft or hard)
@param states_list: list of expected PD state transitions
@returns True if state transitions match, False otherwise
"""
# Want to grab all output until either SRC_READY or SNK_READY
reply_exp = ['(.*)(C%d)\s+[\w]+:?\s([\w]+_READY)' % self.port]
m = self.utils.send_pd_command_get_output(cmd, reply_exp)
return self._verify_state_sequence(states_list, m[0][0])
def soft_reset(self):
"""Initates a PD soft reset sequence
@returns True if state transitions match, False otherwise
"""
snk_reset_states = [
'SOFT_RESET',
'SNK_DISCOVERY',
'SNK_REQUESTED',
'SNK_TRANSITION',
'SNK_READY'
]
src_reset_states = [
'SOFT_RESET',
'SRC_DISCOVERY',
'SRC_NEGOCIATE',
'SRC_ACCEPTED',
'SRC_POWERED',
'SRC_TRANSITION',
'SRC_READY'
]
if self.is_src():
states_list = src_reset_states
elif self.is_snk():
states_list = snk_reset_states
else:
raise error.TestFail('Port Pair not in a connected state')
cmd = 'pd %d soft' % self.port
return self._reset(cmd, states_list)
def hard_reset(self):
"""Initates a PD hard reset sequence
@returns True if state transitions match, False otherwise
"""
snk_reset_states = [
'HARD_RESET_SEND',
'HARD_RESET_EXECUTE',
'SNK_HARD_RESET_RECOVER',
'SNK_DISCOVERY',
'SNK_REQUESTED',
'SNK_TRANSITION',
'SNK_READY'
]
src_reset_states = [
'HARD_RESET_SEND',
'HARD_RESET_EXECUTE',
'SRC_HARD_RESET_RECOVER',
'SRC_DISCOVERY',
'SRC_NEGOCIATE',
'SRC_ACCEPTED',
'SRC_POWERED',
'SRC_TRANSITION',
'SRC_READY'
]
if self.is_src():
states_list = src_reset_states
elif self.is_snk():
states_list = snk_reset_states
else:
raise error.TestFail('Port Pair not in a connected state')
cmd = 'pd %d hard' % self.port
return self._reset(cmd, states_list)
class PDPortPartner(object):
"""Methods used to instantiate PD device objects
This class is initalized with a list of servo consoles. It
contains methods to determine if USB PD devices are accessible
via the consoles and attempts to determine USB PD port partners.
A PD device is USB PD port specific, a single console may access
multiple PD devices.
"""
def __init__(self, consoles):
"""Initialization method
@param consoles: list of servo consoles
"""
self.consoles = consoles
def __repr__(self):
"""String representation of the object"""
return "<%s %r>" % (self.__class__.__name__, self.consoles)
def _send_pd_state(self, port, console):
"""Tests if PD device exists on a given port number
@param port: USB PD port number to try
@param console: servo UART console
@returns True if 'pd <port> state' command gives a valid
response, False otherwise
"""
cmd = 'pd %d state' % port
regex = r'(Port C\d)|(Parameter)'
m = console.send_command_get_output(cmd, [regex])
# If PD port exists, then output will be Port C0 or C1
regex = r'Port C{0}'.format(port)
if re.search(regex, m[0][0]):
return True
return False
def _find_num_pd_ports(self, console):
"""Determine number of PD ports for a given console
@param console: uart console accssed via servo
@returns: number of PD ports accessible via console
"""
MAX_PORTS = 2
num_ports = 0
for port in range(MAX_PORTS):
if self._send_pd_state(port, console):
num_ports += 1
return num_ports
def _is_pd_console(self, console):
"""Check if pd option exists in console
@param console: uart console accssed via servo
@returns: True if 'pd' is found, False otherwise
"""
try:
m = console.send_command_get_output('help', [r'(pd)\s+'])
return True
except error.TestFail:
return False
def _is_pdtester_console(self, console):
"""Check for PDTester console
This method looks for a console command option 'usbc_action' which
is unique to PDTester PD devices.
@param console: uart console accssed via servo
@returns True if usbc_action command is present, False otherwise
"""
try:
m = console.send_command_get_output('help', [r'(usbc_action)'])
return True
except error.TestFail:
return False
def _check_port_pair(self, port1, port2):
"""Check if two PD devices could be connected
If two USB PD devices are connected, then they should be in
either the SRC_READY or SNK_READY states and have opposite
power roles. In addition, they must be on different servo
consoles.
@param: list of two possible PD port parters
@returns True if not the same console and both PD devices
are a plausible pair based only on their PD states.
"""
# Don't test if on the same servo console
if port1.console == port2.console:
logging.info("PD Devices are on same platform -> can't be a pair")
return False
state1 = port1.get_pd_state()
port1_is_snk = port1.is_snk(state1)
port1_is_src = port1.is_src(state1)
state2 = port2.get_pd_state()
port2_is_snk = port2.is_snk(state2)
port2_is_src = port2.is_src(state2)
# Must be SRC <--> SNK or SNK <--> SRC
if (port1_is_src and port2_is_snk) or (port1_is_snk and port2_is_src):
logging.debug("SRC+SNK pair: %s (%s) <--> (%s) %s",
port1, state1, state2, port2)
return True
else:
logging.debug("Not a SRC+SNK pair: %s (%s) <--> (%s) %s",
port1, state1, state2, port2)
return False
def _verify_pdtester_connection(self, tester_port, dut_port):
"""Verify DUT to PDTester PD connection
This method checks for a PDTester PD connection for the
given port by first verifying if a PD connection is present.
If found, then it uses a PDTester feature to force a PD disconnect.
If the port is no longer in the connected state, and following
a delay, is found to be back in the connected state, then
a DUT pd to PDTester connection is verified.
@param dev_pair: list of two PD devices
@returns True if DUT to PDTester pd connection is verified
"""
DISC_CHECK_TIME = 10
DISC_WAIT_TIME = 20
CONNECT_TIME = 4
logging.info("Check: %s <--> %s", tester_port, dut_port)
if not self._check_port_pair(tester_port, dut_port):
return False
# Force PD disconnect
logging.debug('Disconnecting to check if devices are partners')
tester_port.cc_disconnect_connect(DISC_WAIT_TIME)
time.sleep(DISC_CHECK_TIME)
# Verify that both devices are now disconnected
tester_state = tester_port.get_pd_state()
dut_state = dut_port.get_pd_state()
logging.debug("Recheck: %s (%s) <--> (%s) %s",
tester_port, tester_state, dut_state, dut_port)
if not (tester_port.is_disconnected(tester_state) and
dut_port.is_disconnected(dut_state)):
logging.info("Ports did not disconnect at the same time, so"
" they aren't considered a pair.")
# Delay to allow non-pair devices to reconnect
time.sleep(DISC_WAIT_TIME - DISC_CHECK_TIME + CONNECT_TIME)
return False
logging.debug('Pair disconnected. Waiting for reconnect...')
# Allow enough time for reconnection
time.sleep(DISC_WAIT_TIME - DISC_CHECK_TIME + CONNECT_TIME)
if self._check_port_pair(tester_port, dut_port):
# Have verified a pd disconnect/reconnect sequence
logging.info('PDTester <--> DUT pair found')
return True
logging.info("Ports did not reconnect at the same time, so"
" they aren't considered a pair.")
return False
def identify_pd_devices(self):
"""Instantiate PD devices present in test setup
@return: list of 2 PD devices if a DUT <-> PDTester found.
If not found, then returns an empty list.
"""
tester_devports = []
dut_devports = []
# For each possible uart console, check to see if a PD console
# is present and determine the number of PD ports.
for console in self.consoles:
if self._is_pd_console(console):
is_tester = self._is_pdtester_console(console)
num_ports = self._find_num_pd_ports(console)
# For each PD port that can be accessed via the console,
# instantiate either PDConsole or PDTester device.
for port in range(num_ports):
if is_tester:
logging.info('PDTesterDevice on %s port %d',
console.name, port)
tester_utils = pd_console.create_pd_console_utils(
console)
tester_devports.append(PDTesterDevice(console,
port, tester_utils))
else:
logging.info('PDConsoleDevice on %s port %d',
console.name, port)
dut_utils = pd_console.create_pd_console_utils(console)
dut_devports.append(PDConsoleDevice(console,
port, dut_utils))
if not tester_devports:
logging.error('The specified consoles did not include any'
' PD testers: %s', self.consoles)
if not dut_devports:
logging.error('The specified consoles did not contain any'
' DUTs: %s', self.consoles)
# Determine PD port partners in the list of PD devices. Note that
# there can be PD devices which are not accessible via a uart console,
# but are connected to a PD port which is accessible.
for tester in reversed(tester_devports):
for dut in dut_devports:
if tester.console == dut.console:
# PD Devices are on same servo console -> can't be a pair
continue
if self._verify_pdtester_connection(tester, dut):
dut_devports.remove(dut)
return [tester, dut]
return []