地面站终端 App
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.

1091 lines
30 KiB

/****************************************************************************
**
** 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(&currentCommTimeouts, 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, &currentDcb)) {
q_ptr->setError(decodeSystemError());
return false;
}
return true;
}
bool QSerialPortPrivate::updateCommTimeouts()
{
if (!::SetCommTimeouts(descriptor, &currentCommTimeouts)) {
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