[PATCH] Add Linux gpio sysfs applets

Tito farmatito at tiscali.it
Tue Apr 29 19:18:06 UTC 2014


On Tuesday 29 April 2014 16:31:33 Sascha Hauer wrote:
> This adds applets for manipulating gpios under Linux via sysfs. It uses
> the /sys/class/gpio API to set direction and value of gpios and to read
> back the actual value. The applets work like the corresponding C functions
> in the kernel:
> 
> gpio_set_value <gpio> <value>
> gpio_get_value <gpio>
> gpio_direction_output <gpio> <value>
> gpio_direction_input <gpio>
> 
> <gpio> is the Linux gpio number and <value> is 0 for low and 1 for high.
> gpio_get_value will report the value to stdout.
> 
> If not already exported the applets export the gpio via
> /sys/class/gpio/export. After usage the gpio is unexported again,
> but only if it wasn't exported before calling the applet.
> 
> Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
> ---
>  include/applets.src.h |   4 +
>  miscutils/Config.src  |   8 ++
>  miscutils/Kbuild.src  |   1 +
>  miscutils/gpio.c      | 354 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 367 insertions(+)
>  create mode 100644 miscutils/gpio.c
> 
> diff --git a/include/applets.src.h b/include/applets.src.h
> index aedcf22..b4abb4f 100644
> --- a/include/applets.src.h
> +++ b/include/applets.src.h
> @@ -167,6 +167,10 @@ IF_GETENFORCE(APPLET(getenforce, BB_DIR_USR_SBIN, BB_SUID_DROP))
>  IF_GETOPT(APPLET(getopt, BB_DIR_BIN, BB_SUID_DROP))
>  IF_GETSEBOOL(APPLET(getsebool, BB_DIR_USR_SBIN, BB_SUID_DROP))
>  IF_GETTY(APPLET(getty, BB_DIR_SBIN, BB_SUID_DROP))

Hi,
why must it be 4  different applets couldn't the same
applet get/set the value as they share a lot of code?
Eventually you could use the app name to check if the
action to perform is to get or to set:

 (applet_name[6] == 's')

Also you maybe could use some libbb functions  to reduce the
code size. I've listed the most obvious of them in the code
but there could be more.
I suspect that the RidgeRun license could a problem
for the inclusion of this code in busybox and I wonder
if it wouldn't be a cleaner solution to rewrite the app
as it is rather simple stuff. 
Of course the  busybox maintainer  will decide on this issue.

Hope it helps.

Ciao,
Tito 


