diff options
author | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 2010-05-16 19:40:56 -0300 |
---|---|---|
committer | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 2010-05-16 19:40:56 -0300 |
commit | b65b34895437915f411882dd40d704eb0863ffb0 (patch) | |
tree | 3e9302ab09c9a9068fd8fd80708ef3a35b55e665 /drivers/platform | |
parent | 88cc83772a3c7756b9f2b4ba835545ad90a08409 (diff) | |
parent | b57f95a38233a2e73b679bea4a5453a1cc2a1cc9 (diff) |
Merge mainline (v2.6.34-rc7)
Diffstat (limited to 'drivers/platform')
24 files changed, 2467 insertions, 1179 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 11fce79b61d..6c3320d7505 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 @@ -79,6 +81,7 @@ config DELL_LAPTOP depends on BACKLIGHT_CLASS_DEVICE depends on RFKILL || RFKILL = n depends on POWER_SUPPLY + depends on SERIO_I8042 default n ---help--- This driver adds support for rfkill and backlight control to Dell @@ -147,6 +150,7 @@ config MSI_LAPTOP tristate "MSI Laptop Extras" depends on ACPI depends on BACKLIGHT_CLASS_DEVICE + depends on RFKILL ---help--- This is a driver for laptops built by MSI (MICRO-STAR INTERNATIONAL): @@ -176,6 +180,7 @@ config COMPAL_LAPTOP tristate "Compal Laptop Extras" depends on ACPI depends on BACKLIGHT_CLASS_DEVICE + depends on RFKILL ---help--- This is a driver for laptops built by Compal: @@ -380,6 +385,17 @@ config EEEPC_LAPTOP If you have an Eee PC laptop, say Y or M here. +config EEEPC_WMI + tristate "Eee PC WMI Hotkey Driver (EXPERIMENTAL)" + depends on ACPI_WMI + depends on INPUT + depends on EXPERIMENTAL + select INPUT_SPARSEKMAP + ---help--- + Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. + + To compile this driver as a module, choose M here: the module will + be called eeepc-wmi. config ACPI_WMI tristate "WMI" diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 9cd9fa0a27e..a906490e353 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o +obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 226b3e93498..1ea6c434d33 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -36,6 +36,7 @@ #include <linux/rfkill.h> #include <linux/workqueue.h> #include <linux/debugfs.h> +#include <linux/slab.h> #include <acpi/acpi_drivers.h> @@ -922,9 +923,13 @@ static struct backlight_ops acer_bl_ops = { static int __devinit acer_backlight_init(struct device *dev) { + struct backlight_properties props; struct backlight_device *bd; - bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops); + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = max_brightness; + bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops, + &props); if (IS_ERR(bd)) { printk(ACER_ERR "Could not register Acer backlight device\n"); acer_backlight_device = NULL; @@ -935,7 +940,6 @@ static int __devinit acer_backlight_init(struct device *dev) bd->props.power = FB_BLANK_UNBLANK; bd->props.brightness = read_brightness(bd); - bd->props.max_brightness = max_brightness; backlight_update_status(bd); return 0; } diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 61a1c750365..efe8f638890 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -45,58 +45,24 @@ #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 <linux/slab.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 +79,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 int wlan_status = 1; +static int bluetooth_status = 1; + +module_param(wlan_status, int, 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, int, 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 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 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 +289,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 +311,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); +} - if (!wireless_status_handle) - return (hotk->status & mask) ? 1 : 0; +static int acpi_check_handle(acpi_handle handle, const char *method, + acpi_handle *ret) +{ + acpi_status status; - 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 (method == NULL) + return -ENODEV; + + 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); + struct asus_led *led = container_of(work, struct asus_led, work); + struct asus_laptop *asus = led->asus; - 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; - } - - 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 +398,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) +{ + 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) { - kled_led_wk = value; - queue_work(led_workqueue, &kled_led_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 void kled_led_update(struct work_struct *ignored) +static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev) { - set_kled_lvl(kled_led_wk); + struct asus_led *led = container_of(led_cdev, struct asus_led, led); + struct asus_laptop *asus = led->asus; + + return asus_kled_lvl(asus); } -static enum led_brightness kled_led_get(struct led_classdev *led_cdev) +static void asus_led_exit(struct asus_laptop *asus) { - return get_kled_lvl(); + 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 get_lcd_state(void) +/* 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) { - return read_status(LCD_ON); + 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 set_lcd_state(int value) +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 +582,94 @@ 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; + struct asus_laptop *asus = bl_get_data(bd); - value = (0 < value) ? ((15 < value) ? 15 : value) : 0; - /* 0 <= value <= 15 */ - - 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; + struct backlight_properties props; + + if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) && + !acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) && + lcd_switch_handle) { + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 15; + + bd = backlight_device_register(ASUS_LAPTOP_FILE, dev, + asus, &asusbl_ops, &props); + 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.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 +684,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 +713,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 +724,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 +761,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 +772,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 +783,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 +908,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 +920,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 +937,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 +1011,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; + acpi_handle handle = data; - for (key = asus_keymap; key->type != KE_END; key++) - if (code == key->code) - return key; - - 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; + } - return -EINVAL; + asus->inputdev = input; + return 0; + +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; + } + + return err; +} - if (wl_switch_handle) - ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan); +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 +1335,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 +1352,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 +1374,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 +1396,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); + acpi_evaluate_integer(asus->handle, "HRWS", NULL, &hwrs_result); + if (!ACPI_FAILURE(status)) + pr_notice(" HRWS returned %x", (int)hwrs_result); - ASUS_HANDLE_INIT(wireless_status); - - 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 (bluetooth_status >= 0) + asus_bluetooth_set(asus, !!bluetooth_status); - /* 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); - - /* 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 */ - - 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); + asus->light_switch = 0; /* Default to light sensor disabled */ + asus->light_level = 5; /* level 5 for sensor sensitivity */ -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/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index c1d2aeeea94..92fd30c9379 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c @@ -32,6 +32,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/types.h> #include <linux/proc_fs.h> @@ -1225,9 +1226,8 @@ static int asus_model_match(char *model) else if (strncmp(model, "M2N", 3) == 0 || strncmp(model, "M3N", 3) == 0 || strncmp(model, "M5N", 3) == 0 || - strncmp(model, "M6N", 3) == 0 || strncmp(model, "S1N", 3) == 0 || - strncmp(model, "S5N", 3) == 0 || strncmp(model, "W1N", 3) == 0) + strncmp(model, "S5N", 3) == 0) return xxN; else if (strncmp(model, "M1", 2) == 0) return M1A; @@ -1482,6 +1482,7 @@ static void asus_acpi_exit(void) static int __init asus_acpi_init(void) { + struct backlight_properties props; int result; result = acpi_bus_register_driver(&asus_hotk_driver); @@ -1508,15 +1509,17 @@ static int __init asus_acpi_init(void) return -ENODEV; } + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 15; asus_backlight_device = backlight_device_register("asus", NULL, NULL, - &asus_backlight_data); + &asus_backlight_data, + &props); if (IS_ERR(asus_backlight_device)) { printk(KERN_ERR "Could not register asus backlight device\n"); asus_backlight_device = NULL; asus_acpi_exit(); return -ENODEV; } - asus_backlight_device->props.max_brightness = 15; return 0; } diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index ed90082cdf1..7f9e5ddc949 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -19,6 +19,7 @@ #include <linux/init.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/workqueue.h> #include <acpi/acpi_drivers.h> #include <linux/backlight.h> @@ -34,6 +35,11 @@ struct cmpc_accel { #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 +#define CMPC_ACCEL_HID "ACCE0000" +#define CMPC_TABLET_HID "TBLT0000" +#define CMPC_BL_HID "IPML200" +#define CMPC_KEYS_HID "FnBT0000" + /* * Generic input device code. */ @@ -282,10 +288,9 @@ static int cmpc_accel_remove(struct acpi_device *acpi, int type) } static const struct acpi_device_id cmpc_accel_device_ids[] = { - {"ACCE0000", 0}, + {CMPC_ACCEL_HID, 0}, {"", 0} }; -MODULE_DEVICE_TABLE(acpi, cmpc_accel_device_ids); static struct acpi_driver cmpc_accel_acpi_driver = { .owner = THIS_MODULE, @@ -366,10 +371,9 @@ static int cmpc_tablet_resume(struct acpi_device *acpi) } static const struct acpi_device_id cmpc_tablet_device_ids[] = { - {"TBLT0000", 0}, + {CMPC_TABLET_HID, 0}, {"", 0} }; -MODULE_DEVICE_TABLE(acpi, cmpc_tablet_device_ids); static struct acpi_driver cmpc_tablet_acpi_driver = { .owner = THIS_MODULE, @@ -452,18 +456,22 @@ static int cmpc_bl_update_status(struct backlight_device *bd) return -1; } -static struct backlight_ops cmpc_bl_ops = { +static const struct backlight_ops cmpc_bl_ops = { .get_brightness = cmpc_bl_get_brightness, .update_status = cmpc_bl_update_status }; static int cmpc_bl_add(struct acpi_device *acpi) { + struct backlight_properties props; struct backlight_device *bd; - bd = backlight_device_register("cmpc_bl", &acpi->dev, - acpi->handle, &cmpc_bl_ops); - bd->props.max_brightness = 7; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 7; + bd = backlight_device_register("cmpc_bl", &acpi->dev, acpi->handle, + &cmpc_bl_ops, &props); + if (IS_ERR(bd)) + return PTR_ERR(bd); dev_set_drvdata(&acpi->dev, bd); return 0; } @@ -477,17 +485,16 @@ static int cmpc_bl_remove(struct acpi_device *acpi, int type) return 0; } -static const struct acpi_device_id cmpc_device_ids[] = { - {"IPML200", 0}, +static const struct acpi_device_id cmpc_bl_device_ids[] = { + {CMPC_BL_HID, 0}, {"", 0} }; -MODULE_DEVICE_TABLE(acpi, cmpc_device_ids); static struct acpi_driver cmpc_bl_acpi_driver = { .owner = THIS_MODULE, .name = "cmpc", .class = "cmpc", - .ids = cmpc_device_ids, + .ids = cmpc_bl_device_ids, .ops = { .add = cmpc_bl_add, .remove = cmpc_bl_remove @@ -505,6 +512,10 @@ static int cmpc_keys_codes[] = { KEY_BRIGHTNESSDOWN, KEY_BRIGHTNESSUP, KEY_VENDOR, + KEY_UNKNOWN, + KEY_CAMERA, + KEY_BACK, + KEY_FORWARD, KEY_MAX }; @@ -540,10 +551,9 @@ static int cmpc_keys_remove(struct acpi_device *acpi, int type) } static const struct acpi_device_id cmpc_keys_device_ids[] = { - {"FnBT0000", 0}, + {CMPC_KEYS_HID, 0}, {"", 0} }; -MODULE_DEVICE_TABLE(acpi, cmpc_keys_device_ids); static struct acpi_driver cmpc_keys_acpi_driver = { .owner = THIS_MODULE, @@ -607,3 +617,13 @@ static void cmpc_exit(void) module_init(cmpc_init); module_exit(cmpc_exit); + +static const struct acpi_device_id cmpc_device_ids[] = { + {CMPC_ACCEL_HID, 0}, + {CMPC_TABLET_HID, 0}, + {CMPC_BL_HID, 0}, + {CMPC_KEYS_HID, 0}, + {"", 0} +}; + +MODULE_DEVICE_TABLE(acpi, cmpc_device_ids); diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 1a387e79f71..71ff1545a93 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -26,17 +26,8 @@ /* * comapl-laptop.c - Compal laptop support. * - * This driver exports a few files in /sys/devices/platform/compal-laptop/: - * - * wlan - wlan subsystem state: contains 0 or 1 (rw) - * - * bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw) - * - * raw - raw value taken from embedded controller register (ro) - * - * In addition to these platform device attributes the driver - * registers itself in the Linux backlight control subsystem and is - * available to userspace under /sys/class/backlight/compal-laptop/. + * The driver registers itself with the rfkill subsystem and + * the Linux backlight control subsystem. * * This driver might work on other laptops produced by Compal. If you * want to try it you can pass force=1 as argument to the module which @@ -51,6 +42,7 @@ #include <linux/dmi.h> #include <linux/backlight.h> #include <linux/platform_device.h> +#include <linux/rfkill.h> #define COMPAL_DRIVER_VERSION "0.2.6" @@ -63,6 +55,10 @@ #define WLAN_MASK 0x01 #define BT_MASK 0x02 +static struct rfkill *wifi_rfkill; +static struct rfkill *bt_rfkill; +static struct platform_device *compal_device; + static int force; module_param(force, bool, 0); MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); @@ -88,65 +84,75 @@ static int get_lcd_level(void) return (int) result; } -static int set_wlan_state(int state) +static int compal_rfkill_set(void *data, bool blocked) { + unsigned long radio = (unsigned long) data; u8 result, value; ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); - if ((result & KILLSWITCH_MASK) == 0) - return -EINVAL; - else { - if (state) - value = (u8) (result | WLAN_MASK); - else - value = (u8) (result & ~WLAN_MASK); - ec_write(COMPAL_EC_COMMAND_WIRELESS, value); - } + if (!blocked) + value = (u8) (result | radio); + else + value = (u8) (result & ~radio); + ec_write(COMPAL_EC_COMMAND_WIRELESS, value); return 0; } -static int set_bluetooth_state(int state) +static void compal_rfkill_poll(struct rfkill *rfkill, void *data) { - u8 result, value; + u8 result; + bool hw_blocked; ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); - if ((result & KILLSWITCH_MASK) == 0) - return -EINVAL; - else { - if (state) - value = (u8) (result | BT_MASK); - else - value = (u8) (result & ~BT_MASK); - ec_write(COMPAL_EC_COMMAND_WIRELESS, value); - } - - return 0; + hw_blocked = !(result & KILLSWITCH_MASK); + rfkill_set_hw_state(rfkill, hw_blocked); } -static int get_wireless_state(int *wlan, int *bluetooth) +static const struct rfkill_ops compal_rfkill_ops = { + .poll = compal_rfkill_poll, + .set_block = compal_rfkill_set, +}; + +static int setup_rfkill(void) { - u8 result; + int ret; - ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); + wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev, + RFKILL_TYPE_WLAN, &compal_rfkill_ops, + (void *) WLAN_MASK); + if (!wifi_rfkill) + return -ENOMEM; - if (wlan) { - if ((result & KILLSWITCH_MASK) == 0) - *wlan = 0; - else - *wlan = result & WLAN_MASK; - } + ret = rfkill_register(wifi_rfkill); + if (ret) + goto err_wifi; - if (bluetooth) { - if ((result & KILLSWITCH_MASK) == 0) - *bluetooth = 0; - else - *bluetooth = (result & BT_MASK) >> 1; + bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev, + RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops, + (void *) BT_MASK); + if (!bt_rfkill) { + ret = -ENOMEM; + goto err_allocate_bt; } + ret = rfkill_register(bt_rfkill); + if (ret) + goto err_register_bt; return 0; + +err_register_bt: + rfkill_destroy(bt_rfkill); + +err_allocate_bt: + rfkill_unregister(wifi_rfkill); + +err_wifi: + rfkill_destroy(wifi_rfkill); + + return ret; } /* Backlight device stuff */ @@ -169,86 +175,6 @@ static struct backlight_ops compalbl_ops = { static struct backlight_device *compalbl_device; -/* Platform device */ - -static ssize_t show_wlan(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int ret, enabled; - - ret = get_wireless_state(&enabled, NULL); - if (ret < 0) - return ret; - - return sprintf(buf, "%i\n", enabled); -} - -static ssize_t show_raw(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 result; - - ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); - - return sprintf(buf, "%i\n", result); -} - -static ssize_t show_bluetooth(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int ret, enabled; - - ret = get_wireless_state(NULL, &enabled); - if (ret < 0) - return ret; - - return sprintf(buf, "%i\n", enabled); -} - -static ssize_t store_wlan_state(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int state, ret; - - if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) - return -EINVAL; - - ret = set_wlan_state(state); - if (ret < 0) - return ret; - - return count; -} - -static ssize_t store_bluetooth_state(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int state, ret; - - if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) - return -EINVAL; - - ret = set_bluetooth_state(state); - if (ret < 0) - return ret; - - return count; -} - -static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state); -static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state); -static DEVICE_ATTR(raw, 0444, show_raw, NULL); - -static struct attribute *compal_attributes[] = { - &dev_attr_bluetooth.attr, - &dev_attr_wlan.attr, - &dev_attr_raw.attr, - NULL -}; - -static struct attribute_group compal_attribute_group = { - .attrs = compal_attributes -}; static struct platform_driver compal_driver = { .driver = { @@ -257,8 +183,6 @@ static struct platform_driver compal_driver = { } }; -static struct platform_device *compal_device; - /* Initialization */ static int dmi_check_cb(const struct dmi_system_id *id) @@ -310,6 +234,47 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { }, .callback = dmi_check_cb }, + { + .ident = "Dell Mini 9", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), + }, + .callback = dmi_check_cb + }, + { + .ident = "Dell Mini 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), + }, + .callback = dmi_check_cb + }, + { + .ident = "Dell Mini 10v", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), + }, + .callback = dmi_check_cb + }, + { + .ident = "Dell Inspiron 11z", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), + }, + .callback = dmi_check_cb + }, + { + .ident = "Dell Mini 12", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), + }, + .callback = dmi_check_cb + }, + { } }; @@ -326,12 +291,15 @@ static int __init compal_init(void) /* Register backlight stuff */ if (!acpi_video_backlight_support()) { - compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, - &compalbl_ops); + struct backlight_properties props; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = COMPAL_LCD_LEVEL_MAX - 1; + compalbl_device = backlight_device_register("compal-laptop", + NULL, NULL, + &compalbl_ops, + &props); if (IS_ERR(compalbl_device)) return PTR_ERR(compalbl_device); - - compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; } ret = platform_driver_register(&compal_driver); @@ -348,23 +316,21 @@ static int __init compal_init(void) ret = platform_device_add(compal_device); if (ret) - goto fail_platform_device1; + goto fail_platform_device; - ret = sysfs_create_group(&compal_device->dev.kobj, - &compal_attribute_group); + ret = setup_rfkill(); if (ret) - goto fail_platform_device2; + goto fail_rfkill; printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION " successfully loaded.\n"); return 0; -fail_platform_device2: - +fail_rfkill: platform_device_del(compal_device); -fail_platform_device1: +fail_platform_device: platform_device_put(compal_device); @@ -382,10 +348,13 @@ fail_backlight: static void __exit compal_cleanup(void) { - sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group); platform_device_unregister(compal_device); platform_driver_unregister(&compal_driver); backlight_device_unregister(compalbl_device); + rfkill_unregister(wifi_rfkill); + rfkill_destroy(wifi_rfkill); + rfkill_unregister(bt_rfkill); + rfkill_destroy(bt_rfkill); printk(KERN_INFO "compal-laptop: driver unloaded.\n"); } @@ -403,3 +372,8 @@ MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*"); MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); +MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*"); +MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*"); +MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*"); +MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*"); +MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*"); diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 3780994dc8f..661e3ac4d5b 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -22,6 +22,9 @@ #include <linux/rfkill.h> #include <linux/power_supply.h> #include <linux/acpi.h> +#include <linux/mm.h> +#include <linux/i8042.h> +#include <linux/slab.h> #include "../../firmware/dcdbas.h" #define BRIGHTNESS_TOKEN 0x7d @@ -79,9 +82,73 @@ static const struct dmi_system_id __initdata dell_device_table[] = { DMI_MATCH(DMI_CHASSIS_TYPE, "8"), }, }, + { + .ident = "Dell Computer Corporation", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), + }, + }, { } }; +static struct dmi_system_id __devinitdata dell_blacklist[] = { + /* Supported by compal-laptop */ + { + .ident = "Dell Mini 9", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), + }, + }, + { + .ident = "Dell Mini 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), + }, + }, + { + .ident = "Dell Mini 10v", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), + }, + }, + { + .ident = "Dell Inspiron 11z", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), + }, + }, + { + .ident = "Dell Mini 12", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), + }, + }, + {} +}; + +static struct calling_interface_buffer *buffer; +static struct page *bufferpage; +static DEFINE_MUTEX(buffer_mutex); + +static int hwswitch_state; + +static void get_buffer(void) +{ + mutex_lock(&buffer_mutex); + memset(buffer, 0, sizeof(struct calling_interface_buffer)); +} + +static void release_buffer(void) +{ + mutex_unlock(&buffer_mutex); +} + static void __init parse_da_table(const struct dmi_header *dm) { /* Final token is a terminator, so we don't want to copy it */ @@ -160,6 +227,8 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, /* Derived from information in DellWirelessCtl.cpp: Class 17, select 11 is radio control. It returns an array of 32-bit values. + Input byte 0 = 0: Wireless information + result[0]: return code result[1]: Bit 0: Hardware switch supported @@ -180,33 +249,62 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, Bits 20-31: Reserved result[2]: NVRAM size in bytes result[3]: NVRAM format version number + + Input byte 0 = 2: Wireless switch configuration + result[0]: return code + result[1]: + Bit 0: Wifi controlled by switch + Bit 1: Bluetooth controlled by switch + Bit 2: WWAN controlled by switch + Bits 3-6: Reserved + Bit 7: Wireless switch config locked + Bit 8: Wifi locator enabled + Bits 9-14: Reserved + Bit 15: Wifi locator setting locked + Bits 16-31: Reserved */ static int dell_rfkill_set(void *data, bool blocked) { - struct calling_interface_buffer buffer; int disable = blocked ? 1 : 0; unsigned long radio = (unsigned long)data; + int hwswitch_bit = (unsigned long)data - 1; + int ret = 0; + + get_buffer(); + dell_send_request(buffer, 17, 11); + + /* If the hardware switch controls this radio, and the hardware + switch is disabled, don't allow changing the software state */ + if ((hwswitch_state & BIT(hwswitch_bit)) && + !(buffer->output[1] & BIT(16))) { + ret = -EINVAL; + goto out; + } - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - buffer.input[0] = (1 | (radio<<8) | (disable << 16)); - dell_send_request(&buffer, 17, 11); + buffer->input[0] = (1 | (radio<<8) | (disable << 16)); + dell_send_request(buffer, 17, 11); - return 0; +out: + release_buffer(); + return ret; } static void dell_rfkill_query(struct rfkill *rfkill, void *data) { - struct calling_interface_buffer buffer; int status; int bit = (unsigned long)data + 16; + int hwswitch_bit = (unsigned long)data - 1; - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - dell_send_request(&buffer, 17, 11); - status = buffer.output[1]; + get_buffer(); + dell_send_request(buffer, 17, 11); + status = buffer->output[1]; + release_buffer(); rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); - rfkill_set_hw_state(rfkill, !(status & BIT(16))); + + if (hwswitch_state & (BIT(hwswitch_bit))) + rfkill_set_hw_state(rfkill, !(status & BIT(16))); } static const struct rfkill_ops dell_rfkill_ops = { @@ -214,15 +312,36 @@ static const struct rfkill_ops dell_rfkill_ops = { .query = dell_rfkill_query, }; +static void dell_update_rfkill(struct work_struct *ignored) +{ + if (wifi_rfkill) + dell_rfkill_query(wifi_rfkill, (void *)1); + if (bluetooth_rfkill) + dell_rfkill_query(bluetooth_rfkill, (void *)2); + if (wwan_rfkill) + dell_rfkill_query(wwan_rfkill, (void *)3); +} +static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); + + static int __init dell_setup_rfkill(void) { - struct calling_interface_buffer buffer; int status; int ret; - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - dell_send_request(&buffer, 17, 11); - status = buffer.output[1]; + if (dmi_check_system(dell_blacklist)) { + printk(KERN_INFO "dell-laptop: Blacklisted hardware detected - " + "not enabling rfkill\n"); + return 0; + } + + get_buffer(); + dell_send_request(buffer, 17, 11); + status = buffer->output[1]; + buffer->input[0] = 0x2; + dell_send_request(buffer, 17, 11); + hwswitch_state = buffer->output[1]; + release_buffer(); if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev, @@ -298,39 +417,49 @@ static void dell_cleanup_rfkill(void) static int dell_send_intensity(struct backlight_device *bd) { - struct calling_interface_buffer buffer; + int ret = 0; - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); - buffer.input[1] = bd->props.brightness; + get_buffer(); + buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); + buffer->input[1] = bd->props.brightness; - if (buffer.input[0] == -1) - return -ENODEV; + if (buffer->input[0] == -1) { + ret = -ENODEV; + goto out; + } if (power_supply_is_system_supplied() > 0) - dell_send_request(&buffer, 1, 2); + dell_send_request(buffer, 1, 2); else - dell_send_request(&buffer, 1, 1); + dell_send_request(buffer, 1, 1); +out: + release_buffer(); return 0; } static int dell_get_intensity(struct backlight_device *bd) { - struct calling_interface_buffer buffer; + int ret = 0; - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); + get_buffer(); + buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); - if (buffer.input[0] == -1) - return -ENODEV; + if (buffer->input[0] == -1) { + ret = -ENODEV; + goto out; + } if (power_supply_is_system_supplied() > 0) - dell_send_request(&buffer, 0, 2); + dell_send_request(buffer, 0, 2); else - dell_send_request(&buffer, 0, 1); + dell_send_request(buffer, 0, 1); - return buffer.output[1]; +out: + release_buffer(); + if (ret) + return ret; + return buffer->output[1]; } static struct backlight_ops dell_ops = { @@ -338,9 +467,32 @@ static struct backlight_ops dell_ops = { .update_status = dell_send_intensity, }; +bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, + struct serio *port) +{ + static bool extended; + + if (str & 0x20) + return false; + + if (unlikely(data == 0xe0)) { + extended = true; + return false; + } else if (unlikely(extended)) { + switch (data) { + case 0x8: + schedule_delayed_work(&dell_rfkill_work, + round_jiffies_relative(HZ)); + break; + } + extended = false; + } + + return false; +} + static int __init dell_init(void) { - struct calling_interface_buffer buffer; int max_intensity = 0; int ret; @@ -366,6 +518,17 @@ static int __init dell_init(void) if (ret) goto fail_platform_device2; + /* + * Allocate buffer below 4GB for SMI data--only 32-bit physical addr + * is passed to SMI handler. + */ + bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32); + + if (!bufferpage) + goto fail_buffer; + buffer = page_address(bufferpage); + mutex_init(&buffer_mutex); + ret = dell_setup_rfkill(); if (ret) { @@ -373,6 +536,13 @@ static int __init dell_init(void) goto fail_rfkill; } + ret = i8042_install_filter(dell_laptop_i8042_filter); + if (ret) { + printk(KERN_WARNING + "dell-laptop: Unable to install key filter\n"); + goto fail_filter; + } + #ifdef CONFIG_ACPI /* In the event of an ACPI backlight being available, don't * register the platform controller. @@ -381,19 +551,23 @@ static int __init dell_init(void) return 0; #endif - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); - - if (buffer.input[0] != -1) { - dell_send_request(&buffer, 0, 2); - max_intensity = buffer.output[3]; + get_buffer(); + buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); + if (buffer->input[0] != -1) { + dell_send_request(buffer, 0, 2); + max_intensity = buffer->output[3]; } + release_buffer(); if (max_intensity) { - dell_backlight_device = backlight_device_register( - "dell_backlight", - &platform_device->dev, NULL, - &dell_ops); + struct backlight_properties props; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = max_intensity; + dell_backlight_device = backlight_device_register("dell_backlight", + &platform_device->dev, + NULL, + &dell_ops, + &props); if (IS_ERR(dell_backlight_device)) { ret = PTR_ERR(dell_backlight_device); @@ -401,7 +575,6 @@ static int __init dell_init(void) goto fail_backlight; } - dell_backlight_device->props.max_brightness = max_intensity; dell_backlight_device->props.brightness = dell_get_intensity(dell_backlight_device); backlight_update_status(dell_backlight_device); @@ -410,8 +583,13 @@ static int __init dell_init(void) return 0; fail_backlight: + i8042_remove_filter(dell_laptop_i8042_filter); + cancel_delayed_work_sync(&dell_rfkill_work); +fail_filter: dell_cleanup_rfkill(); fail_rfkill: + free_page((unsigned long)bufferpage); +fail_buffer: platform_device_del(platform_device); fail_platform_device2: platform_device_put(platform_device); @@ -424,8 +602,16 @@ fail_platform_driver: static void __exit dell_exit(void) { + i8042_remove_filter(dell_laptop_i8042_filter); + cancel_delayed_work_sync(&dell_rfkill_work); backlight_device_unregister(dell_backlight_device); dell_cleanup_rfkill(); + if (platform_device) { + platform_device_unregister(platform_device); + platform_driver_unregister(&platform_driver); + } + kfree(da_tokens); + free_page((unsigned long)buffer); } module_init(dell_init); @@ -435,3 +621,4 @@ MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); MODULE_DESCRIPTION("Dell laptop driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); +MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*"); diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 1b1dddbd574..66f53c3c35e 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -26,6 +26,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/types.h> #include <linux/input.h> #include <acpi/acpi_drivers.h> @@ -142,7 +143,7 @@ static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap; static struct input_dev *dell_wmi_input_dev; -static struct key_entry *dell_wmi_get_entry_by_scancode(int code) +static struct key_entry *dell_wmi_get_entry_by_scancode(unsigned int code) { struct key_entry *key; @@ -153,7 +154,7 @@ static struct key_entry *dell_wmi_get_entry_by_scancode(int code) return NULL; } -static struct key_entry *dell_wmi_get_entry_by_keycode(int keycode) +static struct key_entry *dell_wmi_get_entry_by_keycode(unsigned int keycode) { struct key_entry *key; @@ -164,8 +165,8 @@ static struct key_entry *dell_wmi_get_entry_by_keycode(int keycode) return NULL; } -static int dell_wmi_getkeycode(struct input_dev *dev, int scancode, - int *keycode) +static int dell_wmi_getkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int *keycode) { struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode); @@ -177,13 +178,11 @@ static int dell_wmi_getkeycode(struct input_dev *dev, int scancode, return -EINVAL; } -static int dell_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode) +static int dell_wmi_setkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int keycode) { struct key_entry *key; - int old_keycode; - - if (keycode < 0 || keycode > KEY_MAX) - return -EINVAL; + unsigned int old_keycode; key = dell_wmi_get_entry_by_scancode(scancode); if (key && key->type == KE_KEY) { @@ -218,6 +217,7 @@ static void dell_wmi_notify(u32 value, void *context) if (dell_new_hk_type && (buffer_entry[1] != 0x10)) { printk(KERN_INFO "dell-wmi: Received unknown WMI event" " (0x%x)\n", buffer_entry[1]); + kfree(obj); return; } @@ -235,7 +235,7 @@ static void dell_wmi_notify(u32 value, void *context) key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) { /* Don't report brightness notifications that will also * come via ACPI */ - return; + ; } else { input_report_key(dell_wmi_input_dev, key->keycode, 1); input_sync(dell_wmi_input_dev); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index e2be6bb33d9..0306174ba87 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -27,6 +27,7 @@ #include <linux/fb.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> +#include <linux/slab.h> #include <acpi/acpi_drivers.h> #include <acpi/acpi_bus.h> #include <linux/uaccess.h> @@ -168,7 +169,6 @@ struct eeepc_laptop { struct backlight_device *backlight_device; struct input_dev *inputdev; - struct key_entry *keymap; struct rfkill *wlan_rfkill; struct rfkill *bluetooth_rfkill; @@ -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) { @@ -1113,18 +1131,20 @@ static int eeepc_backlight_notify(struct eeepc_laptop *eeepc) static int eeepc_backlight_init(struct eeepc_laptop *eeepc) { + struct backlight_properties props; struct backlight_device *bd; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 15; bd = backlight_device_register(EEEPC_LAPTOP_FILE, - &eeepc->platform_device->dev, - eeepc, &eeepcbl_ops); + &eeepc->platform_device->dev, eeepc, + &eeepcbl_ops, &props); if (IS_ERR(bd)) { pr_err("Could not register eeepc backlight device\n"); eeepc->backlight_device = NULL; return PTR_ERR(bd); } eeepc->backlight_device = bd; - bd->props.max_brightness = 15; bd->props.brightness = read_brightness(bd); bd->props.power = FB_BLANK_UNBLANK; backlight_update_status(bd); @@ -1183,8 +1203,8 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc) static void eeepc_input_exit(struct eeepc_laptop *eeepc) { if (eeepc->inputdev) { + sparse_keymap_free(eeepc->inputdev); input_unregister_device(eeepc->inputdev); - kfree(eeepc->keymap); } } @@ -1277,7 +1297,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"); } diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c new file mode 100644 index 00000000000..b227eb469f4 --- /dev/null +++ b/drivers/platform/x86/eeepc-wmi.c @@ -0,0 +1,413 @@ +/* + * Eee PC WMI hotkey driver + * + * Copyright(C) 2010 Intel Corporation. + * + * Portions based on wistron_btns.c: + * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> + * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> + * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/platform_device.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +#define EEEPC_WMI_FILE "eeepc-wmi" + +MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>"); +MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); +MODULE_LICENSE("GPL"); + +#define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" +#define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" + +MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); +MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); + +#define NOTIFY_BRNUP_MIN 0x11 +#define NOTIFY_BRNUP_MAX 0x1f +#define NOTIFY_BRNDOWN_MIN 0x20 +#define NOTIFY_BRNDOWN_MAX 0x2e + +#define EEEPC_WMI_METHODID_DEVS 0x53564544 +#define EEEPC_WMI_METHODID_DSTS 0x53544344 + +#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 + +static const struct key_entry eeepc_wmi_keymap[] = { + /* Sleep already handled via generic ACPI code */ + { KE_KEY, 0x5d, { KEY_WLAN } }, + { KE_KEY, 0x32, { KEY_MUTE } }, + { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, + { KE_KEY, 0x30, { KEY_VOLUMEUP } }, + { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, + { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, + { KE_END, 0}, +}; + +struct bios_args { + u32 dev_id; + u32 ctrl_param; +}; + +struct eeepc_wmi { + struct input_dev *inputdev; + struct backlight_device *backlight_device; +}; + +static struct platform_device *platform_device; + +static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) +{ + int err; + + eeepc->inputdev = input_allocate_device(); + if (!eeepc->inputdev) + return -ENOMEM; + + eeepc->inputdev->name = "Eee PC WMI hotkeys"; + eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; + eeepc->inputdev->id.bustype = BUS_HOST; + eeepc->inputdev->dev.parent = &platform_device->dev; + + err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); + if (err) + goto err_free_dev; + + err = input_register_device(eeepc->inputdev); + if (err) + goto err_free_keymap; + + return 0; + +err_free_keymap: + sparse_keymap_free(eeepc->inputdev); +err_free_dev: + input_free_device(eeepc->inputdev); + return err; +} + +static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc) +{ + if (eeepc->inputdev) { + sparse_keymap_free(eeepc->inputdev); + input_unregister_device(eeepc->inputdev); + } + + eeepc->inputdev = NULL; +} + +static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) +{ + struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + u32 tmp; + + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, + 1, EEEPC_WMI_METHODID_DSTS, &input, &output); + + if (ACPI_FAILURE(status)) + return status; + + obj = (union acpi_object *)output.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + tmp = (u32)obj->integer.value; + else + tmp = 0; + + if (ctrl_param) + *ctrl_param = tmp; + + kfree(obj); + + return status; + +} + +static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) +{ + struct bios_args args = { + .dev_id = dev_id, + .ctrl_param = ctrl_param, + }; + struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; + acpi_status status; + + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, + 1, EEEPC_WMI_METHODID_DEVS, &input, NULL); + + return status; +} + +static int read_brightness(struct backlight_device *bd) +{ + static u32 ctrl_param; + acpi_status status; + + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param); + + if (ACPI_FAILURE(status)) + return -1; + else + return ctrl_param & 0xFF; +} + +static int update_bl_status(struct backlight_device *bd) +{ + + static u32 ctrl_param; + acpi_status status; + + ctrl_param = bd->props.brightness; + + status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, ctrl_param); + + if (ACPI_FAILURE(status)) + return -1; + else + return 0; +} + +static const struct backlight_ops eeepc_wmi_bl_ops = { + .get_brightness = read_brightness, + .update_status = update_bl_status, +}; + +static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code) +{ + struct backlight_device *bd = eeepc->backlight_device; + int old = bd->props.brightness; + int new; + + if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) + new = code - NOTIFY_BRNUP_MIN + 1; + else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX) + new = code - NOTIFY_BRNDOWN_MIN; + + bd->props.brightness = new; + backlight_update_status(bd); + backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); + + return old; +} + +static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) +{ + struct backlight_device *bd; + struct backlight_properties props; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 15; + bd = backlight_device_register(EEEPC_WMI_FILE, + &platform_device->dev, eeepc, + &eeepc_wmi_bl_ops, &props); + if (IS_ERR(bd)) { + pr_err("Could not register backlight device\n"); + return PTR_ERR(bd); + } + + eeepc->backlight_device = bd; + + bd->props.brightness = read_brightness(bd); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); + + return 0; +} + +static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc) +{ + if (eeepc->backlight_device) + backlight_device_unregister(eeepc->backlight_device); + + eeepc->backlight_device = NULL; +} + +static void eeepc_wmi_notify(u32 value, void *context) +{ + struct eeepc_wmi *eeepc = context; + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + int code; + int orig_code; + + status = wmi_get_event_data(value, &response); + if (status != AE_OK) { + pr_err("bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + + if (obj && obj->type == ACPI_TYPE_INTEGER) { + code = obj->integer.value; + orig_code = code; + + if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) + code = NOTIFY_BRNUP_MIN; + else if (code >= NOTIFY_BRNDOWN_MIN && + code <= NOTIFY_BRNDOWN_MAX) + code = NOTIFY_BRNDOWN_MIN; + + if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { + if (!acpi_video_backlight_support()) + eeepc_wmi_backlight_notify(eeepc, orig_code); + } + + if (!sparse_keymap_report_event(eeepc->inputdev, + code, 1, true)) + pr_info("Unknown key %x pressed\n", code); + } + + kfree(obj); +} + +static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) +{ + struct eeepc_wmi *eeepc; + int err; + acpi_status status; + + eeepc = platform_get_drvdata(device); + + err = eeepc_wmi_input_init(eeepc); + if (err) + goto error_input; + + if (!acpi_video_backlight_support()) { + err = eeepc_wmi_backlight_init(eeepc); + if (err) + goto error_backlight; + } else + pr_info("Backlight controlled by ACPI video driver\n"); + + status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, + eeepc_wmi_notify, eeepc); + if (ACPI_FAILURE(status)) { + pr_err("Unable to register notify handler - %d\n", + status); + err = -ENODEV; + goto error_wmi; + } + + return 0; + +error_wmi: + eeepc_wmi_backlight_exit(eeepc); +error_backlight: + eeepc_wmi_input_exit(eeepc); +error_input: + return err; +} + +static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) +{ + struct eeepc_wmi *eeepc; + + eeepc = platform_get_drvdata(device); + wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); + eeepc_wmi_backlight_exit(eeepc); + eeepc_wmi_input_exit(eeepc); + + return 0; +} + +static struct platform_driver platform_driver = { + .driver = { + .name = EEEPC_WMI_FILE, + .owner = THIS_MODULE, + }, + .probe = eeepc_wmi_platform_probe, + .remove = __devexit_p(eeepc_wmi_platform_remove), +}; + +static int __init eeepc_wmi_init(void) +{ + struct eeepc_wmi *eeepc; + int err; + + if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || + !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) { + pr_warning("No known WMI GUID found\n"); + return -ENODEV; + } + + eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); + if (!eeepc) + return -ENOMEM; + + platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); + if (!platform_device) { + pr_warning("Unable to allocate platform device\n"); + err = -ENOMEM; + goto fail_platform; + } + + err = platform_device_add(platform_device); + if (err) { + pr_warning("Unable to add platform device\n"); + goto put_dev; + } + + platform_set_drvdata(platform_device, eeepc); + + err = platform_driver_register(&platform_driver); + if (err) { + pr_warning("Unable to register platform driver\n"); + goto del_dev; + } + + return 0; + +del_dev: + platform_device_del(platform_device); +put_dev: + platform_device_put(platform_device); +fail_platform: + kfree(eeepc); + + return err; +} + +static void __exit eeepc_wmi_exit(void) +{ + struct eeepc_wmi *eeepc; + + eeepc = platform_get_drvdata(platform_device); + platform_driver_unregister(&platform_driver); + platform_device_unregister(platform_device); + kfree(eeepc); +} + +module_init(eeepc_wmi_init); +module_exit(eeepc_wmi_exit); diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 5f3320d468f..47b4fd75aa3 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -66,6 +66,7 @@ #include <linux/kfifo.h> #include <linux/video_output.h> #include <linux/platform_device.h> +#include <linux/slab.h> #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) #include <linux/leds.h> #endif @@ -1126,16 +1127,20 @@ static int __init fujitsu_init(void) /* Register backlight stuff */ if (!acpi_video_backlight_support()) { - fujitsu->bl_device = - backlight_device_register("fujitsu-laptop", NULL, NULL, - &fujitsubl_ops); + struct backlight_properties props; + + memset(&props, 0, sizeof(struct backlight_properties)); + max_brightness = fujitsu->max_brightness; + props.max_brightness = max_brightness - 1; + fujitsu->bl_device = backlight_device_register("fujitsu-laptop", + NULL, NULL, + &fujitsubl_ops, + &props); if (IS_ERR(fujitsu->bl_device)) { ret = PTR_ERR(fujitsu->bl_device); fujitsu->bl_device = NULL; goto fail_sysfs_group; } - max_brightness = fujitsu->max_brightness; - fujitsu->bl_device->props.max_brightness = max_brightness - 1; fujitsu->bl_device->props.brightness = fujitsu->brightness_level; } diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index ad4c414dbfb..51c07a05a7b 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -26,6 +26,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/types.h> #include <linux/input.h> #include <acpi/acpi_drivers.h> @@ -57,7 +58,7 @@ enum hp_wmi_radio { HPWMI_WWAN = 2, }; -static int __init hp_wmi_bios_setup(struct platform_device *device); +static int __devinit hp_wmi_bios_setup(struct platform_device *device); static int __exit hp_wmi_bios_remove(struct platform_device *device); static int hp_wmi_resume_handler(struct device *device); @@ -89,6 +90,7 @@ static struct key_entry hp_wmi_keymap[] = { {KE_KEY, 0x20e6, KEY_PROG1}, {KE_KEY, 0x2142, KEY_MEDIA}, {KE_KEY, 0x213b, KEY_INFO}, + {KE_KEY, 0x2169, KEY_DIRECTION}, {KE_KEY, 0x231b, KEY_HELP}, {KE_END, 0} }; @@ -277,7 +279,7 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); -static struct key_entry *hp_wmi_get_entry_by_scancode(int code) +static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code) { struct key_entry *key; @@ -288,7 +290,7 @@ static struct key_entry *hp_wmi_get_entry_by_scancode(int code) return NULL; } -static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode) +static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode) { struct key_entry *key; @@ -299,7 +301,8 @@ static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode) return NULL; } -static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode) +static int hp_wmi_getkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int *keycode) { struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); @@ -311,13 +314,11 @@ static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode) return -EINVAL; } -static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode) +static int hp_wmi_setkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int keycode) { struct key_entry *key; - int old_keycode; - - if (keycode < 0 || keycode > KEY_MAX) - return -EINVAL; + unsigned int old_keycode; key = hp_wmi_get_entry_by_scancode(scancode); if (key && key->type == KE_KEY) { @@ -446,7 +447,7 @@ static void cleanup_sysfs(struct platform_device *device) device_remove_file(&device->dev, &dev_attr_tablet); } -static int __init hp_wmi_bios_setup(struct platform_device *device) +static int __devinit hp_wmi_bios_setup(struct platform_device *device) { int err; int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index f0a90a6bf39..2f795ce2b93 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -30,6 +30,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/types.h> #include <linux/pci.h> #include <linux/pm.h> @@ -396,6 +397,7 @@ static int intel_menlow_add_one_attribute(char *name, int mode, void *show, if (!attr) return -ENOMEM; + sysfs_attr_init(&attr->attr.attr); /* That is consistent naming :D */ attr->attr.attr.name = name; attr->attr.attr.mode = mode; attr->attr.show = show; diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 759763d18e4..996223a7c00 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -58,6 +58,7 @@ #include <linux/dmi.h> #include <linux/backlight.h> #include <linux/platform_device.h> +#include <linux/rfkill.h> #define MSI_DRIVER_VERSION "0.5" @@ -66,6 +67,20 @@ #define MSI_EC_COMMAND_WIRELESS 0x10 #define MSI_EC_COMMAND_LCD_LEVEL 0x11 +#define MSI_STANDARD_EC_COMMAND_ADDRESS 0x2e +#define MSI_STANDARD_EC_BLUETOOTH_MASK (1 << 0) +#define MSI_STANDARD_EC_WEBCAM_MASK (1 << 1) +#define MSI_STANDARD_EC_WLAN_MASK (1 << 3) +#define MSI_STANDARD_EC_3G_MASK (1 << 4) + +/* For set SCM load flag to disable BIOS fn key */ +#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d +#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) + +static int msi_laptop_resume(struct platform_device *device); + +#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f + static int force; module_param(force, bool, 0); MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); @@ -74,6 +89,23 @@ static int auto_brightness; module_param(auto_brightness, int, 0); MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); +static bool old_ec_model; +static int wlan_s, bluetooth_s, threeg_s; +static int threeg_exists; + +/* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G, + * those netbook will load the SCM (windows app) to disable the original + * Wlan/Bluetooth control by BIOS when user press fn key, then control + * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user + * cann't on/off 3G module on those 3G netbook. + * On Linux, msi-laptop driver will do the same thing to disable the + * original BIOS control, then might need use HAL or other userland + * application to do the software control that simulate with SCM. + * e.g. MSI N034 netbook + */ +static bool load_scm_model; +static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg; + /* Hardware access */ static int set_lcd_level(int level) @@ -130,6 +162,35 @@ static int set_auto_brightness(int enable) return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1); } +static ssize_t set_device_state(const char *buf, size_t count, u8 mask) +{ + int status; + u8 wdata = 0, rdata; + int result; + + if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1)) + return -EINVAL; + + /* read current device state */ + result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata); + if (result < 0) + return -EINVAL; + + if (!!(rdata & mask) != status) { + /* reverse device bit */ + if (rdata & mask) + wdata = rdata & ~mask; + else + wdata = rdata | mask; + + result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata); + if (result < 0) + return -EINVAL; + } + + return count; +} + static int get_wireless_state(int *wlan, int *bluetooth) { u8 wdata = 0, rdata; @@ -148,6 +209,38 @@ static int get_wireless_state(int *wlan, int *bluetooth) return 0; } +static int get_wireless_state_ec_standard(void) +{ + u8 rdata; + int result; + + result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata); + if (result < 0) + return -1; + + wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK); + + bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK); + + threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK); + + return 0; +} + +static int get_threeg_exists(void) +{ + u8 rdata; + int result; + + result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata); + if (result < 0) + return -1; + + threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK); + + return 0; +} + /* Backlight device stuff */ static int bl_get_brightness(struct backlight_device *b) @@ -176,26 +269,71 @@ static ssize_t show_wlan(struct device *dev, int ret, enabled; - ret = get_wireless_state(&enabled, NULL); + if (old_ec_model) { + ret = get_wireless_state(&enabled, NULL); + } else { + ret = get_wireless_state_ec_standard(); + enabled = wlan_s; + } if (ret < 0) return ret; return sprintf(buf, "%i\n", enabled); } +static ssize_t store_wlan(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK); +} + static ssize_t show_bluetooth(struct device *dev, struct device_attribute *attr, char *buf) { int ret, enabled; - ret = get_wireless_state(NULL, &enabled); + if (old_ec_model) { + ret = get_wireless_state(NULL, &enabled); + } else { + ret = get_wireless_state_ec_standard(); + enabled = bluetooth_s; + } if (ret < 0) return ret; return sprintf(buf, "%i\n", enabled); } +static ssize_t store_bluetooth(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK); +} + +static ssize_t show_threeg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + int ret; + + /* old msi ec not support 3G */ + if (old_ec_model) + return -1; + + ret = get_wireless_state_ec_standard(); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", threeg_s); +} + +static ssize_t store_threeg(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK); +} + static ssize_t show_lcd_level(struct device *dev, struct device_attribute *attr, char *buf) { @@ -258,6 +396,7 @@ static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness); static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); +static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); static struct attribute *msipf_attributes[] = { &dev_attr_lcd_level.attr, @@ -275,7 +414,8 @@ static struct platform_driver msipf_driver = { .driver = { .name = "msi-laptop-pf", .owner = THIS_MODULE, - } + }, + .resume = msi_laptop_resume, }; static struct platform_device *msipf_device; @@ -332,6 +472,192 @@ static struct dmi_system_id __initdata msi_dmi_table[] = { { } }; +static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = { + { + .ident = "MSI N034", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"), + DMI_MATCH(DMI_CHASSIS_VENDOR, + "MICRO-STAR INTERNATIONAL CO., LTD") + }, + .callback = dmi_check_cb + }, + { } +}; + +static int rfkill_bluetooth_set(void *data, bool blocked) +{ + /* Do something with blocked...*/ + /* + * blocked == false is on + * blocked == true is off + */ + if (blocked) + set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); + else + set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); + + return 0; +} + +static int rfkill_wlan_set(void *data, bool blocked) +{ + if (blocked) + set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK); + else + set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK); + + return 0; +} + +static int rfkill_threeg_set(void *data, bool blocked) +{ + if (blocked) + set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK); + else + set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK); + + return 0; +} + +static struct rfkill_ops rfkill_bluetooth_ops = { + .set_block = rfkill_bluetooth_set +}; + +static struct rfkill_ops rfkill_wlan_ops = { + .set_block = rfkill_wlan_set +}; + +static struct rfkill_ops rfkill_threeg_ops = { + .set_block = rfkill_threeg_set +}; + +static void rfkill_cleanup(void) +{ + if (rfk_bluetooth) { + rfkill_unregister(rfk_bluetooth); + rfkill_destroy(rfk_bluetooth); + } + + if (rfk_threeg) { + rfkill_unregister(rfk_threeg); + rfkill_destroy(rfk_threeg); + } + + if (rfk_wlan) { + rfkill_unregister(rfk_wlan); + rfkill_destroy(rfk_wlan); + } +} + +static int rfkill_init(struct platform_device *sdev) +{ + /* add rfkill */ + int retval; + + rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev, + RFKILL_TYPE_BLUETOOTH, + &rfkill_bluetooth_ops, NULL); + if (!rfk_bluetooth) { + retval = -ENOMEM; + goto err_bluetooth; + } + retval = rfkill_register(rfk_bluetooth); + if (retval) + goto err_bluetooth; + + rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN, + &rfkill_wlan_ops, NULL); + if (!rfk_wlan) { + retval = -ENOMEM; + goto err_wlan; + } + retval = rfkill_register(rfk_wlan); + if (retval) + goto err_wlan; + + if (threeg_exists) { + rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev, + RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL); + if (!rfk_threeg) { + retval = -ENOMEM; + goto err_threeg; + } + retval = rfkill_register(rfk_threeg); + if (retval) + goto err_threeg; + } + + return 0; + +err_threeg: + rfkill_destroy(rfk_threeg); + if (rfk_wlan) + rfkill_unregister(rfk_wlan); +err_wlan: + rfkill_destroy(rfk_wlan); + if (rfk_bluetooth) + rfkill_unregister(rfk_bluetooth); +err_bluetooth: + rfkill_destroy(rfk_bluetooth); + + return retval; +} + +static int msi_laptop_resume(struct platform_device *device) +{ + u8 data; + int result; + + if (!load_scm_model) + return 0; + + /* set load SCM to disable hardware control by fn key */ + result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); + if (result < 0) + return result; + + result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, + data | MSI_STANDARD_EC_SCM_LOAD_MASK); + if (result < 0) + return result; + + return 0; +} + +static int load_scm_model_init(struct platform_device *sdev) +{ + u8 data; + int result; + + /* allow userland write sysfs file */ + dev_attr_bluetooth.store = store_bluetooth; + dev_attr_wlan.store = store_wlan; + dev_attr_threeg.store = store_threeg; + dev_attr_bluetooth.attr.mode |= S_IWUSR; + dev_attr_wlan.attr.mode |= S_IWUSR; + dev_attr_threeg.attr.mode |= S_IWUSR; + + /* disable hardware control by fn key */ + result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); + if (result < 0) + return result; + + result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, + data | MSI_STANDARD_EC_SCM_LOAD_MASK); + if (result < 0) + return result; + + /* initial rfkill */ + result = rfkill_init(sdev); + if (result < 0) + return result; + + return 0; +} + static int __init msi_init(void) { int ret; @@ -339,8 +665,14 @@ static int __init msi_init(void) if (acpi_disabled) return -ENODEV; - if (!force && !dmi_check_system(msi_dmi_table)) - return -ENODEV; + if (force || dmi_check_system(msi_dmi_table)) + old_ec_model = 1; + + if (!old_ec_model) + get_threeg_exists(); + + if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table)) + load_scm_model = 1; if (auto_brightness < 0 || auto_brightness > 2) return -EINVAL; @@ -351,11 +683,14 @@ static int __init msi_init(void) printk(KERN_INFO "MSI: Brightness ignored, must be controlled " "by ACPI video driver\n"); } else { + struct backlight_properties props; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = MSI_LCD_LEVEL_MAX - 1; msibl_device = backlight_device_register("msi-laptop-bl", NULL, - NULL, &msibl_ops); + NULL, &msibl_ops, + &props); if (IS_ERR(msibl_device)) return PTR_ERR(msibl_device); - msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1; } ret = platform_driver_register(&msipf_driver); @@ -374,10 +709,23 @@ static int __init msi_init(void) if (ret) goto fail_platform_device1; + if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) { + ret = -EINVAL; + goto fail_platform_device1; + } + ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group); if (ret) goto fail_platform_device2; + if (!old_ec_model) { + if (threeg_exists) + ret = device_create_file(&msipf_device->dev, + &dev_attr_threeg); + if (ret) + goto fail_platform_device2; + } + /* Disable automatic brightness control by default because * this module was probably loaded to do brightness control in * software. */ @@ -412,10 +760,14 @@ static void __exit msi_cleanup(void) { sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); + if (!old_ec_model && threeg_exists) + device_remove_file(&msipf_device->dev, &dev_attr_threeg); platform_device_unregister(msipf_device); platform_driver_unregister(&msipf_driver); backlight_device_unregister(msibl_device); + rfkill_cleanup(); + /* Enable automatic brightness control again */ if (auto_brightness != 2) set_auto_brightness(1); @@ -435,3 +787,4 @@ MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARIN MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*"); MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); +MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*"); diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index f5f70d4c691..d1736009636 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c @@ -26,6 +26,7 @@ #include <linux/input/sparse-keymap.h> #include <linux/acpi.h> #include <linux/backlight.h> +#include <linux/slab.h> MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>"); MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver"); @@ -138,7 +139,7 @@ static int bl_set_status(struct backlight_device *bd) return msi_wmi_set_block(0, backlight_map[bright]); } -static struct backlight_ops msi_backlight_ops = { +static const struct backlight_ops msi_backlight_ops = { .get_brightness = bl_get, .update_status = bl_set_status, }; @@ -249,12 +250,17 @@ static int __init msi_wmi_init(void) goto err_uninstall_notifier; if (!acpi_video_backlight_support()) { - backlight = backlight_device_register(DRV_NAME, - NULL, NULL, &msi_backlight_ops); - if (IS_ERR(backlight)) + struct backlight_properties props; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = ARRAY_SIZE(backlight_map) - 1; + backlight = backlight_device_register(DRV_NAME, NULL, NULL, + &msi_backlight_ops, + &props); + if (IS_ERR(backlight)) { + err = PTR_ERR(backlight); goto err_free_input; + } - backlight->props.max_brightness = ARRAY_SIZE(backlight_map) - 1; err = bl_get(NULL); if (err < 0) goto err_free_backlight; diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index fe7cf0188ac..2fb9a32926f 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -124,6 +124,7 @@ #include <linux/ctype.h> #include <linux/seq_file.h> #include <linux/uaccess.h> +#include <linux/slab.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> #include <linux/input.h> @@ -200,7 +201,7 @@ static struct acpi_driver acpi_pcc_driver = { }; #define KEYMAP_SIZE 11 -static const int initial_keymap[KEYMAP_SIZE] = { +static const unsigned int initial_keymap[KEYMAP_SIZE] = { /* 0 */ KEY_RESERVED, /* 1 */ KEY_BRIGHTNESSDOWN, /* 2 */ KEY_BRIGHTNESSUP, @@ -222,7 +223,7 @@ struct pcc_acpi { struct acpi_device *device; struct input_dev *input_dev; struct backlight_device *backlight; - int keymap[KEYMAP_SIZE]; + unsigned int keymap[KEYMAP_SIZE]; }; struct pcc_keyinput { @@ -352,7 +353,7 @@ static int bl_set_status(struct backlight_device *bd) return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright); } -static struct backlight_ops pcc_backlight_ops = { +static const struct backlight_ops pcc_backlight_ops = { .get_brightness = bl_get, .update_status = bl_set_status, }; @@ -445,7 +446,8 @@ static struct attribute_group pcc_attr_group = { /* hotkey input device driver */ -static int pcc_getkeycode(struct input_dev *dev, int scancode, int *keycode) +static int pcc_getkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int *keycode) { struct pcc_acpi *pcc = input_get_drvdata(dev); @@ -457,7 +459,7 @@ static int pcc_getkeycode(struct input_dev *dev, int scancode, int *keycode) return 0; } -static int keymap_get_by_keycode(struct pcc_acpi *pcc, int keycode) +static int keymap_get_by_keycode(struct pcc_acpi *pcc, unsigned int keycode) { int i; @@ -469,7 +471,8 @@ static int keymap_get_by_keycode(struct pcc_acpi *pcc, int keycode) return 0; } -static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode) +static int pcc_setkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int keycode) { struct pcc_acpi *pcc = input_get_drvdata(dev); int oldkeycode; @@ -477,9 +480,6 @@ static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode) if (scancode >= ARRAY_SIZE(pcc->keymap)) return -EINVAL; - if (keycode < 0 || keycode > KEY_MAX) - return -EINVAL; - oldkeycode = pcc->keymap[scancode]; pcc->keymap[scancode] = keycode; @@ -601,6 +601,7 @@ static int acpi_pcc_hotkey_resume(struct acpi_device *device) static int acpi_pcc_hotkey_add(struct acpi_device *device) { + struct backlight_properties props; struct pcc_acpi *pcc; int num_sifr, result; @@ -638,24 +639,25 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) if (result) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error installing keyinput handler\n")); - goto out_sinf; + goto out_hotkey; } - /* initialize backlight */ - pcc->backlight = backlight_device_register("panasonic", NULL, pcc, - &pcc_backlight_ops); - if (IS_ERR(pcc->backlight)) - goto out_input; - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't retrieve BIOS data\n")); - goto out_backlight; + goto out_input; + } + /* initialize backlight */ + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT]; + pcc->backlight = backlight_device_register("panasonic", NULL, pcc, + &pcc_backlight_ops, &props); + if (IS_ERR(pcc->backlight)) { + result = PTR_ERR(pcc->backlight); + goto out_sinf; } /* read the initial brightness setting from the hardware */ - pcc->backlight->props.max_brightness = - pcc->sinf[SINF_AC_MAX_BRIGHT]; pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT]; /* read the initial sticky key mode from the hardware */ @@ -670,12 +672,12 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) out_backlight: backlight_device_unregister(pcc->backlight); +out_sinf: + kfree(pcc->sinf); out_input: input_unregister_device(pcc->input_dev); /* no need to input_free_device() since core input API refcount and * free()s the device */ -out_sinf: - kfree(pcc->sinf); out_hotkey: kfree(pcc); diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 3f71a605a49..1387c5f9c24 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -58,6 +58,7 @@ #include <linux/kfifo.h> #include <linux/workqueue.h> #include <linux/acpi.h> +#include <linux/slab.h> #include <acpi/acpi_drivers.h> #include <acpi/acpi_bus.h> #include <asm/uaccess.h> @@ -145,7 +146,7 @@ struct sony_laptop_input_s { struct input_dev *key_dev; struct kfifo fifo; spinlock_t fifo_lock; - struct workqueue_struct *wq; + struct timer_list release_key_timer; }; static struct sony_laptop_input_s sony_laptop_input = { @@ -299,20 +300,26 @@ static int sony_laptop_input_keycode_map[] = { }; /* release buttons after a short delay if pressed */ -static void do_sony_laptop_release_key(struct work_struct *work) +static void do_sony_laptop_release_key(unsigned long unused) { struct sony_laptop_keypress kp; + unsigned long flags; + + spin_lock_irqsave(&sony_laptop_input.fifo_lock, flags); - while (kfifo_out_locked(&sony_laptop_input.fifo, (unsigned char *)&kp, - sizeof(kp), &sony_laptop_input.fifo_lock) - == sizeof(kp)) { - msleep(10); + if (kfifo_out(&sony_laptop_input.fifo, + (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) { input_report_key(kp.dev, kp.key, 0); input_sync(kp.dev); } + + /* If there is something in the fifo schedule next release. */ + if (kfifo_len(&sony_laptop_input.fifo) != 0) + mod_timer(&sony_laptop_input.release_key_timer, + jiffies + msecs_to_jiffies(10)); + + spin_unlock_irqrestore(&sony_laptop_input.fifo_lock, flags); } -static DECLARE_WORK(sony_laptop_release_key_work, - do_sony_laptop_release_key); /* forward event to the input subsystem */ static void sony_laptop_report_input_event(u8 event) @@ -366,13 +373,13 @@ static void sony_laptop_report_input_event(u8 event) /* we emit the scancode so we can always remap the key */ input_event(kp.dev, EV_MSC, MSC_SCAN, event); input_sync(kp.dev); - kfifo_in_locked(&sony_laptop_input.fifo, - (unsigned char *)&kp, sizeof(kp), - &sony_laptop_input.fifo_lock); - if (!work_pending(&sony_laptop_release_key_work)) - queue_work(sony_laptop_input.wq, - &sony_laptop_release_key_work); + /* schedule key release */ + kfifo_in_locked(&sony_laptop_input.fifo, + (unsigned char *)&kp, sizeof(kp), + &sony_laptop_input.fifo_lock); + mod_timer(&sony_laptop_input.release_key_timer, + jiffies + msecs_to_jiffies(10)); } else dprintk("unknown input event %.2x\n", event); } @@ -390,27 +397,21 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) /* kfifo */ spin_lock_init(&sony_laptop_input.fifo_lock); - error = - kfifo_alloc(&sony_laptop_input.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); + error = kfifo_alloc(&sony_laptop_input.fifo, + SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); if (error) { printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); goto err_dec_users; } - /* init workqueue */ - sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop"); - if (!sony_laptop_input.wq) { - printk(KERN_ERR DRV_PFX - "Unable to create workqueue.\n"); - error = -ENXIO; - goto err_free_kfifo; - } + setup_timer(&sony_laptop_input.release_key_timer, + do_sony_laptop_release_key, 0); /* input keys */ key_dev = input_allocate_device(); if (!key_dev) { error = -ENOMEM; - goto err_destroy_wq; + goto err_free_kfifo; } key_dev->name = "Sony Vaio Keys"; @@ -419,18 +420,15 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) key_dev->dev.parent = &acpi_device->dev; /* Initialize the Input Drivers: special keys */ - set_bit(EV_KEY, key_dev->evbit); - set_bit(EV_MSC, key_dev->evbit); - set_bit(MSC_SCAN, key_dev->mscbit); + input_set_capability(key_dev, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, key_dev->evbit); key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]); key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map); key_dev->keycode = &sony_laptop_input_keycode_map; - for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) { - if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) { - set_bit(sony_laptop_input_keycode_map[i], - key_dev->keybit); - } - } + for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) + __set_bit(sony_laptop_input_keycode_map[i], key_dev->keybit); + __clear_bit(KEY_RESERVED, key_dev->keybit); error = input_register_device(key_dev); if (error) @@ -450,9 +448,8 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) jog_dev->id.vendor = PCI_VENDOR_ID_SONY; key_dev->dev.parent = &acpi_device->dev; - jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE); - jog_dev->relbit[0] = BIT_MASK(REL_WHEEL); + input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE); + input_set_capability(jog_dev, EV_REL, REL_WHEEL); error = input_register_device(jog_dev); if (error) @@ -473,9 +470,6 @@ err_unregister_keydev: err_free_keydev: input_free_device(key_dev); -err_destroy_wq: - destroy_workqueue(sony_laptop_input.wq); - err_free_kfifo: kfifo_free(&sony_laptop_input.fifo); @@ -486,12 +480,23 @@ err_dec_users: static void sony_laptop_remove_input(void) { - /* cleanup only after the last user has gone */ + struct sony_laptop_keypress kp = { NULL }; + + /* Cleanup only after the last user has gone */ if (!atomic_dec_and_test(&sony_laptop_input.users)) return; - /* flush workqueue first */ - flush_workqueue(sony_laptop_input.wq); + del_timer_sync(&sony_laptop_input.release_key_timer); + + /* + * Generate key-up events for remaining keys. Note that we don't + * need locking since nobody is adding new events to the kfifo. + */ + while (kfifo_out(&sony_laptop_input.fifo, + (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) { + input_report_key(kp.dev, kp.key, 0); + input_sync(kp.dev); + } /* destroy input devs */ input_unregister_device(sony_laptop_input.key_dev); @@ -502,7 +507,6 @@ static void sony_laptop_remove_input(void) sony_laptop_input.jog_dev = NULL; } - destroy_workqueue(sony_laptop_input.wq); kfifo_free(&sony_laptop_input.fifo); } @@ -1288,9 +1292,13 @@ static int sony_nc_add(struct acpi_device *device) "controlled by ACPI video driver\n"); } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) { + struct backlight_properties props; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = SONY_MAX_BRIGHTNESS - 1; sony_backlight_device = backlight_device_register("sony", NULL, NULL, - &sony_backlight_ops); + &sony_backlight_ops, + &props); if (IS_ERR(sony_backlight_device)) { printk(KERN_WARNING DRV_PFX "unable to register backlight device\n"); @@ -1299,8 +1307,6 @@ static int sony_nc_add(struct acpi_device *device) sony_backlight_device->props.brightness = sony_backlight_get_brightness (sony_backlight_device); - sony_backlight_device->props.max_brightness = - SONY_MAX_BRIGHTNESS - 1; } } diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c index dd33b51c348..1fe0f1feff7 100644 --- a/drivers/platform/x86/tc1100-wmi.c +++ b/drivers/platform/x86/tc1100-wmi.c @@ -27,6 +27,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/types.h> #include <acpi/acpi.h> diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index e7b0c3bcef8..63290b33c87 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -58,6 +58,7 @@ #include <linux/kthread.h> #include <linux/freezer.h> #include <linux/delay.h> +#include <linux/slab.h> #include <linux/nvram.h> #include <linux/proc_fs.h> @@ -1668,7 +1669,7 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv) * Table of recommended minimum BIOS versions * * Reasons for listing: - * 1. Stable BIOS, listed because the unknown ammount of + * 1. Stable BIOS, listed because the unknown amount of * bugs and bad ACPI behaviour on older versions * * 2. BIOS or EC fw with known bugs that trigger on Linux @@ -6170,6 +6171,7 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { static int __init brightness_init(struct ibm_init_struct *iibm) { + struct backlight_properties props; int b; unsigned long quirks; @@ -6259,9 +6261,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm) printk(TPACPI_INFO "detected a 16-level brightness capable ThinkPad\n"); - ibm_backlight_device = backlight_device_register( - TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, - &ibm_backlight_data); + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = (tp_features.bright_16levels) ? 15 : 7; + ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME, + NULL, NULL, + &ibm_backlight_data, + &props); if (IS_ERR(ibm_backlight_device)) { int rc = PTR_ERR(ibm_backlight_device); ibm_backlight_device = NULL; @@ -6280,8 +6285,6 @@ static int __init brightness_init(struct ibm_init_struct *iibm) "or not on your ThinkPad\n", TPACPI_MAIL); } - ibm_backlight_device->props.max_brightness = - (tp_features.bright_16levels)? 15 : 7; ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; backlight_update_status(ibm_backlight_device); @@ -7108,7 +7111,7 @@ static struct ibm_struct volume_driver_data = { * * Fan speed changes of any sort (including those caused by the * disengaged mode) are usually done slowly by the firmware as the - * maximum ammount of fan duty cycle change per second seems to be + * maximum amount of fan duty cycle change per second seems to be * limited. * * Reading is not available if GFAN exists. diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index 02f3d4e9e66..ff4b476f195 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -16,6 +16,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/acpi.h> #include <linux/input.h> @@ -46,7 +47,7 @@ static struct tps_key_entry topstar_keymap[] = { { } }; -static struct tps_key_entry *tps_get_key_by_scancode(int code) +static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code) { struct tps_key_entry *key; @@ -57,7 +58,7 @@ static struct tps_key_entry *tps_get_key_by_scancode(int code) return NULL; } -static struct tps_key_entry *tps_get_key_by_keycode(int code) +static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code) { struct tps_key_entry *key; @@ -126,7 +127,8 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) return 0; } -static int topstar_getkeycode(struct input_dev *dev, int scancode, int *keycode) +static int topstar_getkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int *keycode) { struct tps_key_entry *key = tps_get_key_by_scancode(scancode); @@ -137,14 +139,12 @@ static int topstar_getkeycode(struct input_dev *dev, int scancode, int *keycode) return 0; } -static int topstar_setkeycode(struct input_dev *dev, int scancode, int keycode) +static int topstar_setkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int keycode) { struct tps_key_entry *key; int old_keycode; - if (keycode < 0 || keycode > KEY_MAX) - return -EINVAL; - key = tps_get_key_by_scancode(scancode); if (!key) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 77bf5d8f893..37aa1479855 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -46,6 +46,8 @@ #include <linux/backlight.h> #include <linux/platform_device.h> #include <linux/rfkill.h> +#include <linux/input.h> +#include <linux/slab.h> #include <asm/uaccess.h> @@ -62,9 +64,10 @@ MODULE_LICENSE("GPL"); /* Toshiba ACPI method paths */ #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" -#define METHOD_HCI_1 "\\_SB_.VALD.GHCI" -#define METHOD_HCI_2 "\\_SB_.VALZ.GHCI" +#define TOSH_INTERFACE_1 "\\_SB_.VALD" +#define TOSH_INTERFACE_2 "\\_SB_.VALZ" #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" +#define GHCI_METHOD ".GHCI" /* Toshiba HCI interface definitions * @@ -116,6 +119,36 @@ static const struct acpi_device_id toshiba_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); +struct key_entry { + char type; + u16 code; + u16 keycode; +}; + +enum {KE_KEY, KE_END}; + +static struct key_entry toshiba_acpi_keymap[] = { + {KE_KEY, 0x101, KEY_MUTE}, + {KE_KEY, 0x13b, KEY_COFFEE}, + {KE_KEY, 0x13c, KEY_BATTERY}, + {KE_KEY, 0x13d, KEY_SLEEP}, + {KE_KEY, 0x13e, KEY_SUSPEND}, + {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE}, + {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN}, + {KE_KEY, 0x141, KEY_BRIGHTNESSUP}, + {KE_KEY, 0x142, KEY_WLAN}, + {KE_KEY, 0x143, KEY_PROG1}, + {KE_KEY, 0xb05, KEY_PROG2}, + {KE_KEY, 0xb06, KEY_WWW}, + {KE_KEY, 0xb07, KEY_MAIL}, + {KE_KEY, 0xb30, KEY_STOP}, + {KE_KEY, 0xb31, KEY_PREVIOUSSONG}, + {KE_KEY, 0xb32, KEY_NEXTSONG}, + {KE_KEY, 0xb33, KEY_PLAYPAUSE}, + {KE_KEY, 0xb5a, KEY_MEDIA}, + {KE_END, 0, 0}, +}; + /* utility */ @@ -251,6 +284,8 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result) struct toshiba_acpi_dev { struct platform_device *p_dev; struct rfkill *bt_rfk; + struct input_dev *hotkey_dev; + acpi_handle handle; const char *bt_name; @@ -711,8 +746,158 @@ static struct backlight_ops toshiba_backlight_data = { .update_status = set_lcd_status, }; +static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code) +{ + struct key_entry *key; + + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code) +{ + struct key_entry *key; + + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) + if (code == key->keycode && key->type == KE_KEY) + return key; + + return NULL; +} + +static int toshiba_acpi_getkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int *keycode) +{ + struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int toshiba_acpi_setkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int keycode) +{ + struct key_entry *key; + unsigned int old_keycode; + + key = toshiba_acpi_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!toshiba_acpi_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + + return -EINVAL; +} + +static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) +{ + u32 hci_result, value; + struct key_entry *key; + + if (event != 0x80) + return; + do { + hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); + if (hci_result == HCI_SUCCESS) { + if (value == 0x100) + continue; + /* act on key press; ignore key release */ + if (value & 0x80) + continue; + + key = toshiba_acpi_get_entry_by_scancode + (value); + if (!key) { + printk(MY_INFO "Unknown key %x\n", + value); + continue; + } + input_report_key(toshiba_acpi.hotkey_dev, + key->keycode, 1); + input_sync(toshiba_acpi.hotkey_dev); + input_report_key(toshiba_acpi.hotkey_dev, + key->keycode, 0); + input_sync(toshiba_acpi.hotkey_dev); + } else if (hci_result == HCI_NOT_SUPPORTED) { + /* This is a workaround for an unresolved issue on + * some machines where system events sporadically + * become disabled. */ + hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); + printk(MY_NOTICE "Re-enabled hotkeys\n"); + } + } while (hci_result != HCI_EMPTY); +} + +static int toshiba_acpi_setup_keyboard(char *device) +{ + acpi_status status; + acpi_handle handle; + int result; + const struct key_entry *key; + + status = acpi_get_handle(NULL, device, &handle); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to get notification device\n"); + return -ENODEV; + } + + toshiba_acpi.handle = handle; + + status = acpi_evaluate_object(handle, "ENAB", NULL, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to enable hotkeys\n"); + return -ENODEV; + } + + status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to install hotkey notification\n"); + return -ENODEV; + } + + toshiba_acpi.hotkey_dev = input_allocate_device(); + if (!toshiba_acpi.hotkey_dev) { + printk(MY_INFO "Unable to register input device\n"); + return -ENOMEM; + } + + toshiba_acpi.hotkey_dev->name = "Toshiba input device"; + toshiba_acpi.hotkey_dev->phys = device; + toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; + toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode; + toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode; + + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) { + set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit); + set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit); + } + + result = input_register_device(toshiba_acpi.hotkey_dev); + if (result) { + printk(MY_INFO "Unable to register input device\n"); + return result; + } + + return 0; +} + static void toshiba_acpi_exit(void) { + if (toshiba_acpi.hotkey_dev) + input_unregister_device(toshiba_acpi.hotkey_dev); + if (toshiba_acpi.bt_rfk) { rfkill_unregister(toshiba_acpi.bt_rfk); rfkill_destroy(toshiba_acpi.bt_rfk); @@ -726,6 +911,9 @@ static void toshiba_acpi_exit(void) if (toshiba_proc_dir) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); + acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify); + platform_device_unregister(toshiba_acpi.p_dev); return; @@ -737,16 +925,21 @@ static int __init toshiba_acpi_init(void) u32 hci_result; bool bt_present; int ret = 0; + struct backlight_properties props; if (acpi_disabled) return -ENODEV; /* simple device detection: look for HCI method */ - if (is_valid_acpi_path(METHOD_HCI_1)) - method_hci = METHOD_HCI_1; - else if (is_valid_acpi_path(METHOD_HCI_2)) - method_hci = METHOD_HCI_2; - else + if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { + method_hci = TOSH_INTERFACE_1 GHCI_METHOD; + if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1)) + printk(MY_INFO "Unable to activate hotkeys\n"); + } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { + method_hci = TOSH_INTERFACE_2 GHCI_METHOD; + if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2)) + printk(MY_INFO "Unable to activate hotkeys\n"); + } else return -ENODEV; printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n", @@ -783,10 +976,12 @@ static int __init toshiba_acpi_init(void) } } + props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; toshiba_backlight_device = backlight_device_register("toshiba", - &toshiba_acpi.p_dev->dev, - NULL, - &toshiba_backlight_data); + &toshiba_acpi.p_dev->dev, + NULL, + &toshiba_backlight_data, + &props); if (IS_ERR(toshiba_backlight_device)) { ret = PTR_ERR(toshiba_backlight_device); @@ -795,7 +990,6 @@ static int __init toshiba_acpi_init(void) toshiba_acpi_exit(); return ret; } - toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; /* Register rfkill switch for Bluetooth */ if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index a350418e87e..94406861191 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -57,7 +57,7 @@ static struct acpi_driver toshiba_bt_rfkill_driver = { static int toshiba_bluetooth_enable(acpi_handle handle) { acpi_status res1, res2; - acpi_integer result; + u64 result; /* * Query ACPI to verify RFKill switch is set to 'on'. @@ -95,7 +95,7 @@ static int toshiba_bt_resume(struct acpi_device *device) static int toshiba_bt_rfkill_add(struct acpi_device *device) { acpi_status status; - acpi_integer bt_present; + u64 bt_present; int result = -ENODEV; /* diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index b104302fea0..39ec5b6c2e3 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -33,6 +33,7 @@ #include <linux/device.h> #include <linux/list.h> #include <linux/acpi.h> +#include <linux/slab.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -796,7 +797,7 @@ static __init acpi_status parse_wdg(acpi_handle handle) */ static acpi_status acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, - u32 bits, acpi_integer * value, + u32 bits, u64 *value, void *handler_context, void *region_context) { int result = 0, i = 0; @@ -813,7 +814,7 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, if (function == ACPI_READ) { result = ec_read(address, &temp); - (*value) |= ((acpi_integer)temp) << i; + (*value) |= ((u64)temp) << i; } else { temp = 0xff & ((*value) >> i); result = ec_write(address, temp); |