Patch from Jim Ramsay - fix u8 vs bool error with 3G, clean up earlier fix for AMW0
[acer_acpi.git] / acer_acpi.c
blob86a0697bf7ec767c318bbe41decff29ebb36bce3
1 /*
2 * acer_acpi.c - Acer Laptop ACPI Extras
5 * Copyright (C) 2005 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.6"
43 #define PROC_INTERFACE_VERSION 1
44 #define PROC_ACER "acer"
46 #include <linux/kernel.h>
47 #include <linux/module.h>
48 #include <linux/init.h>
49 #include <linux/types.h>
50 #include <linux/proc_fs.h>
51 #include <linux/delay.h>
52 #include <linux/suspend.h>
53 #include <asm/uaccess.h>
55 #include <acpi/acpi_drivers.h>
57 MODULE_AUTHOR("Mark Smith");
58 MODULE_DESCRIPTION("Acer Laptop ACPI Extras Driver");
59 MODULE_LICENSE("GPL");
61 #define MY_LOGPREFIX "acer_acpi: "
62 #define MY_ERR KERN_ERR MY_LOGPREFIX
63 #define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
64 #define MY_INFO KERN_INFO MY_LOGPREFIX
66 #define DEBUG(level, message...) { \
67 if (debug >= level) \
68 printk(KERN_DEBUG MY_LOGPREFIX message);\
72 * On the 5580, brightness values range from 0x0 to 0xf, inclusive.
73 * This may vary on other machines.
75 #define ACER_MAX_BRIGHTNESS 0xf
78 * Magic Number -
79 * Meaning is unknown - this number is required for writing to ACPI
80 * (it's also used in acerhk when directly accessing the EC)
82 #define ACER_WRITE 0x9610
85 * Bit masks for the old AMW0 interface
86 * These could vary between the particular interface
88 #define ACER_AMW0_WIRELESS_MASK 0x35
89 #define ACER_AMW0_BLUETOOTH_MASK 0x34
90 #define ACER_AMW0_MAILLED_MASK 0x31
93 * Method IDs for new WMID interface
94 * These could be different for other untested machines
96 #define ACER_WMID_GET_WIRELESS_METHODID 1
97 #define ACER_WMID_GET_BLUETOOTH_METHODID 2
98 #define ACER_WMID_GET_BRIGHTNESS_METHODID 3
99 #define ACER_WMID_SET_WIRELESS_METHODID 4
100 #define ACER_WMID_SET_BLUETOOTH_METHODID 5
101 #define ACER_WMID_SET_BRIGHTNESS_METHODID 6
102 #define ACER_WMID_GET_THREEG_METHODID 10
103 #define ACER_WMID_SET_THREEG_METHODID 11
106 * Acer ACPI method paths
108 * TODO: It may be possbile to autodetect these, since these are all at HID PNP0C14
110 #define AMW0_METHOD "\\_SB_.AMW0.WMAB"
111 #define AMW0_GETDATA "\\_SB_.AMW0._WED"
113 #define WMID_METHOD "\\_SB.WMID.WMBA"
114 #define WMID_GETDATA "\\_SB.WMID._WED"
117 * Interface capability flags
119 #define ACER_CAP_MAILLED (1<<0)
120 #define ACER_CAP_WIRELESS (1<<1)
121 #define ACER_CAP_BLUETOOTH (1<<2)
122 #define ACER_CAP_BRIGHTNESS (1<<3)
123 #define ACER_CAP_THREEG (1<<4)
124 #define ACER_CAP_ANY (0xffffffff)
127 * Presumed start states -
128 * For the old-style interfaces, there is no way to know for certain what the start state
129 * is for any of the parameters (ACPI does not provide any methods or store this
130 * anywhere). We therefore start with an unknown state (this is also how acerhk
131 * does it); we then change the status so that we are in a known state (I
132 * suspect LaunchManager on Windows does something similar, since the wireless
133 * appears to turn off as soon as it launches).
135 * Plus, we can't tell which features are enabled or disabled on a specific
136 * model, just ranges - e.g. The 5020 series can _support_ bluetooth; but the
137 * 5021 has no bluetooth, whilst the 5024 does. However, the BIOS identifies
138 * both laptops as 5020 - we can't tell them apart!
140 * Basically the code works like this:
141 * - On init, any values specified on the commandline are set.
142 * - For interfaces where the current values cannot be detected and which
143 * have not been set on the commandline, we set them to some sane default
144 * (disabled)
146 * See AMW0_init and acer_commandline_init
149 #define ACER_DEFAULT_WIRELESS 0
150 #define ACER_DEFAULT_BLUETOOTH 0
151 #define ACER_DEFAULT_MAILLED 0
152 #define ACER_DEFAULT_THREEG 0
153 #define ACER_DEFAULT_BRIGHTNESS ACER_MAX_BRIGHTNESS
155 static int wireless = -1;
156 static int bluetooth = -1;
157 static int mailled = -1;
158 static int brightness = -1;
159 static int threeg = -1;
160 static int debug = 0;
162 module_param(mailled, int, 0444);
163 module_param(wireless, int, 0444);
164 module_param(bluetooth, int, 0444);
165 module_param(brightness, int, 0444);
166 module_param(threeg, int, 0444);
167 module_param(debug, int, 0664);
168 MODULE_PARM_DESC(wireless, "Set initial state of Wireless hardware");
169 MODULE_PARM_DESC(bluetooth, "Set initial state of Bluetooth hardware");
170 MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
171 MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
172 MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
173 MODULE_PARM_DESC(debug, "Debugging verbosity level (0=least 2=most)");
175 typedef struct _ProcItem {
176 const char *name;
177 char *(*read_func) (char *, uint32_t);
178 unsigned long (*write_func) (const char *, unsigned long, uint32_t);
179 unsigned int capability;
180 } ProcItem;
182 struct acer_hotk {
183 struct acpi_device *device;
184 acpi_handle handle;
187 static struct proc_dir_entry *acer_proc_dir;
189 static int is_valid_acpi_path(const char *methodName)
191 acpi_handle handle;
192 acpi_status status;
194 status = acpi_get_handle(NULL, (char *)methodName, &handle);
195 return ACPI_SUCCESS(status);
198 /* Each low-level interface must define at least some of the following */
199 typedef struct _Interface {
201 * The capabilities this interface provides
202 * In the future, these can be removed/added at runtime when we have a
203 * way of detecting what capabilities are /actually/ present on an
204 * interface
206 uint32_t capability;
209 * Initializes an interface, should allocate the interface-specific
210 * data
212 acpi_status (*init) (struct _Interface*);
215 * Frees an interface, should free the interface-specific data
217 void (*free) (struct _Interface*);
220 * Gets and sets various data types.
221 * First paramater: Value to set, or pointer to place got value into
222 * Second parameter: Specific capability being requested
223 * Third paramater: Pointer to this interface
225 acpi_status (*get_bool) (bool*, uint32_t, struct _Interface*);
226 acpi_status (*set_bool) (bool, uint32_t, struct _Interface*);
227 acpi_status (*get_u8) (uint8_t*, uint32_t, struct _Interface*);
228 acpi_status (*set_u8) (uint8_t, uint32_t, struct _Interface*);
231 * Interface-specific private data member. Must *not* be touched by
232 * anyone outside of this struct
234 void *data;
235 } Interface;
237 /* The static interface pointer, points to the currently detected interface */
238 static Interface *interface;
241 * General interface convenience methods
244 /* These *_via_u8 use the interface's *_u8 methods to emulate other gets/sets */
245 static acpi_status get_bool_via_u8(bool *value, uint32_t cap, Interface *iface) {
246 acpi_status status;
247 uint8_t result;
249 status = iface->get_u8(&result, cap, iface);
251 if (ACPI_SUCCESS(status))
252 *value = (result != 0);
254 return status;
257 static acpi_status set_bool_via_u8(bool value, uint32_t cap, Interface *iface) {
258 uint8_t v = value ? 1 : 0;
260 return iface->set_u8(v, cap, iface);
264 * Old interface (now known as the AMW0 interface)
266 typedef struct _WMAB_args {
267 u32 eax;
268 u32 ebx;
269 u32 ecx;
270 u32 edx;
271 } WMAB_args;
273 typedef struct _AMW0_Data {
274 int mailled;
275 int wireless;
276 int bluetooth;
277 } AMW0_Data;
279 static acpi_status WMAB_execute(WMAB_args * regbuf, struct acpi_buffer *result)
281 struct acpi_object_list input;
282 union acpi_object params[3];
284 acpi_status status = AE_OK;
285 input.count = 3;
286 input.pointer = params;
288 params[0].type = ACPI_TYPE_INTEGER;
289 params[0].integer.value = 0x01; /* Only one instance of this object */
290 params[1].type = ACPI_TYPE_INTEGER;
291 params[1].integer.value = 0x01; /* Technically this should be method ID */
292 params[2].type = ACPI_TYPE_BUFFER;
293 params[2].buffer.length = sizeof(WMAB_args);
294 params[2].buffer.pointer = (u8 *) regbuf;
296 DEBUG(2, "Doing %s( 1, 1, [%u-byte buffer] )\n", AMW0_METHOD, params[2].buffer.length);
298 status = acpi_evaluate_object(NULL, AMW0_METHOD, &input, result);
300 DEBUG(2, " Execution status: %d\n", status);
301 DEBUG(2, " Args: 0x%08x 0x%08x 0x%08x 0x%08x\n", regbuf->eax, regbuf->ebx, regbuf->ecx, regbuf->edx );
302 DEBUG(2, " Result: %llu bytes\n", result ? result->length : 0 );
304 return status;
307 static acpi_status AMW0_init(Interface *iface) {
308 WMAB_args args;
309 acpi_status status;
310 AMW0_Data *data;
311  
312         /* Allocate our private data structure */
313 iface->data = kmalloc(sizeof(AMW0_Data), GFP_KERNEL);
314 data = (AMW0_Data*)iface->data;
317 * Call the interface once so the BIOS knows it's to notify us of
318 * events via PNP0C14
320 memset(&args, 0, sizeof(WMAB_args));
321 args.eax = ACER_WRITE;
322 args.ebx = 0;
323 status = WMAB_execute(&args, NULL);
326 * If the commandline doesn't specify these, we need to force them to
327 * the default values
329 if (mailled == -1)
330 mailled = ACER_DEFAULT_MAILLED;
331 if (wireless == -1)
332 wireless = ACER_DEFAULT_WIRELESS;
333 if (bluetooth == -1)
334 bluetooth = ACER_DEFAULT_BLUETOOTH;
337 * Set the cached "current" values to impossible ones so that
338 * acer_commandline_init will definitely set them.
340 data->wireless = data->mailled = data->bluetooth = -1;
342 return status;
345 static void AMW0_free(Interface *iface) {
346 /* Free our private data structure */
347 kfree(iface->data);
350 static acpi_status AMW0_get_bool(bool *value, uint32_t cap, Interface *iface)
352 AMW0_Data *data = iface->data;
354 /* Currently no way to query the state, so just return the cached value */
355 switch (cap) {
356 case ACER_CAP_MAILLED:
357 *value = data->mailled;
358 break;
359 case ACER_CAP_WIRELESS:
360 *value = data->wireless;
361 break;
362 case ACER_CAP_BLUETOOTH:
363 *value = data->bluetooth;
364 break;
365 default:
366 return AE_BAD_ADDRESS;
368 return AE_OK;
371 static acpi_status AMW0_set_bool(bool value, uint32_t cap, Interface *iface)
373 WMAB_args args;
374 acpi_status status;
376 args.eax = ACER_WRITE;
377 args.ebx = value ? (1<<8) : 0;
379 switch (cap) {
380 case ACER_CAP_MAILLED:
381 args.ebx |= ACER_AMW0_MAILLED_MASK;
382 break;
383 case ACER_CAP_WIRELESS:
384 args.ebx |= ACER_AMW0_WIRELESS_MASK;
385 break;
386 case ACER_CAP_BLUETOOTH:
387 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
388 break;
389 default:
390 return AE_BAD_ADDRESS;
393 /* Actually do the set */
394 status = WMAB_execute(&args, NULL);
397 * Currently no way to query the state, so cache the new value on
398 * success
400 if (ACPI_SUCCESS(status)) {
401 AMW0_Data *data = iface->data;
402 switch (cap) {
403 case ACER_CAP_MAILLED:
404 data->mailled = value;
405 break;
406 case ACER_CAP_WIRELESS:
407 data->wireless = value;
408 break;
409 case ACER_CAP_BLUETOOTH:
410 data->bluetooth = value;
411 break;
415 return status;
418 static Interface AMW0_interface = {
419 .capability = (
420 ACER_CAP_MAILLED |
421 ACER_CAP_WIRELESS |
422 ACER_CAP_BLUETOOTH
424 .init = AMW0_init,
425 .free = AMW0_free,
426 .get_bool = AMW0_get_bool,
427 .set_bool = AMW0_set_bool,
431 * New interface (The WMID interface)
434 static acpi_status
435 WMI_execute(uint32_t methodId, const struct acpi_buffer *in, struct acpi_buffer *out) {
436 struct acpi_object_list input;
437 union acpi_object params[3];
438 acpi_status status = AE_OK;
440 /* WMI calling convention:
441 * _SB.WMID.WMxx( instance, methodId, input_buffer )
442 * - instance is always 1, since there's only this module
443 * - methodId is the method number within the current method group.
444 * - Input buffer is ignored for read-only commands
445 * - Returns a buffer of results
447 input.count = 3;
448 input.pointer = params;
449 params[0].type = ACPI_TYPE_INTEGER;
450 params[0].integer.value = 0x01;
451 params[1].type = ACPI_TYPE_INTEGER;
452 params[1].integer.value = methodId;
453 params[2].type = ACPI_TYPE_BUFFER;
454 params[2].buffer.length = in->length;
455 params[2].buffer.pointer = in->pointer;
457 DEBUG(2, "Doing %s( 1, %u, [%llu-byte buffer] )\n", WMID_METHOD, methodId, in->length);
459 status = acpi_evaluate_object(NULL, WMID_METHOD, &input, out);
461 return status;
464 static acpi_status
465 WMI_execute_uint32(uint32_t methodId, uint32_t in, uint32_t *out)
467 struct acpi_buffer input = { (acpi_size)sizeof(uint32_t), (void*)(&in) };
468 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
469 union acpi_object *obj;
470 uint32_t tmp;
471 acpi_status status;
473 status = WMI_execute(methodId, &input, &result);
474 DEBUG(2, " Execution status: %d\n", status);
475 DEBUG(2, " In: 0x%08x\n", in);
477 if (ACPI_FAILURE(status))
478 return status;
480 obj = (union acpi_object *)result.pointer;
481 if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == sizeof(uint32_t)) {
482 tmp = *((uint32_t*)obj->buffer.pointer);
483 DEBUG(2, " Out: 0x%08x\n", tmp);
484 } else {
485 tmp = 0;
486 if (obj) {
487 DEBUG(2, " Got unexpected result of type %d\n", obj->type);
488 } else {
489 DEBUG(2, " Got unexpected null result\n");
493 if (out)
494 *out = tmp;
496 if (result.length > 0 && result.pointer)
497 kfree(result.pointer);
499 return status;
502 static acpi_status WMID_get_u8(uint8_t *value, uint32_t cap, Interface *iface) {
503 acpi_status status;
504 uint32_t result;
505 uint32_t methodId = 0;
507 switch (cap) {
508 case ACER_CAP_WIRELESS:
509 methodId = ACER_WMID_GET_WIRELESS_METHODID;
510 break;
511 case ACER_CAP_BLUETOOTH:
512 methodId = ACER_WMID_GET_BLUETOOTH_METHODID;
513 break;
514 case ACER_CAP_BRIGHTNESS:
515 methodId = ACER_WMID_GET_BRIGHTNESS_METHODID;
516 break;
517 case ACER_CAP_THREEG:
518 methodId = ACER_WMID_GET_THREEG_METHODID;
519 break;
520 default:
521 return AE_BAD_ADDRESS;
523 status = WMI_execute_uint32(methodId, 0, &result);
525 if (ACPI_SUCCESS(status))
526 *value = (uint8_t)result;
528 return status;
531 static acpi_status WMID_set_u8(uint8_t value, uint32_t cap, Interface *iface) {
532 uint32_t methodId = 0;
534 switch (cap) {
535 case ACER_CAP_BRIGHTNESS:
536 methodId = ACER_WMID_SET_BRIGHTNESS_METHODID;
537 break;
538 case ACER_CAP_WIRELESS:
539 methodId = ACER_WMID_SET_WIRELESS_METHODID;
540 break;
541 case ACER_CAP_BLUETOOTH:
542 methodId = ACER_WMID_SET_BLUETOOTH_METHODID;
543 case ACER_CAP_THREEG:
544 methodId = ACER_WMID_SET_THREEG_METHODID;
545 break;
546 default:
547 return AE_BAD_ADDRESS;
549 return WMI_execute_uint32(methodId, (uint32_t)value, NULL);
553 static Interface WMID_interface = {
554 .capability = (
555 ACER_CAP_WIRELESS |
556 ACER_CAP_BLUETOOTH |
557 ACER_CAP_BRIGHTNESS |
558 ACER_CAP_THREEG
560 .get_bool = get_bool_via_u8,
561 .set_bool = set_bool_via_u8,
562 .get_u8 = WMID_get_u8,
563 .set_u8 = WMID_set_u8,
564 .data = NULL,
568 * High-level Procfs file handlers
571 static int
572 dispatch_read(char *page, char **start, off_t off, int count, int *eof,
573 ProcItem * item)
575 char *p = page;
576 int len;
578 if (off == 0)
579 p = item->read_func(p, item->capability);
582 * ISSUE: I don't understand this code
584 len = (p - page);
585 if (len <= off + count)
586 *eof = 1;
587 *start = page + off;
588 len -= off;
589 if (len > count)
590 len = count;
591 if (len < 0)
592 len = 0;
593 return len;
596 static int
597 dispatch_write(struct file *file, const char __user * buffer,
598 unsigned long count, ProcItem * item)
600 int result;
601 char *tmp_buffer;
604 * Arg buffer points to userspace memory, which can't be accessed
605 * directly. Since we're making a copy, zero-terminate the
606 * destination so that sscanf can be used on it safely.
608 tmp_buffer = kmalloc(count + 1, GFP_KERNEL);
609 if (copy_from_user(tmp_buffer, buffer, count)) {
610 result = -EFAULT;
611 } else {
612 tmp_buffer[count] = 0;
613 result = item->write_func(tmp_buffer, count, item->capability);
615 kfree(tmp_buffer);
616 return result;
620 * Generic Device (interface-independent)
623 static acpi_status get_bool(bool *value, uint32_t cap) {
624 acpi_status status = AE_BAD_ADDRESS;
625 if (interface->get_bool)
626 status = interface->get_bool(value, cap, interface);
627 return status;
630 static acpi_status set_bool(int value, uint32_t cap) {
631 acpi_status status = AE_BAD_PARAMETER;
632 if ((value == 0 || value == 1) &&
633 (interface->capability & cap)) {
634 if (interface->get_bool) {
635 /* If possible, only set if the value has changed */
636 bool actual;
637 status = interface->get_bool(&actual, cap, interface);
638 if (ACPI_SUCCESS(status) && actual == (bool)value)
639 return status;
641 if (interface->set_bool)
642 status = interface->set_bool(value == 1, cap, interface);
644 return status;
648 static acpi_status get_u8(uint8_t *value, uint32_t cap) {
649 acpi_status status = AE_BAD_ADDRESS;
650 if (interface->get_u8)
651 status = interface->get_u8(value, cap, interface);
652 return status;
655 static acpi_status set_u8(uint8_t value, uint8_t min, uint8_t max, uint32_t cap) {
656 acpi_status status = AE_BAD_PARAMETER;
657 if ((value >= min && value <= max) &&
658 (interface->capability & cap) ) {
659 if (interface->get_u8) {
660 /* If possible, only set if the value has changed */
661 uint8_t actual;
662 status = interface->get_u8(&actual, cap, interface);
663 if (ACPI_SUCCESS(status) && actual == value)
664 return status;
666 if (interface->set_u8)
667 status = interface->set_u8(value, cap, interface);
669 return status;
672 /* Each _u8 needs a small wrapper that sets the boundary values */
673 static acpi_status set_brightness(uint8_t value)
675 return set_u8(value, 0, ACER_MAX_BRIGHTNESS, ACER_CAP_BRIGHTNESS);
678 static void acpi_commandline_init(void)
680 DEBUG(1, "Commandline args: mailled(%d) wireless(%d) bluetooth(%d) brightness(%d)\n",
681 mailled, wireless, bluetooth, brightness);
684 * These will all fail silently if the value given is invalid, or the
685 * capability isn't available on the given interface
687 set_bool(mailled, ACER_CAP_MAILLED);
688 set_bool(wireless, ACER_CAP_WIRELESS);
689 set_bool(bluetooth, ACER_CAP_BLUETOOTH);
690 set_bool(threeg, ACER_CAP_THREEG);
691 set_brightness((uint8_t)brightness);
695 * Procfs interface
697 static char *read_bool(char *p, uint32_t cap)
699 bool result;
700 acpi_status status = get_bool(&result, cap);
701 if (ACPI_SUCCESS(status))
702 p += sprintf(p, "%d\n", result);
703 else
704 p += sprintf(p, "Read error" );
705 return p;
708 static unsigned long write_bool(const char *buffer, unsigned long count, uint32_t cap)
710 int value;
713 * For now, we are still supporting the "enabled: %i" - this _will_ be deprecated in 0.7
715 if ((sscanf(buffer, " enabled : %i", &value) == 1
716 || sscanf(buffer, "%i", &value) == 1)) {
717 acpi_status status = set_bool(value, cap);
718 if (ACPI_FAILURE(status))
719 return -EINVAL;
720 } else {
721 return -EINVAL;
723 return count;
726 static char *read_u8(char *p, uint32_t cap)
728 uint8_t result;
729 acpi_status status = get_u8(&result, cap);
730 if (ACPI_SUCCESS(status))
731 p += sprintf(p, "%u\n", result);
732 else
733 p += sprintf(p, "Read error" );
734 return p;
737 static unsigned long write_u8(const char *buffer, unsigned long count, uint32_t cap)
739 int value;
740 acpi_status (*set_method)(uint8_t);
742 /* Choose the appropriate set_u8 wrapper here, based on the capability */
743 switch (cap) {
744 case ACER_CAP_BRIGHTNESS:
745 set_method = set_brightness;
746 break;
747 default:
748 return -EINVAL;
751 if (sscanf(buffer, "%i", &value) == 1) {
752 acpi_status status = (*set_method)(value);
753 if (ACPI_FAILURE(status))
754 return -EINVAL;
755 } else {
756 return -EINVAL;
758 return count;
761 static char *read_version(char *p, uint32_t cap)
763 p += sprintf(p, "driver: %s\n", ACER_ACPI_VERSION);
764 p += sprintf(p, "proc_interface: %d\n",
765 PROC_INTERFACE_VERSION);
766 return p;
769 ProcItem proc_items[] = {
770 {"mailled", read_bool, write_bool, ACER_CAP_MAILLED},
771 {"bluetooth", read_bool, write_bool, ACER_CAP_BLUETOOTH},
772 {"wireless", read_bool, write_bool, ACER_CAP_WIRELESS},
773 {"brightness", read_u8, write_u8, ACER_CAP_BRIGHTNESS},
774 {"threeg", read_bool, write_bool, ACER_CAP_THREEG},
775 {"version", read_version, NULL, ACER_CAP_ANY},
776 {NULL}
779 static acpi_status __init add_proc_entries(void)
781 struct proc_dir_entry *proc;
782 ProcItem *item;
784 for (item = proc_items; item->name; ++item) {
786 * Only add the proc file if the current interface actually
787 * supports it
789 if (interface->capability & item->capability) {
790 proc = create_proc_read_entry(item->name,
791 S_IFREG | S_IRUGO | S_IWUSR,
792 acer_proc_dir,
793 (read_proc_t *) dispatch_read,
794 item);
795 if (proc)
796 proc->owner = THIS_MODULE;
797 if (proc && item->write_func)
798 proc->write_proc = (write_proc_t *) dispatch_write;
802 return AE_OK;
805 static acpi_status __exit remove_proc_entries(void)
807 ProcItem *item;
809 for (item = proc_items; item->name; ++item)
810 remove_proc_entry(item->name, acer_proc_dir);
811 return AE_OK;
815 * TODO: make this actually do useful stuff, if we ever see events
817 static void acer_acerkeys_notify(acpi_handle handle, u32 event, void *data)
819 struct acer_hotk *hotk = (struct acer_hotk *)data;
821 if (!hotk)
822 return;
823 printk(MY_ERR "Got an event!! %X", event);
825 return;
828 static int acpi_acerkeys_add(struct acpi_device *device)
830 struct acer_hotk *hotk = NULL;
831 acpi_status status = AE_OK;
833 if (!device)
834 return -EINVAL;
836 hotk =
837 (struct acer_hotk *)kmalloc(sizeof(struct acer_hotk), GFP_KERNEL);
838 if (!hotk)
839 return -ENOMEM;
840 memset(hotk, 0, sizeof(struct acer_hotk));
841 hotk->handle = device->handle;
842 strcpy(acpi_device_name(device), "Acer Laptop ACPI Extras");
843 strcpy(acpi_device_class(device), "hkey");
844 acpi_driver_data(device) = hotk;
845 hotk->device = device;
847 status = acpi_install_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
848 acer_acerkeys_notify, hotk);
849 if (ACPI_FAILURE(status))
850 printk(MY_ERR "Error installing notify handler.\n");
851 return 0;
854 static int acpi_acerkeys_remove(struct acpi_device *device, int type)
856 acpi_status status = 0;
857 struct acer_hotk *hotk = NULL;
859 if (!device || !acpi_driver_data(device))
860 return -EINVAL;
861 hotk = (struct acer_hotk *)acpi_driver_data(device);
863 status = acpi_remove_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
864 acer_acerkeys_notify);
865 if (ACPI_FAILURE(status))
866 printk(MY_ERR "Error removing notify handler.\n");
867 kfree(hotk);
869 return 0;
872 static struct acpi_driver acpi_acerkeys = {
873 .name = "acer_acpi",
874 .class = "hotkey",
875 .ids = "PNP0C14",
876 .ops = {
877 .add = acpi_acerkeys_add,
878 .remove = acpi_acerkeys_remove,
882 static int __init acer_acpi_init(void)
884 acpi_status status = AE_OK;
886 printk(MY_INFO "Acer Laptop ACPI Extras version %s\n",
887 ACER_ACPI_VERSION);
888 if (acpi_disabled) {
889 printk(MY_ERR "ACPI Disabled, unable to load.\n");
890 return -ENODEV;
894 * Detect which WMI interface we're using.
896 * TODO: This could be more dynamic, and perhaps done in part by the
897 * acpi_bus driver?
899 if (is_valid_acpi_path(AMW0_METHOD)) {
900 DEBUG(0, "Detected ACER AMW0 interface\n");
901 interface = &AMW0_interface;
902 } else if (is_valid_acpi_path(WMID_METHOD)) {
903 DEBUG(0, "Detected ACER WMID interface\n");
904 interface = &WMID_interface;
905 } else {
906 printk(MY_ERR "No or unsupported WMI interface, unable to load.\n");
907 goto error_no_interface;
910 /* Now that we have a known interface, initialize it */
911 if (interface->init) {
912 status = interface->init(interface);
913 if (ACPI_FAILURE(status)) {
914 printk(MY_ERR "Interface initialization failed.\n");
915 goto error_interface_init;
919 /* Create the proc entries */
920 acer_proc_dir = proc_mkdir(PROC_ACER, acpi_root_dir);
921 if (!acer_proc_dir) {
922 printk(MY_ERR "Unable to create /proc entries, aborting.\n");
923 goto error_proc_mkdir;
926 acer_proc_dir->owner = THIS_MODULE;
927 status = add_proc_entries();
928 if (ACPI_FAILURE(status)) {
929 printk(MY_ERR "Unable to create /proc entries, aborting.\n");
930 goto error_proc_add;
934 * Register the hotkeys driver
936 * TODO: Does this do anything? Can we use the bus detection code to
937 * check for the interface or all or part of the method ID path?
939 status = acpi_bus_register_driver(&acpi_acerkeys);
940 if (ACPI_FAILURE(status)) {
941 printk(MY_ERR "Unable to register driver, aborting.\n");
942 goto error_acpi_bus_register;
945 /* Finally, override any initial settings with values from the commandline */
946 acpi_commandline_init();
948 return 0;
950 error_acpi_bus_register:
951 remove_proc_entries();
952 error_proc_add:
953 if (acer_proc_dir)
954 remove_proc_entry(PROC_ACER, acpi_root_dir);
955 error_proc_mkdir:
956 if (interface->free)
957 interface->free(interface);
958 error_interface_init:
959 error_no_interface:
960 return -ENODEV;
963 static void __exit acer_acpi_exit(void)
965 acpi_bus_unregister_driver(&acpi_acerkeys);
967 remove_proc_entries();
969 if (acer_proc_dir)
970 remove_proc_entry(PROC_ACER, acpi_root_dir);
972 if (interface->free)
973 interface->free(interface);
975 printk(MY_INFO "Acer Laptop ACPI Extras unloaded\n");
976 return;
979 module_init(acer_acpi_init);
980 module_exit(acer_acpi_exit);