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