* [PATCH v12 1/2] platform/x86/lenovo: lenovo-ymc: Suppress probe on Yoga Book 9 14IAH10
2026-07-08 15:09 ` [PATCH v12 0/2] Lenovo Yoga Book 9 keyboard dock detection Dave Carey
@ 2026-07-08 15:09 ` Dave Carey
2026-07-08 15:09 ` [PATCH v12 2/2] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Dave Carey
2026-07-09 10:33 ` [PATCH v12 0/2] Lenovo Yoga Book 9 keyboard dock detection Ilpo Järvinen
2 siblings, 0 replies; 5+ messages in thread
From: Dave Carey @ 2026-07-08 15:09 UTC (permalink / raw)
To: platform-driver-x86
Cc: ilpo.jarvinen, W_Armin, johannes.goede, linux-kernel, Dave Carey
The Yoga Book 9 14IAH10 (DMI product name "83KJ") has a dedicated
yb9-kbdock WMI driver that registers an input device reporting
SW_TABLET_MODE to track the detachable Bluetooth keyboard.
lenovo-ymc also loads on this machine and creates an input node with the
SW_TABLET_MODE capability bit set. For input switches, the presence of
the capability bit has semantic meaning: userspace (e.g. GNOME) reads
the switch state at startup from every node advertising the capability
and does not expect more than one such node.
Add a DMI match for the Yoga Book 9 14IAH10 to probe() so that
lenovo-ymc returns -ENODEV on this hardware, leaving yb9-kbdock as the
sole SW_TABLET_MODE source. The ymc_ec_trigger EC write, the only
other action taken in response to a YMC event, is guarded by a separate
DMI table that excludes this machine; no other functionality is affected.
Signed-off-by: Dave Carey <carvsdriver@gmail.com>
---
drivers/platform/x86/lenovo/ymc.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/drivers/platform/x86/lenovo/ymc.c b/drivers/platform/x86/lenovo/ymc.c
--- a/drivers/platform/x86/lenovo/ymc.c
+++ b/drivers/platform/x86/lenovo/ymc.c
@@ -23,7 +23,23 @@ module_param(force, bool, 0444);
static bool force;
module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "Force loading on boards without a convertible DMI chassis-type");
+static const struct dmi_system_id lenovo_ymc_nosupport_dmi_table[] = {
+ {
+ /*
+ * Yoga Book 9 14IAH10: SW_TABLET_MODE is reported by the
+ * yb9-kbdock driver. Suppress lenovo-ymc on this machine to
+ * avoid userspace seeing two input nodes that both advertise
+ * the SW_TABLET_MODE capability.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
+ },
+ },
+ { }
+};
+
static const struct dmi_system_id allowed_chasis_types_dmi_table[] = {
{
.matches = {
@@ -100,7 +116,10 @@ static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
struct lenovo_ymc_private *priv;
struct input_dev *input_dev;
int err;
+ if (dmi_check_system(lenovo_ymc_nosupport_dmi_table))
+ return -ENODEV;
+
if (!dmi_check_system(allowed_chasis_types_dmi_table)) {
if (force)
dev_info(&wdev->dev, "Force loading Lenovo YMC support\n");
--
2.54.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v12 2/2] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
2026-07-08 15:09 ` [PATCH v12 0/2] Lenovo Yoga Book 9 keyboard dock detection Dave Carey
2026-07-08 15:09 ` [PATCH v12 1/2] platform/x86/lenovo: lenovo-ymc: Suppress probe on Yoga Book 9 14IAH10 Dave Carey
@ 2026-07-08 15:09 ` Dave Carey
2026-07-09 10:33 ` [PATCH v12 0/2] Lenovo Yoga Book 9 keyboard dock detection Ilpo Järvinen
2 siblings, 0 replies; 5+ messages in thread
From: Dave Carey @ 2026-07-08 15:09 UTC (permalink / raw)
To: platform-driver-x86
Cc: ilpo.jarvinen, W_Armin, johannes.goede, linux-kernel, Dave Carey
The Lenovo Yoga Book 9 14IAH10 ships with a detachable Bluetooth keyboard
that magnetically attaches to the bottom (secondary) screen in one of two
positions. The Embedded Controller tracks the attachment state in a 2-bit
field called BKBD and signals changes via WMI event GUID
806BD2A2-177B-481D-BFB5-3BA0BB4A2285 (notify ID 0xEB on the WM10 ACPI
device, _UID "GMZN").
The device contains embedded BMOF data (WQDD, 20705 bytes) documenting
both WMI interfaces used by this driver:
LENOVO_BTKBD_EVENT (event GUID): WmiDataId(1) uint32 Status.
The ACPI _WED(0xEB) method returns EC.BKBD directly as an integer,
so the notify callback receives BKBD without a separate query.
LENOVO_FEATURE_STATUS_DATA (block GUID, WQAF method): returns an
8-byte buffer {uint32 IDs=0x00060000, uint32 Status=BKBD}.
Used for the initial state read on probe and after resume.
BKBD encoding:
0 = keyboard detached
1 = keyboard docked on top half of bottom screen
2 = keyboard docked on bottom half of bottom screen
3 = reserved (not observed in practice)
This driver registers two WMI drivers sharing a module-level
BLOCKING_NOTIFIER_HEAD:
- The event driver (LENOVO_BTKBD_EVENT) uses .notify_new() to receive
a pre-parsed wmi_buffer and fires the notifier chain with the BKBD
value extracted from the buffer.
- The block driver (LENOVO_FEATURE_STATUS_DATA) owns the input_dev in
its per-device private struct. At probe time it registers a
notifier_block on the chain and reads the initial BKBD state via
wmidev_query_block(). The WMI buffer is parsed as
struct lenovo_feature_status { __le32 id; __le32 status; }, and the
ID field is verified before the status is used.
- SW_TABLET_MODE=1 is reported when the keyboard is detached;
SW_TABLET_MODE=0 when docked in either position (keyboard present).
- The raw BKBD value is exposed via read-only sysfs attribute
"keyboard_position".
- BKBD state is re-read via wmidev_query_block() on resume from
suspend or hibernation.
Tested on: Lenovo Yoga Book 9 14IAH10 (model 83KJ), kernel 7.0.
Acked-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Dave Carey <carvsdriver@gmail.com>
---
v12:
- Add #include <linux/sysfs.h> (for sysfs_emit; Ilpo Järvinen)
- Add #include <linux/types.h> (for __le32 and __packed; Ilpo Järvinen)
v11:
- ABI doc: correct Date to "June 2027" and KernelVersion to "7.3";
these fields should reflect when the ABI lands in the kernel, not
the submission date (Armin Wolf).
---
.../testing/sysfs-driver-lenovo-yb9-kbdock | 19 ++
MAINTAINERS | 7 +
drivers/platform/x86/lenovo/Kconfig | 14 +
drivers/platform/x86/lenovo/Makefile | 1 +
drivers/platform/x86/lenovo/yb9-kbdock.c | 322 ++++++++++++++++++
5 files changed, 363 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
create mode 100644 drivers/platform/x86/lenovo/yb9-kbdock.c
diff --git a/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
new file mode 100644
index 0000000..04e5294
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
@@ -0,0 +1,19 @@
+What: /sys/bus/wmi/drivers/lenovo-yb9-kbdock/<guid>/keyboard_position
+Date: June 2027
+KernelVersion: 7.3
+Contact: Dave Carey <carvsdriver@gmail.com>
+Description:
+ Read-only attribute reporting the current keyboard dock position
+ as reported by the Embedded Controller on the Lenovo Yoga Book 9
+ 14IAH10.
+
+ Possible values:
+
+ == ============================================================
+ 0 keyboard is not docked to any screen (detached)
+ 1 keyboard docked on the top half of the bottom screen
+ 2 keyboard docked on the bottom half of the bottom screen
+ == ============================================================
+
+ SW_TABLET_MODE input events are also emitted: 0 when the keyboard
+ is docked (either position), 1 when detached.
diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e1..00e8275 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14479,6 +14479,13 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/lenovo/wmi-hotkey-utilities.c
+LENOVO YOGA BOOK 9 KEYBOARD DOCK DRIVER
+M: Dave Carey <carvsdriver@gmail.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
+F: drivers/platform/x86/lenovo/yb9-kbdock.c
+
LETSKETCH HID TABLET DRIVER
M: Hans de Goede <hansg@kernel.org>
L: linux-input@vger.kernel.org
diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig
index 9c48487..938b361 100644
--- a/drivers/platform/x86/lenovo/Kconfig
+++ b/drivers/platform/x86/lenovo/Kconfig
@@ -43,6 +43,20 @@ config LENOVO_WMI_CAMERA
To compile this driver as a module, choose M here: the module
will be called lenovo-wmi-camera.
+config LENOVO_YB9_KBDOCK
+ tristate "Lenovo Yoga Book 9 keyboard dock detection"
+ depends on ACPI_WMI
+ depends on DMI
+ depends on INPUT
+ help
+ Say Y here to enable keyboard dock detection on the Lenovo Yoga Book 9
+ 14IAH10. The detachable Bluetooth keyboard magnetically attaches to
+ either screen; this driver reports SW_TABLET_MODE input events based
+ on the attachment state and exposes the raw position in sysfs.
+
+ To compile this driver as a module, choose M here: the module will be
+ called lenovo-yb9-kbdock.
+
config LENOVO_YMC
tristate "Lenovo Yoga Tablet Mode Control"
depends on ACPI_WMI
diff --git a/drivers/platform/x86/lenovo/Makefile b/drivers/platform/x86/lenovo/Makefile
index 7b2128e..2842d7d 100644
--- a/drivers/platform/x86/lenovo/Makefile
+++ b/drivers/platform/x86/lenovo/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
lenovo-target-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES) += wmi-hotkey-utilities.o
+lenovo-target-$(CONFIG_LENOVO_YB9_KBDOCK) += yb9-kbdock.o
lenovo-target-$(CONFIG_LENOVO_YMC) += ymc.o
lenovo-target-$(CONFIG_YOGABOOK) += yogabook.o
lenovo-target-$(CONFIG_YT2_1380) += yoga-tab2-pro-1380-fastcharger.o
diff --git a/drivers/platform/x86/lenovo/yb9-kbdock.c b/drivers/platform/x86/lenovo/yb9-kbdock.c
new file mode 100644
index 0000000..0000000
--- /dev/null
+++ b/drivers/platform/x86/lenovo/yb9-kbdock.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Lenovo Yoga Book 9 keyboard-dock detection
+ *
+ * The Yoga Book 9 ships with a detachable Bluetooth keyboard that magnetically
+ * attaches to the bottom screen in one of two positions. The EC tracks
+ * attachment state in a 2-bit field called BKBD and signals changes via WMI
+ * event 0xEB on the WM10 ACPI device (_UID "GMZN").
+ *
+ * BKBD values:
+ * 0 = keyboard detached
+ * 1 = keyboard docked on the top half of the bottom screen
+ * 2 = keyboard docked on the bottom half of the bottom screen
+ * 3 = reserved / not observed
+ *
+ * Two WMI interfaces are used (documented in embedded BMOF, WQDD, 20705 bytes):
+ *
+ * LENOVO_BTKBD_EVENT (event GUID, 806BD2A2-...)
+ * WmiDataId(1) uint32 Status — _WED(0xEB) returns EC.BKBD directly.
+ * The notify callback receives BKBD as an integer; no separate query needed.
+ *
+ * LENOVO_FEATURE_STATUS_DATA (block GUID, E7F300FA-...)
+ * WmiDataId(1) uint32 IDs = 0x00060000 (feature selector)
+ * WmiDataId(2) uint32 Status = BKBD value
+ * Used on probe and resume to read initial state.
+ *
+ * The event driver (LENOVO_BTKBD_EVENT) fires a notifier chain on each WMI
+ * event. The block driver (LENOVO_FEATURE_STATUS_DATA) owns the input_dev
+ * and registers a notifier_block to receive those events, eliminating the
+ * need for shared global state or a mutex.
+ *
+ * SW_TABLET_MODE=1 is reported when the keyboard is detached;
+ * SW_TABLET_MODE=0 when docked in either position (keyboard present).
+ * The raw BKBD value is exposed via the sysfs attribute "keyboard_position".
+ *
+ * Copyright (C) 2026 Dave Carey <carvsdriver@gmail.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#include <linux/compiler_attributes.h>
+#include <linux/dev_printk.h>
+#include <linux/dmi.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/wmi.h>
+
+#define YB9_KBDOCK_EVENT_GUID "806BD2A2-177B-481D-BFB5-3BA0BB4A2285"
+#define YB9_KBDOCK_QUERY_GUID "E7F300FA-21CD-4003-ADAC-2696135982E6"
+
+/* BKBD encoding */
+#define BKBD_DETACHED 0
+
+/* LENOVO_FEATURE_STATUS_DATA feature selector */
+#define YB9_FEATURE_STATUS_ID 0x00060000u
+
+/*
+ * LENOVO_FEATURE_STATUS_DATA: 8-byte buffer {uint32 IDs, uint32 Status}.
+ * IDs is always 0x00060000; Status holds the BKBD value (0–3).
+ */
+struct lenovo_feature_status {
+ __le32 id;
+ __le32 status;
+} __packed;
+
+/* ------------------------------------------------------------------
+ * Notifier chain — event driver fires it, block driver listens
+ * ------------------------------------------------------------------ */
+
+static BLOCKING_NOTIFIER_HEAD(yb9_kbdock_chain_head);
+
+static void devm_yb9_kbdock_unregister_notifier(void *data)
+{
+ struct notifier_block *nb = data;
+
+ blocking_notifier_chain_unregister(&yb9_kbdock_chain_head, nb);
+}
+
+static int devm_yb9_kbdock_register_notifier(struct device *dev,
+ struct notifier_block *nb)
+{
+ int ret;
+
+ ret = blocking_notifier_chain_register(&yb9_kbdock_chain_head, nb);
+ if (ret < 0)
+ return ret;
+
+ return devm_add_action_or_reset(dev, devm_yb9_kbdock_unregister_notifier, nb);
+}
+
+/* ------------------------------------------------------------------
+ * Block WMI driver — LENOVO_FEATURE_STATUS_DATA
+ * (owns input_dev, sysfs, PM resume)
+ * ------------------------------------------------------------------ */
+
+struct yb9_kbdock_data {
+ struct wmi_device *wdev;
+ struct input_dev *input_dev;
+ struct notifier_block nb;
+ spinlock_t lock; /* protects input_report_switch + input_sync */
+};
+
+static int yb9_kbdock_query(struct yb9_kbdock_data *d, u32 *bkbd)
+{
+ struct wmi_buffer out;
+ int ret;
+
+ ret = wmidev_query_block(d->wdev, 0, &out,
+ sizeof(struct lenovo_feature_status));
+ if (ret)
+ return ret;
+
+ struct lenovo_feature_status *fs __free(kfree) = out.data;
+
+ if (le32_to_cpu(fs->id) != YB9_FEATURE_STATUS_ID)
+ return -EIO;
+
+ *bkbd = le32_to_cpu(fs->status);
+ return 0;
+}
+
+static void yb9_kbdock_report(struct yb9_kbdock_data *d, u32 bkbd)
+{
+ int tablet = (bkbd == BKBD_DETACHED) ? 1 : 0;
+
+ spin_lock(&d->lock);
+ input_report_switch(d->input_dev, SW_TABLET_MODE, tablet);
+ input_sync(d->input_dev);
+ spin_unlock(&d->lock);
+ dev_dbg(&d->wdev->dev, "BKBD=%u SW_TABLET_MODE=%d\n", bkbd, tablet);
+}
+
+static int yb9_kbdock_sync(struct yb9_kbdock_data *d)
+{
+ u32 bkbd;
+ int ret;
+
+ ret = yb9_kbdock_query(d, &bkbd);
+ if (ret)
+ return ret;
+
+ yb9_kbdock_report(d, bkbd);
+ return 0;
+}
+
+static int yb9_kbdock_nb_call(struct notifier_block *nb,
+ unsigned long bkbd, void *unused)
+{
+ struct yb9_kbdock_data *d =
+ container_of(nb, struct yb9_kbdock_data, nb);
+
+ yb9_kbdock_report(d, bkbd);
+ return NOTIFY_DONE;
+}
+
+static ssize_t keyboard_position_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct yb9_kbdock_data *d = dev_get_drvdata(dev);
+ u32 bkbd;
+ int ret;
+
+ ret = yb9_kbdock_query(d, &bkbd);
+ if (ret)
+ return ret;
+ return sysfs_emit(buf, "%u\n", bkbd);
+}
+static DEVICE_ATTR_RO(keyboard_position);
+
+static const struct attribute * const yb9_kbdock_attrs[] = {
+ &dev_attr_keyboard_position.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(yb9_kbdock);
+
+static int yb9_kbdock_resume(struct device *dev)
+{
+ struct yb9_kbdock_data *d = dev_get_drvdata(dev);
+
+ return yb9_kbdock_sync(d);
+}
+static DEFINE_SIMPLE_DEV_PM_OPS(yb9_kbdock_pm_ops, NULL, yb9_kbdock_resume);
+
+static int yb9_kbdock_block_probe(struct wmi_device *wdev, const void *ctx)
+{
+ struct yb9_kbdock_data *d;
+ struct input_dev *input_dev;
+ int ret;
+
+ d = devm_kzalloc(&wdev->dev, sizeof(*d), GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ d->wdev = wdev;
+ spin_lock_init(&d->lock);
+
+ input_dev = devm_input_allocate_device(&wdev->dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = "Lenovo Yoga Book 9 keyboard dock switch";
+ input_dev->phys = YB9_KBDOCK_QUERY_GUID "/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
+
+ ret = input_register_device(input_dev);
+ if (ret)
+ return ret;
+
+ d->input_dev = input_dev;
+ d->nb.notifier_call = yb9_kbdock_nb_call;
+
+ ret = devm_yb9_kbdock_register_notifier(&wdev->dev, &d->nb);
+ if (ret)
+ return ret;
+
+ dev_set_drvdata(&wdev->dev, d);
+ return yb9_kbdock_sync(d);
+}
+
+static const struct wmi_device_id yb9_kbdock_block_id_table[] = {
+ { .guid_string = YB9_KBDOCK_QUERY_GUID },
+ { }
+};
+
+static struct wmi_driver yb9_kbdock_block_driver = {
+ .driver = {
+ .name = "lenovo-yb9-kbdock",
+ .dev_groups = yb9_kbdock_groups,
+ .pm = pm_sleep_ptr(&yb9_kbdock_pm_ops),
+ },
+ .id_table = yb9_kbdock_block_id_table,
+ .no_singleton = true,
+ .probe = yb9_kbdock_block_probe,
+};
+
+/* ------------------------------------------------------------------
+ * Event WMI driver — LENOVO_BTKBD_EVENT
+ * (fires the notifier chain on each WMI event)
+ * ------------------------------------------------------------------ */
+
+static void yb9_kbdock_notify_new(struct wmi_device *wdev,
+ const struct wmi_buffer *data)
+{
+ /*
+ * _WED(0xEB) returns EC.BKBD directly as a 32-bit integer
+ * (LENOVO_BTKBD_EVENT WmiDataId(1) uint32 Status).
+ * Short-buffer guard is handled by .min_event_size below.
+ */
+ u32 bkbd = le32_to_cpu(*(const __le32 *)data->data);
+
+ blocking_notifier_call_chain(&yb9_kbdock_chain_head, bkbd, NULL);
+}
+
+static const struct wmi_device_id yb9_kbdock_event_id_table[] = {
+ { .guid_string = YB9_KBDOCK_EVENT_GUID },
+ { }
+};
+MODULE_DEVICE_TABLE(wmi, yb9_kbdock_event_id_table);
+
+static struct wmi_driver yb9_kbdock_event_driver = {
+ .driver = {
+ .name = "lenovo-yb9-kbdock-event",
+ },
+ .id_table = yb9_kbdock_event_id_table,
+ .no_singleton = true,
+ .notify_new = yb9_kbdock_notify_new,
+ .min_event_size = sizeof(__le32),
+};
+
+/* ------------------------------------------------------------------
+ * Module init / exit
+ * ------------------------------------------------------------------ */
+
+static const struct dmi_system_id yb9_kbdock_dmi_table[] __initconst = {
+ {
+ /* Lenovo Yoga Book 9 14IAH10 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
+ },
+ },
+ { }
+};
+
+static int __init yb9_kbdock_init(void)
+{
+ int ret;
+
+ if (!dmi_check_system(yb9_kbdock_dmi_table))
+ return -ENODEV;
+
+ ret = wmi_driver_register(&yb9_kbdock_event_driver);
+ if (ret)
+ return ret;
+
+ ret = wmi_driver_register(&yb9_kbdock_block_driver);
+ if (ret) {
+ wmi_driver_unregister(&yb9_kbdock_event_driver);
+ return ret;
+ }
+
+ return 0;
+}
+module_init(yb9_kbdock_init);
+
+static void __exit yb9_kbdock_exit(void)
+{
+ wmi_driver_unregister(&yb9_kbdock_block_driver);
+ wmi_driver_unregister(&yb9_kbdock_event_driver);
+}
+module_exit(yb9_kbdock_exit);
+
+MODULE_AUTHOR("Dave Carey <carvsdriver@gmail.com>");
+MODULE_DESCRIPTION("Lenovo Yoga Book 9 keyboard dock detection");
+MODULE_LICENSE("GPL");
2.47.0
^ permalink raw reply related [flat|nested] 5+ messages in thread