1# Copyright 2017 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_Cr50CCDServoCap(Cr50Test): 13 """Verify Cr50 CCD output enable/disable when servo is connected. 14 15 Verify Cr50 will enable/disable the CCD servo output capabilities when servo 16 is attached/detached. 17 """ 18 version = 1 19 20 # Time used to wait for Cr50 to detect the servo state. Cr50 updates the ccd 21 # state once a second. Wait 2 seconds to be conservative. 22 SLEEP = 2 23 24 # A list of the actions we should verify 25 TEST_CASES = [ 26 'fake_servo on, cr50_run reboot', 27 'fake_servo on, rdd attach, cr50_run reboot', 28 29 'rdd attach, fake_servo on, cr50_run reboot, fake_servo off', 30 'rdd attach, fake_servo on, rdd detach', 31 'rdd attach, fake_servo off, rdd detach', 32 ] 33 34 ON = 0 35 OFF = 1 36 UNDETECTABLE = 2 37 STATUS_MAP = [ 'on', 'off', 'unknown' ] 38 # Create maps for the different ccd states. Mapping each state to 'on', 39 # 'off', and 'unknown'. These lists map to the acceptable [ on values, off 40 # values, and unknown state values] 41 ON_MAP = [ 'on', 'off', '' ] 42 ENABLED_MAP = [ 'enabled', 'disabled', '' ] 43 CONNECTED_MAP = [ 'connected', 'disconnected', 'undetectable' ] 44 VALID_STATES = { 45 'AP' : ON_MAP, 46 'EC' : ON_MAP, 47 'AP UART' : ON_MAP, 48 'Rdd' : CONNECTED_MAP, 49 'Servo' : CONNECTED_MAP, 50 'CCD EXT' : ENABLED_MAP, 51 } 52 # RESULT_ORDER is a list of the CCD state strings. The order corresponds 53 # with the order of the key states in EXPECTED_RESULTS. 54 RESULT_ORDER = ['Rdd', 'CCD EXT', 'Servo'] 55 # A dictionary containing an order of steps to verify and the expected ccd 56 # states as the value. 57 # 58 # The keys are a list of strings with the order of steps to run. 59 # 60 # The values are the expected state of [rdd, ccd ext, servo]. The ccdstate 61 # strings are in RESULT_ORDER. The order of the EXPECTED_RESULTS key states 62 # must match the order in RESULT_ORDER. 63 # 64 # There are three valid states: UNDETECTABLE, ON, or OFF. Undetectable only 65 # describes the servo state when EC uart is enabled. If the ec uart is 66 # enabled, cr50 cannot detect servo and the state becomes undetectable. All 67 # other ccdstates can only be off or on. Cr50 has a lot of different words 68 # for off off and on. So VALID_STATES can be used to convert off, on, and 69 # undetectable to the actual state strings. 70 EXPECTED_RESULTS = { 71 # The state all tests will start with. Servo and the ccd cable are 72 # disconnected. 73 'reset_ccd state' : [OFF, OFF, OFF], 74 75 # If rdd is attached all ccd functionality will be enabled, and servo 76 # will be undetectable. 77 'rdd attach' : [ON, ON, UNDETECTABLE], 78 79 # Cr50 cannot detect servo if ccd has been enabled first 80 'rdd attach, fake_servo off' : [ON, ON, UNDETECTABLE], 81 'rdd attach, fake_servo off, rdd detach' : [OFF, OFF, OFF], 82 'rdd attach, fake_servo on' : [ON, ON, UNDETECTABLE], 83 'rdd attach, fake_servo on, rdd detach' : [OFF, OFF, ON], 84 # Cr50 can detect servo after a reboot even if rdd was attached before 85 # servo. 86 'rdd attach, fake_servo on, cr50_run reboot' : [ON, ON, ON], 87 # Once servo is detached, Cr50 will immediately reenable the EC uart. 88 'rdd attach, fake_servo on, cr50_run reboot, fake_servo off' : 89 [ON, ON, UNDETECTABLE], 90 91 # Cr50 can detect a servo attach 92 'fake_servo on' : [OFF, OFF, ON], 93 # Cr50 knows servo is attached when ccd is enabled, so it wont enable 94 # uart. 95 'fake_servo on, rdd attach' : [ON, ON, ON], 96 'fake_servo on, rdd attach, cr50_run reboot' : [ON, ON, ON], 97 'fake_servo on, cr50_run reboot' : [OFF, OFF, ON], 98 } 99 100 101 def initialize(self, host, cmdline_args, full_args): 102 super(firmware_Cr50CCDServoCap, self).initialize(host, cmdline_args, 103 full_args) 104 if not hasattr(self, 'cr50'): 105 raise error.TestNAError('Test can only be run on devices with ' 106 'access to the Cr50 console') 107 108 if (self.servo.get_servo_version(active=True) != 109 'servo_v4_with_servo_micro'): 110 raise error.TestNAError('Must use servo v4 with servo micro') 111 112 if not self.cr50.servo_dts_mode_is_valid(): 113 raise error.TestNAError('Need working servo v4 DTS control') 114 115 if not self.cr50.check_servo_monitor(): 116 raise error.TestNAError('Cannot run on device that does not ' 117 'support servo dectection with ' 118 'ec_uart_en:off/on') 119 # Make sure cr50 is open with testlab enabled. 120 self.fast_ccd_open(enable_testlab=True) 121 if not self.cr50.testlab_is_on(): 122 raise error.TestNAError('Cr50 testlab mode needs to be enabled') 123 logging.info('Cr50 is %s', self.servo.get('cr50_ccd_level')) 124 self.cr50.set_cap('UartGscTxECRx', 'Always') 125 self.ec_efs_support = ( 126 self.cr50.uses_board_property('BOARD_EC_CR50_COMM_SUPPORT')) 127 # Check EC uart if servo has ccd controls and the board has an EC. 128 self.check_ec_uart = (self.servo.has_control('ccd_cr50.ec_board') and 129 self.check_ec_capability(suppress_warning=True)) 130 131 132 def cleanup(self): 133 """Reenable the EC uart""" 134 try: 135 self.fake_servo('on') 136 self.rdd('detach') 137 self.rdd('attach') 138 finally: 139 super(firmware_Cr50CCDServoCap, self).cleanup() 140 141 142 def state_matches(self, state_dict, state_name, expected_value): 143 """Check the current state. Make sure it matches expected value""" 144 valid_state = self.VALID_STATES[state_name][expected_value] 145 # I2C isn't a reliable flag, because the hardware often doesn't support 146 # it. Remove any I2C flags from the ccdstate output. 147 current_state = state_dict[state_name].replace(' I2C', '') 148 if isinstance(valid_state, list): 149 return current_state in valid_state 150 return current_state == valid_state 151 152 153 def state_is_on(self, ccdstate, state_name): 154 """Returns true if the state is on""" 155 return self.state_matches(ccdstate, state_name, self.ON) 156 157 158 def ccd_ec_uart_works(self): 159 """Returns True if the CCD ec uart works.""" 160 try: 161 self.servo.get('ccd_cr50.ec_board') 162 logging.info('ccd ec console is responsive') 163 return True 164 except: 165 logging.info('ccd ec console is unresponsive') 166 return False 167 168 169 def check_state_flags(self, ccdstate): 170 """Check the state flags against the reset of the device state 171 172 If there is any mismatch between the device state and state flags, 173 return a list of errors. 174 """ 175 flags = ccdstate['State flags'] 176 ap_uart_enabled = 'UARTAP' in flags 177 ec_uart_enabled = 'UARTEC' in flags 178 ap_uart_tx_enabled = 'UARTAP+TX' in flags 179 ec_uart_tx_enabled = 'UARTEC+TX' in flags 180 ec_usb_tx_enabled = 'USBEC+TX' in flags 181 182 ccd_ec_uart_enabled = ec_uart_tx_enabled and ec_usb_tx_enabled 183 ccd_enabled = ap_uart_enabled or ec_usb_tx_enabled 184 output_enabled = ap_uart_tx_enabled 185 if not self.ec_efs_support: 186 output_enabled |= ec_uart_tx_enabled 187 ccd_enabled |= ec_uart_enabled 188 189 ccd_ext_is_enabled = ccdstate['CCD EXT'] == 'enabled' 190 mismatch = [] 191 logging.info('checking state flags') 192 if ccd_enabled and not ccd_ext_is_enabled: 193 mismatch.append('CCD functionality enabled without CCD EXT') 194 if ccd_ext_is_enabled: 195 if output_enabled and self.state_is_on(ccdstate, 'Servo'): 196 mismatch.append('CCD output is enabled with servo attached') 197 if ap_uart_enabled != self.state_is_on(ccdstate, 'AP UART'): 198 mismatch.append('AP UART enabled without AP UART on') 199 if ec_uart_enabled != self.state_is_on(ccdstate, 'EC'): 200 mismatch.append('EC UART enabled without EC on') 201 if self.check_ec_uart: 202 ccd_ec_uart_works = self.ccd_ec_uart_works() 203 if (self.servo.get('ec_uart_en') == 'off' 204 and ccd_ec_uart_enabled and not ccd_ec_uart_works): 205 mismatch.append('ccd ec uart does not work with EC+TX ' 206 'enabled.') 207 if not ccd_ec_uart_enabled and ccd_ec_uart_works: 208 mismatch.append('ccd ec uart works with EC+TX disabled.') 209 return mismatch 210 211 212 213 def verify_ccdstate(self, run): 214 """Verify the current state matches the expected result from the run. 215 216 Args: 217 run: the string representing the actions that have been run. 218 219 Raises: 220 TestError if any of the states are not correct 221 """ 222 if run not in self.EXPECTED_RESULTS: 223 raise error.TestError('Add results for %s to EXPECTED_RESULTS' % run) 224 expected_states = self.EXPECTED_RESULTS[run] 225 226 # Wait a short time for the ccd state to settle 227 time.sleep(self.SLEEP) 228 229 ccdstate = self.cr50.get_ccdstate() 230 # Check the state flags. Make sure they're in line with the rest of 231 # ccdstate 232 mismatch = self.check_state_flags(ccdstate) 233 for i, expected_state in enumerate(expected_states): 234 name = self.RESULT_ORDER[i] 235 if expected_state == None: 236 logging.info('No expected %s state skipping check', name) 237 continue 238 # Check that the current state matches the expected state 239 if not self.state_matches(ccdstate, name, expected_state): 240 mismatch.append('%s is %r not %r' % (name, ccdstate[name], 241 self.STATUS_MAP[expected_state])) 242 if mismatch: 243 logging.info(ccdstate) 244 raise error.TestFail('Unexpected states after %s: %s' % (run, 245 mismatch)) 246 247 248 def cr50_run(self, action): 249 """Reboot cr50 250 251 @param action: string 'reboot' 252 """ 253 if action == 'reboot': 254 self.cr50.reboot() 255 self.cr50.send_command('ccd testlab open') 256 time.sleep(self.SLEEP) 257 258 259 def reset_ccd(self, state=None): 260 """detach the ccd cable and disconnect servo. 261 262 State is ignored. It just exists to be consistent with the other action 263 functions. 264 265 @param state: a var that is ignored 266 """ 267 self.rdd('detach') 268 self.fake_servo('off') 269 270 271 def rdd(self, state): 272 """Attach or detach the ccd cable. 273 274 @param state: string 'attach' or 'detach' 275 """ 276 self.servo.set_dts_mode('on' if state == 'attach' else 'off') 277 time.sleep(self.SLEEP) 278 279 280 def fake_servo(self, state): 281 """Mimic servo on/off 282 283 Cr50 monitors the servo EC uart tx signal to detect servo. If the signal 284 is pulled up, then Cr50 will think servo is connnected. Enable the ec 285 uart to enable the pullup. Disable the it to remove the pullup. 286 287 It takes some time for Cr50 to detect the servo state so wait 2 seconds 288 before returning. 289 """ 290 self.servo.set('ec_uart_en', state) 291 292 # Cr50 needs time to detect the servo state 293 time.sleep(self.SLEEP) 294 295 296 def run_steps(self, steps): 297 """Do each step in steps and then verify the uart state. 298 299 The uart state is order dependent, so we need to know all of the 300 previous steps to verify the state. This will do all of the steps in 301 the string and verify the Cr50 CCD uart state after each step. 302 303 @param steps: a comma separated string with the steps to run 304 """ 305 # The order of steps is separated by ', '. Remove the last step and 306 # run all of the steps before it. 307 separated_steps = steps.rsplit(', ', 1) 308 if len(separated_steps) > 1: 309 self.run_steps(separated_steps[0]) 310 311 step = separated_steps[-1] 312 # The func and state are separated by ' ' 313 func, state = step.split(' ') 314 logging.info('running %s', step) 315 getattr(self, func)(state) 316 317 # Verify the ccd state is correct 318 self.verify_ccdstate(steps) 319 320 321 def run_once(self): 322 """Run through TEST_CASES and verify that Cr50 enables/disables uart""" 323 for steps in self.TEST_CASES: 324 self.run_steps('reset_ccd state') 325 logging.info('TESTING: %s', steps) 326 self.run_steps(steps) 327 logging.info('VERIFIED: %s', steps) 328