helper/log: remove dead code in log_forward()
[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 <jtag/interface.h>
56 #include "bitbang.h"
57
58 /*
59 * Helper func to determine if gpio number valid
60 *
61 * Assume here that there will be less than 10000 gpios on a system
62 */
63 static int is_gpio_valid(int gpio)
64 {
65 return gpio >= 0 && gpio < 10000;
66 }
67
68 /*
69 * Helper func to open, write to and close a file
70 * name and valstr must be null terminated.
71 *
72 * Returns negative on failure.
73 */
74 static int open_write_close(const char *name, const char *valstr)
75 {
76 int ret;
77 int fd = open(name, O_WRONLY);
78 if (fd < 0)
79 return fd;
80
81 ret = write(fd, valstr, strlen(valstr));
82 close(fd);
83
84 return ret;
85 }
86
87 /*
88 * Helper func to unexport gpio from sysfs
89 */
90 static void unexport_sysfs_gpio(int gpio)
91 {
92 char gpiostr[5];
93
94 if (!is_gpio_valid(gpio))
95 return;
96
97 snprintf(gpiostr, sizeof(gpiostr), "%d", gpio);
98 if (open_write_close("/sys/class/gpio/unexport", gpiostr) < 0)
99 LOG_ERROR("Couldn't unexport gpio %d", gpio);
100
101 return;
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 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 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);
135 ret = open_write_close(buf, is_output ? (init_high ? "high" : "low") : "in");
136 if (ret < 0) {
137 LOG_ERROR("Couldn't set direction for gpio %d", gpio);
138 perror("sysfsgpio: ");
139 unexport_sysfs_gpio(gpio);
140 return ERROR_FAIL;
141 }
142
143 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
144 ret = open(buf, O_RDWR | O_NONBLOCK | O_SYNC);
145 if (ret < 0) {
146 LOG_ERROR("Couldn't open value for gpio %d", gpio);
147 perror("sysfsgpio: ");
148 unexport_sysfs_gpio(gpio);
149 }
150
151 return ret;
152 }
153
154 /* gpio numbers for each gpio. Negative values are invalid */
155 static int tck_gpio = -1;
156 static int tms_gpio = -1;
157 static int tdi_gpio = -1;
158 static int tdo_gpio = -1;
159 static int trst_gpio = -1;
160 static int srst_gpio = -1;
161 static int swclk_gpio = -1;
162 static int swdio_gpio = -1;
163
164 /*
165 * file descriptors for /sys/class/gpio/gpioXX/value
166 * Set up during init.
167 */
168 static int tck_fd = -1;
169 static int tms_fd = -1;
170 static int tdi_fd = -1;
171 static int tdo_fd = -1;
172 static int trst_fd = -1;
173 static int srst_fd = -1;
174 static int swclk_fd = -1;
175 static int swdio_fd = -1;
176
177 static int last_swclk;
178 static int last_swdio;
179 static bool last_stored;
180 static bool swdio_input;
181
182 static void sysfsgpio_swdio_drive(bool is_output)
183 {
184 char buf[40];
185 int ret;
186
187 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", swdio_gpio);
188 ret = open_write_close(buf, is_output ? "high" : "in");
189 if (ret < 0) {
190 LOG_ERROR("Couldn't set direction for gpio %d", swdio_gpio);
191 perror("sysfsgpio: ");
192 }
193
194 last_stored = false;
195 swdio_input = !is_output;
196 }
197
198 static int sysfsgpio_swdio_read(void)
199 {
200 char buf[1];
201
202 /* important to seek to signal sysfs of new read */
203 lseek(swdio_fd, 0, SEEK_SET);
204 int ret = read(swdio_fd, &buf, sizeof(buf));
205
206 if (ret < 0) {
207 LOG_WARNING("reading swdio failed");
208 return 0;
209 }
210
211 return buf[0] != '0';
212 }
213
214 static void sysfsgpio_swdio_write(int swclk, int swdio)
215 {
216 const char one[] = "1";
217 const char zero[] = "0";
218
219 size_t bytes_written;
220
221 if (!swdio_input) {
222 if (!last_stored || (swdio != last_swdio)) {
223 bytes_written = write(swdio_fd, swdio ? &one : &zero, 1);
224 if (bytes_written != 1)
225 LOG_WARNING("writing swdio failed");
226 }
227 }
228
229 /* write swclk last */
230 if (!last_stored || (swclk != last_swclk)) {
231 bytes_written = write(swclk_fd, swclk ? &one : &zero, 1);
232 if (bytes_written != 1)
233 LOG_WARNING("writing swclk failed");
234 }
235
236 last_swdio = swdio;
237 last_swclk = swclk;
238 last_stored = true;
239 }
240
241 /*
242 * Bitbang interface read of TDO
243 *
244 * The sysfs value will read back either '0' or '1'. The trick here is to call
245 * lseek to bypass buffering in the sysfs kernel driver.
246 */
247 static bb_value_t sysfsgpio_read(void)
248 {
249 char buf[1];
250
251 /* important to seek to signal sysfs of new read */
252 lseek(tdo_fd, 0, SEEK_SET);
253 int ret = read(tdo_fd, &buf, sizeof(buf));
254
255 if (ret < 0) {
256 LOG_WARNING("reading tdo failed");
257 return 0;
258 }
259
260 return buf[0] == '0' ? BB_LOW : BB_HIGH;
261 }
262
263 /*
264 * Bitbang interface write of TCK, TMS, TDI
265 *
266 * Seeing as this is the only function where the outputs are changed,
267 * we can cache the old value to avoid needlessly writing it.
268 */
269 static int sysfsgpio_write(int tck, int tms, int tdi)
270 {
271 if (swd_mode) {
272 sysfsgpio_swdio_write(tck, tdi);
273 return ERROR_OK;
274 }
275
276 const char one[] = "1";
277 const char zero[] = "0";
278
279 static int last_tck;
280 static int last_tms;
281 static int last_tdi;
282
283 static int first_time;
284 size_t bytes_written;
285
286 if (!first_time) {
287 last_tck = !tck;
288 last_tms = !tms;
289 last_tdi = !tdi;
290 first_time = 1;
291 }
292
293 if (tdi != last_tdi) {
294 bytes_written = write(tdi_fd, tdi ? &one : &zero, 1);
295 if (bytes_written != 1)
296 LOG_WARNING("writing tdi failed");
297 }
298
299 if (tms != last_tms) {
300 bytes_written = write(tms_fd, tms ? &one : &zero, 1);
301 if (bytes_written != 1)
302 LOG_WARNING("writing tms failed");
303 }
304
305 /* write clk last */
306 if (tck != last_tck) {
307 bytes_written = write(tck_fd, tck ? &one : &zero, 1);
308 if (bytes_written != 1)
309 LOG_WARNING("writing tck failed");
310 }
311
312 last_tdi = tdi;
313 last_tms = tms;
314 last_tck = tck;
315
316 return ERROR_OK;
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 int 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 return ERROR_OK;
346 }
347
348 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionums)
349 {
350 if (CMD_ARGC == 4) {
351 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
352 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tms_gpio);
353 COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], tdi_gpio);
354 COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], tdo_gpio);
355 } else if (CMD_ARGC != 0) {
356 return ERROR_COMMAND_SYNTAX_ERROR;
357 }
358
359 command_print(CMD_CTX,
360 "SysfsGPIO nums: tck = %d, tms = %d, tdi = %d, tdo = %d",
361 tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
362
363 return ERROR_OK;
364 }
365
366 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tck)
367 {
368 if (CMD_ARGC == 1)
369 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
370
371 command_print(CMD_CTX, "SysfsGPIO num: tck = %d", tck_gpio);
372 return ERROR_OK;
373 }
374
375 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tms)
376 {
377 if (CMD_ARGC == 1)
378 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio);
379
380 command_print(CMD_CTX, "SysfsGPIO num: tms = %d", tms_gpio);
381 return ERROR_OK;
382 }
383
384 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tdo)
385 {
386 if (CMD_ARGC == 1)
387 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio);
388
389 command_print(CMD_CTX, "SysfsGPIO num: tdo = %d", tdo_gpio);
390 return ERROR_OK;
391 }
392
393 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tdi)
394 {
395 if (CMD_ARGC == 1)
396 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio);
397
398 command_print(CMD_CTX, "SysfsGPIO num: tdi = %d", tdi_gpio);
399 return ERROR_OK;
400 }
401
402 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_srst)
403 {
404 if (CMD_ARGC == 1)
405 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio);
406
407 command_print(CMD_CTX, "SysfsGPIO num: srst = %d", srst_gpio);
408 return ERROR_OK;
409 }
410
411 COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_trst)
412 {
413 if (CMD_ARGC == 1)
414 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio);
415
416 command_print(CMD_CTX, "SysfsGPIO num: trst = %d", trst_gpio);
417 return ERROR_OK;
418 }
419
420 COMMAND_HANDLER(sysfsgpio_handle_swd_gpionums)
421 {
422 if (CMD_ARGC == 2) {
423 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
424 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], swdio_gpio);
425 } else if (CMD_ARGC != 0) {
426 return ERROR_COMMAND_SYNTAX_ERROR;
427 }
428
429 command_print(CMD_CTX,
430 "SysfsGPIO nums: swclk = %d, swdio = %d",
431 swclk_gpio, swdio_gpio);
432
433 return ERROR_OK;
434 }
435
436 COMMAND_HANDLER(sysfsgpio_handle_swd_gpionum_swclk)
437 {
438 if (CMD_ARGC == 1)
439 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
440
441 command_print(CMD_CTX, "SysfsGPIO num: swclk = %d", swclk_gpio);
442 return ERROR_OK;
443 }
444
445 COMMAND_HANDLER(sysfsgpio_handle_swd_gpionum_swdio)
446 {
447 if (CMD_ARGC == 1)
448 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_gpio);
449
450 command_print(CMD_CTX, "SysfsGPIO num: swdio = %d", swdio_gpio);
451 return ERROR_OK;
452 }
453
454 static const struct command_registration sysfsgpio_command_handlers[] = {
455 {
456 .name = "sysfsgpio_jtag_nums",
457 .handler = &sysfsgpio_handle_jtag_gpionums,
458 .mode = COMMAND_CONFIG,
459 .help = "gpio numbers for tck, tms, tdi, tdo. (in that order)",
460 .usage = "(tck tms tdi tdo)* ",
461 },
462 {
463 .name = "sysfsgpio_tck_num",
464 .handler = &sysfsgpio_handle_jtag_gpionum_tck,
465 .mode = COMMAND_CONFIG,
466 .help = "gpio number for tck.",
467 },
468 {
469 .name = "sysfsgpio_tms_num",
470 .handler = &sysfsgpio_handle_jtag_gpionum_tms,
471 .mode = COMMAND_CONFIG,
472 .help = "gpio number for tms.",
473 },
474 {
475 .name = "sysfsgpio_tdo_num",
476 .handler = &sysfsgpio_handle_jtag_gpionum_tdo,
477 .mode = COMMAND_CONFIG,
478 .help = "gpio number for tdo.",
479 },
480 {
481 .name = "sysfsgpio_tdi_num",
482 .handler = &sysfsgpio_handle_jtag_gpionum_tdi,
483 .mode = COMMAND_CONFIG,
484 .help = "gpio number for tdi.",
485 },
486 {
487 .name = "sysfsgpio_srst_num",
488 .handler = &sysfsgpio_handle_jtag_gpionum_srst,
489 .mode = COMMAND_CONFIG,
490 .help = "gpio number for srst.",
491 },
492 {
493 .name = "sysfsgpio_trst_num",
494 .handler = &sysfsgpio_handle_jtag_gpionum_trst,
495 .mode = COMMAND_CONFIG,
496 .help = "gpio number for trst.",
497 },
498 {
499 .name = "sysfsgpio_swd_nums",
500 .handler = &sysfsgpio_handle_swd_gpionums,
501 .mode = COMMAND_CONFIG,
502 .help = "gpio numbers for swclk, swdio. (in that order)",
503 .usage = "(swclk swdio)* ",
504 },
505 {
506 .name = "sysfsgpio_swclk_num",
507 .handler = &sysfsgpio_handle_swd_gpionum_swclk,
508 .mode = COMMAND_CONFIG,
509 .help = "gpio number for swclk.",
510 },
511 {
512 .name = "sysfsgpio_swdio_num",
513 .handler = &sysfsgpio_handle_swd_gpionum_swdio,
514 .mode = COMMAND_CONFIG,
515 .help = "gpio number for swdio.",
516 },
517 COMMAND_REGISTRATION_DONE
518 };
519
520 static int sysfsgpio_init(void);
521 static int sysfsgpio_quit(void);
522
523 static const char * const sysfsgpio_transports[] = { "jtag", "swd", NULL };
524
525 struct jtag_interface sysfsgpio_interface = {
526 .name = "sysfsgpio",
527 .supported = DEBUG_CAP_TMS_SEQ,
528 .execute_queue = bitbang_execute_queue,
529 .transports = sysfsgpio_transports,
530 .swd = &bitbang_swd,
531 .commands = sysfsgpio_command_handlers,
532 .init = sysfsgpio_init,
533 .quit = sysfsgpio_quit,
534 };
535
536 static struct bitbang_interface sysfsgpio_bitbang = {
537 .read = sysfsgpio_read,
538 .write = sysfsgpio_write,
539 .reset = sysfsgpio_reset,
540 .swdio_read = sysfsgpio_swdio_read,
541 .swdio_drive = sysfsgpio_swdio_drive,
542 .blink = 0
543 };
544
545 /* helper func to close and cleanup files only if they were valid/ used */
546 static void cleanup_fd(int fd, int gpio)
547 {
548 if (gpio >= 0) {
549 if (fd >= 0)
550 close(fd);
551
552 unexport_sysfs_gpio(gpio);
553 }
554 }
555
556 static void cleanup_all_fds(void)
557 {
558 cleanup_fd(tck_fd, tck_gpio);
559 cleanup_fd(tms_fd, tms_gpio);
560 cleanup_fd(tdi_fd, tdi_gpio);
561 cleanup_fd(tdo_fd, tdo_gpio);
562 cleanup_fd(trst_fd, trst_gpio);
563 cleanup_fd(srst_fd, srst_gpio);
564 }
565
566 static bool sysfsgpio_jtag_mode_possible(void)
567 {
568 if (!is_gpio_valid(tck_gpio))
569 return 0;
570 if (!is_gpio_valid(tms_gpio))
571 return 0;
572 if (!is_gpio_valid(tdi_gpio))
573 return 0;
574 if (!is_gpio_valid(tdo_gpio))
575 return 0;
576 return 1;
577 }
578
579 static bool sysfsgpio_swd_mode_possible(void)
580 {
581 if (!is_gpio_valid(swclk_gpio))
582 return 0;
583 if (!is_gpio_valid(swdio_gpio))
584 return 0;
585 return 1;
586 }
587
588 static int sysfsgpio_init(void)
589 {
590 bitbang_interface = &sysfsgpio_bitbang;
591
592 LOG_INFO("SysfsGPIO JTAG/SWD bitbang driver");
593
594 if (sysfsgpio_jtag_mode_possible()) {
595 if (sysfsgpio_swd_mode_possible())
596 LOG_INFO("JTAG and SWD modes enabled");
597 else
598 LOG_INFO("JTAG only mode enabled (specify swclk and swdio gpio to add SWD mode)");
599 } else if (sysfsgpio_swd_mode_possible()) {
600 LOG_INFO("SWD only mode enabled (specify tck, tms, tdi and tdo gpios to add JTAG mode)");
601 } else {
602 LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode and/or swclk and swdio gpio for SWD mode");
603 return ERROR_JTAG_INIT_FAILED;
604 }
605
606
607 /*
608 * Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST
609 * as outputs. Drive TDI and TCK low, and TMS/TRST/SRST high.
610 * For SWD, SWCLK and SWDIO are configures as output high.
611 */
612 if (tck_gpio >= 0) {
613 tck_fd = setup_sysfs_gpio(tck_gpio, 1, 0);
614 if (tck_fd < 0)
615 goto out_error;
616 }
617
618 if (tms_gpio >= 0) {
619 tms_fd = setup_sysfs_gpio(tms_gpio, 1, 1);
620 if (tms_fd < 0)
621 goto out_error;
622 }
623
624 if (tdi_gpio >= 0) {
625 tdi_fd = setup_sysfs_gpio(tdi_gpio, 1, 0);
626 if (tdi_fd < 0)
627 goto out_error;
628 }
629
630 if (tdo_gpio >= 0) {
631 tdo_fd = setup_sysfs_gpio(tdo_gpio, 0, 0);
632 if (tdo_fd < 0)
633 goto out_error;
634 }
635
636 /* assume active low*/
637 if (trst_gpio >= 0) {
638 trst_fd = setup_sysfs_gpio(trst_gpio, 1, 1);
639 if (trst_fd < 0)
640 goto out_error;
641 }
642
643 /* assume active low*/
644 if (srst_gpio >= 0) {
645 srst_fd = setup_sysfs_gpio(srst_gpio, 1, 1);
646 if (srst_fd < 0)
647 goto out_error;
648 }
649
650 if (swclk_gpio >= 0) {
651 swclk_fd = setup_sysfs_gpio(swclk_gpio, 1, 0);
652 if (swclk_fd < 0)
653 goto out_error;
654 }
655
656 if (swdio_gpio >= 0) {
657 swdio_fd = setup_sysfs_gpio(swdio_gpio, 1, 0);
658 if (swdio_fd < 0)
659 goto out_error;
660 }
661
662 if (sysfsgpio_swd_mode_possible()) {
663 if (swd_mode)
664 bitbang_swd_switch_seq(JTAG_TO_SWD);
665 else
666 bitbang_swd_switch_seq(SWD_TO_JTAG);
667 }
668
669 return ERROR_OK;
670
671 out_error:
672 cleanup_all_fds();
673 return ERROR_JTAG_INIT_FAILED;
674 }
675
676 static int sysfsgpio_quit(void)
677 {
678 cleanup_all_fds();
679 return ERROR_OK;
680 }
681

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)