~ubuntu-branches/ubuntu/vivid/upower/vivid

« back to all changes in this revision

Viewing changes to .pc/00git_updates.patch/src/linux/hidpp-device.c

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2013-09-06 15:19:05 UTC
  • Revision ID: package-import@ubuntu.com-20130906151905-rl005irvg7wd8p0h
Tags: 0.9.21-3ubuntu1
* Update 00git_updates.patch to today's upstream git:
  - Rework of hidpp detection to determine correct charge values for
    Logitech wireless keyboards/mouse (LP: #1103064)
  - Detect bluetooth mouse/keyboard batteries as such, instead of as system
    batteries. (LP: #115348)

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) 2012 Julien Danjou <julien@danjou.info>
 
4
 * Copyright (C) 2012 Richard Hughes <richard@hughsie.com>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
19
 *
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#  include "config.h"
 
24
#endif
 
25
 
 
26
#include <fcntl.h>
 
27
#include <glib-object.h>
 
28
#include <string.h>
 
29
#include <sys/stat.h>
 
30
#include <sys/types.h>
 
31
#include <unistd.h>
 
32
 
 
33
#include "hidpp-device.h"
 
34
 
 
35
/* Arbitrary value used in ping */
 
36
#define HIDPP_PING_DATA                                         0x42
 
37
 
 
38
#define HIDPP_RECEIVER_ADDRESS                                  0xff
 
39
 
 
40
#define HIDPP_RESPONSE_SHORT_LENGTH                             7
 
41
#define HIDPP_RESPONSE_LONG_LENGTH                              20
 
42
 
 
43
#define HIDPP_HEADER_REQUEST                                    0x10
 
44
#define HIDPP_HEADER_RESPONSE                                   0x11
 
45
 
 
46
/* HID++ 1.0 */
 
47
#define HIDPP_READ_SHORT_REGISTER                               0x81
 
48
#define HIDPP_READ_SHORT_REGISTER_BATTERY                       0x0d
 
49
 
 
50
#define HIDPP_READ_LONG_REGISTER                                0x83
 
51
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE                    11
 
52
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_KEYBOARD           0x1
 
53
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_MOUSE              0x2
 
54
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_NUMPAD             0x3
 
55
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_PRESENTER          0x4
 
56
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_REMOTE_CONTROL     0x7
 
57
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TRACKBALL          0x8
 
58
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TOUCHPAD           0x9
 
59
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TABLET             0xa
 
60
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_GAMEPAD            0xb
 
61
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_JOYSTICK           0xc
 
62
 
 
63
#define HIDPP_ERR_INVALID_SUBID                                 0x8f
 
64
 
 
65
/* HID++ 2.0 */
 
66
 
 
67
/* HID++2.0 error codes */
 
68
#define HIDPP_ERROR_CODE_NOERROR                                0x00
 
69
#define HIDPP_ERROR_CODE_UNKNOWN                                0x01
 
70
#define HIDPP_ERROR_CODE_INVALIDARGUMENT                        0x02
 
71
#define HIDPP_ERROR_CODE_OUTOFRANGE                             0x03
 
72
#define HIDPP_ERROR_CODE_HWERROR                                0x04
 
73
#define HIDPP_ERROR_CODE_LOGITECH_INTERNAL                      0x05
 
74
#define HIDPP_ERROR_CODE_INVALID_FEATURE_INDEX                  0x06
 
75
#define HIDPP_ERROR_CODE_INVALID_FUNCTION_ID                    0x07
 
76
#define HIDPP_ERROR_CODE_BUSY                                   0x08
 
77
#define HIDPP_ERROR_CODE_UNSUPPORTED                            0x09
 
78
 
 
79
#define HIDPP_FEATURE_ROOT                                      0x0000
 
80
#define HIDPP_FEATURE_ROOT_INDEX                                0x00
 
81
#define HIDPP_FEATURE_ROOT_FN_GET_FEATURE                       (0x00 << 4)
 
82
#define HIDPP_FEATURE_ROOT_FN_PING                              (0x01 << 4)
 
83
#define HIDPP_FEATURE_I_FEATURE_SET                             0x0001
 
84
#define HIDPP_FEATURE_I_FEATURE_SET_FN_GET_COUNT                (0x00 << 4)
 
85
#define HIDPP_FEATURE_I_FEATURE_SET_FN_GET_FEATURE_ID           (0x01 << 4)
 
86
#define HIDPP_FEATURE_I_FIRMWARE_INFO                           0x0003
 
87
#define HIDPP_FEATURE_I_FIRMWARE_INFO_FN_GET_COUNT              (0x00 << 4)
 
88
#define HIDPP_FEATURE_I_FIRMWARE_INFO_FN_GET_INFO               (0x01 << 4)
 
89
#define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE                      0x0005
 
90
#define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_COUNT         (0x00 << 4)
 
91
#define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_NAME          (0x01 << 4)
 
92
#define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_TYPE          (0x02 << 4)
 
93
#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS                      0x1000
 
94
//#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_FN_GET_STATUS      (0x00 << 4)
 
95
//#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_BE                 (0x01 << 4)
 
96
#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_FN_GET_CAPABILITY    (0x02 << 4)
 
97
 
 
98
#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_FN_GET_STATUS        0x02
 
99
#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_BE                   0x02
 
100
 
 
101
#define HIDPP_FEATURE_SPECIAL_KEYS_MSE_BUTTONS                  0x1B00
 
102
#define HIDPP_FEATURE_WIRELESS_DEVICE_STATUS                    0x1D4B
 
103
#define HIDPP_FEATURE_WIRELESS_DEVICE_STATUS_BE                 (0x00 << 4)
 
104
 
 
105
#define HIDPP_FEATURE_SOLAR_DASHBOARD                           0x4301
 
106
#define HIDPP_FEATURE_SOLAR_DASHBOARD_FN_SET_LIGHT_MEASURE      (0x00 << 4)
 
107
#define HIDPP_FEATURE_SOLAR_DASHBOARD_BE_BATTERY_LEVEL_STATUS   (0x01 << 4)
 
108
 
 
109
#define HIDPP_DEVICE_READ_RESPONSE_TIMEOUT                      3000 /* miliseconds */
 
110
 
 
111
struct HidppDevicePrivate
 
112
{
 
113
        gboolean                 enable_debug;
 
114
        gchar                   *hidraw_device;
 
115
        gchar                   *model;
 
116
        GIOChannel              *channel;
 
117
        GPtrArray               *feature_index;
 
118
        guint                    batt_percentage;
 
119
        guint                    channel_source_id;
 
120
        guint                    device_idx;
 
121
        guint                    version;
 
122
        HidppDeviceBattStatus    batt_status;
 
123
        HidppDeviceKind          kind;
 
124
        int                      fd;
 
125
};
 
126
 
 
127
typedef struct {
 
128
        gint                     idx;
 
129
        guint16                  feature;
 
130
        gchar                   *name;
 
131
} HidppDeviceMap;
 
132
 
 
133
G_DEFINE_TYPE (HidppDevice, hidpp_device, G_TYPE_OBJECT)
 
134
#define HIDPP_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), HIDPP_TYPE_DEVICE, HidppDevicePrivate))
 
135
 
 
136
/**
 
137
 * hidpp_device_map_print:
 
138
 **/
 
139
static void
 
140
hidpp_device_map_print (HidppDevice *device)
 
141
{
 
142
        guint i;
 
143
        HidppDeviceMap *map;
 
144
        HidppDevicePrivate *priv = device->priv;
 
145
 
 
146
        if (!device->priv->enable_debug)
 
147
                return;
 
148
        for (i = 0; i < priv->feature_index->len; i++) {
 
149
                map = g_ptr_array_index (priv->feature_index, i);
 
150
                g_print ("%02x\t%s [%i]\n", map->idx, map->name, map->feature);
 
151
        }
 
152
}
 
153
 
 
154
/**
 
155
 * hidpp_device_map_get_by_feature:
 
156
 *
 
157
 * Gets the cached index from the function number.
 
158
 **/
 
159
static const HidppDeviceMap *
 
160
hidpp_device_map_get_by_feature (HidppDevice *device, guint16 feature)
 
161
{
 
162
        guint i;
 
163
        HidppDeviceMap *map;
 
164
        HidppDevicePrivate *priv = device->priv;
 
165
 
 
166
        for (i = 0; i < priv->feature_index->len; i++) {
 
167
                map = g_ptr_array_index (priv->feature_index, i);
 
168
                if (map->feature == feature)
 
169
                        return map;
 
170
        }
 
171
        return NULL;
 
172
}
 
173
 
 
174
/**
 
175
 * hidpp_device_map_get_by_idx:
 
176
 *
 
177
 * Gets the cached index from the function index.
 
178
 **/
 
179
static const HidppDeviceMap *
 
180
hidpp_device_map_get_by_idx (HidppDevice *device, gint idx)
 
181
{
 
182
        guint i;
 
183
        HidppDeviceMap *map;
 
184
        HidppDevicePrivate *priv = device->priv;
 
185
 
 
186
        for (i = 0; i < priv->feature_index->len; i++) {
 
187
                map = g_ptr_array_index (priv->feature_index, i);
 
188
                if (map->idx == idx)
 
189
                        return map;
 
190
        }
 
191
        return NULL;
 
192
}
 
193
 
 
194
/**
 
195
 * hidpp_device_print_buffer:
 
196
 *
 
197
 * Pretty print the send/recieve buffer.
 
198
 **/
 
199
static void
 
200
hidpp_device_print_buffer (HidppDevice *device, const guint8 *buffer)
 
201
{
 
202
        guint i;
 
203
        const HidppDeviceMap *map;
 
204
 
 
205
        if (!device->priv->enable_debug)
 
206
                return;
 
207
        for (i = 0; i < HIDPP_RESPONSE_LONG_LENGTH; i++)
 
208
                g_print ("%02x ", buffer[i]);
 
209
        g_print ("\n");
 
210
 
 
211
        /* direction */
 
212
        if (buffer[0] == HIDPP_HEADER_REQUEST)
 
213
                g_print ("REQUEST\n");
 
214
        else if (buffer[0] == HIDPP_HEADER_RESPONSE)
 
215
                g_print ("RESPONSE\n");
 
216
        else
 
217
                g_print ("??\n");
 
218
 
 
219
        /* dev index */
 
220
        g_print ("device-idx=%02x ", buffer[1]);
 
221
        if (buffer[1] == HIDPP_RECEIVER_ADDRESS) {
 
222
                g_print ("[Receiver]\n");
 
223
        } else if (device->priv->device_idx == buffer[1]) {
 
224
                g_print ("[This Device]\n");
 
225
        } else {
 
226
                g_print ("[Random Device]\n");
 
227
        }
 
228
 
 
229
        /* feature index */
 
230
        if (buffer[2] == HIDPP_READ_LONG_REGISTER) {
 
231
                g_print ("feature-idx=%s [%02x]\n",
 
232
                         "v1(ReadLongRegister)", buffer[2]);
 
233
        } else {
 
234
                map = hidpp_device_map_get_by_idx (device, buffer[2]);
 
235
                g_print ("feature-idx=v2(%s) [%02x]\n",
 
236
                         map != NULL ? map->name : "unknown", buffer[2]);
 
237
        }
 
238
 
 
239
        g_print ("function-id=%01x\n", buffer[3] & 0xf);
 
240
        g_print ("software-id=%01x\n", buffer[3] >> 4);
 
241
        g_print ("param[0]=%02x\n\n", buffer[4]);
 
242
}
 
243
 
 
244
/**
 
245
 * hidpp_device_cmd:
 
246
 **/
 
247
static gboolean
 
248
hidpp_device_cmd (HidppDevice   *device,
 
249
                  guint8         device_idx,
 
250
                  guint8         feature_idx,
 
251
                  guint8         function_idx,
 
252
                  guint8        *request_data,
 
253
                  gsize          request_len,
 
254
                  guint8        *response_data,
 
255
                  gsize          response_len,
 
256
                  GError        **error)
 
257
{
 
258
        gboolean ret = TRUE;
 
259
        gssize wrote;
 
260
        guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
 
261
        guint i;
 
262
        HidppDevicePrivate *priv = device->priv;
 
263
        GPollFD poll[] = {
 
264
                {
 
265
                        .fd = priv->fd,
 
266
                        .events = G_IO_IN | G_IO_OUT | G_IO_ERR,
 
267
                },
 
268
        };
 
269
 
 
270
        /* make the request packet */
 
271
        memset (buf, 0x00, HIDPP_RESPONSE_LONG_LENGTH);
 
272
        buf[0] = HIDPP_HEADER_REQUEST;
 
273
        buf[1] = device_idx;
 
274
        buf[2] = feature_idx;
 
275
        buf[3] = function_idx;
 
276
        for (i = 0; i < request_len; i++)
 
277
                buf[4 + i] = request_data[i];
 
278
 
 
279
        /* write to the device */
 
280
        hidpp_device_print_buffer (device, buf);
 
281
        wrote = write (priv->fd, buf, 4 + request_len);
 
282
        if ((gsize) wrote != 4 + request_len) {
 
283
                g_set_error (error, 1, 0,
 
284
                             "Unable to write request to device: %" G_GSIZE_FORMAT,
 
285
                             wrote);
 
286
                ret = FALSE;
 
287
                goto out;
 
288
        }
 
289
 
 
290
        /* read from the device */
 
291
        wrote = g_poll (poll, G_N_ELEMENTS(poll),
 
292
                        HIDPP_DEVICE_READ_RESPONSE_TIMEOUT);
 
293
        if (wrote <= 0) {
 
294
                g_set_error (error, 1, 0,
 
295
                             "Attempt to read response from device timed out: %" G_GSIZE_FORMAT,
 
296
                             wrote);
 
297
                ret = FALSE;
 
298
                goto out;
 
299
        }
 
300
        memset (buf, 0x00, HIDPP_RESPONSE_LONG_LENGTH);
 
301
        wrote = read (priv->fd, buf, sizeof (buf));
 
302
        if (wrote <= 0) {
 
303
                g_set_error (error, 1, 0,
 
304
                             "Unable to read response from device: %" G_GSIZE_FORMAT,
 
305
                             wrote);
 
306
                ret = FALSE;
 
307
                goto out;
 
308
        }
 
309
 
 
310
        /* is device offline */
 
311
        hidpp_device_print_buffer (device, buf);
 
312
        if (buf[0] == HIDPP_HEADER_REQUEST &&
 
313
            buf[1] == device_idx &&
 
314
            buf[2] == HIDPP_ERR_INVALID_SUBID &&
 
315
            buf[3] == 0x00 &&
 
316
            buf[4] == HIDPP_FEATURE_ROOT_FN_PING) {
 
317
                /* HID++ 1.0 ping reply, so fake success with version 1  */
 
318
                if (priv->version < 2 && (buf[5] == HIDPP_ERROR_CODE_UNKNOWN
 
319
                                        || buf[5] == HIDPP_ERROR_CODE_UNSUPPORTED)) {
 
320
                        response_data[0] = 1;
 
321
                        goto out;
 
322
                }
 
323
        }
 
324
        if ((buf[0] != HIDPP_HEADER_REQUEST && buf[0] != HIDPP_HEADER_RESPONSE) ||
 
325
            buf[1] != device_idx ||
 
326
            buf[2] != feature_idx ||
 
327
            buf[3] != function_idx) {
 
328
                g_set_error (error, 1, 0,
 
329
                             "invalid response from device: %" G_GSIZE_FORMAT,
 
330
                             wrote);
 
331
                ret = FALSE;
 
332
                goto out;
 
333
        }
 
334
        for (i = 0; i < response_len; i++)
 
335
                response_data[i] = buf[4 + i];
 
336
out:
 
337
        return ret;
 
338
}
 
339
 
 
340
/**
 
341
 * hidpp_device_map_add:
 
342
 *
 
343
 * Requests the index for a function, and adds it to the memeory cache
 
344
 * if it exists.
 
345
 **/
 
346
static gboolean
 
347
hidpp_device_map_add (HidppDevice *device,
 
348
                      guint16 feature,
 
349
                      const gchar *name)
 
350
{
 
351
        gboolean ret;
 
352
        GError *error = NULL;
 
353
        guint8 buf[3];
 
354
        HidppDeviceMap *map;
 
355
        HidppDevicePrivate *priv = device->priv;
 
356
 
 
357
        buf[0] = feature >> 8;
 
358
        buf[1] = feature;
 
359
        buf[2] = 0x00;
 
360
 
 
361
        g_debug ("Getting idx for feature %s [%02x]", name, feature);
 
362
        ret = hidpp_device_cmd (device,
 
363
                                priv->device_idx,
 
364
                                HIDPP_FEATURE_ROOT_INDEX,
 
365
                                HIDPP_FEATURE_ROOT_FN_GET_FEATURE,
 
366
                                buf, sizeof (buf),
 
367
                                buf, sizeof (buf),
 
368
                                &error);
 
369
        if (!ret) {
 
370
                g_warning ("Failed to get feature idx: %s", error->message);
 
371
                g_error_free (error);
 
372
                goto out;
 
373
        }
 
374
 
 
375
        /* zero index */
 
376
        if (buf[0] == 0x00) {
 
377
                ret = FALSE;
 
378
                g_debug ("Feature not found");
 
379
                goto out;
 
380
        }
 
381
 
 
382
        /* add to map */
 
383
        map = g_new0 (HidppDeviceMap, 1);
 
384
        map->idx = buf[0];
 
385
        map->feature = feature;
 
386
        map->name = g_strdup (name);
 
387
        g_ptr_array_add (priv->feature_index, map);
 
388
        g_debug ("Added feature %s [%02x] as idx %02x",
 
389
                 name, feature, map->idx);
 
390
out:
 
391
        return ret;
 
392
}
 
393
 
 
394
/**
 
395
 * hidpp_device_get_model:
 
396
 **/
 
397
const gchar *
 
398
hidpp_device_get_model (HidppDevice *device)
 
399
{
 
400
        g_return_val_if_fail (HIDPP_IS_DEVICE (device), NULL);
 
401
        return device->priv->model;
 
402
}
 
403
 
 
404
/**
 
405
 * hidpp_device_get_batt_percentage:
 
406
 **/
 
407
guint
 
408
hidpp_device_get_batt_percentage (HidppDevice *device)
 
409
{
 
410
        g_return_val_if_fail (HIDPP_IS_DEVICE (device), 0);
 
411
        return device->priv->batt_percentage;
 
412
}
 
413
 
 
414
/**
 
415
 * hidpp_device_get_version:
 
416
 **/
 
417
guint
 
418
hidpp_device_get_version (HidppDevice *device)
 
419
{
 
420
        g_return_val_if_fail (HIDPP_IS_DEVICE (device), 0);
 
421
        return device->priv->version;
 
422
}
 
423
 
 
424
/**
 
425
 * hidpp_device_get_batt_status:
 
426
 **/
 
427
HidppDeviceBattStatus
 
428
hidpp_device_get_batt_status (HidppDevice *device)
 
429
{
 
430
        g_return_val_if_fail (HIDPP_IS_DEVICE (device), HIDPP_DEVICE_BATT_STATUS_UNKNOWN);
 
431
        return device->priv->batt_status;
 
432
}
 
433
 
 
434
/**
 
435
 * hidpp_device_get_kind:
 
436
 **/
 
437
HidppDeviceKind
 
438
hidpp_device_get_kind (HidppDevice *device)
 
439
{
 
440
        g_return_val_if_fail (HIDPP_IS_DEVICE (device), HIDPP_DEVICE_KIND_UNKNOWN);
 
441
        return device->priv->kind;
 
442
}
 
443
 
 
444
/**
 
445
 * hidpp_device_set_hidraw_device:
 
446
 **/
 
447
void
 
448
hidpp_device_set_hidraw_device (HidppDevice *device,
 
449
                                const gchar *hidraw_device)
 
450
{
 
451
        g_return_if_fail (HIDPP_IS_DEVICE (device));
 
452
        device->priv->hidraw_device = g_strdup (hidraw_device);
 
453
}
 
454
 
 
455
/**
 
456
 * hidpp_device_set_index:
 
457
 **/
 
458
void
 
459
hidpp_device_set_index (HidppDevice *device,
 
460
                        guint device_idx)
 
461
{
 
462
        g_return_if_fail (HIDPP_IS_DEVICE (device));
 
463
        device->priv->device_idx = device_idx;
 
464
}
 
465
 
 
466
/**
 
467
 * hidpp_device_set_enable_debug:
 
468
 **/
 
469
void
 
470
hidpp_device_set_enable_debug (HidppDevice *device,
 
471
                               gboolean enable_debug)
 
472
{
 
473
        g_return_if_fail (HIDPP_IS_DEVICE (device));
 
474
        device->priv->enable_debug = enable_debug;
 
475
}
 
476
 
 
477
/**
 
478
 * hidpp_device_refresh:
 
479
 **/
 
480
gboolean
 
481
hidpp_device_refresh (HidppDevice *device,
 
482
                      HidppRefreshFlags refresh_flags,
 
483
                      GError **error)
 
484
{
 
485
        const HidppDeviceMap *map;
 
486
        gboolean ret = TRUE;
 
487
        GString *name = NULL;
 
488
        guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
 
489
        guint i;
 
490
        guint len;
 
491
        HidppDevicePrivate *priv = device->priv;
 
492
 
 
493
        g_return_val_if_fail (HIDPP_IS_DEVICE (device), FALSE);
 
494
 
 
495
        /* open the device if it's not already opened */
 
496
        if (priv->fd < 0) {
 
497
                priv->fd = open (device->priv->hidraw_device, O_RDWR | O_NONBLOCK);
 
498
                if (priv->fd < 0) {
 
499
                        g_set_error (error, 1, 0,
 
500
                                     "cannot open device file %s",
 
501
                                     priv->hidraw_device);
 
502
                        ret = FALSE;
 
503
                        goto out;
 
504
                }
 
505
        }
 
506
 
 
507
        /* get version */
 
508
        if ((refresh_flags & HIDPP_REFRESH_FLAGS_VERSION) > 0) {
 
509
                guint version_old = priv->version;
 
510
 
 
511
                buf[0] = 0x00;
 
512
                buf[1] = 0x00;
 
513
                buf[2] = HIDPP_PING_DATA;
 
514
                ret = hidpp_device_cmd (device,
 
515
                                        priv->device_idx,
 
516
                                        HIDPP_FEATURE_ROOT_INDEX,
 
517
                                        HIDPP_FEATURE_ROOT_FN_PING,
 
518
                                        buf, 3,
 
519
                                        buf, 4,
 
520
                                        error);
 
521
                if (!ret)
 
522
                        goto out;
 
523
 
 
524
                priv->version = buf[0];
 
525
 
 
526
                if (version_old != priv->version)
 
527
                        g_debug("protocol for hid++ device changed from v%d to v%d",
 
528
                                        version_old, priv->version);
 
529
 
 
530
                if (version_old < 2 && priv->version >= 2)
 
531
                        refresh_flags |= HIDPP_REFRESH_FLAGS_FEATURES;
 
532
 
 
533
        }
 
534
 
 
535
        if ((refresh_flags & HIDPP_REFRESH_FLAGS_FEATURES) > 0) {
 
536
                /* add features we are going to use */
 
537
//              hidpp_device_map_add (device,
 
538
//                                    HIDPP_FEATURE_I_FEATURE_SET,
 
539
//                                    "IFeatureSet");
 
540
//              hidpp_device_map_add (device,
 
541
//                                    HIDPP_FEATURE_I_FIRMWARE_INFO,
 
542
//                                    "IFirmwareInfo");
 
543
                hidpp_device_map_add (device,
 
544
                                HIDPP_FEATURE_GET_DEVICE_NAME_TYPE,
 
545
                                "GetDeviceNameType");
 
546
                hidpp_device_map_add (device,
 
547
                                HIDPP_FEATURE_BATTERY_LEVEL_STATUS,
 
548
                                "BatteryLevelStatus");
 
549
//              hidpp_device_map_add (device,
 
550
//                                    HIDPP_FEATURE_WIRELESS_DEVICE_STATUS,
 
551
//                                    "WirelessDeviceStatus");
 
552
                hidpp_device_map_add (device,
 
553
                                HIDPP_FEATURE_SOLAR_DASHBOARD,
 
554
                                "SolarDashboard");
 
555
                hidpp_device_map_print (device);
 
556
        }
 
557
 
 
558
        /* get device kind */
 
559
        if ((refresh_flags & HIDPP_REFRESH_FLAGS_KIND) > 0) {
 
560
 
 
561
                if (priv->version == 1) {
 
562
                        buf[0] = 0x20 | (priv->device_idx - 1);
 
563
                        buf[1] = 0x00;
 
564
                        buf[2] = 0x00;
 
565
                        ret = hidpp_device_cmd (device,
 
566
                                                HIDPP_RECEIVER_ADDRESS,
 
567
                                                HIDPP_READ_LONG_REGISTER,
 
568
                                                0xb5,
 
569
                                                buf, 3,
 
570
                                                buf, 8,
 
571
                                                error);
 
572
                        if (!ret)
 
573
                                goto out;
 
574
                        switch (buf[7]) {
 
575
                        case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_KEYBOARD:
 
576
                        case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_NUMPAD:
 
577
                        case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_REMOTE_CONTROL:
 
578
                                priv->kind = HIDPP_DEVICE_KIND_KEYBOARD;
 
579
                                break;
 
580
                        case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_MOUSE:
 
581
                        case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TRACKBALL:
 
582
                        case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TOUCHPAD:
 
583
                        case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_PRESENTER:
 
584
                                priv->kind = HIDPP_DEVICE_KIND_MOUSE;
 
585
                                break;
 
586
                        case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TABLET:
 
587
                                priv->kind = HIDPP_DEVICE_KIND_TABLET;
 
588
                                break;
 
589
                        case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_GAMEPAD:
 
590
                        case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_JOYSTICK:
 
591
                                /* upower doesn't have something for this yet */
 
592
                                priv->kind = HIDPP_DEVICE_KIND_UNKNOWN;
 
593
                                break;
 
594
                        }
 
595
                } else if (priv->version == 2) {
 
596
 
 
597
                        /* send a BatteryLevelStatus report */
 
598
                        map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE);
 
599
                        if (map != NULL) {
 
600
                                buf[0] = 0x00;
 
601
                                buf[1] = 0x00;
 
602
                                buf[2] = 0x00;
 
603
                                ret = hidpp_device_cmd (device,
 
604
                                                        priv->device_idx,
 
605
                                                        map->idx,
 
606
                                                        HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_TYPE,
 
607
                                                        buf, 3,
 
608
                                                        buf, 1,
 
609
                                                        error);
 
610
                                if (!ret)
 
611
                                        goto out;
 
612
                                switch (buf[0]) {
 
613
                                case 0: /* keyboard */
 
614
                                case 2: /* numpad */
 
615
                                        priv->kind = HIDPP_DEVICE_KIND_KEYBOARD;
 
616
                                        break;
 
617
                                case 3: /* mouse */
 
618
                                case 4: /* touchpad */
 
619
                                case 5: /* trackball */
 
620
                                        priv->kind = HIDPP_DEVICE_KIND_MOUSE;
 
621
                                        break;
 
622
                                case 1: /* remote-control */
 
623
                                case 6: /* presenter */
 
624
                                case 7: /* receiver */
 
625
                                        priv->kind = HIDPP_DEVICE_KIND_UNKNOWN;
 
626
                                        break;
 
627
                                }
 
628
                        }
 
629
                }
 
630
        }
 
631
 
 
632
        /* get device model string */
 
633
        if ((refresh_flags & HIDPP_REFRESH_FLAGS_MODEL) > 0) {
 
634
                if (priv->version == 1) {
 
635
                        buf[0] = 0x40 | (priv->device_idx - 1);
 
636
                        buf[1] = 0x00;
 
637
                        buf[2] = 0x00;
 
638
 
 
639
                        ret = hidpp_device_cmd (device,
 
640
                                        HIDPP_RECEIVER_ADDRESS,
 
641
                                        HIDPP_READ_LONG_REGISTER,
 
642
                                        0xb5,
 
643
                                        buf, 3,
 
644
                                        buf, HIDPP_RESPONSE_LONG_LENGTH,
 
645
                                        error);
 
646
                        if (!ret)
 
647
                                goto out;
 
648
 
 
649
                        len = buf[1];
 
650
                        name = g_string_new ("");
 
651
                        g_string_append_len (name, (gchar *) buf+2, len);
 
652
                        priv->model = g_strdup (name->str);
 
653
                } else if (priv->version == 2) {
 
654
                        buf[0] = 0x00;
 
655
                        buf[1] = 0x00;
 
656
                        buf[2] = 0x00;
 
657
                        map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE);
 
658
                        if (map != NULL) {
 
659
                                ret = hidpp_device_cmd (device,
 
660
                                                priv->device_idx,
 
661
                                                map->idx,
 
662
                                                HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_COUNT,
 
663
                                                buf, 3,
 
664
                                                buf, 1,
 
665
                                                error);
 
666
                                if (!ret)
 
667
                                        goto out;
 
668
                        }
 
669
                        len = buf[0];
 
670
                        name = g_string_new ("");
 
671
                        for (i = 0; i < len; i +=4 ) {
 
672
                                buf[0] = i;
 
673
                                buf[1] = 0x00;
 
674
                                buf[2] = 0x00;
 
675
                                ret = hidpp_device_cmd (device,
 
676
                                                priv->device_idx,
 
677
                                                map->idx,
 
678
                                                HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_NAME,
 
679
                                                buf, 3,
 
680
                                                buf, 4,
 
681
                                                error);
 
682
                                if (!ret)
 
683
                                        goto out;
 
684
                                g_string_append_len (name, (gchar *) &buf[0], 4);
 
685
                        }
 
686
                        priv->model = g_strdup (name->str);
 
687
                }
 
688
        }
 
