From fdf6120d17ec49e1aa06477cd10fbf31bd33a3d6 Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Thu, 20 Aug 2009 15:58:20 +0200
Subject: [PATCH] Trk: Add device class.

---
 tests/manual/trk/adapter.cpp     |   7 +-
 tests/manual/trk/launcher.cpp    | 213 +++-----------------
 tests/manual/trk/launcher.h      |   8 +-
 tests/manual/trk/trkdevice.cpp   | 323 +++++++++++++++++++++++++++++++
 tests/manual/trk/trkdevice.h     |  84 ++++++++
 tests/manual/trk/trklauncher.pri |   4 +-
 tests/manual/trk/trkserver.cpp   |   7 +-
 tests/manual/trk/trkutils.cpp    |  30 +--
 tests/manual/trk/trkutils.h      |   3 +-
 9 files changed, 473 insertions(+), 206 deletions(-)
 create mode 100644 tests/manual/trk/trkdevice.cpp
 create mode 100644 tests/manual/trk/trkdevice.h

diff --git a/tests/manual/trk/adapter.cpp b/tests/manual/trk/adapter.cpp
index 0f28ad8f012..18d06b5bd13 100644
--- a/tests/manual/trk/adapter.cpp
+++ b/tests/manual/trk/adapter.cpp
@@ -1059,7 +1059,7 @@ void Adapter::tryTrkRead()
         while (TryReadFile(m_winComDevice, buffer, BUFFERSIZE, &charsRead, NULL)) {
             m_trkReadQueue.append(buffer, charsRead);
             totalCharsRead += charsRead;
-            if (isValidTrkResult(m_trkReadQueue))
+            if (isValidTrkResult(m_trkReadQueue, m_serialFrame))
                 break;
         }
         if (!totalCharsRead)
@@ -1074,8 +1074,9 @@ void Adapter::tryTrkRead()
         return;
     }
 
-    while (!m_trkReadQueue.isEmpty())
-        handleResult(extractResult(&m_trkReadQueue, m_serialFrame));
+    TrkResult r;
+    while (extractResult(&m_trkReadQueue, m_serialFrame, &r))
+        handleResult(r);
 
     m_trkWriteBusy = false;
 }
diff --git a/tests/manual/trk/launcher.cpp b/tests/manual/trk/launcher.cpp
index 389548fd053..6e3a68ee3f3 100644
--- a/tests/manual/trk/launcher.cpp
+++ b/tests/manual/trk/launcher.cpp
@@ -29,6 +29,7 @@
 
 #include "launcher.h"
 #include "trkutils.h"
+#include "trkdevice.h"
 
 #include <QtCore/QTimer>
 #include <QtCore/QDateTime>
@@ -37,60 +38,6 @@
 #include <QtCore/QQueue>
 #include <QtCore/QFile>
 
