Search code examples
qtusblibusblibusb-1.0

libusb connects to device fine, interrupt transfer results in LIBUSB_ERROR_IO


I have created a USB device based on STM32 which works perfectly with Python during prototyping. I am currently attempting to port it to C++/Qt for production. I have integrated libusb with Qt and can successfully open and close the device and read the device's strings and descriptors. However, when I attempt to send any interrupt OUT transfer to the device or receive an interrupt IN transfer, I get nothing on the device and an ERROR_IO (-1) in my application.

#define HIL_USB_VID     0x0483
#define HIL_USB_PID     0x572b
#define HIL_USB_OUT_EP  0x01
#define HIL_USB_IN_EP   0x81
#define HIL_PKT_LENGTH  64
#define HIL_PKT_TIMEOUT 100
#define USB_CONFIG_NUM  1
#define USB_IFACE_NUM   0

HILInterface::HILInterface()
{
    this->handle = NULL;
    this->worker = NULL;
}

HILInterface::~HILInterface()
{
    this->disconnect();
}

bool HILInterface::connectMjolnir() {
    if (libusb_init(NULL) < 0)
    {
        qDebug() << "Failed to initialize libusb";
        return false;
    }
    this->handle = libusb_open_device_with_vid_pid(NULL, HIL_USB_VID, HIL_USB_PID);
    if (NULL == this->handle) {
        qDebug() << "Failed to open HIL interface";
        return false;
    }
    int needToDetach = libusb_kernel_driver_active(this->handle, USB_IFACE_NUM);
    if (LIBUSB_SUCCESS == needToDetach || LIBUSB_ERROR_NOT_SUPPORTED == needToDetach) {
        qDebug() << "No need to detach HIL interface from OS";
    } else {
        if (LIBUSB_SUCCESS != libusb_detach_kernel_driver(this->handle, USB_IFACE_NUM)) {
            qDebug() << "Failed to detach HIL interface from OS";
            this->disconnect();
            return false;
        }
    }
    if (LIBUSB_SUCCESS != libusb_set_configuration(this->handle, USB_CONFIG_NUM)) {
        qDebug() << "Failed to set-configuration on HIL interface";
        this->disconnect();
        return false;
    }
    if (LIBUSB_SUCCESS != libusb_claim_interface(this->handle, USB_IFACE_NUM))
    {
        qDebug() << "Failed to claim HIL interface";
        this->disconnect();
        return false;
    }
    qDebug() << "HIL interface connected successfully";
    unsigned char strDesc[256];
    struct libusb_device_descriptor desc;
    struct libusb_config_descriptor ** conDesc = NULL;
    libusb_get_device_descriptor(libusb_get_device(this->handle), &desc);
    libusb_get_string_descriptor_ascii(this->handle, desc.iManufacturer, strDesc, 256);
    qDebug() << "HIL Manufacturer:" << (char*)strDesc;
    libusb_get_string_descriptor_ascii(this->handle, desc.iProduct, strDesc, 256);
    qDebug() << "HIL Product:" << (char*)strDesc;
    libusb_get_string_descriptor_ascii(this->handle, desc.iSerialNumber, strDesc, 256);
    qDebug() << "HIL Serial Number:" << (char*)strDesc;
    this->worker = new HILInterfaceThread();
    this->worker->moveToThread(&workerThread);
    connect(&workerThread, &QThread::finished, this->worker, &QObject::deleteLater);
    connect(this, &HILInterface::startReceiveThread, this->worker, &HILInterfaceThread::run);
    this->worker->handle = this->handle;
    workerThread.start();
    emit startReceiveThread();
    return true;
}

bool HILInterface::disconnectMjolnir() {
    if (NULL == this->handle) {
        return false;
    }
    workerThread.terminate();
    workerThread.wait();
    libusb_release_interface(this->handle, USB_IFACE_NUM);
    libusb_attach_kernel_driver(this->handle, USB_IFACE_NUM);
    libusb_close(this->handle);
    this->handle = NULL;
    libusb_exit(NULL);
    return true;
}

bool HILInterface::sendCommand(uint8_t * msgBuffer, uint16_t msgLen) {
    if (NULL == this->handle) {
        return false;
    }
    int xferCount = 0;
    int errorCode = libusb_interrupt_transfer(this->handle, HIL_USB_OUT_EP, msgBuffer, msgLen, &xferCount, HIL_PKT_TIMEOUT);
    if (LIBUSB_SUCCESS == errorCode) {
        if (HIL_PKT_LENGTH == xferCount) {
            return true;
        }
    }
    return false;
}

Solution

  • The issue ended up being with the HID descriptor on the device side, not the libusb code. Here is the HID descriptor that ended up working with the code above:

    __ALIGN_BEGIN static uint8_t USBD_HID_ReportDesc[HID_REPORT_DESC_SIZE]  __ALIGN_END =
    {
            0x06, 0x00, 0xFF,  // Usage Page (Vendor Defined 0xFF00)
            0x09, 0x01,        // Usage (0x01)
            0xA1, 0x01,        // Collection (Application)
            0x19, 0x01,        //   Usage Minimum (0x01)
            0x29, 0x40,        //   Usage Maximum (0x40)
            0x15, 0x00,        //   Logical Minimum (0)
            0x26, 0xFF, 0x00,  //   Logical Maximum (255)
            0x75, 0x08,        //   Report Size (8)
            0x95, 0x40,        //   Report Count (64)
            0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
            0x19, 0x01,        //   Usage Minimum (0x01)
            0x29, 0x40,        //   Usage Maximum (0x40)
            0x91, 0x00,        //   Output (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
            0xC0,              // End Collection
    
            // 29 bytes
    };