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