-#ifdef Q_OS_WIN
-#  include <windows.h>
-#else
-#  include <stdio.h>
-#  include <sys/ioctl.h>
-#  include <termios.h>
-#  include <errno.h>
-#  include <string.h>
-#endif
-
-#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)){
-        qDebug() << "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 {
 
 struct TrkMessage {
@@ -104,12 +51,7 @@ struct TrkMessage {
 
 struct LauncherPrivate {
     LauncherPrivate();
-#ifdef Q_OS_WIN
-    HANDLE m_hdevice;
-#else
-    QFile m_file;
-#endif
-
+    TrkDevice m_device;
     QString m_trkServerName;
     QByteArray m_trkReadBuffer;
 
@@ -128,18 +70,13 @@ struct LauncherPrivate {
     QString m_copySrcFileName;
     QString m_copyDstFileName;
     QString m_installFileName;
-    bool m_serialFrame;
     int m_verbose;
 };
 
 LauncherPrivate::LauncherPrivate() :
-#ifdef Q_OS_WIN
-    m_hdevice(0),
-#endif
     m_trkWriteToken(0),
     m_trkWriteBusy(false),
     m_timerId(-1),
-    m_serialFrame(true),
     m_verbose(0)
 {
 }
@@ -149,6 +86,7 @@ LauncherPrivate::LauncherPrivate() :
 Launcher::Launcher() :
     d(new LauncherPrivate)
 {
+    connect(&d->m_device, SIGNAL(messageReceived(TrkResult)), this, SLOT(handleResult(TrkResult)));
 }
 
 Launcher::~Launcher()
@@ -180,7 +118,12 @@ void Launcher::setInstallFileName(const QString &name)
 
 void Launcher::setSerialFrame(bool b)
 {
-    d->m_serialFrame = b;
+    d->m_device.setSerialFrame(b);
+}
+
+bool Launcher::serialFrame() const
+{
+    return d->m_device.serialFrame();
 }
 
 bool Launcher::startServer(QString *errorMessage)
@@ -190,7 +133,7 @@ bool Launcher::startServer(QString *errorMessage)
                             .arg(d->m_trkServerName, d->m_fileName, d->m_copySrcFileName, d->m_copyDstFileName, d->m_installFileName);
         logMessage(msg);
     }
-    if (!openTrkPort(d->m_trkServerName, errorMessage))
+    if (!d->m_device.open(d->m_trkServerName, errorMessage))
         return false;
     d->m_timerId = startTimer(100);
     sendTrkInitialPing();
@@ -210,6 +153,7 @@ bool Launcher::startServer(QString *errorMessage)
 void Launcher::setVerbose(int v)
 {
     d->m_verbose = v;
+    d->m_device.setVerbose(v > 1);
 }
 
 void Launcher::installAndRun()
@@ -226,60 +170,11 @@ void Launcher::logMessage(const QString &msg)
         qDebug() << "ADAPTER: " << qPrintable(msg);
 }
 
-bool Launcher::openTrkPort(const QString &port, QString *errorMessage)
-{
-#ifdef Q_OS_WIN
-    d->m_hdevice = CreateFile(port.toStdWString().c_str(),
-                           GENERIC_READ | GENERIC_WRITE,
-                           0,
-                           NULL,
-                           OPEN_EXISTING,
-                           FILE_ATTRIBUTE_NORMAL,
-                           NULL);
-
-    if (INVALID_HANDLE_VALUE == d->m_hdevice){
-        *errorMessage = QString::fromLatin1("Could not open device '%1': %2").arg(port, winErrorMessage(GetLastError()));
-        logMessage(*errorMessage);
-        return false;
-    }
-    return true;
-#else
-    d->m_file.setFileName(port);
-    if (!d->m_file.open(QIODevice::ReadWrite|QIODevice::Unbuffered)) {
-        *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(port, d->m_file.errorString());
-        return false;
-    }
-
-    struct termios termInfo;
-    if (tcgetattr(d->m_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->m_file.handle(), TCSAFLUSH, &termInfo) < 0) {
-        *errorMessage = QString::fromLatin1("Unable to apply terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno)));
-        return false;
-    }
-    return true;
-#endif
-}
-
 void Launcher::timerEvent(QTimerEvent *)
 {
     if (d->m_verbose>1)
         qDebug(".");
     tryTrkWrite();
-    tryTrkRead();
 }
 
 byte Launcher::nextTrkWriteToken()
@@ -333,7 +228,7 @@ void Launcher::sendTrkAck(byte token)
     msg.data.append('\0');
     // The acknowledgement must not be queued!
     //queueMessage(msg);
-    trkWrite(msg);
+    trkWriteRawMessage(msg);
     // 01 90 00 07 7e 80 01 00 7d 5e 7e
 }
 
@@ -353,80 +248,25 @@ void Launcher::tryTrkWrite()
     trkWrite(msg);
 }
 
