1 // SPDX-License-Identifier: GPL-2.0-or-later
3 /***************************************************************************
4 * Copyright (C) 2010 Øyvind Harboe *
5 * oyvind.harboe@zylin.com *
6 ***************************************************************************/
12 #include "tcl_server.h"
13 #include <target/target.h>
14 #include <helper/binarybuffer.h>
16 #define TCL_SERVER_VERSION "TCL Server 0.1"
17 #define TCL_LINE_INITIAL (4*1024)
18 #define TCL_LINE_MAX (4*1024*1024)
20 struct tcl_connection
{
25 int tc_outerror
;/* flag an output error */
26 enum target_state tc_laststate
;
31 static char *tcl_port
;
34 static int tcl_new_connection(struct connection
*connection
);
35 static int tcl_input(struct connection
*connection
);
36 static int tcl_output(struct connection
*connection
, const void *buf
, ssize_t len
);
37 static int tcl_closed(struct connection
*connection
);
39 static int tcl_target_callback_event_handler(struct target
*target
,
40 enum target_event event
, void *priv
)
42 struct connection
*connection
= priv
;
43 struct tcl_connection
*tclc
;
46 tclc
= connection
->priv
;
48 if (tclc
->tc_notify
) {
49 snprintf(buf
, sizeof(buf
), "type target_event event %s\r\n\x1a", target_event_name(event
));
50 tcl_output(connection
, buf
, strlen(buf
));
53 if (tclc
->tc_laststate
!= target
->state
) {
54 tclc
->tc_laststate
= target
->state
;
55 if (tclc
->tc_notify
) {
56 snprintf(buf
, sizeof(buf
), "type target_state state %s\r\n\x1a", target_state_name(target
));
57 tcl_output(connection
, buf
, strlen(buf
));
64 static int tcl_target_callback_reset_handler(struct target
*target
,
65 enum target_reset_mode reset_mode
, void *priv
)
67 struct connection
*connection
= priv
;
68 struct tcl_connection
*tclc
;
71 tclc
= connection
->priv
;
73 if (tclc
->tc_notify
) {
74 snprintf(buf
, sizeof(buf
), "type target_reset mode %s\r\n\x1a", target_reset_mode_name(reset_mode
));
75 tcl_output(connection
, buf
, strlen(buf
));
81 static int tcl_target_callback_trace_handler(struct target
*target
,
82 size_t len
, uint8_t *data
, void *priv
)
84 struct connection
*connection
= priv
;
85 struct tcl_connection
*tclc
;
86 char *header
= "type target_trace data ";
87 char *trailer
= "\r\n\x1a";
88 size_t hex_len
= len
* 2 + 1;
89 size_t max_len
= hex_len
+ strlen(header
) + strlen(trailer
);
92 tclc
= connection
->priv
;
95 hex
= malloc(hex_len
);
96 buf
= malloc(max_len
);
97 hexify(hex
, data
, len
, hex_len
);
98 snprintf(buf
, max_len
, "%s%s%s", header
, hex
, trailer
);
99 tcl_output(connection
, buf
, strlen(buf
));
107 /* write data out to a socket.
109 * this is a blocking write, so the return value must equal the length, if
110 * that is not the case then flag the connection with an output error.
112 int tcl_output(struct connection
*connection
, const void *data
, ssize_t len
)
115 struct tcl_connection
*tclc
;
117 tclc
= connection
->priv
;
118 if (tclc
->tc_outerror
)
119 return ERROR_SERVER_REMOTE_CLOSED
;
121 wlen
= connection_write(connection
, data
, len
);
126 LOG_ERROR("error during write: %d != %d", (int)wlen
, (int)len
);
127 tclc
->tc_outerror
= 1;
128 return ERROR_SERVER_REMOTE_CLOSED
;
132 static int tcl_new_connection(struct connection
*connection
)
134 struct tcl_connection
*tclc
;
136 tclc
= calloc(1, sizeof(struct tcl_connection
));
138 return ERROR_CONNECTION_REJECTED
;
140 tclc
->tc_line_size
= TCL_LINE_INITIAL
;
141 tclc
->tc_line
= malloc(tclc
->tc_line_size
);
142 if (!tclc
->tc_line
) {
144 return ERROR_CONNECTION_REJECTED
;
147 connection
->priv
= tclc
;
149 struct target
*target
= get_current_target_or_null(connection
->cmd_ctx
);
151 tclc
->tc_laststate
= target
->state
;
153 /* store the connection object on cmd_ctx so we can access it from command handlers */
154 connection
->cmd_ctx
->output_handler_priv
= connection
;
156 target_register_event_callback(tcl_target_callback_event_handler
, connection
);
157 target_register_reset_callback(tcl_target_callback_reset_handler
, connection
);
158 target_register_trace_callback(tcl_target_callback_trace_handler
, connection
);
163 static int tcl_input(struct connection
*connection
)
165 Jim_Interp
*interp
= (Jim_Interp
*)connection
->cmd_ctx
->interp
;
171 struct tcl_connection
*tclc
;
172 unsigned char in
[256];
174 int tc_line_size_new
;
176 rlen
= connection_read(connection
, &in
, sizeof(in
));
179 LOG_ERROR("error during read: %s", strerror(errno
));
180 return ERROR_SERVER_REMOTE_CLOSED
;
183 tclc
= connection
->priv
;
185 return ERROR_CONNECTION_REJECTED
;
187 /* push as much data into the line as possible */
188 for (i
= 0; i
< rlen
; i
++) {
189 /* buffer the data */
190 tclc
->tc_line
[tclc
->tc_lineoffset
] = in
[i
];
191 if (tclc
->tc_lineoffset
+ 1 < tclc
->tc_line_size
) {
192 tclc
->tc_lineoffset
++;
193 } else if (tclc
->tc_line_size
>= TCL_LINE_MAX
) {
194 /* maximum line size reached, drop line */
195 tclc
->tc_linedrop
= 1;
197 /* grow line buffer: exponential below 1 MB, linear above */
198 if (tclc
->tc_line_size
<= 1*1024*1024)
199 tc_line_size_new
= tclc
->tc_line_size
* 2;
201 tc_line_size_new
= tclc
->tc_line_size
+ 1*1024*1024;
203 if (tc_line_size_new
> TCL_LINE_MAX
)
204 tc_line_size_new
= TCL_LINE_MAX
;
206 tc_line_new
= realloc(tclc
->tc_line
, tc_line_size_new
);
208 tclc
->tc_linedrop
= 1;
210 tclc
->tc_line
= tc_line_new
;
211 tclc
->tc_line_size
= tc_line_size_new
;
212 tclc
->tc_lineoffset
++;
217 /* ctrl-z is end of command. When testing from telnet, just
218 * press ctrl-z a couple of times first to put telnet into the
219 * mode where it will send 0x1a in response to pressing ctrl-z
224 /* process the line */
225 if (tclc
->tc_linedrop
) {
226 #define ESTR "line too long\n"
227 retval
= tcl_output(connection
, ESTR
, sizeof(ESTR
));
228 if (retval
!= ERROR_OK
)
232 tclc
->tc_line
[tclc
->tc_lineoffset
-1] = '\0';
233 command_run_line(connection
->cmd_ctx
, tclc
->tc_line
);
234 result
= Jim_GetString(Jim_GetResult(interp
), &reslen
);
235 retval
= tcl_output(connection
, result
, reslen
);
236 if (retval
!= ERROR_OK
)
238 /* Always output ctrl-z as end of line to allow multiline results */
239 tcl_output(connection
, "\x1a", 1);
242 tclc
->tc_lineoffset
= 0;
243 tclc
->tc_linedrop
= 0;
249 static int tcl_closed(struct connection
*connection
)
251 struct tcl_connection
*tclc
;
252 tclc
= connection
->priv
;
254 /* cleanup connection context */
258 connection
->priv
= NULL
;
261 target_unregister_event_callback(tcl_target_callback_event_handler
, connection
);
262 target_unregister_reset_callback(tcl_target_callback_reset_handler
, connection
);
263 target_unregister_trace_callback(tcl_target_callback_trace_handler
, connection
);
268 static const struct service_driver tcl_service_driver
= {
270 .new_connection_during_keep_alive_handler
= NULL
,
271 .new_connection_handler
= tcl_new_connection
,
272 .input_handler
= tcl_input
,
273 .connection_closed_handler
= tcl_closed
,
274 .keep_client_alive_handler
= NULL
,
279 if (strcmp(tcl_port
, "disabled") == 0) {
280 LOG_INFO("tcl server disabled");
284 return add_service(&tcl_service_driver
, tcl_port
, CONNECTION_LIMIT_UNLIMITED
, NULL
);
287 COMMAND_HANDLER(handle_tcl_port_command
)
289 return CALL_COMMAND_HANDLER(server_pipe_command
, &tcl_port
);
292 COMMAND_HANDLER(handle_tcl_notifications_command
)
294 struct connection
*connection
= NULL
;
295 struct tcl_connection
*tclc
= NULL
;
297 if (CMD_CTX
->output_handler_priv
)
298 connection
= CMD_CTX
->output_handler_priv
;
300 if (connection
&& !strcmp(connection
->service
->name
, "tcl")) {
301 tclc
= connection
->priv
;
302 return CALL_COMMAND_HANDLER(handle_command_parse_bool
, &tclc
->tc_notify
, "Target Notification output ");
304 LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME
);
305 return ERROR_COMMAND_SYNTAX_ERROR
;
309 COMMAND_HANDLER(handle_tcl_trace_command
)
311 struct connection
*connection
= NULL
;
312 struct tcl_connection
*tclc
= NULL
;
314 if (CMD_CTX
->output_handler_priv
)
315 connection
= CMD_CTX
->output_handler_priv
;
317 if (connection
&& !strcmp(connection
->service
->name
, "tcl")) {
318 tclc
= connection
->priv
;
319 return CALL_COMMAND_HANDLER(handle_command_parse_bool
, &tclc
->tc_trace
, "Target trace output ");
321 LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME
);
322 return ERROR_COMMAND_SYNTAX_ERROR
;
326 static const struct command_registration tcl_command_handlers
[] = {
329 .handler
= handle_tcl_port_command
,
330 .mode
= COMMAND_CONFIG
,
331 .help
= "Specify port on which to listen "
332 "for incoming Tcl syntax. "
333 "Read help on 'gdb_port'.",
334 .usage
= "[port_num]",
337 .name
= "tcl_notifications",
338 .handler
= handle_tcl_notifications_command
,
339 .mode
= COMMAND_EXEC
,
340 .help
= "Target Notification output",
345 .handler
= handle_tcl_trace_command
,
346 .mode
= COMMAND_EXEC
,
347 .help
= "Target trace output",
350 COMMAND_REGISTRATION_DONE
353 int tcl_register_commands(struct command_context
*cmd_ctx
)
355 tcl_port
= strdup("6666");
356 return register_commands(cmd_ctx
, NULL
, tcl_command_handlers
);
359 void tcl_service_free(void)
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)