USB 原始 Gadget

USB 原始 Gadget 是一种 gadget 驱动程序,它为用户空间提供了对 gadget 通信过程的底层控制。

像任何其他 gadget 驱动程序一样,原始 Gadget 通过 USB gadget API 实现 USB 设备。与大多数 gadget 驱动程序不同,原始 Gadget 本身不实现任何具体的 USB 功能,而是需要用户空间来完成。

原始 Gadget 目前严格来说是一个调试功能,不应在生产中使用。请改用 GadgetFS。

通过 CONFIG_USB_RAW_GADGET 启用。

与 GadgetFS 的比较

原始 Gadget 与 GadgetFS 类似,但为用户空间提供了对 USB gadget 层的更直接访问。主要区别在于

  1. 原始 Gadget 将每个 USB 请求传递给用户空间以获取响应,而 GadgetFS 根据提供的描述符在内部响应某些 USB 请求。请注意,UDC 驱动程序可能会自行响应某些请求,并且永远不会将它们转发到 gadget 层。

  2. 原始 Gadget 允许提供任意数据作为对 USB 请求的响应,而 GadgetFS 对提供的 USB 描述符执行健全性检查。这使得原始 Gadget 适合通过提供格式错误的数据作为对 USB 请求的响应来进行模糊测试。

  3. 原始 Gadget 提供了一种选择要绑定的 UDC 设备/驱动程序的方法,而 GadgetFS 当前绑定到第一个可用的 UDC。这允许将多个原始 Gadget 实例绑定到不同的 UDC。

  4. 原始 Gadget 显式地公开有关端点地址和功能的信息。这允许用户编写与 UDC 无关的 gadget。

  5. 原始 Gadget 具有基于 ioctl 的接口,而不是基于文件系统的接口。

用户空间接口

用户可以通过打开 /dev/raw-gadget 并发出 ioctl 调用与原始 Gadget 交互;有关详细信息,请参见 include/uapi/linux/usb/raw_gadget.h 中的注释。可以同时使用多个原始 Gadget 实例(绑定到不同的 UDC)。

原始 Gadget 的典型使用场景

  1. 通过打开 /dev/raw-gadget 创建一个原始 Gadget 实例。

  2. 通过 USB_RAW_IOCTL_INIT 初始化该实例。

  3. 使用 USB_RAW_IOCTL_RUN 启动该实例。

  4. 在一个循环中,发出 USB_RAW_IOCTL_EVENT_FETCH 以接收来自原始 Gadget 的事件,并根据必须实现的 USB gadget 的类型对这些事件做出反应。

请注意,某些 UDC 驱动程序为端点分配了固定的地址,因此不能在描述符中使用任意端点地址。尽管如此,原始 Gadget 提供了一种与 UDC 无关的方式来编写 USB gadget。一旦通过 USB_RAW_IOCTL_EVENT_FETCH 接收到 USB_RAW_EVENT_CONNECT,就可以使用 USB_RAW_IOCTL_EPS_INFO 来查找有关 UDC 驱动程序具有的端点的信息。基于此,用户空间必须为 gadget 选择 UDC 端点,并在端点描述符中相应地分配地址。

原始 Gadget 用法示例和测试套件

https://github.com/xairy/raw-gadget

内部细节

每个原始 Gadget 端点读取/写入 ioctl 都会提交一个 USB 请求并等待其完成。这样做是故意的,目的是通过让单个 syscall 完全处理单个 USB 请求来协助覆盖引导的模糊测试。此功能必须保留在实现中。

潜在的未来改进

  • 支持 O_NONBLOCK I/O。这将是另一种操作模式,其中原始 Gadget 不会等到每个 USB 请求完成。

  • 支持 USB 3 功能(在启用端点时接受 SS 端点伴侣描述符;允许为批量传输提供 stream_id)。

  • 支持 ISO 传输功能(公开已完成请求的 frame_number)。