summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenis Shienkov <denis.shienkov@gmail.com>2015-09-08 22:05:49 +0300
committerDenis Shienkov <denis.shienkov@gmail.com>2015-09-10 08:39:12 +0000
commit2ab0a814dcd050e6cfb278a450549482400d5f73 (patch)
tree11b345c5ad4b70615221acaec30d7fa0af6545cc
parent5c3b6cc7704145c4bfd7fad9c001e40d841e2ee2 (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.cpp57
-rw-r--r--tests/auto/qserialport/tst_qserialport.cpp10
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()