[PATCH] platform/x86: bitland-mifs-wmi: Only expose kb_mode when it is supported
From: Anton Karasev
Date: Thu Oct 08 2026 - 11:26:07 EST
WMI_FN_RGB_KB_MODE is the RGB keyboard mode on the Bitland machines this
driver was written for, but the driver also binds to Xiaomi laptops with
the same WMI interface, where the function is the battery interface of
the firmware. kb_mode_store() sends the mode in payload[0] only. On the
Redmi Book Pro 16 2024 (TM2309), and per Milos Vlku's reading of its
ACPI tables on the Redmi Book Pro 14 2024 (TM2307), payload[0] selects
the subcommand there, and "fixed" (2) is the charge limit; as the value
field stays 0, the SET turns the limit off. Writing "fixed" to kb_mode
therefore turns off the battery charge limit, even where the write
reports an error.
That firmware does not handle the GET that kb_mode_show() sends: the
reply is all zeros, and with commit 23cc56f6dea6 ("platform/x86:
bitland-mifs-wmi: Detect failed function calls") bitland_mifs_wmi_call()
fails with -EIO for it, so kb_mode cannot be read there anyway.
Do that GET once at probe time and only expose kb_mode if it succeeds.
A DMI list would have to name every Xiaomi model with this interface;
the failed GET covers any firmware that does not implement it. On
machines where kb_mode can be read, nothing changes. The attribute also
disappears from the event device, where it never worked.
Fixes: dc1ec4fa86b2 ("platform/x86: bitland-mifs-wmi: Add new Bitland MIFS WMI driver")
Link: https://lore.kernel.org/all/20260930015058.3076905-1-uselessfire@xxxxxxxxx/
Link: https://lore.kernel.org/all/CAObHBTwnv+kEaFdCp+cr31HtdSo3CaRq84T8vr-Pm75yZMTHqQ@xxxxxxxxxxxxxx/
Signed-off-by: Anton Karasev <uselessfire@xxxxxxxxx>
---
This is against pdx86 for-next and relies on 23cc56f6dea6, which is
not in mainline yet: without it the GET "succeeds" on that firmware and
kb_mode stays. So please take it via for-next only. Stable kernels
since 7.1 have the same problem, but a backport would first need
23cc56f6dea6 together with 4cfc3631d4ca and, for the Xiaomi models,
Chris's "Only check the function id of GET responses".
It does not depend on Mingyou's v9 series. Either can go first: the
other then applies with "git am -3", because this patch replaces the
ATTRIBUTE_GROUPS(laptop) line, which is context in the keymap hunk of
v9 2/5.
Tested on the TM2309: for-next as is shows kb_mode and reading it fails
with -EIO; with this patch kb_mode is gone, and gpu_mode, fan_boost and
platform_profile are unchanged.
Mingyou, Rsplwe, Bozhen: on your Bitland machines kb_mode should still
be there, read back the current mode, and writing a mode should still
change the keyboard lighting. If it is gone, loading the module with
dyndbg=+p prints the error from the probe. Milos, Junwen (TM2307),
Aleksandr (TM2309), Chris, Kento, Yuming (Xiaomi Book Pro 14): on your
machines kb_mode should be gone; if it is still there on the Book Pro
14, its firmware answers that GET, and this check does not cover it.
drivers/platform/x86/bitland-mifs-wmi.c | 57 ++++++++++++++++++++++---
1 file changed, 50 insertions(+), 7 deletions(-)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index dd1648b7d..d95a02159 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -155,6 +155,7 @@ struct bitland_fan_notify_data {
struct bitland_mifs_wmi_data {
struct wmi_device *wdev;
struct mutex lock; /* Protects WMI calls */
+ bool has_kb_mode;
struct led_classdev kbd_led;
struct notifier_block notifier;
struct input_dev *input_dev;
@@ -520,26 +521,38 @@ static const char *const kb_mode_strings[] = {
"custom", /* 3 */
};
-static ssize_t kb_mode_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int bitland_kb_mode_get(struct bitland_mifs_wmi_data *data, u8 *mode)
{
- struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
struct bitland_mifs_input input = {
.operation = cpu_to_le16(WMI_METHOD_GET),
.function = cpu_to_le16(WMI_FN_RGB_KB_MODE),
};
struct bitland_mifs_output res;
- u8 mode_val;
int ret;
ret = bitland_mifs_wmi_call(data, &input, &res);
if (ret)
return ret;
- mode_val = res.data[0];
- if (mode_val >= ARRAY_SIZE(kb_mode_strings))
+ if (res.data[0] >= ARRAY_SIZE(kb_mode_strings))
return -EPROTO;
+ *mode = res.data[0];
+
+ return 0;
+}
+
+static ssize_t kb_mode_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
+ u8 mode_val;
+ int ret;
+
+ ret = bitland_kb_mode_get(data, &mode_val);
+ if (ret)
+ return ret;
+
return sysfs_emit(buf, "%s\n", kb_mode_strings[mode_val]);
}
@@ -604,7 +617,23 @@ static const struct attribute *const laptop_attrs[] = {
&dev_attr_fan_boost.attr,
NULL,
};
-ATTRIBUTE_GROUPS(laptop);
+
+static umode_t laptop_attr_is_visible(struct kobject *kobj,
+ const struct attribute *attr, int n)
+{
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(kobj_to_dev(kobj));
+
+ if (attr == &dev_attr_kb_mode.attr && !data->has_kb_mode)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group laptop_group = {
+ .attrs_const = laptop_attrs,
+ .is_visible_const = laptop_attr_is_visible,
+};
+__ATTRIBUTE_GROUPS(laptop);
static const struct key_entry bitland_mifs_wmi_keymap[] = {
{ KE_KEY, WMI_EVENT_OPEN_APP, { KEY_PROG1 } },
@@ -666,6 +695,7 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
.default_label = ":" LED_FUNCTION_KBD_BACKLIGHT,
.devname_mandatory = true,
};
+ u8 kb_mode;
int ret;
drv_data = devm_kzalloc(&wdev->dev, sizeof(*drv_data), GFP_KERNEL);
@@ -699,6 +729,19 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
return input_register_device(drv_data->input_dev);
}
+ /*
+ * WMI_FN_RGB_KB_MODE is the RGB keyboard mode on the machines this
+ * driver was written for, but some firmware uses the function for
+ * something else and does not answer this GET. On some Xiaomi models
+ * it is the battery interface, where the SET that kb_mode_store()
+ * sends for "fixed" turns the charge limit off. Only expose kb_mode
+ * when its GET works.
+ */
+ ret = bitland_kb_mode_get(drv_data, &kb_mode);
+ if (ret)
+ dev_dbg(&wdev->dev, "kb_mode not supported (%d), hiding it\n", ret);
+ drv_data->has_kb_mode = !ret;
+
/* Register platform profile */
drv_data->pp_dev = devm_platform_profile_register(&wdev->dev, DRV_NAME, drv_data,
&laptop_profile_ops);
base-commit: 98f039265e580291d5e76a11b2072541fbb1c393
--
2.56.0