jtag: fix osbdm.c typo
[openocd.git] / src / jtag / drivers / osbdm.c
1 /***************************************************************************
2 * Copyright (C) 2012 by Jan Dakinevich *
3 * jan.dakinevich@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include <helper/log.h>
25 #include <helper/binarybuffer.h>
26 #include <helper/command.h>
27 #include <jtag/interface.h>
28 #include "libusb_common.h"
29
30 struct sequence {
31 int len;
32 void *tms;
33 void *tdo;
34 const void *tdi;
35 struct sequence *next;
36 };
37
38 struct queue {
39 struct sequence *head;
40 struct sequence *tail;
41 };
42
43 static struct sequence *queue_add_tail(struct queue *queue, int len)
44 {
45 if (len <= 0) {
46 LOG_ERROR("BUG: sequences with zero length are not allowed");
47 return NULL;
48 }
49
50 struct sequence *next;
51 next = (struct sequence *)malloc(sizeof(*next));
52 if (next) {
53 next->tms = calloc(1, DIV_ROUND_UP(len, 8));
54 if (next->tms) {
55 next->len = len;
56 next->tdo = NULL;
57 next->tdi = NULL;
58 next->next = NULL;
59
60 if (!queue->head) {
61 /* Queue is empty at the moment */
62 queue->head = next;
63 } else {
64 /* Queue already contains at least one sequence */
65 queue->tail->next = next;
66 }
67
68 queue->tail = next;
69 } else {
70 free(next);
71 next = NULL;
72 }
73 }
74
75 if (!next)
76 LOG_ERROR("Not enough memory");
77
78 return next;
79 }
80
81 static void queue_drop_head(struct queue *queue)
82 {
83 struct sequence *head = queue->head->next; /* New head */
84 free(queue->head->tms);
85 free(queue->head);
86 queue->head = head;
87 }
88
89 static void queue_free(struct queue *queue)
90 {
91 if (queue) {
92 while (queue->head)
93 queue_drop_head(queue);
94
95 free(queue);
96 }
97 }
98
99 static struct queue *queue_alloc(void)
100 {
101 struct queue *queue = (struct queue *)malloc(sizeof(struct queue));
102 if (queue)
103 queue->head = NULL;
104 else
105 LOG_ERROR("Not enough memory");
106
107 return queue;
108 }
109
110 /* Size of usb communnication buffer */
111 #define OSBDM_USB_BUFSIZE 64
112 /* Timeout for USB transfer, ms */
113 #define OSBDM_USB_TIMEOUT 1000
114 /* Write end point */
115 #define OSBDM_USB_EP_WRITE 0x01
116 /* Read end point */
117 #define OSBDM_USB_EP_READ 0x82
118
119 /* Initialize OSBDM device */
120 #define OSBDM_CMD_INIT 0x11
121 /* Execute special, not-BDM command. But only this
122 * command is used for JTAG operation */
123 #define OSBDM_CMD_SPECIAL 0x27
124 /* Execute JTAG swap (tms/tdi -> tdo) */
125 #define OSBDM_CMD_SPECIAL_SWAP 0x05
126 /* Reset control */
127 #define OSBDM_CMD_SPECIAL_SRST 0x01
128 /* Maximum bit-length in one swap */
129 #define OSBDM_SWAP_MAX (((OSBDM_USB_BUFSIZE - 6) / 5) * 16)
130
131 /* Lists of valid VID/PID pairs
132 */
133 static const uint16_t osbdm_vid[] = { 0x15a2, 0x15a2, 0 };
134 static const uint16_t osbdm_pid[] = { 0x0042, 0x0058, 0 };
135
136 struct osbdm {
137 struct jtag_libusb_device_handle *devh; /* USB handle */
138 uint8_t buffer[OSBDM_USB_BUFSIZE]; /* Data to send and receive */
139 int count; /* Count data to send and to read */
140 };
141
142 /* osbdm instance
143 */
144 static struct osbdm osbdm_context;
145
146 static int osbdm_send_and_recv(struct osbdm *osbdm)
147 {
148 /* Send request */
149 int count = jtag_libusb_bulk_write(osbdm->devh, OSBDM_USB_EP_WRITE,
150 (char *)osbdm->buffer, osbdm->count, OSBDM_USB_TIMEOUT);
151
152 if (count != osbdm->count) {
153 LOG_ERROR("OSBDM communnication error: can't write");
154 return ERROR_FAIL;
155 }
156
157 /* Save command code for next checking */
158 uint8_t cmd_saved = osbdm->buffer[0];
159
160 /* Reading answer */
161 osbdm->count = jtag_libusb_bulk_read(osbdm->devh, OSBDM_USB_EP_READ,
162 (char *)osbdm->buffer, OSBDM_USB_BUFSIZE, OSBDM_USB_TIMEOUT);
163
164 /* Now perform basic checks for data sent by BDM device
165 */
166
167 if (osbdm->count < 0) {
168 LOG_ERROR("OSBDM communnication error: can't read");
169 return ERROR_FAIL;
170 }
171
172 if (osbdm->count < 2) {
173 LOG_ERROR("OSBDM communnication error: answer too small");
174 return ERROR_FAIL;
175 }
176
177 if (osbdm->count != osbdm->buffer[1]) {
178 LOG_ERROR("OSBDM communnication error: answer size mismatch");
179 return ERROR_FAIL;
180 }
181
182 if (cmd_saved != osbdm->buffer[0]) {
183 LOG_ERROR("OSBDM communnication error: answer command mismatch");
184 return ERROR_FAIL;
185 }
186
187 return ERROR_OK;
188 }
189
190 static int osbdm_srst(struct osbdm *osbdm, int srst)
191 {
192 osbdm->count = 0;
193 (void)memset(osbdm->buffer, 0, OSBDM_USB_BUFSIZE);
194
195 /* Composing request
196 */
197 osbdm->buffer[osbdm->count++] = OSBDM_CMD_SPECIAL; /* Command */
198 osbdm->buffer[osbdm->count++] = OSBDM_CMD_SPECIAL_SRST; /* Subcommand */
199 /* Length in bytes - not used */
200 osbdm->buffer[osbdm->count++] = 0;
201 osbdm->buffer[osbdm->count++] = 0;
202 /* SRST state */
203 osbdm->buffer[osbdm->count++] = (srst ? 0 : 0x08);
204
205 /* Sending data
206 */
207 if (osbdm_send_and_recv(osbdm) != ERROR_OK)
208 return ERROR_FAIL;
209
210 return ERROR_OK;
211 }
212
213 static int osbdm_swap(struct osbdm *osbdm, void *tms, void *tdi,
214 void *tdo, int length)
215 {
216 if (length > OSBDM_SWAP_MAX) {
217 LOG_ERROR("BUG: bit sequence too long");
218 return ERROR_FAIL;
219 }
220
221 if (length <= 0) {
222 LOG_ERROR("BUG: bit sequence equal or less to 0");
223 return ERROR_FAIL;
224 }
225
226 int swap_count = DIV_ROUND_UP(length, 16);
227
228 /* cleanup */
229 osbdm->count = 0;
230 (void)memset(osbdm->buffer, 0, OSBDM_USB_BUFSIZE);
231
232 /* Composing request
233 */
234
235 osbdm->buffer[osbdm->count++] = OSBDM_CMD_SPECIAL; /* Command */
236 osbdm->buffer[osbdm->count++] = OSBDM_CMD_SPECIAL_SWAP; /* Subcommand */
237 /* Length in bytes - not used */
238 osbdm->buffer[osbdm->count++] = 0;
239 osbdm->buffer[osbdm->count++] = 0;
240 /* Swap count */
241 osbdm->buffer[osbdm->count++] = 0;
242 osbdm->buffer[osbdm->count++] = (uint8_t)swap_count;
243
244 for (int bit_idx = 0; bit_idx < length; ) {
245 /* Bit count in swap */
246 int bit_count = length - bit_idx;
247 if (bit_count > 16)
248 bit_count = 16;
249
250 osbdm->buffer[osbdm->count++] = (uint8_t)bit_count;
251
252 /* Copying TMS and TDI data to output buffer */
253 uint32_t tms_data = buf_get_u32(tms, bit_idx, bit_count);
254 uint32_t tdi_data = buf_get_u32(tdi, bit_idx, bit_count);
255 osbdm->buffer[osbdm->count++] = (uint8_t)(tdi_data >> 8);
256 osbdm->buffer[osbdm->count++] = (uint8_t)tdi_data;
257 osbdm->buffer[osbdm->count++] = (uint8_t)(tms_data >> 8);
258 osbdm->buffer[osbdm->count++] = (uint8_t)tms_data;
259
260 /* Next bit offset */
261 bit_idx += bit_count;
262 }
263
264 assert(osbdm->count <= OSBDM_USB_BUFSIZE);
265
266 /* Sending data
267 */
268 if (osbdm_send_and_recv(osbdm) != ERROR_OK)
269 return ERROR_FAIL;
270
271 /* Extra check
272 */
273 if (((osbdm->buffer[2] << 8) | osbdm->buffer[3]) != 2 * swap_count) {
274 LOG_ERROR("OSBDM communnication error: not proper answer to swap command");
275 return ERROR_FAIL;
276 }
277
278 /* Copy TDO responce
279 */
280 uint8_t *buffer = (uint8_t *)osbdm->buffer + 4;
281 for (int bit_idx = 0; bit_idx < length; ) {
282 int bit_count = length - bit_idx;
283 if (bit_count > 16)
284 bit_count = 16;
285
286 /* Prepare data */
287 uint32_t tdo_data = 0;
288 tdo_data |= (*buffer++) << 8;
289 tdo_data |= (*buffer++);
290 tdo_data >>= (16 - bit_count);
291
292 /* Copy TDO to return */
293 buf_set_u32(tdo, bit_idx, bit_count, tdo_data);
294
295 bit_idx += bit_count;
296 }
297
298 return ERROR_OK;
299 }
300
301 static int osbdm_flush(struct osbdm *osbdm, struct queue* queue)
302 {
303 uint8_t tms[DIV_ROUND_UP(OSBDM_SWAP_MAX, 8)];
304 uint8_t tdi[DIV_ROUND_UP(OSBDM_SWAP_MAX, 8)];
305 uint8_t tdo[DIV_ROUND_UP(OSBDM_SWAP_MAX, 8)];
306
307 int seq_back_len = 0;
308
309 while (queue->head) {
310 (void)memset(tms, 0, sizeof(tms));
311 (void)memset(tdi, 0, sizeof(tdi));
312 (void)memset(tdo, 0, sizeof(tdo));
313
314 int seq_len;
315 int swap_len;
316 struct sequence *seq;
317
318 /* Copy from queue to tms/tdi streams
319 */
320 seq = queue->head;
321 seq_len = seq_back_len;
322 swap_len = 0;
323
324 while (seq && swap_len != OSBDM_SWAP_MAX) {
325 /* Count bit for copy at this iteration.
326 * len should fit into remaining space
327 * in tms/tdo bitstreams
328 */
329 int len = seq->len - seq_len;
330 if (len > OSBDM_SWAP_MAX - swap_len)
331 len = OSBDM_SWAP_MAX - swap_len;
332
333 /* Set tms data */
334 buf_set_buf(seq->tms, seq_len, tms, swap_len, len);
335
336 /* Set tdi data if they exists */
337 if (seq->tdi)
338 buf_set_buf(seq->tdi, seq_len, tdi, swap_len, len);
339
340 swap_len += len;
341 seq_len += len;
342 if (seq_len == seq->len) {
343 seq = seq->next; /* Move to next sequence */
344 seq_len = 0;
345 }
346 }
347
348 if (osbdm_swap(osbdm, tms, tdi, tdo, swap_len))
349 return ERROR_FAIL;
350
351 /* Copy from tdo stream to queue
352 */
353
354 for (int swap_back_len = 0; swap_back_len < swap_len; ) {
355 int len = queue->head->len - seq_back_len;
356 if (len > swap_len - swap_back_len)
357 len = swap_len - swap_back_len;
358
359 if (queue->head->tdo)
360 buf_set_buf(tdo, swap_back_len, queue->head->tdo, seq_back_len, len);
361
362 swap_back_len += len;
363 seq_back_len += len;
364 if (seq_back_len == queue->head->len) {
365 queue_drop_head(queue);
366 seq_back_len = 0;
367 }
368 }
369 }
370
371 return ERROR_OK;
372 }
373
374 /* Basic operation for opening USB device */
375 static int osbdm_open(struct osbdm *osbdm)
376 {
377 (void)memset(osbdm, 0, sizeof(*osbdm));
378 if (jtag_libusb_open(osbdm_vid, osbdm_pid, &osbdm->devh) != ERROR_OK)
379 return ERROR_FAIL;
380
381 if (jtag_libusb_claim_interface(osbdm->devh, 0) != ERROR_OK)
382 return ERROR_FAIL;
383
384 return ERROR_OK;
385 }
386
387 static int osbdm_quit(void)
388 {
389 jtag_libusb_close(osbdm_context.devh);
390 return ERROR_OK;
391 }
392
393 static int osbdm_add_pathmove(
394 struct queue *queue,
395 tap_state_t *path,
396 int num_states)
397 {
398 assert(num_states <= 32);
399
400 struct sequence *next = queue_add_tail(queue, num_states);
401 if (!next) {
402 LOG_ERROR("BUG: can't allocate bit sequence");
403 return ERROR_FAIL;
404 }
405
406 uint32_t tms = 0;
407 for (int i = 0; i < num_states; i++) {
408 if (tap_state_transition(tap_get_state(), 1) == path[i]) {
409 tms |= (1 << i);
410 } else if (tap_state_transition(tap_get_state(), 0) == path[i]) {
411 tms &= ~(1 << i); /* This line not so needed */
412 } else {
413 LOG_ERROR("BUG: %s -> %s isn't a valid TAP state transition",
414 tap_state_name(tap_get_state()),
415 tap_state_name(path[i]));
416 return ERROR_FAIL;
417 }
418
419 tap_set_state(path[i]);
420 }
421
422 buf_set_u32(next->tms, 0, num_states, tms);
423 tap_set_end_state(tap_get_state());
424
425 return ERROR_OK;
426 }
427
428 static int osbdm_add_statemove(
429 struct queue *queue,
430 tap_state_t new_state,
431 int skip_first)
432 {
433 int len = 0;
434 int tms;
435
436 tap_set_end_state(new_state);
437 if (tap_get_end_state() == TAP_RESET) {
438 /* Ignore current state */
439 tms = 0xff;
440 len = 5;
441 } else if (tap_get_state() != tap_get_end_state()) {
442 tms = tap_get_tms_path(tap_get_state(), new_state);
443 len = tap_get_tms_path_len(tap_get_state(), new_state);
444 }
445
446 if (len && skip_first) {
447 len--;
448 tms >>= 1;
449 }
450
451 if (len) {
452 struct sequence *next = queue_add_tail(queue, len);
453 if (!next) {
454 LOG_ERROR("BUG: can't allocate bit sequence");
455 return ERROR_FAIL;
456 }
457 buf_set_u32(next->tms, 0, len, tms);
458 }
459
460 tap_set_state(tap_get_end_state());
461 return ERROR_OK;
462 }
463
464 static int osbdm_add_stableclocks(
465 struct queue *queue,
466 int count)
467 {
468 if (!tap_is_state_stable(tap_get_state())) {
469 LOG_ERROR("BUG: current state (%s) is not stable",
470 tap_state_name(tap_get_state()));
471 return ERROR_FAIL;
472 }
473
474 struct sequence *next = queue_add_tail(queue, count);
475 if (!next) {
476 LOG_ERROR("BUG: can't allocate bit sequence");
477 return ERROR_FAIL;
478 }
479
480 if (tap_get_state() == TAP_RESET)
481 (void)memset(next->tms, 0xff, DIV_ROUND_UP(count, 8));
482
483 return ERROR_OK;
484 }
485
486 static int osbdm_add_tms(
487 struct queue *queue,
488 const uint8_t *tms,
489 int num_bits)
490 {
491 struct sequence *next = queue_add_tail(queue, num_bits);
492 if (!next) {
493 LOG_ERROR("BUG: can't allocate bit sequence");
494 return ERROR_FAIL;
495 }
496 buf_set_buf(tms, 0, next->tms, 0, num_bits);
497
498 return ERROR_OK;
499 }
500
501 static int osbdm_add_scan(
502 struct queue *queue,
503 struct scan_field *fields,
504 int num_fields,
505 tap_state_t end_state,
506 bool ir_scan)
507 {
508 /* Move to desired shift state */
509 if (ir_scan) {
510 if (tap_get_state() != TAP_IRSHIFT) {
511 if (osbdm_add_statemove(queue, TAP_IRSHIFT, 0) != ERROR_OK)
512 return ERROR_FAIL;
513 }
514 } else {
515 if (tap_get_state() != TAP_DRSHIFT) {
516 if (osbdm_add_statemove(queue, TAP_DRSHIFT, 0) != ERROR_OK)
517 return ERROR_FAIL;
518 }
519 }
520
521 /* Add scan */
522 tap_set_end_state(end_state);
523 for (int idx = 0; idx < num_fields; idx++) {
524 struct sequence *next = queue_add_tail(queue, fields[idx].num_bits);
525 if (!next) {
526 LOG_ERROR("Can't allocate bit sequence");
527 return ERROR_FAIL;
528 }
529
530 (void)memset(next->tms, 0, DIV_ROUND_UP(fields[idx].num_bits, 8));
531 next->tdi = fields[idx].out_value;
532 next->tdo = fields[idx].in_value;
533 }
534
535 /* Move to end state
536 */
537 if (tap_get_state() != tap_get_end_state()) {
538 /* Exit from IRSHIFT/DRSHIFT */
539 buf_set_u32(queue->tail->tms, queue->tail->len - 1, 1, 1);
540
541 /* Move with skip_first flag */
542 if (osbdm_add_statemove(queue, tap_get_end_state(), 1) != ERROR_OK)
543 return ERROR_FAIL;
544 }
545
546 return ERROR_OK;
547 }
548
549 static int osbdm_add_runtest(
550 struct queue *queue,
551 int num_cycles,
552 tap_state_t end_state)
553 {
554 if (osbdm_add_statemove(queue, TAP_IDLE, 0) != ERROR_OK)
555 return ERROR_FAIL;
556
557 if (osbdm_add_stableclocks(queue, num_cycles) != ERROR_OK)
558 return ERROR_FAIL;
559
560 if (osbdm_add_statemove(queue, end_state, 0) != ERROR_OK)
561 return ERROR_FAIL;
562
563 return ERROR_OK;
564 }
565
566 static int osbdm_execute_command(
567 struct osbdm *osbdm,
568 struct queue *queue,
569 struct jtag_command *cmd)
570 {
571 int retval = ERROR_OK;
572
573 switch (cmd->type) {
574 case JTAG_RESET:
575 if (cmd->cmd.reset->trst) {
576 LOG_ERROR("BUG: nTRST signal is not supported");
577 retval = ERROR_FAIL;
578 } else {
579 retval = osbdm_flush(osbdm, queue);
580 if (retval == ERROR_OK)
581 retval = osbdm_srst(osbdm, cmd->cmd.reset->srst);
582 }
583 break;
584
585 case JTAG_PATHMOVE:
586 retval = osbdm_add_pathmove(
587 queue,
588 cmd->cmd.pathmove->path,
589 cmd->cmd.pathmove->num_states);
590 break;
591
592 case JTAG_TLR_RESET:
593 retval = osbdm_add_statemove(
594 queue,
595 cmd->cmd.statemove->end_state,
596 0);
597 break;
598
599 case JTAG_STABLECLOCKS:
600 retval = osbdm_add_stableclocks(
601 queue,
602 cmd->cmd.stableclocks->num_cycles);
603 break;
604
605 case JTAG_TMS:
606 retval = osbdm_add_tms(
607 queue,
608 cmd->cmd.tms->bits,
609 cmd->cmd.tms->num_bits);
610 break;
611
612 case JTAG_SCAN:
613 retval = osbdm_add_scan(
614 queue,
615 cmd->cmd.scan->fields,
616 cmd->cmd.scan->num_fields,
617 cmd->cmd.scan->end_state,
618 cmd->cmd.scan->ir_scan);
619 break;
620
621 case JTAG_SLEEP:
622 retval = osbdm_flush(osbdm, queue);
623 if (retval == ERROR_OK)
624 jtag_sleep(cmd->cmd.sleep->us);
625 break;
626
627 case JTAG_RUNTEST:
628 retval = osbdm_add_runtest(
629 queue,
630 cmd->cmd.runtest->num_cycles,
631 cmd->cmd.runtest->end_state);
632 break;
633
634 default:
635 LOG_ERROR("BUG: unknown JTAG command type encountered");
636 retval = ERROR_FAIL;
637 break;
638 }
639
640 return retval;
641 }
642
643 static int osbdm_execute_queue(void)
644 {
645 int retval = ERROR_OK;
646
647 struct queue *queue = queue_alloc();
648 if (!queue) {
649 LOG_ERROR("BUG: can't allocate bit queue");
650 retval = ERROR_FAIL;
651 } else {
652 struct jtag_command *cmd = jtag_command_queue;
653
654 while (retval == ERROR_OK && cmd) {
655 retval = osbdm_execute_command(&osbdm_context, queue, cmd);
656 cmd = cmd->next;
657 }
658
659 if (retval == ERROR_OK)
660 retval = osbdm_flush(&osbdm_context, queue);
661
662 queue_free(queue);
663 }
664
665 if (retval != ERROR_OK) {
666 LOG_ERROR("FATAL: can't execute jtag command");
667 exit(-1);
668 }
669
670 return retval;
671 }
672
673 static int osbdm_init(void)
674 {
675 /* Open device */
676 if (osbdm_open(&osbdm_context) != ERROR_OK) {
677 LOG_ERROR("Can't open OSBDM device");
678 return ERROR_FAIL;
679 } else {
680 /* Device successfully opened */
681 LOG_INFO("OSBDM has opened");
682 }
683
684 /* Perform initialize command */
685 osbdm_context.count = 0;
686 osbdm_context.buffer[osbdm_context.count++] = OSBDM_CMD_INIT;
687 if (osbdm_send_and_recv(&osbdm_context) != ERROR_OK)
688 return ERROR_FAIL;
689
690 return ERROR_OK;
691 }
692
693 static int osbdm_khz(int khz, int *speed)
694 {
695 *speed = khz;
696 return ERROR_OK;
697 }
698
699 static int osbdm_speed(int speed)
700 {
701 return ERROR_OK;
702 }
703
704 static int osbdm_speed_div(int speed, int *khz)
705 {
706 *khz = speed;
707 return ERROR_OK;
708 }
709
710 struct jtag_interface osbdm_interface = {
711 .name = "osbdm",
712
713 .transports = jtag_only,
714 .execute_queue = osbdm_execute_queue,
715
716 .khz = osbdm_khz,
717 .speed = osbdm_speed,
718 .speed_div = osbdm_speed_div,
719
720 .init = osbdm_init,
721 .quit = osbdm_quit
722 };

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)