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