• 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_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. */
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 = 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 
cras_a2dp_select_configuration(struct cras_bt_endpoint * endpoint,void * capabilities,int len,void * configuration)61 static 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 
cras_a2dp_set_configuration(struct cras_bt_endpoint * endpoint,struct cras_bt_transport * transport)134 static 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 
cras_a2dp_suspend(struct cras_bt_endpoint * endpoint,struct cras_bt_transport * transport)143 static 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 
a2dp_transport_state_changed(struct cras_bt_endpoint * endpoint,struct cras_bt_transport * transport)150 static 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 
163 static 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 
cras_a2dp_endpoint_create(DBusConnection * conn)179 int cras_a2dp_endpoint_create(DBusConnection *conn)
180 {
181 	return cras_bt_endpoint_add(conn, &cras_a2dp_endpoint);
182 }
183 
cras_a2dp_start(struct cras_bt_device * device)184 void 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 
cras_a2dp_connected_device()206 struct cras_bt_device *cras_a2dp_connected_device()
207 {
208 	return connected_a2dp.device;
209 }
210 
cras_a2dp_suspend_connected_device(struct cras_bt_device * device)211 void 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