18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Dell WMI hotkeys 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Red Hat <mjg@redhat.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Pali Rohár <pali@kernel.org> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Portions based on wistron_btns.c: 98c2ecf20Sopenharmony_ci * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> 108c2ecf20Sopenharmony_ci * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> 118c2ecf20Sopenharmony_ci * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/types.h> 218c2ecf20Sopenharmony_ci#include <linux/input.h> 228c2ecf20Sopenharmony_ci#include <linux/input/sparse-keymap.h> 238c2ecf20Sopenharmony_ci#include <linux/acpi.h> 248c2ecf20Sopenharmony_ci#include <linux/string.h> 258c2ecf20Sopenharmony_ci#include <linux/dmi.h> 268c2ecf20Sopenharmony_ci#include <linux/wmi.h> 278c2ecf20Sopenharmony_ci#include <acpi/video.h> 288c2ecf20Sopenharmony_ci#include "dell-smbios.h" 298c2ecf20Sopenharmony_ci#include "dell-wmi-descriptor.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); 338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); 348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic bool wmi_requires_smbios_request; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct dell_wmi_priv { 418c2ecf20Sopenharmony_ci struct input_dev *input_dev; 428c2ecf20Sopenharmony_ci u32 interface_version; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int __init dmi_matched(const struct dmi_system_id *dmi) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci wmi_requires_smbios_request = 1; 488c2ecf20Sopenharmony_ci return 1; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic const struct dmi_system_id dell_wmi_smbios_list[] __initconst = { 528c2ecf20Sopenharmony_ci { 538c2ecf20Sopenharmony_ci .callback = dmi_matched, 548c2ecf20Sopenharmony_ci .ident = "Dell Inspiron M5110", 558c2ecf20Sopenharmony_ci .matches = { 568c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 578c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), 588c2ecf20Sopenharmony_ci }, 598c2ecf20Sopenharmony_ci }, 608c2ecf20Sopenharmony_ci { 618c2ecf20Sopenharmony_ci .callback = dmi_matched, 628c2ecf20Sopenharmony_ci .ident = "Dell Vostro V131", 638c2ecf20Sopenharmony_ci .matches = { 648c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 658c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), 668c2ecf20Sopenharmony_ci }, 678c2ecf20Sopenharmony_ci }, 688c2ecf20Sopenharmony_ci { } 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * Keymap for WMI events of type 0x0000 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * Certain keys are flagged as KE_IGNORE. All of these are either 758c2ecf20Sopenharmony_ci * notifications (rather than requests for change) or are also sent 768c2ecf20Sopenharmony_ci * via the keyboard controller so should not be sent again. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistatic const struct key_entry dell_wmi_keymap_type_0000[] = { 798c2ecf20Sopenharmony_ci { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } }, 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Key code is followed by brightness level */ 828c2ecf20Sopenharmony_ci { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } }, 838c2ecf20Sopenharmony_ci { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } }, 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* Battery health status button */ 868c2ecf20Sopenharmony_ci { KE_KEY, 0xe007, { KEY_BATTERY } }, 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* Radio devices state change, key code is followed by other values */ 898c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe008, { KEY_RFKILL } }, 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci { KE_KEY, 0xe009, { KEY_EJECTCD } }, 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Key code is followed by: next, active and attached devices */ 948c2ecf20Sopenharmony_ci { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } }, 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Key code is followed by keyboard illumination level */ 978c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } }, 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* BIOS error detected */ 1008c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe00d, { KEY_RESERVED } }, 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Battery was removed or inserted */ 1038c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe00e, { KEY_RESERVED } }, 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* Wifi Catcher */ 1068c2ecf20Sopenharmony_ci { KE_KEY, 0xe011, { KEY_WLAN } }, 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Ambient light sensor toggle */ 1098c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe013, { KEY_RESERVED } }, 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe020, { KEY_MUTE } }, 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Unknown, defined in ACPI DSDT */ 1148c2ecf20Sopenharmony_ci /* { KE_IGNORE, 0xe023, { KEY_RESERVED } }, */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* Untested, Dell Instant Launch key on Inspiron 7520 */ 1178c2ecf20Sopenharmony_ci /* { KE_IGNORE, 0xe024, { KEY_RESERVED } }, */ 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* Dell Instant Launch key */ 1208c2ecf20Sopenharmony_ci { KE_KEY, 0xe025, { KEY_PROG4 } }, 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Audio panel key */ 1238c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe026, { KEY_RESERVED } }, 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* LCD Display On/Off Control key */ 1268c2ecf20Sopenharmony_ci { KE_KEY, 0xe027, { KEY_DISPLAYTOGGLE } }, 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Untested, Multimedia key on Dell Vostro 3560 */ 1298c2ecf20Sopenharmony_ci /* { KE_IGNORE, 0xe028, { KEY_RESERVED } }, */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Dell Instant Launch key */ 1328c2ecf20Sopenharmony_ci { KE_KEY, 0xe029, { KEY_PROG4 } }, 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* Untested, Windows Mobility Center button on Inspiron 7520 */ 1358c2ecf20Sopenharmony_ci /* { KE_IGNORE, 0xe02a, { KEY_RESERVED } }, */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Unknown, defined in ACPI DSDT */ 1388c2ecf20Sopenharmony_ci /* { KE_IGNORE, 0xe02b, { KEY_RESERVED } }, */ 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Untested, Dell Audio With Preset Switch button on Inspiron 7520 */ 1418c2ecf20Sopenharmony_ci /* { KE_IGNORE, 0xe02c, { KEY_RESERVED } }, */ 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } }, 1448c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } }, 1458c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } }, 1468c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } }, 1478c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } }, 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* NIC Link is Up */ 1508c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe043, { KEY_RESERVED } }, 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* NIC Link is Down */ 1538c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe044, { KEY_RESERVED } }, 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* 1568c2ecf20Sopenharmony_ci * This entry is very suspicious! 1578c2ecf20Sopenharmony_ci * Originally Matthew Garrett created this dell-wmi driver specially for 1588c2ecf20Sopenharmony_ci * "button with a picture of a battery" which has event code 0xe045. 1598c2ecf20Sopenharmony_ci * Later Mario Limonciello from Dell told us that event code 0xe045 is 1608c2ecf20Sopenharmony_ci * reported by Num Lock and should be ignored because key is send also 1618c2ecf20Sopenharmony_ci * by keyboard controller. 1628c2ecf20Sopenharmony_ci * So for now we will ignore this event to prevent potential double 1638c2ecf20Sopenharmony_ci * Num Lock key press. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe045, { KEY_NUMLOCK } }, 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* Scroll lock and also going to tablet mode on portable devices */ 1688c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } }, 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Untested, going from tablet mode on portable devices */ 1718c2ecf20Sopenharmony_ci /* { KE_IGNORE, 0xe047, { KEY_RESERVED } }, */ 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* Dell Support Center key */ 1748c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe06e, { KEY_RESERVED } }, 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe0f7, { KEY_MUTE } }, 1778c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } }, 1788c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } }, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistruct dell_bios_keymap_entry { 1828c2ecf20Sopenharmony_ci u16 scancode; 1838c2ecf20Sopenharmony_ci u16 keycode; 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistruct dell_bios_hotkey_table { 1878c2ecf20Sopenharmony_ci struct dmi_header header; 1888c2ecf20Sopenharmony_ci struct dell_bios_keymap_entry keymap[]; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistruct dell_dmi_results { 1938c2ecf20Sopenharmony_ci int err; 1948c2ecf20Sopenharmony_ci int keymap_size; 1958c2ecf20Sopenharmony_ci struct key_entry *keymap; 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* Uninitialized entries here are KEY_RESERVED == 0. */ 1998c2ecf20Sopenharmony_cistatic const u16 bios_to_linux_keycode[256] = { 2008c2ecf20Sopenharmony_ci [0] = KEY_MEDIA, 2018c2ecf20Sopenharmony_ci [1] = KEY_NEXTSONG, 2028c2ecf20Sopenharmony_ci [2] = KEY_PLAYPAUSE, 2038c2ecf20Sopenharmony_ci [3] = KEY_PREVIOUSSONG, 2048c2ecf20Sopenharmony_ci [4] = KEY_STOPCD, 2058c2ecf20Sopenharmony_ci [5] = KEY_UNKNOWN, 2068c2ecf20Sopenharmony_ci [6] = KEY_UNKNOWN, 2078c2ecf20Sopenharmony_ci [7] = KEY_UNKNOWN, 2088c2ecf20Sopenharmony_ci [8] = KEY_WWW, 2098c2ecf20Sopenharmony_ci [9] = KEY_UNKNOWN, 2108c2ecf20Sopenharmony_ci [10] = KEY_VOLUMEDOWN, 2118c2ecf20Sopenharmony_ci [11] = KEY_MUTE, 2128c2ecf20Sopenharmony_ci [12] = KEY_VOLUMEUP, 2138c2ecf20Sopenharmony_ci [13] = KEY_UNKNOWN, 2148c2ecf20Sopenharmony_ci [14] = KEY_BATTERY, 2158c2ecf20Sopenharmony_ci [15] = KEY_EJECTCD, 2168c2ecf20Sopenharmony_ci [16] = KEY_UNKNOWN, 2178c2ecf20Sopenharmony_ci [17] = KEY_SLEEP, 2188c2ecf20Sopenharmony_ci [18] = KEY_PROG1, 2198c2ecf20Sopenharmony_ci [19] = KEY_BRIGHTNESSDOWN, 2208c2ecf20Sopenharmony_ci [20] = KEY_BRIGHTNESSUP, 2218c2ecf20Sopenharmony_ci [21] = KEY_BRIGHTNESS_AUTO, 2228c2ecf20Sopenharmony_ci [22] = KEY_KBDILLUMTOGGLE, 2238c2ecf20Sopenharmony_ci [23] = KEY_UNKNOWN, 2248c2ecf20Sopenharmony_ci [24] = KEY_SWITCHVIDEOMODE, 2258c2ecf20Sopenharmony_ci [25] = KEY_UNKNOWN, 2268c2ecf20Sopenharmony_ci [26] = KEY_UNKNOWN, 2278c2ecf20Sopenharmony_ci [27] = KEY_SWITCHVIDEOMODE, 2288c2ecf20Sopenharmony_ci [28] = KEY_UNKNOWN, 2298c2ecf20Sopenharmony_ci [29] = KEY_UNKNOWN, 2308c2ecf20Sopenharmony_ci [30] = KEY_PROG2, 2318c2ecf20Sopenharmony_ci [31] = KEY_UNKNOWN, 2328c2ecf20Sopenharmony_ci [32] = KEY_UNKNOWN, 2338c2ecf20Sopenharmony_ci [33] = KEY_UNKNOWN, 2348c2ecf20Sopenharmony_ci [34] = KEY_UNKNOWN, 2358c2ecf20Sopenharmony_ci [35] = KEY_UNKNOWN, 2368c2ecf20Sopenharmony_ci [36] = KEY_UNKNOWN, 2378c2ecf20Sopenharmony_ci [37] = KEY_UNKNOWN, 2388c2ecf20Sopenharmony_ci [38] = KEY_MICMUTE, 2398c2ecf20Sopenharmony_ci [255] = KEY_PROG3, 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* 2438c2ecf20Sopenharmony_ci * Keymap for WMI events of type 0x0010 2448c2ecf20Sopenharmony_ci * 2458c2ecf20Sopenharmony_ci * These are applied if the 0xB2 DMI hotkey table is present and doesn't 2468c2ecf20Sopenharmony_ci * override them. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_cistatic const struct key_entry dell_wmi_keymap_type_0010[] = { 2498c2ecf20Sopenharmony_ci /* Fn-lock switched to function keys */ 2508c2ecf20Sopenharmony_ci { KE_IGNORE, 0x0, { KEY_RESERVED } }, 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Fn-lock switched to multimedia keys */ 2538c2ecf20Sopenharmony_ci { KE_IGNORE, 0x1, { KEY_RESERVED } }, 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Keyboard backlight change notification */ 2568c2ecf20Sopenharmony_ci { KE_IGNORE, 0x3f, { KEY_RESERVED } }, 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* Backlight brightness level */ 2598c2ecf20Sopenharmony_ci { KE_KEY, 0x57, { KEY_BRIGHTNESSDOWN } }, 2608c2ecf20Sopenharmony_ci { KE_KEY, 0x58, { KEY_BRIGHTNESSUP } }, 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /*Speaker Mute*/ 2638c2ecf20Sopenharmony_ci { KE_KEY, 0x109, { KEY_MUTE} }, 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* Mic mute */ 2668c2ecf20Sopenharmony_ci { KE_KEY, 0x150, { KEY_MICMUTE } }, 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Fn-lock */ 2698c2ecf20Sopenharmony_ci { KE_IGNORE, 0x151, { KEY_RESERVED } }, 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* Change keyboard illumination */ 2728c2ecf20Sopenharmony_ci { KE_IGNORE, 0x152, { KEY_KBDILLUMTOGGLE } }, 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * Radio disable (notify only -- there is no model for which the 2768c2ecf20Sopenharmony_ci * WMI event is supposed to trigger an action). 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ci { KE_IGNORE, 0x153, { KEY_RFKILL } }, 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* RGB keyboard backlight control */ 2818c2ecf20Sopenharmony_ci { KE_IGNORE, 0x154, { KEY_RESERVED } }, 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* 2848c2ecf20Sopenharmony_ci * Stealth mode toggle. This will "disable all lights and sounds". 2858c2ecf20Sopenharmony_ci * The action is performed by the BIOS and EC; the WMI event is just 2868c2ecf20Sopenharmony_ci * a notification. On the XPS 13 9350, this is Fn+F7, and there's 2878c2ecf20Sopenharmony_ci * a BIOS setting to enable and disable the hotkey. 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ci { KE_IGNORE, 0x155, { KEY_RESERVED } }, 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* Rugged magnetic dock attach/detach events */ 2928c2ecf20Sopenharmony_ci { KE_IGNORE, 0x156, { KEY_RESERVED } }, 2938c2ecf20Sopenharmony_ci { KE_IGNORE, 0x157, { KEY_RESERVED } }, 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Rugged programmable (P1/P2/P3 keys) */ 2968c2ecf20Sopenharmony_ci { KE_KEY, 0x850, { KEY_PROG1 } }, 2978c2ecf20Sopenharmony_ci { KE_KEY, 0x851, { KEY_PROG2 } }, 2988c2ecf20Sopenharmony_ci { KE_KEY, 0x852, { KEY_PROG3 } }, 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * Radio disable (notify only -- there is no model for which the 3028c2ecf20Sopenharmony_ci * WMI event is supposed to trigger an action). 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe008, { KEY_RFKILL } }, 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Fn-lock */ 3078c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe035, { KEY_RESERVED } }, 3088c2ecf20Sopenharmony_ci}; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* 3118c2ecf20Sopenharmony_ci * Keymap for WMI events of type 0x0011 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_cistatic const struct key_entry dell_wmi_keymap_type_0011[] = { 3148c2ecf20Sopenharmony_ci /* Battery unplugged */ 3158c2ecf20Sopenharmony_ci { KE_IGNORE, 0xfff0, { KEY_RESERVED } }, 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Battery inserted */ 3188c2ecf20Sopenharmony_ci { KE_IGNORE, 0xfff1, { KEY_RESERVED } }, 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* 3218c2ecf20Sopenharmony_ci * Detachable keyboard detached / undocked 3228c2ecf20Sopenharmony_ci * Note SW_TABLET_MODE is already reported through the intel_vbtn 3238c2ecf20Sopenharmony_ci * driver for this, so we ignore it. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci { KE_IGNORE, 0xfff2, { KEY_RESERVED } }, 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* Detachable keyboard attached / docked */ 3288c2ecf20Sopenharmony_ci { KE_IGNORE, 0xfff3, { KEY_RESERVED } }, 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Keyboard backlight level changed */ 3318c2ecf20Sopenharmony_ci { KE_IGNORE, KBD_LED_OFF_TOKEN, { KEY_RESERVED } }, 3328c2ecf20Sopenharmony_ci { KE_IGNORE, KBD_LED_ON_TOKEN, { KEY_RESERVED } }, 3338c2ecf20Sopenharmony_ci { KE_IGNORE, KBD_LED_AUTO_TOKEN, { KEY_RESERVED } }, 3348c2ecf20Sopenharmony_ci { KE_IGNORE, KBD_LED_AUTO_25_TOKEN, { KEY_RESERVED } }, 3358c2ecf20Sopenharmony_ci { KE_IGNORE, KBD_LED_AUTO_50_TOKEN, { KEY_RESERVED } }, 3368c2ecf20Sopenharmony_ci { KE_IGNORE, KBD_LED_AUTO_75_TOKEN, { KEY_RESERVED } }, 3378c2ecf20Sopenharmony_ci { KE_IGNORE, KBD_LED_AUTO_100_TOKEN, { KEY_RESERVED } }, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/* 3418c2ecf20Sopenharmony_ci * Keymap for WMI events of type 0x0012 3428c2ecf20Sopenharmony_ci * They are events with extended data 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_cistatic const struct key_entry dell_wmi_keymap_type_0012[] = { 3458c2ecf20Sopenharmony_ci /* Fn-lock button pressed */ 3468c2ecf20Sopenharmony_ci { KE_IGNORE, 0xe035, { KEY_RESERVED } }, 3478c2ecf20Sopenharmony_ci}; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic void dell_wmi_process_key(struct wmi_device *wdev, int type, int code) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); 3528c2ecf20Sopenharmony_ci const struct key_entry *key; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci key = sparse_keymap_entry_from_scancode(priv->input_dev, 3558c2ecf20Sopenharmony_ci (type << 16) | code); 3568c2ecf20Sopenharmony_ci if (!key) { 3578c2ecf20Sopenharmony_ci pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n", 3588c2ecf20Sopenharmony_ci type, code); 3598c2ecf20Sopenharmony_ci return; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci pr_debug("Key with type 0x%04x and code 0x%04x pressed\n", type, code); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* Don't report brightness notifications that will also come via ACPI */ 3658c2ecf20Sopenharmony_ci if ((key->keycode == KEY_BRIGHTNESSUP || 3668c2ecf20Sopenharmony_ci key->keycode == KEY_BRIGHTNESSDOWN) && 3678c2ecf20Sopenharmony_ci acpi_video_handles_brightness_key_presses()) 3688c2ecf20Sopenharmony_ci return; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request) 3718c2ecf20Sopenharmony_ci return; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (key->keycode == KEY_KBDILLUMTOGGLE) 3748c2ecf20Sopenharmony_ci dell_laptop_call_notifier( 3758c2ecf20Sopenharmony_ci DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED, NULL); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci sparse_keymap_report_entry(priv->input_dev, key, 1, true); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic void dell_wmi_notify(struct wmi_device *wdev, 3818c2ecf20Sopenharmony_ci union acpi_object *obj) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); 3848c2ecf20Sopenharmony_ci u16 *buffer_entry, *buffer_end; 3858c2ecf20Sopenharmony_ci acpi_size buffer_size; 3868c2ecf20Sopenharmony_ci int len, i; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (obj->type != ACPI_TYPE_BUFFER) { 3898c2ecf20Sopenharmony_ci pr_warn("bad response type %x\n", obj->type); 3908c2ecf20Sopenharmony_ci return; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci pr_debug("Received WMI event (%*ph)\n", 3948c2ecf20Sopenharmony_ci obj->buffer.length, obj->buffer.pointer); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci buffer_entry = (u16 *)obj->buffer.pointer; 3978c2ecf20Sopenharmony_ci buffer_size = obj->buffer.length/2; 3988c2ecf20Sopenharmony_ci buffer_end = buffer_entry + buffer_size; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* 4018c2ecf20Sopenharmony_ci * BIOS/ACPI on devices with WMI interface version 0 does not clear 4028c2ecf20Sopenharmony_ci * buffer before filling it. So next time when BIOS/ACPI send WMI event 4038c2ecf20Sopenharmony_ci * which is smaller as previous then it contains garbage in buffer from 4048c2ecf20Sopenharmony_ci * previous event. 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * BIOS/ACPI on devices with WMI interface version 1 clears buffer and 4078c2ecf20Sopenharmony_ci * sometimes send more events in buffer at one call. 4088c2ecf20Sopenharmony_ci * 4098c2ecf20Sopenharmony_ci * So to prevent reading garbage from buffer we will process only first 4108c2ecf20Sopenharmony_ci * one event on devices with WMI interface version 0. 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci if (priv->interface_version == 0 && buffer_entry < buffer_end) 4138c2ecf20Sopenharmony_ci if (buffer_end > buffer_entry + buffer_entry[0] + 1) 4148c2ecf20Sopenharmony_ci buffer_end = buffer_entry + buffer_entry[0] + 1; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci while (buffer_entry < buffer_end) { 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci len = buffer_entry[0]; 4198c2ecf20Sopenharmony_ci if (len == 0) 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci len++; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (buffer_entry + len > buffer_end) { 4258c2ecf20Sopenharmony_ci pr_warn("Invalid length of WMI event\n"); 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci switch (buffer_entry[1]) { 4328c2ecf20Sopenharmony_ci case 0x0000: /* One key pressed or event occurred */ 4338c2ecf20Sopenharmony_ci case 0x0012: /* Event with extended data occurred */ 4348c2ecf20Sopenharmony_ci if (len > 2) 4358c2ecf20Sopenharmony_ci dell_wmi_process_key(wdev, buffer_entry[1], 4368c2ecf20Sopenharmony_ci buffer_entry[2]); 4378c2ecf20Sopenharmony_ci /* Extended data is currently ignored */ 4388c2ecf20Sopenharmony_ci break; 4398c2ecf20Sopenharmony_ci case 0x0010: /* Sequence of keys pressed */ 4408c2ecf20Sopenharmony_ci case 0x0011: /* Sequence of events occurred */ 4418c2ecf20Sopenharmony_ci for (i = 2; i < len; ++i) 4428c2ecf20Sopenharmony_ci dell_wmi_process_key(wdev, buffer_entry[1], 4438c2ecf20Sopenharmony_ci buffer_entry[i]); 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci default: /* Unknown event */ 4468c2ecf20Sopenharmony_ci pr_info("Unknown WMI event type 0x%x\n", 4478c2ecf20Sopenharmony_ci (int)buffer_entry[1]); 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci buffer_entry += len; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic bool have_scancode(u32 scancode, const struct key_entry *keymap, int len) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci int i; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 4628c2ecf20Sopenharmony_ci if (keymap[i].code == scancode) 4638c2ecf20Sopenharmony_ci return true; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return false; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic void handle_dmi_entry(const struct dmi_header *dm, void *opaque) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct dell_dmi_results *results = opaque; 4718c2ecf20Sopenharmony_ci struct dell_bios_hotkey_table *table; 4728c2ecf20Sopenharmony_ci int hotkey_num, i, pos = 0; 4738c2ecf20Sopenharmony_ci struct key_entry *keymap; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (results->err || results->keymap) 4768c2ecf20Sopenharmony_ci return; /* We already found the hotkey table. */ 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* The Dell hotkey table is type 0xB2. Scan until we find it. */ 4798c2ecf20Sopenharmony_ci if (dm->type != 0xb2) 4808c2ecf20Sopenharmony_ci return; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci table = container_of(dm, struct dell_bios_hotkey_table, header); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci hotkey_num = (table->header.length - 4858c2ecf20Sopenharmony_ci sizeof(struct dell_bios_hotkey_table)) / 4868c2ecf20Sopenharmony_ci sizeof(struct dell_bios_keymap_entry); 4878c2ecf20Sopenharmony_ci if (hotkey_num < 1) { 4888c2ecf20Sopenharmony_ci /* 4898c2ecf20Sopenharmony_ci * Historically, dell-wmi would ignore a DMI entry of 4908c2ecf20Sopenharmony_ci * fewer than 7 bytes. Sizes between 4 and 8 bytes are 4918c2ecf20Sopenharmony_ci * nonsensical (both the header and all entries are 4 4928c2ecf20Sopenharmony_ci * bytes), so we approximate the old behavior by 4938c2ecf20Sopenharmony_ci * ignoring tables with fewer than one entry. 4948c2ecf20Sopenharmony_ci */ 4958c2ecf20Sopenharmony_ci return; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci keymap = kcalloc(hotkey_num, sizeof(struct key_entry), GFP_KERNEL); 4998c2ecf20Sopenharmony_ci if (!keymap) { 5008c2ecf20Sopenharmony_ci results->err = -ENOMEM; 5018c2ecf20Sopenharmony_ci return; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci for (i = 0; i < hotkey_num; i++) { 5058c2ecf20Sopenharmony_ci const struct dell_bios_keymap_entry *bios_entry = 5068c2ecf20Sopenharmony_ci &table->keymap[i]; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Uninitialized entries are 0 aka KEY_RESERVED. */ 5098c2ecf20Sopenharmony_ci u16 keycode = (bios_entry->keycode < 5108c2ecf20Sopenharmony_ci ARRAY_SIZE(bios_to_linux_keycode)) ? 5118c2ecf20Sopenharmony_ci bios_to_linux_keycode[bios_entry->keycode] : 5128c2ecf20Sopenharmony_ci (bios_entry->keycode == 0xffff ? KEY_UNKNOWN : KEY_RESERVED); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* 5158c2ecf20Sopenharmony_ci * Log if we find an entry in the DMI table that we don't 5168c2ecf20Sopenharmony_ci * understand. If this happens, we should figure out what 5178c2ecf20Sopenharmony_ci * the entry means and add it to bios_to_linux_keycode. 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_ci if (keycode == KEY_RESERVED) { 5208c2ecf20Sopenharmony_ci pr_info("firmware scancode 0x%x maps to unrecognized keycode 0x%x\n", 5218c2ecf20Sopenharmony_ci bios_entry->scancode, bios_entry->keycode); 5228c2ecf20Sopenharmony_ci continue; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (keycode == KEY_KBDILLUMTOGGLE) 5268c2ecf20Sopenharmony_ci keymap[pos].type = KE_IGNORE; 5278c2ecf20Sopenharmony_ci else 5288c2ecf20Sopenharmony_ci keymap[pos].type = KE_KEY; 5298c2ecf20Sopenharmony_ci keymap[pos].code = bios_entry->scancode; 5308c2ecf20Sopenharmony_ci keymap[pos].keycode = keycode; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci pos++; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci results->keymap = keymap; 5368c2ecf20Sopenharmony_ci results->keymap_size = pos; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic int dell_wmi_input_setup(struct wmi_device *wdev) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); 5428c2ecf20Sopenharmony_ci struct dell_dmi_results dmi_results = {}; 5438c2ecf20Sopenharmony_ci struct key_entry *keymap; 5448c2ecf20Sopenharmony_ci int err, i, pos = 0; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci priv->input_dev = input_allocate_device(); 5478c2ecf20Sopenharmony_ci if (!priv->input_dev) 5488c2ecf20Sopenharmony_ci return -ENOMEM; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci priv->input_dev->name = "Dell WMI hotkeys"; 5518c2ecf20Sopenharmony_ci priv->input_dev->id.bustype = BUS_HOST; 5528c2ecf20Sopenharmony_ci priv->input_dev->dev.parent = &wdev->dev; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (dmi_walk(handle_dmi_entry, &dmi_results)) { 5558c2ecf20Sopenharmony_ci /* 5568c2ecf20Sopenharmony_ci * Historically, dell-wmi ignored dmi_walk errors. A failure 5578c2ecf20Sopenharmony_ci * is certainly surprising, but it probably just indicates 5588c2ecf20Sopenharmony_ci * a very old laptop. 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_ci pr_warn("no DMI; using the old-style hotkey interface\n"); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (dmi_results.err) { 5648c2ecf20Sopenharmony_ci err = dmi_results.err; 5658c2ecf20Sopenharmony_ci goto err_free_dev; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci keymap = kcalloc(dmi_results.keymap_size + 5698c2ecf20Sopenharmony_ci ARRAY_SIZE(dell_wmi_keymap_type_0000) + 5708c2ecf20Sopenharmony_ci ARRAY_SIZE(dell_wmi_keymap_type_0010) + 5718c2ecf20Sopenharmony_ci ARRAY_SIZE(dell_wmi_keymap_type_0011) + 5728c2ecf20Sopenharmony_ci ARRAY_SIZE(dell_wmi_keymap_type_0012) + 5738c2ecf20Sopenharmony_ci 1, 5748c2ecf20Sopenharmony_ci sizeof(struct key_entry), GFP_KERNEL); 5758c2ecf20Sopenharmony_ci if (!keymap) { 5768c2ecf20Sopenharmony_ci kfree(dmi_results.keymap); 5778c2ecf20Sopenharmony_ci err = -ENOMEM; 5788c2ecf20Sopenharmony_ci goto err_free_dev; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* Append table with events of type 0x0010 which comes from DMI */ 5828c2ecf20Sopenharmony_ci for (i = 0; i < dmi_results.keymap_size; i++) { 5838c2ecf20Sopenharmony_ci keymap[pos] = dmi_results.keymap[i]; 5848c2ecf20Sopenharmony_ci keymap[pos].code |= (0x0010 << 16); 5858c2ecf20Sopenharmony_ci pos++; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci kfree(dmi_results.keymap); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* Append table with extra events of type 0x0010 which are not in DMI */ 5918c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0010); i++) { 5928c2ecf20Sopenharmony_ci const struct key_entry *entry = &dell_wmi_keymap_type_0010[i]; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* 5958c2ecf20Sopenharmony_ci * Check if we've already found this scancode. This takes 5968c2ecf20Sopenharmony_ci * quadratic time, but it doesn't matter unless the list 5978c2ecf20Sopenharmony_ci * of extra keys gets very long. 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ci if (dmi_results.keymap_size && 6008c2ecf20Sopenharmony_ci have_scancode(entry->code | (0x0010 << 16), 6018c2ecf20Sopenharmony_ci keymap, dmi_results.keymap_size) 6028c2ecf20Sopenharmony_ci ) 6038c2ecf20Sopenharmony_ci continue; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci keymap[pos] = *entry; 6068c2ecf20Sopenharmony_ci keymap[pos].code |= (0x0010 << 16); 6078c2ecf20Sopenharmony_ci pos++; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* Append table with events of type 0x0011 */ 6118c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0011); i++) { 6128c2ecf20Sopenharmony_ci keymap[pos] = dell_wmi_keymap_type_0011[i]; 6138c2ecf20Sopenharmony_ci keymap[pos].code |= (0x0011 << 16); 6148c2ecf20Sopenharmony_ci pos++; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci /* Append table with events of type 0x0012 */ 6188c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0012); i++) { 6198c2ecf20Sopenharmony_ci keymap[pos] = dell_wmi_keymap_type_0012[i]; 6208c2ecf20Sopenharmony_ci keymap[pos].code |= (0x0012 << 16); 6218c2ecf20Sopenharmony_ci pos++; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* 6258c2ecf20Sopenharmony_ci * Now append also table with "legacy" events of type 0x0000. Some of 6268c2ecf20Sopenharmony_ci * them are reported also on laptops which have scancodes in DMI. 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0000); i++) { 6298c2ecf20Sopenharmony_ci keymap[pos] = dell_wmi_keymap_type_0000[i]; 6308c2ecf20Sopenharmony_ci pos++; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci keymap[pos].type = KE_END; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci err = sparse_keymap_setup(priv->input_dev, keymap, NULL); 6368c2ecf20Sopenharmony_ci /* 6378c2ecf20Sopenharmony_ci * Sparse keymap library makes a copy of keymap so we don't need the 6388c2ecf20Sopenharmony_ci * original one that was allocated. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci kfree(keymap); 6418c2ecf20Sopenharmony_ci if (err) 6428c2ecf20Sopenharmony_ci goto err_free_dev; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci err = input_register_device(priv->input_dev); 6458c2ecf20Sopenharmony_ci if (err) 6468c2ecf20Sopenharmony_ci goto err_free_dev; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci return 0; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci err_free_dev: 6518c2ecf20Sopenharmony_ci input_free_device(priv->input_dev); 6528c2ecf20Sopenharmony_ci return err; 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic void dell_wmi_input_destroy(struct wmi_device *wdev) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci input_unregister_device(priv->input_dev); 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci/* 6638c2ecf20Sopenharmony_ci * According to Dell SMBIOS documentation: 6648c2ecf20Sopenharmony_ci * 6658c2ecf20Sopenharmony_ci * 17 3 Application Program Registration 6668c2ecf20Sopenharmony_ci * 6678c2ecf20Sopenharmony_ci * cbArg1 Application ID 1 = 0x00010000 6688c2ecf20Sopenharmony_ci * cbArg2 Application ID 2 6698c2ecf20Sopenharmony_ci * QUICKSET/DCP = 0x51534554 "QSET" 6708c2ecf20Sopenharmony_ci * ALS Driver = 0x416c7353 "AlsS" 6718c2ecf20Sopenharmony_ci * Latitude ON = 0x4c6f6e52 "LonR" 6728c2ecf20Sopenharmony_ci * cbArg3 Application version or revision number 6738c2ecf20Sopenharmony_ci * cbArg4 0 = Unregister application 6748c2ecf20Sopenharmony_ci * 1 = Register application 6758c2ecf20Sopenharmony_ci * cbRes1 Standard return codes (0, -1, -2) 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic int dell_wmi_events_set_enabled(bool enable) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct calling_interface_buffer *buffer; 6818c2ecf20Sopenharmony_ci int ret; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL); 6848c2ecf20Sopenharmony_ci if (!buffer) 6858c2ecf20Sopenharmony_ci return -ENOMEM; 6868c2ecf20Sopenharmony_ci buffer->cmd_class = CLASS_INFO; 6878c2ecf20Sopenharmony_ci buffer->cmd_select = SELECT_APP_REGISTRATION; 6888c2ecf20Sopenharmony_ci buffer->input[0] = 0x10000; 6898c2ecf20Sopenharmony_ci buffer->input[1] = 0x51534554; 6908c2ecf20Sopenharmony_ci buffer->input[3] = enable; 6918c2ecf20Sopenharmony_ci ret = dell_smbios_call(buffer); 6928c2ecf20Sopenharmony_ci if (ret == 0) 6938c2ecf20Sopenharmony_ci ret = buffer->output[0]; 6948c2ecf20Sopenharmony_ci kfree(buffer); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci return dell_smbios_error(ret); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic int dell_wmi_probe(struct wmi_device *wdev, const void *context) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci struct dell_wmi_priv *priv; 7028c2ecf20Sopenharmony_ci int ret; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci ret = dell_wmi_get_descriptor_valid(); 7058c2ecf20Sopenharmony_ci if (ret) 7068c2ecf20Sopenharmony_ci return ret; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci priv = devm_kzalloc( 7098c2ecf20Sopenharmony_ci &wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL); 7108c2ecf20Sopenharmony_ci if (!priv) 7118c2ecf20Sopenharmony_ci return -ENOMEM; 7128c2ecf20Sopenharmony_ci dev_set_drvdata(&wdev->dev, priv); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (!dell_wmi_get_interface_version(&priv->interface_version)) 7158c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci return dell_wmi_input_setup(wdev); 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic int dell_wmi_remove(struct wmi_device *wdev) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci dell_wmi_input_destroy(wdev); 7238c2ecf20Sopenharmony_ci return 0; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_cistatic const struct wmi_device_id dell_wmi_id_table[] = { 7268c2ecf20Sopenharmony_ci { .guid_string = DELL_EVENT_GUID }, 7278c2ecf20Sopenharmony_ci { }, 7288c2ecf20Sopenharmony_ci}; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic struct wmi_driver dell_wmi_driver = { 7318c2ecf20Sopenharmony_ci .driver = { 7328c2ecf20Sopenharmony_ci .name = "dell-wmi", 7338c2ecf20Sopenharmony_ci }, 7348c2ecf20Sopenharmony_ci .id_table = dell_wmi_id_table, 7358c2ecf20Sopenharmony_ci .probe = dell_wmi_probe, 7368c2ecf20Sopenharmony_ci .remove = dell_wmi_remove, 7378c2ecf20Sopenharmony_ci .notify = dell_wmi_notify, 7388c2ecf20Sopenharmony_ci}; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic int __init dell_wmi_init(void) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci int err; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci dmi_check_system(dell_wmi_smbios_list); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (wmi_requires_smbios_request) { 7478c2ecf20Sopenharmony_ci err = dell_wmi_events_set_enabled(true); 7488c2ecf20Sopenharmony_ci if (err) { 7498c2ecf20Sopenharmony_ci pr_err("Failed to enable WMI events\n"); 7508c2ecf20Sopenharmony_ci return err; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return wmi_driver_register(&dell_wmi_driver); 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_cilate_initcall(dell_wmi_init); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic void __exit dell_wmi_exit(void) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci if (wmi_requires_smbios_request) 7618c2ecf20Sopenharmony_ci dell_wmi_events_set_enabled(false); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci wmi_driver_unregister(&dell_wmi_driver); 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_cimodule_exit(dell_wmi_exit); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(wmi, dell_wmi_id_table); 768