1# Copyright 2018 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.cros.faft.cr50_test import Cr50Test 10 11 12class firmware_Cr50OpenWhileAPOff(Cr50Test): 13 """Verify the console can be opened while the AP is off. 14 15 Make sure it runs ok when cr50 saw the AP turn off and when it resets while 16 the AP is off. 17 18 This test would work the same with any cr50 ccd command that uses vendor 19 commands. 'ccd open' is just one. 20 """ 21 version = 1 22 23 SLEEP_DELAY = 20 24 SHORT_DELAY = 2 25 CCD_PASSWORD_RATE_LIMIT = 3 26 PASSWORD = 'Password' 27 PLT_RST = 1 << 6 28 29 def initialize(self, host, cmdline_args, full_args): 30 """Initialize the test""" 31 self.changed_dut_state = False 32 super(firmware_Cr50OpenWhileAPOff, self).initialize(host, cmdline_args, 33 full_args) 34 35 if not hasattr(self, 'cr50'): 36 raise error.TestNAError('Test can only be run on devices with ' 37 'access to the Cr50 console') 38 39 # TODO(mruthven): replace with dependency on servo v4 with servo micro 40 # and type c cable. 41 if 'servo_v4_with_servo_micro' != self.servo.get_servo_version(): 42 raise error.TestNAError('Run using servo v4 with servo micro') 43 44 if not self.cr50.has_command('ccdstate'): 45 raise error.TestNAError('Cannot test on Cr50 with old CCD version') 46 47 dts_mode_works = self.cr50.servo_v4_supports_dts_mode() 48 if not dts_mode_works: 49 raise error.TestNAError('Plug in servo v4 type c cable into ccd ' 50 'port') 51 52 # Asserting warm_reset will hold the AP in reset if the system uses 53 # SYS_RST instead of PLT_RST. If the system uses PLT_RST, we have to 54 # hold the EC in reset to guarantee the device won't turn on during 55 # open. 56 # warm_reset doesn't interfere with rdd, so it's best to use that when 57 # possible. 58 self.reset_signal = ('cold_reset' if self.cr50.get_board_properties() & 59 self.PLT_RST else 'warm_reset') 60 logging.info('Using %r for reset', self.reset_signal) 61 62 self.fast_open(enable_testlab=True) 63 # make sure password is cleared. 64 self.cr50.send_command('ccd reset') 65 self.cr50.get_ccd_info() 66 # You can only open cr50 from the console if a password is set. Set 67 # a password, so we can use it to open cr50 while the AP is off. 68 self.set_ccd_password(self.PASSWORD) 69 70 self.changed_dut_state = True 71 self.assert_reset = True 72 if not self.reset_device_get_deep_sleep_count(True): 73 # Some devices can't tell the AP is off when the EC is off. Try 74 # deep sleep with just the AP off. 75 self.assert_reset = False 76 # If deep sleep doesn't work at all, we can't run the test. 77 if not self.reset_device_get_deep_sleep_count(True): 78 raise error.TestNAError('Skipping test on device without deep ' 79 'sleep support') 80 # We can't hold the ec in reset and enter deep sleep. Set the 81 # capability so physical presence isn't required for open. 82 logging.info("deep sleep doesn't work with EC in reset. skipping " 83 "physical presence checks.") 84 # set OpenNoLongPP so open won't require pressing the power button. 85 self.cr50.set_cap('OpenNoLongPP', 'Always') 86 else: 87 logging.info('Physical presence can be used during open') 88 89 90 def cleanup(self): 91 """Make sure the device is on at the end of the test""" 92 # If we got far enough to start changing the DUT power state, attempt to 93 # turn the DUT back on and reenable the cr50 console. 94 try: 95 if self.changed_dut_state: 96 self.restore_dut() 97 finally: 98 super(firmware_Cr50OpenWhileAPOff, self).cleanup() 99 100 101 def restore_dut(self): 102 """Turn on the device and reset cr50 103 104 Do a deep sleep reset to fix the cr50 console. Then turn the device on. 105 106 Raises: 107 TestFail if the cr50 console doesn't work 108 """ 109 logging.info('attempt cr50 console recovery') 110 111 # The console may be hung. Run through reset manually, so we dont need 112 # the console. 113 self.turn_device('off') 114 # Toggle dts mode to enter and exit deep sleep 115 self.toggle_dts_mode() 116 # Turn the device back on 117 self.turn_device('on') 118 119 # Verify the cr50 console responds to commands. 120 try: 121 logging.info(self.cr50.send_command_get_output('ccdstate', 122 ['ccdstate.*>'])) 123 except error.TestFail, e: 124 if 'Timeout waiting for response' in e.message: 125 raise error.TestFail('Could not restore Cr50 console') 126 raise 127 128 129 def turn_device(self, state): 130 """Turn the device off or on. 131 132 If we are testing ccd open fully, it will also assert device reset so 133 power button presses wont turn on the AP 134 """ 135 # Make sure to release the device from reset before trying anything 136 self.servo.set(self.reset_signal, 'off') 137 138 time.sleep(self.SHORT_DELAY) 139 140 # Turn off the AP 141 if state == 'off': 142 self.servo.set_nocheck('power_state', 'off') 143 time.sleep(self.SHORT_DELAY) 144 145 # Hold the EC in reset or release it from reset based on state 146 if self.assert_reset: 147 # The reset control is the inverse of device state, so convert the 148 # state self.servo.set(reset_signal, 'on' if state == 'off' else 149 # 'off') 150 self.servo.set(self.reset_signal, 'on' if state == 'off' else 'off') 151 time.sleep(self.SHORT_DELAY) 152 153 # Turn on the AP 154 if state == 'on': 155 self.servo.power_short_press() 156 157 158 def reset_device_get_deep_sleep_count(self, deep_sleep): 159 """Reset the device. Use dts mode to enable deep sleep if requested. 160 161 Args: 162 deep_sleep: True if Cr50 should enter deep sleep 163 164 Returns: 165 The number of times Cr50 entered deep sleep during reset 166 """ 167 self.turn_device('off') 168 # Do a deep sleep reset to restore the cr50 console. 169 ds_count = self.deep_sleep_reset_get_count() if deep_sleep else 0 170 self.turn_device('on') 171 return ds_count 172 173 174 def toggle_dts_mode(self): 175 """Toggle DTS mode to enable and disable deep sleep""" 176 # We cant use cr50 ccd_disable/enable, because those uses the cr50 177 # console. Call servo_v4_dts_mode directly. 178 self.servo.set_nocheck('servo_v4_dts_mode', 'off') 179 time.sleep(self.SLEEP_DELAY) 180 self.servo.set_nocheck('servo_v4_dts_mode', 'on') 181 182 183 def deep_sleep_reset_get_count(self): 184 """Toggle ccd to get to do a deep sleep reset 185 186 Returns: 187 The number of times cr50 entered deep sleep 188 """ 189 start_count = self.cr50.get_deep_sleep_count() 190 # CCD is what's keeping Cr50 awake. Toggle DTS mode to turn off ccd 191 # so cr50 will enter deep sleep 192 self.toggle_dts_mode() 193 # Return the number of times cr50 entered deep sleep. 194 return self.cr50.get_deep_sleep_count() - start_count 195 196 197 def try_ccd_open(self, cr50_reset): 198 """Try 'ccd open' and make sure the console doesn't hang""" 199 self.cr50.set_ccd_level('lock', self.PASSWORD) 200 try: 201 self.turn_device('off') 202 if cr50_reset: 203 if not self.deep_sleep_reset_get_count(): 204 raise error.TestFail('Did not detect a cr50 reset') 205 # Verify ccd open 206 self.cr50.set_ccd_level('open', self.PASSWORD) 207 finally: 208 self.restore_dut() 209 210 211 def run_once(self): 212 """Turn off the AP and try ccd open.""" 213 self.try_ccd_open(False) 214 self.try_ccd_open(True) 215