Merge rewrite branch, prepare for 0.9 release
[acer_acpi.git] / acer_acpi.c
blobd9ccd7c24d8de2259d0e80ab1c7d7f2956ea69e4
1 /*
2 * acer_acpi.c - Acer Laptop ACPI Extras
5 * Copyright (C) 2005-2007 E.M. Smith
6 * Copyright (C) 2007 Carlos Corbacho <cathectic@gmail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * The devolpment page for this driver is located at
24 * http://code.google.com/p/aceracpi
26 * Credits:
28 * John Belmonte - the Toshiba ACPI driver I've adapted for this module.
29 * Julien Lerouge & Karol Kozimor - ASUS Acpi driver authors.
30 * Olaf Tauber - developer of acerhk, the inspiration to solve the 64-bit
31 * driver problem for my Aspire 5024.
32 * Mathieu Segaud - solved the ACPI problem that needed a double-modprobe
33 * in version 0.2 and below.
34 * Carlos Corbacho - added initial status support for wireless/ mail/
35 * bluetooth, added module parameter support to turn
36 * hardware/ LEDs on and off at module loading (thanks
37 * again to acerhk for the inspiration)
38 * Jim Ramsay - Figured out and added support for WMID interface
42 #define ACER_ACPI_VERSION "0.9.0"
45 * Comment the following line out to remove /proc support
47 #define CONFIG_PROC
49 #ifdef CONFIG_PROC
50 #define PROC_ACER "acer"
51 #endif
53 #include <linux/kernel.h>
54 #include <linux/module.h>
55 #include <linux/init.h>
56 #include <linux/types.h>
57 #include <linux/proc_fs.h>
58 #include <linux/delay.h>
59 #include <linux/version.h>
61 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17)
62 #include <asm/uaccess.h>
63 #else
64 #include <linux/uaccess.h>
65 #endif
67 #include <linux/preempt.h>
68 #include <linux/io.h>
69 #include <linux/dmi.h>
70 #include <linux/backlight.h>
71 #include <linux/leds.h>
72 #include <linux/platform_device.h>
74 #include <acpi/acpi_drivers.h>
76 /* Workaround needed for older kernels */
77 #ifndef bool
78 #define bool int
79 #endif
81 MODULE_AUTHOR("Mark Smith, Carlos Corbacho");
82 MODULE_DESCRIPTION("Acer Laptop ACPI Extras Driver");
83 MODULE_LICENSE("GPL");
85 #define MY_LOGPREFIX "acer_acpi: "
86 #define MY_ERR KERN_ERR MY_LOGPREFIX
87 #define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
88 #define MY_INFO KERN_INFO MY_LOGPREFIX
90 #define DEBUG(level, message...) { \
91 if (debug >= level) \
92 printk(KERN_DEBUG MY_LOGPREFIX message);\
96 * The maximum temperature one can set for fan control override.
97 * Doesn't propably make much sense if over 80 degrees celsius though...
99 #define ACER_MAX_TEMPERATURE_OVERRIDE 150
102 * The following defines quirks to get some specific functions to work
103 * which are known to not be supported over ACPI (such as the mail LED
104 * on WMID based Acer's)
106 struct acer_quirks {
107 const char *vendor;
108 const char *model;
109 u16 quirks;
113 * Keyboard controller ports
115 #define ACER_KBD_STATUS_REG 0x64 /* Status register (R) */
116 #define ACER_KBD_CNTL_REG 0x64 /* Controller command register (W) */
117 #define ACER_KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */
120 * Magic Number
121 * Meaning is unknown - this number is required for writing to ACPI for AMW0
122 * (it's also used in acerhk when directly accessing the EC)
124 #define ACER_AMW0_WRITE 0x9610
127 * Bit masks for the old AMW0 interface
128 * These could vary between the particular interface
130 #define ACER_AMW0_WIRELESS_MASK 0x35
131 #define ACER_AMW0_BLUETOOTH_MASK 0x34
132 #define ACER_AMW0_MAILLED_MASK 0x31
135 * Method IDs for new WMID interface
136 * These could be different for other untested machines
138 #define ACER_WMID_GET_WIRELESS_METHODID 1
139 #define ACER_WMID_GET_BLUETOOTH_METHODID 2
140 #define ACER_WMID_GET_BRIGHTNESS_METHODID 3
141 #define ACER_WMID_SET_WIRELESS_METHODID 4
142 #define ACER_WMID_SET_BLUETOOTH_METHODID 5
143 #define ACER_WMID_SET_BRIGHTNESS_METHODID 6
144 #define ACER_WMID_GET_THREEG_METHODID 10
145 #define ACER_WMID_SET_THREEG_METHODID 11
148 * Acer ACPI method paths
150 * TODO: It may be possbile to autodetect these, since these are all at HID PNP0C14
152 #define AMW0_METHOD "\\_SB_.AMW0.WMAB"
153 #define AMW0_GETDATA "\\_SB_.AMW0._WED"
155 #define WMID_METHOD "\\_SB.WMID.WMBA"
156 #define WMID_GETDATA "\\_SB.WMID._WED"
159 * Interface capability flags
161 #define ACER_CAP_MAILLED (1<<0)
162 #define ACER_CAP_WIRELESS (1<<1)
163 #define ACER_CAP_BLUETOOTH (1<<2)
164 #define ACER_CAP_BRIGHTNESS (1<<3)
165 #define ACER_CAP_THREEG (1<<4)
166 #define ACER_CAP_TOUCHPAD_READ (1<<5)
167 #define ACER_CAP_TEMPERATURE_OVERRIDE (1<<6)
168 #define ACER_CAP_ANY (0xffffffff)
171 * Interface type flags
173 #define ACER_AMW0 (1<<0)
174 #define ACER_WMID (1<<1)
177 * Presumed start states -
178 * On some AMW0 laptops, we do not yet know how to get the device status from
179 * the EC, so we must store this ourselves.
181 * Plus, we can't tell which features are enabled or disabled on a specific
182 * model - e.g. The 5020 series can _support_ bluetooth; but the 5021 has no
183 * bluetooth, whilst the 5024 does. However, the BIOS identifies both laptops
184 * as 5020, and you can add bluetooth later.
186 * Basically the code works like this:
187 * - On init, any values specified on the commandline are set.
188 * - For interfaces where the current values cannot be detected and which
189 * have not been set on the commandline, we set them to some sane default
190 * (disabled)
192 * See AMW0_init and acer_commandline_init
195 #define ACER_DEFAULT_WIRELESS 0
196 #define ACER_DEFAULT_BLUETOOTH 0
197 #define ACER_DEFAULT_MAILLED 0
198 #define ACER_DEFAULT_THREEG 0
200 static int max_brightness = 0xF;
202 static int wireless = -1;
203 static int bluetooth = -1;
204 static int mailled = -1;
205 static int brightness = -1;
206 static int threeg = -1;
207 static int fan_temperature_override = -1;
208 static int debug = 0;
209 static int force_series;
211 module_param(mailled, int, 0444);
212 module_param(wireless, int, 0444);
213 module_param(bluetooth, int, 0444);
214 module_param(brightness, int, 0444);
215 module_param(threeg, int, 0444);
216 module_param(force_series, int, 0444);
217 module_param(fan_temperature_override, int, 0444);
218 module_param(debug, int, 0664);
219 MODULE_PARM_DESC(wireless, "Set initial state of Wireless hardware");
220 MODULE_PARM_DESC(bluetooth, "Set initial state of Bluetooth hardware");
221 MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
222 MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
223 MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
224 MODULE_PARM_DESC(fan_temperature_override, "Set initial state of the 'FAN temperature-override'");
225 MODULE_PARM_DESC(debug, "Debugging verbosity level (0=least 2=most)");
226 MODULE_PARM_DESC(force_series, "Force a different laptop series for extra features (5020 or 2490)");
228 #ifdef CONFIG_PROC
229 struct ProcItem {
230 const char *name;
231 char *(*read_func) (char *, u32);
232 unsigned long (*write_func) (const char *, unsigned long, u32);
233 unsigned int capability;
236 static struct proc_dir_entry *acer_proc_dir;
237 #endif
239 static int is_valid_acpi_path(const char *methodName)
241 acpi_handle handle;
242 acpi_status status;
244 status = acpi_get_handle(NULL, (char *)methodName, &handle);
245 return ACPI_SUCCESS(status);
249 * Wait for the keyboard controller to become ready
251 static int wait_kbd_write(void)
253 int i = 0;
254 while ((inb(ACER_KBD_STATUS_REG) & 0x02) && (i < 10000)) {
255 udelay(50);
256 i++;
258 return -(i == 10000);
261 static void send_kbd_cmd(u8 cmd, u8 val)
263 preempt_disable();
264 if (!wait_kbd_write())
265 outb(cmd, ACER_KBD_CNTL_REG);
266 if (!wait_kbd_write())
267 outb(val, ACER_KBD_DATA_REG);
268 preempt_enable_no_resched();
271 static void set_keyboard_quirk(void)
273 send_kbd_cmd(0x59, 0x90);
276 /* Each low-level interface must define at least some of the following */
277 struct Interface {
279 * The ACPI device type
281 u32 type;
284 * The capabilities this interface provides
285 * In the future, these can be removed/added at runtime when we have a
286 * way of detecting what capabilities are /actually/ present on an
287 * interface
289 u32 capability;
292 * Initializes an interface, should allocate the interface-specific
293 * data
295 void (*init) (struct Interface*);
298 * Frees an interface, should free the interface-specific data
300 void (*free) (struct Interface*);
303 * Interface-specific private data member. Must *not* be touched by
304 * anyone outside of this struct
306 void *data;
309 /* The static interface pointer, points to the currently detected interface */
310 static struct Interface *interface;
313 * Embedded Controller quirks
314 * Some laptops require us to directly access the EC to either enable or query
315 * features that are not available through ACPI.
318 struct quirk_entry {
319 int wireless;
320 int mailled;
321 int brightness;
322 int touchpad;
323 int temperature_override;
324 int mmkeys;
325 int bluetooth;
326 int max_brightness;
329 static struct quirk_entry *quirks;
331 static void set_quirks(void)
333 if (quirks->mailled) {
334 interface->capability |= ACER_CAP_MAILLED;
335 DEBUG(1, "Using EC direct-access quirk for mail LED\n");
338 if (quirks->touchpad) {
339 interface->capability |= ACER_CAP_TOUCHPAD_READ;
340 DEBUG(1, "Using EC direct-access quirk for reading touchpad status\n");
343 if (quirks->temperature_override) {
344 interface->capability |= ACER_CAP_TEMPERATURE_OVERRIDE;
345 DEBUG(1, "Using EC direct-access quirk for temperature override setting (fan)\n");
348 if (quirks->brightness) {
349 interface->capability |= ACER_CAP_BRIGHTNESS;
350 DEBUG(1, "Using EC direct-access quirk for backlight brightness\n");
353 if (quirks->mmkeys) {
354 set_keyboard_quirk();
355 printk(MY_INFO "Setting keyboard quirk to enable multimedia keys\n");
358 if (quirks->bluetooth) {
359 interface->capability |= ACER_CAP_BLUETOOTH;
360 DEBUG(1, "Using EC direct-access quirk for bluetooth\n");
363 if (quirks->wireless) {
364 interface->capability |= ACER_CAP_WIRELESS;
365 DEBUG(1, "Using EC direct-access quirk for wireless\n");
368 if (quirks->max_brightness) {
369 max_brightness = quirks->max_brightness;
370 DEBUG(1, "Changing maximum brightness level\n");
374 static int dmi_matched(struct dmi_system_id *dmi)
376 quirks = dmi->driver_data;
377 return 0;
380 static struct quirk_entry quirk_unknown = {
383 static struct quirk_entry quirk_acer_aspire_5020 = {
384 .wireless = 1,
385 .mailled = 2,
386 .brightness = 1,
387 .bluetooth = 1,
390 static struct quirk_entry quirk_acer_aspire_5680 = {
391 .mmkeys = 1,
394 static struct quirk_entry quirk_acer_aspire_9300 = {
395 .brightness = 2,
398 static struct quirk_entry quirk_acer_travelmate_2490 = {
399 .mmkeys = 1,
400 .mailled = 1,
401 .temperature_override = 1,
402 .touchpad = 1,
405 static struct quirk_entry quirk_acer_travelmate_5720 = {
406 .max_brightness = 0x9,
407 .touchpad = 2,
408 .wireless = 2,
409 .bluetooth = 2,
410 .brightness = 1,
413 static struct dmi_system_id acer_quirks[] = {
415 .callback = dmi_matched,
416 .ident = "Acer Aspire 3020",
417 .matches = {
418 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
419 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"),
421 .driver_data = &quirk_acer_aspire_5020,
424 .callback = dmi_matched,
425 .ident = "Acer Aspire 3040",
426 .matches = {
427 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
428 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3040"),
430 .driver_data = &quirk_acer_aspire_5020,
433 .callback = dmi_matched,
434 .ident = "Acer Aspire 5020",
435 .matches = {
436 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
437 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"),
439 .driver_data = &quirk_acer_aspire_5020,
442 .callback = dmi_matched,
443 .ident = "Acer Aspire 5040",
444 .matches = {
445 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
446 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5040"),
448 .driver_data = &quirk_acer_aspire_5020,
451 .callback = dmi_matched,
452 .ident = "Acer Aspire 5560",
453 .matches = {
454 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
455 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5560"),
457 .driver_data = &quirk_acer_aspire_5020,
460 .callback = dmi_matched,
461 .ident = "Acer Aspire 5650",
462 .matches = {
463 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
464 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
466 .driver_data = &quirk_acer_travelmate_2490,
469 .callback = dmi_matched,
470 .ident = "Acer Aspire 5680",
471 .matches = {
472 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
473 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
475 .driver_data = &quirk_acer_aspire_5680,
478 .callback = dmi_matched,
479 .ident = "Acer Aspire 9300",
480 .matches = {
481 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
482 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9300"),
484 .driver_data = &quirk_acer_aspire_9300,
487 .callback = dmi_matched,
488 .ident = "Acer TravelMate 2420",
489 .matches = {
490 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
491 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"),
493 .driver_data = &quirk_acer_aspire_5020,
496 .callback = dmi_matched,
497 .ident = "Acer TravelMate 2490",
498 .matches = {
499 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
500 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
502 .driver_data = &quirk_acer_travelmate_2490,
505 .callback = dmi_matched,
506 .ident = "Acer TravelMate 5720",
507 .matches = {
508 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
509 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5720"),
511 .driver_data = &quirk_acer_travelmate_5720,
516 /* Find which quirks are needed for a particular vendor/ model pair */
517 static void find_quirks(void)
519 DEBUG (1, "Looking for quirks\n");
520 if (!force_series) {
521 dmi_check_system(acer_quirks);
522 } else if (force_series == 5020) {
523 DEBUG(0, "Forcing Acer Aspire 5020\n");
524 quirks = &quirk_acer_aspire_5020;
525 } else if (force_series == 2490) {
526 DEBUG(0, "Forcing Acer TravelMate 2490\n");
527 quirks = &quirk_acer_travelmate_2490;
528 } else if (force_series == 5720) {
529 DEBUG(0, "Forcing Acer TravelMate 5720\n");
530 quirks = &quirk_acer_travelmate_5720;
533 if (quirks == NULL) {
534 DEBUG(1, "No quirks known for this laptop\n");
535 quirks = &quirk_unknown;
537 set_quirks();
541 * General interface convenience methods
544 static bool has_cap(u32 cap)
546 if ((interface->capability & cap) != 0) {
547 return 1;
549 return 0;
552 static void interface_free(struct Interface *iface)
554 /* Free our private data structure */
555 kfree(iface->data);
558 /* General wrapper around the ACPI call */
559 static acpi_status
560 WMI_execute(char *methodPath, u32 methodId, const struct acpi_buffer *in, struct acpi_buffer *out) {
561 struct acpi_object_list input;
562 union acpi_object params[3];
563 acpi_status status = AE_OK;
565 /* WMI calling convention:
566 * methodPath( instance, methodId, input_buffer )
567 * - instance is always 1, since there's only this module
568 * - methodId is the method number within the current method group.
569 * - Input buffer is ignored for read-only commands
570 * - May return a buffer of results (optional)
572 input.count = 3;
573 input.pointer = params;
574 params[0].type = ACPI_TYPE_INTEGER;
575 params[0].integer.value = 0x01;
576 params[1].type = ACPI_TYPE_INTEGER;
577 params[1].integer.value = methodId;
578 params[2].type = ACPI_TYPE_BUFFER;
579 params[2].buffer.length = in->length;
580 params[2].buffer.pointer = in->pointer;
582 DEBUG(2, "Doing %s( 1, %u, [%llu-byte buffer] )\n", methodPath, methodId, (u64)in->length);
584 status = acpi_evaluate_object(NULL, methodPath, &input, out);
586 DEBUG(2, " Execution status: %d\n", status);
587 DEBUG(2, " Result: %llu bytes\n", (u64)(out ? out->length : 0) );
589 return status;
593 * Old interface (now known as the AMW0 interface)
595 struct WMAB_args {
596 u32 eax;
597 u32 ebx;
598 u32 ecx;
599 u32 edx;
602 struct AMW0_Data {
603 int mailled;
604 int wireless;
605 int bluetooth;
608 static acpi_status WMAB_execute(struct WMAB_args * regbuf, struct acpi_buffer *result)
610 struct acpi_buffer input;
611 acpi_status status;
612 input.length = sizeof(struct WMAB_args);
613 input.pointer = (u8*)regbuf;
615 status = WMI_execute( AMW0_METHOD, 1, &input, result);
616 DEBUG(2, " Args: 0x%08x 0x%08x 0x%08x 0x%08x\n", regbuf->eax, regbuf->ebx, regbuf->ecx, regbuf->edx );
618 return status;
621 static void AMW0_init(struct Interface *iface) {
622 bool help = 0;
623 struct AMW0_Data *data;
625 /* Allocate our private data structure */
626 iface->data = kmalloc(sizeof(struct AMW0_Data), GFP_KERNEL);
627 data = (struct AMW0_Data*)iface->data;
630 * If the commandline doesn't specify these, we need to force them to
631 * the default values
633 if (mailled == -1 && !quirks->mailled)
634 mailled = ACER_DEFAULT_MAILLED;
635 if (wireless == -1 && !quirks->wireless)
636 wireless = ACER_DEFAULT_WIRELESS;
637 if (bluetooth == -1 && !quirks->bluetooth)
638 bluetooth = ACER_DEFAULT_BLUETOOTH;
641 * Set the cached "current" values to impossible ones so that
642 * acer_commandline_init will definitely set them.
644 if (!quirks->bluetooth) {
645 help = 1;
646 data->bluetooth = -1;
647 printk(MY_INFO "No EC data for reading bluetooth - bluetooth value when read will be a 'best guess'\n");
650 if (!quirks->wireless) {
651 help = 1;
652 printk(MY_INFO "No EC data for reading wireless - wireless value when read will be a 'best guess'\n");
653 data->wireless = -1;
655 if (!quirks->mailled) {
656 help = 1;
657 printk(MY_INFO "No EC data for reading mail LED - mail LED value when read will be a 'best guess'\n");
658 data->mailled = -1;
661 if (help) {
662 printk(MY_INFO "We need more data from your laptop's Embedded Controller (EC) to better support it\n");
663 printk(MY_INFO "Please see http://code.google.com/p/aceracpi/wiki/EmbeddedController on how to help\n");
667 static acpi_status AMW0_get_bool(bool *value, u32 cap, struct Interface *iface)
669 struct AMW0_Data *data = iface->data;
670 u8 result;
673 * On some models, we can read these values from the EC. On others,
674 * we use a stored value
676 switch (cap) {
677 case ACER_CAP_MAILLED:
678 if (quirks->mailled == 2) {
679 ec_read(0x0A, &result);
680 *value = (result >> 7) & 0x01;
681 return 0;
683 else
684 *value = data->mailled;
685 break;
686 case ACER_CAP_WIRELESS:
687 switch (quirks->wireless) {
688 case 1:
689 ec_read(0x0A, &result);
690 *value = (result >> 2) & 0x01;
691 return 0;
692 case 2:
693 ec_read(0x71, &result);
694 *value = result & 0x01;
695 return 0;
696 default:
697 *value = data->wireless;
699 break;
700 case ACER_CAP_BLUETOOTH:
701 switch (quirks->bluetooth) {
702 case 1:
703 ec_read(0x0A, &result);
704 *value = (result >> 4) & 0x01;
705 return 0;
706 case 2:
707 ec_read(0x71, &result);
708 *value = (result >> 1) & 0x01;
709 return 0;
710 default:
711 *value = data->bluetooth;
713 break;
714 case ACER_CAP_TOUCHPAD_READ:
715 switch (quirks->touchpad) {
716 case 2:
717 ec_read(0x74, &result);
718 *value = (result >> 3) & 0x01;
719 return 0;
720 default:
721 break;
723 default:
724 return AE_BAD_ADDRESS;
726 return AE_OK;
729 static acpi_status AMW0_set_bool(bool value, u32 cap, struct Interface *iface)
731 struct WMAB_args args;
732 acpi_status status;
734 args.eax = ACER_AMW0_WRITE;
735 args.ebx = value ? (1<<8) : 0;
737 switch (cap) {
738 case ACER_CAP_MAILLED:
739 args.ebx |= ACER_AMW0_MAILLED_MASK;
740 break;
741 case ACER_CAP_WIRELESS:
742 args.ebx |= ACER_AMW0_WIRELESS_MASK;
743 break;
744 case ACER_CAP_BLUETOOTH:
745 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
746 break;
747 default:
748 return AE_BAD_ADDRESS;
751 /* Actually do the set */
752 status = WMAB_execute(&args, NULL);
755 * Currently no way to query the state, so cache the new value on
756 * success
758 if (ACPI_SUCCESS(status)) {
759 struct AMW0_Data *data = iface->data;
760 switch (cap) {
761 case ACER_CAP_MAILLED:
762 data->mailled = value;
763 break;
764 case ACER_CAP_WIRELESS:
765 data->wireless = value;
766 break;
767 case ACER_CAP_BLUETOOTH:
768 data->bluetooth = value;
769 break;
773 return status;
776 static acpi_status AMW0_get_u8(u8 *value, u32 cap, struct Interface *iface) {
777 switch (cap) {
778 case ACER_CAP_BRIGHTNESS:
779 switch (quirks->brightness) {
780 case 1:
781 ec_read(0x83, value);
782 break;
783 case 2:
784 ec_read(0x85, value);
785 break;
786 default:
787 return AE_BAD_ADDRESS;
789 break;
790 default:
791 return AE_BAD_ADDRESS;
793 return AE_OK;
796 static acpi_status AMW0_set_u8(u8 value, u32 cap, struct Interface *iface) {
797 switch (cap) {
798 case ACER_CAP_BRIGHTNESS:
799 switch (quirks->brightness) {
800 case 1:
801 ec_write(0x83, value);
802 break;
803 case 2:
804 ec_write(0x85, value);
805 break;
806 default:
807 return AE_BAD_ADDRESS;
808 break;
810 default:
811 return AE_BAD_ADDRESS;
813 return AE_OK;
816 static struct Interface AMW0_interface = {
817 .type = ACER_AMW0,
818 .capability = (
819 ACER_CAP_MAILLED |
820 ACER_CAP_WIRELESS |
821 ACER_CAP_BLUETOOTH
823 .init = AMW0_init,
824 .free = interface_free,
828 * New interface (The WMID interface)
830 struct WMID_Data {
831 int mailled;
832 int wireless;
833 int bluetooth;
834 int threeg;
835 int brightness;
838 static void WMID_init(struct Interface *iface)
840 struct WMID_Data *data;
842 /* Allocate our private data structure */
843 iface->data = kmalloc(sizeof(struct WMID_Data), GFP_KERNEL);
844 data = (struct WMID_Data*)iface->data;
847 static acpi_status
848 WMI_execute_u32(u32 methodId, u32 in, u32 *out)
850 struct acpi_buffer input = { (acpi_size)sizeof(u32), (void*)(&in) };
851 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
852 union acpi_object *obj;
853 u32 tmp;
854 acpi_status status;
856 status = WMI_execute(WMID_METHOD, methodId, &input, &result);
857 DEBUG(2, " In: 0x%08x\n", in);
859 if (ACPI_FAILURE(status))
860 return status;
862 obj = (union acpi_object *)result.pointer;
863 if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == sizeof(u32)) {
864 tmp = *((u32*)obj->buffer.pointer);
865 DEBUG(2, " Out: 0x%08x\n", tmp);
866 } else {
867 tmp = 0;
868 if (obj) {
869 DEBUG(2, " Got unexpected result of type %d\n", obj->type);
870 } else {
871 DEBUG(2, " Got unexpected null result\n");
875 if (out)
876 *out = tmp;
878 if (result.length > 0 && result.pointer)
879 kfree(result.pointer);
881 return status;
884 static acpi_status WMID_get_u8(u8 *value, u32 cap, struct Interface *iface) {
885 acpi_status status;
886 u32 result;
887 u32 methodId = 0;
889 switch (cap) {
890 case ACER_CAP_WIRELESS:
891 methodId = ACER_WMID_GET_WIRELESS_METHODID;
892 break;
893 case ACER_CAP_BLUETOOTH:
894 methodId = ACER_WMID_GET_BLUETOOTH_METHODID;
895 break;
896 case ACER_CAP_BRIGHTNESS:
897 methodId = ACER_WMID_GET_BRIGHTNESS_METHODID;
898 break;
899 case ACER_CAP_THREEG:
900 methodId = ACER_WMID_GET_THREEG_METHODID;
901 break;
902 case ACER_CAP_MAILLED:
903 if (quirks->mailled == 1) {
904 ec_read(0x9f, value);
905 *value &= 0x01;
906 return 0;
908 case ACER_CAP_TOUCHPAD_READ:
909 switch (quirks->touchpad) {
910 case 1:
911 ec_read(0x9e, value);
912 *value = 1 - ((*value >> 3) & 0x01);
913 return 0;
914 default:
915 break;
917 case ACER_CAP_TEMPERATURE_OVERRIDE:
918 if (quirks->temperature_override == 1) {
919 ec_read(0xa9, value);
920 return 0;
922 default:
923 return AE_BAD_ADDRESS;
925 status = WMI_execute_u32(methodId, 0, &result);
927 if (ACPI_SUCCESS(status))
928 *value = (u8)result;
930 return status;
933 static acpi_status WMID_set_u8(u8 value, u32 cap, struct Interface *iface) {
934 u32 methodId = 0;
936 switch (cap) {
937 case ACER_CAP_BRIGHTNESS:
938 methodId = ACER_WMID_SET_BRIGHTNESS_METHODID;
939 break;
940 case ACER_CAP_WIRELESS:
941 methodId = ACER_WMID_SET_WIRELESS_METHODID;
942 break;
943 case ACER_CAP_BLUETOOTH:
944 methodId = ACER_WMID_SET_BLUETOOTH_METHODID;
945 break;
946 case ACER_CAP_THREEG:
947 methodId = ACER_WMID_SET_THREEG_METHODID;
948 break;
949 case ACER_CAP_MAILLED:
950 if (quirks->mailled == 1) {
951 send_kbd_cmd(0x59, value ? 0x92 : 0x93);
952 return 0;
954 case ACER_CAP_TEMPERATURE_OVERRIDE:
955 if (quirks->temperature_override == 1) {
956 ec_write(0xa9, value);
957 return 0;
959 default:
960 return AE_BAD_ADDRESS;
962 return WMI_execute_u32(methodId, (u32)value, NULL);
966 static struct Interface WMID_interface = {
967 .capability = (
968 ACER_CAP_WIRELESS
969 | ACER_CAP_BRIGHTNESS
970 | ACER_CAP_BLUETOOTH
971 | ACER_CAP_THREEG
973 .init = WMID_init,
974 .free = interface_free,
975 .data = NULL,
978 #ifdef CONFIG_PROC
980 * High-level Procfs file handlers
983 static int
984 dispatch_read(char *page, char **start, off_t off, int count, int *eof,
985 struct ProcItem * item)
987 char *p = page;
988 int len;
990 if (off == 0)
991 p = item->read_func(p, item->capability);
992 len = (p - page);
993 if (len <= off + count)
994 *eof = 1;
995 *start = page + off;
996 len -= off;
997 if (len > count)
998 len = count;
999 if (len < 0)
1000 len = 0;
1001 return len;
1004 static int
1005 dispatch_write(struct file *file, const char __user * buffer,
1006 unsigned long count, struct ProcItem * item)
1008 int result;
1009 char *tmp_buffer;
1012 * Arg buffer points to userspace memory, which can't be accessed
1013 * directly. Since we're making a copy, zero-terminate the
1014 * destination so that sscanf can be used on it safely.
1016 tmp_buffer = kmalloc(count + 1, GFP_KERNEL);
1017 if (copy_from_user(tmp_buffer, buffer, count)) {
1018 result = -EFAULT;
1019 } else {
1020 tmp_buffer[count] = 0;
1021 result = item->write_func(tmp_buffer, count, item->capability);
1023 kfree(tmp_buffer);
1024 return result;
1026 #endif
1029 * Generic Device (interface-independent)
1032 static acpi_status get_bool(bool *value, u32 cap) {
1033 acpi_status status = AE_BAD_ADDRESS;
1034 u8 *tmp = 0;
1036 switch (interface->type) {
1037 case ACER_AMW0:
1038 status = AMW0_get_bool(value, cap, interface);
1039 break;
1040 case ACER_WMID:
1041 status = WMID_get_u8(tmp, cap, interface);
1042 *value = (*tmp == 1) ? 1 : 0;
1043 break;
1045 return status;
1048 static acpi_status set_bool(int value, u32 cap) {
1049 acpi_status status = AE_BAD_PARAMETER;
1051 if ((value == 0 || value == 1) && (interface->capability & cap)) {
1052 switch (interface->type) {
1053 case ACER_AMW0:
1054 status = AMW0_set_bool(value == 1, cap, interface);
1055 break;
1056 case ACER_WMID:
1057 status = AMW0_set_u8(value == 1, cap, interface);
1058 break;
1061 return status;
1065 static acpi_status get_u8(u8 *value, u32 cap) {
1066 switch (interface->type) {
1067 case ACER_AMW0:
1068 return AMW0_get_u8(value, cap, interface);
1069 break;
1070 case ACER_WMID:
1071 return WMID_get_u8(value, cap, interface);
1072 break;
1073 default:
1074 return AE_BAD_ADDRESS;
1078 static acpi_status set_u8(u8 value, u8 min, u8 max, u32 cap) {
1079 if ((value >= min && value <= max) && (interface->capability & cap) ) {
1080 switch (interface->type) {
1081 case ACER_AMW0:
1082 return AMW0_set_u8(value, cap, interface);
1083 case ACER_WMID:
1084 return WMID_set_u8(value, cap, interface);
1085 default:
1086 return AE_BAD_PARAMETER;
1089 return AE_BAD_PARAMETER;
1092 /* Each _u8 needs a small wrapper that sets the boundary values */
1093 static acpi_status set_brightness(u8 value)
1095 return set_u8(value, 0, max_brightness, ACER_CAP_BRIGHTNESS);
1098 static acpi_status set_temperature_override(u8 value)
1100 return set_u8(value, 0, ACER_MAX_TEMPERATURE_OVERRIDE, ACER_CAP_TEMPERATURE_OVERRIDE);
1103 static void __init acer_commandline_init(void)
1105 DEBUG(1, "Commandline args: mailled(%d) wireless(%d) bluetooth(%d) brightness(%d)\n",
1106 mailled, wireless, bluetooth, brightness);
1109 * These will all fail silently if the value given is invalid, or the
1110 * capability isn't available on the given interface
1112 set_bool(mailled, ACER_CAP_MAILLED);
1113 set_bool(wireless, ACER_CAP_WIRELESS);
1114 set_bool(bluetooth, ACER_CAP_BLUETOOTH);
1115 set_bool(threeg, ACER_CAP_THREEG);
1116 set_temperature_override(fan_temperature_override);
1117 set_brightness((u8)brightness);
1120 #ifdef CONFIG_PROC
1122 * Procfs interface (deprecated)
1124 static char *read_bool(char *p, u32 cap)
1126 bool result;
1127 acpi_status status = get_bool(&result, cap);
1128 if (ACPI_SUCCESS(status))
1129 p += sprintf(p, "%d\n", result);
1130 else
1131 p += sprintf(p, "Read error" );
1132 return p;
1135 static unsigned long write_bool(const char *buffer, unsigned long count, u32 cap)
1137 int value;
1139 if (sscanf(buffer, "%i", &value) == 1) {
1140 acpi_status status = set_bool(value, cap);
1141 if (ACPI_FAILURE(status))
1142 return -EINVAL;
1143 } else {
1144 return -EINVAL;
1146 return count;
1149 static char *read_u8(char *p, u32 cap)
1151 u8 result;
1152 acpi_status status = get_u8(&result, cap);
1153 if (ACPI_SUCCESS(status))
1154 p += sprintf(p, "%u\n", result);
1155 else
1156 p += sprintf(p, "Read error" );
1157 return p;
1160 static unsigned long write_u8(const char *buffer, unsigned long count, u32 cap)
1162 int value;
1163 acpi_status (*set_method)(u8);
1165 /* Choose the appropriate set_u8 wrapper here, based on the capability */
1166 switch (cap) {
1167 case ACER_CAP_BRIGHTNESS:
1168 set_method = set_brightness;
1169 break;
1170 case ACER_CAP_TEMPERATURE_OVERRIDE:
1171 set_method = set_temperature_override;
1172 break;
1173 default:
1174 return -EINVAL;
1177 if (sscanf(buffer, "%i", &value) == 1) {
1178 acpi_status status = (*set_method)(value);
1179 if (ACPI_FAILURE(status))
1180 return -EINVAL;
1181 } else {
1182 return -EINVAL;
1184 return count;
1187 static char *read_version(char *p, u32 cap)
1189 p += sprintf(p, "%s\n", ACER_ACPI_VERSION);
1190 return p;
1193 static char *read_interface(char *p, u32 cap)
1195 p += sprintf(p, "%s\n", (interface->type == ACER_AMW0 ) ? "AMW0": "WMID");
1196 return p;
1199 struct ProcItem proc_items[] = {
1200 {"mailled", read_bool, write_bool, ACER_CAP_MAILLED},
1201 {"bluetooth", read_bool, write_bool, ACER_CAP_BLUETOOTH},
1202 {"wireless", read_bool, write_bool, ACER_CAP_WIRELESS},
1203 {"brightness", read_u8, write_u8, ACER_CAP_BRIGHTNESS},
1204 {"threeg", read_bool, write_bool, ACER_CAP_THREEG},
1205 {"touchpad", read_bool, NULL, ACER_CAP_TOUCHPAD_READ},
1206 {"fan_temperature_override", read_u8, write_u8, ACER_CAP_TEMPERATURE_OVERRIDE},
1207 {"version", read_version, NULL, ACER_CAP_ANY},
1208 {"interface", read_interface, NULL, ACER_CAP_ANY},
1209 {NULL}
1212 static acpi_status __init add_proc_entries(void)
1214 struct proc_dir_entry *proc;
1215 struct ProcItem *item;
1217 for (item = proc_items; item->name; ++item) {
1219 * Only add the proc file if the current interface actually
1220 * supports it
1222 if (interface->capability & item->capability) {
1223 proc = create_proc_read_entry(item->name,
1224 S_IFREG | S_IRUGO | S_IWUSR,
1225 acer_proc_dir,
1226 (read_proc_t *) dispatch_read,
1227 item);
1228 if (proc)
1229 proc->owner = THIS_MODULE;
1230 if (proc && item->write_func)
1231 proc->write_proc = (write_proc_t *) dispatch_write;
1235 return AE_OK;
1238 static acpi_status __exit remove_proc_entries(void)
1240 struct ProcItem *item;
1242 for (item = proc_items; item->name; ++item)
1243 remove_proc_entry(item->name, acer_proc_dir);
1244 return AE_OK;
1246 #endif
1249 * LED device (Mail LED only, no other LEDs known yet)
1251 static void mail_led_set(struct led_classdev *led_cdev, enum led_brightness value)
1253 bool tmp = value;
1254 set_bool(tmp, ACER_CAP_MAILLED);
1257 static struct led_classdev mail_led = {
1258 .name = "acer_acpi:mail",
1259 .brightness_set = mail_led_set,
1262 static void acer_led_init(struct device *dev)
1264 led_classdev_register(dev, &mail_led);
1267 static void acer_led_exit(void)
1269 led_classdev_unregister(&mail_led);
1272 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
1274 * Backlight device
1276 static struct backlight_device *acer_backlight_device;
1278 static int read_brightness(struct backlight_device *bd)
1280 u8 value;
1281 get_u8(&value, ACER_CAP_BRIGHTNESS);
1282 return value;
1285 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,20)
1286 static int update_bl_status(struct backlight_device *bd)
1288 set_brightness(bd->props->brightness);
1289 return 0;
1292 static struct backlight_properties acer_backlight_properties = {
1293 .get_brightness = read_brightness,
1294 .update_status = update_bl_status,
1297 static int __init acer_backlight_init(struct device *dev)
1299 struct backlight_device *bd;
1301 DEBUG(1, "Loading backlight driver\n");
1302 bd = backlight_device_register("acer_acpi", dev, NULL, &acer_backlight_properties);
1303 if (IS_ERR(bd)) {
1304 printk(MY_ERR "Could not register Acer backlight device\n");
1305 acer_backlight_device = NULL;
1306 return PTR_ERR(bd);
1309 acer_backlight_device = bd;
1311 bd->props->max_brightness = max_brightness;
1312 return 0;
1314 #else
1315 static int update_bl_status(struct backlight_device *bd)
1317 set_brightness(bd->props.brightness);
1318 return 0;
1321 static struct backlight_ops acer_backlight_ops = {
1322 .get_brightness = read_brightness,
1323 .update_status = update_bl_status,
1326 static int __init acer_backlight_init(struct device *dev)
1328 struct backlight_device *bd;
1330 DEBUG(1, "Loading backlight driver\n");
1331 bd = backlight_device_register("acer_acpi", dev, NULL, &acer_backlight_ops);
1332 if (IS_ERR(bd)) {
1333 printk(MY_ERR "Could not register Acer backlight device\n");
1334 acer_backlight_device = NULL;
1335 return PTR_ERR(bd);
1338 acer_backlight_device = bd;
1340 bd->props.max_brightness = max_brightness;
1341 bd->props.brightness = read_brightness(NULL);
1342 backlight_update_status(bd);
1343 return 0;
1345 #endif
1347 static void __exit acer_backlight_exit(void)
1349 backlight_device_unregister(acer_backlight_device);
1351 #endif
1354 * Platform device
1358 * Read/ write bool sysfs macro
1360 #define show_set_bool(value, cap) \
1361 static ssize_t \
1362 show_bool_##value(struct device *dev, struct device_attribute *attr, \
1363 char *buf) \
1365 bool result; \
1366 acpi_status status = get_bool(&result, cap); \
1367 if (ACPI_SUCCESS(status)) \
1368 return sprintf(buf, "%d\n", result); \
1369 return sprintf(buf, "Read error" ); \
1372 static ssize_t \
1373 set_bool_##value(struct device *dev, struct device_attribute *attr, \
1374 const char *buf, size_t count) \
1376 bool tmp = simple_strtoul(buf, NULL, 10); \
1377 acpi_status status = set_bool(tmp, cap); \
1378 if (ACPI_FAILURE(status)) \
1379 return -EINVAL; \
1380 return count; \
1382 static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
1383 show_bool_##value, set_bool_##value);
1385 show_set_bool(wireless, ACER_CAP_WIRELESS);
1386 show_set_bool(bluetooth, ACER_CAP_BLUETOOTH);
1387 show_set_bool(threeg, ACER_CAP_THREEG);
1388 show_set_bool(fan_temperature_override, ACER_CAP_TEMPERATURE_OVERRIDE);
1391 * Read-only bool sysfs macro
1393 #define show_bool(value, cap) \
1394 static ssize_t \
1395 show_bool_##value(struct device *dev, struct device_attribute *attr, \
1396 char *buf) \
1398 bool result; \
1399 acpi_status status = get_bool(&result, cap); \
1400 if (ACPI_SUCCESS(status)) \
1401 return sprintf(buf, "%d\n", result); \
1402 return sprintf(buf, "Read error" ); \
1404 static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
1405 show_bool_##value, NULL);
1407 show_bool(touchpad, ACER_CAP_TOUCHPAD_READ);
1410 * Read interface sysfs macro
1412 static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
1413 char *buf)
1415 return sprintf(buf, "%s\n", (interface->type == ACER_AMW0 ) ? "AMW0": "WMID");
1418 static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR, show_interface, NULL);
1420 static struct platform_driver acer_platform_driver = {
1421 .driver = {
1422 .name = "acer_acpi",
1423 .owner = THIS_MODULE,
1427 static struct platform_device *acer_platform_device;
1429 static int remove_sysfs(struct platform_device *device)
1431 #define remove_device_file(value, cap) \
1432 if (has_cap(cap)) \
1433 device_remove_file(&device->dev, &dev_attr_##value);
1435 remove_device_file(wireless, ACER_CAP_WIRELESS);
1436 remove_device_file(bluetooth, ACER_CAP_BLUETOOTH);
1437 remove_device_file(threeg, ACER_CAP_THREEG);
1438 remove_device_file(interface, ACER_CAP_ANY);
1439 remove_device_file(fan_temperature_override, ACER_CAP_TEMPERATURE_OVERRIDE);
1440 remove_device_file(touchpad, ACER_CAP_TOUCHPAD_READ);
1441 return 0;
1444 static int acer_platform_add(void)
1446 int retval = -ENOMEM;
1447 platform_driver_register(&acer_platform_driver);
1449 acer_platform_device = platform_device_alloc("acer_acpi", -1);
1451 platform_device_add(acer_platform_device);
1453 #define add_device_file(value, cap) \
1454 if (has_cap(cap)) {\
1455 retval = device_create_file(&acer_platform_device->dev, &dev_attr_##value);\
1456 if (retval)\
1457 goto error;\
1460 add_device_file(wireless, ACER_CAP_WIRELESS);
1461 add_device_file(bluetooth, ACER_CAP_BLUETOOTH);
1462 add_device_file(threeg, ACER_CAP_THREEG);
1463 add_device_file(interface, ACER_CAP_ANY);
1464 add_device_file(fan_temperature_override, ACER_CAP_TEMPERATURE_OVERRIDE);
1465 add_device_file(touchpad, ACER_CAP_TOUCHPAD_READ);
1467 return 0;
1469 error:
1470 remove_sysfs(acer_platform_device);
1471 return retval;
1474 static void acer_platform_remove(void)
1476 remove_sysfs(acer_platform_device);
1477 platform_device_del(acer_platform_device);
1478 platform_driver_unregister(&acer_platform_driver);
1482 * ACPI driver
1484 static int acer_acpi_suspend(struct acpi_device *device, pm_message_t state)
1487 * WMID fix for suspend-to-disk - save all current states now so we can
1488 * restore them on resume
1490 bool value;
1491 u8 u8value;
1493 #define save_bool_device(device, cap) \
1494 if (has_cap(cap)) {\
1495 get_bool(&value, cap);\
1496 data->device = value;\
1499 #define save_u8_device(device, cap) \
1500 if (has_cap(cap)) {\
1501 get_u8(&u8value, cap);\
1502 data->device = u8value;\
1505 if (interface->type == ACER_WMID) {
1506 struct WMID_Data *data = interface->data;
1507 save_bool_device(wireless, ACER_CAP_WIRELESS);
1508 save_bool_device(bluetooth, ACER_CAP_BLUETOOTH);
1509 save_bool_device(threeg, ACER_CAP_THREEG);
1510 save_u8_device(brightness, ACER_CAP_BRIGHTNESS);
1513 return 0;
1516 static int acer_acpi_resume(struct acpi_device *device)
1518 #define restore_bool_device(device, cap) \
1519 if (has_cap(cap))\
1520 set_bool(data->device, cap);\
1523 * We must _always_ restore AMW0's values, otherwise the values
1524 * after suspend-to-disk are wrong
1526 if (interface->type == ACER_AMW0) {
1527 struct AMW0_Data *data = interface->data;
1529 restore_bool_device(wireless, ACER_CAP_WIRELESS);
1530 restore_bool_device(bluetooth, ACER_CAP_BLUETOOTH);
1531 restore_bool_device(mailled, ACER_CAP_MAILLED);
1533 else if (interface->type == ACER_WMID) {
1534 struct WMID_Data *data = interface->data;
1536 if (has_cap(ACER_CAP_BRIGHTNESS))
1537 set_brightness((u8)data->brightness);
1538 restore_bool_device(threeg, ACER_CAP_THREEG);
1539 restore_bool_device(wireless, ACER_CAP_WIRELESS);
1540 restore_bool_device(bluetooth, ACER_CAP_BLUETOOTH);
1543 /* Check if this laptop requires the keyboard quirk */
1544 if (quirks->mmkeys) {
1545 set_keyboard_quirk();
1546 printk(MY_INFO "Setting keyboard quirk to enable multimedia keys\n");
1549 return 0;
1552 static int acer_acpi_add(struct acpi_device *device)
1554 struct device *dev = acpi_get_physical_device(device->handle);
1555 if (has_cap(ACER_CAP_MAILLED))
1556 acer_led_init(dev);
1557 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
1558 if (has_cap(ACER_CAP_BRIGHTNESS))
1559 acer_backlight_init(dev);
1560 #endif
1562 acer_platform_add();
1563 return 0;
1566 static int acer_acpi_remove(struct acpi_device *device, int type)
1568 if (has_cap(ACER_CAP_MAILLED))
1569 acer_led_exit();
1570 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
1571 if (has_cap(ACER_CAP_BRIGHTNESS))
1572 acer_backlight_exit();
1573 #endif
1575 acer_platform_remove();
1576 return 0;
1579 static struct acpi_driver acer = {
1580 .name = "acer_acpi",
1581 .class = "acer",
1582 .ids = "PNP0C14",
1583 .ops = {
1584 .add = acer_acpi_add,
1585 .remove = acer_acpi_remove,
1586 .suspend = acer_acpi_suspend,
1587 .resume = acer_acpi_resume,
1591 static int __init acer_acpi_init(void)
1593 acpi_status status = AE_OK;
1595 printk(MY_INFO "Acer Laptop ACPI Extras version %s\n",
1596 ACER_ACPI_VERSION);
1597 if (acpi_disabled) {
1598 printk(MY_ERR "ACPI Disabled, unable to load.\n");
1599 return -ENODEV;
1603 * Detect which WMI interface we're using.
1605 * TODO: This could be more dynamic, and perhaps done in part by the
1606 * acpi_bus driver?
1608 if (is_valid_acpi_path(AMW0_METHOD)) {
1609 DEBUG(0, "Detected Acer AMW0 interface\n");
1610 /* .ids is case sensitive - and AMW0 uses a strange mixed case */
1611 acer.ids = "pnp0c14";
1612 interface = &AMW0_interface;
1613 } else if (is_valid_acpi_path(WMID_METHOD)) {
1614 DEBUG(0, "Detected Acer WMID interface\n");
1615 interface = &WMID_interface;
1616 } else {
1617 printk(MY_ERR "No or unsupported WMI interface, unable to load.\n");
1618 goto error_no_interface;
1621 /* Find if this laptop requires any quirks */
1622 DEBUG(1, "Finding quirks\n");
1623 find_quirks();
1625 /* Now that we have a known interface, initialize it */
1626 DEBUG(1, "Initialising interface\n");
1627 if (interface->init)
1628 interface->init(interface);
1630 #ifdef CONFIG_PROC
1631 /* Create the proc entries */
1632 acer_proc_dir = proc_mkdir(PROC_ACER, acpi_root_dir);
1633 if (!acer_proc_dir) {
1634 printk(MY_ERR "Unable to create /proc entries, aborting.\n");
1635 goto error_proc_mkdir;
1638 acer_proc_dir->owner = THIS_MODULE;
1639 status = add_proc_entries();
1640 if (status) {
1641 printk(MY_ERR "Unable to create /proc entries, aborting.\n");
1642 goto error_proc_add;
1644 #endif
1647 * Register the driver
1649 status = acpi_bus_register_driver(&acer);
1650 DEBUG(1, "ACPI driver registered\n");
1651 if (status) {
1652 printk(MY_ERR "Unable to register ACPI driver, aborting.\n");
1653 goto error_acpi_bus_register;
1656 /* Override any initial settings with values from the commandline */
1657 acer_commandline_init();
1659 return 0;
1661 error_acpi_bus_register:
1662 #ifdef CONFIG_PROC
1663 remove_proc_entries();
1664 error_proc_add:
1665 if (acer_proc_dir)
1666 remove_proc_entry(PROC_ACER, acpi_root_dir);
1667 error_proc_mkdir:
1668 if (interface->free)
1669 interface->free(interface);
1670 #endif
1671 error_no_interface:
1672 return -ENODEV;
1675 static void __exit acer_acpi_exit(void)
1677 acpi_bus_unregister_driver(&acer);
1679 #ifdef CONFIG_PROC
1680 remove_proc_entries();
1682 if (acer_proc_dir)
1683 remove_proc_entry(PROC_ACER, acpi_root_dir);
1684 #endif
1686 if (interface->free)
1687 interface->free(interface);
1689 printk(MY_INFO "Acer Laptop ACPI Extras unloaded\n");
1690 return;
1693 module_init(acer_acpi_init);
1694 module_exit(acer_acpi_exit);