1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
5 * Copyright (C) 2007,2008 Øyvind Harboe *
6 * oyvind.harboe@zylin.com *
8 * Copyright (C) 2008, Duane Ellis *
9 * openocd@duaneeellis.com *
11 * part of this file is taken from libcli (libcli.sourceforge.net) *
12 * Copyright (C) David Parrish (david@dparrish.com) *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
19 * This program is distributed in the hope that it will be useful, *
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
22 * GNU General Public License for more details. *
24 * You should have received a copy of the GNU General Public License *
25 * along with this program; if not, write to the *
26 * Free Software Foundation, Inc., *
27 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
28 ***************************************************************************/
34 /* see Embedder-HOWTO.txt in Jim Tcl project hosted on BerliOS*/
38 // @todo the inclusion of target.h here is a layering violation
41 #include "configuration.h"
43 #include "time_support.h"
44 #include "jim-eventloop.h"
47 Jim_Interp
*interp
= NULL
;
49 static int run_command(struct command_context
*context
,
50 struct command
*c
, const char *words
[], unsigned num_words
);
52 static void tcl_output(void *privData
, const char *file
, unsigned line
,
53 const char *function
, const char *string
)
55 Jim_Obj
*tclOutput
= (Jim_Obj
*)privData
;
56 Jim_AppendString(interp
, tclOutput
, string
, strlen(string
));
59 extern struct command_context
*global_cmd_ctx
;
61 void script_debug(Jim_Interp
*interp
, const char *name
,
62 unsigned argc
, Jim_Obj
*const *argv
)
64 LOG_DEBUG("command - %s", name
);
65 for (unsigned i
= 0; i
< argc
; i
++)
68 const char *w
= Jim_GetString(argv
[i
], &len
);
70 /* end of line comment? */
74 LOG_DEBUG("%s - argv[%d]=%s", name
, i
, w
);
78 static int script_command(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
80 /* the private data is stashed in the interp structure */
82 struct command_context
*context
;
88 /* DANGER!!!! be careful what we invoke here, since interp->cmdPrivData might
89 * get overwritten by running other Jim commands! Treat it as an
90 * emphemeral global variable that is used in lieu of an argument
91 * to the fn and fish it out manually.
93 c
= interp
->cmdPrivData
;
96 LOG_ERROR("BUG: interp->cmdPrivData == NULL");
99 target_call_timer_callbacks_now();
100 LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
102 script_debug(interp
, c
->name
, argc
, argv
);
104 words
= malloc(argc
* sizeof(char *));
105 for (i
= 0; i
< argc
; i
++)
108 const char *w
= Jim_GetString(argv
[i
], &len
);
111 /* hit an end of line comment */
114 words
[i
] = strdup(w
);
115 if (words
[i
] == NULL
)
118 for (j
= 0; j
< i
; j
++)
126 /* grab the command context from the associated data */
127 context
= Jim_GetAssocData(interp
, "context");
130 /* Tcl can invoke commands directly instead of via command_run_line(). This would
131 * happen when the Jim Tcl interpreter is provided by eCos.
133 context
= global_cmd_ctx
;
136 /* capture log output and return it */
137 Jim_Obj
*tclOutput
= Jim_NewStringObj(interp
, "", 0);
138 /* a garbage collect can happen, so we need a reference count to this object */
139 Jim_IncrRefCount(tclOutput
);
141 log_add_callback(tcl_output
, tclOutput
);
143 retval
= run_command(context
, c
, (const char **)words
, nwords
);
145 log_remove_callback(tcl_output
, tclOutput
);
147 /* We dump output into this local variable */
148 Jim_SetResult(interp
, tclOutput
);
149 Jim_DecrRefCount(interp
, tclOutput
);
151 for (i
= 0; i
< nwords
; i
++)
155 int *return_retval
= Jim_GetAssocData(interp
, "retval");
156 if (return_retval
!= NULL
)
158 *return_retval
= retval
;
161 return (retval
== ERROR_OK
)?JIM_OK
:JIM_ERR
;
164 static Jim_Obj
*command_name_list(struct command
*c
)
166 Jim_Obj
*cmd_list
= c
->parent
?
167 command_name_list(c
->parent
) :
168 Jim_NewListObj(interp
, NULL
, 0);
169 Jim_ListAppendElement(interp
, cmd_list
,
170 Jim_NewStringObj(interp
, c
->name
, -1));
175 static void command_helptext_add(Jim_Obj
*cmd_list
, const char *help
)
177 Jim_Obj
*cmd_entry
= Jim_NewListObj(interp
, NULL
, 0);
178 Jim_ListAppendElement(interp
, cmd_entry
, cmd_list
);
179 Jim_ListAppendElement(interp
, cmd_entry
,
180 Jim_NewStringObj(interp
, help
? : "", -1));
182 /* accumulate help text in Tcl helptext list. */
183 Jim_Obj
*helptext
= Jim_GetGlobalVariableStr(interp
,
184 "ocd_helptext", JIM_ERRMSG
);
185 if (Jim_IsShared(helptext
))
186 helptext
= Jim_DuplicateObj(interp
, helptext
);
187 Jim_ListAppendElement(interp
, helptext
, cmd_entry
);
190 /* nice short description of source file */
191 #define __THIS__FILE__ "command.c"
194 * Find a command by name from a list of commands.
195 * @returns The named command if found, or NULL.
197 static struct command
*command_find(struct command
**head
, const char *name
)
200 for (struct command
*cc
= *head
; cc
; cc
= cc
->next
)
202 if (strcmp(cc
->name
, name
) == 0)
209 * Add the command to the end of linked list.
210 * @returns Returns false if the named command already exists in the list.
211 * Returns true otherwise.
213 static void command_add_child(struct command
**head
, struct command
*c
)
221 struct command
*cc
= *head
;
222 while (cc
->next
) cc
= cc
->next
;
226 struct command
* register_command(struct command_context
*context
,
227 struct command
*parent
, char *name
, command_handler_t handler
,
228 enum command_mode mode
, char *help
)
230 if (!context
|| !name
)
233 struct command
**head
= parent
? &parent
->children
: &context
->commands
;
234 struct command
*c
= command_find(head
, name
);
238 c
= malloc(sizeof(struct command
));
240 c
->name
= strdup(name
);
243 c
->handler
= handler
;
247 command_add_child(head
, c
);
249 command_helptext_add(command_name_list(c
), help
);
251 /* just a placeholder, no handler */
252 if (c
->handler
== NULL
)
255 const char *full_name
= command_name(c
, '_');
257 const char *ocd_name
= alloc_printf("ocd_%s", full_name
);
258 Jim_CreateCommand(interp
, ocd_name
, script_command
, c
, NULL
);
259 free((void *)ocd_name
);
261 /* we now need to add an overrideable proc */
262 const char *override_name
= alloc_printf("proc %s {args} {"
263 "if {[catch {eval ocd_%s $args}] == 0} "
264 "{return \"\"} else {return -code error}}",
265 full_name
, full_name
);
266 Jim_Eval_Named(interp
, override_name
, __THIS__FILE__
, __LINE__
);
267 free((void *)override_name
);
269 free((void *)full_name
);
274 int unregister_all_commands(struct command_context
*context
)
276 struct command
*c
, *c2
;
281 while (NULL
!= context
->commands
)
283 c
= context
->commands
;
285 while (NULL
!= c
->children
)
288 c
->children
= c
->children
->next
;
295 context
->commands
= context
->commands
->next
;
306 int unregister_command(struct command_context
*context
, char *name
)
308 struct command
*c
, *p
= NULL
, *c2
;
310 if ((!context
) || (!name
))
311 return ERROR_INVALID_ARGUMENTS
;
314 c
= context
->commands
;
318 if (strcmp(name
, c
->name
) == 0)
327 /* first element in command list */
328 context
->commands
= c
->next
;
331 /* unregister children */
332 while (NULL
!= c
->children
)
335 c
->children
= c
->children
->next
;
350 /* remember the last command for unlinking */
358 void command_output_text(struct command_context
*context
, const char *data
)
360 if (context
&& context
->output_handler
&& data
) {
361 context
->output_handler(context
, data
);
365 void command_print_sameline(struct command_context
*context
, const char *format
, ...)
370 va_start(ap
, format
);
372 string
= alloc_vprintf(format
, ap
);
375 /* we want this collected in the log + we also want to pick it up as a tcl return
378 * The latter bit isn't precisely neat, but will do for now.
380 LOG_USER_N("%s", string
);
381 /* We already printed it above */
382 /* command_output_text(context, string); */
389 void command_print(struct command_context
*context
, const char *format
, ...)
394 va_start(ap
, format
);
396 string
= alloc_vprintf(format
, ap
);
399 strcat(string
, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
400 /* we want this collected in the log + we also want to pick it up as a tcl return
403 * The latter bit isn't precisely neat, but will do for now.
405 LOG_USER_N("%s", string
);
406 /* We already printed it above */
407 /* command_output_text(context, string); */
414 static char *__command_name(struct command
*c
, char delim
, unsigned extra
)
417 unsigned len
= strlen(c
->name
);
418 if (NULL
== c
->parent
) {
419 // allocate enough for the name, child names, and '\0'
420 name
= malloc(len
+ extra
+ 1);
421 strcpy(name
, c
->name
);
423 // parent's extra must include both the space and name
424 name
= __command_name(c
->parent
, delim
, 1 + len
+ extra
);
425 char dstr
[2] = { delim
, 0 };
427 strcat(name
, c
->name
);
431 char *command_name(struct command
*c
, char delim
)
433 return __command_name(c
, delim
, 0);
436 static int run_command(struct command_context
*context
,
437 struct command
*c
, const char *words
[], unsigned num_words
)
439 if (!((context
->mode
== COMMAND_CONFIG
) || (c
->mode
== COMMAND_ANY
) || (c
->mode
== context
->mode
)))
441 /* Config commands can not run after the config stage */
442 LOG_ERROR("Command '%s' only runs during configuration stage", c
->name
);
446 struct command_invocation cmd
= {
449 .argc
= num_words
- 1,
452 int retval
= c
->handler(&cmd
);
453 if (retval
== ERROR_COMMAND_SYNTAX_ERROR
)
455 /* Print help for command */
456 char *full_name
= command_name(c
, ' ');
457 if (NULL
!= full_name
) {
458 command_run_linef(context
, "help %s", full_name
);
463 else if (retval
== ERROR_COMMAND_CLOSE_CONNECTION
)
465 /* just fall through for a shutdown request */
467 else if (retval
!= ERROR_OK
)
469 /* we do not print out an error message because the command *should*
470 * have printed out an error
472 LOG_DEBUG("Command failed with error code %d", retval
);
478 int command_run_line(struct command_context
*context
, char *line
)
480 /* all the parent commands have been registered with the interpreter
481 * so, can just evaluate the line as a script and check for
484 /* run the line thru a script engine */
485 int retval
= ERROR_FAIL
;
487 /* Beware! This code needs to be reentrant. It is also possible
488 * for OpenOCD commands to be invoked directly from Tcl. This would
489 * happen when the Jim Tcl interpreter is provided by eCos for
492 Jim_DeleteAssocData(interp
, "context");
493 retcode
= Jim_SetAssocData(interp
, "context", NULL
, context
);
494 if (retcode
== JIM_OK
)
496 /* associated the return value */
497 Jim_DeleteAssocData(interp
, "retval");
498 retcode
= Jim_SetAssocData(interp
, "retval", NULL
, &retval
);
499 if (retcode
== JIM_OK
)
501 retcode
= Jim_Eval_Named(interp
, line
, __THIS__FILE__
, __LINE__
);
503 Jim_DeleteAssocData(interp
, "retval");
505 Jim_DeleteAssocData(interp
, "context");
507 if (retcode
== JIM_ERR
) {
508 if (retval
!= ERROR_COMMAND_CLOSE_CONNECTION
)
510 /* We do not print the connection closed error message */
511 Jim_PrintErrorMessage(interp
);
513 if (retval
== ERROR_OK
)
515 /* It wasn't a low level OpenOCD command that failed */
519 } else if (retcode
== JIM_EXIT
) {
521 /* exit(Jim_GetExitCode(interp)); */
526 result
= Jim_GetString(Jim_GetResult(interp
), &reslen
);
531 for (i
= 0; i
< reslen
; i
+= 256)
537 strncpy(buff
, result
+ i
, chunk
);
539 LOG_USER_N("%s", buff
);
541 LOG_USER_N("%s", "\n");
548 int command_run_linef(struct command_context
*context
, const char *format
, ...)
550 int retval
= ERROR_FAIL
;
553 va_start(ap
, format
);
554 string
= alloc_vprintf(format
, ap
);
557 retval
= command_run_line(context
, string
);
563 void command_set_output_handler(struct command_context
* context
,
564 command_output_handler_t output_handler
, void *priv
)
566 context
->output_handler
= output_handler
;
567 context
->output_handler_priv
= priv
;
570 struct command_context
* copy_command_context(struct command_context
* context
)
572 struct command_context
* copy_context
= malloc(sizeof(struct command_context
));
574 *copy_context
= *context
;
579 int command_done(struct command_context
*context
)
587 /* find full path to file */
588 static int jim_find(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
592 const char *file
= Jim_GetString(argv
[1], NULL
);
593 char *full_path
= find_file(file
);
594 if (full_path
== NULL
)
596 Jim_Obj
*result
= Jim_NewStringObj(interp
, full_path
, strlen(full_path
));
599 Jim_SetResult(interp
, result
);
603 static int jim_echo(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
607 const char *str
= Jim_GetString(argv
[1], NULL
);
612 static size_t openocd_jim_fwrite(const void *_ptr
, size_t size
, size_t n
, void *cookie
)
618 /* make it a char easier to read code */
622 if (ptr
== NULL
|| interp
== NULL
|| nbytes
== 0) {
626 /* do we have to chunk it? */
627 if (ptr
[nbytes
] == 0)
629 /* no it is a C style string */
630 LOG_USER_N("%s", ptr
);
633 /* GRR we must chunk - not null terminated */
643 memcpy(chunk
, ptr
, x
);
647 LOG_USER_N("%s", chunk
);
655 static size_t openocd_jim_fread(void *ptr
, size_t size
, size_t n
, void *cookie
)
657 /* TCL wants to read... tell him no */
661 static int openocd_jim_vfprintf(void *cookie
, const char *fmt
, va_list ap
)
672 cp
= alloc_vprintf(fmt
, ap
);
675 LOG_USER_N("%s", cp
);
682 static int openocd_jim_fflush(void *cookie
)
684 /* nothing to flush */
688 static char* openocd_jim_fgets(char *s
, int size
, void *cookie
)
695 static int jim_capture(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
700 const char *str
= Jim_GetString(argv
[1], NULL
);
702 /* capture log output and return it */
703 Jim_Obj
*tclOutput
= Jim_NewStringObj(interp
, "", 0);
704 /* a garbage collect can happen, so we need a reference count to this object */
705 Jim_IncrRefCount(tclOutput
);
707 log_add_callback(tcl_output
, tclOutput
);
709 retcode
= Jim_Eval_Named(interp
, str
, __THIS__FILE__
, __LINE__
);
711 log_remove_callback(tcl_output
, tclOutput
);
713 /* We dump output into this local variable */
714 Jim_SetResult(interp
, tclOutput
);
715 Jim_DecrRefCount(interp
, tclOutput
);
720 /* sleep command sleeps for <n> miliseconds
721 * this is useful in target startup scripts
723 COMMAND_HANDLER(handle_sleep_command
)
728 if (strcmp(CMD_ARGV
[1], "busy") == 0)
731 return ERROR_COMMAND_SYNTAX_ERROR
;
733 else if (CMD_ARGC
< 1 || CMD_ARGC
> 2)
734 return ERROR_COMMAND_SYNTAX_ERROR
;
736 unsigned long duration
= 0;
737 int retval
= parse_ulong(CMD_ARGV
[0], &duration
);
738 if (ERROR_OK
!= retval
)
743 long long then
= timeval_ms();
744 while (timeval_ms() - then
< (long long)duration
)
746 target_call_timer_callbacks_now();
751 busy_sleep(duration
);
756 struct command_context
* command_init(const char *startup_tcl
)
758 struct command_context
* context
= malloc(sizeof(struct command_context
));
761 context
->mode
= COMMAND_EXEC
;
762 context
->commands
= NULL
;
763 context
->current_target
= 0;
764 context
->output_handler
= NULL
;
765 context
->output_handler_priv
= NULL
;
769 /* Create an interpreter */
770 interp
= Jim_CreateInterp();
771 /* Add all the Jim core commands */
772 Jim_RegisterCoreCommands(interp
);
775 #if defined(_MSC_VER)
776 /* WinXX - is generic, the forward
777 * looking problem is this:
781 * "winxx" is generic.
784 #elif defined(__linux__)
786 #elif defined(__DARWIN__)
788 #elif defined(__CYGWIN__)
790 #elif defined(__MINGW32__)
792 #elif defined(__ECOS)
795 #warn unrecognized host OS...
798 Jim_SetGlobalVariableStr(interp
, "ocd_HOSTOS",
799 Jim_NewStringObj(interp
, HostOs
, strlen(HostOs
)));
801 Jim_CreateCommand(interp
, "ocd_find", jim_find
, NULL
, NULL
);
802 Jim_CreateCommand(interp
, "echo", jim_echo
, NULL
, NULL
);
803 Jim_CreateCommand(interp
, "capture", jim_capture
, NULL
, NULL
);
805 /* Set Jim's STDIO */
806 interp
->cookie_stdin
= interp
;
807 interp
->cookie_stdout
= interp
;
808 interp
->cookie_stderr
= interp
;
809 interp
->cb_fwrite
= openocd_jim_fwrite
;
810 interp
->cb_fread
= openocd_jim_fread
;
811 interp
->cb_vfprintf
= openocd_jim_vfprintf
;
812 interp
->cb_fflush
= openocd_jim_fflush
;
813 interp
->cb_fgets
= openocd_jim_fgets
;
816 Jim_EventLoopOnLoad(interp
);
818 if (Jim_Eval_Named(interp
, startup_tcl
, "embedded:startup.tcl",1) == JIM_ERR
)
820 LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD)");
821 Jim_PrintErrorMessage(interp
);
825 register_command(context
, NULL
, "sleep",
826 handle_sleep_command
, COMMAND_ANY
,
827 "<n> [busy] - sleep for n milliseconds. "
828 "\"busy\" means busy wait");
833 int command_context_mode(struct command_context
*cmd_ctx
, enum command_mode mode
)
836 return ERROR_INVALID_ARGUMENTS
;
838 cmd_ctx
->mode
= mode
;
842 void process_jim_events(void)
845 static int recursion
= 0;
850 Jim_ProcessEvents (interp
, JIM_ALL_EVENTS
| JIM_DONT_WAIT
);
856 void register_jim(struct command_context
*cmd_ctx
, const char *name
,
857 Jim_CmdProc cmd
, const char *help
)
859 Jim_CreateCommand(interp
, name
, cmd
, NULL
, NULL
);
861 Jim_Obj
*cmd_list
= Jim_NewListObj(interp
, NULL
, 0);
862 Jim_ListAppendElement(interp
, cmd_list
,
863 Jim_NewStringObj(interp
, name
, -1));
865 command_helptext_add(cmd_list
, help
);
868 #define DEFINE_PARSE_NUM_TYPE(name, type, func, min, max) \
869 int parse##name(const char *str, type *ul) \
873 LOG_ERROR("Invalid command argument"); \
874 return ERROR_COMMAND_ARGUMENT_INVALID; \
877 *ul = func(str, &end, 0); \
880 LOG_ERROR("Invalid command argument"); \
881 return ERROR_COMMAND_ARGUMENT_INVALID; \
883 if ((max == *ul) && (ERANGE == errno)) \
885 LOG_ERROR("Argument overflow"); \
886 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
888 if (min && (min == *ul) && (ERANGE == errno)) \
890 LOG_ERROR("Argument underflow"); \
891 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
895 DEFINE_PARSE_NUM_TYPE(_ulong
, unsigned long , strtoul
, 0, ULONG_MAX
)
896 DEFINE_PARSE_NUM_TYPE(_ullong
, unsigned long long, strtoull
, 0, ULLONG_MAX
)
897 DEFINE_PARSE_NUM_TYPE(_long
, long , strtol
, LONG_MIN
, LONG_MAX
)
898 DEFINE_PARSE_NUM_TYPE(_llong
, long long, strtoll
, LLONG_MIN
, LLONG_MAX
)
900 #define DEFINE_PARSE_WRAPPER(name, type, min, max, functype, funcname) \
901 int parse##name(const char *str, type *ul) \
904 int retval = parse##funcname(str, &n); \
905 if (ERROR_OK != retval) \
908 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
910 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
915 #define DEFINE_PARSE_ULONG(name, type, min, max) \
916 DEFINE_PARSE_WRAPPER(name, type, min, max, unsigned long, _ulong)
917 DEFINE_PARSE_ULONG(_uint
, unsigned, 0, UINT_MAX
)
918 DEFINE_PARSE_ULONG(_u32
, uint32_t, 0, UINT32_MAX
)
919 DEFINE_PARSE_ULONG(_u16
, uint16_t, 0, UINT16_MAX
)
920 DEFINE_PARSE_ULONG(_u8
, uint8_t, 0, UINT8_MAX
)
922 #define DEFINE_PARSE_LONG(name, type, min, max) \
923 DEFINE_PARSE_WRAPPER(name, type, min, max, long, _long)
924 DEFINE_PARSE_LONG(_int
, int, n
< INT_MIN
, INT_MAX
)
925 DEFINE_PARSE_LONG(_s32
, int32_t, n
< INT32_MIN
, INT32_MAX
)
926 DEFINE_PARSE_LONG(_s16
, int16_t, n
< INT16_MIN
, INT16_MAX
)
927 DEFINE_PARSE_LONG(_s8
, int8_t, n
< INT8_MIN
, INT8_MAX
)
929 static int command_parse_bool(const char *in
, bool *out
,
930 const char *on
, const char *off
)
932 if (strcasecmp(in
, on
) == 0)
934 else if (strcasecmp(in
, off
) == 0)
937 return ERROR_COMMAND_SYNTAX_ERROR
;
941 int command_parse_bool_arg(const char *in
, bool *out
)
943 if (command_parse_bool(in
, out
, "on", "off") == ERROR_OK
)
945 if (command_parse_bool(in
, out
, "enable", "disable") == ERROR_OK
)
947 if (command_parse_bool(in
, out
, "true", "false") == ERROR_OK
)
949 if (command_parse_bool(in
, out
, "yes", "no") == ERROR_OK
)
951 if (command_parse_bool(in
, out
, "1", "0") == ERROR_OK
)
953 return ERROR_INVALID_ARGUMENTS
;
956 COMMAND_HELPER(handle_command_parse_bool
, bool *out
, const char *label
)
960 const char *in
= CMD_ARGV
[0];
961 if (command_parse_bool_arg(in
, out
) != ERROR_OK
)
963 LOG_ERROR("%s: argument '%s' is not valid", CMD_NAME
, in
);
964 return ERROR_INVALID_ARGUMENTS
;
969 LOG_INFO("%s is %s", label
, *out
? "enabled" : "disabled");
972 return ERROR_INVALID_ARGUMENTS
;
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)