diff options
-rw-r--r-- | plugins/bluetooth.c | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c index 7e7c35ef..f1ea66c2 100644 --- a/plugins/bluetooth.c +++ b/plugins/bluetooth.c @@ -24,13 +24,292 @@ #endif #include <errno.h> +#include <string.h> #define CONNMAN_API_SUBJECT_TO_CHANGE #include <connman/plugin.h> #include <connman/dbus.h> #include <connman/technology.h> +#include <connman/device.h> +#include <gdbus.h> + +#define BLUEZ_SERVICE "org.bluez" +#define BLUEZ_PATH "/org/bluez" + +#define BLUETOOTH_ADDR_LEN 6 static DBusConnection *connection; +static GDBusClient *client; +static GHashTable *devices; + +static void address2ident(const char *address, char *ident) +{ + int i; + + for (i = 0; i < BLUETOOTH_ADDR_LEN; i++) { + ident[i * 2] = address[i * 3]; + ident[i * 2 + 1] = address[i * 3 + 1]; + } + ident[BLUETOOTH_ADDR_LEN * 2] = '\0'; +} + +static const char *proxy_get_string(GDBusProxy *proxy, const char *property) +{ + DBusMessageIter iter; + const char *str; + + if (g_dbus_proxy_get_property(proxy, property, &iter) == FALSE) + return NULL; + dbus_message_iter_get_basic(&iter, &str); + return str; +} + +static connman_bool_t proxy_get_bool(GDBusProxy *proxy, const char *property) +{ + DBusMessageIter iter; + connman_bool_t value; + + if (g_dbus_proxy_get_property(proxy, property, &iter) == FALSE) + return FALSE; + dbus_message_iter_get_basic(&iter, &value); + return value; +} + +static void device_enable_cb(const DBusError *error, void *user_data) +{ + char *path = user_data; + struct connman_device *device; + + device = g_hash_table_lookup(devices, path); + if (device == NULL) { + DBG("device already removed"); + goto out; + } + + if (dbus_error_is_set(error) == TRUE) { + connman_warn("Bluetooth device %s not enabled %s", + path, error->message); + goto out; + } + + DBG("device %p", device); + connman_device_set_powered(device, TRUE); + +out: + g_free(path); +} + +static int bluetooth_device_enable(struct connman_device *device) +{ + GDBusProxy *proxy = connman_device_get_data(device); + connman_bool_t device_powered = TRUE; + const char *path; + + if (proxy == NULL) + return 0; + + path = g_dbus_proxy_get_path(proxy); + + if (proxy_get_bool(proxy, "Powered") == TRUE) { + DBG("already enabled %p %s", device, path); + return -EALREADY; + } + + DBG("device %p %s", device, path); + + g_dbus_proxy_set_property_basic(proxy, "Powered", + DBUS_TYPE_BOOLEAN, &device_powered, + device_enable_cb, g_strdup(path), NULL); + + return -EINPROGRESS; +} + +static void device_disable_cb(const DBusError *error, void *user_data) +{ + char *path = user_data; + struct connman_device *device; + + device = g_hash_table_lookup(devices, path); + if (device == NULL) { + DBG("device already removed"); + goto out; + } + + if (dbus_error_is_set(error) == TRUE) { + connman_warn("Bluetooth device %s not disabled: %s", + path, error->message); + goto out; + } + + DBG("device %p", device); + connman_device_set_powered(device, FALSE); + +out: + g_free(path); +} + +static int bluetooth_device_disable(struct connman_device *device) +{ + GDBusProxy *proxy = connman_device_get_data(device); + connman_bool_t device_powered = FALSE; + const char *path; + + if (proxy == NULL) + return 0; + + path = g_dbus_proxy_get_path(proxy); + + if (proxy_get_bool(proxy, "Powered") == FALSE) { + DBG("already disabled %p %s", device, path); + return -EALREADY; + } + + DBG("device %p %s", device, path); + + g_dbus_proxy_set_property_basic(proxy, "Powered", + DBUS_TYPE_BOOLEAN, &device_powered, + device_disable_cb, g_strdup(path), NULL); + + return -EINPROGRESS; +} + +static void adapter_property_change(GDBusProxy *proxy, const char *name, + DBusMessageIter *iter, void *user_data) +{ + struct connman_device *device; + const char *path; + connman_bool_t adapter_powered, device_powered; + + if (strcmp(name, "Powered") != 0) + return; + + path = g_dbus_proxy_get_path(proxy); + device = g_hash_table_lookup(devices, path); + + adapter_powered = proxy_get_bool(proxy, "Powered"); + device_powered = connman_device_get_powered(device); + + DBG("device %p %s device powered %d adapter powered %d", device, path, + device_powered, adapter_powered); + + if (device_powered != adapter_powered) { + if (device_powered == TRUE) + bluetooth_device_enable(device); + else + bluetooth_device_disable(device); + } +} + +static void device_free(gpointer data) +{ + struct connman_device *device = data; + GDBusProxy *proxy = connman_device_get_data(device); + + connman_device_set_data(device, NULL); + if (proxy != NULL) + g_dbus_proxy_unref(proxy); + + connman_device_unregister(device); + connman_device_unref(device); +} + +static void device_create(GDBusProxy *proxy) +{ + struct connman_device *device = NULL; + const char *path = g_dbus_proxy_get_path(proxy); + const char *address; + char ident[BLUETOOTH_ADDR_LEN * 2 + 1]; + connman_bool_t powered; + + address = proxy_get_string(proxy, "Address"); + if (address == NULL) + return; + + address2ident(address, ident); + + device = connman_device_create("bluetooth", + CONNMAN_DEVICE_TYPE_BLUETOOTH); + if (device == NULL) + return; + + connman_device_set_data(device, g_dbus_proxy_ref(proxy)); + connman_device_set_ident(device, ident); + + g_hash_table_replace(devices, g_strdup(path), device); + + DBG("device %p %s device powered %d adapter powered %d", device, + path, connman_device_get_powered(device), + proxy_get_bool(proxy, "Powered")); + + if (connman_device_register(device) < 0) { + g_hash_table_remove(devices, device); + return; + } + + g_dbus_proxy_set_property_watch(proxy, adapter_property_change, NULL); + + powered = proxy_get_bool(proxy, "Powered"); + connman_device_set_powered(device, powered); +} + +static void object_added(GDBusProxy *proxy, void *user_data) +{ + const char *interface; + + interface = g_dbus_proxy_get_interface(proxy); + + if (strcmp(interface, "org.bluez.Adapter1") == 0) { + DBG("%s %s", interface, g_dbus_proxy_get_path(proxy)); + device_create(proxy); + return; + } + +} + +static void object_removed(GDBusProxy *proxy, void *user_data) +{ + const char *interface, *path; + + interface = g_dbus_proxy_get_interface(proxy); + + if (strcmp(interface, "org.bluez.Adapter1") == 0) { + path = g_dbus_proxy_get_path(proxy); + DBG("%s %s", interface, path); + + g_hash_table_remove(devices, path); + } +} + +static int bluetooth_device_probe(struct connman_device *device) +{ + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, devices); + + while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { + struct connman_device *known = value; + + if (device == known) + return 0; + } + + return -EOPNOTSUPP; +} + +static void bluetooth_device_remove(struct connman_device *device) +{ + DBG("%p", device); +} + +static struct connman_device_driver device_driver = { + .name = "bluetooth", + .type = CONNMAN_DEVICE_TYPE_BLUETOOTH, + .probe = bluetooth_device_probe, + .remove = bluetooth_device_remove, + .enable = bluetooth_device_enable, + .disable = bluetooth_device_disable, +}; static int bluetooth_tech_probe(struct connman_technology *technology) { @@ -60,9 +339,35 @@ static int bluetooth_init(void) goto out; } + devices = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + device_free); + + if (connman_device_driver_register(&device_driver) < 0) { + connman_warn("Failed to initialize device driver for " + BLUEZ_SERVICE); + connman_technology_driver_unregister(&tech_driver); + goto out; + } + + client = g_dbus_client_new(connection, BLUEZ_SERVICE, BLUEZ_PATH); + if (client == NULL) { + connman_warn("Failed to initialize D-Bus client for " + BLUEZ_SERVICE); + goto out; + } + + g_dbus_client_set_proxy_handlers(client, object_added, object_removed, + NULL, NULL); + return 0; out: + if (devices != NULL) + g_hash_table_destroy(devices); + + if (client != NULL) + g_dbus_client_unref(client); + if (connection != NULL) dbus_connection_unref(connection); @@ -71,6 +376,9 @@ out: static void bluetooth_exit(void) { + connman_device_driver_unregister(&device_driver); + g_hash_table_destroy(devices); + connman_technology_driver_unregister(&tech_driver); dbus_connection_unref(connection); } |