• 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 <dbus/dbus.h>
7 
8 #include <errno.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <syslog.h>
13 #include <unistd.h>
14 
15 #include "cras_bt_device.h"
16 #include "cras_bt_endpoint.h"
17 #include "cras_bt_log.h"
18 #include "cras_bt_transport.h"
19 #include "cras_bt_constants.h"
20 #include "utlist.h"
21 
22 struct cras_bt_transport {
23 	DBusConnection *conn;
24 	char *object_path;
25 	struct cras_bt_device *device;
26 	enum cras_bt_device_profile profile;
27 	int codec;
28 	void *configuration;
29 	int configuration_len;
30 	enum cras_bt_transport_state state;
31 	int fd;
32 	uint16_t read_mtu;
33 	uint16_t write_mtu;
34 	int volume;
35 	int removed;
36 
37 	struct cras_bt_endpoint *endpoint;
38 	struct cras_bt_transport *prev, *next;
39 };
40 
41 static struct cras_bt_transport *transports;
42 
cras_bt_transport_create(DBusConnection * conn,const char * object_path)43 struct cras_bt_transport *cras_bt_transport_create(DBusConnection *conn,
44 						   const char *object_path)
45 {
46 	struct cras_bt_transport *transport;
47 
48 	transport = calloc(1, sizeof(*transport));
49 	if (transport == NULL)
50 		return NULL;
51 
52 	transport->object_path = strdup(object_path);
53 	if (transport->object_path == NULL) {
54 		free(transport);
55 		return NULL;
56 	}
57 
58 	transport->conn = conn;
59 	dbus_connection_ref(transport->conn);
60 
61 	transport->fd = -1;
62 	transport->volume = -1;
63 
64 	DL_APPEND(transports, transport);
65 
66 	return transport;
67 }
68 
cras_bt_transport_set_endpoint(struct cras_bt_transport * transport,struct cras_bt_endpoint * endpoint)69 void cras_bt_transport_set_endpoint(struct cras_bt_transport *transport,
70 				    struct cras_bt_endpoint *endpoint)
71 {
72 	transport->endpoint = endpoint;
73 }
74 
cras_bt_transport_is_removed(struct cras_bt_transport * transport)75 int cras_bt_transport_is_removed(struct cras_bt_transport *transport)
76 {
77 	return transport->removed;
78 }
79 
cras_bt_transport_remove(struct cras_bt_transport * transport)80 void cras_bt_transport_remove(struct cras_bt_transport *transport)
81 {
82 	/*
83 	 * If the transport object is still associated with a valid
84 	 * endpoint. Flag it as removed and wait for the ClearConfiguration
85 	 * message from BT to actually suspend this A2DP connection and
86 	 * destroy the transport.
87 	 */
88 	if (transport->endpoint)
89 		transport->removed = 1;
90 	else
91 		cras_bt_transport_destroy(transport);
92 }
93 
cras_bt_transport_destroy(struct cras_bt_transport * transport)94 void cras_bt_transport_destroy(struct cras_bt_transport *transport)
95 {
96 	DL_DELETE(transports, transport);
97 
98 	dbus_connection_unref(transport->conn);
99 
100 	if (transport->fd >= 0)
101 		close(transport->fd);
102 
103 	free(transport->object_path);
104 	free(transport->configuration);
105 	free(transport);
106 }
107 
cras_bt_transport_reset()108 void cras_bt_transport_reset()
109 {
110 	while (transports) {
111 		syslog(LOG_INFO, "Bluetooth Transport: %s removed",
112 		       transports->object_path);
113 		cras_bt_transport_destroy(transports);
114 	}
115 }
116 
cras_bt_transport_get(const char * object_path)117 struct cras_bt_transport *cras_bt_transport_get(const char *object_path)
118 {
119 	struct cras_bt_transport *transport;
120 
121 	DL_FOREACH (transports, transport) {
122 		if (strcmp(transport->object_path, object_path) == 0)
123 			return transport;
124 	}
125 
126 	return NULL;
127 }
128 
129 size_t
cras_bt_transport_get_list(struct cras_bt_transport *** transport_list_out)130 cras_bt_transport_get_list(struct cras_bt_transport ***transport_list_out)
131 {
132 	struct cras_bt_transport *transport;
133 	struct cras_bt_transport **transport_list = NULL;
134 	size_t num_transports = 0;
135 
136 	DL_FOREACH (transports, transport) {
137 		struct cras_bt_transport **tmp;
138 
139 		tmp = realloc(transport_list,
140 			      sizeof(transport_list[0]) * (num_transports + 1));
141 		if (!tmp) {
142 			free(transport_list);
143 			return -ENOMEM;
144 		}
145 
146 		transport_list = tmp;
147 		transport_list[num_transports++] = transport;
148 	}
149 
150 	*transport_list_out = transport_list;
151 	return num_transports;
152 }
153 
154 const char *
cras_bt_transport_object_path(const struct cras_bt_transport * transport)155 cras_bt_transport_object_path(const struct cras_bt_transport *transport)
156 {
157 	return transport->object_path;
158 }
159 
160 struct cras_bt_device *
cras_bt_transport_device(const struct cras_bt_transport * transport)161 cras_bt_transport_device(const struct cras_bt_transport *transport)
162 {
163 	return transport->device;
164 }
165 
166 enum cras_bt_device_profile
cras_bt_transport_profile(const struct cras_bt_transport * transport)167 cras_bt_transport_profile(const struct cras_bt_transport *transport)
168 {
169 	return transport->profile;
170 }
171 
cras_bt_transport_configuration(const struct cras_bt_transport * transport,void * configuration,int len)172 int cras_bt_transport_configuration(const struct cras_bt_transport *transport,
173 				    void *configuration, int len)
174 {
175 	if (len < transport->configuration_len)
176 		return -ENOSPC;
177 
178 	memcpy(configuration, transport->configuration,
179 	       transport->configuration_len);
180 
181 	return 0;
182 }
183 
184 enum cras_bt_transport_state
cras_bt_transport_state(const struct cras_bt_transport * transport)185 cras_bt_transport_state(const struct cras_bt_transport *transport)
186 {
187 	return transport->state;
188 }
189 
cras_bt_transport_fd(const struct cras_bt_transport * transport)190 int cras_bt_transport_fd(const struct cras_bt_transport *transport)
191 {
192 	return transport->fd;
193 }
194 
cras_bt_transport_write_mtu(const struct cras_bt_transport * transport)195 uint16_t cras_bt_transport_write_mtu(const struct cras_bt_transport *transport)
196 {
197 	return transport->write_mtu;
198 }
199 
200 static enum cras_bt_transport_state
cras_bt_transport_state_from_string(const char * value)201 cras_bt_transport_state_from_string(const char *value)
202 {
203 	if (strcmp("idle", value) == 0)
204 		return CRAS_BT_TRANSPORT_STATE_IDLE;
205 	else if (strcmp("pending", value) == 0)
206 		return CRAS_BT_TRANSPORT_STATE_PENDING;
207 	else if (strcmp("active", value) == 0)
208 		return CRAS_BT_TRANSPORT_STATE_ACTIVE;
209 	else
210 		return CRAS_BT_TRANSPORT_STATE_IDLE;
211 }
212 
cras_bt_transport_state_changed(struct cras_bt_transport * transport)213 static void cras_bt_transport_state_changed(struct cras_bt_transport *transport)
214 {
215 	if (transport->endpoint && transport->endpoint->transport_state_changed)
216 		transport->endpoint->transport_state_changed(
217 			transport->endpoint, transport);
218 }
219 
220 /* Updates bt_device when certain transport property has changed. */
cras_bt_transport_update_device(struct cras_bt_transport * transport)221 static void cras_bt_transport_update_device(struct cras_bt_transport *transport)
222 {
223 	if (!transport->device)
224 		return;
225 
226 	/* When the transport has non-negaive volume, it means the remote
227 	 * BT audio devices supports AVRCP absolute volume. Set the flag in bt
228 	 * device to use hardware volume. Also map the volume value from 0-127
229 	 * to 0-100.
230 	 */
231 	if (transport->volume != -1) {
232 		cras_bt_device_set_use_hardware_volume(transport->device, 1);
233 		cras_bt_device_update_hardware_volume(
234 			transport->device, transport->volume * 100 / 127);
235 	}
236 }
237 
cras_bt_transport_update_properties(struct cras_bt_transport * transport,DBusMessageIter * properties_array_iter,DBusMessageIter * invalidated_array_iter)238 void cras_bt_transport_update_properties(struct cras_bt_transport *transport,
239 					 DBusMessageIter *properties_array_iter,
240 					 DBusMessageIter *invalidated_array_iter)
241 {
242 	while (dbus_message_iter_get_arg_type(properties_array_iter) !=
243 	       DBUS_TYPE_INVALID) {
244 		DBusMessageIter properties_dict_iter, variant_iter;
245 		const char *key;
246 		int type;
247 
248 		dbus_message_iter_recurse(properties_array_iter,
249 					  &properties_dict_iter);
250 
251 		dbus_message_iter_get_basic(&properties_dict_iter, &key);
252 		dbus_message_iter_next(&properties_dict_iter);
253 
254 		dbus_message_iter_recurse(&properties_dict_iter, &variant_iter);
255 		type = dbus_message_iter_get_arg_type(&variant_iter);
256 
257 		if (type == DBUS_TYPE_STRING) {
258 			const char *value;
259 
260 			dbus_message_iter_get_basic(&variant_iter, &value);
261 
262 			if (strcmp(key, "UUID") == 0) {
263 				transport->profile =
264 					cras_bt_device_profile_from_uuid(value);
265 
266 			} else if (strcmp(key, "State") == 0) {
267 				enum cras_bt_transport_state old_state =
268 					transport->state;
269 				transport->state =
270 					cras_bt_transport_state_from_string(
271 						value);
272 				if (transport->state != old_state)
273 					cras_bt_transport_state_changed(
274 						transport);
275 			}
276 
277 		} else if (type == DBUS_TYPE_BYTE) {
278 			int value;
279 
280 			dbus_message_iter_get_basic(&variant_iter, &value);
281 
282 			if (strcmp(key, "Codec") == 0)
283 				transport->codec = value;
284 		} else if (type == DBUS_TYPE_OBJECT_PATH) {
285 			const char *obj_path;
286 
287 			/* Property: object Device [readonly] */
288 			dbus_message_iter_get_basic(&variant_iter, &obj_path);
289 			transport->device = cras_bt_device_get(obj_path);
290 			if (!transport->device) {
291 				syslog(LOG_ERR,
292 				       "Device %s not found at update"
293 				       "transport properties",
294 				       obj_path);
295 				transport->device = cras_bt_device_create(
296 					transport->conn, obj_path);
297 				cras_bt_transport_update_device(transport);
298 			}
299 		} else if (strcmp(dbus_message_iter_get_signature(&variant_iter),
300 				  "ay") == 0 &&
301 			   strcmp(key, "Configuration") == 0) {
302 			DBusMessageIter value_iter;
303 			char *value;
304 			int len;
305 
306 			dbus_message_iter_recurse(&variant_iter, &value_iter);
307 			dbus_message_iter_get_fixed_array(&value_iter, &value,
308 							  &len);
309 
310 			free(transport->configuration);
311 			transport->configuration_len = 0;
312 
313 			transport->configuration = malloc(len);
314 			if (transport->configuration) {
315 				memcpy(transport->configuration, value, len);
316 				transport->configuration_len = len;
317 			}
318 
319 		} else if (strcmp(key, "Volume") == 0) {
320 			uint16_t volume;
321 
322 			dbus_message_iter_get_basic(&variant_iter, &volume);
323 			transport->volume = volume;
324 			cras_bt_transport_update_device(transport);
325 		}
326 
327 		dbus_message_iter_next(properties_array_iter);
328 	}
329 
330 	while (invalidated_array_iter &&
331 	       dbus_message_iter_get_arg_type(invalidated_array_iter) !=
332 		       DBUS_TYPE_INVALID) {
333 		const char *key;
334 
335 		dbus_message_iter_get_basic(invalidated_array_iter, &key);
336 
337 		if (strcmp(key, "Device") == 0) {
338 			transport->device = NULL;
339 		} else if (strcmp(key, "UUID") == 0) {
340 			transport->profile = 0;
341 		} else if (strcmp(key, "State") == 0) {
342 			transport->state = CRAS_BT_TRANSPORT_STATE_IDLE;
343 		} else if (strcmp(key, "Codec") == 0) {
344 			transport->codec = 0;
345 		} else if (strcmp(key, "Configuration") == 0) {
346 			free(transport->configuration);
347 			transport->configuration = NULL;
348 			transport->configuration_len = 0;
349 		}
350 
351 		dbus_message_iter_next(invalidated_array_iter);
352 	}
353 }
354 
on_transport_volume_set(DBusPendingCall * pending_call,void * data)355 static void on_transport_volume_set(DBusPendingCall *pending_call, void *data)
356 {
357 	DBusMessage *reply;
358 
359 	reply = dbus_pending_call_steal_reply(pending_call);
360 	dbus_pending_call_unref(pending_call);
361 
362 	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
363 		syslog(LOG_ERR, "Set absolute volume returned error: %s",
364 		       dbus_message_get_error_name(reply));
365 	dbus_message_unref(reply);
366 }
367 
cras_bt_transport_set_volume(struct cras_bt_transport * transport,uint16_t volume)368 int cras_bt_transport_set_volume(struct cras_bt_transport *transport,
369 				 uint16_t volume)
370 {
371 	const char *key = "Volume";
372 	const char *interface = BLUEZ_INTERFACE_MEDIA_TRANSPORT;
373 	DBusMessage *method_call;
374 	DBusMessageIter message_iter, variant;
375 	DBusPendingCall *pending_call;
376 
377 	method_call =
378 		dbus_message_new_method_call(BLUEZ_SERVICE,
379 					     transport->object_path,
380 					     DBUS_INTERFACE_PROPERTIES, "Set");
381 	if (!method_call)
382 		return -ENOMEM;
383 
384 	dbus_message_iter_init_append(method_call, &message_iter);
385 
386 	dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_STRING,
387 				       &interface);
388 	dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_STRING, &key);
389 
390 	dbus_message_iter_open_container(&message_iter, DBUS_TYPE_VARIANT,
391 					 DBUS_TYPE_UINT16_AS_STRING, &variant);
392 	dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT16, &volume);
393 	dbus_message_iter_close_container(&message_iter, &variant);
394 
395 	if (!dbus_connection_send_with_reply(transport->conn, method_call,
396 					     &pending_call,
397 					     DBUS_TIMEOUT_USE_DEFAULT)) {
398 		dbus_message_unref(method_call);
399 		return -ENOMEM;
400 	}
401 
402 	dbus_message_unref(method_call);
403 	if (!pending_call)
404 		return -EIO;
405 
406 	if (!dbus_pending_call_set_notify(pending_call, on_transport_volume_set,
407 					  NULL, NULL)) {
408 		dbus_pending_call_cancel(pending_call);
409 		dbus_pending_call_unref(pending_call);
410 		return -ENOMEM;
411 	}
412 
413 	return 0;
414 }
415 
cras_bt_transport_acquire(struct cras_bt_transport * transport)416 int cras_bt_transport_acquire(struct cras_bt_transport *transport)
417 {
418 	DBusMessage *method_call, *reply;
419 	DBusError dbus_error;
420 	int rc = 0;
421 
422 	if (transport->fd >= 0)
423 		return 0;
424 
425 	method_call = dbus_message_new_method_call(
426 		BLUEZ_SERVICE, transport->object_path,
427 		BLUEZ_INTERFACE_MEDIA_TRANSPORT, "Acquire");
428 	if (!method_call)
429 		return -ENOMEM;
430 
431 	dbus_error_init(&dbus_error);
432 
433 	reply = dbus_connection_send_with_reply_and_block(
434 		transport->conn, method_call, DBUS_TIMEOUT_USE_DEFAULT,
435 		&dbus_error);
436 	if (!reply) {
437 		syslog(LOG_ERR, "Failed to acquire transport %s: %s",
438 		       transport->object_path, dbus_error.message);
439 		dbus_error_free(&dbus_error);
440 		dbus_message_unref(method_call);
441 		rc = -EIO;
442 		goto acquire_fail;
443 	}
444 
445 	dbus_message_unref(method_call);
446 
447 	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
448 		syslog(LOG_ERR, "Acquire returned error: %s",
449 		       dbus_message_get_error_name(reply));
450 		dbus_message_unref(reply);
451 		rc = -EIO;
452 		goto acquire_fail;
453 	}
454 
455 	if (!dbus_message_get_args(
456 		    reply, &dbus_error, DBUS_TYPE_UNIX_FD, &(transport->fd),
457 		    DBUS_TYPE_UINT16, &(transport->read_mtu), DBUS_TYPE_UINT16,
458 		    &(transport->write_mtu), DBUS_TYPE_INVALID)) {
459 		syslog(LOG_ERR, "Bad Acquire reply received: %s",
460 		       dbus_error.message);
461 		dbus_error_free(&dbus_error);
462 		dbus_message_unref(reply);
463 		rc = -EINVAL;
464 		goto acquire_fail;
465 	}
466 
467 	BTLOG(btlog, BT_TRANSPORT_ACQUIRE, 1, transport->fd);
468 	dbus_message_unref(reply);
469 	return 0;
470 
471 acquire_fail:
472 	BTLOG(btlog, BT_TRANSPORT_ACQUIRE, 0, 0);
473 	return rc;
474 }
475 
cras_bt_transport_try_acquire(struct cras_bt_transport * transport)476 int cras_bt_transport_try_acquire(struct cras_bt_transport *transport)
477 {
478 	DBusMessage *method_call, *reply;
479 	DBusError dbus_error;
480 	int fd, read_mtu, write_mtu;
481 
482 	method_call = dbus_message_new_method_call(
483 		BLUEZ_SERVICE, transport->object_path,
484 		BLUEZ_INTERFACE_MEDIA_TRANSPORT, "TryAcquire");
485 	if (!method_call)
486 		return -ENOMEM;
487 
488 	dbus_error_init(&dbus_error);
489 
490 	reply = dbus_connection_send_with_reply_and_block(
491 		transport->conn, method_call, DBUS_TIMEOUT_USE_DEFAULT,
492 		&dbus_error);
493 	if (!reply) {
494 		syslog(LOG_ERR, "Failed to try acquire transport %s: %s",
495 		       transport->object_path, dbus_error.message);
496 		dbus_error_free(&dbus_error);
497 		dbus_message_unref(method_call);
498 		return -EIO;
499 	}
500 
501 	dbus_message_unref(method_call);
502 
503 	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
504 		syslog(LOG_ERR, "TryAcquire returned error: %s",
505 		       dbus_message_get_error_name(reply));
506 		dbus_message_unref(reply);
507 		return -EIO;
508 	}
509 
510 	if (!dbus_message_get_args(reply, &dbus_error, DBUS_TYPE_UNIX_FD, &fd,
511 				   DBUS_TYPE_UINT16, &read_mtu,
512 				   DBUS_TYPE_UINT16, &write_mtu,
513 				   DBUS_TYPE_INVALID)) {
514 		syslog(LOG_ERR, "Bad TryAcquire reply received: %s",
515 		       dbus_error.message);
516 		dbus_error_free(&dbus_error);
517 		dbus_message_unref(reply);
518 		return -EINVAL;
519 	}
520 
521 	/* Done TryAcquired the transport so it won't be released in bluez,
522 	 * no need for the new file descriptor so close it. */
523 	if (transport->fd != fd)
524 		close(fd);
525 
526 	dbus_message_unref(reply);
527 	return 0;
528 }
529 
530 /* Callback to trigger when transport release completed. */
cras_bt_on_transport_release(DBusPendingCall * pending_call,void * data)531 static void cras_bt_on_transport_release(DBusPendingCall *pending_call,
532 					 void *data)
533 {
534 	DBusMessage *reply;
535 
536 	reply = dbus_pending_call_steal_reply(pending_call);
537 	dbus_pending_call_unref(pending_call);
538 
539 	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
540 		syslog(LOG_WARNING, "Release transport returned error: %s",
541 		       dbus_message_get_error_name(reply));
542 		dbus_message_unref(reply);
543 		return;
544 	}
545 
546 	dbus_message_unref(reply);
547 }
548 
cras_bt_transport_release(struct cras_bt_transport * transport,unsigned int blocking)549 int cras_bt_transport_release(struct cras_bt_transport *transport,
550 			      unsigned int blocking)
551 {
552 	DBusMessage *method_call, *reply;
553 	DBusPendingCall *pending_call;
554 	DBusError dbus_error;
555 
556 	if (transport->fd < 0)
557 		return 0;
558 
559 	BTLOG(btlog, BT_TRANSPORT_RELEASE, transport->fd, 0);
560 
561 	/* Close the transport on our end no matter whether or not the server
562 	 * gives us an error.
563 	 */
564 	close(transport->fd);
565 	transport->fd = -1;
566 
567 	method_call = dbus_message_new_method_call(
568 		BLUEZ_SERVICE, transport->object_path,
569 		BLUEZ_INTERFACE_MEDIA_TRANSPORT, "Release");
570 	if (!method_call)
571 		return -ENOMEM;
572 
573 	if (blocking) {
574 		dbus_error_init(&dbus_error);
575 
576 		reply = dbus_connection_send_with_reply_and_block(
577 			transport->conn, method_call, DBUS_TIMEOUT_USE_DEFAULT,
578 			&dbus_error);
579 		if (!reply) {
580 			syslog(LOG_ERR, "Failed to release transport %s: %s",
581 			       transport->object_path, dbus_error.message);
582 			dbus_error_free(&dbus_error);
583 			dbus_message_unref(method_call);
584 			return -EIO;
585 		}
586 
587 		dbus_message_unref(method_call);
588 
589 		if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
590 			syslog(LOG_ERR, "Release returned error: %s",
591 			       dbus_message_get_error_name(reply));
592 			dbus_message_unref(reply);
593 			return -EIO;
594 		}
595 
596 		dbus_message_unref(reply);
597 	} else {
598 		if (!dbus_connection_send_with_reply(
599 			    transport->conn, method_call, &pending_call,
600 			    DBUS_TIMEOUT_USE_DEFAULT)) {
601 			dbus_message_unref(method_call);
602 			return -ENOMEM;
603 		}
604 
605 		dbus_message_unref(method_call);
606 		if (!pending_call)
607 			return -EIO;
608 
609 		if (!dbus_pending_call_set_notify(pending_call,
610 						  cras_bt_on_transport_release,
611 						  transport, NULL)) {
612 			dbus_pending_call_cancel(pending_call);
613 			dbus_pending_call_unref(pending_call);
614 			return -ENOMEM;
615 		}
616 	}
617 	return 0;
618 }
619