// SPDX-License-Identifier: GPL-2.0-only /* * Driver for Dell laptop extras * * Copyright (c) Red Hat <[email protected]> * Copyright (c) 2014 Gabriele Mazzotta <[email protected]> * Copyright (c) 2014 Pali Rohár <[email protected]> * * Based on documentation in the libsmbios package: * Copyright (C) 2005-2014 Dell Inc. */ #define pr_fmt(fmt) … #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/backlight.h> #include <linux/err.h> #include <linux/dmi.h> #include <linux/io.h> #include <linux/rfkill.h> #include <linux/power_supply.h> #include <linux/sysfs.h> #include <linux/acpi.h> #include <linux/mm.h> #include <linux/i8042.h> #include <linux/debugfs.h> #include <linux/seq_file.h> #include <acpi/battery.h> #include <acpi/video.h> #include "dell-rbtn.h" #include "dell-smbios.h" #include "dell-wmi-privacy.h" struct quirk_entry { … }; static struct quirk_entry *quirks; static struct quirk_entry quirk_dell_vostro_v130 = …; static int __init dmi_matched(const struct dmi_system_id *dmi) { … } /* * These values come from Windows utility provided by Dell. If any other value * is used then BIOS silently set timeout to 0 without any error message. */ static struct quirk_entry quirk_dell_xps13_9333 = …; static struct quirk_entry quirk_dell_xps13_9370 = …; static struct quirk_entry quirk_dell_latitude_e6410 = …; static struct quirk_entry quirk_dell_inspiron_1012 = …; static struct quirk_entry quirk_dell_latitude_7520 = …; static struct platform_driver platform_driver = …; static struct platform_device *platform_device; static struct backlight_device *dell_backlight_device; static struct rfkill *wifi_rfkill; static struct rfkill *bluetooth_rfkill; static struct rfkill *wwan_rfkill; static bool force_rfkill; static bool micmute_led_registered; static bool mute_led_registered; struct battery_mode_info { … }; static const struct battery_mode_info battery_modes[] = …; static u32 battery_supported_modes; module_param(force_rfkill, bool, 0444); MODULE_PARM_DESC(…) …; static const struct dmi_system_id dell_device_table[] __initconst = …; MODULE_DEVICE_TABLE(dmi, dell_device_table); static const struct dmi_system_id dell_quirks[] __initconst = …; /* -1 is a sentinel value, telling us to use token->value */ #define USE_TVAL … static int dell_send_request_for_tokenid(struct calling_interface_buffer *buffer, u16 class, u16 select, u16 tokenid, u32 val) { … } static inline int dell_set_std_token_value(struct calling_interface_buffer *buffer, u16 tokenid, u32 value) { … } /* * Derived from information in smbios-wireless-ctl: * * cbSelect 17, Value 11 * * Return Wireless Info * cbArg1, byte0 = 0x00 * * cbRes1 Standard return codes (0, -1, -2) * cbRes2 Info bit flags: * * 0 Hardware switch supported (1) * 1 WiFi locator supported (1) * 2 WLAN supported (1) * 3 Bluetooth (BT) supported (1) * 4 WWAN supported (1) * 5 Wireless KBD supported (1) * 6 Uw b supported (1) * 7 WiGig supported (1) * 8 WLAN installed (1) * 9 BT installed (1) * 10 WWAN installed (1) * 11 Uw b installed (1) * 12 WiGig installed (1) * 13-15 Reserved (0) * 16 Hardware (HW) switch is On (1) * 17 WLAN disabled (1) * 18 BT disabled (1) * 19 WWAN disabled (1) * 20 Uw b disabled (1) * 21 WiGig disabled (1) * 20-31 Reserved (0) * * cbRes3 NVRAM size in bytes * cbRes4, byte 0 NVRAM format version number * * * Set QuickSet Radio Disable Flag * cbArg1, byte0 = 0x01 * cbArg1, byte1 * Radio ID value: * 0 Radio Status * 1 WLAN ID * 2 BT ID * 3 WWAN ID * 4 UWB ID * 5 WIGIG ID * cbArg1, byte2 Flag bits: * 0 QuickSet disables radio (1) * 1-7 Reserved (0) * * cbRes1 Standard return codes (0, -1, -2) * cbRes2 QuickSet (QS) radio disable bit map: * 0 QS disables WLAN * 1 QS disables BT * 2 QS disables WWAN * 3 QS disables UWB * 4 QS disables WIGIG * 5-31 Reserved (0) * * Wireless Switch Configuration * cbArg1, byte0 = 0x02 * * cbArg1, byte1 * Subcommand: * 0 Get config * 1 Set config * 2 Set WiFi locator enable/disable * cbArg1,byte2 * Switch settings (if byte 1==1): * 0 WLAN sw itch control (1) * 1 BT sw itch control (1) * 2 WWAN sw itch control (1) * 3 UWB sw itch control (1) * 4 WiGig sw itch control (1) * 5-7 Reserved (0) * cbArg1, byte2 Enable bits (if byte 1==2): * 0 Enable WiFi locator (1) * * cbRes1 Standard return codes (0, -1, -2) * cbRes2 QuickSet radio disable bit map: * 0 WLAN controlled by sw itch (1) * 1 BT controlled by sw itch (1) * 2 WWAN controlled by sw itch (1) * 3 UWB controlled by sw itch (1) * 4 WiGig controlled by sw itch (1) * 5-6 Reserved (0) * 7 Wireless sw itch config locked (1) * 8 WiFi locator enabled (1) * 9-14 Reserved (0) * 15 WiFi locator setting locked (1) * 16-31 Reserved (0) * * Read Local Config Data (LCD) * cbArg1, byte0 = 0x10 * cbArg1, byte1 NVRAM index low byte * cbArg1, byte2 NVRAM index high byte * cbRes1 Standard return codes (0, -1, -2) * cbRes2 4 bytes read from LCD[index] * cbRes3 4 bytes read from LCD[index+4] * cbRes4 4 bytes read from LCD[index+8] * * Write Local Config Data (LCD) * cbArg1, byte0 = 0x11 * cbArg1, byte1 NVRAM index low byte * cbArg1, byte2 NVRAM index high byte * cbArg2 4 bytes to w rite at LCD[index] * cbArg3 4 bytes to w rite at LCD[index+4] * cbArg4 4 bytes to w rite at LCD[index+8] * cbRes1 Standard return codes (0, -1, -2) * * Populate Local Config Data from NVRAM * cbArg1, byte0 = 0x12 * cbRes1 Standard return codes (0, -1, -2) * * Commit Local Config Data to NVRAM * cbArg1, byte0 = 0x13 * cbRes1 Standard return codes (0, -1, -2) */ static int dell_rfkill_set(void *data, bool blocked) { … } static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio, int status) { … } static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio, int status, int hwswitch) { … } static void dell_rfkill_query(struct rfkill *rfkill, void *data) { … } static const struct rfkill_ops dell_rfkill_ops = …; static struct dentry *dell_laptop_dir; static int dell_debugfs_show(struct seq_file *s, void *data) { … } DEFINE_SHOW_ATTRIBUTE(…); static void dell_update_rfkill(struct work_struct *ignored) { … } static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, struct serio *port) { … } static int (*dell_rbtn_notifier_register_func)(struct notifier_block *); static int (*dell_rbtn_notifier_unregister_func)(struct notifier_block *); static int dell_laptop_rbtn_notifier_call(struct notifier_block *nb, unsigned long action, void *data) { … } static struct notifier_block dell_laptop_rbtn_notifier = …; static int __init dell_setup_rfkill(void) { … } static void dell_cleanup_rfkill(void) { … } static int dell_send_intensity(struct backlight_device *bd) { … } static int dell_get_intensity(struct backlight_device *bd) { … } static const struct backlight_ops dell_ops = …; static void touchpad_led_on(void) { … } static void touchpad_led_off(void) { … } static void touchpad_led_set(struct led_classdev *led_cdev, enum led_brightness value) { … } static struct led_classdev touchpad_led = …; static int __init touchpad_led_init(struct device *dev) { … } static void touchpad_led_exit(void) { … } /* * Derived from information in smbios-keyboard-ctl: * * cbClass 4 * cbSelect 11 * Keyboard illumination * cbArg1 determines the function to be performed * * cbArg1 0x0 = Get Feature Information * cbRES1 Standard return codes (0, -1, -2) * cbRES2, word0 Bitmap of user-selectable modes * bit 0 Always off (All systems) * bit 1 Always on (Travis ATG, Siberia) * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG) * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off * bit 4 Auto: Input-activity-based On; input-activity based Off * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off * bits 9-15 Reserved for future use * cbRES2, byte2 Reserved for future use * cbRES2, byte3 Keyboard illumination type * 0 Reserved * 1 Tasklight * 2 Backlight * 3-255 Reserved for future use * cbRES3, byte0 Supported auto keyboard illumination trigger bitmap. * bit 0 Any keystroke * bit 1 Touchpad activity * bit 2 Pointing stick * bit 3 Any mouse * bits 4-7 Reserved for future use * cbRES3, byte1 Supported timeout unit bitmap * bit 0 Seconds * bit 1 Minutes * bit 2 Hours * bit 3 Days * bits 4-7 Reserved for future use * cbRES3, byte2 Number of keyboard light brightness levels * cbRES4, byte0 Maximum acceptable seconds value (0 if seconds not supported). * cbRES4, byte1 Maximum acceptable minutes value (0 if minutes not supported). * cbRES4, byte2 Maximum acceptable hours value (0 if hours not supported). * cbRES4, byte3 Maximum acceptable days value (0 if days not supported) * * cbArg1 0x1 = Get Current State * cbRES1 Standard return codes (0, -1, -2) * cbRES2, word0 Bitmap of current mode state * bit 0 Always off (All systems) * bit 1 Always on (Travis ATG, Siberia) * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG) * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off * bit 4 Auto: Input-activity-based On; input-activity based Off * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off * bits 9-15 Reserved for future use * Note: Only One bit can be set * cbRES2, byte2 Currently active auto keyboard illumination triggers. * bit 0 Any keystroke * bit 1 Touchpad activity * bit 2 Pointing stick * bit 3 Any mouse * bits 4-7 Reserved for future use * cbRES2, byte3 Current Timeout on battery * bits 7:6 Timeout units indicator: * 00b Seconds * 01b Minutes * 10b Hours * 11b Days * bits 5:0 Timeout value (0-63) in sec/min/hr/day * NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte * are set upon return from the [Get feature information] call. * cbRES3, byte0 Current setting of ALS value that turns the light on or off. * cbRES3, byte1 Current ALS reading * cbRES3, byte2 Current keyboard light level. * cbRES3, byte3 Current timeout on AC Power * bits 7:6 Timeout units indicator: * 00b Seconds * 01b Minutes * 10b Hours * 11b Days * Bits 5:0 Timeout value (0-63) in sec/min/hr/day * NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte2 * are set upon return from the upon return from the [Get Feature information] call. * * cbArg1 0x2 = Set New State * cbRES1 Standard return codes (0, -1, -2) * cbArg2, word0 Bitmap of current mode state * bit 0 Always off (All systems) * bit 1 Always on (Travis ATG, Siberia) * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG) * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off * bit 4 Auto: Input-activity-based On; input-activity based Off * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off * bits 9-15 Reserved for future use * Note: Only One bit can be set * cbArg2, byte2 Desired auto keyboard illumination triggers. Must remain inactive to allow * keyboard to turn off automatically. * bit 0 Any keystroke * bit 1 Touchpad activity * bit 2 Pointing stick * bit 3 Any mouse * bits 4-7 Reserved for future use * cbArg2, byte3 Desired Timeout on battery * bits 7:6 Timeout units indicator: * 00b Seconds * 01b Minutes * 10b Hours * 11b Days * bits 5:0 Timeout value (0-63) in sec/min/hr/day * cbArg3, byte0 Desired setting of ALS value that turns the light on or off. * cbArg3, byte2 Desired keyboard light level. * cbArg3, byte3 Desired Timeout on AC power * bits 7:6 Timeout units indicator: * 00b Seconds * 01b Minutes * 10b Hours * 11b Days * bits 5:0 Timeout value (0-63) in sec/min/hr/day */ enum kbd_timeout_unit { … }; enum kbd_mode_bit { … }; #define kbd_is_als_mode_bit(bit) … #define kbd_is_trigger_mode_bit(bit) … #define kbd_is_level_mode_bit(bit) … struct kbd_info { … }; struct kbd_state { … }; static const int kbd_tokens[] = …; static u16 kbd_token_bits; static struct kbd_info kbd_info; static bool kbd_als_supported; static bool kbd_triggers_supported; static bool kbd_timeout_ac_supported; static u8 kbd_mode_levels[16]; static int kbd_mode_levels_count; static u8 kbd_previous_level; static u8 kbd_previous_mode_bit; static bool kbd_led_present; static DEFINE_MUTEX(kbd_led_mutex); static enum led_brightness kbd_led_level; /* * NOTE: there are three ways to set the keyboard backlight level. * First, via kbd_state.mode_bit (assigning KBD_MODE_BIT_TRIGGER_* value). * Second, via kbd_state.level (assigning numerical value <= kbd_info.levels). * Third, via SMBIOS tokens (KBD_LED_* in kbd_tokens) * * There are laptops which support only one of these methods. If we want to * support as many machines as possible we need to implement all three methods. * The first two methods use the kbd_state structure. The third uses SMBIOS * tokens. If kbd_info.levels == 0, the machine does not support setting the * keyboard backlight level via kbd_state.level. */ static int kbd_get_info(struct kbd_info *info) { … } static unsigned int kbd_get_max_level(void) { … } static int kbd_get_level(struct kbd_state *state) { … } static int kbd_set_level(struct kbd_state *state, u8 level) { … } static int kbd_get_state(struct kbd_state *state) { … } static int kbd_set_state(struct kbd_state *state) { … } static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old) { … } static int kbd_set_token_bit(u8 bit) { … } static int kbd_get_token_bit(u8 bit) { … } static int kbd_get_first_active_token_bit(void) { … } static int kbd_get_valid_token_counts(void) { … } static inline int kbd_init_info(void) { … } static inline void __init kbd_init_tokens(void) { … } static void __init kbd_init(void) { … } static ssize_t kbd_led_timeout_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static ssize_t kbd_led_timeout_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR(stop_timeout, S_IRUGO | S_IWUSR, kbd_led_timeout_show, kbd_led_timeout_store); static const char * const kbd_led_triggers[] = …; static ssize_t kbd_led_triggers_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static ssize_t kbd_led_triggers_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR(start_triggers, S_IRUGO | S_IWUSR, kbd_led_triggers_show, kbd_led_triggers_store); static ssize_t kbd_led_als_enabled_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static ssize_t kbd_led_als_enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR(als_enabled, S_IRUGO | S_IWUSR, kbd_led_als_enabled_show, kbd_led_als_enabled_store); static ssize_t kbd_led_als_setting_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static ssize_t kbd_led_als_setting_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR(als_setting, S_IRUGO | S_IWUSR, kbd_led_als_setting_show, kbd_led_als_setting_store); static struct attribute *kbd_led_attrs[] = …; static const struct attribute_group kbd_led_group = …; static struct attribute *kbd_led_als_attrs[] = …; static const struct attribute_group kbd_led_als_group = …; static const struct attribute_group *kbd_led_groups[] = …; static enum led_brightness kbd_led_level_get(struct led_classdev *led_cdev) { … } static int kbd_led_level_set(struct led_classdev *led_cdev, enum led_brightness value) { … } static struct led_classdev kbd_led = …; static int __init kbd_led_init(struct device *dev) { … } static void brightness_set_exit(struct led_classdev *led_cdev, enum led_brightness value) { /* Don't change backlight level on exit */ }; static void kbd_led_exit(void) { … } static int dell_laptop_notifier_call(struct notifier_block *nb, unsigned long action, void *data) { … } static struct notifier_block dell_laptop_notifier = …; static int micmute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) { … } static struct led_classdev micmute_led_cdev = …; static int mute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) { … } static struct led_classdev mute_led_cdev = …; static int dell_battery_set_mode(const u16 tokenid) { … } static int dell_battery_read(const u16 tokenid) { … } static bool dell_battery_mode_is_active(const u16 tokenid) { … } /* * The rules: the minimum start charging value is 50%. The maximum * start charging value is 95%. The minimum end charging value is * 55%. The maximum end charging value is 100%. And finally, there * has to be at least a 5% difference between start & end values. */ #define CHARGE_START_MIN … #define CHARGE_START_MAX … #define CHARGE_END_MIN … #define CHARGE_END_MAX … #define CHARGE_MIN_DIFF … static int dell_battery_set_custom_charge_start(int start) { … } static int dell_battery_set_custom_charge_end(int end) { … } static ssize_t charge_types_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t charge_types_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { … } static ssize_t charge_control_start_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t charge_control_start_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { … } static ssize_t charge_control_end_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t charge_control_end_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { … } static DEVICE_ATTR_RW(charge_control_start_threshold); static DEVICE_ATTR_RW(charge_control_end_threshold); static DEVICE_ATTR_RW(charge_types); static struct attribute *dell_battery_attrs[] = …; ATTRIBUTE_GROUPS(…); static bool dell_battery_supported(struct power_supply *battery) { … } static int dell_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) { … } static int dell_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) { … } static struct acpi_battery_hook dell_battery_hook = …; static u32 __init battery_get_supported_modes(void) { … } static void __init dell_battery_init(struct device *dev) { … } static void dell_battery_exit(void) { … } static int __init dell_init(void) { … } static void __exit dell_exit(void) { … } /* dell-rbtn.c driver export functions which will not work correctly (and could * cause kernel crash) if they are called before dell-rbtn.c init code. This is * not problem when dell-rbtn.c is compiled as external module. When both files * (dell-rbtn.c and dell-laptop.c) are compiled statically into kernel, then we * need to ensure that dell_init() will be called after initializing dell-rbtn. * This can be achieved by late_initcall() instead module_init(). */ late_initcall(dell_init); module_exit(dell_exit); MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;