| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2006-2010 Nokia Corporation
- * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
- * Copyright (C) 2011 Texas Instruments, Inc.
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include <stdlib.h>
- #include <stdint.h>
- #include <errno.h>
- #include <unistd.h>
- #include <assert.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <glib.h>
- #include <dbus/dbus.h>
- #include "lib/bluetooth.h"
- #include "lib/sdp.h"
- #include "lib/sdp_lib.h"
- #include "lib/uuid.h"
- #include "gdbus/gdbus.h"
- #include "src/adapter.h"
- #include "src/device.h"
- #include "src/profile.h"
- #include "src/service.h"
- #include "src/log.h"
- #include "src/error.h"
- #include "src/sdpd.h"
- #include "src/uuid-helper.h"
- #include "src/dbus-common.h"
- #include "avctp.h"
- #include "control.h"
- #include "player.h"
- static GSList *devices = NULL;
- struct control {
- struct btd_device *dev;
- struct avctp *session;
- struct btd_service *target;
- struct btd_service *remote;
- unsigned int avctp_id;
- const char *player;
- };
- static void state_changed(struct btd_device *dev, avctp_state_t old_state,
- avctp_state_t new_state, int err,
- void *user_data)
- {
- struct control *control = user_data;
- DBusConnection *conn = btd_get_dbus_connection();
- const char *path = device_get_path(dev);
- switch (new_state) {
- case AVCTP_STATE_DISCONNECTED:
- control->session = NULL;
- control->player = NULL;
- g_dbus_emit_property_changed(conn, path,
- AUDIO_CONTROL_INTERFACE, "Connected");
- g_dbus_emit_property_changed(conn, path,
- AUDIO_CONTROL_INTERFACE, "Player");
- break;
- case AVCTP_STATE_CONNECTING:
- if (control->session)
- break;
- control->session = avctp_get(dev);
- break;
- case AVCTP_STATE_CONNECTED:
- g_dbus_emit_property_changed(conn, path,
- AUDIO_CONTROL_INTERFACE, "Connected");
- break;
- case AVCTP_STATE_BROWSING_CONNECTING:
- case AVCTP_STATE_BROWSING_CONNECTED:
- default:
- return;
- }
- }
- int control_connect(struct btd_service *service)
- {
- struct control *control = btd_service_get_user_data(service);
- if (control->session)
- return -EALREADY;
- control->session = avctp_connect(control->dev);
- if (!control->session)
- return -EIO;
- return 0;
- }
- int control_disconnect(struct btd_service *service)
- {
- struct control *control = btd_service_get_user_data(service);
- if (!control || !control->session)
- return -ENOTCONN;
- avctp_disconnect(control->session);
- return 0;
- }
- static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
- uint8_t op, bool hold, void *data)
- {
- struct control *control = data;
- int err;
- if (!control->session)
- return btd_error_not_connected(msg);
- if (!control->target)
- return btd_error_not_supported(msg);
- err = avctp_send_passthrough(control->session, op, hold);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return dbus_message_new_method_return(msg);
- }
- static DBusMessage *control_volume_up(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- return key_pressed(conn, msg, AVC_VOLUME_UP, false, data);
- }
- static DBusMessage *control_volume_down(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- return key_pressed(conn, msg, AVC_VOLUME_DOWN, false, data);
- }
- static DBusMessage *control_play(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- return key_pressed(conn, msg, AVC_PLAY, false, data);
- }
- static DBusMessage *control_pause(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- return key_pressed(conn, msg, AVC_PAUSE, false, data);
- }
- static DBusMessage *control_stop(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- return key_pressed(conn, msg, AVC_STOP, false, data);
- }
- static DBusMessage *control_next(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- return key_pressed(conn, msg, AVC_FORWARD, false, data);
- }
- static DBusMessage *control_previous(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- return key_pressed(conn, msg, AVC_BACKWARD, false, data);
- }
- static DBusMessage *control_fast_forward(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- return key_pressed(conn, msg, AVC_FAST_FORWARD, true, data);
- }
- static DBusMessage *control_rewind(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- return key_pressed(conn, msg, AVC_REWIND, true, data);
- }
- static gboolean control_property_get_connected(
- const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct control *control = data;
- dbus_bool_t value = (control->session != NULL);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
- return TRUE;
- }
- static gboolean control_player_exists(const GDBusPropertyTable *property,
- void *data)
- {
- struct control *control = data;
- return control->player != NULL;
- }
- static gboolean control_get_player(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct control *control = data;
- if (!control->player)
- return FALSE;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
- &control->player);
- return TRUE;
- }
- static const GDBusMethodTable control_methods[] = {
- { GDBUS_DEPRECATED_METHOD("Play", NULL, NULL, control_play) },
- { GDBUS_DEPRECATED_METHOD("Pause", NULL, NULL, control_pause) },
- { GDBUS_DEPRECATED_METHOD("Stop", NULL, NULL, control_stop) },
- { GDBUS_DEPRECATED_METHOD("Next", NULL, NULL, control_next) },
- { GDBUS_DEPRECATED_METHOD("Previous", NULL, NULL, control_previous) },
- { GDBUS_DEPRECATED_METHOD("VolumeUp", NULL, NULL, control_volume_up) },
- { GDBUS_DEPRECATED_METHOD("VolumeDown", NULL, NULL,
- control_volume_down) },
- { GDBUS_DEPRECATED_METHOD("FastForward", NULL, NULL,
- control_fast_forward) },
- { GDBUS_DEPRECATED_METHOD("Rewind", NULL, NULL, control_rewind) },
- { }
- };
- static const GDBusPropertyTable control_properties[] = {
- { "Connected", "b", control_property_get_connected },
- { "Player", "o", control_get_player, NULL, control_player_exists },
- { }
- };
- static void path_unregister(void *data)
- {
- struct control *control = data;
- DBG("Unregistered interface %s on path %s", AUDIO_CONTROL_INTERFACE,
- device_get_path(control->dev));
- if (control->session)
- avctp_disconnect(control->session);
- avctp_remove_state_cb(control->avctp_id);
- if (control->target) {
- btd_service_set_user_data(control->target, NULL);
- btd_service_unref(control->target);
- }
- if (control->remote) {
- btd_service_set_user_data(control->remote, NULL);
- btd_service_unref(control->remote);
- }
- devices = g_slist_remove(devices, control);
- g_free(control);
- }
- void control_unregister(struct btd_service *service)
- {
- struct btd_device *dev = btd_service_get_device(service);
- g_dbus_unregister_interface(btd_get_dbus_connection(),
- device_get_path(dev),
- AUDIO_CONTROL_INTERFACE);
- }
- static struct control *find_control(struct btd_device *dev)
- {
- GSList *l;
- for (l = devices; l; l = l->next) {
- struct control *control = l->data;
- if (control->dev == dev)
- return control;
- }
- return NULL;
- }
- static struct control *control_init(struct btd_service *service)
- {
- struct control *control;
- struct btd_device *dev = btd_service_get_device(service);
- control = find_control(dev);
- if (control != NULL)
- return control;
- control = g_new0(struct control, 1);
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- device_get_path(dev),
- AUDIO_CONTROL_INTERFACE,
- control_methods, NULL,
- control_properties, control,
- path_unregister)) {
- g_free(control);
- return NULL;
- }
- DBG("Registered interface %s on path %s", AUDIO_CONTROL_INTERFACE,
- device_get_path(dev));
- control->dev = dev;
- control->avctp_id = avctp_add_state_cb(dev, state_changed, control);
- devices = g_slist_prepend(devices, control);
- return control;
- }
- int control_init_target(struct btd_service *service)
- {
- struct control *control;
- control = control_init(service);
- if (control == NULL)
- return -EINVAL;
- control->target = btd_service_ref(service);
- btd_service_set_user_data(service, control);
- return 0;
- }
- int control_init_remote(struct btd_service *service)
- {
- struct control *control;
- control = control_init(service);
- if (control == NULL)
- return -EINVAL;
- control->remote = btd_service_ref(service);
- btd_service_set_user_data(service, control);
- return 0;
- }
- int control_set_player(struct btd_service *service, const char *path)
- {
- struct control *control = btd_service_get_user_data(service);
- if (!control->session)
- return -ENOTCONN;
- if (g_strcmp0(control->player, path) == 0)
- return -EALREADY;
- control->player = path;
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- device_get_path(control->dev),
- AUDIO_CONTROL_INTERFACE, "Player");
- return 0;
- }
|