diff --git a/doc/openocd.texi b/doc/openocd.texi index 65b5d65b2..cc7b6b22e 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -8423,6 +8423,94 @@ the watchpoint should trigger. The value may be first be masked using @var{mask} to mark ``don't care'' fields. @end deffn + +@section Real Time Transfer (RTT) + +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. +The specification is independent of the target architecture. +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. +This interface is especially of interest for targets without +Serial Wire Output (SWO), such as ARM Cortex-M0, or where semihosting is not +applicable because of real-time constraints. + +@quotation Note +The current implementation supports only single target devices. +@end quotation + +The data transfer between host and target device is organized through +unidirectional up/down-channels for target-to-host and host-to-target +communication, respectively. + +@quotation Note +The current implementation does not respect channel buffer flags. +They are used to determine what happens when writing to a full buffer, for +example. +@end quotation + +Channels are exposed via raw TCP/IP connections. One or more RTT servers can be +assigned to each channel to make them accessible to an unlimited number +of TCP/IP connections. + +@deffn Command {rtt setup} address size ID +Configure RTT for the currently selected target. +Once RTT is started, OpenOCD searches for a control block with the +identifier @var{ID} starting at the memory address @var{address} within the next +@var{size} bytes. +@end deffn + +@deffn Command {rtt start} +Start RTT. +If the control block location is not known, OpenOCD starts searching for it. +@end deffn + +@deffn Command {rtt stop} +Stop RTT. +@end deffn + +@deffn Command {rtt polling_interval [interval]} +Display the polling interval. +If @var{interval} is provided, set the polling interval. +The polling interval determines (in milliseconds) how often the up-channels are +checked for new data. +@end deffn + +@deffn Command {rtt channels} +Display a list of all channels and their properties. +@end deffn + +@deffn Command {rtt channellist} +Return a list of all channels and their properties as Tcl list. +The list can be manipulated easily from within scripts. +@end deffn + +@deffn Command {rtt server start} port channel +Start a TCP server on @var{port} for the channel @var{channel}. +@end deffn + +@deffn Command {rtt server stop} port +Stop the TCP sever with port @var{port}. +@end deffn + +The following example shows how to setup RTT using the SEGGER RTT implementation +on the target device. + +@example +resume + +rtt setup 0x20000000 2048 "SEGGER RTT" +rtt start + +rtt server start 9090 0 +@end example + +In this example, OpenOCD searches the control block with the ID "SEGGER RTT" +starting at 0x20000000 for 2048 bytes. The RTT channel 0 is exposed through the +TCP/IP port 9090. + + @section Misc Commands @cindex profiling diff --git a/src/Makefile.am b/src/Makefile.am index 07981aa67..781c1e74f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/openocd.c b/src/openocd.c index 604b36d21..83c35458b 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -38,9 +38,11 @@ #include #include #include +#include #include #include +#include #ifdef HAVE_STRINGS_H #include @@ -244,6 +246,7 @@ static struct command_context *setup_command_handler(Jim_Interp *interp) &server_register_commands, &gdb_register_commands, &log_register_commands, + &rtt_server_register_commands, &transport_register_commands, &interface_register_commands, &target_register_commands, @@ -335,6 +338,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"); @@ -364,6 +370,7 @@ int openocd_main(int argc, char *argv[]) /* Shutdown commandline interface */ command_exit(cmd_ctx); + rtt_exit(); free_config(); if (ERROR_FAIL == ret) diff --git a/src/rtt/Makefile.am b/src/rtt/Makefile.am new file mode 100644 index 000000000..e3fcefdbf --- /dev/null +++ b/src/rtt/Makefile.am @@ -0,0 +1,2 @@ +noinst_LTLIBRARIES += %D%/librtt.la +%C%_librtt_la_SOURCES = %D%/rtt.c %D%/rtt.h %D%/tcl.c diff --git a/src/rtt/rtt.c b/src/rtt/rtt.c new file mode 100644 index 000000000..bf3cca51b --- /dev/null +++ b/src/rtt/rtt.c @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2016-2020 by Marc Schink + * + * 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 . + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "rtt.h" + +static struct { + struct rtt_source source; + /** Control block. */ + struct rtt_control ctrl; + struct target *target; + /** Start address to search for the control block. */ + target_addr_t addr; + /** Size of the control block search area. */ + size_t size; + /** Control block identifier. */ + char id[RTT_CB_MAX_ID_LENGTH]; + /** Whether RTT is configured. */ + bool configured; + /** Whether RTT is started. */ + bool started; + /** Whether configuration changed. */ + bool changed; + /** Whether the control block was found. */ + bool found_cb; + + struct rtt_sink_list **sink_list; + size_t sink_list_length; + + unsigned int polling_interval; +} rtt; + +int rtt_init(void) +{ + rtt.sink_list_length = 1; + rtt.sink_list = calloc(rtt.sink_list_length, + sizeof(struct rtt_sink_list *)); + + if (!rtt.sink_list) + return ERROR_FAIL; + + rtt.sink_list[0] = NULL; + rtt.started = false; + + rtt.polling_interval = 100; + + return ERROR_OK; +} + +int rtt_exit(void) +{ + free(rtt.sink_list); + + return ERROR_OK; +} + +static int read_channel_callback(void *user_data) +{ + int ret; + + ret = rtt.source.read(rtt.target, &rtt.ctrl, rtt.sink_list, + rtt.sink_list_length, NULL); + + if (ret != ERROR_OK) { + target_unregister_timer_callback(&read_channel_callback, NULL); + rtt.source.stop(rtt.target, NULL); + return ret; + } + + return ERROR_OK; +} + +int rtt_setup(target_addr_t address, size_t size, const char *id) +{ + size_t id_length = strlen(id); + + if (!id_length || id_length >= RTT_CB_MAX_ID_LENGTH) { + LOG_ERROR("rtt: Invalid control block ID"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + rtt.addr = address; + rtt.size = size; + strncpy(rtt.id, id, id_length + 1); + rtt.changed = true; + rtt.configured = true; + + return ERROR_OK; +} + +int rtt_register_source(const struct rtt_source source, + struct target *target) +{ + if (!source.find_cb || !source.read_cb || !source.read_channel_info) + return ERROR_FAIL; + + if (!source.start || !source.stop) + return ERROR_FAIL; + + if (!source.read || !source.write) + return ERROR_FAIL; + + rtt.source = source; + rtt.target = target; + + return ERROR_OK; +} + +int rtt_start(void) +{ + int ret; + target_addr_t addr = rtt.addr; + + if (rtt.started) + return ERROR_OK; + + if (!rtt.found_cb || rtt.changed) { + rtt.source.find_cb(rtt.target, &addr, rtt.size, rtt.id, + &rtt.found_cb, NULL); + + rtt.changed = false; + + if (rtt.found_cb) { + LOG_INFO("rtt: Control block found at 0x%" TARGET_PRIxADDR, + addr); + rtt.ctrl.address = addr; + } else { + LOG_INFO("rtt: No control block found"); + return ERROR_OK; + } + } + + ret = rtt.source.read_cb(rtt.target, rtt.ctrl.address, &rtt.ctrl, NULL); + + if (ret != ERROR_OK) + return ret; + + ret = rtt.source.start(rtt.target, &rtt.ctrl, NULL); + + if (ret != ERROR_OK) + return ret; + + target_register_timer_callback(&read_channel_callback, + rtt.polling_interval, 1, NULL); + rtt.started = true; + + return ERROR_OK; +} + +int rtt_stop(void) +{ + int ret; + + if (!rtt.configured) { + LOG_ERROR("rtt: Not configured"); + return ERROR_FAIL; + } + + target_unregister_timer_callback(&read_channel_callback, NULL); + rtt.started = false; + + ret = rtt.source.stop(rtt.target, NULL); + + if (ret != ERROR_OK) + return ret; + + return ERROR_OK; +} + +static int adjust_sink_list(size_t length) +{ + struct rtt_sink_list **tmp; + + if (length <= rtt.sink_list_length) + return ERROR_OK; + + tmp = realloc(rtt.sink_list, sizeof(struct rtt_sink_list *) * length); + + if (!tmp) + return ERROR_FAIL; + + for (size_t i = rtt.sink_list_length; i < length; i++) + tmp[i] = NULL; + + rtt.sink_list = tmp; + rtt.sink_list_length = length; + + return ERROR_OK; +} + +int rtt_register_sink(unsigned int channel_index, rtt_sink_read read, + void *user_data) +{ + struct rtt_sink_list *tmp; + + if (channel_index >= rtt.sink_list_length) { + if (adjust_sink_list(channel_index + 1) != ERROR_OK) + return ERROR_FAIL; + } + + LOG_DEBUG("rtt: Registering sink for channel %u", channel_index); + + tmp = malloc(sizeof(struct rtt_sink_list)); + + if (!tmp) + return ERROR_FAIL; + + tmp->read = read; + tmp->user_data = user_data; + tmp->next = rtt.sink_list[channel_index]; + + rtt.sink_list[channel_index] = tmp; + + return ERROR_OK; +} + +int rtt_unregister_sink(unsigned int channel_index, rtt_sink_read read, + void *user_data) +{ + struct rtt_sink_list *prev_sink; + + LOG_DEBUG("rtt: Unregistering sink for channel %u", channel_index); + + if (channel_index >= rtt.sink_list_length) + return ERROR_FAIL; + + prev_sink = rtt.sink_list[channel_index]; + + for (struct rtt_sink_list *sink = rtt.sink_list[channel_index]; sink; + prev_sink = sink, sink = sink->next) { + if (sink->read == read && sink->user_data == user_data) { + + if (sink == rtt.sink_list[channel_index]) + rtt.sink_list[channel_index] = sink->next; + else + prev_sink->next = sink->next; + + free(sink); + + return ERROR_OK; + } + } + + return ERROR_OK; +} + +int rtt_get_polling_interval(unsigned int *interval) +{ + if (!interval) + return ERROR_FAIL; + + *interval = rtt.polling_interval; + + return ERROR_OK; +} + +int rtt_set_polling_interval(unsigned int interval) +{ + if (!interval) + return ERROR_FAIL; + + if (rtt.polling_interval != interval) { + target_unregister_timer_callback(&read_channel_callback, NULL); + target_register_timer_callback(&read_channel_callback, interval, 1, + NULL); + } + + rtt.polling_interval = interval; + + return ERROR_OK; +} + +int rtt_write_channel(unsigned int channel_index, const uint8_t *buffer, + size_t *length) +{ + if (channel_index >= rtt.ctrl.num_up_channels) { + LOG_WARNING("rtt: Down-channel %u is not available", channel_index); + return ERROR_OK; + } + + return rtt.source.write(rtt.target, &rtt.ctrl, channel_index, buffer, + length, NULL); +} + +bool rtt_started(void) +{ + return rtt.started; +} + +bool rtt_configured(void) +{ + return rtt.configured; +} + +bool rtt_found_cb(void) +{ + return rtt.found_cb; +} + +const struct rtt_control *rtt_get_control(void) +{ + return &rtt.ctrl; +} + +int rtt_read_channel_info(unsigned int channel_index, + enum rtt_channel_type type, struct rtt_channel_info *info) +{ + return rtt.source.read_channel_info(rtt.target, &rtt.ctrl, + channel_index, type, info, NULL); +} diff --git a/src/rtt/rtt.h b/src/rtt/rtt.h new file mode 100644 index 000000000..597c83829 --- /dev/null +++ b/src/rtt/rtt.h @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2016-2020 by Marc Schink + * + * 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 . + */ + +#ifndef OPENOCD_RTT_RTT_H +#define OPENOCD_RTT_RTT_H + +#include +#include + +#include +#include + +/** + * Control block ID length in bytes, including the trailing null-terminator. + */ +#define RTT_CB_MAX_ID_LENGTH 16 + +/* Control block size in bytes. */ +#define RTT_CB_SIZE (RTT_CB_MAX_ID_LENGTH + 2 * sizeof(uint32_t)) + +/* Channel structure size in bytes. */ +#define RTT_CHANNEL_SIZE 24 + +/* Minimal channel buffer size in bytes. */ +#define RTT_CHANNEL_BUFFER_MIN_SIZE 2 + +/** RTT control block. */ +struct rtt_control { + /** Control block address on the target. */ + target_addr_t address; + /** Control block identifier, including trailing null-terminator. */ + char id[RTT_CB_MAX_ID_LENGTH]; + /** Maximum number of up-channels. */ + uint32_t num_up_channels; + /** Maximum number of down-channels. */ + uint32_t num_down_channels; +}; + +/** RTT channel. */ +struct rtt_channel { + /** Channel structure address on the target. */ + target_addr_t address; + /** Channel name address on the target. */ + uint32_t name_addr; + /** Buffer address on the target. */ + uint32_t buffer_addr; + /** Channel buffer size in bytes. */ + uint32_t size; + /** Write position within the buffer in bytes. */ + uint32_t write_pos; + /** Read position within the buffer in bytes. */ + uint32_t read_pos; + /** + * Buffer flags. + * + * @note: Not used at the moment. + */ + uint32_t flags; +}; + +/** RTT channel information. */ +struct rtt_channel_info { + /** Channel name. */ + char *name; + /** Length of the name in bytes, including the trailing null-terminator. */ + size_t name_length; + /** Buffer size in bytes. */ + uint32_t size; + /** + * Buffer flags. + * + * @note: Not used at the moment. + */ + 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; +}; + +/** Channel type. */ +enum rtt_channel_type { + /** Up channel (target to host). */ + RTT_CHANNEL_TYPE_UP, + /** Down channel (host to target). */ + RTT_CHANNEL_TYPE_DOWN +}; + +typedef int (*rtt_source_find_ctrl_block)(struct target *target, + target_addr_t *address, size_t size, const char *id, bool *found, + void *user_data); +typedef int (*rtt_source_read_ctrl_block)(struct target *target, + target_addr_t address, struct rtt_control *ctrl_block, + void *user_data); +typedef int (*rtt_source_read_channel_info)(struct target *target, + const struct rtt_control *ctrl, unsigned int channel, + enum rtt_channel_type type, struct rtt_channel_info *info, + void *user_data); +typedef int (*rtt_source_start)(struct target *target, + const struct rtt_control *ctrl, void *user_data); +typedef int (*rtt_source_stop)(struct target *target, void *user_data); +typedef int (*rtt_source_read)(struct target *target, + const struct rtt_control *ctrl, struct rtt_sink_list **sinks, + size_t num_channels, void *user_data); +typedef int (*rtt_source_write)(struct target *target, + struct rtt_control *ctrl, unsigned int channel, + const uint8_t *buffer, size_t *length, void *user_data); + +/** RTT source. */ +struct rtt_source { + rtt_source_find_ctrl_block find_cb; + rtt_source_read_ctrl_block read_cb; + rtt_source_read_channel_info read_channel_info; + rtt_source_start start; + rtt_source_stop stop; + rtt_source_read read; + rtt_source_write write; +}; + +/** + * Initialize Real-Time Transfer (RTT). + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_init(void); + +/** + * Shutdown Real-Time Transfer (RTT). + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_exit(void); + +/** + * Register an RTT source for a target. + * + * @param[in] source RTT source. + * @param[in,out] target Target. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_register_source(const struct rtt_source source, + struct target *target); + +/** + * Setup RTT. + * + * @param[in] address Start address to search for the control block. + * @param[in] size Size of the control block search area. + * @param[in] id Identifier of the control block. Must be null-terminated. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_setup(target_addr_t address, size_t size, const char *id); + +/** + * Start Real-Time Transfer (RTT). + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_start(void); + +/** + * Stop Real-Time Transfer (RTT). + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_stop(void); + +/** + * Get the polling interval. + * + * @param[out] interval Polling interval in milliseconds. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_get_polling_interval(unsigned int *interval); + +/** + * Set the polling interval. + * + * @param[in] interval Polling interval in milliseconds. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_set_polling_interval(unsigned int interval); + +/** + * Get whether RTT is started. + * + * @returns Whether RTT is started. + */ +bool rtt_started(void); + +/** + * Get whether RTT is configured. + * + * @returns Whether RTT is configured. + */ +bool rtt_configured(void); + +/** + * Get whether RTT control block was found. + * + * @returns Whether RTT was found. + */ +bool rtt_found_cb(void); + +/** + * Get the RTT control block. + * + * @returns The RTT control block. + */ +const struct rtt_control *rtt_get_control(void); + +/** + * Read channel information. + * + * @param[in] channel_index Channel index. + * @param[in] channel_type Channel type. + * @param[out] info Channel information. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_read_channel_info(unsigned int channel_index, + enum rtt_channel_type type, struct rtt_channel_info *info); + +/** + * Register an RTT sink. + * + * @param[in] channel_index Channel index. + * @param[in] read Read callback function. + * @param[in,out] user_data User data to be passed to the callback function. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_register_sink(unsigned int channel_index, rtt_sink_read read, + void *user_data); + +/** + * Unregister an RTT sink. + * + * @param[in] channel_index Channel index. + * @param[in] read Read callback function. + * @param[in,out] user_data User data to be passed to the callback function. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_unregister_sink(unsigned int channel_index, rtt_sink_read read, + void *user_data); + +/** + * Write to an RTT channel. + * + * @param[in] channel_index Channel index. + * @param[in] buffer Buffer with data that should be written to the channel. + * @param[in,out] length Number of bytes to write. On success, the argument gets + * updated with the actual number of written bytes. + * + * @returns ERROR_OK on success, an error code on failure. + */ +int rtt_write_channel(unsigned int channel_index, const uint8_t *buffer, + size_t *length); + +extern const struct command_registration rtt_target_command_handlers[]; + +#endif /* OPENOCD_RTT_RTT_H */ diff --git a/src/rtt/tcl.c b/src/rtt/tcl.c new file mode 100644 index 000000000..f5abf2e5e --- /dev/null +++ b/src/rtt/tcl.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2019-2020 by Marc Schink + * + * 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 . + */ + +#include +#include + +#include "rtt.h" + +#define CHANNEL_NAME_SIZE 128 + +COMMAND_HANDLER(handle_rtt_setup_command) +{ +struct rtt_source source; + + if (CMD_ARGC != 3) + return ERROR_COMMAND_SYNTAX_ERROR; + + 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_channel_info = &target_rtt_read_channel_info; + + target_addr_t address; + uint32_t size; + + COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[0], address); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size); + + rtt_register_source(source, get_current_target(CMD_CTX)); + + if (rtt_setup(address, size, CMD_ARGV[2]) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_rtt_start_command) +{ + if (CMD_ARGC > 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (!rtt_configured()) { + command_print(CMD, "RTT is not configured"); + return ERROR_FAIL; + } + + return rtt_start(); +} + +COMMAND_HANDLER(handle_rtt_stop_command) +{ + if (CMD_ARGC > 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + return rtt_stop(); +} + +COMMAND_HANDLER(handle_rtt_polling_interval_command) +{ + if (CMD_ARGC == 0) { + int ret; + unsigned int interval; + + ret = rtt_get_polling_interval(&interval); + + if (ret != ERROR_OK) { + command_print(CMD, "Failed to get polling interval"); + return ret; + } + + command_print(CMD, "%u ms", interval); + } else if (CMD_ARGC == 1) { + int ret; + unsigned int interval; + + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], interval); + ret = rtt_set_polling_interval(interval); + + if (ret != ERROR_OK) { + command_print(CMD, "Failed to set polling interval"); + return ret; + } + } else { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_rtt_channels_command) +{ + int ret; + char channel_name[CHANNEL_NAME_SIZE]; + const struct rtt_control *ctrl; + struct rtt_channel_info info; + + if (!rtt_found_cb()) { + command_print(CMD, "rtt: Control block not available"); + return ERROR_FAIL; + } + + ctrl = rtt_get_control(); + + command_print(CMD, "Channels: up=%u, down=%u", ctrl->num_up_channels, + ctrl->num_down_channels); + + command_print(CMD, "Up-channels:"); + + info.name = channel_name; + info.name_length = sizeof(channel_name); + + for (unsigned int i = 0; i < ctrl->num_up_channels; i++) { + ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_UP, &info); + + if (ret != ERROR_OK) + return ret; + + if (!info.size) + continue; + + command_print(CMD, "%u: %s %u %u", i, info.name, info.size, + info.flags); + } + + command_print(CMD, "Down-channels:"); + + for (unsigned int i = 0; i < ctrl->num_down_channels; i++) { + ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_DOWN, &info); + + if (ret != ERROR_OK) + return ret; + + if (!info.size) + continue; + + command_print(CMD, "%u: %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) +{ + Jim_Obj *list; + Jim_Obj *channel_list; + char channel_name[CHANNEL_NAME_SIZE]; + const struct rtt_control *ctrl; + struct rtt_channel_info info; + + if (!rtt_found_cb()) { + Jim_SetResultFormatted(interp, "rtt: Control block not available"); + return ERROR_FAIL; + } + + ctrl = rtt_get_control(); + + info.name = channel_name; + info.name_length = sizeof(channel_name); + + list = Jim_NewListObj(interp, NULL, 0); + channel_list = Jim_NewListObj(interp, NULL, 0); + + for (unsigned int i = 0; i < ctrl->num_up_channels; i++) { + int ret; + Jim_Obj *tmp; + + ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_UP, &info); + + if (ret != ERROR_OK) + return ret; + + if (!info.size) + continue; + + 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 (unsigned int i = 0; i < ctrl->num_down_channels; i++) { + int ret; + Jim_Obj *tmp; + + ret = rtt_read_channel_info(i, RTT_CHANNEL_TYPE_DOWN, &info); + + if (ret != ERROR_OK) + return ret; + + if (!info.size) + continue; + + 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 = "
" + }, + { + .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 = "polling_interval", + .handler = handle_rtt_polling_interval_command, + .mode = COMMAND_EXEC, + .help = "show or set polling interval in ms", + .usage = "[interval]" + }, + { + .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 +}; + +const struct command_registration rtt_target_command_handlers[] = { + { + .name = "rtt", + .mode = COMMAND_EXEC, + .help = "RTT target commands", + .usage = "", + .chain = rtt_subcommand_handlers + }, + COMMAND_REGISTRATION_DONE +}; diff --git a/src/server/Makefile.am b/src/server/Makefile.am index 804efac16..d270ee281 100644 --- a/src/server/Makefile.am +++ b/src/server/Makefile.am @@ -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 diff --git a/src/server/rtt_server.c b/src/server/rtt_server.c new file mode 100644 index 000000000..3c885cce0 --- /dev/null +++ b/src/server/rtt_server.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2016-2017 by Marc Schink + * + * 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 . + */ + +#include +#include + +#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("rtt: New connection for 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("rtt: Connection for 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, NULL); + + 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_server_subcommand_handlers[] = { + { + .name = "start", + .handler = handle_rtt_start_command, + .mode = COMMAND_ANY, + .help = "Start a RTT server", + .usage = " " + }, + { + .name = "stop", + .handler = handle_rtt_stop_command, + .mode = COMMAND_ANY, + .help = "Stop a RTT server", + .usage = "" + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration rtt_server_command_handlers[] = { + { + .name = "server", + .mode = COMMAND_ANY, + .help = "RTT server", + .usage = "", + .chain = rtt_server_subcommand_handlers + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration rtt_command_handlers[] = { + { + .name = "rtt", + .mode = COMMAND_ANY, + .help = "RTT", + .usage = "", + .chain = rtt_server_command_handlers + }, + COMMAND_REGISTRATION_DONE +}; + +int rtt_server_register_commands(struct command_context *ctx) +{ + return register_commands(ctx, NULL, rtt_command_handlers); +} diff --git a/src/server/rtt_server.h b/src/server/rtt_server.h new file mode 100644 index 000000000..aec6f2222 --- /dev/null +++ b/src/server/rtt_server.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016-2017 by Marc Schink + * + * 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 . + */ + +#ifndef OPENOCD_SERVER_RTT_SERVER_H +#define OPENOCD_SERVER_RTT_SERVER_H + +#include + +int rtt_server_register_commands(struct command_context *ctx); + +#endif /* OPENOCD_SERVER_RTT_SERVER_H */ diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 19ba7714e..1d30747b6 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -48,7 +48,8 @@ TARGET_CORE_SRC = \ %D%/target_request.c \ %D%/testee.c \ %D%/semihosting_common.c \ - %D%/smp.c + %D%/smp.c \ + %D%/rtt.c ARMV4_5_SRC = \ %D%/armv4_5.c \ @@ -259,7 +260,8 @@ ARC_SRC = \ %D%/arc.h \ %D%/arc_cmd.h \ %D%/arc_jtag.h \ - %D%/arc_mem.h + %D%/arc_mem.h \ + %D%/rtt.h include %D%/openrisc/Makefile.am include %D%/riscv/Makefile.am diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index fae2aac5d..316089c35 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -39,6 +39,7 @@ #include "arm_opcodes.h" #include "arm_semihosting.h" #include +#include /* NOTE: most of this should work fine for the Cortex-M1 and * Cortex-M0 cores too, although they're ARMv6-M not ARMv7-M. @@ -2489,6 +2490,9 @@ static const struct command_registration cortex_m_command_handlers[] = { .usage = "", .chain = cortex_m_exec_command_handlers, }, + { + .chain = rtt_target_command_handlers, + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/hla_target.c b/src/target/hla_target.c index 99afccc49..3d41387fd 100644 --- a/src/target/hla_target.c +++ b/src/target/hla_target.c @@ -39,6 +39,7 @@ #include "cortex_m.h" #include "arm_semihosting.h" #include "target_request.h" +#include #define savedDCRDR dbgbase /* FIXME: using target->dbgbase to preserve DCRDR */ @@ -626,6 +627,9 @@ static const struct command_registration adapter_command_handlers[] = { { .chain = armv7m_trace_command_handlers, }, + { + .chain = rtt_target_command_handlers, + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/rtt.c b/src/target/rtt.c new file mode 100644 index 000000000..7e556e1cb --- /dev/null +++ b/src/target/rtt.c @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2016-2020 by Marc Schink + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include "target.h" + +static int read_rtt_channel(struct target *target, + const struct rtt_control *ctrl, unsigned int channel_index, + enum rtt_channel_type type, struct rtt_channel *channel) +{ + int ret; + uint8_t buf[RTT_CHANNEL_SIZE]; + target_addr_t address; + + address = ctrl->address + RTT_CB_SIZE + (channel_index * RTT_CHANNEL_SIZE); + + if (type == RTT_CHANNEL_TYPE_DOWN) + address += ctrl->num_up_channels * RTT_CHANNEL_SIZE; + + ret = target_read_buffer(target, address, RTT_CHANNEL_SIZE, buf); + + if (ret != ERROR_OK) + return ret; + + channel->address = address; + channel->name_addr = buf_get_u32(buf + 0, 0, 32); + channel->buffer_addr = buf_get_u32(buf + 4, 0, 32); + channel->size = buf_get_u32(buf + 8, 0, 32); + channel->write_pos = buf_get_u32(buf + 12, 0, 32); + channel->read_pos = buf_get_u32(buf + 16, 0, 32); + channel->flags = buf_get_u32(buf + 20, 0, 32); + + return ERROR_OK; +} + +int target_rtt_start(struct target *target, const struct rtt_control *ctrl, + void *user_data) +{ + return ERROR_OK; +} + +int target_rtt_stop(struct target *target, void *user_data) +{ + return ERROR_OK; +} + +static int read_channel_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 read_length; + + read_length = MIN(32, length - offset); + ret = target_read_buffer(target, address + offset, read_length, + (uint8_t *)name + offset); + + if (ret != ERROR_OK) + return ret; + + if (memchr(name + offset, '\0', read_length)) + return ERROR_OK; + + offset += read_length; + } + + name[length - 1] = '\0'; + + return ERROR_OK; +} + +static int write_to_channel(struct target *target, + const struct rtt_channel *channel, const uint8_t *buffer, + size_t *length) +{ + int ret; + uint32_t len; + + if (!*length) + return ERROR_OK; + + if (channel->write_pos == channel->read_pos) { + uint32_t first_length; + + len = MIN(*length, channel->size - 1); + first_length = MIN(len, channel->size - channel->write_pos); + + ret = target_write_buffer(target, + channel->buffer_addr + channel->write_pos, first_length, + buffer); + + if (ret != ERROR_OK) + return ret; + + ret = target_write_buffer(target, channel->buffer_addr, + len - first_length, buffer + first_length); + + if (ret != ERROR_OK) + return ret; + } else if (channel->write_pos < channel->read_pos) { + len = MIN(*length, channel->read_pos - channel->write_pos - 1); + + if (!len) { + *length = 0; + return ERROR_OK; + } + + ret = target_write_buffer(target, + channel->buffer_addr + channel->write_pos, len, buffer); + + if (ret != ERROR_OK) + return ret; + } else { + uint32_t first_length; + + len = MIN(*length, + channel->size - channel->write_pos + channel->read_pos - 1); + + if (!len) { + *length = 0; + return ERROR_OK; + } + + first_length = MIN(len, channel->size - channel->write_pos); + + ret = target_write_buffer(target, + channel->buffer_addr + channel->write_pos, first_length, + buffer); + + if (ret != ERROR_OK) + return ret; + + buffer = buffer + first_length; + + ret = target_write_buffer(target, channel->buffer_addr, + len - first_length, buffer); + + if (ret != ERROR_OK) + return ret; + } + + ret = target_write_u32(target, channel->address + 12, + (channel->write_pos + len) % channel->size); + + if (ret != ERROR_OK) + return ret; + + *length = len; + + return ERROR_OK; +} + +static bool channel_is_active(const struct rtt_channel *channel) +{ + if (!channel) + return false; + + if (!channel->size) + return false; + + return true; +} + +int target_rtt_write_callback(struct target *target, struct rtt_control *ctrl, + unsigned int channel_index, const uint8_t *buffer, size_t *length, + void *user_data) +{ + int ret; + struct rtt_channel channel; + + ret = read_rtt_channel(target, ctrl, channel_index, + RTT_CHANNEL_TYPE_DOWN, &channel); + + if (ret != ERROR_OK) { + LOG_ERROR("rtt: Failed to read down-channel %u description", + channel_index); + return ret; + } + + if (!channel_is_active(&channel)) { + LOG_WARNING("rtt: Down-channel %u is not active", channel_index); + return ERROR_OK; + } + + if (channel.size < RTT_CHANNEL_BUFFER_MIN_SIZE) { + LOG_WARNING("rtt: Down-channel %u is not large enough", + channel_index); + return ERROR_OK; + } + + ret = write_to_channel(target, &channel, buffer, length); + + if (ret != ERROR_OK) + return ret; + + LOG_DEBUG("rtt: Wrote %zu bytes into down-channel %u", *length, + channel_index); + + return ERROR_OK; +} + +int target_rtt_read_control_block(struct target *target, + target_addr_t address, struct rtt_control *ctrl, void *user_data) +{ + int ret; + uint8_t buf[RTT_CB_SIZE]; + + ret = target_read_buffer(target, address, RTT_CB_SIZE, buf); + + if (ret != ERROR_OK) + return ret; + + memcpy(ctrl->id, buf, RTT_CB_MAX_ID_LENGTH); + ctrl->id[RTT_CB_MAX_ID_LENGTH - 1] = '\0'; + ctrl->num_up_channels = buf_get_u32(buf + RTT_CB_MAX_ID_LENGTH + 0, + 0, 32); + ctrl->num_down_channels = buf_get_u32(buf + RTT_CB_MAX_ID_LENGTH + 4, + 0, 32); + + return ERROR_OK; +} + +int target_rtt_find_control_block(struct target *target, + target_addr_t *address, size_t size, const char *id, bool *found, + void *user_data) +{ + uint8_t buf[1024]; + + *found = false; + + size_t j = 0; + size_t cb_offset = 0; + const size_t id_length = strlen(id); + + LOG_INFO("rtt: Searching for control block '%s'", id); + + for (target_addr_t addr = 0; addr < size; addr = addr + sizeof(buf)) { + int ret; + + const size_t buf_size = MIN(sizeof(buf), size - addr); + ret = target_read_buffer(target, *address + addr, buf_size, buf); + + if (ret != ERROR_OK) + return ret; + + size_t start = 0; + size_t i = 0; + + while (i < buf_size) { + if (buf[i] != id[j]) { + start++; + cb_offset++; + i = start; + j = 0; + + continue; + } + + i++; + j++; + + if (j == id_length) { + *address = *address + cb_offset; + *found = true; + return ERROR_OK; + } + } + } + + return ERROR_OK; +} + +int target_rtt_read_channel_info(struct target *target, + const struct rtt_control *ctrl, unsigned int channel_index, + enum rtt_channel_type type, struct rtt_channel_info *info, + void *user_data) +{ + int ret; + struct rtt_channel channel; + + ret = read_rtt_channel(target, ctrl, channel_index, type, &channel); + + if (ret != ERROR_OK) { + LOG_ERROR("rtt: Failed to read channel %u description", + channel_index); + return ret; + } + + ret = read_channel_name(target, channel.name_addr, info->name, + info->name_length); + + if (ret != ERROR_OK) + return ret; + + info->size = channel.size; + info->flags = channel.flags; + + return ERROR_OK; +} + +static int read_from_channel(struct target *target, + const struct rtt_channel *channel, uint8_t *buffer, + size_t *length) +{ + int ret; + uint32_t len; + + if (!*length) + return ERROR_OK; + + if (channel->read_pos == channel->write_pos) { + len = 0; + } else if (channel->read_pos < channel->write_pos) { + len = MIN(*length, channel->write_pos - channel->read_pos); + + ret = target_read_buffer(target, + channel->buffer_addr + channel->read_pos, len, buffer); + + if (ret != ERROR_OK) + return ret; + } else { + uint32_t first_length; + + len = MIN(*length, + channel->size - channel->read_pos + channel->write_pos); + first_length = MIN(len, channel->size - channel->read_pos); + + ret = target_read_buffer(target, + channel->buffer_addr + channel->read_pos, first_length, buffer); + + if (ret != ERROR_OK) + return ret; + + ret = target_read_buffer(target, channel->buffer_addr, + len - first_length, buffer + first_length); + + if (ret != ERROR_OK) + return ret; + } + + if (len > 0) { + ret = target_write_u32(target, channel->address + 16, + (channel->read_pos + len) % channel->size); + + if (ret != ERROR_OK) + return ret; + } + + *length = len; + + return ERROR_OK; +} + +int target_rtt_read_callback(struct target *target, + const struct rtt_control *ctrl, struct rtt_sink_list **sinks, + size_t num_channels, void *user_data) +{ + num_channels = MIN(num_channels, ctrl->num_up_channels); + + for (size_t i = 0; i < num_channels; i++) { + int ret; + struct rtt_channel channel; + uint8_t buffer[1024]; + size_t length; + + if (!sinks[i]) + continue; + + ret = read_rtt_channel(target, ctrl, i, RTT_CHANNEL_TYPE_UP, + &channel); + + if (ret != ERROR_OK) { + LOG_ERROR("rtt: Failed to read up-channel %zu description", i); + return ret; + } + + if (!channel_is_active(&channel)) { + LOG_WARNING("rtt: Up-channel %zu is not active", i); + continue; + } + + if (channel.size < RTT_CHANNEL_BUFFER_MIN_SIZE) { + LOG_WARNING("rtt: Up-channel %zu is not large enough", i); + continue; + } + + length = sizeof(buffer); + ret = read_from_channel(target, &channel, buffer, &length); + + if (ret != ERROR_OK) { + LOG_ERROR("rtt: Failed to read from up-channel %zu", i); + return ret; + } + + for (struct rtt_sink_list *sink = sinks[i]; sink; sink = sink->next) + sink->read(i, buffer, length, sink->user_data); + } + + return ERROR_OK; +} diff --git a/src/target/rtt.h b/src/target/rtt.h new file mode 100644 index 000000000..01224750f --- /dev/null +++ b/src/target/rtt.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016-2020 by Marc Schink + * + * 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 . + */ + +#ifndef OPENOCD_TARGET_RTT_H +#define OPENOCD_TARGET_RTT_H + +#include +#include + +#include +#include + +int target_rtt_start(struct target *target, const struct rtt_control *ctrl, + void *user_data); +int target_rtt_stop(struct target *target, void *user_data); +int target_rtt_find_control_block(struct target *target, + target_addr_t *address, size_t size, const char *id, bool *found, + void *user_data); +int target_rtt_read_control_block(struct target *target, + target_addr_t address, struct rtt_control *ctrl, void *user_data); +int target_rtt_write_callback(struct target *target, + struct rtt_control *ctrl, unsigned int channel_index, + const uint8_t *buffer, size_t *length, void *user_data); +int target_rtt_read_callback(struct target *target, + const struct rtt_control *ctrl, struct rtt_sink_list **sinks, + size_t length, void *user_data); +int target_rtt_read_channel_info(struct target *target, + const struct rtt_control *ctrl, unsigned int channel_index, + enum rtt_channel_type type, struct rtt_channel_info *info, + void *user_data); + +#endif /* OPENOCD_TARGET_RTT_H */