blob: cc84f5c255ed7365e528072f1b45d2fa8cb9592b [file] [log] [blame]
# Copyright 2020 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.
"""Servo-related functionality."""
import logging
from typing import List, NamedTuple
from chromite.lib import cros_build_lib
SERVO_C2D2 = 'c2d2'
SERVO_CCD_CR50 = 'ccd_cr50'
SERVO_CCD_TI50 = 'ccd_ti50'
SERVO_MICRO = 'servo_micro'
SERVO_V2 = 'servo_v2'
SERVO_V4_CCD = 'servo_v4_with_ccd'
SERVO_V4_CCD_CR50 = 'servo_v4_with_ccd_cr50'
SERVO_V4_CCD_TI50 = 'servo_v4_with_ccd_ti50'
SERVO_V4_MICRO = 'servo_v4_with_servo_micro'
VALID_SERVOS = (
SERVO_C2D2,
SERVO_CCD_CR50,
SERVO_CCD_TI50,
SERVO_MICRO,
SERVO_V2,
SERVO_V4_CCD,
SERVO_V4_CCD_CR50,
SERVO_V4_CCD_TI50,
SERVO_V4_MICRO,
)
CCD_SERVOS = (SERVO_CCD_CR50, SERVO_CCD_TI50, SERVO_V4_CCD, SERVO_V4_CCD_CR50,
SERVO_V4_CCD_TI50)
MICRO_SERVOS = (SERVO_MICRO, SERVO_V4_MICRO)
V2_SERVOS = (SERVO_V2,)
V4_SERVOS = (SERVO_V4_CCD, SERVO_V4_CCD_CR50, SERVO_V4_MICRO, SERVO_V4_CCD_TI50)
_SERIAL_NUMBER_OPTION = 'serialname'
_SERIAL_NUMBER_OPTION_OVERRIDE = {
SERVO_V4_CCD: 'ccd_serialname',
SERVO_V4_CCD_CR50: 'ccd_serialname',
SERVO_V4_CCD_TI50: 'ccd_serialname',
SERVO_V4_MICRO: 'servo_micro_serialname',
}
class FirmwareConfig(NamedTuple):
"""Stores dut controls for specific servos.
Attributes:
dut_control_on: 2d array formatted like [["cmd1", "arg1", "arg2"],
["cmd2", "arg3", "arg4"]]
with commands that need to be ran before flashing,
where cmd1 will be run before cmd2.
dut_control_off: 2d array formatted like [["cmd1", "arg1", "arg2"],
["cmd2", "arg3", "arg4"]]
with commands that need to be ran after flashing,
where cmd1 will be run before cmd2.
programmer: programmer argument (-p) for flashrom and futility.
"""
dut_control_on: List[List[str]]
dut_control_off: List[List[str]]
programmer: str
class Error(Exception):
"""Base error class for the module."""
class InvalidServoVersionError(Error):
"""Invalid servo version error."""
class UnsupportedServoVersionError(Error):
"""Unsupported servo version error (e.g. some servos do not support CCD)."""
class DutConnectionError(Error):
"""Error when fetching data from a dut."""
class DutControl(object):
"""Wrapper for dut_control calls."""
def __init__(self, port):
self._base_cmd = ['dut-control']
if port:
self._base_cmd.append('--port=%s' % port)
def get_value(self, arg):
"""Get the value of |arg| from dut_control."""
try:
result = cros_build_lib.run(
self._base_cmd + [arg], stdout=True, encoding='utf-8')
except cros_build_lib.CalledProcessError as e:
logging.debug('dut-control error: %s', str(e))
raise DutConnectionError(
'Could not establish servo connection. Verify servod is running in '
'the background, and the servo is properly connected.')
# Return value from the "key:value" output.
return result.stdout.partition(':')[2].strip()
def run(self, cmd_fragment, verbose=False, dryrun=False):
"""Run a dut_control command.
Args:
cmd_fragment (list[str]): The dut_control command to run.
verbose (bool): Whether to print the command before it's run.
dryrun (bool): Whether to actually execute the command or just print it.
"""
cros_build_lib.run(
self._base_cmd + cmd_fragment, print_cmd=verbose, dryrun=dryrun)
def run_all(self, cmd_fragments, verbose=False, dryrun=False):
"""Run multiple dut_control commands in the order given.
Args:
cmd_fragments (list[list[str]]): The dut_control commands to run.
verbose (bool): Whether to print the commands as they are run.
dryrun (bool): Whether to actually execute the command or just print it.
"""
for cmd in cmd_fragments:
self.run(cmd, verbose=verbose, dryrun=dryrun)
class Servo(object):
"""Data class for servos."""
def __init__(self, version, serial):
assert version in VALID_SERVOS
self.version = version
self.serial = serial
@property
def is_ccd(self):
return self.version in CCD_SERVOS
@property
def is_c2d2(self):
return self.version == SERVO_C2D2
@property
def is_micro(self):
return self.version in MICRO_SERVOS
@property
def is_v2(self):
return self.version in V2_SERVOS
@property
def is_v4(self):
return self.version in V4_SERVOS
def get(dut_ctl: DutControl) -> Servo:
"""Get the Servo instance the given dut_control command is using.
Args:
dut_ctl: The dut_control command wrapper instance.
"""
version = dut_ctl.get_value('servo_type')
if version not in VALID_SERVOS:
raise InvalidServoVersionError('Unrecognized servo version: %s' % version)
option = _SERIAL_NUMBER_OPTION_OVERRIDE.get(version, _SERIAL_NUMBER_OPTION)
serial = dut_ctl.get_value(option)
return Servo(version, serial)