All of lore.kernel.org
 help / color / mirror / Atom feed
From: info@kovacsandre.com
To: linux-gpio@vger.kernel.org
Subject: [libgpiod] daemon for controlling GPIOs
Date: Sat, 09 Nov 2019 22:29:45 +0100	[thread overview]
Message-ID: <1573334985.32589.1.camel@localhost> (raw)

[-- Attachment #1: Type: text/plain, Size: 767 bytes --]

Hi!

I wrote a simple daemon for controlling GPIOs via UNIX socket.
Currently only the output set/unset works. The program read a config
file and configuring the pins with libgpiod. The config file is in the
tools directory. I don't really know where should I drop and how to
configure libtool. The fork() is commented yet so the daemon writes
info to the terminal. With the command for ex.

echo "gpiochip1:PD21 set=1" | socat - UNIX-
CONNECT:/var/run/gpiodaemon.sock 

you can set a configured out to high. If it is useful for the project I
can send more patch in the future with more feature. This is as you can
see a demo only. I tested with an Allwinner H6 SoC (OrangePi Lite2). I
am newbie in contributing so please fix me if required.

Regards,
André Kovács

[-- Attachment #2: 0001-gpiodaemon-basics.patch --]
[-- Type: text/x-patch, Size: 14098 bytes --]

From f18d2fbdff11798cd457ac75684f7dbff6812cad Mon Sep 17 00:00:00 2001
From: Andre Kovacs <info@kovacsandre.com>
Date: Sat, 9 Nov 2019 19:14:13 +0100
Subject: [PATCH] gpiodaemon basics

The program read the config file and configure the IOs. Over a UNIX
socket it is possible to set the configured output(s). Some little
modifications was needed in the lib.

Signed-off-by: Andre Kovacs <info@kovacsandre.com>
---
 include/gpiod.h    |  16 ++
 lib/core.c         |   5 +
 lib/iter.c         |   5 +
 tools/Makefile.am  |   4 +-
 tools/daemonconfig |   2 +
 tools/gpiodaemon.c | 466 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 497 insertions(+), 1 deletion(-)
 create mode 100644 tools/daemonconfig
 create mode 100644 tools/gpiodaemon.c

diff --git a/include/gpiod.h b/include/gpiod.h
index 9860ea8..ad231b9 100644
--- a/include/gpiod.h
+++ b/include/gpiod.h
@@ -659,6 +659,13 @@ enum {
 };
 
 /**
+ * @brief Get the line's chip name.
+ * @param line GPIO line object.
+ * @return Chip name.
+ */
+const char *gpiod_line_chip_name(struct gpiod_line *line) GPIOD_API;
+
+/**
  * @brief Read the GPIO line offset.
  * @param line GPIO line object.
  * @return Line offset.
@@ -1321,6 +1328,15 @@ gpiod_chip_iter_next(struct gpiod_chip_iter *iter) GPIOD_API;
 struct gpiod_chip *
 gpiod_chip_iter_next_noclose(struct gpiod_chip_iter *iter) GPIOD_API;
 
+
+/**
+ * @brief Set offset to zero.
+ * @param iter The gpiochip iterator object.
+ */
+
+
+void gpiod_chip_iter_reset_offset(struct gpiod_chip_iter *iter) GPIOD_API;
+
 /**
  * @brief Iterate over all GPIO chips present in the system.
  * @param iter An initialized GPIO chip iterator.
diff --git a/lib/core.c b/lib/core.c
index a04514e..fd4e17d 100644
--- a/lib/core.c
+++ b/lib/core.c
@@ -334,6 +334,11 @@ struct gpiod_chip *gpiod_line_get_chip(struct gpiod_line *line)
 	return line->chip;
 }
 
+const char *gpiod_line_chip_name(struct gpiod_line *line)
+{
+	return line->chip->name[0] == '\0' ? NULL : line->chip->name;
+}
+
 unsigned int gpiod_line_offset(struct gpiod_line *line)
 {
 	return line->offset;
diff --git a/lib/iter.c b/lib/iter.c
index a4d883a..afaf567 100644
--- a/lib/iter.c
+++ b/lib/iter.c
@@ -169,3 +169,8 @@ struct gpiod_line *gpiod_line_iter_next(struct gpiod_line_iter *iter)
 	return iter->offset < (iter->num_lines)
 					? iter->lines[iter->offset++] : NULL;
 }
+
+void gpiod_chip_iter_reset_offset(struct gpiod_chip_iter *iter)
+{
+	iter->offset = 0;
+}
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 897ff32..bbc8950 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -14,7 +14,7 @@ libtools_common_la_SOURCES = tools-common.c tools-common.h
 
 LDADD = libtools-common.la $(top_builddir)/lib/libgpiod.la
 
-bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiofind
+bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiofind gpiodaemon
 
 gpiodetect_SOURCES = gpiodetect.c
 
@@ -28,6 +28,8 @@ gpiomon_SOURCES = gpiomon.c
 
 gpiofind_SOURCES = gpiofind.c
 
+gpiodaemon_SOURCES = gpiodaemon.c
+
 EXTRA_DIST = gpio-tools-test gpio-tools-test.bats
 
 if WITH_TESTS
diff --git a/tools/daemonconfig b/tools/daemonconfig
new file mode 100644
index 0000000..7bbc00d
--- /dev/null
+++ b/tools/daemonconfig
@@ -0,0 +1,2 @@
+gpiochip1:PD21	direction=out,defval=0,consumer=asd;
+gpiochip1:118	direction=in,consumer=asd2;
diff --git a/tools/gpiodaemon.c b/tools/gpiodaemon.c
new file mode 100644
index 0000000..70d9a4f
--- /dev/null
+++ b/tools/gpiodaemon.c
@@ -0,0 +1,466 @@
+/*
+ * 2019 André Kovács <info@kovacsandre.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <gpiod.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define SOCK_PATH "/var/run/gpiodaemon.sock"
+#define MAX_CLIENTS 10
+
+#define CONSUMER_MAX 32
+
+volatile sig_atomic_t exit_flag = 0;
+
+void exit_program(int s)
+{
+	exit_flag = 1;
+}
+
+static int match_keyword(char *kw, char *opt,
+						 char *consumer, int *val,
+						 struct gpiod_line_request_config *line)
+{
+	int rv = 0;
+
+	if (!strcmp(kw, "direction")) {
+		if (!strcmp(opt, "in")) {
+			puts("directon set to input");
+			line->request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT;
+		}
+		else if (!strcmp(opt, "out")) {
+			puts("directon set to output");
+			line->request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT;
+		}
+		else {
+			fprintf(stderr, "Wrong option for \"%s\"\n", kw);
+			rv = 1;
+		}
+	}
+	else if (!strcmp(kw, "defval")) {
+		if (atoi(opt) == 0) {
+			puts("default value LOW");
+			*val = 0;
+		}
+		else {
+			puts("default value HIGH");
+			*val = 1;
+		}
+	}
+	else if (!strcmp(kw, "consumer")) {
+		printf("consumer is \"%s\"\n", opt);
+		strncpy(consumer, opt, CONSUMER_MAX);
+	}
+	else if (!strcmp(kw, "flags")) {
+		if (!strcmp(opt, "open_drain")) {
+			line->flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN;
+			puts("flag open drain");
+		}
+		else if (!strcmp(opt, "open_source")) {
+			line->flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE;
+			puts("flag open source");
+		}
+		else if (!strcmp(opt, "active_low")) {
+			line->flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
+			puts("flag active low");
+		}
+		else {
+			fprintf(stderr, "Wrong option for \"%s\"\n", kw);
+			rv = 1;
+		}
+	}
+	else if (!strcmp(kw, "edge")) {
+		if (!strcmp(opt, "rising")) {
+			line->request_type = GPIOD_LINE_REQUEST_EVENT_RISING_EDGE;
+			puts("rising edge");
+		}
+		else if (!strcmp(opt, "falling")) {
+			line->request_type = GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE;
+			puts("falling edge");
+		}
+		else if (!strcmp(opt, "both")) {
+			line->request_type = GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES;
+			puts("both edges");
+		}
+		else {
+			fprintf(stderr, "Wrong option for \"%s\"\n", kw);
+			rv = 1;
+		}
+	}
+	else {
+		fprintf(stderr, "Wrong kw \"%s\"\n", kw);
+		rv = 1;
+	}
+
+	return rv;
+}
+
+static int set_gpio_line(const char *chip_name, int line_num, int defval,
+						 struct gpiod_line_request_config *linecfg,
+						 struct gpiod_chip_iter *iter,
+						 struct gpiod_line_bulk *entries)
+{
+	int rv = -1, found = 0;
+	struct gpiod_chip *gpio_chip;
+
+	gpiod_foreach_chip_noclose(iter, gpio_chip) {
+		if (!strcmp(gpiod_chip_name(gpio_chip), chip_name)) {
+			found = 1;
+			break;
+		}
+	}
+	gpiod_chip_iter_reset_offset(iter);
+
+	if (found != 1) {
+		fprintf(stderr, "No chip found by name \"%s\"\n", chip_name);
+		goto out;
+	} else
+		printf("chip_name is \"%s\"\n", gpiod_chip_name(gpio_chip));
+
+	entries->lines[entries->num_lines] = gpiod_chip_get_line(gpio_chip, line_num);
+	if (entries->lines[entries->num_lines] == NULL) {
+		fprintf(stderr, "gpiod_chip_get_line(): %s\n", strerror(errno));
+		goto out;
+	}
+
+	if (!gpiod_line_is_free(entries->lines[entries->num_lines])) {
+		fprintf(stderr, "GPIO line (%s) is used\n",
+						 gpiod_line_consumer(entries->lines[entries->num_lines]));
+		free(entries->lines[entries->num_lines]);
+		goto out;
+	}
+
+	if (gpiod_line_request(entries->lines[entries->num_lines], linecfg, defval) < 0) {
+		fprintf(stderr, "gpiod_line_request(): %s\n", strerror(errno));
+		free(entries->lines[entries->num_lines]);
+		goto out;
+	}
+
+	entries->num_lines += 1;
+	rv = 0;
+
+out:
+	return rv;
+}
+
+static int find_chip_name(char **lineptr, char **arri,
+						  char *line, char *gpiochip, size_t len)
+{
+	while (**lineptr && **lineptr != ':')
+			(*lineptr)++;
+
+		if (**lineptr == '\0') {
+			fprintf(stderr, "Missing gpiochip\n");
+			return -1;
+		}
+
+		**lineptr = '\0';
+		(*lineptr)++;
+		*arri = *lineptr;
+		strncpy(gpiochip, line, len);
+
+		return 0;
+}
+
+static int find_pin(char **lineptr, char **arri, unsigned int *line_num)
+{
+	char pin[12];
+	int port = -1;
+
+	while (**lineptr && !isspace(**lineptr))
+		(*lineptr)++;
+
+	if (**lineptr == '\n') {
+		fprintf(stderr, "Missing pin\n");
+		return -1;
+	}
+
+	**lineptr = '\0';
+	(*lineptr)++;
+	strncpy(pin, *arri, sizeof(pin)-1);
+
+	if (sscanf(pin, "%d", line_num) != 1) {
+		for (int i = 'A'; i < 'Z'; i++) {
+			if (pin[1] == i) {
+				port = i - 65;
+				break;
+			}
+		}
+
+		if (port < 0) {
+			fprintf(stderr, "No valid pin found.");
+			return -1;
+		}
+		else
+			/* Line number calculation. This is the pin identifier in the GPIO block device */
+			*line_num = port * 32 + atoi(&pin[2]);
+	}
+
+	return 0;
+}
+
+static int parser(const char *config_file,
+				  struct gpiod_chip_iter *iter,
+				  struct gpiod_line_bulk *entries)
+{
+	FILE *fp;
+	char line[1024], property[20], keyword[20],
+		 gpiochip[32], consumer_str[CONSUMER_MAX];
+	char *lineptr, *arri;
+	int defval = 0;
+	unsigned int pin;
+
+	struct gpiod_line_request_config config = {.flags = 0};
+
+	if ((fp = fopen(config_file, "r")) == NULL) {
+		perror("fopen()");
+		return -1;
+	}
+
+	bzero(property, sizeof(property));
+	bzero(consumer_str, sizeof(consumer_str));
+
+	config.consumer = consumer_str;
+
+	while (fgets(line, sizeof(line), fp) != NULL) {
+		lineptr = line;
+		printf("%s\n", lineptr);
+
+		if (*lineptr == '#')
+			continue;
+
+		if (line[strlen(line)-2] != ';') {
+			fprintf(stderr, "%s\n", "Missing semicolon or newline");
+			return -1;
+		}
+
+		if (find_chip_name(&lineptr, &arri, line, gpiochip, sizeof(gpiochip)))
+			return -1;
+
+		if (find_pin(&lineptr, &arri, &pin))
+			return -1;
+
+		/* find keyword */
+		arri = property;
+		while (*lineptr && *lineptr != '\n') {
+			if (*lineptr == ',' || *lineptr == ';') {
+				match_keyword(keyword, property,
+							  consumer_str, &defval, &config);
+
+				bzero(property, sizeof(property));
+				arri = property;
+			}
+			else if (*lineptr == '=') {
+				strncpy(keyword, property, sizeof(keyword));
+				bzero(property, sizeof(property));
+				arri = property;
+			}
+			else if (isspace(*lineptr)) {}
+			else {
+				*arri = *lineptr;
+				arri++;
+			}
+			lineptr++;
+		}
+
+		if (set_gpio_line(gpiochip, pin, defval, &config, iter, entries) < 0) {
+			fprintf(stderr, "Skip line: %d\n", pin);
+		}
+		config.flags = 0;
+		config.request_type = 0;
+	}
+
+	fclose(fp);
+
+	return 0;
+}
+
+static int processing_client_req(char *message, struct gpiod_line_bulk *entries)
+{
+	char chipname[32];
+	char *ptr, *start;
+	const char *lines_chipname;
+	int value = 0;
+	unsigned int line_num, offset;
+
+	start = ptr = message;
+
+	if (find_chip_name(&ptr, &start, message, chipname, sizeof(chipname)))
+		return -1;
+	printf("chip: %s\n", chipname);
+
+	if (find_pin(&ptr, &start, &line_num))
+		return -1;
+	printf("line: %d\n", line_num);
+
+	if (!strcmp(ptr, "set=1\n"))
+		value = 1;
+	else if (!strcmp(ptr, "set=0\n"))
+		value = 0;
+	else {
+		fprintf(stderr, "Wrong property\n");
+		return -1;
+	}
+
+	for (size_t i = 0; i <= entries->num_lines; i++) {
+		offset = gpiod_line_offset(entries->lines[i]);
+		lines_chipname = gpiod_line_chip_name(entries->lines[i]);
+
+		if (!strcmp(lines_chipname, chipname) && offset == line_num) {
+			gpiod_line_set_value(entries->lines[i], value);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+int main(int argc, const char *argv[])
+{
+	int client_socket[MAX_CLIENTS], master_socket,
+		new_socket, sd, max_sd, srv, len;
+	socklen_t t;
+	fd_set rdfs;
+	struct sockaddr_un local, remote;
+
+	struct gpiod_chip *gpio_chip;
+	struct gpiod_chip_iter *iter;
+	struct gpiod_line_bulk entries = { .num_lines = 0 };
+
+	struct sigaction sa_exit;
+
+	argc -= optind;
+	if (argc != 1) {
+		fprintf(stderr, "Usage: %s <config path>\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	sa_exit.sa_handler = exit_program;
+	sa_exit.sa_flags = 0;
+	sigemptyset(&sa_exit.sa_mask);
+	if (sigaction(SIGINT, &sa_exit, NULL) < 0) {
+		perror("sigaction()");
+		exit(EXIT_FAILURE);
+	}
+
+	exit_flag = 0;
+
+	iter = gpiod_chip_iter_new();
+	if (!iter) {
+		fprintf(stderr, "%s\n", "unable to access GPIO chips");
+		exit(EXIT_FAILURE);
+	}
+
+	if (parser(argv[1], iter, &entries) < 0)
+		goto out;
+
+	/* Init socket */
+	for (size_t i = 0; i < MAX_CLIENTS; i++)
+		client_socket[i] = 0;
+
+	if ((master_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+		perror("socket()");
+		goto out;
+	}
+
+	local.sun_family = AF_UNIX;
+	strcpy(local.sun_path, SOCK_PATH);
+	unlink(local.sun_path);
+	len = strlen(local.sun_path) + sizeof(local.sun_family);
+	if (bind(master_socket, (struct sockaddr *)&local, len) == -1) {
+		perror("bind()");
+		goto out;
+	}
+
+	if (listen(master_socket, 5) == -1) {
+		perror("listen()");
+		goto out;
+	}
+
+	t = sizeof(remote);
+
+/*
+	if (daemon(0,0) < 0)
+		fprintf(stderr, "daemon(): %s\n", strerror(errno));
+*/
+	while (!exit_flag) {
+		FD_ZERO(&rdfs);
+
+		FD_SET(master_socket, &rdfs);
+		max_sd = master_socket;
+
+		for (size_t i = 0; i < MAX_CLIENTS; i++) {
+			sd = client_socket[i];
+			if(sd > 0)
+				FD_SET(sd , &rdfs);
+
+			if(sd > max_sd)
+				max_sd = sd;
+		}
+
+		srv = select(max_sd + 1 , &rdfs , NULL , NULL , NULL);
+
+		if (srv < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select()");
+		}
+		/* New client */
+		if (FD_ISSET(master_socket, &rdfs)) {
+			if ((new_socket = accept(master_socket,
+				(struct sockaddr *)&remote, (socklen_t*)&t)) < 0) {
+				perror("accept");
+			}
+
+			for (size_t i = 0; i < MAX_CLIENTS; i++) {
+				if(client_socket[i] == 0) {
+					client_socket[i] = new_socket;
+					break;
+				}
+			}
+		}
+		/* Incoming data from client */
+		else {
+			for (size_t i = 0; i < MAX_CLIENTS; i++) {
+				sd = client_socket[i];
+				char buffer[128];
+				bzero(buffer, sizeof(buffer));
+
+				if (FD_ISSET(sd, &rdfs)) {
+					/* Somebody disconnected */
+					if (read(sd, buffer, 127) == 0) {
+						close(sd);
+						client_socket[i] = 0;
+					}
+					else {
+						printf("%s", buffer);
+						processing_client_req(buffer, &entries);
+					}
+				}
+			}
+		}
+	}
+
+	for (size_t i = 0; i < MAX_CLIENTS; i++) {
+		if (client_socket[i] != 0)
+			close(client_socket[i]);
+	}
+	close(master_socket);
+
+
+out:
+	unlink(local.sun_path);
+	/* Creepy free but need */
+	gpiod_foreach_chip(iter, gpio_chip) {}
+	gpiod_chip_iter_free(iter);
+
+	return 0;
+}
-- 
2.11.0


                 reply	other threads:[~2019-11-09 21:29 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1573334985.32589.1.camel@localhost \
    --to=info@kovacsandre.com \
    --cc=linux-gpio@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.