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);
109 const QSerialPort::SerialPortError openError = (errno == ENOENT) ? QSerialPort::DeviceNotFoundError
110 : (errno == EACCES) ? QSerialPort::PermissionError
111 : QSerialPort::OpenError;
112 setError(QSerialPortErrorInfo(openError, 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
238qint64 QSerialPortPrivate::_posixWriteWithProgress(const char* data, qint64 maxSize, int timeoutMs)
239{
240 qint64 totalWritten = 0;
241 // Match the USB backend: zero waits without limit.
242 const QDeadlineTimer deadline =
243 timeoutMs == 0 ? QDeadlineTimer(QDeadlineTimer::Forever) : QDeadlineTimer(timeoutMs);
244
245 while (totalWritten < maxSize) {
246 const ssize_t written = ::write(descriptor, data + totalWritten, static_cast<size_t>(maxSize - totalWritten));
247 if (written > 0) {
248 totalWritten += written;
249 continue;
250 }
251
252 if ((written < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR)) {
253 qCWarning(AndroidSerialPortLog) << "Write error on" << systemLocation << ":" << strerror(errno);
254 // Report bytes already sent; the next write reports the error.
255 return totalWritten > 0 ? totalWritten : -1;
256 }
257
258 if (deadline.hasExpired()) {
259 // A short count is a timeout, not an error.
260 qCDebug(AndroidSerialPortLog) << "Write timeout on" << systemLocation << "after" << totalWritten << "bytes";
261 return totalWritten;
262 }
263
264 struct pollfd pfd = {descriptor, POLLOUT, 0};
265 const int pollTimeout = deadline.isForever() ? -1 : static_cast<int>(qMax<qint64>(0, deadline.remainingTime()));
266 (void) ::poll(&pfd, 1, pollTimeout);
267 }
268
269 return totalWritten;
270}
271
272bool QSerialPortPrivate::_posixApplyPortSettings(qint32 baudRate, QSerialPort::DataBits dataBits_,
274 QSerialPort::FlowControl flowControl_)
275{
276 struct termios tio;
277 if (::tcgetattr(descriptor, &tio) == -1) {
278 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QString::fromLocal8Bit(strerror(errno))));
279 return false;
280 }
281
282 ::cfmakeraw(&tio);
283 tio.c_cflag |= (CLOCAL | CREAD);
284 tio.c_cc[VMIN] = 0;
285 tio.c_cc[VTIME] = 0;
286
287 const speed_t speed = baudRateToSpeed(baudRate);
288 if (speed == B0) {
289 qCWarning(AndroidSerialPortLog) << "Unsupported baud rate:" << baudRate;
290 setError(
291 QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, QSerialPort::tr("Unsupported baud rate")));
292 return false;
293 }
294 (void) ::cfsetispeed(&tio, speed);
295 (void) ::cfsetospeed(&tio, speed);
296
297 tio.c_cflag &= ~CSIZE;
298 switch (dataBits_) {
300 tio.c_cflag |= CS5;
301 break;
303 tio.c_cflag |= CS6;
304 break;
306 tio.c_cflag |= CS7;
307 break;
309 default:
310 tio.c_cflag |= CS8;
311 break;
312 }
313
314 switch (parity_) {
316 tio.c_cflag &= ~PARENB;
317 break;
319 tio.c_cflag |= PARENB;
320 tio.c_cflag &= ~PARODD;
321 break;
323 tio.c_cflag |= (PARENB | PARODD);
324 break;
325 default:
326 qCWarning(AndroidSerialPortLog) << "Unsupported parity:" << parity_;
327 setError(
328 QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, QSerialPort::tr("Unsupported parity")));
329 return false;
330 }
331
332 switch (stopBits_) {
334 tio.c_cflag &= ~CSTOPB;
335 break;
337 tio.c_cflag |= CSTOPB;
338 break;
339 default:
340 qCWarning(AndroidSerialPortLog) << "Unsupported stop bits:" << stopBits_;
341 setError(
342 QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, QSerialPort::tr("Unsupported stop bits")));
343 return false;
344 }
345
346 switch (flowControl_) {
348 tio.c_cflag &= ~CRTSCTS;
349 tio.c_iflag &= ~(IXON | IXOFF);
350 break;
352 tio.c_cflag |= CRTSCTS;
353 tio.c_iflag &= ~(IXON | IXOFF);
354 break;
356 tio.c_cflag &= ~CRTSCTS;
357 tio.c_iflag |= (IXON | IXOFF);
358 break;
359 default:
360 qCWarning(AndroidSerialPortLog) << "Unsupported flow control:" << flowControl_;
362 QSerialPort::tr("Unsupported flow control")));
363 return false;
364 }
365
366 if (::tcsetattr(descriptor, TCSANOW, &tio) == -1) {
367 qCWarning(AndroidSerialPortLog) << "tcsetattr failed on" << systemLocation << ":" << strerror(errno);
368 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QString::fromLocal8Bit(strerror(errno))));
369 return false;
370 }
371
372 return true;
373}
374
375bool QSerialPortPrivate::_posixClear(QSerialPort::Directions directions)
376{
377 int queues = 0;
378 if (directions == QSerialPort::AllDirections) {
379 queues = TCIOFLUSH;
380 } else if (directions & QSerialPort::Input) {
381 queues = TCIFLUSH;
382 } else if (directions & QSerialPort::Output) {
383 queues = TCOFLUSH;
384 } else {
385 return true;
386 }
387
388 if (::tcflush(descriptor, queues) == -1) {
389 qCWarning(AndroidSerialPortLog) << "tcflush failed on" << systemLocation << ":" << strerror(errno);
390 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QString::fromLocal8Bit(strerror(errno))));
391 return false;
392 }
393
394 return true;
395}
396
397QSerialPort::PinoutSignals QSerialPortPrivate::_posixPinoutSignals()
398{
399 int arg = 0;
400 if (::ioctl(descriptor, TIOCMGET, &arg) == -1) {
401 qCWarning(AndroidSerialPortLog) << "TIOCMGET failed on" << systemLocation << ":" << strerror(errno);
402 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QString::fromLocal8Bit(strerror(errno))));
404 }
405
406 QSerialPort::PinoutSignals signals_ = QSerialPort::NoSignal;
407 if (arg & TIOCM_DTR) {
409 }
410 if (arg & TIOCM_RTS) {
412 }
413 if (arg & TIOCM_CTS) {
415 }
416 if (arg & TIOCM_DSR) {
418 }
419 if (arg & TIOCM_CAR) {
421 }
422 if (arg & TIOCM_RNG) {
424 }
425 if (arg & TIOCM_ST) {
427 }
428 if (arg & TIOCM_SR) {
430 }
431
432 return signals_;
433}
434
435static bool setControlLine(int descriptor, int line, bool set)
436{
437 return (::ioctl(descriptor, set ? TIOCMBIS : TIOCMBIC, &line) != -1);
438}
439
440bool QSerialPortPrivate::_posixSetDataTerminalReady(bool set)
441{
442 if (!setControlLine(descriptor, TIOCM_DTR, set)) {
443 const int dtrError = errno;
444 const auto code = dtrError == ENOTTY || dtrError == EOPNOTSUPP ? QSerialPort::UnsupportedOperationError
446 qCDebug(AndroidSerialPortLog) << "Failed to set DTR on" << systemLocation << ":" << strerror(dtrError);
447 setError(QSerialPortErrorInfo(code, QSerialPort::tr("Failed to set DTR")));
448 return false;
449 }
450
451 return true;
452}
453
454bool QSerialPortPrivate::_posixSetRequestToSend(bool set)
455{
456 if (!setControlLine(descriptor, TIOCM_RTS, set)) {
457 const int rtsError = errno;
458 const auto code = rtsError == ENOTTY || rtsError == EOPNOTSUPP ? QSerialPort::UnsupportedOperationError
460 qCDebug(AndroidSerialPortLog) << "Failed to set RTS on" << systemLocation << ":" << strerror(rtsError);
461 setError(QSerialPortErrorInfo(code, QSerialPort::tr("Failed to set RTS")));
462 return false;
463 }
464
465 return true;
466}
467
468bool QSerialPortPrivate::_posixSetBreakEnabled(bool set)
469{
470 if (::ioctl(descriptor, set ? TIOCSBRK : TIOCCBRK) == -1) {
471 qCWarning(AndroidSerialPortLog) << "Failed to set break on" << systemLocation << ":" << strerror(errno);
472 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set Break Enabled")));
473 return false;
474 }
475
476 return true;
477}
478
479bool QSerialPortPrivate::_posixWaitForReadyRead(int msecs)
480{
481 {
482 QMutexLocker locker(&_readMutex);
483 if (!buffer.isEmpty()) {
484 return true;
485 }
486 if (_pendingSizeLocked() > 0) {
487 (void) _drainPendingDataLocked();
488 return true;
489 }
490 }
491
492 QDeadlineTimer deadline(msecs);
493 for (;;) {
494 struct pollfd pfd = {descriptor, POLLIN, 0};
495 const int pollTimeout = deadline.isForever() ? -1 : static_cast<int>(qMax<qint64>(0, deadline.remainingTime()));
496 const int ret = ::poll(&pfd, 1, pollTimeout);
497 if (ret < 0) {
498 if (errno == EINTR) {
499 continue;
500 }
501 qCWarning(AndroidSerialPortLog) << "Poll error on" << systemLocation << ":" << strerror(errno);
502 setError(QSerialPortErrorInfo(QSerialPort::ReadError, QString::fromLocal8Bit(strerror(errno))));
503 return false;
504 }
505
506 if (ret > 0) {
507 // Attempt the read on any wakeup (POLLIN/POLLERR/POLLHUP); a failed read
508 // surfaces the real error via _posixReadActivated.
509 _posixReadActivated();
510
511 {
512 QMutexLocker locker(&_readMutex);
513 if (_pendingSizeLocked() > 0) {
514 (void) _drainPendingDataLocked();
515 }
516 if (!buffer.isEmpty()) {
517 return true;
518 }
519 }
520
521 // A read error was reported with no data delivered: surface it instead
522 // of looping until the deadline stamps TimeoutError over it.
524 return false;
525 }
526
527 // No data delivered on an error/hangup wakeup: bail out rather than
528 // busy-spinning on a dead descriptor until the deadline.
529 if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
531 setError(QSerialPortErrorInfo(QSerialPort::ResourceError, QSerialPort::tr("Device disconnected")));
532 }
533 return false;
534 }
535 }
536
537 if (deadline.hasExpired()) {
538 break;
539 }
540 }
541
542 setError(QSerialPortErrorInfo(QSerialPort::TimeoutError, QSerialPort::tr("Timeout while waiting for ready read")));
543 return false;
544}
545
546QT_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)