/*
* 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 "qwincommevtnotifier.h"
#include "commdcbhelper.h"
#include "qserialport.h"
#include "qserialportnative.h"
namespace TNX {
/*!
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)
{
#ifdef __MINGW32__
ZeroMemory(&ovRead_, sizeof(OVERLAPPED));
ZeroMemory(&ovWrite_, sizeof(OVERLAPPED));
#else
SecureZeroMemory(&ovRead_, sizeof(OVERLAPPED));
SecureZeroMemory(&ovWrite_, sizeof(OVERLAPPED));
#endif
}
/*!
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)
{
#ifdef __MINGW32__
ZeroMemory(&ovRead_, sizeof(OVERLAPPED));
ZeroMemory(&ovWrite_, sizeof(OVERLAPPED));
#else
SecureZeroMemory(&ovRead_, sizeof(OVERLAPPED));
SecureZeroMemory(&ovWrite_, sizeof(OVERLAPPED));
#endif
}
/*!
*/
bool QSerialPortNative::open_impl()
{
// This is needed if comport number
// is higher than 9
if ( "//./" != portName_.left(4) )
portName_.insert(0, "//./");
fileDescriptor_ = CreateFileA(portName_.toLatin1(), GENERIC_READ|GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 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_);
// Prepare overlapped io structure for Read/Write operations
ovRead_.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // manual-reset event and initial state is nonsignaled
Q_ASSERT(ovRead_.hEvent != NULL);
ovWrite_.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);// manual-reset event and initial state is nonsignaled
Q_ASSERT(ovWrite_.hEvent != NULL);
return true;
}
/*!
*/
void QSerialPortNative::close_impl()
{
// Close Read/Write overlapped io event handles
CloseHandle(ovRead_.hEvent);
CloseHandle(ovWrite_.hEvent);
// 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)
{
int result = -1;
OVERLAPPED osStatus;
DWORD dwMask;
DWORD orgFlags;
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;
{
#ifdef __MINGW32__
ZeroMemory(&osStatus, sizeof(OVERLAPPED));
#else
SecureZeroMemory(&osStatus, sizeof(OVERLAPPED));
#endif
osStatus.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
Q_ASSERT(osStatus.hEvent != NULL);
if ( !WaitCommEvent(fileDescriptor_, &dwMask, &osStatus) ) {
if ( GetLastError() == ERROR_IO_PENDING ) {
// Wait until the operation is completed
DWORD dwRes = WaitForSingleObject(osStatus.hEvent, (timeout == -1 ? INFINITE : timeout));
switch (dwRes)
{
// Event occurred
case WAIT_OBJECT_0:
if ( !GetOverlappedResult(fileDescriptor_, &osStatus, (LPDWORD)&dwRes, FALSE) ) {
// An error occurred in the overlapped operation; call GetLastError to find out what it is.
result = -1;
}
else {
if ( dwMask & EV_RXCHAR )
result = 1; // data pending
else if ( dwMask & EV_ERR )
result = -1;
else {
qWarning() << QString("QSerialPortNative::waitForReadyRead(%1): unexpected event returned.")
.arg(portName_);
result = 0; // this path is not expected
}
}
break;
case WAIT_TIMEOUT:
result = 0;
break;
// Error in the WaitForSingleObject;
// This indicates a problem with the OVERLAPPED structure's
// event handle.
default:
result = -1;
}// switch
}
else {
// Error while waiting for data
result = -1;
}
}
else {
// WaitCommEvent returned immediately.
result = 1;
}
CloseHandle(osStatus.hEvent);
}
// reset event mask for event notifier thread
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)
{
ResetEvent(ovRead_.hEvent);
qint64 numBytes = 0LL;
if ( !ReadFile(fileDescriptor_, (void*)data, (DWORD)maxlen, (LPDWORD)&numBytes, &ovRead_) ) {
if ( GetLastError() == ERROR_IO_PENDING ) {
// Wait until read operation is completed
DWORD dwRes = WaitForSingleObject(ovRead_.hEvent, INFINITE);
switch (dwRes)
{
// Event occurred
case WAIT_OBJECT_0:
if ( !GetOverlappedResult(fileDescriptor_, &ovRead_, (LPDWORD)&numBytes, FALSE) ) {
// An error occurred in the overlapped operation;
// call GetLastError to find out what it was
numBytes = -1LL;
}
break;
// Error in the WaitForSingleObject;
// This indicates a problem with the OVERLAPPED structure's
// event handle.
default:
numBytes = -1LL;
}// switch
}
else {
// Error while reading data
numBytes = -1LL;
}
}//endif
return numBytes;
}
/*!
*/
qint64 QSerialPortNative::writeData_impl(const char *data, qint64 len)
{
ResetEvent(ovWrite_.hEvent);
qint64 numBytes = 0LL;
if ( !WriteFile(fileDescriptor_, (void*)data, (DWORD)len, (DWORD*)&numBytes, &ovWrite_) ) {
if ( GetLastError() == ERROR_IO_PENDING ) {
// Wait until write operation is completed
DWORD dwRes = WaitForSingleObject(ovWrite_.hEvent, INFINITE);
switch (dwRes)
{
// Event occurred
case WAIT_OBJECT_0:
if ( !GetOverlappedResult(fileDescriptor_, &ovWrite_, (LPDWORD)&numBytes, FALSE) ) {
// An error occurred in the overlapped operation;
// call GetLastError to find out what it was
numBytes = -1LL;
}
break;
// Error in the WaitForSingleObject;
// This indicates a problem with the OVERLAPPED structure's
// event handle.
default:
numBytes = -1LL;
}// switch
}
else {
// Error while writing data
numBytes = -1LL;
}
}
return numBytes;
}
} // namespace