689
 
 
690
        /* get battery status */
 
691
        if ((refresh_flags & HIDPP_REFRESH_FLAGS_BATTERY) > 0) {
 
692
                if (priv->version == 1) {
 
693
                        buf[0] = 0x00;
 
694
                        buf[1] = 0x00;
 
695
                        buf[2] = 0x00;
 
696
                        ret = hidpp_device_cmd (device,
 
697
                                                priv->device_idx,
 
698
                                                HIDPP_READ_SHORT_REGISTER,
 
699
                                                HIDPP_READ_SHORT_REGISTER_BATTERY,
 
700
                                                buf, 3,
 
701
                                                buf, 1,
 
702
                                                error);
 
703
                        if (!ret)
 
704
                                goto out;
 
705
                        priv->batt_percentage = buf[0];
 
706
                        priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
 
707
                } else if (priv->version == 2) {
 
708
 
 
709
                        /* sent a SetLightMeasure report */
 
710
                        map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_SOLAR_DASHBOARD);
 
711
                        if (map != NULL) {
 
712
                                buf[0] = 0x01; /* Max number of reports: number of report sent after function call */
 
713
                                buf[1] = 0x01; /* Report period: time between reports, in seconds */
 
714
                                ret = hidpp_device_cmd (device,
 
715
                                                        priv->device_idx,
 
716
                                                        map->idx,
 
717
                                                        HIDPP_FEATURE_SOLAR_DASHBOARD_FN_SET_LIGHT_MEASURE,
 
718
                                                        buf, 2,
 
719
                                                        buf, 3,
 
720
                                                        error);
 
721
                                if (!ret)
 
722
                                        goto out;
 
723
                                priv->batt_percentage = buf[0];
 
724
                                priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
 
725
                        }
 
726
 
 
727
                        /* send a BatteryLevelStatus report */
 
728
                        map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_BATTERY_LEVEL_STATUS);
 
