diff options
author | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 2008-04-26 00:02:25 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-04-29 09:47:02 -0400 |
commit | af116101924914a9655dfad108548d0db58c40f9 (patch) | |
tree | cc5dddd4579c4b3099234b8d99139f9246b6e989 | |
parent | e306501d1c4ff610feaba74ac35dd13e470480e6 (diff) |
ACPI: thinkpad-acpi: add sysfs led class support to thinkpad leds (v3.2)
Add a sysfs led class interface to the led subdriver.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | Documentation/laptops/thinkpad-acpi.txt | 47 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 136 |
2 files changed, 176 insertions, 7 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index af1f2bcb6f66..73b80a7e9b03 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
@@ -876,28 +876,63 @@ The cmos command interface is prone to firmware split-brain problems, as | |||
876 | in newer ThinkPads it is just a compatibility layer. Do not use it, it is | 876 | in newer ThinkPads it is just a compatibility layer. Do not use it, it is |
877 | exported just as a debug tool. | 877 | exported just as a debug tool. |
878 | 878 | ||
879 | LED control -- /proc/acpi/ibm/led | 879 | LED control |
880 | --------------------------------- | 880 | ----------- |
881 | 881 | ||
882 | Some of the LED indicators can be controlled through this feature. The | 882 | procfs: /proc/acpi/ibm/led |
883 | available commands are: | 883 | sysfs attributes: as per led class, see below for names |
884 | |||
885 | Some of the LED indicators can be controlled through this feature. On | ||
886 | some older ThinkPad models, it is possible to query the status of the | ||
887 | LED indicators as well. Newer ThinkPads cannot query the real status | ||
888 | of the LED indicators. | ||
889 | |||
890 | procfs notes: | ||
891 | |||
892 | The available commands are: | ||
884 | 893 | ||
885 | echo '<led number> on' >/proc/acpi/ibm/led | 894 | echo '<led number> on' >/proc/acpi/ibm/led |
886 | echo '<led number> off' >/proc/acpi/ibm/led | 895 | echo '<led number> off' >/proc/acpi/ibm/led |
887 | echo '<led number> blink' >/proc/acpi/ibm/led | 896 | echo '<led number> blink' >/proc/acpi/ibm/led |
888 | 897 | ||
889 | The <led number> range is 0 to 7. The set of LEDs that can be | 898 | The <led number> range is 0 to 7. The set of LEDs that can be |
890 | controlled varies from model to model. Here is the mapping on the X40: | 899 | controlled varies from model to model. Here is the common ThinkPad |
900 | mapping: | ||
891 | 901 | ||
892 | 0 - power | 902 | 0 - power |
893 | 1 - battery (orange) | 903 | 1 - battery (orange) |
894 | 2 - battery (green) | 904 | 2 - battery (green) |
895 | 3 - UltraBase | 905 | 3 - UltraBase/dock |
896 | 4 - UltraBay | 906 | 4 - UltraBay |
907 | 5 - UltraBase battery slot | ||
908 | 6 - (unknown) | ||
897 | 7 - standby | 909 | 7 - standby |
898 | 910 | ||
899 | All of the above can be turned on and off and can be made to blink. | 911 | All of the above can be turned on and off and can be made to blink. |
900 | 912 | ||
913 | sysfs notes: | ||
914 | |||
915 | The ThinkPad LED sysfs interface is described in detail by the led class | ||
916 | documentation, in Documentation/leds-class.txt. | ||
917 | |||
918 | The leds are named (in LED ID order, from 0 to 7): | ||
919 | "tpacpi::power", "tpacpi:orange:batt", "tpacpi:green:batt", | ||
920 | "tpacpi::dock_active", "tpacpi::bay_active", "tpacpi::dock_batt", | ||
921 | "tpacpi::unknown_led", "tpacpi::standby". | ||
922 | |||
923 | Due to limitations in the sysfs led class, if the status of the LED | ||
924 | indicators cannot be read due to an error, thinkpad-acpi will report it as | ||
925 | a brightness of zero (same as LED off). | ||
926 | |||
927 | If the thinkpad firmware doesn't support reading the current status, | ||
928 | trying to read the current LED brightness will just return whatever | ||
929 | brightness was last written to that attribute. | ||
930 | |||
931 | These LEDs can blink using hardware acceleration. To request that a | ||
932 | ThinkPad indicator LED should blink in hardware accelerated mode, use the | ||
933 | "timer" trigger, and leave the delay_on and delay_off parameters set to | ||
934 | zero (to request hardware acceleration autodetection). | ||
935 | |||
901 | ACPI sounds -- /proc/acpi/ibm/beep | 936 | ACPI sounds -- /proc/acpi/ibm/beep |
902 | ---------------------------------- | 937 | ---------------------------------- |
903 | 938 | ||
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 38a119bd931e..2ab3633f4e56 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -277,6 +277,7 @@ struct tpacpi_led_classdev { | |||
277 | struct led_classdev led_classdev; | 277 | struct led_classdev led_classdev; |
278 | struct work_struct work; | 278 | struct work_struct work; |
279 | enum led_brightness new_brightness; | 279 | enum led_brightness new_brightness; |
280 | unsigned int led; | ||
280 | }; | 281 | }; |
281 | 282 | ||
282 | /**************************************************************************** | 283 | /**************************************************************************** |
@@ -3814,20 +3815,38 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */ | |||
3814 | "LED", /* all others */ | 3815 | "LED", /* all others */ |
3815 | ); /* R30, R31 */ | 3816 | ); /* R30, R31 */ |
3816 | 3817 | ||
3818 | #define TPACPI_LED_NUMLEDS 8 | ||
3819 | static struct tpacpi_led_classdev *tpacpi_leds; | ||
3820 | static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS]; | ||
3821 | static const char const *tpacpi_led_names[TPACPI_LED_NUMLEDS] = { | ||
3822 | /* there's a limit of 19 chars + NULL before 2.6.26 */ | ||
3823 | "tpacpi::power", | ||
3824 | "tpacpi:orange:batt", | ||
3825 | "tpacpi:green:batt", | ||
3826 | "tpacpi::dock_active", | ||
3827 | "tpacpi::bay_active", | ||
3828 | "tpacpi::dock_batt", | ||
3829 | "tpacpi::unknown_led", | ||
3830 | "tpacpi::standby", | ||
3831 | }; | ||
3832 | |||
3817 | static int led_get_status(unsigned int led) | 3833 | static int led_get_status(unsigned int led) |
3818 | { | 3834 | { |
3819 | int status; | 3835 | int status; |
3836 | enum led_status_t led_s; | ||
3820 | 3837 | ||
3821 | switch (led_supported) { | 3838 | switch (led_supported) { |
3822 | case TPACPI_LED_570: | 3839 | case TPACPI_LED_570: |
3823 | if (!acpi_evalf(ec_handle, | 3840 | if (!acpi_evalf(ec_handle, |
3824 | &status, "GLED", "dd", 1 << led)) | 3841 | &status, "GLED", "dd", 1 << led)) |
3825 | return -EIO; | 3842 | return -EIO; |
3826 | return (status == 0)? | 3843 | led_s = (status == 0)? |
3827 | TPACPI_LED_OFF : | 3844 | TPACPI_LED_OFF : |
3828 | ((status == 1)? | 3845 | ((status == 1)? |
3829 | TPACPI_LED_ON : | 3846 | TPACPI_LED_ON : |
3830 | TPACPI_LED_BLINK); | 3847 | TPACPI_LED_BLINK); |
3848 | tpacpi_led_state_cache[led] = led_s; | ||
3849 | return led_s; | ||
3831 | default: | 3850 | default: |
3832 | return -ENXIO; | 3851 | return -ENXIO; |
3833 | } | 3852 | } |
@@ -3874,11 +3893,96 @@ static int led_set_status(unsigned int led, enum led_status_t ledstatus) | |||
3874 | rc = -ENXIO; | 3893 | rc = -ENXIO; |
3875 | } | 3894 | } |
3876 | 3895 | ||
3896 | if (!rc) | ||
3897 | tpacpi_led_state_cache[led] = ledstatus; | ||
3898 | |||
3877 | return rc; | 3899 | return rc; |
3878 | } | 3900 | } |
3879 | 3901 | ||
3902 | static void led_sysfs_set_status(unsigned int led, | ||
3903 | enum led_brightness brightness) | ||
3904 | { | ||
3905 | led_set_status(led, | ||
3906 | (brightness == LED_OFF) ? | ||
3907 | TPACPI_LED_OFF : | ||
3908 | (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ? | ||
3909 | TPACPI_LED_BLINK : TPACPI_LED_ON); | ||
3910 | } | ||
3911 | |||
3912 | static void led_set_status_worker(struct work_struct *work) | ||
3913 | { | ||
3914 | struct tpacpi_led_classdev *data = | ||
3915 | container_of(work, struct tpacpi_led_classdev, work); | ||
3916 | |||
3917 | if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) | ||
3918 | led_sysfs_set_status(data->led, data->new_brightness); | ||
3919 | } | ||
3920 | |||
3921 | static void led_sysfs_set(struct led_classdev *led_cdev, | ||
3922 | enum led_brightness brightness) | ||
3923 | { | ||
3924 | struct tpacpi_led_classdev *data = container_of(led_cdev, | ||
3925 | struct tpacpi_led_classdev, led_classdev); | ||
3926 | |||
3927 | data->new_brightness = brightness; | ||
3928 | schedule_work(&data->work); | ||
3929 | } | ||
3930 | |||
3931 | static int led_sysfs_blink_set(struct led_classdev *led_cdev, | ||
3932 | unsigned long *delay_on, unsigned long *delay_off) | ||
3933 | { | ||
3934 | struct tpacpi_led_classdev *data = container_of(led_cdev, | ||
3935 | struct tpacpi_led_classdev, led_classdev); | ||
3936 | |||
3937 | /* Can we choose the flash rate? */ | ||
3938 | if (*delay_on == 0 && *delay_off == 0) { | ||
3939 | /* yes. set them to the hardware blink rate (1 Hz) */ | ||
3940 | *delay_on = 500; /* ms */ | ||
3941 | *delay_off = 500; /* ms */ | ||
3942 | } else if ((*delay_on != 500) || (*delay_off != 500)) | ||
3943 | return -EINVAL; | ||
3944 | |||
3945 | data->new_brightness = TPACPI_LED_BLINK; | ||
3946 | schedule_work(&data->work); | ||
3947 | |||
3948 | return 0; | ||
3949 | } | ||
3950 | |||
3951 | static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev) | ||
3952 | { | ||
3953 | int rc; | ||
3954 | |||
3955 | struct tpacpi_led_classdev *data = container_of(led_cdev, | ||
3956 | struct tpacpi_led_classdev, led_classdev); | ||
3957 | |||
3958 | rc = led_get_status(data->led); | ||
3959 | |||
3960 | if (rc == TPACPI_LED_OFF || rc < 0) | ||
3961 | rc = LED_OFF; /* no error handling in led class :( */ | ||
3962 | else | ||
3963 | rc = LED_FULL; | ||
3964 | |||
3965 | return rc; | ||
3966 | } | ||
3967 | |||
3968 | static void led_exit(void) | ||
3969 | { | ||
3970 | unsigned int i; | ||
3971 | |||
3972 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { | ||
3973 | if (tpacpi_leds[i].led_classdev.name) | ||
3974 | led_classdev_unregister(&tpacpi_leds[i].led_classdev); | ||
3975 | } | ||
3976 | |||
3977 | kfree(tpacpi_leds); | ||
3978 | tpacpi_leds = NULL; | ||
3979 | } | ||
3980 | |||
3880 | static int __init led_init(struct ibm_init_struct *iibm) | 3981 | static int __init led_init(struct ibm_init_struct *iibm) |
3881 | { | 3982 | { |
3983 | unsigned int i; | ||
3984 | int rc; | ||
3985 | |||
3882 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); | 3986 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); |
3883 | 3987 | ||
3884 | TPACPI_ACPIHANDLE_INIT(led); | 3988 | TPACPI_ACPIHANDLE_INIT(led); |
@@ -3899,6 +4003,35 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
3899 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", | 4003 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", |
3900 | str_supported(led_supported), led_supported); | 4004 | str_supported(led_supported), led_supported); |
3901 | 4005 | ||
4006 | tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, | ||
4007 | GFP_KERNEL); | ||
4008 | if (!tpacpi_leds) { | ||
4009 | printk(TPACPI_ERR "Out of memory for LED data\n"); | ||
4010 | return -ENOMEM; | ||
4011 | } | ||
4012 | |||
4013 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { | ||
4014 | tpacpi_leds[i].led = i; | ||
4015 | |||
4016 | tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set; | ||
4017 | tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set; | ||
4018 | if (led_supported == TPACPI_LED_570) | ||
4019 | tpacpi_leds[i].led_classdev.brightness_get = | ||
4020 | &led_sysfs_get; | ||
4021 | |||
4022 | tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i]; | ||
4023 | |||
4024 | INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker); | ||
4025 | |||
4026 | rc = led_classdev_register(&tpacpi_pdev->dev, | ||
4027 | &tpacpi_leds[i].led_classdev); | ||
4028 | if (rc < 0) { | ||
4029 | tpacpi_leds[i].led_classdev.name = NULL; | ||
4030 | led_exit(); | ||
4031 | return rc; | ||
4032 | } | ||
4033 | } | ||
4034 | |||
3902 | return (led_supported != TPACPI_LED_NONE)? 0 : 1; | 4035 | return (led_supported != TPACPI_LED_NONE)? 0 : 1; |
3903 | } | 4036 | } |
3904 | 4037 | ||
@@ -3969,6 +4102,7 @@ static struct ibm_struct led_driver_data = { | |||
3969 | .name = "led", | 4102 | .name = "led", |
3970 | .read = led_read, | 4103 | .read = led_read, |
3971 | .write = led_write, | 4104 | .write = led_write, |
4105 | .exit = led_exit, | ||
3972 | }; | 4106 | }; |
3973 | 4107 | ||
3974 | /************************************************************************* | 4108 | /************************************************************************* |