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