~ubuntu-branches/ubuntu/wily/bluez/wily

« back to all changes in this revision

Viewing changes to audio/transport.c

ImportĀ upstreamĀ versionĀ 4.81

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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@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 acquire_request {
 
53
        DBusMessage             *msg;
 
54
        guint                   id;
 
55
        struct media_owner      *owner;
 
56
};
 
57
 
 
58
struct media_owner {
 
59
        struct media_transport  *transport;
 
60
        struct acquire_request *request;
 
61
        char                    *name;
 
62
        char                    *accesstype;
 
63
        guint                   watch;
 
64
};
 
65
 
 
66
struct media_transport {
 
67
        DBusConnection          *conn;
 
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) */
 
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
        void                    (*suspend) (struct media_transport *transport);
 
85
        void                    (*cancel) (struct media_transport *transport,
 
86
                                                                guint id);
 
87
        void                    (*get_properties) (
 
88
                                        struct media_transport *transport,
 
89
                                        DBusMessageIter *dict);
 
90
        int                     (*set_property) (
 
91
                                        struct media_transport *transport,
 
92
                                        const char *property,
 
93
                                        DBusMessageIter *value);
 
94
};
 
95
 
 
96
static inline DBusMessage *invalid_args(DBusMessage *msg)
 
97
{
 
98
        return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
 
99
                                        "Invalid arguments in method call");
 
100
}
 
101
 
 
102
static inline DBusMessage *error_failed(DBusMessage *msg, const char *desc)
 
103
{
 
104
        return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s", desc);
 
105
}
 
106
 
 
107
void media_transport_remove(struct media_transport *transport)
 
108
{
 
109
        char *path;
 
110
 
 
111
        path = g_strdup(transport->path);
 
112
 
 
113
        g_dbus_unregister_interface(transport->conn, path,
 
114
                                                MEDIA_TRANSPORT_INTERFACE);
 
115
 
 
116
        g_free(path);
 
117
}
 
118
 
 
119
static void acquire_request_free(struct acquire_request *req)
 
120
{
 
121
        struct media_owner *owner = req->owner;
 
122
        struct media_transport *transport = owner->transport;
 
123
 
 
124
        if (req->id)
 
125
                transport->cancel(owner->transport, req->id);
 
126
 
 
127
        if (req->msg)
 
128
                dbus_message_unref(req->msg);
 
129
 
 
130
        owner->request = NULL;
 
131
        g_free(req);
 
132
}
 
133
 
 
134
static gboolean media_transport_release(struct media_transport *transport,
 
135
                                        const char *accesstype)
 
136
{
 
137
        if (g_strstr_len(accesstype, -1, "r") != NULL) {
 
138
                transport->read_lock = FALSE;
 
139
                DBG("Transport %s: read lock released", transport->path);
 
140
        }
 
141
 
 
142
        if (g_strstr_len(accesstype, -1, "w") != NULL) {
 
143
                transport->write_lock = FALSE;
 
144
                DBG("Transport %s: write lock released", transport->path);
 
145
        }
 
146
 
 
147
        return TRUE;
 
148
}
 
149
 
 
150
static void media_owner_remove(struct media_owner *owner)
 
151
{
 
152
        struct media_transport *transport = owner->transport;
 
153
 
 
154
        media_transport_release(transport, owner->accesstype);
 
155
 
 
156
        if (owner->watch)
 
157
                g_dbus_remove_watch(transport->conn, owner->watch);
 
158
 
 
159
        if (owner->request) {
 
160
                DBusMessage *reply = g_dbus_create_error(owner->request->msg,
 
161
                                                ERROR_INTERFACE ".Failed",
 
162
                                                "%s", strerror(EIO));
 
163
 
 
164
                g_dbus_send_message(transport->conn, reply);
 
165
 
 
166
                acquire_request_free(owner->request);
 
167
        }
 
168
 
 
169
        transport->owners = g_slist_remove(transport->owners, owner);
 
170
 
 
171
        /* Suspend if the is no longer any owner */
 
172
        if (transport->owners == NULL)
 
173
                transport->suspend(transport);
 
174
 
 
175
        DBG("Owner removed: sender=%s accesstype=%s", owner->name,
 
176
                                                        owner->accesstype);
 
177
 
 
178
        g_free(owner->name);
 
179
        g_free(owner->accesstype);
 
180
        g_free(owner);
 
181
}
 
