From c4f5bd52184643b59fb9e6cae177b3f0e4f4d8b9 Mon Sep 17 00:00:00 2001 From: Thomas Hindoe Paaboel Andersen Date: Sat, 14 Jul 2018 02:44:08 +0200 Subject: [PATCH] Driver for logitech G600 A driver for the logitech G600. The behavior is similar to the G300 but with enough differences that it seemed cleaner to implement as a separate driver. --- data/devices/logitech-g600.device | 5 + meson.build | 2 + src/driver-logitech-g600.c | 656 ++++++++++++++++++++++++++++++ src/libratbag-data.c | 2 + src/libratbag-private.h | 1 + src/libratbag.c | 1 + 6 files changed, 667 insertions(+) create mode 100644 data/devices/logitech-g600.device create mode 100644 src/driver-logitech-g600.c diff --git a/data/devices/logitech-g600.device b/data/devices/logitech-g600.device new file mode 100644 index 00000000..49526358 --- /dev/null +++ b/data/devices/logitech-g600.device @@ -0,0 +1,5 @@ +# WARNING: this file was automatically converted and has not yet been vetted +[Device] +Name=Logitech G600 +DeviceMatch=usb:046d:c24a +Driver=logitech_g600 \ No newline at end of file diff --git a/meson.build b/meson.build index 50ca0763..3be0a86b 100644 --- a/meson.build +++ b/meson.build @@ -131,6 +131,7 @@ src_libratbag = [ 'src/driver-hidpp20.c', 'src/driver-hidpp10.c', 'src/driver-logitech-g300.c', + 'src/driver-logitech-g600.c', 'src/driver-roccat.c', 'src/driver-gskill.c', 'src/driver-steelseries.c', @@ -254,6 +255,7 @@ data_files = files( 'data/devices/logitech-g502-Proteus-Spectrum-over-USB.device', 'data/devices/logitech-g5-2007.device', 'data/devices/logitech-g5.device', + 'data/devices/logitech-g600.device', 'data/devices/logitech-g602.device', 'data/devices/logitech-g700.device', 'data/devices/logitech-g700s.device', diff --git a/src/driver-logitech-g600.c b/src/driver-logitech-g600.c new file mode 100644 index 00000000..b9326f17 --- /dev/null +++ b/src/driver-logitech-g600.c @@ -0,0 +1,656 @@ +/* + * Copyright © 2018 Thomas Hindoe Paaboel Andersen. + * + * 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. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#include "libratbag-private.h" +#include "libratbag-hidraw.h" + +#define LOGITECH_G600_NUM_PROFILES 3 +#define LOGITECH_G600_NUM_BUTTONS 20 +#define LOGITECH_G600_NUM_DPI 4 +#define LOGITECH_G600_NUM_LED 1 +#define LOGITECH_G600_DPI_MIN 200 +#define LOGITECH_G600_DPI_MAX 8200 + +#define LOGITECH_G600_REPORT_ID_GET_ACTIVE 0xF0 +#define LOGITECH_G600_REPORT_ID_SET_ACTIVE 0xF0 +#define LOGITECH_G600_REPORT_ID_PROFILE_0 0xF3 +#define LOGITECH_G600_REPORT_ID_PROFILE_1 0xF4 +#define LOGITECH_G600_REPORT_ID_PROFILE_2 0xF5 + +#define LOGITECH_G600_REPORT_SIZE_PROFILE 154 + +#define LOGITECH_G600_LED_SOLID 0x00 +#define LOGITECH_G600_LED_BREATHE 0x01 +#define LOGITECH_G600_LED_CYCLE 0x02 + +struct logitech_g600_button { + uint8_t code; + uint8_t modifier; + uint8_t key; +} __attribute__((packed)); + +struct logitech_g600_profile_report { + uint8_t id; + uint8_t led_red; + uint8_t led_green; + uint8_t led_blue; + uint8_t led_effect; + uint8_t led_duration; + uint8_t unknown1[5]; + uint8_t frequency; /* frequency = 1000 / (value + 1) */ + uint8_t dpi_shift; /* value is a linear range between 200->0x04, 8200->0xa4, so value * 50, 0x00 is disabled */ + uint8_t dpi_default; /* between 1 and 4*/ + uint8_t dpi[4]; /* value is a linear range between 200->0x04, 8200->0xa4, so value * 50, 0x00 is disabled */ + uint8_t unknown2[13]; + struct logitech_g600_button buttons[20]; + uint8_t g_shift_color[3]; /* can't assign it in LGS, but the 3rd profile has one that shows the feature :) */ + struct logitech_g600_button g_shift_buttons[20]; +} __attribute__((packed)); + +struct logitech_g600_active_profile_report { + uint8_t id; + uint8_t unknown1 :1; + uint8_t resolution :2; + uint8_t unknown2 :1; + uint8_t profile :4; + uint8_t unknown3; + uint8_t unknown4; +} __attribute__((packed)); + +struct logitech_g600_profile_data { + struct logitech_g600_profile_report report; +}; + +struct logitech_g600_data { + struct logitech_g600_profile_data profile_data[LOGITECH_G600_NUM_PROFILES]; +}; + +_Static_assert(sizeof(struct logitech_g600_profile_report) == LOGITECH_G600_REPORT_SIZE_PROFILE, + "Size of logitech_g600_profile_report is wrong"); + +struct logitech_g600_button_type_mapping { + uint8_t raw; + enum ratbag_button_type type; +}; + +static const struct logitech_g600_button_type_mapping logitech_g600_button_type_mapping[] = { + { 0, RATBAG_BUTTON_TYPE_LEFT }, + { 1, RATBAG_BUTTON_TYPE_RIGHT }, + { 2, RATBAG_BUTTON_TYPE_MIDDLE }, + { 3, RATBAG_BUTTON_TYPE_WHEEL_LEFT }, + { 4, RATBAG_BUTTON_TYPE_WHEEL_RIGHT }, + { 5, RATBAG_BUTTON_TYPE_PINKIE }, + { 6, RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP }, + { 7, RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP }, + { 8, RATBAG_BUTTON_TYPE_SIDE }, // 8 = G9 ... 19 = G20 + { 9, RATBAG_BUTTON_TYPE_SIDE }, + { 10, RATBAG_BUTTON_TYPE_SIDE }, + { 11, RATBAG_BUTTON_TYPE_SIDE }, + { 12, RATBAG_BUTTON_TYPE_SIDE }, + { 13, RATBAG_BUTTON_TYPE_SIDE }, + { 14, RATBAG_BUTTON_TYPE_SIDE }, + { 15, RATBAG_BUTTON_TYPE_SIDE }, + { 16, RATBAG_BUTTON_TYPE_SIDE }, + { 17, RATBAG_BUTTON_TYPE_SIDE }, + { 18, RATBAG_BUTTON_TYPE_SIDE }, + { 19, RATBAG_BUTTON_TYPE_SIDE }, +}; + +static enum ratbag_button_type +logitech_g600_raw_to_button_type(uint8_t data) +{ + const struct logitech_g600_button_type_mapping *mapping; + + ARRAY_FOR_EACH(logitech_g600_button_type_mapping, mapping) { + if (mapping->raw == data) + return mapping->type; + } + + return RATBAG_BUTTON_TYPE_UNKNOWN; +} + +struct logitech_g600_button_mapping { + uint8_t raw; + struct ratbag_button_action action; +}; + +static struct logitech_g600_button_mapping logitech_g600_button_mapping[] = { + /* 0x00 is either key or unassigned. Must be handled separatly */ + { 0x01, BUTTON_ACTION_BUTTON(1) }, + { 0x02, BUTTON_ACTION_BUTTON(2) }, + { 0x03, BUTTON_ACTION_BUTTON(3) }, + { 0x04, BUTTON_ACTION_BUTTON(4) }, + { 0x05, BUTTON_ACTION_BUTTON(5) }, + { 0x11, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP) }, + { 0x12, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN) }, + { 0x13, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP) }, + { 0x14, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP) }, + { 0x15, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE) }, + { 0x17, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE) }, +}; + +static const struct ratbag_button_action* +logitech_g600_raw_to_button_action(uint8_t data) +{ + struct logitech_g600_button_mapping *mapping; + + ARRAY_FOR_EACH(logitech_g600_button_mapping, mapping) { + if (mapping->raw == data) + return &mapping->action; + } + + return NULL; +} + +static uint8_t logitech_g600_modifier_to_raw(int modifier_flags) +{ + uint8_t modifiers = 0x00; + + if (modifier_flags & MODIFIER_LEFTCTRL) + modifiers |= 0x01; + if (modifier_flags & MODIFIER_LEFTSHIFT) + modifiers |= 0x02; + if (modifier_flags & MODIFIER_LEFTALT) + modifiers |= 0x04; + if (modifier_flags & MODIFIER_LEFTMETA) + modifiers |= 0x08; + if (modifier_flags & MODIFIER_RIGHTCTRL) + modifiers |= 0x10; + if (modifier_flags & MODIFIER_RIGHTSHIFT) + modifiers |= 0x20; + if (modifier_flags & MODIFIER_RIGHTALT) + modifiers |= 0x40; + if (modifier_flags & MODIFIER_RIGHTMETA) + modifiers |= 0x80; + + return modifiers; +} + +static int logitech_g600_raw_to_modifiers(uint8_t data) +{ + int modifiers = 0; + + if (data & 0x01) + modifiers |= MODIFIER_LEFTCTRL; + if (data & 0x02) + modifiers |= MODIFIER_LEFTSHIFT; + if (data & 0x04) + modifiers |= MODIFIER_LEFTALT; + if (data & 0x08) + modifiers |= MODIFIER_LEFTMETA; + if (data & 0x10) + modifiers |= MODIFIER_RIGHTCTRL; + if (data & 0x20) + modifiers |= MODIFIER_RIGHTSHIFT; + if (data & 0x40) + modifiers |= MODIFIER_RIGHTALT; + if (data & 0x80) + modifiers |= MODIFIER_RIGHTMETA; + + return modifiers; +} + +static uint8_t +logitech_g600_button_action_to_raw(const struct ratbag_button_action *action) +{ + struct logitech_g600_button_mapping *mapping; + + ARRAY_FOR_EACH(logitech_g600_button_mapping, mapping) { + if (ratbag_button_action_match(&mapping->action, action)) + return mapping->raw; + } + + return 0; +} + +static int +logitech_g600_get_active_profile_and_resolution(struct ratbag_device *device) +{ + struct ratbag_profile *profile; + struct logitech_g600_active_profile_report buf; + int ret; + + ret = ratbag_hidraw_raw_request(device, LOGITECH_G600_REPORT_ID_GET_ACTIVE, + (uint8_t*)&buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + + if (ret < 0) + return ret; + + if (ret != sizeof(buf)) + return -EIO; + + list_for_each(profile, &device->profiles, link) { + struct ratbag_resolution *resolution; + + if (profile->index != buf.profile) + continue; + + profile->is_active = true; + ratbag_profile_for_each_resolution(profile, resolution) { + resolution->is_active = resolution->index == buf.resolution; + } + } + + return 0; +} + +static int +logitech_g600_set_active_profile(struct ratbag_device *device, unsigned int index) +{ + struct ratbag_profile *profile; + uint8_t buf[] = {LOGITECH_G600_REPORT_ID_SET_ACTIVE, 0x80 | (index << 4), 0x00, 0x00}; + int ret; + + if (index >= LOGITECH_G600_NUM_PROFILES) + return -EINVAL; + + ret = ratbag_hidraw_raw_request(device, buf[0], buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + + if (ret != sizeof(buf)) + return ret; + + /* Update the active resolution. After profile change the default is used. */ + list_for_each(profile, &device->profiles, link) { + struct ratbag_resolution *resolution; + + if (profile->index != index) + continue; + + ratbag_profile_for_each_resolution(profile, resolution) { + resolution->is_active = resolution->is_default; + } + } + + return 0; +} + +static int +logitech_g600_set_current_resolution(struct ratbag_device *device, unsigned int index) +{ + uint8_t buf[] = {LOGITECH_G600_REPORT_ID_SET_ACTIVE, 0x40 | (index << 1), 0x00, 0x00}; + int ret; + + if (index >= LOGITECH_G600_NUM_DPI) + return -EINVAL; + + ret = ratbag_hidraw_raw_request(device, buf[0], buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + + return ret == sizeof(buf) ? 0 : ret; +} + +static void +logitech_g600_read_button(struct ratbag_button *button) +{ + const struct ratbag_button_action *action; + struct ratbag_profile *profile = button->profile; + struct ratbag_device *device = profile->device; + struct logitech_g600_data *drv_data = device->drv_data; + struct logitech_g600_profile_data *pdata; + struct logitech_g600_profile_report *profile_report; + struct logitech_g600_button *button_report; + + pdata = &drv_data->profile_data[profile->index]; + profile_report = &pdata->report; + button_report = &profile_report->buttons[button->index]; + + ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON); + ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL); + ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY); + ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO); + + button->type = logitech_g600_raw_to_button_type(button->index); + + action = logitech_g600_raw_to_button_action(button_report->code); + if (action) { + ratbag_button_set_action(button, action); + } + else if (button_report->code == 0x00 && (button_report->modifier > 0x00 || button_report->key > 0x00)) + { + unsigned int key, modifiers; + int rc; + + key = ratbag_hidraw_get_keycode_from_keyboard_usage(device, + button_report->key); + modifiers = logitech_g600_raw_to_modifiers(button_report->modifier); + + rc = ratbag_button_macro_new_from_keycode(button, key, modifiers); + if (rc < 0) { + log_error(device->ratbag, + "Error while reading button %d\n", + button->index); + button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE; + } + } +} + +static void +logitech_g600_read_led(struct ratbag_led *led) +{ + struct ratbag_profile *profile = led->profile; + struct ratbag_device *device = profile->device; + struct logitech_g600_data *drv_data = device->drv_data; + struct logitech_g600_profile_data *pdata; + struct logitech_g600_profile_report *report; + + pdata = &drv_data->profile_data[profile->index]; + report = &pdata->report; + + led->type = RATBAG_LED_TYPE_SIDE; + led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888; + ratbag_led_set_mode_capability(led, RATBAG_LED_OFF); + ratbag_led_set_mode_capability(led, RATBAG_LED_ON); + ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING); + ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE); + + switch (report->led_effect) { + case LOGITECH_G600_LED_SOLID: + led->mode = RATBAG_LED_ON; + break; + case LOGITECH_G600_LED_BREATHE: + led->mode = RATBAG_LED_BREATHING; + led->ms = report->led_duration * 1000; + break; + case LOGITECH_G600_LED_CYCLE: + led->mode = RATBAG_LED_CYCLE; + led->ms = report->led_duration * 1000; + break; + } + led->color.red = report->led_red; + led->color.green = report->led_green; + led->color.blue = report->led_blue; +} + +static void +logitech_g600_read_profile(struct ratbag_profile *profile) +{ + struct ratbag_device *device = profile->device; + struct logitech_g600_data *drv_data = device->drv_data; + struct logitech_g600_profile_data *pdata; + struct logitech_g600_profile_report *report; + struct ratbag_resolution *resolution; + unsigned int report_rates[] = { 125, 142, 166, 200, 250, 333, 500, 1000 }; + uint8_t report_id; + int rc; + struct ratbag_button *button; + struct ratbag_led *led; + + assert(profile->index < LOGITECH_G600_NUM_PROFILES); + + pdata = &drv_data->profile_data[profile->index]; + report = &pdata->report; + + switch (profile->index) { + case 0: report_id = LOGITECH_G600_REPORT_ID_PROFILE_0; break; + case 1: report_id = LOGITECH_G600_REPORT_ID_PROFILE_1; break; + case 2: report_id = LOGITECH_G600_REPORT_ID_PROFILE_2; break; + } + + rc = ratbag_hidraw_raw_request(device, report_id, + (uint8_t*)report, + sizeof(*report), + HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); + + if (rc < (int)sizeof(*report)) { + log_error(device->ratbag, + "Error while requesting profile: %d\n", rc); + return; + } + + + ratbag_profile_for_each_resolution(profile, resolution) { + resolution->dpi_x = report->dpi[resolution->index] * 50; + resolution->dpi_y = report->dpi[resolution->index] * 50; + resolution->hz = 1000 / (report->frequency + 1); + resolution->is_default = report->dpi_default - 1U == resolution->index; + resolution->is_active = false; //is set in a separate call + + ratbag_resolution_set_dpi_list_from_range(resolution, + LOGITECH_G600_DPI_MIN, + LOGITECH_G600_DPI_MAX); + ratbag_resolution_set_report_rate_list(resolution, report_rates, + ARRAY_LENGTH(report_rates)); + } + + ratbag_profile_for_each_button(profile, button) + logitech_g600_read_button(button); + + ratbag_profile_for_each_led(profile, led) + logitech_g600_read_led(led); + + log_debug(device->ratbag, "Unknown data in profile %d\n", profile->index); + log_buf_debug(device->ratbag, " profile->unknown1: ", + report->unknown1, 5); + log_buf_debug(device->ratbag, " profile->unknown2: ", + report->unknown2, 13); +} + +static int +logitech_g600_test_hidraw(struct ratbag_device *device) +{ + return ratbag_hidraw_has_report(device, LOGITECH_G600_REPORT_ID_GET_ACTIVE); +} + +static int +logitech_g600_probe(struct ratbag_device *device) +{ + int rc; + struct logitech_g600_data *drv_data = NULL; + struct ratbag_profile *profile; + + rc = ratbag_find_hidraw(device, logitech_g600_test_hidraw); + if (rc) + goto err; + + drv_data = zalloc(sizeof(*drv_data)); + ratbag_set_drv_data(device, drv_data); + + ratbag_device_init_profiles(device, + LOGITECH_G600_NUM_PROFILES, + LOGITECH_G600_NUM_DPI, + LOGITECH_G600_NUM_BUTTONS, + LOGITECH_G600_NUM_LED); + + ratbag_device_for_each_profile(device, profile) + logitech_g600_read_profile(profile); + + ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_RESOLUTION); + ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION); + ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_PROFILE); + ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_PROFILE); + ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON); + ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_KEY); + ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS); + + rc = logitech_g600_get_active_profile_and_resolution(device); + + if (rc < 0) { + log_error(device->ratbag, + "Can't talk to the mouse: '%s' (%d)\n", + strerror(-rc), + rc); + rc = -ENODEV; + goto err; + } + + return 0; + +err: + free(drv_data); + ratbag_set_drv_data(device, NULL); + return rc; +} + +static int +logitech_g600_write_profile(struct ratbag_profile *profile) +{ + struct ratbag_device *device = profile->device; + struct logitech_g600_data *drv_data = device->drv_data; + struct logitech_g600_profile_data *pdata; + struct logitech_g600_profile_report *report; + struct ratbag_resolution *resolution; + struct ratbag_button *button; + struct ratbag_led *led; + + uint8_t *buf; + int rc; + + pdata = &drv_data->profile_data[profile->index]; + report = &pdata->report; + + ratbag_profile_for_each_resolution(profile, resolution) { + report->dpi[resolution->index] = resolution->dpi_x / 50; + + if (resolution->is_default) + report->dpi_default = resolution->index + 1; + + if (profile->is_active && resolution->is_active) + logitech_g600_set_current_resolution(device, resolution->index); + + /* The same hz is used for all resolutions */ + if (resolution->index == 0) + report->frequency = (1000 / resolution->hz) - 1; + } + + list_for_each(button, &profile->buttons, link) { + struct ratbag_button_action *action = &button->action; + struct logitech_g600_button *raw_button; + + raw_button = &report->buttons[button->index]; + + raw_button->code = logitech_g600_button_action_to_raw(action); + raw_button->modifier = 0x00; + raw_button->key = 0x00; + + if (action->type == RATBAG_BUTTON_ACTION_TYPE_MACRO) { + unsigned int key, modifiers; + + rc = ratbag_action_keycode_from_macro(action, + &key, + &modifiers); + if (rc < 0) { + log_error(device->ratbag, + "Error while writing macro for button %d\n", + button->index); + } + + raw_button->key = ratbag_hidraw_get_keyboard_usage_from_keycode( + device, key); + raw_button->modifier = logitech_g600_modifier_to_raw(modifiers); + } + } + + list_for_each(led, &profile->leds, link) { + report->led_red = led->color.red; + report->led_green = led->color.green; + report->led_blue = led->color.blue; + + switch (led->mode) { + case RATBAG_LED_ON: + report->led_effect = LOGITECH_G600_LED_SOLID; + break; + case RATBAG_LED_OFF: + report->led_effect = LOGITECH_G600_LED_SOLID; + report->led_red = 0x00; + report->led_green = 0x00; + report->led_blue = 0x00; + break; + case RATBAG_LED_BREATHING: + report->led_effect = LOGITECH_G600_LED_BREATHE; + report->led_duration = led->ms / 1000; + break; + case RATBAG_LED_CYCLE: + report->led_effect = LOGITECH_G600_LED_CYCLE; + report->led_duration = led->ms / 1000; + break; + } + + if (report->led_duration > 0x0f) + report->led_duration = 0x0f; + } + + buf = (uint8_t*)report; + + rc = ratbag_hidraw_raw_request(device, report->id, + buf, + sizeof(*report), + HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); + + if (rc < (int)sizeof(*report)) { + log_error(device->ratbag, + "Error while writing profile: %d\n", rc); + return rc; + } + + return 0; +} + +static int +logitech_g600_commit(struct ratbag_device *device) +{ + struct ratbag_profile *profile; + int rc = 0; + + list_for_each(profile, &device->profiles, link) { + if (!profile->dirty) + continue; + + log_debug(device->ratbag, + "Profile %d changed, rewriting\n", profile->index); + + rc = logitech_g600_write_profile(profile); + if (rc) + return rc; + } + + return 0; +} + +static void +logitech_g600_remove(struct ratbag_device *device) +{ + ratbag_close_hidraw(device); + free(ratbag_get_drv_data(device)); +} + +struct ratbag_driver logitech_g600_driver = { + .name = "Logitech G600", + .id = "logitech_g600", + .probe = logitech_g600_probe, + .remove = logitech_g600_remove, + .commit = logitech_g600_commit, + .set_active_profile = logitech_g600_set_active_profile, +}; diff --git a/src/libratbag-data.c b/src/libratbag-data.c index d3ae1041..3813c2ac 100644 --- a/src/libratbag-data.c +++ b/src/libratbag-data.c @@ -47,6 +47,7 @@ enum driver { ETEKCITY, GSKILL, LOGITECH_G300, + LOGITECH_G600, STEELSERIES, }; @@ -243,6 +244,7 @@ static const struct driver_map { { ETEKCITY, "etekcity", NULL}, { GSKILL, "gskill", NULL }, { LOGITECH_G300, "logitech_g300", NULL}, + { LOGITECH_G600, "logitech_g600", NULL}, { STEELSERIES, "steelseries", init_data_steelseries }, }; diff --git a/src/libratbag-private.h b/src/libratbag-private.h index 4b516625..26823f9d 100644 --- a/src/libratbag-private.h +++ b/src/libratbag-private.h @@ -603,6 +603,7 @@ struct ratbag_driver etekcity_driver; struct ratbag_driver hidpp20_driver; struct ratbag_driver hidpp10_driver; struct ratbag_driver logitech_g300_driver; +struct ratbag_driver logitech_g600_driver; struct ratbag_driver roccat_driver; struct ratbag_driver gskill_driver; struct ratbag_driver steelseries_driver; diff --git a/src/libratbag.c b/src/libratbag.c index 85b9b3a2..432a3cc9 100644 --- a/src/libratbag.c +++ b/src/libratbag.c @@ -583,6 +583,7 @@ ratbag_create_context(const struct ratbag_interface *interface, ratbag_register_driver(ratbag, &hidpp20_driver); ratbag_register_driver(ratbag, &hidpp10_driver); ratbag_register_driver(ratbag, &logitech_g300_driver); + ratbag_register_driver(ratbag, &logitech_g600_driver); ratbag_register_driver(ratbag, &roccat_driver); ratbag_register_driver(ratbag, &gskill_driver); ratbag_register_driver(ratbag, &steelseries_driver);