1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import ast 6import logging 7import re 8import time 9 10from autotest_lib.client.common_lib import error 11from autotest_lib.client.cros import ec 12from autotest_lib.server.cros.servo import servo 13 14# Hostevent codes, copied from: 15# ec/include/ec_commands.h 16HOSTEVENT_LID_CLOSED = 0x00000001 17HOSTEVENT_LID_OPEN = 0x00000002 18HOSTEVENT_POWER_BUTTON = 0x00000004 19HOSTEVENT_AC_CONNECTED = 0x00000008 20HOSTEVENT_AC_DISCONNECTED = 0x00000010 21HOSTEVENT_BATTERY_LOW = 0x00000020 22HOSTEVENT_BATTERY_CRITICAL = 0x00000040 23HOSTEVENT_BATTERY = 0x00000080 24HOSTEVENT_THERMAL_THRESHOLD = 0x00000100 25HOSTEVENT_THERMAL_OVERLOAD = 0x00000200 26HOSTEVENT_THERMAL = 0x00000400 27HOSTEVENT_USB_CHARGER = 0x00000800 28HOSTEVENT_KEY_PRESSED = 0x00001000 29HOSTEVENT_INTERFACE_READY = 0x00002000 30# Keyboard recovery combo has been pressed 31HOSTEVENT_KEYBOARD_RECOVERY = 0x00004000 32# Shutdown due to thermal overload 33HOSTEVENT_THERMAL_SHUTDOWN = 0x00008000 34# Shutdown due to battery level too low 35HOSTEVENT_BATTERY_SHUTDOWN = 0x00010000 36HOSTEVENT_INVALID = 0x80000000 37 38# Time to wait after sending keypress commands. 39KEYPRESS_RECOVERY_TIME = 0.5 40 41# Wakemask types, copied from: 42# ec/include/ec_commands.h 43EC_HOST_EVENT_MAIN = 0 44EC_HOST_EVENT_B = 1 45EC_HOST_EVENT_SCI_MASK = 2 46EC_HOST_EVENT_SMI_MASK = 3 47EC_HOST_EVENT_ALWAYS_REPORT_MASK = 4 48EC_HOST_EVENT_ACTIVE_WAKE_MASK = 5 49EC_HOST_EVENT_LAZY_WAKE_MASK_S0IX = 6 50EC_HOST_EVENT_LAZY_WAKE_MASK_S3 = 7 51EC_HOST_EVENT_LAZY_WAKE_MASK_S5 = 8 52 53 54class ChromeConsole(object): 55 """Manages control of a Chrome console. 56 57 We control the Chrome console via the UART of a Servo board. Chrome console 58 provides many interfaces to set and get its behavior via console commands. 59 This class is to abstract these interfaces. 60 """ 61 62 CMD = "_cmd" 63 REGEXP = "_regexp" 64 MULTICMD = "_multicmd" 65 66 # EC Features 67 # Quoted from 'enum ec_feature_code' in platform/ec/include/ec_commands.h. 68 EC_FEATURE = { 69 'EC_FEATURE_LIMITED' : 0, 70 'EC_FEATURE_FLASH' : 1, 71 'EC_FEATURE_PWM_FAN' : 2, 72 'EC_FEATURE_PWM_KEYB' : 3, 73 'EC_FEATURE_LIGHTBAR' : 4, 74 'EC_FEATURE_LED' : 5, 75 'EC_FEATURE_MOTION_SENSE' : 6, 76 'EC_FEATURE_KEYB' : 7, 77 'EC_FEATURE_PSTORE' : 8, 78 'EC_FEATURE_PORT80' : 9, 79 'EC_FEATURE_THERMAL' : 10, 80 'EC_FEATURE_BKLIGHT_SWITCH' : 11, 81 'EC_FEATURE_WIFI_SWITCH' : 12, 82 'EC_FEATURE_HOST_EVENTS' : 13, 83 'EC_FEATURE_GPIO' : 14, 84 'EC_FEATURE_I2C' : 15, 85 'EC_FEATURE_CHARGER' : 16, 86 'EC_FEATURE_BATTERY' : 17, 87 'EC_FEATURE_SMART_BATTERY' : 18, 88 'EC_FEATURE_HANG_DETECT' : 19, 89 'EC_FEATURE_PMU' : 20, 90 'EC_FEATURE_SUB_MCU' : 21, 91 'EC_FEATURE_USB_PD' : 22, 92 'EC_FEATURE_USB_MUX' : 23, 93 'EC_FEATURE_MOTION_SENSE_FIFO' : 24, 94 'EC_FEATURE_VSTORE' : 25, 95 'EC_FEATURE_USBC_SS_MUX_VIRTUAL' : 26, 96 'EC_FEATURE_RTC' : 27, 97 'EC_FEATURE_FINGERPRINT' : 28, 98 'EC_FEATURE_TOUCHPAD' : 29, 99 'EC_FEATURE_RWSIG' : 30, 100 'EC_FEATURE_DEVICE_EVENT' : 31, 101 'EC_FEATURE_UNIFIED_WAKE_MASKS' : 32, 102 'EC_FEATURE_HOST_EVENT64' : 33, 103 'EC_FEATURE_EXEC_IN_RAM' : 34, 104 'EC_FEATURE_CEC' : 35, 105 'EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS' : 36, 106 'EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS' : 37, 107 'EC_FEATURE_EFS2' : 38, 108 'EC_FEATURE_SCP' : 39, 109 'EC_FEATURE_ISH' : 40, 110 } 111 112 def __init__(self, servo, name): 113 """Initialize and keep the servo object. 114 115 Args: 116 servo: A Servo object. 117 name: The console name. 118 """ 119 self.name = name 120 self.uart_cmd = self.name + self.CMD 121 self.uart_regexp = self.name + self.REGEXP 122 self.uart_multicmd = self.name + self.MULTICMD 123 124 self._servo = servo 125 126 def __repr__(self): 127 """Return a string representation: <ChromeConsole 'foo_uart'>""" 128 return "<%s %r>" % (self.__class__.__name__, self.name) 129 130 def set_uart_regexp(self, regexp): 131 self._servo.set(self.uart_regexp, regexp) 132 133 def clear_uart_regex(self): 134 """Clear uart_regexp""" 135 self.set_uart_regexp('None') 136 137 def send_command(self, commands): 138 """Send command through UART. 139 140 This function opens UART pty when called, and then command is sent 141 through UART. 142 143 Args: 144 commands: The commands to send, either a list or a string. 145 """ 146 self.clear_uart_regex() 147 if isinstance(commands, list): 148 try: 149 self._servo.set_nocheck(self.uart_multicmd, ';'.join(commands)) 150 except servo.ControlUnavailableError: 151 logging.warning('The servod is too old that uart_multicmd not ' 152 'supported. Use uart_cmd instead.') 153 for command in commands: 154 self._servo.set_nocheck(self.uart_cmd, command) 155 else: 156 self._servo.set_nocheck(self.uart_cmd, commands) 157 self.clear_uart_regex() 158 159 def has_command(self, command): 160 """Check whether EC console supports |command|. 161 162 Args: 163 command: Command to look for. 164 165 Returns: 166 True: If the |command| exist on the EC image of the device. 167 False: If the |command| does not exist on the EC image of the device. 168 """ 169 result = None 170 try: 171 # Throws error.TestFail (on timeout) if it cannot find a line with 172 # 'command' in the output. Thus return False in that case. 173 result = self.send_command_get_output('help', [command]) 174 except error.TestFail: 175 return False 176 return result is not None 177 178 def send_command_get_output(self, command, regexp_list): 179 """Send command through UART and wait for response. 180 181 This function waits for response message matching regular expressions. 182 183 Args: 184 command: The command sent. 185 regexp_list: List of regular expressions used to match response 186 message. Note, list must be ordered. 187 188 Returns: 189 List of tuples, each of which contains the entire matched string and 190 all the subgroups of the match. None if not matched. 191 For example: 192 response of the given command: 193 High temp: 37.2 194 Low temp: 36.4 195 regexp_list: 196 ['High temp: (\d+)\.(\d+)', 'Low temp: (\d+)\.(\d+)'] 197 returns: 198 [('High temp: 37.2', '37', '2'), ('Low temp: 36.4', '36', '4')] 199 200 Raises: 201 error.TestError: An error when the given regexp_list is not valid. 202 """ 203 if not isinstance(regexp_list, list): 204 raise error.TestError('Arugment regexp_list is not a list: %s' % 205 str(regexp_list)) 206 207 self.set_uart_regexp(str(regexp_list)) 208 self._servo.set_nocheck(self.uart_cmd, command) 209 rv = ast.literal_eval(self._servo.get(self.uart_cmd)) 210 self.clear_uart_regex() 211 212 return rv 213 214 215 def is_dfp(self, port=0): 216 """This function checks if EC is DFP 217 218 Args: 219 port: Port of EC to check 220 221 Returns: 222 True: if EC is DFP 223 False: if EC is not DFP 224 """ 225 is_dfp = None 226 try: 227 # After reboot, EC should be UFP, but workaround in servod 228 # can perform PD Data Swap in workaroud so check that 229 ret = self.send_command_get_output("pd %d state" % port, ["DFP"]) 230 is_dfp = True 231 except Exception as e: 232 # EC is UFP 233 is_dfp = False 234 235 return is_dfp 236 237 238class ChromeEC(ChromeConsole): 239 """Manages control of a Chrome EC. 240 241 We control the Chrome EC via the UART of a Servo board. Chrome EC 242 provides many interfaces to set and get its behavior via console commands. 243 This class is to abstract these interfaces. 244 """ 245 246 def __init__(self, servo, name="ec_uart"): 247 super(ChromeEC, self).__init__(servo, name) 248 249 def __repr__(self): 250 """Return a string representation of the object: <ChromeEC 'ec_uart'>""" 251 return "<%s %r>" % (self.__class__.__name__, self.name) 252 253 def key_down(self, keyname): 254 """Simulate pressing a key. 255 256 Args: 257 keyname: Key name, one of the keys of KEYMATRIX. 258 """ 259 self.send_command('kbpress %d %d 1' % 260 (ec.KEYMATRIX[keyname][1], ec.KEYMATRIX[keyname][0])) 261 262 263 def key_up(self, keyname): 264 """Simulate releasing a key. 265 266 Args: 267 keyname: Key name, one of the keys of KEYMATRIX. 268 """ 269 self.send_command('kbpress %d %d 0' % 270 (ec.KEYMATRIX[keyname][1], ec.KEYMATRIX[keyname][0])) 271 272 273 def key_press(self, keyname): 274 """Press and then release a key. 275 276 Args: 277 keyname: Key name, one of the keys of KEYMATRIX. 278 """ 279 self.send_command([ 280 'kbpress %d %d 1' % 281 (ec.KEYMATRIX[keyname][1], ec.KEYMATRIX[keyname][0]), 282 'kbpress %d %d 0' % 283 (ec.KEYMATRIX[keyname][1], ec.KEYMATRIX[keyname][0]), 284 ]) 285 # Don't spam the EC console as fast as we can; leave some recovery time 286 # in between commands. 287 time.sleep(KEYPRESS_RECOVERY_TIME) 288 289 290 def send_key_string_raw(self, string): 291 """Send key strokes consisting of only characters. 292 293 Args: 294 string: Raw string. 295 """ 296 for c in string: 297 self.key_press(c) 298 299 300 def send_key_string(self, string): 301 """Send key strokes including special keys. 302 303 Args: 304 string: Character string including special keys. An example 305 is "this is an<tab>example<enter>". 306 """ 307 for m in re.finditer("(<[^>]+>)|([^<>]+)", string): 308 sp, raw = m.groups() 309 if raw is not None: 310 self.send_key_string_raw(raw) 311 else: 312 self.key_press(sp) 313 314 315 def reboot(self, flags=''): 316 """Reboot EC with given flags. 317 318 Args: 319 flags: Optional, a space-separated string of flags passed to the 320 reboot command, including: 321 default: EC soft reboot; 322 'hard': EC hard/cold reboot; 323 'ap-off': Leave AP off after EC reboot (by default, EC turns 324 AP on after reboot if lid is open). 325 326 Raises: 327 error.TestError: If the string of flags is invalid. 328 """ 329 for flag in flags.split(): 330 if flag not in ('hard', 'ap-off'): 331 raise error.TestError( 332 'The flag %s of EC reboot command is invalid.' % flag) 333 self.send_command("reboot %s" % flags) 334 335 336 def set_flash_write_protect(self, enable): 337 """Set the software write protect of EC flash. 338 339 Args: 340 enable: True to enable write protect, False to disable. 341 """ 342 if enable: 343 self.send_command("flashwp enable") 344 else: 345 self.send_command("flashwp disable") 346 347 348 def set_hostevent(self, codes): 349 """Set the EC hostevent codes. 350 351 Args: 352 codes: Hostevent codes, HOSTEVENT_* 353 """ 354 self.send_command("hostevent set %#x" % codes) 355 # Allow enough time for EC to process input and set flag. 356 # See chromium:371631 for details. 357 # FIXME: Stop importing time module if this hack becomes obsolete. 358 time.sleep(1) 359 360 361 def enable_console_channel(self, channel): 362 """Find console channel mask and enable that channel only 363 364 @param channel: console channel name 365 """ 366 # The 'chan' command returns a list of console channels, 367 # their channel masks and channel numbers 368 regexp = r'(\d+)\s+([\w]+)\s+\*?\s+{0}'.format(channel) 369 l = self.send_command_get_output('chan', [regexp]) 370 # Use channel mask and append the 0x for proper hex input value 371 cmd = 'chan 0x' + l[0][2] 372 # Set console to only output the desired channel 373 self.send_command(cmd) 374 375 376 def get_version(self): 377 """Get version information from the Chrome EC console. 378 Additionally, can be used to verify if EC console is available. 379 """ 380 self.send_command("chan 0") 381 expected_output = ["Chip:\s+([^\r\n]*)\r\n", 382 "RO:\s+([^\r\n]*)\r\n", 383 "RW_?[AB]?:\s+([^\r\n]*)\r\n", 384 "Build:\s+([^\r\n]*)\r\n"] 385 l = self.send_command_get_output("version", expected_output) 386 self.send_command("chan 0xffffffff") 387 return l 388 389 def check_ro_rw(self, img_exp): 390 """Tell if the current EC image matches the given input, 'RW' or 'RO. 391 392 Args: 393 img_exp: Expected image type. It should be either 'RW' or 'RO'. 394 Return: 395 True if the active EC image matches to 'img_exp'. 396 False otherwise. 397 Raise: 398 TestError if img_exp is neither 'RW' nor 'RO'. 399 """ 400 if img_exp not in ['RW', 'RO']: 401 raise error.TestError('Arugment img_exp is neither RW nor RO') 402 403 result = self.send_command_get_output('sysinfo', [r'Copy:\s*(RO|RW)']) 404 return result[0][1] == img_exp 405 406 def check_feature(self, feature): 407 """Return true if EC supports the given feature 408 409 Args: 410 feature: feature name as a string as in self.EC_FEATURE. 411 412 Returns: 413 True if 'feature' is in EC's feature set. 414 False otherwise 415 """ 416 feat_id = self.EC_FEATURE[feature] 417 if feat_id < 32: 418 feat_start = 0 419 else: 420 feat_start = 32 421 422 regexp = r'%d-%d:\s*(0x[0-9a-fA-F]{8})' % (feat_start, 423 feat_start + 31) 424 425 try: 426 result = self.send_command_get_output('feat', [regexp]) 427 except servo.ResponsiveConsoleError as e: 428 logging.warn("feat command is not available: %s", str(e)) 429 return False 430 431 feat_bitmap = int(result[0][1], 16) 432 433 return ((1 << (feat_id - feat_start)) & feat_bitmap) != 0 434 435 436class ChromeUSBPD(ChromeEC): 437 """Manages control of a Chrome USBPD. 438 439 We control the Chrome EC via the UART of a Servo board. Chrome USBPD 440 provides many interfaces to set and get its behavior via console commands. 441 This class is to abstract these interfaces. 442 """ 443 444 def __init__(self, servo): 445 super(ChromeUSBPD, self).__init__(servo, "usbpd_uart") 446