forked from drath/libratbag
Might not be the best solution for hidpp10, because it just removes the duplicates, but this way all of the other drivers can benefit from the raw debug parameter. Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
915 lines
23 KiB
C
915 lines
23 KiB
C
/*
|
|
* 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 <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbool.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"
|
|
|
|
const char*
|
|
hidpp20_feature_get_name(uint16_t feature)
|
|
{
|
|
static char numeric[8];
|
|
const char *str;
|
|
#define CASE_RETURN_STRING(a) case a: return #a; break
|
|
|
|
switch(feature)
|
|
{
|
|
CASE_RETURN_STRING(HIDPP_PAGE_ROOT);
|
|
CASE_RETURN_STRING(HIDPP_PAGE_FEATURE_SET);
|
|
CASE_RETURN_STRING(HIDPP_PAGE_MOUSE_POINTER_BASIC);
|
|
CASE_RETURN_STRING(HIDPP_PAGE_ADJUSTABLE_DPI);
|
|
CASE_RETURN_STRING(HIDPP_PAGE_SPECIAL_KEYS_BUTTONS);
|
|
CASE_RETURN_STRING(HIDPP_PAGE_BATTERY_LEVEL_STATUS);
|
|
CASE_RETURN_STRING(HIDPP_PAGE_KBD_REPROGRAMMABLE_KEYS);
|
|
default:
|
|
sprintf(numeric, "%#4x", feature);
|
|
str = numeric;
|
|
break;
|
|
}
|
|
|
|
#undef CASE_RETURN_STRING
|
|
return str;
|
|
}
|
|
|
|
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: %s (%d)\n", strerror(-res), -res);
|
|
|
|
return res;
|
|
}
|
|
|
|
static int
|
|
hidpp20_request_command_allow_error(struct ratbag_device *device, union hidpp20_message *msg,
|
|
bool allow_error)
|
|
{
|
|
struct ratbag *ratbag = device->ratbag;
|
|
union hidpp20_message read_buffer;
|
|
int ret;
|
|
uint8_t hidpp_err = 0;
|
|
size_t msg_len;
|
|
|
|
/* msg->address is 4 MSB: subcommand, 4 LSB: 4-bit SW identifier so
|
|
* the device knows who to respond to. kernel uses 0x1 */
|
|
const int DEVICE_SW_ID = 0x8;
|
|
|
|
if (msg->msg.address & 0xf) {
|
|
log_bug_libratbag(ratbag, "hidpp20 error: sw address is already set\n");
|
|
return -EINVAL;
|
|
}
|
|
msg->msg.address |= DEVICE_SW_ID;
|
|
|
|
msg_len = msg->msg.report_id == REPORT_ID_SHORT ? SHORT_MESSAGE_LENGTH : LONG_MESSAGE_LENGTH;
|
|
|
|
/* 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);
|
|
|
|
/* Wait and retry if the USB timed out */
|
|
if (ret == -ETIMEDOUT) {
|
|
msleep(10);
|
|
ret = ratbag_hidraw_read_input_report(device, read_buffer.data, LONG_MESSAGE_LENGTH);
|
|
}
|
|
|
|
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.sub_id == 0xff) &&
|
|
read_buffer.msg.address == msg->msg.sub_id &&
|
|
read_buffer.msg.parameters[0] == msg->msg.address) {
|
|
hidpp_err = read_buffer.msg.parameters[1];
|
|
if (allow_error)
|
|
log_debug(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);
|
|
else
|
|
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: %s (%d)\n", strerror(-ret), -ret);
|
|
perror("write");
|
|
goto out_err;
|
|
}
|
|
|
|
if (!hidpp_err) {
|
|
/* copy the answer for the caller */
|
|
*msg = read_buffer;
|
|
}
|
|
|
|
ret = hidpp_err;
|
|
|
|
out_err:
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
hidpp20_request_command(struct ratbag_device *device, union hidpp20_message *msg)
|
|
{
|
|
return hidpp20_request_command_allow_error(device, msg, false);
|
|
}
|
|
|
|
static inline uint16_t
|
|
hidpp20_get_unaligned_u16(uint8_t *buf)
|
|
{
|
|
return (buf[0] << 8) | buf[1];
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* 0x0000: Root */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#define HIDPP_PAGE_ROOT_IDX 0x00
|
|
|
|
#define CMD_ROOT_GET_FEATURE 0x00
|
|
#define CMD_ROOT_GET_PROTOCOL_VERSION 0x10
|
|
|
|
int
|
|
hidpp_root_get_feature(struct ratbag_device *device,
|
|
uint16_t feature,
|
|
uint8_t *feature_index,
|
|
uint8_t *feature_type,
|
|
uint8_t *feature_version)
|
|
{
|
|
int rc;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = HIDPP_PAGE_ROOT_IDX,
|
|
.msg.address = CMD_ROOT_GET_FEATURE,
|
|
.msg.parameters[0] = feature >> 8,
|
|
.msg.parameters[1] = feature & 0xff,
|
|
};
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
*feature_index = msg.msg.parameters[0];
|
|
*feature_type = msg.msg.parameters[1];
|
|
*feature_version = msg.msg.parameters[2];
|
|
|
|
log_raw(device->ratbag, "feature 0x%04x is at 0x%02x\n", feature, *feature_index);
|
|
return 0;
|
|
}
|
|
|
|
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_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = HIDPP_PAGE_ROOT_IDX,
|
|
.msg.address = CMD_ROOT_GET_PROTOCOL_VERSION,
|
|
};
|
|
|
|
rc = hidpp20_request_command_allow_error(device, &msg, true);
|
|
|
|
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;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* 0x0001: Feature Set */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#define CMD_FEATURE_SET_GET_COUNT 0x00
|
|
#define CMD_FEATURE_SET_GET_FEATURE_ID 0x10
|
|
|
|
static int
|
|
hidpp20_feature_set_get_count(struct ratbag_device *device, uint8_t reg)
|
|
{
|
|
int rc;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = reg,
|
|
.msg.address = CMD_FEATURE_SET_GET_COUNT,
|
|
};
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return msg.msg.parameters[0];
|
|
}
|
|
|
|
static int
|
|
hidpp20_feature_set_get_feature_id(struct ratbag_device *device,
|
|
uint8_t reg,
|
|
uint8_t feature_index,
|
|
uint16_t *feature,
|
|
uint8_t *type)
|
|
{
|
|
int rc;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = reg,
|
|
.msg.address = CMD_FEATURE_SET_GET_FEATURE_ID,
|
|
.msg.parameters[0] = feature_index,
|
|
};
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
*feature = hidpp20_get_unaligned_u16(msg.msg.parameters);
|
|
*type = msg.msg.parameters[2];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hidpp20_feature_set_get(struct ratbag_device *device,
|
|
struct hidpp20_feature **feature_list)
|
|
{
|
|
uint8_t feature_index, feature_type, feature_version;
|
|
struct hidpp20_feature *flist;
|
|
int rc;
|
|
uint8_t feature_count;
|
|
unsigned int i;
|
|
|
|
rc = hidpp_root_get_feature(device,
|
|
HIDPP_PAGE_FEATURE_SET,
|
|
&feature_index,
|
|
&feature_type,
|
|
&feature_version);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = hidpp20_feature_set_get_count(device, feature_index);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
feature_count = (uint8_t)rc;
|
|
|
|
if (!feature_count) {
|
|
*feature_list = NULL;
|
|
return 0;
|
|
}
|
|
|
|
flist = zalloc(feature_count * sizeof(struct hidpp20_feature));
|
|
|
|
for (i = 0; i < feature_count; i++) {
|
|
rc = hidpp20_feature_set_get_feature_id(device,
|
|
feature_index,
|
|
i,
|
|
&flist[i].feature,
|
|
&flist[i].type);
|
|
if (rc)
|
|
goto err;
|
|
}
|
|
|
|
*feature_list = flist;
|
|
return feature_count;
|
|
err:
|
|
free(flist);
|
|
return rc;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* 0x1000: Battery level status */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#define CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS 0x00
|
|
#define CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_CAPABILITY 0x10
|
|
|
|
int
|
|
hidpp20_batterylevel_get_battery_level(struct ratbag_device *device,
|
|
uint16_t *level,
|
|
uint16_t *next_level)
|
|
{
|
|
uint8_t feature_index, feature_type, feature_version;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.address = CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS,
|
|
};
|
|
int rc;
|
|
|
|
rc = hidpp_root_get_feature(device,
|
|
HIDPP_PAGE_BATTERY_LEVEL_STATUS,
|
|
&feature_index,
|
|
&feature_type,
|
|
&feature_version);
|
|
if (rc)
|
|
return rc;
|
|
|
|
msg.msg.sub_id = feature_index;
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
*level = msg.msg.parameters[0];
|
|
*next_level = msg.msg.parameters[1];
|
|
|
|
return msg.msg.parameters[2];
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* 0x1b00: KBD reprogrammable keys and mouse buttons */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#define CMD_KBD_REPROGRAMMABLE_KEYS_GET_COUNT 0x00
|
|
#define CMD_KBD_REPROGRAMMABLE_KEYS_GET_CTRL_ID_INFO 0x10
|
|
|
|
static int
|
|
hidpp20_kbd_reprogrammable_keys_get_count(struct ratbag_device *device, uint8_t reg)
|
|
{
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = reg,
|
|
.msg.address = CMD_KBD_REPROGRAMMABLE_KEYS_GET_COUNT,
|
|
};
|
|
int rc;
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return msg.msg.parameters[0];
|
|
}
|
|
|
|
static int
|
|
hidpp20_kbd_reprogrammable_keys_get_info(struct ratbag_device *device,
|
|
uint8_t reg,
|
|
struct hidpp20_control_id *control)
|
|
{
|
|
int rc;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = reg,
|
|
.msg.address = CMD_KBD_REPROGRAMMABLE_KEYS_GET_CTRL_ID_INFO,
|
|
.msg.parameters[0] = control->index,
|
|
};
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
control->control_id = hidpp20_get_unaligned_u16(&msg.msg.parameters[0]);
|
|
control->task_id = hidpp20_get_unaligned_u16(&msg.msg.parameters[2]);
|
|
control->flags = msg.msg.parameters[4];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
hidpp20_kbd_reprogrammable_keys_get_controls(struct ratbag_device *device,
|
|
struct hidpp20_control_id **controls_list)
|
|
{
|
|
uint8_t feature_index, feature_type, feature_version;
|
|
struct hidpp20_control_id *c_list, *control;
|
|
uint8_t num_controls;
|
|
unsigned i;
|
|
int rc;
|
|
|
|
rc = hidpp_root_get_feature(device,
|
|
HIDPP_PAGE_KBD_REPROGRAMMABLE_KEYS,
|
|
&feature_index,
|
|
&feature_type,
|
|
&feature_version);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = hidpp20_kbd_reprogrammable_keys_get_count(device, feature_index);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
num_controls = rc;
|
|
if (num_controls == 0) {
|
|
*controls_list = NULL;
|
|
return 0;
|
|
}
|
|
|
|
c_list = zalloc(num_controls * sizeof(struct hidpp20_control_id));
|
|
|
|
for (i = 0; i < num_controls; i++) {
|
|
control = &c_list[i];
|
|
control->index = i;
|
|
rc = hidpp20_kbd_reprogrammable_keys_get_info(device,
|
|
feature_index,
|
|
control);
|
|
if (rc)
|
|
goto err;
|
|
|
|
/* 0x1b00 and 0x1b04 have the same control/task id mappings.
|
|
* I hope */
|
|
log_raw(device->ratbag,
|
|
"control %d: cid: '%s' (%d) tid: '%s' (%d) flags: 0x%02x\n",
|
|
control->index,
|
|
hidpp20_1b04_get_logical_mapping_name(control->control_id),
|
|
control->control_id,
|
|
hidpp20_1b04_get_physical_mapping_name(control->task_id),
|
|
control->task_id,
|
|
control->flags);
|
|
}
|
|
|
|
*controls_list = c_list;
|
|
return num_controls;
|
|
err:
|
|
free(c_list);
|
|
return rc;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* 0x1b04: Special keys and mouse buttons */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#define CMD_SPECIAL_KEYS_BUTTONS_GET_COUNT 0x00
|
|
#define CMD_SPECIAL_KEYS_BUTTONS_GET_INFO 0x10
|
|
#define CMD_SPECIAL_KEYS_BUTTONS_GET_REPORTING 0x20
|
|
#define CMD_SPECIAL_KEYS_BUTTONS_SET_REPORTING 0x30
|
|
|
|
static int
|
|
hidpp20_special_keys_buttons_get_count(struct ratbag_device *device, uint8_t reg)
|
|
{
|
|
int rc;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = reg,
|
|
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_COUNT,
|
|
};
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return msg.msg.parameters[0];
|
|
}
|
|
|
|
static int
|
|
hidpp20_special_keys_buttons_get_info(struct ratbag_device *device,
|
|
uint8_t reg,
|
|
struct hidpp20_control_id *control)
|
|
{
|
|
int rc;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = reg,
|
|
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_INFO,
|
|
.msg.parameters[0] = control->index,
|
|
};
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
control->control_id = hidpp20_get_unaligned_u16(&msg.msg.parameters[0]);
|
|
control->task_id = hidpp20_get_unaligned_u16(&msg.msg.parameters[2]);
|
|
control->flags = msg.msg.parameters[4];
|
|
control->position = msg.msg.parameters[5];
|
|
control->group = msg.msg.parameters[6];
|
|
control->group_mask = msg.msg.parameters[7];
|
|
control->raw_XY = msg.msg.parameters[8] & 0x01;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hidpp20_special_keys_buttons_get_reporting(struct ratbag_device *device,
|
|
uint8_t reg,
|
|
struct hidpp20_control_id *control)
|
|
{
|
|
int rc;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = reg,
|
|
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_REPORTING,
|
|
.msg.parameters[0] = control->control_id >> 8,
|
|
.msg.parameters[1] = control->control_id & 0xff,
|
|
};
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
control->reporting.remapped = hidpp20_get_unaligned_u16(&msg.msg.parameters[3]);
|
|
control->reporting.raw_XY = !!(msg.msg.parameters[2] & 0x10);
|
|
control->reporting.persist = !!(msg.msg.parameters[2] & 0x04);
|
|
control->reporting.divert = !!(msg.msg.parameters[2] & 0x01);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hidpp20_special_key_mouse_get_controls(struct ratbag_device *device,
|
|
struct hidpp20_control_id **controls_list)
|
|
{
|
|
uint8_t feature_index, feature_type, feature_version;
|
|
struct hidpp20_control_id *c_list, *control;
|
|
uint8_t num_controls;
|
|
unsigned i;
|
|
int rc;
|
|
|
|
|
|
rc = hidpp_root_get_feature(device,
|
|
HIDPP_PAGE_SPECIAL_KEYS_BUTTONS,
|
|
&feature_index,
|
|
&feature_type,
|
|
&feature_version);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = hidpp20_special_keys_buttons_get_count(device, feature_index);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
num_controls = rc;
|
|
if (num_controls == 0) {
|
|
*controls_list = NULL;
|
|
return 0;
|
|
}
|
|
|
|
c_list = zalloc(num_controls * sizeof(struct hidpp20_control_id));
|
|
|
|
for (i = 0; i < num_controls; i++) {
|
|
control = &c_list[i];
|
|
control->index = i;
|
|
rc = hidpp20_special_keys_buttons_get_info(device,
|
|
feature_index,
|
|
control);
|
|
if (rc)
|
|
goto err;
|
|
|
|
rc = hidpp20_special_keys_buttons_get_reporting(device,
|
|
feature_index,
|
|
control);
|
|
if (rc)
|
|
goto err;
|
|
|
|
log_raw(device->ratbag,
|
|
"control %d: cid: '%s' (%d) tid: '%s' (%d) flags: 0x%02x pos: %d group: %d gmask: 0x%02x raw_XY: %s\n"
|
|
" reporting: raw_xy: %s persist: %s divert: %s remapped: '%s' (%d)\n",
|
|
control->index,
|
|
hidpp20_1b04_get_logical_mapping_name(control->control_id),
|
|
control->control_id,
|
|
hidpp20_1b04_get_physical_mapping_name(control->task_id),
|
|
control->task_id,
|
|
control->flags,
|
|
control->position,
|
|
control->group,
|
|
control->group_mask,
|
|
control->raw_XY ? "yes" : "no",
|
|
control->reporting.raw_XY ? "yes" : "no",
|
|
control->reporting.persist ? "yes" : "no",
|
|
control->reporting.divert ? "yes" : "no",
|
|
hidpp20_1b04_get_logical_mapping_name(control->reporting.remapped),
|
|
control->reporting.remapped);
|
|
}
|
|
|
|
*controls_list = c_list;
|
|
return num_controls;
|
|
err:
|
|
free(c_list);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
hidpp20_special_key_mouse_set_control(struct ratbag_device *device,
|
|
struct hidpp20_control_id *control)
|
|
{
|
|
uint8_t feature_index, feature_type, feature_version;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_SET_REPORTING,
|
|
.msg.parameters[0] = control->control_id >> 8,
|
|
.msg.parameters[1] = control->control_id & 0xff,
|
|
.msg.parameters[2] = 0x00,
|
|
.msg.parameters[3] = control->reporting.remapped >> 8,
|
|
.msg.parameters[4] = control->reporting.remapped & 0xff,
|
|
};
|
|
int rc;
|
|
|
|
|
|
rc = hidpp_root_get_feature(device,
|
|
HIDPP_PAGE_SPECIAL_KEYS_BUTTONS,
|
|
&feature_index,
|
|
&feature_type,
|
|
&feature_version);
|
|
if (rc)
|
|
return rc;
|
|
|
|
msg.msg.sub_id = feature_index;
|
|
if (control->reporting.divert)
|
|
msg.msg.parameters[2] |= 0x03;
|
|
if (control->reporting.persist)
|
|
msg.msg.parameters[2] |= 0x0c;
|
|
if (control->reporting.raw_XY)
|
|
msg.msg.parameters[2] |= 0x20;
|
|
|
|
return hidpp20_request_command(device, &msg);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* 0x2200: Mouse Pointer Basic Optical Sensors */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#define CMD_MOUSE_POINTER_BASIC_GET_INFO 0x00
|
|
|
|
int
|
|
hidpp20_mousepointer_get_mousepointer_info(struct ratbag_device *device,
|
|
uint16_t *resolution,
|
|
uint8_t *flags)
|
|
{
|
|
uint8_t feature_index, feature_type, feature_version;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.address = CMD_MOUSE_POINTER_BASIC_GET_INFO,
|
|
.msg.parameters[0] = feature_index,
|
|
};
|
|
int rc;
|
|
|
|
rc = hidpp_root_get_feature(device,
|
|
HIDPP_PAGE_MOUSE_POINTER_BASIC,
|
|
&feature_index,
|
|
&feature_type,
|
|
&feature_version);
|
|
if (rc)
|
|
return rc;
|
|
|
|
msg.msg.sub_id = feature_index;
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
*resolution = hidpp20_get_unaligned_u16(msg.msg.parameters);
|
|
*flags = msg.msg.parameters[2];
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* 0x2201: Adjustable DPI */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#define CMD_ADJUSTABLE_DPI_GET_SENSOR_COUNT 0x00
|
|
#define CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI_LIST 0x10
|
|
#define CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI 0x20
|
|
#define CMD_ADJUSTABLE_DPI_SET_SENSOR_DPI 0x30
|
|
|
|
static int
|
|
hidpp20_adjustable_dpi_get_count(struct ratbag_device *device, uint8_t reg)
|
|
{
|
|
int rc;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = reg,
|
|
.msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_COUNT,
|
|
};
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return msg.msg.parameters[0];
|
|
}
|
|
|
|
static int
|
|
hidpp20_adjustable_dpi_get_dpi_list(struct ratbag_device *device,
|
|
uint8_t reg,
|
|
struct hidpp20_sensor *sensor)
|
|
{
|
|
int rc;
|
|
unsigned i = 1, dpi_index = 0;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = reg,
|
|
.msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI_LIST,
|
|
.msg.parameters[0] = sensor->index,
|
|
};
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
sensor->dpi_min = 0xffff;
|
|
|
|
sensor->index = msg.msg.parameters[0];
|
|
while (i < LONG_MESSAGE_LENGTH - 4U &&
|
|
hidpp20_get_unaligned_u16(&msg.msg.parameters[i]) != 0) {
|
|
uint16_t value = hidpp20_get_unaligned_u16(&msg.msg.parameters[i]);
|
|
|
|
if (value > 0xe000) {
|
|
sensor->dpi_steps = value - 0xe000;
|
|
} else {
|
|
sensor->dpi_min = value < sensor->dpi_min ? value : sensor->dpi_min;
|
|
sensor->dpi_max = value > sensor->dpi_max ? value : sensor->dpi_max;
|
|
sensor->dpi_list[dpi_index++] = value;
|
|
}
|
|
assert(sensor->dpi_list[dpi_index] == 0x0000);
|
|
i += 2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
hidpp20_adjustable_dpi_get_dpi(struct ratbag_device *device,
|
|
uint8_t reg,
|
|
struct hidpp20_sensor *sensor)
|
|
{
|
|
int rc;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.sub_id = reg,
|
|
.msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI,
|
|
.msg.parameters[0] = sensor->index,
|
|
};
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
sensor->dpi = hidpp20_get_unaligned_u16(&msg.msg.parameters[1]);
|
|
sensor->default_dpi = hidpp20_get_unaligned_u16(&msg.msg.parameters[3]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hidpp20_adjustable_dpi_get_sensors(struct ratbag_device *device,
|
|
struct hidpp20_sensor **sensors_list)
|
|
{
|
|
uint8_t feature_index, feature_type, feature_version;
|
|
struct hidpp20_sensor *s_list, *sensor;
|
|
uint8_t num_sensors;
|
|
unsigned i;
|
|
int rc;
|
|
|
|
|
|
rc = hidpp_root_get_feature(device,
|
|
HIDPP_PAGE_ADJUSTABLE_DPI,
|
|
&feature_index,
|
|
&feature_type,
|
|
&feature_version);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = hidpp20_adjustable_dpi_get_count(device, feature_index);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
num_sensors = rc;
|
|
if (num_sensors == 0) {
|
|
*sensors_list = NULL;
|
|
return 0;
|
|
}
|
|
|
|
s_list = zalloc(num_sensors * sizeof(struct hidpp20_sensor));
|
|
|
|
for (i = 0; i < num_sensors; i++) {
|
|
sensor = &s_list[i];
|
|
sensor->index = i;
|
|
rc = hidpp20_adjustable_dpi_get_dpi_list(device,
|
|
feature_index,
|
|
sensor);
|
|
if (rc)
|
|
goto err;
|
|
|
|
rc = hidpp20_adjustable_dpi_get_dpi(device, feature_index, sensor);
|
|
if (rc)
|
|
goto err;
|
|
|
|
log_raw(device->ratbag,
|
|
"sensor %d: current dpi: %d (default: %d) min: %d max: %d steps: %d\n",
|
|
sensor->index,
|
|
sensor->dpi,
|
|
sensor->default_dpi,
|
|
sensor->dpi_min,
|
|
sensor->dpi_max,
|
|
sensor->dpi_steps);
|
|
}
|
|
|
|
*sensors_list = s_list;
|
|
return num_sensors;
|
|
err:
|
|
free(s_list);
|
|
return rc;
|
|
}
|
|
|
|
int hidpp20_adjustable_dpi_set_sensor_dpi(struct ratbag_device *device,
|
|
struct hidpp20_sensor *sensor, uint16_t dpi)
|
|
{
|
|
uint8_t feature_index, feature_type, feature_version;
|
|
int rc;
|
|
union hidpp20_message msg = {
|
|
.msg.report_id = REPORT_ID_LONG,
|
|
.msg.device_idx = 0xff,
|
|
.msg.address = CMD_ADJUSTABLE_DPI_SET_SENSOR_DPI,
|
|
.msg.parameters[0] = sensor->index,
|
|
.msg.parameters[1] = dpi >> 8,
|
|
.msg.parameters[2] = dpi & 0xff,
|
|
};
|
|
|
|
rc = hidpp_root_get_feature(device,
|
|
HIDPP_PAGE_ADJUSTABLE_DPI,
|
|
&feature_index,
|
|
&feature_type,
|
|
&feature_version);
|
|
if (rc)
|
|
return rc;
|
|
|
|
msg.msg.sub_id = feature_index;
|
|
|
|
rc = hidpp20_request_command(device, &msg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
if (hidpp20_get_unaligned_u16(&msg.msg.parameters[1]) != dpi)
|
|
return -EIO;
|
|
|
|
return 0;
|
|
}
|