• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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