Author | SHA1 | Message | Date |
---|---|---|---|
|
e8d8d9c53b |
rtt: Fix writing to down channels
Change-Id: I8d8d458ce687f5112a575c084a9c111c6896707b |
6 years ago |
|
cdd93e3cff |
Add initial RTT support (WIP+RFC)
Real Time Transfer (RTT) is an interface specified by SEGGER based on basic memory reads and writes to transfer data bidirectionally between target and host. Every target that supports so called "background memory access", which means that the target memory can be accessed by the debugger while the target is running, can be used. RTT is especially of interest for targets which do not support Serial Wire Output (SWO) (e.g. ARM Cortex-M0) or where using semihosting is not possible (e.g. real-time applications) [1]. The data transfer is organized in channels where each channel consists of an up- and/or down-channel. See [2] for more details. Channels are exposed via TCP connections. One or more RTT server can be assigned to each channel to make them accessible to an unlimited number of TCP connections. The current implementation does not respect buffer flags which are used to determine what happens when writing to a full buffer. Note that the implementation is designed in a way that the RTT operations can be directly performed by an adapter (e.g. J-Link). [1] https://devzone.nordicsemi.com/tutorials/6/ [2] https://www.segger.com/jlink-rtt.html Change-Id: I8bc8a1b381fb74e08b8752d5cf53804cc573c1e0 Signed-off-by: Marc Schink <openocd-dev@marcschink.de> |
6 years ago |
|
0326788077 |
server/server: Add ability to remove services
Add the ability to remove services while OpenOCD is running. Change-Id: I4067916fda6d03485463fa40901b40484d94e24e Signed-off-by: Marc Schink <openocd-dev@marcschink.de> |
6 years ago |
|
afce40dd48 |
server/server: Remove all connections on shutdown
This patch fixes a memory leak in the internal server. Steps for reproduction: * valgrind --leak-check=full --show-reachable=yes ./build/src/openocd * Establish more than one connection to OpenOCD (e.g. telnet) * Shutdown OpenOCD * Check for memory leaks in add_connection() Change-Id: I0ae6fcf2918fd9bdec350446d3e26742d08ff698 Signed-off-by: Marc Schink <openocd-dev@marcschink.de> |
6 years ago |
|
072d6bac02 |
helper/command.h: Add missing #include for target_addr_t
Change-Id: Ic406257c0da6e1889d4434cc98cf59c2b97aa30c Signed-off-by: Marc Schink <openocd-dev@marcschink.de> |
6 years ago |
|
eda66b053a |
server/server.h: Add missing #include
Change-Id: I9d0615f9218470d190223f7f6b5b406e5c7f2b11 Signed-off-by: Marc Schink <openocd-dev@marcschink.de> |
6 years ago |
|
7e8a88a304 |
helper/types.h: Add missing #includes
Change-Id: I02ae0fb9527c4b87308da9c2cab66c80d84579eb Signed-off-by: Marc Schink <openocd-dev@marcschink.de> |
6 years ago |
|
032447a1f1 |
helper/command.h: Add missing #includes
Change-Id: I84650a51cdb015f5e8ae933a3288f6e87f9fb80b Signed-off-by: Marc Schink <openocd-dev@marcschink.de> |
6 years ago |
|
20c7212cf1 |
helper/replacements.h: Add missing #include
Change-Id: Ic6c47f7fbc999d00ef3211c1fa44867e3aabc321 Signed-off-by: Marc Schink <openocd-dev@marcschink.de> |
6 years ago |
|
4494c645b0 |
target: Fix memory leak
Change-Id: Ib23dfd653d8edacb890a46179e9d437c027d58e8 Signed-off-by: Marc Schink <openocd-dev@marcschink.de> |
6 years ago |
|
ba52458dfc |
server/server: Remove all exit() calls
With this patch OpenOCD shuts down properly when errors occur in the server instead of just calling exit(). Change-Id: I2ae1a6153dafc88667951cab9152941cb487be85 Signed-off-by: Marc Schink <openocd-dev@marcschink.de> |
7 years ago |
|
e8f065c616 |
Fix Jim interpreter memory leak
Change-Id: I71d7d97e7dc315c42fc43b65cb5fcecd7bdfb581 Signed-off-by: Marc Schink <openocd-dev@marcschink.de> |
7 years ago |
@@ -53,7 +53,8 @@ endif | |||
%D%/target/libtarget.la \ | |||
%D%/server/libserver.la \ | |||
%D%/rtos/librtos.la \ | |||
%D%/helper/libhelper.la | |||
%D%/helper/libhelper.la \ | |||
%D%/rtt/librtt.la | |||
BIN2C = $(srcdir)/%D%/helper/bin2char.sh | |||
@@ -83,3 +84,4 @@ include %D%/rtos/Makefile.am | |||
include %D%/server/Makefile.am | |||
include %D%/flash/Makefile.am | |||
include %D%/pld/Makefile.am | |||
include %D%/rtt/Makefile.am |
@@ -1356,6 +1356,7 @@ COMMAND_HANDLER(stellaris_handle_recover_command) | |||
{ | |||
struct flash_bank *bank; | |||
int retval; | |||
const char *tmp; | |||
if (CMD_ARGC != 0) | |||
return ERROR_COMMAND_SYNTAX_ERROR; | |||
@@ -1370,8 +1371,11 @@ COMMAND_HANDLER(stellaris_handle_recover_command) | |||
* cycle to recover. | |||
*/ | |||
Jim_Eval_Named(CMD_CTX->interp, "catch { hla_command \"debug unlock\" }", 0, 0); | |||
if (!strcmp(Jim_GetString(Jim_GetResult(CMD_CTX->interp), NULL), "0")) { | |||
Jim_Eval_Named(CMD_CTX->interp->interp, | |||
"catch { hla_command \"debug unlock\" }", 0, 0); | |||
tmp = Jim_GetString(Jim_GetResult(CMD_CTX->interp->interp), NULL); | |||
if (!strcmp(tmp, "0")) { | |||
retval = ERROR_OK; | |||
goto user_action; | |||
} | |||
@@ -356,7 +356,7 @@ static int command_unknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv); | |||
static int register_command_handler(struct command_context *cmd_ctx, | |||
struct command *c) | |||
{ | |||
Jim_Interp *interp = cmd_ctx->interp; | |||
Jim_Interp *interp = cmd_ctx->interp->interp; | |||
char *ocd_name = alloc_printf("ocd_%s", c->name); | |||
if (NULL == ocd_name) | |||
return JIM_ERR; | |||
@@ -406,7 +406,7 @@ struct command *register_command(struct command_context *context, | |||
int retval = ERROR_OK; | |||
if (NULL != cr->jim_handler && NULL == parent) { | |||
retval = Jim_CreateCommand(context->interp, cr->name, | |||
retval = Jim_CreateCommand(context->interp->interp, cr->name, | |||
cr->jim_handler, cr->jim_handler_data, NULL); | |||
} else if (NULL != cr->handler || NULL != parent) | |||
retval = register_command_handler(context, command_root(c)); | |||
@@ -643,7 +643,7 @@ int command_run_line(struct command_context *context, char *line) | |||
* happen when the Jim Tcl interpreter is provided by eCos for | |||
* instance. | |||
*/ | |||
Jim_Interp *interp = context->interp; | |||
Jim_Interp *interp = context->interp->interp; | |||
Jim_DeleteAssocData(interp, "context"); | |||
retcode = Jim_SetAssocData(interp, "context", NULL, context); | |||
if (retcode == JIM_OK) { | |||
@@ -720,8 +720,14 @@ void command_set_output_handler(struct command_context *context, | |||
struct command_context *copy_command_context(struct command_context *context) | |||
{ | |||
struct command_context *copy_context = malloc(sizeof(struct command_context)); | |||
struct command_context *copy_context; | |||
copy_context = malloc(sizeof(struct command_context)); | |||
if (!copy_context) | |||
return NULL; | |||
context->interp->refcnt++; | |||
*copy_context = *context; | |||
return copy_context; | |||
@@ -729,9 +735,18 @@ struct command_context *copy_command_context(struct command_context *context) | |||
void command_done(struct command_context *cmd_ctx) | |||
{ | |||
if (NULL == cmd_ctx) | |||
if (cmd_ctx == NULL) | |||
return; | |||
cmd_ctx->interp->refcnt--; | |||
if (!cmd_ctx->interp->refcnt) { | |||
if (cmd_ctx->interp->free) | |||
Jim_FreeInterp(cmd_ctx->interp->interp); | |||
free(cmd_ctx->interp); | |||
} | |||
free(cmd_ctx); | |||
} | |||
@@ -1269,14 +1284,28 @@ static const struct command_registration command_builtin_handlers[] = { | |||
struct command_context *command_init(const char *startup_tcl, Jim_Interp *interp) | |||
{ | |||
struct command_context *context = malloc(sizeof(struct command_context)); | |||
struct command_context *context; | |||
const char *HostOs; | |||
context = malloc(sizeof(struct command_context)); | |||
if (!context) { | |||
LOG_ERROR("Failed to allocate command context."); | |||
return NULL; | |||
} | |||
context->mode = COMMAND_EXEC; | |||
context->commands = NULL; | |||
context->current_target = 0; | |||
context->output_handler = NULL; | |||
context->output_handler_priv = NULL; | |||
context->interp = malloc(sizeof(struct command_interpreter)); | |||
if (!context->interp) { | |||
LOG_ERROR("Failed to allocate command interpreter."); | |||
free(context); | |||
return NULL; | |||
} | |||
/* Create a jim interpreter if we were not handed one */ | |||
if (interp == NULL) { | |||
@@ -1285,9 +1314,13 @@ struct command_context *command_init(const char *startup_tcl, Jim_Interp *interp | |||
/* Add all the Jim core commands */ | |||
Jim_RegisterCoreCommands(interp); | |||
Jim_InitStaticExtensions(interp); | |||
context->interp->free = true; | |||
} else { | |||
context->interp->free = false; | |||
} | |||
context->interp = interp; | |||
context->interp->interp = interp; | |||
context->interp->refcnt = 1; | |||
/* Stick to lowercase for HostOS strings. */ | |||
#if defined(_MSC_VER) | |||
@@ -1355,7 +1388,7 @@ void process_jim_events(struct command_context *cmd_ctx) | |||
return; | |||
recursion++; | |||
Jim_ProcessEvents(cmd_ctx->interp, JIM_ALL_EVENTS | JIM_DONT_WAIT); | |||
Jim_ProcessEvents(cmd_ctx->interp->interp, JIM_ALL_EVENTS | JIM_DONT_WAIT); | |||
recursion--; | |||
} | |||
@@ -22,8 +22,12 @@ | |||
#ifndef OPENOCD_HELPER_COMMAND_H | |||
#define OPENOCD_HELPER_COMMAND_H | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include <jim-nvp.h> | |||
#include <helper/types.h> | |||
/* To achieve C99 printf compatibility in MinGW, gnu_printf should be | |||
* used for __attribute__((format( ... ))), with GCC v4.4 or later | |||
*/ | |||
@@ -39,6 +43,22 @@ enum command_mode { | |||
COMMAND_ANY, | |||
}; | |||
/** | |||
* Helper struct to share a common Jim interpreter among multiple command | |||
* contexts. | |||
*/ | |||
struct command_interpreter { | |||
/** Jim interpreter. */ | |||
Jim_Interp *interp; | |||
/** Number of references held on this interpreter. */ | |||
unsigned int refcnt; | |||
/** | |||
* Determines whether the Jim interpreter should be free'd when the last | |||
* reference is released. | |||
*/ | |||
bool free; | |||
}; | |||
struct command_context; | |||
/** The type signature for command context's output handler. */ | |||
@@ -46,7 +66,7 @@ typedef int (*command_output_handler_t)(struct command_context *context, | |||
const char *line); | |||
struct command_context { | |||
Jim_Interp *interp; | |||
struct command_interpreter *interp; | |||
enum command_mode mode; | |||
struct command *commands; | |||
int current_target; | |||
@@ -25,6 +25,8 @@ | |||
#ifndef OPENOCD_HELPER_REPLACEMENTS_H | |||
#define OPENOCD_HELPER_REPLACEMENTS_H | |||
#include <stdint.h> | |||
/* MIN,MAX macros */ | |||
#ifndef MIN | |||
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) | |||
@@ -22,7 +22,12 @@ | |||
#ifndef OPENOCD_HELPER_TYPES_H | |||
#define OPENOCD_HELPER_TYPES_H | |||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include <stddef.h> | |||
#include <assert.h> | |||
#ifdef HAVE_SYS_TYPES_H | |||
#include <sys/types.h> | |||
#endif | |||
@@ -1582,7 +1582,10 @@ int jtag_init(struct command_context *cmd_ctx) | |||
if (retval != ERROR_OK) | |||
return retval; | |||
if (Jim_Eval_Named(cmd_ctx->interp, "jtag_init", __FILE__, __LINE__) != JIM_OK) | |||
retval = Jim_Eval_Named(cmd_ctx->interp->interp, "jtag_init", __FILE__, | |||
__LINE__); | |||
if (retval != JIM_OK) | |||
return ERROR_FAIL; | |||
return ERROR_OK; | |||
@@ -37,9 +37,11 @@ | |||
#include <flash/nand/core.h> | |||
#include <pld/pld.h> | |||
#include <flash/mflash.h> | |||
#include <rtt/rtt.h> | |||
#include <server/server.h> | |||
#include <server/gdb_server.h> | |||
#include <server/rtt_server.h> | |||
#ifdef HAVE_STRINGS_H | |||
#include <strings.h> | |||
@@ -170,7 +172,10 @@ COMMAND_HANDLER(handle_init_command) | |||
command_context_mode(CMD_CTX, COMMAND_EXEC); | |||
/* initialize telnet subsystem */ | |||
gdb_target_add_all(all_targets); | |||
retval = gdb_target_add_all(all_targets); | |||
if (retval != ERROR_OK) | |||
return retval; | |||
target_register_event_callback(log_target_callback_event_handler, CMD_CTX); | |||
@@ -245,6 +250,8 @@ struct command_context *setup_command_handler(Jim_Interp *interp) | |||
&server_register_commands, | |||
&gdb_register_commands, | |||
&log_register_commands, | |||
&rtt_register_commands, | |||
&rtt_server_register_commands, | |||
&transport_register_commands, | |||
&interface_register_commands, | |||
&target_register_commands, | |||
@@ -297,8 +304,10 @@ static int openocd_thread(int argc, char *argv[], struct command_context *cmd_ct | |||
if (init_at_startup) { | |||
ret = command_run_line(cmd_ctx, "init"); | |||
if (ERROR_OK != ret) | |||
if (ERROR_OK != ret) { | |||
server_quit(); | |||
return ERROR_FAIL; | |||
} | |||
} | |||
ret = server_loop(cmd_ctx); | |||
@@ -330,6 +339,9 @@ int openocd_main(int argc, char *argv[]) | |||
if (ioutil_init(cmd_ctx) != ERROR_OK) | |||
return EXIT_FAILURE; | |||
if (rtt_init() != ERROR_OK) | |||
return EXIT_FAILURE; | |||
LOG_OUTPUT("For bug reports, read\n\t" | |||
"http://openocd.org/doc/doxygen/bugs.html" | |||
"\n"); | |||
@@ -347,6 +359,8 @@ int openocd_main(int argc, char *argv[]) | |||
adapter_quit(); | |||
rtt_exit(); | |||
if (ERROR_FAIL == ret) | |||
return EXIT_FAILURE; | |||
else if (ERROR_OK != ret) | |||
@@ -0,0 +1,2 @@ | |||
noinst_LTLIBRARIES += %D%/librtt.la | |||
%C%_librtt_la_SOURCES = %D%/rtt.c %D%/rtt.h |
@@ -0,0 +1,516 @@ | |||
/* | |||
* Copyright (C) 2016-2017 by Marc Schink | |||
* openocd-dev@marcschink.de | |||
* | |||
* 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, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include <string.h> | |||
#include <helper/log.h> | |||
#include <helper/list.h> | |||
#include <target/target.h> | |||
#include <target/rtt.h> | |||
#include "rtt.h" | |||
static struct rtt_source global_source; | |||
static struct rtt_control global_ctrl; | |||
static struct target *global_target; | |||
static target_addr_t global_addr; | |||
static uint32_t global_length; | |||
static char global_id[RTT_MAX_CB_ID_LENGTH]; | |||
static size_t global_id_length; | |||
static bool global_configured; | |||
static bool global_started; | |||
static bool global_changed; | |||
static bool global_found_cb; | |||
static struct rtt_sink_list **global_sink_list; | |||
static size_t global_sink_list_length; | |||
int rtt_init(void) | |||
{ | |||
global_sink_list_length = 1; | |||
global_sink_list = calloc(global_sink_list_length, | |||
sizeof(struct rtt_sink_list *)); | |||
if (!global_sink_list) | |||
return ERROR_FAIL; | |||
global_sink_list[0] = NULL; | |||
global_started = false; | |||
return ERROR_OK; | |||
} | |||
int rtt_exit(void) | |||
{ | |||
free(global_sink_list); | |||
return ERROR_OK; | |||
} | |||
static int read_channel_callback(void *user_data) | |||
{ | |||
int ret; | |||
ret = global_source.read(&global_ctrl, global_sink_list, | |||
global_sink_list_length, global_target, NULL); | |||
if (ret != ERROR_OK) { | |||
target_unregister_timer_callback(&read_channel_callback, NULL); | |||
global_source.stop(global_target, NULL); | |||
return ret; | |||
} | |||
return ERROR_OK; | |||
} | |||
int rtt_register_source(const struct rtt_source source, struct target *target) | |||
{ | |||
global_source = source; | |||
global_target = target; | |||
return ERROR_OK; | |||
} | |||
int rtt_start(void) | |||
{ | |||
int ret; | |||
target_addr_t addr = global_addr; | |||
if (global_started) { | |||
LOG_INFO("RTT already started"); | |||
return ERROR_OK; | |||
} | |||
if (!global_found_cb || global_changed) { | |||
global_source.find_cb(&addr, global_length, global_id, | |||
global_id_length, &global_found_cb, global_target, NULL); | |||
global_changed = false; | |||
if (global_found_cb) { | |||
LOG_INFO("RTT control block found at 0x%" TARGET_PRIxADDR, addr); | |||
global_ctrl.address = addr; | |||
} else { | |||
LOG_INFO("No RTT control block found"); | |||
return ERROR_OK; | |||
} | |||
} | |||
ret = global_source.read_cb(global_ctrl.address, &global_ctrl, | |||
global_target, NULL); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
ret = global_source.start(&global_ctrl, global_target, NULL); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
target_register_timer_callback(&read_channel_callback, 100, 1, NULL); | |||
global_started = true; | |||
return ERROR_OK; | |||
} | |||
int rtt_stop(void) | |||
{ | |||
int ret; | |||
target_unregister_timer_callback(&read_channel_callback, NULL); | |||
global_started = false; | |||
ret = global_source.stop(global_target, NULL); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
return ERROR_OK; | |||
} | |||
static int adjust_sink_list(size_t length) | |||
{ | |||
size_t i; | |||
struct rtt_sink_list **tmp; | |||
if (length <= global_sink_list_length) | |||
return ERROR_OK; | |||
tmp = realloc(global_sink_list, sizeof(struct rtt_sink_list *) * length); | |||
if (!tmp) | |||
return ERROR_FAIL; | |||
for (i = global_sink_list_length; i < length; i++) | |||
tmp[i] = NULL; | |||
global_sink_list = tmp; | |||
global_sink_list_length = length; | |||
return ERROR_OK; | |||
} | |||
int rtt_register_sink(unsigned int channel, rtt_sink_read read, | |||
void *user_data) | |||
{ | |||
struct rtt_sink_list *tmp; | |||
if (channel >= global_sink_list_length) { | |||
if (adjust_sink_list(channel + 1) != ERROR_OK) | |||
return ERROR_FAIL; | |||
} | |||
LOG_DEBUG("Registering sink for RTT channel %u", channel); | |||
tmp = malloc(sizeof(struct rtt_sink_list)); | |||
if (!tmp) | |||
return ERROR_FAIL; | |||
tmp->read = read; | |||
tmp->user_data = user_data; | |||
tmp->next = global_sink_list[channel]; | |||
global_sink_list[channel] = tmp; | |||
return ERROR_OK; | |||
} | |||
int rtt_unregister_sink(unsigned int channel, rtt_sink_read read, | |||
void *user_data) | |||
{ | |||
struct rtt_sink_list *sink; | |||
struct rtt_sink_list *prev_sink; | |||
LOG_DEBUG("Unregistering sink for RTT channel %u", channel); | |||
if (channel >= global_sink_list_length) | |||
return ERROR_FAIL; | |||
prev_sink = global_sink_list[channel]; | |||
for (sink = global_sink_list[channel]; sink; prev_sink = sink, | |||
sink = sink->next) { | |||
if (sink->read == read && sink->user_data == user_data) { | |||
if (sink == global_sink_list[channel]) | |||
global_sink_list[channel] = sink->next; | |||
else | |||
prev_sink->next = sink->next; | |||
free(sink); | |||
return ERROR_OK; | |||
} | |||
} | |||
return ERROR_OK; | |||
} | |||
int rtt_write_channel(unsigned int channel, const uint8_t *buffer, | |||
size_t *length) | |||
{ | |||
if (!global_source.write) | |||
return ERROR_FAIL; | |||
if (channel >= global_ctrl.num_up_buffers) { | |||
LOG_WARNING("Down-channel %u is not available", channel); | |||
return ERROR_OK; | |||
} | |||
return global_source.write(&global_ctrl, channel, buffer, length, | |||
global_target, NULL); | |||
} | |||
COMMAND_HANDLER(handle_rtt_setup_command) | |||
{ | |||
target_addr_t addr; | |||
uint32_t length; | |||
struct rtt_source source; | |||
if (CMD_ARGC != 3) | |||
return ERROR_COMMAND_SYNTAX_ERROR; | |||
COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[0], addr); | |||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], length); | |||
global_id_length = strlen(CMD_ARGV[2]); | |||
if (!global_id_length || global_id_length > RTT_MAX_CB_ID_LENGTH) { | |||
LOG_ERROR("Invalid RTT control block ID"); | |||
return ERROR_COMMAND_ARGUMENT_INVALID; | |||
} | |||
source.find_cb = &target_rtt_find_control_block; | |||
source.read_cb = &target_rtt_read_control_block; | |||
source.start = &target_rtt_start; | |||
source.stop = &target_rtt_stop; | |||
source.read = &target_rtt_read_callback; | |||
source.write = &target_rtt_write_callback; | |||
source.read_buffer_info = &target_rtt_read_buffer_info; | |||
rtt_register_source(source, get_current_target(CMD_CTX)); | |||
global_addr = addr; | |||
global_length = length; | |||
memcpy(global_id, CMD_ARGV[2], global_id_length); | |||
global_changed = true; | |||
global_configured = true; | |||
return ERROR_OK; | |||
} | |||
COMMAND_HANDLER(handle_rtt_start_command) | |||
{ | |||
int ret; | |||
if (CMD_ARGC > 0) | |||
return ERROR_COMMAND_SYNTAX_ERROR; | |||
if (global_started) { | |||
LOG_INFO("RTT already started"); | |||
return ERROR_OK; | |||
} | |||
if (!global_configured) { | |||
LOG_ERROR("RTT is not configured"); | |||
return ERROR_FAIL; | |||
} | |||
ret = rtt_start(); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
return ERROR_OK; | |||
} | |||
COMMAND_HANDLER(handle_rtt_stop_command) | |||
{ | |||
int ret; | |||
if (CMD_ARGC > 0) | |||
return ERROR_COMMAND_SYNTAX_ERROR; | |||
ret = rtt_stop(); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
return ERROR_OK; | |||
} | |||
COMMAND_HANDLER(handle_rtt_channels_command) | |||
{ | |||
int ret; | |||
size_t i; | |||
char channel_name[32]; | |||
struct rtt_buffer_info info; | |||
if (!global_found_cb) { | |||
LOG_ERROR("RTT control block not available"); | |||
return ERROR_FAIL; | |||
} | |||
command_print(CMD_CTX, "Channels: up=%u, down=%u", | |||
global_ctrl.num_up_buffers, global_ctrl.num_down_buffers); | |||
LOG_INFO("Up-channels:"); | |||
info.name = channel_name; | |||
info.name_length = sizeof(channel_name); | |||
for (i = 0; i < global_ctrl.num_up_buffers; i++) { | |||
ret = global_source.read_buffer_info(&global_ctrl, i, true, &info, | |||
global_target, NULL); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
if (!info.size) | |||
continue; | |||
LOG_INFO("%zu: %s %u %u", i, info.name, info.size, info.flags); | |||
} | |||
LOG_INFO("Down-channels:"); | |||
for (i = 0; i < global_ctrl.num_down_buffers; i++) { | |||
ret = global_source.read_buffer_info(&global_ctrl, i, false, &info, | |||
global_target, NULL); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
if (!info.size) | |||
continue; | |||
LOG_INFO("%zu: %s %u %u", i, info.name, info.size, info.flags); | |||
} | |||
return ERROR_OK; | |||
} | |||
static int jim_channel_list(Jim_Interp *interp, int argc, | |||
Jim_Obj * const *argv) | |||
{ | |||
int ret; | |||
size_t i; | |||
Jim_Obj *list; | |||
Jim_Obj *channel_list; | |||
char channel_name[128]; | |||
struct rtt_buffer_info info; | |||
if (!global_found_cb) { | |||
LOG_ERROR("RTT control block not available"); | |||
return ERROR_FAIL; | |||
} | |||
info.name = channel_name; | |||
info.name_length = sizeof(channel_name); | |||
list = Jim_NewListObj(interp, NULL, 0); | |||
channel_list = Jim_NewListObj(interp, NULL, 0); | |||
for (i = 0; i < global_ctrl.num_up_buffers; i++) { | |||
ret = global_source.read_buffer_info(&global_ctrl, i, true, &info, | |||
global_target, NULL); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
if (!info.size) | |||
continue; | |||
Jim_Obj *tmp = Jim_NewListObj(interp, NULL, 0); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, | |||
"name", -1)); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, | |||
info.name, -1)); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, | |||
"size", -1)); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp, | |||
info.size)); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, | |||
"flags", -1)); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp, | |||
info.flags)); | |||
Jim_ListAppendElement(interp, channel_list, tmp); | |||
} | |||
Jim_ListAppendElement(interp, list, channel_list); | |||
channel_list = Jim_NewListObj(interp, NULL, 0); | |||
for (i = 0; i < global_ctrl.num_down_buffers; i++) { | |||
ret = global_source.read_buffer_info(&global_ctrl, i, false, &info, | |||
global_target, NULL); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
if (!info.size) | |||
continue; | |||
Jim_Obj *tmp = Jim_NewListObj(interp, NULL, 0); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, | |||
"name", -1)); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, | |||
info.name, -1)); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, | |||
"size", -1)); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp, | |||
info.size)); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewStringObj(interp, | |||
"flags", -1)); | |||
Jim_ListAppendElement(interp, tmp, Jim_NewIntObj(interp, | |||
info.flags)); | |||
Jim_ListAppendElement(interp, channel_list, tmp); | |||
} | |||
Jim_ListAppendElement(interp, list, channel_list); | |||
Jim_SetResult(interp, list); | |||
return JIM_OK; | |||
} | |||
static const struct command_registration rtt_subcommand_handlers[] = { | |||
{ | |||
.name = "setup", | |||
.handler = handle_rtt_setup_command, | |||
.mode = COMMAND_ANY, | |||
.help = "setup RTT", | |||
.usage = "<address> <length> <ID>" | |||
}, | |||
{ | |||
.name = "start", | |||
.handler = handle_rtt_start_command, | |||
.mode = COMMAND_EXEC, | |||
.help = "start RTT", | |||
.usage = "" | |||
}, | |||
{ | |||
.name = "stop", | |||
.handler = handle_rtt_stop_command, | |||
.mode = COMMAND_EXEC, | |||
.help = "stop RTT", | |||
.usage = "" | |||
}, | |||
{ | |||
.name = "channels", | |||
.handler = handle_rtt_channels_command, | |||
.mode = COMMAND_EXEC, | |||
.help = "list available channels", | |||
.usage = "" | |||
}, | |||
{ | |||
.name = "channellist", | |||
.jim_handler = jim_channel_list, | |||
.mode = COMMAND_EXEC, | |||
.help = "list available channels", | |||
.usage = "" | |||
}, | |||
COMMAND_REGISTRATION_DONE | |||
}; | |||
static const struct command_registration rtt_command_handlers[] = { | |||
{ | |||
.name = "rtt", | |||
.mode = COMMAND_EXEC, | |||
.help = "RTT commands", | |||
.usage = "", | |||
.chain = rtt_subcommand_handlers | |||
}, | |||
COMMAND_REGISTRATION_DONE | |||
}; | |||
int rtt_register_commands(struct command_context *ctx) | |||
{ | |||
return register_commands(ctx, NULL, rtt_command_handlers); | |||
} |
@@ -0,0 +1,119 @@ | |||
/* | |||
* Copyright (C) 2016-2017 by Marc Schink | |||
* openocd-dev@marcschink.de | |||
* | |||
* 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, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef OPENOCD_RTT_RTT_H | |||
#define OPENOCD_RTT_RTT_H | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include <helper/command.h> | |||
#include <target/target.h> | |||
#define RTT_MAX_CB_ID_LENGTH 16 | |||
#define RTT_MIN_BUFFER_SIZE 2 | |||
#define RTT_CB_LENGTH (RTT_MAX_CB_ID_LENGTH + 4 + 4) | |||
#define RTT_BUFFER_LENGTH 24 | |||
struct rtt_control { | |||
target_addr_t address; | |||
char id[RTT_MAX_CB_ID_LENGTH + 1]; | |||
uint32_t num_up_buffers; | |||
uint32_t num_down_buffers; | |||
}; | |||
struct rtt_buffer { | |||
target_addr_t address; | |||
target_addr_t name_addr; | |||
target_addr_t buffer_addr; | |||
uint32_t size; | |||
uint32_t write_offset; | |||
uint32_t read_offset; | |||
uint32_t flags; | |||
}; | |||
struct rtt_buffer_info { | |||
char *name; | |||
size_t name_length; | |||
uint32_t size; | |||
uint32_t flags; | |||
}; | |||
typedef int (*rtt_sink_read)(unsigned int channel, const uint8_t *buffer, | |||
size_t length, void *user_data); | |||
struct rtt_sink_list { | |||
rtt_sink_read read; | |||
void *user_data; | |||
struct rtt_sink_list *next; | |||
}; | |||
enum rtt_channel_type { | |||
RTT_CHANNEL_TYPE_UP, | |||
RTT_CHANNEL_TYPE_DOWN | |||
}; | |||
typedef int (*rtt_source_find_ctrl_block)(target_addr_t *address, | |||
size_t length, const char *id, size_t id_length, bool *found, | |||
struct target *target, void *user_data); | |||
typedef int (*rtt_source_read_ctrl_block)(target_addr_t address, | |||
struct rtt_control *ctrl_block, struct target *target, | |||
void *user_data); | |||
typedef int (*rtt_source_read_buffer_info)(const struct rtt_control *ctrl, | |||
unsigned int channel, enum rtt_channel_type type, | |||
struct rtt_buffer_info *info, struct target *target, void *user_data); | |||
typedef int (*rtt_source_start)(const struct rtt_control *ctrl, | |||
struct target *target, void *user_data); | |||
typedef int (*rtt_source_stop)(struct target *target, void *user_data); | |||
typedef int (*rtt_source_read)(const struct rtt_control *ctrl, | |||
struct rtt_sink_list **sinks, size_t num_channels, | |||
struct target *target, void *user_data); | |||
typedef int (*rtt_source_write)(struct rtt_control *ctrl, | |||
unsigned int channel, const uint8_t *buffer, size_t *length, | |||
struct target *target, void *user_data); | |||
struct rtt_source { | |||
rtt_source_find_ctrl_block find_cb; | |||
rtt_source_read_ctrl_block read_cb; | |||
rtt_source_read_buffer_info read_buffer_info; | |||
rtt_source_start start; | |||
rtt_source_stop stop; | |||
rtt_source_read read; | |||
rtt_source_write write; | |||
}; | |||
int rtt_init(void); | |||
int rtt_exit(void); | |||
int rtt_register_source(const struct rtt_source source, struct target *target); | |||
int rtt_start(void); | |||
int rtt_stop(void); | |||
int rtt_register_sink(unsigned int channel, rtt_sink_read read, | |||
void *user_data); | |||
int rtt_unregister_sink(unsigned int channel, rtt_sink_read read, | |||
void *user_data); | |||
int rtt_write_channel(unsigned int channel, const uint8_t *buffer, | |||
size_t *length); | |||
int rtt_register_commands(struct command_context *ctx); | |||
#endif /* OPENOCD_RTT_RTT_H */ |
@@ -8,7 +8,9 @@ noinst_LTLIBRARIES += %D%/libserver.la | |||
%D%/gdb_server.h \ | |||
%D%/server_stubs.c \ | |||
%D%/tcl_server.c \ | |||
%D%/tcl_server.h | |||
%D%/tcl_server.h \ | |||
%D%/rtt_server.c \ | |||
%D%/rtt_server.h | |||
%C%_libserver_la_CFLAGS = $(AM_CFLAGS) | |||
if IS_MINGW | |||
@@ -0,0 +1,182 @@ | |||
/* | |||
* Copyright (C) 2016-2017 by Marc Schink | |||
* openocd-dev@marcschink.de | |||
* | |||
* 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, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <stdint.h> | |||
#include <rtt/rtt.h> | |||
#include "server.h" | |||
#include "rtt_server.h" | |||
/** | |||
* @file | |||
* | |||
* RTT server. | |||
* | |||
* This server allows access to Real Time Transfer (RTT) channels via TCP | |||
* connections. | |||
*/ | |||
struct rtt_service { | |||
unsigned int channel; | |||
}; | |||
static int read_callback(unsigned int channel, const uint8_t *buffer, | |||
size_t length, void *user_data) | |||
{ | |||
int ret; | |||
struct connection *connection; | |||
size_t offset; | |||
connection = (struct connection *)user_data; | |||
offset = 0; | |||
while (offset < length) { | |||
ret = connection_write(connection, buffer + offset, length - offset); | |||
if (ret < 0) { | |||
LOG_ERROR("Failed to write data to socket."); | |||
return ERROR_FAIL; | |||
} | |||
offset += ret; | |||
} | |||
return ERROR_OK; | |||
} | |||
static int rtt_new_connection(struct connection *connection) | |||
{ | |||
int ret; | |||
struct rtt_service *service; | |||
service = connection->service->priv; | |||
LOG_DEBUG("New connection for RTT channel %u", service->channel); | |||
ret = rtt_register_sink(service->channel, &read_callback, connection); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
return ERROR_OK; | |||
} | |||
static int rtt_connection_closed(struct connection *connection) | |||
{ | |||
struct rtt_service *service; | |||
service = (struct rtt_service *)connection->service->priv; | |||
rtt_unregister_sink(service->channel, &read_callback, connection); | |||
LOG_DEBUG("Connection for RTT channel %u closed", service->channel); | |||
return ERROR_OK; | |||
} | |||
static int rtt_input(struct connection *connection) | |||
{ | |||
int bytes_read; | |||
unsigned char buffer[1024]; | |||
struct rtt_service *service; | |||
size_t length; | |||
service = (struct rtt_service *)connection->service->priv; | |||
bytes_read = connection_read(connection, buffer, sizeof(buffer)); | |||
if (!bytes_read) | |||
return ERROR_SERVER_REMOTE_CLOSED; | |||
else if (bytes_read < 0) { | |||
LOG_ERROR("error during read: %s", strerror(errno)); | |||
return ERROR_SERVER_REMOTE_CLOSED; | |||
} | |||
length = bytes_read; | |||
rtt_write_channel(service->channel, buffer, &length); | |||
return ERROR_OK; | |||
} | |||
COMMAND_HANDLER(handle_rtt_start_command) | |||
{ | |||
int ret; | |||
struct rtt_service *service; | |||
if (CMD_ARGC != 2) | |||
return ERROR_COMMAND_SYNTAX_ERROR; | |||
service = malloc(sizeof(struct rtt_service)); | |||
if (!service) | |||
return ERROR_FAIL; | |||
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], service->channel); | |||
ret = add_service("RTT", CMD_ARGV[0], CONNECTION_LIMIT_UNLIMITED, | |||
rtt_new_connection, rtt_input, rtt_connection_closed, service); | |||
if (ret != ERROR_OK) { | |||
free(service); | |||
return ERROR_FAIL; | |||
} | |||
return ERROR_OK; | |||
} | |||
COMMAND_HANDLER(handle_rtt_stop_command) | |||
{ | |||
if (CMD_ARGC != 1) | |||
return ERROR_COMMAND_SYNTAX_ERROR; | |||
remove_service("RTT", CMD_ARGV[0]); | |||
return ERROR_OK; | |||
} | |||
static const struct command_registration rtt_subcommand_handlers[] = { | |||
{ | |||
.name = "start", | |||
.handler = handle_rtt_start_command, | |||
.mode = COMMAND_ANY, | |||
.help = "Start a RTT server", | |||
.usage = "<port> <channel>" | |||
}, | |||
{ | |||
.name = "stop", | |||
.handler = handle_rtt_stop_command, | |||
.mode = COMMAND_ANY, | |||
.help = "Stop a RTT server", | |||
.usage = "<port>" | |||
}, | |||
COMMAND_REGISTRATION_DONE | |||
}; | |||
static const struct command_registration rtt_command_handlers[] = { | |||
{ | |||
.name = "rttserver", | |||
.mode = COMMAND_ANY, | |||
.help = "RTT server", | |||
.usage = "", | |||
.chain = rtt_subcommand_handlers | |||
}, | |||
COMMAND_REGISTRATION_DONE | |||
}; | |||
int rtt_server_register_commands(struct command_context *ctx) | |||
{ | |||
return register_commands(ctx, NULL, rtt_command_handlers); | |||
} |
@@ -0,0 +1,26 @@ | |||
/* | |||
* Copyright (C) 2016-2017 by Marc Schink | |||
* openocd-dev@marcschink.de | |||
* | |||
* 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, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef OPENOCD_SERVER_RTT_SERVER_H | |||
#define OPENOCD_SERVER_RTT_SERVER_H | |||
#include <helper/command.h> | |||
int rtt_server_register_commands(struct command_context *ctx); | |||
#endif /* OPENOCD_SERVER_RTT_SERVER_H */ |
@@ -67,10 +67,23 @@ static int add_connection(struct service *service, struct command_context *cmd_c | |||
int flag = 1; | |||
c = malloc(sizeof(struct connection)); | |||
if (!c) { | |||
LOG_ERROR("Failed to allocate connection."); | |||
return ERROR_FAIL; | |||
} | |||
c->cmd_ctx = copy_command_context(cmd_ctx); | |||
if (!c->cmd_ctx) { | |||
LOG_ERROR("Failed to copy command context."); | |||
free(c); | |||
return ERROR_FAIL; | |||
} | |||
c->fd = -1; | |||
c->fd_out = -1; | |||
memset(&c->sin, 0, sizeof(c->sin)); | |||
c->cmd_ctx = copy_command_context(cmd_ctx); | |||
c->service = service; | |||
c->input_pending = 0; | |||
c->priv = NULL; | |||
@@ -132,7 +145,9 @@ static int add_connection(struct service *service, struct command_context *cmd_c | |||
free(out_file); | |||
if (c->fd_out == -1) { | |||
LOG_ERROR("could not open %s", service->port); | |||
exit(1); | |||
command_done(c->cmd_ctx); | |||
free(c); | |||
return ERROR_FAIL; | |||
} | |||
LOG_INFO("accepting '%s' connection from pipe %s", service->name, service->port); | |||
@@ -191,7 +206,13 @@ static int remove_connection(struct service *service, struct connection *connect | |||
return ERROR_OK; | |||
} | |||
/* FIX! make service return error instead of invoking exit() */ | |||
static void free_service(struct service *c) | |||
{ | |||
free(c->name); | |||
free(c->port); | |||
free(c); | |||
} | |||
int add_service(char *name, | |||
const char *port, | |||
int max_connections, | |||
@@ -235,7 +256,8 @@ int add_service(char *name, | |||
c->fd = socket(AF_INET, SOCK_STREAM, 0); | |||
if (c->fd == -1) { | |||
LOG_ERROR("error creating socket: %s", strerror(errno)); | |||
exit(-1); | |||
free_service(c); | |||
return ERROR_FAIL; | |||
} | |||
setsockopt(c->fd, | |||
@@ -255,7 +277,9 @@ int add_service(char *name, | |||
hp = gethostbyname(bindto_name); | |||
if (hp == NULL) { | |||
LOG_ERROR("couldn't resolve bindto address: %s", bindto_name); | |||
exit(-1); | |||
close_socket(c->fd); | |||
free_service(c); | |||
return ERROR_FAIL; | |||
} | |||
memcpy(&c->sin.sin_addr, hp->h_addr_list[0], hp->h_length); | |||
} | |||
@@ -263,7 +287,9 @@ int add_service(char *name, | |||
if (bind(c->fd, (struct sockaddr *)&c->sin, sizeof(c->sin)) == -1) { | |||
LOG_ERROR("couldn't bind %s to socket: %s", name, strerror(errno)); | |||
exit(-1); | |||
close_socket(c->fd); | |||
free_service(c); | |||
return ERROR_FAIL; | |||
} | |||
#ifndef _WIN32 | |||
@@ -281,7 +307,9 @@ int add_service(char *name, | |||
if (listen(c->fd, 1) == -1) { | |||
LOG_ERROR("couldn't listen on socket: %s", strerror(errno)); | |||
exit(-1); | |||
close_socket(c->fd); | |||
free_service(c); | |||
return ERROR_FAIL; | |||
} | |||
} else if (c->type == CONNECTION_STDINOUT) { | |||
c->fd = fileno(stdin); | |||
@@ -302,13 +330,15 @@ int add_service(char *name, | |||
/* we currenty do not support named pipes under win32 | |||
* so exit openocd for now */ | |||
LOG_ERROR("Named pipes currently not supported under this os"); | |||
exit(1); | |||
free_service(c); | |||
return ERROR_FAIL; | |||
#else | |||
/* Pipe we're reading from */ | |||
c->fd = open(c->port, O_RDONLY | O_NONBLOCK); | |||
if (c->fd == -1) { | |||
LOG_ERROR("could not open %s", c->port); | |||
exit(1); | |||
free_service(c); | |||
return ERROR_FAIL; | |||
} | |||
#endif | |||
} | |||
@@ -321,6 +351,50 @@ int add_service(char *name, | |||
return ERROR_OK; | |||
} | |||
static void remove_connections(struct service *service) | |||
{ | |||
struct connection *connection; | |||
connection = service->connections; | |||
while (connection) { | |||
struct connection *tmp; | |||
tmp = connection->next; | |||
remove_connection(service, connection); | |||
connection = tmp; | |||
} | |||
} | |||
int remove_service(const char *name, const char *port) | |||
{ | |||
struct service *tmp; | |||
struct service *prev; | |||
prev = services; | |||
for (tmp = services; tmp; prev = tmp, tmp = tmp->next) { | |||
if (!strcmp(tmp->name, name) && !strcmp(tmp->port, port)) { | |||
remove_connections(tmp); | |||
if (tmp == services) | |||
services = tmp->next; | |||
else | |||
prev->next = tmp->next; | |||
if (tmp->type != CONNECTION_STDINOUT) | |||
close_socket(tmp->fd); | |||
free(tmp->priv); | |||
free_service(tmp); | |||
return ERROR_OK; | |||
} | |||
} | |||
return ERROR_OK; | |||
} | |||
static int remove_services(void) | |||
{ | |||
struct service *c = services; | |||
@@ -329,6 +403,8 @@ static int remove_services(void) | |||
while (c) { | |||
struct service *next = c->next; | |||
remove_connections(c); | |||
if (c->name) | |||
free(c->name); | |||
@@ -425,7 +501,7 @@ int server_loop(struct command_context *command_context) | |||
FD_ZERO(&read_fds); | |||
else { | |||
LOG_ERROR("error during select: %s", strerror(errno)); | |||
exit(-1); | |||
return ERROR_FAIL; | |||
} | |||
#else | |||
@@ -433,7 +509,7 @@ int server_loop(struct command_context *command_context) | |||
FD_ZERO(&read_fds); | |||
else { | |||
LOG_ERROR("error during select: %s", strerror(errno)); | |||
exit(-1); | |||
return ERROR_FAIL; | |||
} | |||
#endif | |||
} | |||
@@ -463,11 +539,15 @@ int server_loop(struct command_context *command_context) | |||
for (service = services; service; service = service->next) { | |||
/* handle new connections on listeners */ | |||
if ((service->fd != -1) | |||
&& (FD_ISSET(service->fd, &read_fds))) { | |||
if (service->max_connections != 0) | |||
add_connection(service, command_context); | |||
else { | |||
if ((service->fd != -1) && (FD_ISSET(service->fd, &read_fds))) { | |||
if (service->max_connections != 0) { | |||
retval = add_connection(service, command_context); | |||
if (retval != ERROR_OK) { | |||
LOG_ERROR("%i", retval); | |||
return ERROR_FAIL; | |||
} | |||
} else { | |||
if (service->type == CONNECTION_TCP) { | |||
struct sockaddr_in sin; | |||
socklen_t address_size = sizeof(sin); | |||
@@ -552,7 +632,7 @@ int server_preinit(void) | |||
if (WSAStartup(wVersionRequested, &wsaData) != 0) { | |||
LOG_ERROR("Failed to Open Winsock"); | |||
exit(-1); | |||
return ERROR_FAIL; | |||
} | |||
/* register ctrl-c handler */ | |||
@@ -569,11 +649,21 @@ int server_preinit(void) | |||
int server_init(struct command_context *cmd_ctx) | |||
{ | |||
int ret = tcl_init(); | |||
if (ERROR_OK != ret) | |||
int ret; | |||
ret = tcl_init(); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
ret = telnet_init("Open On-Chip Debugger"); | |||
if (ret != ERROR_OK) { | |||
remove_services(); | |||
return ret; | |||
} | |||
return telnet_init("Open On-Chip Debugger"); | |||
return ERROR_OK; | |||
} | |||
int server_quit(void) | |||
@@ -25,6 +25,10 @@ | |||
#ifndef OPENOCD_SERVER_SERVER_H | |||
#define OPENOCD_SERVER_SERVER_H | |||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include <helper/log.h> | |||
#ifdef HAVE_NETINET_IN_H | |||
@@ -74,6 +78,7 @@ int add_service(char *name, const char *port, | |||
int max_connections, new_connection_handler_t new_connection_handler, | |||
input_handler_t in_handler, connection_closed_handler_t close_handler, | |||
void *priv); | |||
int remove_service(const char *name, const char *port); | |||
int server_preinit(void); | |||
int server_init(struct command_context *cmd_ctx); | |||
@@ -173,7 +173,7 @@ static int tcl_new_connection(struct connection *connection) | |||
static int tcl_input(struct connection *connection) | |||
{ | |||
Jim_Interp *interp = (Jim_Interp *)connection->cmd_ctx->interp; | |||
Jim_Interp *interp; | |||
int retval; | |||
int i; | |||
ssize_t rlen; | |||
@@ -184,6 +184,8 @@ static int tcl_input(struct connection *connection) | |||
char *tc_line_new; | |||
int tc_line_size_new; | |||
interp = connection->cmd_ctx->interp->interp; | |||
rlen = connection_read(connection, &in, sizeof(in)); | |||
if (rlen <= 0) { | |||
if (rlen < 0) | |||
@@ -619,13 +619,14 @@ static int telnet_connection_closed(struct connection *connection) | |||
int telnet_init(char *banner) | |||
{ | |||
int ret; | |||
struct telnet_service *telnet_service; | |||
if (strcmp(telnet_port, "disabled") == 0) { | |||
LOG_INFO("telnet server disabled"); | |||
return ERROR_OK; | |||
} | |||
struct telnet_service *telnet_service; | |||
telnet_service = malloc(sizeof(struct telnet_service)); | |||
if (!telnet_service) { | |||
@@ -635,13 +636,16 @@ int telnet_init(char *banner) | |||
telnet_service->banner = banner; | |||
return add_service("telnet", | |||
telnet_port, | |||
CONNECTION_LIMIT_UNLIMITED, | |||
telnet_new_connection, | |||
telnet_input, | |||
telnet_connection_closed, | |||
ret = add_service("telnet", telnet_port, CONNECTION_LIMIT_UNLIMITED, | |||
telnet_new_connection, telnet_input, telnet_connection_closed, | |||
telnet_service); | |||
if (ret != ERROR_OK) { | |||
free(telnet_service); | |||
return ret; | |||
} | |||
return ERROR_OK; | |||
} | |||
/* daemon configuration command telnet_port */ | |||
@@ -38,7 +38,8 @@ TARGET_CORE_SRC = \ | |||
%D%/target.c \ | |||
%D%/target_request.c \ | |||
%D%/testee.c \ | |||
%D%/smp.c | |||
%D%/smp.c \ | |||
%D%/rtt.c | |||
ARMV4_5_SRC = \ | |||
%D%/armv4_5.c \ | |||
@@ -207,6 +208,7 @@ INTEL_IA32_SRC = \ | |||
%D%/nds32_aice.h \ | |||
%D%/lakemont.h \ | |||
%D%/x86_32_common.h \ | |||
%D%/arm_cti.h | |||
%D%/arm_cti.h \ | |||
%D%/rtt.h | |||
include %D%/openrisc/Makefile.am |
@@ -1470,7 +1470,7 @@ static int dsp563xx_get_default_memory(void) | |||
if (!global_cmd_ctx) | |||
return MEM_P; | |||
interp = global_cmd_ctx->interp; | |||
interp = global_cmd_ctx->interp->interp; | |||
if (!interp) | |||
return MEM_P; | |||
@@ -0,0 +1,412 @@ | |||
/* | |||
* Copyright (C) 2016-2017 by Marc Schink | |||
* openocd-dev@marcschink.de | |||
* | |||
* 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, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <helper/log.h> | |||
#include <helper/binarybuffer.h> | |||
#include <helper/command.h> | |||
#include <rtt/rtt.h> | |||
#include "target.h" | |||
static uint8_t rtt_buffer[1024]; | |||
static int read_rtt_buffer(struct target *target, | |||
const struct rtt_control *ctrl, unsigned int channel, | |||
enum rtt_channel_type type, struct rtt_buffer *buffer) | |||
{ | |||
int ret; | |||
uint8_t buf[RTT_BUFFER_LENGTH]; | |||
target_addr_t address; | |||
address = ctrl->address + RTT_CB_LENGTH + (channel * RTT_BUFFER_LENGTH); | |||
if (type == RTT_CHANNEL_TYPE_DOWN) | |||
address += ctrl->num_up_buffers * RTT_BUFFER_LENGTH; | |||
ret = target_read_buffer(target, address, RTT_BUFFER_LENGTH, buf); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
buffer->address = address; | |||
buffer->name_addr = buf_get_u32(buf, 0, 32); | |||
buffer->buffer_addr = buf_get_u32(buf + 4, 0, 32); | |||
buffer->size = buf_get_u32(buf + 8, 0, 32); | |||
buffer->write_offset = buf_get_u32(buf + 12, 0, 32); | |||
buffer->read_offset = buf_get_u32(buf + 16, 0, 32); | |||
buffer->flags = buf_get_u32(buf + 20, 0, 32); | |||
return ERROR_OK; | |||
} | |||
int target_rtt_start(const struct rtt_control *ctrl, struct target *target, | |||
void *user_data) | |||
{ | |||
return ERROR_OK; | |||
} | |||
int target_rtt_stop(struct target *target, void *user_data) | |||
{ | |||
return ERROR_OK; | |||
} | |||
static int read_buffer_name(struct target *target, target_addr_t address, | |||
char *name, size_t length) | |||
{ | |||
size_t offset; | |||
offset = 0; | |||
while (offset < length) { | |||
int ret; | |||
size_t tmp; | |||
tmp = MIN(32, length - offset); | |||
ret = target_read_buffer(target, address + offset, tmp, | |||
(uint8_t *)name + offset); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
if (memchr(name + offset, '\0', tmp)) | |||
return ERROR_OK; | |||
offset += tmp; | |||
} | |||
name[length - 1] = '\0'; | |||
return ERROR_OK; | |||
} | |||
static int write_to_channel(struct target *target, | |||
const struct rtt_buffer *rttbuf, const uint8_t *buffer, size_t *length) | |||
{ | |||
int ret; | |||
uint32_t len; | |||
if (!*length) | |||
return ERROR_OK; | |||
if (rttbuf->write_offset == rttbuf->read_offset) { | |||
uint32_t first_length; | |||
len = MIN(*length, rttbuf->size - 1); | |||
first_length = MIN(len, rttbuf->size - rttbuf->write_offset); | |||
ret = target_write_buffer(target, | |||
rttbuf->buffer_addr + rttbuf->write_offset, first_length, buffer); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
ret = target_write_buffer(target, rttbuf->buffer_addr, | |||
len - first_length, buffer + first_length); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
} else if (rttbuf->write_offset < rttbuf->read_offset) { | |||
len = MIN(*length, rttbuf->read_offset - rttbuf->write_offset - 1); | |||
if (!len) { | |||
*length = 0; | |||
return ERROR_OK; | |||
} | |||
ret = target_write_buffer(target, | |||
rttbuf->buffer_addr + rttbuf->write_offset, len, buffer); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
} else { | |||
uint32_t first_length; | |||
len = MIN(*length, | |||
rttbuf->size - rttbuf->write_offset + rttbuf->read_offset - 1); | |||
if (!len) { | |||
*length = 0; | |||
return ERROR_OK; | |||
} | |||
first_length = MIN(len, rttbuf->size - rttbuf->write_offset); | |||
ret = target_write_buffer(target, | |||
rttbuf->buffer_addr + rttbuf->write_offset, first_length, buffer); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
buffer = buffer + first_length; | |||
ret = target_write_buffer(target, rttbuf->buffer_addr, | |||
len - first_length, buffer); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
} | |||
ret = target_write_u32(target, rttbuf->address + 12, | |||
(rttbuf->write_offset + len) % rttbuf->size); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
*length = len; | |||
return ERROR_OK; | |||
} | |||
static bool buffer_is_active(const struct rtt_buffer *buf) | |||
{ | |||
if (!buf) | |||
return false; | |||
if (!buf->size) | |||
return false; | |||
return true; | |||
} | |||
int target_rtt_write_callback(struct rtt_control *ctrl, | |||
unsigned int channel, const uint8_t *buffer, size_t *length, | |||
struct target *target, void *user_data) | |||
{ | |||
int ret; | |||
struct rtt_buffer rttbuf; | |||
ret = read_rtt_buffer(target, ctrl, channel, RTT_CHANNEL_TYPE_DOWN, &rttbuf); | |||
if (ret != ERROR_OK) { | |||
LOG_ERROR("Failed to read RTT buffer of down-channel %u", channel); | |||
return ret; | |||
} | |||
if (!buffer_is_active(&rttbuf)) { | |||
LOG_WARNING("Down-channel %u is not active", channel); | |||
return ERROR_OK; | |||
} | |||
if (rttbuf.size < RTT_MIN_BUFFER_SIZE) { | |||
LOG_WARNING("Down-channel %u is not large enough", channel); | |||
return ERROR_OK; | |||
} | |||
ret = write_to_channel(target, &rttbuf, buffer, length); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
LOG_DEBUG("Wrote %zu bytes into RTT down-channel %u", *length, channel); | |||
return ERROR_OK; | |||
} | |||
int target_rtt_read_control_block(target_addr_t address, | |||
struct rtt_control *ctrl, struct target *target, void *user_data) | |||
{ | |||
int ret; | |||
uint8_t buf[RTT_CB_LENGTH]; | |||
ret = target_read_buffer(target, address, RTT_CB_LENGTH, buf); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
memcpy(ctrl->id, buf, RTT_MAX_CB_ID_LENGTH); | |||
ctrl->id[RTT_MAX_CB_ID_LENGTH] = '\0'; | |||
ctrl->num_up_buffers = buf_get_u32(buf + RTT_MAX_CB_ID_LENGTH, 0, 32); | |||
ctrl->num_down_buffers = buf_get_u32(buf + RTT_MAX_CB_ID_LENGTH + 4, 0, | |||
32); | |||
return ERROR_OK; | |||
} | |||
int target_rtt_find_control_block(target_addr_t *address, size_t length, | |||
const char *id, size_t id_length, bool *found, struct target *target, | |||
void *user_data) | |||
{ | |||
target_addr_t addr; | |||
uint8_t buf[1024]; | |||
size_t j; | |||
size_t start; | |||
*found = false; | |||
j = 0; | |||
start = 0; | |||
LOG_INFO("Searching for RTT control block '%s'", id); | |||
for (addr = 0; addr < length; addr = addr + sizeof(buf)) { | |||
int ret; | |||
size_t i; | |||
ret = target_read_buffer(target, *address + addr, sizeof(buf), buf); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
for (i = 0; i < sizeof(buf); i++) { | |||
if (buf[i] == id[j]) { | |||
j++; | |||
} else { | |||
j = 0; | |||
start = addr + i + 1; | |||
} | |||
if (j == id_length) { | |||
*address = *address + start; | |||
*found = true; | |||
return ERROR_OK; | |||
} | |||
} | |||
} | |||
return ERROR_OK; | |||
} | |||
int target_rtt_read_buffer_info(const struct rtt_control *ctrl, | |||
unsigned int channel, enum rtt_channel_type type, | |||
struct rtt_buffer_info *info, struct target *target, void *user_data) | |||
{ | |||
int ret; | |||
struct rtt_buffer rttbuf; | |||
ret = read_rtt_buffer(target, ctrl, channel, type, &rttbuf); | |||
if (ret != ERROR_OK) { | |||
LOG_ERROR("Failed to read RTT buffer of channel %u", channel); | |||
return ret; | |||
} | |||
ret = read_buffer_name(target, rttbuf.name_addr, info->name, | |||
info->name_length); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
info->size = rttbuf.size; | |||
info->flags = rttbuf.flags; | |||
return ERROR_OK; | |||
} | |||
static int read_from_channel(struct target *target, | |||
const struct rtt_buffer *rttbuf, uint8_t *buffer, size_t *length) | |||
{ | |||
int ret; | |||
uint32_t len; | |||
if (!*length) | |||
return ERROR_OK; | |||
if (rttbuf->read_offset == rttbuf->write_offset) { | |||
len = 0; | |||
} else if (rttbuf->read_offset < rttbuf->write_offset) { | |||
len = MIN(*length, rttbuf->write_offset - rttbuf->read_offset); | |||
ret = target_read_buffer(target, | |||
rttbuf->buffer_addr + rttbuf->read_offset, len, buffer); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
} else { | |||
uint32_t first_length; | |||
len = MIN(*length, | |||
rttbuf->size - rttbuf->read_offset + rttbuf->write_offset); | |||
first_length = MIN(len, rttbuf->size - rttbuf->read_offset); | |||
ret = target_read_buffer(target, | |||
rttbuf->buffer_addr + rttbuf->read_offset, first_length, buffer); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
ret = target_read_buffer(target, rttbuf->buffer_addr, | |||
len - first_length, buffer + first_length); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
} | |||
if (len > 0) { | |||
ret = target_write_u32(target, rttbuf->address + 16, | |||
(rttbuf->read_offset + len) % rttbuf->size); | |||
if (ret != ERROR_OK) | |||
return ret; | |||
} | |||
*length = len; | |||
return ERROR_OK; | |||
} | |||
int target_rtt_read_callback(const struct rtt_control *ctrl, | |||
struct rtt_sink_list **sinks, size_t num_channels, | |||
struct target *target, void *user_data) | |||
{ | |||
size_t channel; | |||
num_channels = MIN(num_channels, ctrl->num_up_buffers); | |||
for (channel = 0; channel < num_channels; channel++) { | |||
int ret; | |||
struct rtt_buffer rttbuf; | |||
size_t length; | |||
struct rtt_sink_list *tmp; | |||
if (!sinks[channel]) | |||
continue; | |||
ret = read_rtt_buffer(target, ctrl, channel, RTT_CHANNEL_TYPE_UP, | |||
&rttbuf); | |||
if (ret != ERROR_OK) { | |||
LOG_ERROR("Failed to read RTT buffer of up-channel %zu", channel); | |||
return ret; | |||
} | |||
if (!buffer_is_active(&rttbuf)) { | |||
LOG_WARNING("Up-channel %zu is not active", channel); | |||
continue; | |||
} | |||
if (rttbuf.size < RTT_MIN_BUFFER_SIZE) { | |||
LOG_WARNING("Up-channel %zu is not large enough", channel); | |||
continue; | |||
} | |||
length = sizeof(rtt_buffer); | |||
ret = read_from_channel(target, &rttbuf, rtt_buffer, &length); | |||
if (ret != ERROR_OK) { | |||
LOG_ERROR("Failed to read from RTT up-channel %zu", channel); | |||
return ret; | |||
} | |||
for (tmp = sinks[channel]; tmp; tmp = tmp->next) | |||
tmp->read(channel, rtt_buffer, length, tmp->user_data); | |||
} | |||
return ERROR_OK; | |||
} |
@@ -0,0 +1,46 @@ | |||
/* | |||
* Copyright (C) 2016-2017 by Marc Schink | |||
* openocd-dev@marcschink.de | |||
* | |||
* 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, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef OPENOCD_TARGET_RTT_H | |||
#define OPENOCD_TARGET_RTT_H | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include <target/target.h> | |||
#include <rtt/rtt.h> | |||
int target_rtt_start(const struct rtt_control *ctrl, struct target *target, | |||
void *user_data); | |||
int target_rtt_stop(struct target *target, void *user_data); | |||
int target_rtt_find_control_block(target_addr_t *address, size_t length, | |||
const char *id, size_t id_length, bool *found, struct target *target, | |||
void *uer_data); | |||
int target_rtt_read_control_block(target_addr_t address, | |||
struct rtt_control *ctrl, struct target *target, void *user_data); | |||
int target_rtt_write_callback(struct rtt_control *ctrl, | |||
unsigned int channel, const uint8_t *buffer, size_t *length, | |||
struct target *target, void *user_data); | |||
int target_rtt_read_callback(const struct rtt_control *ctrl, | |||
struct rtt_sink_list **sinks, size_t length, struct target *target, | |||
void *user_data); | |||
int target_rtt_read_buffer_info(const struct rtt_control *ctrl, | |||
unsigned int channel, enum rtt_channel_type type, | |||
struct rtt_buffer_info *info, struct target *target, void *user_data); | |||
#endif /* OPENOCD_TARGET_RTT_H */ |
@@ -651,13 +651,14 @@ static int target_process_reset(struct command_context *cmd_ctx, enum target_res | |||
jtag_poll_set_enabled(false); | |||
sprintf(buf, "ocd_process_reset %s", n->name); | |||
retval = Jim_Eval(cmd_ctx->interp, buf); | |||
retval = Jim_Eval(cmd_ctx->interp->interp, buf); | |||
jtag_poll_set_enabled(save_poll); | |||
if (retval != JIM_OK) { | |||
Jim_MakeErrorMessage(cmd_ctx->interp); | |||
command_print(NULL, "%s\n", Jim_GetString(Jim_GetResult(cmd_ctx->interp), NULL)); | |||
Jim_MakeErrorMessage(cmd_ctx->interp->interp); | |||
command_print(NULL, "%s\n", | |||
Jim_GetString(Jim_GetResult(cmd_ctx->interp->interp), NULL)); | |||
return ERROR_FAIL; | |||
} | |||
@@ -1293,7 +1294,7 @@ static int target_init(struct command_context *cmd_ctx) | |||
return retval; | |||
retval = target_register_timer_callback(&handle_target, | |||
polling_interval, 1, cmd_ctx->interp); | |||
polling_interval, 1, cmd_ctx->interp->interp); | |||
if (ERROR_OK != retval) | |||
return retval; | |||
@@ -1870,6 +1871,17 @@ int target_free_working_area(struct target *target, struct working_area *area) | |||
return target_free_working_area_restore(target, area, 1); | |||
} | |||
static void target_destroy(struct target *target) | |||
{ | |||
if (target->type->deinit_target) | |||
target->type->deinit_target(target); | |||
free(target->type); | |||
free(target->trace_info); | |||
free(target->cmd_name); | |||
free(target); | |||
} | |||
void target_quit(void) | |||
{ | |||
struct target_event_callback *pe = target_event_callbacks; | |||