1// Copyright (C) 2012 Denis Shienkov <denis.shienkov@gmail.com>
2// Copyright (C) 2012 Laszlo Papp <lpapp@kde.org>
3// Copyright (C) 2012 Andre Hartmann <aha_1980@gmx.de>
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#include "qserialport_p.h"
7#include "qserialportinfo_p.h"
8
9#include <QtCore/qdeadlinetimer.h>
10#include <QtCore/qelapsedtimer.h>
11#include <QtCore/qloggingcategory.h>
12#include <QtCore/qmap.h>
13#include <QtCore/qsocketnotifier.h>
14#include <QtCore/qstandardpaths.h>
15
16#include <private/qcore_unix_p.h>
17
18#include <errno.h>
19#include <fcntl.h>
20#include <sys/ioctl.h>
21#include <sys/time.h>
22#include <unistd.h>
23
24#ifdef Q_OS_MACOS
25#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
26#include <IOKit/serial/ioss.h>
27#endif
28#endif
29
30#ifdef Q_OS_QNX
31#define CRTSCTS (IHFLOW | OHFLOW)
32#endif
33
34#ifdef Q_OS_LINUX
35
36# ifdef Q_OS_ANDROID
37# include <android/api-level.h>
38# else
39# define __ANDROID_API__ 16
40# endif
41
42# if !defined(Q_OS_ANDROID) || (!defined(Q_PROCESSOR_X86) && __ANDROID_API__ < 16)
43struct termios2 {
44 tcflag_t c_iflag; /* input mode flags */
45 tcflag_t c_oflag; /* output mode flags */
46 tcflag_t c_cflag; /* control mode flags */
47 tcflag_t c_lflag; /* local mode flags */
48 cc_t c_line; /* line discipline */
49 cc_t c_cc[19]; /* control characters */
50 speed_t c_ispeed; /* input speed */
51 speed_t c_ospeed; /* output speed */
52};
53# endif
54
55#ifndef TCGETS2
56#define TCGETS2 _IOR('T', 0x2A, struct termios2)
57#endif
58
59#ifndef TCSETS2
60#define TCSETS2 _IOW('T', 0x2B, struct termios2)
61#endif
62
63#ifndef BOTHER
64#define BOTHER 0010000
65#endif
66
67#endif
68
69[[maybe_unused]]
70Q_STATIC_LOGGING_CATEGORY(lcUnixWarnings, "qt.serialport.unix.warnings");
71
72QT_BEGIN_NAMESPACE
73
74QString serialPortLockFilePath(const QString &portName)
75{
76 static const QStringList lockDirectoryPaths = QStringList()
77 << QStringLiteral("/var/lock")
78 << QStringLiteral("/etc/locks")
79 << QStringLiteral("/var/spool/locks")
80 << QStringLiteral("/var/spool/uucp")
81 << QStringLiteral("/tmp")
82 << QStringLiteral("/var/tmp")
83 << QStringLiteral("/var/lock/lockdev")
84 << QStringLiteral("/run/lock")
85#ifdef Q_OS_ANDROID
86 << QStringLiteral("/data/local/tmp")
87#endif
88 << QStandardPaths::writableLocation(type: QStandardPaths::TempLocation);
89
90 QString fileName = portName;
91 fileName.replace(before: QLatin1Char('/'), after: QLatin1Char('_'));
92 fileName.prepend(s: QLatin1String("/LCK.."));
93
94 QString lockFilePath;
95
96 for (const QString &lockDirectoryPath : lockDirectoryPaths) {
97 const QString filePath = lockDirectoryPath + fileName;
98
99 QFileInfo lockDirectoryInfo(lockDirectoryPath);
100 if (lockDirectoryInfo.isReadable()) {
101 if (QFile::exists(fileName: filePath) || lockDirectoryInfo.isWritable()) {
102 lockFilePath = filePath;
103 break;
104 }
105 }
106 }
107
108 if (lockFilePath.isEmpty()) {
109 qWarning(msg: "The following directories are not readable or writable for detaling with lock files\n");
110 for (const QString &lockDirectoryPath : lockDirectoryPaths)
111 qWarning(msg: "\t%s\n", qPrintable(lockDirectoryPath));
112 return QString();
113 }
114
115 return lockFilePath;
116}
117
118class ReadNotifier : public QSocketNotifier
119{
120public:
121 explicit ReadNotifier(QSerialPortPrivate *d, QObject *parent)
122 : QSocketNotifier(d->descriptor, QSocketNotifier::Read, parent)
123 , dptr(d)
124 {
125 }
126
127protected:
128 bool event(QEvent *e) override
129 {
130 if (e->type() == QEvent::SockAct) {
131 dptr->readNotification();
132 return true;
133 }
134 return QSocketNotifier::event(e);
135 }
136
137private:
138 QSerialPortPrivate * const dptr;
139};
140
141class WriteNotifier : public QSocketNotifier
142{
143public:
144 explicit WriteNotifier(QSerialPortPrivate *d, QObject *parent)
145 : QSocketNotifier(d->descriptor, QSocketNotifier::Write, parent)
146 , dptr(d)
147 {
148 }
149
150protected:
151 bool event(QEvent *e) override
152 {
153 if (e->type() == QEvent::SockAct) {
154 dptr->completeAsyncWrite();
155 return true;
156 }
157 return QSocketNotifier::event(e);
158 }
159
160private:
161 QSerialPortPrivate * const dptr;
162};
163
164class ExceptionNotifier : public QSocketNotifier
165{
166public:
167 explicit ExceptionNotifier(QSerialPortPrivate *d, QObject *parent)
168 : QSocketNotifier(d->descriptor, QSocketNotifier::Exception, parent),
169 dptr(d)
170 {
171 }
172
173protected:
174 bool event(QEvent *e) override
175 {
176 if (e->type() == QEvent::SockAct) {
177 dptr->handleException();
178 return true;
179 }
180 return QSocketNotifier::event(e);
181 }
182
183private:
184 QSerialPortPrivate * const dptr;
185};
186
187static inline void qt_set_common_props(termios *tio, QIODevice::OpenMode m)
188{
189#ifdef Q_OS_SOLARIS
190 tio->c_iflag &= ~(IMAXBEL|IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
191 tio->c_oflag &= ~OPOST;
192 tio->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
193 tio->c_cflag &= ~(CSIZE|PARENB);
194 tio->c_cflag |= CS8;
195#else
196 ::cfmakeraw(termios_p: tio);
197#endif
198
199 tio->c_cflag |= CLOCAL;
200 tio->c_cc[VTIME] = 0;
201 tio->c_cc[VMIN] = 0;
202
203 if (m & QIODevice::ReadOnly)
204 tio->c_cflag |= CREAD;
205}
206
207static inline void qt_set_databits(termios *tio, QSerialPort::DataBits databits)
208{
209 tio->c_cflag &= ~CSIZE;
210 switch (databits) {
211 case QSerialPort::Data5:
212 tio->c_cflag |= CS5;
213 break;
214 case QSerialPort::Data6:
215 tio->c_cflag |= CS6;
216 break;
217 case QSerialPort::Data7:
218 tio->c_cflag |= CS7;
219 break;
220 case QSerialPort::Data8:
221 tio->c_cflag |= CS8;
222 break;
223 default:
224 tio->c_cflag |= CS8;
225 break;
226 }
227}
228
229static inline void qt_set_parity(termios *tio, QSerialPort::Parity parity)
230{
231 tio->c_iflag &= ~(PARMRK | INPCK);
232 tio->c_iflag |= IGNPAR;
233
234 switch (parity) {
235
236#ifdef CMSPAR
237 // Here Installation parity only for GNU/Linux where the macro CMSPAR.
238 case QSerialPort::SpaceParity:
239 tio->c_cflag &= ~PARODD;
240 tio->c_cflag |= PARENB | CMSPAR;
241 break;
242 case QSerialPort::MarkParity:
243 tio->c_cflag |= PARENB | CMSPAR | PARODD;
244 break;
245#endif
246 case QSerialPort::NoParity:
247 tio->c_cflag &= ~PARENB;
248 break;
249 case QSerialPort::EvenParity:
250 tio->c_cflag &= ~PARODD;
251 tio->c_cflag |= PARENB;
252 break;
253 case QSerialPort::OddParity:
254 tio->c_cflag |= PARENB | PARODD;
255 break;
256 default:
257#ifndef CMSPAR
258 case QSerialPort::SpaceParity:
259 case QSerialPort::MarkParity:
260 qCWarning(lcUnixWarnings, "Space and Mark parity is not supported by the OS.");
261#endif
262 tio->c_cflag |= PARENB;
263 tio->c_iflag |= PARMRK | INPCK;
264 tio->c_iflag &= ~IGNPAR;
265 break;
266 }
267}
268
269static inline void qt_set_stopbits(termios *tio, QSerialPort::StopBits stopbits)
270{
271 switch (stopbits) {
272 case QSerialPort::OneStop:
273 tio->c_cflag &= ~CSTOPB;
274 break;
275 case QSerialPort::TwoStop:
276 tio->c_cflag |= CSTOPB;
277 break;
278 default:
279 tio->c_cflag &= ~CSTOPB;
280 break;
281 }
282}
283
284static inline void qt_set_flowcontrol(termios *tio, QSerialPort::FlowControl flowcontrol)
285{
286 switch (flowcontrol) {
287 case QSerialPort::NoFlowControl:
288 tio->c_cflag &= ~CRTSCTS;
289 tio->c_iflag &= ~(IXON | IXOFF | IXANY);
290 break;
291 case QSerialPort::HardwareControl:
292 tio->c_cflag |= CRTSCTS;
293 tio->c_iflag &= ~(IXON | IXOFF | IXANY);
294 break;
295 case QSerialPort::SoftwareControl:
296 tio->c_cflag &= ~CRTSCTS;
297 tio->c_iflag |= IXON | IXOFF | IXANY;
298 break;
299 default:
300 tio->c_cflag &= ~CRTSCTS;
301 tio->c_iflag &= ~(IXON | IXOFF | IXANY);
302 break;
303 }
304}
305
306bool QSerialPortPrivate::open(QIODevice::OpenMode mode)
307{
308 QString lockFilePath = serialPortLockFilePath(portName: QSerialPortInfoPrivate::portNameFromSystemLocation(source: systemLocation));
309 bool isLockFileEmpty = lockFilePath.isEmpty();
310 if (isLockFileEmpty) {
311 qWarning(msg: "Failed to create a lock file for opening the device");
312 setError(QSerialPortErrorInfo(QSerialPort::PermissionError, QSerialPort::tr(s: "Permission error while creating lock file")));
313 return false;
314 }
315
316 auto newLockFileScopedPointer = std::make_unique<QLockFile>(args&: lockFilePath);
317
318 if (!newLockFileScopedPointer->tryLock()) {
319 setError(QSerialPortErrorInfo(QSerialPort::PermissionError, QSerialPort::tr(s: "Permission error while locking the device")));
320 return false;
321 }
322
323 int flags = O_NOCTTY | O_NONBLOCK;
324
325 switch (mode & QIODevice::ReadWrite) {
326 case QIODevice::WriteOnly:
327 flags |= O_WRONLY;
328 break;
329 case QIODevice::ReadWrite:
330 flags |= O_RDWR;
331 break;
332 default:
333 flags |= O_RDONLY;
334 break;
335 }
336
337 descriptor = qt_safe_open(pathname: systemLocation.toLocal8Bit().constData(), flags);
338
339 if (descriptor == -1) {
340 setError(getSystemError());
341 return false;
342 }
343
344 if (!initialize(mode)) {
345 qt_safe_close(fd: descriptor);
346 return false;
347 }
348
349 lockFileScopedPointer = std::move(newLockFileScopedPointer);
350
351 return true;
352}
353
354void QSerialPortPrivate::close()
355{
356 if (settingsRestoredOnClose)
357 ::tcsetattr(fd: descriptor, TCSANOW, termios_p: &restoredTermios);
358
359#ifdef TIOCNXCL
360 ::ioctl(fd: descriptor, TIOCNXCL);
361#endif
362
363 delete readNotifier;
364 readNotifier = nullptr;
365
366 delete writeNotifier;
367 writeNotifier = nullptr;
368
369 delete exceptionNotifier;
370 exceptionNotifier = nullptr;
371
372 qt_safe_close(fd: descriptor);
373
374 lockFileScopedPointer.reset(p: nullptr);
375
376 descriptor = -1;
377 pendingBytesWritten = 0;
378 writeSequenceStarted = false;
379 gotException = false;
380}
381
382QSerialPort::PinoutSignals QSerialPortPrivate::pinoutSignals()
383{
384 int arg = 0;
385
386 if (::ioctl(fd: descriptor, TIOCMGET, &arg) == -1) {
387 setError(getSystemError());
388 return QSerialPort::NoSignal;
389 }
390
391 QSerialPort::PinoutSignals ret = QSerialPort::NoSignal;
392
393#ifdef TIOCM_LE
394 if (arg & TIOCM_LE)
395 ret |= QSerialPort::DataSetReadySignal;
396#endif
397#ifdef TIOCM_DTR
398 if (arg & TIOCM_DTR)
399 ret |= QSerialPort::DataTerminalReadySignal;
400#endif
401#ifdef TIOCM_RTS
402 if (arg & TIOCM_RTS)
403 ret |= QSerialPort::RequestToSendSignal;
404#endif
405#ifdef TIOCM_ST
406 if (arg & TIOCM_ST)
407 ret |= QSerialPort::SecondaryTransmittedDataSignal;
408#endif
409#ifdef TIOCM_SR
410 if (arg & TIOCM_SR)
411 ret |= QSerialPort::SecondaryReceivedDataSignal;
412#endif
413#ifdef TIOCM_CTS
414 if (arg & TIOCM_CTS)
415 ret |= QSerialPort::ClearToSendSignal;
416#endif
417#ifdef TIOCM_CAR
418 if (arg & TIOCM_CAR)
419 ret |= QSerialPort::DataCarrierDetectSignal;
420#elif defined(TIOCM_CD)
421 if (arg & TIOCM_CD)
422 ret |= QSerialPort::DataCarrierDetectSignal;
423#endif
424#ifdef TIOCM_RNG
425 if (arg & TIOCM_RNG)
426 ret |= QSerialPort::RingIndicatorSignal;
427#elif defined(TIOCM_RI)
428 if (arg & TIOCM_RI)
429 ret |= QSerialPort::RingIndicatorSignal;
430#endif
431#ifdef TIOCM_DSR
432 if (arg & TIOCM_DSR)
433 ret |= QSerialPort::DataSetReadySignal;
434#endif
435
436 return ret;
437}
438
439bool QSerialPortPrivate::setDataTerminalReady(bool set)
440{
441 int status = TIOCM_DTR;
442 if (::ioctl(fd: descriptor, request: set ? TIOCMBIS : TIOCMBIC, &status) == -1) {
443 setError(getSystemError());
444 return false;
445 }
446
447 return true;
448}
449
450bool QSerialPortPrivate::setRequestToSend(bool set)
451{
452 int status = TIOCM_RTS;
453 if (::ioctl(fd: descriptor, request: set ? TIOCMBIS : TIOCMBIC, &status) == -1) {
454 setError(getSystemError());
455 return false;
456 }
457
458 return true;
459}
460
461bool QSerialPortPrivate::flush()
462{
463 return completeAsyncWrite();
464}
465
466bool QSerialPortPrivate::clear(QSerialPort::Directions directions)
467{
468 if (::tcflush(fd: descriptor, queue_selector: (directions == QSerialPort::AllDirections)
469 ? TCIOFLUSH : (directions & QSerialPort::Input) ? TCIFLUSH : TCOFLUSH) == -1) {
470 setError(getSystemError());
471 return false;
472 }
473
474 return true;
475}
476
477bool QSerialPortPrivate::sendBreak(int duration)
478{
479 if (::tcsendbreak(fd: descriptor, duration: duration) == -1) {
480 setError(getSystemError());
481 return false;
482 }
483
484 return true;
485}
486
487bool QSerialPortPrivate::setBreakEnabled(bool set)
488{
489 if (::ioctl(fd: descriptor, request: set ? TIOCSBRK : TIOCCBRK) == -1) {
490 setError(getSystemError());
491 return false;
492 }
493
494 return true;
495}
496
497bool QSerialPortPrivate::waitForReadyRead(int msecs)
498{
499 QElapsedTimer stopWatch;
500 stopWatch.start();
501
502 do {
503 bool readyToRead = false;
504 bool readyToWrite = false;
505 if (!waitForReadOrWrite(selectForRead: &readyToRead, selectForWrite: &readyToWrite, checkRead: true, checkWrite: !writeBuffer.isEmpty(),
506 msecs: qt_subtract_from_timeout(timeout: msecs, elapsed: stopWatch.elapsed()))) {
507 return false;
508 }
509
510 if (readyToRead)
511 return readNotification();
512
513 if (readyToWrite && !completeAsyncWrite())
514 return false;
515 } while (msecs == -1 || qt_subtract_from_timeout(timeout: msecs, elapsed: stopWatch.elapsed()) > 0);
516 return false;
517}
518
519bool QSerialPortPrivate::waitForBytesWritten(int msecs)
520{
521 if (writeBuffer.isEmpty() && pendingBytesWritten <= 0)
522 return false;
523
524 QElapsedTimer stopWatch;
525 stopWatch.start();
526
527 for (;;) {
528 bool readyToRead = false;
529 bool readyToWrite = false;
530 const bool checkRead = q_func()->isReadable();
531 if (!waitForReadOrWrite(selectForRead: &readyToRead, selectForWrite: &readyToWrite, checkRead, checkWrite: !writeBuffer.isEmpty(),
532 msecs: qt_subtract_from_timeout(timeout: msecs, elapsed: stopWatch.elapsed()))) {
533 return false;
534 }
535
536 if (readyToRead && !readNotification())
537 return false;
538
539 if (readyToWrite)
540 return completeAsyncWrite();
541 }
542 return false;
543}
544
545bool QSerialPortPrivate::setBaudRate()
546{
547 if (inputBaudRate == outputBaudRate)
548 return setBaudRate(baudRate: inputBaudRate, directions: QSerialPort::AllDirections);
549
550 return (setBaudRate(baudRate: inputBaudRate, directions: QSerialPort::Input)
551 && setBaudRate(baudRate: outputBaudRate, directions: QSerialPort::Output));
552}
553
554bool QSerialPortPrivate::setStandardBaudRate(qint32 baudRate, QSerialPort::Directions directions)
555{
556#ifdef Q_OS_LINUX
557 // try to clear custom baud rate, using termios v2
558 struct termios2 tio2;
559 if (::ioctl(fd: descriptor, TCGETS2, &tio2) != -1) {
560 if (tio2.c_cflag & BOTHER) {
561 tio2.c_cflag &= ~BOTHER;
562 tio2.c_cflag |= CBAUD;
563 ::ioctl(fd: descriptor, TCSETS2, &tio2);
564 }
565 }
566
567 // try to clear custom baud rate, using serial_struct (old way)
568 struct serial_struct serial;
569 ::memset(s: &serial, c: 0, n: sizeof(serial));
570 if (::ioctl(fd: descriptor, TIOCGSERIAL, &serial) != -1) {
571 if (serial.flags & ASYNC_SPD_CUST) {
572 serial.flags &= ~ASYNC_SPD_CUST;
573 serial.custom_divisor = 0;
574 // we don't check on errors because a driver can has not this feature
575 ::ioctl(fd: descriptor, TIOCSSERIAL, &serial);
576 }
577 }
578#endif
579
580 termios tio;
581 if (!getTermios(tio: &tio))
582 return false;
583
584 if ((directions & QSerialPort::Input) && ::cfsetispeed(termios_p: &tio, speed: baudRate) < 0) {
585 setError(getSystemError());
586 return false;
587 }
588
589 if ((directions & QSerialPort::Output) && ::cfsetospeed(termios_p: &tio, speed: baudRate) < 0) {
590 setError(getSystemError());
591 return false;
592 }
593
594 return setTermios(&tio);
595}
596
597#if defined(Q_OS_LINUX)
598
599bool QSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions)
600{
601 if (directions != QSerialPort::AllDirections) {
602 setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError,
603 QSerialPort::tr(s: "Cannot set custom speed for one direction")));
604 return false;
605 }
606
607 struct termios2 tio2;
608
609 if (::ioctl(fd: descriptor, TCGETS2, &tio2) != -1) {
610 tio2.c_cflag &= ~CBAUD;
611 tio2.c_cflag |= BOTHER;
612
613 tio2.c_ispeed = baudRate;
614 tio2.c_ospeed = baudRate;
615
616 if (::ioctl(fd: descriptor, TCSETS2, &tio2) != -1
617 && ::ioctl(fd: descriptor, TCGETS2, &tio2) != -1) {
618 return true;
619 }
620 }
621
622 struct serial_struct serial;
623
624 if (::ioctl(fd: descriptor, TIOCGSERIAL, &serial) == -1) {
625 setError(getSystemError());
626 return false;
627 }
628
629 serial.flags &= ~ASYNC_SPD_MASK;
630 serial.flags |= (ASYNC_SPD_CUST /* | ASYNC_LOW_LATENCY*/);
631 serial.custom_divisor = serial.baud_base / baudRate;
632
633 if (serial.custom_divisor == 0) {
634 setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError,
635 QSerialPort::tr(s: "No suitable custom baud rate divisor")));
636 return false;
637 }
638
639 if (serial.custom_divisor * baudRate != serial.baud_base) {
640 qWarning(msg: "Baud rate of serial port %s is set to %f instead of %d: divisor %f unsupported",
641 qPrintable(systemLocation),
642 float(serial.baud_base) / serial.custom_divisor,
643 baudRate, float(serial.baud_base) / baudRate);
644 }
645
646 if (::ioctl(fd: descriptor, TIOCSSERIAL, &serial) == -1) {
647 setError(getSystemError());
648 return false;
649 }
650
651 return setStandardBaudRate(B38400, directions);
652}
653
654#elif defined(Q_OS_MACOS)
655
656bool QSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions)
657{
658 if (directions != QSerialPort::AllDirections) {
659 setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError,
660 QSerialPort::tr("Cannot set custom speed for one direction")));
661 return false;
662 }
663
664#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
665 if (::ioctl(descriptor, IOSSIOSPEED, &baudRate) == -1) {
666 setError(getSystemError());
667 return false;
668 }
669
670 return true;
671#else
672 setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError,
673 QSerialPort::tr("Custom baud rate is not supported")));
674 return false;
675#endif
676}
677
678#elif defined(Q_OS_QNX)
679
680bool QSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions)
681{
682 // On QNX, the values of the 'Bxxxx' constants are set to 'xxxx' (i.e.
683 // B115200 is defined to '115200'), which means that literal values can be
684 // passed to cfsetispeed/cfsetospeed, including custom values, provided
685 // that the underlying hardware supports them.
686 return setStandardBaudRate(baudRate, directions);
687}
688
689#else
690
691bool QSerialPortPrivate::setCustomBaudRate(qint32 baudRate, QSerialPort::Directions directions)
692{
693 Q_UNUSED(baudRate);
694 Q_UNUSED(directions);
695
696 setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError,
697 QSerialPort::tr("Custom baud rate is not supported")));
698 return false;
699}
700
701#endif
702
703bool QSerialPortPrivate::setBaudRate(qint32 baudRate, QSerialPort::Directions directions)
704{
705 if (baudRate <= 0) {
706 setError(QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, QSerialPort::tr(s: "Invalid baud rate value")));
707 return false;
708 }
709
710 const qint32 unixBaudRate = QSerialPortPrivate::settingFromBaudRate(baudRate);
711
712 return (unixBaudRate > 0)
713 ? setStandardBaudRate(baudRate: unixBaudRate, directions)
714 : setCustomBaudRate(baudRate, directions);
715}
716
717bool QSerialPortPrivate::setDataBits(QSerialPort::DataBits dataBits)
718{
719 termios tio;
720 if (!getTermios(tio: &tio))
721 return false;
722
723 qt_set_databits(tio: &tio, databits: dataBits);
724
725 return setTermios(&tio);
726}
727
728bool QSerialPortPrivate::setParity(QSerialPort::Parity parity)
729{
730 termios tio;
731 if (!getTermios(tio: &tio))
732 return false;
733
734 qt_set_parity(tio: &tio, parity);
735
736 return setTermios(&tio);
737}
738
739bool QSerialPortPrivate::setStopBits(QSerialPort::StopBits stopBits)
740{
741 termios tio;
742 if (!getTermios(tio: &tio))
743 return false;
744
745 qt_set_stopbits(tio: &tio, stopbits: stopBits);
746
747 return setTermios(&tio);
748}
749
750bool QSerialPortPrivate::setFlowControl(QSerialPort::FlowControl flowControl)
751{
752 termios tio;
753 if (!getTermios(tio: &tio))
754 return false;
755
756 qt_set_flowcontrol(tio: &tio, flowcontrol: flowControl);
757
758 return setTermios(&tio);
759}
760
761bool QSerialPortPrivate::startAsyncRead()
762{
763 setReadNotificationEnabled(true);
764 return true;
765}
766
767bool QSerialPortPrivate::readNotification()
768{
769 Q_Q(QSerialPort);
770
771 // Always buffered, read data from the port into the read buffer
772 qint64 newBytes = buffer.size();
773 qint64 bytesToRead = QSERIALPORT_BUFFERSIZE;
774
775 if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - buffer.size())) {
776 bytesToRead = readBufferMaxSize - buffer.size();
777 if (bytesToRead <= 0) {
778 // Buffer is full. User must read data from the buffer
779 // before we can read more from the port.
780 setReadNotificationEnabled(false);
781 return false;
782 }
783 }
784
785 char *ptr = buffer.reserve(bytes: bytesToRead);
786 const qint64 readBytes = readFromPort(data: ptr, maxSize: bytesToRead);
787
788 buffer.chop(bytes: bytesToRead - qMax(a: readBytes, b: qint64(0)));
789
790 if (readBytes < 0) {
791 QSerialPortErrorInfo error = getSystemError();
792 if (error.errorCode != QSerialPort::ResourceError)
793 error.errorCode = QSerialPort::ReadError;
794 else
795 setReadNotificationEnabled(false);
796 setError(error);
797 return false;
798 } else if (readBytes == 0) {
799 // We can get here at least in two cases:
800 // * there is no data in the port
801 // * the device was disconnected (unplugged)
802 // The first case is perfectly valid, and we should simply keep
803 // reading. The second case should be reported as a ResourceError.
804 // We use exception notifier to detect this case.
805 if (gotException) {
806 setReadNotificationEnabled(false);
807 // Force a specific error
808 QSerialPortErrorInfo error = getSystemError(EIO);
809 setError(error);
810 }
811 return false;
812 }
813
814 newBytes = buffer.size() - newBytes;
815
816 // only emit readyRead() when not recursing, and only if there is data available
817 const bool hasData = newBytes > 0;
818
819 if (!emittedReadyRead && hasData) {
820 emittedReadyRead = true;
821 emit q->readyRead();
822 emittedReadyRead = false;
823 }
824
825 return true;
826}
827
828bool QSerialPortPrivate::startAsyncWrite()
829{
830 if (writeBuffer.isEmpty() || writeSequenceStarted)
831 return true;
832
833 // Attempt to write it all in one chunk.
834 qint64 written = writeToPort(data: writeBuffer.readPointer(), maxSize: writeBuffer.nextDataBlockSize());
835 if (written < 0) {
836 QSerialPortErrorInfo error = getSystemError();
837 if (error.errorCode != QSerialPort::ResourceError)
838 error.errorCode = QSerialPort::WriteError;
839 setError(error);
840 return false;
841 }
842
843 writeBuffer.free(bytes: written);
844 pendingBytesWritten += written;
845 writeSequenceStarted = true;
846
847 if (!isWriteNotificationEnabled())
848 setWriteNotificationEnabled(true);
849 return true;
850}
851
852bool QSerialPortPrivate::completeAsyncWrite()
853{
854 Q_Q(QSerialPort);
855
856 if (pendingBytesWritten > 0) {
857 if (!emittedBytesWritten) {
858 emittedBytesWritten = true;
859 emit q->bytesWritten(bytes: pendingBytesWritten);
860 pendingBytesWritten = 0;
861 emittedBytesWritten = false;
862 }
863 }
864
865 writeSequenceStarted = false;
866
867 if (writeBuffer.isEmpty()) {
868 setWriteNotificationEnabled(false);
869 return true;
870 }
871
872 return startAsyncWrite();
873}
874
875void QSerialPortPrivate::handleException()
876{
877 gotException = true;
878}
879
880inline bool QSerialPortPrivate::initialize(QIODevice::OpenMode mode)
881{
882#ifdef TIOCEXCL
883 if (::ioctl(fd: descriptor, TIOCEXCL) == -1)
884 setError(getSystemError());
885#endif
886
887 termios tio;
888 if (!getTermios(tio: &tio))
889 return false;
890
891 restoredTermios = tio;
892
893 auto restoreParametersOnError = qScopeGuard(f: [this] {
894 if (settingsRestoredOnClose)
895 ::tcsetattr(fd: descriptor, TCSANOW, termios_p: &restoredTermios);
896 });
897
898 qt_set_common_props(tio: &tio, m: mode);
899 qt_set_databits(tio: &tio, databits: dataBits);
900 qt_set_parity(tio: &tio, parity);
901 qt_set_stopbits(tio: &tio, stopbits: stopBits);
902 qt_set_flowcontrol(tio: &tio, flowcontrol: flowControl);
903
904 if (!setTermios(&tio))
905 return false;
906
907 if (!setBaudRate())
908 return false;
909
910 if (mode & QIODevice::ReadOnly)
911 setReadNotificationEnabled(true);
912
913 // flush IO buffers
914 clear(directions: QSerialPort::AllDirections);
915
916 exceptionNotifier = new ExceptionNotifier(this, q_func());
917 gotException = false;
918
919 restoreParametersOnError.dismiss();
920 return true;
921}
922
923qint64 QSerialPortPrivate::writeData(const char *data, qint64 maxSize)
924{
925 qint64 toAppend = maxSize;
926
927 if (writeBufferMaxSize && (writeBuffer.size() + toAppend > writeBufferMaxSize))
928 toAppend = writeBufferMaxSize - writeBuffer.size();
929
930 writeBuffer.append(data, size: toAppend);
931 if (!writeBuffer.isEmpty() && !isWriteNotificationEnabled())
932 setWriteNotificationEnabled(true);
933 return toAppend;
934}
935
936bool QSerialPortPrivate::setTermios(const termios *tio)
937{
938 if (::tcsetattr(fd: descriptor, TCSANOW, termios_p: tio) == -1) {
939 setError(getSystemError());
940 return false;
941 }
942 return true;
943}
944
945bool QSerialPortPrivate::getTermios(termios *tio)
946{
947 ::memset(s: tio, c: 0, n: sizeof(termios));
948 if (::tcgetattr(fd: descriptor, termios_p: tio) == -1) {
949 setError(getSystemError());
950 return false;
951 }
952 return true;
953}
954
955QSerialPortErrorInfo QSerialPortPrivate::getSystemError(int systemErrorCode) const
956{
957 if (systemErrorCode == -1)
958 systemErrorCode = errno;
959
960 QSerialPortErrorInfo error;
961 error.errorString = qt_error_string(errorCode: systemErrorCode);
962
963 switch (systemErrorCode) {
964 case ENODEV:
965 error.errorCode = QSerialPort::DeviceNotFoundError;
966 break;
967#ifdef ENOENT
968 case ENOENT:
969 error.errorCode = QSerialPort::DeviceNotFoundError;
970 break;
971#endif
972 case EACCES:
973 error.errorCode = QSerialPort::PermissionError;
974 break;
975 case EBUSY:
976 error.errorCode = QSerialPort::PermissionError;
977 break;
978 case EAGAIN:
979 error.errorCode = QSerialPort::ResourceError;
980 break;
981 case EIO:
982 error.errorCode = QSerialPort::ResourceError;
983 break;
984 case EBADF:
985 error.errorCode = QSerialPort::ResourceError;
986 break;
987#ifdef Q_OS_MACOS
988 case ENXIO:
989 error.errorCode = QSerialPort::ResourceError;
990 break;
991#endif
992#ifdef EINVAL
993 case EINVAL:
994 error.errorCode = QSerialPort::UnsupportedOperationError;
995 break;
996#endif
997#ifdef ENOIOCTLCMD
998 case ENOIOCTLCMD:
999 error.errorCode = QSerialPort::UnsupportedOperationError;
1000 break;
1001#endif
1002#ifdef ENOTTY
1003 case ENOTTY:
1004 error.errorCode = QSerialPort::UnsupportedOperationError;
1005 break;
1006#endif
1007#ifdef EPERM
1008 case EPERM:
1009 error.errorCode = QSerialPort::PermissionError;
1010 break;
1011#endif
1012 default:
1013 error.errorCode = QSerialPort::UnknownError;
1014 break;
1015 }
1016 return error;
1017}
1018
1019bool QSerialPortPrivate::isReadNotificationEnabled() const
1020{
1021 return readNotifier && readNotifier->isEnabled();
1022}
1023
1024void QSerialPortPrivate::setReadNotificationEnabled(bool enable)
1025{
1026 Q_Q(QSerialPort);
1027
1028 if (readNotifier) {
1029 readNotifier->setEnabled(enable);
1030 } else if (enable) {
1031 readNotifier = new ReadNotifier(this, q);
1032 readNotifier->setEnabled(true);
1033 }
1034}
1035
1036bool QSerialPortPrivate::isWriteNotificationEnabled() const
1037{
1038 return writeNotifier && writeNotifier->isEnabled();
1039}
1040
1041void QSerialPortPrivate::setWriteNotificationEnabled(bool enable)
1042{
1043 Q_Q(QSerialPort);
1044
1045 if (writeNotifier) {
1046 writeNotifier->setEnabled(enable);
1047 } else if (enable) {
1048 writeNotifier = new WriteNotifier(this, q);
1049 writeNotifier->setEnabled(true);
1050 }
1051}
1052
1053bool QSerialPortPrivate::waitForReadOrWrite(bool *selectForRead, bool *selectForWrite,
1054 bool checkRead, bool checkWrite,
1055 int msecs)
1056{
1057 Q_ASSERT(selectForRead);
1058 Q_ASSERT(selectForWrite);
1059
1060 pollfd pfd = qt_make_pollfd(fd: descriptor, events: 0);
1061
1062 if (checkRead)
1063 pfd.events |= POLLIN;
1064
1065 if (checkWrite)
1066 pfd.events |= POLLOUT;
1067
1068 const int ret = qt_safe_poll(fds: &pfd, nfds: 1, deadline: QDeadlineTimer(msecs));
1069 if (ret < 0) {
1070 setError(getSystemError());
1071 return false;
1072 }
1073 if (ret == 0) {
1074 setError(QSerialPortErrorInfo(QSerialPort::TimeoutError));
1075 return false;
1076 }
1077 if (pfd.revents & POLLNVAL) {
1078 setError(getSystemError(EBADF));
1079 return false;
1080 }
1081
1082 *selectForWrite = ((pfd.revents & POLLOUT) != 0);
1083 *selectForRead = ((pfd.revents & POLLIN) != 0);
1084 return true;
1085}
1086
1087qint64 QSerialPortPrivate::readFromPort(char *data, qint64 maxSize)
1088{
1089 return qt_safe_read(fd: descriptor, data, maxlen: maxSize);
1090}
1091
1092qint64 QSerialPortPrivate::writeToPort(const char *data, qint64 maxSize)
1093{
1094 qint64 bytesWritten = 0;
1095#if defined(CMSPAR)
1096 bytesWritten = qt_safe_write(fd: descriptor, data, len: maxSize);
1097#else
1098 if (parity != QSerialPort::MarkParity
1099 && parity != QSerialPort::SpaceParity) {
1100 bytesWritten = qt_safe_write(descriptor, data, maxSize);
1101 } else {// Perform parity emulation.
1102 bytesWritten = writePerChar(data, maxSize);
1103 }
1104#endif
1105
1106 return bytesWritten;
1107}
1108
1109#ifndef CMSPAR
1110
1111static inline bool evenParity(quint8 c)
1112{
1113 c ^= c >> 4; //(c7 ^ c3)(c6 ^ c2)(c5 ^ c1)(c4 ^ c0)
1114 c ^= c >> 2; //[(c7 ^ c3)(c5 ^ c1)][(c6 ^ c2)(c4 ^ c0)]
1115 c ^= c >> 1;
1116 return c & 1; //(c7 ^ c3)(c5 ^ c1)(c6 ^ c2)(c4 ^ c0)
1117}
1118
1119qint64 QSerialPortPrivate::writePerChar(const char *data, qint64 maxSize)
1120{
1121 termios tio;
1122 if (!getTermios(&tio))
1123 return -1;
1124
1125 qint64 ret = 0;
1126 quint8 const charMask = (0xFF >> (8 - dataBits));
1127
1128 while (ret < maxSize) {
1129
1130 bool par = evenParity(*data & charMask);
1131 // False if need EVEN, true if need ODD.
1132 par ^= parity == QSerialPort::MarkParity;
1133 if (par ^ (tio.c_cflag & PARODD)) { // Need switch parity mode?
1134 tio.c_cflag ^= PARODD;
1135 //force sending already buffered data, because setTermios(&tio); cleares buffers
1136 if (::tcdrain(descriptor) == -1) {
1137 setError(getSystemError());
1138 break;
1139 }
1140 //todo: add receiving buffered data!!!
1141 if (!setTermios(&tio))
1142 break;
1143 }
1144
1145 int r = qt_safe_write(descriptor, data, 1);
1146 if (r < 0)
1147 return -1;
1148 if (r > 0) {
1149 data += r;
1150 ret += r;
1151 }
1152 }
1153 return ret;
1154}
1155
1156#endif //CMSPAR
1157
1158typedef QMap<qint32, qint32> BaudRateMap;
1159
1160// The OS specific defines can be found in termios.h
1161
1162static const BaudRateMap createStandardBaudRateMap()
1163{
1164 BaudRateMap baudRateMap;
1165
1166#ifdef B50
1167 baudRateMap.insert(key: 50, B50);
1168#endif
1169
1170#ifdef B75
1171 baudRateMap.insert(key: 75, B75);
1172#endif
1173
1174#ifdef B110
1175 baudRateMap.insert(key: 110, B110);
1176#endif
1177
1178#ifdef B134
1179 baudRateMap.insert(key: 134, B134);
1180#endif
1181
1182#ifdef B150
1183 baudRateMap.insert(key: 150, B150);
1184#endif
1185
1186#ifdef B200
1187 baudRateMap.insert(key: 200, B200);
1188#endif
1189
1190#ifdef B300
1191 baudRateMap.insert(key: 300, B300);
1192#endif
1193
1194#ifdef B600
1195 baudRateMap.insert(key: 600, B600);
1196#endif
1197
1198#ifdef B1200
1199 baudRateMap.insert(key: 1200, B1200);
1200#endif
1201
1202#ifdef B1800
1203 baudRateMap.insert(key: 1800, B1800);
1204#endif
1205
1206#ifdef B2400
1207 baudRateMap.insert(key: 2400, B2400);
1208#endif
1209
1210#ifdef B4800
1211 baudRateMap.insert(key: 4800, B4800);
1212#endif
1213
1214#ifdef B7200
1215 baudRateMap.insert(7200, B7200);
1216#endif
1217
1218#ifdef B9600
1219 baudRateMap.insert(key: 9600, B9600);
1220#endif
1221
1222#ifdef B14400
1223 baudRateMap.insert(14400, B14400);
1224#endif
1225
1226#ifdef B19200
1227 baudRateMap.insert(key: 19200, B19200);
1228#endif
1229
1230#ifdef B28800
1231 baudRateMap.insert(28800, B28800);
1232#endif
1233
1234#ifdef B38400
1235 baudRateMap.insert(key: 38400, B38400);
1236#endif
1237
1238#ifdef B57600
1239 baudRateMap.insert(key: 57600, B57600);
1240#endif
1241
1242#ifdef B76800
1243 baudRateMap.insert(76800, B76800);
1244#endif
1245
1246#ifdef B115200
1247 baudRateMap.insert(key: 115200, B115200);
1248#endif
1249
1250#ifdef B230400
1251 baudRateMap.insert(key: 230400, B230400);
1252#endif
1253
1254#ifdef B460800
1255 baudRateMap.insert(key: 460800, B460800);
1256#endif
1257
1258#ifdef B500000
1259 baudRateMap.insert(key: 500000, B500000);
1260#endif
1261
1262#ifdef B576000
1263 baudRateMap.insert(key: 576000, B576000);
1264#endif
1265
1266#ifdef B921600
1267 baudRateMap.insert(key: 921600, B921600);
1268#endif
1269
1270#ifdef B1000000
1271 baudRateMap.insert(key: 1000000, B1000000);
1272#endif
1273
1274#ifdef B1152000
1275 baudRateMap.insert(key: 1152000, B1152000);
1276#endif
1277
1278#ifdef B1500000
1279 baudRateMap.insert(key: 1500000, B1500000);
1280#endif
1281
1282#ifdef B2000000
1283 baudRateMap.insert(key: 2000000, B2000000);
1284#endif
1285
1286#ifdef B2500000
1287 baudRateMap.insert(key: 2500000, B2500000);
1288#endif
1289
1290#ifdef B3000000
1291 baudRateMap.insert(key: 3000000, B3000000);
1292#endif
1293
1294#ifdef B3500000
1295 baudRateMap.insert(key: 3500000, B3500000);
1296#endif
1297
1298#ifdef B4000000
1299 baudRateMap.insert(key: 4000000, B4000000);
1300#endif
1301
1302 return baudRateMap;
1303}
1304
1305static const BaudRateMap& standardBaudRateMap()
1306{
1307 static const BaudRateMap baudRateMap = createStandardBaudRateMap();
1308 return baudRateMap;
1309}
1310
1311qint32 QSerialPortPrivate::settingFromBaudRate(qint32 baudRate)
1312{
1313 return standardBaudRateMap().value(key: baudRate);
1314}
1315
1316QList<qint32> QSerialPortPrivate::standardBaudRates()
1317{
1318 return standardBaudRateMap().keys();
1319}
1320
1321QSerialPort::Handle QSerialPort::handle() const
1322{
1323 Q_D(const QSerialPort);
1324 return d->descriptor;
1325}
1326
1327QT_END_NAMESPACE
1328

source code of qtserialport/src/serialport/qserialport_unix.cpp