[PATCH v2 2/6] Add GPIO ioctl decoding

Kent Gibson warthog618 at gmail.com
Mon Jan 11 15:09:08 UTC 2021


Decode the GPIO character device ioctls first introduced in Linux v4.8,
as well as those added in subsequent kernel releases up to Linux v5.7.

Signed-off-by: Kent Gibson <warthog618 at gmail.com>
---
 Makefile.am               |   1 +
 configure.ac              |   1 +
 defs.h                    |   1 +
 gpio_ioctl.c              | 259 ++++++++++++++++++++++++++++++++++++++
 ioctl.c                   |   4 +
 xlat/gpio_event_flags.in  |   3 +
 xlat/gpio_handle_flags.in |   8 ++
 xlat/gpio_line_flags.in   |   8 ++
 8 files changed, 285 insertions(+)
 create mode 100644 gpio_ioctl.c
 create mode 100644 xlat/gpio_event_flags.in
 create mode 100644 xlat/gpio_handle_flags.in
 create mode 100644 xlat/gpio_line_flags.in

diff --git a/Makefile.am b/Makefile.am
index c0199f65..5fbee7b5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -139,6 +139,7 @@ libstrace_a_SOURCES =	\
 	getpagesize.c \
 	getpid.c	\
 	getrandom.c	\
+	gpio_ioctl.c	\
 	hdio.c		\
 	hostname.c	\
 	inotify.c	\
