All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
@ 2026-04-25 13:23 Dave Carey
  2026-04-28 14:39 ` Ilpo Järvinen
  0 siblings, 1 reply; 10+ messages in thread
From: Dave Carey @ 2026-04-25 13:23 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: linux-kernel, linux-input, Hans de Goede, Ilpo Järvinen,
	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).

The current BKBD state is read via WMI query GUID
E7F300FA-21CD-4003-ADAC-2696135982E6 (WQAF method), which returns an
8-byte buffer: bytes [0..3] hold the LFID constant 0x00060000 and bytes
[4..7] hold the BKBD value.

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 as a WMI driver on the event GUID.
  - Queries BKBD state on probe and on each WMI notification.
  - Reports SW_TABLET_MODE=1 when detached, SW_TABLET_MODE=0 when docked
    in either position (a physical keyboard is present in both cases).
  - Exposes the raw BKBD value via a read-only sysfs attribute
    "keyboard_position" for use by userspace (e.g. to distinguish between
    the two docked positions for different UI layouts).

Tested on: Lenovo Yoga Book 9 14IAH10 (model 83KJ), kernel 6.19.

Signed-off-by: Dave Carey <carvsdriver@gmail.com>
---
 .../testing/sysfs-driver-lenovo-yb9-kbdock    |  21 ++
 MAINTAINERS                                   |   6 +
 drivers/platform/x86/lenovo/Kconfig           |  14 ++
 drivers/platform/x86/lenovo/Makefile          |   1 +
 drivers/platform/x86/lenovo/yb9-kbdock.c      | 216 ++++++++++++++++++
 5 files changed, 258 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
 create mode 100644 MAINTAINERS
 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..bb57690
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
@@ -0,0 +1,21 @@
+What:		/sys/bus/wmi/drivers/lenovo-yb9-kbdock/<guid>/keyboard_position
+Date:		April 2026
+KernelVersion:	6.10
+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   detached  — keyboard is not docked to any screen
+		1   top-half  — keyboard docked on the top half of the bottom screen
+		2   bottom-half — keyboard docked on the bottom half of the bottom screen
+		==  ============================================================
+
+		The value is formatted as "<n> (<name>)\n", e.g. "1 (top-half)\n".
+
+		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
new file mode 100644
index 0000000..cb765b4
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,6 @@
+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
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_LMI)	+= think-lmi.o
 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..693e287
--- /dev/null
+++ b/drivers/platform/x86/lenovo/yb9-kbdock.c
@@ -0,0 +1,216 @@
+// 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.
+ *
+ * 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
+ *
+ * This driver registers for the WMI event GUID, queries BKBD on probe and on
+ * each event, reports SW_TABLET_MODE=0 when the keyboard is docked (either
+ * position) and SW_TABLET_MODE=1 when detached, and exposes the raw BKBD
+ * value in sysfs as "keyboard_position".
+ *
+ * Copyright (C) 2026 Dave Carey <carvsdriver@gmail.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/wmi.h>
+
+/*
+ * WM10 ACPI device (_UID "GMZN"):
+ *   Event GUID — notify ID 0xEB fires on keyboard attachment change.
+ *   Query GUID — object "AF", maps to WQAF(); returns 8-byte buffer
+ *                {LFID=0x00060000, BKBD[31:0]}.
+ */
+#define YB9_KBDOCK_EVENT_GUID	"806BD2A2-177B-481D-BFB5-3BA0BB4A2285"
+#define YB9_KBDOCK_QUERY_GUID	"E7F300FA-21CD-4003-ADAC-2696135982E6"
+
+#define YB9_KBDOCK_QUERY_INSTANCE	0
+
+/* BKBD encoding — keyboard always docks on the bottom screen */
+#define BKBD_DETACHED		0
+#define BKBD_TOP_HALF		1	/* docked on top half of bottom screen */
+#define BKBD_BOTTOM_HALF	2	/* docked on bottom half of bottom screen */
+
+static const struct dmi_system_id yb9_kbdock_dmi_table[] = {
+	{
+		/* Lenovo Yoga Book 9 14IAH10 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR,   "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
+		},
+	},
+	{ }
+};
+
+struct yb9_kbdock_priv {
+	struct input_dev *input_dev;
+	unsigned int bkbd;	/* last read BKBD value (0-3) */
+};
+
+/* Read current BKBD state via WQAF.  Returns 0-3 or -errno. */
+static int yb9_kbdock_query(struct wmi_device *wdev)
+{
+	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *obj;
+	acpi_status status;
+	u32 bkbd;
+
+	status = wmi_query_block(YB9_KBDOCK_QUERY_GUID,
+				 YB9_KBDOCK_QUERY_INSTANCE, &out);
+	if (ACPI_FAILURE(status)) {
+		dev_warn(&wdev->dev, "WQAF query failed: %s\n",
+			 acpi_format_exception(status));
+		return -EIO;
+	}
+
+	obj = out.pointer;
+	if (!obj) {
+		dev_warn(&wdev->dev, "WQAF returned NULL\n");
+		return -EIO;
+	}
+
+	/*
+	 * WQAF returns an 8-byte buffer: bytes [0..3] = LFID (0x00060000),
+	 * bytes [4..7] = BKBD value.  Guard against short buffers.
+	 */
+	if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length >= 8) {
+		memcpy(&bkbd, obj->buffer.pointer + 4, sizeof(bkbd));
+		bkbd &= 0x3;
+	} else if (obj->type == ACPI_TYPE_INTEGER) {
+		bkbd = obj->integer.value & 0x3;
+	} else {
+		dev_warn(&wdev->dev,
+			 "WQAF: unexpected result type %d len %u\n",
+			 obj->type,
+			 obj->type == ACPI_TYPE_BUFFER
+				? obj->buffer.length : 0);
+		kfree(obj);
+		return -EIO;
+	}
+
+	kfree(obj);
+	return (int)bkbd;
+}
+
+static void yb9_kbdock_update(struct wmi_device *wdev)
+{
+	struct yb9_kbdock_priv *priv = dev_get_drvdata(&wdev->dev);
+	int bkbd;
+	int tablet_mode;
+
+	bkbd = yb9_kbdock_query(wdev);
+	if (bkbd < 0)
+		return;
+
+	priv->bkbd = bkbd;
+
+	/*
+	 * Report tablet mode only when the keyboard is fully detached.
+	 * Both docked positions (top-half and bottom-half of the bottom screen)
+	 * indicate a physical keyboard is present — report laptop mode.
+	 */
+	tablet_mode = (bkbd == BKBD_DETACHED) ? 1 : 0;
+
+	input_report_switch(priv->input_dev, SW_TABLET_MODE, tablet_mode);
+	input_sync(priv->input_dev);
+
+	dev_dbg(&wdev->dev, "BKBD=%u tablet_mode=%d\n", bkbd, tablet_mode);
+}
+
+static void yb9_kbdock_notify(struct wmi_device *wdev, union acpi_object *data)
+{
+	yb9_kbdock_update(wdev);
+}
+
+/* sysfs: keyboard_position — exposes raw BKBD value */
+static ssize_t keyboard_position_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct yb9_kbdock_priv *priv = dev_get_drvdata(dev);
+	static const char * const names[] = {
+		"detached", "top-half", "bottom-half", "unknown"
+	};
+	unsigned int bkbd = priv->bkbd;
+
+	if (bkbd > 3)
+		bkbd = 3;
+	return sysfs_emit(buf, "%u (%s)\n", bkbd, names[bkbd]);
+}
+static DEVICE_ATTR_RO(keyboard_position);
+
+static struct attribute *yb9_kbdock_attrs[] = {
+	&dev_attr_keyboard_position.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(yb9_kbdock);
+
+static int yb9_kbdock_probe(struct wmi_device *wdev, const void *ctx)
+{
+	struct yb9_kbdock_priv *priv;
+	struct input_dev *input_dev;
+	int err;
+
+	if (!dmi_check_system(yb9_kbdock_dmi_table)) {
+		dev_dbg(&wdev->dev, "not a Yoga Book 9, skipping\n");
+		return -ENODEV;
+	}
+
+	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	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_EVENT_GUID "/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
+
+	err = input_register_device(input_dev);
+	if (err) {
+		dev_err(&wdev->dev, "failed to register input device: %d\n", err);
+		return err;
+	}
+
+	priv->input_dev = input_dev;
+	dev_set_drvdata(&wdev->dev, priv);
+
+	/* Report initial state */
+	yb9_kbdock_update(wdev);
+	return 0;
+}
+
+static const struct wmi_device_id yb9_kbdock_wmi_id_table[] = {
+	{ .guid_string = YB9_KBDOCK_EVENT_GUID },
+	{ }
+};
+MODULE_DEVICE_TABLE(wmi, yb9_kbdock_wmi_id_table);
+
+static struct wmi_driver yb9_kbdock_driver = {
+	.driver = {
+		.name = "lenovo-yb9-kbdock",
+		.dev_groups = yb9_kbdock_groups,
+	},
+	.id_table = yb9_kbdock_wmi_id_table,
+	.probe    = yb9_kbdock_probe,
+	.notify   = yb9_kbdock_notify,
+};
+module_wmi_driver(yb9_kbdock_driver);
+
+MODULE_AUTHOR("Dave Carey <carvsdriver@gmail.com>");
+MODULE_DESCRIPTION("Lenovo Yoga Book 9 keyboard dock detection");
+MODULE_LICENSE("GPL");
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
  2026-04-25 13:23 [PATCH] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Dave Carey
@ 2026-04-28 14:39 ` Ilpo Järvinen
  2026-05-17 15:01   ` Dave Carey
  2026-05-17 15:02   ` [PATCH v2] platform/x86/lenovo: add Yoga Book 9 keyboard dock driver Dave Carey
  0 siblings, 2 replies; 10+ messages in thread
From: Ilpo Järvinen @ 2026-04-28 14:39 UTC (permalink / raw)
  To: Dave Carey, Hans de Goede, Pit Henrich
  Cc: platform-driver-x86, LKML, linux-input

[-- Attachment #1: Type: text/plain, Size: 14388 bytes --]

On Sat, 25 Apr 2026, Dave Carey wrote:

> 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).

Please put this in depth explanation in own paragraph.

> The current BKBD state is read via WMI query GUID
> E7F300FA-21CD-4003-ADAC-2696135982E6 (WQAF method), 

This seems mostly duplicate of what was said previously.

>which returns an
> 8-byte buffer: bytes [0..3] hold the LFID constant 0x00060000 and bytes
> [4..7] hold the BKBD value.
>
> 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)

These two can be combined with the in depth explanation paragraph.

> This driver:
>   - Registers as a WMI driver on the event GUID.

Unnecessary / obvious.

>   - Queries BKBD state on probe and on each WMI notification.
>   - Reports SW_TABLET_MODE=1 when detached, SW_TABLET_MODE=0 when docked
>     in either position (a physical keyboard is present in both cases).
>   - Exposes the raw BKBD value via a read-only sysfs attribute
>     "keyboard_position" for use by userspace (e.g. to distinguish between
>     the two docked positions for different UI layouts).

Please write this without bullet points. Bullet points usually break 
relationships between sentences.

> Tested on: Lenovo Yoga Book 9 14IAH10 (model 83KJ), kernel 6.19.
> 
> Signed-off-by: Dave Carey <carvsdriver@gmail.com>
> ---
>  .../testing/sysfs-driver-lenovo-yb9-kbdock    |  21 ++
>  MAINTAINERS                                   |   6 +
>  drivers/platform/x86/lenovo/Kconfig           |  14 ++
>  drivers/platform/x86/lenovo/Makefile          |   1 +
>  drivers/platform/x86/lenovo/yb9-kbdock.c      | 216 ++++++++++++++++++
>  5 files changed, 258 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
>  create mode 100644 MAINTAINERS
>  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..bb57690
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
> @@ -0,0 +1,21 @@
> +What:		/sys/bus/wmi/drivers/lenovo-yb9-kbdock/<guid>/keyboard_position
> +Date:		April 2026
> +KernelVersion:	6.10
> +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   detached  — keyboard is not docked to any screen
> +		1   top-half  — keyboard docked on the top half of the bottom screen
> +		2   bottom-half — keyboard docked on the bottom half of the bottom screen
> +		==  ============================================================

Interesting, I wonder if this is similar physically to what is being added 
here:

https://lore.kernel.org/all/20260419102724.91451-1-pithenrich2d@gmail.com/

