1. 简介版本演进应用领域2. 与传统 Linux USB访问模型对比主要区别接口差异对比3. 实现细节libusb核心数据结构函数调用流程 - 同步操作函数调用流程 - 异步操作4. 用法和API接口4.1 用法流程4.2 接口分类4.3 主要API接口4.3.1 初始化/反初始化4.3.2 获取设备4.3.3 打开/关闭设备4.3.4 根据ID打开设备4.3.5 描述符相关函数4.3.5.1 获得设备描述符4.3.5.2 获得/释放配置描述符4.3.6 detach/attach驱动4.3.6.1 两种方法4.3.6.2 函数原型4.3.7 同步传输函数4.3.7.1 控制传输4.3.7.2 批量传输4.3.7.3 中断传输4.3.8 异步传输函数4.3.8.1 使用步骤4.3.8.2 分配transfer结构体4.3.8.3 填充控制传输4.3.8.4 填充批量传输4.3.8.5 填充中断传输4.3.8.6 填充实时传输4.3.8.7 提交传输4.3.8.8 处理事件4.3.8.9 释放transfer结构体5. 用法示例5.1 基本用法示例5.2 异步传输示例5.3 热插拔监控示例7. 真实示例7.1 dnw工具7.2 openocd7.2.1 找到设备3.2.2 读写数据8. 实用技巧与最佳实践调试与日志错误处理多线程考虑跨平台注意事项8. 总结
1. 简介
libusb是一个开源的跨平台USB通信库,它为应用程序提供了一套与USB设备进行通信的API接口,让开发者能够在应用层直接访问和控制USB设备,而无需编写复杂的设备驱动程序(详细查看2. 与传统 Linux USB访问模型对比 )。
libusb的设计核心是为USB设备通信提供一个简单、统一且跨平台的编程接口,主要解决以下问题:
- 消除平台差异:屏蔽Windows、Linux、macOS等不同操作系统的底层USB实现差异
- 简化开发流程:无需编写内核态驱动,直接在用户空间操作USB设备
- 统一访问模型:提供一致的API处理各种USB传输类型(控制、批量、中断、同步)
- 灵活的设备管理:支持设备发现、热插拔通知、设备特性查询等功能
版本演进
- libusb-0.1:最初版本,提供了基本的USB设备访问功能。
- libusb-1.0:当前广泛使用的版本,引入了异步传输、热插拔事件通知、线程安全等特性,API设计更加合理。
- libusb-win32:专为Windows平台设计的libusb实现。
- libusbx:2012年出现的一个分支,后来与libusb合并。
应用领域
- 开发跨平台USB设备驱动和应用
- 硬件测试与调试工具
- 自定义USB设备的控制和通信
- 逆向工程和USB协议分析
- USB设备固件更新工具
2. 与传统 Linux USB访问模型对比
libusb与传统的Linux USB设备访问方式最大的不同在于:libusb提供了一种在用户空间直接访问USB设备的方法,无需为每个特定设备开发内核驱动程序。
下面是两种访问方式的对比图:
从图中可以看出,libusb主要替换/绕过了以下部分:
- 设备特定的内核驱动程序:传统方式中,每种USB设备都需要有一个对应的设备驱动程序(紫色部分),而libusb使用通用的接口直接访问设备。
- 设备文件接口层:传统方式使用特定的字符设备节点(如/dev/ttyUSB0),而libusb则通过USB设备文件系统(/dev/bus/usb)直接访问(橙色部分)。
主要区别
- 驱动开发需求:
- 传统方式:需要为特定设备开发和维护内核驱动
- libusb方式:无需开发设备驱动,在用户空间实现设备通信逻辑
- 灵活性与开发效率:
- 传统方式:需要Linux内核开发经验,开发周期长
- libusb方式:使用高级语言在用户空间开发,开发周期短
- 跨平台能力:
- 传统方式:驱动与Linux内核绑定,跨平台需重写
- libusb方式:可跨平台使用(支持Linux、Windows、macOS等)
- 权限模型:
- 传统方式:通过udev规则和驱动权限控制访问
- libusb方式:需要/dev/bus/usb设备节点的访问权限
- 性能考虑:
- 传统方式:内核空间直接处理,理论上性能更高
- libusb方式:用户空间与内核空间交互有一定开销
接口差异对比
功能 | 传统设备驱动接口 | libusb接口 | libusb的解决方案 |
设备打开 | open("/dev/myusb", O_RDWR) | libusb_open(dev, &handle) | 通过特定VID/PID/序列号直接定位设备,而非依赖固定设备节点 |
设备关闭 | close(fd) | libusb_close(handle) | 增加设备句柄资源管理功能 |
读取数据 | read(fd, buffer, length) | libusb_bulk_transfer()libusb_interrupt_transfer() | 明确区分不同传输类型,并提供更多参数控制(如终端地址、超时等) |
写入数据 | write(fd, buffer, length) | libusb_bulk_transfer()libusb_interrupt_transfer() | 同上,按USB规范区分传输类型 |
控制命令 | ioctl(fd, CMD_CODE, &data) | libusb_control_transfer() | 遵循USB标准请求格式,统一控制传输处理 |
异步操作 | 依赖于驱动实现或非阻塞模式 | libusb_submit_transfer()libusb_handle_events() | 提供完整的异步传输框架,包含回调机制 |
设备发现 | 通过设备节点名称猜测 | libusb_get_device_list()libusb_get_device_descriptor() | 提供USB总线枚举功能,可查询所有设备属性 |
配置访问 | 特定ioctl命令 | libusb_get_config_descriptor()libusb_set_configuration() | 直接映射USB标准配置操作 |
接口管理 | 驱动特定接口 | libusb_claim_interface()libusb_release_interface() | 显式声明/释放接口所有权 |
描述符读取 | 驱动特定ioctl | libusb_get_*_descriptor() | 提供完整标准USB描述符访问方法 |
端点操作 | 通过ioctl或特定文件描述符 | 通过端点地址参数 | 直接使用USB标准端点寻址 |
热插拔支持 | 依赖内核事件(udev) | libusb_hotplug_register_callback() | 提供集成的热插拔事件注册回调机制 |
错误处理 | errno系统 | 返回值和 libusb_error_name() | 提供详细的错误码和文本描述 |
调试支持 | 内核日志(dmesg) | libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL) | 集成可控级别的日志系统 |
1. 设备识别与访问机制
libusb通过以下方式解决了不使用设备驱动的问题:
- 通用设备访问:使用
/dev/bus/usb
接口直接访问USB设备,无需设备专用节点
- 描述符识别:通过标准USB描述符识别设备,不依赖驱动提供的设备信息
- VID/PID定位:使用厂商ID和产品ID直接定位设备,无需预先知道设备节点名称
2. 传输类型明确区分
- 替代了通用的
read()/write()
接口,提供与USB规范一致的传输类型: libusb_control_transfer()
- 控制传输libusb_bulk_transfer()
- 批量传输libusb_interrupt_transfer()
- 中断传输libusb_*_iso_transfer()
- 同步传输
3. 设备配置与接口管理
- 增加了
libusb_claim_interface()
和libusb_set_configuration()
等接口,明确USB设备的配置过程
- 解决了传统驱动中配置混乱的问题,实现标准化的接口配置
4. 异步操作框架
- 提供完整的异步操作支持,包括:
- 传输结构分配与填充
- 传输提交与取消
- 回调通知机制
- 事件循环处理
5. 直接映射USB规范
- libusb接口设计直接映射USB规范中的概念和操作
- 不需要为每种设备设计特定的ioctl命令,使接口更加标准化和一致
libusb通过重新设计USB设备接口,成功解决了不使用设备驱动的场景需求。它不是简单地模仿文件操作接口,而是提供了一套完整映射USB规范的API,使应用程序能够直接控制USB设备的各个方面。这种设计使得开发者可以在用户空间实现完整的USB设备通信功能,虽然在某些高性能场景可能不如内核驱动方案,但对于大多数应用程序来说,libusb提供了更高的开发效率和更好的跨平台能力。
3. 实现细节
libusb核心数据结构
libusb的实现围绕几个关键数据结构:
- libusb_context:表示库的全局上下文,管理所有其他资源
- libusb_device:表示一个USB设备
- libusb_device_handle:表示一个已打开的USB设备连接
- libusb_transfer:表示一个USB传输请求
- libusb_device_descriptor:设备描述符
- libusb_config_descriptor:配置描述符
函数调用流程 - 同步操作
一个典型的libusb应用程序的函数调用序列如下:
函数调用流程 - 异步操作
libusb支持异步操作,这在处理多个USB设备或高吞吐量场景中非常重要:
4. 用法和API接口
4.1 用法流程
可以通过libusb访问USB设备,不需要USB设备端的驱动程序,需要移除原来的驱动程序。然后就可以直接访问USB控制器的驱动程序,使用open/read/write/ioctl/close这些接口来打开设备、收发数据、关闭设备。
libusb封装了更好用的函数,这些函数的使用可以分为5个步骤:
- 初始化
- 打开设备
- 移除原驱动/认领接口
- 传输
- 关闭设备

