1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * GPIO mockup cdev test helper |
4 | * |
5 | * Copyright (C) 2020 Kent Gibson |
6 | */ |
7 | |
8 | #include <errno.h> |
9 | #include <fcntl.h> |
10 | #include <signal.h> |
11 | #include <stdint.h> |
12 | #include <stdio.h> |
13 | #include <stdlib.h> |
14 | #include <string.h> |
15 | #include <unistd.h> |
16 | #include <sys/ioctl.h> |
17 | #include <linux/gpio.h> |
18 | |
19 | #define CONSUMER "gpio-mockup-cdev" |
20 | |
21 | static int request_line_v2(int cfd, unsigned int offset, |
22 | uint64_t flags, unsigned int val) |
23 | { |
24 | struct gpio_v2_line_request req; |
25 | int ret; |
26 | |
27 | memset(&req, 0, sizeof(req)); |
28 | req.num_lines = 1; |
29 | req.offsets[0] = offset; |
30 | req.config.flags = flags; |
31 | strcpy(req.consumer, CONSUMER); |
32 | if (flags & GPIO_V2_LINE_FLAG_OUTPUT) { |
33 | req.config.num_attrs = 1; |
34 | req.config.attrs[0].mask = 1; |
35 | req.config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES; |
36 | if (val) |
37 | req.config.attrs[0].attr.values = 1; |
38 | } |
39 | ret = ioctl(cfd, GPIO_V2_GET_LINE_IOCTL, &req); |
40 | if (ret == -1) |
41 | return -errno; |
42 | return req.fd; |
43 | } |
44 | |
45 | |
46 | static int get_value_v2(int lfd) |
47 | { |
48 | struct gpio_v2_line_values vals; |
49 | int ret; |
50 | |
51 | memset(&vals, 0, sizeof(vals)); |
52 | vals.mask = 1; |
53 | ret = ioctl(lfd, GPIO_V2_LINE_GET_VALUES_IOCTL, &vals); |
54 | if (ret == -1) |
55 | return -errno; |
56 | return vals.bits & 0x1; |
57 | } |
58 | |
59 | static int request_line_v1(int cfd, unsigned int offset, |
60 | uint32_t flags, unsigned int val) |
61 | { |
62 | struct gpiohandle_request req; |
63 | int ret; |
64 | |
65 | memset(&req, 0, sizeof(req)); |
66 | req.lines = 1; |
67 | req.lineoffsets[0] = offset; |
68 | req.flags = flags; |
69 | strcpy(req.consumer_label, CONSUMER); |
70 | if (flags & GPIOHANDLE_REQUEST_OUTPUT) |
71 | req.default_values[0] = val; |
72 | |
73 | ret = ioctl(cfd, GPIO_GET_LINEHANDLE_IOCTL, &req); |
74 | if (ret == -1) |
75 | return -errno; |
76 | return req.fd; |
77 | } |
78 | |
79 | static int get_value_v1(int lfd) |
80 | { |
81 | struct gpiohandle_data vals; |
82 | int ret; |
83 | |
84 | memset(&vals, 0, sizeof(vals)); |
85 | ret = ioctl(lfd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &vals); |
86 | if (ret == -1) |
87 | return -errno; |
88 | return vals.values[0]; |
89 | } |
90 | |
91 | static void usage(char *prog) |
92 | { |
93 | printf("Usage: %s [-l] [-b <bias>] [-s <value>] [-u <uAPI>] <gpiochip> <offset>\n" , prog); |
94 | printf(" -b: set line bias to one of pull-down, pull-up, disabled\n" ); |
95 | printf(" (default is to leave bias unchanged):\n" ); |
96 | printf(" -l: set line active low (default is active high)\n" ); |
97 | printf(" -s: set line value (default is to get line value)\n" ); |
98 | printf(" -u: uAPI version to use (default is 2)\n" ); |
99 | exit(-1); |
100 | } |
101 | |
102 | static int wait_signal(void) |
103 | { |
104 | int sig; |
105 | sigset_t wset; |
106 | |
107 | sigemptyset(&wset); |
108 | sigaddset(&wset, SIGHUP); |
109 | sigaddset(&wset, SIGINT); |
110 | sigaddset(&wset, SIGTERM); |
111 | sigwait(&wset, &sig); |
112 | |
113 | return sig; |
114 | } |
115 | |
116 | int main(int argc, char *argv[]) |
117 | { |
118 | char *chip; |
119 | int opt, ret, cfd, lfd; |
120 | unsigned int offset, val = 0, abiv; |
121 | uint32_t flags_v1; |
122 | uint64_t flags_v2; |
123 | |
124 | abiv = 2; |
125 | ret = 0; |
126 | flags_v1 = GPIOHANDLE_REQUEST_INPUT; |
127 | flags_v2 = GPIO_V2_LINE_FLAG_INPUT; |
128 | |
129 | while ((opt = getopt(argc, argv, "lb:s:u:" )) != -1) { |
130 | switch (opt) { |
131 | case 'l': |
132 | flags_v1 |= GPIOHANDLE_REQUEST_ACTIVE_LOW; |
133 | flags_v2 |= GPIO_V2_LINE_FLAG_ACTIVE_LOW; |
134 | break; |
135 | case 'b': |
136 | if (strcmp("pull-up" , optarg) == 0) { |
137 | flags_v1 |= GPIOHANDLE_REQUEST_BIAS_PULL_UP; |
138 | flags_v2 |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP; |
139 | } else if (strcmp("pull-down" , optarg) == 0) { |
140 | flags_v1 |= GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; |
141 | flags_v2 |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN; |
142 | } else if (strcmp("disabled" , optarg) == 0) { |
143 | flags_v1 |= GPIOHANDLE_REQUEST_BIAS_DISABLE; |
144 | flags_v2 |= GPIO_V2_LINE_FLAG_BIAS_DISABLED; |
145 | } |
146 | break; |
147 | case 's': |
148 | val = atoi(optarg); |
149 | flags_v1 &= ~GPIOHANDLE_REQUEST_INPUT; |
150 | flags_v1 |= GPIOHANDLE_REQUEST_OUTPUT; |
151 | flags_v2 &= ~GPIO_V2_LINE_FLAG_INPUT; |
152 | flags_v2 |= GPIO_V2_LINE_FLAG_OUTPUT; |
153 | break; |
154 | case 'u': |
155 | abiv = atoi(optarg); |
156 | break; |
157 | default: |
158 | usage(prog: argv[0]); |
159 | } |
160 | } |
161 | |
162 | if (argc < optind + 2) |
163 | usage(prog: argv[0]); |
164 | |
165 | chip = argv[optind]; |
166 | offset = atoi(argv[optind + 1]); |
167 | |
168 | cfd = open(chip, 0); |
169 | if (cfd == -1) { |
170 | fprintf(stderr, "Failed to open %s: %s\n" , chip, strerror(errno)); |
171 | return -errno; |
172 | } |
173 | |
174 | if (abiv == 1) |
175 | lfd = request_line_v1(cfd, offset, flags: flags_v1, val); |
176 | else |
177 | lfd = request_line_v2(cfd, offset, flags: flags_v2, val); |
178 | |
179 | close(cfd); |
180 | |
181 | if (lfd < 0) { |
182 | fprintf(stderr, "Failed to request %s:%d: %s\n" , chip, offset, strerror(-lfd)); |
183 | return lfd; |
184 | } |
185 | |
186 | if (flags_v2 & GPIO_V2_LINE_FLAG_OUTPUT) { |
187 | wait_signal(); |
188 | } else { |
189 | if (abiv == 1) |
190 | ret = get_value_v1(lfd); |
191 | else |
192 | ret = get_value_v2(lfd); |
193 | } |
194 | |
195 | close(lfd); |
196 | |
197 | return ret; |
198 | } |
199 | |