public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Lee Jones <lee@kernel.org>
To: Craig McQueen <craig@mcqueen.au>
Cc: linux-leds@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v3 2/2] leds: Add some setup ioctls to uleds driver
Date: Fri, 4 Apr 2025 13:30:45 +0100	[thread overview]
Message-ID: <20250404123045.GA278642@google.com> (raw)
In-Reply-To: <20250317022220.423966-2-craig@mcqueen.au>

On Mon, 17 Mar 2025, Craig McQueen wrote:

> * Add an ioctl for setup as an alternative to doing a write.
>   This is similar to the ioctl that was added to uinput for setup.
> * Add an ioctl to set a default trigger for the LED.
> 
> Signed-off-by: Craig McQueen <craig@mcqueen.au>
> ---
>  Documentation/leds/uleds.rst |   6 ++
>  drivers/leds/uleds.c         | 125 ++++++++++++++++++++++++++++-------
>  include/uapi/linux/uleds.h   |  30 ++++++++-
>  3 files changed, 137 insertions(+), 24 deletions(-)
> 
> diff --git a/Documentation/leds/uleds.rst b/Documentation/leds/uleds.rst
> index 4077745dae00..0512e577d63a 100644
> --- a/Documentation/leds/uleds.rst
> +++ b/Documentation/leds/uleds.rst
> @@ -24,6 +24,12 @@ A new LED class device will be created with the name given. The name can be
>  any valid sysfs device node name, but consider using the LED class naming
>  convention of "devicename:color:function".
>  
> +Alternatively, setup can be done with an ioctl call, passing a pointer to
> +the structure.
> +
> +There is also an ioctl call to configure a default trigger for the LED, by
> +passing a pointer to a structure containing the name of a trigger.
> +
>  The current brightness is found by reading an int value from the character
>  device. Reading will block until the brightness changes. The device node can
>  also be polled to notify when the brightness value changes.
> diff --git a/drivers/leds/uleds.c b/drivers/leds/uleds.c
> index 374a841f18c3..598e376a4b1b 100644
> --- a/drivers/leds/uleds.c
> +++ b/drivers/leds/uleds.c
> @@ -6,6 +6,7 @@
>   *
>   * Based on uinput.c: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
>   */
> +#include <linux/ctype.h>
>  #include <linux/fs.h>
>  #include <linux/init.h>
>  #include <linux/leds.h>
> @@ -25,13 +26,14 @@ enum uleds_state {
>  };
>  
>  struct uleds_device {
> -	struct uleds_user_dev	user_dev;
> -	struct led_classdev	led_cdev;
> -	struct mutex		mutex;
> -	enum uleds_state	state;
> -	wait_queue_head_t	waitq;
> -	int			brightness;
> -	bool			new_data;
> +	struct uleds_user_dev		user_dev;
> +	struct uleds_user_trigger	default_trigger;
> +	struct led_classdev		led_cdev;
> +	struct mutex			mutex;
> +	enum uleds_state		state;
> +	wait_queue_head_t		waitq;
> +	int				brightness;
> +	bool				new_data;
>  };
>  
>  static struct miscdevice uleds_misc;
> @@ -70,15 +72,17 @@ static int uleds_open(struct inode *inode, struct file *file)
>  	return 0;
>  }
>  
> -static ssize_t uleds_write(struct file *file, const char __user *buffer,
> -			   size_t count, loff_t *ppos)
> +static bool is_led_name_valid(const char *name)
>  {
> -	struct uleds_device *udev = file->private_data;
> -	const char *name;
> -	int ret;
> +	return ((name[0] != '\0') &&
> +		(strcmp(name, ".") != 0) &&
> +		(strcmp(name, "..") != 0) &&
> +		(strchr(name, '/') == NULL));
> +}
>  
> -	if (count == 0)
> -		return 0;
> +static int dev_setup(struct uleds_device *udev, const char __user *buffer)

uleds_dev_setup()

