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

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