Cleanup audio manager.

This commit is contained in:
Luiz Augusto von Dentz 2008-07-28 17:15:27 -03:00
parent d2fd09864d
commit 12d81a98fe
10 changed files with 99 additions and 1212 deletions

View File

@ -137,7 +137,7 @@ static struct audio_device *a2dp_get_dev(struct avdtp *session)
avdtp_get_peers(session, NULL, &addr);
return manager_device_connected(&addr, A2DP_SOURCE_UUID);
return manager_find_device(&addr, NULL, FALSE);
}
static gboolean finalize_config(struct a2dp_setup *s)

View File

@ -583,17 +583,15 @@ static int uinput_create(char *name)
static void init_uinput(struct avctp *session)
{
char address[18], *name;
char address[18];
ba2str(&session->dst, address);
name = session->dev->name ? session->dev->name : address;
session->uinput = uinput_create(name);
session->uinput = uinput_create(address);
if (session->uinput < 0)
error("AVRCP: failed to init uinput for %s", name);
error("AVRCP: failed to init uinput for %s", address);
else
debug("AVRCP: uinput initialized for %s", name);
debug("AVRCP: uinput initialized for %s", address);
}
static void avctp_connect_session(struct avctp *session)
@ -601,8 +599,10 @@ static void avctp_connect_session(struct avctp *session)
GIOChannel *io;
session->state = AVCTP_STATE_CONNECTED;
session->dev = manager_device_connected(&session->dst,
AVRCP_TARGET_UUID);
session->dev = manager_find_device(&session->dst, NULL, FALSE);
if (!session->dev)
return;
session->dev->control->session = session;
init_uinput(session);
@ -923,6 +923,9 @@ struct control *control_init(struct audio_device *dev)
dev, NULL))
return NULL;
info("Registered interface %s on path %s",
AUDIO_CONTROL_INTERFACE, dev->path);
return g_new0(struct control, 1);
}

View File

