1/* Copyright (c) 2013 The Chromium 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
6#include <errno.h>
7#include <stdint.h>
8#include <syslog.h>
9
10#include "a2dp-codecs.h"
11#include "cras_a2dp_endpoint.h"
12#include "cras_a2dp_iodev.h"
13#include "cras_iodev.h"
14#include "cras_bt_constants.h"
15#include "cras_bt_endpoint.h"
16#include "cras_system_state.h"
17#include "cras_util.h"
18
19#define A2DP_SOURCE_ENDPOINT_PATH "/org/chromium/Cras/Bluetooth/A2DPSource"
20#define A2DP_SINK_ENDPOINT_PATH   "/org/chromium/Cras/Bluetooth/A2DPSink"
21
22
23/* Pointers for the only connected a2dp device. */
24static struct a2dp {
25	struct cras_iodev *iodev;
26	struct cras_bt_device *device;
27} connected_a2dp;
28
29static int cras_a2dp_get_capabilities(struct cras_bt_endpoint *endpoint,
30				      void *capabilities, int *len)
31{
32	a2dp_sbc_t *sbc_caps = capabilities;
33
34	if (*len < sizeof(*sbc_caps))
35		return -ENOSPC;
36
37	*len = sizeof(*sbc_caps);
38
39	/* Return all capabilities. */
40	sbc_caps->channel_mode = SBC_CHANNEL_MODE_MONO |
41			SBC_CHANNEL_MODE_DUAL_CHANNEL |
42			SBC_CHANNEL_MODE_STEREO |
43			SBC_CHANNEL_MODE_JOINT_STEREO;
44	sbc_caps->frequency = SBC_SAMPLING_FREQ_16000 |
45			SBC_SAMPLING_FREQ_32000 |
46			SBC_SAMPLING_FREQ_44100 |
47			SBC_SAMPLING_FREQ_48000;
48	sbc_caps->allocation_method = SBC_ALLOCATION_SNR |
49			SBC_ALLOCATION_LOUDNESS;
50	sbc_caps->subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8;
51	sbc_caps->block_length = SBC_BLOCK_LENGTH_4 |
52			SBC_BLOCK_LENGTH_8 |
53			SBC_BLOCK_LENGTH_12 |
54			SBC_BLOCK_LENGTH_16;
55	sbc_caps->min_bitpool = MIN_BITPOOL;
56	sbc_caps->max_bitpool = MAX_BITPOOL;
57
58	return 0;
59}
60
61static int cras_a2dp_select_configuration(struct cras_bt_endpoint *endpoint,
62					  void *capabilities, int len,
63					  void *configuration)
64{
65	a2dp_sbc_t *sbc_caps = capabilities;
66	a2dp_sbc_t *sbc_config = configuration;
67
68	/* Pick the highest configuration. */
69	if (sbc_caps->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) {
70		sbc_config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
71	} else if (sbc_caps->channel_mode & SBC_CHANNEL_MODE_STEREO) {
72		sbc_config->channel_mode = SBC_CHANNEL_MODE_STEREO;
73	} else if (sbc_caps->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) {
74		sbc_config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
75	} else if (sbc_caps->channel_mode & SBC_CHANNEL_MODE_MONO) {
76		sbc_config->channel_mode = SBC_CHANNEL_MODE_MONO;
77	} else {
78		syslog(LOG_WARNING, "No supported channel modes.");
79		return -ENOSYS;
80	}
81
82	if (sbc_caps->frequency & SBC_SAMPLING_FREQ_48000) {
83		sbc_config->frequency = SBC_SAMPLING_FREQ_48000;
84	} else if (sbc_caps->frequency & SBC_SAMPLING_FREQ_44100) {
85		sbc_config->frequency = SBC_SAMPLING_FREQ_44100;
86	} else if (sbc_caps->frequency & SBC_SAMPLING_FREQ_32000) {
87		sbc_config->frequency = SBC_SAMPLING_FREQ_32000;
88	} else if (sbc_caps->frequency & SBC_SAMPLING_FREQ_16000) {
89		sbc_config->frequency = SBC_SAMPLING_FREQ_16000;
90	} else {
91		syslog(LOG_WARNING, "No supported sampling frequencies.");
92		return -ENOSYS;
93	}
94
95	if (sbc_caps->allocation_method & SBC_ALLOCATION_LOUDNESS) {
96		sbc_config->allocation_method = SBC_ALLOCATION_LOUDNESS;
97	} else if (sbc_caps->allocation_method & SBC_ALLOCATION_SNR) {
98		sbc_config->allocation_method = SBC_ALLOCATION_SNR;
99	} else {
100		syslog(LOG_WARNING, "No supported allocation method.");
101		return -ENOSYS;
102	}
103
104	if (sbc_caps->subbands & SBC_SUBBANDS_8) {
105		sbc_config->subbands = SBC_SUBBANDS_8;
106	} else if (sbc_caps->subbands & SBC_SUBBANDS_4) {
107		sbc_config->subbands = SBC_SUBBANDS_4;
108	} else {
109		syslog(LOG_WARNING, "No supported subbands.");
110		return -ENOSYS;
111	}
112
113	if (sbc_caps->block_length & SBC_BLOCK_LENGTH_16) {
114		sbc_config->block_length = SBC_BLOCK_LENGTH_16;
115	} else if (sbc_caps->block_length & SBC_BLOCK_LENGTH_12) {
116		sbc_config->block_length = SBC_BLOCK_LENGTH_12;
117	} else if (sbc_caps->block_length & SBC_BLOCK_LENGTH_8) {
118		sbc_config->block_length = SBC_BLOCK_LENGTH_8;
119	} else if (sbc_caps->block_length & SBC_BLOCK_LENGTH_4) {
120		sbc_config->block_length = SBC_BLOCK_LENGTH_4;
121	} else {
122		syslog(LOG_WARNING, "No supported block length.");
123		return -ENOSYS;
124	}
125
126	sbc_config->min_bitpool = (sbc_caps->min_bitpool > MIN_BITPOOL
127				   ? sbc_caps->min_bitpool : MIN_BITPOOL);
128	sbc_config->max_bitpool = (sbc_caps->max_bitpool < MAX_BITPOOL
129				   ? sbc_caps->max_bitpool : MAX_BITPOOL);
130
131	return 0;
132}
133
134static void cras_a2dp_set_configuration(struct cras_bt_endpoint *endpoint,
135			    struct cras_bt_transport *transport)
136{
137	struct cras_bt_device *device;
138
139	device = cras_bt_transport_device(transport);
140	cras_bt_device_a2dp_configured(device);
141}
142
143static void cras_a2dp_suspend(struct cras_bt_endpoint *endpoint,
144			      struct cras_bt_transport *transport)
145{
146	struct cras_bt_device *device = cras_bt_transport_device(transport);
147	cras_a2dp_suspend_connected_device(device);
148}
149
150static void a2dp_transport_state_changed(struct cras_bt_endpoint *endpoint,
151					 struct cras_bt_transport *transport)
152{
153	if (connected_a2dp.iodev && transport) {
154		/* When pending message is received in bluez, try to aquire
155		 * the transport. */
156		if (cras_bt_transport_fd(transport) != -1 &&
157		    cras_bt_transport_state(transport) ==
158				CRAS_BT_TRANSPORT_STATE_PENDING)
159			cras_bt_transport_try_acquire(transport);
160	}
161}
162
163static struct cras_bt_endpoint cras_a2dp_endpoint = {
164	/* BlueZ connects the device A2DP Sink to our A2DP Source endpoint,
165	 * and the device A2DP Source to our A2DP Sink. It's best if you don't
166	 * think about it too hard.
167	 */
168	.object_path = A2DP_SOURCE_ENDPOINT_PATH,
169	.uuid = A2DP_SOURCE_UUID,
170	.codec = A2DP_CODEC_SBC,
171
172	.get_capabilities = cras_a2dp_get_capabilities,
173	.select_configuration = cras_a2dp_select_configuration,
174	.set_configuration = cras_a2dp_set_configuration,
175	.suspend = cras_a2dp_suspend,
176	.transport_state_changed = a2dp_transport_state_changed
177};
178
179int cras_a2dp_endpoint_create(DBusConnection *conn)
180{
181	return cras_bt_endpoint_add(conn, &cras_a2dp_endpoint);
182}
183
184void cras_a2dp_start(struct cras_bt_device *device)
185{
186	struct cras_bt_transport *transport = cras_a2dp_endpoint.transport;
187
188	if (!transport || device != cras_bt_transport_device(transport)) {
189		syslog(LOG_ERR, "Device and active transport not match.");
190		return;
191	}
192
193	if (connected_a2dp.iodev) {
194		syslog(LOG_WARNING,
195		       "Replacing existing endpoint configuration");
196		a2dp_iodev_destroy(connected_a2dp.iodev);
197	}
198
199	connected_a2dp.iodev = a2dp_iodev_create(transport);
200	connected_a2dp.device = cras_bt_transport_device(transport);
201
202	if (!connected_a2dp.iodev)
203		syslog(LOG_WARNING, "Failed to create a2dp iodev");
204}
205
206struct cras_bt_device *cras_a2dp_connected_device()
207{
208	return connected_a2dp.device;
209}
210
211void cras_a2dp_suspend_connected_device(struct cras_bt_device *device)
212{
213	if (connected_a2dp.device != device)
214		return;
215
216	if (connected_a2dp.iodev) {
217		syslog(LOG_INFO, "Destroying iodev for A2DP device");
218		a2dp_iodev_destroy(connected_a2dp.iodev);
219		connected_a2dp.iodev = NULL;
220		connected_a2dp.device = NULL;
221	}
222}
223