[v3,1/5] tools/gpio: add gpio basic opereations

Message ID 20160831094548.12574-2-bamvor.zhangjian@linaro.org
State New
Headers show

Commit Message

Bamvor Zhang Jian Aug. 31, 2016, 9:45 a.m.
From: Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>


Add basic gpio operations. User could get/set gpio value and/or flag for
specific gpio chardev.

Reference the "tools/testing/selftest/gpio/gpio-mockup-chardev.c" for
how to use it.

Signed-off-by: Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>

---
 tools/gpio/gpio-utils.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++++
 tools/gpio/gpio-utils.h | 121 +++++++++++++++++++++++++++++++++++
 2 files changed, 284 insertions(+)

-- 
1.8.4.5

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Linus Walleij Sept. 26, 2016, 7:37 p.m. | #1
On Wed, Aug 31, 2016 at 2:45 AM,  <bamvor.zhangjian@linaro.org> wrote:

> +int gpio_request(const char *device_name, unsigned int *lines,

> +                unsigned int nlines, unsigned int flag,

> +                struct gpiohandle_data *data, const char *consumer_label)


Could it be named
gpiotools_request_linehandle()?

Or the name is maybe a bit long.

It's nice if we have a name that tells all the function is doing.

> +int gpio_set_values(const int fd, struct gpiohandle_data *data)


Pls rename
gpiotools_set_values()

> +int gpio_get_values(const int fd, struct gpiohandle_data *data)


Pls rename
gpiotools_get_values()

> +int gpio_release(const int fd)


Pls rename
gpiotools_release_linehandle()

> +int gpio_gets(const char *device_name, unsigned int *lines, unsigned int nlines,

> +             unsigned int flag, struct gpiohandle_data *data)

> +int gpio_sets(const char *device_name, unsigned int *lines, unsigned int nlines,

> +             unsigned int flag, struct gpiohandle_data *data)

> +int gpio_get(const char *device_name, unsigned int line, unsigned int flag)

> +int gpio_set(const char *device_name, unsigned int line, unsigned int flag,

> +            unsigned int value)

> +int gpio_set_flag(const char *device_name, unsigned int line, unsigned int flag)


Prefix all gpiotools_*

> +/*

> + * Request the lines for gpio with device_name. Could set the default value

> + * in request.

> + * device_name:    the name of gpiochip in "/dev", such as gpiochip0.

> + * lines:         the array of which line should be requested.

> + * nline:          the total number of line

> + * flag:           input, output and so on. Reference "linux/gpio.h" for the

> + *                 meaning for flag.

> + * data:          default value when flag is GPIOHANDLE_REQUEST_OUTPUT.

> + * consumer_label: the name of consumer, such as "sysfs", "powerkey".

> + *

> + * Return value:   On success return the fd of specific gpiochip. It could be

> + *                 release by gpio_release.

> + *                On failure return the errno.

> + */

> +int gpio_request(const char *device_name, unsigned int *lines,

> +                unsigned int nlines, unsigned int flag,

> +                struct gpiohandle_data *data, const char *consumer_label);


For all of those, convert to kerneldoc (Documentation/kernel-doc-nano-HOWTO)
and move the kerneldoc above the actual implementation, keep the
header file spartan.

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bamvor Zhang Jian Sept. 28, 2016, 9:35 p.m. | #2
Hi, Michael

On 28 September 2016 at 12:16, Michael Welling <mwelling@ieee.org> wrote:
> On Wed, Aug 31, 2016 at 11:45:44AM +0200, bamvor.zhangjian@linaro.org wrote:

>> From: Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>

[...]
>> +     ret = ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, data);

>> +     if (ret == -1) {

>> +             ret = -errno;

>> +             fprintf(stderr, "Failed to issue %s (%d)\n",

>> +                     "GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret);

>> +             goto exit_close_error;

>

> This goto is unnecessary because there is no code to be skipped.

>

>> +     }

>> +

>> +exit_close_error:

>> +     return ret;

>> +}

>> +

>> +int gpio_get_values(const int fd, struct gpiohandle_data *data)

