Re: [PATCH v2] gpio: add userspace ABI for GPIO line information

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On Monday, February 15, 2016 02:20:35 PM Linus Walleij wrote:
> This adds a GPIO line ABI for getting name, label and a few select
> flags from the kernel.
> 
> This hides the kernel internals and only tells userspace what it
> may need to know: the different in-kernel consumers are masked
> behind the flag "kernel" and that is all userspace needs to know.
> 
> However electric characteristics like active low, open drain etc
> are reflected to userspace, as this is important information.
> 
> We provide information on all lines on all chips, later on we will
> likely add a flag for the chardev consumer so we can filter and
> display only the lines userspace actually uses in e.g. lsgpio,
> but then we first need an ABI for userspace to grab and use
> (get/set/select direction) a GPIO line.
> 
> Sample output from "lsgpio" on ux500:
> 
> GPIO chip: gpiochip7, "8011e000.gpio", 32 GPIO lines
>         line 0: unnamed unlabeled
>         line 1: unnamed unlabeled
> (...)
>         line 25: unnamed "SFH7741 Proximity Sensor" [kernel output open-drain]
>         line 26: unnamed unlabeled
> (...)
> 
> Signed-off-by: Linus Walleij <linus.walleij@xxxxxxxxxx>
> ---
> ChangeLog v1->v2:
> - Fix unitialized lineinfo variables, especially flags,
>   on both the userspace and kernelspace side.
> - Pretty print.
> ---
>  drivers/gpio/gpiolib.c    | 51 ++++++++++++++++++++++++--
>  include/uapi/linux/gpio.h | 26 ++++++++++++++
>  tools/gpio/gpio-utils.h   |  2 ++
>  tools/gpio/lsgpio.c       | 91 ++++++++++++++++++++++++++++++++++++++++-------
>  4 files changed, 156 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index a022e9249f69..a38265dec794 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -331,14 +331,15 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  	struct gpio_device *gdev = filp->private_data;
>  	struct gpio_chip *chip = gdev->chip;
>  	int __user *ip = (int __user *)arg;
> -	struct gpiochip_info chipinfo;
>  
>  	/* We fail any subsequent ioctl():s when the chip is gone */
>  	if (!chip)
>  		return -ENODEV;
>  
> +	/* Fill in the struct and pass to userspace */
>  	if (cmd == GPIO_GET_CHIPINFO_IOCTL) {
> -		/* Fill in the struct and pass to userspace */
> +		struct gpiochip_info chipinfo;
> +
>  		strncpy(chipinfo.name, dev_name(&gdev->dev),
>  			sizeof(chipinfo.name));
>  		chipinfo.name[sizeof(chipinfo.name)-1] = '\0';
> @@ -349,6 +350,52 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  		if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
>  			return -EFAULT;
>  		return 0;
> +	} else if (cmd == GPIO_GET_LINEINFO_IOCTL) {
> +		struct gpioline_info lineinfo;
> +		struct gpio_desc *desc;
> +
> +		if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
> +			return -EFAULT;
> +		if (lineinfo.line_offset > gdev->ngpio)
> +			return -EINVAL;
> +
> +		desc = &gdev->descs[lineinfo.line_offset];
> +		if (desc->name) {
> +			strncpy(lineinfo.name, desc->name,
> +				sizeof(lineinfo.name));
> +			lineinfo.name[sizeof(lineinfo.name)-1] = '\0';
> +		} else {
> +			lineinfo.name[0] = '\0';
> +		}
> +		if (desc->label) {
> +			strncpy(lineinfo.label, desc->label,
> +				sizeof(lineinfo.label));
> +			lineinfo.label[sizeof(lineinfo.label)-1] = '\0';
> +		} else {
> +			lineinfo.label[0] = '\0';
> +		}
> +
> +		/*
> +		 * Userspace only need to know that the kernel is using
> +		 * this GPIO so it can't use it.
> +		 */
> +		lineinfo.flags = 0;
> +		if (desc->flags & (FLAG_REQUESTED | FLAG_IS_HOGGED |
> +				   FLAG_USED_AS_IRQ | FLAG_EXPORT |
> +				   FLAG_SYSFS))
> +			lineinfo.flags |= GPIOLINE_FLAG_KERNEL;
> +		if (desc->flags & FLAG_IS_OUT)
> +			lineinfo.flags |= GPIOLINE_FLAG_IS_OUT;
> +		if (desc->flags & FLAG_ACTIVE_LOW)
> +			lineinfo.flags |= GPIOLINE_FLAG_ACTIVE_LOW;
> +		if (desc->flags & FLAG_OPEN_DRAIN)
> +			lineinfo.flags |= GPIOLINE_FLAG_OPEN_DRAIN;
> +		if (desc->flags & FLAG_OPEN_SOURCE)
> +			lineinfo.flags |= GPIOLINE_FLAG_OPEN_SOURCE;

I just noticed while working on DT name parsing that these flags are bit
numbers and not the real bits. So we have to use test_bit() here. I will
send a patch later.

Best Regards,

Markus

> +
> +		if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
> +			return -EFAULT;
> +		return 0;
>  	}
>  	return -EINVAL;
>  }
> diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h
> index 3f93e1bcd3dd..416ce47f2291 100644
> --- a/include/uapi/linux/gpio.h
> +++ b/include/uapi/linux/gpio.h
> @@ -25,6 +25,32 @@ struct gpiochip_info {
>  	__u32 lines;
>  };
>  
> +/* Line is in use by the kernel */
> +#define GPIOLINE_FLAG_KERNEL		(1UL << 0)
> +#define GPIOLINE_FLAG_IS_OUT		(1UL << 1)
> +#define GPIOLINE_FLAG_ACTIVE_LOW	(1UL << 2)
> +#define GPIOLINE_FLAG_OPEN_DRAIN	(1UL << 3)
> +#define GPIOLINE_FLAG_OPEN_SOURCE	(1UL << 4)
> +
> +/**
> + * struct gpioline_info - Information about a certain GPIO line
> + * @line_offset: the local offset on this GPIO device, fill in when
> + * requesting information from the kernel
> + * @flags: various flags for this line
> + * @name: the name of this GPIO line
> + * @label: a functional name for this GPIO line
> + * @kernel: this GPIO is in use by the kernel
> + * @out: this GPIO is an output line (false means it is an input)
> + * @active_low: this GPIO is active low
> + */
> +struct gpioline_info {
> +	__u32 line_offset;
> +	__u32 flags;
> +	char name[32];
> +	char label[32];
> +};
> +
>  #define GPIO_GET_CHIPINFO_IOCTL _IOR('o', 0x01, struct gpiochip_info)
> +#define GPIO_GET_LINEINFO_IOCTL _IOWR('o', 0x02, struct gpioline_info)
>  
>  #endif /* _UAPI_GPIO_H_ */
> diff --git a/tools/gpio/gpio-utils.h b/tools/gpio/gpio-utils.h
> index b18209a45ad3..5f57133b8c04 100644
> --- a/tools/gpio/gpio-utils.h
> +++ b/tools/gpio/gpio-utils.h
> @@ -16,6 +16,8 @@
>  
>  #include <string.h>
>  
> +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
> +
>  static inline int check_prefix(const char *str, const char *prefix)
>  {
>  	return strlen(str) > strlen(prefix) &&
> diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c
> index 692233f561fb..5535ce81f8f7 100644
> --- a/tools/gpio/lsgpio.c
> +++ b/tools/gpio/lsgpio.c
> @@ -26,12 +26,56 @@
>  
>  #include "gpio-utils.h"
>  
> +struct gpio_flag {
> +	char *name;
> +	unsigned long mask;
> +};
> +
> +struct gpio_flag flagnames[] = {
> +	{
> +		.name = "kernel",
> +		.mask = GPIOLINE_FLAG_KERNEL,
> +	},
> +	{
> +		.name = "output",
> +		.mask = GPIOLINE_FLAG_IS_OUT,
> +	},
> +	{
> +		.name = "active-low",
> +		.mask = GPIOLINE_FLAG_ACTIVE_LOW,
> +	},
> +	{
> +		.name = "open-drain",
> +		.mask = GPIOLINE_FLAG_OPEN_DRAIN,
> +	},
> +	{
> +		.name = "open-source",
> +		.mask = GPIOLINE_FLAG_OPEN_SOURCE,
> +	},
> +};
> +
> +void print_flags(unsigned long flags)
> +{
> +	int i;
> +	int printed = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(flagnames); i++) {
> +		if (flags & flagnames[i].mask) {
> +			if (printed)
> +				fprintf(stdout, " ");
> +			fprintf(stdout, "%s", flagnames[i].name);
> +			printed++;
> +		}
> +	}
> +}
> +
>  int list_device(const char *device_name)
>  {
>  	struct gpiochip_info cinfo;
>  	char *chrdev_name;
>  	int fd;
>  	int ret;
> +	int i;
>  
>  	ret = asprintf(&chrdev_name, "/dev/%s", device_name);
>  	if (ret < 0)
> @@ -41,32 +85,55 @@ int list_device(const char *device_name)
>  	if (fd == -1) {
>  		ret = -errno;
>  		fprintf(stderr, "Failed to open %s\n", chrdev_name);
> -		goto free_chrdev_name;
> +		goto exit_close_error;
>  	}
>  
>  	/* Inspect this GPIO chip */
>  	ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo);
>  	if (ret == -1) {
>  		ret = -errno;
> -		fprintf(stderr, "Failed to retrieve GPIO fd\n");
> -		if (close(fd) == -1)
> -			perror("Failed to close GPIO character device file");
> -
> -		goto free_chrdev_name;
> +		perror("Failed to issue CHIPINFO IOCTL\n");
> +		goto exit_close_error;
>  	}
>  	fprintf(stdout, "GPIO chip: %s, \"%s\", %u GPIO lines\n",
>  		cinfo.name, cinfo.label, cinfo.lines);
>  
> -	if (close(fd) == -1)  {
> -		ret = -errno;
> -		goto free_chrdev_name;
> +	/* Loop over the lines and print info */
> +	for (i = 0; i < cinfo.lines; i++) {
> +		struct gpioline_info linfo;
> +
> +		memset(&linfo, 0, sizeof(linfo));
> +		linfo.line_offset = i;
> +
> +		ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &linfo);
> +		if (ret == -1) {
> +			ret = -errno;
> +			perror("Failed to issue LINEINFO IOCTL\n");
> +			goto exit_close_error;
> +		}
> +		fprintf(stdout, "\tline %d:", linfo.line_offset);
> +		if (linfo.name[0])
> +			fprintf(stdout, " %s", linfo.name);
> +		else
> +			fprintf(stdout, " unnamed");
> +		if (linfo.label[0])
> +			fprintf(stdout, " \"%s\"", linfo.label);
> +		else
> +			fprintf(stdout, " unlabeled");
> +		if (linfo.flags) {
> +			fprintf(stdout, " [");
> +			print_flags(linfo.flags);
> +			fprintf(stdout, "]");
> +		}
> +		fprintf(stdout, "\n");
> +
>  	}
>  
> -free_chrdev_name:
> +exit_close_error:
> +	if (close(fd) == -1)
> +		perror("Failed to close GPIO character device file");
>  	free(chrdev_name);
> -
>  	return ret;
> -
>  }
>  
>  void print_usage(void)
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

Attachment: signature.asc
Description: This is a digitally signed message part.


[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux