Rookie Question
fredkelly opened this issue · 0 comments
fredkelly commented
Hello there,
Firstly, apologies if this is not the appropriate forum for implementation questions. I'm trying to use the library to create a ruby translation of the following C++ library:
void UsbDownloader::run()
{
bool hasError = true;
#ifndef USB_READ_DUMP
libusb_device_handle* handle = 0;
do { // Error loop
int r;
// Open USB device
handle = libusb_open_device_with_vid_pid(ctx, BSM_VID, BSM_PID);
if (!handle) {
qCritical() << "Failed to open the device";
break;
}
qDebug() << "USB device opened";
// Detach kernel driver
if (libusb_kernel_driver_active(handle, USB_INTERFACE_IN)) {
qDebug() << "Detaching kernel driver...";
r = libusb_detach_kernel_driver(handle, USB_INTERFACE_IN);
if (r < 0) {
qCritical() << "libusb_detach_kernel_driver error" << r;
break;
}
qDebug() << "Kernel driver detached";
}
// Claim interface
qDebug() << "Claiming interface...";
r = libusb_claim_interface(handle, USB_INTERFACE_IN);
if (r < 0) {
qCritical() << "usb_claim_interface error" << r;
break;
}
qDebug() << "Interface claimed";
// Prepare to receive data
qDebug() << "Register for interrupt data";
libusb_transfer *transfer_receive = libusb_alloc_transfer(0);
unsigned char buffer_receive[8];
UsbDownloaderData usb_data;
#ifdef USB_WRITE_DUMP
usb_data.dump.setFileName(USB_WRITE_DUMP);
usb_data.dump.open(QIODevice::WriteOnly | QIODevice::Truncate);
#endif
libusb_fill_interrupt_transfer(transfer_receive, handle, LIBUSB_ENDPOINT_IN | USB_INTERFACE_OUT, buffer_receive, sizeof(buffer_receive), cb_in, &usb_data, 30000);
r = libusb_submit_transfer(transfer_receive);
if (r < 0) {
qCritical() << "libusb_submit_transfer error" << r;
break;
}
// Prepare to send request
qDebug() << "Send control request";
libusb_transfer *transfer_send = libusb_alloc_transfer(0);
unsigned char buffer_send[LIBUSB_CONTROL_SETUP_SIZE + USB_CTRL_DATA_LEN] __attribute__ ((aligned (2)));
libusb_fill_control_setup(buffer_send, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, USB_CTRL_REQUEST, USB_CTRL_VALUE, 0, USB_CTRL_DATA_LEN);
buffer_send[LIBUSB_CONTROL_SETUP_SIZE] = USB_CTRL_DATA_FIRST;
memset(buffer_send + LIBUSB_CONTROL_SETUP_SIZE + 1, 0, USB_CTRL_DATA_LEN - 1);
libusb_fill_control_transfer(transfer_send, handle, buffer_send, cb_out, 0, 3000);
r = libusb_submit_transfer(transfer_send);
if (r < 0) {
qCritical() << "libusb_submit_transfer error" << r;
break;
}
// Wait for completion
while (!usb_data.completed) {
r = libusb_handle_events_completed(ctx, 0);
emit progress(100 * usb_data.data.size() / USB_EXPECTED_LEN);
if (r < 0)
break;
}
#ifdef USB_WRITE_DUMP
if (usb_data.dump.isOpen())
usb_data.dump.close();
#endif
// Emit completion signal
if (usb_data.completed) {
emit completed(usb_data.data);
hasError = false;
}
} while(false);
// Close USB device
if (handle) {
libusb_release_interface(handle, USB_INTERFACE_IN);
qDebug() << "Released interface";
libusb_close(handle);
handle = 0;
qDebug() << "Closed USB device";
}
#else
do { // Error loop
QFile usb_data_file(USB_READ_DUMP);
if (!usb_data_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qCritical() << "Failed to open" << USB_READ_DUMP;
break;
}
UsbDownloaderData usb_data;
#ifdef USB_WRITE_DUMP
usb_data.dump.setFileName(USB_WRITE_DUMP);
usb_data.dump.open(QIODevice::WriteOnly | QIODevice::Truncate);
#endif
while (!usb_data_file.atEnd()) {
char buff[20];
qint64 s = usb_data_file.readLine(buff, 20);
if (s < 16 || s > 17)
break;
if (s == 17 && buff[16] != '\n')
break;
libusb_transfer t;
unsigned char b[8];
bool ok;
t.buffer = b;
t.actual_length = 8;
t.status = LIBUSB_TRANSFER_ERROR; // ignored as error, to avoid resubmit
t.user_data = &usb_data;
for(int i = 0; i < 8; ++i) {
b[i] = (unsigned char) QString("%1%2").arg(buff[i * 2]).arg(buff[i * 2 + 1]).toUShort(&ok, 16);
if (!ok)
break;
}
if (!ok)
break;
cb_in(&t);
emit progress(100 * usb_data.data.size() / USB_EXPECTED_LEN);
}
#ifdef USB_WRITE_DUMP
if (usb_data.dump.isOpen())
usb_data.dump.close();
#endif
if (usb_data_file.atEnd()) {
emit completed(usb_data.data);
hasError = false;
}
usb_data_file.close();
} while(false);
#endif
// Emit error signal
if (hasError)
emit error();
}
I understand that I need to initiate a control_transfer
to set up the transfer, and then use interrupt_transfer
for the passing of data from my device. My current implementation looks something like this:
usb = LIBUSB::Context.new
@device = usb.devices(idVendor: BSM_VID, idProduct: BSM_PID).first
@device.open do |device|
# setup interrupt transfer (ready to receive)
device.claim_interface(0) do |handle|
handle.control_transfer(
bmRequestType: 0x21,
bRequest: USB_CTRL_REQUEST,
wValue: USB_CTRL_VALUE,
wIndex: 0x000,
dataOut: '\x10\x00\x00\x00\x00\x00\x00\x00'
) do |result|
puts "control_transfer result: #{result}"
end
handle.interrupt_transfer(
endpoint: @device.endpoints.first,
dataIn: 8
)
end
end
This yields a TRANSFER_STALL
in the control_transfer
block and raises error TRANSFER_TIMED_OUT (LIBUSB::ERROR_TIMEOUT)
.
I'm hoping someone might be able to shed some light on how to correctly use the control_transfer
and interrupt_transfer
methods of LIBUSB::DevHandle
in tandem?
Really appreciate the help!