>> +{

>> +     int ret;

>> +

>> +     ret = ioctl(fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, data);

>> +     if (ret == -1) {

>> +             ret = -errno;

>> +             fprintf(stderr, "Failed to issue %s (%d)\n",

>> +                     "GPIOHANDLE_GET_LINE_VALUES_IOCTL", ret);

>> +             goto exit_close_error;

>

> Same here.

Thanks. I will fix above two in next version.
>

>> +     }

>> +

>> +exit_close_error:

>> +     return ret;

>> +}

>> +

>> +int gpio_release(const int fd)

>> +{

>> +     int ret;

>> +

>> +     ret = close(fd);

>> +     if (ret < -1)

>> +             perror("Failed to close GPIO LINEHANDLE device file");

>> +

>> +     return ret;

>> +}

>> +

>> +int gpio_gets(const char *device_name, unsigned int *lines, unsigned int nlines,

>> +           unsigned int flag, struct gpiohandle_data *data)

>> +{

>> +     int fd;

>> +     int ret;

>> +

>> +     ret = gpio_request(device_name, lines, nlines, flag, data, COMSUMER);

>> +     if (ret < 0)

>> +             return ret;

>> +

>> +     fd = ret;

>> +     ret = gpio_get_values(fd, data);

>

> The error checking is missing here.

Well, no matter the gpio_get_values fails or not. We both need to release the
line we request. But, we need the return value if gpio_get_values fails. In the
next version, I will return the errno of gpio_get_values if it fails.
>

>> +     ret = gpio_release(fd);

>

> Shouldn't we leave it up the user how they want the deal with the file handle?

> There is more system call overhead if we open and close the GPIO device for

> each access.

Yes. I understand and agree the syscall overhead you mentioned. So, I provide
the request, ops, release ops above. These gpio_[sg]ets? functions is the easy
way if the user only access the gpio in a few times.
>

>> +     return ret;

>> +}

>> +

>> +int gpio_sets(const char *device_name, unsigned int *lines, unsigned int nlines,

>> +           unsigned int flag, struct gpiohandle_data *data)

>> +{

>> +     int ret;

>> +

>> +     ret = gpio_request(device_name, lines, nlines, flag, data, COMSUMER);

>> +     if (ret < 0)

>> +             return ret;

>> +

>> +     ret = gpio_release(ret);

>> +     return ret;

>> +}

>> +

>> +int gpio_get(const char *device_name, unsigned int line, unsigned int flag)

