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, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
19 ***************************************************************************/
25 #include "tcl_server.h"
26 #include <target/target.h>
28 #define TCL_SERVER_VERSION "TCL Server 0.1"
29 #define TCL_MAX_LINE (4096)
31 struct tcl_connection
{
34 char tc_line
[TCL_MAX_LINE
];
35 int tc_outerror
;/* flag an output error */
36 enum target_state tc_laststate
;
40 static char *tcl_port
;
43 static int tcl_new_connection(struct connection
*connection
);
44 static int tcl_input(struct connection
*connection
);
45 static int tcl_output(struct connection
*connection
, const void *buf
, ssize_t len
);
46 static int tcl_closed(struct connection
*connection
);
48 static int tcl_target_callback_event_handler(struct target
*target
,
49 enum target_event event
, void *priv
)
51 struct connection
*connection
= priv
;
52 struct tcl_connection
*tclc
;
55 tclc
= connection
->priv
;
57 if (tclc
->tc_notify
) {
58 snprintf(buf
, sizeof(buf
), "type target_event event %s\r\n\x1a", target_event_name(event
));
59 tcl_output(connection
, buf
, strlen(buf
));
62 if (tclc
->tc_laststate
!= target
->state
) {
63 tclc
->tc_laststate
= target
->state
;
64 if (tclc
->tc_notify
) {
65 snprintf(buf
, sizeof(buf
), "type target_state state %s\r\n\x1a", target_state_name(target
));
66 tcl_output(connection
, buf
, strlen(buf
));
73 static int tcl_target_callback_reset_handler(struct target
*target
,
74 enum target_reset_mode reset_mode
, void *priv
)
76 struct connection
*connection
= priv
;
77 struct tcl_connection
*tclc
;
80 tclc
= connection
->priv
;
82 if (tclc
->tc_notify
) {
83 snprintf(buf
, sizeof(buf
), "type target_reset mode %s\r\n\x1a", target_reset_mode_name(reset_mode
));
84 tcl_output(connection
, buf
, strlen(buf
));
90 /* write data out to a socket.
92 * this is a blocking write, so the return value must equal the length, if
93 * that is not the case then flag the connection with an output error.
95 int tcl_output(struct connection
*connection
, const void *data
, ssize_t len
)
98 struct tcl_connection
*tclc
;
100 tclc
= connection
->priv
;
101 if (tclc
->tc_outerror
)
102 return ERROR_SERVER_REMOTE_CLOSED
;
104 wlen
= connection_write(connection
, data
, len
);
109 LOG_ERROR("error during write: %d != %d", (int)wlen
, (int)len
);
110 tclc
->tc_outerror
= 1;
111 return ERROR_SERVER_REMOTE_CLOSED
;
115 static int tcl_new_connection(struct connection
*connection
)
117 struct tcl_connection
*tclc
;
119 tclc
= malloc(sizeof(struct tcl_connection
));
121 return ERROR_CONNECTION_REJECTED
;
123 memset(tclc
, 0, sizeof(struct tcl_connection
));
124 connection
->priv
= tclc
;
126 struct target
*target
= get_current_target(connection
->cmd_ctx
);
128 tclc
->tc_laststate
= target
->state
;
130 /* store the connection object on cmd_ctx so we can access it from command handlers */
131 connection
->cmd_ctx
->output_handler_priv
= connection
;
133 target_register_event_callback(tcl_target_callback_event_handler
, connection
);
134 target_register_reset_callback(tcl_target_callback_reset_handler
, connection
);
139 static int tcl_input(struct connection
*connection
)
141 Jim_Interp
*interp
= (Jim_Interp
*)connection
->cmd_ctx
->interp
;
147 struct tcl_connection
*tclc
;
148 unsigned char in
[256];
150 rlen
= connection_read(connection
, &in
, sizeof(in
));
153 LOG_ERROR("error during read: %s", strerror(errno
));
154 return ERROR_SERVER_REMOTE_CLOSED
;
157 tclc
= connection
->priv
;
159 return ERROR_CONNECTION_REJECTED
;
161 /* push as much data into the line as possible */
162 for (i
= 0; i
< rlen
; i
++) {
163 /* buffer the data */
164 tclc
->tc_line
[tclc
->tc_lineoffset
] = in
[i
];
165 if (tclc
->tc_lineoffset
< TCL_MAX_LINE
)
166 tclc
->tc_lineoffset
++;
168 tclc
->tc_linedrop
= 1;
170 /* ctrl-z is end of command. When testing from telnet, just
171 * press ctrl-z a couple of times first to put telnet into the
172 * mode where it will send 0x1a in response to pressing ctrl-z
177 /* process the line */
178 if (tclc
->tc_linedrop
) {
179 #define ESTR "line too long\n"
180 retval
= tcl_output(connection
, ESTR
, sizeof(ESTR
));
181 if (retval
!= ERROR_OK
)
185 tclc
->tc_line
[tclc
->tc_lineoffset
-1] = '\0';
186 retval
= command_run_line(connection
->cmd_ctx
, tclc
->tc_line
);
187 result
= Jim_GetString(Jim_GetResult(interp
), &reslen
);
188 retval
= tcl_output(connection
, result
, reslen
);
189 if (retval
!= ERROR_OK
)
191 /* Always output ctrl-d as end of line to allow multiline results */
192 tcl_output(connection
, "\x1a", 1);
195 tclc
->tc_lineoffset
= 0;
196 tclc
->tc_linedrop
= 0;
202 static int tcl_closed(struct connection
*connection
)
204 /* cleanup connection context */
205 if (connection
->priv
) {
206 free(connection
->priv
);
207 connection
->priv
= NULL
;
210 target_unregister_event_callback(tcl_target_callback_event_handler
, connection
);
211 target_unregister_reset_callback(tcl_target_callback_reset_handler
, connection
);
218 if (strcmp(tcl_port
, "disabled") == 0) {
219 LOG_INFO("tcl server disabled");
223 return add_service("tcl", tcl_port
, 1,
224 &tcl_new_connection
, &tcl_input
,
228 COMMAND_HANDLER(handle_tcl_port_command
)
230 return CALL_COMMAND_HANDLER(server_pipe_command
, &tcl_port
);
233 COMMAND_HANDLER(handle_tcl_notifications_command
)
235 struct connection
*connection
= NULL
;
236 struct tcl_connection
*tclc
= NULL
;
238 if (CMD_CTX
->output_handler_priv
!= NULL
)
239 connection
= CMD_CTX
->output_handler_priv
;
241 if (connection
!= NULL
&& !strcmp(connection
->service
->name
, "tcl")) {
242 tclc
= connection
->priv
;
243 return CALL_COMMAND_HANDLER(handle_command_parse_bool
, &tclc
->tc_notify
, "Target Notification output is");
245 LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME
);
246 return ERROR_COMMAND_SYNTAX_ERROR
;
250 static const struct command_registration tcl_command_handlers
[] = {
253 .handler
= handle_tcl_port_command
,
255 .help
= "Specify port on which to listen "
256 "for incoming Tcl syntax. "
257 "Read help on 'gdb_port'.",
258 .usage
= "[port_num]",
261 .name
= "tcl_notifications",
262 .handler
= handle_tcl_notifications_command
,
263 .mode
= COMMAND_EXEC
,
264 .help
= "Target Notification output",
267 COMMAND_REGISTRATION_DONE
270 int tcl_register_commands(struct command_context
*cmd_ctx
)
272 tcl_port
= strdup("6666");
273 return register_commands(cmd_ctx
, NULL
, tcl_command_handlers
);