blob: 57c6c8a4db24c52ffbd848a98eb01e939e3928c8 [file] [log] [blame]
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001/*
2 * Acer WMI Laptop Extras
3 *
Carlos Corbacho4f0175d2009-04-04 09:33:39 +01004 * Copyright (C) 2007-2009 Carlos Corbacho <carlos@strangeworlds.co.uk>
Carlos Corbacho745a5d22008-02-05 02:17:10 +00005 *
6 * Based on acer_acpi:
7 * Copyright (C) 2005-2007 E.M. Smith
8 * Copyright (C) 2007-2008 Carlos Corbacho <cathectic@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
Lee, Chun-Yicae15702011-03-16 18:52:36 +080025#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
26
Carlos Corbacho745a5d22008-02-05 02:17:10 +000027#include <linux/kernel.h>
28#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/types.h>
31#include <linux/dmi.h>
Carlos Corbachof2b585b2008-06-21 09:09:27 +010032#include <linux/fb.h>
Carlos Corbacho745a5d22008-02-05 02:17:10 +000033#include <linux/backlight.h>
34#include <linux/leds.h>
35#include <linux/platform_device.h>
36#include <linux/acpi.h>
37#include <linux/i8042.h>
Carlos Corbacho0606e1a2008-10-08 21:40:21 +010038#include <linux/rfkill.h>
39#include <linux/workqueue.h>
Carlos Corbacho81143522008-06-21 09:09:53 +010040#include <linux/debugfs.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090041#include <linux/slab.h>
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +080042#include <linux/input.h>
43#include <linux/input/sparse-keymap.h>
Carlos Corbacho745a5d22008-02-05 02:17:10 +000044
45#include <acpi/acpi_drivers.h>
46
47MODULE_AUTHOR("Carlos Corbacho");
48MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
49MODULE_LICENSE("GPL");
50
Carlos Corbacho745a5d22008-02-05 02:17:10 +000051/*
Carlos Corbacho745a5d22008-02-05 02:17:10 +000052 * Magic Number
53 * Meaning is unknown - this number is required for writing to ACPI for AMW0
54 * (it's also used in acerhk when directly accessing the BIOS)
55 */
56#define ACER_AMW0_WRITE 0x9610
57
58/*
59 * Bit masks for the AMW0 interface
60 */
61#define ACER_AMW0_WIRELESS_MASK 0x35
62#define ACER_AMW0_BLUETOOTH_MASK 0x34
63#define ACER_AMW0_MAILLED_MASK 0x31
64
65/*
66 * Method IDs for WMID interface
67 */
68#define ACER_WMID_GET_WIRELESS_METHODID 1
69#define ACER_WMID_GET_BLUETOOTH_METHODID 2
70#define ACER_WMID_GET_BRIGHTNESS_METHODID 3
71#define ACER_WMID_SET_WIRELESS_METHODID 4
72#define ACER_WMID_SET_BLUETOOTH_METHODID 5
73#define ACER_WMID_SET_BRIGHTNESS_METHODID 6
74#define ACER_WMID_GET_THREEG_METHODID 10
75#define ACER_WMID_SET_THREEG_METHODID 11
76
77/*
78 * Acer ACPI method GUIDs
79 */
80#define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
Carlos Corbacho5753dd52008-06-21 09:09:48 +010081#define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C"
Matthew Garrettbbb70602011-02-09 16:39:40 -050082#define WMID_GUID1 "6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3"
Pali Rohár298f19b2011-03-11 12:36:43 -050083#define WMID_GUID2 "95764E09-FB56-4E83-B31A-37761F60994A"
Lee, Chun-Yib3c90922010-12-07 10:29:22 +080084#define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531"
Carlos Corbacho745a5d22008-02-05 02:17:10 +000085
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +080086/*
87 * Acer ACPI event GUIDs
88 */
89#define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026"
90
Carlos Corbacho745a5d22008-02-05 02:17:10 +000091MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
Lee, Chun-Yi08a07992011-04-06 17:40:06 +080092MODULE_ALIAS("wmi:6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3");
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +080093MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
94
95enum acer_wmi_event_ids {
96 WMID_HOTKEY_EVENT = 0x1,
97};
98
99static const struct key_entry acer_wmi_keymap[] = {
100 {KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */
Melchior FRANZ8ae68de2011-05-24 10:35:55 +0200101 {KE_KEY, 0x03, {KEY_WLAN} }, /* WiFi */
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +0800102 {KE_KEY, 0x12, {KEY_BLUETOOTH} }, /* BT */
103 {KE_KEY, 0x21, {KEY_PROG1} }, /* Backup */
104 {KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */
105 {KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */
106 {KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */
Melchior FRANZ8ae68de2011-05-24 10:35:55 +0200107 {KE_IGNORE, 0x41, {KEY_MUTE} },
108 {KE_IGNORE, 0x42, {KEY_PREVIOUSSONG} },
109 {KE_IGNORE, 0x43, {KEY_NEXTSONG} },
110 {KE_IGNORE, 0x44, {KEY_PLAYPAUSE} },
111 {KE_IGNORE, 0x45, {KEY_STOP} },
112 {KE_IGNORE, 0x48, {KEY_VOLUMEUP} },
113 {KE_IGNORE, 0x49, {KEY_VOLUMEDOWN} },
114 {KE_IGNORE, 0x61, {KEY_SWITCHVIDEOMODE} },
115 {KE_IGNORE, 0x62, {KEY_BRIGHTNESSUP} },
116 {KE_IGNORE, 0x63, {KEY_BRIGHTNESSDOWN} },
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +0800117 {KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */
Melchior FRANZ8ae68de2011-05-24 10:35:55 +0200118 {KE_IGNORE, 0x81, {KEY_SLEEP} },
Lee, Chun-Yi89411782011-03-02 01:07:11 +0800119 {KE_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} }, /* Touch Pad On/Off */
Melchior FRANZ8ae68de2011-05-24 10:35:55 +0200120 {KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} },
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +0800121 {KE_END, 0}
122};
123
124static struct input_dev *acer_wmi_input_dev;
125
126struct event_return_value {
127 u8 function;
128 u8 key_num;
129 u16 device_state;
130 u32 reserved;
131} __attribute__((packed));
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000132
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000133/*
Lee, Chun-Yib3c90922010-12-07 10:29:22 +0800134 * GUID3 Get Device Status device flags
135 */
Lee, Chun-Yi6c3df882010-12-07 10:29:23 +0800136#define ACER_WMID3_GDS_WIRELESS (1<<0) /* WiFi */
Lee, Chun-Yib3c90922010-12-07 10:29:22 +0800137#define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */
Lee, Chun-Yi6c3df882010-12-07 10:29:23 +0800138#define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */
Lee, Chun-Yib3c90922010-12-07 10:29:22 +0800139
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -0500140struct lm_input_params {
141 u8 function_num; /* Function Number */
142 u16 commun_devices; /* Communication type devices default status */
143 u16 devices; /* Other type devices default status */
144 u8 lm_status; /* Launch Manager Status */
145 u16 reserved;
146} __attribute__((packed));
147
148struct lm_return_value {
149 u8 error_code; /* Error Code */
150 u8 ec_return_value; /* EC Return Value */
151 u16 reserved;
152} __attribute__((packed));
153
Lee, Chun-Yib3c90922010-12-07 10:29:22 +0800154struct wmid3_gds_input_param { /* Get Device Status input parameter */
155 u8 function_num; /* Function Number */
156 u8 hotkey_number; /* Hotkey Number */
157 u16 devices; /* Get Device */
158} __attribute__((packed));
159
160struct wmid3_gds_return_value { /* Get Device Status return value*/
161 u8 error_code; /* Error Code */
162 u8 ec_return_value; /* EC Return Value */
163 u16 devices; /* Current Device Status */
164 u32 reserved;
165} __attribute__((packed));
166
Lee, Chun-Yi6c3df882010-12-07 10:29:23 +0800167struct hotkey_function_type_aa {
168 u8 type;
169 u8 length;
170 u16 handle;
171 u16 commun_func_bitmap;
172} __attribute__((packed));
173
Lee, Chun-Yib3c90922010-12-07 10:29:22 +0800174/*
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000175 * Interface capability flags
176 */
177#define ACER_CAP_MAILLED (1<<0)
178#define ACER_CAP_WIRELESS (1<<1)
179#define ACER_CAP_BLUETOOTH (1<<2)
180#define ACER_CAP_BRIGHTNESS (1<<3)
181#define ACER_CAP_THREEG (1<<4)
182#define ACER_CAP_ANY (0xFFFFFFFF)
183
184/*
185 * Interface type flags
186 */
187enum interface_flags {
188 ACER_AMW0,
189 ACER_AMW0_V2,
190 ACER_WMID,
191};
192
193#define ACER_DEFAULT_WIRELESS 0
194#define ACER_DEFAULT_BLUETOOTH 0
195#define ACER_DEFAULT_MAILLED 0
196#define ACER_DEFAULT_THREEG 0
197
198static int max_brightness = 0xF;
199
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000200static int mailled = -1;
201static int brightness = -1;
202static int threeg = -1;
203static int force_series;
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -0500204static bool ec_raw_mode;
Lee, Chun-Yi6c3df882010-12-07 10:29:23 +0800205static bool has_type_aa;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000206
207module_param(mailled, int, 0444);
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000208module_param(brightness, int, 0444);
209module_param(threeg, int, 0444);
210module_param(force_series, int, 0444);
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -0500211module_param(ec_raw_mode, bool, 0444);
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000212MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
213MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
214MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
215MODULE_PARM_DESC(force_series, "Force a different laptop series");
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -0500216MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode");
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000217
218struct acer_data {
219 int mailled;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000220 int threeg;
221 int brightness;
222};
223
Carlos Corbacho81143522008-06-21 09:09:53 +0100224struct acer_debug {
225 struct dentry *root;
226 struct dentry *devices;
227 u32 wmid_devices;
228};
229
Carlos Corbacho0606e1a2008-10-08 21:40:21 +0100230static struct rfkill *wireless_rfkill;
231static struct rfkill *bluetooth_rfkill;
Lee, Chun-Yib3c90922010-12-07 10:29:22 +0800232static struct rfkill *threeg_rfkill;
Lee, Chun-Yi8215af02011-03-28 16:52:02 +0800233static bool rfkill_inited;
Carlos Corbacho0606e1a2008-10-08 21:40:21 +0100234
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000235/* Each low-level interface must define at least some of the following */
236struct wmi_interface {
237 /* The WMI device type */
238 u32 type;
239
240 /* The capabilities this interface provides */
241 u32 capability;
242
243 /* Private data for the current interface */
244 struct acer_data data;
Carlos Corbacho81143522008-06-21 09:09:53 +0100245
246 /* debugfs entries associated with this interface */
247 struct acer_debug debug;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000248};
249
250/* The static interface pointer, points to the currently detected interface */
251static struct wmi_interface *interface;
252
253/*
254 * Embedded Controller quirks
255 * Some laptops require us to directly access the EC to either enable or query
256 * features that are not available through WMI.
257 */
258
259struct quirk_entry {
260 u8 wireless;
261 u8 mailled;
Carlos Corbacho9991d9f2008-06-21 09:09:22 +0100262 s8 brightness;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000263 u8 bluetooth;
264};
265
266static struct quirk_entry *quirks;
267
268static void set_quirks(void)
269{
Arjan van de Ven83097ac2008-08-23 21:45:21 -0700270 if (!interface)
271 return;
272
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000273 if (quirks->mailled)
274 interface->capability |= ACER_CAP_MAILLED;
275
276 if (quirks->brightness)
277 interface->capability |= ACER_CAP_BRIGHTNESS;
278}
279
280static int dmi_matched(const struct dmi_system_id *dmi)
281{
282 quirks = dmi->driver_data;
Axel Lind53bf0f2010-06-30 13:32:16 +0800283 return 1;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000284}
285
286static struct quirk_entry quirk_unknown = {
287};
288
Carlos Corbacho9991d9f2008-06-21 09:09:22 +0100289static struct quirk_entry quirk_acer_aspire_1520 = {
290 .brightness = -1,
291};
292
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000293static struct quirk_entry quirk_acer_travelmate_2490 = {
294 .mailled = 1,
295};
296
297/* This AMW0 laptop has no bluetooth */
298static struct quirk_entry quirk_medion_md_98300 = {
299 .wireless = 1,
300};
301
Carlos Corbacho6f061ab2008-06-21 09:09:38 +0100302static struct quirk_entry quirk_fujitsu_amilo_li_1718 = {
303 .wireless = 2,
304};
305
Carlos Corbachoa74dd5f2009-04-04 09:33:29 +0100306/* The Aspire One has a dummy ACPI-WMI interface - disable it */
307static struct dmi_system_id __devinitdata acer_blacklist[] = {
308 {
309 .ident = "Acer Aspire One (SSD)",
310 .matches = {
311 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
312 DMI_MATCH(DMI_PRODUCT_NAME, "AOA110"),
313 },
314 },
315 {
316 .ident = "Acer Aspire One (HDD)",
317 .matches = {
318 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
319 DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"),
320 },
321 },
322 {}
323};
324
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000325static struct dmi_system_id acer_quirks[] = {
326 {
327 .callback = dmi_matched,
Carlos Corbacho9991d9f2008-06-21 09:09:22 +0100328 .ident = "Acer Aspire 1360",
329 .matches = {
330 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
331 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
332 },
333 .driver_data = &quirk_acer_aspire_1520,
334 },
335 {
336 .callback = dmi_matched,
337 .ident = "Acer Aspire 1520",
338 .matches = {
339 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
340 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"),
341 },
342 .driver_data = &quirk_acer_aspire_1520,
343 },
344 {
345 .callback = dmi_matched,
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000346 .ident = "Acer Aspire 3100",
347 .matches = {
348 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
349 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
350 },
351 .driver_data = &quirk_acer_travelmate_2490,
352 },
353 {
354 .callback = dmi_matched,
Carlos Corbachoed9cfe92008-03-12 20:13:00 +0000355 .ident = "Acer Aspire 3610",
356 .matches = {
357 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
358 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3610"),
359 },
360 .driver_data = &quirk_acer_travelmate_2490,
361 },
362 {
363 .callback = dmi_matched,
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000364 .ident = "Acer Aspire 5100",
365 .matches = {
366 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
367 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
368 },
369 .driver_data = &quirk_acer_travelmate_2490,
370 },
371 {
372 .callback = dmi_matched,
Carlos Corbachoed9cfe92008-03-12 20:13:00 +0000373 .ident = "Acer Aspire 5610",
374 .matches = {
375 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
376 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
377 },
378 .driver_data = &quirk_acer_travelmate_2490,
379 },
380 {
381 .callback = dmi_matched,
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000382 .ident = "Acer Aspire 5630",
383 .matches = {
384 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
385 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
386 },
387 .driver_data = &quirk_acer_travelmate_2490,
388 },
389 {
390 .callback = dmi_matched,
391 .ident = "Acer Aspire 5650",
392 .matches = {
393 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
394 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
395 },
396 .driver_data = &quirk_acer_travelmate_2490,
397 },
398 {
399 .callback = dmi_matched,
400 .ident = "Acer Aspire 5680",
401 .matches = {
402 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
403 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
404 },
405 .driver_data = &quirk_acer_travelmate_2490,
406 },
407 {
408 .callback = dmi_matched,
409 .ident = "Acer Aspire 9110",
410 .matches = {
411 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
412 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
413 },
414 .driver_data = &quirk_acer_travelmate_2490,
415 },
416 {
417 .callback = dmi_matched,
418 .ident = "Acer TravelMate 2490",
419 .matches = {
420 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
421 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
422 },
423 .driver_data = &quirk_acer_travelmate_2490,
424 },
425 {
426 .callback = dmi_matched,
Carlos Corbacho262ee352008-02-16 00:02:56 +0000427 .ident = "Acer TravelMate 4200",
428 .matches = {
429 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
430 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4200"),
431 },
432 .driver_data = &quirk_acer_travelmate_2490,
433 },
434 {
435 .callback = dmi_matched,
Carlos Corbacho6f061ab2008-06-21 09:09:38 +0100436 .ident = "Fujitsu Siemens Amilo Li 1718",
437 .matches = {
438 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
439 DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Li 1718"),
440 },
441 .driver_data = &quirk_fujitsu_amilo_li_1718,
442 },
443 {
444 .callback = dmi_matched,
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000445 .ident = "Medion MD 98300",
446 .matches = {
447 DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
448 DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
449 },
450 .driver_data = &quirk_medion_md_98300,
451 },
452 {}
453};
454
455/* Find which quirks are needed for a particular vendor/ model pair */
456static void find_quirks(void)
457{
458 if (!force_series) {
459 dmi_check_system(acer_quirks);
460 } else if (force_series == 2490) {
461 quirks = &quirk_acer_travelmate_2490;
462 }
463
464 if (quirks == NULL)
465 quirks = &quirk_unknown;
466
467 set_quirks();
468}
469
470/*
471 * General interface convenience methods
472 */
473
474static bool has_cap(u32 cap)
475{
476 if ((interface->capability & cap) != 0)
477 return 1;
478
479 return 0;
480}
481
482/*
483 * AMW0 (V1) interface
484 */
485struct wmab_args {
486 u32 eax;
487 u32 ebx;
488 u32 ecx;
489 u32 edx;
490};
491
492struct wmab_ret {
493 u32 eax;
494 u32 ebx;
495 u32 ecx;
496 u32 edx;
497 u32 eex;
498};
499
500static acpi_status wmab_execute(struct wmab_args *regbuf,
501struct acpi_buffer *result)
502{
503 struct acpi_buffer input;
504 acpi_status status;
505 input.length = sizeof(struct wmab_args);
506 input.pointer = (u8 *)regbuf;
507
508 status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
509
510 return status;
511}
512
513static acpi_status AMW0_get_u32(u32 *value, u32 cap,
514struct wmi_interface *iface)
515{
516 int err;
517 u8 result;
518
519 switch (cap) {
520 case ACER_CAP_MAILLED:
521 switch (quirks->mailled) {
522 default:
523 err = ec_read(0xA, &result);
524 if (err)
525 return AE_ERROR;
526 *value = (result >> 7) & 0x1;
527 return AE_OK;
528 }
529 break;
530 case ACER_CAP_WIRELESS:
531 switch (quirks->wireless) {
532 case 1:
533 err = ec_read(0x7B, &result);
534 if (err)
535 return AE_ERROR;
536 *value = result & 0x1;
537 return AE_OK;
Carlos Corbacho6f061ab2008-06-21 09:09:38 +0100538 case 2:
539 err = ec_read(0x71, &result);
540 if (err)
541 return AE_ERROR;
542 *value = result & 0x1;
543 return AE_OK;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000544 default:
545 err = ec_read(0xA, &result);
546 if (err)
547 return AE_ERROR;
548 *value = (result >> 2) & 0x1;
549 return AE_OK;
550 }
551 break;
552 case ACER_CAP_BLUETOOTH:
553 switch (quirks->bluetooth) {
554 default:
555 err = ec_read(0xA, &result);
556 if (err)
557 return AE_ERROR;
558 *value = (result >> 4) & 0x1;
559 return AE_OK;
560 }
561 break;
562 case ACER_CAP_BRIGHTNESS:
563 switch (quirks->brightness) {
564 default:
565 err = ec_read(0x83, &result);
566 if (err)
567 return AE_ERROR;
568 *value = result;
569 return AE_OK;
570 }
571 break;
572 default:
Lin Ming08237972008-08-08 11:57:11 +0800573 return AE_ERROR;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000574 }
575 return AE_OK;
576}
577
578static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
579{
580 struct wmab_args args;
581
582 args.eax = ACER_AMW0_WRITE;
583 args.ebx = value ? (1<<8) : 0;
584 args.ecx = args.edx = 0;
585
586 switch (cap) {
587 case ACER_CAP_MAILLED:
588 if (value > 1)
589 return AE_BAD_PARAMETER;
590 args.ebx |= ACER_AMW0_MAILLED_MASK;
591 break;
592 case ACER_CAP_WIRELESS:
593 if (value > 1)
594 return AE_BAD_PARAMETER;
595 args.ebx |= ACER_AMW0_WIRELESS_MASK;
596 break;
597 case ACER_CAP_BLUETOOTH:
598 if (value > 1)
599 return AE_BAD_PARAMETER;
600 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
601 break;
602 case ACER_CAP_BRIGHTNESS:
603 if (value > max_brightness)
604 return AE_BAD_PARAMETER;
605 switch (quirks->brightness) {
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000606 default:
Carlos Corbacho4609d022008-02-08 23:51:49 +0000607 return ec_write(0x83, value);
608 break;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000609 }
610 default:
Lin Ming08237972008-08-08 11:57:11 +0800611 return AE_ERROR;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000612 }
613
614 /* Actually do the set */
615 return wmab_execute(&args, NULL);
616}
617
618static acpi_status AMW0_find_mailled(void)
619{
620 struct wmab_args args;
621 struct wmab_ret ret;
622 acpi_status status = AE_OK;
623 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
624 union acpi_object *obj;
625
626 args.eax = 0x86;
627 args.ebx = args.ecx = args.edx = 0;
628
629 status = wmab_execute(&args, &out);
630 if (ACPI_FAILURE(status))
631 return status;
632
633 obj = (union acpi_object *) out.pointer;
634 if (obj && obj->type == ACPI_TYPE_BUFFER &&
635 obj->buffer.length == sizeof(struct wmab_ret)) {
636 ret = *((struct wmab_ret *) obj->buffer.pointer);
637 } else {
Axel Lin7677fbd2010-07-20 15:19:53 -0700638 kfree(out.pointer);
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000639 return AE_ERROR;
640 }
641
642 if (ret.eex & 0x1)
643 interface->capability |= ACER_CAP_MAILLED;
644
645 kfree(out.pointer);
646
647 return AE_OK;
648}
649
650static acpi_status AMW0_set_capabilities(void)
651{
652 struct wmab_args args;
653 struct wmab_ret ret;
Axel Lin7677fbd2010-07-20 15:19:53 -0700654 acpi_status status;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000655 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
656 union acpi_object *obj;
657
Carlos Corbacho5753dd52008-06-21 09:09:48 +0100658 /*
659 * On laptops with this strange GUID (non Acer), normal probing doesn't
660 * work.
661 */
662 if (wmi_has_guid(AMW0_GUID2)) {
663 interface->capability |= ACER_CAP_WIRELESS;
664 return AE_OK;
665 }
666
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000667 args.eax = ACER_AMW0_WRITE;
668 args.ecx = args.edx = 0;
669
670 args.ebx = 0xa2 << 8;
671 args.ebx |= ACER_AMW0_WIRELESS_MASK;
672
673 status = wmab_execute(&args, &out);
674 if (ACPI_FAILURE(status))
675 return status;
676
Axel Lin7677fbd2010-07-20 15:19:53 -0700677 obj = out.pointer;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000678 if (obj && obj->type == ACPI_TYPE_BUFFER &&
679 obj->buffer.length == sizeof(struct wmab_ret)) {
680 ret = *((struct wmab_ret *) obj->buffer.pointer);
681 } else {
Axel Lin7677fbd2010-07-20 15:19:53 -0700682 status = AE_ERROR;
683 goto out;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000684 }
685
686 if (ret.eax & 0x1)
687 interface->capability |= ACER_CAP_WIRELESS;
688
689 args.ebx = 2 << 8;
690 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
691
Axel Lin7677fbd2010-07-20 15:19:53 -0700692 /*
693 * It's ok to use existing buffer for next wmab_execute call.
694 * But we need to kfree(out.pointer) if next wmab_execute fail.
695 */
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000696 status = wmab_execute(&args, &out);
697 if (ACPI_FAILURE(status))
Axel Lin7677fbd2010-07-20 15:19:53 -0700698 goto out;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000699
700 obj = (union acpi_object *) out.pointer;
701 if (obj && obj->type == ACPI_TYPE_BUFFER
702 && obj->buffer.length == sizeof(struct wmab_ret)) {
703 ret = *((struct wmab_ret *) obj->buffer.pointer);
704 } else {
Axel Lin7677fbd2010-07-20 15:19:53 -0700705 status = AE_ERROR;
706 goto out;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000707 }
708
709 if (ret.eax & 0x1)
710 interface->capability |= ACER_CAP_BLUETOOTH;
711
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000712 /*
713 * This appears to be safe to enable, since all Wistron based laptops
714 * appear to use the same EC register for brightness, even if they
715 * differ for wireless, etc
716 */
Carlos Corbacho9991d9f2008-06-21 09:09:22 +0100717 if (quirks->brightness >= 0)
718 interface->capability |= ACER_CAP_BRIGHTNESS;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000719
Axel Lin7677fbd2010-07-20 15:19:53 -0700720 status = AE_OK;
721out:
722 kfree(out.pointer);
723 return status;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000724}
725
726static struct wmi_interface AMW0_interface = {
727 .type = ACER_AMW0,
728};
729
730static struct wmi_interface AMW0_V2_interface = {
731 .type = ACER_AMW0_V2,
732};
733
734/*
735 * New interface (The WMID interface)
736 */
737static acpi_status
738WMI_execute_u32(u32 method_id, u32 in, u32 *out)
739{
740 struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
741 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
742 union acpi_object *obj;
743 u32 tmp;
744 acpi_status status;
745
746 status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
747
748 if (ACPI_FAILURE(status))
749 return status;
750
751 obj = (union acpi_object *) result.pointer;
752 if (obj && obj->type == ACPI_TYPE_BUFFER &&
753 obj->buffer.length == sizeof(u32)) {
754 tmp = *((u32 *) obj->buffer.pointer);
755 } else {
756 tmp = 0;
757 }
758
759 if (out)
760 *out = tmp;
761
762 kfree(result.pointer);
763
764 return status;
765}
766
767static acpi_status WMID_get_u32(u32 *value, u32 cap,
768struct wmi_interface *iface)
769{
770 acpi_status status;
771 u8 tmp;
772 u32 result, method_id = 0;
773
774 switch (cap) {
775 case ACER_CAP_WIRELESS:
776 method_id = ACER_WMID_GET_WIRELESS_METHODID;
777 break;
778 case ACER_CAP_BLUETOOTH:
779 method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
780 break;
781 case ACER_CAP_BRIGHTNESS:
782 method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
783 break;
784 case ACER_CAP_THREEG:
785 method_id = ACER_WMID_GET_THREEG_METHODID;
786 break;
787 case ACER_CAP_MAILLED:
788 if (quirks->mailled == 1) {
789 ec_read(0x9f, &tmp);
790 *value = tmp & 0x1;
791 return 0;
792 }
793 default:
Lin Ming08237972008-08-08 11:57:11 +0800794 return AE_ERROR;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000795 }
796 status = WMI_execute_u32(method_id, 0, &result);
797
798 if (ACPI_SUCCESS(status))
799 *value = (u8)result;
800
801 return status;
802}
803
804static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
805{
806 u32 method_id = 0;
807 char param;
808
809 switch (cap) {
810 case ACER_CAP_BRIGHTNESS:
811 if (value > max_brightness)
812 return AE_BAD_PARAMETER;
813 method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
814 break;
815 case ACER_CAP_WIRELESS:
816 if (value > 1)
817 return AE_BAD_PARAMETER;
818 method_id = ACER_WMID_SET_WIRELESS_METHODID;
819 break;
820 case ACER_CAP_BLUETOOTH:
821 if (value > 1)
822 return AE_BAD_PARAMETER;
823 method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
824 break;
825 case ACER_CAP_THREEG:
826 if (value > 1)
827 return AE_BAD_PARAMETER;
828 method_id = ACER_WMID_SET_THREEG_METHODID;
829 break;
830 case ACER_CAP_MAILLED:
831 if (value > 1)
832 return AE_BAD_PARAMETER;
833 if (quirks->mailled == 1) {
834 param = value ? 0x92 : 0x93;
Dmitry Torokhov181d6832009-09-16 01:06:43 -0700835 i8042_lock_chip();
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000836 i8042_command(&param, 0x1059);
Dmitry Torokhov181d6832009-09-16 01:06:43 -0700837 i8042_unlock_chip();
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000838 return 0;
839 }
840 break;
841 default:
Lin Ming08237972008-08-08 11:57:11 +0800842 return AE_ERROR;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000843 }
844 return WMI_execute_u32(method_id, (u32)value, NULL);
845}
846
Lee, Chun-Yi6c3df882010-12-07 10:29:23 +0800847static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
848{
849 struct hotkey_function_type_aa *type_aa;
850
851 /* We are looking for OEM-specific Type AAh */
852 if (header->type != 0xAA)
853 return;
854
855 has_type_aa = true;
856 type_aa = (struct hotkey_function_type_aa *) header;
857
Lee, Chun-Yicae15702011-03-16 18:52:36 +0800858 pr_info("Function bitmap for Communication Button: 0x%x\n",
Lee, Chun-Yi6c3df882010-12-07 10:29:23 +0800859 type_aa->commun_func_bitmap);
860
861 if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS)
862 interface->capability |= ACER_CAP_WIRELESS;
863 if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_THREEG)
864 interface->capability |= ACER_CAP_THREEG;
865 if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH)
866 interface->capability |= ACER_CAP_BLUETOOTH;
867}
868
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000869static acpi_status WMID_set_capabilities(void)
870{
871 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
872 union acpi_object *obj;
873 acpi_status status;
874 u32 devices;
875
876 status = wmi_query_block(WMID_GUID2, 1, &out);
877 if (ACPI_FAILURE(status))
878 return status;
879
880 obj = (union acpi_object *) out.pointer;
881 if (obj && obj->type == ACPI_TYPE_BUFFER &&
882 obj->buffer.length == sizeof(u32)) {
883 devices = *((u32 *) obj->buffer.pointer);
884 } else {
Axel Lin66904862010-07-20 15:19:52 -0700885 kfree(out.pointer);
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000886 return AE_ERROR;
887 }
888
Lee, Chun-Yi6c3df882010-12-07 10:29:23 +0800889 dmi_walk(type_aa_dmi_decode, NULL);
890 if (!has_type_aa) {
891 interface->capability |= ACER_CAP_WIRELESS;
892 interface->capability |= ACER_CAP_THREEG;
893 if (devices & 0x10)
894 interface->capability |= ACER_CAP_BLUETOOTH;
895 }
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000896
897 /* WMID always provides brightness methods */
898 interface->capability |= ACER_CAP_BRIGHTNESS;
899
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000900 if (!(devices & 0x20))
901 max_brightness = 0x9;
902
Axel Lin66904862010-07-20 15:19:52 -0700903 kfree(out.pointer);
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000904 return status;
905}
906
907static struct wmi_interface wmid_interface = {
908 .type = ACER_WMID,
909};
910
911/*
912 * Generic Device (interface-independent)
913 */
914
915static acpi_status get_u32(u32 *value, u32 cap)
916{
Lin Ming08237972008-08-08 11:57:11 +0800917 acpi_status status = AE_ERROR;
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000918
919 switch (interface->type) {
920 case ACER_AMW0:
921 status = AMW0_get_u32(value, cap, interface);
922 break;
923 case ACER_AMW0_V2:
924 if (cap == ACER_CAP_MAILLED) {
925 status = AMW0_get_u32(value, cap, interface);
926 break;
927 }
928 case ACER_WMID:
929 status = WMID_get_u32(value, cap, interface);
930 break;
931 }
932
933 return status;
934}
935
936static acpi_status set_u32(u32 value, u32 cap)
937{
Carlos Corbacho5c742b42008-08-06 19:13:56 +0100938 acpi_status status;
939
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000940 if (interface->capability & cap) {
941 switch (interface->type) {
942 case ACER_AMW0:
943 return AMW0_set_u32(value, cap, interface);
944 case ACER_AMW0_V2:
Carlos Corbacho5c742b42008-08-06 19:13:56 +0100945 if (cap == ACER_CAP_MAILLED)
946 return AMW0_set_u32(value, cap, interface);
947
948 /*
949 * On some models, some WMID methods don't toggle
950 * properly. For those cases, we want to run the AMW0
951 * method afterwards to be certain we've really toggled
952 * the device state.
953 */
954 if (cap == ACER_CAP_WIRELESS ||
955 cap == ACER_CAP_BLUETOOTH) {
956 status = WMID_set_u32(value, cap, interface);
957 if (ACPI_FAILURE(status))
958 return status;
959
960 return AMW0_set_u32(value, cap, interface);
961 }
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000962 case ACER_WMID:
963 return WMID_set_u32(value, cap, interface);
964 default:
965 return AE_BAD_PARAMETER;
966 }
967 }
968 return AE_BAD_PARAMETER;
969}
970
971static void __init acer_commandline_init(void)
972{
973 /*
974 * These will all fail silently if the value given is invalid, or the
975 * capability isn't available on the given interface
976 */
Lee, Chun-Yic2647b52011-04-15 18:42:47 +0800977 if (mailled >= 0)
978 set_u32(mailled, ACER_CAP_MAILLED);
979 if (!has_type_aa && threeg >= 0)
Lee, Chun-Yi6c3df882010-12-07 10:29:23 +0800980 set_u32(threeg, ACER_CAP_THREEG);
Lee, Chun-Yic2647b52011-04-15 18:42:47 +0800981 if (brightness >= 0)
982 set_u32(brightness, ACER_CAP_BRIGHTNESS);
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000983}
984
985/*
986 * LED device (Mail LED only, no other LEDs known yet)
987 */
988static void mail_led_set(struct led_classdev *led_cdev,
989enum led_brightness value)
990{
991 set_u32(value, ACER_CAP_MAILLED);
992}
993
994static struct led_classdev mail_led = {
Carlos Corbacho343c0042008-02-24 13:34:18 +0000995 .name = "acer-wmi::mail",
Carlos Corbacho745a5d22008-02-05 02:17:10 +0000996 .brightness_set = mail_led_set,
997};
998
Sam Ravnborg7560e382008-02-17 13:22:54 +0100999static int __devinit acer_led_init(struct device *dev)
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001000{
1001 return led_classdev_register(dev, &mail_led);
1002}
1003
1004static void acer_led_exit(void)
1005{
Pali Rohár9a0b74f2011-02-26 21:18:58 +01001006 set_u32(LED_OFF, ACER_CAP_MAILLED);
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001007 led_classdev_unregister(&mail_led);
1008}
1009
1010/*
1011 * Backlight device
1012 */
1013static struct backlight_device *acer_backlight_device;
1014
1015static int read_brightness(struct backlight_device *bd)
1016{
1017 u32 value;
1018 get_u32(&value, ACER_CAP_BRIGHTNESS);
1019 return value;
1020}
1021
1022static int update_bl_status(struct backlight_device *bd)
1023{
Carlos Corbachof2b585b2008-06-21 09:09:27 +01001024 int intensity = bd->props.brightness;
1025
1026 if (bd->props.power != FB_BLANK_UNBLANK)
1027 intensity = 0;
1028 if (bd->props.fb_blank != FB_BLANK_UNBLANK)
1029 intensity = 0;
1030
1031 set_u32(intensity, ACER_CAP_BRIGHTNESS);
1032
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001033 return 0;
1034}
1035
Lionel Debrouxacc24722010-11-16 14:14:02 +01001036static const struct backlight_ops acer_bl_ops = {
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001037 .get_brightness = read_brightness,
1038 .update_status = update_bl_status,
1039};
1040
Sam Ravnborg7560e382008-02-17 13:22:54 +01001041static int __devinit acer_backlight_init(struct device *dev)
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001042{
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001043 struct backlight_properties props;
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001044 struct backlight_device *bd;
1045
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001046 memset(&props, 0, sizeof(struct backlight_properties));
Matthew Garrettbb7ca742011-03-22 16:30:21 -07001047 props.type = BACKLIGHT_PLATFORM;
Matthew Garretta19a6ee2010-02-17 16:39:44 -05001048 props.max_brightness = max_brightness;
1049 bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops,
1050 &props);
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001051 if (IS_ERR(bd)) {
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001052 pr_err("Could not register Acer backlight device\n");
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001053 acer_backlight_device = NULL;
1054 return PTR_ERR(bd);
1055 }
1056
1057 acer_backlight_device = bd;
1058
Carlos Corbachof2b585b2008-06-21 09:09:27 +01001059 bd->props.power = FB_BLANK_UNBLANK;
Carlos Corbacho6f6ef822009-12-26 19:24:31 +00001060 bd->props.brightness = read_brightness(bd);
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001061 backlight_update_status(bd);
1062 return 0;
1063}
1064
Sam Ravnborg7560e382008-02-17 13:22:54 +01001065static void acer_backlight_exit(void)
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001066{
1067 backlight_device_unregister(acer_backlight_device);
1068}
1069
Lee, Chun-Yib3c90922010-12-07 10:29:22 +08001070static acpi_status wmid3_get_device_status(u32 *value, u16 device)
1071{
1072 struct wmid3_gds_return_value return_value;
1073 acpi_status status;
1074 union acpi_object *obj;
1075 struct wmid3_gds_input_param params = {
1076 .function_num = 0x1,
1077 .hotkey_number = 0x01,
1078 .devices = device,
1079 };
1080 struct acpi_buffer input = {
1081 sizeof(struct wmid3_gds_input_param),
1082 &params
1083 };
1084 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
1085
1086 status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
1087 if (ACPI_FAILURE(status))
1088 return status;
1089
1090 obj = output.pointer;
1091
1092 if (!obj)
1093 return AE_ERROR;
1094 else if (obj->type != ACPI_TYPE_BUFFER) {
1095 kfree(obj);
1096 return AE_ERROR;
1097 }
1098 if (obj->buffer.length != 8) {
Joe Perches249c7202011-03-29 15:21:34 -07001099 pr_warn("Unknown buffer length %d\n", obj->buffer.length);
Lee, Chun-Yib3c90922010-12-07 10:29:22 +08001100 kfree(obj);
1101 return AE_ERROR;
1102 }
1103
1104 return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
1105 kfree(obj);
1106
1107 if (return_value.error_code || return_value.ec_return_value)
Joe Perches249c7202011-03-29 15:21:34 -07001108 pr_warn("Get Device Status failed: 0x%x - 0x%x\n",
1109 return_value.error_code,
Lee, Chun-Yib3c90922010-12-07 10:29:22 +08001110 return_value.ec_return_value);
1111 else
1112 *value = !!(return_value.devices & device);
1113
1114 return status;
1115}
1116
Lee, Chun-Yi466449c2010-12-13 10:02:41 +08001117static acpi_status get_device_status(u32 *value, u32 cap)
1118{
1119 if (wmi_has_guid(WMID_GUID3)) {
1120 u16 device;
1121
1122 switch (cap) {
1123 case ACER_CAP_WIRELESS:
1124 device = ACER_WMID3_GDS_WIRELESS;
1125 break;
1126 case ACER_CAP_BLUETOOTH:
1127 device = ACER_WMID3_GDS_BLUETOOTH;
1128 break;
1129 case ACER_CAP_THREEG:
1130 device = ACER_WMID3_GDS_THREEG;
1131 break;
1132 default:
1133 return AE_ERROR;
1134 }
1135 return wmid3_get_device_status(value, device);
1136
1137 } else {
1138 return get_u32(value, cap);
1139 }
1140}
1141
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001142/*
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001143 * Rfkill devices
1144 */
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001145static void acer_rfkill_update(struct work_struct *ignored);
1146static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update);
1147static void acer_rfkill_update(struct work_struct *ignored)
1148{
1149 u32 state;
1150 acpi_status status;
1151
1152 status = get_u32(&state, ACER_CAP_WIRELESS);
1153 if (ACPI_SUCCESS(status))
Troy Mourea8784172009-06-17 11:51:56 +01001154 rfkill_set_sw_state(wireless_rfkill, !state);
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001155
1156 if (has_cap(ACER_CAP_BLUETOOTH)) {
1157 status = get_u32(&state, ACER_CAP_BLUETOOTH);
1158 if (ACPI_SUCCESS(status))
Troy Mourea8784172009-06-17 11:51:56 +01001159 rfkill_set_sw_state(bluetooth_rfkill, !state);
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001160 }
1161
Lee, Chun-Yib3c90922010-12-07 10:29:22 +08001162 if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) {
1163 status = wmid3_get_device_status(&state,
1164 ACER_WMID3_GDS_THREEG);
1165 if (ACPI_SUCCESS(status))
1166 rfkill_set_sw_state(threeg_rfkill, !state);
1167 }
1168
Carlos Corbachoae3a1b42008-10-10 17:33:35 +01001169 schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001170}
1171
Johannes Berg19d337d2009-06-02 13:01:37 +02001172static int acer_rfkill_set(void *data, bool blocked)
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001173{
1174 acpi_status status;
Johannes Berg19d337d2009-06-02 13:01:37 +02001175 u32 cap = (unsigned long)data;
Lee, Chun-Yi8215af02011-03-28 16:52:02 +08001176
1177 if (rfkill_inited) {
1178 status = set_u32(!blocked, cap);
1179 if (ACPI_FAILURE(status))
1180 return -ENODEV;
1181 }
1182
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001183 return 0;
1184}
1185
Johannes Berg19d337d2009-06-02 13:01:37 +02001186static const struct rfkill_ops acer_rfkill_ops = {
1187 .set_block = acer_rfkill_set,
1188};
1189
1190static struct rfkill *acer_rfkill_register(struct device *dev,
1191 enum rfkill_type type,
1192 char *name, u32 cap)
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001193{
1194 int err;
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001195 struct rfkill *rfkill_dev;
Lee, Chun-Yi466449c2010-12-13 10:02:41 +08001196 u32 state;
1197 acpi_status status;
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001198
Johannes Berg19d337d2009-06-02 13:01:37 +02001199 rfkill_dev = rfkill_alloc(name, dev, type,
1200 &acer_rfkill_ops,
1201 (void *)(unsigned long)cap);
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001202 if (!rfkill_dev)
1203 return ERR_PTR(-ENOMEM);
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001204
Lee, Chun-Yi466449c2010-12-13 10:02:41 +08001205 status = get_device_status(&state, cap);
Lee, Chun-Yi466449c2010-12-13 10:02:41 +08001206
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001207 err = rfkill_register(rfkill_dev);
1208 if (err) {
Johannes Berg19d337d2009-06-02 13:01:37 +02001209 rfkill_destroy(rfkill_dev);
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001210 return ERR_PTR(err);
1211 }
Lee, Chun-Yi8215af02011-03-28 16:52:02 +08001212
1213 if (ACPI_SUCCESS(status))
1214 rfkill_set_sw_state(rfkill_dev, !state);
1215
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001216 return rfkill_dev;
1217}
1218
1219static int acer_rfkill_init(struct device *dev)
1220{
1221 wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
1222 "acer-wireless", ACER_CAP_WIRELESS);
1223 if (IS_ERR(wireless_rfkill))
1224 return PTR_ERR(wireless_rfkill);
1225
1226 if (has_cap(ACER_CAP_BLUETOOTH)) {
1227 bluetooth_rfkill = acer_rfkill_register(dev,
1228 RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
1229 ACER_CAP_BLUETOOTH);
1230 if (IS_ERR(bluetooth_rfkill)) {
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001231 rfkill_unregister(wireless_rfkill);
Johannes Berg19d337d2009-06-02 13:01:37 +02001232 rfkill_destroy(wireless_rfkill);
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001233 return PTR_ERR(bluetooth_rfkill);
1234 }
1235 }
1236
Lee, Chun-Yib3c90922010-12-07 10:29:22 +08001237 if (has_cap(ACER_CAP_THREEG)) {
1238 threeg_rfkill = acer_rfkill_register(dev,
1239 RFKILL_TYPE_WWAN, "acer-threeg",
1240 ACER_CAP_THREEG);
1241 if (IS_ERR(threeg_rfkill)) {
1242 rfkill_unregister(wireless_rfkill);
1243 rfkill_destroy(wireless_rfkill);
1244 rfkill_unregister(bluetooth_rfkill);
1245 rfkill_destroy(bluetooth_rfkill);
1246 return PTR_ERR(threeg_rfkill);
1247 }
1248 }
1249
Lee, Chun-Yi8215af02011-03-28 16:52:02 +08001250 rfkill_inited = true;
1251
Lee, Chun-Yi70a9b902011-03-28 06:34:13 -04001252 if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
1253 schedule_delayed_work(&acer_rfkill_work,
1254 round_jiffies_relative(HZ));
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001255
1256 return 0;
1257}
1258
1259static void acer_rfkill_exit(void)
1260{
Lee, Chun-Yi70a9b902011-03-28 06:34:13 -04001261 if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
1262 cancel_delayed_work_sync(&acer_rfkill_work);
Johannes Berg19d337d2009-06-02 13:01:37 +02001263
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001264 rfkill_unregister(wireless_rfkill);
Johannes Berg19d337d2009-06-02 13:01:37 +02001265 rfkill_destroy(wireless_rfkill);
1266
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001267 if (has_cap(ACER_CAP_BLUETOOTH)) {
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001268 rfkill_unregister(bluetooth_rfkill);
Johannes Berg19d337d2009-06-02 13:01:37 +02001269 rfkill_destroy(bluetooth_rfkill);
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001270 }
Lee, Chun-Yib3c90922010-12-07 10:29:22 +08001271
1272 if (has_cap(ACER_CAP_THREEG)) {
1273 rfkill_unregister(threeg_rfkill);
1274 rfkill_destroy(threeg_rfkill);
1275 }
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001276 return;
1277}
1278
1279/*
Carlos Corbacho7d9a06d2008-10-08 21:40:26 +01001280 * sysfs interface
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001281 */
Carlos Corbacho7d9a06d2008-10-08 21:40:26 +01001282static ssize_t show_bool_threeg(struct device *dev,
1283 struct device_attribute *attr, char *buf)
1284{
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001285 u32 result; \
Lee, Chun-Yib3c90922010-12-07 10:29:22 +08001286 acpi_status status;
1287 if (wmi_has_guid(WMID_GUID3))
1288 status = wmid3_get_device_status(&result,
1289 ACER_WMID3_GDS_THREEG);
1290 else
1291 status = get_u32(&result, ACER_CAP_THREEG);
Carlos Corbacho7d9a06d2008-10-08 21:40:26 +01001292 if (ACPI_SUCCESS(status))
1293 return sprintf(buf, "%u\n", result);
1294 return sprintf(buf, "Read error\n");
1295}
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001296
Carlos Corbacho7d9a06d2008-10-08 21:40:26 +01001297static ssize_t set_bool_threeg(struct device *dev,
1298 struct device_attribute *attr, const char *buf, size_t count)
1299{
1300 u32 tmp = simple_strtoul(buf, NULL, 10);
1301 acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
1302 if (ACPI_FAILURE(status))
1303 return -EINVAL;
1304 return count;
1305}
Vasiliy Kulikovb80b1682011-02-04 15:23:56 +03001306static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg,
Carlos Corbacho7d9a06d2008-10-08 21:40:26 +01001307 set_bool_threeg);
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001308
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001309static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
1310 char *buf)
1311{
1312 switch (interface->type) {
1313 case ACER_AMW0:
1314 return sprintf(buf, "AMW0\n");
1315 case ACER_AMW0_V2:
1316 return sprintf(buf, "AMW0 v2\n");
1317 case ACER_WMID:
1318 return sprintf(buf, "WMID\n");
1319 default:
1320 return sprintf(buf, "Error!\n");
1321 }
1322}
1323
Axel Lin370525d2010-06-30 10:20:23 +08001324static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL);
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001325
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +08001326static void acer_wmi_notify(u32 value, void *context)
1327{
1328 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
1329 union acpi_object *obj;
1330 struct event_return_value return_value;
1331 acpi_status status;
1332
1333 status = wmi_get_event_data(value, &response);
1334 if (status != AE_OK) {
Joe Perches249c7202011-03-29 15:21:34 -07001335 pr_warn("bad event status 0x%x\n", status);
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +08001336 return;
1337 }
1338
1339 obj = (union acpi_object *)response.pointer;
1340
1341 if (!obj)
1342 return;
1343 if (obj->type != ACPI_TYPE_BUFFER) {
Joe Perches249c7202011-03-29 15:21:34 -07001344 pr_warn("Unknown response received %d\n", obj->type);
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +08001345 kfree(obj);
1346 return;
1347 }
1348 if (obj->buffer.length != 8) {
Joe Perches249c7202011-03-29 15:21:34 -07001349 pr_warn("Unknown buffer length %d\n", obj->buffer.length);
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +08001350 kfree(obj);
1351 return;
1352 }
1353
1354 return_value = *((struct event_return_value *)obj->buffer.pointer);
1355 kfree(obj);
1356
1357 switch (return_value.function) {
1358 case WMID_HOTKEY_EVENT:
Lee, Chun-Yi70a9b902011-03-28 06:34:13 -04001359 if (return_value.device_state) {
1360 u16 device_state = return_value.device_state;
Melchior FRANZ8ae68de2011-05-24 10:35:55 +02001361 pr_debug("device state: 0x%x\n", device_state);
Lee, Chun-Yi70a9b902011-03-28 06:34:13 -04001362 if (has_cap(ACER_CAP_WIRELESS))
1363 rfkill_set_sw_state(wireless_rfkill,
1364 !(device_state & ACER_WMID3_GDS_WIRELESS));
1365 if (has_cap(ACER_CAP_BLUETOOTH))
1366 rfkill_set_sw_state(bluetooth_rfkill,
1367 !(device_state & ACER_WMID3_GDS_BLUETOOTH));
1368 if (has_cap(ACER_CAP_THREEG))
1369 rfkill_set_sw_state(threeg_rfkill,
1370 !(device_state & ACER_WMID3_GDS_THREEG));
1371 }
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +08001372 if (!sparse_keymap_report_event(acer_wmi_input_dev,
1373 return_value.key_num, 1, true))
Joe Perches249c7202011-03-29 15:21:34 -07001374 pr_warn("Unknown key number - 0x%x\n",
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +08001375 return_value.key_num);
1376 break;
1377 default:
Joe Perches249c7202011-03-29 15:21:34 -07001378 pr_warn("Unknown function number - %d - %d\n",
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +08001379 return_value.function, return_value.key_num);
1380 break;
1381 }
1382}
1383
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -05001384static acpi_status
1385wmid3_set_lm_mode(struct lm_input_params *params,
1386 struct lm_return_value *return_value)
1387{
1388 acpi_status status;
1389 union acpi_object *obj;
1390
1391 struct acpi_buffer input = { sizeof(struct lm_input_params), params };
1392 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
1393
1394 status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output);
1395 if (ACPI_FAILURE(status))
1396 return status;
1397
1398 obj = output.pointer;
1399
1400 if (!obj)
1401 return AE_ERROR;
1402 else if (obj->type != ACPI_TYPE_BUFFER) {
1403 kfree(obj);
1404 return AE_ERROR;
1405 }
1406 if (obj->buffer.length != 4) {
Joe Perches249c7202011-03-29 15:21:34 -07001407 pr_warn("Unknown buffer length %d\n", obj->buffer.length);
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -05001408 kfree(obj);
1409 return AE_ERROR;
1410 }
1411
1412 *return_value = *((struct lm_return_value *)obj->buffer.pointer);
1413 kfree(obj);
1414
1415 return status;
1416}
1417
1418static int acer_wmi_enable_ec_raw(void)
1419{
1420 struct lm_return_value return_value;
1421 acpi_status status;
1422 struct lm_input_params params = {
1423 .function_num = 0x1,
1424 .commun_devices = 0xFFFF,
1425 .devices = 0xFFFF,
1426 .lm_status = 0x00, /* Launch Manager Deactive */
1427 };
1428
1429 status = wmid3_set_lm_mode(&params, &return_value);
1430
1431 if (return_value.error_code || return_value.ec_return_value)
Joe Perches249c7202011-03-29 15:21:34 -07001432 pr_warn("Enabling EC raw mode failed: 0x%x - 0x%x\n",
1433 return_value.error_code,
1434 return_value.ec_return_value);
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -05001435 else
Joe Perches249c7202011-03-29 15:21:34 -07001436 pr_info("Enabled EC raw mode\n");
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -05001437
1438 return status;
1439}
1440
1441static int acer_wmi_enable_lm(void)
1442{
1443 struct lm_return_value return_value;
1444 acpi_status status;
1445 struct lm_input_params params = {
1446 .function_num = 0x1,
1447 .commun_devices = 0xFFFF,
1448 .devices = 0xFFFF,
1449 .lm_status = 0x01, /* Launch Manager Active */
1450 };
1451
1452 status = wmid3_set_lm_mode(&params, &return_value);
1453
1454 if (return_value.error_code || return_value.ec_return_value)
Joe Perches249c7202011-03-29 15:21:34 -07001455 pr_warn("Enabling Launch Manager failed: 0x%x - 0x%x\n",
1456 return_value.error_code,
1457 return_value.ec_return_value);
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -05001458
1459 return status;
1460}
1461
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +08001462static int __init acer_wmi_input_setup(void)
1463{
1464 acpi_status status;
1465 int err;
1466
1467 acer_wmi_input_dev = input_allocate_device();
1468 if (!acer_wmi_input_dev)
1469 return -ENOMEM;
1470
1471 acer_wmi_input_dev->name = "Acer WMI hotkeys";
1472 acer_wmi_input_dev->phys = "wmi/input0";
1473 acer_wmi_input_dev->id.bustype = BUS_HOST;
1474
1475 err = sparse_keymap_setup(acer_wmi_input_dev, acer_wmi_keymap, NULL);
1476 if (err)
1477 goto err_free_dev;
1478
1479 status = wmi_install_notify_handler(ACERWMID_EVENT_GUID,
1480 acer_wmi_notify, NULL);
1481 if (ACPI_FAILURE(status)) {
1482 err = -EIO;
1483 goto err_free_keymap;
1484 }
1485
1486 err = input_register_device(acer_wmi_input_dev);
1487 if (err)
1488 goto err_uninstall_notifier;
1489
1490 return 0;
1491
1492err_uninstall_notifier:
1493 wmi_remove_notify_handler(ACERWMID_EVENT_GUID);
1494err_free_keymap:
1495 sparse_keymap_free(acer_wmi_input_dev);
1496err_free_dev:
1497 input_free_device(acer_wmi_input_dev);
1498 return err;
1499}
1500
1501static void acer_wmi_input_destroy(void)
1502{
1503 wmi_remove_notify_handler(ACERWMID_EVENT_GUID);
1504 sparse_keymap_free(acer_wmi_input_dev);
1505 input_unregister_device(acer_wmi_input_dev);
1506}
1507
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001508/*
Carlos Corbacho81143522008-06-21 09:09:53 +01001509 * debugfs functions
1510 */
1511static u32 get_wmid_devices(void)
1512{
1513 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
1514 union acpi_object *obj;
1515 acpi_status status;
Axel Lin66904862010-07-20 15:19:52 -07001516 u32 devices = 0;
Carlos Corbacho81143522008-06-21 09:09:53 +01001517
1518 status = wmi_query_block(WMID_GUID2, 1, &out);
1519 if (ACPI_FAILURE(status))
1520 return 0;
1521
1522 obj = (union acpi_object *) out.pointer;
1523 if (obj && obj->type == ACPI_TYPE_BUFFER &&
1524 obj->buffer.length == sizeof(u32)) {
Axel Lin66904862010-07-20 15:19:52 -07001525 devices = *((u32 *) obj->buffer.pointer);
Carlos Corbacho81143522008-06-21 09:09:53 +01001526 }
Axel Lin66904862010-07-20 15:19:52 -07001527
1528 kfree(out.pointer);
1529 return devices;
Carlos Corbacho81143522008-06-21 09:09:53 +01001530}
1531
1532/*
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001533 * Platform device
1534 */
1535static int __devinit acer_platform_probe(struct platform_device *device)
1536{
1537 int err;
1538
1539 if (has_cap(ACER_CAP_MAILLED)) {
1540 err = acer_led_init(&device->dev);
1541 if (err)
1542 goto error_mailled;
1543 }
1544
1545 if (has_cap(ACER_CAP_BRIGHTNESS)) {
1546 err = acer_backlight_init(&device->dev);
1547 if (err)
1548 goto error_brightness;
1549 }
1550
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001551 err = acer_rfkill_init(&device->dev);
Andy Whitcroft350e3292009-04-04 09:33:34 +01001552 if (err)
1553 goto error_rfkill;
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001554
1555 return err;
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001556
Andy Whitcroft350e3292009-04-04 09:33:34 +01001557error_rfkill:
1558 if (has_cap(ACER_CAP_BRIGHTNESS))
1559 acer_backlight_exit();
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001560error_brightness:
Andy Whitcroft350e3292009-04-04 09:33:34 +01001561 if (has_cap(ACER_CAP_MAILLED))
1562 acer_led_exit();
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001563error_mailled:
1564 return err;
1565}
1566
1567static int acer_platform_remove(struct platform_device *device)
1568{
1569 if (has_cap(ACER_CAP_MAILLED))
1570 acer_led_exit();
1571 if (has_cap(ACER_CAP_BRIGHTNESS))
1572 acer_backlight_exit();
Carlos Corbacho0606e1a2008-10-08 21:40:21 +01001573
1574 acer_rfkill_exit();
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001575 return 0;
1576}
1577
1578static int acer_platform_suspend(struct platform_device *dev,
1579pm_message_t state)
1580{
1581 u32 value;
1582 struct acer_data *data = &interface->data;
1583
1584 if (!data)
1585 return -ENOMEM;
1586
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001587 if (has_cap(ACER_CAP_MAILLED)) {
1588 get_u32(&value, ACER_CAP_MAILLED);
Pali Rohár9a0b74f2011-02-26 21:18:58 +01001589 set_u32(LED_OFF, ACER_CAP_MAILLED);
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001590 data->mailled = value;
1591 }
1592
1593 if (has_cap(ACER_CAP_BRIGHTNESS)) {
1594 get_u32(&value, ACER_CAP_BRIGHTNESS);
1595 data->brightness = value;
1596 }
1597
1598 return 0;
1599}
1600
1601static int acer_platform_resume(struct platform_device *device)
1602{
1603 struct acer_data *data = &interface->data;
1604
1605 if (!data)
1606 return -ENOMEM;
1607
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001608 if (has_cap(ACER_CAP_MAILLED))
1609 set_u32(data->mailled, ACER_CAP_MAILLED);
1610
1611 if (has_cap(ACER_CAP_BRIGHTNESS))
1612 set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
1613
1614 return 0;
1615}
1616
Pali Rohár9a0b74f2011-02-26 21:18:58 +01001617static void acer_platform_shutdown(struct platform_device *device)
1618{
1619 struct acer_data *data = &interface->data;
1620
1621 if (!data)
1622 return;
1623
1624 if (has_cap(ACER_CAP_MAILLED))
1625 set_u32(LED_OFF, ACER_CAP_MAILLED);
1626}
1627
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001628static struct platform_driver acer_platform_driver = {
1629 .driver = {
1630 .name = "acer-wmi",
1631 .owner = THIS_MODULE,
1632 },
1633 .probe = acer_platform_probe,
1634 .remove = acer_platform_remove,
1635 .suspend = acer_platform_suspend,
1636 .resume = acer_platform_resume,
Pali Rohár9a0b74f2011-02-26 21:18:58 +01001637 .shutdown = acer_platform_shutdown,
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001638};
1639
1640static struct platform_device *acer_platform_device;
1641
1642static int remove_sysfs(struct platform_device *device)
1643{
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001644 if (has_cap(ACER_CAP_THREEG))
1645 device_remove_file(&device->dev, &dev_attr_threeg);
1646
1647 device_remove_file(&device->dev, &dev_attr_interface);
1648
1649 return 0;
1650}
1651
1652static int create_sysfs(void)
1653{
1654 int retval = -ENOMEM;
1655
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001656 if (has_cap(ACER_CAP_THREEG)) {
1657 retval = device_create_file(&acer_platform_device->dev,
1658 &dev_attr_threeg);
1659 if (retval)
1660 goto error_sysfs;
1661 }
1662
1663 retval = device_create_file(&acer_platform_device->dev,
1664 &dev_attr_interface);
1665 if (retval)
1666 goto error_sysfs;
1667
1668 return 0;
1669
1670error_sysfs:
1671 remove_sysfs(acer_platform_device);
1672 return retval;
1673}
1674
Carlos Corbacho81143522008-06-21 09:09:53 +01001675static void remove_debugfs(void)
1676{
1677 debugfs_remove(interface->debug.devices);
1678 debugfs_remove(interface->debug.root);
1679}
1680
1681static int create_debugfs(void)
1682{
1683 interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
1684 if (!interface->debug.root) {
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001685 pr_err("Failed to create debugfs directory");
Carlos Corbacho81143522008-06-21 09:09:53 +01001686 return -ENOMEM;
1687 }
1688
1689 interface->debug.devices = debugfs_create_u32("devices", S_IRUGO,
1690 interface->debug.root,
1691 &interface->debug.wmid_devices);
1692 if (!interface->debug.devices)
1693 goto error_debugfs;
1694
1695 return 0;
1696
1697error_debugfs:
Russ Dill39dbbb42008-09-02 14:35:40 -07001698 remove_debugfs();
Carlos Corbacho81143522008-06-21 09:09:53 +01001699 return -ENOMEM;
1700}
1701
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001702static int __init acer_wmi_init(void)
1703{
1704 int err;
1705
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001706 pr_info("Acer Laptop ACPI-WMI Extras\n");
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001707
Carlos Corbachoa74dd5f2009-04-04 09:33:29 +01001708 if (dmi_check_system(acer_blacklist)) {
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001709 pr_info("Blacklisted hardware detected - not loading\n");
Carlos Corbachoa74dd5f2009-04-04 09:33:29 +01001710 return -ENODEV;
1711 }
1712
Carlos Corbacho9991d9f2008-06-21 09:09:22 +01001713 find_quirks();
1714
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001715 /*
1716 * Detect which ACPI-WMI interface we're using.
1717 */
1718 if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1719 interface = &AMW0_V2_interface;
1720
1721 if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1722 interface = &wmid_interface;
1723
1724 if (wmi_has_guid(WMID_GUID2) && interface) {
1725 if (ACPI_FAILURE(WMID_set_capabilities())) {
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001726 pr_err("Unable to detect available WMID devices\n");
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001727 return -ENODEV;
1728 }
1729 } else if (!wmi_has_guid(WMID_GUID2) && interface) {
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001730 pr_err("No WMID device detection method found\n");
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001731 return -ENODEV;
1732 }
1733
1734 if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
1735 interface = &AMW0_interface;
1736
1737 if (ACPI_FAILURE(AMW0_set_capabilities())) {
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001738 pr_err("Unable to detect available AMW0 devices\n");
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001739 return -ENODEV;
1740 }
1741 }
1742
Carlos Corbacho9b963c42008-02-24 13:34:29 +00001743 if (wmi_has_guid(AMW0_GUID1))
1744 AMW0_find_mailled();
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001745
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001746 if (!interface) {
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001747 pr_err("No or unsupported WMI interface, unable to load\n");
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001748 return -ENODEV;
1749 }
1750
Arjan van de Ven83097ac2008-08-23 21:45:21 -07001751 set_quirks();
1752
Michael Spang1ba869e2009-03-12 14:31:34 -07001753 if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
Thomas Renningerfebf2d92008-08-01 17:37:56 +02001754 interface->capability &= ~ACER_CAP_BRIGHTNESS;
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001755 pr_info("Brightness must be controlled by "
Thomas Renningerfebf2d92008-08-01 17:37:56 +02001756 "generic video driver\n");
1757 }
1758
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -05001759 if (wmi_has_guid(WMID_GUID3)) {
1760 if (ec_raw_mode) {
1761 if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) {
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001762 pr_err("Cannot enable EC raw mode\n");
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -05001763 return -ENODEV;
1764 }
1765 } else if (ACPI_FAILURE(acer_wmi_enable_lm())) {
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001766 pr_err("Cannot enable Launch Manager mode\n");
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -05001767 return -ENODEV;
1768 }
1769 } else if (ec_raw_mode) {
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001770 pr_info("No WMID EC raw mode enable method\n");
From: Lee, Chun-Yi59ccf2f2011-01-07 17:25:14 -05001771 }
1772
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +08001773 if (wmi_has_guid(ACERWMID_EVENT_GUID)) {
1774 err = acer_wmi_input_setup();
1775 if (err)
1776 return err;
1777 }
1778
Axel Lin1c796322010-06-22 16:17:38 +08001779 err = platform_driver_register(&acer_platform_driver);
1780 if (err) {
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001781 pr_err("Unable to register platform driver.\n");
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001782 goto error_platform_register;
1783 }
Axel Lin1c796322010-06-22 16:17:38 +08001784
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001785 acer_platform_device = platform_device_alloc("acer-wmi", -1);
Axel Lin1c796322010-06-22 16:17:38 +08001786 if (!acer_platform_device) {
1787 err = -ENOMEM;
1788 goto error_device_alloc;
1789 }
1790
1791 err = platform_device_add(acer_platform_device);
1792 if (err)
1793 goto error_device_add;
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001794
1795 err = create_sysfs();
1796 if (err)
Axel Lin1c796322010-06-22 16:17:38 +08001797 goto error_create_sys;
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001798
Carlos Corbacho81143522008-06-21 09:09:53 +01001799 if (wmi_has_guid(WMID_GUID2)) {
1800 interface->debug.wmid_devices = get_wmid_devices();
1801 err = create_debugfs();
1802 if (err)
Axel Lin1c796322010-06-22 16:17:38 +08001803 goto error_create_debugfs;
Carlos Corbacho81143522008-06-21 09:09:53 +01001804 }
1805
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001806 /* Override any initial settings with values from the commandline */
1807 acer_commandline_init();
1808
1809 return 0;
1810
Axel Lin1c796322010-06-22 16:17:38 +08001811error_create_debugfs:
1812 remove_sysfs(acer_platform_device);
1813error_create_sys:
1814 platform_device_del(acer_platform_device);
1815error_device_add:
1816 platform_device_put(acer_platform_device);
1817error_device_alloc:
1818 platform_driver_unregister(&acer_platform_driver);
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001819error_platform_register:
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +08001820 if (wmi_has_guid(ACERWMID_EVENT_GUID))
1821 acer_wmi_input_destroy();
1822
Axel Lin1c796322010-06-22 16:17:38 +08001823 return err;
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001824}
1825
1826static void __exit acer_wmi_exit(void)
1827{
Lee, Chun-Yi3fdca872010-12-07 10:29:20 +08001828 if (wmi_has_guid(ACERWMID_EVENT_GUID))
1829 acer_wmi_input_destroy();
1830
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001831 remove_sysfs(acer_platform_device);
Russ Dill39dbbb42008-09-02 14:35:40 -07001832 remove_debugfs();
Axel Lin97ba0af2010-06-03 15:18:03 +08001833 platform_device_unregister(acer_platform_device);
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001834 platform_driver_unregister(&acer_platform_driver);
1835
Lee, Chun-Yicae15702011-03-16 18:52:36 +08001836 pr_info("Acer Laptop WMI Extras unloaded\n");
Carlos Corbacho745a5d22008-02-05 02:17:10 +00001837 return;
1838}
1839
1840module_init(acer_wmi_init);
1841module_exit(acer_wmi_exit);