diff options
Diffstat (limited to 'src/controllers/hid/hidioglobaloutputreportfifo.cpp')
-rw-r--r-- | src/controllers/hid/hidioglobaloutputreportfifo.cpp | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/src/controllers/hid/hidioglobaloutputreportfifo.cpp b/src/controllers/hid/hidioglobaloutputreportfifo.cpp new file mode 100644 index 0000000000..c8cb0c958f --- /dev/null +++ b/src/controllers/hid/hidioglobaloutputreportfifo.cpp @@ -0,0 +1,94 @@ +#include "controllers/hid/hidioglobaloutputreportfifo.h" + +#include <hidapi.h> + +#include "controllers/defs_controllers.h" +#include "controllers/hid/legacyhidcontrollermappingfilehandler.h" +#include "util/compatibility/qbytearray.h" +#include "util/compatibility/qmutex.h" +#include "util/string.h" +#include "util/time.h" +#include "util/trace.h" + +namespace { +constexpr size_t kMaxHidErrorMessageSize = 512; +constexpr size_t kSizeOfFifoInReports = 32; +} // namespace + +HidIoGlobalOutputReportFifo::HidIoGlobalOutputReportFifo() + : m_fifoQueue(kSizeOfFifoInReports) { +} + +void HidIoGlobalOutputReportFifo::addReportDatasetToFifo(const quint8 reportId, + const QByteArray& reportData, + const mixxx::hid::DeviceInfo& deviceInfo, + const RuntimeLoggingCategory& logOutput) { + // First byte must always be the ReportID-Byte + QByteArray report(reportData); + report.prepend(reportId); // In Qt6 this is a very fast operation without reallocation + + // Swap report to lockless FIFO queue + bool success = m_fifoQueue.try_emplace(std::move(report)); + + // Handle the case, that the FIFO queue is full - which is an error case + if (!success) { + // If the FIFO is full, we skip the report dataset even + // in non-skipping mode, to keep the controller mapping thread + // responsive for InputReports from the controller. + // Alternative would be to block processing of the controller + // mapping thread, until the FIFO has space again. + qCWarning(logOutput) + << "FIFO overflow: Unable to add OutputReport " << reportId + << "to the global cache for non-skipping sending of OututReports for" + << deviceInfo.formatName(); + } +} + +bool HidIoGlobalOutputReportFifo::sendNextReportDataset(QMutex* pHidDeviceAndPollMutex, + hid_device* pHidDevice, + const mixxx::hid::DeviceInfo& deviceInfo, + const RuntimeLoggingCategory& logOutput) { + auto startOfHidWrite = mixxx::Time::elapsed(); + + QByteArray* pFront = m_fifoQueue.front(); + + if (pFront == nullptr) { + // No data in FIFO to be send + // Return with false, to signal the caller, that no time consuming IO + // operation was ncessary + return false; + } + + // Array containing the ReportID byte followed by the data to be send + QByteArray reportToSend(std::move(*pFront)); + m_fifoQueue.pop(); + + auto hidDeviceLock = lockMutex(pHidDeviceAndPollMutex); + + // hid_write can take several milliseconds, because hidapi synchronizes + // the asyncron HID communication from the OS + int result = hid_write(pHidDevice, + reinterpret_cast<const unsigned char*>(reportToSend.constData()), + reportToSend.size()); + if (result == -1) { + qCWarning(logOutput) << "Unable to send data to" << deviceInfo.formatName() << ":" + << mixxx::convertWCStringToQString( + hid_error(pHidDevice), + kMaxHidErrorMessageSize); + } + + hidDeviceLock.unlock(); + + if (result != -1) { + qCDebug(logOutput) << "t:" << startOfHidWrite.formatMillisWithUnit() + << " " << result << "bytes (including ReportID of" + << static_cast<quint8>(reportToSend[0]) + << ") sent from non-skipping FIFO - Needed: " + << (mixxx::Time::elapsed() - startOfHidWrite) + .formatMicrosWithUnit(); + } + + // Return with true, to signal the caller, that the time consuming hid_write + // operation was executed + return true; +} |