mirror of
https://github.com/libratbag/libratbag.git
synced 2026-11-07 23:23:52 +01:00
hidpp10: add code to read the profile
Profiles are... interesting https://github.com/cvuchener/g500/blob/master/doc/profile.md is pretty detailed on the information, but there's some oddities. The profile configured on my mouse is on page 5, not sure if that's the OS X logitech tool though (which doesn't do profile configuration btw). Pages 3 and 3 are valid too, the rest looks to be garbage. There's probably some magic string to detect a profile (cvuchener says maybe FF 00 00 80), so more profiles may be lying around elsewhere in memory... Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
270
src/hidpp10.c
270
src/hidpp10.c
@@ -175,6 +175,13 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* HID++ 1.0 commands 10 */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
hidpp10_read_memory(struct hidpp10_device *dev, uint8_t page, uint16_t offset,
|
||||
uint8_t bytes[16]);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x00: Enable HID++ Notifications */
|
||||
@@ -283,6 +290,230 @@ hidpp10_set_individual_feature(struct hidpp10_device *dev,
|
||||
return res;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x0F: Profile queries */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
#define __CMD_PROFILE 0x0F
|
||||
|
||||
#define CMD_PROFILE(idx, sub) { \
|
||||
.msg = { \
|
||||
.report_id = REPORT_ID_SHORT, \
|
||||
.device_idx = idx, \
|
||||
.sub_id = sub, \
|
||||
.address = __CMD_PROFILE, \
|
||||
.parameters = {0x00, 0x00, 0x00 }, \
|
||||
} \
|
||||
}
|
||||
|
||||
struct _hidpp10_dpi_mode {
|
||||
uint16_t xres;
|
||||
uint16_t yres;
|
||||
uint8_t led1:4;
|
||||
uint8_t led2:4;
|
||||
uint8_t led3:4;
|
||||
uint8_t led4:4;
|
||||
} __attribute__((packed));
|
||||
_Static_assert(sizeof(struct _hidpp10_dpi_mode) == 6, "Invalid size");
|
||||
|
||||
union _hidpp10_button_binding {
|
||||
struct {
|
||||
uint8_t type;
|
||||
uint8_t pad;
|
||||
uint8_t pad1;
|
||||
} any;
|
||||
struct {
|
||||
uint8_t type; /* 0x81 */
|
||||
uint8_t button_flags_lsb;
|
||||
uint8_t button_flags_msb;
|
||||
} button;
|
||||
struct {
|
||||
uint8_t type; /* 0x82 */
|
||||
uint8_t modifier_flags;
|
||||
uint8_t key;
|
||||
} keyboard_keys;
|
||||
struct {
|
||||
uint8_t type; /* 0x83 */
|
||||
uint8_t flags1;
|
||||
uint8_t flags2;
|
||||
} special;
|
||||
struct {
|
||||
uint8_t type; /* 0x84 */
|
||||
uint8_t consumer_control1;
|
||||
uint8_t consumer_control2;
|
||||
} consumer_control;
|
||||
struct {
|
||||
uint8_t type; /* 0x8F */
|
||||
uint8_t zero0;
|
||||
uint8_t zero1;
|
||||
} disabled;
|
||||
} __attribute__((packed));
|
||||
_Static_assert(sizeof(union _hidpp10_button_binding) == 3, "Invalid size");
|
||||
|
||||
struct _hidpp10_profile {
|
||||
uint8_t unknown[4];
|
||||
struct _hidpp10_dpi_mode dpi_modes[PROFILE_NUM_DPI_MODES];
|
||||
uint8_t angle_correction;
|
||||
uint8_t default_dpi_mode;
|
||||
uint8_t unknown2[2];
|
||||
uint8_t usb_refresh_rate;
|
||||
union _hidpp10_button_binding buttons[PROFILE_NUM_BUTTONS];
|
||||
} __attribute__((packed));
|
||||
_Static_assert(sizeof(struct _hidpp10_profile) == 78, "Invalid size");
|
||||
|
||||
union _hidpp10_profile_data {
|
||||
struct _hidpp10_profile profile;
|
||||
uint8_t data[((sizeof(struct _hidpp10_profile) + 15)/16)*16];
|
||||
};
|
||||
_Static_assert((sizeof(union _hidpp10_profile_data) % 16) == 0, "Invalid size");
|
||||
|
||||
int
|
||||
hidpp10_get_current_profile(struct hidpp10_device *dev, int8_t *current_profile)
|
||||
{
|
||||
unsigned idx = dev->index;
|
||||
union hidpp10_message profile = CMD_PROFILE(idx, GET_REGISTER_REQ);
|
||||
int res;
|
||||
int8_t page;
|
||||
|
||||
res = hidpp10_request_command(dev, &profile);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
page = profile.msg.parameters[0]; /* FIXME: my mouse is always 0 */
|
||||
*current_profile = page;
|
||||
|
||||
/* FIXME: my mouse appears to be on profile 5, not sure how to
|
||||
* change this */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_profile *profile_return)
|
||||
{
|
||||
struct ratbag *ratbag = dev->ratbag_device->ratbag;
|
||||
union _hidpp10_profile_data data;
|
||||
struct _hidpp10_profile *p = &data.profile;
|
||||
size_t i;
|
||||
int res;
|
||||
struct hidpp10_profile profile;
|
||||
|
||||
/* FIXME: profile offset appears to be 3, 1 and 2 are garbage */
|
||||
number += 3;
|
||||
|
||||
for (i = 0; i < sizeof(data); i += 16) {
|
||||
res = hidpp10_read_memory(dev, number, i, &data.data[i]);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
profile.angle_correction = p->angle_correction;
|
||||
profile.default_dpi_mode = p->default_dpi_mode;
|
||||
profile.refresh_rate = p->usb_refresh_rate ? 1000/p->usb_refresh_rate : 0;
|
||||
|
||||
for (i = 0; i < PROFILE_NUM_DPI_MODES; i++) {
|
||||
uint8_t *be; /* in big endian */
|
||||
struct _hidpp10_dpi_mode *dpi = &p->dpi_modes[i];
|
||||
|
||||
be = (uint8_t*)&dpi->xres;
|
||||
profile.dpi_modes[i].xres = hidpp10_get_unaligned_u16(be) * 50;
|
||||
be = (uint8_t*)&dpi->yres;
|
||||
profile.dpi_modes[i].yres = hidpp10_get_unaligned_u16(be) * 50;
|
||||
|
||||
profile.dpi_modes[i].led[0] = dpi->led1 == 0x2;
|
||||
profile.dpi_modes[i].led[1] = dpi->led2 == 0x2;
|
||||
profile.dpi_modes[i].led[2] = dpi->led3 == 0x2;
|
||||
profile.dpi_modes[i].led[3] = dpi->led4 == 0x2;
|
||||
}
|
||||
|
||||
for (i = 0; i < PROFILE_NUM_BUTTONS; i++) {
|
||||
union _hidpp10_button_binding *b = &p->buttons[i];
|
||||
union hidpp10_buttons *button = &profile.buttons[i];
|
||||
|
||||
button->any.type = b->any.type;
|
||||
|
||||
switch (b->any.type) {
|
||||
case PROFILE_BUTTON_TYPE_BUTTON:
|
||||
button->button.button =
|
||||
ffs(hidpp10_get_unaligned_u16le(&b->button.button_flags_lsb));
|
||||
break;
|
||||
case PROFILE_BUTTON_TYPE_KEYS:
|
||||
button->keys.modifier_flags = b->keyboard_keys.modifier_flags;
|
||||
button->keys.key = b->keyboard_keys.key;
|
||||
break;
|
||||
case PROFILE_BUTTON_TYPE_SPECIAL:
|
||||
button->special.special = ffs(hidpp10_get_unaligned_u16le(&b->special.flags1));
|
||||
break;
|
||||
case PROFILE_BUTTON_TYPE_CONSUMER_CONTROL:
|
||||
button->consumer_control.consumer_control =
|
||||
hidpp10_get_unaligned_u16(&b->consumer_control.consumer_control1);
|
||||
break;
|
||||
case PROFILE_BUTTON_TYPE_DISABLED:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
log_buf_debug(ratbag,
|
||||
"+++++++++++++++++++ Profile data: +++++++++++++++++ \n",
|
||||
data.data, 78);
|
||||
|
||||
log_debug(ratbag, "Profile %d:\n", number);
|
||||
for (i = 0; i < 5; i++) {
|
||||
log_debug(ratbag,
|
||||
"DPI mode: %dx%d dpi\n",
|
||||
profile.dpi_modes[i].xres,
|
||||
profile.dpi_modes[i].yres);
|
||||
log_debug(ratbag,
|
||||
"LED status: 1:%s 2:%s 3:%s 4:%s\n",
|
||||
(p->dpi_modes[i].led1 & 0x2) ? "on" : "off",
|
||||
(p->dpi_modes[i].led2 & 0x2) ? "on" : "off",
|
||||
(p->dpi_modes[i].led3 & 0x2) ? "on" : "off",
|
||||
(p->dpi_modes[i].led4 & 0x2) ? "on" : "off");
|
||||
}
|
||||
log_debug(ratbag, "Angle correction: %d\n", profile.angle_correction);
|
||||
log_debug(ratbag, "Default DPI mode: %d\n", profile.default_dpi_mode);
|
||||
log_debug(ratbag, "Refresh rate: %d\n", profile.refresh_rate);
|
||||
for (i = 0; i < 13; i++) {
|
||||
union _hidpp10_button_binding *button = &p->buttons[i];
|
||||
switch (button->any.type) {
|
||||
case 0x81:
|
||||
log_debug(ratbag,
|
||||
"Button %d: button %d\n",
|
||||
i,
|
||||
ffs(hidpp10_get_unaligned_u16le(&button->button.button_flags_lsb)));
|
||||
break;
|
||||
case 0x82:
|
||||
log_debug(ratbag,
|
||||
"Button %d: key %d modifier %x\n",
|
||||
i,
|
||||
button->keyboard_keys.key,
|
||||
button->keyboard_keys.modifier_flags);
|
||||
break;
|
||||
case 0x83:
|
||||
log_debug(ratbag,
|
||||
"Button %d: special %x\n",
|
||||
i,
|
||||
ffs(hidpp10_get_unaligned_u16le(&button->special.flags1)));
|
||||
break;
|
||||
case 0x84:
|
||||
log_debug(ratbag,
|
||||
"Button %d: consumer: %x\n",
|
||||
i,
|
||||
hidpp10_get_unaligned_u16(&button->consumer_control.consumer_control1));
|
||||
break;
|
||||
case 0x8F:
|
||||
log_debug(ratbag, "Button %d: disabled\n", i);
|
||||
break;
|
||||
default:
|
||||
/* FIXME: this is the page number for the macro,
|
||||
* followed by a 1-byte offset */
|
||||
break ;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x51: LED Status */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -440,6 +671,38 @@ hidpp10_get_usb_refresh_rate(struct hidpp10_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0xA2: Reading memory */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
#define __CMD_READ_MEMORY 0xA2
|
||||
|
||||
#define CMD_READ_MEMORY(idx, page, offset) { \
|
||||
.msg = { \
|
||||
.report_id = REPORT_ID_SHORT, \
|
||||
.device_idx = idx, \
|
||||
.sub_id = GET_LONG_REGISTER_REQ, \
|
||||
.address = __CMD_READ_MEMORY, \
|
||||
.parameters = {page, offset & 0xff, offset >> 8 }, \
|
||||
} \
|
||||
}
|
||||
|
||||
static int
|
||||
hidpp10_read_memory(struct hidpp10_device *dev, uint8_t page, uint16_t offset,
|
||||
uint8_t bytes[16])
|
||||
{
|
||||
unsigned idx = dev->index;
|
||||
union hidpp10_message readmem = CMD_READ_MEMORY(idx, page, offset/2);
|
||||
int res;
|
||||
|
||||
res = hidpp10_request_command(dev, &readmem);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
memcpy(bytes, readmem.msg.string, sizeof(readmem.msg.string));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0xB2: Device Connection and Disconnection (Pairing) */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -613,6 +876,7 @@ hidpp10_get_device_info(struct hidpp10_device *dev)
|
||||
size_t name_size = sizeof(dev->name);
|
||||
uint8_t f1, f2;
|
||||
uint8_t reflect;
|
||||
int i;
|
||||
|
||||
hidpp10_get_pairing_information(dev,
|
||||
&dev->report_interval,
|
||||
@@ -631,6 +895,12 @@ hidpp10_get_device_info(struct hidpp10_device *dev)
|
||||
hidpp10_get_usb_refresh_rate(dev, &dev->refresh_rate);
|
||||
|
||||
hidpp10_get_optical_sensor_settings(dev, &reflect);
|
||||
|
||||
hidpp10_get_current_profile(dev, &dev->current_profile);
|
||||
|
||||
for (i = 0; i < HIDPP10_NUM_PROFILES; i++)
|
||||
hidpp10_get_profile(dev, i, &dev->profiles[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,20 +36,7 @@
|
||||
#include "libratbag-private.h"
|
||||
#include "libratbag.h"
|
||||
|
||||
struct hidpp10_device {
|
||||
struct ratbag_device *ratbag_device;
|
||||
unsigned index;
|
||||
char name[15];
|
||||
uint16_t wpid;
|
||||
uint8_t report_interval;
|
||||
uint8_t device_type;
|
||||
uint8_t fw_major;
|
||||
uint8_t fw_minor;
|
||||
uint8_t build;
|
||||
uint16_t xres, yres;
|
||||
uint16_t refresh_rate;
|
||||
bool led[4];
|
||||
};
|
||||
struct hidpp10_device;
|
||||
|
||||
struct _hidpp10_message {
|
||||
uint8_t report_id;
|
||||
@@ -122,6 +109,62 @@ hidpp10_set_individual_feature(struct hidpp10_device *dev,
|
||||
uint8_t feature_bit_r2);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x0F: Profile queries */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
#define PROFILE_NUM_BUTTONS 13
|
||||
#define PROFILE_NUM_DPI_MODES 5
|
||||
#define PROFILE_BUTTON_TYPE_BUTTON 0x81
|
||||
#define PROFILE_BUTTON_TYPE_KEYS 0x82
|
||||
#define PROFILE_BUTTON_TYPE_SPECIAL 0x83
|
||||
#define PROFILE_BUTTON_TYPE_CONSUMER_CONTROL 0x84
|
||||
#define PROFILE_BUTTON_TYPE_DISABLED 0x8F
|
||||
|
||||
#define PROFILE_BUTTON_SPECIAL_PAN_LEFT 0x1
|
||||
#define PROFILE_BUTTON_SPECIAL_PAN_RIGHT 0x2
|
||||
#define PROFILE_BUTTON_SPECIAL_DPI_NEXT 0x4
|
||||
#define PROFILE_BUTTON_SPECIAL_DPI_PREV 0x8
|
||||
|
||||
struct hidpp10_profile {
|
||||
struct {
|
||||
uint16_t xres;
|
||||
uint16_t yres;
|
||||
bool led[4];
|
||||
} dpi_modes[5];
|
||||
bool angle_correction;
|
||||
uint8_t default_dpi_mode;
|
||||
uint16_t refresh_rate;
|
||||
union hidpp10_buttons {
|
||||
struct { uint8_t type; } any;
|
||||
struct {
|
||||
uint8_t type;
|
||||
uint16_t button;
|
||||
} button;
|
||||
struct {
|
||||
uint8_t type;
|
||||
uint8_t modifier_flags;
|
||||
uint8_t key;
|
||||
} keys;
|
||||
struct {
|
||||
uint8_t type;
|
||||
uint16_t special;
|
||||
} special;
|
||||
struct {
|
||||
uint8_t type;
|
||||
uint16_t consumer_control;
|
||||
} consumer_control;
|
||||
struct {
|
||||
uint8_t type;
|
||||
} disabled;
|
||||
} buttons[PROFILE_NUM_BUTTONS];
|
||||
};
|
||||
|
||||
int
|
||||
hidpp10_get_current_profile(struct hidpp10_device *dev, int8_t *current_profile);
|
||||
|
||||
int
|
||||
hidpp10_get_profile(struct hidpp10_device *dev, int8_t number,
|
||||
struct hidpp10_profile *profile);
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x51: LED Status */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
int
|
||||
@@ -172,4 +215,24 @@ int
|
||||
hidpp10_get_pairing_information_device_name(struct hidpp10_device *dev,
|
||||
char *name,
|
||||
size_t *name_sz);
|
||||
|
||||
/* FIXME: that's what my G500s supports, but only pages 3-5 are valid.
|
||||
* 0 is zeroed, 1 and 2 are garbage, all above 6 is garbage */
|
||||
#define HIDPP10_NUM_PROFILES 3
|
||||
struct hidpp10_device {
|
||||
struct ratbag_device *ratbag_device;
|
||||
unsigned index;
|
||||
char name[15];
|
||||
uint16_t wpid;
|
||||
uint8_t report_interval;
|
||||
uint8_t device_type;
|
||||
uint8_t fw_major;
|
||||
uint8_t fw_minor;
|
||||
uint8_t build;
|
||||
uint16_t xres, yres;
|
||||
uint16_t refresh_rate;
|
||||
bool led[4];
|
||||
int8_t current_profile;
|
||||
struct hidpp10_profile profiles[HIDPP10_NUM_PROFILES];
|
||||
};
|
||||
#endif /* HIDPP_10_H */
|
||||
|
||||
Reference in New Issue
Block a user