blob: 0caa6464e9d727e363fc2133b1d6d8815b13b6c9 [file] [log] [blame]
Rong Chang42ea8de2015-03-09 15:19:20 +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
5import logging
6import re
7import time
8import xmlrpclib
9
Johny Lind5c46fa2015-03-26 17:24:40 +080010from autotest_lib.client.bin import utils
11
Rong Chang42ea8de2015-03-09 15:19:20 +080012
13class PlanktonError(Exception):
14 pass
15
16
17class Plankton(object):
18 """Manages control of a Plankton board via servod XMLRPC.
19
20 Plankton is a testing board developed to aid in USB type C debug and
21 control of various type C host devices. Plankton's features include the
22 simulation of charger, USB 2.0 pass through, USB 3.0 hub, and display port
23 pass through. This class manages setting up and communication with a servo
24 daemon (servod) process. It provides high level functions for setting and
25 reading USB type C role, mux and common controls.
26 """
27
28 DEFAULT_SERVO_HOST = 'localhost'
29 DEFAULT_SERVO_PORT = 9999
30 # USB charging command delays in seconds.
31 USBC_COMMAND_DELAY = 0.5
32 # Plankton USBC commands.
33 USBC_ROLE = 'usbc_role'
Rong Chang4408fec2015-03-10 19:29:08 +080034 USBC_MUX = 'usbc_mux'
Rong Chang42ea8de2015-03-09 15:19:20 +080035 RE_USBC_ROLE_VOLTAGE = re.compile(r'src(\d+)v')
36 USBC_CHARGING_VOLTAGES = {
37 0: 'sink',
38 5: 'src5v',
39 12: 'src12v',
40 20: 'src20v'}
41 VBUS_VOLTAGE_MV = 'vbus_voltage'
42 VBUS_CURRENT_MA = 'vbus_current'
43 VBUS_POWER_MW = 'vbus_power'
Johny Lind5c46fa2015-03-26 17:24:40 +080044 # USBC PD states.
45 USBC_PD_STATES = {
46 'sink': 'SNK_READY',
47 'source': 'SRC_READY'}
48 POLL_STATE_SECS = 2
Rong Chang42ea8de2015-03-09 15:19:20 +080049
50
51 def __init__(self, args_dict=None):
52 """Sets up servo daemon communication.
53
54 @param args_dict: A dictionary contains plankton servod host and port.
55 Example: {'plankton_host': 'localhost',
56 'plankton_port': 9999}
57 """
58 if args_dict is None:
59 args_dict = {}
60 plankton_host = args_dict.get('plankton_host', self.DEFAULT_SERVO_HOST)
61 plankton_port = args_dict.get('plankton_port', self.DEFAULT_SERVO_PORT)
62 remote = 'http://%s:%s' % (plankton_host, plankton_port)
63 self._server = xmlrpclib.ServerProxy(remote)
Johny Lin1f255912015-03-12 12:38:11 +080064 self.init_io_expander()
65
66
67 def init_io_expander(self):
68 """Initializes Plankton IO expander register settings."""
69 if not int(self.get('debug_usb_sel')):
70 raise PlanktonError('debug_usb_sel (SW3) should be ON!! '
71 'Please use CN15 to connect Plankton.')
72 self.set('typec_to_hub_sw', '0')
73 self.set('usb2_mux_sw', '1')
74 self.set('usb_dn_pwren', 'on')
Rong Chang42ea8de2015-03-09 15:19:20 +080075
76
77 def set(self, control_name, value):
78 """Sets the value of a control using servod."""
79 assert control_name
80 self._server.set(control_name, value)
81
82
83 def get(self, control_name):
84 """Gets the value of a control from servod."""
85 assert control_name
86 return self._server.get(control_name)
87
88
89 @property
90 def vbus_voltage(self):
91 """Gets Plankton VBUS voltage in volts."""
92 return float(self.get(self.VBUS_VOLTAGE_MV)) / 1000.0
93
94
95 @property
96 def vbus_current(self):
97 """Gets Plankton VBUS current in amps."""
98 return float(self.get(self.VBUS_CURRENT_MA)) / 1000.0
99
100
101 @property
102 def vbus_power(self):
103 """Gets Plankton charging power in watts."""
104 return float(self.get(self.VBUS_POWER_MW)) / 1000.0
105
106
107 def get_charging_voltages(self):
108 """Gets the lists of available charging voltages."""
109 return self.USBC_CHARGING_VOLTAGES.keys()
110
111
112 def charge(self, voltage):
113 """Sets Plankton to provide power at specific voltage.
114
115 @param voltage: Specified charging voltage in volts.
116 """
117 if voltage not in self.USBC_CHARGING_VOLTAGES:
118 raise PlanktonError('Invalid charging voltage: %s' % voltage)
119
120 self.set(self.USBC_ROLE, self.USBC_CHARGING_VOLTAGES[voltage])
121 time.sleep(self.USBC_COMMAND_DELAY)
122
123
124 @property
125 def charging_voltage(self):
126 """Gets current charging voltage."""
127 usbc_role = self.get(self.USBC_ROLE)
128 match = self.RE_USBC_ROLE_VOLTAGE(usbc_role)
129 if match:
130 return int(match.group(1))
131
132 if usbc_role == self.USBC_CHARGING_VOLTAGES[0]:
133 return 0
134
135 raise PlanktonError('Invalid USBC role: %s' % usbc_role)
Johny Lind5c46fa2015-03-26 17:24:40 +0800136
137
138 def poll_pd_state(self, state):
139 """Polls until Plankton pd goes to the specific state.
140
141 @param state: Specified pd state name.
142 """
143 if state not in self.USBC_PD_STATES:
144 raise PlanktonError('Invalid state name: %s' % state)
145 utils.poll_for_condition(
146 lambda: self.get('pd_state') == self.USBC_PD_STATES[state],
147 exception=utils.TimeoutError('Plankton not in %s state '
148 'after %s seconds.' %
149 (self.USBC_PD_STATES[state],
150 self.POLL_STATE_SECS)),
151 timeout=self.POLL_STATE_SECS)
Rong Chang4408fec2015-03-10 19:29:08 +0800152
153
154 def set_usbc_mux(self, mux):
155 """Sets Plankton usbc_mux.
156
157 @param mux: Specified mux state name.
158 """
159 if mux not in ['dp', 'usb']:
160 raise PlanktonError('Invalid mux name: %s, '
161 'should be either \'dp\' or \'usb\'.' % mux)
162 self.set(self.USBC_MUX, mux)
163 time.sleep(self.USBC_COMMAND_DELAY)
164