ledtrig-oneshot.c (4858B)
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 21struct oneshot_trig_data { 22 unsigned int invert; 23}; 24 25static 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} 38static 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 46static 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 68static 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 76static 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 92static 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 100static 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 116static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store); 117static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store); 118static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store); 119static DEVICE_ATTR(shot, 0200, NULL, led_shot); 120 121static 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}; 128ATTRIBUTE_GROUPS(oneshot_trig); 129 130static 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 152out_default: 153 kfree(pattern); 154 led_cdev->blink_delay_on = DEFAULT_DELAY; 155 led_cdev->blink_delay_off = DEFAULT_DELAY; 156} 157 158static 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 180static 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 190static 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}; 196module_led_trigger(oneshot_led_trigger); 197 198MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri@gmail.com>"); 199MODULE_DESCRIPTION("One-shot LED trigger"); 200MODULE_LICENSE("GPL v2");