summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2008-08-12 14:49:47 +0100
committerRichard Hughes <richard@hughsie.com>2008-08-12 14:49:47 +0100
commit5b950ddeb3e79fb2f6fbe76e2c1120e4c0cb23e7 (patch)
tree59d550a37dd139f47c89b34f4b4e981cb26b3ec6
parent5f0bb1e2497228f7348820cffe6f56bfa1e53edd (diff)
downloadupower-5b950ddeb3e79fb2f6fbe76e2c1120e4c0cb23e7.tar.gz
add support for old style HID UPS devices
-rw-r--r--src/Makefile.am1
-rw-r--r--src/dkp-daemon.c8
-rw-r--r--src/dkp-hid.c408
-rw-r--r--src/dkp-hid.h55
4 files changed, 472 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 0fdba82..1effc54 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,7 @@ devkit_power_daemon_SOURCES = \
dkp-device-list.h dkp-device-list.c \
dkp-supply.h dkp-supply.c \
dkp-csr.h dkp-csr.c \
+ dkp-hid.h dkp-hid.c \
dkp-history.h dkp-history.c \
sysfs-utils.h sysfs-utils.c \
main.c \
diff --git a/src/dkp-daemon.c b/src/dkp-daemon.c
index 7f9510c..aaf5b9b 100644
--- a/src/dkp-daemon.c
+++ b/src/dkp-daemon.c
@@ -36,6 +36,7 @@
#include "dkp-device.h"
#include "dkp-supply.h"
#include "dkp-csr.h"
+#include "dkp-hid.h"
#include "dkp-device-list.h"
#include "dkp-daemon-glue.h"
@@ -434,6 +435,13 @@ gpk_daemon_device_get (DkpDaemon *daemon, DevkitDevice *d)
goto out;
g_object_unref (device);
+ /* try to detect a HID UPS */
+ device = DKP_DEVICE (dkp_hid_new ());
+ ret = dkp_device_coldplug (device, daemon, d);
+ if (ret)
+ goto out;
+ g_object_unref (device);
+
/* no valid USB object ;-( */
device = NULL;
diff --git a/src/dkp-hid.c b/src/dkp-hid.c
new file mode 100644
index 0000000..5d1d3d8
--- /dev/null
+++ b/src/dkp-hid.c
@@ -0,0 +1,408 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006-2008 Richard Hughes <richard@hughsie.com>
+ *
+ * Based on hid-ups.c: Copyright (c) 2001 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001 Paul Stewart <hiddev@wetlogic.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 <string.h>
+#include <math.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
+#include <glib-object.h>
+#include <devkit-gobject.h>
+
+/* asm/types.h required for __s32 in linux/hiddev.h */
+#include <asm/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/hiddev.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "sysfs-utils.h"
+#include "dkp-debug.h"
+#include "dkp-enum.h"
+#include "dkp-object.h"
+#include "dkp-hid.h"
+
+#define DKP_HID_REFRESH_TIMEOUT 30L
+
+#define DKP_HID_USAGE 0x840000
+#define DKP_HID_SERIAL 0x8400fe
+#define DKP_HID_CHEMISTRY 0x850089
+#define DKP_HID_CAPACITY_MODE 0x85002c
+#define DKP_HID_BATTERY_VOLTAGE 0x840030
+#define DKP_HID_BELOW_RCL 0x840042
+#define DKP_HID_SHUTDOWN_IMMINENT 0x840069
+#define DKP_HID_PRODUCT 0x8400fe
+#define DKP_HID_SERIAL_NUMBER 0x8400ff
+#define DKP_HID_CHARGING 0x850044
+#define DKP_HID_DISCHARGING 0x850045
+#define DKP_HID_REMAINING_CAPACITY 0x850066
+#define DKP_HID_RUNTIME_TO_EMPTY 0x850068
+#define DKP_HID_AC_PRESENT 0x8500d0
+#define DKP_HID_BATTERY_PRESENT 0x8500d1
+#define DKP_HID_DESIGN_CAPACITY 0x850083
+#define DKP_HID_DEVICE_NAME 0x850088
+#define DKP_HID_DEVICE_CHEMISTRY 0x850089
+#define DKP_HID_RECHARGEABLE 0x85008b
+#define DKP_HID_OEM_INFORMATION 0x85008f
+
+struct DkpHidPrivate
+{
+ guint poll_timer_id;
+ int fd;
+};
+
+static void dkp_hid_class_init (DkpHidClass *klass);
+
+G_DEFINE_TYPE (DkpHid, dkp_hid, DKP_TYPE_DEVICE)
+#define DKP_HID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DKP_TYPE_HID, DkpHidPrivate))
+
+static gboolean dkp_hid_refresh (DkpDevice *device);
+
+/**
+ * dkp_hid_is_ups:
+ **/
+static gboolean
+dkp_hid_is_ups (DkpHid *hid)
+{
+ guint i;
+ int retval;
+ gboolean ret = FALSE;
+ struct hiddev_devinfo device_info;
+
+ /* get device info */
+ retval = ioctl (hid->priv->fd, HIDIOCGDEVINFO, &device_info);
+ if (retval < 0)
+ dkp_debug ("error: %s", strerror (errno));
+ goto out;
+
+ /* can we use UPS? */
+ for (i = 0; i < device_info.num_applications; i++) {
+ retval = ioctl(hid->priv->fd, HIDIOCAPPLICATION, i);
+ if ((retval & 0xff0000) == DKP_HID_USAGE) {
+ ret = TRUE;
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * dkp_hid_poll:
+ **/
+static gboolean
+dkp_hid_poll (DkpHid *hid)
+{
+ gboolean ret;
+ DkpDevice *device = DKP_DEVICE (hid);
+ DkpObject *obj = dkp_device_get_obj (device);
+
+ dkp_debug ("Polling: %s", obj->native_path);
+ ret = dkp_hid_refresh (device);
+ if (ret)
+ dkp_device_emit_changed (device);
+ return TRUE;
+}
+
+/**
+ * dkp_hid_get_string:
+ **/
+static const gchar *
+dkp_hid_get_string (DkpHid *hid, int sindex)
+{
+ static struct hiddev_string_descriptor sdesc;
+
+ /* nothing to get */
+ if (sindex == 0)
+ return "";
+
+ sdesc.index = sindex;
+
+ /* failed */
+ if (ioctl (hid->priv->fd, HIDIOCGSTRING, &sdesc) < 0)
+ return "";
+
+ dkp_debug ("value: '%s'", sdesc.value);
+ return sdesc.value;
+}
+
+/**
+ * dkp_hid_set_obj:
+ **/
+static gboolean
+dkp_hid_set_obj (DkpHid *hid, int code, int value)
+{
+ const gchar *type;
+ gboolean ret = TRUE;
+ DkpDevice *device = DKP_DEVICE (hid);
+ DkpObject *obj = dkp_device_get_obj (device);
+
+ switch (code) {
+ case DKP_HID_REMAINING_CAPACITY:
+ obj->battery_percentage = value;
+ break;
+ case DKP_HID_RUNTIME_TO_EMPTY:
+ obj->battery_time_to_empty = value;
+ break;
+ case DKP_HID_CHARGING:
+ if (value != 0)
+ obj->battery_state = DKP_DEVICE_STATE_CHARGING;
+ break;
+ case DKP_HID_DISCHARGING:
+ if (value != 0)
+ obj->battery_state = DKP_DEVICE_STATE_DISCHARGING;
+ break;
+ case DKP_HID_BATTERY_PRESENT:
+ obj->battery_is_present = (value != 0);
+ break;
+ case DKP_HID_DEVICE_NAME:
+ //obj->device_name = dkp_hid_get_string (hid, value);
+ break;
+ case DKP_HID_CHEMISTRY:
+ type = dkp_hid_get_string (hid, value);
+ obj->battery_technology = dkp_acpi_to_device_technology (type);
+ break;
+ case DKP_HID_RECHARGEABLE:
+ obj->battery_is_rechargeable = (value != 0);
+ break;
+ case DKP_HID_OEM_INFORMATION:
+ obj->vendor = g_strdup (dkp_hid_get_string (hid, value));
+ break;
+ case DKP_HID_PRODUCT:
+ obj->model = g_strdup (dkp_hid_get_string (hid, value));
+ break;
+ case DKP_HID_SERIAL_NUMBER:
+ obj->serial = g_strdup (dkp_hid_get_string (hid, value));
+ break;
+ case DKP_HID_DESIGN_CAPACITY:
+ obj->battery_energy_full_design = value;
+ break;
+ default:
+ ret = FALSE;
+ break;
+ }
+ return ret;
+}
+
+/**
+ * dkp_hid_get_all_data:
+ **/
+static gboolean
+dkp_hid_get_all_data (DkpHid *hid)
+{
+ struct hiddev_report_info rinfo;
+ struct hiddev_field_info finfo;
+ struct hiddev_usage_ref uref;
+ int rtype;
+ guint i, j;
+
+ /* get all results */
+ for (rtype = HID_REPORT_TYPE_MIN; rtype <= HID_REPORT_TYPE_MAX; rtype++) {
+ rinfo.report_type = rtype;
+ rinfo.report_id = HID_REPORT_ID_FIRST;
+ while (ioctl (hid->priv->fd, HIDIOCGREPORTINFO, &rinfo) >= 0) {
+ for (i = 0; i < rinfo.num_fields; i++) {
+ memset (&finfo, 0, sizeof (finfo));
+ finfo.report_type = rinfo.report_type;
+ finfo.report_id = rinfo.report_id;
+ finfo.field_index = i;
+ ioctl (hid->priv->fd, HIDIOCGFIELDINFO, &finfo);
+
+ memset (&uref, 0, sizeof (uref));
+ for (j = 0; j < finfo.maxusage; j++) {
+ uref.report_type = finfo.report_type;
+ uref.report_id = finfo.report_id;
+ uref.field_index = i;
+ uref.usage_index = j;
+ ioctl (hid->priv->fd, HIDIOCGUCODE, &uref);
+ ioctl (hid->priv->fd, HIDIOCGUSAGE, &uref);
+
+ /* process each */
+ dkp_hid_set_obj (hid, uref.usage_code, uref.value);
+ }
+ }
+ rinfo.report_id |= HID_REPORT_ID_NEXT;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * dkp_hid_coldplug:
+ **/
+static gboolean
+dkp_hid_coldplug (DkpDevice *device)
+{
+ DkpHid *hid = DKP_HID (device);
+ DevkitDevice *d;
+ gboolean ret = FALSE;
+ gchar *device_file = NULL;
+ guint dev_num;
+ guint bus_num;
+ DkpObject *obj = dkp_device_get_obj (device);
+
+ /* detect what kind of device we are */
+ d = dkp_device_get_d (device);
+ if (d == NULL)
+ dkp_error ("could not get device");
+
+ /* get what USB device we are */
+ bus_num = sysfs_get_int (obj->native_path, "busnum");
+ dev_num = sysfs_get_int (obj->native_path, "devnum");
+
+ /* get correct bus numbers? */
+ if (bus_num == 0 || dev_num == 0) {
+ dkp_debug ("unable to get HID bus or device numbers");
+ goto out;
+ }
+
+ /* connect to the device */
+ device_file = g_strdup_printf ("/dev/bus/usb/%03i/%03i", bus_num, dev_num);
+ hid->priv->fd = open (device_file, O_RDWR);
+ if (hid->priv->fd < 0) {
+ dkp_debug ("cannot open device file %s", device_file);
+ goto out;
+ }
+
+ /* first check that we are an UPS */
+ ret = dkp_hid_is_ups (hid);
+ if (!ret) {
+ dkp_debug ("not a HID device: %s", device_file);
+ goto out;
+ }
+
+ obj->type = DKP_DEVICE_TYPE_UPS;
+ obj->battery_is_rechargeable = TRUE;
+ obj->power_supply = TRUE;
+ obj->battery_is_present = TRUE;
+
+ /* coldplug everything */
+ dkp_hid_get_all_data (hid);
+
+ /* coldplug */
+ ret = dkp_hid_refresh (device);
+out:
+ g_free (device_file);
+ return ret;
+}
+
+/**
+ * dkp_hid_refresh:
+ **/
+static gboolean
+dkp_hid_refresh (DkpDevice *device)
+{
+ gboolean ret = FALSE;
+ GTimeVal time;
+ guint i;
+ struct hiddev_event ev[64];
+ int rd;
+ DkpHid *hid = DKP_HID (device);
+ DkpObject *obj = dkp_device_get_obj (device);
+
+ /* reset time */
+ g_get_current_time (&time);
+ obj->update_time = time.tv_sec;
+
+ /* read any data */
+ rd = read (hid->priv->fd, ev, sizeof (ev));
+ if (rd < (int) sizeof (ev[0]))
+ goto out;
+
+ /* process each event */
+ for (i=0; i < rd / sizeof (ev[0]); i++)
+ ret = dkp_hid_set_obj (hid, ev[i].hid, ev[i].value);
+out:
+ return ret;
+}
+
+/**
+ * dkp_hid_init:
+ **/
+static void
+dkp_hid_init (DkpHid *hid)
+{
+ hid->priv = DKP_HID_GET_PRIVATE (hid);
+ hid->priv->fd = -1;
+ hid->priv->poll_timer_id = g_timeout_add_seconds (DKP_HID_REFRESH_TIMEOUT,
+ (GSourceFunc) dkp_hid_poll, hid);
+}
+
+/**
+ * dkp_hid_finalize:
+ **/
+static void
+dkp_hid_finalize (GObject *object)
+{
+ DkpHid *hid;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (DKP_IS_HID (object));
+
+ hid = DKP_HID (object);
+ g_return_if_fail (hid->priv != NULL);
+
+ if (hid->priv->fd > 0)
+ close (hid->priv->fd);
+ if (hid->priv->poll_timer_id > 0)
+ g_source_remove (hid->priv->poll_timer_id);
+
+ G_OBJECT_CLASS (dkp_hid_parent_class)->finalize (object);
+}
+
+/**
+ * dkp_hid_class_init:
+ **/
+static void
+dkp_hid_class_init (DkpHidClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ DkpDeviceClass *device_class = DKP_DEVICE_CLASS (klass);
+
+ object_class->finalize = dkp_hid_finalize;
+ device_class->coldplug = dkp_hid_coldplug;
+ device_class->refresh = dkp_hid_refresh;
+
+ g_type_class_add_private (klass, sizeof (DkpHidPrivate));
+}
+
+/**
+ * dkp_hid_new:
+ **/
+DkpHid *
+dkp_hid_new (void)
+{
+ return g_object_new (DKP_TYPE_HID, NULL);
+}
+
diff --git a/src/dkp-hid.h b/src/dkp-hid.h
new file mode 100644
index 0000000..b8e28af
--- /dev/null
+++ b/src/dkp-hid.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * 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 __DKP_HID_H__
+#define __DKP_HID_H__
+
+#include <glib-object.h>
+#include "dkp-device.h"
+
+G_BEGIN_DECLS
+
+#define DKP_TYPE_HID (dkp_hid_get_type ())
+#define DKP_HID(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), DKP_TYPE_HID, DkpHid))
+#define DKP_HID_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), DKP_TYPE_HID, DkpHidClass))
+#define DKP_IS_HID(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), DKP_TYPE_HID))
+#define DKP_IS_HID_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), DKP_TYPE_HID))
+#define DKP_HID_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DKP_TYPE_HID, DkpHidClass))
+
+typedef struct DkpHidPrivate DkpHidPrivate;
+
+typedef struct
+{
+ DkpDevice parent;
+ DkpHidPrivate *priv;
+} DkpHid;
+
+typedef struct
+{
+ DkpDeviceClass parent_class;
+} DkpHidClass;
+
+GType dkp_hid_get_type (void);
+DkpHid *dkp_hid_new (void);
+
+G_END_DECLS
+
+#endif /* __DKP_HID_H__ */
+