162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * GPIO tools - helpers library for the GPIO tools
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Linus Walleij
662306a36Sopenharmony_ci * Copyright (C) 2016 Bamvor Jian Zhang
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <unistd.h>
1062306a36Sopenharmony_ci#include <stdlib.h>
1162306a36Sopenharmony_ci#include <stdio.h>
1262306a36Sopenharmony_ci#include <errno.h>
1362306a36Sopenharmony_ci#include <string.h>
1462306a36Sopenharmony_ci#include <fcntl.h>
1562306a36Sopenharmony_ci#include <getopt.h>
1662306a36Sopenharmony_ci#include <sys/ioctl.h>
1762306a36Sopenharmony_ci#include <linux/gpio.h>
1862306a36Sopenharmony_ci#include "gpio-utils.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define CONSUMER "gpio-utils"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/**
2362306a36Sopenharmony_ci * DOC: Operation of gpio
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * Provide the api of gpiochip for chardev interface. There are two
2662306a36Sopenharmony_ci * types of api.  The first one provide as same function as each
2762306a36Sopenharmony_ci * ioctl, including request and release for lines of gpio, read/write
2862306a36Sopenharmony_ci * the value of gpio. If the user want to do lots of read and write of
2962306a36Sopenharmony_ci * lines of gpio, user should use this type of api.
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * The second one provide the easy to use api for user. Each of the
3262306a36Sopenharmony_ci * following api will request gpio lines, do the operation and then
3362306a36Sopenharmony_ci * release these lines.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/**
3762306a36Sopenharmony_ci * gpiotools_request_line() - request gpio lines in a gpiochip
3862306a36Sopenharmony_ci * @device_name:	The name of gpiochip without prefix "/dev/",
3962306a36Sopenharmony_ci *			such as "gpiochip0"
4062306a36Sopenharmony_ci * @lines:		An array desired lines, specified by offset
4162306a36Sopenharmony_ci *			index for the associated GPIO device.
4262306a36Sopenharmony_ci * @num_lines:		The number of lines to request.
4362306a36Sopenharmony_ci * @config:		The new config for requested gpio. Reference
4462306a36Sopenharmony_ci *			"linux/gpio.h" for config details.
4562306a36Sopenharmony_ci * @consumer:		The name of consumer, such as "sysfs",
4662306a36Sopenharmony_ci *			"powerkey". This is useful for other users to
4762306a36Sopenharmony_ci *			know who is using.
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * Request gpio lines through the ioctl provided by chardev. User
5062306a36Sopenharmony_ci * could call gpiotools_set_values() and gpiotools_get_values() to
5162306a36Sopenharmony_ci * read and write respectively through the returned fd. Call
5262306a36Sopenharmony_ci * gpiotools_release_line() to release these lines after that.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Return:		On success return the fd;
5562306a36Sopenharmony_ci *			On failure return the errno.
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_ciint gpiotools_request_line(const char *device_name, unsigned int *lines,
5862306a36Sopenharmony_ci			   unsigned int num_lines,
5962306a36Sopenharmony_ci			   struct gpio_v2_line_config *config,
6062306a36Sopenharmony_ci			   const char *consumer)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct gpio_v2_line_request req;
6362306a36Sopenharmony_ci	char *chrdev_name;
6462306a36Sopenharmony_ci	int fd;
6562306a36Sopenharmony_ci	int i;
6662306a36Sopenharmony_ci	int ret;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	ret = asprintf(&chrdev_name, "/dev/%s", device_name);
6962306a36Sopenharmony_ci	if (ret < 0)
7062306a36Sopenharmony_ci		return -ENOMEM;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	fd = open(chrdev_name, 0);
7362306a36Sopenharmony_ci	if (fd == -1) {
7462306a36Sopenharmony_ci		ret = -errno;
7562306a36Sopenharmony_ci		fprintf(stderr, "Failed to open %s, %s\n",
7662306a36Sopenharmony_ci			chrdev_name, strerror(errno));
7762306a36Sopenharmony_ci		goto exit_free_name;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
8162306a36Sopenharmony_ci	for (i = 0; i < num_lines; i++)
8262306a36Sopenharmony_ci		req.offsets[i] = lines[i];
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	req.config = *config;
8562306a36Sopenharmony_ci	strcpy(req.consumer, consumer);
8662306a36Sopenharmony_ci	req.num_lines = num_lines;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	ret = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req);
8962306a36Sopenharmony_ci	if (ret == -1) {
9062306a36Sopenharmony_ci		ret = -errno;
9162306a36Sopenharmony_ci		fprintf(stderr, "Failed to issue %s (%d), %s\n",
9262306a36Sopenharmony_ci			"GPIO_GET_LINE_IOCTL", ret, strerror(errno));
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (close(fd) == -1)
9662306a36Sopenharmony_ci		perror("Failed to close GPIO character device file");
9762306a36Sopenharmony_ciexit_free_name:
9862306a36Sopenharmony_ci	free(chrdev_name);
9962306a36Sopenharmony_ci	return ret < 0 ? ret : req.fd;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/**
10362306a36Sopenharmony_ci * gpiotools_set_values() - Set the value of gpio(s)
10462306a36Sopenharmony_ci * @fd:			The fd returned by
10562306a36Sopenharmony_ci *			gpiotools_request_line().
10662306a36Sopenharmony_ci * @values:		The array of values want to set.
10762306a36Sopenharmony_ci *
10862306a36Sopenharmony_ci * Return:		On success return 0;
10962306a36Sopenharmony_ci *			On failure return the errno.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_ciint gpiotools_set_values(const int fd, struct gpio_v2_line_values *values)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	int ret;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	ret = ioctl(fd, GPIO_V2_LINE_SET_VALUES_IOCTL, values);
11662306a36Sopenharmony_ci	if (ret == -1) {
11762306a36Sopenharmony_ci		ret = -errno;
11862306a36Sopenharmony_ci		fprintf(stderr, "Failed to issue %s (%d), %s\n",
11962306a36Sopenharmony_ci			"GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret,
12062306a36Sopenharmony_ci			strerror(errno));
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return ret;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/**
12762306a36Sopenharmony_ci * gpiotools_get_values() - Get the value of gpio(s)
12862306a36Sopenharmony_ci * @fd:			The fd returned by
12962306a36Sopenharmony_ci *			gpiotools_request_line().
13062306a36Sopenharmony_ci * @values:		The array of values get from hardware.
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * Return:		On success return 0;
13362306a36Sopenharmony_ci *			On failure return the errno.
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_ciint gpiotools_get_values(const int fd, struct gpio_v2_line_values *values)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	int ret;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	ret = ioctl(fd, GPIO_V2_LINE_GET_VALUES_IOCTL, values);
14062306a36Sopenharmony_ci	if (ret == -1) {
14162306a36Sopenharmony_ci		ret = -errno;
14262306a36Sopenharmony_ci		fprintf(stderr, "Failed to issue %s (%d), %s\n",
14362306a36Sopenharmony_ci			"GPIOHANDLE_GET_LINE_VALUES_IOCTL", ret,
14462306a36Sopenharmony_ci			strerror(errno));
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return ret;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/**
15162306a36Sopenharmony_ci * gpiotools_release_line() - Release the line(s) of gpiochip
15262306a36Sopenharmony_ci * @fd:			The fd returned by
15362306a36Sopenharmony_ci *			gpiotools_request_line().
15462306a36Sopenharmony_ci *
15562306a36Sopenharmony_ci * Return:		On success return 0;
15662306a36Sopenharmony_ci *			On failure return the errno.
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_ciint gpiotools_release_line(const int fd)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	int ret;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	ret = close(fd);
16362306a36Sopenharmony_ci	if (ret == -1) {
16462306a36Sopenharmony_ci		perror("Failed to close GPIO LINE device file");
16562306a36Sopenharmony_ci		ret = -errno;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return ret;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/**
17262306a36Sopenharmony_ci * gpiotools_get() - Get value from specific line
17362306a36Sopenharmony_ci * @device_name:	The name of gpiochip without prefix "/dev/",
17462306a36Sopenharmony_ci *			such as "gpiochip0"
17562306a36Sopenharmony_ci * @line:		number of line, such as 2.
17662306a36Sopenharmony_ci *
17762306a36Sopenharmony_ci * Return:		On success return 0;
17862306a36Sopenharmony_ci *			On failure return the errno.
17962306a36Sopenharmony_ci */
18062306a36Sopenharmony_ciint gpiotools_get(const char *device_name, unsigned int line)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	int ret;
18362306a36Sopenharmony_ci	unsigned int value;
18462306a36Sopenharmony_ci	unsigned int lines[] = {line};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	ret = gpiotools_gets(device_name, lines, 1, &value);
18762306a36Sopenharmony_ci	if (ret)
18862306a36Sopenharmony_ci		return ret;
18962306a36Sopenharmony_ci	return value;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/**
19462306a36Sopenharmony_ci * gpiotools_gets() - Get values from specific lines.
19562306a36Sopenharmony_ci * @device_name:	The name of gpiochip without prefix "/dev/",
19662306a36Sopenharmony_ci *			such as "gpiochip0".
19762306a36Sopenharmony_ci * @lines:		An array desired lines, specified by offset
19862306a36Sopenharmony_ci *			index for the associated GPIO device.
19962306a36Sopenharmony_ci * @num_lines:		The number of lines to request.
20062306a36Sopenharmony_ci * @values:		The array of values get from gpiochip.
20162306a36Sopenharmony_ci *
20262306a36Sopenharmony_ci * Return:		On success return 0;
20362306a36Sopenharmony_ci *			On failure return the errno.
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_ciint gpiotools_gets(const char *device_name, unsigned int *lines,
20662306a36Sopenharmony_ci		   unsigned int num_lines, unsigned int *values)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	int fd, i;
20962306a36Sopenharmony_ci	int ret;
21062306a36Sopenharmony_ci	int ret_close;
21162306a36Sopenharmony_ci	struct gpio_v2_line_config config;
21262306a36Sopenharmony_ci	struct gpio_v2_line_values lv;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	memset(&config, 0, sizeof(config));
21562306a36Sopenharmony_ci	config.flags = GPIO_V2_LINE_FLAG_INPUT;
21662306a36Sopenharmony_ci	ret = gpiotools_request_line(device_name, lines, num_lines,
21762306a36Sopenharmony_ci				     &config, CONSUMER);
21862306a36Sopenharmony_ci	if (ret < 0)
21962306a36Sopenharmony_ci		return ret;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	fd = ret;
22262306a36Sopenharmony_ci	for (i = 0; i < num_lines; i++)
22362306a36Sopenharmony_ci		gpiotools_set_bit(&lv.mask, i);
22462306a36Sopenharmony_ci	ret = gpiotools_get_values(fd, &lv);
22562306a36Sopenharmony_ci	if (!ret)
22662306a36Sopenharmony_ci		for (i = 0; i < num_lines; i++)
22762306a36Sopenharmony_ci			values[i] = gpiotools_test_bit(lv.bits, i);
22862306a36Sopenharmony_ci	ret_close = gpiotools_release_line(fd);
22962306a36Sopenharmony_ci	return ret < 0 ? ret : ret_close;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/**
23362306a36Sopenharmony_ci * gpiotools_set() - Set value to specific line
23462306a36Sopenharmony_ci * @device_name:	The name of gpiochip without prefix "/dev/",
23562306a36Sopenharmony_ci *			such as "gpiochip0"
23662306a36Sopenharmony_ci * @line:		number of line, such as 2.
23762306a36Sopenharmony_ci * @value:		The value of gpio, must be 0(low) or 1(high).
23862306a36Sopenharmony_ci *
23962306a36Sopenharmony_ci * Return:		On success return 0;
24062306a36Sopenharmony_ci *			On failure return the errno.
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_ciint gpiotools_set(const char *device_name, unsigned int line,
24362306a36Sopenharmony_ci		  unsigned int value)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	unsigned int lines[] = {line};
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return gpiotools_sets(device_name, lines, 1, &value);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/**
25162306a36Sopenharmony_ci * gpiotools_sets() - Set values to specific lines.
25262306a36Sopenharmony_ci * @device_name:	The name of gpiochip without prefix "/dev/",
25362306a36Sopenharmony_ci *			such as "gpiochip0".
25462306a36Sopenharmony_ci * @lines:		An array desired lines, specified by offset
25562306a36Sopenharmony_ci *			index for the associated GPIO device.
25662306a36Sopenharmony_ci * @num_lines:		The number of lines to request.
25762306a36Sopenharmony_ci * @values:		The array of values set to gpiochip, must be
25862306a36Sopenharmony_ci *			0(low) or 1(high).
25962306a36Sopenharmony_ci *
26062306a36Sopenharmony_ci * Return:		On success return 0;
26162306a36Sopenharmony_ci *			On failure return the errno.
26262306a36Sopenharmony_ci */
26362306a36Sopenharmony_ciint gpiotools_sets(const char *device_name, unsigned int *lines,
26462306a36Sopenharmony_ci		   unsigned int num_lines, unsigned int *values)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	int ret, i;
26762306a36Sopenharmony_ci	struct gpio_v2_line_config config;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	memset(&config, 0, sizeof(config));
27062306a36Sopenharmony_ci	config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
27162306a36Sopenharmony_ci	config.num_attrs = 1;
27262306a36Sopenharmony_ci	config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
27362306a36Sopenharmony_ci	for (i = 0; i < num_lines; i++) {
27462306a36Sopenharmony_ci		gpiotools_set_bit(&config.attrs[0].mask, i);
27562306a36Sopenharmony_ci		gpiotools_assign_bit(&config.attrs[0].attr.values,
27662306a36Sopenharmony_ci				     i, values[i]);
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci	ret = gpiotools_request_line(device_name, lines, num_lines,
27962306a36Sopenharmony_ci				     &config, CONSUMER);
28062306a36Sopenharmony_ci	if (ret < 0)
28162306a36Sopenharmony_ci		return ret;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	return gpiotools_release_line(ret);
28462306a36Sopenharmony_ci}
285