1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3
* Copyright (C) 2012 Julien Danjou <julien@danjou.info>
4
* Copyright (C) 2012 Richard Hughes <richard@hughsie.com>
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.
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.
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
27
#include <glib-object.h>
30
#include <sys/types.h>
33
#include "hidpp-device.h"
35
/* Arbitrary value used in ping */
36
#define HIDPP_PING_DATA 0x42
38
#define HIDPP_RECEIVER_ADDRESS 0xff
40
#define HIDPP_RESPONSE_SHORT_LENGTH 7
41
#define HIDPP_RESPONSE_LONG_LENGTH 20
43
#define HIDPP_HEADER_REQUEST 0x10
44
#define HIDPP_HEADER_RESPONSE 0x11
47
#define HIDPP_READ_SHORT_REGISTER 0x81
48
#define HIDPP_READ_SHORT_REGISTER_BATTERY 0x0d
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
63
#define HIDPP_ERR_INVALID_SUBID 0x8f
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
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)
98
#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_FN_GET_STATUS 0x02
99
#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_BE 0x02
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)
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)
109
#define HIDPP_DEVICE_READ_RESPONSE_TIMEOUT 3000 /* miliseconds */
111
struct HidppDevicePrivate
113
gboolean enable_debug;
114
gchar *hidraw_device;
117
GPtrArray *feature_index;
118
guint batt_percentage;
119
guint channel_source_id;
122
HidppDeviceBattStatus batt_status;
123
HidppDeviceKind kind;
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))
137
* hidpp_device_map_print:
140
hidpp_device_map_print (HidppDevice *device)
144
HidppDevicePrivate *priv = device->priv;
146
if (!device->priv->enable_debug)
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);
155
* hidpp_device_map_get_by_feature:
157
* Gets the cached index from the function number.
159
static const HidppDeviceMap *
160
hidpp_device_map_get_by_feature (HidppDevice *device, guint16 feature)
164
HidppDevicePrivate *priv = device->priv;
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)
175
* hidpp_device_map_get_by_idx:
177
* Gets the cached index from the function index.
179
static const HidppDeviceMap *
180
hidpp_device_map_get_by_idx (HidppDevice *device, gint idx)
184
HidppDevicePrivate *priv = device->priv;
186
for (i = 0; i < priv->feature_index->len; i++) {
187
map = g_ptr_array_index (priv->feature_index, i);
195
* hidpp_device_print_buffer:
197
* Pretty print the send/recieve buffer.
200
hidpp_device_print_buffer (HidppDevice *device, const guint8 *buffer)
203
const HidppDeviceMap *map;
205
if (!device->priv->enable_debug)
207
for (i = 0; i < HIDPP_RESPONSE_LONG_LENGTH; i++)
208
g_print ("%02x ", buffer[i]);
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");
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");
226
g_print ("[Random Device]\n");
230
if (buffer[2] == HIDPP_READ_LONG_REGISTER) {
231
g_print ("feature-idx=%s [%02x]\n",
232
"v1(ReadLongRegister)", buffer[2]);
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]);
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]);
248
hidpp_device_cmd (HidppDevice *device,
252
guint8 *request_data,
254
guint8 *response_data,
260
guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
262
HidppDevicePrivate *priv = device->priv;
266
.events = G_IO_IN | G_IO_OUT | G_IO_ERR,
270
/* make the request packet */
271
memset (buf, 0x00, HIDPP_RESPONSE_LONG_LENGTH);
272
buf[0] = HIDPP_HEADER_REQUEST;
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];
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,
290
/* read from the device */
291
wrote = g_poll (poll, G_N_ELEMENTS(poll),
292
HIDPP_DEVICE_READ_RESPONSE_TIMEOUT);
294
g_set_error (error, 1, 0,
295
"Attempt to read response from device timed out: %" G_GSIZE_FORMAT,
300
memset (buf, 0x00, HIDPP_RESPONSE_LONG_LENGTH);
301
wrote = read (priv->fd, buf, sizeof (buf));
303
g_set_error (error, 1, 0,
304
"Unable to read response from device: %" G_GSIZE_FORMAT,
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 &&
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;
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,
334
for (i = 0; i < response_len; i++)
335
response_data[i] = buf[4 + i];
341
* hidpp_device_map_add:
343
* Requests the index for a function, and adds it to the memeory cache
347
hidpp_device_map_add (HidppDevice *device,
352
GError *error = NULL;
355
HidppDevicePrivate *priv = device->priv;
357
buf[0] = feature >> 8;
361
g_debug ("Getting idx for feature %s [%02x]", name, feature);
362
ret = hidpp_device_cmd (device,
364
HIDPP_FEATURE_ROOT_INDEX,
365
HIDPP_FEATURE_ROOT_FN_GET_FEATURE,
370
g_warning ("Failed to get feature idx: %s", error->message);
371
g_error_free (error);
376
if (buf[0] == 0x00) {
378
g_debug ("Feature not found");
383
map = g_new0 (HidppDeviceMap, 1);
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);
395
* hidpp_device_get_model:
398
hidpp_device_get_model (HidppDevice *device)
400
g_return_val_if_fail (HIDPP_IS_DEVICE (device), NULL);
401
return device->priv->model;
405
* hidpp_device_get_batt_percentage:
408
hidpp_device_get_batt_percentage (HidppDevice *device)
410
g_return_val_if_fail (HIDPP_IS_DEVICE (device), 0);
411
return device->priv->batt_percentage;
415
* hidpp_device_get_version:
418
hidpp_device_get_version (HidppDevice *device)
420
g_return_val_if_fail (HIDPP_IS_DEVICE (device), 0);
421
return device->priv->version;
425
* hidpp_device_get_batt_status:
427
HidppDeviceBattStatus
428
hidpp_device_get_batt_status (HidppDevice *device)
430
g_return_val_if_fail (HIDPP_IS_DEVICE (device), HIDPP_DEVICE_BATT_STATUS_UNKNOWN);
431
return device->priv->batt_status;
435
* hidpp_device_get_kind:
438
hidpp_device_get_kind (HidppDevice *device)
440
g_return_val_if_fail (HIDPP_IS_DEVICE (device), HIDPP_DEVICE_KIND_UNKNOWN);
441
return device->priv->kind;
445
* hidpp_device_set_hidraw_device:
448
hidpp_device_set_hidraw_device (HidppDevice *device,
449
const gchar *hidraw_device)
451
g_return_if_fail (HIDPP_IS_DEVICE (device));
452
device->priv->hidraw_device = g_strdup (hidraw_device);
456
* hidpp_device_set_index:
459
hidpp_device_set_index (HidppDevice *device,
462
g_return_if_fail (HIDPP_IS_DEVICE (device));
463
device->priv->device_idx = device_idx;
467
* hidpp_device_set_enable_debug:
470
hidpp_device_set_enable_debug (HidppDevice *device,
471
gboolean enable_debug)
473
g_return_if_fail (HIDPP_IS_DEVICE (device));
474
device->priv->enable_debug = enable_debug;
478
* hidpp_device_refresh:
481
hidpp_device_refresh (HidppDevice *device,
482
HidppRefreshFlags refresh_flags,
485
const HidppDeviceMap *map;
487
GString *name = NULL;
488
guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
491
HidppDevicePrivate *priv = device->priv;
493
g_return_val_if_fail (HIDPP_IS_DEVICE (device), FALSE);
495
/* open the device if it's not already opened */
497
priv->fd = open (device->priv->hidraw_device, O_RDWR | O_NONBLOCK);
499
g_set_error (error, 1, 0,
500
"cannot open device file %s",
501
priv->hidraw_device);
508
if ((refresh_flags & HIDPP_REFRESH_FLAGS_VERSION) > 0) {
509
guint version_old = priv->version;
513
buf[2] = HIDPP_PING_DATA;
514
ret = hidpp_device_cmd (device,
516
HIDPP_FEATURE_ROOT_INDEX,
517
HIDPP_FEATURE_ROOT_FN_PING,
524
priv->version = buf[0];
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);
530
if (version_old < 2 && priv->version >= 2)
531
refresh_flags |= HIDPP_REFRESH_FLAGS_FEATURES;
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,
540
// hidpp_device_map_add (device,
541
// HIDPP_FEATURE_I_FIRMWARE_INFO,
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,
555
hidpp_device_map_print (device);
558
/* get device kind */
559
if ((refresh_flags & HIDPP_REFRESH_FLAGS_KIND) > 0) {
561
if (priv->version == 1) {
562
buf[0] = 0x20 | (priv->device_idx - 1);
565
ret = hidpp_device_cmd (device,
566
HIDPP_RECEIVER_ADDRESS,
567
HIDPP_READ_LONG_REGISTER,
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;
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;
586
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TABLET:
587
priv->kind = HIDPP_DEVICE_KIND_TABLET;
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;
595
} else if (priv->version == 2) {
597
/* send a BatteryLevelStatus report */
598
map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE);
603
ret = hidpp_device_cmd (device,
606
HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_TYPE,
613
case 0: /* keyboard */
615
priv->kind = HIDPP_DEVICE_KIND_KEYBOARD;
618
case 4: /* touchpad */
619
case 5: /* trackball */
620
priv->kind = HIDPP_DEVICE_KIND_MOUSE;
622
case 1: /* remote-control */
623
case 6: /* presenter */
624
case 7: /* receiver */
625
priv->kind = HIDPP_DEVICE_KIND_UNKNOWN;
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);
639
ret = hidpp_device_cmd (device,
640
HIDPP_RECEIVER_ADDRESS,
641
HIDPP_READ_LONG_REGISTER,
644
buf, HIDPP_RESPONSE_LONG_LENGTH,
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) {
657
map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE);
659
ret = hidpp_device_cmd (device,
662
HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_COUNT,
670
name = g_string_new ("");
671
for (i = 0; i < len; i +=4 ) {
675
ret = hidpp_device_cmd (device,
678
HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_NAME,
684
g_string_append_len (name, (gchar *) &buf[0], 4);
686
priv->model = g_strdup (name->str);
690
/* get battery status */
691
if ((refresh_flags & HIDPP_REFRESH_FLAGS_BATTERY) > 0) {
692
if (priv->version == 1) {
696
ret = hidpp_device_cmd (device,
698
HIDPP_READ_SHORT_REGISTER,
699
HIDPP_READ_SHORT_REGISTER_BATTERY,
705
priv->batt_percentage = buf[0];
706
priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
707
} else if (priv->version == 2) {
709
/* sent a SetLightMeasure report */
710
map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_SOLAR_DASHBOARD);
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,
717
HIDPP_FEATURE_SOLAR_DASHBOARD_FN_SET_LIGHT_MEASURE,
723
priv->batt_percentage = buf[0];
724
priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
727
/* send a BatteryLevelStatus report */
728
map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_BATTERY_LEVEL_STATUS);
733
ret = hidpp_device_cmd (device,
736
HIDPP_FEATURE_BATTERY_LEVEL_STATUS_FN_GET_STATUS,
743
/* convert the HID++ v2 status into something
744
* we can set on the device */
746
case 0: /* discharging */
747
priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
749
case 1: /* recharging */
750
case 2: /* charge nearly complete */
751
case 4: /* charging slowly */
752
priv->batt_status = HIDPP_DEVICE_BATT_STATUS_CHARGING;
754
case 3: /* charging complete */
755
priv->batt_status = HIDPP_DEVICE_BATT_STATUS_CHARGED;
760
priv->batt_percentage = buf[0];
761
g_debug ("level=%i%%, next-level=%i%%, battery-status=%i",
762
buf[0], buf[1], buf[2]);
768
g_string_free (name, TRUE);
776
hidpp_device_init (HidppDevice *device)
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;
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);
795
* hidpp_device_finalize:
798
hidpp_device_finalize (GObject *object)
802
g_return_if_fail (object != NULL);
803
g_return_if_fail (HIDPP_IS_DEVICE (object));
805
device = HIDPP_DEVICE (object);
806
g_return_if_fail (device->priv != NULL);
808
if (device->priv->channel_source_id > 0)
809
g_source_remove (device->priv->channel_source_id);
811
if (device->priv->channel) {
812
g_io_channel_shutdown (device->priv->channel, FALSE, NULL);
813
g_io_channel_unref (device->priv->channel);
815
g_ptr_array_unref (device->priv->feature_index);
817
g_free (device->priv->hidraw_device);
818
g_free (device->priv->model);
820
G_OBJECT_CLASS (hidpp_device_parent_class)->finalize (object);
824
* hidpp_device_class_init:
827
hidpp_device_class_init (HidppDeviceClass *klass)
829
GObjectClass *object_class = G_OBJECT_CLASS (klass);
830
object_class->finalize = hidpp_device_finalize;
831
g_type_class_add_private (klass, sizeof (HidppDevicePrivate));
838
hidpp_device_new (void)
840
return g_object_new (HIDPP_TYPE_DEVICE, NULL);