diff --git a/doc/qtcreator.qdoc b/doc/qtcreator.qdoc
index bff660795746e4de244316b510da162f4dbc6ddc..eab64f1af5a3dcc47618fd0c65b1e498a64d5132 100644
--- a/doc/qtcreator.qdoc
+++ b/doc/qtcreator.qdoc
@@ -1428,12 +1428,50 @@
 
     Since Qt Creator 1.1, generic projects are supported, in addition to
     \c qmake projects. In other words, you can import existing projects that
-    do not use \c qmake or \c CMake and  Qt Creator will simply ignore your
+    do not use \c qmake or \c CMake and Qt Creator will simply ignore your
     build system.
 
-    This feature lets you use Qt Creator as a code editor. You can build your
-    project by modifying the \c make command in the \gui{Project Settings}
-    page.
+    This feature lets you use Qt Creator as a code editor. You can change the
+    way your project is built by modifying the \c make command on the
+    \gui{Project Settings} page.
+
+    Since Qt Creator has no way of knowing which files belong to your project,
+    or which include directories and defines you're passing to your compiler,
+    the generic project allows you to specify this information manually.
+
+    \section1 Specifying which files belong to your project
+
+    The list of files belonging to a generic project is specified in the
+    \c{.files} file. Qt Creator adds any files that it recognizes when you
+    first create the generic project. If you want to add additional files, or
+    need to add/remove files later, edit the .files file in Qt Creator. Your
+    project tree will be refreshed on saving this file.
+
+    If you frequently need to update this file, for example after updating
+    from a source control system, you may want to write a small script that
+    updates the file for you. At the moment Qt Creator needs to be restarted
+    when the file is modified externally, in order to pick up the changes.
+
+    \section1 Specifying the include paths
+
+    The include paths are specified in the \c{.includes} file.
+
+    \section1 Specifying the defines
+
+    The defines are specified in the \c{.config} file. This is basically a
+    regular C++ file that is prepended to all your source files when they are
+    being parsed, but you should generally only use it to add lines like the
+    following:
+
+    #define NAME value
+
+    \section1 Creating a run configuration
+
+    Qt Creator can't automatically determine which executable it should run.
+    Hence, set up a custom executable run configuration in the Projects mode,
+    using the + button. Specify the name, executable, optionally some
+    arguments. The working directory is $BUILDDIR by default, which should
+    generally work fine.
 
 */
 
diff --git a/src/plugins/qt4projectmanager/qt-s60/qt-s60-todo.txt b/src/plugins/qt4projectmanager/qt-s60/qt-s60-todo.txt
index 45b879216ebeb9a45372981d8922454374e0ac08..e0c197c7eeef6b20695cb6986fd3e15ad2024ad1 100644
--- a/src/plugins/qt4projectmanager/qt-s60/qt-s60-todo.txt
+++ b/src/plugins/qt4projectmanager/qt-s60/qt-s60-todo.txt
@@ -26,8 +26,7 @@
     * auto-create run configurations the first time s60 qt is selected.
 
 * Run on device
-    * makesis, signsis and applicationinstaller don't report errors back
-      via exit code :-(
+    * Finish runner when application exits
     * passphrase for signing
 
 * Add compile output parser winscw at least
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
index e9eaddf69ef96827a80ffa42f7bf055924cd8d39..91a22bab6a1e74b32b9a49a457ec149e818bc3c7 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
@@ -108,6 +108,12 @@ void S60DeviceRunConfiguration::restore(const PersistentSettingsReader &reader)
     m_customKeyPath = reader.restoreValue("CustomKeyPath").toString();
 }
 
+QString S60DeviceRunConfiguration::targetName() const
+{
+    const_cast<S60DeviceRunConfiguration *>(this)->updateTarget();
+    return m_targetName;
+}
+
 QString S60DeviceRunConfiguration::basePackageFilePath() const
 {
     const_cast<S60DeviceRunConfiguration *>(this)->updateTarget();
@@ -200,7 +206,8 @@ void S60DeviceRunConfiguration::updateTarget()
         m_workingDir = baseDir;
     }
 
-    m_baseFileName = QDir::cleanPath(m_workingDir + QLatin1Char('/') + reader->value("TARGET"));
+    m_targetName = reader->value("TARGET");
+    m_baseFileName = QDir::cleanPath(m_workingDir + QLatin1Char('/') + m_targetName);
 
     if (pro->toolChainType(pro->activeBuildConfiguration()) == ToolChain::GCCE)
         m_baseFileName += "_gcce";
@@ -433,6 +440,15 @@ S60DeviceRunControl::S60DeviceRunControl(QSharedPointer<RunConfiguration> runCon
             this, SLOT(installProcessFailed()));
     connect(m_install, SIGNAL(finished(int,QProcess::ExitStatus)),
             this, SLOT(installProcessFinished()));
+    m_run = new QProcess(this);
+    connect(m_run, SIGNAL(readyReadStandardError()),
+            this, SLOT(readStandardError()));
+    connect(m_run, SIGNAL(readyReadStandardOutput()),
+            this, SLOT(readStandardOutput()));
+    connect(m_run, SIGNAL(error(QProcess::ProcessError)),
+            this, SLOT(runProcessFailed()));
+    connect(m_run, SIGNAL(finished(int,QProcess::ExitStatus)),
+            this, SLOT(runProcessFinished()));
 }
 
 void S60DeviceRunControl::start()