@ -54,114 +54,6 @@
#include "headset.h"
#include "sink.h"
static DBusMessage *device_get_address(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct audio_device *device = data;
DBusMessage *reply;
char address[18], *ptr = address;
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
ba2str(&device->dst, address);
dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr,
DBUS_TYPE_INVALID);
return reply;
}
static char *get_dev_name(DBusConnection *conn, const bdaddr_t *src,
const bdaddr_t *bda)
{
char address[18], filename[PATH_MAX + 1];
ba2str(src, address);
/* check if it is in the cache */
create_name(filename, PATH_MAX, STORAGEDIR, address, "names");
ba2str(bda, address);
return textfile_caseget(filename, address);
}
static DBusMessage *device_get_name(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct audio_device *dev = data;
DBusMessage *reply;
const char *name = dev->name ? dev->name : "";
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
dbus_message_append_args(reply, DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID);
return reply;
}
static DBusMessage *device_get_adapter(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct audio_device *device = data;
DBusMessage *reply;
char address[18], *ptr = address;
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
ba2str(&device->src, address);
dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr,
DBUS_TYPE_INVALID);
return reply;
}
static DBusMessage *device_get_connected(DBusConnection *conn,
DBusMessage *msg, void *data)
{
DBusMessageIter iter, array_iter;
struct audio_device *device = data;
DBusMessage *reply;
const char *iface;
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING,
&array_iter);
if (device->headset &&
headset_get_state(device) >= HEADSET_STATE_CONNECTED) {
iface = AUDIO_HEADSET_INTERFACE;
dbus_message_iter_append_basic(&array_iter,
DBUS_TYPE_STRING, &iface);
}
dbus_message_iter_close_container(&iter, &array_iter);
return reply;
}
static GDBusMethodTable device_methods[] = {
{ "GetAddress", "", "s", device_get_address },
{ "GetName", "", "s", device_get_name },
{ "GetAdapter", "", "s", device_get_adapter },
{ "GetConnectedInterfaces", "", "as", device_get_connected },
{ }
};
static void device_free(struct audio_device *dev)
{
if (dev->headset)
@ -176,238 +68,29 @@ static void device_free(struct audio_device *dev)
if (dev->conn)
dbus_connection_unref(dev->conn);
g_free(dev->adapter_path);
g_free(dev->path);
g_free(dev->name);
g_free(dev);
}
static void device_unregister(void *data)
{
struct audio_device *device = data;
info("Unregistered device path:%s", device->path);
device_free(device);
}
struct audio_device *device_register(DBusConnection *conn,
const char *path, const bdaddr_t *bda)
const char *path, const bdaddr_t *src,
const bdaddr_t *dst)
{
struct audio_device *dev;
bdaddr_t src;
int dev_id;
if (!conn || !path)
return NULL;
bacpy(&src, BDADDR_ANY);
dev_id = hci_get_route(&src);
if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0))
return NULL;
dev = g_new0(struct audio_device, 1);
/* FIXME just to maintain compatibility */
dev->adapter_path = g_strdup_printf("/org/bluez/hci%d", dev_id);
if (!dev->adapter_path) {
device_free(dev);
return NULL;
}
if (!g_dbus_register_interface(conn, path,
AUDIO_DEVICE_INTERFACE,
device_methods, NULL, NULL,
dev, device_unregister)) {
error("Failed to register %s interface to %s",
AUDIO_DEVICE_INTERFACE, path);
device_free(dev);
return NULL;
}
dev->name = get_dev_name(conn, &src, bda);
dev->path = g_strdup(path);
bacpy(&dev->dst, bda);
bacpy(&dev->src, &src);
bacpy(&dev->store, &src);
bacpy(&dev->dst, dst);
bacpy(&dev->src, src);
dev->conn = dbus_connection_ref(conn);
return dev;
}
int device_store(struct audio_device *dev, gboolean is_default)
{
char value[64];
char filename[PATH_MAX + 1];
char src_addr[18], dst_addr[18];
int offset = 0;
if (!dev->path)
return -EINVAL;
ba2str(&dev->dst, dst_addr);
ba2str(&dev->store, src_addr);
create_name(filename, PATH_MAX, STORAGEDIR, src_addr, "audio");
create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (is_default)
textfile_put(filename, "default", dst_addr);
if (dev->headset) {
snprintf(value, 64, "headset ");
offset += strlen("headset ");
}
if (dev->gateway) {
snprintf(value + offset, 64 - offset, "gateway ");
offset += strlen("gateway ");
}
if (dev->sink) {
snprintf(value + offset, 64 - offset, "sink ");
offset += strlen("sink ");
}
if (dev->source) {
snprintf(value + offset, 64 - offset, "source ");
offset += strlen("source ");
}
if (dev->control) {
snprintf(value + offset, 64 - offset, "control ");
offset += strlen("control ");
}
if (dev->target)
snprintf(value + offset, 64 - offset, "target");
return textfile_put(filename, dst_addr, value);
}
int device_remove_stored(struct audio_device *dev)
{
char filename[PATH_MAX + 1];
char src_addr[18], dst_addr[18];
ba2str(&dev->dst, dst_addr);
ba2str(&dev->store, src_addr);
create_name(filename, PATH_MAX, STORAGEDIR, src_addr, "audio");
return textfile_del(filename, dst_addr);
}
void device_finish_sdp_transaction(struct audio_device *dev)
{
char address[18], *addr_ptr = address;
DBusMessage *msg;
ba2str(&dev->dst, address);
msg = dbus_message_new_method_call("org.bluez", dev->adapter_path,
"org.bluez.Adapter",
"FinishRemoteServiceTransaction");
if (!msg) {
error("Unable to allocate new method call");
return;
}
dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr,
DBUS_TYPE_INVALID);
dbus_connection_send(dev->conn, msg, NULL);
dbus_message_unref(msg);
}
#if 0
static avdtp_state_t ipc_to_avdtp_state(uint8_t ipc_state)
{
switch (ipc_state) {
case STATE_DISCONNECTED:
return AVDTP_STATE_IDLE;
case STATE_CONNECTING:
return AVDTP_STATE_CONFIGURED;
case STATE_CONNECTED:
return AVDTP_STATE_OPEN;
case STATE_STREAM_STARTING:
case STATE_STREAMING:
return AVDTP_STATE_STREAMING;
default:
error("Unknown ipc state");
return AVDTP_STATE_IDLE;
}
}
static headset_state_t ipc_to_hs_state(uint8_t ipc_state)
{
switch (ipc_state) {
case STATE_DISCONNECTED:
return HEADSET_STATE_DISCONNECTED;
case STATE_CONNECTING:
return HEADSET_STATE_CONNECT_IN_PROGRESS;
case STATE_CONNECTED:
return HEADSET_STATE_CONNECTED;
case STATE_STREAM_STARTING:
return HEADSET_STATE_PLAY_IN_PROGRESS;
case STATE_STREAMING:
return HEADSET_STATE_PLAYING;
default:
error("Unknown ipc state");
return HEADSET_STATE_DISCONNECTED;
}
}
static uint8_t avdtp_to_ipc_state(avdtp_state_t state)
{
switch (state) {
case AVDTP_STATE_IDLE:
return STATE_DISCONNECTED;
case AVDTP_STATE_CONFIGURED:
return STATE_CONNECTING;
case AVDTP_STATE_OPEN:
return STATE_CONNECTED;
case AVDTP_STATE_STREAMING:
return STATE_STREAMING;
default:
error("Unknown avdt state");
return AVDTP_STATE_IDLE;
}
}
static uint8_t hs_to_ipc_state(headset_state_t state)
{
switch (state) {
case HEADSET_STATE_DISCONNECTED:
return STATE_DISCONNECTED;
case HEADSET_STATE_CONNECT_IN_PROGRESS:
return STATE_CONNECTING;
case HEADSET_STATE_CONNECTED:
return STATE_CONNECTED;
case HEADSET_STATE_PLAY_IN_PROGRESS:
return STATE_STREAMING;
default:
error("Unknown headset state");
return AVDTP_STATE_IDLE;
}
}
uint8_t device_get_state(struct audio_device *dev)
{
avdtp_state_t sink_state;
headset_state_t hs_state;
if (dev->sink && sink_is_active(dev)) {
sink_state = sink_get_state(dev);
return avdtp_to_ipc_state(sink_state);
}
else if (dev->headset && headset_is_active(dev)) {
hs_state = headset_get_state(dev);
return hs_to_ipc_state(hs_state);
}
else if (dev->control && control_is_active(dev))
return STATE_CONNECTED;
return STATE_DISCONNECTED;
}
#endif
gboolean device_is_connected(struct audio_device *dev, const char *interface)
{
if (!interface) {
@ -433,3 +116,10 @@ gboolean device_is_connected(struct audio_device *dev, const char *interface)
return FALSE;
}
void device_unregister(struct audio_device *device)
{
g_dbus_unregister_all_interfaces(device->conn, device->path);
device_free(device);
}

View File

@ -54,10 +54,7 @@ struct gateway;
struct audio_device {
DBusConnection *conn;
char *adapter_path;
char *path;
char *name;
bdaddr_t store;
bdaddr_t src;
bdaddr_t dst;
@ -70,14 +67,9 @@ struct audio_device {
};
struct audio_device *device_register(DBusConnection *conn,
const char *path, const bdaddr_t *bda);
const char *path, const bdaddr_t *src,
const bdaddr_t *dst);
int device_store(struct audio_device *device, gboolean is_default);
int device_remove_stored(struct audio_device *dev);
void device_finish_sdp_transaction(struct audio_device *device);
uint8_t device_get_state(struct audio_device *dev);
void device_unregister(struct audio_device *device);
gboolean device_is_connected(struct audio_device *dev, const char *interface);

View File

@ -1498,6 +1498,9 @@ register_iface:
return NULL;
}
info("Registered interface %s on path %s",
AUDIO_HEADSET_INTERFACE, dev->path);
return hs;
}

