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 = { | |||
212 | .resume = led_resume, | 212 | .resume = led_resume, |
213 | }; | 213 | }; |
214 | 214 | ||
215 | static int match_name(struct device *dev, const void *data) | ||
216 | { | ||
217 | if (!dev_name(dev)) | ||
218 | return 0; | ||
219 | return !strcmp(dev_name(dev), (char *)data); | ||
220 | } | ||
221 | |||
222 | static int led_classdev_next_name(const char *init_name, char *name, | ||
223 | size_t len) | ||
224 | { | ||
225 | unsigned int i = 0; | ||
226 | int ret = 0; | ||
227 | |||
228 | strlcpy(name, init_name, len); | ||
229 | |||
230 | while (class_find_device(leds_class, NULL, name, match_name) && | ||
231 | (ret < len)) | ||
232 | ret = snprintf(name, len, "%s_%u", init_name, ++i); | ||
233 | |||
234 | if (ret >= len) | ||
235 | return -ENOMEM; | ||
236 | |||
237 | return i; | ||
238 | } | ||
239 | |||
215 | /** | 240 | /** |
216 | * led_classdev_register - register a new object of led_classdev class. | 241 | * led_classdev_register - register a new object of led_classdev class. |
217 | * @parent: The device to register. | 242 | * @parent: The device to register. |
@@ -219,12 +244,22 @@ static const struct dev_pm_ops leds_class_dev_pm_ops = { | |||
219 | */ | 244 | */ |
220 | int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) | 245 | int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) |
221 | { | 246 | { |
247 | char name[64]; | ||
248 | int ret; | ||
249 | |||
250 | ret = led_classdev_next_name(led_cdev->name, name, sizeof(name)); | ||
251 | if (ret < 0) | ||
252 | return ret; | ||
253 | |||
222 | led_cdev->dev = device_create_with_groups(leds_class, parent, 0, | 254 | led_cdev->dev = device_create_with_groups(leds_class, parent, 0, |
223 | led_cdev, led_cdev->groups, | 255 | led_cdev, led_cdev->groups, "%s", name); |
224 | "%s", led_cdev->name); | ||
225 | if (IS_ERR(led_cdev->dev)) | 256 | if (IS_ERR(led_cdev->dev)) |
226 | return PTR_ERR(led_cdev->dev); | 257 | return PTR_ERR(led_cdev->dev); |
227 | 258 | ||
259 | if (ret) | ||
260 | dev_warn(parent, "Led %s renamed to %s due to name collision", | ||
261 | led_cdev->name, dev_name(led_cdev->dev)); | ||
262 | |||
228 | #ifdef CONFIG_LEDS_TRIGGERS | 263 | #ifdef CONFIG_LEDS_TRIGGERS |
229 | init_rwsem(&led_cdev->trigger_lock); | 264 | init_rwsem(&led_cdev->trigger_lock); |
230 | #endif | 265 | #endif |
@@ -288,6 +323,63 @@ void led_classdev_unregister(struct led_classdev *led_cdev) | |||
288 | } | 323 | } |
289 | EXPORT_SYMBOL_GPL(led_classdev_unregister); | 324 | EXPORT_SYMBOL_GPL(led_classdev_unregister); |
290 | 325 | ||
326 | static void devm_led_classdev_release(struct device *dev, void *res) | ||
327 | { | ||
328 | led_classdev_unregister(*(struct led_classdev **)res); | ||
329 | } | ||
330 | |||
331 | /** | ||
332 | * devm_led_classdev_register - resource managed led_classdev_register() | ||
333 | * @parent: The device to register. | ||
334 | * @led_cdev: the led_classdev structure for this device. | ||
335 | */ | ||
336 | int devm_led_classdev_register(struct device *parent, | ||
337 | struct led_classdev *led_cdev) | ||
338 | { | ||
339 | struct led_classdev **dr; | ||
340 | int rc; | ||
341 | |||
342 | dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL); | ||
343 | if (!dr) | ||
344 | return -ENOMEM; | ||
345 | |||
346 | rc = led_classdev_register(parent, led_cdev); | ||
347 | if (rc) { | ||
348 | devres_free(dr); | ||
349 | return rc; | ||
350 | } | ||
351 | |||
352 | *dr = led_cdev; | ||
353 | devres_add(parent, dr); | ||
354 | |||
355 | return 0; | ||
356 | } | ||
357 | EXPORT_SYMBOL_GPL(devm_led_classdev_register); | ||
358 | |||
359 | static int devm_led_classdev_match(struct device *dev, void *res, void *data) | ||
360 | { | ||
361 | struct led_cdev **p = res; | ||
362 | |||
363 | if (WARN_ON(!p || !*p)) | ||
364 | return 0; | ||
365 | |||
366 | return *p == data; | ||
367 | } | ||
368 | |||
369 | /** | ||
370 | * devm_led_classdev_unregister() - resource managed led_classdev_unregister() | ||
371 | * @parent: The device to unregister. | ||
372 | * @led_cdev: the led_classdev structure for this device. | ||
373 | */ | ||
374 | void devm_led_classdev_unregister(struct device *dev, | ||
375 | struct led_classdev *led_cdev) | ||
376 | { | ||
377 | WARN_ON(devres_release(dev, | ||
378 | devm_led_classdev_release, | ||
379 | devm_led_classdev_match, led_cdev)); | ||
380 | } | ||
381 | EXPORT_SYMBOL_GPL(devm_led_classdev_unregister); | ||
382 | |||
291 | static int __init leds_init(void) | 383 | static int __init leds_init(void) |
292 | { | 384 | { |
293 | leds_class = class_create(THIS_MODULE, "leds"); | 385 | leds_class = class_create(THIS_MODULE, "leds"); |