959 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * HID driver for gaming keys on Logitech gaming keyboards (such as the G15)4 *5 * Copyright (c) 2019 Hans de Goede <hdegoede@redhat.com>6 */7 8#include <linux/device.h>9#include <linux/hid.h>10#include <linux/leds.h>11#include <linux/module.h>12#include <linux/random.h>13#include <linux/sched.h>14#include <linux/usb.h>15#include <linux/wait.h>16 17#include "hid-ids.h"18 19#define LG_G15_TRANSFER_BUF_SIZE 2020 21#define LG_G15_FEATURE_REPORT 0x0222 23#define LG_G510_FEATURE_M_KEYS_LEDS 0x0424#define LG_G510_FEATURE_BACKLIGHT_RGB 0x0525#define LG_G510_FEATURE_POWER_ON_RGB 0x0626 27enum lg_g15_model {28 LG_G15,29 LG_G15_V2,30 LG_G510,31 LG_G510_USB_AUDIO,32 LG_Z10,33};34 35enum lg_g15_led_type {36 LG_G15_KBD_BRIGHTNESS,37 LG_G15_LCD_BRIGHTNESS,38 LG_G15_BRIGHTNESS_MAX,39 LG_G15_MACRO_PRESET1 = 2,40 LG_G15_MACRO_PRESET2,41 LG_G15_MACRO_PRESET3,42 LG_G15_MACRO_RECORD,43 LG_G15_LED_MAX44};45 46struct lg_g15_led {47 struct led_classdev cdev;48 enum led_brightness brightness;49 enum lg_g15_led_type led;50 u8 red, green, blue;51};52 53struct lg_g15_data {54 /* Must be first for proper dma alignment */55 u8 transfer_buf[LG_G15_TRANSFER_BUF_SIZE];56 /* Protects the transfer_buf and led brightness */57 struct mutex mutex;58 struct work_struct work;59 struct input_dev *input;60 struct hid_device *hdev;61 enum lg_g15_model model;62 struct lg_g15_led leds[LG_G15_LED_MAX];63 bool game_mode_enabled;64};65 66/******** G15 and G15 v2 LED functions ********/67 68static int lg_g15_update_led_brightness(struct lg_g15_data *g15)69{70 int ret;71 72 ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT,73 g15->transfer_buf, 4,74 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);75 if (ret != 4) {76 hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret);77 return (ret < 0) ? ret : -EIO;78 }79 80 g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = g15->transfer_buf[1];81 g15->leds[LG_G15_LCD_BRIGHTNESS].brightness = g15->transfer_buf[2];82 83 g15->leds[LG_G15_MACRO_PRESET1].brightness =84 !(g15->transfer_buf[3] & 0x01);85 g15->leds[LG_G15_MACRO_PRESET2].brightness =86 !(g15->transfer_buf[3] & 0x02);87 g15->leds[LG_G15_MACRO_PRESET3].brightness =88 !(g15->transfer_buf[3] & 0x04);89 g15->leds[LG_G15_MACRO_RECORD].brightness =90 !(g15->transfer_buf[3] & 0x08);91 92 return 0;93}94 95static enum led_brightness lg_g15_led_get(struct led_classdev *led_cdev)96{97 struct lg_g15_led *g15_led =98 container_of(led_cdev, struct lg_g15_led, cdev);99 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);100 enum led_brightness brightness;101 102 mutex_lock(&g15->mutex);103 lg_g15_update_led_brightness(g15);104 brightness = g15->leds[g15_led->led].brightness;105 mutex_unlock(&g15->mutex);106 107 return brightness;108}109 110static int lg_g15_led_set(struct led_classdev *led_cdev,111 enum led_brightness brightness)112{113 struct lg_g15_led *g15_led =114 container_of(led_cdev, struct lg_g15_led, cdev);115 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);116 u8 val, mask = 0;117 int i, ret;118 119 /* Ignore LED off on unregister / keyboard unplug */120 if (led_cdev->flags & LED_UNREGISTERING)121 return 0;122 123 mutex_lock(&g15->mutex);124 125 g15->transfer_buf[0] = LG_G15_FEATURE_REPORT;126 g15->transfer_buf[3] = 0;127 128 if (g15_led->led < LG_G15_BRIGHTNESS_MAX) {129 g15->transfer_buf[1] = g15_led->led + 1;130 g15->transfer_buf[2] = brightness << (g15_led->led * 4);131 } else {132 for (i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; i++) {133 if (i == g15_led->led)134 val = brightness;135 else136 val = g15->leds[i].brightness;137 138 if (val)139 mask |= 1 << (i - LG_G15_MACRO_PRESET1);140 }141 142 g15->transfer_buf[1] = 0x04;143 g15->transfer_buf[2] = ~mask;144 }145 146 ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT,147 g15->transfer_buf, 4,148 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);149 if (ret == 4) {150 /* Success */151 g15_led->brightness = brightness;152 ret = 0;153 } else {154 hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);155 ret = (ret < 0) ? ret : -EIO;156 }157 158 mutex_unlock(&g15->mutex);159 160 return ret;161}162 163static void lg_g15_leds_changed_work(struct work_struct *work)164{165 struct lg_g15_data *g15 = container_of(work, struct lg_g15_data, work);166 enum led_brightness old_brightness[LG_G15_BRIGHTNESS_MAX];167 enum led_brightness brightness[LG_G15_BRIGHTNESS_MAX];168 int i, ret;169 170 mutex_lock(&g15->mutex);171 for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++)172 old_brightness[i] = g15->leds[i].brightness;173 174 ret = lg_g15_update_led_brightness(g15);175 176 for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++)177 brightness[i] = g15->leds[i].brightness;178 mutex_unlock(&g15->mutex);179 180 if (ret)181 return;182 183 for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++) {184 if (brightness[i] == old_brightness[i])185 continue;186 187 led_classdev_notify_brightness_hw_changed(&g15->leds[i].cdev,188 brightness[i]);189 }190}191 192/******** G510 LED functions ********/193 194static int lg_g510_get_initial_led_brightness(struct lg_g15_data *g15, int i)195{196 int ret, high;197 198 ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_BACKLIGHT_RGB + i,199 g15->transfer_buf, 4,200 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);201 if (ret != 4) {202 hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret);203 return (ret < 0) ? ret : -EIO;204 }205 206 high = max3(g15->transfer_buf[1], g15->transfer_buf[2],207 g15->transfer_buf[3]);208 209 if (high) {210 g15->leds[i].red =211 DIV_ROUND_CLOSEST(g15->transfer_buf[1] * 255, high);212 g15->leds[i].green =213 DIV_ROUND_CLOSEST(g15->transfer_buf[2] * 255, high);214 g15->leds[i].blue =215 DIV_ROUND_CLOSEST(g15->transfer_buf[3] * 255, high);216 g15->leds[i].brightness = high;217 } else {218 g15->leds[i].red = 255;219 g15->leds[i].green = 255;220 g15->leds[i].blue = 255;221 g15->leds[i].brightness = 0;222 }223 224 return 0;225}226 227/* Must be called with g15->mutex locked */228static int lg_g510_kbd_led_write(struct lg_g15_data *g15,229 struct lg_g15_led *g15_led,230 enum led_brightness brightness)231{232 int ret;233 234 g15->transfer_buf[0] = 5 + g15_led->led;235 g15->transfer_buf[1] =236 DIV_ROUND_CLOSEST(g15_led->red * brightness, 255);237 g15->transfer_buf[2] =238 DIV_ROUND_CLOSEST(g15_led->green * brightness, 255);239 g15->transfer_buf[3] =240 DIV_ROUND_CLOSEST(g15_led->blue * brightness, 255);241 242 ret = hid_hw_raw_request(g15->hdev,243 LG_G510_FEATURE_BACKLIGHT_RGB + g15_led->led,244 g15->transfer_buf, 4,245 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);246 if (ret == 4) {247 /* Success */248 g15_led->brightness = brightness;249 ret = 0;250 } else {251 hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);252 ret = (ret < 0) ? ret : -EIO;253 }254 255 return ret;256}257 258static int lg_g510_kbd_led_set(struct led_classdev *led_cdev,259 enum led_brightness brightness)260{261 struct lg_g15_led *g15_led =262 container_of(led_cdev, struct lg_g15_led, cdev);263 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);264 int ret;265 266 /* Ignore LED off on unregister / keyboard unplug */267 if (led_cdev->flags & LED_UNREGISTERING)268 return 0;269 270 mutex_lock(&g15->mutex);271 ret = lg_g510_kbd_led_write(g15, g15_led, brightness);272 mutex_unlock(&g15->mutex);273 274 return ret;275}276 277static enum led_brightness lg_g510_kbd_led_get(struct led_classdev *led_cdev)278{279 struct lg_g15_led *g15_led =280 container_of(led_cdev, struct lg_g15_led, cdev);281 282 return g15_led->brightness;283}284 285static ssize_t color_store(struct device *dev, struct device_attribute *attr,286 const char *buf, size_t count)287{288 struct led_classdev *led_cdev = dev_get_drvdata(dev);289 struct lg_g15_led *g15_led =290 container_of(led_cdev, struct lg_g15_led, cdev);291 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);292 unsigned long value;293 int ret;294 295 if (count < 7 || (count == 8 && buf[7] != '\n') || count > 8)296 return -EINVAL;297 298 if (buf[0] != '#')299 return -EINVAL;300 301 ret = kstrtoul(buf + 1, 16, &value);302 if (ret)303 return ret;304 305 mutex_lock(&g15->mutex);306 g15_led->red = (value & 0xff0000) >> 16;307 g15_led->green = (value & 0x00ff00) >> 8;308 g15_led->blue = (value & 0x0000ff);309 ret = lg_g510_kbd_led_write(g15, g15_led, g15_led->brightness);310 mutex_unlock(&g15->mutex);311 312 return (ret < 0) ? ret : count;313}314 315static ssize_t color_show(struct device *dev, struct device_attribute *attr,316 char *buf)317{318 struct led_classdev *led_cdev = dev_get_drvdata(dev);319 struct lg_g15_led *g15_led =320 container_of(led_cdev, struct lg_g15_led, cdev);321 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);322 ssize_t ret;323 324 mutex_lock(&g15->mutex);325 ret = sprintf(buf, "#%02x%02x%02x\n",326 g15_led->red, g15_led->green, g15_led->blue);327 mutex_unlock(&g15->mutex);328 329 return ret;330}331 332static DEVICE_ATTR_RW(color);333 334static struct attribute *lg_g510_kbd_led_attrs[] = {335 &dev_attr_color.attr,336 NULL,337};338 339static const struct attribute_group lg_g510_kbd_led_group = {340 .attrs = lg_g510_kbd_led_attrs,341};342 343static const struct attribute_group *lg_g510_kbd_led_groups[] = {344 &lg_g510_kbd_led_group,345 NULL,346};347 348static void lg_g510_leds_sync_work(struct work_struct *work)349{350 struct lg_g15_data *g15 = container_of(work, struct lg_g15_data, work);351 352 mutex_lock(&g15->mutex);353 lg_g510_kbd_led_write(g15, &g15->leds[LG_G15_KBD_BRIGHTNESS],354 g15->leds[LG_G15_KBD_BRIGHTNESS].brightness);355 mutex_unlock(&g15->mutex);356}357 358static int lg_g510_update_mkey_led_brightness(struct lg_g15_data *g15)359{360 int ret;361 362 ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_M_KEYS_LEDS,363 g15->transfer_buf, 2,364 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);365 if (ret != 2) {366 hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret);367 ret = (ret < 0) ? ret : -EIO;368 }369 370 g15->leds[LG_G15_MACRO_PRESET1].brightness =371 !!(g15->transfer_buf[1] & 0x80);372 g15->leds[LG_G15_MACRO_PRESET2].brightness =373 !!(g15->transfer_buf[1] & 0x40);374 g15->leds[LG_G15_MACRO_PRESET3].brightness =375 !!(g15->transfer_buf[1] & 0x20);376 g15->leds[LG_G15_MACRO_RECORD].brightness =377 !!(g15->transfer_buf[1] & 0x10);378 379 return 0;380}381 382static enum led_brightness lg_g510_mkey_led_get(struct led_classdev *led_cdev)383{384 struct lg_g15_led *g15_led =385 container_of(led_cdev, struct lg_g15_led, cdev);386 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);387 enum led_brightness brightness;388 389 mutex_lock(&g15->mutex);390 lg_g510_update_mkey_led_brightness(g15);391 brightness = g15->leds[g15_led->led].brightness;392 mutex_unlock(&g15->mutex);393 394 return brightness;395}396 397static int lg_g510_mkey_led_set(struct led_classdev *led_cdev,398 enum led_brightness brightness)399{400 struct lg_g15_led *g15_led =401 container_of(led_cdev, struct lg_g15_led, cdev);402 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);403 u8 val, mask = 0;404 int i, ret;405 406 /* Ignore LED off on unregister / keyboard unplug */407 if (led_cdev->flags & LED_UNREGISTERING)408 return 0;409 410 mutex_lock(&g15->mutex);411 412 for (i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; i++) {413 if (i == g15_led->led)414 val = brightness;415 else416 val = g15->leds[i].brightness;417 418 if (val)419 mask |= 0x80 >> (i - LG_G15_MACRO_PRESET1);420 }421 422 g15->transfer_buf[0] = LG_G510_FEATURE_M_KEYS_LEDS;423 g15->transfer_buf[1] = mask;424 425 ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_M_KEYS_LEDS,426 g15->transfer_buf, 2,427 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);428 if (ret == 2) {429 /* Success */430 g15_led->brightness = brightness;431 ret = 0;432 } else {433 hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);434 ret = (ret < 0) ? ret : -EIO;435 }436 437 mutex_unlock(&g15->mutex);438 439 return ret;440}441 442/******** Generic LED functions ********/443static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15)444{445 int ret;446 447 switch (g15->model) {448 case LG_G15:449 case LG_G15_V2:450 return lg_g15_update_led_brightness(g15);451 case LG_G510:452 case LG_G510_USB_AUDIO:453 ret = lg_g510_get_initial_led_brightness(g15, 0);454 if (ret)455 return ret;456 457 ret = lg_g510_get_initial_led_brightness(g15, 1);458 if (ret)459 return ret;460 461 return lg_g510_update_mkey_led_brightness(g15);462 case LG_Z10:463 /*464 * Getting the LCD backlight brightness is not supported.465 * Reading Feature(2) fails with -EPIPE and this crashes466 * the LCD and touch keys part of the speakers.467 */468 return 0;469 }470 return -EINVAL; /* Never reached */471}472 473/******** Input functions ********/474 475/* On the G15 Mark I Logitech has been quite creative with which bit is what */476static void lg_g15_handle_lcd_menu_keys(struct lg_g15_data *g15, u8 *data)477{478 int i, val;479 480 /* Most left (round/display) button below the LCD */481 input_report_key(g15->input, KEY_KBD_LCD_MENU1, data[8] & 0x80);482 /* 4 other buttons below the LCD */483 for (i = 0; i < 4; i++) {484 val = data[i + 2] & 0x80;485 input_report_key(g15->input, KEY_KBD_LCD_MENU2 + i, val);486 }487}488 489static int lg_g15_event(struct lg_g15_data *g15, u8 *data)490{491 int i, val;492 493 /* G1 - G6 */494 for (i = 0; i < 6; i++) {495 val = data[i + 1] & (1 << i);496 input_report_key(g15->input, KEY_MACRO1 + i, val);497 }498 /* G7 - G12 */499 for (i = 0; i < 6; i++) {500 val = data[i + 2] & (1 << i);501 input_report_key(g15->input, KEY_MACRO7 + i, val);502 }503 /* G13 - G17 */504 for (i = 0; i < 5; i++) {505 val = data[i + 1] & (4 << i);506 input_report_key(g15->input, KEY_MACRO13 + i, val);507 }508 /* G18 */509 input_report_key(g15->input, KEY_MACRO18, data[8] & 0x40);510 511 /* M1 - M3 */512 for (i = 0; i < 3; i++) {513 val = data[i + 6] & (1 << i);514 input_report_key(g15->input, KEY_MACRO_PRESET1 + i, val);515 }516 /* MR */517 input_report_key(g15->input, KEY_MACRO_RECORD_START, data[7] & 0x40);518 519 lg_g15_handle_lcd_menu_keys(g15, data);520 521 /* Backlight cycle button pressed? */522 if (data[1] & 0x80)523 schedule_work(&g15->work);524 525 input_sync(g15->input);526 return 0;527}528 529static int lg_g15_v2_event(struct lg_g15_data *g15, u8 *data)530{531 int i, val;532 533 /* G1 - G6 */534 for (i = 0; i < 6; i++) {535 val = data[1] & (1 << i);536 input_report_key(g15->input, KEY_MACRO1 + i, val);537 }538 539 /* M1 - M3 + MR */540 input_report_key(g15->input, KEY_MACRO_PRESET1, data[1] & 0x40);541 input_report_key(g15->input, KEY_MACRO_PRESET2, data[1] & 0x80);542 input_report_key(g15->input, KEY_MACRO_PRESET3, data[2] & 0x20);543 input_report_key(g15->input, KEY_MACRO_RECORD_START, data[2] & 0x40);544 545 /* Round button to the left of the LCD */546 input_report_key(g15->input, KEY_KBD_LCD_MENU1, data[2] & 0x80);547 /* 4 buttons below the LCD */548 for (i = 0; i < 4; i++) {549 val = data[2] & (2 << i);550 input_report_key(g15->input, KEY_KBD_LCD_MENU2 + i, val);551 }552 553 /* Backlight cycle button pressed? */554 if (data[2] & 0x01)555 schedule_work(&g15->work);556 557 input_sync(g15->input);558 return 0;559}560 561static int lg_g510_event(struct lg_g15_data *g15, u8 *data)562{563 bool game_mode_enabled;564 int i, val;565 566 /* G1 - G18 */567 for (i = 0; i < 18; i++) {568 val = data[i / 8 + 1] & (1 << (i % 8));569 input_report_key(g15->input, KEY_MACRO1 + i, val);570 }571 572 /* Game mode on/off slider */573 game_mode_enabled = data[3] & 0x04;574 if (game_mode_enabled != g15->game_mode_enabled) {575 if (game_mode_enabled)576 hid_info(g15->hdev, "Game Mode enabled, Windows (super) key is disabled\n");577 else578 hid_info(g15->hdev, "Game Mode disabled\n");579 g15->game_mode_enabled = game_mode_enabled;580 }581 582 /* M1 - M3 */583 for (i = 0; i < 3; i++) {584 val = data[3] & (0x10 << i);585 input_report_key(g15->input, KEY_MACRO_PRESET1 + i, val);586 }587 /* MR */588 input_report_key(g15->input, KEY_MACRO_RECORD_START, data[3] & 0x80);589 590 /* LCD menu keys */591 for (i = 0; i < 5; i++) {592 val = data[4] & (1 << i);593 input_report_key(g15->input, KEY_KBD_LCD_MENU1 + i, val);594 }595 596 /* Headphone Mute */597 input_report_key(g15->input, KEY_MUTE, data[4] & 0x20);598 /* Microphone Mute */599 input_report_key(g15->input, KEY_F20, data[4] & 0x40);600 601 input_sync(g15->input);602 return 0;603}604 605static int lg_g510_leds_event(struct lg_g15_data *g15, u8 *data)606{607 bool backlight_disabled;608 609 /*610 * The G510 ignores backlight updates when the backlight is turned off611 * through the light toggle button on the keyboard, to work around this612 * we queue a workitem to sync values when the backlight is turned on.613 */614 backlight_disabled = data[1] & 0x04;615 if (!backlight_disabled)616 schedule_work(&g15->work);617 618 return 0;619}620 621static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,622 u8 *data, int size)623{624 struct lg_g15_data *g15 = hid_get_drvdata(hdev);625 626 if (!g15)627 return 0;628 629 switch (g15->model) {630 case LG_G15:631 if (data[0] == 0x02 && size == 9)632 return lg_g15_event(g15, data);633 break;634 case LG_G15_V2:635 if (data[0] == 0x02 && size == 5)636 return lg_g15_v2_event(g15, data);637 break;638 case LG_Z10:639 if (data[0] == 0x02 && size == 9) {640 lg_g15_handle_lcd_menu_keys(g15, data);641 input_sync(g15->input);642 }643 break;644 case LG_G510:645 case LG_G510_USB_AUDIO:646 if (data[0] == 0x03 && size == 5)647 return lg_g510_event(g15, data);648 if (data[0] == 0x04 && size == 2)649 return lg_g510_leds_event(g15, data);650 break;651 }652 653 return 0;654}655 656static int lg_g15_input_open(struct input_dev *dev)657{658 struct hid_device *hdev = input_get_drvdata(dev);659 660 return hid_hw_open(hdev);661}662 663static void lg_g15_input_close(struct input_dev *dev)664{665 struct hid_device *hdev = input_get_drvdata(dev);666 667 hid_hw_close(hdev);668}669 670static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name)671{672 g15->leds[i].led = i;673 g15->leds[i].cdev.name = name;674 675 switch (g15->model) {676 case LG_G15:677 case LG_G15_V2:678 g15->leds[i].cdev.brightness_get = lg_g15_led_get;679 fallthrough;680 case LG_Z10:681 g15->leds[i].cdev.brightness_set_blocking = lg_g15_led_set;682 if (i < LG_G15_BRIGHTNESS_MAX) {683 g15->leds[i].cdev.flags = LED_BRIGHT_HW_CHANGED;684 g15->leds[i].cdev.max_brightness = 2;685 } else {686 g15->leds[i].cdev.max_brightness = 1;687 }688 break;689 case LG_G510:690 case LG_G510_USB_AUDIO:691 switch (i) {692 case LG_G15_LCD_BRIGHTNESS:693 /*694 * The G510 does not have a separate LCD brightness,695 * but it does have a separate power-on (reset) value.696 */697 g15->leds[i].cdev.name = "g15::power_on_backlight_val";698 fallthrough;699 case LG_G15_KBD_BRIGHTNESS:700 g15->leds[i].cdev.brightness_set_blocking =701 lg_g510_kbd_led_set;702 g15->leds[i].cdev.brightness_get =703 lg_g510_kbd_led_get;704 g15->leds[i].cdev.max_brightness = 255;705 g15->leds[i].cdev.groups = lg_g510_kbd_led_groups;706 break;707 default:708 g15->leds[i].cdev.brightness_set_blocking =709 lg_g510_mkey_led_set;710 g15->leds[i].cdev.brightness_get =711 lg_g510_mkey_led_get;712 g15->leds[i].cdev.max_brightness = 1;713 }714 break;715 }716 717 return devm_led_classdev_register(&g15->hdev->dev, &g15->leds[i].cdev);718}719 720/* Common input device init code shared between keyboards and Z-10 speaker handling */721static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *input,722 const char *name)723{724 int i;725 726 input->name = name;727 input->phys = hdev->phys;728 input->uniq = hdev->uniq;729 input->id.bustype = hdev->bus;730 input->id.vendor = hdev->vendor;731 input->id.product = hdev->product;732 input->id.version = hdev->version;733 input->dev.parent = &hdev->dev;734 input->open = lg_g15_input_open;735 input->close = lg_g15_input_close;736 737 /* Keys below the LCD, intended for controlling a menu on the LCD */738 for (i = 0; i < 5; i++)739 input_set_capability(input, EV_KEY, KEY_KBD_LCD_MENU1 + i);740}741 742static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)743{744 static const char * const led_names[] = {745 "g15::kbd_backlight",746 "g15::lcd_backlight",747 "g15::macro_preset1",748 "g15::macro_preset2",749 "g15::macro_preset3",750 "g15::macro_record",751 };752 u8 gkeys_settings_output_report = 0;753 u8 gkeys_settings_feature_report = 0;754 struct hid_report_enum *rep_enum;755 unsigned int connect_mask = 0;756 bool has_ff000000 = false;757 struct lg_g15_data *g15;758 struct input_dev *input;759 struct hid_report *rep;760 int ret, i, gkeys = 0;761 762 hdev->quirks |= HID_QUIRK_INPUT_PER_APP;763 764 ret = hid_parse(hdev);765 if (ret)766 return ret;767 768 /*769 * Some models have multiple interfaces, we want the interface with770 * the f000.0000 application input report.771 */772 rep_enum = &hdev->report_enum[HID_INPUT_REPORT];773 list_for_each_entry(rep, &rep_enum->report_list, list) {774 if (rep->application == 0xff000000)775 has_ff000000 = true;776 }777 if (!has_ff000000)778 return hid_hw_start(hdev, HID_CONNECT_DEFAULT);779 780 g15 = devm_kzalloc(&hdev->dev, sizeof(*g15), GFP_KERNEL);781 if (!g15)782 return -ENOMEM;783 784 mutex_init(&g15->mutex);785 786 input = devm_input_allocate_device(&hdev->dev);787 if (!input)788 return -ENOMEM;789 790 g15->hdev = hdev;791 g15->model = id->driver_data;792 g15->input = input;793 input_set_drvdata(input, hdev);794 hid_set_drvdata(hdev, (void *)g15);795 796 switch (g15->model) {797 case LG_G15:798 INIT_WORK(&g15->work, lg_g15_leds_changed_work);799 /*800 * The G15 and G15 v2 use a separate usb-device (on a builtin801 * hub) which emulates a keyboard for the F1 - F12 emulation802 * on the G-keys, which we disable, rendering the emulated kbd803 * non-functional, so we do not let hid-input connect.804 */805 connect_mask = HID_CONNECT_HIDRAW;806 gkeys_settings_output_report = 0x02;807 gkeys = 18;808 break;809 case LG_G15_V2:810 INIT_WORK(&g15->work, lg_g15_leds_changed_work);811 connect_mask = HID_CONNECT_HIDRAW;812 gkeys_settings_output_report = 0x02;813 gkeys = 6;814 break;815 case LG_G510:816 case LG_G510_USB_AUDIO:817 INIT_WORK(&g15->work, lg_g510_leds_sync_work);818 connect_mask = HID_CONNECT_HIDINPUT | HID_CONNECT_HIDRAW;819 gkeys_settings_feature_report = 0x01;820 gkeys = 18;821 break;822 case LG_Z10:823 connect_mask = HID_CONNECT_HIDRAW;824 break;825 }826 827 ret = hid_hw_start(hdev, connect_mask);828 if (ret)829 return ret;830 831 /* Tell the keyboard to stop sending F1-F12 + 1-6 for G1 - G18 */832 if (gkeys_settings_output_report) {833 g15->transfer_buf[0] = gkeys_settings_output_report;834 memset(g15->transfer_buf + 1, 0, gkeys);835 /*836 * The kbd ignores our output report if we do not queue837 * an URB on the USB input endpoint first...838 */839 ret = hid_hw_open(hdev);840 if (ret)841 goto error_hw_stop;842 ret = hid_hw_output_report(hdev, g15->transfer_buf, gkeys + 1);843 hid_hw_close(hdev);844 }845 846 if (gkeys_settings_feature_report) {847 g15->transfer_buf[0] = gkeys_settings_feature_report;848 memset(g15->transfer_buf + 1, 0, gkeys);849 ret = hid_hw_raw_request(g15->hdev,850 gkeys_settings_feature_report,851 g15->transfer_buf, gkeys + 1,852 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);853 }854 855 if (ret < 0) {856 hid_err(hdev, "Error %d disabling keyboard emulation for the G-keys, falling back to generic hid-input driver\n",857 ret);858 hid_set_drvdata(hdev, NULL);859 return 0;860 }861 862 /* Get initial brightness levels */863 ret = lg_g15_get_initial_led_brightness(g15);864 if (ret)865 goto error_hw_stop;866 867 if (g15->model == LG_Z10) {868 lg_g15_init_input_dev(hdev, g15->input, "Logitech Z-10 LCD Menu Keys");869 ret = input_register_device(g15->input);870 if (ret)871 goto error_hw_stop;872 873 ret = lg_g15_register_led(g15, 1, "z-10::lcd_backlight");874 if (ret)875 goto error_hw_stop;876 877 return 0; /* All done */878 }879 880 /* Setup and register input device */881 lg_g15_init_input_dev(hdev, input, "Logitech Gaming Keyboard Gaming Keys");882 883 /* G-keys */884 for (i = 0; i < gkeys; i++)885 input_set_capability(input, EV_KEY, KEY_MACRO1 + i);886 887 /* M1 - M3 and MR keys */888 for (i = 0; i < 3; i++)889 input_set_capability(input, EV_KEY, KEY_MACRO_PRESET1 + i);890 input_set_capability(input, EV_KEY, KEY_MACRO_RECORD_START);891 892 /*893 * On the G510 only report headphone and mic mute keys when *not* using894 * the builtin USB audio device. When the builtin audio is used these895 * keys directly toggle mute (and the LEDs) on/off.896 */897 if (g15->model == LG_G510) {898 input_set_capability(input, EV_KEY, KEY_MUTE);899 /* Userspace expects F20 for micmute */900 input_set_capability(input, EV_KEY, KEY_F20);901 }902 903 ret = input_register_device(input);904 if (ret)905 goto error_hw_stop;906 907 /* Register LED devices */908 for (i = 0; i < LG_G15_LED_MAX; i++) {909 ret = lg_g15_register_led(g15, i, led_names[i]);910 if (ret)911 goto error_hw_stop;912 }913 914 return 0;915 916error_hw_stop:917 hid_hw_stop(hdev);918 return ret;919}920 921static const struct hid_device_id lg_g15_devices[] = {922 /* The G11 is a G15 without the LCD, treat it as a G15 */923 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,924 USB_DEVICE_ID_LOGITECH_G11),925 .driver_data = LG_G15 },926 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,927 USB_DEVICE_ID_LOGITECH_G15_LCD),928 .driver_data = LG_G15 },929 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,930 USB_DEVICE_ID_LOGITECH_G15_V2_LCD),931 .driver_data = LG_G15_V2 },932 /* G510 without a headset plugged in */933 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,934 USB_DEVICE_ID_LOGITECH_G510),935 .driver_data = LG_G510 },936 /* G510 with headset plugged in / with extra USB audio interface */937 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,938 USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO),939 .driver_data = LG_G510_USB_AUDIO },940 /* Z-10 speakers */941 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,942 USB_DEVICE_ID_LOGITECH_Z_10_SPK),943 .driver_data = LG_Z10 },944 { }945};946MODULE_DEVICE_TABLE(hid, lg_g15_devices);947 948static struct hid_driver lg_g15_driver = {949 .name = "lg-g15",950 .id_table = lg_g15_devices,951 .raw_event = lg_g15_raw_event,952 .probe = lg_g15_probe,953};954module_hid_driver(lg_g15_driver);955 956MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");957MODULE_DESCRIPTION("HID driver for gaming keys on Logitech gaming keyboards");958MODULE_LICENSE("GPL");959