| 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); |
| } |
| } |
| |
| } |