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

Rookie Question #25

Open
fredkelly opened this issue May 14, 2017 · 0 comments
Open

Rookie Question #25

fredkelly opened this issue May 14, 2017 · 0 comments

Comments

@fredkelly
Copy link

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant