From bbc4af641969a0a0cd69f4ffb8bca1b6bcc385c4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Sat, 22 Aug 2015 17:52:20 +1000 Subject: [PATCH] hidpp20: add support for 0x1b00 reprogrammable keys As seen on the T650. Reusing most of 0x1b04 here, except this one only gives us control id, task id and flags. Signed-off-by: Peter Hutterer --- src/driver-hidpp20.c | 32 +++++++++++++ src/hidpp20.c | 111 +++++++++++++++++++++++++++++++++++++++++++ src/hidpp20.h | 23 ++++++++- 3 files changed, 164 insertions(+), 2 deletions(-) diff --git a/src/driver-hidpp20.c b/src/driver-hidpp20.c index a54c0dd7..bf54020c 100644 --- a/src/driver-hidpp20.c +++ b/src/driver-hidpp20.c @@ -50,6 +50,7 @@ #define HIDPP_CAP_SWITCHABLE_RESOLUTION_2201 (1 << 1) #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) struct hidpp20drv_data { unsigned proto_major; @@ -274,6 +275,27 @@ hidpp20drv_read_special_key_mouse(struct ratbag_device *device) return rc; } +static int +hidpp20drv_read_kbd_reprogrammable_key(struct ratbag_device *device) +{ + struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); + int rc; + + if (!(drv_data->capabilities & HIDPP_CAP_KBD_REPROGRAMMABLE_KEYS_1b00)) + return 0; + + free(drv_data->controls); + drv_data->controls = NULL; + drv_data->num_controls = 0; + rc = hidpp20_kbd_reprogrammable_keys_get_controls(device, &drv_data->controls); + if (rc > 0) { + drv_data->num_controls = rc; + rc = 0; + } + + return rc; +} + static void hidpp20drv_read_profile(struct ratbag_profile *profile, unsigned int index) { @@ -338,6 +360,16 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature) drv_data->capabilities |= HIDPP_CAP_BATTERY_LEVEL_1000; break; } + case HIDPP_PAGE_KBD_REPROGRAMMABLE_KEYS: { + log_info(ratbag, "device has programmable keys/buttons\n"); + drv_data->capabilities |= HIDPP_CAP_KBD_REPROGRAMMABLE_KEYS_1b00; + + /* we read the profile once to get the correct number of + * supported buttons. */ + if (!hidpp20drv_read_kbd_reprogrammable_key(device)) + device->num_buttons = drv_data->num_controls; + break; + } default: log_debug(device->ratbag, "unknown feature 0x%04x\n", feature); } diff --git a/src/hidpp20.c b/src/hidpp20.c index abb1f73b..273d7dc5 100644 --- a/src/hidpp20.c +++ b/src/hidpp20.c @@ -56,6 +56,7 @@ hidpp20_feature_get_name(uint16_t feature) 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; @@ -384,6 +385,116 @@ hidpp20_batterylevel_get_battery_level(struct ratbag_device *device, 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)); + if (!c_list) + return -ENOMEM; + + 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_debug(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 */ /* -------------------------------------------------------------------------- */ diff --git a/src/hidpp20.h b/src/hidpp20.h index 5bc687f3..bf519a1a 100644 --- a/src/hidpp20.h +++ b/src/hidpp20.h @@ -110,16 +110,26 @@ int hidpp20_batterylevel_get_battery_level(struct ratbag_device *device, uint16_t *next_level); /* -------------------------------------------------------------------------- */ -/* 0x1b04: Special keys and mouse buttons */ +/* 0x1b00: KBD reprogrammable keys and mouse buttons */ /* -------------------------------------------------------------------------- */ -#define HIDPP_PAGE_SPECIAL_KEYS_BUTTONS 0x1b04 +#define HIDPP_PAGE_KBD_REPROGRAMMABLE_KEYS 0x1b00 + +enum hidpp2_controL_id_flags { + HIDPP20_CONTROL_ID_FLAG_NONE = 0, + HIDPP20_CONTROL_ID_FLAG_MOUSE_BUTTON = (1 << 0), /**< Is a mouse button */ + HIDPP20_CONTROL_ID_FLAG_FN_KEY = (1 << 1), /**< Is a fn button */ + HIDPP20_CONTROL_ID_FLAG_HOTKEY = (1 << 2), /**< Is a Hot key, not a standard kbd key */ + HIDPP20_CONTROL_ID_FLAG_FN_TOGGLE_AFFECTED = (1 << 3), /**< Fn toggle affects this key */ + HIDPP20_CONTROL_ID_FLAG_REPROGRAMMABLE = (1 << 4), /**< Key can be reprogrammed */ +}; struct hidpp20_control_id { uint8_t index; uint16_t control_id; uint16_t task_id; uint8_t flags; + /* fields below are only set for 0x1b04, not for 0x1b00 */ uint8_t position; uint8_t group; uint8_t group_mask; @@ -133,6 +143,15 @@ struct hidpp20_control_id { } reporting; }; +int hidpp20_kbd_reprogrammable_keys_get_controls(struct ratbag_device *device, + struct hidpp20_control_id **controls_list); + +/* -------------------------------------------------------------------------- */ +/* 0x1b04: Special keys and mouse buttons */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_SPECIAL_KEYS_BUTTONS 0x1b04 + /** * allocates a list of controls that has to be freed by the caller. *