18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Eee PC WMI hotkey driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright(C) 2010 Intel Corporation. 68c2ecf20Sopenharmony_ci * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com> 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/input.h> 208c2ecf20Sopenharmony_ci#include <linux/input/sparse-keymap.h> 218c2ecf20Sopenharmony_ci#include <linux/dmi.h> 228c2ecf20Sopenharmony_ci#include <linux/fb.h> 238c2ecf20Sopenharmony_ci#include <linux/acpi.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "asus-wmi.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define EEEPC_WMI_FILE "eeepc-wmi" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>"); 308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); 318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ciMODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic bool hotplug_wireless; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cimodule_param(hotplug_wireless, bool, 0444); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(hotplug_wireless, 438c2ecf20Sopenharmony_ci "Enable hotplug for wireless device. " 448c2ecf20Sopenharmony_ci "If your laptop needs that, please report to " 458c2ecf20Sopenharmony_ci "acpi4asus-user@lists.sourceforge.net."); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Values for T101MT "Home" key */ 488c2ecf20Sopenharmony_ci#define HOME_PRESS 0xe4 498c2ecf20Sopenharmony_ci#define HOME_HOLD 0xea 508c2ecf20Sopenharmony_ci#define HOME_RELEASE 0xe5 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic const struct key_entry eeepc_wmi_keymap[] = { 538c2ecf20Sopenharmony_ci { KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } }, 548c2ecf20Sopenharmony_ci { KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } }, 558c2ecf20Sopenharmony_ci /* Sleep already handled via generic ACPI code */ 568c2ecf20Sopenharmony_ci { KE_KEY, 0x30, { KEY_VOLUMEUP } }, 578c2ecf20Sopenharmony_ci { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, 588c2ecf20Sopenharmony_ci { KE_KEY, 0x32, { KEY_MUTE } }, 598c2ecf20Sopenharmony_ci { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */ 608c2ecf20Sopenharmony_ci { KE_KEY, 0x5d, { KEY_WLAN } }, 618c2ecf20Sopenharmony_ci { KE_KEY, 0x6b, { KEY_TOUCHPAD_TOGGLE } }, /* Toggle Touchpad */ 628c2ecf20Sopenharmony_ci { KE_KEY, 0x82, { KEY_CAMERA } }, 638c2ecf20Sopenharmony_ci { KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } }, 648c2ecf20Sopenharmony_ci { KE_KEY, 0x88, { KEY_WLAN } }, 658c2ecf20Sopenharmony_ci { KE_KEY, 0xbd, { KEY_CAMERA } }, 668c2ecf20Sopenharmony_ci { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, 678c2ecf20Sopenharmony_ci { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */ 688c2ecf20Sopenharmony_ci { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ 698c2ecf20Sopenharmony_ci { KE_KEY, HOME_PRESS, { KEY_CONFIG } }, /* Home/Express gate key */ 708c2ecf20Sopenharmony_ci { KE_KEY, 0xe8, { KEY_SCREENLOCK } }, 718c2ecf20Sopenharmony_ci { KE_KEY, 0xe9, { KEY_DISPLAYTOGGLE } }, 728c2ecf20Sopenharmony_ci { KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } }, 738c2ecf20Sopenharmony_ci { KE_KEY, 0xec, { KEY_CAMERA_UP } }, 748c2ecf20Sopenharmony_ci { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, 758c2ecf20Sopenharmony_ci { KE_KEY, 0xee, { KEY_CAMERA_LEFT } }, 768c2ecf20Sopenharmony_ci { KE_KEY, 0xef, { KEY_CAMERA_RIGHT } }, 778c2ecf20Sopenharmony_ci { KE_KEY, 0xf3, { KEY_MENU } }, 788c2ecf20Sopenharmony_ci { KE_KEY, 0xf5, { KEY_HOMEPAGE } }, 798c2ecf20Sopenharmony_ci { KE_KEY, 0xf6, { KEY_ESC } }, 808c2ecf20Sopenharmony_ci { KE_END, 0}, 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic struct quirk_entry quirk_asus_unknown = { 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic struct quirk_entry quirk_asus_1000h = { 878c2ecf20Sopenharmony_ci .hotplug_wireless = true, 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic struct quirk_entry quirk_asus_et2012_type1 = { 918c2ecf20Sopenharmony_ci .store_backlight_power = true, 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct quirk_entry quirk_asus_et2012_type3 = { 958c2ecf20Sopenharmony_ci .scalar_panel_brightness = true, 968c2ecf20Sopenharmony_ci .store_backlight_power = true, 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic struct quirk_entry quirk_asus_x101ch = { 1008c2ecf20Sopenharmony_ci /* We need this when ACPI function doesn't do this well */ 1018c2ecf20Sopenharmony_ci .wmi_backlight_power = true, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic struct quirk_entry *quirks; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void et2012_quirks(void) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci const struct dmi_device *dev = NULL; 1098c2ecf20Sopenharmony_ci char oemstring[30]; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { 1128c2ecf20Sopenharmony_ci if (sscanf(dev->name, "AEMS%24c", oemstring) == 1) { 1138c2ecf20Sopenharmony_ci if (oemstring[18] == '1') 1148c2ecf20Sopenharmony_ci quirks = &quirk_asus_et2012_type1; 1158c2ecf20Sopenharmony_ci else if (oemstring[18] == '3') 1168c2ecf20Sopenharmony_ci quirks = &quirk_asus_et2012_type3; 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int dmi_matched(const struct dmi_system_id *dmi) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci char *model; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci quirks = dmi->driver_data; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci model = (char *)dmi->matches[1].substr; 1298c2ecf20Sopenharmony_ci if (unlikely(strncmp(model, "ET2012", 6) == 0)) 1308c2ecf20Sopenharmony_ci et2012_quirks(); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return 1; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic const struct dmi_system_id asus_quirks[] = { 1368c2ecf20Sopenharmony_ci { 1378c2ecf20Sopenharmony_ci .callback = dmi_matched, 1388c2ecf20Sopenharmony_ci .ident = "ASUSTeK Computer INC. 1000H", 1398c2ecf20Sopenharmony_ci .matches = { 1408c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), 1418c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "1000H"), 1428c2ecf20Sopenharmony_ci }, 1438c2ecf20Sopenharmony_ci .driver_data = &quirk_asus_1000h, 1448c2ecf20Sopenharmony_ci }, 1458c2ecf20Sopenharmony_ci { 1468c2ecf20Sopenharmony_ci .callback = dmi_matched, 1478c2ecf20Sopenharmony_ci .ident = "ASUSTeK Computer INC. ET2012E/I", 1488c2ecf20Sopenharmony_ci .matches = { 1498c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), 1508c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "ET2012"), 1518c2ecf20Sopenharmony_ci }, 1528c2ecf20Sopenharmony_ci .driver_data = &quirk_asus_unknown, 1538c2ecf20Sopenharmony_ci }, 1548c2ecf20Sopenharmony_ci { 1558c2ecf20Sopenharmony_ci .callback = dmi_matched, 1568c2ecf20Sopenharmony_ci .ident = "ASUSTeK Computer INC. X101CH", 1578c2ecf20Sopenharmony_ci .matches = { 1588c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 1598c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "X101CH"), 1608c2ecf20Sopenharmony_ci }, 1618c2ecf20Sopenharmony_ci .driver_data = &quirk_asus_x101ch, 1628c2ecf20Sopenharmony_ci }, 1638c2ecf20Sopenharmony_ci { 1648c2ecf20Sopenharmony_ci .callback = dmi_matched, 1658c2ecf20Sopenharmony_ci .ident = "ASUSTeK Computer INC. 1015CX", 1668c2ecf20Sopenharmony_ci .matches = { 1678c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 1688c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "1015CX"), 1698c2ecf20Sopenharmony_ci }, 1708c2ecf20Sopenharmony_ci .driver_data = &quirk_asus_x101ch, 1718c2ecf20Sopenharmony_ci }, 1728c2ecf20Sopenharmony_ci {}, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void eeepc_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code, 1768c2ecf20Sopenharmony_ci unsigned int *value, bool *autorelease) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci switch (*code) { 1798c2ecf20Sopenharmony_ci case HOME_PRESS: 1808c2ecf20Sopenharmony_ci *value = 1; 1818c2ecf20Sopenharmony_ci *autorelease = 0; 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci case HOME_HOLD: 1848c2ecf20Sopenharmony_ci *code = ASUS_WMI_KEY_IGNORE; 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci case HOME_RELEASE: 1878c2ecf20Sopenharmony_ci *code = HOME_PRESS; 1888c2ecf20Sopenharmony_ci *value = 0; 1898c2ecf20Sopenharmony_ci *autorelease = 0; 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int eeepc_wmi_probe(struct platform_device *pdev) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci if (acpi_dev_found(EEEPC_ACPI_HID)) { 1978c2ecf20Sopenharmony_ci pr_warn("Found legacy ATKD device (%s)\n", EEEPC_ACPI_HID); 1988c2ecf20Sopenharmony_ci pr_warn("WMI device present, but legacy ATKD device is also " 1998c2ecf20Sopenharmony_ci "present and enabled\n"); 2008c2ecf20Sopenharmony_ci pr_warn("You probably booted with acpi_osi=\"Linux\" or " 2018c2ecf20Sopenharmony_ci "acpi_osi=\"!Windows 2009\"\n"); 2028c2ecf20Sopenharmony_ci pr_warn("Can't load eeepc-wmi, use default acpi_osi " 2038c2ecf20Sopenharmony_ci "(preferred) or eeepc-laptop\n"); 2048c2ecf20Sopenharmony_ci return -EBUSY; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void eeepc_wmi_quirks(struct asus_wmi_driver *driver) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci quirks = &quirk_asus_unknown; 2128c2ecf20Sopenharmony_ci quirks->hotplug_wireless = hotplug_wireless; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci dmi_check_system(asus_quirks); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci driver->quirks = quirks; 2178c2ecf20Sopenharmony_ci driver->quirks->wapf = -1; 2188c2ecf20Sopenharmony_ci driver->panel_power = FB_BLANK_UNBLANK; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic struct asus_wmi_driver asus_wmi_driver = { 2228c2ecf20Sopenharmony_ci .name = EEEPC_WMI_FILE, 2238c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2248c2ecf20Sopenharmony_ci .event_guid = EEEPC_WMI_EVENT_GUID, 2258c2ecf20Sopenharmony_ci .keymap = eeepc_wmi_keymap, 2268c2ecf20Sopenharmony_ci .input_name = "Eee PC WMI hotkeys", 2278c2ecf20Sopenharmony_ci .input_phys = EEEPC_WMI_FILE "/input0", 2288c2ecf20Sopenharmony_ci .key_filter = eeepc_wmi_key_filter, 2298c2ecf20Sopenharmony_ci .probe = eeepc_wmi_probe, 2308c2ecf20Sopenharmony_ci .detect_quirks = eeepc_wmi_quirks, 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int __init eeepc_wmi_init(void) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci return asus_wmi_register_driver(&asus_wmi_driver); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic void __exit eeepc_wmi_exit(void) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci asus_wmi_unregister_driver(&asus_wmi_driver); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cimodule_init(eeepc_wmi_init); 2458c2ecf20Sopenharmony_cimodule_exit(eeepc_wmi_exit); 246