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