[PATCH v2 4/6] Add GPIO v2 ioctl decoding

Dmitry V. Levin ldv at altlinux.org
Tue Jan 19 20:14:32 UTC 2021


On Mon, Jan 11, 2021 at 11:09:10PM +0800, Kent Gibson wrote:
> Add decoding of GPIO uAPI v2 ioctls added in Linux v5.10.
> 
> Signed-off-by: Kent Gibson <warthog618 at gmail.com>
> ---
>  gpio_ioctl.c                  | 304 ++++++++++++++++++++++++++++++++++
>  xlat/gpio_v2_line_attr_ids.in |   5 +
>  xlat/gpio_v2_line_flags.in    |  13 ++
>  3 files changed, 322 insertions(+)
>  create mode 100644 xlat/gpio_v2_line_attr_ids.in
>  create mode 100644 xlat/gpio_v2_line_flags.in
> 
> diff --git a/gpio_ioctl.c b/gpio_ioctl.c
> index 7bd5dfc8..f1acaaad 100644
> --- a/gpio_ioctl.c
> +++ b/gpio_ioctl.c
> @@ -32,6 +32,68 @@ struct gpiohandle_config {
>  # define GPIO_GET_LINEINFO_UNWATCH_IOCTL _IOWR(0xB4, 0x0C, uint32_t)
>  #endif
>  
> +#ifndef GPIO_V2_GET_LINEINFO_IOCTL
> +/* added in Linux v5.10 */
> +# define GPIO_V2_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x05, struct gpio_v2_line_info)
> +# define GPIO_V2_GET_LINEINFO_WATCH_IOCTL _IOWR(0xB4, 0x06, struct gpio_v2_line_info)
> +# define GPIO_V2_GET_LINE_IOCTL _IOWR(0xB4, 0x07, struct gpio_v2_line_request)
> +# define GPIO_V2_LINE_SET_CONFIG_IOCTL _IOWR(0xB4, 0x0D, struct gpio_v2_line_config)
> +# define GPIO_V2_LINE_GET_VALUES_IOCTL _IOWR(0xB4, 0x0E, struct gpio_v2_line_values)
> +# define GPIO_V2_LINE_SET_VALUES_IOCTL _IOWR(0xB4, 0x0F, struct gpio_v2_line_values)
> +
> +# define GPIO_MAX_NAME_SIZE 32
> +# define GPIO_V2_LINES_MAX 64
> +# define GPIO_V2_LINE_NUM_ATTRS_MAX 10
> +
> +struct gpio_v2_line_values {
> +	uint64_t bits;
> +	uint64_t mask;
> +};
> +
> +struct gpio_v2_line_attribute {
> +	uint32_t id;
> +	uint32_t padding;
> +	union {
> +		uint64_t flags;
> +		uint64_t values;
> +		uint32_t debounce_period_us;
> +	};
> +};
> +
> +struct gpio_v2_line_config_attribute {
> +	struct gpio_v2_line_attribute attr;
> +	uint64_t mask;
> +};
> +
> +struct gpio_v2_line_config {
> +	uint64_t flags;
> +	uint32_t num_attrs;
> +	uint32_t padding[5];
> +	struct gpio_v2_line_config_attribute attrs[GPIO_V2_LINE_NUM_ATTRS_MAX];
> +};
> +
> +struct gpio_v2_line_request {
> +	uint32_t offsets[GPIO_V2_LINES_MAX];
> +	char consumer[GPIO_MAX_NAME_SIZE];
> +	struct gpio_v2_line_config config;
> +	uint32_t num_lines;
> +	uint32_t event_buffer_size;
> +	uint32_t padding[5];
> +	int32_t fd;
> +};
> +
> +struct gpio_v2_line_info {
> +	char name[GPIO_MAX_NAME_SIZE];
> +	char consumer[GPIO_MAX_NAME_SIZE];
> +	uint32_t offset;
> +	uint32_t num_attrs;
> +	uint64_t flags;
> +	struct gpio_v2_line_attribute attrs[GPIO_V2_LINE_NUM_ATTRS_MAX];
> +	uint32_t padding[4];
> +};
> +
> +#endif /* GPIO_V2_GET_LINEINFO_IOCTL */
> +
>  static int
>  print_gpiochip_info(struct tcb *const tcp, const kernel_ulong_t arg)
>  {
> @@ -230,6 +292,237 @@ print_gpiohandle_set_config(struct tcb *const tcp, const kernel_ulong_t arg)
>  	return RVAL_IOCTL_DECODED;
>  }
>  
> +#include "xlat/gpio_v2_line_flags.h"
> +#include "xlat/gpio_v2_line_attr_ids.h"
> +
> +static void
> +print_gpio_v2_line_attribute(struct tcb *const tcp,
> +			     struct gpio_v2_line_attribute *attr)
> +{
> +	switch (attr->id) {
> +	case GPIO_V2_LINE_ATTR_ID_FLAGS:
> +		PRINT_FIELD_FLAGS("", *attr, flags, gpio_v2_line_flags,
> +				  "GPIO_V2_LINE_FLAG_???");
> +		break;
> +	case GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES:
> +		PRINT_FIELD_X("", *attr, values);
> +		break;
> +	case GPIO_V2_LINE_ATTR_ID_DEBOUNCE:
> +		PRINT_FIELD_U("", *attr, debounce_period_us);
> +		break;
> +	default:
> +		tprintf("(id=%u)=%#" PRIx64, attr->id, (uint64_t)attr->values);
> +		break;

We already discussed this before, but I'm still not quite happy with the
output because (id=%u)=%#x is very odd looking thing indeed, it doesn't
look similar to any valid C construct I'm aware of, and we strive to
produce a C-like output.

What if we invent a non-exiting field and print it instead?  For example,

	PRINT_FIELD_U("", *attr, id);
#define unknown_data values
	PRINT_FIELD_X("", *attr, unknown_data);
#undef unknown_data

> +	}
> +}
> +
> +static void
> +print_gpio_v2_line_config_attribute(struct tcb *const tcp,
> +				    struct gpio_v2_line_config_attribute *attr)
> +{
> +	tprints("{");
> +	print_gpio_v2_line_attribute(tcp, &attr->attr);
> +	PRINT_FIELD_X(", ", *attr, mask);
> +	tprints("}");
> +}
> +
> +static bool
> +print_gpio_v2_line_attr_array_member(struct tcb *tcp, void *elem_buf,
> +				     size_t elem_size, void *data)
> +{
> +	struct gpio_v2_line_attribute *la;
> +
> +	la = (struct gpio_v2_line_attribute *)elem_buf;
> +	if (la->padding) {
> +		PRINT_FIELD_X("{", *la, padding);
> +		tprints(", ");
> +	}
> +	print_gpio_v2_line_attribute(tcp, la);
> +	if (la->padding)
> +		tprints("}");
> +
> +	return true;
> +}

If struct gpio_v2_line_attribute a structure regardless of the value
stored in la->padding, then I suggest to print "{" and "}"
unconditionally.

> +
> +
> +static bool
> +print_gpio_v2_line_config_attr_array_member(struct tcb *tcp, void *elem_buf,
> +					    size_t elem_size, void *data)
> +{
> +	print_gpio_v2_line_config_attribute(tcp,
> +		(struct gpio_v2_line_config_attribute *)elem_buf);
> +
> +	return true;
> +}
> +
> +static void
> +print_gpio_v2_line_config(struct tcb *const tcp, struct gpio_v2_line_config *lc)
> +{
> +	uint32_t num_attrs = lc->num_attrs;
> +
> +	PRINT_FIELD_FLAGS("{", *lc, flags, gpio_v2_line_flags,
> +			  "GPIO_V2_LINE_FLAG_???");
> +	PRINT_FIELD_U(", ", *lc, num_attrs);
> +	if (!IS_ARRAY_ZERO(lc->padding))
> +		PRINT_FIELD_X_ARRAY(", ", *lc, padding);
> +	if (num_attrs) {
> +		if (num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX)
> +			num_attrs = GPIO_V2_LINE_NUM_ATTRS_MAX;
> +		tprints(", attrs=");
> +		print_local_array_ex(tcp, lc->attrs, num_attrs,
> +			sizeof(lc->attrs[0]),
> +			print_gpio_v2_line_config_attr_array_member,
> +			NULL, 0, NULL, NULL);
> +	}

Now that we've got PRINT_FIELD_ARRAY_UPTO, please consider using it
instead.

> +	tprints("}");
> +}
> +
> +static void
> +print_gpio_v2_line_values(struct tcb *const tcp, struct gpio_v2_line_values *vals)
> +{
> +	PRINT_FIELD_X("{", *vals, bits);
> +	PRINT_FIELD_X(", ", *vals, mask);
> +	tprints("}");
> +}
> +
> +static int
> +print_gpio_v2_line_info(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct gpio_v2_line_info li;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &li))
> +		return RVAL_IOCTL_DECODED;
> +
> +	if (entering(tcp)) {
> +		PRINT_FIELD_U("{", li, offset);
> +		tprints("}");
> +		return 0;
> +	}
> +
> +	/* exiting */
> +	PRINT_FIELD_CSTRING("{", li, name);
> +	PRINT_FIELD_CSTRING(", ", li, consumer);
> +	PRINT_FIELD_FLAGS(", ", li, flags, gpio_v2_line_flags, "GPIO_V2_LINE_FLAG_???");
> +	PRINT_FIELD_U(", ", li, num_attrs);
> +	if (li.num_attrs) {
> +		if (li.num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX)
> +			li.num_attrs = GPIO_V2_LINE_NUM_ATTRS_MAX;
> +		tprints(", attrs=");
> +		print_local_array_ex(tcp, li.attrs, li.num_attrs,
> +			sizeof(li.attrs[0]), print_gpio_v2_line_attr_array_member,
> +			NULL, 0, NULL, NULL);
> +	}

