From 0eb3a258daaa15a2cde3a815a24553f9394c028d Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Fri, 28 May 2010 01:56:35 +0100 Subject: [PATCH] Add battery query support for iDevices https://bugs.freedesktop.org/show_bug.cgi?id=28286 --- configure.ac | 8 + src/Makefile.am | 3 +- src/linux/Makefile.am | 12 ++- src/linux/up-backend.c | 12 ++ src/linux/up-device-idevice.c | 285 +++++++++++++++++++++++++++++++++++++++++ src/linux/up-device-idevice.h | 57 ++++++++ 6 files changed, 375 insertions(+), 2 deletions(-) create mode 100644 src/linux/up-device-idevice.c create mode 100644 src/linux/up-device-idevice.h diff --git a/configure.ac b/configure.ac index 64a8af7..94b805e 100644 --- a/configure.ac +++ b/configure.ac @@ -178,11 +178,18 @@ AC_DEFINE_UNQUOTED(BACKEND, "$with_backend", [backend]) AC_SUBST(BACKEND, "$with_backend") # only need GUdev on linux +with_idevice=no if test x$with_backend = xlinux; then PKG_CHECK_MODULES(GUDEV, [gudev-1.0 >= 001]) PKG_CHECK_MODULES(USB, [libusb-1.0 >= 1.0.0]) + PKG_CHECK_MODULES(IDEVICE, libimobiledevice-1.0 >= 0.9.7, with_idevice=yes) + if test x$with_idevice = xyes; then + AC_DEFINE(HAVE_IDEVICE, 1, [Define to 1 if AFC is going to be built]) + fi fi +AM_CONDITIONAL(HAVE_IDEVICE, [test x$with_idevice = xyes]) + # export to Makefile.am AM_CONDITIONAL(BACKEND_TYPE_DUMMY, [test x$with_backend = xdummy]) AM_CONDITIONAL(BACKEND_TYPE_LINUX, [test x$with_backend = xlinux]) @@ -253,6 +260,7 @@ echo " xsltproc: ${XSLTPROC} Backend: ${with_backend} + libimobiledevice support: ${with_idevice} Maintainer mode: ${USE_MAINTAINER_MODE} Building api docs: ${enable_gtk_doc} Building man pages: ${enable_man_pages} diff --git a/src/Makefile.am b/src/Makefile.am index 967a3f1..7853172 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -109,7 +109,8 @@ if BACKEND_TYPE_LINUX upowerd_LDADD += \ linux/libupshared.la \ $(USB_LIBS) \ - $(GUDEV_LIBS) + $(GUDEV_LIBS) \ + $(IDEVICE_LIBS) endif upowerd_CFLAGS = \ diff --git a/src/linux/Makefile.am b/src/linux/Makefile.am index 4d54ae3..fb36235 100644 --- a/src/linux/Makefile.am +++ b/src/linux/Makefile.am @@ -10,12 +10,19 @@ INCLUDES = \ $(DBUS_GLIB_CFLAGS) \ $(GUDEV_CFLAGS) \ $(POLKIT_CFLAGS) \ - $(GLIB_CFLAGS) + $(GLIB_CFLAGS) \ + $(IDEVICE_CFLAGS) if BACKEND_TYPE_LINUX noinst_LTLIBRARIES = libupshared.la endif +if HAVE_IDEVICE +idevice_files = up-device-idevice.c up-device-idevice.h +else +idevice_files = +endif + libupshared_la_SOURCES = \ up-device-supply.c \ up-device-supply.h \ @@ -31,8 +38,11 @@ libupshared_la_SOURCES = \ up-native.c \ sysfs-utils.c \ sysfs-utils.h \ + $(idevice_files) \ $(BUILT_SOURCES) +EXTRA_DIST = up-device-idevice.c up-device-idevice.h + libupshared_la_CFLAGS = \ $(WARNINGFLAGS_C) diff --git a/src/linux/up-backend.c b/src/linux/up-backend.c index 574921d..2739c37 100644 --- a/src/linux/up-backend.c +++ b/src/linux/up-backend.c @@ -40,6 +40,9 @@ #include "up-device-wup.h" #include "up-device-hid.h" #include "up-input.h" +#ifdef HAVE_IDEVICE +#include "up-device-idevice.h" +#endif /* HAVE_IDEVICE */ static void up_backend_class_init (UpBackendClass *klass); static void up_backend_init (UpBackend *backend); @@ -110,6 +113,15 @@ up_backend_device_new (UpBackend *backend, GUdevDevice *native) } else if (g_strcmp0 (subsys, "usb") == 0) { +#ifdef HAVE_IDEVICE + /* see if this is an iDevice */ + device = UP_DEVICE (up_device_idevice_new ()); + ret = up_device_coldplug (device, backend->priv->daemon, G_OBJECT (native)); + if (ret) + goto out; + g_object_unref (device); +#endif /* HAVE_IDEVICE */ + /* see if this is a CSR mouse or keyboard */ device = UP_DEVICE (up_device_csr_new ()); ret = up_device_coldplug (device, backend->priv->daemon, G_OBJECT (native)); diff --git a/src/linux/up-device-idevice.c b/src/linux/up-device-idevice.c new file mode 100644 index 0000000..fff8d62 --- /dev/null +++ b/src/linux/up-device-idevice.c @@ -0,0 +1,285 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2010 Bastien Nocera + * Copyright (C) 2005-2010 Richard Hughes + * Copyright (C) 2004 Sergey V. Udaltsov + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sysfs-utils.h" +#include "egg-debug.h" + +#include "up-types.h" +#include "up-device-idevice.h" + +#define DEFAULT_POLL_TIME 60 + +struct UpDeviceIdevicePrivate +{ + idevice_t dev; + lockdownd_client_t client; + guint poll_timer_id; +}; + +G_DEFINE_TYPE (UpDeviceIdevice, up_device_idevice, UP_TYPE_DEVICE) +#define UP_DEVICE_IDEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UP_TYPE_DEVICE_IDEVICE, UpDeviceIdevicePrivate)) + +static gboolean up_device_idevice_refresh (UpDevice *device); + +/** + * up_device_idevice_poll_cb: + **/ +static gboolean +up_device_idevice_poll_cb (UpDeviceIdevice *idevice) +{ + UpDevice *device = UP_DEVICE (idevice); + + egg_debug ("Polling: %s", up_device_get_object_path (device)); + up_device_idevice_refresh (device); + + /* always continue polling */ + return TRUE; +} + +/** + * up_device_idevice_coldplug: + * + * Return %TRUE on success, %FALSE if we failed to get data and should be removed + **/ +static gboolean +up_device_idevice_coldplug (UpDevice *device) +{ + UpDeviceIdevice *idevice = UP_DEVICE_IDEVICE (device); + GUdevDevice *native; + idevice_error_t ret; + const gchar *uuid; + const gchar *model; + plist_t dict, node; + guint64 poll_seconds; + idevice_t dev = NULL; + lockdownd_client_t client = NULL; + UpDeviceKind kind; + + /* Is it an iDevice? */ + native = G_UDEV_DEVICE (up_device_get_native (device)); + if (g_udev_device_get_property_as_boolean (native, "USBMUX_SUPPORTED") == FALSE) + return FALSE; + + /* Get the UUID */ + uuid = g_udev_device_get_property (native, "ID_SERIAL_SHORT"); + if (uuid == NULL) + return FALSE; + + /* Connect to the device */ + ret = idevice_new (&dev, uuid); + if (ret != IDEVICE_E_SUCCESS) + return FALSE; + + if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake (dev, &client, "upower")) { + idevice_free (dev); + return FALSE; + } + + /* Get the poll timeout */ + if (lockdownd_get_value (client, "com.apple.mobile.iTunes", "BatteryPollInterval", &dict) != LOCKDOWN_E_SUCCESS) { + lockdownd_client_free (client); + idevice_free (dev); + return FALSE; + } + + poll_seconds = 0; + node = plist_dict_get_item (dict, "BatteryPollInterval"); + if (node != NULL) + plist_get_uint_val (node, &poll_seconds); + if (poll_seconds == 0) + poll_seconds = DEFAULT_POLL_TIME; + plist_free (dict); + + /* Set up struct */ + idevice->priv->dev = dev; + idevice->priv->client = client; + + model = g_udev_device_get_property (native, "ID_MODEL"); + kind = UP_DEVICE_KIND_PHONE; + if (model != NULL && strstr (model, "iPad")) { + kind = UP_DEVICE_KIND_TABLET; + } else if (model != NULL && strstr (model, "iPod")) { + kind = UP_DEVICE_KIND_MEDIA_PLAYER; + } + + /* hardcode some values */ + g_object_set (device, + "type", kind, + "vendor", g_udev_device_get_property (native, "ID_VENDOR"), + "model", g_udev_device_get_property (native, "ID_MODEL"), + "power-supply", FALSE, + "is-present", TRUE, + "is-rechargeable", TRUE, + "has-history", TRUE, + NULL); + + + /* coldplug */ + ret = up_device_idevice_refresh (device); + + lockdownd_client_free (idevice->priv->client); + idevice->priv->client = NULL; + + /* set up a poll */ + idevice->priv->poll_timer_id = g_timeout_add_seconds (poll_seconds, + (GSourceFunc) up_device_idevice_poll_cb, idevice); + + return TRUE; +} + +/** + * up_device_idevice_refresh: + * + * Return %TRUE on success, %FALSE if we failed to refresh or no data + **/ +static gboolean +up_device_idevice_refresh (UpDevice *device) +{ + GTimeVal timeval; + UpDeviceIdevice *idevice = UP_DEVICE_IDEVICE (device); + lockdownd_client_t client = NULL; + plist_t dict, node; + guint64 percentage; + guint8 charging; + UpDeviceState state; + + /* Open a lockdown port, or re-use the one we have */ + if (idevice->priv->client == NULL) { + if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake (idevice->priv->dev, &client, "upower")) + return FALSE; + } else { + client = idevice->priv->client; + } + + if (lockdownd_get_value (client, "com.apple.mobile.battery", NULL, &dict) != LOCKDOWN_E_SUCCESS) + return FALSE; + + /* get battery status */ + node = plist_dict_get_item (dict, "BatteryCurrentCapacity"); + plist_get_uint_val (node, &percentage); + + g_object_set (device, "percentage", (double) percentage, NULL); + egg_debug ("percentage=%"G_GUINT64_FORMAT, percentage); + + /* get charging status */ + node = plist_dict_get_item (dict, "BatteryIsCharging"); + plist_get_bool_val (node, &charging); + + if (percentage == 100) + state = UP_DEVICE_STATE_FULLY_CHARGED; + else if (percentage == 0) + state = UP_DEVICE_STATE_EMPTY; + else if (charging) + state = UP_DEVICE_STATE_CHARGING; + else + state = UP_DEVICE_STATE_DISCHARGING; /* upower doesn't have a "not charging" state */ + + g_object_set (device, "state", charging ? UP_DEVICE_STATE_CHARGING : UP_DEVICE_STATE_DISCHARGING, NULL); + egg_debug ("state=%d", charging); + + plist_free (dict); + + /* reset time */ + g_get_current_time (&timeval); + g_object_set (device, "update-time", (guint64) timeval.tv_sec, NULL); + + /* Only free it if we opened it */ + if (idevice->priv->client == NULL) + lockdownd_client_free (client); + + return TRUE; +} + +/** + * up_device_idevice_init: + **/ +static void +up_device_idevice_init (UpDeviceIdevice *idevice) +{ + idevice->priv = UP_DEVICE_IDEVICE_GET_PRIVATE (idevice); +} + +/** + * up_device_idevice_finalize: + **/ +static void +up_device_idevice_finalize (GObject *object) +{ + UpDeviceIdevice *idevice; + + g_return_if_fail (object != NULL); + g_return_if_fail (UP_IS_DEVICE_IDEVICE (object)); + + idevice = UP_DEVICE_IDEVICE (object); + g_return_if_fail (idevice->priv != NULL); + + if (idevice->priv->poll_timer_id > 0) + g_source_remove (idevice->priv->poll_timer_id); + if (idevice->priv->client != NULL) + lockdownd_client_free (idevice->priv->client); + idevice_free (idevice->priv->dev); + + G_OBJECT_CLASS (up_device_idevice_parent_class)->finalize (object); +} + +/** + * up_device_idevice_class_init: + **/ +static void +up_device_idevice_class_init (UpDeviceIdeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + UpDeviceClass *device_class = UP_DEVICE_CLASS (klass); + + object_class->finalize = up_device_idevice_finalize; + device_class->coldplug = up_device_idevice_coldplug; + device_class->refresh = up_device_idevice_refresh; + + g_type_class_add_private (klass, sizeof (UpDeviceIdevicePrivate)); +} + +/** + * up_device_idevice_new: + **/ +UpDeviceIdevice * +up_device_idevice_new (void) +{ + return g_object_new (UP_TYPE_DEVICE_IDEVICE, NULL); +} + diff --git a/src/linux/up-device-idevice.h b/src/linux/up-device-idevice.h new file mode 100644 index 0000000..4f37ab5 --- /dev/null +++ b/src/linux/up-device-idevice.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2010 Bastien Nocera + * Copyright (C) 2008 David Zeuthen + * Copyright (C) 2008 Richard Hughes + * + * 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_IDEVICE_H__ +#define __UP_DEVICE_IDEVICE_H__ + +#include +#include "up-device.h" + +G_BEGIN_DECLS + +#define UP_TYPE_DEVICE_IDEVICE (up_device_idevice_get_type ()) +#define UP_DEVICE_IDEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), UP_TYPE_DEVICE_IDEVICE, UpDeviceIdevice)) +#define UP_DEVICE_IDEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), UP_TYPE_DEVICE_IDEVICE, UpDeviceIdeviceClass)) +#define UP_IS_DEVICE_IDEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UP_TYPE_DEVICE_IDEVICE)) +#define UP_IS_DEVICE_IDEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), UP_TYPE_DEVICE_IDEVICE)) +#define UP_DEVICE_IDEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), UP_TYPE_DEVICE_IDEVICE, UpDeviceIdeviceClass)) + +typedef struct UpDeviceIdevicePrivate UpDeviceIdevicePrivate; + +typedef struct +{ + UpDevice parent; + UpDeviceIdevicePrivate *priv; +} UpDeviceIdevice; + +typedef struct +{ + UpDeviceClass parent_class; +} UpDeviceIdeviceClass; + +GType up_device_idevice_get_type (void); +UpDeviceIdevice *up_device_idevice_new (void); + +G_END_DECLS + +#endif /* __UP_DEVICE_IDEVICE_H__ */ + -- 1.7.0.1