Skip to content

Commit

Permalink
Merge tag 'ib-leds-led_get-v6.3' into HEAD
Browse files Browse the repository at this point in the history
Immutable branch from LEDs due for the v6.3 merge window
  • Loading branch information
Hans de Goede committed Feb 3, 2023
2 parents 391bb17 + abc3100 commit 39f0932
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 20 deletions.
138 changes: 118 additions & 20 deletions drivers/leds/led-class.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include "leds.h"

static struct class *leds_class;
static DEFINE_MUTEX(leds_lookup_lock);
static LIST_HEAD(leds_lookup_list);

static ssize_t brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
Expand Down Expand Up @@ -215,6 +217,23 @@ static int led_resume(struct device *dev)

static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);

static struct led_classdev *led_module_get(struct device *led_dev)
{
struct led_classdev *led_cdev;

if (!led_dev)
return ERR_PTR(-EPROBE_DEFER);

led_cdev = dev_get_drvdata(led_dev);

if (!try_module_get(led_cdev->dev->parent->driver->owner)) {
put_device(led_cdev->dev);
return ERR_PTR(-ENODEV);
}

return led_cdev;
}

/**
* of_led_get() - request a LED device via the LED framework
* @np: device node to get the LED device from
Expand All @@ -226,7 +245,6 @@ static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
struct led_classdev *of_led_get(struct device_node *np, int index)
{
struct device *led_dev;
struct led_classdev *led_cdev;
struct device_node *led_node;

led_node = of_parse_phandle(np, "leds", index);
Expand All @@ -236,15 +254,7 @@ struct led_classdev *of_led_get(struct device_node *np, int index)
led_dev = class_find_device_by_of_node(leds_class, led_node);
of_node_put(led_node);

if (!led_dev)
return ERR_PTR(-EPROBE_DEFER);

led_cdev = dev_get_drvdata(led_dev);

if (!try_module_get(led_cdev->dev->parent->driver->owner))
return ERR_PTR(-ENODEV);

return led_cdev;
return led_module_get(led_dev);
}
EXPORT_SYMBOL_GPL(of_led_get);

Expand All @@ -255,6 +265,7 @@ EXPORT_SYMBOL_GPL(of_led_get);
void led_put(struct led_classdev *led_cdev)
{
module_put(led_cdev->dev->parent->driver->owner);
put_device(led_cdev->dev);
}
EXPORT_SYMBOL_GPL(led_put);

Expand All @@ -265,6 +276,22 @@ static void devm_led_release(struct device *dev, void *res)
led_put(*p);
}

static struct led_classdev *__devm_led_get(struct device *dev, struct led_classdev *led)
{
struct led_classdev **dr;

dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *), GFP_KERNEL);
if (!dr) {
led_put(led);
return ERR_PTR(-ENOMEM);
}

*dr = led;
devres_add(dev, dr);

return led;
}

/**
* devm_of_led_get - Resource-managed request of a LED device
* @dev: LED consumer
Expand All @@ -280,7 +307,6 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev,
int index)
{
struct led_classdev *led;
struct led_classdev **dr;

if (!dev)
return ERR_PTR(-EINVAL);
Expand All @@ -289,19 +315,91 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev,
if (IS_ERR(led))
return led;

dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *),
GFP_KERNEL);
if (!dr) {
led_put(led);
return ERR_PTR(-ENOMEM);
return __devm_led_get(dev, led);
}
EXPORT_SYMBOL_GPL(devm_of_led_get);

/**
* led_get() - request a LED device via the LED framework
* @dev: device for which to get the LED device
* @con_id: name of the LED from the device's point of view
*
* @return a pointer to a LED device or ERR_PTR(errno) on failure.
*/
struct led_classdev *led_get(struct device *dev, char *con_id)
{
struct led_lookup_data *lookup;
const char *provider = NULL;
struct device *led_dev;

mutex_lock(&leds_lookup_lock);
list_for_each_entry(lookup, &leds_lookup_list, list) {
if (!strcmp(lookup->dev_id, dev_name(dev)) &&
!strcmp(lookup->con_id, con_id)) {
provider = kstrdup_const(lookup->provider, GFP_KERNEL);
break;
}
}
mutex_unlock(&leds_lookup_lock);

*dr = led;
devres_add(dev, dr);
if (!provider)
return ERR_PTR(-ENOENT);

return led;
led_dev = class_find_device_by_name(leds_class, provider);
kfree_const(provider);

return led_module_get(led_dev);
}
EXPORT_SYMBOL_GPL(devm_of_led_get);
EXPORT_SYMBOL_GPL(led_get);

/**
* devm_led_get() - request a LED device via the LED framework
* @dev: device for which to get the LED device
* @con_id: name of the LED from the device's point of view
*
* The LED device returned from this function is automatically released
* on driver detach.
*
* @return a pointer to a LED device or ERR_PTR(errno) on failure.
*/
struct led_classdev *devm_led_get(struct device *dev, char *con_id)
{
struct led_classdev *led;

led = led_get(dev, con_id);
if (IS_ERR(led))
return led;

return __devm_led_get(dev, led);
}
EXPORT_SYMBOL_GPL(devm_led_get);

/**
* led_add_lookup() - Add a LED lookup table entry
* @led_lookup: the lookup table entry to add
*
* Add a LED lookup table entry. On systems without devicetree the lookup table
* is used by led_get() to find LEDs.
*/
void led_add_lookup(struct led_lookup_data *led_lookup)
{
mutex_lock(&leds_lookup_lock);
list_add_tail(&led_lookup->list, &leds_lookup_list);
mutex_unlock(&leds_lookup_lock);
}
EXPORT_SYMBOL_GPL(led_add_lookup);

/**
* led_remove_lookup() - Remove a LED lookup table entry
* @led_lookup: the lookup table entry to remove
*/
void led_remove_lookup(struct led_lookup_data *led_lookup)
{
mutex_lock(&leds_lookup_lock);
list_del(&led_lookup->list);
mutex_unlock(&leds_lookup_lock);
}
EXPORT_SYMBOL_GPL(led_remove_lookup);

static int led_classdev_next_name(const char *init_name, char *name,
size_t len)
Expand Down
21 changes: 21 additions & 0 deletions include/linux/leds.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ enum led_default_state {
LEDS_DEFSTATE_KEEP = 2,
};

/**
* struct led_lookup_data - represents a single LED lookup entry
*
* @list: internal list of all LED lookup entries
* @provider: name of led_classdev providing the LED
* @dev_id: name of the device associated with this LED
* @con_id: name of the LED from the device's point of view
*/
struct led_lookup_data {
struct list_head list;
const char *provider;
const char *dev_id;
const char *con_id;
};

struct led_init_data {
/* device fwnode handle */
struct fwnode_handle *fwnode;
Expand Down Expand Up @@ -211,6 +226,12 @@ void devm_led_classdev_unregister(struct device *parent,
void led_classdev_suspend(struct led_classdev *led_cdev);
void led_classdev_resume(struct led_classdev *led_cdev);

void led_add_lookup(struct led_lookup_data *led_lookup);
void led_remove_lookup(struct led_lookup_data *led_lookup);

struct led_classdev *__must_check led_get(struct device *dev, char *con_id);
struct led_classdev *__must_check devm_led_get(struct device *dev, char *con_id);

extern struct led_classdev *of_led_get(struct device_node *np, int index);
extern void led_put(struct led_classdev *led_cdev);
struct led_classdev *__must_check devm_of_led_get(struct device *dev,
Expand Down

0 comments on commit 39f0932

Please sign in to comment.