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

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)