@@ -442,6 +458,7 @@ void S60DeviceRunControl::start()
 
     Qt4Project *project = qobject_cast<Qt4Project *>(rc->project());
 
+    m_targetName = rc->targetName();
     m_baseFileName = rc->basePackageFilePath();
     m_workingDirectory = QFileInfo(m_baseFileName).absolutePath();
     m_qtDir = project->qtVersion(project->activeBuildConfiguration())->path();
@@ -470,7 +487,10 @@ void S60DeviceRunControl::start()
 
 void S60DeviceRunControl::stop()
 {
-    // TODO
+    m_makesis->kill();
+    m_signsis->kill();
+    m_install->kill();
+    m_run->kill();
 }
 
 bool S60DeviceRunControl::isRunning() const
@@ -548,7 +568,27 @@ void S60DeviceRunControl::installProcessFailed()
 void S60DeviceRunControl::installProcessFinished()
 {
     if (m_install->exitCode() != 0) {
-        error(this, tr("An error occurred while creating the package."));
+        error(this, tr("An error occurred while installing the package."));
+        emit finished();
+        return;
+    }
+    QString trklauncher = QApplication::applicationDirPath() + "/../tests/manual/trk/debug/trklauncher.exe";
+    QStringList arguments;
+    arguments << "COM5" << QString("C:\\sys\\bin\\%1.exe").arg(m_targetName); //TODO com selection and file path
+    emit addToOutputWindow(this, tr("%1 %2").arg(QDir::toNativeSeparators(trklauncher), arguments.join(tr(" "))));
+    m_run->start(trklauncher, arguments, QIODevice::ReadOnly);
+}
+
+void S60DeviceRunControl::runProcessFailed()
+{
+    processFailed("trklauncher", m_run->error());
+    error(this, tr("Did you compile the trklauncher application in tests\\manual\\trk ?"));
+}
+
+void S60DeviceRunControl::runProcessFinished()
+{
+    if (m_run->exitCode() != 0) {
+        error(this, tr("An error occurred while starting the application."));
     }
     emit addToOutputWindow(this, tr("Finished."));
     emit finished();
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h
index 5cf304a0b96a01140aba114c6e68e8e0b56b7914..63491f2959e66ae39c0720c1d8b005eb2946185c 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h
@@ -58,6 +58,7 @@ public:
     void save(ProjectExplorer::PersistentSettingsWriter &writer) const;
     void restore(const ProjectExplorer::PersistentSettingsReader &reader);
 
+    QString targetName() const;
     QString basePackageFilePath() const;
     SigningMode signingMode() const;
     void setSigningMode(SigningMode mode);
@@ -76,6 +77,7 @@ private:
     void updateTarget();
 
     QString m_proFilePath;
+    QString m_targetName;
     QString m_baseFileName;
     bool m_cachedTargetInformationValid;
     SigningMode m_signingMode;
@@ -147,10 +149,13 @@ private slots:
     void signsisProcessFinished();
     void installProcessFailed();
     void installProcessFinished();
+    void runProcessFailed();
+    void runProcessFinished();
 
 private:
     void processFailed(const QString &program, QProcess::ProcessError errorCode);
 
+    QString m_targetName;
     QString m_baseFileName;
     QString m_workingDirectory;
     QString m_toolsDirectory;
@@ -161,6 +166,7 @@ private:
     QProcess *m_makesis;
     QProcess *m_signsis;
     QProcess *m_install;
+    QProcess *m_run;
 };
 
 } // namespace Internal
diff --git a/tests/manual/trk/adapter.cpp b/tests/manual/trk/adapter.cpp
index 8071b537e46e5e31e7822a2b4dcf1acbdcb282b7..4b69fa16b3348321f8250a147ca4d8a62a2a1f01 100644
--- a/tests/manual/trk/adapter.cpp
+++ b/tests/manual/trk/adapter.cpp
@@ -38,6 +38,10 @@
 #include <QtNetwork/QLocalServer>
 #include <QtNetwork/QLocalSocket>
 
+#if USE_NATIVE
+#include <windows.h>
+#endif
+
 #ifdef Q_OS_UNIX
 
 #include <signal.h>
@@ -64,7 +68,7 @@ public:
     ~Adapter();
     void setGdbServerName(const QString &name);
     void setTrkServerName(const QString &name) { m_trkServerName = name; }
-    void startServer();
+    bool startServer();
 
 private:
     //
@@ -128,7 +132,11 @@ private:
     void startInferiorIfNeeded();
     void interruptInferior();
 
-    QLocalSocket *m_trkDevice;
+#if USE_NATIVE
+    HANDLE m_hdevice;
+#else
+     QLocalSocket *m_trkDevice;
+#endif
 
     QString m_trkServerName;
     QByteArray m_trkReadBuffer;
@@ -192,13 +200,13 @@ Adapter::~Adapter()
 #if USE_NATIVE
     CloseHandle(m_hdevice);
 #else
+    m_trkDevice->abort();
     delete m_trkDevice;
 #endif
 
     // Gdb
     m_gdbServer.close();
     //>disconnectFromServer();
-    m_trkDevice->abort();
     logMessage("Shutting down.\n");
 }
 
@@ -214,11 +222,11 @@ void Adapter::setGdbServerName(const QString &name)
     }
 }
 
