openocd: src/jtag: replace the GPL-2.0-or-later license tag
[openocd.git] / src / jtag / drivers / versaloon / usbtoxxx / usbtoxxx.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4 * Copyright (C) 2009 - 2010 by Simon Qian <SimonQian@SimonQian.com> *
5 ***************************************************************************/
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #include <string.h>
12
13 #include "../versaloon_include.h"
14 #include "../versaloon.h"
15 #include "../versaloon_internal.h"
16 #include "usbtoxxx.h"
17 #include "usbtoxxx_internal.h"
18
19 #define N_A "n/a"
20
21 static const char *types_name[96] = {
22 "usbtousart", "usbtospi", "usbtoi2c", "usbtogpio", "usbtocan", "usbtopwm",
23 "usbtoadc", "usbtodac",
24 "usbtomicrowire", "usbtoswim", "usbtodusi", N_A, N_A, N_A, "usbtopower", "usbtodelay",
25 N_A, N_A, N_A, N_A, N_A, N_A, N_A, N_A, N_A,
26 N_A, N_A, N_A, N_A, N_A, N_A, N_A,
27 "usbtojtagll", "usbtojtaghl", "usbtoissp", "usbtoc2", "usbtosbw",
28 "usbtolpcicp", "usbtoswd", "usbtojtagraw",
29 "usbtobdm", N_A, N_A, N_A, N_A, N_A, N_A, N_A,
30 N_A, N_A, N_A, N_A, N_A, N_A, N_A, N_A,
31 "usbtomsp430jtag", N_A, N_A, N_A, N_A, N_A, N_A, N_A,
32 "usbtopower", "usbtodelay", "usbtopoll", N_A, N_A, N_A, N_A, N_A,
33 N_A, N_A, N_A, N_A, N_A, N_A, N_A, "usbtoall"
34 };
35
36 uint8_t usbtoxxx_abilities[USB_TO_XXX_ABILITIES_LEN];
37
38 #define usbtoxxx_get_type_name(type) \
39 types_name[((type) - VERSALOON_USB_TO_XXX_CMD_START) \
40 % ARRAY_SIZE(types_name)]
41
42 static uint8_t type_pre;
43 static uint16_t usbtoxxx_buffer_index;
44 static uint16_t usbtoxxx_current_cmd_index;
45 static uint8_t *usbtoxxx_buffer;
46
47 static uint16_t collect_index;
48 static uint8_t collect_cmd;
49 static uint8_t poll_nesting;
50
51 struct usbtoxxx_context_t {
52 uint8_t type_pre;
53 uint8_t *usbtoxxx_buffer;
54 uint16_t usbtoxxx_current_cmd_index;
55 uint16_t usbtoxxx_buffer_index;
56 uint16_t versaloon_pending_idx;
57 };
58 static struct usbtoxxx_context_t poll_context;
59
60 static void usbtoxxx_save_context(struct usbtoxxx_context_t *c)
61 {
62 c->type_pre = type_pre;
63 c->usbtoxxx_buffer = usbtoxxx_buffer;
64 c->usbtoxxx_buffer_index = usbtoxxx_buffer_index;
65 c->usbtoxxx_current_cmd_index = usbtoxxx_current_cmd_index;
66 c->versaloon_pending_idx = versaloon_pending_idx;
67 }
68
69 static void usbtoxxx_pop_context(struct usbtoxxx_context_t *c)
70 {
71 type_pre = c->type_pre;
72 usbtoxxx_buffer = c->usbtoxxx_buffer;
73 usbtoxxx_buffer_index = c->usbtoxxx_buffer_index;
74 usbtoxxx_current_cmd_index = c->usbtoxxx_current_cmd_index;
75 versaloon_pending_idx = c->versaloon_pending_idx;
76 }
77
78 static RESULT usbtoxxx_validate_current_command_type(void)
79 {
80 if (type_pre > 0) {
81 /* not the first command */
82 if (!usbtoxxx_buffer) {
83 LOG_BUG(ERRMSG_INVALID_BUFFER, TO_STR(usbtoxxx_buffer));
84 return ERRCODE_INVALID_BUFFER;
85 }
86
87 usbtoxxx_buffer[0] = type_pre;
88 SET_LE_U16(&usbtoxxx_buffer[1], usbtoxxx_current_cmd_index);
89
90 usbtoxxx_buffer_index += usbtoxxx_current_cmd_index;
91 } else {
92 /* first command */
93 usbtoxxx_buffer_index = 3;
94 }
95
96 /* prepare for next command */
97 usbtoxxx_current_cmd_index = 3;
98 usbtoxxx_buffer = versaloon_buf + usbtoxxx_buffer_index;
99
100 collect_index = 0;
101 collect_cmd = 0;
102
103 return ERROR_OK;
104 }
105
106 RESULT usbtoxxx_execute_command(void)
107 {
108 uint16_t i;
109 uint16_t inlen;
110 RESULT result = ERROR_OK;
111
112 if (poll_nesting) {
113 LOG_BUG(ERRMSG_INVALID_USAGE, "USB_TO_POLL");
114 versaloon_free_want_pos();
115 return ERROR_FAIL;
116 }
117
118 if (usbtoxxx_validate_current_command_type() != ERROR_OK) {
119 LOG_BUG(ERRMSG_FAILURE_OPERATION, "validate previous commands");
120 versaloon_free_want_pos();
121 return ERRCODE_FAILURE_OPERATION;
122 }
123 if (usbtoxxx_buffer_index == 3) {
124 versaloon_free_want_pos();
125 return ERROR_OK;
126 }
127
128 versaloon_buf[0] = USB_TO_ALL;
129 SET_LE_U16(&versaloon_buf[1], usbtoxxx_buffer_index);
130
131 if (versaloon_send_command(usbtoxxx_buffer_index, &inlen) != ERROR_OK) {
132 versaloon_free_want_pos();
133 return ERROR_FAIL;
134 }
135
136 /* process return data */
137 usbtoxxx_buffer_index = 0;
138 for (i = 0; i < versaloon_pending_idx; i++) {
139 /* check result */
140 if ((i == 0) || !((versaloon_pending[i].collect)
141 && (versaloon_pending[i - 1].collect)
142 && (versaloon_pending[i].cmd
143 == versaloon_pending[i - 1].cmd))) {
144 if (USB_TO_XXX_CMD_NOT_SUPPORT
145 == versaloon_buf[usbtoxxx_buffer_index]) {
146 LOG_ERROR(ERRMSG_NOT_SUPPORT_BY,
147 usbtoxxx_get_type_name(versaloon_pending[i].type),
148 "current dongle");
149 result = ERROR_FAIL;
150 break;
151 } else if (versaloon_buf[usbtoxxx_buffer_index] != USB_TO_XXX_OK) {
152 LOG_ERROR("%s command 0x%02x failed with 0x%02x",
153 usbtoxxx_get_type_name(versaloon_pending[i].type),
154 versaloon_pending[i].cmd,
155 versaloon_buf[usbtoxxx_buffer_index]);
156 result = ERROR_FAIL;
157 break;
158 }
159 usbtoxxx_buffer_index++;
160 }
161
162 /* get result data */
163 if (versaloon_pending[i].pos) {
164 uint8_t processed = 0;
165
166 if (versaloon_pending[i].callback) {
167 versaloon_pending[i].callback(&versaloon_pending[i],
168 versaloon_buf + usbtoxxx_buffer_index, &processed);
169 }
170 if (!processed) {
171 struct versaloon_want_pos_t *tmp;
172
173 tmp = versaloon_pending[i].pos;
174 while (tmp) {
175 if ((tmp->buff) && (tmp->size > 0)) {
176 memcpy(tmp->buff,
177 versaloon_buf + usbtoxxx_buffer_index
178 + tmp->offset,
179 tmp->size);
180 }
181 struct versaloon_want_pos_t *free_tmp;
182 free_tmp = tmp;
183 tmp = tmp->next;
184 free(free_tmp);
185 }
186 versaloon_pending[i].pos = NULL;
187 }
188 } else if ((versaloon_pending[i].want_data_size > 0)
189 && (versaloon_pending[i].data_buffer)) {
190 uint8_t processed = 0;
191
192 if (versaloon_pending[i].callback) {
193 versaloon_pending[i].callback(&versaloon_pending[i],
194 versaloon_buf + usbtoxxx_buffer_index, &processed);
195 }
196 if (!processed) {
197 memcpy(versaloon_pending[i].data_buffer,
198 versaloon_buf + usbtoxxx_buffer_index
199 + versaloon_pending[i].want_data_pos,
200 versaloon_pending[i].want_data_size);
201 }
202 }
203 usbtoxxx_buffer_index += versaloon_pending[i].actual_data_size;
204 if (usbtoxxx_buffer_index > inlen) {
205 LOG_BUG("%s command 0x%02x process error",
206 usbtoxxx_get_type_name(versaloon_pending[i].type),
207 versaloon_pending[i].cmd);
208 result = ERROR_FAIL;
209 break;
210 }
211 }
212
213 /* data is not the right size */
214 if (inlen != usbtoxxx_buffer_index) {
215 LOG_ERROR(ERRMSG_INVALID_TARGET, "length of return data");
216 result = ERROR_FAIL;
217 }
218
219 if (versaloon_pending_idx > 0)
220 versaloon_pending_idx = 0;
221 else {
222 /* no receive data, avoid collision */
223 sleep_ms(10);
224 }
225
226 type_pre = 0;
227 collect_cmd = 0;
228 collect_index = 0;
229 versaloon_free_want_pos();
230 return result;
231 }
232
233 RESULT usbtoxxx_init(void)
234 {
235 versaloon_pending_idx = 0;
236
237 if ((usbtoinfo_get_abilities(usbtoxxx_abilities) != ERROR_OK) ||
238 (usbtoxxx_execute_command() != ERROR_OK))
239 return ERROR_FAIL;
240 LOG_INFO("USB_TO_XXX abilities: 0x%08X:0x%08X:0x%08X",
241 GET_LE_U32(&usbtoxxx_abilities[0]),
242 GET_LE_U32(&usbtoxxx_abilities[4]),
243 GET_LE_U32(&usbtoxxx_abilities[8]));
244 return ERROR_OK;
245 }
246
247 RESULT usbtoxxx_fini(void)
248 {
249 usbtoxxx_buffer = NULL;
250 type_pre = 0;
251 return ERROR_OK;
252 }
253
254 bool usbtoxxx_interface_supported(uint8_t cmd)
255 {
256 if ((cmd < VERSALOON_USB_TO_XXX_CMD_START) ||
257 (cmd > VERSALOON_USB_TO_XXX_CMD_END))
258 return false;
259
260 cmd -= VERSALOON_USB_TO_XXX_CMD_START;
261 return (usbtoxxx_abilities[cmd / 8] & (1 << (cmd % 8))) > 0;
262 }
263
264 static RESULT usbtoxxx_ensure_buffer_size(uint16_t cmdlen)
265 {
266 /* check free space, commit if not enough */
267 if (((usbtoxxx_buffer_index + usbtoxxx_current_cmd_index + cmdlen)
268 >= versaloon_buf_size)
269 || (versaloon_pending_idx >= VERSALOON_MAX_PENDING_NUMBER)) {
270 struct usbtoxxx_context_t context_tmp;
271 uint8_t poll_nesting_tmp = 0;
272
273 memset(&context_tmp, 0, sizeof(context_tmp));
274 if (poll_nesting) {
275 if (poll_context.type_pre == 0) {
276 LOG_BUG("USB_TO_POLL toooooo long");
277 return ERROR_OK;
278 }
279
280 usbtoxxx_save_context(&context_tmp);
281 usbtoxxx_pop_context(&poll_context);
282 poll_nesting_tmp = poll_nesting;
283 poll_nesting = 0;
284 }
285
286 if (usbtoxxx_execute_command() != ERROR_OK)
287 return ERROR_FAIL;
288
289 if (poll_nesting_tmp) {
290 uint16_t newlen, oldlen;
291
292 newlen = context_tmp.versaloon_pending_idx
293 - poll_context.versaloon_pending_idx;
294 memcpy(&versaloon_pending[0],
295 &versaloon_pending[poll_context.versaloon_pending_idx],
296 sizeof(versaloon_pending[0]) * newlen);
297 context_tmp.versaloon_pending_idx = newlen;
298 oldlen = poll_context.usbtoxxx_buffer_index
299 + poll_context.usbtoxxx_current_cmd_index;
300 newlen = context_tmp.usbtoxxx_buffer_index
301 + context_tmp.usbtoxxx_current_cmd_index;
302 memcpy(versaloon_buf + 3, versaloon_buf + oldlen, newlen - oldlen);
303 oldlen -= 3;
304 context_tmp.usbtoxxx_buffer -= oldlen;
305 context_tmp.usbtoxxx_buffer_index -= oldlen;
306 usbtoxxx_pop_context(&context_tmp);
307 poll_nesting = poll_nesting_tmp;
308 }
309 }
310 return ERROR_OK;
311 }
312
313 RESULT usbtoxxx_add_command(uint8_t type, uint8_t cmd, uint8_t *cmdbuf,
314 uint16_t cmdlen, uint16_t retlen, uint8_t *wantbuf,
315 uint16_t wantpos, uint16_t wantlen, uint8_t collect)
316 {
317 uint16_t len_tmp;
318
319 /* 3 more bytes by usbtoxxx_validate_current_command_type */
320 /* 3 more bytes when ((0 == collect_index) || (collect_cmd != cmd)) */
321 if (usbtoxxx_ensure_buffer_size(cmdlen + 6) != ERROR_OK)
322 return ERROR_FAIL;
323
324 if ((type_pre != type) || (!usbtoxxx_buffer)) {
325 if (usbtoxxx_validate_current_command_type() != ERROR_OK) {
326 LOG_BUG(ERRMSG_FAILURE_OPERATION, "validate previous commands");
327 return ERRCODE_FAILURE_OPERATION;
328 }
329 type_pre = type;
330 }
331
332 if ((collect_index == 0) || (collect_cmd != cmd)) {
333 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = cmd;
334
335 if (collect) {
336 collect_index = usbtoxxx_current_cmd_index;
337 collect_cmd = cmd;
338 } else {
339 collect_index = 0;
340 collect_cmd = 0;
341 }
342 SET_LE_U16(&usbtoxxx_buffer[usbtoxxx_current_cmd_index], cmdlen);
343 usbtoxxx_current_cmd_index += 2;
344 } else {
345 len_tmp = GET_LE_U16(&usbtoxxx_buffer[collect_index]) + cmdlen;
346 SET_LE_U16(&usbtoxxx_buffer[collect_index], len_tmp);
347 }
348
349 if (cmdbuf) {
350 memcpy(usbtoxxx_buffer + usbtoxxx_current_cmd_index, cmdbuf, cmdlen);
351 usbtoxxx_current_cmd_index += cmdlen;
352 }
353
354 return versaloon_add_pending(type, cmd, retlen, wantpos, wantlen,
355 wantbuf, collect);
356 }
357
358 RESULT usbtoinfo_get_abilities(uint8_t abilities[USB_TO_XXX_ABILITIES_LEN])
359 {
360 if (usbtoxxx_ensure_buffer_size(3) != ERROR_OK)
361 return ERROR_FAIL;
362
363 if (usbtoxxx_validate_current_command_type() != ERROR_OK) {
364 LOG_BUG(ERRMSG_FAILURE_OPERATION, "validate previous commands");
365 return ERRCODE_FAILURE_OPERATION;
366 }
367 type_pre = USB_TO_INFO;
368
369 return versaloon_add_pending(USB_TO_INFO, 0, USB_TO_XXX_ABILITIES_LEN, 0,
370 USB_TO_XXX_ABILITIES_LEN, abilities, 0);
371 }
372
373 RESULT usbtopoll_start(uint16_t retry_cnt, uint16_t interval_us)
374 {
375 if (usbtoxxx_ensure_buffer_size(3 + 5) != ERROR_OK)
376 return ERROR_FAIL;
377 if (!poll_nesting)
378 usbtoxxx_save_context(&poll_context);
379
380 if (usbtoxxx_validate_current_command_type() != ERROR_OK) {
381 LOG_BUG(ERRMSG_FAILURE_OPERATION, "validate previous commands");
382 return ERRCODE_FAILURE_OPERATION;
383 }
384 poll_nesting++;
385 type_pre = USB_TO_POLL;
386
387 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = USB_TO_POLL_START;
388 SET_LE_U16(&usbtoxxx_buffer[usbtoxxx_current_cmd_index], retry_cnt);
389 usbtoxxx_current_cmd_index += 2;
390 SET_LE_U16(&usbtoxxx_buffer[usbtoxxx_current_cmd_index], interval_us);
391 usbtoxxx_current_cmd_index += 2;
392
393 return versaloon_add_pending(USB_TO_POLL, 0, 0, 0, 0, NULL, 0);
394 }
395
396 RESULT usbtopoll_end(void)
397 {
398 if (!poll_nesting) {
399 LOG_BUG(ERRMSG_FAILURE_OPERATION, "check poll nesting");
400 return ERRCODE_FAILURE_OPERATION;
401 }
402 if (usbtoxxx_ensure_buffer_size(3 + 1) != ERROR_OK)
403 return ERROR_FAIL;
404
405 if (usbtoxxx_validate_current_command_type() != ERROR_OK) {
406 LOG_BUG(ERRMSG_FAILURE_OPERATION, "validate previous commands");
407 return ERRCODE_FAILURE_OPERATION;
408 }
409
410 poll_nesting--;
411 type_pre = USB_TO_POLL;
412
413 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = USB_TO_POLL_END;
414
415 return versaloon_add_pending(USB_TO_POLL, 0, 0, 0, 0, NULL, 0);
416 }
417
418 RESULT usbtopoll_checkok(uint8_t equ, uint16_t offset, uint8_t size,
419 uint32_t mask, uint32_t value)
420 {
421 uint8_t i;
422
423 if (size > 4) {
424 LOG_BUG(ERRMSG_INVALID_PARAMETER, __func__);
425 return ERRCODE_INVALID_PARAMETER;
426 }
427 if (!poll_nesting) {
428 LOG_BUG(ERRMSG_FAILURE_OPERATION, "check poll nesting");
429 return ERRCODE_FAILURE_OPERATION;
430 }
431 if (usbtoxxx_ensure_buffer_size(3 + 4 + 2 * size) != ERROR_OK)
432 return ERROR_FAIL;
433
434 if (usbtoxxx_validate_current_command_type() != ERROR_OK) {
435 LOG_BUG(ERRMSG_FAILURE_OPERATION, "validate previous commands");
436 return ERRCODE_FAILURE_OPERATION;
437 }
438
439 type_pre = USB_TO_POLL;
440
441 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = USB_TO_POLL_CHECKOK;
442 SET_LE_U16(&usbtoxxx_buffer[usbtoxxx_current_cmd_index], offset);
443 usbtoxxx_current_cmd_index += 2;
444 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = size;
445 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = equ;
446 for (i = 0; i < size; i++)
447 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = (mask >> (8 * i)) & 0xFF;
448 for (i = 0; i < size; i++)
449 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = (value >> (8 * i)) & 0xFF;
450
451 return ERROR_OK;
452 }
453
454 RESULT usbtopoll_checkfail(uint8_t equ, uint16_t offset, uint8_t size,
455 uint32_t mask, uint32_t value)
456 {
457 uint8_t i;
458
459 if (size > 4) {
460 LOG_BUG(ERRMSG_INVALID_PARAMETER, __func__);
461 return ERRCODE_INVALID_PARAMETER;
462 }
463 if (!poll_nesting) {
464 LOG_BUG(ERRMSG_FAILURE_OPERATION, "check poll nesting");
465 return ERRCODE_FAILURE_OPERATION;
466 }
467 if (usbtoxxx_ensure_buffer_size(3 + 4 + 2 * size) != ERROR_OK)
468 return ERROR_FAIL;
469
470 if (usbtoxxx_validate_current_command_type() != ERROR_OK) {
471 LOG_BUG(ERRMSG_FAILURE_OPERATION, "validate previous commands");
472 return ERRCODE_FAILURE_OPERATION;
473 }
474
475 type_pre = USB_TO_POLL;
476
477 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = USB_TO_POLL_CHECKFAIL;
478 SET_LE_U16(&usbtoxxx_buffer[usbtoxxx_current_cmd_index], offset);
479 usbtoxxx_current_cmd_index += 2;
480 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = size;
481 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = equ;
482 for (i = 0; i < size; i++)
483 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = (mask >> (8 * i)) & 0xFF;
484 for (i = 0; i < size; i++)
485 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = (value >> (8 * i)) & 0xFF;
486
487 return ERROR_OK;
488 }
489
490 RESULT usbtopoll_verifybuff(uint16_t offset, uint16_t size, uint8_t *buff)
491 {
492 if (!poll_nesting) {
493 LOG_BUG(ERRMSG_FAILURE_OPERATION, "check poll nesting");
494 return ERRCODE_FAILURE_OPERATION;
495 }
496 if (usbtoxxx_ensure_buffer_size(3 + 5 + size) != ERROR_OK)
497 return ERROR_FAIL;
498
499 if (usbtoxxx_validate_current_command_type() != ERROR_OK) {
500 LOG_BUG(ERRMSG_FAILURE_OPERATION, "validate previous commands");
501 return ERRCODE_FAILURE_OPERATION;
502 }
503
504 type_pre = USB_TO_POLL;
505
506 usbtoxxx_buffer[usbtoxxx_current_cmd_index++] = USB_TO_POLL_VERIFYBUFF;
507 SET_LE_U16(&usbtoxxx_buffer[usbtoxxx_current_cmd_index], offset);
508 usbtoxxx_current_cmd_index += 2;
509 SET_LE_U16(&usbtoxxx_buffer[usbtoxxx_current_cmd_index], size);
510 usbtoxxx_current_cmd_index += 2;
511 memcpy(&usbtoxxx_buffer[usbtoxxx_current_cmd_index], buff, size);
512 usbtoxxx_current_cmd_index += size;
513
514 return ERROR_OK;
515 }
516
517 RESULT usbtodelay_delay(uint16_t dly)
518 {
519 if (usbtoxxx_ensure_buffer_size(3 + 2) != ERROR_OK)
520 return ERROR_FAIL;
521
522 if (usbtoxxx_validate_current_command_type() != ERROR_OK) {
523 LOG_BUG(ERRMSG_FAILURE_OPERATION, "validate previous commands");
524 return ERRCODE_FAILURE_OPERATION;
525 }
526 type_pre = USB_TO_DELAY;
527
528 SET_LE_U16(&usbtoxxx_buffer[usbtoxxx_current_cmd_index], dly);
529 usbtoxxx_current_cmd_index += 2;
530
531 return versaloon_add_pending(USB_TO_DELAY, 0, 0, 0, 0, NULL, 0);
532 }
533
534 RESULT usbtodelay_delayms(uint16_t ms)
535 {
536 return usbtodelay_delay(ms | 0x8000);
537 }
538
539 RESULT usbtodelay_delayus(uint16_t us)
540 {
541 return usbtodelay_delay(us & 0x7FFF);
542 }

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)