1 /* 2 * One-shot LED Trigger 3 * 4 * Copyright 2012, Fabio Baltieri <fabio.baltieri@gmail.com> 5 * 6 * Based on ledtrig-timer.c by Richard Purdie <rpurdie@openedhand.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13 #include <linux/module.h> 14 #include <linux/kernel.h> 15 #include <linux/init.h> 16 #include <linux/device.h> 17 #include <linux/ctype.h> 18 #include <linux/slab.h> 19 #include <linux/leds.h> 20 #include "../leds.h" 21 22 #define DEFAULT_DELAY 100 23 24 struct oneshot_trig_data { 25 unsigned int invert; 26 }; 27 28 static ssize_t led_shot(struct device *dev, 29 struct device_attribute *attr, const char *buf, size_t size) 30 { 31 struct led_classdev *led_cdev = led_trigger_get_led(dev); 32 struct oneshot_trig_data *oneshot_data = led_trigger_get_drvdata(dev); 33 34 led_blink_set_oneshot(led_cdev, 35 &led_cdev->blink_delay_on, &led_cdev->blink_delay_off, 36 oneshot_data->invert); 37 38 /* content is ignored */ 39 return size; 40 } 41 static ssize_t led_invert_show(struct device *dev, 42 struct device_attribute *attr, char *buf) 43 { 44 struct oneshot_trig_data *oneshot_data = led_trigger_get_drvdata(dev); 45 46 return sprintf(buf, "%u\n", oneshot_data->invert); 47 } 48 49 static ssize_t led_invert_store(struct device *dev, 50 struct device_attribute *attr, const char *buf, size_t size) 51 { 52 struct led_classdev *led_cdev = led_trigger_get_led(dev); 53 struct oneshot_trig_data *oneshot_data = led_trigger_get_drvdata(dev); 54 unsigned long state; 55 int ret; 56 57 ret = kstrtoul(buf, 0, &state); 58 if (ret) 59 return ret; 60 61 oneshot_data->invert = !!state; 62 63 if (oneshot_data->invert) 64 led_set_brightness_nosleep(led_cdev, LED_FULL); 65 else 66 led_set_brightness_nosleep(led_cdev, LED_OFF); 67 68 return size; 69 } 70 71 static ssize_t led_delay_on_show(struct device *dev, 72 struct device_attribute *attr, char *buf) 73 { 74 struct led_classdev *led_cdev = led_trigger_get_led(dev); 75 76 return sprintf(buf, "%lu\n", led_cdev->blink_delay_on); 77 } 78 79 static ssize_t led_delay_on_store(struct device *dev, 80 struct device_attribute *attr, const char *buf, size_t size) 81 { 82 struct led_classdev *led_cdev = led_trigger_get_led(dev); 83 unsigned long state; 84 int ret; 85 86 ret = kstrtoul(buf, 0, &state); 87 if (ret) 88 return ret; 89 90 led_cdev->blink_delay_on = state; 91 92 return size; 93 } 94 95 static ssize_t led_delay_off_show(struct device *dev, 96 struct device_attribute *attr, char *buf) 97 { 98 struct led_classdev *led_cdev = led_trigger_get_led(dev); 99 100 return sprintf(buf, "%lu\n", led_cdev->blink_delay_off); 101 } 102 103 static ssize_t led_delay_off_store(struct device *dev, 104 struct device_attribute *attr, const char *buf, size_t size) 105 { 106 struct led_classdev *led_cdev = led_trigger_get_led(dev); 107 unsigned long state; 108 int ret; 109 110 ret = kstrtoul(buf, 0, &state); 111 if (ret) 112 return ret; 113 114 led_cdev->blink_delay_off = state; 115 116 return size; 117 } 118 119 static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store); 120 static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store); 121 static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store); 122 static DEVICE_ATTR(shot, 0200, NULL, led_shot); 123 124 static struct attribute *oneshot_trig_attrs[] = { 125 &dev_attr_delay_on.attr, 126 &dev_attr_delay_off.attr, 127 &dev_attr_invert.attr, 128 &dev_attr_shot.attr, 129 NULL 130 }; 131 ATTRIBUTE_GROUPS(oneshot_trig); 132 133 static void pattern_init(struct led_classdev *led_cdev) 134 { 135 u32 *pattern; 136 unsigned int size = 0; 137 138 pattern = led_get_default_pattern(led_cdev, &size); 139 if (!pattern) 140 goto out_default; 141 142 if (size != 2) { 143 dev_warn(led_cdev->dev, 144 "Expected 2 but got %u values for delays pattern\n", 145 size); 146 goto out_default; 147 } 148 149 led_cdev->blink_delay_on = pattern[0]; 150 led_cdev->blink_delay_off = pattern[1]; 151 kfree(pattern); 152 153 return; 154 155 out_default: 156 kfree(pattern); 157 led_cdev->blink_delay_on = DEFAULT_DELAY; 158 led_cdev->blink_delay_off = DEFAULT_DELAY; 159 } 160 161 static int oneshot_trig_activate(struct led_classdev *led_cdev) 162 { 163 struct oneshot_trig_data *oneshot_data; 164 165 oneshot_data = kzalloc(sizeof(*oneshot_data), GFP_KERNEL); 166 if (!oneshot_data) 167 return -ENOMEM; 168 169 led_set_trigger_data(led_cdev, oneshot_data); 170 171 if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) { 172 pattern_init(led_cdev); 173 /* 174 * Mark as initialized even on pattern_init() error because 175 * any consecutive call to it would produce the same error. 176 */ 177 led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER; 178 } 179 180 return 0; 181 } 182 183 static void oneshot_trig_deactivate(struct led_classdev *led_cdev) 184 { 185 struct oneshot_trig_data *oneshot_data = led_get_trigger_data(led_cdev); 186 187 kfree(oneshot_data); 188 189 /* Stop blinking */ 190 led_set_brightness(led_cdev, LED_OFF); 191 } 192 193 static struct led_trigger oneshot_led_trigger = { 194 .name = "oneshot", 195 .activate = oneshot_trig_activate, 196 .deactivate = oneshot_trig_deactivate, 197 .groups = oneshot_trig_groups, 198 }; 199 module_led_trigger(oneshot_led_trigger); 200 201 MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri@gmail.com>"); 202 MODULE_DESCRIPTION("One-shot LED trigger"); 203 MODULE_LICENSE("GPL v2"); 204