# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import ast, logging, re, time from autotest_lib.client.common_lib import error # en-US key matrix (from "kb membrane pin matrix.pdf") KEYMATRIX = {'`': (3, 1), '1': (6, 1), '2': (6, 4), '3': (6, 2), '4': (6, 3), '5': (3, 3), '6': (3, 6), '7': (6, 6), '8': (6, 5), '9': (6, 9), '0': (6, 8), '-': (3, 8), '=': (0, 8), 'q': (7, 1), 'w': (7, 4), 'e': (7, 2), 'r': (7, 3), 't': (2, 3), 'y': (2, 6), 'u': (7, 6), 'i': (7, 5), 'o': (7, 9), 'p': (7, 8), '[': (2, 8), ']': (2, 5), '\\': (3, 11), 'a': (4, 1), 's': (4, 4), 'd': (4, 2), 'f': (4, 3), 'g': (1, 3), 'h': (1, 6), 'j': (4, 6), 'k': (4, 5), 'l': (4, 9), ';': (4, 8), '\'': (1, 8), 'z': (5, 1), 'x': (5, 4), 'c': (5, 2), 'v': (5, 3), 'b': (0, 3), 'n': (0, 6), 'm': (5, 6), ',': (5, 5), '.': (5, 9), '/': (5, 8), ' ': (5, 11), '': (6, 12), '': (0, 10), '': (6, 11), '': (2, 1), '': (0, 4), '': (7, 7), '': (4, 0), '': (1, 1), '': (1, 11), '': (3, 2), '': (6, 10), '': (2, 0), '': (0, 2), '': (0, 1), '': (2, 2), '': (1, 2), '': (3, 4), '': (2, 4), '': (1, 4), '': (2, 9), '': (1, 9), '': (7, 11), '': (5, 7), '': (4, 11), '': (7, 12)} # Hostevent codes, copied from: # ec/include/ec_commands.h HOSTEVENT_LID_CLOSED = 0x00000001 HOSTEVENT_LID_OPEN = 0x00000002 HOSTEVENT_POWER_BUTTON = 0x00000004 HOSTEVENT_AC_CONNECTED = 0x00000008 HOSTEVENT_AC_DISCONNECTED = 0x00000010 HOSTEVENT_BATTERY_LOW = 0x00000020 HOSTEVENT_BATTERY_CRITICAL = 0x00000040 HOSTEVENT_BATTERY = 0x00000080 HOSTEVENT_THERMAL_THRESHOLD = 0x00000100 HOSTEVENT_THERMAL_OVERLOAD = 0x00000200 HOSTEVENT_THERMAL = 0x00000400 HOSTEVENT_USB_CHARGER = 0x00000800 HOSTEVENT_KEY_PRESSED = 0x00001000 HOSTEVENT_INTERFACE_READY = 0x00002000 # Keyboard recovery combo has been pressed HOSTEVENT_KEYBOARD_RECOVERY = 0x00004000 # Shutdown due to thermal overload HOSTEVENT_THERMAL_SHUTDOWN = 0x00008000 # Shutdown due to battery level too low HOSTEVENT_BATTERY_SHUTDOWN = 0x00010000 HOSTEVENT_INVALID = 0x80000000 class ChromeEC(object): """Manages control of a Chrome EC. We control the Chrome EC via the UART of a Servo board. Chrome EC provides many interfaces to set and get its behavior via console commands. This class is to abstract these interfaces. """ def __init__(self, servo): """Initialize and keep the servo object. Args: servo: A Servo object. """ self._servo = servo self._cached_uart_regexp = None def set_uart_regexp(self, regexp): if self._cached_uart_regexp == regexp: return self._cached_uart_regexp = regexp self._servo.set('ec_uart_regexp', regexp) def send_command(self, commands): """Send command through UART. This function opens UART pty when called, and then command is sent through UART. Args: commands: The commands to send, either a list or a string. """ self.set_uart_regexp('None') if isinstance(commands, list): try: self._servo.set_nocheck('ec_uart_multicmd', ';'.join(commands)) except error.TestFail as e: if 'No control named' in str(e): logging.warning( 'The servod is too old that ec_uart_multicmd ' 'not supported. Use ec_uart_cmd instead.') for command in commands: self._servo.set_nocheck('ec_uart_cmd', command) else: raise else: self._servo.set_nocheck('ec_uart_cmd', commands) def send_command_get_output(self, command, regexp_list): """Send command through UART and wait for response. This function waits for response message matching regular expressions. Args: command: The command sent. regexp_list: List of regular expressions used to match response message. Note, list must be ordered. Returns: List of tuples, each of which contains the entire matched string and all the subgroups of the match. None if not matched. For example: response of the given command: High temp: 37.2 Low temp: 36.4 regexp_list: ['High temp: (\d+)\.(\d+)', 'Low temp: (\d+)\.(\d+)'] returns: [('High temp: 37.2', '37', '2'), ('Low temp: 36.4', '36', '4')] Raises: error.TestError: An error when the given regexp_list is not valid. """ if not isinstance(regexp_list, list): raise error.TestError('Arugment regexp_list is not a list: %s' % str(regexp_list)) self.set_uart_regexp(str(regexp_list)) self._servo.set_nocheck('ec_uart_cmd', command) return ast.literal_eval(self._servo.get('ec_uart_cmd')) def key_down(self, keyname): """Simulate pressing a key. Args: keyname: Key name, one of the keys of KEYMATRIX. """ self.send_command('kbpress %d %d 1' % (KEYMATRIX[keyname][1], KEYMATRIX[keyname][0])) def key_up(self, keyname): """Simulate releasing a key. Args: keyname: Key name, one of the keys of KEYMATRIX. """ self.send_command('kbpress %d %d 0' % (KEYMATRIX[keyname][1], KEYMATRIX[keyname][0])) def key_press(self, keyname): """Press and then release a key. Args: keyname: Key name, one of the keys of KEYMATRIX. """ self.send_command([ 'kbpress %d %d 1' % (KEYMATRIX[keyname][1], KEYMATRIX[keyname][0]), 'kbpress %d %d 0' % (KEYMATRIX[keyname][1], KEYMATRIX[keyname][0]), ]) def send_key_string_raw(self, string): """Send key strokes consisting of only characters. Args: string: Raw string. """ for c in string: self.key_press(c) def send_key_string(self, string): """Send key strokes including special keys. Args: string: Character string including special keys. An example is "this is anexample". """ for m in re.finditer("(<[^>]+>)|([^<>]+)", string): sp, raw = m.groups() if raw is not None: self.send_key_string_raw(raw) else: self.key_press(sp) def reboot(self, flags=''): """Reboot EC with given flags. Args: flags: Optional, a space-separated string of flags passed to the reboot command, including: default: EC soft reboot; 'hard': EC hard/cold reboot; 'ap-off': Leave AP off after EC reboot (by default, EC turns AP on after reboot if lid is open). Raises: error.TestError: If the string of flags is invalid. """ for flag in flags.split(): if flag not in ('hard', 'ap-off'): raise error.TestError( 'The flag %s of EC reboot command is invalid.' % flag) self.send_command("reboot %s" % flags) def set_flash_write_protect(self, enable): """Set the software write protect of EC flash. Args: enable: True to enable write protect, False to disable. """ if enable: self.send_command("flashwp enable") else: self.send_command("flashwp disable") def set_hostevent(self, codes): """Set the EC hostevent codes. Args: codes: Hostevent codes, HOSTEVENT_* """ self.send_command("hostevent set %#x" % codes) # Allow enough time for EC to process input and set flag. # See chromium:371631 for details. # FIXME: Stop importing time module if this hack becomes obsolete. time.sleep(1)