// SPDX-License-Identifier: GPL-2.0-only /* * Dell privacy notification driver * * Copyright (C) 2021 Dell Inc. All Rights Reserved. */ #define pr_fmt(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 … #define MICROPHONE_STATUS … #define CAMERA_STATUS … #define DELL_PRIVACY_AUDIO_EVENT … #define DELL_PRIVACY_CAMERA_EVENT … #define led_to_priv(c) … /* * 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 { … }; /* DELL Privacy Type */ enum dell_hardware_privacy_type { … }; static const char * const privacy_types[DELL_PRIVACY_TYPE_MAX] = …; /* * Keymap for WMI privacy events of type 0x0012 */ static const struct key_entry dell_wmi_keymap_type_0012[] = …; bool dell_privacy_has_mic_mute(void) { … } EXPORT_SYMBOL_GPL(…); /* * 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) { … } static ssize_t dell_privacy_supported_type_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t dell_privacy_current_state_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(dell_privacy_supported_type); static DEVICE_ATTR_RO(dell_privacy_current_state); static struct attribute *privacy_attrs[] = …; ATTRIBUTE_GROUPS(…); /* * 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) { … } static int dell_privacy_micmute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) { … } /* * 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) { … } static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context) { … } static void dell_privacy_wmi_remove(struct wmi_device *wdev) { … } static const struct wmi_device_id dell_wmi_privacy_wmi_id_table[] = …; static struct wmi_driver dell_privacy_wmi_driver = …; int dell_privacy_register_driver(void) { … } void dell_privacy_unregister_driver(void) { … }