blob: 8154f34061b0dc20e03c1f13d429ec45dcbb0eb3 [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'
34 RE_USBC_ROLE_VOLTAGE = re.compile(r'src(\d+)v')
35 USBC_CHARGING_VOLTAGES = {
36 0: 'sink',
37 5: 'src5v',
38 12: 'src12v',
39 20: 'src20v'}
40 VBUS_VOLTAGE_MV = 'vbus_voltage'
41 VBUS_CURRENT_MA = 'vbus_current'
42 VBUS_POWER_MW = 'vbus_power'
Johny Lind5c46fa2015-03-26 17:24:40 +080043 # USBC PD states.
44 USBC_PD_STATES = {
45 'sink': 'SNK_READY',
46 'source': 'SRC_READY'}
47 POLL_STATE_SECS = 2
Rong Chang42ea8de2015-03-09 15:19:20 +080048
49
50 def __init__(self, args_dict=None):
51 """Sets up servo daemon communication.
52
53 @param args_dict: A dictionary contains plankton servod host and port.
54 Example: {'plankton_host': 'localhost',
55 'plankton_port': 9999}
56 """
57 if args_dict is None:
58 args_dict = {}
59 plankton_host = args_dict.get('plankton_host', self.DEFAULT_SERVO_HOST)
60 plankton_port = args_dict.get('plankton_port', self.DEFAULT_SERVO_PORT)
61 remote = 'http://%s:%s' % (plankton_host, plankton_port)
62 self._server = xmlrpclib.ServerProxy(remote)
Johny Lin1f255912015-03-12 12:38:11 +080063 self.init_io_expander()
64
65
66 def init_io_expander(self):
67 """Initializes Plankton IO expander register settings."""
68 if not int(self.get('debug_usb_sel')):
69 raise PlanktonError('debug_usb_sel (SW3) should be ON!! '
70 'Please use CN15 to connect Plankton.')
71 self.set('typec_to_hub_sw', '0')
72 self.set('usb2_mux_sw', '1')
73 self.set('usb_dn_pwren', 'on')
Rong Chang42ea8de2015-03-09 15:19:20 +080074
75
76 def set(self, control_name, value):
77 """Sets the value of a control using servod."""
78 assert control_name
79 self._server.set(control_name, value)
80
81
82 def get(self, control_name):
83 """Gets the value of a control from servod."""
84 assert control_name
85 return self._server.get(control_name)
86
87
88 @property
89 def vbus_voltage(self):
90 """Gets Plankton VBUS voltage in volts."""
91 return float(self.get(self.VBUS_VOLTAGE_MV)) / 1000.0
92
93
94 @property
95 def vbus_current(self):
96 """Gets Plankton VBUS current in amps."""
97 return float(self.get(self.VBUS_CURRENT_MA)) / 1000.0
98
99
100 @property
101 def vbus_power(self):
102 """Gets Plankton charging power in watts."""
103 return float(self.get(self.VBUS_POWER_MW)) / 1000.0
104
105
106 def get_charging_voltages(self):
107 """Gets the lists of available charging voltages."""
108 return self.USBC_CHARGING_VOLTAGES.keys()
109
110
111 def charge(self, voltage):
112 """Sets Plankton to provide power at specific voltage.
113
114 @param voltage: Specified charging voltage in volts.
115 """
116 if voltage not in self.USBC_CHARGING_VOLTAGES:
117 raise PlanktonError('Invalid charging voltage: %s' % voltage)
118
119 self.set(self.USBC_ROLE, self.USBC_CHARGING_VOLTAGES[voltage])
120 time.sleep(self.USBC_COMMAND_DELAY)
121
122
123 @property
124 def charging_voltage(self):
125 """Gets current charging voltage."""
126 usbc_role = self.get(self.USBC_ROLE)
127 match = self.RE_USBC_ROLE_VOLTAGE(usbc_role)
128 if match:
129 return int(match.group(1))
130
131 if usbc_role == self.USBC_CHARGING_VOLTAGES[0]:
132 return 0
133
134 raise PlanktonError('Invalid USBC role: %s' % usbc_role)
Johny Lind5c46fa2015-03-26 17:24:40 +0800135
136
137 def poll_pd_state(self, state):
138 """Polls until Plankton pd goes to the specific state.
139
140 @param state: Specified pd state name.
141 """
142 if state not in self.USBC_PD_STATES:
143 raise PlanktonError('Invalid state name: %s' % state)
144 utils.poll_for_condition(
145 lambda: self.get('pd_state') == self.USBC_PD_STATES[state],
146 exception=utils.TimeoutError('Plankton not in %s state '
147 'after %s seconds.' %
148 (self.USBC_PD_STATES[state],
149 self.POLL_STATE_SECS)),
150 timeout=self.POLL_STATE_SECS)