]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
6d591c46 LW |
2 | /* |
3 | * GPIO tools - helpers library for the GPIO tools | |
4 | * | |
5 | * Copyright (C) 2015 Linus Walleij | |
e1acec0e | 6 | * Copyright (C) 2016 Bamvor Jian Zhang |
6d591c46 LW |
7 | */ |
8 | ||
e1acec0e BJZ |
9 | #include <unistd.h> |
10 | #include <stdlib.h> | |
11 | #include <stdio.h> | |
12 | #include <errno.h> | |
13 | #include <string.h> | |
14 | #include <fcntl.h> | |
15 | #include <getopt.h> | |
16 | #include <sys/ioctl.h> | |
17 | #include <linux/gpio.h> | |
6d591c46 | 18 | #include "gpio-utils.h" |
e1acec0e | 19 | |
97551625 | 20 | #define CONSUMER "gpio-utils" |
e1acec0e BJZ |
21 | |
22 | /** | |
23 | * doc: Operation of gpio | |
24 | * | |
25 | * Provide the api of gpiochip for chardev interface. There are two | |
26 | * types of api. The first one provide as same function as each | |
27 | * ioctl, including request and release for lines of gpio, read/write | |
28 | * the value of gpio. If the user want to do lots of read and write of | |
29 | * lines of gpio, user should use this type of api. | |
30 | * | |
31 | * The second one provide the easy to use api for user. Each of the | |
32 | * following api will request gpio lines, do the operation and then | |
33 | * release these lines. | |
34 | */ | |
35 | /** | |
36 | * gpiotools_request_linehandle() - request gpio lines in a gpiochip | |
37 | * @device_name: The name of gpiochip without prefix "/dev/", | |
38 | * such as "gpiochip0" | |
39 | * @lines: An array desired lines, specified by offset | |
40 | * index for the associated GPIO device. | |
ed60aee0 | 41 | * @num_lines: The number of lines to request. |
e1acec0e BJZ |
42 | * @flag: The new flag for requsted gpio. Reference |
43 | * "linux/gpio.h" for the meaning of flag. | |
44 | * @data: Default value will be set to gpio when flag is | |
45 | * GPIOHANDLE_REQUEST_OUTPUT. | |
46 | * @consumer_label: The name of consumer, such as "sysfs", | |
47 | * "powerkey". This is useful for other users to | |
48 | * know who is using. | |
49 | * | |
50 | * Request gpio lines through the ioctl provided by chardev. User | |
51 | * could call gpiotools_set_values() and gpiotools_get_values() to | |
52 | * read and write respectively through the returned fd. Call | |
53 | * gpiotools_release_linehandle() to release these lines after that. | |
54 | * | |
55 | * Return: On success return the fd; | |
56 | * On failure return the errno. | |
57 | */ | |
58 | int gpiotools_request_linehandle(const char *device_name, unsigned int *lines, | |
ed60aee0 | 59 | unsigned int num_lines, unsigned int flag, |
e1acec0e BJZ |
60 | struct gpiohandle_data *data, |
61 | const char *consumer_label) | |
62 | { | |
63 | struct gpiohandle_request req; | |
64 | char *chrdev_name; | |
65 | int fd; | |
66 | int i; | |
67 | int ret; | |
68 | ||
69 | ret = asprintf(&chrdev_name, "/dev/%s", device_name); | |
70 | if (ret < 0) | |
71 | return -ENOMEM; | |
72 | ||
73 | fd = open(chrdev_name, 0); | |
74 | if (fd == -1) { | |
75 | ret = -errno; | |
d327a224 JM |
76 | fprintf(stderr, "Failed to open %s, %s\n", |
77 | chrdev_name, strerror(errno)); | |
e890678f | 78 | goto exit_free_name; |
e1acec0e BJZ |
79 | } |
80 | ||
ed60aee0 | 81 | for (i = 0; i < num_lines; i++) |
e1acec0e BJZ |
82 | req.lineoffsets[i] = lines[i]; |
83 | ||
84 | req.flags = flag; | |
85 | strcpy(req.consumer_label, consumer_label); | |
ed60aee0 | 86 | req.lines = num_lines; |
e1acec0e BJZ |
87 | if (flag & GPIOHANDLE_REQUEST_OUTPUT) |
88 | memcpy(req.default_values, data, sizeof(req.default_values)); | |
89 | ||
90 | ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req); | |
91 | if (ret == -1) { | |
92 | ret = -errno; | |
d327a224 JM |
93 | fprintf(stderr, "Failed to issue %s (%d), %s\n", |
94 | "GPIO_GET_LINEHANDLE_IOCTL", ret, strerror(errno)); | |
e1acec0e BJZ |
95 | } |
96 | ||
e1acec0e BJZ |
97 | if (close(fd) == -1) |
98 | perror("Failed to close GPIO character device file"); | |
e890678f | 99 | exit_free_name: |
e1acec0e BJZ |
100 | free(chrdev_name); |
101 | return ret < 0 ? ret : req.fd; | |
102 | } | |
7ff6d1d2 KG |
103 | |
104 | /** | |
105 | * gpiotools_request_line() - request gpio lines in a gpiochip | |
106 | * @device_name: The name of gpiochip without prefix "/dev/", | |
107 | * such as "gpiochip0" | |
108 | * @lines: An array desired lines, specified by offset | |
109 | * index for the associated GPIO device. | |
110 | * @num_lines: The number of lines to request. | |
111 | * @config: The new config for requested gpio. Reference | |
112 | * "linux/gpio.h" for config details. | |
113 | * @consumer: The name of consumer, such as "sysfs", | |
114 | * "powerkey". This is useful for other users to | |
115 | * know who is using. | |
116 | * | |
117 | * Request gpio lines through the ioctl provided by chardev. User | |
118 | * could call gpiotools_set_values() and gpiotools_get_values() to | |
119 | * read and write respectively through the returned fd. Call | |
120 | * gpiotools_release_line() to release these lines after that. | |
121 | * | |
122 | * Return: On success return the fd; | |
123 | * On failure return the errno. | |
124 | */ | |
125 | int gpiotools_request_line(const char *device_name, unsigned int *lines, | |
126 | unsigned int num_lines, | |
127 | struct gpio_v2_line_config *config, | |
128 | const char *consumer) | |
129 | { | |
130 | struct gpio_v2_line_request req; | |
131 | char *chrdev_name; | |
132 | int fd; | |
133 | int i; | |
134 | int ret; | |
135 | ||
136 | ret = asprintf(&chrdev_name, "/dev/%s", device_name); | |
137 | if (ret < 0) | |
138 | return -ENOMEM; | |
139 | ||
140 | fd = open(chrdev_name, 0); | |
141 | if (fd == -1) { | |
142 | ret = -errno; | |
143 | fprintf(stderr, "Failed to open %s, %s\n", | |
144 | chrdev_name, strerror(errno)); | |
145 | goto exit_free_name; | |
146 | } | |
147 | ||
148 | memset(&req, 0, sizeof(req)); | |
149 | for (i = 0; i < num_lines; i++) | |
150 | req.offsets[i] = lines[i]; | |
151 | ||
152 | req.config = *config; | |
153 | strcpy(req.consumer, consumer); | |
154 | req.num_lines = num_lines; | |
155 | ||
156 | ret = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req); | |
157 | if (ret == -1) { | |
158 | ret = -errno; | |
159 | fprintf(stderr, "Failed to issue %s (%d), %s\n", | |
160 | "GPIO_GET_LINE_IOCTL", ret, strerror(errno)); | |
161 | } | |
162 | ||
163 | if (close(fd) == -1) | |
164 | perror("Failed to close GPIO character device file"); | |
165 | exit_free_name: | |
166 | free(chrdev_name); | |
167 | return ret < 0 ? ret : req.fd; | |
168 | } | |
169 | ||
e1acec0e BJZ |
170 | /** |
171 | * gpiotools_set_values(): Set the value of gpio(s) | |
172 | * @fd: The fd returned by | |
7ff6d1d2 KG |
173 | * gpiotools_request_line(). |
174 | * @values: The array of values want to set. | |
e1acec0e BJZ |
175 | * |
176 | * Return: On success return 0; | |
177 | * On failure return the errno. | |
178 | */ | |
7ff6d1d2 | 179 | int gpiotools_set_values(const int fd, struct gpio_v2_line_values *values) |
e1acec0e BJZ |
180 | { |
181 | int ret; | |
182 | ||
7ff6d1d2 | 183 | ret = ioctl(fd, GPIO_V2_LINE_SET_VALUES_IOCTL, values); |
e1acec0e BJZ |
184 | if (ret == -1) { |
185 | ret = -errno; | |
d327a224 JM |
186 | fprintf(stderr, "Failed to issue %s (%d), %s\n", |
187 | "GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret, | |
188 | strerror(errno)); | |
e1acec0e BJZ |
189 | } |
190 | ||
191 | return ret; | |
192 | } | |
193 | ||
194 | /** | |
195 | * gpiotools_get_values(): Get the value of gpio(s) | |
196 | * @fd: The fd returned by | |
7ff6d1d2 KG |
197 | * gpiotools_request_line(). |
198 | * @values: The array of values get from hardware. | |
e1acec0e BJZ |
199 | * |
200 | * Return: On success return 0; | |
201 | * On failure return the errno. | |
202 | */ | |
7ff6d1d2 | 203 | int gpiotools_get_values(const int fd, struct gpio_v2_line_values *values) |
e1acec0e BJZ |
204 | { |
205 | int ret; | |
206 | ||
7ff6d1d2 | 207 | ret = ioctl(fd, GPIO_V2_LINE_GET_VALUES_IOCTL, values); |
e1acec0e BJZ |
208 | if (ret == -1) { |
209 | ret = -errno; | |
d327a224 JM |
210 | fprintf(stderr, "Failed to issue %s (%d), %s\n", |
211 | "GPIOHANDLE_GET_LINE_VALUES_IOCTL", ret, | |
212 | strerror(errno)); | |
e1acec0e BJZ |
213 | } |
214 | ||
215 | return ret; | |
216 | } | |
217 | ||
218 | /** | |
219 | * gpiotools_release_linehandle(): Release the line(s) of gpiochip | |
220 | * @fd: The fd returned by | |
221 | * gpiotools_request_linehandle(). | |
222 | * | |
223 | * Return: On success return 0; | |
224 | * On failure return the errno. | |
225 | */ | |
226 | int gpiotools_release_linehandle(const int fd) | |
227 | { | |
228 | int ret; | |
229 | ||
230 | ret = close(fd); | |
231 | if (ret == -1) { | |
232 | perror("Failed to close GPIO LINEHANDLE device file"); | |
233 | ret = -errno; | |
234 | } | |
235 | ||
236 | return ret; | |
237 | } | |
238 | ||
7ff6d1d2 KG |
239 | /** |
240 | * gpiotools_release_line(): Release the line(s) of gpiochip | |
241 | * @fd: The fd returned by | |
242 | * gpiotools_request_line(). | |
243 | * | |
244 | * Return: On success return 0; | |
245 | * On failure return the errno. | |
246 | */ | |
247 | int gpiotools_release_line(const int fd) | |
248 | { | |
249 | int ret; | |
250 | ||
251 | ret = close(fd); | |
252 | if (ret == -1) { | |
253 | perror("Failed to close GPIO LINE device file"); | |
254 | ret = -errno; | |
255 | } | |
256 | ||
257 | return ret; | |
258 | } | |
259 | ||
e1acec0e BJZ |
260 | /** |
261 | * gpiotools_get(): Get value from specific line | |
262 | * @device_name: The name of gpiochip without prefix "/dev/", | |
263 | * such as "gpiochip0" | |
264 | * @line: number of line, such as 2. | |
265 | * | |
266 | * Return: On success return 0; | |
267 | * On failure return the errno. | |
268 | */ | |
269 | int gpiotools_get(const char *device_name, unsigned int line) | |
270 | { | |
7ff6d1d2 KG |
271 | int ret; |
272 | unsigned int value; | |
e1acec0e BJZ |
273 | unsigned int lines[] = {line}; |
274 | ||
7ff6d1d2 KG |
275 | ret = gpiotools_gets(device_name, lines, 1, &value); |
276 | if (ret) | |
277 | return ret; | |
278 | return value; | |
e1acec0e BJZ |
279 | } |
280 | ||
281 | ||
282 | /** | |
283 | * gpiotools_gets(): Get values from specific lines. | |
284 | * @device_name: The name of gpiochip without prefix "/dev/", | |
285 | * such as "gpiochip0". | |
286 | * @lines: An array desired lines, specified by offset | |
287 | * index for the associated GPIO device. | |
ed60aee0 | 288 | * @num_lines: The number of lines to request. |
7ff6d1d2 | 289 | * @values: The array of values get from gpiochip. |
e1acec0e BJZ |
290 | * |
291 | * Return: On success return 0; | |
292 | * On failure return the errno. | |
293 | */ | |
294 | int gpiotools_gets(const char *device_name, unsigned int *lines, | |
7ff6d1d2 | 295 | unsigned int num_lines, unsigned int *values) |
e1acec0e | 296 | { |
7ff6d1d2 | 297 | int fd, i; |
e1acec0e BJZ |
298 | int ret; |
299 | int ret_close; | |
7ff6d1d2 KG |
300 | struct gpio_v2_line_config config; |
301 | struct gpio_v2_line_values lv; | |
e1acec0e | 302 | |
7ff6d1d2 KG |
303 | memset(&config, 0, sizeof(config)); |
304 | config.flags = GPIO_V2_LINE_FLAG_INPUT; | |
305 | ret = gpiotools_request_line(device_name, lines, num_lines, | |
306 | &config, CONSUMER); | |
e1acec0e BJZ |
307 | if (ret < 0) |
308 | return ret; | |
309 | ||
310 | fd = ret; | |
7ff6d1d2 KG |
311 | for (i = 0; i < num_lines; i++) |
312 | gpiotools_set_bit(&lv.mask, i); | |
313 | ret = gpiotools_get_values(fd, &lv); | |
314 | if (!ret) | |
315 | for (i = 0; i < num_lines; i++) | |
316 | values[i] = gpiotools_test_bit(lv.bits, i); | |
317 | ret_close = gpiotools_release_line(fd); | |
e1acec0e BJZ |
318 | return ret < 0 ? ret : ret_close; |
319 | } | |
320 | ||
321 | /** | |
322 | * gpiotools_set(): Set value to specific line | |
323 | * @device_name: The name of gpiochip without prefix "/dev/", | |
324 | * such as "gpiochip0" | |
325 | * @line: number of line, such as 2. | |
326 | * @value: The value of gpio, must be 0(low) or 1(high). | |
327 | * | |
328 | * Return: On success return 0; | |
329 | * On failure return the errno. | |
330 | */ | |
331 | int gpiotools_set(const char *device_name, unsigned int line, | |
332 | unsigned int value) | |
333 | { | |
e1acec0e BJZ |
334 | unsigned int lines[] = {line}; |
335 | ||
7ff6d1d2 | 336 | return gpiotools_sets(device_name, lines, 1, &value); |
e1acec0e BJZ |
337 | } |
338 | ||
339 | /** | |
340 | * gpiotools_sets(): Set values to specific lines. | |
341 | * @device_name: The name of gpiochip without prefix "/dev/", | |
342 | * such as "gpiochip0". | |
343 | * @lines: An array desired lines, specified by offset | |
344 | * index for the associated GPIO device. | |
ed60aee0 | 345 | * @num_lines: The number of lines to request. |
7ff6d1d2 | 346 | * @value: The array of values set to gpiochip, must be |
e1acec0e BJZ |
347 | * 0(low) or 1(high). |
348 | * | |
349 | * Return: On success return 0; | |
350 | * On failure return the errno. | |
351 | */ | |
352 | int gpiotools_sets(const char *device_name, unsigned int *lines, | |
7ff6d1d2 | 353 | unsigned int num_lines, unsigned int *values) |
e1acec0e | 354 | { |
7ff6d1d2 KG |
355 | int ret, i; |
356 | struct gpio_v2_line_config config; | |
e1acec0e | 357 | |
7ff6d1d2 KG |
358 | memset(&config, 0, sizeof(config)); |
359 | config.flags = GPIO_V2_LINE_FLAG_OUTPUT; | |
360 | config.num_attrs = 1; | |
361 | config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES; | |
362 | for (i = 0; i < num_lines; i++) { | |
363 | gpiotools_set_bit(&config.attrs[0].mask, i); | |
364 | gpiotools_assign_bit(&config.attrs[0].attr.values, | |
365 | i, values[i]); | |
366 | } | |
367 | ret = gpiotools_request_line(device_name, lines, num_lines, | |
368 | &config, CONSUMER); | |
e1acec0e BJZ |
369 | if (ret < 0) |
370 | return ret; | |
371 | ||
7ff6d1d2 | 372 | return gpiotools_release_line(ret); |
e1acec0e | 373 | } |