aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/laptops/thinkpad-acpi.txt47
-rw-r--r--drivers/misc/thinkpad_acpi.c136
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
876in newer ThinkPads it is just a compatibility layer. Do not use it, it is 876in newer ThinkPads it is just a compatibility layer. Do not use it, it is
877exported just as a debug tool. 877exported just as a debug tool.
878 878
879LED control -- /proc/acpi/ibm/led 879LED control
880--------------------------------- 880-----------
881 881
882Some of the LED indicators can be controlled through this feature. The 882procfs: /proc/acpi/ibm/led
883available commands are: 883sysfs attributes: as per led class, see below for names
884
885Some of the LED indicators can be controlled through this feature. On
886some older ThinkPad models, it is possible to query the status of the
887LED indicators as well. Newer ThinkPads cannot query the real status
888of the LED indicators.
889
890procfs notes:
891
892The 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
889The <led number> range is 0 to 7. The set of LEDs that can be 898The <led number> range is 0 to 7. The set of LEDs that can be
890controlled varies from model to model. Here is the mapping on the X40: 899controlled varies from model to model. Here is the common ThinkPad
900mapping:
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
899All of the above can be turned on and off and can be made to blink. 911All of the above can be turned on and off and can be made to blink.
900 912
913sysfs notes:
914
915The ThinkPad LED sysfs interface is described in detail by the led class
916documentation, in Documentation/leds-class.txt.
917
918The 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
923Due to limitations in the sysfs led class, if the status of the LED
924indicators cannot be read due to an error, thinkpad-acpi will report it as
925a brightness of zero (same as LED off).
926
927If the thinkpad firmware doesn't support reading the current status,
928trying to read the current LED brightness will just return whatever
929brightness was last written to that attribute.
930
931These LEDs can blink using hardware acceleration. To request that a
932ThinkPad indicator LED should blink in hardware accelerated mode, use the
933"timer" trigger, and leave the delay_on and delay_off parameters set to
934zero (to request hardware acceleration autodetection).
935
901ACPI sounds -- /proc/acpi/ibm/beep 936ACPI 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
3819static struct tpacpi_led_classdev *tpacpi_leds;
3820static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
3821static 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
3817static int led_get_status(unsigned int led) 3833static 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
3902static 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
3912static 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
3921static 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
3931static 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
3951static 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
3968static 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
3880static int __init led_init(struct ibm_init_struct *iibm) 3981static 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/*************************************************************************