diff --git a/[refs] b/[refs] index 080374de01fb..727132da3324 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 060cbce6c4431465e83907102e1903ecea366db5 +refs/heads/master: 409a415fa18f3bdb9bef616ac6abafd30a4f1415 diff --git a/trunk/Documentation/ABI/testing/sysfs-platform-asus-laptop b/trunk/Documentation/ABI/testing/sysfs-platform-asus-laptop index 1d775390e856..a1cb660c50cf 100644 --- a/trunk/Documentation/ABI/testing/sysfs-platform-asus-laptop +++ b/trunk/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" @@ -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" @@ -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" @@ -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" @@ -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" diff --git a/trunk/Documentation/ABI/testing/sysfs-platform-eeepc-laptop b/trunk/Documentation/ABI/testing/sysfs-platform-eeepc-laptop index 7445dfb321b5..5b026c69587a 100644 --- a/trunk/Documentation/ABI/testing/sysfs-platform-eeepc-laptop +++ b/trunk/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" @@ -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" 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" 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" @@ -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" diff --git a/trunk/drivers/platform/x86/Kconfig b/trunk/drivers/platform/x86/Kconfig index 527abf1b5bd1..f526e735c5ab 100644 --- a/trunk/drivers/platform/x86/Kconfig +++ b/trunk/drivers/platform/x86/Kconfig @@ -59,8 +59,6 @@ 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/trunk/drivers/platform/x86/asus-laptop.c b/trunk/drivers/platform/x86/asus-laptop.c index 791fcf321506..61a1c7503658 100644 --- a/trunk/drivers/platform/x86/asus-laptop.c +++ b/trunk/drivers/platform/x86/asus-laptop.c @@ -45,23 +45,58 @@ #include #include #include -#include -#include -#include -#include #include #include +#include +#include + +#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" -#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." +/* + * 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 MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary"); -MODULE_DESCRIPTION(ASUS_LAPTOP_NAME); +MODULE_DESCRIPTION(ASUS_HOTK_NAME); MODULE_LICENSE("GPL"); /* @@ -78,208 +113,224 @@ static uint wapf = 1; module_param(wapf, uint, 0644); MODULE_PARM_DESC(wapf, "WAPF value"); -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 */ +#define ASUS_HANDLE(object, paths...) \ + static acpi_handle object##_handle = NULL; \ + static char *object##_paths[] = { paths } /* LED */ -#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) */ +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) */ /* LEDD */ -#define METHOD_LEDD "SLCM" +ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM"); /* * Bluetooth and WLAN * WLED and BLED are not handled like other XLED, because in some dsdt * they also control the WLAN/Bluetooth device. */ -#define METHOD_WLAN "WLED" -#define METHOD_BLUETOOTH "BLED" -#define METHOD_WL_STATUS "RSTS" +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 */ /* Brightness */ -#define METHOD_BRIGHTNESS_SET "SPLV" -#define METHOD_BRIGHTNESS_GET "GPLV" +ASUS_HANDLE(brightness_set, ASUS_HOTK_PREFIX "SPLV"); +ASUS_HANDLE(brightness_get, ASUS_HOTK_PREFIX "GPLV"); /* Backlight */ -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 */ +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 */ /* Display */ -#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 */ +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 */ /* GPS */ /* R2H use different handle for GPS on/off */ -#define METHOD_GPS_ON "SDON" -#define METHOD_GPS_OFF "SDOF" -#define METHOD_GPS_STATUS "GPST" +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"); /* Keyboard light */ -#define METHOD_KBD_LIGHT_SET "SLKB" -#define METHOD_KBD_LIGHT_GET "GLKB" +ASUS_HANDLE(kled_set, ASUS_HOTK_PREFIX "SLKB"); +ASUS_HANDLE(kled_get, ASUS_HOTK_PREFIX "GLKB"); /* - * Define a specific led structure to keep the main structure clean + * This is the main structure, we can use it to store anything interesting + * about the hotk device */ -struct asus_led { - int wk; - struct work_struct work; - struct led_classdev led; - struct asus_laptop *asus; - const char *method; +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; }; /* - * This is the main structure, we can use it to store anything interesting - * about the hotk device + * 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 */ -struct asus_laptop { - char *name; /* laptop name */ +static struct acpi_table_header *asus_info; - 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; +/* The actual device the driver binds to */ +static struct asus_hotk *hotk; - struct input_dev *inputdev; - struct key_entry *keymap; +/* + * The hotkey driver declaration + */ +static const struct acpi_device_id asus_device_ids[] = { + {"ATK0100", 0}, + {"ATK0101", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, asus_device_ids); - 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; +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); - int wireless_status; - bool have_rsts; - int lcd_state; +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 rfkill *gps_rfkill; +/* The backlight device /sys/class/backlight */ +static struct backlight_device *asus_backlight_device; - 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; +/* + * 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, }; -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}, +/* + * 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 \ + } + +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; }; +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}, + {KE_END, 0}, +}; /* * This function evaluates an ACPI method, given an int as parameter, the @@ -288,8 +339,8 @@ static const struct key_entry asus_keymap[] = { * * returns 0 if write is successful, -1 else. */ -static int write_acpi_int_ret(acpi_handle handle, const char *method, int val, - struct acpi_buffer *output) +static int write_acpi_int(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 */ @@ -310,82 +361,102 @@ static int write_acpi_int_ret(acpi_handle handle, const char *method, int val, return -1; } -static int write_acpi_int(acpi_handle handle, const char *method, int val) +static int read_wireless_status(int mask) { - 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 (method == NULL) - return -ENODEV; + unsigned long long status; + acpi_status rv = AE_OK; - if (ret) - status = acpi_get_handle(handle, (char *)method, - ret); - else { - acpi_handle dummy; + if (!wireless_status_handle) + return (hotk->status & mask) ? 1 : 0; - status = acpi_get_handle(handle, (char *)method, - &dummy); - } + 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 (status != AE_OK) { - if (ret) - pr_warning("Error finding %s\n", method); - return -ENODEV; - } - return 0; + return (hotk->status & mask) ? 1 : 0; } -/* Generic LED function */ -static int asus_led_set(struct asus_laptop *asus, const char *method, - int value) +static int read_gps_status(void) { - if (!strcmp(method, METHOD_MLED)) - value = !value; - else if (!strcmp(method, METHOD_GLED)) - value = !value + 1; + 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"); else - value = !!value; + return status ? 1 : 0; - return write_acpi_int(asus->handle, method, value); + return (hotk->status & GPS_ON) ? 1 : 0; } -/* - * LEDs - */ -/* /sys/class/led handlers */ -static void asus_led_cdev_set(struct led_classdev *led_cdev, - enum led_brightness value) +/* Generic LED functions */ +static int read_status(int mask) { - struct asus_led *led = container_of(led_cdev, struct asus_led, led); - struct asus_laptop *asus = led->asus; + /* 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(); - led->wk = !!value; - queue_work(asus->led_workqueue, &led->work); + return (hotk->status & mask) ? 1 : 0; } -static void asus_led_cdev_update(struct work_struct *work) +static void write_status(acpi_handle handle, int out, int mask) { - struct asus_led *led = container_of(work, struct asus_led, work); - struct asus_laptop *asus = led->asus; + hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask); - asus_led_set(asus, led->method, led->wk); -} + 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; + } -static enum led_brightness asus_led_cdev_get(struct led_classdev *led_cdev) -{ - return led_cdev->brightness; + if (write_acpi_int(handle, NULL, out, NULL)) + pr_warning(" write failed %x\n", mask); } +/* /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); + /* - * Keyboard backlight (also a LED) + * Keyboard backlight */ -static int asus_kled_lvl(struct asus_laptop *asus) +static int get_kled_lvl(void) { unsigned long long kblv; struct acpi_object_list params; @@ -397,183 +468,75 @@ static int asus_kled_lvl(struct asus_laptop *asus) in_obj.type = ACPI_TYPE_INTEGER; in_obj.integer.value = 2; - rv = acpi_evaluate_integer(asus->handle, METHOD_KBD_LIGHT_GET, - ¶ms, &kblv); + rv = acpi_evaluate_integer(kled_get_handle, NULL, ¶ms, &kblv); if (ACPI_FAILURE(rv)) { pr_warning("Error reading kled level\n"); - return -ENODEV; + return 0; } return kblv; } -static int asus_kled_set(struct asus_laptop *asus, int kblv) +static int set_kled_lvl(int kblv) { if (kblv > 0) kblv = (1 << 7) | (kblv & 0x7F); else kblv = 0; - if (write_acpi_int(asus->handle, METHOD_KBD_LIGHT_SET, kblv)) { + if (write_acpi_int(kled_set_handle, NULL, kblv, NULL)) { pr_warning("Keyboard LED display write failed\n"); return -EINVAL; } return 0; } -static void asus_kled_cdev_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - 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 asus_kled_cdev_update(struct work_struct *work) -{ - 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 asus_kled_cdev_get(struct led_classdev *led_cdev) +static void kled_led_set(struct led_classdev *led_cdev, + enum led_brightness value) { - struct asus_led *led = container_of(led_cdev, struct asus_led, led); - struct asus_laptop *asus = led->asus; - - return asus_kled_lvl(asus); + kled_led_wk = value; + queue_work(led_workqueue, &kled_led_work); } -static void asus_led_exit(struct asus_laptop *asus) +static void kled_led_update(struct work_struct *ignored) { - 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; - } + set_kled_lvl(kled_led_wk); } -/* 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) +static enum led_brightness kled_led_get(struct led_classdev *led_cdev) { - 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); + return get_kled_lvl(); } -static int asus_led_init(struct asus_laptop *asus) +static int get_lcd_state(void) { - 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; + return read_status(LCD_ON); } -/* - * 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) +static int set_lcd_state(int value) { int lcd = 0; acpi_status status = 0; - lcd = !!value; + lcd = value ? 1 : 0; - if (lcd == asus_lcd_status(asus)) + if (lcd == get_lcd_state()) return 0; - if (!lcd_switch_handle) - return -ENODEV; - - status = acpi_evaluate_object(lcd_switch_handle, - NULL, NULL, NULL); + if (lcd_switch_handle) { + status = acpi_evaluate_object(lcd_switch_handle, + NULL, NULL, NULL); - if (ACPI_FAILURE(status)) { - pr_warning("Error switching LCD\n"); - return -ENODEV; + if (ACPI_FAILURE(status)) + pr_warning("Error switching LCD\n"); } - asus->lcd_state = lcd; + write_status(NULL, lcd, LCD_ON); return 0; } -static void lcd_blank(struct asus_laptop *asus, int blank) +static void lcd_blank(int blank) { - struct backlight_device *bd = asus->backlight_device; - - asus->lcd_state = (blank == FB_BLANK_UNBLANK); + struct backlight_device *bd = asus_backlight_device; if (bd) { bd->props.power = blank; @@ -581,91 +544,44 @@ static void lcd_blank(struct asus_laptop *asus, int blank) } } -static int asus_read_brightness(struct backlight_device *bd) +static int 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(asus->handle, METHOD_BRIGHTNESS_GET, - NULL, &value); + rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value); if (ACPI_FAILURE(rv)) pr_warning("Error reading brightness\n"); return value; } -static int asus_set_brightness(struct backlight_device *bd, int value) +static int set_brightness(struct backlight_device *bd, int value) { - struct asus_laptop *asus = bl_get_data(bd); + int ret = 0; + + value = (0 < value) ? ((15 < value) ? 15 : value) : 0; + /* 0 <= value <= 15 */ - if (write_acpi_int(asus->handle, METHOD_BRIGHTNESS_SET, value)) { + if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) { pr_warning("Error changing brightness\n"); - return -EIO; + ret = -EIO; } - return 0; + + return ret; } 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); + rv = set_brightness(bd, value); if (rv) return rv; value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0; - 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; + return set_lcd_state(value); } /* @@ -680,26 +596,25 @@ static void asus_backlight_exit(struct asus_laptop *asus) 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_LAPTOP_NAME " " ASUS_LAPTOP_VERSION "\n"); - len += sprintf(page + len, "Model reference : %s\n", asus->name); + len += sprintf(page, ASUS_HOTK_NAME " " ASUS_LAPTOP_VERSION "\n"); + len += sprintf(page + len, "Model reference : %s\n", hotk->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(asus->handle, "SFUN", NULL, &temp); + rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp); if (!ACPI_FAILURE(rv)) len += sprintf(page + len, "SFUN value : %#x\n", (uint) temp); @@ -709,7 +624,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(asus->handle, "HRWS", NULL, &temp); + rv = acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &temp); if (!ACPI_FAILURE(rv)) len += sprintf(page + len, "HRWS value : %#x\n", (uint) temp); @@ -720,26 +635,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(asus->handle, "ASYM", NULL, &temp); + rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp); if (!ACPI_FAILURE(rv)) len += sprintf(page + len, "ASYM value : %#x\n", (uint) temp); - if (asus->dsdt_info) { - snprintf(buf, 16, "%d", asus->dsdt_info->length); + if (asus_info) { + snprintf(buf, 16, "%d", asus_info->length); len += sprintf(page + len, "DSDT length : %s\n", buf); - snprintf(buf, 16, "%d", asus->dsdt_info->checksum); + snprintf(buf, 16, "%d", asus_info->checksum); len += sprintf(page + len, "DSDT checksum : %s\n", buf); - snprintf(buf, 16, "%d", asus->dsdt_info->revision); + snprintf(buf, 16, "%d", asus_info->revision); len += sprintf(page + len, "DSDT revision : %s\n", buf); - snprintf(buf, 7, "%s", asus->dsdt_info->oem_id); + snprintf(buf, 7, "%s", asus_info->oem_id); len += sprintf(page + len, "OEM id : %s\n", buf); - snprintf(buf, 9, "%s", asus->dsdt_info->oem_table_id); + snprintf(buf, 9, "%s", asus_info->oem_table_id); len += sprintf(page + len, "OEM table id : %s\n", buf); - snprintf(buf, 16, "%x", asus->dsdt_info->oem_revision); + snprintf(buf, 16, "%x", asus_info->oem_revision); len += sprintf(page + len, "OEM revision : 0x%s\n", buf); - snprintf(buf, 5, "%s", asus->dsdt_info->asl_compiler_id); + snprintf(buf, 5, "%s", asus_info->asl_compiler_id); len += sprintf(page + len, "ASL comp vendor id : %s\n", buf); - snprintf(buf, 16, "%x", asus->dsdt_info->asl_compiler_revision); + snprintf(buf, 16, "%x", asus_info->asl_compiler_revision); len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf); } @@ -757,9 +672,8 @@ static int parse_arg(const char *buf, unsigned long count, int *val) return count; } -static ssize_t sysfs_acpi_set(struct asus_laptop *asus, - const char *buf, size_t count, - const char *method) +static ssize_t store_status(const char *buf, size_t count, + acpi_handle handle, int mask) { int rv, value; int out = 0; @@ -768,8 +682,8 @@ static ssize_t sysfs_acpi_set(struct asus_laptop *asus, if (rv > 0) out = value ? 1 : 0; - if (write_acpi_int(asus->handle, method, value)) - return -ENODEV; + write_status(handle, out, mask); + return rv; } @@ -779,116 +693,67 @@ static ssize_t sysfs_acpi_set(struct asus_laptop *asus, static ssize_t show_ledd(struct device *dev, struct device_attribute *attr, char *buf) { - struct asus_laptop *asus = dev_get_drvdata(dev); - - return sprintf(buf, "0x%08x\n", asus->ledd_status); + return sprintf(buf, "0x%08x\n", hotk->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(asus->handle, METHOD_LEDD, value)) + if (write_acpi_int(ledd_set_handle, NULL, value, NULL)) pr_warning("LED display write failed\n"); else - asus->ledd_status = (u32) value; + hotk->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) { - struct asus_laptop *asus = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", asus_wireless_status(asus, WL_RSTS)); + return sprintf(buf, "%d\n", read_status(WL_ON)); } static ssize_t store_wlan(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_WLAN); + return store_status(buf, count, wl_switch_handle, WL_ON); } /* * 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) { - struct asus_laptop *asus = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", asus_wireless_status(asus, BT_RSTS)); + return sprintf(buf, "%d\n", read_status(BT_ON)); } static ssize_t store_bluetooth(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_BLUETOOTH); + return store_status(buf, count, bt_switch_handle, BT_ON); } /* * Display */ -static void asus_set_display(struct asus_laptop *asus, int value) +static void set_display(int value) { /* no sanity check needed for now */ - if (write_acpi_int(asus->handle, METHOD_SWITCH_DISPLAY, value)) + if (write_acpi_int(display_set_handle, NULL, value, NULL)) pr_warning("Error setting display\n"); return; } -static int read_display(struct asus_laptop *asus) +static int read_display(void) { unsigned long long value = 0; acpi_status rv = AE_OK; @@ -904,7 +769,7 @@ static int read_display(struct asus_laptop *asus) 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; } @@ -916,11 +781,7 @@ static int read_display(struct asus_laptop *asus) static ssize_t show_disp(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", read_display()); } /* @@ -933,72 +794,65 @@ 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) - asus_set_display(asus, value); + set_display(value); return rv; } /* * Light Sens */ -static void asus_als_switch(struct asus_laptop *asus, int value) +static void set_light_sens_switch(int value) { - if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value)) + if (write_acpi_int(ls_switch_handle, NULL, value, NULL)) pr_warning("Error setting light sensor switch\n"); - asus->light_switch = value; + hotk->light_switch = value; } static ssize_t show_lssw(struct device *dev, struct device_attribute *attr, char *buf) { - struct asus_laptop *asus = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", asus->light_switch); + return sprintf(buf, "%d\n", hotk->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) - asus_als_switch(asus, value ? 1 : 0); + set_light_sens_switch(value ? 1 : 0); return rv; } -static void asus_als_level(struct asus_laptop *asus, int value) +static void set_light_sens_level(int value) { - if (write_acpi_int(asus->handle, METHOD_ALS_LEVEL, value)) + if (write_acpi_int(ls_level_handle, NULL, value, NULL)) pr_warning("Error setting light sensor level\n"); - asus->light_level = value; + hotk->light_level = value; } static ssize_t show_lslvl(struct device *dev, struct device_attribute *attr, char *buf) { - struct asus_laptop *asus = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", asus->light_level); + return sprintf(buf, "%d\n", hotk->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 */ - asus_als_level(asus, value); + set_light_sens_level(value); } return rv; @@ -1007,308 +861,196 @@ 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) { - struct asus_laptop *asus = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", asus_gps_status(asus)); + return sprintf(buf, "%d\n", read_status(GPS_ON)); } static ssize_t store_gps(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - 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; + return store_status(buf, count, NULL, GPS_ON); } /* - * rfkill + * Hotkey functions */ -static int asus_gps_rfkill_set(void *data, bool blocked) +static struct key_entry *asus_get_entry_by_scancode(int code) { - acpi_handle handle = data; + struct key_entry *key; - return asus_gps_switch(handle, !blocked); -} + for (key = asus_keymap; key->type != KE_END; key++) + if (code == key->code) + 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) +static struct key_entry *asus_get_entry_by_keycode(int code) { - if (asus->gps_rfkill) { - rfkill_unregister(asus->gps_rfkill); - rfkill_destroy(asus->gps_rfkill); - asus->gps_rfkill = NULL; - } + struct key_entry *key; + + for (key = asus_keymap; key->type != KE_END; key++) + if (code == key->keycode && key->type == KE_KEY) + return key; + + return NULL; } -static int asus_rfkill_init(struct asus_laptop *asus) +static int asus_getkeycode(struct input_dev *dev, int scancode, int *keycode) { - int result; + struct key_entry *key = asus_get_entry_by_scancode(scancode); - 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)) + if (key && key->type == KE_KEY) { + *keycode = key->keycode; 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 result; + return -EINVAL; } -/* - * Input device (i.e. hotkeys) - */ -static void asus_input_notify(struct asus_laptop *asus, int event) +static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode) { - if (asus->inputdev) - sparse_keymap_report_event(asus->inputdev, event, 1, true); -} + struct key_entry *key; + int old_keycode; -static int asus_input_init(struct asus_laptop *asus) -{ - struct input_dev *input; - int error; + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; - input = input_allocate_device(); - if (!input) { - pr_info("Unable to allocate input device\n"); + 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); 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; -err_keymap: - sparse_keymap_free(input); -err_device: - input_free_device(input); - return error; -} - -static void asus_input_exit(struct asus_laptop *asus) -{ - if (asus->inputdev) { - sparse_keymap_free(asus->inputdev); - input_unregister_device(asus->inputdev); - } + return -EINVAL; } -/* - * ACPI driver - */ -static void asus_acpi_notify(struct acpi_device *device, u32 event) +static void asus_hotk_notify(struct acpi_device *device, u32 event) { - struct asus_laptop *asus = acpi_driver_data(device); + static struct key_entry *key; u16 count; + /* TODO Find a better way to handle events count. */ + if (!hotk) + return; + /* * 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); + 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); + } - /* 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 = 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, count); - /* 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); + 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; } - return ; } - asus_input_notify(asus, event); } -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; - } - - if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) { - err = device_create_file(&device->dev, &dev_attr_bluetooth); - if (err) - return err; +#define ASUS_CREATE_DEVICE_ATTR(_name) \ + struct device_attribute dev_attr_##_name = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0 }, \ + .show = NULL, \ + .store = NULL, \ } - if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) { - err = device_create_file(&device->dev, &dev_attr_display); - if (err) - return err; - } - - if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) { - err = device_create_file(&device->dev, &dev_attr_ledd); - 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_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 struct attribute_group asuspf_attribute_group = { + .attrs = asuspf_attributes +}; - 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; - } +static struct platform_driver asuspf_driver = { + .driver = { + .name = ASUS_HOTK_FILE, + .owner = THIS_MODULE, + } +}; - return err; -} +static struct platform_device *asuspf_device; -static int asus_platform_init(struct asus_laptop *asus) +static void asus_hotk_add_fs(void) { - int err; - - asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1); - if (!asus->platform_device) - return -ENOMEM; - platform_set_drvdata(asus->platform_device, asus); + ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL); - err = platform_device_add(asus->platform_device); - if (err) - goto fail_platform_device; + if (wl_switch_handle) + ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan); - err = asus_sysfs_init(asus); - if (err) - goto fail_sysfs; - return 0; + if (bt_switch_handle) + ASUS_SET_DEVICE_ATTR(bluetooth, 0644, + show_bluetooth, store_bluetooth); -fail_sysfs: - asus_sysfs_exit(asus); - platform_device_del(asus->platform_device); -fail_platform_device: - platform_device_put(asus->platform_device); - return err; -} + 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); -static void asus_platform_exit(struct asus_laptop *asus) -{ - asus_sysfs_exit(asus); - platform_device_unregister(asus->platform_device); -} + if (ledd_set_handle) + ASUS_SET_DEVICE_ATTR(ledd, 0644, show_ledd, store_ledd); -static struct platform_driver platform_driver = { - .driver = { - .name = ASUS_LAPTOP_FILE, - .owner = THIS_MODULE, + 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); } -}; + + if (gps_status_handle && gps_on_handle && gps_off_handle) + ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps); +} static int asus_handle_init(char *name, acpi_handle * handle, char **paths, int num_paths) @@ -1331,11 +1073,10 @@ static int asus_handle_init(char *name, acpi_handle * handle, 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 - * struct + * 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 */ -static int asus_laptop_get_info(struct asus_laptop *asus) +static int asus_hotk_get_info(void) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *model = NULL; @@ -1348,21 +1089,22 @@ static int asus_laptop_get_info(struct asus_laptop *asus) * 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. + * HID), this bit will be moved. A global variable asus_info contains + * the DSDT header. */ - status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus->dsdt_info); + status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_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_ret(asus->handle, "INIT", 0, &buffer)) { + if (write_acpi_int(hotk->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(asus->handle, "BSTS", NULL, &bsts_result); + acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result); if (ACPI_FAILURE(status)) pr_warning("Error calling BSTS\n"); else if (bsts_result) @@ -1370,8 +1112,8 @@ static int asus_laptop_get_info(struct asus_laptop *asus) (uint) bsts_result); /* This too ... */ - if (write_acpi_int(asus->handle, "CWAP", wapf)) - pr_err("Error calling CWAP(%d)\n", wapf); + write_acpi_int(hotk->handle, "CWAP", wapf, NULL); + /* * Try to match the object returned by INIT to the specific model. * Handle every possible object (or the lack of thereof) the DSDT @@ -1392,209 +1134,396 @@ static int asus_laptop_get_info(struct asus_laptop *asus) break; } } - asus->name = kstrdup(string, GFP_KERNEL); - if (!asus->name) + hotk->name = kstrdup(string, GFP_KERNEL); + if (!hotk->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(asus->handle, "HRWS", NULL, &hwrs_result); - if (!ACPI_FAILURE(status)) - pr_notice(" HRWS returned %x", (int)hwrs_result); + 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); - if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL)) - asus->have_rsts = true; + ASUS_HANDLE_INIT(brightness_set); + ASUS_HANDLE_INIT(brightness_get); - /* 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 bool asus_device_present; +static int asus_input_init(void) +{ + const struct key_entry *key; + int result; -static int __devinit asus_acpi_init(struct asus_laptop *asus) + 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) { int result = 0; - result = acpi_bus_get_status(asus->device); + result = acpi_bus_get_status(hotk->device); if (result) return result; - if (!asus->device->status.present) { + + if (hotk->device->status.present) { + result = asus_hotk_get_info(); + } else { pr_err("Hotkey device not present, aborting\n"); - return -ENODEV; + return -EINVAL; } - result = asus_laptop_get_info(asus); + 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(); if (result) - return result; + goto end; + + asus_hotk_add_fs(); + + asus_hotk_found = 1; /* WLED and BLED are on by default */ - if (bluetooth_status >= 0) - asus_bluetooth_set(asus, !!bluetooth_status); + 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 (wlan_status >= 0) - asus_wlan_set(asus, !!wlan_status); + /* LCD Backlight is on by default */ + write_status(NULL, 1, LCD_ON); /* Keyboard Backlight is on by default */ - if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL)) - asus_kled_set(asus, 1); + if (kled_set_handle) + set_kled_lvl(1); /* LED display is off by default */ - asus->ledd_status = 0xFFF; + hotk->ledd_status = 0xFFF; /* Set initial values of light sensor and level */ - asus->light_switch = 0; /* Default to light sensor disabled */ - asus->light_level = 5; /* level 5 for sensor sensitivity */ + hotk->light_switch = 0; /* Default to light sensor disabled */ + hotk->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)) { - asus_als_switch(asus, asus->light_switch); - asus_als_level(asus, asus->light_level); + 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); } - asus->lcd_state = 1; /* LCD should be on when the module load */ return result; } -static int __devinit asus_acpi_add(struct acpi_device *device) +static int asus_hotk_remove(struct acpi_device *device, int type) { - struct asus_laptop *asus; - int result; + kfree(hotk->name); + kfree(hotk); - 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; + return 0; +} - result = asus_acpi_init(asus); - if (result) - goto fail_platform; +static void asus_backlight_exit(void) +{ + if (asus_backlight_device) + backlight_device_unregister(asus_backlight_device); +} - /* - * 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; +#define ASUS_LED_UNREGISTER(object) \ + if (object##_led.dev) \ + led_classdev_unregister(&object##_led) - 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 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); +} - result = asus_input_init(asus); - if (result) - goto fail_input; +static void asus_input_exit(void) +{ + if (hotk->inputdev) + input_unregister_device(hotk->inputdev); +} - result = asus_led_init(asus); - if (result) - goto fail_led; +static void __exit asus_laptop_exit(void) +{ + asus_backlight_exit(); + asus_led_exit(); + asus_input_exit(); - result = asus_rfkill_init(asus); - if (result) - goto fail_rfkill; + 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); +} - asus_device_present = true; +static int asus_backlight_init(struct device *dev) +{ + struct backlight_device *bd; + + 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); + } + + asus_backlight_device = bd; + + bd->props.max_brightness = 15; + bd->props.brightness = read_brightness(NULL); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); + } 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); +static int asus_led_register(acpi_handle handle, + struct led_classdev *ldev, struct device *dev) +{ + if (!handle) + return 0; - return result; + return led_classdev_register(dev, ldev); } -static int asus_acpi_remove(struct acpi_device *device, int type) +#define ASUS_LED_REGISTER(object, device) \ + asus_led_register(object##_set_handle, &object##_led, device) + +static int asus_led_init(struct device *dev) { - struct asus_laptop *asus = acpi_driver_data(device); + int rv; - asus_backlight_exit(asus); - asus_rfkill_exit(asus); - asus_led_exit(asus); - asus_input_exit(asus); - asus_platform_exit(asus); + rv = ASUS_LED_REGISTER(mled, dev); + if (rv) + goto out; - kfree(asus->name); - kfree(asus); - return 0; -} + rv = ASUS_LED_REGISTER(tled, dev); + if (rv) + goto out1; -static const struct acpi_device_id asus_device_ids[] = { - {"ATK0100", 0}, - {"ATK0101", 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, asus_device_ids); + rv = ASUS_LED_REGISTER(rled, dev); + if (rv) + goto out2; -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, - }, -}; + rv = ASUS_LED_REGISTER(pled, dev); + if (rv) + goto out3; + + rv = ASUS_LED_REGISTER(gled, dev); + if (rv) + goto out4; + + 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; + + 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 int __init asus_laptop_init(void) { int result; - result = platform_driver_register(&platform_driver); + result = acpi_bus_register_driver(&asus_hotk_driver); if (result < 0) return result; - 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; + /* + * 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 = 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_no_device: - acpi_bus_unregister_driver(&asus_acpi_driver); -fail_acpi_driver: - platform_driver_unregister(&platform_driver); - return result; -} +fail_backlight: + asus_led_exit(); -static void __exit asus_laptop_exit(void) -{ - acpi_bus_unregister_driver(&asus_acpi_driver); - platform_driver_unregister(&platform_driver); +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: + + return result; } module_init(asus_laptop_init);