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