acer_acpi - Fix set method for AMW0 V2 mail LED
[acer_acpi.git] / acer_acpi.c
blob970fc19f199a5e877dc31b31c3c1a4858c56f327
1 /*
2 * Acer Laptop ACPI Extras
4 * Copyright (C) 2005-2007 E.M. Smith
5 * Copyright (C) 2007 Carlos Corbacho <cathectic@gmail.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * The devolpment page for this driver is located at
23 * http://code.google.com/p/aceracpi
25 * Credits:
27 * John Belmonte - the Toshiba ACPI driver originally adapted for this module.
28 * Julien Lerouge & Karol Kozimor - ASUS Acpi driver authors.
29 * Olaf Tauber - developer of acerhk, the inspiration to solve the 64-bit
30 * driver problem for my Aspire 5024.
31 * Mathieu Segaud - solved the ACPI problem that needed a double-modprobe
32 * in version 0.2 and below.
33 * Jim Ramsay - Figured out and added support for WMID interface
36 #define ACER_ACPI_VERSION "0.11.0"
39 * Comment the following line out to remove /proc support
41 #define CONFIG_PROC
43 #ifdef CONFIG_PROC
44 #define PROC_ACER "acer"
45 #include <linux/proc_fs.h>
46 #endif
48 #include <linux/kernel.h>
49 #include <linux/module.h>
50 #include <linux/init.h>
51 #include <linux/types.h>
52 #include <linux/delay.h>
53 #include <linux/version.h>
55 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17)
56 #include <asm/uaccess.h>
57 #else
58 #include <linux/uaccess.h>
59 #endif
61 #include <linux/io.h>
62 #include <linux/dmi.h>
63 #include <linux/backlight.h>
64 #include <linux/leds.h>
65 #include <linux/platform_device.h>
67 #include <acpi/acpi_drivers.h>
69 #include "wmi-acer.h"
71 /* Workaround needed for older kernels */
72 #ifndef bool
73 #define bool int
74 #endif
76 MODULE_AUTHOR("Mark Smith, Carlos Corbacho");
77 MODULE_DESCRIPTION("Acer Laptop ACPI Extras Driver");
78 MODULE_LICENSE("GPL");
80 MODULE_ALIAS("dmi:*:*Acer*:*:");
82 #define ACER_LOGPREFIX "acer_acpi: "
83 #define ACER_ERR KERN_ERR ACER_LOGPREFIX
84 #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
85 #define ACER_INFO KERN_INFO ACER_LOGPREFIX
87 #define DEBUG(level, message...) { \
88 if (debug >= level) \
89 printk(KERN_DEBUG ACER_LOGPREFIX message);\
93 * The maximum temperature one can set for fan control override.
94 * Doesn't propably make much sense if over 80 degrees celsius though...
96 #define ACER_MAX_TEMPERATURE_OVERRIDE 150
99 * The following defines quirks to get some specific functions to work
100 * which are known to not be supported over ACPI (such as the mail LED
101 * on WMID based Acer's)
103 struct acer_quirks {
104 const char *vendor;
105 const char *model;
106 u16 quirks;
110 * Keyboard controller ports
112 #define ACER_KBD_STATUS_REG 0x64 /* Status register (R) */
113 #define ACER_KBD_CNTL_REG 0x64 /* Controller command register (W) */
114 #define ACER_KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */
117 * Magic Number
118 * Meaning is unknown - this number is required for writing to ACPI for AMW0
119 * (it's also used in acerhk when directly accessing the EC)
121 #define ACER_AMW0_WRITE 0x9610
124 * Bit masks for the old AMW0 interface
126 #define ACER_AMW0_WIRELESS_MASK 0x35
127 #define ACER_AMW0_BLUETOOTH_MASK 0x34
128 #define ACER_AMW0_MAILLED_MASK 0x31
131 * Method IDs for new WMID interface
133 #define ACER_WMID_GET_WIRELESS_METHODID 1
134 #define ACER_WMID_GET_BLUETOOTH_METHODID 2
135 #define ACER_WMID_GET_BRIGHTNESS_METHODID 3
136 #define ACER_WMID_SET_WIRELESS_METHODID 4
137 #define ACER_WMID_SET_BLUETOOTH_METHODID 5
138 #define ACER_WMID_SET_BRIGHTNESS_METHODID 6
139 #define ACER_WMID_GET_THREEG_METHODID 10
140 #define ACER_WMID_SET_THREEG_METHODID 11
143 * Acer ACPI method GUIDs
145 #define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
146 #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
147 #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A"
150 * Interface capability flags
152 #define ACER_CAP_MAILLED (1<<0)
153 #define ACER_CAP_WIRELESS (1<<1)
154 #define ACER_CAP_BLUETOOTH (1<<2)
155 #define ACER_CAP_BRIGHTNESS (1<<3)
156 #define ACER_CAP_THREEG (1<<4)
157 #define ACER_CAP_TOUCHPAD_READ (1<<5)
158 #define ACER_CAP_TEMPERATURE_OVERRIDE (1<<6)
159 #define ACER_CAP_ANY (0xFFFFFFFF)
162 * Interface type flags
164 enum interface_flags {
165 ACER_AMW0,
166 ACER_AMW0_V2,
167 ACER_WMID,
171 * Presumed start states -
172 * On some AMW0 laptops, we do not yet know how to get the device status from
173 * the EC, so we must store this ourselves.
175 * Plus, we can't tell which features are enabled or disabled on a specific
176 * model - e.g. The 5020 series can _support_ bluetooth; but the 5021 has no
177 * bluetooth, whilst the 5024 does. However, the BIOS identifies both laptops
178 * as 5020, and you can add bluetooth later.
180 * Basically the code works like this:
181 * - On init, any values specified on the commandline are set.
182 * - For interfaces where the current values cannot be detected and which
183 * have not been set on the commandline, we set them to some sane default
184 * (disabled)
186 * See AMW0_init and acer_commandline_init
189 #define ACER_DEFAULT_WIRELESS 0
190 #define ACER_DEFAULT_BLUETOOTH 0
191 #define ACER_DEFAULT_MAILLED 0
192 #define ACER_DEFAULT_THREEG 0
194 static int max_brightness = 0xF;
196 static int wireless = -1;
197 static int bluetooth = -1;
198 static int mailled = -1;
199 static int brightness = -1;
200 static int threeg = -1;
201 static int fan_temperature_override = -1;
202 static int debug;
203 static int force_series;
205 module_param(mailled, int, 0444);
206 module_param(wireless, int, 0444);
207 module_param(bluetooth, int, 0444);
208 module_param(brightness, int, 0444);
209 module_param(threeg, int, 0444);
210 module_param(force_series, int, 0444);
211 module_param(fan_temperature_override, int, 0444);
212 module_param(debug, int, 0664);
213 MODULE_PARM_DESC(wireless, "Set initial state of Wireless hardware");
214 MODULE_PARM_DESC(bluetooth, "Set initial state of Bluetooth hardware");
215 MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
216 MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
217 MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
218 MODULE_PARM_DESC(fan_temperature_override, "Set initial state of the 'FAN temperature-override'");
219 MODULE_PARM_DESC(debug, "Debugging verbosity level (0=least 2=most)");
220 MODULE_PARM_DESC(force_series, "Force a different laptop series for extra features (5020, 5720 or 2490)");
222 #ifdef CONFIG_PROC
223 struct ProcItem {
224 const char *name;
225 char *(*read_func) (char *, u32);
226 unsigned long (*write_func) (const char *, unsigned long, u32);
227 unsigned int capability;
230 static struct proc_dir_entry *acer_proc_dir;
231 #endif
234 * Wait for the keyboard controller to become ready
236 static int wait_kbd_write(void)
238 int i = 0;
239 while ((inb(ACER_KBD_STATUS_REG) & 0x02) && (i < 10000)) {
240 udelay(50);
241 i++;
243 return -(i == 10000);
246 static void send_kbd_cmd(u8 cmd, u8 val)
248 preempt_disable();
249 if (!wait_kbd_write())
250 outb(cmd, ACER_KBD_CNTL_REG);
251 if (!wait_kbd_write())
252 outb(val, ACER_KBD_DATA_REG);
253 preempt_enable_no_resched();
256 static void set_keyboard_quirk(void)
258 send_kbd_cmd(0x59, 0x90);
261 struct acer_data {
262 int mailled;
263 int wireless;
264 int bluetooth;
265 int threeg;
266 int brightness;
269 /* Each low-level interface must define at least some of the following */
270 struct Interface {
271 u32 type; /* WMI device type */
272 u32 capability; /* The capabilities this interface provides */
273 struct acer_data data; /* Private data for interface */
276 /* The static interface pointer, points to the currently detected interface */
277 static struct Interface *interface;
280 * Embedded Controller quirks
281 * Some laptops require us to directly access the EC to either enable or query
282 * features that are not available through ACPI.
285 struct quirk_entry {
286 u8 wireless;
287 u8 mailled;
288 u8 brightness;
289 u8 touchpad;
290 u8 temperature_override;
291 u8 mmkeys;
292 u8 bluetooth;
295 static struct quirk_entry *quirks;
297 static void set_quirks(void)
299 if (quirks->mailled != 0) {
300 interface->capability |= ACER_CAP_MAILLED;
301 DEBUG(1, "Using EC direct-access quirk for mail LED\n");
304 if (quirks->touchpad != 0) {
305 interface->capability |= ACER_CAP_TOUCHPAD_READ;
306 DEBUG(1, "Using EC direct-access quirk for reading touchpad status\n");
309 if (quirks->temperature_override != 0) {
310 interface->capability |= ACER_CAP_TEMPERATURE_OVERRIDE;
311 DEBUG(1, "Using EC direct-access quirk for temperature override setting (fan)\n");
314 if (quirks->brightness != 0) {
315 interface->capability |= ACER_CAP_BRIGHTNESS;
316 DEBUG(1, "Using EC direct-access quirk for backlight brightness\n");
319 if (quirks->mmkeys != 0) {
320 set_keyboard_quirk();
321 printk(ACER_INFO "Setting keyboard quirk to enable multimedia keys\n");
324 if (quirks->bluetooth != 0) {
325 interface->capability |= ACER_CAP_BLUETOOTH;
326 DEBUG(1, "Using EC direct-access quirk for bluetooth\n");
329 if (quirks->wireless != 0) {
330 interface->capability |= ACER_CAP_WIRELESS;
331 DEBUG(1, "Using EC direct-access quirk for wireless\n");
335 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
336 static int dmi_matched(const struct dmi_system_id *dmi)
337 #else
338 static int dmi_matched(struct dmi_system_id *dmi)
339 #endif
341 quirks = dmi->driver_data;
342 return 0;
345 static struct quirk_entry quirk_unknown = {
348 /* Same Mail LED quirk as TM2490, but does not require keyboard quirk */
349 static struct quirk_entry quirk_acer_aspire_5100 = {
350 .mailled = 1,
353 static struct quirk_entry quirk_acer_travelmate_2490 = {
354 .mmkeys = 1,
355 .mailled = 1,
356 .temperature_override = 1,
357 .touchpad = 1,
360 static struct quirk_entry quirk_acer_travelmate_5720 = {
361 .touchpad = 2,
364 static struct dmi_system_id acer_quirks[] = {
366 .callback = dmi_matched,
367 .ident = "Acer Aspire 3100",
368 .matches = {
369 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
370 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
372 .driver_data = &quirk_acer_aspire_5100,
375 .callback = dmi_matched,
376 .ident = "Acer Aspire 5100",
377 .matches = {
378 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
379 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
381 .driver_data = &quirk_acer_aspire_5100,
384 .callback = dmi_matched,
385 .ident = "Acer Aspire 5630",
386 .matches = {
387 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
388 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
390 .driver_data = &quirk_acer_travelmate_2490,
393 .callback = dmi_matched,
394 .ident = "Acer Aspire 5650",
395 .matches = {
396 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
397 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
399 .driver_data = &quirk_acer_travelmate_2490,
402 .callback = dmi_matched,
403 .ident = "Acer Aspire 5680",
404 .matches = {
405 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
406 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
408 .driver_data = &quirk_acer_travelmate_2490,
411 .callback = dmi_matched,
412 .ident = "Acer Extensa 5220",
413 .matches = {
414 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
415 DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5220"),
417 .driver_data = &quirk_acer_travelmate_5720,
420 .callback = dmi_matched,
421 .ident = "Acer TravelMate 2490",
422 .matches = {
423 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
424 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
426 .driver_data = &quirk_acer_travelmate_2490,
429 .callback = dmi_matched,
430 .ident = "Acer TravelMate 5720",
431 .matches = {
432 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
433 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5720"),
435 .driver_data = &quirk_acer_travelmate_5720,
440 /* Find which quirks are needed for a particular vendor/ model pair */
441 static void find_quirks(void)
443 DEBUG(1, "Looking for quirks\n");
444 if (!force_series) {
445 dmi_check_system(acer_quirks);
446 } else if (force_series == 2490) {
447 DEBUG(0, "Forcing Acer TravelMate 2490\n");
448 quirks = &quirk_acer_travelmate_2490;
451 if (quirks == NULL) {
452 DEBUG(1, "No quirks known for this laptop\n");
453 quirks = &quirk_unknown;
455 set_quirks();
459 * General interface convenience methods
462 static bool has_cap(u32 cap)
464 if ((interface->capability & cap) != 0)
465 return 1;
466 return 0;
470 * Old interface (now known as the AMW0 interface)
472 struct WMAB_args {
473 u32 eax;
474 u32 ebx;
475 u32 ecx;
476 u32 edx;
479 static acpi_status WMAB_execute(struct WMAB_args *regbuf, struct acpi_buffer *result)
481 struct acpi_buffer input;
482 acpi_status status;
483 input.length = sizeof(struct WMAB_args);
484 input.pointer = (u8 *) regbuf;
486 status = wmi_acer_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
487 DEBUG(2, " Args: 0x%08x 0x%08x 0x%08x 0x%08x\n", regbuf->eax, regbuf->ebx, regbuf->ecx, regbuf->edx );
489 return status;
492 static acpi_status AMW0_get_u32(u32 *value, u32 cap, struct Interface *iface)
494 u8 result;
496 DEBUG(2, " AMW0_get_u32: cap=%d\n", cap);
497 switch (cap) {
498 case ACER_CAP_MAILLED:
499 switch (quirks->mailled) {
500 default:
501 ec_read(0x0A, &result);
502 *value = (result >> 7) & 0x01;
503 return 0;
505 break;
506 case ACER_CAP_WIRELESS:
507 switch (quirks->wireless) {
508 default:
509 ec_read(0x0A, &result);
510 *value = (result >> 2) & 0x01;
511 return 0;
513 break;
514 case ACER_CAP_BLUETOOTH:
515 switch (quirks->bluetooth) {
516 default:
517 ec_read(0x0A, &result);
518 *value = (result >> 4) & 0x01;
519 return 0;
521 break;
522 case ACER_CAP_BRIGHTNESS:
523 switch (quirks->brightness) {
524 default:
525 ec_read(0x83, &result);
526 *value = result;
527 return 0;
529 break;
530 default:
531 return AE_BAD_ADDRESS;
533 return AE_OK;
536 static acpi_status AMW0_set_u32(u32 value, u32 cap, struct Interface *iface)
538 struct WMAB_args args;
540 args.eax = ACER_AMW0_WRITE;
541 args.ebx = value ? (1<<8) : 0;
542 args.ecx = args.edx = 0;
544 switch (cap) {
545 case ACER_CAP_MAILLED:
546 if (value > 1)
547 return AE_BAD_PARAMETER;
548 args.ebx |= ACER_AMW0_MAILLED_MASK;
549 break;
550 case ACER_CAP_WIRELESS:
551 if (value > 1)
552 return AE_BAD_PARAMETER;
553 args.ebx |= ACER_AMW0_WIRELESS_MASK;
554 break;
555 case ACER_CAP_BLUETOOTH:
556 if (value > 1)
557 return AE_BAD_PARAMETER;
558 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
559 break;
560 case ACER_CAP_BRIGHTNESS:
561 if (value > max_brightness)
562 return AE_BAD_PARAMETER;
563 switch (quirks->brightness) {
564 case 1:
565 return ec_write(0x83, value);
566 default:
567 return AE_BAD_ADDRESS;
568 break;
570 default:
571 return AE_BAD_ADDRESS;
574 /* Actually do the set */
575 return WMAB_execute(&args, NULL);
578 static struct Interface AMW0_interface = {
579 .type = ACER_AMW0,
580 .capability = (
581 ACER_CAP_MAILLED |
582 ACER_CAP_WIRELESS |
583 ACER_CAP_BLUETOOTH
587 static struct Interface AMW0_V2_interface = {
588 .type = ACER_AMW0_V2,
589 .capability = (
590 ACER_CAP_BRIGHTNESS |
591 ACER_CAP_MAILLED
596 * New interface (The WMID interface)
598 static acpi_status
599 WMI_execute_u32(u32 method_id, u32 in, u32 *out)
601 struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
602 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
603 union acpi_object *obj;
604 u32 tmp;
605 acpi_status status;
607 DEBUG(2, " WMI_execute_u32:\n");
608 status = wmi_acer_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
609 DEBUG(2, " In: 0x%08x\n", in);
611 if (ACPI_FAILURE(status))
612 return status;
614 obj = (union acpi_object *) result.pointer;
615 if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == sizeof(u32)) {
616 tmp = *((u32 *) obj->buffer.pointer);
617 DEBUG(2, " Out: 0x%08x\n", tmp);
618 } else {
619 tmp = 0;
620 if (obj) {
621 DEBUG(2, " Got unexpected result of type %d\n", obj->type);
622 } else {
623 DEBUG(2, " Got unexpected null result\n");
627 if (out)
628 *out = tmp;
630 if (result.length > 0 && result.pointer)
631 kfree(result.pointer);
633 DEBUG(2, " Returning from WMI_execute_u32:\n");
634 return status;
637 static acpi_status WMID_get_u32(u32 *value, u32 cap, struct Interface *iface)
639 acpi_status status;
640 u8 tmp;
641 u32 result, method_id = 0;
643 DEBUG(2, " WMID_get_u32: cap=%d\n", cap);
644 switch (cap) {
645 case ACER_CAP_WIRELESS:
646 method_id = ACER_WMID_GET_WIRELESS_METHODID;
647 break;
648 case ACER_CAP_BLUETOOTH:
649 method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
650 break;
651 case ACER_CAP_BRIGHTNESS:
652 method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
653 break;
654 case ACER_CAP_THREEG:
655 method_id = ACER_WMID_GET_THREEG_METHODID;
656 break;
657 case ACER_CAP_MAILLED:
658 if (quirks->mailled == 1) {
659 ec_read(0x9f, &tmp);
660 *value = tmp;
661 return 0;
663 case ACER_CAP_TOUCHPAD_READ:
664 switch (quirks->touchpad) {
665 case 1:
666 ec_read(0x9e, &tmp);
667 *value = 1 - ((tmp >> 3) & 0x01);
668 return 0;
669 case 2:
670 ec_read(0x74, &tmp);
671 *value = ((tmp >> 3) & 0x01);
672 return 0;
673 default:
674 break;
676 case ACER_CAP_TEMPERATURE_OVERRIDE:
677 if (quirks->temperature_override == 1) {
678 ec_read(0xa9, &tmp);
679 *value = tmp;
680 return 0;
682 default:
683 return AE_BAD_ADDRESS;
685 status = WMI_execute_u32(method_id, 0, &result);
686 DEBUG(2, " WMI_execute_u32 status=%d:\n", status);
688 if (ACPI_SUCCESS(status))
689 *value = (u8)result;
691 DEBUG(2, " Returning from WMID_get_u32:\n");
692 return status;
695 static acpi_status WMID_set_u32(u32 value, u32 cap, struct Interface *iface)
697 u32 method_id = 0;
699 switch (cap) {
700 case ACER_CAP_BRIGHTNESS:
701 if (value > max_brightness)
702 return AE_BAD_PARAMETER;
703 method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
704 break;
705 case ACER_CAP_WIRELESS:
706 if (value > 1)
707 return AE_BAD_PARAMETER;
708 method_id = ACER_WMID_SET_WIRELESS_METHODID;
709 break;
710 case ACER_CAP_BLUETOOTH:
711 if (value > 1)
712 return AE_BAD_PARAMETER;
713 method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
714 break;
715 case ACER_CAP_THREEG:
716 if (value > 1)
717 return AE_BAD_PARAMETER;
718 method_id = ACER_WMID_SET_THREEG_METHODID;
719 break;
720 case ACER_CAP_MAILLED:
721 if (value > 1)
722 return AE_BAD_PARAMETER;
723 if (quirks->mailled == 1) {
724 send_kbd_cmd(0x59, value ? 0x92 : 0x93);
725 return 0;
727 break;
728 case ACER_CAP_TEMPERATURE_OVERRIDE:
729 if (value > ACER_MAX_TEMPERATURE_OVERRIDE)
730 return AE_BAD_PARAMETER;
732 if (quirks->temperature_override == 1) {
733 ec_write(0xa9, value);
734 return 0;
736 default:
737 return AE_BAD_ADDRESS;
739 return WMI_execute_u32(method_id, (u32)value, NULL);
742 static acpi_status WMID_set_capabilities(void) {
743 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
744 union acpi_object *obj;
745 acpi_status status;
746 u32 devices;
748 status = wmi_acer_query_block(WMID_GUID2, 1, &out);
749 if (ACPI_FAILURE(status))
750 return status;
752 obj = (union acpi_object *) out.pointer;
753 if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == sizeof(u32)) {
754 devices = *((u32 *) obj->buffer.pointer);
755 } else {
756 return AE_ERROR;
759 /* Not sure on the meaning of the relevant bits yet */
760 interface->capability |= ACER_CAP_WIRELESS;
761 interface->capability |= ACER_CAP_THREEG;
763 if (devices & 0x10) {
764 DEBUG(1, "Bluetooth hardware available - enabling\n");
765 interface->capability |= ACER_CAP_BLUETOOTH;
768 if (!(devices & 0x20)) {
769 DEBUG(1, "Maximum brightness quirk detected - enabling\n");
770 max_brightness = 0x9;
773 return status;
777 static struct Interface WMID_interface = {
778 .type = ACER_WMID,
779 .capability = ACER_CAP_BRIGHTNESS,
782 #ifdef CONFIG_PROC
784 * High-level Procfs file handlers
787 static int
788 dispatch_read(char *page, char **start, off_t off, int count, int *eof,
789 struct ProcItem *item)
791 char *p = page;
792 int len;
794 DEBUG(2, " dispatch_read: \n");
795 if (off == 0)
796 p = item->read_func(p, item->capability);
797 len = (p - page);
798 if (len <= off + count)
799 *eof = 1;
800 *start = page + off;
801 len -= off;
802 if (len > count)
803 len = count;
804 if (len < 0)
805 len = 0;
806 return len;
809 static int
810 dispatch_write(struct file *file, const char __user *buffer,
811 unsigned long count, struct ProcItem *item)
813 int result;
814 char *tmp_buffer;
817 * Arg buffer points to userspace memory, which can't be accessed
818 * directly. Since we're making a copy, zero-terminate the
819 * destination so that sscanf can be used on it safely.
821 tmp_buffer = kmalloc(count + 1, GFP_KERNEL);
822 if (copy_from_user(tmp_buffer, buffer, count)) {
823 result = -EFAULT;
824 } else {
825 tmp_buffer[count] = 0;
826 result = item->write_func(tmp_buffer, count, item->capability);
828 kfree(tmp_buffer);
829 return result;
831 #endif
834 * Generic Device (interface-independent)
836 static acpi_status get_u32(u32 *value, u32 cap)
838 DEBUG(2, " get_u32: cap=%u, value=%u\n", cap, *value);
840 if (!(interface->capability & cap))
841 return AE_BAD_PARAMETER;
843 switch (interface->type) {
844 case ACER_AMW0:
845 return AMW0_get_u32(value, cap, interface);
846 case ACER_AMW0_V2:
847 if (cap == ACER_CAP_MAILLED)
848 return AMW0_get_u32(value, cap, interface);
849 case ACER_WMID:
850 return WMID_get_u32(value, cap, interface);
851 default:
852 return AE_BAD_PARAMETER;
856 static acpi_status set_u32(u32 value, u32 cap)
858 DEBUG(2, " set_u32: cap=%u, value=%u\n", cap, value);
860 if (!(interface->capability & cap))
861 return AE_BAD_PARAMETER;
863 switch (interface->type) {
864 case ACER_AMW0:
865 return AMW0_set_u32(value, cap, interface);
866 case ACER_AMW0_V2:
867 if (cap == ACER_CAP_MAILLED)
868 return AMW0_set_u32(value, cap, interface);
869 case ACER_WMID:
870 return WMID_set_u32(value, cap, interface);
871 default:
872 return AE_BAD_PARAMETER;
876 static void __init acer_commandline_init(void)
878 DEBUG(1, "Commandline args: mailled(%d) wireless(%d) bluetooth(%d) brightness(%d)\n",
879 mailled, wireless, bluetooth, brightness);
882 * These will all fail silently if the value given is invalid, or the
883 * capability isn't available on the given interface
885 set_u32(mailled, ACER_CAP_MAILLED);
886 set_u32(wireless, ACER_CAP_WIRELESS);
887 set_u32(bluetooth, ACER_CAP_BLUETOOTH);
888 set_u32(threeg, ACER_CAP_THREEG);
889 set_u32(fan_temperature_override, ACER_CAP_TEMPERATURE_OVERRIDE);
890 set_u32(brightness, ACER_CAP_BRIGHTNESS);
893 #ifdef CONFIG_PROC
895 * Procfs interface (deprecated)
897 static char *read_u32(char *p, u32 cap)
899 u32 result;
900 acpi_status status;
902 DEBUG(2, " read_u32: cap=%d\n", cap);
903 status = get_u32(&result, cap);
904 if (ACPI_SUCCESS(status))
905 p += sprintf(p, "%u\n", result);
906 else
907 p += sprintf(p, "Read error\n");
908 return p;
911 static unsigned long write_u32(const char *buffer, unsigned long count, u32 cap)
913 u32 value;
915 if (sscanf(buffer, "%u", &value) == 1) {
916 acpi_status status = set_u32(value, cap);
917 if (ACPI_FAILURE(status))
918 return -EINVAL;
919 } else {
920 return -EINVAL;
922 return count;
925 static char *read_version(char *p, u32 cap)
927 p += sprintf(p, "%s\n", ACER_ACPI_VERSION);
928 return p;
931 static char *read_interface(char *p, u32 cap)
933 p += sprintf(p, "%s\n", (interface->type == ACER_AMW0) ? "AMW0": "WMID");
934 return p;
937 static struct ProcItem proc_items[] = {
938 {"mailled", read_u32, write_u32, ACER_CAP_MAILLED},
939 {"bluetooth", read_u32, write_u32, ACER_CAP_BLUETOOTH},
940 {"wireless", read_u32, write_u32, ACER_CAP_WIRELESS},
941 {"brightness", read_u32, write_u32, ACER_CAP_BRIGHTNESS},
942 {"threeg", read_u32, write_u32, ACER_CAP_THREEG},
943 {"touchpad", read_u32, NULL, ACER_CAP_TOUCHPAD_READ},
944 {"fan_temperature_override", read_u32, write_u32, ACER_CAP_TEMPERATURE_OVERRIDE},
945 {"version", read_version, NULL, ACER_CAP_ANY},
946 {"interface", read_interface, NULL, ACER_CAP_ANY},
947 {NULL}
950 static int __init add_proc_entries(void)
952 struct proc_dir_entry *proc;
953 struct ProcItem *item;
955 for (item = proc_items; item->name; ++item) {
957 * Only add the proc file if the current interface actually
958 * supports it
960 if (interface->capability & item->capability) {
961 proc = create_proc_read_entry(item->name,
962 S_IFREG | S_IRUGO | S_IWUSR,
963 acer_proc_dir,
964 (read_proc_t *) dispatch_read,
965 item);
966 if (proc)
967 proc->owner = THIS_MODULE;
968 if (proc && item->write_func)
969 proc->write_proc = (write_proc_t *) dispatch_write;
973 return 0;
976 static int __exit remove_proc_entries(void)
978 struct ProcItem *item;
980 for (item = proc_items; item->name; ++item)
981 remove_proc_entry(item->name, acer_proc_dir);
982 return 0;
984 #endif
987 * LED device (Mail LED only, no other LEDs known yet)
989 static void mail_led_set(struct led_classdev *led_cdev, enum led_brightness value)
991 u32 tmp = value;
992 set_u32(tmp, ACER_CAP_MAILLED);
995 static struct led_classdev mail_led = {
996 .name = "acer_acpi:mail",
997 .brightness_set = mail_led_set,
1000 static void acer_led_init(struct device *dev)
1002 led_classdev_register(dev, &mail_led);
1005 static void acer_led_exit(void)
1007 led_classdev_unregister(&mail_led);
1010 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
1012 * Backlight device
1014 static struct backlight_device *acer_backlight_device;
1016 static int read_brightness(struct backlight_device *bd)
1018 u32 value;
1019 get_u32(&value, ACER_CAP_BRIGHTNESS);
1020 return value;
1023 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,20)
1024 static int update_bl_status(struct backlight_device *bd)
1026 set_u32(bd->props->brightness, ACER_CAP_BRIGHTNESS);
1027 return 0;
1030 static struct backlight_properties acer_backlight_properties = {
1031 .get_brightness = read_brightness,
1032 .update_status = update_bl_status,
1035 static int __init acer_backlight_init(struct device *dev)
1037 struct backlight_device *bd;
1039 DEBUG(1, "Loading backlight driver\n");
1040 bd = backlight_device_register("acer_acpi", dev, NULL, &acer_backlight_properties);
1041 if (IS_ERR(bd)) {
1042 printk(ACER_ERR "Could not register Acer backlight device\n");
1043 acer_backlight_device = NULL;
1044 return PTR_ERR(bd);
1047 acer_backlight_device = bd;
1049 bd->props->max_brightness = max_brightness;
1050 return 0;
1052 #else
1053 static int update_bl_status(struct backlight_device *bd)
1055 set_u32(bd->props.brightness, ACER_CAP_BRIGHTNESS);
1056 return 0;
1059 static struct backlight_ops acer_backlight_ops = {
1060 .get_brightness = read_brightness,
1061 .update_status = update_bl_status,
1064 static int __init acer_backlight_init(struct device *dev)
1066 struct backlight_device *bd;
1068 DEBUG(1, "Loading backlight driver\n");
1069 bd = backlight_device_register("acer_acpi", dev, NULL, &acer_backlight_ops);
1070 if (IS_ERR(bd)) {
1071 printk(ACER_ERR "Could not register Acer backlight device\n");
1072 acer_backlight_device = NULL;
1073 return PTR_ERR(bd);
1076 acer_backlight_device = bd;
1078 bd->props.max_brightness = max_brightness;
1079 bd->props.brightness = read_brightness(NULL);
1080 backlight_update_status(bd);
1081 return 0;
1083 #endif
1085 static void __exit acer_backlight_exit(void)
1087 backlight_device_unregister(acer_backlight_device);
1089 #endif
1092 * Read/ write bool sysfs macro
1094 #define show_set_bool(value, cap) \
1095 static ssize_t \
1096 show_bool_##value(struct device *dev, struct device_attribute *attr, \
1097 char *buf) \
1099 u32 result; \
1100 acpi_status status = get_u32(&result, cap); \
1101 if (ACPI_SUCCESS(status)) \
1102 return sprintf(buf, "%u\n", result); \
1103 return sprintf(buf, "Read error\n"); \
1106 static ssize_t \
1107 set_bool_##value(struct device *dev, struct device_attribute *attr, \
1108 const char *buf, size_t count) \
1110 u32 tmp = simple_strtoul(buf, NULL, 10); \
1111 acpi_status status = set_u32(tmp, cap); \
1112 if (ACPI_FAILURE(status)) \
1113 return -EINVAL; \
1114 return count; \
1116 static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
1117 show_bool_##value, set_bool_##value);
1119 show_set_bool(wireless, ACER_CAP_WIRELESS);
1120 show_set_bool(bluetooth, ACER_CAP_BLUETOOTH);
1121 show_set_bool(threeg, ACER_CAP_THREEG);
1122 show_set_bool(fan_temperature_override, ACER_CAP_TEMPERATURE_OVERRIDE);
1125 * Read-only bool sysfs macro
1127 #define show_bool(value, cap) \
1128 static ssize_t \
1129 show_bool_##value(struct device *dev, struct device_attribute *attr, \
1130 char *buf) \
1132 u32 result; \
1133 acpi_status status = get_u32(&result, cap); \
1134 if (ACPI_SUCCESS(status)) \
1135 return sprintf(buf, "%u\n", result); \
1136 return sprintf(buf, "Read error\n"); \
1138 static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
1139 show_bool_##value, NULL);
1141 show_bool(touchpad, ACER_CAP_TOUCHPAD_READ);
1144 * Read interface sysfs macro
1146 static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
1147 char *buf)
1149 switch (interface->type) {
1150 case ACER_AMW0:
1151 return sprintf(buf, "AMW0\n");
1152 case ACER_AMW0_V2:
1153 return sprintf(buf, "AMW0 v2\n");
1154 case ACER_WMID:
1155 return sprintf(buf, "WMID\n");
1156 default:
1157 return sprintf(buf, "Error!\n");
1161 static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR, show_interface, NULL);
1163 static ssize_t show_devices(struct device *dev, struct device_attribute *attr,
1164 char *buf)
1166 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
1167 union acpi_object *obj;
1168 acpi_status status;
1169 u32 tmp;
1171 status = wmi_acer_query_block(WMID_GUID2, 1, &out);
1172 if (ACPI_FAILURE(status))
1173 return sprintf(buf, "Error\n");
1175 obj = (union acpi_object *) out.pointer;
1176 if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == sizeof(u32)) {
1177 tmp = *((u32 *) obj->buffer.pointer);
1178 } else {
1179 return sprintf(buf, "Error\n");
1182 return sprintf(buf, "%u\n", tmp);
1185 static DEVICE_ATTR(devices, S_IWUGO | S_IRUGO | S_IWUSR, show_devices, NULL);
1188 * Platform device
1190 static int __devinit acer_platform_probe(struct platform_device *device)
1192 if (has_cap(ACER_CAP_MAILLED))
1193 acer_led_init(&device->dev);
1194 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
1195 if (has_cap(ACER_CAP_BRIGHTNESS))
1196 acer_backlight_init(&device->dev);
1197 #endif
1198 return 0;
1201 static int acer_platform_remove(struct platform_device *device)
1203 if (has_cap(ACER_CAP_MAILLED))
1204 acer_led_exit();
1205 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
1206 if (has_cap(ACER_CAP_BRIGHTNESS))
1207 acer_backlight_exit();
1208 #endif
1209 return 0;
1212 static int acer_platform_suspend(struct platform_device *device, pm_message_t state)
1215 * WMID fix for suspend-to-disk - save all current states now so we can
1216 * restore them on resume
1218 u32 value;
1219 struct acer_data *data = &interface->data;
1221 #define save_device(device, cap) \
1222 if (has_cap(cap)) {\
1223 get_u32(&value, cap);\
1224 data->device = value;\
1227 save_device(wireless, ACER_CAP_WIRELESS);
1228 save_device(bluetooth, ACER_CAP_BLUETOOTH);
1229 save_device(threeg, ACER_CAP_THREEG);
1230 save_device(mailled, ACER_CAP_MAILLED);
1231 save_device(brightness, ACER_CAP_BRIGHTNESS);
1233 return 0;
1236 static int acer_platform_resume(struct platform_device *device)
1238 struct acer_data *data = &interface->data;
1240 #define restore_bool_device(device, cap) \
1241 if (has_cap(cap))\
1242 set_u32(data->device, cap);\
1244 restore_bool_device(wireless, ACER_CAP_WIRELESS);
1245 restore_bool_device(bluetooth, ACER_CAP_BLUETOOTH);
1246 restore_bool_device(threeg, ACER_CAP_THREEG);
1247 restore_bool_device(mailled, ACER_CAP_MAILLED);
1248 restore_bool_device(brightness, ACER_CAP_BRIGHTNESS);
1250 /* Check if this laptop requires the keyboard quirk */
1251 if (quirks->mmkeys != 0) {
1252 set_keyboard_quirk();
1253 printk(ACER_INFO "Setting keyboard quirk to enable multimedia keys\n");
1256 return 0;
1259 static struct platform_driver acer_platform_driver = {
1260 .driver = {
1261 .name = "acer_acpi",
1262 .owner = THIS_MODULE,
1264 .probe = acer_platform_probe,
1265 .remove = acer_platform_remove,
1266 .suspend = acer_platform_suspend,
1267 .resume = acer_platform_resume,
1270 static struct platform_device *acer_platform_device;
1272 static int remove_sysfs(struct platform_device *device)
1274 #define remove_device_file(value, cap) \
1275 if (has_cap(cap)) \
1276 device_remove_file(&device->dev, &dev_attr_##value);
1278 if (wmi_acer_has_guid(WMID_GUID2))
1279 device_remove_file(&device->dev, &dev_attr_devices);
1281 remove_device_file(wireless, ACER_CAP_WIRELESS);
1282 remove_device_file(bluetooth, ACER_CAP_BLUETOOTH);
1283 remove_device_file(threeg, ACER_CAP_THREEG);
1284 remove_device_file(interface, ACER_CAP_ANY);
1285 remove_device_file(fan_temperature_override, ACER_CAP_TEMPERATURE_OVERRIDE);
1286 remove_device_file(touchpad, ACER_CAP_TOUCHPAD_READ);
1287 return 0;
1290 static int create_sysfs(void)
1292 int retval = -ENOMEM;
1294 #define add_device_file(value, cap) \
1295 if (has_cap(cap)) {\
1296 retval = device_create_file(&acer_platform_device->dev, &dev_attr_##value);\
1297 if (retval)\
1298 goto error;\
1301 if (wmi_acer_has_guid(WMID_GUID2)) {
1302 retval = device_create_file(&acer_platform_device->dev, &dev_attr_devices);
1303 if (retval)
1304 goto error;
1307 add_device_file(wireless, ACER_CAP_WIRELESS);
1308 add_device_file(bluetooth, ACER_CAP_BLUETOOTH);
1309 add_device_file(threeg, ACER_CAP_THREEG);
1310 add_device_file(interface, ACER_CAP_ANY);
1311 add_device_file(fan_temperature_override, ACER_CAP_TEMPERATURE_OVERRIDE);
1312 add_device_file(touchpad, ACER_CAP_TOUCHPAD_READ);
1314 return 0;
1316 error:
1317 remove_sysfs(acer_platform_device);
1318 return retval;
1321 static int __init acer_acpi_init(void)
1323 printk(ACER_INFO "Acer Laptop ACPI Extras version %s\n",
1324 ACER_ACPI_VERSION);
1326 /* Find if this laptop requires any quirks */
1327 DEBUG(1, "Finding quirks\n");
1328 find_quirks();
1331 * Detect which WMI interface we're using.
1333 if (wmi_acer_has_guid(AMW0_GUID1)) {
1334 interface = &AMW0_interface;
1336 if (wmi_acer_has_guid(WMID_GUID1)) {
1337 DEBUG(0, "Detected Acer AMW0 version 2 interface\n");
1338 interface = &AMW0_V2_interface;
1339 } else {
1340 DEBUG(0, "Detected Acer AMW0 version 1 interface\n");
1341 if (!quirks->brightness) {
1342 quirks->brightness = 1;
1343 interface->capability |= ACER_CAP_BRIGHTNESS;
1346 } else if (wmi_acer_has_guid(WMID_GUID1)) {
1347 DEBUG(0, "Detected Acer WMID interface\n");
1348 interface = &WMID_interface;
1349 } else {
1350 printk(ACER_ERR "No or unsupported WMI interface, unable to load.\n");
1351 return -ENODEV;
1354 if (wmi_acer_has_guid(WMID_GUID2)) {
1355 if (ACPI_FAILURE(WMID_set_capabilities())) {
1356 printk(ACER_ERR "Unable to detect available devices\n");
1357 return -ENODEV;
1361 #ifdef CONFIG_PROC
1362 /* Create the proc entries */
1363 acer_proc_dir = proc_mkdir(PROC_ACER, acpi_root_dir);
1364 if (!acer_proc_dir) {
1365 printk(ACER_ERR "Unable to create /proc entries, aborting.\n");
1366 goto error_proc_mkdir;
1369 acer_proc_dir->owner = THIS_MODULE;
1370 if (add_proc_entries()) {
1371 printk(ACER_ERR "Unable to create /proc entries, aborting.\n");
1372 goto error_proc_add;
1374 #endif
1377 * Register the driver
1379 if (platform_driver_register(&acer_platform_driver)) {
1380 printk(ACER_ERR "Unable to register platform driver, aborting.\n");
1381 goto error_platform_register;
1383 acer_platform_device = platform_device_alloc("acer_acpi", -1);
1384 platform_device_add(acer_platform_device);
1386 create_sysfs();
1388 DEBUG(1, "Driver registered\n");
1390 /* Override any initial settings with values from the commandline */
1391 acer_commandline_init();
1393 return 0;
1395 error_platform_register:
1396 #ifdef CONFIG_PROC
1397 remove_proc_entries();
1398 error_proc_add:
1399 if (acer_proc_dir)
1400 remove_proc_entry(PROC_ACER, acpi_root_dir);
1401 error_proc_mkdir:
1402 #endif
1403 return -ENODEV;
1406 static void __exit acer_acpi_exit(void)
1408 remove_sysfs(acer_platform_device);
1409 platform_device_del(acer_platform_device);
1410 platform_driver_unregister(&acer_platform_driver);
1412 #ifdef CONFIG_PROC
1413 remove_proc_entries();
1415 if (acer_proc_dir)
1416 remove_proc_entry(PROC_ACER, acpi_root_dir);
1417 #endif
1418 printk(ACER_INFO "Acer Laptop ACPI Extras unloaded\n");
1419 return;
1422 module_init(acer_acpi_init);
1423 module_exit(acer_acpi_exit);