-void Adapter::startServer()
+bool Adapter::startServer()
 {
     if (!openTrkPort(m_trkServerName)) {
         logMessage("Unable to connect to TRK server");
-        return;
+        return false;
     }
 
     sendTrkInitialPing();
@@ -237,7 +245,7 @@ void Adapter::startServer()
         logMessage(QString("Unable to start the gdb server at %1:%2: %3.")
             .arg(m_gdbServerName).arg(m_gdbServerPort)
             .arg(m_gdbServer.errorString()));
-        return;
+        return false;
     }
 
     logMessage(QString("Gdb server running on port %1. Run arm-gdb now.")
@@ -245,6 +253,7 @@ void Adapter::startServer()
 
     connect(&m_gdbServer, SIGNAL(newConnection()),
         this, SLOT(handleGdbConnection()));
+    return true;
 }
 
 void Adapter::logMessage(const QString &msg)
@@ -669,17 +678,21 @@ void Adapter::readFromTrk()
 
 bool Adapter::openTrkPort(const QString &port)
 {
-    // QFile does not work with "COM3", so work around
-    /*
-    FILE *f = fopen("COM3", "r+");
-    if (!f) {
-        logMessage("Could not open file ");
-        return;
-    }
-    m_trkDevice = new QFile;
-    if (!m_trkDevice->open(f, QIODevice::ReadWrite))
-    */
-
+#if USE_NATIVE
+    m_hdevice = CreateFile(port.toStdWString().c_str(),
+                           GENERIC_READ | GENERIC_WRITE,
+                           0,
+                           NULL,
+                           OPEN_EXISTING,
+                           FILE_ATTRIBUTE_NORMAL,
+                           NULL);
+
+    if (INVALID_HANDLE_VALUE == m_hdevice){
+        logMessage("Could not open device " + port);
+        return false;
+     }
+    return true;
+#else
 #if 0
     m_trkDevice = new Win_QextSerialPort(port);
     m_trkDevice->setBaudRate(BAUD115200);
@@ -692,13 +705,15 @@ bool Adapter::openTrkPort(const QString &port)
     if (!m_trkDevice->open(QIODevice::ReadWrite)) {
         QByteArray ba = m_trkDevice->errorString().toLatin1();
         logMessage("Could not open device " << ba);
-        return;
+        return false;
     }
+    return true
 #else
     m_trkDevice = new QLocalSocket(this);
     m_trkDevice->connectToServer(port);
     return m_trkDevice->waitForConnected();
 #endif
+#endif
 }
 
 void Adapter::timerEvent(QTimerEvent *)
@@ -794,18 +809,15 @@ void Adapter::trkWrite(const TrkMessage &msg)
     m_writtenTrkMessages.insert(msg.token, msg);
     m_trkWriteBusy = true;
 
-#if USE_NATIVE
+    logMessage("WRITE: " + stringFromArray(ba));
 
+#if USE_NATIVE
     DWORD charsWritten;
     if (!WriteFile(m_hdevice, ba.data(), ba.size(), &charsWritten, NULL))
         logMessage("WRITE ERROR: ");
 
-    //logMessage("WRITE: " + stringFromArray(ba));
     FlushFileBuffers(m_hdevice);
-
 #else
-
-    //logMessage("WRITE: " + stringFromArray(ba));
     if (!m_trkDevice->write(ba))
         logMessage("WRITE ERROR: " + m_trkDevice->errorString());
     m_trkDevice->flush();
@@ -819,17 +831,15 @@ void Adapter::tryTrkRead()
     //        << stringFromArray(m_trkReadQueue);
 
 #if USE_NATIVE
-
-    const int BUFFERSIZE = 1024;
+    const DWORD BUFFERSIZE = 1;
     char buffer[BUFFERSIZE];
     DWORD charsRead;
 
-    while (ReadFile(m_hdevice, buffer, BUFFERSIZE, &charsRead, NULL)
-            && BUFFERSIZE == charsRead) {
+    while (ReadFile(m_hdevice, buffer, BUFFERSIZE, &charsRead, NULL)) {
         m_trkReadQueue.append(buffer, charsRead);
+        if (isValidTrkResult(m_trkReadQueue))
+            break;
     }
-    m_trkReadQueue.append(buffer, charsRead);
-
 #else // USE_NATIVE
 
     if (m_trkDevice->bytesAvailable() == 0 && m_trkReadQueue.isEmpty())
@@ -1079,12 +1089,10 @@ void Adapter::handleAndReportReadRegisters(const TrkResult &result)
         m_snapshot.registers[i] = extractInt(data + 4 * i);
         //qDebug() << i << hexNumber(m_snapshot.registers[i], 8);
     }
-
     //QByteArray ba = result.data.toHex();
     QByteArray ba;
     for (int i = 0; i < 16; ++i)
         ba += hexNumber(m_snapshot.registers[i], 8);
-
     sendGdbMessage(ba, "register contents");
 }
 
@@ -1310,9 +1318,10 @@ void Adapter::startInferiorIfNeeded()
     appendByte(&ba, 0); // ?
     appendByte(&ba, 0); // ?
 
-    appendString(&ba, "C:\\sys\\bin\\filebrowseapp.exe", TargetByteOrder);
-    ba.append('\0');
-    ba.append('\0');
+    QByteArray file("C:\\sys\\bin\\filebrowseapp.exe");
+    file.append('\0');
+    file.append('\0');
+    appendString(&ba, file, TargetByteOrder);
     sendTrkMessage(0x40, CB(handleCreateProcess), ba); // Create Item
 }
 
