Lazy loaded image
USB专题05 - 开源USB通信库(libusb)详解
Words 12746Read Time 32 min
2025-4-27
 

1. 简介

libusb是一个开源的跨平台USB通信库,它为应用程序提供了一套与USB设备进行通信的API接口,让开发者能够在应用层直接访问和控制USB设备,而无需编写复杂的设备驱动程序(详细查看2. 与传统 Linux USB访问模型对比
libusb的设计核心是为USB设备通信提供一个简单、统一且跨平台的编程接口,主要解决以下问题:
  1. 消除平台差异:屏蔽Windows、Linux、macOS等不同操作系统的底层USB实现差异
  1. 简化开发流程:无需编写内核态驱动,直接在用户空间操作USB设备
  1. 统一访问模型:提供一致的API处理各种USB传输类型(控制、批量、中断、同步)
  1. 灵活的设备管理:支持设备发现、热插拔通知、设备特性查询等功能

版本演进

  • 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主要替换/绕过了以下部分:
  1. 设备特定的内核驱动程序:传统方式中,每种USB设备都需要有一个对应的设备驱动程序(紫色部分),而libusb使用通用的接口直接访问设备。
  1. 设备文件接口层:传统方式使用特定的字符设备节点(如/dev/ttyUSB0),而libusb则通过USB设备文件系统(/dev/bus/usb)直接访问(橙色部分)。

主要区别

  1. 驱动开发需求
      • 传统方式:需要为特定设备开发和维护内核驱动
      • libusb方式:无需开发设备驱动,在用户空间实现设备通信逻辑
  1. 灵活性与开发效率
      • 传统方式:需要Linux内核开发经验,开发周期长
      • libusb方式:使用高级语言在用户空间开发,开发周期短
  1. 跨平台能力
      • 传统方式:驱动与Linux内核绑定,跨平台需重写
      • libusb方式:可跨平台使用(支持Linux、Windows、macOS等)
  1. 权限模型
      • 传统方式:通过udev规则和驱动权限控制访问
      • libusb方式:需要/dev/bus/usb设备节点的访问权限
  1. 性能考虑
      • 传统方式:内核空间直接处理,理论上性能更高
      • 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的实现围绕几个关键数据结构:
  1. libusb_context:表示库的全局上下文,管理所有其他资源
  1. libusb_device:表示一个USB设备
  1. libusb_device_handle:表示一个已打开的USB设备连接
  1. libusb_transfer:表示一个USB传输请求
  1. libusb_device_descriptor:设备描述符
  1. libusb_config_descriptor:配置描述符

函数调用流程 - 同步操作

一个典型的libusb应用程序的函数调用序列如下:

函数调用流程 - 异步操作

libusb支持异步操作,这在处理多个USB设备或高吞吐量场景中非常重要:

4. 用法和API接口

4.1 用法流程

可以通过libusb访问USB设备,不需要USB设备端的驱动程序,需要移除原来的驱动程序。然后就可以直接访问USB控制器的驱动程序,使用open/read/write/ioctl/close这些接口来打开设备、收发数据、关闭设备。
libusb封装了更好用的函数,这些函数的使用可以分为5个步骤:
  • 初始化
  • 打开设备
  • 移除原驱动/认领接口
  • 传输
  • 关闭设备
notion image

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打开设备

如果知道设备的VIDPID,那么可以使用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需要注意以下几点:
      1. libusb的上下文(context)是线程安全的
      1. 不同线程可以使用相同或不同的上下文
      1. 多线程中的事件处理需要特别注意
      1. 使用异步传输和线程可以提高性能

      跨平台注意事项

      虽然libusb旨在提供跨平台的API,但开发者仍需注意不同平台的特殊情况:
      1. Windows:可能需要安装驱动程序(WinUSB、libusb-win32或libusbK)
      1. Linux:可能需要udev规则来设置设备权限
      1. macOS:可能需要特殊权限处理

      8. 总结

      libusb是一个功能强大、设计良好的USB通信库,它通过抽象USB通信的复杂性,为开发者提供了一个简单、统一且跨平台的编程接口。其关键特点包括:
      1. 跨平台兼容性:在Windows、Linux、macOS等多个操作系统上提供一致的API
      1. 全面的USB支持:支持各种USB传输类型和协议功能
      1. 灵活的编程模型:同时支持同步和异步操作
      1. 热插拔支持:可以动态检测USB设备的连接和断开
      1. 精心设计的API:易于使用且功能强大
      通过正确使用libusb,开发者可以创建可靠、高效的USB应用程序,而无需深入了解各操作系统的底层USB实现细节。
      无论是开发简单的USB通信应用,还是构建复杂的USB设备控制系统,libusb都提供了必要的工具和API,使开发过程变得更加简单和高效。
      上一篇
      模板设计模式:让你的代码结构更清晰
      下一篇
      Guide to Linux System

      Comments
      Loading...