1
0
forked from drath/libratbag

Add first hid++ 2.0 protocol support

The Logitech devices talk HID++, and we need a lib to handle that.
The first implemented command is the get protocol version which allows
to know if the device is using hid++ 1.0 or 2.0.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
This commit is contained in:
Benjamin Tissoires
2015-08-20 14:12:08 -04:00
parent 874fc3386f
commit b8637f0bc3
4 changed files with 238 additions and 0 deletions

View File

@@ -8,6 +8,8 @@ libratbag_la_SOURCES = \
driver-logitech-unifying.c \
hidpp10.c \
hidpp10.h \
hidpp20.c \
hidpp20.h \
libratbag.c \
libratbag.h \
libratbag-hidraw.c \

View File

@@ -29,6 +29,12 @@
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
*/
/*
* for this driver to work, you need a kernel >= v3.19 or one which contains
* 925f0f3ed24f98b40c28627e74ff3e7f9d1e28bc ("HID: logitech-dj: allow transfer
* of HID++ reports from/to the correct dj device")
*/
#include "config.h"
#include <linux/types.h>
@@ -37,6 +43,7 @@
#include <string.h>
#include <unistd.h>
#include "hidpp20.h"
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
@@ -108,6 +115,15 @@ unifying_probe(struct ratbag_device *device, const struct ratbag_id id)
drv_data->proto_major = 1;
drv_data->proto_minor = 0;
rc = hidpp20_root_get_protocol_version(device, &drv_data->proto_major, &drv_data->proto_minor);
if (rc) {
/* communication error, best to ignore the device */
rc = -EINVAL;
goto err;
}
log_info(device->ratbag, "'%s' is using protocol v%d.%d\n", ratbag_device_get_name(device), drv_data->proto_major, drv_data->proto_minor);
ratbag_set_drv_data(device, drv_data);
device->num_profiles = 1;

160
src/hidpp20.c Normal file
View File

@@ -0,0 +1,160 @@
/*
* HID++ 2.0 library.
*
* Copyright 2015 Benjamin Tissoires <benjamin.tissoires@gmail.com>
* Copyright 2015 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++ 2.0 documentation provided by Nestor Lopez Casado at:
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "hidpp20.h"
#include "libratbag.h"
#include "libratbag-hidraw.h"
#include "libratbag-util.h"
#include "libratbag-private.h"
static int
hidpp20_write_command(struct ratbag_device *device, uint8_t *cmd, int size)
{
int res = ratbag_hidraw_output_report(device, cmd, size);
if (res == 0)
return 0;
if (res < 0)
log_error(device->ratbag, "Error: %d\n", errno);
return res;
}
int
hidpp20_request_command(struct ratbag_device *device, union hidpp20_message *msg)
{
struct ratbag *ratbag = device->ratbag;
union hidpp20_message read_buffer;
int ret;
uint8_t hidpp_err = 0;
size_t msg_len;
msg_len = msg->msg.report_id == REPORT_ID_SHORT ? SHORT_MESSAGE_LENGTH : LONG_MESSAGE_LENGTH;
log_buf_debug(ratbag, "sending: ", msg->data, msg_len);
/* Send the message to the Device */
ret = hidpp20_write_command(device, msg->data, msg_len);
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 = ratbag_hidraw_read_input_report(device, read_buffer.data, LONG_MESSAGE_LENGTH);
log_buf_debug(ratbag, " *** received: ", read_buffer.data, ret);
if (read_buffer.msg.report_id != REPORT_ID_SHORT &&
read_buffer.msg.report_id != REPORT_ID_LONG)
continue;
/* actual answer */
if (read_buffer.msg.sub_id == msg->msg.sub_id &&
read_buffer.msg.address == msg->msg.address)
break;
/* error */
if (read_buffer.msg.sub_id == __ERROR_MSG &&
(read_buffer.msg.parameters[0] & 0x0f) == (read_buffer.msg.address & 0x0f)) {
hidpp_err = read_buffer.msg.parameters[1];
log_error(ratbag,
" HID++ error from the device (%d): %s (%02x)\n",
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) {
log_error(ratbag, " USB error: %d\n", errno);
perror("write");
goto out_err;
}
if (!hidpp_err) {
log_buf_debug(ratbag, " received: ", read_buffer.data, ret);
/* copy the answer for the caller */
*msg = read_buffer;
}
ret = hidpp_err;
out_err:
return ret;
}
/* -------------------------------------------------------------------------- */
/* 0x0000: Root */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_ROOT 0x0000
#define HIDPP_PAGE_ROOT_IDX 0x00
#define CMD_ROOT_GET_FEATURE 0x08
#define CMD_ROOT_GET_PROTOCOL_VERSION 0x18
int
hidpp20_root_get_protocol_version(struct ratbag_device *device,
unsigned *major,
unsigned *minor)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = 0xff,
.msg.sub_id = HIDPP_PAGE_ROOT_IDX,
.msg.address = CMD_ROOT_GET_PROTOCOL_VERSION,
};
rc = hidpp20_request_command(device, &msg);
if (rc == ERR_INVALID_SUBID) {
*major = 1;
*minor = 0;
return 0;
}
if (rc == 0) {
*major = msg.msg.parameters[0];
*minor = msg.msg.parameters[1];
}
return rc;
}

60
src/hidpp20.h Normal file
View File

@@ -0,0 +1,60 @@
/*
* HID++ 2.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++ 2.0 documentation provided by Nestor Lopez Casado at:
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
*/
#ifndef HIDPP_20_H
#define HIDPP_20_H
#include "hidpp10.h"
extern const char *hidpp_errors[0xFF];
struct _hidpp20_message {
uint8_t report_id;
uint8_t device_idx;
uint8_t sub_id;
uint8_t address;
uint8_t parameters[LONG_MESSAGE_LENGTH - 4U];
} __attribute__((packed));
union hidpp20_message {
struct _hidpp20_message msg;
uint8_t data[LONG_MESSAGE_LENGTH];
};
int hidpp20_request_command(struct ratbag_device *device, union hidpp20_message *msg);
/* -------------------------------------------------------------------------- */
/* 0x0000: Root */
/* -------------------------------------------------------------------------- */
int hidpp20_root_get_protocol_version(struct ratbag_device *device,
unsigned *major,
unsigned *minor);
#endif /* HIDPP_20_H */