729
                        if (map != NULL) {
 
730
                                buf[0] = 0x00;
 
731
                                buf[1] = 0x00;
 
732
                                buf[2] = 0x00;
 
733
                                ret = hidpp_device_cmd (device,
 
734
                                                        priv->device_idx,
 
735
                                                        map->idx,
 
736
                                                        HIDPP_FEATURE_BATTERY_LEVEL_STATUS_FN_GET_STATUS,
 
737
                                                        buf, 3,
 
738
                                                        buf, 3,
 
739
                                                        error);
 
740
                                if (!ret)
 
741
                                        goto out;
 
742
 
 
743
                                /* convert the HID++ v2 status into something
 
744
                                 * we can set on the device */
 
745
                                switch (buf[2]) {
 
746
                                case 0: /* discharging */
 
747
                                        priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
 
748
                                        break;
 
749
                                case 1: /* recharging */
 
750
                                case 2: /* charge nearly complete */
 
751
                                case 4: /* charging slowly */
 
752
                                        priv->batt_status = HIDPP_DEVICE_BATT_STATUS_CHARGING;
 
753
                                        break;
 
754
                                case 3: /* charging complete */
 
755
                                        priv->batt_status = HIDPP_DEVICE_BATT_STATUS_CHARGED;
 
756
                                        break;
 
757
                                default:
 
758
                                        break;
 
759
                                }
 
760
                                priv->batt_percentage = buf[0];
 
761
                                g_debug ("level=%i%%, next-level=%i%%, battery-status=%i",
 
762
                                         buf[0], buf[1], buf[2]);
 
763
                        }
 
