blob: 892b756806f0f29cb3cf54bb73fda0fe887e3717 [file] [log] [blame]
package xdc.rta;
import java.util.BitSet;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/*!
* ======== Control ========
* Defines a protocol for communicating RTA control commands to the target.
*
* The control APIs form a command packet which is sent to the target over
* the configured transport.
*
* The target will respond to all commands with either the requested data
* or an empty acknowledgement.
*
* The control APIs are asynchronous, so that the caller does not have to wait
* for the acknowledgement. To implement this, the caller passes an
* IControlResponse object (defined by the caller) which specifies some
* callback functions that the Control class will call when it receives a
* response from the target.
*/
public class Control {
/* Underlying transport for sending down commands. */
IControlTransport transport;
/* RTA Decoder */
private Decoder decoder = null;
private MetaData meta = null;
/* Control command ids. */
static final int READ_MASK = 0;
static final int WRITE_MASK = 1;
static final int LOGGER_OFF = 2;
static final int LOGGER_ON = 3;
static final int GET_CPU_SPEED = 4;
static final int RESET_LOGGER = 5;
static final int CHANGE_PERIOD = 6;
static final int START_TRANSFER = 7;
static final int STOP_TRANSFER = 8;
/*
* Thread for continually polling response stream and to make command APIs
* asyncrhonous.
*/
private ResponseThread responseThread = null;
/* Flag for notifying responseThread to terminate. */
public Boolean terminated = false;
/*
* ======== IControlResponse ========
* Response object interface for making control commands asynchronous.
*
* Because the Control object may block on the response stream, all of the
* command APIs are asynchronous so that they will return immediately.
*
* Each API takes an IControlResponse object as an argument, and will use
* this object to notify the caller when a response has been received.
*/
public interface IControlResponse {
/**
* Is called when a response is received from target, done()
* implementaion need to take response, responsetype and error,
* and perform proper checkings and either report an error or posts
* the result to a SWT thread to update a GUI component.
*/
void done();
/**
* Need to be called before {@link IControl.IControlResponse#done()}
* and if set, done implementation need to perform type checking on
* the response
* @param klass
*/
void setResponseType(Class<?> klass);
/**
* Actual response, need to be called before
* {@link IControl.IControlResponse#done()}
* @param response
*/
void setResponse(Object response);
/**
* In case there is an error, setError need be called with the
* errorMessage before calling {@link IControl.IControlResponse#done()}
* @param errorMessage
*/
void setError(String errorMessage);
}
/*
* ======== Control ========
* Control object constructor.
*
* The Control object requires a transport object and a decoder instance.
*/
public Control(IControlTransport transport, Decoder decoder, MetaData meta) {
this.transport = transport;
this.decoder = decoder;
this.meta = meta;
/* Create a response thread to continuously read the response stream. */
responseThread = new ResponseThread(transport, this);
Thread t = new Thread(responseThread);
t.setName("RTA Response Thread");
t.start();
}
/*
* ======== terminate ========
* Shuts down the Control object.
*
* 'terminate' must be called on the Control object before the underlying
* transport is closed.
*/
public void terminate() throws Exception {
/* Notify the responseThread to terminate. */
synchronized (terminated) {
terminated = true;
}
/* Terminate the underlying transport. */
transport.terminate();
}
/*
* ======== writeCommmand ========
*/
public void writeCommand(int cmdId, long arg1, long arg2) throws Exception
{
int argSize = meta.getTargetArgSize();
byte[] command = new byte[3 * argSize];
decoder.encodeArg(command, 0, cmdId, argSize);
decoder.encodeArg(command, argSize, arg1, argSize);
decoder.encodeArg(command, 2 * argSize, arg2, argSize);
/* Obtain a lock on the terminated flag. */
synchronized (terminated) {
/* Check terminated flag before writing to target. */
if (terminated) {
throw (new TerminateException());
}
/* Write command */
transport.write(command, 0, 3 * argSize);
}
}
/*
* ======== getLogConfig ========
*
*/
public void getLogConfig(IControlResponse asyncResponse, String module) throws Exception {
/* Perform address lookup for module name */
// TODO - Try removing the leading underscore. Elf targets don't support the leading underscore.
String maskSym = "_" + module.replace('.', '_') + "_Module__diagsMask__C";
//long symAddr = symbolTable.get(maskSym);
long symAddr = 0;
symAddr = transport.getAddress(maskSym);
/* Send the command to the target. */
try {
debugPrint("Writing command to getDiagsMask for " + module + "...");
writeCommand(READ_MASK, symAddr, 0);
debugPrint("...command sent.");
}
catch (TerminateException e) {
// TODO - What to do here?
return;
}
catch (Exception e) {
// TODO - Report this error.
e.printStackTrace();
return;
}
/* Tell the response thread to expect a reply. */
responseThread.addCommand(new ExpectedResponse(READ_MASK, module, asyncResponse));
}
/*
* ======== setLogConfig ========
*
*/
public void setLogConfig(IControlResponse asyncResponse, String module, DiagConfig mask) {
/* Get the address of the diagsMask__C */
String maskSym = "_" + module.replace('.', '_') + "_Module__diagsMask__C";
long symAddr = 0;
try {
symAddr = transport.getAddress(maskSym);
}
catch (Exception e) {
e.printStackTrace();
return;
}
/* Get the new diags mask. */
BitSet bitMask = mask.toBitSet();
/* Convert the diags mask to a value to write to the target. */
// TODO - DiagConfig should implement this.
String byteStr = "";
for (int i = 15; i >= 0; i--) {
byteStr += bitMask.get(i) ? "1" : "0";
}
int value = Integer.parseInt(byteStr, 2);
/* Send the command to the target. */
try {
debugPrint("Writing command to setDiagsMask");
writeCommand(WRITE_MASK, symAddr, value);
}
catch (TerminateException e) {
// TODO - What to do here?
return;
}
catch (Exception e) {
// TODO - Report this error.
e.printStackTrace();
return;
}
/* Tell the response thread to expect a reply. */
responseThread.addCommand(new ExpectedResponse(WRITE_MASK, null, asyncResponse));
}
/*
* ======== disableLogging ========
*/
public void disableLogging(IControlResponse asyncResponse, String logger) {
/* Find the logger index */
int logIndex = -1;
String[] loggers = meta.getLoggerNames();
// TODO - Does decoder already have this mapping?
for (int i = 0; i < loggers.length; i++) {
if (loggers[i].equals(logger)) {
logIndex = i;
break;
}
}
/* TODO - What if logger isn't found? */
if (logIndex == -1) {
System.out.println("Didn't find logger: " + logger);
return;
}
/* Send the command to the target. */
try {
debugPrint("Writing command to disable log number: " + logIndex);
writeCommand(LOGGER_OFF, logIndex, 0);
}
catch (TerminateException e) {
// TODO - What to do here?
return;
}
catch (Exception e) {
// TODO - Report this error.
e.printStackTrace();
return;
}
/* Tell the response thread to expect a reply. */
responseThread.addCommand(new ExpectedResponse(LOGGER_OFF, null, asyncResponse));
}
/*
* ======== enableLogging ========
*/
public void enableLogging(IControlResponse asyncResponse, String logger) {
/* Find the logger index */
// TODO - Does decoder already have this mapping?
int logIndex = -1;
String[] loggers = meta.getLoggerNames();
for (int i = 0; i < loggers.length; i++) {
if (loggers[i].equals(logger)) {
logIndex = i;
break;
}
}
/* TODO - What if logger isn't found? */
if (logIndex == -1) {
return;
}
/* Send the command to the target. */
try {
debugPrint("Writing command to enable log");
writeCommand(LOGGER_ON, logIndex, 0);
}
catch (TerminateException e) {
// TODO - What to do here?
return;
}
catch (Exception e) {
// TODO - Report this error.
e.printStackTrace();
return;
}
/* Tell the response thread to expect a reply. */
responseThread.addCommand(new ExpectedResponse(LOGGER_ON, null, asyncResponse));
}
/*
* ======== startTransfer ========
* Sends a command to the target to start transferring up log events.
*/
public void startTransfer(IControlResponse asyncResponse)
{
/* Tell the response thread to expect a reply. */
responseThread.addCommand(new ExpectedResponse(START_TRANSFER, null, asyncResponse));
/* Send the command to the target. */
try {
debugPrint("Writing command to start transferring events.");
writeCommand(START_TRANSFER, 0, 0);
}
catch (TerminateException e) {
// TODO - What to do here?
return;
}
catch (Exception e) {
// TODO - Report this error.
e.printStackTrace();
return;
}
}
/*
* ======== stopTransfer ========
* Sends a command to the target to stop transferring up log events.
*/
public void stopTransfer(IControlResponse asyncResponse)
{
/* Send the command to the target. */
try {
debugPrint("Writing command to stop transferring events.");
writeCommand(STOP_TRANSFER, 0, 0);
}
catch (TerminateException e) {
// TODO - What to do here?
return;
}
catch (Exception e) {
// TODO - Report this error.
e.printStackTrace();
return;
}
/* Tell the response thread to expect a reply. */
responseThread.addCommand(new ExpectedResponse(STOP_TRANSFER, null, asyncResponse));
}
/*
* ======== getCPUSpeed ========
*/
public void getCPUSpeed(IControlResponse asyncResponse) {
/* Send the command to the target. */
try {
debugPrint("Writing command to get CPU speed.");
writeCommand(GET_CPU_SPEED, 0, 0);
}
catch (TerminateException e) {
// TODO - What to do here?
return;
}
catch (Exception e) {
// TODO - Report this error.
e.printStackTrace();
return;
}
/* Tell the response thread to expect a reply. */
responseThread.addCommand(new ExpectedResponse(GET_CPU_SPEED, null, asyncResponse));
}
/*
* ======== resetLogger ========
*/
public void resetLogger(IControlResponse asyncResponse, String logger) {
/* Find the logger index */
// TODO - Does decoder already have this mapping?
int logIndex = -1;
String[] loggers = meta.getLoggerNames();
for (int i = 0; i < loggers.length; i++) {
if (loggers[i] == logger) {
logIndex = i;
break;
}
}
/* TODO - What if logger isn't found? */
if (logIndex == -1) {
return;
}
/* Send the command to the target. */
try {
debugPrint("Writing command to reset log");
writeCommand(RESET_LOGGER, logIndex, 0);
}
catch (TerminateException e) {
// TODO - What to do here?
return;
}
catch (Exception e) {
// TODO - Report this error.
e.printStackTrace();
return;
}
/* Tell the response thread to expect a reply. */
responseThread.addCommand(new ExpectedResponse(RESET_LOGGER, null, asyncResponse));
}
/*
* ======== changePeriod ========
*/
public void changePeriod(IControlResponse asyncResponse, int period) {
/* Send the command to the target. */
try {
debugPrint("Writing command to change period");
writeCommand(CHANGE_PERIOD, period, 0);
}
catch (TerminateException e) {
// TODO - What to do here?
return;
}
catch (Exception e) {
// TODO - Report this error.
e.printStackTrace();
return;
}
/* Tell the response thread to expect a reply. */
responseThread.addCommand(new ExpectedResponse(CHANGE_PERIOD, null, asyncResponse));
}
public boolean hasDiagsMask(String module)
{
/*
ISymbolPackage symPkg = SymbolManager.get().getSymbolPackage(null, device);
ISymbolFile[] symFiles = symPkg.getSymbolFiles();
ISymbol sym = symFiles[0].getSymbolByName("_ti_sysbios_knl_Task_Module__root__V");
*/
/*
* Perform address lookup for module name to determine if diagsMask
* exists for this module.
*/
String maskSym = "_" + module.replace('.', '_') + "_Module__diagsMask__C";
try {
transport.getAddress(maskSym);
}
catch (Exception e) {
return(false);
}
return(true);
}
/*
* ======== valToBitSet ========
* Convert value to 16-bit bitset.
* TODO - does this belong in DiagConfig?
*/
private BitSet valToBitSet(long value)
{
int bit = 0;
/* diagsMask is a 16-bit mask */
BitSet mask = new BitSet(16);
/* Convert the value to the bitset. */
while (value != 0) {
mask.set(bit, (value % 2) > 0);
value /= 2;
bit++;
}
return(mask);
}
/*
* ======== postResponse ========
*/
private void postResponse(byte[] response, ExpectedResponse expectResp)
{
int argSize = meta.getTargetArgSize();
long respCmd = decoder.decodeBytes(response, 0, argSize, false);
long resp0 = decoder.decodeBytes(response, argSize * 1, argSize, false);
long resp1 = decoder.decodeBytes(response, argSize * 2, argSize, false);
debugPrint("HOST: Received response for command id " + respCmd);
if (respCmd != expectResp.cmdId) {
//TODO
System.out.println("Exception: RTA received a corrupted control response from the target." +
"Received command id: " + respCmd + ", expected: " + expectResp.cmdId);
return;
//postControlResponse(Exception: "RTA received a corrupted control response from the target." +
// "Received command id: " + respCmd + ", expected: " + expectResp);
//return;
}
switch ((int) respCmd) {
case READ_MASK:
debugPrint("Mask from target for module " + expectResp.arg + " =\n " +
response[4] + " " + response[5] + " " + response[6] + " " + response[7]);
debugPrint("Mask value: " + resp0);
/* Create a bit set from the byte array. */
BitSet mask = valToBitSet(resp0);
debugPrint("Bitset of target mask: " + mask.toString());
/*
* Start with the initial settings so we can check for
* inconsistencies in 'ALWAYS' values. The argument in
* the expected response here is the module name.
*/
DiagConfig config = meta.getModuleDiagConfig((String) expectResp.arg);
debugPrint("Original diags mask (" + expectResp.arg + "):");
config.printDiags();
/* Update will throw exception if bits don't match up */
try {
config.updateFromBitSet(mask);
}
catch (Exception e) {
expectResp.asyncResponse.setError("Received error updating diags " +
"mask for module " + expectResp.arg +
": " + e.getMessage());
}
debugPrint("Updated diags mask from target (" + expectResp.arg + "):");
config.printDiags();
expectResp.asyncResponse.setResponseType(DiagConfig.class);
expectResp.asyncResponse.setResponse(config);
expectResp.asyncResponse.done();
break;
case GET_CPU_SPEED:
long hi = resp0;
long lo = resp1;
long freq = (long) (hi * Math.pow(2, argSize)) + lo;
expectResp.asyncResponse.setResponseType(Long.class);
expectResp.asyncResponse.setResponse(freq);
expectResp.asyncResponse.done();
break;
case WRITE_MASK:
case LOGGER_OFF:
case LOGGER_ON:
case RESET_LOGGER:
case CHANGE_PERIOD:
case START_TRANSFER:
case STOP_TRANSFER:
expectResp.asyncResponse.done();
break;
default:
expectResp.asyncResponse.setError("RTA received corrupted control response from target. Received command id: " + respCmd);
expectResp.asyncResponse.done();
break;
}
}
static public class TerminateException extends Exception {
private static final long serialVersionUID = 1L;
public TerminateException() {
super();
}
}
/*
* ======== readResponse ========
* Reads and returns any available data from the transport.
*/
public byte[] readResponse() throws Exception
{
debugPrint("Control.readResponse called...");
int avail = 0;
while (true) {
/* Obtain a lock on terminated flag */
synchronized (terminated) {
/* Check if reader is terminated before calling available. */
if (terminated) {
throw (new TerminateException());
}
/* Check for available data. */
if ((avail = transport.available()) != 0) {
break;
}
}
/* If no data is available, sleep before checking again. */
Thread.sleep(1);
}
byte[] buffer = new byte[avail];
/* Obtain a lock on terminated flag. */
synchronized (terminated) {
/* Check terminated before reading. */
if (terminated) {
throw (new TerminateException());
}
/* Read the data */
transport.read(buffer, 0, avail, 0);
}
return (buffer);
}
public class ExpectedResponse {
int cmdId;
Object arg;
IControlResponse asyncResponse;
public ExpectedResponse(int cmdId, Object arg, IControlResponse asyncResponse)
{
this.cmdId = cmdId;
this.arg = arg;
this.asyncResponse = asyncResponse;
}
}
/*
*
* ======== ResponseThread ========
* The ResponseThread continuously reads the response stream.
* This is a necessary part of RTDX--if the response stream is not read,
* then eventually the RTDX host buffers will fill up. RTDX cannot clear
* the host buffers until all streams have checked for any available data.
*/
public class ResponseThread implements Runnable {
/* Underlying transport for receiving responses. */
IControlTransport transport;
/* Control object for calling postResponse. */
Control control;
/* Size in bytes of a response */
static final int responseSize = 12; // Three 32-bit values = 12 bytes
LinkedBlockingQueue<ExpectedResponse> expectedResponses = new LinkedBlockingQueue<ExpectedResponse>();
ArrayBlockingQueue<byte[]> responses = new ArrayBlockingQueue<byte[]>(2);
/*
* ======== ResponseThread ========
* ResponseThread constructor.
*
* The response thread requires the underlying transport for reading
* responses and the control object for calling postResponse.
*/
public ResponseThread(IControlTransport transport, Control control) {
this.transport = transport;
this.control = control;
}
/*
* ======== run ========
*
*/
public void run() {
/* Buffer for receiving responses. */
byte[] buffer = null;
/* Continuously read the response stream for responses. */
while (true) {
/* Retrieve any responses from the target. */
try {
buffer = control.readResponse();
}
catch (TerminateException e) {
debugPrint("xdc.rta.Control - ResponseThread received terminate, exiting.");
return;
}
catch (Exception e) {
e.printStackTrace();
// TODO - Report problem.
}
debugPrint("Received " + buffer.length + " bytes (" +
(buffer.length / responseSize) + " responses) from target.");
/* For each response... */
for (int i = 0; i < buffer.length / responseSize; i++) {
/* If we were not expecting any responses, report an error. */
if (expectedResponses.size() == 0) {
// TODO - Report this error
System.out.println("Exception: RTA received a control " +
"response from the target but was not " +
"expecting one.");
return;
}
byte[] response = new byte[responseSize];
/* Copy the bytes into a response buffer. */
System.arraycopy(buffer, i * responseSize, response, 0, responseSize);
/* Get the current expected response. */
ExpectedResponse expectResp = null;
try {
expectResp = expectedResponses.take();
}
catch (InterruptedException e) {
// TODO
e.printStackTrace();
}
/* Post the response. */
control.postResponse(response, expectResp);
}
}
}
public void addCommand(ExpectedResponse resp) {
try {
expectedResponses.put(resp);
}
/* TODO - What do we do here? */
catch (InterruptedException e) {
System.out.println("Caught exception while trying to add command to expectedResponses: " + e);
}
}
}
/*
* ======== debugPrint ========
*/
public static String traceEnable = System.getProperty("xdc.rta.traceEnable");
public static void debugPrint(String str) {
if ((traceEnable != null) && traceEnable.equals("true")) {
System.out.println("[RTA] " + str);
}
}
}