182
 
 
183
static gboolean media_transport_set_fd(struct media_transport *transport,
 
184
                                        int fd, uint16_t imtu, uint16_t omtu)
 
185
{
 
186
        if (transport->fd == fd)
 
187
                return TRUE;
 
188
 
 
189
        transport->fd = fd;
 
190
        transport->imtu = imtu;
 
191
        transport->omtu = omtu;
 
192
 
 
193
        info("%s: fd(%d) ready", transport->path, fd);
 
194
 
 
195
        emit_property_changed(transport->conn, transport->path,
 
196
                                MEDIA_TRANSPORT_INTERFACE, "IMTU",
 
197
                                DBUS_TYPE_UINT16, &transport->imtu);
 
198
 
 
199
        emit_property_changed(transport->conn, transport->path,
 
200
                                MEDIA_TRANSPORT_INTERFACE, "OMTU",
 
201
                                DBUS_TYPE_UINT16, &transport->omtu);
 
202
 
 
203
        return TRUE;
 
204
}
 
205
 
 
206
static gboolean remove_owner(gpointer data)
 
207
{
 
208
        media_owner_remove(data);
 
209
 
 
210
        return FALSE;
 
211
}
 
212
 
 
213
static void a2dp_resume_complete(struct avdtp *session,
 
214
                                struct avdtp_error *err, void *user_data)
 
215
{
 
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;
 
221
        int fd;
 
222
        uint16_t imtu, omtu;
 
223
 
 
224
        req->id = 0;
 
225
 
 
226
        if (err)
 
227
                goto fail;
 
228
 
 
229
        stream = a2dp_sep_get_stream(sep);
 
230
        if (stream == NULL)
 
231
                goto fail;
 
232
 
 
233
        if (avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, NULL) ==
 
234
                        FALSE)
 
235
                goto fail;
 
236
 
 
237
        media_transport_set_fd(transport, fd, imtu, omtu);
 
238
 
 
239
        if (g_dbus_send_reply(transport->conn, req->msg,
 
240
                                DBUS_TYPE_UNIX_FD, &fd,
 
241
                                DBUS_TYPE_INVALID) == FALSE)
 
242
                goto fail;
 
243
 
 
244
        return;
 
245
 
 
246
fail:
 
247
        /* Let the stream state change before removing the owner */
 
248
        g_idle_add(remove_owner, owner);
 
249
}
 
250
 
 
251
static guint resume_a2dp(struct media_transport *transport,
 
252
                                struct media_owner *owner)
 
253
{
 
254
        struct media_endpoint *endpoint = transport->endpoint;
 
255
        struct audio_device *device = transport->device;
 
256
        struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
 
257
 
 
258
        if (transport->session == NULL) {
 
259
                transport->session = avdtp_get(&device->src, &device->dst);
 
260
                if (transport->session == NULL)
 
261
                        return 0;
 
262
        }
 
263
 
 
264
        if (transport->in_use == TRUE)
 
265
                goto done;
 
266
 
 
267
        transport->in_use = a2dp_sep_lock(sep, transport->session);
 
268
        if (transport->in_use == FALSE)
 
269
                return 0;
 
270
 
 
271
done:
 
272
        return a2dp_resume(transport->session, sep, a2dp_resume_complete,
 
273
                                owner);
 
274
}
 
275
 
 
276
static void suspend_a2dp(struct media_transport *transport)
 
