tcl/interface: support for Raspberry Pi 5
[openocd.git] / src / server / tcl_server.c
blob16cbedc76cb5ef6ad68074cf0c497734c9887812
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 /***************************************************************************
4 * Copyright (C) 2010 Øyvind Harboe *
5 * oyvind.harboe@zylin.com *
6 ***************************************************************************/
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
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 {
21 int tc_linedrop;
22 int tc_lineoffset;
23 int tc_line_size;
24 char *tc_line;
25 int tc_outerror;/* flag an output error */
26 enum target_state tc_laststate;
27 bool tc_notify;
28 bool tc_trace;
31 static char *tcl_port;
33 /* handlers */
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;
44 char buf[256];
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));
61 return ERROR_OK;
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;
69 char buf[256];
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));
78 return ERROR_OK;
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);
90 char *buf, *hex;
92 tclc = connection->priv;
94 if (tclc->tc_trace) {
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));
100 free(hex);
101 free(buf);
104 return ERROR_OK;
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)
114 ssize_t wlen;
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);
123 if (wlen == len)
124 return ERROR_OK;
126 LOG_ERROR("error during write: %d != %d", (int)wlen, (int)len);
127 tclc->tc_outerror = 1;
128 return ERROR_SERVER_REMOTE_CLOSED;
131 /* connections */
132 static int tcl_new_connection(struct connection *connection)
134 struct tcl_connection *tclc;
136 tclc = calloc(1, sizeof(struct tcl_connection));
137 if (!tclc)
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) {
143 free(tclc);
144 return ERROR_CONNECTION_REJECTED;
147 connection->priv = tclc;
149 struct target *target = get_current_target_or_null(connection->cmd_ctx);
150 if (target)
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);
160 return ERROR_OK;
163 static int tcl_input(struct connection *connection)
165 Jim_Interp *interp = (Jim_Interp *)connection->cmd_ctx->interp;
166 int retval;
167 int i;
168 ssize_t rlen;
169 const char *result;
170 int reslen;
171 struct tcl_connection *tclc;
172 unsigned char in[256];
173 char *tc_line_new;
174 int tc_line_size_new;
176 rlen = connection_read(connection, &in, sizeof(in));
177 if (rlen <= 0) {
178 if (rlen < 0)
179 LOG_ERROR("error during read: %s", strerror(errno));
180 return ERROR_SERVER_REMOTE_CLOSED;
183 tclc = connection->priv;
184 if (!tclc)
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;
196 } else {
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;
200 else
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);
207 if (!tc_line_new) {
208 tclc->tc_linedrop = 1;
209 } else {
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
221 if (in[i] != '\x1a')
222 continue;
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)
229 return retval;
230 #undef ESTR
231 } else {
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)
237 return retval;
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;
246 return ERROR_OK;
249 static int tcl_closed(struct connection *connection)
251 struct tcl_connection *tclc;
252 tclc = connection->priv;
254 /* cleanup connection context */
255 if (tclc) {
256 free(tclc->tc_line);
257 free(tclc);
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);
265 return ERROR_OK;
268 static const struct service_driver tcl_service_driver = {
269 .name = "tcl",
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,
277 int tcl_init(void)
279 if (strcmp(tcl_port, "disabled") == 0) {
280 LOG_INFO("tcl server disabled");
281 return ERROR_OK;
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 ");
303 } else {
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 ");
320 } else {
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[] = {
328 .name = "tcl_port",
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",
341 .usage = "[on|off]",
344 .name = "tcl_trace",
345 .handler = handle_tcl_trace_command,
346 .mode = COMMAND_EXEC,
347 .help = "Target trace output",
348 .usage = "[on|off]",
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)
361 free(tcl_port);