lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Tue, 19 Nov 2019 14:23:09 +0000
From:   "Vaittinen, Matti" <Matti.Vaittinen@...rohmeurope.com>
To:     "mazziesaccount@...il.com" <mazziesaccount@...il.com>
CC:     "corbet@....net" <corbet@....net>,
        "linux-doc@...r.kernel.org" <linux-doc@...r.kernel.org>,
        "phil.edworthy@...esas.com" <phil.edworthy@...esas.com>,
        "linux-rtc@...r.kernel.org" <linux-rtc@...r.kernel.org>,
        "dmurphy@...com" <dmurphy@...com>,
        "linux-leds@...r.kernel.org" <linux-leds@...r.kernel.org>,
        "devicetree@...r.kernel.org" <devicetree@...r.kernel.org>,
        "jeffrey.t.kirsher@...el.com" <jeffrey.t.kirsher@...el.com>,
        "linux-gpio@...r.kernel.org" <linux-gpio@...r.kernel.org>,
        "mchehab+samsung@...nel.org" <mchehab+samsung@...nel.org>,
        "alexandre.belloni@...tlin.com" <alexandre.belloni@...tlin.com>,
        "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
        "mturquette@...libre.com" <mturquette@...libre.com>,
        "lgirdwood@...il.com" <lgirdwood@...il.com>,
        "jacek.anaszewski@...il.com" <jacek.anaszewski@...il.com>,
        "wsa+renesas@...g-engineering.com" <wsa+renesas@...g-engineering.com>,
        "a.zummo@...ertech.it" <a.zummo@...ertech.it>,
        "linus.walleij@...aro.org" <linus.walleij@...aro.org>,
        "m.szyprowski@...sung.com" <m.szyprowski@...sung.com>,
        "mark.rutland@....com" <mark.rutland@....com>,
        "robh+dt@...nel.org" <robh+dt@...nel.org>,
        "linux-clk@...r.kernel.org" <linux-clk@...r.kernel.org>,
        "hkallweit1@...il.com" <hkallweit1@...il.com>,
        "bgolaszewski@...libre.com" <bgolaszewski@...libre.com>,
        "hofrat@...dl.org" <hofrat@...dl.org>,
        "lee.jones@...aro.org" <lee.jones@...aro.org>,
        "pavel@....cz" <pavel@....cz>,
        "broonie@...nel.org" <broonie@...nel.org>,
        "sboyd@...nel.org" <sboyd@...nel.org>
Subject: Re: [PATCH v5 15/16] leds: Add common LED binding parsing support
 to LED class/core


On Mon, 2019-11-18 at 09:03 +0200, Matti Vaittinen wrote:
> Qucik grep for 'for_each' or 'linux,default-trigger' or
> 'default-state' under drivers/leds can tell quite a lot. It seems
> multiple LED controller drivers implement the very similar looping
> through the child nodes in order to locate the LED nodes and read
> and support the common LED dt bindings. Implementing this same
> stuff for all LED controllers gets old pretty fast.
> 
> This commit adds support for locating the LED node (based on known
> node names - or linux,led-compatible property) and handling of
> few common LED properties.

This is actually not completely true. I originally thought of adding
led-compatible - but I changed my mind after I saw that at least some
of the existing drivers used value of "reg"-property to do the matching
in-driver. So, to make it simple for them, I did add property
name/value pair in init data for matching. This allows one to do led-
compatible, or to use existing fixed reg (or other) property value. I'd
better to change the commit message to reflect this too.

> 
> linux,default-trigger,
> default-state (with the exception of keep),
> 
> (in addition to already handled
> function-enumerator,
> function,
> color
> and label).
> 
> Regarding the node look-up: If no init_data is given, then no
> node-lookup is done and cdev name is used as such.
> 
> If init_data is goven but no starting point for node lookup - then
> (parent) device's own DT node is used. If no led-compatible is given,
> then of_match is searched for. If neither led-compatible not of_match
> is given then device's own node or passed starting point are used as
> such.

