diff --git a/tests/manual/trk/runner.cpp b/tests/manual/trk/runner.cpp index 45cc4ba9bac09517bd889e1a44b7854b04d28058..eeb080ca0d8ec2128e0c2ab79a0f3099b0cb7359 100755 --- a/tests/manual/trk/runner.cpp +++ b/tests/manual/trk/runner.cpp @@ -28,7 +28,7 @@ **************************************************************************/ #include "trkutils.h" -#include "trkdevice.h" +#include "trkdevicex.h" #include <QtCore/QCoreApplication> #include <QtCore/QDebug> @@ -58,6 +58,8 @@ using namespace trk; enum { KnownRegisters = RegisterPSGdb + 1}; +#define CB(s) Callback(this, &Adapter::s) + static const char *registerNames[KnownRegisters] = { "A1", "A2", "A3", "A4", @@ -69,17 +71,18 @@ static const char *registerNames[KnownRegisters] = 0, "PSGdb" }; -static inline void dumpRegister(int n, uint value, QByteArray &a) +static QByteArray dumpRegister(int n, uint value) { - a += ' '; + QByteArray ba; + ba += ' '; if (n < KnownRegisters && registerNames[n]) { - a += registerNames[n]; + ba += registerNames[n]; } else { - a += '#'; - a += QByteArray::number(n); + ba += '#'; + ba += QByteArray::number(n); } - a += "=0x"; - a += QByteArray::number(value, 16); + ba += "=0x" + hexNumber(value); + return ba; } /////////////////////////////////////////////////////////////////////// @@ -104,7 +107,7 @@ public: void setVerbose(int verbose) { m_verbose = verbose; } void setSerialFrame(bool b) { m_serialFrame = b; } void setRegisterEndianness(Endianness r) { m_registerEndianness = r; } - void setBufferedMemoryRead(bool b) { qDebug() << "Buffered=" << b; m_bufferedMemoryRead = b; } + void setBufferedMemoryRead(bool b) { m_bufferedMemoryRead = b; } public slots: void startServer(); @@ -124,6 +127,7 @@ private slots: void handleProcStateChanged(QProcess::ProcessState newState); void run(); void startGdb(); + void writeToGdb(const QString &msg); private: friend class RunnerGui; @@ -131,11 +135,8 @@ private: void sendOutput(QObject *sender, const QString &data); void sendOutput(const QString &data) { sendOutput(0, data); } - QStringList m_trkServerOptions; QString m_endianness; - QString m_trkServerName; - bool m_isUnix; - bool m_waitForAdapter; + QString m_trkServerName; // QString m_gdbServerName; // 127.0.0.1:(2222+uid) QProcess m_gdbProc; @@ -178,6 +179,7 @@ public: void handleAndReportSetBreakpoint(const TrkResult &result); void handleReadMemoryBuffered(const TrkResult &result); void handleReadMemoryUnbuffered(const TrkResult &result); + void handleStepRange(const TrkResult &result); void reportReadMemoryBuffered(const TrkResult &result); void reportToGdb(const TrkResult &result); @@ -190,7 +192,7 @@ public: void startInferiorIfNeeded(); void interruptInferior(); - TrkWriteQueueDevice m_trkDevice; + TrkDevice m_trkDevice; QList<Breakpoint> m_breakpoints; @@ -200,13 +202,16 @@ public: Q_SLOT void handleGdbConnection(); Q_SLOT void readFromGdb(); void handleGdbResponse(const QByteArray &ba); - void sendGdbMessage(const QByteArray &msg, const QByteArray &logNote = QByteArray()); - void sendGdbMessageAfterSync(const QByteArray &msg, const QByteArray &logNote = QByteArray()); + void sendGdbMessage(const QByteArray &msg, + const QByteArray &logNote = QByteArray()); + void sendGdbMessageAfterSync(const QByteArray &msg, + const QByteArray &logNote = QByteArray()); void sendGdbAckMessage(); bool sendGdbPacket(const QByteArray &packet, bool doFlush); void executeGdbCommand(const QString &msg); void logMessage(const QString &msg, bool force = false); + Q_SLOT void trkLogMessage(const QString &msg); QTcpServer m_gdbServer; QPointer<QTcpSocket> m_gdbConnection; @@ -226,22 +231,18 @@ public: Adapter::Adapter() { m_gdbAckMode = true; - m_verbose = 1; + m_verbose = 2; m_registerEndianness = LittleEndian; //m_serialFrame = true; m_serialFrame = false; m_startInferiorTriggered = false; - m_bufferedMemoryRead = true; + //m_bufferedMemoryRead = true; + m_bufferedMemoryRead = false; // m_breakpoints.append(Breakpoint(0x0040)); // E32Main + m_breakpoints.append(Breakpoint(0x0cc8)); // E32Main m_trkServerName = "/dev/rfcomm0"; -#ifdef Q_OS_UNIX - m_isUnix = true; -#else - m_isUnix = false; -#endif m_endianness = "little"; - m_waitForAdapter = false; uid_t userId = getuid(); m_gdbServerName = QString("127.0.0.1:%1").arg(2222 + userId); @@ -251,17 +252,22 @@ Adapter::Adapter() m_rfcommProc.setObjectName("RFCOMM PROCESS"); connectProcess(&m_rfcommProc); + + connect(&m_trkDevice, SIGNAL(logMessage(QString)), + this, SLOT(trkLogMessage(QString))); } Adapter::~Adapter() { - // Trk - - // Gdb m_gdbServer.close(); logMessage("Shutting down.\n", true); } +void Adapter::trkLogMessage(const QString &msg) +{ + logMessage("TRK " + msg); +} + void Adapter::setGdbServerName(const QString &name) { m_gdbServerName = name; @@ -295,9 +301,9 @@ void Adapter::startServer() sendTrkInitialPing(); sendTrkMessage(0x01); // Connect - sendTrkMessage(0x05, Callback(this, &Adapter::handleSupportMask)); - sendTrkMessage(0x06, Callback(this, &Adapter::handleCpuType)); - sendTrkMessage(0x04, Callback(this, &Adapter::handleTrkVersions)); // Versions + sendTrkMessage(0x05, CB(handleSupportMask)); + sendTrkMessage(0x06, CB(handleCpuType)); + sendTrkMessage(0x04, CB(handleTrkVersions)); // Versions //sendTrkMessage(0x09); // Unrecognized command //sendTrkMessage(0x4a, 0, // "10 " + formatString("C:\\data\\usingdlls.sisx")); // Open File @@ -324,7 +330,7 @@ void Adapter::startServer() void Adapter::logMessage(const QString &msg, bool force) { if (m_verbose || force) - emit output("ADAPTER: ", msg); + emit output(QString(), msg); } // @@ -344,7 +350,7 @@ void Adapter::handleGdbConnection() static inline QString msgGdbPacket(const QString &p) { - return QLatin1String("gdb: -> ") + p; + return QLatin1String("gdb: ") + p; } void Adapter::readFromGdb() @@ -352,7 +358,7 @@ void Adapter::readFromGdb() QByteArray packet = m_gdbConnection->readAll(); m_gdbReadBuffer.append(packet); - logMessage(msgGdbPacket(QString::fromAscii(packet))); + logMessage("gdb: -> " + QString::fromAscii(packet)); if (packet != m_gdbReadBuffer) logMessage("buffer: " + m_gdbReadBuffer); @@ -462,7 +468,7 @@ void Adapter::sendGdbMessage(const QByteArray &msg, const QByteArray &logNote) void Adapter::sendGdbMessageAfterSync(const QByteArray &msg, const QByteArray &logNote) { QByteArray ba = msg + char(1) + logNote; - sendTrkMessage(TRK_WRITE_QUEUE_NOOP_CODE, Callback(this, &Adapter::reportToGdb), "", ba); // Answer gdb + sendTrkMessage(TRK_WRITE_QUEUE_NOOP_CODE, CB(reportToGdb), "", ba); // Answer gdb } void Adapter::reportToGdb(const TrkResult &result) @@ -481,7 +487,7 @@ void Adapter::reportToGdb(const TrkResult &result) sendGdbMessage(message, note); } -static QByteArray breakpointTrkMessage(uint addr, int len, int pid, bool armMode = true) +static QByteArray trkBreakpointMessage(uint addr, int len, int pid, bool armMode = true) { QByteArray ba; appendByte(&ba, 0x82); // unused option @@ -543,7 +549,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) QByteArray ba; appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); - sendTrkMessage(0x18, Callback(this, &Adapter::handleSignalContinue), ba, signalNumber); // Continue + sendTrkMessage(0x18, CB(handleSignalContinue), ba, signalNumber); // Continue } else if (response.startsWith("D")) { @@ -564,7 +570,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) appendShort(&ba, RegisterCount - 1); // last register appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); - sendTrkMessage(0x12, Callback(this, &Adapter::handleAndReportReadRegisters), ba, QVariant(), true); + sendTrkMessage(0x12, CB(handleAndReportReadRegisters), ba, QVariant(), true); } else if (response.startsWith("Hc")) { @@ -574,7 +580,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) // for step and continue operations //$Hc-1#09 sendGdbAckMessage(); - sendGdbMessage("OK", "set current thread for step & continue"); + sendGdbMessage("OK", "Set current thread for step & continue"); } else if (response.startsWith("Hg")) { @@ -585,7 +591,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) //$Hg0#df sendGdbAckMessage(); m_session.currentThread = response.mid(2).toInt(0, 16); - sendGdbMessage("OK", "set current thread " + sendGdbMessage("OK", "Set current thread " + QByteArray::number(m_session.currentThread)); } @@ -665,16 +671,16 @@ void Adapter::handleGdbResponse(const QByteArray &response) #endif bool ok = false; const uint registerNumber = response.mid(1).toInt(&ok, 16); - QByteArray logMsg = "read register"; + QByteArray logMsg = "Read Register"; if (registerNumber == RegisterPSGdb) { QByteArray ba; appendInt(&ba, m_snapshot.registers[RegisterPSTrk], m_registerEndianness); - dumpRegister(registerNumber, m_snapshot.registers[RegisterPSTrk], logMsg); + logMsg += dumpRegister(registerNumber, m_snapshot.registers[RegisterPSTrk]); sendGdbMessage(ba.toHex(), logMsg); } else if (registerNumber < RegisterCount) { QByteArray ba; appendInt(&ba, m_snapshot.registers[registerNumber], m_registerEndianness); - dumpRegister(registerNumber, m_snapshot.registers[registerNumber], logMsg); + logMsg += dumpRegister(registerNumber, m_snapshot.registers[registerNumber]); sendGdbMessage(ba.toHex(), logMsg); } else { sendGdbMessage("0000", "read single unknown register #" + QByteArray::number(registerNumber)); @@ -775,7 +781,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) appendInt(&ba, m_snapshot.registers[RegisterPC] + 4); // end address appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); - sendTrkMessage(0x19, Callback(), ba, "Step range"); + sendTrkMessage(0x19, CB(handleStepRange), ba, "Step range"); // FIXME: should be triggered by "real" stop" //sendGdbMessageAfterSync("S05", "target halted"); } @@ -823,8 +829,8 @@ void Adapter::handleGdbResponse(const QByteArray &response) // ThreadID: 0xffffffff (-1) // [1B 09 82 00 78 67 43 40 00 00 00 01 00 00 00 00 // 00 00 01 B5 FF FF FF FF] - const QByteArray ba = breakpointTrkMessage(addr, len, m_session.pid); - sendTrkMessage(0x1B, Callback(this, &Adapter::handleAndReportSetBreakpoint), ba); + const QByteArray ba = trkBreakpointMessage(addr, len, m_session.pid); + sendTrkMessage(0x1B, CB(handleAndReportSetBreakpoint), ba); //m_session.toekn //---TRK------------------------------------------------------ @@ -865,9 +871,11 @@ void Adapter::executeGdbCommand(const QString &msg) { logMessage("EXECUTING GDB COMMAND " + msg); if (msg == "S") - m_gdbProc.write("-exec-interrupt"); + writeToGdb("-exec-interrupt"); + if (msg == "I") + interruptInferior(); else - m_gdbProc.write(msg.toLatin1()); + writeToGdb(msg); } bool Adapter::openTrkPort(const QString &port, QString *errorMessage) @@ -902,7 +910,7 @@ void Adapter::sendTrkContinue() void Adapter::waitForTrkFinished() { // initiate one last roundtrip to ensure all is flushed - sendTrkMessage(0x0, Callback(this, &Adapter::handleWaitForFinished)); + sendTrkMessage(0x0, CB(handleWaitForFinished)); } void Adapter::sendTrkAck(byte token) @@ -934,11 +942,13 @@ void Adapter::handleResult(const TrkResult &result) const uint addr = extractInt(data); //code address: 4 bytes; code base address for the library const uint pid = extractInt(data + 4); // ProcessID: 4 bytes; const uint tid = extractInt(data + 8); // ThreadID: 4 bytes - logMessage(prefix + QString::fromLatin1("NOTE: PID %1/TID %2 STOPPED at 0x%3").arg(pid).arg(tid).arg(addr, 0, 16)); + logMessage(prefix + QString::fromLatin1("NOTE: PID %1/TID %2 " + "STOPPED at 0x%3").arg(pid).arg(tid).arg(addr, 0, 16)); sendTrkAck(result.token); if (addr) { - // Todo: Do not send off GdbMessages if a synced gdb query is pending, queue instead - sendGdbMessage("S05", "Target stopped"); + // Todo: Do not send off GdbMessages if a synced gdb + // query is pending, queue instead + //sendGdbMessage("S05", "Target stopped"); } else { if (m_verbose) logMessage(QLatin1String("Ignoring stop at 0")); @@ -988,9 +998,10 @@ void Adapter::handleResult(const TrkResult &result) const QString name = len ? QString::fromAscii(result.data.mid(12, len)) : QString(); if (!name.isEmpty()) m_session.modules.removeAll(name); - logMessage(QString::fromLatin1("%1 %2 UNLOAD: %3"). - arg(QString::fromAscii(prefix)).arg(itemType ? QLatin1String("LIB") : QLatin1String("PROCESS")). - arg(name)); + logMessage(QString::fromLatin1("%1 %2 UNLOAD: %3") + .arg(QString::fromAscii(prefix)) + .arg(itemType ? QLatin1String("LIB") : QLatin1String("PROCESS")) + .arg(name)); sendTrkAck(result.token); break; } @@ -1040,24 +1051,24 @@ void Adapter::handleCpuType(const TrkResult &result) void Adapter::setTrkBreakpoint(const Breakpoint &bp) { - //---IDE------------------------------------------------------ - // Command: 0x1B Set Break - //BreakType: 0x82 - // Options: 0x00 - // Address: 0x78674340 (2020033344) i.e + 0x00000340 - // Length: 0x00000001 (1) - // Count: 0x00000000 (0) - //ProcessID: 0x000001b5 (437) - // ThreadID: 0xffffffff (-1) - // [1B 09 82 00 78 67 43 40 00 00 00 01 00 00 00 00 - // 00 00 01 B5 FF FF FF FF] - const QByteArray ba = breakpointTrkMessage(m_session.codeseg + bp.offset, 1, m_session.pid); - sendTrkMessage(0x1B, Callback(this, &Adapter::handleSetTrkBreakpoint), ba); + //---IDE------------------------------------------------------ + // Command: 0x1B Set Break + //BreakType: 0x82 + // Options: 0x00 + // Address: 0x78674340 (2020033344) i.e + 0x00000340 + // Length: 0x00000001 (1) + // Count: 0x00000000 (0) + //ProcessID: 0x000001b5 (437) + // ThreadID: 0xffffffff (-1) + // [1B 09 82 00 78 67 43 40 00 00 00 01 00 00 00 00 + // 00 00 01 B5 FF FF FF FF] + const QByteArray ba = trkBreakpointMessage(m_session.codeseg + bp.offset, 1, m_session.pid); + sendTrkMessage(0x1B, CB(handleSetTrkBreakpoint), ba); - //---TRK------------------------------------------------------ - // Command: 0x80 Acknowledge - // Error: 0x00 - // [80 09 00 00 00 00 0A] + //---TRK------------------------------------------------------ + // Command: 0x80 Acknowledge + // Error: 0x00 + // [80 09 00 00 00 00 0A] } void Adapter::handleSetTrkBreakpoint(const TrkResult &result) @@ -1081,37 +1092,34 @@ void Adapter::handleCreateProcess(const TrkResult &result) m_session.tid = extractInt(data + 5); m_session.codeseg = extractInt(data + 9); m_session.dataseg = extractInt(data + 13); - QString logMsg = QString::fromLatin1("handleCreateProcess PID=%1 TID=%2 CODE=0x%3 (%4) DATA=0x%5 (%6)") - .arg(m_session.pid).arg(m_session.tid).arg(m_session.codeseg, 0 ,16).arg(m_session.codeseg).arg(m_session.dataseg, 0, 16).arg(m_session.dataseg); - logMessage(logMsg); + + logMessage("PID: 0x" + hexNumber(m_session.pid)); + logMessage("TID: 0x" + hexNumber(m_session.tid)); + logMessage("COD: 0x" + hexNumber(m_session.codeseg)); + logMessage("DAT: 0x" + hexNumber(m_session.dataseg)); + + writeToGdb("add-symbol-file filebrowseapp.sym 0x" + + hexNumber(m_session.codeseg)); + writeToGdb("symbol-file filebrowseapp.sym"); + foreach (const Breakpoint &bp, m_breakpoints) setTrkBreakpoint(bp); sendTrkContinue(); -#if 0 - /* - logMessage("PID: " + formatInt(m_session.pid) + m_session.pid); - logMessage("TID: " + formatInt(m_session.tid) + m_session.tid); - logMessage("COD: " + formatInt(m_session.codeseg) + m_session.codeseg); - logMessage("DAT: " + formatInt(m_session.dataseg) + m_session.dataseg); - */ - +#if 0 //setTrkBreakpoint(0x0000, ArmMode); //clearTrkBreakpoint(0x0000); - - - #if 1 //---IDE------------------------------------------------------ // Command: 0x42 Read Info // [42 0C 00 06 00 00 00 00 00 14 50 6F 6C 79 6D 6F // 72 70 68 69 63 44 4C 4C 32 2E 64 6C 6C 00] - sendTrkMessage(0x42, Callback(this, &Adapter::handleReadInfo), + sendTrkMessage(0x42, CB(handleReadInfo), "00 06 00 00 00 00 00 14 50 6F 6C 79 6D 6F " "72 70 68 69 63 44 4C 4C 32 2E 64 6C 6C 00"); - //sendTrkMessage(0x42, Callback(this, &Adapter::handleReadInfo), + //sendTrkMessage(0x42, CB(handleReadInfo), // "00 01 00 00 00 00"); //---TRK------------------------------------------------------ // Command: 0x80 Acknowledge @@ -1123,7 +1131,7 @@ void Adapter::handleCreateProcess(const TrkResult &result) // Command: 0x42 Read Info // [42 0D 00 06 00 00 00 00 00 14 50 6F 6C 79 6D 6F // 72 70 68 69 63 44 4C 4C 31 2E 64 6C 6C 00] - sendTrkMessage(0x42, Callback(this, &Adapter::handleReadInfo), + sendTrkMessage(0x42, CB(handleReadInfo), "00 06 00 00 00 00 00 14 50 6F 6C 79 6D 6F " "72 70 68 69 63 44 4C 4C 31 2E 64 6C 6C 00"); //---TRK------------------------------------------------------ @@ -1132,7 +1140,7 @@ void Adapter::handleCreateProcess(const TrkResult &result) // [80 0D 20] #endif - //sendTrkMessage(0x18, Callback(this, &Adapter::handleStop), + //sendTrkMessage(0x18, CB(handleStop), // "01 " + formatInt(m_session.pid) + formatInt(m_session.tid)); //---IDE------------------------------------------------------ @@ -1143,8 +1151,8 @@ void Adapter::handleCreateProcess(const TrkResult &result) QByteArray ba; appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); - sendTrkMessage(0x18, Callback(this, &Adapter::handleContinue), ba); - //sendTrkMessage(0x18, Callback(this, &Adapter::handleContinue), + sendTrkMessage(0x18, CB(handleContinue), ba); + //sendTrkMessage(0x18, CB(handleContinue), // formatInt(m_session.pid) + "ff ff ff ff"); //---TRK------------------------------------------------------ // Command: 0x80 Acknowledge @@ -1155,7 +1163,7 @@ void Adapter::handleCreateProcess(const TrkResult &result) void Adapter::handleAndReportReadRegisters(const TrkResult &result) { - //logMessage(" RESULT: " + result.toString()); + logMessage(" RESULT: " + result.toString()); // [80 0B 00 00 00 00 00 C9 24 FF BC 00 00 00 00 00 // 60 00 00 00 00 00 00 78 67 79 70 00 00 00 00 00...] @@ -1165,13 +1173,16 @@ void Adapter::handleAndReportReadRegisters(const TrkResult &result) } QByteArray ba; for (int i = 0; i < 16; ++i) { - const uint reg = m_registerEndianness == LittleEndian ? swapEndian(m_snapshot.registers[i]) : m_snapshot.registers[i]; + const uint reg = m_registerEndianness == LittleEndian + ? swapEndian(m_snapshot.registers[i]) : m_snapshot.registers[i]; ba += hexNumber(reg, 8); } - QByteArray logMsg = "register contents"; + QByteArray logMsg = "REGISTER CONTENTS: "; if (m_verbose > 1) { - for (int i = 0; i < RegisterCount; ++i) - dumpRegister(i, m_snapshot.registers[i], logMsg); + for (int i = 0; i < RegisterCount; ++i) { + logMsg += dumpRegister(i, m_snapshot.registers[i]); + logMsg += ' '; + } } sendGdbMessage(ba, logMsg); } @@ -1210,7 +1221,8 @@ QByteArray Adapter::memoryReadLogMessage(uint addr, uint len, const QByteArray & logMsg += "[SP]"; } else if (addr == m_snapshot.registers[RegisterLR]) { logMsg += "[LR]"; - } else if (addr > m_snapshot.registers[RegisterSP] && (addr - m_snapshot.registers[RegisterSP]) < 10240) { + } else if (addr > m_snapshot.registers[RegisterSP] && + (addr - m_snapshot.registers[RegisterSP]) < 10240) { logMsg += "[SP+"; // Stack area ...stack seems to be top-down logMsg += QByteArray::number(addr - m_snapshot.registers[RegisterSP]); logMsg += ']'; @@ -1256,16 +1268,28 @@ void Adapter::reportReadMemoryBuffered(const TrkResult &result) void Adapter::handleReadMemoryUnbuffered(const TrkResult &result) { + //logMessage("UNBUFFERED MEMORY READ: " + stringFromArray(result.data)); const uint blockaddr = result.cookie.toUInt(); + if (extractShort(result.data.data() + 1) + 3 != result.data.size()) { + logMessage("\n BAD MEMORY RESULT: " + result.data.toHex() + "\n"); + } if (const int errorCode = result.errorCode()) { const QByteArray ba = "E20"; sendGdbMessage(ba, msgMemoryReadError(32, blockaddr).toLatin1()); } else { - const QByteArray ba = result.data.mid(1); + const QByteArray ba = result.data.mid(3); sendGdbMessage(ba.toHex(), memoryReadLogMessage(blockaddr, ba.size(), ba)); } } +void Adapter::handleStepRange(const TrkResult &result) +{ + // [80 0f 12] + //uint bpnr = extractInt(result.data.data()); + logMessage("STEPPING FINISHED " + stringFromArray(result.data.data())); + sendGdbMessage("S05", "Stepping finished"); +} + void Adapter::handleAndReportSetBreakpoint(const TrkResult &result) { //---TRK------------------------------------------------------ @@ -1286,7 +1310,7 @@ void Adapter::clearTrkBreakpoint(const Breakpoint &bp) appendByte(&ba, 0x00); appendShort(&ba, bp.number); appendInt(&ba, m_session.codeseg + bp.offset); - sendTrkMessage(0x1C, Callback(this, &Adapter::handleClearBreakpoint), ba); + sendTrkMessage(0x1C, CB(handleClearBreakpoint), ba); } void Adapter::handleClearBreakpoint(const TrkResult &result) @@ -1333,8 +1357,10 @@ void Adapter::handleTrkVersions(const TrkResult &result) QTextStream str(&logMsg); str << "Versions: "; if (result.data.size() >= 5) { - str << "Trk version " << int(result.data.at(1)) << '.' << int(result.data.at(2)) - << ", Protocol version " << int(result.data.at(3)) << '.' << int(result.data.at(4)); + str << "Trk version " << int(result.data.at(1)) << '.' + << int(result.data.at(2)) + << ", Protocol version " << int(result.data.at(3)) + << '.' << int(result.data.at(4)); } logMessage(logMsg); } @@ -1370,7 +1396,7 @@ void Adapter::cleanUp() foreach (const Breakpoint &bp, m_breakpoints) clearTrkBreakpoint(bp); - sendTrkMessage(0x02, Callback(this, &Adapter::handleDisconnect)); + sendTrkMessage(0x02, CB(handleDisconnect)); m_startInferiorTriggered = false; //---IDE------------------------------------------------------ // Command: 0x1C Clear Break @@ -1401,7 +1427,7 @@ void Adapter::cleanUp() //---IDE------------------------------------------------------ // Command: 0x02 Disconnect // [02 27] -// sendTrkMessage(0x02, Callback(this, &Adapter::handleDisconnect)); +// sendTrkMessage(0x02, CB(handleDisconnect)); //---TRK------------------------------------------------------ // Command: 0x80 Acknowledge // Error: 0x00 @@ -1432,17 +1458,17 @@ void Adapter::readMemory(uint addr, uint len) if (!m_snapshot.memory.contains(blockaddr)) { if (m_verbose) logMessage(QString::fromLatin1("Requesting buffered memory %1 bytes from 0x%2").arg(MemoryChunkSize).arg(blockaddr, 0, 16)); - sendTrkMessage(0x10, Callback(this, &Adapter::handleReadMemoryBuffered), + sendTrkMessage(0x10, CB(handleReadMemoryBuffered), memoryRequestTrkMessage(blockaddr, MemoryChunkSize, m_session.pid, m_session.tid), QVariant(blockaddr), true); } } const qulonglong cookie = (qulonglong(addr) << 32) + len; - sendTrkMessage(TRK_WRITE_QUEUE_NOOP_CODE, Callback(this, &Adapter::reportReadMemoryBuffered), QByteArray(), cookie); + sendTrkMessage(TRK_WRITE_QUEUE_NOOP_CODE, CB(reportReadMemoryBuffered), QByteArray(), cookie); } else { if (m_verbose) logMessage(QString::fromLatin1("Requesting unbuffered memory %1 bytes from 0x%2").arg(len).arg(addr, 0, 16)); - sendTrkMessage(0x10, Callback(this, &Adapter::handleReadMemoryUnbuffered), + sendTrkMessage(0x10, CB(handleReadMemoryUnbuffered), memoryRequestTrkMessage(addr, len, m_session.pid, m_session.tid), QVariant(addr), true); } @@ -1465,7 +1491,7 @@ void Adapter::startInferiorIfNeeded() QByteArray file("C:\\sys\\bin\\filebrowseapp.exe"); appendString(&ba, file, TargetByteOrder); - sendTrkMessage(0x40, Callback(this, &Adapter::handleCreateProcess), ba); // Create Item + sendTrkMessage(0x40, CB(handleCreateProcess), ba); // Create Item } void Adapter::interruptInferior() @@ -1478,7 +1504,6 @@ void Adapter::interruptInferior() sendTrkMessage(0x1A, Callback(), ba, "Interrupting..."); } - void Adapter::connectProcess(QProcess *proc) { connect(proc, SIGNAL(error(QProcess::ProcessError)), @@ -1495,11 +1520,10 @@ void Adapter::connectProcess(QProcess *proc) this, SLOT(handleProcStateChanged(QProcess::ProcessState))); } - void Adapter::sendOutput(QObject *sender, const QString &data) { if (sender) - emit output(sender->objectName(), data); + emit output(sender->objectName() + " : ", data); else emit output(QString(), data); } @@ -1539,11 +1563,6 @@ void Adapter::handleProcStateChanged(QProcess::ProcessState newState) void Adapter::run() { - if (m_isUnix) { - QProcess::execute("killall -s USR adapter trkserver"); - QProcess::execute("killall adapter trkserver"); - } - startServer(); sendOutput("### Starting Adapter"); @@ -1564,22 +1583,26 @@ void Adapter::startGdb() m_gdbProc.start(QDir::currentPath() + "/cs-gdb", gdbArgs); m_gdbProc.waitForStarted(); - QTextStream ts(&m_gdbProc); - ts << "# This is generated. Changes will be lost.\n"; - ts << "#set remote noack-packet on\n"; - ts << "set confirm off\n"; - ts << "set endian " + m_endianness + "\n"; - ts << "#set debug remote 1\n"; - ts << "#target remote " + m_gdbServerName + "\n"; - ts << "target extended-remote " + m_gdbServerName + "\n"; - ts << "#file filebrowseapp.sym\n"; -// ts << "add-symbol-file filebrowseapp.sym " + m_baseAddress + "\n"; -// ts << "symbol-file filebrowseapp.sym\n"; -// ts << "print E32Main\n"; -// ts << "break E32Main\n"; - ts << "#continue\n"; - ts << "#info files\n"; - ts << "#file filebrowseapp.sym -readnow\n"; + //writeToGdb("set remote noack-packet on"); + writeToGdb("set confirm off"); + writeToGdb("set endian " + m_endianness); + //writeToGdb("set debug remote 1"); + //writeToGdb("target remote " + m_gdbServerName); + writeToGdb("target extended-remote " + m_gdbServerName); + //writeToGdb("file filebrowseapp.sym"); +// writeToGdb("add-symbol-file filebrowseapp.sym " + m_baseAddress); +// writeToGdb("symbol-file filebrowseapp.sym"); +// writeToGdb("print E32Main"); +// writeToGdb("break E32Main"); + //writeToGdb("continue"); + //writeToGdb("info files"); + //writeToGdb("file filebrowseapp.sym -readnow"); +} + +void Adapter::writeToGdb(const QString &msg) +{ + logMessage("<- GDB: " + msg); + m_gdbProc.write(msg.toLatin1() + "\n"); } /////////////////////////////////////////////////////////////////////// @@ -1606,15 +1629,29 @@ private: RunnerGui::RunnerGui(Adapter *adapter) : m_adapter(adapter) { - resize(1000, 1000); + resize(1200, 1000); connect(adapter, SIGNAL(output(QString,QString)), this, SLOT(handleOutput(QString,QString))); } void RunnerGui::handleOutput(const QString &senderName, const QString &data) { - append(senderName + " : " + data); + append(senderName + data); + QTextCursor tc = textCursor(); + tc.movePosition(QTextCursor::End); + setTextCursor(tc); + if (senderName.startsWith("GDB PROCESS")) { + QString str = data; + int pos = str.indexOf("~\""); + if (pos != -1) + str = str.mid(pos + 2); + str.replace("\\t", QString(QChar(0x09))); + str.replace("\\n", QString("\n")); + insertHtml("<b>" + str + "</b>"); + setCurrentCharFormat(QTextCharFormat()); + } ensureCursorVisible(); + } void RunnerGui::keyPressEvent(QKeyEvent *ev) diff --git a/tests/manual/trk/runner.pro b/tests/manual/trk/runner.pro index 0bf878e42027672082e8b89352eae99c6cfd3fb3..4ced7ccd71abbd0302dc062e6acca3867e284054 100644 --- a/tests/manual/trk/runner.pro +++ b/tests/manual/trk/runner.pro @@ -7,9 +7,9 @@ win32:CONFIG+=console HEADERS += \ trkutils.h \ - trkdevice.h \ + trkdevicex.h \ SOURCES += \ runner.cpp \ trkutils.cpp \ - trkdevice.cpp \ + trkdevicex.cpp \ diff --git a/tests/manual/trk/trkdevice.cpp b/tests/manual/trk/trkdevice.cpp index 6746e5b1f64e5d286af6a14a284381aa15c02890..b0e5c2f8140232f02d92e5b354a92c36618903b5 100644 --- a/tests/manual/trk/trkdevice.cpp +++ b/tests/manual/trk/trkdevice.cpp @@ -80,7 +80,7 @@ BOOL WINAPI TryReadFile(HANDLE hFile, { COMSTAT comStat; if (!ClearCommError(hFile, NULL, &comStat)){ - qDebug() << "ClearCommError() failed"; + logMessage("ClearCommError() failed"); return FALSE; } if (comStat.cbInQue == 0) { @@ -202,8 +202,8 @@ void TrkDevice::close() #else d->file.close(); #endif - if (d->verbose) - qDebug() << "Close"; + if (verbose()) + logMessage("Close"); } bool TrkDevice::isOpen() const @@ -232,7 +232,7 @@ void TrkDevice::setSerialFrame(bool f) bool TrkDevice::verbose() const { - return d->verbose; + return true || d->verbose; } void TrkDevice::setVerbose(bool b) @@ -242,8 +242,8 @@ void TrkDevice::setVerbose(bool b) bool TrkDevice::write(const QByteArray &data, QString *errorMessage) { - if (d->verbose) - qDebug() << ">WRITE" << data.toHex(); + if (verbose()) + logMessage("XWRITE " + data.toHex()); #ifdef Q_OS_WIN DWORD charsWritten; if (!WriteFile(d->hdevice, data.data(), data.size(), &charsWritten, NULL)) { @@ -286,8 +286,8 @@ void TrkDevice::tryTrkRead() if (isValidTrkResult(d->trkReadBuffer, d->serialFrame)) break; } - if (d->verbose && totalCharsRead) - qDebug() << "Read" << d->trkReadBuffer.toHex(); + if (verbose() && totalCharsRead) + logMessage("Read" + d->trkReadBuffer.toHex()); if (!totalCharsRead) return; const ushort len = isValidTrkResult(d->trkReadBuffer, d->serialFrame); @@ -301,6 +301,8 @@ void TrkDevice::tryTrkRead() if (!size) return; const QByteArray data = d->file.read(size); + if (verbose()) + logMessage("READ " + data.toHex()); d->trkReadBuffer.append(data); const ushort len = isValidTrkResult(d->trkReadBuffer, d->serialFrame); if (!len) { @@ -315,8 +317,8 @@ void TrkDevice::tryTrkRead() TrkResult r; QByteArray rawData; while (extractResult(&d->trkReadBuffer, d->serialFrame, &r, &rawData)) { - if (d->verbose) - qDebug() << "Read TrkResult " << r.data.toHex(); + if (verbose()) + logMessage("Read TrkResult " + r.data.toHex()); emit messageReceived(r); if (!rawData.isEmpty()) emit rawDataReceived(rawData); @@ -547,7 +549,7 @@ bool TrkWriteQueueDevice::trkWriteRawMessage(const TrkMessage &msg) { const QByteArray ba = frameMessage(msg.code, msg.token, msg.data, serialFrame()); if (verbose()) - qDebug() << ("WRITE: " + stringFromArray(ba)); + logMessage("WRITE: " + stringFromArray(ba)); QString errorMessage; const bool rc = write(ba, &errorMessage); if (!rc) @@ -610,7 +612,7 @@ void TrkWriteQueueIODevice::setSerialFrame(bool f) bool TrkWriteQueueIODevice::verbose() const { - return d->verbose; + return true || d->verbose; } void TrkWriteQueueIODevice::setVerbose(bool b) @@ -659,7 +661,7 @@ bool TrkWriteQueueIODevice::trkWriteRawMessage(const TrkMessage &msg) { const QByteArray ba = frameMessage(msg.code, msg.token, msg.data, serialFrame()); if (verbose()) - qDebug() << ("WRITE: " + stringFromArray(ba)); + logMessage("WRITE: " + stringFromArray(ba)); const bool ok = d->device->write(ba) != -1; if (!ok) { const QString msg = QString::fromLatin1("Unable to write %1 bytes: %2:").arg(ba.size()).arg(d->device->errorString()); @@ -674,8 +676,8 @@ void TrkWriteQueueIODevice::tryTrkRead() if (!bytesAvailable) return; const QByteArray newData = d->device->read(bytesAvailable); - if (d->verbose) - qDebug() << "READ " << newData.toHex(); + //if (verbose()) + logMessage("READ " + newData.toHex()); d->readBuffer.append(newData); TrkResult r; QByteArray rawData; diff --git a/tests/manual/trk/trkdevice.h b/tests/manual/trk/trkdevice.h index 61b382ad5766bdc0a89d2dbfe2fc7a8160169f5d..7b2a5dbcd38fd2b1286353d5db6ba94126fc4300 100644 --- a/tests/manual/trk/trkdevice.h +++ b/tests/manual/trk/trkdevice.h @@ -80,13 +80,14 @@ public: bool write(const QByteArray &data, QString *errorMessage); signals: - void messageReceived(const trk::TrkResult&); + void messageReceived(const trk::TrkResult &result); // Emitted with the contents of messages enclosed in 07e, not for log output void rawDataReceived(const QByteArray &data); - void error(const QString &s); - + void error(const QString &msg); + void logMessage(const QString &msg); + protected: - void emitError(const QString &); + void emitError(const QString &msg); virtual void timerEvent(QTimerEvent *ev); private: @@ -127,6 +128,9 @@ public: // Send an Ack synchronously, bypassing the queue bool sendTrkAck(unsigned char token); +signals: + void logMessage(const QString &msg); + private slots: void slotHandleResult(const trk::TrkResult &); @@ -175,6 +179,8 @@ signals: void messageReceived(const trk::TrkResult&); // Emitted with the contents of messages enclosed in 07e, not for log output void rawDataReceived(const QByteArray &data); + void logMessage(const QString &msg); + protected: virtual void timerEvent(QTimerEvent *ev); diff --git a/tests/manual/trk/trkdevicex.cpp b/tests/manual/trk/trkdevicex.cpp new file mode 100644 index 0000000000000000000000000000000000000000..31382de3f03c237a043171487cf419c1d0388abf --- /dev/null +++ b/tests/manual/trk/trkdevicex.cpp @@ -0,0 +1,562 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "trkdevicex.h" +#include "trkutils.h" + +#include <QtCore/QString> +#include <QtCore/QDebug> +#include <QtCore/QQueue> +#include <QtCore/QHash> +#include <QtCore/QMap> +#include <QtCore/QSharedPointer> + +#ifdef Q_OS_WIN +# include <windows.h> +#else +# include <QtCore/QFile> + +# include <stdio.h> +# include <sys/ioctl.h> +# include <termios.h> +# include <errno.h> +# include <string.h> +#endif + +enum { TimerInterval = 100 }; + +#ifdef Q_OS_WIN + +// Format windows error from GetLastError() value: TODO: Use the one provided by the utisl lib. +QString winErrorMessage(unsigned long error) +{ + QString rc = QString::fromLatin1("#%1: ").arg(error); + ushort *lpMsgBuf; + + const int len = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL); + if (len) { + rc = QString::fromUtf16(lpMsgBuf, len); + LocalFree(lpMsgBuf); + } else { + rc += QString::fromLatin1("<unknown error>"); + } + return rc; +} + +// Non-blocking replacement for win-api ReadFile function +BOOL WINAPI TryReadFile(HANDLE hFile, + LPVOID lpBuffer, + DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesRead, + LPOVERLAPPED lpOverlapped) +{ + COMSTAT comStat; + if (!ClearCommError(hFile, NULL, &comStat)){ + logMessage("ClearCommError() failed"); + return FALSE; + } + if (comStat.cbInQue == 0) { + *lpNumberOfBytesRead = 0; + return FALSE; + } + return ReadFile(hFile, + lpBuffer, + qMin(comStat.cbInQue, nNumberOfBytesToRead), + lpNumberOfBytesRead, + lpOverlapped); +} +#endif + +namespace trk { + +/////////////////////////////////////////////////////////////////////// +// +// TrkMessage +// +/////////////////////////////////////////////////////////////////////// + +/* A message to be send to TRK, triggering a callback on receipt + * of the answer. */ +struct TrkMessage +{ + typedef TrkFunctor1<const TrkResult &> Callback; + + explicit TrkMessage(byte code = 0u, byte token = 0u, + Callback callback = Callback()); + + byte code; + byte token; + QByteArray data; + QVariant cookie; + Callback callback; + bool invokeOnNAK; +}; + +TrkMessage::TrkMessage(byte c, byte t, Callback cb) : + code(c), + token(t), + callback(cb), + invokeOnNAK(false) +{ +} + +/////////////////////////////////////////////////////////////////////// +// +// TrkWriteQueue +// +/////////////////////////////////////////////////////////////////////// + +/* Mixin class that manages a write queue of Trk messages. */ +class TrkWriteQueue +{ +public: + typedef TrkDevice::Callback Callback; + + TrkWriteQueue(); + + // Enqueue messages. + void queueTrkMessage(byte code, Callback callback, + const QByteArray &data, const QVariant &cookie, + bool invokeOnNAK); + void queueTrkInitialPing(); + + // Call this from the device read notification with the results. + void slotHandleResult(const TrkResult &result); + + // This can be called periodically in a timer to retrieve + // the pending messages to be sent. + bool pendingMessage(TrkMessage *message); + // Notify the queue about the success of the write operation + // after taking the pendingMessage off. + void notifyWriteResult(bool ok); + +private: + typedef QMap<byte, TrkMessage> TokenMessageMap; + + byte nextTrkWriteToken(); + + byte trkWriteToken; + QQueue<TrkMessage> trkWriteQueue; + TokenMessageMap writtenTrkMessages; + bool trkWriteBusy; +}; + +TrkWriteQueue::TrkWriteQueue() : + trkWriteToken(0), + trkWriteBusy(false) +{ +} + +byte TrkWriteQueue::nextTrkWriteToken() +{ + ++trkWriteToken; + if (trkWriteToken == 0) + ++trkWriteToken; + return trkWriteToken; +} + +void TrkWriteQueue::queueTrkMessage(byte code, Callback callback, + const QByteArray &data, const QVariant &cookie, bool invokeOnNAK) +{ + const byte token = code == TRK_WRITE_QUEUE_NOOP_CODE ? + byte(0) : nextTrkWriteToken(); + TrkMessage msg(code, token, callback); + msg.data = data; + msg.cookie = cookie; + msg.invokeOnNAK = invokeOnNAK; + trkWriteQueue.append(msg); +} + +bool TrkWriteQueue::pendingMessage(TrkMessage *message) +{ + // Invoked from timer, try to flush out message queue + if (trkWriteBusy || trkWriteQueue.isEmpty()) + return false; + // Handle the noop message, just invoke CB + if (trkWriteQueue.front().code == TRK_WRITE_QUEUE_NOOP_CODE) { + TrkMessage noopMessage = trkWriteQueue.dequeue(); + if (noopMessage.callback) { + TrkResult result; + result.code = noopMessage.code; + result.token = noopMessage.token; + result.data = noopMessage.data; + result.cookie = noopMessage.cookie; + noopMessage.callback(result); + } + } + // Check again for real messages + if (trkWriteQueue.isEmpty()) + return false; + if (message) + *message = trkWriteQueue.front(); + return true; +} + +void TrkWriteQueue::notifyWriteResult(bool ok) +{ + // On success, dequeue message and await result + if (ok) { + TrkMessage firstMsg = trkWriteQueue.dequeue(); + writtenTrkMessages.insert(firstMsg.token, firstMsg); + trkWriteBusy = true; + } +} + +void TrkWriteQueue::slotHandleResult(const TrkResult &result) +{ + trkWriteBusy = false; + if (result.code != TrkNotifyAck && result.code != TrkNotifyNak) + return; + // Find which request the message belongs to and invoke callback + // if ACK or on NAK if desired. + const TokenMessageMap::iterator it = writtenTrkMessages.find(result.token); + if (it == writtenTrkMessages.end()) + return; + const bool invokeCB = it.value().callback + && (result.code == TrkNotifyAck || it.value().invokeOnNAK); + + if (invokeCB) { + TrkResult result1 = result; + result1.cookie = it.value().cookie; + it.value().callback(result1); + } + writtenTrkMessages.erase(it); +} + +void TrkWriteQueue::queueTrkInitialPing() +{ + // Ping, reset sequence count + trkWriteQueue.append(TrkMessage(0, 0)); +} + +struct TrkDevicePrivate { + TrkDevicePrivate(); +#ifdef Q_OS_WIN + HANDLE hdevice; +#else + QFile file; +#endif + + QByteArray trkReadBuffer; + bool trkWriteBusy; + int timerId; + bool serialFrame; + bool verbose; + QString errorString; +}; + +/////////////////////////////////////////////////////////////////////// +// +// TrkDevice +// +/////////////////////////////////////////////////////////////////////// + +TrkDevicePrivate::TrkDevicePrivate() : +#ifdef Q_OS_WIN + hdevice(INVALID_HANDLE_VALUE), +#endif + trkWriteBusy(false), + timerId(-1), + serialFrame(true), + verbose(false) +{ +} + +/////////////////////////////////////////////////////////////////////// +// +// TrkDevice +// +/////////////////////////////////////////////////////////////////////// + +TrkDevice::TrkDevice(QObject *parent) : + QObject(parent), + d(new TrkDevicePrivate), + qd(new TrkWriteQueue) +{ + connect(this, SIGNAL(messageReceived(trk::TrkResult)), + this, SLOT(slotHandleResult(trk::TrkResult))); +} + +bool TrkDevice::open(const QString &port, QString *errorMessage) +{ + close(); +#ifdef Q_OS_WIN + d->hdevice = CreateFile(port.toStdWString().c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (INVALID_HANDLE_VALUE == d->hdevice) { + *errorMessage = QString::fromLatin1("Could not open device '%1': %2").arg(port, winErrorMessage(GetLastError())); + return false; + } + d->timerId = startTimer(TimerInterval); + return true; +#else + d->file.setFileName(port); + if (!d->file.open(QIODevice::ReadWrite|QIODevice::Unbuffered)) { + *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(port, d->file.errorString()); + return false; + } + + struct termios termInfo; + if (tcgetattr(d->file.handle(), &termInfo) < 0) { + *errorMessage = QString::fromLatin1("Unable to retrieve terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno))); + return false; + } + // Turn off terminal echo as not get messages back, among other things + termInfo.c_cflag |= CREAD|CLOCAL; + termInfo.c_lflag &= (~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|ISIG)); + termInfo.c_iflag &= (~(INPCK|IGNPAR|PARMRK|ISTRIP|ICRNL|IXANY)); + termInfo.c_oflag &= (~OPOST); + termInfo.c_cc[VMIN] = 0; + termInfo.c_cc[VINTR] = _POSIX_VDISABLE; + termInfo.c_cc[VQUIT] = _POSIX_VDISABLE; + termInfo.c_cc[VSTART] = _POSIX_VDISABLE; + termInfo.c_cc[VSTOP] = _POSIX_VDISABLE; + termInfo.c_cc[VSUSP] = _POSIX_VDISABLE; + if (tcsetattr(d->file.handle(), TCSAFLUSH, &termInfo) < 0) { + *errorMessage = QString::fromLatin1("Unable to apply terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno))); + return false; + } + d->timerId = startTimer(TimerInterval); + return true; +#endif +} + + +TrkDevice::~TrkDevice() +{ + close(); + delete d; + delete qd; +} + +void TrkDevice::close() +{ + if (!isOpen()) + return; + if (d->timerId != -1) { + killTimer(d->timerId); + d->timerId = -1; + } +#ifdef Q_OS_WIN + CloseHandle(d->hdevice); + d->hdevice = INVALID_HANDLE_VALUE; +#else + d->file.close(); +#endif + if (verbose()) + logMessage("Close"); +} + +bool TrkDevice::isOpen() const +{ +#ifdef Q_OS_WIN + return d->hdevice != INVALID_HANDLE_VALUE; +#else + return d->file.isOpen(); +#endif +} + +QString TrkDevice::errorString() const +{ + return d->errorString; +} + +bool TrkDevice::serialFrame() const +{ + return d->serialFrame; +} + +void TrkDevice::setSerialFrame(bool f) +{ + d->serialFrame = f; +} + +bool TrkDevice::verbose() const +{ + return true || d->verbose; +} + +void TrkDevice::setVerbose(bool b) +{ + d->verbose = b; +} + +bool TrkDevice::write(const QByteArray &data, QString *errorMessage) +{ +#ifdef Q_OS_WIN + DWORD charsWritten; + if (!WriteFile(d->hdevice, data.data(), data.size(), &charsWritten, NULL)) { + *errorMessage = QString::fromLatin1("Error writing data: %1").arg(winErrorMessage(GetLastError())); + return false; + } + FlushFileBuffers(d->hdevice); + return true; +#else + if (d->file.write(data) == -1 || !d->file.flush()) { + *errorMessage = QString::fromLatin1("Cannot write: %1").arg(d->file.errorString()); + return false; + } + return true; +#endif +} + +#ifndef Q_OS_WIN +static inline int bytesAvailable(int fileNo) +{ + int numBytes; + const int rc = ioctl(fileNo, FIONREAD, &numBytes); + if (rc < 0) + numBytes=0; + return numBytes; +} +#endif + +void TrkDevice::tryTrkRead() +{ +#ifdef Q_OS_WIN + const DWORD BUFFERSIZE = 1024; + char buffer[BUFFERSIZE]; + DWORD charsRead; + DWORD totalCharsRead = 0; + + while (TryReadFile(d->hdevice, buffer, BUFFERSIZE, &charsRead, NULL)) { + totalCharsRead += charsRead; + d->trkReadBuffer.append(buffer, charsRead); + if (isValidTrkResult(d->trkReadBuffer, d->serialFrame)) + break; + } + if (verbose() && totalCharsRead) + logMessage("Read" + d->trkReadBuffer.toHex()); + if (!totalCharsRead) + return; + const ushort len = isValidTrkResult(d->trkReadBuffer, d->serialFrame); + if (!len) { + const QString msg = QString::fromLatin1("Partial message: %1").arg(stringFromArray(d->trkReadBuffer)); + emitError(msg); + return; + } +#else + const int size = bytesAvailable(d->file.handle()); + if (!size) + return; + const QByteArray data = d->file.read(size); + if (verbose()) + logMessage("trk: <- " + stringFromArray(data)); + d->trkReadBuffer.append(data); + const ushort len = isValidTrkResult(d->trkReadBuffer, d->serialFrame); + if (!len) { + if (d->trkReadBuffer.size() > 10) { + const QString msg = QString::fromLatin1("Unable to extract message from '%1' '%2'"). + arg(QLatin1String(d->trkReadBuffer.toHex())).arg(QString::fromAscii(d->trkReadBuffer)); + emitError(msg); + } + return; + } +#endif // Q_OS_WIN + TrkResult r; + QByteArray rawData; + while (extractResult(&d->trkReadBuffer, d->serialFrame, &r, &rawData)) { + //if (verbose()) + // logMessage("Read TrkResult " + r.data.toHex()); + emit messageReceived(r); + if (!rawData.isEmpty()) + emit rawDataReceived(rawData); + } +} + +void TrkDevice::timerEvent(QTimerEvent *) +{ + tryTrkWrite(); + tryTrkRead(); +} + +void TrkDevice::emitError(const QString &s) +{ + d->errorString = s; + qWarning("%s\n", qPrintable(s)); + emit error(s); +} + +void TrkDevice::sendTrkMessage(byte code, Callback callback, + const QByteArray &data, const QVariant &cookie, bool invokeOnNAK) +{ + qd->queueTrkMessage(code, callback, data, cookie, invokeOnNAK); +} + +void TrkDevice::sendTrkInitialPing() +{ + qd->queueTrkInitialPing(); +} + +bool TrkDevice::sendTrkAck(byte token) +{ + // The acknowledgement must not be queued! + TrkMessage msg(0x80, token); + msg.token = token; + msg.data.append('\0'); + return trkWriteRawMessage(msg); + // 01 90 00 07 7e 80 01 00 7d 5e 7e +} + +void TrkDevice::tryTrkWrite() +{ + TrkMessage message; + if (!qd->pendingMessage(&message)) + return; + const bool success = trkWriteRawMessage(message); + qd->notifyWriteResult(success); +} + +bool TrkDevice::trkWriteRawMessage(const TrkMessage &msg) +{ + const QByteArray ba = frameMessage(msg.code, msg.token, msg.data, serialFrame()); + if (verbose()) + logMessage("trk: -> " + stringFromArray(ba)); + QString errorMessage; + const bool rc = write(ba, &errorMessage); + if (!rc) + emitError(errorMessage); + return rc; +} + +void TrkDevice::slotHandleResult(const TrkResult &result) +{ + qd->slotHandleResult(result); +} + +} // namespace trk + diff --git a/tests/manual/trk/trkdevicex.h b/tests/manual/trk/trkdevicex.h new file mode 100644 index 0000000000000000000000000000000000000000..9d0e9da0dcc62c0cf9bc925f26f194c645d8e7fe --- /dev/null +++ b/tests/manual/trk/trkdevicex.h @@ -0,0 +1,132 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TRKDEVICE_H +#define TRKDEVICE_H + +#include "trkfunctor.h" + +#include <QtCore/QObject> +#include <QtCore/QVariant> +#include <QtCore/QByteArray> +#include <QtCore/QSharedPointer> + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +namespace trk { + +struct TrkResult; +struct TrkMessage; +struct TrkDevicePrivate; +class TrkWriteQueue; +struct TrkWriteQueueIODevicePrivate; + +/* TrkDevice: Implements a Windows COM or Linux device for + * Trk communications. Provides synchronous write and asynchronous + * read operation. + * The serialFrames property specifies whether packets are encapsulated in + * "0x90 <length>" frames, which is currently the case for serial ports. + * Contains write message queue allowing + * for queueing messages with a notification callback. If the message receives + * an ACK, the callback is invoked. + * The special message TRK_WRITE_QUEUE_NOOP_CODE code can be used for synchronisation. + * The respective message will not be sent, the callback is just invoked. */ + +enum { TRK_WRITE_QUEUE_NOOP_CODE = 0x7f }; + +class TrkDevice : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool serialFrame READ serialFrame WRITE setSerialFrame) + Q_PROPERTY(bool verbose READ verbose WRITE setVerbose) +public: + explicit TrkDevice(QObject *parent = 0); + virtual ~TrkDevice(); + + bool open(const QString &port, QString *errorMessage); + bool isOpen() const; + void close(); + + QString errorString() const; + + bool serialFrame() const; + void setSerialFrame(bool f); + + bool verbose() const; + void setVerbose(bool b); + + bool write(const QByteArray &data, QString *errorMessage); + + // Construct as 'TrkWriteQueueDevice::Callback(instance, &Klass::method);' + typedef TrkFunctor1<const TrkResult &> Callback; + +signals: + void messageReceived(const trk::TrkResult &result); + // Emitted with the contents of messages enclosed in 07e, not for log output + void rawDataReceived(const QByteArray &data); + void error(const QString &msg); + void logMessage(const QString &msg); + +protected: + void emitError(const QString &msg); + virtual void timerEvent(QTimerEvent *ev); + +public: + void tryTrkRead(); + + // Enqueue a message with a notification callback. + void sendTrkMessage(unsigned char code, + Callback callBack = Callback(), + const QByteArray &data = QByteArray(), + const QVariant &cookie = QVariant(), + // Invoke callback on receiving NAK, too. + bool invokeOnNAK = false); + + // Enqeue an initial ping + void sendTrkInitialPing(); + + // Send an Ack synchronously, bypassing the queue + bool sendTrkAck(unsigned char token); + +private slots: + void slotHandleResult(const trk::TrkResult &); + +private: + void tryTrkWrite(); + bool trkWriteRawMessage(const TrkMessage &msg); + + TrkDevicePrivate *d; + TrkWriteQueue *qd; +}; + +} // namespace trk + +#endif // TRKDEVICE_H