Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * LED Class Core
0004  *
0005  * Copyright 2005-2006 Openedhand Ltd.
0006  *
0007  * Author: Richard Purdie <rpurdie@openedhand.com>
0008  */
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/leds.h>
0012 #include <linux/list.h>
0013 #include <linux/module.h>
0014 #include <linux/mutex.h>
0015 #include <linux/of.h>
0016 #include <linux/property.h>
0017 #include <linux/rwsem.h>
0018 #include <linux/slab.h>
0019 #include <uapi/linux/uleds.h>
0020 #include "leds.h"
0021 
0022 DECLARE_RWSEM(leds_list_lock);
0023 EXPORT_SYMBOL_GPL(leds_list_lock);
0024 
0025 LIST_HEAD(leds_list);
0026 EXPORT_SYMBOL_GPL(leds_list);
0027 
0028 const char * const led_colors[LED_COLOR_ID_MAX] = {
0029     [LED_COLOR_ID_WHITE] = "white",
0030     [LED_COLOR_ID_RED] = "red",
0031     [LED_COLOR_ID_GREEN] = "green",
0032     [LED_COLOR_ID_BLUE] = "blue",
0033     [LED_COLOR_ID_AMBER] = "amber",
0034     [LED_COLOR_ID_VIOLET] = "violet",
0035     [LED_COLOR_ID_YELLOW] = "yellow",
0036     [LED_COLOR_ID_IR] = "ir",
0037     [LED_COLOR_ID_MULTI] = "multicolor",
0038     [LED_COLOR_ID_RGB] = "rgb",
0039 };
0040 EXPORT_SYMBOL_GPL(led_colors);
0041 
0042 static int __led_set_brightness(struct led_classdev *led_cdev, unsigned int value)
0043 {
0044     if (!led_cdev->brightness_set)
0045         return -ENOTSUPP;
0046 
0047     led_cdev->brightness_set(led_cdev, value);
0048 
0049     return 0;
0050 }
0051 
0052 static int __led_set_brightness_blocking(struct led_classdev *led_cdev, unsigned int value)
0053 {
0054     if (!led_cdev->brightness_set_blocking)
0055         return -ENOTSUPP;
0056 
0057     return led_cdev->brightness_set_blocking(led_cdev, value);
0058 }
0059 
0060 static void led_timer_function(struct timer_list *t)
0061 {
0062     struct led_classdev *led_cdev = from_timer(led_cdev, t, blink_timer);
0063     unsigned long brightness;
0064     unsigned long delay;
0065 
0066     if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
0067         led_set_brightness_nosleep(led_cdev, LED_OFF);
0068         clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
0069         return;
0070     }
0071 
0072     if (test_and_clear_bit(LED_BLINK_ONESHOT_STOP,
0073                    &led_cdev->work_flags)) {
0074         clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
0075         return;
0076     }
0077 
0078     brightness = led_get_brightness(led_cdev);
0079     if (!brightness) {
0080         /* Time to switch the LED on. */
0081         if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE,
0082                     &led_cdev->work_flags))
0083             brightness = led_cdev->new_blink_brightness;
0084         else
0085             brightness = led_cdev->blink_brightness;
0086         delay = led_cdev->blink_delay_on;
0087     } else {
0088         /* Store the current brightness value to be able
0089          * to restore it when the delay_off period is over.
0090          */
0091         led_cdev->blink_brightness = brightness;
0092         brightness = LED_OFF;
0093         delay = led_cdev->blink_delay_off;
0094     }
0095 
0096     led_set_brightness_nosleep(led_cdev, brightness);
0097 
0098     /* Return in next iteration if led is in one-shot mode and we are in
0099      * the final blink state so that the led is toggled each delay_on +
0100      * delay_off milliseconds in worst case.
0101      */
0102     if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags)) {
0103         if (test_bit(LED_BLINK_INVERT, &led_cdev->work_flags)) {
0104             if (brightness)
0105                 set_bit(LED_BLINK_ONESHOT_STOP,
0106                     &led_cdev->work_flags);
0107         } else {
0108             if (!brightness)
0109                 set_bit(LED_BLINK_ONESHOT_STOP,
0110                     &led_cdev->work_flags);
0111         }
0112     }
0113 
0114     mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
0115 }
0116 
0117 static void set_brightness_delayed(struct work_struct *ws)
0118 {
0119     struct led_classdev *led_cdev =
0120         container_of(ws, struct led_classdev, set_brightness_work);
0121     int ret = 0;
0122 
0123     if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
0124         led_cdev->delayed_set_value = LED_OFF;
0125         led_stop_software_blink(led_cdev);
0126     }
0127 
0128     ret = __led_set_brightness(led_cdev, led_cdev->delayed_set_value);
0129     if (ret == -ENOTSUPP)
0130         ret = __led_set_brightness_blocking(led_cdev,
0131                     led_cdev->delayed_set_value);
0132     if (ret < 0 &&
0133         /* LED HW might have been unplugged, therefore don't warn */
0134         !(ret == -ENODEV && (led_cdev->flags & LED_UNREGISTERING) &&
0135         (led_cdev->flags & LED_HW_PLUGGABLE)))
0136         dev_err(led_cdev->dev,
0137             "Setting an LED's brightness failed (%d)\n", ret);
0138 }
0139 
0140 static void led_set_software_blink(struct led_classdev *led_cdev,
0141                    unsigned long delay_on,
0142                    unsigned long delay_off)
0143 {
0144     int current_brightness;
0145 
0146     current_brightness = led_get_brightness(led_cdev);
0147     if (current_brightness)
0148         led_cdev->blink_brightness = current_brightness;
0149     if (!led_cdev->blink_brightness)
0150         led_cdev->blink_brightness = led_cdev->max_brightness;
0151 
0152     led_cdev->blink_delay_on = delay_on;
0153     led_cdev->blink_delay_off = delay_off;
0154 
0155     /* never on - just set to off */
0156     if (!delay_on) {
0157         led_set_brightness_nosleep(led_cdev, LED_OFF);
0158         return;
0159     }
0160 
0161     /* never off - just set to brightness */
0162     if (!delay_off) {
0163         led_set_brightness_nosleep(led_cdev,
0164                        led_cdev->blink_brightness);
0165         return;
0166     }
0167 
0168     set_bit(LED_BLINK_SW, &led_cdev->work_flags);
0169     mod_timer(&led_cdev->blink_timer, jiffies + 1);
0170 }
0171 
0172 
0173 static void led_blink_setup(struct led_classdev *led_cdev,
0174              unsigned long *delay_on,
0175              unsigned long *delay_off)
0176 {
0177     if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
0178         led_cdev->blink_set &&
0179         !led_cdev->blink_set(led_cdev, delay_on, delay_off))
0180         return;
0181 
0182     /* blink with 1 Hz as default if nothing specified */
0183     if (!*delay_on && !*delay_off)
0184         *delay_on = *delay_off = 500;
0185 
0186     led_set_software_blink(led_cdev, *delay_on, *delay_off);
0187 }
0188 
0189 void led_init_core(struct led_classdev *led_cdev)
0190 {
0191     INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);
0192 
0193     timer_setup(&led_cdev->blink_timer, led_timer_function, 0);
0194 }
0195 EXPORT_SYMBOL_GPL(led_init_core);
0196 
0197 void led_blink_set(struct led_classdev *led_cdev,
0198            unsigned long *delay_on,
0199            unsigned long *delay_off)
0200 {
0201     del_timer_sync(&led_cdev->blink_timer);
0202 
0203     clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
0204     clear_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
0205     clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
0206 
0207     led_blink_setup(led_cdev, delay_on, delay_off);
0208 }
0209 EXPORT_SYMBOL_GPL(led_blink_set);
0210 
0211 void led_blink_set_oneshot(struct led_classdev *led_cdev,
0212                unsigned long *delay_on,
0213                unsigned long *delay_off,
0214                int invert)
0215 {
0216     if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
0217          timer_pending(&led_cdev->blink_timer))
0218         return;
0219 
0220     set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
0221     clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
0222 
0223     if (invert)
0224         set_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
0225     else
0226         clear_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
0227 
0228     led_blink_setup(led_cdev, delay_on, delay_off);
0229 }
0230 EXPORT_SYMBOL_GPL(led_blink_set_oneshot);
0231 
0232 void led_stop_software_blink(struct led_classdev *led_cdev)
0233 {
0234     del_timer_sync(&led_cdev->blink_timer);
0235     led_cdev->blink_delay_on = 0;
0236     led_cdev->blink_delay_off = 0;
0237     clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
0238 }
0239 EXPORT_SYMBOL_GPL(led_stop_software_blink);
0240 
0241 void led_set_brightness(struct led_classdev *led_cdev, unsigned int brightness)
0242 {
0243     /*
0244      * If software blink is active, delay brightness setting
0245      * until the next timer tick.
0246      */
0247     if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
0248         /*
0249          * If we need to disable soft blinking delegate this to the
0250          * work queue task to avoid problems in case we are called
0251          * from hard irq context.
0252          */
0253         if (!brightness) {
0254             set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
0255             schedule_work(&led_cdev->set_brightness_work);
0256         } else {
0257             set_bit(LED_BLINK_BRIGHTNESS_CHANGE,
0258                 &led_cdev->work_flags);
0259             led_cdev->new_blink_brightness = brightness;
0260         }
0261         return;
0262     }
0263 
0264     led_set_brightness_nosleep(led_cdev, brightness);
0265 }
0266 EXPORT_SYMBOL_GPL(led_set_brightness);
0267 
0268 void led_set_brightness_nopm(struct led_classdev *led_cdev, unsigned int value)
0269 {
0270     /* Use brightness_set op if available, it is guaranteed not to sleep */
0271     if (!__led_set_brightness(led_cdev, value))
0272         return;
0273 
0274     /* If brightness setting can sleep, delegate it to a work queue task */
0275     led_cdev->delayed_set_value = value;
0276     schedule_work(&led_cdev->set_brightness_work);
0277 }
0278 EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
0279 
0280 void led_set_brightness_nosleep(struct led_classdev *led_cdev, unsigned int value)
0281 {
0282     led_cdev->brightness = min(value, led_cdev->max_brightness);
0283 
0284     if (led_cdev->flags & LED_SUSPENDED)
0285         return;
0286 
0287     led_set_brightness_nopm(led_cdev, led_cdev->brightness);
0288 }
0289 EXPORT_SYMBOL_GPL(led_set_brightness_nosleep);
0290 
0291 int led_set_brightness_sync(struct led_classdev *led_cdev, unsigned int value)
0292 {
0293     if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
0294         return -EBUSY;
0295 
0296     led_cdev->brightness = min(value, led_cdev->max_brightness);
0297 
0298     if (led_cdev->flags & LED_SUSPENDED)
0299         return 0;
0300 
0301     return __led_set_brightness_blocking(led_cdev, led_cdev->brightness);
0302 }
0303 EXPORT_SYMBOL_GPL(led_set_brightness_sync);
0304 
0305 int led_update_brightness(struct led_classdev *led_cdev)
0306 {
0307     int ret = 0;
0308 
0309     if (led_cdev->brightness_get) {
0310         ret = led_cdev->brightness_get(led_cdev);
0311         if (ret >= 0) {
0312             led_cdev->brightness = ret;
0313             return 0;
0314         }
0315     }
0316 
0317     return ret;
0318 }
0319 EXPORT_SYMBOL_GPL(led_update_brightness);
0320 
0321 u32 *led_get_default_pattern(struct led_classdev *led_cdev, unsigned int *size)
0322 {
0323     struct fwnode_handle *fwnode = led_cdev->dev->fwnode;
0324     u32 *pattern;
0325     int count;
0326 
0327     count = fwnode_property_count_u32(fwnode, "led-pattern");
0328     if (count < 0)
0329         return NULL;
0330 
0331     pattern = kcalloc(count, sizeof(*pattern), GFP_KERNEL);
0332     if (!pattern)
0333         return NULL;
0334 
0335     if (fwnode_property_read_u32_array(fwnode, "led-pattern", pattern, count)) {
0336         kfree(pattern);
0337         return NULL;
0338     }
0339 
0340     *size = count;
0341 
0342     return pattern;
0343 }
0344 EXPORT_SYMBOL_GPL(led_get_default_pattern);
0345 
0346 /* Caller must ensure led_cdev->led_access held */
0347 void led_sysfs_disable(struct led_classdev *led_cdev)
0348 {
0349     lockdep_assert_held(&led_cdev->led_access);
0350 
0351     led_cdev->flags |= LED_SYSFS_DISABLE;
0352 }
0353 EXPORT_SYMBOL_GPL(led_sysfs_disable);
0354 
0355 /* Caller must ensure led_cdev->led_access held */
0356 void led_sysfs_enable(struct led_classdev *led_cdev)
0357 {
0358     lockdep_assert_held(&led_cdev->led_access);
0359 
0360     led_cdev->flags &= ~LED_SYSFS_DISABLE;
0361 }
0362 EXPORT_SYMBOL_GPL(led_sysfs_enable);
0363 
0364 static void led_parse_fwnode_props(struct device *dev,
0365                    struct fwnode_handle *fwnode,
0366                    struct led_properties *props)
0367 {
0368     int ret;
0369 
0370     if (!fwnode)
0371         return;
0372 
0373     if (fwnode_property_present(fwnode, "label")) {
0374         ret = fwnode_property_read_string(fwnode, "label", &props->label);
0375         if (ret)
0376             dev_err(dev, "Error parsing 'label' property (%d)\n", ret);
0377         return;
0378     }
0379 
0380     if (fwnode_property_present(fwnode, "color")) {
0381         ret = fwnode_property_read_u32(fwnode, "color", &props->color);
0382         if (ret)
0383             dev_err(dev, "Error parsing 'color' property (%d)\n", ret);
0384         else if (props->color >= LED_COLOR_ID_MAX)
0385             dev_err(dev, "LED color identifier out of range\n");
0386         else
0387             props->color_present = true;
0388     }
0389 
0390 
0391     if (!fwnode_property_present(fwnode, "function"))
0392         return;
0393 
0394     ret = fwnode_property_read_string(fwnode, "function", &props->function);
0395     if (ret) {
0396         dev_err(dev,
0397             "Error parsing 'function' property (%d)\n",
0398             ret);
0399     }
0400 
0401     if (!fwnode_property_present(fwnode, "function-enumerator"))
0402         return;
0403 
0404     ret = fwnode_property_read_u32(fwnode, "function-enumerator",
0405                        &props->func_enum);
0406     if (ret) {
0407         dev_err(dev,
0408             "Error parsing 'function-enumerator' property (%d)\n",
0409             ret);
0410     } else {
0411         props->func_enum_present = true;
0412     }
0413 }
0414 
0415 int led_compose_name(struct device *dev, struct led_init_data *init_data,
0416              char *led_classdev_name)
0417 {
0418     struct led_properties props = {};
0419     struct fwnode_handle *fwnode = init_data->fwnode;
0420     const char *devicename = init_data->devicename;
0421 
0422     /* We want to label LEDs that can produce full range of colors
0423      * as RGB, not multicolor */
0424     BUG_ON(props.color == LED_COLOR_ID_MULTI);
0425 
0426     if (!led_classdev_name)
0427         return -EINVAL;
0428 
0429     led_parse_fwnode_props(dev, fwnode, &props);
0430 
0431     if (props.label) {
0432         /*
0433          * If init_data.devicename is NULL, then it indicates that
0434          * DT label should be used as-is for LED class device name.
0435          * Otherwise the label is prepended with devicename to compose
0436          * the final LED class device name.
0437          */
0438         if (!devicename) {
0439             strscpy(led_classdev_name, props.label,
0440                 LED_MAX_NAME_SIZE);
0441         } else {
0442             snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
0443                  devicename, props.label);
0444         }
0445     } else if (props.function || props.color_present) {
0446         char tmp_buf[LED_MAX_NAME_SIZE];
0447 
0448         if (props.func_enum_present) {
0449             snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-%d",
0450                  props.color_present ? led_colors[props.color] : "",
0451                  props.function ?: "", props.func_enum);
0452         } else {
0453             snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
0454                  props.color_present ? led_colors[props.color] : "",
0455                  props.function ?: "");
0456         }
0457         if (init_data->devname_mandatory) {
0458             snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
0459                  devicename, tmp_buf);
0460         } else {
0461             strscpy(led_classdev_name, tmp_buf, LED_MAX_NAME_SIZE);
0462 
0463         }
0464     } else if (init_data->default_label) {
0465         if (!devicename) {
0466             dev_err(dev, "Legacy LED naming requires devicename segment");
0467             return -EINVAL;
0468         }
0469         snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
0470              devicename, init_data->default_label);
0471     } else if (is_of_node(fwnode)) {
0472         strscpy(led_classdev_name, to_of_node(fwnode)->name,
0473             LED_MAX_NAME_SIZE);
0474     } else
0475         return -EINVAL;
0476 
0477     return 0;
0478 }
0479 EXPORT_SYMBOL_GPL(led_compose_name);
0480 
0481 enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode)
0482 {
0483     const char *state = NULL;
0484 
0485     if (!fwnode_property_read_string(fwnode, "default-state", &state)) {
0486         if (!strcmp(state, "keep"))
0487             return LEDS_DEFSTATE_KEEP;
0488         if (!strcmp(state, "on"))
0489             return LEDS_DEFSTATE_ON;
0490     }
0491 
0492     return LEDS_DEFSTATE_OFF;
0493 }
0494 EXPORT_SYMBOL_GPL(led_init_default_state_get);