Skip to content

Commit ad4203f

Browse files
jwrdegoedeJiri Kosina
authored andcommitted
HID: lg-g15: Add support for the G510 keyboards' gaming keys
Add support for the gaming and LCD menu keys on the G510 keyboard. Note this commit does not add support for the keyboard and LCD backlight and the status LEDs, this is done in a follow up commit. Note the G510 generates 4 different input reports on its second (Consumer Keys) interface: -input report 1 is standard bootclass keyboard input report, mirroring normal keyboard interface -input report 2 is consumer page keys -input report 3 is gkeys, etc. -input report 4 is LED status, single byte, bits: bit 2: kbd and LCD backlight is *off* when set, toggled by the light key bit 3: headphone mute LED bit 4: mic mute LED Input-report 1 we ignore since this is a duplicate report from the first interface, report 2 is handled by the regular hid-input code. In this commit we add handling for input report 3. Signed-off-by: Hans de Goede <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent d5b5fc8 commit ad4203f

File tree

2 files changed

+124
-4
lines changed

2 files changed

+124
-4
lines changed

drivers/hid/hid-ids.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,8 @@
749749
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219
750750
#define USB_DEVICE_ID_LOGITECH_G15_LCD 0xc222
751751
#define USB_DEVICE_ID_LOGITECH_G15_V2_LCD 0xc227
752+
#define USB_DEVICE_ID_LOGITECH_G510 0xc22d
753+
#define USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO 0xc22e
752754
#define USB_DEVICE_ID_LOGITECH_G29_WHEEL 0xc24f
753755
#define USB_DEVICE_ID_LOGITECH_G920_WHEEL 0xc262
754756
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283

drivers/hid/hid-lg-g15.c

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
enum lg_g15_model {
2323
LG_G15,
2424
LG_G15_V2,
25+
LG_G510,
26+
LG_G510_USB_AUDIO,
2527
};
2628

2729
enum lg_g15_led_type {
@@ -51,12 +53,16 @@ struct lg_g15_data {
5153
struct hid_device *hdev;
5254
enum lg_g15_model model;
5355
struct lg_g15_led leds[LG_G15_LED_MAX];
56+
bool game_mode_enabled;
5457
};
5558

5659
static int lg_g15_update_led_brightness(struct lg_g15_data *g15)
5760
{
5861
int ret;
5962

63+
if (g15->model == LG_G510 || g15->model == LG_G510_USB_AUDIO)
64+
return 0;
65+
6066
ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT,
6167
g15->transfer_buf, 4,
6268
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
@@ -256,16 +262,73 @@ static int lg_g15_v2_event(struct lg_g15_data *g15, u8 *data, int size)
256262
return 0;
257263
}
258264

265+
static int lg_g510_event(struct lg_g15_data *g15, u8 *data, int size)
266+
{
267+
bool game_mode_enabled;
268+
int i, val;
269+
270+
/* G1 - G18 */
271+
for (i = 0; i < 18; i++) {
272+
val = data[i / 8 + 1] & (1 << (i % 8));
273+
input_report_key(g15->input, KEY_MACRO1 + i, val);
274+
}
275+
276+
/* Game mode on/off slider */
277+
game_mode_enabled = data[3] & 0x04;
278+
if (game_mode_enabled != g15->game_mode_enabled) {
279+
if (game_mode_enabled)
280+
hid_info(g15->hdev, "Game Mode enabled, Windows (super) key is disabled\n");
281+
else
282+
hid_info(g15->hdev, "Game Mode disabled\n");
283+
g15->game_mode_enabled = game_mode_enabled;
284+
}
285+
286+
/* M1 - M3 */
287+
for (i = 0; i < 3; i++) {
288+
val = data[3] & (0x10 << i);
289+
input_report_key(g15->input, KEY_MACRO_PRESET1 + i, val);
290+
}
291+
/* MR */
292+
input_report_key(g15->input, KEY_MACRO_RECORD_START, data[3] & 0x80);
293+
294+
/* LCD menu keys */
295+
for (i = 0; i < 5; i++) {
296+
val = data[4] & (1 << i);
297+
input_report_key(g15->input, KEY_KBD_LCD_MENU1 + i, val);
298+
}
299+
300+
/* Headphone Mute */
301+
input_report_key(g15->input, KEY_MUTE, data[4] & 0x20);
302+
/* Microphone Mute */
303+
input_report_key(g15->input, KEY_F20, data[4] & 0x40);
304+
305+
input_sync(g15->input);
306+
return 0;
307+
}
308+
259309
static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,
260310
u8 *data, int size)
261311
{
262312
struct lg_g15_data *g15 = hid_get_drvdata(hdev);
263313

264-
if (g15->model == LG_G15 && data[0] == 0x02 && size == 9)
265-
return lg_g15_event(g15, data, size);
314+
if (!g15)
315+
return 0;
266316

267-
if (g15->model == LG_G15_V2 && data[0] == 0x02 && size == 5)
268-
return lg_g15_v2_event(g15, data, size);
317+
switch (g15->model) {
318+
case LG_G15:
319+
if (data[0] == 0x02 && size == 9)
320+
return lg_g15_event(g15, data, size);
321+
break;
322+
case LG_G15_V2:
323+
if (data[0] == 0x02 && size == 5)
324+
return lg_g15_v2_event(g15, data, size);
325+
break;
326+
case LG_G510:
327+
case LG_G510_USB_AUDIO:
328+
if (data[0] == 0x03 && size == 5)
329+
return lg_g510_event(g15, data, size);
330+
break;
331+
}
269332

270333
return 0;
271334
}
@@ -312,15 +375,33 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i)
312375
static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
313376
{
314377
u8 gkeys_settings_output_report = 0;
378+
u8 gkeys_settings_feature_report = 0;
379+
struct hid_report_enum *rep_enum;
315380
unsigned int connect_mask = 0;
381+
bool has_ff000000 = false;
316382
struct lg_g15_data *g15;
317383
struct input_dev *input;
384+
struct hid_report *rep;
318385
int ret, i, gkeys = 0;
319386

387+
hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
388+
320389
ret = hid_parse(hdev);
321390
if (ret)
322391
return ret;
323392

393+
/*
394+
* Some models have multiple interfaces, we want the interface with
395+
* with the f000.0000 application input report.
396+
*/
397+
rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
398+
list_for_each_entry(rep, &rep_enum->report_list, list) {
399+
if (rep->application == 0xff000000)
400+
has_ff000000 = true;
401+
}
402+
if (!has_ff000000)
403+
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
404+
324405
g15 = devm_kzalloc(&hdev->dev, sizeof(*g15), GFP_KERNEL);
325406
if (!g15)
326407
return -ENOMEM;
@@ -353,6 +434,12 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
353434
gkeys_settings_output_report = 0x02;
354435
gkeys = 6;
355436
break;
437+
case LG_G510:
438+
case LG_G510_USB_AUDIO:
439+
connect_mask = HID_CONNECT_HIDINPUT | HID_CONNECT_HIDRAW;
440+
gkeys_settings_feature_report = 0x01;
441+
gkeys = 18;
442+
break;
356443
}
357444

