0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/ctype.h>
0010 #include <linux/device.h>
0011 #include <linux/err.h>
0012 #include <linux/init.h>
0013 #include <linux/kernel.h>
0014 #include <linux/leds.h>
0015 #include <linux/list.h>
0016 #include <linux/module.h>
0017 #include <linux/property.h>
0018 #include <linux/slab.h>
0019 #include <linux/spinlock.h>
0020 #include <linux/timer.h>
0021 #include <uapi/linux/uleds.h>
0022 #include <linux/of.h>
0023 #include "leds.h"
0024
0025 static struct class *leds_class;
0026
0027 static ssize_t brightness_show(struct device *dev,
0028 struct device_attribute *attr, char *buf)
0029 {
0030 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0031
0032
0033 led_update_brightness(led_cdev);
0034
0035 return sprintf(buf, "%u\n", led_cdev->brightness);
0036 }
0037
0038 static ssize_t brightness_store(struct device *dev,
0039 struct device_attribute *attr, const char *buf, size_t size)
0040 {
0041 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0042 unsigned long state;
0043 ssize_t ret;
0044
0045 mutex_lock(&led_cdev->led_access);
0046
0047 if (led_sysfs_is_disabled(led_cdev)) {
0048 ret = -EBUSY;
0049 goto unlock;
0050 }
0051
0052 ret = kstrtoul(buf, 10, &state);
0053 if (ret)
0054 goto unlock;
0055
0056 if (state == LED_OFF)
0057 led_trigger_remove(led_cdev);
0058 led_set_brightness(led_cdev, state);
0059 flush_work(&led_cdev->set_brightness_work);
0060
0061 ret = size;
0062 unlock:
0063 mutex_unlock(&led_cdev->led_access);
0064 return ret;
0065 }
0066 static DEVICE_ATTR_RW(brightness);
0067
0068 static ssize_t max_brightness_show(struct device *dev,
0069 struct device_attribute *attr, char *buf)
0070 {
0071 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0072
0073 return sprintf(buf, "%u\n", led_cdev->max_brightness);
0074 }
0075 static DEVICE_ATTR_RO(max_brightness);
0076
0077 #ifdef CONFIG_LEDS_TRIGGERS
0078 static BIN_ATTR(trigger, 0644, led_trigger_read, led_trigger_write, 0);
0079 static struct bin_attribute *led_trigger_bin_attrs[] = {
0080 &bin_attr_trigger,
0081 NULL,
0082 };
0083 static const struct attribute_group led_trigger_group = {
0084 .bin_attrs = led_trigger_bin_attrs,
0085 };
0086 #endif
0087
0088 static struct attribute *led_class_attrs[] = {
0089 &dev_attr_brightness.attr,
0090 &dev_attr_max_brightness.attr,
0091 NULL,
0092 };
0093
0094 static const struct attribute_group led_group = {
0095 .attrs = led_class_attrs,
0096 };
0097
0098 static const struct attribute_group *led_groups[] = {
0099 &led_group,
0100 #ifdef CONFIG_LEDS_TRIGGERS
0101 &led_trigger_group,
0102 #endif
0103 NULL,
0104 };
0105
0106 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
0107 static ssize_t brightness_hw_changed_show(struct device *dev,
0108 struct device_attribute *attr, char *buf)
0109 {
0110 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0111
0112 if (led_cdev->brightness_hw_changed == -1)
0113 return -ENODATA;
0114
0115 return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed);
0116 }
0117
0118 static DEVICE_ATTR_RO(brightness_hw_changed);
0119
0120 static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
0121 {
0122 struct device *dev = led_cdev->dev;
0123 int ret;
0124
0125 ret = device_create_file(dev, &dev_attr_brightness_hw_changed);
0126 if (ret) {
0127 dev_err(dev, "Error creating brightness_hw_changed\n");
0128 return ret;
0129 }
0130
0131 led_cdev->brightness_hw_changed_kn =
0132 sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed");
0133 if (!led_cdev->brightness_hw_changed_kn) {
0134 dev_err(dev, "Error getting brightness_hw_changed kn\n");
0135 device_remove_file(dev, &dev_attr_brightness_hw_changed);
0136 return -ENXIO;
0137 }
0138
0139 return 0;
0140 }
0141
0142 static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
0143 {
0144 sysfs_put(led_cdev->brightness_hw_changed_kn);
0145 device_remove_file(led_cdev->dev, &dev_attr_brightness_hw_changed);
0146 }
0147
0148 void led_classdev_notify_brightness_hw_changed(struct led_classdev *led_cdev, unsigned int brightness)
0149 {
0150 if (WARN_ON(!led_cdev->brightness_hw_changed_kn))
0151 return;
0152
0153 led_cdev->brightness_hw_changed = brightness;
0154 sysfs_notify_dirent(led_cdev->brightness_hw_changed_kn);
0155 }
0156 EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed);
0157 #else
0158 static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
0159 {
0160 return 0;
0161 }
0162 static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
0163 {
0164 }
0165 #endif
0166
0167
0168
0169
0170
0171 void led_classdev_suspend(struct led_classdev *led_cdev)
0172 {
0173 led_cdev->flags |= LED_SUSPENDED;
0174 led_set_brightness_nopm(led_cdev, 0);
0175 flush_work(&led_cdev->set_brightness_work);
0176 }
0177 EXPORT_SYMBOL_GPL(led_classdev_suspend);
0178
0179
0180
0181
0182
0183 void led_classdev_resume(struct led_classdev *led_cdev)
0184 {
0185 led_set_brightness_nopm(led_cdev, led_cdev->brightness);
0186
0187 if (led_cdev->flash_resume)
0188 led_cdev->flash_resume(led_cdev);
0189
0190 led_cdev->flags &= ~LED_SUSPENDED;
0191 }
0192 EXPORT_SYMBOL_GPL(led_classdev_resume);
0193
0194 #ifdef CONFIG_PM_SLEEP
0195 static int led_suspend(struct device *dev)
0196 {
0197 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0198
0199 if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
0200 led_classdev_suspend(led_cdev);
0201
0202 return 0;
0203 }
0204
0205 static int led_resume(struct device *dev)
0206 {
0207 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0208
0209 if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
0210 led_classdev_resume(led_cdev);
0211
0212 return 0;
0213 }
0214 #endif
0215
0216 static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226 struct led_classdev *of_led_get(struct device_node *np, int index)
0227 {
0228 struct device *led_dev;
0229 struct led_classdev *led_cdev;
0230 struct device_node *led_node;
0231
0232 led_node = of_parse_phandle(np, "leds", index);
0233 if (!led_node)
0234 return ERR_PTR(-ENOENT);
0235
0236 led_dev = class_find_device_by_of_node(leds_class, led_node);
0237 of_node_put(led_node);
0238
0239 if (!led_dev)
0240 return ERR_PTR(-EPROBE_DEFER);
0241
0242 led_cdev = dev_get_drvdata(led_dev);
0243
0244 if (!try_module_get(led_cdev->dev->parent->driver->owner))
0245 return ERR_PTR(-ENODEV);
0246
0247 return led_cdev;
0248 }
0249 EXPORT_SYMBOL_GPL(of_led_get);
0250
0251
0252
0253
0254
0255 void led_put(struct led_classdev *led_cdev)
0256 {
0257 module_put(led_cdev->dev->parent->driver->owner);
0258 }
0259 EXPORT_SYMBOL_GPL(led_put);
0260
0261 static void devm_led_release(struct device *dev, void *res)
0262 {
0263 struct led_classdev **p = res;
0264
0265 led_put(*p);
0266 }
0267
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277
0278
0279 struct led_classdev *__must_check devm_of_led_get(struct device *dev,
0280 int index)
0281 {
0282 struct led_classdev *led;
0283 struct led_classdev **dr;
0284
0285 if (!dev)
0286 return ERR_PTR(-EINVAL);
0287
0288 led = of_led_get(dev->of_node, index);
0289 if (IS_ERR(led))
0290 return led;
0291
0292 dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *),
0293 GFP_KERNEL);
0294 if (!dr) {
0295 led_put(led);
0296 return ERR_PTR(-ENOMEM);
0297 }
0298
0299 *dr = led;
0300 devres_add(dev, dr);
0301
0302 return led;
0303 }
0304 EXPORT_SYMBOL_GPL(devm_of_led_get);
0305
0306 static int led_classdev_next_name(const char *init_name, char *name,
0307 size_t len)
0308 {
0309 unsigned int i = 0;
0310 int ret = 0;
0311 struct device *dev;
0312
0313 strlcpy(name, init_name, len);
0314
0315 while ((ret < len) &&
0316 (dev = class_find_device_by_name(leds_class, name))) {
0317 put_device(dev);
0318 ret = snprintf(name, len, "%s_%u", init_name, ++i);
0319 }
0320
0321 if (ret >= len)
0322 return -ENOMEM;
0323
0324 return i;
0325 }
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335 int led_classdev_register_ext(struct device *parent,
0336 struct led_classdev *led_cdev,
0337 struct led_init_data *init_data)
0338 {
0339 char composed_name[LED_MAX_NAME_SIZE];
0340 char final_name[LED_MAX_NAME_SIZE];
0341 const char *proposed_name = composed_name;
0342 int ret;
0343
0344 if (init_data) {
0345 if (init_data->devname_mandatory && !init_data->devicename) {
0346 dev_err(parent, "Mandatory device name is missing");
0347 return -EINVAL;
0348 }
0349 ret = led_compose_name(parent, init_data, composed_name);
0350 if (ret < 0)
0351 return ret;
0352
0353 if (init_data->fwnode) {
0354 fwnode_property_read_string(init_data->fwnode,
0355 "linux,default-trigger",
0356 &led_cdev->default_trigger);
0357
0358 if (fwnode_property_present(init_data->fwnode,
0359 "retain-state-shutdown"))
0360 led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
0361 }
0362 } else {
0363 proposed_name = led_cdev->name;
0364 }
0365
0366 ret = led_classdev_next_name(proposed_name, final_name, sizeof(final_name));
0367 if (ret < 0)
0368 return ret;
0369
0370 mutex_init(&led_cdev->led_access);
0371 mutex_lock(&led_cdev->led_access);
0372 led_cdev->dev = device_create_with_groups(leds_class, parent, 0,
0373 led_cdev, led_cdev->groups, "%s", final_name);
0374 if (IS_ERR(led_cdev->dev)) {
0375 mutex_unlock(&led_cdev->led_access);
0376 return PTR_ERR(led_cdev->dev);
0377 }
0378 if (init_data && init_data->fwnode)
0379 device_set_node(led_cdev->dev, init_data->fwnode);
0380
0381 if (ret)
0382 dev_warn(parent, "Led %s renamed to %s due to name collision",
0383 proposed_name, dev_name(led_cdev->dev));
0384
0385 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
0386 ret = led_add_brightness_hw_changed(led_cdev);
0387 if (ret) {
0388 device_unregister(led_cdev->dev);
0389 led_cdev->dev = NULL;
0390 mutex_unlock(&led_cdev->led_access);
0391 return ret;
0392 }
0393 }
0394
0395 led_cdev->work_flags = 0;
0396 #ifdef CONFIG_LEDS_TRIGGERS
0397 init_rwsem(&led_cdev->trigger_lock);
0398 #endif
0399 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
0400 led_cdev->brightness_hw_changed = -1;
0401 #endif
0402
0403 down_write(&leds_list_lock);
0404 list_add_tail(&led_cdev->node, &leds_list);
0405 up_write(&leds_list_lock);
0406
0407 if (!led_cdev->max_brightness)
0408 led_cdev->max_brightness = LED_FULL;
0409
0410 led_update_brightness(led_cdev);
0411
0412 led_init_core(led_cdev);
0413
0414 #ifdef CONFIG_LEDS_TRIGGERS
0415 led_trigger_set_default(led_cdev);
0416 #endif
0417
0418 mutex_unlock(&led_cdev->led_access);
0419
0420 dev_dbg(parent, "Registered led device: %s\n",
0421 led_cdev->name);
0422
0423 return 0;
0424 }
0425 EXPORT_SYMBOL_GPL(led_classdev_register_ext);
0426
0427
0428
0429
0430
0431
0432
0433 void led_classdev_unregister(struct led_classdev *led_cdev)
0434 {
0435 if (IS_ERR_OR_NULL(led_cdev->dev))
0436 return;
0437
0438 #ifdef CONFIG_LEDS_TRIGGERS
0439 down_write(&led_cdev->trigger_lock);
0440 if (led_cdev->trigger)
0441 led_trigger_set(led_cdev, NULL);
0442 up_write(&led_cdev->trigger_lock);
0443 #endif
0444
0445 led_cdev->flags |= LED_UNREGISTERING;
0446
0447
0448 led_stop_software_blink(led_cdev);
0449
0450 if (!(led_cdev->flags & LED_RETAIN_AT_SHUTDOWN))
0451 led_set_brightness(led_cdev, LED_OFF);
0452
0453 flush_work(&led_cdev->set_brightness_work);
0454
0455 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
0456 led_remove_brightness_hw_changed(led_cdev);
0457
0458 device_unregister(led_cdev->dev);
0459
0460 down_write(&leds_list_lock);
0461 list_del(&led_cdev->node);
0462 up_write(&leds_list_lock);
0463
0464 mutex_destroy(&led_cdev->led_access);
0465 }
0466 EXPORT_SYMBOL_GPL(led_classdev_unregister);
0467
0468 static void devm_led_classdev_release(struct device *dev, void *res)
0469 {
0470 led_classdev_unregister(*(struct led_classdev **)res);
0471 }
0472
0473
0474
0475
0476
0477
0478
0479
0480 int devm_led_classdev_register_ext(struct device *parent,
0481 struct led_classdev *led_cdev,
0482 struct led_init_data *init_data)
0483 {
0484 struct led_classdev **dr;
0485 int rc;
0486
0487 dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL);
0488 if (!dr)
0489 return -ENOMEM;
0490
0491 rc = led_classdev_register_ext(parent, led_cdev, init_data);
0492 if (rc) {
0493 devres_free(dr);
0494 return rc;
0495 }
0496
0497 *dr = led_cdev;
0498 devres_add(parent, dr);
0499
0500 return 0;
0501 }
0502 EXPORT_SYMBOL_GPL(devm_led_classdev_register_ext);
0503
0504 static int devm_led_classdev_match(struct device *dev, void *res, void *data)
0505 {
0506 struct led_classdev **p = res;
0507
0508 if (WARN_ON(!p || !*p))
0509 return 0;
0510
0511 return *p == data;
0512 }
0513
0514
0515
0516
0517
0518
0519 void devm_led_classdev_unregister(struct device *dev,
0520 struct led_classdev *led_cdev)
0521 {
0522 WARN_ON(devres_release(dev,
0523 devm_led_classdev_release,
0524 devm_led_classdev_match, led_cdev));
0525 }
0526 EXPORT_SYMBOL_GPL(devm_led_classdev_unregister);
0527
0528 static int __init leds_init(void)
0529 {
0530 leds_class = class_create(THIS_MODULE, "leds");
0531 if (IS_ERR(leds_class))
0532 return PTR_ERR(leds_class);
0533 leds_class->pm = &leds_class_dev_pm_ops;
0534 leds_class->dev_groups = led_groups;
0535 return 0;
0536 }
0537
0538 static void __exit leds_exit(void)
0539 {
0540 class_destroy(leds_class);
0541 }
0542
0543 subsys_initcall(leds_init);
0544 module_exit(leds_exit);
0545
0546 MODULE_AUTHOR("John Lenz, Richard Purdie");
0547 MODULE_LICENSE("GPL");
0548 MODULE_DESCRIPTION("LED Class Interface");