diff --git a/src/server/server.c b/src/server/server.c index 5169319c5..7fbceb19a 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -31,6 +31,7 @@ #include "server.h" #include #include +#include #include "openocd.h" #include "tcl_server.h" #include "telnet_server.h" @@ -46,6 +47,9 @@ static struct service *services; /* shutdown_openocd == 1: exit the main event loop, and quit the debugger */ static int shutdown_openocd; +/* set the polling period to 100ms */ +static int polling_period = 100; + static int add_connection(struct service *service, struct command_context *cmd_ctx) { socklen_t address_size; @@ -380,8 +384,8 @@ int server_loop(struct command_context *command_context) tv.tv_usec = 0; retval = socket_select(fd_max + 1, &read_fds, NULL, NULL, &tv); } else { - /* Every 100ms */ - tv.tv_usec = 100000; + /* Every 100ms, can be changed with "poll_period" command */ + tv.tv_usec = polling_period * 1000; /* Only while we're sleeping we'll let others run */ openocd_sleep_prelude(); kept_alive(); @@ -588,6 +592,18 @@ COMMAND_HANDLER(handle_shutdown_command) return ERROR_OK; } +COMMAND_HANDLER(handle_poll_period_command) +{ + if (CMD_ARGC == 0) + LOG_WARNING("You need to set a period value"); + else + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], polling_period); + + LOG_INFO("set servers polling period to %ums", polling_period); + + return ERROR_OK; +} + static const struct command_registration server_command_handlers[] = { { .name = "shutdown", @@ -596,6 +612,13 @@ static const struct command_registration server_command_handlers[] = { .usage = "", .help = "shut the server down", }, + { + .name = "poll_period", + .handler = &handle_poll_period_command, + .mode = COMMAND_ANY, + .usage = "", + .help = "set the servers polling period", + }, COMMAND_REGISTRATION_DONE }; @@ -609,6 +632,10 @@ int server_register_commands(struct command_context *cmd_ctx) if (ERROR_OK != retval) return retval; + retval = jsp_register_commands(cmd_ctx); + if (ERROR_OK != retval) + return retval; + return register_commands(cmd_ctx, NULL, server_command_handlers); } diff --git a/src/target/openrisc/Makefile.am b/src/target/openrisc/Makefile.am index f1e7eaadf..b00a30d6f 100644 --- a/src/target/openrisc/Makefile.am +++ b/src/target/openrisc/Makefile.am @@ -8,9 +8,11 @@ OPENRISC_SRC = \ or1k_du_adv.c \ or1k_tap_mohor.c \ or1k_tap_vjtag.c \ - or1k_tap_xilinx_bscan.c + or1k_tap_xilinx_bscan.c \ + jsp_server.c noinst_HEADERS = \ or1k.h \ or1k_du.h \ - or1k_tap.h + or1k_tap.h \ + jsp_server.h diff --git a/src/target/openrisc/jsp_server.c b/src/target/openrisc/jsp_server.c new file mode 100644 index 000000000..597bfcb64 --- /dev/null +++ b/src/target/openrisc/jsp_server.c @@ -0,0 +1,247 @@ +/*************************************************************************** + * Copyright (C) 2014 by Franck Jullien * + * franck.jullien@gmail.com * + * * + * Based on ./src/server/telnet_server.c * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * 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. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "or1k_tap.h" +#include "or1k_du.h" +#include "jsp_server.h" + +static char *jsp_port; + +/**A skim of the relevant RFCs suggests that if my application simply sent the + * characters IAC DONT LINEMODE (\377\376\042) as soon as the client connects, + * the client should be forced into character mode. However it doesn't make any difference. + */ + +static char *negotiate = + "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */ + "\xFF\xFB\x01" /* IAC WILL Echo */ + "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */ + "\xFF\xFE\x01"; /* IAC DON'T Echo */ + +/* The only way we can detect that the socket is closed is the first time + * we write to it, we will fail. Subsequent write operations will + * succeed. Shudder! + */ +static int telnet_write(struct connection *connection, const void *data, int len) +{ + struct telnet_connection *t_con = connection->priv; + if (t_con->closed) + return ERROR_SERVER_REMOTE_CLOSED; + + if (connection_write(connection, data, len) == len) + return ERROR_OK; + t_con->closed = 1; + return ERROR_SERVER_REMOTE_CLOSED; +} + +int jsp_poll_read(void *priv) +{ + struct jsp_service *jsp_service = (struct jsp_service *)priv; + unsigned char out_buffer[10]; + unsigned char in_buffer[10]; + int out_len = 0; + int in_len; + + if (!jsp_service->connection) + return ERROR_FAIL; + + memset(out_buffer, 0, 10); + + or1k_adv_jtag_jsp_xfer(jsp_service->jtag_info, &out_len, out_buffer, &in_len, in_buffer); + if (in_len) + telnet_write(jsp_service->connection, in_buffer, in_len); + + return ERROR_OK; +} + +static int jsp_new_connection(struct connection *connection) +{ + struct telnet_connection *telnet_connection = malloc(sizeof(struct telnet_connection)); + struct jsp_service *jsp_service = connection->service->priv; + + connection->priv = telnet_connection; + + /* initialize telnet connection information */ + telnet_connection->closed = 0; + telnet_connection->line_size = 0; + telnet_connection->line_cursor = 0; + telnet_connection->option_size = 0; + telnet_connection->state = TELNET_STATE_DATA; + + /* negotiate telnet options */ + telnet_write(connection, negotiate, strlen(negotiate)); + + /* print connection banner */ + if (jsp_service->banner) { + telnet_write(connection, jsp_service->banner, strlen(jsp_service->banner)); + telnet_write(connection, "\r\n", 2); + } + + jsp_service->connection = connection; + + int retval = target_register_timer_callback(&jsp_poll_read, 1, 1, jsp_service); + if (ERROR_OK != retval) + return retval; + + return ERROR_OK; +} + +static int jsp_input(struct connection *connection) +{ + int bytes_read; + unsigned char buffer[TELNET_BUFFER_SIZE]; + unsigned char *buf_p; + struct telnet_connection *t_con = connection->priv; + struct jsp_service *jsp_service = connection->service->priv; + + bytes_read = connection_read(connection, buffer, TELNET_BUFFER_SIZE); + + if (bytes_read == 0) + return ERROR_SERVER_REMOTE_CLOSED; + else if (bytes_read == -1) { + LOG_ERROR("error during read: %s", strerror(errno)); + return ERROR_SERVER_REMOTE_CLOSED; + } + + buf_p = buffer; + while (bytes_read) { + switch (t_con->state) { + case TELNET_STATE_DATA: + if (*buf_p == 0xff) + t_con->state = TELNET_STATE_IAC; + else { + int out_len = 1; + int in_len; + unsigned char in_buffer[10]; + or1k_adv_jtag_jsp_xfer(jsp_service->jtag_info, + &out_len, buf_p, &in_len, + in_buffer); + if (in_len) + telnet_write(connection, + in_buffer, in_len); + } + break; + case TELNET_STATE_IAC: + switch (*buf_p) { + case 0xfe: + t_con->state = TELNET_STATE_DONT; + break; + case 0xfd: + t_con->state = TELNET_STATE_DO; + break; + case 0xfc: + t_con->state = TELNET_STATE_WONT; + break; + case 0xfb: + t_con->state = TELNET_STATE_WILL; + break; + } + break; + case TELNET_STATE_SB: + break; + case TELNET_STATE_SE: + break; + case TELNET_STATE_WILL: + case TELNET_STATE_WONT: + case TELNET_STATE_DO: + case TELNET_STATE_DONT: + t_con->state = TELNET_STATE_DATA; + break; + default: + LOG_ERROR("unknown telnet state"); + exit(-1); + } + + bytes_read--; + buf_p++; + } + + return ERROR_OK; +} + +static int jsp_connection_closed(struct connection *connection) +{ + struct telnet_connection *t_con = connection->priv; + struct jsp_service *jsp_service = connection->service->priv; + + if (t_con->prompt) { + free(t_con->prompt); + t_con->prompt = NULL; + } + + int retval = target_unregister_timer_callback(&jsp_poll_read, jsp_service); + if (ERROR_OK != retval) + return retval; + + if (connection->priv) { + free(connection->priv); + connection->priv = NULL; + } else + LOG_ERROR("BUG: connection->priv == NULL"); + + return ERROR_OK; +} + +int jsp_init(struct or1k_jtag *jtag_info, char *banner) +{ + struct jsp_service *jsp_service = malloc(sizeof(struct jsp_service)); + jsp_service->banner = banner; + jsp_service->jtag_info = jtag_info; + + return add_service("jsp", + jsp_port, + 1, + jsp_new_connection, + jsp_input, + jsp_connection_closed, + jsp_service); +} + +COMMAND_HANDLER(handle_jsp_port_command) +{ + return CALL_COMMAND_HANDLER(server_pipe_command, &jsp_port); +} + +static const struct command_registration jsp_command_handlers[] = { + { + .name = "jsp_port", + .handler = handle_jsp_port_command, + .mode = COMMAND_ANY, + .help = "Specify port on which to listen " + "for incoming JSP telnet connections.", + .usage = "[port_num]", + }, + COMMAND_REGISTRATION_DONE +}; + +int jsp_register_commands(struct command_context *cmd_ctx) +{ + jsp_port = strdup("7777"); + return register_commands(cmd_ctx, NULL, jsp_command_handlers); +} + diff --git a/src/target/openrisc/jsp_server.h b/src/target/openrisc/jsp_server.h new file mode 100644 index 000000000..3e7c1145d --- /dev/null +++ b/src/target/openrisc/jsp_server.h @@ -0,0 +1,17 @@ +#ifndef _JSP_SERVER_H_ +#define _JSP_SERVER_H_ + +#include "or1k_tap.h" +#include "or1k.h" +#include "or1k_du.h" + +struct jsp_service { + char *banner; + struct or1k_jtag *jtag_info; + struct connection *connection; +}; + +int jsp_init(struct or1k_jtag *jtag_info, char *banner); +int jsp_register_commands(struct command_context *cmd_ctx); + +#endif /* _JSP_SERVER_H_ */ diff --git a/src/target/openrisc/or1k_du.h b/src/target/openrisc/or1k_du.h index 564241d8c..f5ee36435 100644 --- a/src/target/openrisc/or1k_du.h +++ b/src/target/openrisc/or1k_du.h @@ -73,5 +73,9 @@ static inline struct or1k_du *or1k_to_du(struct or1k_common *or1k) return (struct or1k_du *)jtag->du_core; } +int or1k_adv_jtag_jsp_xfer(struct or1k_jtag *jtag_info, + int *out_len, unsigned char *out_buffer, + int *in_len, unsigned char *in_buffer); + #endif diff --git a/src/target/openrisc/or1k_du_adv.c b/src/target/openrisc/or1k_du_adv.c index 8a5562f09..e25a711b8 100644 --- a/src/target/openrisc/or1k_du_adv.c +++ b/src/target/openrisc/or1k_du_adv.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Copyright (C) 2013 by Franck Jullien * + * Copyright (C) 2013-2014 by Franck Jullien * * elec4fun@gmail.com * * * - * Inspired from adv_jtag_bridge which is: * + * Inspired from adv_jtag_bridge which is: * * Copyright (C) 2008-2010 Nathan Yawn * * nyawn@opencores.net * * * @@ -33,10 +33,19 @@ #include "or1k_tap.h" #include "or1k.h" #include "or1k_du.h" +#include "jsp_server.h" #include #include +#define JSP_BANNER "\n\r" \ + "******************************\n\r" \ + "** JTAG Serial Port **\n\r" \ + "******************************\n\r" \ + "\n\r" + +#define NO_OPTION 0 + /* This an option to the adv debug unit. * If this is defined, status bits will be skipped on burst * reads and writes to improve download speeds. @@ -44,6 +53,17 @@ */ #define ADBG_USE_HISPEED 1 +/* This an option to the adv debug unit. + * If this is defined, the JTAG Serial Port Server is started. + * This option must match the RTL configured option. + */ +#define ENABLE_JSP_SERVER 2 + +/* Define this if you intend to use the JSP in a system with multiple + * devices on the JTAG chain + */ +#define ENABLE_JSP_MULTI 4 + /* Definitions for the top-level debug unit. This really just consists * of a single register, used to select the active debug module ("chain"). */ @@ -182,6 +202,17 @@ static int or1k_adv_jtag_init(struct or1k_jtag *jtag_info) if (or1k_du_adv.options & ADBG_USE_HISPEED) LOG_INFO("adv debug unit is configured with option ADBG_USE_HISPEED"); + if (or1k_du_adv.options & ENABLE_JSP_SERVER) { + if (or1k_du_adv.options & ENABLE_JSP_MULTI) + LOG_INFO("adv debug unit is configured with option ENABLE_JSP_MULTI"); + LOG_INFO("adv debug unit is configured with option ENABLE_JSP_SERVER"); + retval = jsp_init(jtag_info, JSP_BANNER); + if (retval != ERROR_OK) { + LOG_ERROR("Couldn't start the JSP server"); + return retval; + } + } + LOG_DEBUG("Init done"); return ERROR_OK; @@ -962,9 +993,93 @@ static int or1k_adv_jtag_write_memory(struct or1k_jtag *jtag_info, return ERROR_OK; } +int or1k_adv_jtag_jsp_xfer(struct or1k_jtag *jtag_info, + int *out_len, unsigned char *out_buffer, + int *in_len, unsigned char *in_buffer) +{ + LOG_DEBUG("JSP transfert"); + + int retval; + if (!jtag_info->or1k_jtag_inited) + return ERROR_OK; + + retval = adbg_select_module(jtag_info, DC_JSP); + if (retval != ERROR_OK) + return retval; + + /* return nb char xmit */ + int xmitsize; + if (*out_len > 8) + xmitsize = 8; + else + xmitsize = *out_len; + + uint8_t out_data[10]; + uint8_t in_data[10]; + struct scan_field field; + int startbit, stopbit, wrapbit; + + memset(out_data, 0, 10); + + if (or1k_du_adv.options & ENABLE_JSP_MULTI) { + + startbit = 1; + wrapbit = (xmitsize >> 3) & 0x1; + out_data[0] = (xmitsize << 5) | 0x1; /* set the start bit */ + + int i; + /* don't copy off the end of the input array */ + for (i = 0; i < xmitsize; i++) { + out_data[i + 1] = (out_buffer[i] << 1) | wrapbit; + wrapbit = (out_buffer[i] >> 7) & 0x1; + } + + if (i < 8) + out_data[i + 1] = wrapbit; + else + out_data[9] = wrapbit; + + /* If the last data bit is a '1', then we need to append a '0' so the top-level module + * won't treat the burst as a 'module select' command. + */ + stopbit = !!(out_data[9] & 0x01); + + } else { + startbit = 0; + /* First byte out has write count in upper nibble */ + out_data[0] = 0x0 | (xmitsize << 4); + if (xmitsize > 0) + memcpy(&out_data[1], out_buffer, xmitsize); + + /* If the last data bit is a '1', then we need to append a '0' so the top-level module + * won't treat the burst as a 'module select' command. + */ + stopbit = !!(out_data[8] & 0x80); + } + + field.num_bits = 72 + startbit + stopbit; + field.out_value = out_data; + field.in_value = in_data; + + jtag_add_dr_scan(jtag_info->tap, 1, &field, TAP_IDLE); + + retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + + /* bytes available is in the upper nibble */ + *in_len = (in_data[0] >> 4) & 0xF; + memcpy(in_buffer, &in_data[1], *in_len); + + int bytes_free = in_data[0] & 0x0F; + *out_len = (bytes_free < xmitsize) ? bytes_free : xmitsize; + + return ERROR_OK; +} + static struct or1k_du or1k_du_adv = { .name = "adv", - .options = ADBG_USE_HISPEED, + .options = NO_OPTION, .or1k_jtag_init = or1k_adv_jtag_init, .or1k_is_cpu_running = or1k_adv_is_cpu_running, diff --git a/tcl/board/or1k_generic.cfg b/tcl/board/or1k_generic.cfg index 5d23641a9..c543ebe25 100644 --- a/tcl/board/or1k_generic.cfg +++ b/tcl/board/or1k_generic.cfg @@ -13,6 +13,9 @@ set CHIPNAME or1200 source [find target/or1k.cfg] +# Set the servers polling period to 1ms (needed to JSP Server) +poll_period 1 + # Set the adapter speed adapter_khz 3000 diff --git a/tcl/target/or1k.cfg b/tcl/target/or1k.cfg index acec70026..360a0ddf3 100644 --- a/tcl/target/or1k.cfg +++ b/tcl/target/or1k.cfg @@ -61,10 +61,12 @@ if { [string compare $_TAP_TYPE "VJTAG"] == 0 } { # Select the debug unit core we are using. This debug unit as an option. -proc ADBG_USE_HISPEED {} { return 1 } +set ADBG_USE_HISPEED 1 +set ENABLE_JSP_SERVER 2 +set ENABLE_JSP_MULTI 4 # If ADBG_USE_HISPEED is set (options bit 1), status bits will be skipped # on burst reads and writes to improve download speeds. # This option must match the RTL configured option. -du_select adv [ADBG_USE_HISPEED] +du_select adv [expr $ADBG_USE_HISPEED | $ENABLE_JSP_SERVER | $ENABLE_JSP_MULTI]