View File

@ -35,86 +35,11 @@
#include <dbus/dbus.h>
#include "plugin.h"
#include "../hcid/device.h"
#include "logging.h"
#include "unix.h"
#include "device.h"
#include "manager.h"
static DBusConnection *conn;
static int headset_probe(struct btd_device_driver *driver,
struct btd_device *device, GSList *records)
{
const gchar *path = device_get_path(device);
DBG("path %s", path);
return 0;
}
static void headset_remove(struct btd_device_driver *driver,
struct btd_device *device)
{
const gchar *path = device_get_path(device);
DBG("path %s", path);
}
static struct btd_device_driver headset_driver = {
.name = "headset",
.uuids = BTD_UUIDS(HSP_HS_UUID, HFP_HS_UUID),
.probe = headset_probe,
.remove = headset_remove,
};
static int a2dp_probe(struct btd_device_driver *driver,
struct btd_device *device, GSList *records)
{
const gchar *path = device_get_path(device);
DBG("path %s", path);
return 0;
}
static void a2dp_remove(struct btd_device_driver *driver,
struct btd_device *device)
{
const gchar *path = device_get_path(device);
DBG("path %s", path);
}
static struct btd_device_driver a2dp_driver = {
.name = "sink",
.uuids = BTD_UUIDS(A2DP_SINK_UUID),
.probe = a2dp_probe,
.remove = a2dp_remove,
};
static int audio_probe(struct btd_device_driver *driver,
struct btd_device *device, GSList *records)
{
const gchar *path = device_get_path(device);
DBG("path %s", path);
return 0;
}
static void audio_remove(struct btd_device_driver *driver,
struct btd_device *device)
{
const gchar *path = device_get_path(device);
DBG("path %s", path);
}
static struct btd_device_driver audio_driver = {
.name = "audio",
.uuids = BTD_UUIDS(HSP_HS_UUID, HFP_HS_UUID, HSP_AG_UUID, HFP_AG_UUID,
ADVANCED_AUDIO_UUID, A2DP_SOURCE_UUID, A2DP_SINK_UUID,
AVRCP_TARGET_UUID, AVRCP_REMOTE_UUID),
.probe = audio_probe,
.remove = audio_remove,
};
static GKeyFile *load_config_file(const char *file)
{
GError *err = NULL;
@ -134,6 +59,7 @@ static GKeyFile *load_config_file(const char *file)
static int audio_init(void)
{
DBusConnection *conn;
GKeyFile *config;
conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
@ -155,28 +81,14 @@ static int audio_init(void)
if (config)
g_key_file_free(config);
btd_register_device_driver(&headset_driver);
btd_register_device_driver(&a2dp_driver);
btd_register_device_driver(&audio_driver);
return 0;
}
static void audio_exit(void)
{
btd_unregister_device_driver(&audio_driver);
btd_unregister_device_driver(&a2dp_driver);
btd_unregister_device_driver(&headset_driver);
audio_manager_exit();
unix_exit();
dbus_connection_unref(conn);
}
BLUETOOTH_PLUGIN_DEFINE("audio", audio_init, audio_exit)

View File

@ -49,6 +49,8 @@
#include <gdbus.h>
#include "glib-helper.h"
#include "../hcid/adapter.h"
#include "../hcid/device.h"
#include "dbus-service.h"
#include "logging.h"
@ -97,9 +99,6 @@ struct audio_sdp_data {
static DBusConnection *connection = NULL;
static struct audio_device *default_hs = NULL;
static struct audio_device *default_dev = NULL;
static GSList *devices = NULL;
static uint32_t hsp_ag_record_id = 0;
@ -120,74 +119,6 @@ static struct enabled_interfaces enabled = {
.control = TRUE,
};
static DBusMessage *get_records(uuid_t *uuid, struct audio_sdp_data *data);
static struct audio_device *create_device(const bdaddr_t *bda)
{
static int device_id = 0;
char path[128];
snprintf(path, sizeof(path) - 1,
"%s/device%d", AUDIO_MANAGER_PATH, device_id++);
return device_register(connection, path, bda);
}
static void destroy_device(struct audio_device *device)
{
g_dbus_unregister_all_interfaces(connection, device->path);
}
static void remove_device(struct audio_device *device)
{
if (device == default_dev) {
debug("Removing default device");
default_dev = NULL;
}
if (device == default_hs) {
debug("Removing default headset");
default_hs = NULL;
}
devices = g_slist_remove(devices, device);
destroy_device(device);
}
static gboolean add_device(struct audio_device *device, gboolean send_signals)
{
if (!send_signals)
goto add;
g_dbus_emit_signal(connection, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"DeviceCreated",
DBUS_TYPE_STRING, &device->path,
DBUS_TYPE_INVALID);
if (device->headset)
g_dbus_emit_signal(connection,
AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"HeadsetCreated",
DBUS_TYPE_STRING, &device->path,
DBUS_TYPE_INVALID);
add:
if (default_dev == NULL && g_slist_length(devices) == 0) {
debug("Selecting default device");
default_dev = device;
}
if (!default_hs && device->headset && !devices)
default_hs = device;
devices = g_slist_append(devices, device);
return TRUE;
}
static uint16_t get_service_uuid(const sdp_record_t *record)
{
sdp_list_t *classes;
@ -254,7 +185,6 @@ gboolean server_is_enabled(uint16_t svc)
static void handle_record(sdp_record_t *record, struct audio_device *device)
{
gboolean is_default;
uint16_t uuid16;
uuid16 = get_service_uuid(record);
@ -311,666 +241,6 @@ static void handle_record(sdp_record_t *record, struct audio_device *device)
debug("Unrecognized UUID: 0x%04X", uuid16);
break;
}
is_default = (default_dev == device) ? TRUE : FALSE;
device_store(device, is_default);
}
static void finish_sdp(struct audio_sdp_data *data, gboolean success)
{
const char *addr;
DBusMessage *reply = NULL;
DBusError derr;
debug("Audio service discovery completed with %s",
success ? "success" : "failure");
if (!success)
goto done;
if (!data->msg)
goto update;
dbus_error_init(&derr);
dbus_message_get_args(data->msg, &derr,
DBUS_TYPE_STRING, &addr,
DBUS_TYPE_INVALID);
if (dbus_error_is_set(&derr)) {
error("Unable to get message args");
success = FALSE;
error_failed(connection, data->msg, derr.message);
dbus_error_free(&derr);
goto done;
}
/* Return error if no audio related service records were found */
if (!data->records) {
debug("No audio audio related service records were found");
success = FALSE;
error_not_supported(connection, data->msg);
goto done;
}
reply = dbus_message_new_method_return(data->msg);
if (!reply) {
success = FALSE;
error_failed(connection, data->msg, "Out of memory");
goto done;
}
update:
g_slist_foreach(data->records, (GFunc) handle_record, data->device);
if (!g_slist_find(devices, data->device))
add_device(data->device, TRUE);
if (reply) {
dbus_message_append_args(reply, DBUS_TYPE_STRING,
&data->device->path,
DBUS_TYPE_INVALID);
dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
}
done:
if (success) {
if (data->cb)
data->cb(data->device, data->cb_data);
} else {
if (data->cb)
data->cb(NULL, data->cb_data);
if (!g_slist_find(devices, data->device))
destroy_device(data->device);
}
if (data->msg)
dbus_message_unref(data->msg);
g_slist_foreach(data->records, (GFunc) sdp_record_free, NULL);
g_slist_free(data->records);
g_free(data);
}
static void get_records_cb(sdp_list_t *recs, int err, gpointer user_data)
{
struct audio_sdp_data *data = user_data;
sdp_list_t *seq;
uuid_t uuid;
if (err < 0) {
error_connection_attempt_failed(connection, data->msg, -err);
finish_sdp(data, FALSE);
return;
}
for (seq = recs; seq; seq = seq->next) {
sdp_record_t *rec = (sdp_record_t *) seq->data;
if (!rec)
break;
data->records = g_slist_append(data->records, rec);
}
sdp_list_free(recs, NULL);
data->state++;
switch (data->state) {
case ADVANCED_AUDIO:
sdp_uuid16_create(&uuid, ADVANCED_AUDIO_SVCLASS_ID);
break;
case AV_REMOTE:
sdp_uuid16_create(&uuid, AV_REMOTE_SVCLASS_ID);
break;
default:
finish_sdp(data, TRUE);
return;
}
get_records(&uuid, data);
}
static DBusMessage *get_records(uuid_t *uuid, struct audio_sdp_data *data)
{
struct audio_device *device = data->device;
DBusMessage *reply = NULL;
int err;
err = bt_search_service(&device->src, &device->dst, uuid,
get_records_cb, data, NULL);
if (!err)
return NULL;
if (data->msg)
reply = g_dbus_create_error(data->msg,
ERROR_INTERFACE ".ConnectionAttemptFailed",
strerror(-err));
finish_sdp(data, FALSE);
return reply;
}
static DBusMessage *resolve_services(DBusMessage *msg,
struct audio_device *device,
create_dev_cb_t cb,
void *user_data)
{
struct audio_sdp_data *sdp_data;
uuid_t uuid;
sdp_data = g_new0(struct audio_sdp_data, 1);
if (msg)
sdp_data->msg = dbus_message_ref(msg);
sdp_data->device = device;
sdp_data->cb = cb;
sdp_data->cb_data = user_data;
sdp_uuid16_create(&uuid, GENERIC_AUDIO_SVCLASS_ID);
return get_records(&uuid, sdp_data);
}
struct audio_device *manager_device_connected(const bdaddr_t *bda, const char *uuid)
{
struct audio_device *device;
const char *path;
gboolean headset = FALSE, created = FALSE;
device = manager_find_device(bda, NULL, FALSE);
if (!device) {
device = create_device(bda);
if (!device)
return NULL;
if (!add_device(device, TRUE)) {
destroy_device(device);
return NULL;
}
created = TRUE;
}
if (!strcmp(uuid, HSP_AG_UUID) || !strcmp(uuid, HFP_AG_UUID)) {
if (device->headset)
return device;
device->headset = headset_init(device, NULL, 0);
if (!device->headset)
return NULL;
headset = TRUE;
} else if (!strcmp(uuid, A2DP_SOURCE_UUID)) {
if (device->sink)
return device;
device->sink = sink_init(device);
if (!device->sink)
return NULL;
} else if (!strcmp(uuid, AVRCP_TARGET_UUID)) {
if (device->control)
return device;
device->control = control_init(device);
if (!device->control)
return NULL;
} else
return NULL;
path = device->path;
if (created) {
g_dbus_emit_signal(connection, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"DeviceCreated",
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID);
resolve_services(NULL, device, NULL, NULL);
}
if (headset)
g_dbus_emit_signal(connection, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"HeadsetCreated",
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID);
if (headset && !default_hs) {
default_hs = device;
g_dbus_emit_signal(connection, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"DefaultHeadsetChanged",
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID);
}
if (!default_dev) {
default_dev = device;
g_dbus_emit_signal(connection, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"DefaultDeviceChanged",
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID);
}
return device;
}
gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb,
void *user_data)
{
struct audio_device *dev;
dev = create_device(bda);
if (!dev)
return FALSE;
resolve_services(NULL, dev, cb, user_data);
return TRUE;
}
static DBusMessage *am_create_device(DBusConnection *conn,
DBusMessage *msg,
void *data)
{
const char *address, *path;
bdaddr_t bda;
struct audio_device *device;
DBusMessage *reply;
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_STRING, &address,
DBUS_TYPE_INVALID))
return NULL;
str2ba(address, &bda);
device = manager_find_device(&bda, NULL, FALSE);
if (!device) {
device = create_device(&bda);
return resolve_services(msg, device, NULL, NULL);
}
path = device->path;
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
dbus_message_append_args(reply, DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID);
return reply;
}
static DBusMessage *am_list_devices(DBusConnection *conn,
DBusMessage *msg,
void *data)
{
DBusMessageIter iter, array_iter;
DBusMessage *reply;
DBusError derr;
GSList *l;
gboolean hs_only = FALSE;
dbus_error_init(&derr);
if (dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE,
"ListHeadsets"))
hs_only = TRUE;
else
hs_only = FALSE;
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING, &array_iter);
for (l = devices; l != NULL; l = l->next) {
struct audio_device *device = l->data;
if (hs_only && !device->headset)
continue;
dbus_message_iter_append_basic(&array_iter,
DBUS_TYPE_STRING, &device->path);
}
dbus_message_iter_close_container(&iter, &array_iter);
return reply;
}
static gint device_path_cmp(gconstpointer a, gconstpointer b)
{
const struct audio_device *device = a;
const char *path = b;
return strcmp(device->path, path);
}
static DBusMessage *am_remove_device(DBusConnection *conn,
DBusMessage *msg,
void *data)
{
DBusMessage *reply;
GSList *match;
const char *path;
struct audio_device *device;
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID))
return NULL;
match = g_slist_find_custom(devices, path, device_path_cmp);
if (!match)
return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists",
"Device does not exists");
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
device = match->data;
device_remove_stored(device);
remove_device(device);
/* Fallback to a valid default */
if (default_dev == NULL) {
const char *param;
GSList *l;
default_dev = manager_find_device(BDADDR_ANY, NULL, TRUE);
if (!default_dev && devices) {
l = devices;
default_dev = (g_slist_last(l))->data;
}
param = default_dev ? default_dev->path : "";
g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"DefaultHeadsetChanged",
DBUS_TYPE_STRING, &param,
DBUS_TYPE_INVALID);
g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"DefaultDeviceChanged",
DBUS_TYPE_STRING, &param,
DBUS_TYPE_INVALID);
if (default_dev)
device_store(default_dev, TRUE);
}
g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"HeadsetRemoved",
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID);
g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"DeviceRemoved",
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID);
return reply;
}
static DBusMessage *am_find_by_addr(DBusConnection *conn,
DBusMessage *msg,
void *data)
{
const char *address;
DBusMessage *reply;
struct audio_device *device;
bdaddr_t bda;
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_STRING, &address,
DBUS_TYPE_INVALID))
return NULL;
str2ba(address, &bda);
device = manager_find_device(&bda, NULL, FALSE);
if (!device)
return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists",
"Device does not exists");
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
dbus_message_append_args(reply, DBUS_TYPE_STRING, &device->path,
DBUS_TYPE_INVALID);
return reply;
}
static DBusMessage *am_default_device(DBusConnection *conn,
DBusMessage *msg,
void *data)
{
DBusMessage *reply;
if (!default_dev)
return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists",
"Device does not exists");
if (default_dev->headset == NULL &&
dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE,
"DefaultHeadset"))
return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists",
"Device does not exists");
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
dbus_message_append_args(reply, DBUS_TYPE_STRING, &default_dev->path,
DBUS_TYPE_INVALID);
return reply;
}
static DBusMessage *am_change_default_device(DBusConnection *conn,
DBusMessage *msg,
void *data)
{
DBusMessage *reply;
GSList *match;
const char *path;
struct audio_device *device;
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID))
return NULL;
match = g_slist_find_custom(devices, path, device_path_cmp);
if (!match)
return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists",
"Device does not exists");
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
device = match->data;
if (!dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE,
"ChangeDefaultHeadset"))
g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"DefaultDeviceChanged",
DBUS_TYPE_STRING, &device->path,
DBUS_TYPE_INVALID);
else if (device->headset)
g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
"DefaultHeadsetChanged",
DBUS_TYPE_STRING, &device->path,
DBUS_TYPE_INVALID);
else
return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists",
"Device does not exists");
default_dev = device;
device_store(device, TRUE);
return reply;
}
static GDBusMethodTable manager_methods[] = {
{ "CreateDevice", "s", "s", am_create_device,
G_DBUS_METHOD_FLAG_ASYNC },
{ "RemoveDevice", "s", "", am_remove_device },
{ "ListDevices", "", "as", am_list_devices },
{ "DefaultDevice", "", "s", am_default_device },
{ "ChangeDefaultDevice", "s", "", am_change_default_device },
{ "CreateHeadset", "s", "s", am_create_device,
G_DBUS_METHOD_FLAG_ASYNC },
{ "RemoveHeadset", "s", "", am_remove_device },
{ "ListHeadsets", "", "as", am_list_devices },
{ "FindDeviceByAddress", "s", "s", am_find_by_addr },
{ "DefaultHeadset", "", "s", am_default_device },
{ "ChangeDefaultHeadset", "s", "", am_change_default_device },
{ }
};
static GDBusSignalTable manager_signals[] = {
{ "DeviceCreated", "s" },
{ "DeviceRemoved", "s" },
{ "HeadsetCreated", "s" },
{ "HeadsetRemoved", "s" },
{ "DefaultDeviceChanged", "s" },
{ "DefaultHeadsetChanged", "s" },
{ }
};
static void parse_stored_devices(char *key, char *value, void *data)
{
bdaddr_t *src = data;
struct audio_device *device;
bdaddr_t dst;
if (!key || !value || strcmp(key, "default") == 0)
return;
str2ba(key, &dst);
device = manager_find_device(&dst, NULL, FALSE);
if (device)
return;
info("Loading device %s (%s)", key, value);
device = create_device(&dst);
if (!device)
return;
/* Change storage to source adapter */
bacpy(&device->store, src);
if (enabled.headset && strstr(value, "headset"))
device->headset = headset_init(device, NULL, 0);
if (enabled.sink && strstr(value, "sink"))
device->sink = sink_init(device);
if (enabled.control && strstr(value, "control"))
device->control = control_init(device);
add_device(device, FALSE);
}
static void register_devices_stored(const char *adapter)
{
char filename[PATH_MAX + 1];
struct stat st;
struct audio_device *device;
bdaddr_t default_src;
bdaddr_t dst;
bdaddr_t src;
char *addr;
int dev_id;
create_name(filename, PATH_MAX, STORAGEDIR, adapter, "audio");
str2ba(adapter, &src);
if (stat(filename, &st) < 0)
return;
if (!(st.st_mode & __S_IFREG))
return;
textfile_foreach(filename, parse_stored_devices, &src);
bacpy(&default_src, BDADDR_ANY);
dev_id = hci_get_route(&default_src);
if (dev_id < 0 || hci_devba(dev_id, &default_src) < 0)
return;
if (bacmp(&default_src, &src) != 0)
return;
addr = textfile_get(filename, "default");
if (!addr)
return;
str2ba(addr, &dst);
device = manager_find_device(&dst, NULL, FALSE);
if (device) {
info("Setting %s as default device", addr);
default_dev = device;
}
free(addr);
}
static void register_stored(void)
{
char dirname[PATH_MAX + 1];
struct dirent *de;
DIR *dir;
snprintf(dirname, PATH_MAX, "%s", STORAGEDIR);
dir = opendir(dirname);
if (!dir)
return;
while ((de = readdir(dir)) != NULL) {
if (!isdigit(de->d_name[0]))
continue;
/* Device objects */
register_devices_stored(de->d_name);
}
closedir(dir);
}
static void manager_unregister(void *data)
{
info("Unregistered manager path");
if (devices) {
g_slist_foreach(devices, (GFunc) remove_device, NULL);
g_slist_free(devices);
devices = NULL;
}
}
static sdp_record_t *hsp_ag_record(uint8_t ch)
@ -1201,7 +471,7 @@ static void ag_io_cb(GIOChannel *chan, int err, const bdaddr_t *src,
uuid = HFP_AG_UUID;
}
device = manager_device_connected(dst, uuid);
device = manager_find_device(dst, NULL, FALSE);
if (!device)
goto drop;
@ -1416,6 +686,61 @@ static void server_exit(void)
}
}
static int audio_probe(struct btd_device_driver *driver,
struct btd_device *device, GSList *records)
{
struct adapter *adapter = device_get_adapter(device);
const gchar *path = device_get_path(device);
const char *source, *destination;
bdaddr_t src, dst;
struct audio_device *dev;
source = adapter_get_address(adapter);
destination = device_get_address(device);
str2ba(source, &src);
str2ba(destination, &dst);
dev = manager_find_device(&dst, NULL, FALSE);
if (!dev) {
dev = device_register(connection, path, &src, &dst);
if (!dev)
return -EINVAL;
devices = g_slist_append(devices, dev);
}
g_slist_foreach(records, (GFunc) handle_record, dev);
return 0;
}
static void audio_remove(struct btd_device_driver *driver,
struct btd_device *device)
{
struct audio_device *dev;
const char *destination = device_get_address(device);
bdaddr_t dst;
str2ba(destination, &dst);
dev = manager_find_device(&dst, NULL, FALSE);
if (!dev)
return;
devices = g_slist_remove(devices, dev);
device_unregister(dev);
}
static struct btd_device_driver audio_driver = {
.name = "audio",
.uuids = BTD_UUIDS(HSP_HS_UUID, HFP_HS_UUID, HSP_AG_UUID, HFP_AG_UUID,
ADVANCED_AUDIO_UUID, A2DP_SOURCE_UUID, A2DP_SINK_UUID,
AVRCP_TARGET_UUID, AVRCP_REMOTE_UUID),
.probe = audio_probe,
.remove = audio_remove,
};
int audio_manager_init(DBusConnection *conn, GKeyFile *config)
{
char **list;
@ -1477,18 +802,7 @@ proceed:
if (enabled.control && avrcp_init(conn, config) < 0)
goto failed;
if (!g_dbus_register_interface(conn, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE,
manager_methods, manager_signals,
NULL, NULL, manager_unregister)) {
error("Failed to register %s interface to %s",
AUDIO_MANAGER_INTERFACE, AUDIO_MANAGER_PATH);
goto failed;
}
info("Registered manager path:%s", AUDIO_MANAGER_PATH);
register_stored();
btd_register_device_driver(&audio_driver);
return 0;
failed:
@ -1500,17 +814,11 @@ void audio_manager_exit(void)
{
server_exit();
g_dbus_unregister_interface(connection, AUDIO_MANAGER_PATH,
AUDIO_MANAGER_INTERFACE);
dbus_connection_unref(connection);
connection = NULL;
}
btd_unregister_device_driver(&audio_driver);
struct audio_device *manager_default_device(void)
{
return default_dev;
connection = NULL;
}
struct audio_device *manager_get_connected_device(void)
@ -1537,7 +845,7 @@ struct audio_device *manager_find_device(const bdaddr_t *bda, const char *interf
GSList *l;
if (!bacmp(bda, BDADDR_ANY) && !interface && !connected)
return default_dev;
return devices->data;
for (l = devices; l != NULL; l = l->next) {
struct audio_device *dev = l->data;

View File

@ -44,7 +44,5 @@ gboolean server_is_enabled(uint16_t svc);
struct audio_device *manager_find_device(const bdaddr_t *bda, const char *interface,
gboolean connected);
struct audio_device *manager_device_connected(const bdaddr_t *bda, const char *uuid);
gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb,
void *user_data);

View File

@ -502,6 +502,9 @@ struct sink *sink_init(struct audio_device *dev)
dev, NULL))
return NULL;
info("Registered interface %s on path %s",
AUDIO_SINK_INTERFACE, dev->path);
return g_new0(struct sink, 1);
}

