Revision control

++ // 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>
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qserialport_p.h"
++ #include "qserialportinfo_p.h"
++
++ #include <QtCore/qdeadlinetimer.h>
++ #include <QtCore/qelapsedtimer.h>
++ #include <QtCore/qloggingcategory.h>
++ #include <QtCore/qmap.h>
++ #include <QtCore/qsocketnotifier.h>
++ #include <QtCore/qstandardpaths.h>
++
++ #include <private/qcore_unix_p.h>
++
++ #include <errno.h>
++ #include <fcntl.h>
++ #include <sys/ioctl.h>
++ #include <sys/time.h>
++ #include <unistd.h>
++
++ #ifdef Q_OS_MACOS
++ #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
++
++ #ifdef Q_OS_QNX
++ #define CRTSCTS (IHFLOW | OHFLOW)
++ #endif
++
++ #ifdef Q_OS_LINUX
++
++ # ifdef Q_OS_ANDROID
++ # include <android/api-level.h>
++ # else
++ # define __ANDROID_API__ 16
++ # endif
++
++ # if !defined(Q_OS_ANDROID) || (!defined(Q_PROCESSOR_X86) && __ANDROID_API__ < 16)
++ struct termios2 {
++ tcflag_t c_iflag; /* input mode flags */
++ tcflag_t c_oflag; /* output mode flags */
++ tcflag_t c_cflag; /* control mode flags */
++ tcflag_t c_lflag; /* local mode flags */
++ cc_t c_line; /* line discipline */
++ cc_t c_cc[19]; /* control characters */
++ speed_t c_ispeed; /* input speed */
++ speed_t c_ospeed; /* output speed */
++ };
++ # endif
++
++ #ifndef TCGETS2
++ #define TCGETS2 _IOR('T', 0x2A, struct termios2)
++ #endif
++
++ #ifndef TCSETS2
++ #define TCSETS2 _IOW('T', 0x2B, struct termios2)
++ #endif
++
++ #ifndef BOTHER
++ #define BOTHER 0010000
++ #endif
++
++ #endif
++
++ [[maybe_unused]]
++ Q_STATIC_LOGGING_CATEGORY(lcUnixWarnings, "qt.serialport.unix.warnings");
++
++ QT_BEGIN_NAMESPACE
++
++ QString serialPortLockFilePath(const QString &portName)
++ {
++ static const QStringList lockDirectoryPaths = QStringList()
++ << QStringLiteral("/var/lock")
++ << QStringLiteral("/etc/locks")
++ << QStringLiteral("/var/spool/locks")
++ << QStringLiteral("/var/spool/uucp")
++ << QStringLiteral("/tmp")
++ << QStringLiteral("/var/tmp")
++ << QStringLiteral("/var/lock/lockdev")
++ << QStringLiteral("/run/lock")
++ #ifdef Q_OS_ANDROID
++ << QStringLiteral("/data/local/tmp")
++ #endif
++ << QStandardPaths::writableLocation(QStandardPaths::TempLocation);
++
++ QString fileName = portName;
++ fileName.replace(QLatin1Char('/'), QLatin1Char('_'));
++ fileName.prepend(QLatin1String("/LCK.."));
++
++ QString lockFilePath;
++
++ for (const QString &lockDirectoryPath : lockDirectoryPaths) {
++ const QString filePath = lockDirectoryPath + fileName;
++
++ QFileInfo lockDirectoryInfo(lockDirectoryPath);
++ if (lockDirectoryInfo.isReadable()) {
++ if (QFile::exists(filePath) || lockDirectoryInfo.isWritable()) {
++ lockFilePath = filePath;
++ break;
++ }
++ }
++ }
++
++ if (lockFilePath.isEmpty()) {
++ qWarning("The following directories are not readable or writable for detaling with lock files\n");
++ for (const QString &lockDirectoryPath : lockDirectoryPaths)
++ qWarning("\t%s\n", qPrintable(lockDirectoryPath));
++ return QString();
++ }
++
++ return lockFilePath;
++ }
++
++ class ReadNotifier : public QSocketNotifier
++ {
++ public:
++ explicit ReadNotifier(QSerialPortPrivate *d, QObject *parent)
++ : QSocketNotifier(d->descriptor, QSocketNotifier::Read, parent)
++ , dptr(d)
++ {
++ }
++
++ protected:
++ bool event(QEvent *e) override
++ {
++ if (e->type() == QEvent::SockAct) {
++ dptr->readNotification();
++ return true;
++ }
++ return QSocketNotifier::event(e);
++ }
++
++ private:
++ QSerialPortPrivate * const dptr;
++ };
++
++ class WriteNotifier : public QSocketNotifier
++ {
++ public:
++ explicit WriteNotifier(QSerialPortPrivate *d, QObject *parent)
++ : QSocketNotifier(d->descriptor, QSocketNotifier::Write, parent)
++ , dptr(d)
++ {
++ }
++
++ protected:
++ bool event(QEvent *e) override
++ {
++ if (e->type() == QEvent::SockAct) {
++ dptr->completeAsyncWrite();
++ return true;
++ }
++ return QSocketNotifier::event(e);
++ }
++
++ private:
++ QSerialPortPrivate * const dptr;
++ };
++
++ class ExceptionNotifier : public QSocketNotifier
++ {
++ public:
++ explicit ExceptionNotifier(QSerialPortPrivate *d, QObject *parent)
++ : QSocketNotifier(d->descriptor, QSocketNotifier::Exception, parent),
++ dptr(d)
++ {
++ }
++
++ protected:
++ bool event(QEvent *e) override
++ {
++ if (e->type() == QEvent::SockAct) {
++ dptr->handleException();
++ return true;
++ }
++ return QSocketNotifier::event(e);
++ }
++
++ private:
++ QSerialPortPrivate * const dptr;
++ };
++
++ static inline void qt_set_common_props(termios *tio, QIODevice::OpenMode m)
++ {
++ #ifdef Q_OS_SOLARIS
++ tio->c_iflag &= ~(IMAXBEL|IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
++ tio->c_oflag &= ~OPOST;
++ tio->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
++ tio->c_cflag &= ~(CSIZE|PARENB);
++ tio->c_cflag |= CS8;
++ #else
++ ::cfmakeraw(tio);
++ #endif
++
++ tio->c_cflag |= CLOCAL;
++ tio->c_cc[VTIME] = 0;
++ tio->c_cc[VMIN] = 0;
++
++ if (m & QIODevice::ReadOnly)
++ tio->c_cflag |= CREAD;
++ }
++
++ static inline void qt_set_databits(termios *tio, QSerialPort::DataBits databits)
++ {
++ tio->c_cflag &= ~CSIZE;
++ switch (databits) {
++ case QSerialPort::Data5:
++ tio->c_cflag |= CS5;
++ break;
++ case QSerialPort::Data6:
++ tio->c_cflag |= CS6;
++ break;
++ case QSerialPort::Data7:
++ tio->c_cflag |= CS7;
++ break;
++ case QSerialPort::Data8:
++ tio->c_cflag |= CS8;
++ break;
++ default:
++ tio->c_cflag |= CS8;
++ break;
++ }
++ }
++
++ static inline void qt_set_parity(termios *tio, QSerialPort::Parity parity)
++ {
++ tio->c_iflag &= ~(PARMRK | INPCK);
++ tio->c_iflag |= IGNPAR;
++
++ switch (parity) {
++
++ #ifdef CMSPAR
++ // Here Installation parity only for GNU/Linux where the macro CMSPAR.
++ case QSerialPort::SpaceParity:
++ tio->c_cflag &= ~PARODD;
++ tio->c_cflag |= PARENB | CMSPAR;
++ break;
++ case QSerialPort::MarkParity:
++ tio->c_cflag |= PARENB | CMSPAR | PARODD;
++ break;
++ #endif
++ case QSerialPort::NoParity:
++ tio->c_cflag &= ~PARENB;
++ break;
++ case QSerialPort::EvenParity:
++ tio->c_cflag &= ~PARODD;
++ tio->c_cflag |= PARENB;
++ break;
++ case QSerialPort::OddParity:
++ tio->c_cflag |= PARENB | PARODD;
++ break;
++ default:
++ #ifndef CMSPAR
++ case QSerialPort::SpaceParity:
++ case QSerialPort::MarkParity:
++ qCWarning(lcUnixWarnings, "Space and Mark parity is not supported by the OS.");
++ #endif
++ tio->c_cflag |= PARENB;
++ tio->c_iflag |= PARMRK | INPCK;
++ tio->c_iflag &= ~IGNPAR;
++ break;
++ }
++ }
++
++ static inline void qt_set_stopbits(termios *tio, QSerialPort::StopBits stopbits)
++ {
++ switch (stopbits) {
++ case QSerialPort::OneStop:
++ tio->c_cflag &= ~CSTOPB;
++ break;
++ case QSerialPort::TwoStop:
++ tio->c_cflag |= CSTOPB;
++ break;
++ default:
++ tio->c_cflag &= ~CSTOPB;
++ break;
++ }
++ }
++
++ static inline void qt_set_flowcontrol(termios *tio, QSerialPort::FlowControl flowcontrol)
++ {
++ switch (flowcontrol) {
++ case QSerialPort::NoFlowControl:
++ tio->c_cflag &= ~CRTSCTS;
++ tio->c_iflag &= ~(IXON | IXOFF | IXANY);
++ break;
++ case QSerialPort::HardwareControl:
++ tio->c_cflag |= CRTSCTS;
++ tio->c_iflag &= ~(IXON | IXOFF | IXANY);
++ break;
++ case QSerialPort::SoftwareControl:
++ tio->c_cflag &= ~CRTSCTS;
++ tio->c_iflag |= IXON | IXOFF | IXANY;
++ break;
++ default:
++ tio->c_cflag &= ~CRTSCTS;
++ tio->c_iflag &= ~(IXON | IXOFF | IXANY);
++ break;
++ }
++ }
++
++ bool QSerialPortPrivate::open(QIODevice::OpenMode mode)
++ {
++ QString lockFilePath = serialPortLockFilePath(QSerialPortInfoPrivate::portNameFromSystemLocation(systemLocation));
++ bool isLockFileEmpty = lockFilePath.isEmpty();
++ if (isLockFileEmpty) {
++ qWarning("Failed to create a lock file for opening the device");
++ setError(QSerialPortErrorInfo(QSerialPort::PermissionError, QSerialPort::tr("Permission error while creating lock file")));
++ return false;
++ }
++
++ auto newLockFileScopedPointer = std::make_unique<QLockFile>(lockFilePath);
++
++ if (!newLockFileScopedPointer->tryLock()) {
++ setError(QSerialPortErrorInfo(QSerialPort::PermissionError, QSerialPort::tr("Permission error while locking the device")));
++ 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 = qt_safe_open(systemLocation.toLocal8Bit().constData(), flags);
++
++ if (descriptor == -1) {
++ setError(getSystemError());
++ return false;
++ }
++
++ if (!initialize(mode)) {
++ qt_safe_close(descriptor);
++ return false;
++ }
++
++ lockFileScopedPointer = std::move(newLockFileScopedPointer);
++
++ return true;
++ }
++
++ void QSerialPortPrivate::close()
++ {
++ if (settingsRestoredOnClose)
++ ::tcsetattr(descriptor, TCSANOW, &restoredTermios);
++
++ #ifdef TIOCNXCL
++ ::ioctl(descriptor, TIOCNXCL);
++ #endif
++
++ delete readNotifier;
++ readNotifier = nullptr;
++
++ delete writeNotifier;
++ writeNotifier = nullptr;
++
++ delete exceptionNotifier;
++ exceptionNotifier = nullptr;
++
++ qt_safe_close(descriptor);
++
++ lockFileScopedPointer.reset(nullptr);
++
++ descriptor = -1;
++ pendingBytesWritten = 0;
++ writeSequenceStarted = false;
++ gotException = false;
++ }
++
++ QSerialPort::PinoutSignals QSerialPortPrivate::pinoutSignals()
++ {
++ int arg = 0;
++
++ if (::ioctl(descriptor, TIOCMGET, &arg) == -1) {
++ setError(getSystemError());
++ return QSerialPort::NoSignal;
++ }
++
++ QSerialPort::PinoutSignals ret = QSerialPort::NoSignal;
++
++ #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;
++ if (::ioctl(descriptor, set ? TIOCMBIS : TIOCMBIC, &status) == -1) {
++ setError(getSystemError());
++ return false;
++ }
++
++ return true;
++ }
++
++ bool QSerialPortPrivate::setRequestToSend(bool set)
++ {
++ int status = TIOCM_RTS;
++ if (::ioctl(descriptor, set ? TIOCMBIS : TIOCMBIC, &status) == -1) {
++ setError(getSystemError());
++ return false;
++ }
++
++ return true;
++ }
++
++ bool QSerialPortPrivate::flush()
++ {
++ return completeAsyncWrite();
++ }
++
++ bool QSerialPortPrivate::clear(QSerialPort::Directions directions)
++ {
++ if (::tcflush(descriptor, (directions == QSerialPort::AllDirections)
++ ? TCIOFLUSH : (directions & QSerialPort::Input) ? TCIFLUSH : TCOFLUSH) == -1) {
++ setError(getSystemError());
++ return false;
++ }
++
++ return true;
++ }
++
++ bool QSerialPortPrivate::sendBreak(int duration)
++ {
++ if (::tcsendbreak(descriptor, duration) == -1) {
++ setError(getSystemError());
++ return false;
++ }
++
++ return true;
++ }
++
++ bool QSerialPortPrivate::setBreakEnabled(bool set)
++ {
++ if (::ioctl(descriptor, set ? TIOCSBRK : TIOCCBRK) == -1) {
++ setError(getSystemError());
++ return false;
++ }
++
++ return true;
++ }
++
++ bool QSerialPortPrivate::waitForReadyRead(int msecs)
++ {
++ QElapsedTimer stopWatch;
++ stopWatch.start();
++
++ do {
++ bool readyToRead = false;
++ bool readyToWrite = false;
++ if (!waitForReadOrWrite(&readyToRead, &readyToWrite, true, !writeBuffer.isEmpty(),
++ qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
++ return false;
++ }
++
++ if (readyToRead)
++ return readNotification();
++
++ if (readyToWrite && !completeAsyncWrite())
++ return false;
++ } while (msecs == -1 || qt_subtract_from_timeout(msecs, stopWatch.elapsed()) > 0);
++ return false;
++ }
++
++ bool QSerialPortPrivate::waitForBytesWritten(int msecs)
++ {
++ if (writeBuffer.isEmpty() && pendingBytesWritten <= 0)
++ return false;
++
++ QElapsedTimer stopWatch;
++ stopWatch.start();
++
++ for (;;) {
++ bool readyToRead = false;
++ bool readyToWrite = false;
++ const bool checkRead = q_func()->isReadable();
++ if (!waitForReadOrWrite(&readyToRead, &readyToWrite, checkRead, !writeBuffer.isEmpty(),
++ qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
++ return false;
++ }
++
++ if (readyToRead && !readNotification())
++ return false;
++
++ if (readyToWrite)
++ return completeAsyncWrite();
++ }
++ return false;
++ }
++
++ bool QSerialPortPrivate::setBaudRate()
++ {
++ if (inputBaudRate == outputBaudRate)
++ return setBaudRate(inputBaudRate, QSerialPort::AllDirections);
++
++ return (setBaudRate(inputBaudRate, QSerialPort::Input)
++ && setBaudRate(outputBaudRate, QSerialPort::Output));
++ }
++
++ bool QSerialPortPrivate::setStandardBaudRate(qint32 baudRate, QSerialPort::Directions directions)
++ {
++ #ifdef Q_OS_LINUX
++ // try to clear custom baud rate, using termios v2
++ struct termios2 tio2;
++ if (::ioctl(descriptor, TCGETS2, &tio2) != -1) {
++ if (tio2.c_cflag & BOTHER) {
++ tio2.c_cflag &= ~BOTHER;
++ tio2.c_cflag |= CBAUD;
++ ::ioctl(descriptor, TCSETS2, &tio2);
++ }
++ }
++
++ // try to clear custom baud rate, using serial_struct (old way)
++ struct serial_struct serial;
++ ::memset(&serial, 0, sizeof(serial));
++ if (::ioctl(descriptor, TIOCGSERIAL, &serial) != -1) {
++ if (serial.flags & ASYNC_SPD_CUST) {
++ serial.flags &= ~ASYNC_SPD_CUST;
++ serial.custom_divisor = 0;
++ // we don't check on errors because a driver can has not this feature
++ ::ioctl(descriptor, TIOCSSERIAL, &serial);
++ }
++ }
++ #endif
++
++ termios tio;
++ if (!getTermios(&tio))
++ return false;
++
++ if ((directions & QSerialPort::Input) && ::cfsetispeed(&tio, baudRate) < 0) {
++ setError(getSystemError());
++ return false;
++ }
++
++ if ((directions & QSerialPort::Output) && ::cfsetospeed(&tio, baudRate) < 0) {
++ setError(getSystemError());
++ return false;
++ }
++
++ return setTermios(&tio);
++ }
++
++ #if defined(Q_OS_LINUX)
++
++ bool QSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions)
++ {
++ if (directions != QSerialPort::AllDirections) {
++ setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError,
++ QSerialPort::tr("Cannot set custom speed for one direction")));
++ return false;
++ }
++
++ struct termios2 tio2;
++
++ if (::ioctl(descriptor, TCGETS2, &tio2) != -1) {
++ tio2.c_cflag &= ~CBAUD;
++ tio2.c_cflag |= BOTHER;
++
++ tio2.c_ispeed = baudRate;
++ tio2.c_ospeed = baudRate;
++
++ if (::ioctl(descriptor, TCSETS2, &tio2) != -1
++ && ::ioctl(descriptor, TCGETS2, &tio2) != -1) {
++ return true;
++ }
++ }
++
++ struct serial_struct serial;
++
++ if (::ioctl(descriptor, TIOCGSERIAL, &serial) == -1) {
++ setError(getSystemError());
++ return false;
++ }
++
++ serial.flags &= ~ASYNC_SPD_MASK;
++ serial.flags |= (ASYNC_SPD_CUST /* | ASYNC_LOW_LATENCY*/);
++ serial.custom_divisor = serial.baud_base / baudRate;
++
++ if (serial.custom_divisor == 0) {
++ setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError,
++ QSerialPort::tr("No suitable custom baud rate divisor")));
++ return false;
++ }
++
++ if (serial.custom_divisor * baudRate != serial.baud_base) {
++ qWarning("Baud rate of serial port %s is set to %f instead of %d: divisor %f unsupported",
++ qPrintable(systemLocation),
++ float(serial.baud_base) / serial.custom_divisor,
++ baudRate, float(serial.baud_base) / baudRate);
++ }
++
++ if (::ioctl(descriptor, TIOCSSERIAL, &serial) == -1) {
++ setError(getSystemError());
++ return false;
++ }
++
++ return setStandardBaudRate(B38400, directions);
++ }
++
++ #elif defined(Q_OS_MACOS)
++
++ bool QSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions)
++ {
++ if (directions != QSerialPort::AllDirections) {
++ setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError,
++ QSerialPort::tr("Cannot set custom speed for one direction")));
++ return false;
++ }
++
++ #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
++ if (::ioctl(descriptor, IOSSIOSPEED, &baudRate) == -1) {
++ setError(getSystemError());
++ return false;
++ }
++
++ return true;
++ #else
++ setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError,
++ QSerialPort::tr("Custom baud rate is not supported")));
++ return false;
++ #endif
++ }
++
++ #elif defined(Q_OS_QNX)
++
++ bool QSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions)
++ {
++ // On QNX, the values of the 'Bxxxx' constants are set to 'xxxx' (i.e.
++ // B115200 is defined to '115200'), which means that literal values can be
++ // passed to cfsetispeed/cfsetospeed, including custom values, provided
++ // that the underlying hardware supports them.
++ return setStandardBaudRate(baudRate, directions);
++ }
++
++ #else
++
++ bool QSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions)
++ {
++ Q_UNUSED(baudRate);
++ Q_UNUSED(directions);
++
++ setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError,
++ QSerialPort::tr("Custom baud rate is not supported")));
++ return false;
++ }
++
++ #endif
++
++ bool QSerialPortPrivate::setBaudRate(qint32 baudRate, QSerialPort::Directions directions)
++ {
++ if (baudRate <= 0) {
++ setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, QSerialPort::tr("Invalid baud rate value")));
++ return false;
++ }
++
++ const qint32 unixBaudRate = QSerialPortPrivate::settingFromBaudRate(baudRate);
++
++ return (unixBaudRate > 0)
++ ? setStandardBaudRate(unixBaudRate, directions)
++ : setCustomBaudRate(baudRate, directions);
++ }
++
++ bool QSerialPortPrivate::setDataBits(QSerialPort::DataBits dataBits)
++ {
++ termios tio;
++ if (!getTermios(&tio))
++ return false;
++
++ qt_set_databits(&tio, dataBits);
++
++ return setTermios(&tio);
++ }
++
++ bool QSerialPortPrivate::setParity(QSerialPort::Parity parity)
++ {
++ termios tio;
++ if (!getTermios(&tio))
++ return false;
++
++ qt_set_parity(&tio, parity);
++
++ return setTermios(&tio);
++ }
++
++ bool QSerialPortPrivate::setStopBits(QSerialPort::StopBits stopBits)
++ {
++ termios tio;
++ if (!getTermios(&tio))
++ return false;
++
++ qt_set_stopbits(&tio, stopBits);
++
++ return setTermios(&tio);
++ }
++
++ bool QSerialPortPrivate::setFlowControl(QSerialPort::FlowControl flowControl)
++ {
++ termios tio;
++ if (!getTermios(&tio))
++ return false;
++
++ qt_set_flowcontrol(&tio, flowControl);
++
++ return setTermios(&tio);
++ }
++
++ bool QSerialPortPrivate::startAsyncRead()
++ {
++ setReadNotificationEnabled(true);
++ return true;
++ }
++
++ bool QSerialPortPrivate::readNotification()
++ {
++ Q_Q(QSerialPort);
++
++ // Always buffered, read data from the port into the read buffer
++ qint64 newBytes = buffer.size();
++ qint64 bytesToRead = QSERIALPORT_BUFFERSIZE;
++
++ if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - buffer.size())) {
++ bytesToRead = readBufferMaxSize - buffer.size();
++ if (bytesToRead <= 0) {
++ // Buffer is full. User must read data from the buffer
++ // before we can read more from the port.
++ setReadNotificationEnabled(false);
++ return false;
++ }
++ }
++
++ char *ptr = buffer.reserve(bytesToRead);
++ const qint64 readBytes = readFromPort(ptr, bytesToRead);
++
++ buffer.chop(bytesToRead - qMax(readBytes, qint64(0)));
++
++ if (readBytes < 0) {
++ QSerialPortErrorInfo error = getSystemError();
++ if (error.errorCode != QSerialPort::ResourceError)
++ error.errorCode = QSerialPort::ReadError;
++ else
++ setReadNotificationEnabled(false);
++ setError(error);
++ return false;
++ } else if (readBytes == 0) {
++ // We can get here at least in two cases:
++ // * there is no data in the port
++ // * the device was disconnected (unplugged)
++ // The first case is perfectly valid, and we should simply keep
++ // reading. The second case should be reported as a ResourceError.
++ // We use exception notifier to detect this case.
++ if (gotException) {
++ setReadNotificationEnabled(false);
++ // Force a specific error
++ QSerialPortErrorInfo error = getSystemError(EIO);
++ setError(error);
++ }
++ return false;
++ }
++
++ newBytes = buffer.size() - newBytes;
++
++ // 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->readyRead();
++ emittedReadyRead = false;
++ }
++
++ return true;
++ }
++
++ bool QSerialPortPrivate::startAsyncWrite()
++ {
++ if (writeBuffer.isEmpty() || writeSequenceStarted)
++ return true;
++
++ // Attempt to write it all in one chunk.
++ qint64 written = writeToPort(writeBuffer.readPointer(), writeBuffer.nextDataBlockSize());
++ if (written < 0) {
++ QSerialPortErrorInfo error = getSystemError();
++ if (error.errorCode != QSerialPort::ResourceError)
++ error.errorCode = QSerialPort::WriteError;
++ setError(error);
++ return false;
++ }
++
++ writeBuffer.free(written);
++ pendingBytesWritten += written;
++ writeSequenceStarted = true;
++
++ if (!isWriteNotificationEnabled())
++ setWriteNotificationEnabled(true);
++ return true;
++ }
++
++ bool QSerialPortPrivate::completeAsyncWrite()
++ {
++ Q_Q(QSerialPort);
++
++ if (pendingBytesWritten > 0) {
++ if (!emittedBytesWritten) {
++ emittedBytesWritten = true;
++ emit q->bytesWritten(pendingBytesWritten);
++ pendingBytesWritten = 0;
++ emittedBytesWritten = false;
++ }
++ }
++
++ writeSequenceStarted = false;
++
++ if (writeBuffer.isEmpty()) {
++ setWriteNotificationEnabled(false);
++ return true;
++ }
++
++ return startAsyncWrite();
++ }
++
++ void QSerialPortPrivate::handleException()
++ {
++ gotException = true;
++ }
++
++ inline bool QSerialPortPrivate::initialize(QIODevice::OpenMode mode)
++ {
++ #ifdef TIOCEXCL
++ if (::ioctl(descriptor, TIOCEXCL) == -1)
++ setError(getSystemError());
++ #endif
++
++ termios tio;
++ if (!getTermios(&tio))
++ return false;
++
++ restoredTermios = tio;
++
++ auto restoreParametersOnError = qScopeGuard([this] {
++ if (settingsRestoredOnClose)
++ ::tcsetattr(descriptor, TCSANOW, &restoredTermios);
++ });
++
++ qt_set_common_props(&tio, mode);
++ qt_set_databits(&tio, dataBits);
++ qt_set_parity(&tio, parity);
++ qt_set_stopbits(&tio, stopBits);
++ qt_set_flowcontrol(&tio, flowControl);
++
++ if (!setTermios(&tio))
++ return false;
++
++ if (!setBaudRate())
++ return false;
++
++ if (mode & QIODevice::ReadOnly)
++ setReadNotificationEnabled(true);
++
++ // flush IO buffers
++ clear(QSerialPort::AllDirections);
++
++ exceptionNotifier = new ExceptionNotifier(this, q_func());
++ gotException = false;
++
++ restoreParametersOnError.dismiss();
++ return true;
++ }
++
++ qint64 QSerialPortPrivate::writeData(const char *data, qint64 maxSize)
++ {
++ qint64 toAppend = maxSize;
++
++ if (writeBufferMaxSize && (writeBuffer.size() + toAppend > writeBufferMaxSize))
++ toAppend = writeBufferMaxSize - writeBuffer.size();
++
++ writeBuffer.append(data, toAppend);
++ if (!writeBuffer.isEmpty() && !isWriteNotificationEnabled())
++ setWriteNotificationEnabled(true);
++ return toAppend;
++ }
++
++ bool QSerialPortPrivate::setTermios(const termios *tio)
++ {
++ if (::tcsetattr(descriptor, TCSANOW, tio) == -1) {
++ setError(getSystemError());
++ return false;
++ }
++ return true;
++ }
++
++ bool QSerialPortPrivate::getTermios(termios *tio)
++ {
++ ::memset(tio, 0, sizeof(termios));
++ if (::tcgetattr(descriptor, tio) == -1) {
++ setError(getSystemError());
++ return false;
++ }
++ return true;
++ }
++
++ QSerialPortErrorInfo QSerialPortPrivate::getSystemError(int systemErrorCode) const
++ {
++ if (systemErrorCode == -1)
++ systemErrorCode = errno;
++
++ QSerialPortErrorInfo error;
++ error.errorString = qt_error_string(systemErrorCode);
++
++ switch (systemErrorCode) {
++ case ENODEV:
++ error.errorCode = QSerialPort::DeviceNotFoundError;
++ break;
++ #ifdef ENOENT
++ case ENOENT:
++ error.errorCode = QSerialPort::DeviceNotFoundError;
++ break;
++ #endif
++ case EACCES:
++ error.errorCode = QSerialPort::PermissionError;
++ break;
++ case EBUSY:
++ error.errorCode = QSerialPort::PermissionError;
++ break;
++ case EAGAIN:
++ error.errorCode = QSerialPort::ResourceError;
++ break;
++ case EIO:
++ error.errorCode = QSerialPort::ResourceError;
++ break;
++ case EBADF:
++ error.errorCode = QSerialPort::ResourceError;
++ break;
++ #ifdef Q_OS_MACOS
++ case ENXIO:
++ error.errorCode = QSerialPort::ResourceError;
++ break;
++ #endif
++ #ifdef EINVAL
++ case EINVAL:
++ error.errorCode = QSerialPort::UnsupportedOperationError;
++ break;
++ #endif
++ #ifdef ENOIOCTLCMD
++ case ENOIOCTLCMD:
++ error.errorCode = QSerialPort::UnsupportedOperationError;
++ break;
++ #endif
++ #ifdef ENOTTY
++ case ENOTTY:
++ error.errorCode = QSerialPort::UnsupportedOperationError;
++ break;
++ #endif
++ #ifdef EPERM
++ case EPERM:
++ error.errorCode = QSerialPort::PermissionError;
++ break;
++ #endif
++ default:
++ error.errorCode = QSerialPort::UnknownError;
++ break;
++ }
++ return error;
++ }
++
++ bool QSerialPortPrivate::isReadNotificationEnabled() const
++ {
++ return readNotifier && readNotifier->isEnabled();
++ }
++
++ void QSerialPortPrivate::setReadNotificationEnabled(bool enable)
++ {
++ Q_Q(QSerialPort);
++
++ if (readNotifier) {
++ readNotifier->setEnabled(enable);
++ } else if (enable) {
++ readNotifier = new ReadNotifier(this, q);
++ readNotifier->setEnabled(true);
++ }
++ }
++
++ bool QSerialPortPrivate::isWriteNotificationEnabled() const
++ {
++ return writeNotifier && writeNotifier->isEnabled();
++ }
++
++ void QSerialPortPrivate::setWriteNotificationEnabled(bool enable)
++ {
++ Q_Q(QSerialPort);
++
++ if (writeNotifier) {
++ writeNotifier->setEnabled(enable);
++ } else if (enable) {
++ writeNotifier = new WriteNotifier(this, q);
++ writeNotifier->setEnabled(true);
++ }
++ }
++
++ bool QSerialPortPrivate::waitForReadOrWrite(bool *selectForRead, bool *selectForWrite,
++ bool checkRead, bool checkWrite,
++ int msecs)
++ {
++ Q_ASSERT(selectForRead);
++ Q_ASSERT(selectForWrite);
++
++ pollfd pfd = qt_make_pollfd(descriptor, 0);
++
++ if (checkRead)
++ pfd.events |= POLLIN;
++
++ if (checkWrite)
++ pfd.events |= POLLOUT;
++
++ const int ret = qt_safe_poll(&pfd, 1, QDeadlineTimer(msecs));
++ if (ret < 0) {
++ setError(getSystemError());
++ return false;
++ }
++ if (ret == 0) {
++ setError(QSerialPortErrorInfo(QSerialPort::TimeoutError));
++ return false;
++ }
++ if (pfd.revents & POLLNVAL) {
++ setError(getSystemError(EBADF));
++ return false;
++ }
++
++ *selectForWrite = ((pfd.revents & POLLOUT) != 0);
++ *selectForRead = ((pfd.revents & POLLIN) != 0);
++ return true;
++ }
++
++ qint64 QSerialPortPrivate::readFromPort(char *data, qint64 maxSize)
++ {
++ return qt_safe_read(descriptor, data, maxSize);
++ }
++
++ qint64 QSerialPortPrivate::writeToPort(const char *data, qint64 maxSize)
++ {
++ qint64 bytesWritten = 0;
++ #if defined(CMSPAR)
++ bytesWritten = qt_safe_write(descriptor, data, maxSize);
++ #else
++ if (parity != QSerialPort::MarkParity
++ && parity != QSerialPort::SpaceParity) {
++ bytesWritten = qt_safe_write(descriptor, data, maxSize);
++ } else {// Perform parity emulation.
++ bytesWritten = writePerChar(data, maxSize);
++ }
++ #endif
++
++ return bytesWritten;
++ }
++
++ #ifndef CMSPAR
++
++ 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)
++ }
++
++ qint64 QSerialPortPrivate::writePerChar(const char *data, qint64 maxSize)
++ {
++ termios tio;
++ if (!getTermios(&tio))
++ return -1;
++
++ 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 ^ (tio.c_cflag & PARODD)) { // Need switch parity mode?
++ tio.c_cflag ^= PARODD;
++ //force sending already buffered data, because setTermios(&tio); cleares buffers
++ if (::tcdrain(descriptor) == -1) {
++ setError(getSystemError());
++ break;
++ }
++ //todo: add receiving buffered data!!!
++ if (!setTermios(&tio))
++ break;
++ }
++
++ int r = qt_safe_write(descriptor, data, 1);
++ if (r < 0)
++ return -1;
++ if (r > 0) {
++ data += r;
++ ret += r;
++ }
++ }
++ return ret;
++ }
++
++ #endif //CMSPAR
++
++ 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 B7200
++ baudRateMap.insert(7200, B7200);
++ #endif
++
++ #ifdef B9600
++ baudRateMap.insert(9600, B9600);
++ #endif
++
++ #ifdef B14400
++ baudRateMap.insert(14400, B14400);
++ #endif
++
++ #ifdef B19200
++ baudRateMap.insert(19200, B19200);
++ #endif
++
++ #ifdef B28800
++ baudRateMap.insert(28800, B28800);
++ #endif
++
++ #ifdef B38400
++ baudRateMap.insert(38400, B38400);
++ #endif
++
++ #ifdef B57600
++ baudRateMap.insert(57600, B57600);
++ #endif
++
++ #ifdef B76800
++ baudRateMap.insert(76800, B76800);
++ #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::settingFromBaudRate(qint32 baudRate)
++ {
++ return standardBaudRateMap().value(baudRate);
++ }
++
++ QList<qint32> QSerialPortPrivate::standardBaudRates()
++ {
++ return standardBaudRateMap().keys();
++ }
++
++ QSerialPort::Handle QSerialPort::handle() const
++ {
++ Q_D(const QSerialPort);
++ return d->descriptor;
++ }
++
++ QT_END_NAMESPACE