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 logging 6import time 7 8from autotest_lib.client.common_lib import error 9from autotest_lib.server import autotest 10from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 11from autotest_lib.server.cros.faft.firmware_test import ConnectionError 12from autotest_lib.server.cros.servo import servo 13 14 15class firmware_ECWakeSource(FirmwareTest): 16 """ 17 Servo based EC wake source test. 18 """ 19 version = 1 20 21 # After suspending the device, wait this long before waking it again. 22 SUSPEND_WAIT_TIME_SECONDS = 5 23 # Retries allowed for reaching S0ix|S3 after suspend and S0 after wake. 24 POWER_STATE_RETRY_COUNT = 10 25 # The timeout (in seconds) to confirm the device is woken up from 26 # suspend mode. 27 RESUME_TIMEOUT = 60 28 # Delay before the USB keyboard is seen by DUT after initialization 29 USB_PRESENT_DELAY = 1 30 31 def initialize(self, host, cmdline_args): 32 super(firmware_ECWakeSource, self).initialize(host, cmdline_args) 33 # Only run in normal mode 34 self.switcher.setup_mode('normal') 35 self.has_internal_display = host.has_internal_display() 36 37 def cleanup(self): 38 # Restore the lid_open switch in case the test failed in the middle. 39 if self.check_ec_capability(['lid']): 40 self.servo.set('lid_open', 'yes') 41 super(firmware_ECWakeSource, self).cleanup() 42 43 def is_ec_console_responsive(self): 44 """Test if EC console is responsive.""" 45 try: 46 self.ec.send_command_get_output('help', ['.*>']) 47 return True 48 except servo.UnresponsiveConsoleError: 49 return False 50 51 def wake_by_lid_switch(self): 52 """Wake up the device by lid switch.""" 53 self.servo.set('lid_open', 'no') 54 time.sleep(self.LID_DELAY) 55 self.servo.set('lid_open', 'yes') 56 57 def suspend_and_wake(self, suspend_func, wake_func): 58 """Suspend and then wake up the device. 59 60 Args: 61 suspend_func: The method used to suspend the device 62 wake_func: The method used to resume the device 63 """ 64 suspend_func() 65 self.switcher.wait_for_client_offline() 66 if not self.wait_power_state(self.POWER_STATE_SUSPEND, 67 self.POWER_STATE_RETRY_COUNT): 68 raise error.TestFail('Platform failed to reach S0ix or S3 state.') 69 time.sleep(self.SUSPEND_WAIT_TIME_SECONDS) 70 wake_func() 71 if not self.wait_power_state(self.POWER_STATE_S0, 72 self.POWER_STATE_RETRY_COUNT): 73 raise error.TestFail('Platform failed to reach S0 state.') 74 self.switcher.wait_for_client(timeout=self.RESUME_TIMEOUT) 75 76 def suspend_and_dont_wake(self, suspend_func, wake_func): 77 """Suspend and then check the the device doesn't wake up. 78 79 Args: 80 suspend_func: The method used to suspend the device 81 wake_func: The method used to wake the device 82 """ 83 suspend_func() 84 self.switcher.wait_for_client_offline() 85 if not self.wait_power_state(self.POWER_STATE_SUSPEND, 86 self.POWER_STATE_RETRY_COUNT): 87 raise error.TestFail('Platform failed to reach S0ix or S3 state.') 88 time.sleep(self.SUSPEND_WAIT_TIME_SECONDS) 89 wake_func() 90 if self.wait_power_state(self.POWER_STATE_S0, 91 self.POWER_STATE_RETRY_COUNT): 92 raise error.TestFail('Platform woke up unexpectedly.') 93 else: 94 self.servo.power_normal_press() 95 if not self.wait_power_state(self.POWER_STATE_S0, 96 self.POWER_STATE_RETRY_COUNT): 97 raise error.TestFail('Platform failed to reach S0 state.') 98 self.switcher.wait_for_client(timeout=self.RESUME_TIMEOUT) 99 100 def check_boot_id(self, host, orig_boot_id, wake_method): 101 """Check current boot id matches original boot id. 102 103 Args: 104 host: test host object 105 orig_boot_id: original boot_id to compare against 106 wake_method: string indicating the method used to wake the device 107 """ 108 boot_id = host.get_boot_id() 109 if boot_id != orig_boot_id: 110 raise error.TestFail('Unexpected reboot by suspend and wake: ' + 111 wake_method) 112 113 def run_once(self, host): 114 """Runs a single iteration of the test.""" 115 if not self.check_ec_capability(): 116 raise error.TestNAError( 117 "Nothing needs to be tested on this device") 118 119 # Login as a normal user and stay there, such that closing lid triggers 120 # suspend, instead of shutdown. 121 autotest_client = autotest.Autotest(host) 122 autotest_client.run_test('desktopui_SimpleLogin', 123 exit_without_logout=True) 124 original_boot_id = host.get_boot_id() 125 126 # Test suspend and wake by power button 127 wake_src = 'power button' 128 if not self.has_internal_display: 129 # With no display connected, pressing the power button in suspend mode 130 # would lead to shutdown. 131 logging.info( 132 'The device has no internal display. ' 133 'Skip testing suspend/resume by %s.', wake_src) 134 else: 135 logging.info('Suspend and wake by %s.', wake_src) 136 self.suspend_and_wake(self.suspend, self.servo.power_normal_press) 137 self.check_boot_id(host, original_boot_id, wake_src) 138 139 # Test suspend and wake by internal key press 140 wake_src = 'internal key press' 141 if not self.check_ec_capability(['keyboard']): 142 logging.info( 143 'The device has no internal keyboard. ' 144 'Skip testing suspend/resume by %s.', wake_src) 145 elif not self.ec.has_command('ksstate'): 146 logging.info( 147 'The device does not support the ksstate command. ' 148 'Skip testing suspend/resume by %s.', wake_src) 149 else: 150 result = self.ec.send_command_get_output( 151 'ksstate', 152 ['Keyboard scan disable mask: 0x([0-9a-fA-F]{8})']) 153 kb_scan_disable_mask = int(result[0][1], 16) 154 if kb_scan_disable_mask == 0: 155 logging.info('Suspend and wake by %s.', wake_src) 156 self.suspend_and_wake(self.suspend, 157 lambda: self.ec.key_press('<enter>')) 158 else: 159 logging.info( 160 'Tablet mode enabled; suspend and check device ' 161 'does not wake by %s.', wake_src) 162 self.suspend_and_dont_wake( 163 self.suspend, lambda: self.ec.key_press('<enter>')) 164 self.check_boot_id(host, original_boot_id, wake_src) 165 166 # Test suspend and wake by USB HID key press 167 wake_src = 'USB HID key press' 168 if not self.faft_config.usb_hid_wake_enabled: 169 logging.info( 170 'Device does not support wake by USB HID. ' 171 'Skip suspend and wake by %s.', wake_src) 172 else: 173 logging.info('Suspend and wake by %s.', wake_src) 174 175 logging.debug('Initializing HID keyboard emulator.') 176 self.servo.set_nocheck('init_usb_keyboard', 'on') 177 time.sleep(self.USB_PRESENT_DELAY) 178 179 try: 180 self.suspend_and_wake(self.suspend, 181 lambda:self.servo.set_nocheck('usb_keyboard_enter_key', 182 'press')) 183 except ConnectionError: 184 raise error.TestFail( 185 'USB HID suspend/resume fails. Maybe try to ' 186 'update firmware for Atmel USB KB emulator by running ' 187 'firmware_FlashServoKeyboardMap test and then try again?' 188 ) 189 self.check_boot_id(host, original_boot_id, wake_src) 190 191 logging.debug('Turning off HID keyboard emulator.') 192 self.servo.set_nocheck('init_usb_keyboard', 'off') 193 194 # Test suspend and wake by lid switch 195 wake_src = 'lid switch' 196 if not self.check_ec_capability(['lid']): 197 logging.info( 198 'The device has no lid. ' 199 'Skip testing suspend/resume by %s.', wake_src) 200 else: 201 logging.info('Suspend and wake by %s.', wake_src) 202 self.suspend_and_wake(self.suspend, self.wake_by_lid_switch) 203 logging.info('Close lid to suspend and wake by %s.', wake_src) 204 self.suspend_and_wake(lambda:self.servo.set('lid_open', 'no'), 205 self.wake_by_lid_switch) 206 self.check_boot_id(host, original_boot_id, wake_src) 207