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)