/*
* remote_commands.c
*
* Ctools Profiler Server Implementation
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdint.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <errno.h>
#include "error_handler.h"
#include "logging.h"
#include "ctoolsprof.h"
#include "remote_commands.h"
#include "etb_handler.h"
/*****************************************************************************
* Remote RSP Command Definitions
*
* Note - All remote command handlers return 0 for success
* and -1 for failure. Returning 0 causes the command_process()
* function (see command_handler.c) to return rsp message "OK".
* Returning -1 causes the command_handler to issue a rsp error packet.
* The error handler may be called to generate a local error but these
* must be non-fatal so the rsp communications with the client can be
* completed.
*****************************************************************************/
struct set_element_table_t {
char * element;
bool is_valid;
int value;
};
static struct set_element_table_t set_element_table[] = {
[OP_MODE] = {"op-mode", false, 0},
[DURATION] = {"duration", false, 0},
[START_DELAY] = {"start-delay", false, 0},
[ETB_ENABLE] = {"etb_enable", false, 0},
[ETB_MODE] = {"etb", false, 0}
};
typedef enum {
OP_MODE_TIME,
OP_MODE_SIGNAL,
OP_MODE_TRIGGER,
OP_MODE_LAST
} op_mode_t;
static char * op_mode_table[] = {
[OP_MODE_TIME] = "time",
[OP_MODE_SIGNAL] = "signal",
[OP_MODE_TRIGGER] = "trigger"
};
static char * etb_mode_table[] = {
[ETB_MODE_ONESHOT_FIXED] = "fixed",
[ETB_MODE_ONESHOT_CIRCULAR] = "circular",
[ETB_MODE_DRAIN] = "drain"
};
/* Note these strings must all be the same size */
static const char msg_recording[] = "ctprof recording\n";
static const char msg_stopped[] = "ctprof stopped__\n";
static const char msg_ready[] = "ctprof ready____\n";
/*****************************************************************************
* Internal Function Prototypes
* - See Private Function section below for implementations )
*****************************************************************************/
void * cTools_memMap(uint32_t map_addr, uint32_t map_size);
void cTools_memUnMap(uint32_t map_addr, uint32_t map_size);
static void respond_ok(char * rbuf, size_t rsp_size);
static void * is_memory_mapped(uint32_t addr, size_t byte_cnt, void ** v_start, size_t * len);
/*****************************************************************************
* Public Functions
*****************************************************************************/
/*****************************************************************************
* get_set_value()
*
* - Provide the value for the requested element and if the value
* is valid(has been set).
*****************************************************************************/
void get_set_value(set_element_t element, unsigned int *value, bool *is_valid)
{
#if DEBUG
if (element >= LAST_SET_ELEMENT) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
if ((value == NULL) || (is_valid == NULL)) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
*is_valid = set_element_table[element].is_valid;
*value = set_element_table[element].value;
}
/*****************************************************************************
* set_command_handler()
*
* - Set a value for the requested element
* - argv definition:
* argv[0] should always be "set".
* argv[1] is element to set (ie "op_mode")
* argv[2] is the string value.
* - argv elements are by definition always strings
* - Based on the element, the element value is converted to either an
* int or an enumeration and stored in set_table[n].value.
*****************************************************************************/
void set_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
int set_index;
size_t set_element_cnt = sizeof(set_element_table)
/sizeof(struct set_element_table_t);
LOGFUNC();
#if DEBUG
if ((argv == NULL) || (rbuf == NULL)) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
/* Search the command table*/
for (set_index = 0; set_index < set_element_cnt; set_index++) {
if (!strcmp(argv[1], set_element_table[set_index].element)) {
break;
}
}
#if DEBUG
if (set_index == set_element_cnt) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
switch(set_index) {
int i;
case OP_MODE:
for (i = 0; i < OP_MODE_LAST; i++) {
if (!strcmp(argv[2], op_mode_table[i])) {
set_element_table[OP_MODE].value = i;
set_element_table[OP_MODE].is_valid = true;
break;
}
}
#if DEBUG
if (i == OP_MODE_LAST) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
break;
case DURATION:
set_element_table[DURATION].value = atoi(argv[2]);
set_element_table[DURATION].is_valid = true;
break;
case START_DELAY:
set_element_table[START_DELAY].value = atoi(argv[2]);
set_element_table[START_DELAY].is_valid = true;
break;
case ETB_ENABLE:
set_element_table[ETB_ENABLE].value = atoi(argv[2]);
set_element_table[ETB_ENABLE].is_valid = true;
break;
case ETB_MODE:
for (i = 0; i < ETB_MODE_LAST; i++) {
if (!strcmp(argv[2], etb_mode_table[i])) {
set_element_table[ETB_MODE].value = i;
set_element_table[ETB_MODE].is_valid = true;
break;
}
}
#if DEBUG
if (i == ETB_MODE_LAST) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
break;
}
respond_ok(rbuf, rsp_size);
LOGMSG1("%s: set %s to %d", __func__, set_element_table[set_index].element,
set_element_table[set_index].value);
}
/*****************************************************************************
* version_command_handler()
*
* - Fill the response buffer with the version of the server.
*
*****************************************************************************/
void version_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
LOGFUNC();
#if DEBUG
if ((argv == NULL) || (rbuf == NULL)) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
char * msg;
int sn_size;
sn_size = snprintf(rbuf, rsp_size,"server version %d.%d, copyright %d,",
g_major_version, g_minor_version, g_copyright_year);
#if DEBUG
if (sn_size > rsp_size) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
}
/*****************************************************************************
* mmap_command_handler()
*
* - Fill the response buffer with the version of the server.
* - argv definition:
* argv[0] should always be "mmap".
* argv[1] address string value.
* argv[2] size to map value.
* - argv elements are by definition always strings
*
*****************************************************************************/
void mmap_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
LOGFUNC();
#if DEBUG
if ((argv == NULL) || (rbuf == NULL)) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
uint32_t map_addr = strtoul(argv[1], NULL, 16);
uint32_t map_size = strtoul(argv[2], NULL, 16);
LOGMSG1("%s:physical address to map is 0x%x, size %d",__func__,
map_addr, map_size);
cTools_memMap(map_addr, map_size);
respond_ok(rbuf, rsp_size);
}
/*****************************************************************************
* ummap_command_handler()
*
* - Fill the response buffer with the version of the server.
* - argv definition:
* argv[0] should always be "ummap".
* argv[1] address string value.
* argv[2] size to ummap value.
* - argv elements are by definition always strings
*
* - Address from the client is always physical address so must
* be converted to virtual address first.
*
*****************************************************************************/
void ummap_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
LOGFUNC();
#if DEBUG
if (argc < 3) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
if ((argv == NULL) || (rbuf == NULL)) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
uint32_t map_addr = strtoul(argv[1], NULL, 16);
size_t byte_cnt = strtoul(argv[2], NULL, 16);
void * v_start_addr;
size_t len;
void * v_addr = is_memory_mapped(map_addr, byte_cnt, &v_start_addr, &len);
cTools_memUnMap((uint32_t)v_addr, 0);
respond_ok(rbuf, rsp_size);
}
/******************************************************************************
* Commands Specific Definitions for arm and disarm commands
*
*****************************************************************************/
struct modcntl_t {
uint32_t addr;
size_t value;
struct modcntl_t * prev_modcntl;
struct modcntl_t * next_modcntl;
};
static struct modcntl_t * arm_table_head = NULL; // Map Object Link List head
static struct modcntl_t * disarm_table_head = NULL; // Map Object Link List head
/*****************************************************************************
* modcntl_add_command_handler()
*
* - Add a arm or disarm command to the arm or disarm
* module control (modcntl_t) link lists
* - argv definition:
* argv[0] should always be "arm" or "disarm".
* argv[1] address string.
* argv[2] value to write for arm or disarm operation
* - argv elements are by definition always strings
*
*****************************************************************************/
void modcntl_add_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
struct modcntl_t *this_modcntl = NULL;
struct modcntl_t **modcntl_head = NULL;
LOGFUNC();
#if DEBUG
if ((argv == NULL) || (rbuf == NULL)) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
/* Decide which table to add arm (enable) or disarm (disable) values to. */
if (!strcmp(argv[0], "arm")) {
modcntl_head = &arm_table_head;
LOGMSG1("%s:modcntl add arm address", __func__);
} else if (!strcmp(argv[0], "disarm")) {
modcntl_head = &disarm_table_head;
LOGMSG1("%s:modcntl add disarm address", __func__);
}
#if DEBUG
else {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
/* Allocate space for the map element */
this_modcntl = (struct modcntl_t *)malloc(sizeof(struct modcntl_t));
if (this_modcntl == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_MEM_ALLOC);
}
this_modcntl->addr = strtoul(argv[1], NULL, 16);
this_modcntl->value = strtoul(argv[2], NULL, 16);
LOGMSG1("%s:add address 0x%x, value 0x%x", __func__,this_modcntl->addr,
this_modcntl->value);
/* Add element to end of link list */
this_modcntl->next_modcntl = NULL;
if (*modcntl_head == NULL) {
*modcntl_head = this_modcntl;
this_modcntl->prev_modcntl = NULL;
} else {
/* Search for the last element */
struct modcntl_t * modcntl_element = *modcntl_head;
while (modcntl_element->next_modcntl != NULL) {
modcntl_element = modcntl_element->next_modcntl;
}
modcntl_element->next_modcntl = this_modcntl;
this_modcntl->prev_modcntl = modcntl_element;
}
respond_ok(rbuf, rsp_size);
}
/*****************************************************************************
* modcntl_remove_command_handler()
*
* - Add a arm or disarm command to the arm or disarm
* module control (modcntl_t) link lists
* - argv definition:
* argv[0] should always be "rm_arm" or "rm_disarm".
* argv[1] address string.
* - argv elements are by definition always strings
*
*****************************************************************************/
void modcntl_remove_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
struct modcntl_t *this_modcntl = NULL;
struct modcntl_t **modcntl_head = NULL;
LOGFUNC();
#if DEBUG
if ((argv == NULL) || (rbuf == NULL)) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
uint32_t addr = strtoul(argv[1], NULL, 16);
/* Decide which table to remove arm (enable) or disarm (disable) values */
if (!strcmp(argv[0], "rm_arm")) {
modcntl_head = &arm_table_head;
LOGMSG1("%s:remove arm address 0x%x", __func__, addr);
} else if (!strcmp(argv[0], "rm_disarm")) {
modcntl_head = &disarm_table_head;
LOGMSG1("%s:remove disarm address 0x%x", __func__, addr);
}
#if DEBUG
else {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
if (*modcntl_head == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
this_modcntl = *modcntl_head;
while (this_modcntl->addr != addr) {
this_modcntl = this_modcntl->next_modcntl;
if (this_modcntl == NULL) break;
}
#if DEBUG
if (this_modcntl == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
/* Only element in link list */
if ((this_modcntl->next_modcntl == NULL)
&& (this_modcntl->prev_modcntl == NULL)) {
LOGMSG2("%s:only one element in list", __func__);
*modcntl_head = NULL;
}
/* First element in link list */
if ((this_modcntl->next_modcntl != NULL)
&& (this_modcntl->prev_modcntl == NULL)) {
LOGMSG2("%s:first element in list", __func__);
*modcntl_head = this_modcntl->next_modcntl;
this_modcntl->next_modcntl->prev_modcntl = NULL;
}
/* Last element in link list */
if ((this_modcntl->next_modcntl == NULL)
&& (this_modcntl->prev_modcntl != NULL)) {
LOGMSG2("%s:last element in list", __func__);
this_modcntl->prev_modcntl->next_modcntl = NULL;
}
/* Middle element in link list */
if ((this_modcntl->next_modcntl != NULL)
&& (this_modcntl->prev_modcntl != NULL)) {
LOGMSG2("%s:element in middle of list", __func__);
this_modcntl->prev_modcntl->next_modcntl =
this_modcntl->next_modcntl;
this_modcntl->next_modcntl->prev_modcntl =
this_modcntl->prev_modcntl;
}
free(this_modcntl);
respond_ok(rbuf, rsp_size);
}
/*****************************************************************************
* modcntl_get_element()
*
* - Retrieve the next address/value pair for a arm or disarm table element.
* - The calling function sets table to either arm_table_head or disarm_table_head
* to start retrieving address/value pairs at the beginning of a table, or NULL
* when it wants to get the next value pair in the list.
*
*****************************************************************************/
static struct modcntl_t *next_table_element = NULL;
bool modcntl_get_element(struct modcntl_t *table, uint32_t *addr, uint32_t *value)
{
LOGFUNC();
#if DEBUG
if ((addr == NULL) || (value == NULL)) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
if (table != NULL) {
LOGMSG1("%s:First time so initialize next_table_element",__func__);
next_table_element = table;
}
if (next_table_element != NULL) {
*addr = next_table_element->addr;
*value = next_table_element->value;
LOGMSG1("%s:Found addr 0x%x, value 0x%x",__func__, *addr, *value);
next_table_element = next_table_element->next_modcntl;
return true;
}
return false;
}
/*****************************************************************************
* start_recording()
*
* - If not recording and duration_active is true then setup the timer
* to generate a signal when the duration is complete, terminating the
* recording session.
* - If not recording:
* - Enable the ETB if required.
* - Arm all trace sources.
* - Send the recording message to the app if the fifo is enabled.
* - Set the global recording state to recording.
*****************************************************************************/
void start_recording()
{
unsigned int duration;
bool duration_valid;
LOGFUNC();
if ((global_state.recording != STATE_RECORDING)
&& (global_state.duration_active == true)) {
get_set_value(DURATION, &duration, &duration_valid);
if (duration_valid == false) {
duration = 0;
}
if (duration > 0) {
int err;
/* Setup the interval timer */
struct itimerval duration_timer; //delay;
struct timeval duration_time, intraval_time;
LOGMSG1("%s:setting ITIMER_REAL for duration %d", __func__, duration);
/* Set the delay time:
* start_delay is microseconds so divide by 1000 to get seconds
* and then multiply the remainder by 1000 to get micro seconds remaining
*/
duration_time.tv_sec = duration/1000;
duration_time.tv_usec = (duration % 1000) * 1000;
intraval_time.tv_sec = 0;
intraval_time.tv_usec = 0;
duration_timer.it_interval = intraval_time; /* Set up time for one shot */
duration_timer.it_value = duration_time;
err = setitimer(ITIMER_REAL, &duration_timer, NULL);
/* Note the signal handler for setitimer has already been
* by the call to signal_handler_init() from main.
*/
#if DEBUG
if (err == -1) {
err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
}
#endif
}
}
/* Enable the ETB and start all modules that have an arm command registered */
if (global_state.recording != STATE_RECORDING) {
if (global_state.etb_active) {
LOGMSG1("%s:ETB enabled", __func__);
etb_enable();
}
{
uint32_t addr, value;
bool is_valid = modcntl_get_element(arm_table_head, &addr, &value);
LOGMSG1("%s:Starting armed modules", __func__);
while (is_valid) {
LOGMSG1("%s:arming addr 0x%x, with value 0x%x", __func__,addr, value);
remote_memory_write(addr, sizeof(uint32_t), &value);
is_valid = modcntl_get_element(NULL, &addr, &value);
}
}
if (g_fifo_enabled) {
char * broken_pipe_msg = "recording status";
remote_pipe_write(msg_recording, strlen(msg_recording), broken_pipe_msg);
LOGMSG1("%s:write to the pipe %s", __func__, msg_recording);
}
fprintf(g_stdout, "\r%s:Recording started\n", g_whoami);
fflush(g_stdout);
global_state.recording = STATE_RECORDING;
return;
}
#if DEBUG
err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
#endif
}
/*****************************************************************************
* stop_recording()
*
* - If recording:
* - Disarm all trace sources.
* - Disable the ETB if required.
* - Send the stop recording message to the app if the fifo is enabled.
* - Set the global recording state to stopped.
*****************************************************************************/
void stop_recording()
{
LOGFUNC();
if (global_state.recording == STATE_RECORDING) {
/* Stop all modules that have a disarm command registered */
uint32_t addr, value;
bool is_valid = modcntl_get_element(disarm_table_head, &addr, &value);
while (is_valid) {
LOGMSG1("%s:disarming addr 0x%x, with value 0x%x", __func__,addr, value);
remote_memory_write(addr, sizeof(uint32_t), &value);
is_valid = modcntl_get_element(NULL, &addr, &value);
}
global_state.recording = STATE_STOPPED;
/* If we are not draining the ETB then need to send it's data back.
* Note - While TEST_MODE active must change state to STATE_STOPPED before
* a non-zero etb element count is reported.
* Note - regardless of the mode (FIXED or DRAIN) we are going to
* add all the current ETB data to the queue.
*/
if (global_state.etb_active) {
LOGMSG1("%s:ETB disabled", __func__);
etb_disable();
etb_add_queue(ETB_QUEUE_ALL);
}
if (g_fifo_enabled) {
LOGMSG2("%s:write msg_stopped to pipe", __func__);
char * broken_pipe_msg = "stopped status";
remote_pipe_write(msg_stopped, strlen(msg_stopped), broken_pipe_msg);
LOGMSG1("%s:write to the pipe %s", __func__, msg_stopped);
if (!g_terminate) {
g_fifo_enabled = false;
}
}
fprintf(g_stdout, "\r%s:Recording stopped\n", g_whoami);
fflush(g_stdout);
return;
}
}
/*****************************************************************************
* start_command_handler()
*
* The client is telling the server it can start the recording process:
*
* - Initialize the global_state elements based on the operation mode (op-mode).
* - If ETB is enabled, process the ETB parameters and setup global_state
* etb elements. Configure the ETB.
* - If global_state.delay_active is true setup the timer to delay recording,
* else start recording.
* - if global_state.signal_active is true send the ready message to the app.
*
*****************************************************************************/
void start_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
unsigned int op_mode;
bool op_mode_valid;
unsigned int duration;
bool duration_valid;
unsigned int start_delay;
bool start_delay_valid;
unsigned int etb_value;
bool etb_value_valid;
LOGFUNC();
#if DEBUG
if (rbuf == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
if (global_state.recording == STATE_RECORDING) {
#if DEBUG
err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
}
#else
respond_ok(rbuf, rsp_size);
return;
}
#endif
/* Initialize the global state elements based on the operation mode. */
get_set_value(OP_MODE, &op_mode, &op_mode_valid);
if (op_mode_valid == false) {
/* The default is OP_MODE_TIME with 0 delay and ~ duration
* (must send end command to stop)
*/
op_mode = OP_MODE_TIME;
}
LOGMSG1("%s:start recording with operation mode %s", __func__, op_mode_table[op_mode]);
global_state.delay_active = false;
global_state.duration_active = false;
global_state.signal_active = false;
global_state.etb_active = false;
global_state.recording = STATE_WAITING;
switch (op_mode) {
case OP_MODE_TIME:
case OP_MODE_TRIGGER:
get_set_value(DURATION, &duration, &duration_valid);
if (duration_valid == false) {
duration = 0;
}
get_set_value(START_DELAY, &start_delay, &start_delay_valid);
if (start_delay_valid == false) {
start_delay = 0;
}
if (start_delay > 0) {
global_state.delay_active = true;
} else {
global_state.duration_active = true;
}
break;
case OP_MODE_SIGNAL:
duration = 0;
start_delay = 0;
global_state.signal_active = true;
break;
}
LOGMSG1("%s:start with duration %d ms, start delay %d, signal mode %d",
__func__, duration, start_delay, global_state.signal_active);
/* If ETB is enabled, process the ETB parameters and setup global_state
* etb elements.
*/
get_set_value(ETB_ENABLE, &etb_value, &etb_value_valid);
if ((etb_value_valid == true) && (etb_value != 0)) {
etb_bufmode_t etb_mode;
global_state.etb_active = true;
get_set_value(ETB_MODE, &etb_value, &etb_value_valid);
if (etb_value_valid == true) {
global_state.etb_mode = etb_value;
} else {
global_state.etb_mode = ETB_MODE_ONESHOT_FIXED;
}
switch (global_state.etb_mode) {
case ETB_MODE_ONESHOT_FIXED:
etb_mode = ETB_FIXED;
break;
case ETB_MODE_ONESHOT_CIRCULAR:
case ETB_MODE_DRAIN:
etb_mode = ETB_CIRCULAR;
break;
case ETB_MODE_LAST: /* This avoids a compiler warning */
break;
}
/* All requirements for TCI6614 can be handled with SYS_TIETB */
etb_config(etb_mode, SYS_TIETB);
LOGMSG1("%s:start with etb enable, etb mode is %s", __func__,
etb_mode_table[global_state.etb_mode]);
} else {
LOGMSG1("%s:start with etb disabled", __func__);
}
/* If global_state.delay_active is true setup the timer to delay recording. */
if (global_state.delay_active == true) {
int err;
/* Setup the interval timer */
struct itimerval delay;
struct timeval delay_time, intraval;
LOGMSG1("%s:setting ITIMER_REAL for start delay", __func__);
/* Set the delay time:
* start_delay is microseconds so divide by 1000 to get seconds
* and then multiply the remainder by 1000 to get micro seconds remaining
*/
delay_time.tv_sec = start_delay/1000;
delay_time.tv_usec = (start_delay % 1000) * 1000;
intraval.tv_sec = 0;
intraval.tv_usec = 0;
delay.it_interval = intraval; /* Set up time for one shot */
delay.it_value = delay_time;
err = setitimer(ITIMER_REAL, &delay, NULL);
/* Note the signal handler for setitimer has already been
* by the call to signal_handler_init() from main.
*/
#if DEBUG
if (err == -1) {
err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
}
#endif
} else {
if (global_state.duration_active == true) {
start_recording();
}
}
respond_ok(rbuf, rsp_size);
/* If signal_active is true send the ready message to the app. */
if ((global_state.signal_active == true) && (g_fifo_enabled)) {
char * broken_pipe_msg = "ready status";
remote_pipe_write(msg_ready, strlen(msg_ready), broken_pipe_msg);
LOGMSG1("%s:write to the pipe %s", __func__, msg_ready);
}
}
/*****************************************************************************
* end_command_handler()
*
* The client is telling the server to stop recording:
*
* - If delay_active is true then terminate the timer.
* - Call stop_recording().
*
*****************************************************************************/
void end_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
LOGFUNC();
#if DEBUG
if (rbuf == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
/* If delay_active is true then terminate the timer */
if (global_state.delay_active == true) {
int err;
struct timeval delay_time, intraval;
struct itimerval delay;
intraval.tv_sec = 0;
intraval.tv_usec = 0;
delay_time.tv_sec = 0;
delay_time.tv_usec = 0;
delay.it_interval = intraval;
delay.it_value = delay_time;
err = setitimer(ITIMER_REAL, &delay, NULL);
#if DEBUG
if (err == -1) {
err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
}
#endif
global_state.delay_active = false;
}
stop_recording();
respond_ok(rbuf, rsp_size);
}
/*****************************************************************************
* status_command_handler()
*
* The client is requesting status:
*
* - The status format is:
* recording or stopped, etb_bytes_available, wrapped
*
*****************************************************************************/
void status_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
LOGFUNC();
#if DEBUG
if (rbuf == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
char * msg;
int sn_size;
int etb_bytes_avaiable;
bool is_wrapped = false;
if (global_state.recording == STATE_RECORDING) {
msg = "recording";
}
else {
if ((global_state.delay_active == true) || (global_state.signal_active == true)) {
msg = "recording";
} else {
msg = "stopped";
}
}
if (set_element_table[ETB_MODE].is_valid == true) {
/* etb_data_available returns the number of uint32_t words available*/
etb_bytes_avaiable = etb_status(NULL, &is_wrapped) * sizeof(uint32_t);
} else {
etb_bytes_avaiable = 0;
}
sn_size = snprintf(rbuf, rsp_size,"%s, %d", msg, etb_bytes_avaiable);
if (is_wrapped) {
sn_size += snprintf(rbuf + sn_size, rsp_size - sn_size,", wrapped");
}
#if DEBUG
if (sn_size > rsp_size) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
}
/*****************************************************************************
* debug_command_handler()
*
* This is a dummy command handler that can be used for developing new commands.
*
*****************************************************************************/
void debug_command_handler(int argc, char *argv[], char * rbuf, size_t rsp_size)
{
LOGFUNC();
#if DEBUG
if (rbuf == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
for (int i=0; i < argc; i++) {
fprintf(g_stdout, "%s:argv[%d] is:%s\n", __func__, i, argv[i]);
}
respond_ok(rbuf, rsp_size);
}
/*****************************************************************************
* remote_memory_write()
*
* This function performs the physical memory write.
*
* - Get the virtual address
* - Write the data to the virtual address, 1 32-bit word per access.
*****************************************************************************/
int remote_memory_write(uint32_t addr, size_t byte_cnt, uint32_t * pbuf)
{
LOGFUNC();
#if DEBUG
if (pbuf == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
void * v_start_addr;
size_t len;
void * v_addr = is_memory_mapped(addr, byte_cnt, &v_start_addr, &len);
int err;
#if DEBUG
if ((byte_cnt % 4) != 0) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
while (byte_cnt > 0) {
LOGMSG1("%s:Writing phy addr 0x%x, vir addr 0x%x, data is:0x%x",
__func__, addr, v_addr, *pbuf);
*(volatile uint32_t *)v_addr++ = *pbuf++;
byte_cnt -= 4;
}
#if DEBUG
if ( err == -1) {
err_handler(ERR_TYPE_SYSTEM, ERR_DEBUG);
}
#endif
return 0;
}
/*****************************************************************************
* remote_memory_read()
*
* This function performs the physical memory read.
*
* - Get the virtual address
* - Read the data from the virtual address, 1 32-bit word per access.
*****************************************************************************/
int remote_memory_read(uint32_t addr, size_t byte_cnt, uint32_t * pbuf)
{
LOGFUNC();
#if DEBUG
if (pbuf == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
void * v_start_addr;
size_t len;
volatile uint32_t * v_addr = (uint32_t *)is_memory_mapped(addr, byte_cnt, &v_start_addr, &len);
#if DEBUG
if ((byte_cnt % 4) != 0) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
while (byte_cnt > 0) {
*pbuf = *(volatile uint32_t *)v_addr;
LOGMSG1("%s:Reading phy addr 0x%x, vir addr 0x%x, data is:0x%x",
__func__, addr, v_addr, *pbuf);
byte_cnt -= 4;
pbuf++;
v_addr++;
}
return 0;
}
/*****************************************************************************
* remote_pipe_write()
*
* This function performs the g_fifo_fd write.
*
*****************************************************************************/
void remote_pipe_write(const char * wr_buf_p, size_t wr_bytecnt, char * broken_pipe_msg)
{
LOGFUNC();
while ( wr_bytecnt > 0) {
size_t rc = write(g_fifo_fd, wr_buf_p, wr_bytecnt);
if ((rc == -1) && (errno == EPIPE)) {
if (broken_pipe_msg != NULL) {
fprintf(g_stdout,"%s:warning - pipe reading end closed when sending %s.\n"
"%s: This can be caused by issuing a client operation before\n"
"%s: starting the task that opens the pipe's reading end OR\n"
"%s: early termination of the task that opens the pipe's reading end.\n",
g_whoami, broken_pipe_msg, g_whoami, g_whoami, g_whoami);
}
break;
}
if ((rc == -1) && (errno == EINTR)) {
continue;
}
wr_buf_p += rc;
wr_bytecnt -= rc;
}
}
/*****************************************************************************
* Private Functions
*****************************************************************************/
/******************************************************************************
* Commands Specific Definitions for cTools_memMap and cTools_memUnMap
*
*****************************************************************************/
/* Only need to open mem_fd once */
static int mem_fd = 0;
struct map_element_t {
void * v_map_addr; /* Virtual address */
size_t v_map_size; /* Mapped size in bytes - may be whole number of pages */
uint32_t phy_addr; /* Used to request adj_v_addr */
struct map_element_t * prev_map_element;
struct map_element_t * next_map_element;
};
static struct map_element_t * map_table_head = NULL; // Map Object Link List head
/*****************************************************************************
* cTools_memMap()
*
* This function performs the physical memory mapping to this virtual space.
*
* - Mapped memory blocks are kept in a link list, so first check the list
* if the map already exist. This is common if the client is re-started.
* - Open /dev/mem if not already open.
* - Allocate space for the map element.
* - Adjust the size to a whole number of pages.
* - Map the physical address to the virtual space. Align the physical address
* to a page boundary if necessary.
* - Add map element to end of link list.
*
* Note: Even though this is a private function to ctprof_srv, it is also
* used by ETBLib (which is statically linked with ctprof_srv). For this
* reason the function prototype is required to be compatible with
* the cToolsHelper version that may be used with ETBLib.
*****************************************************************************/
void * cTools_memMap(uint32_t map_addr, uint32_t map_size)
{
struct map_element_t * map_element;
uint32_t page_size = sysconf(_SC_PAGE_SIZE);
/* Check if map_addr already mapped.
* Could make this more general purpose by checking
* if the address is in a mapped range, but this works fine
* as a simple check of debug ip mapping.
*/
struct map_element_t * this_map_element = map_table_head;
while (this_map_element != NULL) {
if ((this_map_element->phy_addr == map_addr)
&& (this_map_element->v_map_size >= map_size)) {
LOGMSG1("%s:0x%x already mapped", __func__, map_addr);
return this_map_element->v_map_addr;
}
this_map_element = this_map_element->next_map_element;
}
if(mem_fd == 0) {
mem_fd = open("/dev/mem", O_RDWR | O_SYNC | O_RSYNC );
if (mem_fd == -1) {
err_handler(ERR_TYPE_SYSTEM, ERR_MMAP_OPEN);
}
}
/* Allocate space for the map element */
map_element = (struct map_element_t *)malloc(sizeof(struct map_element_t));
if (map_element == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_MEM_ALLOC);
}
/* Note: the physical address (map_addr) may need to be aligned to a
* PAGE_SIZE and map_size may need to be a multiple of PAGE_SIZE.
* This means the virtual address may need to be adjusted by the page mask
* to get the same physical address (adj_v_addr).
*/
if ((map_size % page_size) != 0) {
map_element->v_map_size = ((map_size / page_size) + 1) * page_size;
} else {
map_element->v_map_size = map_size;
}
map_element->v_map_addr = mmap(0, map_element->v_map_size,
PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd,
map_addr & ~(map_element->v_map_size -1));
mlock(map_element->v_map_addr, map_element->v_map_size);
map_element->phy_addr = map_addr;
LOGMSG1("%s:phy addr: 0x%08x, phy size: %d", __func__, map_addr, map_size);
LOGMSG1("%s:mapping phy addr 0x%08x",__func__,
map_addr & ~(map_element->v_map_size -1));
LOGMSG1("%s:vir addr: 0x%08x, vir size: %d",__func__,
map_element->v_map_addr, map_element->v_map_size);
if (map_element->v_map_addr == MAP_FAILED) {
free(map_element);
/* Fatal so error handler exits */
err_handler(ERR_TYPE_SYSTEM, ERR_MMAP_FAIL);
}
/* Add map element to end of link list */
map_element->next_map_element = NULL;
if (map_table_head == NULL) {
map_table_head = map_element;
map_element->prev_map_element = NULL;
} else {
/* Search for the last element */
struct map_element_t * this_map_element = map_table_head;
while (this_map_element->next_map_element != NULL) {
this_map_element = this_map_element->next_map_element;
}
this_map_element->next_map_element = map_element;
map_element->prev_map_element = this_map_element;
}
return map_element->v_map_addr;
}
/*****************************************************************************
* cTools_memUnMap()
*
* This function performs the virtual memory un-mapping.
*
* - Find the virtual address in the link list.
* - Unmap the virtual block.
* - Remove it from the link list.
*
* Note: Eventhough this is a private function to ctprof_srv, it is also
* used by ETBLib (which is statically linked with ctprof_srv). For this
* reason the function prototype is required to be compatible with
* the cToolsHelper version that may be used with ETBLib.
*****************************************************************************/
void cTools_memUnMap(uint32_t v_map_addr, uint32_t map_size)
{
LOGFUNC();
struct map_element_t * map_element;
/* Search for the element */
struct map_element_t * this_map_element = map_table_head;
#if DEBUG
if (this_map_element == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
while (this_map_element->v_map_addr != (void *)v_map_addr) {
this_map_element = this_map_element->next_map_element;
if (this_map_element == NULL) break;
}
#if DEBUG
if (this_map_element == NULL) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
int ret_val = munmap(this_map_element->v_map_addr,
this_map_element->v_map_size);
if (ret_val == (int)MAP_FAILED) {
free(map_element);
/* Fatal so error handler exits */
err_handler(ERR_TYPE_SYSTEM, ERR_MUNMAP_FAIL);
}
/* Only element in link list */
if ((this_map_element->next_map_element == NULL)
&& (this_map_element->prev_map_element == NULL)) {
map_table_head = NULL;
close(mem_fd);
mem_fd = 0;
}
/* First element in link list */
if ((this_map_element->next_map_element != NULL)
&& (this_map_element->prev_map_element == NULL)) {
map_table_head = this_map_element->next_map_element;
map_table_head->prev_map_element = NULL;
}
/* Last element in link list */
if ((this_map_element->next_map_element == NULL)
&& (this_map_element->prev_map_element != NULL)) {
this_map_element->prev_map_element->next_map_element = NULL;
}
/* Middle element in link list */
if ((this_map_element->next_map_element != NULL)
&& (this_map_element->prev_map_element != NULL)) {
this_map_element->prev_map_element->next_map_element =
this_map_element->next_map_element;
this_map_element->next_map_element->prev_map_element =
this_map_element->prev_map_element;
}
free(this_map_element);
}
/*****************************************************************************
* is_memory_mapped()
*
* Return virtual version of address if mapped
*
* - Find the physical address in the link list and return it's virtual address.
*****************************************************************************/
static void * is_memory_mapped(uint32_t addr, size_t byte_cnt, void ** v_start, size_t * len)
{
LOGFUNC();
uint32_t page_size = sysconf(_SC_PAGE_SIZE);
uint32_t page_mask = page_size -1;
/*Search map table for match */
struct map_element_t * this_map_element = map_table_head;
while (this_map_element != NULL) {
if ((this_map_element->phy_addr == (addr & ~(this_map_element->v_map_size - 1)))
&& ((addr + byte_cnt - 1) <= (this_map_element->phy_addr
+ this_map_element->v_map_size - 1)) ) {
LOGMSG1("%s:Map element found, returning address 0x%x", __func__,
this_map_element->v_map_addr + (addr & page_mask));
*v_start = this_map_element->v_map_addr;
*len = this_map_element->v_map_size;
return this_map_element->v_map_addr + (addr & page_mask);
}
this_map_element = this_map_element->next_map_element;
}
#if DEBUG
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
#endif
return 0;
}
/*****************************************************************************
* respond_ok()
*
* Provide the RSP "OK" response.
*
*****************************************************************************/
static void respond_ok(char * rbuf, size_t rsp_size)
{
int sn_size = snprintf(rbuf, rsp_size,"OK");
#if DEBUG
if (sn_size > rsp_size) {
err_handler(ERR_TYPE_LOCAL, ERR_DEBUG);
}
#endif
}