linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4] HID: logitech-dj: Add support for a new lightspeed receiver iteration
@ 2025-09-02 18:43 Mavroudis Chatzilazaridis
  2025-09-02 20:03 ` Stuart
  0 siblings, 1 reply; 12+ messages in thread
From: Mavroudis Chatzilazaridis @ 2025-09-02 18:43 UTC (permalink / raw)
  To: jikos
  Cc: linux-input, benjamin.tissoires, hadess, lains, stuart.a.hayhurst,
	mavchatz

The lightspeed receiver for the Pro X Superlight uses 13 byte mouse reports
without a report id. The workaround for such cases has been adjusted to
handle these larger packets.

The device now reports the status of its battery in wireless mode and
libratbag now recognizes the device and it can be configured with Piper.

This receiver has new descriptors, which have been added. The mouse
descriptor has 5 extra vendor defined bytes at the end, while the keyboard
one has a different report layout and minimums/maximums. As such, mice
with key press macros and keyboards that use this receiver misbehave
without them.

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
Link: https://lore.kernel.org/r/CAOEevLOrTSugnLePJwpcqx2_AacNbBxFZDTqp0Qm_jjVpWKgFQ@mail.gmail.com/
Link: https://lore.kernel.org/r/6929ebbf-f2e0-4cd4-addc-1e33ecf3277f@gmail.com/
Co-developed-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Mavroudis Chatzilazaridis <mavchatz@protonmail.com>
---
V3 -> V4: Rebased and renamed dev id define from 1_2 to 1_3,
          gaming_kbd_descriptor to kbd_lightspeed_1_3_descriptor
V2 -> V3: Fixed regression which broke keyboard related input
V1 -> V2: Addressed review comment for commit message

 drivers/hid/hid-ids.h         |   1 +
 drivers/hid/hid-logitech-dj.c | 106 ++++++++++++++++++++++++++++++++--
 2 files changed, 103 insertions(+), 4 deletions(-)

diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 149798754570..25d52024f729 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -911,6 +911,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 cce54dd9884a..00a975b70f59 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,45 @@ 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, 0x05,		/*   Report Count (5)           */
+	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)     */
+	0x95, 0x70,		/*   Report Count (112)         */
+	0x75, 0x01,		/*   Report Size (1)            */
+	0x05, 0x07,		/*   Usage Page (Kbrd/Keypad)   */
+	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)       */
+	0xC0,			/* End Collection               */
+};
+
 /* Mouse descriptor (2)     */
 static const char mse_descriptor[] = {
 	0x05, 0x01,		/*  USAGE_PAGE (Generic Desktop)        */
@@ -415,6 +455,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)       */
@@ -1426,7 +1511,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 +1525,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 +1787,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 +1869,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 +2081,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),
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2025-09-09 23:24 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-02 18:43 [PATCH v4] HID: logitech-dj: Add support for a new lightspeed receiver iteration Mavroudis Chatzilazaridis
2025-09-02 20:03 ` Stuart
2025-09-03 15:56   ` Mavroudis Chatzilazaridis
2025-09-03 17:21     ` Stuart
2025-09-03 19:34       ` Mavroudis Chatzilazaridis
2025-09-03 20:40         ` Stuart
2025-09-04 12:21           ` Mavroudis Chatzilazaridis
2025-09-04 13:12             ` Stuart
2025-09-08 21:30               ` Mavroudis Chatzilazaridis
2025-09-08 23:59                 ` Stuart
2025-09-09 22:23                   ` Mavroudis Chatzilazaridis
2025-09-09 23:24                     ` Stuart

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).