From: Saalim Quadri <danascape@gmail.com>
To: jikos@kernel.org, bentiss@kernel.org
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
Saalim Quadri <danascape@gmail.com>
Subject: [PATCH v3 RESEND] HID: Xinmeng: Add driver for Xinmeng M71 Keyboard
Date: Sun, 7 Sep 2025 16:42:46 +0530 [thread overview]
Message-ID: <20250907111246.9733-1-danascape@gmail.com> (raw)
In this driver, the battery is probed, and it checks the capacity
and charging status of the battery
Signed-off-by: Saalim Quadri <danascape@gmail.com>
---
v2 -> v3:
Match lore email to the driver
assign ret val while probing battery
v1 -> v2: Update Kconfig paragraph :/
drivers/hid/Kconfig | 12 +++
drivers/hid/Makefile | 1 +
drivers/hid/hid-ids.h | 3 +
drivers/hid/hid-xinmeng.c | 170 ++++++++++++++++++++++++++++++++++++++
4 files changed, 186 insertions(+)
create mode 100644 drivers/hid/hid-xinmeng.c
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 43859fc75747..1cc61ca63682 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -536,6 +536,18 @@ config HID_XIAOMI
Adds support for side buttons of Xiaomi Mi Dual Mode Wireless
Mouse Silent Edition.
+config HID_XINMENG
+ tristate "Xinmeng Keyboards"
+ depends on USB_HID
+ help
+ Support for Xinmeng Keyboard.
+
+ Say Y here if you have a Xinmeng M71 Keyboard
+ and want to be able to read its battery capacity.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hid-xinmeng.
+
config HID_GYRATION
tristate "Gyration remote control"
help
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 10ae5dedbd84..e5c9d1276138 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -145,6 +145,7 @@ obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o
obj-$(CONFIG_HID_UNIVERSAL_PIDFF) += hid-universal-pidff.o
obj-$(CONFIG_HID_LED) += hid-led.o
obj-$(CONFIG_HID_XIAOMI) += hid-xiaomi.o
+obj-$(CONFIG_HID_XINMENG) += hid-xinmeng.o
obj-$(CONFIG_HID_XINMO) += hid-xinmo.o
obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index e3fb4e2fe911..f649549bd58c 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -1471,6 +1471,9 @@
#define USB_DEVICE_ID_XIN_MO_DUAL_ARCADE 0x05e1
#define USB_DEVICE_ID_THT_2P_ARCADE 0x75e1
+#define USB_VENDOR_ID_XINMENG 0x3554
+#define USB_DEVICE_ID_XINMENG_M71_RECEIVER 0xfa09
+
#define USB_VENDOR_ID_XIROKU 0x1477
#define USB_DEVICE_ID_XIROKU_SPX 0x1006
#define USB_DEVICE_ID_XIROKU_MPX 0x1007
diff --git a/drivers/hid/hid-xinmeng.c b/drivers/hid/hid-xinmeng.c
new file mode 100644
index 000000000000..a4f5b181bca4
--- /dev/null
+++ b/drivers/hid/hid-xinmeng.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID driver for Xinmeng M71 Keyboard.
+ *
+ * Copyright (c) 2025 Saalim Quadri <danascape@gmail.com>
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+#define BATTERY_REPORT_ID (0x13)
+
+struct xinmeng_drvdata {
+ struct hid_device *hdev;
+ bool online;
+
+ struct power_supply *battery;
+ struct power_supply_desc battery_desc;
+ u8 battery_capacity;
+ bool battery_charging;
+};
+
+static enum power_supply_property xinmeng_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_ONLINE
+};
+
+static int xinmeng_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct xinmeng_drvdata *drv_data = power_supply_get_drvdata(psy);
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = drv_data->online;
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ if (drv_data->online)
+ val->intval = drv_data->battery_charging ?
+ POWER_SUPPLY_STATUS_CHARGING :
+ POWER_SUPPLY_STATUS_DISCHARGING;
+ else
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = drv_data->battery_capacity;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = drv_data->hdev->name;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int xinmeng_battery_probe(struct hid_device *hdev)
+{
+ struct xinmeng_drvdata *drv_data = hid_get_drvdata(hdev);
+ struct power_supply_config pscfg = { .drv_data = drv_data };
+ int ret = 0;
+
+ drv_data->online = false;
+ drv_data->battery_capacity = 0;
+
+ drv_data->battery_desc.name = "xinmeng-m71-battery";
+ drv_data->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+ drv_data->battery_desc.properties = xinmeng_battery_props;
+ drv_data->battery_desc.num_properties = ARRAY_SIZE(xinmeng_battery_props);
+ drv_data->battery_desc.get_property = xinmeng_battery_get_property;
+
+ drv_data->battery = devm_power_supply_register(&hdev->dev,
+ &drv_data->battery_desc, &pscfg);
+
+ if (IS_ERR(drv_data->battery)) {
+ ret = PTR_ERR(drv_data->battery);
+ drv_data->battery = NULL;
+ hid_err(hdev, "Unable to register battery device\n");
+ return ret;
+ }
+
+ power_supply_powers(drv_data->battery, &hdev->dev);
+
+ return ret;
+}
+
+static int xinmeng_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+ struct xinmeng_drvdata *drv_data;
+ struct usb_interface *usbif;
+
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
+ usbif = to_usb_interface(hdev->dev.parent);
+
+ drv_data = devm_kzalloc(&hdev->dev, sizeof(*drv_data), GFP_KERNEL);
+ if (!drv_data)
+ return -ENOMEM;
+
+ hid_set_drvdata(hdev, drv_data);
+ drv_data->hdev = hdev;
+
+ ret = hid_parse(hdev);
+ if (ret)
+ return ret;
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (ret)
+ return ret;
+
+ if (usbif->cur_altsetting->desc.bInterfaceNumber == 1) {
+ ret = xinmeng_battery_probe(hdev);
+ if (ret < 0) {
+ hid_err(hdev, "Xinmeng hid battery_probe failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int xinmeng_raw_event(struct hid_device *hdev,
+ struct hid_report *report, u8 *data, int size)
+{
+ struct xinmeng_drvdata *drv_data = hid_get_drvdata(hdev);
+
+ if (drv_data->battery && data[0] == BATTERY_REPORT_ID) {
+ u8 charging_status = data[7];
+ bool is_charging = (charging_status == 0x11 || charging_status == 0x10);
+
+ drv_data->battery_capacity = data[6];
+ drv_data->battery_charging = is_charging;
+ drv_data->online = true;
+ }
+
+ return 0;
+}
+
+static const struct hid_device_id xinmeng_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_XINMENG, USB_DEVICE_ID_XINMENG_M71_RECEIVER) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, xinmeng_devices);
+
+static struct hid_driver xinmeng_driver = {
+ .name = "xinmeng",
+ .id_table = xinmeng_devices,
+ .probe = xinmeng_probe,
+ .raw_event = xinmeng_raw_event
+};
+module_hid_driver(xinmeng_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HID driver for Xinmeng keyboards");
+MODULE_AUTHOR("Saalim Quadri <danascape@gmail.com>");
--
2.34.1
next reply other threads:[~2025-09-07 11:12 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-07 11:12 Saalim Quadri [this message]
2025-09-07 11:30 ` [PATCH v3 RESEND] HID: Xinmeng: Add driver for Xinmeng M71 Keyboard Saalim Quadri
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250907111246.9733-1-danascape@gmail.com \
--to=danascape@gmail.com \
--cc=bentiss@kernel.org \
--cc=jikos@kernel.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.