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