1 /***************************************************************************
2 * Copyright (C) 2010 Øyvind Harboe *
3 * oyvind.harboe@zylin.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 ***************************************************************************/
23 #include "tcl_server.h"
24 #include <target/target.h>
25 #include <helper/binarybuffer.h>
27 #define TCL_SERVER_VERSION "TCL Server 0.1"
28 #define TCL_LINE_INITIAL (4*1024)
29 #define TCL_LINE_MAX (4*1024*1024)
31 struct tcl_connection
{
36 int tc_outerror
;/* flag an output error */
37 enum target_state tc_laststate
;
42 static char *tcl_port
;
45 static int tcl_new_connection(struct connection
*connection
);
46 static int tcl_input(struct connection
*connection
);
47 static int tcl_output(struct connection
*connection
, const void *buf
, ssize_t len
);
48 static int tcl_closed(struct connection
*connection
);
50 static int tcl_target_callback_event_handler(struct target
*target
,
51 enum target_event event
, void *priv
)
53 struct connection
*connection
= priv
;
54 struct tcl_connection
*tclc
;
57 tclc
= connection
->priv
;
59 if (tclc
->tc_notify
) {
60 snprintf(buf
, sizeof(buf
), "type target_event event %s\r\n\x1a", target_event_name(event
));
61 tcl_output(connection
, buf
, strlen(buf
));
64 if (tclc
->tc_laststate
!= target
->state
) {
65 tclc
->tc_laststate
= target
->state
;
66 if (tclc
->tc_notify
) {
67 snprintf(buf
, sizeof(buf
), "type target_state state %s\r\n\x1a", target_state_name(target
));
68 tcl_output(connection
, buf
, strlen(buf
));
75 static int tcl_target_callback_reset_handler(struct target
*target
,
76 enum target_reset_mode reset_mode
, void *priv
)
78 struct connection
*connection
= priv
;
79 struct tcl_connection
*tclc
;
82 tclc
= connection
->priv
;
84 if (tclc
->tc_notify
) {
85 snprintf(buf
, sizeof(buf
), "type target_reset mode %s\r\n\x1a", target_reset_mode_name(reset_mode
));
86 tcl_output(connection
, buf
, strlen(buf
));
92 static int tcl_target_callback_trace_handler(struct target
*target
,
93 size_t len
, uint8_t *data
, void *priv
)
95 struct connection
*connection
= priv
;
96 struct tcl_connection
*tclc
;
97 char *header
= "type target_trace data ";
98 char *trailer
= "\r\n\x1a";
99 size_t hex_len
= len
* 2 + 1;
100 size_t max_len
= hex_len
+ strlen(header
) + strlen(trailer
);
103 tclc
= connection
->priv
;
105 if (tclc
->tc_trace
) {
106 hex
= malloc(hex_len
);
107 buf
= malloc(max_len
);
108 hexify(hex
, data
, len
, hex_len
);
109 snprintf(buf
, max_len
, "%s%s%s", header
, hex
, trailer
);
110 tcl_output(connection
, buf
, strlen(buf
));
118 /* write data out to a socket.
120 * this is a blocking write, so the return value must equal the length, if
121 * that is not the case then flag the connection with an output error.
123 int tcl_output(struct connection
*connection
, const void *data
, ssize_t len
)
126 struct tcl_connection
*tclc
;
128 tclc
= connection
->priv
;
129 if (tclc
->tc_outerror
)
130 return ERROR_SERVER_REMOTE_CLOSED
;
132 wlen
= connection_write(connection
, data
, len
);
137 LOG_ERROR("error during write: %d != %d", (int)wlen
, (int)len
);
138 tclc
->tc_outerror
= 1;
139 return ERROR_SERVER_REMOTE_CLOSED
;
143 static int tcl_new_connection(struct connection
*connection
)
145 struct tcl_connection
*tclc
;
147 tclc
= calloc(1, sizeof(struct tcl_connection
));
149 return ERROR_CONNECTION_REJECTED
;
151 tclc
->tc_line_size
= TCL_LINE_INITIAL
;
152 tclc
->tc_line
= malloc(tclc
->tc_line_size
);
153 if (!tclc
->tc_line
) {
155 return ERROR_CONNECTION_REJECTED
;
158 connection
->priv
= tclc
;
160 struct target
*target
= get_current_target_or_null(connection
->cmd_ctx
);
162 tclc
->tc_laststate
= target
->state
;
164 /* store the connection object on cmd_ctx so we can access it from command handlers */
165 connection
->cmd_ctx
->output_handler_priv
= connection
;
167 target_register_event_callback(tcl_target_callback_event_handler
, connection
);
168 target_register_reset_callback(tcl_target_callback_reset_handler
, connection
);
169 target_register_trace_callback(tcl_target_callback_trace_handler
, connection
);
174 static int tcl_input(struct connection
*connection
)
176 Jim_Interp
*interp
= (Jim_Interp
*)connection
->cmd_ctx
->interp
;
182 struct tcl_connection
*tclc
;
183 unsigned char in
[256];
185 int tc_line_size_new
;
187 rlen
= connection_read(connection
, &in
, sizeof(in
));
190 LOG_ERROR("error during read: %s", strerror(errno
));
191 return ERROR_SERVER_REMOTE_CLOSED
;
194 tclc
= connection
->priv
;
196 return ERROR_CONNECTION_REJECTED
;
198 /* push as much data into the line as possible */
199 for (i
= 0; i
< rlen
; i
++) {
200 /* buffer the data */
201 tclc
->tc_line
[tclc
->tc_lineoffset
] = in
[i
];
202 if (tclc
->tc_lineoffset
+ 1 < tclc
->tc_line_size
) {
203 tclc
->tc_lineoffset
++;
204 } else if (tclc
->tc_line_size
>= TCL_LINE_MAX
) {
205 /* maximum line size reached, drop line */
206 tclc
->tc_linedrop
= 1;
208 /* grow line buffer: exponential below 1 MB, linear above */
209 if (tclc
->tc_line_size
<= 1*1024*1024)
210 tc_line_size_new
= tclc
->tc_line_size
* 2;
212 tc_line_size_new
= tclc
->tc_line_size
+ 1*1024*1024;
214 if (tc_line_size_new
> TCL_LINE_MAX
)
215 tc_line_size_new
= TCL_LINE_MAX
;
217 tc_line_new
= realloc(tclc
->tc_line
, tc_line_size_new
);
219 tclc
->tc_linedrop
= 1;
221 tclc
->tc_line
= tc_line_new
;
222 tclc
->tc_line_size
= tc_line_size_new
;
223 tclc
->tc_lineoffset
++;
228 /* ctrl-z is end of command. When testing from telnet, just
229 * press ctrl-z a couple of times first to put telnet into the
230 * mode where it will send 0x1a in response to pressing ctrl-z
235 /* process the line */
236 if (tclc
->tc_linedrop
) {
237 #define ESTR "line too long\n"
238 retval
= tcl_output(connection
, ESTR
, sizeof(ESTR
));
239 if (retval
!= ERROR_OK
)
243 tclc
->tc_line
[tclc
->tc_lineoffset
-1] = '\0';
244 command_run_line(connection
->cmd_ctx
, tclc
->tc_line
);
245 result
= Jim_GetString(Jim_GetResult(interp
), &reslen
);
246 retval
= tcl_output(connection
, result
, reslen
);
247 if (retval
!= ERROR_OK
)
249 /* Always output ctrl-z as end of line to allow multiline results */
250 tcl_output(connection
, "\x1a", 1);
253 tclc
->tc_lineoffset
= 0;
254 tclc
->tc_linedrop
= 0;
260 static int tcl_closed(struct connection
*connection
)
262 struct tcl_connection
*tclc
;
263 tclc
= connection
->priv
;
265 /* cleanup connection context */
269 connection
->priv
= NULL
;
272 target_unregister_event_callback(tcl_target_callback_event_handler
, connection
);
273 target_unregister_reset_callback(tcl_target_callback_reset_handler
, connection
);
274 target_unregister_trace_callback(tcl_target_callback_trace_handler
, connection
);
281 if (strcmp(tcl_port
, "disabled") == 0) {
282 LOG_INFO("tcl server disabled");
286 return add_service("tcl", tcl_port
, CONNECTION_LIMIT_UNLIMITED
,
287 &tcl_new_connection
, &tcl_input
,
291 COMMAND_HANDLER(handle_tcl_port_command
)
293 return CALL_COMMAND_HANDLER(server_pipe_command
, &tcl_port
);
296 COMMAND_HANDLER(handle_tcl_notifications_command
)
298 struct connection
*connection
= NULL
;
299 struct tcl_connection
*tclc
= NULL
;
301 if (CMD_CTX
->output_handler_priv
)
302 connection
= CMD_CTX
->output_handler_priv
;
304 if (connection
&& !strcmp(connection
->service
->name
, "tcl")) {
305 tclc
= connection
->priv
;
306 return CALL_COMMAND_HANDLER(handle_command_parse_bool
, &tclc
->tc_notify
, "Target Notification output ");
308 LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME
);
309 return ERROR_COMMAND_SYNTAX_ERROR
;
313 COMMAND_HANDLER(handle_tcl_trace_command
)
315 struct connection
*connection
= NULL
;
316 struct tcl_connection
*tclc
= NULL
;
318 if (CMD_CTX
->output_handler_priv
)
319 connection
= CMD_CTX
->output_handler_priv
;
321 if (connection
&& !strcmp(connection
->service
->name
, "tcl")) {
322 tclc
= connection
->priv
;
323 return CALL_COMMAND_HANDLER(handle_command_parse_bool
, &tclc
->tc_trace
, "Target trace output ");
325 LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME
);
326 return ERROR_COMMAND_SYNTAX_ERROR
;
330 static const struct command_registration tcl_command_handlers
[] = {
333 .handler
= handle_tcl_port_command
,
334 .mode
= COMMAND_CONFIG
,
335 .help
= "Specify port on which to listen "
336 "for incoming Tcl syntax. "
337 "Read help on 'gdb_port'.",
338 .usage
= "[port_num]",
341 .name
= "tcl_notifications",
342 .handler
= handle_tcl_notifications_command
,
343 .mode
= COMMAND_EXEC
,
344 .help
= "Target Notification output",
349 .handler
= handle_tcl_trace_command
,
350 .mode
= COMMAND_EXEC
,
351 .help
= "Target trace output",
354 COMMAND_REGISTRATION_DONE
357 int tcl_register_commands(struct command_context
*cmd_ctx
)
359 tcl_port
= strdup("6666");
360 return register_commands(cmd_ctx
, NULL
, tcl_command_handlers
);
363 void tcl_service_free(void)