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 | |
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(type: DMI_DEV_TYPE_OEM_STRING, NULL, from: 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 | |
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 | |
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 | |
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 | |
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(list: 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 | |
211 | static int __init eeepc_wmi_init(void) |
212 | { |
213 | return asus_wmi_register_driver(driver: &asus_wmi_driver); |
214 | } |
215 | |
216 | static void __exit eeepc_wmi_exit(void) |
217 | { |
218 | asus_wmi_unregister_driver(driver: &asus_wmi_driver); |
219 | } |
220 | |
221 | module_init(eeepc_wmi_init); |
222 | module_exit(eeepc_wmi_exit); |
223 | |