blob: 95838f094b28788591ce528facaba974f5c79830 [file] [log] [blame]
Scottfe06ed82015-11-05 17:15:01 -08001# Copyright 2015 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
Scott07a848f2016-01-12 15:04:52 -08005import re
Scottfe06ed82015-11-05 17:15:01 -08006import time
7
8from autotest_lib.client.common_lib import error
9
10class PDConsoleUtils(object):
11 """ Provides a set of methods common to USB PD FAFT tests
12
13 Each instance of this class is associated with a particular
14 servo UART console. USB PD tests will typically use the console
15 command 'pd' and its subcommands to control/monitor Type C PD
16 connections. The servo object used for UART operations is
17 passed in and stored when this object is created.
18
19 """
20
21 SRC_CONNECT = 'SRC_READY'
22 SNK_CONNECT = 'SNK_READY'
23 SRC_DISC = 'SRC_DISCONNECTED'
24 SNK_DISC = 'SNK_DISCONNECTED'
Scott370f9ef2016-01-20 17:34:29 -080025 PD_MAX_PORTS = 2
Scottfe06ed82015-11-05 17:15:01 -080026
27 # dualrole input/ouput values
28 DUALROLE_QUERY_DELAY = 0.25
29 dual_index = {'on': 0, 'off': 1, 'snk': 2, 'src': 3}
30 dualrole_cmd = ['on', 'off', 'sink', 'source']
31 dualrole_resp = ['on', 'off', 'force sink', 'force source']
32
Scott07a848f2016-01-12 15:04:52 -080033 # Dictionary for 'pd 0/1 state' parsing
34 PD_STATE_DICT = {
35 'port': 'Port\s+([\w]+)',
36 'role': 'Role:\s+([\w]+-[\w]+)',
37 'pd_state': 'State:\s+([\w]+_[\w]+)',
38 'flags': 'Flags:\s+([\w]+)',
39 'polarity': '(CC\d)'
40 }
41
Scott370f9ef2016-01-20 17:34:29 -080042 # Dictionary for PD control message types
43 PD_CONTROL_MSG_MASK = 0x1f
44 PD_CONTROL_MSG_DICT = {
45 'GoodCRC': 1,
46 'GotoMin': 2,
47 'Accept': 3,
48 'Reject': 4,
49 'Ping': 5,
50 'PS_RDY': 6,
51 'Get_Source_Cap': 7,
52 'Get_Sink_Cap': 8,
53 'DR_Swap': 9,
54 'PR_Swap': 10,
55 'VCONN_Swap': 11,
56 'Wait': 12,
57 'Soft_Reset': 13
58 }
59
60 # Dictionary for PD firmware state flags
61 PD_STATE_FLAGS_DICT = {
62 'power_swap': 1 << 1,
63 'data_swap': 1 << 2,
64 'data_swap_active': 1 << 3,
65 'vconn_on': 1 << 12
66 }
67
Scottfe06ed82015-11-05 17:15:01 -080068 def __init__(self, console):
69 """ Console can be either usbpd, ec, or plankton_ec UART
70 This object with then be used by the class which creates
71 the PDConsoleUtils class to send/receive commands to UART
72 """
73 # save console for UART access functions
74 self.console = console
75
76 def send_pd_command(self, cmd):
77 """Send command to PD console UART
78
79 @param cmd: pd command string
80 """
81 self.console.send_command(cmd)
82
Scott07a848f2016-01-12 15:04:52 -080083 def send_pd_command_get_output(self, cmd, regexp):
84 """Send command to PD console, wait for response
85
86 @param cmd: pd command string
87 @param regexp: regular expression for desired output
88 """
89 return self.console.send_command_get_output(cmd, regexp)
90
Scottfe06ed82015-11-05 17:15:01 -080091 def verify_pd_console(self):
92 """Verify that PD commands exist on UART console
93
94 Send 'help' command to UART console
95 @returns: True if 'pd' is found, False if not
96 """
97
98 l = self.console.send_command_get_output('help', ['(pd)\s+([\w]+)'])
99 if l[0][1] == 'pd':
100 return True
101 else:
102 return False
103
104 def execute_pd_state_cmd(self, port):
105 """Get PD state for specified channel
106
Scott07a848f2016-01-12 15:04:52 -0800107 pd 0/1 state command gives produces 5 fields. The full response
108 line is captured and then parsed to extract each field to fill
109 the dict containing port, polarity, role, pd_state, and flags.
Scottfe06ed82015-11-05 17:15:01 -0800110
111 @param port: Type C PD port 0 or 1
112
113 @returns: A dict with the 5 fields listed above
114 """
115 cmd = 'pd'
116 subcmd = 'state'
117 pd_cmd = cmd +" " + str(port) + " " + subcmd
Scott07a848f2016-01-12 15:04:52 -0800118 # Two FW versions for this command, get full line.
119 m = self.send_pd_command_get_output(pd_cmd,
120 ['(Port.*) - (Role:.*)\r'])
Scottfe06ed82015-11-05 17:15:01 -0800121
Scott07a848f2016-01-12 15:04:52 -0800122 # Extract desired values from result string
Scottfe06ed82015-11-05 17:15:01 -0800123 state_result = {}
Scott07a848f2016-01-12 15:04:52 -0800124 for key, regexp in self.PD_STATE_DICT.iteritems():
125 value = re.search(regexp, m[0][0])
126 if value:
127 state_result[key] = value.group(1)
128 else:
129 raise error.TestFail('pd 0/1 state: %r value not found' % (key))
Scottfe06ed82015-11-05 17:15:01 -0800130
131 return state_result
132
133 def get_pd_state(self, port):
134 """Get the current PD state
135
136 @param port: Type C PD port 0/1
137 @returns: current pd state
138 """
139
140 pd_dict = self.execute_pd_state_cmd(port)
141 return pd_dict['pd_state']
142
143 def get_pd_port(self, port):
144 """Get the current PD port
145
146 @param port: Type C PD port 0/1
147 @returns: current pd state
148 """
149 pd_dict = self.execute_pd_state_cmd(port)
150 return pd_dict['port']
151
152 def get_pd_role(self, port):
153 """Get the current PD power role (source or sink)
154
155 @param port: Type C PD port 0/1
156 @returns: current pd state
157 """
158 pd_dict = self.execute_pd_state_cmd(port)
159 return pd_dict['role']
160
161 def get_pd_flags(self, port):
162 """Get the current PD flags
163
164 @param port: Type C PD port 0/1
165 @returns: current pd state
166 """
167 pd_dict = self.execute_pd_state_cmd(port)
168 return pd_dict['flags']
169
170 def get_pd_dualrole(self):
171 """Get the current PD dualrole setting
172
173 @returns: current PD dualrole setting
174 """
175 cmd = 'pd dualrole'
Scott07a848f2016-01-12 15:04:52 -0800176 dual_list = self.send_pd_command_get_output(cmd,
Scottfe06ed82015-11-05 17:15:01 -0800177 ['dual-role toggling:\s+([\w ]+)'])
178 return dual_list[0][1]
179
180 def set_pd_dualrole(self, value):
181 """Set pd dualrole
182
183 It can be set to either:
184 1. on
185 2. off
186 3. snk (force sink mode)
187 4. src (force source mode)
188 After setting, the current value is read to confirm that it
189 was set properly.
190
191 @param value: One of the 4 options listed
192 """
193 # Get string required for console command
194 dual_index = self.dual_index[value]
195 # Create console command
196 cmd = 'pd dualrole ' + self.dualrole_cmd[dual_index]
197 self.console.send_command(cmd)
198 time.sleep(self.DUALROLE_QUERY_DELAY)
199 # Get current setting to verify that command was successful
200 dual = self.get_pd_dualrole()
201 # If it doesn't match, then raise error
202 if dual != self.dualrole_resp[dual_index]:
203 raise error.TestFail("dualrole error: " +
204 self.dualrole_resp[dual_index] + " != "+dual)
205
206 def query_pd_connection(self):
207 """Determine if PD connection is present
208
209 Try the 'pd 0/1 state' command and see if it's in either
210 expected state of a conneciton. Record the port number
211 that has an active connection
212
213 @returns: dict with params port, connect, and state
214 """
215 status = {}
216 port = 0;
217 status['connect'] = False
218 status['port'] = port
219 state = self.get_pd_state(port)
220 # Check port 0 first
221 if state == self.SRC_CONNECT or state == self.SNK_CONNECT:
222 status['connect'] = True
223 status['role'] = state
224 else:
225 port = 1
226 status['port'] = port
227 state = self.get_pd_state(port)
228 # Check port 1
229 if state == self.SRC_CONNECT or state == self.SNK_CONNECT:
230 status['connect'] = True
231 status['role'] = state
232
233 return status
234
Scott370f9ef2016-01-20 17:34:29 -0800235 def disable_pd_console_debug(self):
236 """Turn off PD console debug
237
238 """
239 cmd = 'pd dump 0'
240 self.send_pd_command(cmd)
241
242 def enable_pd_console_debug(self):
243 """Enable PD console debug level 1
244
245 """
246 cmd = 'pd dump 1'
247 self.send_pd_command(cmd)
248
249 def is_pd_flag_set(self, port, key):
250 """Test a bit in PD protocol state flags
251
252 The flag word contains various PD protocol state information.
253 This method allows for a specific flag to be tested.
254
255 @param port: Port which has the active PD connection
256 @param key: dict key to retrieve the flag bit mapping
257
258 @returns True if the bit to be tested is set
259 """
260 pd_flags = self.get_pd_flags(port)
261 return bool(self.PD_STATE_FLAGS_DICT[key] & int(pd_flags, 16))
262
263 def is_pd_connected(self, port):
264 """Check if a PD connection is active
265
266 @param port: port to be used for pd console commands
267
268 @returns True if port is in connected state
269 """
270 state = self.get_pd_state(port)
271 return bool(state == self.SRC_CONNECT or state == self.SNK_CONNECT)
Scottfe06ed82015-11-05 17:15:01 -0800272