/* * 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 */ #include #include #include #include "commdcbhelper.h" #include "qwincommevtnotifier.h" namespace TNX { /*! */ QWinCommEvtNotifier::QWinCommEvtNotifier(HANDLE hComm, QObject *parent) : QObject(parent), notifier_(NULL), commHandle_(hComm) { suspendEvent_ = CreateEvent(NULL, TRUE, TRUE, NULL); // manual-reset event and initial state is signaled Q_ASSERT(suspendEvent_ != NULL); notifier_ = new NotifierThread(commHandle_, suspendEvent_, this); Q_CHECK_PTR(notifier_); notifier_->start(); } QWinCommEvtNotifier::~QWinCommEvtNotifier() { resume(); notifier_->terminate(); // force notifier thread to exit from WaitCommEvent() by changing the comm mask int allFlags = EV_RXCHAR | EV_TXEMPTY | EV_CTS | EV_DSR | EV_RLSD | EV_BREAK | EV_ERR | EV_RING | EV_PERR | EV_RX80FULL | EV_EVENT1 | EV_EVENT2; if ( !SetCommMask(commHandle_, allFlags) ) { qDebug() << QString("QWinCommEvtNotifier::dtor(hnd: %1) failed when setting comm mask for exit: %2(Err #%3)") .arg((quintptr)commHandle_) .arg(CommDCBHelper::errorText(GetLastError())) .arg(GetLastError()); } // wait until notifier thread is terminated notifier_->wait(); CloseHandle(suspendEvent_); } void QWinCommEvtNotifier::setEnabled(bool val) { blockSignals(!val); if ( val ) { DWORD errorMask = 0; COMSTAT comStat; if ( !SetCommMask(commHandle_, EV_ERR) ) { qDebug() << QString("QWinCommEvtNotifier::setEnabled(hnd: %1) failed when setting comm" \ " event mask: %2(Err #%3)") .arg((quintptr)commHandle_) .arg(CommDCBHelper::errorText(GetLastError())) .arg(GetLastError()); } // Get the number of pending bytes if ( !ClearCommError(commHandle_, &errorMask, &comStat) ) { qDebug() << QString("QWinCommEvtNotifier::setEnabled(hnd: %1) failed when requesting" \ " comm errors: %2(Err #%3)") .arg((quintptr)commHandle_) .arg(CommDCBHelper::errorText(GetLastError())) .arg(GetLastError()); } if ( comStat.cbInQue > 0 ) { // Generate a byte pending event until there is no more data in the character buffer bytesPending(comStat.cbInQue); } else { if ( !SetCommMask(commHandle_, EV_RXCHAR | EV_ERR) ) { qDebug() << QString("QWinCommEvtNotifier::setEnabled(hnd: %1) failed when re-setting" \ " comm event mask: %2(Err #%3)") .arg((quintptr)commHandle_) .arg(CommDCBHelper::errorText(GetLastError())) .arg(GetLastError()); } }//endif } } /* NotifierThread class Implementation */ void QWinCommEvtNotifier::NotifierThread::run() { // store this thread's id threadId_ = QThread::currentThreadId(); if ( !SetCommMask(commHandle_, EV_RXCHAR | EV_ERR) ) { qDebug() << QString("NotifierThread::run(hnd: %1) failed when setting comm event mask: %2(Err #%3)") .arg((quintptr)commHandle_) .arg(CommDCBHelper::errorText(GetLastError())) .arg(GetLastError()); } DWORD dwCommEvent = 0; forever { // wait indefinitely if the event-drive model is suspended by the caller // using suspend() method. DWORD dwWaitResult = WaitForSingleObject(suspendEvent_, INFINITE); switch (dwWaitResult) { // Event object was signaled case WAIT_OBJECT_0: break; // An error occurred default: qDebug(qPrintable(QString("NotifierThread::run(hnd: %1) failed while waiting for suspend event: err #%2. ") .arg((quintptr)commHandle_).arg(GetLastError()))); } if ( !WaitCommEvent(commHandle_, &dwCommEvent, 0) ) { qDebug(qPrintable(QString("NotifierThread::run(hnd: %1) failed in WaitCommEvent. " \ "Event driven model is disabled.") .arg((quintptr)commHandle_))); break; } else { if ( dwCommEvent & EV_RXCHAR ) parent_->bytesPending(-1L); // pending bytes count is unknown if ( dwCommEvent & EV_ERR ) { DWORD errorMask = 0; COMSTAT comStat; if ( !ClearCommError(commHandle_, &errorMask, &comStat) ) { qDebug() << QString("NotifierThread::run(hnd: %1) failed when requesting comm errors: %2(Err #%3)") .arg((quintptr)commHandle_) .arg(CommDCBHelper::errorText(GetLastError())) .arg(GetLastError()); } else { // Printout the error. if ( errorMask & CE_BREAK ) qDebug() << QString("NotifierThread::run(hnd: %1) failed with error: " \ "The hardware detected a break condition.") .arg((quintptr)commHandle_); else if ( errorMask & CE_FRAME ) qDebug() << QString("NotifierThread::run(hnd: %1) failed with error: " \ "The hardware detected a framing error.") .arg((quintptr)commHandle_); else if ( errorMask & CE_OVERRUN ) qDebug() << QString("NotifierThread::run(hnd: %1) failed with error: " \ "A character-buffer overrun has occurred. The next character is lost.") .arg((quintptr)commHandle_); else if ( errorMask & CE_RXOVER ) qDebug() << QString("NotifierThread::run(hnd: %1) failed with error: " \ "An input buffer overflow has occurred. There is either no room in the input buffer, " \ "or a character was received after the end-of-file (EOF) character.") .arg((quintptr)commHandle_); else if ( errorMask & CE_RXPARITY ) qDebug() << QString("NotifierThread::run(hnd: %1) failed with error: " \ "The hardware detected a parity error.") .arg((quintptr)commHandle_); } } } if ( doquit_ ) break; }//forever } } // namespace