>> +{

>> +     struct gpiohandle_data data;

>> +     unsigned int lines[] = {line};

>

> Is the lines variable really needed?

Do you mean something like gpio_gets(device_name, {line}, 1, flag, &data);?
Personally, I like the first version. Because it is more clear and compiler
will optimize it anyway. Ideas?

Regards

Bamvor
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch hide | download patch | download mbox

diff --git a/tools/gpio/gpio-utils.c b/tools/gpio/gpio-utils.c
index 8208718..3c0a35c 100644
--- a/tools/gpio/gpio-utils.c
+++ b/tools/gpio/gpio-utils.c
@@ -2,10 +2,173 @@ 
  * GPIO tools - helpers library for the GPIO tools
  *
  * Copyright (C) 2015 Linus Walleij
+ * Copyright (C) 2016 Bamvor Jian Zhang
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published by
  * the Free Software Foundation.
  */
 
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <linux/gpio.h>
 #include "gpio-utils.h"
+
+#define COMSUMER "gpio-utils"
+
+int gpio_request(const char *device_name, unsigned int *lines,
+		 unsigned int nlines, unsigned int flag,
+		 struct gpiohandle_data *data, const char *consumer_label)
+{
+	struct gpiohandle_request req;
+	char *chrdev_name;
+	int fd;
+	int i;
+	int ret;
+
+	ret = asprintf(&chrdev_name, "/dev/%s", device_name);
+	if (ret < 0)
+		return -ENOMEM;
+
+	fd = open(chrdev_name, 0);
+	if (fd == -1) {
+		ret = -errno;
+		fprintf(stderr, "Failed to open %s\n", chrdev_name);
+		goto exit_close_error;
+	}
+
+	for (i = 0; i < nlines; i++)
+		req.lineoffsets[i] = lines[i];
+
+	req.flags = flag;
+	strcpy(req.consumer_label, consumer_label);
+	req.lines = nlines;
+	if (flag & GPIOHANDLE_REQUEST_OUTPUT)
+		memcpy(req.default_values, data, sizeof(req.default_values));
+
+	ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
+	if (ret == -1) {
+		ret = -errno;
+		fprintf(stderr, "Failed to issue GET LINEHANDLE IOCTL (%d)\n",
+			ret);
+		goto exit_close_error;
+	}
+
+exit_close_error:
+	if (close(fd) == -1)
+		perror("Failed to close GPIO character device file");
+	free(chrdev_name);
+	return ret < 0 ? ret : req.fd;
+}
+
+int gpio_set_values(const int fd, struct gpiohandle_data *data)
+{
+	int ret;
+
+	ret = ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, data);
+	if (ret == -1) {
+		ret = -errno;
+		fprintf(stderr, "Failed to issue %s (%d)\n",
+			"GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret);
+		goto exit_close_error;
+	}
+
+exit_close_error:
+	return ret;
+}
+
+int gpio_get_values(const int fd, struct gpiohandle_data *data)
+{
+	int ret;
+
+	ret = ioctl(fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, data);
+	if (ret == -1) {
+		ret = -errno;
+		fprintf(stderr, "Failed to issue %s (%d)\n",
+			"GPIOHANDLE_GET_LINE_VALUES_IOCTL", ret);
+		goto exit_close_error;
+	}
+
+exit_close_error:
+	return ret;
+}
+
+int gpio_release(const int fd)
+{
+	int ret;
+
+	ret = close(fd);
+	if (ret < -1)
+		perror("Failed to close GPIO LINEHANDLE device file");
+
+	return ret;
+}
+
+int gpio_gets(const char *device_name, unsigned int *lines, unsigned int nlines,
+	      unsigned int flag, struct gpiohandle_data *data)
+{
+	int fd;
+	int ret;
+
+	ret = gpio_request(device_name, lines, nlines, flag, data, COMSUMER);
+	if (ret < 0)
+		return ret;
+
+	fd = ret;
+	ret = gpio_get_values(fd, data);
+	ret = gpio_release(fd);
+	return ret;
+}
+
+int gpio_sets(const char *device_name, unsigned int *lines, unsigned int nlines,
+	      unsigned int flag, struct gpiohandle_data *data)
+{
+	int ret;
+
+	ret = gpio_request(device_name, lines, nlines, flag, data, COMSUMER);
+	if (ret < 0)
+		return ret;
+
+	ret = gpio_release(ret);
+	return ret;
+}
+
+int gpio_get(const char *device_name, unsigned int line, unsigned int flag)
+{
+	struct gpiohandle_data data;
+	unsigned int lines[] = {line};
+
+	gpio_gets(device_name, lines, 1, flag, &data);
+	return data.values[0];
+}
+
+int gpio_set(const char *device_name, unsigned int line, unsigned int flag,
+	     unsigned int value)
+{
+	struct gpiohandle_data data;
+	unsigned int lines[] = {line};
+
+	data.values[0] = value;
+	return gpio_sets(device_name, lines, 1, flag, &data);
+}
+
+int gpio_set_flag(const char *device_name, unsigned int line, unsigned int flag)
+{
+	struct gpiohandle_data data;
+	unsigned int lines[] = {line};
+	int ret;
+
+	ret = gpio_request(device_name, lines, 1, flag, &data, COMSUMER);
+	if (ret < 0)
+		return ret;
+
+	ret = gpio_release(ret);
+	return ret;
+}
+
diff --git a/tools/gpio/gpio-utils.h b/tools/gpio/gpio-utils.h
index 5f57133..fcf87b6 100644
--- a/tools/gpio/gpio-utils.h
+++ b/tools/gpio/gpio-utils.h
@@ -24,4 +24,125 @@  static inline int check_prefix(const char *str, const char *prefix)
 		strncmp(str, prefix, strlen(prefix)) == 0;
 }
 
