diff options
Diffstat (limited to 'drivers/platform/x86/asus-laptop.c')
| -rw-r--r-- | drivers/platform/x86/asus-laptop.c | 950 | 
1 files changed, 680 insertions, 270 deletions
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 60a5a5c6b50..7f4dc6f51f8 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -4,6 +4,7 @@   *   *  Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor   *  Copyright (C) 2006-2007 Corentin Chary + *  Copyright (C) 2011 Wind River Systems   *   *  This program is free software; you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -29,7 +30,7 @@   *  John Belmonte  - ACPI code for Toshiba laptop was a good starting point.   *  Eric Burghard  - LED display support for W1N   *  Josh Green     - Light Sens support - *  Thomas Tuttle  - His first patch for led support was very helpfull + *  Thomas Tuttle  - His first patch for led support was very helpful   *  Sam Lin        - GPS support   */ @@ -48,10 +49,11 @@  #include <linux/uaccess.h>  #include <linux/input.h>  #include <linux/input/sparse-keymap.h> +#include <linux/input-polldev.h>  #include <linux/rfkill.h>  #include <linux/slab.h> -#include <acpi/acpi_drivers.h> -#include <acpi/acpi_bus.h> +#include <linux/dmi.h> +#include <linux/acpi.h>  #define ASUS_LAPTOP_VERSION	"0.42" @@ -69,36 +71,68 @@ MODULE_LICENSE("GPL");   * WAPF defines the behavior of the Fn+Fx wlan key   * The significance of values is yet to be found, but   * most of the time: - * 0x0 will do nothing - * 0x1 will allow to control the device with Fn+Fx key. - * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key - * 0x5 like 0x1 or 0x4 - * So, if something doesn't work as you want, just try other values =) + * Bit | Bluetooth | WLAN + *  0  | Hardware  | Hardware + *  1  | Hardware  | Software + *  4  | Software  | Software   */  static uint wapf = 1;  module_param(wapf, uint, 0444);  MODULE_PARM_DESC(wapf, "WAPF value"); +static char *wled_type = "unknown"; +static char *bled_type = "unknown"; + +module_param(wled_type, charp, 0444); +MODULE_PARM_DESC(wled_type, "Set the wled type on boot " +		 "(unknown, led or rfkill). " +		 "default is unknown"); + +module_param(bled_type, charp, 0444); +MODULE_PARM_DESC(bled_type, "Set the bled type on boot " +		 "(unknown, led or rfkill). " +		 "default is unknown"); +  static int wlan_status = 1;  static int bluetooth_status = 1; +static int wimax_status = -1; +static int wwan_status = -1; +static int als_status;  module_param(wlan_status, int, 0444);  MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot "  		 "(0 = disabled, 1 = enabled, -1 = don't do anything). " -		 "default is 1"); +		 "default is -1");  module_param(bluetooth_status, int, 0444);  MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot "  		 "(0 = disabled, 1 = enabled, -1 = don't do anything). " -		 "default is 1"); +		 "default is -1"); + +module_param(wimax_status, int, 0444); +MODULE_PARM_DESC(wimax_status, "Set the wireless status on boot " +		 "(0 = disabled, 1 = enabled, -1 = don't do anything). " +		 "default is -1"); + +module_param(wwan_status, int, 0444); +MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " +		 "(0 = disabled, 1 = enabled, -1 = don't do anything). " +		 "default is -1"); + +module_param(als_status, int, 0444); +MODULE_PARM_DESC(als_status, "Set the ALS status on boot " +		 "(0 = disabled, 1 = enabled). " +		 "default is 0");  /*   * Some events we use, same for all Asus   */ -#define ATKD_BR_UP	0x10	/* (event & ~ATKD_BR_UP) = brightness level */ -#define ATKD_BR_DOWN	0x20	/* (event & ~ATKD_BR_DOWN) = britghness level */ -#define ATKD_BR_MIN	ATKD_BR_UP -#define ATKD_BR_MAX	(ATKD_BR_DOWN | 0xF)	/* 0x2f */ +#define ATKD_BRNUP_MIN		0x10 +#define ATKD_BRNUP_MAX		0x1f +#define ATKD_BRNDOWN_MIN	0x20 +#define ATKD_BRNDOWN_MAX	0x2f +#define ATKD_BRNDOWN		0x20 +#define ATKD_BRNUP		0x2f  #define ATKD_LCD_ON	0x33  #define ATKD_LCD_OFF	0x34 @@ -114,6 +148,13 @@ MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot "   */  #define WL_RSTS		0x01	/* internal Wifi */  #define BT_RSTS		0x02	/* internal Bluetooth */ +#define WM_RSTS		0x08    /* internal wimax */ +#define WW_RSTS		0x20    /* internal wwan */ + +/* WLED and BLED type */ +#define TYPE_UNKNOWN	0 +#define TYPE_LED	1 +#define TYPE_RFKILL	2  /* LED */  #define METHOD_MLED		"MLED" @@ -132,52 +173,20 @@ MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot "   */  #define METHOD_WLAN		"WLED"  #define METHOD_BLUETOOTH	"BLED" + +/* WWAN and WIMAX */ +#define METHOD_WWAN		"GSMC" +#define METHOD_WIMAX		"WMXC" +  #define METHOD_WL_STATUS	"RSTS"  /* Brightness */  #define METHOD_BRIGHTNESS_SET	"SPLV"  #define METHOD_BRIGHTNESS_GET	"GPLV" -/* Backlight */ -static acpi_handle lcd_switch_handle; -static char *lcd_switch_paths[] = { -  "\\_SB.PCI0.SBRG.EC0._Q10",	/* All new models */ -  "\\_SB.PCI0.ISA.EC0._Q10",	/* A1x */ -  "\\_SB.PCI0.PX40.ECD0._Q10",	/* L3C */ -  "\\_SB.PCI0.PX40.EC0.Q10",	/* M1A */ -  "\\_SB.PCI0.LPCB.EC0._Q10",	/* P30 */ -  "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */ -  "\\_SB.PCI0.PX40.Q10",	/* S1x */ -  "\\Q10"};		/* A2x, L2D, L3D, M2E */ -  /* Display */  #define METHOD_SWITCH_DISPLAY	"SDSP" -static acpi_handle display_get_handle; -static char *display_get_paths[] = { -  /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */ -  "\\_SB.PCI0.P0P1.VGA.GETD", -  /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */ -  "\\_SB.PCI0.P0P2.VGA.GETD", -  /* A6V A6Q */ -  "\\_SB.PCI0.P0P3.VGA.GETD", -  /* A6T, A6M */ -  "\\_SB.PCI0.P0PA.VGA.GETD", -  /* L3C */ -  "\\_SB.PCI0.PCI1.VGAC.NMAP", -  /* Z96F */ -  "\\_SB.PCI0.VGA.GETD", -  /* A2D */ -  "\\ACTD", -  /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ -  "\\ADVG", -  /* P30 */ -  "\\DNXT", -  /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ -  "\\INFB", -  /* A3F A6F A3N A3L M6N W3N W6A */ -  "\\SSTE"}; -  #define METHOD_ALS_CONTROL	"ALSC" /* Z71A Z71V */  #define METHOD_ALS_LEVEL	"ALSL" /* Z71A Z71V */ @@ -191,6 +200,29 @@ static char *display_get_paths[] = {  #define METHOD_KBD_LIGHT_SET	"SLKB"  #define METHOD_KBD_LIGHT_GET	"GLKB" +/* For Pegatron Lucid tablet */ +#define DEVICE_NAME_PEGA	"Lucid" + +#define METHOD_PEGA_ENABLE	"ENPR" +#define METHOD_PEGA_DISABLE	"DAPR" +#define PEGA_WLAN	0x00 +#define PEGA_BLUETOOTH	0x01 +#define PEGA_WWAN	0x02 +#define PEGA_ALS	0x04 +#define PEGA_ALS_POWER	0x05 + +#define METHOD_PEGA_READ	"RDLN" +#define PEGA_READ_ALS_H	0x02 +#define PEGA_READ_ALS_L	0x03 + +#define PEGA_ACCEL_NAME "pega_accel" +#define PEGA_ACCEL_DESC "Pegatron Lucid Tablet Accelerometer" +#define METHOD_XLRX "XLRX" +#define METHOD_XLRY "XLRY" +#define METHOD_XLRZ "XLRZ" +#define PEGA_ACC_CLAMP 512 /* 1G accel is reported as ~256, so clamp to 2G */ +#define PEGA_ACC_RETRIES 3 +  /*   * Define a specific led structure to keep the main structure clean   */ @@ -203,6 +235,16 @@ struct asus_led {  };  /* + * Same thing for rfkill + */ +struct asus_rfkill { +	/* type of control. Maps to PEGA_* values or *_RSTS  */ +	int control_id; +	struct rfkill *rfkill; +	struct asus_laptop *asus; +}; + +/*   * This is the main structure, we can use it to store anything interesting   * about the hotk device   */ @@ -216,7 +258,10 @@ struct asus_laptop {  	struct input_dev *inputdev;  	struct key_entry *keymap; +	struct input_polled_dev *pega_accel_poll; +	struct asus_led wled; +	struct asus_led bled;  	struct asus_led mled;  	struct asus_led tled;  	struct asus_led rled; @@ -225,11 +270,21 @@ struct asus_laptop {  	struct asus_led kled;  	struct workqueue_struct *led_workqueue; +	int wled_type; +	int bled_type;  	int wireless_status;  	bool have_rsts; -	int lcd_state; - -	struct rfkill *gps_rfkill; +	bool is_pega_lucid; +	bool pega_acc_live; +	int pega_acc_x; +	int pega_acc_y; +	int pega_acc_z; + +	struct asus_rfkill wlan; +	struct asus_rfkill bluetooth; +	struct asus_rfkill wwan; +	struct asus_rfkill wimax; +	struct asus_rfkill gps;  	acpi_handle handle;	/* the handle of the hotk device */  	u32 ledd_status;	/* status of the LED display */ @@ -243,41 +298,69 @@ static const struct key_entry asus_keymap[] = {  	{KE_KEY, 0x02, { KEY_SCREENLOCK } },  	{KE_KEY, 0x05, { KEY_WLAN } },  	{KE_KEY, 0x08, { KEY_F13 } }, +	{KE_KEY, 0x09, { KEY_PROG2 } }, /* Dock */  	{KE_KEY, 0x17, { KEY_ZOOM } },  	{KE_KEY, 0x1f, { KEY_BATTERY } },  	/* End of Lenovo SL Specific keycodes */ +	{KE_KEY, ATKD_BRNDOWN, { KEY_BRIGHTNESSDOWN } }, +	{KE_KEY, ATKD_BRNUP, { KEY_BRIGHTNESSUP } },  	{KE_KEY, 0x30, { KEY_VOLUMEUP } },  	{KE_KEY, 0x31, { KEY_VOLUMEDOWN } },  	{KE_KEY, 0x32, { KEY_MUTE } }, -	{KE_KEY, 0x33, { KEY_SWITCHVIDEOMODE } }, -	{KE_KEY, 0x34, { KEY_SWITCHVIDEOMODE } }, +	{KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */ +	{KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */  	{KE_KEY, 0x40, { KEY_PREVIOUSSONG } },  	{KE_KEY, 0x41, { KEY_NEXTSONG } }, -	{KE_KEY, 0x43, { KEY_STOPCD } }, +	{KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */  	{KE_KEY, 0x45, { KEY_PLAYPAUSE } }, -	{KE_KEY, 0x4c, { KEY_MEDIA } }, +	{KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */  	{KE_KEY, 0x50, { KEY_EMAIL } },  	{KE_KEY, 0x51, { KEY_WWW } },  	{KE_KEY, 0x55, { KEY_CALC } }, +	{KE_IGNORE, 0x57, },  /* Battery mode */ +	{KE_IGNORE, 0x58, },  /* AC mode */  	{KE_KEY, 0x5C, { KEY_SCREENLOCK } },  /* Screenlock */ -	{KE_KEY, 0x5D, { KEY_WLAN } }, -	{KE_KEY, 0x5E, { KEY_WLAN } }, -	{KE_KEY, 0x5F, { KEY_WLAN } }, -	{KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } }, -	{KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, -	{KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, -	{KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, -	{KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */ -	{KE_KEY, 0x7E, { KEY_BLUETOOTH } }, -	{KE_KEY, 0x7D, { KEY_BLUETOOTH } }, +	{KE_KEY, 0x5D, { KEY_WLAN } }, /* WLAN Toggle */ +	{KE_KEY, 0x5E, { KEY_WLAN } }, /* WLAN Enable */ +	{KE_KEY, 0x5F, { KEY_WLAN } }, /* WLAN Disable */ +	{KE_KEY, 0x60, { KEY_TOUCHPAD_ON } }, +	{KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */ +	{KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */ +	{KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */ +	{KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */ +	{KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */ +	{KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */ +	{KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */ +	{KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, /* Lock Touchpad */ +	{KE_KEY, 0x6C, { KEY_SLEEP } }, /* Suspend */ +	{KE_KEY, 0x6D, { KEY_SLEEP } }, /* Hibernate */ +	{KE_IGNORE, 0x6E, },  /* Low Battery notification */ +	{KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ +	{KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */  	{KE_KEY, 0x82, { KEY_CAMERA } }, -	{KE_KEY, 0x88, { KEY_WLAN  } }, -	{KE_KEY, 0x8A, { KEY_PROG1 } }, +	{KE_KEY, 0x88, { KEY_RFKILL  } }, /* Radio Toggle Key */ +	{KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */ +	{KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */ +	{KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */ +	{KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */ +	{KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */ +	{KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */ +	{KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */ +	{KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */ +	{KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */  	{KE_KEY, 0x95, { KEY_MEDIA } },  	{KE_KEY, 0x99, { KEY_PHONE } }, -	{KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, -	{KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, -	{KE_KEY, 0xb5, { KEY_CALC } }, +	{KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */ +	{KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */ +	{KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */ +	{KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */ +	{KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */ +	{KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */ +	{KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */ +	{KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */ +	{KE_KEY, 0xB5, { KEY_CALC } }, +	{KE_KEY, 0xC4, { KEY_KBDILLUMUP } }, +	{KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },  	{KE_END, 0},  }; @@ -336,12 +419,133 @@ static int acpi_check_handle(acpi_handle handle, const char *method,  	if (status != AE_OK) {  		if (ret) -			pr_warning("Error finding %s\n", method); +			pr_warn("Error finding %s\n", method);  		return -ENODEV;  	}  	return 0;  } +static bool asus_check_pega_lucid(struct asus_laptop *asus) +{ +	return !strcmp(asus->name, DEVICE_NAME_PEGA) && +	   !acpi_check_handle(asus->handle, METHOD_PEGA_ENABLE, NULL) && +	   !acpi_check_handle(asus->handle, METHOD_PEGA_DISABLE, NULL) && +	   !acpi_check_handle(asus->handle, METHOD_PEGA_READ, NULL); +} + +static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable) +{ +	char *method = enable ? METHOD_PEGA_ENABLE : METHOD_PEGA_DISABLE; +	return write_acpi_int(asus->handle, method, unit); +} + +static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method) +{ +	int i, delta; +	unsigned long long val; +	for (i = 0; i < PEGA_ACC_RETRIES; i++) { +		acpi_evaluate_integer(asus->handle, method, NULL, &val); + +		/* The output is noisy.  From reading the ASL +		 * dissassembly, timeout errors are returned with 1's +		 * in the high word, and the lack of locking around +		 * thei hi/lo byte reads means that a transition +		 * between (for example) -1 and 0 could be read as +		 * 0xff00 or 0x00ff. */ +		delta = abs(curr - (short)val); +		if (delta < 128 && !(val & ~0xffff)) +			break; +	} +	return clamp_val((short)val, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP); +} + +static void pega_accel_poll(struct input_polled_dev *ipd) +{ +	struct device *parent = ipd->input->dev.parent; +	struct asus_laptop *asus = dev_get_drvdata(parent); + +	/* In some cases, the very first call to poll causes a +	 * recursive fault under the polldev worker.  This is +	 * apparently related to very early userspace access to the +	 * device, and perhaps a firmware bug. Fake the first report. */ +	if (!asus->pega_acc_live) { +		asus->pega_acc_live = true; +		input_report_abs(ipd->input, ABS_X, 0); +		input_report_abs(ipd->input, ABS_Y, 0); +		input_report_abs(ipd->input, ABS_Z, 0); +		input_sync(ipd->input); +		return; +	} + +	asus->pega_acc_x = pega_acc_axis(asus, asus->pega_acc_x, METHOD_XLRX); +	asus->pega_acc_y = pega_acc_axis(asus, asus->pega_acc_y, METHOD_XLRY); +	asus->pega_acc_z = pega_acc_axis(asus, asus->pega_acc_z, METHOD_XLRZ); + +	/* Note transform, convert to "right/up/out" in the native +	 * landscape orientation (i.e. the vector is the direction of +	 * "real up" in the device's cartiesian coordinates). */ +	input_report_abs(ipd->input, ABS_X, -asus->pega_acc_x); +	input_report_abs(ipd->input, ABS_Y, -asus->pega_acc_y); +	input_report_abs(ipd->input, ABS_Z,  asus->pega_acc_z); +	input_sync(ipd->input); +} + +static void pega_accel_exit(struct asus_laptop *asus) +{ +	if (asus->pega_accel_poll) { +		input_unregister_polled_device(asus->pega_accel_poll); +		input_free_polled_device(asus->pega_accel_poll); +	} +	asus->pega_accel_poll = NULL; +} + +static int pega_accel_init(struct asus_laptop *asus) +{ +	int err; +	struct input_polled_dev *ipd; + +	if (!asus->is_pega_lucid) +		return -ENODEV; + +	if (acpi_check_handle(asus->handle, METHOD_XLRX, NULL) || +	    acpi_check_handle(asus->handle, METHOD_XLRY, NULL) || +	    acpi_check_handle(asus->handle, METHOD_XLRZ, NULL)) +		return -ENODEV; + +	ipd = input_allocate_polled_device(); +	if (!ipd) +		return -ENOMEM; + +	ipd->poll = pega_accel_poll; +	ipd->poll_interval = 125; +	ipd->poll_interval_min = 50; +	ipd->poll_interval_max = 2000; + +	ipd->input->name = PEGA_ACCEL_DESC; +	ipd->input->phys = PEGA_ACCEL_NAME "/input0"; +	ipd->input->dev.parent = &asus->platform_device->dev; +	ipd->input->id.bustype = BUS_HOST; + +	set_bit(EV_ABS, ipd->input->evbit); +	input_set_abs_params(ipd->input, ABS_X, +			     -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); +	input_set_abs_params(ipd->input, ABS_Y, +			     -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); +	input_set_abs_params(ipd->input, ABS_Z, +			     -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); + +	err = input_register_polled_device(ipd); +	if (err) +		goto exit; + +	asus->pega_accel_poll = ipd; +	return 0; + +exit: +	input_free_polled_device(ipd); +	return err; +} +  /* Generic LED function */  static int asus_led_set(struct asus_laptop *asus, const char *method,  			 int value) @@ -401,7 +605,7 @@ static int asus_kled_lvl(struct asus_laptop *asus)  	rv = acpi_evaluate_integer(asus->handle, METHOD_KBD_LIGHT_GET,  				   ¶ms, &kblv);  	if (ACPI_FAILURE(rv)) { -		pr_warning("Error reading kled level\n"); +		pr_warn("Error reading kled level\n");  		return -ENODEV;  	}  	return kblv; @@ -415,7 +619,7 @@ static int asus_kled_set(struct asus_laptop *asus, int kblv)  		kblv = 0;  	if (write_acpi_int(asus->handle, METHOD_KBD_LIGHT_SET, kblv)) { -		pr_warning("Keyboard LED display write failed\n"); +		pr_warn("Keyboard LED display write failed\n");  		return -EINVAL;  	}  	return 0; @@ -449,17 +653,21 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev)  static void asus_led_exit(struct asus_laptop *asus)  { -	if (asus->mled.led.dev) +	if (!IS_ERR_OR_NULL(asus->wled.led.dev)) +		led_classdev_unregister(&asus->wled.led); +	if (!IS_ERR_OR_NULL(asus->bled.led.dev)) +		led_classdev_unregister(&asus->bled.led); +	if (!IS_ERR_OR_NULL(asus->mled.led.dev))  		led_classdev_unregister(&asus->mled.led); -	if (asus->tled.led.dev) +	if (!IS_ERR_OR_NULL(asus->tled.led.dev))  		led_classdev_unregister(&asus->tled.led); -	if (asus->pled.led.dev) +	if (!IS_ERR_OR_NULL(asus->pled.led.dev))  		led_classdev_unregister(&asus->pled.led); -	if (asus->rled.led.dev) +	if (!IS_ERR_OR_NULL(asus->rled.led.dev))  		led_classdev_unregister(&asus->rled.led); -	if (asus->gled.led.dev) +	if (!IS_ERR_OR_NULL(asus->gled.led.dev))  		led_classdev_unregister(&asus->gled.led); -	if (asus->kled.led.dev) +	if (!IS_ERR_OR_NULL(asus->kled.led.dev))  		led_classdev_unregister(&asus->kled.led);  	if (asus->led_workqueue) {  		destroy_workqueue(asus->led_workqueue); @@ -490,7 +698,14 @@ static int asus_led_register(struct asus_laptop *asus,  static int asus_led_init(struct asus_laptop *asus)  { -	int r; +	int r = 0; + +	/* +	 * The Pegatron Lucid has no physical leds, but all methods are +	 * available in the DSDT... +	 */ +	if (asus->is_pega_lucid) +		return 0;  	/*  	 * Functions that actually update the LED's are called from a @@ -502,6 +717,16 @@ static int asus_led_init(struct asus_laptop *asus)  	if (!asus->led_workqueue)  		return -ENOMEM; +	if (asus->wled_type == TYPE_LED) +		r = asus_led_register(asus, &asus->wled, "asus::wlan", +				      METHOD_WLAN); +	if (r) +		goto error; +	if (asus->bled_type == TYPE_LED) +		r = asus_led_register(asus, &asus->bled, "asus::bluetooth", +				      METHOD_BLUETOOTH); +	if (r) +		goto error;  	r = asus_led_register(asus, &asus->mled, "asus::mail", METHOD_MLED);  	if (r)  		goto error; @@ -540,48 +765,6 @@ error:  /*   * Backlight device   */ -static int asus_lcd_status(struct asus_laptop *asus) -{ -	return asus->lcd_state; -} - -static int asus_lcd_set(struct asus_laptop *asus, int value) -{ -	int lcd = 0; -	acpi_status status = 0; - -	lcd = !!value; - -	if (lcd == asus_lcd_status(asus)) -		return 0; - -	if (!lcd_switch_handle) -		return -ENODEV; - -	status = acpi_evaluate_object(lcd_switch_handle, -				      NULL, NULL, NULL); - -	if (ACPI_FAILURE(status)) { -		pr_warning("Error switching LCD\n"); -		return -ENODEV; -	} - -	asus->lcd_state = lcd; -	return 0; -} - -static void lcd_blank(struct asus_laptop *asus, int blank) -{ -	struct backlight_device *bd = asus->backlight_device; - -	asus->lcd_state = (blank == FB_BLANK_UNBLANK); - -	if (bd) { -		bd->props.power = blank; -		backlight_update_status(bd); -	} -} -  static int asus_read_brightness(struct backlight_device *bd)  {  	struct asus_laptop *asus = bl_get_data(bd); @@ -591,7 +774,7 @@ static int asus_read_brightness(struct backlight_device *bd)  	rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET,  				   NULL, &value);  	if (ACPI_FAILURE(rv)) -		pr_warning("Error reading brightness\n"); +		pr_warn("Error reading brightness\n");  	return value;  } @@ -601,7 +784,7 @@ static int asus_set_brightness(struct backlight_device *bd, int value)  	struct asus_laptop *asus = bl_get_data(bd);  	if (write_acpi_int(asus->handle, METHOD_BRIGHTNESS_SET, value)) { -		pr_warning("Error changing brightness\n"); +		pr_warn("Error changing brightness\n");  		return -EIO;  	}  	return 0; @@ -609,19 +792,12 @@ static int asus_set_brightness(struct backlight_device *bd, int value)  static int update_bl_status(struct backlight_device *bd)  { -	struct asus_laptop *asus = bl_get_data(bd); -	int rv;  	int value = bd->props.brightness; -	rv = asus_set_brightness(bd, value); -	if (rv) -		return rv; - -	value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0; -	return asus_lcd_set(asus, value); +	return asus_set_brightness(bd, value);  } -static struct backlight_ops asusbl_ops = { +static const struct backlight_ops asusbl_ops = {  	.get_brightness = asus_read_brightness,  	.update_status = update_bl_status,  }; @@ -642,12 +818,12 @@ static int asus_backlight_init(struct asus_laptop *asus)  	struct backlight_properties props;  	if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) || -	    acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) || -	    !lcd_switch_handle) +	    acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL))  		return 0;  	memset(&props, 0, sizeof(struct backlight_properties));  	props.max_brightness = 15; +	props.type = BACKLIGHT_PLATFORM;  	bd = backlight_device_register(ASUS_LAPTOP_FILE,  				       &asus->platform_device->dev, asus, @@ -710,12 +886,14 @@ static ssize_t show_infos(struct device *dev,  	/*  	 * The HWRS method return informations about the hardware.  	 * 0x80 bit is for WLAN, 0x100 for Bluetooth. +	 * 0x40 for WWAN, 0x10 for WIMAX.  	 * The significance of others is yet to be found. -	 * If we don't find the method, we assume the device are present. +	 * We don't currently use this for device detection, and it +	 * takes several seconds to run on some systems.  	 */ -	rv = acpi_evaluate_integer(asus->handle, "HRWS", NULL, &temp); +	rv = acpi_evaluate_integer(asus->handle, "HWRS", NULL, &temp);  	if (!ACPI_FAILURE(rv)) -		len += sprintf(page + len, "HRWS value         : %#x\n", +		len += sprintf(page + len, "HWRS value         : %#x\n",  			       (uint) temp);  	/*  	 * Another value for userspace: the ASYM method returns 0x02 for @@ -797,7 +975,7 @@ static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,  	rv = parse_arg(buf, count, &value);  	if (rv > 0) {  		if (write_acpi_int(asus->handle, METHOD_LEDD, value)) { -			pr_warning("LED display write failed\n"); +			pr_warn("LED display write failed\n");  			return -ENODEV;  		}  		asus->ledd_status = (u32) value; @@ -819,7 +997,7 @@ static int asus_wireless_status(struct asus_laptop *asus, int mask)  	rv = acpi_evaluate_integer(asus->handle, METHOD_WL_STATUS,  				   NULL, &status);  	if (ACPI_FAILURE(rv)) { -		pr_warning("Error reading Wireless status\n"); +		pr_warn("Error reading Wireless status\n");  		return -EINVAL;  	}  	return !!(status & mask); @@ -831,7 +1009,7 @@ static int asus_wireless_status(struct asus_laptop *asus, int mask)  static int asus_wlan_set(struct asus_laptop *asus, int status)  {  	if (write_acpi_int(asus->handle, METHOD_WLAN, !!status)) { -		pr_warning("Error setting wlan status to %d", status); +		pr_warn("Error setting wlan status to %d\n", status);  		return -EIO;  	}  	return 0; @@ -853,13 +1031,13 @@ static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,  	return sysfs_acpi_set(asus, buf, count, METHOD_WLAN);  } -/* +/*e   * Bluetooth   */  static int asus_bluetooth_set(struct asus_laptop *asus, int status)  {  	if (write_acpi_int(asus->handle, METHOD_BLUETOOTH, !!status)) { -		pr_warning("Error setting bluetooth status to %d", status); +		pr_warn("Error setting bluetooth status to %d\n", status);  		return -EIO;  	}  	return 0; @@ -883,49 +1061,72 @@ static ssize_t store_bluetooth(struct device *dev,  }  /* - * Display + * Wimax   */ -static void asus_set_display(struct asus_laptop *asus, int value) +static int asus_wimax_set(struct asus_laptop *asus, int status)  { -	/* no sanity check needed for now */ -	if (write_acpi_int(asus->handle, METHOD_SWITCH_DISPLAY, value)) -		pr_warning("Error setting display\n"); -	return; +	if (write_acpi_int(asus->handle, METHOD_WIMAX, !!status)) { +		pr_warn("Error setting wimax status to %d\n", status); +		return -EIO; +	} +	return 0;  } -static int read_display(struct asus_laptop *asus) +static ssize_t show_wimax(struct device *dev, +			      struct device_attribute *attr, char *buf)  { -	unsigned long long value = 0; -	acpi_status rv = AE_OK; +	struct asus_laptop *asus = dev_get_drvdata(dev); -	/* -	 * In most of the case, we know how to set the display, but sometime -	 * we can't read it -	 */ -	if (display_get_handle) { -		rv = acpi_evaluate_integer(display_get_handle, NULL, -					   NULL, &value); -		if (ACPI_FAILURE(rv)) -			pr_warning("Error reading display status\n"); -	} +	return sprintf(buf, "%d\n", asus_wireless_status(asus, WM_RSTS)); +} -	value &= 0x0F; /* needed for some models, shouldn't hurt others */ +static ssize_t store_wimax(struct device *dev, +			       struct device_attribute *attr, const char *buf, +			       size_t count) +{ +	struct asus_laptop *asus = dev_get_drvdata(dev); -	return value; +	return sysfs_acpi_set(asus, buf, count, METHOD_WIMAX);  }  /* - * Now, *this* one could be more user-friendly, but so far, no-one has - * complained. The significance of bits is the same as in store_disp() + * Wwan   */ -static ssize_t show_disp(struct device *dev, -			 struct device_attribute *attr, char *buf) +static int asus_wwan_set(struct asus_laptop *asus, int status) +{ +	if (write_acpi_int(asus->handle, METHOD_WWAN, !!status)) { +		pr_warn("Error setting wwan status to %d\n", status); +		return -EIO; +	} +	return 0; +} + +static ssize_t show_wwan(struct device *dev, +			      struct device_attribute *attr, char *buf)  {  	struct asus_laptop *asus = dev_get_drvdata(dev); -	if (!display_get_handle) -		return -ENODEV; -	return sprintf(buf, "%d\n", read_display(asus)); +	return sprintf(buf, "%d\n", asus_wireless_status(asus, WW_RSTS)); +} + +static ssize_t store_wwan(struct device *dev, +			       struct device_attribute *attr, const char *buf, +			       size_t count) +{ +	struct asus_laptop *asus = dev_get_drvdata(dev); + +	return sysfs_acpi_set(asus, buf, count, METHOD_WWAN); +} + +/* + * Display + */ +static void asus_set_display(struct asus_laptop *asus, int value) +{ +	/* no sanity check needed for now */ +	if (write_acpi_int(asus->handle, METHOD_SWITCH_DISPLAY, value)) +		pr_warn("Error setting display\n"); +	return;  }  /* @@ -952,8 +1153,18 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr,   */  static void asus_als_switch(struct asus_laptop *asus, int value)  { -	if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value)) +	int ret; + +	if (asus->is_pega_lucid) { +		ret = asus_pega_lucid_set(asus, PEGA_ALS, value); +		if (!ret) +			ret = asus_pega_lucid_set(asus, PEGA_ALS_POWER, value); +	} else { +		ret = write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value); +	} +	if (ret)  		pr_warning("Error setting light sensor switch\n"); +  	asus->light_switch = value;  } @@ -981,7 +1192,7 @@ static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,  static void asus_als_level(struct asus_laptop *asus, int value)  {  	if (write_acpi_int(asus->handle, METHOD_ALS_LEVEL, value)) -		pr_warning("Error setting light sensor level\n"); +		pr_warn("Error setting light sensor level\n");  	asus->light_level = value;  } @@ -1009,6 +1220,35 @@ static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,  	return rv;  } +static int pega_int_read(struct asus_laptop *asus, int arg, int *result) +{ +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; +	int err = write_acpi_int_ret(asus->handle, METHOD_PEGA_READ, arg, +				     &buffer); +	if (!err) { +		union acpi_object *obj = buffer.pointer; +		if (obj && obj->type == ACPI_TYPE_INTEGER) +			*result = obj->integer.value; +		else +			err = -EIO; +	} +	return err; +} + +static ssize_t show_lsvalue(struct device *dev, +			    struct device_attribute *attr, char *buf) +{ +	struct asus_laptop *asus = dev_get_drvdata(dev); +	int err, hi, lo; + +	err = pega_int_read(asus, PEGA_READ_ALS_H, &hi); +	if (!err) +		err = pega_int_read(asus, PEGA_READ_ALS_L, &lo); +	if (!err) +		return sprintf(buf, "%d\n", 10 * hi + lo); +	return err; +} +  /*   * GPS   */ @@ -1020,7 +1260,7 @@ static int asus_gps_status(struct asus_laptop *asus)  	rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS,  				   NULL, &status);  	if (ACPI_FAILURE(rv)) { -		pr_warning("Error reading GPS status\n"); +		pr_warn("Error reading GPS status\n");  		return -ENODEV;  	}  	return !!status; @@ -1056,7 +1296,7 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr,  	ret = asus_gps_switch(asus, !!value);  	if (ret)  		return ret; -	rfkill_set_sw_state(asus->gps_rfkill, !value); +	rfkill_set_sw_state(asus->gps.rfkill, !value);  	return rv;  } @@ -1074,46 +1314,177 @@ static const struct rfkill_ops asus_gps_rfkill_ops = {  	.set_block = asus_gps_rfkill_set,  }; +static int asus_rfkill_set(void *data, bool blocked) +{ +	struct asus_rfkill *rfk = data; +	struct asus_laptop *asus = rfk->asus; + +	if (rfk->control_id == WL_RSTS) +		return asus_wlan_set(asus, !blocked); +	else if (rfk->control_id == BT_RSTS) +		return asus_bluetooth_set(asus, !blocked); +	else if (rfk->control_id == WM_RSTS) +		return asus_wimax_set(asus, !blocked); +	else if (rfk->control_id == WW_RSTS) +		return asus_wwan_set(asus, !blocked); + +	return -EINVAL; +} + +static const struct rfkill_ops asus_rfkill_ops = { +	.set_block = asus_rfkill_set, +}; + +static void asus_rfkill_terminate(struct asus_rfkill *rfk) +{ +	if (!rfk->rfkill) +		return ; + +	rfkill_unregister(rfk->rfkill); +	rfkill_destroy(rfk->rfkill); +	rfk->rfkill = NULL; +} +  static void asus_rfkill_exit(struct asus_laptop *asus)  { -	if (asus->gps_rfkill) { -		rfkill_unregister(asus->gps_rfkill); -		rfkill_destroy(asus->gps_rfkill); -		asus->gps_rfkill = NULL; -	} +	asus_rfkill_terminate(&asus->wwan); +	asus_rfkill_terminate(&asus->bluetooth); +	asus_rfkill_terminate(&asus->wlan); +	asus_rfkill_terminate(&asus->gps);  } -static int asus_rfkill_init(struct asus_laptop *asus) +static int asus_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk, +			     const char *name, int control_id, int type, +			     const struct rfkill_ops *ops)  {  	int result; -	if (acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) || -	    acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) || -	    acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) -		return 0; - -	asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, -					RFKILL_TYPE_GPS, -					&asus_gps_rfkill_ops, asus); -	if (!asus->gps_rfkill) +	rfk->control_id = control_id; +	rfk->asus = asus; +	rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev, +				   type, ops, rfk); +	if (!rfk->rfkill)  		return -EINVAL; -	result = rfkill_register(asus->gps_rfkill); +	result = rfkill_register(rfk->rfkill);  	if (result) { -		rfkill_destroy(asus->gps_rfkill); -		asus->gps_rfkill = NULL; +		rfkill_destroy(rfk->rfkill); +		rfk->rfkill = NULL;  	}  	return result;  } +static int asus_rfkill_init(struct asus_laptop *asus) +{ +	int result = 0; + +	if (asus->is_pega_lucid) +		return -ENODEV; + +	if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && +	    !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && +	    !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) +		result = asus_rfkill_setup(asus, &asus->gps, "asus-gps", +					   -1, RFKILL_TYPE_GPS, +					   &asus_gps_rfkill_ops); +	if (result) +		goto exit; + + +	if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL) && +	    asus->wled_type == TYPE_RFKILL) +		result = asus_rfkill_setup(asus, &asus->wlan, "asus-wlan", +					   WL_RSTS, RFKILL_TYPE_WLAN, +					   &asus_rfkill_ops); +	if (result) +		goto exit; + +	if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL) && +	    asus->bled_type == TYPE_RFKILL) +		result = asus_rfkill_setup(asus, &asus->bluetooth, +					   "asus-bluetooth", BT_RSTS, +					   RFKILL_TYPE_BLUETOOTH, +					   &asus_rfkill_ops); +	if (result) +		goto exit; + +	if (!acpi_check_handle(asus->handle, METHOD_WWAN, NULL)) +		result = asus_rfkill_setup(asus, &asus->wwan, "asus-wwan", +					   WW_RSTS, RFKILL_TYPE_WWAN, +					   &asus_rfkill_ops); +	if (result) +		goto exit; + +	if (!acpi_check_handle(asus->handle, METHOD_WIMAX, NULL)) +		result = asus_rfkill_setup(asus, &asus->wimax, "asus-wimax", +					   WM_RSTS, RFKILL_TYPE_WIMAX, +					   &asus_rfkill_ops); +	if (result) +		goto exit; + +exit: +	if (result) +		asus_rfkill_exit(asus); + +	return result; +} + +static int pega_rfkill_set(void *data, bool blocked) +{ +	struct asus_rfkill *rfk = data; + +	int ret = asus_pega_lucid_set(rfk->asus, rfk->control_id, !blocked); +	return ret; +} + +static const struct rfkill_ops pega_rfkill_ops = { +	.set_block = pega_rfkill_set, +}; + +static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk, +			     const char *name, int controlid, int rfkill_type) +{ +	return asus_rfkill_setup(asus, rfk, name, controlid, rfkill_type, +				 &pega_rfkill_ops); +} + +static int pega_rfkill_init(struct asus_laptop *asus) +{ +	int ret = 0; + +	if(!asus->is_pega_lucid) +		return -ENODEV; + +	ret = pega_rfkill_setup(asus, &asus->wlan, "pega-wlan", +				PEGA_WLAN, RFKILL_TYPE_WLAN); +	if(ret) +		goto exit; + +	ret = pega_rfkill_setup(asus, &asus->bluetooth, "pega-bt", +				PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH); +	if(ret) +		goto exit; + +	ret = pega_rfkill_setup(asus, &asus->wwan, "pega-wwan", +				PEGA_WWAN, RFKILL_TYPE_WWAN); + +exit: +	if (ret) +		asus_rfkill_exit(asus); + +	return ret; +} +  /*   * Input device (i.e. hotkeys)   */  static void asus_input_notify(struct asus_laptop *asus, int event)  { -	if (asus->inputdev) -		sparse_keymap_report_event(asus->inputdev, event, 1, true); +	if (!asus->inputdev) +		return ; +	if (!sparse_keymap_report_event(asus->inputdev, event, 1, true)) +		pr_info("Unknown key %x pressed\n", event);  }  static int asus_input_init(struct asus_laptop *asus) @@ -1122,10 +1493,9 @@ static int asus_input_init(struct asus_laptop *asus)  	int error;  	input = input_allocate_device(); -	if (!input) { -		pr_info("Unable to allocate input device\n"); +	if (!input)  		return -ENOMEM; -	} +  	input->name = "Asus Laptop extra buttons";  	input->phys = ASUS_LAPTOP_FILE "/input0";  	input->id.bustype = BUS_HOST; @@ -1138,7 +1508,7 @@ static int asus_input_init(struct asus_laptop *asus)  	}  	error = input_register_device(input);  	if (error) { -		pr_info("Unable to register input device\n"); +		pr_warn("Unable to register input device\n");  		goto err_free_keymap;  	} @@ -1169,32 +1539,34 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)  	struct asus_laptop *asus = acpi_driver_data(device);  	u16 count; -	/* -	 * We need to tell the backlight device when the backlight power is -	 * switched -	 */ -	if (event == ATKD_LCD_ON) -		lcd_blank(asus, FB_BLANK_UNBLANK); -	else if (event == ATKD_LCD_OFF) -		lcd_blank(asus, FB_BLANK_POWERDOWN); -  	/* TODO Find a better way to handle events count. */  	count = asus->event_count[event % 128]++; -	acpi_bus_generate_proc_event(asus->device, event, count);  	acpi_bus_generate_netlink_event(asus->device->pnp.device_class,  					dev_name(&asus->device->dev), event,  					count); -	/* Brightness events are special */ -	if (event >= ATKD_BR_MIN && event <= ATKD_BR_MAX) { +	if (event >= ATKD_BRNUP_MIN && event <= ATKD_BRNUP_MAX) +		event = ATKD_BRNUP; +	else if (event >= ATKD_BRNDOWN_MIN && +		 event <= ATKD_BRNDOWN_MAX) +		event = ATKD_BRNDOWN; -		/* Ignore them completely if the acpi video driver is used */ +	/* Brightness events are special */ +	if (event == ATKD_BRNDOWN || event == ATKD_BRNUP) {  		if (asus->backlight_device != NULL) {  			/* Update the backlight device. */  			asus_backlight_notify(asus); +			return ;  		} +	} + +	/* Accelerometer "coarse orientation change" event */ +	if (asus->pega_accel_poll && event == 0xEA) { +		kobject_uevent(&asus->pega_accel_poll->input->dev.kobj, +			       KOBJ_CHANGE);  		return ;  	} +  	asus_input_notify(asus, event);  } @@ -1202,8 +1574,11 @@ static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL);  static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan);  static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR,  		   show_bluetooth, store_bluetooth); -static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp); +static DEVICE_ATTR(wimax, S_IRUGO | S_IWUSR, show_wimax, store_wimax); +static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan); +static DEVICE_ATTR(display, S_IWUSR, NULL, store_disp);  static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); +static DEVICE_ATTR(ls_value, S_IRUGO, show_lsvalue, NULL);  static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl);  static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);  static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); @@ -1212,15 +1587,18 @@ static struct attribute *asus_attributes[] = {  	&dev_attr_infos.attr,  	&dev_attr_wlan.attr,  	&dev_attr_bluetooth.attr, +	&dev_attr_wimax.attr, +	&dev_attr_wwan.attr,  	&dev_attr_display.attr,  	&dev_attr_ledd.attr, +	&dev_attr_ls_value.attr,  	&dev_attr_ls_level.attr,  	&dev_attr_ls_switch.attr,  	&dev_attr_gps.attr,  	NULL  }; -static mode_t asus_sysfs_is_visible(struct kobject *kobj, +static umode_t asus_sysfs_is_visible(struct kobject *kobj,  				    struct attribute *attr,  				    int idx)  { @@ -1230,6 +1608,19 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj,  	acpi_handle handle = asus->handle;  	bool supported; +	if (asus->is_pega_lucid) { +		/* no ls_level interface on the Lucid */ +		if (attr == &dev_attr_ls_switch.attr) +			supported = true; +		else if (attr == &dev_attr_ls_level.attr) +			supported = false; +		else +			goto normal; + +		return supported; +	} + +normal:  	if (attr == &dev_attr_wlan.attr) {  		supported = !acpi_check_handle(handle, METHOD_WLAN, NULL); @@ -1239,14 +1630,22 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj,  	} else if (attr == &dev_attr_display.attr) {  		supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL); +	} else if (attr == &dev_attr_wimax.attr) { +		supported = +			!acpi_check_handle(asus->handle, METHOD_WIMAX, NULL); + +	} else if (attr == &dev_attr_wwan.attr) { +		supported = !acpi_check_handle(asus->handle, METHOD_WWAN, NULL); +  	} else if (attr == &dev_attr_ledd.attr) {  		supported = !acpi_check_handle(handle, METHOD_LEDD, NULL);  	} else if (attr == &dev_attr_ls_switch.attr ||  		   attr == &dev_attr_ls_level.attr) {  		supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) && -			    !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); - +			!acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); +	} else if (attr == &dev_attr_ls_value.attr) { +		supported = asus->is_pega_lucid;  	} else if (attr == &dev_attr_gps.attr) {  		supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) &&  			    !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) && @@ -1301,29 +1700,9 @@ static struct platform_driver platform_driver = {  	.driver = {  		.name = ASUS_LAPTOP_FILE,  		.owner = THIS_MODULE, -	} +	},  }; -static int asus_handle_init(char *name, acpi_handle * handle, -			    char **paths, int num_paths) -{ -	int i; -	acpi_status status; - -	for (i = 0; i < num_paths; i++) { -		status = acpi_get_handle(NULL, paths[i], handle); -		if (ACPI_SUCCESS(status)) -			return 0; -	} - -	*handle = NULL; -	return -ENODEV; -} - -#define ASUS_HANDLE_INIT(object)					\ -	asus_handle_init(#object, &object##_handle, object##_paths,	\ -			 ARRAY_SIZE(object##_paths)) -  /*   * This function is used to initialize the context with right values. In this   * method, we can make all the detection we want, and modify the asus_laptop @@ -1333,7 +1712,7 @@ static int asus_laptop_get_info(struct asus_laptop *asus)  {  	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };  	union acpi_object *model = NULL; -	unsigned long long bsts_result, hwrs_result; +	unsigned long long bsts_result;  	char *string = NULL;  	acpi_status status; @@ -1346,7 +1725,7 @@ static int asus_laptop_get_info(struct asus_laptop *asus)  	 */  	status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus->dsdt_info);  	if (ACPI_FAILURE(status)) -		pr_warning("Couldn't get the DSDT table header\n"); +		pr_warn("Couldn't get the DSDT table header\n");  	/* We have to write 0 on init this far for all ASUS models */  	if (write_acpi_int_ret(asus->handle, "INIT", 0, &buffer)) { @@ -1358,7 +1737,7 @@ static int asus_laptop_get_info(struct asus_laptop *asus)  	status =  	    acpi_evaluate_integer(asus->handle, "BSTS", NULL, &bsts_result);  	if (ACPI_FAILURE(status)) -		pr_warning("Error calling BSTS\n"); +		pr_warn("Error calling BSTS\n");  	else if (bsts_result)  		pr_notice("BSTS called, 0x%02x returned\n",  		       (uint) bsts_result); @@ -1392,32 +1771,18 @@ static int asus_laptop_get_info(struct asus_laptop *asus)  		return -ENOMEM;  	} -	if (*string) +	if (string)  		pr_notice("  %s model detected\n", string); -	/* -	 * The HWRS method return informations about the hardware. -	 * 0x80 bit is for WLAN, 0x100 for Bluetooth. -	 * The significance of others is yet to be found. -	 */ -	status = -	    acpi_evaluate_integer(asus->handle, "HRWS", NULL, &hwrs_result); -	if (!ACPI_FAILURE(status)) -		pr_notice("  HRWS returned %x", (int)hwrs_result); -  	if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL))  		asus->have_rsts = true; -	/* Scheduled for removal */ -	ASUS_HANDLE_INIT(lcd_switch); -	ASUS_HANDLE_INIT(display_get); -  	kfree(model);  	return AE_OK;  } -static int __devinit asus_acpi_init(struct asus_laptop *asus) +static int asus_acpi_init(struct asus_laptop *asus)  {  	int result = 0; @@ -1433,13 +1798,28 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)  	if (result)  		return result; -	/* WLED and BLED are on by default */ +	if (!strcmp(bled_type, "led")) +		asus->bled_type = TYPE_LED; +	else if (!strcmp(bled_type, "rfkill")) +		asus->bled_type = TYPE_RFKILL; + +	if (!strcmp(wled_type, "led")) +		asus->wled_type = TYPE_LED; +	else if (!strcmp(wled_type, "rfkill")) +		asus->wled_type = TYPE_RFKILL; +  	if (bluetooth_status >= 0)  		asus_bluetooth_set(asus, !!bluetooth_status);  	if (wlan_status >= 0)  		asus_wlan_set(asus, !!wlan_status); +	if (wimax_status >= 0) +		asus_wimax_set(asus, !!wimax_status); + +	if (wwan_status >= 0) +		asus_wwan_set(asus, !!wwan_status); +  	/* Keyboard Backlight is on by default */  	if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL))  		asus_kled_set(asus, 1); @@ -1448,22 +1828,37 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)  	asus->ledd_status = 0xFFF;  	/* Set initial values of light sensor and level */ -	asus->light_switch = 0;	/* Default to light sensor disabled */ +	asus->light_switch = !!als_status;  	asus->light_level = 5;	/* level 5 for sensor sensitivity */ -	if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && -	    !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { +	if (asus->is_pega_lucid) { +		asus_als_switch(asus, asus->light_switch); +	} else if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && +		   !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {  		asus_als_switch(asus, asus->light_switch);  		asus_als_level(asus, asus->light_level);  	} -	asus->lcd_state = 1; /* LCD should be on when the module load */  	return result;  } +static void asus_dmi_check(void) +{ +	const char *model; + +	model = dmi_get_system_info(DMI_PRODUCT_NAME); +	if (!model) +		return; + +	/* On L1400B WLED control the sound card, don't mess with it ... */ +	if (strncmp(model, "L1400B", 6) == 0) { +		wlan_status = -1; +	} +} +  static bool asus_device_present; -static int __devinit asus_acpi_add(struct acpi_device *device) +static int asus_acpi_add(struct acpi_device *device)  {  	struct asus_laptop *asus;  	int result; @@ -1479,14 +1874,17 @@ static int __devinit asus_acpi_add(struct acpi_device *device)  	device->driver_data = asus;  	asus->device = device; +	asus_dmi_check(); +  	result = asus_acpi_init(asus);  	if (result)  		goto fail_platform;  	/* -	 * Register the platform device first.  It is used as a parent for the -	 * sub-devices below. +	 * Need platform type detection first, then the platform +	 * device.  It is used as a parent for the sub-devices below.  	 */ +	asus->is_pega_lucid = asus_check_pega_lucid(asus);  	result = asus_platform_init(asus);  	if (result)  		goto fail_platform; @@ -1507,12 +1905,24 @@ static int __devinit asus_acpi_add(struct acpi_device *device)  		goto fail_led;  	result = asus_rfkill_init(asus); -	if (result) +	if (result && result != -ENODEV)  		goto fail_rfkill; +	result = pega_accel_init(asus); +	if (result && result != -ENODEV) +		goto fail_pega_accel; + +	result = pega_rfkill_init(asus); +	if (result && result != -ENODEV) +		goto fail_pega_rfkill; +  	asus_device_present = true;  	return 0; +fail_pega_rfkill: +	pega_accel_exit(asus); +fail_pega_accel: +	asus_rfkill_exit(asus);  fail_rfkill:  	asus_led_exit(asus);  fail_led: @@ -1522,13 +1932,12 @@ fail_input:  fail_backlight:  	asus_platform_exit(asus);  fail_platform: -	kfree(asus->name);  	kfree(asus);  	return result;  } -static int asus_acpi_remove(struct acpi_device *device, int type) +static int asus_acpi_remove(struct acpi_device *device)  {  	struct asus_laptop *asus = acpi_driver_data(device); @@ -1536,6 +1945,7 @@ static int asus_acpi_remove(struct acpi_device *device, int type)  	asus_rfkill_exit(asus);  	asus_led_exit(asus);  	asus_input_exit(asus); +	pega_accel_exit(asus);  	asus_platform_exit(asus);  	kfree(asus->name);  | 
