1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26d591c46SLinus Walleij /*
36d591c46SLinus Walleij * GPIO tools - helpers library for the GPIO tools
46d591c46SLinus Walleij *
56d591c46SLinus Walleij * Copyright (C) 2015 Linus Walleij
6e1acec0eSBamvor Jian Zhang * Copyright (C) 2016 Bamvor Jian Zhang
76d591c46SLinus Walleij */
86d591c46SLinus Walleij
9e1acec0eSBamvor Jian Zhang #include <unistd.h>
10e1acec0eSBamvor Jian Zhang #include <stdlib.h>
11e1acec0eSBamvor Jian Zhang #include <stdio.h>
12e1acec0eSBamvor Jian Zhang #include <errno.h>
13e1acec0eSBamvor Jian Zhang #include <string.h>
14e1acec0eSBamvor Jian Zhang #include <fcntl.h>
15e1acec0eSBamvor Jian Zhang #include <getopt.h>
16e1acec0eSBamvor Jian Zhang #include <sys/ioctl.h>
17e1acec0eSBamvor Jian Zhang #include <linux/gpio.h>
186d591c46SLinus Walleij #include "gpio-utils.h"
19e1acec0eSBamvor Jian Zhang
2097551625SMykyta Poturai #define CONSUMER "gpio-utils"
21e1acec0eSBamvor Jian Zhang
22e1acec0eSBamvor Jian Zhang /**
23*b0922c07SRandy Dunlap * DOC: Operation of gpio
24e1acec0eSBamvor Jian Zhang *
25e1acec0eSBamvor Jian Zhang * Provide the api of gpiochip for chardev interface. There are two
26e1acec0eSBamvor Jian Zhang * types of api. The first one provide as same function as each
27e1acec0eSBamvor Jian Zhang * ioctl, including request and release for lines of gpio, read/write
28e1acec0eSBamvor Jian Zhang * the value of gpio. If the user want to do lots of read and write of
29e1acec0eSBamvor Jian Zhang * lines of gpio, user should use this type of api.
30e1acec0eSBamvor Jian Zhang *
31e1acec0eSBamvor Jian Zhang * The second one provide the easy to use api for user. Each of the
32e1acec0eSBamvor Jian Zhang * following api will request gpio lines, do the operation and then
33e1acec0eSBamvor Jian Zhang * release these lines.
34e1acec0eSBamvor Jian Zhang */
357ff6d1d2SKent Gibson
367ff6d1d2SKent Gibson /**
377ff6d1d2SKent Gibson * gpiotools_request_line() - request gpio lines in a gpiochip
387ff6d1d2SKent Gibson * @device_name: The name of gpiochip without prefix "/dev/",
397ff6d1d2SKent Gibson * such as "gpiochip0"
407ff6d1d2SKent Gibson * @lines: An array desired lines, specified by offset
417ff6d1d2SKent Gibson * index for the associated GPIO device.
427ff6d1d2SKent Gibson * @num_lines: The number of lines to request.
437ff6d1d2SKent Gibson * @config: The new config for requested gpio. Reference
447ff6d1d2SKent Gibson * "linux/gpio.h" for config details.
457ff6d1d2SKent Gibson * @consumer: The name of consumer, such as "sysfs",
467ff6d1d2SKent Gibson * "powerkey". This is useful for other users to
477ff6d1d2SKent Gibson * know who is using.
487ff6d1d2SKent Gibson *
497ff6d1d2SKent Gibson * Request gpio lines through the ioctl provided by chardev. User
507ff6d1d2SKent Gibson * could call gpiotools_set_values() and gpiotools_get_values() to
517ff6d1d2SKent Gibson * read and write respectively through the returned fd. Call
527ff6d1d2SKent Gibson * gpiotools_release_line() to release these lines after that.
537ff6d1d2SKent Gibson *
547ff6d1d2SKent Gibson * Return: On success return the fd;
557ff6d1d2SKent Gibson * On failure return the errno.
567ff6d1d2SKent Gibson */
gpiotools_request_line(const char * device_name,unsigned int * lines,unsigned int num_lines,struct gpio_v2_line_config * config,const char * consumer)577ff6d1d2SKent Gibson int gpiotools_request_line(const char *device_name, unsigned int *lines,
587ff6d1d2SKent Gibson unsigned int num_lines,
597ff6d1d2SKent Gibson struct gpio_v2_line_config *config,
607ff6d1d2SKent Gibson const char *consumer)
617ff6d1d2SKent Gibson {
627ff6d1d2SKent Gibson struct gpio_v2_line_request req;
637ff6d1d2SKent Gibson char *chrdev_name;
647ff6d1d2SKent Gibson int fd;
657ff6d1d2SKent Gibson int i;
667ff6d1d2SKent Gibson int ret;
677ff6d1d2SKent Gibson
687ff6d1d2SKent Gibson ret = asprintf(&chrdev_name, "/dev/%s", device_name);
697ff6d1d2SKent Gibson if (ret < 0)
707ff6d1d2SKent Gibson return -ENOMEM;
717ff6d1d2SKent Gibson
727ff6d1d2SKent Gibson fd = open(chrdev_name, 0);
737ff6d1d2SKent Gibson if (fd == -1) {
747ff6d1d2SKent Gibson ret = -errno;
757ff6d1d2SKent Gibson fprintf(stderr, "Failed to open %s, %s\n",
767ff6d1d2SKent Gibson chrdev_name, strerror(errno));
777ff6d1d2SKent Gibson goto exit_free_name;
787ff6d1d2SKent Gibson }
797ff6d1d2SKent Gibson
807ff6d1d2SKent Gibson memset(&req, 0, sizeof(req));
817ff6d1d2SKent Gibson for (i = 0; i < num_lines; i++)
827ff6d1d2SKent Gibson req.offsets[i] = lines[i];
837ff6d1d2SKent Gibson
847ff6d1d2SKent Gibson req.config = *config;
857ff6d1d2SKent Gibson strcpy(req.consumer, consumer);
867ff6d1d2SKent Gibson req.num_lines = num_lines;
877ff6d1d2SKent Gibson
887ff6d1d2SKent Gibson ret = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req);
897ff6d1d2SKent Gibson if (ret == -1) {
907ff6d1d2SKent Gibson ret = -errno;
917ff6d1d2SKent Gibson fprintf(stderr, "Failed to issue %s (%d), %s\n",
927ff6d1d2SKent Gibson "GPIO_GET_LINE_IOCTL", ret, strerror(errno));
937ff6d1d2SKent Gibson }
947ff6d1d2SKent Gibson
957ff6d1d2SKent Gibson if (close(fd) == -1)
967ff6d1d2SKent Gibson perror("Failed to close GPIO character device file");
977ff6d1d2SKent Gibson exit_free_name:
987ff6d1d2SKent Gibson free(chrdev_name);
997ff6d1d2SKent Gibson return ret < 0 ? ret : req.fd;
1007ff6d1d2SKent Gibson }
1017ff6d1d2SKent Gibson
102e1acec0eSBamvor Jian Zhang /**
103*b0922c07SRandy Dunlap * gpiotools_set_values() - Set the value of gpio(s)
104e1acec0eSBamvor Jian Zhang * @fd: The fd returned by
1057ff6d1d2SKent Gibson * gpiotools_request_line().
1067ff6d1d2SKent Gibson * @values: The array of values want to set.
107e1acec0eSBamvor Jian Zhang *
108e1acec0eSBamvor Jian Zhang * Return: On success return 0;
109e1acec0eSBamvor Jian Zhang * On failure return the errno.
110e1acec0eSBamvor Jian Zhang */
gpiotools_set_values(const int fd,struct gpio_v2_line_values * values)1117ff6d1d2SKent Gibson int gpiotools_set_values(const int fd, struct gpio_v2_line_values *values)
112e1acec0eSBamvor Jian Zhang {
113e1acec0eSBamvor Jian Zhang int ret;
114e1acec0eSBamvor Jian Zhang
1157ff6d1d2SKent Gibson ret = ioctl(fd, GPIO_V2_LINE_SET_VALUES_IOCTL, values);
116e1acec0eSBamvor Jian Zhang if (ret == -1) {
117e1acec0eSBamvor Jian Zhang ret = -errno;
118d327a224SJacopo Mondi fprintf(stderr, "Failed to issue %s (%d), %s\n",
119d327a224SJacopo Mondi "GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret,
120d327a224SJacopo Mondi strerror(errno));
121e1acec0eSBamvor Jian Zhang }
122e1acec0eSBamvor Jian Zhang
123e1acec0eSBamvor Jian Zhang return ret;
124e1acec0eSBamvor Jian Zhang }
125e1acec0eSBamvor Jian Zhang
126e1acec0eSBamvor Jian Zhang /**
127*b0922c07SRandy Dunlap * gpiotools_get_values() - Get the value of gpio(s)
128e1acec0eSBamvor Jian Zhang * @fd: The fd returned by
1297ff6d1d2SKent Gibson * gpiotools_request_line().
1307ff6d1d2SKent Gibson * @values: The array of values get from hardware.
131e1acec0eSBamvor Jian Zhang *
132e1acec0eSBamvor Jian Zhang * Return: On success return 0;
133e1acec0eSBamvor Jian Zhang * On failure return the errno.
134e1acec0eSBamvor Jian Zhang */
gpiotools_get_values(const int fd,struct gpio_v2_line_values * values)1357ff6d1d2SKent Gibson int gpiotools_get_values(const int fd, struct gpio_v2_line_values *values)
136e1acec0eSBamvor Jian Zhang {
137e1acec0eSBamvor Jian Zhang int ret;
138e1acec0eSBamvor Jian Zhang
1397ff6d1d2SKent Gibson ret = ioctl(fd, GPIO_V2_LINE_GET_VALUES_IOCTL, values);
140e1acec0eSBamvor Jian Zhang if (ret == -1) {
141e1acec0eSBamvor Jian Zhang ret = -errno;
142d327a224SJacopo Mondi fprintf(stderr, "Failed to issue %s (%d), %s\n",
143d327a224SJacopo Mondi "GPIOHANDLE_GET_LINE_VALUES_IOCTL", ret,
144d327a224SJacopo Mondi strerror(errno));
145e1acec0eSBamvor Jian Zhang }
146e1acec0eSBamvor Jian Zhang
147e1acec0eSBamvor Jian Zhang return ret;
148e1acec0eSBamvor Jian Zhang }
149e1acec0eSBamvor Jian Zhang
150e1acec0eSBamvor Jian Zhang /**
151*b0922c07SRandy Dunlap * gpiotools_release_line() - Release the line(s) of gpiochip
1527ff6d1d2SKent Gibson * @fd: The fd returned by
1537ff6d1d2SKent Gibson * gpiotools_request_line().
1547ff6d1d2SKent Gibson *
1557ff6d1d2SKent Gibson * Return: On success return 0;
1567ff6d1d2SKent Gibson * On failure return the errno.
1577ff6d1d2SKent Gibson */
gpiotools_release_line(const int fd)1587ff6d1d2SKent Gibson int gpiotools_release_line(const int fd)
1597ff6d1d2SKent Gibson {
1607ff6d1d2SKent Gibson int ret;
1617ff6d1d2SKent Gibson
1627ff6d1d2SKent Gibson ret = close(fd);
1637ff6d1d2SKent Gibson if (ret == -1) {
1647ff6d1d2SKent Gibson perror("Failed to close GPIO LINE device file");
1657ff6d1d2SKent Gibson ret = -errno;
1667ff6d1d2SKent Gibson }
1677ff6d1d2SKent Gibson
1687ff6d1d2SKent Gibson return ret;
1697ff6d1d2SKent Gibson }
1707ff6d1d2SKent Gibson
1717ff6d1d2SKent Gibson /**
172*b0922c07SRandy Dunlap * gpiotools_get() - Get value from specific line
173e1acec0eSBamvor Jian Zhang * @device_name: The name of gpiochip without prefix "/dev/",
174e1acec0eSBamvor Jian Zhang * such as "gpiochip0"
175e1acec0eSBamvor Jian Zhang * @line: number of line, such as 2.
176e1acec0eSBamvor Jian Zhang *
177e1acec0eSBamvor Jian Zhang * Return: On success return 0;
178e1acec0eSBamvor Jian Zhang * On failure return the errno.
179e1acec0eSBamvor Jian Zhang */
gpiotools_get(const char * device_name,unsigned int line)180e1acec0eSBamvor Jian Zhang int gpiotools_get(const char *device_name, unsigned int line)
181e1acec0eSBamvor Jian Zhang {
1827ff6d1d2SKent Gibson int ret;
1837ff6d1d2SKent Gibson unsigned int value;
184e1acec0eSBamvor Jian Zhang unsigned int lines[] = {line};
185e1acec0eSBamvor Jian Zhang
1867ff6d1d2SKent Gibson ret = gpiotools_gets(device_name, lines, 1, &value);
1877ff6d1d2SKent Gibson if (ret)
1887ff6d1d2SKent Gibson return ret;
1897ff6d1d2SKent Gibson return value;
190e1acec0eSBamvor Jian Zhang }
191e1acec0eSBamvor Jian Zhang
192e1acec0eSBamvor Jian Zhang
193e1acec0eSBamvor Jian Zhang /**
194*b0922c07SRandy Dunlap * gpiotools_gets() - Get values from specific lines.
195e1acec0eSBamvor Jian Zhang * @device_name: The name of gpiochip without prefix "/dev/",
196e1acec0eSBamvor Jian Zhang * such as "gpiochip0".
197e1acec0eSBamvor Jian Zhang * @lines: An array desired lines, specified by offset
198e1acec0eSBamvor Jian Zhang * index for the associated GPIO device.
199ed60aee0SKent Gibson * @num_lines: The number of lines to request.
2007ff6d1d2SKent Gibson * @values: The array of values get from gpiochip.
201e1acec0eSBamvor Jian Zhang *
202e1acec0eSBamvor Jian Zhang * Return: On success return 0;
203e1acec0eSBamvor Jian Zhang * On failure return the errno.
204e1acec0eSBamvor Jian Zhang */
gpiotools_gets(const char * device_name,unsigned int * lines,unsigned int num_lines,unsigned int * values)205e1acec0eSBamvor Jian Zhang int gpiotools_gets(const char *device_name, unsigned int *lines,
2067ff6d1d2SKent Gibson unsigned int num_lines, unsigned int *values)
207e1acec0eSBamvor Jian Zhang {
2087ff6d1d2SKent Gibson int fd, i;
209e1acec0eSBamvor Jian Zhang int ret;
210e1acec0eSBamvor Jian Zhang int ret_close;
2117ff6d1d2SKent Gibson struct gpio_v2_line_config config;
2127ff6d1d2SKent Gibson struct gpio_v2_line_values lv;
213e1acec0eSBamvor Jian Zhang
2147ff6d1d2SKent Gibson memset(&config, 0, sizeof(config));
2157ff6d1d2SKent Gibson config.flags = GPIO_V2_LINE_FLAG_INPUT;
2167ff6d1d2SKent Gibson ret = gpiotools_request_line(device_name, lines, num_lines,
2177ff6d1d2SKent Gibson &config, CONSUMER);
218e1acec0eSBamvor Jian Zhang if (ret < 0)
219e1acec0eSBamvor Jian Zhang return ret;
220e1acec0eSBamvor Jian Zhang
221e1acec0eSBamvor Jian Zhang fd = ret;
2227ff6d1d2SKent Gibson for (i = 0; i < num_lines; i++)
2237ff6d1d2SKent Gibson gpiotools_set_bit(&lv.mask, i);
2247ff6d1d2SKent Gibson ret = gpiotools_get_values(fd, &lv);
2257ff6d1d2SKent Gibson if (!ret)
2267ff6d1d2SKent Gibson for (i = 0; i < num_lines; i++)
2277ff6d1d2SKent Gibson values[i] = gpiotools_test_bit(lv.bits, i);
2287ff6d1d2SKent Gibson ret_close = gpiotools_release_line(fd);
229e1acec0eSBamvor Jian Zhang return ret < 0 ? ret : ret_close;
230e1acec0eSBamvor Jian Zhang }
231e1acec0eSBamvor Jian Zhang
232e1acec0eSBamvor Jian Zhang /**
233*b0922c07SRandy Dunlap * gpiotools_set() - Set value to specific line
234e1acec0eSBamvor Jian Zhang * @device_name: The name of gpiochip without prefix "/dev/",
235e1acec0eSBamvor Jian Zhang * such as "gpiochip0"
236e1acec0eSBamvor Jian Zhang * @line: number of line, such as 2.
237e1acec0eSBamvor Jian Zhang * @value: The value of gpio, must be 0(low) or 1(high).
238e1acec0eSBamvor Jian Zhang *
239e1acec0eSBamvor Jian Zhang * Return: On success return 0;
240e1acec0eSBamvor Jian Zhang * On failure return the errno.
241e1acec0eSBamvor Jian Zhang */
gpiotools_set(const char * device_name,unsigned int line,unsigned int value)242e1acec0eSBamvor Jian Zhang int gpiotools_set(const char *device_name, unsigned int line,
243e1acec0eSBamvor Jian Zhang unsigned int value)
244e1acec0eSBamvor Jian Zhang {
245e1acec0eSBamvor Jian Zhang unsigned int lines[] = {line};
246e1acec0eSBamvor Jian Zhang
2477ff6d1d2SKent Gibson return gpiotools_sets(device_name, lines, 1, &value);
248e1acec0eSBamvor Jian Zhang }
249e1acec0eSBamvor Jian Zhang
250e1acec0eSBamvor Jian Zhang /**
251*b0922c07SRandy Dunlap * gpiotools_sets() - Set values to specific lines.
252e1acec0eSBamvor Jian Zhang * @device_name: The name of gpiochip without prefix "/dev/",
253e1acec0eSBamvor Jian Zhang * such as "gpiochip0".
254e1acec0eSBamvor Jian Zhang * @lines: An array desired lines, specified by offset
255e1acec0eSBamvor Jian Zhang * index for the associated GPIO device.
256ed60aee0SKent Gibson * @num_lines: The number of lines to request.
257*b0922c07SRandy Dunlap * @values: The array of values set to gpiochip, must be
258e1acec0eSBamvor Jian Zhang * 0(low) or 1(high).
259e1acec0eSBamvor Jian Zhang *
260e1acec0eSBamvor Jian Zhang * Return: On success return 0;
261e1acec0eSBamvor Jian Zhang * On failure return the errno.
262e1acec0eSBamvor Jian Zhang */
gpiotools_sets(const char * device_name,unsigned int * lines,unsigned int num_lines,unsigned int * values)263e1acec0eSBamvor Jian Zhang int gpiotools_sets(const char *device_name, unsigned int *lines,
2647ff6d1d2SKent Gibson unsigned int num_lines, unsigned int *values)
265e1acec0eSBamvor Jian Zhang {
2667ff6d1d2SKent Gibson int ret, i;
2677ff6d1d2SKent Gibson struct gpio_v2_line_config config;
268e1acec0eSBamvor Jian Zhang
2697ff6d1d2SKent Gibson memset(&config, 0, sizeof(config));
2707ff6d1d2SKent Gibson config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
2717ff6d1d2SKent Gibson config.num_attrs = 1;
2727ff6d1d2SKent Gibson config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
2737ff6d1d2SKent Gibson for (i = 0; i < num_lines; i++) {
2747ff6d1d2SKent Gibson gpiotools_set_bit(&config.attrs[0].mask, i);
2757ff6d1d2SKent Gibson gpiotools_assign_bit(&config.attrs[0].attr.values,
2767ff6d1d2SKent Gibson i, values[i]);
2777ff6d1d2SKent Gibson }
2787ff6d1d2SKent Gibson ret = gpiotools_request_line(device_name, lines, num_lines,
2797ff6d1d2SKent Gibson &config, CONSUMER);
280e1acec0eSBamvor Jian Zhang if (ret < 0)
281e1acec0eSBamvor Jian Zhang return ret;
282e1acec0eSBamvor Jian Zhang
2837ff6d1d2SKent Gibson return gpiotools_release_line(ret);
284e1acec0eSBamvor Jian Zhang }
285