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.
1344 lines
36 KiB
1344 lines
36 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_unix_p.h"
|
||
|
#include "qttylocker_unix_p.h"
|
||
|
|
||
|
#include <errno.h>
|
||
|
#include <sys/time.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#ifdef Q_OS_MAC
|
||
|
#if defined (MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
|
||
|
#include <IOKit/serial/ioss.h>
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#include <QtCore/qelapsedtimer.h>
|
||
|
#include <QtCore/qsocketnotifier.h>
|
||
|
#include <QtCore/qmap.h>
|
||
|
|
||
|
QT_BEGIN_NAMESPACE
|
||
|
|
||
|
class ReadNotifier : public QSocketNotifier
|
||
|
{
|
||
|
public:
|
||
|
ReadNotifier(QSerialPortPrivate *d, QObject *parent)
|
||
|
: QSocketNotifier(d->descriptor, QSocketNotifier::Read, parent)
|
||
|
, dptr(d)
|
||
|
{}
|
||
|
|
||
|
protected:
|
||
|
bool event(QEvent *e) Q_DECL_OVERRIDE {
|
||
|
bool ret = QSocketNotifier::event(e);
|
||
|
if (ret)
|
||
|
dptr->readNotification();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
QSerialPortPrivate *dptr;
|
||
|
};
|
||
|
|
||
|
class WriteNotifier : public QSocketNotifier
|
||
|
{
|
||
|
public:
|
||
|
WriteNotifier(QSerialPortPrivate *d, QObject *parent)
|
||
|
: QSocketNotifier(d->descriptor, QSocketNotifier::Write, parent)
|
||
|
, dptr(d)
|
||
|
{}
|
||
|
|
||
|
protected:
|
||
|
bool event(QEvent *e) Q_DECL_OVERRIDE {
|
||
|
bool ret = QSocketNotifier::event(e);
|
||
|
if (ret)
|
||
|
dptr->writeNotification(QSerialPortPrivateData::WriteChunkSize);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
QSerialPortPrivate *dptr;
|
||
|
};
|
||
|
|
||
|
class ExceptionNotifier : public QSocketNotifier
|
||
|
{
|
||
|
public:
|
||
|
ExceptionNotifier(QSerialPortPrivate *d, QObject *parent)
|
||
|
: QSocketNotifier(d->descriptor, QSocketNotifier::Exception, parent)
|
||
|
, dptr(d)
|
||
|
{}
|
||
|
|
||
|
protected:
|
||
|
bool event(QEvent *e) Q_DECL_OVERRIDE {
|
||
|
bool ret = QSocketNotifier::event(e);
|
||
|
if (ret)
|
||
|
dptr->exceptionNotification();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
QSerialPortPrivate *dptr;
|
||
|
};
|
||
|
|
||
|
QSerialPortPrivate::QSerialPortPrivate(QSerialPort *q)
|
||
|
: QSerialPortPrivateData(q)
|
||
|
, descriptor(-1)
|
||
|
, isCustomBaudRateSupported(false)
|
||
|
, readNotifier(0)
|
||
|
, writeNotifier(0)
|
||
|
, exceptionNotifier(0)
|
||
|
, readPortNotifierCalled(false)
|
||
|
, readPortNotifierState(false)
|
||
|
, readPortNotifierStateSet(false)
|
||
|
, emittedReadyRead(false)
|
||
|
, emittedBytesWritten(false)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::open(QIODevice::OpenMode mode)
|
||
|
{
|
||
|
QByteArray portName = portNameFromSystemLocation(systemLocation).toLocal8Bit();
|
||
|
const char *ptr = portName.constData();
|
||
|
|
||
|
bool byCurrPid = false;
|
||
|
if (QTtyLocker::isLocked(ptr, &byCurrPid)) {
|
||
|
q_ptr->setError(QSerialPort::PermissionError);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int flags = O_NOCTTY | O_NONBLOCK;
|
||
|
|
||
|
switch (mode & QIODevice::ReadWrite) {
|
||
|
case QIODevice::WriteOnly:
|
||
|
flags |= O_WRONLY;
|
||
|
break;
|
||
|
case QIODevice::ReadWrite:
|
||
|
flags |= O_RDWR;
|
||
|
break;
|
||
|
default:
|
||
|
flags |= O_RDONLY;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
descriptor = ::open(systemLocation.toLocal8Bit().constData(), flags);
|
||
|
|
||
|
if (descriptor == -1) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
::fcntl(descriptor, F_SETFL, FNDELAY);
|
||
|
|
||
|
QTtyLocker::lock(ptr);
|
||
|
if (!QTtyLocker::isLocked(ptr, &byCurrPid)) {
|
||
|
q_ptr->setError(QSerialPort::PermissionError);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#ifdef TIOCEXCL
|
||
|
::ioctl(descriptor, TIOCEXCL);
|
||
|
#endif
|
||
|
|
||
|
if (::tcgetattr(descriptor, &restoredTermios) == -1) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
currentTermios = restoredTermios;
|
||
|
::cfmakeraw(¤tTermios);
|
||
|
currentTermios.c_cflag |= CLOCAL;
|
||
|
currentTermios.c_cc[VTIME] = 0;
|
||
|
currentTermios.c_cc[VMIN] = 0;
|
||
|
|
||
|
if (mode & QIODevice::ReadOnly)
|
||
|
currentTermios.c_cflag |= CREAD;
|
||
|
|
||
|
if (!updateTermios())
|
||
|
return false;
|
||
|
|
||
|
setExceptionNotificationEnabled(true);
|
||
|
|
||
|
if ((flags & O_WRONLY) == 0)
|
||
|
setReadNotificationEnabled(true);
|
||
|
|
||
|
detectDefaultSettings();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void QSerialPortPrivate::close()
|
||
|
{
|
||
|
if (settingsRestoredOnClose) {
|
||
|
::tcsetattr(descriptor, TCSANOW, &restoredTermios);
|
||
|
#ifdef Q_OS_LINUX
|
||
|
if (isCustomBaudRateSupported)
|
||
|
::ioctl(descriptor, TIOCSSERIAL, &restoredSerialInfo);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#ifdef TIOCNXCL
|
||
|
::ioctl(descriptor, TIOCNXCL);
|
||
|
#endif
|
||
|
|
||
|
if (readNotifier) {
|
||
|
readNotifier->setEnabled(false);
|
||
|
readNotifier->deleteLater();
|
||
|
readNotifier = 0;
|
||
|
}
|
||
|
|
||
|
if (writeNotifier) {
|
||
|
writeNotifier->setEnabled(false);
|
||
|
writeNotifier->deleteLater();
|
||
|
writeNotifier = 0;
|
||
|
}
|
||
|
|
||
|
if (exceptionNotifier) {
|
||
|
exceptionNotifier->setEnabled(false);
|
||
|
exceptionNotifier->deleteLater();
|
||
|
exceptionNotifier = 0;
|
||
|
}
|
||
|
|
||
|
::close(descriptor);
|
||
|
|
||
|
QByteArray portName = portNameFromSystemLocation(systemLocation).toLocal8Bit();
|
||
|
const char *ptr = portName.constData();
|
||
|
|
||
|
bool byCurrPid = false;
|
||
|
if (QTtyLocker::isLocked(ptr, &byCurrPid) && byCurrPid)
|
||
|
QTtyLocker::unlock(ptr);
|
||
|
|
||
|
descriptor = -1;
|
||
|
isCustomBaudRateSupported = false;
|
||
|
}
|
||
|
|
||
|
QSerialPort::PinoutSignals QSerialPortPrivate::pinoutSignals() const
|
||
|
{
|
||
|
int arg = 0;
|
||
|
QSerialPort::PinoutSignals ret = QSerialPort::NoSignal;
|
||
|
|
||
|
if (::ioctl(descriptor, TIOCMGET, &arg) == -1) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#ifdef TIOCM_LE
|
||
|
if (arg & TIOCM_LE)
|
||
|
ret |= QSerialPort::DataSetReadySignal;
|
||
|
#endif
|
||
|
#ifdef TIOCM_DTR
|
||
|
if (arg & TIOCM_DTR)
|
||
|
ret |= QSerialPort::DataTerminalReadySignal;
|
||
|
#endif
|
||
|
#ifdef TIOCM_RTS
|
||
|
if (arg & TIOCM_RTS)
|
||
|
ret |= QSerialPort::RequestToSendSignal;
|
||
|
#endif
|
||
|
#ifdef TIOCM_ST
|
||
|
if (arg & TIOCM_ST)
|
||
|
ret |= QSerialPort::SecondaryTransmittedDataSignal;
|
||
|
#endif
|
||
|
#ifdef TIOCM_SR
|
||
|
if (arg & TIOCM_SR)
|
||
|
ret |= QSerialPort::SecondaryReceivedDataSignal;
|
||
|
#endif
|
||
|
#ifdef TIOCM_CTS
|
||
|
if (arg & TIOCM_CTS)
|
||
|
ret |= QSerialPort::ClearToSendSignal;
|
||
|
#endif
|
||
|
#ifdef TIOCM_CAR
|
||
|
if (arg & TIOCM_CAR)
|
||
|
ret |= QSerialPort::DataCarrierDetectSignal;
|
||
|
#elif defined TIOCM_CD
|
||
|
if (arg & TIOCM_CD)
|
||
|
ret |= QSerialPort::DataCarrierDetectSignal;
|
||
|
#endif
|
||
|
#ifdef TIOCM_RNG
|
||
|
if (arg & TIOCM_RNG)
|
||
|
ret |= QSerialPort::RingIndicatorSignal;
|
||
|
#elif defined TIOCM_RI
|
||
|
if (arg & TIOCM_RI)
|
||
|
ret |= QSerialPort::RingIndicatorSignal;
|
||
|
#endif
|
||
|
#ifdef TIOCM_DSR
|
||
|
if (arg & TIOCM_DSR)
|
||
|
ret |= QSerialPort::DataSetReadySignal;
|
||
|
#endif
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setDataTerminalReady(bool set)
|
||
|
{
|
||
|
int status = TIOCM_DTR;
|
||
|
return ::ioctl(descriptor, set ? TIOCMBIS : TIOCMBIC, &status) != -1;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setRequestToSend(bool set)
|
||
|
{
|
||
|
int status = TIOCM_RTS;
|
||
|
return ::ioctl(descriptor, set ? TIOCMBIS : TIOCMBIC, &status) != -1;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::flush()
|
||
|
{
|
||
|
return writeNotification() && (::tcdrain(descriptor) != -1);
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::clear(QSerialPort::Directions dir)
|
||
|
{
|
||
|
return ::tcflush(descriptor, (dir == QSerialPort::AllDirections)
|
||
|
? TCIOFLUSH : (dir & QSerialPort::Input) ? TCIFLUSH : TCOFLUSH) != -1;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::sendBreak(int duration)
|
||
|
{
|
||
|
return ::tcsendbreak(descriptor, duration) != -1;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setBreakEnabled(bool set)
|
||
|
{
|
||
|
return ::ioctl(descriptor, set ? TIOCSBRK : TIOCCBRK) != -1;
|
||
|
}
|
||
|
|
||
|
qint64 QSerialPortPrivate::systemInputQueueSize () const
|
||
|
{
|
||
|
int nbytes = 0;
|
||
|
#ifdef TIOCINQ
|
||
|
if (::ioctl(descriptor, TIOCINQ, &nbytes) == -1)
|
||
|
return -1;
|
||
|
#endif
|
||
|
return nbytes;
|
||
|
}
|
||
|
|
||
|
qint64 QSerialPortPrivate::systemOutputQueueSize () const
|
||
|
{
|
||
|
int nbytes = 0;
|
||
|
#ifdef TIOCOUTQ
|
||
|
if (::ioctl(descriptor, TIOCOUTQ, &nbytes) == -1)
|
||
|
return -1;
|
||
|
#endif
|
||
|
return nbytes;
|
||
|
}
|
||
|
|
||
|
qint64 QSerialPortPrivate::bytesAvailable() const
|
||
|
{
|
||
|
return readBuffer.size();
|
||
|
}
|
||
|
|
||
|
qint64 QSerialPortPrivate::readFromBuffer(char *data, qint64 maxSize)
|
||
|
{
|
||
|
if (readBuffer.isEmpty())
|
||
|
return 0;
|
||
|
|
||
|
if (maxSize == 1) {
|
||
|
*data = readBuffer.getChar();
|
||
|
if (readBuffer.isEmpty())
|
||
|
setReadNotificationEnabled(true);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
const qint64 bytesToRead = qMin(qint64(readBuffer.size()), maxSize);
|
||
|
qint64 readSoFar = 0;
|
||
|
while (readSoFar < bytesToRead) {
|
||
|
const char *ptr = readBuffer.readPointer();
|
||
|
const int bytesToReadFromThisBlock = qMin(int(bytesToRead - readSoFar),
|
||
|
readBuffer.nextDataBlockSize());
|
||
|
::memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
|
||
|
readSoFar += bytesToReadFromThisBlock;
|
||
|
readBuffer.free(bytesToReadFromThisBlock);
|
||
|
}
|
||
|
|
||
|
if (!isReadNotificationEnabled())
|
||
|
setReadNotificationEnabled(true);
|
||
|
|
||
|
if (readSoFar > 0) {
|
||
|
if (readBuffer.isEmpty())
|
||
|
setReadNotificationEnabled(true);
|
||
|
return readSoFar;
|
||
|
}
|
||
|
|
||
|
return readSoFar;
|
||
|
}
|
||
|
|
||
|
qint64 QSerialPortPrivate::writeToBuffer(const char *data, qint64 maxSize)
|
||
|
{
|
||
|
char *ptr = writeBuffer.reserve(maxSize);
|
||
|
if (maxSize == 1)
|
||
|
*ptr = *data;
|
||
|
else
|
||
|
::memcpy(ptr, data, maxSize);
|
||
|
|
||
|
const qint64 written = maxSize;
|
||
|
|
||
|
if (!writeBuffer.isEmpty() && !isWriteNotificationEnabled())
|
||
|
setWriteNotificationEnabled(true);
|
||
|
|
||
|
return written;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::waitForReadyRead(int msecs)
|
||
|
{
|
||
|
QElapsedTimer stopWatch;
|
||
|
|
||
|
stopWatch.start();
|
||
|
|
||
|
do {
|
||
|
bool readyToRead = false;
|
||
|
bool readyToWrite = false;
|
||
|
bool timedOut = false;
|
||
|
if (!waitForReadOrWrite(&readyToRead, &readyToWrite, true, !writeBuffer.isEmpty(),
|
||
|
timeoutValue(msecs, stopWatch.elapsed()), &timedOut)) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (readyToRead) {
|
||
|
if (readNotification())
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (readyToWrite)
|
||
|
writeNotification(WriteChunkSize);
|
||
|
|
||
|
} 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 readyToRead = false;
|
||
|
bool readyToWrite = false;
|
||
|
bool timedOut = false;
|
||
|
if (!waitForReadOrWrite(&readyToRead, &readyToWrite, true, !writeBuffer.isEmpty(),
|
||
|
timeoutValue(msecs, stopWatch.elapsed()), &timedOut)) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (readyToRead && !readNotification())
|
||
|
return false;
|
||
|
|
||
|
if (readyToWrite)
|
||
|
return writeNotification(WriteChunkSize);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setBaudRate(qint32 baudRate, QSerialPort::Directions dir)
|
||
|
{
|
||
|
bool ret = baudRate > 0;
|
||
|
|
||
|
// prepare section
|
||
|
|
||
|
if (ret) {
|
||
|
const qint32 unixBaudRate = QSerialPortPrivate::settingFromBaudRate(baudRate);
|
||
|
if (unixBaudRate > 0) {
|
||
|
// try prepate to set standard baud rate
|
||
|
#ifdef Q_OS_LINUX
|
||
|
// prepare to forcefully reset the custom mode
|
||
|
if (isCustomBaudRateSupported) {
|
||
|
//currentSerialInfo.flags |= ASYNC_SPD_MASK;
|
||
|
currentSerialInfo.flags &= ~(ASYNC_SPD_CUST /* | ASYNC_LOW_LATENCY*/);
|
||
|
currentSerialInfo.custom_divisor = 0;
|
||
|
}
|
||
|
#endif
|
||
|
// prepare to set standard baud rate
|
||
|
ret = !(((dir & QSerialPort::Input) && ::cfsetispeed(¤tTermios, unixBaudRate) < 0)
|
||
|
|| ((dir & QSerialPort::Output) && ::cfsetospeed(¤tTermios, unixBaudRate) < 0));
|
||
|
} else {
|
||
|
// try prepate to set custom baud rate
|
||
|
#ifdef Q_OS_LINUX
|
||
|
// prepare to forcefully set the custom mode
|
||
|
if (isCustomBaudRateSupported) {
|
||
|
currentSerialInfo.flags &= ~ASYNC_SPD_MASK;
|
||
|
currentSerialInfo.flags |= (ASYNC_SPD_CUST /* | ASYNC_LOW_LATENCY*/);
|
||
|
currentSerialInfo.custom_divisor = currentSerialInfo.baud_base / baudRate;
|
||
|
if (currentSerialInfo.custom_divisor == 0)
|
||
|
currentSerialInfo.custom_divisor = 1;
|
||
|
// for custom mode needed prepare to set B38400 baud rate
|
||
|
ret = (::cfsetspeed(¤tTermios, B38400) != -1);
|
||
|
} else {
|
||
|
ret = false;
|
||
|
}
|
||
|
#elif defined(Q_OS_MAC)
|
||
|
|
||
|
# if defined (MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
|
||
|
// Starting with Tiger, the IOSSIOSPEED ioctl can be used to set arbitrary baud rates
|
||
|
// other than those specified by POSIX. The driver for the underlying serial hardware
|
||
|
// ultimately determines which baud rates can be used. This ioctl sets both the input
|
||
|
// and output speed.
|
||
|
ret = ::ioctl(descriptor, IOSSIOSPEED, &baudRate) != -1;
|
||
|
# else
|
||
|
// others MacOSX version, can't prepare to set custom baud rate
|
||
|
ret = false;
|
||
|
# endif
|
||
|
|
||
|
#else
|
||
|
// others *nix OS, can't prepare to set custom baud rate
|
||
|
ret = false;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// finally section
|
||
|
|
||
|
#ifdef Q_OS_LINUX
|
||
|
if (ret && isCustomBaudRateSupported) // finally, set or reset the custom mode
|
||
|
ret = ::ioctl(descriptor, TIOCSSERIAL, ¤tSerialInfo) != -1;
|
||
|
#endif
|
||
|
|
||
|
if (ret) // finally, set baud rate
|
||
|
ret = updateTermios();
|
||
|
else
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setDataBits(QSerialPort::DataBits dataBits)
|
||
|
{
|
||
|
currentTermios.c_cflag &= ~CSIZE;
|
||
|
switch (dataBits) {
|
||
|
case QSerialPort::Data5:
|
||
|
currentTermios.c_cflag |= CS5;
|
||
|
break;
|
||
|
case QSerialPort::Data6:
|
||
|
currentTermios.c_cflag |= CS6;
|
||
|
break;
|
||
|
case QSerialPort::Data7:
|
||
|
currentTermios.c_cflag |= CS7;
|
||
|
break;
|
||
|
case QSerialPort::Data8:
|
||
|
currentTermios.c_cflag |= CS8;
|
||
|
break;
|
||
|
default:
|
||
|
currentTermios.c_cflag |= CS8;
|
||
|
break;
|
||
|
}
|
||
|
return updateTermios();
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setParity(QSerialPort::Parity parity)
|
||
|
{
|
||
|
currentTermios.c_iflag &= ~(PARMRK | INPCK);
|
||
|
currentTermios.c_iflag |= IGNPAR;
|
||
|
|
||
|
switch (parity) {
|
||
|
|
||
|
#ifdef CMSPAR
|
||
|
// Here Installation parity only for GNU/Linux where the macro CMSPAR.
|
||
|
case QSerialPort::SpaceParity:
|
||
|
currentTermios.c_cflag &= ~PARODD;
|
||
|
currentTermios.c_cflag |= PARENB | CMSPAR;
|
||
|
break;
|
||
|
case QSerialPort::MarkParity:
|
||
|
currentTermios.c_cflag |= PARENB | CMSPAR | PARODD;
|
||
|
break;
|
||
|
#endif
|
||
|
case QSerialPort::NoParity:
|
||
|
currentTermios.c_cflag &= ~PARENB;
|
||
|
break;
|
||
|
case QSerialPort::EvenParity:
|
||
|
currentTermios.c_cflag &= ~PARODD;
|
||
|
currentTermios.c_cflag |= PARENB;
|
||
|
break;
|
||
|
case QSerialPort::OddParity:
|
||
|
currentTermios.c_cflag |= PARENB | PARODD;
|
||
|
break;
|
||
|
default:
|
||
|
currentTermios.c_cflag |= PARENB;
|
||
|
currentTermios.c_iflag |= PARMRK | INPCK;
|
||
|
currentTermios.c_iflag &= ~IGNPAR;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return updateTermios();
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setStopBits(QSerialPort::StopBits stopBits)
|
||
|
{
|
||
|
switch (stopBits) {
|
||
|
case QSerialPort::OneStop:
|
||
|
currentTermios.c_cflag &= ~CSTOPB;
|
||
|
break;
|
||
|
case QSerialPort::TwoStop:
|
||
|
currentTermios.c_cflag |= CSTOPB;
|
||
|
break;
|
||
|
default:
|
||
|
currentTermios.c_cflag &= ~CSTOPB;
|
||
|
break;
|
||
|
}
|
||
|
return updateTermios();
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setFlowControl(QSerialPort::FlowControl flow)
|
||
|
{
|
||
|
switch (flow) {
|
||
|
case QSerialPort::NoFlowControl:
|
||
|
currentTermios.c_cflag &= ~CRTSCTS;
|
||
|
currentTermios.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||
|
break;
|
||
|
case QSerialPort::HardwareControl:
|
||
|
currentTermios.c_cflag |= CRTSCTS;
|
||
|
currentTermios.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||
|
break;
|
||
|
case QSerialPort::SoftwareControl:
|
||
|
currentTermios.c_cflag &= ~CRTSCTS;
|
||
|
currentTermios.c_iflag |= IXON | IXOFF | IXANY;
|
||
|
break;
|
||
|
default:
|
||
|
currentTermios.c_cflag &= ~CRTSCTS;
|
||
|
currentTermios.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||
|
break;
|
||
|
}
|
||
|
return updateTermios();
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::setDataErrorPolicy(QSerialPort::DataErrorPolicy policy)
|
||
|
{
|
||
|
tcflag_t parmrkMask = PARMRK;
|
||
|
#ifndef CMSPAR
|
||
|
// in space/mark parity emulation also used PARMRK flag
|
||
|
if (parity == QSerialPort::SpaceParity
|
||
|
|| parity == QSerialPort::MarkParity) {
|
||
|
parmrkMask = 0;
|
||
|
}
|
||
|
#endif //CMSPAR
|
||
|
switch (policy) {
|
||
|
case QSerialPort::SkipPolicy:
|
||
|
currentTermios.c_iflag &= ~parmrkMask;
|
||
|
currentTermios.c_iflag |= IGNPAR | INPCK;
|
||
|
break;
|
||
|
case QSerialPort::PassZeroPolicy:
|
||
|
currentTermios.c_iflag &= ~(IGNPAR | parmrkMask);
|
||
|
currentTermios.c_iflag |= INPCK;
|
||
|
break;
|
||
|
case QSerialPort::IgnorePolicy:
|
||
|
currentTermios.c_iflag &= ~INPCK;
|
||
|
break;
|
||
|
case QSerialPort::StopReceivingPolicy:
|
||
|
currentTermios.c_iflag &= ~IGNPAR;
|
||
|
currentTermios.c_iflag |= parmrkMask | INPCK;
|
||
|
break;
|
||
|
default:
|
||
|
currentTermios.c_iflag &= ~INPCK;
|
||
|
break;
|
||
|
}
|
||
|
return updateTermios();
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::readNotification()
|
||
|
{
|
||
|
// Prevent recursive calls
|
||
|
if (readPortNotifierCalled) {
|
||
|
if (!readPortNotifierStateSet) {
|
||
|
readPortNotifierStateSet = true;
|
||
|
readPortNotifierState = isReadNotificationEnabled();
|
||
|
setReadNotificationEnabled(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
readPortNotifierCalled = true;
|
||
|
|
||
|
// Always buffered, read data from the port into the read buffer
|
||
|
qint64 newBytes = readBuffer.size();
|
||
|
qint64 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);
|
||
|
const qint64 readBytes = readFromPort(ptr, bytesToRead);
|
||
|
|
||
|
if (readBytes <= 0) {
|
||
|
readBuffer.chop(bytesToRead);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
readBuffer.chop(bytesToRead - qMax(readBytes, qint64(0)));
|
||
|
|
||
|
newBytes = readBuffer.size() - newBytes;
|
||
|
|
||
|
// If read buffer is full, disable the read port notifier.
|
||
|
if (readBufferMaxSize && readBuffer.size() == readBufferMaxSize)
|
||
|
setReadNotificationEnabled(false);
|
||
|
|
||
|
// only emit readyRead() when not recursing, and only if there is data available
|
||
|
const bool hasData = newBytes > 0;
|
||
|
|
||
|
if (!emittedReadyRead && hasData) {
|
||
|
emittedReadyRead = true;
|
||
|
emit q_ptr->readyRead();
|
||
|
emittedReadyRead = false;
|
||
|
}
|
||
|
|
||
|
if (!hasData)
|
||
|
setReadNotificationEnabled(true);
|
||
|
|
||
|
// reset the read port notifier state if we reentered inside the
|
||
|
// readyRead() connected slot.
|
||
|
if (readPortNotifierStateSet
|
||
|
&& readPortNotifierState != isReadNotificationEnabled()) {
|
||
|
setReadNotificationEnabled(readPortNotifierState);
|
||
|
readPortNotifierStateSet = false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::writeNotification(int maxSize)
|
||
|
{
|
||
|
const int tmp = writeBuffer.size();
|
||
|
|
||
|
if (writeBuffer.isEmpty()) {
|
||
|
setWriteNotificationEnabled(false);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int nextSize = qMin(writeBuffer.nextDataBlockSize(), maxSize);
|
||
|
|
||
|
const char *ptr = writeBuffer.readPointer();
|
||
|
|
||
|
// Attempt to write it chunk.
|
||
|
qint64 written = writeToPort(ptr, nextSize);
|
||
|
if (written < 0)
|
||
|
return false;
|
||
|
|
||
|
// Remove what we wrote so far.
|
||
|
writeBuffer.free(written);
|
||
|
if (written > 0) {
|
||
|
// Don't emit bytesWritten() recursively.
|
||
|
if (!emittedBytesWritten) {
|
||
|
emittedBytesWritten = true;
|
||
|
emit q_ptr->bytesWritten(written);
|
||
|
emittedBytesWritten = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (writeBuffer.isEmpty())
|
||
|
setWriteNotificationEnabled(false);
|
||
|
|
||
|
return (writeBuffer.size() < tmp);
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::exceptionNotification()
|
||
|
{
|
||
|
QSerialPort::SerialPortError error = decodeSystemError();
|
||
|
q_ptr->setError(error);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::updateTermios()
|
||
|
{
|
||
|
if (::tcsetattr(descriptor, TCSANOW, ¤tTermios) == -1) {
|
||
|
q_ptr->setError(decodeSystemError());
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void QSerialPortPrivate::detectDefaultSettings()
|
||
|
{
|
||
|
// Detect baud rate.
|
||
|
const speed_t inputUnixBaudRate = ::cfgetispeed(¤tTermios);
|
||
|
const speed_t outputUnixBaudRate = ::cfgetospeed(¤tTermios);
|
||
|
bool isCustomBaudRateCurrentSet = false;
|
||
|
|
||
|
#ifdef Q_OS_LINUX
|
||
|
// try detect the ability to support custom baud rate
|
||
|
isCustomBaudRateSupported = ::ioctl(descriptor, TIOCGSERIAL, ¤tSerialInfo) != -1
|
||
|
&& ::ioctl(descriptor, TIOCSSERIAL, ¤tSerialInfo) != -1;
|
||
|
|
||
|
if (isCustomBaudRateSupported) {
|
||
|
restoredSerialInfo = currentSerialInfo;
|
||
|
|
||
|
// assume that the baud rate is a custom
|
||
|
isCustomBaudRateCurrentSet = inputUnixBaudRate == B38400 && outputUnixBaudRate == B38400;
|
||
|
|
||
|
if (isCustomBaudRateCurrentSet) {
|
||
|
if ((currentSerialInfo.flags & ASYNC_SPD_CUST)
|
||
|
&& currentSerialInfo.custom_divisor > 0) {
|
||
|
|
||
|
// yes, speed is really custom
|
||
|
inputBaudRate = currentSerialInfo.baud_base / currentSerialInfo.custom_divisor;
|
||
|
outputBaudRate = inputBaudRate;
|
||
|
} else {
|
||
|
// no, we were wrong and the speed is a standard 38400 baud
|
||
|
isCustomBaudRateCurrentSet = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
// other *nix
|
||
|
#endif
|
||
|
if (!isCustomBaudRateSupported || !isCustomBaudRateCurrentSet) {
|
||
|
inputBaudRate = QSerialPortPrivate::baudRateFromSetting(inputUnixBaudRate);
|
||
|
outputBaudRate = QSerialPortPrivate::baudRateFromSetting(outputUnixBaudRate);
|
||
|
}
|
||
|
|
||
|
// Detect databits.
|
||
|
switch (currentTermios.c_cflag & CSIZE) {
|
||
|
case CS5:
|
||
|
dataBits = QSerialPort::Data5;
|
||
|
break;
|
||
|
case CS6:
|
||
|
dataBits = QSerialPort::Data6;
|
||
|
break;
|
||
|
case CS7:
|
||
|
dataBits = QSerialPort::Data7;
|
||
|
break;
|
||
|
case CS8:
|
||
|
dataBits = QSerialPort::Data8;
|
||
|
break;
|
||
|
default:
|
||
|
dataBits = QSerialPort::UnknownDataBits;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Detect parity.
|
||
|
#ifdef CMSPAR
|
||
|
if (currentTermios.c_cflag & CMSPAR) {
|
||
|
parity = currentTermios.c_cflag & PARODD ?
|
||
|
QSerialPort::MarkParity : QSerialPort::SpaceParity;
|
||
|
} else {
|
||
|
#endif
|
||
|
if (currentTermios.c_cflag & PARENB) {
|
||
|
parity = currentTermios.c_cflag & PARODD ?
|
||
|
QSerialPort::OddParity : QSerialPort::EvenParity;
|
||
|
} else {
|
||
|
parity = QSerialPort::NoParity;
|
||
|
}
|
||
|
#ifdef CMSPAR
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Detect stopbits.
|
||
|
stopBits = currentTermios.c_cflag & CSTOPB ?
|
||
|
QSerialPort::TwoStop : QSerialPort::OneStop;
|
||
|
|
||
|
// Detect flow control.
|
||
|
if ((!(currentTermios.c_cflag & CRTSCTS)) && (!(currentTermios.c_iflag & (IXON | IXOFF | IXANY))))
|
||
|
flow = QSerialPort::NoFlowControl;
|
||
|
else if ((!(currentTermios.c_cflag & CRTSCTS)) && (currentTermios.c_iflag & (IXON | IXOFF | IXANY)))
|
||
|
flow = QSerialPort::SoftwareControl;
|
||
|
else if ((currentTermios.c_cflag & CRTSCTS) && (!(currentTermios.c_iflag & (IXON | IXOFF | IXANY))))
|
||
|
flow = QSerialPort::HardwareControl;
|
||
|
else
|
||
|
flow = QSerialPort::UnknownFlowControl;
|
||
|
}
|
||
|
|
||
|
QSerialPort::SerialPortError QSerialPortPrivate::decodeSystemError() const
|
||
|
{
|
||
|
QSerialPort::SerialPortError error;
|
||
|
switch (errno) {
|
||
|
case ENODEV:
|
||
|
error = QSerialPort::DeviceNotFoundError;
|
||
|
break;
|
||
|
case EACCES:
|
||
|
error = QSerialPort::PermissionError;
|
||
|
break;
|
||
|
case EBUSY:
|
||
|
error = QSerialPort::PermissionError;
|
||
|
break;
|
||
|
case EAGAIN:
|
||
|
error = QSerialPort::ResourceError;
|
||
|
break;
|
||
|
case EIO:
|
||
|
error = QSerialPort::ResourceError;
|
||
|
break;
|
||
|
case EBADF:
|
||
|
error = QSerialPort::ResourceError;
|
||
|
break;
|
||
|
#ifdef Q_OS_MAC
|
||
|
case ENXIO:
|
||
|
error = QSerialPort::ResourceError;
|
||
|
break;
|
||
|
#endif
|
||
|
default:
|
||
|
error = QSerialPort::UnknownError;
|
||
|
break;
|
||
|
}
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::isReadNotificationEnabled() const
|
||
|
{
|
||
|
return readNotifier && readNotifier->isEnabled();
|
||
|
}
|
||
|
|
||
|
void QSerialPortPrivate::setReadNotificationEnabled(bool enable)
|
||
|
{
|
||
|
if (readNotifier) {
|
||
|
readNotifier->setEnabled(enable);
|
||
|
} else if (enable) {
|
||
|
readNotifier = new ReadNotifier(this, q_ptr);
|
||
|
readNotifier->setEnabled(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::isWriteNotificationEnabled() const
|
||
|
{
|
||
|
return writeNotifier && writeNotifier->isEnabled();
|
||
|
}
|
||
|
|
||
|
void QSerialPortPrivate::setWriteNotificationEnabled(bool enable)
|
||
|
{
|
||
|
if (writeNotifier) {
|
||
|
writeNotifier->setEnabled(enable);
|
||
|
} else if (enable) {
|
||
|
writeNotifier = new WriteNotifier(this, q_ptr);
|
||
|
writeNotifier->setEnabled(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::isExceptionNotificationEnabled() const
|
||
|
{
|
||
|
return exceptionNotifier && exceptionNotifier->isEnabled();
|
||
|
}
|
||
|
|
||
|
void QSerialPortPrivate::setExceptionNotificationEnabled(bool enable)
|
||
|
{
|
||
|
if (exceptionNotifier) {
|
||
|
exceptionNotifier->setEnabled(enable);
|
||
|
} else if (enable) {
|
||
|
exceptionNotifier = new ExceptionNotifier(this, q_ptr);
|
||
|
exceptionNotifier->setEnabled(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool QSerialPortPrivate::waitForReadOrWrite(bool *selectForRead, bool *selectForWrite,
|
||
|
bool checkRead, bool checkWrite,
|
||
|
int msecs, bool *timedOut)
|
||
|
{
|
||
|
Q_ASSERT(selectForRead);
|
||
|
Q_ASSERT(selectForWrite);
|
||
|
Q_ASSERT(timedOut);
|
||
|
|
||
|
fd_set fdread;
|
||
|
FD_ZERO(&fdread);
|
||
|
if (checkRead)
|
||
|
FD_SET(descriptor, &fdread);
|
||
|
|
||
|
fd_set fdwrite;
|
||
|
FD_ZERO(&fdwrite);
|
||
|
if (checkWrite)
|
||
|
FD_SET(descriptor, &fdwrite);
|
||
|
|
||
|
struct timeval tv;
|
||
|
tv.tv_sec = msecs / 1000;
|
||
|
tv.tv_usec = (msecs % 1000) * 1000;
|
||
|
|
||
|
int ret = ::select(descriptor + 1, &fdread, &fdwrite, 0, msecs < 0 ? 0 : &tv);
|
||
|
if (ret < 0)
|
||
|
return false;
|
||
|
if (ret == 0) {
|
||
|
*timedOut = true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
*selectForRead = FD_ISSET(descriptor, &fdread);
|
||
|
*selectForWrite = FD_ISSET(descriptor, &fdwrite);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
qint64 QSerialPortPrivate::readFromPort(char *data, qint64 maxSize)
|
||
|
{
|
||
|
qint64 bytesRead = 0;
|
||
|
#if defined (CMSPAR)
|
||
|
if (parity == QSerialPort::NoParity
|
||
|
|| policy != QSerialPort::StopReceivingPolicy) {
|
||
|
#else
|
||
|
if (parity != QSerialPort::MarkParity
|
||
|
&& parity != QSerialPort::SpaceParity) {
|
||
|
#endif
|
||
|
bytesRead = ::read(descriptor, data, maxSize);
|
||
|
} else {// Perform parity emulation.
|
||
|
bytesRead = readPerChar(data, maxSize);
|
||
|
}
|
||
|
|
||
|
if (bytesRead <= 0) {
|
||
|
QSerialPort::SerialPortError error = decodeSystemError();
|
||
|
if (error != QSerialPort::ResourceError)
|
||
|
error = QSerialPort::ReadError;
|
||
|
q_ptr->setError(error);
|
||
|
}
|
||
|
|
||
|
return bytesRead;
|
||
|
}
|
||
|
|
||
|
qint64 QSerialPortPrivate::writeToPort(const char *data, qint64 maxSize)
|
||
|
{
|
||
|
qint64 bytesWritten = 0;
|
||
|
#if defined (CMSPAR)
|
||
|
bytesWritten = ::write(descriptor, data, maxSize);
|
||
|
#else
|
||
|
if (parity != QSerialPort::MarkParity
|
||
|
&& parity != QSerialPort::SpaceParity) {
|
||
|
bytesWritten = ::write(descriptor, data, maxSize);
|
||
|
} else {// Perform parity emulation.
|
||
|
bytesWritten = writePerChar(data, maxSize);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (bytesWritten < 0) {
|
||
|
QSerialPort::SerialPortError error = decodeSystemError();
|
||
|
if (error != QSerialPort::ResourceError)
|
||
|
error = QSerialPort::WriteError;
|
||
|
q_ptr->setError(error);
|
||
|
}
|
||
|
|
||
|
return bytesWritten;
|
||
|
}
|
||
|
|
||
|
static inline bool evenParity(quint8 c)
|
||
|
{
|
||
|
c ^= c >> 4; //(c7 ^ c3)(c6 ^ c2)(c5 ^ c1)(c4 ^ c0)
|
||
|
c ^= c >> 2; //[(c7 ^ c3)(c5 ^ c1)][(c6 ^ c2)(c4 ^ c0)]
|
||
|
c ^= c >> 1;
|
||
|
return c & 1; //(c7 ^ c3)(c5 ^ c1)(c6 ^ c2)(c4 ^ c0)
|
||
|
}
|
||
|
|
||
|
#ifndef CMSPAR
|
||
|
|
||
|
qint64 QSerialPortPrivate::writePerChar(const char *data, qint64 maxSize)
|
||
|
{
|
||
|
qint64 ret = 0;
|
||
|
quint8 const charMask = (0xFF >> (8 - dataBits));
|
||
|
|
||
|
while (ret < maxSize) {
|
||
|
|
||
|
bool par = evenParity(*data & charMask);
|
||
|
// False if need EVEN, true if need ODD.
|
||
|
par ^= parity == QSerialPort::MarkParity;
|
||
|
if (par ^ (currentTermios.c_cflag & PARODD)) { // Need switch parity mode?
|
||
|
currentTermios.c_cflag ^= PARODD;
|
||
|
flush(); //force sending already buffered data, because updateTermios() cleares buffers
|
||
|
//todo: add receiving buffered data!!!
|
||
|
if (!updateTermios())
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
int r = ::write(descriptor, data, 1);
|
||
|
if (r < 0)
|
||
|
return -1;
|
||
|
if (r > 0) {
|
||
|
data += r;
|
||
|
ret += r;
|
||
|
}
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#endif //CMSPAR
|
||
|
|
||
|
qint64 QSerialPortPrivate::readPerChar(char *data, qint64 maxSize)
|
||
|
{
|
||
|
qint64 ret = 0;
|
||
|
quint8 const charMask = (0xFF >> (8 - dataBits));
|
||
|
|
||
|
// 0 - prefix not started,
|
||
|
// 1 - received 0xFF,
|
||
|
// 2 - received 0xFF and 0x00
|
||
|
int prefix = 0;
|
||
|
while (ret < maxSize) {
|
||
|
|
||
|
qint64 r = ::read(descriptor, data, 1);
|
||
|
if (r < 0) {
|
||
|
if (errno == EAGAIN) // It is ok for nonblocking mode.
|
||
|
break;
|
||
|
return -1;
|
||
|
}
|
||
|
if (r == 0)
|
||
|
break;
|
||
|
|
||
|
bool par = true;
|
||
|
switch (prefix) {
|
||
|
case 2: // Previously received both 0377 and 0.
|
||
|
par = false;
|
||
|
prefix = 0;
|
||
|
break;
|
||
|
case 1: // Previously received 0377.
|
||
|
if (*data == '\0') {
|
||
|
++prefix;
|
||
|
continue;
|
||
|
}
|
||
|
prefix = 0;
|
||
|
break;
|
||
|
default:
|
||
|
if (*data == '\377') {
|
||
|
prefix = 1;
|
||
|
continue;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
// Now: par contains parity ok or error, *data contains received character
|
||
|
par ^= evenParity(*data & charMask); //par contains parity bit value for EVEN mode
|
||
|
par ^= (currentTermios.c_cflag & PARODD); //par contains parity bit value for current mode
|
||
|
if (par ^ (parity == QSerialPort::SpaceParity)) { //if parity error
|
||
|
switch (policy) {
|
||
|
case QSerialPort::SkipPolicy:
|
||
|
continue; //ignore received character
|
||
|
case QSerialPort::StopReceivingPolicy:
|
||
|
if (parity != QSerialPort::NoParity)
|
||
|
q_ptr->setError(QSerialPort::ParityError);
|
||
|
else
|
||
|
q_ptr->setError(*data == '\0' ?
|
||
|
QSerialPort::BreakConditionError : QSerialPort::FramingError);
|
||
|
return ++ret; //abort receiving
|
||
|
break;
|
||
|
case QSerialPort::UnknownPolicy:
|
||
|
// Unknown error policy is used! Falling back to PassZeroPolicy
|
||
|
case QSerialPort::PassZeroPolicy:
|
||
|
*data = '\0'; //replace received character by zero
|
||
|
break;
|
||
|
case QSerialPort::IgnorePolicy:
|
||
|
break; //ignore error and pass received character
|
||
|
}
|
||
|
}
|
||
|
++data;
|
||
|
++ret;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#ifdef Q_OS_MAC
|
||
|
static const QLatin1String defaultFilePathPrefix("/dev/cu.");
|
||
|
static const QLatin1String unusedFilePathPrefix("/dev/tty.");
|
||
|
#else
|
||
|
static const QLatin1String defaultFilePathPrefix("/dev/");
|
||
|
#endif
|
||
|
|
||
|
QString QSerialPortPrivate::portNameToSystemLocation(const QString &port)
|
||
|
{
|
||
|
QString ret = port;
|
||
|
|
||
|
#ifdef Q_OS_MAC
|
||
|
ret.remove(unusedFilePathPrefix);
|
||
|
#endif
|
||
|
|
||
|
if (!ret.contains(defaultFilePathPrefix))
|
||
|
ret.prepend(defaultFilePathPrefix);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
QString QSerialPortPrivate::portNameFromSystemLocation(const QString &location)
|
||
|
{
|
||
|
QString ret = location;
|
||
|
|
||
|
#ifdef Q_OS_MAC
|
||
|
ret.remove(unusedFilePathPrefix);
|
||
|
#endif
|
||
|
|
||
|
ret.remove(defaultFilePathPrefix);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
typedef QMap<qint32, qint32> BaudRateMap;
|
||
|
|
||
|
// The OS specific defines can be found in termios.h
|
||
|
|
||
|
static const BaudRateMap createStandardBaudRateMap()
|
||
|
{
|
||
|
BaudRateMap baudRateMap;
|
||
|
|
||
|
#ifdef B50
|
||
|
baudRateMap.insert(50, B50);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B75
|
||
|
baudRateMap.insert(75, B75);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B110
|
||
|
baudRateMap.insert(110, B110);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B134
|
||
|
baudRateMap.insert(134, B134);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B150
|
||
|
baudRateMap.insert(150, B150);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B200
|
||
|
baudRateMap.insert(200, B200);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B300
|
||
|
baudRateMap.insert(300, B300);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B600
|
||
|
baudRateMap.insert(600, B600);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B1200
|
||
|
baudRateMap.insert(1200, B1200);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B1800
|
||
|
baudRateMap.insert(1800, B1800);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B2400
|
||
|
baudRateMap.insert(2400, B2400);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B4800
|
||
|
baudRateMap.insert(4800, B4800);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B9600
|
||
|
baudRateMap.insert(9600, B9600);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B19200
|
||
|
baudRateMap.insert(19200, B19200);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B38400
|
||
|
baudRateMap.insert(38400, B38400);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B57600
|
||
|
baudRateMap.insert(57600, B57600);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B115200
|
||
|
baudRateMap.insert(115200, B115200);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B230400
|
||
|
baudRateMap.insert(230400, B230400);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B460800
|
||
|
baudRateMap.insert(460800, B460800);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B500000
|
||
|
baudRateMap.insert(500000, B500000);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B576000
|
||
|
baudRateMap.insert(576000, B576000);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B921600
|
||
|
baudRateMap.insert(921600, B921600);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B1000000
|
||
|
baudRateMap.insert(1000000, B1000000);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B1152000
|
||
|
baudRateMap.insert(1152000, B1152000);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B1500000
|
||
|
baudRateMap.insert(1500000, B1500000);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B2000000
|
||
|
baudRateMap.insert(2000000, B2000000);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B2500000
|
||
|
baudRateMap.insert(2500000, B2500000);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B3000000
|
||
|
baudRateMap.insert(3000000, B3000000);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B3500000
|
||
|
baudRateMap.insert(3500000, B3500000);
|
||
|
#endif
|
||
|
|
||
|
#ifdef B4000000
|
||
|
baudRateMap.insert(4000000, B4000000);
|
||
|
#endif
|
||
|
|
||
|
return baudRateMap;
|
||
|
}
|
||
|
|
||
|
static const BaudRateMap& standardBaudRateMap()
|
||
|
{
|
||
|
static const BaudRateMap baudRateMap = createStandardBaudRateMap();
|
||
|
return baudRateMap;
|
||
|
}
|
||
|
|
||
|
qint32 QSerialPortPrivate::baudRateFromSetting(qint32 setting)
|
||
|
{
|
||
|
return standardBaudRateMap().key(setting);
|
||
|
}
|
||
|
|
||
|
qint32 QSerialPortPrivate::settingFromBaudRate(qint32 baudRate)
|
||
|
{
|
||
|
return standardBaudRateMap().value(baudRate);
|
||
|
}
|
||
|
|
||
|
QList<qint32> QSerialPortPrivate::standardBaudRates()
|
||
|
{
|
||
|
return standardBaudRateMap().keys();
|
||
|
}
|
||
|
|
||
|
QT_END_NAMESPACE
|