* [PATCH v12 0/2] Lenovo Yoga Book 9 keyboard dock detection
@ 2026-07-13 14:31 Dave Carey
2026-07-13 14:31 ` [PATCH v12 1/2] platform/x86/lenovo: lenovo-ymc: Suppress probe on Yoga Book 9 14IAH10 Dave Carey
2026-07-13 14:31 ` [PATCH v12 2/2] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Dave Carey
0 siblings, 2 replies; 4+ messages in thread
From: Dave Carey @ 2026-07-13 14:31 UTC (permalink / raw)
To: platform-driver-x86
Cc: ilpo.jarvinen, W_Armin, johannes.goede, linux-kernel, Dave Carey
Changes in v12 (addressing Ilpo Järvinen's v11 review of patch 2/2):
- Add #include <linux/sysfs.h> (for sysfs_emit)
- Add #include <linux/types.h> (for __le32 and __packed)
Changes in v11 (addressing Armin Wolf's v10 review of patch 2/2):
- 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.
Patch 1/2 (lenovo-ymc) is unchanged from v10.
Changes in v10 (addressing Armin Wolf's v9 review of patch 2/2):
- Update ABI doc: Date: June 2026, KernelVersion: 6.16
- Add #include <linux/compiler_attributes.h> and <linux/types.h>
- yb9_kbdock_query(): change return to pointer-out u32 *bkbd to avoid
a 0xFFFFFFFF status value being misread as -EPERM
- struct yb9_kbdock_data: add spinlock_t lock
- yb9_kbdock_report(): hold lock across input_report_switch + input_sync
- yb9_kbdock_sync(): return int, propagate query error to callers
- yb9_kbdock_resume(), yb9_kbdock_block_probe(): propagate sync error
- struct wmi_buffer out: remove zero-initializer (not needed)
- yb9_kbdock_notify_new(): drop manual length check; use
.min_event_size = sizeof(__le32) in struct wmi_driver instead
Patch 1/2 (lenovo-ymc) is unchanged from v9.
Dave Carey (2):
platform/x86/lenovo: lenovo-ymc: Suppress probe on Yoga Book 9 14IAH10
platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
Documentation/ABI/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 ++++++++++++
drivers/platform/x86/lenovo/ymc.c | 19 ++
6 files changed, 382 insertions(+)
2.47.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v12 1/2] platform/x86/lenovo: lenovo-ymc: Suppress probe on Yoga Book 9 14IAH10 2026-07-13 14:31 [PATCH v12 0/2] Lenovo Yoga Book 9 keyboard dock detection Dave Carey @ 2026-07-13 14:31 ` Dave Carey 2026-07-13 14:31 ` [PATCH v12 2/2] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Dave Carey 1 sibling, 0 replies; 4+ messages in thread From: Dave Carey @ 2026-07-13 14:31 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] 4+ messages in thread
* [PATCH v12 2/2] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver 2026-07-13 14:31 [PATCH v12 0/2] Lenovo Yoga Book 9 keyboard dock detection Dave Carey 2026-07-13 14:31 ` [PATCH v12 1/2] platform/x86/lenovo: lenovo-ymc: Suppress probe on Yoga Book 9 14IAH10 Dave Carey @ 2026-07-13 14:31 ` Dave Carey 1 sibling, 0 replies; 4+ messages in thread From: Dave Carey @ 2026-07-13 14:31 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] 4+ messages in thread
[parent not found: <20260623125914.833063-1-carvsdriver@gmail.com>]
* [PATCH v12 0/2] Lenovo Yoga Book 9 keyboard dock detection [not found] <20260623125914.833063-1-carvsdriver@gmail.com> @ 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 0 siblings, 1 reply; 4+ 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 Changes in v12 (addressing Ilpo Järvinen's v11 review of patch 2/2): - Add #include <linux/sysfs.h> (for sysfs_emit) - Add #include <linux/types.h> (for __le32 and __packed) Changes in v11 (addressing Armin Wolf's v10 review of patch 2/2): - 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. Patch 1/2 (lenovo-ymc) is unchanged from v10. Changes in v10 (addressing Armin Wolf's v9 review of patch 2/2): - Update ABI doc: Date: June 2026, KernelVersion: 6.16 - Add #include <linux/compiler_attributes.h> and <linux/types.h> - yb9_kbdock_query(): change return to pointer-out u32 *bkbd to avoid a 0xFFFFFFFF status value being misread as -EPERM - struct yb9_kbdock_data: add spinlock_t lock - yb9_kbdock_report(): hold lock across input_report_switch + input_sync - yb9_kbdock_sync(): return int, propagate query error to callers - yb9_kbdock_resume(), yb9_kbdock_block_probe(): propagate sync error - struct wmi_buffer out: remove zero-initializer (not needed) - yb9_kbdock_notify_new(): drop manual length check; use .min_event_size = sizeof(__le32) in struct wmi_driver instead Patch 1/2 (lenovo-ymc) is unchanged from v9. Dave Carey (2): platform/x86/lenovo: lenovo-ymc: Suppress probe on Yoga Book 9 14IAH10 platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Documentation/ABI/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 ++++++++++++ drivers/platform/x86/lenovo/ymc.c | 19 ++ 6 files changed, 382 insertions(+) 2.47.0 ^ permalink raw reply [flat|nested] 4+ 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 ` Dave Carey 0 siblings, 0 replies; 4+ 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] 4+ messages in thread
end of thread, other threads:[~2026-07-13 14:31 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 14:31 [PATCH v12 0/2] Lenovo Yoga Book 9 keyboard dock detection Dave Carey
2026-07-13 14:31 ` [PATCH v12 1/2] platform/x86/lenovo: lenovo-ymc: Suppress probe on Yoga Book 9 14IAH10 Dave Carey
2026-07-13 14:31 ` [PATCH v12 2/2] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Dave Carey
[not found] <20260623125914.833063-1-carvsdriver@gmail.com>
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 2/2] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Dave Carey
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox