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, logging, re, time 6 7from autotest_lib.client.common_lib import error 8 9# en-US key matrix (from "kb membrane pin matrix.pdf") 10KEYMATRIX = {'`': (3, 1), '1': (6, 1), '2': (6, 4), '3': (6, 2), '4': (6, 3), 11 '5': (3, 3), '6': (3, 6), '7': (6, 6), '8': (6, 5), '9': (6, 9), 12 '0': (6, 8), '-': (3, 8), '=': (0, 8), 'q': (7, 1), 'w': (7, 4), 13 'e': (7, 2), 'r': (7, 3), 't': (2, 3), 'y': (2, 6), 'u': (7, 6), 14 'i': (7, 5), 'o': (7, 9), 'p': (7, 8), '[': (2, 8), ']': (2, 5), 15 '\\': (3, 11), 'a': (4, 1), 's': (4, 4), 'd': (4, 2), 'f': (4, 3), 16 'g': (1, 3), 'h': (1, 6), 'j': (4, 6), 'k': (4, 5), 'l': (4, 9), 17 ';': (4, 8), '\'': (1, 8), 'z': (5, 1), 'x': (5, 4), 'c': (5, 2), 18 'v': (5, 3), 'b': (0, 3), 'n': (0, 6), 'm': (5, 6), ',': (5, 5), 19 '.': (5, 9), '/': (5, 8), ' ': (5, 11), '<right>': (6, 12), 20 '<alt_r>': (0, 10), '<down>': (6, 11), '<tab>': (2, 1), 21 '<f10>': (0, 4), '<shift_r>': (7, 7), '<ctrl_r>': (4, 0), 22 '<esc>': (1, 1), '<backspace>': (1, 11), '<f2>': (3, 2), 23 '<alt_l>': (6, 10), '<ctrl_l>': (2, 0), '<f1>': (0, 2), 24 '<search>': (0, 1), '<f3>': (2, 2), '<f4>': (1, 2), '<f5>': (3, 4), 25 '<f6>': (2, 4), '<f7>': (1, 4), '<f8>': (2, 9), '<f9>': (1, 9), 26 '<up>': (7, 11), '<shift_l>': (5, 7), '<enter>': (4, 11), 27 '<left>': (7, 12)} 28 29 30# Hostevent codes, copied from: 31# ec/include/ec_commands.h 32HOSTEVENT_LID_CLOSED = 0x00000001 33HOSTEVENT_LID_OPEN = 0x00000002 34HOSTEVENT_POWER_BUTTON = 0x00000004 35HOSTEVENT_AC_CONNECTED = 0x00000008 36HOSTEVENT_AC_DISCONNECTED = 0x00000010 37HOSTEVENT_BATTERY_LOW = 0x00000020 38HOSTEVENT_BATTERY_CRITICAL = 0x00000040 39HOSTEVENT_BATTERY = 0x00000080 40HOSTEVENT_THERMAL_THRESHOLD = 0x00000100 41HOSTEVENT_THERMAL_OVERLOAD = 0x00000200 42HOSTEVENT_THERMAL = 0x00000400 43HOSTEVENT_USB_CHARGER = 0x00000800 44HOSTEVENT_KEY_PRESSED = 0x00001000 45HOSTEVENT_INTERFACE_READY = 0x00002000 46# Keyboard recovery combo has been pressed 47HOSTEVENT_KEYBOARD_RECOVERY = 0x00004000 48# Shutdown due to thermal overload 49HOSTEVENT_THERMAL_SHUTDOWN = 0x00008000 50# Shutdown due to battery level too low 51HOSTEVENT_BATTERY_SHUTDOWN = 0x00010000 52HOSTEVENT_INVALID = 0x80000000 53 54 55class ChromeEC(object): 56 """Manages control of a Chrome EC. 57 58 We control the Chrome EC via the UART of a Servo board. Chrome EC 59 provides many interfaces to set and get its behavior via console commands. 60 This class is to abstract these interfaces. 61 """ 62 63 def __init__(self, servo): 64 """Initialize and keep the servo object. 65 66 Args: 67 servo: A Servo object. 68 """ 69 self._servo = servo 70 self._cached_uart_regexp = None 71 72 73 def set_uart_regexp(self, regexp): 74 if self._cached_uart_regexp == regexp: 75 return 76 self._cached_uart_regexp = regexp 77 self._servo.set('ec_uart_regexp', regexp) 78 79 80 def send_command(self, commands): 81 """Send command through UART. 82 83 This function opens UART pty when called, and then command is sent 84 through UART. 85 86 Args: 87 commands: The commands to send, either a list or a string. 88 """ 89 self.set_uart_regexp('None') 90 if isinstance(commands, list): 91 try: 92 self._servo.set_nocheck('ec_uart_multicmd', ';'.join(commands)) 93 except error.TestFail as e: 94 if 'No control named' in str(e): 95 logging.warning( 96 'The servod is too old that ec_uart_multicmd ' 97 'not supported. Use ec_uart_cmd instead.') 98 for command in commands: 99 self._servo.set_nocheck('ec_uart_cmd', command) 100 else: 101 raise 102 else: 103 self._servo.set_nocheck('ec_uart_cmd', commands) 104 105 106 def send_command_get_output(self, command, regexp_list): 107 """Send command through UART and wait for response. 108 109 This function waits for response message matching regular expressions. 110 111 Args: 112 command: The command sent. 113 regexp_list: List of regular expressions used to match response 114 message. Note, list must be ordered. 115 116 Returns: 117 List of tuples, each of which contains the entire matched string and 118 all the subgroups of the match. None if not matched. 119 For example: 120 response of the given command: 121 High temp: 37.2 122 Low temp: 36.4 123 regexp_list: 124 ['High temp: (\d+)\.(\d+)', 'Low temp: (\d+)\.(\d+)'] 125 returns: 126 [('High temp: 37.2', '37', '2'), ('Low temp: 36.4', '36', '4')] 127 128 Raises: 129 error.TestError: An error when the given regexp_list is not valid. 130 """ 131 if not isinstance(regexp_list, list): 132 raise error.TestError('Arugment regexp_list is not a list: %s' % 133 str(regexp_list)) 134 135 self.set_uart_regexp(str(regexp_list)) 136 self._servo.set_nocheck('ec_uart_cmd', command) 137 return ast.literal_eval(self._servo.get('ec_uart_cmd')) 138 139 140 def key_down(self, keyname): 141 """Simulate pressing a key. 142 143 Args: 144 keyname: Key name, one of the keys of KEYMATRIX. 145 """ 146 self.send_command('kbpress %d %d 1' % 147 (KEYMATRIX[keyname][1], KEYMATRIX[keyname][0])) 148 149 150 def key_up(self, keyname): 151 """Simulate releasing a key. 152 153 Args: 154 keyname: Key name, one of the keys of KEYMATRIX. 155 """ 156 self.send_command('kbpress %d %d 0' % 157 (KEYMATRIX[keyname][1], KEYMATRIX[keyname][0])) 158 159 160 def key_press(self, keyname): 161 """Press and then release a key. 162 163 Args: 164 keyname: Key name, one of the keys of KEYMATRIX. 165 """ 166 self.send_command([ 167 'kbpress %d %d 1' % 168 (KEYMATRIX[keyname][1], KEYMATRIX[keyname][0]), 169 'kbpress %d %d 0' % 170 (KEYMATRIX[keyname][1], KEYMATRIX[keyname][0]), 171 ]) 172 173 174 def send_key_string_raw(self, string): 175 """Send key strokes consisting of only characters. 176 177 Args: 178 string: Raw string. 179 """ 180 for c in string: 181 self.key_press(c) 182 183 184 def send_key_string(self, string): 185 """Send key strokes including special keys. 186 187 Args: 188 string: Character string including special keys. An example 189 is "this is an<tab>example<enter>". 190 """ 191 for m in re.finditer("(<[^>]+>)|([^<>]+)", string): 192 sp, raw = m.groups() 193 if raw is not None: 194 self.send_key_string_raw(raw) 195 else: 196 self.key_press(sp) 197 198 199 def reboot(self, flags=''): 200 """Reboot EC with given flags. 201 202 Args: 203 flags: Optional, a space-separated string of flags passed to the 204 reboot command, including: 205 default: EC soft reboot; 206 'hard': EC hard/cold reboot; 207 'ap-off': Leave AP off after EC reboot (by default, EC turns 208 AP on after reboot if lid is open). 209 210 Raises: 211 error.TestError: If the string of flags is invalid. 212 """ 213 for flag in flags.split(): 214 if flag not in ('hard', 'ap-off'): 215 raise error.TestError( 216 'The flag %s of EC reboot command is invalid.' % flag) 217 self.send_command("reboot %s" % flags) 218 219 220 def set_flash_write_protect(self, enable): 221 """Set the software write protect of EC flash. 222 223 Args: 224 enable: True to enable write protect, False to disable. 225 """ 226 if enable: 227 self.send_command("flashwp enable") 228 else: 229 self.send_command("flashwp disable") 230 231 232 def set_hostevent(self, codes): 233 """Set the EC hostevent codes. 234 235 Args: 236 codes: Hostevent codes, HOSTEVENT_* 237 """ 238 self.send_command("hostevent set %#x" % codes) 239 # Allow enough time for EC to process input and set flag. 240 # See chromium:371631 for details. 241 # FIXME: Stop importing time module if this hack becomes obsolete. 242 time.sleep(1) 243