2 * asus-laptop.c - Asus Laptop Support
5 * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
6 * Copyright (C) 2006 Corentin Chary
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 development page for this driver is located at
24 * http://sourceforge.net/projects/acpi4asus/
27 * Pontus Fuchs - Helper functions, cleanup
28 * Johann Wiesner - Small compile fixes
29 * John Belmonte - ACPI code for Toshiba laptop was a good starting point.
30 * Eric Burghard - LED display support for W1N
31 * Josh Green - Light Sens support
32 * Thomas Tuttle - His first patch for led support was very helpfull
36 #include <linux/autoconf.h>
37 #include <linux/kernel.h>
38 #include <linux/module.h>
39 #include <linux/init.h>
40 #include <linux/types.h>
41 #include <linux/err.h>
42 #include <linux/proc_fs.h>
43 #include <linux/platform_device.h>
44 #include <acpi/acpi_drivers.h>
45 #include <acpi/acpi_bus.h>
46 #include <asm/uaccess.h>
48 #define ASUS_LAPTOP_VERSION "0.40"
50 #define ASUS_HOTK_NAME "Asus Laptop Support"
51 #define ASUS_HOTK_CLASS "hotkey"
52 #define ASUS_HOTK_DEVICE_NAME "Hotkey"
53 #define ASUS_HOTK_HID "ATK0100"
54 #define ASUS_HOTK_FILE "asus-laptop"
55 #define ASUS_HOTK_PREFIX "\\_SB.ATKD."
57 #define ASUS_LOG ASUS_HOTK_FILE ": "
58 #define ASUS_ERR KERN_ERR ASUS_LOG
59 #define ASUS_WARNING KERN_WARNING ASUS_LOG
60 #define ASUS_NOTICE KERN_NOTICE ASUS_LOG
61 #define ASUS_INFO KERN_INFO ASUS_LOG
62 #define ASUS_DEBUG KERN_DEBUG ASUS_LOG
64 MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
65 MODULE_DESCRIPTION(ASUS_HOTK_NAME
);
66 MODULE_LICENSE("GPL");
68 #define ASUS_HANDLE(object, paths...) \
69 static acpi_handle object##_handle = NULL; \
70 static char *object##_paths[] = { paths }
73 * This is the main structure, we can use it to store anything interesting
74 * about the hotk device
77 char *name
; //laptop name
78 struct acpi_device
*device
; //the device we are in
79 acpi_handle handle
; //the handle of the hotk device
80 char status
; //status of the hotk, for LEDs, ...
81 u16 event_count
[128]; //count for each event TODO make this better
85 * This header is made available to allow proper configuration given model,
86 * revision number , ... this info cannot go in struct asus_hotk because it is
87 * available before the hotk
89 static struct acpi_table_header
*asus_info
;
91 /* The actual device the driver binds to */
92 static struct asus_hotk
*hotk
;
95 * The hotkey driver declaration
97 static int asus_hotk_add(struct acpi_device
*device
);
98 static int asus_hotk_remove(struct acpi_device
*device
, int type
);
99 static struct acpi_driver asus_hotk_driver
= {
100 .name
= ASUS_HOTK_NAME
,
101 .class = ASUS_HOTK_CLASS
,
102 .ids
= ASUS_HOTK_HID
,
104 .add
= asus_hotk_add
,
105 .remove
= asus_hotk_remove
,
110 * This function evaluates an ACPI method, given an int as parameter, the
111 * method is searched within the scope of the handle, can be NULL. The output
112 * of the method is written is output, which can also be NULL
114 * returns 1 if write is successful, 0 else.
116 static int write_acpi_int(acpi_handle handle
, const char *method
, int val
,
117 struct acpi_buffer
*output
)
119 struct acpi_object_list params
; //list of input parameters (an int here)
120 union acpi_object in_obj
; //the only param we use
124 params
.pointer
= &in_obj
;
125 in_obj
.type
= ACPI_TYPE_INTEGER
;
126 in_obj
.integer
.value
= val
;
128 status
= acpi_evaluate_object(handle
, (char *)method
, ¶ms
, output
);
129 return (status
== AE_OK
);
132 static int read_acpi_int(acpi_handle handle
, const char *method
, int *val
,
133 struct acpi_object_list
*params
)
135 struct acpi_buffer output
;
136 union acpi_object out_obj
;
139 output
.length
= sizeof(out_obj
);
140 output
.pointer
= &out_obj
;
142 status
= acpi_evaluate_object(handle
, (char *)method
, params
, &output
);
143 *val
= out_obj
.integer
.value
;
144 return (status
== AE_OK
) && (out_obj
.type
== ACPI_TYPE_INTEGER
);
148 * Platform device handlers
152 * We write our info in page, we begin at offset off and cannot write more
153 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
154 * number of bytes written in page
156 static ssize_t
show_infos(struct device
*dev
,
157 struct device_attribute
*attr
, char *page
)
161 char buf
[16]; //enough for all info
163 * We use the easy way, we don't care of off and count, so we don't set eof
167 len
+= sprintf(page
, ASUS_HOTK_NAME
" " ASUS_LAPTOP_VERSION
"\n");
168 len
+= sprintf(page
+ len
, "Model reference : %s\n", hotk
->name
);
170 * The SFUN method probably allows the original driver to get the list
171 * of features supported by a given model. For now, 0x0100 or 0x0800
172 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
173 * The significance of others is yet to be found.
175 if (read_acpi_int(hotk
->handle
, "SFUN", &temp
, NULL
))
177 sprintf(page
+ len
, "SFUN value : 0x%04x\n", temp
);
179 * Another value for userspace: the ASYM method returns 0x02 for
180 * battery low and 0x04 for battery critical, its readings tend to be
181 * more accurate than those provided by _BST.
182 * Note: since not all the laptops provide this method, errors are
185 if (read_acpi_int(hotk
->handle
, "ASYM", &temp
, NULL
))
187 sprintf(page
+ len
, "ASYM value : 0x%04x\n", temp
);
189 snprintf(buf
, 16, "%d", asus_info
->length
);
190 len
+= sprintf(page
+ len
, "DSDT length : %s\n", buf
);
191 snprintf(buf
, 16, "%d", asus_info
->checksum
);
192 len
+= sprintf(page
+ len
, "DSDT checksum : %s\n", buf
);
193 snprintf(buf
, 16, "%d", asus_info
->revision
);
194 len
+= sprintf(page
+ len
, "DSDT revision : %s\n", buf
);
195 snprintf(buf
, 7, "%s", asus_info
->oem_id
);
196 len
+= sprintf(page
+ len
, "OEM id : %s\n", buf
);
197 snprintf(buf
, 9, "%s", asus_info
->oem_table_id
);
198 len
+= sprintf(page
+ len
, "OEM table id : %s\n", buf
);
199 snprintf(buf
, 16, "%x", asus_info
->oem_revision
);
200 len
+= sprintf(page
+ len
, "OEM revision : 0x%s\n", buf
);
201 snprintf(buf
, 5, "%s", asus_info
->asl_compiler_id
);
202 len
+= sprintf(page
+ len
, "ASL comp vendor id : %s\n", buf
);
203 snprintf(buf
, 16, "%x", asus_info
->asl_compiler_revision
);
204 len
+= sprintf(page
+ len
, "ASL comp revision : 0x%s\n", buf
);
210 static int parse_arg(const char *buf
, unsigned long count
, int *val
)
216 if (sscanf(buf
, "%i", val
) != 1)
221 static void asus_hotk_notify(acpi_handle handle
, u32 event
, void *data
)
223 /* TODO Find a better way to handle events count. */
227 acpi_bus_generate_event(hotk
->device
, event
,
228 hotk
->event_count
[event
% 128]++);
233 #define ASUS_CREATE_DEVICE_ATTR(_name) \
234 struct device_attribute dev_attr_##_name = { \
236 .name = __stringify(_name), \
238 .owner = THIS_MODULE }, \
243 #define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store) \
245 dev_attr_##_name.attr.mode = _mode; \
246 dev_attr_##_name.show = _show; \
247 dev_attr_##_name.store = _store; \
250 static ASUS_CREATE_DEVICE_ATTR(infos
);
252 static struct attribute
*asuspf_attributes
[] = {
253 &dev_attr_infos
.attr
,
257 static struct attribute_group asuspf_attribute_group
= {
258 .attrs
= asuspf_attributes
261 static struct platform_driver asuspf_driver
= {
263 .name
= ASUS_HOTK_FILE
,
264 .owner
= THIS_MODULE
,
268 static struct platform_device
*asuspf_device
;
271 static void asus_hotk_add_fs(void)
273 ASUS_SET_DEVICE_ATTR(infos
, 0444, show_infos
, NULL
);
276 static int asus_handle_init(char *name
, acpi_handle
*handle
,
277 char **paths
, int num_paths
)
282 for (i
= 0; i
< num_paths
; i
++) {
283 status
= acpi_get_handle(NULL
, paths
[i
], handle
);
284 if (ACPI_SUCCESS(status
))
292 #define ASUS_HANDLE_INIT(object) \
293 asus_handle_init(#object, &object##_handle, object##_paths, \
294 ARRAY_SIZE(object##_paths))
298 * This function is used to initialize the hotk with right values. In this
299 * method, we can make all the detection we want, and modify the hotk struct
301 static int asus_hotk_get_info(void)
303 struct acpi_buffer buffer
= { ACPI_ALLOCATE_BUFFER
, NULL
};
304 struct acpi_buffer dsdt
= { ACPI_ALLOCATE_BUFFER
, NULL
};
305 union acpi_object
*model
= NULL
;
311 * Get DSDT headers early enough to allow for differentiating between
312 * models, but late enough to allow acpi_bus_register_driver() to fail
313 * before doing anything ACPI-specific. Should we encounter a machine,
314 * which needs special handling (i.e. its hotkey device has a different
315 * HID), this bit will be moved. A global variable asus_info contains
318 status
= acpi_get_table(ACPI_TABLE_ID_DSDT
, 1, &dsdt
);
319 if (ACPI_FAILURE(status
))
320 printk(ASUS_WARNING
"Couldn't get the DSDT table header\n");
322 asus_info
= dsdt
.pointer
;
324 /* We have to write 0 on init this far for all ASUS models */
325 if (!write_acpi_int(hotk
->handle
, "INIT", 0, &buffer
)) {
326 printk(ASUS_ERR
"Hotkey initialization failed\n");
330 /* This needs to be called for some laptops to init properly */
331 if (!read_acpi_int(hotk
->handle
, "BSTS", &bsts_result
, NULL
))
332 printk(ASUS_WARNING
"Error calling BSTS\n");
333 else if (bsts_result
)
334 printk(ASUS_NOTICE
"BSTS called, 0x%02x returned\n",
338 * Try to match the object returned by INIT to the specific model.
339 * Handle every possible object (or the lack of thereof) the DSDT
340 * writers might throw at us. When in trouble, we pass NULL to
341 * asus_model_match() and try something completely different.
343 if (buffer
.pointer
) {
344 model
= buffer
.pointer
;
345 switch (model
->type
) {
346 case ACPI_TYPE_STRING
:
347 string
= model
->string
.pointer
;
349 case ACPI_TYPE_BUFFER
:
350 string
= model
->buffer
.pointer
;
357 hotk
->name
= kstrdup(string
, GFP_KERNEL
);
362 printk(ASUS_NOTICE
" %s model detected\n", string
);
369 static int asus_hotk_check(void)
373 result
= acpi_bus_get_status(hotk
->device
);
377 if (hotk
->device
->status
.present
) {
378 result
= asus_hotk_get_info();
380 printk(ASUS_ERR
"Hotkey device not present, aborting\n");
387 static int asus_hotk_found
;
389 static int asus_hotk_add(struct acpi_device
*device
)
391 acpi_status status
= AE_OK
;
397 printk(ASUS_NOTICE
"Asus Laptop Support version %s\n",
398 ASUS_LAPTOP_VERSION
);
400 hotk
= kmalloc(sizeof(struct asus_hotk
), GFP_KERNEL
);
403 memset(hotk
, 0, sizeof(struct asus_hotk
));
405 hotk
->handle
= device
->handle
;
406 strcpy(acpi_device_name(device
), ASUS_HOTK_DEVICE_NAME
);
407 strcpy(acpi_device_class(device
), ASUS_HOTK_CLASS
);
408 acpi_driver_data(device
) = hotk
;
409 hotk
->device
= device
;
411 result
= asus_hotk_check();
418 * We install the handler, it will receive the hotk in parameter, so, we
419 * could add other data to the hotk struct
421 status
= acpi_install_notify_handler(hotk
->handle
, ACPI_SYSTEM_NOTIFY
,
422 asus_hotk_notify
, hotk
);
423 if (ACPI_FAILURE(status
))
424 printk(ASUS_ERR
"Error installing notify handler\n");
437 static int asus_hotk_remove(struct acpi_device
*device
, int type
)
439 acpi_status status
= 0;
441 if (!device
|| !acpi_driver_data(device
))
444 status
= acpi_remove_notify_handler(hotk
->handle
, ACPI_SYSTEM_NOTIFY
,
446 if (ACPI_FAILURE(status
))
447 printk(ASUS_ERR
"Error removing notify handler\n");
455 static void __exit
asus_laptop_exit(void)
457 acpi_bus_unregister_driver(&asus_hotk_driver
);
458 sysfs_remove_group(&asuspf_device
->dev
.kobj
, &asuspf_attribute_group
);
459 platform_device_unregister(asuspf_device
);
460 platform_driver_unregister(&asuspf_driver
);
465 static int __init
asus_laptop_init(void)
472 if (!acpi_specific_hotkey_enabled
) {
473 printk(ASUS_ERR
"Using generic hotkey driver\n");
477 result
= acpi_bus_register_driver(&asus_hotk_driver
);
482 * This is a bit of a kludge. We only want this module loaded
483 * for ASUS systems, but there's currently no way to probe the
484 * ACPI namespace for ASUS HIDs. So we just return failure if
485 * we didn't find one, which will cause the module to be
488 if (!asus_hotk_found
) {
489 acpi_bus_unregister_driver(&asus_hotk_driver
);
493 /* Register platform stuff */
494 result
= platform_driver_register(&asuspf_driver
);
496 goto fail_platform_driver
;
498 asuspf_device
= platform_device_alloc(ASUS_HOTK_FILE
, -1);
499 if (!asuspf_device
) {
501 goto fail_platform_device1
;
504 result
= platform_device_add(asuspf_device
);
506 goto fail_platform_device2
;
508 result
= sysfs_create_group(&asuspf_device
->dev
.kobj
,
509 &asuspf_attribute_group
);
516 platform_device_del(asuspf_device
);
518 fail_platform_device2
:
519 platform_device_put(asuspf_device
);
521 fail_platform_device1
:
522 platform_driver_unregister(&asuspf_driver
);
524 fail_platform_driver
:
529 module_init(asus_laptop_init
);
530 module_exit(asus_laptop_exit
);