openocd: fix SPDX tag format for files .c
[openocd.git] / contrib / remote_bitbang / remote_bitbang_sysfsgpio.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4 * Copyright (C) 2013 Paul Fertser <fercerpav@gmail.com> *
5 * Copyright (C) 2012 by Creative Product Design, marc @ cpdesign.com.au *
6 ***************************************************************************/
7
8 /*
9 This is a test application to be used as a remote bitbang server for
10 the OpenOCD remote_bitbang interface driver.
11
12 To compile run:
13 gcc -Wall -ansi -pedantic -std=c99 -o remote_bitbang_sysfsgpio remote_bitbang_sysfsgpio.c
14
15
16 Usage example:
17
18 On Raspberry Pi run:
19 socat TCP6-LISTEN:7777,fork EXEC:"sudo ./remote_bitbang_sysfsgpio tck 11 tms 25 tdo 9 tdi 10"
20
21 On host run:
22 openocd -c "interface remote_bitbang; remote_bitbang host raspberrypi; remote_bitbang port 7777" \
23 -f target/stm32f1x.cfg
24
25 Or if you want to test UNIX sockets, run both on Raspberry Pi:
26 socat UNIX-LISTEN:/tmp/remotebitbang-socket,fork EXEC:"sudo ./remote_bitbang_sysfsgpio tck 11 tms 25 tdo 9 tdi 10"
27 openocd -c "interface remote_bitbang; remote_bitbang host /tmp/remotebitbang-socket" -f target/stm32f1x.cfg
28 */
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <errno.h>
38
39 #define LOG_ERROR(...) do { \
40 fprintf(stderr, __VA_ARGS__); \
41 fputc('\n', stderr); \
42 } while (0)
43 #define LOG_WARNING(...) LOG_ERROR(__VA_ARGS__)
44
45 #define ERROR_OK (-1)
46 #define ERROR_FAIL (-2)
47 #define ERROR_JTAG_INIT_FAILED ERROR_FAIL
48
49 /*
50 * Helper func to determine if gpio number valid
51 *
52 * Assume here that there will be less than 1000 gpios on a system
53 */
54 static int is_gpio_valid(int gpio)
55 {
56 return gpio >= 0 && gpio < 1000;
57 }
58
59 /*
60 * Helper func to open, write to and close a file
61 * name and valstr must be null terminated.
62 *
63 * Returns negative on failure.
64 */
65 static int open_write_close(const char *name, const char *valstr)
66 {
67 int ret;
68 int fd = open(name, O_WRONLY);
69 if (fd < 0)
70 return fd;
71
72 ret = write(fd, valstr, strlen(valstr));
73 close(fd);
74
75 return ret;
76 }
77
78 /*
79 * Helper func to unexport gpio from sysfs
80 */
81 static void unexport_sysfs_gpio(int gpio)
82 {
83 char gpiostr[4];
84
85 if (!is_gpio_valid(gpio))
86 return;
87
88 snprintf(gpiostr, sizeof(gpiostr), "%d", gpio);
89 if (open_write_close("/sys/class/gpio/unexport", gpiostr) < 0)
90 LOG_ERROR("Couldn't unexport gpio %d", gpio);
91
92 return;
93 }
94
95 /*
96 * Exports and sets up direction for gpio.
97 * If the gpio is an output, it is initialized according to init_high,
98 * otherwise it is ignored.
99 *
100 * If the gpio is already exported we just show a warning and continue; if
101 * openocd happened to crash (or was killed by user) then the gpios will not
102 * have been cleaned up.
103 */
104 static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
105 {
106 char buf[40];
107 char gpiostr[4];
108 int ret;
109
110 if (!is_gpio_valid(gpio))
111 return ERROR_OK;
112
113 snprintf(gpiostr, sizeof(gpiostr), "%d", gpio);
114 ret = open_write_close("/sys/class/gpio/export", gpiostr);
115 if (ret < 0) {
116 if (errno == EBUSY) {
117 LOG_WARNING("gpio %d is already exported", gpio);
118 } else {
119 LOG_ERROR("Couldn't export gpio %d", gpio);
120 perror("sysfsgpio: ");
121 return ERROR_FAIL;
122 }
123 }
124
125 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);
126 ret = open_write_close(buf, is_output ? (init_high ? "high" : "low") : "in");
127 if (ret < 0) {
128 LOG_ERROR("Couldn't set direction for gpio %d", gpio);
129 perror("sysfsgpio: ");
130 unexport_sysfs_gpio(gpio);
131 return ERROR_FAIL;
132 }
133
134 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
135 if (is_output)
136 ret = open(buf, O_WRONLY | O_NONBLOCK | O_SYNC);
137 else
138 ret = open(buf, O_RDONLY | O_NONBLOCK | O_SYNC);
139
140 if (ret < 0)
141 unexport_sysfs_gpio(gpio);
142
143 return ret;
144 }
145
146 /*
147 * file descriptors for /sys/class/gpio/gpioXX/value
148 * Set up during init.
149 */
150 static int tck_fd = -1;
151 static int tms_fd = -1;
152 static int tdi_fd = -1;
153 static int tdo_fd = -1;
154 static int trst_fd = -1;
155 static int srst_fd = -1;
156
157 /*
158 * Bitbang interface read of TDO
159 *
160 * The sysfs value will read back either '0' or '1'. The trick here is to call
161 * lseek to bypass buffering in the sysfs kernel driver.
162 */
163 static int sysfsgpio_read(void)
164 {
165 char buf[1];
166
167 /* important to seek to signal sysfs of new read */
168 lseek(tdo_fd, 0, SEEK_SET);
169 int ret = read(tdo_fd, &buf, sizeof(buf));
170
171 if (ret < 0) {
172 LOG_WARNING("reading tdo failed");
173 return 0;
174 }
175
176 return buf[0];
177 }
178
179 /*
180 * Bitbang interface write of TCK, TMS, TDI
181 *
182 * Seeing as this is the only function where the outputs are changed,
183 * we can cache the old value to avoid needlessly writing it.
184 */
185 static void sysfsgpio_write(int tck, int tms, int tdi)
186 {
187 const char one[] = "1";
188 const char zero[] = "0";
189
190 static int last_tck;
191 static int last_tms;
192 static int last_tdi;
193
194 static int first_time;
195 size_t bytes_written;
196
197 if (!first_time) {
198 last_tck = !tck;
199 last_tms = !tms;
200 last_tdi = !tdi;
201 first_time = 1;
202 }
203
204 if (tdi != last_tdi) {
205 bytes_written = write(tdi_fd, tdi ? &one : &zero, 1);
206 if (bytes_written != 1)
207 LOG_WARNING("writing tdi failed");
208 }
209
210 if (tms != last_tms) {
211 bytes_written = write(tms_fd, tms ? &one : &zero, 1);
212 if (bytes_written != 1)
213 LOG_WARNING("writing tms failed");
214 }
215
216 /* write clk last */
217 if (tck != last_tck) {
218 bytes_written = write(tck_fd, tck ? &one : &zero, 1);
219 if (bytes_written != 1)
220 LOG_WARNING("writing tck failed");
221 }
222
223 last_tdi = tdi;
224 last_tms = tms;
225 last_tck = tck;
226 }
227
228 /*
229 * Bitbang interface to manipulate reset lines SRST and TRST
230 *
231 * (1) assert or (0) deassert reset lines
232 */
233 static void sysfsgpio_reset(int trst, int srst)
234 {
235 const char one[] = "1";
236 const char zero[] = "0";
237 size_t bytes_written;
238
239 /* assume active low */
240 if (srst_fd >= 0) {
241 bytes_written = write(srst_fd, srst ? &zero : &one, 1);
242 if (bytes_written != 1)
243 LOG_WARNING("writing srst failed");
244 }
245
246 /* assume active low */
247 if (trst_fd >= 0) {
248 bytes_written = write(trst_fd, trst ? &zero : &one, 1);
249 if (bytes_written != 1)
250 LOG_WARNING("writing trst failed");
251 }
252 }
253
254 /* gpio numbers for each gpio. Negative values are invalid */
255 static int tck_gpio = -1;
256 static int tms_gpio = -1;
257 static int tdi_gpio = -1;
258 static int tdo_gpio = -1;
259 static int trst_gpio = -1;
260 static int srst_gpio = -1;
261
262 /* helper func to close and cleanup files only if they were valid/ used */
263 static void cleanup_fd(int fd, int gpio)
264 {
265 if (gpio >= 0) {
266 if (fd >= 0)
267 close(fd);
268
269 unexport_sysfs_gpio(gpio);
270 }
271 }
272
273 static void cleanup_all_fds(void)
274 {
275 cleanup_fd(tck_fd, tck_gpio);
276 cleanup_fd(tms_fd, tms_gpio);
277 cleanup_fd(tdi_fd, tdi_gpio);
278 cleanup_fd(tdo_fd, tdo_gpio);
279 cleanup_fd(trst_fd, trst_gpio);
280 cleanup_fd(srst_fd, srst_gpio);
281 }
282
283 static void process_remote_protocol(void)
284 {
285 int c;
286 while (1) {
287 c = getchar();
288 if (c == EOF || c == 'Q') /* Quit */
289 break;
290 else if (c == 'b' || c == 'B') /* Blink */
291 continue;
292 else if (c >= 'r' && c <= 'r' + 3) { /* Reset */
293 char d = c - 'r';
294 sysfsgpio_reset(!!(d & 2),
295 (d & 1));
296 } else if (c >= '0' && c <= '0' + 7) {/* Write */
297 char d = c - '0';
298 sysfsgpio_write(!!(d & 4),
299 !!(d & 2),
300 (d & 1));
301 } else if (c == 'R')
302 putchar(sysfsgpio_read());
303 else
304 LOG_ERROR("Unknown command '%c' received", c);
305 }
306 }
307
308 int main(int argc, char *argv[])
309 {
310 LOG_WARNING("SysfsGPIO remote_bitbang JTAG driver\n");
311
312 for (int i = 1; i < argc; i++) {
313 if (!strcmp(argv[i], "tck"))
314 tck_gpio = atoi(argv[++i]);
315 else if (!strcmp(argv[i], "tms"))
316 tms_gpio = atoi(argv[++i]);
317 else if (!strcmp(argv[i], "tdo"))
318 tdo_gpio = atoi(argv[++i]);
319 else if (!strcmp(argv[i], "tdi"))
320 tdi_gpio = atoi(argv[++i]);
321 else if (!strcmp(argv[i], "trst"))
322 trst_gpio = atoi(argv[++i]);
323 else if (!strcmp(argv[i], "srst"))
324 srst_gpio = atoi(argv[++i]);
325 else {
326 LOG_ERROR("Usage:\n%s ((tck|tms|tdo|tdi|trst|srst) num)*", argv[0]);
327 return -1;
328 }
329 }
330
331 if (!(is_gpio_valid(tck_gpio)
332 && is_gpio_valid(tms_gpio)
333 && is_gpio_valid(tdi_gpio)
334 && is_gpio_valid(tdo_gpio))) {
335 if (!is_gpio_valid(tck_gpio))
336 LOG_ERROR("gpio num for tck is invalid");
337 if (!is_gpio_valid(tms_gpio))
338 LOG_ERROR("gpio num for tms is invalid");
339 if (!is_gpio_valid(tdo_gpio))
340 LOG_ERROR("gpio num for tdo is invalid");
341 if (!is_gpio_valid(tdi_gpio))
342 LOG_ERROR("gpio num for tdi is invalid");
343
344 LOG_ERROR("Require tck, tms, tdi and tdo gpios to all be specified");
345 return ERROR_JTAG_INIT_FAILED;
346 }
347
348 /*
349 * Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST
350 * as outputs. Drive TDI and TCK low, and TMS/TRST/SRST high.
351 */
352 tck_fd = setup_sysfs_gpio(tck_gpio, 1, 0);
353 if (tck_fd < 0)
354 goto out_error;
355
356 tms_fd = setup_sysfs_gpio(tms_gpio, 1, 1);
357 if (tms_fd < 0)
358 goto out_error;
359
360 tdi_fd = setup_sysfs_gpio(tdi_gpio, 1, 0);
361 if (tdi_fd < 0)
362 goto out_error;
363
364 tdo_fd = setup_sysfs_gpio(tdo_gpio, 0, 0);
365 if (tdo_fd < 0)
366 goto out_error;
367
368 /* assume active low */
369 if (trst_gpio > 0) {
370 trst_fd = setup_sysfs_gpio(trst_gpio, 1, 1);
371 if (trst_fd < 0)
372 goto out_error;
373 }
374
375 /* assume active low */
376 if (srst_gpio > 0) {
377 srst_fd = setup_sysfs_gpio(srst_gpio, 1, 1);
378 if (srst_fd < 0)
379 goto out_error;
380 }
381
382 LOG_WARNING("SysfsGPIO nums: tck = %d, tms = %d, tdi = %d, tdo = %d",
383 tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
384 LOG_WARNING("SysfsGPIO num: srst = %d", srst_gpio);
385 LOG_WARNING("SysfsGPIO num: trst = %d", trst_gpio);
386
387 setvbuf(stdout, NULL, _IONBF, 0);
388 process_remote_protocol();
389
390 cleanup_all_fds();
391 return 0;
392 out_error:
393 cleanup_all_fds();
394 return ERROR_JTAG_INIT_FAILED;
395 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)