contrib: replace the GPLv2-or-later license tag
[fw/openocd] / 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 }