1# 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 re
6import time
7
8from autotest_lib.client.bin import utils
9from autotest_lib.server.cros.servo import chrome_ec
10
11
12class PlanktonError(Exception):
13    pass
14
15
16class Plankton(chrome_ec.ChromeEC):
17    """Manages control of a Plankton PD console.
18
19    Plankton is a testing board developed to aid in USB type C debug and
20    control of various type C host devices. Plankton's features include the
21    simulation of charger, USB 2.0 pass through, USB 3.0 hub, and display port
22    pass through.
23
24    We control the PD console via the UART of a Servo board. Plankton
25    provides many interfaces that access the servo directly. It can
26    also be passed into the PDConsoleUtils as a console which then
27    provides methods to access the pd console.
28
29    This class is to abstract these interfaces.
30    """
31    # USB charging command delays in seconds.
32    USBC_COMMAND_DELAY = 0.5
33    # Plankton USBC commands.
34    USBC_ROLE = 'usbc_role'
35    USBC_MUX = 'usbc_mux'
36    RE_USBC_ROLE_VOLTAGE = r'src(\d+)v'
37    USBC_CHARGING_VOLTAGES = {
38        0: 'sink',
39        5: 'src5v',
40        12: 'src12v',
41        20: 'src20v'}
42    VBUS_VOLTAGE_MV = 'vbus_voltage'
43    VBUS_CURRENT_MA = 'vbus_current'
44    VBUS_POWER_MW = 'vbus_power'
45    # USBC PD states.
46    USBC_PD_STATES = {
47        'sink': 'SNK_READY',
48        'source': 'SRC_READY'}
49    POLL_STATE_SECS = 2
50
51    def __init__(self, servo, servod_proxy):
52        """Initialize and keep the servo object.
53
54        @param servo: A Servo object
55        @param servod_proxy: Servod proxy for plankton host
56        """
57        super(Plankton, self).__init__(servo)
58        # save servod proxy for methods that access Plankton servod
59        self._server = servod_proxy
60        self.init_io_expander()
61
62
63    def init_io_expander(self):
64        """Initializes Plankton IO expander register settings."""
65        if not int(self.get('debug_usb_sel')):
66            raise PlanktonError('debug_usb_sel (SW3) should be ON!! '
67                                'Please use CN15 to connect Plankton.')
68        self.set('typec_to_hub_sw', '0')
69        self.set('usb2_mux_sw', '1')
70        self.set('usb_dn_pwren', 'on')
71
72
73    def set(self, control_name, value):
74        """Sets the value of a control using servod.
75
76        @param control_name: plankton servo control item
77        @param value: value to set plankton servo control item
78        """
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
86        @param control_name: plankton servo control item
87        """
88        assert control_name
89        return self._server.get(control_name)
90
91
92    @property
93    def vbus_voltage(self):
94        """Gets Plankton VBUS voltage in volts."""
95        return float(self.get(self.VBUS_VOLTAGE_MV)) / 1000.0
96
97
98    @property
99    def vbus_current(self):
100        """Gets Plankton VBUS current in amps."""
101        return float(self.get(self.VBUS_CURRENT_MA)) / 1000.0
102
103
104    @property
105    def vbus_power(self):
106        """Gets Plankton charging power in watts."""
107        return float(self.get(self.VBUS_POWER_MW)) / 1000.0
108
109
110    def get_charging_voltages(self):
111        """Gets the lists of available charging voltages."""
112        return self.USBC_CHARGING_VOLTAGES.keys()
113
114
115    def charge(self, voltage):
116        """Sets Plankton to provide power at specific voltage.
117
118        @param voltage: Specified charging voltage in volts.
119        """
120        if voltage not in self.USBC_CHARGING_VOLTAGES:
121            raise PlanktonError('Invalid charging voltage: %s' % voltage)
122
123        self.set(self.USBC_ROLE, self.USBC_CHARGING_VOLTAGES[voltage])
124        time.sleep(self.USBC_COMMAND_DELAY)
125
126
127    @property
128    def charging_voltage(self):
129        """Gets current charging voltage."""
130        usbc_role = self.get(self.USBC_ROLE)
131        m = re.match(self.RE_USBC_ROLE_VOLTAGE, usbc_role)
132        if m:
133            return int(m.group(1))
134
135        if usbc_role == self.USBC_CHARGING_VOLTAGES[0]:
136            return 0
137
138        raise PlanktonError('Invalid USBC role: %s' % usbc_role)
139
140
141    def poll_pd_state(self, state):
142        """Polls until Plankton pd goes to the specific state.
143
144        @param state: Specified pd state name.
145        """
146        if state not in self.USBC_PD_STATES:
147            raise PlanktonError('Invalid state name: %s' % state)
148        utils.poll_for_condition(
149            lambda: self.get('pd_state') == self.USBC_PD_STATES[state],
150            exception=utils.TimeoutError('Plankton not in %s state '
151                                         'after %s seconds.' %
152                                         (self.USBC_PD_STATES[state],
153                                          self.POLL_STATE_SECS)),
154            timeout=self.POLL_STATE_SECS)
155
156
157    def set_usbc_mux(self, mux):
158        """Sets Plankton usbc_mux.
159
160        @param mux: Specified mux state name.
161        """
162        if mux not in ['dp', 'usb']:
163            raise PlanktonError('Invalid mux name: %s, '
164                                'should be either \'dp\' or \'usb\'.' % mux)
165        self.set(self.USBC_MUX, mux)
166        time.sleep(self.USBC_COMMAND_DELAY)
167