1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Lenovo WMI Camera Button Driver
4  *
5  * Author: Ai Chao <[email protected]>
6  * Copyright (C) 2024 KylinSoft Corporation.
7  */
8 
9 #include <linux/acpi.h>
10 #include <linux/device.h>
11 #include <linux/input.h>
12 #include <linux/types.h>
13 #include <linux/module.h>
14 #include <linux/mutex.h>
15 #include <linux/wmi.h>
16 #include <linux/cleanup.h>
17 
18 #define WMI_LENOVO_CAMERABUTTON_EVENT_GUID "50C76F1F-D8E4-D895-0A3D-62F4EA400013"
19 
20 struct lenovo_wmi_priv {
21 	struct input_dev *idev;
22 	struct mutex notify_lock;	/* lenovo WMI camera button notify lock */
23 };
24 
25 enum {
26 	SW_CAMERA_OFF	= 0,
27 	SW_CAMERA_ON	= 1,
28 };
29 
camera_shutter_input_setup(struct wmi_device * wdev,u8 camera_mode)30 static int camera_shutter_input_setup(struct wmi_device *wdev, u8 camera_mode)
31 {
32 	struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
33 	int err;
34 
35 	priv->idev = input_allocate_device();
36 	if (!priv->idev)
37 		return -ENOMEM;
38 
39 	priv->idev->name = "Lenovo WMI Camera Button";
40 	priv->idev->phys = "wmi/input0";
41 	priv->idev->id.bustype = BUS_HOST;
42 	priv->idev->dev.parent = &wdev->dev;
43 
44 	input_set_capability(priv->idev, EV_SW, SW_CAMERA_LENS_COVER);
45 
46 	input_report_switch(priv->idev, SW_CAMERA_LENS_COVER,
47 			    camera_mode == SW_CAMERA_ON ? 0 : 1);
48 	input_sync(priv->idev);
49 
50 	err = input_register_device(priv->idev);
51 	if (err) {
52 		input_free_device(priv->idev);
53 		priv->idev = NULL;
54 	}
55 
56 	return err;
57 }
58 
lenovo_wmi_notify(struct wmi_device * wdev,union acpi_object * obj)59 static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
60 {
61 	struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
62 	u8 camera_mode;
63 
64 	if (obj->type != ACPI_TYPE_BUFFER) {
65 		dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
66 		return;
67 	}
68 
69 	if (obj->buffer.length != 1) {
70 		dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length);
71 		return;
72 	}
73 
74 	/*
75 	 * obj->buffer.pointer[0] is camera mode:
76 	 *      0 camera close
77 	 *      1 camera open
78 	 */
79 	camera_mode = obj->buffer.pointer[0];
80 	if (camera_mode > SW_CAMERA_ON) {
81 		dev_err(&wdev->dev, "Unknown camera mode %u\n", camera_mode);
82 		return;
83 	}
84 
85 	guard(mutex)(&priv->notify_lock);
86 
87 	if (!priv->idev) {
88 		if (camera_shutter_input_setup(wdev, camera_mode))
89 			dev_warn(&wdev->dev, "Failed to register input device\n");
90 		return;
91 	}
92 
93 	if (camera_mode == SW_CAMERA_ON)
94 		input_report_switch(priv->idev, SW_CAMERA_LENS_COVER, 0);
95 	else
96 		input_report_switch(priv->idev, SW_CAMERA_LENS_COVER, 1);
97 	input_sync(priv->idev);
98 }
99 
lenovo_wmi_probe(struct wmi_device * wdev,const void * context)100 static int lenovo_wmi_probe(struct wmi_device *wdev, const void *context)
101 {
102 	struct lenovo_wmi_priv *priv;
103 
104 	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
105 	if (!priv)
106 		return -ENOMEM;
107 
108 	dev_set_drvdata(&wdev->dev, priv);
109 
110 	mutex_init(&priv->notify_lock);
111 
112 	return 0;
113 }
114 
lenovo_wmi_remove(struct wmi_device * wdev)115 static void lenovo_wmi_remove(struct wmi_device *wdev)
116 {
117 	struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
118 
119 	if (priv->idev)
120 		input_unregister_device(priv->idev);
121 
122 	mutex_destroy(&priv->notify_lock);
123 }
124 
125 static const struct wmi_device_id lenovo_wmi_id_table[] = {
126 	{ .guid_string = WMI_LENOVO_CAMERABUTTON_EVENT_GUID },
127 	{  }
128 };
129 MODULE_DEVICE_TABLE(wmi, lenovo_wmi_id_table);
130 
131 static struct wmi_driver lenovo_wmi_driver = {
132 	.driver = {
133 		.name = "lenovo-wmi-camera",
134 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
135 	},
136 	.id_table = lenovo_wmi_id_table,
137 	.no_singleton = true,
138 	.probe = lenovo_wmi_probe,
139 	.notify = lenovo_wmi_notify,
140 	.remove = lenovo_wmi_remove,
141 };
142 module_wmi_driver(lenovo_wmi_driver);
143 
144 MODULE_AUTHOR("Ai Chao <[email protected]>");
145 MODULE_DESCRIPTION("Lenovo WMI Camera Button Driver");
146 MODULE_LICENSE("GPL");
147