asus-laptop.c revision e0ac913374247f000aa97fdd732dcaf0070dd466
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 *  Copyright (C) 2011 Wind River Systems
8 *
9 *  This program is free software; you can redistribute it and/or modify
10 *  it under the terms of the GNU General Public License as published by
11 *  the Free Software Foundation; either version 2 of the License, or
12 *  (at your option) any later version.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 *
23 *
24 *  The development page for this driver is located at
25 *  http://sourceforge.net/projects/acpi4asus/
26 *
27 *  Credits:
28 *  Pontus Fuchs   - Helper functions, cleanup
29 *  Johann Wiesner - Small compile fixes
30 *  John Belmonte  - ACPI code for Toshiba laptop was a good starting point.
31 *  Eric Burghard  - LED display support for W1N
32 *  Josh Green     - Light Sens support
33 *  Thomas Tuttle  - His first patch for led support was very helpful
34 *  Sam Lin        - GPS support
35 */
36
37#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
38
39#include <linux/kernel.h>
40#include <linux/module.h>
41#include <linux/init.h>
42#include <linux/types.h>
43#include <linux/err.h>
44#include <linux/proc_fs.h>
45#include <linux/backlight.h>
46#include <linux/fb.h>
47#include <linux/leds.h>
48#include <linux/platform_device.h>
49#include <linux/uaccess.h>
50#include <linux/input.h>
51#include <linux/input/sparse-keymap.h>
52#include <linux/input-polldev.h>
53#include <linux/rfkill.h>
54#include <linux/slab.h>
55#include <linux/dmi.h>
56#include <acpi/acpi_drivers.h>
57#include <acpi/acpi_bus.h>
58
59#define ASUS_LAPTOP_VERSION	"0.42"
60
61#define ASUS_LAPTOP_NAME	"Asus Laptop Support"
62#define ASUS_LAPTOP_CLASS	"hotkey"
63#define ASUS_LAPTOP_DEVICE_NAME	"Hotkey"
64#define ASUS_LAPTOP_FILE	KBUILD_MODNAME
65#define ASUS_LAPTOP_PREFIX	"\\_SB.ATKD."
66
67MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
68MODULE_DESCRIPTION(ASUS_LAPTOP_NAME);
69MODULE_LICENSE("GPL");
70
71/*
72 * WAPF defines the behavior of the Fn+Fx wlan key
73 * The significance of values is yet to be found, but
74 * most of the time:
75 * Bit | Bluetooth | WLAN
76 *  0  | Hardware  | Hardware
77 *  1  | Hardware  | Software
78 *  4  | Software  | Software
79 */
80static uint wapf = 1;
81module_param(wapf, uint, 0444);
82MODULE_PARM_DESC(wapf, "WAPF value");
83
84static int wlan_status = 1;
85static int bluetooth_status = 1;
86static int wimax_status = -1;
87static int wwan_status = -1;
88static int als_status;
89
90module_param(wlan_status, int, 0444);
91MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot "
92		 "(0 = disabled, 1 = enabled, -1 = don't do anything). "
93		 "default is -1");
94
95module_param(bluetooth_status, int, 0444);
96MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot "
97		 "(0 = disabled, 1 = enabled, -1 = don't do anything). "
98		 "default is -1");
99
100module_param(wimax_status, int, 0444);
101MODULE_PARM_DESC(wimax_status, "Set the wireless status on boot "
102		 "(0 = disabled, 1 = enabled, -1 = don't do anything). "
103		 "default is -1");
104
105module_param(wwan_status, int, 0444);
106MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot "
107		 "(0 = disabled, 1 = enabled, -1 = don't do anything). "
108		 "default is -1");
109
110module_param(als_status, int, 0444);
111MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
112		 "(0 = disabled, 1 = enabled). "
113		 "default is 0");
114
115/*
116 * Some events we use, same for all Asus
117 */
118#define ATKD_BR_UP	0x10	/* (event & ~ATKD_BR_UP) = brightness level */
119#define ATKD_BR_DOWN	0x20	/* (event & ~ATKD_BR_DOWN) = britghness level */
120#define ATKD_BR_MIN	ATKD_BR_UP
121#define ATKD_BR_MAX	(ATKD_BR_DOWN | 0xF)	/* 0x2f */
122#define ATKD_LCD_ON	0x33
123#define ATKD_LCD_OFF	0x34
124
125/*
126 * Known bits returned by \_SB.ATKD.HWRS
127 */
128#define WL_HWRS		0x80
129#define BT_HWRS		0x100
130
131/*
132 * Flags for hotk status
133 * WL_ON and BT_ON are also used for wireless_status()
134 */
135#define WL_RSTS		0x01	/* internal Wifi */
136#define BT_RSTS		0x02	/* internal Bluetooth */
137#define WM_RSTS		0x08    /* internal wimax */
138#define WW_RSTS		0x20    /* internal wwan */
139
140/* LED */
141#define METHOD_MLED		"MLED"
142#define METHOD_TLED		"TLED"
143#define METHOD_RLED		"RLED"	/* W1JC */
144#define METHOD_PLED		"PLED"	/* A7J */
145#define METHOD_GLED		"GLED"	/* G1, G2 (probably) */
146
147/* LEDD */
148#define METHOD_LEDD		"SLCM"
149
150/*
151 * Bluetooth and WLAN
152 * WLED and BLED are not handled like other XLED, because in some dsdt
153 * they also control the WLAN/Bluetooth device.
154 */
155#define METHOD_WLAN		"WLED"
156#define METHOD_BLUETOOTH	"BLED"
157
158/* WWAN and WIMAX */
159#define METHOD_WWAN		"GSMC"
160#define METHOD_WIMAX		"WMXC"
161
162#define METHOD_WL_STATUS	"RSTS"
163
164/* Brightness */
165#define METHOD_BRIGHTNESS_SET	"SPLV"
166#define METHOD_BRIGHTNESS_GET	"GPLV"
167
168/* Display */
169#define METHOD_SWITCH_DISPLAY	"SDSP"
170
171#define METHOD_ALS_CONTROL	"ALSC" /* Z71A Z71V */
172#define METHOD_ALS_LEVEL	"ALSL" /* Z71A Z71V */
173
174/* GPS */
175/* R2H use different handle for GPS on/off */
176#define METHOD_GPS_ON		"SDON"
177#define METHOD_GPS_OFF		"SDOF"
178#define METHOD_GPS_STATUS	"GPST"
179
180/* Keyboard light */
181#define METHOD_KBD_LIGHT_SET	"SLKB"
182#define METHOD_KBD_LIGHT_GET	"GLKB"
183
184/* For Pegatron Lucid tablet */
185#define DEVICE_NAME_PEGA	"Lucid"
186
187#define METHOD_PEGA_ENABLE	"ENPR"
188#define METHOD_PEGA_DISABLE	"DAPR"
189#define PEGA_WLAN	0x00
190#define PEGA_BLUETOOTH	0x01
191#define PEGA_WWAN	0x02
192#define PEGA_ALS	0x04
193#define PEGA_ALS_POWER	0x05
194
195#define METHOD_PEGA_READ	"RDLN"
196#define PEGA_READ_ALS_H	0x02
197#define PEGA_READ_ALS_L	0x03
198
199#define PEGA_ACCEL_NAME "pega_accel"
200#define PEGA_ACCEL_DESC "Pegatron Lucid Tablet Accelerometer"
201#define METHOD_XLRX "XLRX"
202#define METHOD_XLRY "XLRY"
203#define METHOD_XLRZ "XLRZ"
204#define PEGA_ACC_CLAMP 512 /* 1G accel is reported as ~256, so clamp to 2G */
205#define PEGA_ACC_RETRIES 3
206
207/*
208 * Define a specific led structure to keep the main structure clean
209 */
210struct asus_led {
211	int wk;
212	struct work_struct work;
213	struct led_classdev led;
214	struct asus_laptop *asus;
215	const char *method;
216};
217
218/*
219 * Same thing for rfkill
220 */
221struct asus_pega_rfkill {
222	int control_id;		/* type of control. Maps to PEGA_* values */
223	struct rfkill *rfkill;
224	struct asus_laptop *asus;
225};
226
227/*
228 * This is the main structure, we can use it to store anything interesting
229 * about the hotk device
230 */
231struct asus_laptop {
232	char *name;		/* laptop name */
233
234	struct acpi_table_header *dsdt_info;
235	struct platform_device *platform_device;
236	struct acpi_device *device;		/* the device we are in */
237	struct backlight_device *backlight_device;
238
239	struct input_dev *inputdev;
240	struct key_entry *keymap;
241	struct input_polled_dev *pega_accel_poll;
242
243	struct asus_led mled;
244	struct asus_led tled;
245	struct asus_led rled;
246	struct asus_led pled;
247	struct asus_led gled;
248	struct asus_led kled;
249	struct workqueue_struct *led_workqueue;
250
251	int wireless_status;
252	bool have_rsts;
253	bool is_pega_lucid;
254	bool pega_acc_live;
255	int pega_acc_x;
256	int pega_acc_y;
257	int pega_acc_z;
258
259	struct rfkill *gps_rfkill;
260
261	struct asus_pega_rfkill wlanrfk;
262	struct asus_pega_rfkill btrfk;
263	struct asus_pega_rfkill wwanrfk;
264
265	acpi_handle handle;	/* the handle of the hotk device */
266	u32 ledd_status;	/* status of the LED display */
267	u8 light_level;		/* light sensor level */
268	u8 light_switch;	/* light sensor switch value */
269	u16 event_count[128];	/* count for each event TODO make this better */
270};
271
272static const struct key_entry asus_keymap[] = {
273	/* Lenovo SL Specific keycodes */
274	{KE_KEY, 0x02, { KEY_SCREENLOCK } },
275	{KE_KEY, 0x05, { KEY_WLAN } },
276	{KE_KEY, 0x08, { KEY_F13 } },
277	{KE_KEY, 0x17, { KEY_ZOOM } },
278	{KE_KEY, 0x1f, { KEY_BATTERY } },
279	/* End of Lenovo SL Specific keycodes */
280	{KE_KEY, 0x30, { KEY_VOLUMEUP } },
281	{KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
282	{KE_KEY, 0x32, { KEY_MUTE } },
283	{KE_KEY, 0x33, { KEY_SWITCHVIDEOMODE } },
284	{KE_KEY, 0x34, { KEY_SWITCHVIDEOMODE } },
285	{KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
286	{KE_KEY, 0x41, { KEY_NEXTSONG } },
287	{KE_KEY, 0x43, { KEY_STOPCD } },
288	{KE_KEY, 0x45, { KEY_PLAYPAUSE } },
289	{KE_KEY, 0x4c, { KEY_MEDIA } },
290	{KE_KEY, 0x50, { KEY_EMAIL } },
291	{KE_KEY, 0x51, { KEY_WWW } },
292	{KE_KEY, 0x55, { KEY_CALC } },
293	{KE_KEY, 0x5C, { KEY_SCREENLOCK } },  /* Screenlock */
294	{KE_KEY, 0x5D, { KEY_WLAN } },
295	{KE_KEY, 0x5E, { KEY_WLAN } },
296	{KE_KEY, 0x5F, { KEY_WLAN } },
297	{KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } },
298	{KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } },
299	{KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
300	{KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
301	{KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */
302	{KE_KEY, 0x7E, { KEY_BLUETOOTH } },
303	{KE_KEY, 0x7D, { KEY_BLUETOOTH } },
304	{KE_KEY, 0x82, { KEY_CAMERA } },
305	{KE_KEY, 0x88, { KEY_WLAN  } },
306	{KE_KEY, 0x8A, { KEY_PROG1 } },
307	{KE_KEY, 0x95, { KEY_MEDIA } },
308	{KE_KEY, 0x99, { KEY_PHONE } },
309	{KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
310	{KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
311	{KE_KEY, 0xb5, { KEY_CALC } },
312	{KE_END, 0},
313};
314
315
316/*
317 * This function evaluates an ACPI method, given an int as parameter, the
318 * method is searched within the scope of the handle, can be NULL. The output
319 * of the method is written is output, which can also be NULL
320 *
321 * returns 0 if write is successful, -1 else.
322 */
323static int write_acpi_int_ret(acpi_handle handle, const char *method, int val,
324			      struct acpi_buffer *output)
325{
326	struct acpi_object_list params;	/* list of input parameters (an int) */
327	union acpi_object in_obj;	/* the only param we use */
328	acpi_status status;
329
330	if (!handle)
331		return -1;
332
333	params.count = 1;
334	params.pointer = &in_obj;
335	in_obj.type = ACPI_TYPE_INTEGER;
336	in_obj.integer.value = val;
337
338	status = acpi_evaluate_object(handle, (char *)method, &params, output);
339	if (status == AE_OK)
340		return 0;
341	else
342		return -1;
343}
344
345static int write_acpi_int(acpi_handle handle, const char *method, int val)
346{
347	return write_acpi_int_ret(handle, method, val, NULL);
348}
349
350static int acpi_check_handle(acpi_handle handle, const char *method,
351			     acpi_handle *ret)
352{
353	acpi_status status;
354
355	if (method == NULL)
356		return -ENODEV;
357
358	if (ret)
359		status = acpi_get_handle(handle, (char *)method,
360					 ret);
361	else {
362		acpi_handle dummy;
363
364		status = acpi_get_handle(handle, (char *)method,
365					 &dummy);
366	}
367
368	if (status != AE_OK) {
369		if (ret)
370			pr_warn("Error finding %s\n", method);
371		return -ENODEV;
372	}
373	return 0;
374}
375
376static bool asus_check_pega_lucid(struct asus_laptop *asus)
377{
378	return !strcmp(asus->name, DEVICE_NAME_PEGA) &&
379	   !acpi_check_handle(asus->handle, METHOD_PEGA_ENABLE, NULL) &&
380	   !acpi_check_handle(asus->handle, METHOD_PEGA_DISABLE, NULL) &&
381	   !acpi_check_handle(asus->handle, METHOD_PEGA_READ, NULL);
382}
383
384static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable)
385{
386	char *method = enable ? METHOD_PEGA_ENABLE : METHOD_PEGA_DISABLE;
387	return write_acpi_int(asus->handle, method, unit);
388}
389
390static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method)
391{
392	int i, delta;
393	unsigned long long val;
394	for (i = 0; i < PEGA_ACC_RETRIES; i++) {
395		acpi_evaluate_integer(asus->handle, method, NULL, &val);
396
397		/* The output is noisy.  From reading the ASL
398		 * dissassembly, timeout errors are returned with 1's
399		 * in the high word, and the lack of locking around
400		 * thei hi/lo byte reads means that a transition
401		 * between (for example) -1 and 0 could be read as
402		 * 0xff00 or 0x00ff. */
403		delta = abs(curr - (short)val);
404		if (delta < 128 && !(val & ~0xffff))
405			break;
406	}
407	return clamp_val((short)val, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP);
408}
409
410static void pega_accel_poll(struct input_polled_dev *ipd)
411{
412	struct device *parent = ipd->input->dev.parent;
413	struct asus_laptop *asus = dev_get_drvdata(parent);
414
415	/* In some cases, the very first call to poll causes a
416	 * recursive fault under the polldev worker.  This is
417	 * apparently related to very early userspace access to the
418	 * device, and perhaps a firmware bug. Fake the first report. */
419	if (!asus->pega_acc_live) {
420		asus->pega_acc_live = true;
421		input_report_abs(ipd->input, ABS_X, 0);
422		input_report_abs(ipd->input, ABS_Y, 0);
423		input_report_abs(ipd->input, ABS_Z, 0);
424		input_sync(ipd->input);
425		return;
426	}
427
428	asus->pega_acc_x = pega_acc_axis(asus, asus->pega_acc_x, METHOD_XLRX);
429	asus->pega_acc_y = pega_acc_axis(asus, asus->pega_acc_y, METHOD_XLRY);
430	asus->pega_acc_z = pega_acc_axis(asus, asus->pega_acc_z, METHOD_XLRZ);
431
432	/* Note transform, convert to "right/up/out" in the native
433	 * landscape orientation (i.e. the vector is the direction of
434	 * "real up" in the device's cartiesian coordinates). */
435	input_report_abs(ipd->input, ABS_X, -asus->pega_acc_x);
436	input_report_abs(ipd->input, ABS_Y, -asus->pega_acc_y);
437	input_report_abs(ipd->input, ABS_Z,  asus->pega_acc_z);
438	input_sync(ipd->input);
439}
440
441static void pega_accel_exit(struct asus_laptop *asus)
442{
443	if (asus->pega_accel_poll) {
444		input_unregister_polled_device(asus->pega_accel_poll);
445		input_free_polled_device(asus->pega_accel_poll);
446	}
447	asus->pega_accel_poll = NULL;
448}
449
450static int pega_accel_init(struct asus_laptop *asus)
451{
452	int err;
453	struct input_polled_dev *ipd;
454
455	if (!asus->is_pega_lucid)
456		return -ENODEV;
457
458	if (acpi_check_handle(asus->handle, METHOD_XLRX, NULL) ||
459	    acpi_check_handle(asus->handle, METHOD_XLRY, NULL) ||
460	    acpi_check_handle(asus->handle, METHOD_XLRZ, NULL))
461		return -ENODEV;
462
463	ipd = input_allocate_polled_device();
464	if (!ipd)
465		return -ENOMEM;
466
467	ipd->poll = pega_accel_poll;
468	ipd->poll_interval = 125;
469	ipd->poll_interval_min = 50;
470	ipd->poll_interval_max = 2000;
471
472	ipd->input->name = PEGA_ACCEL_DESC;
473	ipd->input->phys = PEGA_ACCEL_NAME "/input0";
474	ipd->input->dev.parent = &asus->platform_device->dev;
475	ipd->input->id.bustype = BUS_HOST;
476
477	set_bit(EV_ABS, ipd->input->evbit);
478	input_set_abs_params(ipd->input, ABS_X,
479			     -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
480	input_set_abs_params(ipd->input, ABS_Y,
481			     -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
482	input_set_abs_params(ipd->input, ABS_Z,
483			     -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
484
485	err = input_register_polled_device(ipd);
486	if (err)
487		goto exit;
488
489	asus->pega_accel_poll = ipd;
490	return 0;
491
492exit:
493	input_free_polled_device(ipd);
494	return err;
495}
496
497/* Generic LED function */
498static int asus_led_set(struct asus_laptop *asus, const char *method,
499			 int value)
500{
501	if (!strcmp(method, METHOD_MLED))
502		value = !value;
503	else if (!strcmp(method, METHOD_GLED))
504		value = !value + 1;
505	else
506		value = !!value;
507
508	return write_acpi_int(asus->handle, method, value);
509}
510
511/*
512 * LEDs
513 */
514/* /sys/class/led handlers */
515static void asus_led_cdev_set(struct led_classdev *led_cdev,
516			 enum led_brightness value)
517{
518	struct asus_led *led = container_of(led_cdev, struct asus_led, led);
519	struct asus_laptop *asus = led->asus;
520
521	led->wk = !!value;
522	queue_work(asus->led_workqueue, &led->work);
523}
524
525static void asus_led_cdev_update(struct work_struct *work)
526{
527	struct asus_led *led = container_of(work, struct asus_led, work);
528	struct asus_laptop *asus = led->asus;
529
530	asus_led_set(asus, led->method, led->wk);
531}
532
533static enum led_brightness asus_led_cdev_get(struct led_classdev *led_cdev)
534{
535	return led_cdev->brightness;
536}
537
538/*
539 * Keyboard backlight (also a LED)
540 */
541static int asus_kled_lvl(struct asus_laptop *asus)
542{
543	unsigned long long kblv;
544	struct acpi_object_list params;
545	union acpi_object in_obj;
546	acpi_status rv;
547
548	params.count = 1;
549	params.pointer = &in_obj;
550	in_obj.type = ACPI_TYPE_INTEGER;
551	in_obj.integer.value = 2;
552
553	rv = acpi_evaluate_integer(asus->handle, METHOD_KBD_LIGHT_GET,
554				   &params, &kblv);
555	if (ACPI_FAILURE(rv)) {
556		pr_warn("Error reading kled level\n");
557		return -ENODEV;
558	}
559	return kblv;
560}
561
562static int asus_kled_set(struct asus_laptop *asus, int kblv)
563{
564	if (kblv > 0)
565		kblv = (1 << 7) | (kblv & 0x7F);
566	else
567		kblv = 0;
568
569	if (write_acpi_int(asus->handle, METHOD_KBD_LIGHT_SET, kblv)) {
570		pr_warn("Keyboard LED display write failed\n");
571		return -EINVAL;
572	}
573	return 0;
574}
575
576static void asus_kled_cdev_set(struct led_classdev *led_cdev,
577			      enum led_brightness value)
578{
579	struct asus_led *led = container_of(led_cdev, struct asus_led, led);
580	struct asus_laptop *asus = led->asus;
581
582	led->wk = value;
583	queue_work(asus->led_workqueue, &led->work);
584}
585
586static void asus_kled_cdev_update(struct work_struct *work)
587{
588	struct asus_led *led = container_of(work, struct asus_led, work);
589	struct asus_laptop *asus = led->asus;
590
591	asus_kled_set(asus, led->wk);
592}
593
594static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev)
595{
596	struct asus_led *led = container_of(led_cdev, struct asus_led, led);
597	struct asus_laptop *asus = led->asus;
598
599	return asus_kled_lvl(asus);
600}
601
602static void asus_led_exit(struct asus_laptop *asus)
603{
604	if (!IS_ERR_OR_NULL(asus->mled.led.dev))
605		led_classdev_unregister(&asus->mled.led);
606	if (!IS_ERR_OR_NULL(asus->tled.led.dev))
607		led_classdev_unregister(&asus->tled.led);
608	if (!IS_ERR_OR_NULL(asus->pled.led.dev))
609		led_classdev_unregister(&asus->pled.led);
610	if (!IS_ERR_OR_NULL(asus->rled.led.dev))
611		led_classdev_unregister(&asus->rled.led);
612	if (!IS_ERR_OR_NULL(asus->gled.led.dev))
613		led_classdev_unregister(&asus->gled.led);
614	if (!IS_ERR_OR_NULL(asus->kled.led.dev))
615		led_classdev_unregister(&asus->kled.led);
616	if (asus->led_workqueue) {
617		destroy_workqueue(asus->led_workqueue);
618		asus->led_workqueue = NULL;
619	}
620}
621
622/*  Ugly macro, need to fix that later */
623static int asus_led_register(struct asus_laptop *asus,
624			     struct asus_led *led,
625			     const char *name, const char *method)
626{
627	struct led_classdev *led_cdev = &led->led;
628
629	if (!method || acpi_check_handle(asus->handle, method, NULL))
630		return 0; /* Led not present */
631
632	led->asus = asus;
633	led->method = method;
634
635	INIT_WORK(&led->work, asus_led_cdev_update);
636	led_cdev->name = name;
637	led_cdev->brightness_set = asus_led_cdev_set;
638	led_cdev->brightness_get = asus_led_cdev_get;
639	led_cdev->max_brightness = 1;
640	return led_classdev_register(&asus->platform_device->dev, led_cdev);
641}
642
643static int asus_led_init(struct asus_laptop *asus)
644{
645	int r;
646
647	/*
648	 * The Pegatron Lucid has no physical leds, but all methods are
649	 * available in the DSDT...
650	 */
651	if (asus->is_pega_lucid)
652		return 0;
653
654	/*
655	 * Functions that actually update the LED's are called from a
656	 * workqueue. By doing this as separate work rather than when the LED
657	 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
658	 * potentially bad time, such as a timer interrupt.
659	 */
660	asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
661	if (!asus->led_workqueue)
662		return -ENOMEM;
663
664	r = asus_led_register(asus, &asus->mled, "asus::mail", METHOD_MLED);
665	if (r)
666		goto error;
667	r = asus_led_register(asus, &asus->tled, "asus::touchpad", METHOD_TLED);
668	if (r)
669		goto error;
670	r = asus_led_register(asus, &asus->rled, "asus::record", METHOD_RLED);
671	if (r)
672		goto error;
673	r = asus_led_register(asus, &asus->pled, "asus::phone", METHOD_PLED);
674	if (r)
675		goto error;
676	r = asus_led_register(asus, &asus->gled, "asus::gaming", METHOD_GLED);
677	if (r)
678		goto error;
679	if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL) &&
680	    !acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_GET, NULL)) {
681		struct asus_led *led = &asus->kled;
682		struct led_classdev *cdev = &led->led;
683
684		led->asus = asus;
685
686		INIT_WORK(&led->work, asus_kled_cdev_update);
687		cdev->name = "asus::kbd_backlight";
688		cdev->brightness_set = asus_kled_cdev_set;
689		cdev->brightness_get = asus_kled_cdev_get;
690		cdev->max_brightness = 3;
691		r = led_classdev_register(&asus->platform_device->dev, cdev);
692	}
693error:
694	if (r)
695		asus_led_exit(asus);
696	return r;
697}
698
699/*
700 * Backlight device
701 */
702static int asus_read_brightness(struct backlight_device *bd)
703{
704	struct asus_laptop *asus = bl_get_data(bd);
705	unsigned long long value;
706	acpi_status rv = AE_OK;
707
708	rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET,
709				   NULL, &value);
710	if (ACPI_FAILURE(rv))
711		pr_warn("Error reading brightness\n");
712
713	return value;
714}
715
716static int asus_set_brightness(struct backlight_device *bd, int value)
717{
718	struct asus_laptop *asus = bl_get_data(bd);
719
720	if (write_acpi_int(asus->handle, METHOD_BRIGHTNESS_SET, value)) {
721		pr_warn("Error changing brightness\n");
722		return -EIO;
723	}
724	return 0;
725}
726
727static int update_bl_status(struct backlight_device *bd)
728{
729	int value = bd->props.brightness;
730
731	return asus_set_brightness(bd, value);
732}
733
734static const struct backlight_ops asusbl_ops = {
735	.get_brightness = asus_read_brightness,
736	.update_status = update_bl_status,
737};
738
739static int asus_backlight_notify(struct asus_laptop *asus)
740{
741	struct backlight_device *bd = asus->backlight_device;
742	int old = bd->props.brightness;
743
744	backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
745
746	return old;
747}
748
749static int asus_backlight_init(struct asus_laptop *asus)
750{
751	struct backlight_device *bd;
752	struct backlight_properties props;
753
754	if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) ||
755	    acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL))
756		return 0;
757
758	memset(&props, 0, sizeof(struct backlight_properties));
759	props.max_brightness = 15;
760	props.type = BACKLIGHT_PLATFORM;
761
762	bd = backlight_device_register(ASUS_LAPTOP_FILE,
763				       &asus->platform_device->dev, asus,
764				       &asusbl_ops, &props);
765	if (IS_ERR(bd)) {
766		pr_err("Could not register asus backlight device\n");
767		asus->backlight_device = NULL;
768		return PTR_ERR(bd);
769	}
770
771	asus->backlight_device = bd;
772	bd->props.brightness = asus_read_brightness(bd);
773	bd->props.power = FB_BLANK_UNBLANK;
774	backlight_update_status(bd);
775	return 0;
776}
777
778static void asus_backlight_exit(struct asus_laptop *asus)
779{
780	if (asus->backlight_device)
781		backlight_device_unregister(asus->backlight_device);
782	asus->backlight_device = NULL;
783}
784
785/*
786 * Platform device handlers
787 */
788
789/*
790 * We write our info in page, we begin at offset off and cannot write more
791 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
792 * number of bytes written in page
793 */
794static ssize_t show_infos(struct device *dev,
795			  struct device_attribute *attr, char *page)
796{
797	struct asus_laptop *asus = dev_get_drvdata(dev);
798	int len = 0;
799	unsigned long long temp;
800	char buf[16];		/* enough for all info */
801	acpi_status rv = AE_OK;
802
803	/*
804	 * We use the easy way, we don't care of off and count,
805	 * so we don't set eof to 1
806	 */
807
808	len += sprintf(page, ASUS_LAPTOP_NAME " " ASUS_LAPTOP_VERSION "\n");
809	len += sprintf(page + len, "Model reference    : %s\n", asus->name);
810	/*
811	 * The SFUN method probably allows the original driver to get the list
812	 * of features supported by a given model. For now, 0x0100 or 0x0800
813	 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
814	 * The significance of others is yet to be found.
815	 */
816	rv = acpi_evaluate_integer(asus->handle, "SFUN", NULL, &temp);
817	if (!ACPI_FAILURE(rv))
818		len += sprintf(page + len, "SFUN value         : %#x\n",
819			       (uint) temp);
820	/*
821	 * The HWRS method return informations about the hardware.
822	 * 0x80 bit is for WLAN, 0x100 for Bluetooth.
823	 * The significance of others is yet to be found.
824	 * If we don't find the method, we assume the device are present.
825	 */
826	rv = acpi_evaluate_integer(asus->handle, "HRWS", NULL, &temp);
827	if (!ACPI_FAILURE(rv))
828		len += sprintf(page + len, "HRWS value         : %#x\n",
829			       (uint) temp);
830	/*
831	 * Another value for userspace: the ASYM method returns 0x02 for
832	 * battery low and 0x04 for battery critical, its readings tend to be
833	 * more accurate than those provided by _BST.
834	 * Note: since not all the laptops provide this method, errors are
835	 * silently ignored.
836	 */
837	rv = acpi_evaluate_integer(asus->handle, "ASYM", NULL, &temp);
838	if (!ACPI_FAILURE(rv))
839		len += sprintf(page + len, "ASYM value         : %#x\n",
840			       (uint) temp);
841	if (asus->dsdt_info) {
842		snprintf(buf, 16, "%d", asus->dsdt_info->length);
843		len += sprintf(page + len, "DSDT length        : %s\n", buf);
844		snprintf(buf, 16, "%d", asus->dsdt_info->checksum);
845		len += sprintf(page + len, "DSDT checksum      : %s\n", buf);
846		snprintf(buf, 16, "%d", asus->dsdt_info->revision);
847		len += sprintf(page + len, "DSDT revision      : %s\n", buf);
848		snprintf(buf, 7, "%s", asus->dsdt_info->oem_id);
849		len += sprintf(page + len, "OEM id             : %s\n", buf);
850		snprintf(buf, 9, "%s", asus->dsdt_info->oem_table_id);
851		len += sprintf(page + len, "OEM table id       : %s\n", buf);
852		snprintf(buf, 16, "%x", asus->dsdt_info->oem_revision);
853		len += sprintf(page + len, "OEM revision       : 0x%s\n", buf);
854		snprintf(buf, 5, "%s", asus->dsdt_info->asl_compiler_id);
855		len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
856		snprintf(buf, 16, "%x", asus->dsdt_info->asl_compiler_revision);
857		len += sprintf(page + len, "ASL comp revision  : 0x%s\n", buf);
858	}
859
860	return len;
861}
862
863static int parse_arg(const char *buf, unsigned long count, int *val)
864{
865	if (!count)
866		return 0;
867	if (count > 31)
868		return -EINVAL;
869	if (sscanf(buf, "%i", val) != 1)
870		return -EINVAL;
871	return count;
872}
873
874static ssize_t sysfs_acpi_set(struct asus_laptop *asus,
875			      const char *buf, size_t count,
876			      const char *method)
877{
878	int rv, value;
879	int out = 0;
880
881	rv = parse_arg(buf, count, &value);
882	if (rv > 0)
883		out = value ? 1 : 0;
884
885	if (write_acpi_int(asus->handle, method, value))
886		return -ENODEV;
887	return rv;
888}
889
890/*
891 * LEDD display
892 */
893static ssize_t show_ledd(struct device *dev,
894			 struct device_attribute *attr, char *buf)
895{
896	struct asus_laptop *asus = dev_get_drvdata(dev);
897
898	return sprintf(buf, "0x%08x\n", asus->ledd_status);
899}
900
901static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
902			  const char *buf, size_t count)
903{
904	struct asus_laptop *asus = dev_get_drvdata(dev);
905	int rv, value;
906
907	rv = parse_arg(buf, count, &value);
908	if (rv > 0) {
909		if (write_acpi_int(asus->handle, METHOD_LEDD, value)) {
910			pr_warn("LED display write failed\n");
911			return -ENODEV;
912		}
913		asus->ledd_status = (u32) value;
914	}
915	return rv;
916}
917
918/*
919 * Wireless
920 */
921static int asus_wireless_status(struct asus_laptop *asus, int mask)
922{
923	unsigned long long status;
924	acpi_status rv = AE_OK;
925
926	if (!asus->have_rsts)
927		return (asus->wireless_status & mask) ? 1 : 0;
928
929	rv = acpi_evaluate_integer(asus->handle, METHOD_WL_STATUS,
930				   NULL, &status);
931	if (ACPI_FAILURE(rv)) {
932		pr_warn("Error reading Wireless status\n");
933		return -EINVAL;
934	}
935	return !!(status & mask);
936}
937
938/*
939 * WLAN
940 */
941static int asus_wlan_set(struct asus_laptop *asus, int status)
942{
943	if (write_acpi_int(asus->handle, METHOD_WLAN, !!status)) {
944		pr_warn("Error setting wlan status to %d\n", status);
945		return -EIO;
946	}
947	return 0;
948}
949
950static ssize_t show_wlan(struct device *dev,
951			 struct device_attribute *attr, char *buf)
952{
953	struct asus_laptop *asus = dev_get_drvdata(dev);
954
955	return sprintf(buf, "%d\n", asus_wireless_status(asus, WL_RSTS));
956}
957
958static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
959			  const char *buf, size_t count)
960{
961	struct asus_laptop *asus = dev_get_drvdata(dev);
962
963	return sysfs_acpi_set(asus, buf, count, METHOD_WLAN);
964}
965
966/*
967 * Bluetooth
968 */
969static int asus_bluetooth_set(struct asus_laptop *asus, int status)
970{
971	if (write_acpi_int(asus->handle, METHOD_BLUETOOTH, !!status)) {
972		pr_warn("Error setting bluetooth status to %d\n", status);
973		return -EIO;
974	}
975	return 0;
976}
977
978static ssize_t show_bluetooth(struct device *dev,
979			      struct device_attribute *attr, char *buf)
980{
981	struct asus_laptop *asus = dev_get_drvdata(dev);
982
983	return sprintf(buf, "%d\n", asus_wireless_status(asus, BT_RSTS));
984}
985
986static ssize_t store_bluetooth(struct device *dev,
987			       struct device_attribute *attr, const char *buf,
988			       size_t count)
989{
990	struct asus_laptop *asus = dev_get_drvdata(dev);
991
992	return sysfs_acpi_set(asus, buf, count, METHOD_BLUETOOTH);
993}
994
995/*
996 * Wimax
997 */
998static int asus_wimax_set(struct asus_laptop *asus, int status)
999{
1000	if (write_acpi_int(asus->handle, METHOD_WIMAX, !!status)) {
1001		pr_warn("Error setting wimax status to %d\n", status);
1002		return -EIO;
1003	}
1004	return 0;
1005}
1006
1007static ssize_t show_wimax(struct device *dev,
1008			      struct device_attribute *attr, char *buf)
1009{
1010	struct asus_laptop *asus = dev_get_drvdata(dev);
1011
1012	return sprintf(buf, "%d\n", asus_wireless_status(asus, WM_RSTS));
1013}
1014
1015static ssize_t store_wimax(struct device *dev,
1016			       struct device_attribute *attr, const char *buf,
1017			       size_t count)
1018{
1019	struct asus_laptop *asus = dev_get_drvdata(dev);
1020
1021	return sysfs_acpi_set(asus, buf, count, METHOD_WIMAX);
1022}
1023
1024/*
1025 * Wwan
1026 */
1027static int asus_wwan_set(struct asus_laptop *asus, int status)
1028{
1029	if (write_acpi_int(asus->handle, METHOD_WWAN, !!status)) {
1030		pr_warn("Error setting wwan status to %d\n", status);
1031		return -EIO;
1032	}
1033	return 0;
1034}
1035
1036static ssize_t show_wwan(struct device *dev,
1037			      struct device_attribute *attr, char *buf)
1038{
1039	struct asus_laptop *asus = dev_get_drvdata(dev);
1040
1041	return sprintf(buf, "%d\n", asus_wireless_status(asus, WW_RSTS));
1042}
1043
1044static ssize_t store_wwan(struct device *dev,
1045			       struct device_attribute *attr, const char *buf,
1046			       size_t count)
1047{
1048	struct asus_laptop *asus = dev_get_drvdata(dev);
1049
1050	return sysfs_acpi_set(asus, buf, count, METHOD_WWAN);
1051}
1052
1053/*
1054 * Display
1055 */
1056static void asus_set_display(struct asus_laptop *asus, int value)
1057{
1058	/* no sanity check needed for now */
1059	if (write_acpi_int(asus->handle, METHOD_SWITCH_DISPLAY, value))
1060		pr_warn("Error setting display\n");
1061	return;
1062}
1063
1064/*
1065 * Experimental support for display switching. As of now: 1 should activate
1066 * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
1067 * Any combination (bitwise) of these will suffice. I never actually tested 4
1068 * displays hooked up simultaneously, so be warned. See the acpi4asus README
1069 * for more info.
1070 */
1071static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
1072			  const char *buf, size_t count)
1073{
1074	struct asus_laptop *asus = dev_get_drvdata(dev);
1075	int rv, value;
1076
1077	rv = parse_arg(buf, count, &value);
1078	if (rv > 0)
1079		asus_set_display(asus, value);
1080	return rv;
1081}
1082
1083/*
1084 * Light Sens
1085 */
1086static void asus_als_switch(struct asus_laptop *asus, int value)
1087{
1088	int ret;
1089
1090	if (asus->is_pega_lucid) {
1091		ret = asus_pega_lucid_set(asus, PEGA_ALS, value);
1092		if (!ret)
1093			ret = asus_pega_lucid_set(asus, PEGA_ALS_POWER, value);
1094	} else {
1095		ret = write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value);
1096	}
1097	if (ret)
1098		pr_warning("Error setting light sensor switch\n");
1099
1100	asus->light_switch = value;
1101}
1102
1103static ssize_t show_lssw(struct device *dev,
1104			 struct device_attribute *attr, char *buf)
1105{
1106	struct asus_laptop *asus = dev_get_drvdata(dev);
1107
1108	return sprintf(buf, "%d\n", asus->light_switch);
1109}
1110
1111static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
1112			  const char *buf, size_t count)
1113{
1114	struct asus_laptop *asus = dev_get_drvdata(dev);
1115	int rv, value;
1116
1117	rv = parse_arg(buf, count, &value);
1118	if (rv > 0)
1119		asus_als_switch(asus, value ? 1 : 0);
1120
1121	return rv;
1122}
1123
1124static void asus_als_level(struct asus_laptop *asus, int value)
1125{
1126	if (write_acpi_int(asus->handle, METHOD_ALS_LEVEL, value))
1127		pr_warn("Error setting light sensor level\n");
1128	asus->light_level = value;
1129}
1130
1131static ssize_t show_lslvl(struct device *dev,
1132			  struct device_attribute *attr, char *buf)
1133{
1134	struct asus_laptop *asus = dev_get_drvdata(dev);
1135
1136	return sprintf(buf, "%d\n", asus->light_level);
1137}
1138
1139static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,
1140			   const char *buf, size_t count)
1141{
1142	struct asus_laptop *asus = dev_get_drvdata(dev);
1143	int rv, value;
1144
1145	rv = parse_arg(buf, count, &value);
1146	if (rv > 0) {
1147		value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
1148		/* 0 <= value <= 15 */
1149		asus_als_level(asus, value);
1150	}
1151
1152	return rv;
1153}
1154
1155static int pega_int_read(struct asus_laptop *asus, int arg, int *result)
1156{
1157	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1158	int err = write_acpi_int_ret(asus->handle, METHOD_PEGA_READ, arg,
1159				     &buffer);
1160	if (!err) {
1161		union acpi_object *obj = buffer.pointer;
1162		if (obj && obj->type == ACPI_TYPE_INTEGER)
1163			*result = obj->integer.value;
1164		else
1165			err = -EIO;
1166	}
1167	return err;
1168}
1169
1170static ssize_t show_lsvalue(struct device *dev,
1171			    struct device_attribute *attr, char *buf)
1172{
1173	struct asus_laptop *asus = dev_get_drvdata(dev);
1174	int err, hi, lo;
1175
1176	err = pega_int_read(asus, PEGA_READ_ALS_H, &hi);
1177	if (!err)
1178		err = pega_int_read(asus, PEGA_READ_ALS_L, &lo);
1179	if (!err)
1180		return sprintf(buf, "%d\n", 10 * hi + lo);
1181	return err;
1182}
1183
1184/*
1185 * GPS
1186 */
1187static int asus_gps_status(struct asus_laptop *asus)
1188{
1189	unsigned long long status;
1190	acpi_status rv = AE_OK;
1191
1192	rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS,
1193				   NULL, &status);
1194	if (ACPI_FAILURE(rv)) {
1195		pr_warn("Error reading GPS status\n");
1196		return -ENODEV;
1197	}
1198	return !!status;
1199}
1200
1201static int asus_gps_switch(struct asus_laptop *asus, int status)
1202{
1203	const char *meth = status ? METHOD_GPS_ON : METHOD_GPS_OFF;
1204
1205	if (write_acpi_int(asus->handle, meth, 0x02))
1206		return -ENODEV;
1207	return 0;
1208}
1209
1210static ssize_t show_gps(struct device *dev,
1211			struct device_attribute *attr, char *buf)
1212{
1213	struct asus_laptop *asus = dev_get_drvdata(dev);
1214
1215	return sprintf(buf, "%d\n", asus_gps_status(asus));
1216}
1217
1218static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
1219			 const char *buf, size_t count)
1220{
1221	struct asus_laptop *asus = dev_get_drvdata(dev);
1222	int rv, value;
1223	int ret;
1224
1225	rv = parse_arg(buf, count, &value);
1226	if (rv <= 0)
1227		return -EINVAL;
1228	ret = asus_gps_switch(asus, !!value);
1229	if (ret)
1230		return ret;
1231	rfkill_set_sw_state(asus->gps_rfkill, !value);
1232	return rv;
1233}
1234
1235/*
1236 * rfkill
1237 */
1238static int asus_gps_rfkill_set(void *data, bool blocked)
1239{
1240	struct asus_laptop *asus = data;
1241
1242	return asus_gps_switch(asus, !blocked);
1243}
1244
1245static const struct rfkill_ops asus_gps_rfkill_ops = {
1246	.set_block = asus_gps_rfkill_set,
1247};
1248
1249static void asus_rfkill_exit(struct asus_laptop *asus)
1250{
1251	if (asus->gps_rfkill) {
1252		rfkill_unregister(asus->gps_rfkill);
1253		rfkill_destroy(asus->gps_rfkill);
1254		asus->gps_rfkill = NULL;
1255	}
1256}
1257
1258static int asus_rfkill_init(struct asus_laptop *asus)
1259{
1260	int result;
1261
1262	if (acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) ||
1263	    acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) ||
1264	    acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL))
1265		return 0;
1266
1267	asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev,
1268					RFKILL_TYPE_GPS,
1269					&asus_gps_rfkill_ops, asus);
1270	if (!asus->gps_rfkill)
1271		return -EINVAL;
1272
1273	result = rfkill_register(asus->gps_rfkill);
1274	if (result) {
1275		rfkill_destroy(asus->gps_rfkill);
1276		asus->gps_rfkill = NULL;
1277	}
1278
1279	return result;
1280}
1281
1282static int pega_rfkill_set(void *data, bool blocked)
1283{
1284	struct asus_pega_rfkill *pega_rfk = data;
1285
1286	int ret = asus_pega_lucid_set(pega_rfk->asus, pega_rfk->control_id, !blocked);
1287	pr_warn("Setting rfkill %d, to %d; returned %d\n", pega_rfk->control_id, !blocked, ret);
1288
1289	return ret;
1290}
1291
1292static const struct rfkill_ops pega_rfkill_ops = {
1293	.set_block = pega_rfkill_set,
1294};
1295
1296static void pega_rfkill_terminate(struct asus_pega_rfkill *pega_rfk)
1297{
1298	pr_warn("Terminating %d\n", pega_rfk->control_id);
1299	if (pega_rfk->rfkill) {
1300		rfkill_unregister(pega_rfk->rfkill);
1301		rfkill_destroy(pega_rfk->rfkill);
1302		pega_rfk->rfkill = NULL;
1303	}
1304}
1305
1306static void pega_rfkill_exit(struct asus_laptop *asus)
1307{
1308	pega_rfkill_terminate(&asus->wwanrfk);
1309	pega_rfkill_terminate(&asus->btrfk);
1310	pega_rfkill_terminate(&asus->wlanrfk);
1311}
1312
1313static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_pega_rfkill *pega_rfk,
1314		const char *name, int controlid, int rfkill_type)
1315{
1316	int result;
1317
1318	pr_warn("Setting up rfk %s, control %d, type %d\n", name, controlid, rfkill_type);
1319	pega_rfk->control_id = controlid;
1320	pega_rfk->asus = asus;
1321	pega_rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev,
1322					rfkill_type, &pega_rfkill_ops, pega_rfk);
1323	if (!pega_rfk->rfkill)
1324		return -EINVAL;
1325
1326	result = rfkill_register(pega_rfk->rfkill);
1327	if (result) {
1328		rfkill_destroy(pega_rfk->rfkill);
1329		pega_rfk->rfkill = NULL;
1330	}
1331
1332	return result;
1333}
1334
1335static int pega_rfkill_init(struct asus_laptop *asus)
1336{
1337	int ret = 0;
1338
1339	if(!asus->is_pega_lucid)
1340		return -ENODEV;
1341
1342	ret = pega_rfkill_setup(asus, &asus->wlanrfk, "pega-wlan", PEGA_WLAN, RFKILL_TYPE_WLAN);
1343	if(ret)
1344		return ret;
1345	ret = pega_rfkill_setup(asus, &asus->btrfk, "pega-bt", PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH);
1346	if(ret)
1347		goto err_btrfk;
1348	ret = pega_rfkill_setup(asus, &asus->wwanrfk, "pega-wwan", PEGA_WWAN, RFKILL_TYPE_WWAN);
1349	if(ret)
1350		goto err_wwanrfk;
1351
1352	pr_warn("Pega rfkill init succeeded\n");
1353	return 0;
1354err_wwanrfk:
1355	pega_rfkill_terminate(&asus->btrfk);
1356err_btrfk:
1357	pega_rfkill_terminate(&asus->wlanrfk);
1358
1359	return ret;
1360}
1361
1362/*
1363 * Input device (i.e. hotkeys)
1364 */
1365static void asus_input_notify(struct asus_laptop *asus, int event)
1366{
1367	if (!asus->inputdev)
1368		return ;
1369	if (!sparse_keymap_report_event(asus->inputdev, event, 1, true))
1370		pr_info("Unknown key %x pressed\n", event);
1371}
1372
1373static int asus_input_init(struct asus_laptop *asus)
1374{
1375	struct input_dev *input;
1376	int error;
1377
1378	input = input_allocate_device();
1379	if (!input) {
1380		pr_info("Unable to allocate input device\n");
1381		return -ENOMEM;
1382	}
1383	input->name = "Asus Laptop extra buttons";
1384	input->phys = ASUS_LAPTOP_FILE "/input0";
1385	input->id.bustype = BUS_HOST;
1386	input->dev.parent = &asus->platform_device->dev;
1387
1388	error = sparse_keymap_setup(input, asus_keymap, NULL);
1389	if (error) {
1390		pr_err("Unable to setup input device keymap\n");
1391		goto err_free_dev;
1392	}
1393	error = input_register_device(input);
1394	if (error) {
1395		pr_info("Unable to register input device\n");
1396		goto err_free_keymap;
1397	}
1398
1399	asus->inputdev = input;
1400	return 0;
1401
1402err_free_keymap:
1403	sparse_keymap_free(input);
1404err_free_dev:
1405	input_free_device(input);
1406	return error;
1407}
1408
1409static void asus_input_exit(struct asus_laptop *asus)
1410{
1411	if (asus->inputdev) {
1412		sparse_keymap_free(asus->inputdev);
1413		input_unregister_device(asus->inputdev);
1414	}
1415	asus->inputdev = NULL;
1416}
1417
1418/*
1419 * ACPI driver
1420 */
1421static void asus_acpi_notify(struct acpi_device *device, u32 event)
1422{
1423	struct asus_laptop *asus = acpi_driver_data(device);
1424	u16 count;
1425
1426	/* TODO Find a better way to handle events count. */
1427	count = asus->event_count[event % 128]++;
1428	acpi_bus_generate_proc_event(asus->device, event, count);
1429	acpi_bus_generate_netlink_event(asus->device->pnp.device_class,
1430					dev_name(&asus->device->dev), event,
1431					count);
1432
1433	/* Brightness events are special */
1434	if (event >= ATKD_BR_MIN && event <= ATKD_BR_MAX) {
1435
1436		/* Ignore them completely if the acpi video driver is used */
1437		if (asus->backlight_device != NULL) {
1438			/* Update the backlight device. */
1439			asus_backlight_notify(asus);
1440		}
1441		return ;
1442	}
1443
1444	/* Accelerometer "coarse orientation change" event */
1445	if (asus->pega_accel_poll && event == 0xEA) {
1446		kobject_uevent(&asus->pega_accel_poll->input->dev.kobj,
1447			       KOBJ_CHANGE);
1448		return ;
1449	}
1450
1451	asus_input_notify(asus, event);
1452}
1453
1454static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL);
1455static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan);
1456static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR,
1457		   show_bluetooth, store_bluetooth);
1458static DEVICE_ATTR(wimax, S_IRUGO | S_IWUSR, show_wimax, store_wimax);
1459static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan);
1460static DEVICE_ATTR(display, S_IWUSR, NULL, store_disp);
1461static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd);
1462static DEVICE_ATTR(ls_value, S_IRUGO, show_lsvalue, NULL);
1463static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl);
1464static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);
1465static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps);
1466
1467static struct attribute *asus_attributes[] = {
1468	&dev_attr_infos.attr,
1469	&dev_attr_wlan.attr,
1470	&dev_attr_bluetooth.attr,
1471	&dev_attr_wimax.attr,
1472	&dev_attr_wwan.attr,
1473	&dev_attr_display.attr,
1474	&dev_attr_ledd.attr,
1475	&dev_attr_ls_value.attr,
1476	&dev_attr_ls_level.attr,
1477	&dev_attr_ls_switch.attr,
1478	&dev_attr_gps.attr,
1479	NULL
1480};
1481
1482static umode_t asus_sysfs_is_visible(struct kobject *kobj,
1483				    struct attribute *attr,
1484				    int idx)
1485{
1486	struct device *dev = container_of(kobj, struct device, kobj);
1487	struct platform_device *pdev = to_platform_device(dev);
1488	struct asus_laptop *asus = platform_get_drvdata(pdev);
1489	acpi_handle handle = asus->handle;
1490	bool supported;
1491
1492	if (asus->is_pega_lucid) {
1493		/* no ls_level interface on the Lucid */
1494		if (attr == &dev_attr_ls_switch.attr)
1495			supported = true;
1496		else if (attr == &dev_attr_ls_level.attr)
1497			supported = false;
1498		else
1499			goto normal;
1500
1501		return supported;
1502	}
1503
1504normal:
1505	if (attr == &dev_attr_wlan.attr) {
1506		supported = !acpi_check_handle(handle, METHOD_WLAN, NULL);
1507
1508	} else if (attr == &dev_attr_bluetooth.attr) {
1509		supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL);
1510
1511	} else if (attr == &dev_attr_display.attr) {
1512		supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL);
1513
1514	} else if (attr == &dev_attr_wimax.attr) {
1515		supported =
1516			!acpi_check_handle(asus->handle, METHOD_WIMAX, NULL);
1517
1518	} else if (attr == &dev_attr_wwan.attr) {
1519		supported = !acpi_check_handle(asus->handle, METHOD_WWAN, NULL);
1520
1521	} else if (attr == &dev_attr_ledd.attr) {
1522		supported = !acpi_check_handle(handle, METHOD_LEDD, NULL);
1523
1524	} else if (attr == &dev_attr_ls_switch.attr ||
1525		   attr == &dev_attr_ls_level.attr) {
1526		supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) &&
1527			!acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL);
1528	} else if (attr == &dev_attr_ls_value.attr) {
1529		supported = asus->is_pega_lucid;
1530	} else if (attr == &dev_attr_gps.attr) {
1531		supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) &&
1532			    !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) &&
1533			    !acpi_check_handle(handle, METHOD_GPS_STATUS, NULL);
1534	} else {
1535		supported = true;
1536	}
1537
1538	return supported ? attr->mode : 0;
1539}
1540
1541
1542static const struct attribute_group asus_attr_group = {
1543	.is_visible	= asus_sysfs_is_visible,
1544	.attrs		= asus_attributes,
1545};
1546
1547static int asus_platform_init(struct asus_laptop *asus)
1548{
1549	int result;
1550
1551	asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1);
1552	if (!asus->platform_device)
1553		return -ENOMEM;
1554	platform_set_drvdata(asus->platform_device, asus);
1555
1556	result = platform_device_add(asus->platform_device);
1557	if (result)
1558		goto fail_platform_device;
1559
1560	result = sysfs_create_group(&asus->platform_device->dev.kobj,
1561				    &asus_attr_group);
1562	if (result)
1563		goto fail_sysfs;
1564
1565	return 0;
1566
1567fail_sysfs:
1568	platform_device_del(asus->platform_device);
1569fail_platform_device:
1570	platform_device_put(asus->platform_device);
1571	return result;
1572}
1573
1574static void asus_platform_exit(struct asus_laptop *asus)
1575{
1576	sysfs_remove_group(&asus->platform_device->dev.kobj, &asus_attr_group);
1577	platform_device_unregister(asus->platform_device);
1578}
1579
1580static struct platform_driver platform_driver = {
1581	.driver = {
1582		.name = ASUS_LAPTOP_FILE,
1583		.owner = THIS_MODULE,
1584	},
1585};
1586
1587/*
1588 * This function is used to initialize the context with right values. In this
1589 * method, we can make all the detection we want, and modify the asus_laptop
1590 * struct
1591 */
1592static int asus_laptop_get_info(struct asus_laptop *asus)
1593{
1594	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1595	union acpi_object *model = NULL;
1596	unsigned long long bsts_result, hwrs_result;
1597	char *string = NULL;
1598	acpi_status status;
1599
1600	/*
1601	 * Get DSDT headers early enough to allow for differentiating between
1602	 * models, but late enough to allow acpi_bus_register_driver() to fail
1603	 * before doing anything ACPI-specific. Should we encounter a machine,
1604	 * which needs special handling (i.e. its hotkey device has a different
1605	 * HID), this bit will be moved.
1606	 */
1607	status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus->dsdt_info);
1608	if (ACPI_FAILURE(status))
1609		pr_warn("Couldn't get the DSDT table header\n");
1610
1611	/* We have to write 0 on init this far for all ASUS models */
1612	if (write_acpi_int_ret(asus->handle, "INIT", 0, &buffer)) {
1613		pr_err("Hotkey initialization failed\n");
1614		return -ENODEV;
1615	}
1616
1617	/* This needs to be called for some laptops to init properly */
1618	status =
1619	    acpi_evaluate_integer(asus->handle, "BSTS", NULL, &bsts_result);
1620	if (ACPI_FAILURE(status))
1621		pr_warn("Error calling BSTS\n");
1622	else if (bsts_result)
1623		pr_notice("BSTS called, 0x%02x returned\n",
1624		       (uint) bsts_result);
1625
1626	/* This too ... */
1627	if (write_acpi_int(asus->handle, "CWAP", wapf))
1628		pr_err("Error calling CWAP(%d)\n", wapf);
1629	/*
1630	 * Try to match the object returned by INIT to the specific model.
1631	 * Handle every possible object (or the lack of thereof) the DSDT
1632	 * writers might throw at us. When in trouble, we pass NULL to
1633	 * asus_model_match() and try something completely different.
1634	 */
1635	if (buffer.pointer) {
1636		model = buffer.pointer;
1637		switch (model->type) {
1638		case ACPI_TYPE_STRING:
1639			string = model->string.pointer;
1640			break;
1641		case ACPI_TYPE_BUFFER:
1642			string = model->buffer.pointer;
1643			break;
1644		default:
1645			string = "";
1646			break;
1647		}
1648	}
1649	asus->name = kstrdup(string, GFP_KERNEL);
1650	if (!asus->name) {
1651		kfree(buffer.pointer);
1652		return -ENOMEM;
1653	}
1654
1655	if (*string)
1656		pr_notice("  %s model detected\n", string);
1657
1658	/*
1659	 * The HWRS method return informations about the hardware.
1660	 * 0x80 bit is for WLAN, 0x100 for Bluetooth,
1661	 * 0x40 for WWAN, 0x10 for WIMAX.
1662	 * The significance of others is yet to be found.
1663	 */
1664	status =
1665	    acpi_evaluate_integer(asus->handle, "HRWS", NULL, &hwrs_result);
1666	if (!ACPI_FAILURE(status))
1667		pr_notice("  HRWS returned %x", (int)hwrs_result);
1668
1669	if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL))
1670		asus->have_rsts = true;
1671
1672	kfree(model);
1673
1674	return AE_OK;
1675}
1676
1677static int __devinit asus_acpi_init(struct asus_laptop *asus)
1678{
1679	int result = 0;
1680
1681	result = acpi_bus_get_status(asus->device);
1682	if (result)
1683		return result;
1684	if (!asus->device->status.present) {
1685		pr_err("Hotkey device not present, aborting\n");
1686		return -ENODEV;
1687	}
1688
1689	result = asus_laptop_get_info(asus);
1690	if (result)
1691		return result;
1692
1693	/* WLED and BLED are on by default */
1694	if (bluetooth_status >= 0)
1695		asus_bluetooth_set(asus, !!bluetooth_status);
1696
1697	if (wlan_status >= 0)
1698		asus_wlan_set(asus, !!wlan_status);
1699
1700	if (wimax_status >= 0)
1701		asus_wimax_set(asus, !!wimax_status);
1702
1703	if (wwan_status >= 0)
1704		asus_wwan_set(asus, !!wwan_status);
1705
1706	/* Keyboard Backlight is on by default */
1707	if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL))
1708		asus_kled_set(asus, 1);
1709
1710	/* LED display is off by default */
1711	asus->ledd_status = 0xFFF;
1712
1713	/* Set initial values of light sensor and level */
1714	asus->light_switch = !!als_status;
1715	asus->light_level = 5;	/* level 5 for sensor sensitivity */
1716
1717	if (asus->is_pega_lucid) {
1718		asus_als_switch(asus, asus->light_switch);
1719	} else if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
1720		   !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
1721		asus_als_switch(asus, asus->light_switch);
1722		asus_als_level(asus, asus->light_level);
1723	}
1724
1725	return result;
1726}
1727
1728static void __devinit asus_dmi_check(void)
1729{
1730	const char *model;
1731
1732	model = dmi_get_system_info(DMI_PRODUCT_NAME);
1733	if (!model)
1734		return;
1735
1736	/* On L1400B WLED control the sound card, don't mess with it ... */
1737	if (strncmp(model, "L1400B", 6) == 0) {
1738		wlan_status = -1;
1739	}
1740}
1741
1742static bool asus_device_present;
1743
1744static int __devinit asus_acpi_add(struct acpi_device *device)
1745{
1746	struct asus_laptop *asus;
1747	int result;
1748
1749	pr_notice("Asus Laptop Support version %s\n",
1750		  ASUS_LAPTOP_VERSION);
1751	asus = kzalloc(sizeof(struct asus_laptop), GFP_KERNEL);
1752	if (!asus)
1753		return -ENOMEM;
1754	asus->handle = device->handle;
1755	strcpy(acpi_device_name(device), ASUS_LAPTOP_DEVICE_NAME);
1756	strcpy(acpi_device_class(device), ASUS_LAPTOP_CLASS);
1757	device->driver_data = asus;
1758	asus->device = device;
1759
1760	asus_dmi_check();
1761
1762	result = asus_acpi_init(asus);
1763	if (result)
1764		goto fail_platform;
1765
1766	/*
1767	 * Need platform type detection first, then the platform
1768	 * device.  It is used as a parent for the sub-devices below.
1769	 */
1770	asus->is_pega_lucid = asus_check_pega_lucid(asus);
1771	result = asus_platform_init(asus);
1772	if (result)
1773		goto fail_platform;
1774
1775	if (!acpi_video_backlight_support()) {
1776		result = asus_backlight_init(asus);
1777		if (result)
1778			goto fail_backlight;
1779	} else
1780		pr_info("Backlight controlled by ACPI video driver\n");
1781
1782	result = asus_input_init(asus);
1783	if (result)
1784		goto fail_input;
1785
1786	result = asus_led_init(asus);
1787	if (result)
1788		goto fail_led;
1789
1790	result = asus_rfkill_init(asus);
1791	if (result)
1792		goto fail_rfkill;
1793
1794	result = pega_accel_init(asus);
1795	if (result && result != -ENODEV)
1796		goto fail_pega_accel;
1797
1798	result = pega_rfkill_init(asus);
1799	if (result && result != -ENODEV)
1800		goto fail_pega_rfkill;
1801
1802	asus_device_present = true;
1803	return 0;
1804
1805fail_pega_rfkill:
1806	pega_accel_exit(asus);
1807fail_pega_accel:
1808	asus_rfkill_exit(asus);
1809fail_rfkill:
1810	asus_led_exit(asus);
1811fail_led:
1812	asus_input_exit(asus);
1813fail_input:
1814	asus_backlight_exit(asus);
1815fail_backlight:
1816	asus_platform_exit(asus);
1817fail_platform:
1818	kfree(asus->name);
1819	kfree(asus);
1820
1821	return result;
1822}
1823
1824static int asus_acpi_remove(struct acpi_device *device, int type)
1825{
1826	struct asus_laptop *asus = acpi_driver_data(device);
1827
1828	asus_backlight_exit(asus);
1829	asus_rfkill_exit(asus);
1830	asus_led_exit(asus);
1831	asus_input_exit(asus);
1832	pega_accel_exit(asus);
1833	pega_rfkill_exit(asus);
1834	asus_platform_exit(asus);
1835
1836	kfree(asus->name);
1837	kfree(asus);
1838	return 0;
1839}
1840
1841static const struct acpi_device_id asus_device_ids[] = {
1842	{"ATK0100", 0},
1843	{"ATK0101", 0},
1844	{"", 0},
1845};
1846MODULE_DEVICE_TABLE(acpi, asus_device_ids);
1847
1848static struct acpi_driver asus_acpi_driver = {
1849	.name = ASUS_LAPTOP_NAME,
1850	.class = ASUS_LAPTOP_CLASS,
1851	.owner = THIS_MODULE,
1852	.ids = asus_device_ids,
1853	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
1854	.ops = {
1855		.add = asus_acpi_add,
1856		.remove = asus_acpi_remove,
1857		.notify = asus_acpi_notify,
1858		},
1859};
1860
1861static int __init asus_laptop_init(void)
1862{
1863	int result;
1864
1865	result = platform_driver_register(&platform_driver);
1866	if (result < 0)
1867		return result;
1868
1869	result = acpi_bus_register_driver(&asus_acpi_driver);
1870	if (result < 0)
1871		goto fail_acpi_driver;
1872	if (!asus_device_present) {
1873		result = -ENODEV;
1874		goto fail_no_device;
1875	}
1876	return 0;
1877
1878fail_no_device:
1879	acpi_bus_unregister_driver(&asus_acpi_driver);
1880fail_acpi_driver:
1881	platform_driver_unregister(&platform_driver);
1882	return result;
1883}
1884
1885static void __exit asus_laptop_exit(void)
1886{
1887	acpi_bus_unregister_driver(&asus_acpi_driver);
1888	platform_driver_unregister(&platform_driver);
1889}
1890
1891module_init(asus_laptop_init);
1892module_exit(asus_laptop_exit);
1893