xref: /linux/drivers/leds/trigger/ledtrig-pattern.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 /*
4  * LED pattern trigger
5  *
6  * Idea discussed with Pavel Machek. Raphael Teysseyre implemented
7  * the first version, Baolin Wang simplified and improved the approach.
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/leds.h>
12 #include <linux/module.h>
13 #include <linux/mutex.h>
14 #include <linux/slab.h>
15 #include <linux/timer.h>
16 #include <linux/hrtimer.h>
17 
18 #define MAX_PATTERNS		1024
19 /*
20  * When doing gradual dimming, the led brightness will be updated
21  * every 50 milliseconds.
22  */
23 #define UPDATE_INTERVAL		50
24 
25 enum pattern_type {
26 	PATTERN_TYPE_SW, /* Use standard timer for software pattern */
27 	PATTERN_TYPE_HR, /* Use hrtimer for software pattern */
28 	PATTERN_TYPE_HW, /* Hardware pattern */
29 };
30 
31 struct pattern_trig_data {
32 	struct led_classdev *led_cdev;
33 	struct led_pattern patterns[MAX_PATTERNS];
34 	struct led_pattern *curr;
35 	struct led_pattern *next;
36 	struct mutex lock;
37 	u32 npatterns;
38 	int repeat;
39 	int last_repeat;
40 	int delta_t;
41 	bool is_indefinite;
42 	enum pattern_type type;
43 	struct timer_list timer;
44 	struct hrtimer hrtimer;
45 };
46 
47 static void pattern_trig_update_patterns(struct pattern_trig_data *data)
48 {
49 	data->curr = data->next;
50 	if (!data->is_indefinite && data->curr == data->patterns)
51 		data->repeat--;
52 
53 	if (data->next == data->patterns + data->npatterns - 1)
54 		data->next = data->patterns;
55 	else
56 		data->next++;
57 
58 	data->delta_t = 0;
59 }
60 
61 static int pattern_trig_compute_brightness(struct pattern_trig_data *data)
62 {
63 	int step_brightness;
64 
65 	/*
66 	 * If current tuple's duration is less than the dimming interval,
67 	 * we should treat it as a step change of brightness instead of
68 	 * doing gradual dimming.
69 	 */
70 	if (data->delta_t == 0 || data->curr->delta_t < UPDATE_INTERVAL)
71 		return data->curr->brightness;
72 
73 	step_brightness = abs(data->next->brightness - data->curr->brightness);
74 	step_brightness = data->delta_t * step_brightness / data->curr->delta_t;
75 
76 	if (data->next->brightness > data->curr->brightness)
77 		return data->curr->brightness + step_brightness;
78 	else
79 		return data->curr->brightness - step_brightness;
80 }
81 
82 static void pattern_trig_timer_start(struct pattern_trig_data *data)
83 {
84 	if (data->type == PATTERN_TYPE_HR) {
85 		hrtimer_start(&data->hrtimer, ns_to_ktime(0), HRTIMER_MODE_REL);
86 	} else {
87 		data->timer.expires = jiffies;
88 		add_timer(&data->timer);
89 	}
90 }
91 
92 static void pattern_trig_timer_cancel(struct pattern_trig_data *data)
93 {
94 	if (data->type == PATTERN_TYPE_HR)
95 		hrtimer_cancel(&data->hrtimer);
96 	else
97 		del_timer_sync(&data->timer);
98 }
99 
100 static void pattern_trig_timer_restart(struct pattern_trig_data *data,
101 				       unsigned long interval)
102 {
103 	if (data->type == PATTERN_TYPE_HR)
104 		hrtimer_forward_now(&data->hrtimer, ms_to_ktime(interval));
105 	else
106 		mod_timer(&data->timer, jiffies + msecs_to_jiffies(interval));
107 }
108 
109 static void pattern_trig_timer_common_function(struct pattern_trig_data *data)
110 {
111 	for (;;) {
112 		if (!data->is_indefinite && !data->repeat)
113 			break;
114 
115 		if (data->curr->brightness == data->next->brightness) {
116 			/* Step change of brightness */
117 			led_set_brightness(data->led_cdev,
118 					   data->curr->brightness);
119 			pattern_trig_timer_restart(data, data->curr->delta_t);
120 			if (!data->next->delta_t) {
121 				/* Skip the tuple with zero duration */
122 				pattern_trig_update_patterns(data);
123 			}
124 			/* Select next tuple */
125 			pattern_trig_update_patterns(data);
126 		} else {
127 			/* Gradual dimming */
128 
129 			/*
130 			 * If the accumulation time is larger than current
131 			 * tuple's duration, we should go next one and re-check
132 			 * if we repeated done.
133 			 */
134 			if (data->delta_t > data->curr->delta_t) {
135 				pattern_trig_update_patterns(data);
136 				continue;
137 			}
138 
139 			led_set_brightness(data->led_cdev,
140 					   pattern_trig_compute_brightness(data));
141 			pattern_trig_timer_restart(data, UPDATE_INTERVAL);
142 
143 			/* Accumulate the gradual dimming time */
144 			data->delta_t += UPDATE_INTERVAL;
145 		}
146 
147 		break;
148 	}
149 }
150 
151 static void pattern_trig_timer_function(struct timer_list *t)
152 {
153 	struct pattern_trig_data *data = from_timer(data, t, timer);
154 
155 	return pattern_trig_timer_common_function(data);
156 }
157 
158 static enum hrtimer_restart pattern_trig_hrtimer_function(struct hrtimer *t)
159 {
160 	struct pattern_trig_data *data =
161 		container_of(t, struct pattern_trig_data, hrtimer);
162 
163 	pattern_trig_timer_common_function(data);
164 	if (!data->is_indefinite && !data->repeat)
165 		return HRTIMER_NORESTART;
166 
167 	return HRTIMER_RESTART;
168 }
169 
170 static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
171 {
172 	struct pattern_trig_data *data = led_cdev->trigger_data;
173 
174 	if (!data->npatterns)
175 		return 0;
176 
177 	if (data->type == PATTERN_TYPE_HW) {
178 		return led_cdev->pattern_set(led_cdev, data->patterns,
179 					     data->npatterns, data->repeat);
180 	}
181 
182 	/* At least 2 tuples for software pattern. */
183 	if (data->npatterns < 2)
184 		return -EINVAL;
185 
186 	data->delta_t = 0;
187 	data->curr = data->patterns;
188 	data->next = data->patterns + 1;
189 	pattern_trig_timer_start(data);
190 
191 	return 0;
192 }
193 
194 static ssize_t repeat_show(struct device *dev, struct device_attribute *attr,
195 			   char *buf)
196 {
197 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
198 	struct pattern_trig_data *data = led_cdev->trigger_data;
199 	int repeat;
200 
201 	mutex_lock(&data->lock);
202 
203 	repeat = data->last_repeat;
204 
205 	mutex_unlock(&data->lock);
206 
207 	return sysfs_emit(buf, "%d\n", repeat);
208 }
209 
210 static ssize_t repeat_store(struct device *dev, struct device_attribute *attr,
211 			    const char *buf, size_t count)
212 {
213 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
214 	struct pattern_trig_data *data = led_cdev->trigger_data;
215 	int err, res;
216 
217 	err = kstrtos32(buf, 10, &res);
218 	if (err)
219 		return err;
220 
221 	/* Number 0 and negative numbers except -1 are invalid. */
222 	if (res < -1 || res == 0)
223 		return -EINVAL;
224 
225 	mutex_lock(&data->lock);
226 
227 	pattern_trig_timer_cancel(data);
228 
229 	if (data->type == PATTERN_TYPE_HW)
230 		led_cdev->pattern_clear(led_cdev);
231 
232 	data->last_repeat = data->repeat = res;
233 	/* -1 means repeat indefinitely */
234 	if (data->repeat == -1)
235 		data->is_indefinite = true;
236 	else
237 		data->is_indefinite = false;
238 
239 	err = pattern_trig_start_pattern(led_cdev);
240 
241 	mutex_unlock(&data->lock);
242 	return err < 0 ? err : count;
243 }
244 
245 static DEVICE_ATTR_RW(repeat);
246 
247 static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data,
248 					  char *buf, enum pattern_type type)
249 {
250 	ssize_t count = 0;
251 	int i;
252 
253 	mutex_lock(&data->lock);
254 
255 	if (!data->npatterns || data->type != type)
256 		goto out;
257 
258 	for (i = 0; i < data->npatterns; i++) {
259 		count += scnprintf(buf + count, PAGE_SIZE - count,
260 				   "%d %u ",
261 				   data->patterns[i].brightness,
262 				   data->patterns[i].delta_t);
263 	}
264 
265 	buf[count - 1] = '\n';
266 
267 out:
268 	mutex_unlock(&data->lock);
269 	return count;
270 }
271 
272 static int pattern_trig_store_patterns_string(struct pattern_trig_data *data,
273 					      const char *buf, size_t count)
274 {
275 	int ccount, cr, offset = 0;
276 
277 	while (offset < count - 1 && data->npatterns < MAX_PATTERNS) {
278 		cr = 0;
279 		ccount = sscanf(buf + offset, "%u %u %n",
280 				&data->patterns[data->npatterns].brightness,
281 				&data->patterns[data->npatterns].delta_t, &cr);
282 
283 		if (ccount != 2 ||
284 		    data->patterns[data->npatterns].brightness > data->led_cdev->max_brightness) {
285 			data->npatterns = 0;
286 			return -EINVAL;
287 		}
288 
289 		offset += cr;
290 		data->npatterns++;
291 	}
292 
293 	return 0;
294 }
295 
296 static int pattern_trig_store_patterns_int(struct pattern_trig_data *data,
297 					   const u32 *buf, size_t count)
298 {
299 	unsigned int i;
300 
301 	for (i = 0; i < count; i += 2) {
302 		data->patterns[data->npatterns].brightness = buf[i];
303 		data->patterns[data->npatterns].delta_t = buf[i + 1];
304 		data->npatterns++;
305 	}
306 
307 	return 0;
308 }
309 
310 static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,
311 					   const char *buf, const u32 *buf_int,
312 					   size_t count, enum pattern_type type)
313 {
314 	struct pattern_trig_data *data = led_cdev->trigger_data;
315 	int err = 0;
316 
317 	mutex_lock(&data->lock);
318 
319 	pattern_trig_timer_cancel(data);
320 
321 	if (data->type == PATTERN_TYPE_HW)
322 		led_cdev->pattern_clear(led_cdev);
323 
324 	data->type = type;
325 	data->npatterns = 0;
326 
327 	if (buf)
328 		err = pattern_trig_store_patterns_string(data, buf, count);
329 	else
330 		err = pattern_trig_store_patterns_int(data, buf_int, count);
331 	if (err)
332 		goto out;
333 
334 	err = pattern_trig_start_pattern(led_cdev);
335 	if (err)
336 		data->npatterns = 0;
337 
338 out:
339 	mutex_unlock(&data->lock);
340 	return err < 0 ? err : count;
341 }
342 
343 static ssize_t pattern_show(struct device *dev, struct device_attribute *attr,
344 			    char *buf)
345 {
346 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
347 	struct pattern_trig_data *data = led_cdev->trigger_data;
348 
349 	return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_SW);
350 }
351 
352 static ssize_t pattern_store(struct device *dev, struct device_attribute *attr,
353 			     const char *buf, size_t count)
354 {
355 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
356 
357 	return pattern_trig_store_patterns(led_cdev, buf, NULL, count,
358 					   PATTERN_TYPE_SW);
359 }
360 
361 static DEVICE_ATTR_RW(pattern);
362 
363 static ssize_t hw_pattern_show(struct device *dev,
364 			       struct device_attribute *attr, char *buf)
365 {
366 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
367 	struct pattern_trig_data *data = led_cdev->trigger_data;
368 
369 	return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_HW);
370 }
371 
372 static ssize_t hw_pattern_store(struct device *dev,
373 				struct device_attribute *attr,
374 				const char *buf, size_t count)
375 {
376 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
377 
378 	return pattern_trig_store_patterns(led_cdev, buf, NULL, count,
379 					   PATTERN_TYPE_HW);
380 }
381 
382 static DEVICE_ATTR_RW(hw_pattern);
383 
384 static ssize_t hr_pattern_show(struct device *dev,
385 			       struct device_attribute *attr, char *buf)
386 {
387 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
388 	struct pattern_trig_data *data = led_cdev->trigger_data;
389 
390 	return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_HR);
391 }
392 
393 static ssize_t hr_pattern_store(struct device *dev,
394 				struct device_attribute *attr,
395 				const char *buf, size_t count)
396 {
397 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
398 
399 	return pattern_trig_store_patterns(led_cdev, buf, NULL, count,
400 					   PATTERN_TYPE_HR);
401 }
402 
403 static DEVICE_ATTR_RW(hr_pattern);
404 
405 static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
406 				       struct attribute *attr, int index)
407 {
408 	struct device *dev = kobj_to_dev(kobj);
409 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
410 
411 	if (attr == &dev_attr_repeat.attr || attr == &dev_attr_pattern.attr)
412 		return attr->mode;
413 	else if (attr == &dev_attr_hr_pattern.attr)
414 		return attr->mode;
415 	else if (attr == &dev_attr_hw_pattern.attr && led_cdev->pattern_set)
416 		return attr->mode;
417 
418 	return 0;
419 }
420 
421 static struct attribute *pattern_trig_attrs[] = {
422 	&dev_attr_pattern.attr,
423 	&dev_attr_hw_pattern.attr,
424 	&dev_attr_hr_pattern.attr,
425 	&dev_attr_repeat.attr,
426 	NULL
427 };
428 
429 static const struct attribute_group pattern_trig_group = {
430 	.attrs = pattern_trig_attrs,
431 	.is_visible = pattern_trig_attrs_mode,
432 };
433 
434 static const struct attribute_group *pattern_trig_groups[] = {
435 	&pattern_trig_group,
436 	NULL,
437 };
438 
439 static void pattern_init(struct led_classdev *led_cdev)
440 {
441 	unsigned int size = 0;
442 	u32 *pattern;
443 	int err;
444 
445 	pattern = led_get_default_pattern(led_cdev, &size);
446 	if (!pattern)
447 		return;
448 
449 	if (size % 2) {
450 		dev_warn(led_cdev->dev, "Expected pattern of tuples\n");
451 		goto out;
452 	}
453 
454 	err = pattern_trig_store_patterns(led_cdev, NULL, pattern, size,
455 					  PATTERN_TYPE_SW);
456 	if (err < 0)
457 		dev_warn(led_cdev->dev,
458 			 "Pattern initialization failed with error %d\n", err);
459 
460 out:
461 	kfree(pattern);
462 }
463 
464 static int pattern_trig_activate(struct led_classdev *led_cdev)
465 {
466 	struct pattern_trig_data *data;
467 
468 	data = kzalloc(sizeof(*data), GFP_KERNEL);
469 	if (!data)
470 		return -ENOMEM;
471 
472 	if (!!led_cdev->pattern_set ^ !!led_cdev->pattern_clear) {
473 		dev_warn(led_cdev->dev,
474 			 "Hardware pattern ops validation failed\n");
475 		led_cdev->pattern_set = NULL;
476 		led_cdev->pattern_clear = NULL;
477 	}
478 
479 	data->type = PATTERN_TYPE_SW;
480 	data->is_indefinite = true;
481 	data->last_repeat = -1;
482 	mutex_init(&data->lock);
483 	data->led_cdev = led_cdev;
484 	led_set_trigger_data(led_cdev, data);
485 	timer_setup(&data->timer, pattern_trig_timer_function, 0);
486 	hrtimer_init(&data->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
487 	data->hrtimer.function = pattern_trig_hrtimer_function;
488 	led_cdev->activated = true;
489 
490 	if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) {
491 		pattern_init(led_cdev);
492 		/*
493 		 * Mark as initialized even on pattern_init() error because
494 		 * any consecutive call to it would produce the same error.
495 		 */
496 		led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER;
497 	}
498 
499 	return 0;
500 }
501 
502 static void pattern_trig_deactivate(struct led_classdev *led_cdev)
503 {
504 	struct pattern_trig_data *data = led_cdev->trigger_data;
505 
506 	if (!led_cdev->activated)
507 		return;
508 
509 	if (led_cdev->pattern_clear)
510 		led_cdev->pattern_clear(led_cdev);
511 
512 	timer_shutdown_sync(&data->timer);
513 	hrtimer_cancel(&data->hrtimer);
514 
515 	led_set_brightness(led_cdev, LED_OFF);
516 	kfree(data);
517 	led_cdev->activated = false;
518 }
519 
520 static struct led_trigger pattern_led_trigger = {
521 	.name = "pattern",
522 	.activate = pattern_trig_activate,
523 	.deactivate = pattern_trig_deactivate,
524 	.groups = pattern_trig_groups,
525 };
526 
527 static int __init pattern_trig_init(void)
528 {
529 	return led_trigger_register(&pattern_led_trigger);
530 }
531 
532 static void __exit pattern_trig_exit(void)
533 {
534 	led_trigger_unregister(&pattern_led_trigger);
535 }
536 
537 module_init(pattern_trig_init);
538 module_exit(pattern_trig_exit);
539 
540 MODULE_AUTHOR("Raphael Teysseyre <rteysseyre@gmail.com>");
541 MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
542 MODULE_DESCRIPTION("LED Pattern trigger");
543 MODULE_LICENSE("GPL v2");
544