mirror of
https://github.com/libratbag/libratbag.git
synced 2026-11-07 23:23:52 +01:00
Looks like the hid++ 1.0 devices are also using the same crc computation, so put it in the generic part. Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
347 lines
10 KiB
C
347 lines
10 KiB
C
/*
|
|
* HID++ generic definitions
|
|
*
|
|
* 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++ documentation provided by Nestor Lopez Casado at:
|
|
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "hidpp-generic.h"
|
|
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
#include <stddef.h>
|
|
|
|
#include "libratbag-private.h"
|
|
|
|
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,
|
|
};
|
|
|
|
struct hidpp20_1b04_action_mapping {
|
|
uint16_t value;
|
|
const char *name;
|
|
struct ratbag_button_action action;
|
|
};
|
|
|
|
#define BUTTON_ACTION_MAPPING(v_, n_, act_) \
|
|
{ v_, n_, act_ }
|
|
|
|
static const struct hidpp20_1b04_action_mapping hidpp20_1b04_logical_mapping[] =
|
|
{
|
|
BUTTON_ACTION_MAPPING(0, "None" , BUTTON_ACTION_NONE),
|
|
BUTTON_ACTION_MAPPING(1, "Volume Up" , BUTTON_ACTION_KEY(KEY_VOLUMEUP)),
|
|
BUTTON_ACTION_MAPPING(2, "Volume Down" , BUTTON_ACTION_KEY(KEY_VOLUMEDOWN)),
|
|
BUTTON_ACTION_MAPPING(3, "Mute" , BUTTON_ACTION_KEY(KEY_MUTE)),
|
|
BUTTON_ACTION_MAPPING(4, "Play/Pause" , BUTTON_ACTION_KEY(KEY_PLAYPAUSE)),
|
|
BUTTON_ACTION_MAPPING(5, "Next" , BUTTON_ACTION_KEY(KEY_NEXTSONG)),
|
|
BUTTON_ACTION_MAPPING(6, "Previous" , BUTTON_ACTION_KEY(KEY_PREVIOUSSONG)),
|
|
BUTTON_ACTION_MAPPING(7, "Stop" , BUTTON_ACTION_KEY(KEY_STOPCD)),
|
|
BUTTON_ACTION_MAPPING(80, "Left" , BUTTON_ACTION_BUTTON(1)),
|
|
BUTTON_ACTION_MAPPING(81, "Right" , BUTTON_ACTION_BUTTON(2)),
|
|
BUTTON_ACTION_MAPPING(82, "Middle" , BUTTON_ACTION_BUTTON(3)),
|
|
BUTTON_ACTION_MAPPING(83, "Back" , BUTTON_ACTION_BUTTON(4)),
|
|
BUTTON_ACTION_MAPPING(86, "Forward" , BUTTON_ACTION_BUTTON(5)),
|
|
BUTTON_ACTION_MAPPING(89, "Button 6" , BUTTON_ACTION_BUTTON(6)),
|
|
BUTTON_ACTION_MAPPING(90, "Button 7" , BUTTON_ACTION_BUTTON(7)),
|
|
BUTTON_ACTION_MAPPING(91, "Left Scroll" , BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT)),
|
|
BUTTON_ACTION_MAPPING(92, "Button 8" , BUTTON_ACTION_BUTTON(8)),
|
|
BUTTON_ACTION_MAPPING(93, "Right Scroll" , BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT)),
|
|
BUTTON_ACTION_MAPPING(94, "Button 9" , BUTTON_ACTION_BUTTON(9)),
|
|
BUTTON_ACTION_MAPPING(95, "Button 10" , BUTTON_ACTION_BUTTON(10)),
|
|
BUTTON_ACTION_MAPPING(96, "Button 11" , BUTTON_ACTION_BUTTON(11)),
|
|
BUTTON_ACTION_MAPPING(97, "Button 12" , BUTTON_ACTION_BUTTON(12)),
|
|
BUTTON_ACTION_MAPPING(98, "Button 13" , BUTTON_ACTION_BUTTON(13)),
|
|
BUTTON_ACTION_MAPPING(99, "Button 14" , BUTTON_ACTION_BUTTON(14)),
|
|
BUTTON_ACTION_MAPPING(100, "Button 15" , BUTTON_ACTION_BUTTON(15)),
|
|
BUTTON_ACTION_MAPPING(101, "Button 16" , BUTTON_ACTION_BUTTON(16)),
|
|
BUTTON_ACTION_MAPPING(102, "Button 17" , BUTTON_ACTION_BUTTON(17)),
|
|
BUTTON_ACTION_MAPPING(103, "Button 18" , BUTTON_ACTION_BUTTON(18)),
|
|
BUTTON_ACTION_MAPPING(104, "Button 19" , BUTTON_ACTION_BUTTON(19)),
|
|
BUTTON_ACTION_MAPPING(105, "Button 20" , BUTTON_ACTION_BUTTON(20)),
|
|
BUTTON_ACTION_MAPPING(106, "Button 21" , BUTTON_ACTION_BUTTON(21)),
|
|
BUTTON_ACTION_MAPPING(107, "Button 22" , BUTTON_ACTION_BUTTON(22)),
|
|
BUTTON_ACTION_MAPPING(108, "Button 23" , BUTTON_ACTION_BUTTON(23)),
|
|
BUTTON_ACTION_MAPPING(109, "Button 24" , BUTTON_ACTION_BUTTON(24)),
|
|
BUTTON_ACTION_MAPPING(184, "Second Left" , BUTTON_ACTION_BUTTON(1)),
|
|
BUTTON_ACTION_MAPPING(195, "AppSwitchGesture" , BUTTON_ACTION_NONE),
|
|
BUTTON_ACTION_MAPPING(196, "SmartShift" , BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH)),
|
|
BUTTON_ACTION_MAPPING(315, "LedToggle" , BUTTON_ACTION_NONE),
|
|
};
|
|
|
|
struct hidpp20_1b04_physical_mapping {
|
|
uint16_t value;
|
|
const char *name;
|
|
enum ratbag_button_type type;
|
|
};
|
|
|
|
#define BUTTON_PHYS_MAPPING(v_, n_, t_) \
|
|
{ v_, n_, t_ }
|
|
|
|
static const struct hidpp20_1b04_physical_mapping hidpp20_1b04_physical_mapping[] =
|
|
{
|
|
BUTTON_PHYS_MAPPING(0, "None" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(1, "Volume Up" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(2, "Volume Down" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(3, "Mute" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(4, "Play/Pause" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(5, "Next" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(6, "Previous" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(7, "Stop" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(56, "Left Click" , RATBAG_BUTTON_TYPE_LEFT),
|
|
BUTTON_PHYS_MAPPING(57, "Right Click" , RATBAG_BUTTON_TYPE_RIGHT),
|
|
BUTTON_PHYS_MAPPING(58, "Middle Click" , RATBAG_BUTTON_TYPE_MIDDLE),
|
|
BUTTON_PHYS_MAPPING(59, "Wheel Side Click Left" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(60, "Back Click" , RATBAG_BUTTON_TYPE_SIDE),
|
|
BUTTON_PHYS_MAPPING(61, "Wheel Side Click Right", RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(62, "Forward Click" , RATBAG_BUTTON_TYPE_EXTRA),
|
|
BUTTON_PHYS_MAPPING(63, "Left Scroll" , RATBAG_BUTTON_TYPE_WHEEL_LEFT),
|
|
BUTTON_PHYS_MAPPING(64, "Right Scroll" , RATBAG_BUTTON_TYPE_WHEEL_RIGHT),
|
|
BUTTON_PHYS_MAPPING(98, "Do Nothing" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(156, "Gesture Button" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
BUTTON_PHYS_MAPPING(157, "SmartShift" , RATBAG_BUTTON_TYPE_WHEEL_RATCHET_MODE_SHIFT),
|
|
BUTTON_PHYS_MAPPING(221, "LedToggle" , RATBAG_BUTTON_TYPE_UNKNOWN),
|
|
};
|
|
|
|
const struct ratbag_button_action *
|
|
hidpp20_1b04_get_logical_mapping(uint16_t value)
|
|
{
|
|
const struct hidpp20_1b04_action_mapping *map;
|
|
|
|
ARRAY_FOR_EACH(hidpp20_1b04_logical_mapping, map) {
|
|
if (map->value == value)
|
|
return &map->action;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
uint16_t
|
|
hidpp20_1b04_get_logical_control_id(const struct ratbag_button_action *action)
|
|
{
|
|
const struct hidpp20_1b04_action_mapping *mapping;
|
|
|
|
ARRAY_FOR_EACH(hidpp20_1b04_logical_mapping, mapping) {
|
|
if (ratbag_button_action_match(&mapping->action, action))
|
|
return mapping->value;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const char *
|
|
hidpp20_1b04_get_logical_mapping_name(uint16_t value)
|
|
{
|
|
const struct hidpp20_1b04_action_mapping *mapping;
|
|
|
|
ARRAY_FOR_EACH(hidpp20_1b04_logical_mapping, mapping) {
|
|
if (mapping->value == value)
|
|
return mapping->name;
|
|
}
|
|
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
enum ratbag_button_type
|
|
hidpp20_1b04_get_physical_mapping(uint16_t value)
|
|
{
|
|
const struct hidpp20_1b04_physical_mapping *map;
|
|
|
|
ARRAY_FOR_EACH(hidpp20_1b04_physical_mapping, map) {
|
|
if (map->value == value)
|
|
return map->type;
|
|
}
|
|
|
|
return RATBAG_BUTTON_TYPE_UNKNOWN;
|
|
}
|
|
|
|
const char *
|
|
hidpp20_1b04_get_physical_mapping_name(uint16_t value)
|
|
{
|
|
const struct hidpp20_1b04_physical_mapping *map;
|
|
|
|
ARRAY_FOR_EACH(hidpp20_1b04_physical_mapping, map) {
|
|
if (map->value == value)
|
|
return map->name;
|
|
}
|
|
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
int
|
|
hidpp_write_command(struct hidpp_device *dev, uint8_t *cmd, int size)
|
|
{
|
|
int fd = dev->hidraw_fd;
|
|
int res;
|
|
|
|
if (size < 1 || !cmd || fd < 0)
|
|
return -EINVAL;
|
|
|
|
hidpp_log_buf_raw(dev, "hidpp write: ", cmd, size);
|
|
res = write(fd, cmd, size);
|
|
if (res < 0) {
|
|
res = -errno;
|
|
hidpp_log_error(dev, "Error: %s (%d)\n", strerror(-res), -res);
|
|
}
|
|
|
|
return res < 0 ? res : 0;
|
|
}
|
|
|
|
int
|
|
hidpp_read_response(struct hidpp_device *dev, uint8_t *buf, size_t size)
|
|
{
|
|
int fd = dev->hidraw_fd;
|
|
struct pollfd fds;
|
|
int rc;
|
|
|
|
if (size < 1 || !buf || fd < 0)
|
|
return -EINVAL;
|
|
|
|
fds.fd = fd;
|
|
fds.events = POLLIN;
|
|
|
|
rc = poll(&fds, 1, 1000);
|
|
if (rc == -1)
|
|
return -errno;
|
|
|
|
if (rc == 0)
|
|
return -ETIMEDOUT;
|
|
|
|
rc = read(fd, buf, size);
|
|
|
|
if (rc > 0)
|
|
hidpp_log_buf_raw(dev, "hidpp read: ", buf, rc);
|
|
|
|
return rc >= 0 ? rc : -errno;
|
|
}
|
|
|
|
void
|
|
hidpp_log(struct hidpp_device *dev,
|
|
enum hidpp_log_priority priority,
|
|
const char *format,
|
|
...)
|
|
{
|
|
va_list args;
|
|
|
|
if (dev->log_priority > priority)
|
|
return;
|
|
|
|
va_start(args, format);
|
|
dev->log_handler(dev->userdata, priority, format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void
|
|
hidpp_log_buffer(struct hidpp_device *dev,
|
|
enum hidpp_log_priority priority,
|
|
const char *header,
|
|
uint8_t *buf, size_t len)
|
|
{
|
|
_cleanup_free_ char *output_buf = NULL;
|
|
char *sep = "";
|
|
unsigned int i, n;
|
|
unsigned int buf_len;
|
|
|
|
buf_len = header ? strlen(header) : 0;
|
|
buf_len += len * 3;
|
|
buf_len += 1; /* terminating '\0' */
|
|
|
|
output_buf = zalloc(buf_len);
|
|
n = 0;
|
|
if (header)
|
|
n += snprintf_safe(output_buf, buf_len - n, "%s", header);
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
n += snprintf_safe(&output_buf[n], buf_len - n, "%s%02x", sep, buf[i] & 0xFF);
|
|
sep = " ";
|
|
}
|
|
|
|
hidpp_log(dev, priority, "%s\n", output_buf);
|
|
}
|
|
|
|
static void
|
|
simple_log(void *userdata, enum hidpp_log_priority priority, const char *format, va_list args)
|
|
{
|
|
vprintf(format, args);
|
|
}
|
|
|
|
void
|
|
hidpp_device_init(struct hidpp_device *dev, int fd)
|
|
{
|
|
dev->hidraw_fd = fd;
|
|
hidpp_device_set_log_handler(dev, simple_log, HIDPP_LOG_PRIORITY_INFO, NULL);
|
|
}
|
|
|
|
void
|
|
hidpp_device_set_log_handler(struct hidpp_device *dev,
|
|
hidpp_log_handler log_handler,
|
|
enum hidpp_log_priority priority,
|
|
void *userdata)
|
|
{
|
|
dev->log_handler = log_handler;
|
|
dev->log_priority = priority;
|
|
dev->userdata = userdata;
|
|
}
|
|
|
|
/*
|
|
* The following crc computation has been provided by Logitech
|
|
*/
|
|
#define CRC_CCITT_SEED 0xFFFF
|
|
|
|
uint16_t
|
|
hidpp_crc_ccitt(uint8_t *data, unsigned int length)
|
|
{
|
|
uint16_t crc, temp, quick;
|
|
unsigned int i;
|
|
|
|
crc = CRC_CCITT_SEED;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
temp = (crc >> 8) ^ (*data++);
|
|
crc <<= 8;
|
|
quick = temp ^ (temp >> 4);
|
|
crc ^= quick;
|
|
quick <<= 5;
|
|
crc ^= quick;
|
|
quick <<= 7;
|
|
crc ^= quick;
|
|
}
|
|
|
|
return crc;
|
|
}
|