277
{
 
278
        struct media_endpoint *endpoint = transport->endpoint;
 
279
        struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
 
280
 
 
281
        a2dp_sep_unlock(sep, transport->session);
 
282
        transport->in_use = FALSE;
 
283
}
 
284
 
 
285
static void cancel_a2dp(struct media_transport *transport, guint id)
 
286
{
 
287
        a2dp_cancel(transport->device, id);
 
288
}
 
289
 
 
290
static void headset_resume_complete(struct audio_device *dev, void *user_data)
 
291
{
 
292
        struct media_owner *owner = user_data;
 
293
        struct acquire_request *req = owner->request;
 
294
        struct media_transport *transport = owner->transport;
 
295
        int fd;
 
296
 
 
297
        req->id = 0;
 
298
 
 
299
        if (dev == NULL)
 
300
                goto fail;
 
301
 
 
302
        fd = headset_get_sco_fd(dev);
 
303
        if (fd < 0)
 
304
                goto fail;
 
305
 
 
306
        media_transport_set_fd(transport, fd, 48, 48);
 
307
 
 
308
        if (g_dbus_send_reply(transport->conn, req->msg,
 
309
                                DBUS_TYPE_UNIX_FD, &fd,
 
310
                                DBUS_TYPE_INVALID) == FALSE)
 
311
                goto fail;
 
312
 
 
313
        return;
 
314
 
 
315
fail:
 
316
        media_owner_remove(owner);
 
317
}
 
318
 
 
319
static guint resume_headset(struct media_transport *transport,
 
320
                                struct media_owner *owner)
 
321
{
 
322
        struct audio_device *device = transport->device;
 
323
 
 
324
        if (transport->in_use == TRUE)
 
325
                goto done;
 
326
 
 
327
        transport->in_use = headset_lock(device, HEADSET_LOCK_READ |
 
328
                                                HEADSET_LOCK_WRITE);
 
329
        if (transport->in_use == FALSE)
 
330
                return 0;
 
331
 
 
332
done:
 
333
        return headset_request_stream(device, headset_resume_complete,
 
334
                                        owner);
 
335
}
 
336
 
 
337
static void suspend_headset(struct media_transport *transport)
 
338
{
 
339
        struct audio_device *device = transport->device;
 
340
 
 
341
        headset_unlock(device, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE);
 
342
        transport->in_use = FALSE;
 
343
}
 
344
 
 
345
static void cancel_headset(struct media_transport *transport, guint id)
 
346
{
 
347
        headset_cancel_stream(transport->device, id);
 
348
}
 
349
 
 
350
static void media_owner_exit(DBusConnection *connection, void *user_data)
 
351
{
 
352
        struct media_owner *owner = user_data;
 
353
 
 
354
        owner->watch = 0;
 
355
        if (owner->request != NULL)
 
356
                acquire_request_free(owner->request);
 
357
 
 
358
        media_owner_remove(owner);
 
359
}
 
360
 
 
361
static gboolean media_transport_acquire(struct media_transport *transport,
 
362
                                                        const char *accesstype)
 
363
{
 
364
        gboolean read_lock = FALSE, write_lock = FALSE;
 
365
 
 
366
        if (g_strstr_len(accesstype, -1, "r") != NULL) {
 
367
                if (transport->read_lock == TRUE)
 
368
                        return FALSE;
 
369
                read_lock = TRUE;
 
370
        }
 
371
 
 
372
        if (g_strstr_len(accesstype, -1, "w") != NULL) {
 
373
                if (transport->write_lock == TRUE)
 
374
                        return FALSE;
 
375
                write_lock = TRUE;
 
376
        }
 
377
 
 
378
        /* Check invalid accesstype */
 
379
        if (read_lock == FALSE && write_lock == FALSE)
 
380
                return FALSE;
 
381
 
 
382
        if (read_lock) {
 
383
                transport->read_lock = read_lock;
 
384
                DBG("Transport %s: read lock acquired", transport->path);
 
385
        }
 
386
 
 
387
        if (write_lock) {
 
388
                transport->write_lock = write_lock;
 
389
                DBG("Transport %s: write lock acquired", transport->path);
 
390
        }
 
391
 
 
392
 
 
393
        return TRUE;
 
394
}
 
