xref: /aosp_15_r20/external/autotest/server/cros/servo/chrome_ec.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Liimport ast
6*9c5db199SXin Liimport logging
7*9c5db199SXin Liimport re
8*9c5db199SXin Liimport time
9*9c5db199SXin Lifrom xml.parsers import expat
10*9c5db199SXin Li
11*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
12*9c5db199SXin Lifrom autotest_lib.client.cros import ec
13*9c5db199SXin Lifrom autotest_lib.server.cros.servo import servo
14*9c5db199SXin Li
15*9c5db199SXin Li# Hostevent codes, copied from:
16*9c5db199SXin Li#     ec/include/ec_commands.h
17*9c5db199SXin LiHOSTEVENT_LID_CLOSED        = 0x00000001
18*9c5db199SXin LiHOSTEVENT_LID_OPEN          = 0x00000002
19*9c5db199SXin LiHOSTEVENT_POWER_BUTTON      = 0x00000004
20*9c5db199SXin LiHOSTEVENT_AC_CONNECTED      = 0x00000008
21*9c5db199SXin LiHOSTEVENT_AC_DISCONNECTED   = 0x00000010
22*9c5db199SXin LiHOSTEVENT_BATTERY_LOW       = 0x00000020
23*9c5db199SXin LiHOSTEVENT_BATTERY_CRITICAL  = 0x00000040
24*9c5db199SXin LiHOSTEVENT_BATTERY           = 0x00000080
25*9c5db199SXin LiHOSTEVENT_THERMAL_THRESHOLD = 0x00000100
26*9c5db199SXin LiHOSTEVENT_THERMAL_OVERLOAD  = 0x00000200
27*9c5db199SXin LiHOSTEVENT_THERMAL           = 0x00000400
28*9c5db199SXin LiHOSTEVENT_USB_CHARGER       = 0x00000800
29*9c5db199SXin LiHOSTEVENT_KEY_PRESSED       = 0x00001000
30*9c5db199SXin LiHOSTEVENT_INTERFACE_READY   = 0x00002000
31*9c5db199SXin Li# Keyboard recovery combo has been pressed
32*9c5db199SXin LiHOSTEVENT_KEYBOARD_RECOVERY = 0x00004000
33*9c5db199SXin Li# Shutdown due to thermal overload
34*9c5db199SXin LiHOSTEVENT_THERMAL_SHUTDOWN  = 0x00008000
35*9c5db199SXin Li# Shutdown due to battery level too low
36*9c5db199SXin LiHOSTEVENT_BATTERY_SHUTDOWN  = 0x00010000
37*9c5db199SXin LiHOSTEVENT_INVALID           = 0x80000000
38*9c5db199SXin Li
39*9c5db199SXin Li# Time to wait after sending keypress commands.
40*9c5db199SXin LiKEYPRESS_RECOVERY_TIME = 0.5
41*9c5db199SXin Li
42*9c5db199SXin Li# Wakemask types, copied from:
43*9c5db199SXin Li#     ec/include/ec_commands.h
44*9c5db199SXin LiEC_HOST_EVENT_MAIN = 0
45*9c5db199SXin LiEC_HOST_EVENT_B = 1
46*9c5db199SXin LiEC_HOST_EVENT_SCI_MASK = 2
47*9c5db199SXin LiEC_HOST_EVENT_SMI_MASK = 3
48*9c5db199SXin LiEC_HOST_EVENT_ALWAYS_REPORT_MASK = 4
49*9c5db199SXin LiEC_HOST_EVENT_ACTIVE_WAKE_MASK = 5
50*9c5db199SXin LiEC_HOST_EVENT_LAZY_WAKE_MASK_S0IX = 6
51*9c5db199SXin LiEC_HOST_EVENT_LAZY_WAKE_MASK_S3 = 7
52*9c5db199SXin LiEC_HOST_EVENT_LAZY_WAKE_MASK_S5 = 8
53*9c5db199SXin Li
54*9c5db199SXin Li
55*9c5db199SXin Liclass ChromeConsole(object):
56*9c5db199SXin Li    """Manages control of a Chrome console.
57*9c5db199SXin Li
58*9c5db199SXin Li    We control the Chrome console via the UART of a Servo board. Chrome console
59*9c5db199SXin Li    provides many interfaces to set and get its behavior via console commands.
60*9c5db199SXin Li    This class is to abstract these interfaces.
61*9c5db199SXin Li    """
62*9c5db199SXin Li
63*9c5db199SXin Li    CMD = "_cmd"
64*9c5db199SXin Li    REGEXP = "_regexp"
65*9c5db199SXin Li    MULTICMD = "_multicmd"
66*9c5db199SXin Li
67*9c5db199SXin Li    # EC Features
68*9c5db199SXin Li    # Quoted from 'enum ec_feature_code' in platform/ec/include/ec_commands.h.
69*9c5db199SXin Li    EC_FEATURE = {
70*9c5db199SXin Li        'EC_FEATURE_LIMITED'                            : 0,
71*9c5db199SXin Li        'EC_FEATURE_FLASH'                              : 1,
72*9c5db199SXin Li        'EC_FEATURE_PWM_FAN'                            : 2,
73*9c5db199SXin Li        'EC_FEATURE_PWM_KEYB'                           : 3,
74*9c5db199SXin Li        'EC_FEATURE_LIGHTBAR'                           : 4,
75*9c5db199SXin Li        'EC_FEATURE_LED'                                : 5,
76*9c5db199SXin Li        'EC_FEATURE_MOTION_SENSE'                       : 6,
77*9c5db199SXin Li        'EC_FEATURE_KEYB'                               : 7,
78*9c5db199SXin Li        'EC_FEATURE_PSTORE'                             : 8,
79*9c5db199SXin Li        'EC_FEATURE_PORT80'                             : 9,
80*9c5db199SXin Li        'EC_FEATURE_THERMAL'                            : 10,
81*9c5db199SXin Li        'EC_FEATURE_BKLIGHT_SWITCH'                     : 11,
82*9c5db199SXin Li        'EC_FEATURE_WIFI_SWITCH'                        : 12,
83*9c5db199SXin Li        'EC_FEATURE_HOST_EVENTS'                        : 13,
84*9c5db199SXin Li        'EC_FEATURE_GPIO'                               : 14,
85*9c5db199SXin Li        'EC_FEATURE_I2C'                                : 15,
86*9c5db199SXin Li        'EC_FEATURE_CHARGER'                            : 16,
87*9c5db199SXin Li        'EC_FEATURE_BATTERY'                            : 17,
88*9c5db199SXin Li        'EC_FEATURE_SMART_BATTERY'                      : 18,
89*9c5db199SXin Li        'EC_FEATURE_HANG_DETECT'                        : 19,
90*9c5db199SXin Li        'EC_FEATURE_PMU'                                : 20,
91*9c5db199SXin Li        'EC_FEATURE_SUB_MCU'                            : 21,
92*9c5db199SXin Li        'EC_FEATURE_USB_PD'                             : 22,
93*9c5db199SXin Li        'EC_FEATURE_USB_MUX'                            : 23,
94*9c5db199SXin Li        'EC_FEATURE_MOTION_SENSE_FIFO'                  : 24,
95*9c5db199SXin Li        'EC_FEATURE_VSTORE'                             : 25,
96*9c5db199SXin Li        'EC_FEATURE_USBC_SS_MUX_VIRTUAL'                : 26,
97*9c5db199SXin Li        'EC_FEATURE_RTC'                                : 27,
98*9c5db199SXin Li        'EC_FEATURE_FINGERPRINT'                        : 28,
99*9c5db199SXin Li        'EC_FEATURE_TOUCHPAD'                           : 29,
100*9c5db199SXin Li        'EC_FEATURE_RWSIG'                              : 30,
101*9c5db199SXin Li        'EC_FEATURE_DEVICE_EVENT'                       : 31,
102*9c5db199SXin Li        'EC_FEATURE_UNIFIED_WAKE_MASKS'                 : 32,
103*9c5db199SXin Li        'EC_FEATURE_HOST_EVENT64'                       : 33,
104*9c5db199SXin Li        'EC_FEATURE_EXEC_IN_RAM'                        : 34,
105*9c5db199SXin Li        'EC_FEATURE_CEC'                                : 35,
106*9c5db199SXin Li        'EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS'      : 36,
107*9c5db199SXin Li        'EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS'     : 37,
108*9c5db199SXin Li        'EC_FEATURE_EFS2'                               : 38,
109*9c5db199SXin Li        'EC_FEATURE_SCP'                                : 39,
110*9c5db199SXin Li        'EC_FEATURE_ISH'                                : 40,
111*9c5db199SXin Li    }
112*9c5db199SXin Li
113*9c5db199SXin Li    def __init__(self, servo, name):
114*9c5db199SXin Li        """Initialize and keep the servo object.
115*9c5db199SXin Li
116*9c5db199SXin Li        Args:
117*9c5db199SXin Li          servo: A Servo object.
118*9c5db199SXin Li          name: The console name.
119*9c5db199SXin Li        """
120*9c5db199SXin Li        self.name = name
121*9c5db199SXin Li        self.uart_cmd = self.name + self.CMD
122*9c5db199SXin Li        self.uart_regexp = self.name + self.REGEXP
123*9c5db199SXin Li        self.uart_multicmd = self.name + self.MULTICMD
124*9c5db199SXin Li
125*9c5db199SXin Li        self._servo = servo
126*9c5db199SXin Li
127*9c5db199SXin Li    def __repr__(self):
128*9c5db199SXin Li        """Return a string representation: <ChromeConsole 'foo_uart'>"""
129*9c5db199SXin Li        return "<%s %r>" % (self.__class__.__name__, self.name)
130*9c5db199SXin Li
131*9c5db199SXin Li    def set_uart_regexp(self, regexp):
132*9c5db199SXin Li        self._servo.set(self.uart_regexp, regexp)
133*9c5db199SXin Li
134*9c5db199SXin Li    def clear_uart_regex(self):
135*9c5db199SXin Li        """Clear uart_regexp"""
136*9c5db199SXin Li        self.set_uart_regexp('None')
137*9c5db199SXin Li
138*9c5db199SXin Li    def send_command(self, commands):
139*9c5db199SXin Li        """Send command through UART.
140*9c5db199SXin Li
141*9c5db199SXin Li        This function opens UART pty when called, and then command is sent
142*9c5db199SXin Li        through UART.
143*9c5db199SXin Li
144*9c5db199SXin Li        Args:
145*9c5db199SXin Li          commands: The commands to send, either a list or a string.
146*9c5db199SXin Li        """
147*9c5db199SXin Li        self.clear_uart_regex()
148*9c5db199SXin Li        if isinstance(commands, list):
149*9c5db199SXin Li            try:
150*9c5db199SXin Li                self._servo.set_nocheck(self.uart_multicmd, ';'.join(commands))
151*9c5db199SXin Li            except servo.ControlUnavailableError:
152*9c5db199SXin Li                logging.warning('The servod is too old that uart_multicmd not '
153*9c5db199SXin Li                                'supported. Use uart_cmd instead.')
154*9c5db199SXin Li                for command in commands:
155*9c5db199SXin Li                    self._servo.set_nocheck(self.uart_cmd, command)
156*9c5db199SXin Li        else:
157*9c5db199SXin Li            self._servo.set_nocheck(self.uart_cmd, commands)
158*9c5db199SXin Li        self.clear_uart_regex()
159*9c5db199SXin Li
160*9c5db199SXin Li    def has_command(self, command):
161*9c5db199SXin Li        """Check whether EC console supports |command|.
162*9c5db199SXin Li
163*9c5db199SXin Li        Args:
164*9c5db199SXin Li          command: Command to look for.
165*9c5db199SXin Li
166*9c5db199SXin Li        Returns:
167*9c5db199SXin Li          True: If the |command| exist on the EC image of the device.
168*9c5db199SXin Li          False: If the |command| does not exist on the EC image of the device.
169*9c5db199SXin Li        """
170*9c5db199SXin Li        result = None
171*9c5db199SXin Li        try:
172*9c5db199SXin Li            # Throws error.TestFail (on timeout) if it cannot find a line with
173*9c5db199SXin Li            # 'command' in the output. Thus return False in that case.
174*9c5db199SXin Li            result = self.send_command_get_output('help', [command])
175*9c5db199SXin Li        except error.TestFail:
176*9c5db199SXin Li            return False
177*9c5db199SXin Li        return result is not None
178*9c5db199SXin Li
179*9c5db199SXin Li    def send_command_get_output(self, command, regexp_list, retries=1):
180*9c5db199SXin Li        """Send command through UART and wait for response.
181*9c5db199SXin Li
182*9c5db199SXin Li        This function waits for response message matching regular expressions.
183*9c5db199SXin Li
184*9c5db199SXin Li        Args:
185*9c5db199SXin Li          command: The command sent.
186*9c5db199SXin Li          regexp_list: List of regular expressions used to match response
187*9c5db199SXin Li            message. Note, list must be ordered.
188*9c5db199SXin Li
189*9c5db199SXin Li        Returns:
190*9c5db199SXin Li          List of tuples, each of which contains the entire matched string and
191*9c5db199SXin Li          all the subgroups of the match. None if not matched.
192*9c5db199SXin Li          For example:
193*9c5db199SXin Li            response of the given command:
194*9c5db199SXin Li              High temp: 37.2
195*9c5db199SXin Li              Low temp: 36.4
196*9c5db199SXin Li            regexp_list:
197*9c5db199SXin Li              ['High temp: (\d+)\.(\d+)', 'Low temp: (\d+)\.(\d+)']
198*9c5db199SXin Li            returns:
199*9c5db199SXin Li              [('High temp: 37.2', '37', '2'), ('Low temp: 36.4', '36', '4')]
200*9c5db199SXin Li
201*9c5db199SXin Li        Raises:
202*9c5db199SXin Li          error.TestError: An error when the given regexp_list is not valid.
203*9c5db199SXin Li        """
204*9c5db199SXin Li        if not isinstance(regexp_list, list):
205*9c5db199SXin Li            raise error.TestError('Arugment regexp_list is not a list: %s' %
206*9c5db199SXin Li                                  str(regexp_list))
207*9c5db199SXin Li
208*9c5db199SXin Li        while retries > 0:
209*9c5db199SXin Li            retries -= 1
210*9c5db199SXin Li            try:
211*9c5db199SXin Li                self.set_uart_regexp(str(regexp_list))
212*9c5db199SXin Li                self._servo.set_nocheck(self.uart_cmd, command)
213*9c5db199SXin Li                return ast.literal_eval(self._servo.get(self.uart_cmd))
214*9c5db199SXin Li            except (servo.UnresponsiveConsoleError,
215*9c5db199SXin Li                    servo.ResponsiveConsoleError, expat.ExpatError) as e:
216*9c5db199SXin Li                if retries <= 0:
217*9c5db199SXin Li                    raise
218*9c5db199SXin Li                logging.warning('Failed to send EC cmd. %s', e)
219*9c5db199SXin Li            finally:
220*9c5db199SXin Li                self.clear_uart_regex()
221*9c5db199SXin Li
222*9c5db199SXin Li
223*9c5db199SXin Li    def is_dfp(self, port=0):
224*9c5db199SXin Li        """This function checks if EC is DFP
225*9c5db199SXin Li
226*9c5db199SXin Li        Args:
227*9c5db199SXin Li          port: Port of EC to check
228*9c5db199SXin Li
229*9c5db199SXin Li        Returns:
230*9c5db199SXin Li          True: if EC is DFP
231*9c5db199SXin Li          False: if EC is not DFP
232*9c5db199SXin Li        """
233*9c5db199SXin Li        is_dfp = None
234*9c5db199SXin Li        ret = None
235*9c5db199SXin Li        try:
236*9c5db199SXin Li            ret = self.send_command_get_output("pd %d state" % port,
237*9c5db199SXin Li                                               ["DFP.*Flag"])
238*9c5db199SXin Li            is_dfp = True
239*9c5db199SXin Li        except Exception as e:
240*9c5db199SXin Li            is_dfp = False
241*9c5db199SXin Li
242*9c5db199SXin Li        # For TCPMv1, after disconnecting a device the data state remains
243*9c5db199SXin Li        # the same, so even when pd state shows DPF, make sure the device is
244*9c5db199SXin Li        # not disconnected
245*9c5db199SXin Li        if is_dfp:
246*9c5db199SXin Li            if "DRP_AUTO_TOGGLE" in ret[0] or "DISCONNECTED" in ret[0]:
247*9c5db199SXin Li                is_dfp = False
248*9c5db199SXin Li
249*9c5db199SXin Li        return is_dfp
250*9c5db199SXin Li
251*9c5db199SXin Li
252*9c5db199SXin Liclass ChromeEC(ChromeConsole):
253*9c5db199SXin Li    """Manages control of a Chrome EC.
254*9c5db199SXin Li
255*9c5db199SXin Li    We control the Chrome EC via the UART of a Servo board. Chrome EC
256*9c5db199SXin Li    provides many interfaces to set and get its behavior via console commands.
257*9c5db199SXin Li    This class is to abstract these interfaces.
258*9c5db199SXin Li    """
259*9c5db199SXin Li
260*9c5db199SXin Li    # The dict to cache the battery information
261*9c5db199SXin Li    BATTERY_INFO = {}
262*9c5db199SXin Li
263*9c5db199SXin Li    def __init__(self, servo, name="ec_uart"):
264*9c5db199SXin Li        super(ChromeEC, self).__init__(servo, name)
265*9c5db199SXin Li
266*9c5db199SXin Li    def __repr__(self):
267*9c5db199SXin Li        """Return a string representation of the object: <ChromeEC 'ec_uart'>"""
268*9c5db199SXin Li        return "<%s %r>" % (self.__class__.__name__, self.name)
269*9c5db199SXin Li
270*9c5db199SXin Li    def key_down(self, keyname):
271*9c5db199SXin Li        """Simulate pressing a key.
272*9c5db199SXin Li
273*9c5db199SXin Li        Args:
274*9c5db199SXin Li          keyname: Key name, one of the keys of KEYMATRIX.
275*9c5db199SXin Li        """
276*9c5db199SXin Li        self.send_command('kbpress %d %d 1' %
277*9c5db199SXin Li                (ec.KEYMATRIX[keyname][1], ec.KEYMATRIX[keyname][0]))
278*9c5db199SXin Li
279*9c5db199SXin Li
280*9c5db199SXin Li    def key_up(self, keyname):
281*9c5db199SXin Li        """Simulate releasing a key.
282*9c5db199SXin Li
283*9c5db199SXin Li        Args:
284*9c5db199SXin Li          keyname: Key name, one of the keys of KEYMATRIX.
285*9c5db199SXin Li        """
286*9c5db199SXin Li        self.send_command('kbpress %d %d 0' %
287*9c5db199SXin Li                (ec.KEYMATRIX[keyname][1], ec.KEYMATRIX[keyname][0]))
288*9c5db199SXin Li
289*9c5db199SXin Li
290*9c5db199SXin Li    def key_press(self, keyname):
291*9c5db199SXin Li        """Press and then release a key.
292*9c5db199SXin Li
293*9c5db199SXin Li        Args:
294*9c5db199SXin Li          keyname: Key name, one of the keys of KEYMATRIX.
295*9c5db199SXin Li        """
296*9c5db199SXin Li        self.send_command([
297*9c5db199SXin Li                'kbpress %d %d 1' %
298*9c5db199SXin Li                    (ec.KEYMATRIX[keyname][1], ec.KEYMATRIX[keyname][0]),
299*9c5db199SXin Li                'kbpress %d %d 0' %
300*9c5db199SXin Li                    (ec.KEYMATRIX[keyname][1], ec.KEYMATRIX[keyname][0]),
301*9c5db199SXin Li                ])
302*9c5db199SXin Li        # Don't spam the EC console as fast as we can; leave some recovery time
303*9c5db199SXin Li        # in between commands.
304*9c5db199SXin Li        time.sleep(KEYPRESS_RECOVERY_TIME)
305*9c5db199SXin Li
306*9c5db199SXin Li
307*9c5db199SXin Li    def send_key_string_raw(self, string):
308*9c5db199SXin Li        """Send key strokes consisting of only characters.
309*9c5db199SXin Li
310*9c5db199SXin Li        Args:
311*9c5db199SXin Li          string: Raw string.
312*9c5db199SXin Li        """
313*9c5db199SXin Li        for c in string:
314*9c5db199SXin Li            self.key_press(c)
315*9c5db199SXin Li
316*9c5db199SXin Li
317*9c5db199SXin Li    def send_key_string(self, string):
318*9c5db199SXin Li        """Send key strokes including special keys.
319*9c5db199SXin Li
320*9c5db199SXin Li        Args:
321*9c5db199SXin Li          string: Character string including special keys. An example
322*9c5db199SXin Li            is "this is an<tab>example<enter>".
323*9c5db199SXin Li        """
324*9c5db199SXin Li        for m in re.finditer("(<[^>]+>)|([^<>]+)", string):
325*9c5db199SXin Li            sp, raw = m.groups()
326*9c5db199SXin Li            if raw is not None:
327*9c5db199SXin Li                self.send_key_string_raw(raw)
328*9c5db199SXin Li            else:
329*9c5db199SXin Li                self.key_press(sp)
330*9c5db199SXin Li
331*9c5db199SXin Li
332*9c5db199SXin Li    def reboot(self, flags=''):
333*9c5db199SXin Li        """Reboot EC with given flags.
334*9c5db199SXin Li
335*9c5db199SXin Li        Args:
336*9c5db199SXin Li          flags: Optional, a space-separated string of flags passed to the
337*9c5db199SXin Li                 reboot command, including:
338*9c5db199SXin Li                   default: EC soft reboot;
339*9c5db199SXin Li                   'hard': EC hard/cold reboot;
340*9c5db199SXin Li                   'ap-off': Leave AP off after EC reboot (by default, EC turns
341*9c5db199SXin Li                             AP on after reboot if lid is open).
342*9c5db199SXin Li
343*9c5db199SXin Li        Raises:
344*9c5db199SXin Li          error.TestError: If the string of flags is invalid.
345*9c5db199SXin Li        """
346*9c5db199SXin Li        for flag in flags.split():
347*9c5db199SXin Li            if flag not in ('hard', 'ap-off'):
348*9c5db199SXin Li                raise error.TestError(
349*9c5db199SXin Li                        'The flag %s of EC reboot command is invalid.' % flag)
350*9c5db199SXin Li        self.send_command("reboot %s" % flags)
351*9c5db199SXin Li
352*9c5db199SXin Li
353*9c5db199SXin Li    def set_flash_write_protect(self, enable):
354*9c5db199SXin Li        """Set the software write protect of EC flash.
355*9c5db199SXin Li
356*9c5db199SXin Li        Args:
357*9c5db199SXin Li          enable: True to enable write protect, False to disable.
358*9c5db199SXin Li        """
359*9c5db199SXin Li        if enable:
360*9c5db199SXin Li            self.send_command("flashwp enable")
361*9c5db199SXin Li        else:
362*9c5db199SXin Li            self.send_command("flashwp disable")
363*9c5db199SXin Li
364*9c5db199SXin Li
365*9c5db199SXin Li    def set_hostevent(self, codes):
366*9c5db199SXin Li        """Set the EC hostevent codes.
367*9c5db199SXin Li
368*9c5db199SXin Li        Args:
369*9c5db199SXin Li          codes: Hostevent codes, HOSTEVENT_*
370*9c5db199SXin Li        """
371*9c5db199SXin Li        self.send_command("hostevent set %#x" % codes)
372*9c5db199SXin Li        # Allow enough time for EC to process input and set flag.
373*9c5db199SXin Li        # See chromium:371631 for details.
374*9c5db199SXin Li        # FIXME: Stop importing time module if this hack becomes obsolete.
375*9c5db199SXin Li        time.sleep(1)
376*9c5db199SXin Li
377*9c5db199SXin Li
378*9c5db199SXin Li    def enable_console_channel(self, channel):
379*9c5db199SXin Li        """Find console channel mask and enable that channel only
380*9c5db199SXin Li
381*9c5db199SXin Li        @param channel: console channel name
382*9c5db199SXin Li        """
383*9c5db199SXin Li        # The 'chan' command returns a list of console channels,
384*9c5db199SXin Li        # their channel masks and channel numbers
385*9c5db199SXin Li        regexp = r'(\d+)\s+([\w]+)\s+\*?\s+{0}'.format(channel)
386*9c5db199SXin Li        l = self.send_command_get_output('chan', [regexp])
387*9c5db199SXin Li        # Use channel mask and append the 0x for proper hex input value
388*9c5db199SXin Li        cmd = 'chan 0x' + l[0][2]
389*9c5db199SXin Li        # Set console to only output the desired channel
390*9c5db199SXin Li        self.send_command(cmd)
391*9c5db199SXin Li
392*9c5db199SXin Li
393*9c5db199SXin Li    def get_version(self):
394*9c5db199SXin Li        """Get version information from the Chrome EC console.
395*9c5db199SXin Li           Additionally, can be used to verify if EC console is available.
396*9c5db199SXin Li        """
397*9c5db199SXin Li        self.send_command("chan 0")
398*9c5db199SXin Li        # Use "[ \t]" here and not \s because sometimes the version is blank,
399*9c5db199SXin Li        # i.e. 'RO:   \r\n' which matches RO:\s+
400*9c5db199SXin Li        expected_output = [
401*9c5db199SXin Li                "Chip:[ \t]+([^\r\n]*)\r\n", "RO:[ \t]+([^\r\n]*)\r\n",
402*9c5db199SXin Li                "RW_?[AB]?:[ \t]+([^\r\n]*)\r\n", "Build:[ \t]+([^\r\n]*)\r\n"
403*9c5db199SXin Li        ]
404*9c5db199SXin Li        l = self.send_command_get_output("version", expected_output)
405*9c5db199SXin Li        self.send_command("chan 0xffffffff")
406*9c5db199SXin Li        return l
407*9c5db199SXin Li
408*9c5db199SXin Li    def check_ro_rw(self, img_exp):
409*9c5db199SXin Li        """Tell if the current EC image matches the given input, 'RW' or 'RO.
410*9c5db199SXin Li
411*9c5db199SXin Li        Args:
412*9c5db199SXin Li            img_exp: Expected image type. It should be either 'RW' or 'RO'.
413*9c5db199SXin Li        Return:
414*9c5db199SXin Li            True if the active EC image matches to 'img_exp'.
415*9c5db199SXin Li            False otherwise.
416*9c5db199SXin Li        Raise:
417*9c5db199SXin Li            TestError if img_exp is neither 'RW' nor 'RO'.
418*9c5db199SXin Li        """
419*9c5db199SXin Li        if img_exp not in ['RW', 'RO']:
420*9c5db199SXin Li            raise error.TestError('Arugment img_exp is neither RW nor RO')
421*9c5db199SXin Li
422*9c5db199SXin Li        result = self.send_command_get_output('sysinfo', [r'Copy:\s*(RO|RW)'])
423*9c5db199SXin Li        return result[0][1] == img_exp
424*9c5db199SXin Li
425*9c5db199SXin Li    def check_feature(self, feature):
426*9c5db199SXin Li        """Return true if EC supports the given feature
427*9c5db199SXin Li
428*9c5db199SXin Li        Args:
429*9c5db199SXin Li            feature: feature name as a string as in self.EC_FEATURE.
430*9c5db199SXin Li
431*9c5db199SXin Li        Returns:
432*9c5db199SXin Li            True if 'feature' is in EC's feature set.
433*9c5db199SXin Li            False otherwise
434*9c5db199SXin Li        """
435*9c5db199SXin Li        feat_id = self.EC_FEATURE[feature]
436*9c5db199SXin Li        if feat_id < 32:
437*9c5db199SXin Li            feat_start = 0
438*9c5db199SXin Li        else:
439*9c5db199SXin Li            feat_start = 32
440*9c5db199SXin Li
441*9c5db199SXin Li        regexp = r'%d-%d:\s*(0x[0-9a-fA-F]{8})' % (feat_start,
442*9c5db199SXin Li                                                   feat_start + 31)
443*9c5db199SXin Li
444*9c5db199SXin Li        try:
445*9c5db199SXin Li            result = self.send_command_get_output('feat', [regexp])
446*9c5db199SXin Li        except servo.ResponsiveConsoleError as e:
447*9c5db199SXin Li            logging.warning("feat command is not available: %s", str(e))
448*9c5db199SXin Li            return False
449*9c5db199SXin Li
450*9c5db199SXin Li        feat_bitmap = int(result[0][1], 16)
451*9c5db199SXin Li
452*9c5db199SXin Li        return ((1 << (feat_id - feat_start)) & feat_bitmap) != 0
453*9c5db199SXin Li
454*9c5db199SXin Li    def update_battery_info(self):
455*9c5db199SXin Li        """Get the battery info we care for this test."""
456*9c5db199SXin Li        # The battery parameters we care for this test. The order must match
457*9c5db199SXin Li        # the output of EC battery command.
458*9c5db199SXin Li        battery_params = [
459*9c5db199SXin Li                'V', 'V-desired', 'I', 'I-desired', 'Charging', 'Remaining'
460*9c5db199SXin Li        ]
461*9c5db199SXin Li        regex_str_list = []
462*9c5db199SXin Li
463*9c5db199SXin Li        for p in battery_params:
464*9c5db199SXin Li            if p == 'Remaining':
465*9c5db199SXin Li                regex_str_list.append(p + ':\s+(\d+)\s+')
466*9c5db199SXin Li            elif p == 'Charging':
467*9c5db199SXin Li                regex_str_list.append(p + r':\s+(Not Allowed|Allowed)\s+')
468*9c5db199SXin Li            else:
469*9c5db199SXin Li                regex_str_list.append(p +
470*9c5db199SXin Li                                      r':\s+0x[0-9a-f]*\s+=\s+([0-9-]+)\s+')
471*9c5db199SXin Li
472*9c5db199SXin Li        # For unknown reasons, servod doesn't always capture the ec
473*9c5db199SXin Li        # command output. It doesn't happen often, but retry if it does.
474*9c5db199SXin Li        retries = 3
475*9c5db199SXin Li        while retries > 0:
476*9c5db199SXin Li            retries -= 1
477*9c5db199SXin Li            try:
478*9c5db199SXin Li                battery_regex_match = self.send_command_get_output(
479*9c5db199SXin Li                        'battery', regex_str_list)
480*9c5db199SXin Li                break
481*9c5db199SXin Li            except (servo.UnresponsiveConsoleError,
482*9c5db199SXin Li                    servo.ResponsiveConsoleError) as e:
483*9c5db199SXin Li                if retries <= 0:
484*9c5db199SXin Li                    raise
485*9c5db199SXin Li                logging.warning('Failed to get battery status. %s', e)
486*9c5db199SXin Li        else:
487*9c5db199SXin Li            battery_regex_match = self.send_command_get_output(
488*9c5db199SXin Li                    'battery', regex_str_list)
489*9c5db199SXin Li
490*9c5db199SXin Li        for i in range(len(battery_params)):
491*9c5db199SXin Li            if battery_params[i] == 'Charging':
492*9c5db199SXin Li                self.BATTERY_INFO[
493*9c5db199SXin Li                        battery_params[i]] = battery_regex_match[i][1]
494*9c5db199SXin Li            else:
495*9c5db199SXin Li                self.BATTERY_INFO[battery_params[i]] = int(
496*9c5db199SXin Li                        battery_regex_match[i][1])
497*9c5db199SXin Li        logging.debug('Battery info: %s', self.BATTERY_INFO)
498*9c5db199SXin Li
499*9c5db199SXin Li    def get_battery_desired_voltage(self, print_result=True):
500*9c5db199SXin Li        """Get battery desired voltage value."""
501*9c5db199SXin Li        if not self.BATTERY_INFO:
502*9c5db199SXin Li            self.update_battery_info()
503*9c5db199SXin Li        if print_result:
504*9c5db199SXin Li            logging.info('Battery desired voltage = %d mV',
505*9c5db199SXin Li                         self.BATTERY_INFO['V-desired'])
506*9c5db199SXin Li        return self.BATTERY_INFO['V-desired']
507*9c5db199SXin Li
508*9c5db199SXin Li    def get_battery_desired_current(self, print_result=True):
509*9c5db199SXin Li        """Get battery desired current value."""
510*9c5db199SXin Li        if not self.BATTERY_INFO:
511*9c5db199SXin Li            self.update_battery_info()
512*9c5db199SXin Li        if print_result:
513*9c5db199SXin Li            logging.info('Battery desired current = %d mA',
514*9c5db199SXin Li                         self.BATTERY_INFO['I-desired'])
515*9c5db199SXin Li        return self.BATTERY_INFO['I-desired']
516*9c5db199SXin Li
517*9c5db199SXin Li    def get_battery_actual_voltage(self, print_result=True):
518*9c5db199SXin Li        """Get the actual voltage from charger to battery."""
519*9c5db199SXin Li        if not self.BATTERY_INFO:
520*9c5db199SXin Li            self.update_battery_info()
521*9c5db199SXin Li        if print_result:
522*9c5db199SXin Li            logging.info('Battery actual voltage = %d mV',
523*9c5db199SXin Li                         self.BATTERY_INFO['V'])
524*9c5db199SXin Li        return self.BATTERY_INFO['V']
525*9c5db199SXin Li
526*9c5db199SXin Li    def get_battery_actual_current(self, print_result=True):
527*9c5db199SXin Li        """Get the actual current from charger to battery."""
528*9c5db199SXin Li        if not self.BATTERY_INFO:
529*9c5db199SXin Li            self.update_battery_info()
530*9c5db199SXin Li        if print_result:
531*9c5db199SXin Li            logging.info('Battery actual current = %d mA',
532*9c5db199SXin Li                         self.BATTERY_INFO['I'])
533*9c5db199SXin Li        return self.BATTERY_INFO['I']
534*9c5db199SXin Li
535*9c5db199SXin Li    def get_battery_remaining(self, print_result=True):
536*9c5db199SXin Li        """Get battery charge remaining in mAh."""
537*9c5db199SXin Li        if not self.BATTERY_INFO:
538*9c5db199SXin Li            self.update_battery_info()
539*9c5db199SXin Li        if print_result:
540*9c5db199SXin Li            logging.info("Battery charge remaining = %d mAh",
541*9c5db199SXin Li                         self.BATTERY_INFO['Remaining'])
542*9c5db199SXin Li        return self.BATTERY_INFO['Remaining']
543*9c5db199SXin Li
544*9c5db199SXin Li    def get_battery_charging_allowed(self, print_result=True):
545*9c5db199SXin Li        """Get the battery charging state.
546*9c5db199SXin Li
547*9c5db199SXin Li        Returns True if charging is allowed.
548*9c5db199SXin Li        """
549*9c5db199SXin Li        if not self.BATTERY_INFO:
550*9c5db199SXin Li            self.update_battery_info()
551*9c5db199SXin Li        if print_result:
552*9c5db199SXin Li            logging.info("Battery charging = %s",
553*9c5db199SXin Li                         self.BATTERY_INFO['Charging'])
554*9c5db199SXin Li        if self.BATTERY_INFO['Charging'] == 'Allowed':
555*9c5db199SXin Li            return True
556*9c5db199SXin Li        return False
557*9c5db199SXin Li
558*9c5db199SXin Li
559*9c5db199SXin Liclass ChromeUSBPD(ChromeEC):
560*9c5db199SXin Li    """Manages control of a Chrome USBPD.
561*9c5db199SXin Li
562*9c5db199SXin Li    We control the Chrome EC via the UART of a Servo board. Chrome USBPD
563*9c5db199SXin Li    provides many interfaces to set and get its behavior via console commands.
564*9c5db199SXin Li    This class is to abstract these interfaces.
565*9c5db199SXin Li    """
566*9c5db199SXin Li
567*9c5db199SXin Li    def __init__(self, servo):
568*9c5db199SXin Li        super(ChromeUSBPD, self).__init__(servo, "usbpd_uart")
569