+/* Basic operation of gpio */
+/*
+ * Request the lines for gpio with device_name. Could set the default value
+ * in request.
+ * device_name:    the name of gpiochip in "/dev", such as gpiochip0.
+ * lines:	   the array of which line should be requested.
+ * nline:          the total number of line
+ * flag:           input, output and so on. Reference "linux/gpio.h" for the
+ *                 meaning for flag.
+ * data:	   default value when flag is GPIOHANDLE_REQUEST_OUTPUT.
+ * consumer_label: the name of consumer, such as "sysfs", "powerkey".
+ *
+ * Return value:   On success return the fd of specific gpiochip. It could be
+ *                 release by gpio_release.
+ *		   On failure return the errno.
+ */
+int gpio_request(const char *device_name, unsigned int *lines,
+		 unsigned int nlines, unsigned int flag,
+		 struct gpiohandle_data *data, const char *consumer_label);
+/*
+ * Set the value of gpio for fd
+ * fd:             the fd returned by gpio_request
+ * data:	   the value want to set
+ *
+ * Return value:   On success return 0
+ *		   On failure return the errno.
+ */
+int gpio_set_values(const int fd, struct gpiohandle_data *data);
+
+/*
+ * Get the value of gpio for fd
+ * fd:             the fd returned by gpio_request
+ * data:	   the valud get from gpiochip.
+ *
+ * Return value:   On success return 0
+ *		   On failure return the errno.
+ */
+int gpio_get_values(const int fd, struct gpiohandle_data *data);
+
+/*
+ * release the fd for gpiochip
+ */
+int gpio_release(const int fd);
+
+/* Easy operation for one time get/set */
+/*
+ * Get one pin value from line of device_name. Could change the flag if
+ * necessary.
+ * device_name: the name of gpiochip in "/dev", such as gpiochip0.
+ * line:        number of line, such as 2.
+ * flag:        input, output and so on. Reference "linux/gpio.h" for the
+ *              meaning for flag. Set to 0 if do not want to update the
+ *              flag. It is the recommandation value.
+ *
+ * Return value:   On success return the value get from gpiochip.
+ *		   On failure return the errno.
+ */
+int gpio_get(const char *device_name, unsigned int line, unsigned int flag);
+
+/*
+ * Get pins value from line of device_name. Could change the flag if
+ * necessary.
+ * device_name: the name of gpiochip in "/dev", such as gpiochip0.
+ * lines:	   the array of which line should be requested.
+ * nline:          the total number of line
+ * flag:           input, output and so on. Reference "linux/gpio.h" for the
+ *                 meaning for flag.
+ * data:	   the valud get from gpiochip.
+ *
+ * Return value:   On success return 0
+ *		   On failure return the errno.
+ */
+int gpio_gets(const char *device_name, unsigned int *lines, unsigned int nlines,
+	      unsigned int flag, struct gpiohandle_data *data);
+
+/*
+ * Set one pin value from line of device_name. Could change the flag if
+ * necessary.
+ * device_name: the name of gpiochip in "/dev", such as gpiochip0.
+ * line:        number of line, such as 2.
+ * flag:        input, output and so on. Reference "linux/gpio.h" for the
+ *              meaning for flag. Set to 0 if do not want to update the
+ *              flag. It is the recommandation value.
+ * value:	the value want to set the gpio line.
+ *
+ * Return value:   On success return 0
+ *		   On failure return the errno.
+ */
+int gpio_set(const char *device_name, unsigned int line, unsigned int flag,
+	     unsigned int value);
+
+/*
+ * Set pins value from line of device_name. Could change the flag if
+ * necessary.
+ * device_name: the name of gpiochip in "/dev", such as gpiochip0.
+ * lines:	   the array of which line should be requested.
+ * nline:          the total number of line
+ * flag:           input, output and so on. Reference "linux/gpio.h" for the
+ *                 meaning for flag.
+ * data:	   the value want to set
+ *
+ * Return value:   On success return 0
+ *		   On failure return the errno.
+ */
+int gpio_sets(const char *device_name, unsigned int *lines, unsigned int nlines,
+	      unsigned int flag, struct gpiohandle_data *data);
+
+/*
+ * set the flag for device_name. Mainly for changing the direction of gpio
+ *
+ * device_name: the name of gpiochip in "/dev", such as gpiochip0.
+ * line:        number of line, such as 2.
+ * flag:        input, output and so on. Reference "linux/gpio.h" for the
+ *              meaning for flag.
+ *
+ * Return value:   On success return 0
+ *		   On failure return the errno.
+ */
+int gpio_set_flag(const char *device_name, unsigned int line,
+		  unsigned int flag);
+
 #endif /* _GPIO_UTILS_H_ */