1370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott# Copyright 2016 The Chromium OS Authors. All rights reserved.
2370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott# Use of this source code is governed by a BSD-style license that can be
3370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott# found in the LICENSE file.
4370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
5370f9ef6f3d7fe53775224ec3e8006f0300aba89Scottimport logging
6370f9ef6f3d7fe53775224ec3e8006f0300aba89Scottimport re
7370f9ef6f3d7fe53775224ec3e8006f0300aba89Scottimport time
8370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
9370f9ef6f3d7fe53775224ec3e8006f0300aba89Scottfrom autotest_lib.client.common_lib import error
10370f9ef6f3d7fe53775224ec3e8006f0300aba89Scottfrom autotest_lib.server.cros.faft.firmware_test import FirmwareTest
11370f9ef6f3d7fe53775224ec3e8006f0300aba89Scottfrom autotest_lib.server.cros.servo import pd_console
12370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
13370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
14370f9ef6f3d7fe53775224ec3e8006f0300aba89Scottclass firmware_PDDataSwap(FirmwareTest):
15370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    """
16370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    Servo based USB PD data role swap test
17370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
18370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    Pass critera is all data role swaps complete, or
19370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    a reject control message is received from the DUT in the
20370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    cases where the swap does not complete.
21370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
22370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    """
23370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    version = 1
24370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
25370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    PD_ROLE_DELAY = 0.5
26370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    PD_CONNECT_DELAY = 4
27370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    PLANKTON_PORT = 0
28370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    DATA_SWAP_ITERATIONS = 10
29370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    # Upward facing port data role
30370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    UFP = 'UFP'
31370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    # Downward facing port data role
32370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    DFP = 'DFP'
33370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    # Plankton initiated data swap request
34370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    PLANKTON_SWAP_REQ = 'pd %d swap data' % PLANKTON_PORT
35370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    # Swap Result Tables
36370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    swap_attempt = {
37370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        ('rx', DFP): 0,
38370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        ('rx', UFP): 0,
39370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        ('tx', DFP): 0,
40370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        ('tx', UFP): 0
41370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    }
42370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    swap_failure = {
43370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        ('rx', DFP): 0,
44370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        ('rx', UFP): 0,
45370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        ('tx', DFP): 0,
46370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        ('tx', UFP): 0
47370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    }
48370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
49370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def _verify_plankton_connection(self, port):
50370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """Verify if DUT to Plankton PD connection
51370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
52370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        This method checks for a Plankton PD connection for the
53370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        given port by first verifying if a PD connection is present.
54370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        If found, then it uses a Plankton feature to force a PD disconnect.
55370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        If the port is no longer in the connected state, and following
56370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        a delay, is found to be back in the connected state, then
57370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        a DUT pd to Plankton connection is verified.
58370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
59370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @param port: DUT pd port to test
60370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
61370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @returns True if DUT to Plankton pd connection is verified
62370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """
63370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        DISCONNECT_TIME_SEC = 2
64370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # plankton console command to force PD disconnect
65370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        disc_cmd = 'fake_disconnect 100 %d' % (DISCONNECT_TIME_SEC*1000)
66370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Only check for Plankton if DUT has active PD connection
67370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        if self.dut_pd_utils.is_pd_connected(port):
68370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # Attempt to force PD disconnection
69370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            self.plankton_pd_utils.send_pd_command(disc_cmd)
70370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            time.sleep(self.PD_ROLE_DELAY)
71370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # Verify that DUT PD port is no longer connected
72370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            if self.dut_pd_utils.is_pd_connected(port) == False:
73370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                # Wait for disconnect timer and give time to reconnect
74370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                time.sleep(self.PD_CONNECT_DELAY + DISCONNECT_TIME_SEC)
75370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                if self.dut_pd_utils.is_pd_connected(port):
76370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                    logging.info('Plankton connection verfied on port %d', port)
77370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                    return True
78370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            else:
79370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                # Could have disconnected other port, allow it to reconnect
80370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                # before exiting.
81370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                time.sleep(self.PD_CONNECT_DELAY + DISCONNECT_TIME_SEC)
82370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        return False
83370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
84370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def _find_dut_to_plankton_connection(self):
85370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """Find the PD port which is connected to Plankton
86370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
87370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @returns DUT pd port number if found, None otherwise
88370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """
89370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        for port in xrange(self.dut_pd_utils.PD_MAX_PORTS):
90370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # Check for DUT to Plankton connection on port
91370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            if self._verify_plankton_connection(port):
92370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                # Plankton PD connection found so exit
93370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                return port
94370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        return None
95370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
96370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def _get_data_role(self, console, port):
97370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """Get data role of PD connection
98370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
99370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @param console: pd console object for uart access
100370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @param port: 0/1 pd port of current connection
101370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
102370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @returns: 'DFP' or 'UFP'
103370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """
104370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        role = console.get_pd_role(port)
105370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        m = re.search('[\w]+-([\w]+)', role)
106370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        return m.group(1)
107370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
108370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def _get_remote_role(self, local_role):
109370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """Invert data role
110370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
111370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @param local_role: data role to be flipped
112370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
113370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @returns: flipped data role value
114370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """
115370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        if local_role == self.DFP:
116370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            return self.UFP
117370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        else:
118370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            return self.DFP
119370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
120370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def _change_dut_power_role(self, port):
121370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """Force power role change via Plankton
122370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
123370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @param port: port of DUT PD connection
124370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
125370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @returns True is power role change is successful
126370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """
127370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        PLANKTON_SRC_VOLTAGE = 5
128370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        PLANKTON_SNK_VOLTAGE = 0
129370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        pd_state = self.dut_pd_utils.get_pd_state(port)
130370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        if pd_state == self.dut_pd_utils.SRC_CONNECT:
131370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # DUT is currently a SRC, so change to SNK
132370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # Use Plankton method to ensure power role change
133370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            self.plankton.charge(PLANKTON_SRC_VOLTAGE)
134370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        else:
135370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # DUT is currently a SNK, so change it to a SRC.
136370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            self.plankton.charge(PLANKTON_SNK_VOLTAGE)
137370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Wait for change to take place
138370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        time.sleep(self.PD_CONNECT_DELAY)
139370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        plankton_state = self.plankton_pd_utils.get_pd_state(0)
140370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Current Plankton state should equal DUT state when called
141370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        return bool(pd_state == plankton_state)
142370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
143370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def _send_data_swap_get_reply(self, console, port):
144370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """Send data swap request, get PD control msg reply
145370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
146370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        The PD console debug mode is enabled prior to sending
147370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        a pd data role swap request message. This allows the
148370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        control message reply to be extracted. The debug mode
149370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        is disabled prior to exiting.
150370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
151370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @param console: pd console object for uart access
152370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
153370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @ returns: PD control header message
154370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """
155370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Enable PD console debug mode to show control messages
156370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        console.enable_pd_console_debug()
157370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        cmd = 'pd %d swap data' % port
158370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        m = console.send_pd_command_get_output(cmd, ['RECV\s([\w]+)'])
159370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        ctrl_msg = int(m[0][1], 16) & console.PD_CONTROL_MSG_MASK
160370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        console.disable_pd_console_debug()
161370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        return ctrl_msg
162370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
163370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def _attempt_data_swap(self, pd_port, direction):
164370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """Perform a data role swap request
165370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
166370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        Data swap requests can be either initiated by the DUT or received
167370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        by the DUT. This direction determines which PD console is used
168370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        to initiate the swap command. The data role before and after
169370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        the swap command are compared to determine if it took place.
170370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
171370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        Even if data swap capability is advertised, a PD device is allowed
172370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        to reject the request. Therefore, not swapping isn't itself a
173370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        failure. When Plankton is used to initate the request, the debug
174370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        mode is enabled which allows the control message from the DUT to
175370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        be analyzed. If the swap does not occur, but the request is rejected
176370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        by the DUT then that is not counted as a failure.
177370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
178370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @param pd_port: DUT pd port value 0/1
179370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @param direction: rx or tx from the DUT perspective
180370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
181370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @returns PD control reply message for tx swaps, 0 otherwise
182370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """
183370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Get starting DUT data role
184370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        dut_dr = self._get_data_role(self.dut_pd_utils, pd_port)
185370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        self.swap_attempt[(direction, dut_dr)] += 1
186370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        if direction == 'tx':
187370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # Initiate swap request from the DUT
188370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            console = self.dut_pd_utils
189370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            cmd = 'pd %d swap data' % pd_port
190370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # Send the 'swap data' command
191370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            self.dut_pd_utils.send_pd_command(cmd)
192370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # Not using debug mode, so there is no reply message
193370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            ctrl = 0
194370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        else:
195370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # Initiate swap request from Plankton
196370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            console = self.plankton_pd_utils
197370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            ctrl  = self._send_data_swap_get_reply(console, self.PLANKTON_PORT)
198370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
199370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        time.sleep(self.PD_ROLE_DELAY)
200370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Get DUT current data role
201370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        swap_dr = self._get_data_role(self.dut_pd_utils, pd_port)
202370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        logging.info('%s swap attempt: prev = %s, new = %s, msg = %s',
203370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                      direction, dut_dr, swap_dr, ctrl)
204370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        if (dut_dr == swap_dr and
205370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                ctrl != self.dut_pd_utils.PD_CONTROL_MSG_DICT['Reject']):
206370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            self.swap_failure[(direction, dut_dr)] += 1
207370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        return ctrl
208370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
209370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def _execute_data_role_swap_test(self, pd_port):
210370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """Execute a series of data role swaps
211370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
212370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        Attempt both rx and tx data swaps, from perspective of DUT.
213370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        Even if the DUT advertises support, it can
214370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        reject swap requests when already in the desired data role. For
215370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        example many devices will not swap if already in DFP mode.
216370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        However, Plankton should always accept a request. Therefore,
217370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        when a swap failed on a rx swap, then that is followed by
218370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        a tx swap attempt.
219370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
220370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @param pd_port: port number of DUT PD connection
221370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """
222370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        for attempt in xrange(self.DATA_SWAP_ITERATIONS):
223370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # Use the same direction for every 2 loop iterations
224370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            if attempt & 2:
225370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                direction = 'tx'
226370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            else:
227370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                direction = 'rx'
228370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            ctrl_msg = self._attempt_data_swap(pd_port, direction)
229370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            if (direction == 'rx' and
230370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                    ctrl_msg ==
231370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                    self.dut_pd_utils.PD_CONTROL_MSG_DICT['Reject']):
232370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                # Use plankton initated swap to change roles
233370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                self._attempt_data_swap(pd_port, 'tx')
234370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
235370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def _test_data_swap_reject(self, pd_port):
236370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """Verify that data swap request is rejected
237370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
238370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        This tests the case where the DUT doesn't advertise support
239370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        for data swaps. A data request is sent by Plankton, and then
240370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        the control message checked to ensure the request was rejected.
241370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        In addition, the data role and connection state are verified
242370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        to remain unchanged.
243370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
244370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        @param pd_port: port for DUT pd connection
245370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """
246370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Get current DUT data role
247370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        dut_data_role = self._get_data_role(self.dut_pd_utils, pd_port)
248370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        dut_connect_state = self.dut_pd_utils.get_pd_state(pd_port)
249370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Send swap command from Plankton and get reply
250370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        ctrl_msg = self._send_data_swap_get_reply(self.plankton_pd_utils,
251370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                                                  self.PLANKTON_PORT)
252370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        if ctrl_msg != self.dut_pd_utils.PD_CONTROL_MSG_DICT['Reject']:
253370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            raise error.TestFail('Data Swap Req not rejected, returned %r' %
254370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                                 ctrl_msg)
255370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Get DUT current state
256370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        pd_state = self.dut_pd_utils.get_pd_state(pd_port)
257370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        if pd_state != dut_connect_state:
258370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            raise error.TestFail('PD not connected! pd_state = %r' %
259370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                                 pd_state)
260370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Since reject message was received, verify data role didn't change
261370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        curr_dr = self._get_data_role(self.dut_pd_utils, pd_port)
262370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        if curr_dr != dut_data_role:
263370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            raise error.TestFail('Unexpected PD data role change')
264370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
265370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def initialize(self, host, cmdline_args):
266370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        super(firmware_PDDataSwap, self).initialize(host, cmdline_args)
267370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Only run in normal mode
268370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        self.switcher.setup_mode('normal')
269370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        self.usbpd.send_command('chan 0')
270370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
271370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def cleanup(self):
272370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        self.usbpd.send_command('chan 0xffffffff')
273370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        super(firmware_PDDataSwap, self).cleanup()
274370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
275370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott    def run_once(self):
276370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """Exectue Data Role swap test.
277370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
278370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        1. Verify that pd console is accessible
279370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        2. Verify that DUT has a valid PD contract
280370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        3. Determine if DUT advertises support for data swaps
281370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        4. Test DUT initiated and received data swaps
282370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        5. Swap power roles if supported
283370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        6. Repeat DUT received data swap requests
284370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
285370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        """
286370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # create objects for pd utilities
287370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        self.dut_pd_utils = pd_console.PDConsoleUtils(self.usbpd)
288370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        self.plankton_pd_utils = pd_console.PDConsoleUtils(self.plankton)
289370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
290370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Make sure PD support exists in the UART console
291370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        if self.dut_pd_utils.verify_pd_console() == False:
292370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            raise error.TestFail("pd command not present on console!")
293370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
294370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Type C connection (PD contract) should exist at this point
295370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # For this test, the DUT must be connected to a Plankton.
296370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        pd_port = self._find_dut_to_plankton_connection()
297370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        if pd_port == None:
298370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            raise error.TestFail("DUT to Plankton PD connection not found")
299370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        dut_connect_state = self.dut_pd_utils.get_pd_state(pd_port)
300370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        logging.info('Initial DUT connect state = %s', dut_connect_state)
301370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
302370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Determine if DUT supports data role swaps
303370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        dr_swap_allowed = self.plankton_pd_utils.is_pd_flag_set(
304370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                self.PLANKTON_PORT, 'data_swap')
305370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # Get current DUT data role
306370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        dut_data_role = self._get_data_role(self.dut_pd_utils, pd_port)
307370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        logging.info('Starting DUT Data Role = %r', dut_data_role)
308370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
309370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # If data swaps are not allowed on the DUT, then still
310370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # attempt a data swap and verify that the request is
311370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # rejected by the DUT and that it remains connected and
312370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        # in the same role.
313370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        if dr_swap_allowed == False:
314370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            logging.info('Data Swap support not advertised by DUT')
315370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            self._test_data_swap_reject(pd_port)
316370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            logging.info('Data Swap request rejected by DUT as expected')
317370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott        else:
318370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # Data role swap support advertised, test this feature.
319370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            self._execute_data_role_swap_test(pd_port)
320370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
321370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # If DUT supports Power Role swap then attempt to change roles.
322370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # This way, data role swaps will be tested in both configurations.
323370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            if self.plankton_pd_utils.is_pd_flag_set(
324370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                     self.PLANKTON_PORT, 'power_swap'):
325370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                logging.info('\nDUT advertises Power Swap Support')
326370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                # Attempt to swap power roles
327370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                power_swap = self._change_dut_power_role(pd_port)
328370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                if power_swap:
329370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                    self._execute_data_role_swap_test(pd_port)
330370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                else:
331370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                    logging.warn('Power swap not successful!')
332370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                    logging.warn('Only tested with DUT in %s state',
333370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                                 dut_connect_state)
334370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            else:
335370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                logging.info('DUT does not advertise power swap support')
336370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
337370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            logging.info('***************** Swap Results ********************')
338370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            total_attempts = 0
339370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            total_failures = 0
340370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            for direction, role in self.swap_attempt.iterkeys():
341370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                logging.info('%s %s swap attempts = %d, failures = %d',
342370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                             direction, role,
343370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                             self.swap_attempt[(direction, role)],
344370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                             self.swap_failure[(direction, role)])
345370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                total_attempts += self.swap_attempt[(direction, role)]
346370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                total_failures += self.swap_failure[(direction, role)]
347370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott
348370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            # If any swap attempts were not successful, flag test as failure
349370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott            if total_failures:
350370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                raise error.TestFail('Data Swap Fail: Attempt = %d, Failure = %d' %
351370f9ef6f3d7fe53775224ec3e8006f0300aba89Scott                                 (total_attempts, total_failures))
352