asus-laptop: add gps rfkill
[linux-2.6-block.git] / drivers / platform / x86 / asus-laptop.c
CommitLineData
85091b71
CC
1/*
2 * asus-laptop.c - Asus Laptop Support
3 *
4 *
5 * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
8ec555c2 6 * Copyright (C) 2006-2007 Corentin Chary
85091b71
CC
7 *
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.
12 *
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.
17 *
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
21 *
22 *
23 * The development page for this driver is located at
24 * http://sourceforge.net/projects/acpi4asus/
25 *
26 * Credits:
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
e539c2f6 33 * Sam Lin - GPS support
85091b71
CC
34 */
35
2fcc23da
CC
36#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
37
85091b71
CC
38#include <linux/kernel.h>
39#include <linux/module.h>
40#include <linux/init.h>
41#include <linux/types.h>
42#include <linux/err.h>
43#include <linux/proc_fs.h>
6b7091e7
CC
44#include <linux/backlight.h>
45#include <linux/fb.h>
be18cdab 46#include <linux/leds.h>
85091b71
CC
47#include <linux/platform_device.h>
48#include <acpi/acpi_drivers.h>
49#include <acpi/acpi_bus.h>
50#include <asm/uaccess.h>
034ce90a 51#include <linux/input.h>
66a71dd1 52#include <linux/input/sparse-keymap.h>
18e1311e 53#include <linux/rfkill.h>
85091b71 54
91687cc8 55#define ASUS_LAPTOP_VERSION "0.42"
85091b71 56
50a90c4d
CC
57#define ASUS_LAPTOP_NAME "Asus Laptop Support"
58#define ASUS_LAPTOP_CLASS "hotkey"
59#define ASUS_LAPTOP_DEVICE_NAME "Hotkey"
60#define ASUS_LAPTOP_FILE KBUILD_MODNAME
61#define ASUS_LAPTOP_PREFIX "\\_SB.ATKD."
85091b71 62
85091b71 63MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
50a90c4d 64MODULE_DESCRIPTION(ASUS_LAPTOP_NAME);
85091b71
CC
65MODULE_LICENSE("GPL");
66
be966660
CC
67/*
68 * WAPF defines the behavior of the Fn+Fx wlan key
185e5af9
CC
69 * The significance of values is yet to be found, but
70 * most of the time:
71 * 0x0 will do nothing
72 * 0x1 will allow to control the device with Fn+Fx key.
73 * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
74 * 0x5 like 0x1 or 0x4
75 * So, if something doesn't work as you want, just try other values =)
76 */
77static uint wapf = 1;
78module_param(wapf, uint, 0644);
79MODULE_PARM_DESC(wapf, "WAPF value");
80
d0930a2d 81static uint wlan_status = 1;
0e875f49
CC
82static uint bluetooth_status = 1;
83
d0930a2d
CC
84module_param(wlan_status, uint, 0644);
85MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot "
0e875f49
CC
86 "(0 = disabled, 1 = enabled, -1 = don't do anything). "
87 "default is 1");
88
89module_param(bluetooth_status, uint, 0644);
90MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot "
91 "(0 = disabled, 1 = enabled, -1 = don't do anything). "
92 "default is 1");
93
be4ee82d
CC
94/*
95 * Some events we use, same for all Asus
96 */
a539df5e
CC
97#define ATKD_BR_UP 0x10 // (event & ~ATKD_BR_UP) = brightness level
98#define ATKD_BR_DOWN 0x20 // (event & ~ATKD_BR_DOWN) = britghness level
99#define ATKD_BR_MIN ATKD_BR_UP
100#define ATKD_BR_MAX (ATKD_BR_DOWN | 0xF) // 0x2f
be4ee82d
CC
101#define ATKD_LCD_ON 0x33
102#define ATKD_LCD_OFF 0x34
103
104/*
105 * Known bits returned by \_SB.ATKD.HWRS
106 */
107#define WL_HWRS 0x80
108#define BT_HWRS 0x100
109
110/*
111 * Flags for hotk status
112 * WL_ON and BT_ON are also used for wireless_status()
113 */
17e78f62
CC
114#define WL_RSTS 0x01 /* internal Wifi */
115#define BT_RSTS 0x02 /* internal Bluetooth */
be4ee82d 116
85091b71
CC
117#define ASUS_HANDLE(object, paths...) \
118 static acpi_handle object##_handle = NULL; \
119 static char *object##_paths[] = { paths }
120
be18cdab 121/* LED */
d99b577c
CC
122#define METHOD_MLED "MLED"
123#define METHOD_TLED "TLED"
124#define METHOD_RLED "RLED" /* W1JC */
125#define METHOD_PLED "PLED" /* A7J */
126#define METHOD_GLED "GLED" /* G1, G2 (probably) */
be18cdab 127
722ad971 128/* LEDD */
d99b577c 129#define METHOD_LEDD "SLCM"
722ad971 130
be966660
CC
131/*
132 * Bluetooth and WLAN
4564de17
CC
133 * WLED and BLED are not handled like other XLED, because in some dsdt
134 * they also control the WLAN/Bluetooth device.
135 */
d99b577c
CC
136#define METHOD_WLAN "WLED"
137#define METHOD_BLUETOOTH "BLED"
138#define METHOD_WL_STATUS "RSTS"
4564de17 139
6b7091e7 140/* Brightness */
d99b577c
CC
141#define METHOD_BRIGHTNESS_SET "SPLV"
142#define METHOD_BRIGHTNESS_GET "GPLV"
6b7091e7
CC
143
144/* Backlight */
8def05fa
LB
145ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */
146 "\\_SB.PCI0.ISA.EC0._Q10", /* A1x */
147 "\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */
148 "\\_SB.PCI0.PX40.EC0.Q10", /* M1A */
149 "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */
4d0b856e 150 "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */
8def05fa
LB
151 "\\_SB.PCI0.PX40.Q10", /* S1x */
152 "\\Q10"); /* A2x, L2D, L3D, M2E */
6b7091e7 153
78127b4a 154/* Display */
d99b577c 155#define METHOD_SWITCH_DISPLAY "SDSP"
be966660
CC
156ASUS_HANDLE(display_get,
157 /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */
158 "\\_SB.PCI0.P0P1.VGA.GETD",
159 /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */
160 "\\_SB.PCI0.P0P2.VGA.GETD",
161 /* A6V A6Q */
162 "\\_SB.PCI0.P0P3.VGA.GETD",
163 /* A6T, A6M */
164 "\\_SB.PCI0.P0PA.VGA.GETD",
165 /* L3C */
166 "\\_SB.PCI0.PCI1.VGAC.NMAP",
167 /* Z96F */
168 "\\_SB.PCI0.VGA.GETD",
169 /* A2D */
170 "\\ACTD",
171 /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
172 "\\ADVG",
173 /* P30 */
174 "\\DNXT",
175 /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
176 "\\INFB",
177 /* A3F A6F A3N A3L M6N W3N W6A */
178 "\\SSTE");
179
d99b577c
CC
180#define METHOD_ALS_CONTROL "ALSC" /* Z71A Z71V */
181#define METHOD_ALS_LEVEL "ALSL" /* Z71A Z71V */
8b857353 182
e539c2f6
CC
183/* GPS */
184/* R2H use different handle for GPS on/off */
d99b577c
CC
185#define METHOD_GPS_ON "SDON"
186#define METHOD_GPS_OFF "SDOF"
187#define METHOD_GPS_STATUS "GPST"
e539c2f6 188
b7d3fbc2 189/* Keyboard light */
d99b577c
CC
190#define METHOD_KBD_LIGHT_SET "SLKB"
191#define METHOD_KBD_LIGHT_GET "GLKB"
b7d3fbc2 192
9129d14d
CC
193/*
194 * Define a specific led structure to keep the main structure clean
195 */
196#define ASUS_DEFINE_LED(object) \
197 int object##_wk; \
198 struct work_struct object##_work; \
199 struct led_classdev object;
200
201
202#define led_to_asus(led_cdev, led) \
203 container_of(container_of(led_cdev, struct asus_laptop_leds, \
204 led), \
205 struct asus_laptop, leds)
206#define work_to_asus(work, led) \
207 container_of(container_of(work, struct asus_laptop_leds, \
208 led##_work), \
209 struct asus_laptop, leds)
210
211struct asus_laptop_leds {
212 ASUS_DEFINE_LED(mled)
213 ASUS_DEFINE_LED(tled)
214 ASUS_DEFINE_LED(rled)
215 ASUS_DEFINE_LED(pled)
216 ASUS_DEFINE_LED(gled)
217 ASUS_DEFINE_LED(kled)
218 struct workqueue_struct *workqueue;
219};
220
85091b71
CC
221/*
222 * This is the main structure, we can use it to store anything interesting
223 * about the hotk device
224 */
50a90c4d 225struct asus_laptop {
be966660 226 char *name; /* laptop name */
600ad520 227
7c247645 228 struct acpi_table_header *dsdt_info;
600ad520 229 struct platform_device *platform_device;
7c247645
CC
230 struct acpi_device *device; /* the device we are in */
231 struct backlight_device *backlight_device;
9129d14d 232
7c247645 233 struct input_dev *inputdev;
9129d14d
CC
234 struct key_entry *keymap;
235
236 struct asus_laptop_leds leds;
7c247645 237
aa9df930
CC
238 int wireless_status;
239 bool have_rsts;
3e68ae7c 240 int lcd_state;
aa9df930 241
18e1311e
CC
242 struct rfkill *gps_rfkill;
243
be966660 244 acpi_handle handle; /* the handle of the hotk device */
be966660
CC
245 u32 ledd_status; /* status of the LED display */
246 u8 light_level; /* light sensor level */
247 u8 light_switch; /* light sensor switch value */
248 u16 event_count[128]; /* count for each event TODO make this better */
034ce90a 249 u16 *keycode_map;
85091b71
CC
250};
251
9129d14d 252static const struct key_entry asus_keymap[] = {
a539df5e 253 /* Lenovo SL Specific keycodes */
66a71dd1
CC
254 {KE_KEY, 0x02, { KEY_SCREENLOCK } },
255 {KE_KEY, 0x05, { KEY_WLAN } },
256 {KE_KEY, 0x08, { KEY_F13 } },
257 {KE_KEY, 0x17, { KEY_ZOOM } },
258 {KE_KEY, 0x1f, { KEY_BATTERY } },
a539df5e 259 /* End of Lenovo SL Specific keycodes */
66a71dd1
CC
260 {KE_KEY, 0x30, { KEY_VOLUMEUP } },
261 {KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
262 {KE_KEY, 0x32, { KEY_MUTE } },
263 {KE_KEY, 0x33, { KEY_SWITCHVIDEOMODE } },
264 {KE_KEY, 0x34, { KEY_SWITCHVIDEOMODE } },
265 {KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
266 {KE_KEY, 0x41, { KEY_NEXTSONG } },
267 {KE_KEY, 0x43, { KEY_STOPCD } },
268 {KE_KEY, 0x45, { KEY_PLAYPAUSE } },
269 {KE_KEY, 0x4c, { KEY_MEDIA } },
270 {KE_KEY, 0x50, { KEY_EMAIL } },
271 {KE_KEY, 0x51, { KEY_WWW } },
272 {KE_KEY, 0x55, { KEY_CALC } },
273 {KE_KEY, 0x5C, { KEY_SCREENLOCK } }, /* Screenlock */
274 {KE_KEY, 0x5D, { KEY_WLAN } },
275 {KE_KEY, 0x5E, { KEY_WLAN } },
276 {KE_KEY, 0x5F, { KEY_WLAN } },
277 {KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } },
278 {KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } },
279 {KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
280 {KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
281 {KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */
7f607d71
CC
282 {KE_KEY, 0x7E, { KEY_BLUETOOTH } },
283 {KE_KEY, 0x7D, { KEY_BLUETOOTH } },
66a71dd1
CC
284 {KE_KEY, 0x82, { KEY_CAMERA } },
285 {KE_KEY, 0x88, { KEY_WLAN } },
286 {KE_KEY, 0x8A, { KEY_PROG1 } },
287 {KE_KEY, 0x95, { KEY_MEDIA } },
288 {KE_KEY, 0x99, { KEY_PHONE } },
289 {KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
290 {KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
034ce90a
CC
291 {KE_END, 0},
292};
293
66a71dd1 294
85091b71
CC
295/*
296 * This function evaluates an ACPI method, given an int as parameter, the
297 * method is searched within the scope of the handle, can be NULL. The output
298 * of the method is written is output, which can also be NULL
299 *
f8d1c94b 300 * returns 0 if write is successful, -1 else.
85091b71 301 */
d8c67323
CC
302static int write_acpi_int_ret(acpi_handle handle, const char *method, int val,
303 struct acpi_buffer *output)
85091b71 304{
be966660
CC
305 struct acpi_object_list params; /* list of input parameters (an int) */
306 union acpi_object in_obj; /* the only param we use */
85091b71
CC
307 acpi_status status;
308
f8d1c94b
CC
309 if (!handle)
310 return 0;
311
85091b71
CC
312 params.count = 1;
313 params.pointer = &in_obj;
314 in_obj.type = ACPI_TYPE_INTEGER;
315 in_obj.integer.value = val;
316
317 status = acpi_evaluate_object(handle, (char *)method, &params, output);
f8d1c94b
CC
318 if (status == AE_OK)
319 return 0;
320 else
321 return -1;
85091b71
CC
322}
323
d8c67323
CC
324static int write_acpi_int(acpi_handle handle, const char *method, int val)
325{
326 return write_acpi_int_ret(handle, method, val, NULL);
327}
328
d99b577c
CC
329static int acpi_check_handle(acpi_handle handle, const char *method,
330 acpi_handle *ret)
331{
332 acpi_status status;
333
334 if (method == NULL)
335 return -ENODEV;
336
337 if (ret)
338 status = acpi_get_handle(handle, (char *)method,
339 ret);
340 else {
341 acpi_handle dummy;
342
343 status = acpi_get_handle(handle, (char *)method,
344 &dummy);
345 }
346
347 if (status != AE_OK) {
348 if (ret)
349 pr_warning("Error finding %s\n", method);
350 return -ENODEV;
351 }
352 return 0;
353}
354
17e78f62 355/* Generic LED function */
e5593bf1 356static int asus_led_set(struct asus_laptop *asus, char *method,
17e78f62 357 int value)
be18cdab 358{
d99b577c 359 if (!strcmp(method, METHOD_MLED))
17e78f62 360 value = !value;
d99b577c 361 else if (!strcmp(method, METHOD_GLED))
17e78f62
CC
362 value = !value + 1;
363 else
364 value = !!value;
be18cdab 365
e5593bf1 366 return write_acpi_int(asus->handle, method, value);
be18cdab
CC
367}
368
be4ee82d
CC
369/*
370 * LEDs
371 */
372#define ASUS_LED(object, ledname, max) \
373 static void object##_led_set(struct led_classdev *led_cdev, \
374 enum led_brightness value); \
375 static enum led_brightness object##_led_get( \
376 struct led_classdev *led_cdev); \
75747129 377 static void object##_led_update(struct work_struct *ignored);
be4ee82d
CC
378
379ASUS_LED(mled, "mail", 1);
380ASUS_LED(tled, "touchpad", 1);
381ASUS_LED(rled, "record", 1);
382ASUS_LED(pled, "phone", 1);
383ASUS_LED(gled, "gaming", 1);
384ASUS_LED(kled, "kbd_backlight", 3);
385
be18cdab 386/* /sys/class/led handlers */
d99b577c 387#define ASUS_LED_HANDLER(object, method) \
be18cdab
CC
388 static void object##_led_set(struct led_classdev *led_cdev, \
389 enum led_brightness value) \
390 { \
9129d14d
CC
391 struct asus_laptop *asus = \
392 led_to_asus(led_cdev, object); \
393 \
394 asus->leds.object##_wk = (value > 0) ? 1 : 0; \
395 queue_work(asus->leds.workqueue, \
396 &asus->leds.object##_work); \
be18cdab 397 } \
9129d14d 398 static void object##_led_update(struct work_struct *work) \
be18cdab 399 { \
9129d14d
CC
400 struct asus_laptop *asus = work_to_asus(work, object); \
401 \
402 int value = asus->leds.object##_wk; \
d99b577c 403 asus_led_set(asus, method, value); \
abfa57e1
CC
404 } \
405 static enum led_brightness object##_led_get( \
406 struct led_classdev *led_cdev) \
407 { \
408 return led_cdev->brightness; \
be18cdab
CC
409 }
410
d99b577c
CC
411ASUS_LED_HANDLER(mled, METHOD_MLED);
412ASUS_LED_HANDLER(pled, METHOD_PLED);
413ASUS_LED_HANDLER(rled, METHOD_RLED);
414ASUS_LED_HANDLER(tled, METHOD_TLED);
415ASUS_LED_HANDLER(gled, METHOD_GLED);
be18cdab 416
b7d3fbc2 417/*
be4ee82d 418 * Keyboard backlight (also a LED)
b7d3fbc2 419 */
d99b577c 420static int asus_kled_lvl(struct asus_laptop *asus)
b7d3fbc2
CC
421{
422 unsigned long long kblv;
423 struct acpi_object_list params;
424 union acpi_object in_obj;
425 acpi_status rv;
426
427 params.count = 1;
428 params.pointer = &in_obj;
429 in_obj.type = ACPI_TYPE_INTEGER;
430 in_obj.integer.value = 2;
431
d99b577c
CC
432 rv = acpi_evaluate_integer(asus->handle, METHOD_KBD_LIGHT_GET,
433 &params, &kblv);
b7d3fbc2
CC
434 if (ACPI_FAILURE(rv)) {
435 pr_warning("Error reading kled level\n");
4d441513 436 return -ENODEV;
b7d3fbc2
CC
437 }
438 return kblv;
439}
440
4d441513 441static int asus_kled_set(struct asus_laptop *asus, int kblv)
b7d3fbc2
CC
442{
443 if (kblv > 0)
444 kblv = (1 << 7) | (kblv & 0x7F);
445 else
446 kblv = 0;
447
d99b577c 448 if (write_acpi_int(asus->handle, METHOD_KBD_LIGHT_SET, kblv)) {
b7d3fbc2
CC
449 pr_warning("Keyboard LED display write failed\n");
450 return -EINVAL;
451 }
452 return 0;
453}
454
455static void kled_led_set(struct led_classdev *led_cdev,
456 enum led_brightness value)
457{
9129d14d
CC
458 struct asus_laptop *asus = led_to_asus(led_cdev, kled);
459
460 asus->leds.kled_wk = value;
461 queue_work(asus->leds.workqueue, &asus->leds.kled_work);
b7d3fbc2
CC
462}
463
9129d14d 464static void kled_led_update(struct work_struct *work)
b7d3fbc2 465{
9129d14d
CC
466 struct asus_laptop *asus = work_to_asus(work, kled);
467
4d441513 468 asus_kled_set(asus, asus->leds.kled_wk);
b7d3fbc2
CC
469}
470
471static enum led_brightness kled_led_get(struct led_classdev *led_cdev)
472{
d99b577c
CC
473 struct asus_laptop *asus = led_to_asus(led_cdev, kled);
474
475 return asus_kled_lvl(asus);
b7d3fbc2
CC
476}
477
be4ee82d
CC
478static void asus_led_exit(struct asus_laptop *asus)
479{
75747129
CC
480 if (asus->leds.mled.dev)
481 led_classdev_unregister(&asus->leds.mled);
482 if (asus->leds.tled.dev)
483 led_classdev_unregister(&asus->leds.tled);
484 if (asus->leds.pled.dev)
485 led_classdev_unregister(&asus->leds.pled);
486 if (asus->leds.rled.dev)
487 led_classdev_unregister(&asus->leds.rled);
488 if (asus->leds.gled.dev)
489 led_classdev_unregister(&asus->leds.gled);
490 if (asus->leds.kled.dev)
491 led_classdev_unregister(&asus->leds.kled);
be4ee82d
CC
492 if (asus->leds.workqueue) {
493 destroy_workqueue(asus->leds.workqueue);
494 asus->leds.workqueue = NULL;
495 }
496}
497
498/* Ugly macro, need to fix that later */
d99b577c 499#define ASUS_LED_REGISTER(asus, object, _name, max, method) \
be4ee82d
CC
500 do { \
501 struct led_classdev *ldev = &asus->leds.object; \
d99b577c
CC
502 \
503 if (method && acpi_check_handle(asus->handle, method, NULL)) \
be4ee82d
CC
504 break ; \
505 \
506 INIT_WORK(&asus->leds.object##_work, object##_led_update); \
507 ldev->name = "asus::" _name; \
508 ldev->brightness_set = object##_led_set; \
75747129 509 ldev->brightness_get = object##_led_get; \
be4ee82d
CC
510 ldev->max_brightness = max; \
511 rv = led_classdev_register(&asus->platform_device->dev, ldev); \
512 if (rv) \
513 goto error; \
514 } while (0)
515
516static int asus_led_init(struct asus_laptop *asus)
517{
518 int rv;
519
520 /*
521 * Functions that actually update the LED's are called from a
522 * workqueue. By doing this as separate work rather than when the LED
523 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
524 * potentially bad time, such as a timer interrupt.
525 */
526 asus->leds.workqueue = create_singlethread_workqueue("led_workqueue");
527 if (!asus->leds.workqueue)
528 return -ENOMEM;
529
d99b577c
CC
530 ASUS_LED_REGISTER(asus, mled, "mail", 1, METHOD_MLED);
531 ASUS_LED_REGISTER(asus, tled, "touchpad", 1, METHOD_TLED);
532 ASUS_LED_REGISTER(asus, rled, "record", 1, METHOD_RLED);
533 ASUS_LED_REGISTER(asus, pled, "phone", 1, METHOD_PLED);
534 ASUS_LED_REGISTER(asus, gled, "gaming", 1, METHOD_GLED);
535 if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL) &&
536 !acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_GET, NULL))
537 ASUS_LED_REGISTER(asus, kled, "kbd_backlight", 3, NULL);
be4ee82d
CC
538error:
539 if (rv)
540 asus_led_exit(asus);
541 return rv;
542}
543
544/*
545 * Backlight device
546 */
3e68ae7c 547static int asus_lcd_status(struct asus_laptop *asus)
6b7091e7 548{
3e68ae7c 549 return asus->lcd_state;
6b7091e7
CC
550}
551
3e68ae7c 552static int asus_lcd_set(struct asus_laptop *asus, int value)
6b7091e7
CC
553{
554 int lcd = 0;
555 acpi_status status = 0;
556
3e68ae7c 557 lcd = !!value;
6b7091e7 558
3e68ae7c 559 if (lcd == asus_lcd_status(asus))
6b7091e7
CC
560 return 0;
561
3e68ae7c
CC
562 if (!lcd_switch_handle)
563 return -ENODEV;
564
565 status = acpi_evaluate_object(lcd_switch_handle,
566 NULL, NULL, NULL);
6b7091e7 567
3e68ae7c
CC
568 if (ACPI_FAILURE(status)) {
569 pr_warning("Error switching LCD\n");
570 return -ENODEV;
6b7091e7
CC
571 }
572
3e68ae7c 573 asus->lcd_state = lcd;
6b7091e7
CC
574 return 0;
575}
576
9129d14d 577static void lcd_blank(struct asus_laptop *asus, int blank)
6b7091e7 578{
7c247645 579 struct backlight_device *bd = asus->backlight_device;
6b7091e7 580
3e68ae7c
CC
581 asus->lcd_state = (blank == FB_BLANK_UNBLANK);
582
8def05fa 583 if (bd) {
599a52d1 584 bd->props.power = blank;
28ee086d 585 backlight_update_status(bd);
6b7091e7
CC
586 }
587}
588
4d441513 589static int asus_read_brightness(struct backlight_device *bd)
6b7091e7 590{
d99b577c 591 struct asus_laptop *asus = bl_get_data(bd);
27663c58 592 unsigned long long value;
9a816850 593 acpi_status rv = AE_OK;
6b7091e7 594
d99b577c
CC
595 rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET,
596 NULL, &value);
9a816850 597 if (ACPI_FAILURE(rv))
2fcc23da 598 pr_warning("Error reading brightness\n");
6b7091e7
CC
599
600 return value;
601}
602
4d441513 603static int asus_set_brightness(struct backlight_device *bd, int value)
6b7091e7 604{
d99b577c
CC
605 struct asus_laptop *asus = bl_get_data(bd);
606
607 if (write_acpi_int(asus->handle, METHOD_BRIGHTNESS_SET, value)) {
2fcc23da 608 pr_warning("Error changing brightness\n");
e5b50f6a 609 return -EIO;
6b7091e7 610 }
e5b50f6a 611 return 0;
6b7091e7
CC
612}
613
614static int update_bl_status(struct backlight_device *bd)
615{
9129d14d 616 struct asus_laptop *asus = bl_get_data(bd);
6b7091e7 617 int rv;
599a52d1 618 int value = bd->props.brightness;
6b7091e7 619
4d441513 620 rv = asus_set_brightness(bd, value);
8def05fa 621 if (rv)
6b7091e7
CC
622 return rv;
623
599a52d1 624 value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0;
3e68ae7c 625 return asus_lcd_set(asus, value);
6b7091e7
CC
626}
627
be4ee82d 628static struct backlight_ops asusbl_ops = {
4d441513 629 .get_brightness = asus_read_brightness,
be4ee82d
CC
630 .update_status = update_bl_status,
631};
632
a539df5e
CC
633static int asus_backlight_notify(struct asus_laptop *asus)
634{
635 struct backlight_device *bd = asus->backlight_device;
636 int old = bd->props.brightness;
637
638 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
639
640 return old;
641}
642
be4ee82d
CC
643static int asus_backlight_init(struct asus_laptop *asus)
644{
645 struct backlight_device *bd;
646 struct device *dev = &asus->platform_device->dev;
647
d99b577c
CC
648 if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) &&
649 !acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) &&
650 lcd_switch_handle) {
be4ee82d
CC
651 bd = backlight_device_register(ASUS_LAPTOP_FILE, dev,
652 asus, &asusbl_ops);
653 if (IS_ERR(bd)) {
654 pr_err("Could not register asus backlight device\n");
655 asus->backlight_device = NULL;
656 return PTR_ERR(bd);
657 }
658
659 asus->backlight_device = bd;
660
661 bd->props.max_brightness = 15;
be4ee82d 662 bd->props.power = FB_BLANK_UNBLANK;
d99b577c 663 bd->props.brightness = asus_read_brightness(bd);
be4ee82d
CC
664 backlight_update_status(bd);
665 }
666 return 0;
667}
668
669static void asus_backlight_exit(struct asus_laptop *asus)
670{
671 if (asus->backlight_device)
672 backlight_device_unregister(asus->backlight_device);
a539df5e 673 asus->backlight_device = NULL;
be4ee82d
CC
674}
675
85091b71
CC
676/*
677 * Platform device handlers
678 */
679
680/*
681 * We write our info in page, we begin at offset off and cannot write more
682 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
683 * number of bytes written in page
684 */
685static ssize_t show_infos(struct device *dev,
8def05fa 686 struct device_attribute *attr, char *page)
85091b71 687{
9129d14d 688 struct asus_laptop *asus = dev_get_drvdata(dev);
85091b71 689 int len = 0;
27663c58 690 unsigned long long temp;
be966660 691 char buf[16]; /* enough for all info */
9a816850
CC
692 acpi_status rv = AE_OK;
693
85091b71
CC
694 /*
695 * We use the easy way, we don't care of off and count, so we don't set eof
696 * to 1
697 */
698
50a90c4d
CC
699 len += sprintf(page, ASUS_LAPTOP_NAME " " ASUS_LAPTOP_VERSION "\n");
700 len += sprintf(page + len, "Model reference : %s\n", asus->name);
85091b71
CC
701 /*
702 * The SFUN method probably allows the original driver to get the list
703 * of features supported by a given model. For now, 0x0100 or 0x0800
704 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
705 * The significance of others is yet to be found.
706 */
50a90c4d 707 rv = acpi_evaluate_integer(asus->handle, "SFUN", NULL, &temp);
9a816850 708 if (!ACPI_FAILURE(rv))
1d4a3800
CC
709 len += sprintf(page + len, "SFUN value : %#x\n",
710 (uint) temp);
711 /*
712 * The HWRS method return informations about the hardware.
713 * 0x80 bit is for WLAN, 0x100 for Bluetooth.
714 * The significance of others is yet to be found.
715 * If we don't find the method, we assume the device are present.
716 */
50a90c4d 717 rv = acpi_evaluate_integer(asus->handle, "HRWS", NULL, &temp);
1d4a3800
CC
718 if (!ACPI_FAILURE(rv))
719 len += sprintf(page + len, "HRWS value : %#x\n",
9a816850 720 (uint) temp);
85091b71
CC
721 /*
722 * Another value for userspace: the ASYM method returns 0x02 for
723 * battery low and 0x04 for battery critical, its readings tend to be
724 * more accurate than those provided by _BST.
725 * Note: since not all the laptops provide this method, errors are
726 * silently ignored.
727 */
50a90c4d 728 rv = acpi_evaluate_integer(asus->handle, "ASYM", NULL, &temp);
9a816850 729 if (!ACPI_FAILURE(rv))
1d4a3800 730 len += sprintf(page + len, "ASYM value : %#x\n",
9a816850 731 (uint) temp);
7c247645
CC
732 if (asus->dsdt_info) {
733 snprintf(buf, 16, "%d", asus->dsdt_info->length);
85091b71 734 len += sprintf(page + len, "DSDT length : %s\n", buf);
7c247645 735 snprintf(buf, 16, "%d", asus->dsdt_info->checksum);
85091b71 736 len += sprintf(page + len, "DSDT checksum : %s\n", buf);
7c247645 737 snprintf(buf, 16, "%d", asus->dsdt_info->revision);
85091b71 738 len += sprintf(page + len, "DSDT revision : %s\n", buf);
7c247645 739 snprintf(buf, 7, "%s", asus->dsdt_info->oem_id);
85091b71 740 len += sprintf(page + len, "OEM id : %s\n", buf);
7c247645 741 snprintf(buf, 9, "%s", asus->dsdt_info->oem_table_id);
85091b71 742 len += sprintf(page + len, "OEM table id : %s\n", buf);
7c247645 743 snprintf(buf, 16, "%x", asus->dsdt_info->oem_revision);
85091b71 744 len += sprintf(page + len, "OEM revision : 0x%s\n", buf);
7c247645 745 snprintf(buf, 5, "%s", asus->dsdt_info->asl_compiler_id);
85091b71 746 len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
7c247645 747 snprintf(buf, 16, "%x", asus->dsdt_info->asl_compiler_revision);
85091b71
CC
748 len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf);
749 }
750
751 return len;
752}
753
754static int parse_arg(const char *buf, unsigned long count, int *val)
755{
756 if (!count)
757 return 0;
758 if (count > 31)
759 return -EINVAL;
760 if (sscanf(buf, "%i", val) != 1)
761 return -EINVAL;
762 return count;
763}
764
17e78f62
CC
765static ssize_t sysfs_acpi_set(struct asus_laptop *asus,
766 const char *buf, size_t count,
d99b577c 767 const char *method)
4564de17
CC
768{
769 int rv, value;
770 int out = 0;
771
772 rv = parse_arg(buf, count, &value);
773 if (rv > 0)
774 out = value ? 1 : 0;
775
d99b577c 776 if (write_acpi_int(asus->handle, method, value))
17e78f62 777 return -ENODEV;
4564de17
CC
778 return rv;
779}
780
722ad971
CC
781/*
782 * LEDD display
783 */
784static ssize_t show_ledd(struct device *dev,
785 struct device_attribute *attr, char *buf)
786{
9129d14d
CC
787 struct asus_laptop *asus = dev_get_drvdata(dev);
788
50a90c4d 789 return sprintf(buf, "0x%08x\n", asus->ledd_status);
722ad971
CC
790}
791
792static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
793 const char *buf, size_t count)
794{
9129d14d 795 struct asus_laptop *asus = dev_get_drvdata(dev);
722ad971
CC
796 int rv, value;
797
798 rv = parse_arg(buf, count, &value);
799 if (rv > 0) {
d99b577c 800 if (write_acpi_int(asus->handle, METHOD_LEDD, value))
2fcc23da 801 pr_warning("LED display write failed\n");
722ad971 802 else
50a90c4d 803 asus->ledd_status = (u32) value;
722ad971
CC
804 }
805 return rv;
806}
807
aa9df930
CC
808/*
809 * Wireless
810 */
811static int asus_wireless_status(struct asus_laptop *asus, int mask)
812{
813 unsigned long long status;
814 acpi_status rv = AE_OK;
815
816 if (!asus->have_rsts)
817 return (asus->wireless_status & mask) ? 1 : 0;
818
d99b577c
CC
819 rv = acpi_evaluate_integer(asus->handle, METHOD_WL_STATUS,
820 NULL, &status);
aa9df930
CC
821 if (ACPI_FAILURE(rv)) {
822 pr_warning("Error reading Wireless status\n");
823 return -EINVAL;
824 }
825 return !!(status & mask);
826}
827
4564de17
CC
828/*
829 * WLAN
830 */
e5593bf1
CC
831static int asus_wlan_set(struct asus_laptop *asus, int status)
832{
833 if (write_acpi_int(asus->handle, METHOD_WLAN, !!status)) {
834 pr_warning("Error setting wlan status to %d", status);
835 return -EIO;
836 }
837 return 0;
838}
839
4564de17
CC
840static ssize_t show_wlan(struct device *dev,
841 struct device_attribute *attr, char *buf)
842{
9129d14d
CC
843 struct asus_laptop *asus = dev_get_drvdata(dev);
844
17e78f62 845 return sprintf(buf, "%d\n", asus_wireless_status(asus, WL_RSTS));
4564de17
CC
846}
847
848static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
849 const char *buf, size_t count)
850{
9129d14d
CC
851 struct asus_laptop *asus = dev_get_drvdata(dev);
852
d99b577c 853 return sysfs_acpi_set(asus, buf, count, METHOD_WLAN);
4564de17
CC
854}
855
856/*
857 * Bluetooth
858 */
e5593bf1
CC
859static int asus_bluetooth_set(struct asus_laptop *asus, int status)
860{
861 if (write_acpi_int(asus->handle, METHOD_BLUETOOTH, !!status)) {
862 pr_warning("Error setting bluetooth status to %d", status);
863 return -EIO;
864 }
865 return 0;
866}
867
4564de17
CC
868static ssize_t show_bluetooth(struct device *dev,
869 struct device_attribute *attr, char *buf)
870{
9129d14d
CC
871 struct asus_laptop *asus = dev_get_drvdata(dev);
872
17e78f62 873 return sprintf(buf, "%d\n", asus_wireless_status(asus, BT_RSTS));
4564de17
CC
874}
875
8def05fa
LB
876static ssize_t store_bluetooth(struct device *dev,
877 struct device_attribute *attr, const char *buf,
878 size_t count)
4564de17 879{
9129d14d
CC
880 struct asus_laptop *asus = dev_get_drvdata(dev);
881
d99b577c 882 return sysfs_acpi_set(asus, buf, count, METHOD_BLUETOOTH);
4564de17
CC
883}
884
78127b4a
CC
885/*
886 * Display
887 */
4d441513 888static void asus_set_display(struct asus_laptop *asus, int value)
78127b4a
CC
889{
890 /* no sanity check needed for now */
d99b577c 891 if (write_acpi_int(asus->handle, METHOD_SWITCH_DISPLAY, value))
2fcc23da 892 pr_warning("Error setting display\n");
78127b4a
CC
893 return;
894}
895
9129d14d 896static int read_display(struct asus_laptop *asus)
78127b4a 897{
27663c58 898 unsigned long long value = 0;
9a816850 899 acpi_status rv = AE_OK;
78127b4a 900
be966660
CC
901 /*
902 * In most of the case, we know how to set the display, but sometime
903 * we can't read it
904 */
8def05fa 905 if (display_get_handle) {
9a816850
CC
906 rv = acpi_evaluate_integer(display_get_handle, NULL,
907 NULL, &value);
908 if (ACPI_FAILURE(rv))
2fcc23da 909 pr_warning("Error reading display status\n");
78127b4a
CC
910 }
911
912 value &= 0x0F; /* needed for some models, shouldn't hurt others */
913
914 return value;
915}
8def05fa 916
78127b4a
CC
917/*
918 * Now, *this* one could be more user-friendly, but so far, no-one has
919 * complained. The significance of bits is the same as in store_disp()
920 */
921static ssize_t show_disp(struct device *dev,
922 struct device_attribute *attr, char *buf)
923{
9129d14d
CC
924 struct asus_laptop *asus = dev_get_drvdata(dev);
925
926 return sprintf(buf, "%d\n", read_display(asus));
78127b4a
CC
927}
928
929/*
930 * Experimental support for display switching. As of now: 1 should activate
931 * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
932 * Any combination (bitwise) of these will suffice. I never actually tested 4
933 * displays hooked up simultaneously, so be warned. See the acpi4asus README
934 * for more info.
935 */
936static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
937 const char *buf, size_t count)
938{
9129d14d 939 struct asus_laptop *asus = dev_get_drvdata(dev);
78127b4a
CC
940 int rv, value;
941
942 rv = parse_arg(buf, count, &value);
943 if (rv > 0)
4d441513 944 asus_set_display(asus, value);
78127b4a
CC
945 return rv;
946}
947
8b857353
CC
948/*
949 * Light Sens
950 */
4d441513 951static void asus_als_switch(struct asus_laptop *asus, int value)
8b857353 952{
d99b577c 953 if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value))
2fcc23da 954 pr_warning("Error setting light sensor switch\n");
50a90c4d 955 asus->light_switch = value;
8b857353
CC
956}
957
958static ssize_t show_lssw(struct device *dev,
959 struct device_attribute *attr, char *buf)
960{
9129d14d
CC
961 struct asus_laptop *asus = dev_get_drvdata(dev);
962
50a90c4d 963 return sprintf(buf, "%d\n", asus->light_switch);
8b857353
CC
964}
965
966static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
967 const char *buf, size_t count)
968{
9129d14d 969 struct asus_laptop *asus = dev_get_drvdata(dev);
8b857353
CC
970 int rv, value;
971
972 rv = parse_arg(buf, count, &value);
973 if (rv > 0)
4d441513 974 asus_als_switch(asus, value ? 1 : 0);
8b857353
CC
975
976 return rv;
977}
978
4d441513 979static void asus_als_level(struct asus_laptop *asus, int value)
8b857353 980{
d99b577c 981 if (write_acpi_int(asus->handle, METHOD_ALS_LEVEL, value))
2fcc23da 982 pr_warning("Error setting light sensor level\n");
50a90c4d 983 asus->light_level = value;
8b857353
CC
984}
985
986static ssize_t show_lslvl(struct device *dev,
987 struct device_attribute *attr, char *buf)
988{
9129d14d
CC
989 struct asus_laptop *asus = dev_get_drvdata(dev);
990
50a90c4d 991 return sprintf(buf, "%d\n", asus->light_level);
8b857353
CC
992}
993
994static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,
995 const char *buf, size_t count)
996{
9129d14d 997 struct asus_laptop *asus = dev_get_drvdata(dev);
8b857353
CC
998 int rv, value;
999
1000 rv = parse_arg(buf, count, &value);
1001 if (rv > 0) {
1002 value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
1003 /* 0 <= value <= 15 */
4d441513 1004 asus_als_level(asus, value);
8b857353
CC
1005 }
1006
1007 return rv;
1008}
1009
e539c2f6
CC
1010/*
1011 * GPS
1012 */
6358bf2c
CC
1013static int asus_gps_status(struct asus_laptop *asus)
1014{
1015 unsigned long long status;
1016 acpi_status rv = AE_OK;
1017
d99b577c
CC
1018 rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS,
1019 NULL, &status);
6358bf2c
CC
1020 if (ACPI_FAILURE(rv)) {
1021 pr_warning("Error reading GPS status\n");
1022 return -ENODEV;
1023 }
1024 return !!status;
1025}
1026
1027static int asus_gps_switch(struct asus_laptop *asus, int status)
1028{
d99b577c 1029 const char *meth = status ? METHOD_GPS_ON : METHOD_GPS_OFF;
6358bf2c 1030
d99b577c 1031 if (write_acpi_int(asus->handle, meth, 0x02))
6358bf2c
CC
1032 return -ENODEV;
1033 return 0;
1034}
1035
e539c2f6
CC
1036static ssize_t show_gps(struct device *dev,
1037 struct device_attribute *attr, char *buf)
1038{
9129d14d
CC
1039 struct asus_laptop *asus = dev_get_drvdata(dev);
1040
6358bf2c 1041 return sprintf(buf, "%d\n", asus_gps_status(asus));
e539c2f6
CC
1042}
1043
1044static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
1045 const char *buf, size_t count)
1046{
6358bf2c
CC
1047 struct asus_laptop *asus = dev_get_drvdata(dev);
1048 int rv, value;
1049 int ret;
9129d14d 1050
6358bf2c
CC
1051 rv = parse_arg(buf, count, &value);
1052 if (rv <= 0)
1053 return -EINVAL;
1054 ret = asus_gps_switch(asus, !!value);
1055 if (ret)
1056 return ret;
18e1311e 1057 rfkill_set_sw_state(asus->gps_rfkill, !value);
6358bf2c 1058 return rv;
e539c2f6
CC
1059}
1060
18e1311e
CC
1061/*
1062 * rfkill
1063 */
1064static int asus_gps_rfkill_set(void *data, bool blocked)
1065{
1066 acpi_handle handle = data;
1067
1068 return asus_gps_switch(handle, !blocked);
1069}
1070
1071static const struct rfkill_ops asus_gps_rfkill_ops = {
1072 .set_block = asus_gps_rfkill_set,
1073};
1074
1075static void asus_rfkill_exit(struct asus_laptop *asus)
1076{
1077 if (asus->gps_rfkill) {
1078 rfkill_unregister(asus->gps_rfkill);
1079 rfkill_destroy(asus->gps_rfkill);
1080 asus->gps_rfkill = NULL;
1081 }
1082}
1083
1084static int asus_rfkill_init(struct asus_laptop *asus)
1085{
1086 int result;
1087
1088 if (acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) ||
1089 acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) ||
1090 acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL))
1091 return 0;
1092
1093 asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev,
1094 RFKILL_TYPE_GPS,
1095 &asus_gps_rfkill_ops, NULL);
1096 if (!asus->gps_rfkill)
1097 return -EINVAL;
1098
1099 result = rfkill_register(asus->gps_rfkill);
1100 if (result) {
1101 rfkill_destroy(asus->gps_rfkill);
1102 asus->gps_rfkill = NULL;
1103 }
1104
1105 return result;
1106}
1107
034ce90a 1108/*
be4ee82d 1109 * Input device (i.e. hotkeys)
034ce90a 1110 */
be4ee82d
CC
1111static void asus_input_notify(struct asus_laptop *asus, int event)
1112{
66a71dd1
CC
1113 if (asus->inputdev)
1114 sparse_keymap_report_event(asus->inputdev, event, 1, true);
be4ee82d
CC
1115}
1116
1117static int asus_input_init(struct asus_laptop *asus)
1118{
66a71dd1
CC
1119 struct input_dev *input;
1120 int error;
be4ee82d 1121
66a71dd1
CC
1122 input = input_allocate_device();
1123 if (!input) {
be4ee82d
CC
1124 pr_info("Unable to allocate input device\n");
1125 return 0;
1126 }
66a71dd1
CC
1127 input->name = "Asus Laptop extra buttons";
1128 input->phys = ASUS_LAPTOP_FILE "/input0";
1129 input->id.bustype = BUS_HOST;
1130 input->dev.parent = &asus->platform_device->dev;
1131 input_set_drvdata(input, asus);
1132
1133 error = sparse_keymap_setup(input, asus_keymap, NULL);
1134 if (error) {
1135 pr_err("Unable to setup input device keymap\n");
1136 goto err_keymap;
be4ee82d 1137 }
66a71dd1
CC
1138 error = input_register_device(input);
1139 if (error) {
be4ee82d 1140 pr_info("Unable to register input device\n");
66a71dd1 1141 goto err_device;
be4ee82d 1142 }
66a71dd1
CC
1143
1144 asus->inputdev = input;
1145 return 0;
1146
1147err_keymap:
1148 sparse_keymap_free(input);
1149err_device:
1150 input_free_device(input);
1151 return error;
be4ee82d
CC
1152}
1153
1154static void asus_input_exit(struct asus_laptop *asus)
1155{
66a71dd1
CC
1156 if (asus->inputdev) {
1157 sparse_keymap_free(asus->inputdev);
be4ee82d 1158 input_unregister_device(asus->inputdev);
66a71dd1 1159 }
be4ee82d
CC
1160}
1161
1162/*
1163 * ACPI driver
1164 */
600ad520 1165static void asus_acpi_notify(struct acpi_device *device, u32 event)
85091b71 1166{
9129d14d 1167 struct asus_laptop *asus = acpi_driver_data(device);
6050c8dd 1168 u16 count;
034ce90a 1169
6b7091e7
CC
1170 /*
1171 * We need to tell the backlight device when the backlight power is
1172 * switched
1173 */
3e68ae7c 1174 if (event == ATKD_LCD_ON)
9129d14d 1175 lcd_blank(asus, FB_BLANK_UNBLANK);
3e68ae7c 1176 else if (event == ATKD_LCD_OFF)
9129d14d 1177 lcd_blank(asus, FB_BLANK_POWERDOWN);
6b7091e7 1178
619d8b11 1179 /* TODO Find a better way to handle events count. */
50a90c4d
CC
1180 count = asus->event_count[event % 128]++;
1181 acpi_bus_generate_proc_event(asus->device, event, count);
1182 acpi_bus_generate_netlink_event(asus->device->pnp.device_class,
1183 dev_name(&asus->device->dev), event,
6050c8dd 1184 count);
85091b71 1185
a539df5e
CC
1186 /* Brightness events are special */
1187 if (event >= ATKD_BR_MIN && event <= ATKD_BR_MAX) {
1188
1189 /* Ignore them completely if the acpi video driver is used */
1190 if (asus->backlight_device != NULL) {
1191 /* Update the backlight device. */
1192 asus_backlight_notify(asus);
1193 }
1194 return ;
1195 }
be4ee82d 1196 asus_input_notify(asus, event);
85091b71
CC
1197}
1198
1199#define ASUS_CREATE_DEVICE_ATTR(_name) \
1200 struct device_attribute dev_attr_##_name = { \
1201 .attr = { \
1202 .name = __stringify(_name), \
7b595756 1203 .mode = 0 }, \
85091b71
CC
1204 .show = NULL, \
1205 .store = NULL, \
1206 }
1207
1208#define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store) \
1209 do { \
1210 dev_attr_##_name.attr.mode = _mode; \
1211 dev_attr_##_name.show = _show; \
1212 dev_attr_##_name.store = _store; \
1213 } while(0)
1214
1215static ASUS_CREATE_DEVICE_ATTR(infos);
4564de17
CC
1216static ASUS_CREATE_DEVICE_ATTR(wlan);
1217static ASUS_CREATE_DEVICE_ATTR(bluetooth);
78127b4a 1218static ASUS_CREATE_DEVICE_ATTR(display);
722ad971 1219static ASUS_CREATE_DEVICE_ATTR(ledd);
8b857353
CC
1220static ASUS_CREATE_DEVICE_ATTR(ls_switch);
1221static ASUS_CREATE_DEVICE_ATTR(ls_level);
e539c2f6 1222static ASUS_CREATE_DEVICE_ATTR(gps);
85091b71
CC
1223
1224static struct attribute *asuspf_attributes[] = {
8def05fa
LB
1225 &dev_attr_infos.attr,
1226 &dev_attr_wlan.attr,
1227 &dev_attr_bluetooth.attr,
1228 &dev_attr_display.attr,
1229 &dev_attr_ledd.attr,
1230 &dev_attr_ls_switch.attr,
1231 &dev_attr_ls_level.attr,
e539c2f6 1232 &dev_attr_gps.attr,
8def05fa 1233 NULL
85091b71
CC
1234};
1235
600ad520 1236static struct attribute_group platform_attribute_group = {
8def05fa 1237 .attrs = asuspf_attributes
85091b71
CC
1238};
1239
9129d14d 1240static int asus_platform_init(struct asus_laptop *asus)
600ad520
CC
1241{
1242 int result;
1243
50a90c4d
CC
1244 asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1);
1245 if (!asus->platform_device)
600ad520 1246 return -ENOMEM;
9129d14d 1247 platform_set_drvdata(asus->platform_device, asus);
600ad520 1248
50a90c4d 1249 result = platform_device_add(asus->platform_device);
600ad520
CC
1250 if (result)
1251 goto fail_platform_device;
1252
50a90c4d 1253 result = sysfs_create_group(&asus->platform_device->dev.kobj,
600ad520
CC
1254 &platform_attribute_group);
1255 if (result)
1256 goto fail_sysfs;
1257 return 0;
1258
1259fail_sysfs:
50a90c4d 1260 platform_device_del(asus->platform_device);
600ad520 1261fail_platform_device:
50a90c4d 1262 platform_device_put(asus->platform_device);
600ad520
CC
1263 return result;
1264}
1265
9129d14d 1266static void asus_platform_exit(struct asus_laptop *asus)
600ad520 1267{
50a90c4d 1268 sysfs_remove_group(&asus->platform_device->dev.kobj,
600ad520 1269 &platform_attribute_group);
50a90c4d 1270 platform_device_unregister(asus->platform_device);
600ad520
CC
1271}
1272
1273static struct platform_driver platform_driver = {
8def05fa 1274 .driver = {
9129d14d
CC
1275 .name = ASUS_LAPTOP_FILE,
1276 .owner = THIS_MODULE,
1277 }
85091b71
CC
1278};
1279
9129d14d 1280static void asus_laptop_add_fs(struct asus_laptop *asus)
85091b71
CC
1281{
1282 ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL);
4564de17 1283
d99b577c 1284 if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL))
4564de17
CC
1285 ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan);
1286
d99b577c 1287 if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL))
4564de17
CC
1288 ASUS_SET_DEVICE_ATTR(bluetooth, 0644,
1289 show_bluetooth, store_bluetooth);
78127b4a 1290
d99b577c
CC
1291 if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) {
1292 if (display_get_handle)
1293 ASUS_SET_DEVICE_ATTR(display, 0644, show_disp,
1294 store_disp);
1295 else
1296 ASUS_SET_DEVICE_ATTR(display, 0200, NULL, store_disp);
1297 }
78127b4a 1298
d99b577c 1299 if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL))
722ad971
CC
1300 ASUS_SET_DEVICE_ATTR(ledd, 0644, show_ledd, store_ledd);
1301
d99b577c
CC
1302 if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
1303 !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
8b857353
CC
1304 ASUS_SET_DEVICE_ATTR(ls_level, 0644, show_lslvl, store_lslvl);
1305 ASUS_SET_DEVICE_ATTR(ls_switch, 0644, show_lssw, store_lssw);
1306 }
e539c2f6 1307
d99b577c
CC
1308 if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) &&
1309 !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) &&
1310 !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL))
e539c2f6 1311 ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps);
85091b71
CC
1312}
1313
8def05fa 1314static int asus_handle_init(char *name, acpi_handle * handle,
85091b71
CC
1315 char **paths, int num_paths)
1316{
1317 int i;
1318 acpi_status status;
1319
1320 for (i = 0; i < num_paths; i++) {
1321 status = acpi_get_handle(NULL, paths[i], handle);
1322 if (ACPI_SUCCESS(status))
1323 return 0;
1324 }
1325
1326 *handle = NULL;
1327 return -ENODEV;
1328}
1329
1330#define ASUS_HANDLE_INIT(object) \
1331 asus_handle_init(#object, &object##_handle, object##_paths, \
1332 ARRAY_SIZE(object##_paths))
1333
85091b71 1334/*
50a90c4d
CC
1335 * This function is used to initialize the context with right values. In this
1336 * method, we can make all the detection we want, and modify the asus_laptop
1337 * struct
85091b71 1338 */
7c247645 1339static int asus_laptop_get_info(struct asus_laptop *asus)
85091b71
CC
1340{
1341 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
85091b71 1342 union acpi_object *model = NULL;
27663c58 1343 unsigned long long bsts_result, hwrs_result;
85091b71
CC
1344 char *string = NULL;
1345 acpi_status status;
1346
1347 /*
1348 * Get DSDT headers early enough to allow for differentiating between
1349 * models, but late enough to allow acpi_bus_register_driver() to fail
1350 * before doing anything ACPI-specific. Should we encounter a machine,
1351 * which needs special handling (i.e. its hotkey device has a different
7c247645 1352 * HID), this bit will be moved.
85091b71 1353 */
7c247645 1354 status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus->dsdt_info);
85091b71 1355 if (ACPI_FAILURE(status))
2fcc23da 1356 pr_warning("Couldn't get the DSDT table header\n");
85091b71
CC
1357
1358 /* We have to write 0 on init this far for all ASUS models */
50a90c4d 1359 if (write_acpi_int_ret(asus->handle, "INIT", 0, &buffer)) {
2fcc23da 1360 pr_err("Hotkey initialization failed\n");
85091b71
CC
1361 return -ENODEV;
1362 }
1363
1364 /* This needs to be called for some laptops to init properly */
9a816850 1365 status =
50a90c4d 1366 acpi_evaluate_integer(asus->handle, "BSTS", NULL, &bsts_result);
9a816850 1367 if (ACPI_FAILURE(status))
2fcc23da 1368 pr_warning("Error calling BSTS\n");
85091b71 1369 else if (bsts_result)
2fcc23da 1370 pr_notice("BSTS called, 0x%02x returned\n",
9a816850 1371 (uint) bsts_result);
85091b71 1372
185e5af9 1373 /* This too ... */
e5593bf1
CC
1374 if (write_acpi_int(asus->handle, "CWAP", wapf))
1375 pr_err("Error calling CWAP(%d)\n", wapf);
85091b71
CC
1376 /*
1377 * Try to match the object returned by INIT to the specific model.
1378 * Handle every possible object (or the lack of thereof) the DSDT
1379 * writers might throw at us. When in trouble, we pass NULL to
1380 * asus_model_match() and try something completely different.
1381 */
1382 if (buffer.pointer) {
1383 model = buffer.pointer;
1384 switch (model->type) {
1385 case ACPI_TYPE_STRING:
1386 string = model->string.pointer;
1387 break;
1388 case ACPI_TYPE_BUFFER:
1389 string = model->buffer.pointer;
1390 break;
1391 default:
1392 string = "";
1393 break;
1394 }
1395 }
50a90c4d
CC
1396 asus->name = kstrdup(string, GFP_KERNEL);
1397 if (!asus->name)
85091b71
CC
1398 return -ENOMEM;
1399
8def05fa 1400 if (*string)
2fcc23da 1401 pr_notice(" %s model detected\n", string);
85091b71 1402
4564de17
CC
1403 /*
1404 * The HWRS method return informations about the hardware.
1405 * 0x80 bit is for WLAN, 0x100 for Bluetooth.
1406 * The significance of others is yet to be found.
4564de17 1407 */
9a816850 1408 status =
50a90c4d 1409 acpi_evaluate_integer(asus->handle, "HRWS", NULL, &hwrs_result);
d99b577c
CC
1410 if (!ACPI_FAILURE(status))
1411 pr_notice(" HRWS returned %x", (int)hwrs_result);
4564de17 1412
d99b577c 1413 if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL))
aa9df930 1414 asus->have_rsts = true;
4564de17 1415
d99b577c 1416 /* Scheduled for removal */
6b7091e7 1417 ASUS_HANDLE_INIT(lcd_switch);
78127b4a
CC
1418 ASUS_HANDLE_INIT(display_get);
1419
85091b71
CC
1420 kfree(model);
1421
1422 return AE_OK;
1423}
1424
600ad520
CC
1425static bool asus_device_present;
1426
9129d14d 1427static int __devinit asus_acpi_init(struct asus_laptop *asus)
85091b71 1428{
600ad520 1429 int result = 0;
85091b71 1430
50a90c4d 1431 result = acpi_bus_get_status(asus->device);
600ad520 1432 if (result)
85091b71 1433 return result;
50a90c4d 1434 if (!asus->device->status.present) {
600ad520 1435 pr_err("Hotkey device not present, aborting\n");
85091b71
CC
1436 return -ENODEV;
1437 }
1438
7c247645 1439 result = asus_laptop_get_info(asus);
034ce90a 1440 if (result)
600ad520 1441 return result;
034ce90a 1442
9129d14d 1443 asus_laptop_add_fs(asus);
85091b71 1444
600ad520 1445 /* WLED and BLED are on by default */
be4ee82d 1446 if (bluetooth_status >= 0)
e5593bf1
CC
1447 asus_bluetooth_set(asus, !!bluetooth_status);
1448
d0930a2d
CC
1449 if (wlan_status >= 0)
1450 asus_wlan_set(asus, !!wlan_status);
85091b71 1451
600ad520 1452 /* Keyboard Backlight is on by default */
d99b577c 1453 if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL))
4d441513 1454 asus_kled_set(asus, 1);
600ad520
CC
1455
1456 /* LED display is off by default */
50a90c4d 1457 asus->ledd_status = 0xFFF;
600ad520
CC
1458
1459 /* Set initial values of light sensor and level */
be4ee82d
CC
1460 asus->light_switch = 0; /* Default to light sensor disabled */
1461 asus->light_level = 5; /* level 5 for sensor sensitivity */
600ad520 1462
d99b577c
CC
1463 if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
1464 !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
4d441513 1465 asus_als_switch(asus, asus->light_switch);
4d441513 1466 asus_als_level(asus, asus->light_level);
d99b577c 1467 }
600ad520 1468
47ee0e99 1469 asus->lcd_state = 1; /* LCD should be on when the module load */
600ad520
CC
1470 return result;
1471}
1472
1473static int __devinit asus_acpi_add(struct acpi_device *device)
1474{
9129d14d 1475 struct asus_laptop *asus;
600ad520
CC
1476 int result;
1477
1478 pr_notice("Asus Laptop Support version %s\n",
1479 ASUS_LAPTOP_VERSION);
50a90c4d
CC
1480 asus = kzalloc(sizeof(struct asus_laptop), GFP_KERNEL);
1481 if (!asus)
600ad520 1482 return -ENOMEM;
50a90c4d
CC
1483 asus->handle = device->handle;
1484 strcpy(acpi_device_name(device), ASUS_LAPTOP_DEVICE_NAME);
1485 strcpy(acpi_device_class(device), ASUS_LAPTOP_CLASS);
1486 device->driver_data = asus;
1487 asus->device = device;
600ad520 1488
9129d14d 1489 result = asus_acpi_init(asus);
8def05fa 1490 if (result)
600ad520 1491 goto fail_platform;
85091b71 1492
600ad520
CC
1493 /*
1494 * Register the platform device first. It is used as a parent for the
1495 * sub-devices below.
1496 */
9129d14d 1497 result = asus_platform_init(asus);
116bf2e0 1498 if (result)
600ad520 1499 goto fail_platform;
116bf2e0
CC
1500
1501 if (!acpi_video_backlight_support()) {
9129d14d 1502 result = asus_backlight_init(asus);
116bf2e0
CC
1503 if (result)
1504 goto fail_backlight;
1505 } else
600ad520 1506 pr_info("Backlight controlled by ACPI video driver\n");
116bf2e0 1507
9129d14d 1508 result = asus_input_init(asus);
600ad520
CC
1509 if (result)
1510 goto fail_input;
1511
9129d14d 1512 result = asus_led_init(asus);
600ad520
CC
1513 if (result)
1514 goto fail_led;
1515
18e1311e
CC
1516 result = asus_rfkill_init(asus);
1517 if (result)
1518 goto fail_rfkill;
1519
600ad520 1520 asus_device_present = true;
8def05fa 1521 return 0;
85091b71 1522
18e1311e
CC
1523fail_rfkill:
1524 asus_led_exit(asus);
600ad520 1525fail_led:
9129d14d 1526 asus_input_exit(asus);
600ad520 1527fail_input:
9129d14d 1528 asus_backlight_exit(asus);
116bf2e0 1529fail_backlight:
9129d14d 1530 asus_platform_exit(asus);
600ad520 1531fail_platform:
50a90c4d
CC
1532 kfree(asus->name);
1533 kfree(asus);
116bf2e0 1534
600ad520
CC
1535 return result;
1536}
116bf2e0 1537
600ad520
CC
1538static int asus_acpi_remove(struct acpi_device *device, int type)
1539{
9129d14d
CC
1540 struct asus_laptop *asus = acpi_driver_data(device);
1541
1542 asus_backlight_exit(asus);
18e1311e 1543 asus_rfkill_exit(asus);
9129d14d
CC
1544 asus_led_exit(asus);
1545 asus_input_exit(asus);
1546 asus_platform_exit(asus);
600ad520 1547
50a90c4d
CC
1548 kfree(asus->name);
1549 kfree(asus);
600ad520
CC
1550 return 0;
1551}
1552
1553static const struct acpi_device_id asus_device_ids[] = {
1554 {"ATK0100", 0},
1555 {"ATK0101", 0},
1556 {"", 0},
1557};
1558MODULE_DEVICE_TABLE(acpi, asus_device_ids);
85091b71 1559
600ad520 1560static struct acpi_driver asus_acpi_driver = {
50a90c4d
CC
1561 .name = ASUS_LAPTOP_NAME,
1562 .class = ASUS_LAPTOP_CLASS,
600ad520
CC
1563 .owner = THIS_MODULE,
1564 .ids = asus_device_ids,
1565 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
1566 .ops = {
1567 .add = asus_acpi_add,
1568 .remove = asus_acpi_remove,
1569 .notify = asus_acpi_notify,
1570 },
1571};
85091b71 1572
600ad520
CC
1573static int __init asus_laptop_init(void)
1574{
1575 int result;
85091b71 1576
600ad520
CC
1577 result = platform_driver_register(&platform_driver);
1578 if (result < 0)
1579 return result;
034ce90a 1580
600ad520
CC
1581 result = acpi_bus_register_driver(&asus_acpi_driver);
1582 if (result < 0)
1583 goto fail_acpi_driver;
1584 if (!asus_device_present) {
1585 result = -ENODEV;
1586 goto fail_no_device;
1587 }
1588 return 0;
85091b71 1589
600ad520
CC
1590fail_no_device:
1591 acpi_bus_unregister_driver(&asus_acpi_driver);
1592fail_acpi_driver:
1593 platform_driver_unregister(&platform_driver);
85091b71
CC
1594 return result;
1595}
1596
600ad520
CC
1597static void __exit asus_laptop_exit(void)
1598{
1599 acpi_bus_unregister_driver(&asus_acpi_driver);
1600 platform_driver_unregister(&platform_driver);
1601}
1602
85091b71
CC
1603module_init(asus_laptop_init);
1604module_exit(asus_laptop_exit);