当前位置: 首页 > 工具软件 > WinUSB > 使用案例 >

基于WinUSB的异步方式bulk传输的稳定性问题

张姚石
2023-12-01

某项目中,设备与PC之间通过USB Bulk模式进行数据传输,PC端的APP跑在Win10上,跟设备通信这部分原本是基于libusb开发的,运行稳定。后来考虑到PC端APP只有for Windows一个版本,使用libusb的意义不大(libusb最大的好处是跨平台),因此打算去掉libusb,直接基于微软WinUSB实现与设备通信(在Windows上libusb缺省也是基于WinUSB实现的)。

libusb中用于bulk通信的API函数libusb_bulk_transfer有一个timeout参数,去掉libusb后直接调用WinUSB的API函数WinUsb_ReadPipe和WinUsb_WritePipe来实现bulk读写,但是这两个函数没有timeout参数,缺省方式调用这两个函数如果没有完成操作(例如WinUsb_ReadPipe一直读不到数据)就会无限期等待而不返回,这不符合我们的要求。不过这两个函数都提供了一个Overlapped参数用于实现异步操作方式,标准调用流程如下(以bulk读为例):

DWORD WinUSBBulkRead(WINUSB_INTERFACE_HANDLE hWinusb, UCHAR endpoint, UCHAR *data, DWORD length, DWORD timeout)
{
    DWORD ret = ERROR_SUCCESS;
    OVERLAPPED ov;
    DWORD n;

	ResetEvent(&eventReadPipe);
	memset(&ov, 0, sizeof(ov));
	ov.hEvent = eventReadPipe;

    if (!WinUsb_ReadPipe(hWinusb, endpoint, data, length, NULL, &ov)) {
        ret = GetLastError();
        if (ret == ERROR_IO_PENDING) {
            ret = WaitForSingleObject(eventReadPipe, timeout);
            if (ret == WAIT_OBJECT_0) {
                ret = ERROR_SUCCESS;
            }
            else if (ret == WAIT_TIMEOUT) {
                ret = ERROR_TIMEOUT;
            }
            else {
                ret = GetLastError();
            }
        }
    }

    if (ret == ERROR_SUCCESS) {
        if (!WinUsb_GetOverlappedResult(hWinusb, &ov, &n, FALSE)) {
            ret = GetLastError();
        }
    }

    return ret;
}

然而上面这段代码在实际运行中发现不稳定,跑一段时间以后经常会出现接收超时,网上查到的参考代码也都是这么写的,不知道问题在哪里。查看libusb源码中调用WinUSB的部分,在调用WinUsb_ReadPipe和WinUsb_WritePipe时用的也是Overlapped方式,但对timeout的处理不太一样,因代码太复杂,没有时间仔细看。这时在微软文档上看到WinUsb_SetPipePolicy可以设置的Policy里面有一个PIPE_TRANSFER_TIMEOUT,之前初始化WinUSB调用WinUsb_SetPipePolicy设置Policy时这个PIPE_TRANSFER_TIMEOUT设的是0,从文档描述看这个值如果设成非0就可以实现我们想要的timeout的效果,而WinUsb_ReadPipe和WinUsb_WritePipe那里不再使用Overlapped方式,代码也因此更简单。试了一下果然可以,而且运行很稳定。

微软文档里对将PIPE_TRANSFER_TIMEOUT设成非0值有一个说明:
A minor performance penalty will occur due to timer management.
但是实际测下来的性能比之前用libusb还要更好一些,能满足我们的要求。

 

 类似资料: