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