summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/linux/Makefile.am2
-rw-r--r--src/linux/up-backend.c208
-rw-r--r--src/linux/up-device-bluez.c182
-rw-r--r--src/linux/up-device-bluez.h56
-rw-r--r--src/linux/up-native.c18
5 files changed, 462 insertions, 4 deletions
diff --git a/src/linux/Makefile.am b/src/linux/Makefile.am
index 139fdad..bacf815 100644
--- a/src/linux/Makefile.am
+++ b/src/linux/Makefile.am
@@ -37,6 +37,8 @@ libupshared_la_SOURCES = \
up-device-hid.h \
up-device-wup.c \
up-device-wup.h \
+ up-device-bluez.c \
+ up-device-bluez.h \
up-input.c \
up-input.h \
up-backend.c \
diff --git a/src/linux/up-backend.c b/src/linux/up-backend.c
index 4b01d15..e668dc8 100644
--- a/src/linux/up-backend.c
+++ b/src/linux/up-backend.c
@@ -39,6 +39,7 @@
#include "up-device-unifying.h"
#include "up-device-wup.h"
#include "up-device-hid.h"
+#include "up-device-bluez.h"
#include "up-input.h"
#include "up-config.h"
#ifdef HAVE_IDEVICE
@@ -65,6 +66,10 @@ struct UpBackendPrivate
GDBusProxy *logind_proxy;
guint logind_sleep_id;
int logind_inhibitor_fd;
+
+ /* BlueZ */
+ guint bluez_watch_id;
+ GDBusObjectManager *bluez_client;
};
enum {
@@ -272,6 +277,190 @@ up_backend_uevent_signal_handler_cb (GUdevClient *client, const gchar *action,
}
}
+static gboolean
+is_battery_iface_proxy (GDBusProxy *interface_proxy)
+{
+ const char *iface;
+
+ iface = g_dbus_proxy_get_interface_name (interface_proxy);
+ return g_str_equal (iface, "org.bluez.Battery1");
+}
+
+static gboolean
+has_battery_iface (GDBusObject *object)
+{
+ GDBusInterface *iface;
+
+ iface = g_dbus_object_get_interface (object, "org.bluez.Battery1");
+ if (!iface)
+ return FALSE;
+ g_object_unref (iface);
+ return TRUE;
+}
+
+static void
+bluez_proxies_changed (GDBusObjectManagerClient *manager,
+ GDBusObjectProxy *object_proxy,
+ GDBusProxy *interface_proxy,
+ GVariant *changed_properties,
+ GStrv invalidated_properties,
+ gpointer user_data)
+{
+ UpBackend *backend = user_data;
+ GObject *object;
+ UpDeviceBluez *bluez;
+
+ if (!is_battery_iface_proxy (interface_proxy))
+ return;
+
+ object = up_device_list_lookup (backend->priv->device_list, G_OBJECT (object_proxy));
+ if (!object)
+ return;
+
+ bluez = UP_DEVICE_BLUEZ (object);
+ up_device_bluez_update (bluez, changed_properties);
+ g_object_unref (object);
+}
+
+static void
+bluez_interface_removed (GDBusObjectManager *manager,
+ GDBusObject *bus_object,
+ GDBusInterface *interface,
+ gpointer user_data)
+{
+ UpBackend *backend = user_data;
+ GObject *object;
+
+ /* It might be another iface on another device that got removed */
+ if (has_battery_iface (bus_object))
+ return;
+
+ object = up_device_list_lookup (backend->priv->device_list, G_OBJECT (bus_object));
+ if (!object)
+ return;
+
+ g_debug ("emitting device-removed: %s", g_dbus_object_get_object_path (bus_object));
+ g_signal_emit (backend, signals[SIGNAL_DEVICE_REMOVED], 0, bus_object, UP_DEVICE (object));
+
+ g_object_unref (object);
+}
+
+static void
+bluez_interface_added (GDBusObjectManager *manager,
+ GDBusObject *bus_object,
+ GDBusInterface *interface,
+ gpointer user_data)
+{
+ UpBackend *backend = user_data;
+ UpDevice *device;
+ GObject *object;
+ gboolean ret;
+
+ if (!has_battery_iface (bus_object))
+ return;
+
+ object = up_device_list_lookup (backend->priv->device_list, G_OBJECT (bus_object));
+ if (object != NULL) {
+ g_object_unref (object);
+ return;
+ }
+
+ device = UP_DEVICE (up_device_bluez_new ());
+ ret = up_device_coldplug (device, backend->priv->daemon, G_OBJECT (bus_object));
+ if (!ret) {
+ g_object_unref (device);
+ return;
+ }
+
+ g_debug ("emitting device-added: %s", g_dbus_object_get_object_path (bus_object));
+ g_signal_emit (backend, signals[SIGNAL_DEVICE_ADDED], 0, bus_object, device);
+}
+
+static void
+bluez_appeared (GDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer user_data)
+{
+ UpBackend *backend = user_data;
+ GError *error = NULL;
+ GList *objects, *l;
+
+ g_assert (backend->priv->bluez_client == NULL);
+
+ backend->priv->bluez_client = g_dbus_object_manager_client_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
+ "org.bluez",
+ "/",
+ NULL, NULL, NULL,
+ NULL, &error);
+ if (!backend->priv->bluez_client) {
+ g_warning ("Failed to create object manager for BlueZ: %s",
+ error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_debug ("BlueZ appeared");
+
+ g_signal_connect (backend->priv->bluez_client, "interface-proxy-properties-changed",
+ G_CALLBACK (bluez_proxies_changed), backend);
+ g_signal_connect (backend->priv->bluez_client, "interface-removed",
+ G_CALLBACK (bluez_interface_removed), backend);
+ g_signal_connect (backend->priv->bluez_client, "interface-added",
+ G_CALLBACK (bluez_interface_added), backend);
+
+ objects = g_dbus_object_manager_get_objects (backend->priv->bluez_client);
+ for (l = objects; l != NULL; l = l->next) {
+ GDBusObject *object = l->data;
+ GList *interfaces, *k;
+
+ interfaces = g_dbus_object_get_interfaces (object);
+
+ for (k = interfaces; k != NULL; k = k->next) {
+ GDBusInterface *iface = k->data;
+
+ bluez_interface_added (backend->priv->bluez_client,
+ object,
+ iface,
+ backend);
+ g_object_unref (iface);
+ }
+ g_list_free (interfaces);
+ g_object_unref (object);
+ }
+ g_list_free (objects);
+}
+
+static void
+bluez_vanished (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ UpBackend *backend = user_data;
+ GPtrArray *array;
+ guint i;
+
+ g_debug ("BlueZ disappeared");
+
+ array = up_device_list_get_array (backend->priv->device_list);
+
+ for (i = 0; i < array->len; i++) {
+ UpDevice *device = UP_DEVICE (g_ptr_array_index (array, i));
+ if (UP_IS_DEVICE_BLUEZ (device)) {
+ GDBusObject *object;
+
+ object = G_DBUS_OBJECT (up_device_get_native (device));
+ g_debug ("emitting device-removed: %s", g_dbus_object_get_object_path (object));
+ g_signal_emit (backend, signals[SIGNAL_DEVICE_REMOVED], 0, object, UP_DEVICE (object));
+ }
+ }
+
+ g_ptr_array_unref (array);
+
+ g_clear_object (&backend->priv->bluez_client);
+}
+
/**
* up_backend_coldplug:
* @backend: The %UpBackend class instance
@@ -312,6 +501,14 @@ up_backend_coldplug (UpBackend *backend, UpDaemon *daemon)
g_list_free_full (devices, (GDestroyNotify) g_object_unref);
}
+ backend->priv->bluez_watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
+ "org.bluez",
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ bluez_appeared,
+ bluez_vanished,
+ backend,
+ NULL);
+
return TRUE;
}
@@ -331,6 +528,11 @@ up_backend_unplug (UpBackend *backend)
if (backend->priv->managed_devices != NULL)
up_device_list_clear (backend->priv->managed_devices, FALSE);
g_clear_object (&backend->priv->daemon);
+ if (backend->priv->bluez_watch_id > 0) {
+ g_bus_unwatch_name (backend->priv->bluez_watch_id);
+ backend->priv->bluez_watch_id = 0;
+ }
+ g_clear_object (&backend->priv->bluez_client);
}
static gboolean
@@ -625,6 +827,12 @@ up_backend_finalize (GObject *object)
backend = UP_BACKEND (object);
+ if (backend->priv->bluez_watch_id > 0) {
+ g_bus_unwatch_name (backend->priv->bluez_watch_id);
+ backend->priv->bluez_watch_id = 0;
+ }
+ g_clear_object (&backend->priv->bluez_client);
+
g_clear_object (&backend->priv->config);
g_clear_object (&backend->priv->daemon);
g_clear_object (&backend->priv->device_list);
diff --git a/src/linux/up-device-bluez.c b/src/linux/up-device-bluez.c
new file mode 100644
index 0000000..42a8e1b
--- /dev/null
+++ b/src/linux/up-device-bluez.c
@@ -0,0 +1,182 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2017 Bastien Nocera <hadess@hadess.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gio/gio.h>
+
+#include "up-types.h"
+#include "up-device-bluez.h"
+
+G_DEFINE_TYPE (UpDeviceBluez, up_device_bluez, UP_TYPE_DEVICE)
+#define UP_DEVICE_BLUEZ_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UP_TYPE_DEVICE_BLUEZ, UpDeviceBluezPrivate))
+
+static UpDeviceKind
+appearance_to_kind (guint16 appearance)
+{
+ switch ((appearance & 0xffc0) >> 6) {
+ case 0x01:
+ return UP_DEVICE_KIND_PHONE;
+ case 0x02:
+ return UP_DEVICE_KIND_COMPUTER;
+ case 0x05:
+ return UP_DEVICE_KIND_MONITOR;
+ case 0x0a:
+ return UP_DEVICE_KIND_MEDIA_PLAYER;
+ case 0x0f: /* HID Generic */
+ switch (appearance & 0x3f) {
+ case 0x01:
+ return UP_DEVICE_KIND_KEYBOARD;
+ case 0x02:
+ return UP_DEVICE_KIND_MOUSE;
+ case 0x03:
+ case 0x04:
+ return UP_DEVICE_KIND_GAMING_INPUT;
+ case 0x05:
+ return UP_DEVICE_KIND_TABLET;
+ }
+ break;
+ }
+
+ return UP_DEVICE_KIND_UNKNOWN;
+}
+
+/**
+ * up_device_bluez_coldplug:
+ *
+ * Return %TRUE on success, %FALSE if we failed to get data and should be removed
+ **/
+static gboolean
+up_device_bluez_coldplug (UpDevice *device)
+{
+ GDBusObjectProxy *object_proxy;
+ GDBusProxy *proxy;
+ GError *error = NULL;
+ UpDeviceKind kind;
+ const char *uuid;
+ const char *model;
+ guint16 appearance;
+ guchar percentage;
+
+ /* Static device properties */
+ object_proxy = G_DBUS_OBJECT_PROXY (up_device_get_native (device));
+ proxy = g_dbus_proxy_new_sync (g_dbus_object_proxy_get_connection (object_proxy),
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.bluez",
+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object_proxy)),
+ "org.bluez.Device1",
+ NULL,
+ &error);
+
+ if (!proxy) {
+ g_warning ("Failed to get proxy for %s (iface org.bluez.Device1)",
+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object_proxy)));
+ return FALSE;
+ }
+
+ appearance = g_variant_get_uint16 (g_dbus_proxy_get_cached_property (proxy, "Appearance"));
+ kind = appearance_to_kind (appearance);
+ uuid = g_variant_get_string (g_dbus_proxy_get_cached_property (proxy, "Address"), NULL);
+ model = g_variant_get_string (g_dbus_proxy_get_cached_property (proxy, "Alias"), NULL);
+
+ /* hardcode some values */
+ g_object_set (device,
+ "type", kind,
+ "serial", uuid,
+ "model", model,
+ "power-supply", FALSE,
+ "has-history", TRUE,
+ NULL);
+
+ g_object_unref (proxy);
+
+ /* Initial battery values */
+ proxy = g_dbus_proxy_new_sync (g_dbus_object_proxy_get_connection (object_proxy),
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.bluez",
+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object_proxy)),
+ "org.bluez.Battery1",
+ NULL,
+ &error);
+
+ if (!proxy) {
+ g_warning ("Failed to get proxy for %s",
+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object_proxy)));
+ return FALSE;
+ }
+
+ percentage = g_variant_get_byte (g_dbus_proxy_get_cached_property (proxy, "Percentage"));
+
+ g_object_set (device,
+ "is-present", TRUE,
+ "percentage", (gdouble) percentage,
+ NULL);
+
+ g_object_unref (proxy);
+
+ return TRUE;
+}
+
+static void
+up_device_bluez_init (UpDeviceBluez *bluez)
+{
+}
+
+void
+up_device_bluez_update (UpDeviceBluez *bluez,
+ GVariant *properties)
+{
+ UpDevice *device = UP_DEVICE (bluez);
+ GVariantIter iter;
+ const gchar *key;
+ GVariant *value;
+
+ g_variant_iter_init (&iter, properties);
+ while (g_variant_iter_next (&iter, "{&sv}", &key, &value)) {
+ if (g_str_equal (key, "Percentage")) {
+ g_object_set (device, "percentage", (gdouble) g_variant_get_byte (value), NULL);
+ } else {
+ char *str = g_variant_print (value, TRUE);
+
+ g_warning ("Unhandled key: %s value: %s", key, str);
+ g_free (str);
+ }
+ g_variant_unref (value);
+ }
+}
+
+static void
+up_device_bluez_class_init (UpDeviceBluezClass *klass)
+{
+ UpDeviceClass *device_class = UP_DEVICE_CLASS (klass);
+
+ device_class->coldplug = up_device_bluez_coldplug;
+}
+
+UpDeviceBluez *
+up_device_bluez_new (void)
+{
+ return g_object_new (UP_TYPE_DEVICE_BLUEZ, NULL);
+}
+
diff --git a/src/linux/up-device-bluez.h b/src/linux/up-device-bluez.h
new file mode 100644
index 0000000..1cc14fb
--- /dev/null
+++ b/src/linux/up-device-bluez.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Bastien Nocera <hadess@hadess.net>
+ * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
+ * Copyright (C) 2008 Richard Hughes <richard@hughsie.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __UP_DEVICE_BLUEZ_H__
+#define __UP_DEVICE_BLUEZ_H__
+
+#include <glib-object.h>
+#include "up-device.h"
+
+G_BEGIN_DECLS
+
+#define UP_TYPE_DEVICE_BLUEZ (up_device_bluez_get_type ())
+#define UP_DEVICE_BLUEZ(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), UP_TYPE_DEVICE_BLUEZ, UpDeviceBluez))
+#define UP_DEVICE_BLUEZ_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), UP_TYPE_DEVICE_BLUEZ, UpDeviceBluezClass))
+#define UP_IS_DEVICE_BLUEZ(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UP_TYPE_DEVICE_BLUEZ))
+#define UP_IS_DEVICE_BLUEZ_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), UP_TYPE_DEVICE_BLUEZ))
+#define UP_DEVICE_BLUEZ_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), UP_TYPE_DEVICE_BLUEZ, UpDeviceBluezClass))
+
+typedef struct
+{
+ UpDevice parent;
+} UpDeviceBluez;
+
+typedef struct
+{
+ UpDeviceClass parent_class;
+} UpDeviceBluezClass;
+
+GType up_device_bluez_get_type (void);
+UpDeviceBluez *up_device_bluez_new (void);
+void up_device_bluez_update (UpDeviceBluez *bluez,
+ GVariant *properties);
+
+G_END_DECLS
+
+#endif /* __UP_DEVICE_BLUEZ_H__ */
+
diff --git a/src/linux/up-native.c b/src/linux/up-native.c
index a700d49..5896dad 100644
--- a/src/linux/up-native.c
+++ b/src/linux/up-native.c
@@ -19,6 +19,7 @@
*/
#include <glib.h>
+#include <gio/gio.h>
#include <gudev/gudev.h>
#include "up-native.h"
@@ -29,7 +30,9 @@
*
* This converts a GObject used as the device data into a native path.
* This would be implemented on a Linux system using:
- * g_udev_device_get_sysfs_path (G_UDEV_DEVICE (object))
+ * g_udev_device_get_sysfs_path (G_UDEV_DEVICE (object)) or
+ * g_dbus_object_get_object_path (G_DBUS_OBJECT (object)) for Bluetooth LE
+ * devices (handled by BlueZ).
*
* Return value: Device name for devices of subsystem "power_supply", otherwise
* the native path for the device which is unique.
@@ -37,16 +40,23 @@
const gchar *
up_native_get_native_path (GObject *object)
{
+ GUdevDevice *device;
+
+ /* That's a UpBluez */
+ if (G_IS_DBUS_OBJECT (object))
+ return g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
+
+ device = G_UDEV_DEVICE (object);
/* Device names within the same subsystem must be unique. To avoid
* treating the same power supply device on variable buses as different
* only because e. g. the USB or bluetooth tree layout changed, only
* use their name as identification. Also see
* http://bugzilla.kernel.org/show_bug.cgi?id=62041 */
- if (g_strcmp0 (g_udev_device_get_subsystem (G_UDEV_DEVICE (object)), "power_supply") == 0)
- return g_udev_device_get_name (G_UDEV_DEVICE (object));
+ if (g_strcmp0 (g_udev_device_get_subsystem (device), "power_supply") == 0)
+ return g_udev_device_get_name (device);
/* we do not expect other devices than power_supply, but provide this
* fallback for completeness */
- return g_udev_device_get_sysfs_path (G_UDEV_DEVICE (object));
+ return g_udev_device_get_sysfs_path (device);
}