1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
// 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>
#include <linux/cleanup.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 int camera_shutter_input_setup(struct wmi_device *wdev, u8 camera_mode)
{
struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
int err;
priv->idev = input_allocate_device();
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);
input_report_switch(priv->idev, SW_CAMERA_LENS_COVER,
camera_mode == SW_CAMERA_ON ? 0 : 1);
input_sync(priv->idev);
err = input_register_device(priv->idev);
if (err) {
input_free_device(priv->idev);
priv->idev = NULL;
}
return err;
}
static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
{
struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
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;
}
guard(mutex)(&priv->notify_lock);
if (!priv->idev) {
if (camera_shutter_input_setup(wdev, camera_mode))
dev_warn(&wdev->dev, "Failed to register input device\n");
return;
}
if (camera_mode == SW_CAMERA_ON)
input_report_switch(priv->idev, SW_CAMERA_LENS_COVER, 0);
else
input_report_switch(priv->idev, SW_CAMERA_LENS_COVER, 1);
input_sync(priv->idev);
}
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);
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);
if (priv->idev)
input_unregister_device(priv->idev);
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");
|