From b3078b2a9297a8422250821111bf082864a93119 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bokowy Date: Mon, 4 Mar 2019 20:38:33 +0100 Subject: [PATCH] Adapter abstraction layer for HCI device Add adapter abstraction layer to the internal transport management stack. Now, it will be possible to handle multiple number of HCI with single BlueALSA instance. The layer hierarchy looks like this: adapter (HCI) -> device (BT device) -> transport (audio) --- src/Makefile.am | 1 + src/ba-adapter.c | 80 ++++++++++++++++ src/ba-adapter.h | 40 ++++++++ src/ba-device.c | 24 ++++- src/ba-device.h | 14 ++- src/ba-transport.c | 75 ++++----------- src/ba-transport.h | 8 +- src/bluealsa.c | 4 - src/bluealsa.h | 21 +--- src/bluez.c | 234 ++++++++++++++++++++++++++++----------------- src/ctl.c | 78 +++++++-------- src/ctl.h | 7 +- src/main.c | 5 +- src/ofono.c | 162 ++++++++++++++++++++++--------- src/utils.c | 12 +++ src/utils.h | 1 + test/server-mock.c | 19 ++-- test/test-ctl.c | 12 +-- 18 files changed, 509 insertions(+), 288 deletions(-) create mode 100644 src/ba-adapter.c create mode 100644 src/ba-adapter.h diff --git a/src/Makefile.am b/src/Makefile.am index 38240ae25..2dba9abef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,6 +9,7 @@ bluealsa_SOURCES = \ shared/log.c \ shared/rt.c \ at.c \ + ba-adapter.c \ ba-device.c \ ba-transport.c \ bluealsa.c \ diff --git a/src/ba-adapter.c b/src/ba-adapter.c new file mode 100644 index 000000000..4ff812017 --- /dev/null +++ b/src/ba-adapter.c @@ -0,0 +1,80 @@ +/* + * BlueALSA - ba-adapter.c + * Copyright (c) 2016-2019 Arkadiusz Bokowy + * + * This file is a part of bluez-alsa. + * + * This project is licensed under the terms of the MIT license. + * + */ + +#include "ba-adapter.h" + +#include +#include + +#include "bluealsa.h" + +static guint g_bdaddr_hash(gconstpointer v) { + const bdaddr_t *ba = (const bdaddr_t *)v; + return ((uint32_t *)ba->b)[0] * ((uint16_t *)ba->b)[2]; +} + +static gboolean g_bdaddr_equal(gconstpointer v1, gconstpointer v2) { + return bacmp(v1, v2) == 0; +} + +struct ba_adapter *ba_adapter_new(int dev_id, const char *name) { + + struct ba_adapter *a; + + /* make sure we are within array boundaries */ + if (dev_id < 0 || dev_id >= HCI_MAX_DEV) { + errno = EINVAL; + return NULL; + } + + if ((a = calloc(1, sizeof(*a))) == NULL) + return NULL; + + a->hci_dev_id = dev_id; + + if (name != NULL) + strncpy(a->hci_name, name, sizeof(a->hci_name) - 1); + else + sprintf(a->hci_name, "hci%d", dev_id); + + pthread_mutex_init(&a->devices_mutex, NULL); + a->devices = g_hash_table_new_full(g_bdaddr_hash, g_bdaddr_equal, NULL, NULL); + + config.adapters[a->hci_dev_id] = a; + return a; +} + +struct ba_adapter *ba_adapter_lookup(int dev_id) { + if (dev_id >= 0 && dev_id < HCI_MAX_DEV) + return config.adapters[dev_id]; + return NULL; +} + +void ba_adapter_free(struct ba_adapter *a) { + + /* detach adapter from global configuration */ + config.adapters[a->hci_dev_id] = NULL; + + if (a->devices != NULL) { + + GHashTableIter iter; + struct ba_device *d; + + g_hash_table_iter_init(&iter, a->devices); + while (g_hash_table_iter_next(&iter, NULL, (gpointer)&d)) + ba_device_free(d); + + g_hash_table_unref(a->devices); + } + + pthread_mutex_destroy(&a->devices_mutex); + + free(a); +} diff --git a/src/ba-adapter.h b/src/ba-adapter.h new file mode 100644 index 000000000..25c7775a7 --- /dev/null +++ b/src/ba-adapter.h @@ -0,0 +1,40 @@ +/* + * BlueALSA - ba-adapter.h + * Copyright (c) 2016-2019 Arkadiusz Bokowy + * + * This file is a part of bluez-alsa. + * + * This project is licensed under the terms of the MIT license. + * + */ + +#ifndef BLUEALSA_BAADAPTER_H +#define BLUEALSA_BAADAPTER_H + +#if HAVE_CONFIG_H +# include +#endif + +#include + +#include + +#include "ba-device.h" + +/* Data associated with BT adapter. */ +struct ba_adapter { + + int hci_dev_id; + char hci_name[8]; + + /* collection of connected devices */ + pthread_mutex_t devices_mutex; + GHashTable *devices; + +}; + +struct ba_adapter *ba_adapter_new(int dev_id, const char *name); +struct ba_adapter *ba_adapter_lookup(int dev_id); +void ba_adapter_free(struct ba_adapter *a); + +#endif diff --git a/src/ba-device.c b/src/ba-device.c index d7c13794d..713d849fa 100644 --- a/src/ba-device.c +++ b/src/ba-device.c @@ -10,6 +10,7 @@ #include "ba-device.h" +#include #include #include "ba-transport.h" @@ -17,7 +18,7 @@ #include "ctl.h" struct ba_device *ba_device_new( - int hci_dev_id, + struct ba_adapter *adapter, const bdaddr_t *addr, const char *name) { @@ -26,21 +27,34 @@ struct ba_device *ba_device_new( if ((d = calloc(1, sizeof(*d))) == NULL) return NULL; - d->hci_dev_id = hci_dev_id; + d->a = adapter; bacpy(&d->addr, addr); strncpy(d->name, name, sizeof(d->name) - 1); - d->transports = g_hash_table_new_full(g_str_hash, g_str_equal, - NULL, (GDestroyNotify)transport_free); + d->transports = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); + g_hash_table_insert(adapter->devices, &d->addr, d); return d; } +struct ba_device *ba_device_lookup( + struct ba_adapter *adapter, + const bdaddr_t *addr) { +#if DEBUG + /* make sure that the device mutex is acquired */ + g_assert(pthread_mutex_trylock(&adapter->devices_mutex) == EBUSY); +#endif + return g_hash_table_lookup(adapter->devices, addr); +} + void ba_device_free(struct ba_device *d) { if (d == NULL) return; + /* detach device from the adapter */ + g_hash_table_steal(d->a->devices, &d->addr); + /* XXX: Modification-safe remove-all loop. * * By the usage of a standard g_hash_table_remove_all() function, one @@ -64,7 +78,7 @@ void ba_device_free(struct ba_device *d) { if (!g_hash_table_iter_next(&iter, NULL, (gpointer)&t)) break; - transport_free(t); + ba_transport_free(t); } g_hash_table_unref(d->transports); diff --git a/src/ba-device.h b/src/ba-device.h index c669f631c..31bc77d17 100644 --- a/src/ba-device.h +++ b/src/ba-device.h @@ -22,10 +22,13 @@ #include #include +#include "ba-adapter.h" + struct ba_device { - /* ID of the underlying HCI device */ - int hci_dev_id; + /* backward reference to adapter */ + struct ba_adapter *a; + /* address of the Bluetooth device */ bdaddr_t addr; /* human-readable Bluetooth device name */ @@ -56,9 +59,14 @@ struct ba_device { }; struct ba_device *ba_device_new( - int hci_dev_id, + struct ba_adapter *adapter, const bdaddr_t *addr, const char *name); + +struct ba_device *ba_device_lookup( + struct ba_adapter *adapter, + const bdaddr_t *addr); + void ba_device_free(struct ba_device *d); void ba_device_set_battery_level(struct ba_device *d, uint8_t value); diff --git a/src/ba-transport.c b/src/ba-transport.c index 40afe8b7c..ee38e8e4b 100644 --- a/src/ba-transport.c +++ b/src/ba-transport.c @@ -161,7 +161,7 @@ struct ba_transport *transport_new( fail: err = errno; - transport_free(t); + ba_transport_free(t); errno = err; return NULL; } @@ -240,7 +240,7 @@ struct ba_transport *transport_new_rfcomm( fail: if (dbus_path_sco != NULL) g_free(dbus_path_sco); - transport_free(t); + ba_transport_free(t); return NULL; } @@ -280,7 +280,17 @@ struct ba_transport *transport_new_sco( return t; } -void transport_free(struct ba_transport *t) { +struct ba_transport *ba_transport_lookup( + struct ba_device *device, + const char *dbus_path) { +#if DEBUG + /* make sure that the device mutex is acquired */ + g_assert(pthread_mutex_trylock(&device->a->devices_mutex) == EBUSY); +#endif + return g_hash_table_lookup(device->transports, dbus_path); +} + +void ba_transport_free(struct ba_transport *t) { if (t == NULL || t->state == TRANSPORT_LIMBO) return; @@ -323,7 +333,7 @@ void transport_free(struct ba_transport *t) { case BA_TRANSPORT_PROFILE_RFCOMM: memset(&t->d->battery, 0, sizeof(t->d->battery)); memset(&t->d->xapl, 0, sizeof(t->d->xapl)); - transport_free(t->rfcomm.sco); + ba_transport_free(t->rfcomm.sco); break; case BA_TRANSPORT_PROFILE_HFP_HF: case BA_TRANSPORT_PROFILE_HFP_AG: @@ -339,9 +349,7 @@ void transport_free(struct ba_transport *t) { break; } - /* If the free action was called on the behalf of the destroy notification, - * removing a value from the hash-table shouldn't hurt - it would have been - * removed anyway. */ + /* detach transport from the device */ g_hash_table_steal(t->d->transports, t->dbus_path); if (pcm_type != BA_PCM_TYPE_NULL) @@ -352,55 +360,6 @@ void transport_free(struct ba_transport *t) { free(t); } -struct ba_transport *transport_lookup(GHashTable *devices, const char *dbus_path) { - -#if DEBUG - /* make sure that the device mutex is acquired */ - g_assert(pthread_mutex_trylock(&config.devices_mutex) == EBUSY); -#endif - - GHashTableIter iter; - struct ba_device *d; - struct ba_transport *t; - - g_hash_table_iter_init(&iter, devices); - while (g_hash_table_iter_next(&iter, NULL, (gpointer)&d)) { - if ((t = g_hash_table_lookup(d->transports, dbus_path)) != NULL) - return t; - } - - return NULL; -} - -bool transport_remove(GHashTable *devices, const char *dbus_path) { - -#if DEBUG - /* make sure that the device mutex is acquired */ - g_assert(pthread_mutex_trylock(&config.devices_mutex) == EBUSY); -#endif - - GHashTableIter iter; - struct ba_device *d; - struct ba_transport *t; - - g_hash_table_iter_init(&iter, devices); - while (g_hash_table_iter_next(&iter, NULL, (gpointer)&d)) { - /* Disassociate D-Bus owner before further actions. This will ensure, - * that we will not generate errors by using non-existent interface. */ - if ((t = g_hash_table_lookup(d->transports, dbus_path)) != NULL) { - free(t->dbus_owner); - t->dbus_owner = NULL; - } - if (g_hash_table_remove(d->transports, dbus_path)) { - if (g_hash_table_size(d->transports) == 0) - g_hash_table_iter_remove(&iter); - return true; - } - } - - return false; -} - int transport_send_signal(struct ba_transport *t, enum ba_transport_signal sig) { return write(t->sig_fd[1], &sig, sizeof(sig)); } @@ -807,7 +766,7 @@ static int transport_release_bt_rfcomm(struct ba_transport *t) { /* BlueZ does not trigger profile disconnection signal when the Bluetooth * link has been lost (e.g. device power down). However, it is required to * remove transport from the transport pool before reconnecting. */ - transport_free(t); + ba_transport_free(t); return 0; } @@ -819,7 +778,7 @@ static int transport_acquire_bt_sco(struct ba_transport *t) { if (t->bt_fd != -1) return t->bt_fd; - if (hci_devinfo(t->d->hci_dev_id, &di) == -1) { + if (hci_devinfo(t->d->a->hci_dev_id, &di) == -1) { error("Couldn't get HCI device info: %s", strerror(errno)); return -1; } diff --git a/src/ba-transport.h b/src/ba-transport.h index 1f56cf1ff..18a8b38b0 100644 --- a/src/ba-transport.h +++ b/src/ba-transport.h @@ -225,10 +225,12 @@ struct ba_transport *transport_new_sco( struct ba_transport_type type, const char *dbus_owner, const char *dbus_path); -void transport_free(struct ba_transport *t); -struct ba_transport *transport_lookup(GHashTable *devices, const char *dbus_path); -bool transport_remove(GHashTable *devices, const char *dbus_path); +struct ba_transport *ba_transport_lookup( + struct ba_device *device, + const char *dbus_path); + +void ba_transport_free(struct ba_transport *t); int transport_send_signal(struct ba_transport *t, enum ba_transport_signal sig); int transport_send_rfcomm(struct ba_transport *t, const char command[32]); diff --git a/src/bluealsa.c b/src/bluealsa.c index 30e31c327..5d56abd0c 100644 --- a/src/bluealsa.c +++ b/src/bluealsa.c @@ -79,10 +79,6 @@ int bluealsa_config_init(void) { config.main_thread = pthread_self(); - pthread_mutex_init(&config.devices_mutex, NULL); - config.devices = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, (GDestroyNotify)ba_device_free); - config.dbus_objects = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); diff --git a/src/bluealsa.h b/src/bluealsa.h index bbdf8153a..cc03de765 100644 --- a/src/bluealsa.h +++ b/src/bluealsa.h @@ -24,8 +24,8 @@ #include #include +#include "ba-adapter.h" #include "ba-transport.h" -#include "bluez.h" #include "bluez-a2dp.h" #include "ctl.h" #include "shared/ctl-proto.h" @@ -49,13 +49,12 @@ struct ba_config { /* established D-Bus connection */ GDBusConnection *dbus; + /* adapters indexed by the HCI device ID */ + struct ba_adapter *adapters[HCI_MAX_DEV]; + /* used for main thread identification */ pthread_t main_thread; - /* collection of connected devices */ - pthread_mutex_t devices_mutex; - GHashTable *devices; - /* registered D-Bus objects */ GHashTable *dbus_objects; @@ -128,16 +127,4 @@ extern struct ba_config config; int bluealsa_config_init(void); -#define bluealsa_devpool_mutex_lock() \ - pthread_mutex_lock(&config.devices_mutex) -#define bluealsa_devpool_mutex_unlock() \ - pthread_mutex_unlock(&config.devices_mutex) - -#define bluealsa_device_insert(key, device) \ - g_hash_table_insert(config.devices, g_strdup(key), device) -#define bluealsa_device_lookup(key) \ - ((struct ba_device *)g_hash_table_lookup(config.devices, key)) -#define bluealsa_device_remove(key) \ - g_hash_table_remove(config.devices, key) - #endif diff --git a/src/bluez.c b/src/bluez.c index 6fba74cbc..0d9193ed6 100644 --- a/src/bluez.c +++ b/src/bluez.c @@ -45,22 +45,20 @@ static int bluez_get_dbus_object_count(struct ba_transport_type ttype) { } /** - * Get device associated with given D-Bus object path. */ -struct ba_device *bluez_get_device(const char *path) { + * Create new device using BlueZ object path. */ +struct ba_device *bluez_ba_device_new( + struct ba_adapter *adapter, + const char *path) { #if DEBUG /* make sure that the device mutex is acquired */ - g_assert(pthread_mutex_trylock(&config.devices_mutex) == EBUSY); + g_assert(pthread_mutex_trylock(&adapter->devices_mutex) == EBUSY); #endif - struct ba_device *d; - char name[sizeof(d->name)]; + char name[sizeof(((struct ba_device *)0)->name)]; GVariant *property; bdaddr_t addr; - if ((d = bluealsa_device_lookup(path)) != NULL) - return d; - g_dbus_bluez_object_path_to_bdaddr(path, &addr); ba2str(&addr, name); @@ -72,9 +70,7 @@ struct ba_device *bluez_get_device(const char *path) { g_variant_unref(property); } - d = ba_device_new(config.hci_dev.dev_id, &addr, name); - bluealsa_device_insert(path, d); - return d; + return ba_device_new(adapter, &addr, name); } /** @@ -174,7 +170,7 @@ static int bluez_a2dp_set_transport_state( static void bluez_endpoint_select_configuration(GDBusMethodInvocation *inv, void *userdata) { - const char *path = g_dbus_method_invocation_get_object_path(inv); + const char *endpoint_path = g_dbus_method_invocation_get_object_path(inv); GVariant *params = g_dbus_method_invocation_get_parameters(inv); const struct bluez_a2dp_codec *codec = userdata; @@ -348,7 +344,7 @@ static void bluez_endpoint_select_configuration(GDBusMethodInvocation *inv, void #endif default: - debug("Endpoint path not supported: %s", path); + debug("Endpoint path not supported: %s", endpoint_path); g_dbus_method_invocation_return_error(inv, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_OBJECT, "Not supported"); goto final; @@ -377,15 +373,15 @@ static void bluez_endpoint_select_configuration(GDBusMethodInvocation *inv, void static int bluez_endpoint_set_configuration(GDBusMethodInvocation *inv, void *userdata) { const gchar *sender = g_dbus_method_invocation_get_sender(inv); - const gchar *path = g_dbus_method_invocation_get_object_path(inv); + const gchar *endpoint_path = g_dbus_method_invocation_get_object_path(inv); GVariant *params = g_dbus_method_invocation_get_parameters(inv); const struct bluez_a2dp_codec *codec = userdata; - bool devpool_mutex_locked = false; - struct ba_transport *t; - struct ba_device *d; - const uint16_t codec_id = codec->id; + struct ba_adapter *a = NULL; + struct ba_transport *t = NULL; + struct ba_device *d = NULL; + char *device = NULL, *state = NULL; uint8_t *configuration = NULL; uint16_t volume = 127; @@ -393,12 +389,12 @@ static int bluez_endpoint_set_configuration(GDBusMethodInvocation *inv, void *us size_t size = 0; int ret = 0; - const char *transport; + const char *transport_path; GVariantIter *properties; GVariant *value = NULL; const char *key; - g_variant_get(params, "(&oa{sv})", &transport, &properties); + g_variant_get(params, "(&oa{sv})", &transport_path, &properties); while (g_variant_iter_next(properties, "{&sv}", &key, &value)) { if (strcmp(key, "Device") == 0) { @@ -578,23 +574,31 @@ static int bluez_endpoint_set_configuration(GDBusMethodInvocation *inv, void *us value = NULL; } + int hci_dev_id = g_dbus_bluez_object_path_to_hci_dev_id(transport_path); + if ((a = ba_adapter_lookup(hci_dev_id)) == NULL && + (a = ba_adapter_new(hci_dev_id, NULL)) == NULL) { + error("Couldn't create new adapter: %s", strerror(errno)); + goto fail; + } + /* we are going to modify the devices hash-map */ - bluealsa_devpool_mutex_lock(); - devpool_mutex_locked = true; + pthread_mutex_lock(&a->devices_mutex); - if (transport_lookup(config.devices, transport) != NULL) { - error("Transport already configured: %s", transport); + bdaddr_t addr; + g_dbus_bluez_object_path_to_bdaddr(transport_path, &addr); + if ((d = ba_device_lookup(a, &addr)) == NULL && + (d = bluez_ba_device_new(a, transport_path)) == NULL) { + error("Couldn't create new device: %s", transport_path); goto fail; } - /* get the device structure for obtained device path */ - if ((d = bluez_get_device(device)) == NULL) { - error("Couldn't get device: %s", strerror(errno)); + if (ba_transport_lookup(d, transport_path) != NULL) { + error("Transport already configured: %s", transport_path); goto fail; } - if ((t = transport_new_a2dp(d, g_dbus_bluez_object_path_to_transport_type(path), - sender, transport, configuration, size)) == NULL) { + if ((t = transport_new_a2dp(d, g_dbus_bluez_object_path_to_transport_type(endpoint_path), + sender, transport_path, configuration, size)) == NULL) { error("Couldn't create new transport: %s", strerror(errno)); goto fail; } @@ -620,8 +624,8 @@ static int bluez_endpoint_set_configuration(GDBusMethodInvocation *inv, void *us ret = -1; final: - if (devpool_mutex_locked) - bluealsa_devpool_mutex_unlock(); + if (a != NULL) + pthread_mutex_unlock(&a->devices_mutex); g_variant_iter_free(properties); if (value != NULL) g_variant_unref(value); @@ -635,14 +639,29 @@ static void bluez_endpoint_clear_configuration(GDBusMethodInvocation *inv, void (void)userdata; GVariant *params = g_dbus_method_invocation_get_parameters(inv); - const char *transport; - g_variant_get(params, "(&o)", &transport); + struct ba_adapter *a = NULL; + struct ba_device *d = NULL; + struct ba_transport *t = NULL; + + const char *transport_path; + g_variant_get(params, "(&o)", &transport_path); - bluealsa_devpool_mutex_lock(); - transport_remove(config.devices, transport); - bluealsa_devpool_mutex_unlock(); + int hci_dev_id = g_dbus_bluez_object_path_to_hci_dev_id(transport_path); + if ((a = ba_adapter_lookup(hci_dev_id)) == NULL) + goto fail; + pthread_mutex_lock(&a->devices_mutex); + + bdaddr_t addr; + g_dbus_bluez_object_path_to_bdaddr(transport_path, &addr); + if ((d = ba_device_lookup(a, &addr)) != NULL && + (t = ba_transport_lookup(d, transport_path)) != NULL) + ba_transport_free(t); + + pthread_mutex_unlock(&a->devices_mutex); + +fail: g_object_unref(inv); } @@ -650,11 +669,11 @@ static void bluez_endpoint_release(GDBusMethodInvocation *inv, void *userdata) { (void)userdata; GDBusConnection *conn = g_dbus_method_invocation_get_connection(inv); - const char *path = g_dbus_method_invocation_get_object_path(inv); - gpointer hash = GINT_TO_POINTER(g_str_hash(path)); + const char *endpoint_path = g_dbus_method_invocation_get_object_path(inv); + gpointer hash = GINT_TO_POINTER(g_str_hash(endpoint_path)); struct ba_dbus_object *obj; - debug("Releasing endpoint: %s", path); + debug("Releasing endpoint: %s", endpoint_path); if ((obj = g_hash_table_lookup(config.dbus_objects, hash)) != NULL) { g_dbus_connection_unregister_object(conn, obj->id); @@ -720,14 +739,14 @@ static int bluez_register_a2dp_endpoint( struct ba_transport_type ttype = { .profile = profile, .codec = codec->id }; - gchar *path = g_strdup_printf("%s/%d", + gchar *transport_path = g_strdup_printf("%s/%d", g_dbus_transport_type_to_bluez_object_path(ttype), bluez_get_dbus_object_count(ttype) + 1); - gpointer hash = GINT_TO_POINTER(g_str_hash(path)); + gpointer hash = GINT_TO_POINTER(g_str_hash(transport_path)); if (g_hash_table_contains(config.dbus_objects, hash)) { - debug("Endpoint already registered: %s", path); - g_free(path); + debug("Endpoint already registered: %s", transport_path); + g_free(transport_path); return 0; } @@ -742,8 +761,8 @@ static int bluez_register_a2dp_endpoint( .ttype = ttype, }; - debug("Registering endpoint: %s", path); - if ((dbus_object.id = g_dbus_connection_register_object(conn, path, + debug("Registering endpoint: %s", transport_path); + if ((dbus_object.id = g_dbus_connection_register_object(conn, transport_path, (GDBusInterfaceInfo *)&bluez_iface_endpoint, &endpoint_vtable, (void *)codec, endpoint_free, &err)) == 0) goto fail; @@ -766,7 +785,7 @@ static int bluez_register_a2dp_endpoint( g_variant_builder_add(&properties, "{sv}", "Codec", g_variant_new_byte(codec->id)); g_variant_builder_add(&properties, "{sv}", "Capabilities", g_variant_builder_end(&caps)); - g_dbus_message_set_body(msg, g_variant_new("(oa{sv})", path, &properties)); + g_dbus_message_set_body(msg, g_variant_new("(oa{sv})", transport_path, &properties)); g_variant_builder_clear(&properties); if ((rep = g_dbus_connection_send_message_with_reply_sync(conn, msg, @@ -787,7 +806,7 @@ static int bluez_register_a2dp_endpoint( ret = -1; final: - g_free(path); + g_free(transport_path); if (msg != NULL) g_object_unref(msg); if (rep != NULL) @@ -830,19 +849,20 @@ static void bluez_profile_new_connection(GDBusMethodInvocation *inv, void *userd GDBusMessage *msg = g_dbus_method_invocation_get_message(inv); const gchar *sender = g_dbus_method_invocation_get_sender(inv); - const gchar *path = g_dbus_method_invocation_get_object_path(inv); + const gchar *profile_path = g_dbus_method_invocation_get_object_path(inv); GVariant *params = g_dbus_method_invocation_get_parameters(inv); - bool devpool_mutex_locked = false; - struct ba_transport *t; - struct ba_device *d; + struct ba_adapter *a = NULL; + struct ba_device *d = NULL; + struct ba_transport *t = NULL; + + const char *device_path; GVariantIter *properties; GUnixFDList *fd_list; - const char *device; GError *err = NULL; - int fd; + int fd = -1; - g_variant_get(params, "(&oha{sv})", &device, &fd, &properties); + g_variant_get(params, "(&oha{sv})", &device_path, &fd, &properties); fd_list = g_dbus_message_get_unix_fd_list(msg); if ((fd = g_unix_fd_list_get(fd_list, 0, &err)) == -1) { @@ -850,17 +870,26 @@ static void bluez_profile_new_connection(GDBusMethodInvocation *inv, void *userd goto fail; } + int hci_dev_id = g_dbus_bluez_object_path_to_hci_dev_id(device_path); + if ((a = ba_adapter_lookup(hci_dev_id)) == NULL && + (a = ba_adapter_new(hci_dev_id, NULL)) == NULL) { + error("Couldn't create new adapter: %s", strerror(errno)); + goto fail; + } + /* we are going to modify the devices hash-map */ - bluealsa_devpool_mutex_lock(); - devpool_mutex_locked = true; + pthread_mutex_lock(&a->devices_mutex); - if ((d = bluez_get_device(device)) == NULL) { - error("Couldn't get device: %s", strerror(errno)); + bdaddr_t addr; + g_dbus_bluez_object_path_to_bdaddr(device_path, &addr); + if ((d = ba_device_lookup(a, &addr)) == NULL && + (d = bluez_ba_device_new(a, device_path)) == NULL) { + error("Couldn't create new device: %s", strerror(errno)); goto fail; } - if ((t = transport_new_rfcomm(d, g_dbus_bluez_object_path_to_transport_type(path), - sender, device)) == NULL) { + if ((t = transport_new_rfcomm(d, g_dbus_bluez_object_path_to_transport_type(profile_path), + sender, device_path)) == NULL) { error("Couldn't create new transport: %s", strerror(errno)); goto fail; } @@ -880,10 +909,12 @@ static void bluez_profile_new_connection(GDBusMethodInvocation *inv, void *userd fail: g_dbus_method_invocation_return_error(inv, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Unable to connect profile"); + if (fd != -1) + close(fd); final: - if (devpool_mutex_locked) - bluealsa_devpool_mutex_unlock(); + if (a != NULL) + pthread_mutex_unlock(&a->devices_mutex); g_variant_iter_free(properties); if (err != NULL) g_error_free(err); @@ -893,14 +924,29 @@ static void bluez_profile_request_disconnection(GDBusMethodInvocation *inv, void (void)userdata; GVariant *params = g_dbus_method_invocation_get_parameters(inv); - const char *device; - g_variant_get(params, "(&o)", &device); + struct ba_adapter *a = NULL; + struct ba_device *d = NULL; + struct ba_transport *t = NULL; - bluealsa_devpool_mutex_lock(); - transport_remove(config.devices, device); - bluealsa_devpool_mutex_unlock(); + const char *device_path; + g_variant_get(params, "(&o)", &device_path); + int hci_dev_id = g_dbus_bluez_object_path_to_hci_dev_id(device_path); + if ((a = ba_adapter_lookup(hci_dev_id)) == NULL) + goto fail; + + pthread_mutex_lock(&a->devices_mutex); + + bdaddr_t addr; + g_dbus_bluez_object_path_to_bdaddr(device_path, &addr); + if ((d = ba_device_lookup(a, &addr)) != NULL && + (t = ba_transport_lookup(d, device_path)) != NULL) + ba_transport_free(t); + + pthread_mutex_unlock(&a->devices_mutex); + +fail: g_object_unref(inv); } @@ -908,11 +954,11 @@ static void bluez_profile_release(GDBusMethodInvocation *inv, void *userdata) { (void)userdata; GDBusConnection *conn = g_dbus_method_invocation_get_connection(inv); - const char *path = g_dbus_method_invocation_get_object_path(inv); - gpointer hash = GINT_TO_POINTER(g_str_hash(path)); + const char *profile_path = g_dbus_method_invocation_get_object_path(inv); + gpointer hash = GINT_TO_POINTER(g_str_hash(profile_path)); struct ba_dbus_object *obj; - debug("Releasing profile: %s", path); + debug("Releasing profile: %s", profile_path); if ((obj = g_hash_table_lookup(config.dbus_objects, hash)) != NULL) { g_dbus_connection_unregister_object(conn, obj->id); @@ -963,11 +1009,11 @@ static int bluez_register_profile( uint16_t features) { const struct ba_transport_type ttype = { .profile = profile }; - const char *path = g_dbus_transport_type_to_bluez_object_path(ttype); - gpointer hash = GINT_TO_POINTER(g_str_hash(path)); + const char *profile_path = g_dbus_transport_type_to_bluez_object_path(ttype); + gpointer hash = GINT_TO_POINTER(g_str_hash(profile_path)); if (g_hash_table_contains(config.dbus_objects, hash)) { - debug("Profile already registered: %s", path); + debug("Profile already registered: %s", profile_path); return 0; } @@ -980,8 +1026,8 @@ static int bluez_register_profile( .ttype = ttype, }; - debug("Registering profile: %s", path); - if ((dbus_object.id = g_dbus_connection_register_object(conn, path, + debug("Registering profile: %s", profile_path); + if ((dbus_object.id = g_dbus_connection_register_object(conn, profile_path, (GDBusInterfaceInfo *)&bluez_iface_profile, &profile_vtable, NULL, NULL, &err)) == 0) goto fail; @@ -997,7 +1043,7 @@ static int bluez_register_profile( if (features) g_variant_builder_add(&options, "{sv}", "Features", g_variant_new_uint16(features)); - g_dbus_message_set_body(msg, g_variant_new("(osa{sv})", path, uuid, &options)); + g_dbus_message_set_body(msg, g_variant_new("(osa{sv})", profile_path, uuid, &options)); g_variant_builder_clear(&options); if ((rep = g_dbus_connection_send_message_with_reply_sync(conn, msg, @@ -1076,7 +1122,7 @@ static void bluez_signal_interfaces_added(GDBusConnection *conn, const gchar *se } static void bluez_signal_transport_changed(GDBusConnection *conn, const gchar *sender, - const gchar *path, const gchar *interface, const gchar *signal, GVariant *params, + const gchar *transport_path, const gchar *interface, const gchar *signal, GVariant *params, void *userdata) { (void)conn; (void)sender; @@ -1084,24 +1130,38 @@ static void bluez_signal_transport_changed(GDBusConnection *conn, const gchar *s (void)userdata; const gchar *signature = g_variant_get_type_string(params); - bool devpool_mutex_locked = false; + if (strcmp(signature, "(sa{sv}as)") != 0) { + error("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)"); + return; + } + + struct ba_adapter *a = NULL; + struct ba_device *d = NULL; + struct ba_transport *t = NULL; + GVariantIter *properties = NULL; GVariantIter *unknown = NULL; GVariant *value = NULL; - struct ba_transport *t; const char *iface; const char *key; - if (strcmp(signature, "(sa{sv}as)") != 0) { - error("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)"); + int hci_dev_id = g_dbus_bluez_object_path_to_hci_dev_id(transport_path); + if ((a = ba_adapter_lookup(hci_dev_id)) == NULL) { + error("Adapter not available: %s", transport_path); goto fail; } - bluealsa_devpool_mutex_lock(); - devpool_mutex_locked = true; + pthread_mutex_lock(&a->devices_mutex); + + bdaddr_t addr; + g_dbus_bluez_object_path_to_bdaddr(transport_path, &addr); + if ((d = ba_device_lookup(a, &addr)) == NULL) { + error("Device not available: %s", transport_path); + goto fail; + } - if ((t = transport_lookup(config.devices, path)) == NULL) { - error("Transport not available: %s", path); + if ((t = ba_transport_lookup(d, transport_path)) == NULL) { + error("Transport not available: %s", transport_path); goto fail; } @@ -1153,8 +1213,8 @@ static void bluez_signal_transport_changed(GDBusConnection *conn, const gchar *s } fail: - if (devpool_mutex_locked) - bluealsa_devpool_mutex_unlock(); + if (a != NULL) + pthread_mutex_unlock(&a->devices_mutex); if (properties != NULL) g_variant_iter_free(properties); if (value != NULL) diff --git a/src/ctl.c b/src/ctl.c index 4dcb7f2b0..fba0ba9b1 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -52,26 +52,26 @@ * by the devices hash-table. If the devices hash-table is modified in some * other thread, it may result in an undefined behavior. * - * @param devices Address of the hash-table with connected devices. + * @param a Address of the adapter structure with connected devices. * @param addr Address to the structure with the looked up BT address. * @param type Looked up PCM type with stream mask. * @param t Address, where the transport structure pointer should be stored. * @return If the lookup succeeded, this function returns 0. Otherwise, -1 or * -2 is returned respectively for not found device and not found stream. * Upon error value of the transport pointer is undefined. */ -static int ctl_lookup_transport(GHashTable *devices, const bdaddr_t *addr, +static int ctl_lookup_transport(struct ba_adapter *a, const bdaddr_t *addr, uint8_t type, struct ba_transport **t) { #if DEBUG /* make sure that the device mutex is acquired */ - g_assert(pthread_mutex_trylock(&config.devices_mutex) == EBUSY); + g_assert(pthread_mutex_trylock(&a->devices_mutex) == EBUSY); #endif bool device_found = false; GHashTableIter iter_d, iter_t; struct ba_device *d; - for (g_hash_table_iter_init(&iter_d, devices); + for (g_hash_table_iter_init(&iter_d, a->devices); g_hash_table_iter_next(&iter_d, NULL, (gpointer)&d); ) { if (bacmp(&d->addr, addr) != 0) @@ -182,7 +182,6 @@ static void ctl_thread_cmd_subscribe(struct ba_ctl *ctl, struct ba_request *req, } static void ctl_thread_cmd_list_devices(struct ba_ctl *ctl, struct ba_request *req, int fd) { - (void)ctl; (void)req; static const struct ba_msg_status status = { BA_STATUS_CODE_SUCCESS }; @@ -190,9 +189,9 @@ static void ctl_thread_cmd_list_devices(struct ba_ctl *ctl, struct ba_request *r GHashTableIter iter_d; struct ba_device *d; - bluealsa_devpool_mutex_lock(); + pthread_mutex_lock(&ctl->a->devices_mutex); - for (g_hash_table_iter_init(&iter_d, config.devices); + for (g_hash_table_iter_init(&iter_d, ctl->a->devices); g_hash_table_iter_next(&iter_d, NULL, (gpointer)&d); ) { bacpy(&device.addr, &d->addr); @@ -205,12 +204,11 @@ static void ctl_thread_cmd_list_devices(struct ba_ctl *ctl, struct ba_request *r send(fd, &device, sizeof(device), MSG_NOSIGNAL); } - bluealsa_devpool_mutex_unlock(); + pthread_mutex_unlock(&ctl->a->devices_mutex); send(fd, &status, sizeof(status), MSG_NOSIGNAL); } static void ctl_thread_cmd_list_transports(struct ba_ctl *ctl, struct ba_request *req, int fd) { - (void)ctl; (void)req; static const struct ba_msg_status status = { BA_STATUS_CODE_SUCCESS }; @@ -219,9 +217,9 @@ static void ctl_thread_cmd_list_transports(struct ba_ctl *ctl, struct ba_request struct ba_device *d; struct ba_transport *t; - bluealsa_devpool_mutex_lock(); + pthread_mutex_lock(&ctl->a->devices_mutex); - for (g_hash_table_iter_init(&iter_d, config.devices); + for (g_hash_table_iter_init(&iter_d, ctl->a->devices); g_hash_table_iter_next(&iter_d, NULL, (gpointer)&d); ) for (g_hash_table_iter_init(&iter_t, d->transports); g_hash_table_iter_next(&iter_t, NULL, (gpointer)&t); ) { @@ -229,20 +227,19 @@ static void ctl_thread_cmd_list_transports(struct ba_ctl *ctl, struct ba_request send(fd, &transport, sizeof(transport), MSG_NOSIGNAL); } - bluealsa_devpool_mutex_unlock(); + pthread_mutex_unlock(&ctl->a->devices_mutex); send(fd, &status, sizeof(status), MSG_NOSIGNAL); } static void ctl_thread_cmd_transport_get(struct ba_ctl *ctl, struct ba_request *req, int fd) { - (void)ctl; struct ba_msg_status status = { BA_STATUS_CODE_SUCCESS }; struct ba_msg_transport transport; struct ba_transport *t; - bluealsa_devpool_mutex_lock(); + pthread_mutex_lock(&ctl->a->devices_mutex); - switch (ctl_lookup_transport(config.devices, &req->addr, req->type, &t)) { + switch (ctl_lookup_transport(ctl->a, &req->addr, req->type, &t)) { case -1: status.code = BA_STATUS_CODE_DEVICE_NOT_FOUND; goto fail; @@ -255,19 +252,18 @@ static void ctl_thread_cmd_transport_get(struct ba_ctl *ctl, struct ba_request * send(fd, &transport, sizeof(transport), MSG_NOSIGNAL); fail: - bluealsa_devpool_mutex_unlock(); + pthread_mutex_unlock(&ctl->a->devices_mutex); send(fd, &status, sizeof(status), MSG_NOSIGNAL); } static void ctl_thread_cmd_transport_set_volume(struct ba_ctl *ctl, struct ba_request *req, int fd) { - (void)ctl; struct ba_msg_status status = { BA_STATUS_CODE_SUCCESS }; struct ba_transport *t; - bluealsa_devpool_mutex_lock(); + pthread_mutex_lock(&ctl->a->devices_mutex); - switch (ctl_lookup_transport(config.devices, &req->addr, req->type, &t)) { + switch (ctl_lookup_transport(ctl->a, &req->addr, req->type, &t)) { case -1: status.code = BA_STATUS_CODE_DEVICE_NOT_FOUND; goto fail; @@ -314,12 +310,11 @@ static void ctl_thread_cmd_transport_set_volume(struct ba_ctl *ctl, struct ba_re bluealsa_ctl_send_event(ctl, BA_EVENT_VOLUME_CHANGED, &req->addr, req->type); fail: - bluealsa_devpool_mutex_unlock(); + pthread_mutex_unlock(&ctl->a->devices_mutex); send(fd, &status, sizeof(status), MSG_NOSIGNAL); } static void ctl_thread_cmd_pcm_open(struct ba_ctl *ctl, struct ba_request *req, int fd) { - (void)ctl; struct ba_msg_status status = { BA_STATUS_CODE_SUCCESS }; struct ba_transport *t; @@ -328,9 +323,9 @@ static void ctl_thread_cmd_pcm_open(struct ba_ctl *ctl, struct ba_request *req, debug("PCM requested for %s type %#x", batostr_(&req->addr), req->type); - bluealsa_devpool_mutex_lock(); + pthread_mutex_lock(&ctl->a->devices_mutex); - switch (ctl_lookup_transport(config.devices, &req->addr, req->type, &t)) { + switch (ctl_lookup_transport(ctl->a, &req->addr, req->type, &t)) { case -1: status.code = BA_STATUS_CODE_DEVICE_NOT_FOUND; goto fail_lookup; @@ -422,20 +417,19 @@ static void ctl_thread_cmd_pcm_open(struct ba_ctl *ctl, struct ba_request *req, final: pthread_mutex_unlock(&t->mutex); fail_lookup: - bluealsa_devpool_mutex_unlock(); + pthread_mutex_unlock(&ctl->a->devices_mutex); send(fd, &status, sizeof(status), MSG_NOSIGNAL); } static void ctl_thread_cmd_pcm_control(struct ba_ctl *ctl, struct ba_request *req, int fd) { - (void)ctl; struct ba_msg_status status = { BA_STATUS_CODE_SUCCESS }; struct ba_transport *t; struct ba_pcm *t_pcm; - bluealsa_devpool_mutex_lock(); + pthread_mutex_lock(&ctl->a->devices_mutex); - switch (ctl_lookup_transport(config.devices, &req->addr, req->type, &t)) { + switch (ctl_lookup_transport(ctl->a, &req->addr, req->type, &t)) { case -1: status.code = BA_STATUS_CODE_DEVICE_NOT_FOUND; goto fail; @@ -468,19 +462,18 @@ static void ctl_thread_cmd_pcm_control(struct ba_ctl *ctl, struct ba_request *re } fail: - bluealsa_devpool_mutex_unlock(); + pthread_mutex_unlock(&ctl->a->devices_mutex); send(fd, &status, sizeof(status), MSG_NOSIGNAL); } static void ctl_thread_cmd_rfcomm_send(struct ba_ctl *ctl, struct ba_request *req, int fd) { - (void)ctl; struct ba_msg_status status = { BA_STATUS_CODE_SUCCESS }; struct ba_transport *t; - bluealsa_devpool_mutex_lock(); + pthread_mutex_lock(&ctl->a->devices_mutex); - switch (ctl_lookup_transport(config.devices, &req->addr, BA_PCM_TYPE_RFCOMM, &t)) { + switch (ctl_lookup_transport(ctl->a, &req->addr, BA_PCM_TYPE_RFCOMM, &t)) { case -1: status.code = BA_STATUS_CODE_DEVICE_NOT_FOUND; goto fail; @@ -492,7 +485,7 @@ static void ctl_thread_cmd_rfcomm_send(struct ba_ctl *ctl, struct ba_request *re transport_send_rfcomm(t, req->rfcomm_command); fail: - bluealsa_devpool_mutex_unlock(); + pthread_mutex_unlock(&ctl->a->devices_mutex); send(fd, &status, sizeof(status), MSG_NOSIGNAL); } @@ -514,7 +507,7 @@ static void *ctl_thread(void *arg) { [BA_COMMAND_RFCOMM_SEND] = ctl_thread_cmd_rfcomm_send, }; - debug("Starting controller loop: %s", ctl->hci); + debug("Starting controller loop: %s", ctl->a->hci_name); for (;;) { if (poll((struct pollfd *)ctl->pfds->data, ctl->pfds->len, -1) == -1) { @@ -546,10 +539,10 @@ static void *ctl_thread(void *arg) { struct ba_device *d; struct ba_transport *t; - bluealsa_devpool_mutex_lock(); + pthread_mutex_lock(&ctl->a->devices_mutex); /* release PCMs associated with disconnected client */ - g_hash_table_iter_init(&iter_d, config.devices); + g_hash_table_iter_init(&iter_d, ctl->a->devices); while (g_hash_table_iter_next(&iter_d, NULL, (gpointer)&d)) { g_hash_table_iter_init(&iter_t, d->transports); while (g_hash_table_iter_next(&iter_t, NULL, (gpointer)&t)) { @@ -572,7 +565,7 @@ static void *ctl_thread(void *arg) { } } - bluealsa_devpool_mutex_unlock(); + pthread_mutex_unlock(&ctl->a->devices_mutex); g_array_remove_index_fast(ctl->pfds, i); g_array_remove_index_fast(ctl->subs, i - __CTL_PFDS_IDX_MAX); @@ -636,11 +629,11 @@ static void *ctl_thread(void *arg) { debug("+-+-"); } - debug("Exiting controller: %s", ctl->hci); + debug("Exiting controller: %s", ctl->a->hci_name); return NULL; } -struct ba_ctl *bluealsa_ctl_init(const char *hci) { +struct ba_ctl *bluealsa_ctl_init(struct ba_adapter *adapter) { struct pollfd pfd = { .events = POLLIN }; struct ba_ctl *ctl; @@ -651,8 +644,7 @@ struct ba_ctl *bluealsa_ctl_init(const char *hci) { ctl->socket_created = false; ctl->thread_created = false; - strncpy(ctl->hci, hci, sizeof(ctl->hci)); - ctl->hci[sizeof(ctl->hci) - 1] = '\0'; + ctl->a = adapter; ctl->evt[0] = -1; ctl->evt[1] = -1; @@ -666,7 +658,7 @@ struct ba_ctl *bluealsa_ctl_init(const char *hci) { struct sockaddr_un saddr = { .sun_family = AF_UNIX }; snprintf(saddr.sun_path, sizeof(saddr.sun_path) - 1, - BLUEALSA_RUN_STATE_DIR "/%s", ctl->hci); + BLUEALSA_RUN_STATE_DIR "/%s", ctl->a->hci_name); if (mkdir(BLUEALSA_RUN_STATE_DIR, 0755) == -1 && errno != EEXIST) { error("Couldn't create run-state directory: %s", strerror(errno)); @@ -708,7 +700,7 @@ struct ba_ctl *bluealsa_ctl_init(const char *hci) { /* set thread name (for easier debugging) */ char name[16] = "ba-ctl-"; - pthread_setname_np(ctl->thread, strcat(name, ctl->hci)); + pthread_setname_np(ctl->thread, strcat(name, ctl->a->hci_name)); return ctl; @@ -735,7 +727,7 @@ void bluealsa_ctl_free(struct ba_ctl *ctl) { if (ctl->socket_created) { char tmp[256] = BLUEALSA_RUN_STATE_DIR "/"; - unlink(strcat(tmp, ctl->hci)); + unlink(strcat(tmp, ctl->a->hci_name)); ctl->socket_created = false; } diff --git a/src/ctl.h b/src/ctl.h index 64f14cdf0..af84aaba1 100644 --- a/src/ctl.h +++ b/src/ctl.h @@ -19,6 +19,7 @@ #include +#include "ba-adapter.h" #include "shared/ctl-proto.h" struct ba_ctl { @@ -27,8 +28,8 @@ struct ba_ctl { bool socket_created; bool thread_created; - /* controller's HCI name */ - char hci[8]; + /* associated BT adapter */ + struct ba_adapter *a; /* special file descriptors + connected clients */ GArray *pfds; @@ -40,7 +41,7 @@ struct ba_ctl { }; -struct ba_ctl *bluealsa_ctl_init(const char *hci); +struct ba_ctl *bluealsa_ctl_init(struct ba_adapter *adapter); void bluealsa_ctl_free(struct ba_ctl *ctl); int bluealsa_ctl_send_event( diff --git a/src/main.c b/src/main.c index bb36a8d08..c742acc12 100644 --- a/src/main.c +++ b/src/main.c @@ -29,7 +29,7 @@ # include #endif -#include "ba-transport.h" +#include "ba-adapter.h" #include "bluealsa.h" #include "bluez.h" #include "ctl.h" @@ -311,7 +311,8 @@ int main(int argc, char **argv) { /* initialize random number generator */ srandom(time(NULL)); - if ((config.ctl = bluealsa_ctl_init(config.hci_dev.name)) == NULL) + struct ba_adapter *a = ba_adapter_new(config.hci_dev.dev_id, NULL); + if ((config.ctl = bluealsa_ctl_init(a)) == NULL) return EXIT_FAILURE; gchar *address; diff --git a/src/ofono.c b/src/ofono.c index 5e55ce4ac..9704f3f98 100644 --- a/src/ofono.c +++ b/src/ofono.c @@ -21,6 +21,9 @@ #include #include +#include +#include +#include #include #include "ba-transport.h" @@ -29,8 +32,15 @@ #include "ofono-iface.h" #include "shared/log.h" -#define OFONO_FAKE_DEV_ID 0xffff +/** + * Lookup data associated with oFono card. */ +struct ofono_card_data { + int hci_dev_id; + bdaddr_t bt_addr; + char transport_path[32]; +}; +static GHashTable *ofono_card_data_map = NULL; static unsigned int dbus_agent_object_id = 0; /** @@ -154,13 +164,15 @@ static struct ba_transport *ofono_transport_new( static void ofono_card_add(const char *dbus_sender, const char *card, GVariantIter *properties) { + struct ba_adapter *a = NULL; + struct ba_device *d = NULL; + struct ba_transport *t = NULL; + const char *key = NULL; GVariant *value = NULL; - gchar *path = g_strdup_printf("/ofono%s", card); - bool devpool_mutex_locked = false; - struct ba_transport *t; - struct ba_device *d; - bdaddr_t addr; + bdaddr_t addr_dev = { 0 }; + bdaddr_t addr_hci = { 0 }; + int hci_dev_id = -1; struct ba_transport_type ttype = { .profile = BA_TRANSPORT_PROFILE_HFP_HF, @@ -169,7 +181,11 @@ static void ofono_card_add(const char *dbus_sender, const char *card, while (g_variant_iter_next(properties, "{&sv}", &key, &value)) { if (strcmp(key, "RemoteAddress") == 0) - str2ba(g_variant_get_string(value, NULL), &addr); + str2ba(g_variant_get_string(value, NULL), &addr_dev); + else if (strcmp(key, "LocalAddress") == 0) { + str2ba(g_variant_get_string(value, NULL), &addr_hci); + hci_dev_id = hci_get_route(&addr_hci); + } else if (strcmp(key, "Type") == 0) { const char *type = g_variant_get_string(value, NULL); if (strcmp(type, OFONO_AUDIO_CARD_TYPE_AG) == 0) @@ -187,32 +203,44 @@ static void ofono_card_add(const char *dbus_sender, const char *card, debug("Adding new oFono card: %s", card); - bluealsa_devpool_mutex_lock(); - devpool_mutex_locked = true; + if ((a = ba_adapter_lookup(hci_dev_id)) == NULL && + (a = ba_adapter_new(hci_dev_id, NULL)) == NULL) { + error("Couldn't create new adapter: %s", strerror(errno)); + goto fail; + } + + pthread_mutex_lock(&a->devices_mutex); char name[sizeof(d->name)]; - ba2str(&addr, name); + ba2str(&addr_dev, name); - if ((d = ba_device_new(OFONO_FAKE_DEV_ID, &addr, name)) == NULL) { - error("Couldn't create device: %s", strerror(errno)); + if ((d = ba_device_lookup(a, &addr_dev)) == NULL && + (d = ba_device_new(a, &addr_dev, name)) == NULL) { + error("Couldn't create new device: %s", strerror(errno)); goto fail; } - bluealsa_device_insert(card, d); + struct ofono_card_data ocd = { + .hci_dev_id = hci_dev_id, + .bt_addr = addr_dev, + }; - if ((t = ofono_transport_new(d, ttype, dbus_sender, path)) == NULL) { + snprintf(ocd.transport_path, sizeof(ocd.transport_path), "/ofono%s", card); + if ((t = ofono_transport_new(d, ttype, dbus_sender, ocd.transport_path)) == NULL) { error("Couldn't create new transport: %s", strerror(errno)); goto fail; } + g_hash_table_insert(ofono_card_data_map, g_strdup(card), + g_memdup(&ocd, sizeof(ocd))); + transport_set_state(t, TRANSPORT_ACTIVE); fail: - if (devpool_mutex_locked) - bluealsa_devpool_mutex_unlock(); + if (a != NULL) + pthread_mutex_unlock(&a->devices_mutex); if (value != NULL) g_variant_unref(value); - g_free(path); } /** @@ -270,18 +298,30 @@ static int ofono_get_all_cards(void) { static void ofono_remove_all_cards(void) { GHashTableIter iter; - struct ba_device *d; + struct ofono_card_data *ocd; + + g_hash_table_iter_init(&iter, ofono_card_data_map); + while (g_hash_table_iter_next(&iter, NULL, (gpointer)&ocd)) { - bluealsa_devpool_mutex_lock(); + struct ba_adapter *a; + struct ba_device *d; + struct ba_transport *t; - g_hash_table_iter_init(&iter, config.devices); - while (g_hash_table_iter_next(&iter, NULL, (gpointer)&d)) { - if (d->hci_dev_id != OFONO_FAKE_DEV_ID) - continue; - g_hash_table_iter_remove(&iter); + if ((a = ba_adapter_lookup(ocd->hci_dev_id)) == NULL) + goto fail; + pthread_mutex_lock(&a->devices_mutex); + if ((d = ba_device_lookup(a, &ocd->bt_addr)) == NULL) + goto fail; + if ((t = ba_transport_lookup(d, ocd->transport_path)) == NULL) + goto fail; + + ba_transport_free(t); + +fail: + if (a != NULL) + pthread_mutex_unlock(&a->devices_mutex); } - bluealsa_devpool_mutex_unlock(); } static void ofono_agent_new_connection(GDBusMethodInvocation *inv, void *userdata) { @@ -289,10 +329,12 @@ static void ofono_agent_new_connection(GDBusMethodInvocation *inv, void *userdat GDBusMessage *msg = g_dbus_method_invocation_get_message(inv); GVariant *params = g_dbus_method_invocation_get_parameters(inv); - bool devpool_mutex_locked = false; - struct ba_transport *t; + + struct ba_adapter *a = NULL; + struct ba_device *d = NULL; + struct ba_transport *t = NULL; + GError *err = NULL; - gchar *path = NULL; GUnixFDList *fd_list; const char *card; uint8_t codec; @@ -306,15 +348,20 @@ static void ofono_agent_new_connection(GDBusMethodInvocation *inv, void *userdat goto fail; } - bluealsa_devpool_mutex_lock(); - devpool_mutex_locked = true; - - path = g_strdup_printf("/ofono%s", card); - if ((t = transport_lookup(config.devices, path)) == NULL) { - error("Transport not available: %s", path); + struct ofono_card_data *ocd; + if ((ocd = g_hash_table_lookup(ofono_card_data_map, card)) == NULL) { + error("Card data not available: %s", card); goto fail; } + if ((a = ba_adapter_lookup(ocd->hci_dev_id)) == NULL) + goto fail; + pthread_mutex_lock(&a->devices_mutex); + if ((d = ba_device_lookup(a, &ocd->bt_addr)) == NULL) + goto fail; + if ((t = ba_transport_lookup(d, ocd->transport_path)) == NULL) + goto fail; + if (ofono_sco_socket_authorize(fd) == -1) { error("Couldn't authorize SCO connection: %s", strerror(errno)); goto fail; @@ -338,12 +385,12 @@ static void ofono_agent_new_connection(GDBusMethodInvocation *inv, void *userdat fail: g_dbus_method_invocation_return_error(inv, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Unable to get connection"); + if (fd != -1) + close(fd); final: - if (devpool_mutex_locked) - bluealsa_devpool_mutex_unlock(); - if (path != NULL) - g_free(path); + if (a != NULL) + pthread_mutex_unlock(&a->devices_mutex); if (err != NULL) g_error_free(err); } @@ -400,7 +447,10 @@ int ofono_register(void) { if (!config.enable.hfp_ofono) goto final; - debug("Registering oFono audio agent"); + if (ofono_card_data_map == NULL) + ofono_card_data_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + debug("Registering oFono audio agent: %s", path); if ((dbus_agent_object_id = g_dbus_connection_register_object(conn, path, (GDBusInterfaceInfo *)&ofono_iface_hf_audio_agent, &ofono_vtable, NULL, NULL, &err)) == 0) @@ -458,14 +508,14 @@ static void ofono_signal_card_added(GDBusConnection *conn, const gchar *sender, (void)userdata; const gchar *signature = g_variant_get_type_string(params); - const char *card = NULL; - GVariantIter *properties = NULL; - if (strcmp(signature, "(oa{sv})") != 0) { error("Invalid signature for %s: %s != %s", signal, signature, "(oa{sv})"); return; } + const char *card = NULL; + GVariantIter *properties = NULL; + g_variant_get(params, "(&oa{sv})", &card, &properties); ofono_card_add(sender, card, properties); @@ -484,21 +534,39 @@ static void ofono_signal_card_removed(GDBusConnection *conn, const gchar *sender (void)userdata; const gchar *signature = g_variant_get_type_string(params); - const char *card = NULL; - if (strcmp(signature, "(o)") != 0) { error("Invalid signature for %s: %s != %s", signal, signature, "(o)"); return; } + struct ba_adapter *a = NULL; + struct ba_device *d = NULL; + struct ba_transport *t = NULL; + + const char *card = NULL; g_variant_get(params, "(&o)", &card); + struct ofono_card_data *ocd; + if ((ocd = g_hash_table_lookup(ofono_card_data_map, card)) == NULL) { + error("Card data not available: %s", card); + goto fail; + } + + if ((a = ba_adapter_lookup(ocd->hci_dev_id)) == NULL) + goto fail; + pthread_mutex_lock(&a->devices_mutex); + if ((d = ba_device_lookup(a, &ocd->bt_addr)) == NULL) + goto fail; + if ((t = ba_transport_lookup(d, ocd->transport_path)) == NULL) + goto fail; + debug("Removing oFono card: %s", card); - bluealsa_devpool_mutex_lock(); - bluealsa_device_remove(card); - bluealsa_devpool_mutex_unlock(); + ba_transport_free(t); +fail: + if (a != NULL) + pthread_mutex_unlock(&a->devices_mutex); } /** diff --git a/src/utils.c b/src/utils.c index 48686f41f..92bd398f6 100644 --- a/src/utils.c +++ b/src/utils.c @@ -138,6 +138,18 @@ int hci_open_sco(const struct hci_dev_info *di, const bdaddr_t *ba, bool transpa return -1; } +/** + * Extract HCI device ID from the BlueZ D-Bus object path. + * + * @param path BlueZ D-Bus object path. + * @return On success this function returns ID of the HCI device. + * Otherwise, -1 is returned. */ +int g_dbus_bluez_object_path_to_hci_dev_id(const char *path) { + if ((path = strstr(path, "/hci")) == NULL || path[3] == '\0') + return -1; + return atoi(&path[3]); +} + /** * Extract BT address from the BlueZ D-Bus object path. * diff --git a/src/utils.h b/src/utils.h index bd1fe4613..7bc1b242b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -30,6 +30,7 @@ int hci_devlist(struct hci_dev_info **di, int *num); int hci_open_sco(const struct hci_dev_info *di, const bdaddr_t *ba, bool transparent); const char *batostr_(const bdaddr_t *ba); +int g_dbus_bluez_object_path_to_hci_dev_id(const char *path); bdaddr_t *g_dbus_bluez_object_path_to_bdaddr(const char *path, bdaddr_t *addr); struct ba_transport_type g_dbus_bluez_object_path_to_transport_type(const char *path); const char *g_dbus_transport_type_to_bluez_object_path(struct ba_transport_type type); diff --git a/test/server-mock.c b/test/server-mock.c index 12da2ee08..038c2315d 100644 --- a/test/server-mock.c +++ b/test/server-mock.c @@ -26,6 +26,7 @@ #include "../src/bluealsa.c" #include "../src/at.c" +#include "../src/ba-adapter.c" #include "../src/ba-device.c" #include "../src/ba-transport.c" #include "../src/ctl.c" @@ -203,11 +204,11 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - /* emulate dummy test HCI device */ - strncpy(config.hci_dev.name, device, sizeof(config.hci_dev.name) - 1); - assert(bluealsa_config_init() == 0); - assert((config.ctl = bluealsa_ctl_init(device)) != NULL); + + /* emulate dummy test HCI device */ + struct ba_adapter *a = ba_adapter_new(0, device); + assert((config.ctl = bluealsa_ctl_init(a)) != NULL); /* make sure to cleanup named pipes */ struct sigaction sigact = { .sa_handler = test_pcm_setup_free_handler }; @@ -231,12 +232,10 @@ int main(int argc, char *argv[]) { * This test will ensure, that it is possible to launch mixer plug-in. */ str2ba("12:34:56:78:9A:BC", &addr); - assert((d1 = ba_device_new(1, &addr, "Test Device With Long Name")) != NULL); - bluealsa_device_insert("/device/1", d1); + assert((d1 = ba_device_new(a, &addr, "Test Device With Long Name")) != NULL); str2ba("12:34:56:9A:BC:DE", &addr); - assert((d2 = ba_device_new(1, &addr, "Test Device With Long Name")) != NULL); - bluealsa_device_insert("/device/2", d2); + assert((d2 = ba_device_new(a, &addr, "Test Device With Long Name")) != NULL); if (source) { struct ba_transport_type ttype = { @@ -278,9 +277,9 @@ int main(int argc, char *argv[]) { timeout = sleep(timeout); if (fuzzing) { - bluealsa_device_remove("/device/1"); + ba_device_free(d1); sleep(1); - bluealsa_device_remove("/device/2"); + ba_device_free(d2); sleep(1); } diff --git a/test/test-ctl.c b/test/test-ctl.c index 540acec64..2cc16bacf 100644 --- a/test/test-ctl.c +++ b/test/test-ctl.c @@ -71,21 +71,21 @@ START_TEST(test_get_devices) { struct ba_msg_device *devices; ck_assert_int_eq(bluealsa_get_devices(fd, &devices), 2); - ck_assert_int_eq(bacmp(&devices[0].addr, &addr0), 0); + ck_assert_int_eq(bacmp(&devices[0].addr, &addr1), 0); ck_assert_str_eq(devices[0].name, "Test Device With Long Name"); - ck_assert_int_eq(bacmp(&devices[1].addr, &addr1), 0); + ck_assert_int_eq(bacmp(&devices[1].addr, &addr0), 0); ck_assert_str_eq(devices[1].name, "Test Device With Long Name"); struct ba_msg_transport *transports; ck_assert_int_eq(bluealsa_get_transports(fd, &transports), 4); - ck_assert_int_eq(bacmp(&transports[0].addr, &addr0), 0); + ck_assert_int_eq(bacmp(&transports[0].addr, &addr1), 0); ck_assert_int_eq(transports[0].type, BA_PCM_TYPE_A2DP | BA_PCM_STREAM_PLAYBACK); - ck_assert_int_eq(bacmp(&transports[1].addr, &addr0), 0); + ck_assert_int_eq(bacmp(&transports[1].addr, &addr1), 0); ck_assert_int_eq(transports[1].type, BA_PCM_TYPE_A2DP | BA_PCM_STREAM_CAPTURE); - ck_assert_int_eq(bacmp(&transports[2].addr, &addr1), 0); + ck_assert_int_eq(bacmp(&transports[2].addr, &addr0), 0); ck_assert_int_eq(transports[2].type, BA_PCM_TYPE_A2DP | BA_PCM_STREAM_PLAYBACK); - ck_assert_int_eq(bacmp(&transports[3].addr, &addr1), 0); + ck_assert_int_eq(bacmp(&transports[3].addr, &addr0), 0); ck_assert_int_eq(transports[3].type, BA_PCM_TYPE_A2DP | BA_PCM_STREAM_CAPTURE); ck_assert_int_eq(transports[0].codec, 0);