4. USB设备驱动框架:核心结构与数据流
USB设备驱动是Linux内核中最为典型的“主机-外设”驱动模型之一。理解usb_driver结构体、probe/disconnect回调机制以及URB(USB Request Block)的生命周期,是进行USB驱动移植与开发的基础。本章将拆解这三个核心要素,并展示它们如何协同工作。
4.1 usb_driver结构体:驱动的“身份证”
在Linux内核中,每个USB设备驱动都需要注册一个struct usb_driver实例。这个结构体是驱动与USB核心之间的契约,它定义了驱动支持的设备、初始化/退出函数以及电源管理回调。
| 成员 | 类型 | 说明 |
|---|---|---|
name |
const char * |
驱动名称,在/sys/bus/usb/drivers/中显示 |
probe |
int (*probe)(struct usb_interface *, const struct usb_device_id *) |
设备匹配成功时调用,初始化接口 |
disconnect |
void (*disconnect)(struct usb_interface *) |
设备拔出或驱动卸载时调用,释放资源 |
id_table |
const struct usb_device_id * |
驱动支持的设备ID列表(VID/PID/类) |
suspend |
int (*suspend)(struct usb_interface *, pm_message_t) |
系统挂起时调用(可选) |
resume |
int (*resume)(struct usb_interface *) |
系统唤醒时调用(可选) |
移植要点:在设备树适配场景中,
id_table通常用于匹配USB设备的VID和PID。如果设备是复合设备(多个接口),驱动需要针对每个usb_interface分别注册usb_driver。
以下是一个典型的usb_driver注册示例:
static struct usb_device_id my_usb_table[] = {
{ USB_DEVICE(0x1234, 0x5678) }, // VID=0x1234, PID=0x5678
{ } // 终止条目
};
MODULE_DEVICE_TABLE(usb, my_usb_table);
static int my_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
// 初始化端点、分配URB、注册字符设备等
return 0;
}
static void my_disconnect(struct usb_interface *intf)
{
// 释放URB、注销设备、清理数据
}
static struct usb_driver my_usb_driver = {
.name = "my_usb_driver",
.probe = my_probe,
.disconnect = my_disconnect,
.id_table = my_usb_table,
};
module_usb_driver(my_usb_driver);
4.2 probe/disconnect回调:驱动的“生命周期钩子”
probe和disconnect是USB驱动中最重要的两个回调函数,它们分别在设备接入和移除时被USB核心自动调用。理解这两个函数的执行时机和职责,是避免资源泄漏和驱动崩溃的关键。
4.2.1 probe回调流程
- 匹配阶段:USB核心遍历已注册的
usb_driver,将新插入设备的usb_interface与驱动的id_table进行匹配。 - 调用probe:匹配成功后,核心调用驱动的
probe函数,传入usb_interface指针和匹配到的usb_device_id。 - 资源初始化:在
probe内部,驱动需要完成以下工作:- 获取接口描述符,解析端点信息。
- 分配URB并初始化。
- 注册字符设备或输入设备等。
- 设置接口私有数据(
usb_set_intfdata)。
- 返回状态:成功返回0,失败返回负错误码(如
-ENOMEM、-EIO)。
4.2.2 disconnect回调流程
- 触发条件:设备物理拔出、驱动卸载或系统进入低功耗模式。
- 资源清理:在
disconnect内部,驱动必须:- 停止所有正在进行的URB传输(
usb_kill_urb)。 - 释放URB内存(
usb_free_urb)。 - 注销字符设备或输入设备。
- 清理接口私有数据。
- 停止所有正在进行的URB传输(
- 返回:
disconnect无返回值,但必须确保所有资源被释放,否则会导致内存泄漏。
常见陷阱:在
disconnect中,不能假设设备仍然处于可访问状态。调用usb_control_msg等函数可能会失败,应优先使用usb_kill_urb而非usb_unlink_urb来确保URB完全停止。
4.3 URB(USB请求块)生命周期
URB是USB数据传输的核心载体。所有USB通信(控制、批量、中断、等时)都通过URB进行。理解URB的完整生命周期——从创建、填充、提交到完成回调——是编写可靠USB驱动的关键。
4.3.1 URB的创建与初始化
URB使用usb_alloc_urb分配,使用usb_fill_*_urb系列函数初始化。不同类型的传输使用不同的填充函数:
| 传输类型 | 填充函数 | 典型用途 |
|---|---|---|
| 控制传输 | usb_fill_control_urb |
设备配置、命令发送 |
| 批量传输 | usb_fill_bulk_urb |
大块数据(U盘、摄像头) |
| 中断传输 | usb_fill_int_urb |
键盘、鼠标、HID设备 |
| 等时传输 | usb_fill_isoc_urb |
音频、视频流 |
初始化示例(批量传输):
struct urb *urb;
unsigned char *buffer;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return -ENOMEM;
buffer = kmalloc(MAX_PACKET_SIZE, GFP_KERNEL);
if (!buffer) {
usb_free_urb(urb);
return -ENOMEM;
}
usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, 0x81),
buffer, MAX_PACKET_SIZE,
my_complete_callback, // 完成回调函数
my_private_data); // 回调上下文
4.3.2 URB的提交与完成
URB初始化后,通过usb_submit_urb提交给USB核心。核心将URB放入主机控制器驱动(HCD)的队列中,等待硬件处理。传输完成后,核心调用驱动注册的完成回调函数。
- 提交阶段:
usb_submit_urb(urb, GFP_ATOMIC)——在中断上下文或持有自旋锁时使用GFP_ATOMIC。 - 传输阶段:URB在HCD内部排队,等待USB总线空闲时进行传输。
- 完成回调:传输成功或失败后,核心调用
complete回调。回调中检查urb->status:0:传输成功。-ENOENT:URB被usb_kill_urb终止。-ECONNRESET:URB被usb_unlink_urb取消。-ESHUTDOWN:设备已断开。
- 重新提交或释放:在完成回调中,驱动可以选择重新提交URB(如周期性中断传输)或释放URB。
性能建议:对于批量传输,尽量使用
usb_fill_bulk_urb并配合usb_submit_urb的GFP_KERNEL标志(在进程上下文)。避免在中断上下文中提交大量URB,以免阻塞中断处理。
4.3.3 URB的取消与释放
在设备断开或驱动卸载时,必须取消所有正在进行的URB。Linux提供了两个取消函数:
| 函数 | 行为 | 适用场景 |
|---|---|---|
usb_kill_urb |
同步等待URB完全停止,直到完成回调被调用 | 设备断开、驱动卸载(推荐) |
usb_unlink_urb |
异步取消URB,立即返回,不等待完成 | 在中断上下文中取消URB |
释放URB必须使用usb_free_urb,且必须在URB完全停止后调用。
4.4 综合示例:一个简单的USB批量传输驱动
以下代码片段展示了usb_driver、probe、disconnect和URB生命周期的完整协作:
static struct usb_device_id my_table[] = {
{ USB_DEVICE(0x1234, 0x5678) },
{ }
};
MODULE_DEVICE_TABLE(usb, my_table);
struct my_usb_dev {
struct usb_device *udev;
struct usb_interface *intf;
struct urb *rx_urb;
unsigned char *rx_buf;
dma_addr_t rx_dma;
};
static void rx_complete(struct urb *urb)
{
struct my_usb_dev *dev = urb->context;
if (urb->status == 0) {
// 处理接收到的数据
dev_info(&dev->intf->dev, "Received %d bytes\n", urb->actual_length);
// 重新提交URB以持续接收
usb_submit_urb(urb, GFP_ATOMIC);
} else if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
// URB被正常取消,无需处理
} else {
dev_err(&dev->intf->dev, "URB error: %d\n", urb->status);
}
}
static int my_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct my_usb_dev *dev;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->udev = udev;
dev->intf = intf;
// 分配URB和DMA缓冲区
dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->rx_urb) {
kfree(dev);
return -ENOMEM;
}
dev->rx_buf = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_KERNEL, &dev->rx_dma);
if (!dev->rx_buf) {
usb_free_urb(dev->rx_urb);
kfree(dev);
return -ENOMEM;
}
// 初始化批量输入URB
usb_fill_bulk_urb(dev->rx_urb, udev,
usb_rcvbulkpipe(udev, 0x81),
dev->rx_buf, MAX_PACKET_SIZE,
rx_complete, dev);
dev->rx_urb->transfer_dma = dev->rx_dma;
dev->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
// 提交URB
ret = usb_submit_urb(dev->rx_urb, GFP_KERNEL);
if (ret) {
usb_free_coherent(udev, MAX_PACKET_SIZE, dev->rx_buf, dev->rx_dma);
usb_free_urb(dev->rx_urb);
kfree(dev);
return ret;
}
usb_set_intfdata(intf, dev);
return 0;
}
static void my_disconnect(struct usb_interface *intf)
{
struct my_usb_dev *dev = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
if (dev) {
usb_kill_urb(dev->rx_urb); // 同步停止URB
usb_free_coherent(dev->udev, MAX_PACKET_SIZE, dev->rx_buf, dev->rx_dma);
usb_free_urb(dev->rx_urb);
kfree(dev);
}
}
static struct usb_driver my_driver = {
.name = "my_bulk_driver",
.probe = my_probe,
.disconnect = my_disconnect,
.id_table = my_table,
};
module_usb_driver(my_driver);
移植检查清单:
- 确认
id_table中的VID/PID与目标设备一致。 - 检查端点地址(
0x81)是否与设备描述符匹配。 - 确保
MAX_PACKET_SIZE不超过端点支持的最大包长。 - 在
disconnect中优先使用usb_kill_urb而非usb_unlink_urb。