QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
qserialport_posix.cpp
Go to the documentation of this file.
1// POSIX serial backend for Android devices which expose accessible /dev/tty* nodes
2// (e.g. rooted devices with built-in serial ports). Selected at startup via
3// AndroidSerial::setUsePosixSerial().
4
5#include <QtCore/QDeadlineTimer>
6#include <QtCore/QSocketNotifier>
7#include <cerrno>
8#include <cstring>
9#include <fcntl.h>
10#include <poll.h>
11#include <sys/ioctl.h>
12#include <termios.h>
13#include <unistd.h>
14
15#include "qserialport_p.h"
16
18
19namespace {
20
21speed_t baudRateToSpeed(qint32 baudRate)
22{
23 switch (baudRate) {
24 case 50:
25 return B50;
26 case 75:
27 return B75;
28 case 110:
29 return B110;
30 case 134:
31 return B134;
32 case 150:
33 return B150;
34 case 200:
35 return B200;
36 case 300:
37 return B300;
38 case 600:
39 return B600;
40 case 1200:
41 return B1200;
42 case 1800:
43 return B1800;
44 case 2400:
45 return B2400;
46 case 4800:
47 return B4800;
48 case 9600:
49 return B9600;
50 case 19200:
51 return B19200;
52 case 38400:
53 return B38400;
54 case 57600:
55 return B57600;
56 case 115200:
57 return B115200;
58 case 230400:
59 return B230400;
60 case 460800:
61 return B460800;
62 case 500000:
63 return B500000;
64 case 576000:
65 return B576000;
66 case 921600:
67 return B921600;
68 case 1000000:
69 return B1000000;
70 case 1152000:
71 return B1152000;
72 case 1500000:
73 return B1500000;
74 case 2000000:
75 return B2000000;
76 case 2500000:
77 return B2500000;
78 case 3000000:
79 return B3000000;
80 case 3500000:
81 return B3500000;
82 case 4000000:
83 return B4000000;
84 default:
85 return B0;
86 }
87}
88
89} // namespace
90
91bool QSerialPortPrivate::_posixOpen(QIODevice::OpenMode mode)
92{
93 Q_Q(QSerialPort);
94
95 qCDebug(AndroidSerialPortLog) << "Opening (POSIX)" << systemLocation;
96
97 int flags = O_NOCTTY | O_NONBLOCK;
98 if ((mode & QIODevice::ReadWrite) == QIODevice::ReadWrite) {
99 flags |= O_RDWR;
100 } else if (mode & QIODevice::WriteOnly) {
101 flags |= O_WRONLY;
102 } else {
103 flags |= O_RDONLY;
104 }
105
106 descriptor = ::open(systemLocation.toLocal8Bit().constData(), flags);
107 if (descriptor == -1) {
108 qCWarning(AndroidSerialPortLog) << "Error opening" << systemLocation << ":" << strerror(errno);
110 : (errno == EACCES) ? QSerialPort::PermissionError
111 : QSerialPort::OpenError;
112 setError(QSerialPortErrorInfo(error, QString::fromLocal8Bit(strerror(errno))));
113 return false;
114 }
115
116 if (::isatty(descriptor) == 0) {
117 qCWarning(AndroidSerialPortLog) << systemLocation << "is not a tty device";
118 setError(QSerialPortErrorInfo(QSerialPort::OpenError, QSerialPort::tr("Not a tty device")));
119 _posixClose();
120 return false;
121 }
122
123 // Request exclusive access. Failure is not fatal.
124 if (::ioctl(descriptor, TIOCEXCL) == -1) {
125 qCDebug(AndroidSerialPortLog) << "Failed to set exclusive access on" << systemLocation << ":"
126 << strerror(errno);
127 }
128
129 if (!_posixApplyPortSettings(inputBaudRate, dataBits, stopBits, parity, flowControl)) {
130 qCWarning(AndroidSerialPortLog) << "Failed to set serial port parameters for" << systemLocation;
131 _posixClose();
132 return false;
133 }
134
135 (void) _posixClear(QSerialPort::AllDirections);
136
137 if (mode & QIODevice::ReadOnly) {
138 _posixReadNotifier = new QSocketNotifier(descriptor, QSocketNotifier::Read, q);
139 QObject::connect(_posixReadNotifier, &QSocketNotifier::activated, q, [this]() { _posixReadActivated(); });
140 }
141
142 return true;
143}
144
145void QSerialPortPrivate::_posixClose()
146{
147 qCDebug(AndroidSerialPortLog) << "Closing (POSIX)" << systemLocation;
148
149 delete _posixReadNotifier;
150 _posixReadNotifier = nullptr;
151
152 if (descriptor != -1) {
153 if (::close(descriptor) == -1) {
154 qCWarning(AndroidSerialPortLog) << "Failed to close" << systemLocation << ":" << strerror(errno);
155 }
156 descriptor = -1;
157 }
158}
159
160bool QSerialPortPrivate::_posixStartAsyncRead()
161{
162 if (_posixReadNotifier) {
163 _posixReadNotifier->setEnabled(true);
164 }
165
166 // Deliver any data still pending in the intermediate buffer.
167 _scheduleReadyRead();
168
169 return true;
170}
171
172bool QSerialPortPrivate::_posixStopAsyncRead()
173{
174 if (_posixReadNotifier) {
175 _posixReadNotifier->setEnabled(false);
176 }
177
178 return true;
179}
180
181void QSerialPortPrivate::_posixReadActivated()
182{
183 char readBuf[4096];
184
185 for (;;) {
186 const ssize_t bytesRead = ::read(descriptor, readBuf, sizeof(readBuf));
187 if (bytesRead > 0) {
188 newDataArrived(readBuf, static_cast<int>(bytesRead));
189 if (bytesRead < static_cast<ssize_t>(sizeof(readBuf))) {
190 break;
191 }
192 continue;
193 }
194
195 if ((bytesRead < 0) && (errno == EINTR)) {
196 continue;
197 }
198
199 if ((bytesRead < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK)) {
200 qCWarning(AndroidSerialPortLog) << "Read error on" << systemLocation << ":" << strerror(errno);
201 setError(QSerialPortErrorInfo(QSerialPort::ReadError, QString::fromLocal8Bit(strerror(errno))));
202 }
203
204 break;
205 }
206}
207
208qint64 QSerialPortPrivate::_posixWrite(const char* data, qint64 maxSize, int timeoutMs)
209{
210 qint64 totalWritten = 0;
211 QDeadlineTimer deadline(timeoutMs);
212
213 while (totalWritten < maxSize) {
214 const ssize_t written = ::write(descriptor, data + totalWritten, static_cast<size_t>(maxSize - totalWritten));
215 if (written > 0) {
216 totalWritten += written;
217 continue;
218 }
219
220 if ((written < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR)) {
221 qCWarning(AndroidSerialPortLog) << "Write error on" << systemLocation << ":" << strerror(errno);
222 return -1;
223 }
224
225 if (deadline.hasExpired()) {
226 qCWarning(AndroidSerialPortLog) << "Write timeout on" << systemLocation;
227 return -1;
228 }
229
230 struct pollfd pfd = {descriptor, POLLOUT, 0};
231 const int pollTimeout = deadline.isForever() ? -1 : static_cast<int>(qMax<qint64>(0, deadline.remainingTime()));
232 (void) ::poll(&pfd, 1, pollTimeout);
233 }
234
235 return totalWritten;
236}
237
238bool QSerialPortPrivate::_posixApplyPortSettings(qint32 baudRate, QSerialPort::DataBits dataBits_,
240 QSerialPort::FlowControl flowControl_)
241{
242 struct termios tio;
243 if (::tcgetattr(descriptor, &tio) == -1) {
244 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QString::fromLocal8Bit(strerror(errno))));
245 return false;
246 }
247
248 ::cfmakeraw(&tio);
249 tio.c_cflag |= (CLOCAL | CREAD);
250 tio.c_cc[VMIN] = 0;
251 tio.c_cc[VTIME] = 0;
252
253 const speed_t speed = baudRateToSpeed(baudRate);
254 if (speed == B0) {
255 qCWarning(AndroidSerialPortLog) << "Unsupported baud rate:" << baudRate;
256 setError(
257 QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, QSerialPort::tr("Unsupported baud rate")));
258 return false;
259 }
260 (void) ::cfsetispeed(&tio, speed);
261 (void) ::cfsetospeed(&tio, speed);
262
263 tio.c_cflag &= ~CSIZE;
264 switch (dataBits_) {
266 tio.c_cflag |= CS5;
267 break;
269 tio.c_cflag |= CS6;
270 break;
272 tio.c_cflag |= CS7;
273 break;
275 default:
276 tio.c_cflag |= CS8;
277 break;
278 }
279
280 switch (parity_) {
282 tio.c_cflag &= ~PARENB;
283 break;
285 tio.c_cflag |= PARENB;
286 tio.c_cflag &= ~PARODD;
287 break;
289 tio.c_cflag |= (PARENB | PARODD);
290 break;
291 default:
292 qCWarning(AndroidSerialPortLog) << "Unsupported parity:" << parity_;
293 setError(
294 QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, QSerialPort::tr("Unsupported parity")));
295 return false;
296 }
297
298 switch (stopBits_) {
300 tio.c_cflag &= ~CSTOPB;
301 break;
303 tio.c_cflag |= CSTOPB;
304 break;
305 default:
306 qCWarning(AndroidSerialPortLog) << "Unsupported stop bits:" << stopBits_;
307 setError(
308 QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, QSerialPort::tr("Unsupported stop bits")));
309 return false;
310 }
311
312 switch (flowControl_) {
314 tio.c_cflag &= ~CRTSCTS;
315 tio.c_iflag &= ~(IXON | IXOFF);
316 break;
318 tio.c_cflag |= CRTSCTS;
319 tio.c_iflag &= ~(IXON | IXOFF);
320 break;
322 tio.c_cflag &= ~CRTSCTS;
323 tio.c_iflag |= (IXON | IXOFF);
324 break;
325 default:
326 qCWarning(AndroidSerialPortLog) << "Unsupported flow control:" << flowControl_;
328 QSerialPort::tr("Unsupported flow control")));
329 return false;
330 }
331
332 if (::tcsetattr(descriptor, TCSANOW, &tio) == -1) {
333 qCWarning(AndroidSerialPortLog) << "tcsetattr failed on" << systemLocation << ":" << strerror(errno);
334 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QString::fromLocal8Bit(strerror(errno))));
335 return false;
336 }
337
338 return true;
339}
340
341bool QSerialPortPrivate::_posixClear(QSerialPort::Directions directions)
342{
343 int queues = 0;
344 if (directions == QSerialPort::AllDirections) {
345 queues = TCIOFLUSH;
346 } else if (directions & QSerialPort::Input) {
347 queues = TCIFLUSH;
348 } else if (directions & QSerialPort::Output) {
349 queues = TCOFLUSH;
350 } else {
351 return true;
352 }
353
354 if (::tcflush(descriptor, queues) == -1) {
355 qCWarning(AndroidSerialPortLog) << "tcflush failed on" << systemLocation << ":" << strerror(errno);
356 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QString::fromLocal8Bit(strerror(errno))));
357 return false;
358 }
359
360 return true;
361}
362
363QSerialPort::PinoutSignals QSerialPortPrivate::_posixPinoutSignals()
364{
365 int arg = 0;
366 if (::ioctl(descriptor, TIOCMGET, &arg) == -1) {
367 qCWarning(AndroidSerialPortLog) << "TIOCMGET failed on" << systemLocation << ":" << strerror(errno);
368 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QString::fromLocal8Bit(strerror(errno))));
370 }
371
372 QSerialPort::PinoutSignals signals_ = QSerialPort::NoSignal;
373 if (arg & TIOCM_DTR) {
375 }
376 if (arg & TIOCM_RTS) {
378 }
379 if (arg & TIOCM_CTS) {
381 }
382 if (arg & TIOCM_DSR) {
384 }
385 if (arg & TIOCM_CAR) {
387 }
388 if (arg & TIOCM_RNG) {
390 }
391 if (arg & TIOCM_ST) {
393 }
394 if (arg & TIOCM_SR) {
396 }
397
398 return signals_;
399}
400
401static bool setControlLine(int descriptor, int line, bool set)
402{
403 return (::ioctl(descriptor, set ? TIOCMBIS : TIOCMBIC, &line) != -1);
404}
405
406bool QSerialPortPrivate::_posixSetDataTerminalReady(bool set)
407{
408 if (!setControlLine(descriptor, TIOCM_DTR, set)) {
409 qCWarning(AndroidSerialPortLog) << "Failed to set DTR on" << systemLocation << ":" << strerror(errno);
410 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set DTR")));
411 return false;
412 }
413
414 return true;
415}
416
417bool QSerialPortPrivate::_posixSetRequestToSend(bool set)
418{
419 if (!setControlLine(descriptor, TIOCM_RTS, set)) {
420 qCWarning(AndroidSerialPortLog) << "Failed to set RTS on" << systemLocation << ":" << strerror(errno);
421 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set RTS")));
422 return false;
423 }
424
425 return true;
426}
427
428bool QSerialPortPrivate::_posixSetBreakEnabled(bool set)
429{
430 if (::ioctl(descriptor, set ? TIOCSBRK : TIOCCBRK) == -1) {
431 qCWarning(AndroidSerialPortLog) << "Failed to set break on" << systemLocation << ":" << strerror(errno);
432 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set Break Enabled")));
433 return false;
434 }
435
436 return true;
437}
438
439bool QSerialPortPrivate::_posixWaitForReadyRead(int msecs)
440{
441 {
442 QMutexLocker locker(&_readMutex);
443 if (!buffer.isEmpty()) {
444 return true;
445 }
446 if (_pendingSizeLocked() > 0) {
447 (void) _drainPendingDataLocked();
448 return true;
449 }
450 }
451
452 QDeadlineTimer deadline(msecs);
453 for (;;) {
454 struct pollfd pfd = {descriptor, POLLIN, 0};
455 const int pollTimeout = deadline.isForever() ? -1 : static_cast<int>(qMax<qint64>(0, deadline.remainingTime()));
456 const int ret = ::poll(&pfd, 1, pollTimeout);
457 if (ret < 0) {
458 if (errno == EINTR) {
459 continue;
460 }
461 qCWarning(AndroidSerialPortLog) << "Poll error on" << systemLocation << ":" << strerror(errno);
462 setError(QSerialPortErrorInfo(QSerialPort::ReadError, QString::fromLocal8Bit(strerror(errno))));
463 return false;
464 }
465
466 if (ret > 0) {
467 // Attempt the read on any wakeup (POLLIN/POLLERR/POLLHUP); a failed read
468 // surfaces the real error via _posixReadActivated.
469 _posixReadActivated();
470
471 {
472 QMutexLocker locker(&_readMutex);
473 if (_pendingSizeLocked() > 0) {
474 (void) _drainPendingDataLocked();
475 }
476 if (!buffer.isEmpty()) {
477 return true;
478 }
479 }
480
481 // A read error was reported with no data delivered: surface it instead
482 // of looping until the deadline stamps TimeoutError over it.
484 return false;
485 }
486
487 // No data delivered on an error/hangup wakeup: bail out rather than
488 // busy-spinning on a dead descriptor until the deadline.
489 if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
491 setError(QSerialPortErrorInfo(QSerialPort::ResourceError, QSerialPort::tr("Device disconnected")));
492 }
493 return false;
494 }
495 }
496
497 if (deadline.hasExpired()) {
498 break;
499 }
500 }
501
502 setError(QSerialPortErrorInfo(QSerialPort::TimeoutError, QSerialPort::tr("Timeout while waiting for ready read")));
503 return false;
504}
505
506QT_END_NAMESPACE
Error error
void setError(const QSerialPortErrorInfo &errorInfo)
bool open(QIODevice::OpenMode mode)
void newDataArrived(const char *bytes, int length)
Provides functions to access serial ports.
Definition qserialport.h:17
DataBits
This enum describes the number of data bits used.
Definition qserialport.h:60
Parity
This enum describes the parity scheme used.
Definition qserialport.h:69
SerialPortError
This enum describes the errors that may be contained by the QSerialPort::error property.
@ UnsupportedOperationError
@ DataTerminalReadySignal
Definition qserialport.h:97
@ DataCarrierDetectSignal
Definition qserialport.h:98
@ SecondaryTransmittedDataSignal
@ SecondaryReceivedDataSignal
StopBits
This enum describes the number of stop bits used.
Definition qserialport.h:79
FlowControl
This enum describes the flow control used.
Definition qserialport.h:87
speed_t baudRateToSpeed(qint32 baudRate)
static bool setControlLine(int descriptor, int line, bool set)