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