Qt Serial Port Baud Rate
I need to be able to change the baud rate on the port without the DTR or RTS lines changing. It works in the SimpleIDE 0-8-5 terminal. There is a baud-rate box in the terminal that can be changed at any time.
PermalinkJoin GitHub today
GitHub is home to over 36 million developers working together to host and review code, manage projects, and build software together.
Sign upBranch:master
Find file Copy path
Cannot retrieve contributors at this time
/**************************************************************************** |
** |
** 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: https://www.qt.io/licensing/ |
** |
** 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 The Qt Company. For licensing terms |
** and conditions see https://www.qt.io/terms-conditions. For further |
** information use the contact form at https://www.qt.io/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 3 as published by the Free Software |
** Foundation and appearing in the file LICENSE.LGPL3 included in the |
** packaging of this file. Please review the following information to |
** ensure the GNU Lesser General Public License version 3 requirements |
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
** |
** GNU General Public License Usage |
** Alternatively, this file may be used under the terms of the GNU |
** General Public License version 2.0 or (at your option) the GNU General |
** Public license version 3 or any later version approved by the KDE Free |
** Qt Foundation. The licenses are as published by the Free Software |
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
** included in the packaging of this file. Please review the following |
** information to ensure the GNU General Public License requirements will |
** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
** https://www.gnu.org/licenses/gpl-3.0.html. |
** |
** $QT_END_LICENSE$ |
** |
****************************************************************************/ |
#include'qserialport_p.h' |
#include'qserialportinfo_p.h' |
#include<QtCore/qelapsedtimer.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_OSX |
#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 |
#defineCRTSCTS (IHFLOW OHFLOW) |
#endif |
#ifdef Q_OS_LINUX |
# ifdef Q_OS_ANDROID |
# include<android/api-level.h> |
# else |
# define__ANDROID_API__21 |
# endif |
# if !defined(Q_OS_ANDROID) (!defined(Q_PROCESSOR_X86) && __ANDROID_API__ < 21) |
structtermios2 { |
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 |
#defineTCGETS2_IOR('T', 0x2A, structtermios2) |
#endif |
#ifndef TCSETS2 |
#defineTCSETS2_IOW('T', 0x2B, structtermios2) |
#endif |
#ifndef BOTHER |
#defineBOTHER0010000 |
#endif |
#endif |
QT_BEGIN_NAMESPACE |
QString serialPortLockFilePath(const QString &portName) |
{ |
staticconst 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 filesn'); |
for (const QString &lockDirectoryPath : lockDirectoryPaths) |
qWarning('t%sn', qPrintable(lockDirectoryPath)); |
returnQString(); |
} |
return lockFilePath; |
} |
classReadNotifier : publicQSocketNotifier |
{ |
public: |
explicitReadNotifier(QSerialPortPrivate *d, QObject *parent) |
: QSocketNotifier(d->descriptor, QSocketNotifier::Read, parent) |
, dptr(d) |
{ |
} |
protected: |
boolevent(QEvent *e) Q_DECL_OVERRIDE |
{ |
if (e->type() QEvent::SockAct) { |
dptr->readNotification(); |
returntrue; |
} |
returnQSocketNotifier::event(e); |
} |
private: |
QSerialPortPrivate * const dptr; |
}; |
classWriteNotifier : publicQSocketNotifier |
{ |
public: |
explicitWriteNotifier(QSerialPortPrivate *d, QObject *parent) |
: QSocketNotifier(d->descriptor, QSocketNotifier::Write, parent) |
, dptr(d) |
{ |
} |
protected: |
boolevent(QEvent *e) Q_DECL_OVERRIDE |
{ |
if (e->type() QEvent::SockAct) { |
dptr->completeAsyncWrite(); |
returntrue; |
} |
returnQSocketNotifier::event(e); |
} |
private: |
QSerialPortPrivate * const dptr; |
}; |
staticinlinevoidqt_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; |
} |
staticinlinevoidqt_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; |
} |
} |
staticinlinevoidqt_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: |
tio->c_cflag = PARENB; |
tio->c_iflag = PARMRK INPCK; |
tio->c_iflag &= ~IGNPAR; |
break; |
} |
} |
staticinlinevoidqt_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; |
} |
} |
staticinlinevoidqt_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; |
} |
} |
boolQSerialPortPrivate::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'))); |
returnfalse; |
} |
QScopedPointer<QLockFile> newLockFileScopedPointer(newQLockFile(lockFilePath)); |
if (!newLockFileScopedPointer->tryLock()) { |
setError(QSerialPortErrorInfo(QSerialPort::PermissionError, QSerialPort::tr('Permission error while locking the device'))); |
returnfalse; |
} |
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()); |
returnfalse; |
} |
if (!initialize(mode)) { |
qt_safe_close(descriptor); |
returnfalse; |
} |
lockFileScopedPointer.swap(newLockFileScopedPointer); |
returntrue; |
} |
voidQSerialPortPrivate::close() |
{ |
if (settingsRestoredOnClose) |
::tcsetattr(descriptor, TCSANOW, &restoredTermios); |
#ifdef TIOCNXCL |
::ioctl(descriptor, TIOCNXCL); |
#endif |
delete readNotifier; |
readNotifier = nullptr; |
delete writeNotifier; |
writeNotifier = nullptr; |
qt_safe_close(descriptor); |
lockFileScopedPointer.reset(nullptr); |
descriptor = -1; |
pendingBytesWritten = 0; |
writeSequenceStarted = 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; |
} |
boolQSerialPortPrivate::setDataTerminalReady(bool set) |
{ |
int status = TIOCM_DTR; |
if (::ioctl(descriptor, set ? TIOCMBIS : TIOCMBIC, &status) -1) { |
setError(getSystemError()); |
returnfalse; |
} |
returntrue; |
} |
boolQSerialPortPrivate::setRequestToSend(bool set) |
{ |
int status = TIOCM_RTS; |
if (::ioctl(descriptor, set ? TIOCMBIS : TIOCMBIC, &status) -1) { |
setError(getSystemError()); |
returnfalse; |
} |
returntrue; |
} |
boolQSerialPortPrivate::flush() |
{ |
returncompleteAsyncWrite(); |
} |
boolQSerialPortPrivate::clear(QSerialPort::Directions directions) |
{ |
if (::tcflush(descriptor, (directions QSerialPort::AllDirections) |
? TCIOFLUSH : (directions & QSerialPort::Input) ? TCIFLUSH : TCOFLUSH) -1) { |
setError(getSystemError()); |
returnfalse; |
} |
returntrue; |
} |
boolQSerialPortPrivate::sendBreak(int duration) |
{ |
if (::tcsendbreak(descriptor, duration) -1) { |
setError(getSystemError()); |
returnfalse; |
} |
returntrue; |
} |
boolQSerialPortPrivate::setBreakEnabled(bool set) |
{ |
if (::ioctl(descriptor, set ? TIOCSBRK : TIOCCBRK) -1) { |
setError(getSystemError()); |
returnfalse; |
} |
returntrue; |
} |
boolQSerialPortPrivate::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()))) { |
returnfalse; |
} |
if (readyToRead) |
returnreadNotification(); |
if (readyToWrite && !completeAsyncWrite()) |
returnfalse; |
} while (msecs -1qt_subtract_from_timeout(msecs, stopWatch.elapsed()) > 0); |
returnfalse; |
} |
boolQSerialPortPrivate::waitForBytesWritten(int msecs) |
{ |
if (writeBuffer.isEmpty() && pendingBytesWritten <= 0) |
returnfalse; |
QElapsedTimer stopWatch; |
stopWatch.start(); |
for (;;) { |
bool readyToRead = false; |
bool readyToWrite = false; |
if (!waitForReadOrWrite(&readyToRead, &readyToWrite, true, !writeBuffer.isEmpty(), |
qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) { |
returnfalse; |
} |
if (readyToRead && !readNotification()) |
returnfalse; |
if (readyToWrite) |
returncompleteAsyncWrite(); |
} |
returnfalse; |
} |
boolQSerialPortPrivate::setBaudRate() |
{ |
if (inputBaudRate outputBaudRate) |
returnsetBaudRate(inputBaudRate, QSerialPort::AllDirections); |
return (setBaudRate(inputBaudRate, QSerialPort::Input) |
&& setBaudRate(outputBaudRate, QSerialPort::Output)); |
} |
boolQSerialPortPrivate::setStandardBaudRate(qint32 baudRate, QSerialPort::Directions directions) |
{ |
#ifdef Q_OS_LINUX |
// try to clear custom baud rate, using termios v2 |
structtermios2 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) |
structserial_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)) |
returnfalse; |
if ((directions & QSerialPort::Input) && ::cfsetispeed(&tio, baudRate) < 0) { |
setError(getSystemError()); |
returnfalse; |
} |
if ((directions & QSerialPort::Output) && ::cfsetospeed(&tio, baudRate) < 0) { |
setError(getSystemError()); |
returnfalse; |
} |
returnsetTermios(&tio); |
} |
#if defined(Q_OS_LINUX) |
boolQSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions) |
{ |
if (directions != QSerialPort::AllDirections) { |
setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, |
QSerialPort::tr('Cannot set custom speed for one direction'))); |
returnfalse; |
} |
structtermios2 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) { |
returntrue; |
} |
} |
structserial_struct serial; |
if (::ioctl(descriptor, TIOCGSERIAL, &serial) -1) { |
setError(getSystemError()); |
returnfalse; |
} |
serial.flags &= ~ASYNC_SPD_MASK; |
serial.flags = (ASYNC_SPD_CUST /* ASYNC_LOW_LATENCY*/); |
serial.custom_divisor = serial.baud_base / baudRate; |
if (serial.custom_divisor0) { |
setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, |
QSerialPort::tr('No suitable custom baud rate divisor'))); |
returnfalse; |
} |
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()); |
returnfalse; |
} |
returnsetStandardBaudRate(B38400, directions); |
} |
#elif defined(Q_OS_OSX) |
boolQSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions) |
{ |
if (directions != QSerialPort::AllDirections) { |
setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, |
QSerialPort::tr('Cannot set custom speed for one direction'))); |
returnfalse; |
} |
#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()); |
returnfalse; |
} |
returntrue; |
#else |
setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, |
QSerialPort::tr('Custom baud rate is not supported'))); |
returnfalse; |
#endif |
} |
#elif defined(Q_OS_QNX) |
boolQSerialPortPrivate::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. |
returnsetStandardBaudRate(baudRate, directions); |
} |
#else |
boolQSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions) |
{ |
Q_UNUSED(baudRate); |
Q_UNUSED(directions); |
setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, |
QSerialPort::tr('Custom baud rate is not supported'))); |
returnfalse; |
} |
#endif |
boolQSerialPortPrivate::setBaudRate(qint32 baudRate, QSerialPort::Directions directions) |
{ |
if (baudRate <= 0) { |
setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, QSerialPort::tr('Invalid baud rate value'))); |
returnfalse; |
} |
const qint32 unixBaudRate = QSerialPortPrivate::settingFromBaudRate(baudRate); |
return (unixBaudRate > 0) |
? setStandardBaudRate(unixBaudRate, directions) |
: setCustomBaudRate(baudRate, directions); |
} |
boolQSerialPortPrivate::setDataBits(QSerialPort::DataBits dataBits) |
{ |
termios tio; |
if (!getTermios(&tio)) |
returnfalse; |
qt_set_databits(&tio, dataBits); |
returnsetTermios(&tio); |
} |
boolQSerialPortPrivate::setParity(QSerialPort::Parity parity) |
{ |
termios tio; |
if (!getTermios(&tio)) |
returnfalse; |
qt_set_parity(&tio, parity); |
returnsetTermios(&tio); |
} |
boolQSerialPortPrivate::setStopBits(QSerialPort::StopBits stopBits) |
{ |
termios tio; |
if (!getTermios(&tio)) |
returnfalse; |
qt_set_stopbits(&tio, stopBits); |
returnsetTermios(&tio); |
} |
boolQSerialPortPrivate::setFlowControl(QSerialPort::FlowControl flowControl) |
{ |
termios tio; |
if (!getTermios(&tio)) |
returnfalse; |
qt_set_flowcontrol(&tio, flowControl); |
returnsetTermios(&tio); |
} |
boolQSerialPortPrivate::startAsyncRead() |
{ |
setReadNotificationEnabled(true); |
returntrue; |
} |
boolQSerialPortPrivate::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); |
returnfalse; |
} |
} |
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); |
returnfalse; |
} |
newBytes = buffer.size() - newBytes; |
// only emit readyRead() when not recursing, and only if there is data available |
constbool hasData = newBytes > 0; |
if (!emittedReadyRead && hasData) { |
emittedReadyRead = true; |
emit q->readyRead(); |
emittedReadyRead = false; |
} |
returntrue; |
} |
boolQSerialPortPrivate::startAsyncWrite() |
{ |
if (writeBuffer.isEmpty() writeSequenceStarted) |
returntrue; |
// 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); |
returnfalse; |
} |
writeBuffer.free(written); |
pendingBytesWritten += written; |
writeSequenceStarted = true; |
if (!isWriteNotificationEnabled()) |
setWriteNotificationEnabled(true); |
returntrue; |
} |
boolQSerialPortPrivate::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); |
returntrue; |
} |
returnstartAsyncWrite(); |
} |
inlineboolQSerialPortPrivate::initialize(QIODevice::OpenMode mode) |
{ |
#ifdef TIOCEXCL |
if (::ioctl(descriptor, TIOCEXCL) -1) |
setError(getSystemError()); |
#endif |
termios tio; |
if (!getTermios(&tio)) |
returnfalse; |
restoredTermios = tio; |
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)) |
returnfalse; |
if (!setBaudRate()) |
returnfalse; |
if (mode & QIODevice::ReadOnly) |
setReadNotificationEnabled(true); |
returntrue; |
} |
qint64 QSerialPortPrivate::writeData(constchar *data, qint64 maxSize) |
{ |
writeBuffer.append(data, maxSize); |
if (!writeBuffer.isEmpty() && !isWriteNotificationEnabled()) |
setWriteNotificationEnabled(true); |
return maxSize; |
} |
boolQSerialPortPrivate::setTermios(const termios *tio) |
{ |
if (::tcsetattr(descriptor, TCSANOW, tio) -1) { |
setError(getSystemError()); |
returnfalse; |
} |
returntrue; |
} |
boolQSerialPortPrivate::getTermios(termios *tio) |
{ |
::memset(tio, 0, sizeof(termios)); |
if (::tcgetattr(descriptor, tio) -1) { |
setError(getSystemError()); |
returnfalse; |
} |
returntrue; |
} |
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_OSX |
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; |
} |
boolQSerialPortPrivate::isReadNotificationEnabled() const |
{ |
return readNotifier && readNotifier->isEnabled(); |
} |
voidQSerialPortPrivate::setReadNotificationEnabled(bool enable) |
{ |
Q_Q(QSerialPort); |
if (readNotifier) { |
readNotifier->setEnabled(enable); |
} elseif (enable) { |
readNotifier = newReadNotifier(this, q); |
readNotifier->setEnabled(true); |
} |
} |
boolQSerialPortPrivate::isWriteNotificationEnabled() const |
{ |
return writeNotifier && writeNotifier->isEnabled(); |
} |
voidQSerialPortPrivate::setWriteNotificationEnabled(bool enable) |
{ |
Q_Q(QSerialPort); |
if (writeNotifier) { |
writeNotifier->setEnabled(enable); |
} elseif (enable) { |
writeNotifier = newWriteNotifier(this, q); |
writeNotifier->setEnabled(true); |
} |
} |
boolQSerialPortPrivate::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; |
constint ret = qt_poll_msecs(&pfd, 1, msecs); |
if (ret < 0) { |
setError(getSystemError()); |
returnfalse; |
} |
if (ret 0) { |
setError(QSerialPortErrorInfo(QSerialPort::TimeoutError)); |
returnfalse; |
} |
if (pfd.revents & POLLNVAL) { |
setError(getSystemError(EBADF)); |
returnfalse; |
} |
*selectForWrite = ((pfd.revents & POLLOUT) != 0); |
*selectForRead = ((pfd.revents & POLLIN) != 0); |
returntrue; |
} |
qint64 QSerialPortPrivate::readFromPort(char *data, qint64 maxSize) |
{ |
returnqt_safe_read(descriptor, data, maxSize); |
} |
qint64 QSerialPortPrivate::writeToPort(constchar *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 |
staticinlineboolevenParity(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(constchar *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; |
flush(); //force sending already buffered data, because setTermios(&tio); cleares buffers |
//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 |
staticconst 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; |
} |
staticconst BaudRateMap& standardBaudRateMap() |
{ |
staticconst BaudRateMap baudRateMap = createStandardBaudRateMap(); |
return baudRateMap; |
} |
qint32 QSerialPortPrivate::settingFromBaudRate(qint32 baudRate) |
{ |
returnstandardBaudRateMap().value(baudRate); |
} |
QList<qint32> QSerialPortPrivate::standardBaudRates() |
{ |
returnstandardBaudRateMap().keys(); |
} |
QSerialPort::HandleQSerialPort::handle() const |
{ |
Q_D(const QSerialPort); |
return d->descriptor; |
} |
QT_END_NAMESPACE |
Copy lines Copy permalink