• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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