普通文本  |  203行  |  8.33 KB

# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import logging
import time

from autotest_lib.client.common_lib import error
from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
from autotest_lib.server.cros.servo import pd_console


class firmware_PDPowerSwap(FirmwareTest):
    """
    Servo based USB PD power role swap test.

    Pass critera is all power role swaps are successful if the DUT
    is dualrole capable. If not dualrole, then pass criteria is
    the DUT sending a reject message in response to swap request.

    """
    version = 1

    PD_ROLE_DELAY = 0.5
    PD_CONNECT_DELAY = 4
    PLANKTON_PORT = 0
    POWER_SWAP_ITERATIONS = 5
    # Source power role
    SRC ='SRC_READY'
    # Sink power role
    SNK = 'SNK_READY'

    def _set_plankton_power_role_to_src(self):
        """Force Plankton to act as a source

        @returns True if Plankton power role is source, false otherwise
        """
        PLANKTON_SRC_VOLTAGE = 20
        self.plankton.charge(PLANKTON_SRC_VOLTAGE)
        # Wait for change to take place
        time.sleep(self.PD_CONNECT_DELAY)
        plankton_state = self.plankton_pd_utils.get_pd_state(self.PLANKTON_PORT)
        # Current Plankton power role should be source
        return bool(plankton_state == self.SRC)

    def _send_power_swap_get_reply(self, console, port):
        """Send power swap request, get PD control msg reply

        The PD console debug mode is enabled prior to sending
        a pd power role swap request message. This allows the
        control message reply to be extracted. The debug mode
        is disabled prior to exiting.

        @param console: pd console object for uart access

        @returns: PD control header message
        """
        # Enable PD console debug mode to show control messages
        console.enable_pd_console_debug()
        cmd = 'pd %d swap power' % port
        m = console.send_pd_command_get_output(cmd, ['RECV\s([\w]+)'])
        ctrl_msg = int(m[0][1], 16) & console.PD_CONTROL_MSG_MASK
        console.disable_pd_console_debug()
        return ctrl_msg

    def _attempt_power_swap(self, pd_port, direction):
        """Perform a power role swap request

        Initiate a power role swap request on either the DUT or
        Plankton depending on the direction parameter. The power
        role swap is then verified to have taken place.

        @param pd_port: DUT pd port value 0/1
        @param direction: rx or tx from the DUT perspective

        @returns True if power swap is successful
        """
        # Get DUT current power role
        dut_pr = self.dut_pd_utils.get_pd_state(pd_port)
        if direction == 'rx':
            console = self.plankton_pd_utils
            port = self.PLANKTON_PORT
        else:
            console = self.dut_pd_utils
            port = pd_port
        # Send power swap request
        self._send_power_swap_get_reply(console, port)
        time.sleep(self.PD_CONNECT_DELAY)
        # Get Plankton power role
        plankton_pr = self.plankton_pd_utils.get_pd_state(self.PLANKTON_PORT)
        return bool(dut_pr == plankton_pr)

    def _test_power_swap_reject(self, pd_port):
        """Verify that a power swap request is rejected

        This tests the case where the DUT isn't in dualrole mode.
        A power swap request is sent by Plankton, and then
        the control message checked to ensure the request was rejected.
        In addition, the connection state is verified to not have
        changed.

        @param pd_port: port for DUT pd connection
        """
        # Get current DUT power role
        dut_power_role = self.dut_pd_utils.get_pd_state(pd_port)
        # Send swap command from Plankton and get reply
        ctrl_msg = self._send_power_swap_get_reply(self.plankton_pd_utils,
                                                   self.PLANKTON_PORT)
        if ctrl_msg != self.dut_pd_utils.PD_CONTROL_MSG_DICT['Reject']:
            raise error.TestFail('Power Swap Req not rejected, returned %r' %
                                 ctrl_msg)
        # Get DUT current state
        pd_state = self.dut_pd_utils.get_pd_state(pd_port)
        if pd_state != dut_power_role:
            raise error.TestFail('PD not connected! pd_state = %r' %
                                 pd_state)

    def initialize(self, host, cmdline_args):
        super(firmware_PDPowerSwap, self).initialize(host, cmdline_args)
        # Only run in normal mode
        self.switcher.setup_mode('normal')
        # Turn off console prints, except for USBPD.
        self.usbpd.send_command('chan 0x08000000')

    def cleanup(self):
        self.usbpd.send_command('chan 0xffffffff')
        super(firmware_PDPowerSwap, self).cleanup()

    def run_once(self):
        """Execute Power Role swap test.

        1. Verify that pd console is accessible
        2. Verify that DUT has a valid PD contract and connected to Plankton
        3. Determine if DUT is in dualrole mode
        4. If not dualrole mode, verify DUT rejects power swap request
           Else test power swap (tx/rx), then Force DUT to be sink or
           source only and verify rejecttion of power swap request.

        """
        # create objects for pd utilities
        self.dut_pd_utils = pd_console.PDConsoleUtils(self.usbpd)
        self.plankton_pd_utils = pd_console.PDConsoleUtils(self.plankton)
        self.connect_utils = pd_console.PDConnectionUtils(self.dut_pd_utils,
                                                          self.plankton_pd_utils)

        # Make sure PD support exists in the UART console
        if self.dut_pd_utils.verify_pd_console() == False:
            raise error.TestFail("pd command not present on console!")

        # Type C connection (PD contract) should exist at this point
        # For this test, the DUT must be connected to a Plankton.
        pd_port = self.connect_utils.find_dut_to_plankton_connection()
        if pd_port is None:
            raise error.TestFail("DUT to Plankton PD connection not found")
        dut_connect_state = self.dut_pd_utils.get_pd_state(pd_port)
        logging.info('Initial DUT connect state = %s', dut_connect_state)

        # Get DUT dualrole status
        if self.dut_pd_utils.is_pd_dual_role_enabled() == False:
            # DUT does not support dualrole mode, power swap
            # requests to the DUT should be rejected.
            logging.info('Power Swap support not advertised by DUT')
            self._test_power_swap_reject(pd_port)
            logging.info('Power Swap request rejected by DUT as expected')
        else:
            # Start with Plankton as source
            if self._set_plankton_power_role_to_src() == False:
                raise error.TestFail('Plankton not set to source')
            # DUT is dualrole in dual role mode. Test power role swap
            # operation intiated both by the DUT and Plankton.
            success = 0
            for attempt in xrange(self.POWER_SWAP_ITERATIONS):
                if attempt & 1:
                    direction = 'rx'
                else:
                    direction = 'tx'
                if self._attempt_power_swap(pd_port, direction):
                    success += 1
                new_state = self.dut_pd_utils.get_pd_state(pd_port)
                logging.info('New DUT power role = %s', new_state)

            if success != self.POWER_SWAP_ITERATIONS:
                raise error.TestFail('Failed %r power swap attempts' %
                                     (self.POWER_SWAP_ITERATIONS - success))

            # Force DUT to only support current power role
            if new_state == self.SRC:
                dual_mode = 'src'
            else:
                dual_mode = 'snk'
            logging.info('Setting dualrole mode to %s', dual_mode)
            self.dut_pd_utils.set_pd_dualrole(dual_mode)
            time.sleep(self.PD_ROLE_DELAY)
            # Expect behavior now is that DUT will reject power swap
            self._test_power_swap_reject(pd_port)
            logging.info('Power Swap request rejected by DUT as expected')
            # Restore DUT dual role operation
            self.dut_pd_utils.set_pd_dualrole('on')
            # Set connection back to default arrangement
            self.plankton_pd_utils.set_pd_dualrole('off')
            self.plankton_pd_utils.send_pd_command('fake disconnect 100 1000')