328 lines
13 KiB
Python
328 lines
13 KiB
Python
# Copyright 2017 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 time
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.server.cros.faft.cr50_test import Cr50Test
|
|
|
|
|
|
class firmware_Cr50CCDServoCap(Cr50Test):
|
|
"""Verify Cr50 CCD output enable/disable when servo is connected.
|
|
|
|
Verify Cr50 will enable/disable the CCD servo output capabilities when servo
|
|
is attached/detached.
|
|
"""
|
|
version = 1
|
|
|
|
# Time used to wait for Cr50 to detect the servo state. Cr50 updates the ccd
|
|
# state once a second. Wait 2 seconds to be conservative.
|
|
SLEEP = 2
|
|
|
|
# A list of the actions we should verify
|
|
TEST_CASES = [
|
|
'fake_servo on, cr50_run reboot',
|
|
'fake_servo on, rdd attach, cr50_run reboot',
|
|
|
|
'rdd attach, fake_servo on, cr50_run reboot, fake_servo off',
|
|
'rdd attach, fake_servo on, rdd detach',
|
|
'rdd attach, fake_servo off, rdd detach',
|
|
]
|
|
|
|
ON = 0
|
|
OFF = 1
|
|
UNDETECTABLE = 2
|
|
STATUS_MAP = [ 'on', 'off', 'unknown' ]
|
|
# Create maps for the different ccd states. Mapping each state to 'on',
|
|
# 'off', and 'unknown'. These lists map to the acceptable [ on values, off
|
|
# values, and unknown state values]
|
|
ON_MAP = [ 'on', 'off', '' ]
|
|
ENABLED_MAP = [ 'enabled', 'disabled', '' ]
|
|
CONNECTED_MAP = [ 'connected', 'disconnected', 'undetectable' ]
|
|
VALID_STATES = {
|
|
'AP' : ON_MAP,
|
|
'EC' : ON_MAP,
|
|
'AP UART' : ON_MAP,
|
|
'Rdd' : CONNECTED_MAP,
|
|
'Servo' : CONNECTED_MAP,
|
|
'CCD EXT' : ENABLED_MAP,
|
|
}
|
|
# RESULT_ORDER is a list of the CCD state strings. The order corresponds
|
|
# with the order of the key states in EXPECTED_RESULTS.
|
|
RESULT_ORDER = ['Rdd', 'CCD EXT', 'Servo']
|
|
# A dictionary containing an order of steps to verify and the expected ccd
|
|
# states as the value.
|
|
#
|
|
# The keys are a list of strings with the order of steps to run.
|
|
#
|
|
# The values are the expected state of [rdd, ccd ext, servo]. The ccdstate
|
|
# strings are in RESULT_ORDER. The order of the EXPECTED_RESULTS key states
|
|
# must match the order in RESULT_ORDER.
|
|
#
|
|
# There are three valid states: UNDETECTABLE, ON, or OFF. Undetectable only
|
|
# describes the servo state when EC uart is enabled. If the ec uart is
|
|
# enabled, cr50 cannot detect servo and the state becomes undetectable. All
|
|
# other ccdstates can only be off or on. Cr50 has a lot of different words
|
|
# for off off and on. So VALID_STATES can be used to convert off, on, and
|
|
# undetectable to the actual state strings.
|
|
EXPECTED_RESULTS = {
|
|
# The state all tests will start with. Servo and the ccd cable are
|
|
# disconnected.
|
|
'reset_ccd state' : [OFF, OFF, OFF],
|
|
|
|
# If rdd is attached all ccd functionality will be enabled, and servo
|
|
# will be undetectable.
|
|
'rdd attach' : [ON, ON, UNDETECTABLE],
|
|
|
|
# Cr50 cannot detect servo if ccd has been enabled first
|
|
'rdd attach, fake_servo off' : [ON, ON, UNDETECTABLE],
|
|
'rdd attach, fake_servo off, rdd detach' : [OFF, OFF, OFF],
|
|
'rdd attach, fake_servo on' : [ON, ON, UNDETECTABLE],
|
|
'rdd attach, fake_servo on, rdd detach' : [OFF, OFF, ON],
|
|
# Cr50 can detect servo after a reboot even if rdd was attached before
|
|
# servo.
|
|
'rdd attach, fake_servo on, cr50_run reboot' : [ON, ON, ON],
|
|
# Once servo is detached, Cr50 will immediately reenable the EC uart.
|
|
'rdd attach, fake_servo on, cr50_run reboot, fake_servo off' :
|
|
[ON, ON, UNDETECTABLE],
|
|
|
|
# Cr50 can detect a servo attach
|
|
'fake_servo on' : [OFF, OFF, ON],
|
|
# Cr50 knows servo is attached when ccd is enabled, so it wont enable
|
|
# uart.
|
|
'fake_servo on, rdd attach' : [ON, ON, ON],
|
|
'fake_servo on, rdd attach, cr50_run reboot' : [ON, ON, ON],
|
|
'fake_servo on, cr50_run reboot' : [OFF, OFF, ON],
|
|
}
|
|
|
|
|
|
def initialize(self, host, cmdline_args, full_args):
|
|
super(firmware_Cr50CCDServoCap, self).initialize(host, cmdline_args,
|
|
full_args)
|
|
if not hasattr(self, 'cr50'):
|
|
raise error.TestNAError('Test can only be run on devices with '
|
|
'access to the Cr50 console')
|
|
|
|
if (self.servo.get_servo_version(active=True) !=
|
|
'servo_v4_with_servo_micro'):
|
|
raise error.TestNAError('Must use servo v4 with servo micro')
|
|
|
|
if not self.cr50.servo_dts_mode_is_valid():
|
|
raise error.TestNAError('Need working servo v4 DTS control')
|
|
|
|
if not self.cr50.check_servo_monitor():
|
|
raise error.TestNAError('Cannot run on device that does not '
|
|
'support servo dectection with '
|
|
'ec_uart_en:off/on')
|
|
# Make sure cr50 is open with testlab enabled.
|
|
self.fast_ccd_open(enable_testlab=True)
|
|
if not self.cr50.testlab_is_on():
|
|
raise error.TestNAError('Cr50 testlab mode needs to be enabled')
|
|
logging.info('Cr50 is %s', self.servo.get('cr50_ccd_level'))
|
|
self.cr50.set_cap('UartGscTxECRx', 'Always')
|
|
self.ec_efs_support = (
|
|
self.cr50.uses_board_property('BOARD_EC_CR50_COMM_SUPPORT'))
|
|
# Check EC uart if servo has ccd controls and the board has an EC.
|
|
self.check_ec_uart = (self.servo.has_control('ccd_cr50.ec_board') and
|
|
self.check_ec_capability(suppress_warning=True))
|
|
|
|
|
|
def cleanup(self):
|
|
"""Reenable the EC uart"""
|
|
try:
|
|
self.fake_servo('on')
|
|
self.rdd('detach')
|
|
self.rdd('attach')
|
|
finally:
|
|
super(firmware_Cr50CCDServoCap, self).cleanup()
|
|
|
|
|
|
def state_matches(self, state_dict, state_name, expected_value):
|
|
"""Check the current state. Make sure it matches expected value"""
|
|
valid_state = self.VALID_STATES[state_name][expected_value]
|
|
# I2C isn't a reliable flag, because the hardware often doesn't support
|
|
# it. Remove any I2C flags from the ccdstate output.
|
|
current_state = state_dict[state_name].replace(' I2C', '')
|
|
if isinstance(valid_state, list):
|
|
return current_state in valid_state
|
|
return current_state == valid_state
|
|
|
|
|
|
def state_is_on(self, ccdstate, state_name):
|
|
"""Returns true if the state is on"""
|
|
return self.state_matches(ccdstate, state_name, self.ON)
|
|
|
|
|
|
def ccd_ec_uart_works(self):
|
|
"""Returns True if the CCD ec uart works."""
|
|
try:
|
|
self.servo.get('ccd_cr50.ec_board')
|
|
logging.info('ccd ec console is responsive')
|
|
return True
|
|
except:
|
|
logging.info('ccd ec console is unresponsive')
|
|
return False
|
|
|
|
|
|
def check_state_flags(self, ccdstate):
|
|
"""Check the state flags against the reset of the device state
|
|
|
|
If there is any mismatch between the device state and state flags,
|
|
return a list of errors.
|
|
"""
|
|
flags = ccdstate['State flags']
|
|
ap_uart_enabled = 'UARTAP' in flags
|
|
ec_uart_enabled = 'UARTEC' in flags
|
|
ap_uart_tx_enabled = 'UARTAP+TX' in flags
|
|
ec_uart_tx_enabled = 'UARTEC+TX' in flags
|
|
ec_usb_tx_enabled = 'USBEC+TX' in flags
|
|
|
|
ccd_ec_uart_enabled = ec_uart_tx_enabled and ec_usb_tx_enabled
|
|
ccd_enabled = ap_uart_enabled or ec_usb_tx_enabled
|
|
output_enabled = ap_uart_tx_enabled
|
|
if not self.ec_efs_support:
|
|
output_enabled |= ec_uart_tx_enabled
|
|
ccd_enabled |= ec_uart_enabled
|
|
|
|
ccd_ext_is_enabled = ccdstate['CCD EXT'] == 'enabled'
|
|
mismatch = []
|
|
logging.info('checking state flags')
|
|
if ccd_enabled and not ccd_ext_is_enabled:
|
|
mismatch.append('CCD functionality enabled without CCD EXT')
|
|
if ccd_ext_is_enabled:
|
|
if output_enabled and self.state_is_on(ccdstate, 'Servo'):
|
|
mismatch.append('CCD output is enabled with servo attached')
|
|
if ap_uart_enabled != self.state_is_on(ccdstate, 'AP UART'):
|
|
mismatch.append('AP UART enabled without AP UART on')
|
|
if ec_uart_enabled != self.state_is_on(ccdstate, 'EC'):
|
|
mismatch.append('EC UART enabled without EC on')
|
|
if self.check_ec_uart:
|
|
ccd_ec_uart_works = self.ccd_ec_uart_works()
|
|
if (self.servo.get('ec_uart_en') == 'off'
|
|
and ccd_ec_uart_enabled and not ccd_ec_uart_works):
|
|
mismatch.append('ccd ec uart does not work with EC+TX '
|
|
'enabled.')
|
|
if not ccd_ec_uart_enabled and ccd_ec_uart_works:
|
|
mismatch.append('ccd ec uart works with EC+TX disabled.')
|
|
return mismatch
|
|
|
|
|
|
|
|
def verify_ccdstate(self, run):
|
|
"""Verify the current state matches the expected result from the run.
|
|
|
|
Args:
|
|
run: the string representing the actions that have been run.
|
|
|
|
Raises:
|
|
TestError if any of the states are not correct
|
|
"""
|
|
if run not in self.EXPECTED_RESULTS:
|
|
raise error.TestError('Add results for %s to EXPECTED_RESULTS' % run)
|
|
expected_states = self.EXPECTED_RESULTS[run]
|
|
|
|
# Wait a short time for the ccd state to settle
|
|
time.sleep(self.SLEEP)
|
|
|
|
ccdstate = self.cr50.get_ccdstate()
|
|
# Check the state flags. Make sure they're in line with the rest of
|
|
# ccdstate
|
|
mismatch = self.check_state_flags(ccdstate)
|
|
for i, expected_state in enumerate(expected_states):
|
|
name = self.RESULT_ORDER[i]
|
|
if expected_state == None:
|
|
logging.info('No expected %s state skipping check', name)
|
|
continue
|
|
# Check that the current state matches the expected state
|
|
if not self.state_matches(ccdstate, name, expected_state):
|
|
mismatch.append('%s is %r not %r' % (name, ccdstate[name],
|
|
self.STATUS_MAP[expected_state]))
|
|
if mismatch:
|
|
logging.info(ccdstate)
|
|
raise error.TestFail('Unexpected states after %s: %s' % (run,
|
|
mismatch))
|
|
|
|
|
|
def cr50_run(self, action):
|
|
"""Reboot cr50
|
|
|
|
@param action: string 'reboot'
|
|
"""
|
|
if action == 'reboot':
|
|
self.cr50.reboot()
|
|
self.cr50.send_command('ccd testlab open')
|
|
time.sleep(self.SLEEP)
|
|
|
|
|
|
def reset_ccd(self, state=None):
|
|
"""detach the ccd cable and disconnect servo.
|
|
|
|
State is ignored. It just exists to be consistent with the other action
|
|
functions.
|
|
|
|
@param state: a var that is ignored
|
|
"""
|
|
self.rdd('detach')
|
|
self.fake_servo('off')
|
|
|
|
|
|
def rdd(self, state):
|
|
"""Attach or detach the ccd cable.
|
|
|
|
@param state: string 'attach' or 'detach'
|
|
"""
|
|
self.servo.set_dts_mode('on' if state == 'attach' else 'off')
|
|
time.sleep(self.SLEEP)
|
|
|
|
|
|
def fake_servo(self, state):
|
|
"""Mimic servo on/off
|
|
|
|
Cr50 monitors the servo EC uart tx signal to detect servo. If the signal
|
|
is pulled up, then Cr50 will think servo is connnected. Enable the ec
|
|
uart to enable the pullup. Disable the it to remove the pullup.
|
|
|
|
It takes some time for Cr50 to detect the servo state so wait 2 seconds
|
|
before returning.
|
|
"""
|
|
self.servo.set('ec_uart_en', state)
|
|
|
|
# Cr50 needs time to detect the servo state
|
|
time.sleep(self.SLEEP)
|
|
|
|
|
|
def run_steps(self, steps):
|
|
"""Do each step in steps and then verify the uart state.
|
|
|
|
The uart state is order dependent, so we need to know all of the
|
|
previous steps to verify the state. This will do all of the steps in
|
|
the string and verify the Cr50 CCD uart state after each step.
|
|
|
|
@param steps: a comma separated string with the steps to run
|
|
"""
|
|
# The order of steps is separated by ', '. Remove the last step and
|
|
# run all of the steps before it.
|
|
separated_steps = steps.rsplit(', ', 1)
|
|
if len(separated_steps) > 1:
|
|
self.run_steps(separated_steps[0])
|
|
|
|
step = separated_steps[-1]
|
|
# The func and state are separated by ' '
|
|
func, state = step.split(' ')
|
|
logging.info('running %s', step)
|
|
getattr(self, func)(state)
|
|
|
|
# Verify the ccd state is correct
|
|
self.verify_ccdstate(steps)
|
|
|
|
|
|
def run_once(self):
|
|
"""Run through TEST_CASES and verify that Cr50 enables/disables uart"""
|
|
for steps in self.TEST_CASES:
|
|
self.run_steps('reset_ccd state')
|
|
logging.info('TESTING: %s', steps)
|
|
self.run_steps(steps)
|
|
logging.info('VERIFIED: %s', steps)
|