4.2 接口分类
libusb的接口函数分为两类:同步(Synchronous device I/O)、异步(Asynchronous device I/O)。
USB数据传输分为两个步骤,对于读数据,先给设备发出数据的请求,一段时间后数据返回;对于写数据,先发送数据给设备,一段时间后得到回应。
- 同步接口的核心在于把上述两个步骤放在一个函数里面。比如想去读取一个USB键盘的数据,给键盘发出读请求后,如果用户一直没有按下键盘,那么读函数会一直等待。
- 异步接口的核心在于把上述两个步骤分开:使用一个非阻塞的函数启动传输,它会立刻返回;提供一个回调函数用来处理返回结果。
同步接口的示例代码如下,在
libusb_bulk_transfer
函数内部,如果没有数据则会休眠:使用同步接口时,代码比较简单。但是无法实现"多endpoint"的操作:上一个endpoint的传输在休眠,除非使用另一个线程,否则在同一个线程内部在等待期间是无法操作另一个endpoint的。还有另一个缺点:无法取消传输。
异步接口是在libusb-1.0引入的新性能,接口更复杂,但是功能更强大。在异步接口函数里,它启动传输、设置回调函数后就立刻返回。等"读到数据"或是"得到回应"后,回调函数被调用。发起数据传输的线程无需休眠等待结果,它支持"多endpoint"的操作,也支持取消传输。
4.3 主要API接口
4.3.1 初始化/反初始化
初始化
libusb
,参数是一个"a context pointer"
的指针,如果这个参数为NULL
,则函数内部会创建一个"default context"
。所谓
"libusb context"
就是libusb
上下文,就是一个结构体,里面保存有各类信息,比如:libusb
的调试信息是否需要打印、各种互斥锁、各类链表(用来记录USB传输等等)。程序退出前,调用如下函数:
4.3.2 获取设备
可以使用
libusb_get_device_list
取出所有设备,函数接口如下:调用此函数后,所有设备的信息存入
list
,然后遍历list
,找到想操作的设备。这个函数内部会分配list的空间,所以用完后要释放掉,使用以下函数释放:4.3.3 打开/关闭设备
使用
libusb_get_device_list
得到设备列表后,可以选择里面的某个设备,然后调用libusb_open
:使用
libusb_open
函数打开USB设备后,可以得到一个句柄:libusb_device_handle
。以后调用各种数据传输函数时,就是使用libusb_device_handle
。使用完毕后,调用libusb_close关闭设备,函数原型如下:
4.3.4 根据ID打开设备
如果知道设备的
VID
、PID
,那么可以使用libusb_open_device_with_vid_pid
来找到它、打开它。这个函数的内部,先使用libusb_get_device_list
列出所有设备,然后遍历它们根据ID选出设备,接着调用libusb_open打开它,最后调用libusb_free_device_list
释放设备。libusb_open_device_with_vid_pid函数原型如下:
4.3.5 描述符相关函数
4.3.5.1 获得设备描述符
4.3.5.2 获得/释放配置描述符
4.3.6 detach/attach
驱动
4.3.6.1 两种方法
使用
libusb
访问USB
设备时,需要先移除(detach
)设备原来的驱动程序,然后认领接口(claim interface
)。有两种办法:- 方法1:
- 方法2:
4.3.6.2 函数原型
函数
libusb_detach_kernel_driver
原型如下:函数
libusb_claim_interface
原型如下:使用完USB设备后,在调用libusb_close之前,应该
libusb_release_interface
释放接口:4.3.7 同步传输函数
4.3.7.1 控制传输
4.3.7.2 批量传输
4.3.7.3 中断传输
4.3.8 异步传输函数
4.3.8.1 使用步骤
使用libusb的异步函数时,有如下步骤:
- 分配:分配一个
libusb_transfer
结构体
- 填充:填充
libusb_transfer
结构体,比如想访问哪个endpoint
、数据buffer
、长度等等
- 提交:提交
libusb_transfer
结构体启动传输
- 处理事件:检查传输的结果,调用
libusb_transfer
结构体的回调函数
- 释放:释放资源,比如释放
libusb_transfer
结构体
4.3.8.2 分配transfer结构体
4.3.8.3 填充控制传输
4.3.8.4 填充批量传输
4.3.8.5 填充中断传输
4.3.8.6 填充实时传输
4.3.8.7 提交传输
4.3.8.8 处理事件
有4个函数,其中2个没有
completed
后缀的函数过时了:4.3.8.9 释放transfer结构体
传输完毕,需要释放libusb_transfer结构体,如果要重复利用这个结构体则无需释放。
5. 用法示例
5.1 基本用法示例
下面是一个完整的使用libusb查找设备并进行控制传输的示例:
5.2 异步传输示例
下面是一个使用libusb异步传输功能的示例:
5.3 热插拔监控示例
以下示例展示如何使用libusb的热插拔功能监控USB设备的连接与断开:
7. 真实示例
在libusb源码的examples目录下有示例程序,我们也有一些真实的案列。
7.1 dnw工具
dnw是s3c2440时代的工具,使用它可以从PC给开发板传输文件。它的核心源码如下,使用同步接口进行数据传输:
7.2 openocd
openocd是一个开源的USB JTAG调试软件,它也是使用libusb,涉及USB的操作代码如下。
7.2.1 找到设备
3.2.2 读写数据
8. 实用技巧与最佳实践
调试与日志
libusb提供了内置的日志功能,可以帮助开发者调试应用程序:
错误处理
正确处理libusb的错误代码是编写可靠USB应用程序的关键:
多线程考虑
在多线程应用程序中使用libusb需要注意以下几点:
- libusb的上下文(context)是线程安全的
- 不同线程可以使用相同或不同的上下文
- 多线程中的事件处理需要特别注意
- 使用异步传输和线程可以提高性能
跨平台注意事项
虽然libusb旨在提供跨平台的API,但开发者仍需注意不同平台的特殊情况:
- Windows:可能需要安装驱动程序(WinUSB、libusb-win32或libusbK)
- Linux:可能需要udev规则来设置设备权限
- macOS:可能需要特殊权限处理
8. 总结
libusb是一个功能强大、设计良好的USB通信库,它通过抽象USB通信的复杂性,为开发者提供了一个简单、统一且跨平台的编程接口。其关键特点包括:
- 跨平台兼容性:在Windows、Linux、macOS等多个操作系统上提供一致的API
- 全面的USB支持:支持各种USB传输类型和协议功能
- 灵活的编程模型:同时支持同步和异步操作
- 热插拔支持:可以动态检测USB设备的连接和断开
- 精心设计的API:易于使用且功能强大
通过正确使用libusb,开发者可以创建可靠、高效的USB应用程序,而无需深入了解各操作系统的底层USB实现细节。
无论是开发简单的USB通信应用,还是构建复杂的USB设备控制系统,libusb都提供了必要的工具和API,使开发过程变得更加简单和高效。