Skip to content

Commit d994895

Browse files
committed
controllers: bulk: Implement asynchronous polling
The current synchronous polling method is wasteful and also only works on one endpoint. Since I am planning to add support for the Traktor Kontrol X1 MK1, which uses two input endpoints for ACK and events, synchronous polling is not only slow, but also doesn't exhibit reliable packet delivery.
1 parent 62c03d7 commit d994895

2 files changed

Lines changed: 98 additions & 33 deletions

File tree

src/controllers/bulk/bulkcontroller.cpp

Lines changed: 86 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,47 +13,105 @@
1313
#include "util/time.h"
1414
#include "util/trace.h"
1515

16-
BulkReader::BulkReader(libusb_device_handle *handle, unsigned char in_epaddr)
16+
struct bulk_transfer_cb_data {
17+
BulkReader* reader;
18+
};
19+
20+
static void transferFinishedCb(libusb_transfer* transfer) {
21+
bulk_transfer_cb_data* cb_data = static_cast<bulk_transfer_cb_data*>(transfer->user_data);
22+
cb_data->reader->handleTransfer(transfer);
23+
}
24+
25+
BulkReader::BulkReader(libusb_device_handle* handle,
26+
libusb_context* context,
27+
std::uint8_t in_epaddr)
1728
: QThread(),
18-
m_phandle(handle),
1929
m_stop(0),
20-
m_in_epaddr(in_epaddr) {
30+
m_context(context) {
31+
m_in_transfer = libusb_alloc_transfer(0);
32+
33+
m_cb_data = std::make_unique<bulk_transfer_cb_data>(this);
34+
35+
libusb_fill_bulk_transfer(m_in_transfer,
36+
handle,
37+
in_epaddr,
38+
m_data.data(),
39+
static_cast<int>(m_data.size()),
40+
transferFinishedCb,
41+
m_cb_data.get(),
42+
100);
43+
44+
libusb_submit_transfer(m_in_transfer);
2145
}
2246

2347
BulkReader::~BulkReader() {
48+
if (m_in_transfer) {
49+
auto status = m_in_transfer->status;
50+
51+
qDebug() << "BulkReader waiting for libusb_transfer termination";
52+
while (true) {
53+
if (status == LIBUSB_TRANSFER_CANCELLED ||
54+
status == LIBUSB_TRANSFER_COMPLETED ||
55+
status == LIBUSB_TRANSFER_TIMED_OUT ||
56+
status == LIBUSB_TRANSFER_ERROR ||
57+
status == LIBUSB_TRANSFER_NO_DEVICE) {
58+
break;
59+
}
60+
}
61+
62+
qDebug() << "Stopping BulkReader";
63+
libusb_free_transfer(m_in_transfer);
64+
}
65+
}
66+
67+
void BulkReader::handleTransfer(libusb_transfer* transfer) {
68+
std::optional<Trace> trace;
69+
70+
switch (transfer->status) {
71+
case LIBUSB_TRANSFER_COMPLETED:
72+
case LIBUSB_TRANSFER_TIMED_OUT:
73+
trace.emplace("BulkReader process packet");
74+
if (transfer->actual_length > 0) {
75+
QByteArray byteArray(reinterpret_cast<char*>(transfer->buffer),
76+
transfer->actual_length);
77+
emit incomingData(byteArray, mixxx::Time::elapsed());
78+
}
79+
if (!m_stop) {
80+
libusb_submit_transfer(transfer);
81+
}
82+
break;
83+
case LIBUSB_TRANSFER_CANCELLED:
84+
trace.emplace("BulkReader transfer cancelled");
85+
break;
86+
case LIBUSB_TRANSFER_ERROR:
87+
trace.emplace("BulkReader transfer error");
88+
break;
89+
case LIBUSB_TRANSFER_NO_DEVICE:
90+
case LIBUSB_TRANSFER_STALL:
91+
trace.emplace("BulkReader transfer stall");
92+
break;
93+
case LIBUSB_TRANSFER_OVERFLOW:
94+
trace.emplace("BulkReader transfer overflow");
95+
break;
96+
}
2497
}
2598

2699
void BulkReader::stop() {
27100
m_stop = 1;
101+
102+
if (m_in_transfer) {
103+
qDebug() << "Cancelling bulk transfer";
104+
libusb_cancel_transfer(m_in_transfer);
105+
}
28106
}
29107

30108
void BulkReader::run() {
31109
m_stop = 0;
32-
unsigned char data[255];
33-
34-
while (m_stop.loadAcquire() == 0) {
35-
// Blocked polling: The only problem with this is that we can't close
36-
// the device until the block is released, which means the controller
37-
// has to send more data
38-
//result = hid_read_timeout(m_pHidDevice, data, 255, -1);
39-
40-
// This relieves that at the cost of higher CPU usage since we only
41-
// block for a short while (500ms)
42-
int transferred;
43-
int result;
44-
45-
result = libusb_bulk_transfer(m_phandle,
46-
m_in_epaddr,
47-
data, sizeof(data),
48-
&transferred, 500);
49-
Trace timeout("BulkReader timeout");
50-
if (result >= 0) {
51-
Trace process("BulkReader process packet");
52-
//qDebug() << "Read" << result << "bytes, pointer:" << data;
53-
QByteArray byteArray(reinterpret_cast<char*>(data), transferred);
54-
emit incomingData(byteArray, mixxx::Time::elapsed());
55-
}
110+
111+
while (!m_stop.loadAcquire()) {
112+
libusb_handle_events(m_context);
56113
}
114+
57115
qDebug() << "Stopped Reader";
58116
}
59117

@@ -314,7 +372,7 @@ int BulkController::open(const QString& resourcePath) {
314372
<< "doesn't require reading the data. Ignoring BulkReader "
315373
"setup.";
316374
} else {
317-
m_pReader = new BulkReader(m_phandle, m_inEndpointAddr);
375+
m_pReader = new BulkReader(m_phandle, m_context, m_inEndpointAddr);
318376
m_pReader->setObjectName(QString("BulkReader %1").arg(getName()));
319377

320378
connect(m_pReader, &BulkReader::incomingData, this, &BulkController::receive);

src/controllers/bulk/bulkcontroller.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,35 @@
1010
#include "controllers/controller.h"
1111
#include "controllers/hid/legacyhidcontrollermapping.h"
1212

13+
struct bulk_transfer_cb_data;
1314
struct libusb_device_handle;
15+
struct libusb_transfer;
1416
struct libusb_context;
1517

1618
/// USB Bulk controller backend
1719
class BulkReader : public QThread {
1820
Q_OBJECT
1921
public:
20-
BulkReader(libusb_device_handle *handle, unsigned char in_epaddr);
21-
virtual ~BulkReader();
22+
BulkReader(libusb_device_handle* handle, libusb_context* context, std::uint8_t in_epaddr);
23+
~BulkReader() override;
2224

2325
void stop();
26+
void handleTransfer(libusb_transfer* transfer);
2427

2528
signals:
2629
void incomingData(const QByteArray& data, mixxx::Duration timestamp);
2730

2831
protected:
29-
void run();
32+
void run() override;
3033

3134
private:
32-
libusb_device_handle* m_phandle;
3335
QAtomicInt m_stop;
34-
unsigned char m_in_epaddr;
36+
37+
libusb_transfer* m_in_transfer;
38+
libusb_context* m_context;
39+
40+
std::unique_ptr<bulk_transfer_cb_data> m_cb_data;
41+
std::array<std::uint8_t, 255> m_data;
3542
};
3643

3744
class BulkController : public Controller {

0 commit comments

Comments
 (0)