764
                }
 
765
        }
 
766
out:
 
767
        if (name != NULL)
 
768
                g_string_free (name, TRUE);
 
769
        return ret;
 
770
}
 
771
 
 
772
/**
 
773
 * hidpp_device_init:
 
774
 **/
 
775
static void
 
776
hidpp_device_init (HidppDevice *device)
 
777
{
 
778
        HidppDeviceMap *map;
 
779
 
 
780
        device->priv = HIDPP_DEVICE_GET_PRIVATE (device);
 
781
        device->priv->fd = -1;
 
782
        device->priv->feature_index = g_ptr_array_new_with_free_func (g_free);
 
783
        device->priv->batt_status = HIDPP_DEVICE_BATT_STATUS_UNKNOWN;
 
784
        device->priv->kind = HIDPP_DEVICE_KIND_UNKNOWN;
 
785
 
 
786
        /* add known root */
 
787
        map = g_new0 (HidppDeviceMap, 1);
 
788
        map->idx = HIDPP_FEATURE_ROOT_INDEX;
 
789
        map->feature = HIDPP_FEATURE_ROOT;
 
790
        map->name = g_strdup ("Root");
 
791
        g_ptr_array_add (device->priv->feature_index, map);
 
792
}
 
793
 
 
794
/**
 
795
 * hidpp_device_finalize:
 
796
 **/
 
