Skip to content

Commit aa172ba

Browse files
m-kurbanovlag-linaro
authored andcommitted
leds: trigger: pattern: Add support for hrtimer
Currently, led pattern trigger uses timer_list to schedule brightness changing. As we know from timer_list API [1], it's not accurate to milliseconds and depends on HZ granularity. Example: "0 10 0 0 50 10 50 0 100 10 100 0 150 10 150 0 200 10 200 0 250 10 250 0", we expect it to be 60ms long, but it can actually be up to ~120ms (add ~10ms for each pattern when HZ == 100). But sometimes, userspace needs time accurate led patterns to make sure that pattern will be executed during expected time slot. To achieve this goal the patch introduces optional hrtimer usage for led trigger pattern, because hrtimer is microseconds accurate timer. [1]: kernel/time/timer.c#L104 Signed-off-by: Martin Kurbanov <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Lee Jones <[email protected]>
1 parent fd05e36 commit aa172ba

File tree

2 files changed

+113
-23
lines changed

2 files changed

+113
-23
lines changed

Documentation/ABI/testing/sysfs-class-led-trigger-pattern

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ Description:
1212
The exact format is described in:
1313
Documentation/devicetree/bindings/leds/leds-trigger-pattern.txt
1414

15+
What: /sys/class/leds/<led>/hr_pattern
16+
Date: April 2024
17+
Description:
18+
Specify a software pattern for the LED, that supports altering
19+
the brightness for the specified duration with one software
20+
timer. It can do gradual dimming and step change of brightness.
21+
22+
Unlike the /sys/class/leds/<led>/pattern, this attribute runs
23+
a pattern on high-resolution timer (hrtimer).
24+
1525
What: /sys/class/leds/<led>/hw_pattern
1626
Date: September 2018
1727
KernelVersion: 4.20

drivers/leds/trigger/ledtrig-pattern.c

Lines changed: 103 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/mutex.h>
1414
#include <linux/slab.h>
1515
#include <linux/timer.h>
16+
#include <linux/hrtimer.h>
1617

1718
#define MAX_PATTERNS 1024
1819
/*
@@ -21,6 +22,12 @@
2122
*/
2223
#define UPDATE_INTERVAL 50
2324

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+
2431
struct pattern_trig_data {
2532
struct led_classdev *led_cdev;
2633
struct led_pattern patterns[MAX_PATTERNS];
@@ -32,8 +39,9 @@ struct pattern_trig_data {
3239
int last_repeat;
3340
int delta_t;
3441
bool is_indefinite;
35-
bool is_hw_pattern;
42+
enum pattern_type type;
3643
struct timer_list timer;
44+
struct hrtimer hrtimer;
3745
};
3846

3947
static void pattern_trig_update_patterns(struct pattern_trig_data *data)
@@ -71,10 +79,35 @@ static int pattern_trig_compute_brightness(struct pattern_trig_data *data)
7179
return data->curr->brightness - step_brightness;
7280
}
7381

