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
5"""This module provides the audio board interface."""
6
7import logging
8
9from autotest_lib.client.cros.chameleon import chameleon_audio_ids as ids
10
11
12class AudioBoard(object):
13    """AudioBoard is an abstraction of an audio board on a Chameleon board.
14
15    It provides methods to control audio board.
16
17    A ChameleonConnection object is passed to the construction.
18
19    """
20    def __init__(self, chameleon_connection):
21        """Constructs an AudioBoard.
22
23        @param chameleon_connection: A ChameleonConnection object.
24
25        """
26        self._audio_buses = {
27                1: AudioBus(1, chameleon_connection),
28                2: AudioBus(2, chameleon_connection)}
29
30        self._jack_plugger = None
31        try:
32            self._jack_plugger = AudioJackPlugger(chameleon_connection)
33        except AudioJackPluggerException:
34            logging.warning(
35                    'There is no jack plugger on this audio board.')
36            self._jack_plugger = None
37
38        self._bluetooth_controller = BluetoothController(chameleon_connection)
39
40
41    def get_audio_bus(self, bus_index):
42        """Gets an audio bus on this audio board.
43
44        @param bus_index: The bus index 1 or 2.
45
46        @returns: An AudioBus object.
47
48        """
49        return self._audio_buses[bus_index]
50
51
52    def get_jack_plugger(self):
53        """Gets an AudioJackPlugger on this audio board.
54
55        @returns: An AudioJackPlugger object if there is an audio jack plugger.
56                  None if there is no audio jack plugger.
57
58        """
59        return self._jack_plugger
60
61
62    def get_bluetooth_controller(self):
63        """Gets an BluetoothController on this audio board.
64
65        @returns: An BluetoothController object.
66
67        """
68        return self._bluetooth_controller
69
70
71class AudioBus(object):
72    """AudioBus is an abstraction of an audio bus on an audio board.
73
74    It provides methods to control audio bus.
75
76    A ChameleonConnection object is passed to the construction.
77
78    @properties:
79        bus_index: The bus index 1 or 2.
80
81    """
82    # Maps port id defined in chameleon_audio_ids to endpoint name used in
83    # chameleond audio bus API.
84    _PORT_ID_AUDIO_BUS_ENDPOINT_MAP = {
85            ids.ChameleonIds.LINEIN: 'Chameleon FPGA line-in',
86            ids.ChameleonIds.LINEOUT: 'Chameleon FPGA line-out',
87            ids.CrosIds.HEADPHONE: 'Cros device headphone',
88            ids.CrosIds.EXTERNAL_MIC: 'Cros device external microphone',
89            ids.PeripheralIds.SPEAKER: 'Peripheral speaker',
90            ids.PeripheralIds.MIC: 'Peripheral microphone',
91            ids.PeripheralIds.BLUETOOTH_DATA_RX:
92                    'Bluetooth module output',
93            ids.PeripheralIds.BLUETOOTH_DATA_TX:
94                    'Bluetooth module input'}
95
96
97    class AudioBusSnapshot(object):
98        """Abstracts the snapshot of AudioBus for user to restore it later."""
99        def __init__(self, endpoints):
100            """Initializes an AudioBusSnapshot.
101
102            @param endpoints: A set of endpoints to keep a copy.
103
104            """
105            self._endpoints = endpoints.copy()
106
107
108    def __init__(self, bus_index, chameleon_connection):
109        """Constructs an AudioBus.
110
111        @param bus_index: The bus index 1 or 2.
112        @param chameleon_connection: A ChameleonConnection object.
113
114        """
115        self.bus_index = bus_index
116        self._chameleond_proxy = chameleon_connection.chameleond_proxy
117        self._connected_endpoints = set()
118
119
120    def _get_endpoint_name(self, port_id):
121        """Gets the endpoint name used in audio bus API.
122
123        @param port_id: A string, that is, id in ChameleonIds, CrosIds, or
124                        PeripheralIds defined in chameleon_audio_ids.
125
126        @returns: The endpoint name for the port used in audio bus API.
127
128        """
129        return self._PORT_ID_AUDIO_BUS_ENDPOINT_MAP[port_id]
130
131
132    def _connect_endpoint(self, endpoint):
133        """Connects an endpoint to audio bus.
134
135        @param endpoint: An endpoint name in _PORT_ID_AUDIO_BUS_ENDPOINT_MAP.
136
137        """
138        logging.debug(
139                'Audio bus %s is connecting endpoint %s',
140                self.bus_index, endpoint)
141        self._chameleond_proxy.AudioBoardConnect(self.bus_index, endpoint)
142        self._connected_endpoints.add(endpoint)
143
144
145    def _disconnect_endpoint(self, endpoint):
146        """Disconnects an endpoint from audio bus.
147
148        @param endpoint: An endpoint name in _PORT_ID_AUDIO_BUS_ENDPOINT_MAP.
149
150        """
151        logging.debug(
152                'Audio bus %s is disconnecting endpoint %s',
153                self.bus_index, endpoint)
154        self._chameleond_proxy.AudioBoardDisconnect(self.bus_index, endpoint)
155        self._connected_endpoints.remove(endpoint)
156
157
158    def connect(self, port_id):
159        """Connects an audio port to this audio bus.
160
161        @param port_id: A string, that is, id in ChameleonIds, CrosIds, or
162                        PeripheralIds defined in chameleon_audio_ids.
163
164        """
165        endpoint = self._get_endpoint_name(port_id)
166        self._connect_endpoint(endpoint)
167
168
169    def disconnect(self, port_id):
170        """Disconnects an audio port from this audio bus.
171
172        @param port_id: A string, that is, id in ChameleonIds, CrosIds, or
173                        PeripheralIds defined in chameleon_audio_ids.
174
175        """
176        endpoint = self._get_endpoint_name(port_id)
177        self._disconnect_endpoint(endpoint)
178
179
180    def clear(self):
181        """Disconnects all audio port from this audio bus."""
182        self._disconnect_all_endpoints()
183
184
185    def _disconnect_all_endpoints(self):
186        """Disconnects all endpoints from this audio bus."""
187        for endpoint in self._connected_endpoints.copy():
188            self._disconnect_endpoint(endpoint)
189
190
191    def get_snapshot(self):
192        """Gets the snapshot of AudioBus so user can restore it later.
193
194        @returns: An AudioBus.AudioBusSnapshot object.
195
196        """
197        return self.AudioBusSnapshot(self._connected_endpoints)
198
199
200    def restore_snapshot(self, snapshot):
201        """Restore the snapshot.
202
203        @param: An AudioBus.AudioBusSnapshot object got from get_snapshot.
204
205        """
206        self._disconnect_all_endpoints()
207        logging.debug('Restoring snapshot with %s', snapshot._endpoints)
208        for endpoint in snapshot._endpoints:
209            self._connect_endpoint(endpoint)
210
211
212class AudioJackPluggerException(Exception):
213    """Errors in AudioJackPlugger."""
214    pass
215
216
217class AudioJackPlugger(object):
218    """AudioJackPlugger is an abstraction of plugger controlled by audio board.
219
220    There is a motor in the audio box which can plug/unplug 3.5mm 4-ring
221    audio cable to/from audio jack of Cros deivce.
222    This motor is controlled by audio board.
223
224    A ChameleonConnection object is passed to the construction.
225
226    """
227    def __init__(self, chameleon_connection):
228        """Constructs an AudioJackPlugger.
229
230        @param chameleon_connection: A ChameleonConnection object.
231
232        @raises:
233            AudioJackPluggerException if there is no jack plugger on
234            this audio board.
235
236        """
237        self._chameleond_proxy = chameleon_connection.chameleond_proxy
238        if not self._chameleond_proxy.AudioBoardHasJackPlugger():
239            raise AudioJackPluggerException(
240                'There is no jack plugger on audio board. '
241                'Perhaps the audio board is not connected to audio box.')
242
243
244    def plug(self):
245        """Plugs the audio cable into audio jack of Cros device."""
246        self._chameleond_proxy.AudioBoardAudioJackPlug()
247        logging.info('Plugged 3.5mm audio cable to Cros device')
248
249
250    def unplug(self):
251        """Unplugs the audio cable from audio jack of Cros device."""
252        self._chameleond_proxy.AudioBoardAudioJackUnplug()
253        logging.info('Unplugged 3.5mm audio cable from Cros device')
254
255
256class BluetoothController(object):
257    """An abstraction of bluetooth module on audio board.
258
259    There is a bluetooth module on the audio board. It can be controlled through
260    API provided by chameleon proxy.
261
262    """
263    def __init__(self, chameleon_connection):
264        """Constructs an BluetoothController.
265
266        @param chameleon_connection: A ChameleonConnection object.
267
268        """
269        self._chameleond_proxy = chameleon_connection.chameleond_proxy
270
271
272    def reset(self):
273        """Resets the bluetooth module."""
274        self._chameleond_proxy.AudioBoardResetBluetooth()
275        logging.info('Resets bluetooth module on audio board.')
276
277
278    def disable(self):
279        """Disables the bluetooth module."""
280        self._chameleond_proxy.AudioBoardDisableBluetooth()
281        logging.info('Disables bluetooth module on audio board.')
282
283
284    def is_enabled(self):
285        """Checks if the bluetooth module is enabled.
286
287        @returns: True if bluetooth module is enabled. False otherwise.
288
289        """
290        return self._chameleond_proxy.AudioBoardIsBluetoothEnabled()
291