207 lines
8.8 KiB
Python
207 lines
8.8 KiB
Python
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import logging
|
|
import time
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.server import autotest
|
|
from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
|
|
from autotest_lib.server.cros.faft.firmware_test import ConnectionError
|
|
from autotest_lib.server.cros.servo import servo
|
|
|
|
|
|
class firmware_ECWakeSource(FirmwareTest):
|
|
"""
|
|
Servo based EC wake source test.
|
|
"""
|
|
version = 1
|
|
|
|
# After suspending the device, wait this long before waking it again.
|
|
SUSPEND_WAIT_TIME_SECONDS = 5
|
|
# Retries allowed for reaching S0ix|S3 after suspend and S0 after wake.
|
|
POWER_STATE_RETRY_COUNT = 10
|
|
# The timeout (in seconds) to confirm the device is woken up from
|
|
# suspend mode.
|
|
RESUME_TIMEOUT = 60
|
|
# Delay before the USB keyboard is seen by DUT after initialization
|
|
USB_PRESENT_DELAY = 1
|
|
|
|
def initialize(self, host, cmdline_args):
|
|
super(firmware_ECWakeSource, self).initialize(host, cmdline_args)
|
|
# Only run in normal mode
|
|
self.switcher.setup_mode('normal')
|
|
self.has_internal_display = host.has_internal_display()
|
|
|
|
def cleanup(self):
|
|
# Restore the lid_open switch in case the test failed in the middle.
|
|
if self.check_ec_capability(['lid']):
|
|
self.servo.set('lid_open', 'yes')
|
|
super(firmware_ECWakeSource, self).cleanup()
|
|
|
|
def is_ec_console_responsive(self):
|
|
"""Test if EC console is responsive."""
|
|
try:
|
|
self.ec.send_command_get_output('help', ['.*>'])
|
|
return True
|
|
except servo.UnresponsiveConsoleError:
|
|
return False
|
|
|
|
def wake_by_lid_switch(self):
|
|
"""Wake up the device by lid switch."""
|
|
self.servo.set('lid_open', 'no')
|
|
time.sleep(self.LID_DELAY)
|
|
self.servo.set('lid_open', 'yes')
|
|
|
|
def suspend_and_wake(self, suspend_func, wake_func):
|
|
"""Suspend and then wake up the device.
|
|
|
|
Args:
|
|
suspend_func: The method used to suspend the device
|
|
wake_func: The method used to resume the device
|
|
"""
|
|
suspend_func()
|
|
self.switcher.wait_for_client_offline()
|
|
if not self.wait_power_state(self.POWER_STATE_SUSPEND,
|
|
self.POWER_STATE_RETRY_COUNT):
|
|
raise error.TestFail('Platform failed to reach S0ix or S3 state.')
|
|
time.sleep(self.SUSPEND_WAIT_TIME_SECONDS)
|
|
wake_func()
|
|
if not self.wait_power_state(self.POWER_STATE_S0,
|
|
self.POWER_STATE_RETRY_COUNT):
|
|
raise error.TestFail('Platform failed to reach S0 state.')
|
|
self.switcher.wait_for_client(timeout=self.RESUME_TIMEOUT)
|
|
|
|
def suspend_and_dont_wake(self, suspend_func, wake_func):
|
|
"""Suspend and then check the the device doesn't wake up.
|
|
|
|
Args:
|
|
suspend_func: The method used to suspend the device
|
|
wake_func: The method used to wake the device
|
|
"""
|
|
suspend_func()
|
|
self.switcher.wait_for_client_offline()
|
|
if not self.wait_power_state(self.POWER_STATE_SUSPEND,
|
|
self.POWER_STATE_RETRY_COUNT):
|
|
raise error.TestFail('Platform failed to reach S0ix or S3 state.')
|
|
time.sleep(self.SUSPEND_WAIT_TIME_SECONDS)
|
|
wake_func()
|
|
if self.wait_power_state(self.POWER_STATE_S0,
|
|
self.POWER_STATE_RETRY_COUNT):
|
|
raise error.TestFail('Platform woke up unexpectedly.')
|
|
else:
|
|
self.servo.power_normal_press()
|
|
if not self.wait_power_state(self.POWER_STATE_S0,
|
|
self.POWER_STATE_RETRY_COUNT):
|
|
raise error.TestFail('Platform failed to reach S0 state.')
|
|
self.switcher.wait_for_client(timeout=self.RESUME_TIMEOUT)
|
|
|
|
def check_boot_id(self, host, orig_boot_id, wake_method):
|
|
"""Check current boot id matches original boot id.
|
|
|
|
Args:
|
|
host: test host object
|
|
orig_boot_id: original boot_id to compare against
|
|
wake_method: string indicating the method used to wake the device
|
|
"""
|
|
boot_id = host.get_boot_id()
|
|
if boot_id != orig_boot_id:
|
|
raise error.TestFail('Unexpected reboot by suspend and wake: ' +
|
|
wake_method)
|
|
|
|
def run_once(self, host):
|
|
"""Runs a single iteration of the test."""
|
|
if not self.check_ec_capability():
|
|
raise error.TestNAError(
|
|
"Nothing needs to be tested on this device")
|
|
|
|
# Login as a normal user and stay there, such that closing lid triggers
|
|
# suspend, instead of shutdown.
|
|
autotest_client = autotest.Autotest(host)
|
|
autotest_client.run_test('desktopui_SimpleLogin',
|
|
exit_without_logout=True)
|
|
original_boot_id = host.get_boot_id()
|
|
|
|
# Test suspend and wake by power button
|
|
wake_src = 'power button'
|
|
if not self.has_internal_display:
|
|
# With no display connected, pressing the power button in suspend mode
|
|
# would lead to shutdown.
|
|
logging.info(
|
|
'The device has no internal display. '
|
|
'Skip testing suspend/resume by %s.', wake_src)
|
|
else:
|
|
logging.info('Suspend and wake by %s.', wake_src)
|
|
self.suspend_and_wake(self.suspend, self.servo.power_normal_press)
|
|
self.check_boot_id(host, original_boot_id, wake_src)
|
|
|
|
# Test suspend and wake by internal key press
|
|
wake_src = 'internal key press'
|
|
if not self.check_ec_capability(['keyboard']):
|
|
logging.info(
|
|
'The device has no internal keyboard. '
|
|
'Skip testing suspend/resume by %s.', wake_src)
|
|
elif not self.ec.has_command('ksstate'):
|
|
logging.info(
|
|
'The device does not support the ksstate command. '
|
|
'Skip testing suspend/resume by %s.', wake_src)
|
|
else:
|
|
result = self.ec.send_command_get_output(
|
|
'ksstate',
|
|
['Keyboard scan disable mask: 0x([0-9a-fA-F]{8})'])
|
|
kb_scan_disable_mask = int(result[0][1], 16)
|
|
if kb_scan_disable_mask == 0:
|
|
logging.info('Suspend and wake by %s.', wake_src)
|
|
self.suspend_and_wake(self.suspend,
|
|
lambda: self.ec.key_press('<enter>'))
|
|
else:
|
|
logging.info(
|
|
'Tablet mode enabled; suspend and check device '
|
|
'does not wake by %s.', wake_src)
|
|
self.suspend_and_dont_wake(
|
|
self.suspend, lambda: self.ec.key_press('<enter>'))
|
|
self.check_boot_id(host, original_boot_id, wake_src)
|
|
|
|
# Test suspend and wake by USB HID key press
|
|
wake_src = 'USB HID key press'
|
|
if not self.faft_config.usb_hid_wake_enabled:
|
|
logging.info(
|
|
'Device does not support wake by USB HID. '
|
|
'Skip suspend and wake by %s.', wake_src)
|
|
else:
|
|
logging.info('Suspend and wake by %s.', wake_src)
|
|
|
|
logging.debug('Initializing HID keyboard emulator.')
|
|
self.servo.set_nocheck('init_usb_keyboard', 'on')
|
|
time.sleep(self.USB_PRESENT_DELAY)
|
|
|
|
try:
|
|
self.suspend_and_wake(self.suspend,
|
|
lambda:self.servo.set_nocheck('usb_keyboard_enter_key',
|
|
'press'))
|
|
except ConnectionError:
|
|
raise error.TestFail(
|
|
'USB HID suspend/resume fails. Maybe try to '
|
|
'update firmware for Atmel USB KB emulator by running '
|
|
'firmware_FlashServoKeyboardMap test and then try again?'
|
|
)
|
|
self.check_boot_id(host, original_boot_id, wake_src)
|
|
|
|
logging.debug('Turning off HID keyboard emulator.')
|
|
self.servo.set_nocheck('init_usb_keyboard', 'off')
|
|
|
|
# Test suspend and wake by lid switch
|
|
wake_src = 'lid switch'
|
|
if not self.check_ec_capability(['lid']):
|
|
logging.info(
|
|
'The device has no lid. '
|
|
'Skip testing suspend/resume by %s.', wake_src)
|
|
else:
|
|
logging.info('Suspend and wake by %s.', wake_src)
|
|
self.suspend_and_wake(self.suspend, self.wake_by_lid_switch)
|
|
logging.info('Close lid to suspend and wake by %s.', wake_src)
|
|
self.suspend_and_wake(lambda:self.servo.set('lid_open', 'no'),
|
|
self.wake_by_lid_switch)
|
|
self.check_boot_id(host, original_boot_id, wake_src)
|