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
%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
include %D%/server/Makefile.am
include %D%/flash/Makefile.am
include %D%/pld/Makefile.am
+include %D%/rtt/Makefile.am
#include <pld/pld.h>
#include <target/arm_cti.h>
#include <target/arm_adi_v5.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>
&server_register_commands,
&gdb_register_commands,
&log_register_commands,
+ &rtt_server_register_commands,
&transport_register_commands,
&interface_register_commands,
&target_register_commands,
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");
/* Shutdown commandline interface */
command_exit(cmd_ctx);
+ rtt_exit();
free_config();
if (ERROR_FAIL == ret)
--- /dev/null
+noinst_LTLIBRARIES += %D%/librtt.la
+%C%_librtt_la_SOURCES = %D%/rtt.c %D%/rtt.h %D%/tcl.c
--- /dev/null
+/*
+ * Copyright (C) 2016-2020 by Marc Schink <dev@zapb.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 {
+ 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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2016-2020 by Marc Schink <dev@zapb.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>
+
+/**
+ * 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 */
--- /dev/null
+/*
+ * Copyright (C) 2019-2020 by Marc Schink <dev@zapb.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 <helper/log.h>
+#include <target/rtt.h>
+
+#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 = "<address> <size> <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 = "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
+};
%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
--- /dev/null
+/*
+ * Copyright (C) 2016-2017 by Marc Schink <dev@zapb.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("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 = "<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_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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2016-2017 by Marc Schink <dev@zapb.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 */
%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 \
%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
#include "arm_opcodes.h"
#include "arm_semihosting.h"
#include <helper/time_support.h>
+#include <rtt/rtt.h>
/* NOTE: most of this should work fine for the Cortex-M1 and
* Cortex-M0 cores too, although they're ARMv6-M not ARMv7-M.
.usage = "",
.chain = cortex_m_exec_command_handlers,
},
+ {
+ .chain = rtt_target_command_handlers,
+ },
COMMAND_REGISTRATION_DONE
};
#include "cortex_m.h"
#include "arm_semihosting.h"
#include "target_request.h"
+#include <rtt/rtt.h>
#define savedDCRDR dbgbase /* FIXME: using target->dbgbase to preserve DCRDR */
{
.chain = armv7m_trace_command_handlers,
},
+ {
+ .chain = rtt_target_command_handlers,
+ },
COMMAND_REGISTRATION_DONE
};
--- /dev/null
+/*
+ * Copyright (C) 2016-2020 by Marc Schink <dev@zapb.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 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2016-2020 by Marc Schink <dev@zapb.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(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 */