OpenOCD
tcl_server.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 /***************************************************************************
4  * Copyright (C) 2010 Øyvind Harboe *
5  * oyvind.harboe@zylin.com *
6  ***************************************************************************/
7 
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11 
12 #include "tcl_server.h"
13 #include <target/target.h>
14 #include <helper/binarybuffer.h>
15 
16 #define TCL_SERVER_VERSION "TCL Server 0.1"
17 #define TCL_LINE_INITIAL (4*1024)
18 #define TCL_LINE_MAX (4*1024*1024)
19 
24  char *tc_line;
25  int tc_outerror;/* flag an output error */
27  bool tc_notify;
28  bool tc_trace;
29 };
30 
31 static char *tcl_port;
32 
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);
38 
40  enum target_event event, void *priv)
41 {
42  struct connection *connection = priv;
43  struct tcl_connection *tclc;
44  char buf[256];
45 
46  tclc = connection->priv;
47 
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));
51  }
52 
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));
58  }
59  }
60 
61  return ERROR_OK;
62 }
63 
65  enum target_reset_mode reset_mode, void *priv)
66 {
67  struct connection *connection = priv;
68  struct tcl_connection *tclc;
69  char buf[256];
70 
71  tclc = connection->priv;
72 
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));
76  }
77 
78  return ERROR_OK;
79 }
80 
82  size_t len, uint8_t *data, void *priv)
83 {
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;
91 
92  tclc = connection->priv;
93 
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);
102  }
103 
104  return ERROR_OK;
105 }
106 
107 /* write data out to a socket.
108  *
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.
111  */
112 int tcl_output(struct connection *connection, const void *data, ssize_t len)
113 {
114  ssize_t wlen;
115  struct tcl_connection *tclc;
116 
117  tclc = connection->priv;
118  if (tclc->tc_outerror)
120 
121  wlen = connection_write(connection, data, len);
122 
123  if (wlen == len)
124  return ERROR_OK;
125 
126  LOG_ERROR("error during write: %d != %d", (int)wlen, (int)len);
127  tclc->tc_outerror = 1;
129 }
130 
131 /* connections */
133 {
134  struct tcl_connection *tclc;
135 
136  tclc = calloc(1, sizeof(struct tcl_connection));
137  if (!tclc)
139 
141  tclc->tc_line = malloc(tclc->tc_line_size);
142  if (!tclc->tc_line) {
143  free(tclc);
145  }
146 
147  connection->priv = tclc;
148 
150  if (target)
151  tclc->tc_laststate = target->state;
152 
153  /* store the connection object on cmd_ctx so we can access it from command handlers */
155 
159 
160  return ERROR_OK;
161 }
162 
163 static int tcl_input(struct connection *connection)
164 {
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;
175 
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));
181  }
182 
183  tclc = connection->priv;
184  if (!tclc)
186 
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;
202 
203  if (tc_line_size_new > TCL_LINE_MAX)
204  tc_line_size_new = TCL_LINE_MAX;
205 
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++;
213  }
214 
215  }
216 
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
220  */
221  if (in[i] != '\x1a')
222  continue;
223 
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);
240  }
241 
242  tclc->tc_lineoffset = 0;
243  tclc->tc_linedrop = 0;
244  }
245 
246  return ERROR_OK;
247 }
248 
249 static int tcl_closed(struct connection *connection)
250 {
251  struct tcl_connection *tclc;
252  tclc = connection->priv;
253 
254  /* cleanup connection context */
255  if (tclc) {
256  free(tclc->tc_line);
257  free(tclc);
258  connection->priv = NULL;
259  }
260 
264 
265  return ERROR_OK;
266 }
267 
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,
275 };
276 
277 int tcl_init(void)
278 {
279  if (strcmp(tcl_port, "disabled") == 0) {
280  LOG_INFO("tcl server disabled");
281  return ERROR_OK;
282  }
283 
285 }
286 
287 COMMAND_HANDLER(handle_tcl_port_command)
288 {
289  return CALL_COMMAND_HANDLER(server_pipe_command, &tcl_port);
290 }
291 
292 COMMAND_HANDLER(handle_tcl_notifications_command)
293 {
294  struct connection *connection = NULL;
295  struct tcl_connection *tclc = NULL;
296 
297  if (CMD_CTX->output_handler_priv)
298  connection = CMD_CTX->output_handler_priv;
299 
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);
306  }
307 }
308 
309 COMMAND_HANDLER(handle_tcl_trace_command)
310 {
311  struct connection *connection = NULL;
312  struct tcl_connection *tclc = NULL;
313 
314  if (CMD_CTX->output_handler_priv)
315  connection = CMD_CTX->output_handler_priv;
316 
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);
323  }
324 }
325 
326 static const struct command_registration tcl_command_handlers[] = {
327  {
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]",
335  },
336  {
337  .name = "tcl_notifications",
338  .handler = handle_tcl_notifications_command,
339  .mode = COMMAND_EXEC,
340  .help = "Target Notification output",
341  .usage = "[on|off]",
342  },
343  {
344  .name = "tcl_trace",
345  .handler = handle_tcl_trace_command,
346  .mode = COMMAND_EXEC,
347  .help = "Target trace output",
348  .usage = "[on|off]",
349  },
351 };
352 
354 {
355  tcl_port = strdup("6666");
356  return register_commands(cmd_ctx, NULL, tcl_command_handlers);
357 }
358 
360 {
361  free(tcl_port);
362 }
size_t hexify(char *hex, const uint8_t *bin, size_t count, size_t length)
Convert binary data into a string of hexadecimal pairs.
Definition: binarybuffer.c:392
Support functions to access arbitrary bits in a byte array.
int command_run_line(struct command_context *context, char *line)
Definition: command.c:547
#define CALL_COMMAND_HANDLER(name, extra ...)
Use this to macro to call a command helper (or a nested handler).
Definition: command.h:118
#define CMD_NAME
Use this macro to access the name of the command being handled, rather than accessing the variable di...
Definition: command.h:166
#define ERROR_COMMAND_SYNTAX_ERROR
Definition: command.h:402
#define CMD_CTX
Use this macro to access the context of the command being handled, rather than accessing the variable...
Definition: command.h:146
#define COMMAND_REGISTRATION_DONE
Use this as the last entry in an array of command_registration records.
Definition: command.h:253
static int register_commands(struct command_context *cmd_ctx, const char *cmd_prefix, const struct command_registration *cmds)
Register one or more commands in the specified context, as children of parent (or top-level commends,...
Definition: command.h:274
@ COMMAND_CONFIG
Definition: command.h:41
@ COMMAND_EXEC
Definition: command.h:40
size_t rlen
Definition: eCos.c:482
static struct esp_usb_jtag * priv
Definition: esp_usb_jtag.c:219
#define LOG_ERROR(expr ...)
Definition: log.h:132
#define LOG_INFO(expr ...)
Definition: log.h:126
#define ERROR_OK
Definition: log.h:164
int connection_write(struct connection *connection, const void *data, int len)
Definition: server.c:732
int connection_read(struct connection *connection, void *data, int len)
Definition: server.c:744
int add_service(const struct service_driver *driver, const char *port, int max_connections, void *priv)
Definition: server.c:198
#define CONNECTION_LIMIT_UNLIMITED
Definition: server.h:34
#define ERROR_SERVER_REMOTE_CLOSED
Definition: server.h:119
#define ERROR_CONNECTION_REJECTED
Definition: server.h:120
Jim_Interp * interp
Definition: command.h:53
void * output_handler_priv
Definition: command.h:65
const char * name
Definition: command.h:235
struct command_context * cmd_ctx
Definition: server.h:40
void * priv
Definition: server.h:43
struct service * service
Definition: server.h:41
const char * name
the name of the server
Definition: server.h:49
char * name
Definition: server.h:68
Definition: target.h:116
enum target_state state
Definition: target.h:157
enum target_state tc_laststate
Definition: tcl_server.c:26
char * tc_line
Definition: tcl_server.c:24
int target_unregister_reset_callback(int(*callback)(struct target *target, enum target_reset_mode reset_mode, void *priv), void *priv)
Definition: target.c:1710
int target_unregister_event_callback(int(*callback)(struct target *target, enum target_event event, void *priv), void *priv)
Definition: target.c:1687
int target_register_event_callback(int(*callback)(struct target *target, enum target_event event, void *priv), void *priv)
Definition: target.c:1592
int target_unregister_trace_callback(int(*callback)(struct target *target, size_t len, uint8_t *data, void *priv), void *priv)
Definition: target.c:1729
struct target * get_current_target_or_null(struct command_context *cmd_ctx)
Definition: target.c:470
const char * target_event_name(enum target_event event)
Return the name of a target event enumeration value.
Definition: target.c:275
const char * target_state_name(const struct target *t)
Return the name of this targets current state.
Definition: target.c:260
int target_register_reset_callback(int(*callback)(struct target *target, enum target_reset_mode reset_mode, void *priv), void *priv)
Definition: target.c:1614
int target_register_trace_callback(int(*callback)(struct target *target, size_t len, uint8_t *data, void *priv), void *priv)
Definition: target.c:1636
const char * target_reset_mode_name(enum target_reset_mode reset_mode)
Return the name of a target reset reason enumeration value.
Definition: target.c:286
target_reset_mode
Definition: target.h:61
target_event
Definition: target.h:240
target_state
Definition: target.h:53
static const struct command_registration tcl_command_handlers[]
Definition: tcl_server.c:326
static int tcl_target_callback_reset_handler(struct target *target, enum target_reset_mode reset_mode, void *priv)
Definition: tcl_server.c:64
static int tcl_new_connection(struct connection *connection)
Definition: tcl_server.c:132
static const struct service_driver tcl_service_driver
Definition: tcl_server.c:268
int tcl_register_commands(struct command_context *cmd_ctx)
Definition: tcl_server.c:353
#define ESTR
#define TCL_LINE_INITIAL
Definition: tcl_server.c:17
static int tcl_target_callback_event_handler(struct target *target, enum target_event event, void *priv)
Definition: tcl_server.c:39
#define TCL_LINE_MAX
Definition: tcl_server.c:18
static int tcl_output(struct connection *connection, const void *buf, ssize_t len)
Definition: tcl_server.c:112
COMMAND_HANDLER(handle_tcl_port_command)
Definition: tcl_server.c:287
static int tcl_closed(struct connection *connection)
Definition: tcl_server.c:249
static int tcl_input(struct connection *connection)
Definition: tcl_server.c:163
static int tcl_target_callback_trace_handler(struct target *target, size_t len, uint8_t *data, void *priv)
Definition: tcl_server.c:81
static char * tcl_port
Definition: tcl_server.c:31
void tcl_service_free(void)
Definition: tcl_server.c:359
int tcl_init(void)
Definition: tcl_server.c:277
#define NULL
Definition: usb.h:16