Merge branch 'locking-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / drivers / leds / led-class.c
index 795ec99..728681d 100644 (file)
@@ -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");