Home | History | Annotate | Download | only in firmware_PDPowerSwap
      1 # Copyright 2016 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 import logging
      6 import time
      7 
      8 from autotest_lib.client.common_lib import error
      9 from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
     10 from autotest_lib.server.cros.servo import pd_console
     11 
     12 
     13 class firmware_PDPowerSwap(FirmwareTest):
     14     """
     15     Servo based USB PD power role swap test.
     16 
     17     Pass critera is all power role swaps are successful if the DUT
     18     is dualrole capable. If not dualrole, then pass criteria is
     19     the DUT sending a reject message in response to swap request.
     20 
     21     """
     22     version = 1
     23 
     24     PD_ROLE_DELAY = 0.5
     25     PD_CONNECT_DELAY = 4
     26     PLANKTON_PORT = 0
     27     POWER_SWAP_ITERATIONS = 5
     28     # Source power role
     29     SRC ='SRC_READY'
     30     # Sink power role
     31     SNK = 'SNK_READY'
     32 
     33     def _set_plankton_power_role_to_src(self):
     34         """Force Plankton to act as a source
     35 
     36         @returns True if Plankton power role is source, false otherwise
     37         """
     38         PLANKTON_SRC_VOLTAGE = 20
     39         self.plankton.charge(PLANKTON_SRC_VOLTAGE)
     40         # Wait for change to take place
     41         time.sleep(self.PD_CONNECT_DELAY)
     42         plankton_state = self.plankton_pd_utils.get_pd_state(self.PLANKTON_PORT)
     43         # Current Plankton power role should be source
     44         return bool(plankton_state == self.SRC)
     45 
     46     def _send_power_swap_get_reply(self, console, port):
     47         """Send power swap request, get PD control msg reply
     48 
     49         The PD console debug mode is enabled prior to sending
     50         a pd power role swap request message. This allows the
     51         control message reply to be extracted. The debug mode
     52         is disabled prior to exiting.
     53 
     54         @param console: pd console object for uart access
     55 
     56         @returns: PD control header message
     57         """
     58         # Enable PD console debug mode to show control messages
     59         console.enable_pd_console_debug()
     60         cmd = 'pd %d swap power' % port
     61         m = console.send_pd_command_get_output(cmd, ['RECV\s([\w]+)'])
     62         ctrl_msg = int(m[0][1], 16) & console.PD_CONTROL_MSG_MASK
     63         console.disable_pd_console_debug()
     64         return ctrl_msg
     65 
     66     def _attempt_power_swap(self, pd_port, direction):
     67         """Perform a power role swap request
     68 
     69         Initiate a power role swap request on either the DUT or
     70         Plankton depending on the direction parameter. The power
     71         role swap is then verified to have taken place.
     72 
     73         @param pd_port: DUT pd port value 0/1
     74         @param direction: rx or tx from the DUT perspective
     75 
     76         @returns True if power swap is successful
     77         """
     78         # Get DUT current power role
     79         dut_pr = self.dut_pd_utils.get_pd_state(pd_port)
     80         if direction == 'rx':
     81             console = self.plankton_pd_utils
     82             port = self.PLANKTON_PORT
     83         else:
     84             console = self.dut_pd_utils
     85             port = pd_port
     86         # Send power swap request
     87         self._send_power_swap_get_reply(console, port)
     88         time.sleep(self.PD_CONNECT_DELAY)
     89         # Get Plankton power role
     90         plankton_pr = self.plankton_pd_utils.get_pd_state(self.PLANKTON_PORT)
     91         return bool(dut_pr == plankton_pr)
     92 
     93     def _test_power_swap_reject(self, pd_port):
     94         """Verify that a power swap request is rejected
     95 
     96         This tests the case where the DUT isn't in dualrole mode.
     97         A power swap request is sent by Plankton, and then
     98         the control message checked to ensure the request was rejected.
     99         In addition, the connection state is verified to not have
    100         changed.
    101 
    102         @param pd_port: port for DUT pd connection
    103         """
    104         # Get current DUT power role
    105         dut_power_role = self.dut_pd_utils.get_pd_state(pd_port)
    106         # Send swap command from Plankton and get reply
    107         ctrl_msg = self._send_power_swap_get_reply(self.plankton_pd_utils,
    108                                                    self.PLANKTON_PORT)
    109         if ctrl_msg != self.dut_pd_utils.PD_CONTROL_MSG_DICT['Reject']:
    110             raise error.TestFail('Power Swap Req not rejected, returned %r' %
    111                                  ctrl_msg)
    112         # Get DUT current state
    113         pd_state = self.dut_pd_utils.get_pd_state(pd_port)
    114         if pd_state != dut_power_role:
    115             raise error.TestFail('PD not connected! pd_state = %r' %
    116                                  pd_state)
    117 
    118     def initialize(self, host, cmdline_args):
    119         super(firmware_PDPowerSwap, self).initialize(host, cmdline_args)
    120         # Only run in normal mode
    121         self.switcher.setup_mode('normal')
    122         # Turn off console prints, except for USBPD.
    123         self.usbpd.send_command('chan 0x08000000')
    124 
    125     def cleanup(self):
    126         self.usbpd.send_command('chan 0xffffffff')
    127         super(firmware_PDPowerSwap, self).cleanup()
    128 
    129     def run_once(self):
    130         """Execute Power Role swap test.
    131 
    132         1. Verify that pd console is accessible
    133         2. Verify that DUT has a valid PD contract and connected to Plankton
    134         3. Determine if DUT is in dualrole mode
    135         4. If not dualrole mode, verify DUT rejects power swap request
    136            Else test power swap (tx/rx), then Force DUT to be sink or
    137            source only and verify rejecttion of power swap request.
    138 
    139         """
    140         # create objects for pd utilities
    141         self.dut_pd_utils = pd_console.PDConsoleUtils(self.usbpd)
    142         self.plankton_pd_utils = pd_console.PDConsoleUtils(self.plankton)
    143         self.connect_utils = pd_console.PDConnectionUtils(self.dut_pd_utils,
    144                                                           self.plankton_pd_utils)
    145 
    146         # Make sure PD support exists in the UART console
    147         if self.dut_pd_utils.verify_pd_console() == False:
    148             raise error.TestFail("pd command not present on console!")
    149 
    150         # Type C connection (PD contract) should exist at this point
    151         # For this test, the DUT must be connected to a Plankton.
    152         pd_port = self.connect_utils.find_dut_to_plankton_connection()
    153         if pd_port is None:
    154             raise error.TestFail("DUT to Plankton PD connection not found")
    155         dut_connect_state = self.dut_pd_utils.get_pd_state(pd_port)
    156         logging.info('Initial DUT connect state = %s', dut_connect_state)
    157 
    158         # Get DUT dualrole status
    159         if self.dut_pd_utils.is_pd_dual_role_enabled() == False:
    160             # DUT does not support dualrole mode, power swap
    161             # requests to the DUT should be rejected.
    162             logging.info('Power Swap support not advertised by DUT')
    163             self._test_power_swap_reject(pd_port)
    164             logging.info('Power Swap request rejected by DUT as expected')
    165         else:
    166             # Start with Plankton as source
    167             if self._set_plankton_power_role_to_src() == False:
    168                 raise error.TestFail('Plankton not set to source')
    169             # DUT is dualrole in dual role mode. Test power role swap
    170             # operation intiated both by the DUT and Plankton.
    171             success = 0
    172             for attempt in xrange(self.POWER_SWAP_ITERATIONS):
    173                 if attempt & 1:
    174                     direction = 'rx'
    175                 else:
    176                     direction = 'tx'
    177                 if self._attempt_power_swap(pd_port, direction):
    178                     success += 1
    179                 new_state = self.dut_pd_utils.get_pd_state(pd_port)
    180                 logging.info('New DUT power role = %s', new_state)
    181 
    182             if success != self.POWER_SWAP_ITERATIONS:
    183                 raise error.TestFail('Failed %r power swap attempts' %
    184                                      (self.POWER_SWAP_ITERATIONS - success))
    185 
    186             # Force DUT to only support current power role
    187             if new_state == self.SRC:
    188                 dual_mode = 'src'
    189             else:
    190                 dual_mode = 'snk'
    191             logging.info('Setting dualrole mode to %s', dual_mode)
    192             self.dut_pd_utils.set_pd_dualrole(dual_mode)
    193             time.sleep(self.PD_ROLE_DELAY)
    194             # Expect behavior now is that DUT will reject power swap
    195             self._test_power_swap_reject(pd_port)
    196             logging.info('Power Swap request rejected by DUT as expected')
    197             # Restore DUT dual role operation
    198             self.dut_pd_utils.set_pd_dualrole('on')
    199             # Set connection back to default arrangement
    200             self.plankton_pd_utils.set_pd_dualrole('off')
    201             self.plankton_pd_utils.send_pd_command('fake disconnect 100 1000')
    202 
    203