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

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)