• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2014 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
5"""Tests for the servo `power_state` control.
6
7## Purpose
8This test exists to ensure that servo's `power_state` control provides a
9consistent, hardware-independent implementation across all models of
10Chrome hardware.
11
12    ==== NOTE NOTE NOTE ====
13    YOU ARE NOT ALLOWED TO CHANGE THIS TEST TO TREAT DIFFERENT HARDWARE
14    DIFFERENTLY.
15
16In particular, importing from `faft.config` in order to customize this
17test for different hardware is never allowed.  This has been tried twice
18before; anyone making a third attempt is liable to be taken out and
19shot.  Or at least, be given a good talking to.
20
21This test is intended to enforce a contract between hardware-independent
22parts of Autotest (especially the repair code) and hardware-dependent
23implementations of servo in `hdctools`.  The contract imposes requirements
24and limitations on both sides.
25
26## Requirements on the servo implementation in `hdctools`
27### Setting `power_state:off`
28Setting `power_state:off` must turn the DUT "off" unconditionally.  The
29operation must have the same effect regardless of the state of the DUT
30prior to the operation.  The operation is not allowed to fail.
31
32The meaning of "off" does not require any specific component to be
33actually powered off, provided that the DUT does not respond on the
34network, and does not respond to USB devices being plugged or unplugged.
35Some examples of "off" that are acceptable:
36  * A system in ACPI power state S5.
37  * A system held in reset indefinitely by asserting `cold_reset:on`.
38  * In general, any system where power is not supplied to the AP.
39
40While a DUT is turned off, it is allowed to respond to the power button,
41the lid, and to the reset signals in ways that are hardware dependent.
42The signals may be ignored, or they may have any other effect that's
43appropriate to the hardware, such as turning the DUT on.
44
45### Setting `power_state:on` and `power_state:rec`
46After applying `power_state:off` to turn a DUT off, setting
47`power_state:on` or `power_state:rec` will turn the DUT on.
48  * Setting "on" turns the DUT on in normal mode.  The DUT will
49    boot normally as for a cold start.
50  * Setting "rec" turns the DUT on in recovery mode.  The DUT
51    will go to the recovery screen and wait for a USB stick to be
52    inserted.
53
54If a DUT is not off because of setting `power_state:off`, the response
55to "on" and "rec" may be hardware dependent.  The settings may turn the
56device on as specified, they may have no effect, or they may provide any
57other response that's appropriate to the hardware.
58
59### Setting `power_state:reset`
60Applying `power_state:reset` has the same visible effects as setting
61`power_state:off` followed by `power_state:on`.  The operation must have
62the same effect regardless of the state of the DUT prior to the
63operation.  The operation is not allowed to fail.
64
65Additionally, applying reset will assert and deassert `cold_reset` to
66ensure a full hardware reset.
67
68### Timing Constraints
69The servo implementation for `power_state` cannot impose timing
70constraints on Autotest.  If the hardware imposes constraints, the servo
71implemention must provide the necessary time delays by sleeping.
72
73In particular:
74  * After `power_state:off`, it is allowed to immediately apply either
75    `on` or `rec`.
76  * After `rec`, USB stick insertion must be recognized immediately.
77  * Setting `power_state:reset` twice in a row must work reliably.
78
79### Requirements on Autotest
80### Setting `power_state:off`
81Once a DUT has been turned "off", changing signals such as the power
82button, the lid, or reset signals isn't allowed.  The only allowed
83state changes are these:
84  * USB muxes may be switched at will.
85  * The `power_state` control can be set to any valid setting.
86
87Any other operation may cause the DUT to change state unpredictably
88(e.g. by powering the DUT on).
89
90## Setting `power_state:on` and `power_state:rec`
91Autotest can only apply `power_state:on` or `power_state:rec` if the DUT
92was previously turned off with `power_state:off`.
93
94"""
95
96import logging
97import time
98
99# STOP!  You are not allowed to import anything from FAFT.  Read the
100# comments above.
101from autotest_lib.client.common_lib import error
102from autotest_lib.server import test
103
104
105class platform_ServoPowerStateController(test.test):
106    """Test servo can power on and off DUT in recovery and non-recovery mode."""
107    version = 1
108
109    # Acceptable power states when device is turned off
110    POWER_OFF_STATES = ['S5','G3']
111
112    def initialize(self, host):
113        """Initialize DUT for testing."""
114        pass
115
116    # This is used as detection of b/159938441 occurrence
117    def check_vbus(self):
118        """Check if Vbus is supplied"""
119        # Check the issue occurs - VBUS is not supplied.
120        mv = self.host.servo.get_vbus_voltage()
121        if mv is not None and mv < self.host.servo.VBUS_THRESHOLD:
122            return False
123        # If vbus voltage monitoring isn't supported, does not signal the issue
124        return True
125
126    def cleanup(self):
127        """Clean up DUT after servo actions."""
128        # Ensure DUTs with type_c servo are in src mode.
129        self.host.servo.set_servo_v4_role('src')
130        if not self.host.is_up():
131            # Power off, then power on DUT from internal storage.
132            self.host.power_off_via_servo()
133            self.host.servo.switch_usbkey('off')
134            self.host.power_on_via_servo(self.controller.REC_OFF)
135            self.host.wait_up(timeout=300)
136
137
138    def assert_dut_on(self, rec_on=False):
139        """Confirm DUT is powered on, claim test failure if DUT is off.
140
141        @param rec_on: True if DUT should boot from external USB stick as in
142                       recovery mode.
143
144        @raise TestFail: If DUT is off or DUT boot from wrong source.
145        """
146        if not self.host.wait_up(timeout=300):
147            raise error.TestFail('power_state:%s did not turn DUT on.' %
148                                 ('rec' if rec_on else 'on'))
149
150        # Check boot source. Raise TestFail if DUT boot from wrong source.
151        boot_from_usb = self.host.is_boot_from_usb()
152        if boot_from_usb != rec_on:
153            boot_source = ('USB' if boot_from_usb else
154                           'non-removable storage')
155            raise error.TestFail('power_state:%s booted from %s.' %
156                                 ('rec' if rec_on else 'on', boot_source))
157
158
159    def assert_dut_off(self, error_message, check_power_state_off=True):
160        """Confirm DUT is off and does not turn back on after 30 seconds.
161
162        @param error_message: Error message to raise if DUT stays on.
163        @raise TestFail: If DUT stays on.
164        """
165        if not self.host.ping_wait_down(timeout=10):
166            raise error.TestFail(error_message)
167
168        # Check that power state is actually off (in S5 or G3)
169        if check_power_state_off:
170            ap_power_state = self.host.servo.get('ec_system_powerstate')
171            logging.info('Read power state: %s' % ap_power_state)
172            if ap_power_state not in self.POWER_OFF_STATES:
173                raise error.TestFail('%s. %s' % (error_message, 'DUT not in S5 or G3 state.'))
174
175        if self.host.ping_wait_up(timeout=30):
176            raise error.TestFail('%s. %s' % (error_message, 'DUT turns back on'
177                                             ' after it is turned off.'))
178
179
180    def test_with_usb_plugged_in(self):
181        """Run test when USB stick is plugged in to servo."""
182
183        # Servo V4 needs to be in snk role to allow booting from USB in
184        # recovery mode (b/161464597).
185        # TODO(waihong): Add a check to see if the battery level is too
186        # low and sleep for a while for charging.
187        if self.host.get_board().split(':')[1] == 'grunt':
188            # Skip on Grunt to avoid breaking CCD (b/170167166).
189            # TODO: Remove this once Grunt FW is fixed.
190            logging.info('Grunt: Do not put servo_v4 into snk role')
191        else:
192            logging.info('Put servo_v4 into snk role')
193            self.host.servo.set_servo_v4_role('snk')
194
195        logging.info('Power off DUT')
196        self.host.power_off_via_servo()
197        self.assert_dut_off('power_state:off did not turn DUT off.')
198
199        logging.info('Power DUT on in recovery mode, DUT shall boot from USB.')
200        self.host.servo.switch_usbkey('off')
201        self.host.power_on_via_servo(self.controller.REC_ON)
202        self.assert_dut_off('power_state:rec didn\'t stay at recovery screen.', False)
203
204        self.host.servo.switch_usbkey('dut')
205        time.sleep(30)
206        if self.check_vbus():
207            self.assert_dut_on(rec_on=True)
208            logging.info('Power off DUT which is up in recovery mode.')
209        else:
210            logging.error('EC/RO bug(b:159938441) present.'
211                          'The check for power_state:rec with USB plugged'
212                          'omitted for this board.')
213            logging.info('Power off DUT at recovery screen.')
214
215        self.host.power_off_via_servo()
216        self.assert_dut_off('power_state:off failed after boot from external '
217                            'USB stick.')
218
219        logging.info('Power DUT off in recovery mode without booting.')
220        self.host.servo.switch_usbkey('off')
221        self.host.power_on_via_servo(self.controller.REC_ON)
222        self.host.power_off_via_servo()
223        self.assert_dut_off('power_state:off failed at recovery screen ')
224
225        # Power DUT on in non-recovery mode with USB stick plugged in.
226        # DUT shall boot from internal storage.
227        logging.info('Power on DUT in non-recovery mode.')
228        self.host.servo.switch_usbkey('dut')
229        self.host.power_on_via_servo(self.controller.REC_OFF)
230        self.assert_dut_on()
231        self.host.servo.switch_usbkey('off')
232
233
234    def test_with_usb_unplugged(self):
235        """Run test when USB stick is not plugged in servo."""
236        # Power off DUT regardless its current status.
237        logging.info('Power off DUT.')
238        self.host.power_off_via_servo()
239        self.assert_dut_off('power_state:off did not turn DUT off.')
240
241        # Try to power off the DUT again, make sure the DUT stays off.
242        logging.info('Power off DUT which is already off.')
243        self.host.power_off_via_servo()
244        self.assert_dut_off('power_state:off turned DUT on.')
245
246        # USB stick should be unplugged before the test.
247        self.host.servo.switch_usbkey('off')
248
249        logging.info('Power on in non-recovery mode.')
250        self.host.power_on_via_servo(self.controller.REC_OFF)
251        self.assert_dut_on(rec_on=False)
252
253        logging.info('Power DUT off and on without delay. DUT should be '
254                     'on after power_on is completed.')
255        self.host.power_off_via_servo()
256        self.host.power_on_via_servo(self.controller.REC_OFF)
257        self.assert_dut_on(rec_on=False)
258
259        logging.info('Power off DUT which is up in non-recovery mode.')
260        self.host.power_off_via_servo()
261        self.assert_dut_off('power_state:off failed after boot from '
262                            'internal storage.')
263
264        logging.info('Power DUT off and reset. DUT should be on after '
265                     'reset is completed.')
266        self.host.reset_via_servo()
267        self.assert_dut_on(rec_on=False)
268
269        logging.info('Reset DUT when it\'s on. DUT should be on after '
270                     'reset is completed.')
271        boot_id = self.host.get_boot_id()
272        self.host.reset_via_servo()
273        self.assert_dut_on(rec_on=False)
274        new_boot_id = self.host.get_boot_id()
275        if not new_boot_id or boot_id == new_boot_id:
276            raise error.TestFail('power_state:reset failed to reboot DUT.')
277
278
279    def run_once(self, host, usb_available=True):
280        """Run the test.
281
282        @param host: host object of tested DUT.
283        @param usb_plugged_in: True if USB stick is plugged in servo.
284        """
285        self.host = host
286        self.controller = host.servo.get_power_state_controller()
287
288        self.test_with_usb_unplugged()
289        if usb_available and host.is_servo_usb_usable():
290            self.test_with_usb_plugged_in()
291