chameleon.py revision 24a9b64a364a434167ceff8544bf188aa0c897e0
13ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# Use of this source code is governed by a BSD-style license that can be
3b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch# found in the LICENSE file.
4b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
5014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochimport httplib
6014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochimport logging
7b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdochimport socket
8014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochimport time
9014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochimport xmlrpclib
10014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochfrom contextlib import contextmanager
11b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
12b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfrom PIL import Image
13b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
14b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdochfrom autotest_lib.client.bin import utils
15b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdochfrom autotest_lib.client.common_lib import error
16b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdochfrom autotest_lib.client.cros.chameleon import audio_board
17b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfrom autotest_lib.client.cros.chameleon import edid as edid_lib
18b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
19b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
20b8a8cc1952d61a2f3a2568848933943a543b5d3eBen MurdochCHAMELEON_PORT = 9992
21b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
22b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
23b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdochclass ChameleonConnectionError(error.TestError):
24b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """Indicates that connecting to Chameleon failed.
25b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
26b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    It is fatal to the test unless caught.
27b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """
28b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    pass
29b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
30b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
31b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass ChameleonConnection(object):
32b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """ChameleonConnection abstracts the network connection to the board.
33b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
34b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    ChameleonBoard and ChameleonPort use it for accessing Chameleon RPC.
35b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
36b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """
37b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
38b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def __init__(self, hostname, port=CHAMELEON_PORT):
39014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        """Constructs a ChameleonConnection.
40b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
41b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @param hostname: Hostname the chameleond process is running.
42b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @param port: Port number the chameleond process is listening on.
43b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
44b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @raise ChameleonConnectionError if connection failed.
45b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
46b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        self.chameleond_proxy = ChameleonConnection._create_server_proxy(
47b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                hostname, port)
48b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
49b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
50b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    @staticmethod
51b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def _create_server_proxy(hostname, port):
52b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Creates the chameleond server proxy.
53b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
54b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @param hostname: Hostname the chameleond process is running.
55b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @param port: Port number the chameleond process is listening on.
56b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
57b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return ServerProxy object to chameleond.
58b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
59b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @raise ChameleonConnectionError if connection failed.
60b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
61b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        remote = 'http://%s:%s' % (hostname, port)
62b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        chameleond_proxy = xmlrpclib.ServerProxy(remote, allow_none=True)
63b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        # Call a RPC to test.
64b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        try:
65b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            chameleond_proxy.GetSupportedPorts()
66b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        except (socket.error,
67b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                xmlrpclib.ProtocolError,
68b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                httplib.BadStatusLine) as e:
69b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            raise ChameleonConnectionError(e)
70b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return chameleond_proxy
71b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
72b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
73537ba893e2530051ec7f296e769fdd37bb4ae4a0Ben Murdochclass ChameleonBoard(object):
74b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """ChameleonBoard is an abstraction of a Chameleon board.
75b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
76b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    A Chameleond RPC proxy is passed to the construction such that it can
77b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    use this proxy to control the Chameleon board.
78b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
79b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    User can use host to access utilities that are not provided by
80b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    Chameleond XMLRPC server, e.g. send_file and get_file, which are provided by
81b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    ssh_host.SSHHost, which is the base class of ChameleonHost.
82014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
83b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """
84b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
85b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def __init__(self, chameleon_connection, chameleon_host=None):
86b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Construct a ChameleonBoard.
87b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
88b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @param chameleon_connection: ChameleonConnection object.
89b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @param chameleon_host: ChameleonHost object. None if this ChameleonBoard
90b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                               is not created by a ChameleonHost.
91b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
92b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        self.host = chameleon_host
93b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        self._chameleond_proxy = chameleon_connection.chameleond_proxy
94b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        if self._chameleond_proxy.HasAudioBoard():
95b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            self._audio_board = audio_board.AudioBoard(chameleon_connection)
96b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        else:
97b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            self._audio_board = None
98b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            logging.info('There is no audio board on this Chameleon.')
99b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
100b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def reset(self):
101b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Resets Chameleon board."""
102b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        self._chameleond_proxy.Reset()
103b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
10421efce637eb329c94f1323b6a2334a1c977e1a9dBen Murdoch
105b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def get_all_ports(self):
1061b268ca467c924004286c97bac133db489cf43d0Ben Murdoch        """Gets all the ports on Chameleon board which are connected.
1071b268ca467c924004286c97bac133db489cf43d0Ben Murdoch
108b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return: A list of ChameleonPort objects.
109b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
110b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        ports = self._chameleond_proxy.ProbePorts()
111b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return [ChameleonPort(self._chameleond_proxy, port) for port in ports]
1121b268ca467c924004286c97bac133db489cf43d0Ben Murdoch
1131b268ca467c924004286c97bac133db489cf43d0Ben Murdoch
11421efce637eb329c94f1323b6a2334a1c977e1a9dBen Murdoch    def get_all_inputs(self):
115b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Gets all the input ports on Chameleon board which are connected.
116014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
117b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return: A list of ChameleonPort objects.
118b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
119b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        ports = self._chameleond_proxy.ProbeInputs()
120b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return [ChameleonPort(self._chameleond_proxy, port) for port in ports]
121b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
122b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
123b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def get_all_outputs(self):
124b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Gets all the output ports on Chameleon board which are connected.
125b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
126b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return: A list of ChameleonPort objects.
127b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
128014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        ports = self._chameleond_proxy.ProbeOutputs()
129b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return [ChameleonPort(self._chameleond_proxy, port) for port in ports]
130b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
131b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
132b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def get_label(self):
133b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Gets the label which indicates the display connection.
134b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
135b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return: A string of the label, like 'hdmi', 'dp_hdmi', etc.
136b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
137b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        connectors = []
138b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        for port in self._chameleond_proxy.ProbeInputs():
139b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            if self._chameleond_proxy.HasVideoSupport(port):
140b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                connector = self._chameleond_proxy.GetConnectorType(port).lower()
141b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                connectors.append(connector)
142b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        # Eliminate duplicated ports. It simplifies the labels of dual-port
143b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        # devices, i.e. dp_dp categorized into dp.
144b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return '_'.join(sorted(set(connectors)))
145b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
146b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
147b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def get_audio_board(self):
148b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Gets the audio board on Chameleon.
149b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
150b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return: An AudioBoard object.
151b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
152b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return self._audio_board
153b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
154b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
155b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass ChameleonPort(object):
1563ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    """ChameleonPort is an abstraction of a general port of a Chameleon board.
157b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
158958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    It only contains some common methods shared with audio and video ports.
159014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
160014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    A Chameleond RPC proxy and an port_id are passed to the construction.
161014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    The port_id is the unique identity to the port.
162958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    """
163958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier
164958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier    def __init__(self, chameleond_proxy, port_id):
165b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """Construct a ChameleonPort.
166b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
167b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @param chameleond_proxy: Chameleond RPC proxy object.
168b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @param port_id: The ID of the input port.
169b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
170b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        self.chameleond_proxy = chameleond_proxy
171b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        self.port_id = port_id
172b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
173b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
174b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def get_connector_id(self):
175b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """Returns the connector ID.
176b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
1771e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block        @return: A number of connector ID.
1781e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block        """
179b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return self.port_id
180b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
181b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
182b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def get_connector_type(self):
183b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """Returns the human readable string for the connector type.
184b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
185b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @return: A string, like "VGA", "DVI", "HDMI", or "DP".
186b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        """
1873ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch        return self.chameleond_proxy.GetConnectorType(self.port_id)
1883ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch
189b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
190257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch    def has_audio_support(self):
191257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        """Returns if the input has audio support.
192257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch
193b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return: True if the input has audio support; otherwise, False.
194257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        """
195257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        return self.chameleond_proxy.HasAudioSupport(self.port_id)
196257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch
197257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch
198257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch    def has_video_support(self):
199257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        """Returns if the input has video support.
200257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch
201257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        @return: True if the input has video support; otherwise, False.
202257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        """
203257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        return self.chameleond_proxy.HasVideoSupport(self.port_id)
204257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch
205257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch
206257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch    def plug(self):
207257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        """Asserts HPD line to high, emulating plug."""
208257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        logging.info('Plug Chameleon port %d', self.port_id)
209b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        self.chameleond_proxy.Plug(self.port_id)
210b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
211b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
212b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def unplug(self):
213b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Deasserts HPD line to low, emulating unplug."""
214b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        logging.info('Unplug Chameleon port %d', self.port_id)
2151e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block        self.chameleond_proxy.Unplug(self.port_id)
2161e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block
2171e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block
218b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def set_plug(self, plug_status):
219b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """Sets plug/unplug by plug_status.
220b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
221b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @param plug_status: True to plug; False to unplug.
222b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
223b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        if plug_status:
224b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch            self.plug()
225b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        else:
226b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            self.unplug()
227b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2281e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block
2291b268ca467c924004286c97bac133db489cf43d0Ben Murdoch    @property
2301b268ca467c924004286c97bac133db489cf43d0Ben Murdoch    def plugged(self):
2311b268ca467c924004286c97bac133db489cf43d0Ben Murdoch        """
2321b268ca467c924004286c97bac133db489cf43d0Ben Murdoch        @returns True if this port is plugged to Chameleon, False otherwise.
2331b268ca467c924004286c97bac133db489cf43d0Ben Murdoch
2341b268ca467c924004286c97bac133db489cf43d0Ben Murdoch        """
2351b268ca467c924004286c97bac133db489cf43d0Ben Murdoch        return self.chameleond_proxy.IsPlugged(self.port_id)
2361e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block
237b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
238b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass ChameleonVideoInput(ChameleonPort):
239b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """ChameleonVideoInput is an abstraction of a video input port.
240b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
241b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    It contains some special methods to control a video input.
2421e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block    """
2431e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block
244b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    _DURATION_UNPLUG_FOR_EDID = 5
2451e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block    _TIMEOUT_VIDEO_STABLE_PROBE = 10
246b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    _EDID_ID_DISABLE = -1
247b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2481e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block    def __init__(self, chameleon_port):
2491e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block        """Construct a ChameleonVideoInput.
2501e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block
2511e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block        @param chameleon_port: A general ChameleonPort object.
252b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
253b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        self.chameleond_proxy = chameleon_port.chameleond_proxy
2541e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block        self.port_id = chameleon_port.port_id
2551e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block
2561e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block
2571e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block    def wait_video_input_stable(self, timeout=None):
258b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Waits the video input stable or timeout.
259b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
260b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @param timeout: The time period to wait for.
261b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
262b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return: True if the video input becomes stable within the timeout
263b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                 period; otherwise, False.
264b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
265b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return self.chameleond_proxy.WaitVideoInputStable(self.port_id,
266b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                                                          timeout)
267b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
268b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
269b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def read_edid(self):
2701b268ca467c924004286c97bac133db489cf43d0Ben Murdoch        """Reads the EDID.
2711b268ca467c924004286c97bac133db489cf43d0Ben Murdoch
272b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return: An Edid object or NO_EDID.
2731e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block        """
274b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        edid_binary = self.chameleond_proxy.ReadEdid(self.port_id)
275b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        if edid_binary is None:
276b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            return edid_lib.NO_EDID
277b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        # Read EDID without verify. It may be made corrupted as intended
278b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        # for the test purpose.
279b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        return edid_lib.Edid(edid_binary.data, skip_verify=True)
2801e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block
281b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
282b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def apply_edid(self, edid):
283b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        """Applies the given EDID.
284b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch
285b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        @param edid: An Edid object or NO_EDID.
286014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        """
287b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        if edid is edid_lib.NO_EDID:
288014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          self.chameleond_proxy.ApplyEdid(self.port_id, self._EDID_ID_DISABLE)
289b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        else:
290b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          edid_binary = xmlrpclib.Binary(edid.data)
291b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          edid_id = self.chameleond_proxy.CreateEdid(edid_binary)
292b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          self.chameleond_proxy.ApplyEdid(self.port_id, edid_id)
293b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch          self.chameleond_proxy.DestroyEdid(edid_id)
294b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch
295b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
296b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    @contextmanager
297b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def use_edid(self, edid):
298b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Uses the given EDID in a with statement.
299b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
300b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        It sets the EDID up in the beginning and restores to the original
3013fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch        EDID in the end. This function is expected to be used in a with
3023fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch        statement, like the following:
303b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
304b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            with chameleon_port.use_edid(edid):
305b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                do_some_test_on(chameleon_port)
306014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
307014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        @param edid: An EDID object.
308b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
309014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        # Set the EDID up in the beginning.
310014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        plugged = self.plugged
311b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        if plugged:
312b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch            self.unplug()
313b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
314b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        original_edid = self.read_edid()
315b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        logging.info('Apply EDID on port %d', self.port_id)
316257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        self.apply_edid(edid)
317b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
318b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        if plugged:
319b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch            time.sleep(self._DURATION_UNPLUG_FOR_EDID)
320b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch            self.plug()
321b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch            self.wait_video_input_stable(self._TIMEOUT_VIDEO_STABLE_PROBE)
322b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
323257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        try:
324014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            # Yeild to execute the with statement.
325014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            yield
326257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        finally:
327b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            # Restore the original EDID in the end.
328257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch            current_edid = self.read_edid()
329257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch            if original_edid.data != current_edid.data:
330b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                logging.info('Restore the original EDID.')
331b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                self.apply_edid(original_edid)
332b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
333b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
334b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def use_edid_file(self, filename):
335b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """Uses the given EDID file in a with statement.
336b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
337b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        It sets the EDID up in the beginning and restores to the original
338b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        EDID in the end. This function is expected to be used in a with
339b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        statement, like the following:
340b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
341b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch            with chameleon_port.use_edid_file(filename):
342b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                do_some_test_on(chameleon_port)
343b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
344b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @param filename: A path to the EDID file.
345b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
346b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return self.use_edid(edid_lib.Edid.from_file(filename))
347b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
348b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
349b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def fire_hpd_pulse(self, deassert_interval_usec, assert_interval_usec=None,
350b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                       repeat_count=1, end_level=1):
351b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
352b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """Fires one or more HPD pulse (low -> high -> low -> ...).
353b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
354b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @param deassert_interval_usec: The time in microsecond of the
355b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                deassert pulse.
356b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @param assert_interval_usec: The time in microsecond of the
357b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                assert pulse. If None, then use the same value as
358b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                deassert_interval_usec.
359b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @param repeat_count: The count of HPD pulses to fire.
360b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @param end_level: HPD ends with 0 for LOW (unplugged) or 1 for
361014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                HIGH (plugged).
362257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        """
363257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        self.chameleond_proxy.FireHpdPulse(
364257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch                self.port_id, deassert_interval_usec,
365014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                assert_interval_usec, repeat_count, int(bool(end_level)))
366b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
367b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
368b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def fire_mixed_hpd_pulses(self, widths):
369257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        """Fires one or more HPD pulses, starting at low, of mixed widths.
370257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch
371257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        One must specify a list of segment widths in the widths argument where
372257744e915dfc84d6d07a6b2accf8402d9ffc708Ben Murdoch        widths[0] is the width of the first low segment, widths[1] is that of
373014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        the first high segment, widths[2] is that of the second low segment...
374b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        etc. The HPD line stops at low if even number of segment widths are
375b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        specified; otherwise, it stops at high.
376b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
377014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        @param widths: list of pulse segment widths in usec.
378b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
379014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        self.chameleond_proxy.FireMixedHpdPulses(self.port_id, widths)
380014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
381b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
382b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def capture_screen(self):
383014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        """Captures Chameleon framebuffer.
384b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
385b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return An Image object.
386b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
387b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return Image.fromstring(
388b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                'RGB',
389b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                self.get_resolution(),
390b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                self.chameleond_proxy.DumpPixels(self.port_id).data)
391b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
392b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
393b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def get_resolution(self):
394014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        """Gets the source resolution.
395014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
396014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        @return: A (width, height) tuple.
397014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        """
398014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        # The return value of RPC is converted to a list. Convert it back to
399014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        # a tuple.
400014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        return tuple(self.chameleond_proxy.DetectResolution(self.port_id))
401b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
402b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
403b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def set_content_protection(self, enable):
404b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """Sets the content protection state on the port.
405b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
406014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        @param enable: True to enable; False to disable.
407b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
408b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        self.chameleond_proxy.SetContentProtection(self.port_id, enable)
409b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
410b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
411b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def is_content_protection_enabled(self):
412b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Returns True if the content protection is enabled on the port.
413014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
414b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return: True if the content protection is enabled; otherwise, False.
415b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
416b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return self.chameleond_proxy.IsContentProtectionEnabled(self.port_id)
417b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
418b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
419b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def is_video_input_encrypted(self):
420b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Returns True if the video input on the port is encrypted.
421b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
422014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        @return: True if the video input is encrypted; otherwise, False.
423b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
424014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        return self.chameleond_proxy.IsVideoInputEncrypted(self.port_id)
425b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
426b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
427b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def start_capturing_video(self, box=None):
428b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
429b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        Captures video frames. Asynchronous, returns immediately.
430014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
431b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @param box: int tuple, left, upper, right, lower pixel coordinates.
432b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                    Defines the rectangular boundary within which to capture.
433b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
434b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
435014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        if box is None:
436b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch            self.chameleond_proxy.StartCapturingVideo(self.port_id)
437b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        else:
438014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            self.chameleond_proxy.StartCapturingVideo(self.port_id, *box)
439b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
440b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
441b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def stop_capturing_video(self):
442b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
443b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        Stops the ongoing video frame capturing.
444b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
445b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
446b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        self.chameleond_proxy.StopCapturingVideo(self.port_id)
447b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
448b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
449b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def get_captured_frame_count(self):
450b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
451b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @return: int, the number of frames that have been captured.
452b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
453b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
454014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        return self.chameleond_proxy.GetCapturedFrameCount()
455b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
456014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
457b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def read_captured_frame(self, index):
458b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
459b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @param index: int, index of the desired captured frame.
460b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @return: xmlrpclib.Binary object containing a byte-array of the pixels.
461014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
462b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
463014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
464b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        frame = self.chameleond_proxy.ReadCapturedFrame(index)
465b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        return Image.fromstring('RGB',
466b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                                self.get_captured_resolution(),
467b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                                frame.data)
4681e0659c275bb392c045087af4f6b0d7565cb3d77Steve Block
469b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch
470b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def get_captured_checksums(self, start_index=0, stop_index=None):
471b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
472b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @param start_index: int, index of the frame to start with.
473014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        @param stop_index: int, index of the frame (excluded) to stop at.
474b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        @return: a list of checksums of frames captured.
4753fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch
4763fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch        """
477b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return self.chameleond_proxy.GetCapturedChecksums(start_index,
478b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                                                          stop_index)
479b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
480b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
481b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def get_captured_resolution(self):
482b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
483b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @return: (width, height) tuple, the resolution of captured frames.
484b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
485b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
486b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return self.chameleond_proxy.GetCapturedResolution()
487b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
488b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
489b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
490b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochclass ChameleonAudioInput(ChameleonPort):
491b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """ChameleonAudioInput is an abstraction of an audio input port.
492b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
493b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    It contains some special methods to control an audio input.
494b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """
495b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
496b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    def __init__(self, chameleon_port):
497b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Construct a ChameleonAudioInput.
498b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
499b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @param chameleon_port: A general ChameleonPort object.
500b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """
501b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        self.chameleond_proxy = chameleon_port.chameleond_proxy
502b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        self.port_id = chameleon_port.port_id
5033fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch
5043fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch
5053fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch    def start_capturing_audio(self):
506b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        """Starts capturing audio."""
507b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return self.chameleond_proxy.StartCapturingAudio(self.port_id)
508b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
509b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
510b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def stop_capturing_audio(self):
511b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """Stops capturing audio.
512014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
5133ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch        Returns:
5143ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch          A tuple (data, format).
5153ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch          data: The captured binary data.
5163ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch          format: A dict containing:
5173ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch            file_type: 'raw' or 'wav'.
5183ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch            sample_format: 'S32_LE' for 32-bit signed integer in little-endian.
5193ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch              Refer to aplay manpage for other formats.
5203ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch            channel: channel number.
5213ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch            rate: sampling rate.
5223ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch        """
5233ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch        rpc_data, data_format = self.chameleond_proxy.StopCapturingAudio(
524b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                self.port_id)
525b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return rpc_data.data, data_format
526b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
527b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
5283ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdochclass ChameleonAudioOutput(ChameleonPort):
5293ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    """ChameleonAudioOutput is an abstraction of an audio output port.
5303ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch
531014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    It contains some special methods to control an audio output.
532b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    """
533b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
534b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    def __init__(self, chameleon_port):
535b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """Construct a ChameleonAudioOutput.
5363ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch
537b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        @param chameleon_port: A general ChameleonPort object.
538b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        """
539b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        self.chameleond_proxy = chameleon_port.chameleond_proxy
540b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        self.port_id = chameleon_port.port_id
541b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch
542b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
543b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch    def start_playing_audio(self, path, data_format):
544b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        """Starts playing audio.
545b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch
546b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        @param path: The path to the file to play on Chameleon.
547b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        @param data_format: A dict containing data format. Currently Chameleon
548b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                            only accepts data format:
5491b268ca467c924004286c97bac133db489cf43d0Ben Murdoch                            dict(file_type='raw', sample_format='S32_LE',
550b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch                                 channel=8, rate=48000).
551b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
552b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch        """
553014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        self.chameleond_proxy.StartPlayingAudio(self.port_id, path, data_format)
554b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
555b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch
556b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch    def stop_playing_audio(self):
557b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        """Stops capturing audio."""
558b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch        self.chameleond_proxy.StopPlayingAudio(self.port_id)
559b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch
560b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
561b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdochdef make_chameleon_hostname(dut_hostname):
562b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch    """Given a DUT's hostname, returns the hostname of its Chameleon.
563b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch
564b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    @param dut_hostname: Hostname of a DUT.
565b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
566b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    @return Hostname of the DUT's Chameleon.
567014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    """
568b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    host_parts = dut_hostname.split('.')
569b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    host_parts[0] = host_parts[0] + '-chameleon'
570b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    return '.'.join(host_parts)
571014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
572b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
573b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdochdef create_chameleon_board(dut_hostname, args):
574b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch    """Given either DUT's hostname or argments, creates a ChameleonBoard object.
575b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch
576b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    If the DUT's hostname is in the lab zone, it connects to the Chameleon by
577b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    append the hostname with '-chameleon' suffix. If not, checks if the args
578b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    contains the key-value pair 'chameleon_host=IP'.
579b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
580b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    @param dut_hostname: Hostname of a DUT.
581b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    @param args: A string of arguments passed from the command line.
582b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
583014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    @return A ChameleonBoard object.
584b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
585b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    @raise ChameleonConnectionError if unknown hostname.
586b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    """
587b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    connection = None
588b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    hostname = make_chameleon_hostname(dut_hostname)
589b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    if utils.host_is_in_lab_zone(hostname):
590014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        connection = ChameleonConnection(hostname)
591b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch    else:
592b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        args_dict = utils.args_to_dict(args)
593b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        hostname = args_dict.get('chameleon_host', None)
594b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        port = args_dict.get('chameleon_port', CHAMELEON_PORT)
595b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        if hostname:
596014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            connection = ChameleonConnection(hostname, port)
597b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        else:
598b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch            raise ChameleonConnectionError('No chameleon_host is given in args')
599b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
600b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return ChameleonBoard(connection)
601b8e0da25ee8efac3bb05cd6b2730aafbd96119f4Ben Murdoch