-void Launcher::trkWrite(const TrkMessage &msg)
+void Launcher::trkWriteRawMessage(const TrkMessage &msg)
 {
-    QByteArray ba = frameMessage(msg.code, msg.token, msg.data, d->m_serialFrame);
-
-    d->m_writtenTrkMessages.insert(msg.token, msg);
-    d->m_trkWriteBusy = true;
-
+    const QByteArray ba = frameMessage(msg.code, msg.token, msg.data, serialFrame());
     logMessage("WRITE: " + stringFromArray(ba));
-
-#ifdef Q_OS_WIN
-    DWORD charsWritten;
-    if (!WriteFile(d->m_hdevice, ba.data(), ba.size(), &charsWritten, NULL))
-        logMessage("WRITE ERROR: ");
-
-    //logMessage("WRITE: " + stringFromArray(ba));
-    FlushFileBuffers(d->m_hdevice);
-#else
-    if (d->m_file.write(ba) == -1 || !d->m_file.flush())
-        logMessage(QString::fromLatin1("Cannot write: %1").arg(d->m_file.errorString()));
-#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;
+    QString errorMessage;
+    if (!d->m_device.write(ba, &errorMessage))
+        logMessage(errorMessage);
 }
-#endif
 
-void Launcher::tryTrkRead()
+void Launcher::trkWrite(const TrkMessage &msg)
 {
-#ifdef Q_OS_WIN
-    const DWORD BUFFERSIZE = 1024;
-    char buffer[BUFFERSIZE];
-    DWORD charsRead;
-
-    while (TryReadFile(d->m_hdevice, buffer, BUFFERSIZE, &charsRead, NULL)) {
-        d->m_trkReadQueue.append(buffer, charsRead);
-        if (isValidTrkResult(d->m_trkReadQueue, d->m_serialFrame))
-            break;
-    }
-    const ushort len = isValidTrkResult(d->m_trkReadQueue, d->m_serialFrame);
-    if (!len) {
-        logMessage("Partial message: " + stringFromArray(d->m_trkReadQueue));
-        return;
-    }
-#else
-    const int size = bytesAvailable(d->m_file.handle());
-    if (!size)
-        return;
-    const QByteArray data = d->m_file.read(size);
-    d->m_trkReadQueue.append(data);
-    const ushort len = isValidTrkResult(d->m_trkReadQueue, d->m_serialFrame);
-    if (!len) {
-        if (d->m_trkReadQueue.size() > 10) {
-            logMessage(QString::fromLatin1("Unable to extract message from '%1' '%2'").
-                       arg(QLatin1String(d->m_trkReadQueue.toHex())).arg(QString::fromAscii(d->m_trkReadQueue)));
-        }
-        return;
-    }
-#endif // Q_OS_WIN
-    logMessage(QString::fromLatin1("READ: %1 bytes %2").arg(len).arg(stringFromArray(d->m_trkReadQueue)));
-    handleResult(extractResult(&d->m_trkReadQueue, d->m_serialFrame));
-
-    d->m_trkWriteBusy = false;
+    d->m_writtenTrkMessages.insert(msg.token, msg);
+    d->m_trkWriteBusy = true;
+    trkWriteRawMessage(msg);
 }
 
