| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * GPIO tools - helpers library for the GPIO tools |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2015 Linus Walleij |
|---|
| 5 | 6 | * Copyright (C) 2016 Bamvor Jian Zhang |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 8 | | - * under the terms of the GNU General Public License version 2 as published by |
|---|
| 9 | | - * the Free Software Foundation. |
|---|
| 10 | 7 | */ |
|---|
| 11 | 8 | |
|---|
| 12 | 9 | #include <unistd.h> |
|---|
| .. | .. |
|---|
| 20 | 17 | #include <linux/gpio.h> |
|---|
| 21 | 18 | #include "gpio-utils.h" |
|---|
| 22 | 19 | |
|---|
| 23 | | -#define COMSUMER "gpio-utils" |
|---|
| 20 | +#define CONSUMER "gpio-utils" |
|---|
| 24 | 21 | |
|---|
| 25 | 22 | /** |
|---|
| 26 | 23 | * doc: Operation of gpio |
|---|
| .. | .. |
|---|
| 41 | 38 | * such as "gpiochip0" |
|---|
| 42 | 39 | * @lines: An array desired lines, specified by offset |
|---|
| 43 | 40 | * index for the associated GPIO device. |
|---|
| 44 | | - * @nline: The number of lines to request. |
|---|
| 41 | + * @num_lines: The number of lines to request. |
|---|
| 45 | 42 | * @flag: The new flag for requsted gpio. Reference |
|---|
| 46 | 43 | * "linux/gpio.h" for the meaning of flag. |
|---|
| 47 | 44 | * @data: Default value will be set to gpio when flag is |
|---|
| .. | .. |
|---|
| 59 | 56 | * On failure return the errno. |
|---|
| 60 | 57 | */ |
|---|
| 61 | 58 | int gpiotools_request_linehandle(const char *device_name, unsigned int *lines, |
|---|
| 62 | | - unsigned int nlines, unsigned int flag, |
|---|
| 59 | + unsigned int num_lines, unsigned int flag, |
|---|
| 63 | 60 | struct gpiohandle_data *data, |
|---|
| 64 | 61 | const char *consumer_label) |
|---|
| 65 | 62 | { |
|---|
| .. | .. |
|---|
| 78 | 75 | ret = -errno; |
|---|
| 79 | 76 | fprintf(stderr, "Failed to open %s, %s\n", |
|---|
| 80 | 77 | chrdev_name, strerror(errno)); |
|---|
| 81 | | - goto exit_close_error; |
|---|
| 78 | + goto exit_free_name; |
|---|
| 82 | 79 | } |
|---|
| 83 | 80 | |
|---|
| 84 | | - for (i = 0; i < nlines; i++) |
|---|
| 81 | + for (i = 0; i < num_lines; i++) |
|---|
| 85 | 82 | req.lineoffsets[i] = lines[i]; |
|---|
| 86 | 83 | |
|---|
| 87 | 84 | req.flags = flag; |
|---|
| 88 | 85 | strcpy(req.consumer_label, consumer_label); |
|---|
| 89 | | - req.lines = nlines; |
|---|
| 86 | + req.lines = num_lines; |
|---|
| 90 | 87 | if (flag & GPIOHANDLE_REQUEST_OUTPUT) |
|---|
| 91 | 88 | memcpy(req.default_values, data, sizeof(req.default_values)); |
|---|
| 92 | 89 | |
|---|
| .. | .. |
|---|
| 97 | 94 | "GPIO_GET_LINEHANDLE_IOCTL", ret, strerror(errno)); |
|---|
| 98 | 95 | } |
|---|
| 99 | 96 | |
|---|
| 100 | | -exit_close_error: |
|---|
| 101 | 97 | if (close(fd) == -1) |
|---|
| 102 | 98 | perror("Failed to close GPIO character device file"); |
|---|
| 99 | +exit_free_name: |
|---|
| 103 | 100 | free(chrdev_name); |
|---|
| 104 | 101 | return ret < 0 ? ret : req.fd; |
|---|
| 105 | 102 | } |
|---|
| 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 | + |
|---|
| 106 | 170 | /** |
|---|
| 107 | 171 | * gpiotools_set_values(): Set the value of gpio(s) |
|---|
| 108 | 172 | * @fd: The fd returned by |
|---|
| 109 | | - * gpiotools_request_linehandle(). |
|---|
| 110 | | - * @data: The array of values want to set. |
|---|
| 173 | + * gpiotools_request_line(). |
|---|
| 174 | + * @values: The array of values want to set. |
|---|
| 111 | 175 | * |
|---|
| 112 | 176 | * Return: On success return 0; |
|---|
| 113 | 177 | * On failure return the errno. |
|---|
| 114 | 178 | */ |
|---|
| 115 | | -int gpiotools_set_values(const int fd, struct gpiohandle_data *data) |
|---|
| 179 | +int gpiotools_set_values(const int fd, struct gpio_v2_line_values *values) |
|---|
| 116 | 180 | { |
|---|
| 117 | 181 | int ret; |
|---|
| 118 | 182 | |
|---|
| 119 | | - ret = ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, data); |
|---|
| 183 | + ret = ioctl(fd, GPIO_V2_LINE_SET_VALUES_IOCTL, values); |
|---|
| 120 | 184 | if (ret == -1) { |
|---|
| 121 | 185 | ret = -errno; |
|---|
| 122 | 186 | fprintf(stderr, "Failed to issue %s (%d), %s\n", |
|---|
| .. | .. |
|---|
| 130 | 194 | /** |
|---|
| 131 | 195 | * gpiotools_get_values(): Get the value of gpio(s) |
|---|
| 132 | 196 | * @fd: The fd returned by |
|---|
| 133 | | - * gpiotools_request_linehandle(). |
|---|
| 134 | | - * @data: The array of values get from hardware. |
|---|
| 197 | + * gpiotools_request_line(). |
|---|
| 198 | + * @values: The array of values get from hardware. |
|---|
| 135 | 199 | * |
|---|
| 136 | 200 | * Return: On success return 0; |
|---|
| 137 | 201 | * On failure return the errno. |
|---|
| 138 | 202 | */ |
|---|
| 139 | | -int gpiotools_get_values(const int fd, struct gpiohandle_data *data) |
|---|
| 203 | +int gpiotools_get_values(const int fd, struct gpio_v2_line_values *values) |
|---|
| 140 | 204 | { |
|---|
| 141 | 205 | int ret; |
|---|
| 142 | 206 | |
|---|
| 143 | | - ret = ioctl(fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, data); |
|---|
| 207 | + ret = ioctl(fd, GPIO_V2_LINE_GET_VALUES_IOCTL, values); |
|---|
| 144 | 208 | if (ret == -1) { |
|---|
| 145 | 209 | ret = -errno; |
|---|
| 146 | 210 | fprintf(stderr, "Failed to issue %s (%d), %s\n", |
|---|
| .. | .. |
|---|
| 173 | 237 | } |
|---|
| 174 | 238 | |
|---|
| 175 | 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 | + |
|---|
| 260 | +/** |
|---|
| 176 | 261 | * gpiotools_get(): Get value from specific line |
|---|
| 177 | 262 | * @device_name: The name of gpiochip without prefix "/dev/", |
|---|
| 178 | 263 | * such as "gpiochip0" |
|---|
| .. | .. |
|---|
| 183 | 268 | */ |
|---|
| 184 | 269 | int gpiotools_get(const char *device_name, unsigned int line) |
|---|
| 185 | 270 | { |
|---|
| 186 | | - struct gpiohandle_data data; |
|---|
| 271 | + int ret; |
|---|
| 272 | + unsigned int value; |
|---|
| 187 | 273 | unsigned int lines[] = {line}; |
|---|
| 188 | 274 | |
|---|
| 189 | | - gpiotools_gets(device_name, lines, 1, &data); |
|---|
| 190 | | - return data.values[0]; |
|---|
| 275 | + ret = gpiotools_gets(device_name, lines, 1, &value); |
|---|
| 276 | + if (ret) |
|---|
| 277 | + return ret; |
|---|
| 278 | + return value; |
|---|
| 191 | 279 | } |
|---|
| 192 | 280 | |
|---|
| 193 | 281 | |
|---|
| .. | .. |
|---|
| 197 | 285 | * such as "gpiochip0". |
|---|
| 198 | 286 | * @lines: An array desired lines, specified by offset |
|---|
| 199 | 287 | * index for the associated GPIO device. |
|---|
| 200 | | - * @nline: The number of lines to request. |
|---|
| 201 | | - * @data: The array of values get from gpiochip. |
|---|
| 288 | + * @num_lines: The number of lines to request. |
|---|
| 289 | + * @values: The array of values get from gpiochip. |
|---|
| 202 | 290 | * |
|---|
| 203 | 291 | * Return: On success return 0; |
|---|
| 204 | 292 | * On failure return the errno. |
|---|
| 205 | 293 | */ |
|---|
| 206 | 294 | int gpiotools_gets(const char *device_name, unsigned int *lines, |
|---|
| 207 | | - unsigned int nlines, struct gpiohandle_data *data) |
|---|
| 295 | + unsigned int num_lines, unsigned int *values) |
|---|
| 208 | 296 | { |
|---|
| 209 | | - int fd; |
|---|
| 297 | + int fd, i; |
|---|
| 210 | 298 | int ret; |
|---|
| 211 | 299 | int ret_close; |
|---|
| 300 | + struct gpio_v2_line_config config; |
|---|
| 301 | + struct gpio_v2_line_values lv; |
|---|
| 212 | 302 | |
|---|
| 213 | | - ret = gpiotools_request_linehandle(device_name, lines, nlines, |
|---|
| 214 | | - GPIOHANDLE_REQUEST_INPUT, data, |
|---|
| 215 | | - COMSUMER); |
|---|
| 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); |
|---|
| 216 | 307 | if (ret < 0) |
|---|
| 217 | 308 | return ret; |
|---|
| 218 | 309 | |
|---|
| 219 | 310 | fd = ret; |
|---|
| 220 | | - ret = gpiotools_get_values(fd, data); |
|---|
| 221 | | - ret_close = gpiotools_release_linehandle(fd); |
|---|
| 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); |
|---|
| 222 | 318 | return ret < 0 ? ret : ret_close; |
|---|
| 223 | 319 | } |
|---|
| 224 | 320 | |
|---|
| .. | .. |
|---|
| 235 | 331 | int gpiotools_set(const char *device_name, unsigned int line, |
|---|
| 236 | 332 | unsigned int value) |
|---|
| 237 | 333 | { |
|---|
| 238 | | - struct gpiohandle_data data; |
|---|
| 239 | 334 | unsigned int lines[] = {line}; |
|---|
| 240 | 335 | |
|---|
| 241 | | - data.values[0] = value; |
|---|
| 242 | | - return gpiotools_sets(device_name, lines, 1, &data); |
|---|
| 336 | + return gpiotools_sets(device_name, lines, 1, &value); |
|---|
| 243 | 337 | } |
|---|
| 244 | 338 | |
|---|
| 245 | 339 | /** |
|---|
| .. | .. |
|---|
| 248 | 342 | * such as "gpiochip0". |
|---|
| 249 | 343 | * @lines: An array desired lines, specified by offset |
|---|
| 250 | 344 | * index for the associated GPIO device. |
|---|
| 251 | | - * @nline: The number of lines to request. |
|---|
| 252 | | - * @data: The array of values set to gpiochip, must be |
|---|
| 345 | + * @num_lines: The number of lines to request. |
|---|
| 346 | + * @value: The array of values set to gpiochip, must be |
|---|
| 253 | 347 | * 0(low) or 1(high). |
|---|
| 254 | 348 | * |
|---|
| 255 | 349 | * Return: On success return 0; |
|---|
| 256 | 350 | * On failure return the errno. |
|---|
| 257 | 351 | */ |
|---|
| 258 | 352 | int gpiotools_sets(const char *device_name, unsigned int *lines, |
|---|
| 259 | | - unsigned int nlines, struct gpiohandle_data *data) |
|---|
| 353 | + unsigned int num_lines, unsigned int *values) |
|---|
| 260 | 354 | { |
|---|
| 261 | | - int ret; |
|---|
| 355 | + int ret, i; |
|---|
| 356 | + struct gpio_v2_line_config config; |
|---|
| 262 | 357 | |
|---|
| 263 | | - ret = gpiotools_request_linehandle(device_name, lines, nlines, |
|---|
| 264 | | - GPIOHANDLE_REQUEST_OUTPUT, data, |
|---|
| 265 | | - COMSUMER); |
|---|
| 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); |
|---|
| 266 | 369 | if (ret < 0) |
|---|
| 267 | 370 | return ret; |
|---|
| 268 | 371 | |
|---|
| 269 | | - return gpiotools_release_linehandle(ret); |
|---|
| 372 | + return gpiotools_release_line(ret); |
|---|
| 270 | 373 | } |
|---|