• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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