/* * Unofficial Qt Serial Port Library * * Copyright (c) 2010 Inbiza Systems Inc. All rights reserved. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * * author labs@inbiza.com */ // See http://msdn.microsoft.com/en-us/library/ms810467.aspx#serial_topic1 // for Win32 Serial Port programming. // Also see the following forum http://www.codeguru.com/forum/archive/index.php/t-324660.html #include #include #include "qwincommevtnotifier.h" #include "wincommevtbreaker.h" #include "commdcbhelper.h" #include "qserialport.h" namespace TNX { WinCommEvtBreaker* QSerialPortNative::waitBreakerThread_ = NULL; QMutex* QSerialPortNative::mutex_ = new QMutex(); // this will leak once /*! Constructs a QSerialPortNative object with the given \a port name and \a parent. */ QSerialPortNative::QSerialPortNative(const QString &portName, QObject *parent) : QIODevice(parent), portName_(portName), fileDescriptor_(0), portHelper_(NULL), readNotifier_(NULL) { } /*! Constructs a QSerialPortNative object with the given \a port name, \a settings and \a parent. */ QSerialPortNative::QSerialPortNative(const QString &portName, const QPortSettings &settings, QObject *parent) : QIODevice(parent), portName_(portName), portSettings_(settings), fileDescriptor_(0), portHelper_(NULL), readNotifier_(NULL) { } /*! */ bool QSerialPortNative::open_impl() { // WinCE requires ':' suffix for all comport names if ( portName_.right(1) != ":" ) portName_.append(":"); fileDescriptor_ = CreateFile(portName_.utf16(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); if ( INVALID_HANDLE_VALUE == fileDescriptor_ ) return false; readNotifier_ = new QWinCommEvtNotifier(fileDescriptor_, this); Q_CHECK_PTR(readNotifier_); if ( !readNotifier_ || !connect(readNotifier_, SIGNAL(activated(int)), this, SLOT(onDataReceived()), Qt::QueuedConnection ) ) { qWarning() << QString("QSerialPort::open(%1) failed when connecting to read notifier") .arg(portName_); } // Create a dcb helper object portHelper_ = new CommDCBHelper(fileDescriptor_); Q_CHECK_PTR(portHelper_); return true; } /*! */ void QSerialPortNative::close_impl() { // Close the serial port, and init the handle if ( !CloseHandle(fileDescriptor_) ) { qWarning() << QString("QSerialPort::close(%1) failed: %2(Err #%3)") .arg(portName_) .arg(lastErrorText_impl()) .arg(lastError_impl()); } fileDescriptor_ = INVALID_HANDLE_VALUE; } /*! */ QString QSerialPortNative::lastErrorText_impl() const { return CommDCBHelper::errorText(GetLastError()); } /*! */ int QSerialPortNative::lastError_impl() const { return GetLastError(); } /*! */ qint64 QSerialPortNative::bytesAvailable_impl() const { DWORD errorMask = 0; COMSTAT comStat; if ( !ClearCommError(fileDescriptor_, &errorMask, &comStat) ) return -1LL; return QIODevice::bytesAvailable() + (qint64)comStat.cbInQue; } /*! * @return -1 if error, 0 if timed-out, 1 if incoming data */ int QSerialPortNative::waitForReadyRead_impl(int timeout) { DWORD orgFlags; DWORD dwMask; int result = -1; readNotifier_->suspend(); // store the current event mask. if ( !GetCommMask(fileDescriptor_, &orgFlags) ) goto LExit; // this one is to release the notifier thread. if ( !SetCommMask(fileDescriptor_, 0) ) goto LExit; // change the event mask to wait incoming bytes. if ( !SetCommMask(fileDescriptor_, EV_RXCHAR | EV_ERR) ) goto LExit; // timeout == -1 means no timeout if ( timeout != -1 ) { // if it is not already done, lazily create the thread that will release // the WaitCommEvent() function when timeout occurs. if ( !waitBreakerThread_ ) { QMutexLocker locker(mutex_); if ( !waitBreakerThread_ ) { waitBreakerThread_ = new WinCommEvtBreaker(); waitBreakerThread_->start(); // wait until thread is ready to run waitBreakerThread_->waitUntilReady(); } } waitBreakerThread_->startWaitTimer(fileDescriptor_, timeout); } if ( WaitCommEvent(fileDescriptor_, &dwMask, 0) ) { // Use the flags returned in dwMask to determine the reason for return if ( dwMask & EV_RXCHAR ) result = 1; else if ( dwMask & EV_ERR ) result = -1; else result = 0; } if ( timeout != -1 ) waitBreakerThread_->stopWaitTimer(fileDescriptor_); // rollback the original event mask. if ( !SetCommMask(fileDescriptor_, orgFlags) ) result = -1; LExit: readNotifier_->resume(); return result; } /*! */ bool QSerialPortNative::flushInBuffer_impl() { // PURGE_RXABORT: Terminates all outstanding overlapped read operations and returns immediately, // even if the read operations have not been completed. // PURGE_TXABORT: Terminates all outstanding overlapped write operations and returns immediately, // even if the write operations have not been completed. // clear internal buffers if ( !PurgeComm(fileDescriptor_, PURGE_RXABORT | PURGE_RXCLEAR ) ) return false; return true; } /*! */ bool QSerialPortNative::flushOutBuffer_impl() { // PURGE_RXABORT: Terminates all outstanding overlapped read operations and returns immediately, // even if the read operations have not been completed. // PURGE_TXABORT: Terminates all outstanding overlapped write operations and returns immediately, // even if the write operations have not been completed. // clear internal buffers if ( !PurgeComm(fileDescriptor_, PURGE_TXABORT | PURGE_TXCLEAR ) ) return false; return true; } /*! */ bool QSerialPortNative::sendBreak_impl(int timeout) { // send a break for a very short time if ( !SetCommBreak(fileDescriptor_) ) return false; Sleep(timeout); // clear the break and continue if ( !ClearCommBreak(fileDescriptor_) ) return false; return true; } /*! */ qint64 QSerialPortNative::readData_impl(char *data, qint64 maxlen) { qint64 numBytes = 0LL; if ( !ReadFile(fileDescriptor_, (void*)data, (DWORD)maxlen, (LPDWORD)&numBytes, NULL) ) numBytes = -1LL; return numBytes; } /*! */ qint64 QSerialPortNative::writeData_impl(const char *data, qint64 len) { qint64 numBytes = 0LL; if ( !WriteFile(fileDescriptor_, (void*)data, (DWORD)len, (DWORD*)&numBytes, NULL) ) numBytes = -1LL; return numBytes; } } // namespace