diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 136 |
1 files changed, 135 insertions, 1 deletions
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 | /************************************************************************* |