qt - libusb 连接到设备正常,中断传输导致 LIBUSB_ERROR_IO
问题描述
我创建了一个基于 STM32 的 USB 设备,它在原型设计期间与 Python 完美配合。我目前正在尝试将其移植到 C++/Qt 进行生产。我已经将 libusb 与 Qt 集成,并且可以成功打开和关闭设备并读取设备的字符串和描述符。但是,当我尝试向设备发送任何中断 OUT 传输或接收中断 IN 传输时,设备上什么都没有,应用程序中出现 ERROR_IO (-1)。
#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;
}
解决方案
问题最终出在设备端的 HID 描述符上,而不是 libusb 代码。这是最终使用上述代码的 HID 描述符:
__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
};
推荐阅读
- android - 在Kotlin中将数据从recyclerview发送到没有intarface的片段
- python - 使用 bs4 抓取后无法使用 sqlite3 插入外键
- java - 如何获取第一个谷歌搜索结果的 URL?
- git - 为什么在 Visual Studio Code 中执行 git pull 后有未提交的更改?
- wordpress - 如何安装多站点 WordPress,例如 www.mysite.com/de 而不是 www.de.mysite.com
- haskell - 类型参数作为数据/值构造函数
- haskell - 通过 `TypeError` 约束消除
- postgresql - 有人可以告诉我为什么会出现此错误,是因为间距(我知道引号很重要)吗?
- cassandra - 理解 Cassandra 中主键和分区之间的关系
- stripe-payments - Stripe 从会话中获取名称