• 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 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