~ubuntu-branches/ubuntu/maverick/devicekit-power/maverick

« back to all changes in this revision

Viewing changes to src/dkp-device-hid.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-07-06 19:12:49 UTC
  • mfrom: (1.1.5 experimental)
  • Revision ID: james.westby@ubuntu.com-20090706191249-hr0a9c2ti5u0b5lc
Tags: 009-1
* New upstream release.   
* debian/control: Add myself to uploaders (discussed with Michael).
* debian/devicekit-power.install: Upstream installs udev rules into
  /lib/udev/rules.d now, update accordingly.
* Add 0001-Add-a-notify-flag-to-set_lid_is_closed.patch: Properly fix the
  silencing of the coldplug lid event, so that the first real lid event
  actually works. Thanks to Loïc Minier! (fd.o #22574)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 
2
 *
 
3
 * Copyright (C) 2006-2008 Richard Hughes <richard@hughsie.com>
 
4
 *
 
5
 * Based on hid-ups.c: Copyright (c) 2001 Vojtech Pavlik <vojtech@ucw.cz>
 
6
 *                     Copyright (c) 2001 Paul Stewart <hiddev@wetlogic.net>
 
7
 *
 
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.
 
12
 *
 
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.
 
17
 *
 
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
 
21
 *
 
22
 */
 
23
 
 
24
#ifdef HAVE_CONFIG_H
 
25
#  include "config.h"
 
26
#endif
 
27
 
 
28
#include <string.h>
 
29
#include <math.h>
 
30
 
 
31
#include <glib.h>
 
32
#include <glib/gstdio.h>
 
33
#include <glib/gi18n-lib.h>
 
34
#include <glib-object.h>
 
35
#include <devkit-gobject/devkit-gobject.h>
 
36
 
 
37
/* asm/types.h required for __s32 in linux/hiddev.h */
 
38
#include <asm/types.h>
 
39
#include <errno.h>
 
40
#include <fcntl.h>
 
41
#include <linux/hiddev.h>
 
42
#include <stdint.h>
 
43
#include <stdio.h>
 
44
#include <stdlib.h>
 
45
#include <string.h>
 
46
#include <sys/ioctl.h>
 
47
#include <unistd.h>
 
48
 
 
49
#include "sysfs-utils.h"
 
50
#include "egg-debug.h"
 
51
 
 
52
#include "dkp-enum.h"
 
53
#include "dkp-device-hid.h"
 
54
 
 
55
#define DKP_DEVICE_HID_REFRESH_TIMEOUT                  30l
 
56
 
 
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
 
77
 
 
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
 
86
 
 
87
struct DkpDeviceHidPrivate
 
88
{
 
89
        guint                    poll_timer_id;
 
90
        int                      fd;
 
91
};
 
92
 
 
93
static void     dkp_device_hid_class_init       (DkpDeviceHidClass      *klass);
 
94
 
 
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))
 
97
 
 
98
static gboolean          dkp_device_hid_refresh         (DkpDevice *device);
 
99
 
 
100
/**
 
101
 * dkp_device_hid_is_ups:
 
102
 **/
 
103
static gboolean
 
104
dkp_device_hid_is_ups (DkpDeviceHid *hid)
 
105
{
 
106
        guint i;
 
107
        int retval;
 
108
        gboolean ret = FALSE;
 
109
        struct hiddev_devinfo device_info;
 
110
 
 
111
        /* get device info */
 
112
        retval = ioctl (hid->priv->fd, HIDIOCGDEVINFO, &device_info);
 
113
        if (retval < 0) {
 
114
                egg_debug ("HIDIOCGDEVINFO failed: %s", strerror (errno));
 
115
                goto out;
 
116
        }
 
117
 
 
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) {
 
122
                        ret = TRUE;
 
123
                        goto out;
 
124
                }
 
125
        }
 
126
out:
 
127
        return ret;
 
128
}
 
129
 
 
130
/**
 
131
 * dkp_device_hid_poll:
 
132
 **/
 
133
static gboolean
 
