如果你正在你的应用程序中使用libusb,你可能想使用执行设备I/O 操作——你想执行USB数据传输。
Libusb提供2种独立的设备I/O接口。本页目的在于介绍这2种方式来帮助你决定哪一个更适合你的应用程序。你也可以选择同时使用这2种接口,通过对于不同传输的需求使用合适的接口。
一但你通读完了下面讨论的内容,你应该查阅以下详细的API文档获得更多的信息:
在逻辑层次上传输数据
在逻辑层次上,USB传输由2部分组成。例如:当从端点读取数据:
- 一个数据请求被发送到设备。
- 一段时间后,读入数据被主机接收。
或者向端点写入数据时:
- 数据被发送到设备。
- 一段时间后,主机接收到来自设备的应答,告知主机数据已经传输完毕。
2个步骤之间的延时是不确定的。 假设一个有一个用户可以按下按键的USB输入设备,为了判断什么时候按键被按下,你应该在块端点或者中断端点提交一个读数据请求,并且等待数据到达。数据将会在用户按下按键后到达,但这也许会发生在几个小时之后。
Libusb同时提供了同步和异步的接口来执行USB传输操作。 主要的不同在于:同步接口将上述提到的步骤集成到了一个单独的函数调用中,而异步是分开的。
同步接口
同步I/O接口允许你通过一个单独的函数调用执行USB传输操作。当函数调用返回时,传输完成并且你可以处理返回值。
如果你已经使用过libusb-0.1,这种I/O 操作方式对你来说应该是熟悉的。 libusb-0.1 只提供了同步接口。
在我们的输入设备的例子中,读取按键你也许会使用下面的方式:
unsigned char data[4];
int actual_length,
int r = libusb_bulk_transfer(handle, EP_IN, data, sizeof(data), &actual_length, 0);
if (r == 0 && actual_length == sizeof(data)) {
// results of the transaction can now be found in the data buffer
// parse them here and report button press
} else {
error();
}
这种模式的主要优势就是简单:你只需要一个函数调用就可以完成所有的事情。
但是,这种方式存在着一定的限制。 你的应用程序将会阻塞在libusb_bulk_transfer() ,知道传输完成。如果等待按键花费3个小时,你的应用程序将也会阻塞那么长时间。执行将被阻塞在库中——这个线程在这段时间是不可用的。
另一个值得注意的问题是,使用单个传输函数阻塞线程方式是不可以对多个端点和/或多个设备同时执行I/O操作的,除非你通过为每个传输都创建一个线程。
而且,在提交请求后也没有办法取消传输。
关于更多如何使用同步API内容,详见同步I/O API 文档 页面.
异步接口
异步I/O是libusb-1.0最重要的新特性。虽然它是一个更复杂的接口,但是它解决了上面说到的问题。
代替那些直到I/O操作结束的阻塞函数,libusb的异步接口提供非阻塞函数,调用后会立即返回。你的应用程序传递一个回调函数给非阻塞函数,当传输结束的时候,libusb将会使用传输结果调用这个函数。
通过调用非阻塞函数提交的传输可以被一个分离出来的函数调用取消。
这项接口的非阻塞特性允许你在不使用多线程的情况下,同时执行多个设备上的多个端点的I/O操作。
增加的灵活性同样带来了一下的一些问题:
- 为了保持作为一个轻量级的库,libusb并不创建线程而且只能在你的应用程序调用它的时候才能执行操作。当事件准备好被处理的时候,你的应用程序必须从它的主循环调用,或者你必须使用一些其他的模式允许libusb接管,无论什么工作需要完成。
- 为了准确的处理传输超时,libusb也需要及时在某些固定位置被调用。
- 内存处理变得更复杂。你不能使用栈内存除非使用栈的函数确定是直到传输回调结束执行才返回。
- 你将会在你的代码中损失一些线性结构,因为提交传输请求已经在一个传输结果被处理的分离函数中完成。尤其当你想提交第二次传输基于之前传输的结果,这回变得更明显。
在内部, libusb同步接口是通过调用异步接口实现的。
关于更多如何使用异步API的内容,请见异步 I/O API 文档页。