Change-Id: I623a8c74bcca2edb5f996b69c02d73a6f67b7d34 Signed-off-by: Franck Jullien <franck.jullien@gmail.com> Reviewed-on: http://openocd.zylin.com/2162 Tested-by: jenkins Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>tags/v0.9.0-rc1
@@ -31,6 +31,7 @@ | |||
#include "server.h" | |||
#include <target/target.h> | |||
#include <target/target_request.h> | |||
#include <target/openrisc/jsp_server.h> | |||
#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); | |||
} | |||
@@ -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 |
@@ -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 <server/telnet_server.h> | |||
#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); | |||
} | |||
@@ -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_ */ |
@@ -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 | |||
@@ -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 <target/target.h> | |||
#include <jtag/jtag.h> | |||
#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, | |||
@@ -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 | |||
@@ -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] |