18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * GPIO tools - helpers library for the GPIO tools
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Linus Walleij
68c2ecf20Sopenharmony_ci * Copyright (C) 2016 Bamvor Jian Zhang
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <unistd.h>
108c2ecf20Sopenharmony_ci#include <stdlib.h>
118c2ecf20Sopenharmony_ci#include <stdio.h>
128c2ecf20Sopenharmony_ci#include <errno.h>
138c2ecf20Sopenharmony_ci#include <string.h>
148c2ecf20Sopenharmony_ci#include <fcntl.h>
158c2ecf20Sopenharmony_ci#include <getopt.h>
168c2ecf20Sopenharmony_ci#include <sys/ioctl.h>
178c2ecf20Sopenharmony_ci#include <linux/gpio.h>
188c2ecf20Sopenharmony_ci#include "gpio-utils.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define CONSUMER "gpio-utils"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/**
238c2ecf20Sopenharmony_ci * doc: Operation of gpio
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * Provide the api of gpiochip for chardev interface. There are two
268c2ecf20Sopenharmony_ci * types of api.  The first one provide as same function as each
278c2ecf20Sopenharmony_ci * ioctl, including request and release for lines of gpio, read/write
288c2ecf20Sopenharmony_ci * the value of gpio. If the user want to do lots of read and write of
298c2ecf20Sopenharmony_ci * lines of gpio, user should use this type of api.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * The second one provide the easy to use api for user. Each of the
328c2ecf20Sopenharmony_ci * following api will request gpio lines, do the operation and then
338c2ecf20Sopenharmony_ci * release these lines.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci/**
368c2ecf20Sopenharmony_ci * gpiotools_request_linehandle() - request gpio lines in a gpiochip
378c2ecf20Sopenharmony_ci * @device_name:	The name of gpiochip without prefix "/dev/",
388c2ecf20Sopenharmony_ci *			such as "gpiochip0"
398c2ecf20Sopenharmony_ci * @lines:		An array desired lines, specified by offset
408c2ecf20Sopenharmony_ci *			index for the associated GPIO device.
418c2ecf20Sopenharmony_ci * @num_lines:		The number of lines to request.
428c2ecf20Sopenharmony_ci * @flag:		The new flag for requsted gpio. Reference
438c2ecf20Sopenharmony_ci *			"linux/gpio.h" for the meaning of flag.
448c2ecf20Sopenharmony_ci * @data:		Default value will be set to gpio when flag is
458c2ecf20Sopenharmony_ci *			GPIOHANDLE_REQUEST_OUTPUT.
468c2ecf20Sopenharmony_ci * @consumer_label:	The name of consumer, such as "sysfs",
478c2ecf20Sopenharmony_ci *			"powerkey". This is useful for other users to
488c2ecf20Sopenharmony_ci *			know who is using.
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * Request gpio lines through the ioctl provided by chardev. User
518c2ecf20Sopenharmony_ci * could call gpiotools_set_values() and gpiotools_get_values() to
528c2ecf20Sopenharmony_ci * read and write respectively through the returned fd. Call
538c2ecf20Sopenharmony_ci * gpiotools_release_linehandle() to release these lines after that.
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * Return:		On success return the fd;
568c2ecf20Sopenharmony_ci *			On failure return the errno.
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_ciint gpiotools_request_linehandle(const char *device_name, unsigned int *lines,
598c2ecf20Sopenharmony_ci				 unsigned int num_lines, unsigned int flag,
608c2ecf20Sopenharmony_ci				 struct gpiohandle_data *data,
618c2ecf20Sopenharmony_ci				 const char *consumer_label)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct gpiohandle_request req;
648c2ecf20Sopenharmony_ci	char *chrdev_name;
658c2ecf20Sopenharmony_ci	int fd;
668c2ecf20Sopenharmony_ci	int i;
678c2ecf20Sopenharmony_ci	int ret;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	ret = asprintf(&chrdev_name, "/dev/%s", device_name);
708c2ecf20Sopenharmony_ci	if (ret < 0)
718c2ecf20Sopenharmony_ci		return -ENOMEM;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	fd = open(chrdev_name, 0);
748c2ecf20Sopenharmony_ci	if (fd == -1) {
758c2ecf20Sopenharmony_ci		ret = -errno;
768c2ecf20Sopenharmony_ci		fprintf(stderr, "Failed to open %s, %s\n",
778c2ecf20Sopenharmony_ci			chrdev_name, strerror(errno));
788c2ecf20Sopenharmony_ci		goto exit_free_name;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	for (i = 0; i < num_lines; i++)
828c2ecf20Sopenharmony_ci		req.lineoffsets[i] = lines[i];
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	req.flags = flag;
858c2ecf20Sopenharmony_ci	strcpy(req.consumer_label, consumer_label);
868c2ecf20Sopenharmony_ci	req.lines = num_lines;
878c2ecf20Sopenharmony_ci	if (flag & GPIOHANDLE_REQUEST_OUTPUT)
888c2ecf20Sopenharmony_ci		memcpy(req.default_values, data, sizeof(req.default_values));
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
918c2ecf20Sopenharmony_ci	if (ret == -1) {
928c2ecf20Sopenharmony_ci		ret = -errno;
938c2ecf20Sopenharmony_ci		fprintf(stderr, "Failed to issue %s (%d), %s\n",
948c2ecf20Sopenharmony_ci			"GPIO_GET_LINEHANDLE_IOCTL", ret, strerror(errno));
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (close(fd) == -1)
988c2ecf20Sopenharmony_ci		perror("Failed to close GPIO character device file");
998c2ecf20Sopenharmony_ciexit_free_name:
1008c2ecf20Sopenharmony_ci	free(chrdev_name);
1018c2ecf20Sopenharmony_ci	return ret < 0 ? ret : req.fd;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/**
1058c2ecf20Sopenharmony_ci * gpiotools_request_line() - request gpio lines in a gpiochip
1068c2ecf20Sopenharmony_ci * @device_name:	The name of gpiochip without prefix "/dev/",
1078c2ecf20Sopenharmony_ci *			such as "gpiochip0"
1088c2ecf20Sopenharmony_ci * @lines:		An array desired lines, specified by offset
1098c2ecf20Sopenharmony_ci *			index for the associated GPIO device.
1108c2ecf20Sopenharmony_ci * @num_lines:		The number of lines to request.
1118c2ecf20Sopenharmony_ci * @config:		The new config for requested gpio. Reference
1128c2ecf20Sopenharmony_ci *			"linux/gpio.h" for config details.
1138c2ecf20Sopenharmony_ci * @consumer:		The name of consumer, such as "sysfs",
1148c2ecf20Sopenharmony_ci *			"powerkey". This is useful for other users to
1158c2ecf20Sopenharmony_ci *			know who is using.
1168c2ecf20Sopenharmony_ci *
1178c2ecf20Sopenharmony_ci * Request gpio lines through the ioctl provided by chardev. User
1188c2ecf20Sopenharmony_ci * could call gpiotools_set_values() and gpiotools_get_values() to
1198c2ecf20Sopenharmony_ci * read and write respectively through the returned fd. Call
1208c2ecf20Sopenharmony_ci * gpiotools_release_line() to release these lines after that.
1218c2ecf20Sopenharmony_ci *
1228c2ecf20Sopenharmony_ci * Return:		On success return the fd;
1238c2ecf20Sopenharmony_ci *			On failure return the errno.
1248c2ecf20Sopenharmony_ci */
1258c2ecf20Sopenharmony_ciint gpiotools_request_line(const char *device_name, unsigned int *lines,
1268c2ecf20Sopenharmony_ci			   unsigned int num_lines,
1278c2ecf20Sopenharmony_ci			   struct gpio_v2_line_config *config,
1288c2ecf20Sopenharmony_ci			   const char *consumer)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct gpio_v2_line_request req;
1318c2ecf20Sopenharmony_ci	char *chrdev_name;
1328c2ecf20Sopenharmony_ci	int fd;
1338c2ecf20Sopenharmony_ci	int i;
1348c2ecf20Sopenharmony_ci	int ret;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	ret = asprintf(&chrdev_name, "/dev/%s", device_name);
1378c2ecf20Sopenharmony_ci	if (ret < 0)
1388c2ecf20Sopenharmony_ci		return -ENOMEM;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	fd = open(chrdev_name, 0);
1418c2ecf20Sopenharmony_ci	if (fd == -1) {
1428c2ecf20Sopenharmony_ci		ret = -errno;
1438c2ecf20Sopenharmony_ci		fprintf(stderr, "Failed to open %s, %s\n",
1448c2ecf20Sopenharmony_ci			chrdev_name, strerror(errno));
1458c2ecf20Sopenharmony_ci		goto exit_free_name;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	memset(&req, 0, sizeof(req));
1498c2ecf20Sopenharmony_ci	for (i = 0; i < num_lines; i++)
1508c2ecf20Sopenharmony_ci		req.offsets[i] = lines[i];
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	req.config = *config;
1538c2ecf20Sopenharmony_ci	strcpy(req.consumer, consumer);
1548c2ecf20Sopenharmony_ci	req.num_lines = num_lines;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ret = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req);
1578c2ecf20Sopenharmony_ci	if (ret == -1) {
1588c2ecf20Sopenharmony_ci		ret = -errno;
1598c2ecf20Sopenharmony_ci		fprintf(stderr, "Failed to issue %s (%d), %s\n",
1608c2ecf20Sopenharmony_ci			"GPIO_GET_LINE_IOCTL", ret, strerror(errno));
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (close(fd) == -1)
1648c2ecf20Sopenharmony_ci		perror("Failed to close GPIO character device file");
1658c2ecf20Sopenharmony_ciexit_free_name:
1668c2ecf20Sopenharmony_ci	free(chrdev_name);
1678c2ecf20Sopenharmony_ci	return ret < 0 ? ret : req.fd;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/**
1718c2ecf20Sopenharmony_ci * gpiotools_set_values(): Set the value of gpio(s)
1728c2ecf20Sopenharmony_ci * @fd:			The fd returned by
1738c2ecf20Sopenharmony_ci *			gpiotools_request_line().
1748c2ecf20Sopenharmony_ci * @values:		The array of values want to set.
1758c2ecf20Sopenharmony_ci *
1768c2ecf20Sopenharmony_ci * Return:		On success return 0;
1778c2ecf20Sopenharmony_ci *			On failure return the errno.
1788c2ecf20Sopenharmony_ci */
1798c2ecf20Sopenharmony_ciint gpiotools_set_values(const int fd, struct gpio_v2_line_values *values)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	int ret;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	ret = ioctl(fd, GPIO_V2_LINE_SET_VALUES_IOCTL, values);
1848c2ecf20Sopenharmony_ci	if (ret == -1) {
1858c2ecf20Sopenharmony_ci		ret = -errno;
1868c2ecf20Sopenharmony_ci		fprintf(stderr, "Failed to issue %s (%d), %s\n",
1878c2ecf20Sopenharmony_ci			"GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret,
1888c2ecf20Sopenharmony_ci			strerror(errno));
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return ret;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/**
1958c2ecf20Sopenharmony_ci * gpiotools_get_values(): Get the value of gpio(s)
1968c2ecf20Sopenharmony_ci * @fd:			The fd returned by
1978c2ecf20Sopenharmony_ci *			gpiotools_request_line().
1988c2ecf20Sopenharmony_ci * @values:		The array of values get from hardware.
1998c2ecf20Sopenharmony_ci *
2008c2ecf20Sopenharmony_ci * Return:		On success return 0;
2018c2ecf20Sopenharmony_ci *			On failure return the errno.
2028c2ecf20Sopenharmony_ci */
2038c2ecf20Sopenharmony_ciint gpiotools_get_values(const int fd, struct gpio_v2_line_values *values)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	int ret;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	ret = ioctl(fd, GPIO_V2_LINE_GET_VALUES_IOCTL, values);
2088c2ecf20Sopenharmony_ci	if (ret == -1) {
2098c2ecf20Sopenharmony_ci		ret = -errno;
2108c2ecf20Sopenharmony_ci		fprintf(stderr, "Failed to issue %s (%d), %s\n",
2118c2ecf20Sopenharmony_ci			"GPIOHANDLE_GET_LINE_VALUES_IOCTL", ret,
2128c2ecf20Sopenharmony_ci			strerror(errno));
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return ret;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/**
2198c2ecf20Sopenharmony_ci * gpiotools_release_linehandle(): Release the line(s) of gpiochip
2208c2ecf20Sopenharmony_ci * @fd:			The fd returned by
2218c2ecf20Sopenharmony_ci *			gpiotools_request_linehandle().
2228c2ecf20Sopenharmony_ci *
2238c2ecf20Sopenharmony_ci * Return:		On success return 0;
2248c2ecf20Sopenharmony_ci *			On failure return the errno.
2258c2ecf20Sopenharmony_ci */
2268c2ecf20Sopenharmony_ciint gpiotools_release_linehandle(const int fd)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	int ret;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	ret = close(fd);
2318c2ecf20Sopenharmony_ci	if (ret == -1) {
2328c2ecf20Sopenharmony_ci		perror("Failed to close GPIO LINEHANDLE device file");
2338c2ecf20Sopenharmony_ci		ret = -errno;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return ret;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/**
2408c2ecf20Sopenharmony_ci * gpiotools_release_line(): Release the line(s) of gpiochip
2418c2ecf20Sopenharmony_ci * @fd:			The fd returned by
2428c2ecf20Sopenharmony_ci *			gpiotools_request_line().
2438c2ecf20Sopenharmony_ci *
2448c2ecf20Sopenharmony_ci * Return:		On success return 0;
2458c2ecf20Sopenharmony_ci *			On failure return the errno.
2468c2ecf20Sopenharmony_ci */
2478c2ecf20Sopenharmony_ciint gpiotools_release_line(const int fd)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	int ret;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	ret = close(fd);
2528c2ecf20Sopenharmony_ci	if (ret == -1) {
2538c2ecf20Sopenharmony_ci		perror("Failed to close GPIO LINE device file");
2548c2ecf20Sopenharmony_ci		ret = -errno;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return ret;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/**
2618c2ecf20Sopenharmony_ci * gpiotools_get(): Get value from specific line
2628c2ecf20Sopenharmony_ci * @device_name:	The name of gpiochip without prefix "/dev/",
2638c2ecf20Sopenharmony_ci *			such as "gpiochip0"
2648c2ecf20Sopenharmony_ci * @line:		number of line, such as 2.
2658c2ecf20Sopenharmony_ci *
2668c2ecf20Sopenharmony_ci * Return:		On success return 0;
2678c2ecf20Sopenharmony_ci *			On failure return the errno.
2688c2ecf20Sopenharmony_ci */
2698c2ecf20Sopenharmony_ciint gpiotools_get(const char *device_name, unsigned int line)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	int ret;
2728c2ecf20Sopenharmony_ci	unsigned int value;
2738c2ecf20Sopenharmony_ci	unsigned int lines[] = {line};
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	ret = gpiotools_gets(device_name, lines, 1, &value);
2768c2ecf20Sopenharmony_ci	if (ret)
2778c2ecf20Sopenharmony_ci		return ret;
2788c2ecf20Sopenharmony_ci	return value;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/**
2838c2ecf20Sopenharmony_ci * gpiotools_gets(): Get values from specific lines.
2848c2ecf20Sopenharmony_ci * @device_name:	The name of gpiochip without prefix "/dev/",
2858c2ecf20Sopenharmony_ci *			such as "gpiochip0".
2868c2ecf20Sopenharmony_ci * @lines:		An array desired lines, specified by offset
2878c2ecf20Sopenharmony_ci *			index for the associated GPIO device.
2888c2ecf20Sopenharmony_ci * @num_lines:		The number of lines to request.
2898c2ecf20Sopenharmony_ci * @values:		The array of values get from gpiochip.
2908c2ecf20Sopenharmony_ci *
2918c2ecf20Sopenharmony_ci * Return:		On success return 0;
2928c2ecf20Sopenharmony_ci *			On failure return the errno.
2938c2ecf20Sopenharmony_ci */
2948c2ecf20Sopenharmony_ciint gpiotools_gets(const char *device_name, unsigned int *lines,
2958c2ecf20Sopenharmony_ci		   unsigned int num_lines, unsigned int *values)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	int fd, i;
2988c2ecf20Sopenharmony_ci	int ret;
2998c2ecf20Sopenharmony_ci	int ret_close;
3008c2ecf20Sopenharmony_ci	struct gpio_v2_line_config config;
3018c2ecf20Sopenharmony_ci	struct gpio_v2_line_values lv;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	memset(&config, 0, sizeof(config));
3048c2ecf20Sopenharmony_ci	config.flags = GPIO_V2_LINE_FLAG_INPUT;
3058c2ecf20Sopenharmony_ci	ret = gpiotools_request_line(device_name, lines, num_lines,
3068c2ecf20Sopenharmony_ci				     &config, CONSUMER);
3078c2ecf20Sopenharmony_ci	if (ret < 0)
3088c2ecf20Sopenharmony_ci		return ret;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	fd = ret;
3118c2ecf20Sopenharmony_ci	for (i = 0; i < num_lines; i++)
3128c2ecf20Sopenharmony_ci		gpiotools_set_bit(&lv.mask, i);
3138c2ecf20Sopenharmony_ci	ret = gpiotools_get_values(fd, &lv);
3148c2ecf20Sopenharmony_ci	if (!ret)
3158c2ecf20Sopenharmony_ci		for (i = 0; i < num_lines; i++)
3168c2ecf20Sopenharmony_ci			values[i] = gpiotools_test_bit(lv.bits, i);
3178c2ecf20Sopenharmony_ci	ret_close = gpiotools_release_line(fd);
3188c2ecf20Sopenharmony_ci	return ret < 0 ? ret : ret_close;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci/**
3228c2ecf20Sopenharmony_ci * gpiotools_set(): Set value to specific line
3238c2ecf20Sopenharmony_ci * @device_name:	The name of gpiochip without prefix "/dev/",
3248c2ecf20Sopenharmony_ci *			such as "gpiochip0"
3258c2ecf20Sopenharmony_ci * @line:		number of line, such as 2.
3268c2ecf20Sopenharmony_ci * @value:		The value of gpio, must be 0(low) or 1(high).
3278c2ecf20Sopenharmony_ci *
3288c2ecf20Sopenharmony_ci * Return:		On success return 0;
3298c2ecf20Sopenharmony_ci *			On failure return the errno.
3308c2ecf20Sopenharmony_ci */
3318c2ecf20Sopenharmony_ciint gpiotools_set(const char *device_name, unsigned int line,
3328c2ecf20Sopenharmony_ci		  unsigned int value)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	unsigned int lines[] = {line};
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	return gpiotools_sets(device_name, lines, 1, &value);
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci/**
3408c2ecf20Sopenharmony_ci * gpiotools_sets(): Set values to specific lines.
3418c2ecf20Sopenharmony_ci * @device_name:	The name of gpiochip without prefix "/dev/",
3428c2ecf20Sopenharmony_ci *			such as "gpiochip0".
3438c2ecf20Sopenharmony_ci * @lines:		An array desired lines, specified by offset
3448c2ecf20Sopenharmony_ci *			index for the associated GPIO device.
3458c2ecf20Sopenharmony_ci * @num_lines:		The number of lines to request.
3468c2ecf20Sopenharmony_ci * @value:		The array of values set to gpiochip, must be
3478c2ecf20Sopenharmony_ci *			0(low) or 1(high).
3488c2ecf20Sopenharmony_ci *
3498c2ecf20Sopenharmony_ci * Return:		On success return 0;
3508c2ecf20Sopenharmony_ci *			On failure return the errno.
3518c2ecf20Sopenharmony_ci */
3528c2ecf20Sopenharmony_ciint gpiotools_sets(const char *device_name, unsigned int *lines,
3538c2ecf20Sopenharmony_ci		   unsigned int num_lines, unsigned int *values)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	int ret, i;
3568c2ecf20Sopenharmony_ci	struct gpio_v2_line_config config;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	memset(&config, 0, sizeof(config));
3598c2ecf20Sopenharmony_ci	config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
3608c2ecf20Sopenharmony_ci	config.num_attrs = 1;
3618c2ecf20Sopenharmony_ci	config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
3628c2ecf20Sopenharmony_ci	for (i = 0; i < num_lines; i++) {
3638c2ecf20Sopenharmony_ci		gpiotools_set_bit(&config.attrs[0].mask, i);
3648c2ecf20Sopenharmony_ci		gpiotools_assign_bit(&config.attrs[0].attr.values,
3658c2ecf20Sopenharmony_ci				     i, values[i]);
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci	ret = gpiotools_request_line(device_name, lines, num_lines,
3688c2ecf20Sopenharmony_ci				     &config, CONSUMER);
3698c2ecf20Sopenharmony_ci	if (ret < 0)
3708c2ecf20Sopenharmony_ci		return ret;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return gpiotools_release_line(ret);
3738c2ecf20Sopenharmony_ci}
374