3
* BlueZ - Bluetooth protocol stack for Linux
5
* Copyright (C) 2006-2007 Nokia Corporation
6
* Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
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.
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.
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
34
#include "../src/adapter.h"
35
#include "../src/dbus-common.h"
42
#include "transport.h"
46
#ifndef DBUS_TYPE_UNIX_FD
47
#define DBUS_TYPE_UNIX_FD -1
50
#define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport"
52
struct acquire_request {
55
struct media_owner *owner;
59
struct media_transport *transport;
60
struct acquire_request *request;
66
struct media_transport {
68
char *path; /* Transport object path */
69
struct audio_device *device; /* Transport device */
70
struct avdtp *session; /* Signalling session (a2dp only) */
71
struct media_endpoint *endpoint; /* Transport endpoint */
72
GSList *owners; /* Transport owners */
73
uint8_t *configuration; /* Transport configuration */
74
int size; /* Transport configuration size */
75
int fd; /* Transport file descriptor */
76
uint16_t imtu; /* Transport input mtu */
77
uint16_t omtu; /* Transport output mtu */
78
uint16_t delay; /* Transport delay (a2dp only) */
82
guint (*resume) (struct media_transport *transport,
83
struct media_owner *owner);
84
void (*suspend) (struct media_transport *transport);
85
void (*cancel) (struct media_transport *transport,
87
void (*get_properties) (
88
struct media_transport *transport,
89
DBusMessageIter *dict);
91
struct media_transport *transport,
93
DBusMessageIter *value);
96
static inline DBusMessage *invalid_args(DBusMessage *msg)
98
return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
99
"Invalid arguments in method call");
102
static inline DBusMessage *error_failed(DBusMessage *msg, const char *desc)
104
return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s", desc);
107
void media_transport_remove(struct media_transport *transport)
111
path = g_strdup(transport->path);
113
g_dbus_unregister_interface(transport->conn, path,
114
MEDIA_TRANSPORT_INTERFACE);
119
static void acquire_request_free(struct acquire_request *req)
121
struct media_owner *owner = req->owner;
122
struct media_transport *transport = owner->transport;
125
transport->cancel(owner->transport, req->id);
128
dbus_message_unref(req->msg);
130
owner->request = NULL;
134
static gboolean media_transport_release(struct media_transport *transport,
135
const char *accesstype)
137
if (g_strstr_len(accesstype, -1, "r") != NULL) {
138
transport->read_lock = FALSE;
139
DBG("Transport %s: read lock released", transport->path);
142
if (g_strstr_len(accesstype, -1, "w") != NULL) {
143
transport->write_lock = FALSE;
144
DBG("Transport %s: write lock released", transport->path);
150
static void media_owner_remove(struct media_owner *owner)
152
struct media_transport *transport = owner->transport;
154
media_transport_release(transport, owner->accesstype);
157
g_dbus_remove_watch(transport->conn, owner->watch);
159
if (owner->request) {
160
DBusMessage *reply = g_dbus_create_error(owner->request->msg,
161
ERROR_INTERFACE ".Failed",
162
"%s", strerror(EIO));
164
g_dbus_send_message(transport->conn, reply);
166
acquire_request_free(owner->request);
169
transport->owners = g_slist_remove(transport->owners, owner);
171
/* Suspend if the is no longer any owner */
172
if (transport->owners == NULL)
173
transport->suspend(transport);
175
DBG("Owner removed: sender=%s accesstype=%s", owner->name,
179
g_free(owner->accesstype);
183
static gboolean media_transport_set_fd(struct media_transport *transport,
184
int fd, uint16_t imtu, uint16_t omtu)
186
if (transport->fd == fd)
190
transport->imtu = imtu;
191
transport->omtu = omtu;
193
info("%s: fd(%d) ready", transport->path, fd);
195
emit_property_changed(transport->conn, transport->path,
196
MEDIA_TRANSPORT_INTERFACE, "IMTU",
197
DBUS_TYPE_UINT16, &transport->imtu);
199
emit_property_changed(transport->conn, transport->path,
200
MEDIA_TRANSPORT_INTERFACE, "OMTU",
201
DBUS_TYPE_UINT16, &transport->omtu);
206
static gboolean remove_owner(gpointer data)
208
media_owner_remove(data);
213
static void a2dp_resume_complete(struct avdtp *session,
214
struct avdtp_error *err, void *user_data)
216
struct media_owner *owner = user_data;
217
struct acquire_request *req = owner->request;
218
struct media_transport *transport = owner->transport;
219
struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
220
struct avdtp_stream *stream;
229
stream = a2dp_sep_get_stream(sep);
233
if (avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, NULL) ==
237
media_transport_set_fd(transport, fd, imtu, omtu);
239
if (g_dbus_send_reply(transport->conn, req->msg,
240
DBUS_TYPE_UNIX_FD, &fd,
241
DBUS_TYPE_INVALID) == FALSE)
247
/* Let the stream state change before removing the owner */
248
g_idle_add(remove_owner, owner);
251
static guint resume_a2dp(struct media_transport *transport,
252
struct media_owner *owner)
254
struct media_endpoint *endpoint = transport->endpoint;
255
struct audio_device *device = transport->device;
256
struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
258
if (transport->session == NULL) {
259
transport->session = avdtp_get(&device->src, &device->dst);
260
if (transport->session == NULL)
264
if (transport->in_use == TRUE)
267
transport->in_use = a2dp_sep_lock(sep, transport->session);
268
if (transport->in_use == FALSE)
272
return a2dp_resume(transport->session, sep, a2dp_resume_complete,
276
static void suspend_a2dp(struct media_transport *transport)
278
struct media_endpoint *endpoint = transport->endpoint;
279
struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
281
a2dp_sep_unlock(sep, transport->session);
282
transport->in_use = FALSE;
285
static void cancel_a2dp(struct media_transport *transport, guint id)
287
a2dp_cancel(transport->device, id);
290
static void headset_resume_complete(struct audio_device *dev, void *user_data)
292
struct media_owner *owner = user_data;
293
struct acquire_request *req = owner->request;
294
struct media_transport *transport = owner->transport;
302
fd = headset_get_sco_fd(dev);
306
media_transport_set_fd(transport, fd, 48, 48);
308
if (g_dbus_send_reply(transport->conn, req->msg,
309
DBUS_TYPE_UNIX_FD, &fd,
310
DBUS_TYPE_INVALID) == FALSE)
316
media_owner_remove(owner);
319
static guint resume_headset(struct media_transport *transport,
320
struct media_owner *owner)
322
struct audio_device *device = transport->device;
324
if (transport->in_use == TRUE)
327
transport->in_use = headset_lock(device, HEADSET_LOCK_READ |
329
if (transport->in_use == FALSE)
333
return headset_request_stream(device, headset_resume_complete,
337
static void suspend_headset(struct media_transport *transport)
339
struct audio_device *device = transport->device;
341
headset_unlock(device, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE);
342
transport->in_use = FALSE;
345
static void cancel_headset(struct media_transport *transport, guint id)
347
headset_cancel_stream(transport->device, id);
350
static void media_owner_exit(DBusConnection *connection, void *user_data)
352
struct media_owner *owner = user_data;
355
if (owner->request != NULL)
356
acquire_request_free(owner->request);
358
media_owner_remove(owner);
361
static gboolean media_transport_acquire(struct media_transport *transport,
362
const char *accesstype)
364
gboolean read_lock = FALSE, write_lock = FALSE;
366
if (g_strstr_len(accesstype, -1, "r") != NULL) {
367
if (transport->read_lock == TRUE)
372
if (g_strstr_len(accesstype, -1, "w") != NULL) {
373
if (transport->write_lock == TRUE)
378
/* Check invalid accesstype */
379
if (read_lock == FALSE && write_lock == FALSE)
383
transport->read_lock = read_lock;
384
DBG("Transport %s: read lock acquired", transport->path);
388
transport->write_lock = write_lock;
389
DBG("Transport %s: write lock acquired", transport->path);
396
static struct media_owner *media_owner_create(
397
struct media_transport *transport,
399
const char *accesstype)
401
struct media_owner *owner;
403
owner = g_new0(struct media_owner, 1);
404
owner->transport = transport;
405
owner->name = g_strdup(dbus_message_get_sender(msg));
406
owner->accesstype = g_strdup(accesstype);
407
owner->watch = g_dbus_add_disconnect_watch(transport->conn,
411
transport->owners = g_slist_append(transport->owners, owner);
413
DBG("Owner created: sender=%s accesstype=%s", owner->name,
419
static struct media_owner *media_transport_find_owner(
420
struct media_transport *transport,
425
for (l = transport->owners; l; l = l->next) {
426
struct media_owner *owner = l->data;
428
if (g_strcmp0(owner->name, name) == 0)
435
static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
438
struct media_transport *transport = data;
439
struct media_owner *owner;
440
struct acquire_request *req;
441
const char *accesstype, *sender;
443
if (!dbus_message_get_args(msg, NULL,
444
DBUS_TYPE_STRING, &accesstype,
448
sender = dbus_message_get_sender(msg);
450
owner = media_transport_find_owner(transport, sender);
452
return error_failed(msg, strerror(EPERM));
454
if (media_transport_acquire(transport, accesstype) == FALSE)
455
return error_failed(msg, strerror(EPERM));
457
owner = media_owner_create(transport, msg, accesstype);
458
req = g_new0(struct acquire_request, 1);
459
req->msg = dbus_message_ref(msg);
461
req->id = transport->resume(transport, owner);
462
owner->request = req;
464
media_owner_remove(owner);
469
static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
472
struct media_transport *transport = data;
473
struct media_owner *owner;
474
const char *accesstype, *sender;
476
if (!dbus_message_get_args(msg, NULL,
477
DBUS_TYPE_STRING, &accesstype,
481
sender = dbus_message_get_sender(msg);
483
owner = media_transport_find_owner(transport, sender);
485
return error_failed(msg, strerror(EPERM));
487
if (g_strcmp0(owner->accesstype, accesstype) == 0)
488
media_owner_remove(owner);
489
else if (g_strstr_len(owner->accesstype, -1, accesstype) != NULL) {
490
media_transport_release(transport, accesstype);
491
g_strdelimit(owner->accesstype, accesstype, ' ');
493
return error_failed(msg, strerror(EPERM));
495
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
498
static int set_property_a2dp(struct media_transport *transport,
499
const char *property,
500
DBusMessageIter *value)
502
if (g_strcmp0(property, "Delay") == 0) {
503
if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
505
dbus_message_iter_get_basic(value, &transport->delay);
507
/* FIXME: send new delay */
514
static int set_property_headset(struct media_transport *transport,
515
const char *property,
516
DBusMessageIter *value)
518
if (g_strcmp0(property, "NREC") == 0) {
521
if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
523
dbus_message_iter_get_basic(value, &nrec);
525
/* FIXME: set new nrec */
527
} else if (g_strcmp0(property, "InbandRingtone") == 0) {
530
if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
532
dbus_message_iter_get_basic(value, &inband);
534
/* FIXME: set new inband */
541
static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
544
struct media_transport *transport = data;
545
DBusMessageIter iter;
546
DBusMessageIter value;
547
const char *property, *sender;
551
if (!dbus_message_iter_init(msg, &iter))
552
return invalid_args(msg);
554
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
555
return invalid_args(msg);
557
dbus_message_iter_get_basic(&iter, &property);
558
dbus_message_iter_next(&iter);
560
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
561
return invalid_args(msg);
562
dbus_message_iter_recurse(&iter, &value);
564
sender = dbus_message_get_sender(msg);
567
/* Check if sender has acquired the transport */
568
for (l = transport->owners; l; l = l->next) {
569
struct media_owner *owner = l->data;
571
if (g_strcmp0(owner->name, sender) == 0) {
572
err = transport->set_property(transport, property,
580
return invalid_args(msg);
581
return error_failed(msg, strerror(-err));
584
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
587
static void get_properties_a2dp(struct media_transport *transport,
588
DBusMessageIter *dict)
590
dict_append_entry(dict, "Delay", DBUS_TYPE_UINT16, &transport->delay);
593
static void get_properties_headset(struct media_transport *transport,
594
DBusMessageIter *dict)
596
gboolean nrec, inband;
598
nrec = headset_get_nrec(transport->device);
599
dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &nrec);
601
inband = headset_get_inband(transport->device);
602
dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN, &inband);
605
void transport_get_properties(struct media_transport *transport,
606
DBusMessageIter *iter)
608
DBusMessageIter dict;
612
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
613
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
614
DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
615
DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
618
dict_append_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH,
619
&transport->device->path);
621
dict_append_entry(&dict, "IMTU", DBUS_TYPE_UINT16,
624
dict_append_entry(&dict, "OMTU", DBUS_TYPE_UINT16,
627
uuid = media_endpoint_get_uuid(transport->endpoint);
628
dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
630
codec = media_endpoint_get_codec(transport->endpoint);
631
dict_append_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec);
633
dict_append_array(&dict, "Configuration", DBUS_TYPE_BYTE,
634
&transport->configuration, transport->size);
636
if (transport->get_properties)
637
transport->get_properties(transport, &dict);
639
dbus_message_iter_close_container(iter, &dict);
642
static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
645
struct media_transport *transport = data;
647
DBusMessageIter iter;
649
reply = dbus_message_new_method_return(msg);
653
dbus_message_iter_init_append(reply, &iter);
655
transport_get_properties(transport, &iter);
660
static GDBusMethodTable transport_methods[] = {
661
{ "GetProperties", "", "a{sv}", get_properties },
662
{ "Acquire", "s", "h", acquire,
663
G_DBUS_METHOD_FLAG_ASYNC},
664
{ "Release", "s", "", release },
665
{ "SetProperty", "sv", "", set_property },
669
static GDBusSignalTable transport_signals[] = {
670
{ "PropertyChanged", "sv" },
674
static void media_transport_free(void *data)
676
struct media_transport *transport = data;
678
g_slist_foreach(transport->owners, (GFunc) media_owner_remove,
680
g_slist_free(transport->owners);
682
if (transport->session)
683
avdtp_unref(transport->session);
686
dbus_connection_unref(transport->conn);
688
g_free(transport->configuration);
689
g_free(transport->path);
693
struct media_transport *media_transport_create(DBusConnection *conn,
694
struct media_endpoint *endpoint,
695
struct audio_device *device,
696
uint8_t *configuration,
699
struct media_transport *transport;
703
transport = g_new0(struct media_transport, 1);
704
transport->conn = dbus_connection_ref(conn);
705
transport->device = device;
706
transport->endpoint = endpoint;
707
transport->configuration = g_new(uint8_t, size);
708
memcpy(transport->configuration, configuration, size);
709
transport->size = size;
710
transport->path = g_strdup_printf("%s/fd%d", device->path, fd++);
713
uuid = media_endpoint_get_uuid(endpoint);
714
if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0 ||
715
strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
716
transport->resume = resume_a2dp;
717
transport->suspend = suspend_a2dp;
718
transport->cancel = cancel_a2dp;
719
transport->get_properties = get_properties_a2dp;
720
transport->set_property = set_property_a2dp;
721
} else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
722
strcasecmp(uuid, HSP_AG_UUID) == 0) {
723
transport->resume = resume_headset;
724
transport->suspend = suspend_headset;
725
transport->cancel = cancel_headset;
726
transport->get_properties = get_properties_headset;
727
transport->set_property = set_property_headset;
731
if (g_dbus_register_interface(transport->conn, transport->path,
732
MEDIA_TRANSPORT_INTERFACE,
733
transport_methods, transport_signals, NULL,
734
transport, media_transport_free) == FALSE) {
735
error("Could not register transport %s", transport->path);
742
media_transport_free(transport);
746
const char *media_transport_get_path(struct media_transport *transport)
748
return transport->path;
751
void media_transport_update_delay(struct media_transport *transport,
754
/* Check if delay really changed */
755
if (transport->delay == delay)
758
transport->delay = delay;
760
emit_property_changed(transport->conn, transport->path,
761
MEDIA_TRANSPORT_INTERFACE, "Delay",
762
DBUS_TYPE_UINT16, &transport->delay);