| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2006-2007 Nokia Corporation
- * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #define _GNU_SOURCE
- #include <errno.h>
- #include <glib.h>
- #include "lib/bluetooth.h"
- #include "lib/sdp.h"
- #include "lib/uuid.h"
- #include "gdbus/gdbus.h"
- #include "src/adapter.h"
- #include "src/device.h"
- #include "src/dbus-common.h"
- #include "src/log.h"
- #include "src/error.h"
- #include "src/shared/queue.h"
- #include "avdtp.h"
- #include "media.h"
- #include "transport.h"
- #include "a2dp.h"
- #include "sink.h"
- #include "source.h"
- #include "avrcp.h"
- #define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1"
- typedef enum {
- TRANSPORT_STATE_IDLE, /* Not acquired and suspended */
- TRANSPORT_STATE_PENDING, /* Playing but not acquired */
- TRANSPORT_STATE_REQUESTING, /* Acquire in progress */
- TRANSPORT_STATE_ACTIVE, /* Acquired and playing */
- TRANSPORT_STATE_SUSPENDING, /* Release in progress */
- } transport_state_t;
- static char *str_state[] = {
- "TRANSPORT_STATE_IDLE",
- "TRANSPORT_STATE_PENDING",
- "TRANSPORT_STATE_REQUESTING",
- "TRANSPORT_STATE_ACTIVE",
- "TRANSPORT_STATE_SUSPENDING",
- };
- struct media_request {
- DBusMessage *msg;
- guint id;
- };
- struct media_owner {
- struct media_transport *transport;
- struct media_request *pending;
- char *name;
- guint watch;
- };
- struct a2dp_transport {
- struct avdtp *session;
- uint16_t delay;
- int8_t volume;
- };
- struct media_transport {
- char *path; /* Transport object path */
- struct btd_device *device; /* Transport device */
- const char *remote_endpoint; /* Transport remote SEP */
- struct media_endpoint *endpoint; /* Transport endpoint */
- struct media_owner *owner; /* Transport owner */
- uint8_t *configuration; /* Transport configuration */
- int size; /* Transport configuration size */
- int fd; /* Transport file descriptor */
- uint16_t imtu; /* Transport input mtu */
- uint16_t omtu; /* Transport output mtu */
- transport_state_t state;
- guint hs_watch;
- guint source_watch;
- guint sink_watch;
- guint (*resume) (struct media_transport *transport,
- struct media_owner *owner);
- guint (*suspend) (struct media_transport *transport,
- struct media_owner *owner);
- void (*cancel) (struct media_transport *transport,
- guint id);
- GDestroyNotify destroy;
- void *data;
- };
- static GSList *transports = NULL;
- static const char *state2str(transport_state_t state)
- {
- switch (state) {
- case TRANSPORT_STATE_IDLE:
- case TRANSPORT_STATE_REQUESTING:
- return "idle";
- case TRANSPORT_STATE_PENDING:
- return "pending";
- case TRANSPORT_STATE_ACTIVE:
- case TRANSPORT_STATE_SUSPENDING:
- return "active";
- }
- return NULL;
- }
- static gboolean state_in_use(transport_state_t state)
- {
- switch (state) {
- case TRANSPORT_STATE_IDLE:
- case TRANSPORT_STATE_PENDING:
- return FALSE;
- case TRANSPORT_STATE_REQUESTING:
- case TRANSPORT_STATE_ACTIVE:
- case TRANSPORT_STATE_SUSPENDING:
- return TRUE;
- }
- return FALSE;
- }
- static void transport_set_state(struct media_transport *transport,
- transport_state_t state)
- {
- transport_state_t old_state = transport->state;
- const char *str;
- if (old_state == state)
- return;
- transport->state = state;
- DBG("State changed %s: %s -> %s", transport->path, str_state[old_state],
- str_state[state]);
- str = state2str(state);
- if (g_strcmp0(str, state2str(old_state)) != 0)
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- transport->path,
- MEDIA_TRANSPORT_INTERFACE,
- "State");
- }
- void media_transport_destroy(struct media_transport *transport)
- {
- char *path;
- if (transport->sink_watch)
- sink_remove_state_cb(transport->sink_watch);
- if (transport->source_watch)
- source_remove_state_cb(transport->source_watch);
- path = g_strdup(transport->path);
- g_dbus_unregister_interface(btd_get_dbus_connection(), path,
- MEDIA_TRANSPORT_INTERFACE);
- g_free(path);
- }
- static struct media_request *media_request_create(DBusMessage *msg, guint id)
- {
- struct media_request *req;
- req = g_new0(struct media_request, 1);
- req->msg = dbus_message_ref(msg);
- req->id = id;
- DBG("Request created: method=%s id=%u", dbus_message_get_member(msg),
- id);
- return req;
- }
- static void media_request_reply(struct media_request *req, int err)
- {
- DBusMessage *reply;
- DBG("Request %s Reply %s", dbus_message_get_member(req->msg),
- strerror(err));
- if (!err)
- reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID);
- else
- reply = g_dbus_create_error(req->msg,
- ERROR_INTERFACE ".Failed",
- "%s", strerror(err));
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- }
- static void media_owner_remove(struct media_owner *owner)
- {
- struct media_transport *transport = owner->transport;
- struct media_request *req = owner->pending;
- if (!req)
- return;
- DBG("Owner %s Request %s", owner->name,
- dbus_message_get_member(req->msg));
- if (req->id)
- transport->cancel(transport, req->id);
- owner->pending = NULL;
- if (req->msg)
- dbus_message_unref(req->msg);
- g_free(req);
- }
- static void media_owner_free(struct media_owner *owner)
- {
- DBG("Owner %s", owner->name);
- media_owner_remove(owner);
- g_free(owner->name);
- g_free(owner);
- }
- static void media_transport_remove_owner(struct media_transport *transport)
- {
- struct media_owner *owner = transport->owner;
- DBG("Transport %s Owner %s", transport->path, owner->name);
- /* Reply if owner has a pending request */
- if (owner->pending)
- media_request_reply(owner->pending, EIO);
- transport->owner = NULL;
- if (owner->watch)
- g_dbus_remove_watch(btd_get_dbus_connection(), owner->watch);
- media_owner_free(owner);
- if (state_in_use(transport->state))
- transport->suspend(transport, NULL);
- }
- static gboolean media_transport_set_fd(struct media_transport *transport,
- int fd, uint16_t imtu, uint16_t omtu)
- {
- if (transport->fd == fd)
- return TRUE;
- transport->fd = fd;
- transport->imtu = imtu;
- transport->omtu = omtu;
- info("%s: fd(%d) ready", transport->path, fd);
- return TRUE;
- }
- static void a2dp_resume_complete(struct avdtp *session, int err,
- void *user_data)
- {
- struct media_owner *owner = user_data;
- struct media_request *req = owner->pending;
- struct media_transport *transport = owner->transport;
- struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
- struct avdtp_stream *stream;
- int fd;
- uint16_t imtu, omtu;
- gboolean ret;
- req->id = 0;
- if (err)
- goto fail;
- stream = a2dp_sep_get_stream(sep);
- if (stream == NULL)
- goto fail;
- ret = avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, NULL);
- if (ret == FALSE)
- goto fail;
- media_transport_set_fd(transport, fd, imtu, omtu);
- ret = g_dbus_send_reply(btd_get_dbus_connection(), req->msg,
- DBUS_TYPE_UNIX_FD, &fd,
- DBUS_TYPE_UINT16, &imtu,
- DBUS_TYPE_UINT16, &omtu,
- DBUS_TYPE_INVALID);
- if (ret == FALSE)
- goto fail;
- media_owner_remove(owner);
- transport_set_state(transport, TRANSPORT_STATE_ACTIVE);
- return;
- fail:
- media_transport_remove_owner(transport);
- }
- static guint resume_a2dp(struct media_transport *transport,
- struct media_owner *owner)
- {
- struct a2dp_transport *a2dp = transport->data;
- struct media_endpoint *endpoint = transport->endpoint;
- struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
- guint id;
- if (a2dp->session == NULL) {
- a2dp->session = a2dp_avdtp_get(transport->device);
- if (a2dp->session == NULL)
- return 0;
- }
- if (state_in_use(transport->state))
- return a2dp_resume(a2dp->session, sep, a2dp_resume_complete,
- owner);
- if (a2dp_sep_lock(sep, a2dp->session) == FALSE)
- return 0;
- id = a2dp_resume(a2dp->session, sep, a2dp_resume_complete, owner);
- if (id == 0) {
- a2dp_sep_unlock(sep, a2dp->session);
- return 0;
- }
- if (transport->state == TRANSPORT_STATE_IDLE)
- transport_set_state(transport, TRANSPORT_STATE_REQUESTING);
- return id;
- }
- static void a2dp_suspend_complete(struct avdtp *session, int err,
- void *user_data)
- {
- struct media_owner *owner = user_data;
- struct media_transport *transport = owner->transport;
- struct a2dp_transport *a2dp = transport->data;
- struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
- /* Release always succeeds */
- if (owner->pending) {
- owner->pending->id = 0;
- media_request_reply(owner->pending, 0);
- media_owner_remove(owner);
- }
- a2dp_sep_unlock(sep, a2dp->session);
- transport_set_state(transport, TRANSPORT_STATE_IDLE);
- media_transport_remove_owner(transport);
- }
- static guint suspend_a2dp(struct media_transport *transport,
- struct media_owner *owner)
- {
- struct a2dp_transport *a2dp = transport->data;
- struct media_endpoint *endpoint = transport->endpoint;
- struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
- if (owner != NULL)
- return a2dp_suspend(a2dp->session, sep, a2dp_suspend_complete,
- owner);
- transport_set_state(transport, TRANSPORT_STATE_IDLE);
- a2dp_sep_unlock(sep, a2dp->session);
- return 0;
- }
- static void cancel_a2dp(struct media_transport *transport, guint id)
- {
- a2dp_cancel(id);
- }
- static void media_owner_exit(DBusConnection *connection, void *user_data)
- {
- struct media_owner *owner = user_data;
- owner->watch = 0;
- media_owner_remove(owner);
- media_transport_remove_owner(owner->transport);
- }
- static void media_transport_set_owner(struct media_transport *transport,
- struct media_owner *owner)
- {
- DBG("Transport %s Owner %s", transport->path, owner->name);
- transport->owner = owner;
- owner->transport = transport;
- owner->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
- owner->name,
- media_owner_exit,
- owner, NULL);
- }
- static struct media_owner *media_owner_create(DBusMessage *msg)
- {
- struct media_owner *owner;
- owner = g_new0(struct media_owner, 1);
- owner->name = g_strdup(dbus_message_get_sender(msg));
- DBG("Owner created: sender=%s", owner->name);
- return owner;
- }
- static void media_owner_add(struct media_owner *owner,
- struct media_request *req)
- {
- DBG("Owner %s Request %s", owner->name,
- dbus_message_get_member(req->msg));
- owner->pending = req;
- }
- static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_transport *transport = data;
- struct media_owner *owner;
- struct media_request *req;
- guint id;
- if (transport->owner != NULL)
- return btd_error_not_authorized(msg);
- if (transport->state >= TRANSPORT_STATE_REQUESTING)
- return btd_error_not_authorized(msg);
- owner = media_owner_create(msg);
- id = transport->resume(transport, owner);
- if (id == 0) {
- media_owner_free(owner);
- return btd_error_not_authorized(msg);
- }
- req = media_request_create(msg, id);
- media_owner_add(owner, req);
- media_transport_set_owner(transport, owner);
- return NULL;
- }
- static DBusMessage *try_acquire(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_transport *transport = data;
- struct media_owner *owner;
- struct media_request *req;
- guint id;
- if (transport->owner != NULL)
- return btd_error_not_authorized(msg);
- if (transport->state >= TRANSPORT_STATE_REQUESTING)
- return btd_error_not_authorized(msg);
- if (transport->state != TRANSPORT_STATE_PENDING)
- return btd_error_not_available(msg);
- owner = media_owner_create(msg);
- id = transport->resume(transport, owner);
- if (id == 0) {
- media_owner_free(owner);
- return btd_error_not_authorized(msg);
- }
- req = media_request_create(msg, id);
- media_owner_add(owner, req);
- media_transport_set_owner(transport, owner);
- return NULL;
- }
- static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_transport *transport = data;
- struct media_owner *owner = transport->owner;
- const char *sender;
- struct media_request *req;
- guint id;
- sender = dbus_message_get_sender(msg);
- if (owner == NULL || g_strcmp0(owner->name, sender) != 0)
- return btd_error_not_authorized(msg);
- if (owner->pending) {
- const char *member;
- member = dbus_message_get_member(owner->pending->msg);
- /* Cancel Acquire request if that exist */
- if (g_str_equal(member, "Acquire"))
- media_owner_remove(owner);
- else
- return btd_error_in_progress(msg);
- }
- transport_set_state(transport, TRANSPORT_STATE_SUSPENDING);
- id = transport->suspend(transport, owner);
- if (id == 0) {
- media_transport_remove_owner(transport);
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- req = media_request_create(msg, id);
- media_owner_add(owner, req);
- return NULL;
- }
- static gboolean get_device(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_transport *transport = data;
- const char *path = device_get_path(transport->device);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
- return TRUE;
- }
- static gboolean get_uuid(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_transport *transport = data;
- const char *uuid = media_endpoint_get_uuid(transport->endpoint);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
- return TRUE;
- }
- static gboolean get_codec(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_transport *transport = data;
- uint8_t codec = media_endpoint_get_codec(transport->endpoint);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &codec);
- return TRUE;
- }
- static gboolean get_configuration(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_transport *transport = data;
- DBusMessageIter array;
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_BYTE_AS_STRING, &array);
- dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
- &transport->configuration,
- transport->size);
- dbus_message_iter_close_container(iter, &array);
- return TRUE;
- }
- static gboolean get_state(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_transport *transport = data;
- const char *state = state2str(transport->state);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &state);
- return TRUE;
- }
- static gboolean delay_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_transport *transport = data;
- struct a2dp_transport *a2dp = transport->data;
- return a2dp->delay != 0;
- }
- static gboolean get_delay(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_transport *transport = data;
- struct a2dp_transport *a2dp = transport->data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &a2dp->delay);
- return TRUE;
- }
- static gboolean volume_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_transport *transport = data;
- struct a2dp_transport *a2dp = transport->data;
- return a2dp->volume >= 0;
- }
- static gboolean get_volume(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_transport *transport = data;
- struct a2dp_transport *a2dp = transport->data;
- uint16_t volume = (uint16_t)a2dp->volume;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &volume);
- return TRUE;
- }
- static void set_volume(const GDBusPropertyTable *property,
- DBusMessageIter *iter, GDBusPendingPropertySet id,
- void *data)
- {
- struct media_transport *transport = data;
- struct a2dp_transport *a2dp = transport->data;
- uint16_t arg;
- int8_t volume;
- bool notify;
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
- goto error;
- dbus_message_iter_get_basic(iter, &arg);
- if (arg > INT8_MAX)
- goto error;
- g_dbus_pending_property_success(id);
- volume = (int8_t)arg;
- if (a2dp->volume == volume)
- return;
- notify = transport->source_watch ? true : false;
- if (notify) {
- a2dp->volume = volume;
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- transport->path,
- MEDIA_TRANSPORT_INTERFACE,
- "Volume");
- }
- avrcp_set_volume(transport->device, volume, notify);
- return;
- error:
- g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- }
- static gboolean endpoint_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_transport *transport = data;
- return transport->remote_endpoint != NULL;
- }
- static gboolean get_endpoint(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_transport *transport = data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
- &transport->remote_endpoint);
- return TRUE;
- }
- static const GDBusMethodTable transport_methods[] = {
- { GDBUS_ASYNC_METHOD("Acquire",
- NULL,
- GDBUS_ARGS({ "fd", "h" }, { "mtu_r", "q" },
- { "mtu_w", "q" }),
- acquire) },
- { GDBUS_ASYNC_METHOD("TryAcquire",
- NULL,
- GDBUS_ARGS({ "fd", "h" }, { "mtu_r", "q" },
- { "mtu_w", "q" }),
- try_acquire) },
- { GDBUS_ASYNC_METHOD("Release", NULL, NULL, release) },
- { },
- };
- static const GDBusPropertyTable transport_properties[] = {
- { "Device", "o", get_device },
- { "UUID", "s", get_uuid },
- { "Codec", "y", get_codec },
- { "Configuration", "ay", get_configuration },
- { "State", "s", get_state },
- { "Delay", "q", get_delay, NULL, delay_exists },
- { "Volume", "q", get_volume, set_volume, volume_exists },
- { "Endpoint", "o", get_endpoint, NULL, endpoint_exists,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { }
- };
- static void destroy_a2dp(void *data)
- {
- struct a2dp_transport *a2dp = data;
- if (a2dp->session)
- avdtp_unref(a2dp->session);
- g_free(a2dp);
- }
- static void media_transport_free(void *data)
- {
- struct media_transport *transport = data;
- transports = g_slist_remove(transports, transport);
- if (transport->owner)
- media_transport_remove_owner(transport);
- if (transport->destroy != NULL)
- transport->destroy(transport->data);
- g_free(transport->configuration);
- g_free(transport->path);
- g_free(transport);
- }
- static void transport_update_playing(struct media_transport *transport,
- gboolean playing)
- {
- DBG("%s State=%s Playing=%d", transport->path,
- str_state[transport->state], playing);
- if (playing == FALSE) {
- if (transport->state == TRANSPORT_STATE_PENDING)
- transport_set_state(transport, TRANSPORT_STATE_IDLE);
- else if (transport->state == TRANSPORT_STATE_ACTIVE) {
- /* Remove owner */
- if (transport->owner != NULL)
- media_transport_remove_owner(transport);
- }
- } else if (transport->state == TRANSPORT_STATE_IDLE)
- transport_set_state(transport, TRANSPORT_STATE_PENDING);
- }
- static void sink_state_changed(struct btd_service *service,
- sink_state_t old_state,
- sink_state_t new_state,
- void *user_data)
- {
- struct media_transport *transport = user_data;
- if (new_state == SINK_STATE_PLAYING)
- transport_update_playing(transport, TRUE);
- else
- transport_update_playing(transport, FALSE);
- }
- static void source_state_changed(struct btd_service *service,
- source_state_t old_state,
- source_state_t new_state,
- void *user_data)
- {
- struct media_transport *transport = user_data;
- if (new_state == SOURCE_STATE_PLAYING)
- transport_update_playing(transport, TRUE);
- else
- transport_update_playing(transport, FALSE);
- }
- static int media_transport_init_source(struct media_transport *transport)
- {
- struct btd_service *service;
- struct a2dp_transport *a2dp;
- service = btd_device_get_service(transport->device, A2DP_SINK_UUID);
- if (service == NULL)
- return -EINVAL;
- a2dp = g_new0(struct a2dp_transport, 1);
- transport->resume = resume_a2dp;
- transport->suspend = suspend_a2dp;
- transport->cancel = cancel_a2dp;
- transport->data = a2dp;
- transport->destroy = destroy_a2dp;
- a2dp->volume = -1;
- transport->sink_watch = sink_add_state_cb(service, sink_state_changed,
- transport);
- return 0;
- }
- static int media_transport_init_sink(struct media_transport *transport)
- {
- struct btd_service *service;
- struct a2dp_transport *a2dp;
- service = btd_device_get_service(transport->device, A2DP_SOURCE_UUID);
- if (service == NULL)
- return -EINVAL;
- a2dp = g_new0(struct a2dp_transport, 1);
- transport->resume = resume_a2dp;
- transport->suspend = suspend_a2dp;
- transport->cancel = cancel_a2dp;
- transport->data = a2dp;
- transport->destroy = destroy_a2dp;
- a2dp->volume = 127;
- transport->source_watch = source_add_state_cb(service,
- source_state_changed,
- transport);
- return 0;
- }
- struct media_transport *media_transport_create(struct btd_device *device,
- const char *remote_endpoint,
- uint8_t *configuration,
- size_t size, void *data)
- {
- struct media_endpoint *endpoint = data;
- struct media_transport *transport;
- const char *uuid;
- static int fd = 0;
- transport = g_new0(struct media_transport, 1);
- transport->device = device;
- transport->endpoint = endpoint;
- transport->configuration = g_new(uint8_t, size);
- memcpy(transport->configuration, configuration, size);
- transport->size = size;
- transport->remote_endpoint = remote_endpoint;
- transport->path = g_strdup_printf("%s/fd%d",
- remote_endpoint ? remote_endpoint :
- device_get_path(device), fd++);
- transport->fd = -1;
- uuid = media_endpoint_get_uuid(endpoint);
- if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) {
- if (media_transport_init_source(transport) < 0)
- goto fail;
- } else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
- if (media_transport_init_sink(transport) < 0)
- goto fail;
- } else
- goto fail;
- if (g_dbus_register_interface(btd_get_dbus_connection(),
- transport->path, MEDIA_TRANSPORT_INTERFACE,
- transport_methods, NULL, transport_properties,
- transport, media_transport_free) == FALSE) {
- error("Could not register transport %s", transport->path);
- goto fail;
- }
- transports = g_slist_append(transports, transport);
- return transport;
- fail:
- media_transport_free(transport);
- return NULL;
- }
- const char *media_transport_get_path(struct media_transport *transport)
- {
- return transport->path;
- }
- void media_transport_update_delay(struct media_transport *transport,
- uint16_t delay)
- {
- struct a2dp_transport *a2dp = transport->data;
- /* Check if delay really changed */
- if (a2dp->delay == delay)
- return;
- a2dp->delay = delay;
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- transport->path,
- MEDIA_TRANSPORT_INTERFACE, "Delay");
- }
- struct btd_device *media_transport_get_dev(struct media_transport *transport)
- {
- return transport->device;
- }
- int8_t media_transport_get_volume(struct media_transport *transport)
- {
- struct a2dp_transport *a2dp = transport->data;
- return a2dp->volume;
- }
- void media_transport_update_volume(struct media_transport *transport,
- int8_t volume)
- {
- struct a2dp_transport *a2dp = transport->data;
- if (volume < 0)
- return;
- /* Check if volume really changed */
- if (a2dp->volume == volume)
- return;
- a2dp->volume = volume;
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- transport->path,
- MEDIA_TRANSPORT_INTERFACE, "Volume");
- }
- int8_t media_transport_get_device_volume(struct btd_device *dev)
- {
- GSList *l;
- if (dev == NULL)
- return -1;
- for (l = transports; l; l = l->next) {
- struct media_transport *transport = l->data;
- if (transport->device != dev)
- continue;
- /* Volume is A2DP only */
- if (media_endpoint_get_sep(transport->endpoint))
- return media_transport_get_volume(transport);
- }
- return 0;
- }
- void media_transport_update_device_volume(struct btd_device *dev,
- int8_t volume)
- {
- GSList *l;
- if (dev == NULL || volume < 0)
- return;
- for (l = transports; l; l = l->next) {
- struct media_transport *transport = l->data;
- if (transport->device != dev)
- continue;
- /* Volume is A2DP only */
- if (media_endpoint_get_sep(transport->endpoint))
- media_transport_update_volume(transport, volume);
- }
- }
|