134
dkp_device_hid_poll (DkpDeviceHid *hid)
 
135
{
 
136
        gboolean ret;
 
137
        DkpDevice *device = DKP_DEVICE (hid);
 
138
 
 
139
        egg_debug ("Polling: %s", dkp_device_get_object_path (device));
 
140
        ret = dkp_device_hid_refresh (device);
 
141
        if (ret)
 
142
                dkp_device_emit_changed (device);
 
143
        return TRUE;
 
144
}
 
145
 
 
146
/**
 
147
 * dkp_device_hid_get_string:
 
148
 **/
 
149
static const gchar *
 
150
dkp_device_hid_get_string (DkpDeviceHid *hid, int sindex)
 
151
{
 
152
        static struct hiddev_string_descriptor sdesc;
 
153
 
 
154
        /* nothing to get */
 
155
        if (sindex == 0)
 
156
                return "";
 
157
 
 
158
        sdesc.index = sindex;
 
159
 
 
160
        /* failed */
 
161
        if (ioctl (hid->priv->fd, HIDIOCGSTRING, &sdesc) < 0)
 
162
                return "";
 
163
 
 
164
        egg_debug ("value: '%s'", sdesc.value);
 
165
        return sdesc.value;
 
166
}
 
167
 
 
168
/**
 
169
 * dkp_device_hid_convert_device_technology:
 
170
 **/
 
171
static DkpDeviceTechnology
 
172
dkp_device_hid_convert_device_technology (const gchar *type)
 
173
{
 
174
        if (type == NULL)
 
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;
 
180
}
 
181
 
 
182
/**
 
183
 * dkp_device_hid_set_values:
 
184
 **/
 
185
static gboolean
 
186
dkp_device_hid_set_values (DkpDeviceHid *hid, int code, int value)
 
187
{
 
188
        const gchar *type;
 
189
        gboolean ret = TRUE;
 
190
        DkpDevice *device = DKP_DEVICE (hid);
 
191
 
 
192
        switch (code) {
 
193
        case DKP_DEVICE_HID_REMAINING_CAPACITY:
 
194
                g_object_set (device, "percentage", (gfloat) value, NULL);
 
195
                break;
 
196
        case DKP_DEVICE_HID_RUNTIME_TO_EMPTY:
 
197
                g_object_set (device, "time-to-empty", (gint64) value, NULL);
 
198
                break;
 
199
        case DKP_DEVICE_HID_CHARGING:
 
200
                if (value != 0)
 
201
                        g_object_set (device, "state", DKP_DEVICE_STATE_CHARGING, NULL);
 
202
                break;
 
203
        case DKP_DEVICE_HID_DISCHARGING:
 
204
                if (value != 0)
 
205
                        g_object_set (device, "state", DKP_DEVICE_STATE_DISCHARGING, NULL);
 
206
                break;
 
207
        case DKP_DEVICE_HID_BATTERY_PRESENT:
 
208
                g_object_set (device, "is-present", (value != 0), NULL);
 
209
                break;
 
210
        case DKP_DEVICE_HID_DEVICE_NAME:
 
211
                g_object_set (device, "device-name", dkp_device_hid_get_string (hid, value), NULL);
 
212
                break;
 
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);
 
216
                break;
 
217
        case DKP_DEVICE_HID_RECHARGEABLE:
 
218
                g_object_set (device, "is-rechargeable", (value != 0), NULL);
 
219
                break;
 
220
        case DKP_DEVICE_HID_OEM_INFORMATION:
 
221
                g_object_set (device, "vendor", dkp_device_hid_get_string (hid, value), NULL);
 
222
                break;
 
223
        case DKP_DEVICE_HID_PRODUCT:
 
224
                g_object_set (device, "model", dkp_device_hid_get_string (hid, value), NULL);
 
225
                break;
 
226
        case DKP_DEVICE_HID_SERIAL_NUMBER:
 
227
                g_object_set (device, "serial", dkp_device_hid_get_string (hid, value), NULL);
 
228
                break;
 
229
        case DKP_DEVICE_HID_DESIGN_CAPACITY:
 
230
                g_object_set (device, "energy-full-design", (gfloat) value, NULL);
 
231
                break;
 
232
        default:
 
233
                ret = FALSE;
 
234
                break;
 
235
        }
 
