123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Dell privacy notification driver
- *
- * Copyright (C) 2021 Dell Inc. All Rights Reserved.
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/acpi.h>
- #include <linux/bitops.h>
- #include <linux/input.h>
- #include <linux/input/sparse-keymap.h>
- #include <linux/list.h>
- #include <linux/leds.h>
- #include <linux/module.h>
- #include <linux/wmi.h>
- #include "dell-wmi-privacy.h"
- #define DELL_PRIVACY_GUID "6932965F-1671-4CEB-B988-D3AB0A901919"
- #define MICROPHONE_STATUS BIT(0)
- #define CAMERA_STATUS BIT(1)
- #define DELL_PRIVACY_AUDIO_EVENT 0x1
- #define DELL_PRIVACY_CAMERA_EVENT 0x2
- #define led_to_priv(c) container_of(c, struct privacy_wmi_data, cdev)
- /*
- * The wmi_list is used to store the privacy_priv struct with mutex protecting
- */
- static LIST_HEAD(wmi_list);
- static DEFINE_MUTEX(list_mutex);
- struct privacy_wmi_data {
- struct input_dev *input_dev;
- struct wmi_device *wdev;
- struct list_head list;
- struct led_classdev cdev;
- u32 features_present;
- u32 last_status;
- };
- /* DELL Privacy Type */
- enum dell_hardware_privacy_type {
- DELL_PRIVACY_TYPE_AUDIO = 0,
- DELL_PRIVACY_TYPE_CAMERA,
- DELL_PRIVACY_TYPE_SCREEN,
- DELL_PRIVACY_TYPE_MAX,
- };
- static const char * const privacy_types[DELL_PRIVACY_TYPE_MAX] = {
- [DELL_PRIVACY_TYPE_AUDIO] = "Microphone",
- [DELL_PRIVACY_TYPE_CAMERA] = "Camera Shutter",
- [DELL_PRIVACY_TYPE_SCREEN] = "ePrivacy Screen",
- };
- /*
- * Keymap for WMI privacy events of type 0x0012
- */
- static const struct key_entry dell_wmi_keymap_type_0012[] = {
- /* privacy mic mute */
- { KE_KEY, 0x0001, { KEY_MICMUTE } },
- /* privacy camera mute */
- { KE_VSW, 0x0002, { SW_CAMERA_LENS_COVER } },
- { KE_END, 0},
- };
- bool dell_privacy_has_mic_mute(void)
- {
- struct privacy_wmi_data *priv;
- mutex_lock(&list_mutex);
- priv = list_first_entry_or_null(&wmi_list,
- struct privacy_wmi_data,
- list);
- mutex_unlock(&list_mutex);
- return priv && (priv->features_present & BIT(DELL_PRIVACY_TYPE_AUDIO));
- }
- EXPORT_SYMBOL_GPL(dell_privacy_has_mic_mute);
- /*
- * The flow of privacy event:
- * 1) User presses key. HW does stuff with this key (timeout is started)
- * 2) WMI event is emitted from BIOS
- * 3) WMI event is received by dell-privacy
- * 4) KEY_MICMUTE emitted from dell-privacy
- * 5) Userland picks up key and modifies kcontrol for SW mute
- * 6) Codec kernel driver catches and calls ledtrig_audio_set which will call
- * led_set_brightness() on the LED registered by dell_privacy_leds_setup()
- * 7) dell-privacy notifies EC, the timeout is cancelled and the HW mute activates.
- * If the EC is not notified then the HW mic mute will activate when the timeout
- * triggers, just a bit later than with the active ack.
- */
- bool dell_privacy_process_event(int type, int code, int status)
- {
- struct privacy_wmi_data *priv;
- const struct key_entry *key;
- bool ret = false;
- mutex_lock(&list_mutex);
- priv = list_first_entry_or_null(&wmi_list,
- struct privacy_wmi_data,
- list);
- if (!priv)
- goto error;
- key = sparse_keymap_entry_from_scancode(priv->input_dev, (type << 16) | code);
- if (!key) {
- dev_warn(&priv->wdev->dev, "Unknown key with type 0x%04x and code 0x%04x pressed\n",
- type, code);
- goto error;
- }
- dev_dbg(&priv->wdev->dev, "Key with type 0x%04x and code 0x%04x pressed\n", type, code);
- switch (code) {
- case DELL_PRIVACY_AUDIO_EVENT: /* Mic mute */
- priv->last_status = status;
- sparse_keymap_report_entry(priv->input_dev, key, 1, true);
- ret = true;
- break;
- case DELL_PRIVACY_CAMERA_EVENT: /* Camera mute */
- priv->last_status = status;
- sparse_keymap_report_entry(priv->input_dev, key, !(status & CAMERA_STATUS), false);
- ret = true;
- break;
- default:
- dev_dbg(&priv->wdev->dev, "unknown event type 0x%04x 0x%04x\n", type, code);
- }
- error:
- mutex_unlock(&list_mutex);
- return ret;
- }
- static ssize_t dell_privacy_supported_type_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct privacy_wmi_data *priv = dev_get_drvdata(dev);
- enum dell_hardware_privacy_type type;
- u32 privacy_list;
- int len = 0;
- privacy_list = priv->features_present;
- for (type = DELL_PRIVACY_TYPE_AUDIO; type < DELL_PRIVACY_TYPE_MAX; type++) {
- if (privacy_list & BIT(type))
- len += sysfs_emit_at(buf, len, "[%s] [supported]\n", privacy_types[type]);
- else
- len += sysfs_emit_at(buf, len, "[%s] [unsupported]\n", privacy_types[type]);
- }
- return len;
- }
- static ssize_t dell_privacy_current_state_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct privacy_wmi_data *priv = dev_get_drvdata(dev);
- u32 privacy_supported = priv->features_present;
- enum dell_hardware_privacy_type type;
- u32 privacy_state = priv->last_status;
- int len = 0;
- for (type = DELL_PRIVACY_TYPE_AUDIO; type < DELL_PRIVACY_TYPE_MAX; type++) {
- if (privacy_supported & BIT(type)) {
- if (privacy_state & BIT(type))
- len += sysfs_emit_at(buf, len, "[%s] [unmuted]\n", privacy_types[type]);
- else
- len += sysfs_emit_at(buf, len, "[%s] [muted]\n", privacy_types[type]);
- }
- }
- return len;
- }
- static DEVICE_ATTR_RO(dell_privacy_supported_type);
- static DEVICE_ATTR_RO(dell_privacy_current_state);
- static struct attribute *privacy_attrs[] = {
- &dev_attr_dell_privacy_supported_type.attr,
- &dev_attr_dell_privacy_current_state.attr,
- NULL,
- };
- ATTRIBUTE_GROUPS(privacy);
- /*
- * Describes the Device State class exposed by BIOS which can be consumed by
- * various applications interested in knowing the Privacy feature capabilities.
- * class DeviceState
- * {
- * [key, read] string InstanceName;
- * [read] boolean ReadOnly;
- *
- * [WmiDataId(1), read] uint32 DevicesSupported;
- * 0 - None; 0x1 - Microphone; 0x2 - Camera; 0x4 - ePrivacy Screen
- *
- * [WmiDataId(2), read] uint32 CurrentState;
- * 0 - Off; 1 - On; Bit0 - Microphone; Bit1 - Camera; Bit2 - ePrivacyScreen
- * };
- */
- static int get_current_status(struct wmi_device *wdev)
- {
- struct privacy_wmi_data *priv = dev_get_drvdata(&wdev->dev);
- union acpi_object *obj_present;
- u32 *buffer;
- int ret = 0;
- if (!priv) {
- dev_err(&wdev->dev, "dell privacy priv is NULL\n");
- return -EINVAL;
- }
- /* check privacy support features and device states */
- obj_present = wmidev_block_query(wdev, 0);
- if (!obj_present) {
- dev_err(&wdev->dev, "failed to read Binary MOF\n");
- return -EIO;
- }
- if (obj_present->type != ACPI_TYPE_BUFFER) {
- dev_err(&wdev->dev, "Binary MOF is not a buffer!\n");
- ret = -EIO;
- goto obj_free;
- }
- /* Although it's not technically a failure, this would lead to
- * unexpected behavior
- */
- if (obj_present->buffer.length != 8) {
- dev_err(&wdev->dev, "Dell privacy buffer has unexpected length (%d)!\n",
- obj_present->buffer.length);
- ret = -EINVAL;
- goto obj_free;
- }
- buffer = (u32 *)obj_present->buffer.pointer;
- priv->features_present = buffer[0];
- priv->last_status = buffer[1];
- obj_free:
- kfree(obj_present);
- return ret;
- }
- static int dell_privacy_micmute_led_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
- {
- struct privacy_wmi_data *priv = led_to_priv(led_cdev);
- static char *acpi_method = (char *)"ECAK";
- acpi_status status;
- acpi_handle handle;
- handle = ec_get_handle();
- if (!handle)
- return -EIO;
- if (!acpi_has_method(handle, acpi_method))
- return -EIO;
- status = acpi_evaluate_object(handle, acpi_method, NULL, NULL);
- if (ACPI_FAILURE(status)) {
- dev_err(&priv->wdev->dev, "Error setting privacy EC ack value: %s\n",
- acpi_format_exception(status));
- return -EIO;
- }
- return 0;
- }
- /*
- * Pressing the mute key activates a time delayed circuit to physically cut
- * off the mute. The LED is in the same circuit, so it reflects the true
- * state of the HW mute. The reason for the EC "ack" is so that software
- * can first invoke a SW mute before the HW circuit is cut off. Without SW
- * cutting this off first does not affect the time delayed muting or status
- * of the LED but there is a possibility of a "popping" noise.
- *
- * If the EC receives the SW ack, the circuit will be activated before the
- * delay completed.
- *
- * Exposing as an LED device allows the codec drivers notification path to
- * EC ACK to work
- */
- static int dell_privacy_leds_setup(struct device *dev)
- {
- struct privacy_wmi_data *priv = dev_get_drvdata(dev);
- priv->cdev.name = "dell-privacy::micmute";
- priv->cdev.max_brightness = 1;
- priv->cdev.brightness_set_blocking = dell_privacy_micmute_led_set;
- priv->cdev.default_trigger = "audio-micmute";
- priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
- return devm_led_classdev_register(dev, &priv->cdev);
- }
- static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context)
- {
- struct privacy_wmi_data *priv;
- struct key_entry *keymap;
- int ret, i, j;
- ret = wmi_has_guid(DELL_PRIVACY_GUID);
- if (!ret)
- pr_debug("Unable to detect available Dell privacy devices!\n");
- priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- dev_set_drvdata(&wdev->dev, priv);
- priv->wdev = wdev;
- ret = get_current_status(priv->wdev);
- if (ret)
- return ret;
- /* create evdev passing interface */
- priv->input_dev = devm_input_allocate_device(&wdev->dev);
- if (!priv->input_dev)
- return -ENOMEM;
- /* remap the wmi keymap event to new keymap */
- keymap = kcalloc(ARRAY_SIZE(dell_wmi_keymap_type_0012),
- sizeof(struct key_entry), GFP_KERNEL);
- if (!keymap)
- return -ENOMEM;
- /* remap the keymap code with Dell privacy key type 0x12 as prefix
- * KEY_MICMUTE scancode will be reported as 0x120001
- */
- for (i = 0, j = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0012); i++) {
- /*
- * Unlike keys where only presses matter, userspace may act
- * on switches in both of their positions. Only register
- * SW_CAMERA_LENS_COVER if it is actually there.
- */
- if (dell_wmi_keymap_type_0012[i].type == KE_VSW &&
- dell_wmi_keymap_type_0012[i].sw.code == SW_CAMERA_LENS_COVER &&
- !(priv->features_present & BIT(DELL_PRIVACY_TYPE_CAMERA)))
- continue;
- keymap[j] = dell_wmi_keymap_type_0012[i];
- keymap[j].code |= (0x0012 << 16);
- j++;
- }
- ret = sparse_keymap_setup(priv->input_dev, keymap, NULL);
- kfree(keymap);
- if (ret)
- return ret;
- priv->input_dev->dev.parent = &wdev->dev;
- priv->input_dev->name = "Dell Privacy Driver";
- priv->input_dev->id.bustype = BUS_HOST;
- /* Report initial camera-cover status */
- if (priv->features_present & BIT(DELL_PRIVACY_TYPE_CAMERA))
- input_report_switch(priv->input_dev, SW_CAMERA_LENS_COVER,
- !(priv->last_status & CAMERA_STATUS));
- ret = input_register_device(priv->input_dev);
- if (ret)
- return ret;
- if (priv->features_present & BIT(DELL_PRIVACY_TYPE_AUDIO)) {
- ret = dell_privacy_leds_setup(&priv->wdev->dev);
- if (ret)
- return ret;
- }
- mutex_lock(&list_mutex);
- list_add_tail(&priv->list, &wmi_list);
- mutex_unlock(&list_mutex);
- return 0;
- }
- static void dell_privacy_wmi_remove(struct wmi_device *wdev)
- {
- struct privacy_wmi_data *priv = dev_get_drvdata(&wdev->dev);
- mutex_lock(&list_mutex);
- list_del(&priv->list);
- mutex_unlock(&list_mutex);
- }
- static const struct wmi_device_id dell_wmi_privacy_wmi_id_table[] = {
- { .guid_string = DELL_PRIVACY_GUID },
- { },
- };
- static struct wmi_driver dell_privacy_wmi_driver = {
- .driver = {
- .name = "dell-privacy",
- .dev_groups = privacy_groups,
- },
- .probe = dell_privacy_wmi_probe,
- .remove = dell_privacy_wmi_remove,
- .id_table = dell_wmi_privacy_wmi_id_table,
- };
- int dell_privacy_register_driver(void)
- {
- return wmi_driver_register(&dell_privacy_wmi_driver);
- }
- void dell_privacy_unregister_driver(void)
- {
- wmi_driver_unregister(&dell_privacy_wmi_driver);
- }
|