2024-03-07 09:21:35

by Ai Chao

[permalink] [raw]
Subject: [PATCH v5] platform/x86: add lenovo wmi camera button driver

Add lenovo generic wmi driver to support camera button.
The Camera button is a GPIO device. This driver receives ACPI notifyi
when the camera button is switched on/off. This driver is used in
Lenovo A70, it is a Computer integrated machine.

Signed-off-by: Ai Chao <[email protected]>
---
v5: Remove camera button groups, modify KEY_CAMERA to SW_CAMERA_LENS_COVER.
v4: Remove lenovo_wmi_input_setup, move camera_mode into struct lenovo_wmi_priv.
v3: Remove lenovo_wmi_remove function.
v2: Adjust GPL v2 to GPL, adjust sprintf to sysfs_emit.

drivers/platform/x86/Kconfig | 12 ++++
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/lenovo-wmi-camera.c | 89 ++++++++++++++++++++++++
3 files changed, 102 insertions(+)
create mode 100644 drivers/platform/x86/lenovo-wmi-camera.c

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index bdd302274b9a..9506a455b547 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1001,6 +1001,18 @@ config INSPUR_PLATFORM_PROFILE
To compile this driver as a module, choose M here: the module
will be called inspur-platform-profile.

+config LENOVO_WMI_CAMERA
+ tristate "Lenovo WMI Camera Button driver"
+ depends on ACPI_WMI
+ depends on INPUT
+ help
+ This driver provides support for Lenovo camera button. The Camera
+ button is a GPIO device. This driver receives ACPI notify when the
+ camera button is switched on/off.
+
+ To compile this driver as a module, choose M here: the module
+ will be called lenovo-wmi-camera.
+
source "drivers/platform/x86/x86-android-tablets/Kconfig"

config FW_ATTR_CLASS
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 1de432e8861e..217e94d7c877 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
obj-$(CONFIG_YOGABOOK) += lenovo-yogabook.o
+obj-$(CONFIG_LENOVO_WMI_CAMERA) += lenovo-wmi-camera.o