358445
ret = hid_hw_start(hdev, connect_mask);
@@ -374,6 +461,15 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
374461
hid_hw_close(hdev);
375462
}
376463

464+
if (gkeys_settings_feature_report) {
465+
g15->transfer_buf[0] = gkeys_settings_feature_report;
466+
memset(g15->transfer_buf + 1, 0, gkeys);
467+
ret = hid_hw_raw_request(g15->hdev,
468+
gkeys_settings_feature_report,
469+
g15->transfer_buf, gkeys + 1,
470+
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
471+
}
472+
377473
if (ret < 0) {
378474
hid_err(hdev, "Error disabling keyboard emulation for the G-keys\n");
379475
goto error_hw_stop;
@@ -409,13 +505,27 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
409505
for (i = 0; i < 5; i++)
410506
input_set_capability(input, EV_KEY, KEY_KBD_LCD_MENU1 + i);
411507

508+
/*
509+
* On the G510 only report headphone and mic mute keys when *not* using
510+
* the builtin USB audio device. When the builtin audio is used these
511+
* keys directly toggle mute (and the LEDs) on/off.
512+
*/
513+
if (g15->model == LG_G510) {
514+
input_set_capability(input, EV_KEY, KEY_MUTE);
515+
/* Userspace expects F20 for micmute */
516+
input_set_capability(input, EV_KEY, KEY_F20);
517+
}
518+
412519
g15->input = input;
413520
input_set_drvdata(input, hdev);
414521

415522
ret = input_register_device(input);
416523
if (ret)
417524
goto error_hw_stop;
418525

526+
if (g15->model == LG_G510 || g15->model == LG_G510_USB_AUDIO)
527+
return 0;
528+
419529
/* Register LED devices */
420530
for (i = 0; i < LG_G15_LED_MAX; i++) {
421531
ret = lg_g15_register_led(g15, i);
@@ -437,6 +547,14 @@ static const struct hid_device_id lg_g15_devices[] = {
437547
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
438548
USB_DEVICE_ID_LOGITECH_G15_V2_LCD),
439549
.driver_data = LG_G15_V2 },
550+
/* G510 without a headset plugged in */
551+
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
552+
USB_DEVICE_ID_LOGITECH_G510),
553+
.driver_data = LG_G510 },
554+
/* G510 with headset plugged in / with extra USB audio interface */
555+
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
556+
USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO),
557+
.driver_data = LG_G510_USB_AUDIO },
440558
{ }
441559
};
442560
MODULE_DEVICE_TABLE(hid, lg_g15_devices);

0 commit comments

Comments
 (0)