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 hibernate_and_wake_by_power_button(self, host): 44 """Shutdown to G3/S5, hibernate EC, and then wake by power button.""" 45 is_ac = host.is_ac_connected() 46 self.run_shutdown_cmd() 47 if not self.wait_power_state(self.POWER_STATE_G3, 48 self.POWER_STATE_RETRY_COUNT): 49 raise error.TestFail('Platform failed to reach G3 state.') 50 51 self.ec.send_command('hibernate') 52 time.sleep(self.WAKE_DELAY) 53 54 # If AC is plugged during the test, the DUT would wake up right after 55 # entering hibernate mode. So skip the verification for EC console 56 # responsiveness. 57 if is_ac != True and self.is_ec_console_responsive(): 58 raise error.TestFail('The DUT is not in hibernate mode.') 59 self.servo.power_short_press() 60 self.switcher.wait_for_client() 61 62 def is_ec_console_responsive(self): 63 """Test if EC console is responsive.""" 64 try: 65 self.ec.send_command_get_output('help', ['.*>']) 66 return True 67 except servo.UnresponsiveConsoleError: 68 return False 69 70 def wake_by_lid_switch(self): 71 """Wake up the device by lid switch.""" 72 self.servo.set('lid_open', 'no') 73 time.sleep(self.LID_DELAY) 74 self.servo.set('lid_open', 'yes') 75 76 def suspend_and_wake(self, suspend_func, wake_func): 77 """Suspend and then wake up the device. 78 79 Args: 80 suspend_func: The method used to suspend the device 81 wake_func: The method used to resume 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 not self.wait_power_state(self.POWER_STATE_S0, 91 self.POWER_STATE_RETRY_COUNT): 92 raise error.TestFail('Platform failed to reach S0 state.') 93 self.switcher.wait_for_client(timeout=self.RESUME_TIMEOUT) 94 95 def suspend_and_dont_wake(self, suspend_func, wake_func): 96 """Suspend and then check the the device doesn't wake up. 97 98 Args: 99 suspend_func: The method used to suspend the device 100 wake_func: The method used to wake the device 101 """ 102 suspend_func() 103 self.switcher.wait_for_client_offline() 104 if not self.wait_power_state(self.POWER_STATE_SUSPEND, 105 self.POWER_STATE_RETRY_COUNT): 106 raise error.TestFail('Platform failed to reach S0ix or S3 state.') 107 time.sleep(self.SUSPEND_WAIT_TIME_SECONDS) 108 wake_func() 109 if self.wait_power_state(self.POWER_STATE_S0, 110 self.POWER_STATE_RETRY_COUNT): 111 raise error.TestFail('Platform woke up unexpectedly.') 112 else: 113 self.servo.power_normal_press() 114 if not self.wait_power_state(self.POWER_STATE_S0, 115 self.POWER_STATE_RETRY_COUNT): 116 raise error.TestFail('Platform failed to reach S0 state.') 117 self.switcher.wait_for_client(timeout=self.RESUME_TIMEOUT) 118 119 def run_once(self, host): 120 """Runs a single iteration of the test.""" 121 # Login as a normal user and stay there, such that closing lid triggers 122 # suspend, instead of shutdown. 123 autotest_client = autotest.Autotest(host) 124 autotest_client.run_test('desktopui_SimpleLogin', 125 exit_without_logout=True) 126 original_boot_id = host.get_boot_id() 127 128 # With no display connected, pressing the power button in suspend mode 129 # would lead to shutdown. 130 if self.has_internal_display: 131 logging.info('Suspend and wake by power button.') 132 self.suspend_and_wake(self.suspend, self.servo.power_normal_press) 133 134 if not self.check_ec_capability(['keyboard']): 135 logging.info('The device has no internal keyboard. ' 136 'Skip testing suspend/resume by internal keyboard.') 137 elif not self.ec.has_command('ksstate'): 138 logging.info('The device does not support the ksstate command. ' 139 'Skip testing suspend/resume by internal keyboard.') 140 else: 141 result = self.ec.send_command_get_output( 142 'ksstate', 143 ['Keyboard scan disable mask: 0x([0-9a-fA-F]{8})']) 144 kb_scan_disable_mask = int(result[0][1], 16) 145 if kb_scan_disable_mask == 0: 146 logging.info('Suspend and wake by internal key press.') 147 self.suspend_and_wake(self.suspend, 148 lambda: self.ec.key_press('<enter>')) 149 else: 150 logging.info('Tablet mode enabled; suspend and check device ' 151 'does not wake by internal key press.') 152 self.suspend_and_dont_wake( 153 self.suspend, lambda: self.ec.key_press('<enter>')) 154 155 if not self.faft_config.usb_hid_wake_enabled: 156 logging.info('Device does not support wake by USB HID. ' 157 'Skip suspend and wake by USB HID key press.') 158 else: 159 logging.info('Suspend and wake by USB HID key press.') 160 161 logging.debug('Initializing HID keyboard emulator.') 162 self.servo.set_nocheck('init_usb_keyboard', 'on') 163 time.sleep(self.USB_PRESENT_DELAY) 164 165 try: 166 self.suspend_and_wake(self.suspend, 167 lambda:self.servo.set_nocheck('usb_keyboard_enter_key', 168 'press')) 169 except ConnectionError: 170 raise error.TestFail( 171 'USB HID suspend/resume fails. Maybe try to ' 172 'update firmware for Atmel USB KB emulator by running ' 173 'firmware_FlashServoKeyboardMap test and then try again?' 174 ) 175 176 logging.debug('Turning off HID keyboard emulator.') 177 self.servo.set_nocheck('init_usb_keyboard', 'off') 178 179 if not self.check_ec_capability(['lid']): 180 logging.info('The device has no lid. ' 181 'Skip testing suspend/resume by lid switch.') 182 else: 183 logging.info('Suspend and wake by lid switch.') 184 self.suspend_and_wake(self.suspend, self.wake_by_lid_switch) 185 logging.info('Close lid to suspend and wake by lid switch.') 186 self.suspend_and_wake(lambda:self.servo.set('lid_open', 'no'), 187 self.wake_by_lid_switch) 188 189 boot_id = host.get_boot_id() 190 if boot_id != original_boot_id: 191 raise error.TestFail('Different boot_id. Unexpected reboot.') 192 193 if self.servo.main_device_is_ccd(): 194 logging.info('With CCD, we can\'t wake up the DUT from hibernate ' 195 'by power button. Skip hibernate test.') 196 elif not self.faft_config.ec_has_hibernate_cmd: 197 logging.info('EC does not support hibernate. Skip hibernate test.') 198 elif not self.has_internal_display: 199 logging.info('For the form factors without internal display, ' 200 'hibernate is not useful. Skip hibernate test.') 201 else: 202 logging.info('EC hibernate and wake by power button.') 203 self.hibernate_and_wake_by_power_button(host) 204