74-
static void pattern_trig_timer_function(struct timer_list *t)
82+
static void pattern_trig_timer_start(struct pattern_trig_data *data)
7583
{
76-
struct pattern_trig_data *data = from_timer(data, t, timer);
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+
}
7791

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+
{
78111
for (;;) {
79112
if (!data->is_indefinite && !data->repeat)
80113
break;
@@ -83,8 +116,7 @@ static void pattern_trig_timer_function(struct timer_list *t)
83116
/* Step change of brightness */
84117
led_set_brightness(data->led_cdev,
85118
data->curr->brightness);
86-
mod_timer(&data->timer,
87-
jiffies + msecs_to_jiffies(data->curr->delta_t));
119+
pattern_trig_timer_restart(data, data->curr->delta_t);
88120
if (!data->next->delta_t) {
89121
/* Skip the tuple with zero duration */
90122
pattern_trig_update_patterns(data);
@@ -106,8 +138,7 @@ static void pattern_trig_timer_function(struct timer_list *t)
106138

107139
led_set_brightness(data->led_cdev,
108140
pattern_trig_compute_brightness(data));
109-
mod_timer(&data->timer,
110-
jiffies + msecs_to_jiffies(UPDATE_INTERVAL));
141+
pattern_trig_timer_restart(data, UPDATE_INTERVAL);
111142

112143
/* Accumulate the gradual dimming time */
113144
data->delta_t += UPDATE_INTERVAL;
@@ -117,14 +148,33 @@ static void pattern_trig_timer_function(struct timer_list *t)
117148
}
118149
}
119150

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+
120170
static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
121171
{
122172
struct pattern_trig_data *data = led_cdev->trigger_data;
123173

124174
if (!data->npatterns)
125175
return 0;
126176

127-
if (data->is_hw_pattern) {
177+
if (data->type == PATTERN_TYPE_HW) {
128178
return led_cdev->pattern_set(led_cdev, data->patterns,
129179
data->npatterns, data->repeat);
130180
}
@@ -136,8 +186,7 @@ static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
136186
data->delta_t = 0;
137187
data->curr = data->patterns;
138188
data->next = data->patterns + 1;
139-
data->timer.expires = jiffies;
140-
add_timer(&data->timer);
189+
pattern_trig_timer_start(data);
141190

142191
return 0;
143192
}
@@ -175,9 +224,9 @@ static ssize_t repeat_store(struct device *dev, struct device_attribute *attr,
175224

176225
mutex_lock(&data->lock);
177226

178-
del_timer_sync(&data->timer);
227+
pattern_trig_timer_cancel(data);
179228

180-
if (data->is_hw_pattern)
229+
if (data->type == PATTERN_TYPE_HW)
181230
led_cdev->pattern_clear(led_cdev);
182231

183232
data->last_repeat = data->repeat = res;
@@ -196,14 +245,14 @@ static ssize_t repeat_store(struct device *dev, struct device_attribute *attr,
196245
static DEVICE_ATTR_RW(repeat);
197246

198247
static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data,
199-
char *buf, bool hw_pattern)
248+
char *buf, enum pattern_type type)
200249
{
201250
ssize_t count = 0;
202251
int i;
203252

204253
mutex_lock(&data->lock);
205254

206-
if (!data->npatterns || (data->is_hw_pattern ^ hw_pattern))
255+
if (!data->npatterns || data->type != type)
207256
goto out;
208257

209258
for (i = 0; i < data->npatterns; i++) {
@@ -260,19 +309,19 @@ static int pattern_trig_store_patterns_int(struct pattern_trig_data *data,
260309

261310
static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,
262311
const char *buf, const u32 *buf_int,
263-
size_t count, bool hw_pattern)
312+
size_t count, enum pattern_type type)
264313
{
265314
struct pattern_trig_data *data = led_cdev->trigger_data;
266315
int err = 0;
267316

268317
mutex_lock(&data->lock);
269318

270-
del_timer_sync(&data->timer);
319+
pattern_trig_timer_cancel(data);
271320

272-
if (data->is_hw_pattern)
321+
if (data->type == PATTERN_TYPE_HW)
273322
led_cdev->pattern_clear(led_cdev);
274323

275-
data->is_hw_pattern = hw_pattern;
324+
data->type = type;
276325
data->npatterns = 0;
277326

278327
if (buf)
@@ -297,15 +346,16 @@ static ssize_t pattern_show(struct device *dev, struct device_attribute *attr,
297346
struct led_classdev *led_cdev = dev_get_drvdata(dev);
298347
struct pattern_trig_data *data = led_cdev->trigger_data;
299348

300-
return pattern_trig_show_patterns(data, buf, false);
349+
return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_SW);
301350
}
302351

303352
static ssize_t pattern_store(struct device *dev, struct device_attribute *attr,
304353
const char *buf, size_t count)
305354
{
306355
struct led_classdev *led_cdev = dev_get_drvdata(dev);
307356

308-
return pattern_trig_store_patterns(led_cdev, buf, NULL, count, false);
357+
return pattern_trig_store_patterns(led_cdev, buf, NULL, count,
358+
PATTERN_TYPE_SW);
309359
}
310360

311361
static DEVICE_ATTR_RW(pattern);
@@ -316,7 +366,7 @@ static ssize_t hw_pattern_show(struct device *dev,
316366
struct led_classdev *led_cdev = dev_get_drvdata(dev);
317367
struct pattern_trig_data *data = led_cdev->trigger_data;
318368

319-
return pattern_trig_show_patterns(data, buf, true);
369+
return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_HW);
320370
}
321371

322372
static ssize_t hw_pattern_store(struct device *dev,
@@ -325,11 +375,33 @@ static ssize_t hw_pattern_store(struct device *dev,
325375
{
326376
struct led_classdev *led_cdev = dev_get_drvdata(dev);
327377

328-
return pattern_trig_store_patterns(led_cdev, buf, NULL, count, true);
378+
return pattern_trig_store_patterns(led_cdev, buf, NULL, count,
379+
PATTERN_TYPE_HW);
329380
}
330381

331382
static DEVICE_ATTR_RW(hw_pattern);
332383

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+
333405
static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
334406
struct attribute *attr, int index)
335407
{
@@ -338,6 +410,8 @@ static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
338410

339411
if (attr == &dev_attr_repeat.attr || attr == &dev_attr_pattern.attr)
340412
return attr->mode;
413+
else if (attr == &dev_attr_hr_pattern.attr)
414+
return attr->mode;
341415
else if (attr == &dev_attr_hw_pattern.attr && led_cdev->pattern_set)
342416
return attr->mode;
343417

@@ -347,6 +421,7 @@ static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
347421
static struct attribute *pattern_trig_attrs[] = {
348422
&dev_attr_pattern.attr,
349423
&dev_attr_hw_pattern.attr,
424+
&dev_attr_hr_pattern.attr,
350425
&dev_attr_repeat.attr,
351426
NULL
352427
};
@@ -376,7 +451,8 @@ static void pattern_init(struct led_classdev *led_cdev)
376451
goto out;
377452
}
378453

379-
err = pattern_trig_store_patterns(led_cdev, NULL, pattern, size, false);
454+
err = pattern_trig_store_patterns(led_cdev, NULL, pattern, size,
455+
PATTERN_TYPE_SW);
380456
if (err < 0)
381457
dev_warn(led_cdev->dev,
382458
"Pattern initialization failed with error %d\n", err);
@@ -400,12 +476,15 @@ static int pattern_trig_activate(struct led_classdev *led_cdev)
400476
led_cdev->pattern_clear = NULL;
401477
}
402478

479+
data->type = PATTERN_TYPE_SW;
403480
data->is_indefinite = true;
404481
data->last_repeat = -1;
405482
mutex_init(&data->lock);
406483
data->led_cdev = led_cdev;
407484
led_set_trigger_data(led_cdev, data);
408485
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;
409488
led_cdev->activated = true;
410489

411490
if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) {
@@ -431,6 +510,7 @@ static void pattern_trig_deactivate(struct led_classdev *led_cdev)
431510
led_cdev->pattern_clear(led_cdev);
432511

433512
timer_shutdown_sync(&data->timer);
513+
hrtimer_cancel(&data->hrtimer);
434514

435515
led_set_brightness(led_cdev, LED_OFF);
436516
kfree(data);

0 commit comments

Comments
 (0)