diff options
author | Denis Shienkov <denis.shienkov@gmail.com> | 2015-09-08 22:05:49 +0300 |
---|---|---|
committer | Denis Shienkov <denis.shienkov@gmail.com> | 2015-09-10 08:39:12 +0000 |
commit | 2ab0a814dcd050e6cfb278a450549482400d5f73 (patch) | |
tree | 11b345c5ad4b70615221acaec30d7fa0af6545cc | |
parent | 5c3b6cc7704145c4bfd7fad9c001e40d841e2ee2 (diff) |
Use the termios v2 to custom baud rate switching
For a long time the Linux kernel has support new version of
the termios structure (V2). To setup the user speed it is
necessary to do following steps:
* Query current content of termios2 by calling the ioctl() with
the TCGETS2 flag.
* Add to the c_cflag field the BOTHER flag.
* Set to the c_ispeed/c_ospeed fields an actual values of custom speeds.
* Write new content of termios2 back by calling the ioctl() with the
TCSETS2 flag.
This new method much simpler and is more transparent in implementation
than changes of a custom divisor. It is preferable and will be used
by default for all cases. If for some reason a current Linux kernel
doesn't support the termios2, then will be falling back to the old
method with changing of a custom divisor.
Tested with the on-board and the USB (PL2303) serial ports.
(cherry-picked from eb0535c9701202d5f74fe1fc9b3b094bbecb8043)
Task-number: QTBUG-48094
Change-Id: I49a5389b089980b616b4e2ff815ce0b579752d0e
Reviewed-by: Denis Shienkov <denis.shienkov@gmail.com>
-rw-r--r-- | src/serialport/qserialport_unix.cpp | 57 | ||||
-rw-r--r-- | tests/auto/qserialport/tst_qserialport.cpp | 10 |
2 files changed, 66 insertions, 1 deletions
diff --git a/src/serialport/qserialport_unix.cpp b/src/serialport/qserialport_unix.cpp index b6194d08..7954cc3d 100644 --- a/src/serialport/qserialport_unix.cpp +++ b/src/serialport/qserialport_unix.cpp @@ -60,6 +60,33 @@ #define CRTSCTS (IHFLOW | OHFLOW) #endif +#ifdef Q_OS_LINUX + +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 */ +}; + +#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 + #include <private/qcore_unix_p.h> #include <QtCore/qelapsedtimer.h> @@ -430,7 +457,17 @@ bool QSerialPortPrivate::setBaudRate() bool QSerialPortPrivate::setStandardBaudRate(qint32 baudRate, QSerialPort::Directions directions) { #ifdef Q_OS_LINUX - // try to clear custom baud rate + // 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) { @@ -464,6 +501,24 @@ bool QSerialPortPrivate::setStandardBaudRate(qint32 baudRate, QSerialPort::Direc bool QSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions) { + struct termios2 tio2; + + if (::ioctl(descriptor, TCGETS2, &tio2) != -1) { + tio2.c_cflag &= ~CBAUD; + tio2.c_cflag |= BOTHER; + + if (directions & QSerialPort::Input) + tio2.c_ispeed = baudRate; + + if (directions & QSerialPort::Output) + 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) { diff --git a/tests/auto/qserialport/tst_qserialport.cpp b/tests/auto/qserialport/tst_qserialport.cpp index cc0fb3c4..8aeca3c1 100644 --- a/tests/auto/qserialport/tst_qserialport.cpp +++ b/tests/auto/qserialport/tst_qserialport.cpp @@ -344,6 +344,8 @@ void tst_QSerialPort::baudRate_data() QTest::newRow("Baud38400") << static_cast<qint32>(QSerialPort::Baud38400); QTest::newRow("Baud57600") << static_cast<qint32>(QSerialPort::Baud57600); QTest::newRow("Baud115200") << static_cast<qint32>(QSerialPort::Baud115200); + + QTest::newRow("31250") << 31250; // custom baudrate (MIDI) } void tst_QSerialPort::baudRate() @@ -1098,6 +1100,14 @@ void tst_QSerialPort::readWriteWithDifferentBaudRate_data() QTest::newRow("9600, 9600") << 9600 << 9600 << true; QTest::newRow("115200, 115200") << 115200 << 115200 << true; QTest::newRow("9600, 115200") << 9600 << 115200 << false; + + QTest::newRow("31250, 31250") << 31250 << 31250 << true; // custom baudrate (MIDI) + QTest::newRow("31250, 115200") << 31250 << 115200 << false; + +#ifdef Q_OS_LINUX + QTest::newRow("14400, 14400") << 14400 << 14400 << true; // custom baudrate for Linux + QTest::newRow("14400, 115200") << 14400 << 115200 << false; +#endif } void tst_QSerialPort::readWriteWithDifferentBaudRate() |