Home | History | Annotate | Download | only in audio
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2006-2007  Nokia Corporation
      6  *  Copyright (C) 2004-2009  Marcel Holtmann <marcel (at) holtmann.org>
      7  *
      8  *
      9  *  This program is free software; you can redistribute it and/or modify
     10  *  it under the terms of the GNU General Public License as published by
     11  *  the Free Software Foundation; either version 2 of the License, or
     12  *  (at your option) any later version.
     13  *
     14  *  This program is distributed in the hope that it will be useful,
     15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  *  GNU General Public License for more details.
     18  *
     19  *  You should have received a copy of the GNU General Public License
     20  *  along with this program; if not, write to the Free Software
     21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     22  *
     23  */
     24 
     25 #ifdef HAVE_CONFIG_H
     26 #include <config.h>
     27 #endif
     28 
     29 #include <errno.h>
     30 
     31 #include <glib.h>
     32 #include <gdbus.h>
     33 
     34 #include "../src/adapter.h"
     35 #include "../src/dbus-common.h"
     36 
     37 #include "log.h"
     38 #include "error.h"
     39 #include "device.h"
     40 #include "avdtp.h"
     41 #include "media.h"
     42 #include "transport.h"
     43 #include "a2dp.h"
     44 #include "headset.h"
     45 
     46 #ifndef DBUS_TYPE_UNIX_FD
     47 #define DBUS_TYPE_UNIX_FD -1
     48 #endif
     49 
     50 #define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport"
     51 
     52 struct media_request {
     53 	DBusMessage		*msg;
     54 	guint			id;
     55 };
     56 
     57 struct media_owner {
     58 	struct media_transport	*transport;
     59 	struct media_request	*pending;
     60 	char			*name;
     61 	char			*accesstype;
     62 	guint			watch;
     63 };
     64 
     65 struct media_transport {
     66 	DBusConnection		*conn;
     67 	char			*path;		/* Transport object path */
     68 	struct audio_device	*device;	/* Transport device */
     69 	struct avdtp		*session;	/* Signalling session (a2dp only) */
     70 	struct media_endpoint	*endpoint;	/* Transport endpoint */
     71 	GSList			*owners;	/* Transport owners */
     72 	uint8_t			*configuration; /* Transport configuration */
     73 	int			size;		/* Transport configuration size */
     74 	int			fd;		/* Transport file descriptor */
     75 	uint16_t		imtu;		/* Transport input mtu */
     76 	uint16_t		omtu;		/* Transport output mtu */
     77 	uint16_t		delay;		/* Transport delay (a2dp only) */
     78 	unsigned int		nrec_id;	/* Transport nrec watch (headset only) */
     79 	gboolean		read_lock;
     80 	gboolean		write_lock;
     81 	gboolean		in_use;
     82 	guint			(*resume) (struct media_transport *transport,
     83 					struct media_owner *owner);
     84 	guint			(*suspend) (struct media_transport *transport,
     85 					struct media_owner *owner);
     86 	void			(*cancel) (struct media_transport *transport,
     87 								guint id);
     88 	void			(*get_properties) (
     89 					struct media_transport *transport,
     90 					DBusMessageIter *dict);
     91 	int			(*set_property) (
     92 					struct media_transport *transport,
     93 					const char *property,
     94 					DBusMessageIter *value);
     95 };
     96 
     97 void media_transport_destroy(struct media_transport *transport)
     98 {
     99 	char *path;
    100 
    101 	path = g_strdup(transport->path);
    102 
    103 	g_dbus_unregister_interface(transport->conn, path,
    104 						MEDIA_TRANSPORT_INTERFACE);
    105 
    106 	g_free(path);
    107 }
    108 
    109 static struct media_request *media_request_create(DBusMessage *msg, guint id)
    110 {
    111 	struct media_request *req;
    112 
    113 	req = g_new0(struct media_request, 1);
    114 	req->msg = dbus_message_ref(msg);
    115 	req->id = id;
    116 
    117 	DBG("Request created: method=%s id=%u", dbus_message_get_member(msg),
    118 									id);
    119 
    120 	return req;
    121 }
    122 
    123 static void media_request_reply(struct media_request *req,
    124 						DBusConnection *conn, int err)
    125 {
    126 	DBusMessage *reply;
    127 
    128 	DBG("Request %s Reply %s", dbus_message_get_member(req->msg),
    129 							strerror(err));
    130 
    131 	if (!err)
    132 		reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID);
    133 	else
    134 		reply = g_dbus_create_error(req->msg,
    135 						ERROR_INTERFACE ".Failed",
    136 						"%s", strerror(err));
    137 
    138 	g_dbus_send_message(conn, reply);
    139 }
    140 
    141 static gboolean media_transport_release(struct media_transport *transport,
    142 					const char *accesstype)
    143 {
    144 	if (g_strstr_len(accesstype, -1, "r") != NULL) {
    145 		transport->read_lock = FALSE;
    146 		DBG("Transport %s: read lock released", transport->path);
    147 	}
    148 
    149 	if (g_strstr_len(accesstype, -1, "w") != NULL) {
    150 		transport->write_lock = FALSE;
    151 		DBG("Transport %s: write lock released", transport->path);
    152 	}
    153 
    154 	return TRUE;
    155 }
    156 
    157 static void media_owner_remove(struct media_owner *owner)
    158 {
    159 	struct media_transport *transport = owner->transport;
    160 	struct media_request *req = owner->pending;
    161 
    162 	if (!req)
    163 		return;
    164 
    165 	DBG("Owner %s Request %s", owner->name,
    166 					dbus_message_get_member(req->msg));
    167 
    168 	if (req->id)
    169 		transport->cancel(transport, req->id);
    170 
    171 	owner->pending = NULL;
    172 	if (req->msg)
    173 		dbus_message_unref(req->msg);
    174 
    175 	g_free(req);
    176 }
    177 
    178 static void media_owner_free(struct media_owner *owner)
    179 {
    180 	DBG("Owner %s", owner->name);
    181 
    182 	media_owner_remove(owner);
    183 
    184 	g_free(owner->name);
    185 	g_free(owner->accesstype);
    186 	g_free(owner);
    187 }
    188 
    189 static void media_transport_remove(struct media_transport *transport,
    190 						struct media_owner *owner)
    191 {
    192 	DBG("Transport %s Owner %s", transport->path, owner->name);
    193 
    194 	media_transport_release(transport, owner->accesstype);
    195 
    196 	/* Reply if owner has a pending request */
    197 	if (owner->pending)
    198 		media_request_reply(owner->pending, transport->conn, EIO);
    199 
    200 	transport->owners = g_slist_remove(transport->owners, owner);
    201 
    202 	if (owner->watch)
    203 		g_dbus_remove_watch(transport->conn, owner->watch);
    204 
    205 	media_owner_free(owner);
    206 
    207 	/* Suspend if there is no longer any owner */
    208 	if (transport->owners == NULL && transport->in_use)
    209 		transport->suspend(transport, NULL);
    210 }
    211 
    212 static gboolean media_transport_set_fd(struct media_transport *transport,
    213 					int fd, uint16_t imtu, uint16_t omtu)
    214 {
    215 	if (transport->fd == fd)
    216 		return TRUE;
    217 
    218 	transport->fd = fd;
    219 	transport->imtu = imtu;
    220 	transport->omtu = omtu;
    221 
    222 	info("%s: fd(%d) ready", transport->path, fd);
    223 
    224 	return TRUE;
    225 }
    226 
    227 static void a2dp_resume_complete(struct avdtp *session,
    228 				struct avdtp_error *err, void *user_data)
    229 {
    230 	struct media_owner *owner = user_data;
    231 	struct media_request *req = owner->pending;
    232 	struct media_transport *transport = owner->transport;
    233 	struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
    234 	struct avdtp_stream *stream;
    235 	int fd;
    236 	uint16_t imtu, omtu;
    237 	gboolean ret;
    238 
    239 	req->id = 0;
    240 
    241 	if (err)
    242 		goto fail;
    243 
    244 	stream = a2dp_sep_get_stream(sep);
    245 	if (stream == NULL)
    246 		goto fail;
    247 
    248 	ret = avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, NULL);
    249 	if (ret == FALSE)
    250 		goto fail;
    251 
    252 	media_transport_set_fd(transport, fd, imtu, omtu);
    253 
    254 	if (g_strstr_len(owner->accesstype, -1, "r") == NULL)
    255 		imtu = 0;
    256 
    257 	if (g_strstr_len(owner->accesstype, -1, "w") == NULL)
    258 		omtu = 0;
    259 
    260 	ret = g_dbus_send_reply(transport->conn, req->msg,
    261 						DBUS_TYPE_UNIX_FD, &fd,
    262 						DBUS_TYPE_UINT16, &imtu,
    263 						DBUS_TYPE_UINT16, &omtu,
    264 						DBUS_TYPE_INVALID);
    265 	if (ret == FALSE)
    266 		goto fail;
    267 
    268 	media_owner_remove(owner);
    269 
    270 	return;
    271 
    272 fail:
    273 	media_transport_remove(transport, owner);
    274 }
    275 
    276 static guint resume_a2dp(struct media_transport *transport,
    277 				struct media_owner *owner)
    278 {
    279 	struct media_endpoint *endpoint = transport->endpoint;
    280 	struct audio_device *device = transport->device;
    281 	struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
    282 
    283 	if (transport->session == NULL) {
    284 		transport->session = avdtp_get(&device->src, &device->dst);
    285 		if (transport->session == NULL)
    286 			return 0;
    287 	}
    288 
    289 	if (transport->in_use == TRUE)
    290 		goto done;
    291 
    292 	transport->in_use = a2dp_sep_lock(sep, transport->session);
    293 	if (transport->in_use == FALSE)
    294 		return 0;
    295 
    296 done:
    297 	return a2dp_resume(transport->session, sep, a2dp_resume_complete,
    298 				owner);
    299 }
    300 
    301 static void a2dp_suspend_complete(struct avdtp *session,
    302 				struct avdtp_error *err, void *user_data)
    303 {
    304 	struct media_owner *owner = user_data;
    305 	struct media_transport *transport = owner->transport;
    306 	struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
    307 
    308 	/* Release always succeeds */
    309 	if (owner->pending) {
    310 		owner->pending->id = 0;
    311 		media_request_reply(owner->pending, transport->conn, 0);
    312 		media_owner_remove(owner);
    313 	}
    314 
    315 	a2dp_sep_unlock(sep, transport->session);
    316 	transport->in_use = FALSE;
    317 	media_transport_remove(transport, owner);
    318 }
    319 
    320 static guint suspend_a2dp(struct media_transport *transport,
    321 						struct media_owner *owner)
    322 {
    323 	struct media_endpoint *endpoint = transport->endpoint;
    324 	struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
    325 
    326 	if (!owner) {
    327 		a2dp_sep_unlock(sep, transport->session);
    328 		transport->in_use = FALSE;
    329 		return 0;
    330 	}
    331 
    332 	return a2dp_suspend(transport->session, sep, a2dp_suspend_complete,
    333 				owner);
    334 }
    335 
    336 static void cancel_a2dp(struct media_transport *transport, guint id)
    337 {
    338 	a2dp_cancel(transport->device, id);
    339 }
    340 
    341 static void headset_resume_complete(struct audio_device *dev, void *user_data)
    342 {
    343 	struct media_owner *owner = user_data;
    344 	struct media_request *req = owner->pending;
    345 	struct media_transport *transport = owner->transport;
    346 	int fd;
    347 	uint16_t imtu, omtu;
    348 	gboolean ret;
    349 
    350 	req->id = 0;
    351 
    352 	if (dev == NULL)
    353 		goto fail;
    354 
    355 	fd = headset_get_sco_fd(dev);
    356 	if (fd < 0)
    357 		goto fail;
    358 
    359 	imtu = 48;
    360 	omtu = 48;
    361 
    362 	media_transport_set_fd(transport, fd, imtu, omtu);
    363 
    364 	if (g_strstr_len(owner->accesstype, -1, "r") == NULL)
    365 		imtu = 0;
    366 
    367 	if (g_strstr_len(owner->accesstype, -1, "w") == NULL)
    368 		omtu = 0;
    369 
    370 	ret = g_dbus_send_reply(transport->conn, req->msg,
    371 						DBUS_TYPE_UNIX_FD, &fd,
    372 						DBUS_TYPE_UINT16, &imtu,
    373 						DBUS_TYPE_UINT16, &omtu,
    374 						DBUS_TYPE_INVALID);
    375 	if (ret == FALSE)
    376 		goto fail;
    377 
    378 	media_owner_remove(owner);
    379 
    380 	return;
    381 
    382 fail:
    383 	media_transport_remove(transport, owner);
    384 }
    385 
    386 static guint resume_headset(struct media_transport *transport,
    387 				struct media_owner *owner)
    388 {
    389 	struct audio_device *device = transport->device;
    390 
    391 	if (transport->in_use == TRUE)
    392 		goto done;
    393 
    394 	transport->in_use = headset_lock(device, HEADSET_LOCK_READ |
    395 						HEADSET_LOCK_WRITE);
    396 	if (transport->in_use == FALSE)
    397 		return 0;
    398 
    399 done:
    400 	return headset_request_stream(device, headset_resume_complete,
    401 					owner);
    402 }
    403 
    404 static void headset_suspend_complete(struct audio_device *dev, void *user_data)
    405 {
    406 	struct media_owner *owner = user_data;
    407 	struct media_transport *transport = owner->transport;
    408 
    409 	/* Release always succeeds */
    410 	if (owner->pending) {
    411 		owner->pending->id = 0;
    412 		media_request_reply(owner->pending, transport->conn, 0);
    413 		media_owner_remove(owner);
    414 	}
    415 
    416 	headset_unlock(dev, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE);
    417 	transport->in_use = FALSE;
    418 	media_transport_remove(transport, owner);
    419 }
    420 
    421 static guint suspend_headset(struct media_transport *transport,
    422 						struct media_owner *owner)
    423 {
    424 	struct audio_device *device = transport->device;
    425 
    426 	if (!owner) {
    427 		headset_unlock(device, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE);
    428 		transport->in_use = FALSE;
    429 		return 0;
    430 	}
    431 
    432 	return headset_suspend_stream(device, headset_suspend_complete, owner);
    433 }
    434 
    435 static void cancel_headset(struct media_transport *transport, guint id)
    436 {
    437 	headset_cancel_stream(transport->device, id);
    438 }
    439 
    440 static void media_owner_exit(DBusConnection *connection, void *user_data)
    441 {
    442 	struct media_owner *owner = user_data;
    443 
    444 	owner->watch = 0;
    445 
    446 	media_owner_remove(owner);
    447 
    448 	media_transport_remove(owner->transport, owner);
    449 }
    450 
    451 static gboolean media_transport_acquire(struct media_transport *transport,
    452 							const char *accesstype)
    453 {
    454 	gboolean read_lock = FALSE, write_lock = FALSE;
    455 
    456 	if (g_strstr_len(accesstype, -1, "r") != NULL) {
    457 		if (transport->read_lock == TRUE)
    458 			return FALSE;
    459 		read_lock = TRUE;
    460 	}
    461 
    462 	if (g_strstr_len(accesstype, -1, "w") != NULL) {
    463 		if (transport->write_lock == TRUE)
    464 			return FALSE;
    465 		write_lock = TRUE;
    466 	}
    467 
    468 	/* Check invalid accesstype */
    469 	if (read_lock == FALSE && write_lock == FALSE)
    470 		return FALSE;
    471 
    472 	if (read_lock) {
    473 		transport->read_lock = read_lock;
    474 		DBG("Transport %s: read lock acquired", transport->path);
    475 	}
    476 
    477 	if (write_lock) {
    478 		transport->write_lock = write_lock;
    479 		DBG("Transport %s: write lock acquired", transport->path);
    480 	}
    481 
    482 
    483 	return TRUE;
    484 }
    485 
    486 static void media_transport_add(struct media_transport *transport,
    487 					struct media_owner *owner)
    488 {
    489 	DBG("Transport %s Owner %s", transport->path, owner->name);
    490 	transport->owners = g_slist_append(transport->owners, owner);
    491 	owner->transport = transport;
    492 }
    493 
    494 static struct media_owner *media_owner_create(DBusConnection *conn,
    495 						DBusMessage *msg,
    496 						const char *accesstype)
    497 {
    498 	struct media_owner *owner;
    499 
    500 	owner = g_new0(struct media_owner, 1);
    501 	owner->name = g_strdup(dbus_message_get_sender(msg));
    502 	owner->accesstype = g_strdup(accesstype);
    503 	owner->watch = g_dbus_add_disconnect_watch(conn, owner->name,
    504 							media_owner_exit,
    505 							owner, NULL);
    506 
    507 	DBG("Owner created: sender=%s accesstype=%s", owner->name,
    508 			accesstype);
    509 
    510 	return owner;
    511 }
    512 
    513 static void media_owner_add(struct media_owner *owner,
    514 						struct media_request *req)
    515 {
    516 	DBG("Owner %s Request %s", owner->name,
    517 					dbus_message_get_member(req->msg));
    518 
    519 	owner->pending = req;
    520 }
    521 
    522 static struct media_owner *media_transport_find_owner(
    523 					struct media_transport *transport,
    524 					const char *name)
    525 {
    526 	GSList *l;
    527 
    528 	for (l = transport->owners; l; l = l->next) {
    529 		struct media_owner *owner = l->data;
    530 
    531 		if (g_strcmp0(owner->name, name) == 0)
    532 			return owner;
    533 	}
    534 
    535 	return NULL;
    536 }
    537 
    538 static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
    539 					void *data)
    540 {
    541 	struct media_transport *transport = data;
    542 	struct media_owner *owner;
    543 	struct media_request *req;
    544 	const char *accesstype, *sender;
    545 	guint id;
    546 
    547 	if (!dbus_message_get_args(msg, NULL,
    548 				DBUS_TYPE_STRING, &accesstype,
    549 				DBUS_TYPE_INVALID))
    550 		return NULL;
    551 
    552 	sender = dbus_message_get_sender(msg);
    553 
    554 	owner = media_transport_find_owner(transport, sender);
    555 	if (owner != NULL)
    556 		return btd_error_not_authorized(msg);
    557 
    558 	if (media_transport_acquire(transport, accesstype) == FALSE)
    559 		return btd_error_not_authorized(msg);
    560 
    561 	owner = media_owner_create(conn, msg, accesstype);
    562 	id = transport->resume(transport, owner);
    563 	if (id == 0) {
    564 		media_owner_free(owner);
    565 		return btd_error_not_authorized(msg);
    566 	}
    567 
    568 	req = media_request_create(msg, id);
    569 	media_owner_add(owner, req);
    570 	media_transport_add(transport, owner);
    571 
    572 	return NULL;
    573 }
    574 
    575 static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
    576 					void *data)
    577 {
    578 	struct media_transport *transport = data;
    579 	struct media_owner *owner;
    580 	const char *accesstype, *sender;
    581 	struct media_request *req;
    582 
    583 	if (!dbus_message_get_args(msg, NULL,
    584 				DBUS_TYPE_STRING, &accesstype,
    585 				DBUS_TYPE_INVALID))
    586 		return NULL;
    587 
    588 	sender = dbus_message_get_sender(msg);
    589 
    590 	owner = media_transport_find_owner(transport, sender);
    591 	if (owner == NULL)
    592 		return btd_error_not_authorized(msg);
    593 
    594 	if (g_strcmp0(owner->accesstype, accesstype) == 0) {
    595 		guint id;
    596 
    597 		/* Not the last owner, no need to suspend */
    598 		if (g_slist_length(transport->owners) != 1) {
    599 			media_transport_remove(transport, owner);
    600 			return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
    601 		}
    602 
    603 		if (owner->pending) {
    604 			const char *member;
    605 
    606 			member = dbus_message_get_member(owner->pending->msg);
    607 			/* Cancel Acquire request if that exist */
    608 			if (g_str_equal(member, "Acquire"))
    609 				media_owner_remove(owner);
    610 			else
    611 				return btd_error_in_progress(msg);
    612 		}
    613 
    614 		id = transport->suspend(transport, owner);
    615 		if (id == 0) {
    616 			media_transport_remove(transport, owner);
    617 			return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
    618 		}
    619 
    620 		req = media_request_create(msg, id);
    621 		media_owner_add(owner, req);
    622 
    623 		return NULL;
    624 	} else if (g_strstr_len(owner->accesstype, -1, accesstype) != NULL) {
    625 		media_transport_release(transport, accesstype);
    626 		g_strdelimit(owner->accesstype, accesstype, ' ');
    627 	} else
    628 		return btd_error_not_authorized(msg);
    629 
    630 	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
    631 }
    632 
    633 static int set_property_a2dp(struct media_transport *transport,
    634 						const char *property,
    635 						DBusMessageIter *value)
    636 {
    637 	if (g_strcmp0(property, "Delay") == 0) {
    638 		if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
    639 			return -EINVAL;
    640 		dbus_message_iter_get_basic(value, &transport->delay);
    641 
    642 		/* FIXME: send new delay */
    643 		return 0;
    644 	}
    645 
    646 	return -EINVAL;
    647 }
    648 
    649 static int set_property_headset(struct media_transport *transport,
    650 						const char *property,
    651 						DBusMessageIter *value)
    652 {
    653 	if (g_strcmp0(property, "NREC") == 0) {
    654 		gboolean nrec;
    655 
    656 		if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
    657 			return -EINVAL;
    658 		dbus_message_iter_get_basic(value, &nrec);
    659 
    660 		/* FIXME: set new nrec */
    661 		return 0;
    662 	} else if (g_strcmp0(property, "InbandRingtone") == 0) {
    663 		gboolean inband;
    664 
    665 		if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
    666 			return -EINVAL;
    667 		dbus_message_iter_get_basic(value, &inband);
    668 
    669 		/* FIXME: set new inband */
    670 		return 0;
    671 	}
    672 
    673 	return -EINVAL;
    674 }
    675 
    676 static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
    677 								void *data)
    678 {
    679 	struct media_transport *transport = data;
    680 	DBusMessageIter iter;
    681 	DBusMessageIter value;
    682 	const char *property, *sender;
    683 	GSList *l;
    684 	int err;
    685 
    686 	if (!dbus_message_iter_init(msg, &iter))
    687 		return btd_error_invalid_args(msg);
    688 
    689 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
    690 		return btd_error_invalid_args(msg);
    691 
    692 	dbus_message_iter_get_basic(&iter, &property);
    693 	dbus_message_iter_next(&iter);
    694 
    695 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
    696 		return btd_error_invalid_args(msg);
    697 	dbus_message_iter_recurse(&iter, &value);
    698 
    699 	sender = dbus_message_get_sender(msg);
    700 	err = -EINVAL;
    701 
    702 	/* Check if sender has acquired the transport */
    703 	for (l = transport->owners; l; l = l->next) {
    704 		struct media_owner *owner = l->data;
    705 
    706 		if (g_strcmp0(owner->name, sender) == 0) {
    707 			err = transport->set_property(transport, property,
    708 								&value);
    709 			break;
    710 		}
    711 	}
    712 
    713 	if (err < 0) {
    714 		if (err == -EINVAL)
    715 			return btd_error_invalid_args(msg);
    716 		return btd_error_failed(msg, strerror(-err));
    717 	}
    718 
    719 	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
    720 }
    721 
    722 static void get_properties_a2dp(struct media_transport *transport,
    723 						DBusMessageIter *dict)
    724 {
    725 	dict_append_entry(dict, "Delay", DBUS_TYPE_UINT16, &transport->delay);
    726 }
    727 
    728 static void get_properties_headset(struct media_transport *transport,
    729 						DBusMessageIter *dict)
    730 {
    731 	gboolean nrec, inband;
    732 	const char *routing;
    733 
    734 	nrec = headset_get_nrec(transport->device);
    735 	dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &nrec);
    736 
    737 	inband = headset_get_inband(transport->device);
    738 	dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN, &inband);
    739 
    740 	routing = headset_get_sco_hci(transport->device) ? "HCI" : "PCM";
    741 	dict_append_entry(dict, "Routing", DBUS_TYPE_STRING, &routing);
    742 }
    743 
    744 void transport_get_properties(struct media_transport *transport,
    745 							DBusMessageIter *iter)
    746 {
    747 	DBusMessageIter dict;
    748 	const char *uuid;
    749 	uint8_t codec;
    750 
    751 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
    752 			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
    753 			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
    754 			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
    755 
    756 	/* Device */
    757 	dict_append_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH,
    758 						&transport->device->path);
    759 
    760 	uuid = media_endpoint_get_uuid(transport->endpoint);
    761 	dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
    762 
    763 	codec = media_endpoint_get_codec(transport->endpoint);
    764 	dict_append_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec);
    765 
    766 	dict_append_array(&dict, "Configuration", DBUS_TYPE_BYTE,
    767 				&transport->configuration, transport->size);
    768 
    769 	if (transport->get_properties)
    770 		transport->get_properties(transport, &dict);
    771 
    772 	dbus_message_iter_close_container(iter, &dict);
    773 }
    774 
    775 static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
    776 					void *data)
    777 {
    778 	struct media_transport *transport = data;
    779 	DBusMessage *reply;
    780 	DBusMessageIter iter;
    781 
    782 	reply = dbus_message_new_method_return(msg);
    783 	if (!reply)
    784 		return NULL;
    785 
    786 	dbus_message_iter_init_append(reply, &iter);
    787 
    788 	transport_get_properties(transport, &iter);
    789 
    790 	return reply;
    791 }
    792 
    793 static GDBusMethodTable transport_methods[] = {
    794 	{ "GetProperties",	"",	"a{sv}",	get_properties },
    795 	{ "Acquire",		"s",	"h",		acquire,
    796 						G_DBUS_METHOD_FLAG_ASYNC},
    797 	{ "Release",		"s",	"",		release,
    798 						G_DBUS_METHOD_FLAG_ASYNC},
    799 	{ "SetProperty",	"sv",	"",		set_property },
    800 	{ },
    801 };
    802 
    803 static GDBusSignalTable transport_signals[] = {
    804 	{ "PropertyChanged",	"sv"	},
    805 	{ }
    806 };
    807 
    808 static void media_transport_free(void *data)
    809 {
    810 	struct media_transport *transport = data;
    811 	GSList *l;
    812 
    813 	for (l = transport->owners; l; l = l->next)
    814 		media_transport_remove(transport, l->data);
    815 
    816 	g_slist_free(transport->owners);
    817 
    818 	if (transport->session)
    819 		avdtp_unref(transport->session);
    820 
    821 	if (transport->nrec_id)
    822 		headset_remove_nrec_cb(transport->device, transport->nrec_id);
    823 
    824 	if (transport->conn)
    825 		dbus_connection_unref(transport->conn);
    826 
    827 	g_free(transport->configuration);
    828 	g_free(transport->path);
    829 	g_free(transport);
    830 }
    831 
    832 static void headset_nrec_changed(struct audio_device *dev, gboolean nrec,
    833 							void *user_data)
    834 {
    835 	struct media_transport *transport = user_data;
    836 
    837 	DBG("");
    838 
    839 	emit_property_changed(transport->conn, transport->path,
    840 				MEDIA_TRANSPORT_INTERFACE, "NREC",
    841 				DBUS_TYPE_BOOLEAN, &nrec);
    842 }
    843 
    844 struct media_transport *media_transport_create(DBusConnection *conn,
    845 						struct media_endpoint *endpoint,
    846 						struct audio_device *device,
    847 						uint8_t *configuration,
    848 						size_t size)
    849 {
    850 	struct media_transport *transport;
    851 	const char *uuid;
    852 	static int fd = 0;
    853 
    854 	transport = g_new0(struct media_transport, 1);
    855 	transport->conn = dbus_connection_ref(conn);
    856 	transport->device = device;
    857 	transport->endpoint = endpoint;
    858 	transport->configuration = g_new(uint8_t, size);
    859 	memcpy(transport->configuration, configuration, size);
    860 	transport->size = size;
    861 	transport->path = g_strdup_printf("%s/fd%d", device->path, fd++);
    862 	transport->fd = -1;
    863 
    864 	uuid = media_endpoint_get_uuid(endpoint);
    865 	if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0 ||
    866 			strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
    867 		transport->resume = resume_a2dp;
    868 		transport->suspend = suspend_a2dp;
    869 		transport->cancel = cancel_a2dp;
    870 		transport->get_properties = get_properties_a2dp;
    871 		transport->set_property = set_property_a2dp;
    872 	} else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
    873 			strcasecmp(uuid, HSP_AG_UUID) == 0) {
    874 		transport->resume = resume_headset;
    875 		transport->suspend = suspend_headset;
    876 		transport->cancel = cancel_headset;
    877 		transport->get_properties = get_properties_headset;
    878 		transport->set_property = set_property_headset;
    879 		transport->nrec_id = headset_add_nrec_cb(device,
    880 							headset_nrec_changed,
    881 							transport);
    882 	} else
    883 		goto fail;
    884 
    885 	if (g_dbus_register_interface(transport->conn, transport->path,
    886 				MEDIA_TRANSPORT_INTERFACE,
    887 				transport_methods, transport_signals, NULL,
    888 				transport, media_transport_free) == FALSE) {
    889 		error("Could not register transport %s", transport->path);
    890 		goto fail;
    891 	}
    892 
    893 	return transport;
    894 
    895 fail:
    896 	media_transport_free(transport);
    897 	return NULL;
    898 }
    899 
    900 const char *media_transport_get_path(struct media_transport *transport)
    901 {
    902 	return transport->path;
    903 }
    904 
    905 void media_transport_update_delay(struct media_transport *transport,
    906 							uint16_t delay)
    907 {
    908 	/* Check if delay really changed */
    909 	if (transport->delay == delay)
    910 		return;
    911 
    912 	transport->delay = delay;
    913 
    914 	emit_property_changed(transport->conn, transport->path,
    915 				MEDIA_TRANSPORT_INTERFACE, "Delay",
    916 				DBUS_TYPE_UINT16, &transport->delay);
    917 }
    918