Likewise, please consider using PRINT_FIELD_ARRAY_UPTO.

> +	if (!IS_ARRAY_ZERO(li.padding))
> +		PRINT_FIELD_X_ARRAY(", ", li, padding);
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +print_gpio_v2_line_request(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct gpio_v2_line_request lr;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &lr))
> +		return RVAL_IOCTL_DECODED;
> +
> +	if (entering(tcp)) {
> +		PRINT_FIELD_U("{", lr, num_lines);
> +		if (lr.num_lines > GPIO_V2_LINES_MAX)
> +			lr.num_lines = GPIO_V2_LINES_MAX;
> +		tprints(", offsets=");
> +		print_local_array_ex(tcp, lr.offsets, lr.num_lines,
> +			sizeof(lr.offsets[0]), print_uint32_array_member,
> +			NULL, 0, NULL, NULL);

Likewise, please consider using PRINT_FIELD_ARRAY_UPTO.

> +		tprints(", config=");
> +		print_gpio_v2_line_config(tcp, &lr.config);
> +		PRINT_FIELD_CSTRING(", ", lr, consumer);
> +		if (lr.event_buffer_size)
> +			PRINT_FIELD_U(", ", lr, event_buffer_size);
> +		if (!IS_ARRAY_ZERO(lr.padding))
> +			PRINT_FIELD_X_ARRAY(", ", lr, padding);
> +		tprints("}");
> +		return 0;
> +	}
> +
> +	/* exiting */
> +	PRINT_FIELD_FD("{", lr, fd, tcp);
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +print_gpio_v2_line_get_values(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct gpio_v2_line_values vals;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &vals))
> +		return RVAL_IOCTL_DECODED;
> +
> +	if (entering(tcp)) {
> +		PRINT_FIELD_X("{", vals, mask);
> +		tprints("}");
> +		return 0;
> +	}
> +
> +	/* exiting */
> +	PRINT_FIELD_X("{", vals, bits);
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +print_gpio_v2_line_set_values(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct gpio_v2_line_values vals;
> +
> +	tprints(", ");
> +	if (umove_or_printaddr(tcp, arg, &vals))
> +		return RVAL_IOCTL_DECODED;
> +
> +	print_gpio_v2_line_values(tcp, &vals);
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +print_gpio_v2_line_set_config(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct gpio_v2_line_config lc;
> +
> +	tprints(", ");
> +	if (umove_or_printaddr(tcp, arg, &lc))
> +		return RVAL_IOCTL_DECODED;
> +
> +	print_gpio_v2_line_config(tcp, &lc);
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
>  int
>  gpio_ioctl(struct tcb *const tcp, const unsigned int code,
>  	   const kernel_ulong_t arg)
> @@ -239,6 +532,17 @@ gpio_ioctl(struct tcb *const tcp, const unsigned int code,
>  		return print_gpiochip_info(tcp, arg);
>  	case GPIO_GET_LINEINFO_UNWATCH_IOCTL:
>  		return print_gpioline_info_unwatch(tcp, arg);
> +	case GPIO_V2_GET_LINEINFO_IOCTL:
> +	case GPIO_V2_GET_LINEINFO_WATCH_IOCTL:
> +		return print_gpio_v2_line_info(tcp, arg);
> +	case GPIO_V2_GET_LINE_IOCTL:
> +		return print_gpio_v2_line_request(tcp, arg);
> +	case GPIO_V2_LINE_SET_CONFIG_IOCTL:
> +		return print_gpio_v2_line_set_config(tcp, arg);
> +	case GPIO_V2_LINE_GET_VALUES_IOCTL:
> +		return print_gpio_v2_line_get_values(tcp, arg);
> +	case GPIO_V2_LINE_SET_VALUES_IOCTL:
> +		return print_gpio_v2_line_set_values(tcp, arg);
>  	case GPIO_GET_LINEINFO_IOCTL:
>  	case GPIO_GET_LINEINFO_WATCH_IOCTL:
>  		return print_gpioline_info(tcp, arg);
> diff --git a/xlat/gpio_v2_line_attr_ids.in b/xlat/gpio_v2_line_attr_ids.in
> new file mode 100644
> index 00000000..5217e400
> --- /dev/null
> +++ b/xlat/gpio_v2_line_attr_ids.in
> @@ -0,0 +1,5 @@
> +#enum
> +#include <linux/gpio.h>
> +GPIO_V2_LINE_ATTR_ID_FLAGS		0x00000001
> +GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES	0x00000002
> +GPIO_V2_LINE_ATTR_ID_DEBOUNCE		0x00000003
> diff --git a/xlat/gpio_v2_line_flags.in b/xlat/gpio_v2_line_flags.in
> new file mode 100644
> index 00000000..04ffa9e4
> --- /dev/null
> +++ b/xlat/gpio_v2_line_flags.in
> @@ -0,0 +1,13 @@
> +#enum
> +#include <linux/gpio.h>
> +GPIO_V2_LINE_FLAG_USED			0x00000001
> +GPIO_V2_LINE_FLAG_ACTIVE_LOW		0x00000002
> +GPIO_V2_LINE_FLAG_INPUT			0x00000004
> +GPIO_V2_LINE_FLAG_OUTPUT		0x00000008
> +GPIO_V2_LINE_FLAG_EDGE_RISING		0x00000010
> +GPIO_V2_LINE_FLAG_EDGE_FALLING		0x00000020
> +GPIO_V2_LINE_FLAG_OPEN_DRAIN		0x00000040
> +GPIO_V2_LINE_FLAG_OPEN_SOURCE		0x00000080
> +GPIO_V2_LINE_FLAG_BIAS_PULL_UP		0x00000100
> +GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN	0x00000200
> +GPIO_V2_LINE_FLAG_BIAS_DISABLED		0x00000400

-- 
ldv


More information about the Strace-devel mailing list