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