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