[patch 87/88] thinkpad-acpi: fix LED blinking through timer trigger

Greg KH
2009-04-30T17:48:16Z

[-- Attachment #1: thinkpad-acpi-fix-led-blinking-through-timer-trigger.patch --]
[-- Type: text/plain, Size: 3925 bytes --]

2.6.28-stable review patch. If anyone has any objections, please let us know.

------------------

From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>

commit 75bd3bf2ade9d548be0d2bde60b5ee0fdce0b127 upstream.

The set_blink hook code in the LED subdriver would never manage to get
a LED to blink, and instead it would just turn it on. The consequence
of this is that the "timer" trigger would not cause the LED to blink
if given default parameters.

This problem exists since 2.6.26-rc1.

To fix it, switch the deferred LED work handling to use the
thinkpad-acpi-specific LED status (off/on/blink) directly.

This also makes the code easier to read, and to extend later.

Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: stable@kernel.org
Signed-off-by: Len Brown <len.brown@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

---
drivers/misc/thinkpad_acpi.c | 41 +++++++++++++++++++----------------------
1 file changed, 19 insertions(+), 22 deletions(-)

--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -281,11 +281,17 @@ static u32 dbg_level;

static struct workqueue_struct *tpacpi_wq;

+enum led_status_t {
+ TPACPI_LED_OFF = 0,
+ TPACPI_LED_ON,
+ TPACPI_LED_BLINK,
+};
+
/* Special LED class that can defer work */
struct tpacpi_led_classdev {
struct led_classdev led_classdev;
struct work_struct work;
- enum led_brightness new_brightness;
+ enum led_status_t new_state;
unsigned int led;
};

@@ -3489,7 +3495,7 @@ static void light_set_status_worker(stru
container_of(work, struct tpacpi_led_classdev, work);

if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
- light_set_status((data->new_brightness != LED_OFF));
+ light_set_status((data->new_state != TPACPI_LED_OFF));
}

static void light_sysfs_set(struct led_classdev *led_cdev,
@@ -3499,7 +3505,8 @@ static void light_sysfs_set(struct led_c
container_of(led_cdev,
struct tpacpi_led_classdev,
led_classdev);
- data->new_brightness = brightness;
+ data->new_state = (brightness != LED_OFF) ?
+ TPACPI_LED_ON : TPACPI_LED_OFF;
queue_work(tpacpi_wq, &data->work);
}

@@ -4006,12 +4013,6 @@ enum { /* For TPACPI_LED_OLD */
TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */
};

-enum led_status_t {
- TPACPI_LED_OFF = 0,
- TPACPI_LED_ON,
- TPACPI_LED_BLINK,
-};
-
static enum led_access_mode led_supported;

TPACPI_HANDLE(led, ec, "SLED", /* 570 */
@@ -4105,23 +4106,13 @@ static int led_set_status(const unsigned
return rc;
}

-static void led_sysfs_set_status(unsigned int led,
- enum led_brightness brightness)
-{
- led_set_status(led,
- (brightness == LED_OFF) ?
- TPACPI_LED_OFF :
- (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ?
- TPACPI_LED_BLINK : TPACPI_LED_ON);
-}
-
static void led_set_status_worker(struct work_struct *work)
{
struct tpacpi_led_classdev *data =
container_of(work, struct tpacpi_led_classdev, work);

if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
- led_sysfs_set_status(data->led, data->new_brightness);
+ led_set_status(data->led, data->new_state);
}

static void led_sysfs_set(struct led_classdev *led_cdev,
@@ -4130,7 +4121,13 @@ static void led_sysfs_set(struct led_cla
struct tpacpi_led_classdev *data = container_of(led_cdev,
struct tpacpi_led_classdev, led_classdev);

- data->new_brightness = brightness;
+ if (brightness == LED_OFF)
+ data->new_state = TPACPI_LED_OFF;
+ else if (tpacpi_led_state_cache[data->led] != TPACPI_LED_BLINK)
+ data->new_state = TPACPI_LED_ON;
+ else
+ data->new_state = TPACPI_LED_BLINK;
+
queue_work(tpacpi_wq, &data->work);
}

@@ -4148,7 +4145,7 @@ static int led_sysfs_blink_set(struct le
} else if ((*delay_on != 500) || (*delay_off != 500))
return -EINVAL;

- data->new_brightness = TPACPI_LED_BLINK;
+ data->new_state = TPACPI_LED_BLINK;
queue_work(tpacpi_wq, &data->work);

return 0;