leds: fix multiple requests and releases of IRQ for GPIO LED Trigger
authorThadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
Wed, 26 Aug 2009 21:29:31 +0000 (14:29 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 27 Aug 2009 03:06:53 +0000 (20:06 -0700)
When setting the same GPIO number, multiple IRQ shared requests will be
done without freing the previous request.  It will also try to free a
failed request or an already freed IRQ if 0 was written to the gpio file.

All these oops and leaks were fixed with the following solution: keep the
previous allocated GPIO (if any) still allocated in case the new request
fails.  The alternative solution would desallocate the previous allocated
GPIO and set gpio as 0.

Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
Signed-off-by: Samuel R. C. Vale <srcvale@holoscopio.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/leds/ledtrig-gpio.c

index a247ae6..8183b81 100644 (file)
@@ -146,20 +146,26 @@ static ssize_t gpio_trig_gpio_store(struct device *dev,
                return -EINVAL;
        }
 
+       if (gpio_data->gpio == gpio)
+               return n;
+
        if (!gpio) {
-               free_irq(gpio_to_irq(gpio_data->gpio), led);
+               if (gpio_data->gpio != 0)
+                       free_irq(gpio_to_irq(gpio_data->gpio), led);
+               gpio_data->gpio = 0;
                return n;
        }
 
-       if (gpio_data->gpio > 0 && gpio_data->gpio != gpio)
-               free_irq(gpio_to_irq(gpio_data->gpio), led);
-
-       gpio_data->gpio = gpio;
        ret = request_irq(gpio_to_irq(gpio), gpio_trig_irq,
                        IRQF_SHARED | IRQF_TRIGGER_RISING
                        | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
-       if (ret)
+       if (ret) {
                dev_err(dev, "request_irq failed with error %d\n", ret);
+       } else {
+               if (gpio_data->gpio != 0)
+                       free_irq(gpio_to_irq(gpio_data->gpio), led);
+               gpio_data->gpio = gpio;
+       }
 
        return ret ? ret : n;
 }
@@ -211,7 +217,8 @@ static void gpio_trig_deactivate(struct led_classdev *led)
                device_remove_file(led->dev, &dev_attr_inverted);
                device_remove_file(led->dev, &dev_attr_desired_brightness);
                flush_work(&gpio_data->work);
-               free_irq(gpio_to_irq(gpio_data->gpio),led);
+               if (gpio_data->gpio != 0)
+                       free_irq(gpio_to_irq(gpio_data->gpio), led);
                kfree(gpio_data);
        }
 }