162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Dell WMI hotkeys 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Red Hat <mjg@redhat.com> 662306a36Sopenharmony_ci * Copyright (C) 2014-2015 Pali Rohár <pali@kernel.org> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Portions based on wistron_btns.c: 962306a36Sopenharmony_ci * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> 1062306a36Sopenharmony_ci * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> 1162306a36Sopenharmony_ci * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/types.h> 2162306a36Sopenharmony_ci#include <linux/input.h> 2262306a36Sopenharmony_ci#include <linux/input/sparse-keymap.h> 2362306a36Sopenharmony_ci#include <linux/acpi.h> 2462306a36Sopenharmony_ci#include <linux/string.h> 2562306a36Sopenharmony_ci#include <linux/dmi.h> 2662306a36Sopenharmony_ci#include <linux/wmi.h> 2762306a36Sopenharmony_ci#include <acpi/video.h> 2862306a36Sopenharmony_ci#include "dell-smbios.h" 2962306a36Sopenharmony_ci#include "dell-wmi-descriptor.h" 3062306a36Sopenharmony_ci#include "dell-wmi-privacy.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciMODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 3362306a36Sopenharmony_ciMODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); 3462306a36Sopenharmony_ciMODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); 3562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic bool wmi_requires_smbios_request; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct dell_wmi_priv { 4262306a36Sopenharmony_ci struct input_dev *input_dev; 4362306a36Sopenharmony_ci struct input_dev *tabletswitch_dev; 4462306a36Sopenharmony_ci u32 interface_version; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int __init dmi_matched(const struct dmi_system_id *dmi) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci wmi_requires_smbios_request = 1; 5062306a36Sopenharmony_ci return 1; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic const struct dmi_system_id dell_wmi_smbios_list[] __initconst = { 5462306a36Sopenharmony_ci { 5562306a36Sopenharmony_ci .callback = dmi_matched, 5662306a36Sopenharmony_ci .ident = "Dell Inspiron M5110", 5762306a36Sopenharmony_ci .matches = { 5862306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 5962306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), 6062306a36Sopenharmony_ci }, 6162306a36Sopenharmony_ci }, 6262306a36Sopenharmony_ci { 6362306a36Sopenharmony_ci .callback = dmi_matched, 6462306a36Sopenharmony_ci .ident = "Dell Vostro V131", 6562306a36Sopenharmony_ci .matches = { 6662306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 6762306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), 6862306a36Sopenharmony_ci }, 6962306a36Sopenharmony_ci }, 7062306a36Sopenharmony_ci { } 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* 7462306a36Sopenharmony_ci * Keymap for WMI events of type 0x0000 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * Certain keys are flagged as KE_IGNORE. All of these are either 7762306a36Sopenharmony_ci * notifications (rather than requests for change) or are also sent 7862306a36Sopenharmony_ci * via the keyboard controller so should not be sent again. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic const struct key_entry dell_wmi_keymap_type_0000[] = { 8162306a36Sopenharmony_ci { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } }, 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Key code is followed by brightness level */ 8462306a36Sopenharmony_ci { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } }, 8562306a36Sopenharmony_ci { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } }, 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Battery health status button */ 8862306a36Sopenharmony_ci { KE_KEY, 0xe007, { KEY_BATTERY } }, 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Radio devices state change, key code is followed by other values */ 9162306a36Sopenharmony_ci { KE_IGNORE, 0xe008, { KEY_RFKILL } }, 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci { KE_KEY, 0xe009, { KEY_EJECTCD } }, 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Key code is followed by: next, active and attached devices */ 9662306a36Sopenharmony_ci { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } }, 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Key code is followed by keyboard illumination level */ 9962306a36Sopenharmony_ci { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } }, 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* BIOS error detected */ 10262306a36Sopenharmony_ci { KE_IGNORE, 0xe00d, { KEY_RESERVED } }, 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Battery was removed or inserted */ 10562306a36Sopenharmony_ci { KE_IGNORE, 0xe00e, { KEY_RESERVED } }, 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Wifi Catcher */ 10862306a36Sopenharmony_ci { KE_KEY, 0xe011, { KEY_WLAN } }, 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Ambient light sensor toggle */ 11162306a36Sopenharmony_ci { KE_IGNORE, 0xe013, { KEY_RESERVED } }, 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci { KE_IGNORE, 0xe020, { KEY_MUTE } }, 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Unknown, defined in ACPI DSDT */ 11662306a36Sopenharmony_ci /* { KE_IGNORE, 0xe023, { KEY_RESERVED } }, */ 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Untested, Dell Instant Launch key on Inspiron 7520 */ 11962306a36Sopenharmony_ci /* { KE_IGNORE, 0xe024, { KEY_RESERVED } }, */ 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Dell Instant Launch key */ 12262306a36Sopenharmony_ci { KE_KEY, 0xe025, { KEY_PROG4 } }, 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Audio panel key */ 12562306a36Sopenharmony_ci { KE_IGNORE, 0xe026, { KEY_RESERVED } }, 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* LCD Display On/Off Control key */ 12862306a36Sopenharmony_ci { KE_KEY, 0xe027, { KEY_DISPLAYTOGGLE } }, 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Untested, Multimedia key on Dell Vostro 3560 */ 13162306a36Sopenharmony_ci /* { KE_IGNORE, 0xe028, { KEY_RESERVED } }, */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Dell Instant Launch key */ 13462306a36Sopenharmony_ci { KE_KEY, 0xe029, { KEY_PROG4 } }, 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Untested, Windows Mobility Center button on Inspiron 7520 */ 13762306a36Sopenharmony_ci /* { KE_IGNORE, 0xe02a, { KEY_RESERVED } }, */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Unknown, defined in ACPI DSDT */ 14062306a36Sopenharmony_ci /* { KE_IGNORE, 0xe02b, { KEY_RESERVED } }, */ 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* Untested, Dell Audio With Preset Switch button on Inspiron 7520 */ 14362306a36Sopenharmony_ci /* { KE_IGNORE, 0xe02c, { KEY_RESERVED } }, */ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } }, 14662306a36Sopenharmony_ci { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } }, 14762306a36Sopenharmony_ci { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } }, 14862306a36Sopenharmony_ci { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } }, 14962306a36Sopenharmony_ci { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } }, 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* NIC Link is Up */ 15262306a36Sopenharmony_ci { KE_IGNORE, 0xe043, { KEY_RESERVED } }, 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* NIC Link is Down */ 15562306a36Sopenharmony_ci { KE_IGNORE, 0xe044, { KEY_RESERVED } }, 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* 15862306a36Sopenharmony_ci * This entry is very suspicious! 15962306a36Sopenharmony_ci * Originally Matthew Garrett created this dell-wmi driver specially for 16062306a36Sopenharmony_ci * "button with a picture of a battery" which has event code 0xe045. 16162306a36Sopenharmony_ci * Later Mario Limonciello from Dell told us that event code 0xe045 is 16262306a36Sopenharmony_ci * reported by Num Lock and should be ignored because key is send also 16362306a36Sopenharmony_ci * by keyboard controller. 16462306a36Sopenharmony_ci * So for now we will ignore this event to prevent potential double 16562306a36Sopenharmony_ci * Num Lock key press. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci { KE_IGNORE, 0xe045, { KEY_NUMLOCK } }, 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Scroll lock and also going to tablet mode on portable devices */ 17062306a36Sopenharmony_ci { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } }, 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* Untested, going from tablet mode on portable devices */ 17362306a36Sopenharmony_ci /* { KE_IGNORE, 0xe047, { KEY_RESERVED } }, */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Dell Support Center key */ 17662306a36Sopenharmony_ci { KE_IGNORE, 0xe06e, { KEY_RESERVED } }, 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci { KE_IGNORE, 0xe0f7, { KEY_MUTE } }, 17962306a36Sopenharmony_ci { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } }, 18062306a36Sopenharmony_ci { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } }, 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistruct dell_bios_keymap_entry { 18462306a36Sopenharmony_ci u16 scancode; 18562306a36Sopenharmony_ci u16 keycode; 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistruct dell_bios_hotkey_table { 18962306a36Sopenharmony_ci struct dmi_header header; 19062306a36Sopenharmony_ci struct dell_bios_keymap_entry keymap[]; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistruct dell_dmi_results { 19562306a36Sopenharmony_ci int err; 19662306a36Sopenharmony_ci int keymap_size; 19762306a36Sopenharmony_ci struct key_entry *keymap; 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* Uninitialized entries here are KEY_RESERVED == 0. */ 20162306a36Sopenharmony_cistatic const u16 bios_to_linux_keycode[256] = { 20262306a36Sopenharmony_ci [0] = KEY_MEDIA, 20362306a36Sopenharmony_ci [1] = KEY_NEXTSONG, 20462306a36Sopenharmony_ci [2] = KEY_PLAYPAUSE, 20562306a36Sopenharmony_ci [3] = KEY_PREVIOUSSONG, 20662306a36Sopenharmony_ci [4] = KEY_STOPCD, 20762306a36Sopenharmony_ci [5] = KEY_UNKNOWN, 20862306a36Sopenharmony_ci [6] = KEY_UNKNOWN, 20962306a36Sopenharmony_ci [7] = KEY_UNKNOWN, 21062306a36Sopenharmony_ci [8] = KEY_WWW, 21162306a36Sopenharmony_ci [9] = KEY_UNKNOWN, 21262306a36Sopenharmony_ci [10] = KEY_VOLUMEDOWN, 21362306a36Sopenharmony_ci [11] = KEY_MUTE, 21462306a36Sopenharmony_ci [12] = KEY_VOLUMEUP, 21562306a36Sopenharmony_ci [13] = KEY_UNKNOWN, 21662306a36Sopenharmony_ci [14] = KEY_BATTERY, 21762306a36Sopenharmony_ci [15] = KEY_EJECTCD, 21862306a36Sopenharmony_ci [16] = KEY_UNKNOWN, 21962306a36Sopenharmony_ci [17] = KEY_SLEEP, 22062306a36Sopenharmony_ci [18] = KEY_PROG1, 22162306a36Sopenharmony_ci [19] = KEY_BRIGHTNESSDOWN, 22262306a36Sopenharmony_ci [20] = KEY_BRIGHTNESSUP, 22362306a36Sopenharmony_ci [21] = KEY_BRIGHTNESS_AUTO, 22462306a36Sopenharmony_ci [22] = KEY_KBDILLUMTOGGLE, 22562306a36Sopenharmony_ci [23] = KEY_UNKNOWN, 22662306a36Sopenharmony_ci [24] = KEY_SWITCHVIDEOMODE, 22762306a36Sopenharmony_ci [25] = KEY_UNKNOWN, 22862306a36Sopenharmony_ci [26] = KEY_UNKNOWN, 22962306a36Sopenharmony_ci [27] = KEY_SWITCHVIDEOMODE, 23062306a36Sopenharmony_ci [28] = KEY_UNKNOWN, 23162306a36Sopenharmony_ci [29] = KEY_UNKNOWN, 23262306a36Sopenharmony_ci [30] = KEY_PROG2, 23362306a36Sopenharmony_ci [31] = KEY_UNKNOWN, 23462306a36Sopenharmony_ci [32] = KEY_UNKNOWN, 23562306a36Sopenharmony_ci [33] = KEY_UNKNOWN, 23662306a36Sopenharmony_ci [34] = KEY_UNKNOWN, 23762306a36Sopenharmony_ci [35] = KEY_UNKNOWN, 23862306a36Sopenharmony_ci [36] = KEY_UNKNOWN, 23962306a36Sopenharmony_ci [37] = KEY_UNKNOWN, 24062306a36Sopenharmony_ci [38] = KEY_MICMUTE, 24162306a36Sopenharmony_ci [255] = KEY_PROG3, 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/* 24562306a36Sopenharmony_ci * Keymap for WMI events of type 0x0010 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * These are applied if the 0xB2 DMI hotkey table is present and doesn't 24862306a36Sopenharmony_ci * override them. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cistatic const struct key_entry dell_wmi_keymap_type_0010[] = { 25162306a36Sopenharmony_ci /* Fn-lock switched to function keys */ 25262306a36Sopenharmony_ci { KE_IGNORE, 0x0, { KEY_RESERVED } }, 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Fn-lock switched to multimedia keys */ 25562306a36Sopenharmony_ci { KE_IGNORE, 0x1, { KEY_RESERVED } }, 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Keyboard backlight change notification */ 25862306a36Sopenharmony_ci { KE_IGNORE, 0x3f, { KEY_RESERVED } }, 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Backlight brightness level */ 26162306a36Sopenharmony_ci { KE_KEY, 0x57, { KEY_BRIGHTNESSDOWN } }, 26262306a36Sopenharmony_ci { KE_KEY, 0x58, { KEY_BRIGHTNESSUP } }, 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /*Speaker Mute*/ 26562306a36Sopenharmony_ci { KE_KEY, 0x109, { KEY_MUTE} }, 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Mic mute */ 26862306a36Sopenharmony_ci { KE_KEY, 0x150, { KEY_MICMUTE } }, 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Fn-lock */ 27162306a36Sopenharmony_ci { KE_IGNORE, 0x151, { KEY_RESERVED } }, 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Change keyboard illumination */ 27462306a36Sopenharmony_ci { KE_IGNORE, 0x152, { KEY_KBDILLUMTOGGLE } }, 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* 27762306a36Sopenharmony_ci * Radio disable (notify only -- there is no model for which the 27862306a36Sopenharmony_ci * WMI event is supposed to trigger an action). 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci { KE_IGNORE, 0x153, { KEY_RFKILL } }, 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* RGB keyboard backlight control */ 28362306a36Sopenharmony_ci { KE_IGNORE, 0x154, { KEY_RESERVED } }, 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* 28662306a36Sopenharmony_ci * Stealth mode toggle. This will "disable all lights and sounds". 28762306a36Sopenharmony_ci * The action is performed by the BIOS and EC; the WMI event is just 28862306a36Sopenharmony_ci * a notification. On the XPS 13 9350, this is Fn+F7, and there's 28962306a36Sopenharmony_ci * a BIOS setting to enable and disable the hotkey. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci { KE_IGNORE, 0x155, { KEY_RESERVED } }, 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* Rugged magnetic dock attach/detach events */ 29462306a36Sopenharmony_ci { KE_IGNORE, 0x156, { KEY_RESERVED } }, 29562306a36Sopenharmony_ci { KE_IGNORE, 0x157, { KEY_RESERVED } }, 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Rugged programmable (P1/P2/P3 keys) */ 29862306a36Sopenharmony_ci { KE_KEY, 0x850, { KEY_PROG1 } }, 29962306a36Sopenharmony_ci { KE_KEY, 0x851, { KEY_PROG2 } }, 30062306a36Sopenharmony_ci { KE_KEY, 0x852, { KEY_PROG3 } }, 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* 30362306a36Sopenharmony_ci * Radio disable (notify only -- there is no model for which the 30462306a36Sopenharmony_ci * WMI event is supposed to trigger an action). 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci { KE_IGNORE, 0xe008, { KEY_RFKILL } }, 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Fn-lock */ 30962306a36Sopenharmony_ci { KE_IGNORE, 0xe035, { KEY_RESERVED } }, 31062306a36Sopenharmony_ci}; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/* 31362306a36Sopenharmony_ci * Keymap for WMI events of type 0x0011 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_cistatic const struct key_entry dell_wmi_keymap_type_0011[] = { 31662306a36Sopenharmony_ci /* Reflex keyboard switch on 2n1 devices */ 31762306a36Sopenharmony_ci { KE_IGNORE, 0xe070, { KEY_RESERVED } }, 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Battery unplugged */ 32062306a36Sopenharmony_ci { KE_IGNORE, 0xfff0, { KEY_RESERVED } }, 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* Battery inserted */ 32362306a36Sopenharmony_ci { KE_IGNORE, 0xfff1, { KEY_RESERVED } }, 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* 32662306a36Sopenharmony_ci * Detachable keyboard detached / undocked 32762306a36Sopenharmony_ci * Note SW_TABLET_MODE is already reported through the intel_vbtn 32862306a36Sopenharmony_ci * driver for this, so we ignore it. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci { KE_IGNORE, 0xfff2, { KEY_RESERVED } }, 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* Detachable keyboard attached / docked */ 33362306a36Sopenharmony_ci { KE_IGNORE, 0xfff3, { KEY_RESERVED } }, 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Keyboard backlight level changed */ 33662306a36Sopenharmony_ci { KE_IGNORE, KBD_LED_OFF_TOKEN, { KEY_RESERVED } }, 33762306a36Sopenharmony_ci { KE_IGNORE, KBD_LED_ON_TOKEN, { KEY_RESERVED } }, 33862306a36Sopenharmony_ci { KE_IGNORE, KBD_LED_AUTO_TOKEN, { KEY_RESERVED } }, 33962306a36Sopenharmony_ci { KE_IGNORE, KBD_LED_AUTO_25_TOKEN, { KEY_RESERVED } }, 34062306a36Sopenharmony_ci { KE_IGNORE, KBD_LED_AUTO_50_TOKEN, { KEY_RESERVED } }, 34162306a36Sopenharmony_ci { KE_IGNORE, KBD_LED_AUTO_75_TOKEN, { KEY_RESERVED } }, 34262306a36Sopenharmony_ci { KE_IGNORE, KBD_LED_AUTO_100_TOKEN, { KEY_RESERVED } }, 34362306a36Sopenharmony_ci}; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci * Keymap for WMI events of type 0x0012 34762306a36Sopenharmony_ci * They are events with extended data 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_cistatic const struct key_entry dell_wmi_keymap_type_0012[] = { 35062306a36Sopenharmony_ci /* Backlight brightness change event */ 35162306a36Sopenharmony_ci { KE_IGNORE, 0x0003, { KEY_RESERVED } }, 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* Ultra-performance mode switch request */ 35462306a36Sopenharmony_ci { KE_IGNORE, 0x000d, { KEY_RESERVED } }, 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Fn-lock button pressed */ 35762306a36Sopenharmony_ci { KE_IGNORE, 0xe035, { KEY_RESERVED } }, 35862306a36Sopenharmony_ci}; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void dell_wmi_switch_event(struct input_dev **subdev, 36162306a36Sopenharmony_ci const char *devname, 36262306a36Sopenharmony_ci int switchid, 36362306a36Sopenharmony_ci int value) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci if (!*subdev) { 36662306a36Sopenharmony_ci struct input_dev *dev = input_allocate_device(); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (!dev) { 36962306a36Sopenharmony_ci pr_warn("could not allocate device for %s\n", devname); 37062306a36Sopenharmony_ci return; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci __set_bit(EV_SW, (dev)->evbit); 37362306a36Sopenharmony_ci __set_bit(switchid, (dev)->swbit); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci (dev)->name = devname; 37662306a36Sopenharmony_ci (dev)->id.bustype = BUS_HOST; 37762306a36Sopenharmony_ci if (input_register_device(dev)) { 37862306a36Sopenharmony_ci input_free_device(dev); 37962306a36Sopenharmony_ci pr_warn("could not register device for %s\n", devname); 38062306a36Sopenharmony_ci return; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci *subdev = dev; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci input_report_switch(*subdev, switchid, value); 38662306a36Sopenharmony_ci input_sync(*subdev); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16 *buffer, int remaining) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); 39262306a36Sopenharmony_ci const struct key_entry *key; 39362306a36Sopenharmony_ci int used = 0; 39462306a36Sopenharmony_ci int value = 1; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci key = sparse_keymap_entry_from_scancode(priv->input_dev, 39762306a36Sopenharmony_ci (type << 16) | code); 39862306a36Sopenharmony_ci if (!key) { 39962306a36Sopenharmony_ci pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n", 40062306a36Sopenharmony_ci type, code); 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci pr_debug("Key with type 0x%04x and code 0x%04x pressed\n", type, code); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Don't report brightness notifications that will also come via ACPI */ 40762306a36Sopenharmony_ci if ((key->keycode == KEY_BRIGHTNESSUP || 40862306a36Sopenharmony_ci key->keycode == KEY_BRIGHTNESSDOWN) && 40962306a36Sopenharmony_ci acpi_video_handles_brightness_key_presses()) 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request) 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (key->keycode == KEY_KBDILLUMTOGGLE) { 41662306a36Sopenharmony_ci dell_laptop_call_notifier( 41762306a36Sopenharmony_ci DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED, NULL); 41862306a36Sopenharmony_ci } else if (type == 0x0011 && code == 0xe070 && remaining > 0) { 41962306a36Sopenharmony_ci dell_wmi_switch_event(&priv->tabletswitch_dev, 42062306a36Sopenharmony_ci "Dell tablet mode switch", 42162306a36Sopenharmony_ci SW_TABLET_MODE, !buffer[0]); 42262306a36Sopenharmony_ci return 1; 42362306a36Sopenharmony_ci } else if (type == 0x0012 && code == 0x000d && remaining > 0) { 42462306a36Sopenharmony_ci value = (buffer[2] == 2); 42562306a36Sopenharmony_ci used = 1; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci sparse_keymap_report_entry(priv->input_dev, key, value, true); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return used; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic void dell_wmi_notify(struct wmi_device *wdev, 43462306a36Sopenharmony_ci union acpi_object *obj) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); 43762306a36Sopenharmony_ci u16 *buffer_entry, *buffer_end; 43862306a36Sopenharmony_ci acpi_size buffer_size; 43962306a36Sopenharmony_ci int len, i; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (obj->type != ACPI_TYPE_BUFFER) { 44262306a36Sopenharmony_ci pr_warn("bad response type %x\n", obj->type); 44362306a36Sopenharmony_ci return; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci pr_debug("Received WMI event (%*ph)\n", 44762306a36Sopenharmony_ci obj->buffer.length, obj->buffer.pointer); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci buffer_entry = (u16 *)obj->buffer.pointer; 45062306a36Sopenharmony_ci buffer_size = obj->buffer.length/2; 45162306a36Sopenharmony_ci buffer_end = buffer_entry + buffer_size; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* 45462306a36Sopenharmony_ci * BIOS/ACPI on devices with WMI interface version 0 does not clear 45562306a36Sopenharmony_ci * buffer before filling it. So next time when BIOS/ACPI send WMI event 45662306a36Sopenharmony_ci * which is smaller as previous then it contains garbage in buffer from 45762306a36Sopenharmony_ci * previous event. 45862306a36Sopenharmony_ci * 45962306a36Sopenharmony_ci * BIOS/ACPI on devices with WMI interface version 1 clears buffer and 46062306a36Sopenharmony_ci * sometimes send more events in buffer at one call. 46162306a36Sopenharmony_ci * 46262306a36Sopenharmony_ci * So to prevent reading garbage from buffer we will process only first 46362306a36Sopenharmony_ci * one event on devices with WMI interface version 0. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci if (priv->interface_version == 0 && buffer_entry < buffer_end) 46662306a36Sopenharmony_ci if (buffer_end > buffer_entry + buffer_entry[0] + 1) 46762306a36Sopenharmony_ci buffer_end = buffer_entry + buffer_entry[0] + 1; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci while (buffer_entry < buffer_end) { 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci len = buffer_entry[0]; 47262306a36Sopenharmony_ci if (len == 0) 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci len++; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (buffer_entry + len > buffer_end) { 47862306a36Sopenharmony_ci pr_warn("Invalid length of WMI event\n"); 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci switch (buffer_entry[1]) { 48562306a36Sopenharmony_ci case 0x0000: /* One key pressed or event occurred */ 48662306a36Sopenharmony_ci if (len > 2) 48762306a36Sopenharmony_ci dell_wmi_process_key(wdev, buffer_entry[1], 48862306a36Sopenharmony_ci buffer_entry[2], 48962306a36Sopenharmony_ci buffer_entry + 3, 49062306a36Sopenharmony_ci len - 3); 49162306a36Sopenharmony_ci /* Extended data is currently ignored */ 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci case 0x0010: /* Sequence of keys pressed */ 49462306a36Sopenharmony_ci case 0x0011: /* Sequence of events occurred */ 49562306a36Sopenharmony_ci for (i = 2; i < len; ++i) 49662306a36Sopenharmony_ci i += dell_wmi_process_key(wdev, buffer_entry[1], 49762306a36Sopenharmony_ci buffer_entry[i], 49862306a36Sopenharmony_ci buffer_entry + i, 49962306a36Sopenharmony_ci len - i - 1); 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci case 0x0012: 50262306a36Sopenharmony_ci if ((len > 4) && dell_privacy_process_event(buffer_entry[1], buffer_entry[3], 50362306a36Sopenharmony_ci buffer_entry[4])) 50462306a36Sopenharmony_ci /* dell_privacy_process_event has handled the event */; 50562306a36Sopenharmony_ci else if (len > 2) 50662306a36Sopenharmony_ci dell_wmi_process_key(wdev, buffer_entry[1], buffer_entry[2], 50762306a36Sopenharmony_ci buffer_entry + 3, len - 3); 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci default: /* Unknown event */ 51062306a36Sopenharmony_ci pr_info("Unknown WMI event type 0x%x\n", 51162306a36Sopenharmony_ci (int)buffer_entry[1]); 51262306a36Sopenharmony_ci break; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci buffer_entry += len; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic bool have_scancode(u32 scancode, const struct key_entry *keymap, int len) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci int i; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci for (i = 0; i < len; i++) 52662306a36Sopenharmony_ci if (keymap[i].code == scancode) 52762306a36Sopenharmony_ci return true; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return false; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic void handle_dmi_entry(const struct dmi_header *dm, void *opaque) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct dell_dmi_results *results = opaque; 53562306a36Sopenharmony_ci struct dell_bios_hotkey_table *table; 53662306a36Sopenharmony_ci int hotkey_num, i, pos = 0; 53762306a36Sopenharmony_ci struct key_entry *keymap; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (results->err || results->keymap) 54062306a36Sopenharmony_ci return; /* We already found the hotkey table. */ 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* The Dell hotkey table is type 0xB2. Scan until we find it. */ 54362306a36Sopenharmony_ci if (dm->type != 0xb2) 54462306a36Sopenharmony_ci return; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci table = container_of(dm, struct dell_bios_hotkey_table, header); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci hotkey_num = (table->header.length - 54962306a36Sopenharmony_ci sizeof(struct dell_bios_hotkey_table)) / 55062306a36Sopenharmony_ci sizeof(struct dell_bios_keymap_entry); 55162306a36Sopenharmony_ci if (hotkey_num < 1) { 55262306a36Sopenharmony_ci /* 55362306a36Sopenharmony_ci * Historically, dell-wmi would ignore a DMI entry of 55462306a36Sopenharmony_ci * fewer than 7 bytes. Sizes between 4 and 8 bytes are 55562306a36Sopenharmony_ci * nonsensical (both the header and all entries are 4 55662306a36Sopenharmony_ci * bytes), so we approximate the old behavior by 55762306a36Sopenharmony_ci * ignoring tables with fewer than one entry. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci return; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci keymap = kcalloc(hotkey_num, sizeof(struct key_entry), GFP_KERNEL); 56362306a36Sopenharmony_ci if (!keymap) { 56462306a36Sopenharmony_ci results->err = -ENOMEM; 56562306a36Sopenharmony_ci return; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci for (i = 0; i < hotkey_num; i++) { 56962306a36Sopenharmony_ci const struct dell_bios_keymap_entry *bios_entry = 57062306a36Sopenharmony_ci &table->keymap[i]; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Uninitialized entries are 0 aka KEY_RESERVED. */ 57362306a36Sopenharmony_ci u16 keycode = (bios_entry->keycode < 57462306a36Sopenharmony_ci ARRAY_SIZE(bios_to_linux_keycode)) ? 57562306a36Sopenharmony_ci bios_to_linux_keycode[bios_entry->keycode] : 57662306a36Sopenharmony_ci (bios_entry->keycode == 0xffff ? KEY_UNKNOWN : KEY_RESERVED); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * Log if we find an entry in the DMI table that we don't 58062306a36Sopenharmony_ci * understand. If this happens, we should figure out what 58162306a36Sopenharmony_ci * the entry means and add it to bios_to_linux_keycode. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci if (keycode == KEY_RESERVED) { 58462306a36Sopenharmony_ci pr_info("firmware scancode 0x%x maps to unrecognized keycode 0x%x\n", 58562306a36Sopenharmony_ci bios_entry->scancode, bios_entry->keycode); 58662306a36Sopenharmony_ci continue; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (keycode == KEY_KBDILLUMTOGGLE) 59062306a36Sopenharmony_ci keymap[pos].type = KE_IGNORE; 59162306a36Sopenharmony_ci else 59262306a36Sopenharmony_ci keymap[pos].type = KE_KEY; 59362306a36Sopenharmony_ci keymap[pos].code = bios_entry->scancode; 59462306a36Sopenharmony_ci keymap[pos].keycode = keycode; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci pos++; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci results->keymap = keymap; 60062306a36Sopenharmony_ci results->keymap_size = pos; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int dell_wmi_input_setup(struct wmi_device *wdev) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); 60662306a36Sopenharmony_ci struct dell_dmi_results dmi_results = {}; 60762306a36Sopenharmony_ci struct key_entry *keymap; 60862306a36Sopenharmony_ci int err, i, pos = 0; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci priv->input_dev = input_allocate_device(); 61162306a36Sopenharmony_ci if (!priv->input_dev) 61262306a36Sopenharmony_ci return -ENOMEM; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci priv->input_dev->name = "Dell WMI hotkeys"; 61562306a36Sopenharmony_ci priv->input_dev->id.bustype = BUS_HOST; 61662306a36Sopenharmony_ci priv->input_dev->dev.parent = &wdev->dev; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (dmi_walk(handle_dmi_entry, &dmi_results)) { 61962306a36Sopenharmony_ci /* 62062306a36Sopenharmony_ci * Historically, dell-wmi ignored dmi_walk errors. A failure 62162306a36Sopenharmony_ci * is certainly surprising, but it probably just indicates 62262306a36Sopenharmony_ci * a very old laptop. 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_ci pr_warn("no DMI; using the old-style hotkey interface\n"); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (dmi_results.err) { 62862306a36Sopenharmony_ci err = dmi_results.err; 62962306a36Sopenharmony_ci goto err_free_dev; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci keymap = kcalloc(dmi_results.keymap_size + 63362306a36Sopenharmony_ci ARRAY_SIZE(dell_wmi_keymap_type_0000) + 63462306a36Sopenharmony_ci ARRAY_SIZE(dell_wmi_keymap_type_0010) + 63562306a36Sopenharmony_ci ARRAY_SIZE(dell_wmi_keymap_type_0011) + 63662306a36Sopenharmony_ci ARRAY_SIZE(dell_wmi_keymap_type_0012) + 63762306a36Sopenharmony_ci 1, 63862306a36Sopenharmony_ci sizeof(struct key_entry), GFP_KERNEL); 63962306a36Sopenharmony_ci if (!keymap) { 64062306a36Sopenharmony_ci kfree(dmi_results.keymap); 64162306a36Sopenharmony_ci err = -ENOMEM; 64262306a36Sopenharmony_ci goto err_free_dev; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* Append table with events of type 0x0010 which comes from DMI */ 64662306a36Sopenharmony_ci for (i = 0; i < dmi_results.keymap_size; i++) { 64762306a36Sopenharmony_ci keymap[pos] = dmi_results.keymap[i]; 64862306a36Sopenharmony_ci keymap[pos].code |= (0x0010 << 16); 64962306a36Sopenharmony_ci pos++; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci kfree(dmi_results.keymap); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* Append table with extra events of type 0x0010 which are not in DMI */ 65562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0010); i++) { 65662306a36Sopenharmony_ci const struct key_entry *entry = &dell_wmi_keymap_type_0010[i]; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* 65962306a36Sopenharmony_ci * Check if we've already found this scancode. This takes 66062306a36Sopenharmony_ci * quadratic time, but it doesn't matter unless the list 66162306a36Sopenharmony_ci * of extra keys gets very long. 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_ci if (dmi_results.keymap_size && 66462306a36Sopenharmony_ci have_scancode(entry->code | (0x0010 << 16), 66562306a36Sopenharmony_ci keymap, dmi_results.keymap_size) 66662306a36Sopenharmony_ci ) 66762306a36Sopenharmony_ci continue; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci keymap[pos] = *entry; 67062306a36Sopenharmony_ci keymap[pos].code |= (0x0010 << 16); 67162306a36Sopenharmony_ci pos++; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* Append table with events of type 0x0011 */ 67562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0011); i++) { 67662306a36Sopenharmony_ci keymap[pos] = dell_wmi_keymap_type_0011[i]; 67762306a36Sopenharmony_ci keymap[pos].code |= (0x0011 << 16); 67862306a36Sopenharmony_ci pos++; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* Append table with events of type 0x0012 */ 68262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0012); i++) { 68362306a36Sopenharmony_ci keymap[pos] = dell_wmi_keymap_type_0012[i]; 68462306a36Sopenharmony_ci keymap[pos].code |= (0x0012 << 16); 68562306a36Sopenharmony_ci pos++; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* 68962306a36Sopenharmony_ci * Now append also table with "legacy" events of type 0x0000. Some of 69062306a36Sopenharmony_ci * them are reported also on laptops which have scancodes in DMI. 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0000); i++) { 69362306a36Sopenharmony_ci keymap[pos] = dell_wmi_keymap_type_0000[i]; 69462306a36Sopenharmony_ci pos++; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci keymap[pos].type = KE_END; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci err = sparse_keymap_setup(priv->input_dev, keymap, NULL); 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * Sparse keymap library makes a copy of keymap so we don't need the 70262306a36Sopenharmony_ci * original one that was allocated. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ci kfree(keymap); 70562306a36Sopenharmony_ci if (err) 70662306a36Sopenharmony_ci goto err_free_dev; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci err = input_register_device(priv->input_dev); 70962306a36Sopenharmony_ci if (err) 71062306a36Sopenharmony_ci goto err_free_dev; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci err_free_dev: 71562306a36Sopenharmony_ci input_free_device(priv->input_dev); 71662306a36Sopenharmony_ci return err; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void dell_wmi_input_destroy(struct wmi_device *wdev) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci input_unregister_device(priv->input_dev); 72462306a36Sopenharmony_ci if (priv->tabletswitch_dev) 72562306a36Sopenharmony_ci input_unregister_device(priv->tabletswitch_dev); 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/* 72962306a36Sopenharmony_ci * According to Dell SMBIOS documentation: 73062306a36Sopenharmony_ci * 73162306a36Sopenharmony_ci * 17 3 Application Program Registration 73262306a36Sopenharmony_ci * 73362306a36Sopenharmony_ci * cbArg1 Application ID 1 = 0x00010000 73462306a36Sopenharmony_ci * cbArg2 Application ID 2 73562306a36Sopenharmony_ci * QUICKSET/DCP = 0x51534554 "QSET" 73662306a36Sopenharmony_ci * ALS Driver = 0x416c7353 "AlsS" 73762306a36Sopenharmony_ci * Latitude ON = 0x4c6f6e52 "LonR" 73862306a36Sopenharmony_ci * cbArg3 Application version or revision number 73962306a36Sopenharmony_ci * cbArg4 0 = Unregister application 74062306a36Sopenharmony_ci * 1 = Register application 74162306a36Sopenharmony_ci * cbRes1 Standard return codes (0, -1, -2) 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int dell_wmi_events_set_enabled(bool enable) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct calling_interface_buffer *buffer; 74762306a36Sopenharmony_ci int ret; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL); 75062306a36Sopenharmony_ci if (!buffer) 75162306a36Sopenharmony_ci return -ENOMEM; 75262306a36Sopenharmony_ci buffer->cmd_class = CLASS_INFO; 75362306a36Sopenharmony_ci buffer->cmd_select = SELECT_APP_REGISTRATION; 75462306a36Sopenharmony_ci buffer->input[0] = 0x10000; 75562306a36Sopenharmony_ci buffer->input[1] = 0x51534554; 75662306a36Sopenharmony_ci buffer->input[3] = enable; 75762306a36Sopenharmony_ci ret = dell_smbios_call(buffer); 75862306a36Sopenharmony_ci if (ret == 0) 75962306a36Sopenharmony_ci ret = buffer->output[0]; 76062306a36Sopenharmony_ci kfree(buffer); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return dell_smbios_error(ret); 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic int dell_wmi_probe(struct wmi_device *wdev, const void *context) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct dell_wmi_priv *priv; 76862306a36Sopenharmony_ci int ret; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci ret = dell_wmi_get_descriptor_valid(); 77162306a36Sopenharmony_ci if (ret) 77262306a36Sopenharmony_ci return ret; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci priv = devm_kzalloc( 77562306a36Sopenharmony_ci &wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL); 77662306a36Sopenharmony_ci if (!priv) 77762306a36Sopenharmony_ci return -ENOMEM; 77862306a36Sopenharmony_ci dev_set_drvdata(&wdev->dev, priv); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (!dell_wmi_get_interface_version(&priv->interface_version)) 78162306a36Sopenharmony_ci return -EPROBE_DEFER; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return dell_wmi_input_setup(wdev); 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic void dell_wmi_remove(struct wmi_device *wdev) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci dell_wmi_input_destroy(wdev); 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_cistatic const struct wmi_device_id dell_wmi_id_table[] = { 79162306a36Sopenharmony_ci { .guid_string = DELL_EVENT_GUID }, 79262306a36Sopenharmony_ci { }, 79362306a36Sopenharmony_ci}; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic struct wmi_driver dell_wmi_driver = { 79662306a36Sopenharmony_ci .driver = { 79762306a36Sopenharmony_ci .name = "dell-wmi", 79862306a36Sopenharmony_ci }, 79962306a36Sopenharmony_ci .id_table = dell_wmi_id_table, 80062306a36Sopenharmony_ci .probe = dell_wmi_probe, 80162306a36Sopenharmony_ci .remove = dell_wmi_remove, 80262306a36Sopenharmony_ci .notify = dell_wmi_notify, 80362306a36Sopenharmony_ci}; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic int __init dell_wmi_init(void) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci int err; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci dmi_check_system(dell_wmi_smbios_list); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (wmi_requires_smbios_request) { 81262306a36Sopenharmony_ci err = dell_wmi_events_set_enabled(true); 81362306a36Sopenharmony_ci if (err) { 81462306a36Sopenharmony_ci pr_err("Failed to enable WMI events\n"); 81562306a36Sopenharmony_ci return err; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci err = dell_privacy_register_driver(); 82062306a36Sopenharmony_ci if (err) 82162306a36Sopenharmony_ci return err; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return wmi_driver_register(&dell_wmi_driver); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_cilate_initcall(dell_wmi_init); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic void __exit dell_wmi_exit(void) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci if (wmi_requires_smbios_request) 83062306a36Sopenharmony_ci dell_wmi_events_set_enabled(false); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci wmi_driver_unregister(&dell_wmi_driver); 83362306a36Sopenharmony_ci dell_privacy_unregister_driver(); 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_cimodule_exit(dell_wmi_exit); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(wmi, dell_wmi_id_table); 838