QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
qserialport_android.cpp
Go to the documentation of this file.
1#include <QtCore/QMetaObject>
2#include <QtCore/QPointer>
3#include <QtCore/QScopeGuard>
4
5#include <iterator>
6
8#include "qserialport_p.h"
9
10QGC_LOGGING_CATEGORY(AndroidSerialPortLog, "Android.AndroidSerialPort")
11
13
14bool QSerialPortPrivate::open(QIODevice::OpenMode mode)
15{
17 return _posixOpen(mode);
18 }
19
20 qCDebug(AndroidSerialPortLog) << "Opening" << systemLocation;
21
23 auto tokenGuard = qScopeGuard([this]() { AndroidSerial::unregisterPointer(this); });
24
25 _deviceId = AndroidSerial::open(systemLocation, this);
26 if (_deviceId == INVALID_DEVICE_ID) {
27 qCWarning(AndroidSerialPortLog) << "Error opening" << systemLocation;
29 return false;
30 }
31
32 descriptor = AndroidSerial::getDeviceHandle(_deviceId);
33 if (descriptor == -1) {
34 qCWarning(AndroidSerialPortLog) << "Failed to get device handle for" << systemLocation;
36 close();
37 return false;
38 }
39
40 if (!_setParameters(inputBaudRate, dataBits, stopBits, parity)) {
41 qCWarning(AndroidSerialPortLog) << "Failed to set serial port parameters for" << systemLocation;
42 close();
43 return false;
44 }
45
46 if (!setFlowControl(flowControl)) {
47 qCWarning(AndroidSerialPortLog) << "Failed to set serial port flow control for" << systemLocation;
48 close();
49 return false;
50 }
51
52 if (mode & QIODevice::ReadOnly) {
53 if (!startAsyncRead()) {
54 qCWarning(AndroidSerialPortLog) << "Failed to start async read for" << systemLocation;
55 close();
56 return false;
57 }
58 } else if (mode & QIODevice::WriteOnly) {
59 if (!_stopAsyncRead()) {
60 qCWarning(AndroidSerialPortLog) << "Failed to stop async read for" << systemLocation;
61 }
62 }
63
64 (void)clear(QSerialPort::AllDirections);
65 tokenGuard.dismiss();
66
67 return true;
68}
69
71{
73 _posixClose();
74 return;
75 }
76
77 qCDebug(AndroidSerialPortLog) << "Closing" << systemLocation;
78
79 _stopAsyncRead();
80
81 {
82 // Drop any undelivered data so a queued drain or later re-open can't
83 // deliver stale bytes into cleared QIODevice buffers.
84 QMutexLocker locker(&_readMutex);
85 _pendingData.clear();
86 _pendingDataOffset = 0;
87 _readyReadPending.store(false);
88 _bufferBytesEstimate.store(0, std::memory_order_relaxed);
89 }
90
91 if (_deviceId != INVALID_DEVICE_ID) {
92 if (!AndroidSerial::close(_deviceId)) {
93 qCWarning(AndroidSerialPortLog) << "Failed to close device with ID" << _deviceId;
94 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Closing device failed")));
95 }
96 _deviceId = INVALID_DEVICE_ID;
97 }
98
99 descriptor = -1;
100
102}
103
105{
106 qCWarning(AndroidSerialPortLog) << "Exception arrived on device ID" << _deviceId << ":" << ex;
108}
109
111{
113 return _posixStartAsyncRead();
114 }
115
116 if (!AndroidSerial::readThreadRunning(_deviceId)) {
117 const bool result = AndroidSerial::startReadThread(_deviceId);
118 if (!result) {
119 qCWarning(AndroidSerialPortLog) << "Failed to start async read thread for device ID" << _deviceId;
120 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to start async read")));
121 return false;
122 }
123 }
124
125 // If pending bytes were left behind due to read buffer backpressure,
126 // schedule another drain as soon as reads are active again.
127 _scheduleReadyRead();
128
129 return true;
130}
131
132bool QSerialPortPrivate::_stopAsyncRead()
133{
135 return _posixStopAsyncRead();
136 }
137
138 bool result = true;
139
140 if (AndroidSerial::readThreadRunning(_deviceId)) {
141 result = AndroidSerial::stopReadThread(_deviceId);
142 if (!result) {
143 qCWarning(AndroidSerialPortLog) << "Failed to stop async read thread for device ID" << _deviceId;
144 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to stop async read")));
145 }
146 }
147
148 return result;
149}
150
151qint64 QSerialPortPrivate::_drainPendingDataLocked(qint64 maxBytes)
152{
153 const qsizetype pendingSize = _pendingSizeLocked();
154 if (pendingSize <= 0) {
155 _pendingData.clear();
156 _pendingDataOffset = 0;
157 return 0;
158 }
159
160 qint64 toDrain = pendingSize;
161 if (maxBytes >= 0) {
162 toDrain = qMin(toDrain, maxBytes);
163 }
164
165 if (toDrain <= 0) {
166 return 0;
167 }
168
169 buffer.append(_pendingData.constData() + _pendingDataOffset, toDrain);
170 _pendingDataOffset += static_cast<qsizetype>(toDrain);
171
172 if (_pendingDataOffset >= _pendingData.size()) {
173 _pendingData.clear();
174 _pendingDataOffset = 0;
175 } else {
176 // Compact occasionally to keep append operations efficient without
177 // paying the cost on every drain.
178 constexpr qsizetype kCompactThreshold = 4096;
179 if (_pendingDataOffset >= kCompactThreshold && (_pendingDataOffset * 2) >= _pendingData.size()) {
180 _compactPendingDataLocked();
181 }
182 }
183
184 _bufferBytesEstimate.store(buffer.size(), std::memory_order_relaxed);
185 return toDrain;
186}
187
188qsizetype QSerialPortPrivate::_pendingSizeLocked() const
189{
190 return qMax<qsizetype>(0, _pendingData.size() - _pendingDataOffset);
191}
192
193void QSerialPortPrivate::_compactPendingDataLocked()
194{
195 if (_pendingDataOffset <= 0) {
196 return;
197 }
198
199 if (_pendingDataOffset >= _pendingData.size()) {
200 _pendingData.clear();
201 _pendingDataOffset = 0;
202 return;
203 }
204
205 _pendingData.remove(0, _pendingDataOffset);
206 _pendingDataOffset = 0;
207}
208
209void QSerialPortPrivate::newDataArrived(const char* bytes, int length)
210{
211 // qCDebug(AndroidSerialPortLog) << "newDataArrived" << length;
212
213 qint64 droppedBytes = 0;
214
215 QMutexLocker locker(&_readMutex);
216 int bytesToRead = length;
217 if (readBufferMaxSize) {
218 const qint64 totalBuffered = _pendingSizeLocked() + _bufferBytesEstimate.load(std::memory_order_relaxed);
219 const qint64 headroom = readBufferMaxSize - totalBuffered;
220 if (bytesToRead > headroom) {
221 bytesToRead = static_cast<int>(qMax(qint64(0), headroom));
222 droppedBytes = static_cast<qint64>(length - bytesToRead);
223 }
224 }
225
226 if (bytesToRead > 0) {
227 constexpr qsizetype kCompactBeforeAppendThreshold = 8192;
228 if (_pendingDataOffset >= kCompactBeforeAppendThreshold) {
229 _compactPendingDataLocked();
230 }
231 _pendingData.append(bytes, bytesToRead);
232 _readWaitCondition.wakeAll();
233 }
234 locker.unlock();
235
236 if (droppedBytes > 0) {
237 qCWarning(AndroidSerialPortLog) << "Read buffer full, dropping" << droppedBytes << "bytes";
238 }
239
240 if (bytesToRead <= 0) {
241 return;
242 }
243
244 _scheduleReadyRead();
245}
246
247void QSerialPortPrivate::_scheduleReadyRead()
248{
249 Q_Q(QSerialPort);
250
251 if (!_readyReadPending.exchange(true)) {
252 QPointer<QSerialPort> guard(q);
253 QMetaObject::invokeMethod(
254 q,
255 [this, guard]() {
256 if (!guard || !guard->isOpen()) {
257 // Device closed before the queued drain ran. close() clears the
258 // QIODevice read buffers, so draining into them would assert.
259 _readyReadPending.store(false);
260 return;
261 }
262
263 QMutexLocker locker(&_readMutex);
264 if (_pendingSizeLocked() <= 0) {
265 _readyReadPending.store(false);
266 return;
267 }
268
269 if (readBufferMaxSize > 0) {
270 const qint64 canAccept = readBufferMaxSize - buffer.size();
271 if (canAccept > 0) {
272 (void)_drainPendingDataLocked(canAccept);
273 }
274 } else {
275 (void)_drainPendingDataLocked();
276 }
277
278 // Reset flag after drain so data arriving during the drain
279 // does not enqueue redundant lambdas. If pending data remains,
280 // reschedule so nothing is left undelivered.
281 const bool more = (_pendingSizeLocked() > 0);
282 _readyReadPending.store(false);
283
284 _readWaitCondition.wakeAll();
285 locker.unlock();
286
287 emit guard->readyRead();
288
289 if (more) {
290 _scheduleReadyRead();
291 }
292 },
293 Qt::QueuedConnection);
294 }
295}
296
297bool QSerialPortPrivate::waitForReadyRead(int msecs)
298{
300 return _posixWaitForReadyRead(msecs);
301 }
302
303 QMutexLocker locker(&_readMutex);
304 if (!buffer.isEmpty()) {
305 return true;
306 }
307
308 if (_pendingSizeLocked() > 0) {
309 (void)_drainPendingDataLocked();
310 return true;
311 }
312
313 QDeadlineTimer deadline(msecs);
314 while (buffer.isEmpty() && (_pendingSizeLocked() <= 0)) {
315 if (!_readWaitCondition.wait(&_readMutex, deadline)) {
316 break;
317 }
318
319 if (!buffer.isEmpty()) {
320 return true;
321 }
322
323 if (_pendingSizeLocked() > 0) {
324 (void)_drainPendingDataLocked();
325 return true;
326 }
327 }
328 locker.unlock();
329
330 qCWarning(AndroidSerialPortLog) << "Timeout while waiting for ready read on device ID" << _deviceId;
331 setError(QSerialPortErrorInfo(QSerialPort::TimeoutError, QSerialPort::tr("Timeout while waiting for ready read")));
332
333 return false;
334}
335
337{
338 const bool result = _writeDataOneShot(msecs);
339 if (!result) {
340 qCWarning(AndroidSerialPortLog) << "Timeout while waiting for bytes written on device ID" << _deviceId;
342 QSerialPort::tr("Timeout while waiting for bytes written")));
343 }
344
345 return result;
346}
347
348bool QSerialPortPrivate::_writeDataOneShot(int msecs)
349{
350 if (writeBuffer.isEmpty()) {
351 return true;
352 }
353
354 qint64 pendingBytesWritten = 0;
355
356 while (!writeBuffer.isEmpty()) {
357 const char* dataPtr = writeBuffer.readPointer();
358 const qint64 dataSize = writeBuffer.nextDataBlockSize();
359
360 const qint64 written = _writeToPort(dataPtr, dataSize, msecs);
361 if (written < 0) {
362 qCWarning(AndroidSerialPortLog) << "Failed to write data one shot on device ID" << _deviceId;
363 setError(QSerialPortErrorInfo(QSerialPort::WriteError, QSerialPort::tr("Failed to write data one shot")));
364 return false;
365 }
366
367 writeBuffer.free(written);
368 pendingBytesWritten += written;
369 }
370
371 const bool result = (pendingBytesWritten > 0);
372 if (result) {
373 Q_Q(QSerialPort);
374 emit q->bytesWritten(pendingBytesWritten);
375 }
376
377 return result;
378}
379
380qint64 QSerialPortPrivate::_writeToPort(const char* data, qint64 maxSize, int timeout, bool async)
381{
382 if (async && AndroidSerial::usePosixSerial()) {
383 qCWarning(AndroidSerialPortLog) << "Async write is not supported by the POSIX backend; writing synchronously";
384 }
385
386 const qint64 result = AndroidSerial::usePosixSerial()
387 ? _posixWrite(data, maxSize, timeout)
388 : AndroidSerial::write(_deviceId, data, maxSize, timeout, async);
389 if (result < 0) {
390 qCWarning(AndroidSerialPortLog) << "Failed to write to port" << systemLocation;
391 setError(QSerialPortErrorInfo(QSerialPort::WriteError, QSerialPort::tr("Failed to write to port")));
392 }
393
394 return result;
395}
396
397qint64 QSerialPortPrivate::writeData(const char* data, qint64 maxSize)
398{
399 if (!data || (maxSize <= 0)) {
400 qCWarning(AndroidSerialPortLog) << "Invalid data or size in writeData for device ID" << _deviceId;
401 setError(QSerialPortErrorInfo(QSerialPort::WriteError, QSerialPort::tr("Invalid data or size")));
402 return -1;
403 }
404
405 return _writeToPort(data, maxSize);
406}
407
409{
410 const bool result = _writeDataOneShot();
411 if (!result) {
412 qCWarning(AndroidSerialPortLog) << "Flush operation failed for device ID" << _deviceId;
413 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to flush")));
414 }
415
416 return result;
417}
418
419bool QSerialPortPrivate::clear(QSerialPort::Directions directions)
420{
422 return _posixClear(directions);
423 }
424
425 const bool input = directions & QSerialPort::Input;
426 const bool output = directions & QSerialPort::Output;
427
428 const bool result = AndroidSerial::purgeBuffers(_deviceId, input, output);
429 if (!result) {
430 qCWarning(AndroidSerialPortLog) << "Failed to purge buffers for device ID" << _deviceId;
431 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to purge buffers")));
432 }
433
434 return result;
435}
436
437QSerialPort::PinoutSignals QSerialPortPrivate::pinoutSignals()
438{
440 return _posixPinoutSignals();
441 }
442
443 return AndroidSerial::getControlLines(_deviceId);
444}
445
447{
449 return _posixSetDataTerminalReady(set);
450 }
451
452 const bool result = AndroidSerial::setDataTerminalReady(_deviceId, set);
453 if (!result) {
454 qCWarning(AndroidSerialPortLog) << "Failed to set DTR for device ID" << _deviceId;
455 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set DTR")));
456 }
457
458 return result;
459}
460
462{
464 return _posixSetRequestToSend(set);
465 }
466
467 const bool result = AndroidSerial::setRequestToSend(_deviceId, set);
468 if (!result) {
469 qCWarning(AndroidSerialPortLog) << "Failed to set RTS for device ID" << _deviceId;
470 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set RTS")));
471 }
472
473 return result;
474}
475
476bool QSerialPortPrivate::_setParameters(qint32 baudRate, QSerialPort::DataBits dataBits_,
478{
480 return _posixApplyPortSettings(baudRate, dataBits_, stopBits_, parity_, flowControl);
481 }
482
483 const bool result =
484 AndroidSerial::setParameters(_deviceId, baudRate, _dataBitsToAndroidDataBits(dataBits_),
485 _stopBitsToAndroidStopBits(stopBits_), _parityToAndroidParity(parity_));
486 if (!result) {
487 qCWarning(AndroidSerialPortLog) << "Failed to set Parameters for device ID" << _deviceId;
488 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set parameters")));
489 }
490
491 return result;
492}
493
498
499bool QSerialPortPrivate::setBaudRate(qint32 baudRate, QSerialPort::Directions directions)
500{
501 if (baudRate <= 0) {
502 qCWarning(AndroidSerialPortLog) << "Invalid baud rate value:" << baudRate;
503 setError(
504 QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, QSerialPort::tr("Invalid baud rate value")));
505 return false;
506 }
507
508 if (directions != QSerialPort::AllDirections) {
509 qCWarning(AndroidSerialPortLog) << "Custom baud rate direction is unsupported:" << directions;
511 QSerialPort::tr("Custom baud rate direction is unsupported")));
512 return false;
513 }
514
515 const bool result = _setParameters(baudRate, dataBits, stopBits, parity);
516 if (result) {
517 inputBaudRate = outputBaudRate = baudRate;
518 } else {
519 qCWarning(AndroidSerialPortLog) << "Failed to set baud rate for device ID" << _deviceId;
520 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set baud rate")));
521 }
522
523 return result;
524}
525
526int QSerialPortPrivate::_dataBitsToAndroidDataBits(QSerialPort::DataBits dataBits_)
527{
528 switch (dataBits_) {
537 default:
538 qCWarning(AndroidSerialPortLog) << "Invalid Data Bits" << dataBits_;
539 return AndroidSerial::Data8; // Default to Data8
540 }
541}
542
544{
545 const bool result = _setParameters(inputBaudRate, dataBits_, stopBits, parity);
546 if (!result) {
547 qCWarning(AndroidSerialPortLog) << "Failed to set data bits for device ID" << _deviceId;
548 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set data bits")));
549 }
550
551 return result;
552}
553
554int QSerialPortPrivate::_parityToAndroidParity(QSerialPort::Parity parity_)
555{
556 switch (parity_) {
567 default:
568 qCWarning(AndroidSerialPortLog) << "Invalid parity type:" << parity_;
569 return AndroidSerial::NoParity; // Default to NoParity
570 }
571}
572
574{
575 const bool result = _setParameters(inputBaudRate, dataBits, stopBits, parity_);
576 if (!result) {
577 qCWarning(AndroidSerialPortLog) << "Failed to set parity for device ID" << _deviceId;
578 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set parity")));
579 }
580
581 return result;
582}
583
584int QSerialPortPrivate::_stopBitsToAndroidStopBits(QSerialPort::StopBits stopBits_)
585{
586 switch (stopBits_) {
593 default:
594 qCWarning(AndroidSerialPortLog) << "Invalid Stop Bits type:" << stopBits_;
595 return AndroidSerial::OneStop; // Default to OneStop
596 }
597}
598
600{
601 const bool result = _setParameters(inputBaudRate, dataBits, stopBits_, parity);
602 if (!result) {
603 qCWarning(AndroidSerialPortLog) << "Failed to set StopBits for device ID" << _deviceId;
604 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set StopBits")));
605 }
606
607 return result;
608}
609
610int QSerialPortPrivate::_flowControlToAndroidFlowControl(QSerialPort::FlowControl flowControl_)
611{
612 switch (flowControl_) {
619 default:
620 qCWarning(AndroidSerialPortLog) << "Invalid Flow Control type:" << flowControl_;
621 return AndroidSerial::NoFlowControl; // Default to NoFlowControl
622 }
623}
624
626{
628 return _posixApplyPortSettings(inputBaudRate, dataBits, stopBits, parity, flowControl_);
629 }
630
631 const bool result = AndroidSerial::setFlowControl(_deviceId, _flowControlToAndroidFlowControl(flowControl_));
632 if (!result) {
633 qCWarning(AndroidSerialPortLog) << "Failed to set Flow Control for device ID" << _deviceId;
634 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set Flow Control")));
635 }
636
637 return result;
638}
639
641{
643 return _posixSetBreakEnabled(set);
644 }
645
646 const bool result = AndroidSerial::setBreak(_deviceId, set);
647 if (!result) {
648 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set Break Enabled")));
649 }
650
651 return result;
652}
653
654static constexpr qint32 kStandardBaudRates[] = {
655 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800,
656 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000,
657 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000,
658};
659
661{
662 return QList<qint32>(std::begin(kStandardBaudRates), std::end(kStandardBaudRates));
663}
664
665QSerialPort::Handle QSerialPort::handle() const
666{
667 Q_D(const QSerialPort);
668 return d->descriptor;
669}
670
671QT_END_NAMESPACE
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool waitForBytesWritten(int msec)
void setError(const QSerialPortErrorInfo &errorInfo)
bool setDataTerminalReady(bool set)
bool setStopBits(QSerialPort::StopBits stopBits)
bool setFlowControl(QSerialPort::FlowControl flowControl)
QSerialPort::PinoutSignals pinoutSignals()
bool clear(QSerialPort::Directions directions)
bool setDataBits(QSerialPort::DataBits dataBits)
static QList< qint32 > standardBaudRates()
qint64 writeData(const char *data, qint64 maxSize)
bool setRequestToSend(bool set)
void exceptionArrived(const QString &ex)
bool setParity(QSerialPort::Parity parity)
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
@ UnsupportedOperationError
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
Handle handle() const
int open(const QString &portName, QSerialPortPrivate *classPtr)
void registerPointer(QSerialPortPrivate *ptr)
int getDeviceHandle(int deviceId)
void unregisterPointer(QSerialPortPrivate *ptr)
bool usePosixSerial()
bool setDataTerminalReady(int deviceId, bool set)
bool setParameters(int deviceId, int baudRate, int dataBits, int stopBits, int parity)
bool startReadThread(int deviceId)
QSerialPort::PinoutSignals getControlLines(int deviceId)
bool setRequestToSend(int deviceId, bool set)
bool purgeBuffers(int deviceId, bool input, bool output)
bool readThreadRunning(int deviceId)
bool close(int deviceId)
bool stopReadThread(int deviceId)
bool setFlowControl(int deviceId, int flowControl)
bool setBreak(int deviceId, bool set)
bool write(QByteArray &buffer, const GeoTagData &geotag)
Definition ExifParser.cc:32
static constexpr qint32 kStandardBaudRates[]
constexpr int INVALID_DEVICE_ID