From ba26f6f35c84071ea8c3d3de8b85021fa77993b9 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 22 Oct 2015 11:35:38 -0400 Subject: [PATCH] hidpp20: implement the write functionality of the profiles Not completely full features, but works to set the DPI, report rate and buttons assignments (no macros yet). The current code reads first the current values, changes that needs to be changed and writes back the data. Long term, we want to build the data directly from the profiles and just write them. Signed-off-by: Benjamin Tissoires --- src/hidpp20.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/hidpp20.h | 3 + 2 files changed, 202 insertions(+) diff --git a/src/hidpp20.c b/src/hidpp20.c index edeab4a8..659d8bca 100644 --- a/src/hidpp20.c +++ b/src/hidpp20.c @@ -972,6 +972,110 @@ hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device, return 0; } +static int +hidpp20_onboard_profiles_write_start(struct hidpp20_device *device, + uint8_t page, + uint8_t section) +{ + 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_ADDR_WRITE, + .msg.parameters[0] = 0x00, + .msg.parameters[1] = page, + .msg.parameters[2] = 0, + .msg.parameters[3] = section, + .msg.parameters[4] = 0x01, + }; + + 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 0; +} + +static int +hidpp20_onboard_profiles_write_end(struct hidpp20_device *device) +{ + 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_MEMORY_WRITE_END, + }; + + 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 0; +} + +static int +hidpp20_onboard_profiles_write_data(struct hidpp20_device *device, + uint8_t page, + uint8_t section, + uint8_t *data, + size_t len) +{ + uint8_t feature_index; + int transfered = 0; + int rc; + union hidpp20_message msg = { + .msg.report_id = REPORT_ID_LONG, + .msg.device_idx = device->index, + .msg.address = CMD_ONBOARD_PROFILES_MEMORY_WRITE, + }; + + 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_onboard_profiles_write_start(device, page, section); + if (rc) + return rc; + + while (len >= 16) { + msg.msg.address = CMD_ONBOARD_PROFILES_MEMORY_WRITE; + memcpy(msg.msg.parameters, data, 16); + + rc = hidpp20_request_command(device, &msg); + if (rc) + return rc; + + len -= 16; + data += 16; + transfered += 16; + } + + rc = hidpp20_onboard_profiles_write_end(device); + if (rc) + return rc; + + return transfered; +} + int hidpp20_onboard_profiles_get_current_profile(struct hidpp20_device *device) { @@ -1121,6 +1225,101 @@ int hidpp20_onboard_profiles_read(struct hidpp20_device *device, return 0; } +/* + * The following crc computation has been provided by Logitech + */ +#define CRC_CCITT_SEED 0xFFFF + +static uint16_t +hidpp20_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; +} + +int hidpp20_onboard_profiles_write(struct hidpp20_device *device, + unsigned int index, + struct hidpp20_profiles *profiles_list) +{ + uint8_t data[HIDPP20_PROFILE_SIZE] = {0}; + uint16_t temp, crc; + struct hidpp20_profile *profile = &profiles_list->profiles[index]; + unsigned i; + int rc; + + if (index >= profiles_list->num_profiles) + return -EINVAL; + + for (i = 0; i < HIDPP20_PROFILE_SIZE / 0x10; i++) { + rc = hidpp20_onboard_profiles_read_memory(device, + 0, + index + 1, + i * 0x10, + data + i * 0x10); + if (rc < 0) + return rc; + } + + data[0] = 1000 / profile->report_rate; + data[1] = profile->default_dpi; + data[2] = profile->switched_dpi; + + for (i = 0; i < 5; i++) { + data[2 * i + 3] = profile->dpi[i] & 0xff; + data[2 * i + 4] = (profile->dpi[i] >> 8) & 0xff; + } + + for (i = 0; i < profiles_list->num_buttons; i++) { + uint8_t *button = data + 0x20 + i * 4; + + button[0] = profile->buttons[i].type & 0xf0; + + if (button[0] == HIDPP20_BUTTON_HID) { + button[1] = profile->buttons[i].type & 0x0f; + + if (profile->buttons[i].type == HIDPP20_BUTTON_HID_KEYBOARD) { + button[2] = profile->buttons[i].modifiers; + button[3] = profile->buttons[i].code; + } else { + temp = 1U << (profile->buttons[i].code - 1); + button[2] = (temp >> 8) & 0xff; + button[3] = temp & 0xff; + } + } else if (button[0] == HIDPP20_BUTTON_SPECIAL) { + button[1] = profile->buttons[i].code; + } + } + + crc = hidpp20_crc_ccitt(data, sizeof(data) - 2); + + data[HIDPP20_PROFILE_SIZE - 2] = (crc >> 8) & 0xff; + data[HIDPP20_PROFILE_SIZE - 1] = crc & 0xff; + + rc = hidpp20_onboard_profiles_write_data(device, index + 1, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + if (rc != sizeof(data)) + return -EIO; + + return 0; +} + static const enum ratbag_button_action_special hidpp20_profiles_specials[] = { [0x00] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID, [0x01] = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT, diff --git a/src/hidpp20.h b/src/hidpp20.h index 66350799..24ec4b44 100644 --- a/src/hidpp20.h +++ b/src/hidpp20.h @@ -317,6 +317,9 @@ int hidpp20_onboard_profiles_read(struct hidpp20_device *device, unsigned int index, struct hidpp20_profiles *profiles_list); +int hidpp20_onboard_profiles_write(struct hidpp20_device *device, + unsigned int index, + struct hidpp20_profiles *profiles_list); enum ratbag_button_action_special hidpp20_onboard_profiles_get_special(uint8_t code);