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