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