[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250703004943.515919-6-derekjohn.clark@gmail.com>
Date: Wed, 2 Jul 2025 17:49:42 -0700
From: "Derek J. Clark" <derekjohn.clark@...il.com>
To: Jiri Kosina <jikos@...nel.org>,
Benjamin Tissoires <bentiss@...nel.org>
Cc: Mario Limonciello <superm1@...nel.org>,
Xino Ni <nijs1@...ovo.com>,
Zhixin Zhang <zhangzx36@...ovo.com>,
Mia Shao <shaohz1@...ovo.com>,
Mark Pearson <mpearson-lenovo@...ebb.ca>,
"Pierre-Loup A . Griffais" <pgriffais@...vesoftware.com>,
"Derek J . Clark" <derekjohn.clark@...il.com>,
linux-input@...r.kernel.org,
linux-doc@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH 5/6] HID: Add lenovo-legos-hid core
Adds core interface for the lenovo-legos-hid driver. The purpose of core
is to identify each available endpoint for the MCU in the Lenovo Legion
Go S and route each one to the appropriate initialization and event
handling functions. Endpoint specific logic will be implemented in
subsequent patches.
Signed-off-by: Derek J. Clark <derekjohn.clark@...il.com>
---
MAINTAINERS | 1 +
drivers/hid/Kconfig | 2 +
drivers/hid/Makefile | 2 +
drivers/hid/lenovo-legos-hid/Kconfig | 11 +++
drivers/hid/lenovo-legos-hid/Makefile | 6 ++
drivers/hid/lenovo-legos-hid/core.c | 113 ++++++++++++++++++++++++++
drivers/hid/lenovo-legos-hid/core.h | 25 ++++++
7 files changed, 160 insertions(+)
create mode 100644 drivers/hid/lenovo-legos-hid/Kconfig
create mode 100644 drivers/hid/lenovo-legos-hid/Makefile
create mode 100644 drivers/hid/lenovo-legos-hid/core.c
create mode 100644 drivers/hid/lenovo-legos-hid/core.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 68211d6eb236..aa61be9e5bc1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13751,6 +13751,7 @@ M: Derek J. Clark <derekjohn.clark@...il.com>
L: linux-input@...r.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-driver-lenovo-legos-hid
+F: drivers/hid/lenovo-legos-hid/*
LETSKETCH HID TABLET DRIVER
M: Hans de Goede <hansg@...nel.org>
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index a57901203aeb..494e8386b598 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1436,4 +1436,6 @@ endif # HID
source "drivers/hid/usbhid/Kconfig"
+source "drivers/hid/lenovo-legos-hid/Kconfig"
+
endif # HID_SUPPORT
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 10ae5dedbd84..bdf3ebaf11e5 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -175,3 +175,5 @@ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/
obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/
obj-$(CONFIG_INTEL_THC_HID) += intel-thc-hid/
+
+obj-$(CONFIG_LENOVO_LEGOS_HID) += lenovo-legos-hid/
diff --git a/drivers/hid/lenovo-legos-hid/Kconfig b/drivers/hid/lenovo-legos-hid/Kconfig
new file mode 100644
index 000000000000..6918b25e191c
--- /dev/null
+++ b/drivers/hid/lenovo-legos-hid/Kconfig
@@ -0,0 +1,11 @@
+config LENOVO_LEGOS_HID
+ tristate "Lenovo Legion Go S HID"
+ depends on USB_HID
+ depends on LEDS_CLASS
+ depends on LEDS_CLASS_MULTICOLOR
+ help
+ Say Y here to include support for the Lenovo Legion Go S Handheld
+ Console Controller.
+
+ To compile this driver as a module, choose M here: the module will
+ be called lenovo-legos-hid.
diff --git a/drivers/hid/lenovo-legos-hid/Makefile b/drivers/hid/lenovo-legos-hid/Makefile
new file mode 100644
index 000000000000..707f1be80c78
--- /dev/null
+++ b/drivers/hid/lenovo-legos-hid/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Makefile - Lenovo Legion Go S Handheld Console Controller driver
+#
+lenovo-legos-hid-y := core.o
+obj-$(CONFIG_LENOVO_LEGOS_HID) := lenovo-legos-hid.o
diff --git a/drivers/hid/lenovo-legos-hid/core.c b/drivers/hid/lenovo-legos-hid/core.c
new file mode 100644
index 000000000000..9049cbb8bd6c
--- /dev/null
+++ b/drivers/hid/lenovo-legos-hid/core.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID driver for Lenovo Legion Go S series gamepad.
+ *
+ * Copyright (c) 2025 Derek J. Clark <derekjohn.clark@...il.com>
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/hid.h>
+#include <linux/types.h>
+#include <linux/usb.h>
+
+#include "core.h"
+#include "../hid-ids.h"
+
+u8 get_endpoint_address(struct hid_device *hdev)
+{
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+ struct usb_host_endpoint *ep;
+
+ if (intf) {
+ ep = intf->cur_altsetting->endpoint;
+ if (ep)
+ return ep->desc.bEndpointAddress;
+ }
+
+ return -ENODEV;
+}
+
+static int lenovo_legos_raw_event(struct hid_device *hdev,
+ struct hid_report *report, u8 *data, int size)
+{
+ int ep;
+
+ ep = get_endpoint_address(hdev);
+
+ switch (ep) {
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int lenovo_legos_hid_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret, ep;
+
+ ep = get_endpoint_address(hdev);
+ if (ep <= 0)
+ return ep;
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "Parse failed\n");
+ return ret;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret) {
+ hid_err(hdev, "Failed to start HID device\n");
+ return ret;
+ }
+
+ ret = hid_hw_open(hdev);
+ if (ret) {
+ hid_err(hdev, "Failed to open HID device\n");
+ hid_hw_stop(hdev);
+ return ret;
+ }
+
+ switch (ep) {
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void lenovo_legos_hid_remove(struct hid_device *hdev)
+{
+ int ep = get_endpoint_address(hdev);
+
+ switch (ep) {
+ default:
+ hid_hw_close(hdev);
+ hid_hw_stop(hdev);
+
+ break;
+ }
+}
+
+static const struct hid_device_id lenovo_legos_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_QHE,
+ USB_DEVICE_ID_LENOVO_LEGION_GO_S_XINPUT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_QHE,
+ USB_DEVICE_ID_LENOVO_LEGION_GO_S_DINPUT) },
+ {}
+};
+
+MODULE_DEVICE_TABLE(hid, lenovo_legos_devices);
+static struct hid_driver lenovo_legos_hid = {
+ .name = "lenovo-legos-hid",
+ .id_table = lenovo_legos_devices,
+ .probe = lenovo_legos_hid_probe,
+ .remove = lenovo_legos_hid_remove,
+ .raw_event = lenovo_legos_raw_event,
+};
+module_hid_driver(lenovo_legos_hid);
+
+MODULE_AUTHOR("Derek J. Clark");
+MODULE_DESCRIPTION("HID Driver for Lenovo Legion Go S Series gamepad.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/lenovo-legos-hid/core.h b/drivers/hid/lenovo-legos-hid/core.h
new file mode 100644
index 000000000000..efbc50896536
--- /dev/null
+++ b/drivers/hid/lenovo-legos-hid/core.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/* Copyright(C) 2025 Derek J. Clark <derekjohn.clark@...il.com> */
+
+#ifndef _LENOVO_LEGOS_HID_CORE_
+#define _LENOVO_LEGOS_HID_CORE_
+
+#include <linux/types.h>
+
+#define GO_S_PACKET_SIZE 64
+
+struct hid_device;
+
+enum legos_interface {
+ LEGION_GO_S_IAP_INTF_IN = 0x81,
+ LEGION_GO_S_TP_INTF_IN = 0x83,
+ LEGION_GO_S_CFG_INTF_IN,
+ LEGION_GO_S_IMU_INTF_IN,
+ LEGION_GO_S_GP_INFT_IN,
+ LEGION_GO_S_UNK_INTF_IN,
+};
+
+u8 get_endpoint_address(struct hid_device *hdev);
+
+#endif /* !_LENOVO_LEGOS_HID_CORE_*/
--
2.50.0
Powered by blists - more mailing lists