]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/misc/acer-wmi.c
acer-wmi: Add EC quirk for Fujitsu Siemens Amilo Li 1718
[linux-2.6-omap-h63xx.git] / drivers / misc / acer-wmi.c
1 /*
2  *  Acer WMI Laptop Extras
3  *
4  *  Copyright (C) 2007-2008     Carlos Corbacho <carlos@strangeworlds.co.uk>
5  *
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
25 #define ACER_WMI_VERSION        "0.1"
26
27 #include <linux/kernel.h>
28 #include <linux/module.h>
29 #include <linux/init.h>
30 #include <linux/types.h>
31 #include <linux/dmi.h>
32 #include <linux/fb.h>
33 #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>
38
39 #include <acpi/acpi_drivers.h>
40
41 MODULE_AUTHOR("Carlos Corbacho");
42 MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
43 MODULE_LICENSE("GPL");
44
45 #define ACER_LOGPREFIX "acer-wmi: "
46 #define ACER_ERR KERN_ERR ACER_LOGPREFIX
47 #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
48 #define ACER_INFO KERN_INFO ACER_LOGPREFIX
49
50 /*
51  * The following defines quirks to get some specific functions to work
52  * which are known to not be supported over ACPI-WMI (such as the mail LED
53  * on WMID based Acer's)
54  */
55 struct acer_quirks {
56         const char *vendor;
57         const char *model;
58         u16 quirks;
59 };
60
61 /*
62  * Magic Number
63  * Meaning is unknown - this number is required for writing to ACPI for AMW0
64  * (it's also used in acerhk when directly accessing the BIOS)
65  */
66 #define ACER_AMW0_WRITE 0x9610
67
68 /*
69  * Bit masks for the AMW0 interface
70  */
71 #define ACER_AMW0_WIRELESS_MASK  0x35
72 #define ACER_AMW0_BLUETOOTH_MASK 0x34
73 #define ACER_AMW0_MAILLED_MASK   0x31
74
75 /*
76  * Method IDs for WMID interface
77  */
78 #define ACER_WMID_GET_WIRELESS_METHODID         1
79 #define ACER_WMID_GET_BLUETOOTH_METHODID        2
80 #define ACER_WMID_GET_BRIGHTNESS_METHODID       3
81 #define ACER_WMID_SET_WIRELESS_METHODID         4
82 #define ACER_WMID_SET_BLUETOOTH_METHODID        5
83 #define ACER_WMID_SET_BRIGHTNESS_METHODID       6
84 #define ACER_WMID_GET_THREEG_METHODID           10
85 #define ACER_WMID_SET_THREEG_METHODID           11
86
87 /*
88  * Acer ACPI method GUIDs
89  */
90 #define AMW0_GUID1              "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
91 #define WMID_GUID1              "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
92 #define WMID_GUID2              "95764E09-FB56-4e83-B31A-37761F60994A"
93
94 MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
95 MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
96
97 /* Temporary workaround until the WMI sysfs interface goes in */
98 MODULE_ALIAS("dmi:*:*Acer*:*:");
99
100 /*
101  * Interface capability flags
102  */
103 #define ACER_CAP_MAILLED                (1<<0)
104 #define ACER_CAP_WIRELESS               (1<<1)
105 #define ACER_CAP_BLUETOOTH              (1<<2)
106 #define ACER_CAP_BRIGHTNESS             (1<<3)
107 #define ACER_CAP_THREEG                 (1<<4)
108 #define ACER_CAP_ANY                    (0xFFFFFFFF)
109
110 /*
111  * Interface type flags
112  */
113 enum interface_flags {
114         ACER_AMW0,
115         ACER_AMW0_V2,
116         ACER_WMID,
117 };
118
119 #define ACER_DEFAULT_WIRELESS  0
120 #define ACER_DEFAULT_BLUETOOTH 0
121 #define ACER_DEFAULT_MAILLED   0
122 #define ACER_DEFAULT_THREEG    0
123
124 static int max_brightness = 0xF;
125
126 static int wireless = -1;
127 static int bluetooth = -1;
128 static int mailled = -1;
129 static int brightness = -1;
130 static int threeg = -1;
131 static int force_series;
132
133 module_param(mailled, int, 0444);
134 module_param(wireless, int, 0444);
135 module_param(bluetooth, int, 0444);
136 module_param(brightness, int, 0444);
137 module_param(threeg, int, 0444);
138 module_param(force_series, int, 0444);
139 MODULE_PARM_DESC(wireless, "Set initial state of Wireless hardware");
140 MODULE_PARM_DESC(bluetooth, "Set initial state of Bluetooth hardware");
141 MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
142 MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
143 MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
144 MODULE_PARM_DESC(force_series, "Force a different laptop series");
145
146 struct acer_data {
147         int mailled;
148         int wireless;
149         int bluetooth;
150         int threeg;
151         int brightness;
152 };
153
154 /* Each low-level interface must define at least some of the following */
155 struct wmi_interface {
156         /* The WMI device type */
157         u32 type;
158
159         /* The capabilities this interface provides */
160         u32 capability;
161
162         /* Private data for the current interface */
163         struct acer_data data;
164 };
165
166 /* The static interface pointer, points to the currently detected interface */
167 static struct wmi_interface *interface;
168
169 /*
170  * Embedded Controller quirks
171  * Some laptops require us to directly access the EC to either enable or query
172  * features that are not available through WMI.
173  */
174
175 struct quirk_entry {
176         u8 wireless;
177         u8 mailled;
178         s8 brightness;
179         u8 bluetooth;
180 };
181
182 static struct quirk_entry *quirks;
183
184 static void set_quirks(void)
185 {
186         if (quirks->mailled)
187                 interface->capability |= ACER_CAP_MAILLED;
188
189         if (quirks->brightness)
190                 interface->capability |= ACER_CAP_BRIGHTNESS;
191 }
192
193 static int dmi_matched(const struct dmi_system_id *dmi)
194 {
195         quirks = dmi->driver_data;
196         return 0;
197 }
198
199 static struct quirk_entry quirk_unknown = {
200 };
201
202 static struct quirk_entry quirk_acer_aspire_1520 = {
203         .brightness = -1,
204 };
205
206 static struct quirk_entry quirk_acer_travelmate_2490 = {
207         .mailled = 1,
208 };
209
210 /* This AMW0 laptop has no bluetooth */
211 static struct quirk_entry quirk_medion_md_98300 = {
212         .wireless = 1,
213 };
214
215 static struct quirk_entry quirk_fujitsu_amilo_li_1718 = {
216         .wireless = 2,
217 };
218
219 static struct dmi_system_id acer_quirks[] = {
220         {
221                 .callback = dmi_matched,
222                 .ident = "Acer Aspire 1360",
223                 .matches = {
224                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
225                         DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
226                 },
227                 .driver_data = &quirk_acer_aspire_1520,
228         },
229         {
230                 .callback = dmi_matched,
231                 .ident = "Acer Aspire 1520",
232                 .matches = {
233                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
234                         DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"),
235                 },
236                 .driver_data = &quirk_acer_aspire_1520,
237         },
238         {
239                 .callback = dmi_matched,
240                 .ident = "Acer Aspire 3100",
241                 .matches = {
242                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
243                         DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
244                 },
245                 .driver_data = &quirk_acer_travelmate_2490,
246         },
247         {
248                 .callback = dmi_matched,
249                 .ident = "Acer Aspire 3610",
250                 .matches = {
251                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
252                         DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3610"),
253                 },
254                 .driver_data = &quirk_acer_travelmate_2490,
255         },
256         {
257                 .callback = dmi_matched,
258                 .ident = "Acer Aspire 5100",
259                 .matches = {
260                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
261                         DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
262                 },
263                 .driver_data = &quirk_acer_travelmate_2490,
264         },
265         {
266                 .callback = dmi_matched,
267                 .ident = "Acer Aspire 5610",
268                 .matches = {
269                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
270                         DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
271                 },
272                 .driver_data = &quirk_acer_travelmate_2490,
273         },
274         {
275                 .callback = dmi_matched,
276                 .ident = "Acer Aspire 5630",
277                 .matches = {
278                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
279                         DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
280                 },
281                 .driver_data = &quirk_acer_travelmate_2490,
282         },
283         {
284                 .callback = dmi_matched,
285                 .ident = "Acer Aspire 5650",
286                 .matches = {
287                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
288                         DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
289                 },
290                 .driver_data = &quirk_acer_travelmate_2490,
291         },
292         {
293                 .callback = dmi_matched,
294                 .ident = "Acer Aspire 5680",
295                 .matches = {
296                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
297                         DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
298                 },
299                 .driver_data = &quirk_acer_travelmate_2490,
300         },
301         {
302                 .callback = dmi_matched,
303                 .ident = "Acer Aspire 9110",
304                 .matches = {
305                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
306                         DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
307                 },
308                 .driver_data = &quirk_acer_travelmate_2490,
309         },
310         {
311                 .callback = dmi_matched,
312                 .ident = "Acer TravelMate 2490",
313                 .matches = {
314                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
315                         DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
316                 },
317                 .driver_data = &quirk_acer_travelmate_2490,
318         },
319         {
320                 .callback = dmi_matched,
321                 .ident = "Acer TravelMate 4200",
322                 .matches = {
323                         DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
324                         DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4200"),
325                 },
326                 .driver_data = &quirk_acer_travelmate_2490,
327         },
328         {
329                 .callback = dmi_matched,
330                 .ident = "Fujitsu Siemens Amilo Li 1718",
331                 .matches = {
332                         DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
333                         DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Li 1718"),
334                 },
335                 .driver_data = &quirk_fujitsu_amilo_li_1718,
336         },
337         {
338                 .callback = dmi_matched,
339                 .ident = "Medion MD 98300",
340                 .matches = {
341                         DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
342                         DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
343                 },
344                 .driver_data = &quirk_medion_md_98300,
345         },
346         {}
347 };
348
349 /* Find which quirks are needed for a particular vendor/ model pair */
350 static void find_quirks(void)
351 {
352         if (!force_series) {
353                 dmi_check_system(acer_quirks);
354         } else if (force_series == 2490) {
355                 quirks = &quirk_acer_travelmate_2490;
356         }
357
358         if (quirks == NULL)
359                 quirks = &quirk_unknown;
360
361         set_quirks();
362 }
363
364 /*
365  * General interface convenience methods
366  */
367
368 static bool has_cap(u32 cap)
369 {
370         if ((interface->capability & cap) != 0)
371                 return 1;
372
373         return 0;
374 }
375
376 /*
377  * AMW0 (V1) interface
378  */
379 struct wmab_args {
380         u32 eax;
381         u32 ebx;
382         u32 ecx;
383         u32 edx;
384 };
385
386 struct wmab_ret {
387         u32 eax;
388         u32 ebx;
389         u32 ecx;
390         u32 edx;
391         u32 eex;
392 };
393
394 static acpi_status wmab_execute(struct wmab_args *regbuf,
395 struct acpi_buffer *result)
396 {
397         struct acpi_buffer input;
398         acpi_status status;
399         input.length = sizeof(struct wmab_args);
400         input.pointer = (u8 *)regbuf;
401
402         status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
403
404         return status;
405 }
406
407 static acpi_status AMW0_get_u32(u32 *value, u32 cap,
408 struct wmi_interface *iface)
409 {
410         int err;
411         u8 result;
412
413         switch (cap) {
414         case ACER_CAP_MAILLED:
415                 switch (quirks->mailled) {
416                 default:
417                         err = ec_read(0xA, &result);
418                         if (err)
419                                 return AE_ERROR;
420                         *value = (result >> 7) & 0x1;
421                         return AE_OK;
422                 }
423                 break;
424         case ACER_CAP_WIRELESS:
425                 switch (quirks->wireless) {
426                 case 1:
427                         err = ec_read(0x7B, &result);
428                         if (err)
429                                 return AE_ERROR;
430                         *value = result & 0x1;
431                         return AE_OK;
432                 case 2:
433                         err = ec_read(0x71, &result);
434                         if (err)
435                                 return AE_ERROR;
436                         *value = result & 0x1;
437                         return AE_OK;
438                 default:
439                         err = ec_read(0xA, &result);
440                         if (err)
441                                 return AE_ERROR;
442                         *value = (result >> 2) & 0x1;
443                         return AE_OK;
444                 }
445                 break;
446         case ACER_CAP_BLUETOOTH:
447                 switch (quirks->bluetooth) {
448                 default:
449                         err = ec_read(0xA, &result);
450                         if (err)
451                                 return AE_ERROR;
452                         *value = (result >> 4) & 0x1;
453                         return AE_OK;
454                 }
455                 break;
456         case ACER_CAP_BRIGHTNESS:
457                 switch (quirks->brightness) {
458                 default:
459                         err = ec_read(0x83, &result);
460                         if (err)
461                                 return AE_ERROR;
462                         *value = result;
463                         return AE_OK;
464                 }
465                 break;
466         default:
467                 return AE_BAD_ADDRESS;
468         }
469         return AE_OK;
470 }
471
472 static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
473 {
474         struct wmab_args args;
475
476         args.eax = ACER_AMW0_WRITE;
477         args.ebx = value ? (1<<8) : 0;
478         args.ecx = args.edx = 0;
479
480         switch (cap) {
481         case ACER_CAP_MAILLED:
482                 if (value > 1)
483                         return AE_BAD_PARAMETER;
484                 args.ebx |= ACER_AMW0_MAILLED_MASK;
485                 break;
486         case ACER_CAP_WIRELESS:
487                 if (value > 1)
488                         return AE_BAD_PARAMETER;
489                 args.ebx |= ACER_AMW0_WIRELESS_MASK;
490                 break;
491         case ACER_CAP_BLUETOOTH:
492                 if (value > 1)
493                         return AE_BAD_PARAMETER;
494                 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
495                 break;
496         case ACER_CAP_BRIGHTNESS:
497                 if (value > max_brightness)
498                         return AE_BAD_PARAMETER;
499                 switch (quirks->brightness) {
500                 default:
501                         return ec_write(0x83, value);
502                         break;
503                 }
504         default:
505                 return AE_BAD_ADDRESS;
506         }
507
508         /* Actually do the set */
509         return wmab_execute(&args, NULL);
510 }
511
512 static acpi_status AMW0_find_mailled(void)
513 {
514         struct wmab_args args;
515         struct wmab_ret ret;
516         acpi_status status = AE_OK;
517         struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
518         union acpi_object *obj;
519
520         args.eax = 0x86;
521         args.ebx = args.ecx = args.edx = 0;
522
523         status = wmab_execute(&args, &out);
524         if (ACPI_FAILURE(status))
525                 return status;
526
527         obj = (union acpi_object *) out.pointer;
528         if (obj && obj->type == ACPI_TYPE_BUFFER &&
529         obj->buffer.length == sizeof(struct wmab_ret)) {
530                 ret = *((struct wmab_ret *) obj->buffer.pointer);
531         } else {
532                 return AE_ERROR;
533         }
534
535         if (ret.eex & 0x1)
536                 interface->capability |= ACER_CAP_MAILLED;
537
538         kfree(out.pointer);
539
540         return AE_OK;
541 }
542
543 static acpi_status AMW0_set_capabilities(void)
544 {
545         struct wmab_args args;
546         struct wmab_ret ret;
547         acpi_status status = AE_OK;
548         struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
549         union acpi_object *obj;
550
551         args.eax = ACER_AMW0_WRITE;
552         args.ecx = args.edx = 0;
553
554         args.ebx = 0xa2 << 8;
555         args.ebx |= ACER_AMW0_WIRELESS_MASK;
556
557         status = wmab_execute(&args, &out);
558         if (ACPI_FAILURE(status))
559                 return status;
560
561         obj = (union acpi_object *) out.pointer;
562         if (obj && obj->type == ACPI_TYPE_BUFFER &&
563         obj->buffer.length == sizeof(struct wmab_ret)) {
564                 ret = *((struct wmab_ret *) obj->buffer.pointer);
565         } else {
566                 return AE_ERROR;
567         }
568
569         if (ret.eax & 0x1)
570                 interface->capability |= ACER_CAP_WIRELESS;
571
572         args.ebx = 2 << 8;
573         args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
574
575         status = wmab_execute(&args, &out);
576         if (ACPI_FAILURE(status))
577                 return status;
578
579         obj = (union acpi_object *) out.pointer;
580         if (obj && obj->type == ACPI_TYPE_BUFFER
581         && obj->buffer.length == sizeof(struct wmab_ret)) {
582                 ret = *((struct wmab_ret *) obj->buffer.pointer);
583         } else {
584                 return AE_ERROR;
585         }
586
587         if (ret.eax & 0x1)
588                 interface->capability |= ACER_CAP_BLUETOOTH;
589
590         kfree(out.pointer);
591
592         /*
593          * This appears to be safe to enable, since all Wistron based laptops
594          * appear to use the same EC register for brightness, even if they
595          * differ for wireless, etc
596          */
597         if (quirks->brightness >= 0)
598                 interface->capability |= ACER_CAP_BRIGHTNESS;
599
600         return AE_OK;
601 }
602
603 static struct wmi_interface AMW0_interface = {
604         .type = ACER_AMW0,
605 };
606
607 static struct wmi_interface AMW0_V2_interface = {
608         .type = ACER_AMW0_V2,
609 };
610
611 /*
612  * New interface (The WMID interface)
613  */
614 static acpi_status
615 WMI_execute_u32(u32 method_id, u32 in, u32 *out)
616 {
617         struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
618         struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
619         union acpi_object *obj;
620         u32 tmp;
621         acpi_status status;
622
623         status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
624
625         if (ACPI_FAILURE(status))
626                 return status;
627
628         obj = (union acpi_object *) result.pointer;
629         if (obj && obj->type == ACPI_TYPE_BUFFER &&
630                 obj->buffer.length == sizeof(u32)) {
631                 tmp = *((u32 *) obj->buffer.pointer);
632         } else {
633                 tmp = 0;
634         }
635
636         if (out)
637                 *out = tmp;
638
639         kfree(result.pointer);
640
641         return status;
642 }
643
644 static acpi_status WMID_get_u32(u32 *value, u32 cap,
645 struct wmi_interface *iface)
646 {
647         acpi_status status;
648         u8 tmp;
649         u32 result, method_id = 0;
650
651         switch (cap) {
652         case ACER_CAP_WIRELESS:
653                 method_id = ACER_WMID_GET_WIRELESS_METHODID;
654                 break;
655         case ACER_CAP_BLUETOOTH:
656                 method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
657                 break;
658         case ACER_CAP_BRIGHTNESS:
659                 method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
660                 break;
661         case ACER_CAP_THREEG:
662                 method_id = ACER_WMID_GET_THREEG_METHODID;
663                 break;
664         case ACER_CAP_MAILLED:
665                 if (quirks->mailled == 1) {
666                         ec_read(0x9f, &tmp);
667                         *value = tmp & 0x1;
668                         return 0;
669                 }
670         default:
671                 return AE_BAD_ADDRESS;
672         }
673         status = WMI_execute_u32(method_id, 0, &result);
674
675         if (ACPI_SUCCESS(status))
676                 *value = (u8)result;
677
678         return status;
679 }
680
681 static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
682 {
683         u32 method_id = 0;
684         char param;
685
686         switch (cap) {
687         case ACER_CAP_BRIGHTNESS:
688                 if (value > max_brightness)
689                         return AE_BAD_PARAMETER;
690                 method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
691                 break;
692         case ACER_CAP_WIRELESS:
693                 if (value > 1)
694                         return AE_BAD_PARAMETER;
695                 method_id = ACER_WMID_SET_WIRELESS_METHODID;
696                 break;
697         case ACER_CAP_BLUETOOTH:
698                 if (value > 1)
699                         return AE_BAD_PARAMETER;
700                 method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
701                 break;
702         case ACER_CAP_THREEG:
703                 if (value > 1)
704                         return AE_BAD_PARAMETER;
705                 method_id = ACER_WMID_SET_THREEG_METHODID;
706                 break;
707         case ACER_CAP_MAILLED:
708                 if (value > 1)
709                         return AE_BAD_PARAMETER;
710                 if (quirks->mailled == 1) {
711                         param = value ? 0x92 : 0x93;
712                         i8042_command(&param, 0x1059);
713                         return 0;
714                 }
715                 break;
716         default:
717                 return AE_BAD_ADDRESS;
718         }
719         return WMI_execute_u32(method_id, (u32)value, NULL);
720 }
721
722 static acpi_status WMID_set_capabilities(void)
723 {
724         struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
725         union acpi_object *obj;
726         acpi_status status;
727         u32 devices;
728
729         status = wmi_query_block(WMID_GUID2, 1, &out);
730         if (ACPI_FAILURE(status))
731                 return status;
732
733         obj = (union acpi_object *) out.pointer;
734         if (obj && obj->type == ACPI_TYPE_BUFFER &&
735                 obj->buffer.length == sizeof(u32)) {
736                 devices = *((u32 *) obj->buffer.pointer);
737         } else {
738                 return AE_ERROR;
739         }
740
741         /* Not sure on the meaning of the relevant bits yet to detect these */
742         interface->capability |= ACER_CAP_WIRELESS;
743         interface->capability |= ACER_CAP_THREEG;
744
745         /* WMID always provides brightness methods */
746         interface->capability |= ACER_CAP_BRIGHTNESS;
747
748         if (devices & 0x10)
749                 interface->capability |= ACER_CAP_BLUETOOTH;
750
751         if (!(devices & 0x20))
752                 max_brightness = 0x9;
753
754         return status;
755 }
756
757 static struct wmi_interface wmid_interface = {
758         .type = ACER_WMID,
759 };
760
761 /*
762  * Generic Device (interface-independent)
763  */
764
765 static acpi_status get_u32(u32 *value, u32 cap)
766 {
767         acpi_status status = AE_BAD_ADDRESS;
768
769         switch (interface->type) {
770         case ACER_AMW0:
771                 status = AMW0_get_u32(value, cap, interface);
772                 break;
773         case ACER_AMW0_V2:
774                 if (cap == ACER_CAP_MAILLED) {
775                         status = AMW0_get_u32(value, cap, interface);
776                         break;
777                 }
778         case ACER_WMID:
779                 status = WMID_get_u32(value, cap, interface);
780                 break;
781         }
782
783         return status;
784 }
785
786 static acpi_status set_u32(u32 value, u32 cap)
787 {
788         if (interface->capability & cap) {
789                 switch (interface->type) {
790                 case ACER_AMW0:
791                         return AMW0_set_u32(value, cap, interface);
792                 case ACER_AMW0_V2:
793                 case ACER_WMID:
794                         return WMID_set_u32(value, cap, interface);
795                 default:
796                         return AE_BAD_PARAMETER;
797                 }
798         }
799         return AE_BAD_PARAMETER;
800 }
801
802 static void __init acer_commandline_init(void)
803 {
804         /*
805          * These will all fail silently if the value given is invalid, or the
806          * capability isn't available on the given interface
807          */
808         set_u32(mailled, ACER_CAP_MAILLED);
809         set_u32(wireless, ACER_CAP_WIRELESS);
810         set_u32(bluetooth, ACER_CAP_BLUETOOTH);
811         set_u32(threeg, ACER_CAP_THREEG);
812         set_u32(brightness, ACER_CAP_BRIGHTNESS);
813 }
814
815 /*
816  * LED device (Mail LED only, no other LEDs known yet)
817  */
818 static void mail_led_set(struct led_classdev *led_cdev,
819 enum led_brightness value)
820 {
821         set_u32(value, ACER_CAP_MAILLED);
822 }
823
824 static struct led_classdev mail_led = {
825         .name = "acer-wmi::mail",
826         .brightness_set = mail_led_set,
827 };
828
829 static int __devinit acer_led_init(struct device *dev)
830 {
831         return led_classdev_register(dev, &mail_led);
832 }
833
834 static void acer_led_exit(void)
835 {
836         led_classdev_unregister(&mail_led);
837 }
838
839 /*
840  * Backlight device
841  */
842 static struct backlight_device *acer_backlight_device;
843
844 static int read_brightness(struct backlight_device *bd)
845 {
846         u32 value;
847         get_u32(&value, ACER_CAP_BRIGHTNESS);
848         return value;
849 }
850
851 static int update_bl_status(struct backlight_device *bd)
852 {
853         int intensity = bd->props.brightness;
854
855         if (bd->props.power != FB_BLANK_UNBLANK)
856                 intensity = 0;
857         if (bd->props.fb_blank != FB_BLANK_UNBLANK)
858                 intensity = 0;
859
860         set_u32(intensity, ACER_CAP_BRIGHTNESS);
861
862         return 0;
863 }
864
865 static struct backlight_ops acer_bl_ops = {
866         .get_brightness = read_brightness,
867         .update_status = update_bl_status,
868 };
869
870 static int __devinit acer_backlight_init(struct device *dev)
871 {
872         struct backlight_device *bd;
873
874         bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops);
875         if (IS_ERR(bd)) {
876                 printk(ACER_ERR "Could not register Acer backlight device\n");
877                 acer_backlight_device = NULL;
878                 return PTR_ERR(bd);
879         }
880
881         acer_backlight_device = bd;
882
883         bd->props.power = FB_BLANK_UNBLANK;
884         bd->props.brightness = max_brightness;
885         bd->props.max_brightness = max_brightness;
886         backlight_update_status(bd);
887         return 0;
888 }
889
890 static void acer_backlight_exit(void)
891 {
892         backlight_device_unregister(acer_backlight_device);
893 }
894
895 /*
896  * Read/ write bool sysfs macro
897  */
898 #define show_set_bool(value, cap) \
899 static ssize_t \
900 show_bool_##value(struct device *dev, struct device_attribute *attr, \
901         char *buf) \
902 { \
903         u32 result; \
904         acpi_status status = get_u32(&result, cap); \
905         if (ACPI_SUCCESS(status)) \
906                 return sprintf(buf, "%u\n", result); \
907         return sprintf(buf, "Read error\n"); \
908 } \
909 \
910 static ssize_t \
911 set_bool_##value(struct device *dev, struct device_attribute *attr, \
912         const char *buf, size_t count) \
913 { \
914         u32 tmp = simple_strtoul(buf, NULL, 10); \
915         acpi_status status = set_u32(tmp, cap); \
916                 if (ACPI_FAILURE(status)) \
917                         return -EINVAL; \
918         return count; \
919 } \
920 static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
921         show_bool_##value, set_bool_##value);
922
923 show_set_bool(wireless, ACER_CAP_WIRELESS);
924 show_set_bool(bluetooth, ACER_CAP_BLUETOOTH);
925 show_set_bool(threeg, ACER_CAP_THREEG);
926
927 /*
928  * Read interface sysfs macro
929  */
930 static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
931         char *buf)
932 {
933         switch (interface->type) {
934         case ACER_AMW0:
935                 return sprintf(buf, "AMW0\n");
936         case ACER_AMW0_V2:
937                 return sprintf(buf, "AMW0 v2\n");
938         case ACER_WMID:
939                 return sprintf(buf, "WMID\n");
940         default:
941                 return sprintf(buf, "Error!\n");
942         }
943 }
944
945 static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR,
946         show_interface, NULL);
947
948 /*
949  * Platform device
950  */
951 static int __devinit acer_platform_probe(struct platform_device *device)
952 {
953         int err;
954
955         if (has_cap(ACER_CAP_MAILLED)) {
956                 err = acer_led_init(&device->dev);
957                 if (err)
958                         goto error_mailled;
959         }
960
961         if (has_cap(ACER_CAP_BRIGHTNESS)) {
962                 err = acer_backlight_init(&device->dev);
963                 if (err)
964                         goto error_brightness;
965         }
966
967         return 0;
968
969 error_brightness:
970         acer_led_exit();
971 error_mailled:
972         return err;
973 }
974
975 static int acer_platform_remove(struct platform_device *device)
976 {
977         if (has_cap(ACER_CAP_MAILLED))
978                 acer_led_exit();
979         if (has_cap(ACER_CAP_BRIGHTNESS))
980                 acer_backlight_exit();
981         return 0;
982 }
983
984 static int acer_platform_suspend(struct platform_device *dev,
985 pm_message_t state)
986 {
987         u32 value;
988         struct acer_data *data = &interface->data;
989
990         if (!data)
991                 return -ENOMEM;
992
993         if (has_cap(ACER_CAP_WIRELESS)) {
994                 get_u32(&value, ACER_CAP_WIRELESS);
995                 data->wireless = value;
996         }
997
998         if (has_cap(ACER_CAP_BLUETOOTH)) {
999                 get_u32(&value, ACER_CAP_BLUETOOTH);
1000                 data->bluetooth = value;
1001         }
1002
1003         if (has_cap(ACER_CAP_MAILLED)) {
1004                 get_u32(&value, ACER_CAP_MAILLED);
1005                 data->mailled = value;
1006         }
1007
1008         if (has_cap(ACER_CAP_BRIGHTNESS)) {
1009                 get_u32(&value, ACER_CAP_BRIGHTNESS);
1010                 data->brightness = value;
1011         }
1012
1013         return 0;
1014 }
1015
1016 static int acer_platform_resume(struct platform_device *device)
1017 {
1018         struct acer_data *data = &interface->data;
1019
1020         if (!data)
1021                 return -ENOMEM;
1022
1023         if (has_cap(ACER_CAP_WIRELESS))
1024                 set_u32(data->wireless, ACER_CAP_WIRELESS);
1025
1026         if (has_cap(ACER_CAP_BLUETOOTH))
1027                 set_u32(data->bluetooth, ACER_CAP_BLUETOOTH);
1028
1029         if (has_cap(ACER_CAP_THREEG))
1030                 set_u32(data->threeg, ACER_CAP_THREEG);
1031
1032         if (has_cap(ACER_CAP_MAILLED))
1033                 set_u32(data->mailled, ACER_CAP_MAILLED);
1034
1035         if (has_cap(ACER_CAP_BRIGHTNESS))
1036                 set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
1037
1038         return 0;
1039 }
1040
1041 static struct platform_driver acer_platform_driver = {
1042         .driver = {
1043                 .name = "acer-wmi",
1044                 .owner = THIS_MODULE,
1045         },
1046         .probe = acer_platform_probe,
1047         .remove = acer_platform_remove,
1048         .suspend = acer_platform_suspend,
1049         .resume = acer_platform_resume,
1050 };
1051
1052 static struct platform_device *acer_platform_device;
1053
1054 static int remove_sysfs(struct platform_device *device)
1055 {
1056         if (has_cap(ACER_CAP_WIRELESS))
1057                 device_remove_file(&device->dev, &dev_attr_wireless);
1058
1059         if (has_cap(ACER_CAP_BLUETOOTH))
1060                 device_remove_file(&device->dev, &dev_attr_bluetooth);
1061
1062         if (has_cap(ACER_CAP_THREEG))
1063                 device_remove_file(&device->dev, &dev_attr_threeg);
1064
1065         device_remove_file(&device->dev, &dev_attr_interface);
1066
1067         return 0;
1068 }
1069
1070 static int create_sysfs(void)
1071 {
1072         int retval = -ENOMEM;
1073
1074         if (has_cap(ACER_CAP_WIRELESS)) {
1075                 retval = device_create_file(&acer_platform_device->dev,
1076                         &dev_attr_wireless);
1077                 if (retval)
1078                         goto error_sysfs;
1079         }
1080
1081         if (has_cap(ACER_CAP_BLUETOOTH)) {
1082                 retval = device_create_file(&acer_platform_device->dev,
1083                         &dev_attr_bluetooth);
1084                 if (retval)
1085                         goto error_sysfs;
1086         }
1087
1088         if (has_cap(ACER_CAP_THREEG)) {
1089                 retval = device_create_file(&acer_platform_device->dev,
1090                         &dev_attr_threeg);
1091                 if (retval)
1092                         goto error_sysfs;
1093         }
1094
1095         retval = device_create_file(&acer_platform_device->dev,
1096                 &dev_attr_interface);
1097         if (retval)
1098                 goto error_sysfs;
1099
1100         return 0;
1101
1102 error_sysfs:
1103                 remove_sysfs(acer_platform_device);
1104         return retval;
1105 }
1106
1107 static int __init acer_wmi_init(void)
1108 {
1109         int err;
1110
1111         printk(ACER_INFO "Acer Laptop ACPI-WMI Extras version %s\n",
1112                         ACER_WMI_VERSION);
1113
1114         find_quirks();
1115
1116         /*
1117          * Detect which ACPI-WMI interface we're using.
1118          */
1119         if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1120                 interface = &AMW0_V2_interface;
1121
1122         if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1123                 interface = &wmid_interface;
1124
1125         if (wmi_has_guid(WMID_GUID2) && interface) {
1126                 if (ACPI_FAILURE(WMID_set_capabilities())) {
1127                         printk(ACER_ERR "Unable to detect available WMID "
1128                                         "devices\n");
1129                         return -ENODEV;
1130                 }
1131         } else if (!wmi_has_guid(WMID_GUID2) && interface) {
1132                 printk(ACER_ERR "No WMID device detection method found\n");
1133                 return -ENODEV;
1134         }
1135
1136         if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
1137                 interface = &AMW0_interface;
1138
1139                 if (ACPI_FAILURE(AMW0_set_capabilities())) {
1140                         printk(ACER_ERR "Unable to detect available AMW0 "
1141                                         "devices\n");
1142                         return -ENODEV;
1143                 }
1144         }
1145
1146         if (wmi_has_guid(AMW0_GUID1))
1147                 AMW0_find_mailled();
1148
1149         if (!interface) {
1150                 printk(ACER_ERR "No or unsupported WMI interface, unable to "
1151                                 "load\n");
1152                 return -ENODEV;
1153         }
1154
1155         if (platform_driver_register(&acer_platform_driver)) {
1156                 printk(ACER_ERR "Unable to register platform driver.\n");
1157                 goto error_platform_register;
1158         }
1159         acer_platform_device = platform_device_alloc("acer-wmi", -1);
1160         platform_device_add(acer_platform_device);
1161
1162         err = create_sysfs();
1163         if (err)
1164                 return err;
1165
1166         /* Override any initial settings with values from the commandline */
1167         acer_commandline_init();
1168
1169         return 0;
1170
1171 error_platform_register:
1172         return -ENODEV;
1173 }
1174
1175 static void __exit acer_wmi_exit(void)
1176 {
1177         remove_sysfs(acer_platform_device);
1178         platform_device_del(acer_platform_device);
1179         platform_driver_unregister(&acer_platform_driver);
1180
1181         printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
1182         return;
1183 }
1184
1185 module_init(acer_wmi_init);
1186 module_exit(acer_wmi_exit);