summaryrefslogtreecommitdiff
path: root/src/serialport
diff options
context:
space:
mode:
Diffstat (limited to 'src/serialport')
-rw-r--r--src/serialport/doc/src/index.qdoc11
-rw-r--r--src/serialport/doc/src/qtserialport-module.qdoc10
-rw-r--r--src/serialport/qserialport.cpp59
-rw-r--r--src/serialport/qserialport_p.h83
-rw-r--r--src/serialport/qserialport_unix.cpp47
-rw-r--r--src/serialport/qserialport_win.cpp129
-rw-r--r--src/serialport/qserialportinfo_p.h20
-rw-r--r--src/serialport/qwinoverlappedionotifier.cpp420
-rw-r--r--src/serialport/qwinoverlappedionotifier_p.h91
-rw-r--r--src/serialport/serialport-lib.pri6
10 files changed, 636 insertions, 240 deletions
diff --git a/src/serialport/doc/src/index.qdoc b/src/serialport/doc/src/index.qdoc
index 3b0f762..041188e 100644
--- a/src/serialport/doc/src/index.qdoc
+++ b/src/serialport/doc/src/index.qdoc
@@ -43,11 +43,18 @@
\li Pinout signal change notification.
\endlist
- To use these classes in your application, use the following include
+ To use the serial port in your application, add the following include
statement:
\code
- #include <QtSerialPort/QtSerialPort>
+ #include <QSerialPort>
+ \endcode
+
+ For information about available serial ports, use the following include
+ statement:
+
+ \code
+ #include <QSerialPortInfo>
\endcode
To link against the module, add this line to your \l qmake \c
diff --git a/src/serialport/doc/src/qtserialport-module.qdoc b/src/serialport/doc/src/qtserialport-module.qdoc
index 325b780..6c153a0 100644
--- a/src/serialport/doc/src/qtserialport-module.qdoc
+++ b/src/serialport/doc/src/qtserialport-module.qdoc
@@ -32,11 +32,17 @@
\qtvariable serialport
\brief List of C++ classes that enable access to a serial port.
- To use these classes in your application, use the following include
+ To use QSerialPort in your application, add the following include
statement:
\code
- #include <QtSerialPort/QtSerialPort>
+ #include <QSerialPort>
+ \endcode
+
+ For QSerialPortInfo, use the following include statement:
+
+ \code
+ #include <QSerialPortInfo>
\endcode
To link against the module, add this line to your \l qmake \c
diff --git a/src/serialport/qserialport.cpp b/src/serialport/qserialport.cpp
index 8ea5a48..3b88736 100644
--- a/src/serialport/qserialport.cpp
+++ b/src/serialport/qserialport.cpp
@@ -87,42 +87,12 @@ QSerialPortErrorInfo::QSerialPortErrorInfo(QSerialPort::SerialPortError newError
}
QSerialPortPrivate::QSerialPortPrivate()
- : readBufferMaxSize(0)
- , error(QSerialPort::NoError)
- , inputBaudRate(9600)
- , outputBaudRate(9600)
- , dataBits(QSerialPort::Data8)
- , parity(QSerialPort::NoParity)
- , stopBits(QSerialPort::OneStop)
- , flowControl(QSerialPort::NoFlowControl)
-#if QT_DEPRECATED_SINCE(5,3)
- , settingsRestoredOnClose(true)
-#endif
- , isBreakEnabled(false)
#if defined(Q_OS_WIN32)
- , handle(INVALID_HANDLE_VALUE)
- , readChunkBuffer(ReadChunkSize, 0)
- , communicationStarted(false)
- , writeStarted(false)
- , readStarted(false)
- , notifier(0)
- , startAsyncWriteTimer(0)
- , originalEventMask(0)
- , triggeredEventMask(0)
-#elif defined(Q_OS_UNIX)
- , descriptor(-1)
- , readNotifier(0)
- , writeNotifier(0)
- , readPortNotifierCalled(false)
- , readPortNotifierState(false)
- , readPortNotifierStateSet(false)
- , emittedReadyRead(false)
- , emittedBytesWritten(false)
- , pendingBytesWritten(0)
- , writeSequenceStarted(false)
+ : readChunkBuffer(QSERIALPORT_BUFFERSIZE, 0)
#endif
{
- writeBufferChunkSize = InitialBufferSize;
+ writeBufferChunkSize = QSERIALPORT_BUFFERSIZE;
+ readBufferChunkSize = QSERIALPORT_BUFFERSIZE;
}
void QSerialPortPrivate::setError(const QSerialPortErrorInfo &errorInfo)
@@ -1244,10 +1214,9 @@ qint64 QSerialPort::readBufferSize() const
void QSerialPort::setReadBufferSize(qint64 size)
{
Q_D(QSerialPort);
-
- if (d->readBufferMaxSize == size)
- return;
d->readBufferMaxSize = size;
+ if (isReadable())
+ d->startAsyncRead();
}
/*!
@@ -1436,21 +1405,9 @@ qint64 QSerialPort::readData(char *data, qint64 maxSize)
Q_UNUSED(data);
Q_UNUSED(maxSize);
-#if defined(Q_OS_WIN32)
- // We need try to start async reading to read a remainder from a driver's queue
- // in case we have a limited read buffer size. Because the read notification can
- // be stalled since Windows do not re-triggered an EV_RXCHAR event if a driver's
- // buffer has a remainder of data ready to read until a new data will be received.
- Q_D(QSerialPort);
- if (d->readBufferMaxSize || d->flowControl == QSerialPort::HardwareControl)
- d->startAsyncRead();
-#elif defined(Q_OS_UNIX)
- // We need try to re-trigger the read notification to read a remainder from a
- // driver's queue in case we have a limited read buffer size.
- Q_D(QSerialPort);
- if (d->readBufferMaxSize && !d->isReadNotificationEnabled())
- d->setReadNotificationEnabled(true);
-#endif
+ // In any case we need to start the notifications if they were
+ // disabled by the read handler. If enabled, next call does nothing.
+ d_func()->startAsyncRead();
// return 0 indicating there may be more data in the future
return qint64(0);
diff --git a/src/serialport/qserialport_p.h b/src/serialport/qserialport_p.h
index 10fda34..b0955ae 100644
--- a/src/serialport/qserialport_p.h
+++ b/src/serialport/qserialport_p.h
@@ -56,6 +56,7 @@
#include "qserialport.h"
#include <private/qiodevice_p.h>
+#include <qdeadlinetimer.h>
#if defined(Q_OS_WIN32)
# include <qt_windows.h>
@@ -97,9 +98,12 @@ struct serial_struct {
# error Unsupported OS
#endif
+#ifndef QSERIALPORT_BUFFERSIZE
+#define QSERIALPORT_BUFFERSIZE 32768
+#endif
+
QT_BEGIN_NAMESPACE
-class QThread;
class QWinOverlappedIoNotifier;
class QTimer;
class QSocketNotifier;
@@ -113,7 +117,7 @@ class QSerialPortErrorInfo
public:
explicit QSerialPortErrorInfo(QSerialPort::SerialPortError newErrorCode = QSerialPort::UnknownError,
const QString &newErrorString = QString());
- QSerialPort::SerialPortError errorCode;
+ QSerialPort::SerialPortError errorCode = QSerialPort::UnknownError;
QString errorString;
};
@@ -121,11 +125,6 @@ class QSerialPortPrivate : public QIODevicePrivate
{
Q_DECLARE_PUBLIC(QSerialPort)
public:
- enum IoConstants {
- ReadChunkSize = 512,
- InitialBufferSize = 16384
- };
-
QSerialPortPrivate();
bool open(QIODevice::OpenMode mode);
@@ -158,33 +157,32 @@ public:
qint64 writeData(const char *data, qint64 maxSize);
+ bool initialize(QIODevice::OpenMode mode);
+
static QString portNameToSystemLocation(const QString &port);
static QString portNameFromSystemLocation(const QString &location);
-#if defined(Q_OS_UNIX)
- static qint32 settingFromBaudRate(qint32 baudRate);
-#endif
-
static QList<qint32> standardBaudRates();
- qint64 readBufferMaxSize;
- QSerialPort::SerialPortError error;
+ qint64 readBufferMaxSize = 0;
+ QSerialPort::SerialPortError error = QSerialPort::NoError;
QString systemLocation;
- qint32 inputBaudRate;
- qint32 outputBaudRate;
- QSerialPort::DataBits dataBits;
- QSerialPort::Parity parity;
- QSerialPort::StopBits stopBits;
- QSerialPort::FlowControl flowControl;
- bool settingsRestoredOnClose;
- bool isBreakEnabled;
+ qint32 inputBaudRate = QSerialPort::Baud9600;
+ qint32 outputBaudRate = QSerialPort::Baud9600;
+ QSerialPort::DataBits dataBits = QSerialPort::Data8;
+ QSerialPort::Parity parity = QSerialPort::NoParity;
+ QSerialPort::StopBits stopBits = QSerialPort::OneStop;
+ QSerialPort::FlowControl flowControl = QSerialPort::NoFlowControl;
+ bool settingsRestoredOnClose = true;
+ bool isBreakEnabled = false;
+
+ bool startAsyncRead();
#if defined(Q_OS_WIN32)
- bool initialize();
bool setDcb(DCB *dcb);
bool getDcb(DCB *dcb);
- OVERLAPPED *waitForNotified(int msecs);
+ OVERLAPPED *waitForNotified(QDeadlineTimer deadline);
qint64 queuedBytesCount(QSerialPort::Direction direction) const;
@@ -193,7 +191,6 @@ public:
bool completeAsyncWrite(qint64 bytesTransferred);
bool startAsyncCommunication();
- bool startAsyncRead();
bool _q_startAsyncWrite();
void _q_notified(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped);
@@ -202,23 +199,23 @@ public:
DCB restoredDcb;
COMMTIMEOUTS currentCommTimeouts;
COMMTIMEOUTS restoredCommTimeouts;
- HANDLE handle;
+ HANDLE handle = INVALID_HANDLE_VALUE;
QByteArray readChunkBuffer;
QByteArray writeChunkBuffer;
- bool communicationStarted;
- bool writeStarted;
- bool readStarted;
- QWinOverlappedIoNotifier *notifier;
- QTimer *startAsyncWriteTimer;
+ bool communicationStarted = false;
+ bool writeStarted = false;
+ bool readStarted = false;
+ QWinOverlappedIoNotifier *notifier = nullptr;
+ QTimer *startAsyncWriteTimer = nullptr;
OVERLAPPED communicationOverlapped;
OVERLAPPED readCompletionOverlapped;
OVERLAPPED writeCompletionOverlapped;
- DWORD originalEventMask;
- DWORD triggeredEventMask;
+ DWORD triggeredEventMask = 0;
#elif defined(Q_OS_UNIX)
- bool initialize(QIODevice::OpenMode mode);
+ static qint32 settingFromBaudRate(qint32 baudRate);
+
bool setTermios(const termios *tio);
bool getTermios(termios *tio);
@@ -246,20 +243,20 @@ public:
bool completeAsyncWrite();
struct termios restoredTermios;
- int descriptor;
+ int descriptor = -1;
- QSocketNotifier *readNotifier;
- QSocketNotifier *writeNotifier;
+ QSocketNotifier *readNotifier = nullptr;
+ QSocketNotifier *writeNotifier = nullptr;
- bool readPortNotifierCalled;
- bool readPortNotifierState;
- bool readPortNotifierStateSet;
+ bool readPortNotifierCalled = false;
+ bool readPortNotifierState = false;
+ bool readPortNotifierStateSet = false;
- bool emittedReadyRead;
- bool emittedBytesWritten;
+ bool emittedReadyRead = false;
+ bool emittedBytesWritten = false;
- qint64 pendingBytesWritten;
- bool writeSequenceStarted;
+ qint64 pendingBytesWritten = 0;
+ bool writeSequenceStarted = false;
QScopedPointer<QLockFile> lockFileScopedPointer;
diff --git a/src/serialport/qserialport_unix.cpp b/src/serialport/qserialport_unix.cpp
index 4df1fcb..05db317 100644
--- a/src/serialport/qserialport_unix.cpp
+++ b/src/serialport/qserialport_unix.cpp
@@ -42,10 +42,17 @@
#include "qserialport_p.h"
#include "qserialportinfo_p.h"
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qsocketnotifier.h>
+#include <QtCore/qstandardpaths.h>
+
+#include <private/qcore_unix_p.h>
+
#include <errno.h>
-#include <sys/time.h>
-#include <sys/ioctl.h>
#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
#include <unistd.h>
#ifdef Q_OS_OSX
@@ -93,16 +100,6 @@ struct termios2 {
#endif
-#include <private/qcore_unix_p.h>
-
-#include <QtCore/qelapsedtimer.h>
-#include <QtCore/qsocketnotifier.h>
-#include <QtCore/qmap.h>
-
-#ifdef Q_OS_OSX
-#include <QtCore/qstandardpaths.h>
-#endif
-
QT_BEGIN_NAMESPACE
QString serialPortLockFilePath(const QString &portName)
@@ -118,12 +115,8 @@ QString serialPortLockFilePath(const QString &portName)
<< QStringLiteral("/run/lock")
#ifdef Q_OS_ANDROID
<< QStringLiteral("/data/local/tmp")
-#elif defined(Q_OS_OSX)
- // This is the workaround to specify a temporary directory
- // on OSX when running the App Sandbox feature.
- << QStandardPaths::writableLocation(QStandardPaths::TempLocation);
#endif
- ;
+ << QStandardPaths::writableLocation(QStandardPaths::TempLocation);
QString fileName = portName;
fileName.replace(QLatin1Char('/'), QLatin1Char('_'));
@@ -156,7 +149,7 @@ QString serialPortLockFilePath(const QString &portName)
class ReadNotifier : public QSocketNotifier
{
public:
- ReadNotifier(QSerialPortPrivate *d, QObject *parent)
+ explicit ReadNotifier(QSerialPortPrivate *d, QObject *parent)
: QSocketNotifier(d->descriptor, QSocketNotifier::Read, parent)
, dptr(d)
{
@@ -173,13 +166,13 @@ protected:
}
private:
- QSerialPortPrivate *dptr;
+ QSerialPortPrivate * const dptr;
};
class WriteNotifier : public QSocketNotifier
{
public:
- WriteNotifier(QSerialPortPrivate *d, QObject *parent)
+ explicit WriteNotifier(QSerialPortPrivate *d, QObject *parent)
: QSocketNotifier(d->descriptor, QSocketNotifier::Write, parent)
, dptr(d)
{
@@ -196,7 +189,7 @@ protected:
}
private:
- QSerialPortPrivate *dptr;
+ QSerialPortPrivate * const dptr;
};
static inline void qt_set_common_props(termios *tio, QIODevice::OpenMode m)
@@ -763,13 +756,19 @@ bool QSerialPortPrivate::setFlowControl(QSerialPort::FlowControl flowControl)
return setTermios(&tio);
}
+bool QSerialPortPrivate::startAsyncRead()
+{
+ setReadNotificationEnabled(true);
+ return true;
+}
+
bool QSerialPortPrivate::readNotification()
{
Q_Q(QSerialPort);
// Always buffered, read data from the port into the read buffer
qint64 newBytes = buffer.size();
- qint64 bytesToRead = ReadChunkSize;
+ qint64 bytesToRead = QSERIALPORT_BUFFERSIZE;
if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - buffer.size())) {
bytesToRead = readBufferMaxSize - buffer.size();
@@ -798,10 +797,6 @@ bool QSerialPortPrivate::readNotification()
newBytes = buffer.size() - newBytes;
- // If read buffer is full, disable the read port notifier.
- if (readBufferMaxSize && buffer.size() == readBufferMaxSize)
- setReadNotificationEnabled(false);
-
// only emit readyRead() when not recursing, and only if there is data available
const bool hasData = newBytes > 0;
diff --git a/src/serialport/qserialport_win.cpp b/src/serialport/qserialport_win.cpp
index 87f9e7b..f90a7ef 100644
--- a/src/serialport/qserialport_win.cpp
+++ b/src/serialport/qserialport_win.cpp
@@ -40,12 +40,12 @@
****************************************************************************/
#include "qserialport_p.h"
+#include "qwinoverlappedionotifier_p.h"
#include <QtCore/qcoreevent.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qvector.h>
#include <QtCore/qtimer.h>
-#include <private/qwinoverlappedionotifier_p.h>
#include <algorithm>
#ifndef CTL_CODE
@@ -176,12 +176,9 @@ static inline void qt_set_flowcontrol(DCB *dcb, QSerialPort::FlowControl flowcon
bool QSerialPortPrivate::open(QIODevice::OpenMode mode)
{
DWORD desiredAccess = 0;
- originalEventMask = 0;
- if (mode & QIODevice::ReadOnly) {
+ if (mode & QIODevice::ReadOnly)
desiredAccess |= GENERIC_READ;
- originalEventMask |= EV_RXCHAR;
- }
if (mode & QIODevice::WriteOnly)
desiredAccess |= GENERIC_WRITE;
@@ -193,7 +190,7 @@ bool QSerialPortPrivate::open(QIODevice::OpenMode mode)
return false;
}
- if (initialize())
+ if (initialize(mode))
return true;
::CloseHandle(handle);
@@ -347,18 +344,16 @@ bool QSerialPortPrivate::waitForReadyRead(int msecs)
const qint64 initialReadBufferSize = buffer.size();
qint64 currentReadBufferSize = initialReadBufferSize;
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer deadline(msecs);
do {
- const OVERLAPPED *overlapped = waitForNotified(
- qt_subtract_from_timeout(msecs, stopWatch.elapsed()));
+ const OVERLAPPED *overlapped = waitForNotified(deadline);
if (!overlapped)
return false;
if (overlapped == &readCompletionOverlapped) {
const qint64 readBytesForOneReadOperation = qint64(buffer.size()) - currentReadBufferSize;
- if (readBytesForOneReadOperation == ReadChunkSize) {
+ if (readBytesForOneReadOperation == QSERIALPORT_BUFFERSIZE) {
currentReadBufferSize = buffer.size();
} else if (readBytesForOneReadOperation == 0) {
if (initialReadBufferSize != currentReadBufferSize)
@@ -368,7 +363,7 @@ bool QSerialPortPrivate::waitForReadyRead(int msecs)
}
}
- } while (msecs == -1 || qt_subtract_from_timeout(msecs, stopWatch.elapsed()) > 0);
+ } while (!deadline.hasExpired());
return false;
}
@@ -381,12 +376,10 @@ bool QSerialPortPrivate::waitForBytesWritten(int msecs)
if (!writeStarted && !_q_startAsyncWrite())
return false;
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer deadline(msecs);
for (;;) {
- const OVERLAPPED *overlapped = waitForNotified(
- qt_subtract_from_timeout(msecs, stopWatch.elapsed()));
+ const OVERLAPPED *overlapped = waitForNotified(deadline);
if (!overlapped)
return false;
@@ -484,11 +477,10 @@ bool QSerialPortPrivate::completeAsyncRead(qint64 bytesTransferred)
readStarted = false;
bool result = true;
- if (bytesTransferred == ReadChunkSize
+ if (bytesTransferred == QSERIALPORT_BUFFERSIZE
|| queuedBytesCount(QSerialPort::Input) > 0) {
result = startAsyncRead();
- } else if (readBufferMaxSize == 0
- || readBufferMaxSize > buffer.size()) {
+ } else {
result = startAsyncCommunication();
}
@@ -541,7 +533,7 @@ bool QSerialPortPrivate::startAsyncRead()
if (readStarted)
return true;
- qint64 bytesToRead = ReadChunkSize;
+ qint64 bytesToRead = QSERIALPORT_BUFFERSIZE;
if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - buffer.size())) {
bytesToRead = readBufferMaxSize - buffer.size();
@@ -552,6 +544,8 @@ bool QSerialPortPrivate::startAsyncRead()
}
}
+ Q_ASSERT(int(bytesToRead) <= readChunkBuffer.size());
+
::ZeroMemory(&readCompletionOverlapped, sizeof(readCompletionOverlapped));
if (::ReadFile(handle, readChunkBuffer.data(), bytesToRead, nullptr, &readCompletionOverlapped)) {
readStarted = true;
@@ -638,9 +632,9 @@ qint64 QSerialPortPrivate::writeData(const char *data, qint64 maxSize)
return maxSize;
}
-OVERLAPPED *QSerialPortPrivate::waitForNotified(int msecs)
+OVERLAPPED *QSerialPortPrivate::waitForNotified(QDeadlineTimer deadline)
{
- OVERLAPPED *overlapped = notifier->waitForAnyNotified(msecs);
+ OVERLAPPED *overlapped = notifier->waitForAnyNotified(deadline);
if (!overlapped) {
setError(getSystemError(WAIT_TIMEOUT));
return nullptr;
@@ -658,7 +652,7 @@ qint64 QSerialPortPrivate::queuedBytesCount(QSerialPort::Direction direction) co
: ((direction == QSerialPort::Output) ? comstat.cbOutQue : -1);
}
-inline bool QSerialPortPrivate::initialize()
+inline bool QSerialPortPrivate::initialize(QIODevice::OpenMode mode)
{
Q_Q(QSerialPort);
@@ -691,20 +685,21 @@ inline bool QSerialPortPrivate::initialize()
return false;
}
- if (!::SetCommMask(handle, originalEventMask)) {
+ const DWORD eventMask = (mode & QIODevice::ReadOnly) ? EV_RXCHAR : 0;
+ if (!::SetCommMask(handle, eventMask)) {
setError(getSystemError());
return false;
}
+ if ((eventMask & EV_RXCHAR) && !startAsyncCommunication())
+ return false;
+
notifier = new QWinOverlappedIoNotifier(q);
QObjectPrivate::connect(notifier, &QWinOverlappedIoNotifier::notified,
this, &QSerialPortPrivate::_q_notified);
notifier->setHandle(handle);
notifier->setEnabled(true);
- if ((originalEventMask & EV_RXCHAR) && !startAsyncCommunication())
- return false;
-
return true;
}
@@ -785,80 +780,16 @@ QSerialPortErrorInfo QSerialPortPrivate::getSystemError(int systemErrorCode) con
}
// This table contains standard values of baud rates that
-// are defined in MSDN and/or in Win SDK file winbase.h
-
-static const QList<qint32> standardBaudRatePairList()
-{
-
- static const QList<qint32> standardBaudRatesTable = QList<qint32>()
-
- #ifdef CBR_110
- << CBR_110
- #endif
-
- #ifdef CBR_300
- << CBR_300
- #endif
-
- #ifdef CBR_600
- << CBR_600
- #endif
-
- #ifdef CBR_1200
- << CBR_1200
- #endif
-
- #ifdef CBR_2400
- << CBR_2400
- #endif
-
- #ifdef CBR_4800
- << CBR_4800
- #endif
-
- #ifdef CBR_9600
- << CBR_9600
- #endif
-
- #ifdef CBR_14400
- << CBR_14400
- #endif
-
- #ifdef CBR_19200
- << CBR_19200
- #endif
-
- #ifdef CBR_38400
- << CBR_38400
- #endif
-
- #ifdef CBR_56000
- << CBR_56000
- #endif
-
- #ifdef CBR_57600
- << CBR_57600
- #endif
-
- #ifdef CBR_115200
- << CBR_115200
- #endif
-
- #ifdef CBR_128000
- << CBR_128000
- #endif
-
- #ifdef CBR_256000
- << CBR_256000
- #endif
- ;
-
- return standardBaudRatesTable;
-};
-
+// are defined in file winbase.h
QList<qint32> QSerialPortPrivate::standardBaudRates()
{
- return standardBaudRatePairList();
+ static const QList<qint32> baudRates = {
+ CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400,
+ CBR_4800, CBR_9600, CBR_14400, CBR_19200, CBR_38400,
+ CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000
+ };
+
+ return baudRates;
}
QSerialPort::Handle QSerialPort::handle() const
diff --git a/src/serialport/qserialportinfo_p.h b/src/serialport/qserialportinfo_p.h
index 483ff8c..405cc41 100644
--- a/src/serialport/qserialportinfo_p.h
+++ b/src/serialport/qserialportinfo_p.h
@@ -60,18 +60,6 @@ QT_BEGIN_NAMESPACE
class Q_AUTOTEST_EXPORT QSerialPortInfoPrivate
{
public:
- QSerialPortInfoPrivate()
- : vendorIdentifier(0)
- , productIdentifier(0)
- , hasVendorIdentifier(false)
- , hasProductIdentifier(false)
- {
- }
-
- ~QSerialPortInfoPrivate()
- {
- }
-
static QString portNameToSystemLocation(const QString &source);
static QString portNameFromSystemLocation(const QString &source);
@@ -81,11 +69,11 @@ public:
QString manufacturer;
QString serialNumber;
- quint16 vendorIdentifier;
- quint16 productIdentifier;
+ quint16 vendorIdentifier = 0;
+ quint16 productIdentifier = 0;
- bool hasVendorIdentifier;
- bool hasProductIdentifier;
+ bool hasVendorIdentifier = false;
+ bool hasProductIdentifier = false;
};
class QSerialPortInfoPrivateDeleter
diff --git a/src/serialport/qwinoverlappedionotifier.cpp b/src/serialport/qwinoverlappedionotifier.cpp
new file mode 100644
index 0000000..dfae3b0
--- /dev/null
+++ b/src/serialport/qwinoverlappedionotifier.cpp
@@ -0,0 +1,420 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinoverlappedionotifier_p.h"
+#include <qdebug.h>
+#include <qatomic.h>
+#include <qelapsedtimer.h>
+#include <qmutex.h>
+#include <qpointer.h>
+#include <qqueue.h>
+#include <qset.h>
+#include <qthread.h>
+#include <qt_windows.h>
+#include <private/qobject_p.h>
+#include <private/qiodevice_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWinOverlappedIoNotifier
+ \inmodule QtCore
+ \brief The QWinOverlappedIoNotifier class provides support for overlapped I/O notifications on Windows.
+ \since 5.0
+ \internal
+
+ The QWinOverlappedIoNotifier class makes it possible to use efficient
+ overlapped (asynchronous) I/O notifications on Windows by using an
+ I/O completion port.
+
+ Once you have obtained a file handle, you can use setHandle() to get
+ notifications for I/O operations. Whenever an I/O operation completes,
+ the notified() signal is emitted which will pass the number of transferred
+ bytes, the operation's error code and a pointer to the operation's
+ OVERLAPPED object to the receiver.
+
+ Every handle that supports overlapped I/O can be used by
+ QWinOverlappedIoNotifier. That includes file handles, TCP sockets
+ and named pipes.
+
+ Note that you must not use ReadFileEx() and WriteFileEx() together
+ with QWinOverlappedIoNotifier. They are not supported as they use a
+ different I/O notification mechanism.
+
+ The hEvent member in the OVERLAPPED structure passed to ReadFile()
+ or WriteFile() is ignored and can be used for other purposes.
+
+ \warning This class is only available on Windows.
+
+ Due to peculiarities of the Windows I/O completion port API, users of
+ QWinOverlappedIoNotifier must pay attention to the following restrictions:
+ \list
+ \li File handles with a QWinOverlappedIoNotifer are assigned to an I/O
+ completion port until the handle is closed. It is impossible to
+ disassociate the file handle from the I/O completion port.
+ \li There can be only one QWinOverlappedIoNotifer per file handle. Creating
+ another QWinOverlappedIoNotifier for that file, even with a duplicated
+ handle, will fail.
+ \li Certain Windows API functions are unavailable for file handles that are
+ assigned to an I/O completion port. This includes the functions
+ \c{ReadFileEx} and \c{WriteFileEx}.
+ \endlist
+ See also the remarks in the MSDN documentation for the
+ \c{CreateIoCompletionPort} function.
+*/
+
+struct IOResult
+{
+ IOResult(DWORD n = 0, DWORD e = 0, OVERLAPPED *p = nullptr)
+ : numberOfBytes(n), errorCode(e), overlapped(p)
+ {}
+
+ DWORD numberOfBytes = 0;
+ DWORD errorCode = 0;
+ OVERLAPPED *overlapped = nullptr;
+};
+
+
+class QWinIoCompletionPort;
+
+class QWinOverlappedIoNotifierPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QWinOverlappedIoNotifier)
+public:
+ OVERLAPPED *waitForAnyNotified(QDeadlineTimer deadline);
+ void notify(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped);
+ void _q_notified();
+ OVERLAPPED *dispatchNextIoResult();
+
+ static QWinIoCompletionPort *iocp;
+ static HANDLE iocpInstanceLock;
+ static unsigned int iocpInstanceRefCount;
+ HANDLE hHandle = INVALID_HANDLE_VALUE;
+ HANDLE hSemaphore = nullptr;
+ HANDLE hResultsMutex = nullptr;
+ QAtomicInt waiting;
+ QQueue<IOResult> results;
+};
+
+QWinIoCompletionPort *QWinOverlappedIoNotifierPrivate::iocp = 0;
+HANDLE QWinOverlappedIoNotifierPrivate::iocpInstanceLock = CreateMutex(NULL, FALSE, NULL);
+unsigned int QWinOverlappedIoNotifierPrivate::iocpInstanceRefCount = 0;
+
+
+class QWinIoCompletionPort : protected QThread
+{
+public:
+ QWinIoCompletionPort()
+ : finishThreadKey(reinterpret_cast<ULONG_PTR>(this)),
+ drainQueueKey(reinterpret_cast<ULONG_PTR>(this + 1))
+ {
+ setObjectName(QLatin1String("I/O completion port thread"));
+ HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
+ if (!hIOCP) {
+ qErrnoWarning("CreateIoCompletionPort failed.");
+ return;
+ }
+ hPort = hIOCP;
+ hQueueDrainedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!hQueueDrainedEvent) {
+ qErrnoWarning("CreateEvent failed.");
+ return;
+ }
+ }
+
+ ~QWinIoCompletionPort()
+ {
+ PostQueuedCompletionStatus(hPort, 0, finishThreadKey, NULL);
+ QThread::wait();
+ CloseHandle(hPort);
+ CloseHandle(hQueueDrainedEvent);
+ }
+
+ void registerNotifier(QWinOverlappedIoNotifierPrivate *notifier)
+ {
+ const HANDLE hHandle = notifier->hHandle;
+ HANDLE hIOCP = CreateIoCompletionPort(hHandle, hPort,
+ reinterpret_cast<ULONG_PTR>(notifier), 0);
+ if (!hIOCP) {
+ qErrnoWarning("Can't associate file handle %x with I/O completion port.", hHandle);
+ return;
+ }
+ mutex.lock();
+ notifiers += notifier;
+ mutex.unlock();
+ if (!QThread::isRunning())
+ QThread::start();
+ }
+
+ void unregisterNotifier(QWinOverlappedIoNotifierPrivate *notifier)
+ {
+ mutex.lock();
+ notifiers.remove(notifier);
+ mutex.unlock();
+ }
+
+ void drainQueue()
+ {
+ QMutexLocker locker(&drainQueueMutex);
+ ResetEvent(hQueueDrainedEvent);
+ PostQueuedCompletionStatus(hPort, 0, drainQueueKey, NULL);
+ WaitForSingleObject(hQueueDrainedEvent, INFINITE);
+ }
+
+ using QThread::isRunning;
+
+protected:
+ void run()
+ {
+ DWORD dwBytesRead = 0;
+ ULONG_PTR pulCompletionKey = 0;
+ OVERLAPPED *overlapped = nullptr;
+ DWORD msecs = INFINITE;
+
+ forever {
+ BOOL success = GetQueuedCompletionStatus(hPort,
+ &dwBytesRead,
+ &pulCompletionKey,
+ &overlapped,
+ msecs);
+
+ DWORD errorCode = success ? ERROR_SUCCESS : GetLastError();
+ if (!success && !overlapped) {
+ if (!msecs) {
+ // Time out in drain mode. The completion status queue is empty.
+ msecs = INFINITE;
+ SetEvent(hQueueDrainedEvent);
+ continue;
+ }
+ qErrnoWarning(errorCode, "GetQueuedCompletionStatus failed.");
+ return;
+ }
+
+ if (pulCompletionKey == finishThreadKey)
+ return;
+ if (pulCompletionKey == drainQueueKey) {
+ // Enter drain mode.
+ Q_ASSERT(msecs == INFINITE);
+ msecs = 0;
+ continue;
+ }
+
+ QWinOverlappedIoNotifierPrivate *notifier
+ = reinterpret_cast<QWinOverlappedIoNotifierPrivate *>(pulCompletionKey);
+ mutex.lock();
+ if (notifiers.contains(notifier))
+ notifier->notify(dwBytesRead, errorCode, overlapped);
+ mutex.unlock();
+ }
+ }
+
+private:
+ const ULONG_PTR finishThreadKey;
+ const ULONG_PTR drainQueueKey;
+ HANDLE hPort = INVALID_HANDLE_VALUE;
+ QSet<QWinOverlappedIoNotifierPrivate *> notifiers;
+ QMutex mutex;
+ QMutex drainQueueMutex;
+ HANDLE hQueueDrainedEvent = nullptr;
+};
+
+
+QWinOverlappedIoNotifier::QWinOverlappedIoNotifier(QObject *parent)
+ : QObject(*new QWinOverlappedIoNotifierPrivate, parent)
+{
+ Q_D(QWinOverlappedIoNotifier);
+ WaitForSingleObject(d->iocpInstanceLock, INFINITE);
+ if (!d->iocp)
+ d->iocp = new QWinIoCompletionPort;
+ d->iocpInstanceRefCount++;
+ ReleaseMutex(d->iocpInstanceLock);
+
+ d->hSemaphore = CreateSemaphore(NULL, 0, 255, NULL);
+ d->hResultsMutex = CreateMutex(NULL, FALSE, NULL);
+ connect(this, SIGNAL(_q_notify()), this, SLOT(_q_notified()), Qt::QueuedConnection);
+}
+
+QWinOverlappedIoNotifier::~QWinOverlappedIoNotifier()
+{
+ Q_D(QWinOverlappedIoNotifier);
+ setEnabled(false);
+ CloseHandle(d->hResultsMutex);
+ CloseHandle(d->hSemaphore);
+
+ WaitForSingleObject(d->iocpInstanceLock, INFINITE);
+ if (!--d->iocpInstanceRefCount) {
+ delete d->iocp;
+ d->iocp = 0;
+ }
+ ReleaseMutex(d->iocpInstanceLock);
+}
+
+void QWinOverlappedIoNotifier::setHandle(Qt::HANDLE h)
+{
+ Q_D(QWinOverlappedIoNotifier);
+ d->hHandle = h;
+}
+
+Qt::HANDLE QWinOverlappedIoNotifier::handle() const
+{
+ Q_D(const QWinOverlappedIoNotifier);
+ return d->hHandle;
+}
+
+void QWinOverlappedIoNotifier::setEnabled(bool enabled)
+{
+ Q_D(QWinOverlappedIoNotifier);
+ if (enabled)
+ d->iocp->registerNotifier(d);
+ else
+ d->iocp->unregisterNotifier(d);
+}
+
+OVERLAPPED *QWinOverlappedIoNotifierPrivate::waitForAnyNotified(QDeadlineTimer deadline)
+{
+ if (!iocp->isRunning()) {
+ qWarning("Called QWinOverlappedIoNotifier::waitForAnyNotified on inactive notifier.");
+ return 0;
+ }
+
+ DWORD msecs = deadline.remainingTime();
+ if (msecs == 0)
+ iocp->drainQueue();
+ if (msecs == -1)
+ msecs = INFINITE;
+
+ const DWORD wfso = WaitForSingleObject(hSemaphore, msecs);
+ switch (wfso) {
+ case WAIT_OBJECT_0:
+ return dispatchNextIoResult();
+ case WAIT_TIMEOUT:
+ return 0;
+ default:
+ qErrnoWarning("QWinOverlappedIoNotifier::waitForAnyNotified: WaitForSingleObject failed.");
+ return 0;
+ }
+}
+
+class QScopedAtomicIntIncrementor
+{
+public:
+ QScopedAtomicIntIncrementor(QAtomicInt &i)
+ : m_int(i)
+ {
+ ++m_int;
+ }
+
+ ~QScopedAtomicIntIncrementor()
+ {
+ --m_int;
+ }
+
+private:
+ QAtomicInt &m_int;
+};
+
+/*!
+ * Wait synchronously for any notified signal.
+ *
+ * The function returns a pointer to the OVERLAPPED object corresponding to the completed I/O
+ * operation. In case no I/O operation was completed during the \a msec timeout, this function
+ * returns a null pointer.
+ */
+OVERLAPPED *QWinOverlappedIoNotifier::waitForAnyNotified(QDeadlineTimer deadline)
+{
+ Q_D(QWinOverlappedIoNotifier);
+ QScopedAtomicIntIncrementor saii(d->waiting);
+ OVERLAPPED *result = d->waitForAnyNotified(deadline);
+ return result;
+}
+
+/*!
+ * Wait synchronously for the notified signal.
+ *
+ * The function returns true if the notified signal was emitted for
+ * the I/O operation that corresponds to the OVERLAPPED object.
+ */
+bool QWinOverlappedIoNotifier::waitForNotified(QDeadlineTimer deadline, OVERLAPPED *overlapped)
+{
+ Q_D(QWinOverlappedIoNotifier);
+ QScopedAtomicIntIncrementor saii(d->waiting);
+ while (!deadline.hasExpired()) {
+ OVERLAPPED *triggeredOverlapped = waitForAnyNotified(deadline);
+ if (!triggeredOverlapped)
+ return false;
+ if (triggeredOverlapped == overlapped)
+ return true;
+ }
+ return false;
+}
+
+/*!
+ * Note: This function runs in the I/O completion port thread.
+ */
+void QWinOverlappedIoNotifierPrivate::notify(DWORD numberOfBytes, DWORD errorCode,
+ OVERLAPPED *overlapped)
+{
+ Q_Q(QWinOverlappedIoNotifier);
+ WaitForSingleObject(hResultsMutex, INFINITE);
+ results.enqueue(IOResult(numberOfBytes, errorCode, overlapped));
+ ReleaseMutex(hResultsMutex);
+ ReleaseSemaphore(hSemaphore, 1, NULL);
+ if (!waiting)
+ emit q->_q_notify();
+}
+
+void QWinOverlappedIoNotifierPrivate::_q_notified()
+{
+ if (WaitForSingleObject(hSemaphore, 0) == WAIT_OBJECT_0)
+ dispatchNextIoResult();
+}
+
+OVERLAPPED *QWinOverlappedIoNotifierPrivate::dispatchNextIoResult()
+{
+ Q_Q(QWinOverlappedIoNotifier);
+ WaitForSingleObject(hResultsMutex, INFINITE);
+ IOResult ioresult = results.dequeue();
+ ReleaseMutex(hResultsMutex);
+ emit q->notified(ioresult.numberOfBytes, ioresult.errorCode, ioresult.overlapped);
+ return ioresult.overlapped;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwinoverlappedionotifier_p.cpp"
diff --git a/src/serialport/qwinoverlappedionotifier_p.h b/src/serialport/qwinoverlappedionotifier_p.h
new file mode 100644
index 0000000..9ee998b
--- /dev/null
+++ b/src/serialport/qwinoverlappedionotifier_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINOVERLAPPEDIONOTIFIER_P_H
+#define QWINOVERLAPPEDIONOTIFIER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qglobal_p.h>
+#include <qobject.h>
+#include <qdeadlinetimer.h>
+
+typedef struct _OVERLAPPED OVERLAPPED;
+
+QT_BEGIN_NAMESPACE
+
+class QWinOverlappedIoNotifierPrivate;
+
+class QWinOverlappedIoNotifier : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(QWinOverlappedIoNotifier)
+ Q_DECLARE_PRIVATE(QWinOverlappedIoNotifier)
+ Q_PRIVATE_SLOT(d_func(), void _q_notified())
+ friend class QWinIoCompletionPort;
+public:
+ QWinOverlappedIoNotifier(QObject *parent = 0);
+ ~QWinOverlappedIoNotifier();
+
+ void setHandle(Qt::HANDLE h);
+ Qt::HANDLE handle() const;
+
+ void setEnabled(bool enabled);
+ OVERLAPPED *waitForAnyNotified(QDeadlineTimer deadline);
+ bool waitForNotified(QDeadlineTimer deadline, OVERLAPPED *overlapped);
+
+Q_SIGNALS:
+ void notified(quint32 numberOfBytes, quint32 errorCode, OVERLAPPED *overlapped);
+#if !defined(Q_QDOC)
+ void _q_notify();
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINOVERLAPPEDIONOTIFIER_P_H
diff --git a/src/serialport/serialport-lib.pri b/src/serialport/serialport-lib.pri
index 9f83989..bb12df2 100644
--- a/src/serialport/serialport-lib.pri
+++ b/src/serialport/serialport-lib.pri
@@ -22,7 +22,11 @@ SOURCES += \
win32:!wince* {
SOURCES += \
$$PWD/qserialport_win.cpp \
- $$PWD/qserialportinfo_win.cpp
+ $$PWD/qserialportinfo_win.cpp \
+ $$PWD/qwinoverlappedionotifier.cpp
+
+ PRIVATE_HEADERS += \
+ $$PWD/qwinoverlappedionotifier_p.h
LIBS_PRIVATE += -lsetupapi -ladvapi32
}