platform/x86: add lenovo WMI camera button driver
authorAi Chao <aichao@kylinos.cn>
Wed, 27 Mar 2024 08:27:37 +0000 (16:27 +0800)
committerHans de Goede <hdegoede@redhat.com>
Mon, 8 Apr 2024 13:21:36 +0000 (15:21 +0200)
Add lenovo WMI camera button driver to support camera button.
The Camera button is a GPIO device. This driver receives ACPI notifications
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 <aichao@kylinos.cn>
Link: https://lore.kernel.org/r/20240327082737.336992-1-aichao@kylinos.cn
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/lenovo-wmi-camera.c [new file with mode: 0644]

index 7e9251fc33416ed8def47fca7a986a342a7f6f2d..cf9aa220529c7ec602d2e36c1f9737d12d39f414 100644 (file)
@@ -996,6 +996,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 notifications 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
index 1de432e8861eac5937f20eca57ac6148837b981b..217e94d7c87745544830e777d4f8a207db651c99 100644 (file)
@@ -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 (file)
index 0000000..0c0beda
--- /dev/null
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lenovo WMI Camera Button Driver
+ *
+ * Author: Ai Chao <aichao@kylinos.cn>
+ * Copyright (C) 2024 KylinSoft Corporation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/mutex.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 mutex notify_lock;       /* lenovo WMI camera button notify lock */
+};
+
+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);
+       unsigned int keycode;
+       u8 camera_mode;
+
+       if (obj->type != ACPI_TYPE_BUFFER) {
+               dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
+               return;
+       }
+
+       if (obj->buffer.length != 1) {
+               dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length);
+               return;
+       }
+
+       /*
+        * obj->buffer.pointer[0] is camera mode:
+        *      0 camera close
+        *      1 camera open
+        */
+       camera_mode = obj->buffer.pointer[0];
+       if (camera_mode > SW_CAMERA_ON) {
+               dev_err(&wdev->dev, "Unknown camera mode %u\n", camera_mode);
+               return;
+       }
+
+       mutex_lock(&priv->notify_lock);
+
+       keycode = camera_mode == SW_CAMERA_ON ?
+                  KEY_CAMERA_ACCESS_ENABLE : KEY_CAMERA_ACCESS_DISABLE;
+       input_report_key(priv->idev, keycode, 1);
+       input_sync(priv->idev);
+       input_report_key(priv->idev, keycode, 0);
+       input_sync(priv->idev);
+
+       mutex_unlock(&priv->notify_lock);
+}
+
+static int lenovo_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+       struct lenovo_wmi_priv *priv;
+       int ret;
+
+       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_KEY, KEY_CAMERA_ACCESS_ENABLE);
+       input_set_capability(priv->idev, EV_KEY, KEY_CAMERA_ACCESS_DISABLE);
+
+       ret = input_register_device(priv->idev);
+       if (ret)
+               return ret;
+
+       mutex_init(&priv->notify_lock);
+
+       return 0;
+}
+
+static void lenovo_wmi_remove(struct wmi_device *wdev)
+{
+       struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
+
+       mutex_destroy(&priv->notify_lock);
+}
+
+static const struct wmi_device_id lenovo_wmi_id_table[] = {
+       { .guid_string = WMI_LENOVO_CAMERABUTTON_EVENT_GUID },
+       {  }
+};
+MODULE_DEVICE_TABLE(wmi, lenovo_wmi_id_table);
+
+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,
+       .remove = lenovo_wmi_remove,
+};
+module_wmi_driver(lenovo_wmi_driver);
+
+MODULE_AUTHOR("Ai Chao <aichao@kylinos.cn>");
+MODULE_DESCRIPTION("Lenovo WMI Camera Button Driver");
+MODULE_LICENSE("GPL");