diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-10 18:39:48 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-10 18:39:48 -0500 |
| commit | 443e6221e465efa8efb752a8405a759ef1161af9 (patch) | |
| tree | 8732213fc309a14f8613849b1ff2a2951757aff1 | |
| parent | 0caca697a2d173c6beff3c24e7d9481b732bd131 (diff) | |
| parent | 1a7d946993aaf2a79e9c65abbe169a108e351bcb (diff) | |
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: (36 commits)
sony-laptop: support new hotkeys on the P, Z and EC series
platform/x86: Consistently select LEDS Kconfig options
sony-laptop: fix sparse non-ANSI function warning
intel_ips: fix sparse non-ANSI function warning
Support KHLB2 in the compal laptop driver
acer-wmi: Enabled Acer Launch Manager mode
[PATCH] intel_pmic_gpio: modify EOI handling following change of kernel irq subsystem
ACPI Thinkpad: We must always call va_end() after va_start() but do not do so in thinkpad_acpi.c::acpi_evalf()
acer-wmi: Initialize wlan/bluetooth/wwan rfkill software block state
acer-wmi: Detect the WiFi/Bluetooth/3G devices available
acer-wmi: Add 3G rfkill sysfs file
acer-wmi: Add acer wmi hotkey events support
platform/x86: Kconfig: Replace select by depends on ACPI_WMI
ideapad: pass ideapad_priv as argument (part 2)
ideapad: pass ideapad_priv as argument (part 1)
ideapad: add markups, unify comments and return result when init
ideapad: add hotkey support
ideapad: let camera power control entry under platform driver
ideapad: add platform driver for ideapad
fujitsu-laptop: fix compiler warning on pnp_ids
...
| -rw-r--r-- | Documentation/ABI/testing/sysfs-platform-ideapad-laptop | 6 | ||||
| -rw-r--r-- | MAINTAINERS | 8 | ||||
| -rw-r--r-- | drivers/platform/x86/Kconfig | 27 | ||||
| -rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
| -rw-r--r-- | drivers/platform/x86/acer-wmi.c | 429 | ||||
| -rw-r--r-- | drivers/platform/x86/classmate-laptop.c | 19 | ||||
| -rw-r--r-- | drivers/platform/x86/compal-laptop.c | 8 | ||||
| -rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 11 | ||||
| -rw-r--r-- | drivers/platform/x86/eeepc-wmi.c | 609 | ||||
| -rw-r--r-- | drivers/platform/x86/fujitsu-laptop.c | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 259 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_ips.c | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_pmic_gpio.c | 6 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_scu_ipc.c | 4 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_scu_ipcutil.c | 133 | ||||
| -rw-r--r-- | drivers/platform/x86/sony-laptop.c | 12 | ||||
| -rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 3 | ||||
| -rw-r--r-- | drivers/platform/x86/wmi.c | 133 | ||||
| -rw-r--r-- | include/linux/sonypi.h | 1 |
19 files changed, 1431 insertions, 242 deletions
diff --git a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop new file mode 100644 index 000000000000..807fca2ae2a4 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | What: /sys/devices/platform/ideapad/camera_power | ||
| 2 | Date: Dec 2010 | ||
| 3 | KernelVersion: 2.6.37 | ||
| 4 | Contact: "Ike Panhc <ike.pan@canonical.com>" | ||
| 5 | Description: | ||
| 6 | Control the power of camera module. 1 means on, 0 means off. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 9e4d4ca690e2..5181828012ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -2271,6 +2271,14 @@ W: http://acpi4asus.sf.net | |||
| 2271 | S: Maintained | 2271 | S: Maintained |
| 2272 | F: drivers/platform/x86/eeepc-laptop.c | 2272 | F: drivers/platform/x86/eeepc-laptop.c |
| 2273 | 2273 | ||
| 2274 | EEEPC WMI EXTRAS DRIVER | ||
| 2275 | M: Corentin Chary <corentincj@iksaif.net> | ||
| 2276 | L: acpi4asus-user@lists.sourceforge.net | ||
| 2277 | L: platform-driver-x86@vger.kernel.org | ||
| 2278 | W: http://acpi4asus.sf.net | ||
| 2279 | S: Maintained | ||
| 2280 | F: drivers/platform/x86/eeepc-wmi.c | ||
| 2281 | |||
| 2274 | EFIFB FRAMEBUFFER DRIVER | 2282 | EFIFB FRAMEBUFFER DRIVER |
| 2275 | L: linux-fbdev@vger.kernel.org | 2283 | L: linux-fbdev@vger.kernel.org |
| 2276 | M: Peter Jones <pjones@redhat.com> | 2284 | M: Peter Jones <pjones@redhat.com> |
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index faec777b1ed4..d163bc2e2b9e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
| @@ -18,12 +18,14 @@ if X86_PLATFORM_DEVICES | |||
| 18 | config ACER_WMI | 18 | config ACER_WMI |
| 19 | tristate "Acer WMI Laptop Extras" | 19 | tristate "Acer WMI Laptop Extras" |
| 20 | depends on ACPI | 20 | depends on ACPI |
| 21 | depends on LEDS_CLASS | 21 | select LEDS_CLASS |
| 22 | depends on NEW_LEDS | 22 | select NEW_LEDS |
| 23 | depends on BACKLIGHT_CLASS_DEVICE | 23 | depends on BACKLIGHT_CLASS_DEVICE |
| 24 | depends on SERIO_I8042 | 24 | depends on SERIO_I8042 |
| 25 | depends on INPUT | ||
| 25 | depends on RFKILL || RFKILL = n | 26 | depends on RFKILL || RFKILL = n |
| 26 | select ACPI_WMI | 27 | depends on ACPI_WMI |
| 28 | select INPUT_SPARSEKMAP | ||
| 27 | ---help--- | 29 | ---help--- |
| 28 | This is a driver for newer Acer (and Wistron) laptops. It adds | 30 | This is a driver for newer Acer (and Wistron) laptops. It adds |
| 29 | wireless radio and bluetooth control, and on some laptops, | 31 | wireless radio and bluetooth control, and on some laptops, |
| @@ -131,7 +133,7 @@ config TC1100_WMI | |||
| 131 | depends on !X86_64 | 133 | depends on !X86_64 |
| 132 | depends on EXPERIMENTAL | 134 | depends on EXPERIMENTAL |
| 133 | depends on ACPI | 135 | depends on ACPI |
| 134 | select ACPI_WMI | 136 | depends on ACPI_WMI |
| 135 | ---help--- | 137 | ---help--- |
| 136 | This is a driver for the WMI extensions (wireless and bluetooth power | 138 | This is a driver for the WMI extensions (wireless and bluetooth power |
| 137 | control) of the HP Compaq TC1100 tablet. | 139 | control) of the HP Compaq TC1100 tablet. |
| @@ -226,6 +228,7 @@ config IDEAPAD_LAPTOP | |||
| 226 | tristate "Lenovo IdeaPad Laptop Extras" | 228 | tristate "Lenovo IdeaPad Laptop Extras" |
| 227 | depends on ACPI | 229 | depends on ACPI |
| 228 | depends on RFKILL | 230 | depends on RFKILL |
| 231 | select INPUT_SPARSEKMAP | ||
| 229 | help | 232 | help |
| 230 | This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. | 233 | This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. |
| 231 | 234 | ||
| @@ -425,7 +428,10 @@ config EEEPC_WMI | |||
| 425 | depends on INPUT | 428 | depends on INPUT |
| 426 | depends on EXPERIMENTAL | 429 | depends on EXPERIMENTAL |
| 427 | depends on BACKLIGHT_CLASS_DEVICE | 430 | depends on BACKLIGHT_CLASS_DEVICE |
| 431 | depends on RFKILL || RFKILL = n | ||
| 428 | select INPUT_SPARSEKMAP | 432 | select INPUT_SPARSEKMAP |
| 433 | select LEDS_CLASS | ||
| 434 | select NEW_LEDS | ||
| 429 | ---help--- | 435 | ---help--- |
| 430 | Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. | 436 | Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. |
| 431 | 437 | ||
| @@ -510,8 +516,8 @@ config TOPSTAR_LAPTOP | |||
| 510 | config ACPI_TOSHIBA | 516 | config ACPI_TOSHIBA |
| 511 | tristate "Toshiba Laptop Extras" | 517 | tristate "Toshiba Laptop Extras" |
| 512 | depends on ACPI | 518 | depends on ACPI |
| 513 | depends on LEDS_CLASS | 519 | select LEDS_CLASS |
| 514 | depends on NEW_LEDS | 520 | select NEW_LEDS |
| 515 | depends on BACKLIGHT_CLASS_DEVICE | 521 | depends on BACKLIGHT_CLASS_DEVICE |
| 516 | depends on INPUT | 522 | depends on INPUT |
| 517 | depends on RFKILL || RFKILL = n | 523 | depends on RFKILL || RFKILL = n |
| @@ -576,6 +582,15 @@ config INTEL_SCU_IPC | |||
| 576 | some embedded Intel x86 platforms. This is not needed for PC-type | 582 | some embedded Intel x86 platforms. This is not needed for PC-type |
| 577 | machines. | 583 | machines. |
| 578 | 584 | ||
| 585 | config INTEL_SCU_IPC_UTIL | ||
| 586 | tristate "Intel SCU IPC utility driver" | ||
| 587 | depends on INTEL_SCU_IPC | ||
| 588 | default y | ||
| 589 | ---help--- | ||
| 590 | The IPC Util driver provides an interface with the SCU enabling | ||
| 591 | low level access for debug work and updating the firmware. Say | ||
| 592 | N unless you will be doing this on an Intel MID platform. | ||
| 593 | |||
| 579 | config GPIO_INTEL_PMIC | 594 | config GPIO_INTEL_PMIC |
| 580 | bool "Intel PMIC GPIO support" | 595 | bool "Intel PMIC GPIO support" |
| 581 | depends on INTEL_SCU_IPC && GPIOLIB | 596 | depends on INTEL_SCU_IPC && GPIOLIB |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 9950ccc940b5..4ec4ff8f9182 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
| @@ -28,6 +28,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o | |||
| 28 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o | 28 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o |
| 29 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o | 29 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o |
| 30 | obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o | 30 | obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o |
| 31 | obj-$(CONFIG_INTEL_SCU_IPC_UTIL)+= intel_scu_ipcutil.o | ||
| 31 | obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o | 32 | obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o |
| 32 | obj-$(CONFIG_INTEL_IPS) += intel_ips.o | 33 | obj-$(CONFIG_INTEL_IPS) += intel_ips.o |
| 33 | obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o | 34 | obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o |
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c8c65375bfe2..ee40d681edd0 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c | |||
| @@ -37,6 +37,9 @@ | |||
| 37 | #include <linux/workqueue.h> | 37 | #include <linux/workqueue.h> |
| 38 | #include <linux/debugfs.h> | 38 | #include <linux/debugfs.h> |
| 39 | #include <linux/slab.h> | 39 | #include <linux/slab.h> |
| 40 | #include <linux/input.h> | ||
| 41 | #include <linux/input/sparse-keymap.h> | ||
| 42 | #include <linux/dmi.h> | ||
| 40 | 43 | ||
| 41 | #include <acpi/acpi_drivers.h> | 44 | #include <acpi/acpi_drivers.h> |
| 42 | 45 | ||
| @@ -48,6 +51,7 @@ MODULE_LICENSE("GPL"); | |||
| 48 | #define ACER_ERR KERN_ERR ACER_LOGPREFIX | 51 | #define ACER_ERR KERN_ERR ACER_LOGPREFIX |
| 49 | #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX | 52 | #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX |
| 50 | #define ACER_INFO KERN_INFO ACER_LOGPREFIX | 53 | #define ACER_INFO KERN_INFO ACER_LOGPREFIX |
| 54 | #define ACER_WARNING KERN_WARNING ACER_LOGPREFIX | ||
| 51 | 55 | ||
| 52 | /* | 56 | /* |
| 53 | * Magic Number | 57 | * Magic Number |
| @@ -82,9 +86,82 @@ MODULE_LICENSE("GPL"); | |||
| 82 | #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" | 86 | #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" |
| 83 | #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" | 87 | #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" |
| 84 | #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" | 88 | #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" |
| 89 | #define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531" | ||
| 90 | |||
| 91 | /* | ||
| 92 | * Acer ACPI event GUIDs | ||
| 93 | */ | ||
| 94 | #define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026" | ||
| 85 | 95 | ||
| 86 | MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); | 96 | MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); |
| 87 | MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); | 97 | MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); |
| 98 | MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026"); | ||
| 99 | |||
| 100 | enum acer_wmi_event_ids { | ||
| 101 | WMID_HOTKEY_EVENT = 0x1, | ||
| 102 | }; | ||
| 103 | |||
| 104 | static const struct key_entry acer_wmi_keymap[] = { | ||
| 105 | {KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */ | ||
| 106 | {KE_KEY, 0x12, {KEY_BLUETOOTH} }, /* BT */ | ||
| 107 | {KE_KEY, 0x21, {KEY_PROG1} }, /* Backup */ | ||
| 108 | {KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */ | ||
| 109 | {KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */ | ||
| 110 | {KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */ | ||
| 111 | {KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */ | ||
| 112 | {KE_KEY, 0x82, {KEY_F22} }, /* Touch Pad On/Off */ | ||
| 113 | {KE_END, 0} | ||
| 114 | }; | ||
| 115 | |||
| 116 | static struct input_dev *acer_wmi_input_dev; | ||
| 117 | |||
| 118 | struct event_return_value { | ||
| 119 | u8 function; | ||
| 120 | u8 key_num; | ||
| 121 | u16 device_state; | ||
| 122 | u32 reserved; | ||
| 123 | } __attribute__((packed)); | ||
| 124 | |||
| 125 | /* | ||
| 126 | * GUID3 Get Device Status device flags | ||
| 127 | */ | ||
| 128 | #define ACER_WMID3_GDS_WIRELESS (1<<0) /* WiFi */ | ||
| 129 | #define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */ | ||
| 130 | #define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ | ||
| 131 | |||
| 132 | struct lm_input_params { | ||
| 133 | u8 function_num; /* Function Number */ | ||
| 134 | u16 commun_devices; /* Communication type devices default status */ | ||
| 135 | u16 devices; /* Other type devices default status */ | ||
| 136 | u8 lm_status; /* Launch Manager Status */ | ||
| 137 | u16 reserved; | ||
| 138 | } __attribute__((packed)); | ||
| 139 | |||
| 140 | struct lm_return_value { | ||
| 141 | u8 error_code; /* Error Code */ | ||
| 142 | u8 ec_return_value; /* EC Return Value */ | ||
| 143 | u16 reserved; | ||
| 144 | } __attribute__((packed)); | ||
| 145 | |||
| 146 | struct wmid3_gds_input_param { /* Get Device Status input parameter */ | ||
| 147 | u8 function_num; /* Function Number */ | ||
| 148 | u8 hotkey_number; /* Hotkey Number */ | ||
| 149 | u16 devices; /* Get Device */ | ||
| 150 | } __attribute__((packed)); | ||
| 151 | |||
| 152 | struct wmid3_gds_return_value { /* Get Device Status return value*/ | ||
| 153 | u8 error_code; /* Error Code */ | ||
| 154 | u8 ec_return_value; /* EC Return Value */ | ||
| 155 | u16 devices; /* Current Device Status */ | ||
| 156 | u32 reserved; | ||
| 157 | } __attribute__((packed)); | ||
| 158 | |||
| 159 | struct hotkey_function_type_aa { | ||
| 160 | u8 type; | ||
| 161 | u8 length; | ||
| 162 | u16 handle; | ||
| 163 | u16 commun_func_bitmap; | ||
| 164 | } __attribute__((packed)); | ||
| 88 | 165 | ||
| 89 | /* | 166 | /* |
| 90 | * Interface capability flags | 167 | * Interface capability flags |
| @@ -116,15 +193,19 @@ static int mailled = -1; | |||
| 116 | static int brightness = -1; | 193 | static int brightness = -1; |
| 117 | static int threeg = -1; | 194 | static int threeg = -1; |
| 118 | static int force_series; | 195 | static int force_series; |
| 196 | static bool ec_raw_mode; | ||
| 197 | static bool has_type_aa; | ||
| 119 | 198 | ||
| 120 | module_param(mailled, int, 0444); | 199 | module_param(mailled, int, 0444); |
| 121 | module_param(brightness, int, 0444); | 200 | module_param(brightness, int, 0444); |
| 122 | module_param(threeg, int, 0444); | 201 | module_param(threeg, int, 0444); |
| 123 | module_param(force_series, int, 0444); | 202 | module_param(force_series, int, 0444); |
| 203 | module_param(ec_raw_mode, bool, 0444); | ||
| 124 | MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); | 204 | MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); |
| 125 | MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); | 205 | MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); |
| 126 | MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); | 206 | MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); |
| 127 | MODULE_PARM_DESC(force_series, "Force a different laptop series"); | 207 | MODULE_PARM_DESC(force_series, "Force a different laptop series"); |
| 208 | MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode"); | ||
| 128 | 209 | ||
| 129 | struct acer_data { | 210 | struct acer_data { |
| 130 | int mailled; | 211 | int mailled; |
| @@ -140,6 +221,7 @@ struct acer_debug { | |||
| 140 | 221 | ||
| 141 | static struct rfkill *wireless_rfkill; | 222 | static struct rfkill *wireless_rfkill; |
| 142 | static struct rfkill *bluetooth_rfkill; | 223 | static struct rfkill *bluetooth_rfkill; |
| 224 | static struct rfkill *threeg_rfkill; | ||
| 143 | 225 | ||
| 144 | /* Each low-level interface must define at least some of the following */ | 226 | /* Each low-level interface must define at least some of the following */ |
| 145 | struct wmi_interface { | 227 | struct wmi_interface { |
| @@ -753,6 +835,28 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) | |||
| 753 | return WMI_execute_u32(method_id, (u32)value, NULL); | 835 | return WMI_execute_u32(method_id, (u32)value, NULL); |
| 754 | } | 836 | } |
| 755 | 837 | ||
| 838 | static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) | ||
| 839 | { | ||
| 840 | struct hotkey_function_type_aa *type_aa; | ||
| 841 | |||
| 842 | /* We are looking for OEM-specific Type AAh */ | ||
| 843 | if (header->type != 0xAA) | ||
| 844 | return; | ||
| 845 | |||
| 846 | has_type_aa = true; | ||
| 847 | type_aa = (struct hotkey_function_type_aa *) header; | ||
| 848 | |||
| 849 | printk(ACER_INFO "Function bitmap for Communication Button: 0x%x\n", | ||
| 850 | type_aa->commun_func_bitmap); | ||
| 851 | |||
| 852 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS) | ||
| 853 | interface->capability |= ACER_CAP_WIRELESS; | ||
| 854 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_THREEG) | ||
| 855 | interface->capability |= ACER_CAP_THREEG; | ||
| 856 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH) | ||
| 857 | interface->capability |= ACER_CAP_BLUETOOTH; | ||
| 858 | } | ||
| 859 | |||
| 756 | static acpi_status WMID_set_capabilities(void) | 860 | static acpi_status WMID_set_capabilities(void) |
| 757 | { | 861 | { |
| 758 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; | 862 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; |
| @@ -773,16 +877,17 @@ static acpi_status WMID_set_capabilities(void) | |||
| 773 | return AE_ERROR; | 877 | return AE_ERROR; |
| 774 | } | 878 | } |
| 775 | 879 | ||
| 776 | /* Not sure on the meaning of the relevant bits yet to detect these */ | 880 | dmi_walk(type_aa_dmi_decode, NULL); |
| 777 | interface->capability |= ACER_CAP_WIRELESS; | 881 | if (!has_type_aa) { |
| 778 | interface->capability |= ACER_CAP_THREEG; | 882 | interface->capability |= ACER_CAP_WIRELESS; |
| 883 | interface->capability |= ACER_CAP_THREEG; | ||
| 884 | if (devices & 0x10) | ||
| 885 | interface->capability |= ACER_CAP_BLUETOOTH; | ||
| 886 | } | ||
| 779 | 887 | ||
| 780 | /* WMID always provides brightness methods */ | 888 | /* WMID always provides brightness methods */ |
| 781 | interface->capability |= ACER_CAP_BRIGHTNESS; | 889 | interface->capability |= ACER_CAP_BRIGHTNESS; |
| 782 | 890 | ||
| 783 | if (devices & 0x10) | ||
| 784 | interface->capability |= ACER_CAP_BLUETOOTH; | ||
| 785 | |||
| 786 | if (!(devices & 0x20)) | 891 | if (!(devices & 0x20)) |
| 787 | max_brightness = 0x9; | 892 | max_brightness = 0x9; |
| 788 | 893 | ||
| @@ -861,7 +966,8 @@ static void __init acer_commandline_init(void) | |||
| 861 | * capability isn't available on the given interface | 966 | * capability isn't available on the given interface |
| 862 | */ | 967 | */ |
| 863 | set_u32(mailled, ACER_CAP_MAILLED); | 968 | set_u32(mailled, ACER_CAP_MAILLED); |
| 864 | set_u32(threeg, ACER_CAP_THREEG); | 969 | if (!has_type_aa) |
| 970 | set_u32(threeg, ACER_CAP_THREEG); | ||
| 865 | set_u32(brightness, ACER_CAP_BRIGHTNESS); | 971 | set_u32(brightness, ACER_CAP_BRIGHTNESS); |
| 866 | } | 972 | } |
| 867 | 973 | ||
| @@ -948,6 +1054,79 @@ static void acer_backlight_exit(void) | |||
| 948 | backlight_device_unregister(acer_backlight_device); | 1054 | backlight_device_unregister(acer_backlight_device); |
| 949 | } | 1055 | } |
| 950 | 1056 | ||
| 1057 | static acpi_status wmid3_get_device_status(u32 *value, u16 device) | ||
| 1058 | { | ||
| 1059 | struct wmid3_gds_return_value return_value; | ||
| 1060 | acpi_status status; | ||
| 1061 | union acpi_object *obj; | ||
| 1062 | struct wmid3_gds_input_param params = { | ||
| 1063 | .function_num = 0x1, | ||
| 1064 | .hotkey_number = 0x01, | ||
| 1065 | .devices = device, | ||
| 1066 | }; | ||
| 1067 | struct acpi_buffer input = { | ||
| 1068 | sizeof(struct wmid3_gds_input_param), | ||
| 1069 | ¶ms | ||
| 1070 | }; | ||
| 1071 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 1072 | |||
| 1073 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); | ||
| 1074 | if (ACPI_FAILURE(status)) | ||
| 1075 | return status; | ||
| 1076 | |||
| 1077 | obj = output.pointer; | ||
| 1078 | |||
| 1079 | if (!obj) | ||
| 1080 | return AE_ERROR; | ||
| 1081 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
| 1082 | kfree(obj); | ||
| 1083 | return AE_ERROR; | ||
| 1084 | } | ||
| 1085 | if (obj->buffer.length != 8) { | ||
| 1086 | printk(ACER_WARNING "Unknown buffer length %d\n", | ||
| 1087 | obj->buffer.length); | ||
| 1088 | kfree(obj); | ||
| 1089 | return AE_ERROR; | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); | ||
| 1093 | kfree(obj); | ||
| 1094 | |||
| 1095 | if (return_value.error_code || return_value.ec_return_value) | ||
| 1096 | printk(ACER_WARNING "Get Device Status failed: " | ||
| 1097 | "0x%x - 0x%x\n", return_value.error_code, | ||
| 1098 | return_value.ec_return_value); | ||
| 1099 | else | ||
| 1100 | *value = !!(return_value.devices & device); | ||
| 1101 | |||
| 1102 | return status; | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | static acpi_status get_device_status(u32 *value, u32 cap) | ||
| 1106 | { | ||
| 1107 | if (wmi_has_guid(WMID_GUID3)) { | ||
| 1108 | u16 device; | ||
| 1109 | |||
| 1110 | switch (cap) { | ||
| 1111 | case ACER_CAP_WIRELESS: | ||
| 1112 | device = ACER_WMID3_GDS_WIRELESS; | ||
| 1113 | break; | ||
| 1114 | case ACER_CAP_BLUETOOTH: | ||
| 1115 | device = ACER_WMID3_GDS_BLUETOOTH; | ||
| 1116 | break; | ||
| 1117 | case ACER_CAP_THREEG: | ||
| 1118 | device = ACER_WMID3_GDS_THREEG; | ||
| 1119 | break; | ||
| 1120 | default: | ||
| 1121 | return AE_ERROR; | ||
| 1122 | } | ||
| 1123 | return wmid3_get_device_status(value, device); | ||
| 1124 | |||
| 1125 | } else { | ||
| 1126 | return get_u32(value, cap); | ||
| 1127 | } | ||
| 1128 | } | ||
| 1129 | |||
| 951 | /* | 1130 | /* |
| 952 | * Rfkill devices | 1131 | * Rfkill devices |
| 953 | */ | 1132 | */ |
| @@ -968,6 +1147,13 @@ static void acer_rfkill_update(struct work_struct *ignored) | |||
| 968 | rfkill_set_sw_state(bluetooth_rfkill, !state); | 1147 | rfkill_set_sw_state(bluetooth_rfkill, !state); |
| 969 | } | 1148 | } |
| 970 | 1149 | ||
| 1150 | if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) { | ||
| 1151 | status = wmid3_get_device_status(&state, | ||
| 1152 | ACER_WMID3_GDS_THREEG); | ||
| 1153 | if (ACPI_SUCCESS(status)) | ||
| 1154 | rfkill_set_sw_state(threeg_rfkill, !state); | ||
| 1155 | } | ||
| 1156 | |||
| 971 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); | 1157 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); |
| 972 | } | 1158 | } |
| 973 | 1159 | ||
| @@ -991,6 +1177,8 @@ static struct rfkill *acer_rfkill_register(struct device *dev, | |||
| 991 | { | 1177 | { |
| 992 | int err; | 1178 | int err; |
| 993 | struct rfkill *rfkill_dev; | 1179 | struct rfkill *rfkill_dev; |
| 1180 | u32 state; | ||
| 1181 | acpi_status status; | ||
| 994 | 1182 | ||
| 995 | rfkill_dev = rfkill_alloc(name, dev, type, | 1183 | rfkill_dev = rfkill_alloc(name, dev, type, |
| 996 | &acer_rfkill_ops, | 1184 | &acer_rfkill_ops, |
| @@ -998,6 +1186,10 @@ static struct rfkill *acer_rfkill_register(struct device *dev, | |||
| 998 | if (!rfkill_dev) | 1186 | if (!rfkill_dev) |
| 999 | return ERR_PTR(-ENOMEM); | 1187 | return ERR_PTR(-ENOMEM); |
| 1000 | 1188 | ||
| 1189 | status = get_device_status(&state, cap); | ||
| 1190 | if (ACPI_SUCCESS(status)) | ||
| 1191 | rfkill_init_sw_state(rfkill_dev, !state); | ||
| 1192 | |||
| 1001 | err = rfkill_register(rfkill_dev); | 1193 | err = rfkill_register(rfkill_dev); |
| 1002 | if (err) { | 1194 | if (err) { |
| 1003 | rfkill_destroy(rfkill_dev); | 1195 | rfkill_destroy(rfkill_dev); |
| @@ -1024,6 +1216,19 @@ static int acer_rfkill_init(struct device *dev) | |||
| 1024 | } | 1216 | } |
| 1025 | } | 1217 | } |
| 1026 | 1218 | ||
| 1219 | if (has_cap(ACER_CAP_THREEG)) { | ||
| 1220 | threeg_rfkill = acer_rfkill_register(dev, | ||
| 1221 | RFKILL_TYPE_WWAN, "acer-threeg", | ||
| 1222 | ACER_CAP_THREEG); | ||
| 1223 | if (IS_ERR(threeg_rfkill)) { | ||
| 1224 | rfkill_unregister(wireless_rfkill); | ||
| 1225 | rfkill_destroy(wireless_rfkill); | ||
| 1226 | rfkill_unregister(bluetooth_rfkill); | ||
| 1227 | rfkill_destroy(bluetooth_rfkill); | ||
| 1228 | return PTR_ERR(threeg_rfkill); | ||
| 1229 | } | ||
| 1230 | } | ||
| 1231 | |||
| 1027 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); | 1232 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); |
| 1028 | 1233 | ||
| 1029 | return 0; | 1234 | return 0; |
| @@ -1040,6 +1245,11 @@ static void acer_rfkill_exit(void) | |||
| 1040 | rfkill_unregister(bluetooth_rfkill); | 1245 | rfkill_unregister(bluetooth_rfkill); |
| 1041 | rfkill_destroy(bluetooth_rfkill); | 1246 | rfkill_destroy(bluetooth_rfkill); |
| 1042 | } | 1247 | } |
| 1248 | |||
| 1249 | if (has_cap(ACER_CAP_THREEG)) { | ||
| 1250 | rfkill_unregister(threeg_rfkill); | ||
| 1251 | rfkill_destroy(threeg_rfkill); | ||
| 1252 | } | ||
| 1043 | return; | 1253 | return; |
| 1044 | } | 1254 | } |
| 1045 | 1255 | ||
| @@ -1050,7 +1260,12 @@ static ssize_t show_bool_threeg(struct device *dev, | |||
| 1050 | struct device_attribute *attr, char *buf) | 1260 | struct device_attribute *attr, char *buf) |
| 1051 | { | 1261 | { |
| 1052 | u32 result; \ | 1262 | u32 result; \ |
| 1053 | acpi_status status = get_u32(&result, ACER_CAP_THREEG); | 1263 | acpi_status status; |
| 1264 | if (wmi_has_guid(WMID_GUID3)) | ||
| 1265 | status = wmid3_get_device_status(&result, | ||
| 1266 | ACER_WMID3_GDS_THREEG); | ||
| 1267 | else | ||
| 1268 | status = get_u32(&result, ACER_CAP_THREEG); | ||
| 1054 | if (ACPI_SUCCESS(status)) | 1269 | if (ACPI_SUCCESS(status)) |
| 1055 | return sprintf(buf, "%u\n", result); | 1270 | return sprintf(buf, "%u\n", result); |
| 1056 | return sprintf(buf, "Read error\n"); | 1271 | return sprintf(buf, "Read error\n"); |
| @@ -1085,6 +1300,178 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, | |||
| 1085 | 1300 | ||
| 1086 | static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); | 1301 | static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); |
| 1087 | 1302 | ||
| 1303 | static void acer_wmi_notify(u32 value, void *context) | ||
| 1304 | { | ||
| 1305 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 1306 | union acpi_object *obj; | ||
| 1307 | struct event_return_value return_value; | ||
| 1308 | acpi_status status; | ||
| 1309 | |||
| 1310 | status = wmi_get_event_data(value, &response); | ||
| 1311 | if (status != AE_OK) { | ||
| 1312 | printk(ACER_WARNING "bad event status 0x%x\n", status); | ||
| 1313 | return; | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | obj = (union acpi_object *)response.pointer; | ||
| 1317 | |||
| 1318 | if (!obj) | ||
| 1319 | return; | ||
| 1320 | if (obj->type != ACPI_TYPE_BUFFER) { | ||
| 1321 | printk(ACER_WARNING "Unknown response received %d\n", | ||
| 1322 | obj->type); | ||
| 1323 | kfree(obj); | ||
| 1324 | return; | ||
| 1325 | } | ||
| 1326 | if (obj->buffer.length != 8) { | ||
| 1327 | printk(ACER_WARNING "Unknown buffer length %d\n", | ||
| 1328 | obj->buffer.length); | ||
| 1329 | kfree(obj); | ||
| 1330 | return; | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | return_value = *((struct event_return_value *)obj->buffer.pointer); | ||
| 1334 | kfree(obj); | ||
| 1335 | |||
| 1336 | switch (return_value.function) { | ||
| 1337 | case WMID_HOTKEY_EVENT: | ||
| 1338 | if (!sparse_keymap_report_event(acer_wmi_input_dev, | ||
| 1339 | return_value.key_num, 1, true)) | ||
| 1340 | printk(ACER_WARNING "Unknown key number - 0x%x\n", | ||
| 1341 | return_value.key_num); | ||
| 1342 | break; | ||
| 1343 | default: | ||
| 1344 | printk(ACER_WARNING "Unknown function number - %d - %d\n", | ||
| 1345 | return_value.function, return_value.key_num); | ||
| 1346 | break; | ||
| 1347 | } | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | static acpi_status | ||
| 1351 | wmid3_set_lm_mode(struct lm_input_params *params, | ||
| 1352 | struct lm_return_value *return_value) | ||
| 1353 | { | ||
| 1354 | acpi_status status; | ||
| 1355 | union acpi_object *obj; | ||
| 1356 | |||
| 1357 | struct acpi_buffer input = { sizeof(struct lm_input_params), params }; | ||
| 1358 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 1359 | |||
| 1360 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output); | ||
| 1361 | if (ACPI_FAILURE(status)) | ||
| 1362 | return status; | ||
| 1363 | |||
| 1364 | obj = output.pointer; | ||
| 1365 | |||
| 1366 | if (!obj) | ||
| 1367 | return AE_ERROR; | ||
| 1368 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
| 1369 | kfree(obj); | ||
| 1370 | return AE_ERROR; | ||
| 1371 | } | ||
| 1372 | if (obj->buffer.length != 4) { | ||
| 1373 | printk(ACER_WARNING "Unknown buffer length %d\n", | ||
| 1374 | obj->buffer.length); | ||
| 1375 | kfree(obj); | ||
| 1376 | return AE_ERROR; | ||
| 1377 | } | ||
| 1378 | |||
| 1379 | *return_value = *((struct lm_return_value *)obj->buffer.pointer); | ||
| 1380 | kfree(obj); | ||
| 1381 | |||
| 1382 | return status; | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | static int acer_wmi_enable_ec_raw(void) | ||
| 1386 | { | ||
| 1387 | struct lm_return_value return_value; | ||
| 1388 | acpi_status status; | ||
| 1389 | struct lm_input_params params = { | ||
| 1390 | .function_num = 0x1, | ||
| 1391 | .commun_devices = 0xFFFF, | ||
| 1392 | .devices = 0xFFFF, | ||
| 1393 | .lm_status = 0x00, /* Launch Manager Deactive */ | ||
| 1394 | }; | ||
| 1395 | |||
| 1396 | status = wmid3_set_lm_mode(¶ms, &return_value); | ||
| 1397 | |||
| 1398 | if (return_value.error_code || return_value.ec_return_value) | ||
| 1399 | printk(ACER_WARNING "Enabling EC raw mode failed: " | ||
| 1400 | "0x%x - 0x%x\n", return_value.error_code, | ||
| 1401 | return_value.ec_return_value); | ||
| 1402 | else | ||
| 1403 | printk(ACER_INFO "Enabled EC raw mode"); | ||
| 1404 | |||
| 1405 | return status; | ||
| 1406 | } | ||
| 1407 | |||
| 1408 | static int acer_wmi_enable_lm(void) | ||
| 1409 | { | ||
| 1410 | struct lm_return_value return_value; | ||
| 1411 | acpi_status status; | ||
| 1412 | struct lm_input_params params = { | ||
| 1413 | .function_num = 0x1, | ||
| 1414 | .commun_devices = 0xFFFF, | ||
| 1415 | .devices = 0xFFFF, | ||
| 1416 | .lm_status = 0x01, /* Launch Manager Active */ | ||
| 1417 | }; | ||
| 1418 | |||
| 1419 | status = wmid3_set_lm_mode(¶ms, &return_value); | ||
| 1420 | |||
| 1421 | if (return_value.error_code || return_value.ec_return_value) | ||
| 1422 | printk(ACER_WARNING "Enabling Launch Manager failed: " | ||
| 1423 | "0x%x - 0x%x\n", return_value.error_code, | ||
| 1424 | return_value.ec_return_value); | ||
| 1425 | |||
| 1426 | return status; | ||
| 1427 | } | ||
| 1428 | |||
| 1429 | static int __init acer_wmi_input_setup(void) | ||
| 1430 | { | ||
| 1431 | acpi_status status; | ||
| 1432 | int err; | ||
| 1433 | |||
| 1434 | acer_wmi_input_dev = input_allocate_device(); | ||
| 1435 | if (!acer_wmi_input_dev) | ||
| 1436 | return -ENOMEM; | ||
| 1437 | |||
| 1438 | acer_wmi_input_dev->name = "Acer WMI hotkeys"; | ||
| 1439 | acer_wmi_input_dev->phys = "wmi/input0"; | ||
| 1440 | acer_wmi_input_dev->id.bustype = BUS_HOST; | ||
| 1441 | |||
| 1442 | err = sparse_keymap_setup(acer_wmi_input_dev, acer_wmi_keymap, NULL); | ||
| 1443 | if (err) | ||
| 1444 | goto err_free_dev; | ||
| 1445 | |||
| 1446 | status = wmi_install_notify_handler(ACERWMID_EVENT_GUID, | ||
| 1447 | acer_wmi_notify, NULL); | ||
| 1448 | if (ACPI_FAILURE(status)) { | ||
| 1449 | err = -EIO; | ||
| 1450 | goto err_free_keymap; | ||
| 1451 | } | ||
| 1452 | |||
| 1453 | err = input_register_device(acer_wmi_input_dev); | ||
| 1454 | if (err) | ||
| 1455 | goto err_uninstall_notifier; | ||
| 1456 | |||
| 1457 | return 0; | ||
| 1458 | |||
| 1459 | err_uninstall_notifier: | ||
| 1460 | wmi_remove_notify_handler(ACERWMID_EVENT_GUID); | ||
| 1461 | err_free_keymap: | ||
| 1462 | sparse_keymap_free(acer_wmi_input_dev); | ||
| 1463 | err_free_dev: | ||
| 1464 | input_free_device(acer_wmi_input_dev); | ||
| 1465 | return err; | ||
| 1466 | } | ||
| 1467 | |||
| 1468 | static void acer_wmi_input_destroy(void) | ||
| 1469 | { | ||
| 1470 | wmi_remove_notify_handler(ACERWMID_EVENT_GUID); | ||
| 1471 | sparse_keymap_free(acer_wmi_input_dev); | ||
| 1472 | input_unregister_device(acer_wmi_input_dev); | ||
| 1473 | } | ||
| 1474 | |||
| 1088 | /* | 1475 | /* |
| 1089 | * debugfs functions | 1476 | * debugfs functions |
| 1090 | */ | 1477 | */ |
| @@ -1327,6 +1714,26 @@ static int __init acer_wmi_init(void) | |||
| 1327 | "generic video driver\n"); | 1714 | "generic video driver\n"); |
| 1328 | } | 1715 | } |
| 1329 | 1716 | ||
| 1717 | if (wmi_has_guid(WMID_GUID3)) { | ||
| 1718 | if (ec_raw_mode) { | ||
| 1719 | if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) { | ||
| 1720 | printk(ACER_ERR "Cannot enable EC raw mode\n"); | ||
| 1721 | return -ENODEV; | ||
| 1722 | } | ||
| 1723 | } else if (ACPI_FAILURE(acer_wmi_enable_lm())) { | ||
| 1724 | printk(ACER_ERR "Cannot enable Launch Manager mode\n"); | ||
| 1725 | return -ENODEV; | ||
| 1726 | } | ||
| 1727 | } else if (ec_raw_mode) { | ||
| 1728 | printk(ACER_INFO "No WMID EC raw mode enable method\n"); | ||
| 1729 | } | ||
| 1730 | |||
| 1731 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) { | ||
| 1732 | err = acer_wmi_input_setup(); | ||
| 1733 | if (err) | ||
| 1734 | return err; | ||
| 1735 | } | ||
| 1736 | |||
| 1330 | err = platform_driver_register(&acer_platform_driver); | 1737 | err = platform_driver_register(&acer_platform_driver); |
| 1331 | if (err) { | 1738 | if (err) { |
| 1332 | printk(ACER_ERR "Unable to register platform driver.\n"); | 1739 | printk(ACER_ERR "Unable to register platform driver.\n"); |
| @@ -1368,11 +1775,17 @@ error_device_add: | |||
| 1368 | error_device_alloc: | 1775 | error_device_alloc: |
| 1369 | platform_driver_unregister(&acer_platform_driver); | 1776 | platform_driver_unregister(&acer_platform_driver); |
| 1370 | error_platform_register: | 1777 | error_platform_register: |
| 1778 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) | ||
| 1779 | acer_wmi_input_destroy(); | ||
| 1780 | |||
| 1371 | return err; | 1781 | return err; |
| 1372 | } | 1782 | } |
| 1373 | 1783 | ||
| 1374 | static void __exit acer_wmi_exit(void) | 1784 | static void __exit acer_wmi_exit(void) |
| 1375 | { | 1785 | { |
| 1786 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) | ||
| 1787 | acer_wmi_input_destroy(); | ||
| 1788 | |||
| 1376 | remove_sysfs(acer_platform_device); | 1789 | remove_sysfs(acer_platform_device); |
| 1377 | remove_debugfs(); | 1790 | remove_debugfs(); |
| 1378 | platform_device_unregister(acer_platform_device); | 1791 | platform_device_unregister(acer_platform_device); |
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 341cbfef93ee..911135425224 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c | |||
| @@ -522,18 +522,20 @@ static int cmpc_rfkill_block(void *data, bool blocked) | |||
| 522 | acpi_status status; | 522 | acpi_status status; |
| 523 | acpi_handle handle; | 523 | acpi_handle handle; |
| 524 | unsigned long long state; | 524 | unsigned long long state; |
| 525 | bool is_blocked; | ||
| 525 | 526 | ||
| 526 | handle = data; | 527 | handle = data; |
| 527 | status = cmpc_get_rfkill_wlan(handle, &state); | 528 | status = cmpc_get_rfkill_wlan(handle, &state); |
| 528 | if (ACPI_FAILURE(status)) | 529 | if (ACPI_FAILURE(status)) |
| 529 | return -ENODEV; | 530 | return -ENODEV; |
| 530 | if (blocked) | 531 | /* Check if we really need to call cmpc_set_rfkill_wlan */ |
| 531 | state &= ~1; | 532 | is_blocked = state & 1 ? false : true; |
| 532 | else | 533 | if (is_blocked != blocked) { |
| 533 | state |= 1; | 534 | state = blocked ? 0 : 1; |
| 534 | status = cmpc_set_rfkill_wlan(handle, state); | 535 | status = cmpc_set_rfkill_wlan(handle, state); |
| 535 | if (ACPI_FAILURE(status)) | 536 | if (ACPI_FAILURE(status)) |
| 536 | return -ENODEV; | 537 | return -ENODEV; |
| 538 | } | ||
| 537 | return 0; | 539 | return 0; |
| 538 | } | 540 | } |
| 539 | 541 | ||
| @@ -653,8 +655,9 @@ static void cmpc_keys_handler(struct acpi_device *dev, u32 event) | |||
| 653 | 655 | ||
| 654 | if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) | 656 | if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) |
| 655 | code = cmpc_keys_codes[event & 0x0F]; | 657 | code = cmpc_keys_codes[event & 0x0F]; |
| 656 | inputdev = dev_get_drvdata(&dev->dev);; | 658 | inputdev = dev_get_drvdata(&dev->dev); |
| 657 | input_report_key(inputdev, code, !(event & 0x10)); | 659 | input_report_key(inputdev, code, !(event & 0x10)); |
| 660 | input_sync(inputdev); | ||
| 658 | } | 661 | } |
| 659 | 662 | ||
| 660 | static void cmpc_keys_idev_init(struct input_dev *inputdev) | 663 | static void cmpc_keys_idev_init(struct input_dev *inputdev) |
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 097083cac413..034572b980c9 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c | |||
| @@ -872,6 +872,14 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { | |||
| 872 | }, | 872 | }, |
| 873 | .callback = dmi_check_cb_extra | 873 | .callback = dmi_check_cb_extra |
| 874 | }, | 874 | }, |
| 875 | { | ||
| 876 | .ident = "KHLB2", | ||
| 877 | .matches = { | ||
| 878 | DMI_MATCH(DMI_BOARD_NAME, "KHLB2"), | ||
| 879 | DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"), | ||
| 880 | }, | ||
| 881 | .callback = dmi_check_cb_extra | ||
| 882 | }, | ||
| 875 | { } | 883 | { } |
| 876 | }; | 884 | }; |
| 877 | 885 | ||
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index b2edfdcdcb84..e9fc530e7dc2 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
| @@ -529,6 +529,15 @@ static void tpd_led_set(struct led_classdev *led_cdev, | |||
| 529 | queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); | 529 | queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); |
| 530 | } | 530 | } |
| 531 | 531 | ||
| 532 | static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) | ||
| 533 | { | ||
| 534 | struct eeepc_laptop *eeepc; | ||
| 535 | |||
| 536 | eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led); | ||
| 537 | |||
| 538 | return get_acpi(eeepc, CM_ASL_TPD); | ||
| 539 | } | ||
| 540 | |||
| 532 | static int eeepc_led_init(struct eeepc_laptop *eeepc) | 541 | static int eeepc_led_init(struct eeepc_laptop *eeepc) |
| 533 | { | 542 | { |
| 534 | int rv; | 543 | int rv; |
| @@ -543,6 +552,8 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc) | |||
| 543 | 552 | ||
| 544 | eeepc->tpd_led.name = "eeepc::touchpad"; | 553 | eeepc->tpd_led.name = "eeepc::touchpad"; |
| 545 | eeepc->tpd_led.brightness_set = tpd_led_set; | 554 | eeepc->tpd_led.brightness_set = tpd_led_set; |
| 555 | if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */ | ||
| 556 | eeepc->tpd_led.brightness_get = tpd_led_get; | ||
| 546 | eeepc->tpd_led.max_brightness = 1; | 557 | eeepc->tpd_led.max_brightness = 1; |
| 547 | 558 | ||
| 548 | rv = led_classdev_register(&eeepc->platform_device->dev, | 559 | rv = led_classdev_register(&eeepc->platform_device->dev, |
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 0d50fbbe2478..4d38f98aa976 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | * Eee PC WMI hotkey driver | 2 | * Eee PC WMI hotkey driver |
| 3 | * | 3 | * |
| 4 | * Copyright(C) 2010 Intel Corporation. | 4 | * Copyright(C) 2010 Intel Corporation. |
| 5 | * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com> | ||
| 5 | * | 6 | * |
| 6 | * Portions based on wistron_btns.c: | 7 | * Portions based on wistron_btns.c: |
| 7 | * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> | 8 | * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> |
| @@ -34,6 +35,10 @@ | |||
| 34 | #include <linux/input/sparse-keymap.h> | 35 | #include <linux/input/sparse-keymap.h> |
| 35 | #include <linux/fb.h> | 36 | #include <linux/fb.h> |
| 36 | #include <linux/backlight.h> | 37 | #include <linux/backlight.h> |
| 38 | #include <linux/leds.h> | ||
| 39 | #include <linux/rfkill.h> | ||
| 40 | #include <linux/debugfs.h> | ||
| 41 | #include <linux/seq_file.h> | ||
| 37 | #include <linux/platform_device.h> | 42 | #include <linux/platform_device.h> |
| 38 | #include <acpi/acpi_bus.h> | 43 | #include <acpi/acpi_bus.h> |
| 39 | #include <acpi/acpi_drivers.h> | 44 | #include <acpi/acpi_drivers.h> |
| @@ -44,6 +49,8 @@ MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>"); | |||
| 44 | MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); | 49 | MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); |
| 45 | MODULE_LICENSE("GPL"); | 50 | MODULE_LICENSE("GPL"); |
| 46 | 51 | ||
| 52 | #define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */ | ||
| 53 | |||
| 47 | #define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" | 54 | #define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" |
| 48 | #define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" | 55 | #define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" |
| 49 | 56 | ||
| @@ -60,6 +67,10 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); | |||
| 60 | #define EEEPC_WMI_METHODID_CFVS 0x53564643 | 67 | #define EEEPC_WMI_METHODID_CFVS 0x53564643 |
| 61 | 68 | ||
| 62 | #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 | 69 | #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 |
| 70 | #define EEEPC_WMI_DEVID_TPDLED 0x00100011 | ||
| 71 | #define EEEPC_WMI_DEVID_WLAN 0x00010011 | ||
| 72 | #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 | ||
| 73 | #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 | ||
| 63 | 74 | ||
| 64 | static const struct key_entry eeepc_wmi_keymap[] = { | 75 | static const struct key_entry eeepc_wmi_keymap[] = { |
| 65 | /* Sleep already handled via generic ACPI code */ | 76 | /* Sleep already handled via generic ACPI code */ |
| @@ -83,11 +94,37 @@ struct bios_args { | |||
| 83 | u32 ctrl_param; | 94 | u32 ctrl_param; |
| 84 | }; | 95 | }; |
| 85 | 96 | ||
| 97 | /* | ||
| 98 | * eeepc-wmi/ - debugfs root directory | ||
| 99 | * dev_id - current dev_id | ||
| 100 | * ctrl_param - current ctrl_param | ||
| 101 | * devs - call DEVS(dev_id, ctrl_param) and print result | ||
| 102 | * dsts - call DSTS(dev_id) and print result | ||
| 103 | */ | ||
| 104 | struct eeepc_wmi_debug { | ||
| 105 | struct dentry *root; | ||
| 106 | u32 dev_id; | ||
| 107 | u32 ctrl_param; | ||
| 108 | }; | ||
| 109 | |||
| 86 | struct eeepc_wmi { | 110 | struct eeepc_wmi { |
| 87 | struct input_dev *inputdev; | 111 | struct input_dev *inputdev; |
| 88 | struct backlight_device *backlight_device; | 112 | struct backlight_device *backlight_device; |
| 113 | struct platform_device *platform_device; | ||
| 114 | |||
| 115 | struct led_classdev tpd_led; | ||
| 116 | int tpd_led_wk; | ||
| 117 | struct workqueue_struct *led_workqueue; | ||
| 118 | struct work_struct tpd_led_work; | ||
| 119 | |||
| 120 | struct rfkill *wlan_rfkill; | ||
| 121 | struct rfkill *bluetooth_rfkill; | ||
| 122 | struct rfkill *wwan3g_rfkill; | ||
| 123 | |||
| 124 | struct eeepc_wmi_debug debug; | ||
| 89 | }; | 125 | }; |
| 90 | 126 | ||
| 127 | /* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */ | ||
| 91 | static struct platform_device *platform_device; | 128 | static struct platform_device *platform_device; |
| 92 | 129 | ||
| 93 | static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) | 130 | static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) |
| @@ -101,7 +138,7 @@ static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) | |||
| 101 | eeepc->inputdev->name = "Eee PC WMI hotkeys"; | 138 | eeepc->inputdev->name = "Eee PC WMI hotkeys"; |
| 102 | eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; | 139 | eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; |
| 103 | eeepc->inputdev->id.bustype = BUS_HOST; | 140 | eeepc->inputdev->id.bustype = BUS_HOST; |
| 104 | eeepc->inputdev->dev.parent = &platform_device->dev; | 141 | eeepc->inputdev->dev.parent = &eeepc->platform_device->dev; |
| 105 | 142 | ||
| 106 | err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); | 143 | err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); |
| 107 | if (err) | 144 | if (err) |
| @@ -130,7 +167,7 @@ static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc) | |||
| 130 | eeepc->inputdev = NULL; | 167 | eeepc->inputdev = NULL; |
| 131 | } | 168 | } |
| 132 | 169 | ||
| 133 | static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) | 170 | static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval) |
| 134 | { | 171 | { |
| 135 | struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; | 172 | struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; |
| 136 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | 173 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
| @@ -150,8 +187,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) | |||
| 150 | else | 187 | else |
| 151 | tmp = 0; | 188 | tmp = 0; |
| 152 | 189 | ||
| 153 | if (ctrl_param) | 190 | if (retval) |
| 154 | *ctrl_param = tmp; | 191 | *retval = tmp; |
| 155 | 192 | ||
| 156 | kfree(obj); | 193 | kfree(obj); |
| 157 | 194 | ||
| @@ -159,7 +196,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) | |||
| 159 | 196 | ||
| 160 | } | 197 | } |
| 161 | 198 | ||
| 162 | static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) | 199 | static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, |
| 200 | u32 *retval) | ||
| 163 | { | 201 | { |
| 164 | struct bios_args args = { | 202 | struct bios_args args = { |
| 165 | .dev_id = dev_id, | 203 | .dev_id = dev_id, |
| @@ -168,34 +206,281 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) | |||
| 168 | struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; | 206 | struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; |
| 169 | acpi_status status; | 207 | acpi_status status; |
| 170 | 208 | ||
| 171 | status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, | 209 | if (!retval) { |
| 172 | 1, EEEPC_WMI_METHODID_DEVS, &input, NULL); | 210 | status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, |
| 211 | EEEPC_WMI_METHODID_DEVS, | ||
| 212 | &input, NULL); | ||
| 213 | } else { | ||
| 214 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 215 | union acpi_object *obj; | ||
| 216 | u32 tmp; | ||
| 217 | |||
| 218 | status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, | ||
| 219 | EEEPC_WMI_METHODID_DEVS, | ||
| 220 | &input, &output); | ||
| 221 | |||
| 222 | if (ACPI_FAILURE(status)) | ||
| 223 | return status; | ||
| 224 | |||
| 225 | obj = (union acpi_object *)output.pointer; | ||
| 226 | if (obj && obj->type == ACPI_TYPE_INTEGER) | ||
| 227 | tmp = (u32)obj->integer.value; | ||
| 228 | else | ||
| 229 | tmp = 0; | ||
| 230 | |||
| 231 | *retval = tmp; | ||
| 232 | |||
| 233 | kfree(obj); | ||
| 234 | } | ||
| 173 | 235 | ||
| 174 | return status; | 236 | return status; |
| 175 | } | 237 | } |
| 176 | 238 | ||
| 239 | /* | ||
| 240 | * LEDs | ||
| 241 | */ | ||
| 242 | /* | ||
| 243 | * These functions actually update the LED's, and are called from a | ||
| 244 | * workqueue. By doing this as separate work rather than when the LED | ||
| 245 | * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a | ||
| 246 | * potentially bad time, such as a timer interrupt. | ||
| 247 | */ | ||
| 248 | static void tpd_led_update(struct work_struct *work) | ||
| 249 | { | ||
| 250 | int ctrl_param; | ||
| 251 | struct eeepc_wmi *eeepc; | ||
| 252 | |||
| 253 | eeepc = container_of(work, struct eeepc_wmi, tpd_led_work); | ||
| 254 | |||
| 255 | ctrl_param = eeepc->tpd_led_wk; | ||
| 256 | eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param, NULL); | ||
| 257 | } | ||
| 258 | |||
| 259 | static void tpd_led_set(struct led_classdev *led_cdev, | ||
| 260 | enum led_brightness value) | ||
| 261 | { | ||
| 262 | struct eeepc_wmi *eeepc; | ||
| 263 | |||
| 264 | eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); | ||
| 265 | |||
| 266 | eeepc->tpd_led_wk = !!value; | ||
| 267 | queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); | ||
| 268 | } | ||
| 269 | |||
| 270 | static int read_tpd_state(struct eeepc_wmi *eeepc) | ||
| 271 | { | ||
| 272 | u32 retval; | ||
| 273 | acpi_status status; | ||
| 274 | |||
| 275 | status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval); | ||
| 276 | |||
| 277 | if (ACPI_FAILURE(status)) | ||
| 278 | return -1; | ||
| 279 | else if (!retval || retval == 0x00060000) | ||
| 280 | /* | ||
| 281 | * if touchpad led is present, DSTS will set some bits, | ||
| 282 | * usually 0x00020000. | ||
| 283 | * 0x00060000 means that the device is not supported | ||
| 284 | */ | ||
| 285 | return -ENODEV; | ||
| 286 | else | ||
| 287 | /* Status is stored in the first bit */ | ||
| 288 | return retval & 0x1; | ||
| 289 | } | ||
| 290 | |||
| 291 | static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) | ||
| 292 | { | ||
| 293 | struct eeepc_wmi *eeepc; | ||
| 294 | |||
| 295 | eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); | ||
| 296 | |||
| 297 | return read_tpd_state(eeepc); | ||
| 298 | } | ||
| 299 | |||
| 300 | static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc) | ||
| 301 | { | ||
| 302 | int rv; | ||
| 303 | |||
| 304 | if (read_tpd_state(eeepc) < 0) | ||
| 305 | return 0; | ||
| 306 | |||
| 307 | eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); | ||
| 308 | if (!eeepc->led_workqueue) | ||
| 309 | return -ENOMEM; | ||
| 310 | INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); | ||
| 311 | |||
| 312 | eeepc->tpd_led.name = "eeepc::touchpad"; | ||
| 313 | eeepc->tpd_led.brightness_set = tpd_led_set; | ||
| 314 | eeepc->tpd_led.brightness_get = tpd_led_get; | ||
| 315 | eeepc->tpd_led.max_brightness = 1; | ||
| 316 | |||
| 317 | rv = led_classdev_register(&eeepc->platform_device->dev, | ||
| 318 | &eeepc->tpd_led); | ||
| 319 | if (rv) { | ||
| 320 | destroy_workqueue(eeepc->led_workqueue); | ||
| 321 | return rv; | ||
| 322 | } | ||
| 323 | |||
| 324 | return 0; | ||
| 325 | } | ||
| 326 | |||
| 327 | static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) | ||
| 328 | { | ||
| 329 | if (eeepc->tpd_led.dev) | ||
| 330 | led_classdev_unregister(&eeepc->tpd_led); | ||
| 331 | if (eeepc->led_workqueue) | ||
| 332 | destroy_workqueue(eeepc->led_workqueue); | ||
| 333 | } | ||
| 334 | |||
| 335 | /* | ||
| 336 | * Rfkill devices | ||
| 337 | */ | ||
| 338 | static int eeepc_rfkill_set(void *data, bool blocked) | ||
| 339 | { | ||
| 340 | int dev_id = (unsigned long)data; | ||
| 341 | u32 ctrl_param = !blocked; | ||
| 342 | |||
| 343 | return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL); | ||
| 344 | } | ||
| 345 | |||
| 346 | static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) | ||
| 347 | { | ||
| 348 | int dev_id = (unsigned long)data; | ||
| 349 | u32 retval; | ||
| 350 | acpi_status status; | ||
| 351 | |||
| 352 | status = eeepc_wmi_get_devstate(dev_id, &retval); | ||
| 353 | |||
| 354 | if (ACPI_FAILURE(status)) | ||
| 355 | return ; | ||
| 356 | |||
| 357 | rfkill_set_sw_state(rfkill, !(retval & 0x1)); | ||
| 358 | } | ||
| 359 | |||
| 360 | static const struct rfkill_ops eeepc_rfkill_ops = { | ||
| 361 | .set_block = eeepc_rfkill_set, | ||
| 362 | .query = eeepc_rfkill_query, | ||
| 363 | }; | ||
| 364 | |||
| 365 | static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, | ||
| 366 | struct rfkill **rfkill, | ||
| 367 | const char *name, | ||
| 368 | enum rfkill_type type, int dev_id) | ||
| 369 | { | ||
| 370 | int result; | ||
| 371 | u32 retval; | ||
| 372 | acpi_status status; | ||
| 373 | |||
| 374 | status = eeepc_wmi_get_devstate(dev_id, &retval); | ||
| 375 | |||
| 376 | if (ACPI_FAILURE(status)) | ||
| 377 | return -1; | ||
| 378 | |||
| 379 | /* If the device is present, DSTS will always set some bits | ||
| 380 | * 0x00070000 - 1110000000000000000 - device supported | ||
| 381 | * 0x00060000 - 1100000000000000000 - not supported | ||
| 382 | * 0x00020000 - 0100000000000000000 - device supported | ||
| 383 | * 0x00010000 - 0010000000000000000 - not supported / special mode ? | ||
| 384 | */ | ||
| 385 | if (!retval || retval == 0x00060000) | ||
| 386 | return -ENODEV; | ||
| 387 | |||
| 388 | *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, | ||
| 389 | &eeepc_rfkill_ops, (void *)(long)dev_id); | ||
| 390 | |||
| 391 | if (!*rfkill) | ||
| 392 | return -EINVAL; | ||
| 393 | |||
| 394 | rfkill_init_sw_state(*rfkill, !(retval & 0x1)); | ||
| 395 | result = rfkill_register(*rfkill); | ||
| 396 | if (result) { | ||
| 397 | rfkill_destroy(*rfkill); | ||
| 398 | *rfkill = NULL; | ||
| 399 | return result; | ||
| 400 | } | ||
| 401 | return 0; | ||
| 402 | } | ||
| 403 | |||
| 404 | static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) | ||
| 405 | { | ||
| 406 | if (eeepc->wlan_rfkill) { | ||
| 407 | rfkill_unregister(eeepc->wlan_rfkill); | ||
| 408 | rfkill_destroy(eeepc->wlan_rfkill); | ||
| 409 | eeepc->wlan_rfkill = NULL; | ||
| 410 | } | ||
| 411 | if (eeepc->bluetooth_rfkill) { | ||
| 412 | rfkill_unregister(eeepc->bluetooth_rfkill); | ||
| 413 | rfkill_destroy(eeepc->bluetooth_rfkill); | ||
| 414 | eeepc->bluetooth_rfkill = NULL; | ||
| 415 | } | ||
| 416 | if (eeepc->wwan3g_rfkill) { | ||
| 417 | rfkill_unregister(eeepc->wwan3g_rfkill); | ||
| 418 | rfkill_destroy(eeepc->wwan3g_rfkill); | ||
| 419 | eeepc->wwan3g_rfkill = NULL; | ||
| 420 | } | ||
| 421 | } | ||
| 422 | |||
| 423 | static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) | ||
| 424 | { | ||
| 425 | int result = 0; | ||
| 426 | |||
| 427 | result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, | ||
| 428 | "eeepc-wlan", RFKILL_TYPE_WLAN, | ||
| 429 | EEEPC_WMI_DEVID_WLAN); | ||
| 430 | |||
| 431 | if (result && result != -ENODEV) | ||
| 432 | goto exit; | ||
| 433 | |||
| 434 | result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill, | ||
| 435 | "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH, | ||
| 436 | EEEPC_WMI_DEVID_BLUETOOTH); | ||
| 437 | |||
| 438 | if (result && result != -ENODEV) | ||
| 439 | goto exit; | ||
| 440 | |||
| 441 | result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, | ||
| 442 | "eeepc-wwan3g", RFKILL_TYPE_WWAN, | ||
| 443 | EEEPC_WMI_DEVID_WWAN3G); | ||
| 444 | |||
| 445 | if (result && result != -ENODEV) | ||
| 446 | goto exit; | ||
| 447 | |||
| 448 | exit: | ||
| 449 | if (result && result != -ENODEV) | ||
| 450 | eeepc_wmi_rfkill_exit(eeepc); | ||
| 451 | |||
| 452 | if (result == -ENODEV) | ||
| 453 | result = 0; | ||
| 454 | |||
| 455 | return result; | ||
| 456 | } | ||
| 457 | |||
| 458 | /* | ||
| 459 | * Backlight | ||
| 460 | */ | ||
| 177 | static int read_brightness(struct backlight_device *bd) | 461 | static int read_brightness(struct backlight_device *bd) |
| 178 | { | 462 | { |
| 179 | static u32 ctrl_param; | 463 | u32 retval; |
| 180 | acpi_status status; | 464 | acpi_status status; |
| 181 | 465 | ||
| 182 | status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param); | 466 | status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval); |
| 183 | 467 | ||
| 184 | if (ACPI_FAILURE(status)) | 468 | if (ACPI_FAILURE(status)) |
| 185 | return -1; | 469 | return -1; |
| 186 | else | 470 | else |
| 187 | return ctrl_param & 0xFF; | 471 | return retval & 0xFF; |
| 188 | } | 472 | } |
| 189 | 473 | ||
| 190 | static int update_bl_status(struct backlight_device *bd) | 474 | static int update_bl_status(struct backlight_device *bd) |
| 191 | { | 475 | { |
| 192 | 476 | ||
| 193 | static u32 ctrl_param; | 477 | u32 ctrl_param; |
| 194 | acpi_status status; | 478 | acpi_status status; |
| 195 | 479 | ||
| 196 | ctrl_param = bd->props.brightness; | 480 | ctrl_param = bd->props.brightness; |
| 197 | 481 | ||
| 198 | status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, ctrl_param); | 482 | status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, |
| 483 | ctrl_param, NULL); | ||
| 199 | 484 | ||
| 200 | if (ACPI_FAILURE(status)) | 485 | if (ACPI_FAILURE(status)) |
| 201 | return -1; | 486 | return -1; |
| @@ -234,7 +519,7 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) | |||
| 234 | memset(&props, 0, sizeof(struct backlight_properties)); | 519 | memset(&props, 0, sizeof(struct backlight_properties)); |
| 235 | props.max_brightness = 15; | 520 | props.max_brightness = 15; |
| 236 | bd = backlight_device_register(EEEPC_WMI_FILE, | 521 | bd = backlight_device_register(EEEPC_WMI_FILE, |
| 237 | &platform_device->dev, eeepc, | 522 | &eeepc->platform_device->dev, eeepc, |
| 238 | &eeepc_wmi_bl_ops, &props); | 523 | &eeepc_wmi_bl_ops, &props); |
| 239 | if (IS_ERR(bd)) { | 524 | if (IS_ERR(bd)) { |
| 240 | pr_err("Could not register backlight device\n"); | 525 | pr_err("Could not register backlight device\n"); |
| @@ -321,65 +606,240 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, | |||
| 321 | 606 | ||
| 322 | static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); | 607 | static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); |
| 323 | 608 | ||
| 609 | static struct attribute *platform_attributes[] = { | ||
| 610 | &dev_attr_cpufv.attr, | ||
| 611 | NULL | ||
| 612 | }; | ||
| 613 | |||
| 614 | static struct attribute_group platform_attribute_group = { | ||
| 615 | .attrs = platform_attributes | ||
| 616 | }; | ||
| 617 | |||
| 324 | static void eeepc_wmi_sysfs_exit(struct platform_device *device) | 618 | static void eeepc_wmi_sysfs_exit(struct platform_device *device) |
| 325 | { | 619 | { |
| 326 | device_remove_file(&device->dev, &dev_attr_cpufv); | 620 | sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); |
| 327 | } | 621 | } |
| 328 | 622 | ||
| 329 | static int eeepc_wmi_sysfs_init(struct platform_device *device) | 623 | static int eeepc_wmi_sysfs_init(struct platform_device *device) |
| 330 | { | 624 | { |
| 331 | int retval = -ENOMEM; | 625 | return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); |
| 626 | } | ||
| 332 | 627 | ||
| 333 | retval = device_create_file(&device->dev, &dev_attr_cpufv); | 628 | /* |
| 334 | if (retval) | 629 | * Platform device |
| 335 | goto error_sysfs; | 630 | */ |
| 631 | static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc) | ||
| 632 | { | ||
| 633 | int err; | ||
| 336 | 634 | ||
| 635 | eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); | ||
| 636 | if (!eeepc->platform_device) | ||
| 637 | return -ENOMEM; | ||
| 638 | platform_set_drvdata(eeepc->platform_device, eeepc); | ||
| 639 | |||
| 640 | err = platform_device_add(eeepc->platform_device); | ||
| 641 | if (err) | ||
| 642 | goto fail_platform_device; | ||
| 643 | |||
| 644 | err = eeepc_wmi_sysfs_init(eeepc->platform_device); | ||
| 645 | if (err) | ||
| 646 | goto fail_sysfs; | ||
| 337 | return 0; | 647 | return 0; |
| 338 | 648 | ||
| 339 | error_sysfs: | 649 | fail_sysfs: |
| 340 | eeepc_wmi_sysfs_exit(platform_device); | 650 | platform_device_del(eeepc->platform_device); |
| 341 | return retval; | 651 | fail_platform_device: |
| 652 | platform_device_put(eeepc->platform_device); | ||
| 653 | return err; | ||
| 342 | } | 654 | } |
| 343 | 655 | ||
| 344 | static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) | 656 | static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc) |
| 345 | { | 657 | { |
| 658 | eeepc_wmi_sysfs_exit(eeepc->platform_device); | ||
| 659 | platform_device_unregister(eeepc->platform_device); | ||
| 660 | } | ||
| 661 | |||
| 662 | /* | ||
| 663 | * debugfs | ||
| 664 | */ | ||
| 665 | struct eeepc_wmi_debugfs_node { | ||
| 346 | struct eeepc_wmi *eeepc; | 666 | struct eeepc_wmi *eeepc; |
| 347 | int err; | 667 | char *name; |
| 668 | int (*show)(struct seq_file *m, void *data); | ||
| 669 | }; | ||
| 670 | |||
| 671 | static int show_dsts(struct seq_file *m, void *data) | ||
| 672 | { | ||
| 673 | struct eeepc_wmi *eeepc = m->private; | ||
| 348 | acpi_status status; | 674 | acpi_status status; |
| 675 | u32 retval = -1; | ||
| 349 | 676 | ||
| 350 | eeepc = platform_get_drvdata(device); | 677 | status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval); |
| 678 | |||
| 679 | if (ACPI_FAILURE(status)) | ||
| 680 | return -EIO; | ||
| 681 | |||
| 682 | seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval); | ||
| 683 | |||
| 684 | return 0; | ||
| 685 | } | ||
| 686 | |||
| 687 | static int show_devs(struct seq_file *m, void *data) | ||
| 688 | { | ||
| 689 | struct eeepc_wmi *eeepc = m->private; | ||
| 690 | acpi_status status; | ||
| 691 | u32 retval = -1; | ||
| 692 | |||
| 693 | status = eeepc_wmi_set_devstate(eeepc->debug.dev_id, | ||
| 694 | eeepc->debug.ctrl_param, &retval); | ||
| 695 | if (ACPI_FAILURE(status)) | ||
| 696 | return -EIO; | ||
| 697 | |||
| 698 | seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id, | ||
| 699 | eeepc->debug.ctrl_param, retval); | ||
| 700 | |||
| 701 | return 0; | ||
| 702 | } | ||
| 703 | |||
| 704 | static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = { | ||
| 705 | { NULL, "devs", show_devs }, | ||
| 706 | { NULL, "dsts", show_dsts }, | ||
| 707 | }; | ||
| 708 | |||
| 709 | static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file) | ||
| 710 | { | ||
| 711 | struct eeepc_wmi_debugfs_node *node = inode->i_private; | ||
| 712 | |||
| 713 | return single_open(file, node->show, node->eeepc); | ||
| 714 | } | ||
| 715 | |||
| 716 | static const struct file_operations eeepc_wmi_debugfs_io_ops = { | ||
| 717 | .owner = THIS_MODULE, | ||
| 718 | .open = eeepc_wmi_debugfs_open, | ||
| 719 | .read = seq_read, | ||
| 720 | .llseek = seq_lseek, | ||
| 721 | .release = single_release, | ||
| 722 | }; | ||
| 723 | |||
| 724 | static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc) | ||
| 725 | { | ||
| 726 | debugfs_remove_recursive(eeepc->debug.root); | ||
| 727 | } | ||
| 728 | |||
| 729 | static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc) | ||
| 730 | { | ||
| 731 | struct dentry *dent; | ||
| 732 | int i; | ||
| 733 | |||
| 734 | eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL); | ||
| 735 | if (!eeepc->debug.root) { | ||
| 736 | pr_err("failed to create debugfs directory"); | ||
| 737 | goto error_debugfs; | ||
| 738 | } | ||
| 739 | |||
| 740 | dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR, | ||
| 741 | eeepc->debug.root, &eeepc->debug.dev_id); | ||
| 742 | if (!dent) | ||
| 743 | goto error_debugfs; | ||
| 744 | |||
| 745 | dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR, | ||
| 746 | eeepc->debug.root, &eeepc->debug.ctrl_param); | ||
| 747 | if (!dent) | ||
| 748 | goto error_debugfs; | ||
| 749 | |||
| 750 | for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) { | ||
| 751 | struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i]; | ||
| 752 | |||
| 753 | node->eeepc = eeepc; | ||
| 754 | dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO, | ||
| 755 | eeepc->debug.root, node, | ||
| 756 | &eeepc_wmi_debugfs_io_ops); | ||
| 757 | if (!dent) { | ||
| 758 | pr_err("failed to create debug file: %s\n", node->name); | ||
| 759 | goto error_debugfs; | ||
| 760 | } | ||
| 761 | } | ||
| 762 | |||
| 763 | return 0; | ||
| 764 | |||
| 765 | error_debugfs: | ||
| 766 | eeepc_wmi_debugfs_exit(eeepc); | ||
| 767 | return -ENOMEM; | ||
| 768 | } | ||
| 769 | |||
| 770 | /* | ||
| 771 | * WMI Driver | ||
| 772 | */ | ||
| 773 | static struct platform_device * __init eeepc_wmi_add(void) | ||
| 774 | { | ||
| 775 | struct eeepc_wmi *eeepc; | ||
| 776 | acpi_status status; | ||
| 777 | int err; | ||
| 778 | |||
| 779 | eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); | ||
| 780 | if (!eeepc) | ||
| 781 | return ERR_PTR(-ENOMEM); | ||
| 782 | |||
| 783 | /* | ||
| 784 | * Register the platform device first. It is used as a parent for the | ||
| 785 | * sub-devices below. | ||
| 786 | */ | ||
| 787 | err = eeepc_wmi_platform_init(eeepc); | ||
| 788 | if (err) | ||
| 789 | goto fail_platform; | ||
| 351 | 790 | ||
| 352 | err = eeepc_wmi_input_init(eeepc); | 791 | err = eeepc_wmi_input_init(eeepc); |
| 353 | if (err) | 792 | if (err) |
| 354 | goto error_input; | 793 | goto fail_input; |
| 794 | |||
| 795 | err = eeepc_wmi_led_init(eeepc); | ||
| 796 | if (err) | ||
| 797 | goto fail_leds; | ||
| 798 | |||
| 799 | err = eeepc_wmi_rfkill_init(eeepc); | ||
| 800 | if (err) | ||
| 801 | goto fail_rfkill; | ||
| 355 | 802 | ||
| 356 | if (!acpi_video_backlight_support()) { | 803 | if (!acpi_video_backlight_support()) { |
| 357 | err = eeepc_wmi_backlight_init(eeepc); | 804 | err = eeepc_wmi_backlight_init(eeepc); |
| 358 | if (err) | 805 | if (err) |
| 359 | goto error_backlight; | 806 | goto fail_backlight; |
| 360 | } else | 807 | } else |
| 361 | pr_info("Backlight controlled by ACPI video driver\n"); | 808 | pr_info("Backlight controlled by ACPI video driver\n"); |
| 362 | 809 | ||
| 363 | status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, | 810 | status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, |
| 364 | eeepc_wmi_notify, eeepc); | 811 | eeepc_wmi_notify, eeepc); |
| 365 | if (ACPI_FAILURE(status)) { | 812 | if (ACPI_FAILURE(status)) { |
| 366 | pr_err("Unable to register notify handler - %d\n", | 813 | pr_err("Unable to register notify handler - %d\n", |
| 367 | status); | 814 | status); |
| 368 | err = -ENODEV; | 815 | err = -ENODEV; |
| 369 | goto error_wmi; | 816 | goto fail_wmi_handler; |
| 370 | } | 817 | } |
| 371 | 818 | ||
| 372 | return 0; | 819 | err = eeepc_wmi_debugfs_init(eeepc); |
| 820 | if (err) | ||
| 821 | goto fail_debugfs; | ||
| 373 | 822 | ||
| 374 | error_wmi: | 823 | return eeepc->platform_device; |
| 824 | |||
| 825 | fail_debugfs: | ||
| 826 | wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); | ||
| 827 | fail_wmi_handler: | ||
| 375 | eeepc_wmi_backlight_exit(eeepc); | 828 | eeepc_wmi_backlight_exit(eeepc); |
| 376 | error_backlight: | 829 | fail_backlight: |
| 830 | eeepc_wmi_rfkill_exit(eeepc); | ||
| 831 | fail_rfkill: | ||
| 832 | eeepc_wmi_led_exit(eeepc); | ||
| 833 | fail_leds: | ||
| 377 | eeepc_wmi_input_exit(eeepc); | 834 | eeepc_wmi_input_exit(eeepc); |
| 378 | error_input: | 835 | fail_input: |
| 379 | return err; | 836 | eeepc_wmi_platform_exit(eeepc); |
| 837 | fail_platform: | ||
| 838 | kfree(eeepc); | ||
| 839 | return ERR_PTR(err); | ||
| 380 | } | 840 | } |
| 381 | 841 | ||
| 382 | static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) | 842 | static int eeepc_wmi_remove(struct platform_device *device) |
| 383 | { | 843 | { |
| 384 | struct eeepc_wmi *eeepc; | 844 | struct eeepc_wmi *eeepc; |
| 385 | 845 | ||
| @@ -387,7 +847,12 @@ static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) | |||
| 387 | wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); | 847 | wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); |
| 388 | eeepc_wmi_backlight_exit(eeepc); | 848 | eeepc_wmi_backlight_exit(eeepc); |
| 389 | eeepc_wmi_input_exit(eeepc); | 849 | eeepc_wmi_input_exit(eeepc); |
| 850 | eeepc_wmi_led_exit(eeepc); | ||
| 851 | eeepc_wmi_rfkill_exit(eeepc); | ||
| 852 | eeepc_wmi_debugfs_exit(eeepc); | ||
| 853 | eeepc_wmi_platform_exit(eeepc); | ||
| 390 | 854 | ||
| 855 | kfree(eeepc); | ||
| 391 | return 0; | 856 | return 0; |
| 392 | } | 857 | } |
| 393 | 858 | ||
| @@ -396,13 +861,31 @@ static struct platform_driver platform_driver = { | |||
| 396 | .name = EEEPC_WMI_FILE, | 861 | .name = EEEPC_WMI_FILE, |
| 397 | .owner = THIS_MODULE, | 862 | .owner = THIS_MODULE, |
| 398 | }, | 863 | }, |
| 399 | .probe = eeepc_wmi_platform_probe, | ||
| 400 | .remove = __devexit_p(eeepc_wmi_platform_remove), | ||
| 401 | }; | 864 | }; |
| 402 | 865 | ||
| 866 | static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level, | ||
| 867 | void *context, void **retval) | ||
| 868 | { | ||
| 869 | pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID); | ||
| 870 | *(bool *)context = true; | ||
| 871 | return AE_CTRL_TERMINATE; | ||
| 872 | } | ||
| 873 | |||
| 874 | static int __init eeepc_wmi_check_atkd(void) | ||
| 875 | { | ||
| 876 | acpi_status status; | ||
| 877 | bool found = false; | ||
| 878 | |||
| 879 | status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device, | ||
| 880 | &found, NULL); | ||
| 881 | |||
| 882 | if (ACPI_FAILURE(status) || !found) | ||
| 883 | return 0; | ||
| 884 | return -1; | ||
| 885 | } | ||
| 886 | |||
| 403 | static int __init eeepc_wmi_init(void) | 887 | static int __init eeepc_wmi_init(void) |
| 404 | { | 888 | { |
| 405 | struct eeepc_wmi *eeepc; | ||
| 406 | int err; | 889 | int err; |
| 407 | 890 | ||
| 408 | if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || | 891 | if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || |
| @@ -411,58 +894,40 @@ static int __init eeepc_wmi_init(void) | |||
| 411 | return -ENODEV; | 894 | return -ENODEV; |
| 412 | } | 895 | } |
| 413 | 896 | ||
| 414 | eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); | 897 | if (eeepc_wmi_check_atkd()) { |
| 415 | if (!eeepc) | 898 | pr_warning("WMI device present, but legacy ATKD device is also " |
| 416 | return -ENOMEM; | 899 | "present and enabled."); |
| 417 | 900 | pr_warning("You probably booted with acpi_osi=\"Linux\" or " | |
| 418 | platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); | 901 | "acpi_osi=\"!Windows 2009\""); |
| 419 | if (!platform_device) { | 902 | pr_warning("Can't load eeepc-wmi, use default acpi_osi " |
| 420 | pr_warning("Unable to allocate platform device\n"); | 903 | "(preferred) or eeepc-laptop"); |
| 421 | err = -ENOMEM; | 904 | return -ENODEV; |
| 422 | goto fail_platform; | ||
| 423 | } | 905 | } |
| 424 | 906 | ||
| 425 | err = platform_device_add(platform_device); | 907 | platform_device = eeepc_wmi_add(); |
| 426 | if (err) { | 908 | if (IS_ERR(platform_device)) { |
| 427 | pr_warning("Unable to add platform device\n"); | 909 | err = PTR_ERR(platform_device); |
| 428 | goto put_dev; | 910 | goto fail_eeepc_wmi; |
| 429 | } | 911 | } |
| 430 | 912 | ||
| 431 | platform_set_drvdata(platform_device, eeepc); | ||
| 432 | |||
| 433 | err = platform_driver_register(&platform_driver); | 913 | err = platform_driver_register(&platform_driver); |
| 434 | if (err) { | 914 | if (err) { |
| 435 | pr_warning("Unable to register platform driver\n"); | 915 | pr_warning("Unable to register platform driver\n"); |
| 436 | goto del_dev; | 916 | goto fail_platform_driver; |
| 437 | } | 917 | } |
| 438 | 918 | ||
| 439 | err = eeepc_wmi_sysfs_init(platform_device); | ||
| 440 | if (err) | ||
| 441 | goto del_sysfs; | ||
| 442 | |||
| 443 | return 0; | 919 | return 0; |
| 444 | 920 | ||
| 445 | del_sysfs: | 921 | fail_platform_driver: |
| 446 | eeepc_wmi_sysfs_exit(platform_device); | 922 | eeepc_wmi_remove(platform_device); |
| 447 | del_dev: | 923 | fail_eeepc_wmi: |
| 448 | platform_device_del(platform_device); | ||
| 449 | put_dev: | ||
| 450 | platform_device_put(platform_device); | ||
| 451 | fail_platform: | ||
| 452 | kfree(eeepc); | ||
| 453 | |||
| 454 | return err; | 924 | return err; |
| 455 | } | 925 | } |
| 456 | 926 | ||
| 457 | static void __exit eeepc_wmi_exit(void) | 927 | static void __exit eeepc_wmi_exit(void) |
| 458 | { | 928 | { |
| 459 | struct eeepc_wmi *eeepc; | 929 | eeepc_wmi_remove(platform_device); |
| 460 | |||
| 461 | eeepc_wmi_sysfs_exit(platform_device); | ||
| 462 | eeepc = platform_get_drvdata(platform_device); | ||
| 463 | platform_driver_unregister(&platform_driver); | 930 | platform_driver_unregister(&platform_driver); |
| 464 | platform_device_unregister(platform_device); | ||
| 465 | kfree(eeepc); | ||
| 466 | } | 931 | } |
| 467 | 932 | ||
| 468 | module_init(eeepc_wmi_init); | 933 | module_init(eeepc_wmi_init); |
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index f44cd2620ff9..ad88b2ec34a1 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c | |||
| @@ -1240,7 +1240,7 @@ MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); | |||
| 1240 | MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); | 1240 | MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); |
| 1241 | MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); | 1241 | MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); |
| 1242 | 1242 | ||
| 1243 | static struct pnp_device_id pnp_ids[] = { | 1243 | static struct pnp_device_id pnp_ids[] __used = { |
| 1244 | {.id = "FUJ02bf"}, | 1244 | {.id = "FUJ02bf"}, |
| 1245 | {.id = "FUJ02B1"}, | 1245 | {.id = "FUJ02B1"}, |
| 1246 | {.id = "FUJ02E3"}, | 1246 | {.id = "FUJ02E3"}, |
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 5ff12205aa6b..114d95247cdf 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * ideapad_acpi.c - Lenovo IdeaPad ACPI Extras | 2 | * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras |
| 3 | * | 3 | * |
| 4 | * Copyright © 2010 Intel Corporation | 4 | * Copyright © 2010 Intel Corporation |
| 5 | * Copyright © 2010 David Woodhouse <dwmw2@infradead.org> | 5 | * Copyright © 2010 David Woodhouse <dwmw2@infradead.org> |
| @@ -27,31 +27,19 @@ | |||
| 27 | #include <acpi/acpi_bus.h> | 27 | #include <acpi/acpi_bus.h> |
| 28 | #include <acpi/acpi_drivers.h> | 28 | #include <acpi/acpi_drivers.h> |
| 29 | #include <linux/rfkill.h> | 29 | #include <linux/rfkill.h> |
| 30 | #include <linux/platform_device.h> | ||
| 31 | #include <linux/input.h> | ||
| 32 | #include <linux/input/sparse-keymap.h> | ||
| 30 | 33 | ||
| 31 | #define IDEAPAD_DEV_CAMERA 0 | 34 | #define IDEAPAD_RFKILL_DEV_NUM (3) |
| 32 | #define IDEAPAD_DEV_WLAN 1 | ||
| 33 | #define IDEAPAD_DEV_BLUETOOTH 2 | ||
| 34 | #define IDEAPAD_DEV_3G 3 | ||
| 35 | #define IDEAPAD_DEV_KILLSW 4 | ||
| 36 | 35 | ||
| 37 | struct ideapad_private { | 36 | struct ideapad_private { |
| 38 | acpi_handle handle; | 37 | struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; |
| 39 | struct rfkill *rfk[5]; | 38 | struct platform_device *platform_device; |
| 40 | } *ideapad_priv; | 39 | struct input_dev *inputdev; |
| 41 | |||
| 42 | static struct { | ||
| 43 | char *name; | ||
| 44 | int cfgbit; | ||
| 45 | int opcode; | ||
| 46 | int type; | ||
| 47 | } ideapad_rfk_data[] = { | ||
| 48 | { "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES }, | ||
| 49 | { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, | ||
| 50 | { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, | ||
| 51 | { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, | ||
| 52 | { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN } | ||
| 53 | }; | 40 | }; |
| 54 | 41 | ||
| 42 | static acpi_handle ideapad_handle; | ||
| 55 | static bool no_bt_rfkill; | 43 | static bool no_bt_rfkill; |
| 56 | module_param(no_bt_rfkill, bool, 0444); | 44 | module_param(no_bt_rfkill, bool, 0444); |
| 57 | MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); | 45 | MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); |
| @@ -163,17 +151,17 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) | |||
| 163 | pr_err("timeout in write_ec_cmd\n"); | 151 | pr_err("timeout in write_ec_cmd\n"); |
| 164 | return -1; | 152 | return -1; |
| 165 | } | 153 | } |
| 166 | /* the above is ACPI helpers */ | ||
| 167 | 154 | ||
| 155 | /* | ||
| 156 | * camera power | ||
| 157 | */ | ||
| 168 | static ssize_t show_ideapad_cam(struct device *dev, | 158 | static ssize_t show_ideapad_cam(struct device *dev, |
| 169 | struct device_attribute *attr, | 159 | struct device_attribute *attr, |
| 170 | char *buf) | 160 | char *buf) |
| 171 | { | 161 | { |
| 172 | struct ideapad_private *priv = dev_get_drvdata(dev); | ||
| 173 | acpi_handle handle = priv->handle; | ||
| 174 | unsigned long result; | 162 | unsigned long result; |
| 175 | 163 | ||
| 176 | if (read_ec_data(handle, 0x1D, &result)) | 164 | if (read_ec_data(ideapad_handle, 0x1D, &result)) |
| 177 | return sprintf(buf, "-1\n"); | 165 | return sprintf(buf, "-1\n"); |
| 178 | return sprintf(buf, "%lu\n", result); | 166 | return sprintf(buf, "%lu\n", result); |
| 179 | } | 167 | } |
| @@ -182,15 +170,13 @@ static ssize_t store_ideapad_cam(struct device *dev, | |||
| 182 | struct device_attribute *attr, | 170 | struct device_attribute *attr, |
| 183 | const char *buf, size_t count) | 171 | const char *buf, size_t count) |
| 184 | { | 172 | { |
| 185 | struct ideapad_private *priv = dev_get_drvdata(dev); | ||
| 186 | acpi_handle handle = priv->handle; | ||
| 187 | int ret, state; | 173 | int ret, state; |
| 188 | 174 | ||
| 189 | if (!count) | 175 | if (!count) |
| 190 | return 0; | 176 | return 0; |
| 191 | if (sscanf(buf, "%i", &state) != 1) | 177 | if (sscanf(buf, "%i", &state) != 1) |
| 192 | return -EINVAL; | 178 | return -EINVAL; |
| 193 | ret = write_ec_cmd(handle, 0x1E, state); | 179 | ret = write_ec_cmd(ideapad_handle, 0x1E, state); |
| 194 | if (ret < 0) | 180 | if (ret < 0) |
| 195 | return ret; | 181 | return ret; |
| 196 | return count; | 182 | return count; |
| @@ -198,16 +184,27 @@ static ssize_t store_ideapad_cam(struct device *dev, | |||
| 198 | 184 | ||
| 199 | static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); | 185 | static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); |
| 200 | 186 | ||
| 187 | /* | ||
| 188 | * Rfkill | ||
| 189 | */ | ||
| 190 | struct ideapad_rfk_data { | ||
| 191 | char *name; | ||
| 192 | int cfgbit; | ||
| 193 | int opcode; | ||
| 194 | int type; | ||
| 195 | }; | ||
| 196 | |||
| 197 | const struct ideapad_rfk_data ideapad_rfk_data[] = { | ||
| 198 | { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, | ||
| 199 | { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, | ||
| 200 | { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, | ||
| 201 | }; | ||
| 202 | |||
| 201 | static int ideapad_rfk_set(void *data, bool blocked) | 203 | static int ideapad_rfk_set(void *data, bool blocked) |
| 202 | { | 204 | { |
| 203 | int device = (unsigned long)data; | 205 | unsigned long opcode = (unsigned long)data; |
| 204 | 206 | ||
| 205 | if (device == IDEAPAD_DEV_KILLSW) | 207 | return write_ec_cmd(ideapad_handle, opcode, !blocked); |
| 206 | return -EINVAL; | ||
| 207 | |||
| 208 | return write_ec_cmd(ideapad_priv->handle, | ||
| 209 | ideapad_rfk_data[device].opcode, | ||
| 210 | !blocked); | ||
| 211 | } | 208 | } |
| 212 | 209 | ||
| 213 | static struct rfkill_ops ideapad_rfk_ops = { | 210 | static struct rfkill_ops ideapad_rfk_ops = { |
| @@ -217,20 +214,20 @@ static struct rfkill_ops ideapad_rfk_ops = { | |||
| 217 | static void ideapad_sync_rfk_state(struct acpi_device *adevice) | 214 | static void ideapad_sync_rfk_state(struct acpi_device *adevice) |
| 218 | { | 215 | { |
| 219 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | 216 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); |
| 220 | acpi_handle handle = priv->handle; | ||
| 221 | unsigned long hw_blocked; | 217 | unsigned long hw_blocked; |
| 222 | int i; | 218 | int i; |
| 223 | 219 | ||
| 224 | if (read_ec_data(handle, 0x23, &hw_blocked)) | 220 | if (read_ec_data(ideapad_handle, 0x23, &hw_blocked)) |
| 225 | return; | 221 | return; |
| 226 | hw_blocked = !hw_blocked; | 222 | hw_blocked = !hw_blocked; |
| 227 | 223 | ||
| 228 | for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) | 224 | for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) |
| 229 | if (priv->rfk[i]) | 225 | if (priv->rfk[i]) |
| 230 | rfkill_set_hw_state(priv->rfk[i], hw_blocked); | 226 | rfkill_set_hw_state(priv->rfk[i], hw_blocked); |
| 231 | } | 227 | } |
| 232 | 228 | ||
| 233 | static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) | 229 | static int __devinit ideapad_register_rfkill(struct acpi_device *adevice, |
| 230 | int dev) | ||
| 234 | { | 231 | { |
| 235 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | 232 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); |
| 236 | int ret; | 233 | int ret; |
| @@ -239,7 +236,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) | |||
| 239 | if (no_bt_rfkill && | 236 | if (no_bt_rfkill && |
| 240 | (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { | 237 | (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { |
| 241 | /* Force to enable bluetooth when no_bt_rfkill=1 */ | 238 | /* Force to enable bluetooth when no_bt_rfkill=1 */ |
| 242 | write_ec_cmd(ideapad_priv->handle, | 239 | write_ec_cmd(ideapad_handle, |
| 243 | ideapad_rfk_data[dev].opcode, 1); | 240 | ideapad_rfk_data[dev].opcode, 1); |
| 244 | return 0; | 241 | return 0; |
| 245 | } | 242 | } |
| @@ -250,7 +247,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) | |||
| 250 | if (!priv->rfk[dev]) | 247 | if (!priv->rfk[dev]) |
| 251 | return -ENOMEM; | 248 | return -ENOMEM; |
| 252 | 249 | ||
| 253 | if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1, | 250 | if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1, |
| 254 | &sw_blocked)) { | 251 | &sw_blocked)) { |
| 255 | rfkill_init_sw_state(priv->rfk[dev], 0); | 252 | rfkill_init_sw_state(priv->rfk[dev], 0); |
| 256 | } else { | 253 | } else { |
| @@ -266,7 +263,8 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) | |||
| 266 | return 0; | 263 | return 0; |
| 267 | } | 264 | } |
| 268 | 265 | ||
| 269 | static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) | 266 | static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice, |
| 267 | int dev) | ||
| 270 | { | 268 | { |
| 271 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | 269 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); |
| 272 | 270 | ||
| @@ -277,73 +275,177 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) | |||
| 277 | rfkill_destroy(priv->rfk[dev]); | 275 | rfkill_destroy(priv->rfk[dev]); |
| 278 | } | 276 | } |
| 279 | 277 | ||
| 278 | /* | ||
| 279 | * Platform device | ||
| 280 | */ | ||
| 281 | static struct attribute *ideapad_attributes[] = { | ||
| 282 | &dev_attr_camera_power.attr, | ||
| 283 | NULL | ||
| 284 | }; | ||
| 285 | |||
| 286 | static struct attribute_group ideapad_attribute_group = { | ||
| 287 | .attrs = ideapad_attributes | ||
| 288 | }; | ||
| 289 | |||
| 290 | static int __devinit ideapad_platform_init(struct ideapad_private *priv) | ||
| 291 | { | ||
| 292 | int result; | ||
| 293 | |||
| 294 | priv->platform_device = platform_device_alloc("ideapad", -1); | ||
| 295 | if (!priv->platform_device) | ||
| 296 | return -ENOMEM; | ||
| 297 | platform_set_drvdata(priv->platform_device, priv); | ||
| 298 | |||
| 299 | result = platform_device_add(priv->platform_device); | ||
| 300 | if (result) | ||
| 301 | goto fail_platform_device; | ||
| 302 | |||
| 303 | result = sysfs_create_group(&priv->platform_device->dev.kobj, | ||
| 304 | &ideapad_attribute_group); | ||
| 305 | if (result) | ||
| 306 | goto fail_sysfs; | ||
| 307 | return 0; | ||
| 308 | |||
| 309 | fail_sysfs: | ||
| 310 | platform_device_del(priv->platform_device); | ||
| 311 | fail_platform_device: | ||
| 312 | platform_device_put(priv->platform_device); | ||
| 313 | return result; | ||
| 314 | } | ||
| 315 | |||
| 316 | static void ideapad_platform_exit(struct ideapad_private *priv) | ||
| 317 | { | ||
| 318 | sysfs_remove_group(&priv->platform_device->dev.kobj, | ||
| 319 | &ideapad_attribute_group); | ||
| 320 | platform_device_unregister(priv->platform_device); | ||
| 321 | } | ||
| 322 | |||
| 323 | /* | ||
| 324 | * input device | ||
| 325 | */ | ||
| 326 | static const struct key_entry ideapad_keymap[] = { | ||
| 327 | { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, | ||
| 328 | { KE_KEY, 0x0D, { KEY_WLAN } }, | ||
| 329 | { KE_END, 0 }, | ||
| 330 | }; | ||
| 331 | |||
| 332 | static int __devinit ideapad_input_init(struct ideapad_private *priv) | ||
| 333 | { | ||
| 334 | struct input_dev *inputdev; | ||
| 335 | int error; | ||
| 336 | |||
| 337 | inputdev = input_allocate_device(); | ||
| 338 | if (!inputdev) { | ||
| 339 | pr_info("Unable to allocate input device\n"); | ||
| 340 | return -ENOMEM; | ||
| 341 | } | ||
| 342 | |||
| 343 | inputdev->name = "Ideapad extra buttons"; | ||
| 344 | inputdev->phys = "ideapad/input0"; | ||
| 345 | inputdev->id.bustype = BUS_HOST; | ||
| 346 | inputdev->dev.parent = &priv->platform_device->dev; | ||
| 347 | |||
| 348 | error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL); | ||
| 349 | if (error) { | ||
| 350 | pr_err("Unable to setup input device keymap\n"); | ||
| 351 | goto err_free_dev; | ||
| 352 | } | ||
| 353 | |||
| 354 | error = input_register_device(inputdev); | ||
| 355 | if (error) { | ||
| 356 | pr_err("Unable to register input device\n"); | ||
| 357 | goto err_free_keymap; | ||
| 358 | } | ||
| 359 | |||
| 360 | priv->inputdev = inputdev; | ||
| 361 | return 0; | ||
| 362 | |||
| 363 | err_free_keymap: | ||
| 364 | sparse_keymap_free(inputdev); | ||
| 365 | err_free_dev: | ||
| 366 | input_free_device(inputdev); | ||
| 367 | return error; | ||
| 368 | } | ||
| 369 | |||
| 370 | static void __devexit ideapad_input_exit(struct ideapad_private *priv) | ||
| 371 | { | ||
| 372 | sparse_keymap_free(priv->inputdev); | ||
| 373 | input_unregister_device(priv->inputdev); | ||
| 374 | priv->inputdev = NULL; | ||
| 375 | } | ||
| 376 | |||
| 377 | static void ideapad_input_report(struct ideapad_private *priv, | ||
| 378 | unsigned long scancode) | ||
| 379 | { | ||
| 380 | sparse_keymap_report_event(priv->inputdev, scancode, 1, true); | ||
| 381 | } | ||
| 382 | |||
| 383 | /* | ||
| 384 | * module init/exit | ||
| 385 | */ | ||
| 280 | static const struct acpi_device_id ideapad_device_ids[] = { | 386 | static const struct acpi_device_id ideapad_device_ids[] = { |
| 281 | { "VPC2004", 0}, | 387 | { "VPC2004", 0}, |
| 282 | { "", 0}, | 388 | { "", 0}, |
| 283 | }; | 389 | }; |
| 284 | MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); | 390 | MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); |
| 285 | 391 | ||
| 286 | static int ideapad_acpi_add(struct acpi_device *adevice) | 392 | static int __devinit ideapad_acpi_add(struct acpi_device *adevice) |
| 287 | { | 393 | { |
| 288 | int i, cfg; | 394 | int ret, i, cfg; |
| 289 | int devs_present[5]; | ||
| 290 | struct ideapad_private *priv; | 395 | struct ideapad_private *priv; |
| 291 | 396 | ||
| 292 | if (read_method_int(adevice->handle, "_CFG", &cfg)) | 397 | if (read_method_int(adevice->handle, "_CFG", &cfg)) |
| 293 | return -ENODEV; | 398 | return -ENODEV; |
| 294 | 399 | ||
| 295 | for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { | ||
| 296 | if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) | ||
| 297 | devs_present[i] = 1; | ||
| 298 | else | ||
| 299 | devs_present[i] = 0; | ||
| 300 | } | ||
| 301 | |||
| 302 | /* The hardware switch is always present */ | ||
| 303 | devs_present[IDEAPAD_DEV_KILLSW] = 1; | ||
| 304 | |||
| 305 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | 400 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
| 306 | if (!priv) | 401 | if (!priv) |
| 307 | return -ENOMEM; | 402 | return -ENOMEM; |
| 403 | dev_set_drvdata(&adevice->dev, priv); | ||
| 404 | ideapad_handle = adevice->handle; | ||
| 308 | 405 | ||
| 309 | if (devs_present[IDEAPAD_DEV_CAMERA]) { | 406 | ret = ideapad_platform_init(priv); |
| 310 | int ret = device_create_file(&adevice->dev, &dev_attr_camera_power); | 407 | if (ret) |
| 311 | if (ret) { | 408 | goto platform_failed; |
| 312 | kfree(priv); | ||
| 313 | return ret; | ||
| 314 | } | ||
| 315 | } | ||
| 316 | 409 | ||
| 317 | priv->handle = adevice->handle; | 410 | ret = ideapad_input_init(priv); |
| 318 | dev_set_drvdata(&adevice->dev, priv); | 411 | if (ret) |
| 319 | ideapad_priv = priv; | 412 | goto input_failed; |
| 320 | for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { | ||
| 321 | if (!devs_present[i]) | ||
| 322 | continue; | ||
| 323 | 413 | ||
| 324 | ideapad_register_rfkill(adevice, i); | 414 | for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { |
| 415 | if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) | ||
| 416 | ideapad_register_rfkill(adevice, i); | ||
| 417 | else | ||
| 418 | priv->rfk[i] = NULL; | ||
| 325 | } | 419 | } |
| 326 | ideapad_sync_rfk_state(adevice); | 420 | ideapad_sync_rfk_state(adevice); |
| 421 | |||
| 327 | return 0; | 422 | return 0; |
| 423 | |||
| 424 | input_failed: | ||
| 425 | ideapad_platform_exit(priv); | ||
| 426 | platform_failed: | ||
| 427 | kfree(priv); | ||
| 428 | return ret; | ||
| 328 | } | 429 | } |
| 329 | 430 | ||
| 330 | static int ideapad_acpi_remove(struct acpi_device *adevice, int type) | 431 | static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) |
| 331 | { | 432 | { |
| 332 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | 433 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); |
| 333 | int i; | 434 | int i; |
| 334 | 435 | ||
| 335 | device_remove_file(&adevice->dev, &dev_attr_camera_power); | 436 | for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) |
| 336 | |||
| 337 | for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) | ||
| 338 | ideapad_unregister_rfkill(adevice, i); | 437 | ideapad_unregister_rfkill(adevice, i); |
| 339 | 438 | ideapad_input_exit(priv); | |
| 439 | ideapad_platform_exit(priv); | ||
| 340 | dev_set_drvdata(&adevice->dev, NULL); | 440 | dev_set_drvdata(&adevice->dev, NULL); |
| 341 | kfree(priv); | 441 | kfree(priv); |
| 442 | |||
| 342 | return 0; | 443 | return 0; |
| 343 | } | 444 | } |
| 344 | 445 | ||
| 345 | static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) | 446 | static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) |
| 346 | { | 447 | { |
| 448 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
| 347 | acpi_handle handle = adevice->handle; | 449 | acpi_handle handle = adevice->handle; |
| 348 | unsigned long vpc1, vpc2, vpc_bit; | 450 | unsigned long vpc1, vpc2, vpc_bit; |
| 349 | 451 | ||
| @@ -357,6 +459,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) | |||
| 357 | if (test_bit(vpc_bit, &vpc1)) { | 459 | if (test_bit(vpc_bit, &vpc1)) { |
| 358 | if (vpc_bit == 9) | 460 | if (vpc_bit == 9) |
| 359 | ideapad_sync_rfk_state(adevice); | 461 | ideapad_sync_rfk_state(adevice); |
| 462 | else | ||
| 463 | ideapad_input_report(priv, vpc_bit); | ||
| 360 | } | 464 | } |
| 361 | } | 465 | } |
| 362 | } | 466 | } |
| @@ -371,19 +475,14 @@ static struct acpi_driver ideapad_acpi_driver = { | |||
| 371 | .owner = THIS_MODULE, | 475 | .owner = THIS_MODULE, |
| 372 | }; | 476 | }; |
| 373 | 477 | ||
| 374 | |||
| 375 | static int __init ideapad_acpi_module_init(void) | 478 | static int __init ideapad_acpi_module_init(void) |
| 376 | { | 479 | { |
| 377 | acpi_bus_register_driver(&ideapad_acpi_driver); | 480 | return acpi_bus_register_driver(&ideapad_acpi_driver); |
| 378 | |||
| 379 | return 0; | ||
| 380 | } | 481 | } |
| 381 | 482 | ||
| 382 | |||
| 383 | static void __exit ideapad_acpi_module_exit(void) | 483 | static void __exit ideapad_acpi_module_exit(void) |
| 384 | { | 484 | { |
| 385 | acpi_bus_unregister_driver(&ideapad_acpi_driver); | 485 | acpi_bus_unregister_driver(&ideapad_acpi_driver); |
| 386 | |||
| 387 | } | 486 | } |
| 388 | 487 | ||
| 389 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | 488 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); |
diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index f0b3ad13c273..1294a39373ba 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c | |||
| @@ -1474,7 +1474,7 @@ ips_gpu_turbo_enabled(struct ips_driver *ips) | |||
| 1474 | } | 1474 | } |
| 1475 | 1475 | ||
| 1476 | void | 1476 | void |
| 1477 | ips_link_to_i915_driver() | 1477 | ips_link_to_i915_driver(void) |
| 1478 | { | 1478 | { |
| 1479 | /* We can't cleanly get at the various ips_driver structs from | 1479 | /* We can't cleanly get at the various ips_driver structs from |
| 1480 | * this caller (the i915 driver), so just set a flag saying | 1480 | * this caller (the i915 driver), so just set a flag saying |
diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index e61db9dfebef..930e62762365 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c | |||
| @@ -244,7 +244,11 @@ static void pmic_irq_handler(unsigned irq, struct irq_desc *desc) | |||
| 244 | generic_handle_irq(pg->irq_base + gpio); | 244 | generic_handle_irq(pg->irq_base + gpio); |
| 245 | } | 245 | } |
| 246 | } | 246 | } |
| 247 | desc->chip->eoi(irq); | 247 | |
| 248 | if (desc->chip->irq_eoi) | ||
| 249 | desc->chip->irq_eoi(irq_get_irq_data(irq)); | ||
| 250 | else | ||
| 251 | dev_warn(pg->chip.dev, "missing EOI handler for irq %d\n", irq); | ||
| 248 | } | 252 | } |
| 249 | 253 | ||
| 250 | static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) | 254 | static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) |
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index ca35b0ce944a..1752ef006d26 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c | |||
| @@ -497,7 +497,7 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) | |||
| 497 | "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); | 497 | "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); |
| 498 | 498 | ||
| 499 | mutex_unlock(&ipclock); | 499 | mutex_unlock(&ipclock); |
| 500 | return -1; | 500 | return -EIO; |
| 501 | } | 501 | } |
| 502 | mutex_unlock(&ipclock); | 502 | mutex_unlock(&ipclock); |
| 503 | return 0; | 503 | return 0; |
| @@ -642,7 +642,7 @@ update_end: | |||
| 642 | 642 | ||
| 643 | if (status == IPC_FW_UPDATE_SUCCESS) | 643 | if (status == IPC_FW_UPDATE_SUCCESS) |
| 644 | return 0; | 644 | return 0; |
| 645 | return -1; | 645 | return -EIO; |
| 646 | } | 646 | } |
| 647 | EXPORT_SYMBOL(intel_scu_ipc_fw_update); | 647 | EXPORT_SYMBOL(intel_scu_ipc_fw_update); |
| 648 | 648 | ||
diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c new file mode 100644 index 000000000000..ba3231d0819e --- /dev/null +++ b/drivers/platform/x86/intel_scu_ipcutil.c | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | /* | ||
| 2 | * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism | ||
| 3 | * | ||
| 4 | * (C) Copyright 2008-2010 Intel Corporation | ||
| 5 | * Author: Sreedhara DS (sreedhara.ds@intel.com) | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; version 2 | ||
| 10 | * of the License. | ||
| 11 | * | ||
| 12 | * This driver provides ioctl interfaces to call intel scu ipc driver api | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/errno.h> | ||
| 18 | #include <linux/types.h> | ||
| 19 | #include <linux/fs.h> | ||
| 20 | #include <linux/fcntl.h> | ||
| 21 | #include <linux/sched.h> | ||
| 22 | #include <linux/uaccess.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/init.h> | ||
| 25 | #include <asm/intel_scu_ipc.h> | ||
| 26 | |||
| 27 | static u32 major; | ||
| 28 | |||
| 29 | #define MAX_FW_SIZE 264192 | ||
| 30 | |||
| 31 | /* ioctl commnds */ | ||
| 32 | #define INTE_SCU_IPC_REGISTER_READ 0 | ||
| 33 | #define INTE_SCU_IPC_REGISTER_WRITE 1 | ||
| 34 | #define INTE_SCU_IPC_REGISTER_UPDATE 2 | ||
| 35 | #define INTE_SCU_IPC_FW_UPDATE 0xA2 | ||
| 36 | |||
| 37 | struct scu_ipc_data { | ||
| 38 | u32 count; /* No. of registers */ | ||
| 39 | u16 addr[5]; /* Register addresses */ | ||
| 40 | u8 data[5]; /* Register data */ | ||
| 41 | u8 mask; /* Valid for read-modify-write */ | ||
| 42 | }; | ||
| 43 | |||
| 44 | /** | ||
| 45 | * scu_reg_access - implement register access ioctls | ||
| 46 | * @cmd: command we are doing (read/write/update) | ||
| 47 | * @data: kernel copy of ioctl data | ||
| 48 | * | ||
| 49 | * Allow the user to perform register accesses on the SCU via the | ||
| 50 | * kernel interface | ||
| 51 | */ | ||
| 52 | |||
| 53 | static int scu_reg_access(u32 cmd, struct scu_ipc_data *data) | ||
| 54 | { | ||
| 55 | int count = data->count; | ||
| 56 | |||
| 57 | if (count == 0 || count == 3 || count > 4) | ||
| 58 | return -EINVAL; | ||
| 59 | |||
| 60 | switch (cmd) { | ||
| 61 | case INTE_SCU_IPC_REGISTER_READ: | ||
| 62 | return intel_scu_ipc_readv(data->addr, data->data, count); | ||
| 63 | case INTE_SCU_IPC_REGISTER_WRITE: | ||
| 64 | return intel_scu_ipc_writev(data->addr, data->data, count); | ||
| 65 | case INTE_SCU_IPC_REGISTER_UPDATE: | ||
| 66 | return intel_scu_ipc_update_register(data->addr[0], | ||
| 67 | data->data[0], data->mask); | ||
| 68 | default: | ||
| 69 | return -ENOTTY; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * scu_ipc_ioctl - control ioctls for the SCU | ||
| 75 | * @fp: file handle of the SCU device | ||
| 76 | * @cmd: ioctl coce | ||
| 77 | * @arg: pointer to user passed structure | ||
| 78 | * | ||
| 79 | * Support the I/O and firmware flashing interfaces of the SCU | ||
| 80 | */ | ||
| 81 | static long scu_ipc_ioctl(struct file *fp, unsigned int cmd, | ||
| 82 | unsigned long arg) | ||
| 83 | { | ||
| 84 | int ret; | ||
| 85 | struct scu_ipc_data data; | ||
| 86 | void __user *argp = (void __user *)arg; | ||
| 87 | |||
| 88 | if (!capable(CAP_SYS_RAWIO)) | ||
| 89 | return -EPERM; | ||
| 90 | |||
| 91 | if (cmd == INTE_SCU_IPC_FW_UPDATE) { | ||
| 92 | u8 *fwbuf = kmalloc(MAX_FW_SIZE, GFP_KERNEL); | ||
| 93 | if (fwbuf == NULL) | ||
| 94 | return -ENOMEM; | ||
| 95 | if (copy_from_user(fwbuf, (u8 *)arg, MAX_FW_SIZE)) { | ||
| 96 | kfree(fwbuf); | ||
| 97 | return -EFAULT; | ||
| 98 | } | ||
| 99 | ret = intel_scu_ipc_fw_update(fwbuf, MAX_FW_SIZE); | ||
| 100 | kfree(fwbuf); | ||
| 101 | return ret; | ||
| 102 | } else { | ||
| 103 | if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data))) | ||
| 104 | return -EFAULT; | ||
| 105 | ret = scu_reg_access(cmd, &data); | ||
| 106 | if (ret < 0) | ||
| 107 | return ret; | ||
| 108 | if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data))) | ||
| 109 | return -EFAULT; | ||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | static const struct file_operations scu_ipc_fops = { | ||
| 115 | .unlocked_ioctl = scu_ipc_ioctl, | ||
| 116 | }; | ||
| 117 | |||
| 118 | static int __init ipc_module_init(void) | ||
| 119 | { | ||
| 120 | return register_chrdev(0, "intel_mid_scu", &scu_ipc_fops); | ||
| 121 | } | ||
| 122 | |||
| 123 | static void __exit ipc_module_exit(void) | ||
| 124 | { | ||
| 125 | unregister_chrdev(major, "intel_mid_scu"); | ||
| 126 | } | ||
| 127 | |||
| 128 | module_init(ipc_module_init); | ||
| 129 | module_exit(ipc_module_exit); | ||
| 130 | |||
| 131 | MODULE_LICENSE("GPL V2"); | ||
| 132 | MODULE_DESCRIPTION("Utility driver for intel scu ipc"); | ||
| 133 | MODULE_AUTHOR("Sreedhara <sreedhara.ds@intel.com>"); | ||
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index f200677851b8..b4a95bb2f232 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
| @@ -235,6 +235,7 @@ static int sony_laptop_input_index[] = { | |||
| 235 | 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ | 235 | 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ |
| 236 | -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ | 236 | -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ |
| 237 | 58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */ | 237 | 58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */ |
| 238 | 59, /* 72 SONYPI_EVENT_VENDOR_PRESSED */ | ||
| 238 | }; | 239 | }; |
| 239 | 240 | ||
| 240 | static int sony_laptop_input_keycode_map[] = { | 241 | static int sony_laptop_input_keycode_map[] = { |
| @@ -297,6 +298,7 @@ static int sony_laptop_input_keycode_map[] = { | |||
| 297 | KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ | 298 | KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ |
| 298 | KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ | 299 | KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ |
| 299 | KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */ | 300 | KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */ |
| 301 | KEY_VENDOR, /* 59 SONYPI_EVENT_VENDOR_PRESSED */ | ||
| 300 | }; | 302 | }; |
| 301 | 303 | ||
| 302 | /* release buttons after a short delay if pressed */ | 304 | /* release buttons after a short delay if pressed */ |
| @@ -894,10 +896,18 @@ static struct sony_nc_event sony_100_events[] = { | |||
| 894 | { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, | 896 | { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, |
| 895 | { 0x8C, SONYPI_EVENT_FNKEY_F12 }, | 897 | { 0x8C, SONYPI_EVENT_FNKEY_F12 }, |
| 896 | { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, | 898 | { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, |
| 899 | { 0x9d, SONYPI_EVENT_ZOOM_PRESSED }, | ||
| 900 | { 0x1d, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
| 897 | { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, | 901 | { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, |
| 898 | { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, | 902 | { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, |
| 899 | { 0xa1, SONYPI_EVENT_MEDIA_PRESSED }, | 903 | { 0xa1, SONYPI_EVENT_MEDIA_PRESSED }, |
| 900 | { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED }, | 904 | { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED }, |
| 905 | { 0xa4, SONYPI_EVENT_CD_EJECT_PRESSED }, | ||
| 906 | { 0x24, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
| 907 | { 0xa5, SONYPI_EVENT_VENDOR_PRESSED }, | ||
| 908 | { 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
| 909 | { 0xa6, SONYPI_EVENT_HELP_PRESSED }, | ||
| 910 | { 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
| 901 | { 0, 0 }, | 911 | { 0, 0 }, |
| 902 | }; | 912 | }; |
| 903 | 913 | ||
| @@ -1131,7 +1141,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, | |||
| 1131 | return err; | 1141 | return err; |
| 1132 | } | 1142 | } |
| 1133 | 1143 | ||
| 1134 | static void sony_nc_rfkill_update() | 1144 | static void sony_nc_rfkill_update(void) |
| 1135 | { | 1145 | { |
| 1136 | enum sony_nc_rfkill i; | 1146 | enum sony_nc_rfkill i; |
| 1137 | int result; | 1147 | int result; |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index e8c21994b36d..a974ca383cb9 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
| @@ -589,6 +589,7 @@ static int acpi_evalf(acpi_handle handle, | |||
| 589 | default: | 589 | default: |
| 590 | printk(TPACPI_ERR "acpi_evalf() called " | 590 | printk(TPACPI_ERR "acpi_evalf() called " |
| 591 | "with invalid format character '%c'\n", c); | 591 | "with invalid format character '%c'\n", c); |
| 592 | va_end(ap); | ||
| 592 | return 0; | 593 | return 0; |
| 593 | } | 594 | } |
| 594 | } | 595 | } |
| @@ -6345,7 +6346,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
| 6345 | "as change notification\n"); | 6346 | "as change notification\n"); |
| 6346 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | 6347 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask |
| 6347 | | TP_ACPI_HKEY_BRGHTUP_MASK | 6348 | | TP_ACPI_HKEY_BRGHTUP_MASK |
| 6348 | | TP_ACPI_HKEY_BRGHTDWN_MASK);; | 6349 | | TP_ACPI_HKEY_BRGHTDWN_MASK); |
| 6349 | return 0; | 6350 | return 0; |
| 6350 | } | 6351 | } |
| 6351 | 6352 | ||
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index aecd9a9b549f..05cc79672a8b 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
| @@ -549,21 +549,34 @@ acpi_status wmi_install_notify_handler(const char *guid, | |||
| 549 | wmi_notify_handler handler, void *data) | 549 | wmi_notify_handler handler, void *data) |
| 550 | { | 550 | { |
| 551 | struct wmi_block *block; | 551 | struct wmi_block *block; |
| 552 | acpi_status status; | 552 | acpi_status status = AE_NOT_EXIST; |
| 553 | char tmp[16], guid_input[16]; | ||
| 554 | struct list_head *p; | ||
| 553 | 555 | ||
| 554 | if (!guid || !handler) | 556 | if (!guid || !handler) |
| 555 | return AE_BAD_PARAMETER; | 557 | return AE_BAD_PARAMETER; |
| 556 | 558 | ||
| 557 | if (!find_guid(guid, &block)) | 559 | wmi_parse_guid(guid, tmp); |
| 558 | return AE_NOT_EXIST; | 560 | wmi_swap_bytes(tmp, guid_input); |
| 559 | 561 | ||
| 560 | if (block->handler && block->handler != wmi_notify_debug) | 562 | list_for_each(p, &wmi_block_list) { |
| 561 | return AE_ALREADY_ACQUIRED; | 563 | acpi_status wmi_status; |
| 564 | block = list_entry(p, struct wmi_block, list); | ||
| 562 | 565 | ||
| 563 | block->handler = handler; | 566 | if (memcmp(block->gblock.guid, guid_input, 16) == 0) { |
| 564 | block->handler_data = data; | 567 | if (block->handler && |
| 568 | block->handler != wmi_notify_debug) | ||
| 569 | return AE_ALREADY_ACQUIRED; | ||
| 565 | 570 | ||
| 566 | status = wmi_method_enable(block, 1); | 571 | block->handler = handler; |
| 572 | block->handler_data = data; | ||
| 573 | |||
| 574 | wmi_status = wmi_method_enable(block, 1); | ||
| 575 | if ((wmi_status != AE_OK) || | ||
| 576 | ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) | ||
| 577 | status = wmi_status; | ||
| 578 | } | ||
| 579 | } | ||
| 567 | 580 | ||
| 568 | return status; | 581 | return status; |
| 569 | } | 582 | } |
| @@ -577,24 +590,40 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); | |||
| 577 | acpi_status wmi_remove_notify_handler(const char *guid) | 590 | acpi_status wmi_remove_notify_handler(const char *guid) |
| 578 | { | 591 | { |
| 579 | struct wmi_block *block; | 592 | struct wmi_block *block; |
| 580 | acpi_status status = AE_OK; | 593 | acpi_status status = AE_NOT_EXIST; |
| 594 | char tmp[16], guid_input[16]; | ||
| 595 | struct list_head *p; | ||
| 581 | 596 | ||
| 582 | if (!guid) | 597 | if (!guid) |
| 583 | return AE_BAD_PARAMETER; | 598 | return AE_BAD_PARAMETER; |
| 584 | 599 | ||
| 585 | if (!find_guid(guid, &block)) | 600 | wmi_parse_guid(guid, tmp); |
| 586 | return AE_NOT_EXIST; | 601 | wmi_swap_bytes(tmp, guid_input); |
| 587 | 602 | ||
| 588 | if (!block->handler || block->handler == wmi_notify_debug) | 603 | list_for_each(p, &wmi_block_list) { |
| 589 | return AE_NULL_ENTRY; | 604 | acpi_status wmi_status; |
| 605 | block = list_entry(p, struct wmi_block, list); | ||
| 590 | 606 | ||
| 591 | if (debug_event) { | 607 | if (memcmp(block->gblock.guid, guid_input, 16) == 0) { |
| 592 | block->handler = wmi_notify_debug; | 608 | if (!block->handler || |
| 593 | } else { | 609 | block->handler == wmi_notify_debug) |
| 594 | status = wmi_method_enable(block, 0); | 610 | return AE_NULL_ENTRY; |
| 595 | block->handler = NULL; | 611 | |
| 596 | block->handler_data = NULL; | 612 | if (debug_event) { |
| 613 | block->handler = wmi_notify_debug; | ||
| 614 | status = AE_OK; | ||
| 615 | } else { | ||
| 616 | wmi_status = wmi_method_enable(block, 0); | ||
| 617 | block->handler = NULL; | ||
| 618 | block->handler_data = NULL; | ||
| 619 | if ((wmi_status != AE_OK) || | ||
| 620 | ((wmi_status == AE_OK) && | ||
| 621 | (status == AE_NOT_EXIST))) | ||
| 622 | status = wmi_status; | ||
| 623 | } | ||
| 624 | } | ||
| 597 | } | 625 | } |
| 626 | |||
| 598 | return status; | 627 | return status; |
| 599 | } | 628 | } |
| 600 | EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); | 629 | EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); |
| @@ -705,22 +734,11 @@ static struct class wmi_class = { | |||
| 705 | .dev_attrs = wmi_dev_attrs, | 734 | .dev_attrs = wmi_dev_attrs, |
| 706 | }; | 735 | }; |
| 707 | 736 | ||
| 708 | static struct wmi_block *wmi_create_device(const struct guid_block *gblock, | 737 | static int wmi_create_device(const struct guid_block *gblock, |
| 709 | acpi_handle handle) | 738 | struct wmi_block *wblock, acpi_handle handle) |
| 710 | { | 739 | { |
| 711 | struct wmi_block *wblock; | ||
| 712 | int error; | ||
| 713 | char guid_string[37]; | 740 | char guid_string[37]; |
| 714 | 741 | ||
| 715 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | ||
| 716 | if (!wblock) { | ||
| 717 | error = -ENOMEM; | ||
| 718 | goto err_out; | ||
| 719 | } | ||
| 720 | |||
| 721 | wblock->handle = handle; | ||
| 722 | wblock->gblock = *gblock; | ||
| 723 | |||
| 724 | wblock->dev.class = &wmi_class; | 742 | wblock->dev.class = &wmi_class; |
| 725 | 743 | ||
| 726 | wmi_gtoa(gblock->guid, guid_string); | 744 | wmi_gtoa(gblock->guid, guid_string); |
| @@ -728,17 +746,7 @@ static struct wmi_block *wmi_create_device(const struct guid_block *gblock, | |||
| 728 | 746 | ||
| 729 | dev_set_drvdata(&wblock->dev, wblock); | 747 | dev_set_drvdata(&wblock->dev, wblock); |
| 730 | 748 | ||
| 731 | error = device_register(&wblock->dev); | 749 | return device_register(&wblock->dev); |
| 732 | if (error) | ||
| 733 | goto err_free; | ||
| 734 | |||
| 735 | list_add_tail(&wblock->list, &wmi_block_list); | ||
| 736 | return wblock; | ||
| 737 | |||
| 738 | err_free: | ||
| 739 | kfree(wblock); | ||
| 740 | err_out: | ||
| 741 | return ERR_PTR(error); | ||
| 742 | } | 750 | } |
| 743 | 751 | ||
| 744 | static void wmi_free_devices(void) | 752 | static void wmi_free_devices(void) |
| @@ -747,7 +755,8 @@ static void wmi_free_devices(void) | |||
| 747 | 755 | ||
| 748 | /* Delete devices for all the GUIDs */ | 756 | /* Delete devices for all the GUIDs */ |
| 749 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) | 757 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) |
| 750 | device_unregister(&wblock->dev); | 758 | if (wblock->dev.class) |
| 759 | device_unregister(&wblock->dev); | ||
| 751 | } | 760 | } |
| 752 | 761 | ||
| 753 | static bool guid_already_parsed(const char *guid_string) | 762 | static bool guid_already_parsed(const char *guid_string) |
| @@ -770,7 +779,6 @@ static acpi_status parse_wdg(acpi_handle handle) | |||
| 770 | union acpi_object *obj; | 779 | union acpi_object *obj; |
| 771 | const struct guid_block *gblock; | 780 | const struct guid_block *gblock; |
| 772 | struct wmi_block *wblock; | 781 | struct wmi_block *wblock; |
| 773 | char guid_string[37]; | ||
| 774 | acpi_status status; | 782 | acpi_status status; |
| 775 | int retval; | 783 | int retval; |
| 776 | u32 i, total; | 784 | u32 i, total; |
| @@ -792,28 +800,31 @@ static acpi_status parse_wdg(acpi_handle handle) | |||
| 792 | total = obj->buffer.length / sizeof(struct guid_block); | 800 | total = obj->buffer.length / sizeof(struct guid_block); |
| 793 | 801 | ||
| 794 | for (i = 0; i < total; i++) { | 802 | for (i = 0; i < total; i++) { |
| 803 | if (debug_dump_wdg) | ||
| 804 | wmi_dump_wdg(&gblock[i]); | ||
| 805 | |||
| 806 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | ||
| 807 | if (!wblock) | ||
| 808 | return AE_NO_MEMORY; | ||
| 809 | |||
| 810 | wblock->handle = handle; | ||
| 811 | wblock->gblock = gblock[i]; | ||
| 812 | |||
| 795 | /* | 813 | /* |
| 796 | Some WMI devices, like those for nVidia hooks, have a | 814 | Some WMI devices, like those for nVidia hooks, have a |
| 797 | duplicate GUID. It's not clear what we should do in this | 815 | duplicate GUID. It's not clear what we should do in this |
| 798 | case yet, so for now, we'll just ignore the duplicate. | 816 | case yet, so for now, we'll just ignore the duplicate |
| 799 | Anyone who wants to add support for that device can come | 817 | for device creation. |
| 800 | up with a better workaround for the mess then. | ||
| 801 | */ | 818 | */ |
| 802 | if (guid_already_parsed(gblock[i].guid) == true) { | 819 | if (!guid_already_parsed(gblock[i].guid)) { |
| 803 | wmi_gtoa(gblock[i].guid, guid_string); | 820 | retval = wmi_create_device(&gblock[i], wblock, handle); |
| 804 | pr_info("Skipping duplicate GUID %s\n", guid_string); | 821 | if (retval) { |
| 805 | continue; | 822 | wmi_free_devices(); |
| 823 | goto out_free_pointer; | ||
| 824 | } | ||
| 806 | } | 825 | } |
| 807 | 826 | ||
| 808 | if (debug_dump_wdg) | 827 | list_add_tail(&wblock->list, &wmi_block_list); |
| 809 | wmi_dump_wdg(&gblock[i]); | ||
| 810 | |||
| 811 | wblock = wmi_create_device(&gblock[i], handle); | ||
| 812 | if (IS_ERR(wblock)) { | ||
| 813 | retval = PTR_ERR(wblock); | ||
| 814 | wmi_free_devices(); | ||
| 815 | break; | ||
| 816 | } | ||
| 817 | 828 | ||
| 818 | if (debug_event) { | 829 | if (debug_event) { |
| 819 | wblock->handler = wmi_notify_debug; | 830 | wblock->handler = wmi_notify_debug; |
diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h index 4f95c1aac2fd..0e6dc3891942 100644 --- a/include/linux/sonypi.h +++ b/include/linux/sonypi.h | |||
| @@ -112,6 +112,7 @@ | |||
| 112 | #define SONYPI_EVENT_VOLUME_DEC_PRESSED 70 | 112 | #define SONYPI_EVENT_VOLUME_DEC_PRESSED 70 |
| 113 | #define SONYPI_EVENT_BRIGHTNESS_PRESSED 71 | 113 | #define SONYPI_EVENT_BRIGHTNESS_PRESSED 71 |
| 114 | #define SONYPI_EVENT_MEDIA_PRESSED 72 | 114 | #define SONYPI_EVENT_MEDIA_PRESSED 72 |
| 115 | #define SONYPI_EVENT_VENDOR_PRESSED 73 | ||
| 115 | 116 | ||
| 116 | /* get/set brightness */ | 117 | /* get/set brightness */ |
| 117 | #define SONYPI_IOCGBRT _IOR('v', 0, __u8) | 118 | #define SONYPI_IOCGBRT _IOR('v', 0, __u8) |
