mirror of
https://gitlab.com/pholy/OSCAR-code.git
synced 2025-04-24 06:30:45 +00:00
871 lines
22 KiB
C++
871 lines
22 KiB
C++
/****************************************************************************
|
|
**
|
|
** 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_wince_p.h"
|
|
|
|
#include <QtCore/qelapsedtimer.h>
|
|
#include <QtCore/qthread.h>
|
|
#include <QtCore/qtimer.h>
|
|
#include <algorithm>
|
|
|
|
#ifndef CTL_CODE
|
|
# define CTL_CODE(DeviceType, Function, Method, Access) ( \
|
|
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
|
|
)
|
|
#endif
|
|
|
|
#ifndef FILE_DEVICE_SERIAL_PORT
|
|
# define FILE_DEVICE_SERIAL_PORT 27
|
|
#endif
|
|
|
|
#ifndef METHOD_BUFFERED
|
|
# define METHOD_BUFFERED 0
|
|
#endif
|
|
|
|
#ifndef FILE_ANY_ACCESS
|
|
# define FILE_ANY_ACCESS 0x00000000
|
|
#endif
|
|
|
|
#ifndef IOCTL_SERIAL_GET_DTRRTS
|
|
# define IOCTL_SERIAL_GET_DTRRTS \
|
|
CTL_CODE(FILE_DEVICE_SERIAL_PORT, 30, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
#endif
|
|
|
|
#ifndef SERIAL_DTR_STATE
|
|
# define SERIAL_DTR_STATE 0x00000001
|
|
#endif
|
|
|
|
#ifndef SERIAL_RTS_STATE
|
|
# define SERIAL_RTS_STATE 0x00000002
|
|
#endif
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
class QSerialPortPrivate;
|
|
|
|
class CommEventNotifier : public QThread
|
|
{
|
|
Q_OBJECT
|
|
signals:
|
|
void eventMask(quint32 mask);
|
|
|
|
public:
|
|
CommEventNotifier(DWORD mask, QSerialPortPrivate *d, QObject *parent)
|
|
: QThread(parent), dptr(d), running(true) {
|
|
connect(this, SIGNAL(eventMask(quint32)), this, SLOT(processNotification(quint32)));
|
|
::SetCommMask(dptr->handle, mask);
|
|
}
|
|
|
|
virtual ~CommEventNotifier() {
|
|
running = false;
|
|
::SetCommMask(dptr->handle, 0);
|
|
wait();
|
|
}
|
|
|
|
protected:
|
|
void run() Q_DECL_OVERRIDE {
|
|
DWORD mask = 0;
|
|
while (running) {
|
|
if (::WaitCommEvent(dptr->handle, &mask, FALSE)) {
|
|
// Wait until complete the operation changes the port settings,
|
|
// see updateDcb().
|
|
dptr->settingsChangeMutex.lock();
|
|
dptr->settingsChangeMutex.unlock();
|
|
emit eventMask(quint32(mask));
|
|
}
|
|
}
|
|
}
|
|
|
|
private slots:
|
|
void processNotification(quint32 eventMask) {
|
|
|
|
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 ((eventMask == 0)
|
|
|| ((eventMask & (EV_ERR | EV_RXCHAR | EV_TXEMPTY)) == 0)) {
|
|
error = true;
|
|
}
|
|
|
|
if (error || (EV_ERR & eventMask))
|
|
dptr->processIoErrors(error);
|
|
if (EV_RXCHAR & eventMask)
|
|
dptr->notifyRead();
|
|
if (EV_TXEMPTY & eventMask)
|
|
dptr->notifyWrite();
|
|
}
|
|
|
|
private:
|
|
QSerialPortPrivate *dptr;
|
|
mutable bool running;
|
|
};
|
|
|
|
class WaitCommEventBreaker : public QThread
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
WaitCommEventBreaker(HANDLE handle, int timeout, QObject *parent = Q_NULLPTR)
|
|
: QThread(parent), handle(handle), timeout(timeout), worked(false) {
|
|
start();
|
|
}
|
|
|
|
virtual ~WaitCommEventBreaker() {
|
|
stop();
|
|
wait();
|
|
}
|
|
|
|
void stop() {
|
|
exit(0);
|
|
}
|
|
|
|
bool isWorked() const {
|
|
return worked;
|
|
}
|
|
|
|
protected:
|
|
void run() {
|
|
QTimer timer;
|
|
QObject::connect(&timer, SIGNAL(timeout()), this, SLOT(processTimeout()), Qt::DirectConnection);
|
|
timer.start(timeout);
|
|
exec();
|
|
worked = true;
|
|
}
|
|
|
|
private slots:
|
|
void processTimeout() {
|
|
::SetCommMask(handle, 0);
|
|
stop();
|
|
}
|
|
|
|
private:
|
|
HANDLE handle;
|
|
int timeout;
|
|
mutable bool worked;
|
|
};
|
|
|
|
#include "qserialport_wince.moc"
|
|
|
|
QSerialPortPrivate::QSerialPortPrivate(QSerialPort *q)
|
|
: QSerialPortPrivateData(q)
|
|
, handle(INVALID_HANDLE_VALUE)
|
|
, parityErrorOccurred(false)
|
|
, eventNotifier(0)
|
|
{
|
|
}
|
|
|
|
bool QSerialPortPrivate::open(QIODevice::OpenMode mode)
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
DWORD desiredAccess = 0;
|
|
DWORD eventMask = EV_ERR;
|
|
|
|
if (mode & QIODevice::ReadOnly) {
|
|
desiredAccess |= GENERIC_READ;
|
|
eventMask |= EV_RXCHAR;
|
|
}
|
|
if (mode & QIODevice::WriteOnly) {
|
|
desiredAccess |= GENERIC_WRITE;
|
|
eventMask |= EV_TXEMPTY;
|
|
}
|
|
|
|
handle = ::CreateFile(reinterpret_cast<const wchar_t*>(systemLocation.utf16()),
|
|
desiredAccess, 0, NULL, OPEN_EXISTING, 0, NULL);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
q->setError(decodeSystemError());
|
|
return false;
|
|
}
|
|
|
|
::ZeroMemory(&restoredDcb, sizeof(restoredDcb));
|
|
restoredDcb.DCBlength = sizeof(restoredDcb);
|
|
|
|
if (!::GetCommState(handle, &restoredDcb)) {
|
|
q->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 (currentDcb.fDtrControl == DTR_CONTROL_HANDSHAKE)
|
|
currentDcb.fDtrControl = DTR_CONTROL_DISABLE;
|
|
|
|
if (!updateDcb())
|
|
return false;
|
|
|
|
if (!::GetCommTimeouts(handle, &restoredCommTimeouts)) {
|
|
q->setError(decodeSystemError());
|
|
return false;
|
|
}
|
|
|
|
::memset(¤tCommTimeouts, 0, sizeof(currentCommTimeouts));
|
|
currentCommTimeouts.ReadIntervalTimeout = MAXDWORD;
|
|
|
|
if (!updateCommTimeouts())
|
|
return false;
|
|
|
|
eventNotifier = new CommEventNotifier(eventMask, this, q);
|
|
eventNotifier->start();
|
|
|
|
return true;
|
|
}
|
|
|
|
void QSerialPortPrivate::close()
|
|
{
|
|
if (eventNotifier) {
|
|
eventNotifier->deleteLater();
|
|
eventNotifier = 0;
|
|
}
|
|
|
|
if (settingsRestoredOnClose) {
|
|
::SetCommState(handle, &restoredDcb);
|
|
::SetCommTimeouts(handle, &restoredCommTimeouts);
|
|
}
|
|
|
|
::CloseHandle(handle);
|
|
handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
QSerialPort::PinoutSignals QSerialPortPrivate::pinoutSignals()
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
DWORD modemStat = 0;
|
|
|
|
if (!::GetCommModemStatus(handle, &modemStat)) {
|
|
q->setError(decodeSystemError());
|
|
return QSerialPort::NoSignal;
|
|
}
|
|
|
|
QSerialPort::PinoutSignals ret = QSerialPort::NoSignal;
|
|
|
|
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(handle, IOCTL_SERIAL_GET_DTRRTS, NULL, 0,
|
|
&modemStat, sizeof(modemStat),
|
|
&bytesReturned, NULL)) {
|
|
q->setError(decodeSystemError());
|
|
return ret;
|
|
}
|
|
|
|
if (modemStat & SERIAL_DTR_STATE)
|
|
ret |= QSerialPort::DataTerminalReadySignal;
|
|
if (modemStat & SERIAL_RTS_STATE)
|
|
ret |= QSerialPort::RequestToSendSignal;
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool QSerialPortPrivate::setDataTerminalReady(bool set)
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
if (!::EscapeCommFunction(handle, set ? SETDTR : CLRDTR)) {
|
|
q->setError(decodeSystemError());
|
|
return false;
|
|
}
|
|
|
|
currentDcb.fDtrControl = set ? DTR_CONTROL_ENABLE : DTR_CONTROL_DISABLE;
|
|
return true;
|
|
}
|
|
|
|
bool QSerialPortPrivate::setRequestToSend(bool set)
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
if (!::EscapeCommFunction(handle, set ? SETRTS : CLRRTS)) {
|
|
q->setError(decodeSystemError());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool QSerialPortPrivate::flush()
|
|
{
|
|
return notifyWrite() && ::FlushFileBuffers(handle);
|
|
}
|
|
|
|
bool QSerialPortPrivate::clear(QSerialPort::Directions directions)
|
|
{
|
|
DWORD flags = 0;
|
|
if (directions & QSerialPort::Input)
|
|
flags |= PURGE_RXABORT | PURGE_RXCLEAR;
|
|
if (directions & QSerialPort::Output)
|
|
flags |= PURGE_TXABORT | PURGE_TXCLEAR;
|
|
return ::PurgeComm(handle, flags);
|
|
}
|
|
|
|
bool QSerialPortPrivate::sendBreak(int duration)
|
|
{
|
|
if (!setBreakEnabled(true))
|
|
return false;
|
|
|
|
::Sleep(duration);
|
|
|
|
if (!setBreakEnabled(false))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool QSerialPortPrivate::setBreakEnabled(bool set)
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
if (set ? !::SetCommBreak(handle) : !::ClearCommBreak(handle)) {
|
|
q->setError(decodeSystemError());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void QSerialPortPrivate::startWriting()
|
|
{
|
|
// trigger write sequence
|
|
notifyWrite();
|
|
}
|
|
|
|
bool QSerialPortPrivate::waitForReadyRead(int msec)
|
|
{
|
|
if (!readBuffer.isEmpty())
|
|
return true;
|
|
|
|
QElapsedTimer stopWatch;
|
|
|
|
stopWatch.start();
|
|
|
|
forever {
|
|
bool readyToRead = false;
|
|
bool readyToWrite = false;
|
|
bool timedOut = false;
|
|
if (!waitForReadOrWrite(&readyToRead, &readyToWrite,
|
|
true, !writeBuffer.isEmpty(),
|
|
timeoutValue(msec, stopWatch.elapsed()),
|
|
&timedOut)) {
|
|
return false;
|
|
}
|
|
if (readyToRead) {
|
|
if (notifyRead())
|
|
return true;
|
|
}
|
|
if (readyToWrite)
|
|
notifyWrite();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QSerialPortPrivate::waitForBytesWritten(int msec)
|
|
{
|
|
if (writeBuffer.isEmpty())
|
|
return false;
|
|
|
|
QElapsedTimer stopWatch;
|
|
|
|
stopWatch.start();
|
|
|
|
forever {
|
|
bool readyToRead = false;
|
|
bool readyToWrite = false;
|
|
bool timedOut = false;
|
|
if (!waitForReadOrWrite(&readyToRead, &readyToWrite,
|
|
true, !writeBuffer.isEmpty(),
|
|
timeoutValue(msec, stopWatch.elapsed()),
|
|
&timedOut)) {
|
|
return false;
|
|
}
|
|
if (readyToRead) {
|
|
if (!notifyRead())
|
|
return false;
|
|
}
|
|
if (readyToWrite) {
|
|
if (notifyWrite())
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QSerialPortPrivate::setBaudRate()
|
|
{
|
|
return setBaudRate(inputBaudRate, QSerialPort::AllDirections);
|
|
}
|
|
|
|
bool QSerialPortPrivate::setBaudRate(qint32 baudRate, QSerialPort::Directions directions)
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
if (directions != QSerialPort::AllDirections) {
|
|
q->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 flowControl)
|
|
{
|
|
currentDcb.fInX = FALSE;
|
|
currentDcb.fOutX = FALSE;
|
|
currentDcb.fOutxCtsFlow = FALSE;
|
|
currentDcb.fRtsControl = RTS_CONTROL_DISABLE;
|
|
switch (flowControl) {
|
|
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;
|
|
}
|
|
|
|
bool QSerialPortPrivate::notifyRead()
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
char *ptr = readBuffer.reserve(bytesToRead);
|
|
|
|
DWORD readBytes = 0;
|
|
BOOL sucessResult = ::ReadFile(handle, ptr, bytesToRead, &readBytes, NULL);
|
|
|
|
if (!sucessResult) {
|
|
readBuffer.truncate(bytesToRead);
|
|
q->setError(QSerialPort::ReadError);
|
|
return false;
|
|
}
|
|
|
|
readBuffer.truncate(readBytes);
|
|
|
|
// Process emulate policy.
|
|
if ((policy != QSerialPort::IgnorePolicy) && parityErrorOccurred) {
|
|
|
|
parityErrorOccurred = false;
|
|
|
|
switch (policy) {
|
|
case QSerialPort::SkipPolicy:
|
|
readBuffer.getChar();
|
|
return true;
|
|
case QSerialPort::PassZeroPolicy:
|
|
readBuffer.getChar();
|
|
readBuffer.putChar('\0');
|
|
break;
|
|
case QSerialPort::StopReceivingPolicy:
|
|
// FIXME: Maybe need disable read notifier?
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (readBytes > 0)
|
|
emit q->readyRead();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool QSerialPortPrivate::notifyWrite()
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
int nextSize = writeBuffer.nextDataBlockSize();
|
|
|
|
const char *ptr = writeBuffer.readPointer();
|
|
|
|
DWORD bytesWritten = 0;
|
|
if (!::WriteFile(handle, ptr, nextSize, &bytesWritten, NULL)) {
|
|
q->setError(QSerialPort::WriteError);
|
|
return false;
|
|
}
|
|
|
|
writeBuffer.free(bytesWritten);
|
|
|
|
if (bytesWritten > 0)
|
|
emit q->bytesWritten(bytesWritten);
|
|
|
|
return true;
|
|
}
|
|
|
|
void QSerialPortPrivate::processIoErrors(bool error)
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
if (error) {
|
|
q->setError(QSerialPort::ResourceError);
|
|
return;
|
|
}
|
|
|
|
DWORD errors = 0;
|
|
if (!::ClearCommError(handle, &errors, NULL)) {
|
|
q->setError(decodeSystemError());
|
|
return;
|
|
}
|
|
|
|
if (errors & CE_FRAME) {
|
|
q->setError(QSerialPort::FramingError);
|
|
} else if (errors & CE_RXPARITY) {
|
|
q->setError(QSerialPort::ParityError);
|
|
parityErrorOccurred = true;
|
|
} else if (errors & CE_BREAK) {
|
|
q->setError(QSerialPort::BreakConditionError);
|
|
} else {
|
|
q->setError(QSerialPort::UnknownError);
|
|
}
|
|
}
|
|
|
|
bool QSerialPortPrivate::updateDcb()
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
QMutexLocker locker(&settingsChangeMutex);
|
|
|
|
DWORD eventMask = 0;
|
|
// Save the event mask
|
|
if (!::GetCommMask(handle, &eventMask))
|
|
return false;
|
|
|
|
// Break event notifier from WaitCommEvent
|
|
::SetCommMask(handle, 0);
|
|
// Change parameters
|
|
bool ret = ::SetCommState(handle, ¤tDcb);
|
|
if (!ret)
|
|
q->setError(decodeSystemError());
|
|
// Restore the event mask
|
|
::SetCommMask(handle, eventMask);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool QSerialPortPrivate::updateCommTimeouts()
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
if (!::SetCommTimeouts(handle, ¤tCommTimeouts)) {
|
|
q->setError(decodeSystemError());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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_INVALID_NAME:
|
|
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;
|
|
}
|
|
|
|
bool QSerialPortPrivate::waitForReadOrWrite(bool *selectForRead, bool *selectForWrite,
|
|
bool checkRead, bool checkWrite,
|
|
int msecs, bool *timedOut)
|
|
{
|
|
Q_Q(QSerialPort);
|
|
|
|
DWORD eventMask = 0;
|
|
// 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!
|
|
WaitCommEventBreaker breaker(handle, qMax(msecs, 0));
|
|
::WaitCommEvent(handle, &eventMask, NULL);
|
|
breaker.stop();
|
|
|
|
if (breaker.isWorked()) {
|
|
*timedOut = true;
|
|
q->setError(QSerialPort::TimeoutError);
|
|
}
|
|
|
|
if (!breaker.isWorked()) {
|
|
if (checkRead) {
|
|
Q_ASSERT(selectForRead);
|
|
*selectForRead = eventMask & EV_RXCHAR;
|
|
}
|
|
if (checkWrite) {
|
|
Q_ASSERT(selectForWrite);
|
|
*selectForWrite = eventMask & EV_TXEMPTY;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
QString QSerialPortPrivate::portNameToSystemLocation(const QString &port)
|
|
{
|
|
QString ret = port;
|
|
if (!ret.contains(QLatin1Char(':')))
|
|
ret.append(QLatin1Char(':'));
|
|
return ret;
|
|
}
|
|
|
|
QString QSerialPortPrivate::portNameFromSystemLocation(const QString &location)
|
|
{
|
|
QString ret = location;
|
|
if (ret.contains(QLatin1Char(':')))
|
|
ret.remove(QLatin1Char(':'));
|
|
return ret;
|
|
}
|
|
|
|
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
|
|
= std::find(baudRatePairs.constBegin(), baudRatePairs.constEnd(), setting);
|
|
|
|
return (baudRatePairListConstIterator != baudRatePairs.constEnd()) ? *baudRatePairListConstIterator : 0;
|
|
}
|
|
|
|
qint32 QSerialPortPrivate::settingFromBaudRate(qint32 baudRate)
|
|
{
|
|
const QList<qint32> baudRatePairList = standardBaudRatePairList();
|
|
const QList<qint32>::const_iterator baudRatePairListConstIterator
|
|
= std::find(baudRatePairList.constBegin(), baudRatePairList.constEnd(), baudRate);
|
|
|
|
return (baudRatePairListConstIterator != baudRatePairList.constEnd()) ? *baudRatePairListConstIterator : 0;
|
|
}
|
|
|
|
QList<qint32> QSerialPortPrivate::standardBaudRates()
|
|
{
|
|
return standardBaudRatePairList();
|
|
}
|
|
|
|
QSerialPort::Handle QSerialPort::handle() const
|
|
{
|
|
Q_D(const QSerialPort);
|
|
return d->handle;
|
|
}
|
|
|
|
QT_END_NAMESPACE
|