1 /* Copyright (c) 2013 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
6 #include <stdint.h>
7 #include <syslog.h>
8 #include <sys/socket.h>
9 #include <unistd.h>
10
11 #include "cras_a2dp_endpoint.h"
12 #include "cras_bt_adapter.h"
13 #include "cras_bt_constants.h"
14 #include "cras_bt_profile.h"
15 #include "cras_hfp_ag_profile.h"
16 #include "cras_hfp_info.h"
17 #include "cras_hfp_iodev.h"
18 #include "cras_hfp_slc.h"
19 #include "cras_system_state.h"
20 #include "utlist.h"
21
22 #define STR(s) #s
23 #define VSTR(id) STR(id)
24
25 #define HFP_AG_PROFILE_NAME "Hands-Free Voice gateway"
26 #define HFP_AG_PROFILE_PATH "/org/chromium/Cras/Bluetooth/HFPAG"
27 #define HFP_VERSION_1_5 0x0105
28 #define HSP_AG_PROFILE_NAME "Headset Voice gateway"
29 #define HSP_AG_PROFILE_PATH "/org/chromium/Cras/Bluetooth/HSPAG"
30 #define HSP_VERSION_1_2 0x0102
31
32 #define HSP_AG_RECORD \
33 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" \
34 "<record>" \
35 " <attribute id=\"0x0001\">" \
36 " <sequence>" \
37 " <uuid value=\"" HSP_AG_UUID "\" />" \
38 " <uuid value=\"" GENERIC_AUDIO_UUID "\" />" \
39 " </sequence>" \
40 " </attribute>" \
41 " <attribute id=\"0x0004\">" \
42 " <sequence>" \
43 " <sequence>" \
44 " <uuid value=\"0x0100\" />" \
45 " </sequence>" \
46 " <sequence>" \
47 " <uuid value=\"0x0003\" />" \
48 " <uint8 value=\"0x0c\" />" \
49 " </sequence>" \
50 " </sequence>" \
51 " </attribute>" \
52 " <attribute id=\"0x0005\">" \
53 " <sequence>" \
54 " <uuid value=\"0x1002\" />" \
55 " </sequence>" \
56 " </attribute>" \
57 " <attribute id=\"0x0009\">" \
58 " <sequence>" \
59 " <sequence>" \
60 " <uuid value=\"" HSP_HS_UUID "\" />" \
61 " <uint16 value=\"" VSTR(HSP_VERSION_1_2) "\" />" \
62 " </sequence>" \
63 " </sequence>" \
64 " </attribute>" \
65 " <attribute id=\"0x0100\">" \
66 " <text value=\"" HSP_AG_PROFILE_NAME "\" />" \
67 " </attribute>" \
68 " <attribute id=\"0x0301\" >" \
69 " <uint8 value=\"0x01\" />" \
70 " </attribute>" \
71 "</record>"
72
73
74 /* Object representing the audio gateway role for HFP/HSP.
75 * Members:
76 * idev - The input iodev for HFP/HSP.
77 * odev - The output iodev for HFP/HSP.
78 * info - The hfp_info object for SCO audio.
79 * slc_handle - The service level connection.
80 * device - The bt device associated with this audio gateway.
81 * a2dp_delay_retries - The number of retries left to delay starting
82 * the hfp/hsp audio gateway to wait for a2dp connection.
83 * conn - The dbus connection used to send message to bluetoothd.
84 * profile - The profile enum of this audio gateway.
85 */
86 struct audio_gateway {
87 struct cras_iodev *idev;
88 struct cras_iodev *odev;
89 struct hfp_info *info;
90 struct hfp_slc_handle *slc_handle;
91 struct cras_bt_device *device;
92 int a2dp_delay_retries;
93 DBusConnection *conn;
94 enum cras_bt_device_profile profile;
95 struct audio_gateway *prev, *next;
96 };
97
98 static struct audio_gateway *connected_ags;
99
destroy_audio_gateway(struct audio_gateway * ag)100 static void destroy_audio_gateway(struct audio_gateway *ag)
101 {
102 DL_DELETE(connected_ags, ag);
103
104 if (ag->idev)
105 hfp_iodev_destroy(ag->idev);
106 if (ag->odev)
107 hfp_iodev_destroy(ag->odev);
108 if (ag->info) {
109 if (hfp_info_running(ag->info))
110 hfp_info_stop(ag->info);
111 hfp_info_destroy(ag->info);
112 }
113 if (ag->slc_handle)
114 hfp_slc_destroy(ag->slc_handle);
115
116 /* If the bt device is not using a2dp, do a deeper clean up
117 * to force disconnect it. */
118 if (!cras_bt_device_has_a2dp(ag->device))
119 cras_bt_device_disconnect(ag->conn, ag->device);
120
121 free(ag);
122 }
123
124 /* Checks if there already a audio gateway connected for device. */
has_audio_gateway(struct cras_bt_device * device)125 static int has_audio_gateway(struct cras_bt_device *device)
126 {
127 struct audio_gateway *ag;
128 DL_FOREACH(connected_ags, ag) {
129 if (ag->device == device)
130 return 1;
131 }
132 return 0;
133 }
134
cras_hfp_ag_release(struct cras_bt_profile * profile)135 static void cras_hfp_ag_release(struct cras_bt_profile *profile)
136 {
137 cras_hfp_ag_suspend();
138 }
139
140 /* Callback triggered when SLC is initialized. */
cras_hfp_ag_slc_initialized(struct hfp_slc_handle * handle)141 static int cras_hfp_ag_slc_initialized(struct hfp_slc_handle *handle)
142 {
143 struct audio_gateway *ag;
144
145 DL_SEARCH_SCALAR(connected_ags, ag, slc_handle, handle);
146 if (!ag)
147 return -EINVAL;
148
149 /* Defer the starting of audio gateway to bt_device. */
150 return cras_bt_device_audio_gateway_initialized(ag->device);
151 }
152
cras_hfp_ag_slc_disconnected(struct hfp_slc_handle * handle)153 static int cras_hfp_ag_slc_disconnected(struct hfp_slc_handle *handle)
154 {
155 struct audio_gateway *ag;
156
157 DL_SEARCH_SCALAR(connected_ags, ag, slc_handle, handle);
158 if (!ag)
159 return -EINVAL;
160
161 destroy_audio_gateway(ag);
162 return 0;
163 }
164
check_for_conflict_ag(struct cras_bt_device * new_connected)165 static int check_for_conflict_ag(struct cras_bt_device *new_connected)
166 {
167 struct audio_gateway *ag;
168
169 /* Check if there's already an A2DP/HFP device. */
170 DL_FOREACH(connected_ags, ag) {
171 if (cras_bt_device_has_a2dp(ag->device))
172 return -1;
173 }
174
175 /* Check if there's already an A2DP-only device. */
176 if (cras_a2dp_connected_device() &&
177 cras_bt_device_supports_profile(
178 new_connected, CRAS_BT_DEVICE_PROFILE_A2DP_SINK))
179 return -1;
180
181 return 0;
182 }
183
possibly_remove_conflict_dev(void * data)184 static void possibly_remove_conflict_dev(void *data)
185 {
186 struct cras_bt_device *device = (struct cras_bt_device *)data;
187 struct audio_gateway *ag, *new_ag = NULL;
188 struct cras_bt_device *a2dp_device;
189
190 /* Check if the device is still connected. */
191 DL_FOREACH(connected_ags, ag) {
192 if (ag->device == device)
193 new_ag = ag;
194 }
195 if (!new_ag)
196 return;
197
198 /* Kick out any previously connected hfp iodev. */
199 DL_FOREACH(connected_ags, ag) {
200 if (ag == new_ag)
201 continue;
202 destroy_audio_gateway(ag);
203 }
204
205 /* Kick out any previously connected a2dp iodev. */
206 a2dp_device = cras_a2dp_connected_device();
207 if (a2dp_device && a2dp_device != device) {
208 cras_a2dp_suspend_connected_device(a2dp_device);
209 cras_bt_device_disconnect(new_ag->conn, a2dp_device);
210 }
211 }
212
cras_hfp_ag_new_connection(DBusConnection * conn,struct cras_bt_profile * profile,struct cras_bt_device * device,int rfcomm_fd)213 static int cras_hfp_ag_new_connection(DBusConnection *conn,
214 struct cras_bt_profile *profile,
215 struct cras_bt_device *device,
216 int rfcomm_fd)
217 {
218 struct audio_gateway *ag;
219
220 if (has_audio_gateway(device)) {
221 syslog(LOG_ERR, "Audio gateway exists when %s connects for profile %s",
222 cras_bt_device_name(device), profile->name);
223 close(rfcomm_fd);
224 return 0;
225 }
226
227 if (check_for_conflict_ag(device))
228 return -1;
229
230 cras_bt_device_set_append_iodev_cb(device, possibly_remove_conflict_dev);
231 ag = (struct audio_gateway *)calloc(1, sizeof(*ag));
232 ag->device = device;
233 ag->conn = conn;
234 ag->profile = cras_bt_device_profile_from_uuid(profile->uuid);
235 ag->slc_handle = hfp_slc_create(rfcomm_fd,
236 0,
237 device,
238 cras_hfp_ag_slc_initialized,
239 cras_hfp_ag_slc_disconnected);
240 DL_APPEND(connected_ags, ag);
241 return 0;
242 }
243
cras_hfp_ag_request_disconnection(struct cras_bt_profile * profile,struct cras_bt_device * device)244 static void cras_hfp_ag_request_disconnection(struct cras_bt_profile *profile,
245 struct cras_bt_device *device)
246 {
247 struct audio_gateway *ag;
248 DL_FOREACH(connected_ags, ag) {
249 if (ag->slc_handle && ag->device == device)
250 destroy_audio_gateway(ag);
251 }
252 }
253
cras_hfp_ag_cancel(struct cras_bt_profile * profile)254 static void cras_hfp_ag_cancel(struct cras_bt_profile *profile)
255 {
256 }
257
258 static struct cras_bt_profile cras_hfp_ag_profile = {
259 .name = HFP_AG_PROFILE_NAME,
260 .object_path = HFP_AG_PROFILE_PATH,
261 .uuid = HFP_AG_UUID,
262 .version = HFP_VERSION_1_5,
263 .role = NULL,
264 .features = HFP_SUPPORTED_FEATURE & 0x1F,
265 .record = NULL,
266 .release = cras_hfp_ag_release,
267 .new_connection = cras_hfp_ag_new_connection,
268 .request_disconnection = cras_hfp_ag_request_disconnection,
269 .cancel = cras_hfp_ag_cancel
270 };
271
cras_hfp_ag_profile_create(DBusConnection * conn)272 int cras_hfp_ag_profile_create(DBusConnection *conn)
273 {
274 return cras_bt_add_profile(conn, &cras_hfp_ag_profile);
275 }
276
cras_hsp_ag_new_connection(DBusConnection * conn,struct cras_bt_profile * profile,struct cras_bt_device * device,int rfcomm_fd)277 static int cras_hsp_ag_new_connection(DBusConnection *conn,
278 struct cras_bt_profile *profile,
279 struct cras_bt_device *device,
280 int rfcomm_fd)
281 {
282 struct audio_gateway *ag;
283
284 if (has_audio_gateway(device)) {
285 syslog(LOG_ERR, "Audio gateway exists when %s connects for profile %s",
286 cras_bt_device_name(device), profile->name);
287 close(rfcomm_fd);
288 return 0;
289 }
290
291 if (check_for_conflict_ag(device))
292 return -1;
293
294 cras_bt_device_set_append_iodev_cb(device, possibly_remove_conflict_dev);
295 ag = (struct audio_gateway *)calloc(1, sizeof(*ag));
296 ag->device = device;
297 ag->conn = conn;
298 ag->profile = cras_bt_device_profile_from_uuid(profile->uuid);
299 ag->slc_handle = hfp_slc_create(rfcomm_fd, 1, device, NULL,
300 cras_hfp_ag_slc_disconnected);
301 DL_APPEND(connected_ags, ag);
302 cras_hfp_ag_slc_initialized(ag->slc_handle);
303 return 0;
304 }
305
306 static struct cras_bt_profile cras_hsp_ag_profile = {
307 .name = HSP_AG_PROFILE_NAME,
308 .object_path = HSP_AG_PROFILE_PATH,
309 .uuid = HSP_AG_UUID,
310 .version = HSP_VERSION_1_2,
311 .role = NULL,
312 .record = HSP_AG_RECORD,
313 .release = cras_hfp_ag_release,
314 .new_connection = cras_hsp_ag_new_connection,
315 .request_disconnection = cras_hfp_ag_request_disconnection,
316 .cancel = cras_hfp_ag_cancel
317 };
318
cras_hfp_ag_start(struct cras_bt_device * device)319 int cras_hfp_ag_start(struct cras_bt_device *device)
320 {
321 struct audio_gateway *ag;
322
323 DL_SEARCH_SCALAR(connected_ags, ag, device, device);
324 if (ag == NULL)
325 return -EEXIST;
326
327 ag->info = hfp_info_create();
328 ag->idev = hfp_iodev_create(CRAS_STREAM_INPUT, ag->device,
329 ag->slc_handle,
330 ag->profile, ag->info);
331 ag->odev = hfp_iodev_create(CRAS_STREAM_OUTPUT, ag->device,
332 ag->slc_handle,
333 ag->profile, ag->info);
334
335 if (!ag->idev && !ag->odev) {
336 destroy_audio_gateway(ag);
337 return -ENOMEM;
338 }
339
340 return 0;
341 }
342
cras_hfp_ag_suspend()343 void cras_hfp_ag_suspend()
344 {
345 struct audio_gateway *ag;
346 DL_FOREACH(connected_ags, ag)
347 destroy_audio_gateway(ag);
348 }
349
cras_hfp_ag_suspend_connected_device(struct cras_bt_device * device)350 void cras_hfp_ag_suspend_connected_device(struct cras_bt_device *device)
351 {
352 struct audio_gateway *ag;
353
354 DL_SEARCH_SCALAR(connected_ags, ag, device, device);
355 if (ag)
356 destroy_audio_gateway(ag);
357 }
358
cras_hfp_ag_get_active_handle()359 struct hfp_slc_handle *cras_hfp_ag_get_active_handle()
360 {
361 /* Returns the first handle for HFP qualification. In future we
362 * might want this to return the HFP device user is selected. */
363 return connected_ags ? connected_ags->slc_handle : NULL;
364 }
365
cras_hfp_ag_get_slc(struct cras_bt_device * device)366 struct hfp_slc_handle *cras_hfp_ag_get_slc(struct cras_bt_device *device)
367 {
368 struct audio_gateway *ag;
369 DL_FOREACH(connected_ags, ag) {
370 if (ag->device == device)
371 return ag->slc_handle;
372 }
373 return NULL;
374 }
375
cras_hsp_ag_profile_create(DBusConnection * conn)376 int cras_hsp_ag_profile_create(DBusConnection *conn)
377 {
378 return cras_bt_add_profile(conn, &cras_hsp_ag_profile);
379 }
380