797
static void
 
798
hidpp_device_finalize (GObject *object)
 
799
{
 
800
        HidppDevice *device;
 
801
 
 
802
        g_return_if_fail (object != NULL);
 
803
        g_return_if_fail (HIDPP_IS_DEVICE (object));
 
804
 
 
805
        device = HIDPP_DEVICE (object);
 
806
        g_return_if_fail (device->priv != NULL);
 
807
 
 
808
        if (device->priv->channel_source_id > 0)
 
809
                g_source_remove (device->priv->channel_source_id);
 
810
 
 
811
        if (device->priv->channel) {
 
812
                g_io_channel_shutdown (device->priv->channel, FALSE, NULL);
 
813
                g_io_channel_unref (device->priv->channel);
 
814
        }
 
815
        g_ptr_array_unref (device->priv->feature_index);
 
816
 
 
817
        g_free (device->priv->hidraw_device);
 
818
        g_free (device->priv->model);
 
819
 
 
820
        G_OBJECT_CLASS (hidpp_device_parent_class)->finalize (object);
 
821
}
 
822
 
 
823
/**
 
824
 * hidpp_device_class_init:
 
825
 **/
 
826
static void
 
827
hidpp_device_class_init (HidppDeviceClass *klass)
 
828
{
 
829
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
830
        object_class->finalize = hidpp_device_finalize;
 
831
        g_type_class_add_private (klass, sizeof (HidppDevicePrivate));
 
832
}
 
833
 
 
834
/**
 
835
 * hidpp_device_new:
 
836
 **/
 
837
HidppDevice *
 
838
hidpp_device_new (void)
 
839
{
 
840
        return g_object_new (HIDPP_TYPE_DEVICE, NULL);
 
841
}