?

If yes, we may have to take another look at how to create the interface 
for this.

You didn't document unknown but return it (maybe it should return some 
-Exx code instead?).

> +		The value is formatted as "<n> (<name>)\n", e.g. "1 (top-half)\n".
> +
> +		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
> new file mode 100644
> index 0000000..cb765b4
> --- /dev/null
> +++ b/MAINTAINERS
> @@ -0,0 +1,6 @@
> +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
> 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_LMI)	+= think-lmi.o
>  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..693e287
> --- /dev/null
> +++ b/drivers/platform/x86/lenovo/yb9-kbdock.c
> @@ -0,0 +1,216 @@
> +// 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.
> + *
> + * 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
>
> + * This driver registers for the WMI event GUID, queries BKBD on probe and on
> + * each event, reports SW_TABLET_MODE=0 when the keyboard is docked (either
> + * position) and SW_TABLET_MODE=1 when detached, and exposes the raw BKBD
> + * value in sysfs as "keyboard_position".

I don't think the functional description on this level is warranted in the 
top comment (may place it below where you define things but if naming is 
obvious, some comments may not even be necessary).

> + * Copyright (C) 2026 Dave Carey <carvsdriver@gmail.com>
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/dmi.h>
> +#include <linux/input.h>
> +#include <linux/module.h>
> +#include <linux/wmi.h>
> +
> +/*
> + * WM10 ACPI device (_UID "GMZN"):
> + *   Event GUID — notify ID 0xEB fires on keyboard attachment change.
> + *   Query GUID — object "AF", maps to WQAF(); returns 8-byte buffer
> + *                {LFID=0x00060000, BKBD[31:0]}.
> + */
> +#define YB9_KBDOCK_EVENT_GUID	"806BD2A2-177B-481D-BFB5-3BA0BB4A2285"
> +#define YB9_KBDOCK_QUERY_GUID	"E7F300FA-21CD-4003-ADAC-2696135982E6"
> +
> +#define YB9_KBDOCK_QUERY_INSTANCE	0
> +
> +/* BKBD encoding — keyboard always docks on the bottom screen */
> +#define BKBD_DETACHED		0
> +#define BKBD_TOP_HALF		1	/* docked on top half of bottom screen */
> +#define BKBD_BOTTOM_HALF	2	/* docked on bottom half of bottom screen */
> +
> +static const struct dmi_system_id yb9_kbdock_dmi_table[] = {
> +	{
> +		/* Lenovo Yoga Book 9 14IAH10 */
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR,   "LENOVO"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
> +		},
> +	},
> +	{ }
> +};

Normally these appear towards the end of the file.