236
        return ret;
 
237
}
 
238
 
 
239
/**
 
240
 * dkp_device_hid_get_all_data:
 
241
 **/
 
242
static gboolean
 
243
dkp_device_hid_get_all_data (DkpDeviceHid *hid)
 
244
{
 
245
        struct hiddev_report_info rinfo;
 
246
        struct hiddev_field_info finfo;
 
247
        struct hiddev_usage_ref uref;
 
248
        int rtype;
 
249
        guint i, j;
 
250
        gboolean ret = FALSE;
 
251
 
 
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);
 
263
 
 
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);
 
272
 
 
273
                                        /* process each */
 
274
                                        dkp_device_hid_set_values (hid, uref.usage_code, uref.value);
 
275
 
 
276
                                        /* we got some data */
 
277
                                        ret = TRUE;
 
278
                                }
 
279
                        }
 
280
                        rinfo.report_id |= HID_REPORT_ID_NEXT;
 
281
                }
 
282
        }
 
283
        return ret;
 
284
}
 
285
 
 
286
/**
 
287
 * dkp_device_hid_fixup_state:
 
288
 **/
 
289
static void
 
290
dkp_device_hid_fixup_state (DkpDevice *device)
 
291
{
 
292
        gdouble percentage;
 
293
 
 
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);
 
300
}
 
301
 
 
302
/**
 
303
 * dkp_device_hid_coldplug:
 
304
 *
 
305
 * Return %TRUE on success, %FALSE if we failed to get data and should be removed
 
306
 **/
 
307
static gboolean
 
308
dkp_device_hid_coldplug (DkpDevice *device)
 
309
{
 
310
        DkpDeviceHid *hid = DKP_DEVICE_HID (device);
 
311
        DevkitDevice *d;
 
312
        gboolean ret = FALSE;
 
313
        const gchar *device_file;
 
314
        const gchar *type;
 
315
        const gchar *vendor;
 
316
 
 
317
        /* detect what kind of device we are */
 
318
        d = dkp_device_get_d (device);
 
319
        if (d == NULL)
 
320
                egg_error ("could not get device");
 
321
 
 
322
        /* get the type */
 
323
        type = devkit_device_get_property (d, "DKP_BATTERY_TYPE");
 
324
        if (type == NULL || g_strcmp0 (type, "ups") != 0)
 
325
                goto out;
 
326
 
 
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");
 
331
                goto out;
 
332
        }
 
333
 
 
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);
 
339
                goto out;
 
340
        }
 
341
 
 
342
        /* first check that we are an UPS */
 
343
        ret = dkp_device_hid_is_ups (hid);
 
344
        if (!ret) {
 
345
                egg_debug ("not a HID device: %s", device_file);
 
346
                goto out;
 
347
        }
 
348
 
 
349
        /* prefer DKP names */
 
350
        vendor = devkit_device_get_property (d, "DKP_VENDOR");
 
351
        if (vendor == NULL)
 
352
                vendor = devkit_device_get_property (d, "ID_VENDOR");
 
353
 
 
354
        /* hardcode some values */
 
355
        g_object_set (device,
 
356
                      "type", DKP_DEVICE_TYPE_UPS,
 
357
                      "is-rechargeable", TRUE,
 
358
                      "power-supply", TRUE,
 
359
                      "is-present", TRUE,
 
360
                      "vendor", vendor,
 
361
                      "has-history", TRUE,
 
362
                      "has-statistics", TRUE,
 
363
                      NULL);
 
364
 
 
365
        /* coldplug everything */
 
366
        ret = dkp_device_hid_get_all_data (hid);
 
367
        if (!ret) {
 
368
                egg_debug ("failed to coldplug: %s", device_file);
 
369
                goto out;
 
370
        }
 