-
 void Launcher::handleResult(const TrkResult &result)
 {
+    d->m_trkWriteBusy = false;
     QByteArray prefix = "READ BUF:                                       ";
     QByteArray str = result.toString().toUtf8();
     if (result.isDebugOutput) { // handle application output
@@ -518,10 +358,13 @@ void Launcher::handleResult(const TrkResult &result)
             break;
         }
         case TrkNotifyDeleted: { // NotifyDeleted
-            logMessage(prefix + "NOTE: LIBRARY UNLOAD: " + str);
+            const ushort itemType = (unsigned char)result.data.at(1);
+            const ushort len = result.data.size() > 12 ? extractShort(result.data.data() + 10) : ushort(0);
+            const QString name = len ? QString::fromAscii(result.data.mid(13, len)) : QString();
+            logMessage(QString::fromLatin1("%1 %2 UNLOAD: %3").
+                       arg(QString::fromAscii(prefix)).arg(itemType ? QLatin1String("LIB") : QLatin1String("PROCESS")).
+                       arg(name));
             sendTrkAck(result.token);
-            const char *data = result.data.data();
-            ushort itemType = extractShort(data);
             if (itemType == 0) { // process
                 sendTrkMessage(TrkDisconnect, CB(waitForTrkFinished));
             }
diff --git a/tests/manual/trk/launcher.h b/tests/manual/trk/launcher.h
index 27af42425e1..d37aab16bb6 100644
--- a/tests/manual/trk/launcher.h
+++ b/tests/manual/trk/launcher.h
@@ -53,6 +53,7 @@ public:
     bool startServer(QString *errorMessage);
     void setVerbose(int v);    
     void setSerialFrame(bool b);
+    bool serialFrame() const;
 
 signals:
     void copyingStarted();
@@ -65,8 +66,10 @@ signals:
 public slots:
     void terminate();
 
+private slots:
+    void handleResult(const TrkResult &data);
+
 private:
-    bool openTrkPort(const QString &port, QString *errorMessage); // or server name for local server
     void sendTrkMessage(unsigned char code,
         TrkCallBack callBack = 0,
         const QByteArray &data = QByteArray(),
@@ -77,6 +80,7 @@ private:
     void tryTrkRead();
     // actually writes a message to the device
     void trkWrite(const TrkMessage &msg);
+    void trkWriteRawMessage(const TrkMessage &msg);
     // convienience messages
     void sendTrkInitialPing();
     void sendTrkAck(unsigned char token);
@@ -99,8 +103,6 @@ private:
     void waitForTrkFinished(const TrkResult &data);
 
     void handleAndReportCreateProcess(const TrkResult &result);
-    void handleResult(const TrkResult &data);
-
     void copyFileToRemote();
     void installRemotePackageSilently(const QString &filename);
     void installAndRun();
diff --git a/tests/manual/trk/trkdevice.cpp b/tests/manual/trk/trkdevice.cpp
new file mode 100644
index 00000000000..a045fa41027
--- /dev/null
+++ b/tests/manual/trk/trkdevice.cpp
@@ -0,0 +1,323 @@
+/**************************************************************************
+**
+** 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 "trkdevice.h"
+#include "trkutils.h"
+
+#include <QtCore/QString>
+#include <QtCore/QDebug>
+
+#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)){
+        qDebug() << "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 {
+
+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;
+};
+
+TrkDevicePrivate::TrkDevicePrivate() :
+#ifdef Q_OS_WIN
+    hdevice(INVALID_HANDLE_VALUE),
+#endif
+    trkWriteBusy(false),
+    timerId(-1),
+    serialFrame(true),
+    verbose(false)
+{
+
+}
+
+TrkDevice::TrkDevice(QObject *parent) :
+    QObject(parent),
+    d(new TrkDevicePrivate)
+{
+}
+
+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;
+}
+
+void TrkDevice::close()
+{
+    if (!isOpen())
+        return;
+    if (d->timerId != -1) {
+        killTimer(d->timerId);
+        d->timerId = -1;
+    }
+#ifdef Q_OS_WIN
+    CloseHandle(d->hdevice);
+#else
+    d->file.close();
+#endif
+    if (d->verbose)
+        qDebug() << "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 d->verbose;
+}
+
+void TrkDevice::setVerbose(bool b)
+{
+    d->verbose = b;
+}
+
+bool TrkDevice::write(const QByteArray &data, QString *errorMessage)
+{
+    if (d->verbose)
+        qDebug() << ">WRITE" << data.toHex();
+#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 (d->verbose && totalCharsRead)
+        qDebug() << "Read" << d->trkReadBuffer.toHex();
+    if (!totalCharsRead)
+        return;
+    const ushort len = isValidTrkResult(d->trkReadBuffer, d->serialFrame);
+    if (!len) {
+        d->errorString = QString::fromLatin1("Partial message: %1").arg(stringFromArray(d->trkReadBuffer));
+        qWarning("%s\n", qPrintable(d->errorString));
+        return;
+    }
+#else
+    const int size = bytesAvailable(d->file.handle());
+    if (!size)
+        return;
+    const QByteArray data = d->file.read(size);
+    d->trkReadBuffer.append(data);
+    const ushort len = isValidTrkResult(d->trkReadBuffer, d->serialFrame);
+    if (!len) {
+        if (d->trkReadBuffer.size() > 10) {
+            d->errorString = QString::fromLatin1("Unable to extract message from '%1' '%2'").
+                             arg(QLatin1String(d->trkReadBuffer.toHex())).arg(QString::fromAscii(d->trkReadBuffer));
+            qWarning("%s\n", qPrintable(d->errorString));
+        }
+        return;
+    }
+#endif // Q_OS_WIN
+    TrkResult r;
+    while (extractResult(&d->trkReadBuffer, d->serialFrame, &r)) {
+        if (d->verbose)
+            qDebug() << "Read TrkResult " << r.data.toHex();
+        emit messageReceived(r);
+    }
+}
+
+void TrkDevice::timerEvent(QTimerEvent *)
+{
+    tryTrkRead();
+}
+
+} // namespace trk
diff --git a/tests/manual/trk/trkdevice.h b/tests/manual/trk/trkdevice.h
new file mode 100644
index 00000000000..7392c8a62c3
--- /dev/null
+++ b/tests/manual/trk/trkdevice.h
@@ -0,0 +1,84 @@
+/**************************************************************************
+**
+** 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 <QtCore/QObject>
+#include <QtCore/QVariant>
+#include <QtCore/QByteArray>
+
+namespace trk {
+
+struct TrkResult;
+struct TrkDevicePrivate;
+
+/* 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. */
+
+class TrkDevice : public QObject
+{
+    Q_DISABLE_COPY(TrkDevice)
+    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);
+
+signals:
+    void messageReceived(const TrkResult&);
+
+private:
+    void tryTrkRead();
+    virtual void timerEvent(QTimerEvent *ev);
+
+    TrkDevicePrivate *d;
+};
+
+} // namespace trk
+
+#endif // TRKDEVICE_H
diff --git a/tests/manual/trk/trklauncher.pri b/tests/manual/trk/trklauncher.pri
index b39ec0a816c..8824c4a5c40 100644
--- a/tests/manual/trk/trklauncher.pri
+++ b/tests/manual/trk/trklauncher.pri
@@ -1,6 +1,8 @@
 DEFINES += DEBUG_TRK=0
 INCLUDEPATH *= $$PWD
 SOURCES += $$PWD/launcher.cpp \
-    $$PWD/trkutils.cpp
+    $$PWD/trkutils.cpp \
+    $$PWD/trkdevice.cpp
 HEADERS += $$PWD/trkutils.h \
+    $$PWD/trkdevice.h \
     $$PWD/launcher.h
diff --git a/tests/manual/trk/trkserver.cpp b/tests/manual/trk/trkserver.cpp
index e764562c3c9..aa3b5534161 100644
--- a/tests/manual/trk/trkserver.cpp
+++ b/tests/manual/trk/trkserver.cpp
@@ -298,8 +298,11 @@ void TrkServer::readFromAdapter()
     if (packet != m_adapterReadBuffer)
         logMessage("buffer: " + stringFromArray(m_adapterReadBuffer));
 
-    while (!m_adapterReadBuffer.isEmpty())
-        handleAdapterMessage(extractResult(&m_adapterReadBuffer, m_serialFrame));
+    while (!m_adapterReadBuffer.isEmpty()) {
+        TrkResult r;
+        while (extractResult(&m_adapterReadBuffer, m_serialFrame, &r))
+            handleAdapterMessage(r);
+    }
 }
 
 void TrkServer::writeToAdapter(byte command, byte token, const QByteArray &data)
