Conform to C99 integer types format specifiers
[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 = handle;
125 int result, retry = 0;
126 int transferred = 0;
127
128 assert(handle != NULL);
129
130 /* check we have a large enough buffer for checksum "#00" */
131 if (len + 3 > h->max_packet) {
132 LOG_ERROR("packet buffer too small");
133 return ERROR_FAIL;
134 }
135
136 /* calculate checksum - offset start of packet */
137 for (int i = 1; i < len; i++)
138 cksum += h->write_buffer[i];
139
140 len += sprintf(&h->write_buffer[len], PACKET_END "%02x", cksum);
141
142 #ifdef _DEBUG_USB_COMMS_
143 char buffer[50];
144 char ch = h->write_buffer[1];
145 if (ch == 'x' || ch == 'X')
146 LOG_DEBUG("writing packet: <binary>");
147 else {
148 memcpy(buffer, h->write_buffer, len >= 50 ? 50-1 : len);
149 buffer[len] = 0;
150 LOG_DEBUG("writing packet: %s", buffer);
151 }
152 #endif
153
154 while (1) {
155
156 result = libusb_bulk_transfer(h->usb_dev, ICDI_WRITE_ENDPOINT, (unsigned char *)h->write_buffer, len,
157 &transferred, ICDI_WRITE_TIMEOUT);
158 if (result != 0 || transferred != len) {
159 LOG_DEBUG("Error TX Data %d", result);
160 return ERROR_FAIL;
161 }
162
163 /* check that the client got the message ok, or shall we resend */
164 result = libusb_bulk_transfer(h->usb_dev, ICDI_READ_ENDPOINT, (unsigned char *)h->read_buffer, h->max_packet,
165 &transferred, ICDI_READ_TIMEOUT);
166 if (result != 0 || transferred < 1) {
167 LOG_DEBUG("Error RX Data %d", result);
168 return ERROR_FAIL;
169 }
170
171 #ifdef _DEBUG_USB_COMMS_
172 LOG_DEBUG("received reply: '%c' : count %d", h->read_buffer[0], transferred);
173 #endif
174
175 if (h->read_buffer[0] == '-') {
176 LOG_DEBUG("Resending packet %d", ++retry);
177 } else {
178 if (h->read_buffer[0] != '+')
179 LOG_DEBUG("Unexpected Reply from ICDI: %c", h->read_buffer[0]);
180 break;
181 }
182
183 if (retry == 3) {
184 LOG_DEBUG("maximum nack retries attempted");
185 return ERROR_FAIL;
186 }
187 }
188
189 retry = 0;
190 h->read_count = transferred;
191
192 while (1) {
193
194 /* read reply from icdi */
195 result = libusb_bulk_transfer(h->usb_dev, ICDI_READ_ENDPOINT, (unsigned char *)h->read_buffer + h->read_count,
196 h->max_packet - h->read_count, &transferred, ICDI_READ_TIMEOUT);
197
198 #ifdef _DEBUG_USB_COMMS_
199 LOG_DEBUG("received data: count %d", transferred);
200 #endif
201
202 /* check for errors but retry for timeout */
203 if (result != 0) {
204
205 if (result == LIBUSB_ERROR_TIMEOUT) {
206 LOG_DEBUG("Error RX timeout %d", result);
207 } else {
208 LOG_DEBUG("Error RX Data %d", result);
209 return ERROR_FAIL;
210 }
211 }
212
213 h->read_count += transferred;
214
215 /* we need to make sure we have a full packet, including checksum */
216 if (h->read_count > 5) {
217
218 /* check that we have received an packet delimiter
219 * we do not validate the checksum
220 * reply should contain $...#AA - so we check for # */
221 if (h->read_buffer[h->read_count - 3] == '#')
222 return ERROR_OK;
223 }
224
225 if (retry++ == 3) {
226 LOG_DEBUG("maximum data retries attempted");
227 break;
228 }
229 }
230
231 return ERROR_FAIL;
232 }
233
234 static int icdi_send_cmd(void *handle, const char *cmd)
235 {
236 struct icdi_usb_handle_s *h = handle;
237
238 int cmd_len = snprintf(h->write_buffer, h->max_packet, PACKET_START "%s", cmd);
239 return icdi_send_packet(handle, cmd_len);
240 }
241
242 static int icdi_send_remote_cmd(void *handle, const char *data)
243 {
244 struct icdi_usb_handle_s *h = handle;
245
246 size_t cmd_len = sprintf(h->write_buffer, PACKET_START "qRcmd,");
247 cmd_len += hexify(h->write_buffer + cmd_len, data, 0, h->max_packet - cmd_len);
248
249 return icdi_send_packet(handle, cmd_len);
250 }
251
252 static int icdi_get_cmd_result(void *handle)
253 {
254 struct icdi_usb_handle_s *h = handle;
255 int offset = 0;
256 char ch;
257
258 assert(handle != NULL);
259
260 do {
261 ch = h->read_buffer[offset++];
262 if (offset > h->read_count)
263 return ERROR_FAIL;
264 } while (ch != '$');
265
266 if (memcmp("OK", h->read_buffer + offset, 2) == 0)
267 return ERROR_OK;
268
269 if (h->read_buffer[offset] == 'E') {
270 /* get error code */
271 char result;
272 if (unhexify(&result, h->read_buffer + offset + 1, 1) != 1)
273 return ERROR_FAIL;
274 return result;
275 }
276
277 /* for now we assume everything else is ok */
278 return ERROR_OK;
279 }
280
281 static int icdi_usb_idcode(void *handle, uint32_t *idcode)
282 {
283 *idcode = 0;
284 return ERROR_OK;
285 }
286
287 static int icdi_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val)
288 {
289 uint8_t buf[4];
290 /* REVISIT: There's no target pointer here so there's no way to use target_buffer_set_u32().
291 * I guess all supported chips are little-endian anyway. */
292 h_u32_to_le(buf, val);
293 return icdi_usb_write_mem(handle, addr, 4, 1, buf);
294 }
295
296 static enum target_state icdi_usb_state(void *handle)
297 {
298 int result;
299 struct icdi_usb_handle_s *h = handle;
300 uint32_t dhcsr;
301 uint8_t buf[4];
302
303 result = icdi_usb_read_mem(h, DCB_DHCSR, 4, 1, buf);
304 if (result != ERROR_OK)
305 return TARGET_UNKNOWN;
306
307 /* REVISIT: There's no target pointer here so there's no way to use target_buffer_get_u32().
308 * I guess all supported chips are little-endian anyway. */
309 dhcsr = le_to_h_u32(buf);
310 if (dhcsr & S_HALT)
311 return TARGET_HALTED;
312
313 return TARGET_RUNNING;
314 }
315
316 static int icdi_usb_version(void *handle)
317 {
318 struct icdi_usb_handle_s *h = handle;
319
320 char version[20];
321
322 /* get info about icdi */
323 int result = icdi_send_remote_cmd(handle, "version");
324 if (result != ERROR_OK)
325 return result;
326
327 if (h->read_count < 8) {
328 LOG_ERROR("Invalid Reply Received");
329 return ERROR_FAIL;
330 }
331
332 /* convert reply */
333 if (unhexify(version, h->read_buffer + 2, 4) != 4) {
334 LOG_WARNING("unable to get ICDI version");
335 return ERROR_OK;
336 }
337
338 /* null terminate and print info */
339 version[4] = 0;
340
341 LOG_INFO("ICDI Firmware version: %s", version);
342
343 return ERROR_OK;
344 }
345
346 static int icdi_usb_query(void *handle)
347 {
348 int result;
349
350 struct icdi_usb_handle_s *h = 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 = strtol(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 : %i 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 = handle;
484 char cmd[10];
485
486 snprintf(cmd, sizeof(cmd), "p%x", num);
487 result = icdi_send_cmd(handle, cmd);
488 if (result != ERROR_OK)
489 return result;
490
491 /* check result */
492 result = icdi_get_cmd_result(handle);
493 if (result != ERROR_OK) {
494 LOG_ERROR("register read failed: 0x%x", result);
495 return ERROR_FAIL;
496 }
497
498 /* convert result */
499 uint8_t buf[4];
500 if (unhexify((char *)buf, h->read_buffer + 2, 4) != 4) {
501 LOG_ERROR("failed to convert result");
502 return ERROR_FAIL;
503 }
504 *val = le_to_h_u32(buf);
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 uint8_t buf[4];
514 h_u32_to_le(buf, val);
515
516 int cmd_len = snprintf(cmd, sizeof(cmd), "P%x=", num);
517 hexify(cmd + cmd_len, (const char *)buf, 4, sizeof(cmd));
518
519 result = icdi_send_cmd(handle, cmd);
520 if (result != ERROR_OK)
521 return result;
522
523 /* check result */
524 result = icdi_get_cmd_result(handle);
525 if (result != ERROR_OK) {
526 LOG_ERROR("register write failed: 0x%x", result);
527 return ERROR_FAIL;
528 }
529
530 return result;
531 }
532
533 static int icdi_usb_read_mem_int(void *handle, uint32_t addr, uint32_t len, uint8_t *buffer)
534 {
535 int result;
536 struct icdi_usb_handle_s *h = handle;
537 char cmd[20];
538
539 snprintf(cmd, sizeof(cmd), "x%" PRIx32 ",%" PRIx32, 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%x 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 = handle;
565
566 size_t cmd_len = snprintf(h->write_buffer, h->max_packet, PACKET_START "X%" PRIx32 ",%" PRIx32 ":", addr, len);
567
568 int out_len;
569 cmd_len += remote_escape_output((const char *)buffer, len, h->write_buffer + cmd_len,
570 &out_len, h->max_packet - cmd_len);
571
572 if (out_len < (int)len) {
573 /* for now issue a error as we have no way of allocating a larger buffer */
574 LOG_ERROR("memory buffer too small: requires 0x%x actual 0x%" PRIx32, out_len, len);
575 return ERROR_FAIL;
576 }
577
578 result = icdi_send_packet(handle, cmd_len);
579 if (result != ERROR_OK)
580 return result;
581
582 /* check result */
583 result = icdi_get_cmd_result(handle);
584 if (result != ERROR_OK) {
585 LOG_ERROR("memory write failed: 0x%x", result);
586 return ERROR_FAIL;
587 }
588
589 return ERROR_OK;
590 }
591
592 static int icdi_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
593 uint32_t count, uint8_t *buffer)
594 {
595 int retval = ERROR_OK;
596 struct icdi_usb_handle_s *h = handle;
597 uint32_t bytes_remaining;
598
599 /* calculate byte count */
600 count *= size;
601
602 while (count) {
603
604 bytes_remaining = h->max_rw_packet;
605 if (count < bytes_remaining)
606 bytes_remaining = count;
607
608 retval = icdi_usb_read_mem_int(handle, addr, bytes_remaining, buffer);
609 if (retval != ERROR_OK)
610 return retval;
611
612 buffer += bytes_remaining;
613 addr += bytes_remaining;
614 count -= bytes_remaining;
615 }
616
617 return retval;
618 }
619
620 static int icdi_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
621 uint32_t count, const uint8_t *buffer)
622 {
623 int retval = ERROR_OK;
624 struct icdi_usb_handle_s *h = handle;
625 uint32_t bytes_remaining;
626
627 /* calculate byte count */
628 count *= size;
629
630 while (count) {
631
632 bytes_remaining = h->max_rw_packet;
633 if (count < bytes_remaining)
634 bytes_remaining = count;
635
636 retval = icdi_usb_write_mem_int(handle, addr, bytes_remaining, buffer);
637 if (retval != ERROR_OK)
638 return retval;
639
640 buffer += bytes_remaining;
641 addr += bytes_remaining;
642 count -= bytes_remaining;
643 }
644
645 return retval;
646 }
647
648 static int icdi_usb_close(void *handle)
649 {
650 struct icdi_usb_handle_s *h = handle;
651
652 if (h->usb_dev)
653 libusb_close(h->usb_dev);
654
655 if (h->usb_ctx)
656 libusb_exit(h->usb_ctx);
657
658 if (h->read_buffer)
659 free(h->read_buffer);
660
661 if (h->write_buffer)
662 free(h->write_buffer);
663
664 free(handle);
665
666 return ERROR_OK;
667 }
668
669 static int icdi_usb_open(struct hl_interface_param_s *param, void **fd)
670 {
671 int retval;
672 struct icdi_usb_handle_s *h;
673
674 LOG_DEBUG("icdi_usb_open");
675
676 h = calloc(1, sizeof(struct icdi_usb_handle_s));
677
678 if (h == 0) {
679 LOG_ERROR("unable to allocate memory");
680 return ERROR_FAIL;
681 }
682
683 LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x", param->transport,
684 param->vid, param->pid);
685
686 if (libusb_init(&h->usb_ctx) != 0) {
687 LOG_ERROR("libusb init failed");
688 goto error_open;
689 }
690
691 h->usb_dev = libusb_open_device_with_vid_pid(h->usb_ctx, param->vid, param->pid);
692 if (!h->usb_dev) {
693 LOG_ERROR("open failed");
694 goto error_open;
695 }
696
697 if (libusb_claim_interface(h->usb_dev, 2)) {
698 LOG_DEBUG("claim interface failed");
699 goto error_open;
700 }
701
702 /* check if mode is supported */
703 retval = ERROR_OK;
704
705 switch (param->transport) {
706 #if 0
707 /* TODO place holder as swd is not currently supported */
708 case HL_TRANSPORT_SWD:
709 #endif
710 case HL_TRANSPORT_JTAG:
711 break;
712 default:
713 retval = ERROR_FAIL;
714 break;
715 }
716
717 if (retval != ERROR_OK) {
718 LOG_ERROR("mode (transport) not supported by device");
719 goto error_open;
720 }
721
722 /* allocate buffer */
723 h->read_buffer = malloc(ICDI_PACKET_SIZE);
724 h->write_buffer = malloc(ICDI_PACKET_SIZE);
725 h->max_packet = ICDI_PACKET_SIZE;
726
727 if (h->read_buffer == 0 || h->write_buffer == 0) {
728 LOG_DEBUG("malloc failed");
729 goto error_open;
730 }
731
732 /* query icdi version etc */
733 retval = icdi_usb_version(h);
734 if (retval != ERROR_OK)
735 goto error_open;
736
737 /* query icdi support */
738 retval = icdi_usb_query(h);
739 if (retval != ERROR_OK)
740 goto error_open;
741
742 *fd = h;
743
744 /* set the max target read/write buffer in bytes
745 * as we are using gdb binary packets to transfer memory we have to
746 * reserve half the buffer for any possible escape chars plus
747 * at least 64 bytes for the gdb packet header */
748 h->max_rw_packet = (((h->max_packet - 64) / 4) * 4) / 2;
749
750 return ERROR_OK;
751
752 error_open:
753 icdi_usb_close(h);
754
755 return ERROR_FAIL;
756 }
757
758 struct hl_layout_api_s icdi_usb_layout_api = {
759 .open = icdi_usb_open,
760 .close = icdi_usb_close,
761 .idcode = icdi_usb_idcode,
762 .state = icdi_usb_state,
763 .reset = icdi_usb_reset,
764 .assert_srst = icdi_usb_assert_srst,
765 .run = icdi_usb_run,
766 .halt = icdi_usb_halt,
767 .step = icdi_usb_step,
768 .read_regs = icdi_usb_read_regs,
769 .read_reg = icdi_usb_read_reg,
770 .write_reg = icdi_usb_write_reg,
771 .read_mem = icdi_usb_read_mem,
772 .write_mem = icdi_usb_write_mem,
773 .write_debug_reg = icdi_usb_write_debug_reg
774 };

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)