mirror of
https://github.com/libratbag/libratbag.git
synced 2026-11-08 07:33:53 +01:00
hidpp20: first parsing of feature 0x8100 - onboard profiles
Obtained by rev-engineering the protocol. Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
This commit is contained in:
@@ -51,6 +51,7 @@
|
||||
#define HIDPP_CAP_BUTTON_KEY_1b04 (1 << 2)
|
||||
#define HIDPP_CAP_BATTERY_LEVEL_1000 (1 << 3)
|
||||
#define HIDPP_CAP_KBD_REPROGRAMMABLE_KEYS_1b00 (1 << 4)
|
||||
#define HIDPP_CAP_ONBOARD_PROFILES_8100 (1 << 5)
|
||||
|
||||
struct hidpp20drv_data {
|
||||
struct hidpp20_device *dev;
|
||||
@@ -59,6 +60,7 @@ struct hidpp20drv_data {
|
||||
struct hidpp20_sensor *sensors;
|
||||
unsigned num_controls;
|
||||
struct hidpp20_control_id *controls;
|
||||
struct hidpp20_profiles *profiles;
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -157,7 +159,18 @@ hidpp20drv_has_capability(const struct ratbag_device *device,
|
||||
static int
|
||||
hidpp20drv_current_profile(struct ratbag_device *device)
|
||||
{
|
||||
return 0;
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data((struct ratbag_device *)device);
|
||||
int rc;
|
||||
|
||||
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100))
|
||||
return 0;
|
||||
|
||||
rc = hidpp20_onboard_profiles_get_current_profile(drv_data->dev,
|
||||
drv_data->profiles);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return rc - 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -324,17 +337,64 @@ hidpp20drv_read_kbd_reprogrammable_key(struct ratbag_device *device)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
hidpp20drv_read_onboard_profile(struct ratbag_device *device, unsigned index)
|
||||
{
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
int rc;
|
||||
|
||||
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100))
|
||||
return 0;
|
||||
|
||||
if (!drv_data->profiles) {
|
||||
rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = hidpp20_onboard_profiles_read(drv_data->dev, index, drv_data->profiles);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hidpp20drv_read_profile(struct ratbag_profile *profile, unsigned int index)
|
||||
{
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct ratbag_resolution *res;
|
||||
unsigned i, dpi;
|
||||
|
||||
hidpp20drv_read_resolution_dpi(profile);
|
||||
hidpp20drv_read_special_key_mouse(device);
|
||||
hidpp20drv_read_onboard_profile(device, profile->index);
|
||||
|
||||
profile->is_active = false;
|
||||
if ((int)index == hidpp20drv_current_profile(device))
|
||||
profile->is_active = true;
|
||||
|
||||
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100))
|
||||
return;
|
||||
|
||||
dpi = ratbag_resolution_get_dpi(profile->resolution.modes);
|
||||
|
||||
profile->resolution.num_modes = drv_data->profiles->num_modes;
|
||||
for (i = 0; i < drv_data->profiles->num_modes; i++) {
|
||||
struct hidpp20_profile *p = &drv_data->profiles->profiles[index];
|
||||
|
||||
res = ratbag_resolution_init(profile, i,
|
||||
p->dpi[i],
|
||||
p->dpi[i],
|
||||
p->report_rate);
|
||||
|
||||
if (profile->is_active &&
|
||||
res->dpi_x == dpi)
|
||||
res->is_active = true;
|
||||
if (i == p->default_dpi)
|
||||
res->is_default = true;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -349,6 +409,13 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct ratbag *ratbag = device->ratbag;
|
||||
int rc;
|
||||
uint8_t feature_index, feature_type, feature_version;
|
||||
|
||||
rc = hidpp_root_get_feature(drv_data->dev,
|
||||
feature,
|
||||
&feature_index,
|
||||
&feature_type,
|
||||
&feature_version);
|
||||
|
||||
switch (feature) {
|
||||
case HIDPP_PAGE_ROOT:
|
||||
@@ -408,6 +475,10 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
|
||||
}
|
||||
case HIDPP_PAGE_ONBOARD_PROFILES: {
|
||||
log_debug(ratbag, "device has onboard profiles\n");
|
||||
drv_data->capabilities |= HIDPP_CAP_ONBOARD_PROFILES_8100;
|
||||
/* we read the profiles once with an incorect profile index
|
||||
* to get the correct number of supported profiles. */
|
||||
hidpp20drv_read_onboard_profile(device, 0xffffff);
|
||||
break;
|
||||
}
|
||||
case HIDPP_PAGE_MOUSE_BUTTON_SPY: {
|
||||
@@ -500,7 +571,7 @@ hidpp20drv_probe(struct ratbag_device *device)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ratbag_device_init_profiles(device, 1,
|
||||
ratbag_device_init_profiles(device, drv_data->profiles ? drv_data->profiles->num_profiles : 1,
|
||||
device->num_buttons ? device->num_buttons : 8);
|
||||
|
||||
return rc;
|
||||
@@ -517,6 +588,8 @@ hidpp20drv_remove(struct ratbag_device *device)
|
||||
|
||||
ratbag_close_hidraw(device);
|
||||
|
||||
if (drv_data->profiles)
|
||||
free(drv_data->profiles);
|
||||
free(drv_data->controls);
|
||||
free(drv_data->sensors);
|
||||
hidpp20_device_destroy(drv_data->dev);
|
||||
|
||||
149
src/hidpp20.c
149
src/hidpp20.c
@@ -171,6 +171,12 @@ hidpp20_get_unaligned_u16(uint8_t *buf)
|
||||
return (buf[0] << 8) | buf[1];
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
hidpp20_get_unaligned_be_u16(uint8_t *buf)
|
||||
{
|
||||
return (buf[1] << 8) | buf[0];
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x0000: Root */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -910,6 +916,149 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x8100 - Onboard Profiles */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define CMD_ONBOARD_PROFILES_GET_PROFILES_DESCR 0x00
|
||||
#define CMD_ONBOARD_PROFILES_SET_CURRENT_PROFILE 0x30
|
||||
#define CMD_ONBOARD_PROFILES_GET_CURRENT_PROFILE 0x40
|
||||
#define CMD_ONBOARD_PROFILES_MEMORY_READ 0x50
|
||||
#define CMD_ONBOARD_PROFILES_MEMORY_ADDR_WRITE 0x60
|
||||
#define CMD_ONBOARD_PROFILES_MEMORY_WRITE 0x70
|
||||
#define CMD_ONBOARD_PROFILES_MEMORY_WRITE_END 0x80
|
||||
#define CMD_ONBOARD_PROFILES_GET_CURRENT_DPI_INDEX 0xb0
|
||||
#define CMD_ONBOARD_PROFILES_SET_CURRENT_DPI_INDEX 0xc0
|
||||
|
||||
static int
|
||||
hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
|
||||
uint16_t page,
|
||||
uint16_t section,
|
||||
uint8_t *result)
|
||||
{
|
||||
uint8_t feature_index;
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.address = CMD_ONBOARD_PROFILES_MEMORY_READ,
|
||||
.msg.parameters[0] = page >> 8,
|
||||
.msg.parameters[1] = page & 0xFF,
|
||||
.msg.parameters[2] = section >> 8,
|
||||
.msg.parameters[3] = section & 0xFF,
|
||||
};
|
||||
|
||||
feature_index = hidpp_root_get_feature_idx(device,
|
||||
HIDPP_PAGE_ONBOARD_PROFILES);
|
||||
if (feature_index == 0)
|
||||
return -ENOTSUP;
|
||||
|
||||
msg.msg.sub_id = feature_index;
|
||||
|
||||
rc = hidpp20_request_command(device, &msg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* msg.msg.parameters is guaranteed to have a size >= 16 */
|
||||
memcpy(result, msg.msg.parameters, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
hidpp20_onboard_profiles_get_current_profile(struct hidpp20_device *device,
|
||||
struct hidpp20_profiles *profiles_list)
|
||||
{
|
||||
uint8_t feature_index;
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.address = CMD_ONBOARD_PROFILES_GET_CURRENT_PROFILE,
|
||||
};
|
||||
|
||||
feature_index = hidpp_root_get_feature_idx(device,
|
||||
HIDPP_PAGE_ONBOARD_PROFILES);
|
||||
if (feature_index == 0)
|
||||
return -ENOTSUP;
|
||||
|
||||
msg.msg.sub_id = feature_index;
|
||||
|
||||
rc = hidpp20_request_command(device, &msg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return msg.msg.parameters[1];
|
||||
}
|
||||
|
||||
int
|
||||
hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
|
||||
struct hidpp20_profiles **profiles_list)
|
||||
{
|
||||
unsigned i;
|
||||
int rc;
|
||||
uint8_t data[16] = {0};
|
||||
struct hidpp20_profiles *profiles;
|
||||
unsigned profile_count = 0;
|
||||
|
||||
rc = hidpp20_onboard_profiles_read_memory(device,
|
||||
0x0000,
|
||||
0x0000,
|
||||
data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
profiles = zalloc(sizeof(struct hidpp20_profiles));
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
uint8_t *d = data + 4 * i;
|
||||
|
||||
if (d[0] == 0xFF && d[1] == 0xFF)
|
||||
break;
|
||||
|
||||
profile_count++;
|
||||
profiles->profiles[i].index = d[1];
|
||||
profiles->profiles[i].enabled = d[2];
|
||||
}
|
||||
|
||||
profiles->num_profiles = profile_count;
|
||||
profiles->num_modes = 5;
|
||||
|
||||
*profiles_list = profiles;
|
||||
|
||||
return profile_count;
|
||||
}
|
||||
|
||||
int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
|
||||
unsigned int index,
|
||||
struct hidpp20_profiles *profiles_list)
|
||||
{
|
||||
uint8_t data[16] = {0};
|
||||
struct hidpp20_profile *profile = &profiles_list->profiles[index];
|
||||
unsigned i;
|
||||
int rc;
|
||||
|
||||
if (index >= profiles_list->num_profiles)
|
||||
return -EINVAL;
|
||||
|
||||
rc = hidpp20_onboard_profiles_read_memory(device,
|
||||
index + 1,
|
||||
0x0000,
|
||||
data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
profile->report_rate = 1000 / max(1, data[0]);
|
||||
profile->default_dpi = data[1];
|
||||
profile->switched_dpi = data[2];
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
profile->dpi[i] = hidpp20_get_unaligned_be_u16(&data[2 * i + 3]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* generic hidpp20 device operations */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
@@ -264,10 +264,51 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
|
||||
|
||||
#define HIDPP_PAGE_ONBOARD_PROFILES 0x8100
|
||||
|
||||
struct hidpp20_profile {
|
||||
uint8_t index;
|
||||
uint8_t enabled;
|
||||
unsigned report_rate;
|
||||
unsigned default_dpi;
|
||||
unsigned switched_dpi;
|
||||
uint16_t dpi[5];
|
||||
};
|
||||
|
||||
struct hidpp20_profiles {
|
||||
uint8_t num_profiles;
|
||||
uint8_t num_modes;
|
||||
struct hidpp20_profile profiles[5];
|
||||
};
|
||||
|
||||
/**
|
||||
* allocates a list of profiles that has to be freed by the caller.
|
||||
*
|
||||
* returns the number of profiles in the list or a negative error
|
||||
*/
|
||||
int hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
|
||||
struct hidpp20_profiles **profiles_list);
|
||||
|
||||
/**
|
||||
* return the current profile index or a negative error.
|
||||
*/
|
||||
int hidpp20_onboard_profiles_get_current_profile(struct hidpp20_device *device,
|
||||
struct hidpp20_profiles *profiles_list);
|
||||
|
||||
/**
|
||||
* parse a given profile from the mouse and fill in the right profile in
|
||||
* profiles_list.
|
||||
*
|
||||
* return 0 or a negative error.
|
||||
*/
|
||||
int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
|
||||
unsigned int index,
|
||||
struct hidpp20_profiles *profiles_list);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x8110 - Mouse Button Spy */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define HIDPP_PAGE_MOUSE_BUTTON_SPY 0x8110
|
||||
|
||||
|
||||
|
||||
#endif /* HIDPP_20_H */
|
||||
|
||||
Reference in New Issue
Block a user