summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Shienkov <denis.shienkov@gmail.com>2019-05-13 18:58:19 +0300
committerDenis Shienkov <denis.shienkov@gmail.com>2019-06-08 13:16:14 +0300
commita0faf986fccdce1d36f3308dc520cdac001f5264 (patch)
tree6353717c6d50e7457c1acd9eca12c94f6335b379
parent450156f37356f67f217de760a5df3149684dd99f (diff)
downloadqtserialport-a0faf986fccdce1d36f3308dc520cdac001f5264.tar.gz
QSerialPort: Port to alertable I/O functions on Windows
This commit removes the QWinOverlappedIoNotifier in favor of alertable I/O functions like {Read|Write}FileEx(). The reason is that the QWinOverlappedIoNotifier is very complex to maintain. To use the alertable functions in the serial port we need an alertable analog of WaitCommEvent function. This function does not exist in the Win32 API, but we implement it trough a set of the system NtXXX and RtlXXX functions, which are resolved dynamically. This patch was tested with auto-tests and the examples using the: * com0com virtual serial port driver. * eltima virtual serial port driver. * pl2303 USB/serial converters. Task-number: QTBUG-74961 Change-Id: Idc428173eee7a1066a4693de00aa38416c4ee86c Reviewed-by: Alex Blasche <alexander.blasche@qt.io> Reviewed-by: Jörg Bornemann <joerg.bornemann@qt.io>
-rw-r--r--src/serialport/qserialport.h1
-rw-r--r--src/serialport/qserialport_p.h18
-rw-r--r--src/serialport/qserialport_win.cpp364
-rw-r--r--src/serialport/qtntdll_p.h158
-rw-r--r--src/serialport/qwinoverlappedionotifier.cpp420
-rw-r--r--src/serialport/qwinoverlappedionotifier_p.h91
-rw-r--r--src/serialport/serialport-lib.pri3
7 files changed, 418 insertions, 637 deletions
diff --git a/src/serialport/qserialport.h b/src/serialport/qserialport.h
index 9a5c1d0..c4c7367 100644
--- a/src/serialport/qserialport.h
+++ b/src/serialport/qserialport.h
@@ -306,7 +306,6 @@ private:
#if defined(Q_OS_WIN32)
Q_PRIVATE_SLOT(d_func(), bool _q_startAsyncWrite())
- Q_PRIVATE_SLOT(d_func(), void _q_notified(quint32, quint32, OVERLAPPED*))
#endif
};
diff --git a/src/serialport/qserialport_p.h b/src/serialport/qserialport_p.h
index b0955ae..7348aed 100644
--- a/src/serialport/qserialport_p.h
+++ b/src/serialport/qserialport_p.h
@@ -104,7 +104,6 @@ struct serial_struct {
QT_BEGIN_NAMESPACE
-class QWinOverlappedIoNotifier;
class QTimer;
class QSocketNotifier;
@@ -182,7 +181,6 @@ public:
bool setDcb(DCB *dcb);
bool getDcb(DCB *dcb);
- OVERLAPPED *waitForNotified(QDeadlineTimer deadline);
qint64 queuedBytesCount(QSerialPort::Direction direction) const;
@@ -192,10 +190,15 @@ public:
bool startAsyncCommunication();
bool _q_startAsyncWrite();
- void _q_notified(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped);
+ void handleNotification(DWORD bytesTransferred, DWORD errorCode,
+ OVERLAPPED *overlapped);
void emitReadyRead();
+ static void CALLBACK ioCompletionRoutine(
+ DWORD errorCode, DWORD bytesTransfered,
+ OVERLAPPED *overlappedBase);
+
DCB restoredDcb;
COMMTIMEOUTS currentCommTimeouts;
COMMTIMEOUTS restoredCommTimeouts;
@@ -205,11 +208,12 @@ public:
bool communicationStarted = false;
bool writeStarted = false;
bool readStarted = false;
- QWinOverlappedIoNotifier *notifier = nullptr;
+ qint64 writeBytesTransferred = 0;
+ qint64 readBytesTransferred = 0;
QTimer *startAsyncWriteTimer = nullptr;
- OVERLAPPED communicationOverlapped;
- OVERLAPPED readCompletionOverlapped;
- OVERLAPPED writeCompletionOverlapped;
+ class Overlapped *communicationCompletionOverlapped = nullptr;
+ class Overlapped *readCompletionOverlapped = nullptr;
+ class Overlapped *writeCompletionOverlapped = nullptr;
DWORD triggeredEventMask = 0;
#elif defined(Q_OS_UNIX)
diff --git a/src/serialport/qserialport_win.cpp b/src/serialport/qserialport_win.cpp
index 85dd8ee..da1e7aa 100644
--- a/src/serialport/qserialport_win.cpp
+++ b/src/serialport/qserialport_win.cpp
@@ -40,45 +40,15 @@
****************************************************************************/
#include "qserialport_p.h"
-#include "qwinoverlappedionotifier_p.h"
+#include "qtntdll_p.h"
#include <QtCore/qcoreevent.h>
#include <QtCore/qelapsedtimer.h>
-#include <QtCore/qvector.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qtimer.h>
+#include <QtCore/qvector.h>
#include <algorithm>
-#ifndef CTL_CODE
-# define CTL_CODE(DeviceType, Function, Method, Access) ( \
- ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
- )
-#endif
-
-#ifndef FILE_DEVICE_SERIAL_PORT
-# define FILE_DEVICE_SERIAL_PORT 27
-#endif
-
-#ifndef METHOD_BUFFERED
-# define METHOD_BUFFERED 0
-#endif
-
-#ifndef FILE_ANY_ACCESS
-# define FILE_ANY_ACCESS 0x00000000
-#endif
-
-#ifndef IOCTL_SERIAL_GET_DTRRTS
-# define IOCTL_SERIAL_GET_DTRRTS \
- CTL_CODE(FILE_DEVICE_SERIAL_PORT, 30, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#endif
-
-#ifndef SERIAL_DTR_STATE
-# define SERIAL_DTR_STATE 0x00000001
-#endif
-
-#ifndef SERIAL_RTS_STATE
-# define SERIAL_RTS_STATE 0x00000002
-#endif
-
QT_BEGIN_NAMESPACE
static inline void qt_set_common_props(DCB *dcb)
@@ -173,8 +143,117 @@ static inline void qt_set_flowcontrol(DCB *dcb, QSerialPort::FlowControl flowcon
}
}
+// Translate NT-callbacks to Win32 callbacks.
+static VOID WINAPI qt_apc_routine(
+ PVOID context,
+ PIO_STATUS_BLOCK ioStatusBlock,
+ DWORD reserved)
+{
+ Q_UNUSED(reserved);
+
+ const DWORD errorCode = ::RtlNtStatusToDosError(ioStatusBlock->Status);
+ const DWORD bytesTransfered = NT_SUCCESS(ioStatusBlock->Status)
+ ? DWORD(ioStatusBlock->Information) : 0;
+ const LPOVERLAPPED overlapped = CONTAINING_RECORD(ioStatusBlock,
+ OVERLAPPED, Internal);
+
+ (reinterpret_cast<LPOVERLAPPED_COMPLETION_ROUTINE>(context))
+ (errorCode, bytesTransfered, overlapped);
+}
+
+// Alertable analog of DeviceIoControl function.
+static BOOL qt_device_io_control_ex(
+ HANDLE deviceHandle,
+ DWORD ioControlCode,
+ LPVOID inputBuffer,
+ DWORD inputBufferSize,
+ LPVOID outputBuffer,
+ DWORD outputBufferSize,
+ LPOVERLAPPED overlapped,
+ LPOVERLAPPED_COMPLETION_ROUTINE completionRoutine)
+{
+ const auto ioStatusBlock = reinterpret_cast<PIO_STATUS_BLOCK>(
+ &overlapped->Internal);
+ ioStatusBlock->Status = STATUS_PENDING;
+
+ const NTSTATUS status = ::NtDeviceIoControlFile(
+ deviceHandle,
+ nullptr,
+ qt_apc_routine,
+ reinterpret_cast<PVOID>(completionRoutine),
+ ioStatusBlock,
+ ioControlCode,
+ inputBuffer,
+ inputBufferSize,
+ outputBuffer,
+ outputBufferSize);
+
+ if (!NT_SUCCESS(status)) {
+ ::SetLastError(::RtlNtStatusToDosError(status));
+ return false;
+ }
+
+ return true;
+}
+
+// Alertable analog of WaitCommEvent function.
+static BOOL qt_wait_comm_event_ex(
+ HANDLE deviceHandle,
+ LPDWORD eventsMask,
+ LPOVERLAPPED overlapped,
+ LPOVERLAPPED_COMPLETION_ROUTINE completionRoutine)
+{
+ return qt_device_io_control_ex(
+ deviceHandle,
+ IOCTL_SERIAL_WAIT_ON_MASK,
+ nullptr,
+ 0,
+ eventsMask,
+ sizeof(DWORD),
+ overlapped,
+ completionRoutine);
+}
+
+struct RuntimeHelper
+{
+ QLibrary ntLibrary;
+ QBasicMutex mutex;
+};
+
+Q_GLOBAL_STATIC(RuntimeHelper, helper)
+
+class Overlapped final : public OVERLAPPED
+{
+ Q_DISABLE_COPY(Overlapped)
+public:
+ explicit Overlapped(QSerialPortPrivate *d);
+ void clear();
+
+ QSerialPortPrivate *dptr = nullptr;
+};
+
+Overlapped::Overlapped(QSerialPortPrivate *d)
+ : dptr(d)
+{
+}
+
+void Overlapped::clear()
+{
+ ::ZeroMemory(this, sizeof(OVERLAPPED));
+}
+
bool QSerialPortPrivate::open(QIODevice::OpenMode mode)
{
+ {
+ QMutexLocker locker(&helper()->mutex);
+ static bool symbolsResolved = resolveSymbols(&helper()->ntLibrary);
+ if (!symbolsResolved) {
+ setError(QSerialPortErrorInfo(QSerialPort::OpenError,
+ helper()->ntLibrary.errorString()));
+ return false;
+ }
+ }
+
DWORD desiredAccess = 0;
if (mode & QIODevice::ReadOnly)
@@ -199,17 +278,44 @@ bool QSerialPortPrivate::open(QIODevice::OpenMode mode)
void QSerialPortPrivate::close()
{
- ::CancelIo(handle);
-
- delete notifier;
- notifier = nullptr;
-
delete startAsyncWriteTimer;
startAsyncWriteTimer = nullptr;
- communicationStarted = false;
- readStarted = false;
- writeStarted = false;
+ if (communicationStarted) {
+ communicationCompletionOverlapped->dptr = nullptr;
+ ::CancelIoEx(handle, communicationCompletionOverlapped);
+ // The object will be deleted in the I/O callback.
+ communicationCompletionOverlapped = nullptr;
+ communicationStarted = false;
+ } else {
+ delete communicationCompletionOverlapped;
+ communicationCompletionOverlapped = nullptr;
+ }
+
+ if (readStarted) {
+ readCompletionOverlapped->dptr = nullptr;
+ ::CancelIoEx(handle, readCompletionOverlapped);
+ // The object will be deleted in the I/O callback.
+ readCompletionOverlapped = nullptr;
+ readStarted = false;
+ } else {
+ delete readCompletionOverlapped;
+ readCompletionOverlapped = nullptr;
+ };
+
+ if (writeStarted) {
+ writeCompletionOverlapped->dptr = nullptr;
+ ::CancelIoEx(handle, writeCompletionOverlapped);
+ // The object will be deleted in the I/O callback.
+ writeCompletionOverlapped = nullptr;
+ writeStarted = false;
+ } else {
+ delete writeCompletionOverlapped;
+ writeCompletionOverlapped = nullptr;
+ }
+
+ readBytesTransferred = 0;
+ writeBytesTransferred = 0;
writeBuffer.clear();
if (settingsRestoredOnClose) {
@@ -341,30 +447,25 @@ bool QSerialPortPrivate::waitForReadyRead(int msecs)
if (!writeStarted && !_q_startAsyncWrite())
return false;
- const qint64 initialReadBufferSize = buffer.size();
- qint64 currentReadBufferSize = initialReadBufferSize;
-
QDeadlineTimer deadline(msecs);
do {
- const OVERLAPPED *overlapped = waitForNotified(deadline);
- if (!overlapped)
- return false;
-
- if (overlapped == &readCompletionOverlapped) {
- const qint64 readBytesForOneReadOperation = qint64(buffer.size()) - currentReadBufferSize;
- if (readBytesForOneReadOperation == QSERIALPORT_BUFFERSIZE) {
- currentReadBufferSize = buffer.size();
- } else if (readBytesForOneReadOperation == 0) {
- if (initialReadBufferSize != currentReadBufferSize)
- return true;
- } else {
- return true;
- }
+ if (readBytesTransferred <= 0) {
+ const qint64 remaining = deadline.remainingTime();
+ const DWORD result = ::SleepEx(
+ remaining == -1 ? INFINITE : DWORD(remaining),
+ TRUE);
+ if (result != WAIT_IO_COMPLETION)
+ continue;
}
+ if (readBytesTransferred > 0) {
+ readBytesTransferred = 0;
+ return true;
+ }
} while (!deadline.hasExpired());
+ setError(getSystemError(WAIT_TIMEOUT));
return false;
}
@@ -378,15 +479,23 @@ bool QSerialPortPrivate::waitForBytesWritten(int msecs)
QDeadlineTimer deadline(msecs);
- for (;;) {
- const OVERLAPPED *overlapped = waitForNotified(deadline);
- if (!overlapped)
- return false;
+ do {
+ if (writeBytesTransferred <= 0) {
+ const qint64 remaining = deadline.remainingTime();
+ const DWORD result = ::SleepEx(
+ remaining == -1 ? INFINITE : DWORD(remaining),
+ TRUE);
+ if (result != WAIT_IO_COMPLETION)
+ continue;
+ }
- if (overlapped == &writeCompletionOverlapped)
+ if (writeBytesTransferred > 0) {
+ writeBytesTransferred = 0;
return true;
- }
+ }
+ } while (!deadline.hasExpired());
+ setError(getSystemError(WAIT_TIMEOUT));
return false;
}
@@ -467,6 +576,10 @@ bool QSerialPortPrivate::completeAsyncCommunication(qint64 bytesTransferred)
bool QSerialPortPrivate::completeAsyncRead(qint64 bytesTransferred)
{
+ // Store the number of transferred bytes which are
+ // required only in waitForReadyRead() method.
+ readBytesTransferred = bytesTransferred;
+
if (bytesTransferred == qint64(-1)) {
readStarted = false;
return false;
@@ -494,6 +607,10 @@ bool QSerialPortPrivate::completeAsyncWrite(qint64 bytesTransferred)
{
Q_Q(QSerialPort);
+ // Store the number of transferred bytes which are
+ // required only in waitForBytesWritten() method.
+ writeBytesTransferred = bytesTransferred;
+
if (writeStarted) {
if (bytesTransferred == qint64(-1)) {
writeChunkBuffer.clear();
@@ -514,8 +631,16 @@ bool QSerialPortPrivate::startAsyncCommunication()
if (communicationStarted)
return true;
- ::ZeroMemory(&communicationOverlapped, sizeof(communicationOverlapped));
- if (!::WaitCommEvent(handle, &triggeredEventMask, &communicationOverlapped)) {
+ if (!communicationCompletionOverlapped)
+ communicationCompletionOverlapped = new Overlapped(this);
+
+ communicationCompletionOverlapped->clear();
+ communicationStarted = true;
+ if (!::qt_wait_comm_event_ex(handle,
+ &triggeredEventMask,
+ communicationCompletionOverlapped,
+ ioCompletionRoutine)) {
+ communicationStarted = false;
QSerialPortErrorInfo error = getSystemError();
if (error.errorCode != QSerialPort::NoError) {
if (error.errorCode == QSerialPort::PermissionError)
@@ -524,7 +649,6 @@ bool QSerialPortPrivate::startAsyncCommunication()
return false;
}
}
- communicationStarted = true;
return true;
}
@@ -546,23 +670,27 @@ bool QSerialPortPrivate::startAsyncRead()
Q_ASSERT(int(bytesToRead) <= readChunkBuffer.size());
- ::ZeroMemory(&readCompletionOverlapped, sizeof(readCompletionOverlapped));
- if (::ReadFile(handle, readChunkBuffer.data(), bytesToRead, nullptr, &readCompletionOverlapped)) {
- readStarted = true;
- return true;
- }
-
- QSerialPortErrorInfo error = getSystemError();
- if (error.errorCode != QSerialPort::NoError) {
- if (error.errorCode == QSerialPort::PermissionError)
- error.errorCode = QSerialPort::ResourceError;
- if (error.errorCode != QSerialPort::ResourceError)
- error.errorCode = QSerialPort::ReadError;
- setError(error);
- return false;
- }
+ if (!readCompletionOverlapped)
+ readCompletionOverlapped = new Overlapped(this);
+ readCompletionOverlapped->clear();
readStarted = true;
+ if (!::ReadFileEx(handle,
+ readChunkBuffer.data(),
+ bytesToRead,
+ readCompletionOverlapped,
+ ioCompletionRoutine)) {
+ readStarted = false;
+ QSerialPortErrorInfo error = getSystemError();
+ if (error.errorCode != QSerialPort::NoError) {
+ if (error.errorCode == QSerialPort::PermissionError)
+ error.errorCode = QSerialPort::ResourceError;
+ if (error.errorCode != QSerialPort::ResourceError)
+ error.errorCode = QSerialPort::ReadError;
+ setError(error);
+ return false;
+ }
+ }
return true;
}
@@ -572,10 +700,18 @@ bool QSerialPortPrivate::_q_startAsyncWrite()
return true;
writeChunkBuffer = writeBuffer.read();
- ::ZeroMemory(&writeCompletionOverlapped, sizeof(writeCompletionOverlapped));
- if (!::WriteFile(handle, writeChunkBuffer.constData(),
- writeChunkBuffer.size(), nullptr, &writeCompletionOverlapped)) {
+ if (!writeCompletionOverlapped)
+ writeCompletionOverlapped = new Overlapped(this);
+
+ writeCompletionOverlapped->clear();
+ writeStarted = true;
+ if (!::WriteFileEx(handle,
+ writeChunkBuffer.constData(),
+ writeChunkBuffer.size(),
+ writeCompletionOverlapped,
+ ioCompletionRoutine)) {
+ writeStarted = false;
QSerialPortErrorInfo error = getSystemError();
if (error.errorCode != QSerialPort::NoError) {
if (error.errorCode != QSerialPort::ResourceError)
@@ -584,25 +720,29 @@ bool QSerialPortPrivate::_q_startAsyncWrite()
return false;
}
}
-
- writeStarted = true;
return true;
}
-void QSerialPortPrivate::_q_notified(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped)
+void QSerialPortPrivate::handleNotification(DWORD bytesTransferred, DWORD errorCode,
+ OVERLAPPED *overlapped)
{
+ // This occurred e.g. after calling the CloseHandle() function,
+ // just skip handling at all.
+ if (handle == INVALID_HANDLE_VALUE)
+ return;
+
const QSerialPortErrorInfo error = getSystemError(errorCode);
if (error.errorCode != QSerialPort::NoError) {
setError(error);
return;
}
- if (overlapped == &communicationOverlapped)
- completeAsyncCommunication(numberOfBytes);
- else if (overlapped == &readCompletionOverlapped)
- completeAsyncRead(numberOfBytes);
- else if (overlapped == &writeCompletionOverlapped)
- completeAsyncWrite(numberOfBytes);
+ if (overlapped == communicationCompletionOverlapped)
+ completeAsyncCommunication(bytesTransferred);
+ else if (overlapped == readCompletionOverlapped)
+ completeAsyncRead(bytesTransferred);
+ else if (overlapped == writeCompletionOverlapped)
+ completeAsyncWrite(bytesTransferred);
else
Q_ASSERT(!"Unknown OVERLAPPED activated");
}
@@ -632,16 +772,6 @@ qint64 QSerialPortPrivate::writeData(const char *data, qint64 maxSize)
return maxSize;
}
-OVERLAPPED *QSerialPortPrivate::waitForNotified(QDeadlineTimer deadline)
-{
- OVERLAPPED *overlapped = notifier->waitForAnyNotified(deadline);
- if (!overlapped) {
- setError(getSystemError(WAIT_TIMEOUT));
- return nullptr;
- }
- return overlapped;
-}
-
qint64 QSerialPortPrivate::queuedBytesCount(QSerialPort::Direction direction) const
{
COMSTAT comstat;
@@ -654,8 +784,6 @@ qint64 QSerialPortPrivate::queuedBytesCount(QSerialPort::Direction direction) co
inline bool QSerialPortPrivate::initialize(QIODevice::OpenMode mode)
{
- Q_Q(QSerialPort);
-
DCB dcb;
if (!getDcb(&dcb))
return false;
@@ -691,17 +819,8 @@ inline bool QSerialPortPrivate::initialize(QIODevice::OpenMode mode)
return false;
}
- notifier = new QWinOverlappedIoNotifier(q);
- QObjectPrivate::connect(notifier, &QWinOverlappedIoNotifier::notified,
- this, &QSerialPortPrivate::_q_notified);
- notifier->setHandle(handle);
- notifier->setEnabled(true);
-
- if ((eventMask & EV_RXCHAR) && !startAsyncCommunication()) {
- delete notifier;
- notifier = nullptr;
+ if ((eventMask & EV_RXCHAR) && !startAsyncCommunication())
return false;
- }
return true;
}
@@ -801,4 +920,17 @@ QSerialPort::Handle QSerialPort::handle() const
return d->handle;
}
+void QSerialPortPrivate::ioCompletionRoutine(
+ DWORD errorCode, DWORD bytesTransfered,
+ OVERLAPPED *overlappedBase)
+{
+ const auto overlapped = static_cast<Overlapped *>(overlappedBase);
+ if (overlapped->dptr) {
+ overlapped->dptr->handleNotification(bytesTransfered, errorCode,
+ overlappedBase);
+ } else {
+ delete overlapped;
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/serialport/qtntdll_p.h b/src/serialport/qtntdll_p.h
new file mode 100644
index 0000000..a8cdaf6
--- /dev/null
+++ b/src/serialport/qtntdll_p.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtSerialPort 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 QTNTDLL_P_H
+#define QTNTDLL_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/qlibrary.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qdebug.h>
+
+#include <qt_windows.h>
+
+// Internal control codes.
+
+#ifndef CTL_CODE
+# define CTL_CODE(DeviceType, Function, Method, Access) ( \
+ ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
+ )
+#endif
+
+#ifndef FILE_DEVICE_SERIAL_PORT
+# define FILE_DEVICE_SERIAL_PORT 27
+#endif
+
+#ifndef METHOD_BUFFERED
+# define METHOD_BUFFERED 0
+#endif
+
+#ifndef FILE_ANY_ACCESS
+# define FILE_ANY_ACCESS 0x00000000
+#endif
+
+#ifndef IOCTL_SERIAL_GET_DTRRTS
+# define IOCTL_SERIAL_GET_DTRRTS \
+ CTL_CODE(FILE_DEVICE_SERIAL_PORT, 30, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
+
+#ifndef SERIAL_DTR_STATE
+# define SERIAL_DTR_STATE 0x00000001
+#endif
+
+#ifndef SERIAL_RTS_STATE
+# define SERIAL_RTS_STATE 0x00000002
+#endif
+
+#ifndef IOCTL_SERIAL_WAIT_ON_MASK
+# define IOCTL_SERIAL_WAIT_ON_MASK \
+ CTL_CODE(FILE_DEVICE_SERIAL_PORT, 18, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
+
+// Internal NT-based data types.
+
+#ifndef NT_SUCCESS
+#define NT_SUCCESS(status) (((NTSTATUS)(status)) >= 0)
+#endif
+
+typedef struct _IO_STATUS_BLOCK {
+ union {
+ NTSTATUS Status;
+ PVOID Pointer;
+ } DUMMYUNIONNAME;
+
+ ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+typedef VOID (WINAPI *PIO_APC_ROUTINE) (
+ PVOID ApcContext,
+ PIO_STATUS_BLOCK IoStatusBlock,
+ ULONG Reserved
+ );
+
+// Resolving macros.
+
+#define GENERATE_SYMBOL_VARIABLE(returnType, symbolName, ...) \
+ typedef returnType (WINAPI *fp_##symbolName)(__VA_ARGS__); \
+ static fp_##symbolName symbolName;
+
+#define RESOLVE_SYMBOL(symbolName) \
+ symbolName = reinterpret_cast<fp_##symbolName>(resolveSymbol(ntLibrary, #symbolName)); \
+ if (!symbolName) \
+ return false;
+
+GENERATE_SYMBOL_VARIABLE(ULONG, RtlNtStatusToDosError, NTSTATUS)
+GENERATE_SYMBOL_VARIABLE(NTSTATUS, NtDeviceIoControlFile, HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, ULONG, PVOID, ULONG, PVOID, ULONG)
+
+inline QFunctionPointer resolveSymbol(QLibrary *ntLibrary, const char *symbolName)
+{
+ QFunctionPointer symbolFunctionPointer = ntLibrary->resolve(symbolName);
+ if (!symbolFunctionPointer)
+ qWarning("Failed to resolve the symbol: %s", symbolName);
+
+ return symbolFunctionPointer;
+}
+
+inline bool resolveSymbols(QLibrary *ntLibrary)
+{
+ if (!ntLibrary->isLoaded()) {
+ ntLibrary->setFileName(QStringLiteral("ntdll"));
+ if (!ntLibrary->load()) {
+ qWarning("Failed to load the library: %s", qPrintable(ntLibrary->fileName()));
+ return false;
+ }
+ }
+
+ RESOLVE_SYMBOL(RtlNtStatusToDosError)
+ RESOLVE_SYMBOL(NtDeviceIoControlFile)
+
+ return true;
+}
+
+#endif // QTNTDLL_P_H
diff --git a/src/serialport/qwinoverlappedionotifier.cpp b/src/serialport/qwinoverlappedionotifier.cpp
deleted file mode 100644
index 615dacc..0000000
--- a/src/serialport/qwinoverlappedionotifier.cpp
+++ /dev/null
@@ -1,420 +0,0 @@
-/****************************************************************************
-**
-** 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
deleted file mode 100644
index 9ee998b..0000000
--- a/src/serialport/qwinoverlappedionotifier_p.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/****************************************************************************
-**
-** 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 ddd31f2..b9b3c83 100644
--- a/src/serialport/serialport-lib.pri
+++ b/src/serialport/serialport-lib.pri
@@ -23,10 +23,9 @@ win32 {
SOURCES += \
$$PWD/qserialport_win.cpp \
$$PWD/qserialportinfo_win.cpp \
- $$PWD/qwinoverlappedionotifier.cpp
PRIVATE_HEADERS += \
- $$PWD/qwinoverlappedionotifier_p.h
+ $$PWD/qtntdll_p.h
LIBS_PRIVATE += -lsetupapi -ladvapi32
}