From 7383354ad59573b42085df3b089ba925a7d1a15a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 4 Apr 2021 20:56:09 +0200 Subject: [PATCH 1/7] HID: logitech-dj/hidpp: Add info/warn/err messages about 27 MHz keyboard encryption By default 27 MHz Logitech keyboards send the keypresses to the receiver in plain text. Logitech's Windows "Keyboard and Mouse settings" tool allows configuring encryption for the keyboard wireless link. Now there also is a Linux tool for setting this up: https://gitlab.freedesktop.org/jwrdegoede/logitech-27mhz-keyboard-encryption-setup Once a keyboard is paired in encrypted mode, it will no longer work with any receiver other then the receiver it was paired with. If a user accidentally tries to pair another keyboard with the receiver with which a keyboard has been paired in encrypted mode, then the receiver looses the encryption key and the originally paired keyboard will no longer work with either that receiver, or another receiver until the keyboard's encryption key is rewritten by another encrypted mode pairing using the tool. Or until it is reset by following a special reset procedure. This commit adds 3 new log messages related to 27 MHz keyboard link encryption to the Logitech dj / hidpp drivers: 1. An info level message when keyboard encryption is being used 2. A warning message when keyboard encryption is not used 3. An error message when the encryption key has been lost and the keyboard will not work because of this Messages 2. and 3. contain a link to the userspace tool to setup the encryption. The linked page also contains instructions to clear the key (without needing the tool) to help a user recover from the keyboard not working in case 3. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 8 ++++++++ drivers/hid/hid-logitech-hidpp.c | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index fa835d565982..18d37b3765f3 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -100,6 +100,7 @@ #define HIDPP_DEVICE_TYPE_MASK GENMASK(3, 0) #define HIDPP_LINK_STATUS_MASK BIT(6) #define HIDPP_MANUFACTURER_MASK BIT(7) +#define HIDPP_27MHZ_SECURE_MASK BIT(7) #define HIDPP_DEVICE_TYPE_KEYBOARD 1 #define HIDPP_DEVICE_TYPE_MOUSE 2 @@ -984,6 +985,13 @@ static void logi_hidpp_dev_conn_notif_27mhz(struct hid_device *hdev, workitem->reports_supported |= STD_MOUSE | HIDPP; break; case 3: /* Index 3 is always the keyboard */ + if (hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] & HIDPP_27MHZ_SECURE_MASK) { + hid_info(hdev, "Keyboard connection is encrypted\n"); + } else { + hid_warn(hdev, "Keyboard events are send over the air in plain-text / unencrypted\n"); + hid_warn(hdev, "See: https://gitlab.freedesktop.org/jwrdegoede/logitech-27mhz-keyboard-encryption-setup/\n"); + } + fallthrough; case 4: /* Index 4 is used for an optional separate numpad */ workitem->device_type = HIDPP_DEVICE_TYPE_KEYBOARD; workitem->reports_supported |= STD_KEYBOARD | MULTIMEDIA | diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index d598094dadd0..69670ca7e1e1 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -56,6 +56,8 @@ MODULE_PARM_DESC(disable_tap_to_click, #define HIDPP_SUB_ID_CONSUMER_VENDOR_KEYS 0x03 #define HIDPP_SUB_ID_ROLLER 0x05 #define HIDPP_SUB_ID_MOUSE_EXTRA_BTNS 0x06 +#define HIDPP_SUB_ID_USER_IFACE_EVENT 0x08 +#define HIDPP_USER_IFACE_EVENT_ENCRYPTION_KEY_LOST BIT(5) #define HIDPP_QUIRK_CLASS_WTP BIT(0) #define HIDPP_QUIRK_CLASS_M560 BIT(1) @@ -3528,6 +3530,16 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, return 1; } + if (hidpp->hid_dev->group == HID_GROUP_LOGITECH_27MHZ_DEVICE && + data[0] == REPORT_ID_HIDPP_SHORT && + data[2] == HIDPP_SUB_ID_USER_IFACE_EVENT && + (data[3] & HIDPP_USER_IFACE_EVENT_ENCRYPTION_KEY_LOST)) { + dev_err_ratelimited(&hidpp->hid_dev->dev, + "Error the keyboard's wireless encryption key has been lost, your keyboard will not work unless you re-configure encryption.\n"); + dev_err_ratelimited(&hidpp->hid_dev->dev, + "See: https://gitlab.freedesktop.org/jwrdegoede/logitech-27mhz-keyboard-encryption-setup/\n"); + } + if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) { ret = hidpp20_battery_event_1000(hidpp, data, size); if (ret != 0) From 042d05b2e30e8a2bb378ecb668a85790dce39355 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 16 Apr 2021 15:13:18 +0200 Subject: [PATCH 2/7] HID: lg-g15: Remove unused size argument from lg_*_event() functions The report-size is already checked in lg_g15_raw_event() before calling the lg_*_event() functions and these functions don't use the passed in size at all, drop the unused parameter. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-lg-g15.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index bfbba0d41933..b887af72957c 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -464,7 +464,7 @@ static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15) /******** Input functions ********/ /* On the G15 Mark I Logitech has been quite creative with which bit is what */ -static int lg_g15_event(struct lg_g15_data *g15, u8 *data, int size) +static int lg_g15_event(struct lg_g15_data *g15, u8 *data) { int i, val; @@ -510,7 +510,7 @@ static int lg_g15_event(struct lg_g15_data *g15, u8 *data, int size) return 0; } -static int lg_g15_v2_event(struct lg_g15_data *g15, u8 *data, int size) +static int lg_g15_v2_event(struct lg_g15_data *g15, u8 *data) { int i, val; @@ -542,7 +542,7 @@ static int lg_g15_v2_event(struct lg_g15_data *g15, u8 *data, int size) return 0; } -static int lg_g510_event(struct lg_g15_data *g15, u8 *data, int size) +static int lg_g510_event(struct lg_g15_data *g15, u8 *data) { bool game_mode_enabled; int i, val; @@ -586,7 +586,7 @@ static int lg_g510_event(struct lg_g15_data *g15, u8 *data, int size) return 0; } -static int lg_g510_leds_event(struct lg_g15_data *g15, u8 *data, int size) +static int lg_g510_leds_event(struct lg_g15_data *g15, u8 *data) { bool backlight_disabled; @@ -613,18 +613,18 @@ static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report, switch (g15->model) { case LG_G15: if (data[0] == 0x02 && size == 9) - return lg_g15_event(g15, data, size); + return lg_g15_event(g15, data); break; case LG_G15_V2: if (data[0] == 0x02 && size == 5) - return lg_g15_v2_event(g15, data, size); + return lg_g15_v2_event(g15, data); break; case LG_G510: case LG_G510_USB_AUDIO: if (data[0] == 0x03 && size == 5) - return lg_g510_event(g15, data, size); + return lg_g510_event(g15, data); if (data[0] == 0x04 && size == 2) - return lg_g510_leds_event(g15, data, size); + return lg_g510_leds_event(g15, data); break; } From ba3e054e7a70d447aa95883dab87a4eae641d6bc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 16 Apr 2021 15:13:19 +0200 Subject: [PATCH 3/7] HID: lg-g15: Add a lg_g15_handle_lcd_menu_keys() helper function Factor out the handling of the G15 LCD menu keys out of lg_g15_event() into a new lg_g15_handle_lcd_menu_keys() helper function. This is a preparation patch for adding support for the LCD menu keys on the Logitech Z-10 speakers (with LCD) which use the same funky HID report format. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-lg-g15.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index b887af72957c..75a27b48d9c9 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -464,6 +464,19 @@ static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15) /******** Input functions ********/ /* On the G15 Mark I Logitech has been quite creative with which bit is what */ +static void lg_g15_handle_lcd_menu_keys(struct lg_g15_data *g15, u8 *data) +{ + int i, val; + + /* Most left (round/display) button below the LCD */ + input_report_key(g15->input, KEY_KBD_LCD_MENU1, data[8] & 0x80); + /* 4 other buttons below the LCD */ + for (i = 0; i < 4; i++) { + val = data[i + 2] & 0x80; + input_report_key(g15->input, KEY_KBD_LCD_MENU2 + i, val); + } +} + static int lg_g15_event(struct lg_g15_data *g15, u8 *data) { int i, val; @@ -494,13 +507,7 @@ static int lg_g15_event(struct lg_g15_data *g15, u8 *data) /* MR */ input_report_key(g15->input, KEY_MACRO_RECORD_START, data[7] & 0x40); - /* Most left (round) button below the LCD */ - input_report_key(g15->input, KEY_KBD_LCD_MENU1, data[8] & 0x80); - /* 4 other buttons below the LCD */ - for (i = 0; i < 4; i++) { - val = data[i + 2] & 0x80; - input_report_key(g15->input, KEY_KBD_LCD_MENU2 + i, val); - } + lg_g15_handle_lcd_menu_keys(g15, data); /* Backlight cycle button pressed? */ if (data[1] & 0x80) From 614d34f8b3dba62ff0d13d0d45e3220c2960b17d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 16 Apr 2021 15:13:20 +0200 Subject: [PATCH 4/7] HID: lg-g15: Add a lg_g15_init_input_dev() helper function Factor the input-device setup + KEY_KBD_LCD_MENU capability setting out of lg_g15_probe() into a new lg_g15_init_input_dev() helper function. This is a preparation patch for adding support for the LCD menu keys + LCD brightness control on the Logitech Z-10 speakers (with LCD) which use the same protocol as the G15 keyboards. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-lg-g15.c | 42 ++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index 75a27b48d9c9..eff8c51a8dce 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -709,6 +709,28 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i) return devm_led_classdev_register(&g15->hdev->dev, &g15->leds[i].cdev); } +/* Common input device init code shared between keyboards and Z-10 speaker handling */ +static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *input, + const char *name) +{ + int i; + + input->name = name; + input->phys = hdev->phys; + input->uniq = hdev->uniq; + input->id.bustype = hdev->bus; + input->id.vendor = hdev->vendor; + input->id.product = hdev->product; + input->id.version = hdev->version; + input->dev.parent = &hdev->dev; + input->open = lg_g15_input_open; + input->close = lg_g15_input_close; + + /* Keys below the LCD, intended for controlling a menu on the LCD */ + for (i = 0; i < 5; i++) + input_set_capability(input, EV_KEY, KEY_KBD_LCD_MENU1 + i); +} + static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) { u8 gkeys_settings_output_report = 0; @@ -751,6 +773,8 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) g15->hdev = hdev; g15->model = id->driver_data; + g15->input = input; + input_set_drvdata(input, hdev); hid_set_drvdata(hdev, (void *)g15); switch (g15->model) { @@ -822,16 +846,7 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) goto error_hw_stop; /* Setup and register input device */ - input->name = "Logitech Gaming Keyboard Gaming Keys"; - input->phys = hdev->phys; - input->uniq = hdev->uniq; - input->id.bustype = hdev->bus; - input->id.vendor = hdev->vendor; - input->id.product = hdev->product; - input->id.version = hdev->version; - input->dev.parent = &hdev->dev; - input->open = lg_g15_input_open; - input->close = lg_g15_input_close; + lg_g15_init_input_dev(hdev, input, "Logitech Gaming Keyboard Gaming Keys"); /* G-keys */ for (i = 0; i < gkeys; i++) @@ -842,10 +857,6 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) input_set_capability(input, EV_KEY, KEY_MACRO_PRESET1 + i); input_set_capability(input, EV_KEY, KEY_MACRO_RECORD_START); - /* Keys below the LCD, intended for controlling a menu on the LCD */ - for (i = 0; i < 5; i++) - input_set_capability(input, EV_KEY, KEY_KBD_LCD_MENU1 + i); - /* * On the G510 only report headphone and mic mute keys when *not* using * the builtin USB audio device. When the builtin audio is used these @@ -857,9 +868,6 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) input_set_capability(input, EV_KEY, KEY_F20); } - g15->input = input; - input_set_drvdata(input, hdev); - ret = input_register_device(input); if (ret) goto error_hw_stop; From 1e5c22983f13fc1ac05fc21772893b2602ddeb66 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 16 Apr 2021 15:13:21 +0200 Subject: [PATCH 5/7] HID: lg-g15: Make the LED-name used by lg_g15_register_led() a parameter Make the LED-name used by lg_g15_register_led() a parameter. This is a preparation patch for adding support for the LCD menu keys + LCD brightness control on the Logitech Z-10 speakers (with LCD) which use the same protocol as the G15 keyboards. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-lg-g15.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index eff8c51a8dce..b76988c19f84 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -652,19 +652,10 @@ static void lg_g15_input_close(struct input_dev *dev) hid_hw_close(hdev); } -static int lg_g15_register_led(struct lg_g15_data *g15, int i) +static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name) { - static const char * const led_names[] = { - "g15::kbd_backlight", - "g15::lcd_backlight", - "g15::macro_preset1", - "g15::macro_preset2", - "g15::macro_preset3", - "g15::macro_record", - }; - g15->leds[i].led = i; - g15->leds[i].cdev.name = led_names[i]; + g15->leds[i].cdev.name = name; switch (g15->model) { case LG_G15: @@ -733,6 +724,14 @@ static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *inp static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) { + static const char * const led_names[] = { + "g15::kbd_backlight", + "g15::lcd_backlight", + "g15::macro_preset1", + "g15::macro_preset2", + "g15::macro_preset3", + "g15::macro_record", + }; u8 gkeys_settings_output_report = 0; u8 gkeys_settings_feature_report = 0; struct hid_report_enum *rep_enum; @@ -874,7 +873,7 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) /* Register LED devices */ for (i = 0; i < LG_G15_LED_MAX; i++) { - ret = lg_g15_register_led(g15, i); + ret = lg_g15_register_led(g15, i, led_names[i]); if (ret) goto error_hw_stop; } From cbe5b6b6a77ad262d9f9a56962c9b1ac2f91c0f5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 16 Apr 2021 15:13:22 +0200 Subject: [PATCH 6/7] HID: lg-g15: Add support for the Logitech Z-10 speakers Add support to hid-lg-g15 for the Logitech Z-10 speakers. This adds support for the LCD menu keys found on these speakers, as well as support for controlling the LCD's brightness through a LED classdev. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-lg-g15.c | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 84b8da3e7d09..b0b0a55de26d 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -763,6 +763,7 @@ #define I2C_DEVICE_ID_LG_7010 0x7010 #define USB_VENDOR_ID_LOGITECH 0x046d +#define USB_DEVICE_ID_LOGITECH_Z_10_SPK 0x0a07 #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e #define USB_DEVICE_ID_LOGITECH_T651 0xb00c #define USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD 0xb309 diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index b76988c19f84..701ddb98e7d2 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -28,6 +28,7 @@ enum lg_g15_model { LG_G15_V2, LG_G510, LG_G510_USB_AUDIO, + LG_Z10, }; enum lg_g15_led_type { @@ -457,6 +458,13 @@ static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15) return ret; return lg_g510_update_mkey_led_brightness(g15); + case LG_Z10: + /* + * Getting the LCD backlight brightness is not supported. + * Reading Feature(2) fails with -EPIPE and this crashes + * the LCD and touch keys part of the speakers. + */ + return 0; } return -EINVAL; /* Never reached */ } @@ -626,6 +634,12 @@ static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report, if (data[0] == 0x02 && size == 5) return lg_g15_v2_event(g15, data); break; + case LG_Z10: + if (data[0] == 0x02 && size == 9) { + lg_g15_handle_lcd_menu_keys(g15, data); + input_sync(g15->input); + } + break; case LG_G510: case LG_G510_USB_AUDIO: if (data[0] == 0x03 && size == 5) @@ -660,8 +674,10 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name) switch (g15->model) { case LG_G15: case LG_G15_V2: - g15->leds[i].cdev.brightness_set_blocking = lg_g15_led_set; g15->leds[i].cdev.brightness_get = lg_g15_led_get; + fallthrough; + case LG_Z10: + g15->leds[i].cdev.brightness_set_blocking = lg_g15_led_set; if (i < LG_G15_BRIGHTNESS_MAX) { g15->leds[i].cdev.flags = LED_BRIGHT_HW_CHANGED; g15->leds[i].cdev.max_brightness = 2; @@ -802,6 +818,9 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) gkeys_settings_feature_report = 0x01; gkeys = 18; break; + case LG_Z10: + connect_mask = HID_CONNECT_HIDRAW; + break; } ret = hid_hw_start(hdev, connect_mask); @@ -844,6 +863,19 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) if (ret) goto error_hw_stop; + if (g15->model == LG_Z10) { + lg_g15_init_input_dev(hdev, g15->input, "Logitech Z-10 LCD Menu Keys"); + ret = input_register_device(g15->input); + if (ret) + goto error_hw_stop; + + ret = lg_g15_register_led(g15, 1, "z-10::lcd_backlight"); + if (ret) + goto error_hw_stop; + + return 0; /* All done */ + } + /* Setup and register input device */ lg_g15_init_input_dev(hdev, input, "Logitech Gaming Keyboard Gaming Keys"); @@ -904,6 +936,10 @@ static const struct hid_device_id lg_g15_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO), .driver_data = LG_G510_USB_AUDIO }, + /* Z-10 speakers */ + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_Z_10_SPK), + .driver_data = LG_Z10 }, { } }; MODULE_DEVICE_TABLE(hid, lg_g15_devices); From b413c59ead320a21eb72ab7c2e0f0a81911f67ab Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 16 Apr 2021 15:13:23 +0200 Subject: [PATCH 7/7] HID: lg-g15 + ite: Add MODULE_AUTHOR I noticed that the 2 HID drivers which I've written and maintain were missing a MODULE_AUTHOR tag, add this so that people can easily figure out who to email with questions. Signed-off-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-ite.c | 1 + drivers/hid/hid-lg-g15.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/hid/hid-ite.c b/drivers/hid/hid-ite.c index 14fc068affad..430fa4f52ed3 100644 --- a/drivers/hid/hid-ite.c +++ b/drivers/hid/hid-ite.c @@ -135,4 +135,5 @@ static struct hid_driver ite_driver = { }; module_hid_driver(ite_driver); +MODULE_AUTHOR("Hans de Goede "); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index 701ddb98e7d2..b2a08233f8d5 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -952,4 +952,5 @@ static struct hid_driver lg_g15_driver = { }; module_hid_driver(lg_g15_driver); +MODULE_AUTHOR("Hans de Goede "); MODULE_LICENSE("GPL");