> +IF_GPIO(APPLET(gpio_set_value, BB_DIR_USR_BIN, BB_SUID_DROP))
> +IF_GPIO(APPLET(gpio_get_value, BB_DIR_USR_BIN, BB_SUID_DROP))
> +IF_GPIO(APPLET(gpio_direction_output, BB_DIR_USR_BIN, BB_SUID_DROP))
> +IF_GPIO(APPLET(gpio_direction_input, BB_DIR_USR_BIN, BB_SUID_DROP))
>  IF_HD(APPLET_NOEXEC(hd, hexdump, BB_DIR_USR_BIN, BB_SUID_DROP, hd))
>  IF_HDPARM(APPLET(hdparm, BB_DIR_SBIN, BB_SUID_DROP))
>  IF_HEAD(APPLET_NOEXEC(head, head, BB_DIR_USR_BIN, BB_SUID_DROP, head))
> diff --git a/miscutils/Config.src b/miscutils/Config.src
> index 1da9800..6048c59 100644
> --- a/miscutils/Config.src
> +++ b/miscutils/Config.src
> @@ -320,6 +320,14 @@ config FLASH_ERASEALL
>  	  The flash_eraseall binary from mtd-utils as of git head c4c6a59eb.
>  	  This utility is used to erase the whole MTD device.
>  
> +config GPIO
> +	bool "gpio tools"
> +	default n
> +	help
> +	  This adds support for manipulating gpios via sysfs. It adds the
> +	  applets gpio_set_value, gpio_get_value, gpio_direction_output and
> +	  gpio_direction_input.
> +
>  config IONICE
>  	bool "ionice"
>  	default y
> diff --git a/miscutils/Kbuild.src b/miscutils/Kbuild.src
> index 9e164f1..e18a816 100644
> --- a/miscutils/Kbuild.src
> +++ b/miscutils/Kbuild.src
> @@ -23,6 +23,7 @@ lib-$(CONFIG_FLASHCP)     += flashcp.o
>  lib-$(CONFIG_FLASH_ERASEALL) += flash_eraseall.o
>  lib-$(CONFIG_FLASH_LOCK)     += flash_lock_unlock.o
>  lib-$(CONFIG_FLASH_UNLOCK)   += flash_lock_unlock.o
> +lib-$(CONFIG_GPIO)        += gpio.o
>  lib-$(CONFIG_IONICE)      += ionice.o
>  lib-$(CONFIG_HDPARM)      += hdparm.o
>  lib-$(CONFIG_INOTIFYD)    += inotifyd.o
> diff --git a/miscutils/gpio.c b/miscutils/gpio.c
> new file mode 100644
> index 0000000..b9848e7
> --- /dev/null
> +++ b/miscutils/gpio.c
> @@ -0,0 +1,354 @@
> +/*
> + * busybox gpio applets
> + *
> + * Copyright (c) 2014, Sascha Hauer, <s.hauer at pengutronix.de>, Pengutronix
> + *
> + * based on code:
> + *
> + * Copyright (c) 2011, RidgeRun
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are met:
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + *    notice, this list of conditions and the following disclaimer in the
> + *    documentation and/or other materials provided with the distribution.
> + * 3. All advertising materials mentioning features or use of this software
> + *    must display the following acknowledgement:
> + *    This product includes software developed by the RidgeRun.
> + * 4. Neither the name of the RidgeRun nor the
> + *    names of its contributors may be used to endorse or promote products
> + *    derived from this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY RIDGERUN ''AS IS'' AND ANY
> + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
> + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
> + * DISCLAIMED. IN NO EVENT SHALL RIDGERUN BE LIABLE FOR ANY
> + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
> + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
> + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +//usage:#define gpio_set_value_trivial_usage
> +//usage:       "<gpio> <value>"
> +//usage:#define gpio_set_value_full_usage "\n\n"
> +//usage:       "set gpio <gpio> to value <value>"
> +
> +//usage:#define gpio_get_value_trivial_usage
> +//usage:       "<gpio>"
> +//usage:#define gpio_get_value_full_usage "\n\n"
> +//usage:       "get value of gpio <gpio>"
> +
> +//usage:#define gpio_direction_output_trivial_usage
> +//usage:       "<gpio> <value>"
> +//usage:#define gpio_direction_output_full_usage "\n\n"
> +//usage:       "configure gpio <gpio> as output with initial value <value>"
> +
> +//usage:#define gpio_direction_input_trivial_usage
> +//usage:       "<gpio>"
> +//usage:#define gpio_direction_input_full_usage "\n\n"
> +//usage:       "configure gpio <gpio> as input"
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <poll.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +
> +#include "libbb.h"
> +
> +#define SYSFS_GPIO_DIR "/sys/class/gpio"
> +#define MAX_BUF 64
> +
> +static int gpio_is_exported(unsigned int gpio)
> +{
> +	char path[MAX_BUF];
> +	int ret;
> +	struct stat s;
> +	sprintf(path, "%s/gpio%d", SYSFS_GPIO_DIR, gpio);
> +

xstat ?

> +	ret = stat(path, &s);
> +	if (ret)
> +		return 0;
> +	return 1;
> +}
> +
> +static int gpio_export(unsigned int gpio)
> +{
> +	int fd, len, ret;
> +	char buf[MAX_BUF];
> +	const char *export_path = SYSFS_GPIO_DIR "/unexport";
> +

xopen ? open_or_warn

> +	fd = open(export_path, O_WRONLY);
> +	if (fd < 0) {
> +		fprintf(stderr, "open %s: %s\n", export_path, strerror(errno));
> +		return -errno;
> +	}
> +
> +	len = snprintf(buf, sizeof(buf), "%d", gpio);
> +	ret = write(fd, buf, len);
> +	if (ret < 0)

	bb_perror_msg

> +		fprintf(stderr, "export gpio %d: %s\n", gpio, strerror(errno));
> +	else
> +		ret = 0;
> +
> +	close(fd);
> +
> +	if (!gpio_is_exported(gpio))
> +		return -ENOSYS;
> +
> +	return ret;
> +}
> +
> +static int gpio_unexport(unsigned int gpio)
> +{
> +	int fd, len, ret;
> +	char buf[MAX_BUF];
> +	const char *unexport_path = SYSFS_GPIO_DIR "/unexport";

xopen ? open_or_warn

> +	fd = open(unexport_path, O_WRONLY);
> +	if (fd < 0) {
> +		fprintf(stderr, "open %s: %s\n", unexport_path, strerror(errno));
> +		return -errno;
> +	}
> +
> +	len = snprintf(buf, sizeof(buf), "%d", gpio);
> +	ret = write(fd, buf, len);
> +	if (ret < 0)

	bb_perror_msg

> +		fprintf(stderr, "unexport gpio %d: %s\n", gpio, strerror(errno));
> +	else
> +		ret = 0;
> +
> +	close(fd);
> +
> +	return ret;
> +}
> +
> +enum gpio_direction {
> +	GPIO_IN,
> +	GPIO_OUT_LOW,
> +	GPIO_OUT_HIGH,
> +};
> +
> +static int gpio_set_direction(unsigned int gpio, enum gpio_direction dir)
> +{
> +	int fd, ret;
> +	char buf[MAX_BUF];
> +	const char *str;
> +
> +	snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR  "/gpio%d/direction", gpio);

xopen ? open_or_warn

> +	fd = open(buf, O_WRONLY);
> +	if (fd < 0) {
> +		fprintf(stderr, "open %s: %s\n", buf, strerror(-errno));
> +		return -errno;
> +	}
> +
> +	switch (dir) {
> +	case GPIO_IN:
> +		str = "in";
> +		break;
> +	case GPIO_OUT_LOW:
> +		str = "low";
> +		break;
> +	case GPIO_OUT_HIGH:
> +		str = "high";
> +		break;
> +	default:
> +		close(fd);
> +		return -EINVAL;
> +	};
> +	ret = write(fd, str, strlen(str) + 1);

	bb_perror_msg

> +	if (ret < 0)
> +		fprintf(stderr, "write direction gpio %d: %s\n", gpio, strerror(-errno));
> +	else
> +		ret = 0;
> +
> +	close(fd);
> +
> +	return ret;
> +}
> +
> +static int gpio_set_value(unsigned int gpio, unsigned int value)
> +{
> +	int fd, ret;
> +	char buf[MAX_BUF];
> +
> +	snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

xopen ? open_or_warn

> +	fd = open(buf, O_WRONLY);
> +	if (fd < 0) {
> +		fprintf(stderr, "open %s: %s\n", buf, strerror(-errno));
> +		return -errno;
> +	}
> +
> +	if (value)
> +		ret = write(fd, "1", 2);
> +	else
> +		ret = write(fd, "0", 2);
> +
> +	if (ret < 0)

bb_perror_msg

> +		fprintf(stderr, "write value gpio %d: %s\n", gpio, strerror(-errno));
> +	else
> +		ret = 0;
> +
> +	close(fd);
> +
> +	return ret;
> +}
> +
> +static int gpio_direction_output(unsigned int gpio, int value)
> +{
> +	return gpio_set_direction(gpio, value ? GPIO_OUT_HIGH : GPIO_OUT_LOW);
> +}
> +
> +static int gpio_direction_input(unsigned int gpio)
> +{
> +	return gpio_set_direction(gpio, GPIO_IN);
> +}
> +
> +static int gpio_get_value(unsigned int gpio, unsigned int *value)
> +{
> +	int fd, ret;
> +	char buf[MAX_BUF];
> +	char ch;
> +
> +	snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

xopen ? open_or_warn

> +	fd = open(buf, O_RDONLY);
> +	if (fd < 0) {
> +		fprintf(stderr, "open %s: %s\n", buf, strerror(-errno));
> +		return fd;
> +	}
> +
> +	ret = read(fd, &ch, 1);
> +	if (ret < 0)

bb_perror_msg

> +		fprintf(stderr, "read value gpio %d: %s\n", gpio, strerror(-errno));
> +	else
> +		ret = 0;
> +
> +	if (ch != '0') {
> +		*value = 1;
> +	} else {
> +		*value = 0;
> +	}
> +
> +	close(fd);
> +
> +	return ret;
> +}
> +
> +static int gpio_open_export(const char *gpiostr, int *gpio_was_exported)
> +{
> +	int gpio, ret;
> +
> +	gpio = atoi(gpiostr);
> +
> +	*gpio_was_exported = gpio_is_exported(gpio);
> +
> +	if (!*gpio_was_exported) {
> +		ret = gpio_export(gpio);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return gpio;
> +}
> +
> +int gpio_direction_output_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
> +
> +int gpio_direction_output_main(int argc, char **argv)
> +{
> +	int gpio, ret, gpio_was_exported;
> +
> +	if (argc != 3)
> +		bb_show_usage();
> +
> +	gpio = gpio_open_export(argv[1], &gpio_was_exported);
> +	if (gpio < 0)
> +		return gpio;
> +
> +	ret = gpio_direction_output(gpio, atoi(argv[2]));
> +
> +	if (!gpio_was_exported)
> +		gpio_unexport(gpio);
> +
> +	return ret;
> +}
> +
> +int gpio_direction_input_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
> +
> +int gpio_direction_input_main(int argc, char **argv)
> +{
> +	int gpio, ret, gpio_was_exported;
> +
> +	if (argc != 2)
> +		bb_show_usage();
> +
> +	gpio = gpio_open_export(argv[1], &gpio_was_exported);
> +	if (gpio < 0)
> +		return gpio;
> +
> +	ret = gpio_direction_input(gpio);
> +
> +	if (!gpio_was_exported)
> +		gpio_unexport(gpio);
> +
> +	return ret;
> +}
> +
> +int gpio_set_value_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
> +
> +int gpio_set_value_main(int argc, char **argv)
> +{
> +	int gpio, ret, gpio_was_exported;
> +
> +	if (argc != 3)
> +		bb_show_usage();
> +
> +	gpio = gpio_open_export(argv[1], &gpio_was_exported);
> +	if (gpio < 0)
> +		return gpio;
> +
> +	ret = gpio_set_value(gpio, atoi(argv[2]));
> +
> +	if (!gpio_was_exported)
> +		gpio_unexport(gpio);
> +
> +	return ret;
> +}
> +
> +int gpio_get_value_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
> +
> +int gpio_get_value_main(int argc, char **argv)
> +{
> +	int gpio, ret, gpio_was_exported;
> +	unsigned int val = 0;
> +
> +	if (argc != 2)
> +		bb_show_usage();
> +
> +	gpio = gpio_open_export(argv[1], &gpio_was_exported);
> +	if (gpio < 0)
> +		return gpio;
> +
> +	ret = gpio_get_value(gpio, &val);
> +
> +	printf("%d\n", val);
> +
> +	if (!gpio_was_exported)
> +		gpio_unexport(gpio);
> +
> +	return ret;
> +}
> 


More information about the busybox mailing list