diff options
author | Richard Hughes <richard@hughsie.com> | 2008-08-12 14:49:47 +0100 |
---|---|---|
committer | Richard Hughes <richard@hughsie.com> | 2008-08-12 14:49:47 +0100 |
commit | 5b950ddeb3e79fb2f6fbe76e2c1120e4c0cb23e7 (patch) | |
tree | 59d550a37dd139f47c89b34f4b4e981cb26b3ec6 | |
parent | 5f0bb1e2497228f7348820cffe6f56bfa1e53edd (diff) | |
download | upower-5b950ddeb3e79fb2f6fbe76e2c1120e4c0cb23e7.tar.gz |
add support for old style HID UPS devices
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/dkp-daemon.c | 8 | ||||
-rw-r--r-- | src/dkp-hid.c | 408 | ||||
-rw-r--r-- | src/dkp-hid.h | 55 |
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__ */ + |