326 lines
6.2 KiB
C
326 lines
6.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/*
|
|
* Copyright (C) 2016-2020 by Marc Schink <dev@zapb.de>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#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);
|
|
}
|