lenovo-wmi-camera.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Lenovo WMI Camera Button Driver
  4. *
  5. * Author: Ai Chao <aichao@kylinos.cn>
  6. * Copyright (C) 2024 KylinSoft Corporation.
  7. */
  8. #include <linux/acpi.h>
  9. #include <linux/device.h>
  10. #include <linux/input.h>
  11. #include <linux/types.h>
  12. #include <linux/module.h>
  13. #include <linux/mutex.h>
  14. #include <linux/wmi.h>
  15. #define WMI_LENOVO_CAMERABUTTON_EVENT_GUID "50C76F1F-D8E4-D895-0A3D-62F4EA400013"
  16. struct lenovo_wmi_priv {
  17. struct input_dev *idev;
  18. struct mutex notify_lock; /* lenovo WMI camera button notify lock */
  19. };
  20. enum {
  21. SW_CAMERA_OFF = 0,
  22. SW_CAMERA_ON = 1,
  23. };
  24. static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
  25. {
  26. struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
  27. unsigned int keycode;
  28. u8 camera_mode;
  29. if (obj->type != ACPI_TYPE_BUFFER) {
  30. dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
  31. return;
  32. }
  33. if (obj->buffer.length != 1) {
  34. dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length);
  35. return;
  36. }
  37. /*
  38. * obj->buffer.pointer[0] is camera mode:
  39. * 0 camera close
  40. * 1 camera open
  41. */
  42. camera_mode = obj->buffer.pointer[0];
  43. if (camera_mode > SW_CAMERA_ON) {
  44. dev_err(&wdev->dev, "Unknown camera mode %u\n", camera_mode);
  45. return;
  46. }
  47. mutex_lock(&priv->notify_lock);
  48. keycode = camera_mode == SW_CAMERA_ON ?
  49. KEY_CAMERA_ACCESS_ENABLE : KEY_CAMERA_ACCESS_DISABLE;
  50. input_report_key(priv->idev, keycode, 1);
  51. input_sync(priv->idev);
  52. input_report_key(priv->idev, keycode, 0);
  53. input_sync(priv->idev);
  54. mutex_unlock(&priv->notify_lock);
  55. }
  56. static int lenovo_wmi_probe(struct wmi_device *wdev, const void *context)
  57. {
  58. struct lenovo_wmi_priv *priv;
  59. int ret;
  60. priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
  61. if (!priv)
  62. return -ENOMEM;
  63. dev_set_drvdata(&wdev->dev, priv);
  64. priv->idev = devm_input_allocate_device(&wdev->dev);
  65. if (!priv->idev)
  66. return -ENOMEM;
  67. priv->idev->name = "Lenovo WMI Camera Button";
  68. priv->idev->phys = "wmi/input0";
  69. priv->idev->id.bustype = BUS_HOST;
  70. priv->idev->dev.parent = &wdev->dev;
  71. input_set_capability(priv->idev, EV_KEY, KEY_CAMERA_ACCESS_ENABLE);
  72. input_set_capability(priv->idev, EV_KEY, KEY_CAMERA_ACCESS_DISABLE);
  73. ret = input_register_device(priv->idev);
  74. if (ret)
  75. return ret;
  76. mutex_init(&priv->notify_lock);
  77. return 0;
  78. }
  79. static void lenovo_wmi_remove(struct wmi_device *wdev)
  80. {
  81. struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
  82. mutex_destroy(&priv->notify_lock);
  83. }
  84. static const struct wmi_device_id lenovo_wmi_id_table[] = {
  85. { .guid_string = WMI_LENOVO_CAMERABUTTON_EVENT_GUID },
  86. { }
  87. };
  88. MODULE_DEVICE_TABLE(wmi, lenovo_wmi_id_table);
  89. static struct wmi_driver lenovo_wmi_driver = {
  90. .driver = {
  91. .name = "lenovo-wmi-camera",
  92. .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  93. },
  94. .id_table = lenovo_wmi_id_table,
  95. .no_singleton = true,
  96. .probe = lenovo_wmi_probe,
  97. .notify = lenovo_wmi_notify,
  98. .remove = lenovo_wmi_remove,
  99. };
  100. module_wmi_driver(lenovo_wmi_driver);
  101. MODULE_AUTHOR("Ai Chao <aichao@kylinos.cn>");
  102. MODULE_DESCRIPTION("Lenovo WMI Camera Button Driver");
  103. MODULE_LICENSE("GPL");