Home | History | Annotate | Download | only in server
      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 
     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 
     68 void cras_bt_transport_set_endpoint(struct cras_bt_transport *transport,
     69 				    struct cras_bt_endpoint *endpoint) {
     70 	transport->endpoint = endpoint;
     71 }
     72 
     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 
     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 
     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 
    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 
    134 const char *cras_bt_transport_object_path(
    135 	const struct cras_bt_transport *transport)
    136 {
    137 	return transport->object_path;
    138 }
    139 
    140 struct cras_bt_device *cras_bt_transport_device(
    141 	const struct cras_bt_transport *transport)
    142 {
    143 	return transport->device;
    144 }
    145 
    146 enum cras_bt_device_profile cras_bt_transport_profile(
    147 	const struct cras_bt_transport *transport)
    148 {
    149 	return transport->profile;
    150 }
    151 
    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 
    164 enum cras_bt_transport_state cras_bt_transport_state(
    165 	const struct cras_bt_transport *transport)
    166 {
    167 	return transport->state;
    168 }
    169 
    170 int cras_bt_transport_fd(const struct cras_bt_transport *transport)
    171 {
    172 	return transport->fd;
    173 }
    174 
    175 uint16_t cras_bt_transport_write_mtu(const struct cras_bt_transport *transport)
    176 {
    177 	return transport->write_mtu;
    178 }
    179 
    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 
    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. */
    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 
    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 
    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 
    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 
    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 
    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. */
    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 
    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