> +
> +struct yb9_kbdock_priv {
> +	struct input_dev *input_dev;
> +	unsigned int bkbd;	/* last read BKBD value (0-3) */
> +};
> +
> +/* Read current BKBD state via WQAF.  Returns 0-3 or -errno. */
> +static int yb9_kbdock_query(struct wmi_device *wdev)
> +{
> +	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
> +	union acpi_object *obj;
> +	acpi_status status;
> +	u32 bkbd;
> +
> +	status = wmi_query_block(YB9_KBDOCK_QUERY_GUID,
> +				 YB9_KBDOCK_QUERY_INSTANCE, &out);

This interface has been deprecated.

> +	if (ACPI_FAILURE(status)) {
> +		dev_warn(&wdev->dev, "WQAF query failed: %s\n",
> +			 acpi_format_exception(status));
> +		return -EIO;
> +	}
> +
> +	obj = out.pointer;
> +	if (!obj) {
> +		dev_warn(&wdev->dev, "WQAF returned NULL\n");
> +		return -EIO;
> +	}
> +
> +	/*
> +	 * WQAF returns an 8-byte buffer: bytes [0..3] = LFID (0x00060000),
> +	 * bytes [4..7] = BKBD value.  Guard against short buffers.

One space is enough.

> +	 */
> +	if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length >= 8) {
> +		memcpy(&bkbd, obj->buffer.pointer + 4, sizeof(bkbd));
> +		bkbd &= 0x3;

Can this literal be named with a define? Should it use FIELD_GET() 
(don't forget the header if you start to use FIELD_GET())?

> +	} else if (obj->type == ACPI_TYPE_INTEGER) {
> +		bkbd = obj->integer.value & 0x3;

Same question here.

> +	} else {
> +		dev_warn(&wdev->dev,
> +			 "WQAF: unexpected result type %d len %u\n",
> +			 obj->type,
> +			 obj->type == ACPI_TYPE_BUFFER
> +				? obj->buffer.length : 0);

Put the last two lines to one line.

> +		kfree(obj);
> +		return -EIO;
> +	}
> +
> +	kfree(obj);

Please use __free() instead of duplicating kfree()s.

When converting to __free(), don't use ... = NULL; pattern, instead place 
the variable declaration mid-function as instructed in the long comment in 
cleanup.h.

> +	return (int)bkbd;

Unnecessary cast. And your types are a major mess between int and 
unsigned types.

> +}
> +
> +static void yb9_kbdock_update(struct wmi_device *wdev)
> +{
> +	struct yb9_kbdock_priv *priv = dev_get_drvdata(&wdev->dev);
> +	int bkbd;
> +	int tablet_mode;

Please use reverse-xmas tree order where there are no internal 
dependencies between local variables that prevent usinbg it.

> +
> +	bkbd = yb9_kbdock_query(wdev);
> +	if (bkbd < 0)
> +		return;
> +
> +	priv->bkbd = bkbd;
> +
> +	/*
> +	 * Report tablet mode only when the keyboard is fully detached.
> +	 * Both docked positions (top-half and bottom-half of the bottom screen)
> +	 * indicate a physical keyboard is present — report laptop mode.
> +	 */
> +	tablet_mode = (bkbd == BKBD_DETACHED) ? 1 : 0;
> +
> +	input_report_switch(priv->input_dev, SW_TABLET_MODE, tablet_mode);
> +	input_sync(priv->input_dev);
> +
> +	dev_dbg(&wdev->dev, "BKBD=%u tablet_mode=%d\n", bkbd, tablet_mode);

Missing include.

> +}
> +
> +static void yb9_kbdock_notify(struct wmi_device *wdev, union acpi_object *data)
> +{
> +	yb9_kbdock_update(wdev);
> +}
> +
> +/* sysfs: keyboard_position — exposes raw BKBD value */
> +static ssize_t keyboard_position_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf)
> +{
> +	struct yb9_kbdock_priv *priv = dev_get_drvdata(dev);
> +	static const char * const names[] = {
> +		"detached", "top-half", "bottom-half", "unknown"

Use comma for all non-terminating entries.

> +	};
> +	unsigned int bkbd = priv->bkbd;
> +
> +	if (bkbd > 3)

How can this happen without it being bug in the driver? Didn't you mask it 
when reading the value?

So maybe

	if (WARN_ON_ONCE(bkbd > 3))
		return -EINVAL;

> +		bkbd = 3;
> +	return sysfs_emit(buf, "%u (%s)\n", bkbd, names[bkbd]);
> +}
> +static DEVICE_ATTR_RO(keyboard_position);
> +
> +static struct attribute *yb9_kbdock_attrs[] = {
> +	&dev_attr_keyboard_position.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(yb9_kbdock);
> +
> +static int yb9_kbdock_probe(struct wmi_device *wdev, const void *ctx)
> +{
> +	struct yb9_kbdock_priv *priv;
> +	struct input_dev *input_dev;
> +	int err;
> +
> +	if (!dmi_check_system(yb9_kbdock_dmi_table)) {
> +		dev_dbg(&wdev->dev, "not a Yoga Book 9, skipping\n");
> +		return -ENODEV;
> +	}
> +
> +	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	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_EVENT_GUID "/input0";
> +	input_dev->id.bustype = BUS_HOST;
> +	input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
> +
> +	err = input_register_device(input_dev);
> +	if (err) {
> +		dev_err(&wdev->dev, "failed to register input device: %d\n", err);
> +		return err;
> +	}
> +
> +	priv->input_dev = input_dev;
> +	dev_set_drvdata(&wdev->dev, priv);
> +
> +	/* Report initial state */
> +	yb9_kbdock_update(wdev);
> +	return 0;
> +}
> +
> +static const struct wmi_device_id yb9_kbdock_wmi_id_table[] = {
> +	{ .guid_string = YB9_KBDOCK_EVENT_GUID },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(wmi, yb9_kbdock_wmi_id_table);
> +
> +static struct wmi_driver yb9_kbdock_driver = {
> +	.driver = {
> +		.name = "lenovo-yb9-kbdock",
> +		.dev_groups = yb9_kbdock_groups,
> +	},
> +	.id_table = yb9_kbdock_wmi_id_table,
> +	.probe    = yb9_kbdock_probe,
> +	.notify   = yb9_kbdock_notify,
> +};
> +module_wmi_driver(yb9_kbdock_driver);
> +
> +MODULE_AUTHOR("Dave Carey <carvsdriver@gmail.com>");
> +MODULE_DESCRIPTION("Lenovo Yoga Book 9 keyboard dock detection");
> +MODULE_LICENSE("GPL");
> 

-- 
 i.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
  2026-04-28 14:39 ` Ilpo Järvinen
@ 2026-05-17 15:01   ` Dave Carey
  2026-05-17 15:02   ` [PATCH v2] platform/x86/lenovo: add Yoga Book 9 keyboard dock driver Dave Carey
  1 sibling, 0 replies; 10+ messages in thread
From: Dave Carey @ 2026-05-17 15:01 UTC (permalink / raw)
  To: ilpo.jarvinen
  Cc: hdegoede, pithenrich2d, mpearson-lenovo, derekjohn.clark, W_Armin,
	platform-driver-x86, linux-input, linux-kernel, Dave Carey

On Tue, 28 Apr 2026 17:39:17 +0300, Ilpo Järvinen wrote:
> On Sat, 25 Apr 2026, Dave Carey wrote:

Thank you for the review. All points addressed in v2 below.

> Please put this in depth explanation in own paragraph.
> This seems mostly duplicate of what was said previously.
> These two can be combined with the in depth explanation paragraph.
> Please write this without bullet points.

Commit message rewritten as prose. The hardware description, WMI
mechanism, and BKBD encoding are now combined into a single explanatory
paragraph. The functional summary (what the driver does) follows in a
second paragraph without bullet points.

> I don't think the functional description on this level is warranted in
> the top comment.

Top-of-file block comment trimmed to hardware context only. The
functional description (query on probe, SW_TABLET_MODE mapping, sysfs
attribute) has been removed from there; the comment now covers only the
two WMI GUIDs and the BKBD encoding table, which are non-obvious from
the code alone.

> This interface has been deprecated.

Done. v2 uses wmidev_block_query() with two arguments, returning
union acpi_object * directly. This required registering both the event
and query GUIDs in the id_table with context pointers (enum
yb9_guid_type) so the query wdev is reachable from probe and the notify
path. The event-device probe defers with -EPROBE_DEFER until the query
device arrives.

> Please use __free() instead of duplicating kfree()s.
> When converting to __free(), don't use ... = NULL; pattern, instead place
> the variable declaration mid-function as instructed in cleanup.h.

Done. The obj declaration now uses __free(kfree) placed mid-function
after the early-exit checks, per the cleanup.h guidance. Added
linux/cleanup.h to the includes.

> Can this literal be named with a define? Should it use FIELD_GET()?
> (don't forget the header if you start to use FIELD_GET())

Done. BKBD_MASK is now GENMASK(1, 0) and both extraction sites use
FIELD_GET(BKBD_MASK, bkbd). Added linux/bitfield.h to the includes.

> Unnecessary cast. And your types are a major mess between int and
> unsigned types.

Fixed. The query function returns int throughout (0-2 on success,
-errno on error); the cast is gone. Local variables in yb9_kbdock_update
are consistently int.

> Please use reverse-xmas tree order.

Fixed in yb9_kbdock_update(): tablet_mode is now declared before bkbd.

> WARN_ON_ONCE() instead of just WARN_ON() in the sysfs show function.

Done.

> You didn't document unknown but return it (maybe it should return some
> -Exx code instead?).

The "unknown" fourth entry has been removed entirely. BKBD value 3 is
now caught in yb9_kbdock_query() and returned as -EINVAL so it never
reaches the sysfs show function or priv->bkbd. The show function uses
WARN_ON_ONCE(bkbd >= ARRAY_SIZE(names)) and returns -EINVAL as
suggested — this can only fire if there is a driver bug.

> Missing include.

Added linux/bitfield.h (FIELD_GET) and linux/cleanup.h (__free).

> Normally these [DMI table] appear towards the end of the file.

Moved to just before yb9_kbdock_probe().

> Put the last two lines to one line.

Done.

> I wonder if this is similar physically to what is being added here:
> https://lore.kernel.org/all/20260419102724.91451-1-pithenrich2d@gmail.com/
> If yes, we may have to take another look at how to create the interface
> for this.

Pit Henrich's patch targets the ThinkPad X1 Fold 16 Gen 1 — also a
Lenovo device with a magnetically-attached keyboard that docks to the
display and changes between tablet and laptop mode, so the concept is
physically similar.

However, the two drivers differ in several meaningful ways:

  * Different hardware families and kernel paths: the X1 Fold patch
    extends thinkpad_acpi using an ACPI method (\\_SB.DEVD.GDST) and
    the existing TP_HKEY_EV_TABLET_CHANGED hotkey path. The Yoga Book 9
    driver is a standalone WMI driver using two separate WMI GUIDs.

  * Different state cardinality: the X1 Fold has a binary
    keyboard_attached_on_screen attribute because the keyboard is either
    present or not. The Yoga Book 9 needs three states — detached,
    docked on the top half of the bottom screen, or docked on the bottom
    half — because the two docked positions select different screen
    layouts that userspace needs to distinguish. A binary attribute
    would lose that information.

  * SW_TABLET_MODE: both drivers emit SW_TABLET_MODE=1 when the
    keyboard is absent and SW_TABLET_MODE=0 when docked, consistent
    with existing drivers in this subsystem.

Given these differences the sysfs attribute semantics do not clash, but
if a preferred naming convention is being established for keyboard-dock
attributes across these devices I am happy to align with whatever is
decided for the X1 Fold patch.

Dave

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2] platform/x86/lenovo: add Yoga Book 9 keyboard dock driver
  2026-04-28 14:39 ` Ilpo Järvinen
  2026-05-17 15:01   ` Dave Carey
@ 2026-05-17 15:02   ` Dave Carey
  2026-05-17 15:25     ` sashiko-bot
  2026-05-19  0:02     ` [PATCH v3] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Dave Carey
  1 sibling, 2 replies; 10+ messages in thread
From: Dave Carey @ 2026-05-17 15:02 UTC (permalink / raw)
  To: ilpo.jarvinen
  Cc: hdegoede, pithenrich2d, mpearson-lenovo, derekjohn.clark, W_Armin,
	platform-driver-x86, linux-input, linux-kernel, Dave Carey

The Lenovo Yoga Book 9 14IAH10 (83KJ) ships with a detachable Bluetooth
keyboard that magnetically attaches to the bottom screen in one of two
positions.

The Embedded Controller tracks the attachment state in a 2-bit field called
BKBD (byte 0x23 of EC RAM, bits 4-5) and signals changes via EC query _QB0,
which calls Notify(WM10, 0xEB) on the WM10 ACPI WMI device (_UID "GMZN").
The WMI event GUID 806BD2A2-177B-481D-BFB5-3BA0BB4A2285 fires on every
state change.  The current state is readable at any time via WMI data block
E7F300FA-21CD-4003-ADAC-2696135982E6 (WQAF), which returns an 8-byte buffer
whose upper four bytes hold the BKBD value.  BKBD encodes the attachment
state as: 0 = keyboard detached, 1 = docked on the top half of the bottom
screen, 2 = docked on the bottom half, 3 = reserved (treated as an error by
this driver).

Add a new driver, lenovo-yb9-kbdock, that queries the BKBD state on probe
and on each WMI event notification.  Both docked positions indicate a
physical keyboard is present and report SW_TABLET_MODE=0 (laptop mode);
keyboard detached reports SW_TABLET_MODE=1 (tablet mode).  The raw BKBD
value is additionally exposed via the read-only sysfs attribute
"keyboard_position" for userspace that needs to distinguish the two docked
positions (e.g. to choose a different UI layout).

Tested on Lenovo Yoga Book 9 14IAH10 (83KJ): all three BKBD states
(detached, top-half dock, bottom-half dock) reported correctly;
SW_TABLET_MODE transitions verified with evtest.

Signed-off-by: Dave Carey <carvsdriver@gmail.com>
---
 drivers/platform/x86/lenovo/Kconfig       | 14 ++++
 drivers/platform/x86/lenovo/Makefile      |  1 +
 drivers/platform/x86/lenovo/yb9-kbdock.c | 233 +++++++++++++++++++++
 3 files changed, 248 insertions(+)
 create mode 100644 drivers/platform/x86/lenovo/yb9-kbdock.c

--- a/drivers/platform/x86/lenovo/Kconfig
+++ b/drivers/platform/x86/lenovo/Kconfig
@@ -43,6 +43,20 @@
 	  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
--- 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
--- a/drivers/platform/x86/lenovo/yb9-kbdock.c
+++ b/drivers/platform/x86/lenovo/yb9-kbdock.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Lenovo Yoga Book 9 keyboard-dock detection
+ *
+ * Reports SW_TABLET_MODE based on keyboard attachment state and exposes the
+ * raw dock position via sysfs.
+ *
+ * Copyright (C) 2026 Dave Carey <carvsdriver@gmail.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/dmi.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/wmi.h>
+
+/*
+ * WM10 ACPI device (_UID "GMZN") exposes two relevant WMI GUIDs:
+ *   YB9_KBDOCK_EVENT_GUID — notify ID 0xEB fires on attachment state change.
+ *   YB9_KBDOCK_QUERY_GUID — object "AF" (WQAF), returns an 8-byte buffer
+ *                           whose upper four bytes hold the BKBD value.
+ *
+ * BKBD encoding:
+ *   0 (BKBD_DETACHED)    — keyboard detached       → SW_TABLET_MODE = 1
+ *   1 (BKBD_TOP_HALF)    — docked, top half        → SW_TABLET_MODE = 0
+ *   2 (BKBD_BOTTOM_HALF) — docked, bottom half     → SW_TABLET_MODE = 0
+ *   3                    — reserved; treated as an error
+ */
+#define YB9_KBDOCK_EVENT_GUID		"806BD2A2-177B-481D-BFB5-3BA0BB4A2285"
+#define YB9_KBDOCK_QUERY_GUID		"E7F300FA-21CD-4003-ADAC-2696135982E6"
+#define YB9_KBDOCK_QUERY_INSTANCE	0
+
+#define BKBD_DETACHED		0
+#define BKBD_TOP_HALF		1
+#define BKBD_BOTTOM_HALF	2
+#define BKBD_MASK		GENMASK(1, 0)
+
+/* Distinguish the two GUIDs via the id_table context field. */
+enum yb9_guid_type { YB9_GUID_EVENT, YB9_GUID_QUERY };
+
+/*
+ * Both GUIDs are children of the same ACPI device (WM10).  Store the query
+ * WMI device globally so the event-device probe and notify path can reach it
+ * via wmidev_block_query().  Protected by yb9_query_lock during probe/remove.
+ */
+static struct wmi_device *yb9_query_wdev;
+static DEFINE_MUTEX(yb9_query_lock);
+
+struct yb9_kbdock_priv {
+	struct wmi_device *query_wdev;
+	struct input_dev *input_dev;
+	unsigned int bkbd;
+};
+
+/* Returns 0–2 on success, -errno on error. */
+static int yb9_kbdock_query(struct wmi_device *event_wdev,
+			     struct wmi_device *query_wdev)
+{
+	u32 bkbd;
+
+	union acpi_object *obj __free(kfree) =
+		wmidev_block_query(query_wdev, YB9_KBDOCK_QUERY_INSTANCE);
+	if (!obj) {
+		dev_warn(&event_wdev->dev, "WQAF query returned NULL\n");
+		return -EIO;
+	}
+
+	/*
+	 * WQAF returns an 8-byte buffer: bytes [0..3] = LFID (0x00060000),
+	 * bytes [4..7] = BKBD value.  Guard against short buffers.
+	 */
+	if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length >= 8)
+		memcpy(&bkbd, obj->buffer.pointer + 4, sizeof(bkbd));
+	else if (obj->type == ACPI_TYPE_INTEGER)
+		bkbd = obj->integer.value;
+	else {
+		dev_warn(&event_wdev->dev,
+			 "WQAF: unexpected result type %d len %u\n",
+			 obj->type, obj->type == ACPI_TYPE_BUFFER ? obj->buffer.length : 0);
+		return -EIO;
+	}
+
+	bkbd = FIELD_GET(BKBD_MASK, bkbd);
+	if (bkbd == 3) {
+		dev_warn(&event_wdev->dev, "BKBD value 3 is reserved\n");
+		return -EINVAL;
+	}
+
+	return bkbd;
+}
+
+static void yb9_kbdock_update(struct wmi_device *wdev)
+{
+	struct yb9_kbdock_priv *priv = dev_get_drvdata(&wdev->dev);
+	int tablet_mode;
+	int bkbd;
+
+	bkbd = yb9_kbdock_query(wdev, priv->query_wdev);
+	if (bkbd < 0)
+		return;
+
+	priv->bkbd = bkbd;
+	tablet_mode = (bkbd == BKBD_DETACHED) ? 1 : 0;
+
+	input_report_switch(priv->input_dev, SW_TABLET_MODE, tablet_mode);
+	input_sync(priv->input_dev);
+
+	dev_dbg(&wdev->dev, "BKBD=%u tablet_mode=%d\n", bkbd, tablet_mode);
+}
+
+static void yb9_kbdock_notify(struct wmi_device *wdev, union acpi_object *data)
+{
+	yb9_kbdock_update(wdev);
+}
+
+static ssize_t keyboard_position_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	static const char * const names[] = {
+		"detached", "top-half", "bottom-half",
+	};
+	struct yb9_kbdock_priv *priv = dev_get_drvdata(dev);
+	unsigned int bkbd = priv->bkbd;
+
+	if (WARN_ON_ONCE(bkbd >= ARRAY_SIZE(names)))
+		return -EINVAL;
+	return sysfs_emit(buf, "%u (%s)\n", bkbd, names[bkbd]);
+}
+static DEVICE_ATTR_RO(keyboard_position);
+
+static struct attribute *yb9_kbdock_attrs[] = {
+	&dev_attr_keyboard_position.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(yb9_kbdock);
+
+static const struct dmi_system_id yb9_kbdock_dmi_table[] = {
+	{
+		/* Lenovo Yoga Book 9 14IAH10 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR,   "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
+		},
+	},
+	{ }
+};
+
+static int yb9_kbdock_probe(struct wmi_device *wdev, const void *ctx)
+{
+	enum yb9_guid_type type = (enum yb9_guid_type)(uintptr_t)ctx;
+	struct yb9_kbdock_priv *priv;
+	struct input_dev *input_dev;
+	struct wmi_device *qwdev;
+	int err;
+
+	if (type == YB9_GUID_QUERY) {
+		mutex_lock(&yb9_query_lock);
+		yb9_query_wdev = wdev;
+		mutex_unlock(&yb9_query_lock);
+		return 0;
+	}
+
+	if (!dmi_check_system(yb9_kbdock_dmi_table))
+		return -ENODEV;
+
+	mutex_lock(&yb9_query_lock);
+	qwdev = yb9_query_wdev;
+	mutex_unlock(&yb9_query_lock);
+	if (!qwdev)
+		return -EPROBE_DEFER;
+
+	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	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_EVENT_GUID "/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->dev.parent = &wdev->dev;
+	input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
+
+	err = input_register_device(input_dev);
+	if (err) {
+		dev_err(&wdev->dev, "failed to register input device: %d\n", err);
+		return err;
+	}
+
+	priv->query_wdev = qwdev;
+	priv->input_dev = input_dev;
+	dev_set_drvdata(&wdev->dev, priv);
+
+	yb9_kbdock_update(wdev);
+	return 0;
+}
+
+static void yb9_kbdock_remove(struct wmi_device *wdev)
+{
+	mutex_lock(&yb9_query_lock);
+	if (wdev == yb9_query_wdev)
+		yb9_query_wdev = NULL;
+	mutex_unlock(&yb9_query_lock);
+}
+
+static const struct wmi_device_id yb9_kbdock_wmi_id_table[] = {
+	{ .guid_string = YB9_KBDOCK_EVENT_GUID, .context = (void *)YB9_GUID_EVENT },
+	{ .guid_string = YB9_KBDOCK_QUERY_GUID, .context = (void *)YB9_GUID_QUERY },
+	{ }
+};
+MODULE_DEVICE_TABLE(wmi, yb9_kbdock_wmi_id_table);
+
+static struct wmi_driver yb9_kbdock_driver = {
+	.driver = {
+		.name = "lenovo-yb9-kbdock",
+		.dev_groups = yb9_kbdock_groups,
+	},
+	.id_table = yb9_kbdock_wmi_id_table,
+	.probe    = yb9_kbdock_probe,
+	.remove   = yb9_kbdock_remove,
+	.notify   = yb9_kbdock_notify,
+};
+module_wmi_driver(yb9_kbdock_driver);
+
+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	[flat|nested] 10+ messages in thread

* Re: [PATCH v2] platform/x86/lenovo: add Yoga Book 9 keyboard dock driver
  2026-05-17 15:02   ` [PATCH v2] platform/x86/lenovo: add Yoga Book 9 keyboard dock driver Dave Carey
@ 2026-05-17 15:25     ` sashiko-bot
  2026-05-19  0:02     ` [PATCH v3] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Dave Carey
  1 sibling, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-05-17 15:25 UTC (permalink / raw)
  To: Dave Carey; +Cc: dmitry.torokhov, linux-input

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] NULL Pointer Dereference on sysfs read for QUERY device and DMI check bypass
- [High] Use-After-Free due to unreferenced QUERY WMI device pointer
- [Medium] Race condition exposing incorrect initial switch state to userspace
--

commit f26ec3cd843093c54646b94f3331c501f619e886
Author: Dave Carey <carvsdriver@gmail.com>

platform/x86/lenovo: add Yoga Book 9 keyboard dock driver

This commit adds a new driver, lenovo-yb9-kbdock, to support the detachable
keyboard dock on the Lenovo Yoga Book 9 14IAH10. It queries the BKBD state
via WMI to report SW_TABLET_MODE input events and exposes the raw position
to userspace via sysfs.

> diff --git a/drivers/platform/x86/lenovo/yb9-kbdock.c b/drivers/platform/x86/lenovo/yb9-kbdock.c
> new file mode 100644
> index 0000000000000..ba0579f7981a3
> --- /dev/null
> +++ b/drivers/platform/x86/lenovo/yb9-kbdock.c
[ ... ]
> +static int yb9_kbdock_probe(struct wmi_device *wdev, const void *ctx)
> +{
> +	enum yb9_guid_type type = (enum yb9_guid_type)(uintptr_t)ctx;
> +	struct yb9_kbdock_priv *priv;
> +	struct input_dev *input_dev;
> +	struct wmi_device *qwdev;
> +	int err;
> +
> +	if (type == YB9_GUID_QUERY) {
> +		mutex_lock(&yb9_query_lock);
> +		yb9_query_wdev = wdev;
> +		mutex_unlock(&yb9_query_lock);
> +		return 0;
> +	}

The wmi_driver struct sets .dev_groups to yb9_kbdock_groups, which means the
keyboard_position sysfs attribute is created for all devices binding to this
driver, including the query WMI device.

If userspace reads this sysfs attribute on the query device, won't
keyboard_position_show() receive a NULL pointer from dev_get_drvdata()
and dereference it when accessing priv->bkbd?

Also, because this returns before dmi_check_system() is called, does this
allow the driver to bind to the query GUID on unsupported hardware?

> +
> +	if (!dmi_check_system(yb9_kbdock_dmi_table))
> +		return -ENODEV;
[ ... ]
> +	input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
> +
> +	err = input_register_device(input_dev);
> +	if (err) {
> +		dev_err(&wdev->dev, "failed to register input device: %d\n", err);
> +		return err;
> +	}
> +
> +	priv->query_wdev = qwdev;

Is a device link or reference count via get_device() needed here?

If the query WMI device is unbound or its parent ACPI device is hot-removed,
could priv->query_wdev become a dangling pointer that gets used in
yb9_kbdock_notify() when a WMI event fires?

> +	priv->input_dev = input_dev;
> +	dev_set_drvdata(&wdev->dev, priv);
> +
> +	yb9_kbdock_update(wdev);

Could this expose an incorrect initial switch state to userspace?

Since input_register_device() makes the device visible before
yb9_kbdock_update() queries the hardware state, userspace might
read the default laptop mode (0) even if the keyboard is detached.

Can the initial hardware state be queried and reported before registering
the input device?

> +	return 0;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260517150224.50191-1-carvsdriver@gmail.com?part=1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v3] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
  2026-05-17 15:02   ` [PATCH v2] platform/x86/lenovo: add Yoga Book 9 keyboard dock driver Dave Carey
  2026-05-17 15:25     ` sashiko-bot
@ 2026-05-19  0:02     ` Dave Carey
  2026-05-21 14:40       ` [PATCH v4] " Dave Carey
  1 sibling, 1 reply; 10+ messages in thread
From: Dave Carey @ 2026-05-19  0:02 UTC (permalink / raw)
  To: ilpo.jarvinen
  Cc: pithenrich2d, mpearson-lenovo, derekjohn.clark, W_Armin,
	platform-driver-x86, linux-input, linux-kernel, dmitry.torokhov,
	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).

The current BKBD state is read via a separate WMI query GUID
E7F300FA-21CD-4003-ADAC-2696135982E6 (WQAF method), which returns an
8-byte buffer: bytes [0..3] hold the LFID constant 0x00060000 and bytes
[4..7] hold the BKBD value.

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)

Both GUIDs are children of the same ACPI device (WM10), so both are
matched by a single WMI driver.  The query device pointer is stored in a
module-level variable protected by a mutex; the event device uses
wmidev_block_query() via the stored pointer rather than the deprecated
global wmi_query_block().  get_device()/put_device() bracket each use of
the stored pointer so probe/remove races cannot produce a use-after-free.

This driver:
  - Registers as a WMI driver on both the event and query GUIDs.
  - Queries BKBD state synchronously on probe and on each WMI
    notification.
  - Sets the initial SW_TABLET_MODE bit before input_register_device()
    via __set_bit() so userspace always reads the correct state on first
    open.
  - Reports SW_TABLET_MODE=1 when detached, SW_TABLET_MODE=0 when docked
    in either position (a physical keyboard is present in both cases).
  - Exposes the raw BKBD value via a read-only sysfs attribute
    "keyboard_position" for use by userspace (e.g. to distinguish between
    the two docked positions for different UI layouts).  The attribute is
    registered per-device via devm_device_add_groups() in the event-device
    probe path only; the query device has no priv and no sysfs groups.

Tested on: Lenovo Yoga Book 9 14IAH10 (model 83KJ), kernel 6.19.

Signed-off-by: Dave Carey <carvsdriver@gmail.com>
---
 .../testing/sysfs-driver-lenovo-yb9-kbdock    |  21 ++
 MAINTAINERS                                   |   6 +
 drivers/platform/x86/lenovo/Kconfig           |  14 ++
 drivers/platform/x86/lenovo/Makefile          |   1 +
 drivers/platform/x86/lenovo/yb9-kbdock.c      | 270 ++++++++++++++++++
 5 files changed, 312 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
 create mode 100644 MAINTAINERS
 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..bb57690
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
@@ -0,0 +1,21 @@
+What:		/sys/bus/wmi/drivers/lenovo-yb9-kbdock/<guid>/keyboard_position
+Date:		April 2026
+KernelVersion:	6.10
+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   detached  — keyboard is not docked to any screen
+		1   top-half  — keyboard docked on the top half of the bottom screen
+		2   bottom-half — keyboard docked on the bottom half of the bottom screen
+		==  ============================================================
+
+		The value is formatted as "<n> (<name>)\n", e.g. "1 (top-half)\n".
+
+		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
new file mode 100644
index 0000000..cb765b4
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,6 @@
+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
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_LMI)	+= think-lmi.o
 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,270 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Lenovo Yoga Book 9 keyboard-dock detection
+ *
+ * Reports SW_TABLET_MODE based on keyboard attachment state and exposes the
+ * raw dock position via sysfs.
+ *
+ * Copyright (C) 2026 Dave Carey <carvsdriver@gmail.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/dmi.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/wmi.h>
+
+/*
+ * WM10 ACPI device (_UID "GMZN") exposes two relevant WMI GUIDs:
+ *   YB9_KBDOCK_EVENT_GUID — notify ID 0xEB fires on attachment state change.
+ *   YB9_KBDOCK_QUERY_GUID — object "AF" (WQAF), returns an 8-byte buffer
+ *                           whose upper four bytes hold the BKBD value.
+ *
+ * BKBD encoding:
+ *   0 (BKBD_DETACHED)    — keyboard detached       → SW_TABLET_MODE = 1
+ *   1 (BKBD_TOP_HALF)    — docked, top half        → SW_TABLET_MODE = 0
+ *   2 (BKBD_BOTTOM_HALF) — docked, bottom half     → SW_TABLET_MODE = 0
+ *   3                    — reserved; treated as an error
+ */
+#define YB9_KBDOCK_EVENT_GUID		"806BD2A2-177B-481D-BFB5-3BA0BB4A2285"
+#define YB9_KBDOCK_QUERY_GUID		"E7F300FA-21CD-4003-ADAC-2696135982E6"
+#define YB9_KBDOCK_QUERY_INSTANCE	0
+
+#define BKBD_DETACHED		0
+#define BKBD_TOP_HALF		1
+#define BKBD_BOTTOM_HALF	2
+#define BKBD_MASK		GENMASK(1, 0)
+
+/* Distinguish the two GUIDs via the id_table context field. */
+enum yb9_guid_type { YB9_GUID_EVENT, YB9_GUID_QUERY };
+
+/*
+ * Both GUIDs are children of the same ACPI device (WM10).  Store the query
+ * WMI device globally so the event-device probe and notify path can reach it
+ * via wmidev_block_query().  Protected by yb9_query_lock during probe/remove.
+ */
+static struct wmi_device *yb9_query_wdev;
+static DEFINE_MUTEX(yb9_query_lock);
+
+struct yb9_kbdock_priv {
+	struct input_dev *input_dev;
+	unsigned int bkbd;
+};
+
+/* Returns 0–2 on success, -errno on error. */
+static int yb9_kbdock_query(struct wmi_device *event_wdev,
+			     struct wmi_device *query_wdev)
+{
+	u32 bkbd;
+
+	union acpi_object *obj __free(kfree) =
+		wmidev_block_query(query_wdev, YB9_KBDOCK_QUERY_INSTANCE);
+	if (!obj) {
+		dev_warn(&event_wdev->dev, "WQAF query returned NULL\n");
+		return -EIO;
+	}
+
+	/*
+	 * WQAF returns an 8-byte buffer: bytes [0..3] = LFID (0x00060000),
+	 * bytes [4..7] = BKBD value.  Guard against short buffers.
+	 */
+	if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length >= 8)
+		memcpy(&bkbd, obj->buffer.pointer + 4, sizeof(bkbd));
+	else if (obj->type == ACPI_TYPE_INTEGER)
+		bkbd = obj->integer.value;
+	else {
+		dev_warn(&event_wdev->dev,
+			 "WQAF: unexpected result type %d len %u\n",
+			 obj->type, obj->type == ACPI_TYPE_BUFFER ? obj->buffer.length : 0);
+		return -EIO;
+	}
+
+	bkbd = FIELD_GET(BKBD_MASK, bkbd);
+	if (bkbd == 3) {
+		dev_warn(&event_wdev->dev, "BKBD value 3 is reserved\n");
+		return -EINVAL;
+	}
+
+	return bkbd;
+}
+
+static void yb9_kbdock_update(struct wmi_device *wdev)
+{
+	struct yb9_kbdock_priv *priv = dev_get_drvdata(&wdev->dev);
+	struct wmi_device *qwdev;
+	int tablet_mode;
+	int bkbd;
+
+	mutex_lock(&yb9_query_lock);
+	qwdev = yb9_query_wdev;
+	if (qwdev)
+		get_device(&qwdev->dev);
+	mutex_unlock(&yb9_query_lock);
+	if (!qwdev)
+		return;
+
+	bkbd = yb9_kbdock_query(wdev, qwdev);
+	put_device(&qwdev->dev);
+	if (bkbd < 0)
+		return;
+
+	priv->bkbd = bkbd;
+	tablet_mode = (bkbd == BKBD_DETACHED) ? 1 : 0;
+
+	input_report_switch(priv->input_dev, SW_TABLET_MODE, tablet_mode);
+	input_sync(priv->input_dev);
+
+	dev_dbg(&wdev->dev, "BKBD=%u tablet_mode=%d\n", bkbd, tablet_mode);
+}
+
+static void yb9_kbdock_notify(struct wmi_device *wdev, union acpi_object *data)
+{
+	yb9_kbdock_update(wdev);
+}
+
+static ssize_t keyboard_position_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	static const char * const names[] = {
+		"detached", "top-half", "bottom-half",
+	};
+	struct yb9_kbdock_priv *priv = dev_get_drvdata(dev);
+	unsigned int bkbd = priv->bkbd;
+
+	if (WARN_ON_ONCE(bkbd >= ARRAY_SIZE(names)))
+		return -EINVAL;
+	return sysfs_emit(buf, "%u (%s)\n", bkbd, names[bkbd]);
+}
+static DEVICE_ATTR_RO(keyboard_position);
+
+static struct attribute *yb9_kbdock_attrs[] = {
+	&dev_attr_keyboard_position.attr,
+	NULL,
+};
+
+static const struct attribute_group yb9_kbdock_group = {
+	.attrs = yb9_kbdock_attrs,
+};
+
+static const struct dmi_system_id yb9_kbdock_dmi_table[] = {
+	{
+		/* Lenovo Yoga Book 9 14IAH10 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR,   "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
+		},
+	},
+	{ }
+};
+
+static int yb9_kbdock_probe(struct wmi_device *wdev, const void *ctx)
+{
+	enum yb9_guid_type type = (enum yb9_guid_type)(uintptr_t)ctx;
+	struct yb9_kbdock_priv *priv;
+	struct input_dev *input_dev;
+	struct wmi_device *qwdev;
+	int bkbd_init;
+	int err;
+
+	if (type == YB9_GUID_QUERY) {
+		if (!dmi_check_system(yb9_kbdock_dmi_table))
+			return -ENODEV;
+		mutex_lock(&yb9_query_lock);
+		yb9_query_wdev = wdev;
+		mutex_unlock(&yb9_query_lock);
+		return 0;
+	}
+
+	if (!dmi_check_system(yb9_kbdock_dmi_table))
+		return -ENODEV;
+
+	mutex_lock(&yb9_query_lock);
+	qwdev = yb9_query_wdev;
+	if (qwdev)
+		get_device(&qwdev->dev);
+	mutex_unlock(&yb9_query_lock);
+	if (!qwdev)
+		return -EPROBE_DEFER;
+
+	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		put_device(&qwdev->dev);
+		return -ENOMEM;
+	}
+
+	input_dev = devm_input_allocate_device(&wdev->dev);
+	if (!input_dev) {
+		put_device(&qwdev->dev);
+		return -ENOMEM;
+	}
+
+	input_dev->name = "Lenovo Yoga Book 9 keyboard dock switch";
+	input_dev->phys = YB9_KBDOCK_EVENT_GUID "/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->dev.parent = &wdev->dev;
+	input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
+
+	priv->input_dev = input_dev;
+	dev_set_drvdata(&wdev->dev, priv);
+
+	/*
+	 * Query the initial dock state and preset the switch bit before
+	 * input_register_device() so userspace never sees SW_TABLET_MODE = 0
+	 * for a detached keyboard on first open.
+	 */
+	bkbd_init = yb9_kbdock_query(wdev, qwdev);
+	put_device(&qwdev->dev);
+	if (bkbd_init >= 0) {
+		priv->bkbd = bkbd_init;
+		if (bkbd_init == BKBD_DETACHED)
+			__set_bit(SW_TABLET_MODE, input_dev->sw);
+	}
+
+	err = input_register_device(input_dev);
+	if (err) {
+		dev_err(&wdev->dev, "failed to register input device: %d\n", err);
+		return err;
+	}
+
+	err = devm_device_add_group(&wdev->dev, &yb9_kbdock_group);
+	if (err) {
+		dev_err(&wdev->dev, "failed to add sysfs group: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void yb9_kbdock_remove(struct wmi_device *wdev)
+{
+	mutex_lock(&yb9_query_lock);
+	if (wdev == yb9_query_wdev)
+		yb9_query_wdev = NULL;
+	mutex_unlock(&yb9_query_lock);
+}
+
+static const struct wmi_device_id yb9_kbdock_wmi_id_table[] = {
+	{ .guid_string = YB9_KBDOCK_EVENT_GUID, .context = (void *)YB9_GUID_EVENT },
+	{ .guid_string = YB9_KBDOCK_QUERY_GUID, .context = (void *)YB9_GUID_QUERY },
+	{ }
+};
+MODULE_DEVICE_TABLE(wmi, yb9_kbdock_wmi_id_table);
+
+static struct wmi_driver yb9_kbdock_driver = {
+	.driver = {
+		.name = "lenovo-yb9-kbdock",
+	},
+	.id_table = yb9_kbdock_wmi_id_table,
+	.probe    = yb9_kbdock_probe,
+	.remove   = yb9_kbdock_remove,
+	.notify   = yb9_kbdock_notify,
+};
+module_wmi_driver(yb9_kbdock_driver);
+
+MODULE_AUTHOR("Dave Carey <carvsdriver@gmail.com>");
+MODULE_DESCRIPTION("Lenovo Yoga Book 9 keyboard dock detection");
+MODULE_LICENSE("GPL");
--
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v4] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
  2026-05-19  0:02     ` [PATCH v3] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Dave Carey
@ 2026-05-21 14:40       ` Dave Carey
  2026-05-22 21:59         ` Armin Wolf
  2026-05-27 12:27         ` [PATCH v5] " Dave Carey
  0 siblings, 2 replies; 10+ messages in thread
From: Dave Carey @ 2026-05-21 14:40 UTC (permalink / raw)
  To: platform-driver-x86; +Cc: ilpo.jarvinen, carvsdriver

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).

The current BKBD state is read via WMI query GUID
E7F300FA-21CD-4003-ADAC-2696135982E6 (WQAF method), which returns an
8-byte buffer: bytes [0..3] hold the LFID constant 0x00060000 and bytes
[4..7] hold the BKBD value.

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 as a WMI driver on the event GUID.
  - Queries BKBD state on probe and on each WMI notification.
  - Reports SW_TABLET_MODE=1 when detached, SW_TABLET_MODE=0 when docked
    in either position (a physical keyboard is present in both cases).
  - Exposes the raw BKBD value via a read-only sysfs attribute
    "keyboard_position" for use by userspace (e.g. to distinguish between
    the two docked positions for different UI layouts).

Tested on: Lenovo Yoga Book 9 14IAH10 (model 83KJ), kernel 6.19.

Signed-off-by: Dave Carey <carvsdriver@gmail.com>
---
 .../testing/sysfs-driver-lenovo-yb9-kbdock    |  21 ++
 MAINTAINERS                                   |   7 +
 drivers/platform/x86/lenovo/Kconfig           |  14 ++
 drivers/platform/x86/lenovo/Makefile          |   1 +
 drivers/platform/x86/lenovo/yb9-kbdock.c      | 216 ++++++++++++++++++
 5 files changed, 259 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..bb57690
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
@@ -0,0 +1,21 @@
+What:		/sys/bus/wmi/drivers/lenovo-yb9-kbdock/<guid>/keyboard_position
+Date:		April 2026
+KernelVersion:	6.10
+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   detached  — keyboard is not docked to any screen
+		1   top-half  — keyboard docked on the top half of the bottom screen
+		2   bottom-half — keyboard docked on the bottom half of the bottom screen
+		==  ============================================================
+
+		The value is formatted as "<n> (<name>)\n", e.g. "1 (top-half)\n".
+
+		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_LMI)	+= think-lmi.o
 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..693e287
--- /dev/null
+++ b/drivers/platform/x86/lenovo/yb9-kbdock.c
@@ -0,0 +1,216 @@
+// 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.
+ *
+ * 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
+ *
+ * This driver registers for the WMI event GUID, queries BKBD on probe and on
+ * each event, reports SW_TABLET_MODE=0 when the keyboard is docked (either
+ * position) and SW_TABLET_MODE=1 when detached, and exposes the raw BKBD
+ * value in sysfs as "keyboard_position".
+ *
+ * Copyright (C) 2026 Dave Carey <carvsdriver@gmail.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/wmi.h>
+
+/*
+ * WM10 ACPI device (_UID "GMZN"):
+ *   Event GUID — notify ID 0xEB fires on keyboard attachment change.
+ *   Query GUID — object "AF", maps to WQAF(); returns 8-byte buffer
+ *                {LFID=0x00060000, BKBD[31:0]}.
+ */
+#define YB9_KBDOCK_EVENT_GUID	"806BD2A2-177B-481D-BFB5-3BA0BB4A2285"
+#define YB9_KBDOCK_QUERY_GUID	"E7F300FA-21CD-4003-ADAC-2696135982E6"
+
+#define YB9_KBDOCK_QUERY_INSTANCE	0
+
+/* BKBD encoding — keyboard always docks on the bottom screen */
+#define BKBD_DETACHED		0
+#define BKBD_TOP_HALF		1	/* docked on top half of bottom screen */
+#define BKBD_BOTTOM_HALF	2	/* docked on bottom half of bottom screen */
+
+static const struct dmi_system_id yb9_kbdock_dmi_table[] = {
+	{
+		/* Lenovo Yoga Book 9 14IAH10 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR,   "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
+		},
+	},
+	{ }
+};
+
+struct yb9_kbdock_priv {
+	struct input_dev *input_dev;
+	unsigned int bkbd;	/* last read BKBD value (0-3) */
+};
+
+/* Read current BKBD state via WQAF.  Returns 0-3 or -errno. */
+static int yb9_kbdock_query(struct wmi_device *wdev)
+{
+	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *obj;
+	acpi_status status;
+	u32 bkbd;
+
+	status = wmi_query_block(YB9_KBDOCK_QUERY_GUID,
+				 YB9_KBDOCK_QUERY_INSTANCE, &out);
+	if (ACPI_FAILURE(status)) {
+		dev_warn(&wdev->dev, "WQAF query failed: %s\n",
+			 acpi_format_exception(status));
+		return -EIO;
+	}
+
+	obj = out.pointer;
+	if (!obj) {
+		dev_warn(&wdev->dev, "WQAF returned NULL\n");
+		return -EIO;
+	}
+
+	/*
+	 * WQAF returns an 8-byte buffer: bytes [0..3] = LFID (0x00060000),
+	 * bytes [4..7] = BKBD value.  Guard against short buffers.
+	 */
+	if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length >= 8) {
+		memcpy(&bkbd, obj->buffer.pointer + 4, sizeof(bkbd));
+		bkbd &= 0x3;
+	} else if (obj->type == ACPI_TYPE_INTEGER) {
+		bkbd = obj->integer.value & 0x3;
+	} else {
+		dev_warn(&wdev->dev,
+			 "WQAF: unexpected result type %d len %u\n",
+			 obj->type,
+			 obj->type == ACPI_TYPE_BUFFER
+				? obj->buffer.length : 0);
+		kfree(obj);
+		return -EIO;
+	}
+
+	kfree(obj);
+	return (int)bkbd;
+}
+
+static void yb9_kbdock_update(struct wmi_device *wdev)
+{
+	struct yb9_kbdock_priv *priv = dev_get_drvdata(&wdev->dev);
+	int bkbd;
+	int tablet_mode;
+
+	bkbd = yb9_kbdock_query(wdev);
+	if (bkbd < 0)
+		return;
+
+	priv->bkbd = bkbd;
+
+	/*
+	 * Report tablet mode only when the keyboard is fully detached.
+	 * Both docked positions (top-half and bottom-half of the bottom screen)
+	 * indicate a physical keyboard is present — report laptop mode.
+	 */
+	tablet_mode = (bkbd == BKBD_DETACHED) ? 1 : 0;
+
+	input_report_switch(priv->input_dev, SW_TABLET_MODE, tablet_mode);
+	input_sync(priv->input_dev);
+
+	dev_dbg(&wdev->dev, "BKBD=%u tablet_mode=%d\n", bkbd, tablet_mode);
+}
+
+static void yb9_kbdock_notify(struct wmi_device *wdev, union acpi_object *data)
+{
+	yb9_kbdock_update(wdev);
+}
+
+/* sysfs: keyboard_position — exposes raw BKBD value */
+static ssize_t keyboard_position_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct yb9_kbdock_priv *priv = dev_get_drvdata(dev);
+	static const char * const names[] = {
+		"detached", "top-half", "bottom-half", "unknown"
+	};
+	unsigned int bkbd = priv->bkbd;
+
+	if (bkbd > 3)
+		bkbd = 3;
+	return sysfs_emit(buf, "%u (%s)\n", bkbd, names[bkbd]);
+}
+static DEVICE_ATTR_RO(keyboard_position);
+
+static struct attribute *yb9_kbdock_attrs[] = {
+	&dev_attr_keyboard_position.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(yb9_kbdock);
+
+static int yb9_kbdock_probe(struct wmi_device *wdev, const void *ctx)
+{
+	struct yb9_kbdock_priv *priv;
+	struct input_dev *input_dev;
+	int err;
+
+	if (!dmi_check_system(yb9_kbdock_dmi_table)) {
+		dev_dbg(&wdev->dev, "not a Yoga Book 9, skipping\n");
+		return -ENODEV;
+	}
+
+	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	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_EVENT_GUID "/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
+
+	err = input_register_device(input_dev);
+	if (err) {
+		dev_err(&wdev->dev, "failed to register input device: %d\n", err);
+		return err;
+	}
+
+	priv->input_dev = input_dev;
+	dev_set_drvdata(&wdev->dev, priv);
+
+	/* Report initial state */
+	yb9_kbdock_update(wdev);
+	return 0;
+}
+
+static const struct wmi_device_id yb9_kbdock_wmi_id_table[] = {
+	{ .guid_string = YB9_KBDOCK_EVENT_GUID },
+	{ }
+};
+MODULE_DEVICE_TABLE(wmi, yb9_kbdock_wmi_id_table);
+
+static struct wmi_driver yb9_kbdock_driver = {
+	.driver = {
+		.name = "lenovo-yb9-kbdock",
+		.dev_groups = yb9_kbdock_groups,
+	},
+	.id_table = yb9_kbdock_wmi_id_table,
+	.probe    = yb9_kbdock_probe,
+	.notify   = yb9_kbdock_notify,
+};
+module_wmi_driver(yb9_kbdock_driver);
+
+MODULE_AUTHOR("Dave Carey <carvsdriver@gmail.com>");
+MODULE_DESCRIPTION("Lenovo Yoga Book 9 keyboard dock detection");
+MODULE_LICENSE("GPL");
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v4] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
  2026-05-21 14:40       ` [PATCH v4] " Dave Carey
@ 2026-05-22 21:59         ` Armin Wolf
  2026-05-26 13:38           ` Dave Carey
  2026-05-27 12:27         ` [PATCH v5] " Dave Carey
  1 sibling, 1 reply; 10+ messages in thread
From: Armin Wolf @ 2026-05-22 21:59 UTC (permalink / raw)
  To: Dave Carey, platform-driver-x86; +Cc: ilpo.jarvinen

Am 21.05.26 um 16:40 schrieb 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).
>
> The current BKBD state is read via WMI query GUID
> E7F300FA-21CD-4003-ADAC-2696135982E6 (WQAF method), which returns an
> 8-byte buffer: bytes [0..3] hold the LFID constant 0x00060000 and bytes
> [4..7] hold the BKBD value.

