Home | History | Annotate | Download | only in servo
      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 
      5 import ast, logging, re, time
      6 
      7 from autotest_lib.client.common_lib import error
      8 
      9 # en-US key matrix (from "kb membrane pin matrix.pdf")
     10 KEYMATRIX = {'`': (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
     32 HOSTEVENT_LID_CLOSED        = 0x00000001
     33 HOSTEVENT_LID_OPEN          = 0x00000002
     34 HOSTEVENT_POWER_BUTTON      = 0x00000004
     35 HOSTEVENT_AC_CONNECTED      = 0x00000008
     36 HOSTEVENT_AC_DISCONNECTED   = 0x00000010
     37 HOSTEVENT_BATTERY_LOW       = 0x00000020
     38 HOSTEVENT_BATTERY_CRITICAL  = 0x00000040
     39 HOSTEVENT_BATTERY           = 0x00000080
     40 HOSTEVENT_THERMAL_THRESHOLD = 0x00000100
     41 HOSTEVENT_THERMAL_OVERLOAD  = 0x00000200
     42 HOSTEVENT_THERMAL           = 0x00000400
     43 HOSTEVENT_USB_CHARGER       = 0x00000800
     44 HOSTEVENT_KEY_PRESSED       = 0x00001000
     45 HOSTEVENT_INTERFACE_READY   = 0x00002000
     46 # Keyboard recovery combo has been pressed
     47 HOSTEVENT_KEYBOARD_RECOVERY = 0x00004000
     48 # Shutdown due to thermal overload
     49 HOSTEVENT_THERMAL_SHUTDOWN  = 0x00008000
     50 # Shutdown due to battery level too low
     51 HOSTEVENT_BATTERY_SHUTDOWN  = 0x00010000
     52 HOSTEVENT_INVALID           = 0x80000000
     53 
     54 
     55 class 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