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