diff --git a/tests/manual/trk/trkutils.cpp b/tests/manual/trk/trkutils.cpp
index 1709cf31b3c..8b5622ed9fa 100644
--- a/tests/manual/trk/trkutils.cpp
+++ b/tests/manual/trk/trkutils.cpp
@@ -50,6 +50,14 @@ TrkResult::TrkResult() :
 {
 }
 
+void TrkResult::clear()
+{
+    code = token= 0;
+    isDebugOutput = false;
+    data.clear();
+    cookie = QVariant();
+}
+
 QString TrkResult::toString() const
 {
     QString res = stringFromByte(code) + "[" + stringFromByte(token);
@@ -114,20 +122,20 @@ ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame)
     return firstDelimiterPos != -1 ? firstDelimiterPos : buffer.size();
 }
 
-TrkResult extractResult(QByteArray *buffer, bool serialFrame)
+bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result)
 {
-    TrkResult result;
+    result->clear();
     const ushort len = isValidTrkResult(*buffer, serialFrame);
     if (!len)
-        return result;
+        return false;
     // handle receiving application output, which is not a regular command
     const int delimiterPos = serialFrame ? 4 : 0;
     if (buffer->at(delimiterPos) != 0x7e) {
-        result.isDebugOutput = true;
-        result.data = buffer->mid(delimiterPos, len);
-        result.data.replace("\r\n", "\n");
+        result->isDebugOutput = true;
+        result->data = buffer->mid(delimiterPos, len);
+        result->data.replace("\r\n", "\n");
         *buffer->remove(0, delimiterPos + len);
-        return result;
+        return true;
     }
     // FIXME: what happens if the length contains 0xfe?
     // Assume for now that it passes unencoded!
@@ -140,14 +148,14 @@ TrkResult extractResult(QByteArray *buffer, bool serialFrame)
     if (sum != 0xff)
         logMessage("*** CHECKSUM ERROR: " << byte(sum));
 
-    result.code = data.at(0);
-    result.token = data.at(1);
-    result.data = data.mid(2, data.size() - 3);
+    result->code = data.at(0);
+    result->token = data.at(1);
+    result->data = data.mid(2, data.size() - 3);
     //logMessage("   REST BUF: " << stringFromArray(*buffer));
     //logMessage("   CURR DATA: " << stringFromArray(data));
     //QByteArray prefix = "READ BUF:                                       ";
     //logMessage((prefix + "HEADER: " + stringFromArray(header).toLatin1()).data());
-    return result;
+    return true;
 }
 
 ushort extractShort(const char *data)
diff --git a/tests/manual/trk/trkutils.h b/tests/manual/trk/trkutils.h
index ce5baa79d4c..77e469b8068 100644
--- a/tests/manual/trk/trkutils.h
+++ b/tests/manual/trk/trkutils.h
@@ -172,6 +172,7 @@ struct Breakpoint
 struct TrkResult
 {
     TrkResult();
+    void clear();
     QString toString() const;
     // 0 for no error.
     int errorCode() const;
@@ -187,7 +188,7 @@ struct TrkResult
 // the serial frame [0x01 0x90 <len>] and 0x7e encoded7d(ba) 0x7e
 QByteArray frameMessage(byte command, byte token, const QByteArray &data, bool serialFrame);
 ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame);
-TrkResult extractResult(QByteArray *buffer, bool serialFrame);
+bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *r);
 QByteArray errorMessage(byte code);
 QByteArray hexNumber(uint n, int digits = 0);
 uint swapEndian(uint in);
-- 
GitLab