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_get_trigger_data(led); 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 gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); 55 56 return sprintf(buf, "%u\n", gpio_data->desired_brightness); 57 } 58 59 static ssize_t gpio_trig_brightness_store(struct device *dev, 60 struct device_attribute *attr, const char *buf, size_t n) 61 { 62 struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); 63 unsigned desired_brightness; 64 int ret; 65 66 ret = sscanf(buf, "%u", &desired_brightness); 67 if (ret < 1 || desired_brightness > 255) { 68 dev_err(dev, "invalid value\n"); 69 return -EINVAL; 70 } 71 72 gpio_data->desired_brightness = desired_brightness; 73 74 return n; 75 } 76 static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show, 77 gpio_trig_brightness_store); 78 79 static ssize_t gpio_trig_inverted_show(struct device *dev, 80 struct device_attribute *attr, char *buf) 81 { 82 struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); 83 84 return sprintf(buf, "%u\n", gpio_data->inverted); 85 } 86 87 static ssize_t gpio_trig_inverted_store(struct device *dev, 88 struct device_attribute *attr, const char *buf, size_t n) 89 { 90 struct led_classdev *led = led_trigger_get_led(dev); 91 struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); 92 unsigned long inverted; 93 int ret; 94 95 ret = kstrtoul(buf, 10, &inverted); 96 if (ret < 0) 97 return ret; 98 99 if (inverted > 1) 100 return -EINVAL; 101 102 gpio_data->inverted = inverted; 103 104 /* After inverting, we need to update the LED. */ 105 gpio_trig_irq(0, led); 106 107 return n; 108 } 109 static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show, 110 gpio_trig_inverted_store); 111 112 static ssize_t gpio_trig_gpio_show(struct device *dev, 113 struct device_attribute *attr, char *buf) 114 { 115 struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); 116 117 return sprintf(buf, "%u\n", gpio_data->gpio); 118 } 119 120 static ssize_t gpio_trig_gpio_store(struct device *dev, 121 struct device_attribute *attr, const char *buf, size_t n) 122 { 123 struct led_classdev *led = led_trigger_get_led(dev); 124 struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); 125 unsigned gpio; 126 int ret; 127 128 ret = sscanf(buf, "%u", &gpio); 129 if (ret < 1) { 130 dev_err(dev, "couldn't read gpio number\n"); 131 return -EINVAL; 132 } 133 134 if (gpio_data->gpio == gpio) 135 return n; 136 137 if (!gpio) { 138 if (gpio_data->gpio != 0) 139 free_irq(gpio_to_irq(gpio_data->gpio), led); 140 gpio_data->gpio = 0; 141 return n; 142 } 143 144 ret = request_threaded_irq(gpio_to_irq(gpio), NULL, gpio_trig_irq, 145 IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING 146 | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led); 147 if (ret) { 148 dev_err(dev, "request_irq failed with error %d\n", ret); 149 } else { 150 if (gpio_data->gpio != 0) 151 free_irq(gpio_to_irq(gpio_data->gpio), led); 152 gpio_data->gpio = gpio; 153 /* After changing the GPIO, we need to update the LED. */ 154 gpio_trig_irq(0, led); 155 } 156 157 return ret ? ret : n; 158 } 159 static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store); 160 161 static struct attribute *gpio_trig_attrs[] = { 162 &dev_attr_desired_brightness.attr, 163 &dev_attr_inverted.attr, 164 &dev_attr_gpio.attr, 165 NULL 166 }; 167 ATTRIBUTE_GROUPS(gpio_trig); 168 169 static int gpio_trig_activate(struct led_classdev *led) 170 { 171 struct gpio_trig_data *gpio_data; 172 173 gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL); 174 if (!gpio_data) 175 return -ENOMEM; 176 177 gpio_data->led = led; 178 led_set_trigger_data(led, gpio_data); 179 180 return 0; 181 } 182 183 static void gpio_trig_deactivate(struct led_classdev *led) 184 { 185 struct gpio_trig_data *gpio_data = led_get_trigger_data(led); 186 187 if (gpio_data->gpio != 0) 188 free_irq(gpio_to_irq(gpio_data->gpio), led); 189 kfree(gpio_data); 190 } 191 192 static struct led_trigger gpio_led_trigger = { 193 .name = "gpio", 194 .activate = gpio_trig_activate, 195 .deactivate = gpio_trig_deactivate, 196 .groups = gpio_trig_groups, 197 }; 198 module_led_trigger(gpio_led_trigger); 199 200 MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>"); 201 MODULE_DESCRIPTION("GPIO LED trigger"); 202 MODULE_LICENSE("GPL v2"); 203