@@ -1342,9 +1351,9 @@ int main(int argc, char *argv[])
     Adapter adapter;
     adapter.setTrkServerName(argv[1]);
     adapter.setGdbServerName(argv[2]);
-    adapter.startServer();
-
-    return app.exec();
+    if (adapter.startServer())
+        return app.exec();
+    return 4;
 }
 
 #include "adapter.moc"
diff --git a/tests/manual/trk/launcher.cpp b/tests/manual/trk/launcher.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5091148166eab6d67a2076be6cabcc05b7ef15d5
--- /dev/null
+++ b/tests/manual/trk/launcher.cpp
@@ -0,0 +1,642 @@
+/**************************************************************************
+**
+** 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://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#include "trkutils.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QFile>
+#include <QtCore/QQueue>
+#include <QtCore/QTimer>
+
+#include <QtNetwork/QTcpServer>
+#include <QtNetwork/QTcpSocket>
+#include <QtNetwork/QLocalServer>
+#include <QtNetwork/QLocalSocket>
+
+#if USE_NATIVE
+#include <windows.h>
+#endif
+
+#ifdef Q_OS_UNIX
+
+#include <signal.h>
+
+void signalHandler(int)
+{
+    qApp->exit(1);
+}
+
+#endif
+
+using namespace trk;
+
+enum { TRK_SYNC = 0x7f };
+
+#define CB(s) &Adapter::s
+
+class Adapter : public QObject
+{
+    Q_OBJECT
+
+public:
+    Adapter();
+    ~Adapter();
+    void setTrkServerName(const QString &name) { m_trkServerName = name; }
+    void setFileName(const QString &name) { m_fileName = name; }
+    bool startServer();
+
+private:
+    //
+    // TRK
+    //
+    typedef void (Adapter::*TrkCallBack)(const TrkResult &);
+
+    struct TrkMessage 
+    {
+        TrkMessage() { code = token = 0; callBack = 0; }
+        byte code;
+        byte token;
+        QByteArray data;
+        QVariant cookie;
+        TrkCallBack callBack;
+    };
+
+    bool openTrkPort(const QString &port); // or server name for local server
+    void sendTrkMessage(byte code,
+        TrkCallBack callBack = 0,
+        const QByteArray &data = QByteArray(),
+        const QVariant &cookie = QVariant());
+    // adds message to 'send' queue
+    void queueTrkMessage(const TrkMessage &msg);
+    void tryTrkWrite();
+    void tryTrkRead();
+    // actually writes a message to the device
+    void trkWrite(const TrkMessage &msg);
+    // convienience messages
+    void sendTrkInitialPing();
+    void waitForTrkFinished();
+    void sendTrkAck(byte token);
+
+    // kill process and breakpoints
+    void cleanUp();
+
+    void timerEvent(QTimerEvent *ev);
+    byte nextTrkWriteToken();
+
+    void handleCpuType(const TrkResult &result);
+    void handleCreateProcess(const TrkResult &result);
+    void handleWaitForFinished(const TrkResult &result);
+    void handleStop(const TrkResult &result);
+    void handleSupportMask(const TrkResult &result);
+
+    void handleAndReportCreateProcess(const TrkResult &result);
+    void handleResult(const TrkResult &data);
+    void startInferiorIfNeeded();
+
+#if USE_NATIVE
+    HANDLE m_hdevice;
+#else
+    QLocalSocket *m_trkDevice;
+#endif
+
+    QString m_trkServerName;
+    QByteArray m_trkReadBuffer;
+
+    unsigned char m_trkWriteToken;
+    QQueue<TrkMessage> m_trkWriteQueue;
+    QHash<byte, TrkMessage> m_writtenTrkMessages;
+    QByteArray m_trkReadQueue;
+    bool m_trkWriteBusy;
+
+    void logMessage(const QString &msg);
+    // Debuggee state
+    Session m_session; // global-ish data (process id, target information)
+
+    QString m_fileName;
+};
+
+Adapter::Adapter()
+{
+    // Trk
+#if USE_NATIVE
+    m_hdevice = NULL;
+#else
+    m_trkDevice = 0;
+#endif
+    m_trkWriteToken = 0;
+    m_trkWriteBusy = false;
+    startTimer(100);
+}
+
+Adapter::~Adapter()
+{
+    // Trk
+#if USE_NATIVE
+    CloseHandle(m_hdevice);
+#else
+    m_trkDevice->abort();
+    delete m_trkDevice;
+#endif
+
+    logMessage("Shutting down.\n");
+}
+
+bool Adapter::startServer()
+{
+    if (!openTrkPort(m_trkServerName)) {
+        logMessage("Unable to connect to TRK server");
+        return false;
+    }
+
+    sendTrkInitialPing();
+    sendTrkMessage(0x01); // Connect
+    sendTrkMessage(0x05, CB(handleSupportMask));
+    sendTrkMessage(0x06, CB(handleCpuType));
+    sendTrkMessage(0x04); // Versions
+//    sendTrkMessage(0x09); // Unrecognized command
+    startInferiorIfNeeded();
+    return true;
+}
+
+void Adapter::logMessage(const QString &msg)
+{
+    qDebug() << "ADAPTER: " << qPrintable(msg);
+}
+
+bool Adapter::openTrkPort(const QString &port)
+{
+#if USE_NATIVE
+    m_hdevice = CreateFile(port.toStdWString().c_str(),
+                           GENERIC_READ | GENERIC_WRITE,
+                           0,
+                           NULL,
+                           OPEN_EXISTING,
+                           FILE_ATTRIBUTE_NORMAL,
+                           NULL);
+
+    if (INVALID_HANDLE_VALUE == m_hdevice){
+        logMessage("Could not open device " + port);
+        return false;
+    }
+    return true;
+#else
+#if 0
+    m_trkDevice = new Win_QextSerialPort(port);
+    m_trkDevice->setBaudRate(BAUD115200);
+    m_trkDevice->setDataBits(DATA_8);
+    m_trkDevice->setParity(PAR_NONE);
+    //m_trkDevice->setStopBits(STO);
+    m_trkDevice->setFlowControl(FLOW_OFF);
+    m_trkDevice->setTimeout(0, 500);
+
+    if (!m_trkDevice->open(QIODevice::ReadWrite)) {
+        QByteArray ba = m_trkDevice->errorString().toLatin1();
+        logMessage("Could not open device " << ba);
+        return false;
+    }
+    return true;
+#else
+    m_trkDevice = new QLocalSocket(this);
+    m_trkDevice->connectToServer(port);
+    return m_trkDevice->waitForConnected();
+#endif
+#endif
+}
+
+void Adapter::timerEvent(QTimerEvent *)
+{
+    //qDebug(".");
+    tryTrkWrite();
+    tryTrkRead();
+}
+
+byte Adapter::nextTrkWriteToken()
+{
+    ++m_trkWriteToken;
+    if (m_trkWriteToken == 0)
+        ++m_trkWriteToken;
+    return m_trkWriteToken;
+}
+
+void Adapter::sendTrkMessage(byte code, TrkCallBack callBack,
+    const QByteArray &data, const QVariant &cookie)
+{
+    TrkMessage msg;
+    msg.code = code;
+    msg.token = nextTrkWriteToken();
+    msg.callBack = callBack;
+    msg.data = data;
+    msg.cookie = cookie;
+    queueTrkMessage(msg);
+}
+
+void Adapter::sendTrkInitialPing()
+{
+    TrkMessage msg;
+    msg.code = 0x00; // Ping
+    msg.token = 0; // reset sequence count
+    queueTrkMessage(msg);
+}
+
+void Adapter::waitForTrkFinished()
+{
+    TrkMessage msg;
+    // initiate one last roundtrip to ensure all is flushed
+    msg.code = 0x00; // Ping
+    msg.token = nextTrkWriteToken();
+    msg.callBack = CB(handleWaitForFinished);
+    queueTrkMessage(msg);
+}
+
+void Adapter::sendTrkAck(byte token)
+{
+    logMessage(QString("SENDING ACKNOWLEDGEMENT FOR TOKEN %1").arg(int(token)));
+    TrkMessage msg;
+    msg.code = 0x80;
+    msg.token = token;
+    msg.data.append('\0');
+    // The acknowledgement must not be queued!
+    //queueMessage(msg);
+    trkWrite(msg);
+    // 01 90 00 07 7e 80 01 00 7d 5e 7e
+}
+
+void Adapter::queueTrkMessage(const TrkMessage &msg)
+{
+    m_trkWriteQueue.append(msg);
+}
+
+void Adapter::tryTrkWrite()
+{
+    if (m_trkWriteBusy)
+        return;
+    if (m_trkWriteQueue.isEmpty())
+        return;
+
+    TrkMessage msg = m_trkWriteQueue.dequeue();
+    if (msg.code == TRK_SYNC) {
+        //logMessage("TRK SYNC");
+        TrkResult result;
+        result.code = msg.code;
+        result.token = msg.token;
+        result.data = msg.data;
+        result.cookie = msg.cookie;
+        TrkCallBack cb = msg.callBack;
+        if (cb)
+            (this->*cb)(result);
+    } else {
+        trkWrite(msg);
+    }
+}
+
+void Adapter::trkWrite(const TrkMessage &msg)
+{
+    QByteArray ba = frameMessage(msg.code, msg.token, msg.data);
+
+    m_writtenTrkMessages.insert(msg.token, msg);
+    m_trkWriteBusy = true;
+
+    logMessage("WRITE: " + stringFromArray(ba));
+
+#if USE_NATIVE
+    DWORD charsWritten;
+    if (!WriteFile(m_hdevice, ba.data(), ba.size(), &charsWritten, NULL))
+        logMessage("WRITE ERROR: ");
+
+    //logMessage("WRITE: " + stringFromArray(ba));
+    FlushFileBuffers(m_hdevice);
+#else
+    if (!m_trkDevice->write(ba))
+        logMessage("WRITE ERROR: " + m_trkDevice->errorString());
+    m_trkDevice->flush();
+
+#endif
+}
+
+void Adapter::tryTrkRead()
+{
+#if USE_NATIVE
+    const DWORD BUFFERSIZE = 1;
+    char buffer[BUFFERSIZE];
+    DWORD charsRead;
+
+    while (ReadFile(m_hdevice, buffer, BUFFERSIZE, &charsRead, NULL)) {
+        m_trkReadQueue.append(buffer, charsRead);
+        if (isValidTrkResult(m_trkReadQueue))
+            break;
+    }
+#else // USE_NATIVE
+    if (m_trkDevice->bytesAvailable() == 0 && m_trkReadQueue.isEmpty()) {
+        return;
+    }
+
+    QByteArray res = m_trkDevice->readAll();
+    m_trkReadQueue.append(res);
+#endif // USE_NATIVE
+
+    logMessage("READ:  " + stringFromArray(m_trkReadQueue));
+    if (m_trkReadQueue.size() < 9) {
+        logMessage("ERROR READBUFFER INVALID (1): "
+            + stringFromArray(m_trkReadQueue));
+        m_trkReadQueue.clear();
+        return;
+    }
+
+    while (!m_trkReadQueue.isEmpty())
+        handleResult(extractResult(&m_trkReadQueue));
+
+    m_trkWriteBusy = false;
+}
+
+
+void Adapter::handleResult(const TrkResult &result)
+{
+    QByteArray prefix = "READ BUF:                                       ";
+    QByteArray str = result.toString().toUtf8();
+    switch (result.code) {
+        case 0x80: { // ACK
+            //logMessage(prefix + "ACK: " + str);
+            if (!result.data.isEmpty() && result.data.at(0))
+                logMessage(prefix + "ERR: " +QByteArray::number(result.data.at(0)));
+            //logMessage("READ RESULT FOR TOKEN: " << token);
+            if (!m_writtenTrkMessages.contains(result.token)) {
+                logMessage("NO ENTRY FOUND!");
+            }
+            TrkMessage msg = m_writtenTrkMessages.take(result.token);
+            TrkResult result1 = result;
+            result1.cookie = msg.cookie;
+            TrkCallBack cb = msg.callBack;
+            if (cb) {
+                //logMessage("HANDLE: " << stringFromArray(result.data));
+                (this->*cb)(result1);
+            } else {
+                QString msg = result.cookie.toString();
+                if (!msg.isEmpty())
+                    logMessage("HANDLE: " + msg + stringFromArray(result.data));
+            }
+            break;
+        }
+        case 0xff: { // NAK
+            logMessage(prefix + "NAK: " + str);
+            //logMessage(prefix << "TOKEN: " << result.token);
+            logMessage(prefix + "ERROR: " + errorMessage(result.data.at(0)));
+            break;
+        }
+        case 0x90: { // Notified Stopped
+            logMessage(prefix + "NOTE: STOPPED  " + str);
+            // 90 01   78 6a 40 40   00 00 07 23   00 00 07 24  00 00
+            //const char *data = result.data.data();
+//            uint addr = extractInt(data); //code address: 4 bytes; code base address for the library
+//            uint pid = extractInt(data + 4); // ProcessID: 4 bytes;
+//            uint tid = extractInt(data + 8); // ThreadID: 4 bytes
+            //logMessage(prefix << "      ADDR: " << addr << " PID: " << pid << " TID: " << tid);
+            sendTrkAck(result.token);
+            break;
+        }
+        case 0x91: { // Notify Exception (obsolete)
+            logMessage(prefix + "NOTE: EXCEPTION  " + str);
+            sendTrkAck(result.token);
+            break;
+        }
+        case 0x92: { //
+            logMessage(prefix + "NOTE: INTERNAL ERROR: " + str);
+            sendTrkAck(result.token);
+            break;
+        }
+
+        // target->host OS notification
+        case 0xa0: { // Notify Created
+            /*
+            const char *data = result.data.data();
+            byte error = result.data.at(0);
+            byte type = result.data.at(1); // type: 1 byte; for dll item, this value is 2.
+            uint pid = extractInt(data + 2); //  ProcessID: 4 bytes;
+            uint tid = extractInt(data + 6); //threadID: 4 bytes
+            uint codeseg = extractInt(data + 10); //code address: 4 bytes; code base address for the library
+            uint dataseg = extractInt(data + 14); //data address: 4 bytes; data base address for the library
+            uint len = extractShort(data + 18); //length: 2 bytes; length of the library name string to follow
+            QByteArray name = result.data.mid(20, len); // name: library name
+
+            logMessage(prefix + "NOTE: LIBRARY LOAD: " + str);
+            logMessage(prefix + "TOKEN: " + result.token);
+            logMessage(prefix + "ERROR: " + int(error));
+            logMessage(prefix + "TYPE:  " + int(type));
+            logMessage(prefix + "PID:   " + pid);
+            logMessage(prefix + "TID:   " + tid);
+            logMessage(prefix + "CODE:  " + codeseg);
+            logMessage(prefix + "DATA:  " + dataseg);
+            logMessage(prefix + "LEN:   " + len);
+            logMessage(prefix + "NAME:  " + name);
+            */
+
+            QByteArray ba;
+            appendInt(&ba, m_session.pid);
+            appendInt(&ba, m_session.tid);
+            sendTrkMessage(0x18, 0, ba, "CONTINUE");
+            //sendTrkAck(result.token)
+            break;
+        }
+        case 0xa1: { // NotifyDeleted
+            logMessage(prefix + "NOTE: LIBRARY UNLOAD: " + str);
+            sendTrkAck(result.token);
+            break;
+        }
+        case 0xa2: { // NotifyProcessorStarted
+            logMessage(prefix + "NOTE: PROCESSOR STARTED: " + str);
+            sendTrkAck(result.token);
+            break;
+        }
+        case 0xa6: { // NotifyProcessorStandby
+            logMessage(prefix + "NOTE: PROCESSOR STANDBY: " + str);
+            sendTrkAck(result.token);
+            break;
+        }
+        case 0xa7: { // NotifyProcessorReset
+            logMessage(prefix + "NOTE: PROCESSOR RESET: " + str);
+            sendTrkAck(result.token);
+            break;
+        }
+        default: {
+            logMessage(prefix + "INVALID: " + str);
+            break;
+        }
+    }
+}
+
+void Adapter::handleCpuType(const TrkResult &result)
+{
+    logMessage("HANDLE CPU TYPE: " + result.toString());
+    //---TRK------------------------------------------------------
+    //  Command: 0x80 Acknowledge
+    //    Error: 0x00
+    // [80 03 00  04 00 00 04 00 00 00]
+    m_session.cpuMajor = result.data[0];
+    m_session.cpuMinor = result.data[1];
+    m_session.bigEndian = result.data[2];
+    m_session.defaultTypeSize = result.data[3];
+    m_session.fpTypeSize = result.data[4];
+    m_session.extended1TypeSize = result.data[5];
+    //m_session.extended2TypeSize = result.data[6];
+}
+
+void Adapter::handleCreateProcess(const TrkResult &result)
+{
+    //  40 00 00]
+    //logMessage("       RESULT: " + result.toString());
+    // [80 08 00   00 00 01 B5   00 00 01 B6   78 67 40 00   00 40 00 00]
+    const char *data = result.data.data();
+    m_session.pid = extractInt(data + 1);
+    m_session.tid = extractInt(data + 5);
+    m_session.codeseg = extractInt(data + 9);
+    m_session.dataseg = extractInt(data + 13);
+    qDebug() << "    READ PID: " << m_session.pid;
+    qDebug() << "    READ TID: " << m_session.tid;
+    qDebug() << "    READ CODE: " << m_session.codeseg;
+    qDebug() << "    READ DATA: " << m_session.dataseg;
+    QByteArray ba;
+    appendInt(&ba, m_session.pid);
+    appendInt(&ba, m_session.tid);
+    sendTrkMessage(0x18, 0, ba, "CONTINUE");
+}
+
+void Adapter::handleWaitForFinished(const TrkResult &result)
+{
+    logMessage("   FINISHED: " + stringFromArray(result.data));
+    //qApp->exit(1);
+}
+
+void Adapter::handleSupportMask(const TrkResult &result)
+{
+    const char *data = result.data.data();
+    QByteArray str;
+    for (int i = 0; i < 32; ++i) {
+        //str.append("  [" + formatByte(data[i]) + "]: ");
+        for (int j = 0; j < 8; ++j)
+        if (data[i] & (1 << j))
+            str.append(QByteArray::number(i * 8 + j, 16));
+    }
+    logMessage("SUPPORTED: " + str);
+}
+
+
+void Adapter::cleanUp()
+{
+    //
+    //---IDE------------------------------------------------------
+    //  Command: 0x41 Delete Item
+    //  Sub Cmd: Delete Process
+    //ProcessID: 0x0000071F (1823)
+    // [41 24 00 00 00 00 07 1F]
+    QByteArray ba;
+    appendByte(&ba, 0x00);
+    appendByte(&ba, 0x00);
+    appendInt(&ba, m_session.pid);
+    sendTrkMessage(0x41, 0, ba, "Delete process");
+
+    //---TRK------------------------------------------------------
+    //  Command: 0x80 Acknowledge
+    //    Error: 0x00
+    // [80 24 00]
+
+    //---IDE------------------------------------------------------
+    //  Command: 0x1C Clear Break
+    // [1C 25 00 00 00 0A 78 6A 43 40]
+
+        //---TRK------------------------------------------------------
+        //  Command: 0xA1 Notify Deleted
+        // [A1 09 00 00 00 00 00 00 00 00 07 1F]
+        //---IDE------------------------------------------------------
+        //  Command: 0x80 Acknowledge
+        //    Error: 0x00
+        // [80 09 00]
+
+    //---TRK------------------------------------------------------
+    //  Command: 0x80 Acknowledge
+    //    Error: 0x00
+    // [80 25 00]
+
+    //---IDE------------------------------------------------------
+    //  Command: 0x1C Clear Break
+    // [1C 26 00 00 00 0B 78 6A 43 70]
+    //---TRK------------------------------------------------------
+    //  Command: 0x80 Acknowledge
+    //    Error: 0x00
+    // [80 26 00]
+
+
+    //---IDE------------------------------------------------------
+    //  Command: 0x02 Disconnect
+    // [02 27]
+//    sendTrkMessage(0x02, CB(handleDisconnect));
+    //---TRK------------------------------------------------------
+    //  Command: 0x80 Acknowledge
+    // Error: 0x00
+}
+
+void Adapter::startInferiorIfNeeded()
+{
+    if (m_session.pid != 0) {
+        qDebug() << "Process already 'started'";
+        return;
+    }
+    // It's not started yet
+    QByteArray ba;
+    appendByte(&ba, 0); // ?
+    appendByte(&ba, 0); // ?
+    appendByte(&ba, 0); // ?
+    QByteArray file = m_fileName.toLocal8Bit();
+    file.append('\0');
+    file.append('\0');
+    appendString(&ba, file, TargetByteOrder);
+    sendTrkMessage(0x40, CB(handleCreateProcess), ba); // Create Item
+}
+
+int main(int argc, char *argv[])
+{
+    if (argc < 3) {
+        qDebug() << "Usage: " << argv[0] << "<trkservername> <remotefilename>";
+        qDebug() << "for example" << argv[0] << "COM5 C:\\sys\\bin\\test.exe";
+        return 1;
+    }
+
+#ifdef Q_OS_UNIX
+    signal(SIGUSR1, signalHandler);
+#endif
+
+    QCoreApplication app(argc, argv);
+
+    Adapter adapter;
+    adapter.setTrkServerName(argv[1]);
+    adapter.setFileName(argv[2]);
+    if (adapter.startServer())
+        return app.exec();
+    return 4;
+}
+
+#include "launcher.moc"
diff --git a/tests/manual/trk/trk.pro b/tests/manual/trk/trk.pro
index a461bb70566ec03c593cda5c127bc41d37a5f6c8..b4fde49cf8c60299f22516740eeb74f7f22f63e3 100644
--- a/tests/manual/trk/trk.pro
+++ b/tests/manual/trk/trk.pro
@@ -1,7 +1,7 @@
 
 TEMPLATE = subdirs
 