Hi,

does the device contain embedded BMOF data? Take a look at Documentation/wmi/driver-development-guide.rst
to find out how to decode said BMOF data.

>
> 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 as a WMI driver on the event GUID.
>    - Queries BKBD state on probe and on each WMI notification.
>    - Reports SW_TABLET_MODE=1 when detached, SW_TABLET_MODE=0 when docked
>      in either position (a physical keyboard is present in both cases).
>    - Exposes the raw BKBD value via a read-only sysfs attribute
>      "keyboard_position" for use by userspace (e.g. to distinguish between
>      the two docked positions for different UI layouts).
>
> Tested on: Lenovo Yoga Book 9 14IAH10 (model 83KJ), kernel 6.19.
>
> Signed-off-by: Dave Carey <carvsdriver@gmail.com>
> ---
>   .../testing/sysfs-driver-lenovo-yb9-kbdock    |  21 ++
>   MAINTAINERS                                   |   7 +
>   drivers/platform/x86/lenovo/Kconfig           |  14 ++
>   drivers/platform/x86/lenovo/Makefile          |   1 +
>   drivers/platform/x86/lenovo/yb9-kbdock.c      | 216 ++++++++++++++++++
>   5 files changed, 259 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..bb57690
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
> @@ -0,0 +1,21 @@
> +What:		/sys/bus/wmi/drivers/lenovo-yb9-kbdock/<guid>/keyboard_position
> +Date:		April 2026
> +KernelVersion:	6.10
> +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   detached  — keyboard is not docked to any screen
> +		1   top-half  — keyboard docked on the top half of the bottom screen
> +		2   bottom-half — keyboard docked on the bottom half of the bottom screen
> +		==  ============================================================
> +
> +		The value is formatted as "<n> (<name>)\n", e.g. "1 (top-half)\n"

