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(argv[0]);
159 		}
160 	}
161 
162 	if (argc < optind + 2)
163 		usage(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_v1, val);
176 	else
177 		lfd = request_line_v2(cfd, offset, 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