# Intel
obj-y += intel/
diff --git a/drivers/platform/x86/lenovo-wmi-camera.c b/drivers/platform/x86/lenovo-wmi-camera.c
new file mode 100644
index 000000000000..571d67ade8ac
--- /dev/null
+++ b/drivers/platform/x86/lenovo-wmi-camera.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lenovo WMI Camera Button Driver
+ *
+ * Author: Ai Chao <[email protected]>
+ * Copyright (C) 2024 KylinSoft Corporation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/wmi.h>
+
+#define WMI_LENOVO_CAMERABUTTON_EVENT_GUID "50C76F1F-D8E4-D895-0A3D-62F4EA400013"
+
+struct lenovo_wmi_priv {
+ struct input_dev *idev;
+ struct device *dev;
+};
+
+enum {
+ SW_CAMERA_OFF = 0,
+ SW_CAMERA_ON = 1,
+};
+
+static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
+{
+ struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
+
+ if (obj->type == ACPI_TYPE_BUFFER &&
+ obj->buffer.pointer[0] <= SW_CAMERA_ON) {
+ /* obj->buffer.pointer[0] is camera mode:
+ * 0 camera close
+ * 1 camera open
+ */
+ input_report_switch(priv->idev, SW_CAMERA_LENS_COVER,
+ obj->buffer.pointer[0]);
+ input_sync(priv->idev);
+ } else {
+ dev_dbg(&wdev->dev, "Bad response type %d\n", obj->type);
+ }
+}
+
+static int lenovo_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+ struct lenovo_wmi_priv *priv;
+
+ priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&wdev->dev, priv);
+
+ priv->idev = devm_input_allocate_device(&wdev->dev);
+ if (!priv->idev)
+ return -ENOMEM;
+
+ priv->idev->name = "Lenovo WMI Camera Button";
+ priv->idev->phys = "wmi/input0";
+ priv->idev->id.bustype = BUS_HOST;
+ priv->idev->dev.parent = &wdev->dev;
+ input_set_capability(priv->idev, EV_SW, SW_CAMERA_LENS_COVER);
+
+ return input_register_device(priv->idev);
+}
+
+static const struct wmi_device_id lenovo_wmi_id_table[] = {
+ { .guid_string = WMI_LENOVO_CAMERABUTTON_EVENT_GUID },
+ { }
+};
+
+static struct wmi_driver lenovo_wmi_driver = {
+ .driver = {
+ .name = "lenovo-wmi-camera",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .id_table = lenovo_wmi_id_table,
+ .no_singleton = true,
+ .probe = lenovo_wmi_probe,
+ .notify = lenovo_wmi_notify,
+};
+
+module_wmi_driver(lenovo_wmi_driver);
+
+MODULE_DEVICE_TABLE(wmi, lenovo_wmi_id_table);
+MODULE_AUTHOR("Ai Chao <[email protected]>");
+MODULE_DESCRIPTION("Lenovo WMI Camera Button Driver");
+MODULE_LICENSE("GPL");
--
2.25.1



2024-03-08 13:06:18

by Ilpo Järvinen

[permalink] [raw]
Subject: Re: [PATCH v5] platform/x86: add lenovo wmi camera button driver

On Thu, 7 Mar 2024, Ai Chao wrote:

> Add lenovo generic wmi driver to support camera button.
> The Camera button is a GPIO device. This driver receives ACPI notifyi
> when the camera button is switched on/off. This driver is used in
> Lenovo A70, it is a Computer integrated machine.
>
> Signed-off-by: Ai Chao <[email protected]>
> ---
> v5: Remove camera button groups, modify KEY_CAMERA to SW_CAMERA_LENS_COVER.
> v4: Remove lenovo_wmi_input_setup, move camera_mode into struct lenovo_wmi_priv.
> v3: Remove lenovo_wmi_remove function.
> v2: Adjust GPL v2 to GPL, adjust sprintf to sysfs_emit.
>
> drivers/platform/x86/Kconfig | 12 ++++
> drivers/platform/x86/Makefile | 1 +
> drivers/platform/x86/lenovo-wmi-camera.c | 89 ++++++++++++++++++++++++
> 3 files changed, 102 insertions(+)
> create mode 100644 drivers/platform/x86/lenovo-wmi-camera.c
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index bdd302274b9a..9506a455b547 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1001,6 +1001,18 @@ config INSPUR_PLATFORM_PROFILE
> To compile this driver as a module, choose M here: the module
> will be called inspur-platform-profile.
>
> +config LENOVO_WMI_CAMERA
> + tristate "Lenovo WMI Camera Button driver"
> + depends on ACPI_WMI
> + depends on INPUT
> + help
> + This driver provides support for Lenovo camera button. The Camera
> + button is a GPIO device. This driver receives ACPI notify when the
> + camera button is switched on/off.
> +
> + To compile this driver as a module, choose M here: the module
> + will be called lenovo-wmi-camera.
> +
> source "drivers/platform/x86/x86-android-tablets/Kconfig"
>
> config FW_ATTR_CLASS
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 1de432e8861e..217e94d7c877 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -66,6 +66,7 @@ obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
> obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
> obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
> obj-$(CONFIG_YOGABOOK) += lenovo-yogabook.o
> +obj-$(CONFIG_LENOVO_WMI_CAMERA) += lenovo-wmi-camera.o
>
> # Intel
> obj-y += intel/
> diff --git a/drivers/platform/x86/lenovo-wmi-camera.c b/drivers/platform/x86/lenovo-wmi-camera.c
> new file mode 100644
> index 000000000000..571d67ade8ac
> --- /dev/null
> +++ b/drivers/platform/x86/lenovo-wmi-camera.c
> @@ -0,0 +1,89 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Lenovo WMI Camera Button Driver
> + *
> + * Author: Ai Chao <[email protected]>
> + * Copyright (C) 2024 KylinSoft Corporation.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/device.h>
> +#include <linux/input.h>
> +#include <linux/module.h>
> +#include <linux/wmi.h>
> +
> +#define WMI_LENOVO_CAMERABUTTON_EVENT_GUID "50C76F1F-D8E4-D895-0A3D-62F4EA400013"
> +
> +struct lenovo_wmi_priv {
> + struct input_dev *idev;
> + struct device *dev;
> +};
> +
> +enum {
> + SW_CAMERA_OFF = 0,
> + SW_CAMERA_ON = 1,
> +};
> +
> +static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
> +{
> + struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
> +
> + if (obj->type == ACPI_TYPE_BUFFER &&
> + obj->buffer.pointer[0] <= SW_CAMERA_ON) {
> + /* obj->buffer.pointer[0] is camera mode:
> + * 0 camera close
> + * 1 camera open
> + */
> + input_report_switch(priv->idev, SW_CAMERA_LENS_COVER,
> + obj->buffer.pointer[0]);
> + input_sync(priv->idev);
> + } else {
> + dev_dbg(&wdev->dev, "Bad response type %d\n", obj->type);

This message seems to assume camera mode is on the valid range. Wouldn't
it be useful from debugging point of view to show also the camera mode
value?

--
i.


2024-03-08 16:16:42

by Armin Wolf

[permalink] [raw]
Subject: Re: [PATCH v5] platform/x86: add lenovo wmi camera button driver

Am 08.03.24 um 09:40 schrieb 艾超:

> Hi
>
>     thanks for your help.
>
> > Is there a way to determine the current state of the camera switch
> without having
> > to wait for an WMI event?
>
> There is no other way to detemine the current state of the camera
> switch without
>
> a WMI event.
>
>
> > If its not possible to determine the current state of the camera
> switch, then your
> > driver has to defer the initialization of the input device until it
> knows the current
> > state of the camera switch. Otherwise the initial switch state
> reported by the
> > input device might be wrong.
>
> > In this case, that means your driver must initialize the input
> device when receiving
> > a valid WMI event for the first time.
>
> > Basically when your driver receives a WMI event, it has to check if
> the input device
> > is already initialized. If this is not the case, then the input
> device is initialized.
> > Please protect this check and the input device initialization with a
> mutex, since WMI
> > event handling is multithreaded.
>
>
> This Camera is a UVC device, and the Carema device is already
> initialized before the WMI
>
> event .  Whether the camera switch is on or off, we can used lsusb to
> check the Camera.
>
> This wmi-camera driver only need to report the current state of the
> camera switch.
>
>
> Thanks.
>
>     Ai Chao
>
I see, so userspace will notice when the camera is disabled. Since the camera disappears from
the USB bus when the switch is activated, i wonder if SW_CAMERA_LENS_COVER is the right thing
to use in this case after all.

I CCed the maintainer of the input subsystem so that he can maybe provide some advise.
I for example would, taking the above information above the UVC device into account, say that
KEY_CAMERA_ACCESS_ENABLE/KEY_CAMERA_ACCESS_DISABLE is more suitable. Then userspace has to
keep track of the camera state (through lsusb for example).

Maybe someone knowing the input subsystem can provide some advise on what to do in this case?

Thanks,
Armin Wolf


2024-03-10 04:53:49

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH v5] platform/x86: add lenovo wmi camera button driver

On Fri, Mar 08, 2024 at 05:15:48PM +0100, Armin Wolf wrote:
> Am 08.03.24 um 09:40 schrieb 艾超:
>
> > Hi
> >
> >     thanks for your help.
> >
> > > Is there a way to determine the current state of the camera switch
> > without having
> > > to wait for an WMI event?
> >
> > There is no other way to detemine the current state of the camera
> > switch without
> >
> > a WMI event.
> >
> >
> > > If its not possible to determine the current state of the camera
> > switch, then your
> > > driver has to defer the initialization of the input device until it
> > knows the current
> > > state of the camera switch. Otherwise the initial switch state
> > reported by the
> > > input device might be wrong.
> >
> > > In this case, that means your driver must initialize the input
> > device when receiving
> > > a valid WMI event for the first time.
> >
> > > Basically when your driver receives a WMI event, it has to check if
> > the input device
> > > is already initialized. If this is not the case, then the input
> > device is initialized.
> > > Please protect this check and the input device initialization with a
> > mutex, since WMI
> > > event handling is multithreaded.
> >
> >
> > This Camera is a UVC device, and the Carema device is already
> > initialized before the WMI
> >
> > event .  Whether the camera switch is on or off, we can used lsusb to
> > check the Camera.

It depends on which drivers are built-in and which are modules. The fact
that on one distribution WMI happens to enumerate after PCI/USB is just
a stroke of luck.

> >
> > This wmi-camera driver only need to report the current state of the
> > camera switch.
> >
> >
> > Thanks.
> >
> >     Ai Chao
> >
> I see, so userspace will notice when the camera is disabled. Since the camera disappears from
> the USB bus when the switch is activated, i wonder if SW_CAMERA_LENS_COVER is the right thing
> to use in this case after all.
>
> I CCed the maintainer of the input subsystem so that he can maybe provide some advise.
> I for example would, taking the above information above the UVC device into account, say that
> KEY_CAMERA_ACCESS_ENABLE/KEY_CAMERA_ACCESS_DISABLE is more suitable. Then userspace has to
> keep track of the camera state (through lsusb for example).

KEY_CAMERA_ACCESS_ENABLE/KEY_CAMERA_ACCESS_DISABLE is supposed to affect
all cameras connected to the system (HUTRR72: "The proposal is to add a
set of new Usage IDs to the Consumer Page (0x0C) to control programmatic
access to *all* camera devices connected to a computer." - emphasis
mine).

SW_CAMERA_LENS_COVER is useful when camera is always present but may be
covered.

If I understand this correctly the camera disappears from the bus and
reappears again when enabled. In this case why do you need the key event
at all? Are you planning on having a "stub" for the camera so that
userspace believes that the camera is always present but may be not
operational?

Thanks.

--
Dmitry

2024-03-07 18:19:11

by Armin Wolf

[permalink] [raw]
Subject: Re: [PATCH v5] platform/x86: add lenovo wmi camera button driver

Am 07.03.24 um 09:59 schrieb Ai Chao:

> Add lenovo generic wmi driver to support camera button.
> The Camera button is a GPIO device. This driver receives ACPI notifyi
> when the camera button is switched on/off. This driver is used in
> Lenovo A70, it is a Computer integrated machine.
>
> Signed-off-by: Ai Chao <[email protected]>
> ---
> v5: Remove camera button groups, modify KEY_CAMERA to SW_CAMERA_LENS_COVER.
> v4: Remove lenovo_wmi_input_setup, move camera_mode into struct lenovo_wmi_priv.
> v3: Remove lenovo_wmi_remove function.
> v2: Adjust GPL v2 to GPL, adjust sprintf to sysfs_emit.
>
> drivers/platform/x86/Kconfig | 12 ++++
> drivers/platform/x86/Makefile | 1 +
> drivers/platform/x86/lenovo-wmi-camera.c | 89 ++++++++++++++++++++++++
> 3 files changed, 102 insertions(+)
> create mode 100644 drivers/platform/x86/lenovo-wmi-camera.c
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index bdd302274b9a..9506a455b547 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1001,6 +1001,18 @@ config INSPUR_PLATFORM_PROFILE
> To compile this driver as a module, choose M here: the module
> will be called inspur-platform-profile.
>
> +config LENOVO_WMI_CAMERA
> + tristate "Lenovo WMI Camera Button driver"
> + depends on ACPI_WMI
> + depends on INPUT
> + help
> + This driver provides support for Lenovo camera button. The Camera
> + button is a GPIO device. This driver receives ACPI notify when the
> + camera button is switched on/off.
> +
> + To compile this driver as a module, choose M here: the module
> + will be called lenovo-wmi-camera.
> +
> source "drivers/platform/x86/x86-android-tablets/Kconfig"
>
> config FW_ATTR_CLASS
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 1de432e8861e..217e94d7c877 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -66,6 +66,7 @@ obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
> obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
> obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
> obj-$(CONFIG_YOGABOOK) += lenovo-yogabook.o
> +obj-$(CONFIG_LENOVO_WMI_CAMERA) += lenovo-wmi-camera.o
>
> # Intel
> obj-y += intel/
> diff --git a/drivers/platform/x86/lenovo-wmi-camera.c b/drivers/platform/x86/lenovo-wmi-camera.c
> new file mode 100644
> index 000000000000..571d67ade8ac
> --- /dev/null
> +++ b/drivers/platform/x86/lenovo-wmi-camera.c
> @@ -0,0 +1,89 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Lenovo WMI Camera Button Driver
> + *
> + * Author: Ai Chao <[email protected]>
> + * Copyright (C) 2024 KylinSoft Corporation.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/device.h>
> +#include <linux/input.h>
> +#include <linux/module.h>
> +#include <linux/wmi.h>
> +
> +#define WMI_LENOVO_CAMERABUTTON_EVENT_GUID "50C76F1F-D8E4-D895-0A3D-62F4EA400013"
> +
> +struct lenovo_wmi_priv {
> + struct input_dev *idev;
> + struct device *dev;
> +};
> +
> +enum {
> + SW_CAMERA_OFF = 0,
> + SW_CAMERA_ON = 1,
> +};
> +
> +static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
> +{
> + struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
> +
> + if (obj->type == ACPI_TYPE_BUFFER &&
> + obj->buffer.pointer[0] <= SW_CAMERA_ON) {

Hi,

please check that the ACPI buffer has an appropriate size before you access it.
In this case, check that the ACPI buffer has a length of 1;

> + /* obj->buffer.pointer[0] is camera mode:
> + * 0 camera close
> + * 1 camera open
> + */
> + input_report_switch(priv->idev, SW_CAMERA_LENS_COVER,
> + obj->buffer.pointer[0]);

If SW_CAMERA_LENS_COVER is set, then it means that the camera lens is covered. So in this case
you have to pass 1 when the camera is closed and 0 when the camera is open (basically the opposite
of camera mode).

> + input_sync(priv->idev);
> + } else {
> + dev_dbg(&wdev->dev, "Bad response type %d\n", obj->type);
> + }
> +}
> +
> +static int lenovo_wmi_probe(struct wmi_device *wdev, const void *context)
> +{
> + struct lenovo_wmi_priv *priv;
> +
> + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + dev_set_drvdata(&wdev->dev, priv);
> +
> + priv->idev = devm_input_allocate_device(&wdev->dev);
> + if (!priv->idev)
> + return -ENOMEM;
> +
> + priv->idev->name = "Lenovo WMI Camera Button";
> + priv->idev->phys = "wmi/input0";
> + priv->idev->id.bustype = BUS_HOST;
> + priv->idev->dev.parent = &wdev->dev;
> + input_set_capability(priv->idev, EV_SW, SW_CAMERA_LENS_COVER);
> +

Is there a way to determine the current state of the camera switch without having
to wait for an WMI event?

If its not possible to determine the current state of the camera switch, then your
driver has to defer the initialization of the input device until it knows the current
state of the camera switch. Otherwise the initial switch state reported by the
input device might be wrong.

In this case, that means your driver must initialize the input device when receiving
a valid WMI event for the first time.

Basically when your driver receives a WMI event, it has to check if the input device
is already initialized. If this is not the case, then the input device is initialized.
Please protect this check and the input device initialization with a mutex, since WMI
event handling is multithreaded.

After that the event is reported as usual.

Other than that, the driver seems to be in good shape.

Thanks,
Armin Wolf

> + return input_register_device(priv->idev);
> +}
> +
> +static const struct wmi_device_id lenovo_wmi_id_table[] = {
> + { .guid_string = WMI_LENOVO_CAMERABUTTON_EVENT_GUID },
> + { }
> +};
> +
> +static struct wmi_driver lenovo_wmi_driver = {
> + .driver = {
> + .name = "lenovo-wmi-camera",
> + .probe_type = PROBE_PREFER_ASYNCHRONOUS,
> + },
> + .id_table = lenovo_wmi_id_table,
> + .no_singleton = true,
> + .probe = lenovo_wmi_probe,
> + .notify = lenovo_wmi_notify,
> +};
> +
> +module_wmi_driver(lenovo_wmi_driver);
> +
> +MODULE_DEVICE_TABLE(wmi, lenovo_wmi_id_table);
> +MODULE_AUTHOR("Ai Chao <[email protected]>");
> +MODULE_DESCRIPTION("Lenovo WMI Camera Button Driver");
> +MODULE_LICENSE("GPL");