> +{
> +	int ret;
>  
>  	ret = mutex_lock_interruptible(&udev->mutex);
>  	if (ret)
> @@ -89,20 +93,13 @@ static ssize_t uleds_write(struct file *file, const char __user *buffer,
>  		goto out;
>  	}
>  
> -	if (count != sizeof(struct uleds_user_dev)) {
> -		ret = -EINVAL;
> -		goto out;
> -	}

Why is this no longer required?

> -
>  	if (copy_from_user(&udev->user_dev, buffer,
>  			   sizeof(struct uleds_user_dev))) {
>  		ret = -EFAULT;
>  		goto out;
>  	}
>  
> -	name = udev->user_dev.name;
> -	if (!name[0] || !strcmp(name, ".") || !strcmp(name, "..") ||
> -	    strchr(name, '/')) {
> +	if (!is_led_name_valid(udev->user_dev.name)) {

Maybe we should make this official and put it in a header somewhere?

>  		ret = -EINVAL;
>  		goto out;
>  	}
> @@ -120,7 +117,6 @@ static ssize_t uleds_write(struct file *file, const char __user *buffer,
>  
>  	udev->new_data = true;
>  	udev->state = ULEDS_STATE_REGISTERED;
> -	ret = count;
>  
>  out:
>  	mutex_unlock(&udev->mutex);
> @@ -128,6 +124,23 @@ static ssize_t uleds_write(struct file *file, const char __user *buffer,
>  	return ret;
>  }
>  
> +static ssize_t uleds_write(struct file *file, const char __user *buffer,
> +	size_t count, loff_t *ppos)
> +{
> +	struct uleds_device *udev = file->private_data;
> +	int ret;
> +
> +	if (count == 0)
> +		return 0;
> +	if (count != sizeof(struct uleds_user_dev))
> +		return -EINVAL;
> +
> +	ret = dev_setup(udev, buffer);
> +	if (ret < 0)
> +		return ret;

Nit: '\n'

> +	return count;
> +}
> +
>  static ssize_t uleds_read(struct file *file, char __user *buffer, size_t count,
>  			  loff_t *ppos)
>  {
> @@ -179,6 +192,71 @@ static __poll_t uleds_poll(struct file *file, poll_table *wait)
>  	return 0;
>  }
>  
> +/*
> + * Trigger name validation: Allow only alphanumeric, hyphen or underscore.
> + */

This is clearly a single line comment.

The first part is also invalidated by the function's nomenclature.

> +static bool is_trigger_name_valid(const char *name)
> +{
> +	size_t i;
> +
> +	if (name[0] == '\0')
> +		return false;
> +
> +	for (i = 0; i < TRIG_NAME_MAX; i++) {

Shouldn't this be <=?

> +		if (name[i] == '\0')
> +			break;
> +		if (!isalnum(name[i]) && name[i] != '-' && name[i] != '_')
> +			return false;
> +	}

Nit: '\n'

> +	/* Length check and avoid any special names. */
> +	return ((i < TRIG_NAME_MAX) &&

What if the name is exactly the max?

> +		(strcmp(name, "default") != 0));

This can go on the line above.

How about?

   return !((i < TRIG_NAME_MAX) && (strcmp(name, "default"))

> +}
> +
> +static long uleds_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> +	struct uleds_device *udev = file->private_data;
> +	struct uleds_user_trigger default_trigger;
> +	int retval = 0;
> +
> +	switch (cmd) {
> +	case ULEDS_IOC_DEV_SETUP:
> +		retval = dev_setup(udev, (const char __user *)arg);
> +		break;
> +
> +	case ULEDS_IOC_SET_DEFAULT_TRIGGER:
> +		retval = copy_from_user(&default_trigger,
> +			(struct uleds_user_trigger __user *)arg,
> +			sizeof(default_trigger));

Subsequent lines need tabbing out to the open parenthesis.

> +		if (retval)
> +			return retval;

Please open things out.

Squished code is hard to read code.

> +		retval = mutex_lock_interruptible(&udev->mutex);
> +		if (retval)
> +			return retval;
> +		if (default_trigger.name[0] == '\0') {
> +			udev->led_cdev.default_trigger = NULL;
> +		} else {
> +			if (!is_trigger_name_valid(default_trigger.name)) {
> +				mutex_unlock(&udev->mutex);
> +				return -EINVAL;
> +			}
> +			memcpy(&udev->default_trigger, &default_trigger,
> +				sizeof(udev->default_trigger));
> +			udev->led_cdev.default_trigger = udev->default_trigger.name;
> +		}
> +		if (udev->state == ULEDS_STATE_REGISTERED)
> +			led_trigger_set_default(&udev->led_cdev);
> +		mutex_unlock(&udev->mutex);
> +		break;
> +
> +	default:
> +		retval = -ENOIOCTLCMD;
> +		break;
> +	}
> +
> +	return retval;
> +}
> +
>  static int uleds_release(struct inode *inode, struct file *file)
>  {
>  	struct uleds_device *udev = file->private_data;
> @@ -200,6 +278,7 @@ static const struct file_operations uleds_fops = {
>  	.read		= uleds_read,
>  	.write		= uleds_write,
>  	.poll		= uleds_poll,
> +	.unlocked_ioctl	= uleds_ioctl,
>  };
>  
>  static struct miscdevice uleds_misc = {
> diff --git a/include/uapi/linux/uleds.h b/include/uapi/linux/uleds.h
> index 4d32a39965f8..0e9861a8c31f 100644
> --- a/include/uapi/linux/uleds.h
> +++ b/include/uapi/linux/uleds.h
> @@ -15,11 +15,39 @@
>  #ifndef _UAPI__ULEDS_H_
>  #define _UAPI__ULEDS_H_
>  
> -#define LED_MAX_NAME_SIZE	64
> +#define LED_MAX_NAME_SIZE		64
> +#define ULEDS_TRIGGER_MAX_NAME_SIZE	64
>  
>  struct uleds_user_dev {
>  	char name[LED_MAX_NAME_SIZE];
>  	int max_brightness;
>  };
>  
> +struct uleds_user_trigger {
> +	char name[ULEDS_TRIGGER_MAX_NAME_SIZE];
> +};
> +
> +

Remove this double line spacing.

> +/* ioctl commands */
> +
> +#define ULEDS_IOC_MAGIC		'l'
> +
> +/*
> + * Initial setup.
> + * E.g.:
> + *	int retval;
> + *	struct uleds_user_dev dev_setup = { "mainboard:green:battery", 255 };
> + *	retval = ioctl(fd, ULEDS_IOC_DEV_SETUP, &dev_setup);
> + */
> +#define ULEDS_IOC_DEV_SETUP		_IOW(ULEDS_IOC_MAGIC, 0x01, struct uleds_user_dev)
> +
> +/*
> + * Set the default trigger.
> + * E.g.:
> + *	int retval;
> + *	struct uleds_user_trigger default_trigger = { "battery" };
> + *	retval = ioctl(fd, ULEDS_IOC_SET_DEFAULT_TRIGGER, &default_trigger);
> + */
> +#define ULEDS_IOC_SET_DEFAULT_TRIGGER	_IOW(ULEDS_IOC_MAGIC, 0x02, struct uleds_user_trigger)
> +
>  #endif /* _UAPI__ULEDS_H_ */
> -- 
> 2.48.1
> 
> 

-- 
Lee Jones [李琼斯]

      reply	other threads:[~2025-04-04 12:30 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-03-17  2:22 [PATCH v3 1/2] leds: uleds documentation fixes Craig McQueen
2025-03-17  2:22 ` [PATCH v3 2/2] leds: Add some setup ioctls to uleds driver Craig McQueen
2025-04-04 12:30   ` Lee Jones [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250404123045.GA278642@google.com \
    --to=lee@kernel.org \
    --cc=craig@mcqueen.au \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-leds@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox