aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorCarlos Corbacho <carlos@strangeworlds.co.uk>2008-02-04 21:17:10 -0500
committerLen Brown <len.brown@intel.com>2008-02-05 15:07:00 -0500
commit745a5d2126926808295742932d0e36d485efa485 (patch)
treea8de440fe3c6caa4a06cda2960e77a351ca3ff83 /drivers/misc
parentbff431e49ff531a343fbb2b4426e313000844f32 (diff)
acer-wmi: Add driver for newer Acer laptops
This is a driver for newer Acer (and Wistron) laptops. It adds wireless radio and bluetooth control, and on some laptops, exposes the mail LED and LCD backlight. v1: * Initial release v2: * Replace left over ACPI references with WMI * Add GUID based autoloading (depends on future work to WMI) * Add DMI based autoloading (backup solution until WMI sysfs/ class work is available) * Checkpatch fixes v3: * Add new EC quirks for Aspire 3100 & 5100, and Extensa 5220 v4: * Simplified internal handling of WMID and AMW0 devices * Add autodetection for bluetooth and maximum brightness on AMW0 V2 and WMID laptops. v5: * Add EC quirk for Medion MD 98000 * Add autodetection for AMW0, and mail LED on AMW0 and AMW0 V2. * Improve error handling * Fix AMW0 V2 bluetooth and wireless, by using both WMID and AMW0 methods to ensure that the correct value is always set. v6: * Fix 'use before initialisation' bug with quirks. v7 * Fix bug on AMW0 where acer-wmi would exit if a mail LED was not detected. * Add Acer Aspire 9110 mail LED support * Fix section mismatch warnings Signed-off-by: Carlos Corbacho <carlos@strangeworlds.co.uk> CC: Matthew Garrett <mjg59@srcf.ucam.org> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig16
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/acer-wmi.c1109
3 files changed, 1126 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b5e67c0ff433..35d2c22c5d24 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -92,6 +92,22 @@ config TIFM_7XX1
92 To compile this driver as a module, choose M here: the module will 92 To compile this driver as a module, choose M here: the module will
93 be called tifm_7xx1. 93 be called tifm_7xx1.
94 94
95config ACER_WMI
96 tristate "Acer WMI Laptop Extras (EXPERIMENTAL)"
97 depends on X86
98 depends on EXPERIMENTAL
99 depends on ACPI
100 depends on ACPI_WMI
101 depends on LEDS_CLASS
102 depends on BACKLIGHT_CLASS_DEVICE
103 ---help---
104 This is a driver for newer Acer (and Wistron) laptops. It adds
105 wireless radio and bluetooth control, and on some laptops,
106 exposes the mail LED and LCD backlight.
107
108 If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
109 here.
110
95config ASUS_LAPTOP 111config ASUS_LAPTOP
96 tristate "Asus Laptop Extras (EXPERIMENTAL)" 112 tristate "Asus Laptop Extras (EXPERIMENTAL)"
97 depends on X86 113 depends on X86
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 87f2685d728f..3da1491f662c 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -6,6 +6,7 @@ obj- := misc.o # Dummy rule to force built-in.o to be made
6obj-$(CONFIG_IBM_ASM) += ibmasm/ 6obj-$(CONFIG_IBM_ASM) += ibmasm/
7obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ 7obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/
8obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o 8obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
9obj-$(CONFIG_ACER_WMI) += acer-wmi.o
9obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o 10obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
10obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o 11obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
11obj-$(CONFIG_LKDTM) += lkdtm.o 12obj-$(CONFIG_LKDTM) += lkdtm.o
diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c
new file mode 100644
index 000000000000..a4d677504250
--- /dev/null
+++ b/drivers/misc/acer-wmi.c
@@ -0,0 +1,1109 @@
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
40MODULE_AUTHOR("Carlos Corbacho");
41MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
42MODULE_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 */
54struct 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
93MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
94MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
95
96/* Temporary workaround until the WMI sysfs interface goes in */
97MODULE_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 */
112enum 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
123static int max_brightness = 0xF;
124
125static int wireless = -1;
126static int bluetooth = -1;
127static int mailled = -1;
128static int brightness = -1;
129static int threeg = -1;
130static int force_series;
131
132module_param(mailled, int, 0444);
133module_param(wireless, int, 0444);
134module_param(bluetooth, int, 0444);
135module_param(brightness, int, 0444);
136module_param(threeg, int, 0444);
137module_param(force_series, int, 0444);
138MODULE_PARM_DESC(wireless, "Set initial state of Wireless hardware");
139MODULE_PARM_DESC(bluetooth, "Set initial state of Bluetooth hardware");
140MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
141MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
142MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
143MODULE_PARM_DESC(force_series, "Force a different laptop series");
144
145struct 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 */
154struct 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 */
166static 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
174struct quirk_entry {
175 u8 wireless;
176 u8 mailled;
177 u8 brightness;
178 u8 bluetooth;
179};
180
181static struct quirk_entry *quirks;
182
183static 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
192static int dmi_matched(const struct dmi_system_id *dmi)
193{
194 quirks = dmi->driver_data;
195 return 0;
196}
197
198static struct quirk_entry quirk_unknown = {
199};
200
201static struct quirk_entry quirk_acer_travelmate_2490 = {
202 .mailled = 1,
203};
204
205/* This AMW0 laptop has no bluetooth */
206static struct quirk_entry quirk_medion_md_98300 = {
207 .wireless = 1,
208};
209
210static 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 = "Medion MD 98300",
277 .matches = {
278 DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
279 DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
280 },
281 .driver_data = &quirk_medion_md_98300,
282 },
283 {}
284};
285
286/* Find which quirks are needed for a particular vendor/ model pair */
287static void find_quirks(void)
288{
289 if (!force_series) {
290 dmi_check_system(acer_quirks);
291 } else if (force_series == 2490) {
292 quirks = &quirk_acer_travelmate_2490;
293 }
294
295 if (quirks == NULL)
296 quirks = &quirk_unknown;
297
298 set_quirks();
299}
300
301/*
302 * General interface convenience methods
303 */
304
305static bool has_cap(u32 cap)
306{
307 if ((interface->capability & cap) != 0)
308 return 1;
309
310 return 0;
311}
312
313/*
314 * AMW0 (V1) interface
315 */
316struct wmab_args {
317 u32 eax;
318 u32 ebx;
319 u32 ecx;
320 u32 edx;
321};
322
323struct wmab_ret {
324 u32 eax;
325 u32 ebx;
326 u32 ecx;
327 u32 edx;
328 u32 eex;
329};
330
331static acpi_status wmab_execute(struct wmab_args *regbuf,
332struct acpi_buffer *result)
333{
334 struct acpi_buffer input;
335 acpi_status status;
336 input.length = sizeof(struct wmab_args);
337 input.pointer = (u8 *)regbuf;
338
339 status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
340
341 return status;
342}
343
344static acpi_status AMW0_get_u32(u32 *value, u32 cap,
345struct wmi_interface *iface)
346{
347 int err;
348 u8 result;
349
350 switch (cap) {
351 case ACER_CAP_MAILLED:
352 switch (quirks->mailled) {
353 default:
354 err = ec_read(0xA, &result);
355 if (err)
356 return AE_ERROR;
357 *value = (result >> 7) & 0x1;
358 return AE_OK;
359 }
360 break;
361 case ACER_CAP_WIRELESS:
362 switch (quirks->wireless) {
363 case 1:
364 err = ec_read(0x7B, &result);
365 if (err)
366 return AE_ERROR;
367 *value = result & 0x1;
368 return AE_OK;
369 default:
370 err = ec_read(0xA, &result);
371 if (err)
372 return AE_ERROR;
373 *value = (result >> 2) & 0x1;
374 return AE_OK;
375 }
376 break;
377 case ACER_CAP_BLUETOOTH:
378 switch (quirks->bluetooth) {
379 default:
380 err = ec_read(0xA, &result);
381 if (err)
382 return AE_ERROR;
383 *value = (result >> 4) & 0x1;
384 return AE_OK;
385 }
386 break;
387 case ACER_CAP_BRIGHTNESS:
388 switch (quirks->brightness) {
389 default:
390 err = ec_read(0x83, &result);
391 if (err)
392 return AE_ERROR;
393 *value = result;
394 return AE_OK;
395 }
396 break;
397 default:
398 return AE_BAD_ADDRESS;
399 }
400 return AE_OK;
401}
402
403static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
404{
405 struct wmab_args args;
406
407 args.eax = ACER_AMW0_WRITE;
408 args.ebx = value ? (1<<8) : 0;
409 args.ecx = args.edx = 0;
410
411 switch (cap) {
412 case ACER_CAP_MAILLED:
413 if (value > 1)
414 return AE_BAD_PARAMETER;
415 args.ebx |= ACER_AMW0_MAILLED_MASK;
416 break;
417 case ACER_CAP_WIRELESS:
418 if (value > 1)
419 return AE_BAD_PARAMETER;
420 args.ebx |= ACER_AMW0_WIRELESS_MASK;
421 break;
422 case ACER_CAP_BLUETOOTH:
423 if (value > 1)
424 return AE_BAD_PARAMETER;
425 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
426 break;
427 case ACER_CAP_BRIGHTNESS:
428 if (value > max_brightness)
429 return AE_BAD_PARAMETER;
430 switch (quirks->brightness) {
431 case 1:
432 return ec_write(0x83, value);
433 default:
434 return AE_BAD_ADDRESS;
435 break;
436 }
437 default:
438 return AE_BAD_ADDRESS;
439 }
440
441 /* Actually do the set */
442 return wmab_execute(&args, NULL);
443}
444
445static acpi_status AMW0_find_mailled(void)
446{
447 struct wmab_args args;
448 struct wmab_ret ret;
449 acpi_status status = AE_OK;
450 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
451 union acpi_object *obj;
452
453 args.eax = 0x86;
454 args.ebx = args.ecx = args.edx = 0;
455
456 status = wmab_execute(&args, &out);
457 if (ACPI_FAILURE(status))
458 return status;
459
460 obj = (union acpi_object *) out.pointer;
461 if (obj && obj->type == ACPI_TYPE_BUFFER &&
462 obj->buffer.length == sizeof(struct wmab_ret)) {
463 ret = *((struct wmab_ret *) obj->buffer.pointer);
464 } else {
465 return AE_ERROR;
466 }
467
468 if (ret.eex & 0x1)
469 interface->capability |= ACER_CAP_MAILLED;
470
471 kfree(out.pointer);
472
473 return AE_OK;
474}
475
476static acpi_status AMW0_set_capabilities(void)
477{
478 struct wmab_args args;
479 struct wmab_ret ret;
480 acpi_status status = AE_OK;
481 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
482 union acpi_object *obj;
483
484 args.eax = ACER_AMW0_WRITE;
485 args.ecx = args.edx = 0;
486
487 args.ebx = 0xa2 << 8;
488 args.ebx |= ACER_AMW0_WIRELESS_MASK;
489
490 status = wmab_execute(&args, &out);
491 if (ACPI_FAILURE(status))
492 return status;
493
494 obj = (union acpi_object *) out.pointer;
495 if (obj && obj->type == ACPI_TYPE_BUFFER &&
496 obj->buffer.length == sizeof(struct wmab_ret)) {
497 ret = *((struct wmab_ret *) obj->buffer.pointer);
498 } else {
499 return AE_ERROR;
500 }
501
502 if (ret.eax & 0x1)
503 interface->capability |= ACER_CAP_WIRELESS;
504
505 args.ebx = 2 << 8;
506 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
507
508 status = wmab_execute(&args, &out);
509 if (ACPI_FAILURE(status))
510 return status;
511
512 obj = (union acpi_object *) out.pointer;
513 if (obj && obj->type == ACPI_TYPE_BUFFER
514 && obj->buffer.length == sizeof(struct wmab_ret)) {
515 ret = *((struct wmab_ret *) obj->buffer.pointer);
516 } else {
517 return AE_ERROR;
518 }
519
520 if (ret.eax & 0x1)
521 interface->capability |= ACER_CAP_BLUETOOTH;
522
523 kfree(out.pointer);
524
525 /*
526 * This appears to be safe to enable, since all Wistron based laptops
527 * appear to use the same EC register for brightness, even if they
528 * differ for wireless, etc
529 */
530 interface->capability |= ACER_CAP_BRIGHTNESS;
531
532 return AE_OK;
533}
534
535static struct wmi_interface AMW0_interface = {
536 .type = ACER_AMW0,
537};
538
539static struct wmi_interface AMW0_V2_interface = {
540 .type = ACER_AMW0_V2,
541};
542
543/*
544 * New interface (The WMID interface)
545 */
546static acpi_status
547WMI_execute_u32(u32 method_id, u32 in, u32 *out)
548{
549 struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
550 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
551 union acpi_object *obj;
552 u32 tmp;
553 acpi_status status;
554
555 status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
556
557 if (ACPI_FAILURE(status))
558 return status;
559
560 obj = (union acpi_object *) result.pointer;
561 if (obj && obj->type == ACPI_TYPE_BUFFER &&
562 obj->buffer.length == sizeof(u32)) {
563 tmp = *((u32 *) obj->buffer.pointer);
564 } else {
565 tmp = 0;
566 }
567
568 if (out)
569 *out = tmp;
570
571 kfree(result.pointer);
572
573 return status;
574}
575
576static acpi_status WMID_get_u32(u32 *value, u32 cap,
577struct wmi_interface *iface)
578{
579 acpi_status status;
580 u8 tmp;
581 u32 result, method_id = 0;
582
583 switch (cap) {
584 case ACER_CAP_WIRELESS:
585 method_id = ACER_WMID_GET_WIRELESS_METHODID;
586 break;
587 case ACER_CAP_BLUETOOTH:
588 method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
589 break;
590 case ACER_CAP_BRIGHTNESS:
591 method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
592 break;
593 case ACER_CAP_THREEG:
594 method_id = ACER_WMID_GET_THREEG_METHODID;
595 break;
596 case ACER_CAP_MAILLED:
597 if (quirks->mailled == 1) {
598 ec_read(0x9f, &tmp);
599 *value = tmp & 0x1;
600 return 0;
601 }
602 default:
603 return AE_BAD_ADDRESS;
604 }
605 status = WMI_execute_u32(method_id, 0, &result);
606
607 if (ACPI_SUCCESS(status))
608 *value = (u8)result;
609
610 return status;
611}
612
613static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
614{
615 u32 method_id = 0;
616 char param;
617
618 switch (cap) {
619 case ACER_CAP_BRIGHTNESS:
620 if (value > max_brightness)
621 return AE_BAD_PARAMETER;
622 method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
623 break;
624 case ACER_CAP_WIRELESS:
625 if (value > 1)
626 return AE_BAD_PARAMETER;
627 method_id = ACER_WMID_SET_WIRELESS_METHODID;
628 break;
629 case ACER_CAP_BLUETOOTH:
630 if (value > 1)
631 return AE_BAD_PARAMETER;
632 method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
633 break;
634 case ACER_CAP_THREEG:
635 if (value > 1)
636 return AE_BAD_PARAMETER;
637 method_id = ACER_WMID_SET_THREEG_METHODID;
638 break;
639 case ACER_CAP_MAILLED:
640 if (value > 1)
641 return AE_BAD_PARAMETER;
642 if (quirks->mailled == 1) {
643 param = value ? 0x92 : 0x93;
644 i8042_command(&param, 0x1059);
645 return 0;
646 }
647 break;
648 default:
649 return AE_BAD_ADDRESS;
650 }
651 return WMI_execute_u32(method_id, (u32)value, NULL);
652}
653
654static acpi_status WMID_set_capabilities(void)
655{
656 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
657 union acpi_object *obj;
658 acpi_status status;
659 u32 devices;
660
661 status = wmi_query_block(WMID_GUID2, 1, &out);
662 if (ACPI_FAILURE(status))
663 return status;
664
665 obj = (union acpi_object *) out.pointer;
666 if (obj && obj->type == ACPI_TYPE_BUFFER &&
667 obj->buffer.length == sizeof(u32)) {
668 devices = *((u32 *) obj->buffer.pointer);
669 } else {
670 return AE_ERROR;
671 }
672
673 /* Not sure on the meaning of the relevant bits yet to detect these */
674 interface->capability |= ACER_CAP_WIRELESS;
675 interface->capability |= ACER_CAP_THREEG;
676
677 /* WMID always provides brightness methods */
678 interface->capability |= ACER_CAP_BRIGHTNESS;
679
680 if (devices & 0x10)
681 interface->capability |= ACER_CAP_BLUETOOTH;
682
683 if (!(devices & 0x20))
684 max_brightness = 0x9;
685
686 return status;
687}
688
689static struct wmi_interface wmid_interface = {
690 .type = ACER_WMID,
691};
692
693/*
694 * Generic Device (interface-independent)
695 */
696
697static acpi_status get_u32(u32 *value, u32 cap)
698{
699 acpi_status status = AE_BAD_ADDRESS;
700
701 switch (interface->type) {
702 case ACER_AMW0:
703 status = AMW0_get_u32(value, cap, interface);
704 break;
705 case ACER_AMW0_V2:
706 if (cap == ACER_CAP_MAILLED) {
707 status = AMW0_get_u32(value, cap, interface);
708 break;
709 }
710 case ACER_WMID:
711 status = WMID_get_u32(value, cap, interface);
712 break;
713 }
714
715 return status;
716}
717
718static acpi_status set_u32(u32 value, u32 cap)
719{
720 if (interface->capability & cap) {
721 switch (interface->type) {
722 case ACER_AMW0:
723 return AMW0_set_u32(value, cap, interface);
724 case ACER_AMW0_V2:
725 case ACER_WMID:
726 return WMID_set_u32(value, cap, interface);
727 default:
728 return AE_BAD_PARAMETER;
729 }
730 }
731 return AE_BAD_PARAMETER;
732}
733
734static void __init acer_commandline_init(void)
735{
736 /*
737 * These will all fail silently if the value given is invalid, or the
738 * capability isn't available on the given interface
739 */
740 set_u32(mailled, ACER_CAP_MAILLED);
741 set_u32(wireless, ACER_CAP_WIRELESS);
742 set_u32(bluetooth, ACER_CAP_BLUETOOTH);
743 set_u32(threeg, ACER_CAP_THREEG);
744 set_u32(brightness, ACER_CAP_BRIGHTNESS);
745}
746
747/*
748 * LED device (Mail LED only, no other LEDs known yet)
749 */
750static void mail_led_set(struct led_classdev *led_cdev,
751enum led_brightness value)
752{
753 set_u32(value, ACER_CAP_MAILLED);
754}
755
756static struct led_classdev mail_led = {
757 .name = "acer-mail:green",
758 .brightness_set = mail_led_set,
759};
760
761static int __init acer_led_init(struct device *dev)
762{
763 return led_classdev_register(dev, &mail_led);
764}
765
766static void acer_led_exit(void)
767{
768 led_classdev_unregister(&mail_led);
769}
770
771/*
772 * Backlight device
773 */
774static struct backlight_device *acer_backlight_device;
775
776static int read_brightness(struct backlight_device *bd)
777{
778 u32 value;
779 get_u32(&value, ACER_CAP_BRIGHTNESS);
780 return value;
781}
782
783static int update_bl_status(struct backlight_device *bd)
784{
785 set_u32(bd->props.brightness, ACER_CAP_BRIGHTNESS);
786 return 0;
787}
788
789static struct backlight_ops acer_bl_ops = {
790 .get_brightness = read_brightness,
791 .update_status = update_bl_status,
792};
793
794static int __init acer_backlight_init(struct device *dev)
795{
796 struct backlight_device *bd;
797
798 bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops);
799 if (IS_ERR(bd)) {
800 printk(ACER_ERR "Could not register Acer backlight device\n");
801 acer_backlight_device = NULL;
802 return PTR_ERR(bd);
803 }
804
805 acer_backlight_device = bd;
806
807 bd->props.max_brightness = max_brightness;
808 bd->props.brightness = read_brightness(NULL);
809 backlight_update_status(bd);
810 return 0;
811}
812
813static void __exit acer_backlight_exit(void)
814{
815 backlight_device_unregister(acer_backlight_device);
816}
817
818/*
819 * Read/ write bool sysfs macro
820 */
821#define show_set_bool(value, cap) \
822static ssize_t \
823show_bool_##value(struct device *dev, struct device_attribute *attr, \
824 char *buf) \
825{ \
826 u32 result; \
827 acpi_status status = get_u32(&result, cap); \
828 if (ACPI_SUCCESS(status)) \
829 return sprintf(buf, "%u\n", result); \
830 return sprintf(buf, "Read error\n"); \
831} \
832\
833static ssize_t \
834set_bool_##value(struct device *dev, struct device_attribute *attr, \
835 const char *buf, size_t count) \
836{ \
837 u32 tmp = simple_strtoul(buf, NULL, 10); \
838 acpi_status status = set_u32(tmp, cap); \
839 if (ACPI_FAILURE(status)) \
840 return -EINVAL; \
841 return count; \
842} \
843static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
844 show_bool_##value, set_bool_##value);
845
846show_set_bool(wireless, ACER_CAP_WIRELESS);
847show_set_bool(bluetooth, ACER_CAP_BLUETOOTH);
848show_set_bool(threeg, ACER_CAP_THREEG);
849
850/*
851 * Read interface sysfs macro
852 */
853static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
854 char *buf)
855{
856 switch (interface->type) {
857 case ACER_AMW0:
858 return sprintf(buf, "AMW0\n");
859 case ACER_AMW0_V2:
860 return sprintf(buf, "AMW0 v2\n");
861 case ACER_WMID:
862 return sprintf(buf, "WMID\n");
863 default:
864 return sprintf(buf, "Error!\n");
865 }
866}
867
868static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR,
869 show_interface, NULL);
870
871/*
872 * Platform device
873 */
874static int __devinit acer_platform_probe(struct platform_device *device)
875{
876 int err;
877
878 if (has_cap(ACER_CAP_MAILLED)) {
879 err = acer_led_init(&device->dev);
880 if (err)
881 goto error_mailled;
882 }
883
884 if (has_cap(ACER_CAP_BRIGHTNESS)) {
885 err = acer_backlight_init(&device->dev);
886 if (err)
887 goto error_brightness;
888 }
889
890 return 0;
891
892error_brightness:
893 acer_led_exit();
894error_mailled:
895 return err;
896}
897
898static int acer_platform_remove(struct platform_device *device)
899{
900 if (has_cap(ACER_CAP_MAILLED))
901 acer_led_exit();
902 if (has_cap(ACER_CAP_BRIGHTNESS))
903 acer_backlight_exit();
904 return 0;
905}
906
907static int acer_platform_suspend(struct platform_device *dev,
908pm_message_t state)
909{
910 u32 value;
911 struct acer_data *data = &interface->data;
912
913 if (!data)
914 return -ENOMEM;
915
916 if (has_cap(ACER_CAP_WIRELESS)) {
917 get_u32(&value, ACER_CAP_WIRELESS);
918 data->wireless = value;
919 }
920
921 if (has_cap(ACER_CAP_BLUETOOTH)) {
922 get_u32(&value, ACER_CAP_BLUETOOTH);
923 data->bluetooth = value;
924 }
925
926 if (has_cap(ACER_CAP_MAILLED)) {
927 get_u32(&value, ACER_CAP_MAILLED);
928 data->mailled = value;
929 }
930
931 if (has_cap(ACER_CAP_BRIGHTNESS)) {
932 get_u32(&value, ACER_CAP_BRIGHTNESS);
933 data->brightness = value;
934 }
935
936 return 0;
937}
938
939static int acer_platform_resume(struct platform_device *device)
940{
941 struct acer_data *data = &interface->data;
942
943 if (!data)
944 return -ENOMEM;
945
946 if (has_cap(ACER_CAP_WIRELESS))
947 set_u32(data->wireless, ACER_CAP_WIRELESS);
948
949 if (has_cap(ACER_CAP_BLUETOOTH))
950 set_u32(data->bluetooth, ACER_CAP_BLUETOOTH);
951
952 if (has_cap(ACER_CAP_THREEG))
953 set_u32(data->threeg, ACER_CAP_THREEG);
954
955 if (has_cap(ACER_CAP_MAILLED))
956 set_u32(data->mailled, ACER_CAP_MAILLED);
957
958 if (has_cap(ACER_CAP_BRIGHTNESS))
959 set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
960
961 return 0;
962}
963
964static struct platform_driver acer_platform_driver = {
965 .driver = {
966 .name = "acer-wmi",
967 .owner = THIS_MODULE,
968 },
969 .probe = acer_platform_probe,
970 .remove = acer_platform_remove,
971 .suspend = acer_platform_suspend,
972 .resume = acer_platform_resume,
973};
974
975static struct platform_device *acer_platform_device;
976
977static int remove_sysfs(struct platform_device *device)
978{
979 if (has_cap(ACER_CAP_WIRELESS))
980 device_remove_file(&device->dev, &dev_attr_wireless);
981
982 if (has_cap(ACER_CAP_BLUETOOTH))
983 device_remove_file(&device->dev, &dev_attr_bluetooth);
984
985 if (has_cap(ACER_CAP_THREEG))
986 device_remove_file(&device->dev, &dev_attr_threeg);
987
988 device_remove_file(&device->dev, &dev_attr_interface);
989
990 return 0;
991}
992
993static int create_sysfs(void)
994{
995 int retval = -ENOMEM;
996
997 if (has_cap(ACER_CAP_WIRELESS)) {
998 retval = device_create_file(&acer_platform_device->dev,
999 &dev_attr_wireless);
1000 if (retval)
1001 goto error_sysfs;
1002 }
1003
1004 if (has_cap(ACER_CAP_BLUETOOTH)) {
1005 retval = device_create_file(&acer_platform_device->dev,
1006 &dev_attr_bluetooth);
1007 if (retval)
1008 goto error_sysfs;
1009 }
1010
1011 if (has_cap(ACER_CAP_THREEG)) {
1012 retval = device_create_file(&acer_platform_device->dev,
1013 &dev_attr_threeg);
1014 if (retval)
1015 goto error_sysfs;
1016 }
1017
1018 retval = device_create_file(&acer_platform_device->dev,
1019 &dev_attr_interface);
1020 if (retval)
1021 goto error_sysfs;
1022
1023 return 0;
1024
1025error_sysfs:
1026 remove_sysfs(acer_platform_device);
1027 return retval;
1028}
1029
1030static int __init acer_wmi_init(void)
1031{
1032 int err;
1033
1034 printk(ACER_INFO "Acer Laptop ACPI-WMI Extras version %s\n",
1035 ACER_WMI_VERSION);
1036
1037 /*
1038 * Detect which ACPI-WMI interface we're using.
1039 */
1040 if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1041 interface = &AMW0_V2_interface;
1042
1043 if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1044 interface = &wmid_interface;
1045
1046 if (wmi_has_guid(WMID_GUID2) && interface) {
1047 if (ACPI_FAILURE(WMID_set_capabilities())) {
1048 printk(ACER_ERR "Unable to detect available devices\n");
1049 return -ENODEV;
1050 }
1051 } else if (!wmi_has_guid(WMID_GUID2) && interface) {
1052 printk(ACER_ERR "Unable to detect available devices\n");
1053 return -ENODEV;
1054 }
1055
1056 if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
1057 interface = &AMW0_interface;
1058
1059 if (ACPI_FAILURE(AMW0_set_capabilities())) {
1060 printk(ACER_ERR "Unable to detect available devices\n");
1061 return -ENODEV;
1062 }
1063 }
1064
1065 if (wmi_has_guid(AMW0_GUID1)) {
1066 if (ACPI_FAILURE(AMW0_find_mailled()))
1067 printk(ACER_ERR "Unable to detect mail LED\n");
1068 }
1069
1070 find_quirks();
1071
1072 if (!interface) {
1073 printk(ACER_ERR "No or unsupported WMI interface, unable to ");
1074 printk(KERN_CONT "load.\n");
1075 return -ENODEV;
1076 }
1077
1078 if (platform_driver_register(&acer_platform_driver)) {
1079 printk(ACER_ERR "Unable to register platform driver.\n");
1080 goto error_platform_register;
1081 }
1082 acer_platform_device = platform_device_alloc("acer-wmi", -1);
1083 platform_device_add(acer_platform_device);
1084
1085 err = create_sysfs();
1086 if (err)
1087 return err;
1088
1089 /* Override any initial settings with values from the commandline */
1090 acer_commandline_init();
1091
1092 return 0;
1093
1094error_platform_register:
1095 return -ENODEV;
1096}
1097
1098static void __exit acer_wmi_exit(void)
1099{
1100 remove_sysfs(acer_platform_device);
1101 platform_device_del(acer_platform_device);
1102 platform_driver_unregister(&acer_platform_driver);
1103
1104 printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
1105 return;
1106}
1107
1108module_init(acer_wmi_init);
1109module_exit(acer_wmi_exit);