371
 
 
372
        /* fix up device states */
 
373
        dkp_device_hid_fixup_state (device);
 
374
out:
 
375
        return ret;
 
376
}
 
377
 
 
378
/**
 
379
 * dkp_device_hid_refresh:
 
380
 *
 
381
 * Return %TRUE on success, %FALSE if we failed to refresh or no data
 
382
 **/
 
383
static gboolean
 
384
dkp_device_hid_refresh (DkpDevice *device)
 
385
{
 
386
        gboolean set = FALSE;
 
387
        gboolean ret = FALSE;
 
388
        GTimeVal time;
 
389
        guint i;
 
390
        struct hiddev_event ev[64];
 
391
        int rd;
 
392
        DkpDeviceHid *hid = DKP_DEVICE_HID (device);
 
393
 
 
394
        /* reset time */
 
395
        g_get_current_time (&time);
 
396
        g_object_set (device, "update-time", (guint64) time.tv_sec, NULL);
 
397
 
 
398
        /* read any data */
 
399
        rd = read (hid->priv->fd, ev, sizeof (ev));
 
400
 
 
401
        /* it's okay if there's nothing as we are non-blocking */
 
402
        if (rd == -1) {
 
403
                egg_debug ("no data");
 
404
                ret = FALSE;
 
405
                goto out;
 
406
        }
 
407
 
 
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]));
 
411
                goto out;
 
412
        }
 
413
 
 
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);
 
417
 
 
418
                /* if only takes one match to make refresh a success */
 
419
                if (set)
 
420
                        ret = TRUE;
 
421
        }
 
422
 
 
423
        /* fix up device states */
 
424
        dkp_device_hid_fixup_state (device);
 
425
out:
 
426
        return ret;
 
427
}
 
428
 
 
429
/**
 
430
 * dkp_device_hid_init:
 
431
 **/
 
432
static void
 
433
dkp_device_hid_init (DkpDeviceHid *hid)
 
434
{
 
435
        hid->priv = DKP_DEVICE_HID_GET_PRIVATE (hid);
 
436
        hid->priv->fd = -1;
 
437
        hid->priv->poll_timer_id = g_timeout_add_seconds (DKP_DEVICE_HID_REFRESH_TIMEOUT,
 
438
                                                          (GSourceFunc) dkp_device_hid_poll, hid);
 
439
}
 
440
 
 
441
/**
 
442
 * dkp_device_hid_finalize:
 
443
 **/
 
444
static void
 
445
dkp_device_hid_finalize (GObject *object)
 
446
{
 
447
        DkpDeviceHid *hid;
 
448
 
 
449
        g_return_if_fail (object != NULL);
 
450
        g_return_if_fail (DKP_IS_HID (object));
 
451
 
 
452
        hid = DKP_DEVICE_HID (object);
 
453
        g_return_if_fail (hid->priv != NULL);
 
454
 
 
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);
 
459
 
 
460
        G_OBJECT_CLASS (dkp_device_hid_parent_class)->finalize (object);
 
461
}
 
462
 
 
463
/**
 
464
 * dkp_device_hid_class_init:
 
465
 **/
 
466
static void
 
467
dkp_device_hid_class_init (DkpDeviceHidClass *klass)
 
468
{
 
469
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
470
        DkpDeviceClass *device_class = DKP_DEVICE_CLASS (klass);
 
471
 
 
472
        object_class->finalize = dkp_device_hid_finalize;
 
473
        device_class->coldplug = dkp_device_hid_coldplug;
 
474
        device_class->refresh = dkp_device_hid_refresh;
 
475
 
 
476
        g_type_class_add_private (klass, sizeof (DkpDeviceHidPrivate));
 
477
}
 
478
 
 
479
/**
 
480
 * dkp_device_hid_new:
 
481
 **/
 
482
DkpDeviceHid *
 
483
dkp_device_hid_new (void)
 
484
{
 
485
        return g_object_new (DKP_TYPE_HID, NULL);
 
486
}
 
487