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 5from threading import Timer 6import logging 7import re 8import time 9 10from autotest_lib.client.common_lib import error 11from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 12 13 14def delayed(seconds): # pylint:disable=missing-docstring 15 def decorator(f): # pylint:disable=missing-docstring 16 def wrapper(*args, **kargs): # pylint:disable=missing-docstring 17 t = Timer(seconds, f, args, kargs) 18 t.start() 19 return wrapper 20 return decorator 21 22 23class firmware_ECLidSwitch(FirmwareTest): 24 """ 25 Servo based EC lid switch test. 26 """ 27 version = 1 28 29 # Delay between closing and opening the lid 30 LID_DELAY = 1 31 32 # Delay to allow FAFT client receive command 33 RPC_DELAY = 2 34 35 # Delay between shutdown and wakeup by lid switch 36 WAKE_DELAY = 10 37 38 # Number of tries when checking power state 39 POWER_STATE_CHECK_TRIES = 50 40 41 # Delay between checking power state 42 POWER_STATE_CHECK_DELAY = 0.5 43 44 def initialize(self, host, cmdline_args): 45 super(firmware_ECLidSwitch, self).initialize(host, cmdline_args) 46 # Only run in normal mode 47 self.switcher.setup_mode('normal') 48 49 def cleanup(self): 50 self.faft_client.system.run_shell_command_get_status( 51 "rm -rf /tmp/power_manager") 52 53 return super().cleanup() 54 55 def _open_lid(self): 56 """Open lid by servo.""" 57 self.servo.set('lid_open', 'yes') 58 59 def _close_lid(self): 60 """Close lid by servo.""" 61 self.servo.set('lid_open', 'no') 62 63 @delayed(RPC_DELAY) 64 def delayed_open_lid(self): 65 """Delay by RPC_DELAY and then open lid by servo.""" 66 self._open_lid() 67 68 @delayed(RPC_DELAY) 69 def delayed_close_lid(self): 70 """Delay by RPC_DELAY and then close lid by servo.""" 71 self._close_lid() 72 73 def _wake_by_lid_switch(self): 74 """Wake DUT with lid switch.""" 75 self._close_lid() 76 time.sleep(self.LID_DELAY) 77 self._open_lid() 78 79 def delayed_wake(self): 80 """ 81 Wait for WAKE_DELAY, and then wake DUT with lid switch. 82 """ 83 time.sleep(self.WAKE_DELAY) 84 self._wake_by_lid_switch() 85 86 def immediate_wake(self): 87 """Wake DUT with lid switch.""" 88 self._wake_by_lid_switch() 89 90 def shutdown_cmd(self): 91 """Shut down the DUT but don't wait for ping failures.""" 92 self.run_shutdown_cmd(wait_for_offline=False) 93 94 def shutdown_and_wake(self, shutdown_func, wake_func): 95 """Software shutdown and wake with check for power state 96 97 Args: 98 shutdown_func: Function to shut down DUT. 99 wake_func: Delayed function to wake DUT. 100 """ 101 102 # Call shutdown function to power down device 103 logging.debug('calling shutdown_func') 104 shutdown_func() 105 106 # Check device shutdown to correct power state 107 shutdown_power_states = '|'.join( 108 [self.POWER_STATE_S5, self.POWER_STATE_G3]) 109 if not self.wait_power_state(shutdown_power_states, 110 self.POWER_STATE_CHECK_TRIES, 111 self.POWER_STATE_CHECK_DELAY): 112 raise error.TestFail( 113 'The device failed to reach %s after calling shutdown function.', 114 shutdown_power_states) 115 116 # Call wake function to wake up device 117 logging.debug('calling wake_func') 118 wake_func() 119 120 # Check power state to verify device woke up to S0 121 wake_power_state = self.POWER_STATE_S0 122 if not self.wait_power_state(wake_power_state, 123 self.POWER_STATE_CHECK_TRIES, 124 self.POWER_STATE_CHECK_DELAY): 125 raise error.TestFail( 126 'The device failed to reach %s after calling wake function.', 127 wake_power_state) 128 # Wait for the DUT to boot and respond to ssh before we move on. 129 self.switcher.wait_for_client() 130 131 def _get_keyboard_backlight(self): 132 """Get keyboard backlight brightness. 133 134 Returns: 135 Backlight brightness percentage 0~100. If it is disabled, 0 is 136 returned. 137 """ 138 cmd = 'ectool pwmgetkblight' 139 pattern_percent = re.compile( 140 'Current keyboard backlight percent: (\d*)') 141 pattern_disable = re.compile('Keyboard backlight disabled.') 142 lines = self.faft_client.system.run_shell_command_get_output(cmd) 143 for line in lines: 144 matched_percent = pattern_percent.match(line) 145 if matched_percent is not None: 146 return int(matched_percent.group(1)) 147 matched_disable = pattern_disable.match(line) 148 if matched_disable is not None: 149 return 0 150 raise error.TestError('Cannot get keyboard backlight status.') 151 152 def _set_keyboard_backlight(self, value): 153 """Set keyboard backlight brightness. 154 155 Args: 156 value: Backlight brightness percentage 0~100. 157 """ 158 cmd = 'ectool pwmsetkblight %d' % value 159 self.faft_client.system.run_shell_command(cmd) 160 161 def check_keycode(self): 162 """Check that lid open/close do not send power button keycode. 163 164 Returns: 165 True if no power button keycode is captured. Otherwise, False. 166 """ 167 # Don't check the keycode if we don't have a keyboard. 168 if not self.check_ec_capability(['keyboard'], suppress_warning=True): 169 return True 170 171 self._open_lid() 172 self.delayed_close_lid() 173 if self.faft_client.system.check_keys([]) < 0: 174 return False 175 self.delayed_open_lid() 176 if self.faft_client.system.check_keys([]) < 0: 177 return False 178 return True 179 180 def check_backlight(self): 181 """Check if lid open/close controls keyboard backlight as expected. 182 183 Returns: 184 True if keyboard backlight is turned off when lid close and on when 185 lid open. 186 """ 187 if not self.check_ec_capability(['kblight'], suppress_warning=True): 188 return True 189 ok = True 190 original_value = self._get_keyboard_backlight() 191 self._set_keyboard_backlight(100) 192 193 self._close_lid() 194 if self._get_keyboard_backlight() != 0: 195 logging.error("Keyboard backlight still on when lid close.") 196 ok = False 197 self._open_lid() 198 if self._get_keyboard_backlight() == 0: 199 logging.error("Keyboard backlight still off when lid open.") 200 ok = False 201 202 self._set_keyboard_backlight(original_value) 203 return ok 204 205 def check_keycode_and_backlight(self): 206 """ 207 Disable powerd to prevent DUT shutting down during test. Then check 208 if lid switch event controls keycode and backlight as we expected. 209 """ 210 ok = True 211 logging.info("Disable use_lid in powerd") 212 self.faft_client.system.run_shell_command( 213 "mkdir -p /tmp/power_manager && " 214 "echo 0 > /tmp/power_manager/use_lid && " 215 "mount --bind /tmp/power_manager /var/lib/power_manager && " 216 "restart powerd") 217 if not self.check_keycode(): 218 logging.error("check_keycode failed.") 219 ok = False 220 if not self.check_backlight(): 221 logging.error("check_backlight failed.") 222 ok = False 223 logging.info("Restarting powerd") 224 self.faft_client.system.run_shell_command( 225 'umount /var/lib/power_manager && restart powerd') 226 return ok 227 228 def run_once(self): 229 """Runs a single iteration of the test.""" 230 if not self.check_ec_capability(['lid']): 231 raise error.TestNAError("Nothing needs to be tested on this device") 232 233 logging.info("Shut down and then wake up DUT after a delay.") 234 self.shutdown_and_wake(shutdown_func=self.shutdown_cmd, 235 wake_func=self.delayed_wake) 236 237 logging.info("Shut down and then wake up DUT immediately.") 238 self.shutdown_and_wake(shutdown_func=self.shutdown_cmd, 239 wake_func=self.immediate_wake) 240 241 logging.info("Close and then open the lid when not logged in.") 242 self.shutdown_and_wake(shutdown_func=self._close_lid, 243 wake_func=self.immediate_wake) 244 245 logging.info("Check keycode and backlight.") 246 self.check_state(self.check_keycode_and_backlight) 247