0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/kernel.h>
0011 #include <linux/leds.h>
0012 #include <linux/module.h>
0013 #include <linux/mutex.h>
0014 #include <linux/slab.h>
0015 #include <linux/timer.h>
0016
0017 #define MAX_PATTERNS 1024
0018
0019
0020
0021
0022 #define UPDATE_INTERVAL 50
0023
0024 struct pattern_trig_data {
0025 struct led_classdev *led_cdev;
0026 struct led_pattern patterns[MAX_PATTERNS];
0027 struct led_pattern *curr;
0028 struct led_pattern *next;
0029 struct mutex lock;
0030 u32 npatterns;
0031 int repeat;
0032 int last_repeat;
0033 int delta_t;
0034 bool is_indefinite;
0035 bool is_hw_pattern;
0036 struct timer_list timer;
0037 };
0038
0039 static void pattern_trig_update_patterns(struct pattern_trig_data *data)
0040 {
0041 data->curr = data->next;
0042 if (!data->is_indefinite && data->curr == data->patterns)
0043 data->repeat--;
0044
0045 if (data->next == data->patterns + data->npatterns - 1)
0046 data->next = data->patterns;
0047 else
0048 data->next++;
0049
0050 data->delta_t = 0;
0051 }
0052
0053 static int pattern_trig_compute_brightness(struct pattern_trig_data *data)
0054 {
0055 int step_brightness;
0056
0057
0058
0059
0060
0061
0062 if (data->delta_t == 0 || data->curr->delta_t < UPDATE_INTERVAL)
0063 return data->curr->brightness;
0064
0065 step_brightness = abs(data->next->brightness - data->curr->brightness);
0066 step_brightness = data->delta_t * step_brightness / data->curr->delta_t;
0067
0068 if (data->next->brightness > data->curr->brightness)
0069 return data->curr->brightness + step_brightness;
0070 else
0071 return data->curr->brightness - step_brightness;
0072 }
0073
0074 static void pattern_trig_timer_function(struct timer_list *t)
0075 {
0076 struct pattern_trig_data *data = from_timer(data, t, timer);
0077
0078 for (;;) {
0079 if (!data->is_indefinite && !data->repeat)
0080 break;
0081
0082 if (data->curr->brightness == data->next->brightness) {
0083
0084 led_set_brightness(data->led_cdev,
0085 data->curr->brightness);
0086 mod_timer(&data->timer,
0087 jiffies + msecs_to_jiffies(data->curr->delta_t));
0088 if (!data->next->delta_t) {
0089
0090 pattern_trig_update_patterns(data);
0091 }
0092
0093 pattern_trig_update_patterns(data);
0094 } else {
0095
0096
0097
0098
0099
0100
0101
0102 if (data->delta_t > data->curr->delta_t) {
0103 pattern_trig_update_patterns(data);
0104 continue;
0105 }
0106
0107 led_set_brightness(data->led_cdev,
0108 pattern_trig_compute_brightness(data));
0109 mod_timer(&data->timer,
0110 jiffies + msecs_to_jiffies(UPDATE_INTERVAL));
0111
0112
0113 data->delta_t += UPDATE_INTERVAL;
0114 }
0115
0116 break;
0117 }
0118 }
0119
0120 static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
0121 {
0122 struct pattern_trig_data *data = led_cdev->trigger_data;
0123
0124 if (!data->npatterns)
0125 return 0;
0126
0127 if (data->is_hw_pattern) {
0128 return led_cdev->pattern_set(led_cdev, data->patterns,
0129 data->npatterns, data->repeat);
0130 }
0131
0132
0133 if (data->npatterns < 2)
0134 return -EINVAL;
0135
0136 data->delta_t = 0;
0137 data->curr = data->patterns;
0138 data->next = data->patterns + 1;
0139 data->timer.expires = jiffies;
0140 add_timer(&data->timer);
0141
0142 return 0;
0143 }
0144
0145 static ssize_t repeat_show(struct device *dev, struct device_attribute *attr,
0146 char *buf)
0147 {
0148 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0149 struct pattern_trig_data *data = led_cdev->trigger_data;
0150 int repeat;
0151
0152 mutex_lock(&data->lock);
0153
0154 repeat = data->last_repeat;
0155
0156 mutex_unlock(&data->lock);
0157
0158 return scnprintf(buf, PAGE_SIZE, "%d\n", repeat);
0159 }
0160
0161 static ssize_t repeat_store(struct device *dev, struct device_attribute *attr,
0162 const char *buf, size_t count)
0163 {
0164 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0165 struct pattern_trig_data *data = led_cdev->trigger_data;
0166 int err, res;
0167
0168 err = kstrtos32(buf, 10, &res);
0169 if (err)
0170 return err;
0171
0172
0173 if (res < -1 || res == 0)
0174 return -EINVAL;
0175
0176 mutex_lock(&data->lock);
0177
0178 del_timer_sync(&data->timer);
0179
0180 if (data->is_hw_pattern)
0181 led_cdev->pattern_clear(led_cdev);
0182
0183 data->last_repeat = data->repeat = res;
0184
0185 if (data->repeat == -1)
0186 data->is_indefinite = true;
0187 else
0188 data->is_indefinite = false;
0189
0190 err = pattern_trig_start_pattern(led_cdev);
0191
0192 mutex_unlock(&data->lock);
0193 return err < 0 ? err : count;
0194 }
0195
0196 static DEVICE_ATTR_RW(repeat);
0197
0198 static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data,
0199 char *buf, bool hw_pattern)
0200 {
0201 ssize_t count = 0;
0202 int i;
0203
0204 mutex_lock(&data->lock);
0205
0206 if (!data->npatterns || (data->is_hw_pattern ^ hw_pattern))
0207 goto out;
0208
0209 for (i = 0; i < data->npatterns; i++) {
0210 count += scnprintf(buf + count, PAGE_SIZE - count,
0211 "%d %u ",
0212 data->patterns[i].brightness,
0213 data->patterns[i].delta_t);
0214 }
0215
0216 buf[count - 1] = '\n';
0217
0218 out:
0219 mutex_unlock(&data->lock);
0220 return count;
0221 }
0222
0223 static int pattern_trig_store_patterns_string(struct pattern_trig_data *data,
0224 const char *buf, size_t count)
0225 {
0226 int ccount, cr, offset = 0;
0227
0228 while (offset < count - 1 && data->npatterns < MAX_PATTERNS) {
0229 cr = 0;
0230 ccount = sscanf(buf + offset, "%u %u %n",
0231 &data->patterns[data->npatterns].brightness,
0232 &data->patterns[data->npatterns].delta_t, &cr);
0233
0234 if (ccount != 2 ||
0235 data->patterns[data->npatterns].brightness > data->led_cdev->max_brightness) {
0236 data->npatterns = 0;
0237 return -EINVAL;
0238 }
0239
0240 offset += cr;
0241 data->npatterns++;
0242 }
0243
0244 return 0;
0245 }
0246
0247 static int pattern_trig_store_patterns_int(struct pattern_trig_data *data,
0248 const u32 *buf, size_t count)
0249 {
0250 unsigned int i;
0251
0252 for (i = 0; i < count; i += 2) {
0253 data->patterns[data->npatterns].brightness = buf[i];
0254 data->patterns[data->npatterns].delta_t = buf[i + 1];
0255 data->npatterns++;
0256 }
0257
0258 return 0;
0259 }
0260
0261 static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,
0262 const char *buf, const u32 *buf_int,
0263 size_t count, bool hw_pattern)
0264 {
0265 struct pattern_trig_data *data = led_cdev->trigger_data;
0266 int err = 0;
0267
0268 mutex_lock(&data->lock);
0269
0270 del_timer_sync(&data->timer);
0271
0272 if (data->is_hw_pattern)
0273 led_cdev->pattern_clear(led_cdev);
0274
0275 data->is_hw_pattern = hw_pattern;
0276 data->npatterns = 0;
0277
0278 if (buf)
0279 err = pattern_trig_store_patterns_string(data, buf, count);
0280 else
0281 err = pattern_trig_store_patterns_int(data, buf_int, count);
0282 if (err)
0283 goto out;
0284
0285 err = pattern_trig_start_pattern(led_cdev);
0286 if (err)
0287 data->npatterns = 0;
0288
0289 out:
0290 mutex_unlock(&data->lock);
0291 return err < 0 ? err : count;
0292 }
0293
0294 static ssize_t pattern_show(struct device *dev, struct device_attribute *attr,
0295 char *buf)
0296 {
0297 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0298 struct pattern_trig_data *data = led_cdev->trigger_data;
0299
0300 return pattern_trig_show_patterns(data, buf, false);
0301 }
0302
0303 static ssize_t pattern_store(struct device *dev, struct device_attribute *attr,
0304 const char *buf, size_t count)
0305 {
0306 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0307
0308 return pattern_trig_store_patterns(led_cdev, buf, NULL, count, false);
0309 }
0310
0311 static DEVICE_ATTR_RW(pattern);
0312
0313 static ssize_t hw_pattern_show(struct device *dev,
0314 struct device_attribute *attr, char *buf)
0315 {
0316 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0317 struct pattern_trig_data *data = led_cdev->trigger_data;
0318
0319 return pattern_trig_show_patterns(data, buf, true);
0320 }
0321
0322 static ssize_t hw_pattern_store(struct device *dev,
0323 struct device_attribute *attr,
0324 const char *buf, size_t count)
0325 {
0326 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0327
0328 return pattern_trig_store_patterns(led_cdev, buf, NULL, count, true);
0329 }
0330
0331 static DEVICE_ATTR_RW(hw_pattern);
0332
0333 static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
0334 struct attribute *attr, int index)
0335 {
0336 struct device *dev = kobj_to_dev(kobj);
0337 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0338
0339 if (attr == &dev_attr_repeat.attr || attr == &dev_attr_pattern.attr)
0340 return attr->mode;
0341 else if (attr == &dev_attr_hw_pattern.attr && led_cdev->pattern_set)
0342 return attr->mode;
0343
0344 return 0;
0345 }
0346
0347 static struct attribute *pattern_trig_attrs[] = {
0348 &dev_attr_pattern.attr,
0349 &dev_attr_hw_pattern.attr,
0350 &dev_attr_repeat.attr,
0351 NULL
0352 };
0353
0354 static const struct attribute_group pattern_trig_group = {
0355 .attrs = pattern_trig_attrs,
0356 .is_visible = pattern_trig_attrs_mode,
0357 };
0358
0359 static const struct attribute_group *pattern_trig_groups[] = {
0360 &pattern_trig_group,
0361 NULL,
0362 };
0363
0364 static void pattern_init(struct led_classdev *led_cdev)
0365 {
0366 unsigned int size = 0;
0367 u32 *pattern;
0368 int err;
0369
0370 pattern = led_get_default_pattern(led_cdev, &size);
0371 if (!pattern)
0372 return;
0373
0374 if (size % 2) {
0375 dev_warn(led_cdev->dev, "Expected pattern of tuples\n");
0376 goto out;
0377 }
0378
0379 err = pattern_trig_store_patterns(led_cdev, NULL, pattern, size, false);
0380 if (err < 0)
0381 dev_warn(led_cdev->dev,
0382 "Pattern initialization failed with error %d\n", err);
0383
0384 out:
0385 kfree(pattern);
0386 }
0387
0388 static int pattern_trig_activate(struct led_classdev *led_cdev)
0389 {
0390 struct pattern_trig_data *data;
0391
0392 data = kzalloc(sizeof(*data), GFP_KERNEL);
0393 if (!data)
0394 return -ENOMEM;
0395
0396 if (!!led_cdev->pattern_set ^ !!led_cdev->pattern_clear) {
0397 dev_warn(led_cdev->dev,
0398 "Hardware pattern ops validation failed\n");
0399 led_cdev->pattern_set = NULL;
0400 led_cdev->pattern_clear = NULL;
0401 }
0402
0403 data->is_indefinite = true;
0404 data->last_repeat = -1;
0405 mutex_init(&data->lock);
0406 data->led_cdev = led_cdev;
0407 led_set_trigger_data(led_cdev, data);
0408 timer_setup(&data->timer, pattern_trig_timer_function, 0);
0409 led_cdev->activated = true;
0410
0411 if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) {
0412 pattern_init(led_cdev);
0413
0414
0415
0416
0417 led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER;
0418 }
0419
0420 return 0;
0421 }
0422
0423 static void pattern_trig_deactivate(struct led_classdev *led_cdev)
0424 {
0425 struct pattern_trig_data *data = led_cdev->trigger_data;
0426
0427 if (!led_cdev->activated)
0428 return;
0429
0430 if (led_cdev->pattern_clear)
0431 led_cdev->pattern_clear(led_cdev);
0432
0433 del_timer_sync(&data->timer);
0434
0435 led_set_brightness(led_cdev, LED_OFF);
0436 kfree(data);
0437 led_cdev->activated = false;
0438 }
0439
0440 static struct led_trigger pattern_led_trigger = {
0441 .name = "pattern",
0442 .activate = pattern_trig_activate,
0443 .deactivate = pattern_trig_deactivate,
0444 .groups = pattern_trig_groups,
0445 };
0446
0447 static int __init pattern_trig_init(void)
0448 {
0449 return led_trigger_register(&pattern_led_trigger);
0450 }
0451
0452 static void __exit pattern_trig_exit(void)
0453 {
0454 led_trigger_unregister(&pattern_led_trigger);
0455 }
0456
0457 module_init(pattern_trig_init);
0458 module_exit(pattern_trig_exit);
0459
0460 MODULE_AUTHOR("Raphael Teysseyre <rteysseyre@gmail.com>");
0461 MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
0462 MODULE_DESCRIPTION("LED Pattern trigger");
0463 MODULE_LICENSE("GPL v2");