USB 驱动程序开发基础知识
USB(Universal Serial Bus)是一种广泛使用的外设连接标准,几乎所有的计算设备都支持 USB 设备,如存储设备、键盘、鼠标、打印机等。开发 USB 驱动程序是硬件驱动开发中的一个重要领域,特别是在嵌入式系统和操作系统开发中。本文将详细介绍 USB 驱动程序开发的基础知识,帮助读者了解 USB 驱动的工作原理及如何进行开发。
1. USB 协议概述
1.1 USB 通信模型
USB 是一种主从架构(Host-Device),主机(Host)负责与设备(Device)进行通信。USB 使用三层通信模型:
- 主机控制器(Host Controller):主机端的硬件和软件负责管理总线通信。
- 设备端(Device):外部 USB 设备,包含硬件接口和固件,支持不同的功能(如存储、输入等)。
传输类型:
- 控制传输(Control Transfer):用于设备配置和命令控制,通常在设备初始化时使用。
- 中断传输(Interrupt Transfer):用于周期性的小数据传输(如键盘、鼠标)。
- 批量传输(Bulk Transfer):大数据量传输,通常用于存储设备。
- 同步传输(Isochronous Transfer):用于音视频等实时数据流传输。
1.2 设备描述符
USB 设备通过一组描述符(Descriptor)向主机报告自身属性,包括设备类型、支持的配置、接口、端点等信息。常见的描述符有:
- 设备描述符(Device Descriptor):描述设备的基本信息,如供应商 ID(VID)和产品 ID(PID)。
- 配置描述符(Configuration Descriptor):定义设备的电源需求和支持的功能配置。
- 接口描述符(Interface Descriptor):定义设备的逻辑接口,例如 HID、Mass Storage 等。
- 端点描述符(Endpoint Descriptor):定义设备通信的通道,USB 设备通过端点进行数据传输。
2. USB 驱动程序的基础架构
2.1 USB 驱动的层次结构
USB 驱动程序由多层次组成,分为主机驱动和设备驱动两部分。主机驱动负责与硬件通信,设备驱动则负责处理设备的高层次功能。
- 主机控制器驱动(Host Controller Driver, HCD):与主机硬件直接交互,管理总线和设备的物理通信。
- 核心 USB 驱动(USB Core Driver):负责初始化、枚举设备以及管理设备的状态和配置。
- 设备类驱动(Class Driver):根据设备的类型(如存储设备、HID 设备等)提供特定的功能。
- USB 设备驱动(Device Driver):具体的设备驱动程序,用于处理设备的高层次操作逻辑。
2.2 Linux 下的 USB 驱动
在 Linux 系统中,USB 驱动程序主要包括以下模块:
- usbcore:USB 核心模块,负责设备的识别、管理和通信。
- ehci-hcd、uhci-hcd:USB 主机控制器驱动,不同的 USB 版本有不同的 HCD 驱动。
- 设备驱动模块:每个 USB 设备都有自己的设备驱动模块,例如
usb-storage
用于 USB 存储设备。
2.3 驱动程序接口
USB 驱动程序通过特定的内核接口与操作系统和设备进行交互。在 Linux 中,USB 驱动通过 struct usb_driver
结构体注册驱动程序,主要包含以下函数:
struct usb_driver {
const char *name; // 驱动程序名称
const struct usb_device_id *id_table; // 设备 ID 表,用于匹配设备
int (*probe)(struct usb_interface *intf, const struct usb_device_id *id); // 插入时调用
void (*disconnect)(struct usb_interface *intf); // 移除时调用
};
- probe:当系统检测到设备时调用,负责初始化设备并为其分配资源。
- disconnect:当设备从系统移除时调用,释放资源。
3. USB 驱动开发的关键步骤
3.1 设备识别与枚举
设备连接后,USB 主机通过读取设备描述符识别设备的类型、供应商和产品信息。驱动程序的 probe
函数用于检查设备 ID 是否与驱动匹配,并初始化设备。
int my_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) {
// 初始化设备,例如分配内存、注册设备等
printk(KERN_INFO "USB Device Connected: Vendor ID=%04X, Product ID=%04X\n", id->idVendor, id->idProduct);
return 0;
}
通过 设备 ID 表 usb_device_id
匹配设备的供应商 ID(VID)和产品 ID(PID),系统根据该表决定调用哪个驱动。
3.2 设备端点与数据传输
USB 设备的通信通过端点(Endpoint)进行,每个端点对应一种传输方式。在驱动中,开发者可以通过 usb_bulk_msg
或 usb_interrupt_msg
函数实现批量传输或中断传输。
int my_usb_read(struct usb_device *dev, unsigned char *data, int size) {
int ret, actual_length;
// 执行批量读取操作
ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, endpoint), data, size, &actual_length, timeout);
return ret;
}
- usb_bulk_msg:用于批量传输数据,常用于存储设备。
- usb_interrupt_msg:用于中断传输,适用于键盘、鼠标等设备。
3.3 设备中断处理
某些 USB 设备需要处理中断传输,如键盘、鼠标等。在这些设备中,驱动程序需要设置中断传输的回调函数来处理输入事件。
void my_usb_interrupt_handler(struct urb *urb) {
// 处理中断数据
printk(KERN_INFO "USB Interrupt Received\n");
}
- urb(USB Request Block):是 USB 驱动中用于传递请求的结构体,用于异步传输数据。
3.4 设备断开与资源释放
当 USB 设备从系统中移除时,驱动程序的 disconnect
函数将被调用,负责释放设备的资源:
void my_usb_disconnect(struct usb_interface *interface) {
printk(KERN_INFO "USB Device Disconnected\n");
// 释放设备资源
}
4. USB 驱动调试与开发工具
4.1 dmesg 与 printk
在 Linux 开发 USB 驱动时,可以使用 dmesg
命令查看内核日志,尤其是 probe
和 disconnect
函数中的 printk
输出信息,了解驱动加载和设备识别情况。
4.2 USB 协议分析工具
- Wireshark:可以捕获 USB 数据包,帮助分析 USB 设备的通信协议。
- usbmon:Linux 内核提供的 USB 调试工具,可以监视和记录 USB 数据流。
4.3 设备文件与调试接口
为了与 USB 设备交互,通常需要通过 /dev
设备文件访问。可以为设备创建字符设备接口,在驱动程序中实现 read
、write
函数,从而允许用户空间程序访问 USB 设备。
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = my_usb_read,
.write = my_usb_write,
};
通过注册字符设备,用户空间程序可以使用 open
、read
、write
等系统调用与设备交互。
5. USB 驱动开发总结
USB 驱动开发涉及的知识包括 USB 协议、内核模块编程、设备端点管理以及数据传输方式等。在实际开发过程中,需要深入理解设备描述符、端点配置和数据传输的工作原理,同时熟练掌握内核模块的开发技巧。
通过掌握 USB 驱动的基本架构与开发流程,开发者可以为各类 USB 设备编写高效的驱动程序,确保设备在不同操作系统和硬件平台上能够稳定运行。