1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Eee PC WMI hotkey driver
4  *
5  * Copyright(C) 2010 Intel Corporation.
6  * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
7  *
8  * Portions based on wistron_btns.c:
9  * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
10  * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
11  * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
12  */
13 
14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15 
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/init.h>
19 #include <linux/input.h>
20 #include <linux/input/sparse-keymap.h>
21 #include <linux/dmi.h>
22 #include <linux/fb.h>
23 #include <linux/acpi.h>
24 
25 #include "asus-wmi.h"
26 
27 #define	EEEPC_WMI_FILE	"eeepc-wmi"
28 
29 MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>");
30 MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
31 MODULE_LICENSE("GPL");
32 
33 #define EEEPC_ACPI_HID		"ASUS010" /* old _HID used in eeepc-laptop */
34 
35 #define EEEPC_WMI_EVENT_GUID	"ABBC0F72-8EA1-11D1-00A0-C90629100000"
36 
37 MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID);
38 
39 static bool hotplug_wireless;
40 
41 module_param(hotplug_wireless, bool, 0444);
42 MODULE_PARM_DESC(hotplug_wireless,
43 		 "Enable hotplug for wireless device. "
44 		 "If your laptop needs that, please report to "
45 		 "acpi4asus-user@lists.sourceforge.net.");
46 
47 /* Values for T101MT "Home" key */
48 #define HOME_PRESS	0xe4
49 #define HOME_HOLD	0xea
50 #define HOME_RELEASE	0xe5
51 
52 static const struct key_entry eeepc_wmi_keymap[] = {
53 	{ KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } },
54 	{ KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },
55 	/* Sleep already handled via generic ACPI code */
56 	{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
57 	{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
58 	{ KE_KEY, 0x32, { KEY_MUTE } },
59 	{ KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */
60 	{ KE_KEY, 0x5d, { KEY_WLAN } },
61 	{ KE_KEY, 0x6b, { KEY_TOUCHPAD_TOGGLE } }, /* Toggle Touchpad */
62 	{ KE_KEY, 0x82, { KEY_CAMERA } },
63 	{ KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } },
64 	{ KE_KEY, 0x88, { KEY_WLAN } },
65 	{ KE_KEY, 0xbd, { KEY_CAMERA } },
66 	{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
67 	{ KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */
68 	{ KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
69 	{ KE_KEY, HOME_PRESS, { KEY_CONFIG } }, /* Home/Express gate key */
70 	{ KE_KEY, 0xe8, { KEY_SCREENLOCK } },
71 	{ KE_KEY, 0xe9, { KEY_DISPLAYTOGGLE } },
72 	{ KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } },
73 	{ KE_KEY, 0xec, { KEY_CAMERA_UP } },
74 	{ KE_KEY, 0xed, { KEY_CAMERA_DOWN } },
75 	{ KE_KEY, 0xee, { KEY_CAMERA_LEFT } },
76 	{ KE_KEY, 0xef, { KEY_CAMERA_RIGHT } },
77 	{ KE_KEY, 0xf3, { KEY_MENU } },
78 	{ KE_KEY, 0xf5, { KEY_HOMEPAGE } },
79 	{ KE_KEY, 0xf6, { KEY_ESC } },
80 	{ KE_END, 0},
81 };
82 
83 static struct quirk_entry quirk_asus_unknown = {
84 };
85 
86 static struct quirk_entry quirk_asus_1000h = {
87 	.hotplug_wireless = true,
88 };
89 
90 static struct quirk_entry quirk_asus_et2012_type1 = {
91 	.store_backlight_power = true,
92 };
93 
94 static struct quirk_entry quirk_asus_et2012_type3 = {
95 	.scalar_panel_brightness = true,
96 	.store_backlight_power = true,
97 };
98 
99 static struct quirk_entry *quirks;
100 
et2012_quirks(void)101 static void et2012_quirks(void)
102 {
103 	const struct dmi_device *dev = NULL;
104 	char oemstring[30];
105 
106 	while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
107 		if (sscanf(dev->name, "AEMS%24c", oemstring) == 1) {
108 			if (oemstring[18] == '1')
109 				quirks = &quirk_asus_et2012_type1;
110 			else if (oemstring[18] == '3')
111 				quirks = &quirk_asus_et2012_type3;
112 			break;
113 		}
114 	}
115 }
116 
dmi_matched(const struct dmi_system_id * dmi)117 static int dmi_matched(const struct dmi_system_id *dmi)
118 {
119 	char *model;
120 
121 	quirks = dmi->driver_data;
122 
123 	model = (char *)dmi->matches[1].substr;
124 	if (unlikely(strncmp(model, "ET2012", 6) == 0))
125 		et2012_quirks();
126 
127 	return 1;
128 }
129 
130 static const struct dmi_system_id asus_quirks[] = {
131 	{
132 		.callback = dmi_matched,
133 		.ident = "ASUSTeK Computer INC. 1000H",
134 		.matches = {
135 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
136 			DMI_MATCH(DMI_PRODUCT_NAME, "1000H"),
137 		},
138 		.driver_data = &quirk_asus_1000h,
139 	},
140 	{
141 		.callback = dmi_matched,
142 		.ident = "ASUSTeK Computer INC. ET2012E/I",
143 		.matches = {
144 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
145 			DMI_MATCH(DMI_PRODUCT_NAME, "ET2012"),
146 		},
147 		.driver_data = &quirk_asus_unknown,
148 	},
149 	{}
150 };
151 
eeepc_wmi_key_filter(struct asus_wmi_driver * asus_wmi,int * code,unsigned int * value,bool * autorelease)152 static void eeepc_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code,
153 				 unsigned int *value, bool *autorelease)
154 {
155 	switch (*code) {
156 	case HOME_PRESS:
157 		*value = 1;
158 		*autorelease = 0;
159 		break;
160 	case HOME_HOLD:
161 		*code = ASUS_WMI_KEY_IGNORE;
162 		break;
163 	case HOME_RELEASE:
164 		*code = HOME_PRESS;
165 		*value = 0;
166 		*autorelease = 0;
167 		break;
168 	}
169 }
170 
eeepc_wmi_probe(struct platform_device * pdev)171 static int eeepc_wmi_probe(struct platform_device *pdev)
172 {
173 	if (acpi_dev_found(EEEPC_ACPI_HID)) {
174 		pr_warn("Found legacy ATKD device (%s)\n", EEEPC_ACPI_HID);
175 		pr_warn("WMI device present, but legacy ATKD device is also "
176 			"present and enabled\n");
177 		pr_warn("You probably booted with acpi_osi=\"Linux\" or "
178 			"acpi_osi=\"!Windows 2009\"\n");
179 		pr_warn("Can't load eeepc-wmi, use default acpi_osi "
180 			"(preferred) or eeepc-laptop\n");
181 		return -EBUSY;
182 	}
183 	return 0;
184 }
185 
eeepc_wmi_quirks(struct asus_wmi_driver * driver)186 static void eeepc_wmi_quirks(struct asus_wmi_driver *driver)
187 {
188 	quirks = &quirk_asus_unknown;
189 	quirks->hotplug_wireless = hotplug_wireless;
190 
191 	dmi_check_system(asus_quirks);
192 
193 	driver->quirks = quirks;
194 	driver->quirks->wapf = -1;
195 	driver->panel_power = FB_BLANK_UNBLANK;
196 }
197 
198 static struct asus_wmi_driver asus_wmi_driver = {
199 	.name = EEEPC_WMI_FILE,
200 	.owner = THIS_MODULE,
201 	.event_guid = EEEPC_WMI_EVENT_GUID,
202 	.keymap = eeepc_wmi_keymap,
203 	.input_name = "Eee PC WMI hotkeys",
204 	.input_phys = EEEPC_WMI_FILE "/input0",
205 	.key_filter = eeepc_wmi_key_filter,
206 	.probe = eeepc_wmi_probe,
207 	.detect_quirks = eeepc_wmi_quirks,
208 };
209 
210 
eeepc_wmi_init(void)211 static int __init eeepc_wmi_init(void)
212 {
213 	return asus_wmi_register_driver(&asus_wmi_driver);
214 }
215 
eeepc_wmi_exit(void)216 static void __exit eeepc_wmi_exit(void)
217 {
218 	asus_wmi_unregister_driver(&asus_wmi_driver);
219 }
220 
221 module_init(eeepc_wmi_init);
222 module_exit(eeepc_wmi_exit);
223