1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
#pragma once
#include <QAtomicInteger>
#include <QSemaphore>
#include <QThread>
#include <map>
#include "controllers/controller.h"
#include "controllers/hid/hiddevice.h"
#include "controllers/hid/hidiooutputreport.h"
#include "util/compatibility/qmutex.h"
#include "util/duration.h"
enum class HidIoThreadState {
Initialized,
OutputActive,
InputOutputActive,
StopWhenAllReportsSent,
StopRequested,
Stopped,
};
class HidIoThread : public QThread {
Q_OBJECT
public:
HidIoThread(hid_device* pDevice,
const mixxx::hid::DeviceInfo& deviceInfo);
~HidIoThread() override;
void run() override;
/// Sets the state of the HidIoThread lifecycle,
/// if the previous state was not the expected,
/// it returns with false as result.
[[nodiscard]] bool testAndSetThreadState(HidIoThreadState expectedState,
HidIoThreadState newState);
/// Set the state of the HidIoThread lifecycles unconditional
void setThreadState(HidIoThreadState expectedState);
/// Wait's until the run loop stopped, or the specified timeout, is reached.
/// Returns immediately with true if the run loop is stopped.
[[nodiscard]] bool waitUntilRunLoopIsStopped(unsigned int timeoutMillis);
void updateCachedOutputReportData(quint8 reportID,
const QByteArray& reportData,
bool resendUnchangedReport);
QByteArray getInputReport(quint8 reportID);
void sendFeatureReport(quint8 reportID, const QByteArray& reportData);
QByteArray getFeatureReport(quint8 reportID);
signals:
/// Signals that a HID InputReport received by Interrupt triggered from HID device
void receive(const QByteArray& data, mixxx::Duration timestamp);
private:
bool sendNextCachedOutputReport();
void pollBufferedInputReports();
void processInputReport(int bytesRead);
const mixxx::hid::DeviceInfo m_deviceInfo;
const RuntimeLoggingCategory m_logBase;
const RuntimeLoggingCategory m_logInput;
const RuntimeLoggingCategory m_logOutput;
/// This mutex must be locked for any hid device operation using the m_pHidDevice structure.
/// If the hid_error functions is called after the hid device operation to get the error message,
/// this mutex must not be unlocked before hid_error.
/// This mutex must be locked also, for access to m_pPollData, m_lastPollSize, m_pollingBufferIndex.
QMutex m_hidDeviceAndPollMutex;
/// const pointer to the C data structure, which hidapi uses for communication between functions
hid_device* const
m_pHidDevice;
static constexpr int kNumBuffers = 2;
static constexpr int kBufferSize = 255;
unsigned char m_pPollData[kNumBuffers][kBufferSize];
int m_lastPollSize;
int m_pollingBufferIndex;
/// Must be locked when a operation changes the size of the m_outputReports map,
/// or when modify the m_outputReportIterator
QMutex m_outputReportMapMutex;
typedef std::map<unsigned char, std::unique_ptr<HidIoOutputReport>> OutputReportMap;
/// m_outputReports is an empty map after class initialization.
/// An entry is inserted each time, when an OutputReport is send for the first time.
/// Until then, it's not known, which OutputReports a device/mapping has.
/// No other modifications to the map are done, until destruction of this class.
OutputReportMap m_outputReports;
OutputReportMap::iterator m_outputReportIterator;
/// State of the HidIoThread lifecycle
QAtomicInt m_state;
/// Semaphore with capacity 1, which is left acquired, as long as the run loop of the thread runs
QSemaphore m_runLoopSemaphore;
};
|