-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
HidController: loop until no more messages are available on poll #2970
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c2e62c9
fe35428
ec70661
1f84927
a362551
7cdc090
cd281c1
229e9f1
ecc5d33
cea971a
d146e74
a2e3267
26bdbef
cd283de
447a432
2343bbf
018f1e2
dff0202
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,8 @@ | |
|
|
||
| HidController::HidController(const hid_device_info& deviceInfo, UserSettingsPointer pConfig) | ||
| : Controller(pConfig), | ||
| m_pHidDevice(NULL) { | ||
| m_pHidDevice(nullptr), | ||
| m_iPollingBufferIndex(0) { | ||
| // Copy required variables from deviceInfo, which will be freed after | ||
| // this class is initialized by caller. | ||
| hid_vendor_id = deviceInfo.vendor_id; | ||
|
|
@@ -215,6 +216,10 @@ int HidController::open() { | |
| return -1; | ||
| } | ||
|
|
||
| for (int i = 0; i < kNumBuffers; i++) { | ||
| memset(m_pPollData[i], 0, kBufferSize); | ||
| } | ||
|
|
||
| setOpen(true); | ||
| startEngine(); | ||
|
|
||
|
|
@@ -243,16 +248,42 @@ int HidController::close() { | |
| bool HidController::poll() { | ||
| Trace hidRead("HidController poll"); | ||
|
|
||
| int result = hid_read(m_pHidDevice, m_pPollData, sizeof(m_pPollData) / sizeof(m_pPollData[0])); | ||
| if (result == -1) { | ||
| return false; | ||
| } else if (result > 0) { | ||
| // This loop risks becoming a high priority endless loop in case processing | ||
| // the mapping JS code takes longer than the controller polling rate. | ||
| // This could stall other low priority tasks. | ||
| // There is no safety net for this because it has not been demonstrated to be | ||
| // a problem in practice. | ||
| while (true) { | ||
| // Cycle between buffers so the memcmp below does not require deep copying to another buffer. | ||
| unsigned char* pPreviousBuffer = m_pPollData[m_iPollingBufferIndex]; | ||
|
Be-ing marked this conversation as resolved.
|
||
| m_iPollingBufferIndex = (m_iPollingBufferIndex + 1) % kNumBuffers; | ||
| unsigned char* pCurrentBuffer = m_pPollData[m_iPollingBufferIndex]; | ||
|
|
||
| int bytesRead = hid_read(m_pHidDevice, pCurrentBuffer, kBufferSize); | ||
| if (bytesRead < 0) { | ||
| // -1 is the only error value according to hidapi documentation. | ||
| DEBUG_ASSERT(bytesRead == -1); | ||
| return false; | ||
| } else if (bytesRead == 0) { | ||
| return true; | ||
| } | ||
|
|
||
| Trace process("HidController process packet"); | ||
| QByteArray outData(reinterpret_cast<char*>(m_pPollData), result); | ||
| receive(outData, mixxx::Time::elapsed()); | ||
| // Some controllers such as the Gemini GMX continuously send input packets even if it | ||
| // is identical to the previous packet. If this loop processed all those redundant | ||
| // packets, it would be a big performance problem to run JS code for every packet and | ||
| // would be unnecessary. | ||
| // This assumes that the redundant packets all use the same report ID. In practice we | ||
| // have not encountered any controllers that send redundant packets with different report | ||
| // IDs. If any such devices exist, this may be changed to use a separate buffer to store | ||
| // the last packet for each report ID. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Be-ing I found an issue with the following assumption, while working on #3317:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does that mean this always loops infinitely??
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I presume hid_read is correctly returning 0 when all packets have been read so there is no infinite loop otherwise I presume you or @codecat would have noticed this earlier. Can you confirm this? What happens when reading the smaller packet past its true size? Are the bytes after that random garbage?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No infinite loop, it fills the remaining bytes with garbage (looks like data from the bigger report). I expect that this comparisition will be triggered by this garbage.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, that's not as bad as an infinite loop. I suppose you could temporarily hack around in your controller script until the hidapi bug is fixed upstream. If you want to work on the hidapi bug, we could merge a fix in our bundled version of hidapi if you open a pull request upstream. As noted in the comment, this comparison checking if the current buffer is identical to the previous buffer will not evaluate to true if the device uses multiple report IDs.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've not such a controller. I stumbled over this, because I used hid_get_input_report, which behaves different (correct) than the existing code with hid_read. |
||
| if (memcmp(pCurrentBuffer, pPreviousBuffer, kBufferSize) == 0) { | ||
| continue; | ||
| } | ||
| auto incomingData = QByteArray::fromRawData( | ||
| reinterpret_cast<char*>(pCurrentBuffer), bytesRead); | ||
| receive(incomingData, mixxx::Time::elapsed()); | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool HidController::isPolling() const { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.