summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/bluetooth.c308
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);
}