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
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(¤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
|
|
|