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
5from threading import Timer
6import logging
7import re
8import time
9
10from autotest_lib.client.common_lib import error
11from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
12
13
14def delayed(seconds):
15    def decorator(f):
16        def wrapper(*args, **kargs):
17            t = Timer(seconds, f, args, kargs)
18            t.start()
19        return wrapper
20    return decorator
21
22
23class firmware_ECLidSwitch(FirmwareTest):
24    """
25    Servo based EC lid switch test.
26    """
27    version = 1
28
29    # Delay between closing and opening the lid
30    LID_DELAY = 1
31
32    # Delay to allow FAFT client receive command
33    RPC_DELAY = 2
34
35    # Delay between shutdown and wake by lid switch including kernel
36    # shutdown time
37    LONG_WAKE_DELAY = 25
38    SHORT_WAKE_DELAY = 15
39
40    def initialize(self, host, cmdline_args):
41        super(firmware_ECLidSwitch, self).initialize(host, cmdline_args)
42        # Only run in normal mode
43        self.switcher.setup_mode('normal')
44
45    def _open_lid(self):
46        """Open lid by servo."""
47        self.servo.set('lid_open', 'yes')
48
49    def _close_lid(self):
50        """Close lid by servo."""
51        self.servo.set('lid_open', 'no')
52
53    @delayed(RPC_DELAY)
54    def delayed_open_lid(self):
55        """Delay by RPC_DELAY and then open lid by servo."""
56        self._open_lid()
57
58    @delayed(RPC_DELAY)
59    def delayed_close_lid(self):
60        """Delay by RPC_DELAY and then close lid by servo."""
61        self._close_lid()
62
63    def _wake_by_lid_switch(self):
64        """Wake DUT with lid switch."""
65        self._close_lid()
66        time.sleep(self.LID_DELAY)
67        self._open_lid()
68
69    def long_delayed_wake(self):
70        """Delay for LONG_WAKE_DELAY and then wake DUT with lid switch."""
71        time.sleep(self.LONG_WAKE_DELAY)
72        self._wake_by_lid_switch()
73
74    def short_delayed_wake(self):
75        """Delay for SHORT_WAKE_DELAY and then wake DUT with lid switch."""
76        time.sleep(self.SHORT_WAKE_DELAY)
77        self._wake_by_lid_switch()
78
79    def shutdown_and_wake(self, wake_func):
80        """Software shutdown and delay. Then wake by lid switch.
81
82        Args:
83          wake_func: Delayed function to wake DUT.
84        """
85        self.faft_client.system.run_shell_command('shutdown -P now')
86        wake_func()
87
88    def _get_keyboard_backlight(self):
89        """Get keyboard backlight brightness.
90
91        Returns:
92          Backlight brightness percentage 0~100. If it is disabled, 0 is
93            returned.
94        """
95        cmd = 'ectool pwmgetkblight'
96        pattern_percent = re.compile(
97            'Current keyboard backlight percent: (\d*)')
98        pattern_disable = re.compile('Keyboard backlight disabled.')
99        lines = self.faft_client.system.run_shell_command_get_output(cmd)
100        for line in lines:
101            matched_percent = pattern_percent.match(line)
102            if matched_percent is not None:
103                return int(matched_percent.group(1))
104            matched_disable = pattern_disable.match(line)
105            if matched_disable is not None:
106                return 0
107        raise error.TestError('Cannot get keyboard backlight status.')
108
109    def _set_keyboard_backlight(self, value):
110        """Set keyboard backlight brightness.
111
112        Args:
113          value: Backlight brightness percentage 0~100.
114        """
115        cmd = 'ectool pwmsetkblight %d' % value
116        self.faft_client.system.run_shell_command(cmd)
117
118    def check_keycode(self):
119        """Check that lid open/close do not send power button keycode.
120
121        Returns:
122          True if no power button keycode is captured. Otherwise, False.
123        """
124        self._open_lid()
125        self.delayed_close_lid()
126        if self.faft_client.system.check_keys([]) < 0:
127            return False
128        self.delayed_open_lid()
129        if self.faft_client.system.check_keys([]) < 0:
130            return False
131        return True
132
133    def check_backlight(self):
134        """Check if lid open/close controls keyboard backlight as expected.
135
136        Returns:
137          True if keyboard backlight is turned off when lid close and on when
138           lid open.
139        """
140        if not self.check_ec_capability(['kblight'], suppress_warning=True):
141            return True
142        ok = True
143        original_value = self._get_keyboard_backlight()
144        self._set_keyboard_backlight(100)
145
146        self._close_lid()
147        if self._get_keyboard_backlight() != 0:
148            logging.error("Keyboard backlight still on when lid close.")
149            ok = False
150        self._open_lid()
151        if self._get_keyboard_backlight() == 0:
152            logging.error("Keyboard backlight still off when lid open.")
153            ok = False
154
155        self._set_keyboard_backlight(original_value)
156        return ok
157
158    def check_keycode_and_backlight(self):
159        """
160        Disable powerd to prevent DUT shutting down during test. Then check
161        if lid switch event controls keycode and backlight as we expected.
162        """
163        ok = True
164        logging.info("Stopping powerd")
165        self.faft_client.system.run_shell_command('stop powerd')
166        if not self.check_keycode():
167            logging.error("check_keycode failed.")
168            ok = False
169        if not self.check_backlight():
170            logging.error("check_backlight failed.")
171            ok = False
172        logging.info("Restarting powerd")
173        self.faft_client.system.run_shell_command('start powerd')
174        return ok
175
176    def run_once(self):
177        if not self.check_ec_capability(['lid']):
178            raise error.TestNAError("Nothing needs to be tested on this device")
179
180        logging.info("Shutdown and long delayed wake.")
181        self.switcher.mode_aware_reboot(
182                'custom',
183                lambda:self.shutdown_and_wake(self.long_delayed_wake))
184
185        logging.info("Shutdown and short delayed wake.")
186        self.switcher.mode_aware_reboot(
187                'custom',
188                lambda:self.shutdown_and_wake(self.short_delayed_wake))
189
190        logging.info("Check keycode and backlight.")
191        self.check_state(self.check_keycode_and_backlight)
192