IMHO a simple number would be much easier to parse.

> +
> +		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_LMI)	+= think-lmi.o
>   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..693e287
> --- /dev/null
> +++ b/drivers/platform/x86/lenovo/yb9-kbdock.c
> @@ -0,0 +1,216 @@
> +// 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.
> + *
> + * 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
> + *
> + * This driver registers for the WMI event GUID, queries BKBD on probe and on
> + * each event, reports SW_TABLET_MODE=0 when the keyboard is docked (either
> + * position) and SW_TABLET_MODE=1 when detached, and exposes the raw BKBD
> + * value in sysfs as "keyboard_position".
> + *
> + * Copyright (C) 2026 Dave Carey <carvsdriver@gmail.com>
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/dmi.h>
> +#include <linux/input.h>
> +#include <linux/module.h>
> +#include <linux/wmi.h>
> +
> +/*
> + * WM10 ACPI device (_UID "GMZN"):
> + *   Event GUID — notify ID 0xEB fires on keyboard attachment change.
> + *   Query GUID — object "AF", maps to WQAF(); returns 8-byte buffer
> + *                {LFID=0x00060000, BKBD[31:0]}.
> + */
> +#define YB9_KBDOCK_EVENT_GUID	"806BD2A2-177B-481D-BFB5-3BA0BB4A2285"
> +#define YB9_KBDOCK_QUERY_GUID	"E7F300FA-21CD-4003-ADAC-2696135982E6"
> +
> +#define YB9_KBDOCK_QUERY_INSTANCE	0
> +
> +/* BKBD encoding — keyboard always docks on the bottom screen */
> +#define BKBD_DETACHED		0
> +#define BKBD_TOP_HALF		1	/* docked on top half of bottom screen */
> +#define BKBD_BOTTOM_HALF	2	/* docked on bottom half of bottom screen */
> +
> +static const struct dmi_system_id yb9_kbdock_dmi_table[] = {
> +	{
> +		/* Lenovo Yoga Book 9 14IAH10 */
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR,   "LENOVO"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
> +		},
> +	},
> +	{ }
> +};
> +
> +struct yb9_kbdock_priv {
> +	struct input_dev *input_dev;
> +	unsigned int bkbd;	/* last read BKBD value (0-3) */
> +};
> +
> +/* Read current BKBD state via WQAF.  Returns 0-3 or -errno. */
> +static int yb9_kbdock_query(struct wmi_device *wdev)
> +{
> +	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
> +	union acpi_object *obj;
> +	acpi_status status;
> +	u32 bkbd;
> +
> +	status = wmi_query_block(YB9_KBDOCK_QUERY_GUID,
> +				 YB9_KBDOCK_QUERY_INSTANCE, &out);

That method is deprecated, please use wmidev_query_block(); I suggest you use a notifier
to connect both the WMI event device and the WMI blockd device. Take a look at drivers/platform/
x86/uniwill/uniwill-wmi.c for example.

> +	if (ACPI_FAILURE(status)) {
> +		dev_warn(&wdev->dev, "WQAF query failed: %s\n",
> +			 acpi_format_exception(status));
> +		return -EIO;
> +	}
> +
> +	obj = out.pointer;
> +	if (!obj) {
> +		dev_warn(&wdev->dev, "WQAF returned NULL\n");
> +		return -EIO;
> +	}
> +
> +	/*
> +	 * WQAF returns an 8-byte buffer: bytes [0..3] = LFID (0x00060000),
> +	 * bytes [4..7] = BKBD value.  Guard against short buffers.
> +	 */
> +	if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length >= 8) {
> +		memcpy(&bkbd, obj->buffer.pointer + 4, sizeof(bkbd));
> +		bkbd &= 0x3;
> +	} else if (obj->type == ACPI_TYPE_INTEGER) {
> +		bkbd = obj->integer.value & 0x3;
> +	} else {
> +		dev_warn(&wdev->dev,
> +			 "WQAF: unexpected result type %d len %u\n",
> +			 obj->type,
> +			 obj->type == ACPI_TYPE_BUFFER
> +				? obj->buffer.length : 0);
> +		kfree(obj);
> +		return -EIO;
> +	}
> +
> +	kfree(obj);
> +	return (int)bkbd;
> +}
> +
> +static void yb9_kbdock_update(struct wmi_device *wdev)
> +{
> +	struct yb9_kbdock_priv *priv = dev_get_drvdata(&wdev->dev);
> +	int bkbd;
> +	int tablet_mode;
> +
> +	bkbd = yb9_kbdock_query(wdev);
> +	if (bkbd < 0)
> +		return;
> +
> +	priv->bkbd = bkbd;
> +
> +	/*
> +	 * Report tablet mode only when the keyboard is fully detached.
> +	 * Both docked positions (top-half and bottom-half of the bottom screen)
> +	 * indicate a physical keyboard is present — report laptop mode.
> +	 */
> +	tablet_mode = (bkbd == BKBD_DETACHED) ? 1 : 0;
> +
> +	input_report_switch(priv->input_dev, SW_TABLET_MODE, tablet_mode);
> +	input_sync(priv->input_dev);
> +
> +	dev_dbg(&wdev->dev, "BKBD=%u tablet_mode=%d\n", bkbd, tablet_mode);
> +}
> +
> +static void yb9_kbdock_notify(struct wmi_device *wdev, union acpi_object *data)
> +{
> +	yb9_kbdock_update(wdev);
> +}
> +
> +/* sysfs: keyboard_position — exposes raw BKBD value */
> +static ssize_t keyboard_position_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf)
> +{
> +	struct yb9_kbdock_priv *priv = dev_get_drvdata(dev);
> +	static const char * const names[] = {
> +		"detached", "top-half", "bottom-half", "unknown"
> +	};
> +	unsigned int bkbd = priv->bkbd;
> +
> +	if (bkbd > 3)
> +		bkbd = 3;

I think you should protect bkbd with a spinlock. Or you could just read the current value
from the hardware directly.

> +	return sysfs_emit(buf, "%u (%s)\n", bkbd, names[bkbd]);
> +}
> +static DEVICE_ATTR_RO(keyboard_position);
> +
> +static struct attribute *yb9_kbdock_attrs[] = {
> +	&dev_attr_keyboard_position.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(yb9_kbdock);
> +
> +static int yb9_kbdock_probe(struct wmi_device *wdev, const void *ctx)
> +{
> +	struct yb9_kbdock_priv *priv;
> +	struct input_dev *input_dev;
> +	int err;
> +
> +	if (!dmi_check_system(yb9_kbdock_dmi_table)) {
> +		dev_dbg(&wdev->dev, "not a Yoga Book 9, skipping\n");
> +		return -ENODEV;
> +	}
> +
> +	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	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_EVENT_GUID "/input0";
> +	input_dev->id.bustype = BUS_HOST;
> +	input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
> +
> +	err = input_register_device(input_dev);
> +	if (err) {
> +		dev_err(&wdev->dev, "failed to register input device: %d\n", err);
> +		return err;
> +	}
> +
> +	priv->input_dev = input_dev;
> +	dev_set_drvdata(&wdev->dev, priv);
> +
> +	/* Report initial state */
> +	yb9_kbdock_update(wdev);
> +	return 0;
> +}
> +
> +static const struct wmi_device_id yb9_kbdock_wmi_id_table[] = {
> +	{ .guid_string = YB9_KBDOCK_EVENT_GUID },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(wmi, yb9_kbdock_wmi_id_table);
> +
> +static struct wmi_driver yb9_kbdock_driver = {
> +	.driver = {
> +		.name = "lenovo-yb9-kbdock",
> +		.dev_groups = yb9_kbdock_groups,
> +	},
> +	.id_table = yb9_kbdock_wmi_id_table,
> +	.probe    = yb9_kbdock_probe,
> +	.notify   = yb9_kbdock_notify,

Please set .no_singleton = true. Also please update the docking state when resuming from suspend or hibernation.

Thanks,
Armin Wolf

> +};
> +module_wmi_driver(yb9_kbdock_driver);
> +
> +MODULE_AUTHOR("Dave Carey <carvsdriver@gmail.com>");
> +MODULE_DESCRIPTION("Lenovo Yoga Book 9 keyboard dock detection");
> +MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v4] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
  2026-05-22 21:59         ` Armin Wolf
@ 2026-05-26 13:38           ` Dave Carey
  0 siblings, 0 replies; 10+ messages in thread
From: Dave Carey @ 2026-05-26 13:38 UTC (permalink / raw)
  To: Armin Wolf, platform-driver-x86; +Cc: ilpo.jarvinen

ack.  will review your feedback.  thank you.

On 5/22/26 5:59 PM, Armin Wolf wrote:
> Am 21.05.26 um 16:40 schrieb 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).
>>
>> The current BKBD state is read via WMI query GUID
>> E7F300FA-21CD-4003-ADAC-2696135982E6 (WQAF method), which returns an
>> 8-byte buffer: bytes [0..3] hold the LFID constant 0x00060000 and bytes
>> [4..7] hold the BKBD value.
>
> Hi,
>
> does the device contain embedded BMOF data? Take a look at 
> Documentation/wmi/driver-development-guide.rst
> to find out how to decode said BMOF data.
>
>>
>> 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 as a WMI driver on the event GUID.
>>    - Queries BKBD state on probe and on each WMI notification.
>>    - Reports SW_TABLET_MODE=1 when detached, SW_TABLET_MODE=0 when 
>> docked
>>      in either position (a physical keyboard is present in both cases).
>>    - Exposes the raw BKBD value via a read-only sysfs attribute
>>      "keyboard_position" for use by userspace (e.g. to distinguish 
>> between
>>      the two docked positions for different UI layouts).
>>
>> Tested on: Lenovo Yoga Book 9 14IAH10 (model 83KJ), kernel 6.19.
>>
>> Signed-off-by: Dave Carey <carvsdriver@gmail.com>
>> ---
>>   .../testing/sysfs-driver-lenovo-yb9-kbdock    |  21 ++
>>   MAINTAINERS                                   |   7 +
>>   drivers/platform/x86/lenovo/Kconfig           |  14 ++
>>   drivers/platform/x86/lenovo/Makefile          |   1 +
>>   drivers/platform/x86/lenovo/yb9-kbdock.c      | 216 ++++++++++++++++++
>>   5 files changed, 259 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..bb57690
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
>> @@ -0,0 +1,21 @@
>> +What: /sys/bus/wmi/drivers/lenovo-yb9-kbdock/<guid>/keyboard_position
>> +Date:        April 2026
>> +KernelVersion:    6.10
>> +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   detached  — keyboard is not docked to any screen
>> +        1   top-half  — keyboard docked on the top half of the 
>> bottom screen
>> +        2   bottom-half — keyboard docked on the bottom half of the 
>> bottom screen
>> +        == ============================================================
>> +
>> +        The value is formatted as "<n> (<name>)\n", e.g. "1 
>> (top-half)\n"
>
> IMHO a simple number would be much easier to parse.
>
>> +
>> +        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_LMI)    += think-lmi.o
>>   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..693e287
>> --- /dev/null
>> +++ b/drivers/platform/x86/lenovo/yb9-kbdock.c
>> @@ -0,0 +1,216 @@
>> +// 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.
>> + *
>> + * 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
>> + *
>> + * This driver registers for the WMI event GUID, queries BKBD on 
>> probe and on
>> + * each event, reports SW_TABLET_MODE=0 when the keyboard is docked 
>> (either
>> + * position) and SW_TABLET_MODE=1 when detached, and exposes the raw 
>> BKBD
>> + * value in sysfs as "keyboard_position".
>> + *
>> + * Copyright (C) 2026 Dave Carey <carvsdriver@gmail.com>
>> + */
>> +
>> +#include <linux/acpi.h>
>> +#include <linux/dmi.h>
>> +#include <linux/input.h>
>> +#include <linux/module.h>
>> +#include <linux/wmi.h>
>> +
>> +/*
>> + * WM10 ACPI device (_UID "GMZN"):
>> + *   Event GUID — notify ID 0xEB fires on keyboard attachment change.
>> + *   Query GUID — object "AF", maps to WQAF(); returns 8-byte buffer
>> + *                {LFID=0x00060000, BKBD[31:0]}.
>> + */
>> +#define YB9_KBDOCK_EVENT_GUID "806BD2A2-177B-481D-BFB5-3BA0BB4A2285"
>> +#define YB9_KBDOCK_QUERY_GUID "E7F300FA-21CD-4003-ADAC-2696135982E6"
>> +
>> +#define YB9_KBDOCK_QUERY_INSTANCE    0
>> +
>> +/* BKBD encoding — keyboard always docks on the bottom screen */
>> +#define BKBD_DETACHED        0
>> +#define BKBD_TOP_HALF        1    /* docked on top half of bottom 
>> screen */
>> +#define BKBD_BOTTOM_HALF    2    /* docked on bottom half of bottom 
>> screen */
>> +
>> +static const struct dmi_system_id yb9_kbdock_dmi_table[] = {
>> +    {
>> +        /* Lenovo Yoga Book 9 14IAH10 */
>> +        .matches = {
>> +            DMI_MATCH(DMI_SYS_VENDOR,   "LENOVO"),
>> +            DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
>> +        },
>> +    },
>> +    { }
>> +};
>> +
>> +struct yb9_kbdock_priv {
>> +    struct input_dev *input_dev;
>> +    unsigned int bkbd;    /* last read BKBD value (0-3) */
>> +};
>> +
>> +/* Read current BKBD state via WQAF.  Returns 0-3 or -errno. */
>> +static int yb9_kbdock_query(struct wmi_device *wdev)
>> +{
>> +    struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
>> +    union acpi_object *obj;
>> +    acpi_status status;
>> +    u32 bkbd;
>> +
>> +    status = wmi_query_block(YB9_KBDOCK_QUERY_GUID,
>> +                 YB9_KBDOCK_QUERY_INSTANCE, &out);
>
> That method is deprecated, please use wmidev_query_block(); I suggest 
> you use a notifier
> to connect both the WMI event device and the WMI blockd device. Take a 
> look at drivers/platform/
> x86/uniwill/uniwill-wmi.c for example.
>
>> +    if (ACPI_FAILURE(status)) {
>> +        dev_warn(&wdev->dev, "WQAF query failed: %s\n",
>> +             acpi_format_exception(status));
>> +        return -EIO;
>> +    }
>> +
>> +    obj = out.pointer;
>> +    if (!obj) {
>> +        dev_warn(&wdev->dev, "WQAF returned NULL\n");
>> +        return -EIO;
>> +    }
>> +
>> +    /*
>> +     * WQAF returns an 8-byte buffer: bytes [0..3] = LFID (0x00060000),
>> +     * bytes [4..7] = BKBD value.  Guard against short buffers.
>> +     */
>> +    if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length >= 8) {
>> +        memcpy(&bkbd, obj->buffer.pointer + 4, sizeof(bkbd));
>> +        bkbd &= 0x3;
>> +    } else if (obj->type == ACPI_TYPE_INTEGER) {
>> +        bkbd = obj->integer.value & 0x3;
>> +    } else {
>> +        dev_warn(&wdev->dev,
>> +             "WQAF: unexpected result type %d len %u\n",
>> +             obj->type,
>> +             obj->type == ACPI_TYPE_BUFFER
>> +                ? obj->buffer.length : 0);
>> +        kfree(obj);
>> +        return -EIO;
>> +    }
>> +
>> +    kfree(obj);
>> +    return (int)bkbd;
>> +}
>> +
>> +static void yb9_kbdock_update(struct wmi_device *wdev)
>> +{
>> +    struct yb9_kbdock_priv *priv = dev_get_drvdata(&wdev->dev);
>> +    int bkbd;
>> +    int tablet_mode;
>> +
>> +    bkbd = yb9_kbdock_query(wdev);
>> +    if (bkbd < 0)
>> +        return;
>> +
>> +    priv->bkbd = bkbd;
>> +
>> +    /*
>> +     * Report tablet mode only when the keyboard is fully detached.
>> +     * Both docked positions (top-half and bottom-half of the bottom 
>> screen)
>> +     * indicate a physical keyboard is present — report laptop mode.
>> +     */
>> +    tablet_mode = (bkbd == BKBD_DETACHED) ? 1 : 0;
>> +
>> +    input_report_switch(priv->input_dev, SW_TABLET_MODE, tablet_mode);
>> +    input_sync(priv->input_dev);
>> +
>> +    dev_dbg(&wdev->dev, "BKBD=%u tablet_mode=%d\n", bkbd, tablet_mode);
>> +}
>> +
>> +static void yb9_kbdock_notify(struct wmi_device *wdev, union 
>> acpi_object *data)
>> +{
>> +    yb9_kbdock_update(wdev);
>> +}
>> +
>> +/* sysfs: keyboard_position — exposes raw BKBD value */
>> +static ssize_t keyboard_position_show(struct device *dev,
>> +                      struct device_attribute *attr, char *buf)
>> +{
>> +    struct yb9_kbdock_priv *priv = dev_get_drvdata(dev);
>> +    static const char * const names[] = {
>> +        "detached", "top-half", "bottom-half", "unknown"
>> +    };
>> +    unsigned int bkbd = priv->bkbd;
>> +
>> +    if (bkbd > 3)
>> +        bkbd = 3;
>
> I think you should protect bkbd with a spinlock. Or you could just 
> read the current value
> from the hardware directly.
>
>> +    return sysfs_emit(buf, "%u (%s)\n", bkbd, names[bkbd]);
>> +}
>> +static DEVICE_ATTR_RO(keyboard_position);
>> +
>> +static struct attribute *yb9_kbdock_attrs[] = {
>> +    &dev_attr_keyboard_position.attr,
>> +    NULL,
>> +};
>> +ATTRIBUTE_GROUPS(yb9_kbdock);
>> +
>> +static int yb9_kbdock_probe(struct wmi_device *wdev, const void *ctx)
>> +{
>> +    struct yb9_kbdock_priv *priv;
>> +    struct input_dev *input_dev;
>> +    int err;
>> +
>> +    if (!dmi_check_system(yb9_kbdock_dmi_table)) {
>> +        dev_dbg(&wdev->dev, "not a Yoga Book 9, skipping\n");
>> +        return -ENODEV;
>> +    }
>> +
>> +    priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
>> +    if (!priv)
>> +        return -ENOMEM;
>> +
>> +    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_EVENT_GUID "/input0";
>> +    input_dev->id.bustype = BUS_HOST;
>> +    input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
>> +
>> +    err = input_register_device(input_dev);
>> +    if (err) {
>> +        dev_err(&wdev->dev, "failed to register input device: %d\n", 
>> err);
>> +        return err;
>> +    }
>> +
>> +    priv->input_dev = input_dev;
>> +    dev_set_drvdata(&wdev->dev, priv);
>> +
>> +    /* Report initial state */
>> +    yb9_kbdock_update(wdev);
>> +    return 0;
>> +}
>> +
>> +static const struct wmi_device_id yb9_kbdock_wmi_id_table[] = {
>> +    { .guid_string = YB9_KBDOCK_EVENT_GUID },
>> +    { }
>> +};
>> +MODULE_DEVICE_TABLE(wmi, yb9_kbdock_wmi_id_table);
>> +
>> +static struct wmi_driver yb9_kbdock_driver = {
>> +    .driver = {
>> +        .name = "lenovo-yb9-kbdock",
>> +        .dev_groups = yb9_kbdock_groups,
>> +    },
>> +    .id_table = yb9_kbdock_wmi_id_table,
>> +    .probe    = yb9_kbdock_probe,
>> +    .notify   = yb9_kbdock_notify,
>
> Please set .no_singleton = true. Also please update the docking state 
> when resuming from suspend or hibernation.
>
> Thanks,
> Armin Wolf
>
>> +};
>> +module_wmi_driver(yb9_kbdock_driver);
>> +
>> +MODULE_AUTHOR("Dave Carey <carvsdriver@gmail.com>");
>> +MODULE_DESCRIPTION("Lenovo Yoga Book 9 keyboard dock detection");
>> +MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v5] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
  2026-05-21 14:40       ` [PATCH v4] " Dave Carey
  2026-05-22 21:59         ` Armin Wolf
