1 /*
2 * LED Kernel Multi-Control Trigger
3 *
4 * Control multi leds at one time using ioctl from userspace.
5 *
6 * Copyright 2017 Allen Zhang <zwp@rock-chips.com>
7 *
8 * Based on Richard Purdie's ledtrig-timer.c.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 */
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/init.h>
18 #include <linux/proc_fs.h>
19 #include <linux/ioctl.h>
20 #include <linux/uaccess.h>
21 #include <linux/slab.h>
22 #include <linux/miscdevice.h>
23 #include <linux/leds.h>
24 #include "../leds.h"
25 #include "../leds-multi.h"
26
27 struct multi_ctrl_data {
28 struct led_ctrl_data *data;
29 struct led_classdev *led_cdev;
30 struct delayed_work delay_trig_work;
31 struct list_head node;
32 struct led_ctrl_data old_data;
33 };
34
35 struct multi_ctrl_scroll_data {
36 struct led_ctrl_scroll_data *data;
37 volatile bool data_valid;
38 struct delayed_work scroll_work;
39 };
40
41 struct multi_ctrl_breath_data {
42 struct led_ctrl_breath_data *data;
43 volatile bool data_valid;
44 struct delayed_work breath_work;
45 };
46
47 enum leds_mode {
48 LEDS_MODE_MULTI_SET = 0,
49 LEDS_MODE_MULTI_SCROLL,
50 LEDS_MODE_MULTI_BREATH,
51 LEDS_MODE_MULTI_INVALID = 0xff,
52 };
53
54 #define bits(nr) ((u64)1 << (nr))
55
56 static DECLARE_RWSEM(multi_leds_list_lock);
57 static LIST_HEAD(multi_leds_list);
58
59 static int led_num;
60 static struct miscdevice multi_ctrl_miscdev;
61 static struct multi_ctrl_scroll_data *multi_ctrl_scroll_info;
62 static struct multi_ctrl_breath_data *multi_ctrl_breath_info;
63 static char *mult_ctrl_trigger[TRIG_MAX] = {
64 "none",
65 "default-on",
66 "timer",
67 "oneshot",
68 };
69
70 static struct led_ctrl_data leds_data[MAX_LEDS_NUMBER];
71 static struct led_ctrl_scroll_data leds_scroll_data;
72 static struct led_ctrl_breath_data leds_breath_data;
73 static enum leds_mode leds_pre_mode = LEDS_MODE_MULTI_INVALID;
74
multi_ctrl_calc_next_scroll_bitmap(u64 cur_bitmap)75 static u64 multi_ctrl_calc_next_scroll_bitmap(u64 cur_bitmap)
76 {
77 u64 bitmap = cur_bitmap << leds_scroll_data.shifts;
78 u64 ret_bitmap = bitmap & (bits(led_num) - 1);
79
80 if (bitmap > (bits(led_num) - 1))
81 ret_bitmap |= bitmap >> led_num;
82 else
83 ret_bitmap = bitmap;
84
85 return ret_bitmap;
86 }
87
multi_ctrl_scroll_work_fn(struct work_struct * ws)88 static void multi_ctrl_scroll_work_fn(struct work_struct *ws)
89 {
90 struct multi_ctrl_data *ctrl_data;
91 int bit = 0;
92 static u64 update_bits = ~0;
93
94 down_read(&multi_leds_list_lock);
95 if (!multi_ctrl_scroll_info->data_valid) {
96 update_bits = bits(led_num) - 1;
97 multi_ctrl_scroll_info->data_valid = true;
98 pr_info("%s, new scroll work is queued\n", __func__);
99 }
100 if (unlikely(update_bits > (bits(led_num) - 1))) {
101 pr_warn("%s,update_bits is exceed the max led numbers!\n",
102 __func__);
103 update_bits = bits(led_num) - 1;
104 }
105
106 if (leds_pre_mode != LEDS_MODE_MULTI_SCROLL) {
107 update_bits = bits(led_num) - 1;
108 leds_pre_mode = LEDS_MODE_MULTI_SCROLL;
109 }
110
111 list_for_each_entry(ctrl_data, &multi_leds_list, node) {
112 struct led_classdev *led_cdev = ctrl_data->led_cdev;
113
114 if (bit >= led_num) {
115 dev_err(led_cdev->dev, "exceed the max number of muti_leds_list\n");
116 break;
117 }
118
119 cancel_delayed_work_sync(&ctrl_data->delay_trig_work);
120
121 /* only change the led status when bits updated */
122 if (update_bits & bits(bit)) {
123 if (leds_scroll_data.init_bitmap & bits(bit)) {
124 led_trigger_set_by_name(led_cdev,
125 mult_ctrl_trigger[TRIG_DEF_ON]);
126 } else {
127 led_trigger_remove(led_cdev);
128 }
129 }
130
131 bit++;
132 }
133 update_bits = leds_scroll_data.init_bitmap;
134 leds_scroll_data.init_bitmap = multi_ctrl_calc_next_scroll_bitmap(update_bits);
135 update_bits ^= leds_scroll_data.init_bitmap;
136 up_read(&multi_leds_list_lock);
137
138 schedule_delayed_work(&multi_ctrl_scroll_info->scroll_work,
139 msecs_to_jiffies(leds_scroll_data.shift_delay_ms));
140 }
141
multi_ctrl_breath_work_fn(struct work_struct * ws)142 static void multi_ctrl_breath_work_fn(struct work_struct *ws)
143 {
144 struct multi_ctrl_data *ctrl_data;
145 int bit = 0;
146 u32 bri_every_step;
147 static u32 pre_brightness = LED_HALF;
148 static u64 pre_bg_bitmap;
149 static u64 pre_br_bitmap;
150 static u64 pre_black_bitmap;
151
152 down_read(&multi_leds_list_lock);
153 if (!multi_ctrl_breath_info->data_valid) {
154 pre_bg_bitmap = 0;
155 pre_br_bitmap = 0;
156 pre_black_bitmap = 0;
157 multi_ctrl_breath_info->data_valid = true;
158 pr_info("%s, new breath work is queued\n", __func__);
159 }
160
161 bri_every_step = LED_FULL / leds_breath_data.breath_steps;
162 list_for_each_entry(ctrl_data, &multi_leds_list, node) {
163 struct led_classdev *led_cdev = ctrl_data->led_cdev;
164
165 if (bit >= led_num) {
166 dev_err(led_cdev->dev, "exceed the max number of muti_leds_list\n");
167 break;
168 }
169
170 cancel_delayed_work_sync(&ctrl_data->delay_trig_work);
171
172 if (pre_bg_bitmap == 0) {
173 if (leds_breath_data.background_bitmap & bits(bit)) {
174 led_trigger_set_by_name(led_cdev,
175 mult_ctrl_trigger[TRIG_DEF_ON]);
176 }
177 }
178
179 if (leds_breath_data.breath_bitmap & bits(bit)) {
180 /* only update the leds indicated by bitmap*/
181 led_cdev->brightness =
182 (pre_brightness + bri_every_step) % LED_FULL;
183 if (unlikely(pre_br_bitmap == 0))
184 led_trigger_set_by_name(led_cdev,
185 mult_ctrl_trigger[TRIG_DEF_ON]);
186 else
187 led_set_brightness_async(led_cdev,
188 led_cdev->brightness);
189 }
190
191 if (pre_black_bitmap == 0) {
192 if ((~(leds_breath_data.background_bitmap |
193 leds_breath_data.breath_bitmap)) &
194 bits(bit))
195 led_trigger_remove(led_cdev);
196 }
197 bit++;
198 }
199 pre_brightness = (pre_brightness + bri_every_step) % LED_FULL;
200 pre_bg_bitmap = leds_breath_data.background_bitmap;
201 pre_br_bitmap = leds_breath_data.breath_bitmap;
202 pre_black_bitmap = ~(pre_bg_bitmap | pre_br_bitmap);
203 leds_pre_mode = LEDS_MODE_MULTI_BREATH;
204 up_read(&multi_leds_list_lock);
205
206 schedule_delayed_work(&multi_ctrl_breath_info->breath_work,
207 msecs_to_jiffies(leds_breath_data.change_delay_ms));
208 }
209
multi_ctrl_delay_trig_func(struct work_struct * ws)210 static void multi_ctrl_delay_trig_func(struct work_struct *ws)
211 {
212 struct multi_ctrl_data *ctrl_data =
213 container_of(ws, struct multi_ctrl_data, delay_trig_work.work);
214 struct led_classdev *led_cdev = ctrl_data->led_cdev;
215 struct led_ctrl_data *led_data = ctrl_data->data;
216
217 /* set brightness*/
218 if (led_data->brightness == LED_OFF) {
219 led_trigger_remove(led_cdev);
220 return;
221 }
222 /* set delay_on and delay_off */
223 led_cdev->blink_delay_off = led_data->delay_off;
224 led_cdev->blink_delay_on = led_data->delay_on;
225 led_cdev->brightness = led_data->brightness;
226
227 led_trigger_set_by_name(led_cdev, mult_ctrl_trigger[led_data->trigger]);
228
229 if (led_data->trigger == TRIG_ONESHOT)
230 led_blink_set_oneshot(led_cdev,
231 &led_cdev->blink_delay_on,
232 &led_cdev->blink_delay_off, 0);
233 }
234
multi_ctrl_set_led(struct multi_ctrl_data * ctrl_data)235 static int multi_ctrl_set_led(struct multi_ctrl_data *ctrl_data)
236 {
237 struct led_ctrl_data *led_data = ctrl_data->data;
238 struct led_classdev *led_cdev = ctrl_data->led_cdev;
239
240 if (!led_data || led_data->trigger >= TRIG_MAX)
241 return -EINVAL;
242
243 if (led_data->delayed_trigger_ms &&
244 (led_data->trigger == TRIG_TIMER ||
245 led_data->trigger == TRIG_ONESHOT)) {
246 schedule_delayed_work(&ctrl_data->delay_trig_work,
247 msecs_to_jiffies(led_data->delayed_trigger_ms));
248 } else {
249 /* set brightness*/
250 if (led_data->brightness == LED_OFF ||
251 led_data->trigger == TRIG_NONE) {
252 led_trigger_remove(led_cdev);
253 return 0;
254 }
255 /* set delay_on and delay_off */
256 led_cdev->blink_delay_off = led_data->delay_off;
257 led_cdev->blink_delay_on = led_data->delay_on;
258 led_cdev->brightness = led_data->brightness;
259
260 led_trigger_set_by_name(led_cdev,
261 mult_ctrl_trigger[led_data->trigger]);
262
263 if (led_data->trigger == TRIG_ONESHOT)
264 led_blink_set_oneshot(led_cdev,
265 &led_cdev->blink_delay_on,
266 &led_cdev->blink_delay_off, 0);
267 }
268
269 return 0;
270 }
271
cancel_all_work_sync(void)272 static void cancel_all_work_sync(void)
273 {
274 multi_ctrl_scroll_info->data_valid = false;
275 multi_ctrl_breath_info->data_valid = false;
276 cancel_delayed_work_sync(&multi_ctrl_scroll_info->scroll_work);
277 cancel_delayed_work_sync(&multi_ctrl_breath_info->breath_work);
278 }
279
multi_ctrl_open(struct inode * inode,struct file * file)280 static int multi_ctrl_open(struct inode *inode, struct file *file)
281 {
282 return 0;
283 }
284
multi_ctrl_release(struct inode * inode,struct file * file)285 static int multi_ctrl_release(struct inode *inode, struct file *file)
286 {
287 return 0;
288 }
289
multi_ctrl_ioctl(struct file * file,unsigned int cmd,unsigned long arg)290 static long multi_ctrl_ioctl(struct file *file,
291 unsigned int cmd, unsigned long arg)
292 {
293 int i = 0;
294 int ret = 0;
295
296 cancel_all_work_sync();
297
298 switch (cmd) {
299 case LEDS_MULTI_CTRL_IOCTL_MULTI_SET:
300 {
301 struct led_ctrl_data *argp = (struct led_ctrl_data *)arg;
302 struct multi_ctrl_data *ctrl_data;
303 bool mode_changed = false;
304
305 down_read(&multi_leds_list_lock);
306
307 if (leds_pre_mode != LEDS_MODE_MULTI_SET) {
308 leds_pre_mode = LEDS_MODE_MULTI_SET;
309 mode_changed = true;
310 }
311
312 if (copy_from_user(leds_data, argp,
313 sizeof(struct led_ctrl_data) * led_num)) {
314 pr_err("%s, copy from user failed\n", __func__);
315 up_read(&multi_leds_list_lock);
316 ret = -EFAULT;
317 break;
318 }
319 list_for_each_entry(ctrl_data, &multi_leds_list, node) {
320 struct led_classdev *led_cdev = ctrl_data->led_cdev;
321
322 cancel_delayed_work_sync(&ctrl_data->delay_trig_work);
323
324 if (i >= led_num) {
325 dev_err(led_cdev->dev, "exceed the max number of muti_leds_list\n");
326 break;
327 }
328 ctrl_data->data = &leds_data[i++];
329 if (!memcmp(&ctrl_data->old_data, ctrl_data->data,
330 sizeof(struct led_ctrl_data)) &&
331 !mode_changed) {
332 continue;
333 }
334 multi_ctrl_set_led(ctrl_data);
335 memcpy(&ctrl_data->old_data, ctrl_data->data,
336 sizeof(struct led_ctrl_data));
337 }
338 up_read(&multi_leds_list_lock);
339 break;
340 }
341 case LEDS_MULTI_CTRL_IOCTL_GET_LED_NUMBER:
342 {
343 int __user *p = (int __user *)arg;
344
345 ret = put_user(led_num, p);
346 break;
347 }
348 case LEDS_MULTI_CTRL_IOCTL_MULTI_SET_SCROLL:
349 {
350 if (led_num > sizeof(leds_scroll_data.init_bitmap) * 8) {
351 pr_err("registered leds is exeeded the size of scroll bitmap!\n");
352 ret = -EINVAL;
353 break;
354 }
355
356 if (copy_from_user(&leds_scroll_data,
357 (struct led_ctrl_scroll_data *)arg,
358 sizeof(struct led_ctrl_scroll_data))) {
359 pr_err("%s, set scroll mode, copy from user failed\n",
360 __func__);
361 ret = -EFAULT;
362 break;
363 }
364 schedule_delayed_work(&multi_ctrl_scroll_info->scroll_work, 0);
365 break;
366 }
367 case LEDS_MULTI_CTRL_IOCTL_MULTI_SET_BREATH:
368 {
369 if (led_num > sizeof(leds_breath_data.breath_bitmap) * 8) {
370 pr_err("registered leds is exeeded the size of breath bitmap!\n");
371 ret = -EINVAL;
372 break;
373 }
374
375 /*
376 * background color bitmap will be set to default-on mode,
377 * so we can't set a bit in background bits if it's one of
378 * bit in breath color bitmap.
379 */
380 leds_breath_data.background_bitmap &=
381 ~leds_breath_data.breath_bitmap;
382 if (copy_from_user(&leds_breath_data,
383 (struct led_ctrl_breath_data *)arg,
384 sizeof(leds_breath_data))) {
385 pr_err("%s, set breath mode, copy from user failed\n",
386 __func__);
387 ret = -EFAULT;
388 break;
389 }
390 schedule_delayed_work(&multi_ctrl_breath_info->breath_work, 0);
391 break;
392 }
393 default:
394 break;
395 }
396
397 return ret;
398 }
399
400 static const struct file_operations multi_ctrl_ops = {
401 .owner = THIS_MODULE,
402 .open = multi_ctrl_open,
403 .release = multi_ctrl_release,
404 .unlocked_ioctl = multi_ctrl_ioctl,
405 #ifdef CONFIG_COMPAT
406 .compat_ioctl = multi_ctrl_ioctl,
407 #endif
408 };
409
led_multi_control_register(struct led_classdev * led_cdev)410 int led_multi_control_register(struct led_classdev *led_cdev)
411 {
412 struct multi_ctrl_data *data;
413
414 if (led_num++ >= MAX_LEDS_NUMBER)
415 return -EINVAL;
416
417 data = kzalloc(sizeof(*data), GFP_KERNEL);
418 if (!data) {
419 dev_err(led_cdev->dev, "malloc multi_ctrl_data failed\n");
420 return -ENOMEM;
421 }
422
423 data->led_cdev = led_cdev;
424 data->old_data.brightness = led_cdev->brightness;
425 data->old_data.delay_off = led_cdev->blink_delay_off;
426 data->old_data.delay_on = led_cdev->blink_delay_on;
427
428 down_write(&multi_leds_list_lock);
429 list_add_tail(&data->node, &multi_leds_list);
430 up_write(&multi_leds_list_lock);
431
432 INIT_DELAYED_WORK(&data->delay_trig_work,
433 multi_ctrl_delay_trig_func);
434
435 return 0;
436 }
437
led_multi_control_unregister(struct led_classdev * cdev)438 int led_multi_control_unregister(struct led_classdev *cdev)
439 {
440 struct multi_ctrl_data *ctrl_data;
441
442 if (led_num-- < 0)
443 return -EINVAL;
444
445 down_write(&multi_leds_list_lock);
446 list_for_each_entry(ctrl_data, &multi_leds_list, node) {
447 if (ctrl_data->led_cdev == cdev) {
448 cancel_delayed_work_sync(&ctrl_data->delay_trig_work);
449 list_del(&ctrl_data->node);
450 break;
451 }
452 }
453 up_write(&multi_leds_list_lock);
454 kfree(ctrl_data);
455
456 return 0;
457 }
458
led_multi_control_init(struct device * dev)459 int led_multi_control_init(struct device *dev)
460 {
461 int ret;
462
463 multi_ctrl_scroll_info =
464 kzalloc(sizeof(*multi_ctrl_scroll_info), GFP_KERNEL);
465 if (!multi_ctrl_scroll_info) {
466 pr_err("malloc multi_ctrl_scroll_info failed\n");
467 return -ENOMEM;
468 }
469 multi_ctrl_breath_info =
470 kzalloc(sizeof(*multi_ctrl_breath_info), GFP_KERNEL);
471 if (!multi_ctrl_breath_info) {
472 pr_err("malloc leds_breath_info failed\n");
473 ret = -ENOMEM;
474 goto breath_info_malloc_err;
475 }
476
477 multi_ctrl_miscdev.fops = &multi_ctrl_ops;
478 multi_ctrl_miscdev.parent = dev;
479 multi_ctrl_miscdev.name = "led_multi_ctrl";
480 ret = misc_register(&multi_ctrl_miscdev);
481 if (ret < 0) {
482 pr_err("Can't register misc dev for led multi-control.\n");
483 goto miscdev_register_err;
484 }
485 led_num = 0;
486
487 multi_ctrl_scroll_info->data = &leds_scroll_data;
488 INIT_DELAYED_WORK(&multi_ctrl_scroll_info->scroll_work,
489 multi_ctrl_scroll_work_fn);
490 multi_ctrl_breath_info->data = &leds_breath_data;
491 INIT_DELAYED_WORK(&multi_ctrl_breath_info->breath_work,
492 multi_ctrl_breath_work_fn);
493
494 return 0;
495
496 miscdev_register_err:
497 kfree(multi_ctrl_breath_info);
498 multi_ctrl_breath_info = NULL;
499
500 breath_info_malloc_err:
501 kfree(multi_ctrl_scroll_info);
502 multi_ctrl_scroll_info = NULL;
503
504 return ret;
505 }
506
led_multi_control_exit(struct device * dev)507 int led_multi_control_exit(struct device *dev)
508 {
509 misc_deregister(&multi_ctrl_miscdev);
510 cancel_delayed_work_sync(&multi_ctrl_scroll_info->scroll_work);
511 cancel_delayed_work_sync(&multi_ctrl_breath_info->breath_work);
512 if (multi_ctrl_scroll_info) {
513 kfree(multi_ctrl_scroll_info);
514 multi_ctrl_scroll_info = NULL;
515 }
516 if (multi_ctrl_breath_info) {
517 kfree(multi_ctrl_breath_info);
518 multi_ctrl_breath_info = NULL;
519 }
520
521 return 0;
522 }
523
524 MODULE_AUTHOR("Allen.zhang <zwp@rock-chips.com>");
525 MODULE_DESCRIPTION("Multi-Contorl LED trigger");
526 MODULE_LICENSE("GPL");
527