395
 
 
396
static struct media_owner *media_owner_create(
 
397
                                        struct media_transport *transport,
 
398
                                        DBusMessage *msg,
 
399
                                        const char *accesstype)
 
400
{
 
401
        struct media_owner *owner;
 
402
 
 
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,
 
408
                                                        owner->name,
 
409
                                                        media_owner_exit,
 
410
                                                        owner, NULL);
 
411
        transport->owners = g_slist_append(transport->owners, owner);
 
412
 
 
413
        DBG("Owner created: sender=%s accesstype=%s", owner->name,
 
414
                        accesstype);
 
415
 
 
416
        return owner;
 
417
}
 
418
 
 
419
static struct media_owner *media_transport_find_owner(
 
420
                                        struct media_transport *transport,
 
421
                                        const char *name)
 
422
{
 
423
        GSList *l;
 
424
 
 
425
        for (l = transport->owners; l; l = l->next) {
 
426
                struct media_owner *owner = l->data;
 
427
 
 
428
                if (g_strcmp0(owner->name, name) == 0)
 
429
                        return owner;
 
430
        }
 
431
 
 
432
        return NULL;
 
433
}
 
434
 
 
435
static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
 
436
                                        void *data)
 
437
{
 
438
        struct media_transport *transport = data;
 
439
        struct media_owner *owner;
 
440
        struct acquire_request *req;
 
441
        const char *accesstype, *sender;
 
442
 
 
443
        if (!dbus_message_get_args(msg, NULL,
 
444
                                DBUS_TYPE_STRING, &accesstype,
 
445
                                DBUS_TYPE_INVALID))
 
446
                return NULL;
 
447
 
 
448
        sender = dbus_message_get_sender(msg);
 
449
 
 
450
        owner = media_transport_find_owner(transport, sender);
 
451
        if (owner != NULL)
 
452
                return error_failed(msg, strerror(EPERM));
 
453
 
 
454
        if (media_transport_acquire(transport, accesstype) == FALSE)
 
455
                return error_failed(msg, strerror(EPERM));
 
456
 
 
457
        owner = media_owner_create(transport, msg, accesstype);
 
458
        req = g_new0(struct acquire_request, 1);
 
459
        req->msg = dbus_message_ref(msg);
 
460
        req->owner = owner;
 
461
        req->id = transport->resume(transport, owner);
 
462
        owner->request = req;
 
463
        if (req->id == 0)
 
464
                media_owner_remove(owner);
 
465
 
 
466
        return NULL;
 
467
}
 
468
 
 
469
static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
 
470
                                        void *data)
 
471
{
 
472
        struct media_transport *transport = data;
 
473
        struct media_owner *owner;
 
474
        const char *accesstype, *sender;
 
475
 
 
476
        if (!dbus_message_get_args(msg, NULL,
 
477
                                DBUS_TYPE_STRING, &accesstype,
 
478
                                DBUS_TYPE_INVALID))
 
479
                return NULL;
 
480
 
 
481
        sender = dbus_message_get_sender(msg);
 
482
 
 
483
        owner = media_transport_find_owner(transport, sender);
 
484
        if (owner == NULL)
 
485
                return error_failed(msg, strerror(EPERM));
 
486
 
 
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, ' ');
 
492
        } else
 
493
                return error_failed(msg, strerror(EPERM));
 
494
 
 
495
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 
496
}
 
497
 
 
498
static int set_property_a2dp(struct media_transport *transport,
 
499
                                                const char *property,
 
500
                                                DBusMessageIter *value)
 
