Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 /*
0004  * LED pattern trigger
0005  *
0006  * Idea discussed with Pavel Machek. Raphael Teysseyre implemented
0007  * the first version, Baolin Wang simplified and improved the approach.
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  * When doing gradual dimming, the led brightness will be updated
0020  * every 50 milliseconds.
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      * If current tuple's duration is less than the dimming interval,
0059      * we should treat it as a step change of brightness instead of
0060      * doing gradual dimming.
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             /* Step change of brightness */
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                 /* Skip the tuple with zero duration */
0090                 pattern_trig_update_patterns(data);
0091             }
0092             /* Select next tuple */
0093             pattern_trig_update_patterns(data);
0094         } else {
0095             /* Gradual dimming */
0096 
0097             /*
0098              * If the accumulation time is larger than current
0099              * tuple's duration, we should go next one and re-check
0100              * if we repeated done.
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             /* Accumulate the gradual dimming time */
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     /* At least 2 tuples for software pattern. */
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     /* Number 0 and negative numbers except -1 are invalid. */
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     /* -1 means repeat indefinitely */
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          * Mark as initialized even on pattern_init() error because
0415          * any consecutive call to it would produce the same error.
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");