16f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch# Copyright 2015 The Chromium OS Authors. All rights reserved.
26f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch# Use of this source code is governed by a BSD-style license that can be
36f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch# found in the LICENSE file.
46f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Brochimport logging
513f929ddf163cc7e475ed34e130e875da11c99c4Todd Brochimport re
613f929ddf163cc7e475ed34e130e875da11c99c4Todd Brochimport time
76f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
86f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Brochfrom autotest_lib.client.bin import utils
96f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Brochfrom autotest_lib.client.common_lib import error
106f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
11b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh# en-US key matrix (from "kb membrane pin matrix.pdf")
12b5210efc64510e1b1335b1561f3e23a99cf91053Victor HsiehKEYMATRIX = {'`': (3, 1), '1': (6, 1), '2': (6, 4), '3': (6, 2), '4': (6, 3),
13b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '5': (3, 3), '6': (3, 6), '7': (6, 6), '8': (6, 5), '9': (6, 9),
14b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '0': (6, 8), '-': (3, 8), '=': (0, 8), 'q': (7, 1), 'w': (7, 4),
15b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             'e': (7, 2), 'r': (7, 3), 't': (2, 3), 'y': (2, 6), 'u': (7, 6),
16b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             'i': (7, 5), 'o': (7, 9), 'p': (7, 8), '[': (2, 8), ']': (2, 5),
17b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '\\': (3, 11), 'a': (4, 1), 's': (4, 4), 'd': (4, 2), 'f': (4, 3),
18b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             'g': (1, 3), 'h': (1, 6), 'j': (4, 6), 'k': (4, 5), 'l': (4, 9),
19b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             ';': (4, 8), '\'': (1, 8), 'z': (5, 1), 'x': (5, 4), 'c': (5, 2),
20b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             'v': (5, 3), 'b': (0, 3), 'n': (0, 6), 'm': (5, 6), ',': (5, 5),
21b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '.': (5, 9), '/': (5, 8), ' ': (5, 11), '<right>': (6, 12),
22b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '<alt_r>': (0, 10), '<down>': (6, 11), '<tab>': (2, 1),
23b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '<f10>': (0, 4), '<shift_r>': (7, 7), '<ctrl_r>': (4, 0),
24b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '<esc>': (1, 1), '<backspace>': (1, 11), '<f2>': (3, 2),
25b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '<alt_l>': (6, 10), '<ctrl_l>': (2, 0), '<f1>': (0, 2),
26b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '<search>': (0, 1), '<f3>': (2, 2), '<f4>': (1, 2), '<f5>': (3, 4),
27b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '<f6>': (2, 4), '<f7>': (1, 4), '<f8>': (2, 9), '<f9>': (1, 9),
28b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '<up>': (7, 11), '<shift_l>': (5, 7), '<enter>': (4, 11),
29b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh             '<left>': (7, 12)}
30b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh
316f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
326f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Brochdef has_ectool():
336f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    """Determine if ectool shell command is present.
346f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
356f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    Returns:
366f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        boolean true if avail, false otherwise.
376f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    """
386f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    cmd = 'which ectool'
396f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    return (utils.system(cmd, ignore_status=True) == 0)
406f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
416f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
4213f929ddf163cc7e475ed34e130e875da11c99c4Todd Brochclass EC_Common(object):
4313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    """Class for EC common.
4413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
4513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    This incredibly brief base class is intended to encapsulate common elements
4613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    across various CrOS MCUs (ec proper, USB-PD, Sensor Hub).  At the moment
4713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    that includes only the use of ectool.
4813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    """
4913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
5013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    def __init__(self, target='cros_ec'):
5113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """Constructor.
526f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
5313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @param target: target name of ec to communicate with.
5413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """
556f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        if not has_ectool():
566f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch            ec_info = utils.system_output("mosys ec info",
576f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch                                          ignore_status=True)
586f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch            logging.warning("Ectool absent on this platform ( %s )",
596f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch                         ec_info)
606f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch            raise error.TestNAError("Platform doesn't support ectool")
6113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        self._target = target
626f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
63ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def ec_command(self, cmd, **kwargs):
646f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """Executes ec command and returns results.
656f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
666f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        @param cmd: string of command to execute.
67ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param kwargs: optional params passed to utils.system_output
686f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
696f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        @returns: string of results from ec command.
706f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """
7113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        full_cmd = 'ectool --name=%s %s' % (self._target, cmd)
72ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        result = utils.system_output(full_cmd, **kwargs)
7313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        logging.debug('Command: %s', full_cmd)
7413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        logging.debug('Result: %s', result)
756f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        return result
766f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
7713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
7813f929ddf163cc7e475ed34e130e875da11c99c4Todd Brochclass EC(EC_Common):
7913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    """Class for CrOS embedded controller (EC)."""
8013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    HELLO_RE = "EC says hello"
8113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    GET_FANSPEED_RE = "Current fan RPM: ([0-9]*)"
8213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    SET_FANSPEED_RE = "Fan target RPM set."
8313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    TEMP_SENSOR_RE = "Reading temperature...([0-9]*)"
8413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    TOGGLE_AUTO_FAN_RE = "Automatic fan control is now on"
8513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    # For battery, check we can see a non-zero capacity value.
8613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    BATTERY_RE = "Design capacity:\s+[1-9]\d*\s+mAh"
8713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    LIGHTBAR_RE = "^ 05\s+3f\s+3f$"
8813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
8913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
906f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    def hello(self):
916f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """Test EC hello command.
926f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
936f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        @returns True if success False otherwise.
946f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """
956f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        response = self.ec_command('hello')
966f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        return (re.search(self.HELLO_RE, response) is not None)
976f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
986f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    def auto_fan_ctrl(self):
996f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """Turns auto fan ctrl on.
1006f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1016f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        @returns True if success False otherwise.
1026f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """
1036f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        response = self.ec_command('autofanctrl')
1046f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        logging.info('Turned on auto fan control.')
1056f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        return (re.search(self.TOGGLE_AUTO_FAN_RE, response) is not None)
1066f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1076f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    def get_fanspeed(self):
1086f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """Gets fanspeed.
1096f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1106f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        @raises error.TestError if regexp fails to match.
1116f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1126f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        @returns integer of fan speed RPM.
1136f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """
1146f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        response = self.ec_command('pwmgetfanrpm')
1156f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        match = re.search(self.GET_FANSPEED_RE, response)
1166f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        if not match:
1176f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch            raise error.TestError('Unable to read fan speed')
1186f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1196f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        rpm = int(match.group(1))
1206f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        logging.info('Fan speed: %d', rpm)
1216f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        return rpm
1226f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1236f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    def set_fanspeed(self, rpm):
1246f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """Sets fan speed.
1256f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
12613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @param rpm: integer of fan speed RPM to set
1276f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1286f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        @returns True if success False otherwise.
1296f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """
1306f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        response = self.ec_command('pwmsetfanrpm %d' % rpm)
1316f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        logging.info('Set fan speed: %d', rpm)
1326f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        return (re.search(self.SET_FANSPEED_RE, response) is not None)
1336f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1346f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    def get_temperature(self, idx):
1356f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """Gets temperature from idx sensor.
1366f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
13713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @param idx: integer of temp sensor to read.
1386f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1396f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        @raises error.TestError if fails to read sensor.
1406f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1416f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        @returns integer of temperature reading in degrees Kelvin.
1426f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """
1436f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        response = self.ec_command('temps %d' % idx)
1446f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        match = re.search(self.TEMP_SENSOR_RE, response)
1456f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        if not match:
1466f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch            raise error.TestError('Unable to read temperature sensor %d' % idx)
1476f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1486f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        return int(match.group(1))
1496f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1506f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    def get_battery(self):
1516f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """Get battery presence (design capacity found).
1526f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1536f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        @returns True if success False otherwise.
1546f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """
1556f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        response = self.ec_command('battery')
1566f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        return (re.search(self.BATTERY_RE, response) is not None)
1576f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1586f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch    def get_lightbar(self):
1596f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """Test lightbar.
1606f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch
1616f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        @returns True if success False otherwise.
1626f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        """
1636f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        self.ec_command('lightbar on')
1646f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        self.ec_command('lightbar init')
1656f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        self.ec_command('lightbar 4 255 255 255')
1666f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        response = self.ec_command('lightbar')
1676f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        self.ec_command('lightbar off')
1686f893d2c9e26ef70ea3ccd2b2866388c73579670Todd Broch        return (re.search(self.LIGHTBAR_RE, response, re.MULTILINE) is not None)
16913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
170b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh    def key_press(self, key):
171b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        """Emit key down and up signal of the keyboard.
172b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh
173b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        @param key: name of a key defined in KEYMATRIX.
174b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        """
175b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        self.key_down(key)
176b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        self.key_up(key)
177b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh
178b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh    def _key_action(self, key, action_type):
179b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        if not key in KEYMATRIX:
180b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh            raise error.TestError('Unknown key: ' + key)
181b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        row, col = KEYMATRIX[key]
182b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        self.ec_command('kbpress %d %d %d' % (row, col, action_type))
183b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh
184b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh    def key_down(self, key):
185b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        """Emit key down signal of the keyboard.
186b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh
187b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        @param key: name of a key defined in KEYMATRIX.
188b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        """
189b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        self._key_action(key, 1)
190b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh
191b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh    def key_up(self, key):
192b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        """Emit key up signal of the keyboard.
193b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh
194b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        @param key: name of a key defined in KEYMATRIX.
195b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        """
196b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh        self._key_action(key, 0)
197b5210efc64510e1b1335b1561f3e23a99cf91053Victor Hsieh
19813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
19913f929ddf163cc7e475ed34e130e875da11c99c4Todd Brochclass EC_USBPD_Port(EC_Common):
20013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    """Class for CrOS embedded controller for USB-PD Port.
20113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
202ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    Public attributes:
203ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        index: integer of USB type-C port index.
204ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
20513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    Public Methods:
20613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        is_dfp: Determine if data role is Downstream Facing Port (DFP).
20713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        is_amode_supported: Check if alternate mode is supported by port.
20813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        is_amode_entered: Check if alternate mode is entered.
20913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        set_amode: Set an alternate mode.
21013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
21113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    Private attributes:
21213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        _port: integer of USB type-C port id.
21313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        _port_info: holds usbpd protocol info.
21413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        _amodes: holds alternate mode info.
21513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
21613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    Private methods:
21713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        _invalidate_port_data: Remove port data to force re-eval.
21813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        _get_port_info: Get USB-PD port info.
21913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        _get_amodes: parse and return port's svid info.
22013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    """
221ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def __init__(self, index):
22213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """Constructor.
22313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
224ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param index: integer of USB type-C port index.
22513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """
226ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        self.index = index
22713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        # TODO(crosbug.com/p/38133) target= only works for samus
22813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        super(EC_USBPD_Port, self).__init__(target='cros_pd')
22913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
23013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        # Interrogate port at instantiation.  Use invalidate to force re-eval.
23113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        self._port_info = self._get_port_info()
23213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        self._amodes = self._get_amodes()
23313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
23413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    def _invalidate_port_data(self):
23513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """Remove port data to force re-eval."""
23613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        self._port_info = None
23713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        self._amodes = None
23813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
23913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    def _get_port_info(self):
24013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """Get USB-PD port info.
24113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
24213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        ectool command usbpd provides the following information about the port:
24313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          - Enabled/Disabled
24413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          - Power & Data Role
24513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          - Polarity
24613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          - Protocol State
24713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
24813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        At time of authoring it looks like:
24913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          Port C0 is enabled, Role:SNK UFP Polarity:CC2 State:SNK_READY
25013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
25113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @raises error.TestError if ...
25213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          port info not parseable.
25313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
25413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @returns dictionary for <port> with keyval pairs:
25513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          enabled: True | False | None
25613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          power_role: sink | source | None
25713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          data_role: UFP | DFP | None
25813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          is_reversed: True | False | None
25913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          state: various strings | None
26013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """
26113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        PORT_INFO_RE = 'Port\s+C(\d+)\s+is\s+(\w+),\s+Role:(\w+)\s+(\w+)\s+' + \
26213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                       'Polarity:CC(\d+)\s+State:(\w+)'
26313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
26413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        match = re.search(PORT_INFO_RE,
265ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                          self.ec_command("usbpd %s" % (self.index)))
266ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        if not match or int(match.group(1)) != self.index:
267ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            error.TestError('Unable to determine port %d info' % self.index)
26813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
26913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        pinfo = dict(enabled=None, power_role=None, data_role=None,
27013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                    is_reversed=None, state=None)
27113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        pinfo['enabled'] = match.group(2) == 'enabled'
27213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        pinfo['power_role'] = 'sink' if match.group(3) == 'SNK' else 'source'
27313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        pinfo['data_role'] = match.group(4)
27413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        pinfo['is_reversed'] = True if match.group(5) == '2' else False
27513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        pinfo['state'] = match.group(6)
27613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        logging.debug('port_info = %s', pinfo)
27713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        return pinfo
27813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
27913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    def _get_amodes(self):
28013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """Parse alternate modes from pdgetmode.
28113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
28213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        Looks like ...
28313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          *SVID:0xff01 *0x00000485  0x00000000 ...
28413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          SVID:0x18d1   0x00000001  0x00000000 ...
28513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
28613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @returns dictionary of format:
28713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          <svid>: {active: True|False, configs: <config_list>, opos:<opos>}
28813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            where:
28913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch              <svid>        : USB-IF Standard or vendor id as
29013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                              hex string (i.e. 0xff01)
29113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch              <config_list> : list of uint32_t configs
29213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch              <opos>        : integer of active object position.
29313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                              Note, this is the config list index + 1
29413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """
29513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        SVID_RE = r'(\*?)SVID:(\S+)\s+(.*)'
29613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        svids = dict()
297ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        cmd = 'pdgetmode %d' % self.index
29813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        for line in self.ec_command(cmd, ignore_status=True).split('\n'):
29913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            if line.strip() == '':
30013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                continue
30113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            logging.debug('pdgetmode line: %s', line)
30213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            match = re.search(SVID_RE, line)
30313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            if not match:
30413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                logging.warning("Unable to parse SVID line %s", line)
30513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                continue
30613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            active = match.group(1) == '*'
30713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            svid = match.group(2)
30813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            configs_str = match.group(3)
30913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            configs = list()
31013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            opos = None
31113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            for i,config in enumerate(configs_str.split(), 1):
31213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                if config.startswith('*'):
31313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                    opos = i
31413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                    config = config[1:]
31513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                config = int(config, 16)
31613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                # ignore unpopulated configs
31713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                if config == 0:
31813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                    continue
31913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                configs.append(config)
32013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            svids[svid] = dict(active=active, configs=configs, opos=opos)
32113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
322ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        logging.debug("Port %d svids = %s", self.index, svids)
32313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        return svids
32413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
32513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    def is_dfp(self):
32613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """Determine if data role is Downstream Facing Port (DFP).
32713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
32813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @returns True if DFP False otherwise.
32913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """
33013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        if self._port_info is None:
33113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            self._port_info = self._get_port_info()
33213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
33313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        return self._port_info['data_role'] == 'DFP'
33413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
33513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    def is_amode_supported(self, svid):
33613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """Check if alternate mode is supported by port partner.
33713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
33813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @param svid: alternate mode SVID hexstring (i.e. 0xff01)
33913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """
34013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        if self._amodes is None:
34113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            self._amodes = self._get_amodes()
34213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
34313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        if svid in self._amodes.keys():
34413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            return True
34513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        return False
34613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
34713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    def is_amode_entered(self, svid, opos):
34813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """Check if alternate mode is entered.
34913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
35013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @param svid: alternate mode SVID hexstring (i.e. 0xff01).
35113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @param opos: object position of config to act on.
35213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
35313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @returns True if entered False otherwise
35413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """
35513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        if self._amodes is None:
35613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            self._amodes = self._get_amodes()
35713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
35813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        if not self.is_amode_supported(svid):
35913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            return False
36013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
36113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        if self._amodes[svid]['active'] and self._amodes[svid]['opos'] == opos:
36213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            return True
36313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
36413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        return False
36513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
36613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    def set_amode(self, svid, opos, enter, delay_secs=2):
36713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """Set alternate mode.
36813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
36913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @param svid: alternate mode SVID hexstring (i.e. 0xff01).
37013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @param opos: object position of config to act on.
37113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @param enter: Boolean of whether to enter mode.
37213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
37313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @raises error.TestError if ...
37413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch           mode not supported.
37513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch           opos is > number of configs.
37613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
37713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @returns True if successful False otherwise
37813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """
37913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        if self._amodes is None:
38013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            self._amodes = self._get_amodes()
38113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
38213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        if svid not in self._amodes.keys():
38313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            raise error.TestError("SVID %s not supported", svid)
38413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
38513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        if opos > len(self._amodes[svid]['configs']):
38613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            raise error.TestError("opos > available configs")
38713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
388ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        cmd = "pdsetmode %d %s %d %d" % (self.index, svid, opos,
38913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch                                         1 if enter else 0)
39013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        self.ec_command(cmd, ignore_status=True)
39113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        self._invalidate_port_data()
39213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
39313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        # allow some time for mode entry/exit
39413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        time.sleep(delay_secs)
39513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        return self.is_amode_entered(svid, opos) == enter
39613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
397ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def get_flash_info(self):
398ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        mat0_re = r'has no discovered device'
399ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        mat1_re = r'.*ptype:(\d+)\s+vid:(\w+)\s+pid:(\w+).*'
400ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        mat2_re = r'.*DevId:(\d+)\.(\d+)\s+Hash:\s*(\w+.*)\s*CurImg:(\w+).*'
401ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        flash_dict = dict.fromkeys(['ptype', 'vid', 'pid', 'dev_major',
402ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                                    'dev_minor', 'rw_hash', 'image_status'])
403ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
404ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        cmd = 'infopddev %d' % self.index
405ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
406ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        tries = 3
407ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        while (tries):
408ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            res = self.ec_command(cmd, ignore_status=True)
409ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            if not 'has no discovered device' in res:
410ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                break
411ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
412ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            tries -= 1
413ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            time.sleep(1)
414ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
415ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        for ln in res.split('\n'):
416ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            mat1 = re.match(mat1_re, ln)
417ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            if mat1:
418ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                flash_dict['ptype'] = int(mat1.group(1))
419ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                flash_dict['vid'] = mat1.group(2)
420ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                flash_dict['pid'] = mat1.group(3)
421ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                continue
422ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
423ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            mat2 = re.match(mat2_re, ln)
424ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            if mat2:
425ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                flash_dict['dev_major'] = int(mat2.group(1))
426ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                flash_dict['dev_minor'] = int(mat2.group(2))
427ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                flash_dict['rw_hash'] = mat2.group(3)
428ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                flash_dict['image_status'] = mat2.group(4)
429ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                break
430ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
431ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        return flash_dict
432ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
43313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
43413f929ddf163cc7e475ed34e130e875da11c99c4Todd Brochclass EC_USBPD(EC_Common):
43513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    """Class for CrOS embedded controller for USB-PD.
43613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
43713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    Public attributes:
43813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        ports: list EC_USBPD_Port instances
43913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
44013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    Public Methods:
44113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        get_num_ports: get number of USB-PD ports device has.
44213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
44313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    Private attributes:
44413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        _num_ports: integer number of USB-PD ports device has.
44513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    """
44613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    def __init__(self, num_ports=None):
44713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """Constructor.
44813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
44913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @param num_ports: total number of USB-PD ports on device.  This is an
45013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch          override.  If left 'None' will try to determine.
45113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """
45213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        self._num_ports = num_ports
45313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        self.ports = list()
45413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
45513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        # TODO(crosbug.com/p/38133) target= only works for samus
45613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        super(EC_USBPD, self).__init__(target='cros_pd')
45713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
45813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        if (self.get_num_ports() == 0):
45913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            raise error.TestNAError("Device has no USB-PD ports")
46013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
46113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        for i in xrange(self._num_ports):
46213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            self.ports.append(EC_USBPD_Port(i))
46313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
46413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch    def get_num_ports(self):
46513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """Determine the number of ports for device.
46613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
46713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        Uses ectool's usbpdpower command which in turn makes host command call
46813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        to EC_CMD_USB_PD_PORTS to determine the number of ports.
46913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
47013f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        TODO(tbroch) May want to consider adding separate ectool command to
47113f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        surface the number of ports directly instead of via usbpdpower
47213f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
47313f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        @returns number of ports.
47413f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        """
47513f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        if (self._num_ports is not None):
47613f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch            return self._num_ports
47713f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch
47813f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        self._num_ports = len(self.ec_command("usbpdpower").split(b'\n'))
47913f929ddf163cc7e475ed34e130e875da11c99c4Todd Broch        return self._num_ports
480