// SPDX-License-Identifier: GPL-2.0-only1/*2* GPIO tools - helpers library for the GPIO tools3*4* Copyright (C) 2015 Linus Walleij5* Copyright (C) 2016 Bamvor Jian Zhang6*/78#include <unistd.h>9#include <stdlib.h>10#include <stdio.h>11#include <errno.h>12#include <string.h>13#include <fcntl.h>14#include <getopt.h>15#include <sys/ioctl.h>16#include <linux/gpio.h>17#include "gpio-utils.h"1819#define CONSUMER "gpio-utils"2021/**22* DOC: Operation of gpio23*24* Provide the api of gpiochip for chardev interface. There are two25* types of api. The first one provide as same function as each26* ioctl, including request and release for lines of gpio, read/write27* the value of gpio. If the user want to do lots of read and write of28* lines of gpio, user should use this type of api.29*30* The second one provide the easy to use api for user. Each of the31* following api will request gpio lines, do the operation and then32* release these lines.33*/3435/**36* gpiotools_request_line() - request gpio lines in a gpiochip37* @device_name: The name of gpiochip without prefix "/dev/",38* such as "gpiochip0"39* @lines: An array desired lines, specified by offset40* index for the associated GPIO device.41* @num_lines: The number of lines to request.42* @config: The new config for requested gpio. Reference43* "linux/gpio.h" for config details.44* @consumer: The name of consumer, such as "sysfs",45* "powerkey". This is useful for other users to46* know who is using.47*48* Request gpio lines through the ioctl provided by chardev. User49* could call gpiotools_set_values() and gpiotools_get_values() to50* read and write respectively through the returned fd. Call51* gpiotools_release_line() to release these lines after that.52*53* Return: On success return the fd;54* On failure return the errno.55*/56int gpiotools_request_line(const char *device_name, unsigned int *lines,57unsigned int num_lines,58struct gpio_v2_line_config *config,59const char *consumer)60{61struct gpio_v2_line_request req;62char *chrdev_name;63int fd;64int i;65int ret;6667ret = asprintf(&chrdev_name, "/dev/%s", device_name);68if (ret < 0)69return -ENOMEM;7071fd = open(chrdev_name, 0);72if (fd == -1) {73ret = -errno;74fprintf(stderr, "Failed to open %s, %s\n",75chrdev_name, strerror(errno));76goto exit_free_name;77}7879memset(&req, 0, sizeof(req));80for (i = 0; i < num_lines; i++)81req.offsets[i] = lines[i];8283req.config = *config;84strcpy(req.consumer, consumer);85req.num_lines = num_lines;8687ret = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req);88if (ret == -1) {89ret = -errno;90fprintf(stderr, "Failed to issue %s (%d), %s\n",91"GPIO_GET_LINE_IOCTL", ret, strerror(errno));92}9394if (close(fd) == -1)95perror("Failed to close GPIO character device file");96exit_free_name:97free(chrdev_name);98return ret < 0 ? ret : req.fd;99}100101/**102* gpiotools_set_values() - Set the value of gpio(s)103* @fd: The fd returned by104* gpiotools_request_line().105* @values: The array of values want to set.106*107* Return: On success return 0;108* On failure return the errno.109*/110int gpiotools_set_values(const int fd, struct gpio_v2_line_values *values)111{112int ret;113114ret = ioctl(fd, GPIO_V2_LINE_SET_VALUES_IOCTL, values);115if (ret == -1) {116ret = -errno;117fprintf(stderr, "Failed to issue %s (%d), %s\n",118"GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret,119strerror(errno));120}121122return ret;123}124125/**126* gpiotools_get_values() - Get the value of gpio(s)127* @fd: The fd returned by128* gpiotools_request_line().129* @values: The array of values get from hardware.130*131* Return: On success return 0;132* On failure return the errno.133*/134int gpiotools_get_values(const int fd, struct gpio_v2_line_values *values)135{136int ret;137138ret = ioctl(fd, GPIO_V2_LINE_GET_VALUES_IOCTL, values);139if (ret == -1) {140ret = -errno;141fprintf(stderr, "Failed to issue %s (%d), %s\n",142"GPIOHANDLE_GET_LINE_VALUES_IOCTL", ret,143strerror(errno));144}145146return ret;147}148149/**150* gpiotools_release_line() - Release the line(s) of gpiochip151* @fd: The fd returned by152* gpiotools_request_line().153*154* Return: On success return 0;155* On failure return the errno.156*/157int gpiotools_release_line(const int fd)158{159int ret;160161ret = close(fd);162if (ret == -1) {163perror("Failed to close GPIO LINE device file");164ret = -errno;165}166167return ret;168}169170/**171* gpiotools_get() - Get value from specific line172* @device_name: The name of gpiochip without prefix "/dev/",173* such as "gpiochip0"174* @line: number of line, such as 2.175*176* Return: On success return 0;177* On failure return the errno.178*/179int gpiotools_get(const char *device_name, unsigned int line)180{181int ret;182unsigned int value;183unsigned int lines[] = {line};184185ret = gpiotools_gets(device_name, lines, 1, &value);186if (ret)187return ret;188return value;189}190191192/**193* gpiotools_gets() - Get values from specific lines.194* @device_name: The name of gpiochip without prefix "/dev/",195* such as "gpiochip0".196* @lines: An array desired lines, specified by offset197* index for the associated GPIO device.198* @num_lines: The number of lines to request.199* @values: The array of values get from gpiochip.200*201* Return: On success return 0;202* On failure return the errno.203*/204int gpiotools_gets(const char *device_name, unsigned int *lines,205unsigned int num_lines, unsigned int *values)206{207int fd, i;208int ret;209int ret_close;210struct gpio_v2_line_config config;211struct gpio_v2_line_values lv;212213memset(&config, 0, sizeof(config));214config.flags = GPIO_V2_LINE_FLAG_INPUT;215ret = gpiotools_request_line(device_name, lines, num_lines,216&config, CONSUMER);217if (ret < 0)218return ret;219220fd = ret;221for (i = 0; i < num_lines; i++)222gpiotools_set_bit(&lv.mask, i);223ret = gpiotools_get_values(fd, &lv);224if (!ret)225for (i = 0; i < num_lines; i++)226values[i] = gpiotools_test_bit(lv.bits, i);227ret_close = gpiotools_release_line(fd);228return ret < 0 ? ret : ret_close;229}230231/**232* gpiotools_set() - Set value to specific line233* @device_name: The name of gpiochip without prefix "/dev/",234* such as "gpiochip0"235* @line: number of line, such as 2.236* @value: The value of gpio, must be 0(low) or 1(high).237*238* Return: On success return 0;239* On failure return the errno.240*/241int gpiotools_set(const char *device_name, unsigned int line,242unsigned int value)243{244unsigned int lines[] = {line};245246return gpiotools_sets(device_name, lines, 1, &value);247}248249/**250* gpiotools_sets() - Set values to specific lines.251* @device_name: The name of gpiochip without prefix "/dev/",252* such as "gpiochip0".253* @lines: An array desired lines, specified by offset254* index for the associated GPIO device.255* @num_lines: The number of lines to request.256* @values: The array of values set to gpiochip, must be257* 0(low) or 1(high).258*259* Return: On success return 0;260* On failure return the errno.261*/262int gpiotools_sets(const char *device_name, unsigned int *lines,263unsigned int num_lines, unsigned int *values)264{265int ret, i;266struct gpio_v2_line_config config;267268memset(&config, 0, sizeof(config));269config.flags = GPIO_V2_LINE_FLAG_OUTPUT;270config.num_attrs = 1;271config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;272for (i = 0; i < num_lines; i++) {273gpiotools_set_bit(&config.attrs[0].mask, i);274gpiotools_assign_bit(&config.attrs[0].attr.values,275i, values[i]);276}277ret = gpiotools_request_line(device_name, lines, num_lines,278&config, CONSUMER);279if (ret < 0)280return ret;281282return gpiotools_release_line(ret);283}284285286