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