View File

@ -744,16 +744,6 @@ failed:
unix_ipc_error(client, BT_STREAMSTOP_RSP, EIO);
}
static void create_cb(struct audio_device *dev, void *user_data)
{
struct unix_client *client = user_data;
if (!dev)
unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO);
else
start_discovery(dev, client);
}
static void handle_getcapabilities_req(struct unix_client *client,
struct bt_getcapabilities_req *req)
{
@ -772,15 +762,8 @@ static void handle_getcapabilities_req(struct unix_client *client,
else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
client->interface = g_strdup(AUDIO_SINK_INTERFACE);
if (!manager_find_device(&bdaddr, NULL, FALSE)) {
if (!(req->flags & BT_FLAG_AUTOCONNECT))
goto failed;
if (!bacmp(&bdaddr, BDADDR_ANY))
goto failed;
if (!manager_create_device(&bdaddr, create_cb, client))
goto failed;
return;
}
if (!manager_find_device(&bdaddr, NULL, FALSE))
goto failed;
dev = manager_find_device(&bdaddr, client->interface, TRUE);
if (!dev) {
@ -918,13 +901,8 @@ static void handle_setconfiguration_req(struct unix_client *client,
}
}
if (!manager_find_device(&bdaddr, NULL, FALSE)) {
if (!bacmp(&bdaddr, BDADDR_ANY))
goto failed;
if (!manager_create_device(&bdaddr, create_cb, client))
goto failed;
return;
}
if (!manager_find_device(&bdaddr, NULL, FALSE))
goto failed;
dev = manager_find_device(&bdaddr, client->interface, TRUE);
if (!dev)