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{
16 qCDebug(AndroidSerialPortLog) << "Opening" << systemLocation;
17
19 auto tokenGuard = qScopeGuard([this]() { AndroidSerial::unregisterPointer(this); });
20
21 _deviceId = AndroidSerial::open(systemLocation, this);
22 if (_deviceId == INVALID_DEVICE_ID) {
23 qCWarning(AndroidSerialPortLog) << "Error opening" << systemLocation;
25 return false;
26 }
27
28 descriptor = AndroidSerial::getDeviceHandle(_deviceId);
29 if (descriptor == -1) {
30 qCWarning(AndroidSerialPortLog) << "Failed to get device handle for" << systemLocation;
32 close();
33 return false;
34 }
35
36 if (!_setParameters(inputBaudRate, dataBits, stopBits, parity)) {
37 qCWarning(AndroidSerialPortLog) << "Failed to set serial port parameters for" << systemLocation;
38 close();
39 return false;
40 }
41
42 if (!setFlowControl(flowControl)) {
43 qCWarning(AndroidSerialPortLog) << "Failed to set serial port flow control for" << systemLocation;
44 close();
45 return false;
46 }
47
48 if (mode & QIODevice::ReadOnly) {
49 if (!startAsyncRead()) {
50 qCWarning(AndroidSerialPortLog) << "Failed to start async read for" << systemLocation;
51 close();
52 return false;
53 }
54 } else if (mode & QIODevice::WriteOnly) {
55 if (!_stopAsyncRead()) {
56 qCWarning(AndroidSerialPortLog) << "Failed to stop async read for" << systemLocation;
57 }
58 }
59
60 (void)clear(QSerialPort::AllDirections);
61 tokenGuard.dismiss();
62
63 return true;
64}
65
67{
68 qCDebug(AndroidSerialPortLog) << "Closing" << systemLocation;
69
70 _stopAsyncRead();
71
72 if (_deviceId != INVALID_DEVICE_ID) {
73 if (!AndroidSerial::close(_deviceId)) {
74 qCWarning(AndroidSerialPortLog) << "Failed to close device with ID" << _deviceId;
75 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Closing device failed")));
76 }
77 _deviceId = INVALID_DEVICE_ID;
78 }
79
80 descriptor = -1;
81
83}
84
86{
87 qCWarning(AndroidSerialPortLog) << "Exception arrived on device ID" << _deviceId << ":" << ex;
89}
90
92{
93 if (!AndroidSerial::readThreadRunning(_deviceId)) {
94 const bool result = AndroidSerial::startReadThread(_deviceId);
95 if (!result) {
96 qCWarning(AndroidSerialPortLog) << "Failed to start async read thread for device ID" << _deviceId;
97 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to start async read")));
98 return false;
99 }
100 }
101
102 // If pending bytes were left behind due to read buffer backpressure,
103 // schedule another drain as soon as reads are active again.
104 _scheduleReadyRead();
105
106 return true;
107}
108
109bool QSerialPortPrivate::_stopAsyncRead()
110{
111 bool result = true;
112
113 if (AndroidSerial::readThreadRunning(_deviceId)) {
114 result = AndroidSerial::stopReadThread(_deviceId);
115 if (!result) {
116 qCWarning(AndroidSerialPortLog) << "Failed to stop async read thread for device ID" << _deviceId;
117 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to stop async read")));
118 }
119 }
120
121 return result;
122}
123
124qint64 QSerialPortPrivate::_drainPendingDataLocked(qint64 maxBytes)
125{
126 const qsizetype pendingSize = _pendingSizeLocked();
127 if (pendingSize <= 0) {
128 _pendingData.clear();
129 _pendingDataOffset = 0;
130 return 0;
131 }
132
133 qint64 toDrain = pendingSize;
134 if (maxBytes >= 0) {
135 toDrain = qMin(toDrain, maxBytes);
136 }
137
138 if (toDrain <= 0) {
139 return 0;
140 }
141
142 buffer.append(_pendingData.constData() + _pendingDataOffset, toDrain);
143 _pendingDataOffset += static_cast<qsizetype>(toDrain);
144
145 if (_pendingDataOffset >= _pendingData.size()) {
146 _pendingData.clear();
147 _pendingDataOffset = 0;
148 } else {
149 // Compact occasionally to keep append operations efficient without
150 // paying the cost on every drain.
151 constexpr qsizetype kCompactThreshold = 4096;
152 if (_pendingDataOffset >= kCompactThreshold && (_pendingDataOffset * 2) >= _pendingData.size()) {
153 _compactPendingDataLocked();
154 }
155 }
156
157 _bufferBytesEstimate.store(buffer.size(), std::memory_order_relaxed);
158 return toDrain;
159}
160
161qsizetype QSerialPortPrivate::_pendingSizeLocked() const
162{
163 return qMax<qsizetype>(0, _pendingData.size() - _pendingDataOffset);
164}
165
166void QSerialPortPrivate::_compactPendingDataLocked()
167{
168 if (_pendingDataOffset <= 0) {
169 return;
170 }
171
172 if (_pendingDataOffset >= _pendingData.size()) {
173 _pendingData.clear();
174 _pendingDataOffset = 0;
175 return;
176 }
177
178 _pendingData.remove(0, _pendingDataOffset);
179 _pendingDataOffset = 0;
180}
181
182void QSerialPortPrivate::newDataArrived(const char* bytes, int length)
183{
184 // qCDebug(AndroidSerialPortLog) << "newDataArrived" << length;
185
186 qint64 droppedBytes = 0;
187
188 QMutexLocker locker(&_readMutex);
189 int bytesToRead = length;
190 if (readBufferMaxSize) {
191 const qint64 totalBuffered = _pendingSizeLocked() + _bufferBytesEstimate.load(std::memory_order_relaxed);
192 const qint64 headroom = readBufferMaxSize - totalBuffered;
193 if (bytesToRead > headroom) {
194 bytesToRead = static_cast<int>(qMax(qint64(0), headroom));
195 droppedBytes = static_cast<qint64>(length - bytesToRead);
196 }
197 }
198
199 if (bytesToRead > 0) {
200 constexpr qsizetype kCompactBeforeAppendThreshold = 8192;
201 if (_pendingDataOffset >= kCompactBeforeAppendThreshold) {
202 _compactPendingDataLocked();
203 }
204 _pendingData.append(bytes, bytesToRead);
205 _readWaitCondition.wakeAll();
206 }
207 locker.unlock();
208
209 if (droppedBytes > 0) {
210 qCWarning(AndroidSerialPortLog) << "Read buffer full, dropping" << droppedBytes << "bytes";
211 }
212
213 if (bytesToRead <= 0) {
214 return;
215 }
216
217 _scheduleReadyRead();
218}
219
220void QSerialPortPrivate::_scheduleReadyRead()
221{
222 Q_Q(QSerialPort);
223
224 if (!_readyReadPending.exchange(true)) {
225 QPointer<QSerialPort> guard(q);
226 QMetaObject::invokeMethod(
227 q,
228 [this, guard]() {
229 if (!guard) {
230 return;
231 }
232
233 QMutexLocker locker(&_readMutex);
234 if (_pendingSizeLocked() <= 0) {
235 _readyReadPending.store(false);
236 return;
237 }
238
239 if (readBufferMaxSize > 0) {
240 const qint64 canAccept = readBufferMaxSize - buffer.size();
241 if (canAccept > 0) {
242 (void)_drainPendingDataLocked(canAccept);
243 }
244 } else {
245 (void)_drainPendingDataLocked();
246 }
247
248 // Reset flag after drain so data arriving during the drain
249 // does not enqueue redundant lambdas. If pending data remains,
250 // reschedule so nothing is left undelivered.
251 const bool more = (_pendingSizeLocked() > 0);
252 _readyReadPending.store(false);
253
254 _readWaitCondition.wakeAll();
255 locker.unlock();
256
257 emit guard->readyRead();
258
259 if (more) {
260 _scheduleReadyRead();
261 }
262 },
263 Qt::QueuedConnection);
264 }
265}
266
267bool QSerialPortPrivate::waitForReadyRead(int msecs)
268{
269 QMutexLocker locker(&_readMutex);
270 if (!buffer.isEmpty()) {
271 return true;
272 }
273
274 if (_pendingSizeLocked() > 0) {
275 (void)_drainPendingDataLocked();
276 return true;
277 }
278
279 QDeadlineTimer deadline(msecs);
280 while (buffer.isEmpty() && (_pendingSizeLocked() <= 0)) {
281 if (!_readWaitCondition.wait(&_readMutex, deadline)) {
282 break;
283 }
284
285 if (!buffer.isEmpty()) {
286 return true;
287 }
288
289 if (_pendingSizeLocked() > 0) {
290 (void)_drainPendingDataLocked();
291 return true;
292 }
293 }
294 locker.unlock();
295
296 qCWarning(AndroidSerialPortLog) << "Timeout while waiting for ready read on device ID" << _deviceId;
297 setError(QSerialPortErrorInfo(QSerialPort::TimeoutError, QSerialPort::tr("Timeout while waiting for ready read")));
298
299 return false;
300}
301
303{
304 const bool result = _writeDataOneShot(msecs);
305 if (!result) {
306 qCWarning(AndroidSerialPortLog) << "Timeout while waiting for bytes written on device ID" << _deviceId;
308 QSerialPort::tr("Timeout while waiting for bytes written")));
309 }
310
311 return result;
312}
313
314bool QSerialPortPrivate::_writeDataOneShot(int msecs)
315{
316 if (writeBuffer.isEmpty()) {
317 return true;
318 }
319
320 qint64 pendingBytesWritten = 0;
321
322 while (!writeBuffer.isEmpty()) {
323 const char* dataPtr = writeBuffer.readPointer();
324 const qint64 dataSize = writeBuffer.nextDataBlockSize();
325
326 const qint64 written = _writeToPort(dataPtr, dataSize, msecs);
327 if (written < 0) {
328 qCWarning(AndroidSerialPortLog) << "Failed to write data one shot on device ID" << _deviceId;
329 setError(QSerialPortErrorInfo(QSerialPort::WriteError, QSerialPort::tr("Failed to write data one shot")));
330 return false;
331 }
332
333 writeBuffer.free(written);
334 pendingBytesWritten += written;
335 }
336
337 const bool result = (pendingBytesWritten > 0);
338 if (result) {
339 Q_Q(QSerialPort);
340 emit q->bytesWritten(pendingBytesWritten);
341 }
342
343 return result;
344}
345
346qint64 QSerialPortPrivate::_writeToPort(const char* data, qint64 maxSize, int timeout, bool async)
347{
348 const qint64 result = AndroidSerial::write(_deviceId, data, maxSize, timeout, async);
349 if (result < 0) {
350 qCWarning(AndroidSerialPortLog) << "Failed to write to port ID" << _deviceId;
351 setError(QSerialPortErrorInfo(QSerialPort::WriteError, QSerialPort::tr("Failed to write to port")));
352 }
353
354 return result;
355}
356
357qint64 QSerialPortPrivate::writeData(const char* data, qint64 maxSize)
358{
359 if (!data || (maxSize <= 0)) {
360 qCWarning(AndroidSerialPortLog) << "Invalid data or size in writeData for device ID" << _deviceId;
361 setError(QSerialPortErrorInfo(QSerialPort::WriteError, QSerialPort::tr("Invalid data or size")));
362 return -1;
363 }
364
365 return _writeToPort(data, maxSize);
366}
367
369{
370 const bool result = _writeDataOneShot();
371 if (!result) {
372 qCWarning(AndroidSerialPortLog) << "Flush operation failed for device ID" << _deviceId;
373 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to flush")));
374 }
375
376 return result;
377}
378
379bool QSerialPortPrivate::clear(QSerialPort::Directions directions)
380{
381 const bool input = directions & QSerialPort::Input;
382 const bool output = directions & QSerialPort::Output;
383
384 const bool result = AndroidSerial::purgeBuffers(_deviceId, input, output);
385 if (!result) {
386 qCWarning(AndroidSerialPortLog) << "Failed to purge buffers for device ID" << _deviceId;
387 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to purge buffers")));
388 }
389
390 return result;
391}
392
393QSerialPort::PinoutSignals QSerialPortPrivate::pinoutSignals()
394{
395 return AndroidSerial::getControlLines(_deviceId);
396}
397
399{
400 const bool result = AndroidSerial::setDataTerminalReady(_deviceId, set);
401 if (!result) {
402 qCWarning(AndroidSerialPortLog) << "Failed to set DTR for device ID" << _deviceId;
403 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set DTR")));
404 }
405
406 return result;
407}
408
410{
411 const bool result = AndroidSerial::setRequestToSend(_deviceId, set);
412 if (!result) {
413 qCWarning(AndroidSerialPortLog) << "Failed to set RTS for device ID" << _deviceId;
414 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set RTS")));
415 }
416
417 return result;
418}
419
420bool QSerialPortPrivate::_setParameters(qint32 baudRate, QSerialPort::DataBits dataBits_,
422{
423 const bool result =
424 AndroidSerial::setParameters(_deviceId, baudRate, _dataBitsToAndroidDataBits(dataBits_),
425 _stopBitsToAndroidStopBits(stopBits_), _parityToAndroidParity(parity_));
426 if (!result) {
427 qCWarning(AndroidSerialPortLog) << "Failed to set Parameters for device ID" << _deviceId;
428 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set parameters")));
429 }
430
431 return result;
432}
433
438
439bool QSerialPortPrivate::setBaudRate(qint32 baudRate, QSerialPort::Directions directions)
440{
441 if (baudRate <= 0) {
442 qCWarning(AndroidSerialPortLog) << "Invalid baud rate value:" << baudRate;
443 setError(
444 QSerialPortErrorInfo(QSerialPort::UnsupportedOperationError, QSerialPort::tr("Invalid baud rate value")));
445 return false;
446 }
447
448 if (directions != QSerialPort::AllDirections) {
449 qCWarning(AndroidSerialPortLog) << "Custom baud rate direction is unsupported:" << directions;
451 QSerialPort::tr("Custom baud rate direction is unsupported")));
452 return false;
453 }
454
455 const bool result = _setParameters(baudRate, dataBits, stopBits, parity);
456 if (result) {
457 inputBaudRate = outputBaudRate = baudRate;
458 } else {
459 qCWarning(AndroidSerialPortLog) << "Failed to set baud rate for device ID" << _deviceId;
460 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set baud rate")));
461 }
462
463 return result;
464}
465
466int QSerialPortPrivate::_dataBitsToAndroidDataBits(QSerialPort::DataBits dataBits_)
467{
468 switch (dataBits_) {
477 default:
478 qCWarning(AndroidSerialPortLog) << "Invalid Data Bits" << dataBits_;
479 return AndroidSerial::Data8; // Default to Data8
480 }
481}
482
484{
485 const bool result = _setParameters(inputBaudRate, dataBits_, stopBits, parity);
486 if (!result) {
487 qCWarning(AndroidSerialPortLog) << "Failed to set data bits for device ID" << _deviceId;
488 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set data bits")));
489 }
490
491 return result;
492}
493
494int QSerialPortPrivate::_parityToAndroidParity(QSerialPort::Parity parity_)
495{
496 switch (parity_) {
507 default:
508 qCWarning(AndroidSerialPortLog) << "Invalid parity type:" << parity_;
509 return AndroidSerial::NoParity; // Default to NoParity
510 }
511}
512
514{
515 const bool result = _setParameters(inputBaudRate, dataBits, stopBits, parity_);
516 if (!result) {
517 qCWarning(AndroidSerialPortLog) << "Failed to set parity for device ID" << _deviceId;
518 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set parity")));
519 }
520
521 return result;
522}
523
524int QSerialPortPrivate::_stopBitsToAndroidStopBits(QSerialPort::StopBits stopBits_)
525{
526 switch (stopBits_) {
533 default:
534 qCWarning(AndroidSerialPortLog) << "Invalid Stop Bits type:" << stopBits_;
535 return AndroidSerial::OneStop; // Default to OneStop
536 }
537}
538
540{
541 const bool result = _setParameters(inputBaudRate, dataBits, stopBits_, parity);
542 if (!result) {
543 qCWarning(AndroidSerialPortLog) << "Failed to set StopBits for device ID" << _deviceId;
544 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set StopBits")));
545 }
546
547 return result;
548}
549
550int QSerialPortPrivate::_flowControlToAndroidFlowControl(QSerialPort::FlowControl flowControl_)
551{
552 switch (flowControl_) {
559 default:
560 qCWarning(AndroidSerialPortLog) << "Invalid Flow Control type:" << flowControl_;
561 return AndroidSerial::NoFlowControl; // Default to NoFlowControl
562 }
563}
564
566{
567 const bool result = AndroidSerial::setFlowControl(_deviceId, _flowControlToAndroidFlowControl(flowControl_));
568 if (!result) {
569 qCWarning(AndroidSerialPortLog) << "Failed to set Flow Control for device ID" << _deviceId;
570 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set Flow Control")));
571 }
572
573 return result;
574}
575
577{
578 const bool result = AndroidSerial::setBreak(_deviceId, set);
579 if (!result) {
580 setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to set Break Enabled")));
581 }
582
583 return result;
584}
585
586static constexpr qint32 kStandardBaudRates[] = {
587 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800,
588 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000,
589 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000,
590};
591
593{
594 return QList<qint32>(std::begin(kStandardBaudRates), std::end(kStandardBaudRates));
595}
596
597QSerialPort::Handle QSerialPort::handle() const
598{
599 Q_D(const QSerialPort);
600 return d->descriptor;
601}
602
603QT_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
@ UnsupportedOperationError
Handle handle() const
int open(const QString &portName, QSerialPortPrivate *classPtr)
void registerPointer(QSerialPortPrivate *ptr)
int getDeviceHandle(int deviceId)
void unregisterPointer(QSerialPortPrivate *ptr)
bool setDataTerminalReady(int deviceId, bool set)
int write(int deviceId, const char *data, int length, int timeout, bool async)
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)
static constexpr qint32 kStandardBaudRates[]
constexpr int INVALID_DEVICE_ID
QT_BEGIN_NAMESPACE