-SUBDIRS = trkserver adapter swapendian
+SUBDIRS = trkserver adapter swapendian trklauncher.pro
 
 trkserver.file = trkserver.pro
 adapter.file = adapter.pro
diff --git a/tests/manual/trk/trklauncher.pro b/tests/manual/trk/trklauncher.pro
new file mode 100644
index 0000000000000000000000000000000000000000..90f4155516bbe64fa36a43a451c648ba4ec1f4dd
--- /dev/null
+++ b/tests/manual/trk/trklauncher.pro
@@ -0,0 +1,10 @@
+TEMPLATE = app
+QT = core network
+QT -= gui
+
+win32:CONFIG += console
+win32:DEFINES += USE_NATIVE
+
+SOURCES = launcher.cpp \
+    trkutils.cpp
+HEADERS = trkutils.h
diff --git a/tests/manual/trk/trkutils.cpp b/tests/manual/trk/trkutils.cpp
index ece55dd247e786b6c0ac5bdc30e8a71cca91ee2c..4c132de3d71d7d0f83cb3a46110f6af4927bc99e 100644
--- a/tests/manual/trk/trkutils.cpp
+++ b/tests/manual/trk/trkutils.cpp
@@ -80,38 +80,38 @@ QByteArray frameMessage(byte command, byte token, const QByteArray &data)
     return ba;
 }
 
