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