And same here.

> 
> Signed-off-by: Matti Vaittinen <matti.vaittinen@...rohmeurope.com>
> ---
> 
> Changes from v4:
> Fixed issues reported by Dan Carpenter and kbuild-bot.
> (leftover kfree and uninitialized return value)
> 
>  drivers/leds/led-class.c |  88 ++++++++++++--
>  drivers/leds/led-core.c  | 246 +++++++++++++++++++++++++++++++----
> ----
>  include/linux/leds.h     |  90 ++++++++++++--
>  3 files changed, 359 insertions(+), 65 deletions(-)
> 
> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> index 647b1263c579..98173b64a597 100644
> --- a/drivers/leds/led-class.c
> +++ b/drivers/leds/led-class.c
> @@ -235,6 +235,29 @@ static int led_classdev_next_name(const char
> *init_name, char *name,
>  	return i;
>  }
>  
> +static void led_add_props(struct led_classdev *ld, struct
> led_properties *props)
> +{
> +	if (props->default_trigger)
> +		ld->default_trigger = props->default_trigger;
> +	/*
> +	 * According to binding docs the LED is by-default turned OFF
> unless
> +	 * default_state is used to indicate it should be ON or that
> state
> +	 * should be kept as is
> +	 */
> +	if (props->default_state) {
> +		ld->brightness = LED_OFF;
> +		if (!strcmp(props->default_state, "on"))
> +			ld->brightness = LED_FULL;
> +	/*
> +	 * We probably should not call the brightness_get prior calling
> +	 * the of_parse_cb if one is provided.
> +	 * Add a flag to advertice that state should be queried and
> kept as-is.
> +	 */
> +		else if (!strcmp(props->default_state, "keep"))
> +			props->brightness_keep = true;
> +	}
> +}
> +
>  /**
>   * led_classdev_register_ext - register a new object of led_classdev
> class
>   *			       with init data.
> @@ -251,22 +274,58 @@ int led_classdev_register_ext(struct device
> *parent,
>  	char final_name[LED_MAX_NAME_SIZE];
>  	const char *proposed_name = composed_name;
>  	int ret;
> -
> +	struct led_properties props = {0};
> +	struct fwnode_handle *fw;
> +
> +	/*
> +	 * We don't try getting the name based on DT node if init-data
> is not
> +	 * given. We could see if we find LED properties from the
> device's node
> +	 * but that might change LED names for old users of
> +	 * led_classdev_register who have been providing the LED name
> in
> +	 * cdev->name. So we seek fwnode for names only if init_data is
> given
> +	 */
>  	if (init_data) {
> +		led_cdev->init_data = init_data;
>  		if (init_data->devname_mandatory && !init_data-
> >devicename) {
>  			dev_err(parent, "Mandatory device name is
> missing");
>  			return -EINVAL;
>  		}
> -		ret = led_compose_name(parent, init_data,
> composed_name);
> +
> +		fw = led_find_fwnode(parent, init_data);
> +		if (!fw) {
> +			dev_err(parent, "No fwnode found\n");
> +			return -ENOENT;
> +		}
> +		/*
> +		 * We did increase refcount for fwnode. Let's set a
> flag so we
> +		 * can decrease it during deregistration
> +		 */
> +		led_cdev->fwnode_owned = true;
> +
> +		ret = led_parse_fwnode_props(parent, fw, &props);
> +		if (ret)
> +			goto err_out;
> +
> +		if (init_data->of_parse_cb)
> +			ret = init_data->of_parse_cb(led_cdev, fw,
> &props);
>  		if (ret < 0)
> -			return ret;
> +			goto err_out;
> +
> +		led_add_props(led_cdev, &props);
> +
> +		ret = led_compose_name(parent, init_data, &props,
> +				       composed_name);
> +		if (ret < 0)
> +			goto err_out;
>  	} else {
>  		proposed_name = led_cdev->name;
> +		led_cdev->fwnode_owned = false;
> +		fw = NULL;
>  	}
>  
>  	ret = led_classdev_next_name(proposed_name, final_name,
> sizeof(final_name));
>  	if (ret < 0)
> -		return ret;
> +		goto err_out;
>  
>  	mutex_init(&led_cdev->led_access);
>  	mutex_lock(&led_cdev->led_access);
> @@ -274,22 +333,28 @@ int led_classdev_register_ext(struct device
> *parent,
>  				led_cdev, led_cdev->groups, "%s",
> final_name);
>  	if (IS_ERR(led_cdev->dev)) {
>  		mutex_unlock(&led_cdev->led_access);
> -		return PTR_ERR(led_cdev->dev);
> +		ret = PTR_ERR(led_cdev->dev);
> +		goto err_out;
>  	}
> -	if (init_data && init_data->fwnode)
> -		led_cdev->dev->fwnode = init_data->fwnode;
> +	if (fw)
> +		led_cdev->dev->fwnode = fw;
>  
>  	if (ret)
>  		dev_warn(parent, "Led %s renamed to %s due to name
> collision",
>  				led_cdev->name, dev_name(led_cdev-
> >dev));
>  
> +	if (props.brightness_keep)
> +		if (led_cdev->brightness_get)
> +			led_cdev->brightness =
> +				 led_cdev->brightness_get(led_cdev);
> +
>  	if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
>  		ret = led_add_brightness_hw_changed(led_cdev);
>  		if (ret) {
>  			device_unregister(led_cdev->dev);
>  			led_cdev->dev = NULL;
>  			mutex_unlock(&led_cdev->led_access);
> -			return ret;
> +			goto err_out;
>  		}
>  	}
>  
> @@ -322,6 +387,10 @@ int led_classdev_register_ext(struct device
> *parent,
>  			led_cdev->name);
>  
>  	return 0;
> +err_out:
> +	if (led_cdev->fwnode_owned)
> +		fwnode_handle_put(fw);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(led_classdev_register_ext);
>  
> @@ -355,6 +424,9 @@ void led_classdev_unregister(struct led_classdev
> *led_cdev)
>  	if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
>  		led_remove_brightness_hw_changed(led_cdev);
>  
> +	if (led_cdev->fwnode_owned)
> +		fwnode_handle_put(led_cdev->dev->fwnode);
> +
>  	device_unregister(led_cdev->dev);
>  
>  	down_write(&leds_list_lock);
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index f1f718dbe0f8..9369f03ee540 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -365,70 +365,214 @@ void led_sysfs_enable(struct led_classdev
> *led_cdev)
>  }
>  EXPORT_SYMBOL_GPL(led_sysfs_enable);
>  
> -static void led_parse_fwnode_props(struct device *dev,
> -				   struct fwnode_handle *fwnode,
> -				   struct led_properties *props)
> +static int fw_is_match(struct fwnode_handle *fw,
> +		       struct led_fw_match_property *mp, void *val)
>  {
> -	int ret;
> +	void *cmp = mp->raw_val;
> +	int ret = -EINVAL;
> +
> +	if (mp->raw_val) {
> +		ret = fwnode_property_read_u8_array(fw, mp->name, val,
> +						    mp->size);
> +	} else if (mp->intval) {
> +		cmp = mp->intval;
> +		switch (mp->size) {
> +		case 1:
> +			ret = fwnode_property_read_u8_array(fw, mp-
> >name, val,
> +						    mp->size);
> +			break;
> +		case 2:
> +			ret = fwnode_property_read_u16_array(fw, mp-
> >name, val,
> +						    mp->size);
> +			break;
> +		case 4:
> +			ret = fwnode_property_read_u32_array(fw, mp-
> >name, val,
> +						    mp->size);
> +			break;
> +		case 8:
> +			ret = fwnode_property_read_u64_array(fw, mp-
> >name, val,
> +						    mp->size);
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +	if (!ret && cmp)
> +		if (!memcmp(val, cmp, mp->size))
> +			return 1;
> +
> +	return 0;
> +}
> +/**
> + * led_find_fwnode - find fwnode for led
> + * @parent	LED controller device
> + * @init_data	led init data with match information
> + *
> + * Scans the firmware nodes and returns node matching the given
> init_data.
> + * NOTE: Function increases refcount for found node. Caller must
> decrease
> + * refcount using fwnode_handle_put when finished with node.
> + */
> +struct fwnode_handle *led_find_fwnode(struct device *parent,
> +				      struct led_init_data *init_data)
> +{
> +	struct fwnode_handle *fw;
> +
> +	/*
> +	 * This should never be called W/O init data. We could always
> return
> +	 * dev_fwnode() - but then we should pump-up the refcount
> +	 */
> +	if (!init_data)
> +		return NULL;
> +
> +	if (!init_data->fwnode)
> +		fw = dev_fwnode(parent);
> +	else
> +		fw = init_data->fwnode;
> +
> +	if (!fw)
> +		return NULL;
> +
> +	/*
> +	 * Simple things are pretty. I think simplest is to use DT
> node-name
> +	 * for matching the node with LED - same way regulators use the
> node
> +	 * name to match with desc.
> +	 *
> +	 * This may not work with existing LED DT entries if the node
> name has
> +	 * been freely selectible. In order to this to work the binding
> doc
> +	 * for LED driver should define usable node names.
> +	 *
> +	 * If this is not working we can define specific match property
> which
> +	 * value we scan and use for matching for LEDs connected to the
> +	 * controller.
> +	 */
> +	if (init_data->match_property.name && init_data-
> >match_property.size) {
> +		u8 *val;
> +		int ret;
> +		struct fwnode_handle *child;
> +		struct led_fw_match_property *mp;
> +
> +		mp = &init_data->match_property;
> +
> +		val = kzalloc(mp->size, GFP_KERNEL);
> +		if (!val)
> +			return ERR_PTR(-ENOMEM);
> +
> +		fwnode_for_each_child_node(fw, child) {
> +			ret = fw_is_match(child, mp, val);
> +			if (ret > 0) {
> +				kfree(val);
> +				return child;
> +			}
> +			if (ret < 0) {
> +				dev_err(parent,
> +					"invalid fw match. Use
> raw_val?\n");
> +				fwnode_handle_put(child);
> +				break;
> +			}
> +		}
> +		kfree(val);
> +	}
> +	if (init_data->of_match)
> +		fw = fwnode_get_named_child_node(fw, init_data-
> >of_match);
> +	else
> +		fw = fwnode_handle_get(fw);
> +
> +	return fw;
> +}
> +EXPORT_SYMBOL(led_find_fwnode);
> +
> +int led_parse_fwnode_props(struct device *dev, struct fwnode_handle
> *fwnode,
> +			   struct led_properties *props)
> +{
> +	int ret = 0;
>  
>  	if (!fwnode)
> -		return;
> +		return 0;
>  
>  	if (fwnode_property_present(fwnode, "label")) {
>  		ret = fwnode_property_read_string(fwnode, "label",
> &props->label);
>  		if (ret)
>  			dev_err(dev, "Error parsing 'label' property
> (%d)\n", ret);
> -		return;
> +		return ret;
>  	}
>  
> +	/**
> +	 * Please note, logic changed - if invalid property is found we
> bail
> +	 * early out without parsing the rest of the properties.
> Originally
> +	 * this was the case only for 'label' property. I don't know
> the
> +	 * rationale behind original logic allowing invalid properties
> to be
> +	 * given. If there is a reason then we should reconsider this.
> +	 * Intuitively it feels correct to just yell and quit if we hit
> value we
> +	 * don't understand - but intuition may be wrong at times :)
> +	 */
>  	if (fwnode_property_present(fwnode, "color")) {
>  		ret = fwnode_property_read_u32(fwnode, "color", &props-
> >color);
> -		if (ret)
> +		if (ret) {
>  			dev_err(dev, "Error parsing 'color' property
> (%d)\n", ret);
> -		else if (props->color >= LED_COLOR_ID_MAX)
> +			return ret;
> +		} else if (props->color >= LED_COLOR_ID_MAX) {
>  			dev_err(dev, "LED color identifier out of
> range\n");
> -		else
> -			props->color_present = true;
> +			return ret;
> +		}
> +		props->color_present = true;
>  	}
>  
> +	if (fwnode_property_present(fwnode, "function")) {
> +		ret = fwnode_property_read_string(fwnode, "function",
> +						  &props->function);
> +		if (ret) {
> +			dev_err(dev,
> +				"Error parsing 'function' property
> (%d)\n",
> +				ret);
> +			return ret;
> +		}
> +	}
>  
> -	if (!fwnode_property_present(fwnode, "function"))
> -		return;
> -
> -	ret = fwnode_property_read_string(fwnode, "function", &props-
> >function);
> -	if (ret) {
> -		dev_err(dev,
> -			"Error parsing 'function' property (%d)\n",
> -			ret);
> +	if (fwnode_property_present(fwnode, "function-enumerator")) {
> +		ret = fwnode_property_read_u32(fwnode, "function-
> enumerator",
> +					       &props->func_enum);
> +		if (ret) {
> +			dev_err(dev,
> +				"Error parsing 'function-enumerator'
> property (%d)\n",
> +				ret);
> +			return ret;
> +		}
> +		props->func_enum_present = true;
>  	}
>  
> -	if (!fwnode_property_present(fwnode, "function-enumerator"))
> -		return;
> +	if (fwnode_property_present(fwnode, "default-state")) {
> +		ret = fwnode_property_read_string(fwnode, "default-
> state",
> +						  &props-
> >default_state);
> +		if (ret) {
> +			dev_err(dev,
> +				"Error parsing 'default-state' property
> (%d)\n",
> +				ret);
> +			return ret;
> +		}
> +	}
>  
> -	ret = fwnode_property_read_u32(fwnode, "function-enumerator",
> -				       &props->func_enum);
> -	if (ret) {
> -		dev_err(dev,
> -			"Error parsing 'function-enumerator' property
> (%d)\n",
> -			ret);
> -	} else {
> -		props->func_enum_present = true;
> +	if (fwnode_property_present(fwnode, "linux,default-trigger")) {
> +		ret = fwnode_property_read_string(fwnode,
> +						  "linux,default-
> trigger",
> +						  &props-
> >default_trigger);
> +		if (ret)
> +			dev_err(dev,
> +				"Error parsing 'linux,default-trigger'
> property (%d)\n",
> +				ret);
>  	}
> +	return ret;
>  }
> +EXPORT_SYMBOL_GPL(led_parse_fwnode_props);
>  
>  int led_compose_name(struct device *dev, struct led_init_data
> *init_data,
> -		     char *led_classdev_name)
> +		     struct led_properties *props, char
> *led_classdev_name)
>  {
> -	struct led_properties props = {};
> -	struct fwnode_handle *fwnode = init_data->fwnode;
>  	const char *devicename = init_data->devicename;
>  
>  	if (!led_classdev_name)
>  		return -EINVAL;
>  
> -	led_parse_fwnode_props(dev, fwnode, &props);
> -
> -	if (props.label) {
> +	if (props->label) {
>  		/*
>  		 * If init_data.devicename is NULL, then it indicates
> that
>  		 * DT label should be used as-is for LED class device
> name.
> @@ -436,23 +580,23 @@ int led_compose_name(struct device *dev, struct
> led_init_data *init_data,
>  		 * the final LED class device name.
>  		 */
>  		if (!devicename) {
> -			strscpy(led_classdev_name, props.label,
> +			strscpy(led_classdev_name, props->label,
>  				LED_MAX_NAME_SIZE);
>  		} else {
>  			snprintf(led_classdev_name, LED_MAX_NAME_SIZE,
> "%s:%s",
> -				 devicename, props.label);
> +				 devicename, props->label);
>  		}
> -	} else if (props.function || props.color_present) {
> +	} else if (props->function || props->color_present) {
>  		char tmp_buf[LED_MAX_NAME_SIZE];
>  
> -		if (props.func_enum_present) {
> +		if (props->func_enum_present) {
>  			snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-
> %d",
> -				 props.color_present ?
> led_colors[props.color] : "",
> -				 props.function ?: "",
> props.func_enum);
> +				 props->color_present ?
> led_colors[props->color] : "",
> +				 props->function ?: "", props-
> >func_enum);
>  		} else {
>  			snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
> -				 props.color_present ?
> led_colors[props.color] : "",
> -				 props.function ?: "");
> +				 props->color_present ?
> led_colors[props->color] : "",
> +				 props->function ?: "");
>  		}
>  		if (init_data->devname_mandatory) {
>  			snprintf(led_classdev_name, LED_MAX_NAME_SIZE,
> "%s:%s",
> @@ -468,11 +612,19 @@ int led_compose_name(struct device *dev, struct
> led_init_data *init_data,
>  		}
>  		snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
>  			 devicename, init_data->default_label);
> -	} else if (is_of_node(fwnode)) {
> -		strscpy(led_classdev_name, to_of_node(fwnode)->name,
> -			LED_MAX_NAME_SIZE);
> -	} else
> -		return -EINVAL;
> +	} else {
> +		struct fwnode_handle *fwnode = led_find_fwnode(dev,
> init_data);
> +		int ret = -EINVAL;
> +
> +		if (is_of_node(fwnode)) {
> +			ret = 0;
> +			strscpy(led_classdev_name, to_of_node(fwnode)-
> >name,
> +				LED_MAX_NAME_SIZE);
> +		}
> +		fwnode_handle_put(fwnode);
> +
> +		return ret;
> +	}
>  
>  	return 0;
>  }
> diff --git a/include/linux/leds.h b/include/linux/leds.h
> index efb309dba914..aea7d6bc7294 100644
> --- a/include/linux/leds.h
> +++ b/include/linux/leds.h
> @@ -13,6 +13,7 @@
>  #include <linux/kernfs.h>
>  #include <linux/list.h>
>  #include <linux/mutex.h>
> +#include <linux/property.h>
>  #include <linux/rwsem.h>
>  #include <linux/spinlock.h>
>  #include <linux/timer.h>
> @@ -20,6 +21,7 @@
>  
>  struct device;
>  struct led_pattern;
> +struct led_classdev;
>  /*
>   * LED Core
>   */
> @@ -31,8 +33,47 @@ enum led_brightness {
>  	LED_FULL	= 255,
>  };
>  
> +struct led_properties {
> +	u32		color;
> +	bool		color_present;
> +	const char	*function;
> +	u32		func_enum;
> +	bool		func_enum_present;
> +	const char	*label;
> +	const char	*default_trigger;
> +	const char	*default_state;
> +	bool		brightness_keep;
> +};
> +
> +struct led_fw_match_property {
> +	const char *name;	/* Name of the property to match */
> +	void *raw_val;		/* Raw property value as present in
> fwnode */
> +	void *intval;		/* Property value if 8,16,32 or 64bit
> integer */
> +	size_t size;		/* Size of value in bytes */
> +};
> +
>  struct led_init_data {
> -	/* device fwnode handle */
> +	/*
> +	 * If DT binding dictates the node name the driver can fill
> of_match
> +	 * corresponding to node name describing this LED. If fwnode is
> given
> +	 * the match is searched from it's child nodes. If not, the
> match is
> +	 * searched from device's own child nodes.
> +	 */
> +	const char *of_match;
> +	/*
> +	 * If fwnode contains property with known value the driver can
> specify
> +	 * correct propertty-value pair here to do the matching. This
> has higher
> +	 * priority than of_match. If fwnode is given the match is
> searched
> +	 * from it's child nodes. If not match is searched from
> device's
> +	 * own child nodes.
> +	 */
> +	struct led_fw_match_property match_property;
> +	/*
> +	 * device fwnode handle. If of_match or led_compatible are not
> given
> +	 * this is used for LED as given. If of_match or led_compatible
> are
> +	 * given then this is used as a parent node whose child nodes
> are
> +	 * scanned for given match.
> +	 */
>  	struct fwnode_handle *fwnode;
>  	/*
>  	 * default <color:function> tuple, for backward compatibility
> @@ -53,9 +94,17 @@ struct led_init_data {
>  	 * set it to true
>  	 */
>  	bool devname_mandatory;
> +	/*
> +	 * Callback which a LED driver can register if it has own non-
> standard
> +	 * DT properties. Core calls this with the located DT node
> during
> +	 * class_device registration
> +	 */
> +	int (*of_parse_cb)(struct led_classdev *ld, struct
> fwnode_handle *fw,
> +			    struct led_properties *props);
>  };
>  
>  struct led_classdev {
> +	struct led_init_data	*init_data;
>  	const char		*name;
>  	enum led_brightness	 brightness;
>  	enum led_brightness	 max_brightness;
> @@ -148,6 +197,7 @@ struct led_classdev {
>  
>  	/* Ensures consistent access to the LED Flash Class device */
>  	struct mutex		led_access;
> +	bool			fwnode_owned;
>  };
>  
>  /**
> @@ -302,6 +352,7 @@ extern void led_sysfs_enable(struct led_classdev
> *led_cdev);
>   * led_compose_name - compose LED class device name
>   * @dev: LED controller device object
>   * @init_data: the LED class device initialization data
> + * @props: LED properties as parsed from fwnode.
>   * @led_classdev_name: composed LED class device name
>   *
>   * Create LED class device name basing on the provided init_data
> argument.
> @@ -311,6 +362,7 @@ extern void led_sysfs_enable(struct led_classdev
> *led_cdev);
>   * Returns: 0 on success or negative error value on failure
>   */
>  extern int led_compose_name(struct device *dev, struct led_init_data
> *init_data,
> +			    struct led_properties *props,
>  			    char *led_classdev_name);
>  
>  /**
> @@ -324,6 +376,33 @@ static inline bool led_sysfs_is_disabled(struct
> led_classdev *led_cdev)
>  	return led_cdev->flags & LED_SYSFS_DISABLE;
>  }
>  
> +/**
> + * led_find_fwnode - find fwnode matching given LED init data
> + * @parent: LED controller device this LED is driven by
> + * @init_data: the LED class device initialization data
> + *
> + * Find the fw node matching given LED init data.
> + * NOTE: Function increases refcount for found node. Caller must
> decrease
> + * refcount using fwnode_handle_put when finished with node.
> + *
> + * Returns: node handle or NULL if matching fw node was not found
> + */
> +struct fwnode_handle *led_find_fwnode(struct device *parent,
> +				      struct led_init_data *init_data);
> +
> +/**
> + * led_parse_fwnode_props - parse LED common properties from fwnode
> + * @dev: pointer to LED device.
> + * @fwnode: LED node containing the properties
> + * @props: structure where found property data is stored.
> + *
> + * Parse the common LED properties from fwnode.
> + *
> + * Returns: 0 on success or negative error value on failure
> + */
> +int led_parse_fwnode_props(struct device *dev, struct fwnode_handle
> *fwnode,
> +			   struct led_properties *props);
> +
>  /*
>   * LED Triggers
>   */
> @@ -490,15 +569,6 @@ struct led_platform_data {
>  	struct led_info	*leds;
>  };
>  
> -struct led_properties {
> -	u32		color;
> -	bool		color_present;
> -	const char	*function;
> -	u32		func_enum;
> -	bool		func_enum_present;
> -	const char	*label;
> -};
> -
>  struct gpio_desc;
>  typedef int (*gpio_blink_set_t)(struct gpio_desc *desc, int state,
>  				unsigned long *delay_on,
> -- 
> 2.21.0
> 
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