Skip to content

Commit

Permalink
Support for bulk devices on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
acolombier committed Mar 29, 2024
1 parent c80f802 commit 5447ce6
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ jobs:
# also adjust the for the local Windows build setup in
# ./tools/windows_buildenv.bat
cmake_args: >-
-DBULK=OFF
-DBULK=ON
-DFFMPEG=OFF
-DHSS1394=ON
-DLOCALECOMPARE=ON
Expand Down
85 changes: 66 additions & 19 deletions src/controllers/bulk/bulkcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ BulkController::BulkController(libusb_context* context,
get_string(handle, desc->iSerialNumber))),
m_context(context),
m_phandle(handle),
in_epaddr(0),
out_epaddr(0) {
vendor_id = desc->idVendor;
product_id = desc->idProduct;
m_inEndpointAddr(0),
m_outEndpointAddr(0) {
m_vendorId = desc->idVendor;
m_productId = desc->idProduct;

manufacturer = get_string(handle, desc->iManufacturer);
product = get_string(handle, desc->iProduct);
m_manufacturer = get_string(handle, desc->iManufacturer);
m_product = get_string(handle, desc->iProduct);
m_sUID = get_string(handle, desc->iSerialNumber);

setDeviceCategory(tr("USB Controller"));
Expand Down Expand Up @@ -122,14 +122,21 @@ bool BulkController::matchProductInfo(const ProductInfo& product) {
bool ok;
// Product and vendor match is always required
value = product.vendor_id.toInt(&ok, 16);
if (!ok || vendor_id != value) {
if (!ok || m_vendorId != value) {
return false;
}
value = product.product_id.toInt(&ok, 16);
if (!ok || product_id != value) {
if (!ok || m_productId != value) {
return false;
}

#if defined(_WIN32) || defined(__APPLE__)
value = product.interface_number.toInt(&ok, 16);
if (!ok || m_InterfaceNumber != static_cast<unsigned int>(value)) {
return false;
}
#endif

// Match found
return true;
}
Expand All @@ -143,10 +150,13 @@ int BulkController::open() {
/* Look up endpoint addresses in supported database */
int i;
for (i = 0; bulk_supported[i].vendor_id; ++i) {
if ((bulk_supported[i].vendor_id == vendor_id) &&
(bulk_supported[i].product_id == product_id)) {
in_epaddr = bulk_supported[i].in_epaddr;
out_epaddr = bulk_supported[i].out_epaddr;
if ((bulk_supported[i].vendor_id == m_vendorId) &&
(bulk_supported[i].product_id == m_productId)) {
m_inEndpointAddr = bulk_supported[i].in_epaddr;
m_outEndpointAddr = bulk_supported[i].out_epaddr;
#if defined(_WIN32) || defined(__APPLE__)
m_InterfaceNumber = bulk_supported[i].interface_number;
#endif
break;
}
}
Expand All @@ -159,21 +169,46 @@ int BulkController::open() {
// XXX: we should enumerate devices and match vendor, product, and serial
if (m_phandle == nullptr) {
m_phandle = libusb_open_device_with_vid_pid(
m_context, vendor_id, product_id);
m_context, m_vendorId, m_productId);
}

if (m_phandle == nullptr) {
qCWarning(m_logBase) << "Unable to open USB Bulk device" << getName();
return -1;
}

#if defined(_WIN32) || defined(__APPLE__)
if (m_InterfaceNumber && libusb_kernel_driver_active(m_phandle, m_InterfaceNumber) == 1) {
qCDebug(m_logBase) << "Found a driver active for" << getName();
if (libusb_detach_kernel_driver(m_phandle, 0) == 0)
qCDebug(m_logBase) << "Kernel driver detached for" << getName();
else {
qCWarning(m_logBase) << "Couldn't detach kernel driver for" << getName();
libusb_close(m_phandle);
return -1;
}
}

if (m_InterfaceNumber) {
int ret = libusb_claim_interface(m_phandle, m_InterfaceNumber);
if (ret < 0) {
qCWarning(m_logBase) << "Cannot claim interface for" << getName()
<< ":" << libusb_error_name(ret);
libusb_close(m_phandle);
return -1;
} else {
qCDebug(m_logBase) << "Claimed interface for" << getName();
}
}
#endif

setOpen(true);
startEngine();

if (m_pReader != nullptr) {
qCWarning(m_logBase) << "BulkReader already present for" << getName();
} else {
m_pReader = new BulkReader(m_phandle, in_epaddr);
m_pReader = new BulkReader(m_phandle, m_inEndpointAddr);
m_pReader->setObjectName(QString("BulkReader %1").arg(getName()));

connect(m_pReader, &BulkReader::incomingData, this, &BulkController::receive);
Expand Down Expand Up @@ -212,6 +247,15 @@ int BulkController::close() {
stopEngine();

// Close device
#if defined(_WIN32) || defined(__APPLE__)
if (m_InterfaceNumber) {
int ret = libusb_release_interface(m_phandle, m_InterfaceNumber);
if (ret < 0) {
qCWarning(m_logBase) << "Cannot release interface for" << getName()
<< ":" << libusb_error_name(ret);
}
}
#endif
qCInfo(m_logBase) << " Closing device";
libusb_close(m_phandle);
m_phandle = nullptr;
Expand All @@ -234,14 +278,17 @@ void BulkController::sendBytes(const QByteArray& data) {
int transferred;

// XXX: don't get drunk again.
ret = libusb_bulk_transfer(m_phandle, out_epaddr,
(unsigned char *)data.constData(), data.size(),
&transferred, 0);
ret = libusb_bulk_transfer(m_phandle,
m_outEndpointAddr,
(unsigned char*)data.constData(),
data.size(),
&transferred,
0);
if (ret < 0) {
qCWarning(m_logOutput) << "Unable to send data to" << getName()
<< "serial #" << m_sUID;
<< "serial #" << m_sUID << "-" << libusb_error_name(ret);
} else {
qCDebug(m_logOutput) << ret << "bytes sent to" << getName()
qCDebug(m_logOutput) << transferred << "bytes sent to" << getName()
<< "serial #" << m_sUID;
}
}
15 changes: 9 additions & 6 deletions src/controllers/bulk/bulkcontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,15 @@ class BulkController : public Controller {

// Local copies of things we need from desc

unsigned short vendor_id;
unsigned short product_id;
unsigned char in_epaddr;
unsigned char out_epaddr;
QString manufacturer;
QString product;
unsigned short m_vendorId;
unsigned short m_productId;
unsigned char m_inEndpointAddr;
unsigned char m_outEndpointAddr;
#if defined(_WIN32) || defined(__APPLE__)
unsigned int m_InterfaceNumber;
#endif
QString m_manufacturer;
QString m_product;

QString m_sUID;
BulkReader* m_pReader;
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/bulk/bulkenumerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ QList<Controller*> BulkEnumerator::queryDevices() {
struct libusb_device_handle* handle = nullptr;
err = libusb_open(device, &handle);
if (err) {
qWarning() << "Error opening a device";
qWarning() << "Error opening a device:" << libusb_error_name(err);
continue;
}

Expand Down
12 changes: 6 additions & 6 deletions src/controllers/bulk/bulksupported.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ typedef struct bulk_supported {
unsigned short product_id;
unsigned char in_epaddr;
unsigned char out_epaddr;
unsigned int interface_number;
} bulk_supported_t;

static bulk_supported_t bulk_supported[] = {
{0x06f8, 0xb105, 0x82, 0x03}, // Hercules MP3e2
{0x06f8, 0xb107, 0x83, 0x03}, // Hercules Mk4
{0x06f8, 0xb100, 0x86, 0x06}, // Hercules Mk2
{0x06f8, 0xb120, 0x82, 0x03}, // Hercules MP3 LE / Glow
{0, 0, 0, 0}
};
{0x06f8, 0xb105, 0x82, 0x03, 0}, // Hercules MP3e2
{0x06f8, 0xb107, 0x83, 0x03, 0}, // Hercules Mk4
{0x06f8, 0xb100, 0x86, 0x06, 0}, // Hercules Mk2
{0x06f8, 0xb120, 0x82, 0x03, 0}, // Hercules MP3 LE / Glow
{0, 0, 0, 0, 0}};
4 changes: 3 additions & 1 deletion src/controllers/controllermappinginfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,15 @@ MappingInfo::MappingInfo(const QString& mapping_path)
}

ProductInfo MappingInfo::parseBulkProduct(const QDomElement& element) const {
// <product protocol="bulk" vendor_id="0x06f8" product_id="0x0b105" in_epaddr="0x82" out_epaddr="0x03">
// <product protocol="bulk" vendor_id="0x06f8" product_id="0x0b105"
// in_epaddr="0x82" out_epaddr="0x03" interface_number="0x04" />
ProductInfo product;
product.protocol = element.attribute("protocol");
product.vendor_id = element.attribute("vendor_id");
product.product_id = element.attribute("product_id");
product.in_epaddr = element.attribute("in_epaddr");
product.out_epaddr = element.attribute("out_epaddr");
product.interface_number = element.attribute("interface_number");
return product;
}

Expand Down
10 changes: 5 additions & 5 deletions src/controllers/controllermappinginfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ struct ProductInfo {
QString protocol;
QString vendor_id;
QString product_id;
QString interface_number;

// HID-specific
QString in_epaddr;
QString out_epaddr;

// Bulk-specific
QString usage_page;
QString usage;
QString interface_number;

// Bulk-specific
QString in_epaddr;
QString out_epaddr;
};

/// Base class handling enumeration and parsing of mapping info headers
Expand Down
2 changes: 1 addition & 1 deletion tools/windows_buildenv.bat
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ REM Generate CMakeSettings.json which is read by MS Visual Studio to determine t
CALL :AddCMakeVar2CMakeSettings_JSON "MIXXX_VCPKG_ROOT" "STRING" "!MIXXX_VCPKG_ROOT:\=\\!"
CALL :AddCMakeVar2CMakeSettings_JSON "BATTERY" "BOOL" "True"
CALL :AddCMakeVar2CMakeSettings_JSON "BROADCAST" "BOOL" "True"
CALL :AddCMakeVar2CMakeSettings_JSON "BULK" "BOOL" "False"
CALL :AddCMakeVar2CMakeSettings_JSON "BULK" "BOOL" "True"
CALL :AddCMakeVar2CMakeSettings_JSON "CMAKE_EXPORT_COMPILE_COMMANDS" "BOOL" "True"
REM Replace all \ by \\ in CMAKE_PREFIX_PATH
REM CALL :AddCMakeVar2CMakeSettings_JSON "CMAKE_PREFIX_PATH" "STRING" "!CMAKE_PREFIX_PATH:\=\\!"
Expand Down

0 comments on commit 5447ce6

Please sign in to comment.