mirror of
https://github.com/libratbag/libratbag.git
synced 2026-11-07 23:23:52 +01:00
Start moving uLogitech files into libratbag proper
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
@@ -4,14 +4,19 @@ include_HEADERS = \
|
||||
libratbag.h
|
||||
|
||||
libratbag_la_SOURCES = \
|
||||
debug.h \
|
||||
etekcity.c \
|
||||
hidpp10.c \
|
||||
hidpp10.h \
|
||||
libratbag.c \
|
||||
libratbag.h \
|
||||
libratbag-hidraw.c \
|
||||
libratbag-hidraw.h \
|
||||
libratbag-util.c \
|
||||
libratbag-private.h \
|
||||
libratbag-util.h
|
||||
libratbag-util.h \
|
||||
unifying.h \
|
||||
unifying.c
|
||||
|
||||
libratbag_la_LIBADD = $(LIBUDEV_LIBS)
|
||||
|
||||
|
||||
59
src/debug.h
Normal file
59
src/debug.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* debug definitions.
|
||||
*
|
||||
* Copyright 2013 Benjamin Tissoires <benjamin.tissoires@gmail.com>
|
||||
* Copyright 2013 Red Hat, Inc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DEBUG_H
|
||||
#define DEBUG_H
|
||||
|
||||
#define DEBUG_LVL 0
|
||||
|
||||
#if DEBUG_LVL > 0
|
||||
static void pr_buffer(__u8 *buffer, int size) {
|
||||
char *sep = "";
|
||||
int i;
|
||||
|
||||
if (size < 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < size; ++i) {
|
||||
printf("%s%02x", sep, buffer[i] & 0xFF);
|
||||
sep = " ";
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
#else
|
||||
#define pr_buffer(buffer, size) do {} while (0)
|
||||
#endif
|
||||
|
||||
#if DEBUG_LVL > 0
|
||||
#define pr_dbg(fmt, arg...) \
|
||||
printf(fmt, ##arg)
|
||||
#else /* DEBUG_LVL > 0 */
|
||||
#define pr_dbg(fmt, arg...) \
|
||||
do {} while(0);
|
||||
#endif /* DEBUG_LVL > 0 */
|
||||
|
||||
#endif /* DEBUG_H */
|
||||
294
src/hidpp10.c
Normal file
294
src/hidpp10.c
Normal file
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* HID++ 1.0 library.
|
||||
*
|
||||
* Copyright 2013 Benjamin Tissoires <benjamin.tissoires@gmail.com>
|
||||
* Copyright 2013 Red Hat, Inc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Based on the HID++ 1.0 documentation provided by Nestor Lopez Casado at:
|
||||
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <debug.h>
|
||||
#include <hidpp10.h>
|
||||
|
||||
#include <libratbag-util.h>
|
||||
|
||||
#if DEBUG_LVL > 0
|
||||
const char *hidpp_errors[0xFF] = {
|
||||
[0x00] = "ERR_SUCCESS",
|
||||
[0x01] = "ERR_INVALID_SUBID",
|
||||
[0x02] = "ERR_INVALID_ADDRESS",
|
||||
[0x03] = "ERR_INVALID_VALUE",
|
||||
[0x04] = "ERR_CONNECT_FAIL",
|
||||
[0x05] = "ERR_TOO_MANY_DEVICES",
|
||||
[0x06] = "ERR_ALREADY_EXISTS",
|
||||
[0x07] = "ERR_BUSY",
|
||||
[0x08] = "ERR_UNKNOWN_DEVICE",
|
||||
[0x09] = "ERR_RESOURCE_ERROR",
|
||||
[0x0A] = "ERR_REQUEST_UNAVAILABLE",
|
||||
[0x0B] = "ERR_INVALID_PARAM_VALUE",
|
||||
[0x0C] = "ERR_WRONG_PIN_CODE",
|
||||
[0x0D ... 0xFE] = NULL,
|
||||
};
|
||||
#endif
|
||||
|
||||
const char *device_types[0xFF] = {
|
||||
[0x00] = "Unknown",
|
||||
[0x01] = "Keyboard",
|
||||
[0x02] = "Mouse",
|
||||
[0x03] = "Numpad",
|
||||
[0x04] = "Presenter",
|
||||
[0x05 ... 0x07] = "Reserved for future",
|
||||
[0x06] = "ERR_ALREADY_EXISTS",
|
||||
[0x07] = "ERR_BUSY",
|
||||
[0x08] = "Trackball",
|
||||
[0x09] = "Touchpad",
|
||||
[0x0A ... 0xFE] = NULL,
|
||||
};
|
||||
|
||||
|
||||
static int hidpp10_write_command(int fd, __u8 *cmd, int size) {
|
||||
int res = write(fd, cmd, size);
|
||||
|
||||
if (res == size)
|
||||
return 0;
|
||||
|
||||
if (res < 0) {
|
||||
printf("Error: %d\n", errno);
|
||||
perror("write");
|
||||
} else {
|
||||
errno = ENOMEM;
|
||||
printf("write: %d were written instead of %d.\n", res, size);
|
||||
perror("write");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int hidpp10_request_command(int fd, union hidpp10_message *msg) {
|
||||
union hidpp10_message read_buffer;
|
||||
union hidpp10_message expected_header;
|
||||
union hidpp10_message expected_error_recv = ERROR_MSG(msg, RECEIVER_IDX);
|
||||
union hidpp10_message expected_error_dev = ERROR_MSG(msg, msg->msg.device_idx);
|
||||
int ret;
|
||||
__u8 hidpp_err = 0;
|
||||
|
||||
/* create the expected header */
|
||||
expected_header = *msg;
|
||||
switch (msg->msg.sub_id) {
|
||||
case SET_REGISTER_REQ:
|
||||
expected_header.msg.report_id = REPORT_ID_SHORT;
|
||||
break;
|
||||
case GET_REGISTER_REQ:
|
||||
expected_header.msg.report_id = REPORT_ID_SHORT;
|
||||
break;
|
||||
case SET_LONG_REGISTER_REQ:
|
||||
expected_header.msg.report_id = REPORT_ID_LONG;
|
||||
break;
|
||||
case GET_LONG_REGISTER_REQ:
|
||||
expected_header.msg.report_id = REPORT_ID_LONG;
|
||||
break;
|
||||
}
|
||||
|
||||
pr_dbg("sending: "); pr_buffer(msg->data, SHORT_MESSAGE_LENGTH);
|
||||
#if DEBUG_LVL > 1
|
||||
pr_dbg(" expected_header: "); pr_buffer(expected_header.data, SHORT_MESSAGE_LENGTH);
|
||||
pr_dbg(" expected_error_recv: "); pr_buffer(expected_error_recv.data, SHORT_MESSAGE_LENGTH);
|
||||
pr_dbg(" expected_error_dev: "); pr_buffer(expected_error_dev.data, SHORT_MESSAGE_LENGTH);
|
||||
#endif
|
||||
|
||||
/* Send the message to the Device */
|
||||
ret = hidpp10_write_command(fd, msg->data, SHORT_MESSAGE_LENGTH);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
/*
|
||||
* Now read the answers from the device:
|
||||
* loop until we get the actual answer or an error code.
|
||||
*/
|
||||
do {
|
||||
ret = read(fd, read_buffer.data, LONG_MESSAGE_LENGTH);
|
||||
#if DEBUG_LVL > 2
|
||||
printf(" *** received: "); pr_buffer(read_buffer.data, ret);
|
||||
#endif
|
||||
/* actual answer */
|
||||
if (!memcmp(read_buffer.data, expected_header.data, 4))
|
||||
break;
|
||||
|
||||
/* error */
|
||||
if (!memcmp(read_buffer.data, expected_error_recv.data, 5) ||
|
||||
!memcmp(read_buffer.data, expected_error_dev.data, 5)) {
|
||||
hidpp_err = read_buffer.msg.parameters[1];
|
||||
pr_dbg(" HID++ error from the %s (%d): %s (%02x)\n",
|
||||
read_buffer.msg.device_idx == RECEIVER_IDX ? "receiver" : "device",
|
||||
read_buffer.msg.device_idx,
|
||||
hidpp_errors[hidpp_err] ? hidpp_errors[hidpp_err] : "Undocumented error code",
|
||||
hidpp_err);
|
||||
break;
|
||||
}
|
||||
} while (ret > 0);
|
||||
|
||||
if (ret < 0) {
|
||||
printf(" USB error: %d\n", errno);
|
||||
perror("write");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (!hidpp_err) {
|
||||
pr_dbg(" received: "); pr_buffer(read_buffer.data, ret);
|
||||
/* copy the answer for the caller */
|
||||
*msg = read_buffer;
|
||||
}
|
||||
|
||||
ret = hidpp_err;
|
||||
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hidpp10_toggle_individual_feature(int fd, struct unifying_device *dev, int feature_bit_r0, int feature_bit_r2) {
|
||||
unsigned idx = dev->index;
|
||||
union hidpp10_message mode = CMD_ENABLE_INDIVIDUAL_FEATURES(idx, GET_REGISTER_REQ);
|
||||
int res;
|
||||
|
||||
/* first read the current values */
|
||||
res = hidpp10_request_command(fd, &mode);
|
||||
if (res)
|
||||
return -1;
|
||||
|
||||
/* toggle bits of r0 */
|
||||
if (feature_bit_r0 >= 0)
|
||||
mode.msg.parameters[0] ^= 1U << feature_bit_r0;
|
||||
|
||||
/* toggle bits of r2 */
|
||||
if (feature_bit_r2 >= 0)
|
||||
mode.msg.parameters[2] ^= 1U << feature_bit_r2;
|
||||
|
||||
/* now write back the change */
|
||||
mode.msg.sub_id = SET_REGISTER_REQ;
|
||||
res = hidpp10_request_command(fd, &mode);
|
||||
return res;
|
||||
}
|
||||
|
||||
int hidpp10_open_lock(int fd) {
|
||||
union hidpp10_message open_lock = CMD_DEVICE_CONNECTION_DISCONNECTION(0x00, CONNECT_DEVICES_OPEN_LOCK, 0x08);
|
||||
|
||||
return hidpp10_request_command(fd, &open_lock);
|
||||
}
|
||||
|
||||
int hidpp10_disconnect(int fd, int idx) {
|
||||
union hidpp10_message disconnect = CMD_DEVICE_CONNECTION_DISCONNECTION(idx + 1, CONNECT_DEVICES_DISCONNECT, 0x00);
|
||||
|
||||
return hidpp10_request_command(fd, &disconnect);
|
||||
}
|
||||
|
||||
void hidpp10_list_devices(int fd) {
|
||||
struct unifying_device dev;
|
||||
int i, res;
|
||||
|
||||
for (i = 0; i < 6; ++i) {
|
||||
res = hidpp10_get_device_from_idx(fd, i, &dev);
|
||||
if (res)
|
||||
continue;
|
||||
|
||||
printf("[%d] %s %s (Wireless PID: %04x)\n", i, device_types[dev.device_type] ? device_types[dev.device_type] : "", dev.name, dev.wpid);
|
||||
}
|
||||
}
|
||||
|
||||
static int hidpp10_get_device_info(int fd, struct unifying_device *dev) {
|
||||
unsigned idx = dev->index;
|
||||
union hidpp10_message pairing_information = CMD_PAIRING_INFORMATION(idx, DEVICE_PAIRING_INFORMATION);
|
||||
union hidpp10_message device_name = CMD_PAIRING_INFORMATION(idx, DEVICE_NAME);
|
||||
union hidpp10_message firmware_information = CMD_DEVICE_FIRMWARE_INFORMATION(idx, FIRMWARE_INFO_ITEM_FW_NAME_AND_VERSION(1));
|
||||
union hidpp10_message build_information = CMD_DEVICE_FIRMWARE_INFORMATION(idx, FIRMWARE_INFO_ITEM_FW_BUILD_NUMBER(1));
|
||||
int name_size;
|
||||
int res;
|
||||
|
||||
res = hidpp10_request_command(fd, &pairing_information);
|
||||
if (res)
|
||||
return -1;
|
||||
|
||||
dev->report_interval = pairing_information.msg.string[2];
|
||||
dev->wpid = (pairing_information.msg.string[3] << 8) |
|
||||
pairing_information.msg.string[4];
|
||||
dev->device_type = pairing_information.msg.string[7];
|
||||
|
||||
res = hidpp10_request_command(fd, &device_name);
|
||||
if (res)
|
||||
return -1;
|
||||
|
||||
name_size = device_name.msg.string[1];
|
||||
memcpy(dev->name, &device_name.msg.string[2], sizeof(device_name.msg.string));
|
||||
dev->name[min(name_size, sizeof(dev->name) - 1)] = '\0';
|
||||
|
||||
/*
|
||||
* This may fail on some devices
|
||||
* => we can not retrieve their FW version through HID++ 1.0.
|
||||
*/
|
||||
res = hidpp10_request_command(fd, &firmware_information);
|
||||
if (res)
|
||||
return 0;
|
||||
|
||||
dev->fw_major = firmware_information.msg.string[1];
|
||||
dev->fw_minor = firmware_information.msg.string[2];
|
||||
|
||||
res = hidpp10_request_command(fd, &build_information);
|
||||
if (res)
|
||||
return 0;
|
||||
|
||||
dev->build = (build_information.msg.string[1] << 8) |
|
||||
build_information.msg.string[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hidpp10_get_device_from_wpid(int fd, __u16 wpid, struct unifying_device *dev) {
|
||||
int i, res;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
res = hidpp10_get_device_from_idx(fd, i, dev);
|
||||
if (res)
|
||||
continue;
|
||||
|
||||
if (dev->wpid == wpid)
|
||||
break;
|
||||
|
||||
res = -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int hidpp10_get_device_from_idx(int fd, int idx, struct unifying_device *dev) {
|
||||
dev->index = idx;
|
||||
return hidpp10_get_device_info(fd, dev);
|
||||
}
|
||||
167
src/hidpp10.h
Normal file
167
src/hidpp10.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* HID++ 1.0 library - headers file.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Based on the HID++ 1.0 documentation provided by Nestor Lopez Casado at:
|
||||
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
|
||||
*/
|
||||
|
||||
#include <debug.h>
|
||||
#include <unifying.h>
|
||||
|
||||
#ifndef HIDPP_10_H
|
||||
#define HIDPP_10_H
|
||||
|
||||
#define RECEIVER_IDX 0xFF
|
||||
|
||||
#define REPORT_ID_SHORT 0x10
|
||||
#define REPORT_ID_LONG 0x11
|
||||
|
||||
#define SHORT_MESSAGE_LENGTH 7
|
||||
#define LONG_MESSAGE_LENGTH 20
|
||||
|
||||
#define SET_REGISTER_REQ 0x80
|
||||
#define SET_REGISTER_RSP 0x80
|
||||
#define GET_REGISTER_REQ 0x81
|
||||
#define GET_REGISTER_RSP 0x81
|
||||
#define SET_LONG_REGISTER_REQ 0x82
|
||||
#define SET_LONG_REGISTER_RSP 0x82
|
||||
#define GET_LONG_REGISTER_REQ 0x83
|
||||
#define GET_LONG_REGISTER_RSP 0x83
|
||||
#define __ERROR_MSG 0x8F
|
||||
|
||||
#define __CMD_ENABLE_INDIVIDUAL_FEATURES 0x01
|
||||
#define FEATURE_BIT_R0_SPECIAL_BUTTON_FUNCTION 1
|
||||
#define FEATURE_BIT_R0_ENHANCED_KEY_USAGE 2
|
||||
#define FEATURE_BIT_R0_FAST_FORWARD_REWIND 3
|
||||
#define FEATURE_BIT_R0_SCROLLING_ACCELERATION 6
|
||||
#define FEATURE_BIT_R0_BUTTONS_CONTROL_THE_RESOLUTION 7
|
||||
#define FEATURE_BIT_R2_INHIBIT_LOCK_KEY_SOUND 0
|
||||
#define FEATURE_BIT_R2_3D_ENGINE 2
|
||||
#define FEATURE_BIT_R2_HOST_SW_CONTROLS_LEDS 3
|
||||
|
||||
#define __CMD_DEVICE_CONNECTION_DISCONNECTION 0xB2
|
||||
#define CONNECT_DEVICES_OPEN_LOCK 1
|
||||
#define CONNECT_DEVICES_CLOSE_LOCK 2
|
||||
#define CONNECT_DEVICES_DISCONNECT 3
|
||||
|
||||
#define __CMD_PAIRING_INFORMATION 0xB5
|
||||
#define DEVICE_PAIRING_INFORMATION 0x20
|
||||
#define DEVICE_EXTENDED_PAIRING_INFORMATION 0x30
|
||||
#define DEVICE_NAME 0x40
|
||||
|
||||
#define __CMD_DEVICE_FIRMWARE_INFORMATION 0xF1
|
||||
#define FIRMWARE_INFO_ITEM_FW_NAME_AND_VERSION(MCU) ((MCU - 1) << 4 | 0x01)
|
||||
#define FIRMWARE_INFO_ITEM_FW_BUILD_NUMBER(MCU) ((MCU - 1) << 4 | 0x02)
|
||||
#define FIRMWARE_INFO_ITEM_HW_VERSION(MCU) ((MCU - 1) << 4 | 0x03)
|
||||
#define FIRMWARE_INFO_ITEM_BOOTLOADER_VERSION(MCU) ((MCU - 1) << 4 | 0x04)
|
||||
|
||||
#define CMD_PAIRING_INFORMATION(idx, type) { \
|
||||
.msg = { \
|
||||
.report_id = REPORT_ID_SHORT, \
|
||||
.device_idx = RECEIVER_IDX, \
|
||||
.sub_id = GET_LONG_REGISTER_REQ, \
|
||||
.address = __CMD_PAIRING_INFORMATION, \
|
||||
.parameters = {type + idx, 0x00, 0x00 }, \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CMD_DEVICE_FIRMWARE_INFORMATION(idx, fw_info_item) { \
|
||||
.msg = { \
|
||||
.report_id = REPORT_ID_SHORT, \
|
||||
.device_idx = idx + 1, \
|
||||
.sub_id = GET_REGISTER_REQ, \
|
||||
.address = __CMD_DEVICE_FIRMWARE_INFORMATION, \
|
||||
.parameters = {fw_info_item, 0x00, 0x00 }, \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CMD_ENABLE_INDIVIDUAL_FEATURES(idx, sub) { \
|
||||
.msg = { \
|
||||
.report_id = REPORT_ID_SHORT, \
|
||||
.device_idx = idx + 1, \
|
||||
.sub_id = sub, \
|
||||
.address = __CMD_ENABLE_INDIVIDUAL_FEATURES, \
|
||||
.parameters = {0x00, 0x00, 0x00 }, \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CMD_DEVICE_CONNECTION_DISCONNECTION(idx, cmd, timeout) { \
|
||||
.msg = { \
|
||||
.report_id = REPORT_ID_SHORT, \
|
||||
.device_idx = RECEIVER_IDX, \
|
||||
.sub_id = SET_REGISTER_REQ, \
|
||||
.address = __CMD_DEVICE_CONNECTION_DISCONNECTION, \
|
||||
.parameters = {cmd, idx, timeout }, \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ERR_SUCCESS 0x00
|
||||
#define ERR_INVALID_SUBID 0x01
|
||||
#define ERR_INVALID_ADDRESS 0x02
|
||||
#define ERR_INVALID_VALUE 0x03
|
||||
#define ERR_CONNECT_FAIL 0x04
|
||||
#define ERR_TOO_MANY_DEVICES 0x05
|
||||
#define ERR_ALREADY_EXISTS 0x06
|
||||
#define ERR_BUSY 0x07
|
||||
#define ERR_UNKNOWN_DEVICE 0x08
|
||||
#define ERR_RESOURCE_ERROR 0x09
|
||||
#define ERR_REQUEST_UNAVAILABLE 0x0A
|
||||
#define ERR_INVALID_PARAM_VALUE 0x0B
|
||||
#define ERR_WRONG_PIN_CODE 0x0C
|
||||
|
||||
#define ERROR_MSG(__hidpp_msg, idx) { \
|
||||
.msg = { \
|
||||
.report_id = REPORT_ID_SHORT, \
|
||||
.device_idx = idx, \
|
||||
.sub_id = __ERROR_MSG, \
|
||||
.address = __hidpp_msg->msg.sub_id, \
|
||||
.parameters = {__hidpp_msg->msg.address, 0x00, 0x00 }, \
|
||||
} \
|
||||
}
|
||||
|
||||
struct _hidpp10_message {
|
||||
__u8 report_id;
|
||||
__u8 device_idx;
|
||||
__u8 sub_id;
|
||||
__u8 address;
|
||||
union {
|
||||
__u8 parameters[3];
|
||||
__u8 string[16];
|
||||
};
|
||||
};
|
||||
|
||||
union hidpp10_message {
|
||||
struct _hidpp10_message msg;
|
||||
__u8 data[LONG_MESSAGE_LENGTH];
|
||||
};
|
||||
|
||||
|
||||
int hidpp10_request_command(int fd, union hidpp10_message *msg);
|
||||
int hidpp10_toggle_individual_feature(int fd, struct unifying_device *dev, int feature_bit_r0, int feature_bit_r2);
|
||||
int hidpp10_open_lock(int fd);
|
||||
int hidpp10_disconnect(int fd, int idx);
|
||||
void hidpp10_list_devices(int fd);
|
||||
int hidpp10_get_device_from_wpid(int fd, __u16 wpid, struct unifying_device *dev);
|
||||
int hidpp10_get_device_from_idx(int fd, int idx, struct unifying_device *dev);
|
||||
#endif /* HIDPP_10_H */
|
||||
126
src/unifying.c
Normal file
126
src/unifying.c
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Logitech Unifying Receiver library.
|
||||
*
|
||||
* Copyright 2013 Benjamin Tissoires <benjamin.tissoires@gmail.com>
|
||||
* Copyright 2013 Red Hat, Inc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Based on the HID++ 1.0 documentation provided by Nestor Lopez Casado at:
|
||||
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/hidraw.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <unifying.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define DEV_DIR "/dev"
|
||||
#define HIDRAW_DEV_NAME "hidraw"
|
||||
|
||||
int unifying_open_receiver(const char *hidraw) {
|
||||
struct hidraw_devinfo info;
|
||||
int fd, res;
|
||||
/* Open the Device with non-blocking reads. */
|
||||
fd = open(hidraw, O_RDWR);
|
||||
|
||||
if (fd < 0) {
|
||||
perror("Unable to open device");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Get Raw Info */
|
||||
res = ioctl(fd, HIDIOCGRAWINFO, &info);
|
||||
if (res < 0) {
|
||||
perror("error while getting info from device");
|
||||
} else {
|
||||
/* Check if the device is an Unifying receiver */
|
||||
if (info.bustype != BUS_USB ||
|
||||
info.vendor != USB_VENDOR_ID_LOGITECH ||
|
||||
(info.product != USB_DEVICE_ID_UNIFYING_RECEIVER &&
|
||||
info.product != USB_DEVICE_ID_UNIFYING_RECEIVER_2))
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
return fd;
|
||||
|
||||
out_err:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return fd >= 0 ? -1 : fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter for the AutoDevProbe scandir on /dev.
|
||||
*
|
||||
* @param dir The current directory entry provided by scandir.
|
||||
*
|
||||
* @return Non-zero if the given directory entry starts with "hidraw", or zero
|
||||
* otherwise.
|
||||
*/
|
||||
static int is_hidraw_device(const struct dirent *dir) {
|
||||
return strncmp(HIDRAW_DEV_NAME, dir->d_name, 6) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans all /dev/hidraw*, open the first unifying receiver.
|
||||
*
|
||||
* @return The hidraw device file descriptor of the first unifying receiver.
|
||||
*/
|
||||
int unifying_find_receiver(void) {
|
||||
struct dirent **namelist;
|
||||
int fd = -1;
|
||||
char fname[64];
|
||||
int i, ndev;
|
||||
|
||||
ndev = scandir(DEV_DIR, &namelist, is_hidraw_device, alphasort);
|
||||
if (ndev <= 0)
|
||||
return fd;
|
||||
|
||||
for (i = 0; i < ndev; i++)
|
||||
{
|
||||
if (fd < 0) {
|
||||
snprintf(fname, sizeof(fname),
|
||||
"%s/%s", DEV_DIR, namelist[i]->d_name);
|
||||
fd = unifying_open_receiver(fname);
|
||||
}
|
||||
|
||||
free(namelist[i]);
|
||||
}
|
||||
|
||||
free(namelist);
|
||||
|
||||
if (fd >= 0)
|
||||
printf("Using %s as the Unifying receiver.\n", fname);
|
||||
else
|
||||
printf("Unable to find an Unifying receiver.\n");
|
||||
|
||||
return fd;
|
||||
}
|
||||
59
src/unifying.h
Normal file
59
src/unifying.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Logitech Unifying Receiver library - headers file.
|
||||
*
|
||||
* Copyright 2013 Benjamin Tissoires <benjamin.tissoires@gmail.com>
|
||||
* Copyright 2013 Red Hat, Inc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Based on the HID++ 1.0 documentation provided by Nestor Lopez Casado at:
|
||||
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
|
||||
*/
|
||||
|
||||
#ifndef UNIFYING_H
|
||||
#define UNIFYING_H
|
||||
|
||||
#define USB_VENDOR_ID_LOGITECH (__u32)0x046d
|
||||
#define USB_DEVICE_ID_UNIFYING_RECEIVER (__s16)0xc52b
|
||||
#define USB_DEVICE_ID_UNIFYING_RECEIVER_2 (__s16)0xc532
|
||||
|
||||
struct unifying_device {
|
||||
unsigned index;
|
||||
char name[15];
|
||||
__u16 wpid;
|
||||
__u8 report_interval;
|
||||
__u8 device_type;
|
||||
__u8 fw_major;
|
||||
__u8 fw_minor;
|
||||
__u16 build;
|
||||
};
|
||||
|
||||
int unifying_open_receiver(const char *hidraw);
|
||||
|
||||
/**
|
||||
* Scans all /dev/hidraw*, open the first unifying receiver.
|
||||
*
|
||||
* @return The hidraw device file descriptor of the first unifying receiver.
|
||||
*/
|
||||
int unifying_find_receiver(void);
|
||||
|
||||
#endif /* UNIFYING_H */
|
||||
Reference in New Issue
Block a user