jtag: linuxgpiod: drop extra parenthesis
[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 commands.
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 <transport/transport.h>
58 #include "bitbang.h"
59
60 /*
61 * Helper func to determine if gpio number valid
62 *
63 * Assume here that there will be less than 10000 gpios on a system
64 */
65 static bool is_gpio_valid(int gpio)
66 {
67 return gpio >= 0 && gpio < 10000;
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[5];
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
104 /*
105 * Exports and sets up direction for gpio.
106 * If the gpio is an output, it is initialized according to init_high,
107 * otherwise it is ignored.
108 *
109 * If the gpio is already exported we just show a warning and continue; if
110 * openocd happened to crash (or was killed by user) then the gpios will not
111 * have been cleaned up.
112 */
113 static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
114 {
115 struct timeval timeout, now;
116 char buf[40];
117 char gpiostr[5];
118 int ret;
119
120 if (!is_gpio_valid(gpio))
121 return ERROR_OK;
122
123 snprintf(gpiostr, sizeof(gpiostr), "%d", gpio);
124 ret = open_write_close("/sys/class/gpio/export", gpiostr);
125 if (ret < 0) {
126 if (errno == EBUSY) {
127 LOG_WARNING("gpio %d is already exported", gpio);
128 } else {
129 LOG_ERROR("Couldn't export gpio %d", gpio);
130 LOG_ERROR("sysfsgpio: %s", strerror(errno));
131 return ERROR_FAIL;
132 }
133 }
134
135 gettimeofday(&timeout, NULL);
136 timeval_add_time(&timeout, 0, 500000);
137
138 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);
139 for (;;) {
140 ret = open_write_close(buf, is_output ? (init_high ? "high" : "low") : "in");
141 if (ret >= 0 || errno != EACCES)
142 break;
143 gettimeofday(&now, NULL);
144 if (timeval_compare(&now, &timeout) >= 0)
145 break;
146 jtag_sleep(10000);
147 }
148 if (ret < 0) {
149 LOG_ERROR("Couldn't set direction for gpio %d", gpio);
150 LOG_ERROR("sysfsgpio: %s", strerror(errno));
151 unexport_sysfs_gpio(gpio);
152 return ERROR_FAIL;
153 }
154
155 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
156 for (;;) {
157 ret = open(buf, O_RDWR | O_NONBLOCK | O_SYNC);
158 if (ret >= 0 || errno != EACCES)
159 break;
160 gettimeofday(&now, NULL);
161 if (timeval_compare(&now, &timeout) >= 0)
162 break;
163 jtag_sleep(10000);
164 }
165 if (ret < 0) {
166 LOG_ERROR("Couldn't open value for gpio %d", gpio);
167 LOG_ERROR("sysfsgpio: %s", strerror(errno));
168 unexport_sysfs_gpio(gpio);
169 }
170
171 return ret;
172 }
173
174 /* gpio numbers for each gpio. Negative values are invalid */
175 static int tck_gpio = -1;
176 static int tms_gpio = -1;
177 static int tdi_gpio = -1;
178 static int tdo_gpio = -1;
179 static int trst_gpio = -1;
180 static int srst_gpio = -1;
181 static int swclk_gpio = -1;
182 static int swdio_gpio = -1;
183
184 /*
185 * file descriptors for /sys/class/gpio/gpioXX/value
186 * Set up during init.
187 */
188 static int tck_fd = -1;
189 static int tms_fd = -1;
190 static int tdi_fd = -1;
191 static int tdo_fd = -1;
192 static int trst_fd = -1;
193 static int srst_fd = -1;
194 static int swclk_fd = -1;
195 static int swdio_fd = -1;
196
197 static int last_swclk;
198 static int last_swdio;
199 static bool last_stored;
200 static bool swdio_input;
201
202 static void sysfsgpio_swdio_drive(bool is_output)
203 {
204 char buf[40];
205 int ret;
206
207 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", swdio_gpio);
208 ret = open_write_close(buf, is_output ? "high" : "in");
209 if (ret < 0) {
210 LOG_ERROR("Couldn't set direction for gpio %d", swdio_gpio);
211 LOG_ERROR("sysfsgpio: %s", strerror(errno));
212 }
213
214 last_stored = false;
215 swdio_input = !is_output;
216 }
217
218 static int sysfsgpio_swdio_read(void)
219 {
220 char buf[1];
221
222 /* important to seek to signal sysfs of new read */
223 lseek(swdio_fd, 0, SEEK_SET);
224 int ret = read(swdio_fd, &buf, sizeof(buf));
225
226 if (ret < 0) {
227 LOG_WARNING("reading swdio failed");
228 return 0;
229 }
230
231 return buf[0] != '0';
232 }
233
234 static int sysfsgpio_swd_write(int swclk, int swdio)
235 {
236 const char one[] = "1";
237 const char zero[] = "0";
238
239 size_t bytes_written;
240
241 if (!swdio_input) {
242 if (!last_stored || (swdio != last_swdio)) {
243 bytes_written = write(swdio_fd, swdio ? &one : &zero, 1);
244 if (bytes_written != 1)
245 LOG_WARNING("writing swdio failed");
246 }
247 }
248
249 /* write swclk last */
250 if (!last_stored || (swclk != last_swclk)) {
251 bytes_written = write(swclk_fd, swclk ? &one : &zero, 1);
252 if (bytes_written != 1)
253 LOG_WARNING("writing swclk failed");
254 }
255
256 last_swdio = swdio;
257 last_swclk = swclk;
258 last_stored = true;
259
260 return ERROR_OK;
261 }
262
263 /*
264 * Bitbang interface read of TDO
265 *
266 * The sysfs value will read back either '0' or '1'. The trick here is to call
267 * lseek to bypass buffering in the sysfs kernel driver.
268 */
269 static bb_value_t sysfsgpio_read(void)
270 {
271 char buf[1];
272
273 /* important to seek to signal sysfs of new read */
274 lseek(tdo_fd, 0, SEEK_SET);
275 int ret = read(tdo_fd, &buf, sizeof(buf));
276
277 if (ret < 0) {
278 LOG_WARNING("reading tdo failed");
279 return 0;
280 }
281
282 return buf[0] == '0' ? BB_LOW : BB_HIGH;
283 }
284
285 /*
286 * Bitbang interface write of TCK, TMS, TDI
287 *
288 * Seeing as this is the only function where the outputs are changed,
289 * we can cache the old value to avoid needlessly writing it.
290 */
291 static int sysfsgpio_write(int tck, int tms, int tdi)
292 {
293 const char one[] = "1";
294 const char zero[] = "0";
295
296 static int last_tck;
297 static int last_tms;
298 static int last_tdi;
299
300 static int first_time;
301 size_t bytes_written;
302
303 if (!first_time) {
304 last_tck = !tck;
305 last_tms = !tms;
306 last_tdi = !tdi;
307 first_time = 1;
308 }
309
310 if (tdi != last_tdi) {
311 bytes_written = write(tdi_fd, tdi ? &one : &zero, 1);
312 if (bytes_written != 1)
313 LOG_WARNING("writing tdi failed");
314 }
315
316 if (tms != last_tms) {
317 bytes_written = write(tms_fd, tms ? &one : &zero, 1);
318 if (bytes_written != 1)
319 LOG_WARNING("writing tms failed");
320 }
321
322 /* write clk last */
323 if (tck != last_tck) {
324 bytes_written = write(tck_fd, tck ? &one : &zero, 1);
325 if (bytes_written != 1)
326 LOG_WARNING("writing tck failed");
327 }
328
329 last_tdi = tdi;
330 last_tms = tms;
331 last_tck = tck;
332
333 return ERROR_OK;
334 }
335
336 /*
337 * Bitbang interface to manipulate reset lines SRST and TRST
338 *
339 * (1) assert or (0) deassert reset lines
340 */
341 static int sysfsgpio_reset(int trst, int srst)
342 {
343 LOG_DEBUG("sysfsgpio_reset");
344 const char one[] = "1";
345 const char zero[] = "0";
346 size_t bytes_written;
347
348 /* assume active low */
349 if (srst_fd >= 0) {
350 bytes_written = write(srst_fd, srst ? &zero : &one, 1);
351 if (bytes_written != 1)
352 LOG_WARNING("writing srst failed");
353 }
354
355 /* assume active low */
356 if (trst_fd >= 0) {
357 bytes_written = write(trst_fd, trst ? &zero : &one, 1);
358 if (bytes_written != 1)
359 LOG_WARNING("writing trst failed");
360 }
361
362 return ERROR_OK;
363 }
364
365 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionums)
366 {
367 if (CMD_ARGC == 4) {
368 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
369 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tms_gpio);
370 COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], tdi_gpio);
371 COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], tdo_gpio);
372 } else if (CMD_ARGC != 0) {
373 return ERROR_COMMAND_SYNTAX_ERROR;
374 }
375
376 command_print(CMD,
377 "SysfsGPIO nums: tck = %d, tms = %d, tdi = %d, tdo = %d",
378 tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
379
380 return ERROR_OK;
381 }
382
383 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tck)
384 {
385 if (CMD_ARGC == 1)
386 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
387
388 command_print(CMD, "SysfsGPIO num: tck = %d", tck_gpio);
389 return ERROR_OK;
390 }
391
392 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tms)
393 {
394 if (CMD_ARGC == 1)
395 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio);
396
397 command_print(CMD, "SysfsGPIO num: tms = %d", tms_gpio);
398 return ERROR_OK;
399 }
400
401 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tdo)
402 {
403 if (CMD_ARGC == 1)
404 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio);
405
406 command_print(CMD, "SysfsGPIO num: tdo = %d", tdo_gpio);
407 return ERROR_OK;
408 }
409
410 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tdi)
411 {
412 if (CMD_ARGC == 1)
413 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio);
414
415 command_print(CMD, "SysfsGPIO num: tdi = %d", tdi_gpio);
416 return ERROR_OK;
417 }
418
419 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_srst)
420 {
421 if (CMD_ARGC == 1)
422 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio);
423
424 command_print(CMD, "SysfsGPIO num: srst = %d", srst_gpio);
425 return ERROR_OK;
426 }
427
428 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_trst)
429 {
430 if (CMD_ARGC == 1)
431 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio);
432
433 command_print(CMD, "SysfsGPIO num: trst = %d", trst_gpio);
434 return ERROR_OK;
435 }
436
437 COMMAND_HANDLER(sysfsgpio_handle_swd_gpionums)
438 {
439 if (CMD_ARGC == 2) {
440 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
441 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], swdio_gpio);
442 } else if (CMD_ARGC != 0) {
443 return ERROR_COMMAND_SYNTAX_ERROR;
444 }
445
446 command_print(CMD,
447 "SysfsGPIO nums: swclk = %d, swdio = %d",
448 swclk_gpio, swdio_gpio);
449
450 return ERROR_OK;
451 }
452
453 COMMAND_HANDLER(sysfsgpio_handle_swd_gpionum_swclk)
454 {
455 if (CMD_ARGC == 1)
456 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
457
458 command_print(CMD, "SysfsGPIO num: swclk = %d", swclk_gpio);
459 return ERROR_OK;
460 }
461
462 COMMAND_HANDLER(sysfsgpio_handle_swd_gpionum_swdio)
463 {
464 if (CMD_ARGC == 1)
465 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_gpio);
466
467 command_print(CMD, "SysfsGPIO num: swdio = %d", swdio_gpio);
468 return ERROR_OK;
469 }
470
471 static const struct command_registration sysfsgpio_subcommand_handlers[] = {
472 {
473 .name = "jtag_nums",
474 .handler = &sysfsgpio_handle_jtag_gpionums,
475 .mode = COMMAND_CONFIG,
476 .help = "gpio numbers for tck, tms, tdi, tdo. (in that order)",
477 .usage = "[tck tms tdi tdo]",
478 },
479 {
480 .name = "tck_num",
481 .handler = &sysfsgpio_handle_jtag_gpionum_tck,
482 .mode = COMMAND_CONFIG,
483 .help = "gpio number for tck.",
484 .usage = "[tck]",
485 },
486 {
487 .name = "tms_num",
488 .handler = &sysfsgpio_handle_jtag_gpionum_tms,
489 .mode = COMMAND_CONFIG,
490 .help = "gpio number for tms.",
491 .usage = "[tms]",
492 },
493 {
494 .name = "tdo_num",
495 .handler = &sysfsgpio_handle_jtag_gpionum_tdo,
496 .mode = COMMAND_CONFIG,
497 .help = "gpio number for tdo.",
498 .usage = "[tdo]",
499 },
500 {
501 .name = "tdi_num",
502 .handler = &sysfsgpio_handle_jtag_gpionum_tdi,
503 .mode = COMMAND_CONFIG,
504 .help = "gpio number for tdi.",
505 .usage = "[tdi]",
506 },
507 {
508 .name = "srst_num",
509 .handler = &sysfsgpio_handle_jtag_gpionum_srst,
510 .mode = COMMAND_CONFIG,
511 .help = "gpio number for srst.",
512 .usage = "[srst]",
513 },
514 {
515 .name = "trst_num",
516 .handler = &sysfsgpio_handle_jtag_gpionum_trst,
517 .mode = COMMAND_CONFIG,
518 .help = "gpio number for trst.",
519 .usage = "[trst]",
520 },
521 {
522 .name = "swd_nums",
523 .handler = &sysfsgpio_handle_swd_gpionums,
524 .mode = COMMAND_CONFIG,
525 .help = "gpio numbers for swclk, swdio. (in that order)",
526 .usage = "[swclk swdio]",
527 },
528 {
529 .name = "swclk_num",
530 .handler = &sysfsgpio_handle_swd_gpionum_swclk,
531 .mode = COMMAND_CONFIG,
532 .help = "gpio number for swclk.",
533 .usage = "[swclk]",
534 },
535 {
536 .name = "swdio_num",
537 .handler = &sysfsgpio_handle_swd_gpionum_swdio,
538 .mode = COMMAND_CONFIG,
539 .help = "gpio number for swdio.",
540 .usage = "[swdio]",
541 },
542 COMMAND_REGISTRATION_DONE
543 };
544
545 static const struct command_registration sysfsgpio_command_handlers[] = {
546 {
547 .name = "sysfsgpio",
548 .mode = COMMAND_ANY,
549 .help = "perform sysfsgpio management",
550 .chain = sysfsgpio_subcommand_handlers,
551 .usage = "",
552 },
553 COMMAND_REGISTRATION_DONE
554 };
555
556 static int sysfsgpio_init(void);
557 static int sysfsgpio_quit(void);
558
559 static const char * const sysfsgpio_transports[] = { "jtag", "swd", NULL };
560
561 static struct jtag_interface sysfsgpio_interface = {
562 .supported = DEBUG_CAP_TMS_SEQ,
563 .execute_queue = bitbang_execute_queue,
564 };
565
566 struct adapter_driver sysfsgpio_adapter_driver = {
567 .name = "sysfsgpio",
568 .transports = sysfsgpio_transports,
569 .commands = sysfsgpio_command_handlers,
570
571 .init = sysfsgpio_init,
572 .quit = sysfsgpio_quit,
573 .reset = sysfsgpio_reset,
574
575 .jtag_ops = &sysfsgpio_interface,
576 .swd_ops = &bitbang_swd,
577 };
578
579 static struct bitbang_interface sysfsgpio_bitbang = {
580 .read = sysfsgpio_read,
581 .write = sysfsgpio_write,
582 .swdio_read = sysfsgpio_swdio_read,
583 .swdio_drive = sysfsgpio_swdio_drive,
584 .swd_write = sysfsgpio_swd_write,
585 .blink = 0
586 };
587
588 /* helper func to close and cleanup files only if they were valid/ used */
589 static void cleanup_fd(int fd, int gpio)
590 {
591 if (gpio >= 0) {
592 if (fd >= 0)
593 close(fd);
594
595 unexport_sysfs_gpio(gpio);
596 }
597 }
598
599 static void cleanup_all_fds(void)
600 {
601 if (transport_is_jtag()) {
602 cleanup_fd(tck_fd, tck_gpio);
603 cleanup_fd(tms_fd, tms_gpio);
604 cleanup_fd(tdi_fd, tdi_gpio);
605 cleanup_fd(tdo_fd, tdo_gpio);
606 cleanup_fd(trst_fd, trst_gpio);
607 }
608 if (transport_is_swd()) {
609 cleanup_fd(swclk_fd, swclk_gpio);
610 cleanup_fd(swdio_fd, swdio_gpio);
611 }
612 cleanup_fd(srst_fd, srst_gpio);
613 }
614
615 static bool sysfsgpio_jtag_mode_possible(void)
616 {
617 if (!is_gpio_valid(tck_gpio))
618 return false;
619 if (!is_gpio_valid(tms_gpio))
620 return false;
621 if (!is_gpio_valid(tdi_gpio))
622 return false;
623 if (!is_gpio_valid(tdo_gpio))
624 return false;
625 return true;
626 }
627
628 static bool sysfsgpio_swd_mode_possible(void)
629 {
630 if (!is_gpio_valid(swclk_gpio))
631 return false;
632 if (!is_gpio_valid(swdio_gpio))
633 return false;
634 return true;
635 }
636
637 static int sysfsgpio_init(void)
638 {
639 bitbang_interface = &sysfsgpio_bitbang;
640
641 LOG_INFO("SysfsGPIO JTAG/SWD bitbang driver");
642
643 /*
644 * Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST
645 * as outputs. Drive TDI and TCK low, and TMS/TRST/SRST high.
646 * For SWD, SWCLK and SWDIO are configures as output high.
647 */
648
649 if (transport_is_jtag()) {
650 if (!sysfsgpio_jtag_mode_possible()) {
651 LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode");
652 return ERROR_JTAG_INIT_FAILED;
653 }
654
655 tck_fd = setup_sysfs_gpio(tck_gpio, 1, 0);
656 if (tck_fd < 0)
657 goto out_error;
658
659 tms_fd = setup_sysfs_gpio(tms_gpio, 1, 1);
660 if (tms_fd < 0)
661 goto out_error;
662
663 tdi_fd = setup_sysfs_gpio(tdi_gpio, 1, 0);
664 if (tdi_fd < 0)
665 goto out_error;
666
667 tdo_fd = setup_sysfs_gpio(tdo_gpio, 0, 0);
668 if (tdo_fd < 0)
669 goto out_error;
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
679 if (transport_is_swd()) {
680 if (!sysfsgpio_swd_mode_possible()) {
681 LOG_ERROR("Require swclk and swdio gpio for SWD mode");
682 return ERROR_JTAG_INIT_FAILED;
683 }
684
685 swclk_fd = setup_sysfs_gpio(swclk_gpio, 1, 0);
686 if (swclk_fd < 0)
687 goto out_error;
688
689 swdio_fd = setup_sysfs_gpio(swdio_gpio, 1, 0);
690 if (swdio_fd < 0)
691 goto out_error;
692 }
693
694 /* assume active low*/
695 if (srst_gpio >= 0) {
696 srst_fd = setup_sysfs_gpio(srst_gpio, 1, 1);
697 if (srst_fd < 0)
698 goto out_error;
699 }
700
701 return ERROR_OK;
702
703 out_error:
704 cleanup_all_fds();
705 return ERROR_JTAG_INIT_FAILED;
706 }
707
708 static int sysfsgpio_quit(void)
709 {
710 cleanup_all_fds();
711 return ERROR_OK;
712 }

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)