hla: move memory read/write functionality to driver
[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 return ERROR_OK;
288 }
289
290 static int icdi_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val)
291 {
292 return icdi_usb_write_mem(handle, addr, 4, 1, (uint8_t *)&val);
293 }
294
295 static enum target_state icdi_usb_state(void *handle)
296 {
297 int result;
298 struct icdi_usb_handle_s *h;
299 uint32_t dhcsr;
300
301 h = (struct icdi_usb_handle_s *)handle;
302
303 result = icdi_usb_read_mem(h, DCB_DHCSR, 4, 1, (uint8_t *)&dhcsr);
304 if (result != ERROR_OK)
305 return TARGET_UNKNOWN;
306
307 if (dhcsr & S_HALT)
308 return TARGET_HALTED;
309
310 return TARGET_RUNNING;
311 }
312
313 static int icdi_usb_version(void *handle)
314 {
315 struct icdi_usb_handle_s *h;
316 h = (struct icdi_usb_handle_s *)handle;
317
318 char version[20];
319
320 /* get info about icdi */
321 int result = icdi_send_remote_cmd(handle, "version");
322 if (result != ERROR_OK)
323 return result;
324
325 if (h->read_count < 8) {
326 LOG_ERROR("Invalid Reply Received");
327 return ERROR_FAIL;
328 }
329
330 /* convert reply */
331 if (unhexify(version, h->read_buffer + 2, 4) != 4) {
332 LOG_WARNING("unable to get ICDI version");
333 return ERROR_OK;
334 }
335
336 /* null terminate and print info */
337 version[4] = 0;
338
339 LOG_INFO("ICDI Firmware version: %s", version);
340
341 return ERROR_OK;
342 }
343
344 static int icdi_usb_query(void *handle)
345 {
346 int result;
347
348 struct icdi_usb_handle_s *h;
349 h = (struct icdi_usb_handle_s *)handle;
350
351 result = icdi_send_cmd(handle, "qSupported");
352 if (result != ERROR_OK)
353 return result;
354
355 /* check result */
356 result = icdi_get_cmd_result(handle);
357 if (result != ERROR_OK) {
358 LOG_ERROR("query supported failed: 0x%x", result);
359 return ERROR_FAIL;
360 }
361
362 /* from this we can get the max packet supported */
363
364 /* query packet buffer size */
365 char *offset = strstr(h->read_buffer, "PacketSize");
366 if (offset) {
367 char *separator;
368 int max_packet;
369
370 max_packet = strtoul(offset + 11, &separator, 16);
371 if (!max_packet)
372 LOG_ERROR("invalid max packet, using defaults");
373 else
374 h->max_packet = max_packet;
375 LOG_DEBUG("max packet supported : %" PRIu32 " bytes", h->max_packet);
376 }
377
378
379 /* if required re allocate packet buffer */
380 if (h->max_packet != ICDI_PACKET_SIZE) {
381 h->read_buffer = realloc(h->read_buffer, h->max_packet);
382 h->write_buffer = realloc(h->write_buffer, h->max_packet);
383 if (h->read_buffer == 0 || h->write_buffer == 0) {
384 LOG_ERROR("unable to reallocate memory");
385 return ERROR_FAIL;
386 }
387 }
388
389 /* set extended mode */
390 result = icdi_send_cmd(handle, "!");
391 if (result != ERROR_OK)
392 return result;
393
394 /* check result */
395 result = icdi_get_cmd_result(handle);
396 if (result != ERROR_OK) {
397 LOG_ERROR("unable to enable extended mode: 0x%x", result);
398 return ERROR_FAIL;
399 }
400
401 return ERROR_OK;
402 }
403
404 static int icdi_usb_reset(void *handle)
405 {
406 /* we do this in hla_target.c */
407 return ERROR_OK;
408 }
409
410 static int icdi_usb_assert_srst(void *handle, int srst)
411 {
412 /* TODO not supported yet */
413 return ERROR_COMMAND_NOTFOUND;
414 }
415
416 static int icdi_usb_run(void *handle)
417 {
418 int result;
419
420 /* resume target at current address */
421 result = icdi_send_cmd(handle, "c");
422 if (result != ERROR_OK)
423 return result;
424
425 /* check result */
426 result = icdi_get_cmd_result(handle);
427 if (result != ERROR_OK) {
428 LOG_ERROR("continue failed: 0x%x", result);
429 return ERROR_FAIL;
430 }
431
432 return result;
433 }
434
435 static int icdi_usb_halt(void *handle)
436 {
437 int result;
438
439 /* this query halts the target ?? */
440 result = icdi_send_cmd(handle, "?");
441 if (result != ERROR_OK)
442 return result;
443
444 /* check result */
445 result = icdi_get_cmd_result(handle);
446 if (result != ERROR_OK) {
447 LOG_ERROR("halt failed: 0x%x", result);
448 return ERROR_FAIL;
449 }
450
451 return result;
452 }
453
454 static int icdi_usb_step(void *handle)
455 {
456 int result;
457
458 /* step target at current address */
459 result = icdi_send_cmd(handle, "s");
460 if (result != ERROR_OK)
461 return result;
462
463 /* check result */
464 result = icdi_get_cmd_result(handle);
465 if (result != ERROR_OK) {
466 LOG_ERROR("step failed: 0x%x", result);
467 return ERROR_FAIL;
468 }
469
470 return result;
471 }
472
473 static int icdi_usb_read_regs(void *handle)
474 {
475 /* currently unsupported */
476 return ERROR_OK;
477 }
478
479 static int icdi_usb_read_reg(void *handle, int num, uint32_t *val)
480 {
481 int result;
482 struct icdi_usb_handle_s *h;
483 char cmd[10];
484
485 h = (struct icdi_usb_handle_s *)handle;
486
487 snprintf(cmd, sizeof(cmd), "p%x", num);
488 result = icdi_send_cmd(handle, cmd);
489 if (result != ERROR_OK)
490 return result;
491
492 /* check result */
493 result = icdi_get_cmd_result(handle);
494 if (result != ERROR_OK) {
495 LOG_ERROR("register read failed: 0x%x", result);
496 return ERROR_FAIL;
497 }
498
499 /* convert result */
500 if (unhexify((char *)val, h->read_buffer + 2, 4) != 4) {
501 LOG_ERROR("failed to convert result");
502 return ERROR_FAIL;
503 }
504
505 return result;
506 }
507
508 static int icdi_usb_write_reg(void *handle, int num, uint32_t val)
509 {
510 int result;
511 char cmd[20];
512
513 int cmd_len = snprintf(cmd, sizeof(cmd), "P%x=", num);
514 hexify(cmd + cmd_len, (char *)&val, 4, sizeof(cmd));
515
516 result = icdi_send_cmd(handle, cmd);
517 if (result != ERROR_OK)
518 return result;
519
520 /* check result */
521 result = icdi_get_cmd_result(handle);
522 if (result != ERROR_OK) {
523 LOG_ERROR("register write failed: 0x%x", result);
524 return ERROR_FAIL;
525 }
526
527 return result;
528 }
529
530 static int icdi_usb_read_mem_int(void *handle, uint32_t addr, uint32_t len, uint8_t *buffer)
531 {
532 int result;
533 struct icdi_usb_handle_s *h;
534 char cmd[20];
535
536 h = (struct icdi_usb_handle_s *)handle;
537
538 snprintf(cmd, sizeof(cmd), "x%x,%x", addr, len);
539 result = icdi_send_cmd(handle, cmd);
540 if (result != ERROR_OK)
541 return result;
542
543 /* check result */
544 result = icdi_get_cmd_result(handle);
545 if (result != ERROR_OK) {
546 LOG_ERROR("memory read failed: 0x%x", result);
547 return ERROR_FAIL;
548 }
549
550 /* unescape input */
551 int read_len = remote_unescape_input(h->read_buffer + 5, h->read_count - 8, (char *)buffer, len);
552 if (read_len != (int)len) {
553 LOG_ERROR("read more bytes than expected: actual 0x%" PRIx32 " expected 0x%" PRIx32, read_len, len);
554 return ERROR_FAIL;
555 }
556
557 return ERROR_OK;
558 }
559
560 static int icdi_usb_write_mem_int(void *handle, uint32_t addr, uint32_t len, const uint8_t *buffer)
561 {
562 int result;
563 struct icdi_usb_handle_s *h;
564
565 h = (struct icdi_usb_handle_s *)handle;
566
567 size_t cmd_len = snprintf(h->write_buffer, h->max_packet, PACKET_START "X%x,%x:", addr, len);
568
569 int out_len;
570 cmd_len += remote_escape_output((char *)buffer, len, h->write_buffer + cmd_len,
571 &out_len, h->max_packet - cmd_len);
572
573 if (out_len < (int)len) {
574 /* for now issue a error as we have no way of allocating a larger buffer */
575 LOG_ERROR("memory buffer too small: requires 0x%" PRIx32 " actual 0x%" PRIx32, out_len, len);
576 return ERROR_FAIL;
577 }
578
579 result = icdi_send_packet(handle, cmd_len);
580 if (result != ERROR_OK)
581 return result;
582
583 /* check result */
584 result = icdi_get_cmd_result(handle);
585 if (result != ERROR_OK) {
586 LOG_ERROR("memory write failed: 0x%x", result);
587 return ERROR_FAIL;
588 }
589
590 return ERROR_OK;
591 }
592
593 static int icdi_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
594 uint32_t count, uint8_t *buffer)
595 {
596 int retval = ERROR_OK;
597 struct icdi_usb_handle_s *h = (struct icdi_usb_handle_s *)handle;
598 uint32_t bytes_remaining;
599
600 /* calculate byte count */
601 count *= size;
602
603 while (count) {
604
605 bytes_remaining = h->max_rw_packet;
606 if (count < bytes_remaining)
607 bytes_remaining = count;
608
609 retval = icdi_usb_read_mem_int(handle, addr, bytes_remaining, buffer);
610 if (retval != ERROR_OK)
611 return retval;
612
613 buffer += bytes_remaining;
614 addr += bytes_remaining;
615 count -= bytes_remaining;
616 }
617
618 return retval;
619 }
620
621 static int icdi_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
622 uint32_t count, const uint8_t *buffer)
623 {
624 int retval = ERROR_OK;
625 struct icdi_usb_handle_s *h = (struct icdi_usb_handle_s *)handle;
626 uint32_t bytes_remaining;
627
628 /* calculate byte count */
629 count *= size;
630
631 while (count) {
632
633 bytes_remaining = h->max_rw_packet;
634 if (count < bytes_remaining)
635 bytes_remaining = count;
636
637 retval = icdi_usb_write_mem_int(handle, addr, bytes_remaining, buffer);
638 if (retval != ERROR_OK)
639 return retval;
640
641 buffer += bytes_remaining;
642 addr += bytes_remaining;
643 count -= bytes_remaining;
644 }
645
646 return retval;
647 }
648
649 static int icdi_usb_close(void *handle)
650 {
651 struct icdi_usb_handle_s *h;
652
653 h = (struct icdi_usb_handle_s *)handle;
654
655 if (h->usb_dev)
656 libusb_close(h->usb_dev);
657
658 if (h->usb_ctx)
659 libusb_exit(h->usb_ctx);
660
661 if (h->read_buffer)
662 free(h->read_buffer);
663
664 if (h->write_buffer)
665 free(h->write_buffer);
666
667 free(handle);
668
669 return ERROR_OK;
670 }
671
672 static int icdi_usb_open(struct hl_interface_param_s *param, void **fd)
673 {
674 int retval;
675 struct icdi_usb_handle_s *h;
676
677 LOG_DEBUG("icdi_usb_open");
678
679 h = calloc(1, sizeof(struct icdi_usb_handle_s));
680
681 if (h == 0) {
682 LOG_ERROR("unable to allocate memory");
683 return ERROR_FAIL;
684 }
685
686 LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x", param->transport,
687 param->vid, param->pid);
688
689 if (libusb_init(&h->usb_ctx) != 0) {
690 LOG_ERROR("libusb init failed");
691 goto error_open;
692 }
693
694 h->usb_dev = libusb_open_device_with_vid_pid(h->usb_ctx, param->vid, param->pid);
695 if (!h->usb_dev) {
696 LOG_ERROR("open failed");
697 goto error_open;
698 }
699
700 if (libusb_claim_interface(h->usb_dev, 2)) {
701 LOG_DEBUG("claim interface failed");
702 goto error_open;
703 }
704
705 /* check if mode is supported */
706 retval = ERROR_OK;
707
708 switch (param->transport) {
709 #if 0
710 /* TODO place holder as swd is not currently supported */
711 case HL_TRANSPORT_SWD:
712 #endif
713 case HL_TRANSPORT_JTAG:
714 break;
715 default:
716 retval = ERROR_FAIL;
717 break;
718 }
719
720 if (retval != ERROR_OK) {
721 LOG_ERROR("mode (transport) not supported by device");
722 goto error_open;
723 }
724
725 /* allocate buffer */
726 h->read_buffer = malloc(ICDI_PACKET_SIZE);
727 h->write_buffer = malloc(ICDI_PACKET_SIZE);
728 h->max_packet = ICDI_PACKET_SIZE;
729
730 if (h->read_buffer == 0 || h->write_buffer == 0) {
731 LOG_DEBUG("malloc failed");
732 goto error_open;
733 }
734
735 /* query icdi version etc */
736 retval = icdi_usb_version(h);
737 if (retval != ERROR_OK)
738 goto error_open;
739
740 /* query icdi support */
741 retval = icdi_usb_query(h);
742 if (retval != ERROR_OK)
743 goto error_open;
744
745 *fd = h;
746
747 /* set the max target read/write buffer in bytes
748 * as we are using gdb binary packets to transfer memory we have to
749 * reserve half the buffer for any possible escape chars plus
750 * at least 64 bytes for the gdb packet header */
751 h->max_rw_packet = (((h->max_packet - 64) / 4) * 4) / 2;
752
753 return ERROR_OK;
754
755 error_open:
756 icdi_usb_close(h);
757
758 return ERROR_FAIL;
759 }
760
761 struct hl_layout_api_s icdi_usb_layout_api = {
762 .open = icdi_usb_open,
763 .close = icdi_usb_close,
764 .idcode = icdi_usb_idcode,
765 .state = icdi_usb_state,
766 .reset = icdi_usb_reset,
767 .assert_srst = icdi_usb_assert_srst,
768 .run = icdi_usb_run,
769 .halt = icdi_usb_halt,
770 .step = icdi_usb_step,
771 .read_regs = icdi_usb_read_regs,
772 .read_reg = icdi_usb_read_reg,
773 .write_reg = icdi_usb_write_reg,
774 .read_mem = icdi_usb_read_mem,
775 .write_mem = icdi_usb_write_mem,
776 .write_debug_reg = icdi_usb_write_debug_reg
777 };

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)