c9b8435f2c9297e404be405a5f913d95676d3def
[openocd.git] / src / jtag / drivers / ti_icdi_usb.c
1 /***************************************************************************
2 * *
3 * Copyright (C) 2012 by Spencer Oliver *
4 * spen@spen-soft.co.uk *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
20 ***************************************************************************/
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 /* project specific includes */
27 #include <helper/binarybuffer.h>
28 #include <jtag/interface.h>
29 #include <jtag/hla/hla_layout.h>
30 #include <jtag/hla/hla_transport.h>
31 #include <jtag/hla/hla_interface.h>
32 #include <target/target.h>
33
34 #include <target/cortex_m.h>
35
36 #include <libusb.h>
37
38 #define ICDI_WRITE_ENDPOINT 0x02
39 #define ICDI_READ_ENDPOINT 0x83
40
41 #define ICDI_WRITE_TIMEOUT 1000
42 #define ICDI_READ_TIMEOUT 1000
43 #define ICDI_PACKET_SIZE 2048
44
45 #define PACKET_START "$"
46 #define PACKET_END "#"
47
48 struct icdi_usb_handle_s {
49 libusb_context *usb_ctx;
50 libusb_device_handle *usb_dev;
51
52 char *read_buffer;
53 char *write_buffer;
54 int max_packet;
55 int read_count;
56 uint32_t max_rw_packet; /* max X packet (read/write memory) transfers */
57 };
58
59 static int icdi_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
60 uint32_t count, uint8_t *buffer);
61 static int icdi_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
62 uint32_t count, const uint8_t *buffer);
63
64 static int remote_escape_output(const char *buffer, int len, char *out_buf, int *out_len, int out_maxlen)
65 {
66 int input_index, output_index;
67
68 output_index = 0;
69
70 for (input_index = 0; input_index < len; input_index++) {
71
72 char b = buffer[input_index];
73
74 if (b == '$' || b == '#' || b == '}' || b == '*') {
75 /* These must be escaped. */
76 if (output_index + 2 > out_maxlen)
77 break;
78 out_buf[output_index++] = '}';
79 out_buf[output_index++] = b ^ 0x20;
80 } else {
81 if (output_index + 1 > out_maxlen)
82 break;
83 out_buf[output_index++] = b;
84 }
85 }
86
87 *out_len = input_index;
88 return output_index;
89 }
90
91 static int remote_unescape_input(const char *buffer, int len, char *out_buf, int out_maxlen)
92 {
93 int input_index, output_index;
94 int escaped;
95
96 output_index = 0;
97 escaped = 0;
98
99 for (input_index = 0; input_index < len; input_index++) {
100
101 char b = buffer[input_index];
102
103 if (output_index + 1 > out_maxlen)
104 LOG_ERROR("Received too much data from the target.");
105
106 if (escaped) {
107 out_buf[output_index++] = b ^ 0x20;
108 escaped = 0;
109 } else if (b == '}')
110 escaped = 1;
111 else
112 out_buf[output_index++] = b;
113 }
114
115 if (escaped)
116 LOG_ERROR("Unmatched escape character in target response.");
117
118 return output_index;
119 }
120
121 static int icdi_send_packet(void *handle, int len)
122 {
123 unsigned char cksum = 0;
124 struct icdi_usb_handle_s *h;
125 int result, retry = 0;
126 int transferred = 0;
127
128 assert(handle != NULL);
129 h = (struct icdi_usb_handle_s *)handle;
130
131 /* check we have a large enough buffer for checksum "#00" */
132 if (len + 3 > h->max_packet) {
133 LOG_ERROR("packet buffer too small");
134 return ERROR_FAIL;
135 }
136
137 /* calculate checksum - offset start of packet */
138 for (int i = 1; i < len; i++)
139 cksum += h->write_buffer[i];
140
141 len += sprintf(&h->write_buffer[len], PACKET_END "%02x", cksum);
142
143 #ifdef _DEBUG_USB_COMMS_
144 char buffer[50];
145 char ch = h->write_buffer[1];
146 if (ch == 'x' || ch == 'X')
147 LOG_DEBUG("writing packet: <binary>");
148 else {
149 memcpy(buffer, h->write_buffer, len >= 50 ? 50-1 : len);
150 buffer[len] = 0;
151 LOG_DEBUG("writing packet: %s", buffer);
152 }
153 #endif
154
155 while (1) {
156
157 result = libusb_bulk_transfer(h->usb_dev, ICDI_WRITE_ENDPOINT, (unsigned char *)h->write_buffer, len,
158 &transferred, ICDI_WRITE_TIMEOUT);
159 if (result != 0 || transferred != len) {
160 LOG_DEBUG("Error TX Data %d", result);
161 return ERROR_FAIL;
162 }
163
164 /* check that the client got the message ok, or shall we resend */
165 result = libusb_bulk_transfer(h->usb_dev, ICDI_READ_ENDPOINT, (unsigned char *)h->read_buffer, h->max_packet,
166 &transferred, ICDI_READ_TIMEOUT);
167 if (result != 0 || transferred < 1) {
168 LOG_DEBUG("Error RX Data %d", result);
169 return ERROR_FAIL;
170 }
171
172 #ifdef _DEBUG_USB_COMMS_
173 LOG_DEBUG("received reply: '%c' : count %d", h->read_buffer[0], transferred);
174 #endif
175
176 if (h->read_buffer[0] == '-') {
177 LOG_DEBUG("Resending packet %d", ++retry);
178 } else {
179 if (h->read_buffer[0] != '+')
180 LOG_DEBUG("Unexpected Reply from ICDI: %c", h->read_buffer[0]);
181 break;
182 }
183
184 if (retry == 3) {
185 LOG_DEBUG("maximum nack retries attempted");
186 return ERROR_FAIL;
187 }
188 }
189
190 retry = 0;
191 h->read_count = transferred;
192
193 while (1) {
194
195 /* read reply from icdi */
196 result = libusb_bulk_transfer(h->usb_dev, ICDI_READ_ENDPOINT, (unsigned char *)h->read_buffer + h->read_count,
197 h->max_packet - h->read_count, &transferred, ICDI_READ_TIMEOUT);
198
199 #ifdef _DEBUG_USB_COMMS_
200 LOG_DEBUG("received data: count %d", transferred);
201 #endif
202
203 /* check for errors but retry for timeout */
204 if (result != 0) {
205
206 if (result == LIBUSB_ERROR_TIMEOUT) {
207 LOG_DEBUG("Error RX timeout %d", result);
208 } else {
209 LOG_DEBUG("Error RX Data %d", result);
210 return ERROR_FAIL;
211 }
212 }
213
214 h->read_count += transferred;
215
216 /* we need to make sure we have a full packet, including checksum */
217 if (h->read_count > 5) {
218
219 /* check that we have received an packet delimiter
220 * we do not validate the checksum
221 * reply should contain $...#AA - so we check for # */
222 if (h->read_buffer[h->read_count - 3] == '#')
223 return ERROR_OK;
224 }
225
226 if (retry++ == 3) {
227 LOG_DEBUG("maximum data retries attempted");
228 break;
229 }
230 }
231
232 return ERROR_FAIL;
233 }
234
235 static int icdi_send_cmd(void *handle, const char *cmd)
236 {
237 struct icdi_usb_handle_s *h;
238 h = (struct icdi_usb_handle_s *)handle;
239
240 int cmd_len = snprintf(h->write_buffer, h->max_packet, PACKET_START "%s", cmd);
241 return icdi_send_packet(handle, cmd_len);
242 }
243
244 static int icdi_send_remote_cmd(void *handle, const char *data)
245 {
246 struct icdi_usb_handle_s *h;
247 h = (struct icdi_usb_handle_s *)handle;
248
249 size_t cmd_len = sprintf(h->write_buffer, PACKET_START "qRcmd,");
250 cmd_len += hexify(h->write_buffer + cmd_len, data, 0, h->max_packet - cmd_len);
251
252 return icdi_send_packet(handle, cmd_len);
253 }
254
255 static int icdi_get_cmd_result(void *handle)
256 {
257 struct icdi_usb_handle_s *h;
258 int offset = 0;
259 char ch;
260
261 assert(handle != NULL);
262 h = (struct icdi_usb_handle_s *)handle;
263
264 do {
265 ch = h->read_buffer[offset++];
266 if (offset > h->read_count)
267 return ERROR_FAIL;
268 } while (ch != '$');
269
270 if (memcmp("OK", h->read_buffer + offset, 2) == 0)
271 return ERROR_OK;
272
273 if (h->read_buffer[offset] == 'E') {
274 /* get error code */
275 char result;
276 if (unhexify(&result, h->read_buffer + offset + 1, 1) != 1)
277 return ERROR_FAIL;
278 return result;
279 }
280
281 /* for now we assume everything else is ok */
282 return ERROR_OK;
283 }
284
285 static int icdi_usb_idcode(void *handle, uint32_t *idcode)
286 {
287 *idcode = 0;
288 return ERROR_OK;
289 }
290
291 static int icdi_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val)
292 {
293 return icdi_usb_write_mem(handle, addr, 4, 1, (uint8_t *)&val);
294 }
295
296 static enum target_state icdi_usb_state(void *handle)
297 {
298 int result;
299 struct icdi_usb_handle_s *h;
300 uint32_t dhcsr;
301
302 h = (struct icdi_usb_handle_s *)handle;
303
304 result = icdi_usb_read_mem(h, DCB_DHCSR, 4, 1, (uint8_t *)&dhcsr);
305 if (result != ERROR_OK)
306 return TARGET_UNKNOWN;
307
308 if (dhcsr & S_HALT)
309 return TARGET_HALTED;
310
311 return TARGET_RUNNING;
312 }
313
314 static int icdi_usb_version(void *handle)
315 {
316 struct icdi_usb_handle_s *h;
317 h = (struct icdi_usb_handle_s *)handle;
318
319 char version[20];
320
321 /* get info about icdi */
322 int result = icdi_send_remote_cmd(handle, "version");
323 if (result != ERROR_OK)
324 return result;
325
326 if (h->read_count < 8) {
327 LOG_ERROR("Invalid Reply Received");
328 return ERROR_FAIL;
329 }
330
331 /* convert reply */
332 if (unhexify(version, h->read_buffer + 2, 4) != 4) {
333 LOG_WARNING("unable to get ICDI version");
334 return ERROR_OK;
335 }
336
337 /* null terminate and print info */
338 version[4] = 0;
339
340 LOG_INFO("ICDI Firmware version: %s", version);
341
342 return ERROR_OK;
343 }
344
345 static int icdi_usb_query(void *handle)
346 {
347 int result;
348
349 struct icdi_usb_handle_s *h;
350 h = (struct icdi_usb_handle_s *)handle;
351
352 result = icdi_send_cmd(handle, "qSupported");
353 if (result != ERROR_OK)
354 return result;
355
356 /* check result */
357 result = icdi_get_cmd_result(handle);
358 if (result != ERROR_OK) {
359 LOG_ERROR("query supported failed: 0x%x", result);
360 return ERROR_FAIL;
361 }
362
363 /* from this we can get the max packet supported */
364
365 /* query packet buffer size */
366 char *offset = strstr(h->read_buffer, "PacketSize");
367 if (offset) {
368 char *separator;
369 int max_packet;
370
371 max_packet = strtoul(offset + 11, &separator, 16);
372 if (!max_packet)
373 LOG_ERROR("invalid max packet, using defaults");
374 else
375 h->max_packet = max_packet;
376 LOG_DEBUG("max packet supported : %" PRIu32 " bytes", h->max_packet);
377 }
378
379
380 /* if required re allocate packet buffer */
381 if (h->max_packet != ICDI_PACKET_SIZE) {
382 h->read_buffer = realloc(h->read_buffer, h->max_packet);
383 h->write_buffer = realloc(h->write_buffer, h->max_packet);
384 if (h->read_buffer == 0 || h->write_buffer == 0) {
385 LOG_ERROR("unable to reallocate memory");
386 return ERROR_FAIL;
387 }
388 }
389
390 /* set extended mode */
391 result = icdi_send_cmd(handle, "!");
392 if (result != ERROR_OK)
393 return result;
394
395 /* check result */
396 result = icdi_get_cmd_result(handle);
397 if (result != ERROR_OK) {
398 LOG_ERROR("unable to enable extended mode: 0x%x", result);
399 return ERROR_FAIL;
400 }
401
402 return ERROR_OK;
403 }
404
405 static int icdi_usb_reset(void *handle)
406 {
407 /* we do this in hla_target.c */
408 return ERROR_OK;
409 }
410
411 static int icdi_usb_assert_srst(void *handle, int srst)
412 {
413 /* TODO not supported yet */
414 return ERROR_COMMAND_NOTFOUND;
415 }
416
417 static int icdi_usb_run(void *handle)
418 {
419 int result;
420
421 /* resume target at current address */
422 result = icdi_send_cmd(handle, "c");
423 if (result != ERROR_OK)
424 return result;
425
426 /* check result */
427 result = icdi_get_cmd_result(handle);
428 if (result != ERROR_OK) {
429 LOG_ERROR("continue failed: 0x%x", result);
430 return ERROR_FAIL;
431 }
432
433 return result;
434 }
435
436 static int icdi_usb_halt(void *handle)
437 {
438 int result;
439
440 /* this query halts the target ?? */
441 result = icdi_send_cmd(handle, "?");
442 if (result != ERROR_OK)
443 return result;
444
445 /* check result */
446 result = icdi_get_cmd_result(handle);
447 if (result != ERROR_OK) {
448 LOG_ERROR("halt failed: 0x%x", result);
449 return ERROR_FAIL;
450 }
451
452 return result;
453 }
454
455 static int icdi_usb_step(void *handle)
456 {
457 int result;
458
459 /* step target at current address */
460 result = icdi_send_cmd(handle, "s");
461 if (result != ERROR_OK)
462 return result;
463
464 /* check result */
465 result = icdi_get_cmd_result(handle);
466 if (result != ERROR_OK) {
467 LOG_ERROR("step failed: 0x%x", result);
468 return ERROR_FAIL;
469 }
470
471 return result;
472 }
473
474 static int icdi_usb_read_regs(void *handle)
475 {
476 /* currently unsupported */
477 return ERROR_OK;
478 }
479
480 static int icdi_usb_read_reg(void *handle, int num, uint32_t *val)
481 {
482 int result;
483 struct icdi_usb_handle_s *h;
484 char cmd[10];
485
486 h = (struct icdi_usb_handle_s *)handle;
487
488 snprintf(cmd, sizeof(cmd), "p%x", num);
489 result = icdi_send_cmd(handle, cmd);
490 if (result != ERROR_OK)
491 return result;
492
493 /* check result */
494 result = icdi_get_cmd_result(handle);
495 if (result != ERROR_OK) {
496 LOG_ERROR("register read failed: 0x%x", result);
497 return ERROR_FAIL;
498 }
499
500 /* convert result */
501 if (unhexify((char *)val, h->read_buffer + 2, 4) != 4) {
502 LOG_ERROR("failed to convert result");
503 return ERROR_FAIL;
504 }
505
506 return result;
507 }
508
509 static int icdi_usb_write_reg(void *handle, int num, uint32_t val)
510 {
511 int result;
512 char cmd[20];
513
514 int cmd_len = snprintf(cmd, sizeof(cmd), "P%x=", num);
515 hexify(cmd + cmd_len, (char *)&val, 4, sizeof(cmd));
516
517 result = icdi_send_cmd(handle, cmd);
518 if (result != ERROR_OK)
519 return result;
520
521 /* check result */
522 result = icdi_get_cmd_result(handle);
523 if (result != ERROR_OK) {
524 LOG_ERROR("register write failed: 0x%x", result);
525 return ERROR_FAIL;
526 }
527
528 return result;
529 }
530
531 static int icdi_usb_read_mem_int(void *handle, uint32_t addr, uint32_t len, uint8_t *buffer)
532 {
533 int result;
534 struct icdi_usb_handle_s *h;
535 char cmd[20];
536
537 h = (struct icdi_usb_handle_s *)handle;
538
539 snprintf(cmd, sizeof(cmd), "x%x,%x", addr, len);
540 result = icdi_send_cmd(handle, cmd);
541 if (result != ERROR_OK)
542 return result;
543
544 /* check result */
545 result = icdi_get_cmd_result(handle);
546 if (result != ERROR_OK) {
547 LOG_ERROR("memory read failed: 0x%x", result);
548 return ERROR_FAIL;
549 }
550
551 /* unescape input */
552 int read_len = remote_unescape_input(h->read_buffer + 5, h->read_count - 8, (char *)buffer, len);
553 if (read_len != (int)len) {
554 LOG_ERROR("read more bytes than expected: actual 0x%" PRIx32 " expected 0x%" PRIx32, read_len, len);
555 return ERROR_FAIL;
556 }
557
558 return ERROR_OK;
559 }
560
561 static int icdi_usb_write_mem_int(void *handle, uint32_t addr, uint32_t len, const uint8_t *buffer)
562 {
563 int result;
564 struct icdi_usb_handle_s *h;
565
566 h = (struct icdi_usb_handle_s *)handle;
567
568 size_t cmd_len = snprintf(h->write_buffer, h->max_packet, PACKET_START "X%x,%x:", addr, len);
569
570 int out_len;
571 cmd_len += remote_escape_output((char *)buffer, len, h->write_buffer + cmd_len,
572 &out_len, h->max_packet - cmd_len);
573
574 if (out_len < (int)len) {
575 /* for now issue a error as we have no way of allocating a larger buffer */
576 LOG_ERROR("memory buffer too small: requires 0x%" PRIx32 " actual 0x%" PRIx32, out_len, len);
577 return ERROR_FAIL;
578 }
579
580 result = icdi_send_packet(handle, cmd_len);
581 if (result != ERROR_OK)
582 return result;
583
584 /* check result */
585 result = icdi_get_cmd_result(handle);
586 if (result != ERROR_OK) {
587 LOG_ERROR("memory write failed: 0x%x", result);
588 return ERROR_FAIL;
589 }
590
591 return ERROR_OK;
592 }
593
594 static int icdi_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
595 uint32_t count, uint8_t *buffer)
596 {
597 int retval = ERROR_OK;
598 struct icdi_usb_handle_s *h = (struct icdi_usb_handle_s *)handle;
599 uint32_t bytes_remaining;
600
601 /* calculate byte count */
602 count *= size;
603
604 while (count) {
605
606 bytes_remaining = h->max_rw_packet;
607 if (count < bytes_remaining)
608 bytes_remaining = count;
609
610 retval = icdi_usb_read_mem_int(handle, addr, bytes_remaining, buffer);
611 if (retval != ERROR_OK)
612 return retval;
613
614 buffer += bytes_remaining;
615 addr += bytes_remaining;
616 count -= bytes_remaining;
617 }
618
619 return retval;
620 }
621
622 static int icdi_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
623 uint32_t count, const uint8_t *buffer)
624 {
625 int retval = ERROR_OK;
626 struct icdi_usb_handle_s *h = (struct icdi_usb_handle_s *)handle;
627 uint32_t bytes_remaining;
628
629 /* calculate byte count */
630 count *= size;
631
632 while (count) {
633
634 bytes_remaining = h->max_rw_packet;
635 if (count < bytes_remaining)
636 bytes_remaining = count;
637
638 retval = icdi_usb_write_mem_int(handle, addr, bytes_remaining, buffer);
639 if (retval != ERROR_OK)
640 return retval;
641
642 buffer += bytes_remaining;
643 addr += bytes_remaining;
644 count -= bytes_remaining;
645 }
646
647 return retval;
648 }
649
650 static int icdi_usb_close(void *handle)
651 {
652 struct icdi_usb_handle_s *h;
653
654 h = (struct icdi_usb_handle_s *)handle;
655
656 if (h->usb_dev)
657 libusb_close(h->usb_dev);
658
659 if (h->usb_ctx)
660 libusb_exit(h->usb_ctx);
661
662 if (h->read_buffer)
663 free(h->read_buffer);
664
665 if (h->write_buffer)
666 free(h->write_buffer);
667
668 free(handle);
669
670 return ERROR_OK;
671 }
672
673 static int icdi_usb_open(struct hl_interface_param_s *param, void **fd)
674 {
675 int retval;
676 struct icdi_usb_handle_s *h;
677
678 LOG_DEBUG("icdi_usb_open");
679
680 h = calloc(1, sizeof(struct icdi_usb_handle_s));
681
682 if (h == 0) {
683 LOG_ERROR("unable to allocate memory");
684 return ERROR_FAIL;
685 }
686
687 LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x", param->transport,
688 param->vid, param->pid);
689
690 if (libusb_init(&h->usb_ctx) != 0) {
691 LOG_ERROR("libusb init failed");
692 goto error_open;
693 }
694
695 h->usb_dev = libusb_open_device_with_vid_pid(h->usb_ctx, param->vid, param->pid);
696 if (!h->usb_dev) {
697 LOG_ERROR("open failed");
698 goto error_open;
699 }
700
701 if (libusb_claim_interface(h->usb_dev, 2)) {
702 LOG_DEBUG("claim interface failed");
703 goto error_open;
704 }
705
706 /* check if mode is supported */
707 retval = ERROR_OK;
708
709 switch (param->transport) {
710 #if 0
711 /* TODO place holder as swd is not currently supported */
712 case HL_TRANSPORT_SWD:
713 #endif
714 case HL_TRANSPORT_JTAG:
715 break;
716 default:
717 retval = ERROR_FAIL;
718 break;
719 }
720
721 if (retval != ERROR_OK) {
722 LOG_ERROR("mode (transport) not supported by device");
723 goto error_open;
724 }
725
726 /* allocate buffer */
727 h->read_buffer = malloc(ICDI_PACKET_SIZE);
728 h->write_buffer = malloc(ICDI_PACKET_SIZE);
729 h->max_packet = ICDI_PACKET_SIZE;
730
731 if (h->read_buffer == 0 || h->write_buffer == 0) {
732 LOG_DEBUG("malloc failed");
733 goto error_open;
734 }
735
736 /* query icdi version etc */
737 retval = icdi_usb_version(h);
738 if (retval != ERROR_OK)
739 goto error_open;
740
741 /* query icdi support */
742 retval = icdi_usb_query(h);
743 if (retval != ERROR_OK)
744 goto error_open;
745
746 *fd = h;
747
748 /* set the max target read/write buffer in bytes
749 * as we are using gdb binary packets to transfer memory we have to
750 * reserve half the buffer for any possible escape chars plus
751 * at least 64 bytes for the gdb packet header */
752 h->max_rw_packet = (((h->max_packet - 64) / 4) * 4) / 2;
753
754 return ERROR_OK;
755
756 error_open:
757 icdi_usb_close(h);
758
759 return ERROR_FAIL;
760 }
761
762 struct hl_layout_api_s icdi_usb_layout_api = {
763 .open = icdi_usb_open,
764 .close = icdi_usb_close,
765 .idcode = icdi_usb_idcode,
766 .state = icdi_usb_state,
767 .reset = icdi_usb_reset,
768 .assert_srst = icdi_usb_assert_srst,
769 .run = icdi_usb_run,
770 .halt = icdi_usb_halt,
771 .step = icdi_usb_step,
772 .read_regs = icdi_usb_read_regs,
773 .read_reg = icdi_usb_read_reg,
774 .write_reg = icdi_usb_write_reg,
775 .read_mem = icdi_usb_read_mem,
776 .write_mem = icdi_usb_write_mem,
777 .write_debug_reg = icdi_usb_write_debug_reg
778 };

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)