blob: b2a08233f8d51318fd271a1969e1281381a52d37 [file] [log] [blame]
Hans de Goede3a027532019-08-28 14:41:25 +02001// 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/module.h>
11#include <linux/random.h>
12#include <linux/sched.h>
13#include <linux/usb.h>
14#include <linux/wait.h>
15
16#include "hid-ids.h"
17
18#define LG_G15_TRANSFER_BUF_SIZE 20
19
Hans de Goede97b741a2019-08-28 14:41:26 +020020#define LG_G15_FEATURE_REPORT 0x02
21
Hans de Goede1f8cde22019-08-28 14:41:29 +020022#define LG_G510_FEATURE_M_KEYS_LEDS 0x04
23#define LG_G510_FEATURE_BACKLIGHT_RGB 0x05
24#define LG_G510_FEATURE_POWER_ON_RGB 0x06
25
Hans de Goede3a027532019-08-28 14:41:25 +020026enum lg_g15_model {
27 LG_G15,
28 LG_G15_V2,
Hans de Goedead4203f2019-08-28 14:41:28 +020029 LG_G510,
30 LG_G510_USB_AUDIO,
Hans de Goedecbe5b6b2021-04-16 15:13:22 +020031 LG_Z10,
Hans de Goede3a027532019-08-28 14:41:25 +020032};
33
Hans de Goede97b741a2019-08-28 14:41:26 +020034enum lg_g15_led_type {
35 LG_G15_KBD_BRIGHTNESS,
36 LG_G15_LCD_BRIGHTNESS,
37 LG_G15_BRIGHTNESS_MAX,
Hans de Goeded5b5fc82019-08-28 14:41:27 +020038 LG_G15_MACRO_PRESET1 = 2,
39 LG_G15_MACRO_PRESET2,
40 LG_G15_MACRO_PRESET3,
41 LG_G15_MACRO_RECORD,
42 LG_G15_LED_MAX
Hans de Goede97b741a2019-08-28 14:41:26 +020043};
44
45struct lg_g15_led {
46 struct led_classdev cdev;
47 enum led_brightness brightness;
48 enum lg_g15_led_type led;
Hans de Goede1f8cde22019-08-28 14:41:29 +020049 u8 red, green, blue;
Hans de Goede97b741a2019-08-28 14:41:26 +020050};
51
Hans de Goede3a027532019-08-28 14:41:25 +020052struct lg_g15_data {
53 /* Must be first for proper dma alignment */
54 u8 transfer_buf[LG_G15_TRANSFER_BUF_SIZE];
Hans de Goede97b741a2019-08-28 14:41:26 +020055 /* Protects the transfer_buf and led brightness */
56 struct mutex mutex;
57 struct work_struct work;
Hans de Goede3a027532019-08-28 14:41:25 +020058 struct input_dev *input;
59 struct hid_device *hdev;
60 enum lg_g15_model model;
Hans de Goeded5b5fc82019-08-28 14:41:27 +020061 struct lg_g15_led leds[LG_G15_LED_MAX];
Hans de Goedead4203f2019-08-28 14:41:28 +020062 bool game_mode_enabled;
Hans de Goede3a027532019-08-28 14:41:25 +020063};
64
Hans de Goede1f8cde22019-08-28 14:41:29 +020065/******** G15 and G15 v2 LED functions ********/
66
Hans de Goede97b741a2019-08-28 14:41:26 +020067static int lg_g15_update_led_brightness(struct lg_g15_data *g15)
68{
69 int ret;
70
71 ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT,
72 g15->transfer_buf, 4,
73 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
74 if (ret != 4) {
75 hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret);
76 return (ret < 0) ? ret : -EIO;
77 }
78
79 g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = g15->transfer_buf[1];
80 g15->leds[LG_G15_LCD_BRIGHTNESS].brightness = g15->transfer_buf[2];
Hans de Goeded5b5fc82019-08-28 14:41:27 +020081
82 g15->leds[LG_G15_MACRO_PRESET1].brightness =
83 !(g15->transfer_buf[3] & 0x01);
84 g15->leds[LG_G15_MACRO_PRESET2].brightness =
85 !(g15->transfer_buf[3] & 0x02);
86 g15->leds[LG_G15_MACRO_PRESET3].brightness =
87 !(g15->transfer_buf[3] & 0x04);
88 g15->leds[LG_G15_MACRO_RECORD].brightness =
89 !(g15->transfer_buf[3] & 0x08);
90
Hans de Goede97b741a2019-08-28 14:41:26 +020091 return 0;
92}
93
94static enum led_brightness lg_g15_led_get(struct led_classdev *led_cdev)
95{
96 struct lg_g15_led *g15_led =
97 container_of(led_cdev, struct lg_g15_led, cdev);
98 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
99 enum led_brightness brightness;
100
101 mutex_lock(&g15->mutex);
102 lg_g15_update_led_brightness(g15);
103 brightness = g15->leds[g15_led->led].brightness;
104 mutex_unlock(&g15->mutex);
105
106 return brightness;
107}
108
109static int lg_g15_led_set(struct led_classdev *led_cdev,
110 enum led_brightness brightness)
111{
112 struct lg_g15_led *g15_led =
113 container_of(led_cdev, struct lg_g15_led, cdev);
114 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
Hans de Goeded5b5fc82019-08-28 14:41:27 +0200115 u8 val, mask = 0;
116 int i, ret;
Hans de Goede97b741a2019-08-28 14:41:26 +0200117
118 /* Ignore LED off on unregister / keyboard unplug */
119 if (led_cdev->flags & LED_UNREGISTERING)
120 return 0;
121
122 mutex_lock(&g15->mutex);
123
124 g15->transfer_buf[0] = LG_G15_FEATURE_REPORT;
Hans de Goede97b741a2019-08-28 14:41:26 +0200125 g15->transfer_buf[3] = 0;
126
Hans de Goeded5b5fc82019-08-28 14:41:27 +0200127 if (g15_led->led < LG_G15_BRIGHTNESS_MAX) {
128 g15->transfer_buf[1] = g15_led->led + 1;
129 g15->transfer_buf[2] = brightness << (g15_led->led * 4);
130 } else {
131 for (i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; i++) {
132 if (i == g15_led->led)
133 val = brightness;
134 else
135 val = g15->leds[i].brightness;
136
137 if (val)
138 mask |= 1 << (i - LG_G15_MACRO_PRESET1);
139 }
140
141 g15->transfer_buf[1] = 0x04;
142 g15->transfer_buf[2] = ~mask;
143 }
144
Hans de Goede97b741a2019-08-28 14:41:26 +0200145 ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT,
146 g15->transfer_buf, 4,
147 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
148 if (ret == 4) {
149 /* Success */
150 g15_led->brightness = brightness;
151 ret = 0;
152 } else {
153 hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);
154 ret = (ret < 0) ? ret : -EIO;
155 }
156
157 mutex_unlock(&g15->mutex);
158
159 return ret;
160}
161
162static void lg_g15_leds_changed_work(struct work_struct *work)
163{
164 struct lg_g15_data *g15 = container_of(work, struct lg_g15_data, work);
165 enum led_brightness old_brightness[LG_G15_BRIGHTNESS_MAX];
166 enum led_brightness brightness[LG_G15_BRIGHTNESS_MAX];
167 int i, ret;
168
169 mutex_lock(&g15->mutex);
170 for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++)
171 old_brightness[i] = g15->leds[i].brightness;
172
173 ret = lg_g15_update_led_brightness(g15);
174
175 for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++)
176 brightness[i] = g15->leds[i].brightness;
177 mutex_unlock(&g15->mutex);
178
179 if (ret)
180 return;
181
182 for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++) {
183 if (brightness[i] == old_brightness[i])
184 continue;
185
186 led_classdev_notify_brightness_hw_changed(&g15->leds[i].cdev,
187 brightness[i]);
188 }
189}
190
Hans de Goede1f8cde22019-08-28 14:41:29 +0200191/******** G510 LED functions ********/
192
193static int lg_g510_get_initial_led_brightness(struct lg_g15_data *g15, int i)
194{
195 int ret, high;
196
197 ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_BACKLIGHT_RGB + i,
198 g15->transfer_buf, 4,
199 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
200 if (ret != 4) {
201 hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret);
202 return (ret < 0) ? ret : -EIO;
203 }
204
205 high = max3(g15->transfer_buf[1], g15->transfer_buf[2],
206 g15->transfer_buf[3]);
207
208 if (high) {
209 g15->leds[i].red =
210 DIV_ROUND_CLOSEST(g15->transfer_buf[1] * 255, high);
211 g15->leds[i].green =
212 DIV_ROUND_CLOSEST(g15->transfer_buf[2] * 255, high);
213 g15->leds[i].blue =
214 DIV_ROUND_CLOSEST(g15->transfer_buf[3] * 255, high);
215 g15->leds[i].brightness = high;
216 } else {
217 g15->leds[i].red = 255;
218 g15->leds[i].green = 255;
219 g15->leds[i].blue = 255;
220 g15->leds[i].brightness = 0;
221 }
222
223 return 0;
224}
225
226/* Must be called with g15->mutex locked */
227static int lg_g510_kbd_led_write(struct lg_g15_data *g15,
228 struct lg_g15_led *g15_led,
229 enum led_brightness brightness)
230{
231 int ret;
232
233 g15->transfer_buf[0] = 5 + g15_led->led;
234 g15->transfer_buf[1] =
235 DIV_ROUND_CLOSEST(g15_led->red * brightness, 255);
236 g15->transfer_buf[2] =
237 DIV_ROUND_CLOSEST(g15_led->green * brightness, 255);
238 g15->transfer_buf[3] =
239 DIV_ROUND_CLOSEST(g15_led->blue * brightness, 255);
240
241 ret = hid_hw_raw_request(g15->hdev,
242 LG_G510_FEATURE_BACKLIGHT_RGB + g15_led->led,
243 g15->transfer_buf, 4,
244 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
245 if (ret == 4) {
246 /* Success */
247 g15_led->brightness = brightness;
248 ret = 0;
249 } else {
250 hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);
251 ret = (ret < 0) ? ret : -EIO;
252 }
253
254 return ret;
255}
256
257static int lg_g510_kbd_led_set(struct led_classdev *led_cdev,
258 enum led_brightness brightness)
259{
260 struct lg_g15_led *g15_led =
261 container_of(led_cdev, struct lg_g15_led, cdev);
262 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
263 int ret;
264
265 /* Ignore LED off on unregister / keyboard unplug */
266 if (led_cdev->flags & LED_UNREGISTERING)
267 return 0;
268
269 mutex_lock(&g15->mutex);
270 ret = lg_g510_kbd_led_write(g15, g15_led, brightness);
271 mutex_unlock(&g15->mutex);
272
273 return ret;
274}
275
276static enum led_brightness lg_g510_kbd_led_get(struct led_classdev *led_cdev)
277{
278 struct lg_g15_led *g15_led =
279 container_of(led_cdev, struct lg_g15_led, cdev);
280
281 return g15_led->brightness;
282}
283
284static ssize_t color_store(struct device *dev, struct device_attribute *attr,
285 const char *buf, size_t count)
286{
287 struct led_classdev *led_cdev = dev_get_drvdata(dev);
288 struct lg_g15_led *g15_led =
289 container_of(led_cdev, struct lg_g15_led, cdev);
290 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
291 unsigned long value;
292 int ret;
293
294 if (count < 7 || (count == 8 && buf[7] != '\n') || count > 8)
295 return -EINVAL;
296
297 if (buf[0] != '#')
298 return -EINVAL;
299
300 ret = kstrtoul(buf + 1, 16, &value);
301 if (ret)
302 return ret;
303
304 mutex_lock(&g15->mutex);
305 g15_led->red = (value & 0xff0000) >> 16;
306 g15_led->green = (value & 0x00ff00) >> 8;
307 g15_led->blue = (value & 0x0000ff);
308 ret = lg_g510_kbd_led_write(g15, g15_led, g15_led->brightness);
309 mutex_unlock(&g15->mutex);
310
311 return (ret < 0) ? ret : count;
312}
313
314static ssize_t color_show(struct device *dev, struct device_attribute *attr,
315 char *buf)
316{
317 struct led_classdev *led_cdev = dev_get_drvdata(dev);
318 struct lg_g15_led *g15_led =
319 container_of(led_cdev, struct lg_g15_led, cdev);
320 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
321 ssize_t ret;
322
323 mutex_lock(&g15->mutex);
324 ret = sprintf(buf, "#%02x%02x%02x\n",
325 g15_led->red, g15_led->green, g15_led->blue);
326 mutex_unlock(&g15->mutex);
327
328 return ret;
329}
330
331static DEVICE_ATTR_RW(color);
332
333static struct attribute *lg_g510_kbd_led_attrs[] = {
334 &dev_attr_color.attr,
335 NULL,
336};
337
338static const struct attribute_group lg_g510_kbd_led_group = {
339 .attrs = lg_g510_kbd_led_attrs,
340};
341
342static const struct attribute_group *lg_g510_kbd_led_groups[] = {
343 &lg_g510_kbd_led_group,
344 NULL,
345};
346
347static void lg_g510_leds_sync_work(struct work_struct *work)
348{
349 struct lg_g15_data *g15 = container_of(work, struct lg_g15_data, work);
350
351 mutex_lock(&g15->mutex);
352 lg_g510_kbd_led_write(g15, &g15->leds[LG_G15_KBD_BRIGHTNESS],
353 g15->leds[LG_G15_KBD_BRIGHTNESS].brightness);
354 mutex_unlock(&g15->mutex);
355}
356
Hans de Goede4682bb82019-08-28 14:41:30 +0200357static int lg_g510_update_mkey_led_brightness(struct lg_g15_data *g15)
358{
359 int ret;
360
361 ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_M_KEYS_LEDS,
362 g15->transfer_buf, 2,
363 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
364 if (ret != 2) {
365 hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret);
366 ret = (ret < 0) ? ret : -EIO;
367 }
368
369 g15->leds[LG_G15_MACRO_PRESET1].brightness =
370 !!(g15->transfer_buf[1] & 0x80);
371 g15->leds[LG_G15_MACRO_PRESET2].brightness =
372 !!(g15->transfer_buf[1] & 0x40);
373 g15->leds[LG_G15_MACRO_PRESET3].brightness =
374 !!(g15->transfer_buf[1] & 0x20);
375 g15->leds[LG_G15_MACRO_RECORD].brightness =
376 !!(g15->transfer_buf[1] & 0x10);
377
378 return 0;
379}
380
381static enum led_brightness lg_g510_mkey_led_get(struct led_classdev *led_cdev)
382{
383 struct lg_g15_led *g15_led =
384 container_of(led_cdev, struct lg_g15_led, cdev);
385 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
386 enum led_brightness brightness;
387
388 mutex_lock(&g15->mutex);
389 lg_g510_update_mkey_led_brightness(g15);
390 brightness = g15->leds[g15_led->led].brightness;
391 mutex_unlock(&g15->mutex);
392
393 return brightness;
394}
395
396static int lg_g510_mkey_led_set(struct led_classdev *led_cdev,
397 enum led_brightness brightness)
398{
399 struct lg_g15_led *g15_led =
400 container_of(led_cdev, struct lg_g15_led, cdev);
401 struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
402 u8 val, mask = 0;
403 int i, ret;
404
405 /* Ignore LED off on unregister / keyboard unplug */
406 if (led_cdev->flags & LED_UNREGISTERING)
407 return 0;
408
409 mutex_lock(&g15->mutex);
410
411 for (i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; i++) {
412 if (i == g15_led->led)
413 val = brightness;
414 else
415 val = g15->leds[i].brightness;
416
417 if (val)
418 mask |= 0x80 >> (i - LG_G15_MACRO_PRESET1);
419 }
420
421 g15->transfer_buf[0] = LG_G510_FEATURE_M_KEYS_LEDS;
422 g15->transfer_buf[1] = mask;
423
424 ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_M_KEYS_LEDS,
425 g15->transfer_buf, 2,
426 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
427 if (ret == 2) {
428 /* Success */
429 g15_led->brightness = brightness;
430 ret = 0;
431 } else {
432 hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);
433 ret = (ret < 0) ? ret : -EIO;
434 }
435
436 mutex_unlock(&g15->mutex);
437
438 return ret;
439}
440
Hans de Goede1f8cde22019-08-28 14:41:29 +0200441/******** Generic LED functions ********/
442static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15)
443{
444 int ret;
445
446 switch (g15->model) {
447 case LG_G15:
448 case LG_G15_V2:
449 return lg_g15_update_led_brightness(g15);
450 case LG_G510:
451 case LG_G510_USB_AUDIO:
452 ret = lg_g510_get_initial_led_brightness(g15, 0);
453 if (ret)
454 return ret;
455
456 ret = lg_g510_get_initial_led_brightness(g15, 1);
457 if (ret)
458 return ret;
459
Hans de Goede4682bb82019-08-28 14:41:30 +0200460 return lg_g510_update_mkey_led_brightness(g15);
Hans de Goedecbe5b6b2021-04-16 15:13:22 +0200461 case LG_Z10:
462 /*
463 * Getting the LCD backlight brightness is not supported.
464 * Reading Feature(2) fails with -EPIPE and this crashes
465 * the LCD and touch keys part of the speakers.
466 */
467 return 0;
Hans de Goede1f8cde22019-08-28 14:41:29 +0200468 }
469 return -EINVAL; /* Never reached */
470}
471
472/******** Input functions ********/
473
Hans de Goede3a027532019-08-28 14:41:25 +0200474/* On the G15 Mark I Logitech has been quite creative with which bit is what */
Hans de Goedeba3e0542021-04-16 15:13:19 +0200475static void lg_g15_handle_lcd_menu_keys(struct lg_g15_data *g15, u8 *data)
476{
477 int i, val;
478
479 /* Most left (round/display) button below the LCD */
480 input_report_key(g15->input, KEY_KBD_LCD_MENU1, data[8] & 0x80);
481 /* 4 other buttons below the LCD */
482 for (i = 0; i < 4; i++) {
483 val = data[i + 2] & 0x80;
484 input_report_key(g15->input, KEY_KBD_LCD_MENU2 + i, val);
485 }
486}
487
Hans de Goede042d05b2021-04-16 15:13:18 +0200488static int lg_g15_event(struct lg_g15_data *g15, u8 *data)
Hans de Goede3a027532019-08-28 14:41:25 +0200489{
490 int i, val;
491
492 /* G1 - G6 */
493 for (i = 0; i < 6; i++) {
494 val = data[i + 1] & (1 << i);
495 input_report_key(g15->input, KEY_MACRO1 + i, val);
496 }
497 /* G7 - G12 */
498 for (i = 0; i < 6; i++) {
499 val = data[i + 2] & (1 << i);
500 input_report_key(g15->input, KEY_MACRO7 + i, val);
501 }
502 /* G13 - G17 */
503 for (i = 0; i < 5; i++) {
504 val = data[i + 1] & (4 << i);
505 input_report_key(g15->input, KEY_MACRO13 + i, val);
506 }
507 /* G18 */
508 input_report_key(g15->input, KEY_MACRO18, data[8] & 0x40);
509
510 /* M1 - M3 */
511 for (i = 0; i < 3; i++) {
512 val = data[i + 6] & (1 << i);
513 input_report_key(g15->input, KEY_MACRO_PRESET1 + i, val);
514 }
515 /* MR */
516 input_report_key(g15->input, KEY_MACRO_RECORD_START, data[7] & 0x40);
517
Hans de Goedeba3e0542021-04-16 15:13:19 +0200518 lg_g15_handle_lcd_menu_keys(g15, data);
Hans de Goede3a027532019-08-28 14:41:25 +0200519
Hans de Goede97b741a2019-08-28 14:41:26 +0200520 /* Backlight cycle button pressed? */
521 if (data[1] & 0x80)
522 schedule_work(&g15->work);
523
Hans de Goede3a027532019-08-28 14:41:25 +0200524 input_sync(g15->input);
525 return 0;
526}
527
Hans de Goede042d05b2021-04-16 15:13:18 +0200528static int lg_g15_v2_event(struct lg_g15_data *g15, u8 *data)
Hans de Goede3a027532019-08-28 14:41:25 +0200529{
530 int i, val;
531
532 /* G1 - G6 */
533 for (i = 0; i < 6; i++) {
534 val = data[1] & (1 << i);
535 input_report_key(g15->input, KEY_MACRO1 + i, val);
536 }
537
538 /* M1 - M3 + MR */
539 input_report_key(g15->input, KEY_MACRO_PRESET1, data[1] & 0x40);
540 input_report_key(g15->input, KEY_MACRO_PRESET2, data[1] & 0x80);
541 input_report_key(g15->input, KEY_MACRO_PRESET3, data[2] & 0x20);
542 input_report_key(g15->input, KEY_MACRO_RECORD_START, data[2] & 0x40);
543
544 /* Round button to the left of the LCD */
545 input_report_key(g15->input, KEY_KBD_LCD_MENU1, data[2] & 0x80);
546 /* 4 buttons below the LCD */
547 for (i = 0; i < 4; i++) {
548 val = data[2] & (2 << i);
549 input_report_key(g15->input, KEY_KBD_LCD_MENU2 + i, val);
550 }
551
Hans de Goede97b741a2019-08-28 14:41:26 +0200552 /* Backlight cycle button pressed? */
553 if (data[2] & 0x01)
554 schedule_work(&g15->work);
555
Hans de Goede3a027532019-08-28 14:41:25 +0200556 input_sync(g15->input);
557 return 0;
558}
559
Hans de Goede042d05b2021-04-16 15:13:18 +0200560static int lg_g510_event(struct lg_g15_data *g15, u8 *data)
Hans de Goedead4203f2019-08-28 14:41:28 +0200561{
562 bool game_mode_enabled;
563 int i, val;
564
565 /* G1 - G18 */
566 for (i = 0; i < 18; i++) {
567 val = data[i / 8 + 1] & (1 << (i % 8));
568 input_report_key(g15->input, KEY_MACRO1 + i, val);
569 }
570
571 /* Game mode on/off slider */
572 game_mode_enabled = data[3] & 0x04;
573 if (game_mode_enabled != g15->game_mode_enabled) {
574 if (game_mode_enabled)
575 hid_info(g15->hdev, "Game Mode enabled, Windows (super) key is disabled\n");
576 else
577 hid_info(g15->hdev, "Game Mode disabled\n");
578 g15->game_mode_enabled = game_mode_enabled;
579 }
580
581 /* M1 - M3 */
582 for (i = 0; i < 3; i++) {
583 val = data[3] & (0x10 << i);
584 input_report_key(g15->input, KEY_MACRO_PRESET1 + i, val);
585 }
586 /* MR */
587 input_report_key(g15->input, KEY_MACRO_RECORD_START, data[3] & 0x80);
588
589 /* LCD menu keys */
590 for (i = 0; i < 5; i++) {
591 val = data[4] & (1 << i);
592 input_report_key(g15->input, KEY_KBD_LCD_MENU1 + i, val);
593 }
594
595 /* Headphone Mute */
596 input_report_key(g15->input, KEY_MUTE, data[4] & 0x20);
597 /* Microphone Mute */
598 input_report_key(g15->input, KEY_F20, data[4] & 0x40);
599
600 input_sync(g15->input);
601 return 0;
602}
603
Hans de Goede042d05b2021-04-16 15:13:18 +0200604static int lg_g510_leds_event(struct lg_g15_data *g15, u8 *data)
Hans de Goede1f8cde22019-08-28 14:41:29 +0200605{
606 bool backlight_disabled;
607
608 /*
609 * The G510 ignores backlight updates when the backlight is turned off
610 * through the light toggle button on the keyboard, to work around this
611 * we queue a workitem to sync values when the backlight is turned on.
612 */
613 backlight_disabled = data[1] & 0x04;
614 if (!backlight_disabled)
615 schedule_work(&g15->work);
616
617 return 0;
618}
619
Hans de Goede3a027532019-08-28 14:41:25 +0200620static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,
621 u8 *data, int size)
622{
623 struct lg_g15_data *g15 = hid_get_drvdata(hdev);
624
Hans de Goedead4203f2019-08-28 14:41:28 +0200625 if (!g15)
626 return 0;
Hans de Goede3a027532019-08-28 14:41:25 +0200627
Hans de Goedead4203f2019-08-28 14:41:28 +0200628 switch (g15->model) {
629 case LG_G15:
630 if (data[0] == 0x02 && size == 9)
Hans de Goede042d05b2021-04-16 15:13:18 +0200631 return lg_g15_event(g15, data);
Hans de Goedead4203f2019-08-28 14:41:28 +0200632 break;
633 case LG_G15_V2:
634 if (data[0] == 0x02 && size == 5)
Hans de Goede042d05b2021-04-16 15:13:18 +0200635 return lg_g15_v2_event(g15, data);
Hans de Goedead4203f2019-08-28 14:41:28 +0200636 break;
Hans de Goedecbe5b6b2021-04-16 15:13:22 +0200637 case LG_Z10:
638 if (data[0] == 0x02 && size == 9) {
639 lg_g15_handle_lcd_menu_keys(g15, data);
640 input_sync(g15->input);
641 }
642 break;
Hans de Goedead4203f2019-08-28 14:41:28 +0200643 case LG_G510:
644 case LG_G510_USB_AUDIO:
645 if (data[0] == 0x03 && size == 5)
Hans de Goede042d05b2021-04-16 15:13:18 +0200646 return lg_g510_event(g15, data);
Hans de Goede1f8cde22019-08-28 14:41:29 +0200647 if (data[0] == 0x04 && size == 2)
Hans de Goede042d05b2021-04-16 15:13:18 +0200648 return lg_g510_leds_event(g15, data);
Hans de Goedead4203f2019-08-28 14:41:28 +0200649 break;
650 }
Hans de Goede3a027532019-08-28 14:41:25 +0200651
652 return 0;
653}
654
655static int lg_g15_input_open(struct input_dev *dev)
656{
657 struct hid_device *hdev = input_get_drvdata(dev);
658
659 return hid_hw_open(hdev);
660}
661
662static void lg_g15_input_close(struct input_dev *dev)
663{
664 struct hid_device *hdev = input_get_drvdata(dev);
665
666 hid_hw_close(hdev);
667}
668
Hans de Goede1e5c2292021-04-16 15:13:21 +0200669static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name)
Hans de Goede97b741a2019-08-28 14:41:26 +0200670{
Hans de Goede97b741a2019-08-28 14:41:26 +0200671 g15->leds[i].led = i;
Hans de Goede1e5c2292021-04-16 15:13:21 +0200672 g15->leds[i].cdev.name = name;
Hans de Goede1f8cde22019-08-28 14:41:29 +0200673
674 switch (g15->model) {
675 case LG_G15:
676 case LG_G15_V2:
Hans de Goede1f8cde22019-08-28 14:41:29 +0200677 g15->leds[i].cdev.brightness_get = lg_g15_led_get;
Hans de Goedecbe5b6b2021-04-16 15:13:22 +0200678 fallthrough;
679 case LG_Z10:
680 g15->leds[i].cdev.brightness_set_blocking = lg_g15_led_set;
Hans de Goede1f8cde22019-08-28 14:41:29 +0200681 if (i < LG_G15_BRIGHTNESS_MAX) {
682 g15->leds[i].cdev.flags = LED_BRIGHT_HW_CHANGED;
683 g15->leds[i].cdev.max_brightness = 2;
684 } else {
685 g15->leds[i].cdev.max_brightness = 1;
686 }
687 break;
688 case LG_G510:
689 case LG_G510_USB_AUDIO:
690 switch (i) {
691 case LG_G15_LCD_BRIGHTNESS:
692 /*
693 * The G510 does not have a separate LCD brightness,
694 * but it does have a separate power-on (reset) value.
695 */
696 g15->leds[i].cdev.name = "g15::power_on_backlight_val";
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -0500697 fallthrough;
Hans de Goede1f8cde22019-08-28 14:41:29 +0200698 case LG_G15_KBD_BRIGHTNESS:
699 g15->leds[i].cdev.brightness_set_blocking =
700 lg_g510_kbd_led_set;
701 g15->leds[i].cdev.brightness_get =
702 lg_g510_kbd_led_get;
703 g15->leds[i].cdev.max_brightness = 255;
704 g15->leds[i].cdev.groups = lg_g510_kbd_led_groups;
705 break;
706 default:
Hans de Goede4682bb82019-08-28 14:41:30 +0200707 g15->leds[i].cdev.brightness_set_blocking =
708 lg_g510_mkey_led_set;
709 g15->leds[i].cdev.brightness_get =
710 lg_g510_mkey_led_get;
711 g15->leds[i].cdev.max_brightness = 1;
Hans de Goede1f8cde22019-08-28 14:41:29 +0200712 }
713 break;
Hans de Goeded5b5fc82019-08-28 14:41:27 +0200714 }
Hans de Goede97b741a2019-08-28 14:41:26 +0200715
716 return devm_led_classdev_register(&g15->hdev->dev, &g15->leds[i].cdev);
717}
718
Hans de Goede614d34f2021-04-16 15:13:20 +0200719/* Common input device init code shared between keyboards and Z-10 speaker handling */
720static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *input,
721 const char *name)
722{
723 int i;
724
725 input->name = name;
726 input->phys = hdev->phys;
727 input->uniq = hdev->uniq;
728 input->id.bustype = hdev->bus;
729 input->id.vendor = hdev->vendor;
730 input->id.product = hdev->product;
731 input->id.version = hdev->version;
732 input->dev.parent = &hdev->dev;
733 input->open = lg_g15_input_open;
734 input->close = lg_g15_input_close;
735
736 /* Keys below the LCD, intended for controlling a menu on the LCD */
737 for (i = 0; i < 5; i++)
738 input_set_capability(input, EV_KEY, KEY_KBD_LCD_MENU1 + i);
739}
740
Hans de Goede3a027532019-08-28 14:41:25 +0200741static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
742{
Hans de Goede1e5c2292021-04-16 15:13:21 +0200743 static const char * const led_names[] = {
744 "g15::kbd_backlight",
745 "g15::lcd_backlight",
746 "g15::macro_preset1",
747 "g15::macro_preset2",
748 "g15::macro_preset3",
749 "g15::macro_record",
750 };
Hans de Goede3a027532019-08-28 14:41:25 +0200751 u8 gkeys_settings_output_report = 0;
Hans de Goedead4203f2019-08-28 14:41:28 +0200752 u8 gkeys_settings_feature_report = 0;
753 struct hid_report_enum *rep_enum;
Hans de Goede3a027532019-08-28 14:41:25 +0200754 unsigned int connect_mask = 0;
Hans de Goedead4203f2019-08-28 14:41:28 +0200755 bool has_ff000000 = false;
Hans de Goede3a027532019-08-28 14:41:25 +0200756 struct lg_g15_data *g15;
757 struct input_dev *input;
Hans de Goedead4203f2019-08-28 14:41:28 +0200758 struct hid_report *rep;
Hans de Goede3a027532019-08-28 14:41:25 +0200759 int ret, i, gkeys = 0;
760
Hans de Goedead4203f2019-08-28 14:41:28 +0200761 hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
762
Hans de Goede3a027532019-08-28 14:41:25 +0200763 ret = hid_parse(hdev);
764 if (ret)
765 return ret;
766
Hans de Goedead4203f2019-08-28 14:41:28 +0200767 /*
768 * Some models have multiple interfaces, we want the interface with
769 * with the f000.0000 application input report.
770 */
771 rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
772 list_for_each_entry(rep, &rep_enum->report_list, list) {
773 if (rep->application == 0xff000000)
774 has_ff000000 = true;
775 }
776 if (!has_ff000000)
777 return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
778
Hans de Goede3a027532019-08-28 14:41:25 +0200779 g15 = devm_kzalloc(&hdev->dev, sizeof(*g15), GFP_KERNEL);
780 if (!g15)
781 return -ENOMEM;
782
Hans de Goede97b741a2019-08-28 14:41:26 +0200783 mutex_init(&g15->mutex);
784
Hans de Goede3a027532019-08-28 14:41:25 +0200785 input = devm_input_allocate_device(&hdev->dev);
786 if (!input)
787 return -ENOMEM;
788
789 g15->hdev = hdev;
790 g15->model = id->driver_data;
Hans de Goede614d34f2021-04-16 15:13:20 +0200791 g15->input = input;
792 input_set_drvdata(input, hdev);
Hans de Goede3a027532019-08-28 14:41:25 +0200793 hid_set_drvdata(hdev, (void *)g15);
794
795 switch (g15->model) {
796 case LG_G15:
Hans de Goede1f8cde22019-08-28 14:41:29 +0200797 INIT_WORK(&g15->work, lg_g15_leds_changed_work);
Hans de Goede3a027532019-08-28 14:41:25 +0200798 /*
799 * The G15 and G15 v2 use a separate usb-device (on a builtin
800 * hub) which emulates a keyboard for the F1 - F12 emulation
801 * on the G-keys, which we disable, rendering the emulated kbd
802 * non-functional, so we do not let hid-input connect.
803 */
804 connect_mask = HID_CONNECT_HIDRAW;
805 gkeys_settings_output_report = 0x02;
806 gkeys = 18;
807 break;
808 case LG_G15_V2:
Hans de Goede1f8cde22019-08-28 14:41:29 +0200809 INIT_WORK(&g15->work, lg_g15_leds_changed_work);
Hans de Goede3a027532019-08-28 14:41:25 +0200810 connect_mask = HID_CONNECT_HIDRAW;
811 gkeys_settings_output_report = 0x02;
812 gkeys = 6;
813 break;
Hans de Goedead4203f2019-08-28 14:41:28 +0200814 case LG_G510:
815 case LG_G510_USB_AUDIO:
Hans de Goede1f8cde22019-08-28 14:41:29 +0200816 INIT_WORK(&g15->work, lg_g510_leds_sync_work);
Hans de Goedead4203f2019-08-28 14:41:28 +0200817 connect_mask = HID_CONNECT_HIDINPUT | HID_CONNECT_HIDRAW;
818 gkeys_settings_feature_report = 0x01;
819 gkeys = 18;
820 break;
Hans de Goedecbe5b6b2021-04-16 15:13:22 +0200821 case LG_Z10:
822 connect_mask = HID_CONNECT_HIDRAW;
823 break;
Hans de Goede3a027532019-08-28 14:41:25 +0200824 }
825
826 ret = hid_hw_start(hdev, connect_mask);
827 if (ret)
828 return ret;
829
830 /* Tell the keyboard to stop sending F1-F12 + 1-6 for G1 - G18 */
831 if (gkeys_settings_output_report) {
832 g15->transfer_buf[0] = gkeys_settings_output_report;
833 memset(g15->transfer_buf + 1, 0, gkeys);
834 /*
835 * The kbd ignores our output report if we do not queue
836 * an URB on the USB input endpoint first...
837 */
838 ret = hid_hw_open(hdev);
839 if (ret)
840 goto error_hw_stop;
841 ret = hid_hw_output_report(hdev, g15->transfer_buf, gkeys + 1);
842 hid_hw_close(hdev);
843 }
844
Hans de Goedead4203f2019-08-28 14:41:28 +0200845 if (gkeys_settings_feature_report) {
846 g15->transfer_buf[0] = gkeys_settings_feature_report;
847 memset(g15->transfer_buf + 1, 0, gkeys);
848 ret = hid_hw_raw_request(g15->hdev,
849 gkeys_settings_feature_report,
850 g15->transfer_buf, gkeys + 1,
851 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
852 }
853
Hans de Goede3a027532019-08-28 14:41:25 +0200854 if (ret < 0) {
Hans de Goedeb8a75ea2020-03-15 18:34:49 +0100855 hid_err(hdev, "Error %d disabling keyboard emulation for the G-keys, falling back to generic hid-input driver\n",
856 ret);
857 hid_set_drvdata(hdev, NULL);
858 return 0;
Hans de Goede3a027532019-08-28 14:41:25 +0200859 }
860
Hans de Goede97b741a2019-08-28 14:41:26 +0200861 /* Get initial brightness levels */
Hans de Goede1f8cde22019-08-28 14:41:29 +0200862 ret = lg_g15_get_initial_led_brightness(g15);
Hans de Goede97b741a2019-08-28 14:41:26 +0200863 if (ret)
864 goto error_hw_stop;
865
Hans de Goedecbe5b6b2021-04-16 15:13:22 +0200866 if (g15->model == LG_Z10) {
867 lg_g15_init_input_dev(hdev, g15->input, "Logitech Z-10 LCD Menu Keys");
868 ret = input_register_device(g15->input);
869 if (ret)
870 goto error_hw_stop;
871
872 ret = lg_g15_register_led(g15, 1, "z-10::lcd_backlight");
873 if (ret)
874 goto error_hw_stop;
875
876 return 0; /* All done */
877 }
878
Hans de Goede97b741a2019-08-28 14:41:26 +0200879 /* Setup and register input device */
Hans de Goede614d34f2021-04-16 15:13:20 +0200880 lg_g15_init_input_dev(hdev, input, "Logitech Gaming Keyboard Gaming Keys");
Hans de Goede3a027532019-08-28 14:41:25 +0200881
882 /* G-keys */
883 for (i = 0; i < gkeys; i++)
884 input_set_capability(input, EV_KEY, KEY_MACRO1 + i);
885
886 /* M1 - M3 and MR keys */
887 for (i = 0; i < 3; i++)
888 input_set_capability(input, EV_KEY, KEY_MACRO_PRESET1 + i);
889 input_set_capability(input, EV_KEY, KEY_MACRO_RECORD_START);
890
Hans de Goedead4203f2019-08-28 14:41:28 +0200891 /*
892 * On the G510 only report headphone and mic mute keys when *not* using
893 * the builtin USB audio device. When the builtin audio is used these
894 * keys directly toggle mute (and the LEDs) on/off.
895 */
896 if (g15->model == LG_G510) {
897 input_set_capability(input, EV_KEY, KEY_MUTE);
898 /* Userspace expects F20 for micmute */
899 input_set_capability(input, EV_KEY, KEY_F20);
900 }
901
Hans de Goede3a027532019-08-28 14:41:25 +0200902 ret = input_register_device(input);
903 if (ret)
904 goto error_hw_stop;
905
Hans de Goede97b741a2019-08-28 14:41:26 +0200906 /* Register LED devices */
Hans de Goeded5b5fc82019-08-28 14:41:27 +0200907 for (i = 0; i < LG_G15_LED_MAX; i++) {
Hans de Goede1e5c2292021-04-16 15:13:21 +0200908 ret = lg_g15_register_led(g15, i, led_names[i]);
Hans de Goede97b741a2019-08-28 14:41:26 +0200909 if (ret)
910 goto error_hw_stop;
911 }
912
Hans de Goede3a027532019-08-28 14:41:25 +0200913 return 0;
914
915error_hw_stop:
916 hid_hw_stop(hdev);
917 return ret;
918}
919
920static const struct hid_device_id lg_g15_devices[] = {
Fabian Schindlatzb1bd0f72020-04-13 18:46:28 +0200921 /* The G11 is a G15 without the LCD, treat it as a G15 */
922 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
923 USB_DEVICE_ID_LOGITECH_G11),
924 .driver_data = LG_G15 },
Hans de Goede3a027532019-08-28 14:41:25 +0200925 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
926 USB_DEVICE_ID_LOGITECH_G15_LCD),
927 .driver_data = LG_G15 },
928 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
929 USB_DEVICE_ID_LOGITECH_G15_V2_LCD),
930 .driver_data = LG_G15_V2 },
Hans de Goedead4203f2019-08-28 14:41:28 +0200931 /* G510 without a headset plugged in */
932 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
933 USB_DEVICE_ID_LOGITECH_G510),
934 .driver_data = LG_G510 },
935 /* G510 with headset plugged in / with extra USB audio interface */
936 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
937 USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO),
938 .driver_data = LG_G510_USB_AUDIO },
Hans de Goedecbe5b6b2021-04-16 15:13:22 +0200939 /* Z-10 speakers */
940 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
941 USB_DEVICE_ID_LOGITECH_Z_10_SPK),
942 .driver_data = LG_Z10 },
Hans de Goede3a027532019-08-28 14:41:25 +0200943 { }
944};
945MODULE_DEVICE_TABLE(hid, lg_g15_devices);
946
947static struct hid_driver lg_g15_driver = {
948 .name = "lg-g15",
949 .id_table = lg_g15_devices,
950 .raw_event = lg_g15_raw_event,
951 .probe = lg_g15_probe,
952};
953module_hid_driver(lg_g15_driver);
954
Hans de Goedeb413c592021-04-16 15:13:23 +0200955MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
Hans de Goede3a027532019-08-28 14:41:25 +0200956MODULE_LICENSE("GPL");