diff options
Diffstat (limited to 'drivers/leds/led-class.c')
-rw-r--r-- | drivers/leds/led-class.c | 96 |
1 files changed, 94 insertions, 2 deletions
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 795ec994c663..728681debdbe 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -212,6 +212,31 @@ static const struct dev_pm_ops leds_class_dev_pm_ops = { .resume = led_resume, }; +static int match_name(struct device *dev, const void *data) +{ + if (!dev_name(dev)) + return 0; + return !strcmp(dev_name(dev), (char *)data); +} + +static int led_classdev_next_name(const char *init_name, char *name, + size_t len) +{ + unsigned int i = 0; + int ret = 0; + + strlcpy(name, init_name, len); + + while (class_find_device(leds_class, NULL, name, match_name) && + (ret < len)) + ret = snprintf(name, len, "%s_%u", init_name, ++i); + + if (ret >= len) + return -ENOMEM; + + return i; +} + /** * led_classdev_register - register a new object of led_classdev class. * @parent: The device to register. @@ -219,12 +244,22 @@ static const struct dev_pm_ops leds_class_dev_pm_ops = { */ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) { + char name[64]; + int ret; + + ret = led_classdev_next_name(led_cdev->name, name, sizeof(name)); + if (ret < 0) + return ret; + led_cdev->dev = device_create_with_groups(leds_class, parent, 0, - led_cdev, led_cdev->groups, - "%s", led_cdev->name); + led_cdev, led_cdev->groups, "%s", name); if (IS_ERR(led_cdev->dev)) return PTR_ERR(led_cdev->dev); + if (ret) + dev_warn(parent, "Led %s renamed to %s due to name collision", + led_cdev->name, dev_name(led_cdev->dev)); + #ifdef CONFIG_LEDS_TRIGGERS init_rwsem(&led_cdev->trigger_lock); #endif @@ -288,6 +323,63 @@ void led_classdev_unregister(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_classdev_unregister); +static void devm_led_classdev_release(struct device *dev, void *res) +{ + led_classdev_unregister(*(struct led_classdev **)res); +} + +/** + * devm_led_classdev_register - resource managed led_classdev_register() + * @parent: The device to register. + * @led_cdev: the led_classdev structure for this device. + */ +int devm_led_classdev_register(struct device *parent, + struct led_classdev *led_cdev) +{ + struct led_classdev **dr; + int rc; + + dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + rc = led_classdev_register(parent, led_cdev); + if (rc) { + devres_free(dr); + return rc; + } + + *dr = led_cdev; + devres_add(parent, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_led_classdev_register); + +static int devm_led_classdev_match(struct device *dev, void *res, void *data) +{ + struct led_cdev **p = res; + + if (WARN_ON(!p || !*p)) + return 0; + + return *p == data; +} + +/** + * devm_led_classdev_unregister() - resource managed led_classdev_unregister() + * @parent: The device to unregister. + * @led_cdev: the led_classdev structure for this device. + */ +void devm_led_classdev_unregister(struct device *dev, + struct led_classdev *led_cdev) +{ + WARN_ON(devres_release(dev, + devm_led_classdev_release, + devm_led_classdev_match, led_cdev)); +} +EXPORT_SYMBOL_GPL(devm_led_classdev_unregister); + static int __init leds_init(void) { leds_class = class_create(THIS_MODULE, "leds"); |