From: Mavroudis Chatzilazaridis Date: Sat, 27 Sep 2025 23:49:45 +0000 (+0000) Subject: HID: logitech-dj: Add support for a new lightspeed receiver iteration X-Git-Tag: v6.19-rc1~139^2~5^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5329fc30cbeabbd8593e5afc3e0f7a5cf86c6ceb;p=thirdparty%2Flinux.git HID: logitech-dj: Add support for a new lightspeed receiver iteration This lightspeed receiver uses 13 byte mouse reports without an ID. There are 5 additional vendor defined bytes appended to the report. The workaround for such cases has been adjusted to handle these larger reports. The keyboard reports have a distinct layout with differing minimums and maximums. Additionally, the LED reports now require a report ID of 1. New keyboard and mouse descriptors have been added to reflect these changes. The devices attached to this receiver now report the status of their battery in wireless mode, libratbag communicates with them and they can be configured with Piper. Fixes: 9d1bd9346241 ("HID: logitech-dj: Add support for a new lightspeed receiver iteration") Link: https://bugzilla.kernel.org/show_bug.cgi?id=218172 Link: https://bugzilla.kernel.org/show_bug.cgi?id=218094 Co-developed-by: Filipe Laíns Signed-off-by: Filipe Laíns Signed-off-by: Mavroudis Chatzilazaridis Tested-by: Stuart Hayhurst Signed-off-by: Jiri Kosina --- diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 1d23e5c8f8df2..bad860af560ac 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -912,6 +912,7 @@ #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1 0xc539 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1 0xc53f #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2 0xc543 +#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_3 0xc547 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY 0xc53a #define USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER 0xc548 #define USB_DEVICE_ID_SPACETRAVELLER 0xc623 diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index cce54dd9884a3..d66f4807311a3 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -116,6 +116,7 @@ enum recvr_type { recvr_type_dj, recvr_type_hidpp, recvr_type_gaming_hidpp, + recvr_type_gaming_hidpp_ls_1_3, recvr_type_mouse_only, recvr_type_27mhz, recvr_type_bluetooth, @@ -211,6 +212,44 @@ static const char kbd_descriptor[] = { 0xC0 }; +/* Gaming Keyboard descriptor (1) */ +static const char kbd_lightspeed_1_3_descriptor[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x06, /* Usage (Keyboard) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x85, 0x01, /* Report ID (1) */ + 0x05, 0x07, /* Usage Page (Kbrd/Keypad) */ + 0x19, 0xE0, /* Usage Minimum (0xE0) */ + 0x29, 0xE7, /* Usage Maximum (0xE7) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x08, /* Report Count (8) */ + 0x81, 0x02, /* Input (Data,Var) */ + 0x95, 0x70, /* Report Count (112) */ + 0x19, 0x04, /* Usage Minimum (0x04) */ + 0x29, 0x73, /* Usage Maximum (0x73) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x95, 0x05, /* Report Count (5) */ + 0x19, 0x87, /* Usage Minimum (0x87) */ + 0x29, 0x8B, /* Usage Maximum (0x8B) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x95, 0x03, /* Report Count (3) */ + 0x19, 0x90, /* Usage Minimum (0x90) */ + 0x29, 0x92, /* Usage Maximum (0x92) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x95, 0x05, /* Report Count (5) */ + 0x85, 0x0E, /* Report ID (14) */ + 0x05, 0x08, /* Usage Page (LEDs) */ + 0x19, 0x01, /* Usage Minimum (Num Lock) */ + 0x29, 0x05, /* Usage Maximum (Kana) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x03, /* Report Size (3) */ + 0x91, 0x03, /* Output (Const,Var,Abs) */ + 0xC0, /* End Collection */ +}; + /* Mouse descriptor (2) */ static const char mse_descriptor[] = { 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ @@ -415,6 +454,51 @@ static const char mse_high_res_descriptor[] = { 0xC0, /* END_COLLECTION */ }; +/* Gaming Mouse descriptor with vendor data (2) */ +static const char mse_high_res_ls_1_3_descriptor[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x02, /* Usage (Mouse) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x85, 0x02, /* Report ID (2) */ + 0x09, 0x01, /* Usage (Pointer) */ + 0xA1, 0x00, /* Collection (Physical) */ + 0x95, 0x10, /* Report Count (16) */ + 0x75, 0x01, /* Report Size (1) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x05, 0x09, /* Usage Page (Button) */ + 0x19, 0x01, /* Usage Minimum (0x01) */ + 0x29, 0x10, /* Usage Maximum (0x10) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x95, 0x02, /* Report Count (2) */ + 0x75, 0x10, /* Report Size (16) */ + 0x16, 0x01, 0x80, /* Logical Minimum (-32767) */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767) */ + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x30, /* Usage (X) */ + 0x09, 0x31, /* Usage (Y) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x08, /* Report Size (8) */ + 0x15, 0x81, /* Logical Minimum (-127) */ + 0x25, 0x7F, /* Logical Maximum (127) */ + 0x09, 0x38, /* Usage (Wheel) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x95, 0x01, /* Report Count (1) */ + 0x05, 0x0C, /* Usage Page (Consumer) */ + 0x0A, 0x38, 0x02, /* Usage (AC Pan) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0xC0, /* End Collection */ + 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */ + 0x09, 0xF1, /* Usage (0xF1) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x05, /* Report Count (5) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x81, 0x00, /* Input (Data,Array,Abs) */ + 0xC0, /* End Collection */ +}; + /* Consumer Control descriptor (3) */ static const char consumer_descriptor[] = { 0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */ @@ -520,9 +604,9 @@ static const char hidpp_descriptor[] = { /* Maximum size of all defined hid reports in bytes (including report id) */ #define MAX_REPORT_SIZE 8 -/* Make sure all descriptors are present here */ +/* Make sure the largest of each descriptor type is present here */ #define MAX_RDESC_SIZE \ - (sizeof(kbd_descriptor) + \ + (sizeof(kbd_lightspeed_1_3_descriptor) +\ sizeof(mse_bluetooth_descriptor) + \ sizeof(mse5_bluetooth_descriptor) + \ sizeof(consumer_descriptor) + \ @@ -1374,12 +1458,19 @@ static int logi_dj_ll_raw_request(struct hid_device *hid, return -EINVAL; if (djrcv_dev->type != recvr_type_dj && count >= 2) { + unsigned char led_report_id = 0; + if (!djrcv_dev->keyboard) { hid_warn(hid, "Received REPORT_TYPE_LEDS request before the keyboard interface was enumerated\n"); return 0; } + + /* This Lightspeed receiver expects LED reports with report ID 1 */ + if (djrcv_dev->type == recvr_type_gaming_hidpp_ls_1_3) + led_report_id = 1; + /* usbhid overrides the report ID and ignores the first byte */ - return hid_hw_raw_request(djrcv_dev->keyboard, 0, buf, count, + return hid_hw_raw_request(djrcv_dev->keyboard, led_report_id, buf, count, report_type, reqtype); } @@ -1426,7 +1517,11 @@ static int logi_dj_ll_parse(struct hid_device *hid) if (djdev->reports_supported & STD_KEYBOARD) { dbg_hid("%s: sending a kbd descriptor, reports_supported: %llx\n", __func__, djdev->reports_supported); - rdcat(rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor)); + if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp_ls_1_3) + rdcat(rdesc, &rsize, kbd_lightspeed_1_3_descriptor, + sizeof(kbd_lightspeed_1_3_descriptor)); + else + rdcat(rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor)); } if (djdev->reports_supported & STD_MOUSE) { @@ -1436,6 +1531,9 @@ static int logi_dj_ll_parse(struct hid_device *hid) djdev->dj_receiver_dev->type == recvr_type_mouse_only) rdcat(rdesc, &rsize, mse_high_res_descriptor, sizeof(mse_high_res_descriptor)); + else if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp_ls_1_3) + rdcat(rdesc, &rsize, mse_high_res_ls_1_3_descriptor, + sizeof(mse_high_res_ls_1_3_descriptor)); else if (djdev->dj_receiver_dev->type == recvr_type_27mhz) rdcat(rdesc, &rsize, mse_27mhz_descriptor, sizeof(mse_27mhz_descriptor)); @@ -1695,11 +1793,12 @@ static int logi_dj_raw_event(struct hid_device *hdev, } /* * Mouse-only receivers send unnumbered mouse data. The 27 MHz - * receiver uses 6 byte packets, the nano receiver 8 bytes. + * receiver uses 6 byte packets, the nano receiver 8 bytes, + * the lightspeed receiver (Pro X Superlight) 13 bytes. */ if (djrcv_dev->unnumbered_application == HID_GD_MOUSE && - size <= 8) { - u8 mouse_report[9]; + size <= 13){ + u8 mouse_report[14]; /* Prepend report id */ mouse_report[0] = REPORT_TYPE_MOUSE; @@ -1776,6 +1875,7 @@ static int logi_dj_probe(struct hid_device *hdev, case recvr_type_dj: no_dj_interfaces = 3; break; case recvr_type_hidpp: no_dj_interfaces = 2; break; case recvr_type_gaming_hidpp: no_dj_interfaces = 3; break; + case recvr_type_gaming_hidpp_ls_1_3: no_dj_interfaces = 3; break; case recvr_type_mouse_only: no_dj_interfaces = 2; break; case recvr_type_27mhz: no_dj_interfaces = 2; break; case recvr_type_bluetooth: no_dj_interfaces = 2; break; @@ -1987,6 +2087,10 @@ static const struct hid_device_id logi_dj_receivers[] = { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2), .driver_data = recvr_type_gaming_hidpp}, + { /* Logitech lightspeed receiver (0xc547) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_3), + .driver_data = recvr_type_gaming_hidpp_ls_1_3}, { /* Logitech 27 MHz HID++ 1.0 receiver (0xc513) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),