mirror of
https://gitlab.freedesktop.org/upower/upower.git
synced 2026-05-05 16:58:07 +02:00
Factor out the Logitech Unifying support to support other devices
This commit is contained in:
parent
b188a49a9c
commit
b1f12feb1f
9 changed files with 1308 additions and 790 deletions
|
|
@ -20,3 +20,8 @@ ATTR{idVendor}=="046d", ATTR{idProduct}=="c702", ENV{UPOWER_PRODUCT}="Presenter"
|
|||
|
||||
LABEL="up_csr_end"
|
||||
|
||||
# Unifying HID++ devices
|
||||
SUBSYSTEM!="hid", GOTO="up_unifying_end"
|
||||
ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", DRIVER=="logitech-djdevice", ENV{UPOWER_BATTERY_TYPE}="unifying"
|
||||
ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c532", DRIVER=="logitech-djdevice", ENV{UPOWER_BATTERY_TYPE}="unifying"
|
||||
LABEL="up_unifying_end"
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ libupshared_la_SOURCES = \
|
|||
up-device-supply.h \
|
||||
up-device-csr.c \
|
||||
up-device-csr.h \
|
||||
up-device-lg-unifying.c \
|
||||
up-device-lg-unifying.h \
|
||||
up-device-unifying.c \
|
||||
up-device-unifying.h \
|
||||
up-device-hid.c \
|
||||
up-device-hid.h \
|
||||
up-device-wup.c \
|
||||
|
|
@ -42,11 +42,25 @@ libupshared_la_SOURCES = \
|
|||
up-dock.h \
|
||||
up-backend.c \
|
||||
up-native.c \
|
||||
hidpp-device.c \
|
||||
hidpp-device.h \
|
||||
sysfs-utils.c \
|
||||
sysfs-utils.h \
|
||||
$(idevice_files) \
|
||||
$(BUILT_SOURCES)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
hidpp-test
|
||||
hidpp_test_SOURCES = \
|
||||
hidpp-device.c \
|
||||
hidpp-device.h \
|
||||
hidpp-test.c
|
||||
hidpp_test_LDADD = \
|
||||
-lm \
|
||||
$(GLIB_LIBS) \
|
||||
$(GIO_LIBS)
|
||||
hidpp_test_CFLAGS = $(AM_CFLAGS) $(WARNINGFLAGS_C)
|
||||
|
||||
EXTRA_DIST = $(libupshared_la_SOURCES) \
|
||||
integration-test
|
||||
|
||||
|
|
|
|||
815
src/linux/hidpp-device.c
Normal file
815
src/linux/hidpp-device.c
Normal file
|
|
@ -0,0 +1,815 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2012 Julien Danjou <julien@danjou.info>
|
||||
* Copyright (C) 2012 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <glib-object.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "hidpp-device.h"
|
||||
|
||||
/* Arbitrary value used in ping */
|
||||
#define HIDPP_PING_DATA 0x42
|
||||
|
||||
#define HIDPP_RECEIVER_ADDRESS 0xff
|
||||
|
||||
#define HIDPP_RESPONSE_SHORT_LENGTH 7
|
||||
#define HIDPP_RESPONSE_LONG_LENGTH 20
|
||||
|
||||
#define HIDPP_HEADER_REQUEST 0x10
|
||||
#define HIDPP_HEADER_RESPONSE 0x11
|
||||
|
||||
/* HID++ 1.0 */
|
||||
#define HIDPP_READ_SHORT_REGISTER 0x81
|
||||
#define HIDPP_READ_SHORT_REGISTER_BATTERY 0x0d
|
||||
|
||||
#define HIDPP_READ_LONG_REGISTER 0x83
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE 11
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_KEYBOARD 0x1
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_MOUSE 0x2
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_NUMPAD 0x3
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_PRESENTER 0x4
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_REMOTE_CONTROL 0x7
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TRACKBALL 0x8
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TOUCHPAD 0x9
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TABLET 0xa
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_GAMEPAD 0xb
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_JOYSTICK 0xc
|
||||
|
||||
#define HIDPP_ERR_INVALID_SUBID 0x8f
|
||||
|
||||
/* HID++ 2.0 */
|
||||
|
||||
/* HID++2.0 error codes */
|
||||
#define HIDPP_ERROR_CODE_NOERROR 0x00
|
||||
#define HIDPP_ERROR_CODE_UNKNOWN 0x01
|
||||
#define HIDPP_ERROR_CODE_INVALIDARGUMENT 0x02
|
||||
#define HIDPP_ERROR_CODE_OUTOFRANGE 0x03
|
||||
#define HIDPP_ERROR_CODE_HWERROR 0x04
|
||||
#define HIDPP_ERROR_CODE_LOGITECH_INTERNAL 0x05
|
||||
#define HIDPP_ERROR_CODE_INVALID_FEATURE_INDEX 0x06
|
||||
#define HIDPP_ERROR_CODE_INVALID_FUNCTION_ID 0x07
|
||||
#define HIDPP_ERROR_CODE_BUSY 0x08
|
||||
#define HIDPP_ERROR_CODE_UNSUPPORTED 0x09
|
||||
|
||||
#define HIDPP_FEATURE_ROOT 0x0000
|
||||
#define HIDPP_FEATURE_ROOT_INDEX 0x00
|
||||
#define HIDPP_FEATURE_ROOT_FN_GET_FEATURE (0x00 << 4)
|
||||
#define HIDPP_FEATURE_ROOT_FN_PING (0x01 << 4)
|
||||
#define HIDPP_FEATURE_I_FEATURE_SET 0x0001
|
||||
#define HIDPP_FEATURE_I_FEATURE_SET_FN_GET_COUNT (0x00 << 4)
|
||||
#define HIDPP_FEATURE_I_FEATURE_SET_FN_GET_FEATURE_ID (0x01 << 4)
|
||||
#define HIDPP_FEATURE_I_FIRMWARE_INFO 0x0003
|
||||
#define HIDPP_FEATURE_I_FIRMWARE_INFO_FN_GET_COUNT (0x00 << 4)
|
||||
#define HIDPP_FEATURE_I_FIRMWARE_INFO_FN_GET_INFO (0x01 << 4)
|
||||
#define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE 0x0005
|
||||
#define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_COUNT (0x00 << 4)
|
||||
#define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_NAME (0x01 << 4)
|
||||
#define HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_TYPE (0x02 << 4)
|
||||
#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS 0x1000
|
||||
//#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_FN_GET_STATUS (0x00 << 4)
|
||||
//#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_BE (0x01 << 4)
|
||||
#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_FN_GET_CAPABILITY (0x02 << 4)
|
||||
|
||||
#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_FN_GET_STATUS 0x02
|
||||
#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS_BE 0x02
|
||||
|
||||
#define HIDPP_FEATURE_SPECIAL_KEYS_MSE_BUTTONS 0x1B00
|
||||
#define HIDPP_FEATURE_WIRELESS_DEVICE_STATUS 0x1D4B
|
||||
#define HIDPP_FEATURE_WIRELESS_DEVICE_STATUS_BE (0x00 << 4)
|
||||
|
||||
#define HIDPP_FEATURE_SOLAR_DASHBOARD 0x4301
|
||||
#define HIDPP_FEATURE_SOLAR_DASHBOARD_FN_SET_LIGHT_MEASURE (0x00 << 4)
|
||||
#define HIDPP_FEATURE_SOLAR_DASHBOARD_BE_BATTERY_LEVEL_STATUS (0x01 << 4)
|
||||
|
||||
#define HIDPP_DEVICE_READ_RESPONSE_TIMEOUT 3000 /* miliseconds */
|
||||
|
||||
struct HidppDevicePrivate
|
||||
{
|
||||
gboolean enable_debug;
|
||||
gchar *hidraw_device;
|
||||
gchar *model;
|
||||
GIOChannel *channel;
|
||||
GPtrArray *feature_index;
|
||||
guint batt_percentage;
|
||||
guint channel_source_id;
|
||||
guint device_idx;
|
||||
guint version;
|
||||
HidppDeviceBattStatus batt_status;
|
||||
HidppDeviceKind kind;
|
||||
int fd;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
gint idx;
|
||||
guint16 feature;
|
||||
gchar *name;
|
||||
} HidppDeviceMap;
|
||||
|
||||
G_DEFINE_TYPE (HidppDevice, hidpp_device, G_TYPE_OBJECT)
|
||||
#define HIDPP_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), HIDPP_TYPE_DEVICE, HidppDevicePrivate))
|
||||
|
||||
/**
|
||||
* hidpp_device_map_print:
|
||||
**/
|
||||
static void
|
||||
hidpp_device_map_print (HidppDevice *device)
|
||||
{
|
||||
guint i;
|
||||
HidppDeviceMap *map;
|
||||
HidppDevicePrivate *priv = device->priv;
|
||||
|
||||
if (!device->priv->enable_debug)
|
||||
return;
|
||||
for (i = 0; i < priv->feature_index->len; i++) {
|
||||
map = g_ptr_array_index (priv->feature_index, i);
|
||||
g_print ("%02x\t%s [%i]\n", map->idx, map->name, map->feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_map_get_by_feature:
|
||||
*
|
||||
* Gets the cached index from the function number.
|
||||
**/
|
||||
static const HidppDeviceMap *
|
||||
hidpp_device_map_get_by_feature (HidppDevice *device, guint16 feature)
|
||||
{
|
||||
guint i;
|
||||
HidppDeviceMap *map;
|
||||
HidppDevicePrivate *priv = device->priv;
|
||||
|
||||
for (i = 0; i < priv->feature_index->len; i++) {
|
||||
map = g_ptr_array_index (priv->feature_index, i);
|
||||
if (map->feature == feature)
|
||||
return map;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_map_get_by_idx:
|
||||
*
|
||||
* Gets the cached index from the function index.
|
||||
**/
|
||||
static const HidppDeviceMap *
|
||||
hidpp_device_map_get_by_idx (HidppDevice *device, gint idx)
|
||||
{
|
||||
guint i;
|
||||
HidppDeviceMap *map;
|
||||
HidppDevicePrivate *priv = device->priv;
|
||||
|
||||
for (i = 0; i < priv->feature_index->len; i++) {
|
||||
map = g_ptr_array_index (priv->feature_index, i);
|
||||
if (map->idx == idx)
|
||||
return map;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_print_buffer:
|
||||
*
|
||||
* Pretty print the send/recieve buffer.
|
||||
**/
|
||||
static void
|
||||
hidpp_device_print_buffer (HidppDevice *device, const guint8 *buffer)
|
||||
{
|
||||
guint i;
|
||||
const HidppDeviceMap *map;
|
||||
|
||||
if (!device->priv->enable_debug)
|
||||
return;
|
||||
for (i = 0; i < HIDPP_RESPONSE_LONG_LENGTH; i++)
|
||||
g_print ("%02x ", buffer[i]);
|
||||
g_print ("\n");
|
||||
|
||||
/* direction */
|
||||
if (buffer[0] == HIDPP_HEADER_REQUEST)
|
||||
g_print ("REQUEST\n");
|
||||
else if (buffer[0] == HIDPP_HEADER_RESPONSE)
|
||||
g_print ("RESPONSE\n");
|
||||
else
|
||||
g_print ("??\n");
|
||||
|
||||
/* dev index */
|
||||
g_print ("device-idx=%02x ", buffer[1]);
|
||||
if (buffer[1] == HIDPP_RECEIVER_ADDRESS) {
|
||||
g_print ("[Receiver]\n");
|
||||
} else if (device->priv->device_idx == buffer[1]) {
|
||||
g_print ("[This Device]\n");
|
||||
} else {
|
||||
g_print ("[Random Device]\n");
|
||||
}
|
||||
|
||||
/* feature index */
|
||||
if (buffer[2] == HIDPP_READ_LONG_REGISTER) {
|
||||
g_print ("feature-idx=%s [%02x]\n",
|
||||
"v1(ReadLongRegister)", buffer[2]);
|
||||
} else {
|
||||
map = hidpp_device_map_get_by_idx (device, buffer[2]);
|
||||
g_print ("feature-idx=v2(%s) [%02x]\n",
|
||||
map != NULL ? map->name : "unknown", buffer[2]);
|
||||
}
|
||||
|
||||
g_print ("function-id=%01x\n", buffer[3] & 0xf);
|
||||
g_print ("software-id=%01x\n", buffer[3] >> 4);
|
||||
g_print ("param[0]=%02x\n\n", buffer[4]);
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_cmd:
|
||||
**/
|
||||
static gboolean
|
||||
hidpp_device_cmd (HidppDevice *device,
|
||||
guint8 device_idx,
|
||||
guint8 feature_idx,
|
||||
guint8 function_idx,
|
||||
guint8 *request_data,
|
||||
gsize request_len,
|
||||
guint8 *response_data,
|
||||
gsize response_len,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
gssize wrote;
|
||||
guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
|
||||
guint i;
|
||||
HidppDevicePrivate *priv = device->priv;
|
||||
GPollFD poll[] = {
|
||||
{
|
||||
.fd = priv->fd,
|
||||
.events = G_IO_IN | G_IO_OUT | G_IO_ERR,
|
||||
},
|
||||
};
|
||||
|
||||
/* make the request packet */
|
||||
memset (buf, 0x00, HIDPP_RESPONSE_LONG_LENGTH);
|
||||
buf[0] = HIDPP_HEADER_REQUEST;
|
||||
buf[1] = device_idx;
|
||||
buf[2] = feature_idx;
|
||||
buf[3] = function_idx;
|
||||
for (i = 0; i < request_len; i++)
|
||||
buf[4 + i] = request_data[i];
|
||||
|
||||
/* write to the device */
|
||||
hidpp_device_print_buffer (device, buf);
|
||||
wrote = write (priv->fd, buf, 4 + request_len);
|
||||
if ((gsize) wrote != 4 + request_len) {
|
||||
g_set_error (error, 1, 0,
|
||||
"Unable to write request to device: %" G_GSIZE_FORMAT,
|
||||
wrote);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read from the device */
|
||||
wrote = g_poll (poll, G_N_ELEMENTS(poll),
|
||||
HIDPP_DEVICE_READ_RESPONSE_TIMEOUT);
|
||||
if (wrote <= 0) {
|
||||
g_set_error (error, 1, 0,
|
||||
"Attempt to read response from device timed out: %" G_GSIZE_FORMAT,
|
||||
wrote);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
memset (buf, 0x00, HIDPP_RESPONSE_LONG_LENGTH);
|
||||
wrote = read (priv->fd, buf, sizeof (buf));
|
||||
if (wrote <= 0) {
|
||||
g_set_error (error, 1, 0,
|
||||
"Unable to read response from device: %" G_GSIZE_FORMAT,
|
||||
wrote);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* is device offline */
|
||||
hidpp_device_print_buffer (device, buf);
|
||||
if (buf[0] == HIDPP_HEADER_REQUEST &&
|
||||
buf[1] == device_idx &&
|
||||
buf[2] == HIDPP_ERR_INVALID_SUBID &&
|
||||
buf[3] == 0x00 &&
|
||||
buf[4] == HIDPP_FEATURE_ROOT_FN_PING) {
|
||||
/* HID++ 1.0 ping reply, so fake success */
|
||||
if (buf[5] == HIDPP_ERROR_CODE_UNKNOWN) {
|
||||
buf[0] = 1;
|
||||
goto out;
|
||||
}
|
||||
if (buf[5] == HIDPP_ERROR_CODE_UNSUPPORTED) {
|
||||
/* device offline / unreachable */
|
||||
g_set_error_literal (error, 1, 0,
|
||||
"device is unreachable");
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (buf[0] != HIDPP_HEADER_RESPONSE ||
|
||||
buf[1] != device_idx ||
|
||||
buf[2] != feature_idx ||
|
||||
buf[3] != function_idx) {
|
||||
g_set_error (error, 1, 0,
|
||||
"invalid response from device: %" G_GSIZE_FORMAT,
|
||||
wrote);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < response_len; i++)
|
||||
response_data[i] = buf[4 + i];
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_map_add:
|
||||
*
|
||||
* Requests the index for a function, and adds it to the memeory cache
|
||||
* if it exists.
|
||||
**/
|
||||
static gboolean
|
||||
hidpp_device_map_add (HidppDevice *device,
|
||||
guint16 feature,
|
||||
const gchar *name)
|
||||
{
|
||||
gboolean ret;
|
||||
GError *error = NULL;
|
||||
guint8 buf[3];
|
||||
HidppDeviceMap *map;
|
||||
HidppDevicePrivate *priv = device->priv;
|
||||
|
||||
buf[0] = feature >> 8;
|
||||
buf[1] = feature;
|
||||
buf[2] = 0x00;
|
||||
|
||||
g_debug ("Getting idx for feature %s [%02x]", name, feature);
|
||||
ret = hidpp_device_cmd (device,
|
||||
priv->device_idx,
|
||||
HIDPP_FEATURE_ROOT_INDEX,
|
||||
HIDPP_FEATURE_ROOT_FN_GET_FEATURE,
|
||||
buf, sizeof (buf),
|
||||
buf, sizeof (buf),
|
||||
&error);
|
||||
if (!ret) {
|
||||
g_warning ("Failed to get feature idx: %s", error->message);
|
||||
g_error_free (error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* zero index */
|
||||
if (buf[0] == 0x00) {
|
||||
ret = FALSE;
|
||||
g_debug ("Feature not found");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* add to map */
|
||||
map = g_new0 (HidppDeviceMap, 1);
|
||||
map->idx = buf[0];
|
||||
map->feature = feature;
|
||||
map->name = g_strdup (name);
|
||||
g_ptr_array_add (priv->feature_index, map);
|
||||
g_debug ("Added feature %s [%02x] as idx %02x",
|
||||
name, feature, map->idx);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_get_model:
|
||||
**/
|
||||
const gchar *
|
||||
hidpp_device_get_model (HidppDevice *device)
|
||||
{
|
||||
g_return_val_if_fail (HIDPP_IS_DEVICE (device), NULL);
|
||||
return device->priv->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_get_batt_percentage:
|
||||
**/
|
||||
guint
|
||||
hidpp_device_get_batt_percentage (HidppDevice *device)
|
||||
{
|
||||
g_return_val_if_fail (HIDPP_IS_DEVICE (device), 0);
|
||||
return device->priv->batt_percentage;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_get_version:
|
||||
**/
|
||||
guint
|
||||
hidpp_device_get_version (HidppDevice *device)
|
||||
{
|
||||
g_return_val_if_fail (HIDPP_IS_DEVICE (device), 0);
|
||||
return device->priv->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_get_batt_status:
|
||||
**/
|
||||
HidppDeviceBattStatus
|
||||
hidpp_device_get_batt_status (HidppDevice *device)
|
||||
{
|
||||
g_return_val_if_fail (HIDPP_IS_DEVICE (device), HIDPP_DEVICE_BATT_STATUS_UNKNOWN);
|
||||
return device->priv->batt_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_get_kind:
|
||||
**/
|
||||
HidppDeviceKind
|
||||
hidpp_device_get_kind (HidppDevice *device)
|
||||
{
|
||||
g_return_val_if_fail (HIDPP_IS_DEVICE (device), HIDPP_DEVICE_KIND_UNKNOWN);
|
||||
return device->priv->kind;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_set_hidraw_device:
|
||||
**/
|
||||
void
|
||||
hidpp_device_set_hidraw_device (HidppDevice *device,
|
||||
const gchar *hidraw_device)
|
||||
{
|
||||
g_return_if_fail (HIDPP_IS_DEVICE (device));
|
||||
device->priv->hidraw_device = g_strdup (hidraw_device);
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_set_index:
|
||||
**/
|
||||
void
|
||||
hidpp_device_set_index (HidppDevice *device,
|
||||
guint device_idx)
|
||||
{
|
||||
g_return_if_fail (HIDPP_IS_DEVICE (device));
|
||||
device->priv->device_idx = device_idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_set_enable_debug:
|
||||
**/
|
||||
void
|
||||
hidpp_device_set_enable_debug (HidppDevice *device,
|
||||
gboolean enable_debug)
|
||||
{
|
||||
g_return_if_fail (HIDPP_IS_DEVICE (device));
|
||||
device->priv->enable_debug = enable_debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_refresh:
|
||||
**/
|
||||
gboolean
|
||||
hidpp_device_refresh (HidppDevice *device,
|
||||
HidppRefreshFlags refresh_flags,
|
||||
GError **error)
|
||||
{
|
||||
const HidppDeviceMap *map;
|
||||
gboolean ret = TRUE;
|
||||
GString *name = NULL;
|
||||
guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
|
||||
guint i;
|
||||
guint len;
|
||||
HidppDevicePrivate *priv = device->priv;
|
||||
|
||||
g_return_val_if_fail (HIDPP_IS_DEVICE (device), FALSE);
|
||||
|
||||
/* open the device if it's not already opened */
|
||||
if (priv->fd < 0) {
|
||||
priv->fd = open (device->priv->hidraw_device, O_RDWR | O_NONBLOCK);
|
||||
if (priv->fd < 0) {
|
||||
g_set_error (error, 1, 0,
|
||||
"cannot open device file %s",
|
||||
priv->hidraw_device);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* add features we are going to use */
|
||||
// hidpp_device_map_add (device,
|
||||
// HIDPP_FEATURE_I_FEATURE_SET,
|
||||
// "IFeatureSet");
|
||||
// hidpp_device_map_add (device,
|
||||
// HIDPP_FEATURE_I_FIRMWARE_INFO,
|
||||
// "IFirmwareInfo");
|
||||
hidpp_device_map_add (device,
|
||||
HIDPP_FEATURE_GET_DEVICE_NAME_TYPE,
|
||||
"GetDeviceNameType");
|
||||
hidpp_device_map_add (device,
|
||||
HIDPP_FEATURE_BATTERY_LEVEL_STATUS,
|
||||
"BatteryLevelStatus");
|
||||
// hidpp_device_map_add (device,
|
||||
// HIDPP_FEATURE_WIRELESS_DEVICE_STATUS,
|
||||
// "WirelessDeviceStatus");
|
||||
hidpp_device_map_add (device,
|
||||
HIDPP_FEATURE_SOLAR_DASHBOARD,
|
||||
"SolarDashboard");
|
||||
hidpp_device_map_print (device);
|
||||
}
|
||||
|
||||
/* get version */
|
||||
if ((refresh_flags & HIDPP_REFRESH_FLAGS_VERSION) > 0) {
|
||||
buf[0] = 0x00;
|
||||
buf[1] = 0x00;
|
||||
buf[2] = HIDPP_PING_DATA;
|
||||
ret = hidpp_device_cmd (device,
|
||||
priv->device_idx,
|
||||
HIDPP_FEATURE_ROOT_INDEX,
|
||||
HIDPP_FEATURE_ROOT_FN_PING,
|
||||
buf, 3,
|
||||
buf, 4,
|
||||
error);
|
||||
if (!ret)
|
||||
goto out;
|
||||
priv->version = buf[0];
|
||||
}
|
||||
|
||||
/* get device kind */
|
||||
if ((refresh_flags & HIDPP_REFRESH_FLAGS_KIND) > 0) {
|
||||
|
||||
if (priv->version == 1) {
|
||||
buf[0] = 0x20 | (priv->device_idx - 1);
|
||||
buf[1] = 0x00;
|
||||
buf[2] = 0x00;
|
||||
ret = hidpp_device_cmd (device,
|
||||
HIDPP_RECEIVER_ADDRESS,
|
||||
HIDPP_READ_LONG_REGISTER,
|
||||
0xb5,
|
||||
buf, 3,
|
||||
buf, 7,
|
||||
error);
|
||||
if (!ret)
|
||||
goto out;
|
||||
switch (buf[7]) {
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_KEYBOARD:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_NUMPAD:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_REMOTE_CONTROL:
|
||||
priv->kind = HIDPP_DEVICE_KIND_KEYBOARD;
|
||||
break;
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_MOUSE:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TRACKBALL:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TOUCHPAD:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_PRESENTER:
|
||||
priv->kind = HIDPP_DEVICE_KIND_MOUSE;
|
||||
break;
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TABLET:
|
||||
priv->kind = HIDPP_DEVICE_KIND_TABLET;
|
||||
break;
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_GAMEPAD:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_JOYSTICK:
|
||||
/* upower doesn't have something for this yet */
|
||||
priv->kind = HIDPP_DEVICE_KIND_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
} else if (priv->version == 2) {
|
||||
|
||||
/* send a BatteryLevelStatus report */
|
||||
map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE);
|
||||
if (map != NULL) {
|
||||
buf[0] = 0x00;
|
||||
buf[1] = 0x00;
|
||||
buf[2] = 0x00;
|
||||
ret = hidpp_device_cmd (device,
|
||||
priv->device_idx,
|
||||
map->idx,
|
||||
HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_TYPE,
|
||||
buf, 3,
|
||||
buf, 1,
|
||||
error);
|
||||
if (!ret)
|
||||
goto out;
|
||||
switch (buf[0]) {
|
||||
case 0: /* keyboard */
|
||||
case 2: /* numpad */
|
||||
priv->kind = HIDPP_DEVICE_KIND_KEYBOARD;
|
||||
break;
|
||||
case 3: /* mouse */
|
||||
case 4: /* touchpad */
|
||||
case 5: /* trackball */
|
||||
priv->kind = HIDPP_DEVICE_KIND_MOUSE;
|
||||
break;
|
||||
case 1: /* remote-control */
|
||||
case 6: /* presenter */
|
||||
case 7: /* receiver */
|
||||
priv->kind = HIDPP_DEVICE_KIND_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* get device model string */
|
||||
if ((refresh_flags & HIDPP_REFRESH_FLAGS_MODEL) > 0) {
|
||||
buf[0] = 0x00;
|
||||
buf[1] = 0x00;
|
||||
buf[2] = 0x00;
|
||||
map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE);
|
||||
if (map != NULL) {
|
||||
ret = hidpp_device_cmd (device,
|
||||
priv->device_idx,
|
||||
map->idx,
|
||||
HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_COUNT,
|
||||
buf, 3,
|
||||
buf, 1,
|
||||
error);
|
||||
if (!ret)
|
||||
goto out;
|
||||
}
|
||||
len = buf[0];
|
||||
name = g_string_new ("");
|
||||
for (i = 0; i < len; i +=4 ) {
|
||||
buf[0] = i;
|
||||
buf[1] = 0x00;
|
||||
buf[2] = 0x00;
|
||||
ret = hidpp_device_cmd (device,
|
||||
priv->device_idx,
|
||||
map->idx,
|
||||
HIDPP_FEATURE_GET_DEVICE_NAME_TYPE_FN_GET_NAME,
|
||||
buf, 3,
|
||||
buf, 4,
|
||||
error);
|
||||
if (!ret)
|
||||
goto out;
|
||||
g_string_append_len (name, (gchar *) &buf[0], 4);
|
||||
}
|
||||
priv->model = g_strdup (name->str);
|
||||
}
|
||||
|
||||
/* get battery status */
|
||||
if ((refresh_flags & HIDPP_REFRESH_FLAGS_BATTERY) > 0) {
|
||||
if (priv->version == 1) {
|
||||
buf[0] = HIDPP_READ_SHORT_REGISTER;
|
||||
buf[1] = HIDPP_READ_SHORT_REGISTER_BATTERY;
|
||||
buf[2] = 0x00;
|
||||
buf[3] = 0x00;
|
||||
buf[4] = 0x00;
|
||||
ret = hidpp_device_cmd (device,
|
||||
priv->device_idx,
|
||||
HIDPP_FEATURE_ROOT_INDEX,
|
||||
HIDPP_FEATURE_ROOT_FN_PING,
|
||||
buf, 5,
|
||||
buf, 1,
|
||||
error);
|
||||
if (!ret)
|
||||
goto out;
|
||||
priv->batt_percentage = buf[0];
|
||||
priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
|
||||
} else if (priv->version == 2) {
|
||||
|
||||
/* sent a SetLightMeasure report */
|
||||
map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_SOLAR_DASHBOARD);
|
||||
if (map != NULL) {
|
||||
buf[0] = 0x01; /* Max number of reports: number of report sent after function call */
|
||||
buf[1] = 0x01; /* Report period: time between reports, in seconds */
|
||||
ret = hidpp_device_cmd (device,
|
||||
priv->device_idx,
|
||||
map->idx,
|
||||
HIDPP_FEATURE_SOLAR_DASHBOARD_FN_SET_LIGHT_MEASURE,
|
||||
buf, 2,
|
||||
buf, 3,
|
||||
error);
|
||||
if (!ret)
|
||||
goto out;
|
||||
priv->batt_percentage = buf[0];
|
||||
priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
|
||||
}
|
||||
|
||||
/* send a BatteryLevelStatus report */
|
||||
map = hidpp_device_map_get_by_feature (device, HIDPP_FEATURE_BATTERY_LEVEL_STATUS);
|
||||
if (map != NULL) {
|
||||
buf[0] = 0x00;
|
||||
buf[1] = 0x00;
|
||||
buf[2] = 0x00;
|
||||
ret = hidpp_device_cmd (device,
|
||||
priv->device_idx,
|
||||
map->idx,
|
||||
HIDPP_FEATURE_BATTERY_LEVEL_STATUS_FN_GET_STATUS,
|
||||
buf, 3,
|
||||
buf, 3,
|
||||
error);
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
/* convert the HID++ v2 status into something
|
||||
* we can set on the device */
|
||||
switch (buf[2]) {
|
||||
case 0: /* discharging */
|
||||
priv->batt_status = HIDPP_DEVICE_BATT_STATUS_DISCHARGING;
|
||||
break;
|
||||
case 1: /* recharging */
|
||||
case 2: /* charge nearly complete */
|
||||
case 4: /* charging slowly */
|
||||
priv->batt_status = HIDPP_DEVICE_BATT_STATUS_CHARGING;
|
||||
break;
|
||||
case 3: /* charging complete */
|
||||
priv->batt_status = HIDPP_DEVICE_BATT_STATUS_CHARGED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
priv->batt_percentage = buf[0];
|
||||
g_debug ("level=%i%%, next-level=%i%%, battery-status=%i",
|
||||
buf[0], buf[1], buf[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (name != NULL)
|
||||
g_string_free (name, TRUE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_init:
|
||||
**/
|
||||
static void
|
||||
hidpp_device_init (HidppDevice *device)
|
||||
{
|
||||
HidppDeviceMap *map;
|
||||
|
||||
device->priv = HIDPP_DEVICE_GET_PRIVATE (device);
|
||||
device->priv->fd = -1;
|
||||
device->priv->feature_index = g_ptr_array_new_with_free_func (g_free);
|
||||
device->priv->batt_status = HIDPP_DEVICE_BATT_STATUS_UNKNOWN;
|
||||
device->priv->kind = HIDPP_DEVICE_KIND_UNKNOWN;
|
||||
|
||||
/* add known root */
|
||||
map = g_new0 (HidppDeviceMap, 1);
|
||||
map->idx = HIDPP_FEATURE_ROOT_INDEX;
|
||||
map->feature = HIDPP_FEATURE_ROOT;
|
||||
map->name = g_strdup ("Root");
|
||||
g_ptr_array_add (device->priv->feature_index, map);
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_finalize:
|
||||
**/
|
||||
static void
|
||||
hidpp_device_finalize (GObject *object)
|
||||
{
|
||||
HidppDevice *device;
|
||||
|
||||
g_return_if_fail (object != NULL);
|
||||
g_return_if_fail (HIDPP_IS_DEVICE (object));
|
||||
|
||||
device = HIDPP_DEVICE (object);
|
||||
g_return_if_fail (device->priv != NULL);
|
||||
|
||||
if (device->priv->channel_source_id > 0)
|
||||
g_source_remove (device->priv->channel_source_id);
|
||||
|
||||
if (device->priv->channel) {
|
||||
g_io_channel_shutdown (device->priv->channel, FALSE, NULL);
|
||||
g_io_channel_unref (device->priv->channel);
|
||||
}
|
||||
g_ptr_array_unref (device->priv->feature_index);
|
||||
|
||||
g_free (device->priv->hidraw_device);
|
||||
g_free (device->priv->model);
|
||||
|
||||
G_OBJECT_CLASS (hidpp_device_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_class_init:
|
||||
**/
|
||||
static void
|
||||
hidpp_device_class_init (HidppDeviceClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
object_class->finalize = hidpp_device_finalize;
|
||||
g_type_class_add_private (klass, sizeof (HidppDevicePrivate));
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_new:
|
||||
**/
|
||||
HidppDevice *
|
||||
hidpp_device_new (void)
|
||||
{
|
||||
return g_object_new (HIDPP_TYPE_DEVICE, NULL);
|
||||
}
|
||||
94
src/linux/hidpp-device.h
Normal file
94
src/linux/hidpp-device.h
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2012 Julien Danjou <julien@danjou.info>
|
||||
* Copyright (C) 2012 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __HIDPP_DEVICE_H__
|
||||
#define __HIDPP_DEVICE_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "hidpp-device.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define HIDPP_TYPE_DEVICE (hidpp_device_get_type ())
|
||||
#define HIDPP_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), HIDPP_TYPE_DEVICE, HidppDevice))
|
||||
#define HIDPP_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), HIDPP_TYPE_DEVICE, HidppDeviceClass))
|
||||
#define HIDPP_IS_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), HIDPP_TYPE_DEVICE))
|
||||
#define HIDPP_IS_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), HIDPP_TYPE_DEVICE))
|
||||
#define HIDPP_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), HIDPP_TYPE_DEVICE, HidppDeviceClass))
|
||||
|
||||
typedef struct HidppDevicePrivate HidppDevicePrivate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GObject parent;
|
||||
HidppDevicePrivate *priv;
|
||||
} HidppDevice;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
} HidppDeviceClass;
|
||||
|
||||
typedef enum {
|
||||
HIDPP_DEVICE_KIND_KEYBOARD,
|
||||
HIDPP_DEVICE_KIND_MOUSE,
|
||||
HIDPP_DEVICE_KIND_TOUCHPAD,
|
||||
HIDPP_DEVICE_KIND_TRACKBALL,
|
||||
HIDPP_DEVICE_KIND_TABLET,
|
||||
HIDPP_DEVICE_KIND_UNKNOWN
|
||||
} HidppDeviceKind;
|
||||
|
||||
typedef enum {
|
||||
HIDPP_DEVICE_BATT_STATUS_CHARGING,
|
||||
HIDPP_DEVICE_BATT_STATUS_DISCHARGING,
|
||||
HIDPP_DEVICE_BATT_STATUS_CHARGED,
|
||||
HIDPP_DEVICE_BATT_STATUS_UNKNOWN
|
||||
} HidppDeviceBattStatus;
|
||||
|
||||
typedef enum {
|
||||
HIDPP_REFRESH_FLAGS_VERSION = 1,
|
||||
HIDPP_REFRESH_FLAGS_KIND = 2,
|
||||
HIDPP_REFRESH_FLAGS_BATTERY = 4,
|
||||
HIDPP_REFRESH_FLAGS_MODEL = 8
|
||||
} HidppRefreshFlags;
|
||||
|
||||
GType hidpp_device_get_type (void);
|
||||
const gchar *hidpp_device_get_model (HidppDevice *device);
|
||||
guint hidpp_device_get_batt_percentage (HidppDevice *device);
|
||||
guint hidpp_device_get_version (HidppDevice *device);
|
||||
HidppDeviceBattStatus hidpp_device_get_batt_status (HidppDevice *device);
|
||||
HidppDeviceKind hidpp_device_get_kind (HidppDevice *device);
|
||||
void hidpp_device_set_hidraw_device (HidppDevice *device,
|
||||
const gchar *hidraw_device);
|
||||
void hidpp_device_set_index (HidppDevice *device,
|
||||
guint device_idx);
|
||||
void hidpp_device_set_enable_debug (HidppDevice *device,
|
||||
gboolean enable_debug);
|
||||
gboolean hidpp_device_refresh (HidppDevice *device,
|
||||
HidppRefreshFlags refresh_flags,
|
||||
GError **error);
|
||||
HidppDevice *hidpp_device_new (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __HIDPP_DEVICE_H__ */
|
||||
|
||||
79
src/linux/hidpp-test.c
Normal file
79
src/linux/hidpp-test.c
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2012 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* Licensed under the GNU General Public License Version 2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "hidpp-device.h"
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
// const gchar *model;
|
||||
// guint batt_percentage;
|
||||
// guint version;
|
||||
// HidppDeviceBattStatus batt_status;
|
||||
HidppDevice *d;
|
||||
// HidppDeviceKind kind;
|
||||
gboolean ret;
|
||||
GError *error = NULL;
|
||||
|
||||
g_type_init ();
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
d = hidpp_device_new ();
|
||||
hidpp_device_set_enable_debug (d, TRUE);
|
||||
g_assert_cmpint (hidpp_device_get_version (d), ==, 0);
|
||||
g_assert_cmpstr (hidpp_device_get_model (d), ==, NULL);
|
||||
g_assert_cmpint (hidpp_device_get_batt_percentage (d), ==, 0);
|
||||
g_assert_cmpint (hidpp_device_get_batt_status (d), ==, HIDPP_DEVICE_BATT_STATUS_UNKNOWN);
|
||||
g_assert_cmpint (hidpp_device_get_kind (d), ==, HIDPP_DEVICE_KIND_UNKNOWN);
|
||||
|
||||
/* setup */
|
||||
hidpp_device_set_hidraw_device (d, "/dev/hidraw0");
|
||||
hidpp_device_set_index (d, 1);
|
||||
ret = hidpp_device_refresh (d,
|
||||
HIDPP_REFRESH_FLAGS_VERSION |
|
||||
HIDPP_REFRESH_FLAGS_KIND |
|
||||
HIDPP_REFRESH_FLAGS_BATTERY |
|
||||
HIDPP_REFRESH_FLAGS_MODEL,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (ret);
|
||||
|
||||
g_assert_cmpint (hidpp_device_get_version (d), !=, 0);
|
||||
g_assert_cmpstr (hidpp_device_get_model (d), !=, NULL);
|
||||
g_assert_cmpint (hidpp_device_get_batt_percentage (d), !=, 0);
|
||||
g_assert_cmpint (hidpp_device_get_batt_status (d), !=, HIDPP_DEVICE_BATT_STATUS_UNKNOWN);
|
||||
g_assert_cmpint (hidpp_device_get_kind (d), !=, HIDPP_DEVICE_KIND_UNKNOWN);
|
||||
|
||||
g_print ("Version:\t\t%i\n", hidpp_device_get_version (d));
|
||||
g_print ("Kind:\t\t\t%i\n", hidpp_device_get_kind (d));
|
||||
g_print ("Model:\t\t\t%s\n", hidpp_device_get_model (d));
|
||||
g_print ("Battery Percentage:\t%i\n", hidpp_device_get_batt_percentage (d));
|
||||
g_print ("Battery Status:\t\t%i\n", hidpp_device_get_batt_status (d));
|
||||
|
||||
g_object_unref (d);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
#include "up-device-supply.h"
|
||||
#include "up-device-csr.h"
|
||||
#include "up-device-lg-unifying.h"
|
||||
#include "up-device-unifying.h"
|
||||
#include "up-device-wup.h"
|
||||
#include "up-device-hid.h"
|
||||
#include "up-input.h"
|
||||
|
|
@ -118,6 +118,18 @@ up_backend_device_new (UpBackend *backend, GUdevDevice *native)
|
|||
/* no valid power supply object */
|
||||
device = NULL;
|
||||
|
||||
} else if (g_strcmp0 (subsys, "hid") == 0) {
|
||||
|
||||
/* see if this is a Unifying mouse or keyboard */
|
||||
device = UP_DEVICE (up_device_unifying_new ());
|
||||
ret = up_device_coldplug (device, backend->priv->daemon, G_OBJECT (native));
|
||||
if (ret)
|
||||
goto out;
|
||||
g_object_unref (device);
|
||||
|
||||
/* no valid power supply object */
|
||||
device = NULL;
|
||||
|
||||
} else if (g_strcmp0 (subsys, "tty") == 0) {
|
||||
|
||||
/* try to detect a Watts Up? Pro monitor */
|
||||
|
|
@ -176,18 +188,8 @@ up_backend_device_new (UpBackend *backend, GUdevDevice *native)
|
|||
|
||||
/* no valid input object */
|
||||
device = NULL;
|
||||
} else {
|
||||
g_object_unref (input);
|
||||
|
||||
/* see if this is a Unifying mouse or keyboard */
|
||||
device = UP_DEVICE (up_device_unifying_new ());
|
||||
ret = up_device_coldplug (device, backend->priv->daemon, G_OBJECT (native));
|
||||
if (!ret) {
|
||||
g_object_unref (device);
|
||||
/* no valid input object */
|
||||
device = NULL;
|
||||
}
|
||||
}
|
||||
g_object_unref (input);
|
||||
} else {
|
||||
native_path = g_udev_device_get_sysfs_path (native);
|
||||
g_warning ("native path %s (%s) ignoring", native_path, subsys);
|
||||
|
|
@ -328,7 +330,7 @@ up_backend_coldplug (UpBackend *backend, UpDaemon *daemon)
|
|||
GList *l;
|
||||
guint i;
|
||||
gboolean ret;
|
||||
const gchar *subsystems[] = {"power_supply", "usb", "usbmisc", "tty", "input", NULL};
|
||||
const gchar *subsystems[] = {"power_supply", "usb", "usbmisc", "tty", "input", "hid", NULL};
|
||||
|
||||
backend->priv->daemon = g_object_ref (daemon);
|
||||
backend->priv->device_list = up_daemon_get_device_list (daemon);
|
||||
|
|
|
|||
|
|
@ -1,775 +0,0 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2012 Julien Danjou <julien@danjou.info>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <linux/hidraw.h>
|
||||
#include <linux/input.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <glib/gi18n-lib.h>
|
||||
#include <glib-object.h>
|
||||
#include <gudev/gudev.h>
|
||||
|
||||
#include "sysfs-utils.h"
|
||||
#include "up-types.h"
|
||||
#include "up-device-lg-unifying.h"
|
||||
|
||||
/* Arbitrary value used in ping */
|
||||
#define HIDPP_PING_DATA 0x42
|
||||
|
||||
#define HIDPP_RECEIVER_ADDRESS 0xff
|
||||
|
||||
#define HIDPP_RESPONSE_SHORT_LENGTH 7
|
||||
#define HIDPP_RESPONSE_LONG_LENGTH 20
|
||||
|
||||
#define HIDPP_HEADER_REQUEST 0x10
|
||||
#define HIDPP_HEADER_RESPONSE 0x11
|
||||
|
||||
/* HID++ 1.0 */
|
||||
#define HIDPP_READ_SHORT_REGISTER 0x81
|
||||
#define HIDPP_READ_SHORT_REGISTER_BATTERY 0x0d
|
||||
|
||||
#define HIDPP_READ_LONG_REGISTER 0x83
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE 11
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_KEYBOARD 0x1
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_MOUSE 0x2
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_NUMPAD 0x3
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_PRESENTER 0x4
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_REMOTE_CONTROL 0x7
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TRACKBALL 0x8
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TOUCHPAD 0x9
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TABLET 0xa
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_GAMEPAD 0xb
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_JOYSTICK 0xc
|
||||
|
||||
#define HIDPP_ERR_INVALID_SUBID 0x8f
|
||||
|
||||
/* HID++ 2.0 */
|
||||
#define HIDPP_FEATURE_ROOT 0x0000
|
||||
/* This is the only feature that has an hard coded index */
|
||||
#define HIDPP_FEATURE_ROOT_INDEX 0x00
|
||||
#define HIDPP_FEATURE_ROOT_FUNCTION_GETFEATURE (0x00 << 4)
|
||||
#define HIDPP_FEATURE_ROOT_FUNCTION_PING (0x01 << 4)
|
||||
|
||||
#define HIDPP_FEATURE_GETDEVICENAMETYPE 0x0005
|
||||
#define HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETCOUNT (0x00 << 4)
|
||||
#define HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETDEVICENAME (0x01 << 4)
|
||||
|
||||
#define HIDPP_FEATURE_SOLAR_DASHBOARD 0x4301
|
||||
#define HIDPP_FEATURE_SOLAR_DASHBOARD_FUNCTION_SetLightMeasure (0x00 << 4)
|
||||
#define HIDPP_FEATURE_SOLAR_DASHBOARD_BattLightMeasureBroadcastEvent (0x01 << 4)
|
||||
|
||||
#define HIDPP_FEATURE_FUNCTION_AS_ARG(feature) \
|
||||
feature >> 8, feature, 0x00
|
||||
|
||||
#define USB_VENDOR_ID_LOGITECH "046d"
|
||||
#define USB_DEVICE_ID_UNIFYING_RECEIVER "c52b"
|
||||
#define USB_DEVICE_ID_UNIFYING_RECEIVER_2 "c532"
|
||||
|
||||
#define UP_DEVICE_UNIFYING_READ_RESPONSE_TIMEOUT 3000 /* miliseconds */
|
||||
#define UP_DEVICE_UNIFYING_REFRESH_TIMEOUT 60L /* seconds */
|
||||
|
||||
struct UpDeviceUnifyingPrivate
|
||||
{
|
||||
guint poll_timer_id;
|
||||
int fd;
|
||||
/* Device index on the Unifying "bus" */
|
||||
gint device_index;
|
||||
gint feature_solar_dashboard_index;
|
||||
GIOChannel *channel;
|
||||
guint channel_source_id;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (UpDeviceUnifying, up_device_unifying, UP_TYPE_DEVICE)
|
||||
#define UP_DEVICE_UNIFYING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UP_TYPE_DEVICE_UNIFYING, UpDeviceUnifyingPrivate))
|
||||
|
||||
/**
|
||||
* up_device_unifying_event_io:
|
||||
*
|
||||
* Read events from Unifying device, and treats them.
|
||||
**/
|
||||
static gboolean
|
||||
up_device_unifying_event_io (GIOChannel *channel, GIOCondition condition, gpointer data)
|
||||
{
|
||||
guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
|
||||
UpDeviceUnifying *unifying = data;
|
||||
UpDevice *device = UP_DEVICE (unifying);
|
||||
GTimeVal timeval;
|
||||
guint16 lux;
|
||||
|
||||
while (read (unifying->priv->fd, buf, sizeof(buf)) > 0)
|
||||
if (buf[0] == HIDPP_HEADER_RESPONSE &&
|
||||
buf[1] == unifying->priv->device_index &&
|
||||
buf[2] == unifying->priv->feature_solar_dashboard_index &&
|
||||
buf[3] == HIDPP_FEATURE_SOLAR_DASHBOARD_BattLightMeasureBroadcastEvent) {
|
||||
lux = (buf[5] << 8) | buf[6];
|
||||
if (lux > 200) {
|
||||
g_object_set (device,
|
||||
"state", UP_DEVICE_STATE_CHARGING,
|
||||
"power-supply", TRUE,
|
||||
NULL);
|
||||
} else if (lux > 0) {
|
||||
g_object_set (device,
|
||||
"state", UP_DEVICE_STATE_DISCHARGING,
|
||||
"power-supply", TRUE,
|
||||
NULL);
|
||||
} else {
|
||||
g_object_set (device,
|
||||
"state", UP_DEVICE_STATE_DISCHARGING,
|
||||
"power-supply", FALSE,
|
||||
NULL);
|
||||
}
|
||||
|
||||
g_get_current_time (&timeval);
|
||||
|
||||
g_object_set (device,
|
||||
"update-time", (guint64) timeval.tv_sec,
|
||||
"percentage", (gdouble) (guint8) buf[4],
|
||||
"luminosity", (gdouble) lux,
|
||||
NULL);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gint
|
||||
up_device_unifying_read_response (int fd,
|
||||
guint8 request[],
|
||||
size_t count,
|
||||
gint64 start_time)
|
||||
{
|
||||
GPollFD poll[] = {
|
||||
{
|
||||
.fd = fd,
|
||||
.events = G_IO_IN | G_IO_HUP | G_IO_ERR,
|
||||
},
|
||||
};
|
||||
gint ret;
|
||||
|
||||
/* If we started to wait for a particular response more than some
|
||||
* time ago, abort */
|
||||
if (g_get_monotonic_time () - start_time
|
||||
>= UP_DEVICE_UNIFYING_READ_RESPONSE_TIMEOUT * 1000)
|
||||
return -1;
|
||||
|
||||
ret = g_poll (poll, G_N_ELEMENTS(poll),
|
||||
UP_DEVICE_UNIFYING_READ_RESPONSE_TIMEOUT);
|
||||
|
||||
if (ret > 0)
|
||||
return read (fd, request, count);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_hidpp1_set_battery:
|
||||
*
|
||||
* Send a READ SHORT REGISTER call to the device, and set battery status.
|
||||
**/
|
||||
static gboolean
|
||||
up_device_unifying_hidpp1_set_battery (UpDeviceUnifying *unifying)
|
||||
{
|
||||
UpDevice *device = UP_DEVICE (unifying);
|
||||
guint8 request[] = {
|
||||
HIDPP_HEADER_REQUEST,
|
||||
unifying->priv->device_index,
|
||||
HIDPP_READ_SHORT_REGISTER,
|
||||
HIDPP_READ_SHORT_REGISTER_BATTERY,
|
||||
0x00, 0x00, 0x00,
|
||||
};
|
||||
guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
|
||||
gint64 start_time;
|
||||
|
||||
if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
|
||||
g_debug ("Unable to read battery status from Unifying device %d",
|
||||
unifying->priv->device_index);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
start_time = g_get_monotonic_time ();
|
||||
|
||||
while (up_device_unifying_read_response (unifying->priv->fd, buf, sizeof (buf), start_time) > 0)
|
||||
if (buf[0] == HIDPP_HEADER_REQUEST
|
||||
&& buf[1] == unifying->priv->device_index
|
||||
&& buf[2] == HIDPP_READ_SHORT_REGISTER
|
||||
&& buf[3] == HIDPP_READ_SHORT_REGISTER_BATTERY) {
|
||||
g_object_set (device,
|
||||
"percentage", (gdouble) buf[4],
|
||||
NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_hidpp2_get_feature_index:
|
||||
*
|
||||
* Get a Unifying HID++ 2.0 feature index and return it.
|
||||
* Returns 0 if the feature does not exists on this device.
|
||||
**/
|
||||
static guint8
|
||||
up_device_unifying_hidpp2_get_feature_index (UpDeviceUnifying *unifying, guint16 feature)
|
||||
{
|
||||
guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
|
||||
guint8 request[] = {
|
||||
HIDPP_HEADER_REQUEST,
|
||||
unifying->priv->device_index,
|
||||
HIDPP_FEATURE_ROOT_INDEX,
|
||||
HIDPP_FEATURE_ROOT_FUNCTION_GETFEATURE,
|
||||
HIDPP_FEATURE_FUNCTION_AS_ARG(feature)
|
||||
};
|
||||
gint64 start_time;
|
||||
|
||||
/* Request the device name feature index */
|
||||
if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
|
||||
g_debug ("Unable to send GetFeature request to device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
start_time = g_get_monotonic_time ();
|
||||
|
||||
while (up_device_unifying_read_response (unifying->priv->fd, buf, sizeof (buf), start_time) > 0)
|
||||
if (buf[0] == HIDPP_HEADER_RESPONSE &&
|
||||
buf[1] == unifying->priv->device_index &&
|
||||
buf[2] == HIDPP_FEATURE_ROOT_INDEX &&
|
||||
buf[3] == HIDPP_FEATURE_ROOT_FUNCTION_GETFEATURE)
|
||||
return buf[4];
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* up_device_unifying_hidpp2_set_battery:
|
||||
*
|
||||
* Send a bunch of HID++ requests to get the device battery and set it.
|
||||
**/
|
||||
static gboolean
|
||||
up_device_unifying_hidpp2_set_battery (UpDeviceUnifying *unifying)
|
||||
{
|
||||
guint8 request[] = {
|
||||
HIDPP_HEADER_REQUEST,
|
||||
unifying->priv->device_index,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
if (unifying->priv->feature_solar_dashboard_index == -1)
|
||||
unifying->priv->feature_solar_dashboard_index =
|
||||
up_device_unifying_hidpp2_get_feature_index (unifying, HIDPP_FEATURE_SOLAR_DASHBOARD);
|
||||
|
||||
if (unifying->priv->feature_solar_dashboard_index == 0) {
|
||||
/* Probably not a solar keyboard */
|
||||
/* TODO: add support for BatteryLevelStatus */
|
||||
} else {
|
||||
/* This request will make the keyboard send a bunch of packets
|
||||
* (events) with lux-meter and battery information */
|
||||
request[2] = unifying->priv->feature_solar_dashboard_index;
|
||||
request[3] = HIDPP_FEATURE_SOLAR_DASHBOARD_FUNCTION_SetLightMeasure;
|
||||
request[4] = 0x01; /* Max number of reports: number of report sent after function call */
|
||||
request[5] = 0x01; /* Report period: time between reports, in seconds */
|
||||
|
||||
|
||||
if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
|
||||
g_debug ("Unable to send solar battery/lux events start request to device");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_hidpp2_get_device_name:
|
||||
*
|
||||
* Send a bunch of HID++ requests to get the device name (model) and return
|
||||
* it.
|
||||
**/
|
||||
static GString *
|
||||
up_device_unifying_hidpp2_get_device_name (UpDeviceUnifying *unifying)
|
||||
{
|
||||
GString *name = NULL;
|
||||
guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
|
||||
ssize_t res;
|
||||
guint8 request[] = {
|
||||
HIDPP_HEADER_REQUEST,
|
||||
unifying->priv->device_index,
|
||||
0x00,
|
||||
HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETCOUNT,
|
||||
0x00, 0x00, 0x00,
|
||||
};
|
||||
ssize_t name_length = 0;
|
||||
gint64 start_time;
|
||||
|
||||
request[2] = up_device_unifying_hidpp2_get_feature_index (unifying, HIDPP_FEATURE_GETDEVICENAMETYPE);
|
||||
|
||||
if (request[2] == 0) {
|
||||
g_debug ("Unable to find GetDeviceNameType feature index");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
|
||||
g_debug ("Unable to send GetDeviceNameType.GetCount request to device");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
start_time = g_get_monotonic_time ();
|
||||
|
||||
while (up_device_unifying_read_response (unifying->priv->fd, buf, sizeof (buf), start_time) > 0)
|
||||
if (buf[0] == HIDPP_HEADER_RESPONSE &&
|
||||
buf[1] == unifying->priv->device_index &&
|
||||
buf[2] == request[2] &&
|
||||
buf[3] == HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETCOUNT) {
|
||||
name_length = buf[4];
|
||||
break;
|
||||
}
|
||||
|
||||
name = g_string_new_len (NULL, name_length);
|
||||
|
||||
while (name_length > 0) {
|
||||
request[3] = HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETDEVICENAME;
|
||||
request[4] = name->len;
|
||||
|
||||
if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
|
||||
g_debug ("Unable to send GetDeviceNameType.GetDeviceName request to device");
|
||||
g_string_free (name, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
start_time = g_get_monotonic_time ();
|
||||
|
||||
while ((res = up_device_unifying_read_response (unifying->priv->fd, buf,
|
||||
sizeof (buf), start_time)) > 0)
|
||||
if (buf[0] == HIDPP_HEADER_RESPONSE &&
|
||||
buf[1] == unifying->priv->device_index &&
|
||||
buf[2] == request[2] &&
|
||||
buf[3] == HIDPP_FEATURE_GETDEVICENAMETYPE_FUNCTION_GETDEVICENAME) {
|
||||
g_string_append_len (name, (gchar *) &buf[4], MIN(res - 4, name_length));
|
||||
name_length -= MIN(res - 4, name_length);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Handle no response case */
|
||||
if (res <= 0) {
|
||||
g_debug ("Error reading GetDeviceNameType.GetDeviceName response");
|
||||
g_string_free (name, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_set_device_type:
|
||||
*
|
||||
* Send a Read Long Register HID++ 1.0 command to the device. This allows to
|
||||
* retrieve the type of the device, and then set it.
|
||||
**/
|
||||
static gboolean
|
||||
up_device_unifying_set_device_type (UpDeviceUnifying *unifying)
|
||||
{
|
||||
guint8 request[] = {
|
||||
HIDPP_HEADER_REQUEST,
|
||||
HIDPP_RECEIVER_ADDRESS,
|
||||
0x83, 0xb5,
|
||||
0x20 | (unifying->priv->device_index - 1),
|
||||
0x00, 0x00,
|
||||
};
|
||||
guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
|
||||
UpDevice *device = UP_DEVICE (unifying);
|
||||
gint64 start_time;
|
||||
|
||||
if (write (unifying->priv->fd, request, sizeof(request)) != sizeof(request)) {
|
||||
g_debug ("Unable to send a HID++ read long register request to device %d",
|
||||
unifying->priv->device_index);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
start_time = g_get_monotonic_time ();
|
||||
|
||||
while (up_device_unifying_read_response (unifying->priv->fd, buf, sizeof (buf), start_time) > 0)
|
||||
if (buf[0] == HIDPP_HEADER_RESPONSE
|
||||
&& buf[1] == HIDPP_RECEIVER_ADDRESS
|
||||
&& buf[2] == HIDPP_READ_LONG_REGISTER
|
||||
&& buf[3] == 0xb5
|
||||
&& buf[4] == (0x20 | (unifying->priv->device_index - 1))) {
|
||||
switch (buf[HIDPP_READ_LONG_REGISTER_DEVICE_TYPE]) {
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_KEYBOARD:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_NUMPAD:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_REMOTE_CONTROL:
|
||||
g_object_set (device, "type", UP_DEVICE_KIND_KEYBOARD, NULL);
|
||||
break;
|
||||
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_MOUSE:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TRACKBALL:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TOUCHPAD:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_PRESENTER:
|
||||
g_object_set (device, "type", UP_DEVICE_KIND_MOUSE, NULL);
|
||||
break;
|
||||
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_TABLET:
|
||||
g_object_set (device, "type", UP_DEVICE_KIND_TABLET, NULL);
|
||||
break;
|
||||
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_GAMEPAD:
|
||||
case HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_JOYSTICK:
|
||||
/* upower doesn't have something for this yet */
|
||||
g_object_set (device, "type", UP_DEVICE_KIND_UNKNOWN, NULL);
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_get_hidpp_version
|
||||
*
|
||||
* Return the version of HID++ used by a device.
|
||||
**/
|
||||
static gint
|
||||
up_device_unifying_get_hidpp_version (UpDeviceUnifying *unifying)
|
||||
{
|
||||
guint8 ping[] = {
|
||||
HIDPP_HEADER_REQUEST,
|
||||
unifying->priv->device_index,
|
||||
HIDPP_FEATURE_ROOT_INDEX,
|
||||
HIDPP_FEATURE_ROOT_FUNCTION_PING,
|
||||
0x00, 0x00, HIDPP_PING_DATA
|
||||
};
|
||||
guint8 buf[HIDPP_RESPONSE_LONG_LENGTH];
|
||||
gint64 start_time;
|
||||
|
||||
if (write(unifying->priv->fd, ping, sizeof(ping)) != sizeof(ping)) {
|
||||
g_debug ("Unable to send a HID++ ping to device %d",
|
||||
unifying->priv->device_index);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* read event */
|
||||
|
||||
start_time = g_get_monotonic_time ();
|
||||
|
||||
while (up_device_unifying_read_response (unifying->priv->fd, buf, sizeof (buf), start_time) > 0)
|
||||
if(buf[0] == HIDPP_HEADER_REQUEST
|
||||
&& buf[1] == unifying->priv->device_index
|
||||
&& buf[2] == HIDPP_ERR_INVALID_SUBID
|
||||
&& buf[3] == 0x00
|
||||
&& buf[4] == HIDPP_FEATURE_ROOT_FUNCTION_PING) {
|
||||
/* HID++ 1.0 ping reply */
|
||||
if (buf[5] == 0x01)
|
||||
return 1;
|
||||
else if (buf[5] == 0x09)
|
||||
/* device offline / unreachable */
|
||||
return 0;
|
||||
} else if (buf[0] == HIDPP_HEADER_RESPONSE
|
||||
&& buf[1] == unifying->priv->device_index
|
||||
&& buf[2] == HIDPP_FEATURE_ROOT_INDEX
|
||||
&& buf[3] == HIDPP_FEATURE_ROOT_FUNCTION_PING
|
||||
&& buf[6] == HIDPP_PING_DATA)
|
||||
/* HID++ >= 2.0 ping reply: buf[4] is major
|
||||
version, buf[5] is minor version but we
|
||||
only care about major for now*/
|
||||
return buf[4];
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_refresh:
|
||||
*
|
||||
* Return %TRUE on success, %FALSE if we failed to refresh or no data
|
||||
**/
|
||||
static gboolean
|
||||
up_device_unifying_refresh (UpDevice *device)
|
||||
{
|
||||
UpDeviceUnifying *unifying = UP_DEVICE_UNIFYING (device);
|
||||
gint hidpp_version = up_device_unifying_get_hidpp_version (unifying);
|
||||
GString *name;
|
||||
char *model;
|
||||
GTimeVal timeval;
|
||||
|
||||
if (hidpp_version > 0)
|
||||
g_debug ("Unifying device %d uses HID++ version %d",
|
||||
unifying->priv->device_index, hidpp_version);
|
||||
|
||||
switch (hidpp_version) {
|
||||
case 0:
|
||||
g_debug ("Unifying device %d is offline",
|
||||
unifying->priv->device_index);
|
||||
g_object_set (device,
|
||||
"is-present", FALSE,
|
||||
"state", UP_DEVICE_STATE_UNKNOWN,
|
||||
NULL);
|
||||
break;
|
||||
case 1:
|
||||
g_object_set (device,
|
||||
"state", UP_DEVICE_STATE_DISCHARGING,
|
||||
"is-present", TRUE,
|
||||
NULL);
|
||||
up_device_unifying_hidpp1_set_battery (unifying);
|
||||
break;
|
||||
case 2:
|
||||
g_object_set (device,
|
||||
"is-present", TRUE,
|
||||
NULL);
|
||||
|
||||
g_object_get (device, "model", &model, NULL);
|
||||
if (!model) {
|
||||
name = up_device_unifying_hidpp2_get_device_name (unifying);
|
||||
if (name) {
|
||||
g_object_set (device, "model", name->str, NULL);
|
||||
g_string_free (name, TRUE);
|
||||
}
|
||||
} else
|
||||
g_free (model);
|
||||
up_device_unifying_hidpp2_set_battery (unifying);
|
||||
break;
|
||||
}
|
||||
|
||||
g_get_current_time (&timeval);
|
||||
g_object_set (device, "update-time", (guint64) timeval.tv_sec, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_coldplug:
|
||||
*
|
||||
* Return %TRUE on success, %FALSE if we failed to get data and should be removed
|
||||
**/
|
||||
static gboolean
|
||||
up_device_unifying_coldplug (UpDevice *device)
|
||||
{
|
||||
UpDeviceUnifying *unifying = UP_DEVICE_UNIFYING (device);
|
||||
GUdevDevice *native;
|
||||
const gchar *device_file;
|
||||
const gchar *vendor;
|
||||
const gchar *parent_sysfs_path;
|
||||
const gchar *bus_address;
|
||||
GList *hidraw_list, *entry;
|
||||
size_t len;
|
||||
GIOStatus status;
|
||||
GError *error = NULL;
|
||||
GUdevClient *gudev_client;
|
||||
GUdevDevice *parent, *hidraw, *receiver = NULL;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
native = G_UDEV_DEVICE (up_device_get_native (device));
|
||||
|
||||
if(g_strcmp0(g_udev_device_get_property (native, "ID_VENDOR_ID"),
|
||||
USB_VENDOR_ID_LOGITECH) ||
|
||||
(g_strcmp0(g_udev_device_get_property (native, "ID_MODEL_ID"),
|
||||
USB_DEVICE_ID_UNIFYING_RECEIVER) &&
|
||||
g_strcmp0(g_udev_device_get_property (native, "ID_MODEL_ID"),
|
||||
USB_DEVICE_ID_UNIFYING_RECEIVER_2))) {
|
||||
g_debug ("Not an Unifying device, ignoring");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bus_address = g_udev_device_get_property (native, "PHYS");
|
||||
|
||||
if (!bus_address) {
|
||||
g_debug ("Device has no physical bus address, ignoring");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
len = strlen (bus_address);
|
||||
|
||||
if (len < 3 || bus_address[len - 3] != ':' || !g_ascii_isdigit (bus_address[len - 2])) {
|
||||
g_debug ("Invalid Unifying device index, ignoring");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
unifying->priv->device_index = g_ascii_digit_value (bus_address[len - 2]);
|
||||
|
||||
/* Find the hidraw device of the parent (the receiver) to
|
||||
* communicate with the devices */
|
||||
gudev_client = g_udev_client_new (NULL);
|
||||
|
||||
parent = g_udev_device_get_parent (native);
|
||||
parent_sysfs_path = g_udev_device_get_sysfs_path (parent);
|
||||
g_object_unref (parent);
|
||||
|
||||
hidraw_list = g_udev_client_query_by_subsystem (gudev_client, "hidraw");
|
||||
|
||||
for (entry = hidraw_list; entry; entry = entry->next) {
|
||||
hidraw = entry->data;
|
||||
if (!g_strcmp0 (g_udev_device_get_sysfs_attr (hidraw, "device"),
|
||||
parent_sysfs_path))
|
||||
receiver = hidraw;
|
||||
else
|
||||
g_object_unref (hidraw);
|
||||
}
|
||||
|
||||
if (!receiver) {
|
||||
g_debug ("Unable to find an hidraw device for Unifying receiver");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* get device file */
|
||||
device_file = g_udev_device_get_device_file (receiver);
|
||||
|
||||
/* connect to the device */
|
||||
g_debug ("Using Unifying receiver hidraw device file: %s", device_file);
|
||||
|
||||
if (device_file == NULL) {
|
||||
g_debug ("Could not get device file for Unifying receiver device");
|
||||
goto out;
|
||||
}
|
||||
|
||||
unifying->priv->fd = open (device_file, O_RDWR | O_NONBLOCK);
|
||||
if (unifying->priv->fd < 0) {
|
||||
g_debug ("cannot open device file %s", device_file);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
vendor = g_udev_device_get_property (native, "ID_VENDOR");
|
||||
|
||||
/* hardcode some default values */
|
||||
g_object_set (device,
|
||||
"vendor", vendor,
|
||||
"is-present", TRUE,
|
||||
"has-history", TRUE,
|
||||
"is-rechargeable", TRUE,
|
||||
"state", UP_DEVICE_STATE_DISCHARGING,
|
||||
"power-supply", FALSE,
|
||||
NULL);
|
||||
|
||||
/* Set device type */
|
||||
if (!up_device_unifying_set_device_type(unifying)) {
|
||||
g_debug ("Unable to guess device type, ignoring the device");
|
||||
goto out;
|
||||
}
|
||||
|
||||
unifying->priv->channel = g_io_channel_unix_new (unifying->priv->fd);
|
||||
|
||||
/* set binary encoding */
|
||||
status = g_io_channel_set_encoding (unifying->priv->channel, NULL, &error);
|
||||
if (status != G_IO_STATUS_NORMAL) {
|
||||
g_warning ("failed to set encoding: %s", error->message);
|
||||
g_error_free (error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* watch this */
|
||||
unifying->priv->channel_source_id = g_io_add_watch (unifying->priv->channel,
|
||||
G_IO_IN,
|
||||
up_device_unifying_event_io,
|
||||
unifying);
|
||||
|
||||
/* set up a poll to send the magic packet */
|
||||
unifying->priv->poll_timer_id = g_timeout_add_seconds (UP_DEVICE_UNIFYING_REFRESH_TIMEOUT,
|
||||
(GSourceFunc) up_device_unifying_refresh,
|
||||
device);
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
g_object_unref (gudev_client);
|
||||
g_object_unref (receiver);
|
||||
g_list_free (hidraw_list);
|
||||
|
||||
if (!ret && unifying->priv->fd >= 0)
|
||||
close (unifying->priv->fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_init:
|
||||
**/
|
||||
static void
|
||||
up_device_unifying_init (UpDeviceUnifying *unifying)
|
||||
{
|
||||
unifying->priv = UP_DEVICE_UNIFYING_GET_PRIVATE (unifying);
|
||||
unifying->priv->poll_timer_id = 0;
|
||||
unifying->priv->fd = -1;
|
||||
unifying->priv->feature_solar_dashboard_index = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_finalize:
|
||||
**/
|
||||
static void
|
||||
up_device_unifying_finalize (GObject *object)
|
||||
{
|
||||
UpDeviceUnifying *unifying;
|
||||
|
||||
g_return_if_fail (object != NULL);
|
||||
g_return_if_fail (UP_IS_DEVICE_UNIFYING (object));
|
||||
|
||||
unifying = UP_DEVICE_UNIFYING (object);
|
||||
g_return_if_fail (unifying->priv != NULL);
|
||||
|
||||
if (unifying->priv->poll_timer_id > 0)
|
||||
g_source_remove (unifying->priv->poll_timer_id);
|
||||
|
||||
if (unifying->priv->channel_source_id > 0)
|
||||
g_source_remove (unifying->priv->channel_source_id);
|
||||
|
||||
if (unifying->priv->channel) {
|
||||
g_io_channel_shutdown (unifying->priv->channel, FALSE, NULL);
|
||||
g_io_channel_unref (unifying->priv->channel);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (up_device_unifying_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_class_init:
|
||||
**/
|
||||
static void
|
||||
up_device_unifying_class_init (UpDeviceUnifyingClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
UpDeviceClass *device_class = UP_DEVICE_CLASS (klass);
|
||||
|
||||
object_class->finalize = up_device_unifying_finalize;
|
||||
device_class->coldplug = up_device_unifying_coldplug;
|
||||
device_class->refresh = up_device_unifying_refresh;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (UpDeviceUnifyingPrivate));
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_new:
|
||||
**/
|
||||
UpDeviceUnifying *
|
||||
up_device_unifying_new (void)
|
||||
{
|
||||
return g_object_new (UP_TYPE_DEVICE_UNIFYING, NULL);
|
||||
}
|
||||
|
||||
284
src/linux/up-device-unifying.c
Normal file
284
src/linux/up-device-unifying.c
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2012 Julien Danjou <julien@danjou.info>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gudev/gudev.h>
|
||||
|
||||
#include "hidpp-device.h"
|
||||
|
||||
#include "up-device-unifying.h"
|
||||
#include "up-types.h"
|
||||
|
||||
#define UP_DEVICE_UNIFYING_REFRESH_TIMEOUT 60 /* seconds */
|
||||
|
||||
struct UpDeviceUnifyingPrivate
|
||||
{
|
||||
guint poll_timer_id;
|
||||
HidppDevice *hidpp_device;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (UpDeviceUnifying, up_device_unifying, UP_TYPE_DEVICE)
|
||||
#define UP_DEVICE_UNIFYING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UP_TYPE_DEVICE_UNIFYING, UpDeviceUnifyingPrivate))
|
||||
|
||||
/**
|
||||
* up_device_unifying_refresh:
|
||||
*
|
||||
* Return %TRUE on success, %FALSE if we failed to refresh or no data
|
||||
**/
|
||||
static gboolean
|
||||
up_device_unifying_refresh (UpDevice *device)
|
||||
{
|
||||
gboolean ret;
|
||||
GError *error = NULL;
|
||||
GTimeVal timeval;
|
||||
UpDeviceState state = UP_DEVICE_STATE_UNKNOWN;
|
||||
UpDeviceUnifying *unifying = UP_DEVICE_UNIFYING (device);
|
||||
UpDeviceUnifyingPrivate *priv = unifying->priv;
|
||||
|
||||
/* refresh just the battery stats */
|
||||
ret = hidpp_device_refresh (priv->hidpp_device,
|
||||
HIDPP_REFRESH_FLAGS_BATTERY,
|
||||
&error);
|
||||
if (!ret) {
|
||||
g_warning ("failed to coldplug unifying device: %s",
|
||||
error->message);
|
||||
g_error_free (error);
|
||||
goto out;
|
||||
}
|
||||
switch (hidpp_device_get_batt_status (priv->hidpp_device)) {
|
||||
case HIDPP_DEVICE_BATT_STATUS_CHARGING:
|
||||
state = UP_DEVICE_STATE_CHARGING;
|
||||
break;
|
||||
case HIDPP_DEVICE_BATT_STATUS_DISCHARGING:
|
||||
state = UP_DEVICE_STATE_DISCHARGING;
|
||||
break;
|
||||
case HIDPP_DEVICE_BATT_STATUS_CHARGED:
|
||||
state = UP_DEVICE_STATE_FULLY_CHARGED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
g_get_current_time (&timeval);
|
||||
g_object_set (device,
|
||||
"is-present", hidpp_device_get_version (priv->hidpp_device) > 0,
|
||||
"percentage", (gdouble) hidpp_device_get_batt_percentage (priv->hidpp_device),
|
||||
"state", state,
|
||||
"update-time", (guint64) timeval.tv_sec,
|
||||
NULL);
|
||||
out:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UpDeviceKind
|
||||
up_device_unifying_get_device_kind (UpDeviceUnifying *unifying)
|
||||
{
|
||||
UpDeviceKind kind;
|
||||
switch (hidpp_device_get_kind (unifying->priv->hidpp_device)) {
|
||||
case HIDPP_DEVICE_KIND_MOUSE:
|
||||
case HIDPP_DEVICE_KIND_TOUCHPAD:
|
||||
case HIDPP_DEVICE_KIND_TRACKBALL:
|
||||
kind = UP_DEVICE_KIND_MOUSE;
|
||||
break;
|
||||
case HIDPP_DEVICE_KIND_KEYBOARD:
|
||||
kind = UP_DEVICE_KIND_KEYBOARD;
|
||||
break;
|
||||
case HIDPP_DEVICE_KIND_TABLET:
|
||||
kind = UP_DEVICE_KIND_TABLET;
|
||||
break;
|
||||
default:
|
||||
kind = UP_DEVICE_KIND_UNKNOWN;
|
||||
}
|
||||
return kind;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_coldplug:
|
||||
*
|
||||
* Return %TRUE on success, %FALSE if we failed to get data and should be removed
|
||||
**/
|
||||
static gboolean
|
||||
up_device_unifying_coldplug (UpDevice *device)
|
||||
{
|
||||
const gchar *bus_address;
|
||||
const gchar *device_file;
|
||||
const gchar *type;
|
||||
gboolean ret = FALSE;
|
||||
gchar *endptr = NULL;
|
||||
gchar *tmp;
|
||||
GError *error = NULL;
|
||||
GUdevDevice *native;
|
||||
GUdevDevice *parent = NULL;
|
||||
GUdevDevice *receiver = NULL;
|
||||
UpDeviceUnifying *unifying = UP_DEVICE_UNIFYING (device);
|
||||
GUdevClient *client = NULL;
|
||||
GList *hidraw_list = NULL;
|
||||
GList *l;
|
||||
|
||||
native = G_UDEV_DEVICE (up_device_get_native (device));
|
||||
|
||||
/* check if we have the right device */
|
||||
type = g_udev_device_get_property (native, "UPOWER_BATTERY_TYPE");
|
||||
if (type == NULL)
|
||||
goto out;
|
||||
if (g_strcmp0 (type, "unifying") != 0)
|
||||
goto out;
|
||||
|
||||
/* get the device index */
|
||||
unifying->priv->hidpp_device = hidpp_device_new ();
|
||||
bus_address = g_udev_device_get_property (native, "HID_PHYS");
|
||||
tmp = g_strrstr (bus_address, ":");
|
||||
if (tmp == NULL) {
|
||||
g_debug ("Could not get physical device index");
|
||||
goto out;
|
||||
}
|
||||
hidpp_device_set_index (unifying->priv->hidpp_device,
|
||||
g_ascii_strtoull (tmp + 1, &endptr, 10));
|
||||
if (endptr != NULL && endptr[0] != '\0') {
|
||||
g_debug ("HID_PHYS malformed: '%s'", bus_address);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* find the hidraw device that matches the parent */
|
||||
parent = g_udev_device_get_parent (native);
|
||||
client = g_udev_client_new (NULL);
|
||||
hidraw_list = g_udev_client_query_by_subsystem (client, "hidraw");
|
||||
for (l = hidraw_list; l != NULL; l = l->next) {
|
||||
if (g_strcmp0 (g_udev_device_get_sysfs_path (parent),
|
||||
g_udev_device_get_sysfs_attr (l->data, "device")) == 0) {
|
||||
receiver = g_object_ref (l->data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (receiver == NULL) {
|
||||
g_debug ("Unable to find an hidraw device for Unifying receiver");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* connect to the receiver */
|
||||
device_file = g_udev_device_get_device_file (receiver);
|
||||
if (device_file == NULL) {
|
||||
g_debug ("Could not get device file for Unifying receiver device");
|
||||
goto out;
|
||||
}
|
||||
g_debug ("Using Unifying receiver hidraw device file: %s", device_file);
|
||||
hidpp_device_set_hidraw_device (unifying->priv->hidpp_device,
|
||||
device_file);
|
||||
|
||||
/* coldplug initial parameters */
|
||||
ret = hidpp_device_refresh (unifying->priv->hidpp_device,
|
||||
HIDPP_REFRESH_FLAGS_VERSION |
|
||||
HIDPP_REFRESH_FLAGS_KIND |
|
||||
HIDPP_REFRESH_FLAGS_MODEL,
|
||||
&error);
|
||||
if (!ret) {
|
||||
g_warning ("failed to coldplug unifying device: %s",
|
||||
error->message);
|
||||
g_error_free (error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* set some default values */
|
||||
g_object_set (device,
|
||||
"vendor", g_udev_device_get_property (native, "ID_VENDOR"),
|
||||
"type", up_device_unifying_get_device_kind (unifying),
|
||||
"model", hidpp_device_get_model (unifying->priv->hidpp_device),
|
||||
"has-history", TRUE,
|
||||
"is-rechargeable", TRUE,
|
||||
"power-supply", FALSE,
|
||||
NULL);
|
||||
|
||||
/* set up a poll to send the magic packet */
|
||||
up_device_unifying_refresh (device);
|
||||
unifying->priv->poll_timer_id = g_timeout_add_seconds (UP_DEVICE_UNIFYING_REFRESH_TIMEOUT,
|
||||
(GSourceFunc) up_device_unifying_refresh,
|
||||
device);
|
||||
ret = TRUE;
|
||||
out:
|
||||
g_list_foreach (hidraw_list, (GFunc) g_object_unref, NULL);
|
||||
g_list_free (hidraw_list);
|
||||
if (parent != NULL)
|
||||
g_object_unref (parent);
|
||||
if (receiver != NULL)
|
||||
g_object_unref (receiver);
|
||||
if (client != NULL)
|
||||
g_object_unref (client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_init:
|
||||
**/
|
||||
static void
|
||||
up_device_unifying_init (UpDeviceUnifying *unifying)
|
||||
{
|
||||
unifying->priv = UP_DEVICE_UNIFYING_GET_PRIVATE (unifying);
|
||||
unifying->priv->poll_timer_id = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_finalize:
|
||||
**/
|
||||
static void
|
||||
up_device_unifying_finalize (GObject *object)
|
||||
{
|
||||
UpDeviceUnifying *unifying;
|
||||
|
||||
g_return_if_fail (object != NULL);
|
||||
g_return_if_fail (UP_IS_DEVICE_UNIFYING (object));
|
||||
|
||||
unifying = UP_DEVICE_UNIFYING (object);
|
||||
g_return_if_fail (unifying->priv != NULL);
|
||||
|
||||
if (unifying->priv->poll_timer_id > 0)
|
||||
g_source_remove (unifying->priv->poll_timer_id);
|
||||
if (unifying->priv->hidpp_device != NULL)
|
||||
g_object_unref (unifying->priv->hidpp_device);
|
||||
|
||||
G_OBJECT_CLASS (up_device_unifying_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_class_init:
|
||||
**/
|
||||
static void
|
||||
up_device_unifying_class_init (UpDeviceUnifyingClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
UpDeviceClass *device_class = UP_DEVICE_CLASS (klass);
|
||||
|
||||
object_class->finalize = up_device_unifying_finalize;
|
||||
device_class->coldplug = up_device_unifying_coldplug;
|
||||
device_class->refresh = up_device_unifying_refresh;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (UpDeviceUnifyingPrivate));
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_unifying_new:
|
||||
**/
|
||||
UpDeviceUnifying *
|
||||
up_device_unifying_new (void)
|
||||
{
|
||||
return g_object_new (UP_TYPE_DEVICE_UNIFYING, NULL);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue