1 #include "controllers/bulk/bulkcontroller.h"
2
3 #include <libusb.h>
4
5 #include "controllers/bulk/bulksupported.h"
6 #include "controllers/controllerdebug.h"
7 #include "controllers/defs_controllers.h"
8 #include "moc_bulkcontroller.cpp"
9 #include "util/compatibility.h"
10 #include "util/time.h"
11 #include "util/trace.h"
12
BulkReader(libusb_device_handle * handle,unsigned char in_epaddr)13 BulkReader::BulkReader(libusb_device_handle *handle, unsigned char in_epaddr)
14 : QThread(),
15 m_phandle(handle),
16 m_stop(0),
17 m_in_epaddr(in_epaddr) {
18 }
19
~BulkReader()20 BulkReader::~BulkReader() {
21 }
22
stop()23 void BulkReader::stop() {
24 m_stop = 1;
25 }
26
run()27 void BulkReader::run() {
28 m_stop = 0;
29 unsigned char data[255];
30
31 while (atomicLoadAcquire(m_stop) == 0) {
32 // Blocked polling: The only problem with this is that we can't close
33 // the device until the block is released, which means the controller
34 // has to send more data
35 //result = hid_read_timeout(m_pHidDevice, data, 255, -1);
36
37 // This relieves that at the cost of higher CPU usage since we only
38 // block for a short while (500ms)
39 int transferred;
40 int result;
41
42 result = libusb_bulk_transfer(m_phandle,
43 m_in_epaddr,
44 data, sizeof(data),
45 &transferred, 500);
46 Trace timeout("BulkReader timeout");
47 if (result >= 0) {
48 Trace process("BulkReader process packet");
49 //qDebug() << "Read" << result << "bytes, pointer:" << data;
50 QByteArray outData((char*)data, transferred);
51 emit incomingData(outData, mixxx::Time::elapsed());
52 }
53 }
54 qDebug() << "Stopped Reader";
55 }
56
get_string(libusb_device_handle * handle,u_int8_t id)57 static QString get_string(libusb_device_handle *handle, u_int8_t id) {
58 unsigned char buf[128] = { 0 };
59
60 if (id) {
61 libusb_get_string_descriptor_ascii(handle, id, buf, sizeof(buf));
62 }
63
64 return QString::fromLatin1((char*)buf);
65 }
66
BulkController(UserSettingsPointer pConfig,libusb_context * context,libusb_device_handle * handle,struct libusb_device_descriptor * desc)67 BulkController::BulkController(UserSettingsPointer pConfig,
68 libusb_context* context,
69 libusb_device_handle* handle,
70 struct libusb_device_descriptor* desc)
71 : Controller(pConfig),
72 m_context(context),
73 m_phandle(handle),
74 in_epaddr(0),
75 out_epaddr(0) {
76 vendor_id = desc->idVendor;
77 product_id = desc->idProduct;
78
79 manufacturer = get_string(handle, desc->iManufacturer);
80 product = get_string(handle, desc->iProduct);
81 m_sUID = get_string(handle, desc->iSerialNumber);
82
83 setDeviceCategory(tr("USB Controller"));
84
85 setDeviceName(QString("%1 %2").arg(product, m_sUID));
86
87 setInputDevice(true);
88 setOutputDevice(true);
89 m_pReader = nullptr;
90 }
91
~BulkController()92 BulkController::~BulkController() {
93 if (isOpen()) {
94 close();
95 }
96 }
97
presetExtension()98 QString BulkController::presetExtension() {
99 return BULK_PRESET_EXTENSION;
100 }
101
visit(const MidiControllerPreset * preset)102 void BulkController::visit(const MidiControllerPreset* preset) {
103 Q_UNUSED(preset);
104 // TODO(XXX): throw a hissy fit.
105 qWarning() << "ERROR: Attempting to load a MidiControllerPreset to an HidController!";
106 }
107
visit(const HidControllerPreset * preset)108 void BulkController::visit(const HidControllerPreset* preset) {
109 m_preset = *preset;
110 // Emit presetLoaded with a clone of the preset.
111 emit presetLoaded(getPreset());
112 }
113
matchPreset(const PresetInfo & preset)114 bool BulkController::matchPreset(const PresetInfo& preset) {
115 const QList<ProductInfo>& products = preset.getProducts();
116 for (const auto& product : products) {
117 if (matchProductInfo(product)) {
118 return true;
119 }
120 }
121 return false;
122 }
123
matchProductInfo(const ProductInfo & product)124 bool BulkController::matchProductInfo(const ProductInfo& product) {
125 int value;
126 bool ok;
127 // Product and vendor match is always required
128 value = product.vendor_id.toInt(&ok, 16);
129 if (!ok || vendor_id != value) {
130 return false;
131 }
132 value = product.product_id.toInt(&ok, 16);
133 if (!ok || product_id != value) {
134 return false;
135 }
136
137 // Match found
138 return true;
139 }
140
open()141 int BulkController::open() {
142 if (isOpen()) {
143 qDebug() << "USB Bulk device" << getName() << "already open";
144 return -1;
145 }
146
147 /* Look up endpoint addresses in supported database */
148 int i;
149 for (i = 0; bulk_supported[i].vendor_id; ++i) {
150 if ((bulk_supported[i].vendor_id == vendor_id) &&
151 (bulk_supported[i].product_id == product_id)) {
152 in_epaddr = bulk_supported[i].in_epaddr;
153 out_epaddr = bulk_supported[i].out_epaddr;
154 break;
155 }
156 }
157
158 if (bulk_supported[i].vendor_id == 0) {
159 qWarning() << "USB Bulk device" << getName() << "unsupported";
160 return -1;
161 }
162
163 // XXX: we should enumerate devices and match vendor, product, and serial
164 if (m_phandle == nullptr) {
165 m_phandle = libusb_open_device_with_vid_pid(
166 m_context, vendor_id, product_id);
167 }
168
169 if (m_phandle == nullptr) {
170 qWarning() << "Unable to open USB Bulk device" << getName();
171 return -1;
172 }
173
174 setOpen(true);
175 startEngine();
176
177 if (m_pReader != nullptr) {
178 qWarning() << "BulkReader already present for" << getName();
179 } else {
180 m_pReader = new BulkReader(m_phandle, in_epaddr);
181 m_pReader->setObjectName(QString("BulkReader %1").arg(getName()));
182
183 connect(m_pReader, &BulkReader::incomingData, this, &BulkController::receive);
184
185 // Controller input needs to be prioritized since it can affect the
186 // audio directly, like when scratching
187 m_pReader->start(QThread::HighPriority);
188 }
189
190 return 0;
191 }
192
close()193 int BulkController::close() {
194 if (!isOpen()) {
195 qDebug() << " device" << getName() << "already closed";
196 return -1;
197 }
198
199 qDebug() << "Shutting down USB Bulk device" << getName();
200
201 // Stop the reading thread
202 if (m_pReader == nullptr) {
203 qWarning() << "BulkReader not present for" << getName()
204 << "yet the device is open!";
205 } else {
206 disconnect(m_pReader, &BulkReader::incomingData, this, &BulkController::receive);
207 m_pReader->stop();
208 controllerDebug(" Waiting on reader to finish");
209 m_pReader->wait();
210 delete m_pReader;
211 m_pReader = nullptr;
212 }
213
214 // Stop controller engine here to ensure it's done before the device is
215 // closed in case it has any final parting messages
216 stopEngine();
217
218 // Close device
219 controllerDebug(" Closing device");
220 libusb_close(m_phandle);
221 m_phandle = nullptr;
222 setOpen(false);
223 return 0;
224 }
225
send(QList<int> data,unsigned int length)226 void BulkController::send(QList<int> data, unsigned int length) {
227 Q_UNUSED(length);
228 QByteArray temp;
229
230 foreach (int datum, data) {
231 temp.append(datum);
232 }
233 send(temp);
234 }
235
send(const QByteArray & data)236 void BulkController::send(const QByteArray& data) {
237 int ret;
238 int transferred;
239
240 // XXX: don't get drunk again.
241 ret = libusb_bulk_transfer(m_phandle, out_epaddr,
242 (unsigned char *)data.constData(), data.size(),
243 &transferred, 0);
244 if (ret < 0) {
245 qWarning() << "Unable to send data to" << getName()
246 << "serial #" << m_sUID;
247 } else {
248 controllerDebug(ret << "bytes sent to" << getName()
249 << "serial #" << m_sUID);
250 }
251 }
252