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