diff options
| author | Matthew Garrett <mjg@redhat.com> | 2010-03-01 09:57:40 -0500 | 
|---|---|---|
| committer | Matthew Garrett <mjg@redhat.com> | 2010-03-01 09:57:40 -0500 | 
| commit | db38a29111cb9232b1982e583fd417ea9c97357c (patch) | |
| tree | 0ed373f0093432c88e2a082c6d757905a04381a6 | |
| parent | b466301b842f3a54e219b5cc587f22233ecb1345 (diff) | |
| parent | b1a96e3606b6efc3a6e9c8559ec6660e515b01a7 (diff) | |
Merge git://git.iksaif.net/acpi4asus into x86-platform
| -rw-r--r-- | Documentation/ABI/testing/sysfs-platform-asus-laptop | 12 | ||||
| -rw-r--r-- | Documentation/ABI/testing/sysfs-platform-eeepc-laptop | 10 | ||||
| -rw-r--r-- | drivers/platform/x86/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/asus-laptop.c | 1741 | ||||
| -rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 21 | 
5 files changed, 939 insertions, 847 deletions
| diff --git a/Documentation/ABI/testing/sysfs-platform-asus-laptop b/Documentation/ABI/testing/sysfs-platform-asus-laptop index a1cb660c50cf..1d775390e856 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-laptop +++ b/Documentation/ABI/testing/sysfs-platform-asus-laptop @@ -1,4 +1,4 @@ -What:		/sys/devices/platform/asus-laptop/display +What:		/sys/devices/platform/asus_laptop/display  Date:		January 2007  KernelVersion:	2.6.20  Contact:	"Corentin Chary" <corentincj@iksaif.net> @@ -13,7 +13,7 @@ Description:  		Ex: - 0 (0000b) means no display  		    - 3 (0011b) CRT+LCD. -What:		/sys/devices/platform/asus-laptop/gps +What:		/sys/devices/platform/asus_laptop/gps  Date:		January 2007  KernelVersion:	2.6.20  Contact:	"Corentin Chary" <corentincj@iksaif.net> @@ -21,7 +21,7 @@ Description:  		Control the gps device. 1 means on, 0 means off.  Users:		Lapsus -What:		/sys/devices/platform/asus-laptop/ledd +What:		/sys/devices/platform/asus_laptop/ledd  Date:		January 2007  KernelVersion:	2.6.20  Contact:	"Corentin Chary" <corentincj@iksaif.net> @@ -29,11 +29,11 @@ Description:  		Some models like the W1N have a LED display that can be  		used to display several informations.  		To control the LED display, use the following : -		    echo 0x0T000DDD > /sys/devices/platform/asus-laptop/ +		    echo 0x0T000DDD > /sys/devices/platform/asus_laptop/  		where T control the 3 letters display, and DDD the 3 digits display.  		The DDD table can be found in Documentation/laptops/asus-laptop.txt -What:		/sys/devices/platform/asus-laptop/bluetooth +What:		/sys/devices/platform/asus_laptop/bluetooth  Date:		January 2007  KernelVersion:	2.6.20  Contact:	"Corentin Chary" <corentincj@iksaif.net> @@ -42,7 +42,7 @@ Description:  		This may control the led, the device or both.  Users:		Lapsus -What:		/sys/devices/platform/asus-laptop/wlan +What:		/sys/devices/platform/asus_laptop/wlan  Date:		January 2007  KernelVersion:	2.6.20  Contact:	"Corentin Chary" <corentincj@iksaif.net> diff --git a/Documentation/ABI/testing/sysfs-platform-eeepc-laptop b/Documentation/ABI/testing/sysfs-platform-eeepc-laptop index 7445dfb321b5..5b026c69587a 100644 --- a/Documentation/ABI/testing/sysfs-platform-eeepc-laptop +++ b/Documentation/ABI/testing/sysfs-platform-eeepc-laptop @@ -1,4 +1,4 @@ -What:		/sys/devices/platform/eeepc-laptop/disp +What:		/sys/devices/platform/eeepc/disp  Date:		May 2008  KernelVersion:	2.6.26  Contact:	"Corentin Chary" <corentincj@iksaif.net> @@ -9,21 +9,21 @@ Description:  		- 3 = LCD+CRT  		If you run X11, you should use xrandr instead. -What:		/sys/devices/platform/eeepc-laptop/camera +What:		/sys/devices/platform/eeepc/camera  Date:		May 2008  KernelVersion:	2.6.26  Contact:	"Corentin Chary" <corentincj@iksaif.net>  Description:  		Control the camera. 1 means on, 0 means off. -What:		/sys/devices/platform/eeepc-laptop/cardr +What:		/sys/devices/platform/eeepc/cardr  Date:		May 2008  KernelVersion:	2.6.26  Contact:	"Corentin Chary" <corentincj@iksaif.net>  Description:  		Control the card reader. 1 means on, 0 means off. -What:		/sys/devices/platform/eeepc-laptop/cpufv +What:		/sys/devices/platform/eeepc/cpufv  Date:		Jun 2009  KernelVersion:	2.6.31  Contact:	"Corentin Chary" <corentincj@iksaif.net> @@ -42,7 +42,7 @@ Description:  		    `------------ Availables modes  		For example, 0x301 means: mode 1 selected, 3 available modes. -What:		/sys/devices/platform/eeepc-laptop/available_cpufv +What:		/sys/devices/platform/eeepc/available_cpufv  Date:		Jun 2009  KernelVersion:	2.6.31  Contact:	"Corentin Chary" <corentincj@iksaif.net> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 6848f213eb53..aef4f17cdfd4 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -59,6 +59,8 @@ config ASUS_LAPTOP  	select NEW_LEDS  	select BACKLIGHT_CLASS_DEVICE  	depends on INPUT +	depends on RFKILL || RFKILL = n +	select INPUT_SPARSEKMAP  	---help---  	  This is the new Linux driver for Asus laptops. It may also support some  	  MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 61a1c7503658..791fcf321506 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -45,58 +45,23 @@  #include <linux/fb.h>  #include <linux/leds.h>  #include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> +#include <linux/rfkill.h>  #include <acpi/acpi_drivers.h>  #include <acpi/acpi_bus.h> -#include <asm/uaccess.h> -#include <linux/input.h> - -#define ASUS_LAPTOP_VERSION "0.42" - -#define ASUS_HOTK_NAME          "Asus Laptop Support" -#define ASUS_HOTK_CLASS         "hotkey" -#define ASUS_HOTK_DEVICE_NAME   "Hotkey" -#define ASUS_HOTK_FILE          KBUILD_MODNAME -#define ASUS_HOTK_PREFIX        "\\_SB.ATKD." +#define ASUS_LAPTOP_VERSION	"0.42" -/* - * Some events we use, same for all Asus - */ -#define ATKD_BR_UP       0x10 -#define ATKD_BR_DOWN     0x20 -#define ATKD_LCD_ON      0x33 -#define ATKD_LCD_OFF     0x34 - -/* - * Known bits returned by \_SB.ATKD.HWRS - */ -#define WL_HWRS     0x80 -#define BT_HWRS     0x100 - -/* - * Flags for hotk status - * WL_ON and BT_ON are also used for wireless_status() - */ -#define WL_ON       0x01	/* internal Wifi */ -#define BT_ON       0x02	/* internal Bluetooth */ -#define MLED_ON     0x04	/* mail LED */ -#define TLED_ON     0x08	/* touchpad LED */ -#define RLED_ON     0x10	/* Record LED */ -#define PLED_ON     0x20	/* Phone LED */ -#define GLED_ON     0x40	/* Gaming LED */ -#define LCD_ON      0x80	/* LCD backlight */ -#define GPS_ON      0x100	/* GPS */ -#define KEY_ON      0x200	/* Keyboard backlight */ - -#define ASUS_LOG    ASUS_HOTK_FILE ": " -#define ASUS_ERR    KERN_ERR    ASUS_LOG -#define ASUS_WARNING    KERN_WARNING    ASUS_LOG -#define ASUS_NOTICE KERN_NOTICE ASUS_LOG -#define ASUS_INFO   KERN_INFO   ASUS_LOG -#define ASUS_DEBUG  KERN_DEBUG  ASUS_LOG +#define ASUS_LAPTOP_NAME	"Asus Laptop Support" +#define ASUS_LAPTOP_CLASS	"hotkey" +#define ASUS_LAPTOP_DEVICE_NAME	"Hotkey" +#define ASUS_LAPTOP_FILE	KBUILD_MODNAME +#define ASUS_LAPTOP_PREFIX	"\\_SB.ATKD."  MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary"); -MODULE_DESCRIPTION(ASUS_HOTK_NAME); +MODULE_DESCRIPTION(ASUS_LAPTOP_NAME);  MODULE_LICENSE("GPL");  /* @@ -113,225 +78,209 @@ static uint wapf = 1;  module_param(wapf, uint, 0644);  MODULE_PARM_DESC(wapf, "WAPF value"); -#define ASUS_HANDLE(object, paths...)					\ -	static acpi_handle  object##_handle = NULL;			\ -	static char *object##_paths[] = { paths } +static uint wlan_status = 1; +static uint bluetooth_status = 1; + +module_param(wlan_status, uint, 0644); +MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot " +		 "(0 = disabled, 1 = enabled, -1 = don't do anything). " +		 "default is 1"); + +module_param(bluetooth_status, uint, 0644); +MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " +		 "(0 = disabled, 1 = enabled, -1 = don't do anything). " +		 "default is 1"); + +/* + * 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_LCD_ON	0x33 +#define ATKD_LCD_OFF	0x34 + +/* + * Known bits returned by \_SB.ATKD.HWRS + */ +#define WL_HWRS		0x80 +#define BT_HWRS		0x100 + +/* + * Flags for hotk status + * WL_ON and BT_ON are also used for wireless_status() + */ +#define WL_RSTS		0x01	/* internal Wifi */ +#define BT_RSTS		0x02	/* internal Bluetooth */  /* LED */ -ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED"); -ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED"); -ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED");	/* W1JC */ -ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED");	/* A7J */ -ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED");	/* G1, G2 (probably) */ +#define METHOD_MLED		"MLED" +#define METHOD_TLED		"TLED" +#define METHOD_RLED		"RLED"	/* W1JC */ +#define METHOD_PLED		"PLED"	/* A7J */ +#define METHOD_GLED		"GLED"	/* G1, G2 (probably) */  /* LEDD */ -ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM"); +#define METHOD_LEDD		"SLCM"  /*   * Bluetooth and WLAN   * WLED and BLED are not handled like other XLED, because in some dsdt   * they also control the WLAN/Bluetooth device.   */ -ASUS_HANDLE(wl_switch, ASUS_HOTK_PREFIX "WLED"); -ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED"); -ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS");	/* All new models */ +#define METHOD_WLAN		"WLED" +#define METHOD_BLUETOOTH	"BLED" +#define METHOD_WL_STATUS	"RSTS"  /* Brightness */ -ASUS_HANDLE(brightness_set, ASUS_HOTK_PREFIX "SPLV"); -ASUS_HANDLE(brightness_get, ASUS_HOTK_PREFIX "GPLV"); +#define METHOD_BRIGHTNESS_SET	"SPLV" +#define METHOD_BRIGHTNESS_GET	"GPLV"  /* Backlight */ -ASUS_HANDLE(lcd_switch, "\\_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 */ +static acpi_handle lcd_switch_handle; +static const 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 */ -ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP"); -ASUS_HANDLE(display_get, -	    /* 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"); - -ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */ -ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL");	 /* Z71A Z71V */ +#define METHOD_SWITCH_DISPLAY	"SDSP" + +static acpi_handle display_get_handle; +static const 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 */  /* GPS */  /* R2H use different handle for GPS on/off */ -ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON");	/* R2H */ -ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF");	/* R2H */ -ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST"); +#define METHOD_GPS_ON		"SDON" +#define METHOD_GPS_OFF		"SDOF" +#define METHOD_GPS_STATUS	"GPST"  /* Keyboard light */ -ASUS_HANDLE(kled_set, ASUS_HOTK_PREFIX "SLKB"); -ASUS_HANDLE(kled_get, ASUS_HOTK_PREFIX "GLKB"); +#define METHOD_KBD_LIGHT_SET	"SLKB" +#define METHOD_KBD_LIGHT_GET	"GLKB"  /* - * This is the main structure, we can use it to store anything interesting - * about the hotk device + * Define a specific led structure to keep the main structure clean   */ -struct asus_hotk { -	char *name;		/* laptop name */ -	struct acpi_device *device;	/* the device we are in */ -	acpi_handle handle;	/* the handle of the hotk device */ -	char status;		/* status of the hotk, for LEDs, ... */ -	u32 ledd_status;	/* status of the LED display */ -	u8 light_level;		/* light sensor level */ -	u8 light_switch;	/* light sensor switch value */ -	u16 event_count[128];	/* count for each event TODO make this better */ -	struct input_dev *inputdev; -	u16 *keycode_map; +struct asus_led { +	int wk; +	struct work_struct work; +	struct led_classdev led; +	struct asus_laptop *asus; +	const char *method;  };  /* - * This header is made available to allow proper configuration given model, - * revision number , ... this info cannot go in struct asus_hotk because it is - * available before the hotk - */ -static struct acpi_table_header *asus_info; - -/* The actual device the driver binds to */ -static struct asus_hotk *hotk; - -/* - * The hotkey driver declaration + * This is the main structure, we can use it to store anything interesting + * about the hotk device   */ -static const struct acpi_device_id asus_device_ids[] = { -	{"ATK0100", 0}, -	{"ATK0101", 0}, -	{"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, asus_device_ids); +struct asus_laptop { +	char *name;		/* laptop name */ -static int asus_hotk_add(struct acpi_device *device); -static int asus_hotk_remove(struct acpi_device *device, int type); -static void asus_hotk_notify(struct acpi_device *device, u32 event); +	struct acpi_table_header *dsdt_info; +	struct platform_device *platform_device; +	struct acpi_device *device;		/* the device we are in */ +	struct backlight_device *backlight_device; -static struct acpi_driver asus_hotk_driver = { -	.name = ASUS_HOTK_NAME, -	.class = ASUS_HOTK_CLASS, -	.owner = THIS_MODULE, -	.ids = asus_device_ids, -	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, -	.ops = { -		.add = asus_hotk_add, -		.remove = asus_hotk_remove, -		.notify = asus_hotk_notify, -		}, -}; +	struct input_dev *inputdev; +	struct key_entry *keymap; -/* The backlight device /sys/class/backlight */ -static struct backlight_device *asus_backlight_device; +	struct asus_led mled; +	struct asus_led tled; +	struct asus_led rled; +	struct asus_led pled; +	struct asus_led gled; +	struct asus_led kled; +	struct workqueue_struct *led_workqueue; -/* - * The backlight class declaration - */ -static int read_brightness(struct backlight_device *bd); -static int update_bl_status(struct backlight_device *bd); -static struct backlight_ops asusbl_ops = { -	.get_brightness = read_brightness, -	.update_status = update_bl_status, -}; +	int wireless_status; +	bool have_rsts; +	int lcd_state; -/* - * These functions actually update the LED's, and are called from a - * workqueue. By doing this as separate work rather than when the LED - * subsystem asks, we avoid messing with the Asus ACPI stuff during a - * potentially bad time, such as a timer interrupt. - */ -static struct workqueue_struct *led_workqueue; - -#define ASUS_LED(object, ledname, max)					\ -	static void object##_led_set(struct led_classdev *led_cdev,	\ -				     enum led_brightness value);	\ -	static enum led_brightness object##_led_get(			\ -		struct led_classdev *led_cdev);				\ -	static void object##_led_update(struct work_struct *ignored);	\ -	static int object##_led_wk;					\ -	static DECLARE_WORK(object##_led_work, object##_led_update);	\ -	static struct led_classdev object##_led = {			\ -		.name           = "asus::" ledname,			\ -		.brightness_set = object##_led_set,			\ -		.brightness_get = object##_led_get,			\ -		.max_brightness = max					\ -	} +	struct rfkill *gps_rfkill; -ASUS_LED(mled, "mail", 1); -ASUS_LED(tled, "touchpad", 1); -ASUS_LED(rled, "record", 1); -ASUS_LED(pled, "phone", 1); -ASUS_LED(gled, "gaming", 1); -ASUS_LED(kled, "kbd_backlight", 3); - -struct key_entry { -	char type; -	u8 code; -	u16 keycode; +	acpi_handle handle;	/* the handle of the hotk device */ +	u32 ledd_status;	/* status of the LED display */ +	u8 light_level;		/* light sensor level */ +	u8 light_switch;	/* light sensor switch value */ +	u16 event_count[128];	/* count for each event TODO make this better */ +	u16 *keycode_map;  }; -enum { KE_KEY, KE_END }; - -static struct key_entry asus_keymap[] = { -	{KE_KEY, 0x02, KEY_SCREENLOCK}, -	{KE_KEY, 0x05, KEY_WLAN}, -	{KE_KEY, 0x08, KEY_F13}, -	{KE_KEY, 0x17, KEY_ZOOM}, -	{KE_KEY, 0x1f, KEY_BATTERY}, -	{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, 0x40, KEY_PREVIOUSSONG}, -	{KE_KEY, 0x41, KEY_NEXTSONG}, -	{KE_KEY, 0x43, KEY_STOPCD}, -	{KE_KEY, 0x45, KEY_PLAYPAUSE}, -	{KE_KEY, 0x4c, KEY_MEDIA}, -	{KE_KEY, 0x50, KEY_EMAIL}, -	{KE_KEY, 0x51, KEY_WWW}, -	{KE_KEY, 0x55, KEY_CALC}, -	{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, 0x82, KEY_CAMERA}, -	{KE_KEY, 0x88, KEY_WLAN }, -	{KE_KEY, 0x8A, KEY_PROG1}, -	{KE_KEY, 0x95, KEY_MEDIA}, -	{KE_KEY, 0x99, KEY_PHONE}, -	{KE_KEY, 0xc4, KEY_KBDILLUMUP}, -	{KE_KEY, 0xc5, KEY_KBDILLUMDOWN}, +static const struct key_entry asus_keymap[] = { +	/* Lenovo SL Specific keycodes */ +	{KE_KEY, 0x02, { KEY_SCREENLOCK } }, +	{KE_KEY, 0x05, { KEY_WLAN } }, +	{KE_KEY, 0x08, { KEY_F13 } }, +	{KE_KEY, 0x17, { KEY_ZOOM } }, +	{KE_KEY, 0x1f, { KEY_BATTERY } }, +	/* End of Lenovo SL Specific keycodes */ +	{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, 0x40, { KEY_PREVIOUSSONG } }, +	{KE_KEY, 0x41, { KEY_NEXTSONG } }, +	{KE_KEY, 0x43, { KEY_STOPCD } }, +	{KE_KEY, 0x45, { KEY_PLAYPAUSE } }, +	{KE_KEY, 0x4c, { KEY_MEDIA } }, +	{KE_KEY, 0x50, { KEY_EMAIL } }, +	{KE_KEY, 0x51, { KEY_WWW } }, +	{KE_KEY, 0x55, { KEY_CALC } }, +	{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, 0x82, { KEY_CAMERA } }, +	{KE_KEY, 0x88, { KEY_WLAN  } }, +	{KE_KEY, 0x8A, { KEY_PROG1 } }, +	{KE_KEY, 0x95, { KEY_MEDIA } }, +	{KE_KEY, 0x99, { KEY_PHONE } }, +	{KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, +	{KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },  	{KE_END, 0},  }; +  /*   * This function evaluates an ACPI method, given an int as parameter, the   * method is searched within the scope of the handle, can be NULL. The output @@ -339,8 +288,8 @@ static struct key_entry asus_keymap[] = {   *   * returns 0 if write is successful, -1 else.   */ -static int write_acpi_int(acpi_handle handle, const char *method, int val, -			  struct acpi_buffer *output) +static int write_acpi_int_ret(acpi_handle handle, const char *method, int val, +			      struct acpi_buffer *output)  {  	struct acpi_object_list params;	/* list of input parameters (an int) */  	union acpi_object in_obj;	/* the only param we use */ @@ -361,102 +310,82 @@ static int write_acpi_int(acpi_handle handle, const char *method, int val,  		return -1;  } -static int read_wireless_status(int mask) +static int write_acpi_int(acpi_handle handle, const char *method, int val)  { -	unsigned long long status; -	acpi_status rv = AE_OK; +	return write_acpi_int_ret(handle, method, val, NULL); +} + +static int acpi_check_handle(acpi_handle handle, const char *method, +			     acpi_handle *ret) +{ +	acpi_status status; -	if (!wireless_status_handle) -		return (hotk->status & mask) ? 1 : 0; +	if (method == NULL) +		return -ENODEV; -	rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status); -	if (ACPI_FAILURE(rv)) -		pr_warning("Error reading Wireless status\n"); -	else -		return (status & mask) ? 1 : 0; +	if (ret) +		status = acpi_get_handle(handle, (char *)method, +					 ret); +	else { +		acpi_handle dummy; -	return (hotk->status & mask) ? 1 : 0; +		status = acpi_get_handle(handle, (char *)method, +					 &dummy); +	} + +	if (status != AE_OK) { +		if (ret) +			pr_warning("Error finding %s\n", method); +		return -ENODEV; +	} +	return 0;  } -static int read_gps_status(void) +/* Generic LED function */ +static int asus_led_set(struct asus_laptop *asus, const char *method, +			 int value)  { -	unsigned long long status; -	acpi_status rv = AE_OK; - -	rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status); -	if (ACPI_FAILURE(rv)) -		pr_warning("Error reading GPS status\n"); +	if (!strcmp(method, METHOD_MLED)) +		value = !value; +	else if (!strcmp(method, METHOD_GLED)) +		value = !value + 1;  	else -		return status ? 1 : 0; +		value = !!value; -	return (hotk->status & GPS_ON) ? 1 : 0; +	return write_acpi_int(asus->handle, method, value);  } -/* Generic LED functions */ -static int read_status(int mask) +/* + * LEDs + */ +/* /sys/class/led handlers */ +static void asus_led_cdev_set(struct led_classdev *led_cdev, +			 enum led_brightness value)  { -	/* There is a special method for both wireless devices */ -	if (mask == BT_ON || mask == WL_ON) -		return read_wireless_status(mask); -	else if (mask == GPS_ON) -		return read_gps_status(); +	struct asus_led *led = container_of(led_cdev, struct asus_led, led); +	struct asus_laptop *asus = led->asus; -	return (hotk->status & mask) ? 1 : 0; +	led->wk = !!value; +	queue_work(asus->led_workqueue, &led->work);  } -static void write_status(acpi_handle handle, int out, int mask) +static void asus_led_cdev_update(struct work_struct *work)  { -	hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask); - -	switch (mask) { -	case MLED_ON: -		out = !(out & 0x1); -		break; -	case GLED_ON: -		out = (out & 0x1) + 1; -		break; -	case GPS_ON: -		handle = (out) ? gps_on_handle : gps_off_handle; -		out = 0x02; -		break; -	default: -		out &= 0x1; -		break; -	} +	struct asus_led *led = container_of(work, struct asus_led, work); +	struct asus_laptop *asus = led->asus; -	if (write_acpi_int(handle, NULL, out, NULL)) -		pr_warning(" write failed %x\n", mask); +	asus_led_set(asus, led->method, led->wk);  } -/* /sys/class/led handlers */ -#define ASUS_LED_HANDLER(object, mask)					\ -	static void object##_led_set(struct led_classdev *led_cdev,	\ -				     enum led_brightness value)		\ -	{								\ -		object##_led_wk = (value > 0) ? 1 : 0;			\ -		queue_work(led_workqueue, &object##_led_work);		\ -	}								\ -	static void object##_led_update(struct work_struct *ignored)	\ -	{								\ -		int value = object##_led_wk;				\ -		write_status(object##_set_handle, value, (mask));	\ -	}								\ -	static enum led_brightness object##_led_get(			\ -		struct led_classdev *led_cdev)				\ -	{								\ -		return led_cdev->brightness;				\ -	} - -ASUS_LED_HANDLER(mled, MLED_ON); -ASUS_LED_HANDLER(pled, PLED_ON); -ASUS_LED_HANDLER(rled, RLED_ON); -ASUS_LED_HANDLER(tled, TLED_ON); -ASUS_LED_HANDLER(gled, GLED_ON); +static enum led_brightness asus_led_cdev_get(struct led_classdev *led_cdev) +{ +	return led_cdev->brightness; +}  /* - * Keyboard backlight + * Keyboard backlight (also a LED)   */ -static int get_kled_lvl(void) +static int asus_kled_lvl(struct asus_laptop *asus)  {  	unsigned long long kblv;  	struct acpi_object_list params; @@ -468,75 +397,183 @@ static int get_kled_lvl(void)  	in_obj.type = ACPI_TYPE_INTEGER;  	in_obj.integer.value = 2; -	rv = acpi_evaluate_integer(kled_get_handle, NULL, ¶ms, &kblv); +	rv = acpi_evaluate_integer(asus->handle, METHOD_KBD_LIGHT_GET, +				   ¶ms, &kblv);  	if (ACPI_FAILURE(rv)) {  		pr_warning("Error reading kled level\n"); -		return 0; +		return -ENODEV;  	}  	return kblv;  } -static int set_kled_lvl(int kblv) +static int asus_kled_set(struct asus_laptop *asus, int kblv)  {  	if (kblv > 0)  		kblv = (1 << 7) | (kblv & 0x7F);  	else  		kblv = 0; -	if (write_acpi_int(kled_set_handle, NULL, kblv, NULL)) { +	if (write_acpi_int(asus->handle, METHOD_KBD_LIGHT_SET, kblv)) {  		pr_warning("Keyboard LED display write failed\n");  		return -EINVAL;  	}  	return 0;  } -static void kled_led_set(struct led_classdev *led_cdev, -			 enum led_brightness value) +static void asus_kled_cdev_set(struct led_classdev *led_cdev, +			      enum led_brightness value)  { -	kled_led_wk = value; -	queue_work(led_workqueue, &kled_led_work); +	struct asus_led *led = container_of(led_cdev, struct asus_led, led); +	struct asus_laptop *asus = led->asus; + +	led->wk = value; +	queue_work(asus->led_workqueue, &led->work);  } -static void kled_led_update(struct work_struct *ignored) +static void asus_kled_cdev_update(struct work_struct *work)  { -	set_kled_lvl(kled_led_wk); +	struct asus_led *led = container_of(work, struct asus_led, work); +	struct asus_laptop *asus = led->asus; + +	asus_kled_set(asus, led->wk);  } -static enum led_brightness kled_led_get(struct led_classdev *led_cdev) +static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev)  { -	return get_kled_lvl(); +	struct asus_led *led = container_of(led_cdev, struct asus_led, led); +	struct asus_laptop *asus = led->asus; + +	return asus_kled_lvl(asus);  } -static int get_lcd_state(void) +static void asus_led_exit(struct asus_laptop *asus)  { -	return read_status(LCD_ON); +	if (asus->mled.led.dev) +		led_classdev_unregister(&asus->mled.led); +	if (asus->tled.led.dev) +		led_classdev_unregister(&asus->tled.led); +	if (asus->pled.led.dev) +		led_classdev_unregister(&asus->pled.led); +	if (asus->rled.led.dev) +		led_classdev_unregister(&asus->rled.led); +	if (asus->gled.led.dev) +		led_classdev_unregister(&asus->gled.led); +	if (asus->kled.led.dev) +		led_classdev_unregister(&asus->kled.led); +	if (asus->led_workqueue) { +		destroy_workqueue(asus->led_workqueue); +		asus->led_workqueue = NULL; +	}  } -static int set_lcd_state(int value) +/*  Ugly macro, need to fix that later */ +static int asus_led_register(struct asus_laptop *asus, +			     struct asus_led *led, +			     const char *name, const char *method) +{ +	struct led_classdev *led_cdev = &led->led; + +	if (!method || acpi_check_handle(asus->handle, method, NULL)) +		return 0; /* Led not present */ + +	led->asus = asus; +	led->method = method; + +	INIT_WORK(&led->work, asus_led_cdev_update); +	led_cdev->name = name; +	led_cdev->brightness_set = asus_led_cdev_set; +	led_cdev->brightness_get = asus_led_cdev_get; +	led_cdev->max_brightness = 1; +	return led_classdev_register(&asus->platform_device->dev, led_cdev); +} + +static int asus_led_init(struct asus_laptop *asus) +{ +	int r; + +	/* +	 * Functions that actually update the LED's are called from a +	 * workqueue. By doing this as separate work rather than when the LED +	 * subsystem asks, we avoid messing with the Asus ACPI stuff during a +	 * potentially bad time, such as a timer interrupt. +	 */ +	asus->led_workqueue = create_singlethread_workqueue("led_workqueue"); +	if (!asus->led_workqueue) +		return -ENOMEM; + +	r = asus_led_register(asus, &asus->mled, "asus::mail", METHOD_MLED); +	if (r) +		goto error; +	r = asus_led_register(asus, &asus->tled, "asus::touchpad", METHOD_TLED); +	if (r) +		goto error; +	r = asus_led_register(asus, &asus->rled, "asus::record", METHOD_RLED); +	if (r) +		goto error; +	r = asus_led_register(asus, &asus->pled, "asus::phone", METHOD_PLED); +	if (r) +		goto error; +	r = asus_led_register(asus, &asus->gled, "asus::gaming", METHOD_GLED); +	if (r) +		goto error; +	if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL) && +	    !acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_GET, NULL)) { +		struct asus_led *led = &asus->kled; +		struct led_classdev *cdev = &led->led; + +		led->asus = asus; + +		INIT_WORK(&led->work, asus_kled_cdev_update); +		cdev->name = "asus::kbd_backlight"; +		cdev->brightness_set = asus_kled_cdev_set; +		cdev->brightness_get = asus_kled_cdev_get; +		cdev->max_brightness = 3; +		r = led_classdev_register(&asus->platform_device->dev, cdev); +	} +error: +	if (r) +		asus_led_exit(asus); +	return r; +} + +/* + * 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 ? 1 : 0; +	lcd = !!value; -	if (lcd == get_lcd_state()) +	if (lcd == asus_lcd_status(asus))  		return 0; -	if (lcd_switch_handle) { -		status = acpi_evaluate_object(lcd_switch_handle, -					      NULL, NULL, NULL); +	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"); +	if (ACPI_FAILURE(status)) { +		pr_warning("Error switching LCD\n"); +		return -ENODEV;  	} -	write_status(NULL, lcd, LCD_ON); +	asus->lcd_state = lcd;  	return 0;  } -static void lcd_blank(int blank) +static void lcd_blank(struct asus_laptop *asus, int blank)  { -	struct backlight_device *bd = asus_backlight_device; +	struct backlight_device *bd = asus->backlight_device; + +	asus->lcd_state = (blank == FB_BLANK_UNBLANK);  	if (bd) {  		bd->props.power = blank; @@ -544,44 +581,91 @@ static void lcd_blank(int blank)  	}  } -static int read_brightness(struct backlight_device *bd) +static int asus_read_brightness(struct backlight_device *bd)  { +	struct asus_laptop *asus = bl_get_data(bd);  	unsigned long long value;  	acpi_status rv = AE_OK; -	rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value); +	rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET, +				   NULL, &value);  	if (ACPI_FAILURE(rv))  		pr_warning("Error reading brightness\n");  	return value;  } -static int set_brightness(struct backlight_device *bd, int value) +static int asus_set_brightness(struct backlight_device *bd, int value)  { -	int ret = 0; - -	value = (0 < value) ? ((15 < value) ? 15 : value) : 0; -	/* 0 <= value <= 15 */ +	struct asus_laptop *asus = bl_get_data(bd); -	if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) { +	if (write_acpi_int(asus->handle, METHOD_BRIGHTNESS_SET, value)) {  		pr_warning("Error changing brightness\n"); -		ret = -EIO; +		return -EIO;  	} - -	return ret; +	return 0;  }  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 = set_brightness(bd, value); +	rv = asus_set_brightness(bd, value);  	if (rv)  		return rv;  	value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0; -	return set_lcd_state(value); +	return asus_lcd_set(asus, value); +} + +static struct backlight_ops asusbl_ops = { +	.get_brightness = asus_read_brightness, +	.update_status = update_bl_status, +}; + +static int asus_backlight_notify(struct asus_laptop *asus) +{ +	struct backlight_device *bd = asus->backlight_device; +	int old = bd->props.brightness; + +	backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); + +	return old; +} + +static int asus_backlight_init(struct asus_laptop *asus) +{ +	struct backlight_device *bd; +	struct device *dev = &asus->platform_device->dev; + +	if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) && +	    !acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) && +	    lcd_switch_handle) { +		bd = backlight_device_register(ASUS_LAPTOP_FILE, dev, +					       asus, &asusbl_ops); +		if (IS_ERR(bd)) { +			pr_err("Could not register asus backlight device\n"); +			asus->backlight_device = NULL; +			return PTR_ERR(bd); +		} + +		asus->backlight_device = bd; + +		bd->props.max_brightness = 15; +		bd->props.power = FB_BLANK_UNBLANK; +		bd->props.brightness = asus_read_brightness(bd); +		backlight_update_status(bd); +	} +	return 0; +} + +static void asus_backlight_exit(struct asus_laptop *asus) +{ +	if (asus->backlight_device) +		backlight_device_unregister(asus->backlight_device); +	asus->backlight_device = NULL;  }  /* @@ -596,25 +680,26 @@ static int update_bl_status(struct backlight_device *bd)  static ssize_t show_infos(struct device *dev,  			  struct device_attribute *attr, char *page)  { +	struct asus_laptop *asus = dev_get_drvdata(dev);  	int len = 0;  	unsigned long long temp;  	char buf[16];		/* enough for all info */  	acpi_status rv = AE_OK;  	/* -	 * We use the easy way, we don't care of off and count, so we don't set eof -	 * to 1 +	 * We use the easy way, we don't care of off and count, +	 * so we don't set eof to 1  	 */ -	len += sprintf(page, ASUS_HOTK_NAME " " ASUS_LAPTOP_VERSION "\n"); -	len += sprintf(page + len, "Model reference    : %s\n", hotk->name); +	len += sprintf(page, ASUS_LAPTOP_NAME " " ASUS_LAPTOP_VERSION "\n"); +	len += sprintf(page + len, "Model reference    : %s\n", asus->name);  	/*  	 * The SFUN method probably allows the original driver to get the list  	 * of features supported by a given model. For now, 0x0100 or 0x0800  	 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.  	 * The significance of others is yet to be found.  	 */ -	rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp); +	rv = acpi_evaluate_integer(asus->handle, "SFUN", NULL, &temp);  	if (!ACPI_FAILURE(rv))  		len += sprintf(page + len, "SFUN value         : %#x\n",  			       (uint) temp); @@ -624,7 +709,7 @@ static ssize_t show_infos(struct device *dev,  	 * The significance of others is yet to be found.  	 * If we don't find the method, we assume the device are present.  	 */ -	rv = acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &temp); +	rv = acpi_evaluate_integer(asus->handle, "HRWS", NULL, &temp);  	if (!ACPI_FAILURE(rv))  		len += sprintf(page + len, "HRWS value         : %#x\n",  			       (uint) temp); @@ -635,26 +720,26 @@ static ssize_t show_infos(struct device *dev,  	 * Note: since not all the laptops provide this method, errors are  	 * silently ignored.  	 */ -	rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp); +	rv = acpi_evaluate_integer(asus->handle, "ASYM", NULL, &temp);  	if (!ACPI_FAILURE(rv))  		len += sprintf(page + len, "ASYM value         : %#x\n",  			       (uint) temp); -	if (asus_info) { -		snprintf(buf, 16, "%d", asus_info->length); +	if (asus->dsdt_info) { +		snprintf(buf, 16, "%d", asus->dsdt_info->length);  		len += sprintf(page + len, "DSDT length        : %s\n", buf); -		snprintf(buf, 16, "%d", asus_info->checksum); +		snprintf(buf, 16, "%d", asus->dsdt_info->checksum);  		len += sprintf(page + len, "DSDT checksum      : %s\n", buf); -		snprintf(buf, 16, "%d", asus_info->revision); +		snprintf(buf, 16, "%d", asus->dsdt_info->revision);  		len += sprintf(page + len, "DSDT revision      : %s\n", buf); -		snprintf(buf, 7, "%s", asus_info->oem_id); +		snprintf(buf, 7, "%s", asus->dsdt_info->oem_id);  		len += sprintf(page + len, "OEM id             : %s\n", buf); -		snprintf(buf, 9, "%s", asus_info->oem_table_id); +		snprintf(buf, 9, "%s", asus->dsdt_info->oem_table_id);  		len += sprintf(page + len, "OEM table id       : %s\n", buf); -		snprintf(buf, 16, "%x", asus_info->oem_revision); +		snprintf(buf, 16, "%x", asus->dsdt_info->oem_revision);  		len += sprintf(page + len, "OEM revision       : 0x%s\n", buf); -		snprintf(buf, 5, "%s", asus_info->asl_compiler_id); +		snprintf(buf, 5, "%s", asus->dsdt_info->asl_compiler_id);  		len += sprintf(page + len, "ASL comp vendor id : %s\n", buf); -		snprintf(buf, 16, "%x", asus_info->asl_compiler_revision); +		snprintf(buf, 16, "%x", asus->dsdt_info->asl_compiler_revision);  		len += sprintf(page + len, "ASL comp revision  : 0x%s\n", buf);  	} @@ -672,8 +757,9 @@ static int parse_arg(const char *buf, unsigned long count, int *val)  	return count;  } -static ssize_t store_status(const char *buf, size_t count, -			    acpi_handle handle, int mask) +static ssize_t sysfs_acpi_set(struct asus_laptop *asus, +			      const char *buf, size_t count, +			      const char *method)  {  	int rv, value;  	int out = 0; @@ -682,8 +768,8 @@ static ssize_t store_status(const char *buf, size_t count,  	if (rv > 0)  		out = value ? 1 : 0; -	write_status(handle, out, mask); - +	if (write_acpi_int(asus->handle, method, value)) +		return -ENODEV;  	return rv;  } @@ -693,67 +779,116 @@ static ssize_t store_status(const char *buf, size_t count,  static ssize_t show_ledd(struct device *dev,  			 struct device_attribute *attr, char *buf)  { -	return sprintf(buf, "0x%08x\n", hotk->ledd_status); +	struct asus_laptop *asus = dev_get_drvdata(dev); + +	return sprintf(buf, "0x%08x\n", asus->ledd_status);  }  static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,  			  const char *buf, size_t count)  { +	struct asus_laptop *asus = dev_get_drvdata(dev);  	int rv, value;  	rv = parse_arg(buf, count, &value);  	if (rv > 0) { -		if (write_acpi_int(ledd_set_handle, NULL, value, NULL)) +		if (write_acpi_int(asus->handle, METHOD_LEDD, value))  			pr_warning("LED display write failed\n");  		else -			hotk->ledd_status = (u32) value; +			asus->ledd_status = (u32) value;  	}  	return rv;  }  /* + * Wireless + */ +static int asus_wireless_status(struct asus_laptop *asus, int mask) +{ +	unsigned long long status; +	acpi_status rv = AE_OK; + +	if (!asus->have_rsts) +		return (asus->wireless_status & mask) ? 1 : 0; + +	rv = acpi_evaluate_integer(asus->handle, METHOD_WL_STATUS, +				   NULL, &status); +	if (ACPI_FAILURE(rv)) { +		pr_warning("Error reading Wireless status\n"); +		return -EINVAL; +	} +	return !!(status & mask); +} + +/*   * WLAN   */ +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); +		return -EIO; +	} +	return 0; +} +  static ssize_t show_wlan(struct device *dev,  			 struct device_attribute *attr, char *buf)  { -	return sprintf(buf, "%d\n", read_status(WL_ON)); +	struct asus_laptop *asus = dev_get_drvdata(dev); + +	return sprintf(buf, "%d\n", asus_wireless_status(asus, WL_RSTS));  }  static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,  			  const char *buf, size_t count)  { -	return store_status(buf, count, wl_switch_handle, WL_ON); +	struct asus_laptop *asus = dev_get_drvdata(dev); + +	return sysfs_acpi_set(asus, buf, count, METHOD_WLAN);  }  /*   * 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); +		return -EIO; +	} +	return 0; +} +  static ssize_t show_bluetooth(struct device *dev,  			      struct device_attribute *attr, char *buf)  { -	return sprintf(buf, "%d\n", read_status(BT_ON)); +	struct asus_laptop *asus = dev_get_drvdata(dev); + +	return sprintf(buf, "%d\n", asus_wireless_status(asus, BT_RSTS));  }  static ssize_t store_bluetooth(struct device *dev,  			       struct device_attribute *attr, const char *buf,  			       size_t count)  { -	return store_status(buf, count, bt_switch_handle, BT_ON); +	struct asus_laptop *asus = dev_get_drvdata(dev); + +	return sysfs_acpi_set(asus, buf, count, METHOD_BLUETOOTH);  }  /*   * Display   */ -static void set_display(int value) +static void asus_set_display(struct asus_laptop *asus, int value)  {  	/* no sanity check needed for now */ -	if (write_acpi_int(display_set_handle, NULL, value, NULL)) +	if (write_acpi_int(asus->handle, METHOD_SWITCH_DISPLAY, value))  		pr_warning("Error setting display\n");  	return;  } -static int read_display(void) +static int read_display(struct asus_laptop *asus)  {  	unsigned long long value = 0;  	acpi_status rv = AE_OK; @@ -769,7 +904,7 @@ static int read_display(void)  			pr_warning("Error reading display status\n");  	} -	value &= 0x0F;		/* needed for some models, shouldn't hurt others */ +	value &= 0x0F; /* needed for some models, shouldn't hurt others */  	return value;  } @@ -781,7 +916,11 @@ static int read_display(void)  static ssize_t show_disp(struct device *dev,  			 struct device_attribute *attr, char *buf)  { -	return sprintf(buf, "%d\n", read_display()); +	struct asus_laptop *asus = dev_get_drvdata(dev); + +	if (!display_get_handle) +		return -ENODEV; +	return sprintf(buf, "%d\n", read_display(asus));  }  /* @@ -794,65 +933,72 @@ static ssize_t show_disp(struct device *dev,  static ssize_t store_disp(struct device *dev, struct device_attribute *attr,  			  const char *buf, size_t count)  { +	struct asus_laptop *asus = dev_get_drvdata(dev);  	int rv, value;  	rv = parse_arg(buf, count, &value);  	if (rv > 0) -		set_display(value); +		asus_set_display(asus, value);  	return rv;  }  /*   * Light Sens   */ -static void set_light_sens_switch(int value) +static void asus_als_switch(struct asus_laptop *asus, int value)  { -	if (write_acpi_int(ls_switch_handle, NULL, value, NULL)) +	if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value))  		pr_warning("Error setting light sensor switch\n"); -	hotk->light_switch = value; +	asus->light_switch = value;  }  static ssize_t show_lssw(struct device *dev,  			 struct device_attribute *attr, char *buf)  { -	return sprintf(buf, "%d\n", hotk->light_switch); +	struct asus_laptop *asus = dev_get_drvdata(dev); + +	return sprintf(buf, "%d\n", asus->light_switch);  }  static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,  			  const char *buf, size_t count)  { +	struct asus_laptop *asus = dev_get_drvdata(dev);  	int rv, value;  	rv = parse_arg(buf, count, &value);  	if (rv > 0) -		set_light_sens_switch(value ? 1 : 0); +		asus_als_switch(asus, value ? 1 : 0);  	return rv;  } -static void set_light_sens_level(int value) +static void asus_als_level(struct asus_laptop *asus, int value)  { -	if (write_acpi_int(ls_level_handle, NULL, value, NULL)) +	if (write_acpi_int(asus->handle, METHOD_ALS_LEVEL, value))  		pr_warning("Error setting light sensor level\n"); -	hotk->light_level = value; +	asus->light_level = value;  }  static ssize_t show_lslvl(struct device *dev,  			  struct device_attribute *attr, char *buf)  { -	return sprintf(buf, "%d\n", hotk->light_level); +	struct asus_laptop *asus = dev_get_drvdata(dev); + +	return sprintf(buf, "%d\n", asus->light_level);  }  static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,  			   const char *buf, size_t count)  { +	struct asus_laptop *asus = dev_get_drvdata(dev);  	int rv, value;  	rv = parse_arg(buf, count, &value);  	if (rv > 0) {  		value = (0 < value) ? ((15 < value) ? 15 : value) : 0;  		/* 0 <= value <= 15 */ -		set_light_sens_level(value); +		asus_als_level(asus, value);  	}  	return rv; @@ -861,197 +1007,309 @@ static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,  /*   * GPS   */ +static int asus_gps_status(struct asus_laptop *asus) +{ +	unsigned long long status; +	acpi_status rv = AE_OK; + +	rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS, +				   NULL, &status); +	if (ACPI_FAILURE(rv)) { +		pr_warning("Error reading GPS status\n"); +		return -ENODEV; +	} +	return !!status; +} + +static int asus_gps_switch(struct asus_laptop *asus, int status) +{ +	const char *meth = status ? METHOD_GPS_ON : METHOD_GPS_OFF; + +	if (write_acpi_int(asus->handle, meth, 0x02)) +		return -ENODEV; +	return 0; +} +  static ssize_t show_gps(struct device *dev,  			struct device_attribute *attr, char *buf)  { -	return sprintf(buf, "%d\n", read_status(GPS_ON)); +	struct asus_laptop *asus = dev_get_drvdata(dev); + +	return sprintf(buf, "%d\n", asus_gps_status(asus));  }  static ssize_t store_gps(struct device *dev, struct device_attribute *attr,  			 const char *buf, size_t count)  { -	return store_status(buf, count, NULL, GPS_ON); +	struct asus_laptop *asus = dev_get_drvdata(dev); +	int rv, value; +	int ret; + +	rv = parse_arg(buf, count, &value); +	if (rv <= 0) +		return -EINVAL; +	ret = asus_gps_switch(asus, !!value); +	if (ret) +		return ret; +	rfkill_set_sw_state(asus->gps_rfkill, !value); +	return rv;  }  /* - * Hotkey functions + * rfkill   */ -static struct key_entry *asus_get_entry_by_scancode(int code) +static int asus_gps_rfkill_set(void *data, bool blocked)  { -	struct key_entry *key; - -	for (key = asus_keymap; key->type != KE_END; key++) -		if (code == key->code) -			return key; +	acpi_handle handle = data; -	return NULL; +	return asus_gps_switch(handle, !blocked);  } -static struct key_entry *asus_get_entry_by_keycode(int code) -{ -	struct key_entry *key; - -	for (key = asus_keymap; key->type != KE_END; key++) -		if (code == key->keycode && key->type == KE_KEY) -			return key; +static const struct rfkill_ops asus_gps_rfkill_ops = { +	.set_block = asus_gps_rfkill_set, +}; -	return 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; +	}  } -static int asus_getkeycode(struct input_dev *dev, int scancode, int *keycode) +static int asus_rfkill_init(struct asus_laptop *asus)  { -	struct key_entry *key = asus_get_entry_by_scancode(scancode); +	int result; -	if (key && key->type == KE_KEY) { -		*keycode = key->keycode; +	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, NULL); +	if (!asus->gps_rfkill) +		return -EINVAL; + +	result = rfkill_register(asus->gps_rfkill); +	if (result) { +		rfkill_destroy(asus->gps_rfkill); +		asus->gps_rfkill = NULL;  	} -	return -EINVAL; +	return result;  } -static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode) +/* + * Input device (i.e. hotkeys) + */ +static void asus_input_notify(struct asus_laptop *asus, int event)  { -	struct key_entry *key; -	int old_keycode; +	if (asus->inputdev) +		sparse_keymap_report_event(asus->inputdev, event, 1, true); +} -	if (keycode < 0 || keycode > KEY_MAX) -		return -EINVAL; +static int asus_input_init(struct asus_laptop *asus) +{ +	struct input_dev *input; +	int error; -	key = asus_get_entry_by_scancode(scancode); -	if (key && key->type == KE_KEY) { -		old_keycode = key->keycode; -		key->keycode = keycode; -		set_bit(keycode, dev->keybit); -		if (!asus_get_entry_by_keycode(old_keycode)) -			clear_bit(old_keycode, dev->keybit); +	input = input_allocate_device(); +	if (!input) { +		pr_info("Unable to allocate input device\n");  		return 0;  	} +	input->name = "Asus Laptop extra buttons"; +	input->phys = ASUS_LAPTOP_FILE "/input0"; +	input->id.bustype = BUS_HOST; +	input->dev.parent = &asus->platform_device->dev; +	input_set_drvdata(input, asus); + +	error = sparse_keymap_setup(input, asus_keymap, NULL); +	if (error) { +		pr_err("Unable to setup input device keymap\n"); +		goto err_keymap; +	} +	error = input_register_device(input); +	if (error) { +		pr_info("Unable to register input device\n"); +		goto err_device; +	} + +	asus->inputdev = input; +	return 0; -	return -EINVAL; +err_keymap: +	sparse_keymap_free(input); +err_device: +	input_free_device(input); +	return error;  } -static void asus_hotk_notify(struct acpi_device *device, u32 event) +static void asus_input_exit(struct asus_laptop *asus)  { -	static struct key_entry *key; -	u16 count; +	if (asus->inputdev) { +		sparse_keymap_free(asus->inputdev); +		input_unregister_device(asus->inputdev); +	} +} -	/* TODO Find a better way to handle events count. */ -	if (!hotk) -		return; +/* + * ACPI driver + */ +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) { -		write_status(NULL, 1, LCD_ON); -		lcd_blank(FB_BLANK_UNBLANK); -	} else if (event == ATKD_LCD_OFF) { -		write_status(NULL, 0, LCD_ON); -		lcd_blank(FB_BLANK_POWERDOWN); -	} +	if (event == ATKD_LCD_ON) +		lcd_blank(asus, FB_BLANK_UNBLANK); +	else if (event == ATKD_LCD_OFF) +		lcd_blank(asus, FB_BLANK_POWERDOWN); -	count = hotk->event_count[event % 128]++; -	acpi_bus_generate_proc_event(hotk->device, event, count); -	acpi_bus_generate_netlink_event(hotk->device->pnp.device_class, -					dev_name(&hotk->device->dev), event, +	/* 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); -	if (hotk->inputdev) { -		key = asus_get_entry_by_scancode(event); -		if (!key) -			return ; - -		switch (key->type) { -		case KE_KEY: -			input_report_key(hotk->inputdev, key->keycode, 1); -			input_sync(hotk->inputdev); -			input_report_key(hotk->inputdev, key->keycode, 0); -			input_sync(hotk->inputdev); -			break; +	/* Brightness events are special */ +	if (event >= ATKD_BR_MIN && event <= ATKD_BR_MAX) { + +		/* Ignore them completely if the acpi video driver is used */ +		if (asus->backlight_device != NULL) { +			/* Update the backlight device. */ +			asus_backlight_notify(asus);  		} +		return ;  	} +	asus_input_notify(asus, event);  } -#define ASUS_CREATE_DEVICE_ATTR(_name)					\ -	struct device_attribute dev_attr_##_name = {			\ -		.attr = {						\ -			.name = __stringify(_name),			\ -			.mode = 0 },					\ -		.show   = NULL,						\ -		.store  = NULL,						\ +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(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); +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); + +static void asus_sysfs_exit(struct asus_laptop *asus) +{ +	struct platform_device *device = asus->platform_device; + +	device_remove_file(&device->dev, &dev_attr_infos); +	device_remove_file(&device->dev, &dev_attr_wlan); +	device_remove_file(&device->dev, &dev_attr_bluetooth); +	device_remove_file(&device->dev, &dev_attr_display); +	device_remove_file(&device->dev, &dev_attr_ledd); +	device_remove_file(&device->dev, &dev_attr_ls_switch); +	device_remove_file(&device->dev, &dev_attr_ls_level); +	device_remove_file(&device->dev, &dev_attr_gps); +} + +static int asus_sysfs_init(struct asus_laptop *asus) +{ +	struct platform_device *device = asus->platform_device; +	int err; + +	err = device_create_file(&device->dev, &dev_attr_infos); +	if (err) +		return err; + +	if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) { +		err = device_create_file(&device->dev, &dev_attr_wlan); +		if (err) +			return err;  	} -#define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store)		\ -	do {								\ -		dev_attr_##_name.attr.mode = _mode;			\ -		dev_attr_##_name.show = _show;				\ -		dev_attr_##_name.store = _store;			\ -	} while(0) - -static ASUS_CREATE_DEVICE_ATTR(infos); -static ASUS_CREATE_DEVICE_ATTR(wlan); -static ASUS_CREATE_DEVICE_ATTR(bluetooth); -static ASUS_CREATE_DEVICE_ATTR(display); -static ASUS_CREATE_DEVICE_ATTR(ledd); -static ASUS_CREATE_DEVICE_ATTR(ls_switch); -static ASUS_CREATE_DEVICE_ATTR(ls_level); -static ASUS_CREATE_DEVICE_ATTR(gps); - -static struct attribute *asuspf_attributes[] = { -	&dev_attr_infos.attr, -	&dev_attr_wlan.attr, -	&dev_attr_bluetooth.attr, -	&dev_attr_display.attr, -	&dev_attr_ledd.attr, -	&dev_attr_ls_switch.attr, -	&dev_attr_ls_level.attr, -	&dev_attr_gps.attr, -	NULL -}; +	if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) { +		err = device_create_file(&device->dev, &dev_attr_bluetooth); +		if (err) +			return err; +	} -static struct attribute_group asuspf_attribute_group = { -	.attrs = asuspf_attributes -}; +	if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) { +		err = device_create_file(&device->dev, &dev_attr_display); +		if (err) +			return err; +	} -static struct platform_driver asuspf_driver = { -	.driver = { -		   .name = ASUS_HOTK_FILE, -		   .owner = THIS_MODULE, -		   } -}; +	if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) { +		err = device_create_file(&device->dev, &dev_attr_ledd); +		if (err) +			return err; +	} -static struct platform_device *asuspf_device; +	if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && +	    !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { +		err = device_create_file(&device->dev, &dev_attr_ls_switch); +		if (err) +			return err; +		err = device_create_file(&device->dev, &dev_attr_ls_level); +		if (err) +			return err; +	} -static void asus_hotk_add_fs(void) -{ -	ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL); +	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)) { +		err = device_create_file(&device->dev, &dev_attr_gps); +		if (err) +			return err; +	} -	if (wl_switch_handle) -		ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan); +	return err; +} + +static int asus_platform_init(struct asus_laptop *asus) +{ +	int err; -	if (bt_switch_handle) -		ASUS_SET_DEVICE_ATTR(bluetooth, 0644, -				     show_bluetooth, store_bluetooth); +	asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1); +	if (!asus->platform_device) +		return -ENOMEM; +	platform_set_drvdata(asus->platform_device, asus); -	if (display_set_handle && display_get_handle) -		ASUS_SET_DEVICE_ATTR(display, 0644, show_disp, store_disp); -	else if (display_set_handle) -		ASUS_SET_DEVICE_ATTR(display, 0200, NULL, store_disp); +	err = platform_device_add(asus->platform_device); +	if (err) +		goto fail_platform_device; -	if (ledd_set_handle) -		ASUS_SET_DEVICE_ATTR(ledd, 0644, show_ledd, store_ledd); +	err = asus_sysfs_init(asus); +	if (err) +		goto fail_sysfs; +	return 0; -	if (ls_switch_handle && ls_level_handle) { -		ASUS_SET_DEVICE_ATTR(ls_level, 0644, show_lslvl, store_lslvl); -		ASUS_SET_DEVICE_ATTR(ls_switch, 0644, show_lssw, store_lssw); -	} +fail_sysfs: +	asus_sysfs_exit(asus); +	platform_device_del(asus->platform_device); +fail_platform_device: +	platform_device_put(asus->platform_device); +	return err; +} -	if (gps_status_handle && gps_on_handle && gps_off_handle) -		ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps); +static void asus_platform_exit(struct asus_laptop *asus) +{ +	asus_sysfs_exit(asus); +	platform_device_unregister(asus->platform_device);  } +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)  { @@ -1073,10 +1331,11 @@ static int asus_handle_init(char *name, acpi_handle * handle,  			 ARRAY_SIZE(object##_paths))  /* - * This function is used to initialize the hotk with right values. In this - * method, we can make all the detection we want, and modify the hotk struct + * 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 + * struct   */ -static int asus_hotk_get_info(void) +static int asus_laptop_get_info(struct asus_laptop *asus)  {  	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };  	union acpi_object *model = NULL; @@ -1089,22 +1348,21 @@ static int asus_hotk_get_info(void)  	 * models, but late enough to allow acpi_bus_register_driver() to fail  	 * before doing anything ACPI-specific. Should we encounter a machine,  	 * which needs special handling (i.e. its hotkey device has a different -	 * HID), this bit will be moved. A global variable asus_info contains -	 * the DSDT header. +	 * HID), this bit will be moved.  	 */ -	status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info); +	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");  	/* We have to write 0 on init this far for all ASUS models */ -	if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { +	if (write_acpi_int_ret(asus->handle, "INIT", 0, &buffer)) {  		pr_err("Hotkey initialization failed\n");  		return -ENODEV;  	}  	/* This needs to be called for some laptops to init properly */  	status = -	    acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result); +	    acpi_evaluate_integer(asus->handle, "BSTS", NULL, &bsts_result);  	if (ACPI_FAILURE(status))  		pr_warning("Error calling BSTS\n");  	else if (bsts_result) @@ -1112,8 +1370,8 @@ static int asus_hotk_get_info(void)  		       (uint) bsts_result);  	/* This too ... */ -	write_acpi_int(hotk->handle, "CWAP", wapf, NULL); - +	if (write_acpi_int(asus->handle, "CWAP", wapf)) +		pr_err("Error calling CWAP(%d)\n", wapf);  	/*  	 * Try to match the object returned by INIT to the specific model.  	 * Handle every possible object (or the lack of thereof) the DSDT @@ -1134,397 +1392,210 @@ static int asus_hotk_get_info(void)  			break;  		}  	} -	hotk->name = kstrdup(string, GFP_KERNEL); -	if (!hotk->name) +	asus->name = kstrdup(string, GFP_KERNEL); +	if (!asus->name)  		return -ENOMEM;  	if (*string)  		pr_notice("  %s model detected\n", string); -	ASUS_HANDLE_INIT(mled_set); -	ASUS_HANDLE_INIT(tled_set); -	ASUS_HANDLE_INIT(rled_set); -	ASUS_HANDLE_INIT(pled_set); -	ASUS_HANDLE_INIT(gled_set); - -	ASUS_HANDLE_INIT(ledd_set); - -	ASUS_HANDLE_INIT(kled_set); -	ASUS_HANDLE_INIT(kled_get); -  	/*  	 * 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. -	 * If we don't find the method, we assume the device are present.  	 */  	status = -	    acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result); -	if (ACPI_FAILURE(status)) -		hwrs_result = WL_HWRS | BT_HWRS; - -	if (hwrs_result & WL_HWRS) -		ASUS_HANDLE_INIT(wl_switch); -	if (hwrs_result & BT_HWRS) -		ASUS_HANDLE_INIT(bt_switch); - -	ASUS_HANDLE_INIT(wireless_status); +	    acpi_evaluate_integer(asus->handle, "HRWS", NULL, &hwrs_result); +	if (!ACPI_FAILURE(status)) +		pr_notice("  HRWS returned %x", (int)hwrs_result); -	ASUS_HANDLE_INIT(brightness_set); -	ASUS_HANDLE_INIT(brightness_get); +	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_set);  	ASUS_HANDLE_INIT(display_get); -	/* -	 * There is a lot of models with "ALSL", but a few get -	 * a real light sens, so we need to check it. -	 */ -	if (!ASUS_HANDLE_INIT(ls_switch)) -		ASUS_HANDLE_INIT(ls_level); - -	ASUS_HANDLE_INIT(gps_on); -	ASUS_HANDLE_INIT(gps_off); -	ASUS_HANDLE_INIT(gps_status); -  	kfree(model);  	return AE_OK;  } -static int asus_input_init(void) -{ -	const struct key_entry *key; -	int result; +static bool asus_device_present; -	hotk->inputdev = input_allocate_device(); -	if (!hotk->inputdev) { -		pr_info("Unable to allocate input device\n"); -		return 0; -	} -	hotk->inputdev->name = "Asus Laptop extra buttons"; -	hotk->inputdev->phys = ASUS_HOTK_FILE "/input0"; -	hotk->inputdev->id.bustype = BUS_HOST; -	hotk->inputdev->getkeycode = asus_getkeycode; -	hotk->inputdev->setkeycode = asus_setkeycode; - -	for (key = asus_keymap; key->type != KE_END; key++) { -		switch (key->type) { -		case KE_KEY: -			set_bit(EV_KEY, hotk->inputdev->evbit); -			set_bit(key->keycode, hotk->inputdev->keybit); -			break; -		} -	} -	result = input_register_device(hotk->inputdev); -	if (result) { -		pr_info("Unable to register input device\n"); -		input_free_device(hotk->inputdev); -	} -	return result; -} - -static int asus_hotk_check(void) +static int __devinit asus_acpi_init(struct asus_laptop *asus)  {  	int result = 0; -	result = acpi_bus_get_status(hotk->device); +	result = acpi_bus_get_status(asus->device);  	if (result)  		return result; - -	if (hotk->device->status.present) { -		result = asus_hotk_get_info(); -	} else { +	if (!asus->device->status.present) {  		pr_err("Hotkey device not present, aborting\n"); -		return -EINVAL; +		return -ENODEV;  	} -	return result; -} - -static int asus_hotk_found; - -static int asus_hotk_add(struct acpi_device *device) -{ -	int result; - -	pr_notice("Asus Laptop Support version %s\n", -	       ASUS_LAPTOP_VERSION); - -	hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL); -	if (!hotk) -		return -ENOMEM; - -	hotk->handle = device->handle; -	strcpy(acpi_device_name(device), ASUS_HOTK_DEVICE_NAME); -	strcpy(acpi_device_class(device), ASUS_HOTK_CLASS); -	device->driver_data = hotk; -	hotk->device = device; - -	result = asus_hotk_check(); +	result = asus_laptop_get_info(asus);  	if (result) -		goto end; - -	asus_hotk_add_fs(); - -	asus_hotk_found = 1; +		return result;  	/* WLED and BLED are on by default */ -	write_status(bt_switch_handle, 1, BT_ON); -	write_status(wl_switch_handle, 1, WL_ON); - -	/* If the h/w switch is off, we need to check the real status */ -	write_status(NULL, read_status(BT_ON), BT_ON); -	write_status(NULL, read_status(WL_ON), WL_ON); +	if (bluetooth_status >= 0) +		asus_bluetooth_set(asus, !!bluetooth_status); -	/* LCD Backlight is on by default */ -	write_status(NULL, 1, LCD_ON); +	if (wlan_status >= 0) +		asus_wlan_set(asus, !!wlan_status);  	/* Keyboard Backlight is on by default */ -	if (kled_set_handle) -		set_kled_lvl(1); +	if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL)) +		asus_kled_set(asus, 1);  	/* LED display is off by default */ -	hotk->ledd_status = 0xFFF; +	asus->ledd_status = 0xFFF;  	/* Set initial values of light sensor and level */ -	hotk->light_switch = 0;	/* Default to light sensor disabled */ -	hotk->light_level = 5;	/* level 5 for sensor sensitivity */ +	asus->light_switch = 0;	/* Default to light sensor disabled */ +	asus->light_level = 5;	/* level 5 for sensor sensitivity */ -	if (ls_switch_handle) -		set_light_sens_switch(hotk->light_switch); - -	if (ls_level_handle) -		set_light_sens_level(hotk->light_level); - -	/* GPS is on by default */ -	write_status(NULL, 1, GPS_ON); - -end: -	if (result) { -		kfree(hotk->name); -		kfree(hotk); +	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 int asus_hotk_remove(struct acpi_device *device, int type) -{ -	kfree(hotk->name); -	kfree(hotk); - -	return 0; -} - -static void asus_backlight_exit(void) +static int __devinit asus_acpi_add(struct acpi_device *device)  { -	if (asus_backlight_device) -		backlight_device_unregister(asus_backlight_device); -} - -#define  ASUS_LED_UNREGISTER(object)				\ -	if (object##_led.dev)					\ -		led_classdev_unregister(&object##_led) +	struct asus_laptop *asus; +	int result; -static void asus_led_exit(void) -{ -	destroy_workqueue(led_workqueue); -	ASUS_LED_UNREGISTER(mled); -	ASUS_LED_UNREGISTER(tled); -	ASUS_LED_UNREGISTER(pled); -	ASUS_LED_UNREGISTER(rled); -	ASUS_LED_UNREGISTER(gled); -	ASUS_LED_UNREGISTER(kled); -} +	pr_notice("Asus Laptop Support version %s\n", +		  ASUS_LAPTOP_VERSION); +	asus = kzalloc(sizeof(struct asus_laptop), GFP_KERNEL); +	if (!asus) +		return -ENOMEM; +	asus->handle = device->handle; +	strcpy(acpi_device_name(device), ASUS_LAPTOP_DEVICE_NAME); +	strcpy(acpi_device_class(device), ASUS_LAPTOP_CLASS); +	device->driver_data = asus; +	asus->device = device; -static void asus_input_exit(void) -{ -	if (hotk->inputdev) -		input_unregister_device(hotk->inputdev); -} +	result = asus_acpi_init(asus); +	if (result) +		goto fail_platform; -static void __exit asus_laptop_exit(void) -{ -	asus_backlight_exit(); -	asus_led_exit(); -	asus_input_exit(); +	/* +	 * Register the platform device first.  It is used as a parent for the +	 * sub-devices below. +	 */ +	result = asus_platform_init(asus); +	if (result) +		goto fail_platform; -	acpi_bus_unregister_driver(&asus_hotk_driver); -	sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group); -	platform_device_unregister(asuspf_device); -	platform_driver_unregister(&asuspf_driver); -} +	if (!acpi_video_backlight_support()) { +		result = asus_backlight_init(asus); +		if (result) +			goto fail_backlight; +	} else +		pr_info("Backlight controlled by ACPI video driver\n"); -static int asus_backlight_init(struct device *dev) -{ -	struct backlight_device *bd; +	result = asus_input_init(asus); +	if (result) +		goto fail_input; -	if (brightness_set_handle && lcd_switch_handle) { -		bd = backlight_device_register(ASUS_HOTK_FILE, dev, -					       NULL, &asusbl_ops); -		if (IS_ERR(bd)) { -			pr_err("Could not register asus backlight device\n"); -			asus_backlight_device = NULL; -			return PTR_ERR(bd); -		} +	result = asus_led_init(asus); +	if (result) +		goto fail_led; -		asus_backlight_device = bd; +	result = asus_rfkill_init(asus); +	if (result) +		goto fail_rfkill; -		bd->props.max_brightness = 15; -		bd->props.brightness = read_brightness(NULL); -		bd->props.power = FB_BLANK_UNBLANK; -		backlight_update_status(bd); -	} +	asus_device_present = true;  	return 0; -} -static int asus_led_register(acpi_handle handle, -			     struct led_classdev *ldev, struct device *dev) -{ -	if (!handle) -		return 0; +fail_rfkill: +	asus_led_exit(asus); +fail_led: +	asus_input_exit(asus); +fail_input: +	asus_backlight_exit(asus); +fail_backlight: +	asus_platform_exit(asus); +fail_platform: +	kfree(asus->name); +	kfree(asus); -	return led_classdev_register(dev, ldev); +	return result;  } -#define ASUS_LED_REGISTER(object, device)				\ -	asus_led_register(object##_set_handle, &object##_led, device) - -static int asus_led_init(struct device *dev) +static int asus_acpi_remove(struct acpi_device *device, int type)  { -	int rv; - -	rv = ASUS_LED_REGISTER(mled, dev); -	if (rv) -		goto out; - -	rv = ASUS_LED_REGISTER(tled, dev); -	if (rv) -		goto out1; - -	rv = ASUS_LED_REGISTER(rled, dev); -	if (rv) -		goto out2; - -	rv = ASUS_LED_REGISTER(pled, dev); -	if (rv) -		goto out3; - -	rv = ASUS_LED_REGISTER(gled, dev); -	if (rv) -		goto out4; +	struct asus_laptop *asus = acpi_driver_data(device); -	if (kled_set_handle && kled_get_handle) -		rv = ASUS_LED_REGISTER(kled, dev); -	if (rv) -		goto out5; - -	led_workqueue = create_singlethread_workqueue("led_workqueue"); -	if (!led_workqueue) -		goto out6; +	asus_backlight_exit(asus); +	asus_rfkill_exit(asus); +	asus_led_exit(asus); +	asus_input_exit(asus); +	asus_platform_exit(asus); +	kfree(asus->name); +	kfree(asus);  	return 0; -out6: -	rv = -ENOMEM; -	ASUS_LED_UNREGISTER(kled); -out5: -	ASUS_LED_UNREGISTER(gled); -out4: -	ASUS_LED_UNREGISTER(pled); -out3: -	ASUS_LED_UNREGISTER(rled); -out2: -	ASUS_LED_UNREGISTER(tled); -out1: -	ASUS_LED_UNREGISTER(mled); -out: -	return rv;  } +static const struct acpi_device_id asus_device_ids[] = { +	{"ATK0100", 0}, +	{"ATK0101", 0}, +	{"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, asus_device_ids); + +static struct acpi_driver asus_acpi_driver = { +	.name = ASUS_LAPTOP_NAME, +	.class = ASUS_LAPTOP_CLASS, +	.owner = THIS_MODULE, +	.ids = asus_device_ids, +	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, +	.ops = { +		.add = asus_acpi_add, +		.remove = asus_acpi_remove, +		.notify = asus_acpi_notify, +		}, +}; +  static int __init asus_laptop_init(void)  {  	int result; -	result = acpi_bus_register_driver(&asus_hotk_driver); +	result = platform_driver_register(&platform_driver);  	if (result < 0)  		return result; -	/* -	 * This is a bit of a kludge.  We only want this module loaded -	 * for ASUS systems, but there's currently no way to probe the -	 * ACPI namespace for ASUS HIDs.  So we just return failure if -	 * we didn't find one, which will cause the module to be -	 * unloaded. -	 */ -	if (!asus_hotk_found) { -		acpi_bus_unregister_driver(&asus_hotk_driver); -		return -ENODEV; -	} - -	result = asus_input_init(); -	if (result) -		goto fail_input; - -	/* Register platform stuff */ -	result = platform_driver_register(&asuspf_driver); -	if (result) -		goto fail_platform_driver; - -	asuspf_device = platform_device_alloc(ASUS_HOTK_FILE, -1); -	if (!asuspf_device) { -		result = -ENOMEM; -		goto fail_platform_device1; +	result = acpi_bus_register_driver(&asus_acpi_driver); +	if (result < 0) +		goto fail_acpi_driver; +	if (!asus_device_present) { +		result = -ENODEV; +		goto fail_no_device;  	} - -	result = platform_device_add(asuspf_device); -	if (result) -		goto fail_platform_device2; - -	result = sysfs_create_group(&asuspf_device->dev.kobj, -				    &asuspf_attribute_group); -	if (result) -		goto fail_sysfs; - -	result = asus_led_init(&asuspf_device->dev); -	if (result) -		goto fail_led; - -	if (!acpi_video_backlight_support()) { -		result = asus_backlight_init(&asuspf_device->dev); -		if (result) -			goto fail_backlight; -	} else -		pr_info("Brightness ignored, must be controlled by " -		       "ACPI video driver\n"); -  	return 0; -fail_backlight: -       asus_led_exit(); - -fail_led: -       sysfs_remove_group(&asuspf_device->dev.kobj, -			  &asuspf_attribute_group); - -fail_sysfs: -	platform_device_del(asuspf_device); - -fail_platform_device2: -	platform_device_put(asuspf_device); - -fail_platform_device1: -	platform_driver_unregister(&asuspf_driver); - -fail_platform_driver: -	asus_input_exit(); - -fail_input: - +fail_no_device: +	acpi_bus_unregister_driver(&asus_acpi_driver); +fail_acpi_driver: +	platform_driver_unregister(&platform_driver);  	return result;  } +static void __exit asus_laptop_exit(void) +{ +	acpi_bus_unregister_driver(&asus_acpi_driver); +	platform_driver_unregister(&platform_driver); +} +  module_init(asus_laptop_init);  module_exit(asus_laptop_exit); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index e2be6bb33d92..9a844caa3756 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -578,6 +578,8 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)  	struct pci_dev *dev;  	struct pci_bus *bus;  	bool blocked = eeepc_wlan_rfkill_blocked(eeepc); +	bool absent; +	u32 l;  	if (eeepc->wlan_rfkill)  		rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); @@ -591,6 +593,22 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)  			goto out_unlock;  		} +		if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) { +			pr_err("Unable to read PCI config space?\n"); +			goto out_unlock; +		} +		absent = (l == 0xffffffff); + +		if (blocked != absent) { +			pr_warning("BIOS says wireless lan is %s, " +					"but the pci device is %s\n", +				blocked ? "blocked" : "unblocked", +				absent ? "absent" : "present"); +			pr_warning("skipped wireless hotplug as probably " +					"inappropriate for this model\n"); +			goto out_unlock; +		} +  		if (!blocked) {  			dev = pci_get_slot(bus, 0);  			if (dev) { @@ -1277,7 +1295,8 @@ static void eeepc_dmi_check(struct eeepc_laptop *eeepc)  	 * hotplug code. In fact, current hotplug code seems to unplug another  	 * device...  	 */ -	if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0) { +	if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0 || +	    strcmp(model, "1005PE") == 0) {  		eeepc->hotplug_disabled = true;  		pr_info("wlan hotplug disabled\n");  	} | 
