cpld: altera-epm240: Add additional IDCODEs
[fw/openocd] / src / server / tcl_server.c
index 409567c9d90e9e515cee078dea3b3048409daf4e..e088232242ff7a78700f41fc9d80628af83997ba 100644 (file)
@@ -13,9 +13,7 @@
  *   GNU General Public License for more details.                          *
  *                                                                         *
  *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
 #include <helper/binarybuffer.h>
 
 #define TCL_SERVER_VERSION             "TCL Server 0.1"
-#define TCL_MAX_LINE                   (4096)
+#define TCL_LINE_INITIAL               (4*1024)
+#define TCL_LINE_MAX                   (4*1024*1024)
 
 struct tcl_connection {
        int tc_linedrop;
        int tc_lineoffset;
-       char tc_line[TCL_MAX_LINE];
+       int tc_line_size;
+       char *tc_line;
        int tc_outerror;/* flag an output error */
        enum target_state tc_laststate;
        bool tc_notify;
@@ -105,7 +105,7 @@ static int tcl_target_callback_trace_handler(struct target *target,
        if (tclc->tc_trace) {
                hex = malloc(hex_len);
                buf = malloc(max_len);
-               hexify(hex, (const char *)data, len, hex_len);
+               hexify(hex, data, len, hex_len);
                snprintf(buf, max_len, "%s%s%s", header, hex, trailer);
                tcl_output(connection, buf, strlen(buf));
                free(hex);
@@ -144,15 +144,21 @@ static int tcl_new_connection(struct connection *connection)
 {
        struct tcl_connection *tclc;
 
-       tclc = malloc(sizeof(struct tcl_connection));
-       if (tclc == NULL)
+       tclc = calloc(1, sizeof(struct tcl_connection));
+       if (!tclc)
                return ERROR_CONNECTION_REJECTED;
 
-       memset(tclc, 0, sizeof(struct tcl_connection));
+       tclc->tc_line_size = TCL_LINE_INITIAL;
+       tclc->tc_line = malloc(tclc->tc_line_size);
+       if (!tclc->tc_line) {
+               free(tclc);
+               return ERROR_CONNECTION_REJECTED;
+       }
+
        connection->priv = tclc;
 
-       struct target *target = get_target_by_num(connection->cmd_ctx->current_target);
-       if (target != NULL)
+       struct target *target = get_current_target_or_null(connection->cmd_ctx);
+       if (target)
                tclc->tc_laststate = target->state;
 
        /* store the connection object on cmd_ctx so we can access it from command handlers */
@@ -175,6 +181,8 @@ static int tcl_input(struct connection *connection)
        int reslen;
        struct tcl_connection *tclc;
        unsigned char in[256];
+       char *tc_line_new;
+       int tc_line_size_new;
 
        rlen = connection_read(connection, &in, sizeof(in));
        if (rlen <= 0) {
@@ -184,17 +192,38 @@ static int tcl_input(struct connection *connection)
        }
 
        tclc = connection->priv;
-       if (tclc == NULL)
+       if (!tclc)
                return ERROR_CONNECTION_REJECTED;
 
        /* push as much data into the line as possible */
        for (i = 0; i < rlen; i++) {
                /* buffer the data */
                tclc->tc_line[tclc->tc_lineoffset] = in[i];
-               if (tclc->tc_lineoffset < TCL_MAX_LINE)
+               if (tclc->tc_lineoffset + 1 < tclc->tc_line_size) {
                        tclc->tc_lineoffset++;
-               else
+               } else if (tclc->tc_line_size >= TCL_LINE_MAX) {
+                       /* maximum line size reached, drop line */
                        tclc->tc_linedrop = 1;
+               } else {
+                       /* grow line buffer: exponential below 1 MB, linear above */
+                       if (tclc->tc_line_size <= 1*1024*1024)
+                               tc_line_size_new = tclc->tc_line_size * 2;
+                       else
+                               tc_line_size_new = tclc->tc_line_size + 1*1024*1024;
+
+                       if (tc_line_size_new > TCL_LINE_MAX)
+                               tc_line_size_new = TCL_LINE_MAX;
+
+                       tc_line_new = realloc(tclc->tc_line, tc_line_size_new);
+                       if (!tc_line_new) {
+                               tclc->tc_linedrop = 1;
+                       } else {
+                               tclc->tc_line = tc_line_new;
+                               tclc->tc_line_size = tc_line_size_new;
+                               tclc->tc_lineoffset++;
+                       }
+
+               }
 
                /* ctrl-z is end of command. When testing from telnet, just
                 * press ctrl-z a couple of times first to put telnet into the
@@ -217,7 +246,7 @@ static int tcl_input(struct connection *connection)
                        retval = tcl_output(connection, result, reslen);
                        if (retval != ERROR_OK)
                                return retval;
-                       /* Always output ctrl-d as end of line to allow multiline results */
+                       /* Always output ctrl-z as end of line to allow multiline results */
                        tcl_output(connection, "\x1a", 1);
                }
 
@@ -230,9 +259,13 @@ static int tcl_input(struct connection *connection)
 
 static int tcl_closed(struct connection *connection)
 {
+       struct tcl_connection *tclc;
+       tclc = connection->priv;
+
        /* cleanup connection context */
-       if (connection->priv) {
-               free(connection->priv);
+       if (tclc) {
+               free(tclc->tc_line);
+               free(tclc);
                connection->priv = NULL;
        }
 
@@ -250,7 +283,7 @@ int tcl_init(void)
                return ERROR_OK;
        }
 
-       return add_service("tcl", tcl_port, 1,
+       return add_service("tcl", tcl_port, CONNECTION_LIMIT_UNLIMITED,
                &tcl_new_connection, &tcl_input,
                &tcl_closed, NULL);
 }
@@ -265,10 +298,10 @@ COMMAND_HANDLER(handle_tcl_notifications_command)
        struct connection *connection = NULL;
        struct tcl_connection *tclc = NULL;
 
-       if (CMD_CTX->output_handler_priv != NULL)
+       if (CMD_CTX->output_handler_priv)
                connection = CMD_CTX->output_handler_priv;
 
-       if (connection != NULL && !strcmp(connection->service->name, "tcl")) {
+       if (connection && !strcmp(connection->service->name, "tcl")) {
                tclc = connection->priv;
                return CALL_COMMAND_HANDLER(handle_command_parse_bool, &tclc->tc_notify, "Target Notification output ");
        } else {
@@ -282,10 +315,10 @@ COMMAND_HANDLER(handle_tcl_trace_command)
        struct connection *connection = NULL;
        struct tcl_connection *tclc = NULL;
 
-       if (CMD_CTX->output_handler_priv != NULL)
+       if (CMD_CTX->output_handler_priv)
                connection = CMD_CTX->output_handler_priv;
 
-       if (connection != NULL && !strcmp(connection->service->name, "tcl")) {
+       if (connection && !strcmp(connection->service->name, "tcl")) {
                tclc = connection->priv;
                return CALL_COMMAND_HANDLER(handle_command_parse_bool, &tclc->tc_trace, "Target trace output ");
        } else {
@@ -298,7 +331,7 @@ static const struct command_registration tcl_command_handlers[] = {
        {
                .name = "tcl_port",
                .handler = handle_tcl_port_command,
-               .mode = COMMAND_ANY,
+               .mode = COMMAND_CONFIG,
                .help = "Specify port on which to listen "
                        "for incoming Tcl syntax.  "
                        "Read help on 'gdb_port'.",
@@ -326,3 +359,8 @@ int tcl_register_commands(struct command_context *cmd_ctx)
        tcl_port = strdup("6666");
        return register_commands(cmd_ctx, NULL, tcl_command_handlers);
 }
+
+void tcl_service_free(void)
+{
+       free(tcl_port);
+}