bitbang: remove superfluous switch between jtag and swd
[openocd.git] / src / jtag / drivers / sysfsgpio.c
1 /***************************************************************************
2 * Copyright (C) 2012 by Creative Product Design, marc @ cpdesign.com.au *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
16 ***************************************************************************/
17
18 /* 2014-12: Addition of the SWD protocol support is based on the initial work
19 * on bcm2835gpio.c by Paul Fertser and modifications by Jean-Christian de Rivaz. */
20
21 /**
22 * @file
23 * This driver implements a bitbang jtag interface using gpio lines via
24 * sysfs.
25 * The aim of this driver implementation is use system GPIOs but avoid the
26 * need for a additional kernel driver.
27 * (Note memory mapped IO is another option, however it doesn't mix well with
28 * the kernel gpiolib driver - which makes sense I guess.)
29 *
30 * A gpio is required for tck, tms, tdi and tdo. One or both of srst and trst
31 * must be also be specified. The required jtag gpios are specified via the
32 * sysfsgpio_jtag_nums command or the relevant sysfsgpio_XXX_num commang.
33 * The srst and trst gpios are set via the sysfsgpio_srst_num and
34 * sysfsgpio_trst_num respectively. GPIO numbering follows the kernel
35 * convention of starting from 0.
36 *
37 * The gpios should not be in use by another entity, and must not be requested
38 * by a kernel driver without also being exported by it (otherwise they can't
39 * be exported by sysfs).
40 *
41 * The sysfs gpio interface can only manipulate one gpio at a time, so the
42 * bitbang write handler remembers the last state for tck, tms, tdi to avoid
43 * superfluous writes.
44 * For speed the sysfs "value" entry is opened at init and held open.
45 * This results in considerable gains over open-write-close (45s vs 900s)
46 *
47 * Further work could address:
48 * -srst and trst open drain/ push pull
49 * -configurable active high/low for srst & trst
50 */
51 #ifdef HAVE_CONFIG_H
52 #include "config.h"
53 #endif
54
55 #include <helper/time_support.h>
56 #include <jtag/interface.h>
57 #include "bitbang.h"
58
59 /*
60 * Helper func to determine if gpio number valid
61 *
62 * Assume here that there will be less than 10000 gpios on a system
63 */
64 static bool is_gpio_valid(int gpio)
65 {
66 return gpio >= 0 && gpio < 10000;
67 }
68
69 /*
70 * Helper func to open, write to and close a file
71 * name and valstr must be null terminated.
72 *
73 * Returns negative on failure.
74 */
75 static int open_write_close(const char *name, const char *valstr)
76 {
77 int ret;
78 int fd = open(name, O_WRONLY);
79 if (fd < 0)
80 return fd;
81
82 ret = write(fd, valstr, strlen(valstr));
83 close(fd);
84
85 return ret;
86 }
87
88 /*
89 * Helper func to unexport gpio from sysfs
90 */
91 static void unexport_sysfs_gpio(int gpio)
92 {
93 char gpiostr[5];
94
95 if (!is_gpio_valid(gpio))
96 return;
97
98 snprintf(gpiostr, sizeof(gpiostr), "%d", gpio);
99 if (open_write_close("/sys/class/gpio/unexport", gpiostr) < 0)
100 LOG_ERROR("Couldn't unexport gpio %d", gpio);
101 }
102
103 /*
104 * Exports and sets up direction for gpio.
105 * If the gpio is an output, it is initialized according to init_high,
106 * otherwise it is ignored.
107 *
108 * If the gpio is already exported we just show a warning and continue; if
109 * openocd happened to crash (or was killed by user) then the gpios will not
110 * have been cleaned up.
111 */
112 static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
113 {
114 struct timeval timeout, now;
115 char buf[40];
116 char gpiostr[5];
117 int ret;
118
119 if (!is_gpio_valid(gpio))
120 return ERROR_OK;
121
122 snprintf(gpiostr, sizeof(gpiostr), "%d", gpio);
123 ret = open_write_close("/sys/class/gpio/export", gpiostr);
124 if (ret < 0) {
125 if (errno == EBUSY) {
126 LOG_WARNING("gpio %d is already exported", gpio);
127 } else {
128 LOG_ERROR("Couldn't export gpio %d", gpio);
129 perror("sysfsgpio: ");
130 return ERROR_FAIL;
131 }
132 }
133
134 gettimeofday(&timeout, NULL);
135 timeval_add_time(&timeout, 0, 500000);
136
137 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);
138 for (;;) {
139 ret = open_write_close(buf, is_output ? (init_high ? "high" : "low") : "in");
140 if (ret >= 0 || errno != EACCES)
141 break;
142 gettimeofday(&now, NULL);
143 if (timeval_compare(&now, &timeout) >= 0)
144 break;
145 jtag_sleep(10000);
146 }
147 if (ret < 0) {
148 LOG_ERROR("Couldn't set direction for gpio %d", gpio);
149 perror("sysfsgpio: ");
150 unexport_sysfs_gpio(gpio);
151 return ERROR_FAIL;
152 }
153
154 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
155 for (;;) {
156 ret = open(buf, O_RDWR | O_NONBLOCK | O_SYNC);
157 if (ret >= 0 || errno != EACCES)
158 break;
159 gettimeofday(&now, NULL);
160 if (timeval_compare(&now, &timeout) >= 0)
161 break;
162 jtag_sleep(10000);
163 }
164 if (ret < 0) {
165 LOG_ERROR("Couldn't open value for gpio %d", gpio);
166 perror("sysfsgpio: ");
167 unexport_sysfs_gpio(gpio);
168 }
169
170 return ret;
171 }
172
173 /* gpio numbers for each gpio. Negative values are invalid */
174 static int tck_gpio = -1;
175 static int tms_gpio = -1;
176 static int tdi_gpio = -1;
177 static int tdo_gpio = -1;
178 static int trst_gpio = -1;
179 static int srst_gpio = -1;
180 static int swclk_gpio = -1;
181 static int swdio_gpio = -1;
182
183 /*
184 * file descriptors for /sys/class/gpio/gpioXX/value
185 * Set up during init.
186 */
187 static int tck_fd = -1;
188 static int tms_fd = -1;
189 static int tdi_fd = -1;
190 static int tdo_fd = -1;
191 static int trst_fd = -1;
192 static int srst_fd = -1;
193 static int swclk_fd = -1;
194 static int swdio_fd = -1;
195
196 static int last_swclk;
197 static int last_swdio;
198 static bool last_stored;
199 static bool swdio_input;
200
201 static void sysfsgpio_swdio_drive(bool is_output)
202 {
203 char buf[40];
204 int ret;
205
206 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", swdio_gpio);
207 ret = open_write_close(buf, is_output ? "high" : "in");
208 if (ret < 0) {
209 LOG_ERROR("Couldn't set direction for gpio %d", swdio_gpio);
210 perror("sysfsgpio: ");
211 }
212
213 last_stored = false;
214 swdio_input = !is_output;
215 }
216
217 static int sysfsgpio_swdio_read(void)
218 {
219 char buf[1];
220
221 /* important to seek to signal sysfs of new read */
222 lseek(swdio_fd, 0, SEEK_SET);
223 int ret = read(swdio_fd, &buf, sizeof(buf));
224
225 if (ret < 0) {
226 LOG_WARNING("reading swdio failed");
227 return 0;
228 }
229
230 return buf[0] != '0';
231 }
232
233 static void sysfsgpio_swdio_write(int swclk, int swdio)
234 {
235 const char one[] = "1";
236 const char zero[] = "0";
237
238 size_t bytes_written;
239
240 if (!swdio_input) {
241 if (!last_stored || (swdio != last_swdio)) {
242 bytes_written = write(swdio_fd, swdio ? &one : &zero, 1);
243 if (bytes_written != 1)
244 LOG_WARNING("writing swdio failed");
245 }
246 }
247
248 /* write swclk last */
249 if (!last_stored || (swclk != last_swclk)) {
250 bytes_written = write(swclk_fd, swclk ? &one : &zero, 1);
251 if (bytes_written != 1)
252 LOG_WARNING("writing swclk failed");
253 }
254
255 last_swdio = swdio;
256 last_swclk = swclk;
257 last_stored = true;
258 }
259
260 /*
261 * Bitbang interface read of TDO
262 *
263 * The sysfs value will read back either '0' or '1'. The trick here is to call
264 * lseek to bypass buffering in the sysfs kernel driver.
265 */
266 static bb_value_t sysfsgpio_read(void)
267 {
268 char buf[1];
269
270 /* important to seek to signal sysfs of new read */
271 lseek(tdo_fd, 0, SEEK_SET);
272 int ret = read(tdo_fd, &buf, sizeof(buf));
273
274 if (ret < 0) {
275 LOG_WARNING("reading tdo failed");
276 return 0;
277 }
278
279 return buf[0] == '0' ? BB_LOW : BB_HIGH;
280 }
281
282 /*
283 * Bitbang interface write of TCK, TMS, TDI
284 *
285 * Seeing as this is the only function where the outputs are changed,
286 * we can cache the old value to avoid needlessly writing it.
287 */
288 static int sysfsgpio_write(int tck, int tms, int tdi)
289 {
290 if (swd_mode) {
291 sysfsgpio_swdio_write(tck, tdi);
292 return ERROR_OK;
293 }
294
295 const char one[] = "1";
296 const char zero[] = "0";
297
298 static int last_tck;
299 static int last_tms;
300 static int last_tdi;
301
302 static int first_time;
303 size_t bytes_written;
304
305 if (!first_time) {
306 last_tck = !tck;
307 last_tms = !tms;
308 last_tdi = !tdi;
309 first_time = 1;
310 }
311
312 if (tdi != last_tdi) {
313 bytes_written = write(tdi_fd, tdi ? &one : &zero, 1);
314 if (bytes_written != 1)
315 LOG_WARNING("writing tdi failed");
316 }
317
318 if (tms != last_tms) {
319 bytes_written = write(tms_fd, tms ? &one : &zero, 1);
320 if (bytes_written != 1)
321 LOG_WARNING("writing tms failed");
322 }
323
324 /* write clk last */
325 if (tck != last_tck) {
326 bytes_written = write(tck_fd, tck ? &one : &zero, 1);
327 if (bytes_written != 1)
328 LOG_WARNING("writing tck failed");
329 }
330
331 last_tdi = tdi;
332 last_tms = tms;
333 last_tck = tck;
334
335 return ERROR_OK;
336 }
337
338 /*
339 * Bitbang interface to manipulate reset lines SRST and TRST
340 *
341 * (1) assert or (0) deassert reset lines
342 */
343 static int sysfsgpio_reset(int trst, int srst)
344 {
345 LOG_DEBUG("sysfsgpio_reset");
346 const char one[] = "1";
347 const char zero[] = "0";
348 size_t bytes_written;
349
350 /* assume active low */
351 if (srst_fd >= 0) {
352 bytes_written = write(srst_fd, srst ? &zero : &one, 1);
353 if (bytes_written != 1)
354 LOG_WARNING("writing srst failed");
355 }
356
357 /* assume active low */
358 if (trst_fd >= 0) {
359 bytes_written = write(trst_fd, trst ? &zero : &one, 1);
360 if (bytes_written != 1)
361 LOG_WARNING("writing trst failed");
362 }
363
364 return ERROR_OK;
365 }
366
367 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionums)
368 {
369 if (CMD_ARGC == 4) {
370 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
371 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tms_gpio);
372 COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], tdi_gpio);
373 COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], tdo_gpio);
374 } else if (CMD_ARGC != 0) {
375 return ERROR_COMMAND_SYNTAX_ERROR;
376 }
377
378 command_print(CMD,
379 "SysfsGPIO nums: tck = %d, tms = %d, tdi = %d, tdo = %d",
380 tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
381
382 return ERROR_OK;
383 }
384
385 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tck)
386 {
387 if (CMD_ARGC == 1)
388 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
389
390 command_print(CMD, "SysfsGPIO num: tck = %d", tck_gpio);
391 return ERROR_OK;
392 }
393
394 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tms)
395 {
396 if (CMD_ARGC == 1)
397 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio);
398
399 command_print(CMD, "SysfsGPIO num: tms = %d", tms_gpio);
400 return ERROR_OK;
401 }
402
403 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tdo)
404 {
405 if (CMD_ARGC == 1)
406 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio);
407
408 command_print(CMD, "SysfsGPIO num: tdo = %d", tdo_gpio);
409 return ERROR_OK;
410 }
411
412 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tdi)
413 {
414 if (CMD_ARGC == 1)
415 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio);
416
417 command_print(CMD, "SysfsGPIO num: tdi = %d", tdi_gpio);
418 return ERROR_OK;
419 }
420
421 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_srst)
422 {
423 if (CMD_ARGC == 1)
424 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio);
425
426 command_print(CMD, "SysfsGPIO num: srst = %d", srst_gpio);
427 return ERROR_OK;
428 }
429
430 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_trst)
431 {
432 if (CMD_ARGC == 1)
433 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio);
434
435 command_print(CMD, "SysfsGPIO num: trst = %d", trst_gpio);
436 return ERROR_OK;
437 }
438
439 COMMAND_HANDLER(sysfsgpio_handle_swd_gpionums)
440 {
441 if (CMD_ARGC == 2) {
442 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
443 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], swdio_gpio);
444 } else if (CMD_ARGC != 0) {
445 return ERROR_COMMAND_SYNTAX_ERROR;
446 }
447
448 command_print(CMD,
449 "SysfsGPIO nums: swclk = %d, swdio = %d",
450 swclk_gpio, swdio_gpio);
451
452 return ERROR_OK;
453 }
454
455 COMMAND_HANDLER(sysfsgpio_handle_swd_gpionum_swclk)
456 {
457 if (CMD_ARGC == 1)
458 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
459
460 command_print(CMD, "SysfsGPIO num: swclk = %d", swclk_gpio);
461 return ERROR_OK;
462 }
463
464 COMMAND_HANDLER(sysfsgpio_handle_swd_gpionum_swdio)
465 {
466 if (CMD_ARGC == 1)
467 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_gpio);
468
469 command_print(CMD, "SysfsGPIO num: swdio = %d", swdio_gpio);
470 return ERROR_OK;
471 }
472
473 static const struct command_registration sysfsgpio_command_handlers[] = {
474 {
475 .name = "sysfsgpio_jtag_nums",
476 .handler = &sysfsgpio_handle_jtag_gpionums,
477 .mode = COMMAND_CONFIG,
478 .help = "gpio numbers for tck, tms, tdi, tdo. (in that order)",
479 .usage = "[tck tms tdi tdo]",
480 },
481 {
482 .name = "sysfsgpio_tck_num",
483 .handler = &sysfsgpio_handle_jtag_gpionum_tck,
484 .mode = COMMAND_CONFIG,
485 .help = "gpio number for tck.",
486 .usage = "[tck]",
487 },
488 {
489 .name = "sysfsgpio_tms_num",
490 .handler = &sysfsgpio_handle_jtag_gpionum_tms,
491 .mode = COMMAND_CONFIG,
492 .help = "gpio number for tms.",
493 .usage = "[tms]",
494 },
495 {
496 .name = "sysfsgpio_tdo_num",
497 .handler = &sysfsgpio_handle_jtag_gpionum_tdo,
498 .mode = COMMAND_CONFIG,
499 .help = "gpio number for tdo.",
500 .usage = "[tdo]",
501 },
502 {
503 .name = "sysfsgpio_tdi_num",
504 .handler = &sysfsgpio_handle_jtag_gpionum_tdi,
505 .mode = COMMAND_CONFIG,
506 .help = "gpio number for tdi.",
507 .usage = "[tdi]",
508 },
509 {
510 .name = "sysfsgpio_srst_num",
511 .handler = &sysfsgpio_handle_jtag_gpionum_srst,
512 .mode = COMMAND_CONFIG,
513 .help = "gpio number for srst.",
514 .usage = "[srst]",
515 },
516 {
517 .name = "sysfsgpio_trst_num",
518 .handler = &sysfsgpio_handle_jtag_gpionum_trst,
519 .mode = COMMAND_CONFIG,
520 .help = "gpio number for trst.",
521 .usage = "[trst]",
522 },
523 {
524 .name = "sysfsgpio_swd_nums",
525 .handler = &sysfsgpio_handle_swd_gpionums,
526 .mode = COMMAND_CONFIG,
527 .help = "gpio numbers for swclk, swdio. (in that order)",
528 .usage = "[swclk swdio]",
529 },
530 {
531 .name = "sysfsgpio_swclk_num",
532 .handler = &sysfsgpio_handle_swd_gpionum_swclk,
533 .mode = COMMAND_CONFIG,
534 .help = "gpio number for swclk.",
535 .usage = "[swclk]",
536 },
537 {
538 .name = "sysfsgpio_swdio_num",
539 .handler = &sysfsgpio_handle_swd_gpionum_swdio,
540 .mode = COMMAND_CONFIG,
541 .help = "gpio number for swdio.",
542 .usage = "[swdio]",
543 },
544 COMMAND_REGISTRATION_DONE
545 };
546
547 static int sysfsgpio_init(void);
548 static int sysfsgpio_quit(void);
549
550 static const char * const sysfsgpio_transports[] = { "jtag", "swd", NULL };
551
552 static struct jtag_interface sysfsgpio_interface = {
553 .supported = DEBUG_CAP_TMS_SEQ,
554 .execute_queue = bitbang_execute_queue,
555 };
556
557 struct adapter_driver sysfsgpio_adapter_driver = {
558 .name = "sysfsgpio",
559 .transports = sysfsgpio_transports,
560 .commands = sysfsgpio_command_handlers,
561
562 .init = sysfsgpio_init,
563 .quit = sysfsgpio_quit,
564 .reset = sysfsgpio_reset,
565
566 .jtag_ops = &sysfsgpio_interface,
567 .swd_ops = &bitbang_swd,
568 };
569
570 static struct bitbang_interface sysfsgpio_bitbang = {
571 .read = sysfsgpio_read,
572 .write = sysfsgpio_write,
573 .swdio_read = sysfsgpio_swdio_read,
574 .swdio_drive = sysfsgpio_swdio_drive,
575 .blink = 0
576 };
577
578 /* helper func to close and cleanup files only if they were valid/ used */
579 static void cleanup_fd(int fd, int gpio)
580 {
581 if (gpio >= 0) {
582 if (fd >= 0)
583 close(fd);
584
585 unexport_sysfs_gpio(gpio);
586 }
587 }
588
589 static void cleanup_all_fds(void)
590 {
591 cleanup_fd(tck_fd, tck_gpio);
592 cleanup_fd(tms_fd, tms_gpio);
593 cleanup_fd(tdi_fd, tdi_gpio);
594 cleanup_fd(tdo_fd, tdo_gpio);
595 cleanup_fd(trst_fd, trst_gpio);
596 cleanup_fd(srst_fd, srst_gpio);
597 cleanup_fd(swclk_fd, swclk_gpio);
598 cleanup_fd(swdio_fd, swdio_gpio);
599 }
600
601 static bool sysfsgpio_jtag_mode_possible(void)
602 {
603 if (!is_gpio_valid(tck_gpio))
604 return false;
605 if (!is_gpio_valid(tms_gpio))
606 return false;
607 if (!is_gpio_valid(tdi_gpio))
608 return false;
609 if (!is_gpio_valid(tdo_gpio))
610 return false;
611 return true;
612 }
613
614 static bool sysfsgpio_swd_mode_possible(void)
615 {
616 if (!is_gpio_valid(swclk_gpio))
617 return false;
618 if (!is_gpio_valid(swdio_gpio))
619 return false;
620 return true;
621 }
622
623 static int sysfsgpio_init(void)
624 {
625 bitbang_interface = &sysfsgpio_bitbang;
626
627 LOG_INFO("SysfsGPIO JTAG/SWD bitbang driver");
628
629 if (sysfsgpio_jtag_mode_possible()) {
630 if (sysfsgpio_swd_mode_possible())
631 LOG_INFO("JTAG and SWD modes enabled");
632 else
633 LOG_INFO("JTAG only mode enabled (specify swclk and swdio gpio to add SWD mode)");
634 } else if (sysfsgpio_swd_mode_possible()) {
635 LOG_INFO("SWD only mode enabled (specify tck, tms, tdi and tdo gpios to add JTAG mode)");
636 } else {
637 LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode and/or swclk and swdio gpio for SWD mode");
638 return ERROR_JTAG_INIT_FAILED;
639 }
640
641
642 /*
643 * Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST
644 * as outputs. Drive TDI and TCK low, and TMS/TRST/SRST high.
645 * For SWD, SWCLK and SWDIO are configures as output high.
646 */
647 if (tck_gpio >= 0) {
648 tck_fd = setup_sysfs_gpio(tck_gpio, 1, 0);
649 if (tck_fd < 0)
650 goto out_error;
651 }
652
653 if (tms_gpio >= 0) {
654 tms_fd = setup_sysfs_gpio(tms_gpio, 1, 1);
655 if (tms_fd < 0)
656 goto out_error;
657 }
658
659 if (tdi_gpio >= 0) {
660 tdi_fd = setup_sysfs_gpio(tdi_gpio, 1, 0);
661 if (tdi_fd < 0)
662 goto out_error;
663 }
664
665 if (tdo_gpio >= 0) {
666 tdo_fd = setup_sysfs_gpio(tdo_gpio, 0, 0);
667 if (tdo_fd < 0)
668 goto out_error;
669 }
670
671 /* assume active low*/
672 if (trst_gpio >= 0) {
673 trst_fd = setup_sysfs_gpio(trst_gpio, 1, 1);
674 if (trst_fd < 0)
675 goto out_error;
676 }
677
678 /* assume active low*/
679 if (srst_gpio >= 0) {
680 srst_fd = setup_sysfs_gpio(srst_gpio, 1, 1);
681 if (srst_fd < 0)
682 goto out_error;
683 }
684
685 if (swclk_gpio >= 0) {
686 swclk_fd = setup_sysfs_gpio(swclk_gpio, 1, 0);
687 if (swclk_fd < 0)
688 goto out_error;
689 }
690
691 if (swdio_gpio >= 0) {
692 swdio_fd = setup_sysfs_gpio(swdio_gpio, 1, 0);
693 if (swdio_fd < 0)
694 goto out_error;
695 }
696
697 return ERROR_OK;
698
699 out_error:
700 cleanup_all_fds();
701 return ERROR_JTAG_INIT_FAILED;
702 }
703
704 static int sysfsgpio_quit(void)
705 {
706 cleanup_all_fds();
707 return ERROR_OK;
708 }

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)