summaryrefslogtreecommitdiffstats
path: root/src/serialportengine_win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/serialportengine_win.cpp')
-rw-r--r--src/serialportengine_win.cpp1305
1 files changed, 1305 insertions, 0 deletions
diff --git a/src/serialportengine_win.cpp b/src/serialportengine_win.cpp
new file mode 100644
index 00000000..b2b87d66
--- /dev/null
+++ b/src/serialportengine_win.cpp
@@ -0,0 +1,1305 @@
+/*
+ License...
+*/
+
+/*!
+ \class WinSerialPortEngine
+ \internal
+
+ \brief The WinSerialPortEngine class provides windows OS
+ platform-specific low level access to a serial port.
+
+ \reentrant
+ \ingroup serial
+ \inmodule QSerialDevice
+
+ Currently the class supports as NT-based OS (Win 2K/XP/Vista/7),
+ and as various embedded WinCE.
+
+ WinSerialPortEngine (as well as other platform-dependent engines)
+ is a class with multiple inheritance, which on the one hand,
+ derives from a general abstract class interface SerialPortEngine,
+ on the other hand of a class inherited from QObject.
+
+ From the abstract class SerialPortEngine, it inherits all virtual
+ interface methods that are common to all serial ports on any platform.
+ These methods, the class WinSerialPortEngine implements on the OS
+ Windows platform, using a corresponding Win API.
+
+ From QObject-like class, it inherits a specific system Qt features.
+ For example, for NT-based platforms WinSerialPortEngine uses private
+ Qt class QWinEventNotifier. Thanks to this class, it have the
+ opportunity to asynchronously track the events from the serial port,
+ such as the appearance of a character in the receive buffer,
+ error I/O, and etc. Ie events are handled in Qt core in its event
+ loop, so no need to create additional threads to perform these
+ operations. However, for embedded systems, this approach does not work,
+ because they have a another Win API. In this case, WinSerialPortEngine
+ is derived from QThread and creates an additional thread to keep track
+ of events.
+
+ That is, as seen from the above, the functional WinSerialPortEngine
+ completely covers all the necessary tasks.
+*/
+
+#include "serialportengine_win_p.h"
+
+#include <QtCore/qregexp.h>
+#if !defined (Q_OS_WINCE)
+# include <QtCore/qcoreevent.h>
+#endif
+//#include <QtCore/QDebug>
+
+#ifndef Q_CC_MSVC
+# include <ddk/ntddser.h>
+#else
+
+# 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
+
+#endif
+
+QT_BEGIN_NAMESPACE_SERIALPORT
+
+/* Public methods */
+
+/*!
+ Constructs a WinSerialPortEngine with \a parent and
+ initializes all the internal variables of the initial values.
+
+ A pointer \a parent to the object class SerialPortPrivate
+ is required for the recursive call some of its methods.
+*/
+WinSerialPortEngine::WinSerialPortEngine(SerialPortPrivate *parent)
+ : m_descriptor(INVALID_HANDLE_VALUE)
+ , m_flagErrorFromCommEvent(false)
+ , m_currentMask(0)
+ , m_setMask(EV_ERR)
+ #if defined (Q_OS_WINCE)
+ , m_running(true)
+ #endif
+{
+ Q_ASSERT(parent);
+ m_parent = parent;
+ size_t size = sizeof(DCB);
+ ::memset(&m_currDCB, 0, size);
+ ::memset(&m_oldDCB, 0, size);
+ size = sizeof(COMMTIMEOUTS);
+ ::memset(&m_currCommTimeouts, 0, size);
+ ::memset(&m_oldCommTimeouts, 0, size);
+
+#if !defined (Q_OS_WINCE)
+ size = sizeof(OVERLAPPED);
+ ::memset(&m_ovRead, 0, size);
+ ::memset(&m_ovWrite, 0, size);
+ ::memset(&m_ovSelect, 0, size);
+ ::memset(&m_ov, 0, size);
+#endif
+}
+
+/*!
+ Stops the tracking events of the serial port and
+ destructs a WinSerialPortEngine.
+*/
+WinSerialPortEngine::~WinSerialPortEngine()
+{
+#if defined (Q_OS_WINCE)
+ m_running = false;
+ ::SetCommMask(m_descriptor, 0);
+ //terminate();
+ wait();
+#else
+ setEnabled(false);
+#endif
+}
+
+/*!
+ Tries to open the handle desired serial port by \a location in the
+ given open \a mode. In the process of discovery, always set a
+ serial port in non-blocking mode (when the read operation returns
+ immediately) and tries to determine its current configuration and
+ install them.
+
+ It should be noted the following features that Windows performs
+ when using the serial port:
+ - support only binary transfers mode
+ - always open in exclusive mode
+
+ For Windows NT-based platforms, the serial port is opened in the
+ overlapped mode, with flag FILE_FLAG_OVERLAPPED.
+
+ If successful, returns true; otherwise false, with the setup a
+ error code.
+*/
+bool WinSerialPortEngine::open(const QString &location, QIODevice::OpenMode mode)
+{
+ DWORD desiredAccess = 0;
+ DWORD shareMode = 0;
+ DWORD flagsAndAttributes = 0;
+ bool rxflag = false;
+ bool txflag = false;
+
+#if !defined (Q_OS_WINCE)
+ flagsAndAttributes |= FILE_FLAG_OVERLAPPED;
+#endif
+
+ if (mode & QIODevice::ReadOnly) {
+ desiredAccess |= GENERIC_READ;
+ //shareMode = FILE_SHARE_READ;
+ rxflag = true;
+ }
+ if (mode & QIODevice::WriteOnly) {
+ desiredAccess |= GENERIC_WRITE;
+ //shareMode = FILE_SHARE_WRITE;
+ txflag = true;
+ }
+
+ QByteArray filePath = QByteArray((const char *)location.utf16(),
+ location.size() * 2 + 1);
+
+ // Try opened serial device.
+ m_descriptor = ::CreateFile((const wchar_t*)filePath.constData(),
+ desiredAccess, shareMode, 0, OPEN_EXISTING, flagsAndAttributes, 0);
+
+ if (m_descriptor == INVALID_HANDLE_VALUE) {
+ switch (::GetLastError()) {
+ case ERROR_FILE_NOT_FOUND:
+ m_parent->setError(SerialPort::NoSuchDeviceError);
+ break;
+ case ERROR_ACCESS_DENIED:
+ m_parent->setError(SerialPort::PermissionDeniedError);
+ break;
+ default:
+ m_parent->setError(SerialPort::UnknownPortError);
+ }
+ return false;
+ }
+
+ // Save current DCB port settings.
+ DWORD confSize = sizeof(DCB);
+ if (::GetCommState(m_descriptor, &m_oldDCB) == 0) {
+ m_parent->setError(SerialPort::UnknownPortError);
+ return false;
+ }
+ ::memcpy(&m_currDCB, &m_oldDCB, confSize);
+
+ // Set other DCB port options.
+ m_currDCB.fBinary = true;
+ m_currDCB.fInX = false;
+ m_currDCB.fOutX = false;
+ m_currDCB.fAbortOnError = false;
+ m_currDCB.fNull = false;
+ m_currDCB.fErrorChar = false;
+
+ // Apply new DCB init settings.
+ if (!updateDcb())
+ return false;
+
+ // Save current port timeouts.
+ confSize = sizeof(COMMTIMEOUTS);
+ if (::GetCommTimeouts(m_descriptor, &m_oldCommTimeouts) == 0) {
+ m_parent->setError(SerialPort::UnknownPortError);
+ return false;
+ }
+ ::memcpy(&m_currCommTimeouts, &m_oldCommTimeouts, confSize);
+
+ // Set new port timeouts.
+ ::memset(&m_currCommTimeouts, 0, confSize);
+ m_currCommTimeouts.ReadIntervalTimeout = MAXDWORD;
+
+ // Apply new port timeouts.
+ if (!updateCommTimeouts())
+ return false;
+
+#if !defined (Q_OS_WINCE)
+ if (!createEvents(rxflag, txflag)) {
+ m_parent->setError(SerialPort::UnknownPortError);
+ return false;
+ }
+#endif
+
+ detectDefaultSettings();
+ return true;
+}
+
+/*!
+ Closes a serial port handle. Before closing - restore previous
+ serial port settings if necessary.
+*/
+void WinSerialPortEngine::close(const QString &location)
+{
+ Q_UNUSED(location);
+
+#if !defined (Q_OS_WINCE)
+ ::CancelIo(m_descriptor);
+#endif
+
+ if (m_parent->m_restoreSettingsOnClose) {
+ ::SetCommState(m_descriptor, &m_oldDCB);
+ ::SetCommTimeouts(m_descriptor, &m_oldCommTimeouts);
+ }
+
+ ::CloseHandle(m_descriptor);
+
+#if !defined (Q_OS_WINCE)
+ closeEvents();
+#endif
+ m_descriptor = INVALID_HANDLE_VALUE;
+}
+
+/*!
+ Returns a bitmap state of RS-232 line signals. On error,
+ bitmap will be empty (equal zero).
+
+ Win API allows you to receive only the state of signals:
+ CTS, DSR, RING, DCD, DTR, RTS. Other signals are not available.
+*/
+SerialPort::Lines WinSerialPortEngine::lines() const
+{
+ DWORD modemStat = 0;
+ SerialPort::Lines ret = 0;
+
+ if (::GetCommModemStatus(m_descriptor, &modemStat) == 0) {
+ // Print error?
+ return ret;
+ }
+
+ if (modemStat & MS_CTS_ON)
+ ret |= SerialPort::Cts;
+ if (modemStat & MS_DSR_ON)
+ ret |= SerialPort::Dsr;
+ if (modemStat & MS_RING_ON)
+ ret |= SerialPort::Ri;
+ if (modemStat & MS_RLSD_ON)
+ ret |= SerialPort::Dcd;
+
+ DWORD bytesReturned = 0;
+ if (::DeviceIoControl(m_descriptor, IOCTL_SERIAL_GET_DTRRTS, 0, 0,
+ &modemStat, sizeof(DWORD),
+ &bytesReturned, 0)) {
+
+ if (modemStat & SERIAL_DTR_STATE)
+ ret |= SerialPort::Dtr;
+ if (modemStat & SERIAL_RTS_STATE)
+ ret |= SerialPort::Rts;
+ }
+
+ return ret;
+}
+
+/*!
+ Set DTR signal to state \a set.
+
+ If successful, returns true; otherwise false.
+*/
+bool WinSerialPortEngine::setDtr(bool set)
+{
+ bool ret = ::EscapeCommFunction(m_descriptor, (set) ? SETDTR : CLRDTR);
+ if (!ret) {
+ // FIXME: Here need call ::GetLastError()
+ // and set error type.
+ }
+ return ret;
+}
+
+/*!
+ Set RTS signal to state \a set.
+
+ If successful, returns true; otherwise false.
+*/
+bool WinSerialPortEngine::setRts(bool set)
+{
+ bool ret = ::EscapeCommFunction(m_descriptor, (set) ? SETRTS : CLRRTS);
+ if (!ret) {
+ // FIXME: Here need call ::GetLastError()
+ // and set error type.
+ }
+ return ret;
+}
+
+/*!
+ Flushes the buffers of a specified serial port and
+ causes all buffered data to be written to a serial port.
+
+ If successful, returns true; otherwise false.
+*/
+bool WinSerialPortEngine::flush()
+{
+ bool ret = ::FlushFileBuffers(m_descriptor);
+ if (!ret) {
+ // FIXME: Here need call ::GetLastError()
+ // and set error type.
+ }
+ return ret;
+}
+
+/*!
+ Discards all characters from the output or input buffer of
+ a specified communications resource. It can also terminate
+ pending read or write operations on the resource.
+
+ If successful, returns true; otherwise false.
+*/
+bool WinSerialPortEngine::reset()
+{
+ DWORD flags = (PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
+ bool ret = ::PurgeComm(m_descriptor, flags);
+ if (!ret) {
+ // FIXME: Here need call ::GetLastError()
+ // and set error type.
+ }
+ return ret;
+}
+
+/*!
+ Sends a continuous stream of zero bits during a specified
+ period of time \a duration in msec.
+
+ If successful, returns true; otherwise false.
+*/
+bool WinSerialPortEngine::sendBreak(int duration)
+{
+ // FIXME:
+ if (setBreak(true)) {
+ ::Sleep(duration);
+ if (setBreak(false))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Restores or suspend character transmission and places the
+ transmission line in a nonbreak or break state,
+ depending on the parameter \a set.
+
+ If successful, returns true; otherwise false.
+*/
+bool WinSerialPortEngine::setBreak(bool set)
+{
+ bool ret = (set) ?
+ (::SetCommBreak(m_descriptor)) : (::ClearCommBreak(m_descriptor));
+ if (!ret) {
+ // FIXME: Here need call ::GetLastError()
+ // and set error type.
+ }
+ return ret;
+}
+
+enum CommStatQue { CS_IN_QUE, CS_OUT_QUE };
+static qint64 get_commstat_que(HANDLE descriptor, enum CommStatQue que)
+{
+ DWORD err;
+ COMSTAT cs;
+ if (::ClearCommError(descriptor, &err, &cs) == 0)
+ return -1;
+ return qint64((que == CS_IN_QUE) ? (cs.cbInQue) : (cs.cbOutQue));
+}
+
+/*!
+ Returns the number of bytes received by the serial provider
+ but not yet read by a read() operation. Also it clears the
+ device's error flag to enable additional input and output
+ (I/O) operations.
+
+ If successful, returns true; otherwise false.
+*/
+qint64 WinSerialPortEngine::bytesAvailable() const
+{
+ return get_commstat_que(m_descriptor, CS_IN_QUE);
+}
+
+/*!
+ Returns the number of bytes of user data remaining to be
+ transmitted for all write operations. This value will be zero
+ for a nonoverlapped write (for embedded platform as WinCE).
+ Also it clears the device's error flag to enable additional
+ input and output (I/O) operations.
+
+ If successful, returns true; otherwise false.
+*/
+qint64 WinSerialPortEngine::bytesToWrite() const
+{
+ return get_commstat_que(m_descriptor, CS_OUT_QUE);
+}
+
+#if !defined (Q_OS_WINCE)
+// Clear overlapped structure, but does not affect the event.
+static void clear_overlapped(OVERLAPPED *overlapped)
+{
+ overlapped->Internal = 0;
+ overlapped->InternalHigh = 0;
+ overlapped->Offset = 0;
+ overlapped->OffsetHigh = 0;
+}
+#endif
+
+/*!
+ Read data from serial port. For NT-based platform, process
+ data's reading with the waiting wehn of pending I/O
+ operations is complete. Maybe this can cause some freezes.
+
+ If successful, returns to the external buffer \a data the
+ real number of bytes read, which can be less than the
+ requested \a len; otherwise returned -1 with set error code.
+
+ Also, this method processed the policy of operating with the
+ received symbol, in which the parity or frame error is detected.
+ This analysis and processing is executed by software-way in
+ this method. Parity or frame error flag determines subsystem
+ notification when it receives an event type EV_ERR. Since the
+ EV_ERR event appears before the event EV_RXCHAR, therefore,
+ we are able to handle errors by ordered, for each bad charachter
+ in this read method. This is true only when enabled the internal
+ read buffer of class SerialPort, ie when it is automatically
+ filled when the notification mode of reading is enabled. In
+ other cases, policy processing bad char is not guaranteed.
+*/
+qint64 WinSerialPortEngine::read(char *data, qint64 len)
+{
+#if !defined (Q_OS_WINCE)
+ clear_overlapped(&m_ovRead);
+#endif
+
+ DWORD readBytes = 0;
+ bool sucessResult = false;
+
+ // FIXME:
+ if (m_parent->m_policy != SerialPort::IgnorePolicy)
+ len = 1;
+
+#if defined (Q_OS_WINCE)
+ sucessResult = ::ReadFile(m_descriptor, data, len, &readBytes, 0);
+#else
+ if (::ReadFile(m_descriptor, data, len, &readBytes, &m_ovRead))
+ sucessResult = true;
+ else {
+ if (::GetLastError() == ERROR_IO_PENDING) {
+ // FIXME: Instead of an infinite wait I/O (not looped), we expect, for example 5 seconds.
+ // Although, maybe there is a better solution.
+ switch (::WaitForSingleObject(m_ovRead.hEvent, 5000)) {
+ case WAIT_OBJECT_0:
+ if (::GetOverlappedResult(m_descriptor, &m_ovRead, &readBytes, false))
+ sucessResult = true;
+ break;
+ default: ;
+ }
+ }
+ }
+#endif
+
+ if(!sucessResult) {
+ m_parent->setError(SerialPort::IoError);
+ return -1;
+ }
+
+ // FIXME: Process emulate policy.
+ if (m_flagErrorFromCommEvent) {
+ m_flagErrorFromCommEvent = false;
+
+ switch (m_parent->m_policy) {
+ case SerialPort::SkipPolicy:
+ return 0;
+ case SerialPort::PassZeroPolicy:
+ *data = '\0';
+ break;
+ case SerialPort::StopReceivingPolicy:
+ break;
+ default:;
+ }
+ }
+ return qint64(readBytes);
+}
+
+/*!
+ Write \a data to serial port. For NT-based platform, process
+ data's writing with the waiting wehn of pending I/O
+ operations is complete. Maybe this can cause some freezes.
+
+ If successful, returns the real number of bytes write, which
+ can be less than the requested \a len; otherwise returned -1
+ with set error code.
+*/
+qint64 WinSerialPortEngine::write(const char *data, qint64 len)
+{
+#if !defined (Q_OS_WINCE)
+ clear_overlapped(&m_ovWrite);
+#endif
+
+ DWORD writeBytes = 0;
+ bool sucessResult = false;
+
+#if defined (Q_OS_WINCE)
+ sucessResult = ::WriteFile(m_descriptor, data, len, &writeBytes, 0);
+#else
+ if (::WriteFile(m_descriptor, data, len, &writeBytes, &m_ovWrite))
+ sucessResult = true;
+ else {
+ if (::GetLastError() == ERROR_IO_PENDING) {
+ // Instead of an infinite wait I/O (not looped), we expect, for example 5 seconds.
+ // Although, maybe there is a better solution.
+ switch (::WaitForSingleObject(m_ovWrite.hEvent, 5000)) {
+ case WAIT_OBJECT_0:
+ if (::GetOverlappedResult(m_descriptor, &m_ovWrite, &writeBytes, false))
+ sucessResult = true;
+ break;
+ default: ;
+ }
+ }
+ }
+#endif
+
+ if(!sucessResult) {
+ m_parent->setError(SerialPort::IoError);
+ return -1;
+ }
+ return quint64(writeBytes);
+}
+
+/*!
+ Implements a function blocking for waiting of events EV_RXCHAR or
+ EV_TXEMPTY, on the \a timeout in millisecond. Event EV_RXCHAR
+ controlled, if the flag \a checkRead is set on true, and
+ EV_TXEMPTY wehn flag \a checkWrite is set on true. The result
+ of catch in each of the events, save to the corresponding
+ variables \a selectForRead and \a selectForWrite.
+
+ For NT-based OS and embedded, this method have different
+ implementation. WinCE has no mechanism to exit out of a timeout,
+ therefore for this feature special class is used
+ WinCeWaitCommEventBreaker, without which it is locked to wait
+ forever in the absence of events EV_RXCHAR or EV_TXEMPTY. For
+ satisfactory operation of the breaker, the timeout should be
+ guaranteed a great, to the timer in the breaker does not trip
+ happen sooner than a function call WaitCommEvent(); otherwise it
+ will block forever (in the absence of events EV_RXCHAR or EV_TXEMPTY).
+
+ Returns true if the occurrence of any event before the timeout;
+ otherwise returns false.
+*/
+bool WinSerialPortEngine::select(int timeout,
+ bool checkRead, bool checkWrite,
+ bool *selectForRead, bool *selectForWrite)
+{
+ // Forward checking data for read.
+ if (checkRead && (bytesAvailable() > 0)) {
+ Q_ASSERT(selectForRead);
+ *selectForRead = true;
+ return true;
+ }
+
+#if !defined (Q_OS_WINCE)
+ clear_overlapped(&m_ovSelect);
+#endif
+
+ DWORD oldEventMask = 0;
+ DWORD currEventMask = 0;
+
+ if (checkRead)
+ currEventMask |= EV_RXCHAR;
+ if (checkWrite)
+ currEventMask |= EV_TXEMPTY;
+
+ // Save old mask.
+ if (::GetCommMask(m_descriptor, &oldEventMask) == 0) {
+ //Print error?
+ return false;
+ }
+
+ // Checking the old mask bits as in the current mask.
+ // And if these bits are not exists, then add them and set the reting mask.
+ if (currEventMask != (oldEventMask & currEventMask)) {
+ currEventMask |= oldEventMask;
+ if (::SetCommMask(m_descriptor, currEventMask) == 0) {
+ //Print error?
+ return false;
+ }
+ }
+
+ currEventMask = 0;
+ bool sucessResult = false;
+
+#if !defined (Q_OS_WINCE)
+ if (::WaitCommEvent(m_descriptor, &currEventMask, &m_ovSelect))
+ sucessResult = true;
+ else {
+ if (::GetLastError() == ERROR_IO_PENDING) {
+ DWORD bytesTransferred = 0;
+ switch (::WaitForSingleObject(m_ovSelect.hEvent, (timeout < 0) ? 0 : timeout)) {
+ case WAIT_OBJECT_0:
+ if (::GetOverlappedResult(m_descriptor, &m_ovSelect, &bytesTransferred, false))
+ sucessResult = true;
+ break;
+ default: ;
+ }
+ }
+ }
+#else
+ // FIXME: Here the situation is not properly handled with zero timeout:
+ // breaker can work out before you call a method WaitCommEvent()
+ // and so it will loop forever!
+ WinCeWaitCommEventBreaker breaker(m_descriptor, (timeout < 0) ? 0 : timeout);
+ ::WaitCommEvent(m_descriptor, &currEventMask, 0);
+ breaker.stop();
+ sucessResult = !breaker.isWorked();
+#endif
+
+ if (sucessResult) {
+ // Here call the bytesAvailable() to protect against false positives WaitForSingleObject(),
+ // for example, when manually pulling USB/Serial converter from system,
+ // ie when devices are in fact not.
+ // While it may be possible to make additional checks - to catch an event EV_ERR,
+ // adding (in the code above) extra bits in the mask currEventMask.
+ if (checkRead) {
+ Q_ASSERT(selectForRead);
+ *selectForRead = (currEventMask & EV_RXCHAR) && (bytesAvailable() > 0);
+ }
+ if (checkWrite) {
+ Q_ASSERT(selectForWrite);
+ *selectForWrite = (currEventMask & EV_TXEMPTY);
+ }
+ }
+
+ // Rerair old mask.
+ ::SetCommMask(m_descriptor, oldEventMask);
+ return sucessResult;
+}
+
+#if !defined (Q_OS_WINCE)
+static const QString defaultPathPrefix = QLatin1String("\\\\.\\");
+#else
+static const QString defaultPathPostfix = QLatin1String(":");
+#endif
+
+/*!
+ Converts a platform specific \a port name to system location
+ and return result.
+*/
+QString WinSerialPortEngine::toSystemLocation(const QString &port) const
+{
+ QString ret = port;
+#if !defined (Q_OS_WINCE)
+ if (!ret.contains(defaultPathPrefix))
+ ret.prepend(defaultPathPrefix);
+#else
+ if (!ret.contains(defaultPathPostfix))
+ ret.append(defaultPathPostfix);
+#endif
+ return ret;
+}
+
+/*!
+ Converts a platform specific system \a location to port name
+ and return result.
+*/
+QString WinSerialPortEngine::fromSystemLocation(const QString &location) const
+{
+ QString ret = location;
+#if !defined (Q_OS_WINCE)
+ if (ret.contains(defaultPathPrefix))
+ ret.remove(defaultPathPrefix);
+#else
+ if (ret.contains(defaultPathPostfix))
+ ret.remove(defaultPathPostfix);
+#endif
+ return ret;
+}
+
+/*!
+ Set desired \a rate by given direction \a dir.
+ However, windows does not support separate directions, so the
+ method will return an error.
+
+ If successful, returns true; otherwise false, with the setup a
+ error code.
+*/
+bool WinSerialPortEngine::setRate(qint32 rate, SerialPort::Directions dir)
+{
+ if (dir != SerialPort::AllDirections) {
+ m_parent->setError(SerialPort::UnsupportedPortOperationError);
+ return false;
+ }
+ m_currDCB.BaudRate = DWORD(rate);
+ return updateDcb();
+}
+
+/*!
+ Set desired number of data bits \a dataBits in byte. Windows
+ native supported all present number of data bits 5, 6, 7, 8.
+
+ If successful, returns true; otherwise false, with the setup a
+ error code.
+*/
+bool WinSerialPortEngine::setDataBits(SerialPort::DataBits dataBits)
+{
+ m_currDCB.ByteSize = BYTE(dataBits);
+ return updateDcb();
+}
+
+/*!
+ Set desired \a parity control mode. Windows native supported
+ all present parity types no parity, space, mark, even, odd.
+
+ If successful, returns true; otherwise false, with the setup a
+ error code.
+*/
+bool WinSerialPortEngine::setParity(SerialPort::Parity parity)
+{
+ m_currDCB.fParity = true;
+ switch (parity) {
+ case SerialPort::NoParity:
+ m_currDCB.Parity = NOPARITY;
+ m_currDCB.fParity = false;
+ break;
+ case SerialPort::SpaceParity:
+ m_currDCB.Parity = SPACEPARITY;
+ break;
+ case SerialPort::MarkParity:
+ m_currDCB.Parity = MARKPARITY;
+ break;
+ case SerialPort::EvenParity:
+ m_currDCB.Parity = EVENPARITY;
+ break;
+ case SerialPort::OddParity:
+ m_currDCB.Parity = ODDPARITY;
+ break;
+ default:
+ m_parent->setError(SerialPort::UnsupportedPortOperationError);
+ return false;
+ }
+ return updateDcb();
+}
+
+/*!
+ Set desired number of stop bits \a stopBits in frame.
+ Windows native supported all present number of stop bits
+ 1, 1.5, 2.
+
+ If successful, returns true; otherwise false, with the setup a
+ error code.
+*/
+bool WinSerialPortEngine::setStopBits(SerialPort::StopBits stopBits)
+{
+ switch (stopBits) {
+ case SerialPort::OneStop:
+ m_currDCB.StopBits = ONESTOPBIT;
+ break;
+ case SerialPort::OneAndHalfStop:
+ m_currDCB.StopBits = ONE5STOPBITS;
+ break;
+ case SerialPort::TwoStop:
+ m_currDCB.StopBits = TWOSTOPBITS;
+ break;
+ default:
+ m_parent->setError(SerialPort::UnsupportedPortOperationError);
+ return false;
+ }
+ return updateDcb();
+}
+
+/*!
+ Set desired \a flow control mode. Windows native supported all
+ present flow control modes no control, hardware (RTS/CTS),
+ software (XON/XOFF).
+
+ If successful, returns true; otherwise false, with the setup a
+ error code.
+*/
+bool WinSerialPortEngine::setFlowControl(SerialPort::FlowControl flow)
+{
+ switch (flow) {
+ case SerialPort::NoFlowControl:
+ m_currDCB.fOutxCtsFlow = false;
+ m_currDCB.fRtsControl = RTS_CONTROL_DISABLE;
+ m_currDCB.fInX = m_currDCB.fOutX = false;
+ break;
+ case SerialPort::SoftwareControl:
+ m_currDCB.fOutxCtsFlow = false;
+ m_currDCB.fRtsControl = RTS_CONTROL_DISABLE;
+ m_currDCB.fInX = m_currDCB.fOutX = true;
+ break;
+ case SerialPort::HardwareControl:
+ m_currDCB.fOutxCtsFlow = true;
+ m_currDCB.fRtsControl = RTS_CONTROL_HANDSHAKE;
+ m_currDCB.fInX = m_currDCB.fOutX = false;
+ break;
+ default:
+ m_parent->setError(SerialPort::UnsupportedPortOperationError);
+ return false;
+ }
+ return updateDcb();
+}
+
+/*!
+ Empty stub. Setting a variable is carried out methods in a
+ private class SerialPortPrivate.
+*/
+bool WinSerialPortEngine::setDataErrorPolicy(SerialPort::DataErrorPolicy policy)
+{
+ Q_UNUSED(policy)
+ return true;
+}
+
+/*!
+ Returns the current status of the read notification subsystem.
+*/
+bool WinSerialPortEngine::isReadNotificationEnabled() const
+{
+#if defined (Q_OS_WINCE)
+ bool flag = isRunning();
+#else
+ bool flag = isEnabled();
+#endif
+ return (flag && (m_setMask & EV_RXCHAR));
+}
+
+/*!
+ Enables or disables read notification subsystem, depending on
+ the \a enable parameter. If the subsystem is enabled, it will
+ asynchronously track the occurrence of an event EV_RXCHAR.
+ Thanks to that, SerialPort can emit a signal readyRead() and
+ fill up the internal receive buffer with new data, that
+ automatically received from a serial port in the event loop.
+*/
+void WinSerialPortEngine::setReadNotificationEnabled(bool enable)
+{
+#if defined (Q_OS_WINCE)
+ m_setCommMaskMutex.lock();
+ ::GetCommMask(m_descriptor, &m_currentMask);
+#endif
+
+ if (enable)
+ m_setMask |= EV_RXCHAR;
+ else
+ m_setMask &= ~EV_RXCHAR;
+
+#if defined (Q_OS_WINCE)
+ if (m_setMask != m_currentMask)
+ ::SetCommMask(m_descriptor, m_setMask);
+
+ m_setCommMaskMutex.unlock();
+
+ if (enable && !isRunning())
+ start();
+#else
+ setMaskAndActivateEvent();
+#endif
+}
+
+/*!
+ Returns the current status of the write notification subsystem.
+*/
+bool WinSerialPortEngine::isWriteNotificationEnabled() const
+{
+#if defined (Q_OS_WINCE)
+ bool flag = isRunning();
+#else
+ bool flag = isEnabled();
+#endif
+ return (flag && (m_setMask & EV_TXEMPTY));
+}
+
+/*!
+ Enables or disables write notification subsystem, depending on
+ the \a enable parameter. If the subsystem is enabled, it will
+ asynchronously track the occurrence of an event EV_TXEMPTY.
+ Thanks to that, SerialPort can write data from internal transfer
+ buffer, to serial port automatically in the event loop.
+*/
+void WinSerialPortEngine::setWriteNotificationEnabled(bool enable)
+{
+#if defined (Q_OS_WINCE)
+ m_setCommMaskMutex.lock();
+ ::GetCommMask(m_descriptor, &m_currentMask);
+#endif
+
+ if (enable)
+ m_setMask |= EV_TXEMPTY;
+ else
+ m_setMask &= ~EV_TXEMPTY;
+
+#if defined (Q_OS_WINCE)
+ if (m_setMask != m_currentMask)
+ ::SetCommMask(m_descriptor, m_setMask);
+
+ m_setCommMaskMutex.unlock();
+
+ if (enable && !isRunning())
+ start();
+#else
+ setMaskAndActivateEvent();
+#endif
+ // This only for OS Windows, as EV_TXEMPTY event is triggered only
+ // after the last byte of data.
+ // Therefore, we are forced to run writeNotification(), as EV_TXEMPTY does not work.
+ if (enable)
+ m_parent->canWriteNotification();
+}
+
+/*!
+ Defines the type of parity or frame error when an event
+ occurs EV_ERR. In addition, in case of any errors, this method
+ sets to true a flag m_flagErrorFromCommEvent, that used in the
+ method of reading for policy processing.
+
+ This method is called automatically from an error handler in
+ parent class SerialPortPrivate, that called by error notification
+ subsystem, wehn occurred a event EV_ERR.
+
+*/
+bool WinSerialPortEngine::processIOErrors()
+{
+ DWORD err = 0;
+ COMSTAT cs;
+ bool ret = (::ClearCommError(m_descriptor, &err, &cs) != 0);
+ if (ret && err) {
+ if (err & CE_FRAME)
+ m_parent->setError(SerialPort::FramingError);
+ else if (err & CE_RXPARITY)
+ m_parent->setError(SerialPort::ParityError);
+ else if (err & CE_BREAK)
+ m_parent->setError(SerialPort::BreakConditionError);
+ else
+ m_parent->setError(SerialPort::UnknownPortError);
+
+ m_flagErrorFromCommEvent = true;
+ }
+ return ret;
+}
+
+#if defined (Q_OS_WINCE)
+
+void WinSerialPortEngine::lockNotification(NotificationLockerType type, bool uselocker)
+{
+ QMutex *mutex = 0;
+ switch (type) {
+ case CanReadLocker:
+ mutex = &m_readNotificationMutex;
+ break;
+ case CanWriteLocker:
+ mutex = &m_writeNotificationMutex;
+ break;
+ case CanErrorLocker:
+ mutex = &m_errorNotificationMutex;
+ break;
+ }
+
+ if (uselocker)
+ QMutexLocker locker(mutex);
+ else
+ mutex->lock();
+}
+
+void WinSerialPortEngine::unlockNotification(NotificationLockerType type)
+{
+ switch (type) {
+ case CanReadLocker: m_readNotificationMutex.unlock();
+ break;
+ case CanWriteLocker: m_writeNotificationMutex.unlock();
+ break;
+ case CanErrorLocker: m_errorNotificationMutex.unlock();
+ break;
+ }
+}
+
+#endif
+
+/* Protected methods */
+
+/*!
+ Attempts to determine the current settings of the serial port,
+ wehn it opened. Used only in the method open().
+*/
+void WinSerialPortEngine::detectDefaultSettings()
+{
+ // Detect rate.
+ m_parent->m_inRate = quint32(m_currDCB.BaudRate);
+ m_parent->m_outRate = m_parent->m_inRate;
+
+ // Detect databits.
+ switch (m_currDCB.ByteSize) {
+ case 5:
+ m_parent->m_dataBits = SerialPort::Data5;
+ break;
+ case 6:
+ m_parent->m_dataBits = SerialPort::Data6;
+ break;
+ case 7:
+ m_parent->m_dataBits = SerialPort::Data7;
+ break;
+ case 8:
+ m_parent->m_dataBits = SerialPort::Data8;
+ break;
+ default:
+ m_parent->m_dataBits = SerialPort::UnknownDataBits;
+ }
+
+ // Detect parity.
+ if ((m_currDCB.Parity == NOPARITY) && !m_currDCB.fParity)
+ m_parent->m_parity = SerialPort::NoParity;
+ else if ((m_currDCB.Parity == SPACEPARITY) && m_currDCB.fParity)
+ m_parent->m_parity = SerialPort::SpaceParity;
+ else if ((m_currDCB.Parity == MARKPARITY) && m_currDCB.fParity)
+ m_parent->m_parity = SerialPort::MarkParity;
+ else if ((m_currDCB.Parity == EVENPARITY) && m_currDCB.fParity)
+ m_parent->m_parity = SerialPort::EvenParity;
+ else if ((m_currDCB.Parity == ODDPARITY) && m_currDCB.fParity)
+ m_parent->m_parity = SerialPort::OddParity;
+ else
+ m_parent->m_parity = SerialPort::UnknownParity;
+
+ // Detect stopbits.
+ switch (m_currDCB.StopBits) {
+ case ONESTOPBIT:
+ m_parent->m_stopBits = SerialPort::OneStop;
+ break;
+ case ONE5STOPBITS:
+ m_parent->m_stopBits = SerialPort::OneAndHalfStop;
+ break;
+ case TWOSTOPBITS:
+ m_parent->m_stopBits = SerialPort::TwoStop;
+ break;
+ default:
+ m_parent->m_stopBits = SerialPort::UnknownStopBits;
+ }
+
+ // Detect flow control.
+ if (!m_currDCB.fOutxCtsFlow && (m_currDCB.fRtsControl == RTS_CONTROL_DISABLE)
+ && !m_currDCB.fInX && !m_currDCB.fOutX) {
+ m_parent->m_flow = SerialPort::NoFlowControl;
+ } else if (!m_currDCB.fOutxCtsFlow && (m_currDCB.fRtsControl == RTS_CONTROL_DISABLE)
+ && m_currDCB.fInX && m_currDCB.fOutX) {
+ m_parent->m_flow = SerialPort::SoftwareControl;
+ } else if (m_currDCB.fOutxCtsFlow && (m_currDCB.fRtsControl == RTS_CONTROL_HANDSHAKE)
+ && !m_currDCB.fInX && !m_currDCB.fOutX) {
+ m_parent->m_flow = SerialPort::HardwareControl;
+ } else
+ m_parent->m_flow = SerialPort::UnknownFlowControl;
+}
+
+#if defined (Q_OS_WINCE)
+
+/*!
+ Embedded-based (WinCE) event loop for notification subsystem.
+ Tracking a separate thread the events from the serial port, as:
+ EV_ERR, EV_RXCHAR, EV_TXEMPTY. When is occur a relevant event,
+ calls him handler from a parent class SerialPortPrivate.
+ At the same time in handlers to capture/release the mutex
+ (see handlers implementation).
+*/
+void WinSerialPortEngine::run()
+{
+ while (m_running) {
+
+ m_setCommMaskMutex.lock();
+ ::SetCommMask(m_descriptor, m_setMask);
+ m_setCommMaskMutex.unlock();
+
+ if (::WaitCommEvent(m_descriptor, &m_currentMask, 0) != 0) {
+
+ // Wait until complete the operation changes the port settings,
+ // see updateDcb().
+ m_settingsChangeMutex.lock();
+ m_settingsChangeMutex.unlock();
+
+ if (EV_ERR & m_currentMask & m_setMask) {
+ m_parent->canErrorNotification();
+ }
+ if (EV_RXCHAR & m_currentMask & m_setMask) {
+ m_parent->canReadNotification();
+ }
+ //FIXME: This is why it does not work?
+ if (EV_TXEMPTY & m_currentMask & m_setMask) {
+ m_parent->canWriteNotification();
+ }
+ }
+ }
+}
+
+#else
+
+/*!
+ Windows NT-based event loop for notification subsystem.
+ Asynchronously in event loop continuous mode tracking the
+ events from the serial port, as: EV_ERR, EV_RXCHAR, EV_TXEMPTY.
+ When is occur a relevant event, calls him handler from
+ a parent class SerialPortPrivate.
+*/
+bool WinSerialPortEngine::event(QEvent *e)
+{
+ bool ret = false;
+ if (e->type() == QEvent::WinEventAct) {
+ if (EV_ERR & m_currentMask & m_setMask) {
+ m_parent->canErrorNotification();
+ ret = true;
+ }
+ if (EV_RXCHAR & m_currentMask & m_setMask) {
+ m_parent->canReadNotification();
+ ret = true;
+ }
+ //FIXME: This is why it does not work?
+ if (EV_TXEMPTY & m_currentMask & m_setMask) {
+ m_parent->canWriteNotification();
+ ret = true;
+ }
+ }
+ else
+ ret = QWinEventNotifier::event(e);
+
+ ::WaitCommEvent(m_descriptor, &m_currentMask, &m_ov);
+ return ret;
+}
+
+#endif
+
+/* Private methods */
+
+#if !defined (Q_OS_WINCE)
+
+/*!
+ For Windows NT-based, creates handles events for OVERLAPPED
+ structures, that are used in the methods of reading \a rx,
+ writing \a tx, and waiting for data from the serial port.
+ This method is only used in the method open().
+
+ If successful, returns true; otherwise false.
+*/
+bool WinSerialPortEngine::createEvents(bool rx, bool tx)
+{
+ if (rx) {
+ m_ovRead.hEvent = ::CreateEvent(0, false, false, 0);
+ Q_ASSERT(m_ovRead.hEvent);
+ }
+ if (tx) {
+ m_ovWrite.hEvent = ::CreateEvent(0, false, false, 0);
+ Q_ASSERT(m_ovWrite.hEvent);
+ }
+ m_ovSelect.hEvent = ::CreateEvent(0, false, false, 0);
+ Q_ASSERT(m_ovSelect.hEvent);
+ m_ov.hEvent = ::CreateEvent(0, false, false, 0);
+ Q_ASSERT(m_ov.hEvent);
+
+ setHandle(m_ov.hEvent);
+ return true;
+}
+
+/*!
+ For Windows NT-based, release and closed handles events from
+ OVERLAPPED structures.
+*/
+void WinSerialPortEngine::closeEvents()
+{
+ if (m_ovRead.hEvent)
+ ::CloseHandle(m_ovRead.hEvent);
+ if (m_ovWrite.hEvent)
+ ::CloseHandle(m_ovWrite.hEvent);
+ if (m_ovSelect.hEvent)
+ ::CloseHandle(m_ovSelect.hEvent);
+ if (m_ov.hEvent)
+ ::CloseHandle(m_ov.hEvent);
+
+ size_t size = sizeof(OVERLAPPED);
+ ::memset(&m_ovRead, 0, size);
+ ::memset(&m_ovWrite, 0, size);
+ ::memset(&m_ovSelect, 0, size);
+ ::memset(&m_ov, 0, size);
+}
+
+/*!
+ For Windows NT-based, sets the mask of tracking events.
+*/
+void WinSerialPortEngine::setMaskAndActivateEvent()
+{
+ ::SetCommMask(m_descriptor, m_setMask);
+ if (m_setMask)
+ ::WaitCommEvent(m_descriptor, &m_currentMask, &m_ov);
+ switch (m_setMask) {
+ case 0:
+ if (isEnabled())
+ setEnabled(false);
+ break;
+ default:
+ if (!isEnabled())
+ setEnabled(true);
+ }
+}
+
+#endif
+
+/*!
+ Updates the DCB structure wehn changing of any the parameters
+ a serial port.
+
+ If successful, returns true; otherwise false.
+*/
+bool WinSerialPortEngine::updateDcb()
+{
+#if defined (Q_OS_WINCE)
+ // Grab a mutex, in order after exit WaitCommEvent
+ // block the flow of run() notifier until there is a change DCB.
+ QMutexLocker locker(&m_settingsChangeMutex);
+ // This way, we reset in class WaitCommEvent to
+ // be able to change the DCB.
+ // Otherwise WaitCommEvent blocking any change!
+ ::SetCommMask(m_descriptor, 0);
+#endif
+ if (::SetCommState(m_descriptor, &m_currDCB) == 0) {
+ m_parent->setError(SerialPort::UnsupportedPortOperationError);
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Updates the COMMTIMEUTS structure wehn changing of any timeout the
+ parameters a serial port.
+
+ If successful, returns true; otherwise false.
+*/
+bool WinSerialPortEngine::updateCommTimeouts()
+{
+ if (::SetCommTimeouts(m_descriptor, &m_currCommTimeouts) == 0) {
+ m_parent->setError(SerialPort::UnsupportedPortOperationError);
+ return false;
+ }
+ return true;
+}
+
+// From <serialportengine_p.h>
+SerialPortEngine *SerialPortEngine::create(SerialPortPrivate *parent)
+{
+ return new WinSerialPortEngine(parent);
+}
+
+#include "moc_serialportengine_win_p.cpp"
+
+QT_END_NAMESPACE_SERIALPORT
+