You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1092 lines
30 KiB
1092 lines
30 KiB
12 years ago
|
/****************************************************************************
|
||
|
**
|
||
|
** Copyright (C) 2012 Denis Shienkov <denis.shienkov@gmail.com>
|
||
|
** Copyright (C) 2012 Laszlo Papp <lpapp@kde.org>
|
||
|
** Copyright (C) 2012 Andre Hartmann <aha_1980@gmx.de>
|
||
|
** Contact: http://www.qt-project.org/legal
|
||
|
**
|
||
|
** 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 Digia. For licensing terms and
|
||
|
** conditions see http://qt.digia.com/licensing. For further information
|
||
|
** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
|
||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||
|
** packaging of this file. Please review the following information to
|
||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||
|
**
|
||
|
** In addition, as a special exception, Digia gives you certain additional
|
||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||
|
**
|
||
|
** GNU General Public License Usage
|
||
|
** Alternatively, this file may be used under the terms of the GNU
|
||
|
** General Public License version 3.0 as published by the Free Software
|
||
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
||
|
** packaging of this file. Please review the following information to
|
||
|
** ensure the GNU General Public License version 3.0 requirements will be
|
||
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
||
|
**
|
||
|
**
|
||
|
** $QT_END_LICENSE$
|
||
|
**
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include "qserialport_win_p.h"
|
||
|
|
||
|
#ifndef Q_OS_WINCE
|
||
|
#include <QtCore/qelapsedtimer.h>
|
||
|
#include <QtCore/qvector.h>
|
||
|
#endif
|
||
|
|
||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||
|
#include <QtCore/qwineventnotifier.h>
|
||
|
#else
|
||
|
#include "qt4support/qwineventnotifier_p.h"
|
||
|
#endif
|
||
|
|
||
|
#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
|
||
|
|
||
|
#ifndef Q_OS_WINCE
|
||
|
|
||
|
class AbstractOverlappedEventNotifier : public QWinEventNotifier
|
||
|
{
|
||
|
public:
|
||
|
enum Type { CommEvent, ReadCompletionEvent, WriteCompletionEvent };
|
||
|
|
||
|
AbstractOverlappedEventNotifier(QSerialPortPrivate *d, Type type, bool manual, QObject *parent)
|
||
|
: QWinEventNotifier(parent), dptr(d), t(type) {
|
||
|
::memset(&o, 0, sizeof(o));
|
||
|
o.hEvent = ::CreateEvent(NULL, manual, FALSE, NULL);
|
||
|
setHandle(o.hEvent);
|
||
|
dptr->notifiers[o.hEvent] = this;
|
||
|
}
|
||
|
|
||
|
virtual bool processCompletionRoutine() = 0;
|
||
|
|
||
|
virtual ~AbstractOverlappedEventNotifier() {
|
||
|
setEnabled(false);
|
||
|
::CancelIo(o.hEvent);
|
||
|
::CloseHandle(o.hEvent);
|
||
|
}
|
||
|
|
||
|
Type type() const { return t; }
|
||
|
OVERLAPPED *overlappedPointer() { return &o; }
|
||
|
|
||
|
protected:
|
||
|
bool event(QEvent *e) Q_DECL_OVERRIDE {
|
||
|
const bool ret = QWinEventNotifier::event(e);
|
||
|
processCompletionRoutine();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
QSerialPortPrivate *dptr;
|
||
|
Type t;
|
||
|
OVERLAPPED o;
|
||
|
};
|
||
|
|
||
|
class CommOverlappedEventNotifier : public AbstractOverlappedEventNotifier
|
||
|
{
|
||
|
public:
|
||
|
CommOverlappedEventNotifier(QSerialPortPrivate *d, DWORD eventMask, QObject *parent)
|
||
|
: AbstractOverlappedEventNotifier(d, CommEvent, false, parent)
|
||
|
, originalEventMask(eventMask), triggeredEventMask(0) {
|
||
|
::SetCommMask(dptr->descriptor, originalEventMask);
|
||
|
startWaitCommEvent();
|
||
|
}
|
||
|
|
||
|
void startWaitCommEvent() { ::WaitCommEvent(dptr->descriptor, &triggeredEventMask, &o); }
|
||
|
|
||
|
bool processCompletionRoutine() Q_DECL_OVERRIDE {
|
||
|
DWORD numberOfBytesTransferred = 0;
|
||
|
::GetOverlappedResult(dptr->descriptor, &o, &numberOfBytesTransferred, FALSE);
|
||
|
|
||
|
bool error = false;
|
||
|
|
||
|
// Check for unexpected event. This event triggered when pulled previously
|
||
|
// opened device from the system, when opened as for not to read and not to
|
||
|
// write options and so forth.
|
||
|
if (triggeredEventMask == 0)
|
||
|
error = true;
|
||
|
|
||
|
// Workaround for standard CDC ACM serial ports, for which triggered an
|
||
|
// unexpected event EV_TXEMPTY at data transmission.
|
||
|
if ((originalEventMask & triggeredEventMask) == 0) {
|
||
|
if ((triggeredEventMask & EV_TXEMPTY) == 0)
|
||
|
error = true;
|
||
|
}
|
||
|
|
||
|
// Start processing a caught error.
|
||
|
if (error || (EV_ERR & triggeredEventMask))
|
||
|
dptr->processIoErrors(error);
|
||
|
|
||
|
if (!error)
|
||
|
dptr->startAsyncRead();
|
||
|
|
||
|
return !error;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
DWORD originalEventMask;
|
||
|
DWORD triggeredEventMask;
|
||
|
};
|
||
|
|
||
|
class ReadOverlappedCompletionNotifier : public AbstractOverlappedEventNotifier
|
||
|
{
|
||
|
public:
|
||
|
ReadOverlappedCompletionNotifier(QSerialPortPrivate *d, QObject *parent)
|
||
|
: AbstractOverlappedEventNotifier(d, ReadCompletionEvent, false, parent) {}
|
||
|
|
||
|
bool processCompletionRoutine() Q_DECL_OVERRIDE {
|
||
|
DWORD numberOfBytesTransferred = 0;
|
||
|
::GetOverlappedResult(dptr->descriptor, &o, &numberOfBytesTransferred, FALSE);
|
||
|
bool ret = dptr->completeAsyncRead(numberOfBytesTransferred);
|
||
|
|
||
|
// start async read for possible remainder into driver queue
|
||
|
if (ret && (numberOfBytesTransferred > 0) && (dptr->policy == QSerialPort::IgnorePolicy)) {
|
||
|
dptr->startAsyncRead();
|
||
|
} else { // driver queue is emplty, so startup wait comm event
|
||
|
CommOverlappedEventNotifier *n =
|
||
|
reinterpret_cast<CommOverlappedEventNotifier *>(dptr->lookupCommEventNotifier());
|
||
|
if (n)
|
||
|
n->startWaitCommEvent();
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class WriteOverlappedCompletionNotifier : public AbstractOverlappedEventNotifier
|
||
|
{
|
||
|
public:
|
||
|
WriteOverlappedCompletionNotifier(QSerialPortPrivate *d, QObject *parent)
|
||
|
: AbstractOverlappedEventNotifier(d, WriteCompletionEvent, false, parent) {}
|
||
|
|
||
|
bool processCompletionRoutine() Q_DECL_OVERRIDE {
|
||
|
setEnabled(false);
|
||
|
DWORD numberOfBytesTransferred = 0;
|
||
|
::GetOverlappedResult(dptr->descriptor, &o, &numberOfBytesTransferred, FALSE);
|
||
|
return dptr->completeAsyncWrite(numberOfBytesTransferred);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
QSerialPortPrivate::QSerialPortPrivate(QSerialPort *q)
|
||
|
: QSerialPortPrivateData(q)
|
||
|
, descriptor(INVALID_HANDLE_VALUE)
|
||
|
, parityErrorOccurred(false)
|
||
|
, actualReadBufferSize(0)
|
||
|
, actualWriteBufferSize(0)
|
||
|
, acyncWritePosition(0)
|
||
|
, readyReadEmitted(0)
|
||
|
, writeSequenceStarted(false)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::open(QIODevice::OpenMode mode)
|
||
|
{
|
||
|
DWORD desiredAccess = 0;
|
||
|
DWORD originalEventMask = EV_ERR;
|
||
|
|
||
|
if (mode & QIODevice::ReadOnly) {
|
||
|
desiredAccess |= GENERIC_READ;
|
||
|
originalEventMask |= EV_RXCHAR;
|
||
|
}
|
||
|
if (mode & QIODevice::WriteOnly)
|
||
|
desiredAccess |= GENERIC_WRITE;
|
||
|
|
||
|
descriptor = ::CreateFile(reinterpret_cast<const wchar_t*>(systemLocation.utf16()),
|
||
|
desiredAccess, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||
|
|
||
|
if (descriptor == INVALID_HANDLE_VALUE) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!::GetCommState(descriptor, &restoredDcb)) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
currentDcb = restoredDcb;
|
||
|
currentDcb.fBinary = TRUE;
|
||
|
currentDcb.fInX = FALSE;
|
||
|
currentDcb.fOutX = FALSE;
|
||
|
currentDcb.fAbortOnError = FALSE;
|
||
|
currentDcb.fNull = FALSE;
|
||
|
currentDcb.fErrorChar = FALSE;
|
||
|
|
||
|
if (!updateDcb())
|
||
|
return false;
|
||
|
|
||
|
if (!::GetCommTimeouts(descriptor, &restoredCommTimeouts)) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
::memset(¤tCommTimeouts, 0, sizeof(currentCommTimeouts));
|
||
|
currentCommTimeouts.ReadIntervalTimeout = MAXDWORD;
|
||
|
|
||
|
if (!updateCommTimeouts())
|
||
|
return false;
|
||
|
|
||
|
if (originalEventMask & EV_RXCHAR) {
|
||
|
QWinEventNotifier *n = new ReadOverlappedCompletionNotifier(this, q_ptr);
|
||
|
n->setEnabled(true);
|
||
|
}
|
||
|
|
||
|
QWinEventNotifier *n = new CommOverlappedEventNotifier(this, originalEventMask, q_ptr);
|
||
|
n->setEnabled(true);
|
||
|
|
||
|
detectDefaultSettings();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void QSerialPortPrivate::close()
|
||
|
{
|
||
|
::CancelIo(descriptor);
|
||
|
|
||
|
qDeleteAll(notifiers);
|
||
|
notifiers.clear();
|
||
|
|
||
|
readBuffer.clear();
|
||
|
actualReadBufferSize = 0;
|
||
|
|
||
|
writeSequenceStarted = false;
|
||
|
writeBuffer.clear();
|
||
|
actualWriteBufferSize = 0;
|
||
|
acyncWritePosition = 0;
|
||
|
|
||
|
readyReadEmitted = false;
|
||
|
parityErrorOccurred = false;
|
||
|
|
||
|
if (settingsRestoredOnClose) {
|
||
|
::SetCommState(descriptor, &restoredDcb);
|
||
|
::SetCommTimeouts(descriptor, &restoredCommTimeouts);
|
||
|
}
|
||
|
|
||
|
::CloseHandle(descriptor);
|
||
|
descriptor = INVALID_HANDLE_VALUE;
|
||
|
}
|
||
|
|
||
|
#endif // #ifndef Q_OS_WINCE
|
||
|
|
||
|
QSerialPort::PinoutSignals QSerialPortPrivate::pinoutSignals() const
|
||
|
{
|
||
|
DWORD modemStat = 0;
|
||
|
QSerialPort::PinoutSignals ret = QSerialPort::NoSignal;
|
||
|
|
||
|
if (!::GetCommModemStatus(descriptor, &modemStat)) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (modemStat & MS_CTS_ON)
|
||
|
ret |= QSerialPort::ClearToSendSignal;
|
||
|
if (modemStat & MS_DSR_ON)
|
||
|
ret |= QSerialPort::DataSetReadySignal;
|
||
|
if (modemStat & MS_RING_ON)
|
||
|
ret |= QSerialPort::RingIndicatorSignal;
|
||
|
if (modemStat & MS_RLSD_ON)
|
||
|
ret |= QSerialPort::DataCarrierDetectSignal;
|
||
|
|
||
|
DWORD bytesReturned = 0;
|
||
|
if (::DeviceIoControl(descriptor, IOCTL_SERIAL_GET_DTRRTS, NULL, 0,
|
||
|
&modemStat, sizeof(modemStat),
|
||
|
&bytesReturned, NULL)) {
|
||
|
|
||
|
if (modemStat & SERIAL_DTR_STATE)
|
||
|
ret |= QSerialPort::DataTerminalReadySignal;
|
||
|
if (modemStat & SERIAL_RTS_STATE)
|
||
|
ret |= QSerialPort::RequestToSendSignal;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setDataTerminalReady(bool set)
|
||
|
{
|
||
|
return ::EscapeCommFunction(descriptor, set ? SETDTR : CLRDTR);
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setRequestToSend(bool set)
|
||
|
{
|
||
|
return ::EscapeCommFunction(descriptor, set ? SETRTS : CLRRTS);
|
||
|
}
|
||
|
|
||
|
#ifndef Q_OS_WINCE
|
||
|
|
||
|
bool QSerialPortPrivate::flush()
|
||
|
{
|
||
|
return startAsyncWrite() && ::FlushFileBuffers(descriptor);
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::clear(QSerialPort::Directions dir)
|
||
|
{
|
||
|
DWORD flags = 0;
|
||
|
if (dir & QSerialPort::Input) {
|
||
|
flags |= PURGE_RXABORT | PURGE_RXCLEAR;
|
||
|
actualReadBufferSize = 0;
|
||
|
}
|
||
|
if (dir & QSerialPort::Output) {
|
||
|
flags |= PURGE_TXABORT | PURGE_TXCLEAR;
|
||
|
actualWriteBufferSize = 0;
|
||
|
acyncWritePosition = 0;
|
||
|
writeSequenceStarted = false;
|
||
|
}
|
||
|
return ::PurgeComm(descriptor, flags);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
bool QSerialPortPrivate::sendBreak(int duration)
|
||
|
{
|
||
|
// FIXME:
|
||
|
if (setBreakEnabled(true)) {
|
||
|
::Sleep(duration);
|
||
|
if (setBreakEnabled(false))
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setBreakEnabled(bool set)
|
||
|
{
|
||
|
if (set)
|
||
|
return ::SetCommBreak(descriptor);
|
||
|
return ::ClearCommBreak(descriptor);
|
||
|
}
|
||
|
|
||
|
qint64 QSerialPortPrivate::systemInputQueueSize () const
|
||
|
{
|
||
|
COMSTAT cs;
|
||
|
::memset(&cs, 0, sizeof(cs));
|
||
|
if (!::ClearCommError(descriptor, NULL, &cs))
|
||
|
return -1;
|
||
|
return cs.cbInQue;
|
||
|
}
|
||
|
|
||
|
qint64 QSerialPortPrivate::systemOutputQueueSize () const
|
||
|
{
|
||
|
COMSTAT cs;
|
||
|
::memset(&cs, 0, sizeof(cs));
|
||
|
if (!::ClearCommError(descriptor, NULL, &cs))
|
||
|
return -1;
|
||
|
return cs.cbOutQue;
|
||
|
}
|
||
|
|
||
|
#ifndef Q_OS_WINCE
|
||
|
|
||
|
qint64 QSerialPortPrivate::bytesAvailable() const
|
||
|
{
|
||
|
return actualReadBufferSize;
|
||
|
}
|
||
|
|
||
|
qint64 QSerialPortPrivate::readFromBuffer(char *data, qint64 maxSize)
|
||
|
{
|
||
|
if (actualReadBufferSize == 0)
|
||
|
return 0;
|
||
|
|
||
|
qint64 readSoFar = -1;
|
||
|
if (maxSize == 1 && actualReadBufferSize > 0) {
|
||
|
*data = readBuffer.getChar();
|
||
|
actualReadBufferSize--;
|
||
|
readSoFar = 1;
|
||
|
} else {
|
||
|
const qint64 bytesToRead = qMin(qint64(actualReadBufferSize), maxSize);
|
||
|
readSoFar = 0;
|
||
|
while (readSoFar < bytesToRead) {
|
||
|
const char *ptr = readBuffer.readPointer();
|
||
|
const int bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar,
|
||
|
qint64(readBuffer.nextDataBlockSize()));
|
||
|
::memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
|
||
|
readSoFar += bytesToReadFromThisBlock;
|
||
|
readBuffer.free(bytesToReadFromThisBlock);
|
||
|
actualReadBufferSize -= bytesToReadFromThisBlock;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return readSoFar;
|
||
|
}
|
||
|
|
||
|
qint64 QSerialPortPrivate::writeToBuffer(const char *data, qint64 maxSize)
|
||
|
{
|
||
|
char *ptr = writeBuffer.reserve(maxSize);
|
||
|
if (maxSize == 1) {
|
||
|
*ptr = *data;
|
||
|
actualWriteBufferSize++;
|
||
|
} else {
|
||
|
::memcpy(ptr, data, maxSize);
|
||
|
actualWriteBufferSize += maxSize;
|
||
|
}
|
||
|
|
||
|
if (!writeSequenceStarted)
|
||
|
startAsyncWrite(WriteChunkSize);
|
||
|
|
||
|
return maxSize;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::waitForReadyRead(int msecs)
|
||
|
{
|
||
|
QElapsedTimer stopWatch;
|
||
|
stopWatch.start();
|
||
|
|
||
|
do {
|
||
|
bool timedOut = false;
|
||
|
AbstractOverlappedEventNotifier *n = 0;
|
||
|
|
||
|
if (!waitAnyEvent(timeoutValue(msecs, stopWatch.elapsed()), &timedOut, &n) || !n) {
|
||
|
// This is occur timeout or another error
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
switch (n->type()) {
|
||
|
case AbstractOverlappedEventNotifier::CommEvent:
|
||
|
if (!n->processCompletionRoutine())
|
||
|
return false;
|
||
|
break;
|
||
|
case AbstractOverlappedEventNotifier::ReadCompletionEvent:
|
||
|
return n->processCompletionRoutine();
|
||
|
case AbstractOverlappedEventNotifier::WriteCompletionEvent:
|
||
|
n->processCompletionRoutine();
|
||
|
break;
|
||
|
default: // newer called
|
||
|
return false;
|
||
|
}
|
||
|
} while (msecs == -1 || timeoutValue(msecs, stopWatch.elapsed()) > 0);
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::waitForBytesWritten(int msecs)
|
||
|
{
|
||
|
if (writeBuffer.isEmpty())
|
||
|
return false;
|
||
|
|
||
|
QElapsedTimer stopWatch;
|
||
|
stopWatch.start();
|
||
|
|
||
|
forever {
|
||
|
bool timedOut = false;
|
||
|
AbstractOverlappedEventNotifier *n = 0;
|
||
|
|
||
|
if (!waitAnyEvent(timeoutValue(msecs, stopWatch.elapsed()), &timedOut, &n) || !n) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
switch (n->type()) {
|
||
|
case AbstractOverlappedEventNotifier::CommEvent:
|
||
|
// do nothing, jump to ReadCompletionEvent case
|
||
|
case AbstractOverlappedEventNotifier::ReadCompletionEvent:
|
||
|
n->processCompletionRoutine();
|
||
|
break;
|
||
|
case AbstractOverlappedEventNotifier::WriteCompletionEvent:
|
||
|
return n->processCompletionRoutine();
|
||
|
default: // newer called
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#endif // #ifndef Q_OS_WINCE
|
||
|
|
||
|
bool QSerialPortPrivate::setBaudRate(qint32 baudRate, QSerialPort::Directions dir)
|
||
|
{
|
||
|
if (dir != QSerialPort::AllDirections) {
|
||
|
q_ptr->setError(QSerialPort::UnsupportedOperationError);
|
||
|
return false;
|
||
|
}
|
||
|
currentDcb.BaudRate = baudRate;
|
||
|
return updateDcb();
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setDataBits(QSerialPort::DataBits dataBits)
|
||
|
{
|
||
|
currentDcb.ByteSize = dataBits;
|
||
|
return updateDcb();
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setParity(QSerialPort::Parity parity)
|
||
|
{
|
||
|
currentDcb.fParity = TRUE;
|
||
|
switch (parity) {
|
||
|
case QSerialPort::NoParity:
|
||
|
currentDcb.Parity = NOPARITY;
|
||
|
currentDcb.fParity = FALSE;
|
||
|
break;
|
||
|
case QSerialPort::OddParity:
|
||
|
currentDcb.Parity = ODDPARITY;
|
||
|
break;
|
||
|
case QSerialPort::EvenParity:
|
||
|
currentDcb.Parity = EVENPARITY;
|
||
|
break;
|
||
|
case QSerialPort::MarkParity:
|
||
|
currentDcb.Parity = MARKPARITY;
|
||
|
break;
|
||
|
case QSerialPort::SpaceParity:
|
||
|
currentDcb.Parity = SPACEPARITY;
|
||
|
break;
|
||
|
default:
|
||
|
currentDcb.Parity = NOPARITY;
|
||
|
currentDcb.fParity = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
return updateDcb();
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setStopBits(QSerialPort::StopBits stopBits)
|
||
|
{
|
||
|
switch (stopBits) {
|
||
|
case QSerialPort::OneStop:
|
||
|
currentDcb.StopBits = ONESTOPBIT;
|
||
|
break;
|
||
|
case QSerialPort::OneAndHalfStop:
|
||
|
currentDcb.StopBits = ONE5STOPBITS;
|
||
|
break;
|
||
|
case QSerialPort::TwoStop:
|
||
|
currentDcb.StopBits = TWOSTOPBITS;
|
||
|
break;
|
||
|
default:
|
||
|
currentDcb.StopBits = ONESTOPBIT;
|
||
|
break;
|
||
|
}
|
||
|
return updateDcb();
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setFlowControl(QSerialPort::FlowControl flow)
|
||
|
{
|
||
|
currentDcb.fInX = FALSE;
|
||
|
currentDcb.fOutX = FALSE;
|
||
|
currentDcb.fOutxCtsFlow = FALSE;
|
||
|
currentDcb.fRtsControl = RTS_CONTROL_DISABLE;
|
||
|
switch (flow) {
|
||
|
case QSerialPort::NoFlowControl:
|
||
|
break;
|
||
|
case QSerialPort::SoftwareControl:
|
||
|
currentDcb.fInX = TRUE;
|
||
|
currentDcb.fOutX = TRUE;
|
||
|
break;
|
||
|
case QSerialPort::HardwareControl:
|
||
|
currentDcb.fOutxCtsFlow = TRUE;
|
||
|
currentDcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return updateDcb();
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setDataErrorPolicy(QSerialPort::DataErrorPolicy policy)
|
||
|
{
|
||
|
policy = policy;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
#ifndef Q_OS_WINCE
|
||
|
|
||
|
bool QSerialPortPrivate::startAsyncRead()
|
||
|
{
|
||
|
DWORD bytesToRead = policy == QSerialPort::IgnorePolicy ? ReadChunkSize : 1;
|
||
|
|
||
|
if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
|
||
|
bytesToRead = readBufferMaxSize - readBuffer.size();
|
||
|
if (bytesToRead == 0) {
|
||
|
// Buffer is full. User must read data from the buffer
|
||
|
// before we can read more from the port.
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
AbstractOverlappedEventNotifier *n = lookupReadCompletionNotifier();
|
||
|
if (!n) {
|
||
|
q_ptr->setError(QSerialPort::ResourceError);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
char *ptr = readBuffer.reserve(bytesToRead);
|
||
|
|
||
|
if (::ReadFile(descriptor, ptr, bytesToRead, NULL, n->overlappedPointer()))
|
||
|
return true;
|
||
|
|
||
|
QSerialPort::SerialPortError error = decodeSystemError();
|
||
|
if (error != QSerialPort::NoError) {
|
||
|
if (error != QSerialPort::ResourceError)
|
||
|
error = QSerialPort::ReadError;
|
||
|
q_ptr->setError(error);
|
||
|
|
||
|
readBuffer.truncate(actualReadBufferSize);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::startAsyncWrite(int maxSize)
|
||
|
{
|
||
|
qint64 nextSize = 0;
|
||
|
const char *ptr = writeBuffer.readPointerAtPosition(acyncWritePosition, nextSize);
|
||
|
|
||
|
nextSize = qMin(nextSize, qint64(maxSize));
|
||
|
acyncWritePosition += nextSize;
|
||
|
|
||
|
// no more data to write
|
||
|
if (!ptr || nextSize == 0)
|
||
|
return true;
|
||
|
|
||
|
writeSequenceStarted = true;
|
||
|
|
||
|
AbstractOverlappedEventNotifier *n = lookupFreeWriteCompletionNotifier();
|
||
|
if (!n) {
|
||
|
q_ptr->setError(QSerialPort::ResourceError);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
n->setEnabled(true);
|
||
|
|
||
|
if (::WriteFile(descriptor, ptr, nextSize, NULL, n->overlappedPointer()))
|
||
|
return true;
|
||
|
|
||
|
QSerialPort::SerialPortError error = decodeSystemError();
|
||
|
if (error != QSerialPort::NoError) {
|
||
|
writeSequenceStarted = false;
|
||
|
|
||
|
if (error != QSerialPort::ResourceError)
|
||
|
error = QSerialPort::WriteError;
|
||
|
|
||
|
q_ptr->setError(error);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
#endif // #ifndef Q_OS_WINCE
|
||
|
|
||
|
bool QSerialPortPrivate::processIoErrors(bool error)
|
||
|
{
|
||
|
if (error) {
|
||
|
q_ptr->setError(QSerialPort::ResourceError);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
DWORD errors = 0;
|
||
|
const bool ret = ::ClearCommError(descriptor, &errors, NULL);
|
||
|
if (ret && errors) {
|
||
|
if (errors & CE_FRAME) {
|
||
|
q_ptr->setError(QSerialPort::FramingError);
|
||
|
} else if (errors & CE_RXPARITY) {
|
||
|
q_ptr->setError(QSerialPort::ParityError);
|
||
|
parityErrorOccurred = true;
|
||
|
} else if (errors & CE_BREAK) {
|
||
|
q_ptr->setError(QSerialPort::BreakConditionError);
|
||
|
} else {
|
||
|
q_ptr->setError(QSerialPort::UnknownError);
|
||
|
}
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#ifndef Q_OS_WINCE
|
||
|
|
||
|
bool QSerialPortPrivate::completeAsyncRead(DWORD numberOfBytes)
|
||
|
{
|
||
|
actualReadBufferSize += qint64(numberOfBytes);
|
||
|
readBuffer.truncate(actualReadBufferSize);
|
||
|
|
||
|
if (numberOfBytes > 0) {
|
||
|
|
||
|
// Process emulate policy.
|
||
|
if ((policy != QSerialPort::IgnorePolicy) && parityErrorOccurred) {
|
||
|
|
||
|
parityErrorOccurred = false;
|
||
|
|
||
|
// Ignore received character, remove it from buffer
|
||
|
if (policy == QSerialPort::SkipPolicy) {
|
||
|
readBuffer.getChar();
|
||
|
// Force returning without emitting a readyRead() signal
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Abort receiving
|
||
|
if (policy == QSerialPort::StopReceivingPolicy) {
|
||
|
readyReadEmitted = true;
|
||
|
emit q_ptr->readyRead();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Replace received character by zero
|
||
|
if (policy == QSerialPort::PassZeroPolicy) {
|
||
|
readBuffer.getChar();
|
||
|
readBuffer.putChar('\0');
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
readyReadEmitted = true;
|
||
|
emit q_ptr->readyRead();
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::completeAsyncWrite(DWORD numberOfBytes)
|
||
|
{
|
||
|
writeBuffer.free(numberOfBytes);
|
||
|
actualWriteBufferSize -= qint64(numberOfBytes);
|
||
|
acyncWritePosition -= qint64(numberOfBytes);
|
||
|
|
||
|
if (numberOfBytes > 0)
|
||
|
emit q_ptr->bytesWritten(numberOfBytes);
|
||
|
|
||
|
if (writeBuffer.isEmpty())
|
||
|
writeSequenceStarted = false;
|
||
|
else
|
||
|
startAsyncWrite(WriteChunkSize);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
AbstractOverlappedEventNotifier *QSerialPortPrivate::lookupFreeWriteCompletionNotifier()
|
||
|
{
|
||
|
// find first free not running write notifier
|
||
|
foreach (AbstractOverlappedEventNotifier *n, notifiers) {
|
||
|
if ((n->type() == AbstractOverlappedEventNotifier::WriteCompletionEvent)
|
||
|
&& !n->isEnabled()) {
|
||
|
return n;
|
||
|
}
|
||
|
}
|
||
|
// if all write notifiers in use, then create new write notifier
|
||
|
return new WriteOverlappedCompletionNotifier(this, q_ptr);
|
||
|
}
|
||
|
|
||
|
AbstractOverlappedEventNotifier *QSerialPortPrivate::lookupCommEventNotifier()
|
||
|
{
|
||
|
foreach (AbstractOverlappedEventNotifier *n, notifiers) {
|
||
|
if (n->type() == AbstractOverlappedEventNotifier::CommEvent)
|
||
|
return n;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
AbstractOverlappedEventNotifier *QSerialPortPrivate::lookupReadCompletionNotifier()
|
||
|
{
|
||
|
foreach (AbstractOverlappedEventNotifier *n, notifiers) {
|
||
|
if (n->type() == AbstractOverlappedEventNotifier::ReadCompletionEvent)
|
||
|
return n;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::updateDcb()
|
||
|
{
|
||
|
if (!::SetCommState(descriptor, ¤tDcb)) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::updateCommTimeouts()
|
||
|
{
|
||
|
if (!::SetCommTimeouts(descriptor, ¤tCommTimeouts)) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
#endif // #ifndef Q_OS_WINCE
|
||
|
|
||
|
void QSerialPortPrivate::detectDefaultSettings()
|
||
|
{
|
||
|
// Detect baud rate.
|
||
|
inputBaudRate = quint32(currentDcb.BaudRate);
|
||
|
outputBaudRate = inputBaudRate;
|
||
|
|
||
|
// Detect databits.
|
||
|
switch (currentDcb.ByteSize) {
|
||
|
case 5:
|
||
|
dataBits = QSerialPort::Data5;
|
||
|
break;
|
||
|
case 6:
|
||
|
dataBits = QSerialPort::Data6;
|
||
|
break;
|
||
|
case 7:
|
||
|
dataBits = QSerialPort::Data7;
|
||
|
break;
|
||
|
case 8:
|
||
|
dataBits = QSerialPort::Data8;
|
||
|
break;
|
||
|
default:
|
||
|
dataBits = QSerialPort::UnknownDataBits;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Detect parity.
|
||
|
if ((currentDcb.Parity == NOPARITY) && !currentDcb.fParity)
|
||
|
parity = QSerialPort::NoParity;
|
||
|
else if ((currentDcb.Parity == SPACEPARITY) && currentDcb.fParity)
|
||
|
parity = QSerialPort::SpaceParity;
|
||
|
else if ((currentDcb.Parity == MARKPARITY) && currentDcb.fParity)
|
||
|
parity = QSerialPort::MarkParity;
|
||
|
else if ((currentDcb.Parity == EVENPARITY) && currentDcb.fParity)
|
||
|
parity = QSerialPort::EvenParity;
|
||
|
else if ((currentDcb.Parity == ODDPARITY) && currentDcb.fParity)
|
||
|
parity = QSerialPort::OddParity;
|
||
|
else
|
||
|
parity = QSerialPort::UnknownParity;
|
||
|
|
||
|
// Detect stopbits.
|
||
|
switch (currentDcb.StopBits) {
|
||
|
case ONESTOPBIT:
|
||
|
stopBits = QSerialPort::OneStop;
|
||
|
break;
|
||
|
case ONE5STOPBITS:
|
||
|
stopBits = QSerialPort::OneAndHalfStop;
|
||
|
break;
|
||
|
case TWOSTOPBITS:
|
||
|
stopBits = QSerialPort::TwoStop;
|
||
|
break;
|
||
|
default:
|
||
|
stopBits = QSerialPort::UnknownStopBits;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Detect flow control.
|
||
|
if (!currentDcb.fOutxCtsFlow && (currentDcb.fRtsControl == RTS_CONTROL_DISABLE)
|
||
|
&& !currentDcb.fInX && !currentDcb.fOutX) {
|
||
|
flow = QSerialPort::NoFlowControl;
|
||
|
} else if (!currentDcb.fOutxCtsFlow && (currentDcb.fRtsControl == RTS_CONTROL_DISABLE)
|
||
|
&& currentDcb.fInX && currentDcb.fOutX) {
|
||
|
flow = QSerialPort::SoftwareControl;
|
||
|
} else if (currentDcb.fOutxCtsFlow && (currentDcb.fRtsControl == RTS_CONTROL_HANDSHAKE)
|
||
|
&& !currentDcb.fInX && !currentDcb.fOutX) {
|
||
|
flow = QSerialPort::HardwareControl;
|
||
|
} else
|
||
|
flow = QSerialPort::UnknownFlowControl;
|
||
|
}
|
||
|
|
||
|
QSerialPort::SerialPortError QSerialPortPrivate::decodeSystemError() const
|
||
|
{
|
||
|
QSerialPort::SerialPortError error;
|
||
|
switch (::GetLastError()) {
|
||
|
case ERROR_IO_PENDING:
|
||
|
error = QSerialPort::NoError;
|
||
|
break;
|
||
|
case ERROR_MORE_DATA:
|
||
|
error = QSerialPort::NoError;
|
||
|
break;
|
||
|
case ERROR_FILE_NOT_FOUND:
|
||
|
error = QSerialPort::DeviceNotFoundError;
|
||
|
break;
|
||
|
case ERROR_ACCESS_DENIED:
|
||
|
error = QSerialPort::PermissionError;
|
||
|
break;
|
||
|
case ERROR_INVALID_HANDLE:
|
||
|
error = QSerialPort::ResourceError;
|
||
|
break;
|
||
|
case ERROR_INVALID_PARAMETER:
|
||
|
error = QSerialPort::UnsupportedOperationError;
|
||
|
break;
|
||
|
case ERROR_BAD_COMMAND:
|
||
|
error = QSerialPort::ResourceError;
|
||
|
break;
|
||
|
case ERROR_DEVICE_REMOVED:
|
||
|
error = QSerialPort::ResourceError;
|
||
|
break;
|
||
|
default:
|
||
|
error = QSerialPort::UnknownError;
|
||
|
break;
|
||
|
}
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
#ifndef Q_OS_WINCE
|
||
|
|
||
|
bool QSerialPortPrivate::waitAnyEvent(int msecs, bool *timedOut,
|
||
|
AbstractOverlappedEventNotifier **triggeredNotifier)
|
||
|
{
|
||
|
Q_ASSERT(timedOut);
|
||
|
|
||
|
QVector<HANDLE> handles = notifiers.keys().toVector();
|
||
|
DWORD waitResult = ::WaitForMultipleObjects(handles.count(),
|
||
|
handles.constData(),
|
||
|
FALSE, // wait any event
|
||
|
qMax(msecs, 0));
|
||
|
if (waitResult == WAIT_TIMEOUT) {
|
||
|
*timedOut = true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (int(waitResult) > (handles.count() - 1))
|
||
|
return false;
|
||
|
|
||
|
HANDLE h = handles.at(waitResult - WAIT_OBJECT_0);
|
||
|
*triggeredNotifier = notifiers.value(h);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static const QLatin1String defaultPathPrefix("\\\\.\\");
|
||
|
|
||
|
QString QSerialPortPrivate::portNameToSystemLocation(const QString &port)
|
||
|
{
|
||
|
QString ret = port;
|
||
|
if (!ret.contains(defaultPathPrefix))
|
||
|
ret.prepend(defaultPathPrefix);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
QString QSerialPortPrivate::portNameFromSystemLocation(const QString &location)
|
||
|
{
|
||
|
QString ret = location;
|
||
|
if (ret.contains(defaultPathPrefix))
|
||
|
ret.remove(defaultPathPrefix);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#endif // #ifndef Q_OS_WINCE
|
||
|
|
||
|
// This table contains standard values of baud rates that
|
||
|
// are defined in MSDN and/or in Win SDK file winbase.h
|
||
|
|
||
|
static const QList<qint32> standardBaudRatePairList()
|
||
|
{
|
||
|
|
||
|
static const QList<qint32> standardBaudRatesTable = QList<qint32>()
|
||
|
|
||
|
#ifdef CBR_110
|
||
|
<< CBR_110
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_300
|
||
|
<< CBR_300
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_600
|
||
|
<< CBR_600
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_1200
|
||
|
<< CBR_1200
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_2400
|
||
|
<< CBR_2400
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_4800
|
||
|
<< CBR_4800
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_9600
|
||
|
<< CBR_9600
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_14400
|
||
|
<< CBR_14400
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_19200
|
||
|
<< CBR_19200
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_38400
|
||
|
<< CBR_38400
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_56000
|
||
|
<< CBR_56000
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_57600
|
||
|
<< CBR_57600
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_115200
|
||
|
<< CBR_115200
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_128000
|
||
|
<< CBR_128000
|
||
|
#endif
|
||
|
|
||
|
#ifdef CBR_256000
|
||
|
<< CBR_256000
|
||
|
#endif
|
||
|
;
|
||
|
|
||
|
return standardBaudRatesTable;
|
||
|
};
|
||
|
|
||
|
qint32 QSerialPortPrivate::baudRateFromSetting(qint32 setting)
|
||
|
{
|
||
|
const QList<qint32> baudRatePairs = standardBaudRatePairList();
|
||
|
const QList<qint32>::const_iterator baudRatePairListConstIterator = qFind(baudRatePairs, setting);
|
||
|
|
||
|
return (baudRatePairListConstIterator != baudRatePairs.constEnd()) ? *baudRatePairListConstIterator : 0;
|
||
|
}
|
||
|
|
||
|
qint32 QSerialPortPrivate::settingFromBaudRate(qint32 baudRate)
|
||
|
{
|
||
|
const QList<qint32> baudRatePairList = standardBaudRatePairList();
|
||
|
const QList<qint32>::const_iterator baudRatePairListConstIterator = qFind(baudRatePairList, baudRate);
|
||
|
|
||
|
return (baudRatePairListConstIterator != baudRatePairList.constEnd()) ? *baudRatePairListConstIterator : 0;
|
||
|
}
|
||
|
|
||
|
QList<qint32> QSerialPortPrivate::standardBaudRates()
|
||
|
{
|
||
|
QList<qint32> ret;
|
||
|
const QList<qint32> baudRatePairs = standardBaudRatePairList();
|
||
|
|
||
|
foreach (qint32 baudRatePair, baudRatePairs) {
|
||
|
ret.append(baudRatePair);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
QT_END_NAMESPACE
|