USB for Software Developers: An introduction to writing userspace USB drivers
USB 驱动程序入门:从零开始
本文旨在为那些对硬件不太熟悉,但想了解如何使用 USB 技术的人提供一个高级介绍。本文将展示如何编写 USB 设备驱动程序,而无需深入了解内核代码的复杂性。
USB 设备
本文以 Android 手机的引导加载程序模式为例。原因如下:
- 易于获取
- 使用的协议简单且文档完善
- 通常系统上没有预安装驱动程序,避免操作系统干扰实验
进入引导加载程序模式的方法因设备而异,通常需要在手机启动时按住特定组合的按钮。
手动枚举设备
枚举是指主机向设备请求有关自身的信息的过程。这通常由操作系统自动完成,并决定加载哪个驱动程序。对于标准设备,操作系统会查看 USB 设备类并加载支持该类的驱动程序。对于供应商特定设备,通常会安装制造商提供的驱动程序,该驱动程序会查看供应商 ID (VID) 和产品 ID (PID) 来确定是否应处理该设备。
使用 lsusb 工具可以查看设备的 VID 和 PID。例如,lsusb 输出可能显示 ID 18d1:4ee0 Google Inc. Nexus/Pixel Device (fastboot),其中 18d1 是 VID,4ee0 是 PID。
类和驱动程序信息
lsusb -t 命令显示 USB 设备的树状结构,包括设备的 USB 类和当前处理该设备的驱动程序。对于供应商特定设备,设备类通常为“Vendor Specific Class”,表示设备使用制造商定义的自定义协议。
使用 libusb 枚举设备
libusb 库提供了一个简单 API,用于从用户空间与 USB 设备通信。它提供了一个通用的驱动程序,可以加载到任何设备上,并提供了一种让用户空间应用程序直接声明设备并与之通信的方法。
以下代码示例使用 libusb 注册一个热插拔事件处理程序,用于检测 VID 为 18d1,PID 为 4ee0 的设备:
#include <iostream>
#include <libusb-1.0/libusb.h>
auto hotplug_callback(
libusb_context *ctx,
libusb_device *device,
libusb_hotplug_event event,
void *user_data) -> int {
std::cout << "Device plugged in!" << std::endl;
return 0;
}
auto main() -> int {
libusb_context *context = nullptr;
libusb_init(&context);
libusb_hotplug_register_callback(
context,
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
LIBUSB_HOTPLUG_ENUMERATE,
0x18d1, 0x4ee0,
LIBUSB_HOTPLUG_MATCH_ANY,
hotplug_callback, nullptr);
while (true) {
if (libusb_handle_events(context) < 0)
break;
}
libusb_hotplug_deregister_callback(context, hotplug_callback_handle);
libusb_exit(context);
}
与设备通信
可以使用标准的控制终端与设备进行通信。控制终端的地址始终为 0x00,具有标准协议。
控制传输类型: 专门用于设置简单的配置值或请求少量数据。
Bulk 传输类型: 用于传输大量非时间敏感的数据。
Interrupt 传输类型: 用于发送少量数据,延迟很低。
Isochronous 传输类型: 用于传输对时间要求严格的大量数据。
Fastboot 协议
Fastboot 协议非常简单:主机发送字符串命令,设备用 4 个字符的状态码和一些数据进行响应。例如,getvar:version 命令返回 OKAY0.4,表明请求成功执行,版本为 0.4。
总结
本文介绍了使用 libusb 库编写 USB 驱动程序的基本步骤,无需深入了解内核代码。 通过此方法,可以与 USB 设备进行交互,并执行诸如获取设备信息和执行 Fastboot 命令之类的操作。
|