diff options
Diffstat (limited to 'drivers/platform')
26 files changed, 6493 insertions, 2654 deletions
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 9652c3fe7f5e..8390dca2b4e1 100644 --- a/drivers/platform/Kconfig +++ b/drivers/platform/Kconfig | |||
@@ -1,5 +1,3 @@ | |||
1 | # drivers/platform/Kconfig | ||
2 | |||
3 | if X86 | 1 | if X86 |
4 | source "drivers/platform/x86/Kconfig" | 2 | source "drivers/platform/x86/Kconfig" |
5 | endif | 3 | endif |
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 55ca39dea42e..6c3320d75055 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -59,6 +59,8 @@ config ASUS_LAPTOP | |||
59 | select NEW_LEDS | 59 | select NEW_LEDS |
60 | select BACKLIGHT_CLASS_DEVICE | 60 | select BACKLIGHT_CLASS_DEVICE |
61 | depends on INPUT | 61 | depends on INPUT |
62 | depends on RFKILL || RFKILL = n | ||
63 | select INPUT_SPARSEKMAP | ||
62 | ---help--- | 64 | ---help--- |
63 | This is the new Linux driver for Asus laptops. It may also support some | 65 | This is the new Linux driver for Asus laptops. It may also support some |
64 | MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate | 66 | MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate |
@@ -79,6 +81,7 @@ config DELL_LAPTOP | |||
79 | depends on BACKLIGHT_CLASS_DEVICE | 81 | depends on BACKLIGHT_CLASS_DEVICE |
80 | depends on RFKILL || RFKILL = n | 82 | depends on RFKILL || RFKILL = n |
81 | depends on POWER_SUPPLY | 83 | depends on POWER_SUPPLY |
84 | depends on SERIO_I8042 | ||
82 | default n | 85 | default n |
83 | ---help--- | 86 | ---help--- |
84 | This driver adds support for rfkill and backlight control to Dell | 87 | This driver adds support for rfkill and backlight control to Dell |
@@ -147,6 +150,7 @@ config MSI_LAPTOP | |||
147 | tristate "MSI Laptop Extras" | 150 | tristate "MSI Laptop Extras" |
148 | depends on ACPI | 151 | depends on ACPI |
149 | depends on BACKLIGHT_CLASS_DEVICE | 152 | depends on BACKLIGHT_CLASS_DEVICE |
153 | depends on RFKILL | ||
150 | ---help--- | 154 | ---help--- |
151 | This is a driver for laptops built by MSI (MICRO-STAR | 155 | This is a driver for laptops built by MSI (MICRO-STAR |
152 | INTERNATIONAL): | 156 | INTERNATIONAL): |
@@ -176,6 +180,7 @@ config COMPAL_LAPTOP | |||
176 | tristate "Compal Laptop Extras" | 180 | tristate "Compal Laptop Extras" |
177 | depends on ACPI | 181 | depends on ACPI |
178 | depends on BACKLIGHT_CLASS_DEVICE | 182 | depends on BACKLIGHT_CLASS_DEVICE |
183 | depends on RFKILL | ||
179 | ---help--- | 184 | ---help--- |
180 | This is a driver for laptops built by Compal: | 185 | This is a driver for laptops built by Compal: |
181 | 186 | ||
@@ -231,8 +236,36 @@ config THINKPAD_ACPI | |||
231 | 236 | ||
232 | This driver was formerly known as ibm-acpi. | 237 | This driver was formerly known as ibm-acpi. |
233 | 238 | ||
239 | Extra functionality will be available if the rfkill (CONFIG_RFKILL) | ||
240 | and/or ALSA (CONFIG_SND) subsystems are available in the kernel. | ||
241 | Note that if you want ThinkPad-ACPI to be built-in instead of | ||
242 | modular, ALSA and rfkill will also have to be built-in. | ||
243 | |||
234 | If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. | 244 | If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. |
235 | 245 | ||
246 | config THINKPAD_ACPI_ALSA_SUPPORT | ||
247 | bool "Console audio control ALSA interface" | ||
248 | depends on THINKPAD_ACPI | ||
249 | depends on SND | ||
250 | depends on SND = y || THINKPAD_ACPI = SND | ||
251 | default y | ||
252 | ---help--- | ||
253 | Enables monitoring of the built-in console audio output control | ||
254 | (headphone and speakers), which is operated by the mute and (in | ||
255 | some ThinkPad models) volume hotkeys. | ||
256 | |||
257 | If this option is enabled, ThinkPad-ACPI will export an ALSA card | ||
258 | with a single read-only mixer control, which should be used for | ||
259 | on-screen-display feedback purposes by the Desktop Environment. | ||
260 | |||
261 | Optionally, the driver will also allow software control (the | ||
262 | ALSA mixer will be made read-write). Please refer to the driver | ||
263 | documentation for details. | ||
264 | |||
265 | All IBM models have both volume and mute control. Newer Lenovo | ||
266 | models only have mute control (the volume hotkeys are just normal | ||
267 | keys and volume control is done through the main HDA mixer). | ||
268 | |||
236 | config THINKPAD_ACPI_DEBUGFACILITIES | 269 | config THINKPAD_ACPI_DEBUGFACILITIES |
237 | bool "Maintainer debug facilities" | 270 | bool "Maintainer debug facilities" |
238 | depends on THINKPAD_ACPI | 271 | depends on THINKPAD_ACPI |
@@ -291,9 +324,15 @@ config THINKPAD_ACPI_VIDEO | |||
291 | server running, phase of the moon, and the current mood of | 324 | server running, phase of the moon, and the current mood of |
292 | Schroedinger's cat. If you can use X.org's RandR to control | 325 | Schroedinger's cat. If you can use X.org's RandR to control |
293 | your ThinkPad's video output ports instead of this feature, | 326 | your ThinkPad's video output ports instead of this feature, |
294 | don't think twice: do it and say N here to save some memory. | 327 | don't think twice: do it and say N here to save memory and avoid |
328 | bad interactions with X.org. | ||
329 | |||
330 | NOTE: access to this feature is limited to processes with the | ||
331 | CAP_SYS_ADMIN capability, to avoid local DoS issues in platforms | ||
332 | where it interacts badly with X.org. | ||
295 | 333 | ||
296 | If you are not sure, say Y here. | 334 | If you are not sure, say Y here but do try to check if you could |
335 | be using X.org RandR instead. | ||
297 | 336 | ||
298 | config THINKPAD_ACPI_HOTKEY_POLL | 337 | config THINKPAD_ACPI_HOTKEY_POLL |
299 | bool "Support NVRAM polling for hot keys" | 338 | bool "Support NVRAM polling for hot keys" |
@@ -334,6 +373,9 @@ config EEEPC_LAPTOP | |||
334 | depends on HOTPLUG_PCI | 373 | depends on HOTPLUG_PCI |
335 | select BACKLIGHT_CLASS_DEVICE | 374 | select BACKLIGHT_CLASS_DEVICE |
336 | select HWMON | 375 | select HWMON |
376 | select LEDS_CLASS | ||
377 | select NEW_LEDS | ||
378 | select INPUT_SPARSEKMAP | ||
337 | ---help--- | 379 | ---help--- |
338 | This driver supports the Fn-Fx keys on Eee PC laptops. | 380 | This driver supports the Fn-Fx keys on Eee PC laptops. |
339 | 381 | ||
@@ -343,6 +385,17 @@ config EEEPC_LAPTOP | |||
343 | 385 | ||
344 | If you have an Eee PC laptop, say Y or M here. | 386 | If you have an Eee PC laptop, say Y or M here. |
345 | 387 | ||
388 | config EEEPC_WMI | ||
389 | tristate "Eee PC WMI Hotkey Driver (EXPERIMENTAL)" | ||
390 | depends on ACPI_WMI | ||
391 | depends on INPUT | ||
392 | depends on EXPERIMENTAL | ||
393 | select INPUT_SPARSEKMAP | ||
394 | ---help--- | ||
395 | Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. | ||
396 | |||
397 | To compile this driver as a module, choose M here: the module will | ||
398 | be called eeepc-wmi. | ||
346 | 399 | ||
347 | config ACPI_WMI | 400 | config ACPI_WMI |
348 | tristate "WMI" | 401 | tristate "WMI" |
@@ -365,6 +418,18 @@ config ACPI_WMI | |||
365 | It is safe to enable this driver even if your DSDT doesn't define | 418 | It is safe to enable this driver even if your DSDT doesn't define |
366 | any ACPI-WMI devices. | 419 | any ACPI-WMI devices. |
367 | 420 | ||
421 | config MSI_WMI | ||
422 | tristate "MSI WMI extras" | ||
423 | depends on ACPI_WMI | ||
424 | depends on INPUT | ||
425 | depends on BACKLIGHT_CLASS_DEVICE | ||
426 | select INPUT_SPARSEKMAP | ||
427 | help | ||
428 | Say Y here if you want to support WMI-based hotkeys on MSI laptops. | ||
429 | |||
430 | To compile this driver as a module, choose M here: the module will | ||
431 | be called msi-wmi. | ||
432 | |||
368 | config ACPI_ASUS | 433 | config ACPI_ASUS |
369 | tristate "ASUS/Medion Laptop Extras (DEPRECATED)" | 434 | tristate "ASUS/Medion Laptop Extras (DEPRECATED)" |
370 | depends on ACPI | 435 | depends on ACPI |
@@ -435,4 +500,31 @@ config ACPI_TOSHIBA | |||
435 | 500 | ||
436 | If you have a legacy free Toshiba laptop (such as the Libretto L1 | 501 | If you have a legacy free Toshiba laptop (such as the Libretto L1 |
437 | series), say Y. | 502 | series), say Y. |
503 | |||
504 | config TOSHIBA_BT_RFKILL | ||
505 | tristate "Toshiba Bluetooth RFKill switch support" | ||
506 | depends on ACPI | ||
507 | ---help--- | ||
508 | This driver adds support for Bluetooth events for the RFKill | ||
509 | switch on modern Toshiba laptops with full ACPI support and | ||
510 | an RFKill switch. | ||
511 | |||
512 | This driver handles RFKill events for the TOS6205 Bluetooth, | ||
513 | and re-enables it when the switch is set back to the 'on' | ||
514 | position. | ||
515 | |||
516 | If you have a modern Toshiba laptop with a Bluetooth and an | ||
517 | RFKill switch (such as the Portege R500), say Y. | ||
518 | |||
519 | config ACPI_CMPC | ||
520 | tristate "CMPC Laptop Extras" | ||
521 | depends on X86 && ACPI | ||
522 | select INPUT | ||
523 | select BACKLIGHT_CLASS_DEVICE | ||
524 | default n | ||
525 | help | ||
526 | Support for Intel Classmate PC ACPI devices, including some | ||
527 | keys as input device, backlight device, tablet and accelerometer | ||
528 | devices. | ||
529 | |||
438 | endif # X86_PLATFORM_DEVICES | 530 | endif # X86_PLATFORM_DEVICES |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index d1c16210a512..a906490e3530 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
@@ -4,7 +4,9 @@ | |||
4 | # | 4 | # |
5 | obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o | 5 | obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o |
6 | obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o | 6 | obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o |
7 | obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o | ||
7 | obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o | 8 | obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o |
9 | obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o | ||
8 | obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o | 10 | obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o |
9 | obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o | 11 | obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o |
10 | obj-$(CONFIG_DELL_WMI) += dell-wmi.o | 12 | obj-$(CONFIG_DELL_WMI) += dell-wmi.o |
@@ -18,6 +20,8 @@ obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o | |||
18 | obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o | 20 | obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o |
19 | obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o | 21 | obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o |
20 | obj-$(CONFIG_ACPI_WMI) += wmi.o | 22 | obj-$(CONFIG_ACPI_WMI) += wmi.o |
23 | obj-$(CONFIG_MSI_WMI) += msi-wmi.o | ||
21 | obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o | 24 | obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o |
22 | obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o | 25 | obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o |
23 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o | 26 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o |
27 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o | ||
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 454970d2d701..1ea6c434d330 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/rfkill.h> | 36 | #include <linux/rfkill.h> |
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 | 40 | ||
40 | #include <acpi/acpi_drivers.h> | 41 | #include <acpi/acpi_drivers.h> |
41 | 42 | ||
@@ -96,9 +97,6 @@ struct acer_quirks { | |||
96 | MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); | 97 | MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); |
97 | MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); | 98 | MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); |
98 | 99 | ||
99 | /* Temporary workaround until the WMI sysfs interface goes in */ | ||
100 | MODULE_ALIAS("dmi:*:*Acer*:*:"); | ||
101 | |||
102 | /* | 100 | /* |
103 | * Interface capability flags | 101 | * Interface capability flags |
104 | */ | 102 | */ |
@@ -925,9 +923,13 @@ static struct backlight_ops acer_bl_ops = { | |||
925 | 923 | ||
926 | static int __devinit acer_backlight_init(struct device *dev) | 924 | static int __devinit acer_backlight_init(struct device *dev) |
927 | { | 925 | { |
926 | struct backlight_properties props; | ||
928 | struct backlight_device *bd; | 927 | struct backlight_device *bd; |
929 | 928 | ||
930 | bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops); | 929 | memset(&props, 0, sizeof(struct backlight_properties)); |
930 | props.max_brightness = max_brightness; | ||
931 | bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops, | ||
932 | &props); | ||
931 | if (IS_ERR(bd)) { | 933 | if (IS_ERR(bd)) { |
932 | printk(ACER_ERR "Could not register Acer backlight device\n"); | 934 | printk(ACER_ERR "Could not register Acer backlight device\n"); |
933 | acer_backlight_device = NULL; | 935 | acer_backlight_device = NULL; |
@@ -937,8 +939,7 @@ static int __devinit acer_backlight_init(struct device *dev) | |||
937 | acer_backlight_device = bd; | 939 | acer_backlight_device = bd; |
938 | 940 | ||
939 | bd->props.power = FB_BLANK_UNBLANK; | 941 | bd->props.power = FB_BLANK_UNBLANK; |
940 | bd->props.brightness = max_brightness; | 942 | bd->props.brightness = read_brightness(bd); |
941 | bd->props.max_brightness = max_brightness; | ||
942 | backlight_update_status(bd); | 943 | backlight_update_status(bd); |
943 | return 0; | 944 | return 0; |
944 | } | 945 | } |
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index ab64522aaa64..7b2384d674d0 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c | |||
@@ -52,7 +52,7 @@ | |||
52 | */ | 52 | */ |
53 | #undef START_IN_KERNEL_MODE | 53 | #undef START_IN_KERNEL_MODE |
54 | 54 | ||
55 | #define DRV_VER "0.5.18" | 55 | #define DRV_VER "0.5.22" |
56 | 56 | ||
57 | /* | 57 | /* |
58 | * According to the Atom N270 datasheet, | 58 | * According to the Atom N270 datasheet, |
@@ -112,12 +112,14 @@ module_param_string(force_product, force_product, 16, 0); | |||
112 | MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check"); | 112 | MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check"); |
113 | 113 | ||
114 | /* | 114 | /* |
115 | * cmd_off: to switch the fan completely off / to check if the fan is off | 115 | * cmd_off: to switch the fan completely off |
116 | * chk_off: to check if the fan is off | ||
116 | * cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then | 117 | * cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then |
117 | * the fan speed depending on the temperature | 118 | * the fan speed depending on the temperature |
118 | */ | 119 | */ |
119 | struct fancmd { | 120 | struct fancmd { |
120 | u8 cmd_off; | 121 | u8 cmd_off; |
122 | u8 chk_off; | ||
121 | u8 cmd_auto; | 123 | u8 cmd_auto; |
122 | }; | 124 | }; |
123 | 125 | ||
@@ -134,32 +136,47 @@ struct bios_settings_t { | |||
134 | /* Register addresses and values for different BIOS versions */ | 136 | /* Register addresses and values for different BIOS versions */ |
135 | static const struct bios_settings_t bios_tbl[] = { | 137 | static const struct bios_settings_t bios_tbl[] = { |
136 | /* AOA110 */ | 138 | /* AOA110 */ |
137 | {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00} }, | 139 | {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x1f, 0x00} }, |
138 | {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00} }, | 140 | {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x1f, 0x00} }, |
139 | {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00} }, | 141 | {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, |
140 | {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00} }, | 142 | {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, |
141 | {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00} }, | 143 | {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, |
142 | {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00} }, | 144 | {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, |
143 | {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00} }, | 145 | {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x21, 0x00} }, |
144 | {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} }, | 146 | {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x21, 0x00} }, |
145 | {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} }, | 147 | {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x21, 0x00} }, |
146 | /* AOA150 */ | 148 | /* AOA150 */ |
147 | {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x00} }, | 149 | {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x20, 0x00} }, |
148 | {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00} }, | 150 | {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x20, 0x00} }, |
149 | {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00} }, | 151 | {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x20, 0x00} }, |
150 | {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00} }, | 152 | {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x20, 0x00} }, |
151 | {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00} }, | 153 | {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x20, 0x00} }, |
152 | {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} }, | 154 | {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x20, 0x00} }, |
153 | {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} }, | 155 | {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x20, 0x00} }, |
154 | {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} }, | 156 | {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x20, 0x00} }, |
155 | /* special BIOS / other */ | 157 | /* Acer 1410 */ |
156 | {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, | 158 | {"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, |
157 | {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00} }, | 159 | {"Acer", "Aspire 1410", "v1.3303", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, |
158 | {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} }, | 160 | /* Acer 1810xx */ |
159 | {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, | 161 | {"Acer", "Aspire 1810TZ", "v0.3120", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, |
160 | {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} }, | 162 | {"Acer", "Aspire 1810T", "v0.3120", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, |
163 | {"Acer", "Aspire 1810T", "v1.3303", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | ||
164 | {"Acer", "Aspire 1810TZ", "v1.3303", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | ||
165 | /* Gateway */ | ||
166 | {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x21, 0x00} }, | ||
167 | {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x20, 0x00} }, | ||
168 | {"Gateway", "LT31", "v1.3103", 0x55, 0x58, {0x10, 0x0f, 0x00} }, | ||
169 | {"Gateway", "LT31", "v1.3201", 0x55, 0x58, {0x10, 0x0f, 0x00} }, | ||
170 | {"Gateway", "LT31", "v1.3302", 0x55, 0x58, {0x10, 0x0f, 0x00} }, | ||
171 | /* Packard Bell */ | ||
172 | {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x21, 0x00} }, | ||
173 | {"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x20, 0x00} }, | ||
174 | {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x21, 0x00} }, | ||
175 | {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x20, 0x00} }, | ||
176 | {"Packard Bell", "DOTMU", "v1.3303", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | ||
177 | {"Packard Bell", "DOTMU", "v0.3120", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | ||
161 | /* pewpew-terminator */ | 178 | /* pewpew-terminator */ |
162 | {"", "", "", 0, 0, {0, 0} } | 179 | {"", "", "", 0, 0, {0, 0, 0} } |
163 | }; | 180 | }; |
164 | 181 | ||
165 | static const struct bios_settings_t *bios_cfg __read_mostly; | 182 | static const struct bios_settings_t *bios_cfg __read_mostly; |
@@ -183,7 +200,7 @@ static int acerhdf_get_fanstate(int *state) | |||
183 | if (ec_read(bios_cfg->fanreg, &fan)) | 200 | if (ec_read(bios_cfg->fanreg, &fan)) |
184 | return -EINVAL; | 201 | return -EINVAL; |
185 | 202 | ||
186 | if (fan != bios_cfg->cmd.cmd_off) | 203 | if (fan != bios_cfg->cmd.chk_off) |
187 | *state = ACERHDF_FAN_AUTO; | 204 | *state = ACERHDF_FAN_AUTO; |
188 | else | 205 | else |
189 | *state = ACERHDF_FAN_OFF; | 206 | *state = ACERHDF_FAN_OFF; |
@@ -460,7 +477,7 @@ static int acerhdf_remove(struct platform_device *device) | |||
460 | return 0; | 477 | return 0; |
461 | } | 478 | } |
462 | 479 | ||
463 | static struct dev_pm_ops acerhdf_pm_ops = { | 480 | static const struct dev_pm_ops acerhdf_pm_ops = { |
464 | .suspend = acerhdf_suspend, | 481 | .suspend = acerhdf_suspend, |
465 | .freeze = acerhdf_suspend, | 482 | .freeze = acerhdf_suspend, |
466 | }; | 483 | }; |
@@ -475,13 +492,26 @@ static struct platform_driver acerhdf_driver = { | |||
475 | .remove = acerhdf_remove, | 492 | .remove = acerhdf_remove, |
476 | }; | 493 | }; |
477 | 494 | ||
495 | /* checks if str begins with start */ | ||
496 | static int str_starts_with(const char *str, const char *start) | ||
497 | { | ||
498 | unsigned long str_len = 0, start_len = 0; | ||
499 | |||
500 | str_len = strlen(str); | ||
501 | start_len = strlen(start); | ||
502 | |||
503 | if (str_len >= start_len && | ||
504 | !strncmp(str, start, start_len)) | ||
505 | return 1; | ||
506 | |||
507 | return 0; | ||
508 | } | ||
478 | 509 | ||
479 | /* check hardware */ | 510 | /* check hardware */ |
480 | static int acerhdf_check_hardware(void) | 511 | static int acerhdf_check_hardware(void) |
481 | { | 512 | { |
482 | char const *vendor, *version, *product; | 513 | char const *vendor, *version, *product; |
483 | int i; | 514 | const struct bios_settings_t *bt = NULL; |
484 | unsigned long prod_len = 0; | ||
485 | 515 | ||
486 | /* get BIOS data */ | 516 | /* get BIOS data */ |
487 | vendor = dmi_get_system_info(DMI_SYS_VENDOR); | 517 | vendor = dmi_get_system_info(DMI_SYS_VENDOR); |
@@ -503,20 +533,20 @@ static int acerhdf_check_hardware(void) | |||
503 | kernelmode = 0; | 533 | kernelmode = 0; |
504 | } | 534 | } |
505 | 535 | ||
506 | prod_len = strlen(product); | ||
507 | |||
508 | if (verbose) | 536 | if (verbose) |
509 | pr_info("BIOS info: %s %s, product: %s\n", | 537 | pr_info("BIOS info: %s %s, product: %s\n", |
510 | vendor, version, product); | 538 | vendor, version, product); |
511 | 539 | ||
512 | /* search BIOS version and vendor in BIOS settings table */ | 540 | /* search BIOS version and vendor in BIOS settings table */ |
513 | for (i = 0; bios_tbl[i].version[0]; i++) { | 541 | for (bt = bios_tbl; bt->vendor[0]; bt++) { |
514 | if (strlen(bios_tbl[i].product) >= prod_len && | 542 | /* |
515 | !strncmp(bios_tbl[i].product, product, | 543 | * check if actual hardware BIOS vendor, product and version |
516 | strlen(bios_tbl[i].product)) && | 544 | * IDs start with the strings of BIOS table entry |
517 | !strcmp(bios_tbl[i].vendor, vendor) && | 545 | */ |
518 | !strcmp(bios_tbl[i].version, version)) { | 546 | if (str_starts_with(vendor, bt->vendor) && |
519 | bios_cfg = &bios_tbl[i]; | 547 | str_starts_with(product, bt->product) && |
548 | str_starts_with(version, bt->version)) { | ||
549 | bios_cfg = bt; | ||
520 | break; | 550 | break; |
521 | } | 551 | } |
522 | } | 552 | } |
@@ -629,9 +659,14 @@ static void __exit acerhdf_exit(void) | |||
629 | MODULE_LICENSE("GPL"); | 659 | MODULE_LICENSE("GPL"); |
630 | MODULE_AUTHOR("Peter Feuerer"); | 660 | MODULE_AUTHOR("Peter Feuerer"); |
631 | MODULE_DESCRIPTION("Aspire One temperature and fan driver"); | 661 | MODULE_DESCRIPTION("Aspire One temperature and fan driver"); |
632 | MODULE_ALIAS("dmi:*:*Acer*:*:"); | 662 | MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:"); |
633 | MODULE_ALIAS("dmi:*:*Gateway*:*:"); | 663 | MODULE_ALIAS("dmi:*:*Acer*:pnAspire 1410*:"); |
634 | MODULE_ALIAS("dmi:*:*Packard Bell*:*:"); | 664 | MODULE_ALIAS("dmi:*:*Acer*:pnAspire 1810*:"); |
665 | MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:"); | ||
666 | MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:"); | ||
667 | MODULE_ALIAS("dmi:*:*Packard Bell*:pnAOA*:"); | ||
668 | MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOA*:"); | ||
669 | MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOTMU*:"); | ||
635 | 670 | ||
636 | module_init(acerhdf_init); | 671 | module_init(acerhdf_init); |
637 | module_exit(acerhdf_exit); | 672 | module_exit(acerhdf_exit); |
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index b39d2bb3e75b..efe8f6388906 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c | |||
@@ -45,58 +45,24 @@ | |||
45 | #include <linux/fb.h> | 45 | #include <linux/fb.h> |
46 | #include <linux/leds.h> | 46 | #include <linux/leds.h> |
47 | #include <linux/platform_device.h> | 47 | #include <linux/platform_device.h> |
48 | #include <linux/uaccess.h> | ||
49 | #include <linux/input.h> | ||
50 | #include <linux/input/sparse-keymap.h> | ||
51 | #include <linux/rfkill.h> | ||
52 | #include <linux/slab.h> | ||
48 | #include <acpi/acpi_drivers.h> | 53 | #include <acpi/acpi_drivers.h> |
49 | #include <acpi/acpi_bus.h> | 54 | #include <acpi/acpi_bus.h> |
50 | #include <asm/uaccess.h> | ||
51 | #include <linux/input.h> | ||
52 | |||
53 | #define ASUS_LAPTOP_VERSION "0.42" | ||
54 | |||
55 | #define ASUS_HOTK_NAME "Asus Laptop Support" | ||
56 | #define ASUS_HOTK_CLASS "hotkey" | ||
57 | #define ASUS_HOTK_DEVICE_NAME "Hotkey" | ||
58 | #define ASUS_HOTK_FILE KBUILD_MODNAME | ||
59 | #define ASUS_HOTK_PREFIX "\\_SB.ATKD." | ||
60 | 55 | ||
56 | #define ASUS_LAPTOP_VERSION "0.42" | ||
61 | 57 | ||
62 | /* | 58 | #define ASUS_LAPTOP_NAME "Asus Laptop Support" |
63 | * Some events we use, same for all Asus | 59 | #define ASUS_LAPTOP_CLASS "hotkey" |
64 | */ | 60 | #define ASUS_LAPTOP_DEVICE_NAME "Hotkey" |
65 | #define ATKD_BR_UP 0x10 | 61 | #define ASUS_LAPTOP_FILE KBUILD_MODNAME |
66 | #define ATKD_BR_DOWN 0x20 | 62 | #define ASUS_LAPTOP_PREFIX "\\_SB.ATKD." |
67 | #define ATKD_LCD_ON 0x33 | ||
68 | #define ATKD_LCD_OFF 0x34 | ||
69 | |||
70 | /* | ||
71 | * Known bits returned by \_SB.ATKD.HWRS | ||
72 | */ | ||
73 | #define WL_HWRS 0x80 | ||
74 | #define BT_HWRS 0x100 | ||
75 | |||
76 | /* | ||
77 | * Flags for hotk status | ||
78 | * WL_ON and BT_ON are also used for wireless_status() | ||
79 | */ | ||
80 | #define WL_ON 0x01 /* internal Wifi */ | ||
81 | #define BT_ON 0x02 /* internal Bluetooth */ | ||
82 | #define MLED_ON 0x04 /* mail LED */ | ||
83 | #define TLED_ON 0x08 /* touchpad LED */ | ||
84 | #define RLED_ON 0x10 /* Record LED */ | ||
85 | #define PLED_ON 0x20 /* Phone LED */ | ||
86 | #define GLED_ON 0x40 /* Gaming LED */ | ||
87 | #define LCD_ON 0x80 /* LCD backlight */ | ||
88 | #define GPS_ON 0x100 /* GPS */ | ||
89 | #define KEY_ON 0x200 /* Keyboard backlight */ | ||
90 | |||
91 | #define ASUS_LOG ASUS_HOTK_FILE ": " | ||
92 | #define ASUS_ERR KERN_ERR ASUS_LOG | ||
93 | #define ASUS_WARNING KERN_WARNING ASUS_LOG | ||
94 | #define ASUS_NOTICE KERN_NOTICE ASUS_LOG | ||
95 | #define ASUS_INFO KERN_INFO ASUS_LOG | ||
96 | #define ASUS_DEBUG KERN_DEBUG ASUS_LOG | ||
97 | 63 | ||
98 | MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary"); | 64 | MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary"); |
99 | MODULE_DESCRIPTION(ASUS_HOTK_NAME); | 65 | MODULE_DESCRIPTION(ASUS_LAPTOP_NAME); |
100 | MODULE_LICENSE("GPL"); | 66 | MODULE_LICENSE("GPL"); |
101 | 67 | ||
102 | /* | 68 | /* |
@@ -113,215 +79,209 @@ static uint wapf = 1; | |||
113 | module_param(wapf, uint, 0644); | 79 | module_param(wapf, uint, 0644); |
114 | MODULE_PARM_DESC(wapf, "WAPF value"); | 80 | MODULE_PARM_DESC(wapf, "WAPF value"); |
115 | 81 | ||
116 | #define ASUS_HANDLE(object, paths...) \ | 82 | static int wlan_status = 1; |
117 | static acpi_handle object##_handle = NULL; \ | 83 | static int bluetooth_status = 1; |
118 | static char *object##_paths[] = { paths } | 84 | |
85 | module_param(wlan_status, int, 0644); | ||
86 | MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot " | ||
87 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " | ||
88 | "default is 1"); | ||
89 | |||
90 | module_param(bluetooth_status, int, 0644); | ||
91 | MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " | ||
92 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " | ||
93 | "default is 1"); | ||
94 | |||
95 | /* | ||
96 | * Some events we use, same for all Asus | ||
97 | */ | ||
98 | #define ATKD_BR_UP 0x10 /* (event & ~ATKD_BR_UP) = brightness level */ | ||
99 | #define ATKD_BR_DOWN 0x20 /* (event & ~ATKD_BR_DOWN) = britghness level */ | ||
100 | #define ATKD_BR_MIN ATKD_BR_UP | ||
101 | #define ATKD_BR_MAX (ATKD_BR_DOWN | 0xF) /* 0x2f */ | ||
102 | #define ATKD_LCD_ON 0x33 | ||
103 | #define ATKD_LCD_OFF 0x34 | ||
104 | |||
105 | /* | ||
106 | * Known bits returned by \_SB.ATKD.HWRS | ||
107 | */ | ||
108 | #define WL_HWRS 0x80 | ||
109 | #define BT_HWRS 0x100 | ||
110 | |||
111 | /* | ||
112 | * Flags for hotk status | ||
113 | * WL_ON and BT_ON are also used for wireless_status() | ||
114 | */ | ||
115 | #define WL_RSTS 0x01 /* internal Wifi */ | ||
116 | #define BT_RSTS 0x02 /* internal Bluetooth */ | ||
119 | 117 | ||
120 | /* LED */ | 118 | /* LED */ |
121 | ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED"); | 119 | #define METHOD_MLED "MLED" |
122 | ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED"); | 120 | #define METHOD_TLED "TLED" |
123 | ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED"); /* W1JC */ | 121 | #define METHOD_RLED "RLED" /* W1JC */ |
124 | ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED"); /* A7J */ | 122 | #define METHOD_PLED "PLED" /* A7J */ |
125 | ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */ | 123 | #define METHOD_GLED "GLED" /* G1, G2 (probably) */ |
126 | 124 | ||
127 | /* LEDD */ | 125 | /* LEDD */ |
128 | ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM"); | 126 | #define METHOD_LEDD "SLCM" |
129 | 127 | ||
130 | /* | 128 | /* |
131 | * Bluetooth and WLAN | 129 | * Bluetooth and WLAN |
132 | * WLED and BLED are not handled like other XLED, because in some dsdt | 130 | * WLED and BLED are not handled like other XLED, because in some dsdt |
133 | * they also control the WLAN/Bluetooth device. | 131 | * they also control the WLAN/Bluetooth device. |
134 | */ | 132 | */ |
135 | ASUS_HANDLE(wl_switch, ASUS_HOTK_PREFIX "WLED"); | 133 | #define METHOD_WLAN "WLED" |
136 | ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED"); | 134 | #define METHOD_BLUETOOTH "BLED" |
137 | ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS"); /* All new models */ | 135 | #define METHOD_WL_STATUS "RSTS" |
138 | 136 | ||
139 | /* Brightness */ | 137 | /* Brightness */ |
140 | ASUS_HANDLE(brightness_set, ASUS_HOTK_PREFIX "SPLV"); | 138 | #define METHOD_BRIGHTNESS_SET "SPLV" |
141 | ASUS_HANDLE(brightness_get, ASUS_HOTK_PREFIX "GPLV"); | 139 | #define METHOD_BRIGHTNESS_GET "GPLV" |
142 | 140 | ||
143 | /* Backlight */ | 141 | /* Backlight */ |
144 | ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */ | 142 | static acpi_handle lcd_switch_handle; |
145 | "\\_SB.PCI0.ISA.EC0._Q10", /* A1x */ | 143 | static char *lcd_switch_paths[] = { |
146 | "\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */ | 144 | "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */ |
147 | "\\_SB.PCI0.PX40.EC0.Q10", /* M1A */ | 145 | "\\_SB.PCI0.ISA.EC0._Q10", /* A1x */ |
148 | "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */ | 146 | "\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */ |
149 | "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */ | 147 | "\\_SB.PCI0.PX40.EC0.Q10", /* M1A */ |
150 | "\\_SB.PCI0.PX40.Q10", /* S1x */ | 148 | "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */ |
151 | "\\Q10"); /* A2x, L2D, L3D, M2E */ | 149 | "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */ |
150 | "\\_SB.PCI0.PX40.Q10", /* S1x */ | ||
151 | "\\Q10"}; /* A2x, L2D, L3D, M2E */ | ||
152 | 152 | ||
153 | /* Display */ | 153 | /* Display */ |
154 | ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP"); | 154 | #define METHOD_SWITCH_DISPLAY "SDSP" |
155 | ASUS_HANDLE(display_get, | 155 | |
156 | /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */ | 156 | static acpi_handle display_get_handle; |
157 | "\\_SB.PCI0.P0P1.VGA.GETD", | 157 | static char *display_get_paths[] = { |
158 | /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */ | 158 | /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */ |
159 | "\\_SB.PCI0.P0P2.VGA.GETD", | 159 | "\\_SB.PCI0.P0P1.VGA.GETD", |
160 | /* A6V A6Q */ | 160 | /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */ |
161 | "\\_SB.PCI0.P0P3.VGA.GETD", | 161 | "\\_SB.PCI0.P0P2.VGA.GETD", |
162 | /* A6T, A6M */ | 162 | /* A6V A6Q */ |
163 | "\\_SB.PCI0.P0PA.VGA.GETD", | 163 | "\\_SB.PCI0.P0P3.VGA.GETD", |
164 | /* L3C */ | 164 | /* A6T, A6M */ |
165 | "\\_SB.PCI0.PCI1.VGAC.NMAP", | 165 | "\\_SB.PCI0.P0PA.VGA.GETD", |
166 | /* Z96F */ | 166 | /* L3C */ |
167 | "\\_SB.PCI0.VGA.GETD", | 167 | "\\_SB.PCI0.PCI1.VGAC.NMAP", |
168 | /* A2D */ | 168 | /* Z96F */ |
169 | "\\ACTD", | 169 | "\\_SB.PCI0.VGA.GETD", |
170 | /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ | 170 | /* A2D */ |
171 | "\\ADVG", | 171 | "\\ACTD", |
172 | /* P30 */ | 172 | /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ |
173 | "\\DNXT", | 173 | "\\ADVG", |
174 | /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ | 174 | /* P30 */ |
175 | "\\INFB", | 175 | "\\DNXT", |
176 | /* A3F A6F A3N A3L M6N W3N W6A */ | 176 | /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ |
177 | "\\SSTE"); | 177 | "\\INFB", |
178 | 178 | /* A3F A6F A3N A3L M6N W3N W6A */ | |
179 | ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */ | 179 | "\\SSTE"}; |
180 | ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */ | 180 | |
181 | #define METHOD_ALS_CONTROL "ALSC" /* Z71A Z71V */ | ||
182 | #define METHOD_ALS_LEVEL "ALSL" /* Z71A Z71V */ | ||
181 | 183 | ||
182 | /* GPS */ | 184 | /* GPS */ |
183 | /* R2H use different handle for GPS on/off */ | 185 | /* R2H use different handle for GPS on/off */ |
184 | ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON"); /* R2H */ | 186 | #define METHOD_GPS_ON "SDON" |
185 | ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */ | 187 | #define METHOD_GPS_OFF "SDOF" |
186 | ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST"); | 188 | #define METHOD_GPS_STATUS "GPST" |
187 | 189 | ||
188 | /* Keyboard light */ | 190 | /* Keyboard light */ |
189 | ASUS_HANDLE(kled_set, ASUS_HOTK_PREFIX "SLKB"); | 191 | #define METHOD_KBD_LIGHT_SET "SLKB" |
190 | ASUS_HANDLE(kled_get, ASUS_HOTK_PREFIX "GLKB"); | 192 | #define METHOD_KBD_LIGHT_GET "GLKB" |
191 | 193 | ||
192 | /* | 194 | /* |
193 | * This is the main structure, we can use it to store anything interesting | 195 | * Define a specific led structure to keep the main structure clean |
194 | * about the hotk device | ||
195 | */ | 196 | */ |
196 | struct asus_hotk { | 197 | struct asus_led { |
197 | char *name; /* laptop name */ | 198 | int wk; |
198 | struct acpi_device *device; /* the device we are in */ | 199 | struct work_struct work; |
199 | acpi_handle handle; /* the handle of the hotk device */ | 200 | struct led_classdev led; |
200 | char status; /* status of the hotk, for LEDs, ... */ | 201 | struct asus_laptop *asus; |
201 | u32 ledd_status; /* status of the LED display */ | 202 | const char *method; |
202 | u8 light_level; /* light sensor level */ | ||
203 | u8 light_switch; /* light sensor switch value */ | ||
204 | u16 event_count[128]; /* count for each event TODO make this better */ | ||
205 | struct input_dev *inputdev; | ||
206 | u16 *keycode_map; | ||
207 | }; | 203 | }; |
208 | 204 | ||
209 | /* | 205 | /* |
210 | * This header is made available to allow proper configuration given model, | 206 | * This is the main structure, we can use it to store anything interesting |
211 | * revision number , ... this info cannot go in struct asus_hotk because it is | 207 | * about the hotk device |
212 | * available before the hotk | ||
213 | */ | ||
214 | static struct acpi_table_header *asus_info; | ||
215 | |||
216 | /* The actual device the driver binds to */ | ||
217 | static struct asus_hotk *hotk; | ||
218 | |||
219 | /* | ||
220 | * The hotkey driver declaration | ||
221 | */ | 208 | */ |
222 | static const struct acpi_device_id asus_device_ids[] = { | 209 | struct asus_laptop { |
223 | {"ATK0100", 0}, | 210 | char *name; /* laptop name */ |
224 | {"", 0}, | ||
225 | }; | ||
226 | MODULE_DEVICE_TABLE(acpi, asus_device_ids); | ||
227 | 211 | ||
228 | static int asus_hotk_add(struct acpi_device *device); | 212 | struct acpi_table_header *dsdt_info; |
229 | static int asus_hotk_remove(struct acpi_device *device, int type); | 213 | struct platform_device *platform_device; |
230 | static void asus_hotk_notify(struct acpi_device *device, u32 event); | 214 | struct acpi_device *device; /* the device we are in */ |
215 | struct backlight_device *backlight_device; | ||
231 | 216 | ||
232 | static struct acpi_driver asus_hotk_driver = { | 217 | struct input_dev *inputdev; |
233 | .name = ASUS_HOTK_NAME, | 218 | struct key_entry *keymap; |
234 | .class = ASUS_HOTK_CLASS, | ||
235 | .ids = asus_device_ids, | ||
236 | .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, | ||
237 | .ops = { | ||
238 | .add = asus_hotk_add, | ||
239 | .remove = asus_hotk_remove, | ||
240 | .notify = asus_hotk_notify, | ||
241 | }, | ||
242 | }; | ||
243 | 219 | ||
244 | /* The backlight device /sys/class/backlight */ | 220 | struct asus_led mled; |
245 | static struct backlight_device *asus_backlight_device; | 221 | struct asus_led tled; |
222 | struct asus_led rled; | ||
223 | struct asus_led pled; | ||
224 | struct asus_led gled; | ||
225 | struct asus_led kled; | ||
226 | struct workqueue_struct *led_workqueue; | ||
246 | 227 | ||
247 | /* | 228 | int wireless_status; |
248 | * The backlight class declaration | 229 | bool have_rsts; |
249 | */ | 230 | int lcd_state; |
250 | static int read_brightness(struct backlight_device *bd); | ||
251 | static int update_bl_status(struct backlight_device *bd); | ||
252 | static struct backlight_ops asusbl_ops = { | ||
253 | .get_brightness = read_brightness, | ||
254 | .update_status = update_bl_status, | ||
255 | }; | ||
256 | 231 | ||
257 | /* | 232 | struct rfkill *gps_rfkill; |
258 | * These functions actually update the LED's, and are called from a | ||
259 | * workqueue. By doing this as separate work rather than when the LED | ||
260 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a | ||
261 | * potentially bad time, such as a timer interrupt. | ||
262 | */ | ||
263 | static struct workqueue_struct *led_workqueue; | ||
264 | |||
265 | #define ASUS_LED(object, ledname, max) \ | ||
266 | static void object##_led_set(struct led_classdev *led_cdev, \ | ||
267 | enum led_brightness value); \ | ||
268 | static enum led_brightness object##_led_get( \ | ||
269 | struct led_classdev *led_cdev); \ | ||
270 | static void object##_led_update(struct work_struct *ignored); \ | ||
271 | static int object##_led_wk; \ | ||
272 | static DECLARE_WORK(object##_led_work, object##_led_update); \ | ||
273 | static struct led_classdev object##_led = { \ | ||
274 | .name = "asus::" ledname, \ | ||
275 | .brightness_set = object##_led_set, \ | ||
276 | .brightness_get = object##_led_get, \ | ||
277 | .max_brightness = max \ | ||
278 | } | ||
279 | 233 | ||
280 | ASUS_LED(mled, "mail", 1); | 234 | acpi_handle handle; /* the handle of the hotk device */ |
281 | ASUS_LED(tled, "touchpad", 1); | 235 | u32 ledd_status; /* status of the LED display */ |
282 | ASUS_LED(rled, "record", 1); | 236 | u8 light_level; /* light sensor level */ |
283 | ASUS_LED(pled, "phone", 1); | 237 | u8 light_switch; /* light sensor switch value */ |
284 | ASUS_LED(gled, "gaming", 1); | 238 | u16 event_count[128]; /* count for each event TODO make this better */ |
285 | ASUS_LED(kled, "kbd_backlight", 3); | 239 | u16 *keycode_map; |
286 | |||
287 | struct key_entry { | ||
288 | char type; | ||
289 | u8 code; | ||
290 | u16 keycode; | ||
291 | }; | 240 | }; |
292 | 241 | ||
293 | enum { KE_KEY, KE_END }; | 242 | static const struct key_entry asus_keymap[] = { |
294 | 243 | /* Lenovo SL Specific keycodes */ | |
295 | static struct key_entry asus_keymap[] = { | 244 | {KE_KEY, 0x02, { KEY_SCREENLOCK } }, |
296 | {KE_KEY, 0x30, KEY_VOLUMEUP}, | 245 | {KE_KEY, 0x05, { KEY_WLAN } }, |
297 | {KE_KEY, 0x31, KEY_VOLUMEDOWN}, | 246 | {KE_KEY, 0x08, { KEY_F13 } }, |
298 | {KE_KEY, 0x32, KEY_MUTE}, | 247 | {KE_KEY, 0x17, { KEY_ZOOM } }, |
299 | {KE_KEY, 0x33, KEY_SWITCHVIDEOMODE}, | 248 | {KE_KEY, 0x1f, { KEY_BATTERY } }, |
300 | {KE_KEY, 0x34, KEY_SWITCHVIDEOMODE}, | 249 | /* End of Lenovo SL Specific keycodes */ |
301 | {KE_KEY, 0x40, KEY_PREVIOUSSONG}, | 250 | {KE_KEY, 0x30, { KEY_VOLUMEUP } }, |
302 | {KE_KEY, 0x41, KEY_NEXTSONG}, | 251 | {KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, |
303 | {KE_KEY, 0x43, KEY_STOPCD}, | 252 | {KE_KEY, 0x32, { KEY_MUTE } }, |
304 | {KE_KEY, 0x45, KEY_PLAYPAUSE}, | 253 | {KE_KEY, 0x33, { KEY_SWITCHVIDEOMODE } }, |
305 | {KE_KEY, 0x4c, KEY_MEDIA}, | 254 | {KE_KEY, 0x34, { KEY_SWITCHVIDEOMODE } }, |
306 | {KE_KEY, 0x50, KEY_EMAIL}, | 255 | {KE_KEY, 0x40, { KEY_PREVIOUSSONG } }, |
307 | {KE_KEY, 0x51, KEY_WWW}, | 256 | {KE_KEY, 0x41, { KEY_NEXTSONG } }, |
308 | {KE_KEY, 0x55, KEY_CALC}, | 257 | {KE_KEY, 0x43, { KEY_STOPCD } }, |
309 | {KE_KEY, 0x5C, KEY_SCREENLOCK}, /* Screenlock */ | 258 | {KE_KEY, 0x45, { KEY_PLAYPAUSE } }, |
310 | {KE_KEY, 0x5D, KEY_WLAN}, | 259 | {KE_KEY, 0x4c, { KEY_MEDIA } }, |
311 | {KE_KEY, 0x5E, KEY_WLAN}, | 260 | {KE_KEY, 0x50, { KEY_EMAIL } }, |
312 | {KE_KEY, 0x5F, KEY_WLAN}, | 261 | {KE_KEY, 0x51, { KEY_WWW } }, |
313 | {KE_KEY, 0x60, KEY_SWITCHVIDEOMODE}, | 262 | {KE_KEY, 0x55, { KEY_CALC } }, |
314 | {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE}, | 263 | {KE_KEY, 0x5C, { KEY_SCREENLOCK } }, /* Screenlock */ |
315 | {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */ | 264 | {KE_KEY, 0x5D, { KEY_WLAN } }, |
316 | {KE_KEY, 0x82, KEY_CAMERA}, | 265 | {KE_KEY, 0x5E, { KEY_WLAN } }, |
317 | {KE_KEY, 0x8A, KEY_PROG1}, | 266 | {KE_KEY, 0x5F, { KEY_WLAN } }, |
318 | {KE_KEY, 0x95, KEY_MEDIA}, | 267 | {KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } }, |
319 | {KE_KEY, 0x99, KEY_PHONE}, | 268 | {KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, |
320 | {KE_KEY, 0xc4, KEY_KBDILLUMUP}, | 269 | {KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, |
321 | {KE_KEY, 0xc5, KEY_KBDILLUMDOWN}, | 270 | {KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, |
271 | {KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */ | ||
272 | {KE_KEY, 0x7E, { KEY_BLUETOOTH } }, | ||
273 | {KE_KEY, 0x7D, { KEY_BLUETOOTH } }, | ||
274 | {KE_KEY, 0x82, { KEY_CAMERA } }, | ||
275 | {KE_KEY, 0x88, { KEY_WLAN } }, | ||
276 | {KE_KEY, 0x8A, { KEY_PROG1 } }, | ||
277 | {KE_KEY, 0x95, { KEY_MEDIA } }, | ||
278 | {KE_KEY, 0x99, { KEY_PHONE } }, | ||
279 | {KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, | ||
280 | {KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, | ||
322 | {KE_END, 0}, | 281 | {KE_END, 0}, |
323 | }; | 282 | }; |
324 | 283 | ||
284 | |||
325 | /* | 285 | /* |
326 | * This function evaluates an ACPI method, given an int as parameter, the | 286 | * This function evaluates an ACPI method, given an int as parameter, the |
327 | * method is searched within the scope of the handle, can be NULL. The output | 287 | * method is searched within the scope of the handle, can be NULL. The output |
@@ -329,8 +289,8 @@ static struct key_entry asus_keymap[] = { | |||
329 | * | 289 | * |
330 | * returns 0 if write is successful, -1 else. | 290 | * returns 0 if write is successful, -1 else. |
331 | */ | 291 | */ |
332 | static int write_acpi_int(acpi_handle handle, const char *method, int val, | 292 | static int write_acpi_int_ret(acpi_handle handle, const char *method, int val, |
333 | struct acpi_buffer *output) | 293 | struct acpi_buffer *output) |
334 | { | 294 | { |
335 | struct acpi_object_list params; /* list of input parameters (an int) */ | 295 | struct acpi_object_list params; /* list of input parameters (an int) */ |
336 | union acpi_object in_obj; /* the only param we use */ | 296 | union acpi_object in_obj; /* the only param we use */ |
@@ -351,102 +311,82 @@ static int write_acpi_int(acpi_handle handle, const char *method, int val, | |||
351 | return -1; | 311 | return -1; |
352 | } | 312 | } |
353 | 313 | ||
354 | static int read_wireless_status(int mask) | 314 | static int write_acpi_int(acpi_handle handle, const char *method, int val) |
355 | { | 315 | { |
356 | unsigned long long status; | 316 | return write_acpi_int_ret(handle, method, val, NULL); |
357 | acpi_status rv = AE_OK; | 317 | } |
358 | 318 | ||
359 | if (!wireless_status_handle) | 319 | static int acpi_check_handle(acpi_handle handle, const char *method, |
360 | return (hotk->status & mask) ? 1 : 0; | 320 | acpi_handle *ret) |
321 | { | ||
322 | acpi_status status; | ||
361 | 323 | ||
362 | rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status); | 324 | if (method == NULL) |
363 | if (ACPI_FAILURE(rv)) | 325 | return -ENODEV; |
364 | pr_warning("Error reading Wireless status\n"); | 326 | |
365 | else | 327 | if (ret) |
366 | return (status & mask) ? 1 : 0; | 328 | status = acpi_get_handle(handle, (char *)method, |
329 | ret); | ||
330 | else { | ||
331 | acpi_handle dummy; | ||
367 | 332 | ||
368 | return (hotk->status & mask) ? 1 : 0; | 333 | status = acpi_get_handle(handle, (char *)method, |
334 | &dummy); | ||
335 | } | ||
336 | |||
337 | if (status != AE_OK) { | ||
338 | if (ret) | ||
339 | pr_warning("Error finding %s\n", method); | ||
340 | return -ENODEV; | ||
341 | } | ||
342 | return 0; | ||
369 | } | 343 | } |
370 | 344 | ||
371 | static int read_gps_status(void) | 345 | /* Generic LED function */ |
346 | static int asus_led_set(struct asus_laptop *asus, const char *method, | ||
347 | int value) | ||
372 | { | 348 | { |
373 | unsigned long long status; | 349 | if (!strcmp(method, METHOD_MLED)) |
374 | acpi_status rv = AE_OK; | 350 | value = !value; |
375 | 351 | else if (!strcmp(method, METHOD_GLED)) | |
376 | rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status); | 352 | value = !value + 1; |
377 | if (ACPI_FAILURE(rv)) | ||
378 | pr_warning("Error reading GPS status\n"); | ||
379 | else | 353 | else |
380 | return status ? 1 : 0; | 354 | value = !!value; |
381 | 355 | ||
382 | return (hotk->status & GPS_ON) ? 1 : 0; | 356 | return write_acpi_int(asus->handle, method, value); |
383 | } | 357 | } |
384 | 358 | ||
385 | /* Generic LED functions */ | 359 | /* |
386 | static int read_status(int mask) | 360 | * LEDs |
361 | */ | ||
362 | /* /sys/class/led handlers */ | ||
363 | static void asus_led_cdev_set(struct led_classdev *led_cdev, | ||
364 | enum led_brightness value) | ||
387 | { | 365 | { |
388 | /* There is a special method for both wireless devices */ | 366 | struct asus_led *led = container_of(led_cdev, struct asus_led, led); |
389 | if (mask == BT_ON || mask == WL_ON) | 367 | struct asus_laptop *asus = led->asus; |
390 | return read_wireless_status(mask); | ||
391 | else if (mask == GPS_ON) | ||
392 | return read_gps_status(); | ||
393 | 368 | ||
394 | return (hotk->status & mask) ? 1 : 0; | 369 | led->wk = !!value; |
370 | queue_work(asus->led_workqueue, &led->work); | ||
395 | } | 371 | } |
396 | 372 | ||
397 | static void write_status(acpi_handle handle, int out, int mask) | 373 | static void asus_led_cdev_update(struct work_struct *work) |
398 | { | 374 | { |
399 | hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask); | 375 | struct asus_led *led = container_of(work, struct asus_led, work); |
376 | struct asus_laptop *asus = led->asus; | ||
400 | 377 | ||
401 | switch (mask) { | 378 | asus_led_set(asus, led->method, led->wk); |
402 | case MLED_ON: | ||
403 | out = !(out & 0x1); | ||
404 | break; | ||
405 | case GLED_ON: | ||
406 | out = (out & 0x1) + 1; | ||
407 | break; | ||
408 | case GPS_ON: | ||
409 | handle = (out) ? gps_on_handle : gps_off_handle; | ||
410 | out = 0x02; | ||
411 | break; | ||
412 | default: | ||
413 | out &= 0x1; | ||
414 | break; | ||
415 | } | ||
416 | |||
417 | if (write_acpi_int(handle, NULL, out, NULL)) | ||
418 | pr_warning(" write failed %x\n", mask); | ||
419 | } | 379 | } |
420 | 380 | ||
421 | /* /sys/class/led handlers */ | 381 | static enum led_brightness asus_led_cdev_get(struct led_classdev *led_cdev) |
422 | #define ASUS_LED_HANDLER(object, mask) \ | 382 | { |
423 | static void object##_led_set(struct led_classdev *led_cdev, \ | 383 | return led_cdev->brightness; |
424 | enum led_brightness value) \ | 384 | } |
425 | { \ | ||
426 | object##_led_wk = (value > 0) ? 1 : 0; \ | ||
427 | queue_work(led_workqueue, &object##_led_work); \ | ||
428 | } \ | ||
429 | static void object##_led_update(struct work_struct *ignored) \ | ||
430 | { \ | ||
431 | int value = object##_led_wk; \ | ||
432 | write_status(object##_set_handle, value, (mask)); \ | ||
433 | } \ | ||
434 | static enum led_brightness object##_led_get( \ | ||
435 | struct led_classdev *led_cdev) \ | ||
436 | { \ | ||
437 | return led_cdev->brightness; \ | ||
438 | } | ||
439 | |||
440 | ASUS_LED_HANDLER(mled, MLED_ON); | ||
441 | ASUS_LED_HANDLER(pled, PLED_ON); | ||
442 | ASUS_LED_HANDLER(rled, RLED_ON); | ||
443 | ASUS_LED_HANDLER(tled, TLED_ON); | ||
444 | ASUS_LED_HANDLER(gled, GLED_ON); | ||
445 | 385 | ||
446 | /* | 386 | /* |
447 | * Keyboard backlight | 387 | * Keyboard backlight (also a LED) |
448 | */ | 388 | */ |
449 | static int get_kled_lvl(void) | 389 | static int asus_kled_lvl(struct asus_laptop *asus) |
450 | { | 390 | { |
451 | unsigned long long kblv; | 391 | unsigned long long kblv; |
452 | struct acpi_object_list params; | 392 | struct acpi_object_list params; |
@@ -458,75 +398,183 @@ static int get_kled_lvl(void) | |||
458 | in_obj.type = ACPI_TYPE_INTEGER; | 398 | in_obj.type = ACPI_TYPE_INTEGER; |
459 | in_obj.integer.value = 2; | 399 | in_obj.integer.value = 2; |
460 | 400 | ||
461 | rv = acpi_evaluate_integer(kled_get_handle, NULL, ¶ms, &kblv); | 401 | rv = acpi_evaluate_integer(asus->handle, METHOD_KBD_LIGHT_GET, |
402 | ¶ms, &kblv); | ||
462 | if (ACPI_FAILURE(rv)) { | 403 | if (ACPI_FAILURE(rv)) { |
463 | pr_warning("Error reading kled level\n"); | 404 | pr_warning("Error reading kled level\n"); |
464 | return 0; | 405 | return -ENODEV; |
465 | } | 406 | } |
466 | return kblv; | 407 | return kblv; |
467 | } | 408 | } |
468 | 409 | ||
469 | static int set_kled_lvl(int kblv) | 410 | static int asus_kled_set(struct asus_laptop *asus, int kblv) |
470 | { | 411 | { |
471 | if (kblv > 0) | 412 | if (kblv > 0) |
472 | kblv = (1 << 7) | (kblv & 0x7F); | 413 | kblv = (1 << 7) | (kblv & 0x7F); |
473 | else | 414 | else |
474 | kblv = 0; | 415 | kblv = 0; |
475 | 416 | ||
476 | if (write_acpi_int(kled_set_handle, NULL, kblv, NULL)) { | 417 | if (write_acpi_int(asus->handle, METHOD_KBD_LIGHT_SET, kblv)) { |
477 | pr_warning("Keyboard LED display write failed\n"); | 418 | pr_warning("Keyboard LED display write failed\n"); |
478 | return -EINVAL; | 419 | return -EINVAL; |
479 | } | 420 | } |
480 | return 0; | 421 | return 0; |
481 | } | 422 | } |
482 | 423 | ||
483 | static void kled_led_set(struct led_classdev *led_cdev, | 424 | static void asus_kled_cdev_set(struct led_classdev *led_cdev, |
484 | enum led_brightness value) | 425 | enum led_brightness value) |
485 | { | 426 | { |
486 | kled_led_wk = value; | 427 | struct asus_led *led = container_of(led_cdev, struct asus_led, led); |
487 | queue_work(led_workqueue, &kled_led_work); | 428 | struct asus_laptop *asus = led->asus; |
429 | |||
430 | led->wk = value; | ||
431 | queue_work(asus->led_workqueue, &led->work); | ||
488 | } | 432 | } |
489 | 433 | ||
490 | static void kled_led_update(struct work_struct *ignored) | 434 | static void asus_kled_cdev_update(struct work_struct *work) |
491 | { | 435 | { |
492 | set_kled_lvl(kled_led_wk); | 436 | struct asus_led *led = container_of(work, struct asus_led, work); |
437 | struct asus_laptop *asus = led->asus; | ||
438 | |||
439 | asus_kled_set(asus, led->wk); | ||
493 | } | 440 | } |
494 | 441 | ||
495 | static enum led_brightness kled_led_get(struct led_classdev *led_cdev) | 442 | static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev) |
496 | { | 443 | { |
497 | return get_kled_lvl(); | 444 | struct asus_led *led = container_of(led_cdev, struct asus_led, led); |
445 | struct asus_laptop *asus = led->asus; | ||
446 | |||
447 | return asus_kled_lvl(asus); | ||
498 | } | 448 | } |
499 | 449 | ||
500 | static int get_lcd_state(void) | 450 | static void asus_led_exit(struct asus_laptop *asus) |
501 | { | 451 | { |
502 | return read_status(LCD_ON); | 452 | if (asus->mled.led.dev) |
453 | led_classdev_unregister(&asus->mled.led); | ||
454 | if (asus->tled.led.dev) | ||
455 | led_classdev_unregister(&asus->tled.led); | ||
456 | if (asus->pled.led.dev) | ||
457 | led_classdev_unregister(&asus->pled.led); | ||
458 | if (asus->rled.led.dev) | ||
459 | led_classdev_unregister(&asus->rled.led); | ||
460 | if (asus->gled.led.dev) | ||
461 | led_classdev_unregister(&asus->gled.led); | ||
462 | if (asus->kled.led.dev) | ||
463 | led_classdev_unregister(&asus->kled.led); | ||
464 | if (asus->led_workqueue) { | ||
465 | destroy_workqueue(asus->led_workqueue); | ||
466 | asus->led_workqueue = NULL; | ||
467 | } | ||
503 | } | 468 | } |
504 | 469 | ||
505 | static int set_lcd_state(int value) | 470 | /* Ugly macro, need to fix that later */ |
471 | static int asus_led_register(struct asus_laptop *asus, | ||
472 | struct asus_led *led, | ||
473 | const char *name, const char *method) | ||
474 | { | ||
475 | struct led_classdev *led_cdev = &led->led; | ||
476 | |||
477 | if (!method || acpi_check_handle(asus->handle, method, NULL)) | ||
478 | return 0; /* Led not present */ | ||
479 | |||
480 | led->asus = asus; | ||
481 | led->method = method; | ||
482 | |||
483 | INIT_WORK(&led->work, asus_led_cdev_update); | ||
484 | led_cdev->name = name; | ||
485 | led_cdev->brightness_set = asus_led_cdev_set; | ||
486 | led_cdev->brightness_get = asus_led_cdev_get; | ||
487 | led_cdev->max_brightness = 1; | ||
488 | return led_classdev_register(&asus->platform_device->dev, led_cdev); | ||
489 | } | ||
490 | |||
491 | static int asus_led_init(struct asus_laptop *asus) | ||
492 | { | ||
493 | int r; | ||
494 | |||
495 | /* | ||
496 | * Functions that actually update the LED's are called from a | ||
497 | * workqueue. By doing this as separate work rather than when the LED | ||
498 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a | ||
499 | * potentially bad time, such as a timer interrupt. | ||
500 | */ | ||
501 | asus->led_workqueue = create_singlethread_workqueue("led_workqueue"); | ||
502 | if (!asus->led_workqueue) | ||
503 | return -ENOMEM; | ||
504 | |||
505 | r = asus_led_register(asus, &asus->mled, "asus::mail", METHOD_MLED); | ||
506 | if (r) | ||
507 | goto error; | ||
508 | r = asus_led_register(asus, &asus->tled, "asus::touchpad", METHOD_TLED); | ||
509 | if (r) | ||
510 | goto error; | ||
511 | r = asus_led_register(asus, &asus->rled, "asus::record", METHOD_RLED); | ||
512 | if (r) | ||
513 | goto error; | ||
514 | r = asus_led_register(asus, &asus->pled, "asus::phone", METHOD_PLED); | ||
515 | if (r) | ||
516 | goto error; | ||
517 | r = asus_led_register(asus, &asus->gled, "asus::gaming", METHOD_GLED); | ||
518 | if (r) | ||
519 | goto error; | ||
520 | if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL) && | ||
521 | !acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_GET, NULL)) { | ||
522 | struct asus_led *led = &asus->kled; | ||
523 | struct led_classdev *cdev = &led->led; | ||
524 | |||
525 | led->asus = asus; | ||
526 | |||
527 | INIT_WORK(&led->work, asus_kled_cdev_update); | ||
528 | cdev->name = "asus::kbd_backlight"; | ||
529 | cdev->brightness_set = asus_kled_cdev_set; | ||
530 | cdev->brightness_get = asus_kled_cdev_get; | ||
531 | cdev->max_brightness = 3; | ||
532 | r = led_classdev_register(&asus->platform_device->dev, cdev); | ||
533 | } | ||
534 | error: | ||
535 | if (r) | ||
536 | asus_led_exit(asus); | ||
537 | return r; | ||
538 | } | ||
539 | |||
540 | /* | ||
541 | * Backlight device | ||
542 | */ | ||
543 | static int asus_lcd_status(struct asus_laptop *asus) | ||
544 | { | ||
545 | return asus->lcd_state; | ||
546 | } | ||
547 | |||
548 | static int asus_lcd_set(struct asus_laptop *asus, int value) | ||
506 | { | 549 | { |
507 | int lcd = 0; | 550 | int lcd = 0; |
508 | acpi_status status = 0; | 551 | acpi_status status = 0; |
509 | 552 | ||
510 | lcd = value ? 1 : 0; | 553 | lcd = !!value; |
511 | 554 | ||
512 | if (lcd == get_lcd_state()) | 555 | if (lcd == asus_lcd_status(asus)) |
513 | return 0; | 556 | return 0; |
514 | 557 | ||
515 | if (lcd_switch_handle) { | 558 | if (!lcd_switch_handle) |
516 | status = acpi_evaluate_object(lcd_switch_handle, | 559 | return -ENODEV; |
517 | NULL, NULL, NULL); | 560 | |
561 | status = acpi_evaluate_object(lcd_switch_handle, | ||
562 | NULL, NULL, NULL); | ||
518 | 563 | ||
519 | if (ACPI_FAILURE(status)) | 564 | if (ACPI_FAILURE(status)) { |
520 | pr_warning("Error switching LCD\n"); | 565 | pr_warning("Error switching LCD\n"); |
566 | return -ENODEV; | ||
521 | } | 567 | } |
522 | 568 | ||
523 | write_status(NULL, lcd, LCD_ON); | 569 | asus->lcd_state = lcd; |
524 | return 0; | 570 | return 0; |
525 | } | 571 | } |
526 | 572 | ||
527 | static void lcd_blank(int blank) | 573 | static void lcd_blank(struct asus_laptop *asus, int blank) |
528 | { | 574 | { |
529 | struct backlight_device *bd = asus_backlight_device; | 575 | struct backlight_device *bd = asus->backlight_device; |
576 | |||
577 | asus->lcd_state = (blank == FB_BLANK_UNBLANK); | ||
530 | 578 | ||
531 | if (bd) { | 579 | if (bd) { |
532 | bd->props.power = blank; | 580 | bd->props.power = blank; |
@@ -534,44 +582,94 @@ static void lcd_blank(int blank) | |||
534 | } | 582 | } |
535 | } | 583 | } |
536 | 584 | ||
537 | static int read_brightness(struct backlight_device *bd) | 585 | static int asus_read_brightness(struct backlight_device *bd) |
538 | { | 586 | { |
587 | struct asus_laptop *asus = bl_get_data(bd); | ||
539 | unsigned long long value; | 588 | unsigned long long value; |
540 | acpi_status rv = AE_OK; | 589 | acpi_status rv = AE_OK; |
541 | 590 | ||
542 | rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value); | 591 | rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET, |
592 | NULL, &value); | ||
543 | if (ACPI_FAILURE(rv)) | 593 | if (ACPI_FAILURE(rv)) |
544 | pr_warning("Error reading brightness\n"); | 594 | pr_warning("Error reading brightness\n"); |
545 | 595 | ||
546 | return value; | 596 | return value; |
547 | } | 597 | } |
548 | 598 | ||
549 | static int set_brightness(struct backlight_device *bd, int value) | 599 | static int asus_set_brightness(struct backlight_device *bd, int value) |
550 | { | 600 | { |
551 | int ret = 0; | 601 | struct asus_laptop *asus = bl_get_data(bd); |
552 | |||
553 | value = (0 < value) ? ((15 < value) ? 15 : value) : 0; | ||
554 | /* 0 <= value <= 15 */ | ||
555 | 602 | ||
556 | if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) { | 603 | if (write_acpi_int(asus->handle, METHOD_BRIGHTNESS_SET, value)) { |
557 | pr_warning("Error changing brightness\n"); | 604 | pr_warning("Error changing brightness\n"); |
558 | ret = -EIO; | 605 | return -EIO; |
559 | } | 606 | } |
560 | 607 | return 0; | |
561 | return ret; | ||
562 | } | 608 | } |
563 | 609 | ||
564 | static int update_bl_status(struct backlight_device *bd) | 610 | static int update_bl_status(struct backlight_device *bd) |
565 | { | 611 | { |
612 | struct asus_laptop *asus = bl_get_data(bd); | ||
566 | int rv; | 613 | int rv; |
567 | int value = bd->props.brightness; | 614 | int value = bd->props.brightness; |
568 | 615 | ||
569 | rv = set_brightness(bd, value); | 616 | rv = asus_set_brightness(bd, value); |
570 | if (rv) | 617 | if (rv) |
571 | return rv; | 618 | return rv; |
572 | 619 | ||
573 | value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0; | 620 | value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0; |
574 | return set_lcd_state(value); | 621 | return asus_lcd_set(asus, value); |
622 | } | ||
623 | |||
624 | static struct backlight_ops asusbl_ops = { | ||
625 | .get_brightness = asus_read_brightness, | ||
626 | .update_status = update_bl_status, | ||
627 | }; | ||
628 | |||
629 | static int asus_backlight_notify(struct asus_laptop *asus) | ||
630 | { | ||
631 | struct backlight_device *bd = asus->backlight_device; | ||
632 | int old = bd->props.brightness; | ||
633 | |||
634 | backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); | ||
635 | |||
636 | return old; | ||
637 | } | ||
638 | |||
639 | static int asus_backlight_init(struct asus_laptop *asus) | ||
640 | { | ||
641 | struct backlight_device *bd; | ||
642 | struct device *dev = &asus->platform_device->dev; | ||
643 | struct backlight_properties props; | ||
644 | |||
645 | if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) && | ||
646 | !acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) && | ||
647 | lcd_switch_handle) { | ||
648 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
649 | props.max_brightness = 15; | ||
650 | |||
651 | bd = backlight_device_register(ASUS_LAPTOP_FILE, dev, | ||
652 | asus, &asusbl_ops, &props); | ||
653 | if (IS_ERR(bd)) { | ||
654 | pr_err("Could not register asus backlight device\n"); | ||
655 | asus->backlight_device = NULL; | ||
656 | return PTR_ERR(bd); | ||
657 | } | ||
658 | |||
659 | asus->backlight_device = bd; | ||
660 | |||
661 | bd->props.power = FB_BLANK_UNBLANK; | ||
662 | bd->props.brightness = asus_read_brightness(bd); | ||
663 | backlight_update_status(bd); | ||
664 | } | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | static void asus_backlight_exit(struct asus_laptop *asus) | ||
669 | { | ||
670 | if (asus->backlight_device) | ||
671 | backlight_device_unregister(asus->backlight_device); | ||
672 | asus->backlight_device = NULL; | ||
575 | } | 673 | } |
576 | 674 | ||
577 | /* | 675 | /* |
@@ -586,25 +684,26 @@ static int update_bl_status(struct backlight_device *bd) | |||
586 | static ssize_t show_infos(struct device *dev, | 684 | static ssize_t show_infos(struct device *dev, |
587 | struct device_attribute *attr, char *page) | 685 | struct device_attribute *attr, char *page) |
588 | { | 686 | { |
687 | struct asus_laptop *asus = dev_get_drvdata(dev); | ||
589 | int len = 0; | 688 | int len = 0; |
590 | unsigned long long temp; | 689 | unsigned long long temp; |
591 | char buf[16]; /* enough for all info */ | 690 | char buf[16]; /* enough for all info */ |
592 | acpi_status rv = AE_OK; | 691 | acpi_status rv = AE_OK; |
593 | 692 | ||
594 | /* | 693 | /* |
595 | * We use the easy way, we don't care of off and count, so we don't set eof | 694 | * We use the easy way, we don't care of off and count, |
596 | * to 1 | 695 | * so we don't set eof to 1 |
597 | */ | 696 | */ |
598 | 697 | ||
599 | len += sprintf(page, ASUS_HOTK_NAME " " ASUS_LAPTOP_VERSION "\n"); | 698 | len += sprintf(page, ASUS_LAPTOP_NAME " " ASUS_LAPTOP_VERSION "\n"); |
600 | len += sprintf(page + len, "Model reference : %s\n", hotk->name); | 699 | len += sprintf(page + len, "Model reference : %s\n", asus->name); |
601 | /* | 700 | /* |
602 | * The SFUN method probably allows the original driver to get the list | 701 | * The SFUN method probably allows the original driver to get the list |
603 | * of features supported by a given model. For now, 0x0100 or 0x0800 | 702 | * of features supported by a given model. For now, 0x0100 or 0x0800 |
604 | * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. | 703 | * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. |
605 | * The significance of others is yet to be found. | 704 | * The significance of others is yet to be found. |
606 | */ | 705 | */ |
607 | rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp); | 706 | rv = acpi_evaluate_integer(asus->handle, "SFUN", NULL, &temp); |
608 | if (!ACPI_FAILURE(rv)) | 707 | if (!ACPI_FAILURE(rv)) |
609 | len += sprintf(page + len, "SFUN value : %#x\n", | 708 | len += sprintf(page + len, "SFUN value : %#x\n", |
610 | (uint) temp); | 709 | (uint) temp); |
@@ -614,7 +713,7 @@ static ssize_t show_infos(struct device *dev, | |||
614 | * The significance of others is yet to be found. | 713 | * The significance of others is yet to be found. |
615 | * If we don't find the method, we assume the device are present. | 714 | * If we don't find the method, we assume the device are present. |
616 | */ | 715 | */ |
617 | rv = acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &temp); | 716 | rv = acpi_evaluate_integer(asus->handle, "HRWS", NULL, &temp); |
618 | if (!ACPI_FAILURE(rv)) | 717 | if (!ACPI_FAILURE(rv)) |
619 | len += sprintf(page + len, "HRWS value : %#x\n", | 718 | len += sprintf(page + len, "HRWS value : %#x\n", |
620 | (uint) temp); | 719 | (uint) temp); |
@@ -625,26 +724,26 @@ static ssize_t show_infos(struct device *dev, | |||
625 | * Note: since not all the laptops provide this method, errors are | 724 | * Note: since not all the laptops provide this method, errors are |
626 | * silently ignored. | 725 | * silently ignored. |
627 | */ | 726 | */ |
628 | rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp); | 727 | rv = acpi_evaluate_integer(asus->handle, "ASYM", NULL, &temp); |
629 | if (!ACPI_FAILURE(rv)) | 728 | if (!ACPI_FAILURE(rv)) |
630 | len += sprintf(page + len, "ASYM value : %#x\n", | 729 | len += sprintf(page + len, "ASYM value : %#x\n", |
631 | (uint) temp); | 730 | (uint) temp); |
632 | if (asus_info) { | 731 | if (asus->dsdt_info) { |
633 | snprintf(buf, 16, "%d", asus_info->length); | 732 | snprintf(buf, 16, "%d", asus->dsdt_info->length); |
634 | len += sprintf(page + len, "DSDT length : %s\n", buf); | 733 | len += sprintf(page + len, "DSDT length : %s\n", buf); |
635 | snprintf(buf, 16, "%d", asus_info->checksum); | 734 | snprintf(buf, 16, "%d", asus->dsdt_info->checksum); |
636 | len += sprintf(page + len, "DSDT checksum : %s\n", buf); | 735 | len += sprintf(page + len, "DSDT checksum : %s\n", buf); |
637 | snprintf(buf, 16, "%d", asus_info->revision); | 736 | snprintf(buf, 16, "%d", asus->dsdt_info->revision); |
638 | len += sprintf(page + len, "DSDT revision : %s\n", buf); | 737 | len += sprintf(page + len, "DSDT revision : %s\n", buf); |
639 | snprintf(buf, 7, "%s", asus_info->oem_id); | 738 | snprintf(buf, 7, "%s", asus->dsdt_info->oem_id); |
640 | len += sprintf(page + len, "OEM id : %s\n", buf); | 739 | len += sprintf(page + len, "OEM id : %s\n", buf); |
641 | snprintf(buf, 9, "%s", asus_info->oem_table_id); | 740 | snprintf(buf, 9, "%s", asus->dsdt_info->oem_table_id); |
642 | len += sprintf(page + len, "OEM table id : %s\n", buf); | 741 | len += sprintf(page + len, "OEM table id : %s\n", buf); |
643 | snprintf(buf, 16, "%x", asus_info->oem_revision); | 742 | snprintf(buf, 16, "%x", asus->dsdt_info->oem_revision); |
644 | len += sprintf(page + len, "OEM revision : 0x%s\n", buf); | 743 | len += sprintf(page + len, "OEM revision : 0x%s\n", buf); |
645 | snprintf(buf, 5, "%s", asus_info->asl_compiler_id); | 744 | snprintf(buf, 5, "%s", asus->dsdt_info->asl_compiler_id); |
646 | len += sprintf(page + len, "ASL comp vendor id : %s\n", buf); | 745 | len += sprintf(page + len, "ASL comp vendor id : %s\n", buf); |
647 | snprintf(buf, 16, "%x", asus_info->asl_compiler_revision); | 746 | snprintf(buf, 16, "%x", asus->dsdt_info->asl_compiler_revision); |
648 | len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf); | 747 | len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf); |
649 | } | 748 | } |
650 | 749 | ||
@@ -662,8 +761,9 @@ static int parse_arg(const char *buf, unsigned long count, int *val) | |||
662 | return count; | 761 | return count; |
663 | } | 762 | } |
664 | 763 | ||
665 | static ssize_t store_status(const char *buf, size_t count, | 764 | static ssize_t sysfs_acpi_set(struct asus_laptop *asus, |
666 | acpi_handle handle, int mask) | 765 | const char *buf, size_t count, |
766 | const char *method) | ||
667 | { | 767 | { |
668 | int rv, value; | 768 | int rv, value; |
669 | int out = 0; | 769 | int out = 0; |
@@ -672,8 +772,8 @@ static ssize_t store_status(const char *buf, size_t count, | |||
672 | if (rv > 0) | 772 | if (rv > 0) |
673 | out = value ? 1 : 0; | 773 | out = value ? 1 : 0; |
674 | 774 | ||
675 | write_status(handle, out, mask); | 775 | if (write_acpi_int(asus->handle, method, value)) |
676 | 776 | return -ENODEV; | |
677 | return rv; | 777 | return rv; |
678 | } | 778 | } |
679 | 779 | ||
@@ -683,67 +783,116 @@ static ssize_t store_status(const char *buf, size_t count, | |||
683 | static ssize_t show_ledd(struct device *dev, | 783 | static ssize_t show_ledd(struct device *dev, |
684 | struct device_attribute *attr, char *buf) | 784 | struct device_attribute *attr, char *buf) |
685 | { | 785 | { |
686 | return sprintf(buf, "0x%08x\n", hotk->ledd_status); | 786 | struct asus_laptop *asus = dev_get_drvdata(dev); |
787 | |||
788 | return sprintf(buf, "0x%08x\n", asus->ledd_status); | ||
687 | } | 789 | } |
688 | 790 | ||
689 | static ssize_t store_ledd(struct device *dev, struct device_attribute *attr, | 791 | static ssize_t store_ledd(struct device *dev, struct device_attribute *attr, |
690 | const char *buf, size_t count) | 792 | const char *buf, size_t count) |
691 | { | 793 | { |
794 | struct asus_laptop *asus = dev_get_drvdata(dev); | ||
692 | int rv, value; | 795 | int rv, value; |
693 | 796 | ||
694 | rv = parse_arg(buf, count, &value); | 797 | rv = parse_arg(buf, count, &value); |
695 | if (rv > 0) { | 798 | if (rv > 0) { |
696 | if (write_acpi_int(ledd_set_handle, NULL, value, NULL)) | 799 | if (write_acpi_int(asus->handle, METHOD_LEDD, value)) |
697 | pr_warning("LED display write failed\n"); | 800 | pr_warning("LED display write failed\n"); |
698 | else | 801 | else |
699 | hotk->ledd_status = (u32) value; | 802 | asus->ledd_status = (u32) value; |
700 | } | 803 | } |
701 | return rv; | 804 | return rv; |
702 | } | 805 | } |
703 | 806 | ||
704 | /* | 807 | /* |
808 | * Wireless | ||
809 | */ | ||
810 | static int asus_wireless_status(struct asus_laptop *asus, int mask) | ||
811 | { | ||
812 | unsigned long long status; | ||
813 | acpi_status rv = AE_OK; | ||
814 | |||
815 | if (!asus->have_rsts) | ||
816 | return (asus->wireless_status & mask) ? 1 : 0; | ||
817 | |||
818 | rv = acpi_evaluate_integer(asus->handle, METHOD_WL_STATUS, | ||
819 | NULL, &status); | ||
820 | if (ACPI_FAILURE(rv)) { | ||
821 | pr_warning("Error reading Wireless status\n"); | ||
822 | return -EINVAL; | ||
823 | } | ||
824 | return !!(status & mask); | ||
825 | } | ||
826 | |||
827 | /* | ||
705 | * WLAN | 828 | * WLAN |
706 | */ | 829 | */ |
830 | static int asus_wlan_set(struct asus_laptop *asus, int status) | ||
831 | { | ||
832 | if (write_acpi_int(asus->handle, METHOD_WLAN, !!status)) { | ||
833 | pr_warning("Error setting wlan status to %d", status); | ||
834 | return -EIO; | ||
835 | } | ||
836 | return 0; | ||
837 | } | ||
838 | |||
707 | static ssize_t show_wlan(struct device *dev, | 839 | static ssize_t show_wlan(struct device *dev, |
708 | struct device_attribute *attr, char *buf) | 840 | struct device_attribute *attr, char *buf) |
709 | { | 841 | { |
710 | return sprintf(buf, "%d\n", read_status(WL_ON)); | 842 | struct asus_laptop *asus = dev_get_drvdata(dev); |
843 | |||
844 | return sprintf(buf, "%d\n", asus_wireless_status(asus, WL_RSTS)); | ||
711 | } | 845 | } |
712 | 846 | ||
713 | static ssize_t store_wlan(struct device *dev, struct device_attribute *attr, | 847 | static ssize_t store_wlan(struct device *dev, struct device_attribute *attr, |
714 | const char *buf, size_t count) | 848 | const char *buf, size_t count) |
715 | { | 849 | { |
716 | return store_status(buf, count, wl_switch_handle, WL_ON); | 850 | struct asus_laptop *asus = dev_get_drvdata(dev); |
851 | |||
852 | return sysfs_acpi_set(asus, buf, count, METHOD_WLAN); | ||
717 | } | 853 | } |
718 | 854 | ||
719 | /* | 855 | /* |
720 | * Bluetooth | 856 | * Bluetooth |
721 | */ | 857 | */ |
858 | static int asus_bluetooth_set(struct asus_laptop *asus, int status) | ||
859 | { | ||
860 | if (write_acpi_int(asus->handle, METHOD_BLUETOOTH, !!status)) { | ||
861 | pr_warning("Error setting bluetooth status to %d", status); | ||
862 | return -EIO; | ||
863 | } | ||
864 | return 0; | ||
865 | } | ||
866 | |||
722 | static ssize_t show_bluetooth(struct device *dev, | 867 | static ssize_t show_bluetooth(struct device *dev, |
723 | struct device_attribute *attr, char *buf) | 868 | struct device_attribute *attr, char *buf) |
724 | { | 869 | { |
725 | return sprintf(buf, "%d\n", read_status(BT_ON)); | 870 | struct asus_laptop *asus = dev_get_drvdata(dev); |
871 | |||
872 | return sprintf(buf, "%d\n", asus_wireless_status(asus, BT_RSTS)); | ||
726 | } | 873 | } |
727 | 874 | ||
728 | static ssize_t store_bluetooth(struct device *dev, | 875 | static ssize_t store_bluetooth(struct device *dev, |
729 | struct device_attribute *attr, const char *buf, | 876 | struct device_attribute *attr, const char *buf, |
730 | size_t count) | 877 | size_t count) |
731 | { | 878 | { |
732 | return store_status(buf, count, bt_switch_handle, BT_ON); | 879 | struct asus_laptop *asus = dev_get_drvdata(dev); |
880 | |||
881 | return sysfs_acpi_set(asus, buf, count, METHOD_BLUETOOTH); | ||
733 | } | 882 | } |
734 | 883 | ||
735 | /* | 884 | /* |
736 | * Display | 885 | * Display |
737 | */ | 886 | */ |
738 | static void set_display(int value) | 887 | static void asus_set_display(struct asus_laptop *asus, int value) |
739 | { | 888 | { |
740 | /* no sanity check needed for now */ | 889 | /* no sanity check needed for now */ |
741 | if (write_acpi_int(display_set_handle, NULL, value, NULL)) | 890 | if (write_acpi_int(asus->handle, METHOD_SWITCH_DISPLAY, value)) |
742 | pr_warning("Error setting display\n"); | 891 | pr_warning("Error setting display\n"); |
743 | return; | 892 | return; |
744 | } | 893 | } |
745 | 894 | ||
746 | static int read_display(void) | 895 | static int read_display(struct asus_laptop *asus) |
747 | { | 896 | { |
748 | unsigned long long value = 0; | 897 | unsigned long long value = 0; |
749 | acpi_status rv = AE_OK; | 898 | acpi_status rv = AE_OK; |
@@ -759,7 +908,7 @@ static int read_display(void) | |||
759 | pr_warning("Error reading display status\n"); | 908 | pr_warning("Error reading display status\n"); |
760 | } | 909 | } |
761 | 910 | ||
762 | value &= 0x0F; /* needed for some models, shouldn't hurt others */ | 911 | value &= 0x0F; /* needed for some models, shouldn't hurt others */ |
763 | 912 | ||
764 | return value; | 913 | return value; |
765 | } | 914 | } |
@@ -771,7 +920,11 @@ static int read_display(void) | |||
771 | static ssize_t show_disp(struct device *dev, | 920 | static ssize_t show_disp(struct device *dev, |
772 | struct device_attribute *attr, char *buf) | 921 | struct device_attribute *attr, char *buf) |
773 | { | 922 | { |
774 | return sprintf(buf, "%d\n", read_display()); | 923 | struct asus_laptop *asus = dev_get_drvdata(dev); |
924 | |||
925 | if (!display_get_handle) | ||
926 | return -ENODEV; | ||
927 | return sprintf(buf, "%d\n", read_display(asus)); | ||
775 | } | 928 | } |
776 | 929 | ||
777 | /* | 930 | /* |
@@ -784,65 +937,72 @@ static ssize_t show_disp(struct device *dev, | |||
784 | static ssize_t store_disp(struct device *dev, struct device_attribute *attr, | 937 | static ssize_t store_disp(struct device *dev, struct device_attribute *attr, |
785 | const char *buf, size_t count) | 938 | const char *buf, size_t count) |
786 | { | 939 | { |
940 | struct asus_laptop *asus = dev_get_drvdata(dev); | ||
787 | int rv, value; | 941 | int rv, value; |
788 | 942 | ||
789 | rv = parse_arg(buf, count, &value); | 943 | rv = parse_arg(buf, count, &value); |
790 | if (rv > 0) | 944 | if (rv > 0) |
791 | set_display(value); | 945 | asus_set_display(asus, value); |
792 | return rv; | 946 | return rv; |
793 | } | 947 | } |
794 | 948 | ||
795 | /* | 949 | /* |
796 | * Light Sens | 950 | * Light Sens |
797 | */ | 951 | */ |
798 | static void set_light_sens_switch(int value) | 952 | static void asus_als_switch(struct asus_laptop *asus, int value) |
799 | { | 953 | { |
800 | if (write_acpi_int(ls_switch_handle, NULL, value, NULL)) | 954 | if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value)) |
801 | pr_warning("Error setting light sensor switch\n"); | 955 | pr_warning("Error setting light sensor switch\n"); |
802 | hotk->light_switch = value; | 956 | asus->light_switch = value; |
803 | } | 957 | } |
804 | 958 | ||
805 | static ssize_t show_lssw(struct device *dev, | 959 | static ssize_t show_lssw(struct device *dev, |
806 | struct device_attribute *attr, char *buf) | 960 | struct device_attribute *attr, char *buf) |
807 | { | 961 | { |
808 | return sprintf(buf, "%d\n", hotk->light_switch); | 962 | struct asus_laptop *asus = dev_get_drvdata(dev); |
963 | |||
964 | return sprintf(buf, "%d\n", asus->light_switch); | ||
809 | } | 965 | } |
810 | 966 | ||
811 | static ssize_t store_lssw(struct device *dev, struct device_attribute *attr, | 967 | static ssize_t store_lssw(struct device *dev, struct device_attribute *attr, |
812 | const char *buf, size_t count) | 968 | const char *buf, size_t count) |
813 | { | 969 | { |
970 | struct asus_laptop *asus = dev_get_drvdata(dev); | ||
814 | int rv, value; | 971 | int rv, value; |
815 | 972 | ||
816 | rv = parse_arg(buf, count, &value); | 973 | rv = parse_arg(buf, count, &value); |
817 | if (rv > 0) | 974 | if (rv > 0) |
818 | set_light_sens_switch(value ? 1 : 0); | 975 | asus_als_switch(asus, value ? 1 : 0); |
819 | 976 | ||
820 | return rv; | 977 | return rv; |
821 | } | 978 | } |
822 | 979 | ||
823 | static void set_light_sens_level(int value) | 980 | static void asus_als_level(struct asus_laptop *asus, int value) |
824 | { | 981 | { |
825 | if (write_acpi_int(ls_level_handle, NULL, value, NULL)) | 982 | if (write_acpi_int(asus->handle, METHOD_ALS_LEVEL, value)) |
826 | pr_warning("Error setting light sensor level\n"); | 983 | pr_warning("Error setting light sensor level\n"); |
827 | hotk->light_level = value; | 984 | asus->light_level = value; |
828 | } | 985 | } |
829 | 986 | ||
830 | static ssize_t show_lslvl(struct device *dev, | 987 | static ssize_t show_lslvl(struct device *dev, |
831 | struct device_attribute *attr, char *buf) | 988 | struct device_attribute *attr, char *buf) |
832 | { | 989 | { |
833 | return sprintf(buf, "%d\n", hotk->light_level); | 990 | struct asus_laptop *asus = dev_get_drvdata(dev); |
991 | |||
992 | return sprintf(buf, "%d\n", asus->light_level); | ||
834 | } | 993 | } |
835 | 994 | ||
836 | static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr, | 995 | static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr, |
837 | const char *buf, size_t count) | 996 | const char *buf, size_t count) |
838 | { | 997 | { |
998 | struct asus_laptop *asus = dev_get_drvdata(dev); | ||
839 | int rv, value; | 999 | int rv, value; |
840 | 1000 | ||
841 | rv = parse_arg(buf, count, &value); | 1001 | rv = parse_arg(buf, count, &value); |
842 | if (rv > 0) { | 1002 | if (rv > 0) { |
843 | value = (0 < value) ? ((15 < value) ? 15 : value) : 0; | 1003 | value = (0 < value) ? ((15 < value) ? 15 : value) : 0; |
844 | /* 0 <= value <= 15 */ | 1004 | /* 0 <= value <= 15 */ |
845 | set_light_sens_level(value); | 1005 | asus_als_level(asus, value); |
846 | } | 1006 | } |
847 | 1007 | ||
848 | return rv; | 1008 | return rv; |
@@ -851,197 +1011,309 @@ static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr, | |||
851 | /* | 1011 | /* |
852 | * GPS | 1012 | * GPS |
853 | */ | 1013 | */ |
1014 | static int asus_gps_status(struct asus_laptop *asus) | ||
1015 | { | ||
1016 | unsigned long long status; | ||
1017 | acpi_status rv = AE_OK; | ||
1018 | |||
1019 | rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS, | ||
1020 | NULL, &status); | ||
1021 | if (ACPI_FAILURE(rv)) { | ||
1022 | pr_warning("Error reading GPS status\n"); | ||
1023 | return -ENODEV; | ||
1024 | } | ||
1025 | return !!status; | ||
1026 | } | ||
1027 | |||
1028 | static int asus_gps_switch(struct asus_laptop *asus, int status) | ||
1029 | { | ||
1030 | const char *meth = status ? METHOD_GPS_ON : METHOD_GPS_OFF; | ||
1031 | |||
1032 | if (write_acpi_int(asus->handle, meth, 0x02)) | ||
1033 | return -ENODEV; | ||
1034 | return 0; | ||
1035 | } | ||
1036 | |||
854 | static ssize_t show_gps(struct device *dev, | 1037 | static ssize_t show_gps(struct device *dev, |
855 | struct device_attribute *attr, char *buf) | 1038 | struct device_attribute *attr, char *buf) |
856 | { | 1039 | { |
857 | return sprintf(buf, "%d\n", read_status(GPS_ON)); | 1040 | struct asus_laptop *asus = dev_get_drvdata(dev); |
1041 | |||
1042 | return sprintf(buf, "%d\n", asus_gps_status(asus)); | ||
858 | } | 1043 | } |
859 | 1044 | ||
860 | static ssize_t store_gps(struct device *dev, struct device_attribute *attr, | 1045 | static ssize_t store_gps(struct device *dev, struct device_attribute *attr, |
861 | const char *buf, size_t count) | 1046 | const char *buf, size_t count) |
862 | { | 1047 | { |
863 | return store_status(buf, count, NULL, GPS_ON); | 1048 | struct asus_laptop *asus = dev_get_drvdata(dev); |
1049 | int rv, value; | ||
1050 | int ret; | ||
1051 | |||
1052 | rv = parse_arg(buf, count, &value); | ||
1053 | if (rv <= 0) | ||
1054 | return -EINVAL; | ||
1055 | ret = asus_gps_switch(asus, !!value); | ||
1056 | if (ret) | ||
1057 | return ret; | ||
1058 | rfkill_set_sw_state(asus->gps_rfkill, !value); | ||
1059 | return rv; | ||
864 | } | 1060 | } |
865 | 1061 | ||
866 | /* | 1062 | /* |
867 | * Hotkey functions | 1063 | * rfkill |
868 | */ | 1064 | */ |
869 | static struct key_entry *asus_get_entry_by_scancode(int code) | 1065 | static int asus_gps_rfkill_set(void *data, bool blocked) |
870 | { | 1066 | { |
871 | struct key_entry *key; | 1067 | acpi_handle handle = data; |
872 | |||
873 | for (key = asus_keymap; key->type != KE_END; key++) | ||
874 | if (code == key->code) | ||
875 | return key; | ||
876 | 1068 | ||
877 | return NULL; | 1069 | return asus_gps_switch(handle, !blocked); |
878 | } | 1070 | } |
879 | 1071 | ||
880 | static struct key_entry *asus_get_entry_by_keycode(int code) | 1072 | static const struct rfkill_ops asus_gps_rfkill_ops = { |
881 | { | 1073 | .set_block = asus_gps_rfkill_set, |
882 | struct key_entry *key; | 1074 | }; |
883 | |||
884 | for (key = asus_keymap; key->type != KE_END; key++) | ||
885 | if (code == key->keycode && key->type == KE_KEY) | ||
886 | return key; | ||
887 | 1075 | ||
888 | return NULL; | 1076 | static void asus_rfkill_exit(struct asus_laptop *asus) |
1077 | { | ||
1078 | if (asus->gps_rfkill) { | ||
1079 | rfkill_unregister(asus->gps_rfkill); | ||
1080 | rfkill_destroy(asus->gps_rfkill); | ||
1081 | asus->gps_rfkill = NULL; | ||
1082 | } | ||
889 | } | 1083 | } |
890 | 1084 | ||
891 | static int asus_getkeycode(struct input_dev *dev, int scancode, int *keycode) | 1085 | static int asus_rfkill_init(struct asus_laptop *asus) |
892 | { | 1086 | { |
893 | struct key_entry *key = asus_get_entry_by_scancode(scancode); | 1087 | int result; |
894 | 1088 | ||
895 | if (key && key->type == KE_KEY) { | 1089 | if (acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) || |
896 | *keycode = key->keycode; | 1090 | acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) || |
1091 | acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) | ||
897 | return 0; | 1092 | return 0; |
1093 | |||
1094 | asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, | ||
1095 | RFKILL_TYPE_GPS, | ||
1096 | &asus_gps_rfkill_ops, NULL); | ||
1097 | if (!asus->gps_rfkill) | ||
1098 | return -EINVAL; | ||
1099 | |||
1100 | result = rfkill_register(asus->gps_rfkill); | ||
1101 | if (result) { | ||
1102 | rfkill_destroy(asus->gps_rfkill); | ||
1103 | asus->gps_rfkill = NULL; | ||
898 | } | 1104 | } |
899 | 1105 | ||
900 | return -EINVAL; | 1106 | return result; |
901 | } | 1107 | } |
902 | 1108 | ||
903 | static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode) | 1109 | /* |
1110 | * Input device (i.e. hotkeys) | ||
1111 | */ | ||
1112 | static void asus_input_notify(struct asus_laptop *asus, int event) | ||
904 | { | 1113 | { |
905 | struct key_entry *key; | 1114 | if (asus->inputdev) |
906 | int old_keycode; | 1115 | sparse_keymap_report_event(asus->inputdev, event, 1, true); |
1116 | } | ||
907 | 1117 | ||
908 | if (keycode < 0 || keycode > KEY_MAX) | 1118 | static int asus_input_init(struct asus_laptop *asus) |
909 | return -EINVAL; | 1119 | { |
1120 | struct input_dev *input; | ||
1121 | int error; | ||
910 | 1122 | ||
911 | key = asus_get_entry_by_scancode(scancode); | 1123 | input = input_allocate_device(); |
912 | if (key && key->type == KE_KEY) { | 1124 | if (!input) { |
913 | old_keycode = key->keycode; | 1125 | pr_info("Unable to allocate input device\n"); |
914 | key->keycode = keycode; | ||
915 | set_bit(keycode, dev->keybit); | ||
916 | if (!asus_get_entry_by_keycode(old_keycode)) | ||
917 | clear_bit(old_keycode, dev->keybit); | ||
918 | return 0; | 1126 | return 0; |
919 | } | 1127 | } |
1128 | input->name = "Asus Laptop extra buttons"; | ||
1129 | input->phys = ASUS_LAPTOP_FILE "/input0"; | ||
1130 | input->id.bustype = BUS_HOST; | ||
1131 | input->dev.parent = &asus->platform_device->dev; | ||
1132 | input_set_drvdata(input, asus); | ||
1133 | |||
1134 | error = sparse_keymap_setup(input, asus_keymap, NULL); | ||
1135 | if (error) { | ||
1136 | pr_err("Unable to setup input device keymap\n"); | ||
1137 | goto err_keymap; | ||
1138 | } | ||
1139 | error = input_register_device(input); | ||
1140 | if (error) { | ||
1141 | pr_info("Unable to register input device\n"); | ||
1142 | goto err_device; | ||
1143 | } | ||
920 | 1144 | ||
921 | return -EINVAL; | 1145 | asus->inputdev = input; |
1146 | return 0; | ||
1147 | |||
1148 | err_keymap: | ||
1149 | sparse_keymap_free(input); | ||
1150 | err_device: | ||
1151 | input_free_device(input); | ||
1152 | return error; | ||
922 | } | 1153 | } |
923 | 1154 | ||
924 | static void asus_hotk_notify(struct acpi_device *device, u32 event) | 1155 | static void asus_input_exit(struct asus_laptop *asus) |
925 | { | 1156 | { |
926 | static struct key_entry *key; | 1157 | if (asus->inputdev) { |
927 | u16 count; | 1158 | sparse_keymap_free(asus->inputdev); |
1159 | input_unregister_device(asus->inputdev); | ||
1160 | } | ||
1161 | } | ||
928 | 1162 | ||
929 | /* TODO Find a better way to handle events count. */ | 1163 | /* |
930 | if (!hotk) | 1164 | * ACPI driver |
931 | return; | 1165 | */ |
1166 | static void asus_acpi_notify(struct acpi_device *device, u32 event) | ||
1167 | { | ||
1168 | struct asus_laptop *asus = acpi_driver_data(device); | ||
1169 | u16 count; | ||
932 | 1170 | ||
933 | /* | 1171 | /* |
934 | * We need to tell the backlight device when the backlight power is | 1172 | * We need to tell the backlight device when the backlight power is |
935 | * switched | 1173 | * switched |
936 | */ | 1174 | */ |
937 | if (event == ATKD_LCD_ON) { | 1175 | if (event == ATKD_LCD_ON) |
938 | write_status(NULL, 1, LCD_ON); | 1176 | lcd_blank(asus, FB_BLANK_UNBLANK); |
939 | lcd_blank(FB_BLANK_UNBLANK); | 1177 | else if (event == ATKD_LCD_OFF) |
940 | } else if (event == ATKD_LCD_OFF) { | 1178 | lcd_blank(asus, FB_BLANK_POWERDOWN); |
941 | write_status(NULL, 0, LCD_ON); | ||
942 | lcd_blank(FB_BLANK_POWERDOWN); | ||
943 | } | ||
944 | 1179 | ||
945 | count = hotk->event_count[event % 128]++; | 1180 | /* TODO Find a better way to handle events count. */ |
946 | acpi_bus_generate_proc_event(hotk->device, event, count); | 1181 | count = asus->event_count[event % 128]++; |
947 | acpi_bus_generate_netlink_event(hotk->device->pnp.device_class, | 1182 | acpi_bus_generate_proc_event(asus->device, event, count); |
948 | dev_name(&hotk->device->dev), event, | 1183 | acpi_bus_generate_netlink_event(asus->device->pnp.device_class, |
1184 | dev_name(&asus->device->dev), event, | ||
949 | count); | 1185 | count); |
950 | 1186 | ||
951 | if (hotk->inputdev) { | 1187 | /* Brightness events are special */ |
952 | key = asus_get_entry_by_scancode(event); | 1188 | if (event >= ATKD_BR_MIN && event <= ATKD_BR_MAX) { |
953 | if (!key) | 1189 | |
954 | return ; | 1190 | /* Ignore them completely if the acpi video driver is used */ |
955 | 1191 | if (asus->backlight_device != NULL) { | |
956 | switch (key->type) { | 1192 | /* Update the backlight device. */ |
957 | case KE_KEY: | 1193 | asus_backlight_notify(asus); |
958 | input_report_key(hotk->inputdev, key->keycode, 1); | ||
959 | input_sync(hotk->inputdev); | ||
960 | input_report_key(hotk->inputdev, key->keycode, 0); | ||
961 | input_sync(hotk->inputdev); | ||
962 | break; | ||
963 | } | 1194 | } |
1195 | return ; | ||
964 | } | 1196 | } |
1197 | asus_input_notify(asus, event); | ||
965 | } | 1198 | } |
966 | 1199 | ||
967 | #define ASUS_CREATE_DEVICE_ATTR(_name) \ | 1200 | static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL); |
968 | struct device_attribute dev_attr_##_name = { \ | 1201 | static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan); |
969 | .attr = { \ | 1202 | static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth, |
970 | .name = __stringify(_name), \ | 1203 | store_bluetooth); |
971 | .mode = 0 }, \ | 1204 | static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp); |
972 | .show = NULL, \ | 1205 | static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); |
973 | .store = NULL, \ | 1206 | static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); |
1207 | static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw); | ||
1208 | static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); | ||
1209 | |||
1210 | static void asus_sysfs_exit(struct asus_laptop *asus) | ||
1211 | { | ||
1212 | struct platform_device *device = asus->platform_device; | ||
1213 | |||
1214 | device_remove_file(&device->dev, &dev_attr_infos); | ||
1215 | device_remove_file(&device->dev, &dev_attr_wlan); | ||
1216 | device_remove_file(&device->dev, &dev_attr_bluetooth); | ||
1217 | device_remove_file(&device->dev, &dev_attr_display); | ||
1218 | device_remove_file(&device->dev, &dev_attr_ledd); | ||
1219 | device_remove_file(&device->dev, &dev_attr_ls_switch); | ||
1220 | device_remove_file(&device->dev, &dev_attr_ls_level); | ||
1221 | device_remove_file(&device->dev, &dev_attr_gps); | ||
1222 | } | ||
1223 | |||
1224 | static int asus_sysfs_init(struct asus_laptop *asus) | ||
1225 | { | ||
1226 | struct platform_device *device = asus->platform_device; | ||
1227 | int err; | ||
1228 | |||
1229 | err = device_create_file(&device->dev, &dev_attr_infos); | ||
1230 | if (err) | ||
1231 | return err; | ||
1232 | |||
1233 | if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) { | ||
1234 | err = device_create_file(&device->dev, &dev_attr_wlan); | ||
1235 | if (err) | ||
1236 | return err; | ||
974 | } | 1237 | } |
975 | 1238 | ||
976 | #define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store) \ | 1239 | if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) { |
977 | do { \ | 1240 | err = device_create_file(&device->dev, &dev_attr_bluetooth); |
978 | dev_attr_##_name.attr.mode = _mode; \ | 1241 | if (err) |
979 | dev_attr_##_name.show = _show; \ | 1242 | return err; |
980 | dev_attr_##_name.store = _store; \ | 1243 | } |
981 | } while(0) | ||
982 | |||
983 | static ASUS_CREATE_DEVICE_ATTR(infos); | ||
984 | static ASUS_CREATE_DEVICE_ATTR(wlan); | ||
985 | static ASUS_CREATE_DEVICE_ATTR(bluetooth); | ||
986 | static ASUS_CREATE_DEVICE_ATTR(display); | ||
987 | static ASUS_CREATE_DEVICE_ATTR(ledd); | ||
988 | static ASUS_CREATE_DEVICE_ATTR(ls_switch); | ||
989 | static ASUS_CREATE_DEVICE_ATTR(ls_level); | ||
990 | static ASUS_CREATE_DEVICE_ATTR(gps); | ||
991 | |||
992 | static struct attribute *asuspf_attributes[] = { | ||
993 | &dev_attr_infos.attr, | ||
994 | &dev_attr_wlan.attr, | ||
995 | &dev_attr_bluetooth.attr, | ||
996 | &dev_attr_display.attr, | ||
997 | &dev_attr_ledd.attr, | ||
998 | &dev_attr_ls_switch.attr, | ||
999 | &dev_attr_ls_level.attr, | ||
1000 | &dev_attr_gps.attr, | ||
1001 | NULL | ||
1002 | }; | ||
1003 | 1244 | ||
1004 | static struct attribute_group asuspf_attribute_group = { | 1245 | if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) { |
1005 | .attrs = asuspf_attributes | 1246 | err = device_create_file(&device->dev, &dev_attr_display); |
1006 | }; | 1247 | if (err) |
1248 | return err; | ||
1249 | } | ||
1007 | 1250 | ||
1008 | static struct platform_driver asuspf_driver = { | 1251 | if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) { |
1009 | .driver = { | 1252 | err = device_create_file(&device->dev, &dev_attr_ledd); |
1010 | .name = ASUS_HOTK_FILE, | 1253 | if (err) |
1011 | .owner = THIS_MODULE, | 1254 | return err; |
1012 | } | 1255 | } |
1013 | }; | ||
1014 | 1256 | ||
1015 | static struct platform_device *asuspf_device; | 1257 | if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && |
1258 | !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { | ||
1259 | err = device_create_file(&device->dev, &dev_attr_ls_switch); | ||
1260 | if (err) | ||
1261 | return err; | ||
1262 | err = device_create_file(&device->dev, &dev_attr_ls_level); | ||
1263 | if (err) | ||
1264 | return err; | ||
1265 | } | ||
1016 | 1266 | ||
1017 | static void asus_hotk_add_fs(void) | 1267 | if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && |
1018 | { | 1268 | !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && |
1019 | ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL); | 1269 | !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) { |
1270 | err = device_create_file(&device->dev, &dev_attr_gps); | ||
1271 | if (err) | ||
1272 | return err; | ||
1273 | } | ||
1020 | 1274 | ||
1021 | if (wl_switch_handle) | 1275 | return err; |
1022 | ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan); | 1276 | } |
1023 | 1277 | ||
1024 | if (bt_switch_handle) | 1278 | static int asus_platform_init(struct asus_laptop *asus) |
1025 | ASUS_SET_DEVICE_ATTR(bluetooth, 0644, | 1279 | { |
1026 | show_bluetooth, store_bluetooth); | 1280 | int err; |
1027 | 1281 | ||
1028 | if (display_set_handle && display_get_handle) | 1282 | asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1); |
1029 | ASUS_SET_DEVICE_ATTR(display, 0644, show_disp, store_disp); | 1283 | if (!asus->platform_device) |
1030 | else if (display_set_handle) | 1284 | return -ENOMEM; |
1031 | ASUS_SET_DEVICE_ATTR(display, 0200, NULL, store_disp); | 1285 | platform_set_drvdata(asus->platform_device, asus); |
1032 | 1286 | ||
1033 | if (ledd_set_handle) | 1287 | err = platform_device_add(asus->platform_device); |
1034 | ASUS_SET_DEVICE_ATTR(ledd, 0644, show_ledd, store_ledd); | 1288 | if (err) |
1289 | goto fail_platform_device; | ||
1035 | 1290 | ||
1036 | if (ls_switch_handle && ls_level_handle) { | 1291 | err = asus_sysfs_init(asus); |
1037 | ASUS_SET_DEVICE_ATTR(ls_level, 0644, show_lslvl, store_lslvl); | 1292 | if (err) |
1038 | ASUS_SET_DEVICE_ATTR(ls_switch, 0644, show_lssw, store_lssw); | 1293 | goto fail_sysfs; |
1039 | } | 1294 | return 0; |
1040 | 1295 | ||
1041 | if (gps_status_handle && gps_on_handle && gps_off_handle) | 1296 | fail_sysfs: |
1042 | ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps); | 1297 | asus_sysfs_exit(asus); |
1298 | platform_device_del(asus->platform_device); | ||
1299 | fail_platform_device: | ||
1300 | platform_device_put(asus->platform_device); | ||
1301 | return err; | ||
1302 | } | ||
1303 | |||
1304 | static void asus_platform_exit(struct asus_laptop *asus) | ||
1305 | { | ||
1306 | asus_sysfs_exit(asus); | ||
1307 | platform_device_unregister(asus->platform_device); | ||
1043 | } | 1308 | } |
1044 | 1309 | ||
1310 | static struct platform_driver platform_driver = { | ||
1311 | .driver = { | ||
1312 | .name = ASUS_LAPTOP_FILE, | ||
1313 | .owner = THIS_MODULE, | ||
1314 | } | ||
1315 | }; | ||
1316 | |||
1045 | static int asus_handle_init(char *name, acpi_handle * handle, | 1317 | static int asus_handle_init(char *name, acpi_handle * handle, |
1046 | char **paths, int num_paths) | 1318 | char **paths, int num_paths) |
1047 | { | 1319 | { |
@@ -1063,10 +1335,11 @@ static int asus_handle_init(char *name, acpi_handle * handle, | |||
1063 | ARRAY_SIZE(object##_paths)) | 1335 | ARRAY_SIZE(object##_paths)) |
1064 | 1336 | ||
1065 | /* | 1337 | /* |
1066 | * This function is used to initialize the hotk with right values. In this | 1338 | * This function is used to initialize the context with right values. In this |
1067 | * method, we can make all the detection we want, and modify the hotk struct | 1339 | * method, we can make all the detection we want, and modify the asus_laptop |
1340 | * struct | ||
1068 | */ | 1341 | */ |
1069 | static int asus_hotk_get_info(void) | 1342 | static int asus_laptop_get_info(struct asus_laptop *asus) |
1070 | { | 1343 | { |
1071 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 1344 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
1072 | union acpi_object *model = NULL; | 1345 | union acpi_object *model = NULL; |
@@ -1079,22 +1352,21 @@ static int asus_hotk_get_info(void) | |||
1079 | * models, but late enough to allow acpi_bus_register_driver() to fail | 1352 | * models, but late enough to allow acpi_bus_register_driver() to fail |
1080 | * before doing anything ACPI-specific. Should we encounter a machine, | 1353 | * before doing anything ACPI-specific. Should we encounter a machine, |
1081 | * which needs special handling (i.e. its hotkey device has a different | 1354 | * which needs special handling (i.e. its hotkey device has a different |
1082 | * HID), this bit will be moved. A global variable asus_info contains | 1355 | * HID), this bit will be moved. |
1083 | * the DSDT header. | ||
1084 | */ | 1356 | */ |
1085 | status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info); | 1357 | status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus->dsdt_info); |
1086 | if (ACPI_FAILURE(status)) | 1358 | if (ACPI_FAILURE(status)) |
1087 | pr_warning("Couldn't get the DSDT table header\n"); | 1359 | pr_warning("Couldn't get the DSDT table header\n"); |
1088 | 1360 | ||
1089 | /* We have to write 0 on init this far for all ASUS models */ | 1361 | /* We have to write 0 on init this far for all ASUS models */ |
1090 | if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { | 1362 | if (write_acpi_int_ret(asus->handle, "INIT", 0, &buffer)) { |
1091 | pr_err("Hotkey initialization failed\n"); | 1363 | pr_err("Hotkey initialization failed\n"); |
1092 | return -ENODEV; | 1364 | return -ENODEV; |
1093 | } | 1365 | } |
1094 | 1366 | ||
1095 | /* This needs to be called for some laptops to init properly */ | 1367 | /* This needs to be called for some laptops to init properly */ |
1096 | status = | 1368 | status = |
1097 | acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result); | 1369 | acpi_evaluate_integer(asus->handle, "BSTS", NULL, &bsts_result); |
1098 | if (ACPI_FAILURE(status)) | 1370 | if (ACPI_FAILURE(status)) |
1099 | pr_warning("Error calling BSTS\n"); | 1371 | pr_warning("Error calling BSTS\n"); |
1100 | else if (bsts_result) | 1372 | else if (bsts_result) |
@@ -1102,8 +1374,8 @@ static int asus_hotk_get_info(void) | |||
1102 | (uint) bsts_result); | 1374 | (uint) bsts_result); |
1103 | 1375 | ||
1104 | /* This too ... */ | 1376 | /* This too ... */ |
1105 | write_acpi_int(hotk->handle, "CWAP", wapf, NULL); | 1377 | if (write_acpi_int(asus->handle, "CWAP", wapf)) |
1106 | 1378 | pr_err("Error calling CWAP(%d)\n", wapf); | |
1107 | /* | 1379 | /* |
1108 | * Try to match the object returned by INIT to the specific model. | 1380 | * Try to match the object returned by INIT to the specific model. |
1109 | * Handle every possible object (or the lack of thereof) the DSDT | 1381 | * Handle every possible object (or the lack of thereof) the DSDT |
@@ -1124,406 +1396,210 @@ static int asus_hotk_get_info(void) | |||
1124 | break; | 1396 | break; |
1125 | } | 1397 | } |
1126 | } | 1398 | } |
1127 | hotk->name = kstrdup(string, GFP_KERNEL); | 1399 | asus->name = kstrdup(string, GFP_KERNEL); |
1128 | if (!hotk->name) | 1400 | if (!asus->name) |
1129 | return -ENOMEM; | 1401 | return -ENOMEM; |
1130 | 1402 | ||
1131 | if (*string) | 1403 | if (*string) |
1132 | pr_notice(" %s model detected\n", string); | 1404 | pr_notice(" %s model detected\n", string); |
1133 | 1405 | ||
1134 | ASUS_HANDLE_INIT(mled_set); | ||
1135 | ASUS_HANDLE_INIT(tled_set); | ||
1136 | ASUS_HANDLE_INIT(rled_set); | ||
1137 | ASUS_HANDLE_INIT(pled_set); | ||
1138 | ASUS_HANDLE_INIT(gled_set); | ||
1139 | |||
1140 | ASUS_HANDLE_INIT(ledd_set); | ||
1141 | |||
1142 | ASUS_HANDLE_INIT(kled_set); | ||
1143 | ASUS_HANDLE_INIT(kled_get); | ||
1144 | |||
1145 | /* | 1406 | /* |
1146 | * The HWRS method return informations about the hardware. | 1407 | * The HWRS method return informations about the hardware. |
1147 | * 0x80 bit is for WLAN, 0x100 for Bluetooth. | 1408 | * 0x80 bit is for WLAN, 0x100 for Bluetooth. |
1148 | * The significance of others is yet to be found. | 1409 | * The significance of others is yet to be found. |
1149 | * If we don't find the method, we assume the device are present. | ||
1150 | */ | 1410 | */ |
1151 | status = | 1411 | status = |
1152 | acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result); | 1412 | acpi_evaluate_integer(asus->handle, "HRWS", NULL, &hwrs_result); |
1153 | if (ACPI_FAILURE(status)) | 1413 | if (!ACPI_FAILURE(status)) |
1154 | hwrs_result = WL_HWRS | BT_HWRS; | 1414 | pr_notice(" HRWS returned %x", (int)hwrs_result); |
1155 | |||
1156 | if (hwrs_result & WL_HWRS) | ||
1157 | ASUS_HANDLE_INIT(wl_switch); | ||
1158 | if (hwrs_result & BT_HWRS) | ||
1159 | ASUS_HANDLE_INIT(bt_switch); | ||
1160 | 1415 | ||
1161 | ASUS_HANDLE_INIT(wireless_status); | 1416 | if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL)) |
1162 | 1417 | asus->have_rsts = true; | |
1163 | ASUS_HANDLE_INIT(brightness_set); | ||
1164 | ASUS_HANDLE_INIT(brightness_get); | ||
1165 | 1418 | ||
1419 | /* Scheduled for removal */ | ||
1166 | ASUS_HANDLE_INIT(lcd_switch); | 1420 | ASUS_HANDLE_INIT(lcd_switch); |
1167 | |||
1168 | ASUS_HANDLE_INIT(display_set); | ||
1169 | ASUS_HANDLE_INIT(display_get); | 1421 | ASUS_HANDLE_INIT(display_get); |
1170 | 1422 | ||
1171 | /* | ||
1172 | * There is a lot of models with "ALSL", but a few get | ||
1173 | * a real light sens, so we need to check it. | ||
1174 | */ | ||
1175 | if (!ASUS_HANDLE_INIT(ls_switch)) | ||
1176 | ASUS_HANDLE_INIT(ls_level); | ||
1177 | |||
1178 | ASUS_HANDLE_INIT(gps_on); | ||
1179 | ASUS_HANDLE_INIT(gps_off); | ||
1180 | ASUS_HANDLE_INIT(gps_status); | ||
1181 | |||
1182 | kfree(model); | 1423 | kfree(model); |
1183 | 1424 | ||
1184 | return AE_OK; | 1425 | return AE_OK; |
1185 | } | 1426 | } |
1186 | 1427 | ||
1187 | static int asus_input_init(void) | 1428 | static bool asus_device_present; |
1188 | { | ||
1189 | const struct key_entry *key; | ||
1190 | int result; | ||
1191 | |||
1192 | hotk->inputdev = input_allocate_device(); | ||
1193 | if (!hotk->inputdev) { | ||
1194 | pr_info("Unable to allocate input device\n"); | ||
1195 | return 0; | ||
1196 | } | ||
1197 | hotk->inputdev->name = "Asus Laptop extra buttons"; | ||
1198 | hotk->inputdev->phys = ASUS_HOTK_FILE "/input0"; | ||
1199 | hotk->inputdev->id.bustype = BUS_HOST; | ||
1200 | hotk->inputdev->getkeycode = asus_getkeycode; | ||
1201 | hotk->inputdev->setkeycode = asus_setkeycode; | ||
1202 | |||
1203 | for (key = asus_keymap; key->type != KE_END; key++) { | ||
1204 | switch (key->type) { | ||
1205 | case KE_KEY: | ||
1206 | set_bit(EV_KEY, hotk->inputdev->evbit); | ||
1207 | set_bit(key->keycode, hotk->inputdev->keybit); | ||
1208 | break; | ||
1209 | } | ||
1210 | } | ||
1211 | result = input_register_device(hotk->inputdev); | ||
1212 | if (result) { | ||
1213 | pr_info("Unable to register input device\n"); | ||
1214 | input_free_device(hotk->inputdev); | ||
1215 | } | ||
1216 | return result; | ||
1217 | } | ||
1218 | 1429 | ||
1219 | static int asus_hotk_check(void) | 1430 | static int __devinit asus_acpi_init(struct asus_laptop *asus) |
1220 | { | 1431 | { |
1221 | int result = 0; | 1432 | int result = 0; |
1222 | 1433 | ||
1223 | result = acpi_bus_get_status(hotk->device); | 1434 | result = acpi_bus_get_status(asus->device); |
1224 | if (result) | 1435 | if (result) |
1225 | return result; | 1436 | return result; |
1226 | 1437 | if (!asus->device->status.present) { | |
1227 | if (hotk->device->status.present) { | ||
1228 | result = asus_hotk_get_info(); | ||
1229 | } else { | ||
1230 | pr_err("Hotkey device not present, aborting\n"); | 1438 | pr_err("Hotkey device not present, aborting\n"); |
1231 | return -EINVAL; | 1439 | return -ENODEV; |
1232 | } | 1440 | } |
1233 | 1441 | ||
1234 | return result; | 1442 | result = asus_laptop_get_info(asus); |
1235 | } | ||
1236 | |||
1237 | static int asus_hotk_found; | ||
1238 | |||
1239 | static int asus_hotk_add(struct acpi_device *device) | ||
1240 | { | ||
1241 | int result; | ||
1242 | |||
1243 | if (!device) | ||
1244 | return -EINVAL; | ||
1245 | |||
1246 | pr_notice("Asus Laptop Support version %s\n", | ||
1247 | ASUS_LAPTOP_VERSION); | ||
1248 | |||
1249 | hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL); | ||
1250 | if (!hotk) | ||
1251 | return -ENOMEM; | ||
1252 | |||
1253 | hotk->handle = device->handle; | ||
1254 | strcpy(acpi_device_name(device), ASUS_HOTK_DEVICE_NAME); | ||
1255 | strcpy(acpi_device_class(device), ASUS_HOTK_CLASS); | ||
1256 | device->driver_data = hotk; | ||
1257 | hotk->device = device; | ||
1258 | |||
1259 | result = asus_hotk_check(); | ||
1260 | if (result) | 1443 | if (result) |
1261 | goto end; | 1444 | return result; |
1262 | |||
1263 | asus_hotk_add_fs(); | ||
1264 | |||
1265 | asus_hotk_found = 1; | ||
1266 | 1445 | ||
1267 | /* WLED and BLED are on by default */ | 1446 | /* WLED and BLED are on by default */ |
1268 | write_status(bt_switch_handle, 1, BT_ON); | 1447 | if (bluetooth_status >= 0) |
1269 | write_status(wl_switch_handle, 1, WL_ON); | 1448 | asus_bluetooth_set(asus, !!bluetooth_status); |
1270 | |||
1271 | /* If the h/w switch is off, we need to check the real status */ | ||
1272 | write_status(NULL, read_status(BT_ON), BT_ON); | ||
1273 | write_status(NULL, read_status(WL_ON), WL_ON); | ||
1274 | 1449 | ||
1275 | /* LCD Backlight is on by default */ | 1450 | if (wlan_status >= 0) |
1276 | write_status(NULL, 1, LCD_ON); | 1451 | asus_wlan_set(asus, !!wlan_status); |
1277 | 1452 | ||
1278 | /* Keyboard Backlight is on by default */ | 1453 | /* Keyboard Backlight is on by default */ |
1279 | if (kled_set_handle) | 1454 | if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL)) |
1280 | set_kled_lvl(1); | 1455 | asus_kled_set(asus, 1); |
1281 | 1456 | ||
1282 | /* LED display is off by default */ | 1457 | /* LED display is off by default */ |
1283 | hotk->ledd_status = 0xFFF; | 1458 | asus->ledd_status = 0xFFF; |
1284 | 1459 | ||
1285 | /* Set initial values of light sensor and level */ | 1460 | /* Set initial values of light sensor and level */ |
1286 | hotk->light_switch = 1; /* Default to light sensor disabled */ | 1461 | asus->light_switch = 0; /* Default to light sensor disabled */ |
1287 | hotk->light_level = 0; /* level 5 for sensor sensitivity */ | 1462 | asus->light_level = 5; /* level 5 for sensor sensitivity */ |
1288 | |||
1289 | if (ls_switch_handle) | ||
1290 | set_light_sens_switch(hotk->light_switch); | ||
1291 | 1463 | ||
1292 | if (ls_level_handle) | 1464 | if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && |
1293 | set_light_sens_level(hotk->light_level); | 1465 | !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { |
1294 | 1466 | asus_als_switch(asus, asus->light_switch); | |
1295 | /* GPS is on by default */ | 1467 | asus_als_level(asus, asus->light_level); |
1296 | write_status(NULL, 1, GPS_ON); | ||
1297 | |||
1298 | end: | ||
1299 | if (result) { | ||
1300 | kfree(hotk->name); | ||
1301 | kfree(hotk); | ||
1302 | } | 1468 | } |
1303 | 1469 | ||
1470 | asus->lcd_state = 1; /* LCD should be on when the module load */ | ||
1304 | return result; | 1471 | return result; |
1305 | } | 1472 | } |
1306 | 1473 | ||
1307 | static int asus_hotk_remove(struct acpi_device *device, int type) | 1474 | static int __devinit asus_acpi_add(struct acpi_device *device) |
1308 | { | 1475 | { |
1309 | if (!device || !acpi_driver_data(device)) | 1476 | struct asus_laptop *asus; |
1310 | return -EINVAL; | 1477 | int result; |
1311 | |||
1312 | kfree(hotk->name); | ||
1313 | kfree(hotk); | ||
1314 | |||
1315 | return 0; | ||
1316 | } | ||
1317 | |||
1318 | static void asus_backlight_exit(void) | ||
1319 | { | ||
1320 | if (asus_backlight_device) | ||
1321 | backlight_device_unregister(asus_backlight_device); | ||
1322 | } | ||
1323 | |||
1324 | #define ASUS_LED_UNREGISTER(object) \ | ||
1325 | if (object##_led.dev) \ | ||
1326 | led_classdev_unregister(&object##_led) | ||
1327 | 1478 | ||
1328 | static void asus_led_exit(void) | 1479 | pr_notice("Asus Laptop Support version %s\n", |
1329 | { | 1480 | ASUS_LAPTOP_VERSION); |
1330 | destroy_workqueue(led_workqueue); | 1481 | asus = kzalloc(sizeof(struct asus_laptop), GFP_KERNEL); |
1331 | ASUS_LED_UNREGISTER(mled); | 1482 | if (!asus) |
1332 | ASUS_LED_UNREGISTER(tled); | 1483 | return -ENOMEM; |
1333 | ASUS_LED_UNREGISTER(pled); | 1484 | asus->handle = device->handle; |
1334 | ASUS_LED_UNREGISTER(rled); | 1485 | strcpy(acpi_device_name(device), ASUS_LAPTOP_DEVICE_NAME); |
1335 | ASUS_LED_UNREGISTER(gled); | 1486 | strcpy(acpi_device_class(device), ASUS_LAPTOP_CLASS); |
1336 | ASUS_LED_UNREGISTER(kled); | 1487 | device->driver_data = asus; |
1337 | } | 1488 | asus->device = device; |
1338 | 1489 | ||
1339 | static void asus_input_exit(void) | 1490 | result = asus_acpi_init(asus); |
1340 | { | 1491 | if (result) |
1341 | if (hotk->inputdev) | 1492 | goto fail_platform; |
1342 | input_unregister_device(hotk->inputdev); | ||
1343 | } | ||
1344 | 1493 | ||
1345 | static void __exit asus_laptop_exit(void) | 1494 | /* |
1346 | { | 1495 | * Register the platform device first. It is used as a parent for the |
1347 | asus_backlight_exit(); | 1496 | * sub-devices below. |
1348 | asus_led_exit(); | 1497 | */ |
1349 | asus_input_exit(); | 1498 | result = asus_platform_init(asus); |
1499 | if (result) | ||
1500 | goto fail_platform; | ||
1350 | 1501 | ||
1351 | acpi_bus_unregister_driver(&asus_hotk_driver); | 1502 | if (!acpi_video_backlight_support()) { |
1352 | sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group); | 1503 | result = asus_backlight_init(asus); |
1353 | platform_device_unregister(asuspf_device); | 1504 | if (result) |
1354 | platform_driver_unregister(&asuspf_driver); | 1505 | goto fail_backlight; |
1355 | } | 1506 | } else |
1507 | pr_info("Backlight controlled by ACPI video driver\n"); | ||
1356 | 1508 | ||
1357 | static int asus_backlight_init(struct device *dev) | 1509 | result = asus_input_init(asus); |
1358 | { | 1510 | if (result) |
1359 | struct backlight_device *bd; | 1511 | goto fail_input; |
1360 | 1512 | ||
1361 | if (brightness_set_handle && lcd_switch_handle) { | 1513 | result = asus_led_init(asus); |
1362 | bd = backlight_device_register(ASUS_HOTK_FILE, dev, | 1514 | if (result) |
1363 | NULL, &asusbl_ops); | 1515 | goto fail_led; |
1364 | if (IS_ERR(bd)) { | ||
1365 | pr_err("Could not register asus backlight device\n"); | ||
1366 | asus_backlight_device = NULL; | ||
1367 | return PTR_ERR(bd); | ||
1368 | } | ||
1369 | 1516 | ||
1370 | asus_backlight_device = bd; | 1517 | result = asus_rfkill_init(asus); |
1518 | if (result) | ||
1519 | goto fail_rfkill; | ||
1371 | 1520 | ||
1372 | bd->props.max_brightness = 15; | 1521 | asus_device_present = true; |
1373 | bd->props.brightness = read_brightness(NULL); | ||
1374 | bd->props.power = FB_BLANK_UNBLANK; | ||
1375 | backlight_update_status(bd); | ||
1376 | } | ||
1377 | return 0; | 1522 | return 0; |
1378 | } | ||
1379 | 1523 | ||
1380 | static int asus_led_register(acpi_handle handle, | 1524 | fail_rfkill: |
1381 | struct led_classdev *ldev, struct device *dev) | 1525 | asus_led_exit(asus); |
1382 | { | 1526 | fail_led: |
1383 | if (!handle) | 1527 | asus_input_exit(asus); |
1384 | return 0; | 1528 | fail_input: |
1529 | asus_backlight_exit(asus); | ||
1530 | fail_backlight: | ||
1531 | asus_platform_exit(asus); | ||
1532 | fail_platform: | ||
1533 | kfree(asus->name); | ||
1534 | kfree(asus); | ||
1385 | 1535 | ||
1386 | return led_classdev_register(dev, ldev); | 1536 | return result; |
1387 | } | 1537 | } |
1388 | 1538 | ||
1389 | #define ASUS_LED_REGISTER(object, device) \ | 1539 | static int asus_acpi_remove(struct acpi_device *device, int type) |
1390 | asus_led_register(object##_set_handle, &object##_led, device) | ||
1391 | |||
1392 | static int asus_led_init(struct device *dev) | ||
1393 | { | 1540 | { |
1394 | int rv; | 1541 | struct asus_laptop *asus = acpi_driver_data(device); |
1395 | |||
1396 | rv = ASUS_LED_REGISTER(mled, dev); | ||
1397 | if (rv) | ||
1398 | goto out; | ||
1399 | |||
1400 | rv = ASUS_LED_REGISTER(tled, dev); | ||
1401 | if (rv) | ||
1402 | goto out1; | ||
1403 | |||
1404 | rv = ASUS_LED_REGISTER(rled, dev); | ||
1405 | if (rv) | ||
1406 | goto out2; | ||
1407 | |||
1408 | rv = ASUS_LED_REGISTER(pled, dev); | ||
1409 | if (rv) | ||
1410 | goto out3; | ||
1411 | |||
1412 | rv = ASUS_LED_REGISTER(gled, dev); | ||
1413 | if (rv) | ||
1414 | goto out4; | ||
1415 | 1542 | ||
1416 | if (kled_set_handle && kled_get_handle) | 1543 | asus_backlight_exit(asus); |
1417 | rv = ASUS_LED_REGISTER(kled, dev); | 1544 | asus_rfkill_exit(asus); |
1418 | if (rv) | 1545 | asus_led_exit(asus); |
1419 | goto out5; | 1546 | asus_input_exit(asus); |
1420 | 1547 | asus_platform_exit(asus); | |
1421 | led_workqueue = create_singlethread_workqueue("led_workqueue"); | ||
1422 | if (!led_workqueue) | ||
1423 | goto out6; | ||
1424 | 1548 | ||
1549 | kfree(asus->name); | ||
1550 | kfree(asus); | ||
1425 | return 0; | 1551 | return 0; |
1426 | out6: | ||
1427 | rv = -ENOMEM; | ||
1428 | ASUS_LED_UNREGISTER(kled); | ||
1429 | out5: | ||
1430 | ASUS_LED_UNREGISTER(gled); | ||
1431 | out4: | ||
1432 | ASUS_LED_UNREGISTER(pled); | ||
1433 | out3: | ||
1434 | ASUS_LED_UNREGISTER(rled); | ||
1435 | out2: | ||
1436 | ASUS_LED_UNREGISTER(tled); | ||
1437 | out1: | ||
1438 | ASUS_LED_UNREGISTER(mled); | ||
1439 | out: | ||
1440 | return rv; | ||
1441 | } | 1552 | } |
1442 | 1553 | ||
1554 | static const struct acpi_device_id asus_device_ids[] = { | ||
1555 | {"ATK0100", 0}, | ||
1556 | {"ATK0101", 0}, | ||
1557 | {"", 0}, | ||
1558 | }; | ||
1559 | MODULE_DEVICE_TABLE(acpi, asus_device_ids); | ||
1560 | |||
1561 | static struct acpi_driver asus_acpi_driver = { | ||
1562 | .name = ASUS_LAPTOP_NAME, | ||
1563 | .class = ASUS_LAPTOP_CLASS, | ||
1564 | .owner = THIS_MODULE, | ||
1565 | .ids = asus_device_ids, | ||
1566 | .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, | ||
1567 | .ops = { | ||
1568 | .add = asus_acpi_add, | ||
1569 | .remove = asus_acpi_remove, | ||
1570 | .notify = asus_acpi_notify, | ||
1571 | }, | ||
1572 | }; | ||
1573 | |||
1443 | static int __init asus_laptop_init(void) | 1574 | static int __init asus_laptop_init(void) |
1444 | { | 1575 | { |
1445 | int result; | 1576 | int result; |
1446 | 1577 | ||
1447 | if (acpi_disabled) | 1578 | result = platform_driver_register(&platform_driver); |
1448 | return -ENODEV; | ||
1449 | |||
1450 | result = acpi_bus_register_driver(&asus_hotk_driver); | ||
1451 | if (result < 0) | 1579 | if (result < 0) |
1452 | return result; | 1580 | return result; |
1453 | 1581 | ||
1454 | /* | 1582 | result = acpi_bus_register_driver(&asus_acpi_driver); |
1455 | * This is a bit of a kludge. We only want this module loaded | 1583 | if (result < 0) |
1456 | * for ASUS systems, but there's currently no way to probe the | 1584 | goto fail_acpi_driver; |
1457 | * ACPI namespace for ASUS HIDs. So we just return failure if | 1585 | if (!asus_device_present) { |
1458 | * we didn't find one, which will cause the module to be | 1586 | result = -ENODEV; |
1459 | * unloaded. | 1587 | goto fail_no_device; |
1460 | */ | ||
1461 | if (!asus_hotk_found) { | ||
1462 | acpi_bus_unregister_driver(&asus_hotk_driver); | ||
1463 | return -ENODEV; | ||
1464 | } | ||
1465 | |||
1466 | result = asus_input_init(); | ||
1467 | if (result) | ||
1468 | goto fail_input; | ||
1469 | |||
1470 | /* Register platform stuff */ | ||
1471 | result = platform_driver_register(&asuspf_driver); | ||
1472 | if (result) | ||
1473 | goto fail_platform_driver; | ||
1474 | |||
1475 | asuspf_device = platform_device_alloc(ASUS_HOTK_FILE, -1); | ||
1476 | if (!asuspf_device) { | ||
1477 | result = -ENOMEM; | ||
1478 | goto fail_platform_device1; | ||
1479 | } | 1588 | } |
1480 | |||
1481 | result = platform_device_add(asuspf_device); | ||
1482 | if (result) | ||
1483 | goto fail_platform_device2; | ||
1484 | |||
1485 | result = sysfs_create_group(&asuspf_device->dev.kobj, | ||
1486 | &asuspf_attribute_group); | ||
1487 | if (result) | ||
1488 | goto fail_sysfs; | ||
1489 | |||
1490 | result = asus_led_init(&asuspf_device->dev); | ||
1491 | if (result) | ||
1492 | goto fail_led; | ||
1493 | |||
1494 | if (!acpi_video_backlight_support()) { | ||
1495 | result = asus_backlight_init(&asuspf_device->dev); | ||
1496 | if (result) | ||
1497 | goto fail_backlight; | ||
1498 | } else | ||
1499 | pr_info("Brightness ignored, must be controlled by " | ||
1500 | "ACPI video driver\n"); | ||
1501 | |||
1502 | return 0; | 1589 | return 0; |
1503 | 1590 | ||
1504 | fail_backlight: | 1591 | fail_no_device: |
1505 | asus_led_exit(); | 1592 | acpi_bus_unregister_driver(&asus_acpi_driver); |
1506 | 1593 | fail_acpi_driver: | |
1507 | fail_led: | 1594 | platform_driver_unregister(&platform_driver); |
1508 | sysfs_remove_group(&asuspf_device->dev.kobj, | ||
1509 | &asuspf_attribute_group); | ||
1510 | |||
1511 | fail_sysfs: | ||
1512 | platform_device_del(asuspf_device); | ||
1513 | |||
1514 | fail_platform_device2: | ||
1515 | platform_device_put(asuspf_device); | ||
1516 | |||
1517 | fail_platform_device1: | ||
1518 | platform_driver_unregister(&asuspf_driver); | ||
1519 | |||
1520 | fail_platform_driver: | ||
1521 | asus_input_exit(); | ||
1522 | |||
1523 | fail_input: | ||
1524 | |||
1525 | return result; | 1595 | return result; |
1526 | } | 1596 | } |
1527 | 1597 | ||
1598 | static void __exit asus_laptop_exit(void) | ||
1599 | { | ||
1600 | acpi_bus_unregister_driver(&asus_acpi_driver); | ||
1601 | platform_driver_unregister(&platform_driver); | ||
1602 | } | ||
1603 | |||
1528 | module_init(asus_laptop_init); | 1604 | module_init(asus_laptop_init); |
1529 | module_exit(asus_laptop_exit); | 1605 | module_exit(asus_laptop_exit); |
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index ddf5240ade8c..92fd30c9379c 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c | |||
@@ -32,9 +32,11 @@ | |||
32 | 32 | ||
33 | #include <linux/kernel.h> | 33 | #include <linux/kernel.h> |
34 | #include <linux/module.h> | 34 | #include <linux/module.h> |
35 | #include <linux/slab.h> | ||
35 | #include <linux/init.h> | 36 | #include <linux/init.h> |
36 | #include <linux/types.h> | 37 | #include <linux/types.h> |
37 | #include <linux/proc_fs.h> | 38 | #include <linux/proc_fs.h> |
39 | #include <linux/seq_file.h> | ||
38 | #include <linux/backlight.h> | 40 | #include <linux/backlight.h> |
39 | #include <acpi/acpi_drivers.h> | 41 | #include <acpi/acpi_drivers.h> |
40 | #include <acpi/acpi_bus.h> | 42 | #include <acpi/acpi_bus.h> |
@@ -466,6 +468,7 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids); | |||
466 | static struct acpi_driver asus_hotk_driver = { | 468 | static struct acpi_driver asus_hotk_driver = { |
467 | .name = "asus_acpi", | 469 | .name = "asus_acpi", |
468 | .class = ACPI_HOTK_CLASS, | 470 | .class = ACPI_HOTK_CLASS, |
471 | .owner = THIS_MODULE, | ||
469 | .ids = asus_device_ids, | 472 | .ids = asus_device_ids, |
470 | .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, | 473 | .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, |
471 | .ops = { | 474 | .ops = { |
@@ -512,26 +515,12 @@ static int read_acpi_int(acpi_handle handle, const char *method, int *val) | |||
512 | return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER); | 515 | return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER); |
513 | } | 516 | } |
514 | 517 | ||
515 | /* | 518 | static int asus_info_proc_show(struct seq_file *m, void *v) |
516 | * We write our info in page, we begin at offset off and cannot write more | ||
517 | * than count bytes. We set eof to 1 if we handle those 2 values. We return the | ||
518 | * number of bytes written in page | ||
519 | */ | ||
520 | static int | ||
521 | proc_read_info(char *page, char **start, off_t off, int count, int *eof, | ||
522 | void *data) | ||
523 | { | 519 | { |
524 | int len = 0; | ||
525 | int temp; | 520 | int temp; |
526 | char buf[16]; /* enough for all info */ | ||
527 | /* | ||
528 | * We use the easy way, we don't care of off and count, | ||
529 | * so we don't set eof to 1 | ||
530 | */ | ||
531 | 521 | ||
532 | len += sprintf(page, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n"); | 522 | seq_printf(m, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n"); |
533 | len += sprintf(page + len, "Model reference : %s\n", | 523 | seq_printf(m, "Model reference : %s\n", hotk->methods->name); |
534 | hotk->methods->name); | ||
535 | /* | 524 | /* |
536 | * The SFUN method probably allows the original driver to get the list | 525 | * The SFUN method probably allows the original driver to get the list |
537 | * of features supported by a given model. For now, 0x0100 or 0x0800 | 526 | * of features supported by a given model. For now, 0x0100 or 0x0800 |
@@ -539,8 +528,7 @@ proc_read_info(char *page, char **start, off_t off, int count, int *eof, | |||
539 | * The significance of others is yet to be found. | 528 | * The significance of others is yet to be found. |
540 | */ | 529 | */ |
541 | if (read_acpi_int(hotk->handle, "SFUN", &temp)) | 530 | if (read_acpi_int(hotk->handle, "SFUN", &temp)) |
542 | len += | 531 | seq_printf(m, "SFUN value : 0x%04x\n", temp); |
543 | sprintf(page + len, "SFUN value : 0x%04x\n", temp); | ||
544 | /* | 532 | /* |
545 | * Another value for userspace: the ASYM method returns 0x02 for | 533 | * Another value for userspace: the ASYM method returns 0x02 for |
546 | * battery low and 0x04 for battery critical, its readings tend to be | 534 | * battery low and 0x04 for battery critical, its readings tend to be |
@@ -549,30 +537,34 @@ proc_read_info(char *page, char **start, off_t off, int count, int *eof, | |||
549 | * silently ignored. | 537 | * silently ignored. |
550 | */ | 538 | */ |
551 | if (read_acpi_int(hotk->handle, "ASYM", &temp)) | 539 | if (read_acpi_int(hotk->handle, "ASYM", &temp)) |
552 | len += | 540 | seq_printf(m, "ASYM value : 0x%04x\n", temp); |
553 | sprintf(page + len, "ASYM value : 0x%04x\n", temp); | ||
554 | if (asus_info) { | 541 | if (asus_info) { |
555 | snprintf(buf, 16, "%d", asus_info->length); | 542 | seq_printf(m, "DSDT length : %d\n", asus_info->length); |
556 | len += sprintf(page + len, "DSDT length : %s\n", buf); | 543 | seq_printf(m, "DSDT checksum : %d\n", asus_info->checksum); |
557 | snprintf(buf, 16, "%d", asus_info->checksum); | 544 | seq_printf(m, "DSDT revision : %d\n", asus_info->revision); |
558 | len += sprintf(page + len, "DSDT checksum : %s\n", buf); | 545 | seq_printf(m, "OEM id : %.*s\n", ACPI_OEM_ID_SIZE, asus_info->oem_id); |
559 | snprintf(buf, 16, "%d", asus_info->revision); | 546 | seq_printf(m, "OEM table id : %.*s\n", ACPI_OEM_TABLE_ID_SIZE, asus_info->oem_table_id); |
560 | len += sprintf(page + len, "DSDT revision : %s\n", buf); | 547 | seq_printf(m, "OEM revision : 0x%x\n", asus_info->oem_revision); |
561 | snprintf(buf, 7, "%s", asus_info->oem_id); | 548 | seq_printf(m, "ASL comp vendor id : %.*s\n", ACPI_NAME_SIZE, asus_info->asl_compiler_id); |
562 | len += sprintf(page + len, "OEM id : %s\n", buf); | 549 | seq_printf(m, "ASL comp revision : 0x%x\n", asus_info->asl_compiler_revision); |
563 | snprintf(buf, 9, "%s", asus_info->oem_table_id); | ||
564 | len += sprintf(page + len, "OEM table id : %s\n", buf); | ||
565 | snprintf(buf, 16, "%x", asus_info->oem_revision); | ||
566 | len += sprintf(page + len, "OEM revision : 0x%s\n", buf); | ||
567 | snprintf(buf, 5, "%s", asus_info->asl_compiler_id); | ||
568 | len += sprintf(page + len, "ASL comp vendor id : %s\n", buf); | ||
569 | snprintf(buf, 16, "%x", asus_info->asl_compiler_revision); | ||
570 | len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf); | ||
571 | } | 550 | } |
572 | 551 | ||
573 | return len; | 552 | return 0; |
553 | } | ||
554 | |||
555 | static int asus_info_proc_open(struct inode *inode, struct file *file) | ||
556 | { | ||
557 | return single_open(file, asus_info_proc_show, NULL); | ||
574 | } | 558 | } |
575 | 559 | ||
560 | static const struct file_operations asus_info_proc_fops = { | ||
561 | .owner = THIS_MODULE, | ||
562 | .open = asus_info_proc_open, | ||
563 | .read = seq_read, | ||
564 | .llseek = seq_lseek, | ||
565 | .release = single_release, | ||
566 | }; | ||
567 | |||
576 | /* | 568 | /* |
577 | * /proc handlers | 569 | * /proc handlers |
578 | * We write our info in page, we begin at offset off and cannot write more | 570 | * We write our info in page, we begin at offset off and cannot write more |
@@ -638,34 +630,48 @@ write_led(const char __user *buffer, unsigned long count, | |||
638 | /* | 630 | /* |
639 | * Proc handlers for MLED | 631 | * Proc handlers for MLED |
640 | */ | 632 | */ |
641 | static int | 633 | static int mled_proc_show(struct seq_file *m, void *v) |
642 | proc_read_mled(char *page, char **start, off_t off, int count, int *eof, | ||
643 | void *data) | ||
644 | { | 634 | { |
645 | return sprintf(page, "%d\n", | 635 | seq_printf(m, "%d\n", read_led(hotk->methods->mled_status, MLED_ON)); |
646 | read_led(hotk->methods->mled_status, MLED_ON)); | 636 | return 0; |
647 | } | 637 | } |
648 | 638 | ||
649 | static int | 639 | static int mled_proc_open(struct inode *inode, struct file *file) |
650 | proc_write_mled(struct file *file, const char __user *buffer, | 640 | { |
651 | unsigned long count, void *data) | 641 | return single_open(file, mled_proc_show, NULL); |
642 | } | ||
643 | |||
644 | static ssize_t mled_proc_write(struct file *file, const char __user *buffer, | ||
645 | size_t count, loff_t *pos) | ||
652 | { | 646 | { |
653 | return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1); | 647 | return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1); |
654 | } | 648 | } |
655 | 649 | ||
650 | static const struct file_operations mled_proc_fops = { | ||
651 | .owner = THIS_MODULE, | ||
652 | .open = mled_proc_open, | ||
653 | .read = seq_read, | ||
654 | .llseek = seq_lseek, | ||
655 | .release = single_release, | ||
656 | .write = mled_proc_write, | ||
657 | }; | ||
658 | |||
656 | /* | 659 | /* |
657 | * Proc handlers for LED display | 660 | * Proc handlers for LED display |
658 | */ | 661 | */ |
659 | static int | 662 | static int ledd_proc_show(struct seq_file *m, void *v) |
660 | proc_read_ledd(char *page, char **start, off_t off, int count, int *eof, | ||
661 | void *data) | ||
662 | { | 663 | { |
663 | return sprintf(page, "0x%08x\n", hotk->ledd_status); | 664 | seq_printf(m, "0x%08x\n", hotk->ledd_status); |
665 | return 0; | ||
664 | } | 666 | } |
665 | 667 | ||
666 | static int | 668 | static int ledd_proc_open(struct inode *inode, struct file *file) |
667 | proc_write_ledd(struct file *file, const char __user *buffer, | 669 | { |
668 | unsigned long count, void *data) | 670 | return single_open(file, ledd_proc_show, NULL); |
671 | } | ||
672 | |||
673 | static ssize_t ledd_proc_write(struct file *file, const char __user *buffer, | ||
674 | size_t count, loff_t *pos) | ||
669 | { | 675 | { |
670 | int rv, value; | 676 | int rv, value; |
671 | 677 | ||
@@ -681,61 +687,104 @@ proc_write_ledd(struct file *file, const char __user *buffer, | |||
681 | return rv; | 687 | return rv; |
682 | } | 688 | } |
683 | 689 | ||
690 | static const struct file_operations ledd_proc_fops = { | ||
691 | .owner = THIS_MODULE, | ||
692 | .open = ledd_proc_open, | ||
693 | .read = seq_read, | ||
694 | .llseek = seq_lseek, | ||
695 | .release = single_release, | ||
696 | .write = ledd_proc_write, | ||
697 | }; | ||
698 | |||
684 | /* | 699 | /* |
685 | * Proc handlers for WLED | 700 | * Proc handlers for WLED |
686 | */ | 701 | */ |
687 | static int | 702 | static int wled_proc_show(struct seq_file *m, void *v) |
688 | proc_read_wled(char *page, char **start, off_t off, int count, int *eof, | ||
689 | void *data) | ||
690 | { | 703 | { |
691 | return sprintf(page, "%d\n", | 704 | seq_printf(m, "%d\n", read_led(hotk->methods->wled_status, WLED_ON)); |
692 | read_led(hotk->methods->wled_status, WLED_ON)); | 705 | return 0; |
693 | } | 706 | } |
694 | 707 | ||
695 | static int | 708 | static int wled_proc_open(struct inode *inode, struct file *file) |
696 | proc_write_wled(struct file *file, const char __user *buffer, | 709 | { |
697 | unsigned long count, void *data) | 710 | return single_open(file, wled_proc_show, NULL); |
711 | } | ||
712 | |||
713 | static ssize_t wled_proc_write(struct file *file, const char __user *buffer, | ||
714 | size_t count, loff_t *pos) | ||
698 | { | 715 | { |
699 | return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0); | 716 | return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0); |
700 | } | 717 | } |
701 | 718 | ||
719 | static const struct file_operations wled_proc_fops = { | ||
720 | .owner = THIS_MODULE, | ||
721 | .open = wled_proc_open, | ||
722 | .read = seq_read, | ||
723 | .llseek = seq_lseek, | ||
724 | .release = single_release, | ||
725 | .write = wled_proc_write, | ||
726 | }; | ||
727 | |||
702 | /* | 728 | /* |
703 | * Proc handlers for Bluetooth | 729 | * Proc handlers for Bluetooth |
704 | */ | 730 | */ |
705 | static int | 731 | static int bluetooth_proc_show(struct seq_file *m, void *v) |
706 | proc_read_bluetooth(char *page, char **start, off_t off, int count, int *eof, | ||
707 | void *data) | ||
708 | { | 732 | { |
709 | return sprintf(page, "%d\n", read_led(hotk->methods->bt_status, BT_ON)); | 733 | seq_printf(m, "%d\n", read_led(hotk->methods->bt_status, BT_ON)); |
734 | return 0; | ||
710 | } | 735 | } |
711 | 736 | ||
712 | static int | 737 | static int bluetooth_proc_open(struct inode *inode, struct file *file) |
713 | proc_write_bluetooth(struct file *file, const char __user *buffer, | 738 | { |
714 | unsigned long count, void *data) | 739 | return single_open(file, bluetooth_proc_show, NULL); |
740 | } | ||
741 | |||
742 | static ssize_t bluetooth_proc_write(struct file *file, | ||
743 | const char __user *buffer, size_t count, loff_t *pos) | ||
715 | { | 744 | { |
716 | /* Note: mt_bt_switch controls both internal Bluetooth adapter's | 745 | /* Note: mt_bt_switch controls both internal Bluetooth adapter's |
717 | presence and its LED */ | 746 | presence and its LED */ |
718 | return write_led(buffer, count, hotk->methods->mt_bt_switch, BT_ON, 0); | 747 | return write_led(buffer, count, hotk->methods->mt_bt_switch, BT_ON, 0); |
719 | } | 748 | } |
720 | 749 | ||
750 | static const struct file_operations bluetooth_proc_fops = { | ||
751 | .owner = THIS_MODULE, | ||
752 | .open = bluetooth_proc_open, | ||
753 | .read = seq_read, | ||
754 | .llseek = seq_lseek, | ||
755 | .release = single_release, | ||
756 | .write = bluetooth_proc_write, | ||
757 | }; | ||
758 | |||
721 | /* | 759 | /* |
722 | * Proc handlers for TLED | 760 | * Proc handlers for TLED |
723 | */ | 761 | */ |
724 | static int | 762 | static int tled_proc_show(struct seq_file *m, void *v) |
725 | proc_read_tled(char *page, char **start, off_t off, int count, int *eof, | ||
726 | void *data) | ||
727 | { | 763 | { |
728 | return sprintf(page, "%d\n", | 764 | seq_printf(m, "%d\n", read_led(hotk->methods->tled_status, TLED_ON)); |
729 | read_led(hotk->methods->tled_status, TLED_ON)); | 765 | return 0; |
730 | } | 766 | } |
731 | 767 | ||
732 | static int | 768 | static int tled_proc_open(struct inode *inode, struct file *file) |
733 | proc_write_tled(struct file *file, const char __user *buffer, | 769 | { |
734 | unsigned long count, void *data) | 770 | return single_open(file, tled_proc_show, NULL); |
771 | } | ||
772 | |||
773 | static ssize_t tled_proc_write(struct file *file, const char __user *buffer, | ||
774 | size_t count, loff_t *pos) | ||
735 | { | 775 | { |
736 | return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0); | 776 | return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0); |
737 | } | 777 | } |
738 | 778 | ||
779 | static const struct file_operations tled_proc_fops = { | ||
780 | .owner = THIS_MODULE, | ||
781 | .open = tled_proc_open, | ||
782 | .read = seq_read, | ||
783 | .llseek = seq_lseek, | ||
784 | .release = single_release, | ||
785 | .write = tled_proc_write, | ||
786 | }; | ||
787 | |||
739 | static int get_lcd_state(void) | 788 | static int get_lcd_state(void) |
740 | { | 789 | { |
741 | int lcd = 0; | 790 | int lcd = 0; |
@@ -828,16 +877,19 @@ static int set_lcd_state(int value) | |||
828 | 877 | ||
829 | } | 878 | } |
830 | 879 | ||
831 | static int | 880 | static int lcd_proc_show(struct seq_file *m, void *v) |
832 | proc_read_lcd(char *page, char **start, off_t off, int count, int *eof, | ||
833 | void *data) | ||
834 | { | 881 | { |
835 | return sprintf(page, "%d\n", get_lcd_state()); | 882 | seq_printf(m, "%d\n", get_lcd_state()); |
883 | return 0; | ||
836 | } | 884 | } |
837 | 885 | ||
838 | static int | 886 | static int lcd_proc_open(struct inode *inode, struct file *file) |
839 | proc_write_lcd(struct file *file, const char __user *buffer, | 887 | { |
840 | unsigned long count, void *data) | 888 | return single_open(file, lcd_proc_show, NULL); |
889 | } | ||
890 | |||
891 | static ssize_t lcd_proc_write(struct file *file, const char __user *buffer, | ||
892 | size_t count, loff_t *pos) | ||
841 | { | 893 | { |
842 | int rv, value; | 894 | int rv, value; |
843 | 895 | ||
@@ -847,6 +899,15 @@ proc_write_lcd(struct file *file, const char __user *buffer, | |||
847 | return rv; | 899 | return rv; |
848 | } | 900 | } |
849 | 901 | ||
902 | static const struct file_operations lcd_proc_fops = { | ||
903 | .owner = THIS_MODULE, | ||
904 | .open = lcd_proc_open, | ||
905 | .read = seq_read, | ||
906 | .llseek = seq_lseek, | ||
907 | .release = single_release, | ||
908 | .write = lcd_proc_write, | ||
909 | }; | ||
910 | |||
850 | static int read_brightness(struct backlight_device *bd) | 911 | static int read_brightness(struct backlight_device *bd) |
851 | { | 912 | { |
852 | int value; | 913 | int value; |
@@ -906,16 +967,19 @@ static int set_brightness_status(struct backlight_device *bd) | |||
906 | return set_brightness(bd->props.brightness); | 967 | return set_brightness(bd->props.brightness); |
907 | } | 968 | } |
908 | 969 | ||
909 | static int | 970 | static int brn_proc_show(struct seq_file *m, void *v) |
910 | proc_read_brn(char *page, char **start, off_t off, int count, int *eof, | ||
911 | void *data) | ||
912 | { | 971 | { |
913 | return sprintf(page, "%d\n", read_brightness(NULL)); | 972 | seq_printf(m, "%d\n", read_brightness(NULL)); |
973 | return 0; | ||
914 | } | 974 | } |
915 | 975 | ||
916 | static int | 976 | static int brn_proc_open(struct inode *inode, struct file *file) |
917 | proc_write_brn(struct file *file, const char __user *buffer, | 977 | { |
918 | unsigned long count, void *data) | 978 | return single_open(file, brn_proc_show, NULL); |
979 | } | ||
980 | |||
981 | static ssize_t brn_proc_write(struct file *file, const char __user *buffer, | ||
982 | size_t count, loff_t *pos) | ||
919 | { | 983 | { |
920 | int rv, value; | 984 | int rv, value; |
921 | 985 | ||
@@ -928,6 +992,15 @@ proc_write_brn(struct file *file, const char __user *buffer, | |||
928 | return rv; | 992 | return rv; |
929 | } | 993 | } |
930 | 994 | ||
995 | static const struct file_operations brn_proc_fops = { | ||
996 | .owner = THIS_MODULE, | ||
997 | .open = brn_proc_open, | ||
998 | .read = seq_read, | ||
999 | .llseek = seq_lseek, | ||
1000 | .release = single_release, | ||
1001 | .write = brn_proc_write, | ||
1002 | }; | ||
1003 | |||
931 | static void set_display(int value) | 1004 | static void set_display(int value) |
932 | { | 1005 | { |
933 | /* no sanity check needed for now */ | 1006 | /* no sanity check needed for now */ |
@@ -941,9 +1014,7 @@ static void set_display(int value) | |||
941 | * Now, *this* one could be more user-friendly, but so far, no-one has | 1014 | * Now, *this* one could be more user-friendly, but so far, no-one has |
942 | * complained. The significance of bits is the same as in proc_write_disp() | 1015 | * complained. The significance of bits is the same as in proc_write_disp() |
943 | */ | 1016 | */ |
944 | static int | 1017 | static int disp_proc_show(struct seq_file *m, void *v) |
945 | proc_read_disp(char *page, char **start, off_t off, int count, int *eof, | ||
946 | void *data) | ||
947 | { | 1018 | { |
948 | int value = 0; | 1019 | int value = 0; |
949 | 1020 | ||
@@ -951,7 +1022,13 @@ proc_read_disp(char *page, char **start, off_t off, int count, int *eof, | |||
951 | printk(KERN_WARNING | 1022 | printk(KERN_WARNING |
952 | "Asus ACPI: Error reading display status\n"); | 1023 | "Asus ACPI: Error reading display status\n"); |
953 | value &= 0x07; /* needed for some models, shouldn't hurt others */ | 1024 | value &= 0x07; /* needed for some models, shouldn't hurt others */ |
954 | return sprintf(page, "%d\n", value); | 1025 | seq_printf(m, "%d\n", value); |
1026 | return 0; | ||
1027 | } | ||
1028 | |||
1029 | static int disp_proc_open(struct inode *inode, struct file *file) | ||
1030 | { | ||
1031 | return single_open(file, disp_proc_show, NULL); | ||
955 | } | 1032 | } |
956 | 1033 | ||
957 | /* | 1034 | /* |
@@ -960,9 +1037,8 @@ proc_read_disp(char *page, char **start, off_t off, int count, int *eof, | |||
960 | * (bitwise) of these will suffice. I never actually tested 3 displays hooked | 1037 | * (bitwise) of these will suffice. I never actually tested 3 displays hooked |
961 | * up simultaneously, so be warned. See the acpi4asus README for more info. | 1038 | * up simultaneously, so be warned. See the acpi4asus README for more info. |
962 | */ | 1039 | */ |
963 | static int | 1040 | static ssize_t disp_proc_write(struct file *file, const char __user *buffer, |
964 | proc_write_disp(struct file *file, const char __user *buffer, | 1041 | size_t count, loff_t *pos) |
965 | unsigned long count, void *data) | ||
966 | { | 1042 | { |
967 | int rv, value; | 1043 | int rv, value; |
968 | 1044 | ||
@@ -972,25 +1048,27 @@ proc_write_disp(struct file *file, const char __user *buffer, | |||
972 | return rv; | 1048 | return rv; |
973 | } | 1049 | } |
974 | 1050 | ||
975 | typedef int (proc_readfunc) (char *page, char **start, off_t off, int count, | 1051 | static const struct file_operations disp_proc_fops = { |
976 | int *eof, void *data); | 1052 | .owner = THIS_MODULE, |
977 | typedef int (proc_writefunc) (struct file *file, const char __user *buffer, | 1053 | .open = disp_proc_open, |
978 | unsigned long count, void *data); | 1054 | .read = seq_read, |
1055 | .llseek = seq_lseek, | ||
1056 | .release = single_release, | ||
1057 | .write = disp_proc_write, | ||
1058 | }; | ||
979 | 1059 | ||
980 | static int | 1060 | static int |
981 | asus_proc_add(char *name, proc_writefunc *writefunc, | 1061 | asus_proc_add(char *name, const struct file_operations *proc_fops, mode_t mode, |
982 | proc_readfunc *readfunc, mode_t mode, | ||
983 | struct acpi_device *device) | 1062 | struct acpi_device *device) |
984 | { | 1063 | { |
985 | struct proc_dir_entry *proc = | 1064 | struct proc_dir_entry *proc; |
986 | create_proc_entry(name, mode, acpi_device_dir(device)); | 1065 | |
1066 | proc = proc_create_data(name, mode, acpi_device_dir(device), | ||
1067 | proc_fops, acpi_driver_data(device)); | ||
987 | if (!proc) { | 1068 | if (!proc) { |
988 | printk(KERN_WARNING " Unable to create %s fs entry\n", name); | 1069 | printk(KERN_WARNING " Unable to create %s fs entry\n", name); |
989 | return -1; | 1070 | return -1; |
990 | } | 1071 | } |
991 | proc->write_proc = writefunc; | ||
992 | proc->read_proc = readfunc; | ||
993 | proc->data = acpi_driver_data(device); | ||
994 | proc->uid = asus_uid; | 1072 | proc->uid = asus_uid; |
995 | proc->gid = asus_gid; | 1073 | proc->gid = asus_gid; |
996 | return 0; | 1074 | return 0; |
@@ -1019,10 +1097,9 @@ static int asus_hotk_add_fs(struct acpi_device *device) | |||
1019 | if (!acpi_device_dir(device)) | 1097 | if (!acpi_device_dir(device)) |
1020 | return -ENODEV; | 1098 | return -ENODEV; |
1021 | 1099 | ||
1022 | proc = create_proc_entry(PROC_INFO, mode, acpi_device_dir(device)); | 1100 | proc = proc_create(PROC_INFO, mode, acpi_device_dir(device), |
1101 | &asus_info_proc_fops); | ||
1023 | if (proc) { | 1102 | if (proc) { |
1024 | proc->read_proc = proc_read_info; | ||
1025 | proc->data = acpi_driver_data(device); | ||
1026 | proc->uid = asus_uid; | 1103 | proc->uid = asus_uid; |
1027 | proc->gid = asus_gid; | 1104 | proc->gid = asus_gid; |
1028 | } else { | 1105 | } else { |
@@ -1031,28 +1108,23 @@ static int asus_hotk_add_fs(struct acpi_device *device) | |||
1031 | } | 1108 | } |
1032 | 1109 | ||
1033 | if (hotk->methods->mt_wled) { | 1110 | if (hotk->methods->mt_wled) { |
1034 | asus_proc_add(PROC_WLED, &proc_write_wled, &proc_read_wled, | 1111 | asus_proc_add(PROC_WLED, &wled_proc_fops, mode, device); |
1035 | mode, device); | ||
1036 | } | 1112 | } |
1037 | 1113 | ||
1038 | if (hotk->methods->mt_ledd) { | 1114 | if (hotk->methods->mt_ledd) { |
1039 | asus_proc_add(PROC_LEDD, &proc_write_ledd, &proc_read_ledd, | 1115 | asus_proc_add(PROC_LEDD, &ledd_proc_fops, mode, device); |
1040 | mode, device); | ||
1041 | } | 1116 | } |
1042 | 1117 | ||
1043 | if (hotk->methods->mt_mled) { | 1118 | if (hotk->methods->mt_mled) { |
1044 | asus_proc_add(PROC_MLED, &proc_write_mled, &proc_read_mled, | 1119 | asus_proc_add(PROC_MLED, &mled_proc_fops, mode, device); |
1045 | mode, device); | ||
1046 | } | 1120 | } |
1047 | 1121 | ||
1048 | if (hotk->methods->mt_tled) { | 1122 | if (hotk->methods->mt_tled) { |
1049 | asus_proc_add(PROC_TLED, &proc_write_tled, &proc_read_tled, | 1123 | asus_proc_add(PROC_TLED, &tled_proc_fops, mode, device); |
1050 | mode, device); | ||
1051 | } | 1124 | } |
1052 | 1125 | ||
1053 | if (hotk->methods->mt_bt_switch) { | 1126 | if (hotk->methods->mt_bt_switch) { |
1054 | asus_proc_add(PROC_BT, &proc_write_bluetooth, | 1127 | asus_proc_add(PROC_BT, &bluetooth_proc_fops, mode, device); |
1055 | &proc_read_bluetooth, mode, device); | ||
1056 | } | 1128 | } |
1057 | 1129 | ||
1058 | /* | 1130 | /* |
@@ -1060,19 +1132,16 @@ static int asus_hotk_add_fs(struct acpi_device *device) | |||
1060 | * accessible from the keyboard | 1132 | * accessible from the keyboard |
1061 | */ | 1133 | */ |
1062 | if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) { | 1134 | if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) { |
1063 | asus_proc_add(PROC_LCD, &proc_write_lcd, &proc_read_lcd, mode, | 1135 | asus_proc_add(PROC_LCD, &lcd_proc_fops, mode, device); |
1064 | device); | ||
1065 | } | 1136 | } |
1066 | 1137 | ||
1067 | if ((hotk->methods->brightness_up && hotk->methods->brightness_down) || | 1138 | if ((hotk->methods->brightness_up && hotk->methods->brightness_down) || |
1068 | (hotk->methods->brightness_get && hotk->methods->brightness_set)) { | 1139 | (hotk->methods->brightness_get && hotk->methods->brightness_set)) { |
1069 | asus_proc_add(PROC_BRN, &proc_write_brn, &proc_read_brn, mode, | 1140 | asus_proc_add(PROC_BRN, &brn_proc_fops, mode, device); |
1070 | device); | ||
1071 | } | 1141 | } |
1072 | 1142 | ||
1073 | if (hotk->methods->display_set) { | 1143 | if (hotk->methods->display_set) { |
1074 | asus_proc_add(PROC_DISP, &proc_write_disp, &proc_read_disp, | 1144 | asus_proc_add(PROC_DISP, &disp_proc_fops, mode, device); |
1075 | mode, device); | ||
1076 | } | 1145 | } |
1077 | 1146 | ||
1078 | return 0; | 1147 | return 0; |
@@ -1157,9 +1226,8 @@ static int asus_model_match(char *model) | |||
1157 | else if (strncmp(model, "M2N", 3) == 0 || | 1226 | else if (strncmp(model, "M2N", 3) == 0 || |
1158 | strncmp(model, "M3N", 3) == 0 || | 1227 | strncmp(model, "M3N", 3) == 0 || |
1159 | strncmp(model, "M5N", 3) == 0 || | 1228 | strncmp(model, "M5N", 3) == 0 || |
1160 | strncmp(model, "M6N", 3) == 0 || | ||
1161 | strncmp(model, "S1N", 3) == 0 || | 1229 | strncmp(model, "S1N", 3) == 0 || |
1162 | strncmp(model, "S5N", 3) == 0 || strncmp(model, "W1N", 3) == 0) | 1230 | strncmp(model, "S5N", 3) == 0) |
1163 | return xxN; | 1231 | return xxN; |
1164 | else if (strncmp(model, "M1", 2) == 0) | 1232 | else if (strncmp(model, "M1", 2) == 0) |
1165 | return M1A; | 1233 | return M1A; |
@@ -1334,9 +1402,6 @@ static int asus_hotk_add(struct acpi_device *device) | |||
1334 | acpi_status status = AE_OK; | 1402 | acpi_status status = AE_OK; |
1335 | int result; | 1403 | int result; |
1336 | 1404 | ||
1337 | if (!device) | ||
1338 | return -EINVAL; | ||
1339 | |||
1340 | printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n", | 1405 | printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n", |
1341 | ASUS_ACPI_VERSION); | 1406 | ASUS_ACPI_VERSION); |
1342 | 1407 | ||
@@ -1392,9 +1457,6 @@ end: | |||
1392 | 1457 | ||
1393 | static int asus_hotk_remove(struct acpi_device *device, int type) | 1458 | static int asus_hotk_remove(struct acpi_device *device, int type) |
1394 | { | 1459 | { |
1395 | if (!device || !acpi_driver_data(device)) | ||
1396 | return -EINVAL; | ||
1397 | |||
1398 | asus_hotk_remove_fs(device); | 1460 | asus_hotk_remove_fs(device); |
1399 | 1461 | ||
1400 | kfree(hotk); | 1462 | kfree(hotk); |
@@ -1420,23 +1482,20 @@ static void asus_acpi_exit(void) | |||
1420 | 1482 | ||
1421 | static int __init asus_acpi_init(void) | 1483 | static int __init asus_acpi_init(void) |
1422 | { | 1484 | { |
1485 | struct backlight_properties props; | ||
1423 | int result; | 1486 | int result; |
1424 | 1487 | ||
1425 | if (acpi_disabled) | 1488 | result = acpi_bus_register_driver(&asus_hotk_driver); |
1426 | return -ENODEV; | 1489 | if (result < 0) |
1490 | return result; | ||
1427 | 1491 | ||
1428 | asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir); | 1492 | asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir); |
1429 | if (!asus_proc_dir) { | 1493 | if (!asus_proc_dir) { |
1430 | printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n"); | 1494 | printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n"); |
1495 | acpi_bus_unregister_driver(&asus_hotk_driver); | ||
1431 | return -ENODEV; | 1496 | return -ENODEV; |
1432 | } | 1497 | } |
1433 | 1498 | ||
1434 | result = acpi_bus_register_driver(&asus_hotk_driver); | ||
1435 | if (result < 0) { | ||
1436 | remove_proc_entry(PROC_ASUS, acpi_root_dir); | ||
1437 | return result; | ||
1438 | } | ||
1439 | |||
1440 | /* | 1499 | /* |
1441 | * This is a bit of a kludge. We only want this module loaded | 1500 | * This is a bit of a kludge. We only want this module loaded |
1442 | * for ASUS systems, but there's currently no way to probe the | 1501 | * for ASUS systems, but there's currently no way to probe the |
@@ -1450,15 +1509,17 @@ static int __init asus_acpi_init(void) | |||
1450 | return -ENODEV; | 1509 | return -ENODEV; |
1451 | } | 1510 | } |
1452 | 1511 | ||
1512 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1513 | props.max_brightness = 15; | ||
1453 | asus_backlight_device = backlight_device_register("asus", NULL, NULL, | 1514 | asus_backlight_device = backlight_device_register("asus", NULL, NULL, |
1454 | &asus_backlight_data); | 1515 | &asus_backlight_data, |
1516 | &props); | ||
1455 | if (IS_ERR(asus_backlight_device)) { | 1517 | if (IS_ERR(asus_backlight_device)) { |
1456 | printk(KERN_ERR "Could not register asus backlight device\n"); | 1518 | printk(KERN_ERR "Could not register asus backlight device\n"); |
1457 | asus_backlight_device = NULL; | 1519 | asus_backlight_device = NULL; |
1458 | asus_acpi_exit(); | 1520 | asus_acpi_exit(); |
1459 | return -ENODEV; | 1521 | return -ENODEV; |
1460 | } | 1522 | } |
1461 | asus_backlight_device->props.max_brightness = 15; | ||
1462 | 1523 | ||
1463 | return 0; | 1524 | return 0; |
1464 | } | 1525 | } |
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c new file mode 100644 index 000000000000..7f9e5ddc9498 --- /dev/null +++ b/drivers/platform/x86/classmate-laptop.c | |||
@@ -0,0 +1,629 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
17 | */ | ||
18 | |||
19 | |||
20 | #include <linux/init.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/workqueue.h> | ||
24 | #include <acpi/acpi_drivers.h> | ||
25 | #include <linux/backlight.h> | ||
26 | #include <linux/input.h> | ||
27 | |||
28 | MODULE_LICENSE("GPL"); | ||
29 | |||
30 | |||
31 | struct cmpc_accel { | ||
32 | int sensitivity; | ||
33 | }; | ||
34 | |||
35 | #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 | ||
36 | |||
37 | |||
38 | #define CMPC_ACCEL_HID "ACCE0000" | ||
39 | #define CMPC_TABLET_HID "TBLT0000" | ||
40 | #define CMPC_BL_HID "IPML200" | ||
41 | #define CMPC_KEYS_HID "FnBT0000" | ||
42 | |||
43 | /* | ||
44 | * Generic input device code. | ||
45 | */ | ||
46 | |||
47 | typedef void (*input_device_init)(struct input_dev *dev); | ||
48 | |||
49 | static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name, | ||
50 | input_device_init idev_init) | ||
51 | { | ||
52 | struct input_dev *inputdev; | ||
53 | int error; | ||
54 | |||
55 | inputdev = input_allocate_device(); | ||
56 | if (!inputdev) | ||
57 | return -ENOMEM; | ||
58 | inputdev->name = name; | ||
59 | inputdev->dev.parent = &acpi->dev; | ||
60 | idev_init(inputdev); | ||
61 | error = input_register_device(inputdev); | ||
62 | if (error) { | ||
63 | input_free_device(inputdev); | ||
64 | return error; | ||
65 | } | ||
66 | dev_set_drvdata(&acpi->dev, inputdev); | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi) | ||
71 | { | ||
72 | struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); | ||
73 | input_unregister_device(inputdev); | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * Accelerometer code. | ||
79 | */ | ||
80 | static acpi_status cmpc_start_accel(acpi_handle handle) | ||
81 | { | ||
82 | union acpi_object param[2]; | ||
83 | struct acpi_object_list input; | ||
84 | acpi_status status; | ||
85 | |||
86 | param[0].type = ACPI_TYPE_INTEGER; | ||
87 | param[0].integer.value = 0x3; | ||
88 | param[1].type = ACPI_TYPE_INTEGER; | ||
89 | input.count = 2; | ||
90 | input.pointer = param; | ||
91 | status = acpi_evaluate_object(handle, "ACMD", &input, NULL); | ||
92 | return status; | ||
93 | } | ||
94 | |||
95 | static acpi_status cmpc_stop_accel(acpi_handle handle) | ||
96 | { | ||
97 | union acpi_object param[2]; | ||
98 | struct acpi_object_list input; | ||
99 | acpi_status status; | ||
100 | |||
101 | param[0].type = ACPI_TYPE_INTEGER; | ||
102 | param[0].integer.value = 0x4; | ||
103 | param[1].type = ACPI_TYPE_INTEGER; | ||
104 | input.count = 2; | ||
105 | input.pointer = param; | ||
106 | status = acpi_evaluate_object(handle, "ACMD", &input, NULL); | ||
107 | return status; | ||
108 | } | ||
109 | |||
110 | static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val) | ||
111 | { | ||
112 | union acpi_object param[2]; | ||
113 | struct acpi_object_list input; | ||
114 | |||
115 | param[0].type = ACPI_TYPE_INTEGER; | ||
116 | param[0].integer.value = 0x02; | ||
117 | param[1].type = ACPI_TYPE_INTEGER; | ||
118 | param[1].integer.value = val; | ||
119 | input.count = 2; | ||
120 | input.pointer = param; | ||
121 | return acpi_evaluate_object(handle, "ACMD", &input, NULL); | ||
122 | } | ||
123 | |||
124 | static acpi_status cmpc_get_accel(acpi_handle handle, | ||
125 | unsigned char *x, | ||
126 | unsigned char *y, | ||
127 | unsigned char *z) | ||
128 | { | ||
129 | union acpi_object param[2]; | ||
130 | struct acpi_object_list input; | ||
131 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 }; | ||
132 | unsigned char *locs; | ||
133 | acpi_status status; | ||
134 | |||
135 | param[0].type = ACPI_TYPE_INTEGER; | ||
136 | param[0].integer.value = 0x01; | ||
137 | param[1].type = ACPI_TYPE_INTEGER; | ||
138 | input.count = 2; | ||
139 | input.pointer = param; | ||
140 | status = acpi_evaluate_object(handle, "ACMD", &input, &output); | ||
141 | if (ACPI_SUCCESS(status)) { | ||
142 | union acpi_object *obj; | ||
143 | obj = output.pointer; | ||
144 | locs = obj->buffer.pointer; | ||
145 | *x = locs[0]; | ||
146 | *y = locs[1]; | ||
147 | *z = locs[2]; | ||
148 | kfree(output.pointer); | ||
149 | } | ||
150 | return status; | ||
151 | } | ||
152 | |||
153 | static void cmpc_accel_handler(struct acpi_device *dev, u32 event) | ||
154 | { | ||
155 | if (event == 0x81) { | ||
156 | unsigned char x, y, z; | ||
157 | acpi_status status; | ||
158 | |||
159 | status = cmpc_get_accel(dev->handle, &x, &y, &z); | ||
160 | if (ACPI_SUCCESS(status)) { | ||
161 | struct input_dev *inputdev = dev_get_drvdata(&dev->dev); | ||
162 | |||
163 | input_report_abs(inputdev, ABS_X, x); | ||
164 | input_report_abs(inputdev, ABS_Y, y); | ||
165 | input_report_abs(inputdev, ABS_Z, z); | ||
166 | input_sync(inputdev); | ||
167 | } | ||
168 | } | ||
169 | } | ||
170 | |||
171 | static ssize_t cmpc_accel_sensitivity_show(struct device *dev, | ||
172 | struct device_attribute *attr, | ||
173 | char *buf) | ||
174 | { | ||
175 | struct acpi_device *acpi; | ||
176 | struct input_dev *inputdev; | ||
177 | struct cmpc_accel *accel; | ||
178 | |||
179 | acpi = to_acpi_device(dev); | ||
180 | inputdev = dev_get_drvdata(&acpi->dev); | ||
181 | accel = dev_get_drvdata(&inputdev->dev); | ||
182 | |||
183 | return sprintf(buf, "%d\n", accel->sensitivity); | ||
184 | } | ||
185 | |||
186 | static ssize_t cmpc_accel_sensitivity_store(struct device *dev, | ||
187 | struct device_attribute *attr, | ||
188 | const char *buf, size_t count) | ||
189 | { | ||
190 | struct acpi_device *acpi; | ||
191 | struct input_dev *inputdev; | ||
192 | struct cmpc_accel *accel; | ||
193 | unsigned long sensitivity; | ||
194 | int r; | ||
195 | |||
196 | acpi = to_acpi_device(dev); | ||
197 | inputdev = dev_get_drvdata(&acpi->dev); | ||
198 | accel = dev_get_drvdata(&inputdev->dev); | ||
199 | |||
200 | r = strict_strtoul(buf, 0, &sensitivity); | ||
201 | if (r) | ||
202 | return r; | ||
203 | |||
204 | accel->sensitivity = sensitivity; | ||
205 | cmpc_accel_set_sensitivity(acpi->handle, sensitivity); | ||
206 | |||
207 | return strnlen(buf, count); | ||
208 | } | ||
209 | |||
210 | struct device_attribute cmpc_accel_sensitivity_attr = { | ||
211 | .attr = { .name = "sensitivity", .mode = 0660 }, | ||
212 | .show = cmpc_accel_sensitivity_show, | ||
213 | .store = cmpc_accel_sensitivity_store | ||
214 | }; | ||
215 | |||
216 | static int cmpc_accel_open(struct input_dev *input) | ||
217 | { | ||
218 | struct acpi_device *acpi; | ||
219 | |||
220 | acpi = to_acpi_device(input->dev.parent); | ||
221 | if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle))) | ||
222 | return 0; | ||
223 | return -EIO; | ||
224 | } | ||
225 | |||
226 | static void cmpc_accel_close(struct input_dev *input) | ||
227 | { | ||
228 | struct acpi_device *acpi; | ||
229 | |||
230 | acpi = to_acpi_device(input->dev.parent); | ||
231 | cmpc_stop_accel(acpi->handle); | ||
232 | } | ||
233 | |||
234 | static void cmpc_accel_idev_init(struct input_dev *inputdev) | ||
235 | { | ||
236 | set_bit(EV_ABS, inputdev->evbit); | ||
237 | input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0); | ||
238 | input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0); | ||
239 | input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0); | ||
240 | inputdev->open = cmpc_accel_open; | ||
241 | inputdev->close = cmpc_accel_close; | ||
242 | } | ||
243 | |||
244 | static int cmpc_accel_add(struct acpi_device *acpi) | ||
245 | { | ||
246 | int error; | ||
247 | struct input_dev *inputdev; | ||
248 | struct cmpc_accel *accel; | ||
249 | |||
250 | accel = kmalloc(sizeof(*accel), GFP_KERNEL); | ||
251 | if (!accel) | ||
252 | return -ENOMEM; | ||
253 | |||
254 | accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; | ||
255 | cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity); | ||
256 | |||
257 | error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr); | ||
258 | if (error) | ||
259 | goto failed_file; | ||
260 | |||
261 | error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", | ||
262 | cmpc_accel_idev_init); | ||
263 | if (error) | ||
264 | goto failed_input; | ||
265 | |||
266 | inputdev = dev_get_drvdata(&acpi->dev); | ||
267 | dev_set_drvdata(&inputdev->dev, accel); | ||
268 | |||
269 | return 0; | ||
270 | |||
271 | failed_input: | ||
272 | device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); | ||
273 | failed_file: | ||
274 | kfree(accel); | ||
275 | return error; | ||
276 | } | ||
277 | |||
278 | static int cmpc_accel_remove(struct acpi_device *acpi, int type) | ||
279 | { | ||
280 | struct input_dev *inputdev; | ||
281 | struct cmpc_accel *accel; | ||
282 | |||
283 | inputdev = dev_get_drvdata(&acpi->dev); | ||
284 | accel = dev_get_drvdata(&inputdev->dev); | ||
285 | |||
286 | device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); | ||
287 | return cmpc_remove_acpi_notify_device(acpi); | ||
288 | } | ||
289 | |||
290 | static const struct acpi_device_id cmpc_accel_device_ids[] = { | ||
291 | {CMPC_ACCEL_HID, 0}, | ||
292 | {"", 0} | ||
293 | }; | ||
294 | |||
295 | static struct acpi_driver cmpc_accel_acpi_driver = { | ||
296 | .owner = THIS_MODULE, | ||
297 | .name = "cmpc_accel", | ||
298 | .class = "cmpc_accel", | ||
299 | .ids = cmpc_accel_device_ids, | ||
300 | .ops = { | ||
301 | .add = cmpc_accel_add, | ||
302 | .remove = cmpc_accel_remove, | ||
303 | .notify = cmpc_accel_handler, | ||
304 | } | ||
305 | }; | ||
306 | |||
307 | |||
308 | /* | ||
309 | * Tablet mode code. | ||
310 | */ | ||
311 | static acpi_status cmpc_get_tablet(acpi_handle handle, | ||
312 | unsigned long long *value) | ||
313 | { | ||
314 | union acpi_object param; | ||
315 | struct acpi_object_list input; | ||
316 | unsigned long long output; | ||
317 | acpi_status status; | ||
318 | |||
319 | param.type = ACPI_TYPE_INTEGER; | ||
320 | param.integer.value = 0x01; | ||
321 | input.count = 1; | ||
322 | input.pointer = ¶m; | ||
323 | status = acpi_evaluate_integer(handle, "TCMD", &input, &output); | ||
324 | if (ACPI_SUCCESS(status)) | ||
325 | *value = output; | ||
326 | return status; | ||
327 | } | ||
328 | |||
329 | static void cmpc_tablet_handler(struct acpi_device *dev, u32 event) | ||
330 | { | ||
331 | unsigned long long val = 0; | ||
332 | struct input_dev *inputdev = dev_get_drvdata(&dev->dev); | ||
333 | |||
334 | if (event == 0x81) { | ||
335 | if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) | ||
336 | input_report_switch(inputdev, SW_TABLET_MODE, !val); | ||
337 | } | ||
338 | } | ||
339 | |||
340 | static void cmpc_tablet_idev_init(struct input_dev *inputdev) | ||
341 | { | ||
342 | unsigned long long val = 0; | ||
343 | struct acpi_device *acpi; | ||
344 | |||
345 | set_bit(EV_SW, inputdev->evbit); | ||
346 | set_bit(SW_TABLET_MODE, inputdev->swbit); | ||
347 | |||
348 | acpi = to_acpi_device(inputdev->dev.parent); | ||
349 | if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) | ||
350 | input_report_switch(inputdev, SW_TABLET_MODE, !val); | ||
351 | } | ||
352 | |||
353 | static int cmpc_tablet_add(struct acpi_device *acpi) | ||
354 | { | ||
355 | return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet", | ||
356 | cmpc_tablet_idev_init); | ||
357 | } | ||
358 | |||
359 | static int cmpc_tablet_remove(struct acpi_device *acpi, int type) | ||
360 | { | ||
361 | return cmpc_remove_acpi_notify_device(acpi); | ||
362 | } | ||
363 | |||
364 | static int cmpc_tablet_resume(struct acpi_device *acpi) | ||
365 | { | ||
366 | struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); | ||
367 | unsigned long long val = 0; | ||
368 | if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) | ||
369 | input_report_switch(inputdev, SW_TABLET_MODE, !val); | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | static const struct acpi_device_id cmpc_tablet_device_ids[] = { | ||
374 | {CMPC_TABLET_HID, 0}, | ||
375 | {"", 0} | ||
376 | }; | ||
377 | |||
378 | static struct acpi_driver cmpc_tablet_acpi_driver = { | ||
379 | .owner = THIS_MODULE, | ||
380 | .name = "cmpc_tablet", | ||
381 | .class = "cmpc_tablet", | ||
382 | .ids = cmpc_tablet_device_ids, | ||
383 | .ops = { | ||
384 | .add = cmpc_tablet_add, | ||
385 | .remove = cmpc_tablet_remove, | ||
386 | .resume = cmpc_tablet_resume, | ||
387 | .notify = cmpc_tablet_handler, | ||
388 | } | ||
389 | }; | ||
390 | |||
391 | |||
392 | /* | ||
393 | * Backlight code. | ||
394 | */ | ||
395 | |||
396 | static acpi_status cmpc_get_brightness(acpi_handle handle, | ||
397 | unsigned long long *value) | ||
398 | { | ||
399 | union acpi_object param; | ||
400 | struct acpi_object_list input; | ||
401 | unsigned long long output; | ||
402 | acpi_status status; | ||
403 | |||
404 | param.type = ACPI_TYPE_INTEGER; | ||
405 | param.integer.value = 0xC0; | ||
406 | input.count = 1; | ||
407 | input.pointer = ¶m; | ||
408 | status = acpi_evaluate_integer(handle, "GRDI", &input, &output); | ||
409 | if (ACPI_SUCCESS(status)) | ||
410 | *value = output; | ||
411 | return status; | ||
412 | } | ||
413 | |||
414 | static acpi_status cmpc_set_brightness(acpi_handle handle, | ||
415 | unsigned long long value) | ||
416 | { | ||
417 | union acpi_object param[2]; | ||
418 | struct acpi_object_list input; | ||
419 | acpi_status status; | ||
420 | unsigned long long output; | ||
421 | |||
422 | param[0].type = ACPI_TYPE_INTEGER; | ||
423 | param[0].integer.value = 0xC0; | ||
424 | param[1].type = ACPI_TYPE_INTEGER; | ||
425 | param[1].integer.value = value; | ||
426 | input.count = 2; | ||
427 | input.pointer = param; | ||
428 | status = acpi_evaluate_integer(handle, "GWRI", &input, &output); | ||
429 | return status; | ||
430 | } | ||
431 | |||
432 | static int cmpc_bl_get_brightness(struct backlight_device *bd) | ||
433 | { | ||
434 | acpi_status status; | ||
435 | acpi_handle handle; | ||
436 | unsigned long long brightness; | ||
437 | |||
438 | handle = bl_get_data(bd); | ||
439 | status = cmpc_get_brightness(handle, &brightness); | ||
440 | if (ACPI_SUCCESS(status)) | ||
441 | return brightness; | ||
442 | else | ||
443 | return -1; | ||
444 | } | ||
445 | |||
446 | static int cmpc_bl_update_status(struct backlight_device *bd) | ||
447 | { | ||
448 | acpi_status status; | ||
449 | acpi_handle handle; | ||
450 | |||
451 | handle = bl_get_data(bd); | ||
452 | status = cmpc_set_brightness(handle, bd->props.brightness); | ||
453 | if (ACPI_SUCCESS(status)) | ||
454 | return 0; | ||
455 | else | ||
456 | return -1; | ||
457 | } | ||
458 | |||
459 | static const struct backlight_ops cmpc_bl_ops = { | ||
460 | .get_brightness = cmpc_bl_get_brightness, | ||
461 | .update_status = cmpc_bl_update_status | ||
462 | }; | ||
463 | |||
464 | static int cmpc_bl_add(struct acpi_device *acpi) | ||
465 | { | ||
466 | struct backlight_properties props; | ||
467 | struct backlight_device *bd; | ||
468 | |||
469 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
470 | props.max_brightness = 7; | ||
471 | bd = backlight_device_register("cmpc_bl", &acpi->dev, acpi->handle, | ||
472 | &cmpc_bl_ops, &props); | ||
473 | if (IS_ERR(bd)) | ||
474 | return PTR_ERR(bd); | ||
475 | dev_set_drvdata(&acpi->dev, bd); | ||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | static int cmpc_bl_remove(struct acpi_device *acpi, int type) | ||
480 | { | ||
481 | struct backlight_device *bd; | ||
482 | |||
483 | bd = dev_get_drvdata(&acpi->dev); | ||
484 | backlight_device_unregister(bd); | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static const struct acpi_device_id cmpc_bl_device_ids[] = { | ||
489 | {CMPC_BL_HID, 0}, | ||
490 | {"", 0} | ||
491 | }; | ||
492 | |||
493 | static struct acpi_driver cmpc_bl_acpi_driver = { | ||
494 | .owner = THIS_MODULE, | ||
495 | .name = "cmpc", | ||
496 | .class = "cmpc", | ||
497 | .ids = cmpc_bl_device_ids, | ||
498 | .ops = { | ||
499 | .add = cmpc_bl_add, | ||
500 | .remove = cmpc_bl_remove | ||
501 | } | ||
502 | }; | ||
503 | |||
504 | |||
505 | /* | ||
506 | * Extra keys code. | ||
507 | */ | ||
508 | static int cmpc_keys_codes[] = { | ||
509 | KEY_UNKNOWN, | ||
510 | KEY_WLAN, | ||
511 | KEY_SWITCHVIDEOMODE, | ||
512 | KEY_BRIGHTNESSDOWN, | ||
513 | KEY_BRIGHTNESSUP, | ||
514 | KEY_VENDOR, | ||
515 | KEY_UNKNOWN, | ||
516 | KEY_CAMERA, | ||
517 | KEY_BACK, | ||
518 | KEY_FORWARD, | ||
519 | KEY_MAX | ||
520 | }; | ||
521 | |||
522 | static void cmpc_keys_handler(struct acpi_device *dev, u32 event) | ||
523 | { | ||
524 | struct input_dev *inputdev; | ||
525 | int code = KEY_MAX; | ||
526 | |||
527 | if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) | ||
528 | code = cmpc_keys_codes[event & 0x0F]; | ||
529 | inputdev = dev_get_drvdata(&dev->dev);; | ||
530 | input_report_key(inputdev, code, !(event & 0x10)); | ||
531 | } | ||
532 | |||
533 | static void cmpc_keys_idev_init(struct input_dev *inputdev) | ||
534 | { | ||
535 | int i; | ||
536 | |||
537 | set_bit(EV_KEY, inputdev->evbit); | ||
538 | for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++) | ||
539 | set_bit(cmpc_keys_codes[i], inputdev->keybit); | ||
540 | } | ||
541 | |||
542 | static int cmpc_keys_add(struct acpi_device *acpi) | ||
543 | { | ||
544 | return cmpc_add_acpi_notify_device(acpi, "cmpc_keys", | ||
545 | cmpc_keys_idev_init); | ||
546 | } | ||
547 | |||
548 | static int cmpc_keys_remove(struct acpi_device *acpi, int type) | ||
549 | { | ||
550 | return cmpc_remove_acpi_notify_device(acpi); | ||
551 | } | ||
552 | |||
553 | static const struct acpi_device_id cmpc_keys_device_ids[] = { | ||
554 | {CMPC_KEYS_HID, 0}, | ||
555 | {"", 0} | ||
556 | }; | ||
557 | |||
558 | static struct acpi_driver cmpc_keys_acpi_driver = { | ||
559 | .owner = THIS_MODULE, | ||
560 | .name = "cmpc_keys", | ||
561 | .class = "cmpc_keys", | ||
562 | .ids = cmpc_keys_device_ids, | ||
563 | .ops = { | ||
564 | .add = cmpc_keys_add, | ||
565 | .remove = cmpc_keys_remove, | ||
566 | .notify = cmpc_keys_handler, | ||
567 | } | ||
568 | }; | ||
569 | |||
570 | |||
571 | /* | ||
572 | * General init/exit code. | ||
573 | */ | ||
574 | |||
575 | static int cmpc_init(void) | ||
576 | { | ||
577 | int r; | ||
578 | |||
579 | r = acpi_bus_register_driver(&cmpc_keys_acpi_driver); | ||
580 | if (r) | ||
581 | goto failed_keys; | ||
582 | |||
583 | r = acpi_bus_register_driver(&cmpc_bl_acpi_driver); | ||
584 | if (r) | ||
585 | goto failed_bl; | ||
586 | |||
587 | r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver); | ||
588 | if (r) | ||
589 | goto failed_tablet; | ||
590 | |||
591 | r = acpi_bus_register_driver(&cmpc_accel_acpi_driver); | ||
592 | if (r) | ||
593 | goto failed_accel; | ||
594 | |||
595 | return r; | ||
596 | |||
597 | failed_accel: | ||
598 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); | ||
599 | |||
600 | failed_tablet: | ||
601 | acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); | ||
602 | |||
603 | failed_bl: | ||
604 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); | ||
605 | |||
606 | failed_keys: | ||
607 | return r; | ||
608 | } | ||
609 | |||
610 | static void cmpc_exit(void) | ||
611 | { | ||
612 | acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); | ||
613 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); | ||
614 | acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); | ||
615 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); | ||
616 | } | ||
617 | |||
618 | module_init(cmpc_init); | ||
619 | module_exit(cmpc_exit); | ||
620 | |||
621 | static const struct acpi_device_id cmpc_device_ids[] = { | ||
622 | {CMPC_ACCEL_HID, 0}, | ||
623 | {CMPC_TABLET_HID, 0}, | ||
624 | {CMPC_BL_HID, 0}, | ||
625 | {CMPC_KEYS_HID, 0}, | ||
626 | {"", 0} | ||
627 | }; | ||
628 | |||
629 | MODULE_DEVICE_TABLE(acpi, cmpc_device_ids); | ||
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 11003bba10d3..71ff1545a93e 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c | |||
@@ -26,17 +26,8 @@ | |||
26 | /* | 26 | /* |
27 | * comapl-laptop.c - Compal laptop support. | 27 | * comapl-laptop.c - Compal laptop support. |
28 | * | 28 | * |
29 | * This driver exports a few files in /sys/devices/platform/compal-laptop/: | 29 | * The driver registers itself with the rfkill subsystem and |
30 | * | 30 | * the Linux backlight control subsystem. |
31 | * wlan - wlan subsystem state: contains 0 or 1 (rw) | ||
32 | * | ||
33 | * bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw) | ||
34 | * | ||
35 | * raw - raw value taken from embedded controller register (ro) | ||
36 | * | ||
37 | * In addition to these platform device attributes the driver | ||
38 | * registers itself in the Linux backlight control subsystem and is | ||
39 | * available to userspace under /sys/class/backlight/compal-laptop/. | ||
40 | * | 31 | * |
41 | * This driver might work on other laptops produced by Compal. If you | 32 | * This driver might work on other laptops produced by Compal. If you |
42 | * want to try it you can pass force=1 as argument to the module which | 33 | * want to try it you can pass force=1 as argument to the module which |
@@ -51,7 +42,7 @@ | |||
51 | #include <linux/dmi.h> | 42 | #include <linux/dmi.h> |
52 | #include <linux/backlight.h> | 43 | #include <linux/backlight.h> |
53 | #include <linux/platform_device.h> | 44 | #include <linux/platform_device.h> |
54 | #include <linux/autoconf.h> | 45 | #include <linux/rfkill.h> |
55 | 46 | ||
56 | #define COMPAL_DRIVER_VERSION "0.2.6" | 47 | #define COMPAL_DRIVER_VERSION "0.2.6" |
57 | 48 | ||
@@ -64,6 +55,10 @@ | |||
64 | #define WLAN_MASK 0x01 | 55 | #define WLAN_MASK 0x01 |
65 | #define BT_MASK 0x02 | 56 | #define BT_MASK 0x02 |
66 | 57 | ||
58 | static struct rfkill *wifi_rfkill; | ||
59 | static struct rfkill *bt_rfkill; | ||
60 | static struct platform_device *compal_device; | ||
61 | |||
67 | static int force; | 62 | static int force; |
68 | module_param(force, bool, 0); | 63 | module_param(force, bool, 0); |
69 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | 64 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); |
@@ -89,65 +84,75 @@ static int get_lcd_level(void) | |||
89 | return (int) result; | 84 | return (int) result; |
90 | } | 85 | } |
91 | 86 | ||
92 | static int set_wlan_state(int state) | 87 | static int compal_rfkill_set(void *data, bool blocked) |
93 | { | 88 | { |
89 | unsigned long radio = (unsigned long) data; | ||
94 | u8 result, value; | 90 | u8 result, value; |
95 | 91 | ||
96 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | 92 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); |
97 | 93 | ||
98 | if ((result & KILLSWITCH_MASK) == 0) | 94 | if (!blocked) |
99 | return -EINVAL; | 95 | value = (u8) (result | radio); |
100 | else { | 96 | else |
101 | if (state) | 97 | value = (u8) (result & ~radio); |
102 | value = (u8) (result | WLAN_MASK); | 98 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); |
103 | else | ||
104 | value = (u8) (result & ~WLAN_MASK); | ||
105 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); | ||
106 | } | ||
107 | 99 | ||
108 | return 0; | 100 | return 0; |
109 | } | 101 | } |
110 | 102 | ||
111 | static int set_bluetooth_state(int state) | 103 | static void compal_rfkill_poll(struct rfkill *rfkill, void *data) |
112 | { | 104 | { |
113 | u8 result, value; | 105 | u8 result; |
106 | bool hw_blocked; | ||
114 | 107 | ||
115 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | 108 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); |
116 | 109 | ||
117 | if ((result & KILLSWITCH_MASK) == 0) | 110 | hw_blocked = !(result & KILLSWITCH_MASK); |
118 | return -EINVAL; | 111 | rfkill_set_hw_state(rfkill, hw_blocked); |
119 | else { | ||
120 | if (state) | ||
121 | value = (u8) (result | BT_MASK); | ||
122 | else | ||
123 | value = (u8) (result & ~BT_MASK); | ||
124 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); | ||
125 | } | ||
126 | |||
127 | return 0; | ||
128 | } | 112 | } |
129 | 113 | ||
130 | static int get_wireless_state(int *wlan, int *bluetooth) | 114 | static const struct rfkill_ops compal_rfkill_ops = { |
115 | .poll = compal_rfkill_poll, | ||
116 | .set_block = compal_rfkill_set, | ||
117 | }; | ||
118 | |||
119 | static int setup_rfkill(void) | ||
131 | { | 120 | { |
132 | u8 result; | 121 | int ret; |
133 | 122 | ||
134 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | 123 | wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev, |
124 | RFKILL_TYPE_WLAN, &compal_rfkill_ops, | ||
125 | (void *) WLAN_MASK); | ||
126 | if (!wifi_rfkill) | ||
127 | return -ENOMEM; | ||
135 | 128 | ||
136 | if (wlan) { | 129 | ret = rfkill_register(wifi_rfkill); |
137 | if ((result & KILLSWITCH_MASK) == 0) | 130 | if (ret) |
138 | *wlan = 0; | 131 | goto err_wifi; |
139 | else | ||
140 | *wlan = result & WLAN_MASK; | ||
141 | } | ||
142 | 132 | ||
143 | if (bluetooth) { | 133 | bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev, |
144 | if ((result & KILLSWITCH_MASK) == 0) | 134 | RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops, |
145 | *bluetooth = 0; | 135 | (void *) BT_MASK); |
146 | else | 136 | if (!bt_rfkill) { |
147 | *bluetooth = (result & BT_MASK) >> 1; | 137 | ret = -ENOMEM; |
138 | goto err_allocate_bt; | ||
148 | } | 139 | } |
140 | ret = rfkill_register(bt_rfkill); | ||
141 | if (ret) | ||
142 | goto err_register_bt; | ||
149 | 143 | ||
150 | return 0; | 144 | return 0; |
145 | |||
146 | err_register_bt: | ||
147 | rfkill_destroy(bt_rfkill); | ||
148 | |||
149 | err_allocate_bt: | ||
150 | rfkill_unregister(wifi_rfkill); | ||
151 | |||
152 | err_wifi: | ||
153 | rfkill_destroy(wifi_rfkill); | ||
154 | |||
155 | return ret; | ||
151 | } | 156 | } |
152 | 157 | ||
153 | /* Backlight device stuff */ | 158 | /* Backlight device stuff */ |
@@ -170,86 +175,6 @@ static struct backlight_ops compalbl_ops = { | |||
170 | 175 | ||
171 | static struct backlight_device *compalbl_device; | 176 | static struct backlight_device *compalbl_device; |
172 | 177 | ||
173 | /* Platform device */ | ||
174 | |||
175 | static ssize_t show_wlan(struct device *dev, | ||
176 | struct device_attribute *attr, char *buf) | ||
177 | { | ||
178 | int ret, enabled; | ||
179 | |||
180 | ret = get_wireless_state(&enabled, NULL); | ||
181 | if (ret < 0) | ||
182 | return ret; | ||
183 | |||
184 | return sprintf(buf, "%i\n", enabled); | ||
185 | } | ||
186 | |||
187 | static ssize_t show_raw(struct device *dev, | ||
188 | struct device_attribute *attr, char *buf) | ||
189 | { | ||
190 | u8 result; | ||
191 | |||
192 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | ||
193 | |||
194 | return sprintf(buf, "%i\n", result); | ||
195 | } | ||
196 | |||
197 | static ssize_t show_bluetooth(struct device *dev, | ||
198 | struct device_attribute *attr, char *buf) | ||
199 | { | ||
200 | int ret, enabled; | ||
201 | |||
202 | ret = get_wireless_state(NULL, &enabled); | ||
203 | if (ret < 0) | ||
204 | return ret; | ||
205 | |||
206 | return sprintf(buf, "%i\n", enabled); | ||
207 | } | ||
208 | |||
209 | static ssize_t store_wlan_state(struct device *dev, | ||
210 | struct device_attribute *attr, const char *buf, size_t count) | ||
211 | { | ||
212 | int state, ret; | ||
213 | |||
214 | if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) | ||
215 | return -EINVAL; | ||
216 | |||
217 | ret = set_wlan_state(state); | ||
218 | if (ret < 0) | ||
219 | return ret; | ||
220 | |||
221 | return count; | ||
222 | } | ||
223 | |||
224 | static ssize_t store_bluetooth_state(struct device *dev, | ||
225 | struct device_attribute *attr, const char *buf, size_t count) | ||
226 | { | ||
227 | int state, ret; | ||
228 | |||
229 | if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) | ||
230 | return -EINVAL; | ||
231 | |||
232 | ret = set_bluetooth_state(state); | ||
233 | if (ret < 0) | ||
234 | return ret; | ||
235 | |||
236 | return count; | ||
237 | } | ||
238 | |||
239 | static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state); | ||
240 | static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state); | ||
241 | static DEVICE_ATTR(raw, 0444, show_raw, NULL); | ||
242 | |||
243 | static struct attribute *compal_attributes[] = { | ||
244 | &dev_attr_bluetooth.attr, | ||
245 | &dev_attr_wlan.attr, | ||
246 | &dev_attr_raw.attr, | ||
247 | NULL | ||
248 | }; | ||
249 | |||
250 | static struct attribute_group compal_attribute_group = { | ||
251 | .attrs = compal_attributes | ||
252 | }; | ||
253 | 178 | ||
254 | static struct platform_driver compal_driver = { | 179 | static struct platform_driver compal_driver = { |
255 | .driver = { | 180 | .driver = { |
@@ -258,8 +183,6 @@ static struct platform_driver compal_driver = { | |||
258 | } | 183 | } |
259 | }; | 184 | }; |
260 | 185 | ||
261 | static struct platform_device *compal_device; | ||
262 | |||
263 | /* Initialization */ | 186 | /* Initialization */ |
264 | 187 | ||
265 | static int dmi_check_cb(const struct dmi_system_id *id) | 188 | static int dmi_check_cb(const struct dmi_system_id *id) |
@@ -311,6 +234,47 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { | |||
311 | }, | 234 | }, |
312 | .callback = dmi_check_cb | 235 | .callback = dmi_check_cb |
313 | }, | 236 | }, |
237 | { | ||
238 | .ident = "Dell Mini 9", | ||
239 | .matches = { | ||
240 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
241 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), | ||
242 | }, | ||
243 | .callback = dmi_check_cb | ||
244 | }, | ||
245 | { | ||
246 | .ident = "Dell Mini 10", | ||
247 | .matches = { | ||
248 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
249 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), | ||
250 | }, | ||
251 | .callback = dmi_check_cb | ||
252 | }, | ||
253 | { | ||
254 | .ident = "Dell Mini 10v", | ||
255 | .matches = { | ||
256 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
257 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), | ||
258 | }, | ||
259 | .callback = dmi_check_cb | ||
260 | }, | ||
261 | { | ||
262 | .ident = "Dell Inspiron 11z", | ||
263 | .matches = { | ||
264 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
265 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), | ||
266 | }, | ||
267 | .callback = dmi_check_cb | ||
268 | }, | ||
269 | { | ||
270 | .ident = "Dell Mini 12", | ||
271 | .matches = { | ||
272 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
273 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), | ||
274 | }, | ||
275 | .callback = dmi_check_cb | ||
276 | }, | ||
277 | |||
314 | { } | 278 | { } |
315 | }; | 279 | }; |
316 | 280 | ||
@@ -327,12 +291,15 @@ static int __init compal_init(void) | |||
327 | /* Register backlight stuff */ | 291 | /* Register backlight stuff */ |
328 | 292 | ||
329 | if (!acpi_video_backlight_support()) { | 293 | if (!acpi_video_backlight_support()) { |
330 | compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, | 294 | struct backlight_properties props; |
331 | &compalbl_ops); | 295 | memset(&props, 0, sizeof(struct backlight_properties)); |
296 | props.max_brightness = COMPAL_LCD_LEVEL_MAX - 1; | ||
297 | compalbl_device = backlight_device_register("compal-laptop", | ||
298 | NULL, NULL, | ||
299 | &compalbl_ops, | ||
300 | &props); | ||
332 | if (IS_ERR(compalbl_device)) | 301 | if (IS_ERR(compalbl_device)) |
333 | return PTR_ERR(compalbl_device); | 302 | return PTR_ERR(compalbl_device); |
334 | |||
335 | compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; | ||
336 | } | 303 | } |
337 | 304 | ||
338 | ret = platform_driver_register(&compal_driver); | 305 | ret = platform_driver_register(&compal_driver); |
@@ -349,23 +316,21 @@ static int __init compal_init(void) | |||
349 | 316 | ||
350 | ret = platform_device_add(compal_device); | 317 | ret = platform_device_add(compal_device); |
351 | if (ret) | 318 | if (ret) |
352 | goto fail_platform_device1; | 319 | goto fail_platform_device; |
353 | 320 | ||
354 | ret = sysfs_create_group(&compal_device->dev.kobj, | 321 | ret = setup_rfkill(); |
355 | &compal_attribute_group); | ||
356 | if (ret) | 322 | if (ret) |
357 | goto fail_platform_device2; | 323 | goto fail_rfkill; |
358 | 324 | ||
359 | printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION | 325 | printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION |
360 | " successfully loaded.\n"); | 326 | " successfully loaded.\n"); |
361 | 327 | ||
362 | return 0; | 328 | return 0; |
363 | 329 | ||
364 | fail_platform_device2: | 330 | fail_rfkill: |
365 | |||
366 | platform_device_del(compal_device); | 331 | platform_device_del(compal_device); |
367 | 332 | ||
368 | fail_platform_device1: | 333 | fail_platform_device: |
369 | 334 | ||
370 | platform_device_put(compal_device); | 335 | platform_device_put(compal_device); |
371 | 336 | ||
@@ -383,10 +348,13 @@ fail_backlight: | |||
383 | static void __exit compal_cleanup(void) | 348 | static void __exit compal_cleanup(void) |
384 | { | 349 | { |
385 | 350 | ||
386 | sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group); | ||
387 | platform_device_unregister(compal_device); | 351 | platform_device_unregister(compal_device); |
388 | platform_driver_unregister(&compal_driver); | 352 | platform_driver_unregister(&compal_driver); |
389 | backlight_device_unregister(compalbl_device); | 353 | backlight_device_unregister(compalbl_device); |
354 | rfkill_unregister(wifi_rfkill); | ||
355 | rfkill_destroy(wifi_rfkill); | ||
356 | rfkill_unregister(bt_rfkill); | ||
357 | rfkill_destroy(bt_rfkill); | ||
390 | 358 | ||
391 | printk(KERN_INFO "compal-laptop: driver unloaded.\n"); | 359 | printk(KERN_INFO "compal-laptop: driver unloaded.\n"); |
392 | } | 360 | } |
@@ -404,3 +372,8 @@ MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*"); | |||
404 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); | 372 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); |
405 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); | 373 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); |
406 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); | 374 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); |
375 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*"); | ||
376 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*"); | ||
377 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*"); | ||
378 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*"); | ||
379 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*"); | ||
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 74909c4aaeea..661e3ac4d5b1 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c | |||
@@ -22,6 +22,9 @@ | |||
22 | #include <linux/rfkill.h> | 22 | #include <linux/rfkill.h> |
23 | #include <linux/power_supply.h> | 23 | #include <linux/power_supply.h> |
24 | #include <linux/acpi.h> | 24 | #include <linux/acpi.h> |
25 | #include <linux/mm.h> | ||
26 | #include <linux/i8042.h> | ||
27 | #include <linux/slab.h> | ||
25 | #include "../../firmware/dcdbas.h" | 28 | #include "../../firmware/dcdbas.h" |
26 | 29 | ||
27 | #define BRIGHTNESS_TOKEN 0x7d | 30 | #define BRIGHTNESS_TOKEN 0x7d |
@@ -58,6 +61,14 @@ static int da_command_code; | |||
58 | static int da_num_tokens; | 61 | static int da_num_tokens; |
59 | static struct calling_interface_token *da_tokens; | 62 | static struct calling_interface_token *da_tokens; |
60 | 63 | ||
64 | static struct platform_driver platform_driver = { | ||
65 | .driver = { | ||
66 | .name = "dell-laptop", | ||
67 | .owner = THIS_MODULE, | ||
68 | } | ||
69 | }; | ||
70 | |||
71 | static struct platform_device *platform_device; | ||
61 | static struct backlight_device *dell_backlight_device; | 72 | static struct backlight_device *dell_backlight_device; |
62 | static struct rfkill *wifi_rfkill; | 73 | static struct rfkill *wifi_rfkill; |
63 | static struct rfkill *bluetooth_rfkill; | 74 | static struct rfkill *bluetooth_rfkill; |
@@ -71,10 +82,74 @@ static const struct dmi_system_id __initdata dell_device_table[] = { | |||
71 | DMI_MATCH(DMI_CHASSIS_TYPE, "8"), | 82 | DMI_MATCH(DMI_CHASSIS_TYPE, "8"), |
72 | }, | 83 | }, |
73 | }, | 84 | }, |
85 | { | ||
86 | .ident = "Dell Computer Corporation", | ||
87 | .matches = { | ||
88 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), | ||
89 | DMI_MATCH(DMI_CHASSIS_TYPE, "8"), | ||
90 | }, | ||
91 | }, | ||
74 | { } | 92 | { } |
75 | }; | 93 | }; |
76 | 94 | ||
77 | static void parse_da_table(const struct dmi_header *dm) | 95 | static struct dmi_system_id __devinitdata dell_blacklist[] = { |
96 | /* Supported by compal-laptop */ | ||
97 | { | ||
98 | .ident = "Dell Mini 9", | ||
99 | .matches = { | ||
100 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
101 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), | ||
102 | }, | ||
103 | }, | ||
104 | { | ||
105 | .ident = "Dell Mini 10", | ||
106 | .matches = { | ||
107 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
108 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), | ||
109 | }, | ||
110 | }, | ||
111 | { | ||
112 | .ident = "Dell Mini 10v", | ||
113 | .matches = { | ||
114 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
115 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), | ||
116 | }, | ||
117 | }, | ||
118 | { | ||
119 | .ident = "Dell Inspiron 11z", | ||
120 | .matches = { | ||
121 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
122 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), | ||
123 | }, | ||
124 | }, | ||
125 | { | ||
126 | .ident = "Dell Mini 12", | ||
127 | .matches = { | ||
128 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
129 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), | ||
130 | }, | ||
131 | }, | ||
132 | {} | ||
133 | }; | ||
134 | |||
135 | static struct calling_interface_buffer *buffer; | ||
136 | static struct page *bufferpage; | ||
137 | static DEFINE_MUTEX(buffer_mutex); | ||
138 | |||
139 | static int hwswitch_state; | ||
140 | |||
141 | static void get_buffer(void) | ||
142 | { | ||
143 | mutex_lock(&buffer_mutex); | ||
144 | memset(buffer, 0, sizeof(struct calling_interface_buffer)); | ||
145 | } | ||
146 | |||
147 | static void release_buffer(void) | ||
148 | { | ||
149 | mutex_unlock(&buffer_mutex); | ||
150 | } | ||
151 | |||
152 | static void __init parse_da_table(const struct dmi_header *dm) | ||
78 | { | 153 | { |
79 | /* Final token is a terminator, so we don't want to copy it */ | 154 | /* Final token is a terminator, so we don't want to copy it */ |
80 | int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1; | 155 | int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1; |
@@ -103,7 +178,7 @@ static void parse_da_table(const struct dmi_header *dm) | |||
103 | da_num_tokens += tokens; | 178 | da_num_tokens += tokens; |
104 | } | 179 | } |
105 | 180 | ||
106 | static void find_tokens(const struct dmi_header *dm, void *dummy) | 181 | static void __init find_tokens(const struct dmi_header *dm, void *dummy) |
107 | { | 182 | { |
108 | switch (dm->type) { | 183 | switch (dm->type) { |
109 | case 0xd4: /* Indexed IO */ | 184 | case 0xd4: /* Indexed IO */ |
@@ -152,6 +227,8 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, | |||
152 | /* Derived from information in DellWirelessCtl.cpp: | 227 | /* Derived from information in DellWirelessCtl.cpp: |
153 | Class 17, select 11 is radio control. It returns an array of 32-bit values. | 228 | Class 17, select 11 is radio control. It returns an array of 32-bit values. |
154 | 229 | ||
230 | Input byte 0 = 0: Wireless information | ||
231 | |||
155 | result[0]: return code | 232 | result[0]: return code |
156 | result[1]: | 233 | result[1]: |
157 | Bit 0: Hardware switch supported | 234 | Bit 0: Hardware switch supported |
@@ -172,33 +249,62 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, | |||
172 | Bits 20-31: Reserved | 249 | Bits 20-31: Reserved |
173 | result[2]: NVRAM size in bytes | 250 | result[2]: NVRAM size in bytes |
174 | result[3]: NVRAM format version number | 251 | result[3]: NVRAM format version number |
252 | |||
253 | Input byte 0 = 2: Wireless switch configuration | ||
254 | result[0]: return code | ||
255 | result[1]: | ||
256 | Bit 0: Wifi controlled by switch | ||
257 | Bit 1: Bluetooth controlled by switch | ||
258 | Bit 2: WWAN controlled by switch | ||
259 | Bits 3-6: Reserved | ||
260 | Bit 7: Wireless switch config locked | ||
261 | Bit 8: Wifi locator enabled | ||
262 | Bits 9-14: Reserved | ||
263 | Bit 15: Wifi locator setting locked | ||
264 | Bits 16-31: Reserved | ||
175 | */ | 265 | */ |
176 | 266 | ||
177 | static int dell_rfkill_set(void *data, bool blocked) | 267 | static int dell_rfkill_set(void *data, bool blocked) |
178 | { | 268 | { |
179 | struct calling_interface_buffer buffer; | ||
180 | int disable = blocked ? 1 : 0; | 269 | int disable = blocked ? 1 : 0; |
181 | unsigned long radio = (unsigned long)data; | 270 | unsigned long radio = (unsigned long)data; |
271 | int hwswitch_bit = (unsigned long)data - 1; | ||
272 | int ret = 0; | ||
182 | 273 | ||
183 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 274 | get_buffer(); |
184 | buffer.input[0] = (1 | (radio<<8) | (disable << 16)); | 275 | dell_send_request(buffer, 17, 11); |
185 | dell_send_request(&buffer, 17, 11); | ||
186 | 276 | ||
187 | return 0; | 277 | /* If the hardware switch controls this radio, and the hardware |
278 | switch is disabled, don't allow changing the software state */ | ||
279 | if ((hwswitch_state & BIT(hwswitch_bit)) && | ||
280 | !(buffer->output[1] & BIT(16))) { | ||
281 | ret = -EINVAL; | ||
282 | goto out; | ||
283 | } | ||
284 | |||
285 | buffer->input[0] = (1 | (radio<<8) | (disable << 16)); | ||
286 | dell_send_request(buffer, 17, 11); | ||
287 | |||
288 | out: | ||
289 | release_buffer(); | ||
290 | return ret; | ||
188 | } | 291 | } |
189 | 292 | ||
190 | static void dell_rfkill_query(struct rfkill *rfkill, void *data) | 293 | static void dell_rfkill_query(struct rfkill *rfkill, void *data) |
191 | { | 294 | { |
192 | struct calling_interface_buffer buffer; | ||
193 | int status; | 295 | int status; |
194 | int bit = (unsigned long)data + 16; | 296 | int bit = (unsigned long)data + 16; |
297 | int hwswitch_bit = (unsigned long)data - 1; | ||
195 | 298 | ||
196 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 299 | get_buffer(); |
197 | dell_send_request(&buffer, 17, 11); | 300 | dell_send_request(buffer, 17, 11); |
198 | status = buffer.output[1]; | 301 | status = buffer->output[1]; |
302 | release_buffer(); | ||
199 | 303 | ||
200 | if (status & BIT(bit)) | 304 | rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); |
201 | rfkill_set_hw_state(rfkill, !!(status & BIT(16))); | 305 | |
306 | if (hwswitch_state & (BIT(hwswitch_bit))) | ||
307 | rfkill_set_hw_state(rfkill, !(status & BIT(16))); | ||
202 | } | 308 | } |
203 | 309 | ||
204 | static const struct rfkill_ops dell_rfkill_ops = { | 310 | static const struct rfkill_ops dell_rfkill_ops = { |
@@ -206,18 +312,40 @@ static const struct rfkill_ops dell_rfkill_ops = { | |||
206 | .query = dell_rfkill_query, | 312 | .query = dell_rfkill_query, |
207 | }; | 313 | }; |
208 | 314 | ||
209 | static int dell_setup_rfkill(void) | 315 | static void dell_update_rfkill(struct work_struct *ignored) |
316 | { | ||
317 | if (wifi_rfkill) | ||
318 | dell_rfkill_query(wifi_rfkill, (void *)1); | ||
319 | if (bluetooth_rfkill) | ||
320 | dell_rfkill_query(bluetooth_rfkill, (void *)2); | ||
321 | if (wwan_rfkill) | ||
322 | dell_rfkill_query(wwan_rfkill, (void *)3); | ||
323 | } | ||
324 | static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); | ||
325 | |||
326 | |||
327 | static int __init dell_setup_rfkill(void) | ||
210 | { | 328 | { |
211 | struct calling_interface_buffer buffer; | ||
212 | int status; | 329 | int status; |
213 | int ret; | 330 | int ret; |
214 | 331 | ||
215 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 332 | if (dmi_check_system(dell_blacklist)) { |
216 | dell_send_request(&buffer, 17, 11); | 333 | printk(KERN_INFO "dell-laptop: Blacklisted hardware detected - " |
217 | status = buffer.output[1]; | 334 | "not enabling rfkill\n"); |
335 | return 0; | ||
336 | } | ||
337 | |||
338 | get_buffer(); | ||
339 | dell_send_request(buffer, 17, 11); | ||
340 | status = buffer->output[1]; | ||
341 | buffer->input[0] = 0x2; | ||
342 | dell_send_request(buffer, 17, 11); | ||
343 | hwswitch_state = buffer->output[1]; | ||
344 | release_buffer(); | ||
218 | 345 | ||
219 | if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { | 346 | if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { |
220 | wifi_rfkill = rfkill_alloc("dell-wifi", NULL, RFKILL_TYPE_WLAN, | 347 | wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev, |
348 | RFKILL_TYPE_WLAN, | ||
221 | &dell_rfkill_ops, (void *) 1); | 349 | &dell_rfkill_ops, (void *) 1); |
222 | if (!wifi_rfkill) { | 350 | if (!wifi_rfkill) { |
223 | ret = -ENOMEM; | 351 | ret = -ENOMEM; |
@@ -229,7 +357,8 @@ static int dell_setup_rfkill(void) | |||
229 | } | 357 | } |
230 | 358 | ||
231 | if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { | 359 | if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { |
232 | bluetooth_rfkill = rfkill_alloc("dell-bluetooth", NULL, | 360 | bluetooth_rfkill = rfkill_alloc("dell-bluetooth", |
361 | &platform_device->dev, | ||
233 | RFKILL_TYPE_BLUETOOTH, | 362 | RFKILL_TYPE_BLUETOOTH, |
234 | &dell_rfkill_ops, (void *) 2); | 363 | &dell_rfkill_ops, (void *) 2); |
235 | if (!bluetooth_rfkill) { | 364 | if (!bluetooth_rfkill) { |
@@ -242,7 +371,9 @@ static int dell_setup_rfkill(void) | |||
242 | } | 371 | } |
243 | 372 | ||
244 | if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { | 373 | if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { |
245 | wwan_rfkill = rfkill_alloc("dell-wwan", NULL, RFKILL_TYPE_WWAN, | 374 | wwan_rfkill = rfkill_alloc("dell-wwan", |
375 | &platform_device->dev, | ||
376 | RFKILL_TYPE_WWAN, | ||
246 | &dell_rfkill_ops, (void *) 3); | 377 | &dell_rfkill_ops, (void *) 3); |
247 | if (!wwan_rfkill) { | 378 | if (!wwan_rfkill) { |
248 | ret = -ENOMEM; | 379 | ret = -ENOMEM; |
@@ -268,41 +399,67 @@ err_wifi: | |||
268 | return ret; | 399 | return ret; |
269 | } | 400 | } |
270 | 401 | ||
402 | static void dell_cleanup_rfkill(void) | ||
403 | { | ||
404 | if (wifi_rfkill) { | ||
405 | rfkill_unregister(wifi_rfkill); | ||
406 | rfkill_destroy(wifi_rfkill); | ||
407 | } | ||
408 | if (bluetooth_rfkill) { | ||
409 | rfkill_unregister(bluetooth_rfkill); | ||
410 | rfkill_destroy(bluetooth_rfkill); | ||
411 | } | ||
412 | if (wwan_rfkill) { | ||
413 | rfkill_unregister(wwan_rfkill); | ||
414 | rfkill_destroy(wwan_rfkill); | ||
415 | } | ||
416 | } | ||
417 | |||
271 | static int dell_send_intensity(struct backlight_device *bd) | 418 | static int dell_send_intensity(struct backlight_device *bd) |
272 | { | 419 | { |
273 | struct calling_interface_buffer buffer; | 420 | int ret = 0; |
274 | 421 | ||
275 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 422 | get_buffer(); |
276 | buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); | 423 | buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); |
277 | buffer.input[1] = bd->props.brightness; | 424 | buffer->input[1] = bd->props.brightness; |
278 | 425 | ||
279 | if (buffer.input[0] == -1) | 426 | if (buffer->input[0] == -1) { |
280 | return -ENODEV; | 427 | ret = -ENODEV; |
428 | goto out; | ||
429 | } | ||
281 | 430 | ||
282 | if (power_supply_is_system_supplied() > 0) | 431 | if (power_supply_is_system_supplied() > 0) |
283 | dell_send_request(&buffer, 1, 2); | 432 | dell_send_request(buffer, 1, 2); |
284 | else | 433 | else |
285 | dell_send_request(&buffer, 1, 1); | 434 | dell_send_request(buffer, 1, 1); |
286 | 435 | ||
436 | out: | ||
437 | release_buffer(); | ||
287 | return 0; | 438 | return 0; |
288 | } | 439 | } |
289 | 440 | ||
290 | static int dell_get_intensity(struct backlight_device *bd) | 441 | static int dell_get_intensity(struct backlight_device *bd) |
291 | { | 442 | { |
292 | struct calling_interface_buffer buffer; | 443 | int ret = 0; |
293 | 444 | ||
294 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 445 | get_buffer(); |
295 | buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); | 446 | buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); |
296 | 447 | ||
297 | if (buffer.input[0] == -1) | 448 | if (buffer->input[0] == -1) { |
298 | return -ENODEV; | 449 | ret = -ENODEV; |
450 | goto out; | ||
451 | } | ||
299 | 452 | ||
300 | if (power_supply_is_system_supplied() > 0) | 453 | if (power_supply_is_system_supplied() > 0) |
301 | dell_send_request(&buffer, 0, 2); | 454 | dell_send_request(buffer, 0, 2); |
302 | else | 455 | else |
303 | dell_send_request(&buffer, 0, 1); | 456 | dell_send_request(buffer, 0, 1); |
304 | 457 | ||
305 | return buffer.output[1]; | 458 | out: |
459 | release_buffer(); | ||
460 | if (ret) | ||
461 | return ret; | ||
462 | return buffer->output[1]; | ||
306 | } | 463 | } |
307 | 464 | ||
308 | static struct backlight_ops dell_ops = { | 465 | static struct backlight_ops dell_ops = { |
@@ -310,9 +467,32 @@ static struct backlight_ops dell_ops = { | |||
310 | .update_status = dell_send_intensity, | 467 | .update_status = dell_send_intensity, |
311 | }; | 468 | }; |
312 | 469 | ||
470 | bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, | ||
471 | struct serio *port) | ||
472 | { | ||
473 | static bool extended; | ||
474 | |||
475 | if (str & 0x20) | ||
476 | return false; | ||
477 | |||
478 | if (unlikely(data == 0xe0)) { | ||
479 | extended = true; | ||
480 | return false; | ||
481 | } else if (unlikely(extended)) { | ||
482 | switch (data) { | ||
483 | case 0x8: | ||
484 | schedule_delayed_work(&dell_rfkill_work, | ||
485 | round_jiffies_relative(HZ)); | ||
486 | break; | ||
487 | } | ||
488 | extended = false; | ||
489 | } | ||
490 | |||
491 | return false; | ||
492 | } | ||
493 | |||
313 | static int __init dell_init(void) | 494 | static int __init dell_init(void) |
314 | { | 495 | { |
315 | struct calling_interface_buffer buffer; | ||
316 | int max_intensity = 0; | 496 | int max_intensity = 0; |
317 | int ret; | 497 | int ret; |
318 | 498 | ||
@@ -326,11 +506,41 @@ static int __init dell_init(void) | |||
326 | return -ENODEV; | 506 | return -ENODEV; |
327 | } | 507 | } |
328 | 508 | ||
509 | ret = platform_driver_register(&platform_driver); | ||
510 | if (ret) | ||
511 | goto fail_platform_driver; | ||
512 | platform_device = platform_device_alloc("dell-laptop", -1); | ||
513 | if (!platform_device) { | ||
514 | ret = -ENOMEM; | ||
515 | goto fail_platform_device1; | ||
516 | } | ||
517 | ret = platform_device_add(platform_device); | ||
518 | if (ret) | ||
519 | goto fail_platform_device2; | ||
520 | |||
521 | /* | ||
522 | * Allocate buffer below 4GB for SMI data--only 32-bit physical addr | ||
523 | * is passed to SMI handler. | ||
524 | */ | ||
525 | bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32); | ||
526 | |||
527 | if (!bufferpage) | ||
528 | goto fail_buffer; | ||
529 | buffer = page_address(bufferpage); | ||
530 | mutex_init(&buffer_mutex); | ||
531 | |||
329 | ret = dell_setup_rfkill(); | 532 | ret = dell_setup_rfkill(); |
330 | 533 | ||
331 | if (ret) { | 534 | if (ret) { |
332 | printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n"); | 535 | printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n"); |
333 | goto out; | 536 | goto fail_rfkill; |
537 | } | ||
538 | |||
539 | ret = i8042_install_filter(dell_laptop_i8042_filter); | ||
540 | if (ret) { | ||
541 | printk(KERN_WARNING | ||
542 | "dell-laptop: Unable to install key filter\n"); | ||
543 | goto fail_filter; | ||
334 | } | 544 | } |
335 | 545 | ||
336 | #ifdef CONFIG_ACPI | 546 | #ifdef CONFIG_ACPI |
@@ -341,53 +551,67 @@ static int __init dell_init(void) | |||
341 | return 0; | 551 | return 0; |
342 | #endif | 552 | #endif |
343 | 553 | ||
344 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 554 | get_buffer(); |
345 | buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); | 555 | buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); |
346 | 556 | if (buffer->input[0] != -1) { | |
347 | if (buffer.input[0] != -1) { | 557 | dell_send_request(buffer, 0, 2); |
348 | dell_send_request(&buffer, 0, 2); | 558 | max_intensity = buffer->output[3]; |
349 | max_intensity = buffer.output[3]; | ||
350 | } | 559 | } |
560 | release_buffer(); | ||
351 | 561 | ||
352 | if (max_intensity) { | 562 | if (max_intensity) { |
353 | dell_backlight_device = backlight_device_register( | 563 | struct backlight_properties props; |
354 | "dell_backlight", | 564 | memset(&props, 0, sizeof(struct backlight_properties)); |
355 | NULL, NULL, | 565 | props.max_brightness = max_intensity; |
356 | &dell_ops); | 566 | dell_backlight_device = backlight_device_register("dell_backlight", |
567 | &platform_device->dev, | ||
568 | NULL, | ||
569 | &dell_ops, | ||
570 | &props); | ||
357 | 571 | ||
358 | if (IS_ERR(dell_backlight_device)) { | 572 | if (IS_ERR(dell_backlight_device)) { |
359 | ret = PTR_ERR(dell_backlight_device); | 573 | ret = PTR_ERR(dell_backlight_device); |
360 | dell_backlight_device = NULL; | 574 | dell_backlight_device = NULL; |
361 | goto out; | 575 | goto fail_backlight; |
362 | } | 576 | } |
363 | 577 | ||
364 | dell_backlight_device->props.max_brightness = max_intensity; | ||
365 | dell_backlight_device->props.brightness = | 578 | dell_backlight_device->props.brightness = |
366 | dell_get_intensity(dell_backlight_device); | 579 | dell_get_intensity(dell_backlight_device); |
367 | backlight_update_status(dell_backlight_device); | 580 | backlight_update_status(dell_backlight_device); |
368 | } | 581 | } |
369 | 582 | ||
370 | return 0; | 583 | return 0; |
371 | out: | 584 | |
372 | if (wifi_rfkill) | 585 | fail_backlight: |
373 | rfkill_unregister(wifi_rfkill); | 586 | i8042_remove_filter(dell_laptop_i8042_filter); |
374 | if (bluetooth_rfkill) | 587 | cancel_delayed_work_sync(&dell_rfkill_work); |
375 | rfkill_unregister(bluetooth_rfkill); | 588 | fail_filter: |
376 | if (wwan_rfkill) | 589 | dell_cleanup_rfkill(); |
377 | rfkill_unregister(wwan_rfkill); | 590 | fail_rfkill: |
591 | free_page((unsigned long)bufferpage); | ||
592 | fail_buffer: | ||
593 | platform_device_del(platform_device); | ||
594 | fail_platform_device2: | ||
595 | platform_device_put(platform_device); | ||
596 | fail_platform_device1: | ||
597 | platform_driver_unregister(&platform_driver); | ||
598 | fail_platform_driver: | ||
378 | kfree(da_tokens); | 599 | kfree(da_tokens); |
379 | return ret; | 600 | return ret; |
380 | } | 601 | } |
381 | 602 | ||
382 | static void __exit dell_exit(void) | 603 | static void __exit dell_exit(void) |
383 | { | 604 | { |
605 | i8042_remove_filter(dell_laptop_i8042_filter); | ||
606 | cancel_delayed_work_sync(&dell_rfkill_work); | ||
384 | backlight_device_unregister(dell_backlight_device); | 607 | backlight_device_unregister(dell_backlight_device); |
385 | if (wifi_rfkill) | 608 | dell_cleanup_rfkill(); |
386 | rfkill_unregister(wifi_rfkill); | 609 | if (platform_device) { |
387 | if (bluetooth_rfkill) | 610 | platform_device_unregister(platform_device); |
388 | rfkill_unregister(bluetooth_rfkill); | 611 | platform_driver_unregister(&platform_driver); |
389 | if (wwan_rfkill) | 612 | } |
390 | rfkill_unregister(wwan_rfkill); | 613 | kfree(da_tokens); |
614 | free_page((unsigned long)buffer); | ||
391 | } | 615 | } |
392 | 616 | ||
393 | module_init(dell_init); | 617 | module_init(dell_init); |
@@ -397,3 +621,4 @@ MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); | |||
397 | MODULE_DESCRIPTION("Dell laptop driver"); | 621 | MODULE_DESCRIPTION("Dell laptop driver"); |
398 | MODULE_LICENSE("GPL"); | 622 | MODULE_LICENSE("GPL"); |
399 | MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); | 623 | MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); |
624 | MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*"); | ||
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 0f900cc9fa7a..66f53c3c35e8 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c | |||
@@ -26,11 +26,13 @@ | |||
26 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <linux/init.h> | 28 | #include <linux/init.h> |
29 | #include <linux/slab.h> | ||
29 | #include <linux/types.h> | 30 | #include <linux/types.h> |
30 | #include <linux/input.h> | 31 | #include <linux/input.h> |
31 | #include <acpi/acpi_drivers.h> | 32 | #include <acpi/acpi_drivers.h> |
32 | #include <linux/acpi.h> | 33 | #include <linux/acpi.h> |
33 | #include <linux/string.h> | 34 | #include <linux/string.h> |
35 | #include <linux/dmi.h> | ||
34 | 36 | ||
35 | MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); | 37 | MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); |
36 | MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); | 38 | MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); |
@@ -38,6 +40,8 @@ MODULE_LICENSE("GPL"); | |||
38 | 40 | ||
39 | #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" | 41 | #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" |
40 | 42 | ||
43 | static int acpi_video; | ||
44 | |||
41 | MODULE_ALIAS("wmi:"DELL_EVENT_GUID); | 45 | MODULE_ALIAS("wmi:"DELL_EVENT_GUID); |
42 | 46 | ||
43 | struct key_entry { | 47 | struct key_entry { |
@@ -54,7 +58,7 @@ enum { KE_KEY, KE_SW, KE_IGNORE, KE_END }; | |||
54 | * via the keyboard controller so should not be sent again. | 58 | * via the keyboard controller so should not be sent again. |
55 | */ | 59 | */ |
56 | 60 | ||
57 | static struct key_entry dell_wmi_keymap[] = { | 61 | static struct key_entry dell_legacy_wmi_keymap[] = { |
58 | {KE_KEY, 0xe045, KEY_PROG1}, | 62 | {KE_KEY, 0xe045, KEY_PROG1}, |
59 | {KE_KEY, 0xe009, KEY_EJECTCD}, | 63 | {KE_KEY, 0xe009, KEY_EJECTCD}, |
60 | 64 | ||
@@ -72,7 +76,7 @@ static struct key_entry dell_wmi_keymap[] = { | |||
72 | 76 | ||
73 | /* The next device is at offset 6, the active devices are at | 77 | /* The next device is at offset 6, the active devices are at |
74 | offset 8 and the attached devices at offset 10 */ | 78 | offset 8 and the attached devices at offset 10 */ |
75 | {KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE}, | 79 | {KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE}, |
76 | 80 | ||
77 | {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, | 81 | {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, |
78 | 82 | ||
@@ -96,9 +100,50 @@ static struct key_entry dell_wmi_keymap[] = { | |||
96 | {KE_END, 0} | 100 | {KE_END, 0} |
97 | }; | 101 | }; |
98 | 102 | ||
103 | static bool dell_new_hk_type; | ||
104 | |||
105 | struct dell_new_keymap_entry { | ||
106 | u16 scancode; | ||
107 | u16 keycode; | ||
108 | }; | ||
109 | |||
110 | struct dell_hotkey_table { | ||
111 | struct dmi_header header; | ||
112 | struct dell_new_keymap_entry keymap[]; | ||
113 | |||
114 | }; | ||
115 | |||
116 | static struct key_entry *dell_new_wmi_keymap; | ||
117 | |||
118 | static u16 bios_to_linux_keycode[256] = { | ||
119 | |||
120 | KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG, | ||
121 | KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, | ||
122 | KEY_WWW, KEY_UNKNOWN, KEY_VOLUMEDOWN, KEY_MUTE, | ||
123 | KEY_VOLUMEUP, KEY_UNKNOWN, KEY_BATTERY, KEY_EJECTCD, | ||
124 | KEY_UNKNOWN, KEY_SLEEP, KEY_PROG1, KEY_BRIGHTNESSDOWN, | ||
125 | KEY_BRIGHTNESSUP, KEY_UNKNOWN, KEY_KBDILLUMTOGGLE, | ||
126 | KEY_UNKNOWN, KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, | ||
127 | KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_PROG2, | ||
128 | KEY_UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
129 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
130 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
131 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
132 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
133 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
134 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
135 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
136 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
137 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
138 | KEY_PROG3 | ||
139 | }; | ||
140 | |||
141 | |||
142 | static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap; | ||
143 | |||
99 | static struct input_dev *dell_wmi_input_dev; | 144 | static struct input_dev *dell_wmi_input_dev; |
100 | 145 | ||
101 | static struct key_entry *dell_wmi_get_entry_by_scancode(int code) | 146 | static struct key_entry *dell_wmi_get_entry_by_scancode(unsigned int code) |
102 | { | 147 | { |
103 | struct key_entry *key; | 148 | struct key_entry *key; |
104 | 149 | ||
@@ -109,7 +154,7 @@ static struct key_entry *dell_wmi_get_entry_by_scancode(int code) | |||
109 | return NULL; | 154 | return NULL; |
110 | } | 155 | } |
111 | 156 | ||
112 | static struct key_entry *dell_wmi_get_entry_by_keycode(int keycode) | 157 | static struct key_entry *dell_wmi_get_entry_by_keycode(unsigned int keycode) |
113 | { | 158 | { |
114 | struct key_entry *key; | 159 | struct key_entry *key; |
115 | 160 | ||
@@ -120,8 +165,8 @@ static struct key_entry *dell_wmi_get_entry_by_keycode(int keycode) | |||
120 | return NULL; | 165 | return NULL; |
121 | } | 166 | } |
122 | 167 | ||
123 | static int dell_wmi_getkeycode(struct input_dev *dev, int scancode, | 168 | static int dell_wmi_getkeycode(struct input_dev *dev, |
124 | int *keycode) | 169 | unsigned int scancode, unsigned int *keycode) |
125 | { | 170 | { |
126 | struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode); | 171 | struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode); |
127 | 172 | ||
@@ -133,13 +178,11 @@ static int dell_wmi_getkeycode(struct input_dev *dev, int scancode, | |||
133 | return -EINVAL; | 178 | return -EINVAL; |
134 | } | 179 | } |
135 | 180 | ||
136 | static int dell_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode) | 181 | static int dell_wmi_setkeycode(struct input_dev *dev, |
182 | unsigned int scancode, unsigned int keycode) | ||
137 | { | 183 | { |
138 | struct key_entry *key; | 184 | struct key_entry *key; |
139 | int old_keycode; | 185 | unsigned int old_keycode; |
140 | |||
141 | if (keycode < 0 || keycode > KEY_MAX) | ||
142 | return -EINVAL; | ||
143 | 186 | ||
144 | key = dell_wmi_get_entry_by_scancode(scancode); | 187 | key = dell_wmi_get_entry_by_scancode(scancode); |
145 | if (key && key->type == KE_KEY) { | 188 | if (key && key->type == KE_KEY) { |
@@ -158,30 +201,91 @@ static void dell_wmi_notify(u32 value, void *context) | |||
158 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | 201 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; |
159 | static struct key_entry *key; | 202 | static struct key_entry *key; |
160 | union acpi_object *obj; | 203 | union acpi_object *obj; |
204 | acpi_status status; | ||
161 | 205 | ||
162 | wmi_get_event_data(value, &response); | 206 | status = wmi_get_event_data(value, &response); |
207 | if (status != AE_OK) { | ||
208 | printk(KERN_INFO "dell-wmi: bad event status 0x%x\n", status); | ||
209 | return; | ||
210 | } | ||
163 | 211 | ||
164 | obj = (union acpi_object *)response.pointer; | 212 | obj = (union acpi_object *)response.pointer; |
165 | 213 | ||
166 | if (obj && obj->type == ACPI_TYPE_BUFFER) { | 214 | if (obj && obj->type == ACPI_TYPE_BUFFER) { |
167 | int *buffer = (int *)obj->buffer.pointer; | 215 | int reported_key; |
168 | /* | 216 | u16 *buffer_entry = (u16 *)obj->buffer.pointer; |
169 | * The upper bytes of the event may contain | 217 | if (dell_new_hk_type && (buffer_entry[1] != 0x10)) { |
170 | * additional information, so mask them off for the | 218 | printk(KERN_INFO "dell-wmi: Received unknown WMI event" |
171 | * scancode lookup | 219 | " (0x%x)\n", buffer_entry[1]); |
172 | */ | 220 | kfree(obj); |
173 | key = dell_wmi_get_entry_by_scancode(buffer[1] & 0xFFFF); | 221 | return; |
174 | if (key) { | 222 | } |
223 | |||
224 | if (dell_new_hk_type) | ||
225 | reported_key = (int)buffer_entry[2]; | ||
226 | else | ||
227 | reported_key = (int)buffer_entry[1] & 0xffff; | ||
228 | |||
229 | key = dell_wmi_get_entry_by_scancode(reported_key); | ||
230 | |||
231 | if (!key) { | ||
232 | printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", | ||
233 | reported_key); | ||
234 | } else if ((key->keycode == KEY_BRIGHTNESSUP || | ||
235 | key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) { | ||
236 | /* Don't report brightness notifications that will also | ||
237 | * come via ACPI */ | ||
238 | ; | ||
239 | } else { | ||
175 | input_report_key(dell_wmi_input_dev, key->keycode, 1); | 240 | input_report_key(dell_wmi_input_dev, key->keycode, 1); |
176 | input_sync(dell_wmi_input_dev); | 241 | input_sync(dell_wmi_input_dev); |
177 | input_report_key(dell_wmi_input_dev, key->keycode, 0); | 242 | input_report_key(dell_wmi_input_dev, key->keycode, 0); |
178 | input_sync(dell_wmi_input_dev); | 243 | input_sync(dell_wmi_input_dev); |
179 | } else if (buffer[1] & 0xFFFF) | 244 | } |
180 | printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", | ||
181 | buffer[1] & 0xFFFF); | ||
182 | } | 245 | } |
246 | kfree(obj); | ||
183 | } | 247 | } |
184 | 248 | ||
249 | |||
250 | static void setup_new_hk_map(const struct dmi_header *dm) | ||
251 | { | ||
252 | |||
253 | int i; | ||
254 | int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry); | ||
255 | struct dell_hotkey_table *table = | ||
256 | container_of(dm, struct dell_hotkey_table, header); | ||
257 | |||
258 | dell_new_wmi_keymap = kzalloc((hotkey_num+1) * | ||
259 | sizeof(struct key_entry), GFP_KERNEL); | ||
260 | |||
261 | for (i = 0; i < hotkey_num; i++) { | ||
262 | dell_new_wmi_keymap[i].type = KE_KEY; | ||
263 | dell_new_wmi_keymap[i].code = table->keymap[i].scancode; | ||
264 | dell_new_wmi_keymap[i].keycode = | ||
265 | (table->keymap[i].keycode > 255) ? 0 : | ||
266 | bios_to_linux_keycode[table->keymap[i].keycode]; | ||
267 | } | ||
268 | |||
269 | dell_new_wmi_keymap[i].type = KE_END; | ||
270 | dell_new_wmi_keymap[i].code = 0; | ||
271 | dell_new_wmi_keymap[i].keycode = 0; | ||
272 | |||
273 | dell_wmi_keymap = dell_new_wmi_keymap; | ||
274 | |||
275 | } | ||
276 | |||
277 | |||
278 | static void find_hk_type(const struct dmi_header *dm, void *dummy) | ||
279 | { | ||
280 | |||
281 | if ((dm->type == 0xb2) && (dm->length > 6)) { | ||
282 | dell_new_hk_type = true; | ||
283 | setup_new_hk_map(dm); | ||
284 | } | ||
285 | |||
286 | } | ||
287 | |||
288 | |||
185 | static int __init dell_wmi_input_setup(void) | 289 | static int __init dell_wmi_input_setup(void) |
186 | { | 290 | { |
187 | struct key_entry *key; | 291 | struct key_entry *key; |
@@ -224,34 +328,37 @@ static int __init dell_wmi_input_setup(void) | |||
224 | static int __init dell_wmi_init(void) | 328 | static int __init dell_wmi_init(void) |
225 | { | 329 | { |
226 | int err; | 330 | int err; |
331 | acpi_status status; | ||
227 | 332 | ||
228 | if (wmi_has_guid(DELL_EVENT_GUID)) { | 333 | if (!wmi_has_guid(DELL_EVENT_GUID)) { |
229 | err = dell_wmi_input_setup(); | 334 | printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n"); |
335 | return -ENODEV; | ||
336 | } | ||
230 | 337 | ||
231 | if (err) | 338 | dmi_walk(find_hk_type, NULL); |
232 | return err; | 339 | acpi_video = acpi_video_backlight_support(); |
233 | 340 | ||
234 | err = wmi_install_notify_handler(DELL_EVENT_GUID, | 341 | err = dell_wmi_input_setup(); |
235 | dell_wmi_notify, NULL); | 342 | if (err) |
236 | if (err) { | 343 | return err; |
237 | input_unregister_device(dell_wmi_input_dev); | ||
238 | printk(KERN_ERR "dell-wmi: Unable to register" | ||
239 | " notify handler - %d\n", err); | ||
240 | return err; | ||
241 | } | ||
242 | 344 | ||
243 | } else | 345 | status = wmi_install_notify_handler(DELL_EVENT_GUID, |
244 | printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n"); | 346 | dell_wmi_notify, NULL); |
347 | if (ACPI_FAILURE(status)) { | ||
348 | input_unregister_device(dell_wmi_input_dev); | ||
349 | printk(KERN_ERR | ||
350 | "dell-wmi: Unable to register notify handler - %d\n", | ||
351 | status); | ||
352 | return -ENODEV; | ||
353 | } | ||
245 | 354 | ||
246 | return 0; | 355 | return 0; |
247 | } | 356 | } |
248 | 357 | ||
249 | static void __exit dell_wmi_exit(void) | 358 | static void __exit dell_wmi_exit(void) |
250 | { | 359 | { |
251 | if (wmi_has_guid(DELL_EVENT_GUID)) { | 360 | wmi_remove_notify_handler(DELL_EVENT_GUID); |
252 | wmi_remove_notify_handler(DELL_EVENT_GUID); | 361 | input_unregister_device(dell_wmi_input_dev); |
253 | input_unregister_device(dell_wmi_input_dev); | ||
254 | } | ||
255 | } | 362 | } |
256 | 363 | ||
257 | module_init(dell_wmi_init); | 364 | module_init(dell_wmi_init); |
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 4226e5352738..0306174ba875 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * eepc-laptop.c - Asus Eee PC extras | 2 | * eeepc-laptop.c - Asus Eee PC extras |
3 | * | 3 | * |
4 | * Based on asus_acpi.c as patched for the Eee PC by Asus: | 4 | * Based on asus_acpi.c as patched for the Eee PC by Asus: |
5 | * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar | 5 | * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar |
@@ -27,27 +27,41 @@ | |||
27 | #include <linux/fb.h> | 27 | #include <linux/fb.h> |
28 | #include <linux/hwmon.h> | 28 | #include <linux/hwmon.h> |
29 | #include <linux/hwmon-sysfs.h> | 29 | #include <linux/hwmon-sysfs.h> |
30 | #include <linux/slab.h> | ||
30 | #include <acpi/acpi_drivers.h> | 31 | #include <acpi/acpi_drivers.h> |
31 | #include <acpi/acpi_bus.h> | 32 | #include <acpi/acpi_bus.h> |
32 | #include <linux/uaccess.h> | 33 | #include <linux/uaccess.h> |
33 | #include <linux/input.h> | 34 | #include <linux/input.h> |
35 | #include <linux/input/sparse-keymap.h> | ||
34 | #include <linux/rfkill.h> | 36 | #include <linux/rfkill.h> |
35 | #include <linux/pci.h> | 37 | #include <linux/pci.h> |
36 | #include <linux/pci_hotplug.h> | 38 | #include <linux/pci_hotplug.h> |
39 | #include <linux/leds.h> | ||
40 | #include <linux/dmi.h> | ||
37 | 41 | ||
38 | #define EEEPC_LAPTOP_VERSION "0.1" | 42 | #define EEEPC_LAPTOP_VERSION "0.1" |
43 | #define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver" | ||
44 | #define EEEPC_LAPTOP_FILE "eeepc" | ||
39 | 45 | ||
40 | #define EEEPC_HOTK_NAME "Eee PC Hotkey Driver" | 46 | #define EEEPC_ACPI_CLASS "hotkey" |
41 | #define EEEPC_HOTK_FILE "eeepc" | 47 | #define EEEPC_ACPI_DEVICE_NAME "Hotkey" |
42 | #define EEEPC_HOTK_CLASS "hotkey" | 48 | #define EEEPC_ACPI_HID "ASUS010" |
43 | #define EEEPC_HOTK_DEVICE_NAME "Hotkey" | ||
44 | #define EEEPC_HOTK_HID "ASUS010" | ||
45 | 49 | ||
50 | MODULE_AUTHOR("Corentin Chary, Eric Cooper"); | ||
51 | MODULE_DESCRIPTION(EEEPC_LAPTOP_NAME); | ||
52 | MODULE_LICENSE("GPL"); | ||
53 | |||
54 | static bool hotplug_disabled; | ||
55 | |||
56 | module_param(hotplug_disabled, bool, 0644); | ||
57 | MODULE_PARM_DESC(hotplug_disabled, | ||
58 | "Disable hotplug for wireless device. " | ||
59 | "If your laptop need that, please report to " | ||
60 | "acpi4asus-user@lists.sourceforge.net."); | ||
46 | 61 | ||
47 | /* | 62 | /* |
48 | * Definitions for Asus EeePC | 63 | * Definitions for Asus EeePC |
49 | */ | 64 | */ |
50 | #define NOTIFY_WLAN_ON 0x10 | ||
51 | #define NOTIFY_BRN_MIN 0x20 | 65 | #define NOTIFY_BRN_MIN 0x20 |
52 | #define NOTIFY_BRN_MAX 0x2f | 66 | #define NOTIFY_BRN_MAX 0x2f |
53 | 67 | ||
@@ -117,145 +131,63 @@ static const char *cm_setv[] = { | |||
117 | NULL, NULL, "PBPS", "TPDS" | 131 | NULL, NULL, "PBPS", "TPDS" |
118 | }; | 132 | }; |
119 | 133 | ||
120 | #define EEEPC_EC "\\_SB.PCI0.SBRG.EC0." | 134 | static const struct key_entry eeepc_keymap[] = { |
121 | 135 | { KE_KEY, 0x10, { KEY_WLAN } }, | |
122 | #define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */ | 136 | { KE_KEY, 0x11, { KEY_WLAN } }, |
123 | #define EEEPC_EC_SC02 0x63 | 137 | { KE_KEY, 0x12, { KEY_PROG1 } }, |
124 | #define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */ | 138 | { KE_KEY, 0x13, { KEY_MUTE } }, |
125 | #define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */ | 139 | { KE_KEY, 0x14, { KEY_VOLUMEDOWN } }, |
126 | #define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */ | 140 | { KE_KEY, 0x15, { KEY_VOLUMEUP } }, |
127 | #define EEEPC_EC_SFB3 0xD3 | 141 | { KE_KEY, 0x16, { KEY_DISPLAY_OFF } }, |
142 | { KE_KEY, 0x1a, { KEY_COFFEE } }, | ||
143 | { KE_KEY, 0x1b, { KEY_ZOOM } }, | ||
144 | { KE_KEY, 0x1c, { KEY_PROG2 } }, | ||
145 | { KE_KEY, 0x1d, { KEY_PROG3 } }, | ||
146 | { KE_KEY, NOTIFY_BRN_MIN, { KEY_BRIGHTNESSDOWN } }, | ||
147 | { KE_KEY, NOTIFY_BRN_MAX, { KEY_BRIGHTNESSUP } }, | ||
148 | { KE_KEY, 0x30, { KEY_SWITCHVIDEOMODE } }, | ||
149 | { KE_KEY, 0x31, { KEY_SWITCHVIDEOMODE } }, | ||
150 | { KE_KEY, 0x32, { KEY_SWITCHVIDEOMODE } }, | ||
151 | { KE_KEY, 0x37, { KEY_F13 } }, /* Disable Touchpad */ | ||
152 | { KE_KEY, 0x38, { KEY_F14 } }, | ||
153 | { KE_END, 0 }, | ||
154 | }; | ||
128 | 155 | ||
129 | /* | 156 | /* |
130 | * This is the main structure, we can use it to store useful information | 157 | * This is the main structure, we can use it to store useful information |
131 | * about the hotk device | ||
132 | */ | 158 | */ |
133 | struct eeepc_hotk { | 159 | struct eeepc_laptop { |
134 | struct acpi_device *device; /* the device we are in */ | 160 | acpi_handle handle; /* the handle of the acpi device */ |
135 | acpi_handle handle; /* the handle of the hotk device */ | ||
136 | u32 cm_supported; /* the control methods supported | 161 | u32 cm_supported; /* the control methods supported |
137 | by this BIOS */ | 162 | by this BIOS */ |
138 | uint init_flag; /* Init flags */ | 163 | bool cpufv_disabled; |
164 | bool hotplug_disabled; | ||
139 | u16 event_count[128]; /* count for each event */ | 165 | u16 event_count[128]; /* count for each event */ |
166 | |||
167 | struct platform_device *platform_device; | ||
168 | struct device *hwmon_device; | ||
169 | struct backlight_device *backlight_device; | ||
170 | |||
140 | struct input_dev *inputdev; | 171 | struct input_dev *inputdev; |
141 | u16 *keycode_map; | 172 | |
142 | struct rfkill *wlan_rfkill; | 173 | struct rfkill *wlan_rfkill; |
143 | struct rfkill *bluetooth_rfkill; | 174 | struct rfkill *bluetooth_rfkill; |
144 | struct rfkill *wwan3g_rfkill; | 175 | struct rfkill *wwan3g_rfkill; |
145 | struct rfkill *wimax_rfkill; | 176 | struct rfkill *wimax_rfkill; |
177 | |||
146 | struct hotplug_slot *hotplug_slot; | 178 | struct hotplug_slot *hotplug_slot; |
147 | struct mutex hotplug_lock; | 179 | struct mutex hotplug_lock; |
148 | }; | ||
149 | |||
150 | /* The actual device the driver binds to */ | ||
151 | static struct eeepc_hotk *ehotk; | ||
152 | |||
153 | /* Platform device/driver */ | ||
154 | static int eeepc_hotk_thaw(struct device *device); | ||
155 | static int eeepc_hotk_restore(struct device *device); | ||
156 | |||
157 | static struct dev_pm_ops eeepc_pm_ops = { | ||
158 | .thaw = eeepc_hotk_thaw, | ||
159 | .restore = eeepc_hotk_restore, | ||
160 | }; | ||
161 | |||
162 | static struct platform_driver platform_driver = { | ||
163 | .driver = { | ||
164 | .name = EEEPC_HOTK_FILE, | ||
165 | .owner = THIS_MODULE, | ||
166 | .pm = &eeepc_pm_ops, | ||
167 | } | ||
168 | }; | ||
169 | |||
170 | static struct platform_device *platform_device; | ||
171 | |||
172 | struct key_entry { | ||
173 | char type; | ||
174 | u8 code; | ||
175 | u16 keycode; | ||
176 | }; | ||
177 | |||
178 | enum { KE_KEY, KE_END }; | ||
179 | |||
180 | static struct key_entry eeepc_keymap[] = { | ||
181 | /* Sleep already handled via generic ACPI code */ | ||
182 | {KE_KEY, 0x10, KEY_WLAN }, | ||
183 | {KE_KEY, 0x11, KEY_WLAN }, | ||
184 | {KE_KEY, 0x12, KEY_PROG1 }, | ||
185 | {KE_KEY, 0x13, KEY_MUTE }, | ||
186 | {KE_KEY, 0x14, KEY_VOLUMEDOWN }, | ||
187 | {KE_KEY, 0x15, KEY_VOLUMEUP }, | ||
188 | {KE_KEY, 0x1a, KEY_COFFEE }, | ||
189 | {KE_KEY, 0x1b, KEY_ZOOM }, | ||
190 | {KE_KEY, 0x1c, KEY_PROG2 }, | ||
191 | {KE_KEY, 0x1d, KEY_PROG3 }, | ||
192 | {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN }, | ||
193 | {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP }, | ||
194 | {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE }, | ||
195 | {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE }, | ||
196 | {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE }, | ||
197 | {KE_END, 0}, | ||
198 | }; | ||
199 | |||
200 | /* | ||
201 | * The hotkey driver declaration | ||
202 | */ | ||
203 | static int eeepc_hotk_add(struct acpi_device *device); | ||
204 | static int eeepc_hotk_remove(struct acpi_device *device, int type); | ||
205 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event); | ||
206 | |||
207 | static const struct acpi_device_id eeepc_device_ids[] = { | ||
208 | {EEEPC_HOTK_HID, 0}, | ||
209 | {"", 0}, | ||
210 | }; | ||
211 | MODULE_DEVICE_TABLE(acpi, eeepc_device_ids); | ||
212 | |||
213 | static struct acpi_driver eeepc_hotk_driver = { | ||
214 | .name = EEEPC_HOTK_NAME, | ||
215 | .class = EEEPC_HOTK_CLASS, | ||
216 | .ids = eeepc_device_ids, | ||
217 | .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, | ||
218 | .ops = { | ||
219 | .add = eeepc_hotk_add, | ||
220 | .remove = eeepc_hotk_remove, | ||
221 | .notify = eeepc_hotk_notify, | ||
222 | }, | ||
223 | }; | ||
224 | |||
225 | /* PCI hotplug ops */ | ||
226 | static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value); | ||
227 | 180 | ||
228 | static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { | 181 | struct led_classdev tpd_led; |
229 | .owner = THIS_MODULE, | 182 | int tpd_led_wk; |
230 | .get_adapter_status = eeepc_get_adapter_status, | 183 | struct workqueue_struct *led_workqueue; |
231 | .get_power_status = eeepc_get_adapter_status, | 184 | struct work_struct tpd_led_work; |
232 | }; | 185 | }; |
233 | 186 | ||
234 | /* The backlight device /sys/class/backlight */ | ||
235 | static struct backlight_device *eeepc_backlight_device; | ||
236 | |||
237 | /* The hwmon device */ | ||
238 | static struct device *eeepc_hwmon_device; | ||
239 | |||
240 | /* | ||
241 | * The backlight class declaration | ||
242 | */ | ||
243 | static int read_brightness(struct backlight_device *bd); | ||
244 | static int update_bl_status(struct backlight_device *bd); | ||
245 | static struct backlight_ops eeepcbl_ops = { | ||
246 | .get_brightness = read_brightness, | ||
247 | .update_status = update_bl_status, | ||
248 | }; | ||
249 | |||
250 | MODULE_AUTHOR("Corentin Chary, Eric Cooper"); | ||
251 | MODULE_DESCRIPTION(EEEPC_HOTK_NAME); | ||
252 | MODULE_LICENSE("GPL"); | ||
253 | |||
254 | /* | 187 | /* |
255 | * ACPI Helpers | 188 | * ACPI Helpers |
256 | */ | 189 | */ |
257 | static int write_acpi_int(acpi_handle handle, const char *method, int val, | 190 | static int write_acpi_int(acpi_handle handle, const char *method, int val) |
258 | struct acpi_buffer *output) | ||
259 | { | 191 | { |
260 | struct acpi_object_list params; | 192 | struct acpi_object_list params; |
261 | union acpi_object in_obj; | 193 | union acpi_object in_obj; |
@@ -266,7 +198,7 @@ static int write_acpi_int(acpi_handle handle, const char *method, int val, | |||
266 | in_obj.type = ACPI_TYPE_INTEGER; | 198 | in_obj.type = ACPI_TYPE_INTEGER; |
267 | in_obj.integer.value = val; | 199 | in_obj.integer.value = val; |
268 | 200 | ||
269 | status = acpi_evaluate_object(handle, (char *)method, ¶ms, output); | 201 | status = acpi_evaluate_object(handle, (char *)method, ¶ms, NULL); |
270 | return (status == AE_OK ? 0 : -1); | 202 | return (status == AE_OK ? 0 : -1); |
271 | } | 203 | } |
272 | 204 | ||
@@ -285,81 +217,56 @@ static int read_acpi_int(acpi_handle handle, const char *method, int *val) | |||
285 | } | 217 | } |
286 | } | 218 | } |
287 | 219 | ||
288 | static int set_acpi(int cm, int value) | 220 | static int set_acpi(struct eeepc_laptop *eeepc, int cm, int value) |
289 | { | 221 | { |
290 | if (ehotk->cm_supported & (0x1 << cm)) { | 222 | const char *method = cm_setv[cm]; |
291 | const char *method = cm_setv[cm]; | ||
292 | if (method == NULL) | ||
293 | return -ENODEV; | ||
294 | if (write_acpi_int(ehotk->handle, method, value, NULL)) | ||
295 | pr_warning("Error writing %s\n", method); | ||
296 | } | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static int get_acpi(int cm) | ||
301 | { | ||
302 | int value = -ENODEV; | ||
303 | if ((ehotk->cm_supported & (0x1 << cm))) { | ||
304 | const char *method = cm_getv[cm]; | ||
305 | if (method == NULL) | ||
306 | return -ENODEV; | ||
307 | if (read_acpi_int(ehotk->handle, method, &value)) | ||
308 | pr_warning("Error reading %s\n", method); | ||
309 | } | ||
310 | return value; | ||
311 | } | ||
312 | 223 | ||
313 | /* | 224 | if (method == NULL) |
314 | * Backlight | 225 | return -ENODEV; |
315 | */ | 226 | if ((eeepc->cm_supported & (0x1 << cm)) == 0) |
316 | static int read_brightness(struct backlight_device *bd) | 227 | return -ENODEV; |
317 | { | ||
318 | return get_acpi(CM_ASL_PANELBRIGHT); | ||
319 | } | ||
320 | 228 | ||
321 | static int set_brightness(struct backlight_device *bd, int value) | 229 | if (write_acpi_int(eeepc->handle, method, value)) |
322 | { | 230 | pr_warning("Error writing %s\n", method); |
323 | value = max(0, min(15, value)); | 231 | return 0; |
324 | return set_acpi(CM_ASL_PANELBRIGHT, value); | ||
325 | } | 232 | } |
326 | 233 | ||
327 | static int update_bl_status(struct backlight_device *bd) | 234 | static int get_acpi(struct eeepc_laptop *eeepc, int cm) |
328 | { | 235 | { |
329 | return set_brightness(bd, bd->props.brightness); | 236 | const char *method = cm_getv[cm]; |
330 | } | 237 | int value; |
331 | 238 | ||
332 | /* | 239 | if (method == NULL) |
333 | * Rfkill helpers | 240 | return -ENODEV; |
334 | */ | 241 | if ((eeepc->cm_supported & (0x1 << cm)) == 0) |
242 | return -ENODEV; | ||
335 | 243 | ||
336 | static bool eeepc_wlan_rfkill_blocked(void) | 244 | if (read_acpi_int(eeepc->handle, method, &value)) |
337 | { | 245 | pr_warning("Error reading %s\n", method); |
338 | if (get_acpi(CM_ASL_WLAN) == 1) | 246 | return value; |
339 | return false; | ||
340 | return true; | ||
341 | } | 247 | } |
342 | 248 | ||
343 | static int eeepc_rfkill_set(void *data, bool blocked) | 249 | static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm, |
250 | acpi_handle *handle) | ||
344 | { | 251 | { |
345 | unsigned long asl = (unsigned long)data; | 252 | const char *method = cm_setv[cm]; |
346 | return set_acpi(asl, !blocked); | 253 | acpi_status status; |
347 | } | ||
348 | 254 | ||
349 | static const struct rfkill_ops eeepc_rfkill_ops = { | 255 | if (method == NULL) |
350 | .set_block = eeepc_rfkill_set, | 256 | return -ENODEV; |
351 | }; | 257 | if ((eeepc->cm_supported & (0x1 << cm)) == 0) |
258 | return -ENODEV; | ||
352 | 259 | ||
353 | static void __devinit eeepc_enable_camera(void) | 260 | status = acpi_get_handle(eeepc->handle, (char *)method, |
354 | { | 261 | handle); |
355 | /* | 262 | if (status != AE_OK) { |
356 | * If the following call to set_acpi() fails, it's because there's no | 263 | pr_warning("Error finding %s\n", method); |
357 | * camera so we can ignore the error. | 264 | return -ENODEV; |
358 | */ | 265 | } |
359 | if (get_acpi(CM_ASL_CAMERA) == 0) | 266 | return 0; |
360 | set_acpi(CM_ASL_CAMERA, 1); | ||
361 | } | 267 | } |
362 | 268 | ||
269 | |||
363 | /* | 270 | /* |
364 | * Sys helpers | 271 | * Sys helpers |
365 | */ | 272 | */ |
@@ -372,60 +279,63 @@ static int parse_arg(const char *buf, unsigned long count, int *val) | |||
372 | return count; | 279 | return count; |
373 | } | 280 | } |
374 | 281 | ||
375 | static ssize_t store_sys_acpi(int cm, const char *buf, size_t count) | 282 | static ssize_t store_sys_acpi(struct device *dev, int cm, |
283 | const char *buf, size_t count) | ||
376 | { | 284 | { |
285 | struct eeepc_laptop *eeepc = dev_get_drvdata(dev); | ||
377 | int rv, value; | 286 | int rv, value; |
378 | 287 | ||
379 | rv = parse_arg(buf, count, &value); | 288 | rv = parse_arg(buf, count, &value); |
380 | if (rv > 0) | 289 | if (rv > 0) |
381 | value = set_acpi(cm, value); | 290 | value = set_acpi(eeepc, cm, value); |
382 | if (value < 0) | 291 | if (value < 0) |
383 | return value; | 292 | return -EIO; |
384 | return rv; | 293 | return rv; |
385 | } | 294 | } |
386 | 295 | ||
387 | static ssize_t show_sys_acpi(int cm, char *buf) | 296 | static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf) |
388 | { | 297 | { |
389 | int value = get_acpi(cm); | 298 | struct eeepc_laptop *eeepc = dev_get_drvdata(dev); |
299 | int value = get_acpi(eeepc, cm); | ||
390 | 300 | ||
391 | if (value < 0) | 301 | if (value < 0) |
392 | return value; | 302 | return -EIO; |
393 | return sprintf(buf, "%d\n", value); | 303 | return sprintf(buf, "%d\n", value); |
394 | } | 304 | } |
395 | 305 | ||
396 | #define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \ | 306 | #define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \ |
397 | static ssize_t show_##_name(struct device *dev, \ | 307 | static ssize_t show_##_name(struct device *dev, \ |
398 | struct device_attribute *attr, \ | 308 | struct device_attribute *attr, \ |
399 | char *buf) \ | 309 | char *buf) \ |
400 | { \ | 310 | { \ |
401 | return show_sys_acpi(_cm, buf); \ | 311 | return show_sys_acpi(dev, _cm, buf); \ |
402 | } \ | 312 | } \ |
403 | static ssize_t store_##_name(struct device *dev, \ | 313 | static ssize_t store_##_name(struct device *dev, \ |
404 | struct device_attribute *attr, \ | 314 | struct device_attribute *attr, \ |
405 | const char *buf, size_t count) \ | 315 | const char *buf, size_t count) \ |
406 | { \ | 316 | { \ |
407 | return store_sys_acpi(_cm, buf, count); \ | 317 | return store_sys_acpi(dev, _cm, buf, count); \ |
408 | } \ | 318 | } \ |
409 | static struct device_attribute dev_attr_##_name = { \ | 319 | static struct device_attribute dev_attr_##_name = { \ |
410 | .attr = { \ | 320 | .attr = { \ |
411 | .name = __stringify(_name), \ | 321 | .name = __stringify(_name), \ |
412 | .mode = 0644 }, \ | 322 | .mode = _mode }, \ |
413 | .show = show_##_name, \ | 323 | .show = show_##_name, \ |
414 | .store = store_##_name, \ | 324 | .store = store_##_name, \ |
415 | } | 325 | } |
416 | 326 | ||
417 | EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA); | 327 | EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA); |
418 | EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER); | 328 | EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER); |
419 | EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH); | 329 | EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH); |
420 | 330 | ||
421 | struct eeepc_cpufv { | 331 | struct eeepc_cpufv { |
422 | int num; | 332 | int num; |
423 | int cur; | 333 | int cur; |
424 | }; | 334 | }; |
425 | 335 | ||
426 | static int get_cpufv(struct eeepc_cpufv *c) | 336 | static int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c) |
427 | { | 337 | { |
428 | c->cur = get_acpi(CM_ASL_CPUFV); | 338 | c->cur = get_acpi(eeepc, CM_ASL_CPUFV); |
429 | c->num = (c->cur >> 8) & 0xff; | 339 | c->num = (c->cur >> 8) & 0xff; |
430 | c->cur &= 0xff; | 340 | c->cur &= 0xff; |
431 | if (c->cur < 0 || c->num <= 0 || c->num > 12) | 341 | if (c->cur < 0 || c->num <= 0 || c->num > 12) |
@@ -437,11 +347,12 @@ static ssize_t show_available_cpufv(struct device *dev, | |||
437 | struct device_attribute *attr, | 347 | struct device_attribute *attr, |
438 | char *buf) | 348 | char *buf) |
439 | { | 349 | { |
350 | struct eeepc_laptop *eeepc = dev_get_drvdata(dev); | ||
440 | struct eeepc_cpufv c; | 351 | struct eeepc_cpufv c; |
441 | int i; | 352 | int i; |
442 | ssize_t len = 0; | 353 | ssize_t len = 0; |
443 | 354 | ||
444 | if (get_cpufv(&c)) | 355 | if (get_cpufv(eeepc, &c)) |
445 | return -ENODEV; | 356 | return -ENODEV; |
446 | for (i = 0; i < c.num; i++) | 357 | for (i = 0; i < c.num; i++) |
447 | len += sprintf(buf + len, "%d ", i); | 358 | len += sprintf(buf + len, "%d ", i); |
@@ -453,9 +364,10 @@ static ssize_t show_cpufv(struct device *dev, | |||
453 | struct device_attribute *attr, | 364 | struct device_attribute *attr, |
454 | char *buf) | 365 | char *buf) |
455 | { | 366 | { |
367 | struct eeepc_laptop *eeepc = dev_get_drvdata(dev); | ||
456 | struct eeepc_cpufv c; | 368 | struct eeepc_cpufv c; |
457 | 369 | ||
458 | if (get_cpufv(&c)) | 370 | if (get_cpufv(eeepc, &c)) |
459 | return -ENODEV; | 371 | return -ENODEV; |
460 | return sprintf(buf, "%#x\n", (c.num << 8) | c.cur); | 372 | return sprintf(buf, "%#x\n", (c.num << 8) | c.cur); |
461 | } | 373 | } |
@@ -464,20 +376,58 @@ static ssize_t store_cpufv(struct device *dev, | |||
464 | struct device_attribute *attr, | 376 | struct device_attribute *attr, |
465 | const char *buf, size_t count) | 377 | const char *buf, size_t count) |
466 | { | 378 | { |
379 | struct eeepc_laptop *eeepc = dev_get_drvdata(dev); | ||
467 | struct eeepc_cpufv c; | 380 | struct eeepc_cpufv c; |
468 | int rv, value; | 381 | int rv, value; |
469 | 382 | ||
470 | if (get_cpufv(&c)) | 383 | if (eeepc->cpufv_disabled) |
384 | return -EPERM; | ||
385 | if (get_cpufv(eeepc, &c)) | ||
471 | return -ENODEV; | 386 | return -ENODEV; |
472 | rv = parse_arg(buf, count, &value); | 387 | rv = parse_arg(buf, count, &value); |
473 | if (rv < 0) | 388 | if (rv < 0) |
474 | return rv; | 389 | return rv; |
475 | if (!rv || value < 0 || value >= c.num) | 390 | if (!rv || value < 0 || value >= c.num) |
476 | return -EINVAL; | 391 | return -EINVAL; |
477 | set_acpi(CM_ASL_CPUFV, value); | 392 | set_acpi(eeepc, CM_ASL_CPUFV, value); |
478 | return rv; | 393 | return rv; |
479 | } | 394 | } |
480 | 395 | ||
396 | static ssize_t show_cpufv_disabled(struct device *dev, | ||
397 | struct device_attribute *attr, | ||
398 | char *buf) | ||
399 | { | ||
400 | struct eeepc_laptop *eeepc = dev_get_drvdata(dev); | ||
401 | |||
402 | return sprintf(buf, "%d\n", eeepc->cpufv_disabled); | ||
403 | } | ||
404 | |||
405 | static ssize_t store_cpufv_disabled(struct device *dev, | ||
406 | struct device_attribute *attr, | ||
407 | const char *buf, size_t count) | ||
408 | { | ||
409 | struct eeepc_laptop *eeepc = dev_get_drvdata(dev); | ||
410 | int rv, value; | ||
411 | |||
412 | rv = parse_arg(buf, count, &value); | ||
413 | if (rv < 0) | ||
414 | return rv; | ||
415 | |||
416 | switch (value) { | ||
417 | case 0: | ||
418 | if (eeepc->cpufv_disabled) | ||
419 | pr_warning("cpufv enabled (not officially supported " | ||
420 | "on this model)\n"); | ||
421 | eeepc->cpufv_disabled = false; | ||
422 | return rv; | ||
423 | case 1: | ||
424 | return -EPERM; | ||
425 | default: | ||
426 | return -EINVAL; | ||
427 | } | ||
428 | } | ||
429 | |||
430 | |||
481 | static struct device_attribute dev_attr_cpufv = { | 431 | static struct device_attribute dev_attr_cpufv = { |
482 | .attr = { | 432 | .attr = { |
483 | .name = "cpufv", | 433 | .name = "cpufv", |
@@ -493,12 +443,22 @@ static struct device_attribute dev_attr_available_cpufv = { | |||
493 | .show = show_available_cpufv | 443 | .show = show_available_cpufv |
494 | }; | 444 | }; |
495 | 445 | ||
446 | static struct device_attribute dev_attr_cpufv_disabled = { | ||
447 | .attr = { | ||
448 | .name = "cpufv_disabled", | ||
449 | .mode = 0644 }, | ||
450 | .show = show_cpufv_disabled, | ||
451 | .store = store_cpufv_disabled | ||
452 | }; | ||
453 | |||
454 | |||
496 | static struct attribute *platform_attributes[] = { | 455 | static struct attribute *platform_attributes[] = { |
497 | &dev_attr_camera.attr, | 456 | &dev_attr_camera.attr, |
498 | &dev_attr_cardr.attr, | 457 | &dev_attr_cardr.attr, |
499 | &dev_attr_disp.attr, | 458 | &dev_attr_disp.attr, |
500 | &dev_attr_cpufv.attr, | 459 | &dev_attr_cpufv.attr, |
501 | &dev_attr_available_cpufv.attr, | 460 | &dev_attr_available_cpufv.attr, |
461 | &dev_attr_cpufv_disabled.attr, | ||
502 | NULL | 462 | NULL |
503 | }; | 463 | }; |
504 | 464 | ||
@@ -506,162 +466,149 @@ static struct attribute_group platform_attribute_group = { | |||
506 | .attrs = platform_attributes | 466 | .attrs = platform_attributes |
507 | }; | 467 | }; |
508 | 468 | ||
509 | /* | 469 | static int eeepc_platform_init(struct eeepc_laptop *eeepc) |
510 | * Hotkey functions | ||
511 | */ | ||
512 | static struct key_entry *eepc_get_entry_by_scancode(int code) | ||
513 | { | 470 | { |
514 | struct key_entry *key; | 471 | int result; |
515 | |||
516 | for (key = eeepc_keymap; key->type != KE_END; key++) | ||
517 | if (code == key->code) | ||
518 | return key; | ||
519 | 472 | ||
520 | return NULL; | 473 | eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1); |
521 | } | 474 | if (!eeepc->platform_device) |
475 | return -ENOMEM; | ||
476 | platform_set_drvdata(eeepc->platform_device, eeepc); | ||
522 | 477 | ||
523 | static struct key_entry *eepc_get_entry_by_keycode(int code) | 478 | result = platform_device_add(eeepc->platform_device); |
524 | { | 479 | if (result) |
525 | struct key_entry *key; | 480 | goto fail_platform_device; |
526 | 481 | ||
527 | for (key = eeepc_keymap; key->type != KE_END; key++) | 482 | result = sysfs_create_group(&eeepc->platform_device->dev.kobj, |
528 | if (code == key->keycode && key->type == KE_KEY) | 483 | &platform_attribute_group); |
529 | return key; | 484 | if (result) |
485 | goto fail_sysfs; | ||
486 | return 0; | ||
530 | 487 | ||
531 | return NULL; | 488 | fail_sysfs: |
489 | platform_device_del(eeepc->platform_device); | ||
490 | fail_platform_device: | ||
491 | platform_device_put(eeepc->platform_device); | ||
492 | return result; | ||
532 | } | 493 | } |
533 | 494 | ||
534 | static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode) | 495 | static void eeepc_platform_exit(struct eeepc_laptop *eeepc) |
535 | { | 496 | { |
536 | struct key_entry *key = eepc_get_entry_by_scancode(scancode); | 497 | sysfs_remove_group(&eeepc->platform_device->dev.kobj, |
498 | &platform_attribute_group); | ||
499 | platform_device_unregister(eeepc->platform_device); | ||
500 | } | ||
537 | 501 | ||
538 | if (key && key->type == KE_KEY) { | 502 | /* |
539 | *keycode = key->keycode; | 503 | * LEDs |
540 | return 0; | 504 | */ |
541 | } | 505 | /* |
506 | * These functions actually update the LED's, and are called from a | ||
507 | * workqueue. By doing this as separate work rather than when the LED | ||
508 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a | ||
509 | * potentially bad time, such as a timer interrupt. | ||
510 | */ | ||
511 | static void tpd_led_update(struct work_struct *work) | ||
512 | { | ||
513 | struct eeepc_laptop *eeepc; | ||
514 | |||
515 | eeepc = container_of(work, struct eeepc_laptop, tpd_led_work); | ||
542 | 516 | ||
543 | return -EINVAL; | 517 | set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk); |
544 | } | 518 | } |
545 | 519 | ||
546 | static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode) | 520 | static void tpd_led_set(struct led_classdev *led_cdev, |
521 | enum led_brightness value) | ||
547 | { | 522 | { |
548 | struct key_entry *key; | 523 | struct eeepc_laptop *eeepc; |
549 | int old_keycode; | ||
550 | 524 | ||
551 | if (keycode < 0 || keycode > KEY_MAX) | 525 | eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led); |
552 | return -EINVAL; | ||
553 | 526 | ||
554 | key = eepc_get_entry_by_scancode(scancode); | 527 | eeepc->tpd_led_wk = (value > 0) ? 1 : 0; |
555 | if (key && key->type == KE_KEY) { | 528 | queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); |
556 | old_keycode = key->keycode; | ||
557 | key->keycode = keycode; | ||
558 | set_bit(keycode, dev->keybit); | ||
559 | if (!eepc_get_entry_by_keycode(old_keycode)) | ||
560 | clear_bit(old_keycode, dev->keybit); | ||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | return -EINVAL; | ||
565 | } | 529 | } |
566 | 530 | ||
567 | static void cmsg_quirk(int cm, const char *name) | 531 | static int eeepc_led_init(struct eeepc_laptop *eeepc) |
568 | { | 532 | { |
569 | int dummy; | 533 | int rv; |
570 | 534 | ||
571 | /* Some BIOSes do not report cm although it is avaliable. | 535 | if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV) |
572 | Check if cm_getv[cm] works and, if yes, assume cm should be set. */ | 536 | return 0; |
573 | if (!(ehotk->cm_supported & (1 << cm)) | ||
574 | && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) { | ||
575 | pr_info("%s (%x) not reported by BIOS," | ||
576 | " enabling anyway\n", name, 1 << cm); | ||
577 | ehotk->cm_supported |= 1 << cm; | ||
578 | } | ||
579 | } | ||
580 | 537 | ||
581 | static void cmsg_quirks(void) | 538 | eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); |
582 | { | 539 | if (!eeepc->led_workqueue) |
583 | cmsg_quirk(CM_ASL_LID, "LID"); | 540 | return -ENOMEM; |
584 | cmsg_quirk(CM_ASL_TYPE, "TYPE"); | 541 | INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); |
585 | cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER"); | ||
586 | cmsg_quirk(CM_ASL_TPD, "TPD"); | ||
587 | } | ||
588 | 542 | ||
589 | static int eeepc_hotk_check(void) | 543 | eeepc->tpd_led.name = "eeepc::touchpad"; |
590 | { | 544 | eeepc->tpd_led.brightness_set = tpd_led_set; |
591 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 545 | eeepc->tpd_led.max_brightness = 1; |
592 | int result; | ||
593 | 546 | ||
594 | result = acpi_bus_get_status(ehotk->device); | 547 | rv = led_classdev_register(&eeepc->platform_device->dev, |
595 | if (result) | 548 | &eeepc->tpd_led); |
596 | return result; | 549 | if (rv) { |
597 | if (ehotk->device->status.present) { | 550 | destroy_workqueue(eeepc->led_workqueue); |
598 | if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag, | 551 | return rv; |
599 | &buffer)) { | ||
600 | pr_err("Hotkey initialization failed\n"); | ||
601 | return -ENODEV; | ||
602 | } else { | ||
603 | pr_notice("Hotkey init flags 0x%x\n", ehotk->init_flag); | ||
604 | } | ||
605 | /* get control methods supported */ | ||
606 | if (read_acpi_int(ehotk->handle, "CMSG" | ||
607 | , &ehotk->cm_supported)) { | ||
608 | pr_err("Get control methods supported failed\n"); | ||
609 | return -ENODEV; | ||
610 | } else { | ||
611 | cmsg_quirks(); | ||
612 | pr_info("Get control methods supported: 0x%x\n", | ||
613 | ehotk->cm_supported); | ||
614 | } | ||
615 | } else { | ||
616 | pr_err("Hotkey device not present, aborting\n"); | ||
617 | return -EINVAL; | ||
618 | } | 552 | } |
553 | |||
619 | return 0; | 554 | return 0; |
620 | } | 555 | } |
621 | 556 | ||
622 | static int notify_brn(void) | 557 | static void eeepc_led_exit(struct eeepc_laptop *eeepc) |
623 | { | 558 | { |
624 | /* returns the *previous* brightness, or -1 */ | 559 | if (eeepc->tpd_led.dev) |
625 | struct backlight_device *bd = eeepc_backlight_device; | 560 | led_classdev_unregister(&eeepc->tpd_led); |
626 | if (bd) { | 561 | if (eeepc->led_workqueue) |
627 | int old = bd->props.brightness; | 562 | destroy_workqueue(eeepc->led_workqueue); |
628 | backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); | ||
629 | return old; | ||
630 | } | ||
631 | return -1; | ||
632 | } | 563 | } |
633 | 564 | ||
634 | static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, | ||
635 | u8 *value) | ||
636 | { | ||
637 | int val = get_acpi(CM_ASL_WLAN); | ||
638 | |||
639 | if (val == 1 || val == 0) | ||
640 | *value = val; | ||
641 | else | ||
642 | return -EINVAL; | ||
643 | 565 | ||
644 | return 0; | 566 | /* |
567 | * PCI hotplug (for wlan rfkill) | ||
568 | */ | ||
569 | static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc) | ||
570 | { | ||
571 | if (get_acpi(eeepc, CM_ASL_WLAN) == 1) | ||
572 | return false; | ||
573 | return true; | ||
645 | } | 574 | } |
646 | 575 | ||
647 | static void eeepc_rfkill_hotplug(void) | 576 | static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc) |
648 | { | 577 | { |
649 | struct pci_dev *dev; | 578 | struct pci_dev *dev; |
650 | struct pci_bus *bus; | 579 | struct pci_bus *bus; |
651 | bool blocked = eeepc_wlan_rfkill_blocked(); | 580 | bool blocked = eeepc_wlan_rfkill_blocked(eeepc); |
581 | bool absent; | ||
582 | u32 l; | ||
652 | 583 | ||
653 | if (ehotk->wlan_rfkill) | 584 | if (eeepc->wlan_rfkill) |
654 | rfkill_set_sw_state(ehotk->wlan_rfkill, blocked); | 585 | rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); |
655 | 586 | ||
656 | mutex_lock(&ehotk->hotplug_lock); | 587 | mutex_lock(&eeepc->hotplug_lock); |
657 | 588 | ||
658 | if (ehotk->hotplug_slot) { | 589 | if (eeepc->hotplug_slot) { |
659 | bus = pci_find_bus(0, 1); | 590 | bus = pci_find_bus(0, 1); |
660 | if (!bus) { | 591 | if (!bus) { |
661 | pr_warning("Unable to find PCI bus 1?\n"); | 592 | pr_warning("Unable to find PCI bus 1?\n"); |
662 | goto out_unlock; | 593 | goto out_unlock; |
663 | } | 594 | } |
664 | 595 | ||
596 | if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) { | ||
597 | pr_err("Unable to read PCI config space?\n"); | ||
598 | goto out_unlock; | ||
599 | } | ||
600 | absent = (l == 0xffffffff); | ||
601 | |||
602 | if (blocked != absent) { | ||
603 | pr_warning("BIOS says wireless lan is %s, " | ||
604 | "but the pci device is %s\n", | ||
605 | blocked ? "blocked" : "unblocked", | ||
606 | absent ? "absent" : "present"); | ||
607 | pr_warning("skipped wireless hotplug as probably " | ||
608 | "inappropriate for this model\n"); | ||
609 | goto out_unlock; | ||
610 | } | ||
611 | |||
665 | if (!blocked) { | 612 | if (!blocked) { |
666 | dev = pci_get_slot(bus, 0); | 613 | dev = pci_get_slot(bus, 0); |
667 | if (dev) { | 614 | if (dev) { |
@@ -685,69 +632,23 @@ static void eeepc_rfkill_hotplug(void) | |||
685 | } | 632 | } |
686 | 633 | ||
687 | out_unlock: | 634 | out_unlock: |
688 | mutex_unlock(&ehotk->hotplug_lock); | 635 | mutex_unlock(&eeepc->hotplug_lock); |
689 | } | 636 | } |
690 | 637 | ||
691 | static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | 638 | static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) |
692 | { | 639 | { |
640 | struct eeepc_laptop *eeepc = data; | ||
641 | |||
693 | if (event != ACPI_NOTIFY_BUS_CHECK) | 642 | if (event != ACPI_NOTIFY_BUS_CHECK) |
694 | return; | 643 | return; |
695 | 644 | ||
696 | eeepc_rfkill_hotplug(); | 645 | eeepc_rfkill_hotplug(eeepc); |
697 | } | ||
698 | |||
699 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event) | ||
700 | { | ||
701 | static struct key_entry *key; | ||
702 | u16 count; | ||
703 | int brn = -ENODEV; | ||
704 | |||
705 | if (!ehotk) | ||
706 | return; | ||
707 | if (event > ACPI_MAX_SYS_NOTIFY) | ||
708 | return; | ||
709 | if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) | ||
710 | brn = notify_brn(); | ||
711 | count = ehotk->event_count[event % 128]++; | ||
712 | acpi_bus_generate_proc_event(ehotk->device, event, count); | ||
713 | acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class, | ||
714 | dev_name(&ehotk->device->dev), event, | ||
715 | count); | ||
716 | if (ehotk->inputdev) { | ||
717 | if (brn != -ENODEV) { | ||
718 | /* brightness-change events need special | ||
719 | * handling for conversion to key events | ||
720 | */ | ||
721 | if (brn < 0) | ||
722 | brn = event; | ||
723 | else | ||
724 | brn += NOTIFY_BRN_MIN; | ||
725 | if (event < brn) | ||
726 | event = NOTIFY_BRN_MIN; /* brightness down */ | ||
727 | else if (event > brn) | ||
728 | event = NOTIFY_BRN_MIN + 2; /* ... up */ | ||
729 | else | ||
730 | event = NOTIFY_BRN_MIN + 1; /* ... unchanged */ | ||
731 | } | ||
732 | key = eepc_get_entry_by_scancode(event); | ||
733 | if (key) { | ||
734 | switch (key->type) { | ||
735 | case KE_KEY: | ||
736 | input_report_key(ehotk->inputdev, key->keycode, | ||
737 | 1); | ||
738 | input_sync(ehotk->inputdev); | ||
739 | input_report_key(ehotk->inputdev, key->keycode, | ||
740 | 0); | ||
741 | input_sync(ehotk->inputdev); | ||
742 | break; | ||
743 | } | ||
744 | } | ||
745 | } | ||
746 | } | 646 | } |
747 | 647 | ||
748 | static int eeepc_register_rfkill_notifier(char *node) | 648 | static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc, |
649 | char *node) | ||
749 | { | 650 | { |
750 | acpi_status status = AE_OK; | 651 | acpi_status status; |
751 | acpi_handle handle; | 652 | acpi_handle handle; |
752 | 653 | ||
753 | status = acpi_get_handle(NULL, node, &handle); | 654 | status = acpi_get_handle(NULL, node, &handle); |
@@ -756,7 +657,7 @@ static int eeepc_register_rfkill_notifier(char *node) | |||
756 | status = acpi_install_notify_handler(handle, | 657 | status = acpi_install_notify_handler(handle, |
757 | ACPI_SYSTEM_NOTIFY, | 658 | ACPI_SYSTEM_NOTIFY, |
758 | eeepc_rfkill_notify, | 659 | eeepc_rfkill_notify, |
759 | NULL); | 660 | eeepc); |
760 | if (ACPI_FAILURE(status)) | 661 | if (ACPI_FAILURE(status)) |
761 | pr_warning("Failed to register notify on %s\n", node); | 662 | pr_warning("Failed to register notify on %s\n", node); |
762 | } else | 663 | } else |
@@ -765,7 +666,8 @@ static int eeepc_register_rfkill_notifier(char *node) | |||
765 | return 0; | 666 | return 0; |
766 | } | 667 | } |
767 | 668 | ||
768 | static void eeepc_unregister_rfkill_notifier(char *node) | 669 | static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc, |
670 | char *node) | ||
769 | { | 671 | { |
770 | acpi_status status = AE_OK; | 672 | acpi_status status = AE_OK; |
771 | acpi_handle handle; | 673 | acpi_handle handle; |
@@ -782,13 +684,33 @@ static void eeepc_unregister_rfkill_notifier(char *node) | |||
782 | } | 684 | } |
783 | } | 685 | } |
784 | 686 | ||
687 | static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, | ||
688 | u8 *value) | ||
689 | { | ||
690 | struct eeepc_laptop *eeepc = hotplug_slot->private; | ||
691 | int val = get_acpi(eeepc, CM_ASL_WLAN); | ||
692 | |||
693 | if (val == 1 || val == 0) | ||
694 | *value = val; | ||
695 | else | ||
696 | return -EINVAL; | ||
697 | |||
698 | return 0; | ||
699 | } | ||
700 | |||
785 | static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) | 701 | static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) |
786 | { | 702 | { |
787 | kfree(hotplug_slot->info); | 703 | kfree(hotplug_slot->info); |
788 | kfree(hotplug_slot); | 704 | kfree(hotplug_slot); |
789 | } | 705 | } |
790 | 706 | ||
791 | static int eeepc_setup_pci_hotplug(void) | 707 | static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { |
708 | .owner = THIS_MODULE, | ||
709 | .get_adapter_status = eeepc_get_adapter_status, | ||
710 | .get_power_status = eeepc_get_adapter_status, | ||
711 | }; | ||
712 | |||
713 | static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) | ||
792 | { | 714 | { |
793 | int ret = -ENOMEM; | 715 | int ret = -ENOMEM; |
794 | struct pci_bus *bus = pci_find_bus(0, 1); | 716 | struct pci_bus *bus = pci_find_bus(0, 1); |
@@ -798,22 +720,22 @@ static int eeepc_setup_pci_hotplug(void) | |||
798 | return -ENODEV; | 720 | return -ENODEV; |
799 | } | 721 | } |
800 | 722 | ||
801 | ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); | 723 | eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); |
802 | if (!ehotk->hotplug_slot) | 724 | if (!eeepc->hotplug_slot) |
803 | goto error_slot; | 725 | goto error_slot; |
804 | 726 | ||
805 | ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), | 727 | eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), |
806 | GFP_KERNEL); | 728 | GFP_KERNEL); |
807 | if (!ehotk->hotplug_slot->info) | 729 | if (!eeepc->hotplug_slot->info) |
808 | goto error_info; | 730 | goto error_info; |
809 | 731 | ||
810 | ehotk->hotplug_slot->private = ehotk; | 732 | eeepc->hotplug_slot->private = eeepc; |
811 | ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug; | 733 | eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug; |
812 | ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops; | 734 | eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops; |
813 | eeepc_get_adapter_status(ehotk->hotplug_slot, | 735 | eeepc_get_adapter_status(eeepc->hotplug_slot, |
814 | &ehotk->hotplug_slot->info->adapter_status); | 736 | &eeepc->hotplug_slot->info->adapter_status); |
815 | 737 | ||
816 | ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi"); | 738 | ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); |
817 | if (ret) { | 739 | if (ret) { |
818 | pr_err("Unable to register hotplug slot - %d\n", ret); | 740 | pr_err("Unable to register hotplug slot - %d\n", ret); |
819 | goto error_register; | 741 | goto error_register; |
@@ -822,17 +744,159 @@ static int eeepc_setup_pci_hotplug(void) | |||
822 | return 0; | 744 | return 0; |
823 | 745 | ||
824 | error_register: | 746 | error_register: |
825 | kfree(ehotk->hotplug_slot->info); | 747 | kfree(eeepc->hotplug_slot->info); |
826 | error_info: | 748 | error_info: |
827 | kfree(ehotk->hotplug_slot); | 749 | kfree(eeepc->hotplug_slot); |
828 | ehotk->hotplug_slot = NULL; | 750 | eeepc->hotplug_slot = NULL; |
829 | error_slot: | 751 | error_slot: |
830 | return ret; | 752 | return ret; |
831 | } | 753 | } |
832 | 754 | ||
755 | /* | ||
756 | * Rfkill devices | ||
757 | */ | ||
758 | static int eeepc_rfkill_set(void *data, bool blocked) | ||
759 | { | ||
760 | acpi_handle handle = data; | ||
761 | |||
762 | return write_acpi_int(handle, NULL, !blocked); | ||
763 | } | ||
764 | |||
765 | static const struct rfkill_ops eeepc_rfkill_ops = { | ||
766 | .set_block = eeepc_rfkill_set, | ||
767 | }; | ||
768 | |||
769 | static int eeepc_new_rfkill(struct eeepc_laptop *eeepc, | ||
770 | struct rfkill **rfkill, | ||
771 | const char *name, | ||
772 | enum rfkill_type type, int cm) | ||
773 | { | ||
774 | acpi_handle handle; | ||
775 | int result; | ||
776 | |||
777 | result = acpi_setter_handle(eeepc, cm, &handle); | ||
778 | if (result < 0) | ||
779 | return result; | ||
780 | |||
781 | *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, | ||
782 | &eeepc_rfkill_ops, handle); | ||
783 | |||
784 | if (!*rfkill) | ||
785 | return -EINVAL; | ||
786 | |||
787 | rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1); | ||
788 | result = rfkill_register(*rfkill); | ||
789 | if (result) { | ||
790 | rfkill_destroy(*rfkill); | ||
791 | *rfkill = NULL; | ||
792 | return result; | ||
793 | } | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc) | ||
798 | { | ||
799 | eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); | ||
800 | eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); | ||
801 | eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); | ||
802 | if (eeepc->wlan_rfkill) { | ||
803 | rfkill_unregister(eeepc->wlan_rfkill); | ||
804 | rfkill_destroy(eeepc->wlan_rfkill); | ||
805 | eeepc->wlan_rfkill = NULL; | ||
806 | } | ||
807 | /* | ||
808 | * Refresh pci hotplug in case the rfkill state was changed after | ||
809 | * eeepc_unregister_rfkill_notifier() | ||
810 | */ | ||
811 | eeepc_rfkill_hotplug(eeepc); | ||
812 | if (eeepc->hotplug_slot) | ||
813 | pci_hp_deregister(eeepc->hotplug_slot); | ||
814 | |||
815 | if (eeepc->bluetooth_rfkill) { | ||
816 | rfkill_unregister(eeepc->bluetooth_rfkill); | ||
817 | rfkill_destroy(eeepc->bluetooth_rfkill); | ||
818 | eeepc->bluetooth_rfkill = NULL; | ||
819 | } | ||
820 | if (eeepc->wwan3g_rfkill) { | ||
821 | rfkill_unregister(eeepc->wwan3g_rfkill); | ||
822 | rfkill_destroy(eeepc->wwan3g_rfkill); | ||
823 | eeepc->wwan3g_rfkill = NULL; | ||
824 | } | ||
825 | if (eeepc->wimax_rfkill) { | ||
826 | rfkill_unregister(eeepc->wimax_rfkill); | ||
827 | rfkill_destroy(eeepc->wimax_rfkill); | ||
828 | eeepc->wimax_rfkill = NULL; | ||
829 | } | ||
830 | } | ||
831 | |||
832 | static int eeepc_rfkill_init(struct eeepc_laptop *eeepc) | ||
833 | { | ||
834 | int result = 0; | ||
835 | |||
836 | mutex_init(&eeepc->hotplug_lock); | ||
837 | |||
838 | result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, | ||
839 | "eeepc-wlan", RFKILL_TYPE_WLAN, | ||
840 | CM_ASL_WLAN); | ||
841 | |||
842 | if (result && result != -ENODEV) | ||
843 | goto exit; | ||
844 | |||
845 | result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill, | ||
846 | "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH, | ||
847 | CM_ASL_BLUETOOTH); | ||
848 | |||
849 | if (result && result != -ENODEV) | ||
850 | goto exit; | ||
851 | |||
852 | result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, | ||
853 | "eeepc-wwan3g", RFKILL_TYPE_WWAN, | ||
854 | CM_ASL_3G); | ||
855 | |||
856 | if (result && result != -ENODEV) | ||
857 | goto exit; | ||
858 | |||
859 | result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill, | ||
860 | "eeepc-wimax", RFKILL_TYPE_WIMAX, | ||
861 | CM_ASL_WIMAX); | ||
862 | |||
863 | if (result && result != -ENODEV) | ||
864 | goto exit; | ||
865 | |||
866 | if (eeepc->hotplug_disabled) | ||
867 | return 0; | ||
868 | |||
869 | result = eeepc_setup_pci_hotplug(eeepc); | ||
870 | /* | ||
871 | * If we get -EBUSY then something else is handling the PCI hotplug - | ||
872 | * don't fail in this case | ||
873 | */ | ||
874 | if (result == -EBUSY) | ||
875 | result = 0; | ||
876 | |||
877 | eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); | ||
878 | eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); | ||
879 | eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); | ||
880 | /* | ||
881 | * Refresh pci hotplug in case the rfkill state was changed during | ||
882 | * setup. | ||
883 | */ | ||
884 | eeepc_rfkill_hotplug(eeepc); | ||
885 | |||
886 | exit: | ||
887 | if (result && result != -ENODEV) | ||
888 | eeepc_rfkill_exit(eeepc); | ||
889 | return result; | ||
890 | } | ||
891 | |||
892 | /* | ||
893 | * Platform driver - hibernate/resume callbacks | ||
894 | */ | ||
833 | static int eeepc_hotk_thaw(struct device *device) | 895 | static int eeepc_hotk_thaw(struct device *device) |
834 | { | 896 | { |
835 | if (ehotk->wlan_rfkill) { | 897 | struct eeepc_laptop *eeepc = dev_get_drvdata(device); |
898 | |||
899 | if (eeepc->wlan_rfkill) { | ||
836 | bool wlan; | 900 | bool wlan; |
837 | 901 | ||
838 | /* | 902 | /* |
@@ -840,8 +904,8 @@ static int eeepc_hotk_thaw(struct device *device) | |||
840 | * during suspend. Normally it restores it on resume, but | 904 | * during suspend. Normally it restores it on resume, but |
841 | * we should kick it ourselves in case hibernation is aborted. | 905 | * we should kick it ourselves in case hibernation is aborted. |
842 | */ | 906 | */ |
843 | wlan = get_acpi(CM_ASL_WLAN); | 907 | wlan = get_acpi(eeepc, CM_ASL_WLAN); |
844 | set_acpi(CM_ASL_WLAN, wlan); | 908 | set_acpi(eeepc, CM_ASL_WLAN, wlan); |
845 | } | 909 | } |
846 | 910 | ||
847 | return 0; | 911 | return 0; |
@@ -849,70 +913,96 @@ static int eeepc_hotk_thaw(struct device *device) | |||
849 | 913 | ||
850 | static int eeepc_hotk_restore(struct device *device) | 914 | static int eeepc_hotk_restore(struct device *device) |
851 | { | 915 | { |
916 | struct eeepc_laptop *eeepc = dev_get_drvdata(device); | ||
917 | |||
852 | /* Refresh both wlan rfkill state and pci hotplug */ | 918 | /* Refresh both wlan rfkill state and pci hotplug */ |
853 | if (ehotk->wlan_rfkill) | 919 | if (eeepc->wlan_rfkill) |
854 | eeepc_rfkill_hotplug(); | 920 | eeepc_rfkill_hotplug(eeepc); |
855 | 921 | ||
856 | if (ehotk->bluetooth_rfkill) | 922 | if (eeepc->bluetooth_rfkill) |
857 | rfkill_set_sw_state(ehotk->bluetooth_rfkill, | 923 | rfkill_set_sw_state(eeepc->bluetooth_rfkill, |
858 | get_acpi(CM_ASL_BLUETOOTH) != 1); | 924 | get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1); |
859 | if (ehotk->wwan3g_rfkill) | 925 | if (eeepc->wwan3g_rfkill) |
860 | rfkill_set_sw_state(ehotk->wwan3g_rfkill, | 926 | rfkill_set_sw_state(eeepc->wwan3g_rfkill, |
861 | get_acpi(CM_ASL_3G) != 1); | 927 | get_acpi(eeepc, CM_ASL_3G) != 1); |
862 | if (ehotk->wimax_rfkill) | 928 | if (eeepc->wimax_rfkill) |
863 | rfkill_set_sw_state(ehotk->wimax_rfkill, | 929 | rfkill_set_sw_state(eeepc->wimax_rfkill, |
864 | get_acpi(CM_ASL_WIMAX) != 1); | 930 | get_acpi(eeepc, CM_ASL_WIMAX) != 1); |
865 | 931 | ||
866 | return 0; | 932 | return 0; |
867 | } | 933 | } |
868 | 934 | ||
935 | static const struct dev_pm_ops eeepc_pm_ops = { | ||
936 | .thaw = eeepc_hotk_thaw, | ||
937 | .restore = eeepc_hotk_restore, | ||
938 | }; | ||
939 | |||
940 | static struct platform_driver platform_driver = { | ||
941 | .driver = { | ||
942 | .name = EEEPC_LAPTOP_FILE, | ||
943 | .owner = THIS_MODULE, | ||
944 | .pm = &eeepc_pm_ops, | ||
945 | } | ||
946 | }; | ||
947 | |||
869 | /* | 948 | /* |
870 | * Hwmon | 949 | * Hwmon device |
871 | */ | 950 | */ |
951 | |||
952 | #define EEEPC_EC_SC00 0x61 | ||
953 | #define EEEPC_EC_FAN_PWM (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */ | ||
954 | #define EEEPC_EC_FAN_HRPM (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */ | ||
955 | #define EEEPC_EC_FAN_LRPM (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */ | ||
956 | |||
957 | #define EEEPC_EC_SFB0 0xD0 | ||
958 | #define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */ | ||
959 | |||
872 | static int eeepc_get_fan_pwm(void) | 960 | static int eeepc_get_fan_pwm(void) |
873 | { | 961 | { |
874 | int value = 0; | 962 | u8 value = 0; |
875 | 963 | ||
876 | read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value); | 964 | ec_read(EEEPC_EC_FAN_PWM, &value); |
877 | value = value * 255 / 100; | 965 | return value * 255 / 100; |
878 | return (value); | ||
879 | } | 966 | } |
880 | 967 | ||
881 | static void eeepc_set_fan_pwm(int value) | 968 | static void eeepc_set_fan_pwm(int value) |
882 | { | 969 | { |
883 | value = SENSORS_LIMIT(value, 0, 255); | 970 | value = SENSORS_LIMIT(value, 0, 255); |
884 | value = value * 100 / 255; | 971 | value = value * 100 / 255; |
885 | ec_write(EEEPC_EC_SC02, value); | 972 | ec_write(EEEPC_EC_FAN_PWM, value); |
886 | } | 973 | } |
887 | 974 | ||
888 | static int eeepc_get_fan_rpm(void) | 975 | static int eeepc_get_fan_rpm(void) |
889 | { | 976 | { |
890 | int high = 0; | 977 | u8 high = 0; |
891 | int low = 0; | 978 | u8 low = 0; |
892 | 979 | ||
893 | read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high); | 980 | ec_read(EEEPC_EC_FAN_HRPM, &high); |
894 | read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low); | 981 | ec_read(EEEPC_EC_FAN_LRPM, &low); |
895 | return (high << 8 | low); | 982 | return high << 8 | low; |
896 | } | 983 | } |
897 | 984 | ||
898 | static int eeepc_get_fan_ctrl(void) | 985 | static int eeepc_get_fan_ctrl(void) |
899 | { | 986 | { |
900 | int value = 0; | 987 | u8 value = 0; |
901 | 988 | ||
902 | read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); | 989 | ec_read(EEEPC_EC_FAN_CTRL, &value); |
903 | return ((value & 0x02 ? 1 : 0)); | 990 | if (value & 0x02) |
991 | return 1; /* manual */ | ||
992 | else | ||
993 | return 2; /* automatic */ | ||
904 | } | 994 | } |
905 | 995 | ||
906 | static void eeepc_set_fan_ctrl(int manual) | 996 | static void eeepc_set_fan_ctrl(int manual) |
907 | { | 997 | { |
908 | int value = 0; | 998 | u8 value = 0; |
909 | 999 | ||
910 | read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); | 1000 | ec_read(EEEPC_EC_FAN_CTRL, &value); |
911 | if (manual) | 1001 | if (manual == 1) |
912 | value |= 0x02; | 1002 | value |= 0x02; |
913 | else | 1003 | else |
914 | value &= ~0x02; | 1004 | value &= ~0x02; |
915 | ec_write(EEEPC_EC_SFB3, value); | 1005 | ec_write(EEEPC_EC_FAN_CTRL, value); |
916 | } | 1006 | } |
917 | 1007 | ||
918 | static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count) | 1008 | static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count) |
@@ -970,348 +1060,464 @@ static struct attribute_group hwmon_attribute_group = { | |||
970 | .attrs = hwmon_attributes | 1060 | .attrs = hwmon_attributes |
971 | }; | 1061 | }; |
972 | 1062 | ||
973 | /* | 1063 | static void eeepc_hwmon_exit(struct eeepc_laptop *eeepc) |
974 | * exit/init | ||
975 | */ | ||
976 | static void eeepc_backlight_exit(void) | ||
977 | { | 1064 | { |
978 | if (eeepc_backlight_device) | 1065 | struct device *hwmon; |
979 | backlight_device_unregister(eeepc_backlight_device); | 1066 | |
980 | eeepc_backlight_device = NULL; | 1067 | hwmon = eeepc->hwmon_device; |
1068 | if (!hwmon) | ||
1069 | return; | ||
1070 | sysfs_remove_group(&hwmon->kobj, | ||
1071 | &hwmon_attribute_group); | ||
1072 | hwmon_device_unregister(hwmon); | ||
1073 | eeepc->hwmon_device = NULL; | ||
981 | } | 1074 | } |
982 | 1075 | ||
983 | static void eeepc_rfkill_exit(void) | 1076 | static int eeepc_hwmon_init(struct eeepc_laptop *eeepc) |
984 | { | 1077 | { |
985 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5"); | 1078 | struct device *hwmon; |
986 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); | 1079 | int result; |
987 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); | 1080 | |
988 | if (ehotk->wlan_rfkill) { | 1081 | hwmon = hwmon_device_register(&eeepc->platform_device->dev); |
989 | rfkill_unregister(ehotk->wlan_rfkill); | 1082 | if (IS_ERR(hwmon)) { |
990 | rfkill_destroy(ehotk->wlan_rfkill); | 1083 | pr_err("Could not register eeepc hwmon device\n"); |
991 | ehotk->wlan_rfkill = NULL; | 1084 | eeepc->hwmon_device = NULL; |
992 | } | 1085 | return PTR_ERR(hwmon); |
993 | /* | ||
994 | * Refresh pci hotplug in case the rfkill state was changed after | ||
995 | * eeepc_unregister_rfkill_notifier() | ||
996 | */ | ||
997 | eeepc_rfkill_hotplug(); | ||
998 | if (ehotk->hotplug_slot) | ||
999 | pci_hp_deregister(ehotk->hotplug_slot); | ||
1000 | |||
1001 | if (ehotk->bluetooth_rfkill) { | ||
1002 | rfkill_unregister(ehotk->bluetooth_rfkill); | ||
1003 | rfkill_destroy(ehotk->bluetooth_rfkill); | ||
1004 | ehotk->bluetooth_rfkill = NULL; | ||
1005 | } | ||
1006 | if (ehotk->wwan3g_rfkill) { | ||
1007 | rfkill_unregister(ehotk->wwan3g_rfkill); | ||
1008 | rfkill_destroy(ehotk->wwan3g_rfkill); | ||
1009 | ehotk->wwan3g_rfkill = NULL; | ||
1010 | } | ||
1011 | if (ehotk->wimax_rfkill) { | ||
1012 | rfkill_unregister(ehotk->wimax_rfkill); | ||
1013 | rfkill_destroy(ehotk->wimax_rfkill); | ||
1014 | ehotk->wimax_rfkill = NULL; | ||
1015 | } | 1086 | } |
1087 | eeepc->hwmon_device = hwmon; | ||
1088 | result = sysfs_create_group(&hwmon->kobj, | ||
1089 | &hwmon_attribute_group); | ||
1090 | if (result) | ||
1091 | eeepc_hwmon_exit(eeepc); | ||
1092 | return result; | ||
1016 | } | 1093 | } |
1017 | 1094 | ||
1018 | static void eeepc_input_exit(void) | 1095 | /* |
1096 | * Backlight device | ||
1097 | */ | ||
1098 | static int read_brightness(struct backlight_device *bd) | ||
1019 | { | 1099 | { |
1020 | if (ehotk->inputdev) | 1100 | struct eeepc_laptop *eeepc = bl_get_data(bd); |
1021 | input_unregister_device(ehotk->inputdev); | 1101 | |
1102 | return get_acpi(eeepc, CM_ASL_PANELBRIGHT); | ||
1022 | } | 1103 | } |
1023 | 1104 | ||
1024 | static void eeepc_hwmon_exit(void) | 1105 | static int set_brightness(struct backlight_device *bd, int value) |
1025 | { | 1106 | { |
1026 | struct device *hwmon; | 1107 | struct eeepc_laptop *eeepc = bl_get_data(bd); |
1027 | 1108 | ||
1028 | hwmon = eeepc_hwmon_device; | 1109 | return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value); |
1029 | if (!hwmon) | ||
1030 | return ; | ||
1031 | sysfs_remove_group(&hwmon->kobj, | ||
1032 | &hwmon_attribute_group); | ||
1033 | hwmon_device_unregister(hwmon); | ||
1034 | eeepc_hwmon_device = NULL; | ||
1035 | } | 1110 | } |
1036 | 1111 | ||
1037 | static int eeepc_new_rfkill(struct rfkill **rfkill, | 1112 | static int update_bl_status(struct backlight_device *bd) |
1038 | const char *name, struct device *dev, | ||
1039 | enum rfkill_type type, int cm) | ||
1040 | { | 1113 | { |
1041 | int result; | 1114 | return set_brightness(bd, bd->props.brightness); |
1115 | } | ||
1042 | 1116 | ||
1043 | result = get_acpi(cm); | 1117 | static struct backlight_ops eeepcbl_ops = { |
1044 | if (result < 0) | 1118 | .get_brightness = read_brightness, |
1045 | return result; | 1119 | .update_status = update_bl_status, |
1120 | }; | ||
1046 | 1121 | ||
1047 | *rfkill = rfkill_alloc(name, dev, type, | 1122 | static int eeepc_backlight_notify(struct eeepc_laptop *eeepc) |
1048 | &eeepc_rfkill_ops, (void *)(unsigned long)cm); | 1123 | { |
1124 | struct backlight_device *bd = eeepc->backlight_device; | ||
1125 | int old = bd->props.brightness; | ||
1049 | 1126 | ||
1050 | if (!*rfkill) | 1127 | backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); |
1051 | return -EINVAL; | ||
1052 | 1128 | ||
1053 | rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1); | 1129 | return old; |
1054 | result = rfkill_register(*rfkill); | 1130 | } |
1055 | if (result) { | 1131 | |
1056 | rfkill_destroy(*rfkill); | 1132 | static int eeepc_backlight_init(struct eeepc_laptop *eeepc) |
1057 | *rfkill = NULL; | 1133 | { |
1058 | return result; | 1134 | struct backlight_properties props; |
1135 | struct backlight_device *bd; | ||
1136 | |||
1137 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1138 | props.max_brightness = 15; | ||
1139 | bd = backlight_device_register(EEEPC_LAPTOP_FILE, | ||
1140 | &eeepc->platform_device->dev, eeepc, | ||
1141 | &eeepcbl_ops, &props); | ||
1142 | if (IS_ERR(bd)) { | ||
1143 | pr_err("Could not register eeepc backlight device\n"); | ||
1144 | eeepc->backlight_device = NULL; | ||
1145 | return PTR_ERR(bd); | ||
1059 | } | 1146 | } |
1147 | eeepc->backlight_device = bd; | ||
1148 | bd->props.brightness = read_brightness(bd); | ||
1149 | bd->props.power = FB_BLANK_UNBLANK; | ||
1150 | backlight_update_status(bd); | ||
1060 | return 0; | 1151 | return 0; |
1061 | } | 1152 | } |
1062 | 1153 | ||
1154 | static void eeepc_backlight_exit(struct eeepc_laptop *eeepc) | ||
1155 | { | ||
1156 | if (eeepc->backlight_device) | ||
1157 | backlight_device_unregister(eeepc->backlight_device); | ||
1158 | eeepc->backlight_device = NULL; | ||
1159 | } | ||
1063 | 1160 | ||
1064 | static int eeepc_rfkill_init(struct device *dev) | 1161 | |
1162 | /* | ||
1163 | * Input device (i.e. hotkeys) | ||
1164 | */ | ||
1165 | static int eeepc_input_init(struct eeepc_laptop *eeepc) | ||
1065 | { | 1166 | { |
1066 | int result = 0; | 1167 | struct input_dev *input; |
1168 | int error; | ||
1067 | 1169 | ||
1068 | mutex_init(&ehotk->hotplug_lock); | 1170 | input = input_allocate_device(); |
1171 | if (!input) { | ||
1172 | pr_info("Unable to allocate input device\n"); | ||
1173 | return -ENOMEM; | ||
1174 | } | ||
1069 | 1175 | ||
1070 | result = eeepc_new_rfkill(&ehotk->wlan_rfkill, | 1176 | input->name = "Asus EeePC extra buttons"; |
1071 | "eeepc-wlan", dev, | 1177 | input->phys = EEEPC_LAPTOP_FILE "/input0"; |
1072 | RFKILL_TYPE_WLAN, CM_ASL_WLAN); | 1178 | input->id.bustype = BUS_HOST; |
1179 | input->dev.parent = &eeepc->platform_device->dev; | ||
1073 | 1180 | ||
1074 | if (result && result != -ENODEV) | 1181 | error = sparse_keymap_setup(input, eeepc_keymap, NULL); |
1075 | goto exit; | 1182 | if (error) { |
1183 | pr_err("Unable to setup input device keymap\n"); | ||
1184 | goto err_free_dev; | ||
1185 | } | ||
1076 | 1186 | ||
1077 | result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill, | 1187 | error = input_register_device(input); |
1078 | "eeepc-bluetooth", dev, | 1188 | if (error) { |
1079 | RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH); | 1189 | pr_err("Unable to register input device\n"); |
1190 | goto err_free_keymap; | ||
1191 | } | ||
1080 | 1192 | ||
1081 | if (result && result != -ENODEV) | 1193 | eeepc->inputdev = input; |
1082 | goto exit; | 1194 | return 0; |
1083 | 1195 | ||
1084 | result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill, | 1196 | err_free_keymap: |
1085 | "eeepc-wwan3g", dev, | 1197 | sparse_keymap_free(input); |
1086 | RFKILL_TYPE_WWAN, CM_ASL_3G); | 1198 | err_free_dev: |
1199 | input_free_device(input); | ||
1200 | return error; | ||
1201 | } | ||
1087 | 1202 | ||
1088 | if (result && result != -ENODEV) | 1203 | static void eeepc_input_exit(struct eeepc_laptop *eeepc) |
1089 | goto exit; | 1204 | { |
1205 | if (eeepc->inputdev) { | ||
1206 | sparse_keymap_free(eeepc->inputdev); | ||
1207 | input_unregister_device(eeepc->inputdev); | ||
1208 | } | ||
1209 | } | ||
1090 | 1210 | ||
1091 | result = eeepc_new_rfkill(&ehotk->wimax_rfkill, | 1211 | /* |
1092 | "eeepc-wimax", dev, | 1212 | * ACPI driver |
1093 | RFKILL_TYPE_WIMAX, CM_ASL_WIMAX); | 1213 | */ |
1214 | static void eeepc_acpi_notify(struct acpi_device *device, u32 event) | ||
1215 | { | ||
1216 | struct eeepc_laptop *eeepc = acpi_driver_data(device); | ||
1217 | u16 count; | ||
1094 | 1218 | ||
1095 | if (result && result != -ENODEV) | 1219 | if (event > ACPI_MAX_SYS_NOTIFY) |
1096 | goto exit; | 1220 | return; |
1221 | count = eeepc->event_count[event % 128]++; | ||
1222 | acpi_bus_generate_proc_event(device, event, count); | ||
1223 | acpi_bus_generate_netlink_event(device->pnp.device_class, | ||
1224 | dev_name(&device->dev), event, | ||
1225 | count); | ||
1226 | |||
1227 | /* Brightness events are special */ | ||
1228 | if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) { | ||
1229 | |||
1230 | /* Ignore them completely if the acpi video driver is used */ | ||
1231 | if (eeepc->backlight_device != NULL) { | ||
1232 | int old_brightness, new_brightness; | ||
1233 | |||
1234 | /* Update the backlight device. */ | ||
1235 | old_brightness = eeepc_backlight_notify(eeepc); | ||
1236 | |||
1237 | /* Convert event to keypress (obsolescent hack) */ | ||
1238 | new_brightness = event - NOTIFY_BRN_MIN; | ||
1239 | |||
1240 | if (new_brightness < old_brightness) { | ||
1241 | event = NOTIFY_BRN_MIN; /* brightness down */ | ||
1242 | } else if (new_brightness > old_brightness) { | ||
1243 | event = NOTIFY_BRN_MAX; /* brightness up */ | ||
1244 | } else { | ||
1245 | /* | ||
1246 | * no change in brightness - already at min/max, | ||
1247 | * event will be desired value (or else ignored) | ||
1248 | */ | ||
1249 | } | ||
1250 | sparse_keymap_report_event(eeepc->inputdev, event, | ||
1251 | 1, true); | ||
1252 | } | ||
1253 | } else { | ||
1254 | /* Everything else is a bona-fide keypress event */ | ||
1255 | sparse_keymap_report_event(eeepc->inputdev, event, 1, true); | ||
1256 | } | ||
1257 | } | ||
1258 | |||
1259 | static void eeepc_dmi_check(struct eeepc_laptop *eeepc) | ||
1260 | { | ||
1261 | const char *model; | ||
1262 | |||
1263 | model = dmi_get_system_info(DMI_PRODUCT_NAME); | ||
1264 | if (!model) | ||
1265 | return; | ||
1097 | 1266 | ||
1098 | result = eeepc_setup_pci_hotplug(); | ||
1099 | /* | 1267 | /* |
1100 | * If we get -EBUSY then something else is handling the PCI hotplug - | 1268 | * Blacklist for setting cpufv (cpu speed). |
1101 | * don't fail in this case | 1269 | * |
1270 | * EeePC 4G ("701") implements CFVS, but it is not supported | ||
1271 | * by the pre-installed OS, and the original option to change it | ||
1272 | * in the BIOS setup screen was removed in later versions. | ||
1273 | * | ||
1274 | * Judging by the lack of "Super Hybrid Engine" on Asus product pages, | ||
1275 | * this applies to all "701" models (4G/4G Surf/2G Surf). | ||
1276 | * | ||
1277 | * So Asus made a deliberate decision not to support it on this model. | ||
1278 | * We have several reports that using it can cause the system to hang | ||
1279 | * | ||
1280 | * The hang has also been reported on a "702" (Model name "8G"?). | ||
1281 | * | ||
1282 | * We avoid dmi_check_system() / dmi_match(), because they use | ||
1283 | * substring matching. We don't want to affect the "701SD" | ||
1284 | * and "701SDX" models, because they do support S.H.E. | ||
1102 | */ | 1285 | */ |
1103 | if (result == -EBUSY) | 1286 | if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) { |
1104 | result = 0; | 1287 | eeepc->cpufv_disabled = true; |
1288 | pr_info("model %s does not officially support setting cpu " | ||
1289 | "speed\n", model); | ||
1290 | pr_info("cpufv disabled to avoid instability\n"); | ||
1291 | } | ||
1105 | 1292 | ||
1106 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5"); | ||
1107 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); | ||
1108 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); | ||
1109 | /* | 1293 | /* |
1110 | * Refresh pci hotplug in case the rfkill state was changed during | 1294 | * Blacklist for wlan hotplug |
1111 | * setup. | 1295 | * |
1296 | * Eeepc 1005HA doesn't work like others models and don't need the | ||
1297 | * hotplug code. In fact, current hotplug code seems to unplug another | ||
1298 | * device... | ||
1112 | */ | 1299 | */ |
1113 | eeepc_rfkill_hotplug(); | 1300 | if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0 || |
1114 | 1301 | strcmp(model, "1005PE") == 0) { | |
1115 | exit: | 1302 | eeepc->hotplug_disabled = true; |
1116 | if (result && result != -ENODEV) | 1303 | pr_info("wlan hotplug disabled\n"); |
1117 | eeepc_rfkill_exit(); | 1304 | } |
1118 | return result; | ||
1119 | } | 1305 | } |
1120 | 1306 | ||
1121 | static int eeepc_backlight_init(struct device *dev) | 1307 | static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name) |
1122 | { | 1308 | { |
1123 | struct backlight_device *bd; | 1309 | int dummy; |
1124 | 1310 | ||
1125 | bd = backlight_device_register(EEEPC_HOTK_FILE, dev, | 1311 | /* Some BIOSes do not report cm although it is avaliable. |
1126 | NULL, &eeepcbl_ops); | 1312 | Check if cm_getv[cm] works and, if yes, assume cm should be set. */ |
1127 | if (IS_ERR(bd)) { | 1313 | if (!(eeepc->cm_supported & (1 << cm)) |
1128 | pr_err("Could not register eeepc backlight device\n"); | 1314 | && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) { |
1129 | eeepc_backlight_device = NULL; | 1315 | pr_info("%s (%x) not reported by BIOS," |
1130 | return PTR_ERR(bd); | 1316 | " enabling anyway\n", name, 1 << cm); |
1317 | eeepc->cm_supported |= 1 << cm; | ||
1131 | } | 1318 | } |
1132 | eeepc_backlight_device = bd; | ||
1133 | bd->props.max_brightness = 15; | ||
1134 | bd->props.brightness = read_brightness(NULL); | ||
1135 | bd->props.power = FB_BLANK_UNBLANK; | ||
1136 | backlight_update_status(bd); | ||
1137 | return 0; | ||
1138 | } | 1319 | } |
1139 | 1320 | ||
1140 | static int eeepc_hwmon_init(struct device *dev) | 1321 | static void cmsg_quirks(struct eeepc_laptop *eeepc) |
1141 | { | 1322 | { |
1142 | struct device *hwmon; | 1323 | cmsg_quirk(eeepc, CM_ASL_LID, "LID"); |
1143 | int result; | 1324 | cmsg_quirk(eeepc, CM_ASL_TYPE, "TYPE"); |
1144 | 1325 | cmsg_quirk(eeepc, CM_ASL_PANELPOWER, "PANELPOWER"); | |
1145 | hwmon = hwmon_device_register(dev); | 1326 | cmsg_quirk(eeepc, CM_ASL_TPD, "TPD"); |
1146 | if (IS_ERR(hwmon)) { | ||
1147 | pr_err("Could not register eeepc hwmon device\n"); | ||
1148 | eeepc_hwmon_device = NULL; | ||
1149 | return PTR_ERR(hwmon); | ||
1150 | } | ||
1151 | eeepc_hwmon_device = hwmon; | ||
1152 | result = sysfs_create_group(&hwmon->kobj, | ||
1153 | &hwmon_attribute_group); | ||
1154 | if (result) | ||
1155 | eeepc_hwmon_exit(); | ||
1156 | return result; | ||
1157 | } | 1327 | } |
1158 | 1328 | ||
1159 | static int eeepc_input_init(struct device *dev) | 1329 | static int eeepc_acpi_init(struct eeepc_laptop *eeepc, |
1330 | struct acpi_device *device) | ||
1160 | { | 1331 | { |
1161 | const struct key_entry *key; | 1332 | unsigned int init_flags; |
1162 | int result; | 1333 | int result; |
1163 | 1334 | ||
1164 | ehotk->inputdev = input_allocate_device(); | 1335 | result = acpi_bus_get_status(device); |
1165 | if (!ehotk->inputdev) { | 1336 | if (result) |
1166 | pr_info("Unable to allocate input device\n"); | 1337 | return result; |
1167 | return -ENOMEM; | 1338 | if (!device->status.present) { |
1339 | pr_err("Hotkey device not present, aborting\n"); | ||
1340 | return -ENODEV; | ||
1168 | } | 1341 | } |
1169 | ehotk->inputdev->name = "Asus EeePC extra buttons"; | 1342 | |
1170 | ehotk->inputdev->dev.parent = dev; | 1343 | init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; |
1171 | ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0"; | 1344 | pr_notice("Hotkey init flags 0x%x\n", init_flags); |
1172 | ehotk->inputdev->id.bustype = BUS_HOST; | 1345 | |
1173 | ehotk->inputdev->getkeycode = eeepc_getkeycode; | 1346 | if (write_acpi_int(eeepc->handle, "INIT", init_flags)) { |
1174 | ehotk->inputdev->setkeycode = eeepc_setkeycode; | 1347 | pr_err("Hotkey initialization failed\n"); |
1175 | 1348 | return -ENODEV; | |
1176 | for (key = eeepc_keymap; key->type != KE_END; key++) { | ||
1177 | switch (key->type) { | ||
1178 | case KE_KEY: | ||
1179 | set_bit(EV_KEY, ehotk->inputdev->evbit); | ||
1180 | set_bit(key->keycode, ehotk->inputdev->keybit); | ||
1181 | break; | ||
1182 | } | ||
1183 | } | 1349 | } |
1184 | result = input_register_device(ehotk->inputdev); | 1350 | |
1185 | if (result) { | 1351 | /* get control methods supported */ |
1186 | pr_info("Unable to register input device\n"); | 1352 | if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) { |
1187 | input_free_device(ehotk->inputdev); | 1353 | pr_err("Get control methods supported failed\n"); |
1188 | return result; | 1354 | return -ENODEV; |
1189 | } | 1355 | } |
1356 | cmsg_quirks(eeepc); | ||
1357 | pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported); | ||
1358 | |||
1190 | return 0; | 1359 | return 0; |
1191 | } | 1360 | } |
1192 | 1361 | ||
1193 | static int __devinit eeepc_hotk_add(struct acpi_device *device) | 1362 | static void __devinit eeepc_enable_camera(struct eeepc_laptop *eeepc) |
1194 | { | 1363 | { |
1195 | struct device *dev; | 1364 | /* |
1365 | * If the following call to set_acpi() fails, it's because there's no | ||
1366 | * camera so we can ignore the error. | ||
1367 | */ | ||
1368 | if (get_acpi(eeepc, CM_ASL_CAMERA) == 0) | ||
1369 | set_acpi(eeepc, CM_ASL_CAMERA, 1); | ||
1370 | } | ||
1371 | |||
1372 | static bool eeepc_device_present; | ||
1373 | |||
1374 | static int __devinit eeepc_acpi_add(struct acpi_device *device) | ||
1375 | { | ||
1376 | struct eeepc_laptop *eeepc; | ||
1196 | int result; | 1377 | int result; |
1197 | 1378 | ||
1198 | if (!device) | 1379 | pr_notice(EEEPC_LAPTOP_NAME "\n"); |
1199 | return -EINVAL; | 1380 | eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL); |
1200 | pr_notice(EEEPC_HOTK_NAME "\n"); | 1381 | if (!eeepc) |
1201 | ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); | ||
1202 | if (!ehotk) | ||
1203 | return -ENOMEM; | 1382 | return -ENOMEM; |
1204 | ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; | 1383 | eeepc->handle = device->handle; |
1205 | ehotk->handle = device->handle; | 1384 | strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); |
1206 | strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME); | 1385 | strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); |
1207 | strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS); | 1386 | device->driver_data = eeepc; |
1208 | device->driver_data = ehotk; | ||
1209 | ehotk->device = device; | ||
1210 | |||
1211 | result = eeepc_hotk_check(); | ||
1212 | if (result) | ||
1213 | goto fail_platform_driver; | ||
1214 | eeepc_enable_camera(); | ||
1215 | 1387 | ||
1216 | /* Register platform stuff */ | 1388 | eeepc->hotplug_disabled = hotplug_disabled; |
1217 | result = platform_driver_register(&platform_driver); | 1389 | |
1218 | if (result) | 1390 | eeepc_dmi_check(eeepc); |
1219 | goto fail_platform_driver; | 1391 | |
1220 | platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1); | 1392 | result = eeepc_acpi_init(eeepc, device); |
1221 | if (!platform_device) { | ||
1222 | result = -ENOMEM; | ||
1223 | goto fail_platform_device1; | ||
1224 | } | ||
1225 | result = platform_device_add(platform_device); | ||
1226 | if (result) | ||
1227 | goto fail_platform_device2; | ||
1228 | result = sysfs_create_group(&platform_device->dev.kobj, | ||
1229 | &platform_attribute_group); | ||
1230 | if (result) | 1393 | if (result) |
1231 | goto fail_sysfs; | 1394 | goto fail_platform; |
1395 | eeepc_enable_camera(eeepc); | ||
1232 | 1396 | ||
1233 | dev = &platform_device->dev; | 1397 | /* |
1398 | * Register the platform device first. It is used as a parent for the | ||
1399 | * sub-devices below. | ||
1400 | * | ||
1401 | * Note that if there are multiple instances of this ACPI device it | ||
1402 | * will bail out, because the platform device is registered with a | ||
1403 | * fixed name. Of course it doesn't make sense to have more than one, | ||
1404 | * and machine-specific scripts find the fixed name convenient. But | ||
1405 | * It's also good for us to exclude multiple instances because both | ||
1406 | * our hwmon and our wlan rfkill subdevice use global ACPI objects | ||
1407 | * (the EC and the wlan PCI slot respectively). | ||
1408 | */ | ||
1409 | result = eeepc_platform_init(eeepc); | ||
1410 | if (result) | ||
1411 | goto fail_platform; | ||
1234 | 1412 | ||
1235 | if (!acpi_video_backlight_support()) { | 1413 | if (!acpi_video_backlight_support()) { |
1236 | result = eeepc_backlight_init(dev); | 1414 | result = eeepc_backlight_init(eeepc); |
1237 | if (result) | 1415 | if (result) |
1238 | goto fail_backlight; | 1416 | goto fail_backlight; |
1239 | } else | 1417 | } else |
1240 | pr_info("Backlight controlled by ACPI video " | 1418 | pr_info("Backlight controlled by ACPI video driver\n"); |
1241 | "driver\n"); | ||
1242 | 1419 | ||
1243 | result = eeepc_input_init(dev); | 1420 | result = eeepc_input_init(eeepc); |
1244 | if (result) | 1421 | if (result) |
1245 | goto fail_input; | 1422 | goto fail_input; |
1246 | 1423 | ||
1247 | result = eeepc_hwmon_init(dev); | 1424 | result = eeepc_hwmon_init(eeepc); |
1248 | if (result) | 1425 | if (result) |
1249 | goto fail_hwmon; | 1426 | goto fail_hwmon; |
1250 | 1427 | ||
1251 | result = eeepc_rfkill_init(dev); | 1428 | result = eeepc_led_init(eeepc); |
1429 | if (result) | ||
1430 | goto fail_led; | ||
1431 | |||
1432 | result = eeepc_rfkill_init(eeepc); | ||
1252 | if (result) | 1433 | if (result) |
1253 | goto fail_rfkill; | 1434 | goto fail_rfkill; |
1254 | 1435 | ||
1436 | eeepc_device_present = true; | ||
1255 | return 0; | 1437 | return 0; |
1256 | 1438 | ||
1257 | fail_rfkill: | 1439 | fail_rfkill: |
1258 | eeepc_hwmon_exit(); | 1440 | eeepc_led_exit(eeepc); |
1441 | fail_led: | ||
1442 | eeepc_hwmon_exit(eeepc); | ||
1259 | fail_hwmon: | 1443 | fail_hwmon: |
1260 | eeepc_input_exit(); | 1444 | eeepc_input_exit(eeepc); |
1261 | fail_input: | 1445 | fail_input: |
1262 | eeepc_backlight_exit(); | 1446 | eeepc_backlight_exit(eeepc); |
1263 | fail_backlight: | 1447 | fail_backlight: |
1264 | sysfs_remove_group(&platform_device->dev.kobj, | 1448 | eeepc_platform_exit(eeepc); |
1265 | &platform_attribute_group); | 1449 | fail_platform: |
1266 | fail_sysfs: | 1450 | kfree(eeepc); |
1267 | platform_device_del(platform_device); | ||
1268 | fail_platform_device2: | ||
1269 | platform_device_put(platform_device); | ||
1270 | fail_platform_device1: | ||
1271 | platform_driver_unregister(&platform_driver); | ||
1272 | fail_platform_driver: | ||
1273 | kfree(ehotk); | ||
1274 | 1451 | ||
1275 | return result; | 1452 | return result; |
1276 | } | 1453 | } |
1277 | 1454 | ||
1278 | static int eeepc_hotk_remove(struct acpi_device *device, int type) | 1455 | static int eeepc_acpi_remove(struct acpi_device *device, int type) |
1279 | { | 1456 | { |
1280 | if (!device || !acpi_driver_data(device)) | 1457 | struct eeepc_laptop *eeepc = acpi_driver_data(device); |
1281 | return -EINVAL; | ||
1282 | 1458 | ||
1283 | eeepc_backlight_exit(); | 1459 | eeepc_backlight_exit(eeepc); |
1284 | eeepc_rfkill_exit(); | 1460 | eeepc_rfkill_exit(eeepc); |
1285 | eeepc_input_exit(); | 1461 | eeepc_input_exit(eeepc); |
1286 | eeepc_hwmon_exit(); | 1462 | eeepc_hwmon_exit(eeepc); |
1287 | sysfs_remove_group(&platform_device->dev.kobj, | 1463 | eeepc_led_exit(eeepc); |
1288 | &platform_attribute_group); | 1464 | eeepc_platform_exit(eeepc); |
1289 | platform_device_unregister(platform_device); | ||
1290 | platform_driver_unregister(&platform_driver); | ||
1291 | 1465 | ||
1292 | kfree(ehotk); | 1466 | kfree(eeepc); |
1293 | return 0; | 1467 | return 0; |
1294 | } | 1468 | } |
1295 | 1469 | ||
1470 | |||
1471 | static const struct acpi_device_id eeepc_device_ids[] = { | ||
1472 | {EEEPC_ACPI_HID, 0}, | ||
1473 | {"", 0}, | ||
1474 | }; | ||
1475 | MODULE_DEVICE_TABLE(acpi, eeepc_device_ids); | ||
1476 | |||
1477 | static struct acpi_driver eeepc_acpi_driver = { | ||
1478 | .name = EEEPC_LAPTOP_NAME, | ||
1479 | .class = EEEPC_ACPI_CLASS, | ||
1480 | .owner = THIS_MODULE, | ||
1481 | .ids = eeepc_device_ids, | ||
1482 | .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, | ||
1483 | .ops = { | ||
1484 | .add = eeepc_acpi_add, | ||
1485 | .remove = eeepc_acpi_remove, | ||
1486 | .notify = eeepc_acpi_notify, | ||
1487 | }, | ||
1488 | }; | ||
1489 | |||
1490 | |||
1296 | static int __init eeepc_laptop_init(void) | 1491 | static int __init eeepc_laptop_init(void) |
1297 | { | 1492 | { |
1298 | int result; | 1493 | int result; |
1299 | 1494 | ||
1300 | if (acpi_disabled) | 1495 | result = platform_driver_register(&platform_driver); |
1301 | return -ENODEV; | ||
1302 | result = acpi_bus_register_driver(&eeepc_hotk_driver); | ||
1303 | if (result < 0) | 1496 | if (result < 0) |
1304 | return result; | 1497 | return result; |
1305 | if (!ehotk) { | 1498 | |
1306 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | 1499 | result = acpi_bus_register_driver(&eeepc_acpi_driver); |
1307 | return -ENODEV; | 1500 | if (result < 0) |
1501 | goto fail_acpi_driver; | ||
1502 | |||
1503 | if (!eeepc_device_present) { | ||
1504 | result = -ENODEV; | ||
1505 | goto fail_no_device; | ||
1308 | } | 1506 | } |
1507 | |||
1309 | return 0; | 1508 | return 0; |
1509 | |||
1510 | fail_no_device: | ||
1511 | acpi_bus_unregister_driver(&eeepc_acpi_driver); | ||
1512 | fail_acpi_driver: | ||
1513 | platform_driver_unregister(&platform_driver); | ||
1514 | return result; | ||
1310 | } | 1515 | } |
1311 | 1516 | ||
1312 | static void __exit eeepc_laptop_exit(void) | 1517 | static void __exit eeepc_laptop_exit(void) |
1313 | { | 1518 | { |
1314 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | 1519 | acpi_bus_unregister_driver(&eeepc_acpi_driver); |
1520 | platform_driver_unregister(&platform_driver); | ||
1315 | } | 1521 | } |
1316 | 1522 | ||
1317 | module_init(eeepc_laptop_init); | 1523 | module_init(eeepc_laptop_init); |
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c new file mode 100644 index 000000000000..b227eb469f49 --- /dev/null +++ b/drivers/platform/x86/eeepc-wmi.c | |||
@@ -0,0 +1,413 @@ | |||
1 | /* | ||
2 | * Eee PC WMI hotkey driver | ||
3 | * | ||
4 | * Copyright(C) 2010 Intel Corporation. | ||
5 | * | ||
6 | * Portions based on wistron_btns.c: | ||
7 | * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> | ||
8 | * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> | ||
9 | * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | */ | ||
25 | |||
26 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
27 | |||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/types.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/input.h> | ||
34 | #include <linux/input/sparse-keymap.h> | ||
35 | #include <linux/fb.h> | ||
36 | #include <linux/backlight.h> | ||
37 | #include <linux/platform_device.h> | ||
38 | #include <acpi/acpi_bus.h> | ||
39 | #include <acpi/acpi_drivers.h> | ||
40 | |||
41 | #define EEEPC_WMI_FILE "eeepc-wmi" | ||
42 | |||
43 | MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>"); | ||
44 | MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); | ||
45 | MODULE_LICENSE("GPL"); | ||
46 | |||
47 | #define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" | ||
48 | #define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" | ||
49 | |||
50 | MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); | ||
51 | MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); | ||
52 | |||
53 | #define NOTIFY_BRNUP_MIN 0x11 | ||
54 | #define NOTIFY_BRNUP_MAX 0x1f | ||
55 | #define NOTIFY_BRNDOWN_MIN 0x20 | ||
56 | #define NOTIFY_BRNDOWN_MAX 0x2e | ||
57 | |||
58 | #define EEEPC_WMI_METHODID_DEVS 0x53564544 | ||
59 | #define EEEPC_WMI_METHODID_DSTS 0x53544344 | ||
60 | |||
61 | #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 | ||
62 | |||
63 | static const struct key_entry eeepc_wmi_keymap[] = { | ||
64 | /* Sleep already handled via generic ACPI code */ | ||
65 | { KE_KEY, 0x5d, { KEY_WLAN } }, | ||
66 | { KE_KEY, 0x32, { KEY_MUTE } }, | ||
67 | { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, | ||
68 | { KE_KEY, 0x30, { KEY_VOLUMEUP } }, | ||
69 | { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, | ||
70 | { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, | ||
71 | { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, | ||
72 | { KE_END, 0}, | ||
73 | }; | ||
74 | |||
75 | struct bios_args { | ||
76 | u32 dev_id; | ||
77 | u32 ctrl_param; | ||
78 | }; | ||
79 | |||
80 | struct eeepc_wmi { | ||
81 | struct input_dev *inputdev; | ||
82 | struct backlight_device *backlight_device; | ||
83 | }; | ||
84 | |||
85 | static struct platform_device *platform_device; | ||
86 | |||
87 | static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) | ||
88 | { | ||
89 | int err; | ||
90 | |||
91 | eeepc->inputdev = input_allocate_device(); | ||
92 | if (!eeepc->inputdev) | ||
93 | return -ENOMEM; | ||
94 | |||
95 | eeepc->inputdev->name = "Eee PC WMI hotkeys"; | ||
96 | eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; | ||
97 | eeepc->inputdev->id.bustype = BUS_HOST; | ||
98 | eeepc->inputdev->dev.parent = &platform_device->dev; | ||
99 | |||
100 | err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); | ||
101 | if (err) | ||
102 | goto err_free_dev; | ||
103 | |||
104 | err = input_register_device(eeepc->inputdev); | ||
105 | if (err) | ||
106 | goto err_free_keymap; | ||
107 | |||
108 | return 0; | ||
109 | |||
110 | err_free_keymap: | ||
111 | sparse_keymap_free(eeepc->inputdev); | ||
112 | err_free_dev: | ||
113 | input_free_device(eeepc->inputdev); | ||
114 | return err; | ||
115 | } | ||
116 | |||
117 | static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc) | ||
118 | { | ||
119 | if (eeepc->inputdev) { | ||
120 | sparse_keymap_free(eeepc->inputdev); | ||
121 | input_unregister_device(eeepc->inputdev); | ||
122 | } | ||
123 | |||
124 | eeepc->inputdev = NULL; | ||
125 | } | ||
126 | |||
127 | static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) | ||
128 | { | ||
129 | struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; | ||
130 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
131 | union acpi_object *obj; | ||
132 | acpi_status status; | ||
133 | u32 tmp; | ||
134 | |||
135 | status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, | ||
136 | 1, EEEPC_WMI_METHODID_DSTS, &input, &output); | ||
137 | |||
138 | if (ACPI_FAILURE(status)) | ||
139 | return status; | ||
140 | |||
141 | obj = (union acpi_object *)output.pointer; | ||
142 | if (obj && obj->type == ACPI_TYPE_INTEGER) | ||
143 | tmp = (u32)obj->integer.value; | ||
144 | else | ||
145 | tmp = 0; | ||
146 | |||
147 | if (ctrl_param) | ||
148 | *ctrl_param = tmp; | ||
149 | |||
150 | kfree(obj); | ||
151 | |||
152 | return status; | ||
153 | |||
154 | } | ||
155 | |||
156 | static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) | ||
157 | { | ||
158 | struct bios_args args = { | ||
159 | .dev_id = dev_id, | ||
160 | .ctrl_param = ctrl_param, | ||
161 | }; | ||
162 | struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; | ||
163 | acpi_status status; | ||
164 | |||
165 | status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, | ||
166 | 1, EEEPC_WMI_METHODID_DEVS, &input, NULL); | ||
167 | |||
168 | return status; | ||
169 | } | ||
170 | |||
171 | static int read_brightness(struct backlight_device *bd) | ||
172 | { | ||
173 | static u32 ctrl_param; | ||
174 | acpi_status status; | ||
175 | |||
176 | status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param); | ||
177 | |||
178 | if (ACPI_FAILURE(status)) | ||
179 | return -1; | ||
180 | else | ||
181 | return ctrl_param & 0xFF; | ||
182 | } | ||
183 | |||
184 | static int update_bl_status(struct backlight_device *bd) | ||
185 | { | ||
186 | |||
187 | static u32 ctrl_param; | ||
188 | acpi_status status; | ||
189 | |||
190 | ctrl_param = bd->props.brightness; | ||
191 | |||
192 | status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, ctrl_param); | ||
193 | |||
194 | if (ACPI_FAILURE(status)) | ||
195 | return -1; | ||
196 | else | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static const struct backlight_ops eeepc_wmi_bl_ops = { | ||
201 | .get_brightness = read_brightness, | ||
202 | .update_status = update_bl_status, | ||
203 | }; | ||
204 | |||
205 | static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code) | ||
206 | { | ||
207 | struct backlight_device *bd = eeepc->backlight_device; | ||
208 | int old = bd->props.brightness; | ||
209 | int new; | ||
210 | |||
211 | if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) | ||
212 | new = code - NOTIFY_BRNUP_MIN + 1; | ||
213 | else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX) | ||
214 | new = code - NOTIFY_BRNDOWN_MIN; | ||
215 | |||
216 | bd->props.brightness = new; | ||
217 | backlight_update_status(bd); | ||
218 | backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); | ||
219 | |||
220 | return old; | ||
221 | } | ||
222 | |||
223 | static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) | ||
224 | { | ||
225 | struct backlight_device *bd; | ||
226 | struct backlight_properties props; | ||
227 | |||
228 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
229 | props.max_brightness = 15; | ||
230 | bd = backlight_device_register(EEEPC_WMI_FILE, | ||
231 | &platform_device->dev, eeepc, | ||
232 | &eeepc_wmi_bl_ops, &props); | ||
233 | if (IS_ERR(bd)) { | ||
234 | pr_err("Could not register backlight device\n"); | ||
235 | return PTR_ERR(bd); | ||
236 | } | ||
237 | |||
238 | eeepc->backlight_device = bd; | ||
239 | |||
240 | bd->props.brightness = read_brightness(bd); | ||
241 | bd->props.power = FB_BLANK_UNBLANK; | ||
242 | backlight_update_status(bd); | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc) | ||
248 | { | ||
249 | if (eeepc->backlight_device) | ||
250 | backlight_device_unregister(eeepc->backlight_device); | ||
251 | |||
252 | eeepc->backlight_device = NULL; | ||
253 | } | ||
254 | |||
255 | static void eeepc_wmi_notify(u32 value, void *context) | ||
256 | { | ||
257 | struct eeepc_wmi *eeepc = context; | ||
258 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
259 | union acpi_object *obj; | ||
260 | acpi_status status; | ||
261 | int code; | ||
262 | int orig_code; | ||
263 | |||
264 | status = wmi_get_event_data(value, &response); | ||
265 | if (status != AE_OK) { | ||
266 | pr_err("bad event status 0x%x\n", status); | ||
267 | return; | ||
268 | } | ||
269 | |||
270 | obj = (union acpi_object *)response.pointer; | ||
271 | |||
272 | if (obj && obj->type == ACPI_TYPE_INTEGER) { | ||
273 | code = obj->integer.value; | ||
274 | orig_code = code; | ||
275 | |||
276 | if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) | ||
277 | code = NOTIFY_BRNUP_MIN; | ||
278 | else if (code >= NOTIFY_BRNDOWN_MIN && | ||
279 | code <= NOTIFY_BRNDOWN_MAX) | ||
280 | code = NOTIFY_BRNDOWN_MIN; | ||
281 | |||
282 | if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { | ||
283 | if (!acpi_video_backlight_support()) | ||
284 | eeepc_wmi_backlight_notify(eeepc, orig_code); | ||
285 | } | ||
286 | |||
287 | if (!sparse_keymap_report_event(eeepc->inputdev, | ||
288 | code, 1, true)) | ||
289 | pr_info("Unknown key %x pressed\n", code); | ||
290 | } | ||
291 | |||
292 | kfree(obj); | ||
293 | } | ||
294 | |||
295 | static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) | ||
296 | { | ||
297 | struct eeepc_wmi *eeepc; | ||
298 | int err; | ||
299 | acpi_status status; | ||
300 | |||
301 | eeepc = platform_get_drvdata(device); | ||
302 | |||
303 | err = eeepc_wmi_input_init(eeepc); | ||
304 | if (err) | ||
305 | goto error_input; | ||
306 | |||
307 | if (!acpi_video_backlight_support()) { | ||
308 | err = eeepc_wmi_backlight_init(eeepc); | ||
309 | if (err) | ||
310 | goto error_backlight; | ||
311 | } else | ||
312 | pr_info("Backlight controlled by ACPI video driver\n"); | ||
313 | |||
314 | status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, | ||
315 | eeepc_wmi_notify, eeepc); | ||
316 | if (ACPI_FAILURE(status)) { | ||
317 | pr_err("Unable to register notify handler - %d\n", | ||
318 | status); | ||
319 | err = -ENODEV; | ||
320 | goto error_wmi; | ||
321 | } | ||
322 | |||
323 | return 0; | ||
324 | |||
325 | error_wmi: | ||
326 | eeepc_wmi_backlight_exit(eeepc); | ||
327 | error_backlight: | ||
328 | eeepc_wmi_input_exit(eeepc); | ||
329 | error_input: | ||
330 | return err; | ||
331 | } | ||
332 | |||
333 | static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) | ||
334 | { | ||
335 | struct eeepc_wmi *eeepc; | ||
336 | |||
337 | eeepc = platform_get_drvdata(device); | ||
338 | wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); | ||
339 | eeepc_wmi_backlight_exit(eeepc); | ||
340 | eeepc_wmi_input_exit(eeepc); | ||
341 | |||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static struct platform_driver platform_driver = { | ||
346 | .driver = { | ||
347 | .name = EEEPC_WMI_FILE, | ||
348 | .owner = THIS_MODULE, | ||
349 | }, | ||
350 | .probe = eeepc_wmi_platform_probe, | ||
351 | .remove = __devexit_p(eeepc_wmi_platform_remove), | ||
352 | }; | ||
353 | |||
354 | static int __init eeepc_wmi_init(void) | ||
355 | { | ||
356 | struct eeepc_wmi *eeepc; | ||
357 | int err; | ||
358 | |||
359 | if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || | ||
360 | !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) { | ||
361 | pr_warning("No known WMI GUID found\n"); | ||
362 | return -ENODEV; | ||
363 | } | ||
364 | |||
365 | eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); | ||
366 | if (!eeepc) | ||
367 | return -ENOMEM; | ||
368 | |||
369 | platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); | ||
370 | if (!platform_device) { | ||
371 | pr_warning("Unable to allocate platform device\n"); | ||
372 | err = -ENOMEM; | ||
373 | goto fail_platform; | ||
374 | } | ||
375 | |||
376 | err = platform_device_add(platform_device); | ||
377 | if (err) { | ||
378 | pr_warning("Unable to add platform device\n"); | ||
379 | goto put_dev; | ||
380 | } | ||
381 | |||
382 | platform_set_drvdata(platform_device, eeepc); | ||
383 | |||
384 | err = platform_driver_register(&platform_driver); | ||
385 | if (err) { | ||
386 | pr_warning("Unable to register platform driver\n"); | ||
387 | goto del_dev; | ||
388 | } | ||
389 | |||
390 | return 0; | ||
391 | |||
392 | del_dev: | ||
393 | platform_device_del(platform_device); | ||
394 | put_dev: | ||
395 | platform_device_put(platform_device); | ||
396 | fail_platform: | ||
397 | kfree(eeepc); | ||
398 | |||
399 | return err; | ||
400 | } | ||
401 | |||
402 | static void __exit eeepc_wmi_exit(void) | ||
403 | { | ||
404 | struct eeepc_wmi *eeepc; | ||
405 | |||
406 | eeepc = platform_get_drvdata(platform_device); | ||
407 | platform_driver_unregister(&platform_driver); | ||
408 | platform_device_unregister(platform_device); | ||
409 | kfree(eeepc); | ||
410 | } | ||
411 | |||
412 | module_init(eeepc_wmi_init); | ||
413 | module_exit(eeepc_wmi_exit); | ||
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index bcd4ba8be7db..47b4fd75aa34 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c | |||
@@ -66,6 +66,7 @@ | |||
66 | #include <linux/kfifo.h> | 66 | #include <linux/kfifo.h> |
67 | #include <linux/video_output.h> | 67 | #include <linux/video_output.h> |
68 | #include <linux/platform_device.h> | 68 | #include <linux/platform_device.h> |
69 | #include <linux/slab.h> | ||
69 | #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) | 70 | #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) |
70 | #include <linux/leds.h> | 71 | #include <linux/leds.h> |
71 | #endif | 72 | #endif |
@@ -164,7 +165,7 @@ struct fujitsu_hotkey_t { | |||
164 | struct input_dev *input; | 165 | struct input_dev *input; |
165 | char phys[32]; | 166 | char phys[32]; |
166 | struct platform_device *pf_device; | 167 | struct platform_device *pf_device; |
167 | struct kfifo *fifo; | 168 | struct kfifo fifo; |
168 | spinlock_t fifo_lock; | 169 | spinlock_t fifo_lock; |
169 | int rfkill_supported; | 170 | int rfkill_supported; |
170 | int rfkill_state; | 171 | int rfkill_state; |
@@ -376,8 +377,8 @@ static int get_lcd_level(void) | |||
376 | 377 | ||
377 | status = | 378 | status = |
378 | acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state); | 379 | acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state); |
379 | if (status < 0) | 380 | if (ACPI_FAILURE(status)) |
380 | return status; | 381 | return 0; |
381 | 382 | ||
382 | fujitsu->brightness_level = state & 0x0fffffff; | 383 | fujitsu->brightness_level = state & 0x0fffffff; |
383 | 384 | ||
@@ -398,8 +399,8 @@ static int get_max_brightness(void) | |||
398 | 399 | ||
399 | status = | 400 | status = |
400 | acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state); | 401 | acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state); |
401 | if (status < 0) | 402 | if (ACPI_FAILURE(status)) |
402 | return status; | 403 | return -1; |
403 | 404 | ||
404 | fujitsu->max_brightness = state; | 405 | fujitsu->max_brightness = state; |
405 | 406 | ||
@@ -824,12 +825,10 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | |||
824 | 825 | ||
825 | /* kfifo */ | 826 | /* kfifo */ |
826 | spin_lock_init(&fujitsu_hotkey->fifo_lock); | 827 | spin_lock_init(&fujitsu_hotkey->fifo_lock); |
827 | fujitsu_hotkey->fifo = | 828 | error = kfifo_alloc(&fujitsu_hotkey->fifo, RINGBUFFERSIZE * sizeof(int), |
828 | kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL, | 829 | GFP_KERNEL); |
829 | &fujitsu_hotkey->fifo_lock); | 830 | if (error) { |
830 | if (IS_ERR(fujitsu_hotkey->fifo)) { | ||
831 | printk(KERN_ERR "kfifo_alloc failed\n"); | 831 | printk(KERN_ERR "kfifo_alloc failed\n"); |
832 | error = PTR_ERR(fujitsu_hotkey->fifo); | ||
833 | goto err_stop; | 832 | goto err_stop; |
834 | } | 833 | } |
835 | 834 | ||
@@ -934,7 +933,7 @@ err_unregister_input_dev: | |||
934 | err_free_input_dev: | 933 | err_free_input_dev: |
935 | input_free_device(input); | 934 | input_free_device(input); |
936 | err_free_fifo: | 935 | err_free_fifo: |
937 | kfifo_free(fujitsu_hotkey->fifo); | 936 | kfifo_free(&fujitsu_hotkey->fifo); |
938 | err_stop: | 937 | err_stop: |
939 | return result; | 938 | return result; |
940 | } | 939 | } |
@@ -956,7 +955,7 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type) | |||
956 | 955 | ||
957 | input_free_device(input); | 956 | input_free_device(input); |
958 | 957 | ||
959 | kfifo_free(fujitsu_hotkey->fifo); | 958 | kfifo_free(&fujitsu_hotkey->fifo); |
960 | 959 | ||
961 | fujitsu_hotkey->acpi_handle = NULL; | 960 | fujitsu_hotkey->acpi_handle = NULL; |
962 | 961 | ||
@@ -1008,9 +1007,10 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event) | |||
1008 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | 1007 | vdbg_printk(FUJLAPTOP_DBG_TRACE, |
1009 | "Push keycode into ringbuffer [%d]\n", | 1008 | "Push keycode into ringbuffer [%d]\n", |
1010 | keycode); | 1009 | keycode); |
1011 | status = kfifo_put(fujitsu_hotkey->fifo, | 1010 | status = kfifo_in_locked(&fujitsu_hotkey->fifo, |
1012 | (unsigned char *)&keycode, | 1011 | (unsigned char *)&keycode, |
1013 | sizeof(keycode)); | 1012 | sizeof(keycode), |
1013 | &fujitsu_hotkey->fifo_lock); | ||
1014 | if (status != sizeof(keycode)) { | 1014 | if (status != sizeof(keycode)) { |
1015 | vdbg_printk(FUJLAPTOP_DBG_WARN, | 1015 | vdbg_printk(FUJLAPTOP_DBG_WARN, |
1016 | "Could not push keycode [0x%x]\n", | 1016 | "Could not push keycode [0x%x]\n", |
@@ -1021,11 +1021,12 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event) | |||
1021 | } | 1021 | } |
1022 | } else if (keycode == 0) { | 1022 | } else if (keycode == 0) { |
1023 | while ((status = | 1023 | while ((status = |
1024 | kfifo_get | 1024 | kfifo_out_locked( |
1025 | (fujitsu_hotkey->fifo, (unsigned char *) | 1025 | &fujitsu_hotkey->fifo, |
1026 | &keycode_r, | 1026 | (unsigned char *) &keycode_r, |
1027 | sizeof | 1027 | sizeof(keycode_r), |
1028 | (keycode_r))) == sizeof(keycode_r)) { | 1028 | &fujitsu_hotkey->fifo_lock)) |
1029 | == sizeof(keycode_r)) { | ||
1029 | input_report_key(input, keycode_r, 0); | 1030 | input_report_key(input, keycode_r, 0); |
1030 | input_sync(input); | 1031 | input_sync(input); |
1031 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | 1032 | vdbg_printk(FUJLAPTOP_DBG_TRACE, |
@@ -1126,16 +1127,20 @@ static int __init fujitsu_init(void) | |||
1126 | /* Register backlight stuff */ | 1127 | /* Register backlight stuff */ |
1127 | 1128 | ||
1128 | if (!acpi_video_backlight_support()) { | 1129 | if (!acpi_video_backlight_support()) { |
1129 | fujitsu->bl_device = | 1130 | struct backlight_properties props; |
1130 | backlight_device_register("fujitsu-laptop", NULL, NULL, | 1131 | |
1131 | &fujitsubl_ops); | 1132 | memset(&props, 0, sizeof(struct backlight_properties)); |
1133 | max_brightness = fujitsu->max_brightness; | ||
1134 | props.max_brightness = max_brightness - 1; | ||
1135 | fujitsu->bl_device = backlight_device_register("fujitsu-laptop", | ||
1136 | NULL, NULL, | ||
1137 | &fujitsubl_ops, | ||
1138 | &props); | ||
1132 | if (IS_ERR(fujitsu->bl_device)) { | 1139 | if (IS_ERR(fujitsu->bl_device)) { |
1133 | ret = PTR_ERR(fujitsu->bl_device); | 1140 | ret = PTR_ERR(fujitsu->bl_device); |
1134 | fujitsu->bl_device = NULL; | 1141 | fujitsu->bl_device = NULL; |
1135 | goto fail_sysfs_group; | 1142 | goto fail_sysfs_group; |
1136 | } | 1143 | } |
1137 | max_brightness = fujitsu->max_brightness; | ||
1138 | fujitsu->bl_device->props.max_brightness = max_brightness - 1; | ||
1139 | fujitsu->bl_device->props.brightness = fujitsu->brightness_level; | 1144 | fujitsu->bl_device->props.brightness = fujitsu->brightness_level; |
1140 | } | 1145 | } |
1141 | 1146 | ||
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index c2842171cec6..51c07a05a7bc 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <linux/init.h> | 28 | #include <linux/init.h> |
29 | #include <linux/slab.h> | ||
29 | #include <linux/types.h> | 30 | #include <linux/types.h> |
30 | #include <linux/input.h> | 31 | #include <linux/input.h> |
31 | #include <acpi/acpi_drivers.h> | 32 | #include <acpi/acpi_drivers.h> |
@@ -51,7 +52,13 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); | |||
51 | #define HPWMI_WIRELESS_QUERY 0x5 | 52 | #define HPWMI_WIRELESS_QUERY 0x5 |
52 | #define HPWMI_HOTKEY_QUERY 0xc | 53 | #define HPWMI_HOTKEY_QUERY 0xc |
53 | 54 | ||
54 | static int __init hp_wmi_bios_setup(struct platform_device *device); | 55 | enum hp_wmi_radio { |
56 | HPWMI_WIFI = 0, | ||
57 | HPWMI_BLUETOOTH = 1, | ||
58 | HPWMI_WWAN = 2, | ||
59 | }; | ||
60 | |||
61 | static int __devinit hp_wmi_bios_setup(struct platform_device *device); | ||
55 | static int __exit hp_wmi_bios_remove(struct platform_device *device); | 62 | static int __exit hp_wmi_bios_remove(struct platform_device *device); |
56 | static int hp_wmi_resume_handler(struct device *device); | 63 | static int hp_wmi_resume_handler(struct device *device); |
57 | 64 | ||
@@ -83,6 +90,7 @@ static struct key_entry hp_wmi_keymap[] = { | |||
83 | {KE_KEY, 0x20e6, KEY_PROG1}, | 90 | {KE_KEY, 0x20e6, KEY_PROG1}, |
84 | {KE_KEY, 0x2142, KEY_MEDIA}, | 91 | {KE_KEY, 0x2142, KEY_MEDIA}, |
85 | {KE_KEY, 0x213b, KEY_INFO}, | 92 | {KE_KEY, 0x213b, KEY_INFO}, |
93 | {KE_KEY, 0x2169, KEY_DIRECTION}, | ||
86 | {KE_KEY, 0x231b, KEY_HELP}, | 94 | {KE_KEY, 0x231b, KEY_HELP}, |
87 | {KE_END, 0} | 95 | {KE_END, 0} |
88 | }; | 96 | }; |
@@ -94,7 +102,7 @@ static struct rfkill *wifi_rfkill; | |||
94 | static struct rfkill *bluetooth_rfkill; | 102 | static struct rfkill *bluetooth_rfkill; |
95 | static struct rfkill *wwan_rfkill; | 103 | static struct rfkill *wwan_rfkill; |
96 | 104 | ||
97 | static struct dev_pm_ops hp_wmi_pm_ops = { | 105 | static const struct dev_pm_ops hp_wmi_pm_ops = { |
98 | .resume = hp_wmi_resume_handler, | 106 | .resume = hp_wmi_resume_handler, |
99 | .restore = hp_wmi_resume_handler, | 107 | .restore = hp_wmi_resume_handler, |
100 | }; | 108 | }; |
@@ -128,10 +136,15 @@ static int hp_wmi_perform_query(int query, int write, int value) | |||
128 | 136 | ||
129 | obj = output.pointer; | 137 | obj = output.pointer; |
130 | 138 | ||
131 | if (!obj || obj->type != ACPI_TYPE_BUFFER) | 139 | if (!obj) |
140 | return -EINVAL; | ||
141 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
142 | kfree(obj); | ||
132 | return -EINVAL; | 143 | return -EINVAL; |
144 | } | ||
133 | 145 | ||
134 | bios_return = *((struct bios_return *)obj->buffer.pointer); | 146 | bios_return = *((struct bios_return *)obj->buffer.pointer); |
147 | kfree(obj); | ||
135 | if (bios_return.return_code > 0) | 148 | if (bios_return.return_code > 0) |
136 | return bios_return.return_code * -1; | 149 | return bios_return.return_code * -1; |
137 | else | 150 | else |
@@ -175,8 +188,8 @@ static int hp_wmi_tablet_state(void) | |||
175 | 188 | ||
176 | static int hp_wmi_set_block(void *data, bool blocked) | 189 | static int hp_wmi_set_block(void *data, bool blocked) |
177 | { | 190 | { |
178 | unsigned long b = (unsigned long) data; | 191 | enum hp_wmi_radio r = (enum hp_wmi_radio) data; |
179 | int query = BIT(b + 8) | ((!blocked) << b); | 192 | int query = BIT(r + 8) | ((!blocked) << r); |
180 | 193 | ||
181 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query); | 194 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query); |
182 | } | 195 | } |
@@ -185,31 +198,23 @@ static const struct rfkill_ops hp_wmi_rfkill_ops = { | |||
185 | .set_block = hp_wmi_set_block, | 198 | .set_block = hp_wmi_set_block, |
186 | }; | 199 | }; |
187 | 200 | ||
188 | static bool hp_wmi_wifi_state(void) | 201 | static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) |
189 | { | 202 | { |
190 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | 203 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); |
204 | int mask = 0x200 << (r * 8); | ||
191 | 205 | ||
192 | if (wireless & 0x100) | 206 | if (wireless & mask) |
193 | return false; | 207 | return false; |
194 | else | 208 | else |
195 | return true; | 209 | return true; |
196 | } | 210 | } |
197 | 211 | ||
198 | static bool hp_wmi_bluetooth_state(void) | 212 | static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) |
199 | { | 213 | { |
200 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | 214 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); |
215 | int mask = 0x800 << (r * 8); | ||
201 | 216 | ||
202 | if (wireless & 0x10000) | 217 | if (wireless & mask) |
203 | return false; | ||
204 | else | ||
205 | return true; | ||
206 | } | ||
207 | |||
208 | static bool hp_wmi_wwan_state(void) | ||
209 | { | ||
210 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | ||
211 | |||
212 | if (wireless & 0x1000000) | ||
213 | return false; | 218 | return false; |
214 | else | 219 | else |
215 | return true; | 220 | return true; |
@@ -274,7 +279,7 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); | |||
274 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); | 279 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); |
275 | static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); | 280 | static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); |
276 | 281 | ||
277 | static struct key_entry *hp_wmi_get_entry_by_scancode(int code) | 282 | static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code) |
278 | { | 283 | { |
279 | struct key_entry *key; | 284 | struct key_entry *key; |
280 | 285 | ||
@@ -285,7 +290,7 @@ static struct key_entry *hp_wmi_get_entry_by_scancode(int code) | |||
285 | return NULL; | 290 | return NULL; |
286 | } | 291 | } |
287 | 292 | ||
288 | static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode) | 293 | static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode) |
289 | { | 294 | { |
290 | struct key_entry *key; | 295 | struct key_entry *key; |
291 | 296 | ||
@@ -296,7 +301,8 @@ static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode) | |||
296 | return NULL; | 301 | return NULL; |
297 | } | 302 | } |
298 | 303 | ||
299 | static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode) | 304 | static int hp_wmi_getkeycode(struct input_dev *dev, |
305 | unsigned int scancode, unsigned int *keycode) | ||
300 | { | 306 | { |
301 | struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); | 307 | struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); |
302 | 308 | ||
@@ -308,13 +314,11 @@ static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode) | |||
308 | return -EINVAL; | 314 | return -EINVAL; |
309 | } | 315 | } |
310 | 316 | ||
311 | static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode) | 317 | static int hp_wmi_setkeycode(struct input_dev *dev, |
318 | unsigned int scancode, unsigned int keycode) | ||
312 | { | 319 | { |
313 | struct key_entry *key; | 320 | struct key_entry *key; |
314 | int old_keycode; | 321 | unsigned int old_keycode; |
315 | |||
316 | if (keycode < 0 || keycode > KEY_MAX) | ||
317 | return -EINVAL; | ||
318 | 322 | ||
319 | key = hp_wmi_get_entry_by_scancode(scancode); | 323 | key = hp_wmi_get_entry_by_scancode(scancode); |
320 | if (key && key->type == KE_KEY) { | 324 | if (key && key->type == KE_KEY) { |
@@ -334,49 +338,62 @@ static void hp_wmi_notify(u32 value, void *context) | |||
334 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | 338 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; |
335 | static struct key_entry *key; | 339 | static struct key_entry *key; |
336 | union acpi_object *obj; | 340 | union acpi_object *obj; |
341 | int eventcode; | ||
342 | acpi_status status; | ||
337 | 343 | ||
338 | wmi_get_event_data(value, &response); | 344 | status = wmi_get_event_data(value, &response); |
345 | if (status != AE_OK) { | ||
346 | printk(KERN_INFO "hp-wmi: bad event status 0x%x\n", status); | ||
347 | return; | ||
348 | } | ||
339 | 349 | ||
340 | obj = (union acpi_object *)response.pointer; | 350 | obj = (union acpi_object *)response.pointer; |
341 | 351 | ||
342 | if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) { | 352 | if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length != 8) { |
343 | int eventcode = *((u8 *) obj->buffer.pointer); | 353 | printk(KERN_INFO "HP WMI: Unknown response received\n"); |
344 | if (eventcode == 0x4) | 354 | kfree(obj); |
345 | eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, | 355 | return; |
346 | 0); | 356 | } |
347 | key = hp_wmi_get_entry_by_scancode(eventcode); | 357 | |
348 | if (key) { | 358 | eventcode = *((u8 *) obj->buffer.pointer); |
349 | switch (key->type) { | 359 | kfree(obj); |
350 | case KE_KEY: | 360 | if (eventcode == 0x4) |
351 | input_report_key(hp_wmi_input_dev, | 361 | eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, |
352 | key->keycode, 1); | 362 | 0); |
353 | input_sync(hp_wmi_input_dev); | 363 | key = hp_wmi_get_entry_by_scancode(eventcode); |
354 | input_report_key(hp_wmi_input_dev, | 364 | if (key) { |
355 | key->keycode, 0); | 365 | switch (key->type) { |
356 | input_sync(hp_wmi_input_dev); | 366 | case KE_KEY: |
357 | break; | 367 | input_report_key(hp_wmi_input_dev, |
358 | } | 368 | key->keycode, 1); |
359 | } else if (eventcode == 0x1) { | ||
360 | input_report_switch(hp_wmi_input_dev, SW_DOCK, | ||
361 | hp_wmi_dock_state()); | ||
362 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, | ||
363 | hp_wmi_tablet_state()); | ||
364 | input_sync(hp_wmi_input_dev); | 369 | input_sync(hp_wmi_input_dev); |
365 | } else if (eventcode == 0x5) { | 370 | input_report_key(hp_wmi_input_dev, |
366 | if (wifi_rfkill) | 371 | key->keycode, 0); |
367 | rfkill_set_sw_state(wifi_rfkill, | 372 | input_sync(hp_wmi_input_dev); |
368 | hp_wmi_wifi_state()); | 373 | break; |
369 | if (bluetooth_rfkill) | 374 | } |
370 | rfkill_set_sw_state(bluetooth_rfkill, | 375 | } else if (eventcode == 0x1) { |
371 | hp_wmi_bluetooth_state()); | 376 | input_report_switch(hp_wmi_input_dev, SW_DOCK, |
372 | if (wwan_rfkill) | 377 | hp_wmi_dock_state()); |
373 | rfkill_set_sw_state(wwan_rfkill, | 378 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, |
374 | hp_wmi_wwan_state()); | 379 | hp_wmi_tablet_state()); |
375 | } else | 380 | input_sync(hp_wmi_input_dev); |
376 | printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", | 381 | } else if (eventcode == 0x5) { |
377 | eventcode); | 382 | if (wifi_rfkill) |
383 | rfkill_set_states(wifi_rfkill, | ||
384 | hp_wmi_get_sw_state(HPWMI_WIFI), | ||
385 | hp_wmi_get_hw_state(HPWMI_WIFI)); | ||
386 | if (bluetooth_rfkill) | ||
387 | rfkill_set_states(bluetooth_rfkill, | ||
388 | hp_wmi_get_sw_state(HPWMI_BLUETOOTH), | ||
389 | hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); | ||
390 | if (wwan_rfkill) | ||
391 | rfkill_set_states(wwan_rfkill, | ||
392 | hp_wmi_get_sw_state(HPWMI_WWAN), | ||
393 | hp_wmi_get_hw_state(HPWMI_WWAN)); | ||
378 | } else | 394 | } else |
379 | printk(KERN_INFO "HP WMI: Unknown response received\n"); | 395 | printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", |
396 | eventcode); | ||
380 | } | 397 | } |
381 | 398 | ||
382 | static int __init hp_wmi_input_setup(void) | 399 | static int __init hp_wmi_input_setup(void) |
@@ -430,7 +447,7 @@ static void cleanup_sysfs(struct platform_device *device) | |||
430 | device_remove_file(&device->dev, &dev_attr_tablet); | 447 | device_remove_file(&device->dev, &dev_attr_tablet); |
431 | } | 448 | } |
432 | 449 | ||
433 | static int __init hp_wmi_bios_setup(struct platform_device *device) | 450 | static int __devinit hp_wmi_bios_setup(struct platform_device *device) |
434 | { | 451 | { |
435 | int err; | 452 | int err; |
436 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | 453 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); |
@@ -455,7 +472,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) | |||
455 | wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, | 472 | wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, |
456 | RFKILL_TYPE_WLAN, | 473 | RFKILL_TYPE_WLAN, |
457 | &hp_wmi_rfkill_ops, | 474 | &hp_wmi_rfkill_ops, |
458 | (void *) 0); | 475 | (void *) HPWMI_WIFI); |
476 | rfkill_init_sw_state(wifi_rfkill, | ||
477 | hp_wmi_get_sw_state(HPWMI_WIFI)); | ||
478 | rfkill_set_hw_state(wifi_rfkill, | ||
479 | hp_wmi_get_hw_state(HPWMI_WIFI)); | ||
459 | err = rfkill_register(wifi_rfkill); | 480 | err = rfkill_register(wifi_rfkill); |
460 | if (err) | 481 | if (err) |
461 | goto register_wifi_error; | 482 | goto register_wifi_error; |
@@ -465,7 +486,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) | |||
465 | bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, | 486 | bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, |
466 | RFKILL_TYPE_BLUETOOTH, | 487 | RFKILL_TYPE_BLUETOOTH, |
467 | &hp_wmi_rfkill_ops, | 488 | &hp_wmi_rfkill_ops, |
468 | (void *) 1); | 489 | (void *) HPWMI_BLUETOOTH); |
490 | rfkill_init_sw_state(bluetooth_rfkill, | ||
491 | hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); | ||
492 | rfkill_set_hw_state(bluetooth_rfkill, | ||
493 | hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); | ||
469 | err = rfkill_register(bluetooth_rfkill); | 494 | err = rfkill_register(bluetooth_rfkill); |
470 | if (err) | 495 | if (err) |
471 | goto register_bluetooth_error; | 496 | goto register_bluetooth_error; |
@@ -475,7 +500,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) | |||
475 | wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, | 500 | wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, |
476 | RFKILL_TYPE_WWAN, | 501 | RFKILL_TYPE_WWAN, |
477 | &hp_wmi_rfkill_ops, | 502 | &hp_wmi_rfkill_ops, |
478 | (void *) 2); | 503 | (void *) HPWMI_WWAN); |
504 | rfkill_init_sw_state(wwan_rfkill, | ||
505 | hp_wmi_get_sw_state(HPWMI_WWAN)); | ||
506 | rfkill_set_hw_state(wwan_rfkill, | ||
507 | hp_wmi_get_hw_state(HPWMI_WWAN)); | ||
479 | err = rfkill_register(wwan_rfkill); | 508 | err = rfkill_register(wwan_rfkill); |
480 | if (err) | 509 | if (err) |
481 | goto register_wwan_err; | 510 | goto register_wwan_err; |
@@ -533,6 +562,19 @@ static int hp_wmi_resume_handler(struct device *device) | |||
533 | input_sync(hp_wmi_input_dev); | 562 | input_sync(hp_wmi_input_dev); |
534 | } | 563 | } |
535 | 564 | ||
565 | if (wifi_rfkill) | ||
566 | rfkill_set_states(wifi_rfkill, | ||
567 | hp_wmi_get_sw_state(HPWMI_WIFI), | ||
568 | hp_wmi_get_hw_state(HPWMI_WIFI)); | ||
569 | if (bluetooth_rfkill) | ||
570 | rfkill_set_states(bluetooth_rfkill, | ||
571 | hp_wmi_get_sw_state(HPWMI_BLUETOOTH), | ||
572 | hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); | ||
573 | if (wwan_rfkill) | ||
574 | rfkill_set_states(wwan_rfkill, | ||
575 | hp_wmi_get_sw_state(HPWMI_WWAN), | ||
576 | hp_wmi_get_hw_state(HPWMI_WWAN)); | ||
577 | |||
536 | return 0; | 578 | return 0; |
537 | } | 579 | } |
538 | 580 | ||
@@ -543,7 +585,7 @@ static int __init hp_wmi_init(void) | |||
543 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | 585 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { |
544 | err = wmi_install_notify_handler(HPWMI_EVENT_GUID, | 586 | err = wmi_install_notify_handler(HPWMI_EVENT_GUID, |
545 | hp_wmi_notify, NULL); | 587 | hp_wmi_notify, NULL); |
546 | if (!err) | 588 | if (ACPI_SUCCESS(err)) |
547 | hp_wmi_input_setup(); | 589 | hp_wmi_input_setup(); |
548 | } | 590 | } |
549 | 591 | ||
diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index 29432a50be45..2f795ce2b939 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/kernel.h> | 30 | #include <linux/kernel.h> |
31 | #include <linux/module.h> | 31 | #include <linux/module.h> |
32 | #include <linux/init.h> | 32 | #include <linux/init.h> |
33 | #include <linux/slab.h> | ||
33 | #include <linux/types.h> | 34 | #include <linux/types.h> |
34 | #include <linux/pci.h> | 35 | #include <linux/pci.h> |
35 | #include <linux/pm.h> | 36 | #include <linux/pm.h> |
@@ -396,6 +397,7 @@ static int intel_menlow_add_one_attribute(char *name, int mode, void *show, | |||
396 | if (!attr) | 397 | if (!attr) |
397 | return -ENOMEM; | 398 | return -ENOMEM; |
398 | 399 | ||
400 | sysfs_attr_init(&attr->attr.attr); /* That is consistent naming :D */ | ||
399 | attr->attr.attr.name = name; | 401 | attr->attr.attr.name = name; |
400 | attr->attr.attr.mode = mode; | 402 | attr->attr.attr.mode = mode; |
401 | attr->attr.show = show; | 403 | attr->attr.show = show; |
@@ -510,7 +512,7 @@ static int __init intel_menlow_module_init(void) | |||
510 | /* Looking for sensors in each ACPI thermal zone */ | 512 | /* Looking for sensors in each ACPI thermal zone */ |
511 | status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT, | 513 | status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT, |
512 | ACPI_UINT32_MAX, | 514 | ACPI_UINT32_MAX, |
513 | intel_menlow_register_sensor, NULL, NULL); | 515 | intel_menlow_register_sensor, NULL, NULL, NULL); |
514 | if (ACPI_FAILURE(status)) | 516 | if (ACPI_FAILURE(status)) |
515 | return -ENODEV; | 517 | return -ENODEV; |
516 | 518 | ||
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 759763d18e4c..996223a7c009 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c | |||
@@ -58,6 +58,7 @@ | |||
58 | #include <linux/dmi.h> | 58 | #include <linux/dmi.h> |
59 | #include <linux/backlight.h> | 59 | #include <linux/backlight.h> |
60 | #include <linux/platform_device.h> | 60 | #include <linux/platform_device.h> |
61 | #include <linux/rfkill.h> | ||
61 | 62 | ||
62 | #define MSI_DRIVER_VERSION "0.5" | 63 | #define MSI_DRIVER_VERSION "0.5" |
63 | 64 | ||
@@ -66,6 +67,20 @@ | |||
66 | #define MSI_EC_COMMAND_WIRELESS 0x10 | 67 | #define MSI_EC_COMMAND_WIRELESS 0x10 |
67 | #define MSI_EC_COMMAND_LCD_LEVEL 0x11 | 68 | #define MSI_EC_COMMAND_LCD_LEVEL 0x11 |
68 | 69 | ||
70 | #define MSI_STANDARD_EC_COMMAND_ADDRESS 0x2e | ||
71 | #define MSI_STANDARD_EC_BLUETOOTH_MASK (1 << 0) | ||
72 | #define MSI_STANDARD_EC_WEBCAM_MASK (1 << 1) | ||
73 | #define MSI_STANDARD_EC_WLAN_MASK (1 << 3) | ||
74 | #define MSI_STANDARD_EC_3G_MASK (1 << 4) | ||
75 | |||
76 | /* For set SCM load flag to disable BIOS fn key */ | ||
77 | #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d | ||
78 | #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) | ||
79 | |||
80 | static int msi_laptop_resume(struct platform_device *device); | ||
81 | |||
82 | #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f | ||
83 | |||
69 | static int force; | 84 | static int force; |
70 | module_param(force, bool, 0); | 85 | module_param(force, bool, 0); |
71 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | 86 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); |
@@ -74,6 +89,23 @@ static int auto_brightness; | |||
74 | module_param(auto_brightness, int, 0); | 89 | module_param(auto_brightness, int, 0); |
75 | MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); | 90 | MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); |
76 | 91 | ||
92 | static bool old_ec_model; | ||
93 | static int wlan_s, bluetooth_s, threeg_s; | ||
94 | static int threeg_exists; | ||
95 | |||
96 | /* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G, | ||
97 | * those netbook will load the SCM (windows app) to disable the original | ||
98 | * Wlan/Bluetooth control by BIOS when user press fn key, then control | ||
99 | * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user | ||
100 | * cann't on/off 3G module on those 3G netbook. | ||
101 | * On Linux, msi-laptop driver will do the same thing to disable the | ||
102 | * original BIOS control, then might need use HAL or other userland | ||
103 | * application to do the software control that simulate with SCM. | ||
104 | * e.g. MSI N034 netbook | ||
105 | */ | ||
106 | static bool load_scm_model; | ||
107 | static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg; | ||
108 | |||
77 | /* Hardware access */ | 109 | /* Hardware access */ |
78 | 110 | ||
79 | static int set_lcd_level(int level) | 111 | static int set_lcd_level(int level) |
@@ -130,6 +162,35 @@ static int set_auto_brightness(int enable) | |||
130 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1); | 162 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1); |
131 | } | 163 | } |
132 | 164 | ||
165 | static ssize_t set_device_state(const char *buf, size_t count, u8 mask) | ||
166 | { | ||
167 | int status; | ||
168 | u8 wdata = 0, rdata; | ||
169 | int result; | ||
170 | |||
171 | if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1)) | ||
172 | return -EINVAL; | ||
173 | |||
174 | /* read current device state */ | ||
175 | result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata); | ||
176 | if (result < 0) | ||
177 | return -EINVAL; | ||
178 | |||
179 | if (!!(rdata & mask) != status) { | ||
180 | /* reverse device bit */ | ||
181 | if (rdata & mask) | ||
182 | wdata = rdata & ~mask; | ||
183 | else | ||
184 | wdata = rdata | mask; | ||
185 | |||
186 | result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata); | ||
187 | if (result < 0) | ||
188 | return -EINVAL; | ||
189 | } | ||
190 | |||
191 | return count; | ||
192 | } | ||
193 | |||
133 | static int get_wireless_state(int *wlan, int *bluetooth) | 194 | static int get_wireless_state(int *wlan, int *bluetooth) |
134 | { | 195 | { |
135 | u8 wdata = 0, rdata; | 196 | u8 wdata = 0, rdata; |
@@ -148,6 +209,38 @@ static int get_wireless_state(int *wlan, int *bluetooth) | |||
148 | return 0; | 209 | return 0; |
149 | } | 210 | } |
150 | 211 | ||
212 | static int get_wireless_state_ec_standard(void) | ||
213 | { | ||
214 | u8 rdata; | ||
215 | int result; | ||
216 | |||
217 | result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata); | ||
218 | if (result < 0) | ||
219 | return -1; | ||
220 | |||
221 | wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK); | ||
222 | |||
223 | bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK); | ||
224 | |||
225 | threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK); | ||
226 | |||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static int get_threeg_exists(void) | ||
231 | { | ||
232 | u8 rdata; | ||
233 | int result; | ||
234 | |||
235 | result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata); | ||
236 | if (result < 0) | ||
237 | return -1; | ||
238 | |||
239 | threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK); | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | |||
151 | /* Backlight device stuff */ | 244 | /* Backlight device stuff */ |
152 | 245 | ||
153 | static int bl_get_brightness(struct backlight_device *b) | 246 | static int bl_get_brightness(struct backlight_device *b) |
@@ -176,26 +269,71 @@ static ssize_t show_wlan(struct device *dev, | |||
176 | 269 | ||
177 | int ret, enabled; | 270 | int ret, enabled; |
178 | 271 | ||
179 | ret = get_wireless_state(&enabled, NULL); | 272 | if (old_ec_model) { |
273 | ret = get_wireless_state(&enabled, NULL); | ||
274 | } else { | ||
275 | ret = get_wireless_state_ec_standard(); | ||
276 | enabled = wlan_s; | ||
277 | } | ||
180 | if (ret < 0) | 278 | if (ret < 0) |
181 | return ret; | 279 | return ret; |
182 | 280 | ||
183 | return sprintf(buf, "%i\n", enabled); | 281 | return sprintf(buf, "%i\n", enabled); |
184 | } | 282 | } |
185 | 283 | ||
284 | static ssize_t store_wlan(struct device *dev, | ||
285 | struct device_attribute *attr, const char *buf, size_t count) | ||
286 | { | ||
287 | return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK); | ||
288 | } | ||
289 | |||
186 | static ssize_t show_bluetooth(struct device *dev, | 290 | static ssize_t show_bluetooth(struct device *dev, |
187 | struct device_attribute *attr, char *buf) | 291 | struct device_attribute *attr, char *buf) |
188 | { | 292 | { |
189 | 293 | ||
190 | int ret, enabled; | 294 | int ret, enabled; |
191 | 295 | ||
192 | ret = get_wireless_state(NULL, &enabled); | 296 | if (old_ec_model) { |
297 | ret = get_wireless_state(NULL, &enabled); | ||
298 | } else { | ||
299 | ret = get_wireless_state_ec_standard(); | ||
300 | enabled = bluetooth_s; | ||
301 | } | ||
193 | if (ret < 0) | 302 | if (ret < 0) |
194 | return ret; | 303 | return ret; |
195 | 304 | ||
196 | return sprintf(buf, "%i\n", enabled); | 305 | return sprintf(buf, "%i\n", enabled); |
197 | } | 306 | } |
198 | 307 | ||
308 | static ssize_t store_bluetooth(struct device *dev, | ||
309 | struct device_attribute *attr, const char *buf, size_t count) | ||
310 | { | ||
311 | return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK); | ||
312 | } | ||
313 | |||
314 | static ssize_t show_threeg(struct device *dev, | ||
315 | struct device_attribute *attr, char *buf) | ||
316 | { | ||
317 | |||
318 | int ret; | ||
319 | |||
320 | /* old msi ec not support 3G */ | ||
321 | if (old_ec_model) | ||
322 | return -1; | ||
323 | |||
324 | ret = get_wireless_state_ec_standard(); | ||
325 | if (ret < 0) | ||
326 | return ret; | ||
327 | |||
328 | return sprintf(buf, "%i\n", threeg_s); | ||
329 | } | ||
330 | |||
331 | static ssize_t store_threeg(struct device *dev, | ||
332 | struct device_attribute *attr, const char *buf, size_t count) | ||
333 | { | ||
334 | return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK); | ||
335 | } | ||
336 | |||
199 | static ssize_t show_lcd_level(struct device *dev, | 337 | static ssize_t show_lcd_level(struct device *dev, |
200 | struct device_attribute *attr, char *buf) | 338 | struct device_attribute *attr, char *buf) |
201 | { | 339 | { |
@@ -258,6 +396,7 @@ static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); | |||
258 | static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness); | 396 | static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness); |
259 | static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); | 397 | static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); |
260 | static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); | 398 | static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); |
399 | static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); | ||
261 | 400 | ||
262 | static struct attribute *msipf_attributes[] = { | 401 | static struct attribute *msipf_attributes[] = { |
263 | &dev_attr_lcd_level.attr, | 402 | &dev_attr_lcd_level.attr, |
@@ -275,7 +414,8 @@ static struct platform_driver msipf_driver = { | |||
275 | .driver = { | 414 | .driver = { |
276 | .name = "msi-laptop-pf", | 415 | .name = "msi-laptop-pf", |
277 | .owner = THIS_MODULE, | 416 | .owner = THIS_MODULE, |
278 | } | 417 | }, |
418 | .resume = msi_laptop_resume, | ||
279 | }; | 419 | }; |
280 | 420 | ||
281 | static struct platform_device *msipf_device; | 421 | static struct platform_device *msipf_device; |
@@ -332,6 +472,192 @@ static struct dmi_system_id __initdata msi_dmi_table[] = { | |||
332 | { } | 472 | { } |
333 | }; | 473 | }; |
334 | 474 | ||
475 | static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = { | ||
476 | { | ||
477 | .ident = "MSI N034", | ||
478 | .matches = { | ||
479 | DMI_MATCH(DMI_SYS_VENDOR, | ||
480 | "MICRO-STAR INTERNATIONAL CO., LTD"), | ||
481 | DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"), | ||
482 | DMI_MATCH(DMI_CHASSIS_VENDOR, | ||
483 | "MICRO-STAR INTERNATIONAL CO., LTD") | ||
484 | }, | ||
485 | .callback = dmi_check_cb | ||
486 | }, | ||
487 | { } | ||
488 | }; | ||
489 | |||
490 | static int rfkill_bluetooth_set(void *data, bool blocked) | ||
491 | { | ||
492 | /* Do something with blocked...*/ | ||
493 | /* | ||
494 | * blocked == false is on | ||
495 | * blocked == true is off | ||
496 | */ | ||
497 | if (blocked) | ||
498 | set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); | ||
499 | else | ||
500 | set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | static int rfkill_wlan_set(void *data, bool blocked) | ||
506 | { | ||
507 | if (blocked) | ||
508 | set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK); | ||
509 | else | ||
510 | set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK); | ||
511 | |||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | static int rfkill_threeg_set(void *data, bool blocked) | ||
516 | { | ||
517 | if (blocked) | ||
518 | set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK); | ||
519 | else | ||
520 | set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK); | ||
521 | |||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | static struct rfkill_ops rfkill_bluetooth_ops = { | ||
526 | .set_block = rfkill_bluetooth_set | ||
527 | }; | ||
528 | |||
529 | static struct rfkill_ops rfkill_wlan_ops = { | ||
530 | .set_block = rfkill_wlan_set | ||
531 | }; | ||
532 | |||
533 | static struct rfkill_ops rfkill_threeg_ops = { | ||
534 | .set_block = rfkill_threeg_set | ||
535 | }; | ||
536 | |||
537 | static void rfkill_cleanup(void) | ||
538 | { | ||
539 | if (rfk_bluetooth) { | ||
540 | rfkill_unregister(rfk_bluetooth); | ||
541 | rfkill_destroy(rfk_bluetooth); | ||
542 | } | ||
543 | |||
544 | if (rfk_threeg) { | ||
545 | rfkill_unregister(rfk_threeg); | ||
546 | rfkill_destroy(rfk_threeg); | ||
547 | } | ||
548 | |||
549 | if (rfk_wlan) { | ||
550 | rfkill_unregister(rfk_wlan); | ||
551 | rfkill_destroy(rfk_wlan); | ||
552 | } | ||
553 | } | ||
554 | |||
555 | static int rfkill_init(struct platform_device *sdev) | ||
556 | { | ||
557 | /* add rfkill */ | ||
558 | int retval; | ||
559 | |||
560 | rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev, | ||
561 | RFKILL_TYPE_BLUETOOTH, | ||
562 | &rfkill_bluetooth_ops, NULL); | ||
563 | if (!rfk_bluetooth) { | ||
564 | retval = -ENOMEM; | ||
565 | goto err_bluetooth; | ||
566 | } | ||
567 | retval = rfkill_register(rfk_bluetooth); | ||
568 | if (retval) | ||
569 | goto err_bluetooth; | ||
570 | |||
571 | rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN, | ||
572 | &rfkill_wlan_ops, NULL); | ||
573 | if (!rfk_wlan) { | ||
574 | retval = -ENOMEM; | ||
575 | goto err_wlan; | ||
576 | } | ||
577 | retval = rfkill_register(rfk_wlan); | ||
578 | if (retval) | ||
579 | goto err_wlan; | ||
580 | |||
581 | if (threeg_exists) { | ||
582 | rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev, | ||
583 | RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL); | ||
584 | if (!rfk_threeg) { | ||
585 | retval = -ENOMEM; | ||
586 | goto err_threeg; | ||
587 | } | ||
588 | retval = rfkill_register(rfk_threeg); | ||
589 | if (retval) | ||
590 | goto err_threeg; | ||
591 | } | ||
592 | |||
593 | return 0; | ||
594 | |||
595 | err_threeg: | ||
596 | rfkill_destroy(rfk_threeg); | ||
597 | if (rfk_wlan) | ||
598 | rfkill_unregister(rfk_wlan); | ||
599 | err_wlan: | ||
600 | rfkill_destroy(rfk_wlan); | ||
601 | if (rfk_bluetooth) | ||
602 | rfkill_unregister(rfk_bluetooth); | ||
603 | err_bluetooth: | ||
604 | rfkill_destroy(rfk_bluetooth); | ||
605 | |||
606 | return retval; | ||
607 | } | ||
608 | |||
609 | static int msi_laptop_resume(struct platform_device *device) | ||
610 | { | ||
611 | u8 data; | ||
612 | int result; | ||
613 | |||
614 | if (!load_scm_model) | ||
615 | return 0; | ||
616 | |||
617 | /* set load SCM to disable hardware control by fn key */ | ||
618 | result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); | ||
619 | if (result < 0) | ||
620 | return result; | ||
621 | |||
622 | result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, | ||
623 | data | MSI_STANDARD_EC_SCM_LOAD_MASK); | ||
624 | if (result < 0) | ||
625 | return result; | ||
626 | |||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | static int load_scm_model_init(struct platform_device *sdev) | ||
631 | { | ||
632 | u8 data; | ||
633 | int result; | ||
634 | |||
635 | /* allow userland write sysfs file */ | ||
636 | dev_attr_bluetooth.store = store_bluetooth; | ||
637 | dev_attr_wlan.store = store_wlan; | ||
638 | dev_attr_threeg.store = store_threeg; | ||
639 | dev_attr_bluetooth.attr.mode |= S_IWUSR; | ||
640 | dev_attr_wlan.attr.mode |= S_IWUSR; | ||
641 | dev_attr_threeg.attr.mode |= S_IWUSR; | ||
642 | |||
643 | /* disable hardware control by fn key */ | ||
644 | result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); | ||
645 | if (result < 0) | ||
646 | return result; | ||
647 | |||
648 | result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, | ||
649 | data | MSI_STANDARD_EC_SCM_LOAD_MASK); | ||
650 | if (result < 0) | ||
651 | return result; | ||
652 | |||
653 | /* initial rfkill */ | ||
654 | result = rfkill_init(sdev); | ||
655 | if (result < 0) | ||
656 | return result; | ||
657 | |||
658 | return 0; | ||
659 | } | ||
660 | |||
335 | static int __init msi_init(void) | 661 | static int __init msi_init(void) |
336 | { | 662 | { |
337 | int ret; | 663 | int ret; |
@@ -339,8 +665,14 @@ static int __init msi_init(void) | |||
339 | if (acpi_disabled) | 665 | if (acpi_disabled) |
340 | return -ENODEV; | 666 | return -ENODEV; |
341 | 667 | ||
342 | if (!force && !dmi_check_system(msi_dmi_table)) | 668 | if (force || dmi_check_system(msi_dmi_table)) |
343 | return -ENODEV; | 669 | old_ec_model = 1; |
670 | |||
671 | if (!old_ec_model) | ||
672 | get_threeg_exists(); | ||
673 | |||
674 | if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table)) | ||
675 | load_scm_model = 1; | ||
344 | 676 | ||
345 | if (auto_brightness < 0 || auto_brightness > 2) | 677 | if (auto_brightness < 0 || auto_brightness > 2) |
346 | return -EINVAL; | 678 | return -EINVAL; |
@@ -351,11 +683,14 @@ static int __init msi_init(void) | |||
351 | printk(KERN_INFO "MSI: Brightness ignored, must be controlled " | 683 | printk(KERN_INFO "MSI: Brightness ignored, must be controlled " |
352 | "by ACPI video driver\n"); | 684 | "by ACPI video driver\n"); |
353 | } else { | 685 | } else { |
686 | struct backlight_properties props; | ||
687 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
688 | props.max_brightness = MSI_LCD_LEVEL_MAX - 1; | ||
354 | msibl_device = backlight_device_register("msi-laptop-bl", NULL, | 689 | msibl_device = backlight_device_register("msi-laptop-bl", NULL, |
355 | NULL, &msibl_ops); | 690 | NULL, &msibl_ops, |
691 | &props); | ||
356 | if (IS_ERR(msibl_device)) | 692 | if (IS_ERR(msibl_device)) |
357 | return PTR_ERR(msibl_device); | 693 | return PTR_ERR(msibl_device); |
358 | msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1; | ||
359 | } | 694 | } |
360 | 695 | ||
361 | ret = platform_driver_register(&msipf_driver); | 696 | ret = platform_driver_register(&msipf_driver); |
@@ -374,10 +709,23 @@ static int __init msi_init(void) | |||
374 | if (ret) | 709 | if (ret) |
375 | goto fail_platform_device1; | 710 | goto fail_platform_device1; |
376 | 711 | ||
712 | if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) { | ||
713 | ret = -EINVAL; | ||
714 | goto fail_platform_device1; | ||
715 | } | ||
716 | |||
377 | ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group); | 717 | ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group); |
378 | if (ret) | 718 | if (ret) |
379 | goto fail_platform_device2; | 719 | goto fail_platform_device2; |
380 | 720 | ||
721 | if (!old_ec_model) { | ||
722 | if (threeg_exists) | ||
723 | ret = device_create_file(&msipf_device->dev, | ||
724 | &dev_attr_threeg); | ||
725 | if (ret) | ||
726 | goto fail_platform_device2; | ||
727 | } | ||
728 | |||
381 | /* Disable automatic brightness control by default because | 729 | /* Disable automatic brightness control by default because |
382 | * this module was probably loaded to do brightness control in | 730 | * this module was probably loaded to do brightness control in |
383 | * software. */ | 731 | * software. */ |
@@ -412,10 +760,14 @@ static void __exit msi_cleanup(void) | |||
412 | { | 760 | { |
413 | 761 | ||
414 | sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); | 762 | sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); |
763 | if (!old_ec_model && threeg_exists) | ||
764 | device_remove_file(&msipf_device->dev, &dev_attr_threeg); | ||
415 | platform_device_unregister(msipf_device); | 765 | platform_device_unregister(msipf_device); |
416 | platform_driver_unregister(&msipf_driver); | 766 | platform_driver_unregister(&msipf_driver); |
417 | backlight_device_unregister(msibl_device); | 767 | backlight_device_unregister(msibl_device); |
418 | 768 | ||
769 | rfkill_cleanup(); | ||
770 | |||
419 | /* Enable automatic brightness control again */ | 771 | /* Enable automatic brightness control again */ |
420 | if (auto_brightness != 2) | 772 | if (auto_brightness != 2) |
421 | set_auto_brightness(1); | 773 | set_auto_brightness(1); |
@@ -435,3 +787,4 @@ MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARIN | |||
435 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*"); | 787 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*"); |
436 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); | 788 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); |
437 | MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); | 789 | MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); |
790 | MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*"); | ||
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c new file mode 100644 index 000000000000..d1736009636f --- /dev/null +++ b/drivers/platform/x86/msi-wmi.c | |||
@@ -0,0 +1,294 @@ | |||
1 | /* | ||
2 | * MSI WMI hotkeys | ||
3 | * | ||
4 | * Copyright (C) 2009 Novell <trenn@suse.de> | ||
5 | * | ||
6 | * Most stuff taken over from hp-wmi | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | |||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/input.h> | ||
26 | #include <linux/input/sparse-keymap.h> | ||
27 | #include <linux/acpi.h> | ||
28 | #include <linux/backlight.h> | ||
29 | #include <linux/slab.h> | ||
30 | |||
31 | MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>"); | ||
32 | MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver"); | ||
33 | MODULE_LICENSE("GPL"); | ||
34 | |||
35 | MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45"); | ||
36 | MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"); | ||
37 | |||
38 | #define DRV_NAME "msi-wmi" | ||
39 | #define DRV_PFX DRV_NAME ": " | ||
40 | |||
41 | #define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45" | ||
42 | #define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2" | ||
43 | |||
44 | #define dprintk(msg...) pr_debug(DRV_PFX msg) | ||
45 | |||
46 | #define KEYCODE_BASE 0xD0 | ||
47 | #define MSI_WMI_BRIGHTNESSUP KEYCODE_BASE | ||
48 | #define MSI_WMI_BRIGHTNESSDOWN (KEYCODE_BASE + 1) | ||
49 | #define MSI_WMI_VOLUMEUP (KEYCODE_BASE + 2) | ||
50 | #define MSI_WMI_VOLUMEDOWN (KEYCODE_BASE + 3) | ||
51 | static struct key_entry msi_wmi_keymap[] = { | ||
52 | { KE_KEY, MSI_WMI_BRIGHTNESSUP, {KEY_BRIGHTNESSUP} }, | ||
53 | { KE_KEY, MSI_WMI_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} }, | ||
54 | { KE_KEY, MSI_WMI_VOLUMEUP, {KEY_VOLUMEUP} }, | ||
55 | { KE_KEY, MSI_WMI_VOLUMEDOWN, {KEY_VOLUMEDOWN} }, | ||
56 | { KE_END, 0} | ||
57 | }; | ||
58 | static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1]; | ||
59 | |||
60 | struct backlight_device *backlight; | ||
61 | |||
62 | static int backlight_map[] = { 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF }; | ||
63 | |||
64 | static struct input_dev *msi_wmi_input_dev; | ||
65 | |||
66 | static int msi_wmi_query_block(int instance, int *ret) | ||
67 | { | ||
68 | acpi_status status; | ||
69 | union acpi_object *obj; | ||
70 | |||
71 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
72 | |||
73 | status = wmi_query_block(MSIWMI_BIOS_GUID, instance, &output); | ||
74 | |||
75 | obj = output.pointer; | ||
76 | |||
77 | if (!obj || obj->type != ACPI_TYPE_INTEGER) { | ||
78 | if (obj) { | ||
79 | printk(KERN_ERR DRV_PFX "query block returned object " | ||
80 | "type: %d - buffer length:%d\n", obj->type, | ||
81 | obj->type == ACPI_TYPE_BUFFER ? | ||
82 | obj->buffer.length : 0); | ||
83 | } | ||
84 | kfree(obj); | ||
85 | return -EINVAL; | ||
86 | } | ||
87 | *ret = obj->integer.value; | ||
88 | kfree(obj); | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static int msi_wmi_set_block(int instance, int value) | ||
93 | { | ||
94 | acpi_status status; | ||
95 | |||
96 | struct acpi_buffer input = { sizeof(int), &value }; | ||
97 | |||
98 | dprintk("Going to set block of instance: %d - value: %d\n", | ||
99 | instance, value); | ||
100 | |||
101 | status = wmi_set_block(MSIWMI_BIOS_GUID, instance, &input); | ||
102 | |||
103 | return ACPI_SUCCESS(status) ? 0 : 1; | ||
104 | } | ||
105 | |||
106 | static int bl_get(struct backlight_device *bd) | ||
107 | { | ||
108 | int level, err, ret; | ||
109 | |||
110 | /* Instance 1 is "get backlight", cmp with DSDT */ | ||
111 | err = msi_wmi_query_block(1, &ret); | ||
112 | if (err) { | ||
113 | printk(KERN_ERR DRV_PFX "Could not query backlight: %d\n", err); | ||
114 | return -EINVAL; | ||
115 | } | ||
116 | dprintk("Get: Query block returned: %d\n", ret); | ||
117 | for (level = 0; level < ARRAY_SIZE(backlight_map); level++) { | ||
118 | if (backlight_map[level] == ret) { | ||
119 | dprintk("Current backlight level: 0x%X - index: %d\n", | ||
120 | backlight_map[level], level); | ||
121 | break; | ||
122 | } | ||
123 | } | ||
124 | if (level == ARRAY_SIZE(backlight_map)) { | ||
125 | printk(KERN_ERR DRV_PFX "get: Invalid brightness value: 0x%X\n", | ||
126 | ret); | ||
127 | return -EINVAL; | ||
128 | } | ||
129 | return level; | ||
130 | } | ||
131 | |||
132 | static int bl_set_status(struct backlight_device *bd) | ||
133 | { | ||
134 | int bright = bd->props.brightness; | ||
135 | if (bright >= ARRAY_SIZE(backlight_map) || bright < 0) | ||
136 | return -EINVAL; | ||
137 | |||
138 | /* Instance 0 is "set backlight" */ | ||
139 | return msi_wmi_set_block(0, backlight_map[bright]); | ||
140 | } | ||
141 | |||
142 | static const struct backlight_ops msi_backlight_ops = { | ||
143 | .get_brightness = bl_get, | ||
144 | .update_status = bl_set_status, | ||
145 | }; | ||
146 | |||
147 | static void msi_wmi_notify(u32 value, void *context) | ||
148 | { | ||
149 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
150 | static struct key_entry *key; | ||
151 | union acpi_object *obj; | ||
152 | ktime_t cur; | ||
153 | acpi_status status; | ||
154 | |||
155 | status = wmi_get_event_data(value, &response); | ||
156 | if (status != AE_OK) { | ||
157 | printk(KERN_INFO DRV_PFX "bad event status 0x%x\n", status); | ||
158 | return; | ||
159 | } | ||
160 | |||
161 | obj = (union acpi_object *)response.pointer; | ||
162 | |||
163 | if (obj && obj->type == ACPI_TYPE_INTEGER) { | ||
164 | int eventcode = obj->integer.value; | ||
165 | dprintk("Eventcode: 0x%x\n", eventcode); | ||
166 | key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev, | ||
167 | eventcode); | ||
168 | if (key) { | ||
169 | ktime_t diff; | ||
170 | cur = ktime_get_real(); | ||
171 | diff = ktime_sub(cur, last_pressed[key->code - | ||
172 | KEYCODE_BASE]); | ||
173 | /* Ignore event if the same event happened in a 50 ms | ||
174 | timeframe -> Key press may result in 10-20 GPEs */ | ||
175 | if (ktime_to_us(diff) < 1000 * 50) { | ||
176 | dprintk("Suppressed key event 0x%X - " | ||
177 | "Last press was %lld us ago\n", | ||
178 | key->code, ktime_to_us(diff)); | ||
179 | return; | ||
180 | } | ||
181 | last_pressed[key->code - KEYCODE_BASE] = cur; | ||
182 | |||
183 | if (key->type == KE_KEY && | ||
184 | /* Brightness is served via acpi video driver */ | ||
185 | (!acpi_video_backlight_support() || | ||
186 | (key->code != MSI_WMI_BRIGHTNESSUP && | ||
187 | key->code != MSI_WMI_BRIGHTNESSDOWN))) { | ||
188 | dprintk("Send key: 0x%X - " | ||
189 | "Input layer keycode: %d\n", key->code, | ||
190 | key->keycode); | ||
191 | sparse_keymap_report_entry(msi_wmi_input_dev, | ||
192 | key, 1, true); | ||
193 | } | ||
194 | } else | ||
195 | printk(KERN_INFO "Unknown key pressed - %x\n", | ||
196 | eventcode); | ||
197 | } else | ||
198 | printk(KERN_INFO DRV_PFX "Unknown event received\n"); | ||
199 | kfree(response.pointer); | ||
200 | } | ||
201 | |||
202 | static int __init msi_wmi_input_setup(void) | ||
203 | { | ||
204 | int err; | ||
205 | |||
206 | msi_wmi_input_dev = input_allocate_device(); | ||
207 | if (!msi_wmi_input_dev) | ||
208 | return -ENOMEM; | ||
209 | |||
210 | msi_wmi_input_dev->name = "MSI WMI hotkeys"; | ||
211 | msi_wmi_input_dev->phys = "wmi/input0"; | ||
212 | msi_wmi_input_dev->id.bustype = BUS_HOST; | ||
213 | |||
214 | err = sparse_keymap_setup(msi_wmi_input_dev, msi_wmi_keymap, NULL); | ||
215 | if (err) | ||
216 | goto err_free_dev; | ||
217 | |||
218 | err = input_register_device(msi_wmi_input_dev); | ||
219 | |||
220 | if (err) | ||
221 | goto err_free_keymap; | ||
222 | |||
223 | memset(last_pressed, 0, sizeof(last_pressed)); | ||
224 | |||
225 | return 0; | ||
226 | |||
227 | err_free_keymap: | ||
228 | sparse_keymap_free(msi_wmi_input_dev); | ||
229 | err_free_dev: | ||
230 | input_free_device(msi_wmi_input_dev); | ||
231 | return err; | ||
232 | } | ||
233 | |||
234 | static int __init msi_wmi_init(void) | ||
235 | { | ||
236 | int err; | ||
237 | |||
238 | if (!wmi_has_guid(MSIWMI_EVENT_GUID)) { | ||
239 | printk(KERN_ERR | ||
240 | "This machine doesn't have MSI-hotkeys through WMI\n"); | ||
241 | return -ENODEV; | ||
242 | } | ||
243 | err = wmi_install_notify_handler(MSIWMI_EVENT_GUID, | ||
244 | msi_wmi_notify, NULL); | ||
245 | if (ACPI_FAILURE(err)) | ||
246 | return -EINVAL; | ||
247 | |||
248 | err = msi_wmi_input_setup(); | ||
249 | if (err) | ||
250 | goto err_uninstall_notifier; | ||
251 | |||
252 | if (!acpi_video_backlight_support()) { | ||
253 | struct backlight_properties props; | ||
254 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
255 | props.max_brightness = ARRAY_SIZE(backlight_map) - 1; | ||
256 | backlight = backlight_device_register(DRV_NAME, NULL, NULL, | ||
257 | &msi_backlight_ops, | ||
258 | &props); | ||
259 | if (IS_ERR(backlight)) { | ||
260 | err = PTR_ERR(backlight); | ||
261 | goto err_free_input; | ||
262 | } | ||
263 | |||
264 | err = bl_get(NULL); | ||
265 | if (err < 0) | ||
266 | goto err_free_backlight; | ||
267 | |||
268 | backlight->props.brightness = err; | ||
269 | } | ||
270 | dprintk("Event handler installed\n"); | ||
271 | |||
272 | return 0; | ||
273 | |||
274 | err_free_backlight: | ||
275 | backlight_device_unregister(backlight); | ||
276 | err_free_input: | ||
277 | input_unregister_device(msi_wmi_input_dev); | ||
278 | err_uninstall_notifier: | ||
279 | wmi_remove_notify_handler(MSIWMI_EVENT_GUID); | ||
280 | return err; | ||
281 | } | ||
282 | |||
283 | static void __exit msi_wmi_exit(void) | ||
284 | { | ||
285 | if (wmi_has_guid(MSIWMI_EVENT_GUID)) { | ||
286 | wmi_remove_notify_handler(MSIWMI_EVENT_GUID); | ||
287 | sparse_keymap_free(msi_wmi_input_dev); | ||
288 | input_unregister_device(msi_wmi_input_dev); | ||
289 | backlight_device_unregister(backlight); | ||
290 | } | ||
291 | } | ||
292 | |||
293 | module_init(msi_wmi_init); | ||
294 | module_exit(msi_wmi_exit); | ||
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index fe7cf0188acc..2fb9a32926f8 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c | |||
@@ -124,6 +124,7 @@ | |||
124 | #include <linux/ctype.h> | 124 | #include <linux/ctype.h> |
125 | #include <linux/seq_file.h> | 125 | #include <linux/seq_file.h> |
126 | #include <linux/uaccess.h> | 126 | #include <linux/uaccess.h> |
127 | #include <linux/slab.h> | ||
127 | #include <acpi/acpi_bus.h> | 128 | #include <acpi/acpi_bus.h> |
128 | #include <acpi/acpi_drivers.h> | 129 | #include <acpi/acpi_drivers.h> |
129 | #include <linux/input.h> | 130 | #include <linux/input.h> |
@@ -200,7 +201,7 @@ static struct acpi_driver acpi_pcc_driver = { | |||
200 | }; | 201 | }; |
201 | 202 | ||
202 | #define KEYMAP_SIZE 11 | 203 | #define KEYMAP_SIZE 11 |
203 | static const int initial_keymap[KEYMAP_SIZE] = { | 204 | static const unsigned int initial_keymap[KEYMAP_SIZE] = { |
204 | /* 0 */ KEY_RESERVED, | 205 | /* 0 */ KEY_RESERVED, |
205 | /* 1 */ KEY_BRIGHTNESSDOWN, | 206 | /* 1 */ KEY_BRIGHTNESSDOWN, |
206 | /* 2 */ KEY_BRIGHTNESSUP, | 207 | /* 2 */ KEY_BRIGHTNESSUP, |
@@ -222,7 +223,7 @@ struct pcc_acpi { | |||
222 | struct acpi_device *device; | 223 | struct acpi_device *device; |
223 | struct input_dev *input_dev; | 224 | struct input_dev *input_dev; |
224 | struct backlight_device *backlight; | 225 | struct backlight_device *backlight; |
225 | int keymap[KEYMAP_SIZE]; | 226 | unsigned int keymap[KEYMAP_SIZE]; |
226 | }; | 227 | }; |
227 | 228 | ||
228 | struct pcc_keyinput { | 229 | struct pcc_keyinput { |
@@ -352,7 +353,7 @@ static int bl_set_status(struct backlight_device *bd) | |||
352 | return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright); | 353 | return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright); |
353 | } | 354 | } |
354 | 355 | ||
355 | static struct backlight_ops pcc_backlight_ops = { | 356 | static const struct backlight_ops pcc_backlight_ops = { |
356 | .get_brightness = bl_get, | 357 | .get_brightness = bl_get, |
357 | .update_status = bl_set_status, | 358 | .update_status = bl_set_status, |
358 | }; | 359 | }; |
@@ -445,7 +446,8 @@ static struct attribute_group pcc_attr_group = { | |||
445 | 446 | ||
446 | /* hotkey input device driver */ | 447 | /* hotkey input device driver */ |
447 | 448 | ||
448 | static int pcc_getkeycode(struct input_dev *dev, int scancode, int *keycode) | 449 | static int pcc_getkeycode(struct input_dev *dev, |
450 | unsigned int scancode, unsigned int *keycode) | ||
449 | { | 451 | { |
450 | struct pcc_acpi *pcc = input_get_drvdata(dev); | 452 | struct pcc_acpi *pcc = input_get_drvdata(dev); |
451 | 453 | ||
@@ -457,7 +459,7 @@ static int pcc_getkeycode(struct input_dev *dev, int scancode, int *keycode) | |||
457 | return 0; | 459 | return 0; |
458 | } | 460 | } |
459 | 461 | ||
460 | static int keymap_get_by_keycode(struct pcc_acpi *pcc, int keycode) | 462 | static int keymap_get_by_keycode(struct pcc_acpi *pcc, unsigned int keycode) |
461 | { | 463 | { |
462 | int i; | 464 | int i; |
463 | 465 | ||
@@ -469,7 +471,8 @@ static int keymap_get_by_keycode(struct pcc_acpi *pcc, int keycode) | |||
469 | return 0; | 471 | return 0; |
470 | } | 472 | } |
471 | 473 | ||
472 | static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode) | 474 | static int pcc_setkeycode(struct input_dev *dev, |
475 | unsigned int scancode, unsigned int keycode) | ||
473 | { | 476 | { |
474 | struct pcc_acpi *pcc = input_get_drvdata(dev); | 477 | struct pcc_acpi *pcc = input_get_drvdata(dev); |
475 | int oldkeycode; | 478 | int oldkeycode; |
@@ -477,9 +480,6 @@ static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode) | |||
477 | if (scancode >= ARRAY_SIZE(pcc->keymap)) | 480 | if (scancode >= ARRAY_SIZE(pcc->keymap)) |
478 | return -EINVAL; | 481 | return -EINVAL; |
479 | 482 | ||
480 | if (keycode < 0 || keycode > KEY_MAX) | ||
481 | return -EINVAL; | ||
482 | |||
483 | oldkeycode = pcc->keymap[scancode]; | 483 | oldkeycode = pcc->keymap[scancode]; |
484 | pcc->keymap[scancode] = keycode; | 484 | pcc->keymap[scancode] = keycode; |
485 | 485 | ||
@@ -601,6 +601,7 @@ static int acpi_pcc_hotkey_resume(struct acpi_device *device) | |||
601 | 601 | ||
602 | static int acpi_pcc_hotkey_add(struct acpi_device *device) | 602 | static int acpi_pcc_hotkey_add(struct acpi_device *device) |
603 | { | 603 | { |
604 | struct backlight_properties props; | ||
604 | struct pcc_acpi *pcc; | 605 | struct pcc_acpi *pcc; |
605 | int num_sifr, result; | 606 | int num_sifr, result; |
606 | 607 | ||
@@ -638,24 +639,25 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) | |||
638 | if (result) { | 639 | if (result) { |
639 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | 640 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
640 | "Error installing keyinput handler\n")); | 641 | "Error installing keyinput handler\n")); |
641 | goto out_sinf; | 642 | goto out_hotkey; |
642 | } | 643 | } |
643 | 644 | ||
644 | /* initialize backlight */ | ||
645 | pcc->backlight = backlight_device_register("panasonic", NULL, pcc, | ||
646 | &pcc_backlight_ops); | ||
647 | if (IS_ERR(pcc->backlight)) | ||
648 | goto out_input; | ||
649 | |||
650 | if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) { | 645 | if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) { |
651 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | 646 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
652 | "Couldn't retrieve BIOS data\n")); | 647 | "Couldn't retrieve BIOS data\n")); |
653 | goto out_backlight; | 648 | goto out_input; |
649 | } | ||
650 | /* initialize backlight */ | ||
651 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
652 | props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT]; | ||
653 | pcc->backlight = backlight_device_register("panasonic", NULL, pcc, | ||
654 | &pcc_backlight_ops, &props); | ||
655 | if (IS_ERR(pcc->backlight)) { | ||
656 | result = PTR_ERR(pcc->backlight); | ||
657 | goto out_sinf; | ||
654 | } | 658 | } |
655 | 659 | ||
656 | /* read the initial brightness setting from the hardware */ | 660 | /* read the initial brightness setting from the hardware */ |
657 | pcc->backlight->props.max_brightness = | ||
658 | pcc->sinf[SINF_AC_MAX_BRIGHT]; | ||
659 | pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT]; | 661 | pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT]; |
660 | 662 | ||
661 | /* read the initial sticky key mode from the hardware */ | 663 | /* read the initial sticky key mode from the hardware */ |
@@ -670,12 +672,12 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) | |||
670 | 672 | ||
671 | out_backlight: | 673 | out_backlight: |
672 | backlight_device_unregister(pcc->backlight); | 674 | backlight_device_unregister(pcc->backlight); |
675 | out_sinf: | ||
676 | kfree(pcc->sinf); | ||
673 | out_input: | 677 | out_input: |
674 | input_unregister_device(pcc->input_dev); | 678 | input_unregister_device(pcc->input_dev); |
675 | /* no need to input_free_device() since core input API refcount and | 679 | /* no need to input_free_device() since core input API refcount and |
676 | * free()s the device */ | 680 | * free()s the device */ |
677 | out_sinf: | ||
678 | kfree(pcc->sinf); | ||
679 | out_hotkey: | 681 | out_hotkey: |
680 | kfree(pcc); | 682 | kfree(pcc); |
681 | 683 | ||
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index a2a742c8ff7e..1387c5f9c24d 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -58,6 +58,7 @@ | |||
58 | #include <linux/kfifo.h> | 58 | #include <linux/kfifo.h> |
59 | #include <linux/workqueue.h> | 59 | #include <linux/workqueue.h> |
60 | #include <linux/acpi.h> | 60 | #include <linux/acpi.h> |
61 | #include <linux/slab.h> | ||
61 | #include <acpi/acpi_drivers.h> | 62 | #include <acpi/acpi_drivers.h> |
62 | #include <acpi/acpi_bus.h> | 63 | #include <acpi/acpi_bus.h> |
63 | #include <asm/uaccess.h> | 64 | #include <asm/uaccess.h> |
@@ -131,6 +132,7 @@ enum sony_nc_rfkill { | |||
131 | N_SONY_RFKILL, | 132 | N_SONY_RFKILL, |
132 | }; | 133 | }; |
133 | 134 | ||
135 | static int sony_rfkill_handle; | ||
134 | static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL]; | 136 | static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL]; |
135 | static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900}; | 137 | static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900}; |
136 | static void sony_nc_rfkill_update(void); | 138 | static void sony_nc_rfkill_update(void); |
@@ -142,9 +144,9 @@ struct sony_laptop_input_s { | |||
142 | atomic_t users; | 144 | atomic_t users; |
143 | struct input_dev *jog_dev; | 145 | struct input_dev *jog_dev; |
144 | struct input_dev *key_dev; | 146 | struct input_dev *key_dev; |
145 | struct kfifo *fifo; | 147 | struct kfifo fifo; |
146 | spinlock_t fifo_lock; | 148 | spinlock_t fifo_lock; |
147 | struct workqueue_struct *wq; | 149 | struct timer_list release_key_timer; |
148 | }; | 150 | }; |
149 | 151 | ||
150 | static struct sony_laptop_input_s sony_laptop_input = { | 152 | static struct sony_laptop_input_s sony_laptop_input = { |
@@ -232,6 +234,7 @@ static int sony_laptop_input_index[] = { | |||
232 | 56, /* 69 SONYPI_EVENT_VOLUME_INC_PRESSED */ | 234 | 56, /* 69 SONYPI_EVENT_VOLUME_INC_PRESSED */ |
233 | 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ | 235 | 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ |
234 | -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ | 236 | -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ |
237 | 58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */ | ||
235 | }; | 238 | }; |
236 | 239 | ||
237 | static int sony_laptop_input_keycode_map[] = { | 240 | static int sony_laptop_input_keycode_map[] = { |
@@ -293,22 +296,30 @@ static int sony_laptop_input_keycode_map[] = { | |||
293 | KEY_F15, /* 55 SONYPI_EVENT_SETTINGKEY_PRESSED */ | 296 | KEY_F15, /* 55 SONYPI_EVENT_SETTINGKEY_PRESSED */ |
294 | KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ | 297 | KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ |
295 | KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ | 298 | KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ |
299 | KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */ | ||
296 | }; | 300 | }; |
297 | 301 | ||
298 | /* release buttons after a short delay if pressed */ | 302 | /* release buttons after a short delay if pressed */ |
299 | static void do_sony_laptop_release_key(struct work_struct *work) | 303 | static void do_sony_laptop_release_key(unsigned long unused) |
300 | { | 304 | { |
301 | struct sony_laptop_keypress kp; | 305 | struct sony_laptop_keypress kp; |
306 | unsigned long flags; | ||
307 | |||
308 | spin_lock_irqsave(&sony_laptop_input.fifo_lock, flags); | ||
302 | 309 | ||
303 | while (kfifo_get(sony_laptop_input.fifo, (unsigned char *)&kp, | 310 | if (kfifo_out(&sony_laptop_input.fifo, |
304 | sizeof(kp)) == sizeof(kp)) { | 311 | (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) { |
305 | msleep(10); | ||
306 | input_report_key(kp.dev, kp.key, 0); | 312 | input_report_key(kp.dev, kp.key, 0); |
307 | input_sync(kp.dev); | 313 | input_sync(kp.dev); |
308 | } | 314 | } |
315 | |||
316 | /* If there is something in the fifo schedule next release. */ | ||
317 | if (kfifo_len(&sony_laptop_input.fifo) != 0) | ||
318 | mod_timer(&sony_laptop_input.release_key_timer, | ||
319 | jiffies + msecs_to_jiffies(10)); | ||
320 | |||
321 | spin_unlock_irqrestore(&sony_laptop_input.fifo_lock, flags); | ||
309 | } | 322 | } |
310 | static DECLARE_WORK(sony_laptop_release_key_work, | ||
311 | do_sony_laptop_release_key); | ||
312 | 323 | ||
313 | /* forward event to the input subsystem */ | 324 | /* forward event to the input subsystem */ |
314 | static void sony_laptop_report_input_event(u8 event) | 325 | static void sony_laptop_report_input_event(u8 event) |
@@ -362,12 +373,13 @@ static void sony_laptop_report_input_event(u8 event) | |||
362 | /* we emit the scancode so we can always remap the key */ | 373 | /* we emit the scancode so we can always remap the key */ |
363 | input_event(kp.dev, EV_MSC, MSC_SCAN, event); | 374 | input_event(kp.dev, EV_MSC, MSC_SCAN, event); |
364 | input_sync(kp.dev); | 375 | input_sync(kp.dev); |
365 | kfifo_put(sony_laptop_input.fifo, | ||
366 | (unsigned char *)&kp, sizeof(kp)); | ||
367 | 376 | ||
368 | if (!work_pending(&sony_laptop_release_key_work)) | 377 | /* schedule key release */ |
369 | queue_work(sony_laptop_input.wq, | 378 | kfifo_in_locked(&sony_laptop_input.fifo, |
370 | &sony_laptop_release_key_work); | 379 | (unsigned char *)&kp, sizeof(kp), |
380 | &sony_laptop_input.fifo_lock); | ||
381 | mod_timer(&sony_laptop_input.release_key_timer, | ||
382 | jiffies + msecs_to_jiffies(10)); | ||
371 | } else | 383 | } else |
372 | dprintk("unknown input event %.2x\n", event); | 384 | dprintk("unknown input event %.2x\n", event); |
373 | } | 385 | } |
@@ -385,29 +397,21 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) | |||
385 | 397 | ||
386 | /* kfifo */ | 398 | /* kfifo */ |
387 | spin_lock_init(&sony_laptop_input.fifo_lock); | 399 | spin_lock_init(&sony_laptop_input.fifo_lock); |
388 | sony_laptop_input.fifo = | 400 | error = kfifo_alloc(&sony_laptop_input.fifo, |
389 | kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL, | 401 | SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); |
390 | &sony_laptop_input.fifo_lock); | 402 | if (error) { |
391 | if (IS_ERR(sony_laptop_input.fifo)) { | ||
392 | printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); | 403 | printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); |
393 | error = PTR_ERR(sony_laptop_input.fifo); | ||
394 | goto err_dec_users; | 404 | goto err_dec_users; |
395 | } | 405 | } |
396 | 406 | ||
397 | /* init workqueue */ | 407 | setup_timer(&sony_laptop_input.release_key_timer, |
398 | sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop"); | 408 | do_sony_laptop_release_key, 0); |
399 | if (!sony_laptop_input.wq) { | ||
400 | printk(KERN_ERR DRV_PFX | ||
401 | "Unable to create workqueue.\n"); | ||
402 | error = -ENXIO; | ||
403 | goto err_free_kfifo; | ||
404 | } | ||
405 | 409 | ||
406 | /* input keys */ | 410 | /* input keys */ |
407 | key_dev = input_allocate_device(); | 411 | key_dev = input_allocate_device(); |
408 | if (!key_dev) { | 412 | if (!key_dev) { |
409 | error = -ENOMEM; | 413 | error = -ENOMEM; |
410 | goto err_destroy_wq; | 414 | goto err_free_kfifo; |
411 | } | 415 | } |
412 | 416 | ||
413 | key_dev->name = "Sony Vaio Keys"; | 417 | key_dev->name = "Sony Vaio Keys"; |
@@ -416,18 +420,15 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) | |||
416 | key_dev->dev.parent = &acpi_device->dev; | 420 | key_dev->dev.parent = &acpi_device->dev; |
417 | 421 | ||
418 | /* Initialize the Input Drivers: special keys */ | 422 | /* Initialize the Input Drivers: special keys */ |
419 | set_bit(EV_KEY, key_dev->evbit); | 423 | input_set_capability(key_dev, EV_MSC, MSC_SCAN); |
420 | set_bit(EV_MSC, key_dev->evbit); | 424 | |
421 | set_bit(MSC_SCAN, key_dev->mscbit); | 425 | __set_bit(EV_KEY, key_dev->evbit); |
422 | key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]); | 426 | key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]); |
423 | key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map); | 427 | key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map); |
424 | key_dev->keycode = &sony_laptop_input_keycode_map; | 428 | key_dev->keycode = &sony_laptop_input_keycode_map; |
425 | for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) { | 429 | for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) |
426 | if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) { | 430 | __set_bit(sony_laptop_input_keycode_map[i], key_dev->keybit); |
427 | set_bit(sony_laptop_input_keycode_map[i], | 431 | __clear_bit(KEY_RESERVED, key_dev->keybit); |
428 | key_dev->keybit); | ||
429 | } | ||
430 | } | ||
431 | 432 | ||
432 | error = input_register_device(key_dev); | 433 | error = input_register_device(key_dev); |
433 | if (error) | 434 | if (error) |
@@ -447,9 +448,8 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) | |||
447 | jog_dev->id.vendor = PCI_VENDOR_ID_SONY; | 448 | jog_dev->id.vendor = PCI_VENDOR_ID_SONY; |
448 | key_dev->dev.parent = &acpi_device->dev; | 449 | key_dev->dev.parent = &acpi_device->dev; |
449 | 450 | ||
450 | jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); | 451 | input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE); |
451 | jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE); | 452 | input_set_capability(jog_dev, EV_REL, REL_WHEEL); |
452 | jog_dev->relbit[0] = BIT_MASK(REL_WHEEL); | ||
453 | 453 | ||
454 | error = input_register_device(jog_dev); | 454 | error = input_register_device(jog_dev); |
455 | if (error) | 455 | if (error) |
@@ -470,11 +470,8 @@ err_unregister_keydev: | |||
470 | err_free_keydev: | 470 | err_free_keydev: |
471 | input_free_device(key_dev); | 471 | input_free_device(key_dev); |
472 | 472 | ||
473 | err_destroy_wq: | ||
474 | destroy_workqueue(sony_laptop_input.wq); | ||
475 | |||
476 | err_free_kfifo: | 473 | err_free_kfifo: |
477 | kfifo_free(sony_laptop_input.fifo); | 474 | kfifo_free(&sony_laptop_input.fifo); |
478 | 475 | ||
479 | err_dec_users: | 476 | err_dec_users: |
480 | atomic_dec(&sony_laptop_input.users); | 477 | atomic_dec(&sony_laptop_input.users); |
@@ -483,12 +480,23 @@ err_dec_users: | |||
483 | 480 | ||
484 | static void sony_laptop_remove_input(void) | 481 | static void sony_laptop_remove_input(void) |
485 | { | 482 | { |
486 | /* cleanup only after the last user has gone */ | 483 | struct sony_laptop_keypress kp = { NULL }; |
484 | |||
485 | /* Cleanup only after the last user has gone */ | ||
487 | if (!atomic_dec_and_test(&sony_laptop_input.users)) | 486 | if (!atomic_dec_and_test(&sony_laptop_input.users)) |
488 | return; | 487 | return; |
489 | 488 | ||
490 | /* flush workqueue first */ | 489 | del_timer_sync(&sony_laptop_input.release_key_timer); |
491 | flush_workqueue(sony_laptop_input.wq); | 490 | |
491 | /* | ||
492 | * Generate key-up events for remaining keys. Note that we don't | ||
493 | * need locking since nobody is adding new events to the kfifo. | ||
494 | */ | ||
495 | while (kfifo_out(&sony_laptop_input.fifo, | ||
496 | (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) { | ||
497 | input_report_key(kp.dev, kp.key, 0); | ||
498 | input_sync(kp.dev); | ||
499 | } | ||
492 | 500 | ||
493 | /* destroy input devs */ | 501 | /* destroy input devs */ |
494 | input_unregister_device(sony_laptop_input.key_dev); | 502 | input_unregister_device(sony_laptop_input.key_dev); |
@@ -499,8 +507,7 @@ static void sony_laptop_remove_input(void) | |||
499 | sony_laptop_input.jog_dev = NULL; | 507 | sony_laptop_input.jog_dev = NULL; |
500 | } | 508 | } |
501 | 509 | ||
502 | destroy_workqueue(sony_laptop_input.wq); | 510 | kfifo_free(&sony_laptop_input.fifo); |
503 | kfifo_free(sony_laptop_input.fifo); | ||
504 | } | 511 | } |
505 | 512 | ||
506 | /*********** Platform Device ***********/ | 513 | /*********** Platform Device ***********/ |
@@ -890,6 +897,8 @@ static struct sony_nc_event sony_100_events[] = { | |||
890 | { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, | 897 | { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, |
891 | { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, | 898 | { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, |
892 | { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, | 899 | { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, |
900 | { 0xa1, SONYPI_EVENT_MEDIA_PRESSED }, | ||
901 | { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
893 | { 0, 0 }, | 902 | { 0, 0 }, |
894 | }; | 903 | }; |
895 | 904 | ||
@@ -961,7 +970,7 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) | |||
961 | else | 970 | else |
962 | sony_laptop_report_input_event(ev); | 971 | sony_laptop_report_input_event(ev); |
963 | } | 972 | } |
964 | } else if (sony_find_snc_handle(0x124) == ev) { | 973 | } else if (sony_find_snc_handle(sony_rfkill_handle) == ev) { |
965 | sony_nc_rfkill_update(); | 974 | sony_nc_rfkill_update(); |
966 | return; | 975 | return; |
967 | } | 976 | } |
@@ -1067,7 +1076,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked) | |||
1067 | if (!blocked) | 1076 | if (!blocked) |
1068 | argument |= 0xff0000; | 1077 | argument |= 0xff0000; |
1069 | 1078 | ||
1070 | return sony_call_snc_handle(0x124, argument, &result); | 1079 | return sony_call_snc_handle(sony_rfkill_handle, argument, &result); |
1071 | } | 1080 | } |
1072 | 1081 | ||
1073 | static const struct rfkill_ops sony_rfkill_ops = { | 1082 | static const struct rfkill_ops sony_rfkill_ops = { |
@@ -1110,7 +1119,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, | |||
1110 | if (!rfk) | 1119 | if (!rfk) |
1111 | return -ENOMEM; | 1120 | return -ENOMEM; |
1112 | 1121 | ||
1113 | sony_call_snc_handle(0x124, 0x200, &result); | 1122 | sony_call_snc_handle(sony_rfkill_handle, 0x200, &result); |
1114 | hwblock = !(result & 0x1); | 1123 | hwblock = !(result & 0x1); |
1115 | rfkill_set_hw_state(rfk, hwblock); | 1124 | rfkill_set_hw_state(rfk, hwblock); |
1116 | 1125 | ||
@@ -1129,7 +1138,7 @@ static void sony_nc_rfkill_update() | |||
1129 | int result; | 1138 | int result; |
1130 | bool hwblock; | 1139 | bool hwblock; |
1131 | 1140 | ||
1132 | sony_call_snc_handle(0x124, 0x200, &result); | 1141 | sony_call_snc_handle(sony_rfkill_handle, 0x200, &result); |
1133 | hwblock = !(result & 0x1); | 1142 | hwblock = !(result & 0x1); |
1134 | 1143 | ||
1135 | for (i = 0; i < N_SONY_RFKILL; i++) { | 1144 | for (i = 0; i < N_SONY_RFKILL; i++) { |
@@ -1145,36 +1154,82 @@ static void sony_nc_rfkill_update() | |||
1145 | continue; | 1154 | continue; |
1146 | } | 1155 | } |
1147 | 1156 | ||
1148 | sony_call_snc_handle(0x124, argument, &result); | 1157 | sony_call_snc_handle(sony_rfkill_handle, argument, &result); |
1149 | rfkill_set_states(sony_rfkill_devices[i], | 1158 | rfkill_set_states(sony_rfkill_devices[i], |
1150 | !(result & 0xf), false); | 1159 | !(result & 0xf), false); |
1151 | } | 1160 | } |
1152 | } | 1161 | } |
1153 | 1162 | ||
1154 | static int sony_nc_rfkill_setup(struct acpi_device *device) | 1163 | static void sony_nc_rfkill_setup(struct acpi_device *device) |
1155 | { | 1164 | { |
1156 | int result, ret; | 1165 | int offset; |
1166 | u8 dev_code, i; | ||
1167 | acpi_status status; | ||
1168 | struct acpi_object_list params; | ||
1169 | union acpi_object in_obj; | ||
1170 | union acpi_object *device_enum; | ||
1171 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1157 | 1172 | ||
1158 | if (sony_find_snc_handle(0x124) == -1) | 1173 | offset = sony_find_snc_handle(0x124); |
1159 | return -1; | 1174 | if (offset == -1) { |
1175 | offset = sony_find_snc_handle(0x135); | ||
1176 | if (offset == -1) | ||
1177 | return; | ||
1178 | else | ||
1179 | sony_rfkill_handle = 0x135; | ||
1180 | } else | ||
1181 | sony_rfkill_handle = 0x124; | ||
1182 | dprintk("Found rkfill handle: 0x%.4x\n", sony_rfkill_handle); | ||
1160 | 1183 | ||
1161 | ret = sony_call_snc_handle(0x124, 0xb00, &result); | 1184 | /* need to read the whole buffer returned by the acpi call to SN06 |
1162 | if (ret) { | 1185 | * here otherwise we may miss some features |
1163 | printk(KERN_INFO DRV_PFX | 1186 | */ |
1164 | "Unable to enumerate rfkill devices: %x\n", ret); | 1187 | params.count = 1; |
1165 | return ret; | 1188 | params.pointer = &in_obj; |
1189 | in_obj.type = ACPI_TYPE_INTEGER; | ||
1190 | in_obj.integer.value = offset; | ||
1191 | status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms, | ||
1192 | &buffer); | ||
1193 | if (ACPI_FAILURE(status)) { | ||
1194 | dprintk("Radio device enumeration failed\n"); | ||
1195 | return; | ||
1196 | } | ||
1197 | |||
1198 | device_enum = (union acpi_object *) buffer.pointer; | ||
1199 | if (!device_enum || device_enum->type != ACPI_TYPE_BUFFER) { | ||
1200 | printk(KERN_ERR "Invalid SN06 return object 0x%.2x\n", | ||
1201 | device_enum->type); | ||
1202 | goto out_no_enum; | ||
1166 | } | 1203 | } |
1167 | 1204 | ||
1168 | if (result & 0x1) | 1205 | /* the buffer is filled with magic numbers describing the devices |
1169 | sony_nc_setup_rfkill(device, SONY_WIFI); | 1206 | * available, 0xff terminates the enumeration |
1170 | if (result & 0x2) | 1207 | */ |
1171 | sony_nc_setup_rfkill(device, SONY_BLUETOOTH); | 1208 | for (i = 0; i < device_enum->buffer.length; i++) { |
1172 | if (result & 0x1c) | ||
1173 | sony_nc_setup_rfkill(device, SONY_WWAN); | ||
1174 | if (result & 0x20) | ||
1175 | sony_nc_setup_rfkill(device, SONY_WIMAX); | ||
1176 | 1209 | ||
1177 | return 0; | 1210 | dev_code = *(device_enum->buffer.pointer + i); |
1211 | if (dev_code == 0xff) | ||
1212 | break; | ||
1213 | |||
1214 | dprintk("Radio devices, looking at 0x%.2x\n", dev_code); | ||
1215 | |||
1216 | if (dev_code == 0 && !sony_rfkill_devices[SONY_WIFI]) | ||
1217 | sony_nc_setup_rfkill(device, SONY_WIFI); | ||
1218 | |||
1219 | if (dev_code == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH]) | ||
1220 | sony_nc_setup_rfkill(device, SONY_BLUETOOTH); | ||
1221 | |||
1222 | if ((0xf0 & dev_code) == 0x20 && | ||
1223 | !sony_rfkill_devices[SONY_WWAN]) | ||
1224 | sony_nc_setup_rfkill(device, SONY_WWAN); | ||
1225 | |||
1226 | if (dev_code == 0x30 && !sony_rfkill_devices[SONY_WIMAX]) | ||
1227 | sony_nc_setup_rfkill(device, SONY_WIMAX); | ||
1228 | } | ||
1229 | |||
1230 | out_no_enum: | ||
1231 | kfree(buffer.pointer); | ||
1232 | return; | ||
1178 | } | 1233 | } |
1179 | 1234 | ||
1180 | static int sony_nc_add(struct acpi_device *device) | 1235 | static int sony_nc_add(struct acpi_device *device) |
@@ -1203,7 +1258,7 @@ static int sony_nc_add(struct acpi_device *device) | |||
1203 | 1258 | ||
1204 | if (debug) { | 1259 | if (debug) { |
1205 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle, | 1260 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle, |
1206 | 1, sony_walk_callback, NULL, NULL); | 1261 | 1, sony_walk_callback, NULL, NULL, NULL); |
1207 | if (ACPI_FAILURE(status)) { | 1262 | if (ACPI_FAILURE(status)) { |
1208 | printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n"); | 1263 | printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n"); |
1209 | result = -ENODEV; | 1264 | result = -ENODEV; |
@@ -1237,9 +1292,13 @@ static int sony_nc_add(struct acpi_device *device) | |||
1237 | "controlled by ACPI video driver\n"); | 1292 | "controlled by ACPI video driver\n"); |
1238 | } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", | 1293 | } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", |
1239 | &handle))) { | 1294 | &handle))) { |
1295 | struct backlight_properties props; | ||
1296 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1297 | props.max_brightness = SONY_MAX_BRIGHTNESS - 1; | ||
1240 | sony_backlight_device = backlight_device_register("sony", NULL, | 1298 | sony_backlight_device = backlight_device_register("sony", NULL, |
1241 | NULL, | 1299 | NULL, |
1242 | &sony_backlight_ops); | 1300 | &sony_backlight_ops, |
1301 | &props); | ||
1243 | 1302 | ||
1244 | if (IS_ERR(sony_backlight_device)) { | 1303 | if (IS_ERR(sony_backlight_device)) { |
1245 | printk(KERN_WARNING DRV_PFX "unable to register backlight device\n"); | 1304 | printk(KERN_WARNING DRV_PFX "unable to register backlight device\n"); |
@@ -1248,8 +1307,6 @@ static int sony_nc_add(struct acpi_device *device) | |||
1248 | sony_backlight_device->props.brightness = | 1307 | sony_backlight_device->props.brightness = |
1249 | sony_backlight_get_brightness | 1308 | sony_backlight_get_brightness |
1250 | (sony_backlight_device); | 1309 | (sony_backlight_device); |
1251 | sony_backlight_device->props.max_brightness = | ||
1252 | SONY_MAX_BRIGHTNESS - 1; | ||
1253 | } | 1310 | } |
1254 | 1311 | ||
1255 | } | 1312 | } |
@@ -2079,7 +2136,7 @@ static struct attribute_group spic_attribute_group = { | |||
2079 | 2136 | ||
2080 | struct sonypi_compat_s { | 2137 | struct sonypi_compat_s { |
2081 | struct fasync_struct *fifo_async; | 2138 | struct fasync_struct *fifo_async; |
2082 | struct kfifo *fifo; | 2139 | struct kfifo fifo; |
2083 | spinlock_t fifo_lock; | 2140 | spinlock_t fifo_lock; |
2084 | wait_queue_head_t fifo_proc_list; | 2141 | wait_queue_head_t fifo_proc_list; |
2085 | atomic_t open_count; | 2142 | atomic_t open_count; |
@@ -2104,12 +2161,12 @@ static int sonypi_misc_open(struct inode *inode, struct file *file) | |||
2104 | /* Flush input queue on first open */ | 2161 | /* Flush input queue on first open */ |
2105 | unsigned long flags; | 2162 | unsigned long flags; |
2106 | 2163 | ||
2107 | spin_lock_irqsave(sonypi_compat.fifo->lock, flags); | 2164 | spin_lock_irqsave(&sonypi_compat.fifo_lock, flags); |
2108 | 2165 | ||
2109 | if (atomic_inc_return(&sonypi_compat.open_count) == 1) | 2166 | if (atomic_inc_return(&sonypi_compat.open_count) == 1) |
2110 | __kfifo_reset(sonypi_compat.fifo); | 2167 | kfifo_reset(&sonypi_compat.fifo); |
2111 | 2168 | ||
2112 | spin_unlock_irqrestore(sonypi_compat.fifo->lock, flags); | 2169 | spin_unlock_irqrestore(&sonypi_compat.fifo_lock, flags); |
2113 | 2170 | ||
2114 | return 0; | 2171 | return 0; |
2115 | } | 2172 | } |
@@ -2120,17 +2177,18 @@ static ssize_t sonypi_misc_read(struct file *file, char __user *buf, | |||
2120 | ssize_t ret; | 2177 | ssize_t ret; |
2121 | unsigned char c; | 2178 | unsigned char c; |
2122 | 2179 | ||
2123 | if ((kfifo_len(sonypi_compat.fifo) == 0) && | 2180 | if ((kfifo_len(&sonypi_compat.fifo) == 0) && |
2124 | (file->f_flags & O_NONBLOCK)) | 2181 | (file->f_flags & O_NONBLOCK)) |
2125 | return -EAGAIN; | 2182 | return -EAGAIN; |
2126 | 2183 | ||
2127 | ret = wait_event_interruptible(sonypi_compat.fifo_proc_list, | 2184 | ret = wait_event_interruptible(sonypi_compat.fifo_proc_list, |
2128 | kfifo_len(sonypi_compat.fifo) != 0); | 2185 | kfifo_len(&sonypi_compat.fifo) != 0); |
2129 | if (ret) | 2186 | if (ret) |
2130 | return ret; | 2187 | return ret; |
2131 | 2188 | ||
2132 | while (ret < count && | 2189 | while (ret < count && |
2133 | (kfifo_get(sonypi_compat.fifo, &c, sizeof(c)) == sizeof(c))) { | 2190 | (kfifo_out_locked(&sonypi_compat.fifo, &c, sizeof(c), |
2191 | &sonypi_compat.fifo_lock) == sizeof(c))) { | ||
2134 | if (put_user(c, buf++)) | 2192 | if (put_user(c, buf++)) |
2135 | return -EFAULT; | 2193 | return -EFAULT; |
2136 | ret++; | 2194 | ret++; |
@@ -2147,7 +2205,7 @@ static ssize_t sonypi_misc_read(struct file *file, char __user *buf, | |||
2147 | static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait) | 2205 | static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait) |
2148 | { | 2206 | { |
2149 | poll_wait(file, &sonypi_compat.fifo_proc_list, wait); | 2207 | poll_wait(file, &sonypi_compat.fifo_proc_list, wait); |
2150 | if (kfifo_len(sonypi_compat.fifo)) | 2208 | if (kfifo_len(&sonypi_compat.fifo)) |
2151 | return POLLIN | POLLRDNORM; | 2209 | return POLLIN | POLLRDNORM; |
2152 | return 0; | 2210 | return 0; |
2153 | } | 2211 | } |
@@ -2309,7 +2367,8 @@ static struct miscdevice sonypi_misc_device = { | |||
2309 | 2367 | ||
2310 | static void sonypi_compat_report_event(u8 event) | 2368 | static void sonypi_compat_report_event(u8 event) |
2311 | { | 2369 | { |
2312 | kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event)); | 2370 | kfifo_in_locked(&sonypi_compat.fifo, (unsigned char *)&event, |
2371 | sizeof(event), &sonypi_compat.fifo_lock); | ||
2313 | kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN); | 2372 | kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN); |
2314 | wake_up_interruptible(&sonypi_compat.fifo_proc_list); | 2373 | wake_up_interruptible(&sonypi_compat.fifo_proc_list); |
2315 | } | 2374 | } |
@@ -2319,11 +2378,11 @@ static int sonypi_compat_init(void) | |||
2319 | int error; | 2378 | int error; |
2320 | 2379 | ||
2321 | spin_lock_init(&sonypi_compat.fifo_lock); | 2380 | spin_lock_init(&sonypi_compat.fifo_lock); |
2322 | sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL, | 2381 | error = |
2323 | &sonypi_compat.fifo_lock); | 2382 | kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); |
2324 | if (IS_ERR(sonypi_compat.fifo)) { | 2383 | if (error) { |
2325 | printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); | 2384 | printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); |
2326 | return PTR_ERR(sonypi_compat.fifo); | 2385 | return error; |
2327 | } | 2386 | } |
2328 | 2387 | ||
2329 | init_waitqueue_head(&sonypi_compat.fifo_proc_list); | 2388 | init_waitqueue_head(&sonypi_compat.fifo_proc_list); |
@@ -2342,14 +2401,14 @@ static int sonypi_compat_init(void) | |||
2342 | return 0; | 2401 | return 0; |
2343 | 2402 | ||
2344 | err_free_kfifo: | 2403 | err_free_kfifo: |
2345 | kfifo_free(sonypi_compat.fifo); | 2404 | kfifo_free(&sonypi_compat.fifo); |
2346 | return error; | 2405 | return error; |
2347 | } | 2406 | } |
2348 | 2407 | ||
2349 | static void sonypi_compat_exit(void) | 2408 | static void sonypi_compat_exit(void) |
2350 | { | 2409 | { |
2351 | misc_deregister(&sonypi_misc_device); | 2410 | misc_deregister(&sonypi_misc_device); |
2352 | kfifo_free(sonypi_compat.fifo); | 2411 | kfifo_free(&sonypi_compat.fifo); |
2353 | } | 2412 | } |
2354 | #else | 2413 | #else |
2355 | static int sonypi_compat_init(void) { return 0; } | 2414 | static int sonypi_compat_init(void) { return 0; } |
diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c index 44166003d4ef..1fe0f1feff71 100644 --- a/drivers/platform/x86/tc1100-wmi.c +++ b/drivers/platform/x86/tc1100-wmi.c | |||
@@ -27,6 +27,7 @@ | |||
27 | 27 | ||
28 | #include <linux/kernel.h> | 28 | #include <linux/kernel.h> |
29 | #include <linux/module.h> | 29 | #include <linux/module.h> |
30 | #include <linux/slab.h> | ||
30 | #include <linux/init.h> | 31 | #include <linux/init.h> |
31 | #include <linux/types.h> | 32 | #include <linux/types.h> |
32 | #include <acpi/acpi.h> | 33 | #include <acpi/acpi.h> |
@@ -47,22 +48,6 @@ MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras"); | |||
47 | MODULE_LICENSE("GPL"); | 48 | MODULE_LICENSE("GPL"); |
48 | MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505"); | 49 | MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505"); |
49 | 50 | ||
50 | static int tc1100_probe(struct platform_device *device); | ||
51 | static int tc1100_remove(struct platform_device *device); | ||
52 | static int tc1100_suspend(struct platform_device *device, pm_message_t state); | ||
53 | static int tc1100_resume(struct platform_device *device); | ||
54 | |||
55 | static struct platform_driver tc1100_driver = { | ||
56 | .driver = { | ||
57 | .name = "tc1100-wmi", | ||
58 | .owner = THIS_MODULE, | ||
59 | }, | ||
60 | .probe = tc1100_probe, | ||
61 | .remove = tc1100_remove, | ||
62 | .suspend = tc1100_suspend, | ||
63 | .resume = tc1100_resume, | ||
64 | }; | ||
65 | |||
66 | static struct platform_device *tc1100_device; | 51 | static struct platform_device *tc1100_device; |
67 | 52 | ||
68 | struct tc1100_data { | 53 | struct tc1100_data { |
@@ -183,51 +168,35 @@ static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \ | |||
183 | show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); | 168 | show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); |
184 | show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL); | 169 | show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL); |
185 | 170 | ||
186 | static void remove_fs(void) | 171 | static struct attribute *tc1100_attributes[] = { |
187 | { | 172 | &dev_attr_wireless.attr, |
188 | device_remove_file(&tc1100_device->dev, &dev_attr_wireless); | 173 | &dev_attr_jogdial.attr, |
189 | device_remove_file(&tc1100_device->dev, &dev_attr_jogdial); | 174 | NULL |
190 | } | 175 | }; |
191 | |||
192 | static int add_fs(void) | ||
193 | { | ||
194 | int ret; | ||
195 | |||
196 | ret = device_create_file(&tc1100_device->dev, &dev_attr_wireless); | ||
197 | if (ret) | ||
198 | goto add_sysfs_error; | ||
199 | |||
200 | ret = device_create_file(&tc1100_device->dev, &dev_attr_jogdial); | ||
201 | if (ret) | ||
202 | goto add_sysfs_error; | ||
203 | |||
204 | return ret; | ||
205 | 176 | ||
206 | add_sysfs_error: | 177 | static struct attribute_group tc1100_attribute_group = { |
207 | remove_fs(); | 178 | .attrs = tc1100_attributes, |
208 | return ret; | 179 | }; |
209 | } | ||
210 | 180 | ||
211 | /* -------------------------------------------------------------------------- | 181 | /* -------------------------------------------------------------------------- |
212 | Driver Model | 182 | Driver Model |
213 | -------------------------------------------------------------------------- */ | 183 | -------------------------------------------------------------------------- */ |
214 | 184 | ||
215 | static int tc1100_probe(struct platform_device *device) | 185 | static int __init tc1100_probe(struct platform_device *device) |
216 | { | 186 | { |
217 | int result = 0; | 187 | return sysfs_create_group(&device->dev.kobj, &tc1100_attribute_group); |
218 | |||
219 | result = add_fs(); | ||
220 | return result; | ||
221 | } | 188 | } |
222 | 189 | ||
223 | 190 | ||
224 | static int tc1100_remove(struct platform_device *device) | 191 | static int __devexit tc1100_remove(struct platform_device *device) |
225 | { | 192 | { |
226 | remove_fs(); | 193 | sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); |
194 | |||
227 | return 0; | 195 | return 0; |
228 | } | 196 | } |
229 | 197 | ||
230 | static int tc1100_suspend(struct platform_device *dev, pm_message_t state) | 198 | #ifdef CONFIG_PM |
199 | static int tc1100_suspend(struct device *dev) | ||
231 | { | 200 | { |
232 | int ret; | 201 | int ret; |
233 | 202 | ||
@@ -239,10 +208,10 @@ static int tc1100_suspend(struct platform_device *dev, pm_message_t state) | |||
239 | if (ret) | 208 | if (ret) |
240 | return ret; | 209 | return ret; |
241 | 210 | ||
242 | return ret; | 211 | return 0; |
243 | } | 212 | } |
244 | 213 | ||
245 | static int tc1100_resume(struct platform_device *dev) | 214 | static int tc1100_resume(struct device *dev) |
246 | { | 215 | { |
247 | int ret; | 216 | int ret; |
248 | 217 | ||
@@ -254,34 +223,61 @@ static int tc1100_resume(struct platform_device *dev) | |||
254 | if (ret) | 223 | if (ret) |
255 | return ret; | 224 | return ret; |
256 | 225 | ||
257 | return ret; | 226 | return 0; |
258 | } | 227 | } |
259 | 228 | ||
229 | static const struct dev_pm_ops tc1100_pm_ops = { | ||
230 | .suspend = tc1100_suspend, | ||
231 | .resume = tc1100_resume, | ||
232 | .freeze = tc1100_suspend, | ||
233 | .restore = tc1100_resume, | ||
234 | }; | ||
235 | #endif | ||
236 | |||
237 | static struct platform_driver tc1100_driver = { | ||
238 | .driver = { | ||
239 | .name = "tc1100-wmi", | ||
240 | .owner = THIS_MODULE, | ||
241 | #ifdef CONFIG_PM | ||
242 | .pm = &tc1100_pm_ops, | ||
243 | #endif | ||
244 | }, | ||
245 | .remove = __devexit_p(tc1100_remove), | ||
246 | }; | ||
247 | |||
260 | static int __init tc1100_init(void) | 248 | static int __init tc1100_init(void) |
261 | { | 249 | { |
262 | int result = 0; | 250 | int error; |
263 | 251 | ||
264 | if (!wmi_has_guid(GUID)) | 252 | if (!wmi_has_guid(GUID)) |
265 | return -ENODEV; | 253 | return -ENODEV; |
266 | 254 | ||
267 | result = platform_driver_register(&tc1100_driver); | ||
268 | if (result) | ||
269 | return result; | ||
270 | |||
271 | tc1100_device = platform_device_alloc("tc1100-wmi", -1); | 255 | tc1100_device = platform_device_alloc("tc1100-wmi", -1); |
272 | platform_device_add(tc1100_device); | 256 | if (!tc1100_device) |
257 | return -ENOMEM; | ||
258 | |||
259 | error = platform_device_add(tc1100_device); | ||
260 | if (error) | ||
261 | goto err_device_put; | ||
262 | |||
263 | error = platform_driver_probe(&tc1100_driver, tc1100_probe); | ||
264 | if (error) | ||
265 | goto err_device_del; | ||
273 | 266 | ||
274 | printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras loaded\n"); | 267 | printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras loaded\n"); |
268 | return 0; | ||
275 | 269 | ||
276 | return result; | 270 | err_device_del: |
271 | platform_device_del(tc1100_device); | ||
272 | err_device_put: | ||
273 | platform_device_put(tc1100_device); | ||
274 | return error; | ||
277 | } | 275 | } |
278 | 276 | ||
279 | static void __exit tc1100_exit(void) | 277 | static void __exit tc1100_exit(void) |
280 | { | 278 | { |
281 | platform_device_del(tc1100_device); | 279 | platform_device_unregister(tc1100_device); |
282 | platform_driver_unregister(&tc1100_driver); | 280 | platform_driver_unregister(&tc1100_driver); |
283 | |||
284 | printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras unloaded\n"); | ||
285 | } | 281 | } |
286 | 282 | ||
287 | module_init(tc1100_init); | 283 | module_init(tc1100_init); |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index a848c7e20aeb..63290b33c879 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -21,8 +21,8 @@ | |||
21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define TPACPI_VERSION "0.23" | 24 | #define TPACPI_VERSION "0.24" |
25 | #define TPACPI_SYSFS_VERSION 0x020500 | 25 | #define TPACPI_SYSFS_VERSION 0x020700 |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * Changelog: | 28 | * Changelog: |
@@ -58,9 +58,11 @@ | |||
58 | #include <linux/kthread.h> | 58 | #include <linux/kthread.h> |
59 | #include <linux/freezer.h> | 59 | #include <linux/freezer.h> |
60 | #include <linux/delay.h> | 60 | #include <linux/delay.h> |
61 | #include <linux/slab.h> | ||
61 | 62 | ||
62 | #include <linux/nvram.h> | 63 | #include <linux/nvram.h> |
63 | #include <linux/proc_fs.h> | 64 | #include <linux/proc_fs.h> |
65 | #include <linux/seq_file.h> | ||
64 | #include <linux/sysfs.h> | 66 | #include <linux/sysfs.h> |
65 | #include <linux/backlight.h> | 67 | #include <linux/backlight.h> |
66 | #include <linux/fb.h> | 68 | #include <linux/fb.h> |
@@ -76,6 +78,10 @@ | |||
76 | #include <linux/jiffies.h> | 78 | #include <linux/jiffies.h> |
77 | #include <linux/workqueue.h> | 79 | #include <linux/workqueue.h> |
78 | 80 | ||
81 | #include <sound/core.h> | ||
82 | #include <sound/control.h> | ||
83 | #include <sound/initval.h> | ||
84 | |||
79 | #include <acpi/acpi_drivers.h> | 85 | #include <acpi/acpi_drivers.h> |
80 | 86 | ||
81 | #include <linux/pci_ids.h> | 87 | #include <linux/pci_ids.h> |
@@ -231,6 +237,7 @@ enum tpacpi_hkey_event_t { | |||
231 | #define TPACPI_DBG_HKEY 0x0008 | 237 | #define TPACPI_DBG_HKEY 0x0008 |
232 | #define TPACPI_DBG_FAN 0x0010 | 238 | #define TPACPI_DBG_FAN 0x0010 |
233 | #define TPACPI_DBG_BRGHT 0x0020 | 239 | #define TPACPI_DBG_BRGHT 0x0020 |
240 | #define TPACPI_DBG_MIXER 0x0040 | ||
234 | 241 | ||
235 | #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") | 242 | #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") |
236 | #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") | 243 | #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") |
@@ -256,7 +263,7 @@ struct tp_acpi_drv_struct { | |||
256 | struct ibm_struct { | 263 | struct ibm_struct { |
257 | char *name; | 264 | char *name; |
258 | 265 | ||
259 | int (*read) (char *); | 266 | int (*read) (struct seq_file *); |
260 | int (*write) (char *); | 267 | int (*write) (char *); |
261 | void (*exit) (void); | 268 | void (*exit) (void); |
262 | void (*resume) (void); | 269 | void (*resume) (void); |
@@ -280,6 +287,7 @@ struct ibm_init_struct { | |||
280 | char param[32]; | 287 | char param[32]; |
281 | 288 | ||
282 | int (*init) (struct ibm_init_struct *); | 289 | int (*init) (struct ibm_init_struct *); |
290 | mode_t base_procfs_mode; | ||
283 | struct ibm_struct *data; | 291 | struct ibm_struct *data; |
284 | }; | 292 | }; |
285 | 293 | ||
@@ -298,6 +306,7 @@ static struct { | |||
298 | u32 fan_ctrl_status_undef:1; | 306 | u32 fan_ctrl_status_undef:1; |
299 | u32 second_fan:1; | 307 | u32 second_fan:1; |
300 | u32 beep_needs_two_args:1; | 308 | u32 beep_needs_two_args:1; |
309 | u32 mixer_no_level_control:1; | ||
301 | u32 input_device_registered:1; | 310 | u32 input_device_registered:1; |
302 | u32 platform_drv_registered:1; | 311 | u32 platform_drv_registered:1; |
303 | u32 platform_drv_attrs_registered:1; | 312 | u32 platform_drv_attrs_registered:1; |
@@ -309,6 +318,7 @@ static struct { | |||
309 | 318 | ||
310 | static struct { | 319 | static struct { |
311 | u16 hotkey_mask_ff:1; | 320 | u16 hotkey_mask_ff:1; |
321 | u16 volume_ctrl_forbidden:1; | ||
312 | } tp_warned; | 322 | } tp_warned; |
313 | 323 | ||
314 | struct thinkpad_id_data { | 324 | struct thinkpad_id_data { |
@@ -425,6 +435,12 @@ static void tpacpi_log_usertask(const char * const what) | |||
425 | .ec = TPACPI_MATCH_ANY, \ | 435 | .ec = TPACPI_MATCH_ANY, \ |
426 | .quirks = (__quirk) } | 436 | .quirks = (__quirk) } |
427 | 437 | ||
438 | #define TPACPI_QEC_LNV(__id1, __id2, __quirk) \ | ||
439 | { .vendor = PCI_VENDOR_ID_LENOVO, \ | ||
440 | .bios = TPACPI_MATCH_ANY, \ | ||
441 | .ec = TPID(__id1, __id2), \ | ||
442 | .quirks = (__quirk) } | ||
443 | |||
428 | struct tpacpi_quirk { | 444 | struct tpacpi_quirk { |
429 | unsigned int vendor; | 445 | unsigned int vendor; |
430 | u16 bios; | 446 | u16 bios; |
@@ -776,36 +792,25 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) | |||
776 | **************************************************************************** | 792 | **************************************************************************** |
777 | ****************************************************************************/ | 793 | ****************************************************************************/ |
778 | 794 | ||
779 | static int dispatch_procfs_read(char *page, char **start, off_t off, | 795 | static int dispatch_proc_show(struct seq_file *m, void *v) |
780 | int count, int *eof, void *data) | ||
781 | { | 796 | { |
782 | struct ibm_struct *ibm = data; | 797 | struct ibm_struct *ibm = m->private; |
783 | int len; | ||
784 | 798 | ||
785 | if (!ibm || !ibm->read) | 799 | if (!ibm || !ibm->read) |
786 | return -EINVAL; | 800 | return -EINVAL; |
801 | return ibm->read(m); | ||
802 | } | ||
787 | 803 | ||
788 | len = ibm->read(page); | 804 | static int dispatch_proc_open(struct inode *inode, struct file *file) |
789 | if (len < 0) | 805 | { |
790 | return len; | 806 | return single_open(file, dispatch_proc_show, PDE(inode)->data); |
791 | |||
792 | if (len <= off + count) | ||
793 | *eof = 1; | ||
794 | *start = page + off; | ||
795 | len -= off; | ||
796 | if (len > count) | ||
797 | len = count; | ||
798 | if (len < 0) | ||
799 | len = 0; | ||
800 | |||
801 | return len; | ||
802 | } | 807 | } |
803 | 808 | ||
804 | static int dispatch_procfs_write(struct file *file, | 809 | static ssize_t dispatch_proc_write(struct file *file, |
805 | const char __user *userbuf, | 810 | const char __user *userbuf, |
806 | unsigned long count, void *data) | 811 | size_t count, loff_t *pos) |
807 | { | 812 | { |
808 | struct ibm_struct *ibm = data; | 813 | struct ibm_struct *ibm = PDE(file->f_path.dentry->d_inode)->data; |
809 | char *kernbuf; | 814 | char *kernbuf; |
810 | int ret; | 815 | int ret; |
811 | 816 | ||
@@ -834,6 +839,15 @@ static int dispatch_procfs_write(struct file *file, | |||
834 | return ret; | 839 | return ret; |
835 | } | 840 | } |
836 | 841 | ||
842 | static const struct file_operations dispatch_proc_fops = { | ||
843 | .owner = THIS_MODULE, | ||
844 | .open = dispatch_proc_open, | ||
845 | .read = seq_read, | ||
846 | .llseek = seq_lseek, | ||
847 | .release = single_release, | ||
848 | .write = dispatch_proc_write, | ||
849 | }; | ||
850 | |||
837 | static char *next_cmd(char **cmds) | 851 | static char *next_cmd(char **cmds) |
838 | { | 852 | { |
839 | char *start = *cmds; | 853 | char *start = *cmds; |
@@ -1006,11 +1020,8 @@ static int parse_strtoul(const char *buf, | |||
1006 | { | 1020 | { |
1007 | char *endp; | 1021 | char *endp; |
1008 | 1022 | ||
1009 | while (*buf && isspace(*buf)) | 1023 | *value = simple_strtoul(skip_spaces(buf), &endp, 0); |
1010 | buf++; | 1024 | endp = skip_spaces(endp); |
1011 | *value = simple_strtoul(buf, &endp, 0); | ||
1012 | while (*endp && isspace(*endp)) | ||
1013 | endp++; | ||
1014 | if (*endp || *value > max) | 1025 | if (*endp || *value > max) |
1015 | return -EINVAL; | 1026 | return -EINVAL; |
1016 | 1027 | ||
@@ -1087,7 +1098,7 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) | |||
1087 | */ | 1098 | */ |
1088 | 1099 | ||
1089 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, | 1100 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, |
1090 | tpacpi_acpi_walk_find_bcl, NULL, | 1101 | tpacpi_acpi_walk_find_bcl, NULL, NULL, |
1091 | &bcl_ptr); | 1102 | &bcl_ptr); |
1092 | 1103 | ||
1093 | if (ACPI_SUCCESS(status) && bcl_levels > 2) { | 1104 | if (ACPI_SUCCESS(status) && bcl_levels > 2) { |
@@ -1264,6 +1275,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1264 | struct tpacpi_rfk *atp_rfk; | 1275 | struct tpacpi_rfk *atp_rfk; |
1265 | int res; | 1276 | int res; |
1266 | bool sw_state = false; | 1277 | bool sw_state = false; |
1278 | bool hw_state; | ||
1267 | int sw_status; | 1279 | int sw_status; |
1268 | 1280 | ||
1269 | BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); | 1281 | BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); |
@@ -1298,7 +1310,8 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1298 | rfkill_init_sw_state(atp_rfk->rfkill, sw_state); | 1310 | rfkill_init_sw_state(atp_rfk->rfkill, sw_state); |
1299 | } | 1311 | } |
1300 | } | 1312 | } |
1301 | rfkill_set_hw_state(atp_rfk->rfkill, tpacpi_rfk_check_hwblock_state()); | 1313 | hw_state = tpacpi_rfk_check_hwblock_state(); |
1314 | rfkill_set_hw_state(atp_rfk->rfkill, hw_state); | ||
1302 | 1315 | ||
1303 | res = rfkill_register(atp_rfk->rfkill); | 1316 | res = rfkill_register(atp_rfk->rfkill); |
1304 | if (res < 0) { | 1317 | if (res < 0) { |
@@ -1311,6 +1324,9 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1311 | } | 1324 | } |
1312 | 1325 | ||
1313 | tpacpi_rfkill_switches[id] = atp_rfk; | 1326 | tpacpi_rfkill_switches[id] = atp_rfk; |
1327 | |||
1328 | printk(TPACPI_INFO "rfkill switch %s: radio is %sblocked\n", | ||
1329 | name, (sw_state || hw_state) ? "" : "un"); | ||
1314 | return 0; | 1330 | return 0; |
1315 | } | 1331 | } |
1316 | 1332 | ||
@@ -1383,12 +1399,10 @@ static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id, | |||
1383 | } | 1399 | } |
1384 | 1400 | ||
1385 | /* procfs -------------------------------------------------------------- */ | 1401 | /* procfs -------------------------------------------------------------- */ |
1386 | static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) | 1402 | static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file *m) |
1387 | { | 1403 | { |
1388 | int len = 0; | ||
1389 | |||
1390 | if (id >= TPACPI_RFK_SW_MAX) | 1404 | if (id >= TPACPI_RFK_SW_MAX) |
1391 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 1405 | seq_printf(m, "status:\t\tnot supported\n"); |
1392 | else { | 1406 | else { |
1393 | int status; | 1407 | int status; |
1394 | 1408 | ||
@@ -1402,13 +1416,13 @@ static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) | |||
1402 | return status; | 1416 | return status; |
1403 | } | 1417 | } |
1404 | 1418 | ||
1405 | len += sprintf(p + len, "status:\t\t%s\n", | 1419 | seq_printf(m, "status:\t\t%s\n", |
1406 | (status == TPACPI_RFK_RADIO_ON) ? | 1420 | (status == TPACPI_RFK_RADIO_ON) ? |
1407 | "enabled" : "disabled"); | 1421 | "enabled" : "disabled"); |
1408 | len += sprintf(p + len, "commands:\tenable, disable\n"); | 1422 | seq_printf(m, "commands:\tenable, disable\n"); |
1409 | } | 1423 | } |
1410 | 1424 | ||
1411 | return len; | 1425 | return 0; |
1412 | } | 1426 | } |
1413 | 1427 | ||
1414 | static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) | 1428 | static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) |
@@ -1655,7 +1669,7 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv) | |||
1655 | * Table of recommended minimum BIOS versions | 1669 | * Table of recommended minimum BIOS versions |
1656 | * | 1670 | * |
1657 | * Reasons for listing: | 1671 | * Reasons for listing: |
1658 | * 1. Stable BIOS, listed because the unknown ammount of | 1672 | * 1. Stable BIOS, listed because the unknown amount of |
1659 | * bugs and bad ACPI behaviour on older versions | 1673 | * bugs and bad ACPI behaviour on older versions |
1660 | * | 1674 | * |
1661 | * 2. BIOS or EC fw with known bugs that trigger on Linux | 1675 | * 2. BIOS or EC fw with known bugs that trigger on Linux |
@@ -1779,7 +1793,7 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = { | |||
1779 | 1793 | ||
1780 | TPV_QL1('7', '9', 'E', '3', '5', '0'), /* T60/p */ | 1794 | TPV_QL1('7', '9', 'E', '3', '5', '0'), /* T60/p */ |
1781 | TPV_QL1('7', 'C', 'D', '2', '2', '2'), /* R60, R60i */ | 1795 | TPV_QL1('7', 'C', 'D', '2', '2', '2'), /* R60, R60i */ |
1782 | TPV_QL0('7', 'E', 'D', '0'), /* R60e, R60i */ | 1796 | TPV_QL1('7', 'E', 'D', '0', '1', '5'), /* R60e, R60i */ |
1783 | 1797 | ||
1784 | /* BIOS FW BIOS VERS EC FW EC VERS */ | 1798 | /* BIOS FW BIOS VERS EC FW EC VERS */ |
1785 | TPV_QI2('1', 'W', '9', '0', '1', 'V', '2', '8'), /* R50e (1) */ | 1799 | TPV_QI2('1', 'W', '9', '0', '1', 'V', '2', '8'), /* R50e (1) */ |
@@ -1795,8 +1809,8 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = { | |||
1795 | TPV_QI1('7', '4', '6', '4', '2', '7'), /* X41 (0) */ | 1809 | TPV_QI1('7', '4', '6', '4', '2', '7'), /* X41 (0) */ |
1796 | TPV_QI1('7', '5', '6', '0', '2', '0'), /* X41t (0) */ | 1810 | TPV_QI1('7', '5', '6', '0', '2', '0'), /* X41t (0) */ |
1797 | 1811 | ||
1798 | TPV_QL0('7', 'B', 'D', '7'), /* X60/s */ | 1812 | TPV_QL1('7', 'B', 'D', '7', '4', '0'), /* X60/s */ |
1799 | TPV_QL0('7', 'J', '3', '0'), /* X60t */ | 1813 | TPV_QL1('7', 'J', '3', '0', '1', '3'), /* X60t */ |
1800 | 1814 | ||
1801 | /* (0) - older versions lack DMI EC fw string and functionality */ | 1815 | /* (0) - older versions lack DMI EC fw string and functionality */ |
1802 | /* (1) - older versions known to lack functionality */ | 1816 | /* (1) - older versions known to lack functionality */ |
@@ -1886,14 +1900,11 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) | |||
1886 | return 0; | 1900 | return 0; |
1887 | } | 1901 | } |
1888 | 1902 | ||
1889 | static int thinkpad_acpi_driver_read(char *p) | 1903 | static int thinkpad_acpi_driver_read(struct seq_file *m) |
1890 | { | 1904 | { |
1891 | int len = 0; | 1905 | seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC); |
1892 | 1906 | seq_printf(m, "version:\t%s\n", TPACPI_VERSION); | |
1893 | len += sprintf(p + len, "driver:\t\t%s\n", TPACPI_DESC); | 1907 | return 0; |
1894 | len += sprintf(p + len, "version:\t%s\n", TPACPI_VERSION); | ||
1895 | |||
1896 | return len; | ||
1897 | } | 1908 | } |
1898 | 1909 | ||
1899 | static struct ibm_struct thinkpad_acpi_driver_data = { | 1910 | static struct ibm_struct thinkpad_acpi_driver_data = { |
@@ -2073,6 +2084,7 @@ static struct attribute_set *hotkey_dev_attributes; | |||
2073 | 2084 | ||
2074 | static void tpacpi_driver_event(const unsigned int hkey_event); | 2085 | static void tpacpi_driver_event(const unsigned int hkey_event); |
2075 | static void hotkey_driver_event(const unsigned int scancode); | 2086 | static void hotkey_driver_event(const unsigned int scancode); |
2087 | static void hotkey_poll_setup(const bool may_warn); | ||
2076 | 2088 | ||
2077 | /* HKEY.MHKG() return bits */ | 2089 | /* HKEY.MHKG() return bits */ |
2078 | #define TP_HOTKEY_TABLET_MASK (1 << 3) | 2090 | #define TP_HOTKEY_TABLET_MASK (1 << 3) |
@@ -2189,7 +2201,8 @@ static int hotkey_mask_set(u32 mask) | |||
2189 | fwmask, hotkey_acpi_mask); | 2201 | fwmask, hotkey_acpi_mask); |
2190 | } | 2202 | } |
2191 | 2203 | ||
2192 | hotkey_mask_warn_incomplete_mask(); | 2204 | if (tpacpi_lifecycle != TPACPI_LIFE_EXITING) |
2205 | hotkey_mask_warn_incomplete_mask(); | ||
2193 | 2206 | ||
2194 | return rc; | 2207 | return rc; |
2195 | } | 2208 | } |
@@ -2254,6 +2267,8 @@ static int tpacpi_hotkey_driver_mask_set(const u32 mask) | |||
2254 | 2267 | ||
2255 | rc = hotkey_mask_set((hotkey_acpi_mask | hotkey_driver_mask) & | 2268 | rc = hotkey_mask_set((hotkey_acpi_mask | hotkey_driver_mask) & |
2256 | ~hotkey_source_mask); | 2269 | ~hotkey_source_mask); |
2270 | hotkey_poll_setup(true); | ||
2271 | |||
2257 | mutex_unlock(&hotkey_mutex); | 2272 | mutex_unlock(&hotkey_mutex); |
2258 | 2273 | ||
2259 | return rc; | 2274 | return rc; |
@@ -2538,7 +2553,7 @@ static void hotkey_poll_stop_sync(void) | |||
2538 | } | 2553 | } |
2539 | 2554 | ||
2540 | /* call with hotkey_mutex held */ | 2555 | /* call with hotkey_mutex held */ |
2541 | static void hotkey_poll_setup(bool may_warn) | 2556 | static void hotkey_poll_setup(const bool may_warn) |
2542 | { | 2557 | { |
2543 | const u32 poll_driver_mask = hotkey_driver_mask & hotkey_source_mask; | 2558 | const u32 poll_driver_mask = hotkey_driver_mask & hotkey_source_mask; |
2544 | const u32 poll_user_mask = hotkey_user_mask & hotkey_source_mask; | 2559 | const u32 poll_user_mask = hotkey_user_mask & hotkey_source_mask; |
@@ -2569,7 +2584,7 @@ static void hotkey_poll_setup(bool may_warn) | |||
2569 | } | 2584 | } |
2570 | } | 2585 | } |
2571 | 2586 | ||
2572 | static void hotkey_poll_setup_safe(bool may_warn) | 2587 | static void hotkey_poll_setup_safe(const bool may_warn) |
2573 | { | 2588 | { |
2574 | mutex_lock(&hotkey_mutex); | 2589 | mutex_lock(&hotkey_mutex); |
2575 | hotkey_poll_setup(may_warn); | 2590 | hotkey_poll_setup(may_warn); |
@@ -2587,7 +2602,11 @@ static void hotkey_poll_set_freq(unsigned int freq) | |||
2587 | 2602 | ||
2588 | #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | 2603 | #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ |
2589 | 2604 | ||
2590 | static void hotkey_poll_setup_safe(bool __unused) | 2605 | static void hotkey_poll_setup(const bool __unused) |
2606 | { | ||
2607 | } | ||
2608 | |||
2609 | static void hotkey_poll_setup_safe(const bool __unused) | ||
2591 | { | 2610 | { |
2592 | } | 2611 | } |
2593 | 2612 | ||
@@ -2597,16 +2616,11 @@ static int hotkey_inputdev_open(struct input_dev *dev) | |||
2597 | { | 2616 | { |
2598 | switch (tpacpi_lifecycle) { | 2617 | switch (tpacpi_lifecycle) { |
2599 | case TPACPI_LIFE_INIT: | 2618 | case TPACPI_LIFE_INIT: |
2600 | /* | ||
2601 | * hotkey_init will call hotkey_poll_setup_safe | ||
2602 | * at the appropriate moment | ||
2603 | */ | ||
2604 | return 0; | ||
2605 | case TPACPI_LIFE_EXITING: | ||
2606 | return -EBUSY; | ||
2607 | case TPACPI_LIFE_RUNNING: | 2619 | case TPACPI_LIFE_RUNNING: |
2608 | hotkey_poll_setup_safe(false); | 2620 | hotkey_poll_setup_safe(false); |
2609 | return 0; | 2621 | return 0; |
2622 | case TPACPI_LIFE_EXITING: | ||
2623 | return -EBUSY; | ||
2610 | } | 2624 | } |
2611 | 2625 | ||
2612 | /* Should only happen if tpacpi_lifecycle is corrupt */ | 2626 | /* Should only happen if tpacpi_lifecycle is corrupt */ |
@@ -2617,7 +2631,7 @@ static int hotkey_inputdev_open(struct input_dev *dev) | |||
2617 | static void hotkey_inputdev_close(struct input_dev *dev) | 2631 | static void hotkey_inputdev_close(struct input_dev *dev) |
2618 | { | 2632 | { |
2619 | /* disable hotkey polling when possible */ | 2633 | /* disable hotkey polling when possible */ |
2620 | if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING && | 2634 | if (tpacpi_lifecycle != TPACPI_LIFE_EXITING && |
2621 | !(hotkey_source_mask & hotkey_driver_mask)) | 2635 | !(hotkey_source_mask & hotkey_driver_mask)) |
2622 | hotkey_poll_setup_safe(false); | 2636 | hotkey_poll_setup_safe(false); |
2623 | } | 2637 | } |
@@ -3185,6 +3199,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3185 | int res, i; | 3199 | int res, i; |
3186 | int status; | 3200 | int status; |
3187 | int hkeyv; | 3201 | int hkeyv; |
3202 | bool radiosw_state = false; | ||
3203 | bool tabletsw_state = false; | ||
3188 | 3204 | ||
3189 | unsigned long quirks; | 3205 | unsigned long quirks; |
3190 | 3206 | ||
@@ -3290,6 +3306,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3290 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3306 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3291 | if (dbg_wlswemul) { | 3307 | if (dbg_wlswemul) { |
3292 | tp_features.hotkey_wlsw = 1; | 3308 | tp_features.hotkey_wlsw = 1; |
3309 | radiosw_state = !!tpacpi_wlsw_emulstate; | ||
3293 | printk(TPACPI_INFO | 3310 | printk(TPACPI_INFO |
3294 | "radio switch emulation enabled\n"); | 3311 | "radio switch emulation enabled\n"); |
3295 | } else | 3312 | } else |
@@ -3297,6 +3314,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3297 | /* Not all thinkpads have a hardware radio switch */ | 3314 | /* Not all thinkpads have a hardware radio switch */ |
3298 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { | 3315 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { |
3299 | tp_features.hotkey_wlsw = 1; | 3316 | tp_features.hotkey_wlsw = 1; |
3317 | radiosw_state = !!status; | ||
3300 | printk(TPACPI_INFO | 3318 | printk(TPACPI_INFO |
3301 | "radio switch found; radios are %s\n", | 3319 | "radio switch found; radios are %s\n", |
3302 | enabled(status, 0)); | 3320 | enabled(status, 0)); |
@@ -3308,11 +3326,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3308 | /* For X41t, X60t, X61t Tablets... */ | 3326 | /* For X41t, X60t, X61t Tablets... */ |
3309 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { | 3327 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { |
3310 | tp_features.hotkey_tablet = 1; | 3328 | tp_features.hotkey_tablet = 1; |
3329 | tabletsw_state = !!(status & TP_HOTKEY_TABLET_MASK); | ||
3311 | printk(TPACPI_INFO | 3330 | printk(TPACPI_INFO |
3312 | "possible tablet mode switch found; " | 3331 | "possible tablet mode switch found; " |
3313 | "ThinkPad in %s mode\n", | 3332 | "ThinkPad in %s mode\n", |
3314 | (status & TP_HOTKEY_TABLET_MASK)? | 3333 | (tabletsw_state) ? "tablet" : "laptop"); |
3315 | "tablet" : "laptop"); | ||
3316 | res = add_to_attr_set(hotkey_dev_attributes, | 3334 | res = add_to_attr_set(hotkey_dev_attributes, |
3317 | &dev_attr_hotkey_tablet_mode.attr); | 3335 | &dev_attr_hotkey_tablet_mode.attr); |
3318 | } | 3336 | } |
@@ -3347,16 +3365,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3347 | TPACPI_HOTKEY_MAP_SIZE); | 3365 | TPACPI_HOTKEY_MAP_SIZE); |
3348 | } | 3366 | } |
3349 | 3367 | ||
3350 | set_bit(EV_KEY, tpacpi_inputdev->evbit); | 3368 | input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN); |
3351 | set_bit(EV_MSC, tpacpi_inputdev->evbit); | ||
3352 | set_bit(MSC_SCAN, tpacpi_inputdev->mscbit); | ||
3353 | tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; | 3369 | tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; |
3354 | tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; | 3370 | tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; |
3355 | tpacpi_inputdev->keycode = hotkey_keycode_map; | 3371 | tpacpi_inputdev->keycode = hotkey_keycode_map; |
3356 | for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { | 3372 | for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { |
3357 | if (hotkey_keycode_map[i] != KEY_RESERVED) { | 3373 | if (hotkey_keycode_map[i] != KEY_RESERVED) { |
3358 | set_bit(hotkey_keycode_map[i], | 3374 | input_set_capability(tpacpi_inputdev, EV_KEY, |
3359 | tpacpi_inputdev->keybit); | 3375 | hotkey_keycode_map[i]); |
3360 | } else { | 3376 | } else { |
3361 | if (i < sizeof(hotkey_reserved_mask)*8) | 3377 | if (i < sizeof(hotkey_reserved_mask)*8) |
3362 | hotkey_reserved_mask |= 1 << i; | 3378 | hotkey_reserved_mask |= 1 << i; |
@@ -3364,12 +3380,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3364 | } | 3380 | } |
3365 | 3381 | ||
3366 | if (tp_features.hotkey_wlsw) { | 3382 | if (tp_features.hotkey_wlsw) { |
3367 | set_bit(EV_SW, tpacpi_inputdev->evbit); | 3383 | input_set_capability(tpacpi_inputdev, EV_SW, SW_RFKILL_ALL); |
3368 | set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit); | 3384 | input_report_switch(tpacpi_inputdev, |
3385 | SW_RFKILL_ALL, radiosw_state); | ||
3369 | } | 3386 | } |
3370 | if (tp_features.hotkey_tablet) { | 3387 | if (tp_features.hotkey_tablet) { |
3371 | set_bit(EV_SW, tpacpi_inputdev->evbit); | 3388 | input_set_capability(tpacpi_inputdev, EV_SW, SW_TABLET_MODE); |
3372 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); | 3389 | input_report_switch(tpacpi_inputdev, |
3390 | SW_TABLET_MODE, tabletsw_state); | ||
3373 | } | 3391 | } |
3374 | 3392 | ||
3375 | /* Do not issue duplicate brightness change events to | 3393 | /* Do not issue duplicate brightness change events to |
@@ -3436,8 +3454,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3436 | tpacpi_inputdev->close = &hotkey_inputdev_close; | 3454 | tpacpi_inputdev->close = &hotkey_inputdev_close; |
3437 | 3455 | ||
3438 | hotkey_poll_setup_safe(true); | 3456 | hotkey_poll_setup_safe(true); |
3439 | tpacpi_send_radiosw_update(); | ||
3440 | tpacpi_input_send_tabletsw(); | ||
3441 | 3457 | ||
3442 | return 0; | 3458 | return 0; |
3443 | 3459 | ||
@@ -3545,49 +3561,57 @@ static bool hotkey_notify_usrevent(const u32 hkey, | |||
3545 | } | 3561 | } |
3546 | } | 3562 | } |
3547 | 3563 | ||
3564 | static void thermal_dump_all_sensors(void); | ||
3565 | |||
3548 | static bool hotkey_notify_thermal(const u32 hkey, | 3566 | static bool hotkey_notify_thermal(const u32 hkey, |
3549 | bool *send_acpi_ev, | 3567 | bool *send_acpi_ev, |
3550 | bool *ignore_acpi_ev) | 3568 | bool *ignore_acpi_ev) |
3551 | { | 3569 | { |
3570 | bool known = true; | ||
3571 | |||
3552 | /* 0x6000-0x6FFF: thermal alarms */ | 3572 | /* 0x6000-0x6FFF: thermal alarms */ |
3553 | *send_acpi_ev = true; | 3573 | *send_acpi_ev = true; |
3554 | *ignore_acpi_ev = false; | 3574 | *ignore_acpi_ev = false; |
3555 | 3575 | ||
3556 | switch (hkey) { | 3576 | switch (hkey) { |
3577 | case TP_HKEY_EV_THM_TABLE_CHANGED: | ||
3578 | printk(TPACPI_INFO | ||
3579 | "EC reports that Thermal Table has changed\n"); | ||
3580 | /* recommended action: do nothing, we don't have | ||
3581 | * Lenovo ATM information */ | ||
3582 | return true; | ||
3557 | case TP_HKEY_EV_ALARM_BAT_HOT: | 3583 | case TP_HKEY_EV_ALARM_BAT_HOT: |
3558 | printk(TPACPI_CRIT | 3584 | printk(TPACPI_CRIT |
3559 | "THERMAL ALARM: battery is too hot!\n"); | 3585 | "THERMAL ALARM: battery is too hot!\n"); |
3560 | /* recommended action: warn user through gui */ | 3586 | /* recommended action: warn user through gui */ |
3561 | return true; | 3587 | break; |
3562 | case TP_HKEY_EV_ALARM_BAT_XHOT: | 3588 | case TP_HKEY_EV_ALARM_BAT_XHOT: |
3563 | printk(TPACPI_ALERT | 3589 | printk(TPACPI_ALERT |
3564 | "THERMAL EMERGENCY: battery is extremely hot!\n"); | 3590 | "THERMAL EMERGENCY: battery is extremely hot!\n"); |
3565 | /* recommended action: immediate sleep/hibernate */ | 3591 | /* recommended action: immediate sleep/hibernate */ |
3566 | return true; | 3592 | break; |
3567 | case TP_HKEY_EV_ALARM_SENSOR_HOT: | 3593 | case TP_HKEY_EV_ALARM_SENSOR_HOT: |
3568 | printk(TPACPI_CRIT | 3594 | printk(TPACPI_CRIT |
3569 | "THERMAL ALARM: " | 3595 | "THERMAL ALARM: " |
3570 | "a sensor reports something is too hot!\n"); | 3596 | "a sensor reports something is too hot!\n"); |
3571 | /* recommended action: warn user through gui, that */ | 3597 | /* recommended action: warn user through gui, that */ |
3572 | /* some internal component is too hot */ | 3598 | /* some internal component is too hot */ |
3573 | return true; | 3599 | break; |
3574 | case TP_HKEY_EV_ALARM_SENSOR_XHOT: | 3600 | case TP_HKEY_EV_ALARM_SENSOR_XHOT: |
3575 | printk(TPACPI_ALERT | 3601 | printk(TPACPI_ALERT |
3576 | "THERMAL EMERGENCY: " | 3602 | "THERMAL EMERGENCY: " |
3577 | "a sensor reports something is extremely hot!\n"); | 3603 | "a sensor reports something is extremely hot!\n"); |
3578 | /* recommended action: immediate sleep/hibernate */ | 3604 | /* recommended action: immediate sleep/hibernate */ |
3579 | return true; | 3605 | break; |
3580 | case TP_HKEY_EV_THM_TABLE_CHANGED: | ||
3581 | printk(TPACPI_INFO | ||
3582 | "EC reports that Thermal Table has changed\n"); | ||
3583 | /* recommended action: do nothing, we don't have | ||
3584 | * Lenovo ATM information */ | ||
3585 | return true; | ||
3586 | default: | 3606 | default: |
3587 | printk(TPACPI_ALERT | 3607 | printk(TPACPI_ALERT |
3588 | "THERMAL ALERT: unknown thermal alarm received\n"); | 3608 | "THERMAL ALERT: unknown thermal alarm received\n"); |
3589 | return false; | 3609 | known = false; |
3590 | } | 3610 | } |
3611 | |||
3612 | thermal_dump_all_sensors(); | ||
3613 | |||
3614 | return known; | ||
3591 | } | 3615 | } |
3592 | 3616 | ||
3593 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) | 3617 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) |
@@ -3635,13 +3659,19 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
3635 | break; | 3659 | break; |
3636 | case 3: | 3660 | case 3: |
3637 | /* 0x3000-0x3FFF: bay-related wakeups */ | 3661 | /* 0x3000-0x3FFF: bay-related wakeups */ |
3638 | if (hkey == TP_HKEY_EV_BAYEJ_ACK) { | 3662 | switch (hkey) { |
3663 | case TP_HKEY_EV_BAYEJ_ACK: | ||
3639 | hotkey_autosleep_ack = 1; | 3664 | hotkey_autosleep_ack = 1; |
3640 | printk(TPACPI_INFO | 3665 | printk(TPACPI_INFO |
3641 | "bay ejected\n"); | 3666 | "bay ejected\n"); |
3642 | hotkey_wakeup_hotunplug_complete_notify_change(); | 3667 | hotkey_wakeup_hotunplug_complete_notify_change(); |
3643 | known_ev = true; | 3668 | known_ev = true; |
3644 | } else { | 3669 | break; |
3670 | case TP_HKEY_EV_OPTDRV_EJ: | ||
3671 | /* FIXME: kick libata if SATA link offline */ | ||
3672 | known_ev = true; | ||
3673 | break; | ||
3674 | default: | ||
3645 | known_ev = false; | 3675 | known_ev = false; |
3646 | } | 3676 | } |
3647 | break; | 3677 | break; |
@@ -3730,14 +3760,13 @@ static void hotkey_resume(void) | |||
3730 | } | 3760 | } |
3731 | 3761 | ||
3732 | /* procfs -------------------------------------------------------------- */ | 3762 | /* procfs -------------------------------------------------------------- */ |
3733 | static int hotkey_read(char *p) | 3763 | static int hotkey_read(struct seq_file *m) |
3734 | { | 3764 | { |
3735 | int res, status; | 3765 | int res, status; |
3736 | int len = 0; | ||
3737 | 3766 | ||
3738 | if (!tp_features.hotkey) { | 3767 | if (!tp_features.hotkey) { |
3739 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 3768 | seq_printf(m, "status:\t\tnot supported\n"); |
3740 | return len; | 3769 | return 0; |
3741 | } | 3770 | } |
3742 | 3771 | ||
3743 | if (mutex_lock_killable(&hotkey_mutex)) | 3772 | if (mutex_lock_killable(&hotkey_mutex)) |
@@ -3749,17 +3778,16 @@ static int hotkey_read(char *p) | |||
3749 | if (res) | 3778 | if (res) |
3750 | return res; | 3779 | return res; |
3751 | 3780 | ||
3752 | len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); | 3781 | seq_printf(m, "status:\t\t%s\n", enabled(status, 0)); |
3753 | if (hotkey_all_mask) { | 3782 | if (hotkey_all_mask) { |
3754 | len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_user_mask); | 3783 | seq_printf(m, "mask:\t\t0x%08x\n", hotkey_user_mask); |
3755 | len += sprintf(p + len, | 3784 | seq_printf(m, "commands:\tenable, disable, reset, <mask>\n"); |
3756 | "commands:\tenable, disable, reset, <mask>\n"); | ||
3757 | } else { | 3785 | } else { |
3758 | len += sprintf(p + len, "mask:\t\tnot supported\n"); | 3786 | seq_printf(m, "mask:\t\tnot supported\n"); |
3759 | len += sprintf(p + len, "commands:\tenable, disable, reset\n"); | 3787 | seq_printf(m, "commands:\tenable, disable, reset\n"); |
3760 | } | 3788 | } |
3761 | 3789 | ||
3762 | return len; | 3790 | return 0; |
3763 | } | 3791 | } |
3764 | 3792 | ||
3765 | static void hotkey_enabledisable_warn(bool enable) | 3793 | static void hotkey_enabledisable_warn(bool enable) |
@@ -3852,7 +3880,7 @@ enum { | |||
3852 | TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ | 3880 | TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ |
3853 | TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */ | 3881 | TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */ |
3854 | TP_ACPI_BLUETOOTH_RESUMECTRL = 0x04, /* Bluetooth state at resume: | 3882 | TP_ACPI_BLUETOOTH_RESUMECTRL = 0x04, /* Bluetooth state at resume: |
3855 | off / last state */ | 3883 | 0 = disable, 1 = enable */ |
3856 | }; | 3884 | }; |
3857 | 3885 | ||
3858 | enum { | 3886 | enum { |
@@ -3866,15 +3894,6 @@ enum { | |||
3866 | 3894 | ||
3867 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" | 3895 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" |
3868 | 3896 | ||
3869 | static void bluetooth_suspend(pm_message_t state) | ||
3870 | { | ||
3871 | /* Try to make sure radio will resume powered off */ | ||
3872 | if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd", | ||
3873 | TP_ACPI_BLTH_PWR_OFF_ON_RESUME)) | ||
3874 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3875 | "bluetooth power down on resume request failed\n"); | ||
3876 | } | ||
3877 | |||
3878 | static int bluetooth_get_status(void) | 3897 | static int bluetooth_get_status(void) |
3879 | { | 3898 | { |
3880 | int status; | 3899 | int status; |
@@ -3907,9 +3926,9 @@ static int bluetooth_set_status(enum tpacpi_rfkill_state state) | |||
3907 | } | 3926 | } |
3908 | #endif | 3927 | #endif |
3909 | 3928 | ||
3910 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ | ||
3911 | if (state == TPACPI_RFK_RADIO_ON) | 3929 | if (state == TPACPI_RFK_RADIO_ON) |
3912 | status = TP_ACPI_BLUETOOTH_RADIOSSW; | 3930 | status = TP_ACPI_BLUETOOTH_RADIOSSW |
3931 | | TP_ACPI_BLUETOOTH_RESUMECTRL; | ||
3913 | else | 3932 | else |
3914 | status = 0; | 3933 | status = 0; |
3915 | 3934 | ||
@@ -4035,9 +4054,9 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
4035 | } | 4054 | } |
4036 | 4055 | ||
4037 | /* procfs -------------------------------------------------------------- */ | 4056 | /* procfs -------------------------------------------------------------- */ |
4038 | static int bluetooth_read(char *p) | 4057 | static int bluetooth_read(struct seq_file *m) |
4039 | { | 4058 | { |
4040 | return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p); | 4059 | return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, m); |
4041 | } | 4060 | } |
4042 | 4061 | ||
4043 | static int bluetooth_write(char *buf) | 4062 | static int bluetooth_write(char *buf) |
@@ -4050,7 +4069,6 @@ static struct ibm_struct bluetooth_driver_data = { | |||
4050 | .read = bluetooth_read, | 4069 | .read = bluetooth_read, |
4051 | .write = bluetooth_write, | 4070 | .write = bluetooth_write, |
4052 | .exit = bluetooth_exit, | 4071 | .exit = bluetooth_exit, |
4053 | .suspend = bluetooth_suspend, | ||
4054 | .shutdown = bluetooth_shutdown, | 4072 | .shutdown = bluetooth_shutdown, |
4055 | }; | 4073 | }; |
4056 | 4074 | ||
@@ -4063,20 +4081,11 @@ enum { | |||
4063 | TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ | 4081 | TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ |
4064 | TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */ | 4082 | TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */ |
4065 | TP_ACPI_WANCARD_RESUMECTRL = 0x04, /* Wan state at resume: | 4083 | TP_ACPI_WANCARD_RESUMECTRL = 0x04, /* Wan state at resume: |
4066 | off / last state */ | 4084 | 0 = disable, 1 = enable */ |
4067 | }; | 4085 | }; |
4068 | 4086 | ||
4069 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" | 4087 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" |
4070 | 4088 | ||
4071 | static void wan_suspend(pm_message_t state) | ||
4072 | { | ||
4073 | /* Try to make sure radio will resume powered off */ | ||
4074 | if (!acpi_evalf(NULL, NULL, "\\WGSV", "qvd", | ||
4075 | TP_ACPI_WGSV_PWR_OFF_ON_RESUME)) | ||
4076 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
4077 | "WWAN power down on resume request failed\n"); | ||
4078 | } | ||
4079 | |||
4080 | static int wan_get_status(void) | 4089 | static int wan_get_status(void) |
4081 | { | 4090 | { |
4082 | int status; | 4091 | int status; |
@@ -4109,9 +4118,9 @@ static int wan_set_status(enum tpacpi_rfkill_state state) | |||
4109 | } | 4118 | } |
4110 | #endif | 4119 | #endif |
4111 | 4120 | ||
4112 | /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ | ||
4113 | if (state == TPACPI_RFK_RADIO_ON) | 4121 | if (state == TPACPI_RFK_RADIO_ON) |
4114 | status = TP_ACPI_WANCARD_RADIOSSW; | 4122 | status = TP_ACPI_WANCARD_RADIOSSW |
4123 | | TP_ACPI_WANCARD_RESUMECTRL; | ||
4115 | else | 4124 | else |
4116 | status = 0; | 4125 | status = 0; |
4117 | 4126 | ||
@@ -4236,9 +4245,9 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
4236 | } | 4245 | } |
4237 | 4246 | ||
4238 | /* procfs -------------------------------------------------------------- */ | 4247 | /* procfs -------------------------------------------------------------- */ |
4239 | static int wan_read(char *p) | 4248 | static int wan_read(struct seq_file *m) |
4240 | { | 4249 | { |
4241 | return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p); | 4250 | return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, m); |
4242 | } | 4251 | } |
4243 | 4252 | ||
4244 | static int wan_write(char *buf) | 4253 | static int wan_write(char *buf) |
@@ -4251,7 +4260,6 @@ static struct ibm_struct wan_driver_data = { | |||
4251 | .read = wan_read, | 4260 | .read = wan_read, |
4252 | .write = wan_write, | 4261 | .write = wan_write, |
4253 | .exit = wan_exit, | 4262 | .exit = wan_exit, |
4254 | .suspend = wan_suspend, | ||
4255 | .shutdown = wan_shutdown, | 4263 | .shutdown = wan_shutdown, |
4256 | }; | 4264 | }; |
4257 | 4265 | ||
@@ -4614,16 +4622,19 @@ static int video_expand_toggle(void) | |||
4614 | /* not reached */ | 4622 | /* not reached */ |
4615 | } | 4623 | } |
4616 | 4624 | ||
4617 | static int video_read(char *p) | 4625 | static int video_read(struct seq_file *m) |
4618 | { | 4626 | { |
4619 | int status, autosw; | 4627 | int status, autosw; |
4620 | int len = 0; | ||
4621 | 4628 | ||
4622 | if (video_supported == TPACPI_VIDEO_NONE) { | 4629 | if (video_supported == TPACPI_VIDEO_NONE) { |
4623 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 4630 | seq_printf(m, "status:\t\tnot supported\n"); |
4624 | return len; | 4631 | return 0; |
4625 | } | 4632 | } |
4626 | 4633 | ||
4634 | /* Even reads can crash X.org, so... */ | ||
4635 | if (!capable(CAP_SYS_ADMIN)) | ||
4636 | return -EPERM; | ||
4637 | |||
4627 | status = video_outputsw_get(); | 4638 | status = video_outputsw_get(); |
4628 | if (status < 0) | 4639 | if (status < 0) |
4629 | return status; | 4640 | return status; |
@@ -4632,20 +4643,20 @@ static int video_read(char *p) | |||
4632 | if (autosw < 0) | 4643 | if (autosw < 0) |
4633 | return autosw; | 4644 | return autosw; |
4634 | 4645 | ||
4635 | len += sprintf(p + len, "status:\t\tsupported\n"); | 4646 | seq_printf(m, "status:\t\tsupported\n"); |
4636 | len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); | 4647 | seq_printf(m, "lcd:\t\t%s\n", enabled(status, 0)); |
4637 | len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); | 4648 | seq_printf(m, "crt:\t\t%s\n", enabled(status, 1)); |
4638 | if (video_supported == TPACPI_VIDEO_NEW) | 4649 | if (video_supported == TPACPI_VIDEO_NEW) |
4639 | len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); | 4650 | seq_printf(m, "dvi:\t\t%s\n", enabled(status, 3)); |
4640 | len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); | 4651 | seq_printf(m, "auto:\t\t%s\n", enabled(autosw, 0)); |
4641 | len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); | 4652 | seq_printf(m, "commands:\tlcd_enable, lcd_disable\n"); |
4642 | len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); | 4653 | seq_printf(m, "commands:\tcrt_enable, crt_disable\n"); |
4643 | if (video_supported == TPACPI_VIDEO_NEW) | 4654 | if (video_supported == TPACPI_VIDEO_NEW) |
4644 | len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); | 4655 | seq_printf(m, "commands:\tdvi_enable, dvi_disable\n"); |
4645 | len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); | 4656 | seq_printf(m, "commands:\tauto_enable, auto_disable\n"); |
4646 | len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); | 4657 | seq_printf(m, "commands:\tvideo_switch, expand_toggle\n"); |
4647 | 4658 | ||
4648 | return len; | 4659 | return 0; |
4649 | } | 4660 | } |
4650 | 4661 | ||
4651 | static int video_write(char *buf) | 4662 | static int video_write(char *buf) |
@@ -4657,6 +4668,10 @@ static int video_write(char *buf) | |||
4657 | if (video_supported == TPACPI_VIDEO_NONE) | 4668 | if (video_supported == TPACPI_VIDEO_NONE) |
4658 | return -ENODEV; | 4669 | return -ENODEV; |
4659 | 4670 | ||
4671 | /* Even reads can crash X.org, let alone writes... */ | ||
4672 | if (!capable(CAP_SYS_ADMIN)) | ||
4673 | return -EPERM; | ||
4674 | |||
4660 | enable = 0; | 4675 | enable = 0; |
4661 | disable = 0; | 4676 | disable = 0; |
4662 | 4677 | ||
@@ -4837,25 +4852,24 @@ static void light_exit(void) | |||
4837 | flush_workqueue(tpacpi_wq); | 4852 | flush_workqueue(tpacpi_wq); |
4838 | } | 4853 | } |
4839 | 4854 | ||
4840 | static int light_read(char *p) | 4855 | static int light_read(struct seq_file *m) |
4841 | { | 4856 | { |
4842 | int len = 0; | ||
4843 | int status; | 4857 | int status; |
4844 | 4858 | ||
4845 | if (!tp_features.light) { | 4859 | if (!tp_features.light) { |
4846 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 4860 | seq_printf(m, "status:\t\tnot supported\n"); |
4847 | } else if (!tp_features.light_status) { | 4861 | } else if (!tp_features.light_status) { |
4848 | len += sprintf(p + len, "status:\t\tunknown\n"); | 4862 | seq_printf(m, "status:\t\tunknown\n"); |
4849 | len += sprintf(p + len, "commands:\ton, off\n"); | 4863 | seq_printf(m, "commands:\ton, off\n"); |
4850 | } else { | 4864 | } else { |
4851 | status = light_get_status(); | 4865 | status = light_get_status(); |
4852 | if (status < 0) | 4866 | if (status < 0) |
4853 | return status; | 4867 | return status; |
4854 | len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); | 4868 | seq_printf(m, "status:\t\t%s\n", onoff(status, 0)); |
4855 | len += sprintf(p + len, "commands:\ton, off\n"); | 4869 | seq_printf(m, "commands:\ton, off\n"); |
4856 | } | 4870 | } |
4857 | 4871 | ||
4858 | return len; | 4872 | return 0; |
4859 | } | 4873 | } |
4860 | 4874 | ||
4861 | static int light_write(char *buf) | 4875 | static int light_write(char *buf) |
@@ -4933,20 +4947,18 @@ static void cmos_exit(void) | |||
4933 | device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); | 4947 | device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); |
4934 | } | 4948 | } |
4935 | 4949 | ||
4936 | static int cmos_read(char *p) | 4950 | static int cmos_read(struct seq_file *m) |
4937 | { | 4951 | { |
4938 | int len = 0; | ||
4939 | |||
4940 | /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, | 4952 | /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, |
4941 | R30, R31, T20-22, X20-21 */ | 4953 | R30, R31, T20-22, X20-21 */ |
4942 | if (!cmos_handle) | 4954 | if (!cmos_handle) |
4943 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 4955 | seq_printf(m, "status:\t\tnot supported\n"); |
4944 | else { | 4956 | else { |
4945 | len += sprintf(p + len, "status:\t\tsupported\n"); | 4957 | seq_printf(m, "status:\t\tsupported\n"); |
4946 | len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n"); | 4958 | seq_printf(m, "commands:\t<cmd> (<cmd> is 0-21)\n"); |
4947 | } | 4959 | } |
4948 | 4960 | ||
4949 | return len; | 4961 | return 0; |
4950 | } | 4962 | } |
4951 | 4963 | ||
4952 | static int cmos_write(char *buf) | 4964 | static int cmos_write(char *buf) |
@@ -5321,15 +5333,13 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
5321 | ((s) == TPACPI_LED_OFF ? "off" : \ | 5333 | ((s) == TPACPI_LED_OFF ? "off" : \ |
5322 | ((s) == TPACPI_LED_ON ? "on" : "blinking")) | 5334 | ((s) == TPACPI_LED_ON ? "on" : "blinking")) |
5323 | 5335 | ||
5324 | static int led_read(char *p) | 5336 | static int led_read(struct seq_file *m) |
5325 | { | 5337 | { |
5326 | int len = 0; | ||
5327 | |||
5328 | if (!led_supported) { | 5338 | if (!led_supported) { |
5329 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 5339 | seq_printf(m, "status:\t\tnot supported\n"); |
5330 | return len; | 5340 | return 0; |
5331 | } | 5341 | } |
5332 | len += sprintf(p + len, "status:\t\tsupported\n"); | 5342 | seq_printf(m, "status:\t\tsupported\n"); |
5333 | 5343 | ||
5334 | if (led_supported == TPACPI_LED_570) { | 5344 | if (led_supported == TPACPI_LED_570) { |
5335 | /* 570 */ | 5345 | /* 570 */ |
@@ -5338,15 +5348,15 @@ static int led_read(char *p) | |||
5338 | status = led_get_status(i); | 5348 | status = led_get_status(i); |
5339 | if (status < 0) | 5349 | if (status < 0) |
5340 | return -EIO; | 5350 | return -EIO; |
5341 | len += sprintf(p + len, "%d:\t\t%s\n", | 5351 | seq_printf(m, "%d:\t\t%s\n", |
5342 | i, str_led_status(status)); | 5352 | i, str_led_status(status)); |
5343 | } | 5353 | } |
5344 | } | 5354 | } |
5345 | 5355 | ||
5346 | len += sprintf(p + len, "commands:\t" | 5356 | seq_printf(m, "commands:\t" |
5347 | "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); | 5357 | "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); |
5348 | 5358 | ||
5349 | return len; | 5359 | return 0; |
5350 | } | 5360 | } |
5351 | 5361 | ||
5352 | static int led_write(char *buf) | 5362 | static int led_write(char *buf) |
@@ -5419,18 +5429,16 @@ static int __init beep_init(struct ibm_init_struct *iibm) | |||
5419 | return (beep_handle)? 0 : 1; | 5429 | return (beep_handle)? 0 : 1; |
5420 | } | 5430 | } |
5421 | 5431 | ||
5422 | static int beep_read(char *p) | 5432 | static int beep_read(struct seq_file *m) |
5423 | { | 5433 | { |
5424 | int len = 0; | ||
5425 | |||
5426 | if (!beep_handle) | 5434 | if (!beep_handle) |
5427 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 5435 | seq_printf(m, "status:\t\tnot supported\n"); |
5428 | else { | 5436 | else { |
5429 | len += sprintf(p + len, "status:\t\tsupported\n"); | 5437 | seq_printf(m, "status:\t\tsupported\n"); |
5430 | len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n"); | 5438 | seq_printf(m, "commands:\t<cmd> (<cmd> is 0-17)\n"); |
5431 | } | 5439 | } |
5432 | 5440 | ||
5433 | return len; | 5441 | return 0; |
5434 | } | 5442 | } |
5435 | 5443 | ||
5436 | static int beep_write(char *buf) | 5444 | static int beep_write(char *buf) |
@@ -5483,8 +5491,11 @@ enum { /* TPACPI_THERMAL_TPEC_* */ | |||
5483 | TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ | 5491 | TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ |
5484 | TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ | 5492 | TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ |
5485 | TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ | 5493 | TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ |
5494 | |||
5495 | TPACPI_THERMAL_SENSOR_NA = -128000, /* Sensor not available */ | ||
5486 | }; | 5496 | }; |
5487 | 5497 | ||
5498 | |||
5488 | #define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ | 5499 | #define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ |
5489 | struct ibm_thermal_sensors_struct { | 5500 | struct ibm_thermal_sensors_struct { |
5490 | s32 temp[TPACPI_MAX_THERMAL_SENSORS]; | 5501 | s32 temp[TPACPI_MAX_THERMAL_SENSORS]; |
@@ -5574,6 +5585,28 @@ static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) | |||
5574 | return n; | 5585 | return n; |
5575 | } | 5586 | } |
5576 | 5587 | ||
5588 | static void thermal_dump_all_sensors(void) | ||
5589 | { | ||
5590 | int n, i; | ||
5591 | struct ibm_thermal_sensors_struct t; | ||
5592 | |||
5593 | n = thermal_get_sensors(&t); | ||
5594 | if (n <= 0) | ||
5595 | return; | ||
5596 | |||
5597 | printk(TPACPI_NOTICE | ||
5598 | "temperatures (Celsius):"); | ||
5599 | |||
5600 | for (i = 0; i < n; i++) { | ||
5601 | if (t.temp[i] != TPACPI_THERMAL_SENSOR_NA) | ||
5602 | printk(KERN_CONT " %d", (int)(t.temp[i] / 1000)); | ||
5603 | else | ||
5604 | printk(KERN_CONT " N/A"); | ||
5605 | } | ||
5606 | |||
5607 | printk(KERN_CONT "\n"); | ||
5608 | } | ||
5609 | |||
5577 | /* sysfs temp##_input -------------------------------------------------- */ | 5610 | /* sysfs temp##_input -------------------------------------------------- */ |
5578 | 5611 | ||
5579 | static ssize_t thermal_temp_input_show(struct device *dev, | 5612 | static ssize_t thermal_temp_input_show(struct device *dev, |
@@ -5589,7 +5622,7 @@ static ssize_t thermal_temp_input_show(struct device *dev, | |||
5589 | res = thermal_get_sensor(idx, &value); | 5622 | res = thermal_get_sensor(idx, &value); |
5590 | if (res) | 5623 | if (res) |
5591 | return res; | 5624 | return res; |
5592 | if (value == TP_EC_THERMAL_TMP_NA * 1000) | 5625 | if (value == TPACPI_THERMAL_SENSOR_NA) |
5593 | return -ENXIO; | 5626 | return -ENXIO; |
5594 | 5627 | ||
5595 | return snprintf(buf, PAGE_SIZE, "%d\n", value); | 5628 | return snprintf(buf, PAGE_SIZE, "%d\n", value); |
@@ -5758,7 +5791,7 @@ static void thermal_exit(void) | |||
5758 | case TPACPI_THERMAL_ACPI_TMP07: | 5791 | case TPACPI_THERMAL_ACPI_TMP07: |
5759 | case TPACPI_THERMAL_ACPI_UPDT: | 5792 | case TPACPI_THERMAL_ACPI_UPDT: |
5760 | sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, | 5793 | sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, |
5761 | &thermal_temp_input16_group); | 5794 | &thermal_temp_input8_group); |
5762 | break; | 5795 | break; |
5763 | case TPACPI_THERMAL_NONE: | 5796 | case TPACPI_THERMAL_NONE: |
5764 | default: | 5797 | default: |
@@ -5766,9 +5799,8 @@ static void thermal_exit(void) | |||
5766 | } | 5799 | } |
5767 | } | 5800 | } |
5768 | 5801 | ||
5769 | static int thermal_read(char *p) | 5802 | static int thermal_read(struct seq_file *m) |
5770 | { | 5803 | { |
5771 | int len = 0; | ||
5772 | int n, i; | 5804 | int n, i; |
5773 | struct ibm_thermal_sensors_struct t; | 5805 | struct ibm_thermal_sensors_struct t; |
5774 | 5806 | ||
@@ -5776,16 +5808,16 @@ static int thermal_read(char *p) | |||
5776 | if (unlikely(n < 0)) | 5808 | if (unlikely(n < 0)) |
5777 | return n; | 5809 | return n; |
5778 | 5810 | ||
5779 | len += sprintf(p + len, "temperatures:\t"); | 5811 | seq_printf(m, "temperatures:\t"); |
5780 | 5812 | ||
5781 | if (n > 0) { | 5813 | if (n > 0) { |
5782 | for (i = 0; i < (n - 1); i++) | 5814 | for (i = 0; i < (n - 1); i++) |
5783 | len += sprintf(p + len, "%d ", t.temp[i] / 1000); | 5815 | seq_printf(m, "%d ", t.temp[i] / 1000); |
5784 | len += sprintf(p + len, "%d\n", t.temp[i] / 1000); | 5816 | seq_printf(m, "%d\n", t.temp[i] / 1000); |
5785 | } else | 5817 | } else |
5786 | len += sprintf(p + len, "not supported\n"); | 5818 | seq_printf(m, "not supported\n"); |
5787 | 5819 | ||
5788 | return len; | 5820 | return 0; |
5789 | } | 5821 | } |
5790 | 5822 | ||
5791 | static struct ibm_struct thermal_driver_data = { | 5823 | static struct ibm_struct thermal_driver_data = { |
@@ -5800,39 +5832,38 @@ static struct ibm_struct thermal_driver_data = { | |||
5800 | 5832 | ||
5801 | static u8 ecdump_regs[256]; | 5833 | static u8 ecdump_regs[256]; |
5802 | 5834 | ||
5803 | static int ecdump_read(char *p) | 5835 | static int ecdump_read(struct seq_file *m) |
5804 | { | 5836 | { |
5805 | int len = 0; | ||
5806 | int i, j; | 5837 | int i, j; |
5807 | u8 v; | 5838 | u8 v; |
5808 | 5839 | ||
5809 | len += sprintf(p + len, "EC " | 5840 | seq_printf(m, "EC " |
5810 | " +00 +01 +02 +03 +04 +05 +06 +07" | 5841 | " +00 +01 +02 +03 +04 +05 +06 +07" |
5811 | " +08 +09 +0a +0b +0c +0d +0e +0f\n"); | 5842 | " +08 +09 +0a +0b +0c +0d +0e +0f\n"); |
5812 | for (i = 0; i < 256; i += 16) { | 5843 | for (i = 0; i < 256; i += 16) { |
5813 | len += sprintf(p + len, "EC 0x%02x:", i); | 5844 | seq_printf(m, "EC 0x%02x:", i); |
5814 | for (j = 0; j < 16; j++) { | 5845 | for (j = 0; j < 16; j++) { |
5815 | if (!acpi_ec_read(i + j, &v)) | 5846 | if (!acpi_ec_read(i + j, &v)) |
5816 | break; | 5847 | break; |
5817 | if (v != ecdump_regs[i + j]) | 5848 | if (v != ecdump_regs[i + j]) |
5818 | len += sprintf(p + len, " *%02x", v); | 5849 | seq_printf(m, " *%02x", v); |
5819 | else | 5850 | else |
5820 | len += sprintf(p + len, " %02x", v); | 5851 | seq_printf(m, " %02x", v); |
5821 | ecdump_regs[i + j] = v; | 5852 | ecdump_regs[i + j] = v; |
5822 | } | 5853 | } |
5823 | len += sprintf(p + len, "\n"); | 5854 | seq_putc(m, '\n'); |
5824 | if (j != 16) | 5855 | if (j != 16) |
5825 | break; | 5856 | break; |
5826 | } | 5857 | } |
5827 | 5858 | ||
5828 | /* These are way too dangerous to advertise openly... */ | 5859 | /* These are way too dangerous to advertise openly... */ |
5829 | #if 0 | 5860 | #if 0 |
5830 | len += sprintf(p + len, "commands:\t0x<offset> 0x<value>" | 5861 | seq_printf(m, "commands:\t0x<offset> 0x<value>" |
5831 | " (<offset> is 00-ff, <value> is 00-ff)\n"); | 5862 | " (<offset> is 00-ff, <value> is 00-ff)\n"); |
5832 | len += sprintf(p + len, "commands:\t0x<offset> <value> " | 5863 | seq_printf(m, "commands:\t0x<offset> <value> " |
5833 | " (<offset> is 00-ff, <value> is 0-255)\n"); | 5864 | " (<offset> is 00-ff, <value> is 0-255)\n"); |
5834 | #endif | 5865 | #endif |
5835 | return len; | 5866 | return 0; |
5836 | } | 5867 | } |
5837 | 5868 | ||
5838 | static int ecdump_write(char *buf) | 5869 | static int ecdump_write(char *buf) |
@@ -6095,6 +6126,12 @@ static int brightness_get(struct backlight_device *bd) | |||
6095 | return status & TP_EC_BACKLIGHT_LVLMSK; | 6126 | return status & TP_EC_BACKLIGHT_LVLMSK; |
6096 | } | 6127 | } |
6097 | 6128 | ||
6129 | static void tpacpi_brightness_notify_change(void) | ||
6130 | { | ||
6131 | backlight_force_update(ibm_backlight_device, | ||
6132 | BACKLIGHT_UPDATE_HOTKEY); | ||
6133 | } | ||
6134 | |||
6098 | static struct backlight_ops ibm_backlight_data = { | 6135 | static struct backlight_ops ibm_backlight_data = { |
6099 | .get_brightness = brightness_get, | 6136 | .get_brightness = brightness_get, |
6100 | .update_status = brightness_update_status, | 6137 | .update_status = brightness_update_status, |
@@ -6116,15 +6153,15 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { | |||
6116 | TPACPI_Q_IBM('1', 'Y', TPACPI_BRGHT_Q_EC), /* T43/p ATI */ | 6153 | TPACPI_Q_IBM('1', 'Y', TPACPI_BRGHT_Q_EC), /* T43/p ATI */ |
6117 | 6154 | ||
6118 | /* Models with ATI GPUs that can use ECNVRAM */ | 6155 | /* Models with ATI GPUs that can use ECNVRAM */ |
6119 | TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_EC), | 6156 | TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_EC), /* R50,51 T40-42 */ |
6120 | TPACPI_Q_IBM('1', 'Q', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), | 6157 | TPACPI_Q_IBM('1', 'Q', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
6121 | TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), | 6158 | TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_EC), /* R52 */ |
6122 | TPACPI_Q_IBM('7', '8', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), | 6159 | TPACPI_Q_IBM('7', '8', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
6123 | 6160 | ||
6124 | /* Models with Intel Extreme Graphics 2 */ | 6161 | /* Models with Intel Extreme Graphics 2 */ |
6125 | TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), | 6162 | TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), /* X40 */ |
6126 | TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), | 6163 | TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
6127 | TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), | 6164 | TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
6128 | 6165 | ||
6129 | /* Models with Intel GMA900 */ | 6166 | /* Models with Intel GMA900 */ |
6130 | TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */ | 6167 | TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */ |
@@ -6134,6 +6171,7 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { | |||
6134 | 6171 | ||
6135 | static int __init brightness_init(struct ibm_init_struct *iibm) | 6172 | static int __init brightness_init(struct ibm_init_struct *iibm) |
6136 | { | 6173 | { |
6174 | struct backlight_properties props; | ||
6137 | int b; | 6175 | int b; |
6138 | unsigned long quirks; | 6176 | unsigned long quirks; |
6139 | 6177 | ||
@@ -6223,9 +6261,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6223 | printk(TPACPI_INFO | 6261 | printk(TPACPI_INFO |
6224 | "detected a 16-level brightness capable ThinkPad\n"); | 6262 | "detected a 16-level brightness capable ThinkPad\n"); |
6225 | 6263 | ||
6226 | ibm_backlight_device = backlight_device_register( | 6264 | memset(&props, 0, sizeof(struct backlight_properties)); |
6227 | TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, | 6265 | props.max_brightness = (tp_features.bright_16levels) ? 15 : 7; |
6228 | &ibm_backlight_data); | 6266 | ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME, |
6267 | NULL, NULL, | ||
6268 | &ibm_backlight_data, | ||
6269 | &props); | ||
6229 | if (IS_ERR(ibm_backlight_device)) { | 6270 | if (IS_ERR(ibm_backlight_device)) { |
6230 | int rc = PTR_ERR(ibm_backlight_device); | 6271 | int rc = PTR_ERR(ibm_backlight_device); |
6231 | ibm_backlight_device = NULL; | 6272 | ibm_backlight_device = NULL; |
@@ -6244,11 +6285,15 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6244 | "or not on your ThinkPad\n", TPACPI_MAIL); | 6285 | "or not on your ThinkPad\n", TPACPI_MAIL); |
6245 | } | 6286 | } |
6246 | 6287 | ||
6247 | ibm_backlight_device->props.max_brightness = | ||
6248 | (tp_features.bright_16levels)? 15 : 7; | ||
6249 | ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; | 6288 | ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; |
6250 | backlight_update_status(ibm_backlight_device); | 6289 | backlight_update_status(ibm_backlight_device); |
6251 | 6290 | ||
6291 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, | ||
6292 | "brightness: registering brightness hotkeys " | ||
6293 | "as change notification\n"); | ||
6294 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | ||
6295 | | TP_ACPI_HKEY_BRGHTUP_MASK | ||
6296 | | TP_ACPI_HKEY_BRGHTDWN_MASK);; | ||
6252 | return 0; | 6297 | return 0; |
6253 | } | 6298 | } |
6254 | 6299 | ||
@@ -6273,23 +6318,22 @@ static void brightness_exit(void) | |||
6273 | tpacpi_brightness_checkpoint_nvram(); | 6318 | tpacpi_brightness_checkpoint_nvram(); |
6274 | } | 6319 | } |
6275 | 6320 | ||
6276 | static int brightness_read(char *p) | 6321 | static int brightness_read(struct seq_file *m) |
6277 | { | 6322 | { |
6278 | int len = 0; | ||
6279 | int level; | 6323 | int level; |
6280 | 6324 | ||
6281 | level = brightness_get(NULL); | 6325 | level = brightness_get(NULL); |
6282 | if (level < 0) { | 6326 | if (level < 0) { |
6283 | len += sprintf(p + len, "level:\t\tunreadable\n"); | 6327 | seq_printf(m, "level:\t\tunreadable\n"); |
6284 | } else { | 6328 | } else { |
6285 | len += sprintf(p + len, "level:\t\t%d\n", level); | 6329 | seq_printf(m, "level:\t\t%d\n", level); |
6286 | len += sprintf(p + len, "commands:\tup, down\n"); | 6330 | seq_printf(m, "commands:\tup, down\n"); |
6287 | len += sprintf(p + len, "commands:\tlevel <level>" | 6331 | seq_printf(m, "commands:\tlevel <level>" |
6288 | " (<level> is 0-%d)\n", | 6332 | " (<level> is 0-%d)\n", |
6289 | (tp_features.bright_16levels) ? 15 : 7); | 6333 | (tp_features.bright_16levels) ? 15 : 7); |
6290 | } | 6334 | } |
6291 | 6335 | ||
6292 | return len; | 6336 | return 0; |
6293 | } | 6337 | } |
6294 | 6338 | ||
6295 | static int brightness_write(char *buf) | 6339 | static int brightness_write(char *buf) |
@@ -6325,6 +6369,9 @@ static int brightness_write(char *buf) | |||
6325 | * Doing it this way makes the syscall restartable in case of EINTR | 6369 | * Doing it this way makes the syscall restartable in case of EINTR |
6326 | */ | 6370 | */ |
6327 | rc = brightness_set(level); | 6371 | rc = brightness_set(level); |
6372 | if (!rc && ibm_backlight_device) | ||
6373 | backlight_force_update(ibm_backlight_device, | ||
6374 | BACKLIGHT_UPDATE_SYSFS); | ||
6328 | return (rc == -EINTR)? -ERESTARTSYS : rc; | 6375 | return (rc == -EINTR)? -ERESTARTSYS : rc; |
6329 | } | 6376 | } |
6330 | 6377 | ||
@@ -6341,101 +6388,704 @@ static struct ibm_struct brightness_driver_data = { | |||
6341 | * Volume subdriver | 6388 | * Volume subdriver |
6342 | */ | 6389 | */ |
6343 | 6390 | ||
6344 | static int volume_offset = 0x30; | 6391 | /* |
6392 | * IBM ThinkPads have a simple volume controller with MUTE gating. | ||
6393 | * Very early Lenovo ThinkPads follow the IBM ThinkPad spec. | ||
6394 | * | ||
6395 | * Since the *61 series (and probably also the later *60 series), Lenovo | ||
6396 | * ThinkPads only implement the MUTE gate. | ||
6397 | * | ||
6398 | * EC register 0x30 | ||
6399 | * Bit 6: MUTE (1 mutes sound) | ||
6400 | * Bit 3-0: Volume | ||
6401 | * Other bits should be zero as far as we know. | ||
6402 | * | ||
6403 | * This is also stored in CMOS NVRAM, byte 0x60, bit 6 (MUTE), and | ||
6404 | * bits 3-0 (volume). Other bits in NVRAM may have other functions, | ||
6405 | * such as bit 7 which is used to detect repeated presses of MUTE, | ||
6406 | * and we leave them unchanged. | ||
6407 | */ | ||
6408 | |||
6409 | #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT | ||
6410 | |||
6411 | #define TPACPI_ALSA_DRVNAME "ThinkPad EC" | ||
6412 | #define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control" | ||
6413 | #define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME | ||
6345 | 6414 | ||
6346 | static int volume_read(char *p) | 6415 | static int alsa_index = ~((1 << (SNDRV_CARDS - 3)) - 1); /* last three slots */ |
6416 | static char *alsa_id = "ThinkPadEC"; | ||
6417 | static int alsa_enable = SNDRV_DEFAULT_ENABLE1; | ||
6418 | |||
6419 | struct tpacpi_alsa_data { | ||
6420 | struct snd_card *card; | ||
6421 | struct snd_ctl_elem_id *ctl_mute_id; | ||
6422 | struct snd_ctl_elem_id *ctl_vol_id; | ||
6423 | }; | ||
6424 | |||
6425 | static struct snd_card *alsa_card; | ||
6426 | |||
6427 | enum { | ||
6428 | TP_EC_AUDIO = 0x30, | ||
6429 | |||
6430 | /* TP_EC_AUDIO bits */ | ||
6431 | TP_EC_AUDIO_MUTESW = 6, | ||
6432 | |||
6433 | /* TP_EC_AUDIO bitmasks */ | ||
6434 | TP_EC_AUDIO_LVL_MSK = 0x0F, | ||
6435 | TP_EC_AUDIO_MUTESW_MSK = (1 << TP_EC_AUDIO_MUTESW), | ||
6436 | |||
6437 | /* Maximum volume */ | ||
6438 | TP_EC_VOLUME_MAX = 14, | ||
6439 | }; | ||
6440 | |||
6441 | enum tpacpi_volume_access_mode { | ||
6442 | TPACPI_VOL_MODE_AUTO = 0, /* Not implemented yet */ | ||
6443 | TPACPI_VOL_MODE_EC, /* Pure EC control */ | ||
6444 | TPACPI_VOL_MODE_UCMS_STEP, /* UCMS step-based control: N/A */ | ||
6445 | TPACPI_VOL_MODE_ECNVRAM, /* EC control w/ NVRAM store */ | ||
6446 | TPACPI_VOL_MODE_MAX | ||
6447 | }; | ||
6448 | |||
6449 | enum tpacpi_volume_capabilities { | ||
6450 | TPACPI_VOL_CAP_AUTO = 0, /* Use white/blacklist */ | ||
6451 | TPACPI_VOL_CAP_VOLMUTE, /* Output vol and mute */ | ||
6452 | TPACPI_VOL_CAP_MUTEONLY, /* Output mute only */ | ||
6453 | TPACPI_VOL_CAP_MAX | ||
6454 | }; | ||
6455 | |||
6456 | static enum tpacpi_volume_access_mode volume_mode = | ||
6457 | TPACPI_VOL_MODE_MAX; | ||
6458 | |||
6459 | static enum tpacpi_volume_capabilities volume_capabilities; | ||
6460 | static int volume_control_allowed; | ||
6461 | |||
6462 | /* | ||
6463 | * Used to syncronize writers to TP_EC_AUDIO and | ||
6464 | * TP_NVRAM_ADDR_MIXER, as we need to do read-modify-write | ||
6465 | */ | ||
6466 | static struct mutex volume_mutex; | ||
6467 | |||
6468 | static void tpacpi_volume_checkpoint_nvram(void) | ||
6347 | { | 6469 | { |
6348 | int len = 0; | 6470 | u8 lec = 0; |
6349 | u8 level; | 6471 | u8 b_nvram; |
6472 | u8 ec_mask; | ||
6473 | |||
6474 | if (volume_mode != TPACPI_VOL_MODE_ECNVRAM) | ||
6475 | return; | ||
6476 | if (!volume_control_allowed) | ||
6477 | return; | ||
6478 | |||
6479 | vdbg_printk(TPACPI_DBG_MIXER, | ||
6480 | "trying to checkpoint mixer state to NVRAM...\n"); | ||
6481 | |||
6482 | if (tp_features.mixer_no_level_control) | ||
6483 | ec_mask = TP_EC_AUDIO_MUTESW_MSK; | ||
6484 | else | ||
6485 | ec_mask = TP_EC_AUDIO_MUTESW_MSK | TP_EC_AUDIO_LVL_MSK; | ||
6486 | |||
6487 | if (mutex_lock_killable(&volume_mutex) < 0) | ||
6488 | return; | ||
6350 | 6489 | ||
6351 | if (!acpi_ec_read(volume_offset, &level)) { | 6490 | if (unlikely(!acpi_ec_read(TP_EC_AUDIO, &lec))) |
6352 | len += sprintf(p + len, "level:\t\tunreadable\n"); | 6491 | goto unlock; |
6492 | lec &= ec_mask; | ||
6493 | b_nvram = nvram_read_byte(TP_NVRAM_ADDR_MIXER); | ||
6494 | |||
6495 | if (lec != (b_nvram & ec_mask)) { | ||
6496 | /* NVRAM needs update */ | ||
6497 | b_nvram &= ~ec_mask; | ||
6498 | b_nvram |= lec; | ||
6499 | nvram_write_byte(b_nvram, TP_NVRAM_ADDR_MIXER); | ||
6500 | dbg_printk(TPACPI_DBG_MIXER, | ||
6501 | "updated NVRAM mixer status to 0x%02x (0x%02x)\n", | ||
6502 | (unsigned int) lec, (unsigned int) b_nvram); | ||
6353 | } else { | 6503 | } else { |
6354 | len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); | 6504 | vdbg_printk(TPACPI_DBG_MIXER, |
6355 | len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); | 6505 | "NVRAM mixer status already is 0x%02x (0x%02x)\n", |
6356 | len += sprintf(p + len, "commands:\tup, down, mute\n"); | 6506 | (unsigned int) lec, (unsigned int) b_nvram); |
6357 | len += sprintf(p + len, "commands:\tlevel <level>" | ||
6358 | " (<level> is 0-15)\n"); | ||
6359 | } | 6507 | } |
6360 | 6508 | ||
6361 | return len; | 6509 | unlock: |
6510 | mutex_unlock(&volume_mutex); | ||
6362 | } | 6511 | } |
6363 | 6512 | ||
6364 | static int volume_write(char *buf) | 6513 | static int volume_get_status_ec(u8 *status) |
6365 | { | 6514 | { |
6366 | int cmos_cmd, inc, i; | 6515 | u8 s; |
6367 | u8 level, mute; | ||
6368 | int new_level, new_mute; | ||
6369 | char *cmd; | ||
6370 | 6516 | ||
6371 | while ((cmd = next_cmd(&buf))) { | 6517 | if (!acpi_ec_read(TP_EC_AUDIO, &s)) |
6372 | if (!acpi_ec_read(volume_offset, &level)) | 6518 | return -EIO; |
6373 | return -EIO; | ||
6374 | new_mute = mute = level & 0x40; | ||
6375 | new_level = level = level & 0xf; | ||
6376 | 6519 | ||
6377 | if (strlencmp(cmd, "up") == 0) { | 6520 | *status = s; |
6378 | if (mute) | ||
6379 | new_mute = 0; | ||
6380 | else | ||
6381 | new_level = level == 15 ? 15 : level + 1; | ||
6382 | } else if (strlencmp(cmd, "down") == 0) { | ||
6383 | if (mute) | ||
6384 | new_mute = 0; | ||
6385 | else | ||
6386 | new_level = level == 0 ? 0 : level - 1; | ||
6387 | } else if (sscanf(cmd, "level %d", &new_level) == 1 && | ||
6388 | new_level >= 0 && new_level <= 15) { | ||
6389 | /* new_level set */ | ||
6390 | } else if (strlencmp(cmd, "mute") == 0) { | ||
6391 | new_mute = 0x40; | ||
6392 | } else | ||
6393 | return -EINVAL; | ||
6394 | 6521 | ||
6395 | if (new_level != level) { | 6522 | dbg_printk(TPACPI_DBG_MIXER, "status 0x%02x\n", s); |
6396 | /* mute doesn't change */ | ||
6397 | 6523 | ||
6398 | cmos_cmd = (new_level > level) ? | 6524 | return 0; |
6399 | TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN; | 6525 | } |
6400 | inc = new_level > level ? 1 : -1; | ||
6401 | 6526 | ||
6402 | if (mute && (issue_thinkpad_cmos_command(cmos_cmd) || | 6527 | static int volume_get_status(u8 *status) |
6403 | !acpi_ec_write(volume_offset, level))) | 6528 | { |
6404 | return -EIO; | 6529 | return volume_get_status_ec(status); |
6530 | } | ||
6405 | 6531 | ||
6406 | for (i = level; i != new_level; i += inc) | 6532 | static int volume_set_status_ec(const u8 status) |
6407 | if (issue_thinkpad_cmos_command(cmos_cmd) || | 6533 | { |
6408 | !acpi_ec_write(volume_offset, i + inc)) | 6534 | if (!acpi_ec_write(TP_EC_AUDIO, status)) |
6409 | return -EIO; | 6535 | return -EIO; |
6410 | 6536 | ||
6411 | if (mute && | 6537 | dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status); |
6412 | (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) || | 6538 | |
6413 | !acpi_ec_write(volume_offset, new_level + mute))) { | 6539 | return 0; |
6414 | return -EIO; | 6540 | } |
6415 | } | 6541 | |
6542 | static int volume_set_status(const u8 status) | ||
6543 | { | ||
6544 | return volume_set_status_ec(status); | ||
6545 | } | ||
6546 | |||
6547 | /* returns < 0 on error, 0 on no change, 1 on change */ | ||
6548 | static int __volume_set_mute_ec(const bool mute) | ||
6549 | { | ||
6550 | int rc; | ||
6551 | u8 s, n; | ||
6552 | |||
6553 | if (mutex_lock_killable(&volume_mutex) < 0) | ||
6554 | return -EINTR; | ||
6555 | |||
6556 | rc = volume_get_status_ec(&s); | ||
6557 | if (rc) | ||
6558 | goto unlock; | ||
6559 | |||
6560 | n = (mute) ? s | TP_EC_AUDIO_MUTESW_MSK : | ||
6561 | s & ~TP_EC_AUDIO_MUTESW_MSK; | ||
6562 | |||
6563 | if (n != s) { | ||
6564 | rc = volume_set_status_ec(n); | ||
6565 | if (!rc) | ||
6566 | rc = 1; | ||
6567 | } | ||
6568 | |||
6569 | unlock: | ||
6570 | mutex_unlock(&volume_mutex); | ||
6571 | return rc; | ||
6572 | } | ||
6573 | |||
6574 | static int volume_alsa_set_mute(const bool mute) | ||
6575 | { | ||
6576 | dbg_printk(TPACPI_DBG_MIXER, "ALSA: trying to %smute\n", | ||
6577 | (mute) ? "" : "un"); | ||
6578 | return __volume_set_mute_ec(mute); | ||
6579 | } | ||
6580 | |||
6581 | static int volume_set_mute(const bool mute) | ||
6582 | { | ||
6583 | int rc; | ||
6584 | |||
6585 | dbg_printk(TPACPI_DBG_MIXER, "trying to %smute\n", | ||
6586 | (mute) ? "" : "un"); | ||
6587 | |||
6588 | rc = __volume_set_mute_ec(mute); | ||
6589 | return (rc < 0) ? rc : 0; | ||
6590 | } | ||
6591 | |||
6592 | /* returns < 0 on error, 0 on no change, 1 on change */ | ||
6593 | static int __volume_set_volume_ec(const u8 vol) | ||
6594 | { | ||
6595 | int rc; | ||
6596 | u8 s, n; | ||
6597 | |||
6598 | if (vol > TP_EC_VOLUME_MAX) | ||
6599 | return -EINVAL; | ||
6600 | |||
6601 | if (mutex_lock_killable(&volume_mutex) < 0) | ||
6602 | return -EINTR; | ||
6603 | |||
6604 | rc = volume_get_status_ec(&s); | ||
6605 | if (rc) | ||
6606 | goto unlock; | ||
6607 | |||
6608 | n = (s & ~TP_EC_AUDIO_LVL_MSK) | vol; | ||
6609 | |||
6610 | if (n != s) { | ||
6611 | rc = volume_set_status_ec(n); | ||
6612 | if (!rc) | ||
6613 | rc = 1; | ||
6614 | } | ||
6615 | |||
6616 | unlock: | ||
6617 | mutex_unlock(&volume_mutex); | ||
6618 | return rc; | ||
6619 | } | ||
6620 | |||
6621 | static int volume_alsa_set_volume(const u8 vol) | ||
6622 | { | ||
6623 | dbg_printk(TPACPI_DBG_MIXER, | ||
6624 | "ALSA: trying to set volume level to %hu\n", vol); | ||
6625 | return __volume_set_volume_ec(vol); | ||
6626 | } | ||
6627 | |||
6628 | static void volume_alsa_notify_change(void) | ||
6629 | { | ||
6630 | struct tpacpi_alsa_data *d; | ||
6631 | |||
6632 | if (alsa_card && alsa_card->private_data) { | ||
6633 | d = alsa_card->private_data; | ||
6634 | if (d->ctl_mute_id) | ||
6635 | snd_ctl_notify(alsa_card, | ||
6636 | SNDRV_CTL_EVENT_MASK_VALUE, | ||
6637 | d->ctl_mute_id); | ||
6638 | if (d->ctl_vol_id) | ||
6639 | snd_ctl_notify(alsa_card, | ||
6640 | SNDRV_CTL_EVENT_MASK_VALUE, | ||
6641 | d->ctl_vol_id); | ||
6642 | } | ||
6643 | } | ||
6644 | |||
6645 | static int volume_alsa_vol_info(struct snd_kcontrol *kcontrol, | ||
6646 | struct snd_ctl_elem_info *uinfo) | ||
6647 | { | ||
6648 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
6649 | uinfo->count = 1; | ||
6650 | uinfo->value.integer.min = 0; | ||
6651 | uinfo->value.integer.max = TP_EC_VOLUME_MAX; | ||
6652 | return 0; | ||
6653 | } | ||
6654 | |||
6655 | static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol, | ||
6656 | struct snd_ctl_elem_value *ucontrol) | ||
6657 | { | ||
6658 | u8 s; | ||
6659 | int rc; | ||
6660 | |||
6661 | rc = volume_get_status(&s); | ||
6662 | if (rc < 0) | ||
6663 | return rc; | ||
6664 | |||
6665 | ucontrol->value.integer.value[0] = s & TP_EC_AUDIO_LVL_MSK; | ||
6666 | return 0; | ||
6667 | } | ||
6668 | |||
6669 | static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol, | ||
6670 | struct snd_ctl_elem_value *ucontrol) | ||
6671 | { | ||
6672 | return volume_alsa_set_volume(ucontrol->value.integer.value[0]); | ||
6673 | } | ||
6674 | |||
6675 | #define volume_alsa_mute_info snd_ctl_boolean_mono_info | ||
6676 | |||
6677 | static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol, | ||
6678 | struct snd_ctl_elem_value *ucontrol) | ||
6679 | { | ||
6680 | u8 s; | ||
6681 | int rc; | ||
6682 | |||
6683 | rc = volume_get_status(&s); | ||
6684 | if (rc < 0) | ||
6685 | return rc; | ||
6686 | |||
6687 | ucontrol->value.integer.value[0] = | ||
6688 | (s & TP_EC_AUDIO_MUTESW_MSK) ? 0 : 1; | ||
6689 | return 0; | ||
6690 | } | ||
6691 | |||
6692 | static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol, | ||
6693 | struct snd_ctl_elem_value *ucontrol) | ||
6694 | { | ||
6695 | return volume_alsa_set_mute(!ucontrol->value.integer.value[0]); | ||
6696 | } | ||
6697 | |||
6698 | static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = { | ||
6699 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
6700 | .name = "Console Playback Volume", | ||
6701 | .index = 0, | ||
6702 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
6703 | .info = volume_alsa_vol_info, | ||
6704 | .get = volume_alsa_vol_get, | ||
6705 | }; | ||
6706 | |||
6707 | static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = { | ||
6708 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
6709 | .name = "Console Playback Switch", | ||
6710 | .index = 0, | ||
6711 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
6712 | .info = volume_alsa_mute_info, | ||
6713 | .get = volume_alsa_mute_get, | ||
6714 | }; | ||
6715 | |||
6716 | static void volume_suspend(pm_message_t state) | ||
6717 | { | ||
6718 | tpacpi_volume_checkpoint_nvram(); | ||
6719 | } | ||
6720 | |||
6721 | static void volume_resume(void) | ||
6722 | { | ||
6723 | volume_alsa_notify_change(); | ||
6724 | } | ||
6725 | |||
6726 | static void volume_shutdown(void) | ||
6727 | { | ||
6728 | tpacpi_volume_checkpoint_nvram(); | ||
6729 | } | ||
6730 | |||
6731 | static void volume_exit(void) | ||
6732 | { | ||
6733 | if (alsa_card) { | ||
6734 | snd_card_free(alsa_card); | ||
6735 | alsa_card = NULL; | ||
6736 | } | ||
6737 | |||
6738 | tpacpi_volume_checkpoint_nvram(); | ||
6739 | } | ||
6740 | |||
6741 | static int __init volume_create_alsa_mixer(void) | ||
6742 | { | ||
6743 | struct snd_card *card; | ||
6744 | struct tpacpi_alsa_data *data; | ||
6745 | struct snd_kcontrol *ctl_vol; | ||
6746 | struct snd_kcontrol *ctl_mute; | ||
6747 | int rc; | ||
6748 | |||
6749 | rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE, | ||
6750 | sizeof(struct tpacpi_alsa_data), &card); | ||
6751 | if (rc < 0 || !card) { | ||
6752 | printk(TPACPI_ERR | ||
6753 | "Failed to create ALSA card structures: %d\n", rc); | ||
6754 | return 1; | ||
6755 | } | ||
6756 | |||
6757 | BUG_ON(!card->private_data); | ||
6758 | data = card->private_data; | ||
6759 | data->card = card; | ||
6760 | |||
6761 | strlcpy(card->driver, TPACPI_ALSA_DRVNAME, | ||
6762 | sizeof(card->driver)); | ||
6763 | strlcpy(card->shortname, TPACPI_ALSA_SHRTNAME, | ||
6764 | sizeof(card->shortname)); | ||
6765 | snprintf(card->mixername, sizeof(card->mixername), "ThinkPad EC %s", | ||
6766 | (thinkpad_id.ec_version_str) ? | ||
6767 | thinkpad_id.ec_version_str : "(unknown)"); | ||
6768 | snprintf(card->longname, sizeof(card->longname), | ||
6769 | "%s at EC reg 0x%02x, fw %s", card->shortname, TP_EC_AUDIO, | ||
6770 | (thinkpad_id.ec_version_str) ? | ||
6771 | thinkpad_id.ec_version_str : "unknown"); | ||
6772 | |||
6773 | if (volume_control_allowed) { | ||
6774 | volume_alsa_control_vol.put = volume_alsa_vol_put; | ||
6775 | volume_alsa_control_vol.access = | ||
6776 | SNDRV_CTL_ELEM_ACCESS_READWRITE; | ||
6777 | |||
6778 | volume_alsa_control_mute.put = volume_alsa_mute_put; | ||
6779 | volume_alsa_control_mute.access = | ||
6780 | SNDRV_CTL_ELEM_ACCESS_READWRITE; | ||
6781 | } | ||
6782 | |||
6783 | if (!tp_features.mixer_no_level_control) { | ||
6784 | ctl_vol = snd_ctl_new1(&volume_alsa_control_vol, NULL); | ||
6785 | rc = snd_ctl_add(card, ctl_vol); | ||
6786 | if (rc < 0) { | ||
6787 | printk(TPACPI_ERR | ||
6788 | "Failed to create ALSA volume control: %d\n", | ||
6789 | rc); | ||
6790 | goto err_exit; | ||
6416 | } | 6791 | } |
6792 | data->ctl_vol_id = &ctl_vol->id; | ||
6793 | } | ||
6417 | 6794 | ||
6418 | if (new_mute != mute) { | 6795 | ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL); |
6419 | /* level doesn't change */ | 6796 | rc = snd_ctl_add(card, ctl_mute); |
6797 | if (rc < 0) { | ||
6798 | printk(TPACPI_ERR "Failed to create ALSA mute control: %d\n", | ||
6799 | rc); | ||
6800 | goto err_exit; | ||
6801 | } | ||
6802 | data->ctl_mute_id = &ctl_mute->id; | ||
6420 | 6803 | ||
6421 | cmos_cmd = (new_mute) ? | 6804 | snd_card_set_dev(card, &tpacpi_pdev->dev); |
6422 | TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP; | 6805 | rc = snd_card_register(card); |
6806 | if (rc < 0) { | ||
6807 | printk(TPACPI_ERR "Failed to register ALSA card: %d\n", rc); | ||
6808 | goto err_exit; | ||
6809 | } | ||
6423 | 6810 | ||
6424 | if (issue_thinkpad_cmos_command(cmos_cmd) || | 6811 | alsa_card = card; |
6425 | !acpi_ec_write(volume_offset, level + new_mute)) | 6812 | return 0; |
6426 | return -EIO; | 6813 | |
6814 | err_exit: | ||
6815 | snd_card_free(card); | ||
6816 | return 1; | ||
6817 | } | ||
6818 | |||
6819 | #define TPACPI_VOL_Q_MUTEONLY 0x0001 /* Mute-only control available */ | ||
6820 | #define TPACPI_VOL_Q_LEVEL 0x0002 /* Volume control available */ | ||
6821 | |||
6822 | static const struct tpacpi_quirk volume_quirk_table[] __initconst = { | ||
6823 | /* Whitelist volume level on all IBM by default */ | ||
6824 | { .vendor = PCI_VENDOR_ID_IBM, | ||
6825 | .bios = TPACPI_MATCH_ANY, | ||
6826 | .ec = TPACPI_MATCH_ANY, | ||
6827 | .quirks = TPACPI_VOL_Q_LEVEL }, | ||
6828 | |||
6829 | /* Lenovo models with volume control (needs confirmation) */ | ||
6830 | TPACPI_QEC_LNV('7', 'C', TPACPI_VOL_Q_LEVEL), /* R60/i */ | ||
6831 | TPACPI_QEC_LNV('7', 'E', TPACPI_VOL_Q_LEVEL), /* R60e/i */ | ||
6832 | TPACPI_QEC_LNV('7', '9', TPACPI_VOL_Q_LEVEL), /* T60/p */ | ||
6833 | TPACPI_QEC_LNV('7', 'B', TPACPI_VOL_Q_LEVEL), /* X60/s */ | ||
6834 | TPACPI_QEC_LNV('7', 'J', TPACPI_VOL_Q_LEVEL), /* X60t */ | ||
6835 | TPACPI_QEC_LNV('7', '7', TPACPI_VOL_Q_LEVEL), /* Z60 */ | ||
6836 | TPACPI_QEC_LNV('7', 'F', TPACPI_VOL_Q_LEVEL), /* Z61 */ | ||
6837 | |||
6838 | /* Whitelist mute-only on all Lenovo by default */ | ||
6839 | { .vendor = PCI_VENDOR_ID_LENOVO, | ||
6840 | .bios = TPACPI_MATCH_ANY, | ||
6841 | .ec = TPACPI_MATCH_ANY, | ||
6842 | .quirks = TPACPI_VOL_Q_MUTEONLY } | ||
6843 | }; | ||
6844 | |||
6845 | static int __init volume_init(struct ibm_init_struct *iibm) | ||
6846 | { | ||
6847 | unsigned long quirks; | ||
6848 | int rc; | ||
6849 | |||
6850 | vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n"); | ||
6851 | |||
6852 | mutex_init(&volume_mutex); | ||
6853 | |||
6854 | /* | ||
6855 | * Check for module parameter bogosity, note that we | ||
6856 | * init volume_mode to TPACPI_VOL_MODE_MAX in order to be | ||
6857 | * able to detect "unspecified" | ||
6858 | */ | ||
6859 | if (volume_mode > TPACPI_VOL_MODE_MAX) | ||
6860 | return -EINVAL; | ||
6861 | |||
6862 | if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) { | ||
6863 | printk(TPACPI_ERR | ||
6864 | "UCMS step volume mode not implemented, " | ||
6865 | "please contact %s\n", TPACPI_MAIL); | ||
6866 | return 1; | ||
6867 | } | ||
6868 | |||
6869 | if (volume_capabilities >= TPACPI_VOL_CAP_MAX) | ||
6870 | return -EINVAL; | ||
6871 | |||
6872 | /* | ||
6873 | * The ALSA mixer is our primary interface. | ||
6874 | * When disabled, don't install the subdriver at all | ||
6875 | */ | ||
6876 | if (!alsa_enable) { | ||
6877 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6878 | "ALSA mixer disabled by parameter, " | ||
6879 | "not loading volume subdriver...\n"); | ||
6880 | return 1; | ||
6881 | } | ||
6882 | |||
6883 | quirks = tpacpi_check_quirks(volume_quirk_table, | ||
6884 | ARRAY_SIZE(volume_quirk_table)); | ||
6885 | |||
6886 | switch (volume_capabilities) { | ||
6887 | case TPACPI_VOL_CAP_AUTO: | ||
6888 | if (quirks & TPACPI_VOL_Q_MUTEONLY) | ||
6889 | tp_features.mixer_no_level_control = 1; | ||
6890 | else if (quirks & TPACPI_VOL_Q_LEVEL) | ||
6891 | tp_features.mixer_no_level_control = 0; | ||
6892 | else | ||
6893 | return 1; /* no mixer */ | ||
6894 | break; | ||
6895 | case TPACPI_VOL_CAP_VOLMUTE: | ||
6896 | tp_features.mixer_no_level_control = 0; | ||
6897 | break; | ||
6898 | case TPACPI_VOL_CAP_MUTEONLY: | ||
6899 | tp_features.mixer_no_level_control = 1; | ||
6900 | break; | ||
6901 | default: | ||
6902 | return 1; | ||
6903 | } | ||
6904 | |||
6905 | if (volume_capabilities != TPACPI_VOL_CAP_AUTO) | ||
6906 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6907 | "using user-supplied volume_capabilities=%d\n", | ||
6908 | volume_capabilities); | ||
6909 | |||
6910 | if (volume_mode == TPACPI_VOL_MODE_AUTO || | ||
6911 | volume_mode == TPACPI_VOL_MODE_MAX) { | ||
6912 | volume_mode = TPACPI_VOL_MODE_ECNVRAM; | ||
6913 | |||
6914 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6915 | "driver auto-selected volume_mode=%d\n", | ||
6916 | volume_mode); | ||
6917 | } else { | ||
6918 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6919 | "using user-supplied volume_mode=%d\n", | ||
6920 | volume_mode); | ||
6921 | } | ||
6922 | |||
6923 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6924 | "mute is supported, volume control is %s\n", | ||
6925 | str_supported(!tp_features.mixer_no_level_control)); | ||
6926 | |||
6927 | rc = volume_create_alsa_mixer(); | ||
6928 | if (rc) { | ||
6929 | printk(TPACPI_ERR | ||
6930 | "Could not create the ALSA mixer interface\n"); | ||
6931 | return rc; | ||
6932 | } | ||
6933 | |||
6934 | printk(TPACPI_INFO | ||
6935 | "Console audio control enabled, mode: %s\n", | ||
6936 | (volume_control_allowed) ? | ||
6937 | "override (read/write)" : | ||
6938 | "monitor (read only)"); | ||
6939 | |||
6940 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6941 | "registering volume hotkeys as change notification\n"); | ||
6942 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | ||
6943 | | TP_ACPI_HKEY_VOLUP_MASK | ||
6944 | | TP_ACPI_HKEY_VOLDWN_MASK | ||
6945 | | TP_ACPI_HKEY_MUTE_MASK); | ||
6946 | |||
6947 | return 0; | ||
6948 | } | ||
6949 | |||
6950 | static int volume_read(struct seq_file *m) | ||
6951 | { | ||
6952 | u8 status; | ||
6953 | |||
6954 | if (volume_get_status(&status) < 0) { | ||
6955 | seq_printf(m, "level:\t\tunreadable\n"); | ||
6956 | } else { | ||
6957 | if (tp_features.mixer_no_level_control) | ||
6958 | seq_printf(m, "level:\t\tunsupported\n"); | ||
6959 | else | ||
6960 | seq_printf(m, "level:\t\t%d\n", | ||
6961 | status & TP_EC_AUDIO_LVL_MSK); | ||
6962 | |||
6963 | seq_printf(m, "mute:\t\t%s\n", | ||
6964 | onoff(status, TP_EC_AUDIO_MUTESW)); | ||
6965 | |||
6966 | if (volume_control_allowed) { | ||
6967 | seq_printf(m, "commands:\tunmute, mute\n"); | ||
6968 | if (!tp_features.mixer_no_level_control) { | ||
6969 | seq_printf(m, | ||
6970 | "commands:\tup, down\n"); | ||
6971 | seq_printf(m, | ||
6972 | "commands:\tlevel <level>" | ||
6973 | " (<level> is 0-%d)\n", | ||
6974 | TP_EC_VOLUME_MAX); | ||
6975 | } | ||
6427 | } | 6976 | } |
6428 | } | 6977 | } |
6429 | 6978 | ||
6430 | return 0; | 6979 | return 0; |
6431 | } | 6980 | } |
6432 | 6981 | ||
6982 | static int volume_write(char *buf) | ||
6983 | { | ||
6984 | u8 s; | ||
6985 | u8 new_level, new_mute; | ||
6986 | int l; | ||
6987 | char *cmd; | ||
6988 | int rc; | ||
6989 | |||
6990 | /* | ||
6991 | * We do allow volume control at driver startup, so that the | ||
6992 | * user can set initial state through the volume=... parameter hack. | ||
6993 | */ | ||
6994 | if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) { | ||
6995 | if (unlikely(!tp_warned.volume_ctrl_forbidden)) { | ||
6996 | tp_warned.volume_ctrl_forbidden = 1; | ||
6997 | printk(TPACPI_NOTICE | ||
6998 | "Console audio control in monitor mode, " | ||
6999 | "changes are not allowed.\n"); | ||
7000 | printk(TPACPI_NOTICE | ||
7001 | "Use the volume_control=1 module parameter " | ||
7002 | "to enable volume control\n"); | ||
7003 | } | ||
7004 | return -EPERM; | ||
7005 | } | ||
7006 | |||
7007 | rc = volume_get_status(&s); | ||
7008 | if (rc < 0) | ||
7009 | return rc; | ||
7010 | |||
7011 | new_level = s & TP_EC_AUDIO_LVL_MSK; | ||
7012 | new_mute = s & TP_EC_AUDIO_MUTESW_MSK; | ||
7013 | |||
7014 | while ((cmd = next_cmd(&buf))) { | ||
7015 | if (!tp_features.mixer_no_level_control) { | ||
7016 | if (strlencmp(cmd, "up") == 0) { | ||
7017 | if (new_mute) | ||
7018 | new_mute = 0; | ||
7019 | else if (new_level < TP_EC_VOLUME_MAX) | ||
7020 | new_level++; | ||
7021 | continue; | ||
7022 | } else if (strlencmp(cmd, "down") == 0) { | ||
7023 | if (new_mute) | ||
7024 | new_mute = 0; | ||
7025 | else if (new_level > 0) | ||
7026 | new_level--; | ||
7027 | continue; | ||
7028 | } else if (sscanf(cmd, "level %u", &l) == 1 && | ||
7029 | l >= 0 && l <= TP_EC_VOLUME_MAX) { | ||
7030 | new_level = l; | ||
7031 | continue; | ||
7032 | } | ||
7033 | } | ||
7034 | if (strlencmp(cmd, "mute") == 0) | ||
7035 | new_mute = TP_EC_AUDIO_MUTESW_MSK; | ||
7036 | else if (strlencmp(cmd, "unmute") == 0) | ||
7037 | new_mute = 0; | ||
7038 | else | ||
7039 | return -EINVAL; | ||
7040 | } | ||
7041 | |||
7042 | if (tp_features.mixer_no_level_control) { | ||
7043 | tpacpi_disclose_usertask("procfs volume", "%smute\n", | ||
7044 | new_mute ? "" : "un"); | ||
7045 | rc = volume_set_mute(!!new_mute); | ||
7046 | } else { | ||
7047 | tpacpi_disclose_usertask("procfs volume", | ||
7048 | "%smute and set level to %d\n", | ||
7049 | new_mute ? "" : "un", new_level); | ||
7050 | rc = volume_set_status(new_mute | new_level); | ||
7051 | } | ||
7052 | volume_alsa_notify_change(); | ||
7053 | |||
7054 | return (rc == -EINTR) ? -ERESTARTSYS : rc; | ||
7055 | } | ||
7056 | |||
6433 | static struct ibm_struct volume_driver_data = { | 7057 | static struct ibm_struct volume_driver_data = { |
6434 | .name = "volume", | 7058 | .name = "volume", |
6435 | .read = volume_read, | 7059 | .read = volume_read, |
6436 | .write = volume_write, | 7060 | .write = volume_write, |
7061 | .exit = volume_exit, | ||
7062 | .suspend = volume_suspend, | ||
7063 | .resume = volume_resume, | ||
7064 | .shutdown = volume_shutdown, | ||
7065 | }; | ||
7066 | |||
7067 | #else /* !CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */ | ||
7068 | |||
7069 | #define alsa_card NULL | ||
7070 | |||
7071 | static void inline volume_alsa_notify_change(void) | ||
7072 | { | ||
7073 | } | ||
7074 | |||
7075 | static int __init volume_init(struct ibm_init_struct *iibm) | ||
7076 | { | ||
7077 | printk(TPACPI_INFO | ||
7078 | "volume: disabled as there is no ALSA support in this kernel\n"); | ||
7079 | |||
7080 | return 1; | ||
7081 | } | ||
7082 | |||
7083 | static struct ibm_struct volume_driver_data = { | ||
7084 | .name = "volume", | ||
6437 | }; | 7085 | }; |
6438 | 7086 | ||
7087 | #endif /* CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */ | ||
7088 | |||
6439 | /************************************************************************* | 7089 | /************************************************************************* |
6440 | * Fan subdriver | 7090 | * Fan subdriver |
6441 | */ | 7091 | */ |
@@ -6461,7 +7111,7 @@ static struct ibm_struct volume_driver_data = { | |||
6461 | * | 7111 | * |
6462 | * Fan speed changes of any sort (including those caused by the | 7112 | * Fan speed changes of any sort (including those caused by the |
6463 | * disengaged mode) are usually done slowly by the firmware as the | 7113 | * disengaged mode) are usually done slowly by the firmware as the |
6464 | * maximum ammount of fan duty cycle change per second seems to be | 7114 | * maximum amount of fan duty cycle change per second seems to be |
6465 | * limited. | 7115 | * limited. |
6466 | * | 7116 | * |
6467 | * Reading is not available if GFAN exists. | 7117 | * Reading is not available if GFAN exists. |
@@ -6545,7 +7195,7 @@ static struct ibm_struct volume_driver_data = { | |||
6545 | * The speeds are stored on handles | 7195 | * The speeds are stored on handles |
6546 | * (FANA:FAN9), (FANC:FANB), (FANE:FAND). | 7196 | * (FANA:FAN9), (FANC:FANB), (FANE:FAND). |
6547 | * | 7197 | * |
6548 | * There are three default speed sets, acessible as handles: | 7198 | * There are three default speed sets, accessible as handles: |
6549 | * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H | 7199 | * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H |
6550 | * | 7200 | * |
6551 | * ACPI DSDT switches which set is in use depending on various | 7201 | * ACPI DSDT switches which set is in use depending on various |
@@ -7510,9 +8160,8 @@ static void fan_resume(void) | |||
7510 | } | 8160 | } |
7511 | } | 8161 | } |
7512 | 8162 | ||
7513 | static int fan_read(char *p) | 8163 | static int fan_read(struct seq_file *m) |
7514 | { | 8164 | { |
7515 | int len = 0; | ||
7516 | int rc; | 8165 | int rc; |
7517 | u8 status; | 8166 | u8 status; |
7518 | unsigned int speed = 0; | 8167 | unsigned int speed = 0; |
@@ -7524,7 +8173,7 @@ static int fan_read(char *p) | |||
7524 | if (rc < 0) | 8173 | if (rc < 0) |
7525 | return rc; | 8174 | return rc; |
7526 | 8175 | ||
7527 | len += sprintf(p + len, "status:\t\t%s\n" | 8176 | seq_printf(m, "status:\t\t%s\n" |
7528 | "level:\t\t%d\n", | 8177 | "level:\t\t%d\n", |
7529 | (status != 0) ? "enabled" : "disabled", status); | 8178 | (status != 0) ? "enabled" : "disabled", status); |
7530 | break; | 8179 | break; |
@@ -7535,54 +8184,54 @@ static int fan_read(char *p) | |||
7535 | if (rc < 0) | 8184 | if (rc < 0) |
7536 | return rc; | 8185 | return rc; |
7537 | 8186 | ||
7538 | len += sprintf(p + len, "status:\t\t%s\n", | 8187 | seq_printf(m, "status:\t\t%s\n", |
7539 | (status != 0) ? "enabled" : "disabled"); | 8188 | (status != 0) ? "enabled" : "disabled"); |
7540 | 8189 | ||
7541 | rc = fan_get_speed(&speed); | 8190 | rc = fan_get_speed(&speed); |
7542 | if (rc < 0) | 8191 | if (rc < 0) |
7543 | return rc; | 8192 | return rc; |
7544 | 8193 | ||
7545 | len += sprintf(p + len, "speed:\t\t%d\n", speed); | 8194 | seq_printf(m, "speed:\t\t%d\n", speed); |
7546 | 8195 | ||
7547 | if (status & TP_EC_FAN_FULLSPEED) | 8196 | if (status & TP_EC_FAN_FULLSPEED) |
7548 | /* Disengaged mode takes precedence */ | 8197 | /* Disengaged mode takes precedence */ |
7549 | len += sprintf(p + len, "level:\t\tdisengaged\n"); | 8198 | seq_printf(m, "level:\t\tdisengaged\n"); |
7550 | else if (status & TP_EC_FAN_AUTO) | 8199 | else if (status & TP_EC_FAN_AUTO) |
7551 | len += sprintf(p + len, "level:\t\tauto\n"); | 8200 | seq_printf(m, "level:\t\tauto\n"); |
7552 | else | 8201 | else |
7553 | len += sprintf(p + len, "level:\t\t%d\n", status); | 8202 | seq_printf(m, "level:\t\t%d\n", status); |
7554 | break; | 8203 | break; |
7555 | 8204 | ||
7556 | case TPACPI_FAN_NONE: | 8205 | case TPACPI_FAN_NONE: |
7557 | default: | 8206 | default: |
7558 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 8207 | seq_printf(m, "status:\t\tnot supported\n"); |
7559 | } | 8208 | } |
7560 | 8209 | ||
7561 | if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) { | 8210 | if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) { |
7562 | len += sprintf(p + len, "commands:\tlevel <level>"); | 8211 | seq_printf(m, "commands:\tlevel <level>"); |
7563 | 8212 | ||
7564 | switch (fan_control_access_mode) { | 8213 | switch (fan_control_access_mode) { |
7565 | case TPACPI_FAN_WR_ACPI_SFAN: | 8214 | case TPACPI_FAN_WR_ACPI_SFAN: |
7566 | len += sprintf(p + len, " (<level> is 0-7)\n"); | 8215 | seq_printf(m, " (<level> is 0-7)\n"); |
7567 | break; | 8216 | break; |
7568 | 8217 | ||
7569 | default: | 8218 | default: |
7570 | len += sprintf(p + len, " (<level> is 0-7, " | 8219 | seq_printf(m, " (<level> is 0-7, " |
7571 | "auto, disengaged, full-speed)\n"); | 8220 | "auto, disengaged, full-speed)\n"); |
7572 | break; | 8221 | break; |
7573 | } | 8222 | } |
7574 | } | 8223 | } |
7575 | 8224 | ||
7576 | if (fan_control_commands & TPACPI_FAN_CMD_ENABLE) | 8225 | if (fan_control_commands & TPACPI_FAN_CMD_ENABLE) |
7577 | len += sprintf(p + len, "commands:\tenable, disable\n" | 8226 | seq_printf(m, "commands:\tenable, disable\n" |
7578 | "commands:\twatchdog <timeout> (<timeout> " | 8227 | "commands:\twatchdog <timeout> (<timeout> " |
7579 | "is 0 (off), 1-120 (seconds))\n"); | 8228 | "is 0 (off), 1-120 (seconds))\n"); |
7580 | 8229 | ||
7581 | if (fan_control_commands & TPACPI_FAN_CMD_SPEED) | 8230 | if (fan_control_commands & TPACPI_FAN_CMD_SPEED) |
7582 | len += sprintf(p + len, "commands:\tspeed <speed>" | 8231 | seq_printf(m, "commands:\tspeed <speed>" |
7583 | " (<speed> is 0-65535)\n"); | 8232 | " (<speed> is 0-65535)\n"); |
7584 | 8233 | ||
7585 | return len; | 8234 | return 0; |
7586 | } | 8235 | } |
7587 | 8236 | ||
7588 | static int fan_write_cmd_level(const char *cmd, int *rc) | 8237 | static int fan_write_cmd_level(const char *cmd, int *rc) |
@@ -7724,10 +8373,23 @@ static struct ibm_struct fan_driver_data = { | |||
7724 | */ | 8373 | */ |
7725 | static void tpacpi_driver_event(const unsigned int hkey_event) | 8374 | static void tpacpi_driver_event(const unsigned int hkey_event) |
7726 | { | 8375 | { |
8376 | if (ibm_backlight_device) { | ||
8377 | switch (hkey_event) { | ||
8378 | case TP_HKEY_EV_BRGHT_UP: | ||
8379 | case TP_HKEY_EV_BRGHT_DOWN: | ||
8380 | tpacpi_brightness_notify_change(); | ||
8381 | } | ||
8382 | } | ||
8383 | if (alsa_card) { | ||
8384 | switch (hkey_event) { | ||
8385 | case TP_HKEY_EV_VOL_UP: | ||
8386 | case TP_HKEY_EV_VOL_DOWN: | ||
8387 | case TP_HKEY_EV_VOL_MUTE: | ||
8388 | volume_alsa_notify_change(); | ||
8389 | } | ||
8390 | } | ||
7727 | } | 8391 | } |
7728 | 8392 | ||
7729 | |||
7730 | |||
7731 | static void hotkey_driver_event(const unsigned int scancode) | 8393 | static void hotkey_driver_event(const unsigned int scancode) |
7732 | { | 8394 | { |
7733 | tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode); | 8395 | tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode); |
@@ -7856,19 +8518,20 @@ static int __init ibm_init(struct ibm_init_struct *iibm) | |||
7856 | "%s installed\n", ibm->name); | 8518 | "%s installed\n", ibm->name); |
7857 | 8519 | ||
7858 | if (ibm->read) { | 8520 | if (ibm->read) { |
7859 | entry = create_proc_entry(ibm->name, | 8521 | mode_t mode = iibm->base_procfs_mode; |
7860 | S_IFREG | S_IRUGO | S_IWUSR, | 8522 | |
7861 | proc_dir); | 8523 | if (!mode) |
8524 | mode = S_IRUGO; | ||
8525 | if (ibm->write) | ||
8526 | mode |= S_IWUSR; | ||
8527 | entry = proc_create_data(ibm->name, mode, proc_dir, | ||
8528 | &dispatch_proc_fops, ibm); | ||
7862 | if (!entry) { | 8529 | if (!entry) { |
7863 | printk(TPACPI_ERR "unable to create proc entry %s\n", | 8530 | printk(TPACPI_ERR "unable to create proc entry %s\n", |
7864 | ibm->name); | 8531 | ibm->name); |
7865 | ret = -ENODEV; | 8532 | ret = -ENODEV; |
7866 | goto err_out; | 8533 | goto err_out; |
7867 | } | 8534 | } |
7868 | entry->data = ibm; | ||
7869 | entry->read_proc = &dispatch_procfs_read; | ||
7870 | if (ibm->write) | ||
7871 | entry->write_proc = &dispatch_procfs_write; | ||
7872 | ibm->flags.proc_created = 1; | 8535 | ibm->flags.proc_created = 1; |
7873 | } | 8536 | } |
7874 | 8537 | ||
@@ -8049,6 +8712,7 @@ static struct ibm_init_struct ibms_init[] __initdata = { | |||
8049 | #ifdef CONFIG_THINKPAD_ACPI_VIDEO | 8712 | #ifdef CONFIG_THINKPAD_ACPI_VIDEO |
8050 | { | 8713 | { |
8051 | .init = video_init, | 8714 | .init = video_init, |
8715 | .base_procfs_mode = S_IRUSR, | ||
8052 | .data = &video_driver_data, | 8716 | .data = &video_driver_data, |
8053 | }, | 8717 | }, |
8054 | #endif | 8718 | #endif |
@@ -8080,6 +8744,7 @@ static struct ibm_init_struct ibms_init[] __initdata = { | |||
8080 | .data = &brightness_driver_data, | 8744 | .data = &brightness_driver_data, |
8081 | }, | 8745 | }, |
8082 | { | 8746 | { |
8747 | .init = volume_init, | ||
8083 | .data = &volume_driver_data, | 8748 | .data = &volume_driver_data, |
8084 | }, | 8749 | }, |
8085 | { | 8750 | { |
@@ -8115,36 +8780,61 @@ static int __init set_ibm_param(const char *val, struct kernel_param *kp) | |||
8115 | return -EINVAL; | 8780 | return -EINVAL; |
8116 | } | 8781 | } |
8117 | 8782 | ||
8118 | module_param(experimental, int, 0); | 8783 | module_param(experimental, int, 0444); |
8119 | MODULE_PARM_DESC(experimental, | 8784 | MODULE_PARM_DESC(experimental, |
8120 | "Enables experimental features when non-zero"); | 8785 | "Enables experimental features when non-zero"); |
8121 | 8786 | ||
8122 | module_param_named(debug, dbg_level, uint, 0); | 8787 | module_param_named(debug, dbg_level, uint, 0); |
8123 | MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); | 8788 | MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); |
8124 | 8789 | ||
8125 | module_param(force_load, bool, 0); | 8790 | module_param(force_load, bool, 0444); |
8126 | MODULE_PARM_DESC(force_load, | 8791 | MODULE_PARM_DESC(force_load, |
8127 | "Attempts to load the driver even on a " | 8792 | "Attempts to load the driver even on a " |
8128 | "mis-identified ThinkPad when true"); | 8793 | "mis-identified ThinkPad when true"); |
8129 | 8794 | ||
8130 | module_param_named(fan_control, fan_control_allowed, bool, 0); | 8795 | module_param_named(fan_control, fan_control_allowed, bool, 0444); |
8131 | MODULE_PARM_DESC(fan_control, | 8796 | MODULE_PARM_DESC(fan_control, |
8132 | "Enables setting fan parameters features when true"); | 8797 | "Enables setting fan parameters features when true"); |
8133 | 8798 | ||
8134 | module_param_named(brightness_mode, brightness_mode, uint, 0); | 8799 | module_param_named(brightness_mode, brightness_mode, uint, 0444); |
8135 | MODULE_PARM_DESC(brightness_mode, | 8800 | MODULE_PARM_DESC(brightness_mode, |
8136 | "Selects brightness control strategy: " | 8801 | "Selects brightness control strategy: " |
8137 | "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM"); | 8802 | "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM"); |
8138 | 8803 | ||
8139 | module_param(brightness_enable, uint, 0); | 8804 | module_param(brightness_enable, uint, 0444); |
8140 | MODULE_PARM_DESC(brightness_enable, | 8805 | MODULE_PARM_DESC(brightness_enable, |
8141 | "Enables backlight control when 1, disables when 0"); | 8806 | "Enables backlight control when 1, disables when 0"); |
8142 | 8807 | ||
8143 | module_param(hotkey_report_mode, uint, 0); | 8808 | module_param(hotkey_report_mode, uint, 0444); |
8144 | MODULE_PARM_DESC(hotkey_report_mode, | 8809 | MODULE_PARM_DESC(hotkey_report_mode, |
8145 | "used for backwards compatibility with userspace, " | 8810 | "used for backwards compatibility with userspace, " |
8146 | "see documentation"); | 8811 | "see documentation"); |
8147 | 8812 | ||
8813 | #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT | ||
8814 | module_param_named(volume_mode, volume_mode, uint, 0444); | ||
8815 | MODULE_PARM_DESC(volume_mode, | ||
8816 | "Selects volume control strategy: " | ||
8817 | "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM"); | ||
8818 | |||
8819 | module_param_named(volume_capabilities, volume_capabilities, uint, 0444); | ||
8820 | MODULE_PARM_DESC(volume_capabilities, | ||
8821 | "Selects the mixer capabilites: " | ||
8822 | "0=auto, 1=volume and mute, 2=mute only"); | ||
8823 | |||
8824 | module_param_named(volume_control, volume_control_allowed, bool, 0444); | ||
8825 | MODULE_PARM_DESC(volume_control, | ||
8826 | "Enables software override for the console audio " | ||
8827 | "control when true"); | ||
8828 | |||
8829 | /* ALSA module API parameters */ | ||
8830 | module_param_named(index, alsa_index, int, 0444); | ||
8831 | MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer"); | ||
8832 | module_param_named(id, alsa_id, charp, 0444); | ||
8833 | MODULE_PARM_DESC(id, "ALSA id for the ACPI EC Mixer"); | ||
8834 | module_param_named(enable, alsa_enable, bool, 0444); | ||
8835 | MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer"); | ||
8836 | #endif /* CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */ | ||
8837 | |||
8148 | #define TPACPI_PARAM(feature) \ | 8838 | #define TPACPI_PARAM(feature) \ |
8149 | module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ | 8839 | module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ |
8150 | MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ | 8840 | MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ |
@@ -8163,25 +8853,25 @@ TPACPI_PARAM(volume); | |||
8163 | TPACPI_PARAM(fan); | 8853 | TPACPI_PARAM(fan); |
8164 | 8854 | ||
8165 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 8855 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
8166 | module_param(dbg_wlswemul, uint, 0); | 8856 | module_param(dbg_wlswemul, uint, 0444); |
8167 | MODULE_PARM_DESC(dbg_wlswemul, "Enables WLSW emulation"); | 8857 | MODULE_PARM_DESC(dbg_wlswemul, "Enables WLSW emulation"); |
8168 | module_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0); | 8858 | module_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0); |
8169 | MODULE_PARM_DESC(wlsw_state, | 8859 | MODULE_PARM_DESC(wlsw_state, |
8170 | "Initial state of the emulated WLSW switch"); | 8860 | "Initial state of the emulated WLSW switch"); |
8171 | 8861 | ||
8172 | module_param(dbg_bluetoothemul, uint, 0); | 8862 | module_param(dbg_bluetoothemul, uint, 0444); |
8173 | MODULE_PARM_DESC(dbg_bluetoothemul, "Enables bluetooth switch emulation"); | 8863 | MODULE_PARM_DESC(dbg_bluetoothemul, "Enables bluetooth switch emulation"); |
8174 | module_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0); | 8864 | module_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0); |
8175 | MODULE_PARM_DESC(bluetooth_state, | 8865 | MODULE_PARM_DESC(bluetooth_state, |
8176 | "Initial state of the emulated bluetooth switch"); | 8866 | "Initial state of the emulated bluetooth switch"); |
8177 | 8867 | ||
8178 | module_param(dbg_wwanemul, uint, 0); | 8868 | module_param(dbg_wwanemul, uint, 0444); |
8179 | MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation"); | 8869 | MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation"); |
8180 | module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); | 8870 | module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); |
8181 | MODULE_PARM_DESC(wwan_state, | 8871 | MODULE_PARM_DESC(wwan_state, |
8182 | "Initial state of the emulated WWAN switch"); | 8872 | "Initial state of the emulated WWAN switch"); |
8183 | 8873 | ||
8184 | module_param(dbg_uwbemul, uint, 0); | 8874 | module_param(dbg_uwbemul, uint, 0444); |
8185 | MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation"); | 8875 | MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation"); |
8186 | module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0); | 8876 | module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0); |
8187 | MODULE_PARM_DESC(uwb_state, | 8877 | MODULE_PARM_DESC(uwb_state, |
@@ -8374,6 +9064,7 @@ static int __init thinkpad_acpi_module_init(void) | |||
8374 | PCI_VENDOR_ID_IBM; | 9064 | PCI_VENDOR_ID_IBM; |
8375 | tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; | 9065 | tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; |
8376 | tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; | 9066 | tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; |
9067 | tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev; | ||
8377 | } | 9068 | } |
8378 | for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { | 9069 | for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { |
8379 | ret = ibm_init(&ibms_init[i]); | 9070 | ret = ibm_init(&ibms_init[i]); |
@@ -8384,6 +9075,9 @@ static int __init thinkpad_acpi_module_init(void) | |||
8384 | return ret; | 9075 | return ret; |
8385 | } | 9076 | } |
8386 | } | 9077 | } |
9078 | |||
9079 | tpacpi_lifecycle = TPACPI_LIFE_RUNNING; | ||
9080 | |||
8387 | ret = input_register_device(tpacpi_inputdev); | 9081 | ret = input_register_device(tpacpi_inputdev); |
8388 | if (ret < 0) { | 9082 | if (ret < 0) { |
8389 | printk(TPACPI_ERR "unable to register input device\n"); | 9083 | printk(TPACPI_ERR "unable to register input device\n"); |
@@ -8393,7 +9087,6 @@ static int __init thinkpad_acpi_module_init(void) | |||
8393 | tp_features.input_device_registered = 1; | 9087 | tp_features.input_device_registered = 1; |
8394 | } | 9088 | } |
8395 | 9089 | ||
8396 | tpacpi_lifecycle = TPACPI_LIFE_RUNNING; | ||
8397 | return 0; | 9090 | return 0; |
8398 | } | 9091 | } |
8399 | 9092 | ||
diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index 02f3d4e9e666..ff4b476f1950 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
19 | #include <linux/slab.h> | ||
19 | #include <linux/acpi.h> | 20 | #include <linux/acpi.h> |
20 | #include <linux/input.h> | 21 | #include <linux/input.h> |
21 | 22 | ||
@@ -46,7 +47,7 @@ static struct tps_key_entry topstar_keymap[] = { | |||
46 | { } | 47 | { } |
47 | }; | 48 | }; |
48 | 49 | ||
49 | static struct tps_key_entry *tps_get_key_by_scancode(int code) | 50 | static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code) |
50 | { | 51 | { |
51 | struct tps_key_entry *key; | 52 | struct tps_key_entry *key; |
52 | 53 | ||
@@ -57,7 +58,7 @@ static struct tps_key_entry *tps_get_key_by_scancode(int code) | |||
57 | return NULL; | 58 | return NULL; |
58 | } | 59 | } |
59 | 60 | ||
60 | static struct tps_key_entry *tps_get_key_by_keycode(int code) | 61 | static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code) |
61 | { | 62 | { |
62 | struct tps_key_entry *key; | 63 | struct tps_key_entry *key; |
63 | 64 | ||
@@ -126,7 +127,8 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) | |||
126 | return 0; | 127 | return 0; |
127 | } | 128 | } |
128 | 129 | ||
129 | static int topstar_getkeycode(struct input_dev *dev, int scancode, int *keycode) | 130 | static int topstar_getkeycode(struct input_dev *dev, |
131 | unsigned int scancode, unsigned int *keycode) | ||
130 | { | 132 | { |
131 | struct tps_key_entry *key = tps_get_key_by_scancode(scancode); | 133 | struct tps_key_entry *key = tps_get_key_by_scancode(scancode); |
132 | 134 | ||
@@ -137,14 +139,12 @@ static int topstar_getkeycode(struct input_dev *dev, int scancode, int *keycode) | |||
137 | return 0; | 139 | return 0; |
138 | } | 140 | } |
139 | 141 | ||
140 | static int topstar_setkeycode(struct input_dev *dev, int scancode, int keycode) | 142 | static int topstar_setkeycode(struct input_dev *dev, |
143 | unsigned int scancode, unsigned int keycode) | ||
141 | { | 144 | { |
142 | struct tps_key_entry *key; | 145 | struct tps_key_entry *key; |
143 | int old_keycode; | 146 | int old_keycode; |
144 | 147 | ||
145 | if (keycode < 0 || keycode > KEY_MAX) | ||
146 | return -EINVAL; | ||
147 | |||
148 | key = tps_get_key_by_scancode(scancode); | 148 | key = tps_get_key_by_scancode(scancode); |
149 | 149 | ||
150 | if (!key) | 150 | if (!key) |
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 51c0a8bee414..37aa14798551 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
@@ -42,9 +42,12 @@ | |||
42 | #include <linux/init.h> | 42 | #include <linux/init.h> |
43 | #include <linux/types.h> | 43 | #include <linux/types.h> |
44 | #include <linux/proc_fs.h> | 44 | #include <linux/proc_fs.h> |
45 | #include <linux/seq_file.h> | ||
45 | #include <linux/backlight.h> | 46 | #include <linux/backlight.h> |
46 | #include <linux/platform_device.h> | 47 | #include <linux/platform_device.h> |
47 | #include <linux/rfkill.h> | 48 | #include <linux/rfkill.h> |
49 | #include <linux/input.h> | ||
50 | #include <linux/slab.h> | ||
48 | 51 | ||
49 | #include <asm/uaccess.h> | 52 | #include <asm/uaccess.h> |
50 | 53 | ||
@@ -61,9 +64,10 @@ MODULE_LICENSE("GPL"); | |||
61 | 64 | ||
62 | /* Toshiba ACPI method paths */ | 65 | /* Toshiba ACPI method paths */ |
63 | #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" | 66 | #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" |
64 | #define METHOD_HCI_1 "\\_SB_.VALD.GHCI" | 67 | #define TOSH_INTERFACE_1 "\\_SB_.VALD" |
65 | #define METHOD_HCI_2 "\\_SB_.VALZ.GHCI" | 68 | #define TOSH_INTERFACE_2 "\\_SB_.VALZ" |
66 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" | 69 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" |
70 | #define GHCI_METHOD ".GHCI" | ||
67 | 71 | ||
68 | /* Toshiba HCI interface definitions | 72 | /* Toshiba HCI interface definitions |
69 | * | 73 | * |
@@ -115,6 +119,36 @@ static const struct acpi_device_id toshiba_device_ids[] = { | |||
115 | }; | 119 | }; |
116 | MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); | 120 | MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); |
117 | 121 | ||
122 | struct key_entry { | ||
123 | char type; | ||
124 | u16 code; | ||
125 | u16 keycode; | ||
126 | }; | ||
127 | |||
128 | enum {KE_KEY, KE_END}; | ||
129 | |||
130 | static struct key_entry toshiba_acpi_keymap[] = { | ||
131 | {KE_KEY, 0x101, KEY_MUTE}, | ||
132 | {KE_KEY, 0x13b, KEY_COFFEE}, | ||
133 | {KE_KEY, 0x13c, KEY_BATTERY}, | ||
134 | {KE_KEY, 0x13d, KEY_SLEEP}, | ||
135 | {KE_KEY, 0x13e, KEY_SUSPEND}, | ||
136 | {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE}, | ||
137 | {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN}, | ||
138 | {KE_KEY, 0x141, KEY_BRIGHTNESSUP}, | ||
139 | {KE_KEY, 0x142, KEY_WLAN}, | ||
140 | {KE_KEY, 0x143, KEY_PROG1}, | ||
141 | {KE_KEY, 0xb05, KEY_PROG2}, | ||
142 | {KE_KEY, 0xb06, KEY_WWW}, | ||
143 | {KE_KEY, 0xb07, KEY_MAIL}, | ||
144 | {KE_KEY, 0xb30, KEY_STOP}, | ||
145 | {KE_KEY, 0xb31, KEY_PREVIOUSSONG}, | ||
146 | {KE_KEY, 0xb32, KEY_NEXTSONG}, | ||
147 | {KE_KEY, 0xb33, KEY_PLAYPAUSE}, | ||
148 | {KE_KEY, 0xb5a, KEY_MEDIA}, | ||
149 | {KE_END, 0, 0}, | ||
150 | }; | ||
151 | |||
118 | /* utility | 152 | /* utility |
119 | */ | 153 | */ |
120 | 154 | ||
@@ -250,6 +284,8 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result) | |||
250 | struct toshiba_acpi_dev { | 284 | struct toshiba_acpi_dev { |
251 | struct platform_device *p_dev; | 285 | struct platform_device *p_dev; |
252 | struct rfkill *bt_rfk; | 286 | struct rfkill *bt_rfk; |
287 | struct input_dev *hotkey_dev; | ||
288 | acpi_handle handle; | ||
253 | 289 | ||
254 | const char *bt_name; | 290 | const char *bt_name; |
255 | 291 | ||
@@ -357,63 +393,6 @@ static int force_fan; | |||
357 | static int last_key_event; | 393 | static int last_key_event; |
358 | static int key_event_valid; | 394 | static int key_event_valid; |
359 | 395 | ||
360 | typedef struct _ProcItem { | ||
361 | const char *name; | ||
362 | char *(*read_func) (char *); | ||
363 | unsigned long (*write_func) (const char *, unsigned long); | ||
364 | } ProcItem; | ||
365 | |||
366 | /* proc file handlers | ||
367 | */ | ||
368 | |||
369 | static int | ||
370 | dispatch_read(char *page, char **start, off_t off, int count, int *eof, | ||
371 | ProcItem * item) | ||
372 | { | ||
373 | char *p = page; | ||
374 | int len; | ||
375 | |||
376 | if (off == 0) | ||
377 | p = item->read_func(p); | ||
378 | |||
379 | /* ISSUE: I don't understand this code */ | ||
380 | len = (p - page); | ||
381 | if (len <= off + count) | ||
382 | *eof = 1; | ||
383 | *start = page + off; | ||
384 | len -= off; | ||
385 | if (len > count) | ||
386 | len = count; | ||
387 | if (len < 0) | ||
388 | len = 0; | ||
389 | return len; | ||
390 | } | ||
391 | |||
392 | static int | ||
393 | dispatch_write(struct file *file, const char __user * buffer, | ||
394 | unsigned long count, ProcItem * item) | ||
395 | { | ||
396 | int result; | ||
397 | char *tmp_buffer; | ||
398 | |||
399 | /* Arg buffer points to userspace memory, which can't be accessed | ||
400 | * directly. Since we're making a copy, zero-terminate the | ||
401 | * destination so that sscanf can be used on it safely. | ||
402 | */ | ||
403 | tmp_buffer = kmalloc(count + 1, GFP_KERNEL); | ||
404 | if (!tmp_buffer) | ||
405 | return -ENOMEM; | ||
406 | |||
407 | if (copy_from_user(tmp_buffer, buffer, count)) { | ||
408 | result = -EFAULT; | ||
409 | } else { | ||
410 | tmp_buffer[count] = 0; | ||
411 | result = item->write_func(tmp_buffer, count); | ||
412 | } | ||
413 | kfree(tmp_buffer); | ||
414 | return result; | ||
415 | } | ||
416 | |||
417 | static int get_lcd(struct backlight_device *bd) | 396 | static int get_lcd(struct backlight_device *bd) |
418 | { | 397 | { |
419 | u32 hci_result; | 398 | u32 hci_result; |
@@ -426,19 +405,24 @@ static int get_lcd(struct backlight_device *bd) | |||
426 | return -EFAULT; | 405 | return -EFAULT; |
427 | } | 406 | } |
428 | 407 | ||
429 | static char *read_lcd(char *p) | 408 | static int lcd_proc_show(struct seq_file *m, void *v) |
430 | { | 409 | { |
431 | int value = get_lcd(NULL); | 410 | int value = get_lcd(NULL); |
432 | 411 | ||
433 | if (value >= 0) { | 412 | if (value >= 0) { |
434 | p += sprintf(p, "brightness: %d\n", value); | 413 | seq_printf(m, "brightness: %d\n", value); |
435 | p += sprintf(p, "brightness_levels: %d\n", | 414 | seq_printf(m, "brightness_levels: %d\n", |
436 | HCI_LCD_BRIGHTNESS_LEVELS); | 415 | HCI_LCD_BRIGHTNESS_LEVELS); |
437 | } else { | 416 | } else { |
438 | printk(MY_ERR "Error reading LCD brightness\n"); | 417 | printk(MY_ERR "Error reading LCD brightness\n"); |
439 | } | 418 | } |
440 | 419 | ||
441 | return p; | 420 | return 0; |
421 | } | ||
422 | |||
423 | static int lcd_proc_open(struct inode *inode, struct file *file) | ||
424 | { | ||
425 | return single_open(file, lcd_proc_show, NULL); | ||
442 | } | 426 | } |
443 | 427 | ||
444 | static int set_lcd(int value) | 428 | static int set_lcd(int value) |
@@ -458,12 +442,20 @@ static int set_lcd_status(struct backlight_device *bd) | |||
458 | return set_lcd(bd->props.brightness); | 442 | return set_lcd(bd->props.brightness); |
459 | } | 443 | } |
460 | 444 | ||
461 | static unsigned long write_lcd(const char *buffer, unsigned long count) | 445 | static ssize_t lcd_proc_write(struct file *file, const char __user *buf, |
446 | size_t count, loff_t *pos) | ||
462 | { | 447 | { |
448 | char cmd[42]; | ||
449 | size_t len; | ||
463 | int value; | 450 | int value; |
464 | int ret; | 451 | int ret; |
465 | 452 | ||
466 | if (sscanf(buffer, " brightness : %i", &value) == 1 && | 453 | len = min(count, sizeof(cmd) - 1); |
454 | if (copy_from_user(cmd, buf, len)) | ||
455 | return -EFAULT; | ||
456 | cmd[len] = '\0'; | ||
457 | |||
458 | if (sscanf(cmd, " brightness : %i", &value) == 1 && | ||
467 | value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { | 459 | value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { |
468 | ret = set_lcd(value); | 460 | ret = set_lcd(value); |
469 | if (ret == 0) | 461 | if (ret == 0) |
@@ -474,7 +466,16 @@ static unsigned long write_lcd(const char *buffer, unsigned long count) | |||
474 | return ret; | 466 | return ret; |
475 | } | 467 | } |
476 | 468 | ||
477 | static char *read_video(char *p) | 469 | static const struct file_operations lcd_proc_fops = { |
470 | .owner = THIS_MODULE, | ||
471 | .open = lcd_proc_open, | ||
472 | .read = seq_read, | ||
473 | .llseek = seq_lseek, | ||
474 | .release = single_release, | ||
475 | .write = lcd_proc_write, | ||
476 | }; | ||
477 | |||
478 | static int video_proc_show(struct seq_file *m, void *v) | ||
478 | { | 479 | { |
479 | u32 hci_result; | 480 | u32 hci_result; |
480 | u32 value; | 481 | u32 value; |
@@ -484,18 +485,25 @@ static char *read_video(char *p) | |||
484 | int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; | 485 | int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; |
485 | int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; | 486 | int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; |
486 | int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; | 487 | int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; |
487 | p += sprintf(p, "lcd_out: %d\n", is_lcd); | 488 | seq_printf(m, "lcd_out: %d\n", is_lcd); |
488 | p += sprintf(p, "crt_out: %d\n", is_crt); | 489 | seq_printf(m, "crt_out: %d\n", is_crt); |
489 | p += sprintf(p, "tv_out: %d\n", is_tv); | 490 | seq_printf(m, "tv_out: %d\n", is_tv); |
490 | } else { | 491 | } else { |
491 | printk(MY_ERR "Error reading video out status\n"); | 492 | printk(MY_ERR "Error reading video out status\n"); |
492 | } | 493 | } |
493 | 494 | ||
494 | return p; | 495 | return 0; |
495 | } | 496 | } |
496 | 497 | ||
497 | static unsigned long write_video(const char *buffer, unsigned long count) | 498 | static int video_proc_open(struct inode *inode, struct file *file) |
498 | { | 499 | { |
500 | return single_open(file, video_proc_show, NULL); | ||
501 | } | ||
502 | |||
503 | static ssize_t video_proc_write(struct file *file, const char __user *buf, | ||
504 | size_t count, loff_t *pos) | ||
505 | { | ||
506 | char *cmd, *buffer; | ||
499 | int value; | 507 | int value; |
500 | int remain = count; | 508 | int remain = count; |
501 | int lcd_out = -1; | 509 | int lcd_out = -1; |
@@ -504,6 +512,17 @@ static unsigned long write_video(const char *buffer, unsigned long count) | |||
504 | u32 hci_result; | 512 | u32 hci_result; |
505 | u32 video_out; | 513 | u32 video_out; |
506 | 514 | ||
515 | cmd = kmalloc(count + 1, GFP_KERNEL); | ||
516 | if (!cmd) | ||
517 | return -ENOMEM; | ||
518 | if (copy_from_user(cmd, buf, count)) { | ||
519 | kfree(cmd); | ||
520 | return -EFAULT; | ||
521 | } | ||
522 | cmd[count] = '\0'; | ||
523 | |||
524 | buffer = cmd; | ||
525 | |||
507 | /* scan expression. Multiple expressions may be delimited with ; | 526 | /* scan expression. Multiple expressions may be delimited with ; |
508 | * | 527 | * |
509 | * NOTE: to keep scanning simple, invalid fields are ignored | 528 | * NOTE: to keep scanning simple, invalid fields are ignored |
@@ -523,6 +542,8 @@ static unsigned long write_video(const char *buffer, unsigned long count) | |||
523 | while (remain && *(buffer - 1) != ';'); | 542 | while (remain && *(buffer - 1) != ';'); |
524 | } | 543 | } |
525 | 544 | ||
545 | kfree(cmd); | ||
546 | |||
526 | hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result); | 547 | hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result); |
527 | if (hci_result == HCI_SUCCESS) { | 548 | if (hci_result == HCI_SUCCESS) { |
528 | unsigned int new_video_out = video_out; | 549 | unsigned int new_video_out = video_out; |
@@ -543,28 +564,50 @@ static unsigned long write_video(const char *buffer, unsigned long count) | |||
543 | return count; | 564 | return count; |
544 | } | 565 | } |
545 | 566 | ||
546 | static char *read_fan(char *p) | 567 | static const struct file_operations video_proc_fops = { |
568 | .owner = THIS_MODULE, | ||
569 | .open = video_proc_open, | ||
570 | .read = seq_read, | ||
571 | .llseek = seq_lseek, | ||
572 | .release = single_release, | ||
573 | .write = video_proc_write, | ||
574 | }; | ||
575 | |||
576 | static int fan_proc_show(struct seq_file *m, void *v) | ||
547 | { | 577 | { |
548 | u32 hci_result; | 578 | u32 hci_result; |
549 | u32 value; | 579 | u32 value; |
550 | 580 | ||
551 | hci_read1(HCI_FAN, &value, &hci_result); | 581 | hci_read1(HCI_FAN, &value, &hci_result); |
552 | if (hci_result == HCI_SUCCESS) { | 582 | if (hci_result == HCI_SUCCESS) { |
553 | p += sprintf(p, "running: %d\n", (value > 0)); | 583 | seq_printf(m, "running: %d\n", (value > 0)); |
554 | p += sprintf(p, "force_on: %d\n", force_fan); | 584 | seq_printf(m, "force_on: %d\n", force_fan); |
555 | } else { | 585 | } else { |
556 | printk(MY_ERR "Error reading fan status\n"); | 586 | printk(MY_ERR "Error reading fan status\n"); |
557 | } | 587 | } |
558 | 588 | ||
559 | return p; | 589 | return 0; |
590 | } | ||
591 | |||
592 | static int fan_proc_open(struct inode *inode, struct file *file) | ||
593 | { | ||
594 | return single_open(file, fan_proc_show, NULL); | ||
560 | } | 595 | } |
561 | 596 | ||
562 | static unsigned long write_fan(const char *buffer, unsigned long count) | 597 | static ssize_t fan_proc_write(struct file *file, const char __user *buf, |
598 | size_t count, loff_t *pos) | ||
563 | { | 599 | { |
600 | char cmd[42]; | ||
601 | size_t len; | ||
564 | int value; | 602 | int value; |
565 | u32 hci_result; | 603 | u32 hci_result; |
566 | 604 | ||
567 | if (sscanf(buffer, " force_on : %i", &value) == 1 && | 605 | len = min(count, sizeof(cmd) - 1); |
606 | if (copy_from_user(cmd, buf, len)) | ||
607 | return -EFAULT; | ||
608 | cmd[len] = '\0'; | ||
609 | |||
610 | if (sscanf(cmd, " force_on : %i", &value) == 1 && | ||
568 | value >= 0 && value <= 1) { | 611 | value >= 0 && value <= 1) { |
569 | hci_write1(HCI_FAN, value, &hci_result); | 612 | hci_write1(HCI_FAN, value, &hci_result); |
570 | if (hci_result != HCI_SUCCESS) | 613 | if (hci_result != HCI_SUCCESS) |
@@ -578,7 +621,16 @@ static unsigned long write_fan(const char *buffer, unsigned long count) | |||
578 | return count; | 621 | return count; |
579 | } | 622 | } |
580 | 623 | ||
581 | static char *read_keys(char *p) | 624 | static const struct file_operations fan_proc_fops = { |
625 | .owner = THIS_MODULE, | ||
626 | .open = fan_proc_open, | ||
627 | .read = seq_read, | ||
628 | .llseek = seq_lseek, | ||
629 | .release = single_release, | ||
630 | .write = fan_proc_write, | ||
631 | }; | ||
632 | |||
633 | static int keys_proc_show(struct seq_file *m, void *v) | ||
582 | { | 634 | { |
583 | u32 hci_result; | 635 | u32 hci_result; |
584 | u32 value; | 636 | u32 value; |
@@ -602,18 +654,30 @@ static char *read_keys(char *p) | |||
602 | } | 654 | } |
603 | } | 655 | } |
604 | 656 | ||
605 | p += sprintf(p, "hotkey_ready: %d\n", key_event_valid); | 657 | seq_printf(m, "hotkey_ready: %d\n", key_event_valid); |
606 | p += sprintf(p, "hotkey: 0x%04x\n", last_key_event); | 658 | seq_printf(m, "hotkey: 0x%04x\n", last_key_event); |
659 | end: | ||
660 | return 0; | ||
661 | } | ||
607 | 662 | ||
608 | end: | 663 | static int keys_proc_open(struct inode *inode, struct file *file) |
609 | return p; | 664 | { |
665 | return single_open(file, keys_proc_show, NULL); | ||
610 | } | 666 | } |
611 | 667 | ||
612 | static unsigned long write_keys(const char *buffer, unsigned long count) | 668 | static ssize_t keys_proc_write(struct file *file, const char __user *buf, |
669 | size_t count, loff_t *pos) | ||
613 | { | 670 | { |
671 | char cmd[42]; | ||
672 | size_t len; | ||
614 | int value; | 673 | int value; |
615 | 674 | ||
616 | if (sscanf(buffer, " hotkey_ready : %i", &value) == 1 && value == 0) { | 675 | len = min(count, sizeof(cmd) - 1); |
676 | if (copy_from_user(cmd, buf, len)) | ||
677 | return -EFAULT; | ||
678 | cmd[len] = '\0'; | ||
679 | |||
680 | if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) { | ||
617 | key_event_valid = 0; | 681 | key_event_valid = 0; |
618 | } else { | 682 | } else { |
619 | return -EINVAL; | 683 | return -EINVAL; |
@@ -622,52 +686,58 @@ static unsigned long write_keys(const char *buffer, unsigned long count) | |||
622 | return count; | 686 | return count; |
623 | } | 687 | } |
624 | 688 | ||
625 | static char *read_version(char *p) | 689 | static const struct file_operations keys_proc_fops = { |
690 | .owner = THIS_MODULE, | ||
691 | .open = keys_proc_open, | ||
692 | .read = seq_read, | ||
693 | .llseek = seq_lseek, | ||
694 | .release = single_release, | ||
695 | .write = keys_proc_write, | ||
696 | }; | ||
697 | |||
698 | static int version_proc_show(struct seq_file *m, void *v) | ||
699 | { | ||
700 | seq_printf(m, "driver: %s\n", TOSHIBA_ACPI_VERSION); | ||
701 | seq_printf(m, "proc_interface: %d\n", PROC_INTERFACE_VERSION); | ||
702 | return 0; | ||
703 | } | ||
704 | |||
705 | static int version_proc_open(struct inode *inode, struct file *file) | ||
626 | { | 706 | { |
627 | p += sprintf(p, "driver: %s\n", TOSHIBA_ACPI_VERSION); | 707 | return single_open(file, version_proc_show, PDE(inode)->data); |
628 | p += sprintf(p, "proc_interface: %d\n", | ||
629 | PROC_INTERFACE_VERSION); | ||
630 | return p; | ||
631 | } | 708 | } |
632 | 709 | ||
710 | static const struct file_operations version_proc_fops = { | ||
711 | .owner = THIS_MODULE, | ||
712 | .open = version_proc_open, | ||
713 | .read = seq_read, | ||
714 | .llseek = seq_lseek, | ||
715 | .release = single_release, | ||
716 | }; | ||
717 | |||
633 | /* proc and module init | 718 | /* proc and module init |
634 | */ | 719 | */ |
635 | 720 | ||
636 | #define PROC_TOSHIBA "toshiba" | 721 | #define PROC_TOSHIBA "toshiba" |
637 | 722 | ||
638 | static ProcItem proc_items[] = { | ||
639 | {"lcd", read_lcd, write_lcd}, | ||
640 | {"video", read_video, write_video}, | ||
641 | {"fan", read_fan, write_fan}, | ||
642 | {"keys", read_keys, write_keys}, | ||
643 | {"version", read_version, NULL}, | ||
644 | {NULL} | ||
645 | }; | ||
646 | |||
647 | static acpi_status __init add_device(void) | 723 | static acpi_status __init add_device(void) |
648 | { | 724 | { |
649 | struct proc_dir_entry *proc; | 725 | proc_create("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, &lcd_proc_fops); |
650 | ProcItem *item; | 726 | proc_create("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, &video_proc_fops); |
651 | 727 | proc_create("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, &fan_proc_fops); | |
652 | for (item = proc_items; item->name; ++item) { | 728 | proc_create("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, &keys_proc_fops); |
653 | proc = create_proc_read_entry(item->name, | 729 | proc_create("version", S_IRUGO, toshiba_proc_dir, &version_proc_fops); |
654 | S_IFREG | S_IRUGO | S_IWUSR, | ||
655 | toshiba_proc_dir, | ||
656 | (read_proc_t *) dispatch_read, | ||
657 | item); | ||
658 | if (proc && item->write_func) | ||
659 | proc->write_proc = (write_proc_t *) dispatch_write; | ||
660 | } | ||
661 | 730 | ||
662 | return AE_OK; | 731 | return AE_OK; |
663 | } | 732 | } |
664 | 733 | ||
665 | static acpi_status remove_device(void) | 734 | static acpi_status remove_device(void) |
666 | { | 735 | { |
667 | ProcItem *item; | 736 | remove_proc_entry("lcd", toshiba_proc_dir); |
668 | 737 | remove_proc_entry("video", toshiba_proc_dir); | |
669 | for (item = proc_items; item->name; ++item) | 738 | remove_proc_entry("fan", toshiba_proc_dir); |
670 | remove_proc_entry(item->name, toshiba_proc_dir); | 739 | remove_proc_entry("keys", toshiba_proc_dir); |
740 | remove_proc_entry("version", toshiba_proc_dir); | ||
671 | return AE_OK; | 741 | return AE_OK; |
672 | } | 742 | } |
673 | 743 | ||
@@ -676,8 +746,158 @@ static struct backlight_ops toshiba_backlight_data = { | |||
676 | .update_status = set_lcd_status, | 746 | .update_status = set_lcd_status, |
677 | }; | 747 | }; |
678 | 748 | ||
749 | static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code) | ||
750 | { | ||
751 | struct key_entry *key; | ||
752 | |||
753 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) | ||
754 | if (code == key->code) | ||
755 | return key; | ||
756 | |||
757 | return NULL; | ||
758 | } | ||
759 | |||
760 | static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code) | ||
761 | { | ||
762 | struct key_entry *key; | ||
763 | |||
764 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) | ||
765 | if (code == key->keycode && key->type == KE_KEY) | ||
766 | return key; | ||
767 | |||
768 | return NULL; | ||
769 | } | ||
770 | |||
771 | static int toshiba_acpi_getkeycode(struct input_dev *dev, | ||
772 | unsigned int scancode, unsigned int *keycode) | ||
773 | { | ||
774 | struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode); | ||
775 | |||
776 | if (key && key->type == KE_KEY) { | ||
777 | *keycode = key->keycode; | ||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | return -EINVAL; | ||
782 | } | ||
783 | |||
784 | static int toshiba_acpi_setkeycode(struct input_dev *dev, | ||
785 | unsigned int scancode, unsigned int keycode) | ||
786 | { | ||
787 | struct key_entry *key; | ||
788 | unsigned int old_keycode; | ||
789 | |||
790 | key = toshiba_acpi_get_entry_by_scancode(scancode); | ||
791 | if (key && key->type == KE_KEY) { | ||
792 | old_keycode = key->keycode; | ||
793 | key->keycode = keycode; | ||
794 | set_bit(keycode, dev->keybit); | ||
795 | if (!toshiba_acpi_get_entry_by_keycode(old_keycode)) | ||
796 | clear_bit(old_keycode, dev->keybit); | ||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | return -EINVAL; | ||
801 | } | ||
802 | |||
803 | static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) | ||
804 | { | ||
805 | u32 hci_result, value; | ||
806 | struct key_entry *key; | ||
807 | |||
808 | if (event != 0x80) | ||
809 | return; | ||
810 | do { | ||
811 | hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); | ||
812 | if (hci_result == HCI_SUCCESS) { | ||
813 | if (value == 0x100) | ||
814 | continue; | ||
815 | /* act on key press; ignore key release */ | ||
816 | if (value & 0x80) | ||
817 | continue; | ||
818 | |||
819 | key = toshiba_acpi_get_entry_by_scancode | ||
820 | (value); | ||
821 | if (!key) { | ||
822 | printk(MY_INFO "Unknown key %x\n", | ||
823 | value); | ||
824 | continue; | ||
825 | } | ||
826 | input_report_key(toshiba_acpi.hotkey_dev, | ||
827 | key->keycode, 1); | ||
828 | input_sync(toshiba_acpi.hotkey_dev); | ||
829 | input_report_key(toshiba_acpi.hotkey_dev, | ||
830 | key->keycode, 0); | ||
831 | input_sync(toshiba_acpi.hotkey_dev); | ||
832 | } else if (hci_result == HCI_NOT_SUPPORTED) { | ||
833 | /* This is a workaround for an unresolved issue on | ||
834 | * some machines where system events sporadically | ||
835 | * become disabled. */ | ||
836 | hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); | ||
837 | printk(MY_NOTICE "Re-enabled hotkeys\n"); | ||
838 | } | ||
839 | } while (hci_result != HCI_EMPTY); | ||
840 | } | ||
841 | |||
842 | static int toshiba_acpi_setup_keyboard(char *device) | ||
843 | { | ||
844 | acpi_status status; | ||
845 | acpi_handle handle; | ||
846 | int result; | ||
847 | const struct key_entry *key; | ||
848 | |||
849 | status = acpi_get_handle(NULL, device, &handle); | ||
850 | if (ACPI_FAILURE(status)) { | ||
851 | printk(MY_INFO "Unable to get notification device\n"); | ||
852 | return -ENODEV; | ||
853 | } | ||
854 | |||
855 | toshiba_acpi.handle = handle; | ||
856 | |||
857 | status = acpi_evaluate_object(handle, "ENAB", NULL, NULL); | ||
858 | if (ACPI_FAILURE(status)) { | ||
859 | printk(MY_INFO "Unable to enable hotkeys\n"); | ||
860 | return -ENODEV; | ||
861 | } | ||
862 | |||
863 | status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, | ||
864 | toshiba_acpi_notify, NULL); | ||
865 | if (ACPI_FAILURE(status)) { | ||
866 | printk(MY_INFO "Unable to install hotkey notification\n"); | ||
867 | return -ENODEV; | ||
868 | } | ||
869 | |||
870 | toshiba_acpi.hotkey_dev = input_allocate_device(); | ||
871 | if (!toshiba_acpi.hotkey_dev) { | ||
872 | printk(MY_INFO "Unable to register input device\n"); | ||
873 | return -ENOMEM; | ||
874 | } | ||
875 | |||
876 | toshiba_acpi.hotkey_dev->name = "Toshiba input device"; | ||
877 | toshiba_acpi.hotkey_dev->phys = device; | ||
878 | toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; | ||
879 | toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode; | ||
880 | toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode; | ||
881 | |||
882 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) { | ||
883 | set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit); | ||
884 | set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit); | ||
885 | } | ||
886 | |||
887 | result = input_register_device(toshiba_acpi.hotkey_dev); | ||
888 | if (result) { | ||
889 | printk(MY_INFO "Unable to register input device\n"); | ||
890 | return result; | ||
891 | } | ||
892 | |||
893 | return 0; | ||
894 | } | ||
895 | |||
679 | static void toshiba_acpi_exit(void) | 896 | static void toshiba_acpi_exit(void) |
680 | { | 897 | { |
898 | if (toshiba_acpi.hotkey_dev) | ||
899 | input_unregister_device(toshiba_acpi.hotkey_dev); | ||
900 | |||
681 | if (toshiba_acpi.bt_rfk) { | 901 | if (toshiba_acpi.bt_rfk) { |
682 | rfkill_unregister(toshiba_acpi.bt_rfk); | 902 | rfkill_unregister(toshiba_acpi.bt_rfk); |
683 | rfkill_destroy(toshiba_acpi.bt_rfk); | 903 | rfkill_destroy(toshiba_acpi.bt_rfk); |
@@ -691,6 +911,9 @@ static void toshiba_acpi_exit(void) | |||
691 | if (toshiba_proc_dir) | 911 | if (toshiba_proc_dir) |
692 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); | 912 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); |
693 | 913 | ||
914 | acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, | ||
915 | toshiba_acpi_notify); | ||
916 | |||
694 | platform_device_unregister(toshiba_acpi.p_dev); | 917 | platform_device_unregister(toshiba_acpi.p_dev); |
695 | 918 | ||
696 | return; | 919 | return; |
@@ -702,16 +925,21 @@ static int __init toshiba_acpi_init(void) | |||
702 | u32 hci_result; | 925 | u32 hci_result; |
703 | bool bt_present; | 926 | bool bt_present; |
704 | int ret = 0; | 927 | int ret = 0; |
928 | struct backlight_properties props; | ||
705 | 929 | ||
706 | if (acpi_disabled) | 930 | if (acpi_disabled) |
707 | return -ENODEV; | 931 | return -ENODEV; |
708 | 932 | ||
709 | /* simple device detection: look for HCI method */ | 933 | /* simple device detection: look for HCI method */ |
710 | if (is_valid_acpi_path(METHOD_HCI_1)) | 934 | if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { |
711 | method_hci = METHOD_HCI_1; | 935 | method_hci = TOSH_INTERFACE_1 GHCI_METHOD; |
712 | else if (is_valid_acpi_path(METHOD_HCI_2)) | 936 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1)) |
713 | method_hci = METHOD_HCI_2; | 937 | printk(MY_INFO "Unable to activate hotkeys\n"); |
714 | else | 938 | } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { |
939 | method_hci = TOSH_INTERFACE_2 GHCI_METHOD; | ||
940 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2)) | ||
941 | printk(MY_INFO "Unable to activate hotkeys\n"); | ||
942 | } else | ||
715 | return -ENODEV; | 943 | return -ENODEV; |
716 | 944 | ||
717 | printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n", | 945 | printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n", |
@@ -748,10 +976,12 @@ static int __init toshiba_acpi_init(void) | |||
748 | } | 976 | } |
749 | } | 977 | } |
750 | 978 | ||
979 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; | ||
751 | toshiba_backlight_device = backlight_device_register("toshiba", | 980 | toshiba_backlight_device = backlight_device_register("toshiba", |
752 | &toshiba_acpi.p_dev->dev, | 981 | &toshiba_acpi.p_dev->dev, |
753 | NULL, | 982 | NULL, |
754 | &toshiba_backlight_data); | 983 | &toshiba_backlight_data, |
984 | &props); | ||
755 | if (IS_ERR(toshiba_backlight_device)) { | 985 | if (IS_ERR(toshiba_backlight_device)) { |
756 | ret = PTR_ERR(toshiba_backlight_device); | 986 | ret = PTR_ERR(toshiba_backlight_device); |
757 | 987 | ||
@@ -760,7 +990,6 @@ static int __init toshiba_acpi_init(void) | |||
760 | toshiba_acpi_exit(); | 990 | toshiba_acpi_exit(); |
761 | return ret; | 991 | return ret; |
762 | } | 992 | } |
763 | toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; | ||
764 | 993 | ||
765 | /* Register rfkill switch for Bluetooth */ | 994 | /* Register rfkill switch for Bluetooth */ |
766 | if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { | 995 | if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { |
diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c new file mode 100644 index 000000000000..944068611919 --- /dev/null +++ b/drivers/platform/x86/toshiba_bluetooth.c | |||
@@ -0,0 +1,144 @@ | |||
1 | /* | ||
2 | * Toshiba Bluetooth Enable Driver | ||
3 | * | ||
4 | * Copyright (C) 2009 Jes Sorensen <Jes.Sorensen@gmail.com> | ||
5 | * | ||
6 | * Thanks to Matthew Garrett for background info on ACPI innards which | ||
7 | * normal people aren't meant to understand :-) | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * Note the Toshiba Bluetooth RFKill switch seems to be a strange | ||
14 | * fish. It only provides a BT event when the switch is flipped to | ||
15 | * the 'on' position. When flipping it to 'off', the USB device is | ||
16 | * simply pulled away underneath us, without any BT event being | ||
17 | * delivered. | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <acpi/acpi_bus.h> | ||
25 | #include <acpi/acpi_drivers.h> | ||
26 | |||
27 | MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>"); | ||
28 | MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver"); | ||
29 | MODULE_LICENSE("GPL"); | ||
30 | |||
31 | |||
32 | static int toshiba_bt_rfkill_add(struct acpi_device *device); | ||
33 | static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type); | ||
34 | static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event); | ||
35 | static int toshiba_bt_resume(struct acpi_device *device); | ||
36 | |||
37 | static const struct acpi_device_id bt_device_ids[] = { | ||
38 | { "TOS6205", 0}, | ||
39 | { "", 0}, | ||
40 | }; | ||
41 | MODULE_DEVICE_TABLE(acpi, bt_device_ids); | ||
42 | |||
43 | static struct acpi_driver toshiba_bt_rfkill_driver = { | ||
44 | .name = "Toshiba BT", | ||
45 | .class = "Toshiba", | ||
46 | .ids = bt_device_ids, | ||
47 | .ops = { | ||
48 | .add = toshiba_bt_rfkill_add, | ||
49 | .remove = toshiba_bt_rfkill_remove, | ||
50 | .notify = toshiba_bt_rfkill_notify, | ||
51 | .resume = toshiba_bt_resume, | ||
52 | }, | ||
53 | .owner = THIS_MODULE, | ||
54 | }; | ||
55 | |||
56 | |||
57 | static int toshiba_bluetooth_enable(acpi_handle handle) | ||
58 | { | ||
59 | acpi_status res1, res2; | ||
60 | u64 result; | ||
61 | |||
62 | /* | ||
63 | * Query ACPI to verify RFKill switch is set to 'on'. | ||
64 | * If not, we return silently, no need to report it as | ||
65 | * an error. | ||
66 | */ | ||
67 | res1 = acpi_evaluate_integer(handle, "BTST", NULL, &result); | ||
68 | if (ACPI_FAILURE(res1)) | ||
69 | return res1; | ||
70 | if (!(result & 0x01)) | ||
71 | return 0; | ||
72 | |||
73 | printk(KERN_INFO "toshiba_bluetooth: Re-enabling Toshiba Bluetooth\n"); | ||
74 | res1 = acpi_evaluate_object(handle, "AUSB", NULL, NULL); | ||
75 | res2 = acpi_evaluate_object(handle, "BTPO", NULL, NULL); | ||
76 | if (!ACPI_FAILURE(res1) || !ACPI_FAILURE(res2)) | ||
77 | return 0; | ||
78 | |||
79 | printk(KERN_WARNING "toshiba_bluetooth: Failed to re-enable " | ||
80 | "Toshiba Bluetooth\n"); | ||
81 | |||
82 | return -ENODEV; | ||
83 | } | ||
84 | |||
85 | static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event) | ||
86 | { | ||
87 | toshiba_bluetooth_enable(device->handle); | ||
88 | } | ||
89 | |||
90 | static int toshiba_bt_resume(struct acpi_device *device) | ||
91 | { | ||
92 | return toshiba_bluetooth_enable(device->handle); | ||
93 | } | ||
94 | |||
95 | static int toshiba_bt_rfkill_add(struct acpi_device *device) | ||
96 | { | ||
97 | acpi_status status; | ||
98 | u64 bt_present; | ||
99 | int result = -ENODEV; | ||
100 | |||
101 | /* | ||
102 | * Some Toshiba laptops may have a fake TOS6205 device in | ||
103 | * their ACPI BIOS, so query the _STA method to see if there | ||
104 | * is really anything there, before trying to enable it. | ||
105 | */ | ||
106 | status = acpi_evaluate_integer(device->handle, "_STA", NULL, | ||
107 | &bt_present); | ||
108 | |||
109 | if (!ACPI_FAILURE(status) && bt_present) { | ||
110 | printk(KERN_INFO "Detected Toshiba ACPI Bluetooth device - " | ||
111 | "installing RFKill handler\n"); | ||
112 | result = toshiba_bluetooth_enable(device->handle); | ||
113 | } | ||
114 | |||
115 | return result; | ||
116 | } | ||
117 | |||
118 | static int __init toshiba_bt_rfkill_init(void) | ||
119 | { | ||
120 | int result; | ||
121 | |||
122 | result = acpi_bus_register_driver(&toshiba_bt_rfkill_driver); | ||
123 | if (result < 0) { | ||
124 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
125 | "Error registering driver\n")); | ||
126 | return result; | ||
127 | } | ||
128 | |||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type) | ||
133 | { | ||
134 | /* clean up */ | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static void __exit toshiba_bt_rfkill_exit(void) | ||
139 | { | ||
140 | acpi_bus_unregister_driver(&toshiba_bt_rfkill_driver); | ||
141 | } | ||
142 | |||
143 | module_init(toshiba_bt_rfkill_init); | ||
144 | module_exit(toshiba_bt_rfkill_exit); | ||
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 177f8d767df4..39ec5b6c2e3a 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -30,8 +30,10 @@ | |||
30 | #include <linux/kernel.h> | 30 | #include <linux/kernel.h> |
31 | #include <linux/init.h> | 31 | #include <linux/init.h> |
32 | #include <linux/types.h> | 32 | #include <linux/types.h> |
33 | #include <linux/device.h> | ||
33 | #include <linux/list.h> | 34 | #include <linux/list.h> |
34 | #include <linux/acpi.h> | 35 | #include <linux/acpi.h> |
36 | #include <linux/slab.h> | ||
35 | #include <acpi/acpi_bus.h> | 37 | #include <acpi/acpi_bus.h> |
36 | #include <acpi/acpi_drivers.h> | 38 | #include <acpi/acpi_drivers.h> |
37 | 39 | ||
@@ -65,6 +67,7 @@ struct wmi_block { | |||
65 | acpi_handle handle; | 67 | acpi_handle handle; |
66 | wmi_notify_handler handler; | 68 | wmi_notify_handler handler; |
67 | void *handler_data; | 69 | void *handler_data; |
70 | struct device *dev; | ||
68 | }; | 71 | }; |
69 | 72 | ||
70 | static struct wmi_block wmi_blocks; | 73 | static struct wmi_block wmi_blocks; |
@@ -195,6 +198,34 @@ static bool wmi_parse_guid(const u8 *src, u8 *dest) | |||
195 | return true; | 198 | return true; |
196 | } | 199 | } |
197 | 200 | ||
201 | /* | ||
202 | * Convert a raw GUID to the ACII string representation | ||
203 | */ | ||
204 | static int wmi_gtoa(const char *in, char *out) | ||
205 | { | ||
206 | int i; | ||
207 | |||
208 | for (i = 3; i >= 0; i--) | ||
209 | out += sprintf(out, "%02X", in[i] & 0xFF); | ||
210 | |||
211 | out += sprintf(out, "-"); | ||
212 | out += sprintf(out, "%02X", in[5] & 0xFF); | ||
213 | out += sprintf(out, "%02X", in[4] & 0xFF); | ||
214 | out += sprintf(out, "-"); | ||
215 | out += sprintf(out, "%02X", in[7] & 0xFF); | ||
216 | out += sprintf(out, "%02X", in[6] & 0xFF); | ||
217 | out += sprintf(out, "-"); | ||
218 | out += sprintf(out, "%02X", in[8] & 0xFF); | ||
219 | out += sprintf(out, "%02X", in[9] & 0xFF); | ||
220 | out += sprintf(out, "-"); | ||
221 | |||
222 | for (i = 10; i <= 15; i++) | ||
223 | out += sprintf(out, "%02X", in[i] & 0xFF); | ||
224 | |||
225 | out = '\0'; | ||
226 | return 0; | ||
227 | } | ||
228 | |||
198 | static bool find_guid(const char *guid_string, struct wmi_block **out) | 229 | static bool find_guid(const char *guid_string, struct wmi_block **out) |
199 | { | 230 | { |
200 | char tmp[16], guid_input[16]; | 231 | char tmp[16], guid_input[16]; |
@@ -462,8 +493,7 @@ wmi_notify_handler handler, void *data) | |||
462 | if (!guid || !handler) | 493 | if (!guid || !handler) |
463 | return AE_BAD_PARAMETER; | 494 | return AE_BAD_PARAMETER; |
464 | 495 | ||
465 | find_guid(guid, &block); | 496 | if (!find_guid(guid, &block)) |
466 | if (!block) | ||
467 | return AE_NOT_EXIST; | 497 | return AE_NOT_EXIST; |
468 | 498 | ||
469 | if (block->handler) | 499 | if (block->handler) |
@@ -491,8 +521,7 @@ acpi_status wmi_remove_notify_handler(const char *guid) | |||
491 | if (!guid) | 521 | if (!guid) |
492 | return AE_BAD_PARAMETER; | 522 | return AE_BAD_PARAMETER; |
493 | 523 | ||
494 | find_guid(guid, &block); | 524 | if (!find_guid(guid, &block)) |
495 | if (!block) | ||
496 | return AE_NOT_EXIST; | 525 | return AE_NOT_EXIST; |
497 | 526 | ||
498 | if (!block->handler) | 527 | if (!block->handler) |
@@ -510,8 +539,8 @@ EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); | |||
510 | /** | 539 | /** |
511 | * wmi_get_event_data - Get WMI data associated with an event | 540 | * wmi_get_event_data - Get WMI data associated with an event |
512 | * | 541 | * |
513 | * @event - Event to find | 542 | * @event: Event to find |
514 | * &out - Buffer to hold event data | 543 | * @out: Buffer to hold event data. out->pointer should be freed with kfree() |
515 | * | 544 | * |
516 | * Returns extra data associated with an event in WMI. | 545 | * Returns extra data associated with an event in WMI. |
517 | */ | 546 | */ |
@@ -555,6 +584,154 @@ bool wmi_has_guid(const char *guid_string) | |||
555 | EXPORT_SYMBOL_GPL(wmi_has_guid); | 584 | EXPORT_SYMBOL_GPL(wmi_has_guid); |
556 | 585 | ||
557 | /* | 586 | /* |
587 | * sysfs interface | ||
588 | */ | ||
589 | static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, | ||
590 | char *buf) | ||
591 | { | ||
592 | char guid_string[37]; | ||
593 | struct wmi_block *wblock; | ||
594 | |||
595 | wblock = dev_get_drvdata(dev); | ||
596 | if (!wblock) | ||
597 | return -ENOMEM; | ||
598 | |||
599 | wmi_gtoa(wblock->gblock.guid, guid_string); | ||
600 | |||
601 | return sprintf(buf, "wmi:%s\n", guid_string); | ||
602 | } | ||
603 | static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); | ||
604 | |||
605 | static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
606 | { | ||
607 | char guid_string[37]; | ||
608 | |||
609 | struct wmi_block *wblock; | ||
610 | |||
611 | if (add_uevent_var(env, "MODALIAS=")) | ||
612 | return -ENOMEM; | ||
613 | |||
614 | wblock = dev_get_drvdata(dev); | ||
615 | if (!wblock) | ||
616 | return -ENOMEM; | ||
617 | |||
618 | wmi_gtoa(wblock->gblock.guid, guid_string); | ||
619 | |||
620 | strcpy(&env->buf[env->buflen - 1], "wmi:"); | ||
621 | memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); | ||
622 | env->buflen += 40; | ||
623 | |||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static void wmi_dev_free(struct device *dev) | ||
628 | { | ||
629 | kfree(dev); | ||
630 | } | ||
631 | |||
632 | static struct class wmi_class = { | ||
633 | .name = "wmi", | ||
634 | .dev_release = wmi_dev_free, | ||
635 | .dev_uevent = wmi_dev_uevent, | ||
636 | }; | ||
637 | |||
638 | static int wmi_create_devs(void) | ||
639 | { | ||
640 | int result; | ||
641 | char guid_string[37]; | ||
642 | struct guid_block *gblock; | ||
643 | struct wmi_block *wblock; | ||
644 | struct list_head *p; | ||
645 | struct device *guid_dev; | ||
646 | |||
647 | /* Create devices for all the GUIDs */ | ||
648 | list_for_each(p, &wmi_blocks.list) { | ||
649 | wblock = list_entry(p, struct wmi_block, list); | ||
650 | |||
651 | guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); | ||
652 | if (!guid_dev) | ||
653 | return -ENOMEM; | ||
654 | |||
655 | wblock->dev = guid_dev; | ||
656 | |||
657 | guid_dev->class = &wmi_class; | ||
658 | dev_set_drvdata(guid_dev, wblock); | ||
659 | |||
660 | gblock = &wblock->gblock; | ||
661 | |||
662 | wmi_gtoa(gblock->guid, guid_string); | ||
663 | dev_set_name(guid_dev, guid_string); | ||
664 | |||
665 | result = device_register(guid_dev); | ||
666 | if (result) | ||
667 | return result; | ||
668 | |||
669 | result = device_create_file(guid_dev, &dev_attr_modalias); | ||
670 | if (result) | ||
671 | return result; | ||
672 | } | ||
673 | |||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | static void wmi_remove_devs(void) | ||
678 | { | ||
679 | struct guid_block *gblock; | ||
680 | struct wmi_block *wblock; | ||
681 | struct list_head *p; | ||
682 | struct device *guid_dev; | ||
683 | |||
684 | /* Delete devices for all the GUIDs */ | ||
685 | list_for_each(p, &wmi_blocks.list) { | ||
686 | wblock = list_entry(p, struct wmi_block, list); | ||
687 | |||
688 | guid_dev = wblock->dev; | ||
689 | gblock = &wblock->gblock; | ||
690 | |||
691 | device_remove_file(guid_dev, &dev_attr_modalias); | ||
692 | |||
693 | device_unregister(guid_dev); | ||
694 | } | ||
695 | } | ||
696 | |||
697 | static void wmi_class_exit(void) | ||
698 | { | ||
699 | wmi_remove_devs(); | ||
700 | class_unregister(&wmi_class); | ||
701 | } | ||
702 | |||
703 | static int wmi_class_init(void) | ||
704 | { | ||
705 | int ret; | ||
706 | |||
707 | ret = class_register(&wmi_class); | ||
708 | if (ret) | ||
709 | return ret; | ||
710 | |||
711 | ret = wmi_create_devs(); | ||
712 | if (ret) | ||
713 | wmi_class_exit(); | ||
714 | |||
715 | return ret; | ||
716 | } | ||
717 | |||
718 | static bool guid_already_parsed(const char *guid_string) | ||
719 | { | ||
720 | struct guid_block *gblock; | ||
721 | struct wmi_block *wblock; | ||
722 | struct list_head *p; | ||
723 | |||
724 | list_for_each(p, &wmi_blocks.list) { | ||
725 | wblock = list_entry(p, struct wmi_block, list); | ||
726 | gblock = &wblock->gblock; | ||
727 | |||
728 | if (strncmp(gblock->guid, guid_string, 16) == 0) | ||
729 | return true; | ||
730 | } | ||
731 | return false; | ||
732 | } | ||
733 | |||
734 | /* | ||
558 | * Parse the _WDG method for the GUID data blocks | 735 | * Parse the _WDG method for the GUID data blocks |
559 | */ | 736 | */ |
560 | static __init acpi_status parse_wdg(acpi_handle handle) | 737 | static __init acpi_status parse_wdg(acpi_handle handle) |
@@ -563,6 +740,7 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
563 | union acpi_object *obj; | 740 | union acpi_object *obj; |
564 | struct guid_block *gblock; | 741 | struct guid_block *gblock; |
565 | struct wmi_block *wblock; | 742 | struct wmi_block *wblock; |
743 | char guid_string[37]; | ||
566 | acpi_status status; | 744 | acpi_status status; |
567 | u32 i, total; | 745 | u32 i, total; |
568 | 746 | ||
@@ -585,6 +763,19 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
585 | memcpy(gblock, obj->buffer.pointer, obj->buffer.length); | 763 | memcpy(gblock, obj->buffer.pointer, obj->buffer.length); |
586 | 764 | ||
587 | for (i = 0; i < total; i++) { | 765 | for (i = 0; i < total; i++) { |
766 | /* | ||
767 | Some WMI devices, like those for nVidia hooks, have a | ||
768 | duplicate GUID. It's not clear what we should do in this | ||
769 | case yet, so for now, we'll just ignore the duplicate. | ||
770 | Anyone who wants to add support for that device can come | ||
771 | up with a better workaround for the mess then. | ||
772 | */ | ||
773 | if (guid_already_parsed(gblock[i].guid) == true) { | ||
774 | wmi_gtoa(gblock[i].guid, guid_string); | ||
775 | printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n", | ||
776 | guid_string); | ||
777 | continue; | ||
778 | } | ||
588 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | 779 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); |
589 | if (!wblock) | 780 | if (!wblock) |
590 | return AE_NO_MEMORY; | 781 | return AE_NO_MEMORY; |
@@ -606,7 +797,7 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
606 | */ | 797 | */ |
607 | static acpi_status | 798 | static acpi_status |
608 | acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, | 799 | acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, |
609 | u32 bits, acpi_integer * value, | 800 | u32 bits, u64 *value, |
610 | void *handler_context, void *region_context) | 801 | void *handler_context, void *region_context) |
611 | { | 802 | { |
612 | int result = 0, i = 0; | 803 | int result = 0, i = 0; |
@@ -623,7 +814,7 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, | |||
623 | 814 | ||
624 | if (function == ACPI_READ) { | 815 | if (function == ACPI_READ) { |
625 | result = ec_read(address, &temp); | 816 | result = ec_read(address, &temp); |
626 | (*value) |= ((acpi_integer)temp) << i; | 817 | (*value) |= ((u64)temp) << i; |
627 | } else { | 818 | } else { |
628 | temp = 0xff & ((*value) >> i); | 819 | temp = 0xff & ((*value) >> i); |
629 | result = ec_write(address, temp); | 820 | result = ec_write(address, temp); |
@@ -709,10 +900,17 @@ static int __init acpi_wmi_init(void) | |||
709 | 900 | ||
710 | if (result < 0) { | 901 | if (result < 0) { |
711 | printk(KERN_INFO PREFIX "Error loading mapper\n"); | 902 | printk(KERN_INFO PREFIX "Error loading mapper\n"); |
712 | } else { | 903 | return -ENODEV; |
713 | printk(KERN_INFO PREFIX "Mapper loaded\n"); | 904 | } |
905 | |||
906 | result = wmi_class_init(); | ||
907 | if (result) { | ||
908 | acpi_bus_unregister_driver(&acpi_wmi_driver); | ||
909 | return result; | ||
714 | } | 910 | } |
715 | 911 | ||
912 | printk(KERN_INFO PREFIX "Mapper loaded\n"); | ||
913 | |||
716 | return result; | 914 | return result; |
717 | } | 915 | } |
718 | 916 | ||
@@ -721,6 +919,8 @@ static void __exit acpi_wmi_exit(void) | |||
721 | struct list_head *p, *tmp; | 919 | struct list_head *p, *tmp; |
722 | struct wmi_block *wblock; | 920 | struct wmi_block *wblock; |
723 | 921 | ||
922 | wmi_class_exit(); | ||
923 | |||
724 | acpi_bus_unregister_driver(&acpi_wmi_driver); | 924 | acpi_bus_unregister_driver(&acpi_wmi_driver); |
725 | 925 | ||
726 | list_for_each_safe(p, tmp, &wmi_blocks.list) { | 926 | list_for_each_safe(p, tmp, &wmi_blocks.list) { |