Linux Audio

Check our new training course

Yocto distribution development and maintenance

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