-TrkResult extractResult(QByteArray *buffer)
+/* returns 0 if array doesn't represent a result,
+otherwise returns the length of the result data */
+ushort isValidTrkResult(const QByteArray &buffer)
 {
-    TrkResult result;
-    if (buffer->at(0) != 0x01 || byte(buffer->at(1)) != 0x90) {
-        logMessage("*** ERROR READBUFFER INVALID (2): "
-            << stringFromArray(*buffer)
-            << int(buffer->at(0))
-            << int(buffer->at(1))
-            << buffer->size());
-        return result;
+    if (buffer.length() < 9)
+        return 0;
+    if (buffer.at(0) != 0x01 || byte(buffer.at(1)) != 0x90) {
+        return 0;
     }
-    ushort len = extractShort(buffer->data() + 2);
+    ushort len = extractShort(buffer.data() + 2);
 
     //logMessage("   READ BUF: " << stringFromArray(*buffer));
-    if (buffer->size() < len + 4) {
-        logMessage("*** INCOMPLETE RESPONSE: "
-            << stringFromArray(*buffer));
-        return result;
+    if (buffer.size() < len + 4) {
+        return 0;
     }
 
-    if (byte(buffer->at(4)) != 0x7e) {
-        logMessage("** ERROR READBUFFER BEGIN FRAME MARKER INVALID: "
-            << stringFromArray(*buffer) << len);
-        return result;
+    if (byte(buffer.at(4)) != 0x7e) {
+        return 0;
     }
 
-    if (byte(buffer->at(4 + len - 1)) != 0x7e) {
-        logMessage("** ERROR READBUFFER END FRAME MARKER INVALID: "
-            << stringFromArray(*buffer) << len);
-        return result;
+    if (byte(buffer.at(4 + len - 1)) != 0x7e) {
+        return 0;
     }
+    return len;
+}
 
+TrkResult extractResult(QByteArray *buffer)
+{
+    TrkResult result;
+    ushort len = isValidTrkResult(*buffer);
+    if (!len)
+        return result;
     // FIXME: what happens if the length contains 0xfe?
     // Assume for now that it passes unencoded!
     QByteArray data = decode7d(buffer->mid(5, len - 2));
diff --git a/tests/manual/trk/trkutils.h b/tests/manual/trk/trkutils.h
index 56d02ddaa0a963874d52365231be7544ee9464f3..5b575f6c449e6a9f48d9f9dbb8015f4f4984b598 100644
--- a/tests/manual/trk/trkutils.h
+++ b/tests/manual/trk/trkutils.h
@@ -35,10 +35,10 @@
 #include <QtCore/QString>
 #include <QtCore/QVariant>
 
-namespace trk {
-
 typedef unsigned char byte;
 
+namespace trk {
+
 QByteArray decode7d(const QByteArray &ba);
 QByteArray encode7d(const QByteArray &ba);
 
@@ -149,6 +149,7 @@ struct TrkResult
 
 // returns a QByteArray containing 0x01 0x90 <len> 0x7e encoded7d(ba) 0x7e
 QByteArray frameMessage(byte command, byte token, const QByteArray &data);
+ushort isValidTrkResult(const QByteArray &buffer);
 TrkResult extractResult(QByteArray *buffer);
 QByteArray errorMessage(byte code);
 QByteArray hexNumber(uint n, int digits = 0);