server: tcl_trace command
[fw/openocd] / src / server / tcl_server.c
1 /***************************************************************************
2  *   Copyright (C) 2010 Ã˜yvind Harboe                                      *
3  *   oyvind.harboe@zylin.com                                               *
4  *                                                                         *
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.                                   *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "tcl_server.h"
26 #include <target/target.h>
27 #include <helper/binarybuffer.h>
28
29 #define TCL_SERVER_VERSION              "TCL Server 0.1"
30 #define TCL_MAX_LINE                    (4096)
31
32 struct tcl_connection {
33         int tc_linedrop;
34         int tc_lineoffset;
35         char tc_line[TCL_MAX_LINE];
36         int tc_outerror;/* flag an output error */
37         enum target_state tc_laststate;
38         bool tc_notify;
39         bool tc_trace;
40 };
41
42 static char *tcl_port;
43
44 /* handlers */
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);
49
50 static int tcl_target_callback_event_handler(struct target *target,
51                 enum target_event event, void *priv)
52 {
53         struct connection *connection = priv;
54         struct tcl_connection *tclc;
55         char buf[256];
56
57         tclc = connection->priv;
58
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));
62         }
63
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));
69                 }
70         }
71
72         return ERROR_OK;
73 }
74
75 static int tcl_target_callback_reset_handler(struct target *target,
76                 enum target_reset_mode reset_mode, void *priv)
77 {
78         struct connection *connection = priv;
79         struct tcl_connection *tclc;
80         char buf[256];
81
82         tclc = connection->priv;
83
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));
87         }
88
89         return ERROR_OK;
90 }
91
92 static int tcl_target_callback_trace_handler(struct target *target,
93                 size_t len, uint8_t *data, void *priv)
94 {
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);
101         char *buf, *hex;
102
103         tclc = connection->priv;
104
105         if (tclc->tc_trace) {
106                 hex = malloc(hex_len);
107                 buf = malloc(max_len);
108                 hexify(hex, (const char *)data, len, hex_len);
109                 snprintf(buf, max_len, "%s%s%s", header, hex, trailer);
110                 tcl_output(connection, buf, strlen(buf));
111                 free(hex);
112                 free(buf);
113         }
114
115         return ERROR_OK;
116 }
117
118 /* write data out to a socket.
119  *
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.
122  */
123 int tcl_output(struct connection *connection, const void *data, ssize_t len)
124 {
125         ssize_t wlen;
126         struct tcl_connection *tclc;
127
128         tclc = connection->priv;
129         if (tclc->tc_outerror)
130                 return ERROR_SERVER_REMOTE_CLOSED;
131
132         wlen = connection_write(connection, data, len);
133
134         if (wlen == len)
135                 return ERROR_OK;
136
137         LOG_ERROR("error during write: %d != %d", (int)wlen, (int)len);
138         tclc->tc_outerror = 1;
139         return ERROR_SERVER_REMOTE_CLOSED;
140 }
141
142 /* connections */
143 static int tcl_new_connection(struct connection *connection)
144 {
145         struct tcl_connection *tclc;
146
147         tclc = malloc(sizeof(struct tcl_connection));
148         if (tclc == NULL)
149                 return ERROR_CONNECTION_REJECTED;
150
151         memset(tclc, 0, sizeof(struct tcl_connection));
152         connection->priv = tclc;
153
154         struct target *target = get_target_by_num(connection->cmd_ctx->current_target);
155         if (target != NULL)
156                 tclc->tc_laststate = target->state;
157
158         /* store the connection object on cmd_ctx so we can access it from command handlers */
159         connection->cmd_ctx->output_handler_priv = connection;
160
161         target_register_event_callback(tcl_target_callback_event_handler, connection);
162         target_register_reset_callback(tcl_target_callback_reset_handler, connection);
163         target_register_trace_callback(tcl_target_callback_trace_handler, connection);
164
165         return ERROR_OK;
166 }
167
168 static int tcl_input(struct connection *connection)
169 {
170         Jim_Interp *interp = (Jim_Interp *)connection->cmd_ctx->interp;
171         int retval;
172         int i;
173         ssize_t rlen;
174         const char *result;
175         int reslen;
176         struct tcl_connection *tclc;
177         unsigned char in[256];
178
179         rlen = connection_read(connection, &in, sizeof(in));
180         if (rlen <= 0) {
181                 if (rlen < 0)
182                         LOG_ERROR("error during read: %s", strerror(errno));
183                 return ERROR_SERVER_REMOTE_CLOSED;
184         }
185
186         tclc = connection->priv;
187         if (tclc == NULL)
188                 return ERROR_CONNECTION_REJECTED;
189
190         /* push as much data into the line as possible */
191         for (i = 0; i < rlen; i++) {
192                 /* buffer the data */
193                 tclc->tc_line[tclc->tc_lineoffset] = in[i];
194                 if (tclc->tc_lineoffset < TCL_MAX_LINE)
195                         tclc->tc_lineoffset++;
196                 else
197                         tclc->tc_linedrop = 1;
198
199                 /* ctrl-z is end of command. When testing from telnet, just
200                  * press ctrl-z a couple of times first to put telnet into the
201                  * mode where it will send 0x1a in response to pressing ctrl-z
202                  */
203                 if (in[i] != '\x1a')
204                         continue;
205
206                 /* process the line */
207                 if (tclc->tc_linedrop) {
208 #define ESTR "line too long\n"
209                         retval = tcl_output(connection, ESTR, sizeof(ESTR));
210                         if (retval != ERROR_OK)
211                                 return retval;
212 #undef ESTR
213                 } else {
214                         tclc->tc_line[tclc->tc_lineoffset-1] = '\0';
215                         command_run_line(connection->cmd_ctx, tclc->tc_line);
216                         result = Jim_GetString(Jim_GetResult(interp), &reslen);
217                         retval = tcl_output(connection, result, reslen);
218                         if (retval != ERROR_OK)
219                                 return retval;
220                         /* Always output ctrl-d as end of line to allow multiline results */
221                         tcl_output(connection, "\x1a", 1);
222                 }
223
224                 tclc->tc_lineoffset = 0;
225                 tclc->tc_linedrop = 0;
226         }
227
228         return ERROR_OK;
229 }
230
231 static int tcl_closed(struct connection *connection)
232 {
233         /* cleanup connection context */
234         if (connection->priv) {
235                 free(connection->priv);
236                 connection->priv = NULL;
237         }
238
239         target_unregister_event_callback(tcl_target_callback_event_handler, connection);
240         target_unregister_reset_callback(tcl_target_callback_reset_handler, connection);
241         target_unregister_trace_callback(tcl_target_callback_trace_handler, connection);
242
243         return ERROR_OK;
244 }
245
246 int tcl_init(void)
247 {
248         if (strcmp(tcl_port, "disabled") == 0) {
249                 LOG_INFO("tcl server disabled");
250                 return ERROR_OK;
251         }
252
253         return add_service("tcl", tcl_port, 1,
254                 &tcl_new_connection, &tcl_input,
255                 &tcl_closed, NULL);
256 }
257
258 COMMAND_HANDLER(handle_tcl_port_command)
259 {
260         return CALL_COMMAND_HANDLER(server_pipe_command, &tcl_port);
261 }
262
263 COMMAND_HANDLER(handle_tcl_notifications_command)
264 {
265         struct connection *connection = NULL;
266         struct tcl_connection *tclc = NULL;
267
268         if (CMD_CTX->output_handler_priv != NULL)
269                 connection = CMD_CTX->output_handler_priv;
270
271         if (connection != NULL && !strcmp(connection->service->name, "tcl")) {
272                 tclc = connection->priv;
273                 return CALL_COMMAND_HANDLER(handle_command_parse_bool, &tclc->tc_notify, "Target Notification output ");
274         } else {
275                 LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME);
276                 return ERROR_COMMAND_SYNTAX_ERROR;
277         }
278 }
279
280 COMMAND_HANDLER(handle_tcl_trace_command)
281 {
282         struct connection *connection = NULL;
283         struct tcl_connection *tclc = NULL;
284
285         if (CMD_CTX->output_handler_priv != NULL)
286                 connection = CMD_CTX->output_handler_priv;
287
288         if (connection != NULL && !strcmp(connection->service->name, "tcl")) {
289                 tclc = connection->priv;
290                 return CALL_COMMAND_HANDLER(handle_command_parse_bool, &tclc->tc_trace, "Target trace output ");
291         } else {
292                 LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME);
293                 return ERROR_COMMAND_SYNTAX_ERROR;
294         }
295 }
296
297 static const struct command_registration tcl_command_handlers[] = {
298         {
299                 .name = "tcl_port",
300                 .handler = handle_tcl_port_command,
301                 .mode = COMMAND_ANY,
302                 .help = "Specify port on which to listen "
303                         "for incoming Tcl syntax.  "
304                         "Read help on 'gdb_port'.",
305                 .usage = "[port_num]",
306         },
307         {
308                 .name = "tcl_notifications",
309                 .handler = handle_tcl_notifications_command,
310                 .mode = COMMAND_EXEC,
311                 .help = "Target Notification output",
312                 .usage = "[on|off]",
313         },
314         {
315                 .name = "tcl_trace",
316                 .handler = handle_tcl_trace_command,
317                 .mode = COMMAND_EXEC,
318                 .help = "Target trace output",
319                 .usage = "[on|off]",
320         },
321         COMMAND_REGISTRATION_DONE
322 };
323
324 int tcl_register_commands(struct command_context *cmd_ctx)
325 {
326         tcl_port = strdup("6666");
327         return register_commands(cmd_ctx, NULL, tcl_command_handlers);
328 }