201 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * One-shot LED Trigger4 *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 10020 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 else63 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 NULL127};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 because172 * 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");201