501
{
 
502
        if (g_strcmp0(property, "Delay") == 0) {
 
503
                if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
 
504
                        return -EINVAL;
 
505
                dbus_message_iter_get_basic(value, &transport->delay);
 
506
 
 
507
                /* FIXME: send new delay */
 
508
                return 0;
 
509
        }
 
510
 
 
511
        return -EINVAL;
 
512
}
 
513
 
 
514
static int set_property_headset(struct media_transport *transport,
 
515
                                                const char *property,
 
516
                                                DBusMessageIter *value)
 
517
{
 
518
        if (g_strcmp0(property, "NREC") == 0) {
 
519
                gboolean nrec;
 
520
 
 
521
                if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
 
522
                        return -EINVAL;
 
523
                dbus_message_iter_get_basic(value, &nrec);
 
524
 
 
525
                /* FIXME: set new nrec */
 
526
                return 0;
 
527
        } else if (g_strcmp0(property, "InbandRingtone") == 0) {
 
528
                gboolean inband;
 
529
 
 
530
                if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
 
531
                        return -EINVAL;
 
532
                dbus_message_iter_get_basic(value, &inband);
 
533
 
 
534
                /* FIXME: set new inband */
 
535
                return 0;
 
536
        }
 
537
 
 
538
        return -EINVAL;
 
539
}
 
540
 
 
541
static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
 
542
                                                                void *data)
 
543
{
 
544
        struct media_transport *transport = data;
 
545
        DBusMessageIter iter;
 
546
        DBusMessageIter value;
 
547
        const char *property, *sender;
 
548
        GSList *l;
 
549
        int err;
 
550
 
 
551
        if (!dbus_message_iter_init(msg, &iter))
 
552
                return invalid_args(msg);
 
553
 
 
554
        if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
 
555
                return invalid_args(msg);
 
556
 
 
557
        dbus_message_iter_get_basic(&iter, &property);
 
558
        dbus_message_iter_next(&iter);
 
559
 
 
560
        if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
 
561
                return invalid_args(msg);
 
562
        dbus_message_iter_recurse(&iter, &value);
 
563
 
 
564
        sender = dbus_message_get_sender(msg);
 
565
        err = -EINVAL;
 
566
 
 
567
        /* Check if sender has acquired the transport */
 
568
        for (l = transport->owners; l; l = l->next) {
 
569
                struct media_owner *owner = l->data;
 
570
 
 
571
                if (g_strcmp0(owner->name, sender) == 0) {
 
572
                        err = transport->set_property(transport, property,
 
573
                                                                &value);
 
574
                        break;
 
575
                }
 
576
        }
 
577
 
 
578
        if (err < 0) {
 
579
                if (err == -EINVAL)
 
580
                        return invalid_args(msg);
 
581
                return error_failed(msg, strerror(-err));
 
582
        }
 
583
 
 
584
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 
585
}
 
586
 
 
587
static void get_properties_a2dp(struct media_transport *transport,
 
588
                                                DBusMessageIter *dict)
 
589
{
 
590
        dict_append_entry(dict, "Delay", DBUS_TYPE_UINT16, &transport->delay);
 
591
}
 
592
 
 
593
static void get_properties_headset(struct media_transport *transport,
 
594
                                                DBusMessageIter *dict)
 
595
{
 
596
        gboolean nrec, inband;
 
597
 
 
598
        nrec = headset_get_nrec(transport->device);
 
599
        dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &nrec);
 
600
 
 
601
        inband = headset_get_inband(transport->device);
 
602
        dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN, &inband);
 
603
}
 
604
 
 
605
void transport_get_properties(struct media_transport *transport,
 
606
                                                        DBusMessageIter *iter)
 
607
{
 
608
        DBusMessageIter dict;
 
609
        const char *uuid;
 
610
        uint8_t codec;
 
611
 
 
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);
 
616
 
 
617
        /* Device */
 
