From 89542289ac1f697ebee891f2d8b5b3c30757279a Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Mon, 14 Nov 2022 01:49:12 +0200 Subject: [PATCH] bluez5: use GDBus in midi-server --- spa/plugins/bluez5/midi-server.c | 736 ++++++++++++++----------------- spa/plugins/bluez5/midi.h | 6 +- 2 files changed, 324 insertions(+), 418 deletions(-) diff --git a/spa/plugins/bluez5/midi-server.c b/spa/plugins/bluez5/midi-server.c index 94abcb717..a1b26825c 100644 --- a/spa/plugins/bluez5/midi-server.c +++ b/spa/plugins/bluez5/midi-server.c @@ -26,23 +26,22 @@ #include #include -#include - #include #include #include -#include "dbus-manager.h" -#include "dbus-monitor.h" #include "midi.h" +#include "bluez5-interface-gen.h" +#include "dbus-monitor.h" + #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT (&impl->log_topic) -#define MIDI_SERVER_PATH "/midiserver" -#define MIDI_SERVICE_PATH "/midiserver/service" -#define MIDI_CHR_PATH "/midiserver/service/chr" -#define MIDI_DSC_PATH "/midiserver/service/chr/dsc" +#define MIDI_SERVER_PATH "/midiserver%u" +#define MIDI_SERVICE_PATH "/midiserver%u/service" +#define MIDI_CHR_PATH "/midiserver%u/service/chr" +#define MIDI_DSC_PATH "/midiserver%u/service/chr/dsc" #define BLE_DEFAULT_MTU 23 @@ -55,55 +54,48 @@ struct impl const struct spa_bt_midi_server_cb *cb; - struct spa_dbus_local_object *service; - struct spa_dbus_local_object *chr; - struct spa_dbus_local_object *dsc; + GDBusConnection *conn; + struct dbus_monitor monitor; + GDBusObjectManagerServer *manager; + + Bluez5GattCharacteristic1 *chr; - struct spa_dbus_monitor *dbus_monitor; - struct spa_dbus_object_manager *objects; - DBusConnection *conn; void *user_data; -}; -struct adapter -{ - struct spa_dbus_object object; - struct spa_dbus_async_call register_call; - unsigned int registered:1; -}; + uint32_t server_id; -struct chr { - struct spa_dbus_local_object object; unsigned int write_acquired:1; unsigned int notify_acquired:1; }; -struct dsc { - struct spa_dbus_local_object object; +struct _MidiServerManagerProxy +{ + Bluez5GattManager1Proxy parent_instance; + + GCancellable *register_call; + unsigned int registered:1; }; +G_DECLARE_FINAL_TYPE(MidiServerManagerProxy, midi_server_manager_proxy, MIDI_SERVER, + MANAGER_PROXY, Bluez5GattManager1Proxy) +G_DEFINE_TYPE(MidiServerManagerProxy, midi_server_manager_proxy, BLUEZ5_TYPE_GATT_MANAGER1_PROXY) +#define MIDI_SERVER_TYPE_MANAGER_PROXY (midi_server_manager_proxy_get_type()) + /* * Characteristic user descriptor: not in BLE MIDI standard, but we * put a device name here in case we have multiple MIDI endpoints. */ -static DBusMessage *parse_options(struct impl *impl, DBusMessage *m, const char *out_key, uint16_t *out_value); - -static DBusMessage *dsc_read_value(struct spa_dbus_local_object *object, DBusMessage *m) +static gboolean dsc_handle_read_value(Bluez5GattDescriptor1 *iface, GDBusMethodInvocation *invocation, + GVariant *arg_options, gpointer user_data) { - struct dsc *dsc = SPA_CONTAINER_OF(object, struct dsc, object); - struct impl *impl = dsc->object.user_data; - DBusMessage *r; - DBusMessageIter i, a; + struct impl *impl = user_data; const char *description = NULL; - const uint8_t *ptr; uint16_t offset = 0; int len; - r = parse_options(impl, m, "offset", &offset); - if (r) - return r; + g_variant_lookup(arg_options, "offset", "q", &offset); if (impl->cb->get_description) description = impl->cb->get_description(impl->user_data); @@ -111,161 +103,79 @@ static DBusMessage *dsc_read_value(struct spa_dbus_local_object *object, DBusMes description = ""; len = strlen(description); - if (offset > len) - return dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, + if (offset > len) { + g_dbus_method_invocation_return_dbus_error(invocation, + "org.freedesktop.DBus.Error.InvalidArgs", "Invalid arguments"); + return TRUE; + } - r = dbus_message_new_method_return(m); - if (r == NULL) - return NULL; - - ptr = SPA_PTROFF(description, offset, const uint8_t); - len -= offset; - - dbus_message_iter_init_append(r, &i); - dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, "y", &a); - dbus_message_iter_append_fixed_array(&a, DBUS_TYPE_BYTE, &ptr, len); - dbus_message_iter_close_container(&i, &a); - return r; + bluez5_gatt_descriptor1_complete_read_value(iface, + invocation, description + offset); + return TRUE; } -static int dsc_prop_uuid_get(struct spa_dbus_local_object *object, DBusMessageIter *value) +static int export_dsc(struct impl *impl) { - const char *uuid = BT_GATT_CHARACTERISTIC_USER_DESCRIPTION_UUID; - dbus_message_iter_append_basic(value, DBUS_TYPE_STRING, &uuid); - return 0; + static const char * const flags[] = { "encrypt-read", NULL }; + GDBusObjectSkeleton *skeleton = NULL; + Bluez5GattDescriptor1 *iface = NULL; + int res = -ENOMEM; + char path[128]; + + iface = bluez5_gatt_descriptor1_skeleton_new(); + if (!iface) + goto done; + + spa_scnprintf(path, sizeof(path), MIDI_DSC_PATH, impl->server_id); + skeleton = g_dbus_object_skeleton_new(path); + if (!skeleton) + goto done; + g_dbus_object_skeleton_add_interface(skeleton, G_DBUS_INTERFACE_SKELETON(iface)); + + bluez5_gatt_descriptor1_set_uuid(iface, BT_GATT_CHARACTERISTIC_USER_DESCRIPTION_UUID); + spa_scnprintf(path, sizeof(path), MIDI_CHR_PATH, impl->server_id); + bluez5_gatt_descriptor1_set_characteristic(iface, path); + bluez5_gatt_descriptor1_set_flags(iface, flags); + + g_signal_connect(iface, "handle-read-value", G_CALLBACK(dsc_handle_read_value), impl); + + g_dbus_object_manager_server_export(impl->manager, skeleton); + + spa_log_debug(impl->log, "MIDI GATT Descriptor exported, path=%s", + g_dbus_object_get_object_path(G_DBUS_OBJECT(skeleton))); + + res = 0; + +done: + g_clear_object(&iface); + g_clear_object(&skeleton); + return res; } -static int dsc_prop_characteristic_get(struct spa_dbus_local_object *object, DBusMessageIter *value) -{ - struct dsc *dsc = SPA_CONTAINER_OF(object, struct dsc, object); - struct impl *impl = dsc->object.user_data; - dbus_message_iter_append_basic(value, DBUS_TYPE_OBJECT_PATH, &impl->chr->path); - return 0; -} - -static int dsc_prop_flags_get(struct spa_dbus_local_object *object, DBusMessageIter *value) -{ - DBusMessageIter a; - static const char * const flags[] = { "encrypt-read" }; - - dbus_message_iter_open_container(value, DBUS_TYPE_ARRAY, "s", &a); - SPA_FOR_EACH_ELEMENT_VAR(flags, p) - dbus_message_iter_append_basic(&a, DBUS_TYPE_STRING, p); - dbus_message_iter_close_container(value, &a); - - return 0; -} - -static const struct spa_dbus_local_interface midi_dsc_interfaces[] = { - { - .name = BLUEZ_GATT_DSC_INTERFACE, - .methods = (struct spa_dbus_method[]) { - { - .name = "ReadValue", - .call = dsc_read_value, - }, - {NULL} - }, - .properties = (struct spa_dbus_property[]) { - { - .name = "UUID", - .signature = "s", - .get = dsc_prop_uuid_get, - }, - { - .name = "Characteristic", - .signature = "o", - .get = dsc_prop_characteristic_get, - }, - { - .name = "Flags", - .signature = "as", - .get = dsc_prop_flags_get, - }, - {NULL} - }, - }, - {NULL} -}; - /* * MIDI characteristic */ -#define CHR_IFACE 0 -#define CHR_PROP_UUID 0 -#define CHR_PROP_SERVICE 1 -#define CHR_PROP_WRITE_ACQUIRED 2 -#define CHR_PROP_NOTIFY_ACQUIRED 3 -#define CHR_PROP_FLAGS 4 - -static DBusMessage *chr_read_value(struct spa_dbus_local_object *object, DBusMessage *m) +static gboolean chr_handle_read_value(Bluez5GattCharacteristic1 *iface, + GDBusMethodInvocation *invocation, GVariant *arg_options, + gpointer user_data) { - DBusMessage *r; - DBusMessageIter i, a; - - r = dbus_message_new_method_return(m); - if (r == NULL) - return NULL; - - /* BLE MIDI-1.0: reading value returns an empty reply */ - dbus_message_iter_init_append(r, &i); - dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, "y", &a); - dbus_message_iter_close_container(&i, &a); - return r; + /* BLE MIDI-1.0: returns empty value */ + bluez5_gatt_characteristic1_complete_read_value(iface, invocation, ""); + return TRUE; } -static void chr_change_acquired(struct impl *impl, struct chr *chr, bool write, bool enabled) +static void chr_change_acquired(struct impl *impl, bool write, bool enabled) { - const struct spa_dbus_local_interface *iface = &chr->object.interfaces[CHR_IFACE]; - struct spa_dbus_property changed[2] = {0}; - if (write) { - if (chr->write_acquired != enabled) - changed[0] = iface->properties[CHR_PROP_WRITE_ACQUIRED]; - chr->write_acquired = enabled; + impl->write_acquired = enabled; + bluez5_gatt_characteristic1_set_write_acquired(impl->chr, enabled); } else { - if (chr->notify_acquired != enabled) - changed[0] = iface->properties[CHR_PROP_NOTIFY_ACQUIRED]; - chr->notify_acquired = enabled; + impl->notify_acquired = enabled; + bluez5_gatt_characteristic1_set_notify_acquired(impl->chr, enabled); } - - spa_dbus_object_manager_properties_changed(impl->objects, impl->chr, iface, changed); -} - -static DBusMessage *parse_options(struct impl *impl, DBusMessage *m, const char *out_key, uint16_t *out_value) -{ - DBusMessageIter args, options; - - if (!dbus_message_iter_init(m, &args) || !spa_streq(dbus_message_get_signature(m), "a{sv}")) - return dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, - "Invalid arguments"); - - dbus_message_iter_recurse(&args, &options); - if (dbus_message_iter_get_arg_type(&options) != DBUS_TYPE_DICT_ENTRY) - return dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, - "Invalid arguments"); - - while (dbus_message_iter_get_arg_type(&options) == DBUS_TYPE_DICT_ENTRY) { - const char *key; - int type; - DBusMessageIter value, entry; - - dbus_message_iter_recurse(&options, &entry); - dbus_message_iter_get_basic(&entry, &key); - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); - type = dbus_message_iter_get_arg_type(&value); - - if (spa_streq(key, out_key) && type == DBUS_TYPE_UINT16) - dbus_message_iter_get_basic(&value, out_value); - - dbus_message_iter_next(&options); - } - - return NULL; } static int create_socketpair(int fds[2]) @@ -275,31 +185,32 @@ static int create_socketpair(int fds[2]) return 0; } -static DBusMessage *chr_acquire(struct spa_dbus_local_object *object, DBusMessage *m, bool write) +static gboolean chr_handle_acquire(Bluez5GattCharacteristic1 *object, + GDBusMethodInvocation *invocation, + GUnixFDList *dummy, GVariant *arg_options, + bool write, gpointer user_data) { - struct chr *chr = SPA_CONTAINER_OF(object, struct chr, object); - struct impl *impl = chr->object.user_data; + struct impl *impl = user_data; const char *err_msg = "Failed"; uint16_t mtu = BLE_DEFAULT_MTU; - int fds[2] = {-1, -1}; + gint fds[2] = {-1, -1}; int res; - DBusMessage *r; - DBusMessageIter i; + GUnixFDList *fd_list = NULL; + GVariant *fd_handle = NULL; + GError *err = NULL; if ((write && (impl->cb->acquire_write == NULL)) || (!write && (impl->cb->acquire_notify == NULL))) { err_msg = "Not supported"; goto fail; } - if ((write && chr->write_acquired) || - (!write && chr->notify_acquired)) { + if ((write && impl->write_acquired) || + (!write && impl->notify_acquired)) { err_msg = "Already acquired"; goto fail; } - r = parse_options(impl, m, "mtu", &mtu); - if (r) - return r; + g_variant_lookup(arg_options, "mtu", "q", &mtu); if (create_socketpair(fds) < 0) { err_msg = "Socketpair creation failed"; @@ -316,297 +227,286 @@ static DBusMessage *chr_acquire(struct spa_dbus_local_object *object, DBusMessag } fds[0] = -1; - r = dbus_message_new_method_return(m); - if (r == NULL) - goto fail; - - dbus_message_iter_init_append(r, &i); - dbus_message_iter_append_basic(&i, DBUS_TYPE_UNIX_FD, &fds[1]); - dbus_message_iter_append_basic(&i, DBUS_TYPE_UINT16, &mtu); - - close(fds[1]); + fd_handle = g_variant_new_handle(0); + fd_list = g_unix_fd_list_new_from_array(&fds[1], 1); fds[1] = -1; - chr_change_acquired(impl, chr, write, true); + chr_change_acquired(impl, write, true); - return r; + if (write) { + bluez5_gatt_characteristic1_complete_acquire_write( + object, invocation, fd_list, fd_handle, mtu); + } else { + bluez5_gatt_characteristic1_complete_acquire_notify( + object, invocation, fd_list, fd_handle, mtu); + } + + g_clear_object(&fd_list); + return TRUE; fail: if (fds[0] >= 0) close(fds[0]); if (fds[1] >= 0) close(fds[1]); - return dbus_message_new_error(m, DBUS_ERROR_FAILED, err_msg); + + if (err) + g_error_free(err); + g_clear_pointer(&fd_handle, g_variant_unref); + g_clear_object(&fd_list); + g_dbus_method_invocation_return_dbus_error(invocation, + "org.freedesktop.DBus.Error.Failed", err_msg); + return TRUE; } -static DBusMessage *chr_acquire_write(struct spa_dbus_local_object *object, DBusMessage *m) +static gboolean chr_handle_acquire_write(Bluez5GattCharacteristic1 *object, + GDBusMethodInvocation *invocation, + GUnixFDList *fd_list, GVariant *arg_options, + gpointer user_data) { - return chr_acquire(object, m, true); + return chr_handle_acquire(object, invocation, fd_list, arg_options, true, user_data); } -static DBusMessage *chr_acquire_notify(struct spa_dbus_local_object *object, DBusMessage *m) +static gboolean chr_handle_acquire_notify(Bluez5GattCharacteristic1 *object, + GDBusMethodInvocation *invocation, + GUnixFDList *fd_list, GVariant *arg_options, + gpointer user_data) { - return chr_acquire(object, m, false); + return chr_handle_acquire(object, invocation, fd_list, arg_options, false, user_data); } -static int chr_prop_uuid_get(struct spa_dbus_local_object *object, DBusMessageIter *value) +static int export_chr(struct impl *impl) { - const char *uuid = BT_MIDI_CHR_UUID; - dbus_message_iter_append_basic(value, DBUS_TYPE_STRING, &uuid); - return 0; + static const char * const flags[] = { "encrypt-read", "write-without-response", + "encrypt-write", "encrypt-notify", NULL }; + GDBusObjectSkeleton *skeleton = NULL; + Bluez5GattCharacteristic1 *iface = NULL; + int res = -ENOMEM; + char path[128]; + + iface = bluez5_gatt_characteristic1_skeleton_new(); + if (!iface) + goto done; + + spa_scnprintf(path, sizeof(path), MIDI_CHR_PATH, impl->server_id); + skeleton = g_dbus_object_skeleton_new(path); + if (!skeleton) + goto done; + g_dbus_object_skeleton_add_interface(skeleton, G_DBUS_INTERFACE_SKELETON(iface)); + + bluez5_gatt_characteristic1_set_uuid(iface, BT_MIDI_CHR_UUID); + spa_scnprintf(path, sizeof(path), MIDI_SERVICE_PATH, impl->server_id); + bluez5_gatt_characteristic1_set_service(iface, path); + bluez5_gatt_characteristic1_set_write_acquired(iface, FALSE); + bluez5_gatt_characteristic1_set_notify_acquired(iface, FALSE); + bluez5_gatt_characteristic1_set_flags(iface, flags); + + g_signal_connect(iface, "handle-read-value", G_CALLBACK(chr_handle_read_value), impl); + g_signal_connect(iface, "handle-acquire-write", G_CALLBACK(chr_handle_acquire_write), impl); + g_signal_connect(iface, "handle-acquire-notify", G_CALLBACK(chr_handle_acquire_notify), impl); + + g_dbus_object_manager_server_export(impl->manager, skeleton); + + impl->chr = g_object_ref(iface); + + spa_log_debug(impl->log, "MIDI GATT Characteristic exported, path=%s", + g_dbus_object_get_object_path(G_DBUS_OBJECT(skeleton))); + + res = 0; + +done: + g_clear_object(&iface); + g_clear_object(&skeleton); + return res; } -static int chr_prop_service_get(struct spa_dbus_local_object *object, DBusMessageIter *value) -{ - const char *service = MIDI_SERVICE_PATH; - dbus_message_iter_append_basic(value, DBUS_TYPE_OBJECT_PATH, &service); - return 0; -} - -static int chr_prop_notify_acquired_get(struct spa_dbus_local_object *object, DBusMessageIter *value) -{ - struct chr *chr = SPA_CONTAINER_OF(object, struct chr, object); - dbus_bool_t v = chr->notify_acquired; - dbus_message_iter_append_basic(value, DBUS_TYPE_BOOLEAN, &v); - return 0; -} - -static int chr_prop_write_acquired_get(struct spa_dbus_local_object *object, DBusMessageIter *value) -{ - struct chr *chr = SPA_CONTAINER_OF(object, struct chr, object); - dbus_bool_t v = chr->write_acquired; - dbus_message_iter_append_basic(value, DBUS_TYPE_BOOLEAN, &v); - return 0; -} - -static int chr_prop_flags_get(struct spa_dbus_local_object *object, DBusMessageIter *value) -{ - DBusMessageIter a; - static const char * const flags[] = {"encrypt-read", "write-without-response", - "encrypt-write", "encrypt-notify" }; - - dbus_message_iter_open_container(value, DBUS_TYPE_ARRAY, "s", &a); - SPA_FOR_EACH_ELEMENT_VAR(flags, p) - dbus_message_iter_append_basic(&a, DBUS_TYPE_STRING, p); - dbus_message_iter_close_container(value, &a); - - return 0; -} - -static const struct spa_dbus_local_interface midi_chr_interfaces[] = { - [CHR_IFACE] = { - .name = BLUEZ_GATT_CHR_INTERFACE, - .methods = (struct spa_dbus_method[]) { - { - .name = "ReadValue", - .call = chr_read_value, - }, - { - .name = "AcquireWrite", - .call = chr_acquire_write, - }, - { - .name = "AcquireNotify", - .call = chr_acquire_notify, - }, - {NULL} - }, - .properties = (struct spa_dbus_property[]) { - [CHR_PROP_UUID] = { - .name = "UUID", - .signature = "s", - .get = chr_prop_uuid_get, - }, - [CHR_PROP_SERVICE] = { - .name = "Service", - .signature = "o", - .get = chr_prop_service_get, - }, - [CHR_PROP_WRITE_ACQUIRED] = { - .name = "WriteAcquired", - .signature = "b", - .get = chr_prop_write_acquired_get, - }, - [CHR_PROP_NOTIFY_ACQUIRED] = { - .name = "NotifyAcquired", - .signature = "b", - .get = chr_prop_notify_acquired_get, - }, - [CHR_PROP_FLAGS] = { - .name = "Flags", - .signature = "as", - .get = chr_prop_flags_get, - }, - {NULL} - }, - }, - {NULL} -}; /* * MIDI service */ -static int service_prop_uuid_get(struct spa_dbus_local_object *object, DBusMessageIter *value) +static int export_service(struct impl *impl) { - const char *uuid = BT_MIDI_SERVICE_UUID; - dbus_message_iter_append_basic(value, DBUS_TYPE_STRING, &uuid); - return 0; + GDBusObjectSkeleton *skeleton = NULL; + Bluez5GattService1 *iface = NULL; + int res = -ENOMEM; + char path[128]; + + iface = bluez5_gatt_service1_skeleton_new(); + if (!iface) + goto done; + + spa_scnprintf(path, sizeof(path), MIDI_SERVICE_PATH, impl->server_id); + skeleton = g_dbus_object_skeleton_new(path); + if (!skeleton) + goto done; + g_dbus_object_skeleton_add_interface(skeleton, G_DBUS_INTERFACE_SKELETON(iface)); + + bluez5_gatt_service1_set_uuid(iface, BT_MIDI_SERVICE_UUID); + bluez5_gatt_service1_set_primary(iface, TRUE); + + g_dbus_object_manager_server_export(impl->manager, skeleton); + + spa_log_debug(impl->log, "MIDI GATT Service exported, path=%s", + g_dbus_object_get_object_path(G_DBUS_OBJECT(skeleton))); + + res = 0; + +done: + g_clear_object(&iface); + g_clear_object(&skeleton); + return res; } -static int service_prop_primary_get(struct spa_dbus_local_object *object, DBusMessageIter *value) -{ - dbus_bool_t primary = TRUE; - dbus_message_iter_append_basic(value, DBUS_TYPE_BOOLEAN, &primary); - return 0; -} - -static const struct spa_dbus_local_interface midi_service_interfaces[] = { - { - .name = BLUEZ_GATT_SERVICE_INTERFACE, - .properties = (struct spa_dbus_property[]) { - { - .name = "UUID", - .signature = "s", - .get = service_prop_uuid_get, - }, - { - .name = "Primary", - .signature = "b", - .get = service_prop_primary_get, - }, - {NULL}, - }, - }, - {NULL} -}; /* - * Adapters + * Registration on all GattManagers */ -static void adapter_register_application_reply(struct spa_dbus_async_call *call, DBusMessage *r) +static void manager_register_application_reply(GObject *source_object, GAsyncResult *res, gpointer user_data) { - struct adapter *adapter = SPA_CONTAINER_OF(call, struct adapter, register_call); - struct impl *impl = adapter->object.user_data; + MidiServerManagerProxy *manager = MIDI_SERVER_MANAGER_PROXY(source_object); + struct impl *impl = user_data; + GError *err = NULL; - if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + bluez5_gatt_manager1_call_register_application_finish( + BLUEZ5_GATT_MANAGER1(source_object), res, &err); + + if (g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + /* Operation canceled: user_data may be invalid by now */ + g_error_free(err); + goto done; + } + if (err) { spa_log_error(impl->log, "%s.RegisterApplication() failed: %s", BLUEZ_GATT_MANAGER_INTERFACE, - dbus_message_get_error_name(r)); - return; + err->message); + g_error_free(err); + goto done; } - adapter->registered = true; + manager->registered = true; + +done: + g_clear_object(&manager->register_call); } -static int adapter_register_application(struct adapter *adapter) +static int manager_register_application(struct impl *impl, MidiServerManagerProxy *manager) { - struct impl *impl = adapter->object.user_data; - DBusMessageIter i, d; - DBusMessage *m; + GVariantBuilder builder; + GVariant *options; - m = dbus_message_new_method_call(BLUEZ_SERVICE, - adapter->object.path, + if (manager->registered) + return 0; + if (manager->register_call) + return -EBUSY; + + spa_log_debug(impl->log, "%s.RegisterApplication(%s) on %s", BLUEZ_GATT_MANAGER_INTERFACE, - "RegisterApplication"); - if (m == NULL) - return -ENOMEM; + g_dbus_object_manager_get_object_path(G_DBUS_OBJECT_MANAGER(impl->manager)), + g_dbus_proxy_get_object_path(G_DBUS_PROXY(manager))); - dbus_message_iter_init_append(m, &i); - dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &impl->objects->path); - dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, "{sv}", &d); - dbus_message_iter_close_container(&i, &d); + manager->register_call = g_cancellable_new(); + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + options = g_variant_builder_end(&builder); + bluez5_gatt_manager1_call_register_application(BLUEZ5_GATT_MANAGER1(manager), + g_dbus_object_manager_get_object_path(G_DBUS_OBJECT_MANAGER(impl->manager)), + options, + manager->register_call, + manager_register_application_reply, + impl); - return spa_dbus_async_call_send(&adapter->register_call, impl->conn, m, - adapter_register_application_reply); + return 0; } -static void adapter_update(struct spa_dbus_object *object) +static void midi_server_manager_proxy_init(MidiServerManagerProxy *manager) { - struct adapter *adapter = SPA_CONTAINER_OF(object, struct adapter, object); - - if (adapter->registered || adapter->register_call.pending) - return; - - adapter_register_application(adapter); } -static void adapter_remove(struct spa_dbus_object *object) +static void midi_server_manager_proxy_finalize(GObject *object) { - struct adapter *adapter = SPA_CONTAINER_OF(object, struct adapter, object); + MidiServerManagerProxy *manager = MIDI_SERVER_MANAGER_PROXY(object); - spa_dbus_async_call_cancel(&adapter->register_call); + g_cancellable_cancel(manager->register_call); + g_clear_object(&manager->register_call); } -static void bluez_remove(struct spa_dbus_object *object) +static void midi_server_manager_proxy_class_init(MidiServerManagerProxyClass *klass) { - struct impl *impl = object->user_data; - struct chr *chr = (struct chr *)impl->chr; + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->finalize = midi_server_manager_proxy_finalize; +} + +static void manager_update(struct dbus_monitor *monitor, GDBusInterface *iface) +{ + struct impl *impl = SPA_CONTAINER_OF(monitor, struct impl, monitor); + + manager_register_application(impl, MIDI_SERVER_MANAGER_PROXY(iface)); +} + +static void manager_clear(struct dbus_monitor *monitor, GDBusInterface *iface) +{ + midi_server_manager_proxy_finalize(G_OBJECT(iface)); +} + +static void on_name_owner_change(struct dbus_monitor *monitor) +{ + struct impl *impl = SPA_CONTAINER_OF(monitor, struct impl, monitor); /* - * BlueZ disappeared. It does not appear to close the sockets it has - * acquired in this case, so we should force the chr release. + * BlueZ disappeared/appeared. It does not appear to close the sockets + * it quits, so we should force the chr release now. */ if (impl->cb->release) impl->cb->release(impl->user_data); - chr_change_acquired(impl, chr, true, false); - chr_change_acquired(impl, chr, false, false); + chr_change_acquired(impl, true, false); + chr_change_acquired(impl, false, false); } -static const struct spa_dbus_interface monitor_interfaces[] = { - { - .name = BLUEZ_ADAPTER_INTERFACE, - .update = adapter_update, - .remove = adapter_remove, - .object_size = sizeof(struct adapter), - }, - { - .name = SPA_DBUS_MONITOR_NAME_OWNER_INTERFACE, - .remove = bluez_remove, - .object_size = sizeof(struct spa_dbus_object), - }, - {NULL} -}; +static void monitor_start(struct impl *impl) +{ + struct dbus_monitor_proxy_type proxy_types[] = { + { BLUEZ_GATT_MANAGER_INTERFACE, MIDI_SERVER_TYPE_MANAGER_PROXY, manager_update, manager_clear }, + { NULL, BLUEZ5_TYPE_OBJECT_PROXY, NULL, NULL }, + { NULL, G_TYPE_INVALID, NULL, NULL }, + }; -static int register_objects(struct impl *impl) + SPA_STATIC_ASSERT(SPA_N_ELEMENTS(proxy_types) <= DBUS_MONITOR_MAX_TYPES); + + dbus_monitor_init(&impl->monitor, BLUEZ5_TYPE_OBJECT_MANAGER_CLIENT, + impl->log, impl->conn, BLUEZ_SERVICE, "/", proxy_types, + on_name_owner_change); +} + + +/* + * Object registration + */ + +static int export_objects(struct impl *impl) { int res = 0; + char path[128]; - impl->objects = spa_dbus_object_manager_new(impl->conn, MIDI_SERVER_PATH, impl->log); - if (impl->objects == NULL) + spa_scnprintf(path, sizeof(path), MIDI_SERVER_PATH, impl->server_id); + impl->manager = g_dbus_object_manager_server_new(path); + if (!impl->manager){ + spa_log_error(impl->log, "Creating GDBus object manager failed"); + goto fail; + } + + if ((res = export_service(impl)) < 0) goto fail; - impl->service = spa_dbus_object_manager_register(impl->objects, - MIDI_SERVICE_PATH, - midi_service_interfaces, - sizeof(struct spa_dbus_local_object), - impl); - if (impl->service == NULL) + if ((res = export_chr(impl)) < 0) goto fail; - impl->chr = spa_dbus_object_manager_register(impl->objects, - MIDI_CHR_PATH, - midi_chr_interfaces, - sizeof(struct chr), - impl); - if (impl->chr == NULL) + if ((res = export_dsc(impl)) < 0) goto fail; - impl->dsc = spa_dbus_object_manager_register(impl->objects, - MIDI_DSC_PATH, - midi_dsc_interfaces, - sizeof(struct dsc), - impl); - if (impl->dsc == NULL) - goto fail; - - impl->dbus_monitor = spa_dbus_monitor_new(impl->conn, - BLUEZ_SERVICE, "/", monitor_interfaces, - impl->log, impl); - if (!impl->dbus_monitor) - goto fail; + g_dbus_object_manager_server_set_connection(impl->manager, impl->conn); return 0; @@ -616,37 +516,40 @@ fail: spa_log_error(impl->log, "Failed to register BLE MIDI services in DBus: %s", spa_strerror(res)); - if (impl->objects) - spa_dbus_object_manager_destroy(impl->objects); - if (impl->dbus_monitor) - spa_dbus_monitor_destroy(impl->dbus_monitor); + g_clear_object(&impl->manager); return res; } -struct spa_bt_midi_server *spa_bt_midi_server_new(DBusConnection *conn, const struct spa_bt_midi_server_cb *cb, - struct spa_log *log, void *user_data) +struct spa_bt_midi_server *spa_bt_midi_server_new(const struct spa_bt_midi_server_cb *cb, + GDBusConnection *conn, struct spa_log *log, void *user_data) { + static unsigned int server_id = 0; struct impl *impl; + char path[128]; int res = 0; impl = calloc(1, sizeof(struct impl)); if (impl == NULL) goto fail; + impl->server_id = server_id++; impl->user_data = user_data; - impl->conn = conn; impl->cb = cb; impl->log = log; impl->log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.midi.server"); + impl->conn = conn; spa_log_topic_init(impl->log, &impl->log_topic); - if ((res = register_objects(impl)) < 0) + if ((res = export_objects(impl)) < 0) goto fail; - impl->this.chr_path = impl->chr->path; + monitor_start(impl); - dbus_connection_ref(impl->conn); + g_object_ref(impl->conn); + + spa_scnprintf(path, sizeof(path), MIDI_CHR_PATH, impl->server_id); + impl->this.chr_path = strdup(path); return &impl->this; @@ -661,15 +564,18 @@ void spa_bt_midi_server_destroy(struct spa_bt_midi_server *server) { struct impl *impl = SPA_CONTAINER_OF(server, struct impl, this); - spa_dbus_object_manager_destroy(impl->objects); - dbus_connection_unref(impl->conn); + free((void *)impl->this.chr_path); + g_clear_object(&impl->chr); + dbus_monitor_clear(&impl->monitor); + g_clear_object(&impl->manager); + g_clear_object(&impl->conn); + free(impl); } void spa_bt_midi_server_released(struct spa_bt_midi_server *server, bool write) { struct impl *impl = SPA_CONTAINER_OF(server, struct impl, this); - struct chr *chr = (struct chr *)impl->chr; - chr_change_acquired(impl, chr, write, false); + chr_change_acquired(impl, write, false); } diff --git a/spa/plugins/bluez5/midi.h b/spa/plugins/bluez5/midi.h index e5955f688..fbf270243 100644 --- a/spa/plugins/bluez5/midi.h +++ b/spa/plugins/bluez5/midi.h @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include @@ -134,8 +134,8 @@ static inline void spa_bt_midi_writer_init(struct spa_bt_midi_writer *writer, un int spa_bt_midi_writer_write(struct spa_bt_midi_writer *writer, uint64_t time, const uint8_t *event, size_t event_size); -struct spa_bt_midi_server *spa_bt_midi_server_new(DBusConnection *conn, - const struct spa_bt_midi_server_cb *cb, struct spa_log *log, void *user_data); +struct spa_bt_midi_server *spa_bt_midi_server_new(const struct spa_bt_midi_server_cb *cb, + GDBusConnection *conn, struct spa_log *log, void *user_data); void spa_bt_midi_server_released(struct spa_bt_midi_server *server, bool write); void spa_bt_midi_server_destroy(struct spa_bt_midi_server *server);