summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2013-01-09 10:28:58 +0000
committerRichard Hughes <richard@hughsie.com>2013-01-10 12:08:46 +0000
commitb1f12feb1fd4535255f04c91bef90ae11ce57311 (patch)
tree2d99025ec329b8e295a8c42c9ab15d54f28090bf
parentb188a49a9c9a2521429552c644151ae4a3acdfc6 (diff)
Factor out the Logitech Unifying support to support other devices
-rw-r--r--rules/95-upower-csr.rules5
-rw-r--r--src/linux/Makefile.am18
-rw-r--r--src/linux/hidpp-device.c815
-rw-r--r--src/linux/hidpp-device.h94
-rw-r--r--src/linux/hidpp-test.c79
-rw-r--r--src/linux/up-backend.c28
-rw-r--r--src/linux/up-device-lg-unifying.c775
-rw-r--r--src/linux/up-device-unifying.c284
-rw-r--r--src/linux/up-device-unifying.h (renamed from src/linux/up-device-lg-unifying.h)0
9 files changed, 1308 insertions, 790 deletions
diff --git a/rules/95-upower-csr.rules b/rules/95-upower-csr.rules
index 17cb36f..bd171b2 100644
--- a/rules/95-upower-csr.rules
+++ b/rules/95-upower-csr.rules
@@ -20,3 +20,8 @@ ATTR{idVendor}=="046d", ATTR{idProduct}=="c702", ENV{UPOWER_PRODUCT}="Presenter"
20 20
21LABEL="up_csr_end" 21LABEL="up_csr_end"
22 22
23# Unifying HID++ devices
24SUBSYSTEM!="hid", GOTO="up_unifying_end"
25ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", DRIVER=="logitech-djdevice", ENV{UPOWER_BATTERY_TYPE}="unifying"
26ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c532", DRIVER=="logitech-djdevice", ENV{UPOWER_BATTERY_TYPE}="unifying"
27LABEL="up_unifying_end"
diff --git a/src/linux/Makefile.am b/src/linux/Makefile.am
index 66ad389..4bb64bc 100644
--- a/src/linux/Makefile.am
+++ b/src/linux/Makefile.am
@@ -30,8 +30,8 @@ libupshared_la_SOURCES = \
30 up-device-supply.h \ 30 up-device-supply.h \
31 up-device-csr.c \ 31 up-device-csr.c \
32 up-device-csr.h \ 32 up-device-csr.h \
33 up-device-lg-unifying.c \ 33 up-device-unifying.c \
34 up-device-lg-unifying.h \ 34 up-device-unifying.h \
35 up-device-hid.c \ 35 up-device-hid.c \
36 up-device-hid.h \ 36 up-device-hid.h \
37 up-device-wup.c \ 37 up-device-wup.c \
@@ -42,11 +42,25 @@ libupshared_la_SOURCES = \
42 up-dock.h \ 42 up-dock.h \
43 up-backend.c \ 43 up-backend.c \
44 up-native.c \ 44 up-native.c \
45 hidpp-device.c \
46 hidpp-device.h \
45 sysfs-utils.c \ 47 sysfs-utils.c \
46 sysfs-utils.h \ 48 sysfs-utils.h \
47 $(idevice_files) \ 49 $(idevice_files) \
48 $(BUILT_SOURCES) 50 $(BUILT_SOURCES)
49 51
52noinst_PROGRAMS = \
53 hidpp-test
54hidpp_test_SOURCES = \
55 hidpp-device.c \
56 hidpp-device.h \
57 hidpp-test.c
58hidpp_test_LDADD = \
59 -lm \
60 $(GLIB_LIBS) \
61 $(GIO_LIBS)
62hidpp_test_CFLAGS = $(AM_CFLAGS) $(WARNINGFLAGS_C)
63
50EXTRA_DIST = $(libupshared_la_SOURCES) \ 64EXTRA_DIST = $(libupshared_la_SOURCES) \
51 integration-test 65 integration-test
52 66
diff --git a/src/linux/hidpp-device.c b/src/linux/hidpp-device.c
new file mode 100644
index 0000000..0a27dbf
--- /dev/null
+++ b/src/linux/hidpp-device.c
@@ -0,0 +1,815 @@
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
111struct 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
127typedef struct {
128 gint idx;
129 guint16 feature;
130 gchar *name;
131} HidppDeviceMap;
132
133G_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 **/
139static void
140hidpp_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 **/
159static const HidppDeviceMap *
160hidpp_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 **/
179static const HidppDeviceMap *
180hidpp_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 **/
199static void
200hidpp_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 **/
247static gboolean
248hidpp_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 */
318 if (buf[5] == HIDPP_ERROR_CODE_UNKNOWN) {
319 buf[0] = 1;
320 goto out;
321 }
322 if (buf[5] == HIDPP_ERROR_CODE_UNSUPPORTED) {
323 /* device offline / unreachable */
324 g_set_error_literal (error, 1, 0,
325 "device is unreachable");
326 ret = FALSE;
327 goto out;
328 }
329 }
330 if (buf[0] != HIDPP_HEADER_RESPONSE ||
331 buf[1] != device_idx ||
332 buf[2] != feature_idx ||
333 buf[3] != function_idx) {
334 g_set_error (error, 1, 0,
335 "invalid response from device: %" G_GSIZE_FORMAT,
336 wrote);
337 ret = FALSE;
338 goto out;
339 }
340 for (i = 0; i < response_len; i++)
341 response_data[i] = buf[4 + i];
342out:
343 return ret;
344}
345
346/**
347 * hidpp_device_map_add:
348 *
349 * Requests the index for a function, and adds it to the memeory cache
350 * if it exists.
351 **/
352static gboolean
353hidpp_device_map_add (HidppDevice *device,
354 guint16 feature,
355 const gchar *name)
356{
357 gboolean ret;
358 GError *error = NULL;
359 guint8 buf[3];
360 HidppDeviceMap *map;
361 HidppDevicePrivate *priv = device->priv;
362
363 buf[0] = feature >> 8;
364 buf[1] = feature;
365 buf[2] = 0x00;
366
367 g_debug ("Getting idx for feature %s [%02x]", name, feature);
368 ret = hidpp_device_cmd (device,
369 priv->device_idx,
370 HIDPP_FEATURE_ROOT_INDEX,
371 HIDPP_FEATURE_ROOT_FN_GET_FEATURE,
372 buf, sizeof (buf),
373 buf, sizeof (buf),
374 &error);
375 if (!ret) {
376 g_warning ("Failed to get feature idx: %s", error->message);
377 g_error_free (error);
378 goto out;
379 }
380
381 /* zero index */
382 if (buf[0] == 0x00) {
383 ret = FALSE;
384 g_debug ("Feature not found");
385 goto out;
386 }
387
388 /* add to map */
389 map = g_new0 (HidppDeviceMap, 1);
390 map->idx = buf[0];
391 map->feature = feature;
392 map->name = g_strdup (name);
393 g_ptr_array_add (priv->feature_index, map);
394 g_debug ("Added feature %s [%02x] as idx %02x",
395 name, feature, map->idx);
396out:
397 return ret;
398}
399
400/**
401 * hidpp_device_get_model:
402 **/
403const gchar *
404hidpp_device_get_model (HidppDevice *device)
405{
406 g_return_val_if_fail (HIDPP_IS_DEVICE (device), NULL);
407 return device->priv->model;
408}
409
410/**
411 * hidpp_device_get_batt_percentage:
412 **/
413guint
414hidpp_device_get_batt_percentage (HidppDevice *device)
415{
416 g_return_val_if_fail (HIDPP_IS_DEVICE (device), 0);
417 return device->priv->batt_percentage;
418}
419
420/**
421 * hidpp_device_get_version:
422 **/
423guint
424hidpp_device_get_version (HidppDevice *device)
425{
426 g_return_val_if_fail (HIDPP_IS_DEVICE (device), 0);
427 return device->priv->version;
428}
429
430/**
431 * hidpp_device_get_batt_status:
432 **/
433HidppDeviceBattStatus
434hidpp_device_get_batt_status (HidppDevice *device)
435{
436 g_return_val_if_fail (HIDPP_IS_DEVICE (device), HIDPP_DEVICE_BATT_STATUS_UNKNOWN);
437 return device->priv->batt_status;
438}
439
440/**
441 * hidpp_device_get_kind:
442 **/
443HidppDeviceKind
444hidpp_device_get_kind (HidppDevice *device)
445{
446 g_return_val_if_fail (HIDPP_IS_DEVICE (device), HIDPP_DEVICE_KIND_UNKNOWN);
447 return device->priv->kind;
448}
449
450/**
451 * hidpp_device_set_hidraw_device:
452 **/
453void
454hidpp_device_set_hidraw_device (HidppDevice *device,
455 const gchar *hidraw_device)
456{
457 g_return_if_fail (HIDPP_IS_DEVICE (device));
458 device->priv->hidraw_device = g_strdup (hidraw_device);
459}
460
461/**
462 * hidpp_device_set_index:
463 **/
464void
465hidpp_device_set_index (HidppDevice *device,
466 guint device_idx)
467{
468 g_return_if_fail (HIDPP_IS_DEVICE (device));
469 device->priv->device_idx = device_idx;
470}
471
472/**
473 * hidpp_device_set_enable_debug:
474 **/
475void
476hidpp_device_set_enable_debug (HidppDevice *device,
477 gboolean enable_debug)
478{
479 g_return_if_fail (HIDPP_IS_DEVICE (device));
480 device->priv->enable_debug = enable_debug;
481}
482
483/**
484 * hidpp_device_refresh:
485 **/
486gboolean
487hidpp_device_refresh (HidppDevice *device,
488 HidppRefreshFlags refresh_flags,
489 GError **error)
490{
491 const HidppDeviceMap *map;
492 gboolean ret = TRUE;
493 GString *name = NULL;
494 guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
495 guint i;
496 guint len;
497 HidppDevicePrivate *priv = device->priv;
498
499 g_return_val_if_fail (HIDPP_IS_DEVICE (device), FALSE);
500
501 /* open the device if it's not already opened */
502 if (priv->fd < 0) {
503 priv->fd = open (device->priv->hidraw_device, O_RDWR | O_NONBLOCK);
504 if (priv->fd < 0) {
505 g_set_error (error, 1, 0,
506 "cannot open device file %s",
507 priv->hidraw_device);
508 ret = FALSE;
509 goto out;
510 }
511
512 /* add features we are going to use */
513// hidpp_device_map_add (device,
514// HIDPP_FEATURE_I_FEATURE_SET,
515// "IFeatureSet");
516// hidpp_device_map_add (device,
517// HIDPP_FEATURE_I_FIRMWARE_INFO,
518// "IFirmwareInfo");
519 hidpp_device_map_add (device,
520 HIDPP_FEATURE_GET_DEVICE_NAME_TYPE,
521 "GetDeviceNameType");
522 hidpp_device_map_add (device,
523 HIDPP_FEATURE_BATTERY_LEVEL_STATUS,
524 "BatteryLevelStatus");
525// hidpp_device_map_add (device,
526// HIDPP_FEATURE_WIRELESS_DEVICE_STATUS,
527// "WirelessDeviceStatus");
528 hidpp_device_map_add (device,
529 HIDPP_FEATURE_SOLAR_DASHBOARD,
530 "SolarDashboard");
531 hidpp_device_map_print (device);
532 }
533
534 /* get version */
535 if ((refresh_flags & HIDPP_REFRESH_FLAGS_VERSION) > 0) {
536 buf[0] = 0x00;
537 buf[1] = 0x00;
538 buf[2] = HIDPP_PING_DATA;
539 ret = hidpp_device_cmd (device,
540 priv->device_idx,
541 HIDPP_FEATURE_ROOT_INDEX,
542 HIDPP_FEATURE_ROOT_FN_PING,
543 buf, 3,
544 buf, 4,
545 error);
546 if (!ret)
547 goto out;
548 priv->version = buf[0];
549 }
550
551 /* get device kind */
552 if ((refresh_flags & HIDPP_REFRESH_FLAGS_KIND) > 0) {
553
554 if (priv->version == 1) {
555 buf[0] = 0x20 | (priv->device_idx - 1);
556 buf[1] = 0x00;
557 buf[2] = 0x00;
558 ret = hidpp_device_cmd (device,
559 HIDPP_RECEIVER_ADDRESS,
560 HIDPP_READ_LONG_REGISTER,
561 0xb5,
562 buf, 3,
563 buf, 7,
564 error);
565 if (!ret)
566 goto out;
567 switch (buf[7]) {
568 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_KEYBOARD:
569 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_NUMPAD:
570 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_REMOTE_CONTROL:
571 priv->kind = HIDPP_DEVICE_KIND_KEYBOARD;
572 break;
573 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_MOUSE:
574 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TRACKBALL:
575 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TOUCHPAD:
576 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_PRESENTER:
577 priv->kind = HIDPP_DEVICE_KIND_MOUSE;
578 break;
579 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TABLET:
580 priv->kind = HIDPP_DEVICE_KIND_TABLET;
581 break;
582 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_GAMEPAD:
583 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_JOYSTICK:
584 /* upower doesn't have something for this yet */
585 priv->kind = HIDPP_DEVICE_KIND_UNKNOWN;
586 break;
587 }
588 } else if (priv->version == 2) {
589
590 /* send a BatteryLevelStatus report */
591 map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE);
592 if (map != NULL) {
593 buf[0] = 0x00;
594 buf[1] = 0x00;
595 buf[2] = 0x00;
596 ret = hidpp_device_cmd (device,
597 priv->device_idx,
598 map->idx,
599 HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_TYPE,
600 buf, 3,
601 buf, 1,
602 error);
603 if (!ret)
604 goto out;
605 switch (buf[0]) {
606 case 0: /* keyboard */
607 case 2: /* numpad */
608 priv->kind = HIDPP_DEVICE_KIND_KEYBOARD;
609 break;
610 case 3: /* mouse */
611 case 4: /* touchpad */
612 case 5: /* trackball */
613 priv->kind = HIDPP_DEVICE_KIND_MOUSE;
614 break;
615 case 1: /* remote-control */
616 case 6: /* presenter */
617 case 7: /* receiver */
618 priv->kind = HIDPP_DEVICE_KIND_UNKNOWN;
619 break;
620 }
621 }
622 }
623 }
624
625 /* get device model string */
626 if ((refresh_flags & HIDPP_REFRESH_FLAGS_MODEL) > 0) {
627 buf[0] = 0x00;
628 buf[1] = 0x00;
629 buf[2] = 0x00;
630 map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE);
631 if (map != NULL) {
632 ret = hidpp_device_cmd (device,
633 priv->device_idx,
634 map->idx,
635 HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_COUNT,
636 buf, 3,
637 buf, 1,
638 error);
639 if (!ret)
640 goto out;
641 }
642 len = buf[0];
643 name = g_string_new ("");
644 for (i = 0; i < len; i +=4 ) {
645 buf[0] = i;
646 buf[1] = 0x00;
647 buf[2] = 0x00;
648 ret = hidpp_device_cmd (device,
649 priv->device_idx,
650 map->idx,
651 HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_NAME,
652 buf, 3,
653 buf, 4,
654 error);
655 if (!ret)
656 goto out;
657 g_string_append_len (name, (gchar *) &buf[0], 4);
658 }
659 priv->model = g_strdup (name->str);
660 }
661
662 /* get battery status */
663 if ((refresh_flags & HIDPP_REFRESH_FLAGS_BATTERY) > 0) {
664 if (priv->version == 1) {
665 buf[0] = HIDPP_READ_SHORT_REGISTER;
666 buf[1] = HIDPP_READ_SHORT_REGISTER_BATTERY;
667 buf[2] = 0x00;
668 buf[3] = 0x00;
669 buf[4] = 0x00;
670 ret = hidpp_device_cmd (device,
671 priv->device_idx,
672 HIDPP_FEATURE_ROOT_INDEX,
673 HIDPP_FEATURE_ROOT_FN_PING,
674 buf, 5,
675 buf, 1,
676 error);
677 if (!ret)
678 goto out;
679 priv->batt_percentage = buf[0];
680 priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
681 } else if (priv->version == 2) {
682
683 /* sent a SetLightMeasure report */
684 map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_SOLAR_DASHBOARD);
685 if (map != NULL) {
686 buf[0] = 0x01; /* Max number of reports: number of report sent after function call */
687 buf[1] = 0x01; /* Report period: time between reports, in seconds */
688 ret = hidpp_device_cmd (device,
689 priv->device_idx,
690 map->idx,
691 HIDPP_FEATURE_SOLAR_DASHBOARD_FN_SET_LIGHT_MEASURE,
692 buf, 2,
693 buf, 3,
694 error);
695 if (!ret)
696 goto out;
697 priv->batt_percentage = buf[0];
698 priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
699 }
700
701 /* send a BatteryLevelStatus report */
702 map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_BATTERY_LEVEL_STATUS);
703 if (map != NULL) {
704 buf[0] = 0x00;
705 buf[1] = 0x00;
706 buf[2] = 0x00;
707 ret = hidpp_device_cmd (device,
708 priv->device_idx,
709 map->idx,
710 HIDPP_FEATURE_BATTERY_LEVEL_STATUS_FN_GET_STATUS,
711 buf, 3,
712 buf, 3,
713 error);
714 if (!ret)
715 goto out;
716
717 /* convert the HID++ v2 status into something
718 * we can set on the device */
719 switch (buf[2]) {
720 case 0: /* discharging */
721 priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
722 break;
723 case 1: /* recharging */
724 case 2: /* charge nearly complete */
725 case 4: /* charging slowly */
726 priv->batt_status = HIDPP_DEVICE_BATT_STATUS_CHARGING;
727 break;
728 case 3: /* charging complete */
729 priv->batt_status = HIDPP_DEVICE_BATT_STATUS_CHARGED;
730 break;
731 default:
732 break;
733 }
734 priv->batt_percentage = buf[0];
735 g_debug ("level=%i%%, next-level=%i%%, battery-status=%i",
736 buf[0], buf[1], buf[2]);
737 }
738 }
739 }
740out:
741 if (name != NULL)
742 g_string_free (name, TRUE);
743 return ret;
744}
745
746/**
747 * hidpp_device_init:
748 **/
749static void
750hidpp_device_init (HidppDevice *device)
751{
752 HidppDeviceMap *map;
753
754 device->priv = HIDPP_DEVICE_GET_PRIVATE (device);
755 device->priv->fd = -1;
756 device->priv->feature_index = g_ptr_array_new_with_free_func (g_free);
757 device->priv->batt_status = HIDPP_DEVICE_BATT_STATUS_UNKNOWN;
758 device->priv->kind = HIDPP_DEVICE_KIND_UNKNOWN;
759
760 /* add known root */
761 map = g_new0 (HidppDeviceMap, 1);
762 map->idx = HIDPP_FEATURE_ROOT_INDEX;
763 map->feature = HIDPP_FEATURE_ROOT;
764 map->name = g_strdup ("Root");
765 g_ptr_array_add (device->priv->feature_index, map);
766}
767
768/**
769 * hidpp_device_finalize:
770 **/
771static void
772hidpp_device_finalize (GObject *object)
773{
774 HidppDevice *device;
775
776 g_return_if_fail (object != NULL);
777 g_return_if_fail (HIDPP_IS_DEVICE (object));
778
779 device = HIDPP_DEVICE (object);
780 g_return_if_fail (device->priv != NULL);
781
782 if (device->priv->channel_source_id > 0)
783 g_source_remove (device->priv->channel_source_id);
784
785 if (device->priv->channel) {
786 g_io_channel_shutdown (device->priv->channel, FALSE, NULL);
787 g_io_channel_unref (device->priv->channel);
788 }
789 g_ptr_array_unref (device->priv->feature_index);
790
791 g_free (device->priv->hidraw_device);
792 g_free (device->priv->model);
793
794 G_OBJECT_CLASS (hidpp_device_parent_class)->finalize (object);
795}
796
797/**
798 * hidpp_device_class_init:
799 **/
800static void
801hidpp_device_class_init (HidppDeviceClass *klass)
802{
803 GObjectClass *object_class = G_OBJECT_CLASS (klass);
804 object_class->finalize = hidpp_device_finalize;
805 g_type_class_add_private (klass, sizeof (HidppDevicePrivate));
806}
807
808/**
809 * hidpp_device_new:
810 **/
811HidppDevice *
812hidpp_device_new (void)
813{
814 return g_object_new (HIDPP_TYPE_DEVICE, NULL);
815}
diff --git a/src/linux/hidpp-device.h b/src/linux/hidpp-device.h
new file mode 100644
index 0000000..935cd07
--- /dev/null
+++ b/src/linux/hidpp-device.h
@@ -0,0 +1,94 @@
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#ifndef __HIDPP_DEVICE_H__
23#define __HIDPP_DEVICE_H__
24
25#include <glib-object.h>
26
27#include "hidpp-device.h"
28
29G_BEGIN_DECLS
30
31#define HIDPP_TYPE_DEVICE (hidpp_device_get_type ())
32#define HIDPP_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), HIDPP_TYPE_DEVICE, HidppDevice))
33#define HIDPP_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), HIDPP_TYPE_DEVICE, HidppDeviceClass))
34#define HIDPP_IS_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), HIDPP_TYPE_DEVICE))
35#define HIDPP_IS_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), HIDPP_TYPE_DEVICE))
36#define HIDPP_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), HIDPP_TYPE_DEVICE, HidppDeviceClass))
37
38typedef struct HidppDevicePrivate HidppDevicePrivate;
39
40typedef struct
41{
42 GObject parent;
43 HidppDevicePrivate *priv;
44} HidppDevice;
45
46typedef struct
47{
48 GObjectClass parent_class;
49} HidppDeviceClass;
50
51typedef enum {
52 HIDPP_DEVICE_KIND_KEYBOARD,
53 HIDPP_DEVICE_KIND_MOUSE,
54 HIDPP_DEVICE_KIND_TOUCHPAD,
55 HIDPP_DEVICE_KIND_TRACKBALL,
56 HIDPP_DEVICE_KIND_TABLET,
57 HIDPP_DEVICE_KIND_UNKNOWN
58} HidppDeviceKind;
59
60typedef enum {
61 HIDPP_DEVICE_BATT_STATUS_CHARGING,
62 HIDPP_DEVICE_BATT_STATUS_DISCHARGING,
63 HIDPP_DEVICE_BATT_STATUS_CHARGED,
64 HIDPP_DEVICE_BATT_STATUS_UNKNOWN
65} HidppDeviceBattStatus;
66
67typedef enum {
68 HIDPP_REFRESH_FLAGS_VERSION = 1,
69 HIDPP_REFRESH_FLAGS_KIND = 2,
70 HIDPP_REFRESH_FLAGS_BATTERY = 4,
71 HIDPP_REFRESH_FLAGS_MODEL = 8
72} HidppRefreshFlags;
73
74GType hidpp_device_get_type (void);
75const gchar *hidpp_device_get_model (HidppDevice *device);
76guint hidpp_device_get_batt_percentage (HidppDevice *device);
77guint hidpp_device_get_version (HidppDevice *device);
78HidppDeviceBattStatus hidpp_device_get_batt_status (HidppDevice *device);
79HidppDeviceKind hidpp_device_get_kind (HidppDevice *device);
80void hidpp_device_set_hidraw_device (HidppDevice *device,
81 const gchar *hidraw_device);
82void hidpp_device_set_index (HidppDevice *device,
83 guint device_idx);
84void hidpp_device_set_enable_debug (HidppDevice *device,
85 gboolean enable_debug);
86gboolean hidpp_device_refresh (HidppDevice *device,
87 HidppRefreshFlags refresh_flags,
88 GError **error);
89HidppDevice *hidpp_device_new (void);
90
91G_END_DECLS
92
93#endif /* __HIDPP_DEVICE_H__ */
94
diff --git a/src/linux/hidpp-test.c b/src/linux/hidpp-test.c
new file mode 100644
index 0000000..5ae85ce
--- /dev/null
+++ b/src/linux/hidpp-test.c
@@ -0,0 +1,79 @@
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2012 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22
23#include "config.h"
24
25#include <glib.h>
26#include <glib-object.h>
27
28#include "hidpp-device.h"
29
30int
31main (int argc, char **argv)
32{
33// const gchar *model;
34// guint batt_percentage;
35// guint version;
36// HidppDeviceBattStatus batt_status;
37 HidppDevice *d;
38// HidppDeviceKind kind;
39 gboolean ret;
40 GError *error = NULL;
41
42 g_type_init ();
43 g_test_init (&argc, &argv, NULL);
44
45 d = hidpp_device_new ();
46 hidpp_device_set_enable_debug (d, TRUE);
47 g_assert_cmpint (hidpp_device_get_version (d), ==, 0);
48 g_assert_cmpstr (hidpp_device_get_model (d), ==, NULL);
49 g_assert_cmpint (hidpp_device_get_batt_percentage (d), ==, 0);
50 g_assert_cmpint (hidpp_device_get_batt_status (d), ==, HIDPP_DEVICE_BATT_STATUS_UNKNOWN);
51 g_assert_cmpint (hidpp_device_get_kind (d), ==, HIDPP_DEVICE_KIND_UNKNOWN);
52
53 /* setup */
54 hidpp_device_set_hidraw_device (d, "/dev/hidraw0");
55 hidpp_device_set_index (d, 1);
56 ret = hidpp_device_refresh (d,
57 HIDPP_REFRESH_FLAGS_VERSION |
58 HIDPP_REFRESH_FLAGS_KIND |
59 HIDPP_REFRESH_FLAGS_BATTERY |
60 HIDPP_REFRESH_FLAGS_MODEL,
61 &error);
62 g_assert_no_error (error);
63 g_assert (ret);
64
65 g_assert_cmpint (hidpp_device_get_version (d), !=, 0);
66 g_assert_cmpstr (hidpp_device_get_model (d), !=, NULL);
67 g_assert_cmpint (hidpp_device_get_batt_percentage (d), !=, 0);
68 g_assert_cmpint (hidpp_device_get_batt_status (d), !=, HIDPP_DEVICE_BATT_STATUS_UNKNOWN);
69 g_assert_cmpint (hidpp_device_get_kind (d), !=, HIDPP_DEVICE_KIND_UNKNOWN);
70
71 g_print ("Version:\t\t%i\n", hidpp_device_get_version (d));
72 g_print ("Kind:\t\t\t%i\n", hidpp_device_get_kind (d));
73 g_print ("Model:\t\t\t%s\n", hidpp_device_get_model (d));
74 g_print ("Battery Percentage:\t%i\n", hidpp_device_get_batt_percentage (d));
75 g_print ("Battery Status:\t\t%i\n", hidpp_device_get_batt_status (d));
76
77 g_object_unref (d);
78 return 0;
79}
diff --git a/src/linux/up-backend.c b/src/linux/up-backend.c
index 0ae3413..3656b69 100644
--- a/src/linux/up-backend.c
+++ b/src/linux/up-backend.c
@@ -36,7 +36,7 @@
36 36
37#include "up-device-supply.h" 37#include "up-device-supply.h"
38#include "up-device-csr.h" 38#include "up-device-csr.h"
39#include "up-device-lg-unifying.h" 39#include "up-device-unifying.h"
40#include "up-device-wup.h" 40#include "up-device-wup.h"
41#include "up-device-hid.h" 41#include "up-device-hid.h"
42#include "up-input.h" 42#include "up-input.h"
@@ -118,6 +118,18 @@ up_backend_device_new (UpBackend *backend, GUdevDevice *native)
118 /* no valid power supply object */ 118 /* no valid power supply object */
119 device = NULL; 119 device = NULL;
120 120
121 } else if (g_strcmp0 (subsys, "hid") == 0) {
122
123 /* see if this is a Unifying mouse or keyboard */
124 device = UP_DEVICE (up_device_unifying_new ());
125 ret = up_device_coldplug (device, backend->priv->daemon, G_OBJECT (native));
126 if (ret)
127 goto out;
128 g_object_unref (device);
129
130 /* no valid power supply object */
131 device = NULL;
132
121 } else if (g_strcmp0 (subsys, "tty") == 0) { 133 } else if (g_strcmp0 (subsys, "tty") == 0) {
122 134
123 /* try to detect a Watts Up? Pro monitor */ 135 /* try to detect a Watts Up? Pro monitor */
@@ -176,18 +188,8 @@ up_backend_device_new (UpBackend *backend, GUdevDevice *native)
176 188
177 /* no valid input object */ 189 /* no valid input object */
178 device = NULL; 190 device = NULL;
179 } else {
180 g_object_unref (input);
181
182 /* see if this is a Unifying mouse or keyboard */
183 device = UP_DEVICE (up_device_unifying_new ());
184 ret = up_device_coldplug (device, backend->priv->daemon, G_OBJECT (native));
185 if (!ret) {
186 g_object_unref (device);
187 /* no valid input object */
188 device = NULL;
189 }
190 } 191 }
192 g_object_unref (input);
191 } else { 193 } else {
192 native_path = g_udev_device_get_sysfs_path (native); 194 native_path = g_udev_device_get_sysfs_path (native);
193 g_warning ("native path %s (%s) ignoring", native_path, subsys); 195 g_warning ("native path %s (%s) ignoring", native_path, subsys);
@@ -328,7 +330,7 @@ up_backend_coldplug (UpBackend *backend, UpDaemon *daemon)
328 GList *l; 330 GList *l;
329 guint i; 331 guint i;
330 gboolean ret; 332 gboolean ret;
331 const gchar *subsystems[] = {"power_supply", "usb", "usbmisc", "tty", "input", NULL}; 333 const gchar *subsystems[] = {"power_supply", "usb", "usbmisc", "tty", "input", "hid", NULL};
332 334
333 backend->priv->daemon = g_object_ref (daemon); 335 backend->priv->daemon = g_object_ref (daemon);
334 backend->priv->device_list = up_daemon_get_device_list (daemon); 336 backend->priv->device_list = up_daemon_get_device_list (daemon);
diff --git a/src/linux/up-device-lg-unifying.c b/src/linux/up-device-lg-unifying.c
deleted file mode 100644
index eddd1ec..0000000
--- a/src/linux/up-device-lg-unifying.c
+++ /dev/null
@@ -1,775 +0,0 @@
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 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21#ifdef HAVE_CONFIG_H
22# include "config.h"
23#endif
24
25#include <linux/hidraw.h>
26#include <linux/input.h>
27#include <string.h>
28#include <math.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <fcntl.h>
32#include <sys/ioctl.h>
33#include <errno.h>
34
35#include <glib.h>
36#include <glib/gstdio.h>
37#include <glib/gprintf.h>
38#include <glib/gi18n-lib.h>
39#include <glib-object.h>
40#include <gudev/gudev.h>
41
42#include "sysfs-utils.h"
43#include "up-types.h"
44#include "up-device-lg-unifying.h"
45
46/* Arbitrary value used in ping */
47#define HIDPP_PING_DATA 0x42
48
49#define HIDPP_RECEIVER_ADDRESS 0xff
50
51#define HIDPP_RESPONSE_SHORT_LENGTH 7
52#define HIDPP_RESPONSE_LONG_LENGTH 20
53
54#define HIDPP_HEADER_REQUEST 0x10
55#define HIDPP_HEADER_RESPONSE 0x11
56
57/* HID++ 1.0 */
58#define HIDPP_READ_SHORT_REGISTER 0x81
59#define HIDPP_READ_SHORT_REGISTER_BATTERY 0x0d
60
61#define HIDPP_READ_LONG_REGISTER 0x83
62#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE 11
63#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_KEYBOARD 0x1
64#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_MOUSE 0x2
65#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_NUMPAD 0x3
66#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_PRESENTER 0x4
67#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_REMOTE_CONTROL 0x7
68#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TRACKBALL 0x8
69#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TOUCHPAD 0x9
70#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TABLET 0xa
71#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_GAMEPAD 0xb
72#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_JOYSTICK 0xc
73
74#define HIDPP_ERR_INVALID_SUBID 0x8f
75
76/* HID++ 2.0 */
77#define HIDPP_FEATURE_ROOT 0x0000
78/* This is the only feature that has an hard coded index */
79#define HIDPP_FEATURE_ROOT_INDEX 0x00
80#define HIDPP_FEATURE_ROOT_FUNCTION_GETFEATURE (0x00 << 4)
81#define HIDPP_FEATURE_ROOT_FUNCTION_PING (0x01 << 4)
82
83#define HIDPP_FEATURE_GETDEVICENAMETYPE 0x0005
84#define HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETCOUNT (0x00 << 4)
85#define HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETDEVICENAME (0x01 << 4)
86
87#define HIDPP_FEATURE_SOLAR_DASHBOARD 0x4301
88#define HIDPP_FEATURE_SOLAR_DASHBOARD_FUNCTION_SetLightMeasure (0x00 << 4)
89#define HIDPP_FEATURE_SOLAR_DASHBOARD_BattLightMeasureBroadcastEvent (0x01 << 4)
90
91#define HIDPP_FEATURE_FUNCTION_AS_ARG(feature) \
92 feature >> 8, feature, 0x00
93
94#define USB_VENDOR_ID_LOGITECH "046d"
95#define USB_DEVICE_ID_UNIFYING_RECEIVER "c52b"
96#define USB_DEVICE_ID_UNIFYING_RECEIVER_2 "c532"
97
98#define UP_DEVICE_UNIFYING_READ_RESPONSE_TIMEOUT 3000 /* miliseconds */
99#define UP_DEVICE_UNIFYING_REFRESH_TIMEOUT 60L /* seconds */
100
101struct UpDeviceUnifyingPrivate
102{
103 guint poll_timer_id;
104 int fd;
105 /* Device index on the Unifying "bus" */
106 gint device_index;
107 gint feature_solar_dashboard_index;
108 GIOChannel *channel;
109 guint channel_source_id;
110};
111
112G_DEFINE_TYPE (UpDeviceUnifying, up_device_unifying, UP_TYPE_DEVICE)
113#define UP_DEVICE_UNIFYING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UP_TYPE_DEVICE_UNIFYING, UpDeviceUnifyingPrivate))
114
115/**
116 * up_device_unifying_event_io:
117 *
118 * Read events from Unifying device, and treats them.
119 **/
120static gboolean
121up_device_unifying_event_io (GIOChannel *channel, GIOCondition condition, gpointer data)
122{
123 guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
124 UpDeviceUnifying *unifying = data;
125 UpDevice *device = UP_DEVICE (unifying);
126 GTimeVal timeval;
127 guint16 lux;
128
129 while (read (unifying->priv->fd, buf, sizeof(buf)) > 0)
130 if (buf[0] == HIDPP_HEADER_RESPONSE &&
131 buf[1] == unifying->priv->device_index &&
132 buf[2] == unifying->priv->feature_solar_dashboard_index &&
133 buf[3] == HIDPP_FEATURE_SOLAR_DASHBOARD_BattLightMeasureBroadcastEvent) {
134 lux = (buf[5] << 8) | buf[6];
135 if (lux > 200) {
136 g_object_set (device,
137 "state", UP_DEVICE_STATE_CHARGING,
138 "power-supply", TRUE,
139 NULL);
140 } else if (lux > 0) {
141 g_object_set (device,
142 "state", UP_DEVICE_STATE_DISCHARGING,
143 "power-supply", TRUE,
144 NULL);
145 } else {
146 g_object_set (device,
147 "state", UP_DEVICE_STATE_DISCHARGING,
148 "power-supply", FALSE,
149 NULL);
150 }
151
152 g_get_current_time (&timeval);
153
154 g_object_set (device,
155 "update-time", (guint64) timeval.tv_sec,
156 "percentage", (gdouble) (guint8) buf[4],
157 "luminosity", (gdouble) lux,
158 NULL);
159 }
160
161 return TRUE;
162}
163
164static gint
165up_device_unifying_read_response (int fd,
166 guint8 request[],
167 size_t count,
168 gint64 start_time)
169{
170 GPollFD poll[] = {
171 {
172 .fd = fd,
173 .events = G_IO_IN | G_IO_HUP | G_IO_ERR,
174 },
175 };
176 gint ret;
177
178 /* If we started to wait for a particular response more than some
179 * time ago, abort */
180 if (g_get_monotonic_time () - start_time
181 >= UP_DEVICE_UNIFYING_READ_RESPONSE_TIMEOUT * 1000)
182 return -1;
183
184 ret = g_poll (poll, G_N_ELEMENTS(poll),
185 UP_DEVICE_UNIFYING_READ_RESPONSE_TIMEOUT);
186
187 if (ret > 0)
188 return read (fd, request, count);
189
190 return ret;
191}
192
193/**
194 * up_device_unifying_hidpp1_set_battery:
195 *
196 * Send a READ SHORT REGISTER call to the device, and set battery status.
197 **/
198static gboolean
199up_device_unifying_hidpp1_set_battery (UpDeviceUnifying *unifying)
200{
201 UpDevice *device = UP_DEVICE (unifying);
202 guint8 request[] = {
203 HIDPP_HEADER_REQUEST,
204 unifying->priv->device_index,
205 HIDPP_READ_SHORT_REGISTER,
206 HIDPP_READ_SHORT_REGISTER_BATTERY,
207 0x00, 0x00, 0x00,
208 };
209 guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
210 gint64 start_time;
211
212 if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
213 g_debug ("Unable to read battery status from Unifying device %d",
214 unifying->priv->device_index);
215 return FALSE;
216 }
217
218 start_time = g_get_monotonic_time ();
219
220 while (up_device_unifying_read_response (unifying->priv->fd, buf, sizeof (buf), start_time) > 0)
221 if (buf[0] == HIDPP_HEADER_REQUEST
222 && buf[1] == unifying->priv->device_index
223 && buf[2] == HIDPP_READ_SHORT_REGISTER
224 && buf[3] == HIDPP_READ_SHORT_REGISTER_BATTERY) {
225 g_object_set (device,
226 "percentage", (gdouble) buf[4],
227 NULL);
228 return TRUE;
229 }
230
231 return FALSE;
232}
233
234/**
235 * up_device_unifying_hidpp2_get_feature_index:
236 *
237 * Get a Unifying HID++ 2.0 feature index and return it.
238 * Returns 0 if the feature does not exists on this device.
239 **/
240static guint8
241up_device_unifying_hidpp2_get_feature_index (UpDeviceUnifying *unifying, guint16 feature)
242{
243 guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
244 guint8 request[] = {
245 HIDPP_HEADER_REQUEST,
246 unifying->priv->device_index,
247 HIDPP_FEATURE_ROOT_INDEX,
248 HIDPP_FEATURE_ROOT_FUNCTION_GETFEATURE,
249 HIDPP_FEATURE_FUNCTION_AS_ARG(feature)
250 };
251 gint64 start_time;
252
253 /* Request the device name feature index */
254 if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
255 g_debug ("Unable to send GetFeature request to device");
256 return -1;
257 }
258
259 start_time = g_get_monotonic_time ();
260
261 while (up_device_unifying_read_response (unifying->priv->fd, buf, sizeof (buf), start_time) > 0)
262 if (buf[0] == HIDPP_HEADER_RESPONSE &&
263 buf[1] == unifying->priv->device_index &&
264 buf[2] == HIDPP_FEATURE_ROOT_INDEX &&
265 buf[3] == HIDPP_FEATURE_ROOT_FUNCTION_GETFEATURE)
266 return buf[4];
267 return -1;
268}
269
270
271/**
272 * up_device_unifying_hidpp2_set_battery:
273 *
274 * Send a bunch of HID++ requests to get the device battery and set it.
275 **/
276static gboolean
277up_device_unifying_hidpp2_set_battery (UpDeviceUnifying *unifying)
278{
279 guint8 request[] = {
280 HIDPP_HEADER_REQUEST,
281 unifying->priv->device_index,
282 0x00, 0x00, 0x00, 0x00, 0x00,
283 };
284
285 if (unifying->priv->feature_solar_dashboard_index == -1)
286 unifying->priv->feature_solar_dashboard_index =
287 up_device_unifying_hidpp2_get_feature_index (unifying, HIDPP_FEATURE_SOLAR_DASHBOARD);
288
289 if (unifying->priv->feature_solar_dashboard_index == 0) {
290 /* Probably not a solar keyboard */
291 /* TODO: add support for BatteryLevelStatus */
292 } else {
293 /* This request will make the keyboard send a bunch of packets
294 * (events) with lux-meter and battery information */
295 request[2] = unifying->priv->feature_solar_dashboard_index;
296 request[3] = HIDPP_FEATURE_SOLAR_DASHBOARD_FUNCTION_SetLightMeasure;
297 request[4] = 0x01; /* Max number of reports: number of report sent after function call */
298 request[5] = 0x01; /* Report period: time between reports, in seconds */
299
300
301 if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
302 g_debug ("Unable to send solar battery/lux events start request to device");
303 return FALSE;
304 }
305
306 return TRUE;
307 }
308
309 return FALSE;
310}
311
312/**
313 * up_device_unifying_hidpp2_get_device_name:
314 *
315 * Send a bunch of HID++ requests to get the device name (model) and return
316 * it.
317 **/
318static GString *
319up_device_unifying_hidpp2_get_device_name (UpDeviceUnifying *unifying)
320{
321 GString *name = NULL;
322 guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
323 ssize_t res;
324 guint8 request[] = {
325 HIDPP_HEADER_REQUEST,
326 unifying->priv->device_index,
327 0x00,
328 HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETCOUNT,
329 0x00, 0x00, 0x00,
330 };
331 ssize_t name_length = 0;
332 gint64 start_time;
333
334 request[2] = up_device_unifying_hidpp2_get_feature_index (unifying, HIDPP_FEATURE_GETDEVICENAMETYPE);
335
336 if (request[2] == 0) {
337 g_debug ("Unable to find GetDeviceNameType feature index");
338 return NULL;
339 }
340
341 if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
342 g_debug ("Unable to send GetDeviceNameType.GetCount request to device");
343 return NULL;
344 }
345
346 start_time = g_get_monotonic_time ();
347
348 while (up_device_unifying_read_response (unifying->priv->fd, buf, sizeof (buf), start_time) > 0)
349 if (buf[0] == HIDPP_HEADER_RESPONSE &&
350 buf[1] == unifying->priv->device_index &&
351 buf[2] == request[2] &&
352 buf[3] == HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETCOUNT) {
353 name_length = buf[4];
354 break;
355 }
356
357 name = g_string_new_len (NULL, name_length);
358
359 while (name_length > 0) {
360 request[3] = HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETDEVICENAME;
361 request[4] = name->len;
362
363 if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
364 g_debug ("Unable to send GetDeviceNameType.GetDeviceName request to device");
365 g_string_free (name, TRUE);
366 return NULL;
367 }
368
369 start_time = g_get_monotonic_time ();
370
371 while ((res = up_device_unifying_read_response (unifying->priv->fd, buf,
372 sizeof (buf), start_time)) > 0)
373 if (buf[0] == HIDPP_HEADER_RESPONSE &&
374 buf[1] == unifying->priv->device_index &&
375 buf[2] == request[2] &&
376 buf[3] == HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETDEVICENAME) {
377 g_string_append_len (name, (gchar *) &buf[4], MIN(res - 4, name_length));
378 name_length -= MIN(res - 4, name_length);
379 break;
380 }
381
382 /* Handle no response case */
383 if (res <= 0) {
384 g_debug ("Error reading GetDeviceNameType.GetDeviceName response");
385 g_string_free (name, TRUE);
386 return NULL;
387 }
388 }
389
390 return name;
391}
392
393/**
394 * up_device_unifying_set_device_type:
395 *
396 * Send a Read Long Register HID++ 1.0 command to the device. This allows to
397 * retrieve the type of the device, and then set it.
398 **/
399static gboolean
400up_device_unifying_set_device_type (UpDeviceUnifying *unifying)
401{
402 guint8 request[] = {
403 HIDPP_HEADER_REQUEST,
404 HIDPP_RECEIVER_ADDRESS,
405 0x83, 0xb5,
406 0x20 | (unifying->priv->device_index - 1),
407 0x00, 0x00,
408 };
409 guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
410 UpDevice *device = UP_DEVICE (unifying);
411 gint64 start_time;
412
413 if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
414 g_debug ("Unable to send a HID++ read long register request to device %d",
415 unifying->priv->device_index);
416 return FALSE;
417 }
418
419 start_time = g_get_monotonic_time ();
420
421 while (up_device_unifying_read_response (unifying->priv->fd, buf, sizeof (buf), start_time) > 0)
422 if (buf[0] == HIDPP_HEADER_RESPONSE
423 && buf[1] == HIDPP_RECEIVER_ADDRESS
424 && buf[2] == HIDPP_READ_LONG_REGISTER
425 && buf[3] == 0xb5
426 && buf[4] == (0x20 | (unifying->priv->device_index - 1))) {
427 switch (buf[HIDPP_READ_LONG_REGISTER_DEVICE_TYPE]) {
428 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_KEYBOARD:
429 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_NUMPAD:
430 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_REMOTE_CONTROL:
431 g_object_set (device, "type", UP_DEVICE_KIND_KEYBOARD, NULL);
432 break;
433
434 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_MOUSE:
435 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TRACKBALL:
436 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TOUCHPAD:
437 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_PRESENTER:
438 g_object_set (device, "type", UP_DEVICE_KIND_MOUSE, NULL);
439 break;
440
441 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TABLET:
442 g_object_set (device, "type", UP_DEVICE_KIND_TABLET, NULL);
443 break;
444
445 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_GAMEPAD:
446 case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_JOYSTICK:
447 /* upower doesn't have something for this yet */
448 g_object_set (device, "type", UP_DEVICE_KIND_UNKNOWN, NULL);
449 break;
450 }
451 return TRUE;
452 }
453
454 return FALSE;
455}
456
457/**
458 * up_device_unifying_get_hidpp_version
459 *
460 * Return the version of HID++ used by a device.
461 **/
462static gint
463up_device_unifying_get_hidpp_version (UpDeviceUnifying *unifying)
464{
465 guint8 ping[] = {
466 HIDPP_HEADER_REQUEST,
467 unifying->priv->device_index,
468 HIDPP_FEATURE_ROOT_INDEX,
469 HIDPP_FEATURE_ROOT_FUNCTION_PING,
470 0x00, 0x00, HIDPP_PING_DATA
471 };
472 guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
473 gint64 start_time;
474
475 if (write(unifying->priv->fd, ping, sizeof(ping)) != sizeof(ping)) {
476 g_debug ("Unable to send a HID++ ping to device %d",
477 unifying->priv->device_index);
478 return -1;
479 }
480
481 /* read event */
482
483 start_time = g_get_monotonic_time ();
484
485 while (up_device_unifying_read_response (unifying->priv->fd, buf, sizeof (buf), start_time) > 0)
486 if(buf[0] == HIDPP_HEADER_REQUEST
487 && buf[1] == unifying->priv->device_index
488 && buf[2] == HIDPP_ERR_INVALID_SUBID
489 && buf[3] == 0x00
490 && buf[4] == HIDPP_FEATURE_ROOT_FUNCTION_PING) {
491 /* HID++ 1.0 ping reply */
492 if (buf[5] == 0x01)
493 return 1;
494 else if (buf[5] == 0x09)
495 /* device offline / unreachable */
496 return 0;
497 } else if (buf[0] == HIDPP_HEADER_RESPONSE
498 && buf[1] == unifying->priv->device_index
499 && buf[2] == HIDPP_FEATURE_ROOT_INDEX
500 && buf[3] == HIDPP_FEATURE_ROOT_FUNCTION_PING
501 && buf[6] == HIDPP_PING_DATA)
502 /* HID++ >= 2.0 ping reply: buf[4] is major
503 version, buf[5] is minor version but we
504 only care about major for now*/
505 return buf[4];
506
507 return -1;
508}
509
510/**
511 * up_device_unifying_refresh:
512 *
513 * Return %TRUE on success, %FALSE if we failed to refresh or no data
514 **/
515static gboolean
516up_device_unifying_refresh (UpDevice *device)
517{
518 UpDeviceUnifying *unifying = UP_DEVICE_UNIFYING (device);
519 gint hidpp_version = up_device_unifying_get_hidpp_version (unifying);
520 GString *name;
521 char *model;
522 GTimeVal timeval;
523
524 if (hidpp_version > 0)
525 g_debug ("Unifying device %d uses HID++ version %d",
526 unifying->priv->device_index, hidpp_version);
527
528 switch (hidpp_version) {
529 case 0:
530 g_debug ("Unifying device %d is offline",
531 unifying->priv->device_index);
532 g_object_set (device,
533 "is-present", FALSE,
534 "state", UP_DEVICE_STATE_UNKNOWN,
535 NULL);
536 break;
537 case 1:
538 g_object_set (device,
539 "state", UP_DEVICE_STATE_DISCHARGING,
540 "is-present", TRUE,
541 NULL);
542 up_device_unifying_hidpp1_set_battery (unifying);
543 break;
544 case 2:
545 g_object_set (device,
546 "is-present", TRUE,
547 NULL);
548
549 g_object_get (device, "model", &model, NULL);
550 if (!model) {
551 name = up_device_unifying_hidpp2_get_device_name (unifying);
552 if (name) {
553 g_object_set (device, "model", name->str, NULL);
554 g_string_free (name, TRUE);
555 }
556 } else
557 g_free (model);
558 up_device_unifying_hidpp2_set_battery (unifying);
559 break;
560 }
561
562 g_get_current_time (&timeval);
563 g_object_set (device, "update-time", (guint64) timeval.tv_sec, NULL);
564
565 return TRUE;
566}
567
568/**
569 * up_device_unifying_coldplug:
570 *
571 * Return %TRUE on success, %FALSE if we failed to get data and should be removed
572 **/
573static gboolean
574up_device_unifying_coldplug (UpDevice *device)
575{
576 UpDeviceUnifying *unifying = UP_DEVICE_UNIFYING (device);
577 GUdevDevice *native;
578 const gchar *device_file;
579 const gchar *vendor;
580 const gchar *parent_sysfs_path;
581 const gchar *bus_address;
582 GList *hidraw_list, *entry;
583 size_t len;
584 GIOStatus status;
585 GError *error = NULL;
586 GUdevClient *gudev_client;
587 GUdevDevice *parent, *hidraw, *receiver = NULL;
588 gboolean ret = FALSE;
589
590 native = G_UDEV_DEVICE (up_device_get_native (device));
591
592 if(g_strcmp0(g_udev_device_get_property (native, "ID_VENDOR_ID"),
593 USB_VENDOR_ID_LOGITECH) ||
594 (g_strcmp0(g_udev_device_get_property (native, "ID_MODEL_ID"),
595 USB_DEVICE_ID_UNIFYING_RECEIVER) &&
596 g_strcmp0(g_udev_device_get_property (native, "ID_MODEL_ID"),
597 USB_DEVICE_ID_UNIFYING_RECEIVER_2))) {
598 g_debug ("Not an Unifying device, ignoring");
599 return FALSE;
600 }
601
602 bus_address = g_udev_device_get_property (native, "PHYS");
603
604 if (!bus_address) {
605 g_debug ("Device has no physical bus address, ignoring");
606 return FALSE;
607 }
608
609 len = strlen (bus_address);
610
611 if (len < 3 || bus_address[len - 3] != ':' || !g_ascii_isdigit (bus_address[len - 2])) {
612 g_debug ("Invalid Unifying device index, ignoring");
613 return FALSE;
614 }
615
616 unifying->priv->device_index = g_ascii_digit_value (bus_address[len - 2]);
617
618 /* Find the hidraw device of the parent (the receiver) to
619 * communicate with the devices */
620 gudev_client = g_udev_client_new (NULL);
621
622 parent = g_udev_device_get_parent (native);
623 parent_sysfs_path = g_udev_device_get_sysfs_path (parent);
624 g_object_unref (parent);
625
626 hidraw_list = g_udev_client_query_by_subsystem (gudev_client, "hidraw");
627
628 for (entry = hidraw_list; entry; entry = entry->next) {
629 hidraw = entry->data;
630 if (!g_strcmp0 (g_udev_device_get_sysfs_attr (hidraw, "device"),
631 parent_sysfs_path))
632 receiver = hidraw;
633 else
634 g_object_unref (hidraw);
635 }
636
637 if (!receiver) {
638 g_debug ("Unable to find an hidraw device for Unifying receiver");
639 return FALSE;
640 }
641
642 /* get device file */
643 device_file = g_udev_device_get_device_file (receiver);
644
645 /* connect to the device */
646 g_debug ("Using Unifying receiver hidraw device file: %s", device_file);
647
648 if (device_file == NULL) {
649 g_debug ("Could not get device file for Unifying receiver device");
650 goto out;
651 }
652
653 unifying->priv->fd = open (device_file, O_RDWR | O_NONBLOCK);
654 if (unifying->priv->fd < 0) {
655 g_debug ("cannot open device file %s", device_file);
656 return FALSE;
657 }
658
659 vendor = g_udev_device_get_property (native, "ID_VENDOR");
660
661 /* hardcode some default values */
662 g_object_set (device,
663 "vendor", vendor,
664 "is-present", TRUE,
665 "has-history", TRUE,
666 "is-rechargeable", TRUE,
667 "state", UP_DEVICE_STATE_DISCHARGING,
668 "power-supply", FALSE,
669 NULL);
670
671 /* Set device type */
672 if (!up_device_unifying_set_device_type(unifying)) {
673 g_debug ("Unable to guess device type, ignoring the device");
674 goto out;
675 }
676
677 unifying->priv->channel = g_io_channel_unix_new (unifying->priv->fd);
678
679 /* set binary encoding */
680 status = g_io_channel_set_encoding (unifying->priv->channel, NULL, &error);
681 if (status != G_IO_STATUS_NORMAL) {
682 g_warning ("failed to set encoding: %s", error->message);
683 g_error_free (error);
684 goto out;
685 }
686
687 /* watch this */
688 unifying->priv->channel_source_id = g_io_add_watch (unifying->priv->channel,
689 G_IO_IN,
690 up_device_unifying_event_io,
691 unifying);
692
693 /* set up a poll to send the magic packet */
694 unifying->priv->poll_timer_id = g_timeout_add_seconds (UP_DEVICE_UNIFYING_REFRESH_TIMEOUT,
695 (GSourceFunc) up_device_unifying_refresh,
696 device);
697
698 ret = TRUE;
699
700 out:
701 g_object_unref (gudev_client);
702 g_object_unref (receiver);
703 g_list_free (hidraw_list);
704
705 if (!ret && unifying->priv->fd >= 0)
706 close (unifying->priv->fd);
707
708 return ret;
709}
710
711/**
712 * up_device_unifying_init:
713 **/
714static void
715up_device_unifying_init (UpDeviceUnifying *unifying)
716{
717 unifying->priv = UP_DEVICE_UNIFYING_GET_PRIVATE (unifying);
718 unifying->priv->poll_timer_id = 0;
719 unifying->priv->fd = -1;
720 unifying->priv->feature_solar_dashboard_index = -1;
721}
722
723/**
724 * up_device_unifying_finalize:
725 **/
726static void
727up_device_unifying_finalize (GObject *object)
728{
729 UpDeviceUnifying *unifying;
730
731 g_return_if_fail (object != NULL);
732 g_return_if_fail (UP_IS_DEVICE_UNIFYING (object));
733
734 unifying = UP_DEVICE_UNIFYING (object);
735 g_return_if_fail (unifying->priv != NULL);
736
737 if (unifying->priv->poll_timer_id > 0)
738 g_source_remove (unifying->priv->poll_timer_id);
739
740 if (unifying->priv->channel_source_id > 0)
741 g_source_remove (unifying->priv->channel_source_id);
742
743 if (unifying->priv->channel) {
744 g_io_channel_shutdown (unifying->priv->channel, FALSE, NULL);
745 g_io_channel_unref (unifying->priv->channel);
746 }
747
748 G_OBJECT_CLASS (up_device_unifying_parent_class)->finalize (object);
749}
750
751/**
752 * up_device_unifying_class_init:
753 **/
754static void
755up_device_unifying_class_init (UpDeviceUnifyingClass *klass)
756{
757 GObjectClass *object_class = G_OBJECT_CLASS (klass);
758 UpDeviceClass *device_class = UP_DEVICE_CLASS (klass);
759
760 object_class->finalize = up_device_unifying_finalize;
761 device_class->coldplug = up_device_unifying_coldplug;
762 device_class->refresh = up_device_unifying_refresh;
763
764 g_type_class_add_private (klass, sizeof (UpDeviceUnifyingPrivate));
765}
766
767/**
768 * up_device_unifying_new:
769 **/
770UpDeviceUnifying *
771up_device_unifying_new (void)
772{
773 return g_object_new (UP_TYPE_DEVICE_UNIFYING, NULL);
774}
775
diff --git a/src/linux/up-device-unifying.c b/src/linux/up-device-unifying.c
new file mode 100644
index 0000000..c7f0103
--- /dev/null
+++ b/src/linux/up-device-unifying.c
@@ -0,0 +1,284 @@
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 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21#ifdef HAVE_CONFIG_H
22# include "config.h"
23#endif
24
25#include <glib-object.h>
26#include <gudev/gudev.h>
27
28#include "hidpp-device.h"
29
30#include "up-device-unifying.h"
31#include "up-types.h"
32
33#define UP_DEVICE_UNIFYING_REFRESH_TIMEOUT 60 /* seconds */
34
35struct UpDeviceUnifyingPrivate
36{
37 guint poll_timer_id;
38 HidppDevice *hidpp_device;
39};
40
41G_DEFINE_TYPE (UpDeviceUnifying, up_device_unifying, UP_TYPE_DEVICE)
42#define UP_DEVICE_UNIFYING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UP_TYPE_DEVICE_UNIFYING, UpDeviceUnifyingPrivate))
43
44/**
45 * up_device_unifying_refresh:
46 *
47 * Return %TRUE on success, %FALSE if we failed to refresh or no data
48 **/
49static gboolean
50up_device_unifying_refresh (UpDevice *device)
51{
52 gboolean ret;
53 GError *error = NULL;
54 GTimeVal timeval;
55 UpDeviceState state = UP_DEVICE_STATE_UNKNOWN;
56 UpDeviceUnifying *unifying = UP_DEVICE_UNIFYING (device);
57 UpDeviceUnifyingPrivate *priv = unifying->priv;
58
59 /* refresh just the battery stats */
60 ret = hidpp_device_refresh (priv->hidpp_device,
61 HIDPP_REFRESH_FLAGS_BATTERY,
62 &error);
63 if (!ret) {
64 g_warning ("failed to coldplug unifying device: %s",
65 error->message);
66 g_error_free (error);
67 goto out;
68 }
69 switch (hidpp_device_get_batt_status (priv->hidpp_device)) {
70 case HIDPP_DEVICE_BATT_STATUS_CHARGING:
71 state = UP_DEVICE_STATE_CHARGING;
72 break;
73 case HIDPP_DEVICE_BATT_STATUS_DISCHARGING:
74 state = UP_DEVICE_STATE_DISCHARGING;
75 break;
76 case HIDPP_DEVICE_BATT_STATUS_CHARGED:
77 state = UP_DEVICE_STATE_FULLY_CHARGED;
78 break;
79 default:
80 break;
81 }
82 g_get_current_time (&timeval);
83 g_object_set (device,
84 "is-present", hidpp_device_get_version (priv->hidpp_device) > 0,
85 "percentage", (gdouble) hidpp_device_get_batt_percentage (priv->hidpp_device),
86 "state", state,
87 "update-time", (guint64) timeval.tv_sec,
88 NULL);
89out:
90 return TRUE;
91}
92
93static UpDeviceKind
94up_device_unifying_get_device_kind (UpDeviceUnifying *unifying)
95{
96 UpDeviceKind kind;
97 switch (hidpp_device_get_kind (unifying->priv->hidpp_device)) {
98 case HIDPP_DEVICE_KIND_MOUSE:
99 case HIDPP_DEVICE_KIND_TOUCHPAD:
100 case HIDPP_DEVICE_KIND_TRACKBALL:
101 kind = UP_DEVICE_KIND_MOUSE;
102 break;
103 case HIDPP_DEVICE_KIND_KEYBOARD:
104 kind = UP_DEVICE_KIND_KEYBOARD;
105 break;
106 case HIDPP_DEVICE_KIND_TABLET:
107 kind = UP_DEVICE_KIND_TABLET;
108 break;
109 default:
110 kind = UP_DEVICE_KIND_UNKNOWN;
111 }
112 return kind;
113}
114
115/**
116 * up_device_unifying_coldplug:
117 *
118 * Return %TRUE on success, %FALSE if we failed to get data and should be removed
119 **/
120static gboolean
121up_device_unifying_coldplug (UpDevice *device)
122{
123 const gchar *bus_address;
124 const gchar *device_file;
125 const gchar *type;
126 gboolean ret = FALSE;
127 gchar *endptr = NULL;
128 gchar *tmp;
129 GError *error = NULL;
130 GUdevDevice *native;
131 GUdevDevice *parent = NULL;
132 GUdevDevice *receiver = NULL;
133 UpDeviceUnifying *unifying = UP_DEVICE_UNIFYING (device);
134 GUdevClient *client = NULL;
135 GList *hidraw_list = NULL;
136 GList *l;
137
138 native = G_UDEV_DEVICE (up_device_get_native (device));
139
140 /* check if we have the right device */
141 type = g_udev_device_get_property (native, "UPOWER_BATTERY_TYPE");
142 if (type == NULL)
143 goto out;
144 if (g_strcmp0 (type, "unifying") != 0)
145 goto out;
146
147 /* get the device index */
148 unifying->priv->hidpp_device = hidpp_device_new ();
149 bus_address = g_udev_device_get_property (native, "HID_PHYS");
150 tmp = g_strrstr (bus_address, ":");
151 if (tmp == NULL) {
152 g_debug ("Could not get physical device index");
153 goto out;
154 }
155 hidpp_device_set_index (unifying->priv->hidpp_device,
156 g_ascii_strtoull (tmp + 1, &endptr, 10));
157 if (endptr != NULL && endptr[0] != '\0') {
158 g_debug ("HID_PHYS malformed: '%s'", bus_address);
159 goto out;
160 }
161
162 /* find the hidraw device that matches the parent */
163 parent = g_udev_device_get_parent (native);
164 client = g_udev_client_new (NULL);
165 hidraw_list = g_udev_client_query_by_subsystem (client, "hidraw");
166 for (l = hidraw_list; l != NULL; l = l->next) {
167 if (g_strcmp0 (g_udev_device_get_sysfs_path (parent),
168 g_udev_device_get_sysfs_attr (l->data, "device")) == 0) {
169 receiver = g_object_ref (l->data);
170 break;
171 }
172 }
173 if (receiver == NULL) {
174 g_debug ("Unable to find an hidraw device for Unifying receiver");
175 return FALSE;
176 }
177
178 /* connect to the receiver */
179 device_file = g_udev_device_get_device_file (receiver);
180 if (device_file == NULL) {
181 g_debug ("Could not get device file for Unifying receiver device");
182 goto out;
183 }
184 g_debug ("Using Unifying receiver hidraw device file: %s", device_file);
185 hidpp_device_set_hidraw_device (unifying->priv->hidpp_device,
186 device_file);
187
188 /* coldplug initial parameters */
189 ret = hidpp_device_refresh (unifying->priv->hidpp_device,
190 HIDPP_REFRESH_FLAGS_VERSION |
191 HIDPP_REFRESH_FLAGS_KIND |
192 HIDPP_REFRESH_FLAGS_MODEL,
193 &error);
194 if (!ret) {
195 g_warning ("failed to coldplug unifying device: %s",
196 error->message);
197 g_error_free (error);
198 goto out;
199 }
200
201 /* set some default values */
202 g_object_set (device,
203 "vendor", g_udev_device_get_property (native, "ID_VENDOR"),
204 "type", up_device_unifying_get_device_kind (unifying),
205 "model", hidpp_device_get_model (unifying->priv->hidpp_device),
206 "has-history", TRUE,
207 "is-rechargeable", TRUE,
208 "power-supply", FALSE,
209 NULL);
210
211 /* set up a poll to send the magic packet */
212 up_device_unifying_refresh (device);
213 unifying->priv->poll_timer_id = g_timeout_add_seconds (UP_DEVICE_UNIFYING_REFRESH_TIMEOUT,
214 (GSourceFunc) up_device_unifying_refresh,
215 device);
216 ret = TRUE;
217out:
218 g_list_foreach (hidraw_list, (GFunc) g_object_unref, NULL);
219 g_list_free (hidraw_list);
220 if (parent != NULL)
221 g_object_unref (parent);
222 if (receiver != NULL)
223 g_object_unref (receiver);
224 if (client != NULL)
225 g_object_unref (client);
226 return ret;
227}
228
229/**
230 * up_device_unifying_init:
231 **/
232static void
233up_device_unifying_init (UpDeviceUnifying *unifying)
234{
235 unifying->priv = UP_DEVICE_UNIFYING_GET_PRIVATE (unifying);
236 unifying->priv->poll_timer_id = 0;
237}
238
239/**
240 * up_device_unifying_finalize:
241 **/
242static void
243up_device_unifying_finalize (GObject *object)
244{
245 UpDeviceUnifying *unifying;
246
247 g_return_if_fail (object != NULL);
248 g_return_if_fail (UP_IS_DEVICE_UNIFYING (object));
249
250 unifying = UP_DEVICE_UNIFYING (object);
251 g_return_if_fail (unifying->priv != NULL);
252
253 if (unifying->priv->poll_timer_id > 0)
254 g_source_remove (unifying->priv->poll_timer_id);
255 if (unifying->priv->hidpp_device != NULL)
256 g_object_unref (unifying->priv->hidpp_device);
257
258 G_OBJECT_CLASS (up_device_unifying_parent_class)->finalize (object);
259}
260
261/**
262 * up_device_unifying_class_init:
263 **/
264static void
265up_device_unifying_class_init (UpDeviceUnifyingClass *klass)
266{
267 GObjectClass *object_class = G_OBJECT_CLASS (klass);
268 UpDeviceClass *device_class = UP_DEVICE_CLASS (klass);
269
270 object_class->finalize = up_device_unifying_finalize;
271 device_class->coldplug = up_device_unifying_coldplug;
272 device_class->refresh = up_device_unifying_refresh;
273
274 g_type_class_add_private (klass, sizeof (UpDeviceUnifyingPrivate));
275}
276
277/**
278 * up_device_unifying_new:
279 **/
280UpDeviceUnifying *
281up_device_unifying_new (void)
282{
283 return g_object_new (UP_TYPE_DEVICE_UNIFYING, NULL);
284}
diff --git a/src/linux/up-device-lg-unifying.h b/src/linux/up-device-unifying.h
index d1debf3..d1debf3 100644
--- a/src/linux/up-device-lg-unifying.h
+++ b/src/linux/up-device-unifying.h