diff options
-rw-r--r-- | src/serialport/qserialport.h | 1 | ||||
-rw-r--r-- | src/serialport/qserialport_p.h | 18 | ||||
-rw-r--r-- | src/serialport/qserialport_win.cpp | 364 | ||||
-rw-r--r-- | src/serialport/qtntdll_p.h | 158 | ||||
-rw-r--r-- | src/serialport/qwinoverlappedionotifier.cpp | 420 | ||||
-rw-r--r-- | src/serialport/qwinoverlappedionotifier_p.h | 91 | ||||
-rw-r--r-- | src/serialport/serialport-lib.pri | 3 |
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 } |