diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 12:32:11 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 12:32:11 -0400 |
commit | c11f6c82581e8be4e1829c677db54e7f55cebece (patch) | |
tree | 1a116241b0831ded998aabe800bdc24104cbd826 /drivers/platform | |
parent | 40aba218969914d1b225e742617adb921cf94eae (diff) | |
parent | 193a6dec1c0246a80b6d0101e4f351ccf877bcac (diff) |
Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6
* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6: (119 commits)
ACPI: don't pass handle for fixed hardware notifications
ACPI: remove null pointer checks in deferred execution path
ACPI: simplify deferred execution path
acerhdf: additional BIOS versions
acerhdf: convert to dev_pm_ops
acerhdf: fix fan control for AOA150 model
thermal: add missing Kconfig dependency
acpi: switch /proc/acpi/{debug_layer,debug_level} to seq_file
hp-wmi: fix rfkill memory leak on unload
ACPI: remove unnecessary #ifdef CONFIG_DMI
ACPI: linux/acpi.h should not include linux/dmi.h
hwmon driver for ACPI 4.0 power meters
topstar-laptop: add new driver for hotkeys support on Topstar N01
thinkpad_acpi: fix rfkill memory leak on unload
thinkpad-acpi: report brightness events when required
thinkpad-acpi: don't poll by default any of the reserved hotkeys
thinkpad-acpi: Fix procfs hotkey reset command
thinkpad-acpi: deprecate hotkey_bios_mask
thinkpad-acpi: hotkey poll fixes
thinkpad-acpi: be more strict when detecting a ThinkPad
...
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/Kconfig | 10 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/acerhdf.c | 121 | ||||
-rw-r--r-- | drivers/platform/x86/asus-laptop.c | 227 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 340 | ||||
-rw-r--r-- | drivers/platform/x86/fujitsu-laptop.c | 109 | ||||
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 7 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 382 | ||||
-rw-r--r-- | drivers/platform/x86/topstar-laptop.c | 265 | ||||
-rw-r--r-- | drivers/platform/x86/wmi.c | 1 |
11 files changed, 1093 insertions, 372 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 77c6097ced80..55ca39dea42e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -99,6 +99,7 @@ config FUJITSU_LAPTOP | |||
99 | depends on ACPI | 99 | depends on ACPI |
100 | depends on INPUT | 100 | depends on INPUT |
101 | depends on BACKLIGHT_CLASS_DEVICE | 101 | depends on BACKLIGHT_CLASS_DEVICE |
102 | depends on LEDS_CLASS || LEDS_CLASS=n | ||
102 | ---help--- | 103 | ---help--- |
103 | This is a driver for laptops built by Fujitsu: | 104 | This is a driver for laptops built by Fujitsu: |
104 | 105 | ||
@@ -396,6 +397,15 @@ config ACPI_ASUS | |||
396 | NOTE: This driver is deprecated and will probably be removed soon, | 397 | NOTE: This driver is deprecated and will probably be removed soon, |
397 | use asus-laptop instead. | 398 | use asus-laptop instead. |
398 | 399 | ||
400 | config TOPSTAR_LAPTOP | ||
401 | tristate "Topstar Laptop Extras" | ||
402 | depends on ACPI | ||
403 | depends on INPUT | ||
404 | ---help--- | ||
405 | This driver adds support for hotkeys found on Topstar laptops. | ||
406 | |||
407 | If you have a Topstar laptop, say Y or M here. | ||
408 | |||
399 | config ACPI_TOSHIBA | 409 | config ACPI_TOSHIBA |
400 | tristate "Toshiba Laptop Extras" | 410 | tristate "Toshiba Laptop Extras" |
401 | depends on ACPI | 411 | depends on ACPI |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 641b8bfa5538..d1c16210a512 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
@@ -19,4 +19,5 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o | |||
19 | obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o | 19 | obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o |
20 | obj-$(CONFIG_ACPI_WMI) += wmi.o | 20 | obj-$(CONFIG_ACPI_WMI) += wmi.o |
21 | obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o | 21 | obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o |
22 | obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o | ||
22 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o | 23 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o |
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index bdfee177eefb..0a8f735f6c4a 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.13" | 55 | #define DRV_VER "0.5.17" |
56 | 56 | ||
57 | /* | 57 | /* |
58 | * According to the Atom N270 datasheet, | 58 | * According to the Atom N270 datasheet, |
@@ -90,6 +90,7 @@ static unsigned int fanoff = 58; | |||
90 | static unsigned int verbose; | 90 | static unsigned int verbose; |
91 | static unsigned int fanstate = ACERHDF_FAN_AUTO; | 91 | static unsigned int fanstate = ACERHDF_FAN_AUTO; |
92 | static char force_bios[16]; | 92 | static char force_bios[16]; |
93 | static char force_product[16]; | ||
93 | static unsigned int prev_interval; | 94 | static unsigned int prev_interval; |
94 | struct thermal_zone_device *thz_dev; | 95 | struct thermal_zone_device *thz_dev; |
95 | struct thermal_cooling_device *cl_dev; | 96 | struct thermal_cooling_device *cl_dev; |
@@ -107,34 +108,62 @@ module_param(verbose, uint, 0600); | |||
107 | MODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); | 108 | MODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); |
108 | module_param_string(force_bios, force_bios, 16, 0); | 109 | module_param_string(force_bios, force_bios, 16, 0); |
109 | MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check"); | 110 | MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check"); |
111 | module_param_string(force_product, force_product, 16, 0); | ||
112 | MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check"); | ||
113 | |||
114 | /* | ||
115 | * cmd_off: to switch the fan completely 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 | * the fan speed depending on the temperature | ||
118 | */ | ||
119 | struct fancmd { | ||
120 | u8 cmd_off; | ||
121 | u8 cmd_auto; | ||
122 | }; | ||
110 | 123 | ||
111 | /* BIOS settings */ | 124 | /* BIOS settings */ |
112 | struct bios_settings_t { | 125 | struct bios_settings_t { |
113 | const char *vendor; | 126 | const char *vendor; |
127 | const char *product; | ||
114 | const char *version; | 128 | const char *version; |
115 | unsigned char fanreg; | 129 | unsigned char fanreg; |
116 | unsigned char tempreg; | 130 | unsigned char tempreg; |
117 | unsigned char fancmd[2]; /* fan off and auto commands */ | 131 | struct fancmd cmd; |
118 | }; | 132 | }; |
119 | 133 | ||
120 | /* Register addresses and values for different BIOS versions */ | 134 | /* Register addresses and values for different BIOS versions */ |
121 | static const struct bios_settings_t bios_tbl[] = { | 135 | static const struct bios_settings_t bios_tbl[] = { |
122 | {"Acer", "v0.3109", 0x55, 0x58, {0x1f, 0x00} }, | 136 | /* AOA110 */ |
123 | {"Acer", "v0.3114", 0x55, 0x58, {0x1f, 0x00} }, | 137 | {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00} }, |
124 | {"Acer", "v0.3301", 0x55, 0x58, {0xaf, 0x00} }, | 138 | {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00} }, |
125 | {"Acer", "v0.3304", 0x55, 0x58, {0xaf, 0x00} }, | 139 | {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00} }, |
126 | {"Acer", "v0.3305", 0x55, 0x58, {0xaf, 0x00} }, | 140 | {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00} }, |
127 | {"Acer", "v0.3308", 0x55, 0x58, {0x21, 0x00} }, | 141 | {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00} }, |
128 | {"Acer", "v0.3309", 0x55, 0x58, {0x21, 0x00} }, | 142 | {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00} }, |
129 | {"Acer", "v0.3310", 0x55, 0x58, {0x21, 0x00} }, | 143 | {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00} }, |
130 | {"Gateway", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, | 144 | {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} }, |
131 | {"Packard Bell", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, | 145 | {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} }, |
132 | {"", "", 0, 0, {0, 0} } | 146 | /* AOA150 */ |
147 | {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x00} }, | ||
148 | {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00} }, | ||
149 | {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00} }, | ||
150 | {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00} }, | ||
151 | {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00} }, | ||
152 | {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} }, | ||
153 | {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} }, | ||
154 | {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} }, | ||
155 | /* special BIOS / other */ | ||
156 | {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, | ||
157 | {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00} }, | ||
158 | {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} }, | ||
159 | {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, | ||
160 | {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} }, | ||
161 | /* pewpew-terminator */ | ||
162 | {"", "", "", 0, 0, {0, 0} } | ||
133 | }; | 163 | }; |
134 | 164 | ||
135 | static const struct bios_settings_t *bios_cfg __read_mostly; | 165 | static const struct bios_settings_t *bios_cfg __read_mostly; |
136 | 166 | ||
137 | |||
138 | static int acerhdf_get_temp(int *temp) | 167 | static int acerhdf_get_temp(int *temp) |
139 | { | 168 | { |
140 | u8 read_temp; | 169 | u8 read_temp; |
@@ -150,13 +179,14 @@ static int acerhdf_get_temp(int *temp) | |||
150 | static int acerhdf_get_fanstate(int *state) | 179 | static int acerhdf_get_fanstate(int *state) |
151 | { | 180 | { |
152 | u8 fan; | 181 | u8 fan; |
153 | bool tmp; | ||
154 | 182 | ||
155 | if (ec_read(bios_cfg->fanreg, &fan)) | 183 | if (ec_read(bios_cfg->fanreg, &fan)) |
156 | return -EINVAL; | 184 | return -EINVAL; |
157 | 185 | ||
158 | tmp = (fan == bios_cfg->fancmd[ACERHDF_FAN_OFF]); | 186 | if (fan != bios_cfg->cmd.cmd_off) |
159 | *state = tmp ? ACERHDF_FAN_OFF : ACERHDF_FAN_AUTO; | 187 | *state = ACERHDF_FAN_AUTO; |
188 | else | ||
189 | *state = ACERHDF_FAN_OFF; | ||
160 | 190 | ||
161 | return 0; | 191 | return 0; |
162 | } | 192 | } |
@@ -175,7 +205,8 @@ static void acerhdf_change_fanstate(int state) | |||
175 | state = ACERHDF_FAN_AUTO; | 205 | state = ACERHDF_FAN_AUTO; |
176 | } | 206 | } |
177 | 207 | ||
178 | cmd = bios_cfg->fancmd[state]; | 208 | cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off |
209 | : bios_cfg->cmd.cmd_auto; | ||
179 | fanstate = state; | 210 | fanstate = state; |
180 | 211 | ||
181 | ec_write(bios_cfg->fanreg, cmd); | 212 | ec_write(bios_cfg->fanreg, cmd); |
@@ -408,7 +439,7 @@ struct thermal_cooling_device_ops acerhdf_cooling_ops = { | |||
408 | }; | 439 | }; |
409 | 440 | ||
410 | /* suspend / resume functionality */ | 441 | /* suspend / resume functionality */ |
411 | static int acerhdf_suspend(struct platform_device *dev, pm_message_t state) | 442 | static int acerhdf_suspend(struct device *dev) |
412 | { | 443 | { |
413 | if (kernelmode) | 444 | if (kernelmode) |
414 | acerhdf_change_fanstate(ACERHDF_FAN_AUTO); | 445 | acerhdf_change_fanstate(ACERHDF_FAN_AUTO); |
@@ -419,14 +450,6 @@ static int acerhdf_suspend(struct platform_device *dev, pm_message_t state) | |||
419 | return 0; | 450 | return 0; |
420 | } | 451 | } |
421 | 452 | ||
422 | static int acerhdf_resume(struct platform_device *device) | ||
423 | { | ||
424 | if (verbose) | ||
425 | pr_notice("resuming\n"); | ||
426 | |||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | static int __devinit acerhdf_probe(struct platform_device *device) | 453 | static int __devinit acerhdf_probe(struct platform_device *device) |
431 | { | 454 | { |
432 | return 0; | 455 | return 0; |
@@ -437,15 +460,19 @@ static int acerhdf_remove(struct platform_device *device) | |||
437 | return 0; | 460 | return 0; |
438 | } | 461 | } |
439 | 462 | ||
440 | struct platform_driver acerhdf_drv = { | 463 | static struct dev_pm_ops acerhdf_pm_ops = { |
464 | .suspend = acerhdf_suspend, | ||
465 | .freeze = acerhdf_suspend, | ||
466 | }; | ||
467 | |||
468 | static struct platform_driver acerhdf_driver = { | ||
441 | .driver = { | 469 | .driver = { |
442 | .name = "acerhdf", | 470 | .name = "acerhdf", |
443 | .owner = THIS_MODULE, | 471 | .owner = THIS_MODULE, |
472 | .pm = &acerhdf_pm_ops, | ||
444 | }, | 473 | }, |
445 | .probe = acerhdf_probe, | 474 | .probe = acerhdf_probe, |
446 | .remove = acerhdf_remove, | 475 | .remove = acerhdf_remove, |
447 | .suspend = acerhdf_suspend, | ||
448 | .resume = acerhdf_resume, | ||
449 | }; | 476 | }; |
450 | 477 | ||
451 | 478 | ||
@@ -454,32 +481,40 @@ static int acerhdf_check_hardware(void) | |||
454 | { | 481 | { |
455 | char const *vendor, *version, *product; | 482 | char const *vendor, *version, *product; |
456 | int i; | 483 | int i; |
484 | unsigned long prod_len = 0; | ||
457 | 485 | ||
458 | /* get BIOS data */ | 486 | /* get BIOS data */ |
459 | vendor = dmi_get_system_info(DMI_SYS_VENDOR); | 487 | vendor = dmi_get_system_info(DMI_SYS_VENDOR); |
460 | version = dmi_get_system_info(DMI_BIOS_VERSION); | 488 | version = dmi_get_system_info(DMI_BIOS_VERSION); |
461 | product = dmi_get_system_info(DMI_PRODUCT_NAME); | 489 | product = dmi_get_system_info(DMI_PRODUCT_NAME); |
462 | 490 | ||
491 | |||
463 | pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); | 492 | pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); |
464 | 493 | ||
465 | if (!force_bios[0]) { | 494 | if (force_bios[0]) { |
466 | if (strncmp(product, "AO", 2)) { | ||
467 | pr_err("no Aspire One hardware found\n"); | ||
468 | return -EINVAL; | ||
469 | } | ||
470 | } else { | ||
471 | pr_info("forcing BIOS version: %s\n", version); | ||
472 | version = force_bios; | 495 | version = force_bios; |
496 | pr_info("forcing BIOS version: %s\n", version); | ||
497 | kernelmode = 0; | ||
498 | } | ||
499 | |||
500 | if (force_product[0]) { | ||
501 | product = force_product; | ||
502 | pr_info("forcing BIOS product: %s\n", product); | ||
473 | kernelmode = 0; | 503 | kernelmode = 0; |
474 | } | 504 | } |
475 | 505 | ||
506 | prod_len = strlen(product); | ||
507 | |||
476 | if (verbose) | 508 | if (verbose) |
477 | pr_info("BIOS info: %s %s, product: %s\n", | 509 | pr_info("BIOS info: %s %s, product: %s\n", |
478 | vendor, version, product); | 510 | vendor, version, product); |
479 | 511 | ||
480 | /* search BIOS version and vendor in BIOS settings table */ | 512 | /* search BIOS version and vendor in BIOS settings table */ |
481 | for (i = 0; bios_tbl[i].version[0]; i++) { | 513 | for (i = 0; bios_tbl[i].version[0]; i++) { |
482 | if (!strcmp(bios_tbl[i].vendor, vendor) && | 514 | if (strlen(bios_tbl[i].product) >= prod_len && |
515 | !strncmp(bios_tbl[i].product, product, | ||
516 | strlen(bios_tbl[i].product)) && | ||
517 | !strcmp(bios_tbl[i].vendor, vendor) && | ||
483 | !strcmp(bios_tbl[i].version, version)) { | 518 | !strcmp(bios_tbl[i].version, version)) { |
484 | bios_cfg = &bios_tbl[i]; | 519 | bios_cfg = &bios_tbl[i]; |
485 | break; | 520 | break; |
@@ -487,8 +522,8 @@ static int acerhdf_check_hardware(void) | |||
487 | } | 522 | } |
488 | 523 | ||
489 | if (!bios_cfg) { | 524 | if (!bios_cfg) { |
490 | pr_err("unknown (unsupported) BIOS version %s/%s, " | 525 | pr_err("unknown (unsupported) BIOS version %s/%s/%s, " |
491 | "please report, aborting!\n", vendor, version); | 526 | "please report, aborting!\n", vendor, product, version); |
492 | return -EINVAL; | 527 | return -EINVAL; |
493 | } | 528 | } |
494 | 529 | ||
@@ -509,7 +544,7 @@ static int acerhdf_register_platform(void) | |||
509 | { | 544 | { |
510 | int err = 0; | 545 | int err = 0; |
511 | 546 | ||
512 | err = platform_driver_register(&acerhdf_drv); | 547 | err = platform_driver_register(&acerhdf_driver); |
513 | if (err) | 548 | if (err) |
514 | return err; | 549 | return err; |
515 | 550 | ||
@@ -525,7 +560,7 @@ static void acerhdf_unregister_platform(void) | |||
525 | return; | 560 | return; |
526 | 561 | ||
527 | platform_device_del(acerhdf_dev); | 562 | platform_device_del(acerhdf_dev); |
528 | platform_driver_unregister(&acerhdf_drv); | 563 | platform_driver_unregister(&acerhdf_driver); |
529 | } | 564 | } |
530 | 565 | ||
531 | static int acerhdf_register_thermal(void) | 566 | static int acerhdf_register_thermal(void) |
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index db657bbeec90..b39d2bb3e75b 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c | |||
@@ -77,15 +77,16 @@ | |||
77 | * Flags for hotk status | 77 | * Flags for hotk status |
78 | * WL_ON and BT_ON are also used for wireless_status() | 78 | * WL_ON and BT_ON are also used for wireless_status() |
79 | */ | 79 | */ |
80 | #define WL_ON 0x01 //internal Wifi | 80 | #define WL_ON 0x01 /* internal Wifi */ |
81 | #define BT_ON 0x02 //internal Bluetooth | 81 | #define BT_ON 0x02 /* internal Bluetooth */ |
82 | #define MLED_ON 0x04 //mail LED | 82 | #define MLED_ON 0x04 /* mail LED */ |
83 | #define TLED_ON 0x08 //touchpad LED | 83 | #define TLED_ON 0x08 /* touchpad LED */ |
84 | #define RLED_ON 0x10 //Record LED | 84 | #define RLED_ON 0x10 /* Record LED */ |
85 | #define PLED_ON 0x20 //Phone LED | 85 | #define PLED_ON 0x20 /* Phone LED */ |
86 | #define GLED_ON 0x40 //Gaming LED | 86 | #define GLED_ON 0x40 /* Gaming LED */ |
87 | #define LCD_ON 0x80 //LCD backlight | 87 | #define LCD_ON 0x80 /* LCD backlight */ |
88 | #define GPS_ON 0x100 //GPS | 88 | #define GPS_ON 0x100 /* GPS */ |
89 | #define KEY_ON 0x200 /* Keyboard backlight */ | ||
89 | 90 | ||
90 | #define ASUS_LOG ASUS_HOTK_FILE ": " | 91 | #define ASUS_LOG ASUS_HOTK_FILE ": " |
91 | #define ASUS_ERR KERN_ERR ASUS_LOG | 92 | #define ASUS_ERR KERN_ERR ASUS_LOG |
@@ -98,7 +99,8 @@ MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary"); | |||
98 | MODULE_DESCRIPTION(ASUS_HOTK_NAME); | 99 | MODULE_DESCRIPTION(ASUS_HOTK_NAME); |
99 | MODULE_LICENSE("GPL"); | 100 | MODULE_LICENSE("GPL"); |
100 | 101 | ||
101 | /* WAPF defines the behavior of the Fn+Fx wlan key | 102 | /* |
103 | * WAPF defines the behavior of the Fn+Fx wlan key | ||
102 | * The significance of values is yet to be found, but | 104 | * The significance of values is yet to be found, but |
103 | * most of the time: | 105 | * most of the time: |
104 | * 0x0 will do nothing | 106 | * 0x0 will do nothing |
@@ -125,7 +127,8 @@ ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */ | |||
125 | /* LEDD */ | 127 | /* LEDD */ |
126 | ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM"); | 128 | ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM"); |
127 | 129 | ||
128 | /* Bluetooth and WLAN | 130 | /* |
131 | * Bluetooth and WLAN | ||
129 | * WLED and BLED are not handled like other XLED, because in some dsdt | 132 | * WLED and BLED are not handled like other XLED, because in some dsdt |
130 | * they also control the WLAN/Bluetooth device. | 133 | * they also control the WLAN/Bluetooth device. |
131 | */ | 134 | */ |
@@ -149,22 +152,32 @@ ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */ | |||
149 | 152 | ||
150 | /* Display */ | 153 | /* Display */ |
151 | ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP"); | 154 | ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP"); |
152 | ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD", /* A6B, A6K A6R A7D F3JM L4R M6R A3G | 155 | ASUS_HANDLE(display_get, |
153 | M6A M6V VX-1 V6J V6V W3Z */ | 156 | /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */ |
154 | "\\_SB.PCI0.P0P2.VGA.GETD", /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V | 157 | "\\_SB.PCI0.P0P1.VGA.GETD", |
155 | S5A M5A z33A W1Jc W2V G1 */ | 158 | /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */ |
156 | "\\_SB.PCI0.P0P3.VGA.GETD", /* A6V A6Q */ | 159 | "\\_SB.PCI0.P0P2.VGA.GETD", |
157 | "\\_SB.PCI0.P0PA.VGA.GETD", /* A6T, A6M */ | 160 | /* A6V A6Q */ |
158 | "\\_SB.PCI0.PCI1.VGAC.NMAP", /* L3C */ | 161 | "\\_SB.PCI0.P0P3.VGA.GETD", |
159 | "\\_SB.PCI0.VGA.GETD", /* Z96F */ | 162 | /* A6T, A6M */ |
160 | "\\ACTD", /* A2D */ | 163 | "\\_SB.PCI0.P0PA.VGA.GETD", |
161 | "\\ADVG", /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ | 164 | /* L3C */ |
162 | "\\DNXT", /* P30 */ | 165 | "\\_SB.PCI0.PCI1.VGAC.NMAP", |
163 | "\\INFB", /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ | 166 | /* Z96F */ |
164 | "\\SSTE"); /* A3F A6F A3N A3L M6N W3N W6A */ | 167 | "\\_SB.PCI0.VGA.GETD", |
165 | 168 | /* A2D */ | |
166 | ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */ | 169 | "\\ACTD", |
167 | ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */ | 170 | /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ |
171 | "\\ADVG", | ||
172 | /* P30 */ | ||
173 | "\\DNXT", | ||
174 | /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ | ||
175 | "\\INFB", | ||
176 | /* A3F A6F A3N A3L M6N W3N W6A */ | ||
177 | "\\SSTE"); | ||
178 | |||
179 | ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */ | ||
180 | ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */ | ||
168 | 181 | ||
169 | /* GPS */ | 182 | /* GPS */ |
170 | /* R2H use different handle for GPS on/off */ | 183 | /* R2H use different handle for GPS on/off */ |
@@ -172,19 +185,23 @@ ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON"); /* R2H */ | |||
172 | ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */ | 185 | ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */ |
173 | ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST"); | 186 | ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST"); |
174 | 187 | ||
188 | /* Keyboard light */ | ||
189 | ASUS_HANDLE(kled_set, ASUS_HOTK_PREFIX "SLKB"); | ||
190 | ASUS_HANDLE(kled_get, ASUS_HOTK_PREFIX "GLKB"); | ||
191 | |||
175 | /* | 192 | /* |
176 | * This is the main structure, we can use it to store anything interesting | 193 | * This is the main structure, we can use it to store anything interesting |
177 | * about the hotk device | 194 | * about the hotk device |
178 | */ | 195 | */ |
179 | struct asus_hotk { | 196 | struct asus_hotk { |
180 | char *name; //laptop name | 197 | char *name; /* laptop name */ |
181 | struct acpi_device *device; //the device we are in | 198 | struct acpi_device *device; /* the device we are in */ |
182 | acpi_handle handle; //the handle of the hotk device | 199 | acpi_handle handle; /* the handle of the hotk device */ |
183 | char status; //status of the hotk, for LEDs, ... | 200 | char status; /* status of the hotk, for LEDs, ... */ |
184 | u32 ledd_status; //status of the LED display | 201 | u32 ledd_status; /* status of the LED display */ |
185 | u8 light_level; //light sensor level | 202 | u8 light_level; /* light sensor level */ |
186 | u8 light_switch; //light sensor switch value | 203 | u8 light_switch; /* light sensor switch value */ |
187 | u16 event_count[128]; //count for each event TODO make this better | 204 | u16 event_count[128]; /* count for each event TODO make this better */ |
188 | struct input_dev *inputdev; | 205 | struct input_dev *inputdev; |
189 | u16 *keycode_map; | 206 | u16 *keycode_map; |
190 | }; | 207 | }; |
@@ -237,28 +254,35 @@ static struct backlight_ops asusbl_ops = { | |||
237 | .update_status = update_bl_status, | 254 | .update_status = update_bl_status, |
238 | }; | 255 | }; |
239 | 256 | ||
240 | /* These functions actually update the LED's, and are called from a | 257 | /* |
258 | * These functions actually update the LED's, and are called from a | ||
241 | * workqueue. By doing this as separate work rather than when the LED | 259 | * workqueue. By doing this as separate work rather than when the LED |
242 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a | 260 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a |
243 | * potentially bad time, such as a timer interrupt. */ | 261 | * potentially bad time, such as a timer interrupt. |
262 | */ | ||
244 | static struct workqueue_struct *led_workqueue; | 263 | static struct workqueue_struct *led_workqueue; |
245 | 264 | ||
246 | #define ASUS_LED(object, ledname) \ | 265 | #define ASUS_LED(object, ledname, max) \ |
247 | static void object##_led_set(struct led_classdev *led_cdev, \ | 266 | static void object##_led_set(struct led_classdev *led_cdev, \ |
248 | enum led_brightness value); \ | 267 | enum led_brightness value); \ |
268 | static enum led_brightness object##_led_get( \ | ||
269 | struct led_classdev *led_cdev); \ | ||
249 | static void object##_led_update(struct work_struct *ignored); \ | 270 | static void object##_led_update(struct work_struct *ignored); \ |
250 | static int object##_led_wk; \ | 271 | static int object##_led_wk; \ |
251 | static DECLARE_WORK(object##_led_work, object##_led_update); \ | 272 | static DECLARE_WORK(object##_led_work, object##_led_update); \ |
252 | static struct led_classdev object##_led = { \ | 273 | static struct led_classdev object##_led = { \ |
253 | .name = "asus::" ledname, \ | 274 | .name = "asus::" ledname, \ |
254 | .brightness_set = object##_led_set, \ | 275 | .brightness_set = object##_led_set, \ |
276 | .brightness_get = object##_led_get, \ | ||
277 | .max_brightness = max \ | ||
255 | } | 278 | } |
256 | 279 | ||
257 | ASUS_LED(mled, "mail"); | 280 | ASUS_LED(mled, "mail", 1); |
258 | ASUS_LED(tled, "touchpad"); | 281 | ASUS_LED(tled, "touchpad", 1); |
259 | ASUS_LED(rled, "record"); | 282 | ASUS_LED(rled, "record", 1); |
260 | ASUS_LED(pled, "phone"); | 283 | ASUS_LED(pled, "phone", 1); |
261 | ASUS_LED(gled, "gaming"); | 284 | ASUS_LED(gled, "gaming", 1); |
285 | ASUS_LED(kled, "kbd_backlight", 3); | ||
262 | 286 | ||
263 | struct key_entry { | 287 | struct key_entry { |
264 | char type; | 288 | char type; |
@@ -278,16 +302,23 @@ static struct key_entry asus_keymap[] = { | |||
278 | {KE_KEY, 0x41, KEY_NEXTSONG}, | 302 | {KE_KEY, 0x41, KEY_NEXTSONG}, |
279 | {KE_KEY, 0x43, KEY_STOPCD}, | 303 | {KE_KEY, 0x43, KEY_STOPCD}, |
280 | {KE_KEY, 0x45, KEY_PLAYPAUSE}, | 304 | {KE_KEY, 0x45, KEY_PLAYPAUSE}, |
305 | {KE_KEY, 0x4c, KEY_MEDIA}, | ||
281 | {KE_KEY, 0x50, KEY_EMAIL}, | 306 | {KE_KEY, 0x50, KEY_EMAIL}, |
282 | {KE_KEY, 0x51, KEY_WWW}, | 307 | {KE_KEY, 0x51, KEY_WWW}, |
308 | {KE_KEY, 0x55, KEY_CALC}, | ||
283 | {KE_KEY, 0x5C, KEY_SCREENLOCK}, /* Screenlock */ | 309 | {KE_KEY, 0x5C, KEY_SCREENLOCK}, /* Screenlock */ |
284 | {KE_KEY, 0x5D, KEY_WLAN}, | 310 | {KE_KEY, 0x5D, KEY_WLAN}, |
311 | {KE_KEY, 0x5E, KEY_WLAN}, | ||
312 | {KE_KEY, 0x5F, KEY_WLAN}, | ||
313 | {KE_KEY, 0x60, KEY_SWITCHVIDEOMODE}, | ||
285 | {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE}, | 314 | {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE}, |
286 | {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */ | 315 | {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */ |
287 | {KE_KEY, 0x82, KEY_CAMERA}, | 316 | {KE_KEY, 0x82, KEY_CAMERA}, |
288 | {KE_KEY, 0x8A, KEY_PROG1}, | 317 | {KE_KEY, 0x8A, KEY_PROG1}, |
289 | {KE_KEY, 0x95, KEY_MEDIA}, | 318 | {KE_KEY, 0x95, KEY_MEDIA}, |
290 | {KE_KEY, 0x99, KEY_PHONE}, | 319 | {KE_KEY, 0x99, KEY_PHONE}, |
320 | {KE_KEY, 0xc4, KEY_KBDILLUMUP}, | ||
321 | {KE_KEY, 0xc5, KEY_KBDILLUMDOWN}, | ||
291 | {KE_END, 0}, | 322 | {KE_END, 0}, |
292 | }; | 323 | }; |
293 | 324 | ||
@@ -301,8 +332,8 @@ static struct key_entry asus_keymap[] = { | |||
301 | static int write_acpi_int(acpi_handle handle, const char *method, int val, | 332 | static int write_acpi_int(acpi_handle handle, const char *method, int val, |
302 | struct acpi_buffer *output) | 333 | struct acpi_buffer *output) |
303 | { | 334 | { |
304 | struct acpi_object_list params; //list of input parameters (an int here) | 335 | struct acpi_object_list params; /* list of input parameters (an int) */ |
305 | union acpi_object in_obj; //the only param we use | 336 | union acpi_object in_obj; /* the only param we use */ |
306 | acpi_status status; | 337 | acpi_status status; |
307 | 338 | ||
308 | if (!handle) | 339 | if (!handle) |
@@ -399,6 +430,11 @@ static void write_status(acpi_handle handle, int out, int mask) | |||
399 | { \ | 430 | { \ |
400 | int value = object##_led_wk; \ | 431 | int value = object##_led_wk; \ |
401 | write_status(object##_set_handle, value, (mask)); \ | 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; \ | ||
402 | } | 438 | } |
403 | 439 | ||
404 | ASUS_LED_HANDLER(mled, MLED_ON); | 440 | ASUS_LED_HANDLER(mled, MLED_ON); |
@@ -407,6 +443,60 @@ ASUS_LED_HANDLER(rled, RLED_ON); | |||
407 | ASUS_LED_HANDLER(tled, TLED_ON); | 443 | ASUS_LED_HANDLER(tled, TLED_ON); |
408 | ASUS_LED_HANDLER(gled, GLED_ON); | 444 | ASUS_LED_HANDLER(gled, GLED_ON); |
409 | 445 | ||
446 | /* | ||
447 | * Keyboard backlight | ||
448 | */ | ||
449 | static int get_kled_lvl(void) | ||
450 | { | ||
451 | unsigned long long kblv; | ||
452 | struct acpi_object_list params; | ||
453 | union acpi_object in_obj; | ||
454 | acpi_status rv; | ||
455 | |||
456 | params.count = 1; | ||
457 | params.pointer = &in_obj; | ||
458 | in_obj.type = ACPI_TYPE_INTEGER; | ||
459 | in_obj.integer.value = 2; | ||
460 | |||
461 | rv = acpi_evaluate_integer(kled_get_handle, NULL, ¶ms, &kblv); | ||
462 | if (ACPI_FAILURE(rv)) { | ||
463 | pr_warning("Error reading kled level\n"); | ||
464 | return 0; | ||
465 | } | ||
466 | return kblv; | ||
467 | } | ||
468 | |||
469 | static int set_kled_lvl(int kblv) | ||
470 | { | ||
471 | if (kblv > 0) | ||
472 | kblv = (1 << 7) | (kblv & 0x7F); | ||
473 | else | ||
474 | kblv = 0; | ||
475 | |||
476 | if (write_acpi_int(kled_set_handle, NULL, kblv, NULL)) { | ||
477 | pr_warning("Keyboard LED display write failed\n"); | ||
478 | return -EINVAL; | ||
479 | } | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | static void kled_led_set(struct led_classdev *led_cdev, | ||
484 | enum led_brightness value) | ||
485 | { | ||
486 | kled_led_wk = value; | ||
487 | queue_work(led_workqueue, &kled_led_work); | ||
488 | } | ||
489 | |||
490 | static void kled_led_update(struct work_struct *ignored) | ||
491 | { | ||
492 | set_kled_lvl(kled_led_wk); | ||
493 | } | ||
494 | |||
495 | static enum led_brightness kled_led_get(struct led_classdev *led_cdev) | ||
496 | { | ||
497 | return get_kled_lvl(); | ||
498 | } | ||
499 | |||
410 | static int get_lcd_state(void) | 500 | static int get_lcd_state(void) |
411 | { | 501 | { |
412 | return read_status(LCD_ON); | 502 | return read_status(LCD_ON); |
@@ -498,7 +588,7 @@ static ssize_t show_infos(struct device *dev, | |||
498 | { | 588 | { |
499 | int len = 0; | 589 | int len = 0; |
500 | unsigned long long temp; | 590 | unsigned long long temp; |
501 | char buf[16]; //enough for all info | 591 | char buf[16]; /* enough for all info */ |
502 | acpi_status rv = AE_OK; | 592 | acpi_status rv = AE_OK; |
503 | 593 | ||
504 | /* | 594 | /* |
@@ -516,7 +606,17 @@ static ssize_t show_infos(struct device *dev, | |||
516 | */ | 606 | */ |
517 | rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp); | 607 | rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp); |
518 | if (!ACPI_FAILURE(rv)) | 608 | if (!ACPI_FAILURE(rv)) |
519 | len += sprintf(page + len, "SFUN value : 0x%04x\n", | 609 | len += sprintf(page + len, "SFUN value : %#x\n", |
610 | (uint) temp); | ||
611 | /* | ||
612 | * The HWRS method return informations about the hardware. | ||
613 | * 0x80 bit is for WLAN, 0x100 for Bluetooth. | ||
614 | * The significance of others is yet to be found. | ||
615 | * If we don't find the method, we assume the device are present. | ||
616 | */ | ||
617 | rv = acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &temp); | ||
618 | if (!ACPI_FAILURE(rv)) | ||
619 | len += sprintf(page + len, "HRWS value : %#x\n", | ||
520 | (uint) temp); | 620 | (uint) temp); |
521 | /* | 621 | /* |
522 | * Another value for userspace: the ASYM method returns 0x02 for | 622 | * Another value for userspace: the ASYM method returns 0x02 for |
@@ -527,7 +627,7 @@ static ssize_t show_infos(struct device *dev, | |||
527 | */ | 627 | */ |
528 | rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp); | 628 | rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp); |
529 | if (!ACPI_FAILURE(rv)) | 629 | if (!ACPI_FAILURE(rv)) |
530 | len += sprintf(page + len, "ASYM value : 0x%04x\n", | 630 | len += sprintf(page + len, "ASYM value : %#x\n", |
531 | (uint) temp); | 631 | (uint) temp); |
532 | if (asus_info) { | 632 | if (asus_info) { |
533 | snprintf(buf, 16, "%d", asus_info->length); | 633 | snprintf(buf, 16, "%d", asus_info->length); |
@@ -648,8 +748,10 @@ static int read_display(void) | |||
648 | unsigned long long value = 0; | 748 | unsigned long long value = 0; |
649 | acpi_status rv = AE_OK; | 749 | acpi_status rv = AE_OK; |
650 | 750 | ||
651 | /* In most of the case, we know how to set the display, but sometime | 751 | /* |
652 | we can't read it */ | 752 | * In most of the case, we know how to set the display, but sometime |
753 | * we can't read it | ||
754 | */ | ||
653 | if (display_get_handle) { | 755 | if (display_get_handle) { |
654 | rv = acpi_evaluate_integer(display_get_handle, NULL, | 756 | rv = acpi_evaluate_integer(display_get_handle, NULL, |
655 | NULL, &value); | 757 | NULL, &value); |
@@ -1037,6 +1139,9 @@ static int asus_hotk_get_info(void) | |||
1037 | 1139 | ||
1038 | ASUS_HANDLE_INIT(ledd_set); | 1140 | ASUS_HANDLE_INIT(ledd_set); |
1039 | 1141 | ||
1142 | ASUS_HANDLE_INIT(kled_set); | ||
1143 | ASUS_HANDLE_INIT(kled_get); | ||
1144 | |||
1040 | /* | 1145 | /* |
1041 | * The HWRS method return informations about the hardware. | 1146 | * The HWRS method return informations about the hardware. |
1042 | * 0x80 bit is for WLAN, 0x100 for Bluetooth. | 1147 | * 0x80 bit is for WLAN, 0x100 for Bluetooth. |
@@ -1063,8 +1168,10 @@ static int asus_hotk_get_info(void) | |||
1063 | ASUS_HANDLE_INIT(display_set); | 1168 | ASUS_HANDLE_INIT(display_set); |
1064 | ASUS_HANDLE_INIT(display_get); | 1169 | ASUS_HANDLE_INIT(display_get); |
1065 | 1170 | ||
1066 | /* There is a lot of models with "ALSL", but a few get | 1171 | /* |
1067 | a real light sens, so we need to check it. */ | 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 | */ | ||
1068 | if (!ASUS_HANDLE_INIT(ls_switch)) | 1175 | if (!ASUS_HANDLE_INIT(ls_switch)) |
1069 | ASUS_HANDLE_INIT(ls_level); | 1176 | ASUS_HANDLE_INIT(ls_level); |
1070 | 1177 | ||
@@ -1168,6 +1275,10 @@ static int asus_hotk_add(struct acpi_device *device) | |||
1168 | /* LCD Backlight is on by default */ | 1275 | /* LCD Backlight is on by default */ |
1169 | write_status(NULL, 1, LCD_ON); | 1276 | write_status(NULL, 1, LCD_ON); |
1170 | 1277 | ||
1278 | /* Keyboard Backlight is on by default */ | ||
1279 | if (kled_set_handle) | ||
1280 | set_kled_lvl(1); | ||
1281 | |||
1171 | /* LED display is off by default */ | 1282 | /* LED display is off by default */ |
1172 | hotk->ledd_status = 0xFFF; | 1283 | hotk->ledd_status = 0xFFF; |
1173 | 1284 | ||
@@ -1222,6 +1333,7 @@ static void asus_led_exit(void) | |||
1222 | ASUS_LED_UNREGISTER(pled); | 1333 | ASUS_LED_UNREGISTER(pled); |
1223 | ASUS_LED_UNREGISTER(rled); | 1334 | ASUS_LED_UNREGISTER(rled); |
1224 | ASUS_LED_UNREGISTER(gled); | 1335 | ASUS_LED_UNREGISTER(gled); |
1336 | ASUS_LED_UNREGISTER(kled); | ||
1225 | } | 1337 | } |
1226 | 1338 | ||
1227 | static void asus_input_exit(void) | 1339 | static void asus_input_exit(void) |
@@ -1301,13 +1413,20 @@ static int asus_led_init(struct device *dev) | |||
1301 | if (rv) | 1413 | if (rv) |
1302 | goto out4; | 1414 | goto out4; |
1303 | 1415 | ||
1416 | if (kled_set_handle && kled_get_handle) | ||
1417 | rv = ASUS_LED_REGISTER(kled, dev); | ||
1418 | if (rv) | ||
1419 | goto out5; | ||
1420 | |||
1304 | led_workqueue = create_singlethread_workqueue("led_workqueue"); | 1421 | led_workqueue = create_singlethread_workqueue("led_workqueue"); |
1305 | if (!led_workqueue) | 1422 | if (!led_workqueue) |
1306 | goto out5; | 1423 | goto out6; |
1307 | 1424 | ||
1308 | return 0; | 1425 | return 0; |
1309 | out5: | 1426 | out6: |
1310 | rv = -ENOMEM; | 1427 | rv = -ENOMEM; |
1428 | ASUS_LED_UNREGISTER(kled); | ||
1429 | out5: | ||
1311 | ASUS_LED_UNREGISTER(gled); | 1430 | ASUS_LED_UNREGISTER(gled); |
1312 | out4: | 1431 | out4: |
1313 | ASUS_LED_UNREGISTER(pled); | 1432 | ASUS_LED_UNREGISTER(pled); |
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 222ffb892f22..da3c08b3dcc1 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
@@ -142,18 +142,28 @@ struct eeepc_hotk { | |||
142 | struct rfkill *wlan_rfkill; | 142 | struct rfkill *wlan_rfkill; |
143 | struct rfkill *bluetooth_rfkill; | 143 | struct rfkill *bluetooth_rfkill; |
144 | struct rfkill *wwan3g_rfkill; | 144 | struct rfkill *wwan3g_rfkill; |
145 | struct rfkill *wimax_rfkill; | ||
145 | struct hotplug_slot *hotplug_slot; | 146 | struct hotplug_slot *hotplug_slot; |
146 | struct work_struct hotplug_work; | 147 | struct mutex hotplug_lock; |
147 | }; | 148 | }; |
148 | 149 | ||
149 | /* The actual device the driver binds to */ | 150 | /* The actual device the driver binds to */ |
150 | static struct eeepc_hotk *ehotk; | 151 | static struct eeepc_hotk *ehotk; |
151 | 152 | ||
152 | /* Platform device/driver */ | 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 | |||
153 | static struct platform_driver platform_driver = { | 162 | static struct platform_driver platform_driver = { |
154 | .driver = { | 163 | .driver = { |
155 | .name = EEEPC_HOTK_FILE, | 164 | .name = EEEPC_HOTK_FILE, |
156 | .owner = THIS_MODULE, | 165 | .owner = THIS_MODULE, |
166 | .pm = &eeepc_pm_ops, | ||
157 | } | 167 | } |
158 | }; | 168 | }; |
159 | 169 | ||
@@ -192,7 +202,6 @@ static struct key_entry eeepc_keymap[] = { | |||
192 | */ | 202 | */ |
193 | static int eeepc_hotk_add(struct acpi_device *device); | 203 | static int eeepc_hotk_add(struct acpi_device *device); |
194 | static int eeepc_hotk_remove(struct acpi_device *device, int type); | 204 | static int eeepc_hotk_remove(struct acpi_device *device, int type); |
195 | static int eeepc_hotk_resume(struct acpi_device *device); | ||
196 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event); | 205 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event); |
197 | 206 | ||
198 | static const struct acpi_device_id eeepc_device_ids[] = { | 207 | static const struct acpi_device_id eeepc_device_ids[] = { |
@@ -209,7 +218,6 @@ static struct acpi_driver eeepc_hotk_driver = { | |||
209 | .ops = { | 218 | .ops = { |
210 | .add = eeepc_hotk_add, | 219 | .add = eeepc_hotk_add, |
211 | .remove = eeepc_hotk_remove, | 220 | .remove = eeepc_hotk_remove, |
212 | .resume = eeepc_hotk_resume, | ||
213 | .notify = eeepc_hotk_notify, | 221 | .notify = eeepc_hotk_notify, |
214 | }, | 222 | }, |
215 | }; | 223 | }; |
@@ -579,7 +587,6 @@ static void cmsg_quirks(void) | |||
579 | 587 | ||
580 | static int eeepc_hotk_check(void) | 588 | static int eeepc_hotk_check(void) |
581 | { | 589 | { |
582 | const struct key_entry *key; | ||
583 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 590 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
584 | int result; | 591 | int result; |
585 | 592 | ||
@@ -604,31 +611,6 @@ static int eeepc_hotk_check(void) | |||
604 | pr_info("Get control methods supported: 0x%x\n", | 611 | pr_info("Get control methods supported: 0x%x\n", |
605 | ehotk->cm_supported); | 612 | ehotk->cm_supported); |
606 | } | 613 | } |
607 | ehotk->inputdev = input_allocate_device(); | ||
608 | if (!ehotk->inputdev) { | ||
609 | pr_info("Unable to allocate input device\n"); | ||
610 | return 0; | ||
611 | } | ||
612 | ehotk->inputdev->name = "Asus EeePC extra buttons"; | ||
613 | ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0"; | ||
614 | ehotk->inputdev->id.bustype = BUS_HOST; | ||
615 | ehotk->inputdev->getkeycode = eeepc_getkeycode; | ||
616 | ehotk->inputdev->setkeycode = eeepc_setkeycode; | ||
617 | |||
618 | for (key = eeepc_keymap; key->type != KE_END; key++) { | ||
619 | switch (key->type) { | ||
620 | case KE_KEY: | ||
621 | set_bit(EV_KEY, ehotk->inputdev->evbit); | ||
622 | set_bit(key->keycode, ehotk->inputdev->keybit); | ||
623 | break; | ||
624 | } | ||
625 | } | ||
626 | result = input_register_device(ehotk->inputdev); | ||
627 | if (result) { | ||
628 | pr_info("Unable to register input device\n"); | ||
629 | input_free_device(ehotk->inputdev); | ||
630 | return 0; | ||
631 | } | ||
632 | } else { | 614 | } else { |
633 | pr_err("Hotkey device not present, aborting\n"); | 615 | pr_err("Hotkey device not present, aborting\n"); |
634 | return -EINVAL; | 616 | return -EINVAL; |
@@ -661,40 +643,48 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, | |||
661 | return 0; | 643 | return 0; |
662 | } | 644 | } |
663 | 645 | ||
664 | static void eeepc_hotplug_work(struct work_struct *work) | 646 | static void eeepc_rfkill_hotplug(void) |
665 | { | 647 | { |
666 | struct pci_dev *dev; | 648 | struct pci_dev *dev; |
667 | struct pci_bus *bus = pci_find_bus(0, 1); | 649 | struct pci_bus *bus; |
668 | bool blocked; | 650 | bool blocked = eeepc_wlan_rfkill_blocked(); |
669 | 651 | ||
670 | if (!bus) { | 652 | if (ehotk->wlan_rfkill) |
671 | pr_warning("Unable to find PCI bus 1?\n"); | 653 | rfkill_set_sw_state(ehotk->wlan_rfkill, blocked); |
672 | return; | ||
673 | } | ||
674 | 654 | ||
675 | blocked = eeepc_wlan_rfkill_blocked(); | 655 | mutex_lock(&ehotk->hotplug_lock); |
676 | if (!blocked) { | 656 | |
677 | dev = pci_get_slot(bus, 0); | 657 | if (ehotk->hotplug_slot) { |
678 | if (dev) { | 658 | bus = pci_find_bus(0, 1); |
679 | /* Device already present */ | 659 | if (!bus) { |
680 | pci_dev_put(dev); | 660 | pr_warning("Unable to find PCI bus 1?\n"); |
681 | return; | 661 | goto out_unlock; |
682 | } | ||
683 | dev = pci_scan_single_device(bus, 0); | ||
684 | if (dev) { | ||
685 | pci_bus_assign_resources(bus); | ||
686 | if (pci_bus_add_device(dev)) | ||
687 | pr_err("Unable to hotplug wifi\n"); | ||
688 | } | 662 | } |
689 | } else { | 663 | |
690 | dev = pci_get_slot(bus, 0); | 664 | if (!blocked) { |
691 | if (dev) { | 665 | dev = pci_get_slot(bus, 0); |
692 | pci_remove_bus_device(dev); | 666 | if (dev) { |
693 | pci_dev_put(dev); | 667 | /* Device already present */ |
668 | pci_dev_put(dev); | ||
669 | goto out_unlock; | ||
670 | } | ||
671 | dev = pci_scan_single_device(bus, 0); | ||
672 | if (dev) { | ||
673 | pci_bus_assign_resources(bus); | ||
674 | if (pci_bus_add_device(dev)) | ||
675 | pr_err("Unable to hotplug wifi\n"); | ||
676 | } | ||
677 | } else { | ||
678 | dev = pci_get_slot(bus, 0); | ||
679 | if (dev) { | ||
680 | pci_remove_bus_device(dev); | ||
681 | pci_dev_put(dev); | ||
682 | } | ||
694 | } | 683 | } |
695 | } | 684 | } |
696 | 685 | ||
697 | rfkill_set_sw_state(ehotk->wlan_rfkill, blocked); | 686 | out_unlock: |
687 | mutex_unlock(&ehotk->hotplug_lock); | ||
698 | } | 688 | } |
699 | 689 | ||
700 | static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | 690 | static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) |
@@ -702,7 +692,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | |||
702 | if (event != ACPI_NOTIFY_BUS_CHECK) | 692 | if (event != ACPI_NOTIFY_BUS_CHECK) |
703 | return; | 693 | return; |
704 | 694 | ||
705 | schedule_work(&ehotk->hotplug_work); | 695 | eeepc_rfkill_hotplug(); |
706 | } | 696 | } |
707 | 697 | ||
708 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event) | 698 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event) |
@@ -839,66 +829,38 @@ error_slot: | |||
839 | return ret; | 829 | return ret; |
840 | } | 830 | } |
841 | 831 | ||
842 | static int eeepc_hotk_add(struct acpi_device *device) | 832 | static int eeepc_hotk_thaw(struct device *device) |
843 | { | ||
844 | int result; | ||
845 | |||
846 | if (!device) | ||
847 | return -EINVAL; | ||
848 | pr_notice(EEEPC_HOTK_NAME "\n"); | ||
849 | ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); | ||
850 | if (!ehotk) | ||
851 | return -ENOMEM; | ||
852 | ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; | ||
853 | ehotk->handle = device->handle; | ||
854 | strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME); | ||
855 | strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS); | ||
856 | device->driver_data = ehotk; | ||
857 | ehotk->device = device; | ||
858 | result = eeepc_hotk_check(); | ||
859 | if (result) | ||
860 | goto ehotk_fail; | ||
861 | |||
862 | return 0; | ||
863 | |||
864 | ehotk_fail: | ||
865 | kfree(ehotk); | ||
866 | ehotk = NULL; | ||
867 | |||
868 | return result; | ||
869 | } | ||
870 | |||
871 | static int eeepc_hotk_remove(struct acpi_device *device, int type) | ||
872 | { | ||
873 | if (!device || !acpi_driver_data(device)) | ||
874 | return -EINVAL; | ||
875 | |||
876 | kfree(ehotk); | ||
877 | return 0; | ||
878 | } | ||
879 | |||
880 | static int eeepc_hotk_resume(struct acpi_device *device) | ||
881 | { | 833 | { |
882 | if (ehotk->wlan_rfkill) { | 834 | if (ehotk->wlan_rfkill) { |
883 | bool wlan; | 835 | bool wlan; |
884 | 836 | ||
885 | /* Workaround - it seems that _PTS disables the wireless | 837 | /* |
886 | without notification or changing the value read by WLAN. | 838 | * Work around bios bug - acpi _PTS turns off the wireless led |
887 | Normally this is fine because the correct value is restored | 839 | * during suspend. Normally it restores it on resume, but |
888 | from the non-volatile storage on resume, but we need to do | 840 | * we should kick it ourselves in case hibernation is aborted. |
889 | it ourself if case suspend is aborted, or we lose wireless. | ||
890 | */ | 841 | */ |
891 | wlan = get_acpi(CM_ASL_WLAN); | 842 | wlan = get_acpi(CM_ASL_WLAN); |
892 | set_acpi(CM_ASL_WLAN, wlan); | 843 | set_acpi(CM_ASL_WLAN, wlan); |
844 | } | ||
893 | 845 | ||
894 | rfkill_set_sw_state(ehotk->wlan_rfkill, wlan != 1); | 846 | return 0; |
847 | } | ||
895 | 848 | ||
896 | schedule_work(&ehotk->hotplug_work); | 849 | static int eeepc_hotk_restore(struct device *device) |
897 | } | 850 | { |
851 | /* Refresh both wlan rfkill state and pci hotplug */ | ||
852 | if (ehotk->wlan_rfkill) | ||
853 | eeepc_rfkill_hotplug(); | ||
898 | 854 | ||
899 | if (ehotk->bluetooth_rfkill) | 855 | if (ehotk->bluetooth_rfkill) |
900 | rfkill_set_sw_state(ehotk->bluetooth_rfkill, | 856 | rfkill_set_sw_state(ehotk->bluetooth_rfkill, |
901 | get_acpi(CM_ASL_BLUETOOTH) != 1); | 857 | get_acpi(CM_ASL_BLUETOOTH) != 1); |
858 | if (ehotk->wwan3g_rfkill) | ||
859 | rfkill_set_sw_state(ehotk->wwan3g_rfkill, | ||
860 | get_acpi(CM_ASL_3G) != 1); | ||
861 | if (ehotk->wimax_rfkill) | ||
862 | rfkill_set_sw_state(ehotk->wimax_rfkill, | ||
863 | get_acpi(CM_ASL_WIMAX) != 1); | ||
902 | 864 | ||
903 | return 0; | 865 | return 0; |
904 | } | 866 | } |
@@ -1019,16 +981,37 @@ static void eeepc_backlight_exit(void) | |||
1019 | 981 | ||
1020 | static void eeepc_rfkill_exit(void) | 982 | static void eeepc_rfkill_exit(void) |
1021 | { | 983 | { |
984 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5"); | ||
1022 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); | 985 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); |
1023 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); | 986 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); |
1024 | if (ehotk->wlan_rfkill) | 987 | if (ehotk->wlan_rfkill) { |
1025 | rfkill_unregister(ehotk->wlan_rfkill); | 988 | rfkill_unregister(ehotk->wlan_rfkill); |
1026 | if (ehotk->bluetooth_rfkill) | 989 | rfkill_destroy(ehotk->wlan_rfkill); |
1027 | rfkill_unregister(ehotk->bluetooth_rfkill); | 990 | ehotk->wlan_rfkill = NULL; |
1028 | if (ehotk->wwan3g_rfkill) | 991 | } |
1029 | rfkill_unregister(ehotk->wwan3g_rfkill); | 992 | /* |
993 | * Refresh pci hotplug in case the rfkill state was changed after | ||
994 | * eeepc_unregister_rfkill_notifier() | ||
995 | */ | ||
996 | eeepc_rfkill_hotplug(); | ||
1030 | if (ehotk->hotplug_slot) | 997 | if (ehotk->hotplug_slot) |
1031 | pci_hp_deregister(ehotk->hotplug_slot); | 998 | pci_hp_deregister(ehotk->hotplug_slot); |
999 | |||
1000 | if (ehotk->bluetooth_rfkill) { | ||
1001 | rfkill_unregister(ehotk->bluetooth_rfkill); | ||
1002 | rfkill_destroy(ehotk->bluetooth_rfkill); | ||
1003 | ehotk->bluetooth_rfkill = NULL; | ||
1004 | } | ||
1005 | if (ehotk->wwan3g_rfkill) { | ||
1006 | rfkill_unregister(ehotk->wwan3g_rfkill); | ||
1007 | rfkill_destroy(ehotk->wwan3g_rfkill); | ||
1008 | ehotk->wwan3g_rfkill = NULL; | ||
1009 | } | ||
1010 | if (ehotk->wimax_rfkill) { | ||
1011 | rfkill_unregister(ehotk->wimax_rfkill); | ||
1012 | rfkill_destroy(ehotk->wimax_rfkill); | ||
1013 | ehotk->wimax_rfkill = NULL; | ||
1014 | } | ||
1032 | } | 1015 | } |
1033 | 1016 | ||
1034 | static void eeepc_input_exit(void) | 1017 | static void eeepc_input_exit(void) |
@@ -1050,19 +1033,6 @@ static void eeepc_hwmon_exit(void) | |||
1050 | eeepc_hwmon_device = NULL; | 1033 | eeepc_hwmon_device = NULL; |
1051 | } | 1034 | } |
1052 | 1035 | ||
1053 | static void __exit eeepc_laptop_exit(void) | ||
1054 | { | ||
1055 | eeepc_backlight_exit(); | ||
1056 | eeepc_rfkill_exit(); | ||
1057 | eeepc_input_exit(); | ||
1058 | eeepc_hwmon_exit(); | ||
1059 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | ||
1060 | sysfs_remove_group(&platform_device->dev.kobj, | ||
1061 | &platform_attribute_group); | ||
1062 | platform_device_unregister(platform_device); | ||
1063 | platform_driver_unregister(&platform_driver); | ||
1064 | } | ||
1065 | |||
1066 | static int eeepc_new_rfkill(struct rfkill **rfkill, | 1036 | static int eeepc_new_rfkill(struct rfkill **rfkill, |
1067 | const char *name, struct device *dev, | 1037 | const char *name, struct device *dev, |
1068 | enum rfkill_type type, int cm) | 1038 | enum rfkill_type type, int cm) |
@@ -1094,10 +1064,7 @@ static int eeepc_rfkill_init(struct device *dev) | |||
1094 | { | 1064 | { |
1095 | int result = 0; | 1065 | int result = 0; |
1096 | 1066 | ||
1097 | INIT_WORK(&ehotk->hotplug_work, eeepc_hotplug_work); | 1067 | mutex_init(&ehotk->hotplug_lock); |
1098 | |||
1099 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); | ||
1100 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); | ||
1101 | 1068 | ||
1102 | result = eeepc_new_rfkill(&ehotk->wlan_rfkill, | 1069 | result = eeepc_new_rfkill(&ehotk->wlan_rfkill, |
1103 | "eeepc-wlan", dev, | 1070 | "eeepc-wlan", dev, |
@@ -1120,6 +1087,13 @@ static int eeepc_rfkill_init(struct device *dev) | |||
1120 | if (result && result != -ENODEV) | 1087 | if (result && result != -ENODEV) |
1121 | goto exit; | 1088 | goto exit; |
1122 | 1089 | ||
1090 | result = eeepc_new_rfkill(&ehotk->wimax_rfkill, | ||
1091 | "eeepc-wimax", dev, | ||
1092 | RFKILL_TYPE_WIMAX, CM_ASL_WIMAX); | ||
1093 | |||
1094 | if (result && result != -ENODEV) | ||
1095 | goto exit; | ||
1096 | |||
1123 | result = eeepc_setup_pci_hotplug(); | 1097 | result = eeepc_setup_pci_hotplug(); |
1124 | /* | 1098 | /* |
1125 | * If we get -EBUSY then something else is handling the PCI hotplug - | 1099 | * If we get -EBUSY then something else is handling the PCI hotplug - |
@@ -1128,6 +1102,15 @@ static int eeepc_rfkill_init(struct device *dev) | |||
1128 | if (result == -EBUSY) | 1102 | if (result == -EBUSY) |
1129 | result = 0; | 1103 | result = 0; |
1130 | 1104 | ||
1105 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5"); | ||
1106 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); | ||
1107 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); | ||
1108 | /* | ||
1109 | * Refresh pci hotplug in case the rfkill state was changed during | ||
1110 | * setup. | ||
1111 | */ | ||
1112 | eeepc_rfkill_hotplug(); | ||
1113 | |||
1131 | exit: | 1114 | exit: |
1132 | if (result && result != -ENODEV) | 1115 | if (result && result != -ENODEV) |
1133 | eeepc_rfkill_exit(); | 1116 | eeepc_rfkill_exit(); |
@@ -1172,21 +1155,61 @@ static int eeepc_hwmon_init(struct device *dev) | |||
1172 | return result; | 1155 | return result; |
1173 | } | 1156 | } |
1174 | 1157 | ||
1175 | static int __init eeepc_laptop_init(void) | 1158 | static int eeepc_input_init(struct device *dev) |
1176 | { | 1159 | { |
1177 | struct device *dev; | 1160 | const struct key_entry *key; |
1178 | int result; | 1161 | int result; |
1179 | 1162 | ||
1180 | if (acpi_disabled) | 1163 | ehotk->inputdev = input_allocate_device(); |
1181 | return -ENODEV; | 1164 | if (!ehotk->inputdev) { |
1182 | result = acpi_bus_register_driver(&eeepc_hotk_driver); | 1165 | pr_info("Unable to allocate input device\n"); |
1183 | if (result < 0) | 1166 | return -ENOMEM; |
1167 | } | ||
1168 | ehotk->inputdev->name = "Asus EeePC extra buttons"; | ||
1169 | ehotk->inputdev->dev.parent = dev; | ||
1170 | ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0"; | ||
1171 | ehotk->inputdev->id.bustype = BUS_HOST; | ||
1172 | ehotk->inputdev->getkeycode = eeepc_getkeycode; | ||
1173 | ehotk->inputdev->setkeycode = eeepc_setkeycode; | ||
1174 | |||
1175 | for (key = eeepc_keymap; key->type != KE_END; key++) { | ||
1176 | switch (key->type) { | ||
1177 | case KE_KEY: | ||
1178 | set_bit(EV_KEY, ehotk->inputdev->evbit); | ||
1179 | set_bit(key->keycode, ehotk->inputdev->keybit); | ||
1180 | break; | ||
1181 | } | ||
1182 | } | ||
1183 | result = input_register_device(ehotk->inputdev); | ||
1184 | if (result) { | ||
1185 | pr_info("Unable to register input device\n"); | ||
1186 | input_free_device(ehotk->inputdev); | ||
1184 | return result; | 1187 | return result; |
1185 | if (!ehotk) { | ||
1186 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | ||
1187 | return -ENODEV; | ||
1188 | } | 1188 | } |
1189 | return 0; | ||
1190 | } | ||
1191 | |||
1192 | static int eeepc_hotk_add(struct acpi_device *device) | ||
1193 | { | ||
1194 | struct device *dev; | ||
1195 | int result; | ||
1189 | 1196 | ||
1197 | if (!device) | ||
1198 | return -EINVAL; | ||
1199 | pr_notice(EEEPC_HOTK_NAME "\n"); | ||
1200 | ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); | ||
1201 | if (!ehotk) | ||
1202 | return -ENOMEM; | ||
1203 | ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; | ||
1204 | ehotk->handle = device->handle; | ||
1205 | strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME); | ||
1206 | strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS); | ||
1207 | device->driver_data = ehotk; | ||
1208 | ehotk->device = device; | ||
1209 | |||
1210 | result = eeepc_hotk_check(); | ||
1211 | if (result) | ||
1212 | goto fail_platform_driver; | ||
1190 | eeepc_enable_camera(); | 1213 | eeepc_enable_camera(); |
1191 | 1214 | ||
1192 | /* Register platform stuff */ | 1215 | /* Register platform stuff */ |
@@ -1216,6 +1239,10 @@ static int __init eeepc_laptop_init(void) | |||
1216 | pr_info("Backlight controlled by ACPI video " | 1239 | pr_info("Backlight controlled by ACPI video " |
1217 | "driver\n"); | 1240 | "driver\n"); |
1218 | 1241 | ||
1242 | result = eeepc_input_init(dev); | ||
1243 | if (result) | ||
1244 | goto fail_input; | ||
1245 | |||
1219 | result = eeepc_hwmon_init(dev); | 1246 | result = eeepc_hwmon_init(dev); |
1220 | if (result) | 1247 | if (result) |
1221 | goto fail_hwmon; | 1248 | goto fail_hwmon; |
@@ -1225,9 +1252,12 @@ static int __init eeepc_laptop_init(void) | |||
1225 | goto fail_rfkill; | 1252 | goto fail_rfkill; |
1226 | 1253 | ||
1227 | return 0; | 1254 | return 0; |
1255 | |||
1228 | fail_rfkill: | 1256 | fail_rfkill: |
1229 | eeepc_hwmon_exit(); | 1257 | eeepc_hwmon_exit(); |
1230 | fail_hwmon: | 1258 | fail_hwmon: |
1259 | eeepc_input_exit(); | ||
1260 | fail_input: | ||
1231 | eeepc_backlight_exit(); | 1261 | eeepc_backlight_exit(); |
1232 | fail_backlight: | 1262 | fail_backlight: |
1233 | sysfs_remove_group(&platform_device->dev.kobj, | 1263 | sysfs_remove_group(&platform_device->dev.kobj, |
@@ -1239,9 +1269,49 @@ fail_platform_device2: | |||
1239 | fail_platform_device1: | 1269 | fail_platform_device1: |
1240 | platform_driver_unregister(&platform_driver); | 1270 | platform_driver_unregister(&platform_driver); |
1241 | fail_platform_driver: | 1271 | fail_platform_driver: |
1242 | eeepc_input_exit(); | 1272 | kfree(ehotk); |
1273 | |||
1243 | return result; | 1274 | return result; |
1244 | } | 1275 | } |
1245 | 1276 | ||
1277 | static int eeepc_hotk_remove(struct acpi_device *device, int type) | ||
1278 | { | ||
1279 | if (!device || !acpi_driver_data(device)) | ||
1280 | return -EINVAL; | ||
1281 | |||
1282 | eeepc_backlight_exit(); | ||
1283 | eeepc_rfkill_exit(); | ||
1284 | eeepc_input_exit(); | ||
1285 | eeepc_hwmon_exit(); | ||
1286 | sysfs_remove_group(&platform_device->dev.kobj, | ||
1287 | &platform_attribute_group); | ||
1288 | platform_device_unregister(platform_device); | ||
1289 | platform_driver_unregister(&platform_driver); | ||
1290 | |||
1291 | kfree(ehotk); | ||
1292 | return 0; | ||
1293 | } | ||
1294 | |||
1295 | static int __init eeepc_laptop_init(void) | ||
1296 | { | ||
1297 | int result; | ||
1298 | |||
1299 | if (acpi_disabled) | ||
1300 | return -ENODEV; | ||
1301 | result = acpi_bus_register_driver(&eeepc_hotk_driver); | ||
1302 | if (result < 0) | ||
1303 | return result; | ||
1304 | if (!ehotk) { | ||
1305 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | ||
1306 | return -ENODEV; | ||
1307 | } | ||
1308 | return 0; | ||
1309 | } | ||
1310 | |||
1311 | static void __exit eeepc_laptop_exit(void) | ||
1312 | { | ||
1313 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | ||
1314 | } | ||
1315 | |||
1246 | module_init(eeepc_laptop_init); | 1316 | module_init(eeepc_laptop_init); |
1247 | module_exit(eeepc_laptop_exit); | 1317 | module_exit(eeepc_laptop_exit); |
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 218b9a16ac3f..f35aee5c2149 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c | |||
@@ -66,11 +66,11 @@ | |||
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 | #ifdef CONFIG_LEDS_CLASS | 69 | #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) |
70 | #include <linux/leds.h> | 70 | #include <linux/leds.h> |
71 | #endif | 71 | #endif |
72 | 72 | ||
73 | #define FUJITSU_DRIVER_VERSION "0.5.0" | 73 | #define FUJITSU_DRIVER_VERSION "0.6.0" |
74 | 74 | ||
75 | #define FUJITSU_LCD_N_LEVELS 8 | 75 | #define FUJITSU_LCD_N_LEVELS 8 |
76 | 76 | ||
@@ -96,7 +96,7 @@ | |||
96 | /* FUNC interface - responses */ | 96 | /* FUNC interface - responses */ |
97 | #define UNSUPPORTED_CMD 0x80000000 | 97 | #define UNSUPPORTED_CMD 0x80000000 |
98 | 98 | ||
99 | #ifdef CONFIG_LEDS_CLASS | 99 | #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) |
100 | /* FUNC interface - LED control */ | 100 | /* FUNC interface - LED control */ |
101 | #define FUNC_LED_OFF 0x1 | 101 | #define FUNC_LED_OFF 0x1 |
102 | #define FUNC_LED_ON 0x30001 | 102 | #define FUNC_LED_ON 0x30001 |
@@ -176,7 +176,7 @@ static struct fujitsu_hotkey_t *fujitsu_hotkey; | |||
176 | 176 | ||
177 | static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event); | 177 | static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event); |
178 | 178 | ||
179 | #ifdef CONFIG_LEDS_CLASS | 179 | #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) |
180 | static enum led_brightness logolamp_get(struct led_classdev *cdev); | 180 | static enum led_brightness logolamp_get(struct led_classdev *cdev); |
181 | static void logolamp_set(struct led_classdev *cdev, | 181 | static void logolamp_set(struct led_classdev *cdev, |
182 | enum led_brightness brightness); | 182 | enum led_brightness brightness); |
@@ -257,7 +257,7 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2) | |||
257 | return out_obj.integer.value; | 257 | return out_obj.integer.value; |
258 | } | 258 | } |
259 | 259 | ||
260 | #ifdef CONFIG_LEDS_CLASS | 260 | #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) |
261 | /* LED class callbacks */ | 261 | /* LED class callbacks */ |
262 | 262 | ||
263 | static void logolamp_set(struct led_classdev *cdev, | 263 | static void logolamp_set(struct led_classdev *cdev, |
@@ -324,9 +324,6 @@ static int set_lcd_level(int level) | |||
324 | if (level < 0 || level >= fujitsu->max_brightness) | 324 | if (level < 0 || level >= fujitsu->max_brightness) |
325 | return -EINVAL; | 325 | return -EINVAL; |
326 | 326 | ||
327 | if (!fujitsu) | ||
328 | return -EINVAL; | ||
329 | |||
330 | status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle); | 327 | status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle); |
331 | if (ACPI_FAILURE(status)) { | 328 | if (ACPI_FAILURE(status)) { |
332 | vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n"); | 329 | vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n"); |
@@ -355,9 +352,6 @@ static int set_lcd_level_alt(int level) | |||
355 | if (level < 0 || level >= fujitsu->max_brightness) | 352 | if (level < 0 || level >= fujitsu->max_brightness) |
356 | return -EINVAL; | 353 | return -EINVAL; |
357 | 354 | ||
358 | if (!fujitsu) | ||
359 | return -EINVAL; | ||
360 | |||
361 | status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle); | 355 | status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle); |
362 | if (ACPI_FAILURE(status)) { | 356 | if (ACPI_FAILURE(status)) { |
363 | vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n"); | 357 | vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n"); |
@@ -697,10 +691,10 @@ static int acpi_fujitsu_add(struct acpi_device *device) | |||
697 | result = acpi_bus_get_power(fujitsu->acpi_handle, &state); | 691 | result = acpi_bus_get_power(fujitsu->acpi_handle, &state); |
698 | if (result) { | 692 | if (result) { |
699 | printk(KERN_ERR "Error reading power state\n"); | 693 | printk(KERN_ERR "Error reading power state\n"); |
700 | goto end; | 694 | goto err_unregister_input_dev; |
701 | } | 695 | } |
702 | 696 | ||
703 | printk(KERN_INFO PREFIX "%s [%s] (%s)\n", | 697 | printk(KERN_INFO "ACPI: %s [%s] (%s)\n", |
704 | acpi_device_name(device), acpi_device_bid(device), | 698 | acpi_device_name(device), acpi_device_bid(device), |
705 | !device->power.state ? "on" : "off"); | 699 | !device->power.state ? "on" : "off"); |
706 | 700 | ||
@@ -728,25 +722,22 @@ static int acpi_fujitsu_add(struct acpi_device *device) | |||
728 | 722 | ||
729 | return result; | 723 | return result; |
730 | 724 | ||
731 | end: | 725 | err_unregister_input_dev: |
726 | input_unregister_device(input); | ||
732 | err_free_input_dev: | 727 | err_free_input_dev: |
733 | input_free_device(input); | 728 | input_free_device(input); |
734 | err_stop: | 729 | err_stop: |
735 | |||
736 | return result; | 730 | return result; |
737 | } | 731 | } |
738 | 732 | ||
739 | static int acpi_fujitsu_remove(struct acpi_device *device, int type) | 733 | static int acpi_fujitsu_remove(struct acpi_device *device, int type) |
740 | { | 734 | { |
741 | struct fujitsu_t *fujitsu = NULL; | 735 | struct fujitsu_t *fujitsu = acpi_driver_data(device); |
736 | struct input_dev *input = fujitsu->input; | ||
742 | 737 | ||
743 | if (!device || !acpi_driver_data(device)) | 738 | input_unregister_device(input); |
744 | return -EINVAL; | ||
745 | 739 | ||
746 | fujitsu = acpi_driver_data(device); | 740 | input_free_device(input); |
747 | |||
748 | if (!device || !acpi_driver_data(device)) | ||
749 | return -EINVAL; | ||
750 | 741 | ||
751 | fujitsu->acpi_handle = NULL; | 742 | fujitsu->acpi_handle = NULL; |
752 | 743 | ||
@@ -871,10 +862,10 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | |||
871 | result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state); | 862 | result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state); |
872 | if (result) { | 863 | if (result) { |
873 | printk(KERN_ERR "Error reading power state\n"); | 864 | printk(KERN_ERR "Error reading power state\n"); |
874 | goto end; | 865 | goto err_unregister_input_dev; |
875 | } | 866 | } |
876 | 867 | ||
877 | printk(KERN_INFO PREFIX "%s [%s] (%s)\n", | 868 | printk(KERN_INFO "ACPI: %s [%s] (%s)\n", |
878 | acpi_device_name(device), acpi_device_bid(device), | 869 | acpi_device_name(device), acpi_device_bid(device), |
879 | !device->power.state ? "on" : "off"); | 870 | !device->power.state ? "on" : "off"); |
880 | 871 | ||
@@ -911,7 +902,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | |||
911 | printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n", | 902 | printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n", |
912 | call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); | 903 | call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); |
913 | 904 | ||
914 | #ifdef CONFIG_LEDS_CLASS | 905 | #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) |
915 | if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { | 906 | if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { |
916 | result = led_classdev_register(&fujitsu->pf_device->dev, | 907 | result = led_classdev_register(&fujitsu->pf_device->dev, |
917 | &logolamp_led); | 908 | &logolamp_led); |
@@ -934,33 +925,41 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | |||
934 | "LED handler for keyboard lamps, error %i\n", result); | 925 | "LED handler for keyboard lamps, error %i\n", result); |
935 | } | 926 | } |
936 | } | 927 | } |
937 | #endif | 928 | #endif |
938 | 929 | ||
939 | return result; | 930 | return result; |
940 | 931 | ||
941 | end: | 932 | err_unregister_input_dev: |
933 | input_unregister_device(input); | ||
942 | err_free_input_dev: | 934 | err_free_input_dev: |
943 | input_free_device(input); | 935 | input_free_device(input); |
944 | err_free_fifo: | 936 | err_free_fifo: |
945 | kfifo_free(fujitsu_hotkey->fifo); | 937 | kfifo_free(fujitsu_hotkey->fifo); |
946 | err_stop: | 938 | err_stop: |
947 | |||
948 | return result; | 939 | return result; |
949 | } | 940 | } |
950 | 941 | ||
951 | static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type) | 942 | static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type) |
952 | { | 943 | { |
953 | struct fujitsu_hotkey_t *fujitsu_hotkey = NULL; | 944 | struct fujitsu_hotkey_t *fujitsu_hotkey = acpi_driver_data(device); |
945 | struct input_dev *input = fujitsu_hotkey->input; | ||
954 | 946 | ||
955 | if (!device || !acpi_driver_data(device)) | 947 | #ifdef CONFIG_LEDS_CLASS |
956 | return -EINVAL; | 948 | if (fujitsu_hotkey->logolamp_registered) |
949 | led_classdev_unregister(&logolamp_led); | ||
957 | 950 | ||
958 | fujitsu_hotkey = acpi_driver_data(device); | 951 | if (fujitsu_hotkey->kblamps_registered) |
952 | led_classdev_unregister(&kblamps_led); | ||
953 | #endif | ||
959 | 954 | ||
960 | fujitsu_hotkey->acpi_handle = NULL; | 955 | input_unregister_device(input); |
956 | |||
957 | input_free_device(input); | ||
961 | 958 | ||
962 | kfifo_free(fujitsu_hotkey->fifo); | 959 | kfifo_free(fujitsu_hotkey->fifo); |
963 | 960 | ||
961 | fujitsu_hotkey->acpi_handle = NULL; | ||
962 | |||
964 | return 0; | 963 | return 0; |
965 | } | 964 | } |
966 | 965 | ||
@@ -1130,8 +1129,11 @@ static int __init fujitsu_init(void) | |||
1130 | fujitsu->bl_device = | 1129 | fujitsu->bl_device = |
1131 | backlight_device_register("fujitsu-laptop", NULL, NULL, | 1130 | backlight_device_register("fujitsu-laptop", NULL, NULL, |
1132 | &fujitsubl_ops); | 1131 | &fujitsubl_ops); |
1133 | if (IS_ERR(fujitsu->bl_device)) | 1132 | if (IS_ERR(fujitsu->bl_device)) { |
1134 | return PTR_ERR(fujitsu->bl_device); | 1133 | ret = PTR_ERR(fujitsu->bl_device); |
1134 | fujitsu->bl_device = NULL; | ||
1135 | goto fail_sysfs_group; | ||
1136 | } | ||
1135 | max_brightness = fujitsu->max_brightness; | 1137 | max_brightness = fujitsu->max_brightness; |
1136 | fujitsu->bl_device->props.max_brightness = max_brightness - 1; | 1138 | fujitsu->bl_device->props.max_brightness = max_brightness - 1; |
1137 | fujitsu->bl_device->props.brightness = fujitsu->brightness_level; | 1139 | fujitsu->bl_device->props.brightness = fujitsu->brightness_level; |
@@ -1171,32 +1173,22 @@ static int __init fujitsu_init(void) | |||
1171 | return 0; | 1173 | return 0; |
1172 | 1174 | ||
1173 | fail_hotkey1: | 1175 | fail_hotkey1: |
1174 | |||
1175 | kfree(fujitsu_hotkey); | 1176 | kfree(fujitsu_hotkey); |
1176 | |||
1177 | fail_hotkey: | 1177 | fail_hotkey: |
1178 | |||
1179 | platform_driver_unregister(&fujitsupf_driver); | 1178 | platform_driver_unregister(&fujitsupf_driver); |
1180 | |||
1181 | fail_backlight: | 1179 | fail_backlight: |
1182 | |||
1183 | if (fujitsu->bl_device) | 1180 | if (fujitsu->bl_device) |
1184 | backlight_device_unregister(fujitsu->bl_device); | 1181 | backlight_device_unregister(fujitsu->bl_device); |
1185 | 1182 | fail_sysfs_group: | |
1183 | sysfs_remove_group(&fujitsu->pf_device->dev.kobj, | ||
1184 | &fujitsupf_attribute_group); | ||
1186 | fail_platform_device2: | 1185 | fail_platform_device2: |
1187 | |||
1188 | platform_device_del(fujitsu->pf_device); | 1186 | platform_device_del(fujitsu->pf_device); |
1189 | |||
1190 | fail_platform_device1: | 1187 | fail_platform_device1: |
1191 | |||
1192 | platform_device_put(fujitsu->pf_device); | 1188 | platform_device_put(fujitsu->pf_device); |
1193 | |||
1194 | fail_platform_driver: | 1189 | fail_platform_driver: |
1195 | |||
1196 | acpi_bus_unregister_driver(&acpi_fujitsu_driver); | 1190 | acpi_bus_unregister_driver(&acpi_fujitsu_driver); |
1197 | |||
1198 | fail_acpi: | 1191 | fail_acpi: |
1199 | |||
1200 | kfree(fujitsu); | 1192 | kfree(fujitsu); |
1201 | 1193 | ||
1202 | return ret; | 1194 | return ret; |
@@ -1204,28 +1196,23 @@ fail_acpi: | |||
1204 | 1196 | ||
1205 | static void __exit fujitsu_cleanup(void) | 1197 | static void __exit fujitsu_cleanup(void) |
1206 | { | 1198 | { |
1207 | #ifdef CONFIG_LEDS_CLASS | 1199 | acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver); |
1208 | if (fujitsu_hotkey->logolamp_registered != 0) | ||
1209 | led_classdev_unregister(&logolamp_led); | ||
1210 | 1200 | ||
1211 | if (fujitsu_hotkey->kblamps_registered != 0) | 1201 | kfree(fujitsu_hotkey); |
1212 | led_classdev_unregister(&kblamps_led); | ||
1213 | #endif | ||
1214 | 1202 | ||
1215 | sysfs_remove_group(&fujitsu->pf_device->dev.kobj, | ||
1216 | &fujitsupf_attribute_group); | ||
1217 | platform_device_unregister(fujitsu->pf_device); | ||
1218 | platform_driver_unregister(&fujitsupf_driver); | 1203 | platform_driver_unregister(&fujitsupf_driver); |
1204 | |||
1219 | if (fujitsu->bl_device) | 1205 | if (fujitsu->bl_device) |
1220 | backlight_device_unregister(fujitsu->bl_device); | 1206 | backlight_device_unregister(fujitsu->bl_device); |
1221 | 1207 | ||
1222 | acpi_bus_unregister_driver(&acpi_fujitsu_driver); | 1208 | sysfs_remove_group(&fujitsu->pf_device->dev.kobj, |
1209 | &fujitsupf_attribute_group); | ||
1223 | 1210 | ||
1224 | kfree(fujitsu); | 1211 | platform_device_unregister(fujitsu->pf_device); |
1225 | 1212 | ||
1226 | acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver); | 1213 | acpi_bus_unregister_driver(&acpi_fujitsu_driver); |
1227 | 1214 | ||
1228 | kfree(fujitsu_hotkey); | 1215 | kfree(fujitsu); |
1229 | 1216 | ||
1230 | printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); | 1217 | printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); |
1231 | } | 1218 | } |
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index af04f5b049db..c2842171cec6 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c | |||
@@ -507,7 +507,7 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device) | |||
507 | } | 507 | } |
508 | if (bluetooth_rfkill) { | 508 | if (bluetooth_rfkill) { |
509 | rfkill_unregister(bluetooth_rfkill); | 509 | rfkill_unregister(bluetooth_rfkill); |
510 | rfkill_destroy(wifi_rfkill); | 510 | rfkill_destroy(bluetooth_rfkill); |
511 | } | 511 | } |
512 | if (wwan_rfkill) { | 512 | if (wwan_rfkill) { |
513 | rfkill_unregister(wwan_rfkill); | 513 | rfkill_unregister(wwan_rfkill); |
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index dafaa4a92df5..f9f68e0e7344 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -976,15 +976,12 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, | |||
976 | void *context, void **return_value) | 976 | void *context, void **return_value) |
977 | { | 977 | { |
978 | struct acpi_device_info *info; | 978 | struct acpi_device_info *info; |
979 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
980 | |||
981 | if (ACPI_SUCCESS(acpi_get_object_info(handle, &buffer))) { | ||
982 | info = buffer.pointer; | ||
983 | 979 | ||
980 | if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) { | ||
984 | printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", | 981 | printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", |
985 | (char *)&info->name, info->param_count); | 982 | (char *)&info->name, info->param_count); |
986 | 983 | ||
987 | kfree(buffer.pointer); | 984 | kfree(info); |
988 | } | 985 | } |
989 | 986 | ||
990 | return AE_OK; | 987 | return AE_OK; |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index e85600852502..f78d27503925 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -1278,6 +1278,7 @@ static void tpacpi_destroy_rfkill(const enum tpacpi_rfk_id id) | |||
1278 | tp_rfk = tpacpi_rfkill_switches[id]; | 1278 | tp_rfk = tpacpi_rfkill_switches[id]; |
1279 | if (tp_rfk) { | 1279 | if (tp_rfk) { |
1280 | rfkill_unregister(tp_rfk->rfkill); | 1280 | rfkill_unregister(tp_rfk->rfkill); |
1281 | rfkill_destroy(tp_rfk->rfkill); | ||
1281 | tpacpi_rfkill_switches[id] = NULL; | 1282 | tpacpi_rfkill_switches[id] = NULL; |
1282 | kfree(tp_rfk); | 1283 | kfree(tp_rfk); |
1283 | } | 1284 | } |
@@ -1601,6 +1602,196 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv) | |||
1601 | #endif | 1602 | #endif |
1602 | } | 1603 | } |
1603 | 1604 | ||
1605 | /************************************************************************* | ||
1606 | * Firmware Data | ||
1607 | */ | ||
1608 | |||
1609 | /* | ||
1610 | * Table of recommended minimum BIOS versions | ||
1611 | * | ||
1612 | * Reasons for listing: | ||
1613 | * 1. Stable BIOS, listed because the unknown ammount of | ||
1614 | * bugs and bad ACPI behaviour on older versions | ||
1615 | * | ||
1616 | * 2. BIOS or EC fw with known bugs that trigger on Linux | ||
1617 | * | ||
1618 | * 3. BIOS with known reduced functionality in older versions | ||
1619 | * | ||
1620 | * We recommend the latest BIOS and EC version. | ||
1621 | * We only support the latest BIOS and EC fw version as a rule. | ||
1622 | * | ||
1623 | * Sources: IBM ThinkPad Public Web Documents (update changelogs), | ||
1624 | * Information from users in ThinkWiki | ||
1625 | * | ||
1626 | * WARNING: we use this table also to detect that the machine is | ||
1627 | * a ThinkPad in some cases, so don't remove entries lightly. | ||
1628 | */ | ||
1629 | |||
1630 | #define TPV_Q(__v, __id1, __id2, __bv1, __bv2) \ | ||
1631 | { .vendor = (__v), \ | ||
1632 | .bios = TPID(__id1, __id2), \ | ||
1633 | .ec = TPACPI_MATCH_ANY, \ | ||
1634 | .quirks = TPACPI_MATCH_ANY << 16 \ | ||
1635 | | (__bv1) << 8 | (__bv2) } | ||
1636 | |||
1637 | #define TPV_Q_X(__v, __bid1, __bid2, __bv1, __bv2, \ | ||
1638 | __eid1, __eid2, __ev1, __ev2) \ | ||
1639 | { .vendor = (__v), \ | ||
1640 | .bios = TPID(__bid1, __bid2), \ | ||
1641 | .ec = TPID(__eid1, __eid2), \ | ||
1642 | .quirks = (__ev1) << 24 | (__ev2) << 16 \ | ||
1643 | | (__bv1) << 8 | (__bv2) } | ||
1644 | |||
1645 | #define TPV_QI0(__id1, __id2, __bv1, __bv2) \ | ||
1646 | TPV_Q(PCI_VENDOR_ID_IBM, __id1, __id2, __bv1, __bv2) | ||
1647 | |||
1648 | #define TPV_QI1(__id1, __id2, __bv1, __bv2, __ev1, __ev2) \ | ||
1649 | TPV_Q_X(PCI_VENDOR_ID_IBM, __id1, __id2, \ | ||
1650 | __bv1, __bv2, __id1, __id2, __ev1, __ev2) | ||
1651 | |||
1652 | #define TPV_QI2(__bid1, __bid2, __bv1, __bv2, \ | ||
1653 | __eid1, __eid2, __ev1, __ev2) \ | ||
1654 | TPV_Q_X(PCI_VENDOR_ID_IBM, __bid1, __bid2, \ | ||
1655 | __bv1, __bv2, __eid1, __eid2, __ev1, __ev2) | ||
1656 | |||
1657 | #define TPV_QL0(__id1, __id2, __bv1, __bv2) \ | ||
1658 | TPV_Q(PCI_VENDOR_ID_LENOVO, __id1, __id2, __bv1, __bv2) | ||
1659 | |||
1660 | #define TPV_QL1(__id1, __id2, __bv1, __bv2, __ev1, __ev2) \ | ||
1661 | TPV_Q_X(PCI_VENDOR_ID_LENOVO, __id1, __id2, \ | ||
1662 | __bv1, __bv2, __id1, __id2, __ev1, __ev2) | ||
1663 | |||
1664 | #define TPV_QL2(__bid1, __bid2, __bv1, __bv2, \ | ||
1665 | __eid1, __eid2, __ev1, __ev2) \ | ||
1666 | TPV_Q_X(PCI_VENDOR_ID_LENOVO, __bid1, __bid2, \ | ||
1667 | __bv1, __bv2, __eid1, __eid2, __ev1, __ev2) | ||
1668 | |||
1669 | static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = { | ||
1670 | /* Numeric models ------------------ */ | ||
1671 | /* FW MODEL BIOS VERS */ | ||
1672 | TPV_QI0('I', 'M', '6', '5'), /* 570 */ | ||
1673 | TPV_QI0('I', 'U', '2', '6'), /* 570E */ | ||
1674 | TPV_QI0('I', 'B', '5', '4'), /* 600 */ | ||
1675 | TPV_QI0('I', 'H', '4', '7'), /* 600E */ | ||
1676 | TPV_QI0('I', 'N', '3', '6'), /* 600E */ | ||
1677 | TPV_QI0('I', 'T', '5', '5'), /* 600X */ | ||
1678 | TPV_QI0('I', 'D', '4', '8'), /* 770, 770E, 770ED */ | ||
1679 | TPV_QI0('I', 'I', '4', '2'), /* 770X */ | ||
1680 | TPV_QI0('I', 'O', '2', '3'), /* 770Z */ | ||
1681 | |||
1682 | /* A-series ------------------------- */ | ||
1683 | /* FW MODEL BIOS VERS EC VERS */ | ||
1684 | TPV_QI0('I', 'W', '5', '9'), /* A20m */ | ||
1685 | TPV_QI0('I', 'V', '6', '9'), /* A20p */ | ||
1686 | TPV_QI0('1', '0', '2', '6'), /* A21e, A22e */ | ||
1687 | TPV_QI0('K', 'U', '3', '6'), /* A21e */ | ||
1688 | TPV_QI0('K', 'X', '3', '6'), /* A21m, A22m */ | ||
1689 | TPV_QI0('K', 'Y', '3', '8'), /* A21p, A22p */ | ||
1690 | TPV_QI0('1', 'B', '1', '7'), /* A22e */ | ||
1691 | TPV_QI0('1', '3', '2', '0'), /* A22m */ | ||
1692 | TPV_QI0('1', 'E', '7', '3'), /* A30/p (0) */ | ||
1693 | TPV_QI1('1', 'G', '4', '1', '1', '7'), /* A31/p (0) */ | ||
1694 | TPV_QI1('1', 'N', '1', '6', '0', '7'), /* A31/p (0) */ | ||
1695 | |||
1696 | /* G-series ------------------------- */ | ||
1697 | /* FW MODEL BIOS VERS */ | ||
1698 | TPV_QI0('1', 'T', 'A', '6'), /* G40 */ | ||
1699 | TPV_QI0('1', 'X', '5', '7'), /* G41 */ | ||
1700 | |||
1701 | /* R-series, T-series --------------- */ | ||
1702 | /* FW MODEL BIOS VERS EC VERS */ | ||
1703 | TPV_QI0('1', 'C', 'F', '0'), /* R30 */ | ||
1704 | TPV_QI0('1', 'F', 'F', '1'), /* R31 */ | ||
1705 | TPV_QI0('1', 'M', '9', '7'), /* R32 */ | ||
1706 | TPV_QI0('1', 'O', '6', '1'), /* R40 */ | ||
1707 | TPV_QI0('1', 'P', '6', '5'), /* R40 */ | ||
1708 | TPV_QI0('1', 'S', '7', '0'), /* R40e */ | ||
1709 | TPV_QI1('1', 'R', 'D', 'R', '7', '1'), /* R50/p, R51, | ||
1710 | T40/p, T41/p, T42/p (1) */ | ||
1711 | TPV_QI1('1', 'V', '7', '1', '2', '8'), /* R50e, R51 (1) */ | ||
1712 | TPV_QI1('7', '8', '7', '1', '0', '6'), /* R51e (1) */ | ||
1713 | TPV_QI1('7', '6', '6', '9', '1', '6'), /* R52 (1) */ | ||
1714 | TPV_QI1('7', '0', '6', '9', '2', '8'), /* R52, T43 (1) */ | ||
1715 | |||
1716 | TPV_QI0('I', 'Y', '6', '1'), /* T20 */ | ||
1717 | TPV_QI0('K', 'Z', '3', '4'), /* T21 */ | ||
1718 | TPV_QI0('1', '6', '3', '2'), /* T22 */ | ||
1719 | TPV_QI1('1', 'A', '6', '4', '2', '3'), /* T23 (0) */ | ||
1720 | TPV_QI1('1', 'I', '7', '1', '2', '0'), /* T30 (0) */ | ||
1721 | TPV_QI1('1', 'Y', '6', '5', '2', '9'), /* T43/p (1) */ | ||
1722 | |||
1723 | TPV_QL1('7', '9', 'E', '3', '5', '0'), /* T60/p */ | ||
1724 | TPV_QL1('7', 'C', 'D', '2', '2', '2'), /* R60, R60i */ | ||
1725 | TPV_QL0('7', 'E', 'D', '0'), /* R60e, R60i */ | ||
1726 | |||
1727 | /* BIOS FW BIOS VERS EC FW EC VERS */ | ||
1728 | TPV_QI2('1', 'W', '9', '0', '1', 'V', '2', '8'), /* R50e (1) */ | ||
1729 | TPV_QL2('7', 'I', '3', '4', '7', '9', '5', '0'), /* T60/p wide */ | ||
1730 | |||
1731 | /* X-series ------------------------- */ | ||
1732 | /* FW MODEL BIOS VERS EC VERS */ | ||
1733 | TPV_QI0('I', 'Z', '9', 'D'), /* X20, X21 */ | ||
1734 | TPV_QI0('1', 'D', '7', '0'), /* X22, X23, X24 */ | ||
1735 | TPV_QI1('1', 'K', '4', '8', '1', '8'), /* X30 (0) */ | ||
1736 | TPV_QI1('1', 'Q', '9', '7', '2', '3'), /* X31, X32 (0) */ | ||
1737 | TPV_QI1('1', 'U', 'D', '3', 'B', '2'), /* X40 (0) */ | ||
1738 | TPV_QI1('7', '4', '6', '4', '2', '7'), /* X41 (0) */ | ||
1739 | TPV_QI1('7', '5', '6', '0', '2', '0'), /* X41t (0) */ | ||
1740 | |||
1741 | TPV_QL0('7', 'B', 'D', '7'), /* X60/s */ | ||
1742 | TPV_QL0('7', 'J', '3', '0'), /* X60t */ | ||
1743 | |||
1744 | /* (0) - older versions lack DMI EC fw string and functionality */ | ||
1745 | /* (1) - older versions known to lack functionality */ | ||
1746 | }; | ||
1747 | |||
1748 | #undef TPV_QL1 | ||
1749 | #undef TPV_QL0 | ||
1750 | #undef TPV_QI2 | ||
1751 | #undef TPV_QI1 | ||
1752 | #undef TPV_QI0 | ||
1753 | #undef TPV_Q_X | ||
1754 | #undef TPV_Q | ||
1755 | |||
1756 | static void __init tpacpi_check_outdated_fw(void) | ||
1757 | { | ||
1758 | unsigned long fwvers; | ||
1759 | u16 ec_version, bios_version; | ||
1760 | |||
1761 | fwvers = tpacpi_check_quirks(tpacpi_bios_version_qtable, | ||
1762 | ARRAY_SIZE(tpacpi_bios_version_qtable)); | ||
1763 | |||
1764 | if (!fwvers) | ||
1765 | return; | ||
1766 | |||
1767 | bios_version = fwvers & 0xffffU; | ||
1768 | ec_version = (fwvers >> 16) & 0xffffU; | ||
1769 | |||
1770 | /* note that unknown versions are set to 0x0000 and we use that */ | ||
1771 | if ((bios_version > thinkpad_id.bios_release) || | ||
1772 | (ec_version > thinkpad_id.ec_release && | ||
1773 | ec_version != TPACPI_MATCH_ANY)) { | ||
1774 | /* | ||
1775 | * The changelogs would let us track down the exact | ||
1776 | * reason, but it is just too much of a pain to track | ||
1777 | * it. We only list BIOSes that are either really | ||
1778 | * broken, or really stable to begin with, so it is | ||
1779 | * best if the user upgrades the firmware anyway. | ||
1780 | */ | ||
1781 | printk(TPACPI_WARN | ||
1782 | "WARNING: Outdated ThinkPad BIOS/EC firmware\n"); | ||
1783 | printk(TPACPI_WARN | ||
1784 | "WARNING: This firmware may be missing critical bug " | ||
1785 | "fixes and/or important features\n"); | ||
1786 | } | ||
1787 | } | ||
1788 | |||
1789 | static bool __init tpacpi_is_fw_known(void) | ||
1790 | { | ||
1791 | return tpacpi_check_quirks(tpacpi_bios_version_qtable, | ||
1792 | ARRAY_SIZE(tpacpi_bios_version_qtable)) != 0; | ||
1793 | } | ||
1794 | |||
1604 | /**************************************************************************** | 1795 | /**************************************************************************** |
1605 | **************************************************************************** | 1796 | **************************************************************************** |
1606 | * | 1797 | * |
@@ -1634,6 +1825,7 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) | |||
1634 | (thinkpad_id.nummodel_str) ? | 1825 | (thinkpad_id.nummodel_str) ? |
1635 | thinkpad_id.nummodel_str : "unknown"); | 1826 | thinkpad_id.nummodel_str : "unknown"); |
1636 | 1827 | ||
1828 | tpacpi_check_outdated_fw(); | ||
1637 | return 0; | 1829 | return 0; |
1638 | } | 1830 | } |
1639 | 1831 | ||
@@ -1731,16 +1923,42 @@ struct tp_nvram_state { | |||
1731 | u8 volume_level; | 1923 | u8 volume_level; |
1732 | }; | 1924 | }; |
1733 | 1925 | ||
1926 | /* kthread for the hotkey poller */ | ||
1734 | static struct task_struct *tpacpi_hotkey_task; | 1927 | static struct task_struct *tpacpi_hotkey_task; |
1735 | static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */ | 1928 | |
1736 | static int hotkey_poll_freq = 10; /* Hz */ | 1929 | /* Acquired while the poller kthread is running, use to sync start/stop */ |
1737 | static struct mutex hotkey_thread_mutex; | 1930 | static struct mutex hotkey_thread_mutex; |
1931 | |||
1932 | /* | ||
1933 | * Acquire mutex to write poller control variables. | ||
1934 | * Increment hotkey_config_change when changing them. | ||
1935 | * | ||
1936 | * See HOTKEY_CONFIG_CRITICAL_START/HOTKEY_CONFIG_CRITICAL_END | ||
1937 | */ | ||
1738 | static struct mutex hotkey_thread_data_mutex; | 1938 | static struct mutex hotkey_thread_data_mutex; |
1739 | static unsigned int hotkey_config_change; | 1939 | static unsigned int hotkey_config_change; |
1740 | 1940 | ||
1941 | /* | ||
1942 | * hotkey poller control variables | ||
1943 | * | ||
1944 | * Must be atomic or readers will also need to acquire mutex | ||
1945 | */ | ||
1946 | static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */ | ||
1947 | static unsigned int hotkey_poll_freq = 10; /* Hz */ | ||
1948 | |||
1949 | #define HOTKEY_CONFIG_CRITICAL_START \ | ||
1950 | do { \ | ||
1951 | mutex_lock(&hotkey_thread_data_mutex); \ | ||
1952 | hotkey_config_change++; \ | ||
1953 | } while (0); | ||
1954 | #define HOTKEY_CONFIG_CRITICAL_END \ | ||
1955 | mutex_unlock(&hotkey_thread_data_mutex); | ||
1956 | |||
1741 | #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | 1957 | #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ |
1742 | 1958 | ||
1743 | #define hotkey_source_mask 0U | 1959 | #define hotkey_source_mask 0U |
1960 | #define HOTKEY_CONFIG_CRITICAL_START | ||
1961 | #define HOTKEY_CONFIG_CRITICAL_END | ||
1744 | 1962 | ||
1745 | #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | 1963 | #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ |
1746 | 1964 | ||
@@ -1765,19 +1983,6 @@ static u16 *hotkey_keycode_map; | |||
1765 | 1983 | ||
1766 | static struct attribute_set *hotkey_dev_attributes; | 1984 | static struct attribute_set *hotkey_dev_attributes; |
1767 | 1985 | ||
1768 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
1769 | #define HOTKEY_CONFIG_CRITICAL_START \ | ||
1770 | do { \ | ||
1771 | mutex_lock(&hotkey_thread_data_mutex); \ | ||
1772 | hotkey_config_change++; \ | ||
1773 | } while (0); | ||
1774 | #define HOTKEY_CONFIG_CRITICAL_END \ | ||
1775 | mutex_unlock(&hotkey_thread_data_mutex); | ||
1776 | #else | ||
1777 | #define HOTKEY_CONFIG_CRITICAL_START | ||
1778 | #define HOTKEY_CONFIG_CRITICAL_END | ||
1779 | #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | ||
1780 | |||
1781 | /* HKEY.MHKG() return bits */ | 1986 | /* HKEY.MHKG() return bits */ |
1782 | #define TP_HOTKEY_TABLET_MASK (1 << 3) | 1987 | #define TP_HOTKEY_TABLET_MASK (1 << 3) |
1783 | 1988 | ||
@@ -1822,7 +2027,9 @@ static int hotkey_mask_get(void) | |||
1822 | if (!acpi_evalf(hkey_handle, &m, "DHKN", "d")) | 2027 | if (!acpi_evalf(hkey_handle, &m, "DHKN", "d")) |
1823 | return -EIO; | 2028 | return -EIO; |
1824 | } | 2029 | } |
2030 | HOTKEY_CONFIG_CRITICAL_START | ||
1825 | hotkey_mask = m | (hotkey_source_mask & hotkey_mask); | 2031 | hotkey_mask = m | (hotkey_source_mask & hotkey_mask); |
2032 | HOTKEY_CONFIG_CRITICAL_END | ||
1826 | 2033 | ||
1827 | return 0; | 2034 | return 0; |
1828 | } | 2035 | } |
@@ -2075,6 +2282,7 @@ static int hotkey_kthread(void *data) | |||
2075 | unsigned int si, so; | 2282 | unsigned int si, so; |
2076 | unsigned long t; | 2283 | unsigned long t; |
2077 | unsigned int change_detector, must_reset; | 2284 | unsigned int change_detector, must_reset; |
2285 | unsigned int poll_freq; | ||
2078 | 2286 | ||
2079 | mutex_lock(&hotkey_thread_mutex); | 2287 | mutex_lock(&hotkey_thread_mutex); |
2080 | 2288 | ||
@@ -2091,12 +2299,17 @@ static int hotkey_kthread(void *data) | |||
2091 | mutex_lock(&hotkey_thread_data_mutex); | 2299 | mutex_lock(&hotkey_thread_data_mutex); |
2092 | change_detector = hotkey_config_change; | 2300 | change_detector = hotkey_config_change; |
2093 | mask = hotkey_source_mask & hotkey_mask; | 2301 | mask = hotkey_source_mask & hotkey_mask; |
2302 | poll_freq = hotkey_poll_freq; | ||
2094 | mutex_unlock(&hotkey_thread_data_mutex); | 2303 | mutex_unlock(&hotkey_thread_data_mutex); |
2095 | hotkey_read_nvram(&s[so], mask); | 2304 | hotkey_read_nvram(&s[so], mask); |
2096 | 2305 | ||
2097 | while (!kthread_should_stop() && hotkey_poll_freq) { | 2306 | while (!kthread_should_stop()) { |
2098 | if (t == 0) | 2307 | if (t == 0) { |
2099 | t = 1000/hotkey_poll_freq; | 2308 | if (likely(poll_freq)) |
2309 | t = 1000/poll_freq; | ||
2310 | else | ||
2311 | t = 100; /* should never happen... */ | ||
2312 | } | ||
2100 | t = msleep_interruptible(t); | 2313 | t = msleep_interruptible(t); |
2101 | if (unlikely(kthread_should_stop())) | 2314 | if (unlikely(kthread_should_stop())) |
2102 | break; | 2315 | break; |
@@ -2112,6 +2325,7 @@ static int hotkey_kthread(void *data) | |||
2112 | change_detector = hotkey_config_change; | 2325 | change_detector = hotkey_config_change; |
2113 | } | 2326 | } |
2114 | mask = hotkey_source_mask & hotkey_mask; | 2327 | mask = hotkey_source_mask & hotkey_mask; |
2328 | poll_freq = hotkey_poll_freq; | ||
2115 | mutex_unlock(&hotkey_thread_data_mutex); | 2329 | mutex_unlock(&hotkey_thread_data_mutex); |
2116 | 2330 | ||
2117 | if (likely(mask)) { | 2331 | if (likely(mask)) { |
@@ -2131,6 +2345,7 @@ exit: | |||
2131 | return 0; | 2345 | return 0; |
2132 | } | 2346 | } |
2133 | 2347 | ||
2348 | /* call with hotkey_mutex held */ | ||
2134 | static void hotkey_poll_stop_sync(void) | 2349 | static void hotkey_poll_stop_sync(void) |
2135 | { | 2350 | { |
2136 | if (tpacpi_hotkey_task) { | 2351 | if (tpacpi_hotkey_task) { |
@@ -2147,10 +2362,11 @@ static void hotkey_poll_stop_sync(void) | |||
2147 | } | 2362 | } |
2148 | 2363 | ||
2149 | /* call with hotkey_mutex held */ | 2364 | /* call with hotkey_mutex held */ |
2150 | static void hotkey_poll_setup(int may_warn) | 2365 | static void hotkey_poll_setup(bool may_warn) |
2151 | { | 2366 | { |
2152 | if ((hotkey_source_mask & hotkey_mask) != 0 && | 2367 | u32 hotkeys_to_poll = hotkey_source_mask & hotkey_mask; |
2153 | hotkey_poll_freq > 0 && | 2368 | |
2369 | if (hotkeys_to_poll != 0 && hotkey_poll_freq > 0 && | ||
2154 | (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) { | 2370 | (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) { |
2155 | if (!tpacpi_hotkey_task) { | 2371 | if (!tpacpi_hotkey_task) { |
2156 | tpacpi_hotkey_task = kthread_run(hotkey_kthread, | 2372 | tpacpi_hotkey_task = kthread_run(hotkey_kthread, |
@@ -2164,26 +2380,37 @@ static void hotkey_poll_setup(int may_warn) | |||
2164 | } | 2380 | } |
2165 | } else { | 2381 | } else { |
2166 | hotkey_poll_stop_sync(); | 2382 | hotkey_poll_stop_sync(); |
2167 | if (may_warn && | 2383 | if (may_warn && hotkeys_to_poll != 0 && |
2168 | hotkey_source_mask != 0 && hotkey_poll_freq == 0) { | 2384 | hotkey_poll_freq == 0) { |
2169 | printk(TPACPI_NOTICE | 2385 | printk(TPACPI_NOTICE |
2170 | "hot keys 0x%08x require polling, " | 2386 | "hot keys 0x%08x require polling, " |
2171 | "which is currently disabled\n", | 2387 | "which is currently disabled\n", |
2172 | hotkey_source_mask); | 2388 | hotkeys_to_poll); |
2173 | } | 2389 | } |
2174 | } | 2390 | } |
2175 | } | 2391 | } |
2176 | 2392 | ||
2177 | static void hotkey_poll_setup_safe(int may_warn) | 2393 | static void hotkey_poll_setup_safe(bool may_warn) |
2178 | { | 2394 | { |
2179 | mutex_lock(&hotkey_mutex); | 2395 | mutex_lock(&hotkey_mutex); |
2180 | hotkey_poll_setup(may_warn); | 2396 | hotkey_poll_setup(may_warn); |
2181 | mutex_unlock(&hotkey_mutex); | 2397 | mutex_unlock(&hotkey_mutex); |
2182 | } | 2398 | } |
2183 | 2399 | ||
2400 | /* call with hotkey_mutex held */ | ||
2401 | static void hotkey_poll_set_freq(unsigned int freq) | ||
2402 | { | ||
2403 | if (!freq) | ||
2404 | hotkey_poll_stop_sync(); | ||
2405 | |||
2406 | HOTKEY_CONFIG_CRITICAL_START | ||
2407 | hotkey_poll_freq = freq; | ||
2408 | HOTKEY_CONFIG_CRITICAL_END | ||
2409 | } | ||
2410 | |||
2184 | #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | 2411 | #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ |
2185 | 2412 | ||
2186 | static void hotkey_poll_setup_safe(int __unused) | 2413 | static void hotkey_poll_setup_safe(bool __unused) |
2187 | { | 2414 | { |
2188 | } | 2415 | } |
2189 | 2416 | ||
@@ -2201,7 +2428,7 @@ static int hotkey_inputdev_open(struct input_dev *dev) | |||
2201 | case TPACPI_LIFE_EXITING: | 2428 | case TPACPI_LIFE_EXITING: |
2202 | return -EBUSY; | 2429 | return -EBUSY; |
2203 | case TPACPI_LIFE_RUNNING: | 2430 | case TPACPI_LIFE_RUNNING: |
2204 | hotkey_poll_setup_safe(0); | 2431 | hotkey_poll_setup_safe(false); |
2205 | return 0; | 2432 | return 0; |
2206 | } | 2433 | } |
2207 | 2434 | ||
@@ -2214,7 +2441,7 @@ static void hotkey_inputdev_close(struct input_dev *dev) | |||
2214 | { | 2441 | { |
2215 | /* disable hotkey polling when possible */ | 2442 | /* disable hotkey polling when possible */ |
2216 | if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING) | 2443 | if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING) |
2217 | hotkey_poll_setup_safe(0); | 2444 | hotkey_poll_setup_safe(false); |
2218 | } | 2445 | } |
2219 | 2446 | ||
2220 | /* sysfs hotkey enable ------------------------------------------------- */ | 2447 | /* sysfs hotkey enable ------------------------------------------------- */ |
@@ -2288,7 +2515,7 @@ static ssize_t hotkey_mask_store(struct device *dev, | |||
2288 | res = hotkey_mask_set(t); | 2515 | res = hotkey_mask_set(t); |
2289 | 2516 | ||
2290 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | 2517 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL |
2291 | hotkey_poll_setup(1); | 2518 | hotkey_poll_setup(true); |
2292 | #endif | 2519 | #endif |
2293 | 2520 | ||
2294 | mutex_unlock(&hotkey_mutex); | 2521 | mutex_unlock(&hotkey_mutex); |
@@ -2318,6 +2545,8 @@ static ssize_t hotkey_bios_mask_show(struct device *dev, | |||
2318 | struct device_attribute *attr, | 2545 | struct device_attribute *attr, |
2319 | char *buf) | 2546 | char *buf) |
2320 | { | 2547 | { |
2548 | printk_deprecated_attribute("hotkey_bios_mask", | ||
2549 | "This attribute is useless."); | ||
2321 | return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask); | 2550 | return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask); |
2322 | } | 2551 | } |
2323 | 2552 | ||
@@ -2377,7 +2606,8 @@ static ssize_t hotkey_source_mask_store(struct device *dev, | |||
2377 | hotkey_source_mask = t; | 2606 | hotkey_source_mask = t; |
2378 | HOTKEY_CONFIG_CRITICAL_END | 2607 | HOTKEY_CONFIG_CRITICAL_END |
2379 | 2608 | ||
2380 | hotkey_poll_setup(1); | 2609 | hotkey_poll_setup(true); |
2610 | hotkey_mask_set(hotkey_mask); | ||
2381 | 2611 | ||
2382 | mutex_unlock(&hotkey_mutex); | 2612 | mutex_unlock(&hotkey_mutex); |
2383 | 2613 | ||
@@ -2410,9 +2640,9 @@ static ssize_t hotkey_poll_freq_store(struct device *dev, | |||
2410 | if (mutex_lock_killable(&hotkey_mutex)) | 2640 | if (mutex_lock_killable(&hotkey_mutex)) |
2411 | return -ERESTARTSYS; | 2641 | return -ERESTARTSYS; |
2412 | 2642 | ||
2413 | hotkey_poll_freq = t; | 2643 | hotkey_poll_set_freq(t); |
2644 | hotkey_poll_setup(true); | ||
2414 | 2645 | ||
2415 | hotkey_poll_setup(1); | ||
2416 | mutex_unlock(&hotkey_mutex); | 2646 | mutex_unlock(&hotkey_mutex); |
2417 | 2647 | ||
2418 | tpacpi_disclose_usertask("hotkey_poll_freq", "set to %lu\n", t); | 2648 | tpacpi_disclose_usertask("hotkey_poll_freq", "set to %lu\n", t); |
@@ -2603,7 +2833,9 @@ static void tpacpi_send_radiosw_update(void) | |||
2603 | static void hotkey_exit(void) | 2833 | static void hotkey_exit(void) |
2604 | { | 2834 | { |
2605 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | 2835 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL |
2836 | mutex_lock(&hotkey_mutex); | ||
2606 | hotkey_poll_stop_sync(); | 2837 | hotkey_poll_stop_sync(); |
2838 | mutex_unlock(&hotkey_mutex); | ||
2607 | #endif | 2839 | #endif |
2608 | 2840 | ||
2609 | if (hotkey_dev_attributes) | 2841 | if (hotkey_dev_attributes) |
@@ -2623,6 +2855,15 @@ static void hotkey_exit(void) | |||
2623 | } | 2855 | } |
2624 | } | 2856 | } |
2625 | 2857 | ||
2858 | static void __init hotkey_unmap(const unsigned int scancode) | ||
2859 | { | ||
2860 | if (hotkey_keycode_map[scancode] != KEY_RESERVED) { | ||
2861 | clear_bit(hotkey_keycode_map[scancode], | ||
2862 | tpacpi_inputdev->keybit); | ||
2863 | hotkey_keycode_map[scancode] = KEY_RESERVED; | ||
2864 | } | ||
2865 | } | ||
2866 | |||
2626 | static int __init hotkey_init(struct ibm_init_struct *iibm) | 2867 | static int __init hotkey_init(struct ibm_init_struct *iibm) |
2627 | { | 2868 | { |
2628 | /* Requirements for changing the default keymaps: | 2869 | /* Requirements for changing the default keymaps: |
@@ -2701,11 +2942,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2701 | KEY_UNKNOWN, /* 0x0D: FN+INSERT */ | 2942 | KEY_UNKNOWN, /* 0x0D: FN+INSERT */ |
2702 | KEY_UNKNOWN, /* 0x0E: FN+DELETE */ | 2943 | KEY_UNKNOWN, /* 0x0E: FN+DELETE */ |
2703 | 2944 | ||
2704 | /* These either have to go through ACPI video, or | 2945 | /* These should be enabled --only-- when ACPI video |
2705 | * act like in the IBM ThinkPads, so don't ever | 2946 | * is disabled (i.e. in "vendor" mode), and are handled |
2706 | * enable them by default */ | 2947 | * in a special way by the init code */ |
2707 | KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ | 2948 | KEY_BRIGHTNESSUP, /* 0x0F: FN+HOME (brightness up) */ |
2708 | KEY_RESERVED, /* 0x10: FN+END (brightness down) */ | 2949 | KEY_BRIGHTNESSDOWN, /* 0x10: FN+END (brightness down) */ |
2709 | 2950 | ||
2710 | KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ | 2951 | KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ |
2711 | 2952 | ||
@@ -2831,19 +3072,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2831 | goto err_exit; | 3072 | goto err_exit; |
2832 | } | 3073 | } |
2833 | 3074 | ||
2834 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
2835 | if (tp_features.hotkey_mask) { | ||
2836 | hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK | ||
2837 | & ~hotkey_all_mask; | ||
2838 | } else { | ||
2839 | hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK; | ||
2840 | } | ||
2841 | |||
2842 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, | ||
2843 | "hotkey source mask 0x%08x, polling freq %d\n", | ||
2844 | hotkey_source_mask, hotkey_poll_freq); | ||
2845 | #endif | ||
2846 | |||
2847 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3075 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
2848 | if (dbg_wlswemul) { | 3076 | if (dbg_wlswemul) { |
2849 | tp_features.hotkey_wlsw = 1; | 3077 | tp_features.hotkey_wlsw = 1; |
@@ -2944,17 +3172,31 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2944 | "Disabling thinkpad-acpi brightness events " | 3172 | "Disabling thinkpad-acpi brightness events " |
2945 | "by default...\n"); | 3173 | "by default...\n"); |
2946 | 3174 | ||
2947 | /* The hotkey_reserved_mask change below is not | 3175 | /* Disable brightness up/down on Lenovo thinkpads when |
2948 | * necessary while the keys are at KEY_RESERVED in the | 3176 | * ACPI is handling them, otherwise it is plain impossible |
2949 | * default map, but better safe than sorry, leave it | 3177 | * for userspace to do something even remotely sane */ |
2950 | * here as a marker of what we have to do, especially | ||
2951 | * when we finally become able to set this at runtime | ||
2952 | * on response to X.org requests */ | ||
2953 | hotkey_reserved_mask |= | 3178 | hotkey_reserved_mask |= |
2954 | (1 << TP_ACPI_HOTKEYSCAN_FNHOME) | 3179 | (1 << TP_ACPI_HOTKEYSCAN_FNHOME) |
2955 | | (1 << TP_ACPI_HOTKEYSCAN_FNEND); | 3180 | | (1 << TP_ACPI_HOTKEYSCAN_FNEND); |
3181 | hotkey_unmap(TP_ACPI_HOTKEYSCAN_FNHOME); | ||
3182 | hotkey_unmap(TP_ACPI_HOTKEYSCAN_FNEND); | ||
2956 | } | 3183 | } |
2957 | 3184 | ||
3185 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
3186 | if (tp_features.hotkey_mask) { | ||
3187 | hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK | ||
3188 | & ~hotkey_all_mask | ||
3189 | & ~hotkey_reserved_mask; | ||
3190 | } else { | ||
3191 | hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK | ||
3192 | & ~hotkey_reserved_mask; | ||
3193 | } | ||
3194 | |||
3195 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, | ||
3196 | "hotkey source mask 0x%08x, polling freq %u\n", | ||
3197 | hotkey_source_mask, hotkey_poll_freq); | ||
3198 | #endif | ||
3199 | |||
2958 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, | 3200 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, |
2959 | "enabling firmware HKEY event interface...\n"); | 3201 | "enabling firmware HKEY event interface...\n"); |
2960 | res = hotkey_status_set(true); | 3202 | res = hotkey_status_set(true); |
@@ -2978,7 +3220,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2978 | tpacpi_inputdev->open = &hotkey_inputdev_open; | 3220 | tpacpi_inputdev->open = &hotkey_inputdev_open; |
2979 | tpacpi_inputdev->close = &hotkey_inputdev_close; | 3221 | tpacpi_inputdev->close = &hotkey_inputdev_close; |
2980 | 3222 | ||
2981 | hotkey_poll_setup_safe(1); | 3223 | hotkey_poll_setup_safe(true); |
2982 | tpacpi_send_radiosw_update(); | 3224 | tpacpi_send_radiosw_update(); |
2983 | tpacpi_input_send_tabletsw(); | 3225 | tpacpi_input_send_tabletsw(); |
2984 | 3226 | ||
@@ -3266,7 +3508,7 @@ static void hotkey_resume(void) | |||
3266 | hotkey_tablet_mode_notify_change(); | 3508 | hotkey_tablet_mode_notify_change(); |
3267 | hotkey_wakeup_reason_notify_change(); | 3509 | hotkey_wakeup_reason_notify_change(); |
3268 | hotkey_wakeup_hotunplug_complete_notify_change(); | 3510 | hotkey_wakeup_hotunplug_complete_notify_change(); |
3269 | hotkey_poll_setup_safe(0); | 3511 | hotkey_poll_setup_safe(false); |
3270 | } | 3512 | } |
3271 | 3513 | ||
3272 | /* procfs -------------------------------------------------------------- */ | 3514 | /* procfs -------------------------------------------------------------- */ |
@@ -3338,7 +3580,8 @@ static int hotkey_write(char *buf) | |||
3338 | hotkey_enabledisable_warn(0); | 3580 | hotkey_enabledisable_warn(0); |
3339 | res = -EPERM; | 3581 | res = -EPERM; |
3340 | } else if (strlencmp(cmd, "reset") == 0) { | 3582 | } else if (strlencmp(cmd, "reset") == 0) { |
3341 | mask = hotkey_orig_mask; | 3583 | mask = (hotkey_all_mask | hotkey_source_mask) |
3584 | & ~hotkey_reserved_mask; | ||
3342 | } else if (sscanf(cmd, "0x%x", &mask) == 1) { | 3585 | } else if (sscanf(cmd, "0x%x", &mask) == 1) { |
3343 | /* mask set */ | 3586 | /* mask set */ |
3344 | } else if (sscanf(cmd, "%x", &mask) == 1) { | 3587 | } else if (sscanf(cmd, "%x", &mask) == 1) { |
@@ -5655,16 +5898,16 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { | |||
5655 | /* Models with ATI GPUs known to require ECNVRAM mode */ | 5898 | /* Models with ATI GPUs known to require ECNVRAM mode */ |
5656 | TPACPI_Q_IBM('1', 'Y', TPACPI_BRGHT_Q_EC), /* T43/p ATI */ | 5899 | TPACPI_Q_IBM('1', 'Y', TPACPI_BRGHT_Q_EC), /* T43/p ATI */ |
5657 | 5900 | ||
5658 | /* Models with ATI GPUs (waiting confirmation) */ | 5901 | /* Models with ATI GPUs that can use ECNVRAM */ |
5659 | TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), | 5902 | TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_EC), |
5660 | TPACPI_Q_IBM('1', 'Q', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), | 5903 | TPACPI_Q_IBM('1', 'Q', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
5661 | TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), | 5904 | TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
5662 | TPACPI_Q_IBM('7', '8', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), | 5905 | TPACPI_Q_IBM('7', '8', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
5663 | 5906 | ||
5664 | /* Models with Intel Extreme Graphics 2 (waiting confirmation) */ | 5907 | /* Models with Intel Extreme Graphics 2 */ |
5908 | TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), | ||
5665 | TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), | 5909 | TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), |
5666 | TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), | 5910 | TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), |
5667 | TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), | ||
5668 | 5911 | ||
5669 | /* Models with Intel GMA900 */ | 5912 | /* Models with Intel GMA900 */ |
5670 | TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */ | 5913 | TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */ |
@@ -7524,9 +7767,11 @@ static int __init probe_for_thinkpad(void) | |||
7524 | 7767 | ||
7525 | /* | 7768 | /* |
7526 | * Non-ancient models have better DMI tagging, but very old models | 7769 | * Non-ancient models have better DMI tagging, but very old models |
7527 | * don't. | 7770 | * don't. tpacpi_is_fw_known() is a cheat to help in that case. |
7528 | */ | 7771 | */ |
7529 | is_thinkpad = (thinkpad_id.model_str != NULL); | 7772 | is_thinkpad = (thinkpad_id.model_str != NULL) || |
7773 | (thinkpad_id.ec_model != 0) || | ||
7774 | tpacpi_is_fw_known(); | ||
7530 | 7775 | ||
7531 | /* ec is required because many other handles are relative to it */ | 7776 | /* ec is required because many other handles are relative to it */ |
7532 | TPACPI_ACPIHANDLE_INIT(ec); | 7777 | TPACPI_ACPIHANDLE_INIT(ec); |
@@ -7537,13 +7782,6 @@ static int __init probe_for_thinkpad(void) | |||
7537 | return -ENODEV; | 7782 | return -ENODEV; |
7538 | } | 7783 | } |
7539 | 7784 | ||
7540 | /* | ||
7541 | * Risks a regression on very old machines, but reduces potential | ||
7542 | * false positives a damn great deal | ||
7543 | */ | ||
7544 | if (!is_thinkpad) | ||
7545 | is_thinkpad = (thinkpad_id.vendor == PCI_VENDOR_ID_IBM); | ||
7546 | |||
7547 | if (!is_thinkpad && !force_load) | 7785 | if (!is_thinkpad && !force_load) |
7548 | return -ENODEV; | 7786 | return -ENODEV; |
7549 | 7787 | ||
diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c new file mode 100644 index 000000000000..02f3d4e9e666 --- /dev/null +++ b/drivers/platform/x86/topstar-laptop.c | |||
@@ -0,0 +1,265 @@ | |||
1 | /* | ||
2 | * ACPI driver for Topstar notebooks (hotkeys support only) | ||
3 | * | ||
4 | * Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br> | ||
5 | * | ||
6 | * Implementation inspired by existing x86 platform drivers, in special | ||
7 | * asus/eepc/fujitsu-laptop, thanks to their authors | ||
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 | |||
14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/acpi.h> | ||
20 | #include <linux/input.h> | ||
21 | |||
22 | #define ACPI_TOPSTAR_CLASS "topstar" | ||
23 | |||
24 | struct topstar_hkey { | ||
25 | struct input_dev *inputdev; | ||
26 | }; | ||
27 | |||
28 | struct tps_key_entry { | ||
29 | u8 code; | ||
30 | u16 keycode; | ||
31 | }; | ||
32 | |||
33 | static struct tps_key_entry topstar_keymap[] = { | ||
34 | { 0x80, KEY_BRIGHTNESSUP }, | ||
35 | { 0x81, KEY_BRIGHTNESSDOWN }, | ||
36 | { 0x83, KEY_VOLUMEUP }, | ||
37 | { 0x84, KEY_VOLUMEDOWN }, | ||
38 | { 0x85, KEY_MUTE }, | ||
39 | { 0x86, KEY_SWITCHVIDEOMODE }, | ||
40 | { 0x87, KEY_F13 }, /* touchpad enable/disable key */ | ||
41 | { 0x88, KEY_WLAN }, | ||
42 | { 0x8a, KEY_WWW }, | ||
43 | { 0x8b, KEY_MAIL }, | ||
44 | { 0x8c, KEY_MEDIA }, | ||
45 | { 0x96, KEY_F14 }, /* G key? */ | ||
46 | { } | ||
47 | }; | ||
48 | |||
49 | static struct tps_key_entry *tps_get_key_by_scancode(int code) | ||
50 | { | ||
51 | struct tps_key_entry *key; | ||
52 | |||
53 | for (key = topstar_keymap; key->code; key++) | ||
54 | if (code == key->code) | ||
55 | return key; | ||
56 | |||
57 | return NULL; | ||
58 | } | ||
59 | |||
60 | static struct tps_key_entry *tps_get_key_by_keycode(int code) | ||
61 | { | ||
62 | struct tps_key_entry *key; | ||
63 | |||
64 | for (key = topstar_keymap; key->code; key++) | ||
65 | if (code == key->keycode) | ||
66 | return key; | ||
67 | |||
68 | return NULL; | ||
69 | } | ||
70 | |||
71 | static void acpi_topstar_notify(struct acpi_device *device, u32 event) | ||
72 | { | ||
73 | struct tps_key_entry *key; | ||
74 | static bool dup_evnt[2]; | ||
75 | bool *dup; | ||
76 | struct topstar_hkey *hkey = acpi_driver_data(device); | ||
77 | |||
78 | /* 0x83 and 0x84 key events comes duplicated... */ | ||
79 | if (event == 0x83 || event == 0x84) { | ||
80 | dup = &dup_evnt[event - 0x83]; | ||
81 | if (*dup) { | ||
82 | *dup = false; | ||
83 | return; | ||
84 | } | ||
85 | *dup = true; | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | * 'G key' generate two event codes, convert to only | ||
90 | * one event/key code for now (3G switch?) | ||
91 | */ | ||
92 | if (event == 0x97) | ||
93 | event = 0x96; | ||
94 | |||
95 | key = tps_get_key_by_scancode(event); | ||
96 | if (key) { | ||
97 | input_report_key(hkey->inputdev, key->keycode, 1); | ||
98 | input_sync(hkey->inputdev); | ||
99 | input_report_key(hkey->inputdev, key->keycode, 0); | ||
100 | input_sync(hkey->inputdev); | ||
101 | return; | ||
102 | } | ||
103 | |||
104 | /* Known non hotkey events don't handled or that we don't care yet */ | ||
105 | if (event == 0x8e || event == 0x8f || event == 0x90) | ||
106 | return; | ||
107 | |||
108 | pr_info("unknown event = 0x%02x\n", event); | ||
109 | } | ||
110 | |||
111 | static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) | ||
112 | { | ||
113 | acpi_status status; | ||
114 | union acpi_object fncx_params[1] = { | ||
115 | { .type = ACPI_TYPE_INTEGER } | ||
116 | }; | ||
117 | struct acpi_object_list fncx_arg_list = { 1, &fncx_params[0] }; | ||
118 | |||
119 | fncx_params[0].integer.value = state ? 0x86 : 0x87; | ||
120 | status = acpi_evaluate_object(device->handle, "FNCX", &fncx_arg_list, NULL); | ||
121 | if (ACPI_FAILURE(status)) { | ||
122 | pr_err("Unable to switch FNCX notifications\n"); | ||
123 | return -ENODEV; | ||
124 | } | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static int topstar_getkeycode(struct input_dev *dev, int scancode, int *keycode) | ||
130 | { | ||
131 | struct tps_key_entry *key = tps_get_key_by_scancode(scancode); | ||
132 | |||
133 | if (!key) | ||
134 | return -EINVAL; | ||
135 | |||
136 | *keycode = key->keycode; | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int topstar_setkeycode(struct input_dev *dev, int scancode, int keycode) | ||
141 | { | ||
142 | struct tps_key_entry *key; | ||
143 | int old_keycode; | ||
144 | |||
145 | if (keycode < 0 || keycode > KEY_MAX) | ||
146 | return -EINVAL; | ||
147 | |||
148 | key = tps_get_key_by_scancode(scancode); | ||
149 | |||
150 | if (!key) | ||
151 | return -EINVAL; | ||
152 | |||
153 | old_keycode = key->keycode; | ||
154 | key->keycode = keycode; | ||
155 | set_bit(keycode, dev->keybit); | ||
156 | if (!tps_get_key_by_keycode(old_keycode)) | ||
157 | clear_bit(old_keycode, dev->keybit); | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) | ||
162 | { | ||
163 | struct tps_key_entry *key; | ||
164 | |||
165 | hkey->inputdev = input_allocate_device(); | ||
166 | if (!hkey->inputdev) { | ||
167 | pr_err("Unable to allocate input device\n"); | ||
168 | return -ENODEV; | ||
169 | } | ||
170 | hkey->inputdev->name = "Topstar Laptop extra buttons"; | ||
171 | hkey->inputdev->phys = "topstar/input0"; | ||
172 | hkey->inputdev->id.bustype = BUS_HOST; | ||
173 | hkey->inputdev->getkeycode = topstar_getkeycode; | ||
174 | hkey->inputdev->setkeycode = topstar_setkeycode; | ||
175 | for (key = topstar_keymap; key->code; key++) { | ||
176 | set_bit(EV_KEY, hkey->inputdev->evbit); | ||
177 | set_bit(key->keycode, hkey->inputdev->keybit); | ||
178 | } | ||
179 | if (input_register_device(hkey->inputdev)) { | ||
180 | pr_err("Unable to register input device\n"); | ||
181 | input_free_device(hkey->inputdev); | ||
182 | return -ENODEV; | ||
183 | } | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static int acpi_topstar_add(struct acpi_device *device) | ||
189 | { | ||
190 | struct topstar_hkey *tps_hkey; | ||
191 | |||
192 | tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL); | ||
193 | if (!tps_hkey) | ||
194 | return -ENOMEM; | ||
195 | |||
196 | strcpy(acpi_device_name(device), "Topstar TPSACPI"); | ||
197 | strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS); | ||
198 | |||
199 | if (acpi_topstar_fncx_switch(device, true)) | ||
200 | goto add_err; | ||
201 | |||
202 | if (acpi_topstar_init_hkey(tps_hkey)) | ||
203 | goto add_err; | ||
204 | |||
205 | device->driver_data = tps_hkey; | ||
206 | return 0; | ||
207 | |||
208 | add_err: | ||
209 | kfree(tps_hkey); | ||
210 | return -ENODEV; | ||
211 | } | ||
212 | |||
213 | static int acpi_topstar_remove(struct acpi_device *device, int type) | ||
214 | { | ||
215 | struct topstar_hkey *tps_hkey = acpi_driver_data(device); | ||
216 | |||
217 | acpi_topstar_fncx_switch(device, false); | ||
218 | |||
219 | input_unregister_device(tps_hkey->inputdev); | ||
220 | kfree(tps_hkey); | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static const struct acpi_device_id topstar_device_ids[] = { | ||
226 | { "TPSACPI01", 0 }, | ||
227 | { "", 0 }, | ||
228 | }; | ||
229 | MODULE_DEVICE_TABLE(acpi, topstar_device_ids); | ||
230 | |||
231 | static struct acpi_driver acpi_topstar_driver = { | ||
232 | .name = "Topstar laptop ACPI driver", | ||
233 | .class = ACPI_TOPSTAR_CLASS, | ||
234 | .ids = topstar_device_ids, | ||
235 | .ops = { | ||
236 | .add = acpi_topstar_add, | ||
237 | .remove = acpi_topstar_remove, | ||
238 | .notify = acpi_topstar_notify, | ||
239 | }, | ||
240 | }; | ||
241 | |||
242 | static int __init topstar_laptop_init(void) | ||
243 | { | ||
244 | int ret; | ||
245 | |||
246 | ret = acpi_bus_register_driver(&acpi_topstar_driver); | ||
247 | if (ret < 0) | ||
248 | return ret; | ||
249 | |||
250 | printk(KERN_INFO "Topstar Laptop ACPI extras driver loaded\n"); | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static void __exit topstar_laptop_exit(void) | ||
256 | { | ||
257 | acpi_bus_unregister_driver(&acpi_topstar_driver); | ||
258 | } | ||
259 | |||
260 | module_init(topstar_laptop_init); | ||
261 | module_exit(topstar_laptop_exit); | ||
262 | |||
263 | MODULE_AUTHOR("Herton Ronaldo Krzesinski"); | ||
264 | MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver"); | ||
265 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index f215a5919192..177f8d767df4 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -42,7 +42,6 @@ MODULE_LICENSE("GPL"); | |||
42 | 42 | ||
43 | #define ACPI_WMI_CLASS "wmi" | 43 | #define ACPI_WMI_CLASS "wmi" |
44 | 44 | ||
45 | #undef PREFIX | ||
46 | #define PREFIX "ACPI: WMI: " | 45 | #define PREFIX "ACPI: WMI: " |
47 | 46 | ||
48 | static DEFINE_MUTEX(wmi_data_lock); | 47 | static DEFINE_MUTEX(wmi_data_lock); |