diff --git a/configure.ac b/configure.ac
index a736e70b..5de50f04 100644
--- a/configure.ac
+++ b/configure.ac
@@ -415,6 +415,7 @@ AC_CHECK_HEADERS(m4_normalize([
 	linux/dqblk_xfs.h
 	linux/falloc.h
 	linux/fib_rules.h
+	linux/gpio.h
 	linux/hiddev.h
 	linux/ip_vs.h
 	linux/ipc.h
diff --git a/defs.h b/defs.h
index 8a12f751..2cfb550a 100644
--- a/defs.h
+++ b/defs.h
@@ -1216,6 +1216,7 @@ DECL_IOCTL(evdev);
 DECL_IOCTL(fs_0x94);
 DECL_IOCTL(fs_f);
 DECL_IOCTL(fs_x);
+DECL_IOCTL(gpio);
 DECL_IOCTL(inotify);
 DECL_IOCTL(kvm);
 DECL_IOCTL(nbd);
diff --git a/gpio_ioctl.c b/gpio_ioctl.c
new file mode 100644
index 00000000..7bd5dfc8
--- /dev/null
+++ b/gpio_ioctl.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2020 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "defs.h"
+#include "print_fields.h"
+
+#if HAVE_LINUX_GPIO_H
+
+#include <linux/gpio.h>
+
+#ifndef GPIOHANDLES_MAX
+# define GPIOHANDLES_MAX 64
+#endif
+
+#ifndef GPIOHANDLE_SET_CONFIG_IOCTL
+/* added in Linux v5.5 */
+# define GPIOHANDLE_SET_CONFIG_IOCTL _IOWR(0xB4, 0x0A, struct gpiohandle_config)
+struct gpiohandle_config {
+	uint32_t flags;
+	uint8_t default_values[GPIOHANDLES_MAX];
+	uint32_t padding[4];
+};
+#endif
+
+#ifndef GPIO_GET_LINEINFO_WATCH_IOCTL
+/* added in Linux v5.7 */
+# define GPIO_GET_LINEINFO_WATCH_IOCTL _IOWR(0xB4, 0x0B, struct gpioline_info)
+# define GPIO_GET_LINEINFO_UNWATCH_IOCTL _IOWR(0xB4, 0x0C, uint32_t)
+#endif
+
+static int
+print_gpiochip_info(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+	struct gpiochip_info info;
+
+	if (entering(tcp))
+		return 0;
+
+	tprints(", ");
+	if (umove_or_printaddr(tcp, arg, &info))
+		return RVAL_IOCTL_DECODED;
+
+	PRINT_FIELD_CSTRING("{", info, name);
+	PRINT_FIELD_CSTRING(", ", info, label);
+	PRINT_FIELD_U(", ", info, lines);
+	tprints("}");
+
+	return RVAL_IOCTL_DECODED;
+}
+
+#include "xlat/gpio_line_flags.h"
+
+static int
+print_gpioline_info(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+	struct gpioline_info info;
+
+	if (entering(tcp))
+		tprints(", ");
+	else if (syserror(tcp))
+		return RVAL_IOCTL_DECODED;
+	else
+		tprints(" => ");
+
+	if (umove_or_printaddr(tcp, arg, &info))
+		return RVAL_IOCTL_DECODED;
+
+	if (entering(tcp)) {
+		PRINT_FIELD_U("{", info, line_offset);
+		tprints("}");
+		return 0;
+	}
+
+	/* exiting */
+	PRINT_FIELD_FLAGS("{", info, flags, gpio_line_flags, "GPIOLINE_FLAG_???");
+	PRINT_FIELD_CSTRING(", ", info, name);
+	PRINT_FIELD_CSTRING(", ", info, consumer);
+	tprints("}");
+
+	return RVAL_IOCTL_DECODED;
+}
+
+static int
+print_gpioline_info_unwatch(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+	tprintf(", {offset=%u}", (uint32_t)arg);
+
+	return RVAL_IOCTL_DECODED;
+}
+
+#include "xlat/gpio_handle_flags.h"
+
+static int
+print_gpiohandle_request(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+	struct gpiohandle_request hr;
+
+	if (entering(tcp))
+		tprints(", ");
+	else if (syserror(tcp))
+		return RVAL_IOCTL_DECODED;
+	else
+		tprints(" => ");
+
+	if (umove_or_printaddr(tcp, arg, &hr))
+		return RVAL_IOCTL_DECODED;
+
+	if (entering(tcp)) {
+		PRINT_FIELD_U("{", hr, lines);
+		tprints(", lineoffsets=");
+		if (hr.lines > GPIOHANDLES_MAX)
+			hr.lines = GPIOHANDLES_MAX;
+		print_local_array_ex(tcp, hr.lineoffsets, hr.lines,
+			sizeof(hr.lineoffsets[0]), print_uint32_array_member,
+			NULL, 0, NULL, NULL);
+		PRINT_FIELD_FLAGS(", ", hr, flags, gpio_handle_flags,
+			"GPIOHANDLE_REQUEST_???");
+		tprints(", default_values=");
+		print_local_array_ex(tcp, hr.default_values, hr.lines,
+			sizeof(hr.default_values[0]), print_uint8_array_member,
+			NULL, 0, NULL, NULL);
+		PRINT_FIELD_CSTRING(", ", hr, consumer_label);
+		tprints("}");
+		return 0;
+	}
+
+	/* exiting */
+	PRINT_FIELD_FD("{", hr, fd, tcp);
+	tprints("}");
+
+	return RVAL_IOCTL_DECODED;
+}
+
+#include "xlat/gpio_event_flags.h"
+
+static int
+print_gpioevent_request(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+	struct gpioevent_request er;
+
+	if (entering(tcp))
+		tprints(", ");
+	else if (syserror(tcp))
+		return RVAL_IOCTL_DECODED;
+	else
+		tprints(" => ");
+
+	if (umove_or_printaddr(tcp, arg, &er))
+		return RVAL_IOCTL_DECODED;
+
+	if (entering(tcp)) {
+		PRINT_FIELD_U("{", er, lineoffset);
+		PRINT_FIELD_FLAGS(", ", er, handleflags, gpio_handle_flags,
+				  "GPIOHANDLE_REQUEST_???");
+		PRINT_FIELD_FLAGS(", ", er, eventflags, gpio_event_flags,
+				  "GPIOEVENT_REQUEST_???");
+		PRINT_FIELD_CSTRING(", ", er, consumer_label);
+		tprints("}");
+		return 0;
+	}
+
+	/* exiting */
+	PRINT_FIELD_FD("{", er, fd, tcp);
+	tprints("}");
+
+	return RVAL_IOCTL_DECODED;
+}
+
+static void
+print_handle_data(struct tcb *const tcp, struct gpiohandle_data *vals)
+{
+	tprints("{values=");
+	print_local_array(tcp, vals->values, print_uint8_array_member);
+	tprints("}");
+}
+
+static int
+print_gpiohandle_get_values(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+	struct gpiohandle_data vals;
+
+	if (entering(tcp)) {
+		tprints(", ");
+		return 0;
+	}
+	/* exiting */
+	if (syserror(tcp)) {
+		printaddr(arg);
+		return RVAL_IOCTL_DECODED;
+	}
+	if (umove_or_printaddr(tcp, arg, &vals))
+		return RVAL_IOCTL_DECODED;
+	print_handle_data(tcp, &vals);
+
+	return RVAL_IOCTL_DECODED;
+}
+
+static int
+print_gpiohandle_set_values(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+	struct gpiohandle_data vals;
+
+	tprints(", ");
+	if (umove_or_printaddr(tcp, arg, &vals))
+		return RVAL_IOCTL_DECODED;
+
+	print_handle_data(tcp, &vals);
+
+	return RVAL_IOCTL_DECODED;
+}
+
+static int
+print_gpiohandle_set_config(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+	struct gpiohandle_config hc;
+
+	tprints(", ");
+	if (umove_or_printaddr(tcp, arg, &hc))
+		return RVAL_IOCTL_DECODED;
+
+	PRINT_FIELD_FLAGS("{", hc, flags, gpio_handle_flags, "GPIOHANDLE_REQUEST_???");
+	tprints(", default_values=");
+	print_local_array(tcp, hc.default_values, print_uint8_array_member);
+	tprints("}");
+
+	return RVAL_IOCTL_DECODED;
+}
+
+int
+gpio_ioctl(struct tcb *const tcp, const unsigned int code,
+	   const kernel_ulong_t arg)
+{
+	switch (code) {
+	case GPIO_GET_CHIPINFO_IOCTL:
+		return print_gpiochip_info(tcp, arg);
+	case GPIO_GET_LINEINFO_UNWATCH_IOCTL:
+		return print_gpioline_info_unwatch(tcp, arg);
+	case GPIO_GET_LINEINFO_IOCTL:
+	case GPIO_GET_LINEINFO_WATCH_IOCTL:
+		return print_gpioline_info(tcp, arg);
+	case GPIO_GET_LINEHANDLE_IOCTL:
+		return print_gpiohandle_request(tcp, arg);
+	case GPIO_GET_LINEEVENT_IOCTL:
+		return print_gpioevent_request(tcp, arg);
+	case GPIOHANDLE_GET_LINE_VALUES_IOCTL:
+		return print_gpiohandle_get_values(tcp, arg);
+	case GPIOHANDLE_SET_LINE_VALUES_IOCTL:
+		return print_gpiohandle_set_values(tcp, arg);
+	case GPIOHANDLE_SET_CONFIG_IOCTL:
+		return print_gpiohandle_set_config(tcp, arg);
+	}
+	return RVAL_DECODED;
+}
+
+#endif /* HAVE_LINUX_GPIO_H */
diff --git a/ioctl.c b/ioctl.c
index f3f6f0ab..944834df 100644
--- a/ioctl.c
+++ b/ioctl.c
@@ -345,6 +345,10 @@ ioctl_decode(struct tcb *tcp)
 #ifdef HAVE_LINUX_KVM_H
 	case 0xae:
 		return kvm_ioctl(tcp, code, arg);
+#endif
+#ifdef HAVE_LINUX_GPIO_H
+	case 0xb4:
+		return gpio_ioctl(tcp, code, arg);
 #endif
 	case 0xb7:
 		return nsfs_ioctl(tcp, code, arg);
diff --git a/xlat/gpio_event_flags.in b/xlat/gpio_event_flags.in
new file mode 100644
index 00000000..c355b10b
--- /dev/null
+++ b/xlat/gpio_event_flags.in
@@ -0,0 +1,3 @@
+GPIOEVENT_REQUEST_BOTH_EDGES	0x00000003
+GPIOEVENT_REQUEST_RISING_EDGE	0x00000001
+GPIOEVENT_REQUEST_FALLING_EDGE	0x00000002
diff --git a/xlat/gpio_handle_flags.in b/xlat/gpio_handle_flags.in
new file mode 100644
index 00000000..0c75034e
--- /dev/null
+++ b/xlat/gpio_handle_flags.in
@@ -0,0 +1,8 @@
+GPIOHANDLE_REQUEST_INPUT	0x00000001
+GPIOHANDLE_REQUEST_OUTPUT	0x00000002
+GPIOHANDLE_REQUEST_ACTIVE_LOW	0x00000004
+GPIOHANDLE_REQUEST_OPEN_DRAIN	0x00000008
+GPIOHANDLE_REQUEST_OPEN_SOURCE	0x00000010
+GPIOHANDLE_REQUEST_BIAS_PULL_UP	0x00000020
+GPIOHANDLE_REQUEST_BIAS_PULL_DOWN	0x00000040
+GPIOHANDLE_REQUEST_BIAS_DISABLE	0x00000080
diff --git a/xlat/gpio_line_flags.in b/xlat/gpio_line_flags.in
new file mode 100644
index 00000000..a7d505e2
--- /dev/null
+++ b/xlat/gpio_line_flags.in
@@ -0,0 +1,8 @@
+GPIOLINE_FLAG_KERNEL		0x00000001
+GPIOLINE_FLAG_IS_OUT		0x00000002
+GPIOLINE_FLAG_ACTIVE_LOW	0x00000004
+GPIOLINE_FLAG_OPEN_DRAIN	0x00000008
+GPIOLINE_FLAG_OPEN_SOURCE	0x00000010
+GPIOLINE_FLAG_BIAS_PULL_UP	0x00000020
+GPIOLINE_FLAG_BIAS_PULL_DOWN	0x00000040
+GPIOLINE_FLAG_BIAS_DISABLE	0x00000080
-- 
2.30.0



More information about the Strace-devel mailing list