tools/runonphone/symbianutils/trkdevice.cpp
changeset 7 f7bc934e204c
child 30 5dc02b23752f
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the tools applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "trkdevice.h"
       
    43 #include "trkutils.h"
       
    44 #include "trkutils_p.h"
       
    45 
       
    46 #include <QtCore/QString>
       
    47 #include <QtCore/QDebug>
       
    48 #include <QtCore/QQueue>
       
    49 #include <QtCore/QHash>
       
    50 #include <QtCore/QMap>
       
    51 #include <QtCore/QThread>
       
    52 #include <QtCore/QMutex>
       
    53 #include <QtCore/QWaitCondition>
       
    54 #include <QtCore/QSharedPointer>
       
    55 #include <QtCore/QMetaType>
       
    56 
       
    57 #ifdef Q_OS_WIN
       
    58 #  include <windows.h>
       
    59 #else
       
    60 #  include <QtCore/QFile>
       
    61 
       
    62 #  include <stdio.h>
       
    63 #  include <sys/ioctl.h>
       
    64 #  include <sys/types.h>
       
    65 #  include <termios.h>
       
    66 #  include <errno.h>
       
    67 #  include <string.h>
       
    68 #  include <unistd.h>
       
    69 /* Required headers for select() according to POSIX.1-2001 */
       
    70 #  include <sys/select.h>
       
    71 /* Required headers for select() according to earlier standards:
       
    72        #include <sys/time.h>
       
    73        #include <sys/types.h>
       
    74        #include <unistd.h>
       
    75 */
       
    76 #endif
       
    77 
       
    78 #ifdef Q_OS_WIN
       
    79 
       
    80 // Format windows error from GetLastError() value:
       
    81 // TODO: Use the one provided by the utils lib.
       
    82 QString winErrorMessage(unsigned long error)
       
    83 {
       
    84     QString rc = QString::fromLatin1("#%1: ").arg(error);
       
    85     ushort *lpMsgBuf;
       
    86 
       
    87     const int len = FormatMessage(
       
    88             FORMAT_MESSAGE_ALLOCATE_BUFFER
       
    89                 | FORMAT_MESSAGE_FROM_SYSTEM
       
    90                 | FORMAT_MESSAGE_IGNORE_INSERTS,
       
    91             NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
       
    92     if (len) {
       
    93         rc = QString::fromUtf16(lpMsgBuf, len);
       
    94         LocalFree(lpMsgBuf);
       
    95     } else {
       
    96         rc += QString::fromLatin1("<unknown error>");
       
    97     }
       
    98     return rc;
       
    99 }
       
   100 
       
   101 #endif
       
   102 
       
   103 enum { verboseTrk = 0 };
       
   104 
       
   105 namespace trk {
       
   106 
       
   107 ///////////////////////////////////////////////////////////////////////
       
   108 //
       
   109 // TrkMessage
       
   110 //
       
   111 ///////////////////////////////////////////////////////////////////////
       
   112 
       
   113 /* A message to be send to TRK, triggering a callback on receipt
       
   114  * of the answer. */
       
   115 struct TrkMessage
       
   116 {
       
   117     explicit TrkMessage(byte code = 0u, byte token = 0u,
       
   118                         TrkCallback callback = TrkCallback());
       
   119 
       
   120     byte code;
       
   121     byte token;
       
   122     QByteArray data;
       
   123     QVariant cookie;
       
   124     TrkCallback callback;
       
   125 };
       
   126 
       
   127 TrkMessage::TrkMessage(byte c, byte t, TrkCallback cb) :
       
   128     code(c),
       
   129     token(t),
       
   130     callback(cb)
       
   131 {
       
   132 }
       
   133 
       
   134 QDebug operator<<(QDebug d, const TrkMessage &msg)
       
   135 {
       
   136     return d << "Message: Code: " << msg.code
       
   137         << " Token: " << msg.token << " " << msg.data.toHex();
       
   138 }
       
   139 
       
   140 } // namespace trk
       
   141 
       
   142 Q_DECLARE_METATYPE(trk::TrkMessage)
       
   143 Q_DECLARE_METATYPE(trk::TrkResult)
       
   144 
       
   145 namespace trk {
       
   146 
       
   147 ///////////////////////////////////////////////////////////////////////
       
   148 //
       
   149 // TrkWriteQueue: Mixin class that manages a write queue of Trk messages.
       
   150 // pendingMessage()/notifyWriteResult() should be called from a worked/timer
       
   151 // that writes the messages. The class does not take precautions for multithreading.
       
   152 // A no-op message is simply taken off the queue. The calling class
       
   153 // can use the helper invokeNoopMessage() to trigger its callback.
       
   154 //
       
   155 ///////////////////////////////////////////////////////////////////////
       
   156 
       
   157 class TrkWriteQueue
       
   158 {    
       
   159     Q_DISABLE_COPY(TrkWriteQueue)
       
   160 public:
       
   161     explicit TrkWriteQueue();
       
   162 
       
   163     // Enqueue messages.
       
   164     void queueTrkMessage(byte code, TrkCallback callback,
       
   165                         const QByteArray &data, const QVariant &cookie);
       
   166     void queueTrkInitialPing();
       
   167 
       
   168     // Call this from the device read notification with the results.
       
   169     void slotHandleResult(const TrkResult &result, QMutex *mutex = 0);
       
   170 
       
   171     // pendingMessage() can be called periodically in a timer to retrieve
       
   172     // the pending messages to be sent.
       
   173     enum PendingMessageResult {
       
   174         NoMessage,               // No message in queue.
       
   175         PendingMessage,          /* There is a queued message. The calling class
       
   176                                   * can write it out and use notifyWriteResult()
       
   177                                   * to notify about the result. */
       
   178         NoopMessageDequeued      // A no-op message has been dequeued. see invokeNoopMessage().
       
   179     };
       
   180 
       
   181     PendingMessageResult pendingMessage(TrkMessage *message);
       
   182     // Notify the queue about the success of the write operation
       
   183     // after taking the pendingMessage off.
       
   184     enum WriteResult {
       
   185         WriteOk,
       
   186         WriteFailedDiscard, // Discard failed message
       
   187         WriteFailedKeep,    // Keep failed message
       
   188     };
       
   189     void notifyWriteResult(WriteResult ok);
       
   190 
       
   191     // Helper function that invokes the callback of a no-op message
       
   192     static void invokeNoopMessage(trk::TrkMessage);
       
   193 
       
   194 private:
       
   195     typedef QMap<byte, TrkMessage> TokenMessageMap;
       
   196 
       
   197     byte nextTrkWriteToken();
       
   198 
       
   199     byte m_trkWriteToken;
       
   200     QQueue<TrkMessage> m_trkWriteQueue;
       
   201     TokenMessageMap m_writtenTrkMessages;
       
   202     bool m_trkWriteBusy;
       
   203 };
       
   204 
       
   205 TrkWriteQueue::TrkWriteQueue() :
       
   206     m_trkWriteToken(0),
       
   207     m_trkWriteBusy(false)
       
   208 {
       
   209 }
       
   210 
       
   211 byte TrkWriteQueue::nextTrkWriteToken()
       
   212 {
       
   213     ++m_trkWriteToken;
       
   214     if (m_trkWriteToken == 0)
       
   215         ++m_trkWriteToken;
       
   216     if (verboseTrk)
       
   217         qDebug() << "Write token: " << m_trkWriteToken;
       
   218     return m_trkWriteToken;
       
   219 }
       
   220 
       
   221 void TrkWriteQueue::queueTrkMessage(byte code, TrkCallback callback,
       
   222     const QByteArray &data, const QVariant &cookie)
       
   223 {
       
   224     const byte token = code == TRK_WRITE_QUEUE_NOOP_CODE ?
       
   225                                 byte(0) : nextTrkWriteToken();
       
   226     TrkMessage msg(code, token, callback);
       
   227     msg.data = data;
       
   228     msg.cookie = cookie;
       
   229     m_trkWriteQueue.append(msg);
       
   230 }
       
   231 
       
   232 TrkWriteQueue::PendingMessageResult TrkWriteQueue::pendingMessage(TrkMessage *message)
       
   233 {
       
   234     // Invoked from timer, try to flush out message queue
       
   235     if (m_trkWriteBusy || m_trkWriteQueue.isEmpty())
       
   236         return NoMessage;
       
   237     // Handle the noop message, just invoke CB in slot (ower thread)
       
   238     if (m_trkWriteQueue.front().code == TRK_WRITE_QUEUE_NOOP_CODE) {
       
   239         *message = m_trkWriteQueue.dequeue();
       
   240         return NoopMessageDequeued;
       
   241     }
       
   242     // Insert into map fir answers (as reading threads might get an
       
   243     // answer before notifyWriteResult(true)) is called.
       
   244     *message = m_trkWriteQueue.front();
       
   245     m_writtenTrkMessages.insert(message->token, *message);
       
   246     m_trkWriteBusy = true;
       
   247     return PendingMessage;
       
   248 }
       
   249 
       
   250 void TrkWriteQueue::invokeNoopMessage(trk::TrkMessage noopMessage)
       
   251 {
       
   252     TrkResult result;
       
   253     result.code = noopMessage.code;
       
   254     result.token = noopMessage.token;
       
   255     result.data = noopMessage.data;
       
   256     result.cookie = noopMessage.cookie;
       
   257     noopMessage.callback(result);
       
   258 }
       
   259 
       
   260 void TrkWriteQueue::notifyWriteResult(WriteResult wr)
       
   261 {
       
   262     // On success, dequeue message and await result
       
   263     const byte token = m_trkWriteQueue.front().token;
       
   264     switch (wr) {
       
   265     case WriteOk:
       
   266         m_trkWriteQueue.dequeue();
       
   267         break;
       
   268     case WriteFailedKeep:
       
   269     case WriteFailedDiscard:
       
   270         m_writtenTrkMessages.remove(token);
       
   271         m_trkWriteBusy = false;
       
   272         if (wr == WriteFailedDiscard)
       
   273             m_trkWriteQueue.dequeue();
       
   274         break;
       
   275     }
       
   276 }
       
   277 
       
   278 void TrkWriteQueue::slotHandleResult(const TrkResult &result, QMutex *mutex)
       
   279 {
       
   280     // Find which request the message belongs to and invoke callback
       
   281     // if ACK or on NAK if desired.
       
   282     if (mutex)
       
   283         mutex->lock();
       
   284     m_trkWriteBusy = false;
       
   285     const TokenMessageMap::iterator it = m_writtenTrkMessages.find(result.token);
       
   286     if (it == m_writtenTrkMessages.end()) {
       
   287         if (mutex)
       
   288             mutex->unlock();
       
   289         return;
       
   290     }
       
   291     TrkCallback callback = it.value().callback;
       
   292     const QVariant cookie = it.value().cookie;
       
   293     m_writtenTrkMessages.erase(it);
       
   294     if (mutex)
       
   295         mutex->unlock();
       
   296     // Invoke callback
       
   297     if (callback) {
       
   298         TrkResult result1 = result;
       
   299         result1.cookie = cookie;
       
   300         callback(result1);
       
   301     }
       
   302 }
       
   303 
       
   304 void TrkWriteQueue::queueTrkInitialPing()
       
   305 {
       
   306     // Ping, reset sequence count
       
   307     m_trkWriteToken = 0;
       
   308     m_trkWriteQueue.append(TrkMessage(TrkPing, 0));
       
   309 }
       
   310 
       
   311 ///////////////////////////////////////////////////////////////////////
       
   312 //
       
   313 // DeviceContext to be shared between threads
       
   314 //
       
   315 ///////////////////////////////////////////////////////////////////////
       
   316 
       
   317 struct DeviceContext {
       
   318     DeviceContext();
       
   319 #ifdef Q_OS_WIN
       
   320     HANDLE device;
       
   321     OVERLAPPED readOverlapped;
       
   322     OVERLAPPED writeOverlapped;
       
   323 #else
       
   324     QFile file;
       
   325 #endif
       
   326     bool serialFrame;
       
   327     QMutex mutex;
       
   328 };
       
   329 
       
   330 DeviceContext::DeviceContext() :
       
   331 #ifdef Q_OS_WIN
       
   332     device(INVALID_HANDLE_VALUE),
       
   333 #endif
       
   334     serialFrame(true)
       
   335 {
       
   336 }
       
   337 
       
   338 ///////////////////////////////////////////////////////////////////////
       
   339 //
       
   340 // TrkWriterThread: A thread operating a TrkWriteQueue.
       
   341 // with exception of the handling of the  TRK_WRITE_QUEUE_NOOP_CODE
       
   342 // synchronization message. The invocation of the callback is then
       
   343 // done by the thread owning the TrkWriteQueue, while pendingMessage() is called
       
   344 // from another thread. This happens via a Qt::BlockingQueuedConnection.
       
   345 
       
   346 ///////////////////////////////////////////////////////////////////////
       
   347 
       
   348 class WriterThread : public QThread
       
   349 {
       
   350     Q_OBJECT
       
   351     Q_DISABLE_COPY(WriterThread)
       
   352 public:            
       
   353     explicit WriterThread(const QSharedPointer<DeviceContext> &context);
       
   354 
       
   355     // Enqueue messages.
       
   356     void queueTrkMessage(byte code, TrkCallback callback,
       
   357                         const QByteArray &data, const QVariant &cookie);
       
   358     void queueTrkInitialPing();
       
   359 
       
   360     // Call this from the device read notification with the results.
       
   361     void slotHandleResult(const TrkResult &result);
       
   362 
       
   363     virtual void run();
       
   364 
       
   365 signals:
       
   366     void error(const QString &);
       
   367     void internalNoopMessageDequeued(const trk::TrkMessage&);
       
   368 
       
   369 public slots:
       
   370     bool trkWriteRawMessage(const TrkMessage &msg);
       
   371     void terminate();
       
   372     void tryWrite();
       
   373 
       
   374 private slots:
       
   375     void invokeNoopMessage(const trk::TrkMessage &);
       
   376 
       
   377 private:    
       
   378     bool write(const QByteArray &data, QString *errorMessage);
       
   379     inline int writePendingMessage();
       
   380 
       
   381     const QSharedPointer<DeviceContext> m_context;
       
   382     QMutex m_dataMutex;
       
   383     QMutex m_waitMutex;
       
   384     QWaitCondition m_waitCondition;
       
   385     TrkWriteQueue m_queue;
       
   386     bool m_terminate;
       
   387 };
       
   388 
       
   389 WriterThread::WriterThread(const QSharedPointer<DeviceContext> &context) :
       
   390     m_context(context),
       
   391     m_terminate(false)
       
   392 {
       
   393     static const int trkMessageMetaId = qRegisterMetaType<trk::TrkMessage>();
       
   394     Q_UNUSED(trkMessageMetaId)
       
   395     connect(this, SIGNAL(internalNoopMessageDequeued(trk::TrkMessage)),
       
   396             this, SLOT(invokeNoopMessage(trk::TrkMessage)), Qt::BlockingQueuedConnection);
       
   397 }
       
   398 
       
   399 void WriterThread::run()
       
   400 {
       
   401     while (writePendingMessage() == 0) ;
       
   402 }
       
   403 
       
   404 int WriterThread::writePendingMessage()
       
   405 {
       
   406     enum { MaxAttempts = 100, RetryIntervalMS = 200 };
       
   407 
       
   408     // Wait. Use a timeout in case something is already queued before we
       
   409     // start up or some weird hanging exit condition
       
   410     m_waitMutex.lock();
       
   411     m_waitCondition.wait(&m_waitMutex, 100);
       
   412     m_waitMutex.unlock();
       
   413     if (m_terminate)
       
   414         return 1;
       
   415 
       
   416     // Send off message
       
   417     m_dataMutex.lock();
       
   418     TrkMessage message;
       
   419     const TrkWriteQueue::PendingMessageResult pr = m_queue.pendingMessage(&message);
       
   420     m_dataMutex.unlock();
       
   421 
       
   422     switch (pr) {
       
   423     case TrkWriteQueue::NoMessage:
       
   424         break;
       
   425     case TrkWriteQueue::PendingMessage: {
       
   426             //qDebug() << "Write pending message " << message;
       
   427             // Untested: try to re-send a few times
       
   428             bool success = false;
       
   429             for (int r = 0; !success && (r < MaxAttempts); r++) {
       
   430                 success = trkWriteRawMessage(message);
       
   431                 if (!success) {
       
   432                     emit error(QString::fromLatin1("Write failure, attempt %1 of %2.").arg(r).arg(int(MaxAttempts)));
       
   433                     if (m_terminate)
       
   434                         return 1;
       
   435                     QThread::msleep(RetryIntervalMS);
       
   436                 }
       
   437             }
       
   438             // Notify queue. If still failed, give up.
       
   439             m_dataMutex.lock();
       
   440             m_queue.notifyWriteResult(success ? TrkWriteQueue::WriteOk : TrkWriteQueue::WriteFailedDiscard);
       
   441             m_dataMutex.unlock();
       
   442         }
       
   443         break;
       
   444     case TrkWriteQueue::NoopMessageDequeued:
       
   445         // Sync with thread that owns us via a blocking signal
       
   446         if (verboseTrk)
       
   447             qDebug() << "Noop message dequeued" << message;
       
   448         emit internalNoopMessageDequeued(message);
       
   449         break;
       
   450     } // switch
       
   451     return 0;
       
   452 }
       
   453 
       
   454 void WriterThread::invokeNoopMessage(const trk::TrkMessage &msg)
       
   455 {
       
   456     TrkWriteQueue::invokeNoopMessage(msg);
       
   457 }
       
   458 
       
   459 void WriterThread::terminate()
       
   460 {
       
   461     m_terminate = true;
       
   462     m_waitCondition.wakeAll();
       
   463     wait();
       
   464     m_terminate = false;
       
   465 }
       
   466 
       
   467 #ifdef Q_OS_WIN
       
   468 
       
   469 static inline QString msgTerminated(int size)
       
   470 {
       
   471     return QString::fromLatin1("Terminated with %1 bytes pending.").arg(size);
       
   472 }
       
   473 
       
   474 // Interruptible synchronous write function.
       
   475 static inline bool overlappedSyncWrite(HANDLE file,
       
   476                                        const bool &terminateFlag,
       
   477                                        const char *data,
       
   478                                        DWORD size, DWORD *charsWritten,
       
   479                                        OVERLAPPED *overlapped,
       
   480                                        QString *errorMessage)
       
   481 {
       
   482     if (WriteFile(file, data, size, charsWritten, overlapped))
       
   483         return true;
       
   484     const DWORD writeError = GetLastError();
       
   485     if (writeError != ERROR_IO_PENDING) {
       
   486         *errorMessage = QString::fromLatin1("WriteFile failed: %1").arg(winErrorMessage(writeError));
       
   487         return false;
       
   488     }
       
   489     // Wait for written or thread terminated
       
   490     const DWORD timeoutMS = 200;
       
   491     const unsigned maxAttempts = 20;
       
   492     DWORD wr = WaitForSingleObject(overlapped->hEvent, timeoutMS);
       
   493     for (unsigned n = 0; wr == WAIT_TIMEOUT && n < maxAttempts && !terminateFlag;
       
   494          wr = WaitForSingleObject(overlapped->hEvent, timeoutMS), n++);
       
   495     if (terminateFlag) {
       
   496         *errorMessage = msgTerminated(size);
       
   497         return false;
       
   498     }
       
   499     switch (wr) {
       
   500     case WAIT_OBJECT_0:
       
   501         break;
       
   502     case WAIT_TIMEOUT:
       
   503         *errorMessage = QString::fromLatin1("Write timed out.");
       
   504         return false;
       
   505     default:
       
   506         *errorMessage = QString::fromLatin1("Error while waiting for WriteFile results: %1").arg(winErrorMessage(GetLastError()));
       
   507         return false;
       
   508     }
       
   509     if (!GetOverlappedResult(file, overlapped, charsWritten, TRUE)) {
       
   510         *errorMessage = QString::fromLatin1("Error writing %1 bytes: %2").arg(size).arg(winErrorMessage(GetLastError()));
       
   511         return false;
       
   512     }
       
   513     return true;
       
   514 }
       
   515 #endif
       
   516 
       
   517 bool WriterThread::write(const QByteArray &data, QString *errorMessage)
       
   518 {
       
   519     if (verboseTrk)
       
   520         qDebug() << "Write raw data: " << stringFromArray(data).toLatin1();
       
   521     QMutexLocker locker(&m_context->mutex);
       
   522 #ifdef Q_OS_WIN
       
   523     DWORD charsWritten;
       
   524     if (!overlappedSyncWrite(m_context->device, m_terminate, data.data(), data.size(), &charsWritten, &m_context->writeOverlapped, errorMessage)) {
       
   525         return false;
       
   526     }
       
   527     FlushFileBuffers(m_context->device);
       
   528     return true;
       
   529 #else
       
   530     if (m_context->file.write(data) == -1 || !m_context->file.flush()) {
       
   531         *errorMessage = QString::fromLatin1("Cannot write: %1").arg(m_context->file.errorString());
       
   532         return false;
       
   533     }
       
   534     return  true;
       
   535 #endif
       
   536 }
       
   537 
       
   538 bool WriterThread::trkWriteRawMessage(const TrkMessage &msg)
       
   539 {
       
   540     const QByteArray ba = frameMessage(msg.code, msg.token, msg.data, m_context->serialFrame);
       
   541     QString errorMessage;
       
   542     const bool rc = write(ba, &errorMessage);
       
   543     if (!rc) {
       
   544         qWarning("%s\n", qPrintable(errorMessage));
       
   545         emit error(errorMessage);
       
   546     }
       
   547     return rc;
       
   548 }
       
   549 
       
   550 void WriterThread::tryWrite()
       
   551 {
       
   552     m_waitCondition.wakeAll();
       
   553 }
       
   554 
       
   555 void WriterThread::queueTrkMessage(byte code, TrkCallback callback,
       
   556                                    const QByteArray &data, const QVariant &cookie)
       
   557 {
       
   558     m_dataMutex.lock();
       
   559     m_queue.queueTrkMessage(code, callback, data, cookie);
       
   560     m_dataMutex.unlock();
       
   561     tryWrite();
       
   562 }
       
   563 
       
   564 void WriterThread::queueTrkInitialPing()
       
   565 {
       
   566     m_dataMutex.lock();
       
   567     m_queue.queueTrkInitialPing();
       
   568     m_dataMutex.unlock();
       
   569     tryWrite();
       
   570 }
       
   571 
       
   572 // Call this from the device read notification with the results.
       
   573 void WriterThread::slotHandleResult(const TrkResult &result)
       
   574 {
       
   575     m_queue.slotHandleResult(result, &m_dataMutex);
       
   576     tryWrite(); // Have messages been enqueued in-between?
       
   577 }
       
   578 
       
   579 
       
   580 ///////////////////////////////////////////////////////////////////////
       
   581 //
       
   582 // ReaderThreadBase: Base class for a thread that reads data from
       
   583 // the device, decodes the messages and emit signals for the messages.
       
   584 // A Qt::BlockingQueuedConnection should be used for the message signal
       
   585 // to ensure messages are processed in the correct sequence.
       
   586 //
       
   587 ///////////////////////////////////////////////////////////////////////
       
   588 
       
   589 class ReaderThreadBase : public QThread
       
   590 {
       
   591     Q_OBJECT
       
   592     Q_DISABLE_COPY(ReaderThreadBase)
       
   593 public:
       
   594 
       
   595 signals:
       
   596     void messageReceived(const trk::TrkResult &result, const QByteArray &rawData);
       
   597 
       
   598 protected:
       
   599     explicit ReaderThreadBase(const QSharedPointer<DeviceContext> &context);
       
   600     void processData(const QByteArray &a);
       
   601     void processData(char c);
       
   602 
       
   603     const QSharedPointer<DeviceContext> m_context;
       
   604 
       
   605 private:
       
   606     void readMessages();
       
   607 
       
   608     QByteArray m_trkReadBuffer;
       
   609 };
       
   610 
       
   611 ReaderThreadBase::ReaderThreadBase(const QSharedPointer<DeviceContext> &context) :
       
   612     m_context(context)
       
   613 {
       
   614     static const int trkResultMetaId = qRegisterMetaType<trk::TrkResult>();
       
   615     Q_UNUSED(trkResultMetaId)
       
   616 }
       
   617 
       
   618 void ReaderThreadBase::processData(const QByteArray &a)
       
   619 {
       
   620     m_trkReadBuffer += a;
       
   621     readMessages();
       
   622 }
       
   623 
       
   624 void ReaderThreadBase::processData(char c)
       
   625 {
       
   626     m_trkReadBuffer += c;
       
   627     if (m_trkReadBuffer.size() > 1)
       
   628         readMessages();
       
   629 }
       
   630 
       
   631 void ReaderThreadBase::readMessages()
       
   632 {
       
   633     TrkResult r;
       
   634     QByteArray rawData;
       
   635     while (extractResult(&m_trkReadBuffer, m_context->serialFrame, &r, &rawData)) {
       
   636         emit messageReceived(r, rawData);
       
   637     }
       
   638 }
       
   639 
       
   640 #ifdef Q_OS_WIN
       
   641 ///////////////////////////////////////////////////////////////////////
       
   642 //
       
   643 // WinReaderThread: A thread reading from the device using Windows API.
       
   644 // Waits on an overlapped I/O handle and an event that tells the thread to
       
   645 // terminate.
       
   646 //
       
   647 ///////////////////////////////////////////////////////////////////////
       
   648 
       
   649 class WinReaderThread : public ReaderThreadBase
       
   650 {
       
   651     Q_OBJECT
       
   652     Q_DISABLE_COPY(WinReaderThread)
       
   653 public:
       
   654     explicit WinReaderThread(const QSharedPointer<DeviceContext> &context);
       
   655     ~WinReaderThread();
       
   656 
       
   657     virtual void run();
       
   658 
       
   659 signals:
       
   660     void error(const QString &);
       
   661 
       
   662 public slots:
       
   663     void terminate();
       
   664 
       
   665 private:
       
   666     enum Handles { FileHandle, TerminateEventHandle, HandleCount };
       
   667 
       
   668     inline int tryRead();
       
   669 
       
   670     HANDLE m_handles[HandleCount];
       
   671 };
       
   672 
       
   673 WinReaderThread::WinReaderThread(const QSharedPointer<DeviceContext> &context) :
       
   674     ReaderThreadBase(context)
       
   675 {
       
   676     m_handles[FileHandle] = NULL;
       
   677     m_handles[TerminateEventHandle] = CreateEvent(NULL, FALSE, FALSE, NULL);
       
   678 }
       
   679 
       
   680 WinReaderThread::~WinReaderThread()
       
   681 {
       
   682     CloseHandle(m_handles[TerminateEventHandle]);
       
   683 }
       
   684 
       
   685 // Return 0 to continue or error code
       
   686 int WinReaderThread::tryRead()
       
   687 {
       
   688     enum { BufSize = 1024 };
       
   689     char buffer[BufSize];
       
   690     // Check if there are already bytes waiting. If not, wait for first byte
       
   691     COMSTAT comStat;
       
   692     if (!ClearCommError(m_context->device, NULL, &comStat)){
       
   693         emit error(QString::fromLatin1("ClearCommError failed: %1").arg(winErrorMessage(GetLastError())));
       
   694         return -7;
       
   695     }    
       
   696     const DWORD bytesToRead = qMax(DWORD(1), qMin(comStat.cbInQue, DWORD(BufSize)));
       
   697     // Trigger read
       
   698     DWORD bytesRead = 0;
       
   699     if (ReadFile(m_context->device, &buffer, bytesToRead, &bytesRead, &m_context->readOverlapped)) {
       
   700         if (bytesRead == 1) {
       
   701             processData(buffer[0]);
       
   702         } else {
       
   703             processData(QByteArray(buffer, bytesRead));
       
   704         }
       
   705         return 0;
       
   706     }
       
   707     const DWORD readError = GetLastError();
       
   708     if (readError != ERROR_IO_PENDING) {
       
   709         emit error(QString::fromLatin1("Read error: %1").arg(winErrorMessage(readError)));
       
   710         return -1;
       
   711     }    
       
   712     // Wait for either termination or data
       
   713     const DWORD wr = WaitForMultipleObjects(HandleCount, m_handles, false, INFINITE);
       
   714     if (wr == WAIT_FAILED) {
       
   715         emit error(QString::fromLatin1("Wait failed: %1").arg(winErrorMessage(GetLastError())));
       
   716         return -2;
       
   717     }
       
   718     if (wr - WAIT_OBJECT_0 == TerminateEventHandle) {
       
   719         return 1; // Terminate
       
   720     }
       
   721     // Check data
       
   722     if (!GetOverlappedResult(m_context->device, &m_context->readOverlapped, &bytesRead, true)) {
       
   723         emit error(QString::fromLatin1("GetOverlappedResult failed: %1").arg(winErrorMessage(GetLastError())));
       
   724         return -3;
       
   725     }
       
   726     if (bytesRead == 1) {
       
   727         processData(buffer[0]);
       
   728     } else {
       
   729         processData(QByteArray(buffer, bytesRead));
       
   730     }
       
   731     return 0;
       
   732 }
       
   733 
       
   734 void WinReaderThread::run()
       
   735 {
       
   736     m_handles[FileHandle] = m_context->readOverlapped.hEvent;
       
   737     while ( tryRead() == 0) ;
       
   738 }
       
   739 
       
   740 void WinReaderThread::terminate()
       
   741 {
       
   742     SetEvent(m_handles[TerminateEventHandle]);
       
   743     wait();
       
   744 }
       
   745 
       
   746 typedef WinReaderThread ReaderThread;
       
   747 
       
   748 #else
       
   749 
       
   750 ///////////////////////////////////////////////////////////////////////
       
   751 //
       
   752 // UnixReaderThread: A thread reading from the device.
       
   753 // Uses select() to wait and a special ioctl() to find out the number
       
   754 // of bytes queued. For clean termination, the self-pipe trick is used.
       
   755 // The class maintains a pipe, on whose read end the select waits besides
       
   756 // the device file handle. To terminate, a byte is written to the pipe.
       
   757 //
       
   758 ///////////////////////////////////////////////////////////////////////
       
   759 
       
   760 static inline QString msgUnixCallFailedErrno(const char *func, int errorNumber)
       
   761 {
       
   762     return QString::fromLatin1("Call to %1() failed: %2").arg(QLatin1String(func), QString::fromLocal8Bit(strerror(errorNumber)));
       
   763 }
       
   764 
       
   765 class UnixReaderThread : public ReaderThreadBase {
       
   766     Q_OBJECT
       
   767     Q_DISABLE_COPY(UnixReaderThread)
       
   768 public:
       
   769     explicit UnixReaderThread(const QSharedPointer<DeviceContext> &context);
       
   770     ~UnixReaderThread();
       
   771 
       
   772     virtual void run();
       
   773 
       
   774 signals:
       
   775     void error(const QString &);
       
   776 
       
   777 public slots:
       
   778     void terminate();
       
   779 
       
   780 private:
       
   781     inline int tryRead();
       
   782 
       
   783     int m_terminatePipeFileDescriptors[2];
       
   784 };
       
   785 
       
   786 UnixReaderThread::UnixReaderThread(const QSharedPointer<DeviceContext> &context) : 
       
   787     ReaderThreadBase(context)
       
   788 {
       
   789     m_terminatePipeFileDescriptors[0] = m_terminatePipeFileDescriptors[1] = -1;
       
   790     // Set up pipes for termination. Should not fail
       
   791     if (pipe(m_terminatePipeFileDescriptors) < 0)
       
   792         qWarning("%s\n", qPrintable(msgUnixCallFailedErrno("pipe", errno)));
       
   793 }
       
   794 
       
   795 UnixReaderThread::~UnixReaderThread()
       
   796 {
       
   797     close(m_terminatePipeFileDescriptors[0]);
       
   798     close(m_terminatePipeFileDescriptors[1]);
       
   799 }
       
   800 
       
   801 int UnixReaderThread::tryRead()
       
   802 {
       
   803     fd_set readSet, tempReadSet, tempExceptionSet;
       
   804     struct timeval timeOut;
       
   805     const int fileDescriptor = m_context->file.handle();
       
   806     FD_ZERO(&readSet);
       
   807     FD_SET(fileDescriptor, &readSet);
       
   808     FD_SET(m_terminatePipeFileDescriptors[0], &readSet);
       
   809     const int maxFileDescriptor = qMax(m_terminatePipeFileDescriptors[0], fileDescriptor);
       
   810     int result = 0;
       
   811     do {
       
   812         memcpy(&tempReadSet, &readSet, sizeof(fd_set));
       
   813         memcpy(&tempExceptionSet, &readSet, sizeof(fd_set));
       
   814         timeOut.tv_sec = 1;
       
   815         timeOut.tv_usec = 0;
       
   816         result = select(maxFileDescriptor + 1, &tempReadSet, NULL, &tempExceptionSet, &timeOut);
       
   817     } while ( result < 0 && errno == EINTR );
       
   818     // Timeout?
       
   819     if (result == 0)
       
   820         return 0;
       
   821    // Something wrong?
       
   822     if (result < 0) {
       
   823         emit error(msgUnixCallFailedErrno("select", errno));
       
   824         return -1;
       
   825     }
       
   826     // Did the exception set trigger on the device?
       
   827     if (FD_ISSET(fileDescriptor,&tempExceptionSet)) {
       
   828         emit error(QLatin1String("An Exception occurred on the device."));
       
   829         return -2;
       
   830     }
       
   831     // Check termination pipe.
       
   832     if (FD_ISSET(m_terminatePipeFileDescriptors[0], &tempReadSet)
       
   833         || FD_ISSET(m_terminatePipeFileDescriptors[0], &tempExceptionSet))
       
   834         return 1;
       
   835 
       
   836     // determine number of pending bytes and read
       
   837     int numBytes;
       
   838     if (ioctl(fileDescriptor, FIONREAD, &numBytes) < 0) {
       
   839         emit error(msgUnixCallFailedErrno("ioctl", errno));
       
   840         return -1;
       
   841     }
       
   842     m_context->mutex.lock();
       
   843     const QByteArray data = m_context->file.read(numBytes);
       
   844     m_context->mutex.unlock();
       
   845     processData(data);
       
   846     return 0;
       
   847 }
       
   848 
       
   849 void UnixReaderThread::run()
       
   850 {
       
   851     // Read loop
       
   852     while (tryRead() == 0)
       
   853         ;
       
   854 }
       
   855 
       
   856 void UnixReaderThread::terminate()
       
   857 {
       
   858     // Trigger select() by writing to the pipe
       
   859     char c = 0;
       
   860     const int written = write(m_terminatePipeFileDescriptors[1], &c, 1);
       
   861     Q_UNUSED(written)
       
   862     wait();
       
   863 }
       
   864 
       
   865 typedef UnixReaderThread ReaderThread;
       
   866 
       
   867 #endif
       
   868 
       
   869 ///////////////////////////////////////////////////////////////////////
       
   870 //
       
   871 // TrkDevicePrivate
       
   872 //
       
   873 ///////////////////////////////////////////////////////////////////////
       
   874 
       
   875 struct TrkDevicePrivate
       
   876 {
       
   877     TrkDevicePrivate();
       
   878 
       
   879     QSharedPointer<DeviceContext> deviceContext;
       
   880     QSharedPointer<WriterThread> writerThread;
       
   881     QSharedPointer<ReaderThread> readerThread;
       
   882 
       
   883     QByteArray trkReadBuffer;
       
   884     int verbose;
       
   885     QString errorString;
       
   886     QString port;
       
   887 };
       
   888 
       
   889 ///////////////////////////////////////////////////////////////////////
       
   890 //
       
   891 // TrkDevice
       
   892 //
       
   893 ///////////////////////////////////////////////////////////////////////
       
   894 
       
   895 TrkDevicePrivate::TrkDevicePrivate() :
       
   896     deviceContext(new DeviceContext),
       
   897     verbose(0)
       
   898 {
       
   899 }
       
   900 
       
   901 ///////////////////////////////////////////////////////////////////////
       
   902 //
       
   903 // TrkDevice
       
   904 //
       
   905 ///////////////////////////////////////////////////////////////////////
       
   906 
       
   907 TrkDevice::TrkDevice(QObject *parent) :
       
   908     QObject(parent),
       
   909     d(new TrkDevicePrivate)
       
   910 {}
       
   911 
       
   912 TrkDevice::~TrkDevice()
       
   913 {
       
   914     close();
       
   915     delete d;
       
   916 }
       
   917 
       
   918 bool TrkDevice::open(QString *errorMessage)
       
   919 {
       
   920     if (d->verbose)
       
   921         qDebug() << "Opening" << port() << "is open: " << isOpen() << " serialFrame=" << serialFrame();
       
   922     if (d->port.isEmpty()) {
       
   923         *errorMessage = QLatin1String("Internal error: No port set on TrkDevice");
       
   924         return false;
       
   925     }
       
   926 
       
   927     close();
       
   928 #ifdef Q_OS_WIN
       
   929     const QString fullPort = QLatin1String("\\\\.\\") + d->port;
       
   930     d->deviceContext->device = CreateFile(reinterpret_cast<const WCHAR*>(fullPort.utf16()),
       
   931                            GENERIC_READ | GENERIC_WRITE,
       
   932                            0,
       
   933                            NULL,
       
   934                            OPEN_EXISTING,
       
   935                            FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING|FILE_FLAG_OVERLAPPED,
       
   936                            NULL);
       
   937 
       
   938     if (INVALID_HANDLE_VALUE == d->deviceContext->device) {
       
   939         *errorMessage = QString::fromLatin1("Could not open device '%1': %2").arg(port(), winErrorMessage(GetLastError()));
       
   940         return false;
       
   941     }
       
   942     memset(&d->deviceContext->readOverlapped, 0, sizeof(OVERLAPPED));
       
   943     d->deviceContext->readOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
       
   944     memset(&d->deviceContext->writeOverlapped, 0, sizeof(OVERLAPPED));
       
   945     d->deviceContext->writeOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
       
   946     if (d->deviceContext->readOverlapped.hEvent == NULL || d->deviceContext->writeOverlapped.hEvent == NULL) {
       
   947         *errorMessage = QString::fromLatin1("Failed to create events: %1").arg(winErrorMessage(GetLastError()));
       
   948         return false;
       
   949     }
       
   950 #else
       
   951     d->deviceContext->file.setFileName(d->port);
       
   952     if (!d->deviceContext->file.open(QIODevice::ReadWrite|QIODevice::Unbuffered)) {
       
   953         *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(d->port, d->deviceContext->file.errorString());
       
   954         return false;
       
   955     }
       
   956 
       
   957     struct termios termInfo;
       
   958     if (tcgetattr(d->deviceContext->file.handle(), &termInfo) < 0) {
       
   959         *errorMessage = QString::fromLatin1("Unable to retrieve terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno)));
       
   960         return false;
       
   961     }
       
   962     // Turn off terminal echo as not get messages back, among other things
       
   963     termInfo.c_cflag |= CREAD|CLOCAL;
       
   964     termInfo.c_lflag &= (~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|ISIG));
       
   965     termInfo.c_iflag &= (~(INPCK|IGNPAR|PARMRK|ISTRIP|ICRNL|IXANY));
       
   966     termInfo.c_oflag &= (~OPOST);
       
   967     termInfo.c_cc[VMIN]  = 0;
       
   968     termInfo.c_cc[VINTR] = _POSIX_VDISABLE;
       
   969     termInfo.c_cc[VQUIT] = _POSIX_VDISABLE;
       
   970     termInfo.c_cc[VSTART] = _POSIX_VDISABLE;
       
   971     termInfo.c_cc[VSTOP] = _POSIX_VDISABLE;
       
   972     termInfo.c_cc[VSUSP] = _POSIX_VDISABLE;
       
   973     if (tcsetattr(d->deviceContext->file.handle(), TCSAFLUSH, &termInfo) < 0) {
       
   974         *errorMessage = QString::fromLatin1("Unable to apply terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno)));
       
   975         return false;
       
   976     }
       
   977 #endif
       
   978     d->readerThread = QSharedPointer<ReaderThread>(new ReaderThread(d->deviceContext));
       
   979     connect(d->readerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)),
       
   980             Qt::QueuedConnection);
       
   981     connect(d->readerThread.data(), SIGNAL(messageReceived(trk::TrkResult,QByteArray)),
       
   982             this, SLOT(slotMessageReceived(trk::TrkResult,QByteArray)),
       
   983             Qt::QueuedConnection);
       
   984     d->readerThread->start();
       
   985 
       
   986     d->writerThread = QSharedPointer<WriterThread>(new WriterThread(d->deviceContext));
       
   987     connect(d->writerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)),
       
   988             Qt::QueuedConnection);    
       
   989     d->writerThread->start();    
       
   990 
       
   991     if (d->verbose)
       
   992         qDebug() << "Opened" << d->port;
       
   993     return true;
       
   994 }
       
   995 
       
   996 void TrkDevice::close()
       
   997 {
       
   998     if (!isOpen())
       
   999         return;
       
  1000     if (d->readerThread)
       
  1001         d->readerThread->terminate();
       
  1002     if (d->writerThread)
       
  1003         d->writerThread->terminate();
       
  1004 #ifdef Q_OS_WIN
       
  1005     CloseHandle(d->deviceContext->device);
       
  1006     d->deviceContext->device = INVALID_HANDLE_VALUE;
       
  1007     CloseHandle(d->deviceContext->readOverlapped.hEvent);
       
  1008     CloseHandle(d->deviceContext->writeOverlapped.hEvent);
       
  1009     d->deviceContext->readOverlapped.hEvent = d->deviceContext->writeOverlapped.hEvent = NULL;
       
  1010 #else
       
  1011     d->deviceContext->file.close();
       
  1012 #endif
       
  1013     if (d->verbose)
       
  1014         emitLogMessage("Close");
       
  1015 }
       
  1016 
       
  1017 bool TrkDevice::isOpen() const
       
  1018 {
       
  1019 #ifdef Q_OS_WIN
       
  1020     return d->deviceContext->device != INVALID_HANDLE_VALUE;
       
  1021 #else
       
  1022     return d->deviceContext->file.isOpen();
       
  1023 #endif
       
  1024 }
       
  1025 
       
  1026 QString TrkDevice::port() const
       
  1027 {
       
  1028     return d->port;
       
  1029 }
       
  1030 
       
  1031 void TrkDevice::setPort(const QString &p)
       
  1032 {
       
  1033     d->port = p;
       
  1034 }
       
  1035 
       
  1036 QString TrkDevice::errorString() const
       
  1037 {
       
  1038     return d->errorString;
       
  1039 }
       
  1040 
       
  1041 bool TrkDevice::serialFrame() const
       
  1042 {
       
  1043     return d->deviceContext->serialFrame;
       
  1044 }
       
  1045 
       
  1046 void TrkDevice::setSerialFrame(bool f)
       
  1047 {
       
  1048     d->deviceContext->serialFrame = f;
       
  1049 }
       
  1050 
       
  1051 int TrkDevice::verbose() const
       
  1052 {
       
  1053     return d->verbose;
       
  1054 }
       
  1055 
       
  1056 void TrkDevice::setVerbose(int b)
       
  1057 {
       
  1058     d->verbose = b;
       
  1059 }
       
  1060 
       
  1061 void TrkDevice::slotMessageReceived(const trk::TrkResult &result, const QByteArray &rawData)
       
  1062 {
       
  1063     d->writerThread->slotHandleResult(result);
       
  1064     if (d->verbose > 1)
       
  1065         qDebug() << "Received: " << result.toString();
       
  1066     emit messageReceived(result);    
       
  1067     if (!rawData.isEmpty())
       
  1068         emit rawDataReceived(rawData);
       
  1069 }
       
  1070 
       
  1071 void TrkDevice::emitError(const QString &s)
       
  1072 {
       
  1073     d->errorString = s;
       
  1074     qWarning("%s\n", qPrintable(s));
       
  1075     emit error(s);
       
  1076 }
       
  1077 
       
  1078 void TrkDevice::sendTrkMessage(byte code, TrkCallback callback,
       
  1079      const QByteArray &data, const QVariant &cookie)
       
  1080 {
       
  1081     if (!d->writerThread.isNull()) {
       
  1082         if (d->verbose > 1) {
       
  1083             QByteArray msg = "Sending:  ";
       
  1084             msg += QByteArray::number(code, 16);
       
  1085             msg += ": ";
       
  1086             msg += stringFromArray(data).toLatin1();
       
  1087             qDebug("%s", msg.data());
       
  1088         }
       
  1089         d->writerThread->queueTrkMessage(code, callback, data, cookie);
       
  1090     }
       
  1091 }
       
  1092 
       
  1093 void TrkDevice::sendTrkInitialPing()
       
  1094 {
       
  1095     if (!d->writerThread.isNull())
       
  1096         d->writerThread->queueTrkInitialPing();
       
  1097 }
       
  1098 
       
  1099 bool TrkDevice::sendTrkAck(byte token)
       
  1100 {
       
  1101     if (d->writerThread.isNull())
       
  1102         return false;
       
  1103     // The acknowledgement must not be queued!
       
  1104     TrkMessage msg(0x80, token);
       
  1105     msg.token = token;
       
  1106     msg.data.append('\0');
       
  1107     if (verboseTrk)
       
  1108         qDebug() << "Write synchroneous message: " << msg;
       
  1109     return d->writerThread->trkWriteRawMessage(msg);
       
  1110     // 01 90 00 07 7e 80 01 00 7d 5e 7e
       
  1111 }
       
  1112 
       
  1113 void TrkDevice::emitLogMessage(const QString &msg)
       
  1114 {
       
  1115     if (d->verbose)
       
  1116         qDebug("%s\n", qPrintable(msg));
       
  1117     emit logMessage(msg);
       
  1118 }
       
  1119 
       
  1120 } // namespace trk
       
  1121 
       
  1122 #include "trkdevice.moc"