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) |
41 | struct 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 | |
67 | QT_BEGIN_NAMESPACE |
68 | |
69 | QString 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 | |
113 | class ReadNotifier : public QSocketNotifier |
114 | { |
115 | public: |
116 | explicit ReadNotifier(QSerialPortPrivate *d, QObject *parent) |
117 | : QSocketNotifier(d->descriptor, QSocketNotifier::Read, parent) |
118 | , dptr(d) |
119 | { |
120 | } |
121 | |
122 | protected: |
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 | |
132 | private: |
133 | QSerialPortPrivate * const dptr; |
134 | }; |
135 | |
136 | class WriteNotifier : public QSocketNotifier |
137 | { |
138 | public: |
139 | explicit WriteNotifier(QSerialPortPrivate *d, QObject *parent) |
140 | : QSocketNotifier(d->descriptor, QSocketNotifier::Write, parent) |
141 | , dptr(d) |
142 | { |
143 | } |
144 | |
145 | protected: |
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 | |
155 | private: |
156 | QSerialPortPrivate * const dptr; |
157 | }; |
158 | |
159 | static 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 | |
179 | static 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 | |
201 | static 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 | |
236 | static 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 | |
251 | static 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 | |
273 | bool 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 | |
321 | void 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 | |
345 | QSerialPort::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 | |
402 | bool 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 | |
413 | bool 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 | |
424 | bool QSerialPortPrivate::flush() |
425 | { |
426 | return completeAsyncWrite(); |
427 | } |
428 | |
429 | bool 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 | |
440 | bool 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 | |
450 | bool 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 | |
460 | bool 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 | |
482 | bool 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 | |
508 | bool 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 | |
517 | bool 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 | |
562 | bool 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 | |
619 | bool 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 | |
643 | bool 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 | |
654 | bool 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 | |
666 | bool 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 | |
680 | bool 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 | |
691 | bool 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 | |
702 | bool 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 | |
713 | bool 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 | |
724 | bool QSerialPortPrivate::startAsyncRead() |
725 | { |
726 | setReadNotificationEnabled(true); |
727 | return true; |
728 | } |
729 | |
730 | bool 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 | |
777 | bool 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 | |
801 | bool 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 | |
824 | inline 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 | |
855 | qint64 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 | |
863 | bool 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 | |
872 | bool 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 | |
882 | QSerialPortErrorInfo 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 | |
946 | bool QSerialPortPrivate::isReadNotificationEnabled() const |
947 | { |
948 | return readNotifier && readNotifier->isEnabled(); |
949 | } |
950 | |
951 | void 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 | |
963 | bool QSerialPortPrivate::isWriteNotificationEnabled() const |
964 | { |
965 | return writeNotifier && writeNotifier->isEnabled(); |
966 | } |
967 | |
968 | void 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 | |
980 | bool 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 | |
1014 | qint64 QSerialPortPrivate::readFromPort(char *data, qint64 maxSize) |
1015 | { |
1016 | return qt_safe_read(fd: descriptor, data, maxlen: maxSize); |
1017 | } |
1018 | |
1019 | qint64 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 | |
1038 | static 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 | |
1046 | qint64 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 | |
1081 | typedef QMap<qint32, qint32> BaudRateMap; |
1082 | |
1083 | // The OS specific defines can be found in termios.h |
1084 | |
1085 | static 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 | |
1228 | static const BaudRateMap& standardBaudRateMap() |
1229 | { |
1230 | static const BaudRateMap baudRateMap = createStandardBaudRateMap(); |
1231 | return baudRateMap; |
1232 | } |
1233 | |
1234 | qint32 QSerialPortPrivate::settingFromBaudRate(qint32 baudRate) |
1235 | { |
1236 | return standardBaudRateMap().value(key: baudRate); |
1237 | } |
1238 | |
1239 | QList<qint32> QSerialPortPrivate::standardBaudRates() |
1240 | { |
1241 | return standardBaudRateMap().keys(); |
1242 | } |
1243 | |
1244 | QSerialPort::Handle QSerialPort::handle() const |
1245 | { |
1246 | Q_D(const QSerialPort); |
1247 | return d->descriptor; |
1248 | } |
1249 | |
1250 | QT_END_NAMESPACE |
1251 | |