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