1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3
* Copyright (C) 2006-2008 Richard Hughes <richard@hughsie.com>
5
* Based on hid-ups.c: Copyright (c) 2001 Vojtech Pavlik <vojtech@ucw.cz>
6
* Copyright (c) 2001 Paul Stewart <hiddev@wetlogic.net>
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32
#include <glib/gstdio.h>
33
#include <glib/gi18n-lib.h>
34
#include <glib-object.h>
35
#include <devkit-gobject/devkit-gobject.h>
37
/* asm/types.h required for __s32 in linux/hiddev.h */
38
#include <asm/types.h>
41
#include <linux/hiddev.h>
46
#include <sys/ioctl.h>
49
#include "sysfs-utils.h"
50
#include "egg-debug.h"
53
#include "dkp-device-hid.h"
55
#define DKP_DEVICE_HID_REFRESH_TIMEOUT 30l
57
#define DKP_DEVICE_HID_USAGE 0x840000
58
#define DKP_DEVICE_HID_SERIAL 0x8400fe
59
#define DKP_DEVICE_HID_CHEMISTRY 0x850089
60
#define DKP_DEVICE_HID_CAPACITY_MODE 0x85002c
61
#define DKP_DEVICE_HID_BATTERY_VOLTAGE 0x840030
62
#define DKP_DEVICE_HID_BELOW_RCL 0x840042
63
#define DKP_DEVICE_HID_SHUTDOWN_IMMINENT 0x840069
64
#define DKP_DEVICE_HID_PRODUCT 0x8400fe
65
#define DKP_DEVICE_HID_SERIAL_NUMBER 0x8400ff
66
#define DKP_DEVICE_HID_CHARGING 0x850044
67
#define DKP_DEVICE_HID_DISCHARGING 0x850045
68
#define DKP_DEVICE_HID_REMAINING_CAPACITY 0x850066
69
#define DKP_DEVICE_HID_RUNTIME_TO_EMPTY 0x850068
70
#define DKP_DEVICE_HID_AC_PRESENT 0x8500d0
71
#define DKP_DEVICE_HID_BATTERY_PRESENT 0x8500d1
72
#define DKP_DEVICE_HID_DESIGN_CAPACITY 0x850083
73
#define DKP_DEVICE_HID_DEVICE_NAME 0x850088
74
#define DKP_DEVICE_HID_DEVICE_CHEMISTRY 0x850089
75
#define DKP_DEVICE_HID_RECHARGEABLE 0x85008b
76
#define DKP_DEVICE_HID_OEM_INFORMATION 0x85008f
78
#define DKP_DEVICE_HID_PAGE_GENERIC_DESKTOP 0x01
79
#define DKP_DEVICE_HID_PAGE_CONSUMER_PRODUCT 0x0c
80
#define DKP_DEVICE_HID_PAGE_USB_MONITOR 0x80
81
#define DKP_DEVICE_HID_PAGE_USB_ENUMERATED_VALUES 0x81
82
#define DKP_DEVICE_HID_PAGE_VESA_VIRTUAL_CONTROLS 0x82
83
#define DKP_DEVICE_HID_PAGE_RESERVED_MONITOR 0x83
84
#define DKP_DEVICE_HID_PAGE_POWER_DEVICE 0x84
85
#define DKP_DEVICE_HID_PAGE_BATTERY_SYSTEM 0x85
87
struct DkpDeviceHidPrivate
93
static void dkp_device_hid_class_init (DkpDeviceHidClass *klass);
95
G_DEFINE_TYPE (DkpDeviceHid, dkp_device_hid, DKP_TYPE_DEVICE)
96
#define DKP_DEVICE_HID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DKP_TYPE_HID, DkpDeviceHidPrivate))
98
static gboolean dkp_device_hid_refresh (DkpDevice *device);
101
* dkp_device_hid_is_ups:
104
dkp_device_hid_is_ups (DkpDeviceHid *hid)
108
gboolean ret = FALSE;
109
struct hiddev_devinfo device_info;
111
/* get device info */
112
retval = ioctl (hid->priv->fd, HIDIOCGDEVINFO, &device_info);
114
egg_debug ("HIDIOCGDEVINFO failed: %s", strerror (errno));
118
/* can we use the hid device as a UPS? */
119
for (i = 0; i < device_info.num_applications; i++) {
120
retval = ioctl (hid->priv->fd, HIDIOCAPPLICATION, i);
121
if (retval >> 16 == DKP_DEVICE_HID_PAGE_POWER_DEVICE) {
131
* dkp_device_hid_poll:
134
dkp_device_hid_poll (DkpDeviceHid *hid)
137
DkpDevice *device = DKP_DEVICE (hid);
139
egg_debug ("Polling: %s", dkp_device_get_object_path (device));
140
ret = dkp_device_hid_refresh (device);
142
dkp_device_emit_changed (device);
147
* dkp_device_hid_get_string:
150
dkp_device_hid_get_string (DkpDeviceHid *hid, int sindex)
152
static struct hiddev_string_descriptor sdesc;
158
sdesc.index = sindex;
161
if (ioctl (hid->priv->fd, HIDIOCGSTRING, &sdesc) < 0)
164
egg_debug ("value: '%s'", sdesc.value);
169
* dkp_device_hid_convert_device_technology:
171
static DkpDeviceTechnology
172
dkp_device_hid_convert_device_technology (const gchar *type)
175
return DKP_DEVICE_TECHNOLOGY_UNKNOWN;
176
if (strcasecmp (type, "pb") == 0 ||
177
strcasecmp (type, "pbac") == 0)
178
return DKP_DEVICE_TECHNOLOGY_LEAD_ACID;
179
return DKP_DEVICE_TECHNOLOGY_UNKNOWN;
183
* dkp_device_hid_set_values:
186
dkp_device_hid_set_values (DkpDeviceHid *hid, int code, int value)
190
DkpDevice *device = DKP_DEVICE (hid);
193
case DKP_DEVICE_HID_REMAINING_CAPACITY:
194
g_object_set (device, "percentage", (gfloat) value, NULL);
196
case DKP_DEVICE_HID_RUNTIME_TO_EMPTY:
197
g_object_set (device, "time-to-empty", (gint64) value, NULL);
199
case DKP_DEVICE_HID_CHARGING:
201
g_object_set (device, "state", DKP_DEVICE_STATE_CHARGING, NULL);
203
case DKP_DEVICE_HID_DISCHARGING:
205
g_object_set (device, "state", DKP_DEVICE_STATE_DISCHARGING, NULL);
207
case DKP_DEVICE_HID_BATTERY_PRESENT:
208
g_object_set (device, "is-present", (value != 0), NULL);
210
case DKP_DEVICE_HID_DEVICE_NAME:
211
g_object_set (device, "device-name", dkp_device_hid_get_string (hid, value), NULL);
213
case DKP_DEVICE_HID_CHEMISTRY:
214
type = dkp_device_hid_get_string (hid, value);
215
g_object_set (device, "technology", dkp_device_hid_convert_device_technology (type), NULL);
217
case DKP_DEVICE_HID_RECHARGEABLE:
218
g_object_set (device, "is-rechargeable", (value != 0), NULL);
220
case DKP_DEVICE_HID_OEM_INFORMATION:
221
g_object_set (device, "vendor", dkp_device_hid_get_string (hid, value), NULL);
223
case DKP_DEVICE_HID_PRODUCT:
224
g_object_set (device, "model", dkp_device_hid_get_string (hid, value), NULL);
226
case DKP_DEVICE_HID_SERIAL_NUMBER:
227
g_object_set (device, "serial", dkp_device_hid_get_string (hid, value), NULL);
229
case DKP_DEVICE_HID_DESIGN_CAPACITY:
230
g_object_set (device, "energy-full-design", (gfloat) value, NULL);
240
* dkp_device_hid_get_all_data:
243
dkp_device_hid_get_all_data (DkpDeviceHid *hid)
245
struct hiddev_report_info rinfo;
246
struct hiddev_field_info finfo;
247
struct hiddev_usage_ref uref;
250
gboolean ret = FALSE;
252
/* get all results */
253
for (rtype = HID_REPORT_TYPE_MIN; rtype <= HID_REPORT_TYPE_MAX; rtype++) {
254
rinfo.report_type = rtype;
255
rinfo.report_id = HID_REPORT_ID_FIRST;
256
while (ioctl (hid->priv->fd, HIDIOCGREPORTINFO, &rinfo) >= 0) {
257
for (i = 0; i < rinfo.num_fields; i++) {
258
memset (&finfo, 0, sizeof (finfo));
259
finfo.report_type = rinfo.report_type;
260
finfo.report_id = rinfo.report_id;
261
finfo.field_index = i;
262
ioctl (hid->priv->fd, HIDIOCGFIELDINFO, &finfo);
264
memset (&uref, 0, sizeof (uref));
265
for (j = 0; j < finfo.maxusage; j++) {
266
uref.report_type = finfo.report_type;
267
uref.report_id = finfo.report_id;
268
uref.field_index = i;
269
uref.usage_index = j;
270
ioctl (hid->priv->fd, HIDIOCGUCODE, &uref);
271
ioctl (hid->priv->fd, HIDIOCGUSAGE, &uref);
274
dkp_device_hid_set_values (hid, uref.usage_code, uref.value);
276
/* we got some data */
280
rinfo.report_id |= HID_REPORT_ID_NEXT;
287
* dkp_device_hid_fixup_state:
290
dkp_device_hid_fixup_state (DkpDevice *device)
294
/* map states the UPS cannot express */
295
g_object_get (device, "percentage", &percentage, NULL);
296
if (percentage < 0.01)
297
g_object_set (device, "state", DKP_DEVICE_STATE_EMPTY, NULL);
298
if (percentage > 99.9)
299
g_object_set (device, "state", DKP_DEVICE_STATE_FULLY_CHARGED, NULL);
303
* dkp_device_hid_coldplug:
305
* Return %TRUE on success, %FALSE if we failed to get data and should be removed
308
dkp_device_hid_coldplug (DkpDevice *device)
310
DkpDeviceHid *hid = DKP_DEVICE_HID (device);
312
gboolean ret = FALSE;
313
const gchar *device_file;
317
/* detect what kind of device we are */
318
d = dkp_device_get_d (device);
320
egg_error ("could not get device");
323
type = devkit_device_get_property (d, "DKP_BATTERY_TYPE");
324
if (type == NULL || g_strcmp0 (type, "ups") != 0)
327
/* get the device file */
328
device_file = devkit_device_get_device_file (d);
329
if (device_file == NULL) {
330
egg_debug ("could not get device file for HID device");
334
/* connect to the device */
335
egg_debug ("using device: %s", device_file);
336
hid->priv->fd = open (device_file, O_RDONLY | O_NONBLOCK);
337
if (hid->priv->fd < 0) {
338
egg_debug ("cannot open device file %s", device_file);
342
/* first check that we are an UPS */
343
ret = dkp_device_hid_is_ups (hid);
345
egg_debug ("not a HID device: %s", device_file);
349
/* prefer DKP names */
350
vendor = devkit_device_get_property (d, "DKP_VENDOR");
352
vendor = devkit_device_get_property (d, "ID_VENDOR");
354
/* hardcode some values */
355
g_object_set (device,
356
"type", DKP_DEVICE_TYPE_UPS,
357
"is-rechargeable", TRUE,
358
"power-supply", TRUE,
362
"has-statistics", TRUE,
365
/* coldplug everything */
366
ret = dkp_device_hid_get_all_data (hid);
368
egg_debug ("failed to coldplug: %s", device_file);
372
/* fix up device states */
373
dkp_device_hid_fixup_state (device);
379
* dkp_device_hid_refresh:
381
* Return %TRUE on success, %FALSE if we failed to refresh or no data
384
dkp_device_hid_refresh (DkpDevice *device)
386
gboolean set = FALSE;
387
gboolean ret = FALSE;
390
struct hiddev_event ev[64];
392
DkpDeviceHid *hid = DKP_DEVICE_HID (device);
395
g_get_current_time (&time);
396
g_object_set (device, "update-time", (guint64) time.tv_sec, NULL);
399
rd = read (hid->priv->fd, ev, sizeof (ev));
401
/* it's okay if there's nothing as we are non-blocking */
403
egg_debug ("no data");
408
/* did we read enough data? */
409
if (rd < (int) sizeof (ev[0])) {
410
egg_warning ("incomplete read (%i<%i)", rd, (int) sizeof (ev[0]));
414
/* process each event */
415
for (i=0; i < rd / sizeof (ev[0]); i++) {
416
set = dkp_device_hid_set_values (hid, ev[i].hid, ev[i].value);
418
/* if only takes one match to make refresh a success */
423
/* fix up device states */
424
dkp_device_hid_fixup_state (device);
430
* dkp_device_hid_init:
433
dkp_device_hid_init (DkpDeviceHid *hid)
435
hid->priv = DKP_DEVICE_HID_GET_PRIVATE (hid);
437
hid->priv->poll_timer_id = g_timeout_add_seconds (DKP_DEVICE_HID_REFRESH_TIMEOUT,
438
(GSourceFunc) dkp_device_hid_poll, hid);
442
* dkp_device_hid_finalize:
445
dkp_device_hid_finalize (GObject *object)
449
g_return_if_fail (object != NULL);
450
g_return_if_fail (DKP_IS_HID (object));
452
hid = DKP_DEVICE_HID (object);
453
g_return_if_fail (hid->priv != NULL);
455
if (hid->priv->fd > 0)
456
close (hid->priv->fd);
457
if (hid->priv->poll_timer_id > 0)
458
g_source_remove (hid->priv->poll_timer_id);
460
G_OBJECT_CLASS (dkp_device_hid_parent_class)->finalize (object);
464
* dkp_device_hid_class_init:
467
dkp_device_hid_class_init (DkpDeviceHidClass *klass)
469
GObjectClass *object_class = G_OBJECT_CLASS (klass);
470
DkpDeviceClass *device_class = DKP_DEVICE_CLASS (klass);
472
object_class->finalize = dkp_device_hid_finalize;
473
device_class->coldplug = dkp_device_hid_coldplug;
474
device_class->refresh = dkp_device_hid_refresh;
476
g_type_class_add_private (klass, sizeof (DkpDeviceHidPrivate));
480
* dkp_device_hid_new:
483
dkp_device_hid_new (void)
485
return g_object_new (DKP_TYPE_HID, NULL);