618
        dict_append_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH,
 
619
                                                &transport->device->path);
 
620
 
 
621
        dict_append_entry(&dict, "IMTU", DBUS_TYPE_UINT16,
 
622
                                                &transport->imtu);
 
623
 
 
624
        dict_append_entry(&dict, "OMTU", DBUS_TYPE_UINT16,
 
625
                                                &transport->omtu);
 
626
 
 
627
        uuid = media_endpoint_get_uuid(transport->endpoint);
 
628
        dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
 
629
 
 
630
        codec = media_endpoint_get_codec(transport->endpoint);
 
631
        dict_append_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec);
 
632
 
 
633
        dict_append_array(&dict, "Configuration", DBUS_TYPE_BYTE,
 
634
                                &transport->configuration, transport->size);
 
635
 
 
636
        if (transport->get_properties)
 
637
                transport->get_properties(transport, &dict);
 
638
 
 
639
        dbus_message_iter_close_container(iter, &dict);
 
640
}
 
641
 
 
642
static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
 
643
                                        void *data)
 
644
{
 
645
        struct media_transport *transport = data;
 
646
        DBusMessage *reply;
 
647
        DBusMessageIter iter;
 
648
 
 
649
        reply = dbus_message_new_method_return(msg);
 
650
        if (!reply)
 
651
                return NULL;
 
652
 
 
653
        dbus_message_iter_init_append(reply, &iter);
 
654
 
 
655
        transport_get_properties(transport, &iter);
 
656
 
 
657
        return reply;
 
658
}
 
659
 
 
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 },
 
666
        { },
 
667
};
 
668
 
 
669
static GDBusSignalTable transport_signals[] = {
 
670
        { "PropertyChanged",    "sv"    },
 
671
        { }
 
672
};
 
673
 
 
674
static void media_transport_free(void *data)
 
675
{
 
676
        struct media_transport *transport = data;
 
677
 
 
678
        g_slist_foreach(transport->owners, (GFunc) media_owner_remove,
 
679
                                NULL);
 
680
        g_slist_free(transport->owners);
 
681
 
 
682
        if (transport->session)
 
683
                avdtp_unref(transport->session);
 
684
 
 
685
        if (transport->conn)
 
686
                dbus_connection_unref(transport->conn);
 
687
 
 
688
        g_free(transport->configuration);
 
689
        g_free(transport->path);
 
690
        g_free(transport);
 
691
}
 
692
 
 
693
struct media_transport *media_transport_create(DBusConnection *conn,
 
694
                                                struct media_endpoint *endpoint,
 
695
                                                struct audio_device *device,
 
696
                                                uint8_t *configuration,
 
697
                                                size_t size)
 
698
{
 
699
        struct media_transport *transport;
 
700
        const char *uuid;
 
701
        static int fd = 0;
 
702
 
 
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++);
 
711
        transport->fd = -1;
 
712
 
 
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;
 
728
        } else
 
729
                goto fail;
 
730
 
 
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);
 
736
                goto fail;
 
737
        }
 
738
 
 
739
        return transport;
 
740
 
 
741
fail:
 
742
        media_transport_free(transport);
 
743
        return NULL;
 
744
}
 
745
 
 
746
const char *media_transport_get_path(struct media_transport *transport)
 
747
{
 
748
        return transport->path;
 
749
}
 
750
 
 
751
void media_transport_update_delay(struct media_transport *transport,
 
752
                                                        uint16_t delay)
 
753
{
 
754
        /* Check if delay really changed */
 
755
        if (transport->delay == delay)
 
756
                return;
 
757
 
 
758
        transport->delay = delay;
 
759
 
 
760
        emit_property_changed(transport->conn, transport->path,
 
761
                                MEDIA_TRANSPORT_INTERFACE, "Delay",
 
762
                                DBUS_TYPE_UINT16, &transport->delay);
 
763
}