linux/drivers/leds/trigger/ledtrig-gpio.c
<<
>>
Prefs
   1/*
   2 * ledtrig-gio.c - LED Trigger Based on GPIO events
   3 *
   4 * Copyright 2009 Felipe Balbi <me@felipebalbi.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/kernel.h>
  14#include <linux/init.h>
  15#include <linux/gpio.h>
  16#include <linux/interrupt.h>
  17#include <linux/workqueue.h>
  18#include <linux/leds.h>
  19#include <linux/slab.h>
  20#include "../leds.h"
  21
  22struct gpio_trig_data {
  23        struct led_classdev *led;
  24        struct work_struct work;
  25
  26        unsigned desired_brightness;    /* desired brightness when led is on */
  27        unsigned inverted;              /* true when gpio is inverted */
  28        unsigned gpio;                  /* gpio that triggers the leds */
  29};
  30
  31static irqreturn_t gpio_trig_irq(int irq, void *_led)
  32{
  33        struct led_classdev *led = _led;
  34        struct gpio_trig_data *gpio_data = led->trigger_data;
  35
  36        /* just schedule_work since gpio_get_value can sleep */
  37        schedule_work(&gpio_data->work);
  38
  39        return IRQ_HANDLED;
  40};
  41
  42static void gpio_trig_work(struct work_struct *work)
  43{
  44        struct gpio_trig_data *gpio_data = container_of(work,
  45                        struct gpio_trig_data, work);
  46        int tmp;
  47
  48        if (!gpio_data->gpio)
  49                return;
  50
  51        tmp = gpio_get_value_cansleep(gpio_data->gpio);
  52        if (gpio_data->inverted)
  53                tmp = !tmp;
  54
  55        if (tmp) {
  56                if (gpio_data->desired_brightness)
  57                        led_set_brightness_nosleep(gpio_data->led,
  58                                           gpio_data->desired_brightness);
  59                else
  60                        led_set_brightness_nosleep(gpio_data->led, LED_FULL);
  61        } else {
  62                led_set_brightness_nosleep(gpio_data->led, LED_OFF);
  63        }
  64}
  65
  66static ssize_t gpio_trig_brightness_show(struct device *dev,
  67                struct device_attribute *attr, char *buf)
  68{
  69        struct led_classdev *led = dev_get_drvdata(dev);
  70        struct gpio_trig_data *gpio_data = led->trigger_data;
  71
  72        return sprintf(buf, "%u\n", gpio_data->desired_brightness);
  73}
  74
  75static ssize_t gpio_trig_brightness_store(struct device *dev,
  76                struct device_attribute *attr, const char *buf, size_t n)
  77{
  78        struct led_classdev *led = dev_get_drvdata(dev);
  79        struct gpio_trig_data *gpio_data = led->trigger_data;
  80        unsigned desired_brightness;
  81        int ret;
  82
  83        ret = sscanf(buf, "%u", &desired_brightness);
  84        if (ret < 1 || desired_brightness > 255) {
  85                dev_err(dev, "invalid value\n");
  86                return -EINVAL;
  87        }
  88
  89        gpio_data->desired_brightness = desired_brightness;
  90
  91        return n;
  92}
  93static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
  94                gpio_trig_brightness_store);
  95
  96static ssize_t gpio_trig_inverted_show(struct device *dev,
  97                struct device_attribute *attr, char *buf)
  98{
  99        struct led_classdev *led = dev_get_drvdata(dev);
 100        struct gpio_trig_data *gpio_data = led->trigger_data;
 101
 102        return sprintf(buf, "%u\n", gpio_data->inverted);
 103}
 104
 105static ssize_t gpio_trig_inverted_store(struct device *dev,
 106                struct device_attribute *attr, const char *buf, size_t n)
 107{
 108        struct led_classdev *led = dev_get_drvdata(dev);
 109        struct gpio_trig_data *gpio_data = led->trigger_data;
 110        unsigned long inverted;
 111        int ret;
 112
 113        ret = kstrtoul(buf, 10, &inverted);
 114        if (ret < 0)
 115                return ret;
 116
 117        if (inverted > 1)
 118                return -EINVAL;
 119
 120        gpio_data->inverted = inverted;
 121
 122        /* After inverting, we need to update the LED. */
 123        schedule_work(&gpio_data->work);
 124
 125        return n;
 126}
 127static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
 128                gpio_trig_inverted_store);
 129
 130static ssize_t gpio_trig_gpio_show(struct device *dev,
 131                struct device_attribute *attr, char *buf)
 132{
 133        struct led_classdev *led = dev_get_drvdata(dev);
 134        struct gpio_trig_data *gpio_data = led->trigger_data;
 135
 136        return sprintf(buf, "%u\n", gpio_data->gpio);
 137}
 138
 139static ssize_t gpio_trig_gpio_store(struct device *dev,
 140                struct device_attribute *attr, const char *buf, size_t n)
 141{
 142        struct led_classdev *led = dev_get_drvdata(dev);
 143        struct gpio_trig_data *gpio_data = led->trigger_data;
 144        unsigned gpio;
 145        int ret;
 146
 147        ret = sscanf(buf, "%u", &gpio);
 148        if (ret < 1) {
 149                dev_err(dev, "couldn't read gpio number\n");
 150                flush_work(&gpio_data->work);
 151                return -EINVAL;
 152        }
 153
 154        if (gpio_data->gpio == gpio)
 155                return n;
 156
 157        if (!gpio) {
 158                if (gpio_data->gpio != 0)
 159                        free_irq(gpio_to_irq(gpio_data->gpio), led);
 160                gpio_data->gpio = 0;
 161                return n;
 162        }
 163
 164        ret = request_irq(gpio_to_irq(gpio), gpio_trig_irq,
 165                        IRQF_SHARED | IRQF_TRIGGER_RISING
 166                        | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
 167        if (ret) {
 168                dev_err(dev, "request_irq failed with error %d\n", ret);
 169        } else {
 170                if (gpio_data->gpio != 0)
 171                        free_irq(gpio_to_irq(gpio_data->gpio), led);
 172                gpio_data->gpio = gpio;
 173        }
 174
 175        return ret ? ret : n;
 176}
 177static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
 178
 179static void gpio_trig_activate(struct led_classdev *led)
 180{
 181        struct gpio_trig_data *gpio_data;
 182        int ret;
 183
 184        gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
 185        if (!gpio_data)
 186                return;
 187
 188        ret = device_create_file(led->dev, &dev_attr_gpio);
 189        if (ret)
 190                goto err_gpio;
 191
 192        ret = device_create_file(led->dev, &dev_attr_inverted);
 193        if (ret)
 194                goto err_inverted;
 195
 196        ret = device_create_file(led->dev, &dev_attr_desired_brightness);
 197        if (ret)
 198                goto err_brightness;
 199
 200        gpio_data->led = led;
 201        led->trigger_data = gpio_data;
 202        INIT_WORK(&gpio_data->work, gpio_trig_work);
 203        led->activated = true;
 204
 205        return;
 206
 207err_brightness:
 208        device_remove_file(led->dev, &dev_attr_inverted);
 209
 210err_inverted:
 211        device_remove_file(led->dev, &dev_attr_gpio);
 212
 213err_gpio:
 214        kfree(gpio_data);
 215}
 216
 217static void gpio_trig_deactivate(struct led_classdev *led)
 218{
 219        struct gpio_trig_data *gpio_data = led->trigger_data;
 220
 221        if (led->activated) {
 222                device_remove_file(led->dev, &dev_attr_gpio);
 223                device_remove_file(led->dev, &dev_attr_inverted);
 224                device_remove_file(led->dev, &dev_attr_desired_brightness);
 225                flush_work(&gpio_data->work);
 226                if (gpio_data->gpio != 0)
 227                        free_irq(gpio_to_irq(gpio_data->gpio), led);
 228                kfree(gpio_data);
 229                led->activated = false;
 230        }
 231}
 232
 233static struct led_trigger gpio_led_trigger = {
 234        .name           = "gpio",
 235        .activate       = gpio_trig_activate,
 236        .deactivate     = gpio_trig_deactivate,
 237};
 238
 239static int __init gpio_trig_init(void)
 240{
 241        return led_trigger_register(&gpio_led_trigger);
 242}
 243module_init(gpio_trig_init);
 244
 245static void __exit gpio_trig_exit(void)
 246{
 247        led_trigger_unregister(&gpio_led_trigger);
 248}
 249module_exit(gpio_trig_exit);
 250
 251MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
 252MODULE_DESCRIPTION("GPIO LED trigger");
 253MODULE_LICENSE("GPL");
 254