* [PATCH] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
@ 2026-09-19 8:41 Marco Giunta
2026-09-20 6:42 ` Huang Wei
2026-09-20 20:28 ` [PATCH v2] " Marco Giunta
0 siblings, 2 replies; 20+ messages in thread
From: Marco Giunta @ 2026-09-19 8:41 UTC (permalink / raw)
To: Ilpo Järvinen, Mark Pearson, Hans de Goede
Cc: Ike Panhc, Derek J . Clark, platform-driver-x86, linux-kernel,
Marco Giunta
On certain 2025 Lenovo laptops, such as the Yoga Pro 7 14ASP10 and Legion
Pro 7 16AFR10H, a switch on the side disables the camera. This is handled
by the firmware, which also sends event 0x0d (camera disabled) or 0x0c
(camera enabled) through the WMI event GUID handled with
ideapad_wmi_context_fn_keys. These events are currently reported as
KEY_UNKNOWN.
Other Lenovo laptops expose a similar camera switch through a separate WMI
GUID, which lenovo-wmi-camera reports as SW_CAMERA_LENS_COVER since
commit d98bf6a6ed61 ("platform/x86: lenovo-wmi-camera: Use
SW_CAMERA_LENS_COVER instead of KEY_CAMERA_ACESS"). That driver does not
bind on these laptops, so do the same here: as the switch state cannot be
read from the firmware, register a separate input device with the switch
on the first event, and report the state carried by each event.
Signed-off-by: Marco Giunta <marco_giunta@outlook.it>
---
Notes:
* I considered a couple of alternatives to the approach in this patch:
1. Simply map 0x0c and 0x0d to KEY_CAMERA_ACCESS_ENABLE/DISABLE:
{ KE_KEY, 0x0c | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_ENABLE } },
{ KE_KEY, 0x0d | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_DISABLE } },
similar to the touchpad off/on hkey events (66/67) this driver emits
after the firmware has toggled the touchpad state. However, HUTRR72
describes these hkey events as asserting a camera access state for
the host to apply, rather than reporting one the firmware has
already applied. Furthermore, lenovo-wmi-camera moved away from
these hkey events in favor of SW_CAMERA_LENS_COVER in the quoted
commit, which is arguably the closest relevant precedent.
2. Ignore these events like the firmware-handled FnLock events:
{ KE_IGNORE, 0x0c | IDEAPAD_WMI_KEY },
{ KE_IGNORE, 0x0d | IDEAPAD_WMI_KEY },
This fixes the KEY_UNKNOWN issue, but userspace gets no information.
Overall, in the end I copied the approach of the lenovo-wmi-camera
driver. Kindly let me know if there are other/better solutions.
* Unlike the touchpad, whose state this driver reads with VPCCMD_R_TOUCHPAD
at probe, on resume and on each event, the camera switch state is only
available in EC-private fields, and the two tested laptops even use
different ones. Hence the lenovo-wmi-camera approach: the switch appears
on the first event, and a change made while suspended is not reported,
leaving the state wrong until the switch is toggled again.
* For reference, on KDE Plasma 6.7.5, using the Legion's touchpad toggle
button shows an OSD, but nothing on a SW_CAMERA_LENS_COVER change or if
sending KEY_CAMERA_ACCESS_ENABLE/DISABLE events. I am not sure whether
this is simply a case of "not implemented yet" or if I misunderstood how
these events are supposed to be used to communicate with userspace.
Please feel free to correct me here.
Best regards,
Marco
---
drivers/platform/x86/lenovo/ideapad-laptop.c | 56 ++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c
index 207368f5d489..c354b5d71399 100644
--- a/drivers/platform/x86/lenovo/ideapad-laptop.c
+++ b/drivers/platform/x86/lenovo/ideapad-laptop.c
@@ -178,6 +178,7 @@ struct ideapad_private {
struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
struct platform_device *platform_device;
struct input_dev *inputdev;
+ struct input_dev *camera_switch_idev;
struct backlight_device *blightdev;
struct ideapad_dytc_priv *dytc;
struct dentry *debug;
@@ -1410,10 +1411,43 @@ static int ideapad_input_init(struct ideapad_private *priv)
return err;
}
+static int ideapad_camera_switch_init(struct ideapad_private *priv, bool covered)
+{
+ struct input_dev *idev;
+ int err;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ idev->name = "Ideapad Camera Switch";
+ idev->phys = "ideapad/input1";
+ idev->id.bustype = BUS_HOST;
+ idev->dev.parent = &priv->platform_device->dev;
+
+ input_set_capability(idev, EV_SW, SW_CAMERA_LENS_COVER);
+ input_report_switch(idev, SW_CAMERA_LENS_COVER, covered);
+ input_sync(idev);
+
+ err = input_register_device(idev);
+ if (err) {
+ input_free_device(idev);
+ return err;
+ }
+
+ priv->camera_switch_idev = idev;
+ return 0;
+}
+
static void ideapad_input_exit(struct ideapad_private *priv)
{
input_unregister_device(priv->inputdev);
priv->inputdev = NULL;
+
+ if (priv->camera_switch_idev) {
+ input_unregister_device(priv->camera_switch_idev);
+ priv->camera_switch_idev = NULL;
+ }
}
static void ideapad_input_report(struct ideapad_private *priv,
@@ -1422,6 +1456,19 @@ static void ideapad_input_report(struct ideapad_private *priv,
sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
}
+static void ideapad_camera_switch_report(struct ideapad_private *priv, bool covered)
+{
+ if (!priv->camera_switch_idev) {
+ if (ideapad_camera_switch_init(priv, covered))
+ dev_warn(&priv->platform_device->dev,
+ "Failed to register camera switch input device\n");
+ return;
+ }
+
+ input_report_switch(priv->camera_switch_idev, SW_CAMERA_LENS_COVER, covered);
+ input_sync(priv->camera_switch_idev);
+}
+
static void ideapad_input_novokey(struct ideapad_private *priv)
{
unsigned long long_pressed;
@@ -2312,6 +2359,15 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
break;
}
+ /*
+ * Camera switch, handled by the firmware:
+ * 0x0c camera enabled, 0x0d camera disabled
+ */
+ if (data->integer.value == 0x0c || data->integer.value == 0x0d) {
+ ideapad_camera_switch_report(priv, data->integer.value == 0x0d);
+ break;
+ }
+
/* 0x02 FnLock, 0x03 Esc */
if (data->integer.value == 0x02 || data->integer.value == 0x03)
ideapad_fn_lock_led_notify(priv, data->integer.value == 0x02);
base-commit: f475845eaf3d749114a63270bf2efea459e14dd2
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-19 8:41 [PATCH] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER Marco Giunta
@ 2026-09-20 6:42 ` Huang Wei
2026-09-20 9:58 ` Marco Giunta
2026-09-20 20:28 ` [PATCH v2] " Marco Giunta
1 sibling, 1 reply; 20+ messages in thread
From: Huang Wei @ 2026-09-20 6:42 UTC (permalink / raw)
To: Marco Giunta
Cc: Hans de Goede, Ilpo Järvinen, platform-driver-x86, linux-kernel
Hi Marco,
I went through the patch and the approach looks right to me - matching
what lenovo-wmi-camera does with SW_CAMERA_LENS_COVER is the correct
call, and reporting the initial state before input_register_device()
is the right way for EVIOCGSW to pick it up.
I initially wondered whether the lazy device creation could race
against a concurrent notification, but ideapad_wmi_notify() holds
ideapad_shared_mutex across the whole handler, so the init is
serialized. It might be worth a comment on the init function saying
it relies on that, since it's not obvious from the code itself - I
had to go and check.
One small thing: if ideapad_camera_switch_init() fails, the state
carried by that event is dropped, and every later event retries the
registration and warns again. Probably fine in practice, but a
persistent failure would log one warning per toggle.
Thanks also for the notes on the alternatives and the
suspend-staleness limitation, that context is really helpful for
review.
Huang Wei
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-20 6:42 ` Huang Wei
@ 2026-09-20 9:58 ` Marco Giunta
2026-09-20 10:29 ` Huang Wei
0 siblings, 1 reply; 20+ messages in thread
From: Marco Giunta @ 2026-09-20 9:58 UTC (permalink / raw)
To: huangwei
Cc: hansg, ilpo.jarvinen, linux-kernel, marco_giunta, platform-driver-x86
Hi Huang,
Thanks for the review and the useful feedback.
> I went through the patch and the approach looks right to me - matching
> what lenovo-wmi-camera does with SW_CAMERA_LENS_COVER is the correct
> call, and reporting the initial state before input_register_device()
> is the right way for EVIOCGSW to pick it up.
Good to know the approach makes sense, thanks for confirming it.
> I initially wondered whether the lazy device creation could race
> against a concurrent notification, but ideapad_wmi_notify() holds
> ideapad_shared_mutex across the whole handler, so the init is
> serialized. It might be worth a comment on the init function saying
> it relies on that, since it's not obvious from the code itself - I
> had to go and check.
This is a fair point. Indeed the patch as it is relies *implicitly* on
the pre-existing mutex infrastructure.
I think there are at least two possible approaches to act on this.
1. A comment above ideapad_camera_switch_init() stating that the caller
holds ideapad_shared_mutex (or something along these lines);
2. using something like lockdep_assert_held(&ideapad_shared_mutex) in
ideapad_camera_switch_report(). This documents the requirement and
checks it on PROVE_LOCKING kernels, and is the approach recommended
by Documentation/locking/lockdep-design.rst (sec. "Annotations") and
Documentation/process/maintainer-tip.rst (sec. "Documenting locking
requirements").
As for 2, the documentation above is not specific to this subsystem, but
the same rationale seems to apply here, and the annotation is already used
elsewhere in platform/x86, e.g. in intel/sdsi.c and amd/hsmp/.
It would be one line that compiles away without CONFIG_LOCKDEP and warns
with CONFIG_PROVE_LOCKING.
On the other hand, I may be missing some context regarding how this class
of annotations is meant to be used. And of course 1 & 2 could be used
together.
Any preference or other thoughts?
> One small thing: if ideapad_camera_switch_init() fails, the state
> carried by that event is dropped, and every later event retries the
> registration and warns again. Probably fine in practice, but a
> persistent failure would log one warning per toggle.
That's another fair point. I admit I copy-pasted that dev_warn() from the
lenovo-wmi-camera driver without too much thought.
Do you think it's a good idea e.g. to replace dev_warn() with
dev_warn_once()? Or something else entirely?
> Thanks also for the notes on the alternatives and the
> suspend-staleness limitation, that context is really helpful for
> review.
Thank you for pointing this out, I have never included such a section
before and was wondering about its usefulness.
I will collect any further comment before sending out a v2.
Thanks again for your review and feedback.
Best regards,
Marco
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-20 9:58 ` Marco Giunta
@ 2026-09-20 10:29 ` Huang Wei
2026-09-20 10:58 ` Marco Giunta
0 siblings, 1 reply; 20+ messages in thread
From: Huang Wei @ 2026-09-20 10:29 UTC (permalink / raw)
To: Marco Giunta
Cc: Hans de Goede, Ilpo Järvinen, platform-driver-x86,
linux-kernel, huangwei
Hi Marco,
Thanks for the quick response.
On the mutex: I like the lockdep_assert_held() option better than my
comment suggestion, honestly. A comment can rot if someone later
changes the call path, while the assert keeps checking the invariant
at runtime (under lockdep). The sdsi/hsmp precedent you found is a
good one. I'd put it at the top of ideapad_camera_switch_report(), and
a one-line comment saying the WMI notify path is serialized through
ideapad_shared_mutex wouldn't hurt either.
On the warn: dev_warn_once() sounds right to me. The retry itself is
good behaviour (a transient failure recovers on the next event), so
keep that, just warn once. Persistent registration failures are rare
enough that the one-time warning carries the signal.
With those two, this looks good to me from my side.
Thanks,
Huang Wei
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-20 10:29 ` Huang Wei
@ 2026-09-20 10:58 ` Marco Giunta
0 siblings, 0 replies; 20+ messages in thread
From: Marco Giunta @ 2026-09-20 10:58 UTC (permalink / raw)
To: huangwei
Cc: hansg, ilpo.jarvinen, linux-kernel, marco_giunta, platform-driver-x86
Hi Huang,
Thanks, that settles both points. I will add
lockdep_assert_held(&ideapad_shared_mutex) with a short comment at the top
of ideapad_camera_switch_report(), and replace dew_warn() with
dev_warn_once().
I'll send a v2 either later today or within the next few days.
Marco
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
@ 2026-09-20 20:28 ` Marco Giunta
2026-09-21 2:36 ` Huang Wei
2026-09-21 14:50 ` Rong Zhang
0 siblings, 2 replies; 20+ messages in thread
From: Marco Giunta @ 2026-09-20 20:28 UTC (permalink / raw)
To: Ilpo Järvinen, Mark Pearson, Hans de Goede
Cc: Huang Wei, Derek J . Clark, Ike Panhc, platform-driver-x86,
linux-kernel, Marco Giunta
On certain 2025 Lenovo laptops, such as the Yoga Pro 7 14ASP10 and Legion
Pro 7 16AFR10H, a switch on the side disables the camera. This is handled
by the firmware, which also sends event 0x0d (camera disabled) or 0x0c
(camera enabled) through the WMI event GUID handled with
ideapad_wmi_context_fn_keys. These events are currently reported as
KEY_UNKNOWN.
Other Lenovo laptops expose a similar camera switch through a separate WMI
GUID, which lenovo-wmi-camera reports as SW_CAMERA_LENS_COVER since
commit d98bf6a6ed61 ("platform/x86: lenovo-wmi-camera: Use
SW_CAMERA_LENS_COVER instead of KEY_CAMERA_ACESS"). That driver does not
bind on these laptops, so do the same here: as the switch state cannot be
read from the firmware, register a separate input device with the switch
on the first event, and report the state carried by each event.
Signed-off-by: Marco Giunta <marco_giunta@outlook.it>
---
Changes in v2:
* Add lockdep_assert_held(&ideapad_shared_mutex) and a comment at the top
of ideapad_camera_switch_report() to document and check that the WMI
notify path is serialized through ideapad_shared_mutex (suggested by
Huang Wei)
* Replace dev_warn() with dev_warn_once() in
ideapad_camera_switch_report() so that a persistent registration failure
does not log a warning on every toggle (suggested by Huang Wei)
Link to v1: https://lore.kernel.org/platform-driver-x86/SN6PR19MB23039910DD1918BEE3825BAFFC862@SN6PR19MB2303.namprd19.prod.outlook.com/
Notes (copied verbatim from v1, to keep the context easily accessible):
* I considered a couple of alternatives to the approach in this patch:
1. Simply map 0x0c and 0x0d to KEY_CAMERA_ACCESS_ENABLE/DISABLE:
{ KE_KEY, 0x0c | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_ENABLE } },
{ KE_KEY, 0x0d | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_DISABLE } },
similar to the touchpad off/on hkey events (66/67) this driver emits
after the firmware has toggled the touchpad state. However, HUTRR72
describes these hkey events as asserting a camera access state for
the host to apply, rather than reporting one the firmware has
already applied. Furthermore, lenovo-wmi-camera moved away from
these hkey events in favor of SW_CAMERA_LENS_COVER in the quoted
commit, which is arguably the closest relevant precedent.
2. Ignore these events like the firmware-handled FnLock events:
{ KE_IGNORE, 0x0c | IDEAPAD_WMI_KEY },
{ KE_IGNORE, 0x0d | IDEAPAD_WMI_KEY },
This fixes the KEY_UNKNOWN issue, but userspace gets no information.
Overall, in the end I copied the approach of the lenovo-wmi-camera
driver. Kindly let me know if there are other/better solutions.
* Unlike the touchpad, whose state this driver reads with VPCCMD_R_TOUCHPAD
at probe, on resume and on each event, the camera switch state is only
available in EC-private fields, and the two tested laptops even use
different ones. Hence the lenovo-wmi-camera approach: the switch appears
on the first event, and a change made while suspended is not reported,
leaving the state wrong until the switch is toggled again.
* For reference, on KDE Plasma 6.7.5, using the Legion's touchpad toggle
button shows an OSD, but nothing on a SW_CAMERA_LENS_COVER change or if
sending KEY_CAMERA_ACCESS_ENABLE/DISABLE events. I am not sure whether
this is simply a case of "not implemented yet" or if I misunderstood how
these events are supposed to be used to communicate with userspace.
Please feel free to correct me here.
Best regards,
Marco
---
drivers/platform/x86/lenovo/ideapad-laptop.c | 59 ++++++++++++++++++++
1 file changed, 59 insertions(+)
diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c
index 207368f5d489..0066638ee332 100644
--- a/drivers/platform/x86/lenovo/ideapad-laptop.c
+++ b/drivers/platform/x86/lenovo/ideapad-laptop.c
@@ -178,6 +178,7 @@ struct ideapad_private {
struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
struct platform_device *platform_device;
struct input_dev *inputdev;
+ struct input_dev *camera_switch_idev;
struct backlight_device *blightdev;
struct ideapad_dytc_priv *dytc;
struct dentry *debug;
@@ -1410,10 +1411,43 @@ static int ideapad_input_init(struct ideapad_private *priv)
return err;
}
+static int ideapad_camera_switch_init(struct ideapad_private *priv, bool covered)
+{
+ struct input_dev *idev;
+ int err;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ idev->name = "Ideapad Camera Switch";
+ idev->phys = "ideapad/input1";
+ idev->id.bustype = BUS_HOST;
+ idev->dev.parent = &priv->platform_device->dev;
+
+ input_set_capability(idev, EV_SW, SW_CAMERA_LENS_COVER);
+ input_report_switch(idev, SW_CAMERA_LENS_COVER, covered);
+ input_sync(idev);
+
+ err = input_register_device(idev);
+ if (err) {
+ input_free_device(idev);
+ return err;
+ }
+
+ priv->camera_switch_idev = idev;
+ return 0;
+}
+
static void ideapad_input_exit(struct ideapad_private *priv)
{
input_unregister_device(priv->inputdev);
priv->inputdev = NULL;
+
+ if (priv->camera_switch_idev) {
+ input_unregister_device(priv->camera_switch_idev);
+ priv->camera_switch_idev = NULL;
+ }
}
static void ideapad_input_report(struct ideapad_private *priv,
@@ -1422,6 +1456,22 @@ static void ideapad_input_report(struct ideapad_private *priv,
sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
}
+static void ideapad_camera_switch_report(struct ideapad_private *priv, bool covered)
+{
+ /* The WMI notify path is serialized through ideapad_shared_mutex */
+ lockdep_assert_held(&ideapad_shared_mutex);
+
+ if (!priv->camera_switch_idev) {
+ if (ideapad_camera_switch_init(priv, covered))
+ dev_warn_once(&priv->platform_device->dev,
+ "Failed to register camera switch input device\n");
+ return;
+ }
+
+ input_report_switch(priv->camera_switch_idev, SW_CAMERA_LENS_COVER, covered);
+ input_sync(priv->camera_switch_idev);
+}
+
static void ideapad_input_novokey(struct ideapad_private *priv)
{
unsigned long long_pressed;
@@ -2312,6 +2362,15 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
break;
}
+ /*
+ * Camera switch, handled by the firmware:
+ * 0x0c camera enabled, 0x0d camera disabled
+ */
+ if (data->integer.value == 0x0c || data->integer.value == 0x0d) {
+ ideapad_camera_switch_report(priv, data->integer.value == 0x0d);
+ break;
+ }
+
/* 0x02 FnLock, 0x03 Esc */
if (data->integer.value == 0x02 || data->integer.value == 0x03)
ideapad_fn_lock_led_notify(priv, data->integer.value == 0x02);
base-commit: f475845eaf3d749114a63270bf2efea459e14dd2
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-20 20:28 ` [PATCH v2] " Marco Giunta
@ 2026-09-21 2:36 ` Huang Wei
2026-09-21 14:50 ` Rong Zhang
1 sibling, 0 replies; 20+ messages in thread
From: Huang Wei @ 2026-09-21 2:36 UTC (permalink / raw)
To: Marco Giunta
Cc: Hans de Goede, Ilpo Järvinen, platform-driver-x86,
linux-kernel, huangwei
Hi Marco,
The v2 looks good - the lockdep assertion with the comment and the
dev_warn_once() are exactly where they should be. Thanks for the quick
turnaround.
Reviewed-by: Huang Wei <huangwei@kylinos.cn>
Thanks,
Huang Wei
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-20 20:28 ` [PATCH v2] " Marco Giunta
2026-09-21 2:36 ` Huang Wei
@ 2026-09-21 14:50 ` Rong Zhang
2026-09-21 15:03 ` Rong Zhang
2026-09-21 19:00 ` Marco Giunta
1 sibling, 2 replies; 20+ messages in thread
From: Rong Zhang @ 2026-09-21 14:50 UTC (permalink / raw)
To: Marco Giunta, Ilpo Järvinen, Mark Pearson, Hans de Goede
Cc: Huang Wei, Derek J . Clark, Ike Panhc, platform-driver-x86, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 10143 bytes --]
Hi Macro,
On Sun, 2026-09-20 at 22:28 +0200, Marco Giunta wrote:
> On certain 2025 Lenovo laptops, such as the Yoga Pro 7 14ASP10 and Legion
> Pro 7 16AFR10H, a switch on the side disables the camera. This is handled
> by the firmware, which also sends event 0x0d (camera disabled) or 0x0c
> (camera enabled) through the WMI event GUID handled with
> ideapad_wmi_context_fn_keys. These events are currently reported as
> KEY_UNKNOWN.
>
> Other Lenovo laptops expose a similar camera switch through a separate WMI
> GUID, which lenovo-wmi-camera reports as SW_CAMERA_LENS_COVER since
> commit d98bf6a6ed61 ("platform/x86: lenovo-wmi-camera: Use
> SW_CAMERA_LENS_COVER instead of KEY_CAMERA_ACESS"). That driver does not
> bind on these laptops, so do the same here: as the switch state cannot be
> read from the firmware, register a separate input device with the switch
> on the first event, and report the state carried by each event.
>
> Signed-off-by: Marco Giunta <marco_giunta@outlook.it>
> ---
> Changes in v2:
>
> * Add lockdep_assert_held(&ideapad_shared_mutex) and a comment at the top
> of ideapad_camera_switch_report() to document and check that the WMI
> notify path is serialized through ideapad_shared_mutex (suggested by
> Huang Wei)
> * Replace dev_warn() with dev_warn_once() in
> ideapad_camera_switch_report() so that a persistent registration failure
> does not log a warning on every toggle (suggested by Huang Wei)
>
> Link to v1: https://lore.kernel.org/platform-driver-x86/SN6PR19MB23039910DD1918BEE3825BAFFC862@SN6PR19MB2303.namprd19.prod.outlook.com/
>
> Notes (copied verbatim from v1, to keep the context easily accessible):
>
> * I considered a couple of alternatives to the approach in this patch:
>
> 1. Simply map 0x0c and 0x0d to KEY_CAMERA_ACCESS_ENABLE/DISABLE:
>
> { KE_KEY, 0x0c | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_ENABLE } },
> { KE_KEY, 0x0d | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_DISABLE } },
>
> similar to the touchpad off/on hkey events (66/67) this driver emits
> after the firmware has toggled the touchpad state. However, HUTRR72
> describes these hkey events as asserting a camera access state for
> the host to apply, rather than reporting one the firmware has
> already applied.
>
Well, it really doesn't matter how the specification says; what matters
is how devices in reality do: whenever the HID Usage Table doesn't define
a usage for hardware/firmware to report the already-applied value but
defines a usage for notifying the host to apply a specific value, a lot
of devices will emerge, using the latter even when the former is
intended.
For example, many USB Audio Class devices come with volume control
buttons or knobs. Some of them tune the volume themselves as well as
emitting Volume Increment/Decrement HID events.
In this case, the audio stack, in response to the HID events,
intentionally overrides the volume set by hardware, effectively
synchronizing the hardware and software volume value.
The pattern has become so common that many devices like that also
implement an internal timeout mechanism. They only set the volume
themselves when reaching the timeout with no UAC volume control request
received.
IOW, it doesn't matter whether the hardware/firmware has done its job as
long as the subsequent software control is harmless. That's why we can
safely use KEY_TOUCHPAD_ON/OFF even if the firmware has toggled the
touchpad state. They cause the desktop environment to mask/unmask
touchpad input, which is essentially a no-op (thus a safe operation) when
the firmware has already disabled/enabled the touchpad.
Fundamentally speaking, using KEY_CAMERA_ACCESS_ENABLE/DISABLE is also
OK. It's just...
> Furthermore, lenovo-wmi-camera moved away from
> these hkey events in favor of SW_CAMERA_LENS_COVER in the quoted
> commit, which is arguably the closest relevant precedent.
...relatively new and isn't widely used compared to SW_CAMERA_LENS_COVER.
Thus, I agreed that using SW_CAMERA_LENS_COVER is a better approach as it
aligns with other drivers' behavior. The patch seems over-engineered
though, see below.
>
> 2. Ignore these events like the firmware-handled FnLock events:
>
> { KE_IGNORE, 0x0c | IDEAPAD_WMI_KEY },
> { KE_IGNORE, 0x0d | IDEAPAD_WMI_KEY },
>
> This fixes the KEY_UNKNOWN issue, but userspace gets no information.
>
> Overall, in the end I copied the approach of the lenovo-wmi-camera
> driver. Kindly let me know if there are other/better solutions.
>
> * Unlike the touchpad, whose state this driver reads with VPCCMD_R_TOUCHPAD
> at probe, on resume and on each event,
>
At least for the input device, the synchronization on probe or on resume
does not matter, as no input event is emitted due to !send_events.
> the camera switch state is only
> available in EC-private fields, and the two tested laptops even use
> different ones. Hence the lenovo-wmi-camera approach: the switch appears
> on the first event, and a change made while suspended is not reported,
> leaving the state wrong until the switch is toggled again.
So the difference compared to the lenovo-wmi-camera approach is only
about when and how the input device is registered.
On recent models, the firmware neither emits VPCCMD_R_TOUCHPAD nor
toggles the touchpad. It leaves the job to software by emitting WMI key
event 0x29, which is converted to KEY_TOUCHPAD_TOGGLE via ideapad_keymap.
It'd be tidier to take that approach and reuse the existing input device.
Sparse keymap supports KEY_SW, so it's viable to map the two WMI events
to SW_CAMERA_LENS_COVER with appropriate switch value.
I've attached a patch implementing the approach. Could you test if it
works on your devices? If so, you may submit my patch yourself by adding
a From: pseudo header with my identity and swapping the two Signed-off-
by: tags. I can also submit it myself as long as you permit me to do so.
>
> * For reference, on KDE Plasma 6.7.5, using the Legion's touchpad toggle
> button shows an OSD, but nothing on a SW_CAMERA_LENS_COVER change or if
> sending KEY_CAMERA_ACCESS_ENABLE/DISABLE events. I am not sure whether
> this is simply a case of "not implemented yet" or if I misunderstood how
> these events are supposed to be used to communicate with userspace.
> Please feel free to correct me here.
I believe KDE Plasma has not implemented it yet. You may raise an issue
on their Bugzilla.
Thanks,
Rong
>
> Best regards,
> Marco
> ---
> drivers/platform/x86/lenovo/ideapad-laptop.c | 59 ++++++++++++++++++++
> 1 file changed, 59 insertions(+)
>
> diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c
> index 207368f5d489..0066638ee332 100644
> --- a/drivers/platform/x86/lenovo/ideapad-laptop.c
> +++ b/drivers/platform/x86/lenovo/ideapad-laptop.c
> @@ -178,6 +178,7 @@ struct ideapad_private {
> struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
> struct platform_device *platform_device;
> struct input_dev *inputdev;
> + struct input_dev *camera_switch_idev;
> struct backlight_device *blightdev;
> struct ideapad_dytc_priv *dytc;
> struct dentry *debug;
> @@ -1410,10 +1411,43 @@ static int ideapad_input_init(struct ideapad_private *priv)
> return err;
> }
>
> +static int ideapad_camera_switch_init(struct ideapad_private *priv, bool covered)
> +{
> + struct input_dev *idev;
> + int err;
> +
> + idev = input_allocate_device();
> + if (!idev)
> + return -ENOMEM;
> +
> + idev->name = "Ideapad Camera Switch";
> + idev->phys = "ideapad/input1";
> + idev->id.bustype = BUS_HOST;
> + idev->dev.parent = &priv->platform_device->dev;
> +
> + input_set_capability(idev, EV_SW, SW_CAMERA_LENS_COVER);
> + input_report_switch(idev, SW_CAMERA_LENS_COVER, covered);
> + input_sync(idev);
> +
> + err = input_register_device(idev);
> + if (err) {
> + input_free_device(idev);
> + return err;
> + }
> +
> + priv->camera_switch_idev = idev;
> + return 0;
> +}
> +
> static void ideapad_input_exit(struct ideapad_private *priv)
> {
> input_unregister_device(priv->inputdev);
> priv->inputdev = NULL;
> +
> + if (priv->camera_switch_idev) {
> + input_unregister_device(priv->camera_switch_idev);
> + priv->camera_switch_idev = NULL;
> + }
> }
>
> static void ideapad_input_report(struct ideapad_private *priv,
> @@ -1422,6 +1456,22 @@ static void ideapad_input_report(struct ideapad_private *priv,
> sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
> }
>
> +static void ideapad_camera_switch_report(struct ideapad_private *priv, bool covered)
> +{
> + /* The WMI notify path is serialized through ideapad_shared_mutex */
> + lockdep_assert_held(&ideapad_shared_mutex);
> +
> + if (!priv->camera_switch_idev) {
> + if (ideapad_camera_switch_init(priv, covered))
> + dev_warn_once(&priv->platform_device->dev,
> + "Failed to register camera switch input device\n");
> + return;
> + }
> +
> + input_report_switch(priv->camera_switch_idev, SW_CAMERA_LENS_COVER, covered);
> + input_sync(priv->camera_switch_idev);
> +}
> +
> static void ideapad_input_novokey(struct ideapad_private *priv)
> {
> unsigned long long_pressed;
> @@ -2312,6 +2362,15 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
> break;
> }
>
> + /*
> + * Camera switch, handled by the firmware:
> + * 0x0c camera enabled, 0x0d camera disabled
> + */
> + if (data->integer.value == 0x0c || data->integer.value == 0x0d) {
> + ideapad_camera_switch_report(priv, data->integer.value == 0x0d);
> + break;
> + }
> +
> /* 0x02 FnLock, 0x03 Esc */
> if (data->integer.value == 0x02 || data->integer.value == 0x03)
> ideapad_fn_lock_led_notify(priv, data->integer.value == 0x02);
>
> base-commit: f475845eaf3d749114a63270bf2efea459e14dd2
[-- Attachment #2: 0001-platform-x86-ideapad-laptop-report-camera-switch-as-sw_camera_lens_cover.patch --]
[-- Type: text/x-patch, Size: 2419 bytes --]
From 69aff9a8705e2ee28a59722fa133c6e492fe526a Mon Sep 17 00:00:00 2001
From: Rong Zhang <i@rong.moe>
Date: Mon, 21 Sep 2026 21:47:17 +0800
Subject: [PATCH v3] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Message-Id: <20260921-ideapad-laptop-camera-sw-v3-1-f028f007f6d7@rong.moe>
X-Change-ID: 8511c6da-ideapad-laptop-camera-sw-948e4653b20e
On certain 2025 Lenovo laptops, such as the Yoga Pro 7 14ASP10 and Legion
Pro 7 16AFR10H, a switch on the side disables the camera. This is handled
by the firmware, which also sends event 0x0d (camera disabled) or 0x0c
(camera enabled) through the WMI event GUID handled with
ideapad_wmi_context_fn_keys. These events are currently reported as
KEY_UNKNOWN.
Other Lenovo laptops expose a similar camera switch through a separate WMI
GUID, which lenovo-wmi-camera reports as SW_CAMERA_LENS_COVER since
commit d98bf6a6ed61 ("platform/x86: lenovo-wmi-camera: Use
SW_CAMERA_LENS_COVER instead of KEY_CAMERA_ACESS"). That driver does not
bind on these laptops, so map the WMI events to SW_CAMERA_LENS_COVER as
well via sparse keymap KE_SW.
Co-developed-by: Marco Giunta <marco_giunta@outlook.it>
Signed-off-by: Marco Giunta <marco_giunta@outlook.it>
Signed-off-by: Rong Zhang <i@rong.moe>
---
drivers/platform/x86/lenovo/ideapad-laptop.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c
index 8213524504ee..3e91cc1df6c2 100644
--- a/drivers/platform/x86/lenovo/ideapad-laptop.c
+++ b/drivers/platform/x86/lenovo/ideapad-laptop.c
@@ -1365,6 +1365,18 @@ static const struct key_entry ideapad_keymap[] = {
{ KE_KEY, 0x29 | IDEAPAD_WMI_KEY, { KEY_TOUCHPAD_TOGGLE } },
{ KE_KEY, 0x2a | IDEAPAD_WMI_KEY, { KEY_ROOT_MENU } },
+ /*
+ * WMI switches
+ *
+ * Some devices come with physical switches. Whenever a switch is
+ * toggled, an WMI event is emitted to inform the software about the
+ * switch's latest state.
+ */
+
+ /* Camera switch */
+ { KE_SW, 0x0c | IDEAPAD_WMI_KEY, .sw = { SW_CAMERA_LENS_COVER, 0 } },
+ { KE_SW, 0x0d | IDEAPAD_WMI_KEY, .sw = { SW_CAMERA_LENS_COVER, 1 } },
+
{ KE_END },
};
---
base-commit: 518e5b794c06c0f0eb40df3e202274a66202c137
change-id: 8511c6da-ideapad-laptop-camera-sw-948e4653b20e
Thanks,
Rong
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-21 14:50 ` Rong Zhang
@ 2026-09-21 15:03 ` Rong Zhang
2026-09-21 19:00 ` Marco Giunta
1 sibling, 0 replies; 20+ messages in thread
From: Rong Zhang @ 2026-09-21 15:03 UTC (permalink / raw)
To: Marco Giunta, Ilpo Järvinen, Mark Pearson, Hans de Goede
Cc: Huang Wei, Derek J . Clark, Ike Panhc, platform-driver-x86, linux-kernel
Hi Marco,
On Mon, 2026-09-21 at 22:50 +0800, Rong Zhang wrote:
> Hi Macro,
Whoops, I mistyped your name. Sorry for that. My fingers definitely have
their own thoughts.
Thanks,
Rong
>
> On Sun, 2026-09-20 at 22:28 +0200, Marco Giunta wrote:
> > On certain 2025 Lenovo laptops, such as the Yoga Pro 7 14ASP10 and Legion
> > Pro 7 16AFR10H, a switch on the side disables the camera. This is handled
> > by the firmware, which also sends event 0x0d (camera disabled) or 0x0c
> > (camera enabled) through the WMI event GUID handled with
> > ideapad_wmi_context_fn_keys. These events are currently reported as
> > KEY_UNKNOWN.
> >
> > Other Lenovo laptops expose a similar camera switch through a separate WMI
> > GUID, which lenovo-wmi-camera reports as SW_CAMERA_LENS_COVER since
> > commit d98bf6a6ed61 ("platform/x86: lenovo-wmi-camera: Use
> > SW_CAMERA_LENS_COVER instead of KEY_CAMERA_ACESS"). That driver does not
> > bind on these laptops, so do the same here: as the switch state cannot be
> > read from the firmware, register a separate input device with the switch
> > on the first event, and report the state carried by each event.
> >
> > Signed-off-by: Marco Giunta <marco_giunta@outlook.it>
> > ---
> > Changes in v2:
> >
> > * Add lockdep_assert_held(&ideapad_shared_mutex) and a comment at the top
> > of ideapad_camera_switch_report() to document and check that the WMI
> > notify path is serialized through ideapad_shared_mutex (suggested by
> > Huang Wei)
> > * Replace dev_warn() with dev_warn_once() in
> > ideapad_camera_switch_report() so that a persistent registration failure
> > does not log a warning on every toggle (suggested by Huang Wei)
> >
> > Link to v1: https://lore.kernel.org/platform-driver-x86/SN6PR19MB23039910DD1918BEE3825BAFFC862@SN6PR19MB2303.namprd19.prod.outlook.com/
> >
> > Notes (copied verbatim from v1, to keep the context easily accessible):
> >
> > * I considered a couple of alternatives to the approach in this patch:
> >
> > 1. Simply map 0x0c and 0x0d to KEY_CAMERA_ACCESS_ENABLE/DISABLE:
> >
> > { KE_KEY, 0x0c | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_ENABLE } },
> > { KE_KEY, 0x0d | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_DISABLE } },
> >
> > similar to the touchpad off/on hkey events (66/67) this driver emits
> > after the firmware has toggled the touchpad state. However, HUTRR72
> > describes these hkey events as asserting a camera access state for
> > the host to apply, rather than reporting one the firmware has
> > already applied.
> >
>
> Well, it really doesn't matter how the specification says; what matters
> is how devices in reality do: whenever the HID Usage Table doesn't define
> a usage for hardware/firmware to report the already-applied value but
> defines a usage for notifying the host to apply a specific value, a lot
> of devices will emerge, using the latter even when the former is
> intended.
>
> For example, many USB Audio Class devices come with volume control
> buttons or knobs. Some of them tune the volume themselves as well as
> emitting Volume Increment/Decrement HID events.
>
> In this case, the audio stack, in response to the HID events,
> intentionally overrides the volume set by hardware, effectively
> synchronizing the hardware and software volume value.
>
> The pattern has become so common that many devices like that also
> implement an internal timeout mechanism. They only set the volume
> themselves when reaching the timeout with no UAC volume control request
> received.
>
> IOW, it doesn't matter whether the hardware/firmware has done its job as
> long as the subsequent software control is harmless. That's why we can
> safely use KEY_TOUCHPAD_ON/OFF even if the firmware has toggled the
> touchpad state. They cause the desktop environment to mask/unmask
> touchpad input, which is essentially a no-op (thus a safe operation) when
> the firmware has already disabled/enabled the touchpad.
>
> Fundamentally speaking, using KEY_CAMERA_ACCESS_ENABLE/DISABLE is also
> OK. It's just...
>
> > Furthermore, lenovo-wmi-camera moved away from
> > these hkey events in favor of SW_CAMERA_LENS_COVER in the quoted
> > commit, which is arguably the closest relevant precedent.
>
> ...relatively new and isn't widely used compared to SW_CAMERA_LENS_COVER.
>
> Thus, I agreed that using SW_CAMERA_LENS_COVER is a better approach as it
> aligns with other drivers' behavior. The patch seems over-engineered
> though, see below.
>
> >
> > 2. Ignore these events like the firmware-handled FnLock events:
> >
> > { KE_IGNORE, 0x0c | IDEAPAD_WMI_KEY },
> > { KE_IGNORE, 0x0d | IDEAPAD_WMI_KEY },
> >
> > This fixes the KEY_UNKNOWN issue, but userspace gets no information.
> >
> > Overall, in the end I copied the approach of the lenovo-wmi-camera
> > driver. Kindly let me know if there are other/better solutions.
> >
> > * Unlike the touchpad, whose state this driver reads with VPCCMD_R_TOUCHPAD
> > at probe, on resume and on each event,
> >
>
> At least for the input device, the synchronization on probe or on resume
> does not matter, as no input event is emitted due to !send_events.
>
> > the camera switch state is only
> > available in EC-private fields, and the two tested laptops even use
> > different ones. Hence the lenovo-wmi-camera approach: the switch appears
> > on the first event, and a change made while suspended is not reported,
> > leaving the state wrong until the switch is toggled again.
>
> So the difference compared to the lenovo-wmi-camera approach is only
> about when and how the input device is registered.
>
> On recent models, the firmware neither emits VPCCMD_R_TOUCHPAD nor
> toggles the touchpad. It leaves the job to software by emitting WMI key
> event 0x29, which is converted to KEY_TOUCHPAD_TOGGLE via ideapad_keymap.
> It'd be tidier to take that approach and reuse the existing input device.
> Sparse keymap supports KEY_SW, so it's viable to map the two WMI events
> to SW_CAMERA_LENS_COVER with appropriate switch value.
>
> I've attached a patch implementing the approach. Could you test if it
> works on your devices? If so, you may submit my patch yourself by adding
> a From: pseudo header with my identity and swapping the two Signed-off-
> by: tags. I can also submit it myself as long as you permit me to do so.
>
> >
> > * For reference, on KDE Plasma 6.7.5, using the Legion's touchpad toggle
> > button shows an OSD, but nothing on a SW_CAMERA_LENS_COVER change or if
> > sending KEY_CAMERA_ACCESS_ENABLE/DISABLE events. I am not sure whether
> > this is simply a case of "not implemented yet" or if I misunderstood how
> > these events are supposed to be used to communicate with userspace.
> > Please feel free to correct me here.
>
> I believe KDE Plasma has not implemented it yet. You may raise an issue
> on their Bugzilla.
>
> Thanks,
> Rong
>
> >
> > Best regards,
> > Marco
> > ---
> > drivers/platform/x86/lenovo/ideapad-laptop.c | 59 ++++++++++++++++++++
> > 1 file changed, 59 insertions(+)
> >
> > diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c
> > index 207368f5d489..0066638ee332 100644
> > --- a/drivers/platform/x86/lenovo/ideapad-laptop.c
> > +++ b/drivers/platform/x86/lenovo/ideapad-laptop.c
> > @@ -178,6 +178,7 @@ struct ideapad_private {
> > struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
> > struct platform_device *platform_device;
> > struct input_dev *inputdev;
> > + struct input_dev *camera_switch_idev;
> > struct backlight_device *blightdev;
> > struct ideapad_dytc_priv *dytc;
> > struct dentry *debug;
> > @@ -1410,10 +1411,43 @@ static int ideapad_input_init(struct ideapad_private *priv)
> > return err;
> > }
> >
> > +static int ideapad_camera_switch_init(struct ideapad_private *priv, bool covered)
> > +{
> > + struct input_dev *idev;
> > + int err;
> > +
> > + idev = input_allocate_device();
> > + if (!idev)
> > + return -ENOMEM;
> > +
> > + idev->name = "Ideapad Camera Switch";
> > + idev->phys = "ideapad/input1";
> > + idev->id.bustype = BUS_HOST;
> > + idev->dev.parent = &priv->platform_device->dev;
> > +
> > + input_set_capability(idev, EV_SW, SW_CAMERA_LENS_COVER);
> > + input_report_switch(idev, SW_CAMERA_LENS_COVER, covered);
> > + input_sync(idev);
> > +
> > + err = input_register_device(idev);
> > + if (err) {
> > + input_free_device(idev);
> > + return err;
> > + }
> > +
> > + priv->camera_switch_idev = idev;
> > + return 0;
> > +}
> > +
> > static void ideapad_input_exit(struct ideapad_private *priv)
> > {
> > input_unregister_device(priv->inputdev);
> > priv->inputdev = NULL;
> > +
> > + if (priv->camera_switch_idev) {
> > + input_unregister_device(priv->camera_switch_idev);
> > + priv->camera_switch_idev = NULL;
> > + }
> > }
> >
> > static void ideapad_input_report(struct ideapad_private *priv,
> > @@ -1422,6 +1456,22 @@ static void ideapad_input_report(struct ideapad_private *priv,
> > sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
> > }
> >
> > +static void ideapad_camera_switch_report(struct ideapad_private *priv, bool covered)
> > +{
> > + /* The WMI notify path is serialized through ideapad_shared_mutex */
> > + lockdep_assert_held(&ideapad_shared_mutex);
> > +
> > + if (!priv->camera_switch_idev) {
> > + if (ideapad_camera_switch_init(priv, covered))
> > + dev_warn_once(&priv->platform_device->dev,
> > + "Failed to register camera switch input device\n");
> > + return;
> > + }
> > +
> > + input_report_switch(priv->camera_switch_idev, SW_CAMERA_LENS_COVER, covered);
> > + input_sync(priv->camera_switch_idev);
> > +}
> > +
> > static void ideapad_input_novokey(struct ideapad_private *priv)
> > {
> > unsigned long long_pressed;
> > @@ -2312,6 +2362,15 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
> > break;
> > }
> >
> > + /*
> > + * Camera switch, handled by the firmware:
> > + * 0x0c camera enabled, 0x0d camera disabled
> > + */
> > + if (data->integer.value == 0x0c || data->integer.value == 0x0d) {
> > + ideapad_camera_switch_report(priv, data->integer.value == 0x0d);
> > + break;
> > + }
> > +
> > /* 0x02 FnLock, 0x03 Esc */
> > if (data->integer.value == 0x02 || data->integer.value == 0x03)
> > ideapad_fn_lock_led_notify(priv, data->integer.value == 0x02);
> >
> > base-commit: f475845eaf3d749114a63270bf2efea459e14dd2
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-21 14:50 ` Rong Zhang
2026-09-21 15:03 ` Rong Zhang
@ 2026-09-21 19:00 ` Marco Giunta
2026-09-22 11:08 ` Huang Wei
2026-09-22 12:18 ` Rong Zhang
1 sibling, 2 replies; 20+ messages in thread
From: Marco Giunta @ 2026-09-21 19:00 UTC (permalink / raw)
To: i
Cc: derekjohn.clark, hansg, huangwei, ikepanhc, ilpo.jarvinen,
linux-kernel, marco_giunta, mpearson-lenovo, platform-driver-x86
Hi Rong,
Thank you so much for your detailed reply and the much improved patch.
Let me immediately go on record and say that, apart from a small addition
to the SMI driver and one line in ideapad-laptop, I am mostly active in
the sound system, and therefore consider myself a platform/x86 newbie.
So I really appreciate you sharing your knowledge here.
> > * I considered a couple of alternatives to the approach in this patch:
> >
> > 1. Simply map 0x0c and 0x0d to KEY_CAMERA_ACCESS_ENABLE/DISABLE:
> >
> > { KE_KEY, 0x0c | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_ENABLE } },
> > { KE_KEY, 0x0d | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_DISABLE } },
> >
> > similar to the touchpad off/on hkey events (66/67) this driver emits
> > after the firmware has toggled the touchpad state. However, HUTRR72
> > describes these hkey events as asserting a camera access state for
> > the host to apply, rather than reporting one the firmware has
> > already applied.
> >
>
> Well, it really doesn't matter how the specification says; what matters
> is how devices in reality do: whenever the HID Usage Table doesn't define
> a usage for hardware/firmware to report the already-applied value but
> defines a usage for notifying the host to apply a specific value, a lot
> of devices will emerge, using the latter even when the former is
> intended.
>
> For example, many USB Audio Class devices come with volume control
> buttons or knobs. Some of them tune the volume themselves as well as
> emitting Volume Increment/Decrement HID events.
>
> In this case, the audio stack, in response to the HID events,
> intentionally overrides the volume set by hardware, effectively
> synchronizing the hardware and software volume value.
>
> The pattern has become so common that many devices like that also
> implement an internal timeout mechanism. They only set the volume
> themselves when reaching the timeout with no UAC volume control request
> received.
>
> IOW, it doesn't matter whether the hardware/firmware has done its job as
> long as the subsequent software control is harmless. That's why we can
> safely use KEY_TOUCHPAD_ON/OFF even if the firmware has toggled the
> touchpad state. They cause the desktop environment to mask/unmask
> touchpad input, which is essentially a no-op (thus a safe operation) when
> the firmware has already disabled/enabled the touchpad.
>
> Fundamentally speaking, using KEY_CAMERA_ACCESS_ENABLE/DISABLE is also
> OK. It's just...
>
> > Furthermore, lenovo-wmi-camera moved away from
> > these hkey events in favor of SW_CAMERA_LENS_COVER in the quoted
> > commit, which is arguably the closest relevant precedent.
>
> ...relatively new and isn't widely used compared to SW_CAMERA_LENS_COVER.
>
> Thus, I agreed that using SW_CAMERA_LENS_COVER is a better approach as it
> aligns with other drivers' behavior. The patch seems over-engineered
> though, see below.
This seems pretty reasonable to me, I figured that a single standard was
hard to enforce and also not strictly needed in practice (the "harmless
no-op" argument above).
I was mostly wondering about certain discussions I saw online while doing
research for this patch of people arguing that such a no-op should be
avoided, see e.g.:
https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/merge_requests/181
Still, your point that userspace duplicating an action already applied by
the hardware is common practice, and fine as long as it is harmless, makes
perfect sense to me. So overall I have no objections to your reasoning here.
> >
> > 2. Ignore these events like the firmware-handled FnLock events:
> >
> > { KE_IGNORE, 0x0c | IDEAPAD_WMI_KEY },
> > { KE_IGNORE, 0x0d | IDEAPAD_WMI_KEY },
> >
> > This fixes the KEY_UNKNOWN issue, but userspace gets no information.
> >
> > Overall, in the end I copied the approach of the lenovo-wmi-camera
> > driver. Kindly let me know if there are other/better solutions.
> >
> > * Unlike the touchpad, whose state this driver reads with VPCCMD_R_TOUCHPAD
> > at probe, on resume and on each event,
> >
>
> At least for the input device, the synchronization on probe or on resume
> does not matter, as no input event is emitted due to !send_events.
Good point, I was conflating stuff.
> > the camera switch state is only
> > available in EC-private fields, and the two tested laptops even use
> > different ones. Hence the lenovo-wmi-camera approach: the switch appears
> > on the first event, and a change made while suspended is not reported,
> > leaving the state wrong until the switch is toggled again.
>
> So the difference compared to the lenovo-wmi-camera approach is only
> about when and how the input device is registered.
>
> On recent models, the firmware neither emits VPCCMD_R_TOUCHPAD nor
> toggles the touchpad. It leaves the job to software by emitting WMI key
> event 0x29, which is converted to KEY_TOUCHPAD_TOGGLE via ideapad_keymap.
> It'd be tidier to take that approach and reuse the existing input device.
> Sparse keymap supports KEY_SW, so it's viable to map the two WMI events
> to SW_CAMERA_LENS_COVER with appropriate switch value.
This makes a lot of sense to me. I appreciate the simplicity of this
approach.
Part of the reason why I initially adopted the "separate input device"
approach was this comment from
drivers/platform/x86/dell/dell-wmi-privacy.c:
/*
* Unlike keys where only presses matter, userspace may act
* on switches in both of their positions. Only register
* SW_CAMERA_LENS_COVER if it is actually there.
*/
and so I implemented a conditional registration (which was then easy to do
by copying the overall approach of the lenovo-wmi-camera driver).
However, I understand that with your patch the switch will simply be
advertised by the existing input device with a default state of 0,
i.e. "camera enabled", which never changes on a device without a
privacy switch, and is indeed a correct description of it. So I
don't think there's any harm in going with your approach.
> I've attached a patch implementing the approach. Could you test if it
> works on your devices?
I compiled it on my Legion and it works as expected. Right after booting,
evtest says:
Input driver version is 1.0.1
Input device ID: bus 0x19 vendor 0x0 product 0x0 version 0x0
Input device name: "Ideapad extra buttons"
Supported events:
Event type 0 (EV_SYN)
Event type 1 (EV_KEY)
Event code 1 (KEY_ESC)
Event code 138 (KEY_HELP)
Event code 148 (KEY_PROG1)
Event code 149 (KEY_PROG2)
Event code 186 (KEY_F16)
Event code 202 (KEY_PROG3)
Event code 203 (KEY_PROG4)
Event code 212 (KEY_CAMERA)
Event code 227 (KEY_SWITCHVIDEOMODE)
Event code 240 (KEY_UNKNOWN)
Event code 247 (KEY_RFKILL)
Event code 248 (KEY_MICMUTE)
Event code 364 (KEY_FAVORITES)
Event code 445 (KEY_PICKUP_PHONE)
Event code 446 (KEY_HANGUP_PHONE)
Event code 447 (?) # commit bc39af8c2c43 was included in this kernel
Event code 530 (KEY_TOUCHPAD_TOGGLE)
Event code 531 (KEY_TOUCHPAD_ON)
Event code 532 (KEY_TOUCHPAD_OFF)
Event code 562 (KEY_REFRESH_RATE_TOGGLE)
Event code 618 (KEY_ROOT_MENU)
Event code 634 (KEY_SELECTIVE_SCREENSHOT)
Event type 4 (EV_MSC)
Event code 4 (MSC_SCAN)
Event type 5 (EV_SW)
Event code 9 (SW_CAMERA_LENS_COVER) state 0
Starting from the "camera enabled" state and toggling the privacy switch
twice causes:
Event: time 1790013067.697647, type 5 (EV_SW), code 9 (SW_CAMERA_LENS_COVER), value 1
Event: time 1790013067.697647, -------------- SYN_REPORT ------------
Event: time 1790013069.208581, type 5 (EV_SW), code 9 (SW_CAMERA_LENS_COVER), value 0
Event: time 1790013069.208581, -------------- SYN_REPORT ------------
This is the same behavior as my original patch, just on the same input
device as everything else (as expected).
The switch starts on state 0 at boot even if the camera was disabled
while the device was turned off, in which case a new event is reported only
after toggling the switch twice. However this is expected and is the same
behavior as in the original patch if e.g. the switch is toggled during
suspend, so this is not a critique, only a confirmation that your patch
works as I expected.
> If so, you may submit my patch yourself by adding
> a From: pseudo header with my identity and swapping the two Signed-off-
> by: tags. I can also submit it myself as long as you permit me to do so.
I am happy to have you submit the new patch as its main author; it's your
code now!
I'll only add that, apart from
Co-developed-by: Marco Giunta <marco_giunta@outlook.it>
Signed-off-by: Marco Giunta <marco_giunta@outlook.it>
I think it's also appropriate to add
Tested-by: Marco Giunta <marco_giunta@outlook.it>
> >
> > * For reference, on KDE Plasma 6.7.5, using the Legion's touchpad toggle
> > button shows an OSD, but nothing on a SW_CAMERA_LENS_COVER change or if
> > sending KEY_CAMERA_ACCESS_ENABLE/DISABLE events. I am not sure whether
> > this is simply a case of "not implemented yet" or if I misunderstood how
> > these events are supposed to be used to communicate with userspace.
> > Please feel free to correct me here.
>
> I believe KDE Plasma has not implemented it yet. You may raise an issue
> on their Bugzilla.
>
Noted, thanks.
> > Hi Macro,
>
> Whoops, I mistyped your name. Sorry for that. My fingers definitely have
> their own thoughts.
No worries!!
Thank you again for your very informative review and much simpler patch.
And please feel free to correct me again if there are further inaccuracies
in my understanding of the problem.
Best regards,
Marco
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-21 19:00 ` Marco Giunta
@ 2026-09-22 11:08 ` Huang Wei
2026-09-22 12:18 ` Rong Zhang
1 sibling, 0 replies; 20+ messages in thread
From: Huang Wei @ 2026-09-22 11:08 UTC (permalink / raw)
To: Marco Giunta, Rong Zhang
Cc: Ilpo Järvinen, Hans de Goede, Mark Pearson,
platform-driver-x86, linux-kernel, huangwei
Hi Marco, Rong,
This is a nice outcome - the KE_SW version is clearly the better shape
for this. It also has good company: intel-vbtn maps its tablet-mode
events the same way (0xCC/0xCD -> SW_TABLET_MODE), and Lenovo's own
ymc driver does KE_SW for the Yoga modes, so it should be an easy call
for the maintainers.
Amusingly, my v2 suggestions die with the code they were guarding -
which is the best way for suggestions to go.
Looking forward to the v3.
Thanks,
Huang Wei
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-21 19:00 ` Marco Giunta
2026-09-22 11:08 ` Huang Wei
@ 2026-09-22 12:18 ` Rong Zhang
2026-09-22 15:52 ` Mark Pearson
1 sibling, 1 reply; 20+ messages in thread
From: Rong Zhang @ 2026-09-22 12:18 UTC (permalink / raw)
To: Marco Giunta
Cc: derekjohn.clark, hansg, huangwei, ikepanhc, ilpo.jarvinen,
linux-kernel, mpearson-lenovo, platform-driver-x86
Hi Marco,
On Mon, 2026-09-21 at 21:00 +0200, Marco Giunta wrote:
> Hi Rong,
>
> Thank you so much for your detailed reply and the much improved patch.
>
> Let me immediately go on record and say that, apart from a small addition
> to the SMI driver and one line in ideapad-laptop, I am mostly active in
> the sound system, and therefore consider myself a platform/x86 newbie.
> So I really appreciate you sharing your knowledge here.
>
> > > * I considered a couple of alternatives to the approach in this patch:
> > >
> > > 1. Simply map 0x0c and 0x0d to KEY_CAMERA_ACCESS_ENABLE/DISABLE:
> > >
> > > { KE_KEY, 0x0c | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_ENABLE } },
> > > { KE_KEY, 0x0d | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_DISABLE } },
> > >
> > > similar to the touchpad off/on hkey events (66/67) this driver emits
> > > after the firmware has toggled the touchpad state. However, HUTRR72
> > > describes these hkey events as asserting a camera access state for
> > > the host to apply, rather than reporting one the firmware has
> > > already applied.
> > >
> >
> > Well, it really doesn't matter how the specification says; what matters
> > is how devices in reality do: whenever the HID Usage Table doesn't define
> > a usage for hardware/firmware to report the already-applied value but
> > defines a usage for notifying the host to apply a specific value, a lot
> > of devices will emerge, using the latter even when the former is
> > intended.
> >
> > For example, many USB Audio Class devices come with volume control
> > buttons or knobs. Some of them tune the volume themselves as well as
> > emitting Volume Increment/Decrement HID events.
> >
> > In this case, the audio stack, in response to the HID events,
> > intentionally overrides the volume set by hardware, effectively
> > synchronizing the hardware and software volume value.
> >
> > The pattern has become so common that many devices like that also
> > implement an internal timeout mechanism. They only set the volume
> > themselves when reaching the timeout with no UAC volume control request
> > received.
> >
> > IOW, it doesn't matter whether the hardware/firmware has done its job as
> > long as the subsequent software control is harmless. That's why we can
> > safely use KEY_TOUCHPAD_ON/OFF even if the firmware has toggled the
> > touchpad state. They cause the desktop environment to mask/unmask
> > touchpad input, which is essentially a no-op (thus a safe operation) when
> > the firmware has already disabled/enabled the touchpad.
> >
> > Fundamentally speaking, using KEY_CAMERA_ACCESS_ENABLE/DISABLE is also
> > OK. It's just...
> >
> > > Furthermore, lenovo-wmi-camera moved away from
> > > these hkey events in favor of SW_CAMERA_LENS_COVER in the quoted
> > > commit, which is arguably the closest relevant precedent.
> >
> > ...relatively new and isn't widely used compared to SW_CAMERA_LENS_COVER.
> >
> > Thus, I agreed that using SW_CAMERA_LENS_COVER is a better approach as it
> > aligns with other drivers' behavior. The patch seems over-engineered
> > though, see below.
>
> This seems pretty reasonable to me, I figured that a single standard was
> hard to enforce and also not strictly needed in practice (the "harmless
> no-op" argument above).
> I was mostly wondering about certain discussions I saw online while doing
> research for this patch of people arguing that such a no-op should be
> avoided, see e.g.:
> https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/merge_requests/181
Thanks for the context. I didn't know the behavior of GNOME as I use KDE
Plasma, whose behavior is unmask/mask touchpad events on
KEY_TOUCHPAD_ON/OFF.
>
> Still, your point that userspace duplicating an action already applied by
> the hardware is common practice, and fine as long as it is harmless, makes
> perfect sense to me. So overall I have no objections to your reasoning here.
>
> > >
> > > 2. Ignore these events like the firmware-handled FnLock events:
> > >
> > > { KE_IGNORE, 0x0c | IDEAPAD_WMI_KEY },
> > > { KE_IGNORE, 0x0d | IDEAPAD_WMI_KEY },
> > >
> > > This fixes the KEY_UNKNOWN issue, but userspace gets no information.
> > >
> > > Overall, in the end I copied the approach of the lenovo-wmi-camera
> > > driver. Kindly let me know if there are other/better solutions.
> > >
> > > * Unlike the touchpad, whose state this driver reads with VPCCMD_R_TOUCHPAD
> > > at probe, on resume and on each event,
> > >
> >
> > At least for the input device, the synchronization on probe or on resume
> > does not matter, as no input event is emitted due to !send_events.
>
> Good point, I was conflating stuff.
>
> > > the camera switch state is only
> > > available in EC-private fields, and the two tested laptops even use
> > > different ones. Hence the lenovo-wmi-camera approach: the switch appears
> > > on the first event, and a change made while suspended is not reported,
> > > leaving the state wrong until the switch is toggled again.
> >
> > So the difference compared to the lenovo-wmi-camera approach is only
> > about when and how the input device is registered.
> >
> > On recent models, the firmware neither emits VPCCMD_R_TOUCHPAD nor
> > toggles the touchpad. It leaves the job to software by emitting WMI key
> > event 0x29, which is converted to KEY_TOUCHPAD_TOGGLE via ideapad_keymap.
> > It'd be tidier to take that approach and reuse the existing input device.
> > Sparse keymap supports KEY_SW, so it's viable to map the two WMI events
> > to SW_CAMERA_LENS_COVER with appropriate switch value.
>
> This makes a lot of sense to me. I appreciate the simplicity of this
> approach.
>
> Part of the reason why I initially adopted the "separate input device"
> approach was this comment from
> drivers/platform/x86/dell/dell-wmi-privacy.c:
>
> /*
> * Unlike keys where only presses matter, userspace may act
> * on switches in both of their positions. Only register
> * SW_CAMERA_LENS_COVER if it is actually there.
> */
Hmm, this makes some sense. Ideally we should prevent exposing
SW_CAMERA_LENS_COVER unless it's there. I wonder, if the capability is
declared in LENOVO_UTILITY_DATA? AFAIK, LENOVO_UTILITY_EVENT (8FC0DE0C-
B4E4-43FD-B0F3-8871711C1294) shares the same ACPI device as
LENOVO_UTILITY_DATA (CE6C0974-0407-4F50-88BA-4FC3B6559AD8). The former's
driver is ideapad-laptop, while latter's driver is lenovo-wmi-hotkey-
utilities.
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Lenovo Utility Key Press Event"), guid("{8fc0de0c-b4e4-43fd-b0f3-8871711c1294}")]
class LENOVO_UTILITY_EVENT : WMIEvent {
[key, read] string InstanceName;
[read] boolean Active;
[WmiDataId(1), read, Description("Lenovo Utility Press key Event")] uint32 PressTypeDataVal;
};
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO_UTILITY_DATA class"), guid("{ce6c0974-0407-4f50-88ba-4fc3b6559ad8}")]
class LENOVO_UTILITY_DATA {
[key, read] string InstanceName;
[read] boolean Active;
[WmiMethodId(1), Implemented, Description("Utility 3.1 function is Support or the function Version")] void GetIfSupportOrVersion([in, Description("Control type")] uint32 datatype, [out, Description("0 is not support 1,2,3...version")] uint32 Data);
[WmiMethodId(2), Implemented, Description("Utility 3.1 Set feature function ")] void SetFeature([in, Description("Control type")] uint32 featuretype, [out, Description("Control result 0 is success 1,2,3...is failed")] uint32 Data);
[WmiMethodId(3), Implemented, Description("Utility 3.2 Set feature function ")] void SetFeatureEx([in] uint32 IDs, [in] uint32 Value, [out, Description("result 0 is success 1,2,3...is failed")] uint32 Ret);
};
I just checked several DSDT tables from different devices (they don't
have camera switches though). The ACPI device is usually \_SB.WMIU. The
former's WMI event is usually implemented by \_SB.WMIU._WED, while the
latter's WMI methods are usually implemented by \_SB.WMIU.WMSK. There may
be some capabilities defined in the first WMI method, and the WMI event
may also reveal some details in EC query handling.
Could you dump the ACPI tables of the two devices and attach the acpidump
files in your reply?
As you've said, "the camera switch state is only available in EC-private
fields, and the two tested laptops even use different ones." Could you
elaborate it as well? Such information may be helpful when cross-
referencing ACPI tables.
>
> and so I implemented a conditional registration (which was then easy to do
> by copying the overall approach of the lenovo-wmi-camera driver).
>
> However, I understand that with your patch the switch will simply be
> advertised by the existing input device with a default state of 0,
> i.e. "camera enabled", which never changes on a device without a
> privacy switch, and is indeed a correct description of it. So I
> don't think there's any harm in going with your approach.
From my perspective, the hazard on exposing SW_CAMERA_LENS_COVER
unconditionally is mostly theoretical, as there is hardly any userspace
program handling it. I believe the reason why dell-wmi-privacy exposes it
conditionally is that it emits an event on probe even when the state is
0. If there is any userspace program handling SW_CAMERA_LENS_COVER, I
really doubt if it will consult EVIOCGBIT() for the initial state. IOW,
having a 0 initial state should be safe as you've said, as long as we
don't emit an SW_CAMERA_LENS_COVER event on probe.
Of course, your approach is also somewhat reasonable considering the
hazard, but I still consider the dedicated input device as unnecessary.
We can simply reregister the existing input device with KE_SW entries if
we had to take that approach.
Still, it'd be better if it's exposed only when the device has such a
capability. Let's see if we can query the capability programmatically
first...
>
> > I've attached a patch implementing the approach. Could you test if it
> > works on your devices?
>
> I compiled it on my Legion and it works as expected. Right after booting,
> evtest says:
>
> Input driver version is 1.0.1
> Input device ID: bus 0x19 vendor 0x0 product 0x0 version 0x0
> Input device name: "Ideapad extra buttons"
> Supported events:
> Event type 0 (EV_SYN)
> Event type 1 (EV_KEY)
> Event code 1 (KEY_ESC)
> Event code 138 (KEY_HELP)
> Event code 148 (KEY_PROG1)
> Event code 149 (KEY_PROG2)
> Event code 186 (KEY_F16)
> Event code 202 (KEY_PROG3)
> Event code 203 (KEY_PROG4)
> Event code 212 (KEY_CAMERA)
> Event code 227 (KEY_SWITCHVIDEOMODE)
> Event code 240 (KEY_UNKNOWN)
> Event code 247 (KEY_RFKILL)
> Event code 248 (KEY_MICMUTE)
> Event code 364 (KEY_FAVORITES)
> Event code 445 (KEY_PICKUP_PHONE)
> Event code 446 (KEY_HANGUP_PHONE)
> Event code 447 (?) # commit bc39af8c2c43 was included in this kernel
> Event code 530 (KEY_TOUCHPAD_TOGGLE)
> Event code 531 (KEY_TOUCHPAD_ON)
> Event code 532 (KEY_TOUCHPAD_OFF)
> Event code 562 (KEY_REFRESH_RATE_TOGGLE)
> Event code 618 (KEY_ROOT_MENU)
> Event code 634 (KEY_SELECTIVE_SCREENSHOT)
> Event type 4 (EV_MSC)
> Event code 4 (MSC_SCAN)
> Event type 5 (EV_SW)
> Event code 9 (SW_CAMERA_LENS_COVER) state 0
>
> Starting from the "camera enabled" state and toggling the privacy switch
> twice causes:
>
> Event: time 1790013067.697647, type 5 (EV_SW), code 9 (SW_CAMERA_LENS_COVER), value 1
> Event: time 1790013067.697647, -------------- SYN_REPORT ------------
> Event: time 1790013069.208581, type 5 (EV_SW), code 9 (SW_CAMERA_LENS_COVER), value 0
> Event: time 1790013069.208581, -------------- SYN_REPORT ------------
>
> This is the same behavior as my original patch, just on the same input
> device as everything else (as expected).
>
> The switch starts on state 0 at boot even if the camera was disabled
> while the device was turned off, in which case a new event is reported only
> after toggling the switch twice. However this is expected and is the same
> behavior as in the original patch if e.g. the switch is toggled during
> suspend, so this is not a critique, only a confirmation that your patch
> works as I expected.
>
> > If so, you may submit my patch yourself by adding
> > a From: pseudo header with my identity and swapping the two Signed-off-
> > by: tags. I can also submit it myself as long as you permit me to do so.
>
> I am happy to have you submit the new patch as its main author; it's your
> code now!
>
> I'll only add that, apart from
>
> Co-developed-by: Marco Giunta <marco_giunta@outlook.it>
> Signed-off-by: Marco Giunta <marco_giunta@outlook.it>
>
> I think it's also appropriate to add
>
> Tested-by: Marco Giunta <marco_giunta@outlook.it>
Thanks for that.
If we eventually figure out an appropriate approach to expose
SW_CAMERA_LENS_COVER conditionally, I'd prefer indicating your credits
(i.e., C-d-b) anyway, as it won't be done without your help :-)
Thanks,
Rong
>
> > >
> > > * For reference, on KDE Plasma 6.7.5, using the Legion's touchpad toggle
> > > button shows an OSD, but nothing on a SW_CAMERA_LENS_COVER change or if
> > > sending KEY_CAMERA_ACCESS_ENABLE/DISABLE events. I am not sure whether
> > > this is simply a case of "not implemented yet" or if I misunderstood how
> > > these events are supposed to be used to communicate with userspace.
> > > Please feel free to correct me here.
> >
> > I believe KDE Plasma has not implemented it yet. You may raise an issue
> > on their Bugzilla.
> >
> Noted, thanks.
>
> > > Hi Macro,
> >
> > Whoops, I mistyped your name. Sorry for that. My fingers definitely have
> > their own thoughts.
>
> No worries!!
>
> Thank you again for your very informative review and much simpler patch.
> And please feel free to correct me again if there are further inaccuracies
> in my understanding of the problem.
>
> Best regards,
> Marco
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-22 12:18 ` Rong Zhang
@ 2026-09-22 15:52 ` Mark Pearson
2026-09-22 15:58 ` Rong Zhang
0 siblings, 1 reply; 20+ messages in thread
From: Mark Pearson @ 2026-09-22 15:52 UTC (permalink / raw)
To: Rong Zhang, Marco Giunta
Cc: Derek J . Clark, Hans de Goede, Huang Wei, Ike Panhc,
Ilpo Järvinen, linux-kernel, platform-driver-x86
Hi Rong
On Tue, Sep 22, 2026, at 8:18 AM, Rong Zhang wrote:
> Hi Marco,
>
> On Mon, 2026-09-21 at 21:00 +0200, Marco Giunta wrote:
>> Hi Rong,
>>
>> Thank you so much for your detailed reply and the much improved patch.
>>
>> Let me immediately go on record and say that, apart from a small addition
>> to the SMI driver and one line in ideapad-laptop, I am mostly active in
>> the sound system, and therefore consider myself a platform/x86 newbie.
>> So I really appreciate you sharing your knowledge here.
>>
>> > > * I considered a couple of alternatives to the approach in this patch:
>> > >
>> > > 1. Simply map 0x0c and 0x0d to KEY_CAMERA_ACCESS_ENABLE/DISABLE:
>> > >
>> > > { KE_KEY, 0x0c | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_ENABLE } },
>> > > { KE_KEY, 0x0d | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_DISABLE } },
>> > >
>> > > similar to the touchpad off/on hkey events (66/67) this driver emits
>> > > after the firmware has toggled the touchpad state. However, HUTRR72
>> > > describes these hkey events as asserting a camera access state for
>> > > the host to apply, rather than reporting one the firmware has
>> > > already applied.
>> > >
>> >
>> > Well, it really doesn't matter how the specification says; what matters
>> > is how devices in reality do: whenever the HID Usage Table doesn't define
>> > a usage for hardware/firmware to report the already-applied value but
>> > defines a usage for notifying the host to apply a specific value, a lot
>> > of devices will emerge, using the latter even when the former is
>> > intended.
>> >
>> > For example, many USB Audio Class devices come with volume control
>> > buttons or knobs. Some of them tune the volume themselves as well as
>> > emitting Volume Increment/Decrement HID events.
>> >
>> > In this case, the audio stack, in response to the HID events,
>> > intentionally overrides the volume set by hardware, effectively
>> > synchronizing the hardware and software volume value.
>> >
>> > The pattern has become so common that many devices like that also
>> > implement an internal timeout mechanism. They only set the volume
>> > themselves when reaching the timeout with no UAC volume control request
>> > received.
>> >
>> > IOW, it doesn't matter whether the hardware/firmware has done its job as
>> > long as the subsequent software control is harmless. That's why we can
>> > safely use KEY_TOUCHPAD_ON/OFF even if the firmware has toggled the
>> > touchpad state. They cause the desktop environment to mask/unmask
>> > touchpad input, which is essentially a no-op (thus a safe operation) when
>> > the firmware has already disabled/enabled the touchpad.
>> >
>> > Fundamentally speaking, using KEY_CAMERA_ACCESS_ENABLE/DISABLE is also
>> > OK. It's just...
>> >
>> > > Furthermore, lenovo-wmi-camera moved away from
>> > > these hkey events in favor of SW_CAMERA_LENS_COVER in the quoted
>> > > commit, which is arguably the closest relevant precedent.
>> >
>> > ...relatively new and isn't widely used compared to SW_CAMERA_LENS_COVER.
>> >
>> > Thus, I agreed that using SW_CAMERA_LENS_COVER is a better approach as it
>> > aligns with other drivers' behavior. The patch seems over-engineered
>> > though, see below.
>>
>> This seems pretty reasonable to me, I figured that a single standard was
>> hard to enforce and also not strictly needed in practice (the "harmless
>> no-op" argument above).
>> I was mostly wondering about certain discussions I saw online while doing
>> research for this patch of people arguing that such a no-op should be
>> avoided, see e.g.:
>> https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/merge_requests/181
>
> Thanks for the context. I didn't know the behavior of GNOME as I use KDE
> Plasma, whose behavior is unmask/mask touchpad events on
> KEY_TOUCHPAD_ON/OFF.
>
>>
>> Still, your point that userspace duplicating an action already applied by
>> the hardware is common practice, and fine as long as it is harmless, makes
>> perfect sense to me. So overall I have no objections to your reasoning here.
>>
>> > >
>> > > 2. Ignore these events like the firmware-handled FnLock events:
>> > >
>> > > { KE_IGNORE, 0x0c | IDEAPAD_WMI_KEY },
>> > > { KE_IGNORE, 0x0d | IDEAPAD_WMI_KEY },
>> > >
>> > > This fixes the KEY_UNKNOWN issue, but userspace gets no information.
>> > >
>> > > Overall, in the end I copied the approach of the lenovo-wmi-camera
>> > > driver. Kindly let me know if there are other/better solutions.
>> > >
>> > > * Unlike the touchpad, whose state this driver reads with VPCCMD_R_TOUCHPAD
>> > > at probe, on resume and on each event,
>> > >
>> >
>> > At least for the input device, the synchronization on probe or on resume
>> > does not matter, as no input event is emitted due to !send_events.
>>
>> Good point, I was conflating stuff.
>>
>> > > the camera switch state is only
>> > > available in EC-private fields, and the two tested laptops even use
>> > > different ones. Hence the lenovo-wmi-camera approach: the switch appears
>> > > on the first event, and a change made while suspended is not reported,
>> > > leaving the state wrong until the switch is toggled again.
>> >
>> > So the difference compared to the lenovo-wmi-camera approach is only
>> > about when and how the input device is registered.
>> >
>> > On recent models, the firmware neither emits VPCCMD_R_TOUCHPAD nor
>> > toggles the touchpad. It leaves the job to software by emitting WMI key
>> > event 0x29, which is converted to KEY_TOUCHPAD_TOGGLE via ideapad_keymap.
>> > It'd be tidier to take that approach and reuse the existing input device.
>> > Sparse keymap supports KEY_SW, so it's viable to map the two WMI events
>> > to SW_CAMERA_LENS_COVER with appropriate switch value.
>>
>> This makes a lot of sense to me. I appreciate the simplicity of this
>> approach.
>>
>> Part of the reason why I initially adopted the "separate input device"
>> approach was this comment from
>> drivers/platform/x86/dell/dell-wmi-privacy.c:
>>
>> /*
>> * Unlike keys where only presses matter, userspace may act
>> * on switches in both of their positions. Only register
>> * SW_CAMERA_LENS_COVER if it is actually there.
>> */
>
> Hmm, this makes some sense. Ideally we should prevent exposing
> SW_CAMERA_LENS_COVER unless it's there. I wonder, if the capability is
> declared in LENOVO_UTILITY_DATA? AFAIK, LENOVO_UTILITY_EVENT (8FC0DE0C-
> B4E4-43FD-B0F3-8871711C1294) shares the same ACPI device as
> LENOVO_UTILITY_DATA (CE6C0974-0407-4F50-88BA-4FC3B6559AD8). The former's
> driver is ideapad-laptop, while latter's driver is lenovo-wmi-hotkey-
> utilities.
>
> [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"),
> Description("Lenovo Utility Key Press Event"),
> guid("{8fc0de0c-b4e4-43fd-b0f3-8871711c1294}")]
> class LENOVO_UTILITY_EVENT : WMIEvent {
> [key, read] string InstanceName;
> [read] boolean Active;
> [WmiDataId(1), read, Description("Lenovo Utility Press key
> Event")] uint32 PressTypeDataVal;
> };
>
> [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"),
> Description("LENOVO_UTILITY_DATA class"),
> guid("{ce6c0974-0407-4f50-88ba-4fc3b6559ad8}")]
> class LENOVO_UTILITY_DATA {
> [key, read] string InstanceName;
> [read] boolean Active;
>
> [WmiMethodId(1), Implemented, Description("Utility 3.1 function is
> Support or the function Version")] void GetIfSupportOrVersion([in,
> Description("Control type")] uint32 datatype, [out, Description("0 is
> not support 1,2,3...version")] uint32 Data);
> [WmiMethodId(2), Implemented, Description("Utility 3.1 Set feature
> function ")] void SetFeature([in, Description("Control type")] uint32
> featuretype, [out, Description("Control result 0 is success 1,2,3...is
> failed")] uint32 Data);
> [WmiMethodId(3), Implemented, Description("Utility 3.2 Set feature
> function ")] void SetFeatureEx([in] uint32 IDs, [in] uint32 Value,
> [out, Description("result 0 is success 1,2,3...is failed")] uint32 Ret);
> };
>
>
> I just checked several DSDT tables from different devices (they don't
> have camera switches though). The ACPI device is usually \_SB.WMIU. The
> former's WMI event is usually implemented by \_SB.WMIU._WED, while the
> latter's WMI methods are usually implemented by \_SB.WMIU.WMSK. There may
> be some capabilities defined in the first WMI method, and the WMI event
> may also reveal some details in EC query handling.
>
> Could you dump the ACPI tables of the two devices and attach the acpidump
> files in your reply?
>
> As you've said, "the camera switch state is only available in EC-private
> fields, and the two tested laptops even use different ones." Could you
> elaborate it as well? Such information may be helpful when cross-
> referencing ACPI tables.
>
I'm way behind on my emails and trying to get through my inbox a bit...so apologies if I've missed subtleties as I skim read this a bit
Reading the internal spec - there are some WMI calls I can see that return Camera shutter status:
LENOVO_OTHER_METHOD (GUID dc2a8805-3a8c-41ba-a6f7-092e0089cd3b)
Get CV DSP Camera Shutter Status - Input ID 0x00200000 - returns 0 (closed) or 1 (open)
Get Normal Camera Shutter Status - Input ID 0x00201000 - returns 0 (closed) or 1 (open)
I can't find the Utility GUIDs mentioned at all I'm afraid. Is the above useful?
When I get a chance I'll check this out on a Legion platform I have that has this switch and confirm....but it might take a little while.
Mark
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-22 15:52 ` Mark Pearson
@ 2026-09-22 15:58 ` Rong Zhang
2026-09-22 16:42 ` Rong Zhang
0 siblings, 1 reply; 20+ messages in thread
From: Rong Zhang @ 2026-09-22 15:58 UTC (permalink / raw)
To: Mark Pearson, Marco Giunta
Cc: Derek J . Clark, Hans de Goede, Huang Wei, Ike Panhc,
Ilpo Järvinen, linux-kernel, platform-driver-x86
Hi Mark,
On Tue, 2026-09-22 at 11:52 -0400, Mark Pearson wrote:
> Hi Rong
>
> On Tue, Sep 22, 2026, at 8:18 AM, Rong Zhang wrote:
> > Hi Marco,
> >
> > On Mon, 2026-09-21 at 21:00 +0200, Marco Giunta wrote:
> > > Hi Rong,
> > >
> > > Thank you so much for your detailed reply and the much improved patch.
> > >
> > > Let me immediately go on record and say that, apart from a small addition
> > > to the SMI driver and one line in ideapad-laptop, I am mostly active in
> > > the sound system, and therefore consider myself a platform/x86 newbie.
> > > So I really appreciate you sharing your knowledge here.
> > >
> > > > > * I considered a couple of alternatives to the approach in this patch:
> > > > >
> > > > > 1. Simply map 0x0c and 0x0d to KEY_CAMERA_ACCESS_ENABLE/DISABLE:
> > > > >
> > > > > { KE_KEY, 0x0c | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_ENABLE } },
> > > > > { KE_KEY, 0x0d | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_DISABLE } },
> > > > >
> > > > > similar to the touchpad off/on hkey events (66/67) this driver emits
> > > > > after the firmware has toggled the touchpad state. However, HUTRR72
> > > > > describes these hkey events as asserting a camera access state for
> > > > > the host to apply, rather than reporting one the firmware has
> > > > > already applied.
> > > > >
> > > >
> > > > Well, it really doesn't matter how the specification says; what matters
> > > > is how devices in reality do: whenever the HID Usage Table doesn't define
> > > > a usage for hardware/firmware to report the already-applied value but
> > > > defines a usage for notifying the host to apply a specific value, a lot
> > > > of devices will emerge, using the latter even when the former is
> > > > intended.
> > > >
> > > > For example, many USB Audio Class devices come with volume control
> > > > buttons or knobs. Some of them tune the volume themselves as well as
> > > > emitting Volume Increment/Decrement HID events.
> > > >
> > > > In this case, the audio stack, in response to the HID events,
> > > > intentionally overrides the volume set by hardware, effectively
> > > > synchronizing the hardware and software volume value.
> > > >
> > > > The pattern has become so common that many devices like that also
> > > > implement an internal timeout mechanism. They only set the volume
> > > > themselves when reaching the timeout with no UAC volume control request
> > > > received.
> > > >
> > > > IOW, it doesn't matter whether the hardware/firmware has done its job as
> > > > long as the subsequent software control is harmless. That's why we can
> > > > safely use KEY_TOUCHPAD_ON/OFF even if the firmware has toggled the
> > > > touchpad state. They cause the desktop environment to mask/unmask
> > > > touchpad input, which is essentially a no-op (thus a safe operation) when
> > > > the firmware has already disabled/enabled the touchpad.
> > > >
> > > > Fundamentally speaking, using KEY_CAMERA_ACCESS_ENABLE/DISABLE is also
> > > > OK. It's just...
> > > >
> > > > > Furthermore, lenovo-wmi-camera moved away from
> > > > > these hkey events in favor of SW_CAMERA_LENS_COVER in the quoted
> > > > > commit, which is arguably the closest relevant precedent.
> > > >
> > > > ...relatively new and isn't widely used compared to SW_CAMERA_LENS_COVER.
> > > >
> > > > Thus, I agreed that using SW_CAMERA_LENS_COVER is a better approach as it
> > > > aligns with other drivers' behavior. The patch seems over-engineered
> > > > though, see below.
> > >
> > > This seems pretty reasonable to me, I figured that a single standard was
> > > hard to enforce and also not strictly needed in practice (the "harmless
> > > no-op" argument above).
> > > I was mostly wondering about certain discussions I saw online while doing
> > > research for this patch of people arguing that such a no-op should be
> > > avoided, see e.g.:
> > > https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/merge_requests/181
> >
> > Thanks for the context. I didn't know the behavior of GNOME as I use KDE
> > Plasma, whose behavior is unmask/mask touchpad events on
> > KEY_TOUCHPAD_ON/OFF.
> >
> > >
> > > Still, your point that userspace duplicating an action already applied by
> > > the hardware is common practice, and fine as long as it is harmless, makes
> > > perfect sense to me. So overall I have no objections to your reasoning here.
> > >
> > > > >
> > > > > 2. Ignore these events like the firmware-handled FnLock events:
> > > > >
> > > > > { KE_IGNORE, 0x0c | IDEAPAD_WMI_KEY },
> > > > > { KE_IGNORE, 0x0d | IDEAPAD_WMI_KEY },
> > > > >
> > > > > This fixes the KEY_UNKNOWN issue, but userspace gets no information.
> > > > >
> > > > > Overall, in the end I copied the approach of the lenovo-wmi-camera
> > > > > driver. Kindly let me know if there are other/better solutions.
> > > > >
> > > > > * Unlike the touchpad, whose state this driver reads with VPCCMD_R_TOUCHPAD
> > > > > at probe, on resume and on each event,
> > > > >
> > > >
> > > > At least for the input device, the synchronization on probe or on resume
> > > > does not matter, as no input event is emitted due to !send_events.
> > >
> > > Good point, I was conflating stuff.
> > >
> > > > > the camera switch state is only
> > > > > available in EC-private fields, and the two tested laptops even use
> > > > > different ones. Hence the lenovo-wmi-camera approach: the switch appears
> > > > > on the first event, and a change made while suspended is not reported,
> > > > > leaving the state wrong until the switch is toggled again.
> > > >
> > > > So the difference compared to the lenovo-wmi-camera approach is only
> > > > about when and how the input device is registered.
> > > >
> > > > On recent models, the firmware neither emits VPCCMD_R_TOUCHPAD nor
> > > > toggles the touchpad. It leaves the job to software by emitting WMI key
> > > > event 0x29, which is converted to KEY_TOUCHPAD_TOGGLE via ideapad_keymap.
> > > > It'd be tidier to take that approach and reuse the existing input device.
> > > > Sparse keymap supports KEY_SW, so it's viable to map the two WMI events
> > > > to SW_CAMERA_LENS_COVER with appropriate switch value.
> > >
> > > This makes a lot of sense to me. I appreciate the simplicity of this
> > > approach.
> > >
> > > Part of the reason why I initially adopted the "separate input device"
> > > approach was this comment from
> > > drivers/platform/x86/dell/dell-wmi-privacy.c:
> > >
> > > /*
> > > * Unlike keys where only presses matter, userspace may act
> > > * on switches in both of their positions. Only register
> > > * SW_CAMERA_LENS_COVER if it is actually there.
> > > */
> >
> > Hmm, this makes some sense. Ideally we should prevent exposing
> > SW_CAMERA_LENS_COVER unless it's there. I wonder, if the capability is
> > declared in LENOVO_UTILITY_DATA? AFAIK, LENOVO_UTILITY_EVENT (8FC0DE0C-
> > B4E4-43FD-B0F3-8871711C1294) shares the same ACPI device as
> > LENOVO_UTILITY_DATA (CE6C0974-0407-4F50-88BA-4FC3B6559AD8). The former's
> > driver is ideapad-laptop, while latter's driver is lenovo-wmi-hotkey-
> > utilities.
> >
> > [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"),
> > Description("Lenovo Utility Key Press Event"),
> > guid("{8fc0de0c-b4e4-43fd-b0f3-8871711c1294}")]
> > class LENOVO_UTILITY_EVENT : WMIEvent {
> > [key, read] string InstanceName;
> > [read] boolean Active;
> > [WmiDataId(1), read, Description("Lenovo Utility Press key
> > Event")] uint32 PressTypeDataVal;
> > };
> >
> > [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"),
> > Description("LENOVO_UTILITY_DATA class"),
> > guid("{ce6c0974-0407-4f50-88ba-4fc3b6559ad8}")]
> > class LENOVO_UTILITY_DATA {
> > [key, read] string InstanceName;
> > [read] boolean Active;
> >
> > [WmiMethodId(1), Implemented, Description("Utility 3.1 function is
> > Support or the function Version")] void GetIfSupportOrVersion([in,
> > Description("Control type")] uint32 datatype, [out, Description("0 is
> > not support 1,2,3...version")] uint32 Data);
> > [WmiMethodId(2), Implemented, Description("Utility 3.1 Set feature
> > function ")] void SetFeature([in, Description("Control type")] uint32
> > featuretype, [out, Description("Control result 0 is success 1,2,3...is
> > failed")] uint32 Data);
> > [WmiMethodId(3), Implemented, Description("Utility 3.2 Set feature
> > function ")] void SetFeatureEx([in] uint32 IDs, [in] uint32 Value,
> > [out, Description("result 0 is success 1,2,3...is failed")] uint32 Ret);
> > };
> >
> >
> > I just checked several DSDT tables from different devices (they don't
> > have camera switches though). The ACPI device is usually \_SB.WMIU. The
> > former's WMI event is usually implemented by \_SB.WMIU._WED, while the
> > latter's WMI methods are usually implemented by \_SB.WMIU.WMSK. There may
> > be some capabilities defined in the first WMI method, and the WMI event
> > may also reveal some details in EC query handling.
> >
> > Could you dump the ACPI tables of the two devices and attach the acpidump
> > files in your reply?
> >
> > As you've said, "the camera switch state is only available in EC-private
> > fields, and the two tested laptops even use different ones." Could you
> > elaborate it as well? Such information may be helpful when cross-
> > referencing ACPI tables.
> >
>
> I'm way behind on my emails and trying to get through my inbox a bit...so apologies if I've missed subtleties as I skim read this a bit
>
> Reading the internal spec - there are some WMI calls I can see that return Camera shutter status:
> LENOVO_OTHER_METHOD (GUID dc2a8805-3a8c-41ba-a6f7-092e0089cd3b)
> Get CV DSP Camera Shutter Status - Input ID 0x00200000 - returns 0 (closed) or 1 (open)
> Get Normal Camera Shutter Status - Input ID 0x00201000 - returns 0 (closed) or 1 (open)
>
> I can't find the Utility GUIDs mentioned at all I'm afraid. Is the above useful?
Thanks for the information. It's useful! I will cross-check with the DSL
code of the correlated WMI method.
>
> When I get a chance I'll check this out on a Legion platform I have that has this switch and confirm....but it might take a little while.
Take your time and don't worry :-)
Thanks,
Rong
>
> Mark
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-22 15:58 ` Rong Zhang
@ 2026-09-22 16:42 ` Rong Zhang
2026-09-22 17:32 ` Mark Pearson
0 siblings, 1 reply; 20+ messages in thread
From: Rong Zhang @ 2026-09-22 16:42 UTC (permalink / raw)
To: Mark Pearson, Marco Giunta
Cc: Derek J . Clark, Hans de Goede, Huang Wei, Ike Panhc,
Ilpo Järvinen, linux-kernel, platform-driver-x86
Hi Mark,
On Tue, 2026-09-22 at 23:58 +0800, Rong Zhang wrote:
> Hi Mark,
>
> On Tue, 2026-09-22 at 11:52 -0400, Mark Pearson wrote:
> > Hi Rong
> >
> > On Tue, Sep 22, 2026, at 8:18 AM, Rong Zhang wrote:
> > > Hi Marco,
> > >
> > > On Mon, 2026-09-21 at 21:00 +0200, Marco Giunta wrote:
> > > > Hi Rong,
> > > >
> > > > Thank you so much for your detailed reply and the much improved patch.
> > > >
> > > > Let me immediately go on record and say that, apart from a small addition
> > > > to the SMI driver and one line in ideapad-laptop, I am mostly active in
> > > > the sound system, and therefore consider myself a platform/x86 newbie.
> > > > So I really appreciate you sharing your knowledge here.
> > > >
> > > > > > * I considered a couple of alternatives to the approach in this patch:
> > > > > >
> > > > > > 1. Simply map 0x0c and 0x0d to KEY_CAMERA_ACCESS_ENABLE/DISABLE:
> > > > > >
> > > > > > { KE_KEY, 0x0c | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_ENABLE } },
> > > > > > { KE_KEY, 0x0d | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_DISABLE } },
> > > > > >
> > > > > > similar to the touchpad off/on hkey events (66/67) this driver emits
> > > > > > after the firmware has toggled the touchpad state. However, HUTRR72
> > > > > > describes these hkey events as asserting a camera access state for
> > > > > > the host to apply, rather than reporting one the firmware has
> > > > > > already applied.
> > > > > >
> > > > >
> > > > > Well, it really doesn't matter how the specification says; what matters
> > > > > is how devices in reality do: whenever the HID Usage Table doesn't define
> > > > > a usage for hardware/firmware to report the already-applied value but
> > > > > defines a usage for notifying the host to apply a specific value, a lot
> > > > > of devices will emerge, using the latter even when the former is
> > > > > intended.
> > > > >
> > > > > For example, many USB Audio Class devices come with volume control
> > > > > buttons or knobs. Some of them tune the volume themselves as well as
> > > > > emitting Volume Increment/Decrement HID events.
> > > > >
> > > > > In this case, the audio stack, in response to the HID events,
> > > > > intentionally overrides the volume set by hardware, effectively
> > > > > synchronizing the hardware and software volume value.
> > > > >
> > > > > The pattern has become so common that many devices like that also
> > > > > implement an internal timeout mechanism. They only set the volume
> > > > > themselves when reaching the timeout with no UAC volume control request
> > > > > received.
> > > > >
> > > > > IOW, it doesn't matter whether the hardware/firmware has done its job as
> > > > > long as the subsequent software control is harmless. That's why we can
> > > > > safely use KEY_TOUCHPAD_ON/OFF even if the firmware has toggled the
> > > > > touchpad state. They cause the desktop environment to mask/unmask
> > > > > touchpad input, which is essentially a no-op (thus a safe operation) when
> > > > > the firmware has already disabled/enabled the touchpad.
> > > > >
> > > > > Fundamentally speaking, using KEY_CAMERA_ACCESS_ENABLE/DISABLE is also
> > > > > OK. It's just...
> > > > >
> > > > > > Furthermore, lenovo-wmi-camera moved away from
> > > > > > these hkey events in favor of SW_CAMERA_LENS_COVER in the quoted
> > > > > > commit, which is arguably the closest relevant precedent.
> > > > >
> > > > > ...relatively new and isn't widely used compared to SW_CAMERA_LENS_COVER.
> > > > >
> > > > > Thus, I agreed that using SW_CAMERA_LENS_COVER is a better approach as it
> > > > > aligns with other drivers' behavior. The patch seems over-engineered
> > > > > though, see below.
> > > >
> > > > This seems pretty reasonable to me, I figured that a single standard was
> > > > hard to enforce and also not strictly needed in practice (the "harmless
> > > > no-op" argument above).
> > > > I was mostly wondering about certain discussions I saw online while doing
> > > > research for this patch of people arguing that such a no-op should be
> > > > avoided, see e.g.:
> > > > https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/merge_requests/181
> > >
> > > Thanks for the context. I didn't know the behavior of GNOME as I use KDE
> > > Plasma, whose behavior is unmask/mask touchpad events on
> > > KEY_TOUCHPAD_ON/OFF.
> > >
> > > >
> > > > Still, your point that userspace duplicating an action already applied by
> > > > the hardware is common practice, and fine as long as it is harmless, makes
> > > > perfect sense to me. So overall I have no objections to your reasoning here.
> > > >
> > > > > >
> > > > > > 2. Ignore these events like the firmware-handled FnLock events:
> > > > > >
> > > > > > { KE_IGNORE, 0x0c | IDEAPAD_WMI_KEY },
> > > > > > { KE_IGNORE, 0x0d | IDEAPAD_WMI_KEY },
> > > > > >
> > > > > > This fixes the KEY_UNKNOWN issue, but userspace gets no information.
> > > > > >
> > > > > > Overall, in the end I copied the approach of the lenovo-wmi-camera
> > > > > > driver. Kindly let me know if there are other/better solutions.
> > > > > >
> > > > > > * Unlike the touchpad, whose state this driver reads with VPCCMD_R_TOUCHPAD
> > > > > > at probe, on resume and on each event,
> > > > > >
> > > > >
> > > > > At least for the input device, the synchronization on probe or on resume
> > > > > does not matter, as no input event is emitted due to !send_events.
> > > >
> > > > Good point, I was conflating stuff.
> > > >
> > > > > > the camera switch state is only
> > > > > > available in EC-private fields, and the two tested laptops even use
> > > > > > different ones. Hence the lenovo-wmi-camera approach: the switch appears
> > > > > > on the first event, and a change made while suspended is not reported,
> > > > > > leaving the state wrong until the switch is toggled again.
> > > > >
> > > > > So the difference compared to the lenovo-wmi-camera approach is only
> > > > > about when and how the input device is registered.
> > > > >
> > > > > On recent models, the firmware neither emits VPCCMD_R_TOUCHPAD nor
> > > > > toggles the touchpad. It leaves the job to software by emitting WMI key
> > > > > event 0x29, which is converted to KEY_TOUCHPAD_TOGGLE via ideapad_keymap.
> > > > > It'd be tidier to take that approach and reuse the existing input device.
> > > > > Sparse keymap supports KEY_SW, so it's viable to map the two WMI events
> > > > > to SW_CAMERA_LENS_COVER with appropriate switch value.
> > > >
> > > > This makes a lot of sense to me. I appreciate the simplicity of this
> > > > approach.
> > > >
> > > > Part of the reason why I initially adopted the "separate input device"
> > > > approach was this comment from
> > > > drivers/platform/x86/dell/dell-wmi-privacy.c:
> > > >
> > > > /*
> > > > * Unlike keys where only presses matter, userspace may act
> > > > * on switches in both of their positions. Only register
> > > > * SW_CAMERA_LENS_COVER if it is actually there.
> > > > */
> > >
> > > Hmm, this makes some sense. Ideally we should prevent exposing
> > > SW_CAMERA_LENS_COVER unless it's there. I wonder, if the capability is
> > > declared in LENOVO_UTILITY_DATA? AFAIK, LENOVO_UTILITY_EVENT (8FC0DE0C-
> > > B4E4-43FD-B0F3-8871711C1294) shares the same ACPI device as
> > > LENOVO_UTILITY_DATA (CE6C0974-0407-4F50-88BA-4FC3B6559AD8). The former's
> > > driver is ideapad-laptop, while latter's driver is lenovo-wmi-hotkey-
> > > utilities.
> > >
> > > [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"),
> > > Description("Lenovo Utility Key Press Event"),
> > > guid("{8fc0de0c-b4e4-43fd-b0f3-8871711c1294}")]
> > > class LENOVO_UTILITY_EVENT : WMIEvent {
> > > [key, read] string InstanceName;
> > > [read] boolean Active;
> > > [WmiDataId(1), read, Description("Lenovo Utility Press key
> > > Event")] uint32 PressTypeDataVal;
> > > };
> > >
> > > [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"),
> > > Description("LENOVO_UTILITY_DATA class"),
> > > guid("{ce6c0974-0407-4f50-88ba-4fc3b6559ad8}")]
> > > class LENOVO_UTILITY_DATA {
> > > [key, read] string InstanceName;
> > > [read] boolean Active;
> > >
> > > [WmiMethodId(1), Implemented, Description("Utility 3.1 function is
> > > Support or the function Version")] void GetIfSupportOrVersion([in,
> > > Description("Control type")] uint32 datatype, [out, Description("0 is
> > > not support 1,2,3...version")] uint32 Data);
> > > [WmiMethodId(2), Implemented, Description("Utility 3.1 Set feature
> > > function ")] void SetFeature([in, Description("Control type")] uint32
> > > featuretype, [out, Description("Control result 0 is success 1,2,3...is
> > > failed")] uint32 Data);
> > > [WmiMethodId(3), Implemented, Description("Utility 3.2 Set feature
> > > function ")] void SetFeatureEx([in] uint32 IDs, [in] uint32 Value,
> > > [out, Description("result 0 is success 1,2,3...is failed")] uint32 Ret);
> > > };
> > >
> > >
> > > I just checked several DSDT tables from different devices (they don't
> > > have camera switches though). The ACPI device is usually \_SB.WMIU. The
> > > former's WMI event is usually implemented by \_SB.WMIU._WED, while the
> > > latter's WMI methods are usually implemented by \_SB.WMIU.WMSK. There may
> > > be some capabilities defined in the first WMI method, and the WMI event
> > > may also reveal some details in EC query handling.
> > >
> > > Could you dump the ACPI tables of the two devices and attach the acpidump
> > > files in your reply?
> > >
> > > As you've said, "the camera switch state is only available in EC-private
> > > fields, and the two tested laptops even use different ones." Could you
> > > elaborate it as well? Such information may be helpful when cross-
> > > referencing ACPI tables.
> > >
> >
> > I'm way behind on my emails and trying to get through my inbox a bit...so apologies if I've missed subtleties as I skim read this a bit
> >
> > Reading the internal spec - there are some WMI calls I can see that return C status:
> > LENOVO_OTHER_METHOD (GUID dc2a8805-3a8c-41ba-a6f7-092e0089cd3b)
> > Get CV DSP Camera Shutter Status - Input ID 0x00200000 - returns 0 (closed) or 1 (open)
> > Get Normal Camera Shutter Status - Input ID 0x00201000 - returns 0 (closed) or 1 (open)
> >
> > I can't find the Utility GUIDs mentioned at all I'm afraid. Is the above useful?
>
> Thanks for the information. It's useful! I will cross-check with the DSL
> code of the correlated WMI method.
I just checked a DSDT dump from 83JR [1], and it does implement
0x00200000!
In detail, LENOVO_CAPABILITY_DATA_00 exposes 0x00200000 as
valid+read+write conditionally, while LENOVO_OTHER_METHOD can be used to
get/set the camera shutter.
Still, I have three questions. Could you kindly check the internal
specification and shed some light?
What's the difference between "CV DSP Camera Shutter" and the "Normal"
one? I guess the former is a firmware switch while the latter is a
hardware switch, am I correct?
The device does not implement LENOVO_CAPABILITY_DATA_01 at all. Is it
possible to determine its existence programmatically using
LENOVO_CAPABILITY_DATA_00? Missing the former is fatal in the current
lenovo-wmi-capdata and lenovo-wmi-other implementations, resulting in
both driver non-functional. We need a programmatic way to tolerate its
non-existence so that we can bind the two drivers via the component
framework in any case.
There is an WMI query in the DSDT dump that looks like
LENOVO_CAPABILITY_DATA_03. What is it?
[1]: I never have such a device. I got the dump when fixing
https://bugzilla.kernel.org/show_bug.cgi?id=221065
Appendix (DSDT snippet):
Device (GZFD)
{
Name (_HID, "PNP0C14" /* Windows Management Instrumentation Device */) // _HID: Hardware ID
Name (_UID, "GMZN") // _UID: Unique ID
[..]
/* This is LENOVO_OTHER_METHOD. */
Method (WMAE, 3, NotSerialized)
{
Local0 = Arg1
CreateDWordField (Arg2, Zero, SIDS)
CreateDWordField (Arg2, 0x04, SVAL)
[..]
If ((Arg1 == 0x11))
{
[..]
/* Get 0x00200000. */
If ((SIDS == 0x00200000))
{
Local0 = ^^PCI0.LPC0.EC0.DCSS /* \_SB_.PCI0.LPC0.EC0_.DCSS */
Return (Local0)
}
[..]
}
If ((Arg1 == 0x12))
{
[..]
/* Set 0x00200000. */
If ((SIDS == 0x00200000))
{
Local0 = ToInteger (SVAL)
^^PCI0.LPC0.EC0.DCSS = Local0
Return (Zero)
}
}
[..]
}
/* The device has no LENOVO_CAPABILITY_DATA_01. */
/* Is this something like LENOVO_CAPABILITY_DATA_03? */
Method (WQC3, 1, NotSerialized)
{
If ((Arg0 == Zero))
{
Return (Buffer (0x10)
{
/* 0000 */ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // ........
/* 0008 */ 0x10, 0x27, 0x00, 0x00, 0xDC, 0x05, 0x00, 0x00 // .'......
})
}
}
[..]
/* This is LENOVO_CAPABILITY_DATA_00. */
Name (CPD0, Buffer (0x0C)
{
0x00 // .
})
CreateDWordField (CPD0, Zero, C0ID)
CreateDWordField (CPD0, 0x04, C0CP)
CreateDWordField (CPD0, 0x08, C0DV)
Method (WQC0, 1, NotSerialized)
{
If ((Arg0 == Zero))
{
Return (Buffer (0x0C)
{
/* 0000 */ 0x00, 0x00, 0x05, 0x04, 0x07, 0x00, 0x00, 0x00, // ........
/* 0008 */ 0x00, 0x00, 0x00, 0x00 // ....
})
}
If ((Arg0 == One))
{
/* 0x00200000 is exposed conditionally. */
C0ID = 0x00200000
C0DV = Zero
If ((^^PCI0.LPC0.EC0.CVSP == One))
{
C0CP = 0x07
}
Else
{
C0CP = Zero
}
}
Return (CPD0) /* \_SB_.GZFD.CPD0 */
}
[..]
}
Thanks,
Rong
>
> >
> > When I get a chance I'll check this out on a Legion platform I have that has this switch and confirm....but it might take a little while.
>
> Take your time and don't worry :-)
>
> Thanks,
> Rong
>
> >
> > Mark
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-22 16:42 ` Rong Zhang
@ 2026-09-22 17:32 ` Mark Pearson
2026-09-22 18:47 ` Rong Zhang
0 siblings, 1 reply; 20+ messages in thread
From: Mark Pearson @ 2026-09-22 17:32 UTC (permalink / raw)
To: Rong Zhang, Marco Giunta
Cc: Derek J . Clark, Hans de Goede, Huang Wei, Ike Panhc,
Ilpo Järvinen, linux-kernel, platform-driver-x86
Hi Rong
On Tue, Sep 22, 2026, at 12:42 PM, Rong Zhang wrote:
> Hi Mark,
>
> On Tue, 2026-09-22 at 23:58 +0800, Rong Zhang wrote:
>> Hi Mark,
>>
>> On Tue, 2026-09-22 at 11:52 -0400, Mark Pearson wrote:
>> > Hi Rong
>> >
>> > On Tue, Sep 22, 2026, at 8:18 AM, Rong Zhang wrote:
>> > > Hi Marco,
>> > >
>> > > On Mon, 2026-09-21 at 21:00 +0200, Marco Giunta wrote:
>> > > > Hi Rong,
>> > > >
>> > > > Thank you so much for your detailed reply and the much improved patch.
>> > > >
>> > > > Let me immediately go on record and say that, apart from a small addition
>> > > > to the SMI driver and one line in ideapad-laptop, I am mostly active in
>> > > > the sound system, and therefore consider myself a platform/x86 newbie.
>> > > > So I really appreciate you sharing your knowledge here.
>> > > >
>> > > > > > * I considered a couple of alternatives to the approach in this patch:
>> > > > > >
>> > > > > > 1. Simply map 0x0c and 0x0d to KEY_CAMERA_ACCESS_ENABLE/DISABLE:
>> > > > > >
>> > > > > > { KE_KEY, 0x0c | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_ENABLE } },
>> > > > > > { KE_KEY, 0x0d | IDEAPAD_WMI_KEY, { KEY_CAMERA_ACCESS_DISABLE } },
>> > > > > >
>> > > > > > similar to the touchpad off/on hkey events (66/67) this driver emits
>> > > > > > after the firmware has toggled the touchpad state. However, HUTRR72
>> > > > > > describes these hkey events as asserting a camera access state for
>> > > > > > the host to apply, rather than reporting one the firmware has
>> > > > > > already applied.
>> > > > > >
>> > > > >
>> > > > > Well, it really doesn't matter how the specification says; what matters
>> > > > > is how devices in reality do: whenever the HID Usage Table doesn't define
>> > > > > a usage for hardware/firmware to report the already-applied value but
>> > > > > defines a usage for notifying the host to apply a specific value, a lot
>> > > > > of devices will emerge, using the latter even when the former is
>> > > > > intended.
>> > > > >
>> > > > > For example, many USB Audio Class devices come with volume control
>> > > > > buttons or knobs. Some of them tune the volume themselves as well as
>> > > > > emitting Volume Increment/Decrement HID events.
>> > > > >
>> > > > > In this case, the audio stack, in response to the HID events,
>> > > > > intentionally overrides the volume set by hardware, effectively
>> > > > > synchronizing the hardware and software volume value.
>> > > > >
>> > > > > The pattern has become so common that many devices like that also
>> > > > > implement an internal timeout mechanism. They only set the volume
>> > > > > themselves when reaching the timeout with no UAC volume control request
>> > > > > received.
>> > > > >
>> > > > > IOW, it doesn't matter whether the hardware/firmware has done its job as
>> > > > > long as the subsequent software control is harmless. That's why we can
>> > > > > safely use KEY_TOUCHPAD_ON/OFF even if the firmware has toggled the
>> > > > > touchpad state. They cause the desktop environment to mask/unmask
>> > > > > touchpad input, which is essentially a no-op (thus a safe operation) when
>> > > > > the firmware has already disabled/enabled the touchpad.
>> > > > >
>> > > > > Fundamentally speaking, using KEY_CAMERA_ACCESS_ENABLE/DISABLE is also
>> > > > > OK. It's just...
>> > > > >
>> > > > > > Furthermore, lenovo-wmi-camera moved away from
>> > > > > > these hkey events in favor of SW_CAMERA_LENS_COVER in the quoted
>> > > > > > commit, which is arguably the closest relevant precedent.
>> > > > >
>> > > > > ...relatively new and isn't widely used compared to SW_CAMERA_LENS_COVER.
>> > > > >
>> > > > > Thus, I agreed that using SW_CAMERA_LENS_COVER is a better approach as it
>> > > > > aligns with other drivers' behavior. The patch seems over-engineered
>> > > > > though, see below.
>> > > >
>> > > > This seems pretty reasonable to me, I figured that a single standard was
>> > > > hard to enforce and also not strictly needed in practice (the "harmless
>> > > > no-op" argument above).
>> > > > I was mostly wondering about certain discussions I saw online while doing
>> > > > research for this patch of people arguing that such a no-op should be
>> > > > avoided, see e.g.:
>> > > > https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/merge_requests/181
>> > >
>> > > Thanks for the context. I didn't know the behavior of GNOME as I use KDE
>> > > Plasma, whose behavior is unmask/mask touchpad events on
>> > > KEY_TOUCHPAD_ON/OFF.
>> > >
>> > > >
>> > > > Still, your point that userspace duplicating an action already applied by
>> > > > the hardware is common practice, and fine as long as it is harmless, makes
>> > > > perfect sense to me. So overall I have no objections to your reasoning here.
>> > > >
>> > > > > >
>> > > > > > 2. Ignore these events like the firmware-handled FnLock events:
>> > > > > >
>> > > > > > { KE_IGNORE, 0x0c | IDEAPAD_WMI_KEY },
>> > > > > > { KE_IGNORE, 0x0d | IDEAPAD_WMI_KEY },
>> > > > > >
>> > > > > > This fixes the KEY_UNKNOWN issue, but userspace gets no information.
>> > > > > >
>> > > > > > Overall, in the end I copied the approach of the lenovo-wmi-camera
>> > > > > > driver. Kindly let me know if there are other/better solutions.
>> > > > > >
>> > > > > > * Unlike the touchpad, whose state this driver reads with VPCCMD_R_TOUCHPAD
>> > > > > > at probe, on resume and on each event,
>> > > > > >
>> > > > >
>> > > > > At least for the input device, the synchronization on probe or on resume
>> > > > > does not matter, as no input event is emitted due to !send_events.
>> > > >
>> > > > Good point, I was conflating stuff.
>> > > >
>> > > > > > the camera switch state is only
>> > > > > > available in EC-private fields, and the two tested laptops even use
>> > > > > > different ones. Hence the lenovo-wmi-camera approach: the switch appears
>> > > > > > on the first event, and a change made while suspended is not reported,
>> > > > > > leaving the state wrong until the switch is toggled again.
>> > > > >
>> > > > > So the difference compared to the lenovo-wmi-camera approach is only
>> > > > > about when and how the input device is registered.
>> > > > >
>> > > > > On recent models, the firmware neither emits VPCCMD_R_TOUCHPAD nor
>> > > > > toggles the touchpad. It leaves the job to software by emitting WMI key
>> > > > > event 0x29, which is converted to KEY_TOUCHPAD_TOGGLE via ideapad_keymap.
>> > > > > It'd be tidier to take that approach and reuse the existing input device.
>> > > > > Sparse keymap supports KEY_SW, so it's viable to map the two WMI events
>> > > > > to SW_CAMERA_LENS_COVER with appropriate switch value.
>> > > >
>> > > > This makes a lot of sense to me. I appreciate the simplicity of this
>> > > > approach.
>> > > >
>> > > > Part of the reason why I initially adopted the "separate input device"
>> > > > approach was this comment from
>> > > > drivers/platform/x86/dell/dell-wmi-privacy.c:
>> > > >
>> > > > /*
>> > > > * Unlike keys where only presses matter, userspace may act
>> > > > * on switches in both of their positions. Only register
>> > > > * SW_CAMERA_LENS_COVER if it is actually there.
>> > > > */
>> > >
>> > > Hmm, this makes some sense. Ideally we should prevent exposing
>> > > SW_CAMERA_LENS_COVER unless it's there. I wonder, if the capability is
>> > > declared in LENOVO_UTILITY_DATA? AFAIK, LENOVO_UTILITY_EVENT (8FC0DE0C-
>> > > B4E4-43FD-B0F3-8871711C1294) shares the same ACPI device as
>> > > LENOVO_UTILITY_DATA (CE6C0974-0407-4F50-88BA-4FC3B6559AD8). The former's
>> > > driver is ideapad-laptop, while latter's driver is lenovo-wmi-hotkey-
>> > > utilities.
>> > >
>> > > [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"),
>> > > Description("Lenovo Utility Key Press Event"),
>> > > guid("{8fc0de0c-b4e4-43fd-b0f3-8871711c1294}")]
>> > > class LENOVO_UTILITY_EVENT : WMIEvent {
>> > > [key, read] string InstanceName;
>> > > [read] boolean Active;
>> > > [WmiDataId(1), read, Description("Lenovo Utility Press key
>> > > Event")] uint32 PressTypeDataVal;
>> > > };
>> > >
>> > > [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"),
>> > > Description("LENOVO_UTILITY_DATA class"),
>> > > guid("{ce6c0974-0407-4f50-88ba-4fc3b6559ad8}")]
>> > > class LENOVO_UTILITY_DATA {
>> > > [key, read] string InstanceName;
>> > > [read] boolean Active;
>> > >
>> > > [WmiMethodId(1), Implemented, Description("Utility 3.1 function is
>> > > Support or the function Version")] void GetIfSupportOrVersion([in,
>> > > Description("Control type")] uint32 datatype, [out, Description("0 is
>> > > not support 1,2,3...version")] uint32 Data);
>> > > [WmiMethodId(2), Implemented, Description("Utility 3.1 Set feature
>> > > function ")] void SetFeature([in, Description("Control type")] uint32
>> > > featuretype, [out, Description("Control result 0 is success 1,2,3...is
>> > > failed")] uint32 Data);
>> > > [WmiMethodId(3), Implemented, Description("Utility 3.2 Set feature
>> > > function ")] void SetFeatureEx([in] uint32 IDs, [in] uint32 Value,
>> > > [out, Description("result 0 is success 1,2,3...is failed")] uint32 Ret);
>> > > };
>> > >
>> > >
>> > > I just checked several DSDT tables from different devices (they don't
>> > > have camera switches though). The ACPI device is usually \_SB.WMIU. The
>> > > former's WMI event is usually implemented by \_SB.WMIU._WED, while the
>> > > latter's WMI methods are usually implemented by \_SB.WMIU.WMSK. There may
>> > > be some capabilities defined in the first WMI method, and the WMI event
>> > > may also reveal some details in EC query handling.
>> > >
>> > > Could you dump the ACPI tables of the two devices and attach the acpidump
>> > > files in your reply?
>> > >
>> > > As you've said, "the camera switch state is only available in EC-private
>> > > fields, and the two tested laptops even use different ones." Could you
>> > > elaborate it as well? Such information may be helpful when cross-
>> > > referencing ACPI tables.
>> > >
>> >
>> > I'm way behind on my emails and trying to get through my inbox a bit...so apologies if I've missed subtleties as I skim read this a bit
>> >
>> > Reading the internal spec - there are some WMI calls I can see that return C status:
>> > LENOVO_OTHER_METHOD (GUID dc2a8805-3a8c-41ba-a6f7-092e0089cd3b)
>> > Get CV DSP Camera Shutter Status - Input ID 0x00200000 - returns 0 (closed) or 1 (open)
>> > Get Normal Camera Shutter Status - Input ID 0x00201000 - returns 0 (closed) or 1 (open)
>> >
>> > I can't find the Utility GUIDs mentioned at all I'm afraid. Is the above useful?
>>
>> Thanks for the information. It's useful! I will cross-check with the DSL
>> code of the correlated WMI method.
>
> I just checked a DSDT dump from 83JR [1], and it does implement
> 0x00200000!
>
> In detail, LENOVO_CAPABILITY_DATA_00 exposes 0x00200000 as
> valid+read+write conditionally, while LENOVO_OTHER_METHOD can be used to
> get/set the camera shutter.
>
> Still, I have three questions. Could you kindly check the internal
> specification and shed some light?
>
> What's the difference between "CV DSP Camera Shutter" and the "Normal"
> one? I guess the former is a firmware switch while the latter is a
> hardware switch, am I correct?
>
The Normal ID is new and was just added in the latest version of the spec.
It's possible it is on a not-released platform yet.
As a note - the 'Normal" entry is get only.
Afraid my hooks into the Yoga and Legion team are still quite new and we're figuring out some of the pieces needed at the moment :)
The WMI spec seems to cover a lot of different platforms that I've not had much experience with too.
> The device does not implement LENOVO_CAPABILITY_DATA_01 at all. Is it
> possible to determine its existence programmatically using
> LENOVO_CAPABILITY_DATA_00? Missing the former is fatal in the current
> lenovo-wmi-capdata and lenovo-wmi-other implementations, resulting in
> both driver non-functional. We need a programmatic way to tolerate its
> non-existence so that we can bind the two drivers via the component
> framework in any case.
>
It should be under CAPABILITY DATA_00, at least from what I'm seeing (pasting a table in text....):
CV DSP Camera Shutter
uint32 IDs //0x00200000
uint32 Capability // 7 :by project
bit 2: 0: not support SetFeatureValue(), 1: support SetFeatureValue()
bit 1: 0: not support GetFeatureValue(), 1: support GetFeatureValue()
bit 0: 0: not support CV DSP Camera Shutter, 1: support CV DSP Camera Shutter
uint32 DefaultValue // 0 : by project
Normal Camera Shutter Status
uint32 IDs //0x00201000
uint32 Capability // 3 :by project
bit 2: 0: not support SetFeatureValue(), 1: support SetFeatureValue()
bit 1: 0: not support GetFeatureValue(), 1: support GetFeatureValue()
bit 0: 0: not support Normal Camera Shutter Status, 1: support Normal Camera Shutter Status
uint32 DefaultValue // 1 : by project
> There is an WMI query in the DSDT dump that looks like
> LENOVO_CAPABILITY_DATA_03. What is it?
>
No idea - my spec sheet doesn't have 03 defined.
Scanning through my doc I'm not spotting anything that looks like a match for WQC either
Mark
> [1]: I never have such a device. I got the dump when fixing
> https://bugzilla.kernel.org/show_bug.cgi?id=221065
>
> Appendix (DSDT snippet):
>
> Device (GZFD)
> {
> Name (_HID, "PNP0C14" /* Windows Management
> Instrumentation Device */) // _HID: Hardware ID
> Name (_UID, "GMZN") // _UID: Unique ID
> [..]
> /* This is LENOVO_OTHER_METHOD. */
> Method (WMAE, 3, NotSerialized)
> {
> Local0 = Arg1
> CreateDWordField (Arg2, Zero, SIDS)
> CreateDWordField (Arg2, 0x04, SVAL)
> [..]
> If ((Arg1 == 0x11))
> {
> [..]
> /* Get 0x00200000. */
> If ((SIDS == 0x00200000))
> {
> Local0 = ^^PCI0.LPC0.EC0.DCSS /*
> \_SB_.PCI0.LPC0.EC0_.DCSS */
> Return (Local0)
> }
> [..]
> }
>
> If ((Arg1 == 0x12))
> {
> [..]
> /* Set 0x00200000. */
> If ((SIDS == 0x00200000))
> {
> Local0 = ToInteger (SVAL)
> ^^PCI0.LPC0.EC0.DCSS = Local0
> Return (Zero)
> }
> }
> [..]
> }
> /* The device has no LENOVO_CAPABILITY_DATA_01. */
>
> /* Is this something like LENOVO_CAPABILITY_DATA_03? */
> Method (WQC3, 1, NotSerialized)
> {
> If ((Arg0 == Zero))
> {
> Return (Buffer (0x10)
> {
> /* 0000 */ 0x01, 0x00, 0x00, 0x00,
> 0x01, 0x00, 0x00, 0x00, // ........
> /* 0008 */ 0x10, 0x27, 0x00, 0x00,
> 0xDC, 0x05, 0x00, 0x00 // .'......
> })
> }
> }
> [..]
> /* This is LENOVO_CAPABILITY_DATA_00. */
> Name (CPD0, Buffer (0x0C)
> {
> 0x00
> // .
> })
> CreateDWordField (CPD0, Zero, C0ID)
> CreateDWordField (CPD0, 0x04, C0CP)
> CreateDWordField (CPD0, 0x08, C0DV)
> Method (WQC0, 1, NotSerialized)
> {
> If ((Arg0 == Zero))
> {
> Return (Buffer (0x0C)
> {
> /* 0000 */ 0x00, 0x00, 0x05, 0x04,
> 0x07, 0x00, 0x00, 0x00, // ........
> /* 0008 */ 0x00, 0x00, 0x00, 0x00
> // ....
> })
> }
>
> If ((Arg0 == One))
> {
> /* 0x00200000 is exposed conditionally. */
> C0ID = 0x00200000
> C0DV = Zero
> If ((^^PCI0.LPC0.EC0.CVSP == One))
> {
> C0CP = 0x07
> }
> Else
> {
> C0CP = Zero
> }
> }
>
> Return (CPD0) /* \_SB_.GZFD.CPD0 */
> }
> [..]
> }
>
> Thanks,
> Rong
>
>>
>> >
>> > When I get a chance I'll check this out on a Legion platform I have that has this switch and confirm....but it might take a little while.
>>
>> Take your time and don't worry :-)
>>
>> Thanks,
>> Rong
>>
>> >
>> > Mark
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-22 17:32 ` Mark Pearson
@ 2026-09-22 18:47 ` Rong Zhang
2026-09-23 11:17 ` Marco Giunta
0 siblings, 1 reply; 20+ messages in thread
From: Rong Zhang @ 2026-09-22 18:47 UTC (permalink / raw)
To: Mark Pearson, Marco Giunta
Cc: Derek J . Clark, Hans de Goede, Huang Wei, Ike Panhc,
Ilpo Järvinen, linux-kernel, platform-driver-x86
Hi Mark,
Thanks for your information.
于 2026年9月23日 GMT+08:00 01:32:41,Mark Pearson <mpearson-lenovo@squebb.ca> 写道:
> Hi Rong
>
> On Tue, Sep 22, 2026, at 12:42 PM, Rong Zhang wrote:
> > Hi Mark,
> >
> > On Tue, 2026-09-22 at 23:58 +0800, Rong Zhang wrote:
> >> Hi Mark,
> >>
> >> On Tue, 2026-09-22 at 11:52 -0400, Mark Pearson wrote:
[...]
> >> > I'm way behind on my emails and trying to get through my inbox a bit...so apologies if I've missed subtleties as I skim read this a bit
> >> >
> >> > Reading the internal spec - there are some WMI calls I can see that return C status:
> >> > LENOVO_OTHER_METHOD (GUID dc2a8805-3a8c-41ba-a6f7-092e0089cd3b)
> >> > Get CV DSP Camera Shutter Status - Input ID 0x00200000 - returns 0 (closed) or 1 (open)
> >> > Get Normal Camera Shutter Status - Input ID 0x00201000 - returns 0 (closed) or 1 (open)
> >> >
> >> > I can't find the Utility GUIDs mentioned at all I'm afraid. Is the above useful?
> >>
> >> Thanks for the information. It's useful! I will cross-check with the DSL
> >> code of the correlated WMI method.
> >
> > I just checked a DSDT dump from 83JR [1], and it does implement
> > 0x00200000!
> >
> > In detail, LENOVO_CAPABILITY_DATA_00 exposes 0x00200000 as
> > valid+read+write conditionally, while LENOVO_OTHER_METHOD can be used to
> > get/set the camera shutter.
> >
> > Still, I have three questions. Could you kindly check the internal
> > specification and shed some light?
> >
> > What's the difference between "CV DSP Camera Shutter" and the "Normal"
> > one? I guess the former is a firmware switch while the latter is a
> > hardware switch, am I correct?
> >
> The Normal ID is new and was just added in the latest version of the spec.
> It's possible it is on a not-released platform yet.
> As a note - the 'Normal" entry is get only.
Got it. The latter being get-only really makes it sound like a hardware switch. But yeah, we will see when the relevant platform is released.
>
> Afraid my hooks into the Yoga and Legion team are still quite new and we're figuring out some of the pieces needed at the moment :)
> The WMI spec seems to cover a lot of different platforms that I've not had much experience with too.
>
> > The device does not implement LENOVO_CAPABILITY_DATA_01 at all. Is it
> > possible to determine its existence programmatically using
> > LENOVO_CAPABILITY_DATA_00? Missing the former is fatal in the current
> > lenovo-wmi-capdata and lenovo-wmi-other implementations, resulting in
> > both driver non-functional. We need a programmatic way to tolerate its
> > non-existence so that we can bind the two drivers via the component
> > framework in any case.
> >
> It should be under CAPABILITY DATA_00, at least from what I'm seeing (pasting a table in text....):
>
> CV DSP Camera Shutter
> uint32 IDs //0x00200000
> uint32 Capability // 7 :by project
> bit 2: 0: not support SetFeatureValue(), 1: support SetFeatureValue()
> bit 1: 0: not support GetFeatureValue(), 1: support GetFeatureValue()
> bit 0: 0: not support CV DSP Camera Shutter, 1: support CV DSP Camera Shutter
> uint32 DefaultValue // 0 : by project
>
> Normal Camera Shutter Status
> uint32 IDs //0x00201000
> uint32 Capability // 3 :by project
> bit 2: 0: not support SetFeatureValue(), 1: support SetFeatureValue()
> bit 1: 0: not support GetFeatureValue(), 1: support GetFeatureValue()
> bit 0: 0: not support Normal Camera Shutter Status, 1: support Normal Camera Shutter Status
> uint32 DefaultValue // 1 : by project
While the information is also useful, it seems that I didn't express the question well in the last reply. Sorry for causing the misunderstanding.
I was asking about:
Is it possible to determine the existence of LENOVO_CAPABILITY_DATA_*01* using *00*?
Full story:
The two drivers (wmi-capdata and wmi-other) make a strong assumption that both 00 and 01 must be present. Missing anyone causes the two drivers non-functional.
Some platforms stub 01. The two drivers still work properly on these platforms, as the WMI GUID of 01 is still there.
Some platforms don't support 01 at all, but somehow forgot to stub it. The two drivers become non-functional in this case. It's easy to fix as the WMI GUID of 01 is still there, too. I've sent a series to fix that (it hasn't received any review yet, hint hint ;-P), see https://lore.kernel.org/all/20260914-lwmi-wmi-new-api-v1-0-7a400f2f69f8@rong.moe/
Not until the last reply did I realize that 01 could be completely missing. Unfortunately, it becomes very tricky as the use of wmi_has_guid() is strongly discouraged, so we need to figure out an approach to determine the existence of 01 using other capability data, e.g. 00.
>
> > There is an WMI query in the DSDT dump that looks like
> > LENOVO_CAPABILITY_DATA_03. What is it?
> >
> No idea - my spec sheet doesn't have 03 defined.
> Scanning through my doc I'm not spotting anything that looks like a match for WQC either
Fine, it's not a big deal. I am just curious about how many capability data GUIDs have been defined.
Thanks,
Rong
>
> Mark
>
>
> > [1]: I never have such a device. I got the dump when fixing
> > https://bugzilla.kernel.org/show_bug.cgi?id=221065
> >
> > Appendix (DSDT snippet):
> >
> > Device (GZFD)
> > {
> > Name (_HID, "PNP0C14" /* Windows Management
> > Instrumentation Device */) // _HID: Hardware ID
> > Name (_UID, "GMZN") // _UID: Unique ID
> > [..]
> > /* This is LENOVO_OTHER_METHOD. */
> > Method (WMAE, 3, NotSerialized)
> > {
> > Local0 = Arg1
> > CreateDWordField (Arg2, Zero, SIDS)
> > CreateDWordField (Arg2, 0x04, SVAL)
> > [..]
> > If ((Arg1 == 0x11))
> > {
> > [..]
> > /* Get 0x00200000. */
> > If ((SIDS == 0x00200000))
> > {
> > Local0 = ^^PCI0.LPC0.EC0.DCSS /*
> > \_SB_.PCI0.LPC0.EC0_.DCSS */
> > Return (Local0)
> > }
> > [..]
> > }
> >
> > If ((Arg1 == 0x12))
> > {
> > [..]
> > /* Set 0x00200000. */
> > If ((SIDS == 0x00200000))
> > {
> > Local0 = ToInteger (SVAL)
> > ^^PCI0.LPC0.EC0.DCSS = Local0
> > Return (Zero)
> > }
> > }
> > [..]
> > }
> > /* The device has no LENOVO_CAPABILITY_DATA_01. */
> >
> > /* Is this something like LENOVO_CAPABILITY_DATA_03? */
> > Method (WQC3, 1, NotSerialized)
> > {
> > If ((Arg0 == Zero))
> > {
> > Return (Buffer (0x10)
> > {
> > /* 0000 */ 0x01, 0x00, 0x00, 0x00,
> > 0x01, 0x00, 0x00, 0x00, // ........
> > /* 0008 */ 0x10, 0x27, 0x00, 0x00,
> > 0xDC, 0x05, 0x00, 0x00 // .'......
> > })
> > }
> > }
> > [..]
> > /* This is LENOVO_CAPABILITY_DATA_00. */
> > Name (CPD0, Buffer (0x0C)
> > {
> > 0x00
> > // .
> > })
> > CreateDWordField (CPD0, Zero, C0ID)
> > CreateDWordField (CPD0, 0x04, C0CP)
> > CreateDWordField (CPD0, 0x08, C0DV)
> > Method (WQC0, 1, NotSerialized)
> > {
> > If ((Arg0 == Zero))
> > {
> > Return (Buffer (0x0C)
> > {
> > /* 0000 */ 0x00, 0x00, 0x05, 0x04,
> > 0x07, 0x00, 0x00, 0x00, // ........
> > /* 0008 */ 0x00, 0x00, 0x00, 0x00
> > // ....
> > })
> > }
> >
> > If ((Arg0 == One))
> > {
> > /* 0x00200000 is exposed conditionally. */
> > C0ID = 0x00200000
> > C0DV = Zero
> > If ((^^PCI0.LPC0.EC0.CVSP == One))
> > {
> > C0CP = 0x07
> > }
> > Else
> > {
> > C0CP = Zero
> > }
> > }
> >
> > Return (CPD0) /* \_SB_.GZFD.CPD0 */
> > }
> > [..]
> > }
> >
> > Thanks,
> > Rong
> >
> >>
> >> >
> >> > When I get a chance I'll check this out on a Legion platform I have that has this switch and confirm....but it might take a little while.
> >>
> >> Take your time and don't worry :-)
> >>
> >> Thanks,
> >> Rong
> >>
> >> >
> >> > Mark
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-22 18:47 ` Rong Zhang
@ 2026-09-23 11:17 ` Marco Giunta
2026-09-23 12:39 ` Rong Zhang
0 siblings, 1 reply; 20+ messages in thread
From: Marco Giunta @ 2026-09-23 11:17 UTC (permalink / raw)
To: i
Cc: derekjohn.clark, hansg, huangwei, ikepanhc, ilpo.jarvinen,
linux-kernel, marco_giunta, mpearson-lenovo, platform-driver-x86
Hi Rong & Mark,
thanks a lot for the impressively detailed and informative replies
(as always). Here's an update from me.
1. overall I changed my mind regarding the conditional switch registration.
I no longer really see it as particularly problematic in practice; as Rong
said earlier, in practice it's mostly a theoretical concern given the
typical userspace usage of this switch.
I do agree that if we can find a smart way to filter for switch presence
based on a good criterion, like a suitable wmi method, it would
definitively be an improvement to patch v3. And I also appreciate that
gaining a better understanding of these issues may improve other lenovo
drivers. Finally, in general, I am certainly not trying to stifle the
useful discussion going on here; what I'm trying to say is: as far as the
camera switch patch is concerned, I am perfectly fine with the simple
approach of v3 (at least for now, we can add more complexity later IMO).
2. here https://github.com/marco-giunta/lenovo-acpi-dump you can find
all raw & decompiled ACPI tables and bmof files from both my yoga and my
legion. Feel free to ask for more data.
3. Let me clarify what I meant when I said that the yoga and the lenovo
handle the switch differently (and more generally report some highlights
from the above files).
The yoga's SSDT2 table contains:
Device (WMIU)
{
Name (_HID, "PNP0C14" /* Windows Management Instrumentation Device */) // _HID: Hardware ID
Name (_UID, "LSK20") // _UID: Unique ID
Name (LSKE, 0x02)
Name (_WDG, Buffer (0x3C)
{
/* 0000 */ 0x74, 0x09, 0x6C, 0xCE, 0x07, 0x04, 0x50, 0x4F, // t.l...PO
/* 0008 */ 0x88, 0xBA, 0x4F, 0xC3, 0xB6, 0x55, 0x9A, 0xD8, // ..O..U..
/* 0010 */ 0x53, 0x4B, 0x01, 0x02, 0x21, 0x12, 0x90, 0x05, // SK..!...
/* 0018 */ 0x66, 0xD5, 0xD1, 0x11, 0xB2, 0xF0, 0x00, 0xA0, // f.......
/* 0020 */ 0xC9, 0x06, 0x29, 0x10, 0x44, 0x41, 0x01, 0x00, // ..).DA..
/* 0028 */ 0x0C, 0xDE, 0xC0, 0x8F, 0xE4, 0xB4, 0xFD, 0x43, // .......C
/* 0030 */ 0xB0, 0xF3, 0x88, 0x71, 0x71, 0x1C, 0x12, 0x94, // ...qq...
/* 0038 */ 0xD0, 0x00, 0x01, 0x08 // ....
})
so GUID 8FC0DE0C-B4E4-43FD-B0F3-8871711C1294 on device WMIU, event (0x08)
with notify value 0xD0, no surprises here.
Its _WED method is:
Method (_WED, 1, NotSerialized) // _Wxx: Wake Event, xx=0x00-0xFF
{
If ((Arg0 == 0xD0))
{
Return (PSD1) /* \PSD1 */
}
}
so "notify" = "return PSD1". Then below:
Scope (\_SB.PCI0.LPC0.EC0)
{
...
Method (_Q29, 0, NotSerialized) // _Qxx: EC Query, xx=0x00-0xFF
{
If ((\_SB.PCI0.LPC0.EC0.COSD == 0x02))
{
PSD1 = 0x0C
}
ElseIf ((\_SB.PCI0.LPC0.EC0.COSD == Zero))
{
PSD1 = 0x0D
}
Notify (\_SB.WMIU, 0xD0) // Hardware-Specific
}
}
So the camera on/off 0x0c/0x0d events are sent based on the COSD memory
register in the EC.
This COSD variable is never referred to again anywhere else in the ACPI tables.
On the Legion:
Device (WMIU)
{
Name (_HID, "PNP0C14" /* Windows Management Instrumentation Device */) // _HID: Hardware ID
Name (_UID, "LSK20") // _UID: Unique ID
Name (_WDG, Buffer (0x3C)
{
/* 0000 */ 0x74, 0x09, 0x6C, 0xCE, 0x07, 0x04, 0x50, 0x4F, // t.l...PO
/* 0008 */ 0x88, 0xBA, 0x4F, 0xC3, 0xB6, 0x55, 0x9A, 0xD8, // ..O..U..
/* 0010 */ 0x53, 0x4B, 0x01, 0x02, 0x0C, 0xDE, 0xC0, 0x8F, // SK......
/* 0018 */ 0xE4, 0xB4, 0xFD, 0x43, 0xB0, 0xF3, 0x88, 0x71, // ...C...q
/* 0020 */ 0x71, 0x1C, 0x12, 0x94, 0xD0, 0x00, 0x01, 0x08, // q.......
/* 0028 */ 0x21, 0x12, 0x90, 0x05, 0x66, 0xD5, 0xD1, 0x11, // !...f...
/* 0030 */ 0xB2, 0xF0, 0x00, 0xA0, 0xC9, 0x06, 0x29, 0x10, // ......).
/* 0038 */ 0x44, 0x41, 0x01, 0x00 // DA..
})
Same 8fc0... GUID, same [0xD0, 0x00, 0x01, 0x08] sequence. Then:
Method (_WED, 1, NotSerialized) // _Wxx: Wake Event, xx=0x00-0xFF
{
If ((Arg0 == 0xD0))
{
Return (LSKD) /* External reference */
}
}
But here this LSKD variable is written to with different logic. The relevant
EC query handler is:
Method (_QDF, 0, NotSerialized) // _Qxx: EC Query, xx=0x00-0xFF
{
P80B = 0xDF
Sleep (0x05)
LSKD = Zero
If ((LSKV != Zero))
{
LSKD = LSKV /* \_SB_.PCI0.LPC0.EC0_.LSKV */
If ((LSKD == 0x02)){}
If ((LSKD == 0x03)){}
LSKV = Zero
}
Notify (WMIU, 0xD0) // Hardware-Specific
Notify (GZFD, 0xE8) // Hardware-Specific
}
so there are no assignments of 0x0c/0x0d like on the Yoga: the EC itself
writes the event code into LSKV (a byte at offset 0x1F of the ERAX region,
SystemMemory 0xFEEC2400), and the AML only copies it to LSKD and clears
the mailbox.
The constants therefore appear to live in the EC firmware rather than
in the ACPI tables, and no AML-visible object keeps the switch state,
unlike COSD on the Yoga (which is at least readable in principle, even if
nothing else reads it).
Overall, the firmware appears to work differently internally, and I wasn't
able to find a common interface that a driver could use for this without
resorting to per-model EC details (see below), though I may be missing
something.
4. Unless I'm missing something, the IDs Mark mentioned don't really apply
here.
0x00201000 does not appear anywhere in either machine's tables - consistent
with Mark's point that this ID is new; both of my devices were released in
2025.
0x00200000 appears a few times in SSDT2 (yoga)/SSDT5 (legion) in contexts that
look unrelated (bitmask arithmetic in DYTC on the Yoga, and in the GPS
method on the Legion), plus once in the Yoga's capability data, see below.
LENOVO_OTHER_METHOD is implemented on both machines as \_SB.GZFD.WMAE
(object "AE" in the GZFD _WDG, flags 0x02 = method). Since the MOF
declares GetFeatureValue as WmiMethodId(17), the relevant branch is
Arg1 == 0x11, the same one as in Rong's 83JR snippet. As far as I can
tell neither machine handles the two shutter IDs there: the Yoga has logic
to dispatch based on device/feature bytes (and covers device 0 features
0x02 and 0x08, device 4 feature 0x03, and device 8 feature 0x01); instead
the Legion compares whole IDs and its device-0 list goes from
0x001F0000 straight to 0x00210000. Unhandled IDs return Zero in both
cases, which, if 0 means "closed", would make those IDs report a closed
shutter regardless of the switch position. I would appreciate a second
opinion on this reading, though.
As for the capability data, the Yoga implements LENOVO_CAPABILITY_DATA_00,
defined in the bmof as
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 00"), guid("{362a3afe-3d96-4665-8530-96dad5bb300e}")]
class LENOVO_CAPABILITY_DATA_00 {
[key, read] string InstanceName;
[read] boolean Active;
[WmiDataId(1), read, Description(" IDs.")] uint32 IDs;
[WmiDataId(2), read, Description("Capability.")] uint32 Capability;
[WmiDataId(3), read, Description("Capability Default Value.")] uint32 DefaultValue;
};
This object is implemented in the acpi tables with GUID
362A3AFE-3D96-4665-8530-96DAD5BB300E, object id "B0", as WQB0 -> GCD0,
which returns the three dwords of each element of the CD00 package.
CD00 enumerates the feature IDs in ascending order, and includes
{ 0x001F0000, Zero, Zero },
{ 0x00200000, Zero, Zero },
{ 0x00210000, Zero, Zero },
while other entries in the same table have capability values such as
0x07, 0x09, 0x05, 0x03 and One. If the capability field is the bitmask
from Mark's table, this would mean the CV DSP Camera Shutter is enumerated
but declared as not supported on this model. (and again there is no
0x00201000 entry)
The Legion's capability data (same 362a... GUID) is built by WQA9 from
hardcoded buffers rather than from a package, and has no entry for
0x00200000 at all.
So while these interfaces look like the right way to expose
SW_CAMERA_LENS_COVER conditionally on firmware that implements them
(like on the 83JR platform), they do not seem usable on these two models.
5. I have basically no experience parsing WMI data and very little with
ACPI tables in general. Please feel free to correct me! And in general
take the above with a pinch of salt.
I hope the linked files will prove more useful than me in this regard.
Thanks again for your work on this, I am learning a lot from this thread.
Best regards,
Marco
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-23 11:17 ` Marco Giunta
@ 2026-09-23 12:39 ` Rong Zhang
2026-09-23 13:11 ` Marco Giunta
0 siblings, 1 reply; 20+ messages in thread
From: Rong Zhang @ 2026-09-23 12:39 UTC (permalink / raw)
To: Marco Giunta
Cc: derekjohn.clark, hansg, huangwei, ikepanhc, ilpo.jarvinen,
linux-kernel, mpearson-lenovo, platform-driver-x86
Hi Marco,
Thanks for the detailed information.
On Wed, 2026-09-23 at 13:17 +0200, Marco Giunta wrote:
> Hi Rong & Mark,
>
> thanks a lot for the impressively detailed and informative replies
> (as always). Here's an update from me.
>
> 1. overall I changed my mind regarding the conditional switch registration.
> I no longer really see it as particularly problematic in practice; as Rong
> said earlier, in practice it's mostly a theoretical concern given the
> typical userspace usage of this switch.
>
> I do agree that if we can find a smart way to filter for switch presence
> based on a good criterion, like a suitable wmi method, it would
> definitively be an improvement to patch v3. And I also appreciate that
> gaining a better understanding of these issues may improve other lenovo
> drivers. Finally, in general, I am certainly not trying to stifle the
> useful discussion going on here; what I'm trying to say is: as far as the
> camera switch patch is concerned, I am perfectly fine with the simple
> approach of v3 (at least for now, we can add more complexity later IMO).
Agreed. More comments below.
>
> 2. here https://github.com/marco-giunta/lenovo-acpi-dump you can find
> all raw & decompiled ACPI tables and bmof files from both my yoga and my
> legion. Feel free to ask for more data.
Good job. They are exactly what I want to cross-check with. They'll also
be useful when I develop future patches for lenovo-wmi-*.
>
> 3. Let me clarify what I meant when I said that the yoga and the lenovo
> handle the switch differently (and more generally report some highlights
> from the above files).
>
> The yoga's SSDT2 table contains:
>
> Device (WMIU)
> {
> Name (_HID, "PNP0C14" /* Windows Management Instrumentation Device */) // _HID: Hardware ID
> Name (_UID, "LSK20") // _UID: Unique ID
> Name (LSKE, 0x02)
> Name (_WDG, Buffer (0x3C)
> {
> /* 0000 */ 0x74, 0x09, 0x6C, 0xCE, 0x07, 0x04, 0x50, 0x4F, // t.l...PO
> /* 0008 */ 0x88, 0xBA, 0x4F, 0xC3, 0xB6, 0x55, 0x9A, 0xD8, // ..O..U..
> /* 0010 */ 0x53, 0x4B, 0x01, 0x02, 0x21, 0x12, 0x90, 0x05, // SK..!...
> /* 0018 */ 0x66, 0xD5, 0xD1, 0x11, 0xB2, 0xF0, 0x00, 0xA0, // f.......
> /* 0020 */ 0xC9, 0x06, 0x29, 0x10, 0x44, 0x41, 0x01, 0x00, // ..).DA..
> /* 0028 */ 0x0C, 0xDE, 0xC0, 0x8F, 0xE4, 0xB4, 0xFD, 0x43, // .......C
> /* 0030 */ 0xB0, 0xF3, 0x88, 0x71, 0x71, 0x1C, 0x12, 0x94, // ...qq...
> /* 0038 */ 0xD0, 0x00, 0x01, 0x08 // ....
> })
>
> so GUID 8FC0DE0C-B4E4-43FD-B0F3-8871711C1294 on device WMIU, event (0x08)
> with notify value 0xD0, no surprises here.
>
> Its _WED method is:
>
> Method (_WED, 1, NotSerialized) // _Wxx: Wake Event, xx=0x00-0xFF
> {
> If ((Arg0 == 0xD0))
> {
> Return (PSD1) /* \PSD1 */
> }
> }
>
> so "notify" = "return PSD1". Then below:
>
> Scope (\_SB.PCI0.LPC0.EC0)
> {
> ...
> Method (_Q29, 0, NotSerialized) // _Qxx: EC Query, xx=0x00-0xFF
> {
> If ((\_SB.PCI0.LPC0.EC0.COSD == 0x02))
> {
> PSD1 = 0x0C
> }
> ElseIf ((\_SB.PCI0.LPC0.EC0.COSD == Zero))
> {
> PSD1 = 0x0D
> }
>
> Notify (\_SB.WMIU, 0xD0) // Hardware-Specific
> }
> }
>
> So the camera on/off 0x0c/0x0d events are sent based on the COSD memory
> register in the EC.
>
> This COSD variable is never referred to again anywhere else in the ACPI tables.
Then we can't query the switch's state via WMI on the Yoga.
>
> On the Legion:
>
> Device (WMIU)
> {
> Name (_HID, "PNP0C14" /* Windows Management Instrumentation Device */) // _HID: Hardware ID
> Name (_UID, "LSK20") // _UID: Unique ID
> Name (_WDG, Buffer (0x3C)
> {
> /* 0000 */ 0x74, 0x09, 0x6C, 0xCE, 0x07, 0x04, 0x50, 0x4F, // t.l...PO
> /* 0008 */ 0x88, 0xBA, 0x4F, 0xC3, 0xB6, 0x55, 0x9A, 0xD8, // ..O..U..
> /* 0010 */ 0x53, 0x4B, 0x01, 0x02, 0x0C, 0xDE, 0xC0, 0x8F, // SK......
> /* 0018 */ 0xE4, 0xB4, 0xFD, 0x43, 0xB0, 0xF3, 0x88, 0x71, // ...C...q
> /* 0020 */ 0x71, 0x1C, 0x12, 0x94, 0xD0, 0x00, 0x01, 0x08, // q.......
> /* 0028 */ 0x21, 0x12, 0x90, 0x05, 0x66, 0xD5, 0xD1, 0x11, // !...f...
> /* 0030 */ 0xB2, 0xF0, 0x00, 0xA0, 0xC9, 0x06, 0x29, 0x10, // ......).
> /* 0038 */ 0x44, 0x41, 0x01, 0x00 // DA..
>
> })
>
> Same 8fc0... GUID, same [0xD0, 0x00, 0x01, 0x08] sequence. Then:
>
> Method (_WED, 1, NotSerialized) // _Wxx: Wake Event, xx=0x00-0xFF
> {
> If ((Arg0 == 0xD0))
> {
> Return (LSKD) /* External reference */
> }
> }
>
> But here this LSKD variable is written to with different logic. The relevant
> EC query handler is:
>
> Method (_QDF, 0, NotSerialized) // _Qxx: EC Query, xx=0x00-0xFF
> {
> P80B = 0xDF
> Sleep (0x05)
> LSKD = Zero
> If ((LSKV != Zero))
> {
> LSKD = LSKV /* \_SB_.PCI0.LPC0.EC0_.LSKV */
> If ((LSKD == 0x02)){}
> If ((LSKD == 0x03)){}
> LSKV = Zero
> }
>
> Notify (WMIU, 0xD0) // Hardware-Specific
> Notify (GZFD, 0xE8) // Hardware-Specific
> }
>
> so there are no assignments of 0x0c/0x0d like on the Yoga: the EC itself
> writes the event code into LSKV (a byte at offset 0x1F of the ERAX region,
> SystemMemory 0xFEEC2400), and the AML only copies it to LSKD and clears
> the mailbox.
>
> The constants therefore appear to live in the EC firmware rather than
> in the ACPI tables, and no AML-visible object keeps the switch state,
> unlike COSD on the Yoga (which is at least readable in principle, even if
> nothing else reads it).
>
> Overall, the firmware appears to work differently internally, and I wasn't
> able to find a common interface that a driver could use for this without
> resorting to per-model EC details (see below), though I may be missing
> something.
>
> 4. Unless I'm missing something, the IDs Mark mentioned don't really apply
> here.
>
> 0x00201000 does not appear anywhere in either machine's tables - consistent
> with Mark's point that this ID is new; both of my devices were released in
> 2025.
>
> 0x00200000 appears a few times in SSDT2 (yoga)/SSDT5 (legion) in contexts that
> look unrelated (bitmask arithmetic in DYTC on the Yoga, and in the GPS
> method on the Legion), plus once in the Yoga's capability data, see below.
>
> LENOVO_OTHER_METHOD is implemented on both machines as \_SB.GZFD.WMAE
> (object "AE" in the GZFD _WDG, flags 0x02 = method). Since the MOF
> declares GetFeatureValue as WmiMethodId(17), the relevant branch is
> Arg1 == 0x11, the same one as in Rong's 83JR snippet. As far as I can
> tell neither machine handles the two shutter IDs there: the Yoga has logic
> to dispatch based on device/feature bytes (and covers device 0 features
> 0x02 and 0x08, device 4 feature 0x03, and device 8 feature 0x01); instead
> the Legion compares whole IDs and its device-0 list goes from
> 0x001F0000 straight to 0x00210000. Unhandled IDs return Zero in both
> cases, which, if 0 means "closed", would make those IDs report a closed
> shutter regardless of the switch position. I would appreciate a second
> opinion on this reading, though.
>
> As for the capability data, the Yoga implements LENOVO_CAPABILITY_DATA_00,
> defined in the bmof as
>
> [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 00"), guid("{362a3afe-3d96-4665-8530-96dad5bb300e}")]
> class LENOVO_CAPABILITY_DATA_00 {
> [key, read] string InstanceName;
> [read] boolean Active;
> [WmiDataId(1), read, Description(" IDs.")] uint32 IDs;
> [WmiDataId(2), read, Description("Capability.")] uint32 Capability;
> [WmiDataId(3), read, Description("Capability Default Value.")] uint32 DefaultValue;
> };
>
> This object is implemented in the acpi tables with GUID
> 362A3AFE-3D96-4665-8530-96DAD5BB300E, object id "B0", as WQB0 -> GCD0,
> which returns the three dwords of each element of the CD00 package.
> CD00 enumerates the feature IDs in ascending order, and includes
>
> { 0x001F0000, Zero, Zero },
> { 0x00200000, Zero, Zero },
> { 0x00210000, Zero, Zero },
>
> while other entries in the same table have capability values such as
> 0x07, 0x09, 0x05, 0x03 and One. If the capability field is the bitmask
> from Mark's table, this would mean the CV DSP Camera Shutter is enumerated
> but declared as not supported on this model. (and again there is no
> 0x00201000 entry)
Just checked the SSDT2 table of Yoga, and I found the implementation of
WMIU very similar to my device (ThinkBook 14 G7+ ASP). Mine has no
0x00200000 package though, which makes some sense as it has a passive
shutter that physically covers the camera.
Perhaps the Yoga Pro 7 14ASP10 was developed in the direction of
supporting 0x00200000, but the dev team eventually removed it before
releasing.
>
> The Legion's capability data (same 362a... GUID) is built by WQA9 from
> hardcoded buffers rather than from a package, and has no entry for
> 0x00200000 at all.
Then we can't query the switch's state via WMI on the Legion.
>
> So while these interfaces look like the right way to expose
> SW_CAMERA_LENS_COVER conditionally on firmware that implements them
> (like on the 83JR platform), they do not seem usable on these two models.
Agreed.
>
> 5. I have basically no experience parsing WMI data and very little with
> ACPI tables in general. Please feel free to correct me! And in general
> take the above with a pinch of salt.
> I hope the linked files will prove more useful than me in this regard.
Your interpretation is well done and impressive!
In conclusion, I agreed that we should take the simple approach, i.e.,
exposing KEY_SW unconditionally. If we see a report for a 0x00200000-
capable device, we can add support for it then. For the time being, let's
focus on what we have first.
>
>
> Thanks again for your work on this, I am learning a lot from this thread.
Thanks a lot for your help, too.
I will submit my patch with your C-d-b, S-o-b, and T-b.
Thanks,
Rong
>
> Best regards,
> Marco
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER
2026-09-23 12:39 ` Rong Zhang
@ 2026-09-23 13:11 ` Marco Giunta
0 siblings, 0 replies; 20+ messages in thread
From: Marco Giunta @ 2026-09-23 13:11 UTC (permalink / raw)
To: i
Cc: derekjohn.clark, hansg, huangwei, ikepanhc, ilpo.jarvinen,
linux-kernel, marco_giunta, mpearson-lenovo, platform-driver-x86
Hi Rong,
> >
> > 1. overall I changed my mind regarding the conditional switch registration.
> > I no longer really see it as particularly problematic in practice; as Rong
> > said earlier, in practice it's mostly a theoretical concern given the
> > typical userspace usage of this switch.
> >
> > I do agree that if we can find a smart way to filter for switch presence
> > based on a good criterion, like a suitable wmi method, it would
> > definitively be an improvement to patch v3. And I also appreciate that
> > gaining a better understanding of these issues may improve other lenovo
> > drivers. Finally, in general, I am certainly not trying to stifle the
> > useful discussion going on here; what I'm trying to say is: as far as the
> > camera switch patch is concerned, I am perfectly fine with the simple
> > approach of v3 (at least for now, we can add more complexity later IMO).
>
> Agreed. More comments below.
Nice. Mark, any thoughts on this?
>
> >
> > 2. here https://github.com/marco-giunta/lenovo-acpi-dump you can find
> > all raw & decompiled ACPI tables and bmof files from both my yoga and my
> > legion. Feel free to ask for more data.
>
> Good job. They are exactly what I want to cross-check with. They'll also
> be useful when I develop future patches for lenovo-wmi-*.
Thanks, I'm happy to contribute this data (I assume no one has assembled
a general database of this stuff)
> Then we can't query the switch's state via WMI on the Yoga.
> Then we can't query the switch's state via WMI on the Legion.
yeah, that leaves only the v1/v2 approach as feasible for a conditional
registration AFAICT. If a maintainer prefers that, it's easy to revert
to the older patch, but my guess is that the simplified v3 is overall
preferable.
> > 5. I have basically no experience parsing WMI data and very little with
> > ACPI tables in general. Please feel free to correct me! And in general
> > take the above with a pinch of salt.
> > I hope the linked files will prove more useful than me in this regard.
>
> Your interpretation is well done and impressive!
Thanks, I spent the last few days studying acpi and wmi documentation/code
to be able to parse these files.
>
> In conclusion, I agreed that we should take the simple approach, i.e.,
> exposing KEY_SW unconditionally. If we see a report for a 0x00200000-
> capable device, we can add support for it then. For the time being, let's
> focus on what we have first.
>
> >
> >
> > Thanks again for your work on this, I am learning a lot from this thread.
>
> Thanks a lot for your help, too.
Happy to help.
I'll make sure to CC you for future pd/x86 patches targeting the Legion.
>
> I will submit my patch with your C-d-b, S-o-b, and T-b.
>
Looking forward to that. And don't forget to cc Huang, so they can offer
their reviewed-by for v3 as well.
Best regards,
Marco
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-09-23 13:11 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-19 8:41 [PATCH] platform/x86: ideapad-laptop: Report camera switch as SW_CAMERA_LENS_COVER Marco Giunta
2026-09-20 6:42 ` Huang Wei
2026-09-20 9:58 ` Marco Giunta
2026-09-20 10:29 ` Huang Wei
2026-09-20 10:58 ` Marco Giunta
2026-09-20 20:28 ` [PATCH v2] " Marco Giunta
2026-09-21 2:36 ` Huang Wei
2026-09-21 14:50 ` Rong Zhang
2026-09-21 15:03 ` Rong Zhang
2026-09-21 19:00 ` Marco Giunta
2026-09-22 11:08 ` Huang Wei
2026-09-22 12:18 ` Rong Zhang
2026-09-22 15:52 ` Mark Pearson
2026-09-22 15:58 ` Rong Zhang
2026-09-22 16:42 ` Rong Zhang
2026-09-22 17:32 ` Mark Pearson
2026-09-22 18:47 ` Rong Zhang
2026-09-23 11:17 ` Marco Giunta
2026-09-23 12:39 ` Rong Zhang
2026-09-23 13:11 ` Marco Giunta
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®