@ 2026-05-27 12:27         ` Dave Carey
  1 sibling, 0 replies; 10+ messages in thread
From: Dave Carey @ 2026-05-27 12:27 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: hdegoede, ilpo.jarvinen, armin.wolf, 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: one on the event GUID (LENOVO_BTKBD_EVENT)
    and one on the block GUID (LENOVO_FEATURE_STATUS_DATA).
  - On probe, reads initial BKBD state via wmidev_block_query() on the
    block device; whichever driver probes last triggers the initial read.
  - On WMI notification, reads BKBD directly from the event data integer
    (ACPI _WED(0xEB) returns EC.BKBD) without a redundant WQAF call.
  - Reports SW_TABLET_MODE=1 when detached, SW_TABLET_MODE=0 when docked
    in either position (a physical keyboard is present in both cases).
  - Exposes the raw BKBD value via read-only sysfs attribute
    "keyboard_position".
  - Re-reads BKBD state on resume from suspend or hibernation.

Tested on: Lenovo Yoga Book 9 14IAH10 (model 83KJ), kernel 7.0.

Signed-off-by: Dave Carey <carvsdriver@gmail.com>
---
v5:
- Rewrote as two WMI drivers (event + block) to avoid the deprecated
  wmi_query_block() API: event driver owns the GUID that delivers
  notifications, block driver owns the GUID used for querying state.
  Either can probe first; both probe callbacks check whether the other
  has already probed and fire the initial read when both are ready.
- Use wmidev_block_query() on the block wmi_device directly (replaces
  the deprecated wmi_query_block()).
- In the notify callback, read BKBD directly from the event data integer
  passed by the WMI core (_WED(0xEB) returns EC.BKBD; confirmed by
  decoding the embedded BMOF from WQDD).  Eliminates the redundant WQAF
  call that v4 made on every notification.
- Documented the two BMOF classes (LENOVO_BTKBD_EVENT and
  LENOVO_FEATURE_STATUS_DATA) with field layouts in the commit message
  and file header.
- sysfs keyboard_position: output bare integer, drop the parenthetical
  position-name suffix that a couple of reviewers found non-standard.
- Set .no_singleton = true on both WMI drivers (was missing from block
  driver in v4).
- Add PM resume callback (yb9_kbdock_resume) to re-read BKBD state
  after suspend/hibernation.

v4:
- Dropped module_wmi_driver(); registered two WMI drivers manually to
  allow sharing state.  module_init/exit pair registers/unregisters
  both.
- Added ATTRIBUTE_GROUPS() and .dev_groups for the sysfs attribute.
- sysfs show: performed a live query instead of returning a cached value.

v3:
- Switched to devm_input_allocate_device().
- Added DMI guard (dmi_check_system) to reject non-YB9 machines.
- Removed redundant MODULE_DEVICE_TABLE on block driver.

v2:
- Added .no_singleton = true on the event WMI driver.
- Added PM suspend/resume skeleton.
- Fixed MODULE_LICENSE to "GPL" (was "GPL v2").

 .../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      | 324 ++++++++++++++++++
 5 files changed, 365 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..04e5293
--- /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:		April 2026
+KernelVersion:	6.10
+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_LMI)	+= think-lmi.o
 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..28a3ec7
--- /dev/null
+++ b/drivers/platform/x86/lenovo/yb9-kbdock.c
@@ -0,0 +1,324 @@
+// 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.
+ *
+ * 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/dmi.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.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
+#define BKBD_TOP_HALF		1
+#define BKBD_BOTTOM_HALF	2
+
+static const struct dmi_system_id yb9_kbdock_dmi_table[] = {
+	{
+		/* Lenovo Yoga Book 9 14IAH10 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR,   "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
+		},
+	},
+	{ }
+};
+
+/*
+ * Shared state between the event and block WMI drivers.  Protected by lock.
+ * The lock may be held across wmidev_query_block() calls (which can sleep);
+ * block_remove() acquires the lock before clearing block_wdev, ensuring
+ * block_wdev remains valid for the duration of any in-progress query.
+ */
+static struct {
+	struct mutex lock;
+	struct input_dev *input_dev;	/* set by event probe */
+	struct wmi_device *block_wdev;	/* set by block probe */
+} yb9;
+
+/*
+ * Read BKBD from the block device via wmidev_block_query().
+ * Returns 0-3 on success, -errno on failure.  Caller must hold yb9.lock.
+ */
+static int yb9_kbdock_query_locked(struct device *log_dev)
+{
+	union acpi_object *obj;
+	u32 bkbd;
+
+	if (!yb9.block_wdev)
+		return -ENODEV;
+
+	obj = wmidev_block_query(yb9.block_wdev, 0);
+	if (!obj) {
+		dev_warn(log_dev, "WQAF returned NULL\n");
+		return -EIO;
+	}
+
+	/*
+	 * LENOVO_FEATURE_STATUS_DATA: 8-byte buffer {IDs=0x00060000, Status=BKBD}.
+	 */
+	if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length >= 8) {
+		memcpy(&bkbd, obj->buffer.pointer + 4, sizeof(bkbd));
+		bkbd &= 0x3;
+	} else if (obj->type == ACPI_TYPE_INTEGER) {
+		bkbd = obj->integer.value & 0x3;
+	} else {
+		dev_warn(log_dev, "WQAF: unexpected type %d len %u\n",
+			 obj->type,
+			 obj->type == ACPI_TYPE_BUFFER ? obj->buffer.length : 0);
+		kfree(obj);
+		return -EIO;
+	}
+
+	kfree(obj);
+	return (int)bkbd;
+}
+
+/* Report SW_TABLET_MODE from BKBD.  Caller must hold yb9.lock. */
+static void yb9_kbdock_report_locked(int bkbd, struct device *log_dev)
+{
+	int tablet = (bkbd == BKBD_DETACHED) ? 1 : 0;
+
+	input_report_switch(yb9.input_dev, SW_TABLET_MODE, tablet);
+	input_sync(yb9.input_dev);
+	dev_dbg(log_dev, "BKBD=%d SW_TABLET_MODE=%d\n", bkbd, tablet);
+}
+
+/* ------------------------------------------------------------------
+ * sysfs
+ * ------------------------------------------------------------------ */
+
+static ssize_t keyboard_position_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	int bkbd;
+
+	mutex_lock(&yb9.lock);
+	bkbd = yb9_kbdock_query_locked(dev);
+	mutex_unlock(&yb9.lock);
+
+	if (bkbd < 0)
+		return bkbd;
+	return sysfs_emit(buf, "%u\n", (unsigned int)bkbd);
+}
+static DEVICE_ATTR_RO(keyboard_position);
+
+static struct attribute *yb9_kbdock_attrs[] = {
+	&dev_attr_keyboard_position.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(yb9_kbdock);
+
+/* ------------------------------------------------------------------
+ * Event WMI driver — LENOVO_BTKBD_EVENT
+ * ------------------------------------------------------------------ */
+
+static void yb9_kbdock_notify(struct wmi_device *wdev, union acpi_object *data)
+{
+	u32 bkbd;
+
+	/*
+	 * _WED(0xEB) returns EC.BKBD directly as an integer
+	 * (LENOVO_BTKBD_EVENT WmiDataId(1) uint32 Status).
+	 */
+	if (!data || data->type != ACPI_TYPE_INTEGER) {
+		dev_warn(&wdev->dev, "unexpected event data type %d\n",
+			 data ? data->type : -1);
+		return;
+	}
+	bkbd = data->integer.value & 0x3;
+
+	mutex_lock(&yb9.lock);
+	if (yb9.input_dev)
+		yb9_kbdock_report_locked(bkbd, &wdev->dev);
+	mutex_unlock(&yb9.lock);
+}
+
+static int yb9_kbdock_event_probe(struct wmi_device *wdev, const void *ctx)
+{
+	struct input_dev *input_dev;
+	int bkbd, err;
+
+	if (!dmi_check_system(yb9_kbdock_dmi_table))
+		return -ENODEV;
+
+	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_EVENT_GUID "/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
+
+	err = input_register_device(input_dev);
+	if (err)
+		return err;
+
+	mutex_lock(&yb9.lock);
+	yb9.input_dev = input_dev;
+	/* Read initial state if the block device has already probed. */
+	if (yb9.block_wdev) {
+		bkbd = yb9_kbdock_query_locked(&wdev->dev);
+		if (bkbd >= 0)
+			yb9_kbdock_report_locked(bkbd, &wdev->dev);
+	}
+	mutex_unlock(&yb9.lock);
+
+	return 0;
+}
+
+static void yb9_kbdock_event_remove(struct wmi_device *wdev)
+{
+	mutex_lock(&yb9.lock);
+	yb9.input_dev = NULL;
+	mutex_unlock(&yb9.lock);
+}
+
+static int yb9_kbdock_resume(struct device *dev)
+{
+	int bkbd;
+
+	mutex_lock(&yb9.lock);
+	if (yb9.input_dev && yb9.block_wdev) {
+		bkbd = yb9_kbdock_query_locked(dev);
+		if (bkbd >= 0)
+			yb9_kbdock_report_locked(bkbd, dev);
+	}
+	mutex_unlock(&yb9.lock);
+	return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(yb9_kbdock_pm_ops, NULL, yb9_kbdock_resume);
+
+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",
+		.dev_groups = yb9_kbdock_groups,
+		.pm = pm_sleep_ptr(&yb9_kbdock_pm_ops),
+	},
+	.id_table = yb9_kbdock_event_id_table,
+	.no_singleton = true,
+	.probe = yb9_kbdock_event_probe,
+	.remove = yb9_kbdock_event_remove,
+	.notify = yb9_kbdock_notify,
+};
+
+/* ------------------------------------------------------------------
+ * Block WMI driver — LENOVO_FEATURE_STATUS_DATA
+ * ------------------------------------------------------------------ */
+
+static int yb9_kbdock_block_probe(struct wmi_device *wdev, const void *ctx)
+{
+	int bkbd;
+
+	if (!dmi_check_system(yb9_kbdock_dmi_table))
+		return -ENODEV;
+
+	mutex_lock(&yb9.lock);
+	yb9.block_wdev = wdev;
+	/* Read initial state if the event device has already probed. */
+	if (yb9.input_dev) {
+		bkbd = yb9_kbdock_query_locked(&wdev->dev);
+		if (bkbd >= 0)
+			yb9_kbdock_report_locked(bkbd, &wdev->dev);
+	}
+	mutex_unlock(&yb9.lock);
+	return 0;
+}
+
+static void yb9_kbdock_block_remove(struct wmi_device *wdev)
+{
+	mutex_lock(&yb9.lock);
+	yb9.block_wdev = NULL;
+	mutex_unlock(&yb9.lock);
+}
+
+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-block",
+	},
+	.id_table = yb9_kbdock_block_id_table,
+	.no_singleton = true,
+	.probe = yb9_kbdock_block_probe,
+	.remove = yb9_kbdock_block_remove,
+};
+
+/* ------------------------------------------------------------------
+ * Module init / exit
+ * ------------------------------------------------------------------ */
+
+static int __init yb9_kbdock_init(void)
+{
+	int ret;
+
+	mutex_init(&yb9.lock);
+
+	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.54.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-05-27 12:27 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-25 13:23 [PATCH] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Dave Carey
2026-04-28 14:39 ` Ilpo Järvinen
2026-05-17 15:01   ` Dave Carey
2026-05-17 15:02   ` [PATCH v2] platform/x86/lenovo: add Yoga Book 9 keyboard dock driver Dave Carey
2026-05-17 15:25     ` sashiko-bot
2026-05-19  0:02     ` [PATCH v3] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Dave Carey
2026-05-21 14:40       ` [PATCH v4] " Dave Carey
2026-05-22 21:59         ` Armin Wolf
2026-05-26 13:38           ` Dave Carey
2026-05-27 12:27         ` [PATCH v5] " Dave Carey

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.