Skip to content
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

Support for bulk devices on Windows and Mac #13008

Merged
merged 1 commit into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(__WINDOWS__) || 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(__WINDOWS__) || 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(__WINDOWS__) || 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(__WINDOWS__) || 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(__WINDOWS__) || 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