* [PATCH v2 0/2] HID: lg-g15: G15 v2 contrast and game-mode LEDs
@ 2026-11-09 17:17 Torstein Eide
2026-11-09 17:17 ` [PATCH v2 1/2] HID: lg-g15: add LCD contrast LED Torstein Eide
2026-11-09 17:17 ` [PATCH v2 2/2] HID: lg-g15: report Super-key-lock state on G15 v2's keyboard interface Torstein Eide
0 siblings, 2 replies; 3+ messages in thread
From: Torstein Eide @ 2026-11-09 17:17 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: Hans de Goede, Maoyi Xie, linux-input, linux-kernel
This series adds two LEDs for the Logitech G15 v2 to hid-lg-g15.
Patch 1 exposes the GamePanel LCD contrast as a g15::lcd_contrast LED.
Patch 2 binds the G15 v2's keyboard device (046D:C226), which is
currently handled by hid-generic, and exposes the Super-key-lock
("Game Mode") state as a read-only g15::game_mode LED. Normal key input
on that device is left to hid-input, with the same
HID_QUIRK_INPUT_PER_APP setup hid-generic uses.
Tested on a Logitech G15 v2, together with lcdproc (g15 driver, hidraw
mode) driving the LCD: the LCD output, the contrast LED and the L1-L5
keys all keep working, and the c226 input devices have the same names
as under hid-generic. Also tested across a suspend/resume cycle.
Changes in v2:
- Drop the framebuffer patch; userspace (lcdproc, libg15) drives the
LCD through hidraw instead.
- Drop the remove-path patch: hid-core holds driver_input_lock during
remove(), so raw_event cannot reschedule the work.
- Contrast LED: clamp the brightness before indexing contrast_values[],
and restore the contrast on resume under g15->mutex.
- Game-mode LED: set HID_QUIRK_INPUT_PER_APP like hid-generic, read the
initial lock state before registering the LED, and guard the cached
state with a spinlock so a resume-time GET_REPORT cannot overwrite a
newer input report.
Torstein Eide (2):
HID: lg-g15: add LCD contrast LED
HID: lg-g15: report Super-key-lock state on G15 v2's keyboard
interface
drivers/hid/hid-ids.h | 1 +
drivers/hid/hid-lg-g15.c | 347 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 343 insertions(+), 5 deletions(-)
base-commit: 2c2fe95ab19b09bef7391e3e5cdf8146d09792b6
--
2.53.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v2 1/2] HID: lg-g15: add LCD contrast LED
2026-11-09 17:17 [PATCH v2 0/2] HID: lg-g15: G15 v2 contrast and game-mode LEDs Torstein Eide
@ 2026-11-09 17:17 ` Torstein Eide
2026-11-09 17:17 ` [PATCH v2 2/2] HID: lg-g15: report Super-key-lock state on G15 v2's keyboard interface Torstein Eide
1 sibling, 0 replies; 3+ messages in thread
From: Torstein Eide @ 2026-11-09 17:17 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: Hans de Goede, Maoyi Xie, linux-input, linux-kernel
Wire values ({2, 32, 129, value}) match libg15's setLCDContrast()
byte-for-byte.
Tested on Logitech G15v2.
Based on libg15.c.
Signed-off-by: Torstein Eide <torsteine+linux@gmail.com>
Based-on-work-by: Philip Lawatsch <philip@lawatsch.at>
Based-on-work-by: Alex Ibrado <alex@ibrado.org>
Based-on-work-by: Anthony J. Mirabella <mirabeaj@gmail.com>
Based-on-work-by: Mike Lampard <mlampard@users.sourceforge.net>
Assisted-by: Claude:claude-sonnet-5
---
drivers/hid/hid-lg-g15.c | 119 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 117 insertions(+), 2 deletions(-)
diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c
index 02ef3e2094b4..3827c66ca3a9 100644
--- a/drivers/hid/hid-lg-g15.c
+++ b/drivers/hid/hid-lg-g15.c
@@ -59,7 +59,16 @@ enum lg_g15_led_type {
LG_G15_MACRO_PRESET2,
LG_G15_MACRO_PRESET3,
LG_G15_MACRO_RECORD,
- LG_G15_LED_MAX
+ LG_G15_LED_MAX,
+ /*
+ * LCD contrast uses a different feature-report sub-format than the
+ * brightness/macro fields above (see lg_g15_contrast_set()) and has
+ * no hardware readback, so it lives outside [0, LG_G15_LED_MAX) to
+ * avoid being swept into the G13/macro loops bounded by
+ * LG_G15_LED_MAX. G15/G15 v2 only; registered explicitly.
+ */
+ LG_G15_LCD_CONTRAST = LG_G15_LED_MAX,
+ LG_G15_LED_ARRAY_SIZE
};
struct g13_input_report {
@@ -89,9 +98,10 @@ struct lg_g15_data {
struct input_dev *input_js; /* Separate joystick device for G13. */
struct hid_device *hdev;
enum lg_g15_model model;
- struct lg_g15_led leds[LG_G15_LED_MAX];
+ struct lg_g15_led leds[LG_G15_LED_ARRAY_SIZE];
bool game_mode_enabled;
bool backlight_disabled; /* true == HW backlight toggled *OFF* */
+ bool lcd_contrast_set; /* LCD contrast written at least once */
};
/********* G13 LED functions ***********/
@@ -365,6 +375,69 @@ static int lg_g15_led_set(struct led_classdev *led_cdev,
return ret;
}
+/*
+ * LCD contrast, report 2 field 32: {2, 32, 129, value}, value in
+ * {18, 22, 26} for low/medium/high. Distinct sub-format from the
+ * brightness/macro fields (value sits in byte 3, not byte 2, and byte 2 is
+ * a fixed marker rather than the field selector) and has no GET_REPORT
+ * readback, so brightness_get just returns the last value we set.
+ */
+/* Must be called with g15->mutex held */
+static int lg_g15_contrast_write(struct lg_g15_data *g15,
+ enum led_brightness brightness)
+{
+ static const u8 contrast_values[] = { 18, 22, 26 };
+ int ret;
+
+ /*
+ * The LED core does not clamp to max_brightness on every path (a
+ * delayed set passes the raw sysfs value), so clamp here.
+ */
+ brightness = min_t(enum led_brightness, brightness,
+ ARRAY_SIZE(contrast_values) - 1);
+
+ g15->transfer_buf[0] = LG_G15_FEATURE_REPORT;
+ g15->transfer_buf[1] = 32;
+ g15->transfer_buf[2] = 129;
+ g15->transfer_buf[3] = contrast_values[brightness];
+
+ ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT,
+ g15->transfer_buf, 4,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret != 4) {
+ hid_err(g15->hdev, "Error setting LCD contrast: %d\n", ret);
+ return (ret < 0) ? ret : -EIO;
+ }
+
+ g15->leds[LG_G15_LCD_CONTRAST].brightness = brightness;
+ g15->lcd_contrast_set = true;
+ return 0;
+}
+
+static int lg_g15_contrast_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
+ int ret;
+
+ if (led_cdev->flags & LED_UNREGISTERING)
+ return 0;
+
+ mutex_lock(&g15->mutex);
+ ret = lg_g15_contrast_write(g15, brightness);
+ mutex_unlock(&g15->mutex);
+
+ return ret;
+}
+
+static enum led_brightness lg_g15_contrast_get(struct led_classdev *led_cdev)
+{
+ struct lg_g15_led *g15_led =
+ container_of(led_cdev, struct lg_g15_led, cdev);
+
+ return g15_led->brightness;
+}
+
static void lg_g15_leds_changed_work(struct work_struct *work)
{
struct lg_g15_data *g15 = container_of(work, struct lg_g15_data, work);
@@ -1017,6 +1090,13 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name)
break;
case LG_G15:
case LG_G15_V2:
+ if (i == LG_G15_LCD_CONTRAST) {
+ g15->leds[i].cdev.brightness_set_blocking = lg_g15_contrast_set;
+ g15->leds[i].cdev.brightness_get = lg_g15_contrast_get;
+ g15->leds[i].cdev.max_brightness = 2;
+ ret = devm_led_classdev_register(&g15->hdev->dev, &g15->leds[i].cdev);
+ break;
+ }
g15->leds[i].cdev.brightness_get = lg_g15_led_get;
fallthrough;
case LG_Z10:
@@ -1333,6 +1413,14 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto error_hw_stop;
}
+ /* The G11 shares the LG_G15 model but has no LCD */
+ if ((g15->model == LG_G15 && hdev->product != USB_DEVICE_ID_LOGITECH_G11) ||
+ g15->model == LG_G15_V2) {
+ ret = lg_g15_register_led(g15, LG_G15_LCD_CONTRAST, "g15::lcd_contrast");
+ if (ret)
+ goto error_hw_stop;
+ }
+
return 0;
error_hw_stop:
@@ -1389,12 +1477,39 @@ static void lg_g15_remove(struct hid_device *hdev)
hid_hw_stop(hdev);
}
+/*
+ * The LCD does not retain its contrast across a USB reset or power loss;
+ * re-apply the last value userspace set, if any.
+ */
+static int lg_g15_resume(struct hid_device *hdev)
+{
+ struct lg_g15_data *g15 = hid_get_drvdata(hdev);
+ struct lg_g15_led *contrast;
+
+ if (!g15)
+ return 0;
+
+ contrast = &g15->leds[LG_G15_LCD_CONTRAST];
+ if (!contrast->cdev.dev)
+ return 0;
+
+ /* Under the mutex, so a concurrent contrast set cannot be undone */
+ mutex_lock(&g15->mutex);
+ if (g15->lcd_contrast_set)
+ lg_g15_contrast_write(g15, contrast->brightness);
+ mutex_unlock(&g15->mutex);
+
+ return 0;
+}
+
static struct hid_driver lg_g15_driver = {
.name = "lg-g15",
.id_table = lg_g15_devices,
.raw_event = lg_g15_raw_event,
.probe = lg_g15_probe,
.remove = lg_g15_remove,
+ .resume = lg_g15_resume,
+ .reset_resume = lg_g15_resume,
};
module_hid_driver(lg_g15_driver);
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH v2 2/2] HID: lg-g15: report Super-key-lock state on G15 v2's keyboard interface
2026-11-09 17:17 [PATCH v2 0/2] HID: lg-g15: G15 v2 contrast and game-mode LEDs Torstein Eide
2026-11-09 17:17 ` [PATCH v2 1/2] HID: lg-g15: add LCD contrast LED Torstein Eide
@ 2026-11-09 17:17 ` Torstein Eide
1 sibling, 0 replies; 3+ messages in thread
From: Torstein Eide @ 2026-11-09 17:17 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: Hans de Goede, Maoyi Xie, linux-input, linux-kernel
The G15 v2 exposes a second USB HID interface (046D:C226) previously
left to hid-generic, which reports the Super-key-lock ("Game Mode")
state on report 3. Bind it as LG_G15_V2_KBD: keep default hid-input
behavior for normal typing, and additionally decode report 3 to expose
the lock state as a read-only g15::game_mode LED.
Tested on Logitech G15v2.
Signed-off-by: Torstein Eide <torsteine+linux@gmail.com>
Assisted-by: Claude:claude-sonnet-5
---
drivers/hid/hid-ids.h | 1 +
drivers/hid/hid-lg-g15.c | 230 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 227 insertions(+), 4 deletions(-)
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 91319aa6516d..44ebf2a0accc 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -950,6 +950,7 @@
#define USB_DEVICE_ID_LOGITECH_G13 0xc21c
#define USB_DEVICE_ID_LOGITECH_G15_LCD 0xc222
#define USB_DEVICE_ID_LOGITECH_G11 0xc225
+#define USB_DEVICE_ID_LOGITECH_G15_V2_KBD 0xc226
#define USB_DEVICE_ID_LOGITECH_G15_V2_LCD 0xc227
#define USB_DEVICE_ID_LOGITECH_G510 0xc22d
#define USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO 0xc22e
diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c
index 3827c66ca3a9..998023ad272d 100644
--- a/drivers/hid/hid-lg-g15.c
+++ b/drivers/hid/hid-lg-g15.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/random.h>
#include <linux/sched.h>
+#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/wait.h>
#include <dt-bindings/leds/common.h>
@@ -45,7 +46,8 @@
enum lg_g15_model {
LG_G13,
LG_G15,
- LG_G15_V2,
+ LG_G15_V2, /* 046D:C227, GamePanel LCD iface */
+ LG_G15_V2_KBD, /* 046D:C226, keyboard/consumer-ctrl iface, not the LCD */
LG_G510,
LG_G510_USB_AUDIO,
LG_Z10,
@@ -104,6 +106,19 @@ struct lg_g15_data {
bool lcd_contrast_set; /* LCD contrast written at least once */
};
+/*
+ * State for the G15 v2's separate keyboard + consumer-control USB device
+ * (046D:C226). Unlike lg_g15_data above, this is not the GamePanel LCD
+ * interface: it carries normal typing (left to generic hid-input) plus one
+ * extra report we care about, so it only needs a small amount of state.
+ */
+struct lg_g15_kbd_data {
+ struct led_classdev led; /* g15::game_mode, read-only */
+ spinlock_t lock; /* Protects super_lock and events */
+ bool super_lock; /* Cached HW state of the Super/Windows-key lock */
+ unsigned int events; /* Report 3 input reports seen so far */
+};
+
/********* G13 LED functions ***********/
/*
* G13 retains no state across power cycles, and always powers up with the backlight on,
@@ -699,6 +714,8 @@ static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15)
* the LCD and touch keys part of the speakers.
*/
return 0;
+ case LG_G15_V2_KBD:
+ break; /* Never reached, this model has no lg_g15_data */
}
return -EINVAL; /* Never reached */
}
@@ -963,11 +980,173 @@ static int lg_g510_leds_event(struct lg_g15_data *g15, u8 *data)
return 0;
}
+/********* G15 v2 keyboard interface functions *********/
+
+/* Report 3, byte 1 bit 0: Super/Windows key lock state ("Game Mode") */
+#define LG_G15_V2_KBD_GAME_MODE_REPORT 0x03
+
+/*
+ * The G15 v2's keyboard device uses struct lg_g15_kbd_data as drvdata, not
+ * struct lg_g15_data. This is the one check used by every entry point to
+ * tell the two apart.
+ */
+static bool lg_g15_is_v2_kbd(struct hid_device *hdev)
+{
+ return hdev->vendor == USB_VENDOR_ID_LOGITECH &&
+ hdev->product == USB_DEVICE_ID_LOGITECH_G15_V2_KBD;
+}
+
+static enum led_brightness lg_g15_v2_kbd_super_lock_get(struct led_classdev *led_cdev)
+{
+ struct lg_g15_kbd_data *kbd = container_of(led_cdev, struct lg_g15_kbd_data, led);
+
+ return kbd->super_lock;
+}
+
+/* Must be called with kbd->lock held */
+static void lg_g15_v2_kbd_set_super_lock(struct lg_g15_kbd_data *kbd, bool super_lock)
+{
+ if (super_lock == kbd->super_lock)
+ return;
+
+ kbd->super_lock = super_lock;
+ led_classdev_notify_brightness_hw_changed(&kbd->led, super_lock);
+}
+
+/* Fetches the current lock state, which may already be on at probe/resume. */
+static int lg_g15_v2_kbd_get_super_lock(struct hid_device *hdev, bool *super_lock)
+{
+ u8 *buf;
+ int ret;
+
+ buf = kzalloc(2, GFP_KERNEL); /* DMA-able, not on the stack */
+ if (!buf)
+ return -ENOMEM;
+
+ ret = hid_hw_raw_request(hdev, LG_G15_V2_KBD_GAME_MODE_REPORT, buf, 2,
+ HID_INPUT_REPORT, HID_REQ_GET_REPORT);
+ if (ret == 2 && buf[0] == LG_G15_V2_KBD_GAME_MODE_REPORT) {
+ *super_lock = buf[1] & 0x01;
+ ret = 0;
+ } else {
+ hid_dbg(hdev, "Error reading Super-key-lock state: %d\n", ret);
+ ret = (ret < 0) ? ret : -EIO;
+ }
+
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * Re-reads the lock state on resume. Input reports keep arriving while the
+ * GET_REPORT is in flight; if one did, it is newer than what we read, so
+ * keep the state it set.
+ */
+static void lg_g15_v2_kbd_refresh_super_lock(struct hid_device *hdev,
+ struct lg_g15_kbd_data *kbd)
+{
+ unsigned long flags;
+ unsigned int events;
+ bool super_lock;
+
+ spin_lock_irqsave(&kbd->lock, flags);
+ events = kbd->events;
+ spin_unlock_irqrestore(&kbd->lock, flags);
+
+ if (lg_g15_v2_kbd_get_super_lock(hdev, &super_lock))
+ return;
+
+ spin_lock_irqsave(&kbd->lock, flags);
+ if (kbd->events == events)
+ lg_g15_v2_kbd_set_super_lock(kbd, super_lock);
+ spin_unlock_irqrestore(&kbd->lock, flags);
+}
+
+/* Decodes report 3 and mirrors the Super-key-lock bit into the game_mode LED. */
+static int lg_g15_v2_kbd_raw_event(struct hid_device *hdev, u8 *data, int size)
+{
+ struct lg_g15_kbd_data *kbd = hid_get_drvdata(hdev);
+ unsigned long flags;
+
+ if (!kbd || data[0] != LG_G15_V2_KBD_GAME_MODE_REPORT || size != 2)
+ return 0; /* Not our report, or the non-consumer-control interface */
+
+ spin_lock_irqsave(&kbd->lock, flags);
+ kbd->events++;
+ lg_g15_v2_kbd_set_super_lock(kbd, data[1] & 0x01);
+ spin_unlock_irqrestore(&kbd->lock, flags);
+
+ return 0;
+}
+
+/*
+ * The G15 v2's keyboard USB device exposes two HID interfaces of its own
+ * (boot keyboard + consumer control); both match this same USB product ID,
+ * so this probe runs once per interface. Only the consumer-control one
+ * carries report 3, so the LED is only registered there; the other gets
+ * plain generic hid-input and nothing else, same as if hid-generic had
+ * bound it.
+ */
+static int lg_g15_v2_kbd_probe(struct hid_device *hdev)
+{
+ struct hid_report_enum *rep_enum;
+ struct hid_report *rep;
+ bool has_game_mode_report = false;
+ struct lg_g15_kbd_data *kbd;
+ int ret;
+
+ hdev->quirks |= HID_QUIRK_INPUT_PER_APP; /* As hid-generic does */
+
+ ret = hid_parse(hdev);
+ if (ret)
+ return ret;
+
+ rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
+ list_for_each_entry(rep, &rep_enum->report_list, list) {
+ if (rep->id == LG_G15_V2_KBD_GAME_MODE_REPORT)
+ has_game_mode_report = true; /* This is the consumer-control interface */
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); /* Plain generic keyboard input */
+ if (ret)
+ return ret;
+
+ if (!has_game_mode_report)
+ return 0; /* Boot-keyboard interface, nothing more to do */
+
+ kbd = devm_kzalloc(&hdev->dev, sizeof(*kbd), GFP_KERNEL);
+ if (!kbd)
+ return 0; /* Non-fatal, keyboard input still works without the LED */
+
+ spin_lock_init(&kbd->lock);
+
+ /* Register the LED with the current state, not a default of off */
+ lg_g15_v2_kbd_get_super_lock(hdev, &kbd->super_lock);
+
+ kbd->led.name = "g15::game_mode";
+ kbd->led.max_brightness = 1;
+ kbd->led.brightness_get = lg_g15_v2_kbd_super_lock_get;
+ kbd->led.flags = LED_BRIGHT_HW_CHANGED; /* Enables the hw_changed notify below */
+
+ ret = devm_led_classdev_register(&hdev->dev, &kbd->led);
+ if (ret)
+ return 0; /* Non-fatal, keyboard input still works without the LED */
+
+ /* hid-core does not deliver input reports until probe() returns */
+ hid_set_drvdata(hdev, kbd);
+
+ return 0;
+}
+
static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
- struct lg_g15_data *g15 = hid_get_drvdata(hdev);
+ struct lg_g15_data *g15;
+
+ if (lg_g15_is_v2_kbd(hdev)) /* Separate kbd/consumer-ctrl dev */
+ return lg_g15_v2_kbd_raw_event(hdev, data, size);
+ g15 = hid_get_drvdata(hdev);
if (!g15)
return 0;
@@ -997,6 +1176,8 @@ static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,
if (data[0] == LG_G510_INPUT_KBD_BACKLIGHT && size == 2)
return lg_g510_leds_event(g15, data);
break;
+ case LG_G15_V2_KBD:
+ break; /* Never reached, handled via lg_g15_is_v2_kbd() above */
}
return 0;
@@ -1135,6 +1316,9 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name)
ret = devm_led_classdev_register(&g15->hdev->dev, &g15->leds[i].cdev);
}
break;
+ case LG_G15_V2_KBD:
+ ret = -EINVAL; /* Never reached, this model has no lg_g15_data LEDs */
+ break;
}
return ret;
@@ -1210,6 +1394,12 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
struct hid_report *rep;
int ret, i, gkeys = 0;
+ if (lg_g15_is_v2_kbd(hdev)) /* Not the GamePanel LCD interface */
+ return lg_g15_v2_kbd_probe(hdev);
+
+ if (id->driver_data == LG_G15_V2_KBD) /* e.g. a mismatching new_id */
+ return -ENODEV;
+
hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
ret = hid_parse(hdev);
@@ -1292,6 +1482,8 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
case LG_Z10:
connect_mask = HID_CONNECT_HIDRAW;
break;
+ case LG_G15_V2_KBD:
+ break; /* Never reached, rejected above */
}
ret = hid_hw_start(hdev, connect_mask);
@@ -1446,6 +1638,16 @@ static const struct hid_device_id lg_g15_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_G15_V2_LCD),
.driver_data = LG_G15_V2 },
+ /*
+ * The G15 v2's keyboard + consumer-control USB device (a separate
+ * device from the GamePanel LCD one above, on the keyboard's builtin
+ * hub). Normal typing is handled by generic hid-input; we only bind
+ * this to also expose the Super/Windows-key-lock ("Game Mode") state,
+ * see lg_g15_v2_kbd_probe().
+ */
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_G15_V2_KBD),
+ .driver_data = LG_G15_V2_KBD },
/* G510 without a headset plugged in */
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_G510),
@@ -1464,7 +1666,18 @@ MODULE_DEVICE_TABLE(hid, lg_g15_devices);
static void lg_g15_remove(struct hid_device *hdev)
{
- struct lg_g15_data *g15 = hid_get_drvdata(hdev);
+ struct lg_g15_data *g15;
+
+ /*
+ * The G15 v2's keyboard device uses struct lg_g15_kbd_data, not
+ * lg_g15_data below; it has nothing to clean up beyond hid_hw_stop().
+ */
+ if (lg_g15_is_v2_kbd(hdev)) {
+ hid_hw_stop(hdev);
+ return;
+ }
+
+ g15 = hid_get_drvdata(hdev);
/*
* g15->work is only initialized for the models that schedule it
@@ -1483,9 +1696,18 @@ static void lg_g15_remove(struct hid_device *hdev)
*/
static int lg_g15_resume(struct hid_device *hdev)
{
- struct lg_g15_data *g15 = hid_get_drvdata(hdev);
+ struct lg_g15_data *g15;
struct lg_g15_led *contrast;
+ if (lg_g15_is_v2_kbd(hdev)) {
+ struct lg_g15_kbd_data *kbd = hid_get_drvdata(hdev);
+
+ if (kbd)
+ lg_g15_v2_kbd_refresh_super_lock(hdev, kbd);
+ return 0;
+ }
+
+ g15 = hid_get_drvdata(hdev);
if (!g15)
return 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-09 17:17 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-09 17:17 [PATCH v2 0/2] HID: lg-g15: G15 v2 contrast and game-mode LEDs Torstein Eide
2026-11-09 17:17 ` [PATCH v2 1/2] HID: lg-g15: add LCD contrast LED Torstein Eide
2026-11-09 17:17 ` [PATCH v2 2/2] HID: lg-g15: report Super-key-lock state on G15 v2's keyboard interface Torstein Eide
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.