Commit abc3100f authored by Hans de Goede's avatar Hans de Goede Committed by Lee Jones
Browse files

leds: led-class: Add generic [devm_]led_get()



Add a generic [devm_]led_get() method which can be used on both devicetree
and non devicetree platforms to get a LED classdev associated with
a specific function on a specific device, e.g. the privacy LED associated
with a specific camera sensor.

Note unlike of_led_get() this takes a string describing the function
rather then an index. This is done because e.g. camera sensors might
have a privacy LED, or a flash LED, or both and using an index
approach leaves it unclear what the function of index 0 is if there is
only 1 LED.

This uses a lookup-table mechanism for non devicetree platforms.
This allows the platform code to map specific LED class_dev-s to a specific
device,function combinations this way.

For devicetree platforms getting the LED by function-name could be made
to work using the standard devicetree pattern of adding a -names string
array to map names to the indexes.

Reviewed-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarLee Jones <lee@kernel.org>
Link: https://lore.kernel.org/r/20230120114524.408368-5-hdegoede@redhat.com
parent 537bdca2
Loading
Loading
Loading
Loading
+84 −0
Original line number Diff line number Diff line
@@ -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)
@@ -317,6 +319,88 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev,
}
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);

	if (!provider)
		return ERR_PTR(-ENOENT);

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

	return led_module_get(led_dev);
}
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)
{
+21 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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,