diff options
-rw-r--r-- | Documentation/laptops/thinkpad-acpi.txt | 11 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 24 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 76 |
3 files changed, 91 insertions, 20 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index de6f14c79070..bce1d959b703 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
@@ -901,6 +901,17 @@ some older ThinkPad models, it is possible to query the status of the | |||
901 | LED indicators as well. Newer ThinkPads cannot query the real status | 901 | LED indicators as well. Newer ThinkPads cannot query the real status |
902 | of the LED indicators. | 902 | of the LED indicators. |
903 | 903 | ||
904 | Because misuse of the LEDs could induce an unaware user to perform | ||
905 | dangerous actions (like undocking or ejecting a bay device while the | ||
906 | buses are still active), or mask an important alarm (such as a nearly | ||
907 | empty battery, or a broken battery), access to most LEDs is | ||
908 | restricted. | ||
909 | |||
910 | Unrestricted access to all LEDs requires that thinkpad-acpi be | ||
911 | compiled with the CONFIG_THINKPAD_ACPI_UNSAFE_LEDS option enabled. | ||
912 | Distributions must never enable this option. Individual users that | ||
913 | are aware of the consequences are welcome to enabling it. | ||
914 | |||
904 | procfs notes: | 915 | procfs notes: |
905 | 916 | ||
906 | The available commands are: | 917 | The available commands are: |
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 3608081bc3e0..d45c6ab729f8 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -226,6 +226,30 @@ config THINKPAD_ACPI_DEBUG | |||
226 | 226 | ||
227 | If you are not sure, say N here. | 227 | If you are not sure, say N here. |
228 | 228 | ||
229 | config THINKPAD_ACPI_UNSAFE_LEDS | ||
230 | bool "Allow control of important LEDs (unsafe)" | ||
231 | depends on THINKPAD_ACPI | ||
232 | default n | ||
233 | ---help--- | ||
234 | Overriding LED state on ThinkPads can mask important | ||
235 | firmware alerts (like critical battery condition), or misled | ||
236 | the user into damaging the hardware (undocking or ejecting | ||
237 | the bay while buses are still active), etc. | ||
238 | |||
239 | LED control on the ThinkPad is write-only (with very few | ||
240 | exceptions on very ancient models), which makes it | ||
241 | impossible to know beforehand if important information will | ||
242 | be lost when one changes LED state. | ||
243 | |||
244 | Users that know what they are doing can enable this option | ||
245 | and the driver will allow control of every LED, including | ||
246 | the ones on the dock stations. | ||
247 | |||
248 | Never enable this option on a distribution kernel. | ||
249 | |||
250 | Say N here, unless you are building a kernel for your own | ||
251 | use, and need to control the important firmware LEDs. | ||
252 | |||
229 | config THINKPAD_ACPI_DOCK | 253 | config THINKPAD_ACPI_DOCK |
230 | bool "Legacy Docking Station Support" | 254 | bool "Legacy Docking Station Support" |
231 | depends on THINKPAD_ACPI | 255 | depends on THINKPAD_ACPI |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index f003fb7c79ca..38c34c79ff35 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -4657,6 +4657,16 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { | |||
4657 | "tpacpi::unknown_led", | 4657 | "tpacpi::unknown_led", |
4658 | "tpacpi::standby", | 4658 | "tpacpi::standby", |
4659 | }; | 4659 | }; |
4660 | #define TPACPI_SAFE_LEDS 0x0081U | ||
4661 | |||
4662 | static inline bool tpacpi_is_led_restricted(const unsigned int led) | ||
4663 | { | ||
4664 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS | ||
4665 | return false; | ||
4666 | #else | ||
4667 | return (TPACPI_SAFE_LEDS & (1 << led)) == 0; | ||
4668 | #endif | ||
4669 | } | ||
4660 | 4670 | ||
4661 | static int led_get_status(const unsigned int led) | 4671 | static int led_get_status(const unsigned int led) |
4662 | { | 4672 | { |
@@ -4694,16 +4704,20 @@ static int led_set_status(const unsigned int led, | |||
4694 | switch (led_supported) { | 4704 | switch (led_supported) { |
4695 | case TPACPI_LED_570: | 4705 | case TPACPI_LED_570: |
4696 | /* 570 */ | 4706 | /* 570 */ |
4697 | if (led > 7) | 4707 | if (unlikely(led > 7)) |
4698 | return -EINVAL; | 4708 | return -EINVAL; |
4709 | if (unlikely(tpacpi_is_led_restricted(led))) | ||
4710 | return -EPERM; | ||
4699 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | 4711 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", |
4700 | (1 << led), led_sled_arg1[ledstatus])) | 4712 | (1 << led), led_sled_arg1[ledstatus])) |
4701 | rc = -EIO; | 4713 | rc = -EIO; |
4702 | break; | 4714 | break; |
4703 | case TPACPI_LED_OLD: | 4715 | case TPACPI_LED_OLD: |
4704 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ | 4716 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ |
4705 | if (led > 7) | 4717 | if (unlikely(led > 7)) |
4706 | return -EINVAL; | 4718 | return -EINVAL; |
4719 | if (unlikely(tpacpi_is_led_restricted(led))) | ||
4720 | return -EPERM; | ||
4707 | rc = ec_write(TPACPI_LED_EC_HLMS, (1 << led)); | 4721 | rc = ec_write(TPACPI_LED_EC_HLMS, (1 << led)); |
4708 | if (rc >= 0) | 4722 | if (rc >= 0) |
4709 | rc = ec_write(TPACPI_LED_EC_HLBL, | 4723 | rc = ec_write(TPACPI_LED_EC_HLBL, |
@@ -4714,6 +4728,10 @@ static int led_set_status(const unsigned int led, | |||
4714 | break; | 4728 | break; |
4715 | case TPACPI_LED_NEW: | 4729 | case TPACPI_LED_NEW: |
4716 | /* all others */ | 4730 | /* all others */ |
4731 | if (unlikely(led >= TPACPI_LED_NUMLEDS)) | ||
4732 | return -EINVAL; | ||
4733 | if (unlikely(tpacpi_is_led_restricted(led))) | ||
4734 | return -EPERM; | ||
4717 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | 4735 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", |
4718 | led, led_led_arg1[ledstatus])) | 4736 | led, led_led_arg1[ledstatus])) |
4719 | rc = -EIO; | 4737 | rc = -EIO; |
@@ -4806,6 +4824,30 @@ static void led_exit(void) | |||
4806 | kfree(tpacpi_leds); | 4824 | kfree(tpacpi_leds); |
4807 | } | 4825 | } |
4808 | 4826 | ||
4827 | static int __init tpacpi_init_led(unsigned int led) | ||
4828 | { | ||
4829 | int rc; | ||
4830 | |||
4831 | tpacpi_leds[led].led = led; | ||
4832 | |||
4833 | tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; | ||
4834 | tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; | ||
4835 | if (led_supported == TPACPI_LED_570) | ||
4836 | tpacpi_leds[led].led_classdev.brightness_get = | ||
4837 | &led_sysfs_get; | ||
4838 | |||
4839 | tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led]; | ||
4840 | |||
4841 | INIT_WORK(&tpacpi_leds[led].work, led_set_status_worker); | ||
4842 | |||
4843 | rc = led_classdev_register(&tpacpi_pdev->dev, | ||
4844 | &tpacpi_leds[led].led_classdev); | ||
4845 | if (rc < 0) | ||
4846 | tpacpi_leds[led].led_classdev.name = NULL; | ||
4847 | |||
4848 | return rc; | ||
4849 | } | ||
4850 | |||
4809 | static int __init led_init(struct ibm_init_struct *iibm) | 4851 | static int __init led_init(struct ibm_init_struct *iibm) |
4810 | { | 4852 | { |
4811 | unsigned int i; | 4853 | unsigned int i; |
@@ -4839,27 +4881,21 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
4839 | } | 4881 | } |
4840 | 4882 | ||
4841 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { | 4883 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { |
4842 | tpacpi_leds[i].led = i; | 4884 | if (!tpacpi_is_led_restricted(i)) { |
4843 | 4885 | rc = tpacpi_init_led(i); | |
4844 | tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set; | 4886 | if (rc < 0) { |
4845 | tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set; | 4887 | led_exit(); |
4846 | if (led_supported == TPACPI_LED_570) | 4888 | return rc; |
4847 | tpacpi_leds[i].led_classdev.brightness_get = | 4889 | } |
4848 | &led_sysfs_get; | ||
4849 | |||
4850 | tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i]; | ||
4851 | |||
4852 | INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker); | ||
4853 | |||
4854 | rc = led_classdev_register(&tpacpi_pdev->dev, | ||
4855 | &tpacpi_leds[i].led_classdev); | ||
4856 | if (rc < 0) { | ||
4857 | tpacpi_leds[i].led_classdev.name = NULL; | ||
4858 | led_exit(); | ||
4859 | return rc; | ||
4860 | } | 4890 | } |
4861 | } | 4891 | } |
4862 | 4892 | ||
4893 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS | ||
4894 | if (led_supported != TPACPI_LED_NONE) | ||
4895 | printk(TPACPI_NOTICE | ||
4896 | "warning: userspace override of important " | ||
4897 | "firmware LEDs is enabled\n"); | ||
4898 | #endif | ||
4863 | return (led_supported != TPACPI_LED_NONE)? 0 : 1; | 4899 | return (led_supported != TPACPI_LED_NONE)? 0 : 1; |
4864 | } | 4900 | } |
4865 | 4901 | ||