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

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)