第一部分:USB 物理层枚举协议与内核状态机

1.1 USB 设备检测与速度协商

USB 设备与主机之间 “物理层信号”“协议握手” 的过程。

1.1.1 设备连接与速度识别

USB 使用 D+D- 两根差分信号线来检测设备并识别其速度。

[ 主机端 (Host) ]                       [ 设备端 (Device) ]
+--------+                              +--------+
| D+     |---[ 15kΩ 下拉 (GND) ]       | D+     |---[ 1.5kΩ 上拉 (3.3V) ]—— (全速/高速)
| D-     |---[ 15kΩ 下拉 (GND) ]       | D-     |---[ 1.5kΩ 上拉 (3.3V) ]—— (低速)
+--------+                              +--------+
               ↓ 上拉电阻决定了速度 ↓
  • 低速设备:在 D- 线上有 1.5kΩ 上拉电阻。

  • 全速/高速设备:在 D+ 线上有 1.5kΩ 上拉电阻。

1.1.2 高速握手(Chirp 序列)

当主机检测到 D+ 被拉高时,它会进行 高速握手(High-Speed Handshake) 来确认设备是否支持高速模式。

  1. 设备发送 Chirp K:设备先断开 D+ 上拉,并在 D- 上发送一个 Chirp K 序列(电平跳变)。

  2. 主机响应 Chirp KJ:主机检测到 Chirp K 后,如果它支持高速模式,就会在总线上回复一组连续的 Chirp KJ 序列。

  3. 速度确认:设备识别到 Chirp KJ 序列后,确认双方都支持高速模式。然后设备断开 D+ 上拉,连接 高速终端电阻。双方进入 高速(High-Speed) 模式。

示波器/逻辑分析仪分析:

  • chirp K:设备发起的高速请求信号。

  • 3对 chirp KJ 序列:主机的高速握手响应。

  • 阈值降低一半:高速模式下的信号电压摆幅降低,以适应 480Mbps 的高速通信。

1.2 USB 枚举状态机

USB 设备从插入到可用的过程,是一个严谨的 状态机

1.2.1 枚举流程图

[插入] -> [主机检测到电平变化] -> [主机复位设备(至少10ms)] -> [主机等待至少100ms(稳定期)]
    ↓
[主机分配地址] -> [主机读取Device Descriptor(前8字节)] -> [主机读取所有Device Descriptor]
    ↓
[主机读取Configuration Descriptor] -> [主机分配配置] -> [主机读取String Descriptor(可选)]
    ↓
[设备配置完成,进入可用状态]

1.2.2 内核中枚举过程的状态映射

Linux 内核中,struct usb_device 通过一系列状态来跟踪枚举进度。

/**
 * @enum usb_device_state
 * @brief USB 设备在 Linux 内核中的状态,对应枚举流程图。
 */
enum usb_device_state {
    USB_STATE_NOTATTACHED = 0,  /**< 设备未插入 */
    USB_STATE_ATTACHED,         /**< 设备已插入(对应图片中的“插入”为YES) */
    USB_STATE_POWERED,          /**< 设备已上电(对应“供电”为YES) */
    USB_STATE_DEFAULT,          /**< 设备已复位,但未分配地址(对应“初始”为YES) */
    USB_STATE_ADDRESS,          /**< 设备已分配唯一地址(对应“地址”为YES) */
    USB_STATE_CONFIGURED,       /**< 设备已完成配置(对应“配置”为YES) */
    USB_STATE_SUSPENDED         /**< 设备处于挂起状态(对应“挂起”为YES) */
};
​
/**
 * @struct usb_device
 * @brief 代表一个 USB 设备,是整个 USB 子系统的核心数据结构。
 */
struct usb_device {
    int devnum;                 /**< 设备地址 (1-127) */
    char devpath[16];           /**< 设备路径 (如 "1-1.2") */
    enum usb_device_state state;/**< 当前设备状态 */
    u32 quirks;                 /**< 设备特殊标志 */
    struct usb_device *parent;  /**< 父设备(根集线器或上级集线器) */
    struct usb_bus *bus;        /**< 所属总线 */
    struct usb_device_descriptor descriptor; /**< 设备描述符 */
    struct usb_config_descriptor *config; /**< 配置描述符 */
    struct usb_host_endpoint *ep0; /**< 端点0 (默认控制端点) */
    // ...
};

1.3 枚举过程中的关键操作

drivers/usb/core/hub.c 中,hub_threadhub_port_connect_change 是处理枚举的核心代码。

/**
 * @brief 处理端口连接变化(包含枚举的核心逻辑)。
 * @param hub 指向集线器的指针。
 * @param port1 端口号。
 */
static void hub_port_connect_change(struct usb_hub *hub, int port1)
{
    struct usb_device *hdev = hub->hdev;
    struct usb_device *udev;
    int status, portstatus;
​
    // 1. 读取端口状态,检测连接
    status = hub_port_status(hub, port1, &portstatus);
    if (status < 0) return;
​
    // 2. 如果连接状态变更,开始枚举
    // ... 省略部分细节
    if (portstatus & USB_PORT_STAT_CONNECTION) {
        // 3. 分配并初始化设备结构体 (对应图片中的“初始”)
        udev = usb_alloc_dev(hdev, hdev->bus, port1);
        if (!udev) return;
​
        // 4. 复位设备 (对应图片中的“复位”)
        usb_set_device_state(udev, USB_STATE_DEFAULT);
        status = hub_port_reset(hub, port1, udev);
        if (status < 0) goto error;
​
        // 5. 分配地址 (对应图片中的“地址”)
        status = usb_get_device_descriptor(udev, 8);
        if (status < 8) goto error;
        status = usb_get_device_descriptor(udev, 18);
        if (status < 18) goto error;
​
        // 6. 设置地址
        udev->devnum = hub->hdev->bus->devnum_next++;
        usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                        USB_REQ_SET_ADDRESS, 0, udev->devnum, 0,
                        NULL, 0, 1000);
        usb_set_device_state(udev, USB_STATE_ADDRESS);
        // ... 后续获取配置描述符、配置设备等
    }
}

1.4 软件设计模式树形分析(基于枚举与状态机)

USB 枚举协议与状态机设计模式
├── 状态模式 (State Pattern)
│   └── usb_device_state 状态机:定义了设备在枚举过程中的不同阶段。
├── 模板方法模式 (Template Method Pattern)
│   └── hub_port_connect_change 流程:定义了处理连接变化的标准步骤(复位 -> 获取描述符 -> 分配地址 -> 配置)。
├── 策略模式 (Strategy Pattern)
│   └── 速度协商逻辑:根据握手结果选择高速/全速/低速的处理策略。
├── 工厂模式 (Factory Pattern)
│   └── usb_alloc_dev():在探测到新设备时,创建并初始化 `usb_device` 对象。
└── 观察者模式 (Observer Pattern)
    └── hub_thread:持续轮询端口状态,当设备插入/拔出时,触发 `hub_port_connect_change` 事件。

1.5 资深视角:枚举调试与常见问题

1.5.1 设备无法被识别(无效状态)

现象lsusb 看不到设备,或 dmesg 显示 Device not accepting address

原因

  1. 信号噪声:上拉/下拉电阻虚焊或布线错误(常见于自制板)。

  2. 电压不足:USB 接口提供的 5V 电流不足。

  3. 握手失败:高速设备与不支持高速的主机握手失败,退回到全速模式,但驱动未正确处理。

调试方法

  1. 使用逻辑分析仪:抓取 D+/D- 信号,确认复位信号是否正常、Chirp 序列是否完成。

  2. 检查供电:使用 lsusb -t 查看总线供电情况。

  3. 启用 usbmonsudo modprobe usbmon,然后用 sudo cat /sys/kernel/debug/usb/usbmon/0u 实时查看 USB 协议包。

1.5.2 枚举过程中导致系统卡死(死锁)

现象:插入特定 USB 设备后,系统响应缓慢或完全冻结。

原因

  1. 设备固件缺陷:设备固件在处理特定控制请求时,导致 USB 核心层卡死。

  2. 内核锁竞争usb_alloc_devhub_thread 之间可能存在资源竞争。

调试方法

  1. 使用 wireshark + usbmon:抓取完整的枚举 URB 包,分析是哪个请求导致了问题。

  2. 使用 lockdepecho 1 > /proc/sys/kernel/lockdep/on,检查内核是否有潜在的死锁。

第二部分 Linux USB 核心架构:主机控制器驱动 HCD 与核心层

2.1 USB 核心架构分层

Linux USB 子系统采用典型的分层架构,从物理硬件到应用层逐级抽象。

2.1.1 分层结构图

[用户空间]
   ↑
[USB 设备驱动 (usb_driver)]     ← 鼠标、键盘、摄像头、串口等
   ↑
[USB 核心层 (USB Core)]
   ↑
[主机控制器驱动 (HCD)]         ← 对应具体硬件(EHCI/OHCI/UHCI/xHCI)
   ↑
[硬件 (USB 控制器)]            ← PCI/Platform 设备

2.1.2 核心组件交互流程

[设备驱动]                [USB Core]                 [HCD]
    ↓                         ↓                         ↓
1. usb_submit_urb()  → 2. 检查URB合法性    → 3. hcd->urb_enqueue()
    ↑                         ↑                         ↑
6. 完成回调 (complete) ← 5. 调用完成回调     ← 4. 中断处理完成

2.2 核心数据结构

2.2.1 主机控制器驱动(HCD)核心结构

/**
 * @struct usb_hcd
 * @brief USB 主机控制器驱动核心数据结构。
 *        每个 USB 控制器实例对应一个 struct usb_hcd。
 */
struct usb_hcd {
    struct usb_bus self;            /**< 关联的 USB 总线 */
    struct usb_device *root_hub;    /**< 根集线器设备 */
    struct hcd_driver *driver;      /**< HCD 驱动操作 */
    struct device *dev;             /**< 关联的设备 */
    unsigned int irq;               /**< 中断号 */
    spinlock_t lock;                /**< 保护 HCD 状态的自旋锁 */
    
    /**
     * @brief HCD 核心操作函数集,由具体硬件驱动实现。
     */
    struct hc_driver *hc_driver;    /**< 硬件驱动操作 */
    dma_addr_t *reserved;           /**< 保留 DMA 地址 */
    unsigned long flags;            /**< 标志位 */
    
    /* 状态与统计 */
    unsigned int rh_timer;          /**< 根集线器轮询定时器 */
    unsigned int rh_timer_delta;    /**< 定时器增量 */
};

2.2.2 USB 总线结构

/**
 * @struct usb_bus
 * @brief 代表一条 USB 总线。
 *        对应于物理 USB 控制器管理的所有设备。
 */
struct usb_bus {
    struct usb_host_bus *bus;       /**< 总线抽象 */
    struct device *controller;      /**< 控制器设备 */
    int busnum;                     /**< 总线编号 */
    const struct usb_bus_ops *ops;  /**< 总线操作 */
    struct usb_hcd *hcd;            /**< 关联的 HCD */
    int bandwidth_allocated;        /**< 带宽分配统计 */
    void *hcpriv;                   /**< HCD 私有数据 */
};

2.2.3 URB (USB Request Block) 结构

/**
 * @struct urb
 * @brief USB 请求块,所有 USB 数据传输的核心结构。
 */
struct urb {
    struct list_head urb_list;      /**< URB 链表节点 */
    struct usb_device *dev;         /**< 目标设备 */
    unsigned int pipe;              /**< 管道类型 (端点+方向) */
    unsigned int status;            /**< 传输状态 */
    unsigned int actual_length;     /**< 实际传输长度 */
    int error_count;                /**< 错误计数 */
    
    /* 缓冲区信息 */
    void *transfer_buffer;          /**< 传输缓冲区 */
    dma_addr_t transfer_dma;        /**< 缓冲区 DMA 地址 */
    unsigned int transfer_buffer_length; /**< 缓冲区长度 */
    
    /* 中断/异步回调 */
    usb_complete_t complete;        /**< 完成回调函数 */
    void *context;                  /**< 回调上下文 */
};

2.3 核心代码实现

2.3.1 HCD 注册与初始化

/**
 * @brief HCD 注册函数(由具体硬件驱动调用)。
 * @param hcd 指向 usb_hcd 结构
 * @return 0 成功,负数错误
 */
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags)
{
    struct usb_bus *bus = &hcd->self;
    struct device *dev = hcd->dev;
    int ret;
​
    // 1. 初始化总线
    bus->busnum = hcd->bus_num ? : usb_busnum_next();
    bus->ops = &usb_bus_ops_common;
    bus->hcd = hcd;
​
    // 2. 注册到 USB 核心
    ret = usb_register_bus(bus);
    if (ret) goto err_bus;
​
    // 3. 创建根集线器
    hcd->root_hub = usb_alloc_dev(NULL, bus, 0);
    if (!hcd->root_hub) {
        ret = -ENOMEM;
        goto err_root;
    }
​
    // 4. 注册中断
    if (hcd->driver->flags & HCD_FLAG_SHARED_IRQ) {
        ret = request_irq(irqnum, usb_hcd_irq, IRQF_SHARED, hcd->driver->name, hcd);
    } else {
        ret = request_irq(irqnum, usb_hcd_irq, 0, hcd->driver->name, hcd);
    }
    if (ret) goto err_irq;
​
    // 5. 启动 HCD
    if (hcd->driver->start) {
        ret = hcd->driver->start(hcd);
        if (ret) goto err_start;
    }
​
    // 6. 启动根集线器轮询
    mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(100));
​
    return 0;
​
err_start:
    free_irq(irqnum, hcd);
err_irq:
    usb_put_dev(hcd->root_hub);
err_root:
    usb_unregister_bus(bus);
err_bus:
    return ret;
}
EXPORT_SYMBOL(usb_add_hcd);

2.3.2 URB 提交与处理

/**
 * @brief 提交 URB 到 HCD。
 * @param urb 指向 urb 结构
 * @return 0 成功,负数错误
 */
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
    struct usb_device *dev = urb->dev;
    struct usb_hcd *hcd = bus_to_hcd(dev->bus);
    int ret;
​
    // 1. 检查 URB 合法性
    if (!urb || !dev) return -EINVAL;
    if (!hcd->hc_driver->urb_enqueue) return -ENXIO;
​
    // 2. 锁定 HCD,提交请求
    spin_lock_irqsave(&hcd->lock, flags);
    ret = hcd->hc_driver->urb_enqueue(hcd, urb, mem_flags);
    spin_unlock_irqrestore(&hcd->lock, flags);
​
    return ret;
}
EXPORT_SYMBOL(usb_submit_urb);
​
/**
 * @brief URB 完成回调(由 HCD 中断处理调用)。
 * @param urb 指向 urb 结构
 */
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb)
{
    struct usb_device *dev = urb->dev;
​
    // 1. 更新状态
    urb->status = 0;
​
    // 2. 调用驱动注册的完成回调
    if (urb->complete) {
        urb->complete(urb);
    }
​
    // 3. 释放 URB(如果是异步的)
    usb_free_urb(urb);
}
EXPORT_SYMBOL(usb_hcd_giveback_urb);

2.3.3 根集线器轮询

/**
 * @brief 根集线器状态轮询定时器回调。
 */
static void rh_timer_callback(struct timer_list *t)
{
    struct usb_hcd *hcd = from_timer(hcd, t, rh_timer);
    struct usb_device *rh = hcd->root_hub;
    unsigned long flags;
​
    // 1. 读取端口状态
    if (hcd->hc_driver->hub_status_data) {
        hcd->hc_driver->hub_status_data(hcd, NULL);
    }
​
    // 2. 如果端口状态变化,触发中断
    if (hcd->port_change) {
        usb_hcd_irq(hcd->irq, hcd);
    }
​
    // 3. 重置定时器
    mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(100));
}

2.4 软件设计模式树形分析

USB 核心架构设计模式
├── 工厂模式 (Factory Pattern)
│   └── usb_alloc_dev():创建并初始化 usb_device 对象。
├── 抽象工厂模式 (Abstract Factory Pattern)
│   └── usb_add_hcd():创建 HCD 实例并绑定到具体硬件驱动。
├── 策略模式 (Strategy Pattern)
│   ├── hcd->hc_driver:不同 HCD(EHCI/xHCI)实现不同的硬件操作策略。
│   └── usb_hcd_irq:HCD 中断处理策略。
├── 观察者模式 (Observer Pattern)
│   └── urb->complete:URB 完成时通知设备驱动。
├── 代理模式 (Proxy Pattern)
│   └── usb_submit_urb():代理对 HCD 的请求,提供安全性检查。
└── 模板方法模式 (Template Method Pattern)
    └── usb_add_hcd():定义了 HCD 注册的标准模板(中断注册、设备创建、定时器启动)。

2.5 资深视角:HCD 调试核心难点

2.5.1 HCD 中断风暴

现象cat /proc/interrupts 显示 USB 中断触发频率极高,CPU 占用率高。

原因

  1. 中断标志未正确清除,导致重复触发。

  2. 根集线器轮询定时器间隔过短。

  3. 硬件驱动在处理短包时卡死。

调试方法

  1. 在 ISR 中检查中断状态并确认清除。

  2. 调整轮询定时器间隔。

  3. 使用 perf 跟踪 ISR 执行时间。

2.5.2 URB 提交失败

现象usb_submit_urb() 返回 -EINVAL-ENODEV

原因

  1. 端点地址或方向错误。

  2. 设备处于 SUSPENDED 状态。

  3. 缓冲区未对齐或大小错误。

调试方法

  1. 打印 URB 字段,检查管道设置。

  2. 检查设备状态。

  3. 使用 usbmon 观察实际请求。

2.6 与其他模块的协同

模块 协同方式 调试关键点
PCI/Platform 驱动 USB 控制器通常作为 PCI/Platform 设备发现,HCD 初始化时注册 设备树解析、中断分配
DMA 控制器 HCD 使用 DMA 进行批量数据传输 地址对齐、传输大小
中断控制器 (GIC) HCD 注册中断处理函数 中断亲和性、优先级
电源管理 HCD 在系统挂起/唤醒时进入低功耗模式 唤醒源、USB 设备唤醒
Input 子系统 USB HID 驱动使用 Input 子系统上报键鼠事件 事件类型、报告描述符解析
V4L2 子系统 USB 摄像头驱动使用 V4L2 传输视频数据 视频格式、帧率控制
ALSA 子系统 USB 音频驱动使用 ALSA 播放/录音 采样率、缓冲区大小
Network 子系统 USB 网卡驱动使用 net_device 接口 吞吐量、丢包率

第三部分 USB 设备驱动模型:usb_driverprobedisconnect

3.1 USB 设备驱动模型概述

USB 设备驱动是运行在 USB 核心层之上的软件模块,负责与特定类型的 USB 设备进行通信。每个 USB 设备驱动通过 usb_driver 结构注册到 USB 核心,核心层在检测到匹配的设备时调用 probe 函数,在设备移除时调用 disconnect 函数。

3.1.1 设备驱动与 USB 核心的交互流程

[USB 核心层]                              [USB 设备驱动]
    ↓                                           ↓
1. 检测到新设备 → 读取设备/配置描述符
    ↓                                           ↓
2. 遍历已注册的 usb_driver 列表
    ↓                                           ↓
3. 匹配成功 (id_table 或 动态匹配)
    ↓                                           ↓
4. 调用 usb_driver->probe()           ← 驱动执行初始化
    ↓                                           ↓
5. 驱动创建设备节点 (如 /dev/input/eventX)
    ↓                                           ↓
6. 正常通信 (URB 提交/完成)           ↔ 处理数据
    ↓                                           ↓
7. 设备拔出 → 调用 usb_driver->disconnect() ← 驱动清理资源

3.1.2 设备驱动生命周期

[设备插入] → [匹配] → [probe()] → [正常通信] → [设备拔出] → [disconnect()] → [驱动卸载]

3.2 核心数据结构

3.2.1 usb_driver 结构

/**
 * @struct usb_driver
 * @brief USB 设备驱动的核心结构。
 *        每个 USB 设备驱动必须填充此结构并注册到 USB 核心。
 */
struct usb_driver {
    const char *name;               /**< 驱动名称 */
    int (*probe)(struct usb_interface *intf, const struct usb_device_id *id);
    void (*disconnect)(struct usb_interface *intf);
    int (*unlocked_ioctl)(struct usb_interface *intf, unsigned int code, void *buf);
    int (*suspend)(struct usb_interface *intf, pm_message_t message);
    int (*resume)(struct usb_interface *intf);
    int (*reset_resume)(struct usb_interface *intf);
    int (*pre_reset)(struct usb_interface *intf);
    int (*post_reset)(struct usb_interface *intf);
    const struct usb_device_id *id_table; /**< 支持的设备 ID 列表 */
    struct device_driver driver;    /**< 通用设备驱动结构 */
    unsigned int no_dynamic_id:1;   /**< 是否禁止动态 ID */
    unsigned int supports_autosuspend:1; /**< 是否支持自动挂起 */
    unsigned int disable_hub_initiated_lpm:1; /**< 是否禁用 LPM */
    unsigned int soft_unbind:1;     /**< 是否软解绑 */
};

3.2.2 usb_device_id 结构

/**
 * @struct usb_device_id
 * @brief USB 设备 ID 匹配结构。
 *        用于在 USB 核心中匹配驱动与设备。
 */
struct usb_device_id {
    __u16 match_flags;              /**< 匹配标志 */
    __u16 idVendor;                 /**< 厂商 ID */
    __u16 idProduct;                /**< 产品 ID */
    __u16 bcdDevice_lo;             /**< 设备版本下限 */
    __u16 bcdDevice_hi;             /**< 设备版本上限 */
    __u8 bDeviceClass;              /**< 设备类 */
    __u8 bDeviceSubClass;           /**< 设备子类 */
    __u8 bDeviceProtocol;           /**< 设备协议 */
    __u8 bInterfaceClass;           /**< 接口类 */
    __u8 bInterfaceSubClass;        /**< 接口子类 */
    __u8 bInterfaceProtocol;        /**< 接口协议 */
    __u8 bInterfaceNumber;          /**< 接口编号 */
    kernel_ulong_t driver_info;     /**< 驱动私有数据 */
};

3.2.3 usb_interface 结构

/**
 * @struct usb_interface
 * @brief 代表 USB 设备的一个接口。
 *        每个接口可能包含多个端点,设备驱动通常绑定到一个接口。
 */
struct usb_interface {
    struct usb_host_interface *altsetting; /**< 接口设置列表 */
    struct usb_host_interface *cur_altsetting; /**< 当前使用的接口设置 */
    unsigned int num_altsetting;    /**< 接口设置数量 */
    int minor;                      /**< 次设备号 */
    enum usb_interface_condition condition; /**< 接口状态 */
    struct usb_device *dev;         /**< 所属设备 */
    struct device dev;              /**< 通用设备结构 */
    struct usb_driver *driver;      /**< 绑定的驱动 */
    void *drvdata;                  /**< 驱动私有数据 */
};

3.3 核心代码实现

3.3.1 USB 设备驱动注册

/**
 * @brief USB 设备驱动注册函数。
 * @param driver 指向 usb_driver 结构
 * @param module 所属模块
 * @return 0 成功,负数错误
 */
int usb_register_driver(struct usb_driver *driver, struct module *module)
{
    int ret;
​
    // 1. 设置驱动名称
    driver->driver.name = driver->name;
    driver->driver.bus = &usb_bus_type;
    driver->driver.owner = module;
​
    // 2. 注册到设备驱动核心
    ret = driver_register(&driver->driver);
    if (ret) return ret;
​
    // 3. 添加到 USB 驱动列表
    usb_driver_list_add(driver);
​
    return 0;
}
EXPORT_SYMBOL(usb_register_driver);
​
/**
 * @brief USB 设备驱动注销函数。
 * @param driver 指向 usb_driver 结构
 */
void usb_deregister_driver(struct usb_driver *driver)
{
    // 1. 从列表移除
    usb_driver_list_remove(driver);
​
    // 2. 注销设备驱动
    driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(usb_deregister_driver);

3.3.2 Probe 函数实现示例(USB HID 驱动)

/**
 * @brief USB HID 驱动 probe 函数。
 * @param intf 指向 usb_interface
 * @param id 指向匹配的 usb_device_id
 * @return 0 成功,负数错误
 */
static int usb_hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    struct usb_device *dev = interface_to_usbdev(intf);
    struct usb_hid *hid;
    int ret;
​
    // 1. 分配驱动私有数据
    hid = kzalloc(sizeof(struct usb_hid), GFP_KERNEL);
    if (!hid) return -ENOMEM;
​
    // 2. 关联接口与私有数据
    usb_set_intfdata(intf, hid);
    hid->dev = dev;
    hid->intf = intf;
​
    // 3. 获取接口端点信息
    ret = usb_find_common_endpoints(intf->cur_altsetting,
                                    &hid->ep_in, &hid->ep_out);
    if (ret) goto error;
​
    // 4. 分配 URB 和缓冲区
    hid->urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!hid->urb) goto error;
​
    hid->buf = kmalloc(8, GFP_KERNEL);
    if (!hid->buf) goto error;
​
    // 5. 初始化 URB(用于读取 HID 报告)
    usb_fill_int_urb(hid->urb, dev,
                     usb_rcvintpipe(dev, hid->ep_in->bEndpointAddress),
                     hid->buf, 8,
                     usb_hid_irq, hid,
                     hid->ep_in->bInterval);
​
    // 6. 提交 URB 开始接收数据
    ret = usb_submit_urb(hid->urb, GFP_KERNEL);
    if (ret) goto error;
​
    // 7. 注册 HID 设备到 HID 子系统
    ret = hid_add_device(hid);
    if (ret) goto error;
​
    dev_info(&intf->dev, "USB HID device initialized\n");
    return 0;
​
error:
    if (hid->urb) usb_free_urb(hid->urb);
    kfree(hid->buf);
    kfree(hid);
    return ret;
}

3.3.3 Disconnect 函数实现

/**
 * @brief USB HID 驱动 disconnect 函数。
 * @param intf 指向 usb_interface
 */
static void usb_hid_disconnect(struct usb_interface *intf)
{
    struct usb_hid *hid = usb_get_intfdata(intf);
​
    if (!hid) return;
​
    // 1. 注销 HID 设备
    hid_remove_device(hid);
​
    // 2. 取消 URB 并释放
    usb_kill_urb(hid->urb);
    usb_free_urb(hid->urb);
​
    // 3. 释放缓冲区
    kfree(hid->buf);
​
    // 4. 释放私有数据
    usb_set_intfdata(intf, NULL);
    kfree(hid);
​
    dev_info(&intf->dev, "USB HID device disconnected\n");
}

3.3.4 USB 设备 ID 表示例

/**
 * @brief USB 设备 ID 表(用于匹配 HID 键盘/鼠标)。
 */
static const struct usb_device_id usb_hid_id_table[] = {
    { USB_INTERFACE_CLASS(USB_INTERFACE_CLASS_HID, 
                          USB_INTERFACE_SUBCLASS_BOOT,
                          USB_INTERFACE_PROTOCOL_KEYBOARD) },
    { USB_INTERFACE_CLASS(USB_INTERFACE_CLASS_HID, 
                          USB_INTERFACE_SUBCLASS_BOOT,
                          USB_INTERFACE_PROTOCOL_MOUSE) },
    { USB_INTERFACE_CLASS(USB_INTERFACE_CLASS_HID, 0, 0) },
    { }
};
MODULE_DEVICE_TABLE(usb, usb_hid_id_table);
​
/**
 * @brief USB HID 驱动结构。
 */
static struct usb_driver usb_hid_driver = {
    .name = "usbhid",
    .id_table = usb_hid_id_table,
    .probe = usb_hid_probe,
    .disconnect = usb_hid_disconnect,
    .suspend = usb_hid_suspend,
    .resume = usb_hid_resume,
    .supports_autosuspend = 1,
};
​
module_usb_driver(usb_hid_driver);

3.3.5 动态 ID 添加(热插拔支持)

/**
 * @brief 用户空间通过 sysfs 动态添加 USB 设备 ID。
 * 
 * echo "vendor_id product_id" > /sys/bus/usb/drivers/驱动名/new_id
 */
static ssize_t new_id_store(struct device_driver *drv, const char *buf, size_t count)
{
    struct usb_driver *usb_drv = to_usb_driver(drv);
    struct usb_device_id *id;
    int vendor, product;
    int ret;
​
    if (sscanf(buf, "%x %x", &vendor, &product) != 2)
        return -EINVAL;
​
    // 1. 分配新的 ID
    id = kzalloc(sizeof(struct usb_device_id), GFP_KERNEL);
    if (!id) return -ENOMEM;
​
    // 2. 设置 ID 匹配字段
    id->match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
    id->idVendor = vendor;
    id->idProduct = product;
​
    // 3. 添加到驱动 ID 列表
    ret = usb_store_new_id(usb_drv, id);
    if (ret) {
        kfree(id);
        return ret;
    }
​
    return count;
}

3.4 软件设计模式树形分析

USB 设备驱动模型设计模式
├── 工厂模式 (Factory Pattern)
│   └── usb_alloc_urb():创建并初始化 URB 对象。
├── 策略模式 (Strategy Pattern)
│   ├── usb_driver->probe:不同驱动有不同的 probe 初始化策略。
│   └── usb_driver->suspend/resume:不同驱动的电源管理策略。
├── 观察者模式 (Observer Pattern)
│   ├── usb_register_driver:注册驱动后,USB 核心检测到设备时自动通知。
│   └── usb_hid_irq:中断回调函数观察 URB 完成事件。
├── 单例模式 (Singleton Pattern)
│   └── USB 核心层的驱动列表由全局变量管理,每个驱动只注册一次。
├── 适配器模式 (Adapter Pattern)
│   └── usb_hid_probe:将 USB 接口适配为 HID 设备。
└── 模板方法模式 (Template Method Pattern)
    └── usb_hid_probe/disconnect:定义了 HID 设备初始化和清理的标准流程(分配 → 初始化 → 提交 → 注销 → 释放)。

3.5 USB 设备驱动调试核心难点

3.5.1 Probe 返回负值但设备仍然绑定

现象probe 返回 -ENOMEM-EIO,但 lsusb 显示驱动已绑定。

原因

  1. probe 中部分资源分配成功,但返回前未清理。

  2. USB 核心未正确处理 probe 返回值。

解决方法

  1. probe 中确保错误路径释放所有已分配资源。

  2. 使用 goto 标签统一错误处理。

3.5.2 URB 提交后无响应

现象usb_submit_urb 成功,但 complete 回调未被调用。

原因

  1. 端点管道设置错误。

  2. 设备未配置为中断传输模式。

  3. 设备停止工作。

解决方法

  1. 使用 usbmon 查看 USB 总线上的实际请求。

  2. 检查端点描述符的传输类型和间隔设置。

  3. probe 中确认设备是否正确配置。

3.5.3 设备拔除后驱动未卸载

现象:设备拔除后,lsmod 仍显示驱动加载。

原因

  1. disconnect 未释放所有资源。

  2. 设备引用计数未归零。

  3. 有进程占用设备节点。

解决方法

  1. disconnect 中使用 usb_kill_urb 确保所有 URB 停止。

  2. 使用 usb_put_dev 减少引用计数。

  3. 检查是否有进程打开 /dev/input/eventX

3.6 与其他模块的协同

模块 协同方式 调试关键点
USB 核心层 通过 usb_register_driver 注册,核心层在设备插入/拔出时调用 probe/disconnect 匹配流程、设备枚举状态
Input 子系统 USB HID 驱动将按键/鼠标事件通过 Input 子系统上报 报告描述符解析、事件类型
V4L2 子系统 USB 摄像头驱动通过 V4L2 接口提供视频流 视频格式协商、缓冲区管理
ALSA 子系统 USB 音频驱动通过 ALSA 接口播放/录音 采样率匹配、音频同步
网络子系统 USB 网卡驱动通过 net_device 接口接入网络栈 吞吐量、MTU、丢包处理
PCI/Platform 驱动 USB 控制器驱动作为 PCI/Platform 设备注册 设备树解析、中断分配
电源管理 驱动实现 suspend/resume 支持系统睡眠 唤醒源、设备状态恢复
sysfs 通过 /sys/bus/usb/drivers/ 动态添加 ID 用户空间配置、权限控制

第四部分 USB 请求块 URB:异步 I/O 与 DMA 数据传输

4.1 URB 的核心地位

URB(USB Request Block)是 Linux USB 子系统中数据传输的核心单元。它封装了 USB 传输所需的所有信息,包括目标设备、端点、数据缓冲区、传输方向、完成回调等。理解 URB 是编写 USB 设备驱动的基础。

4.1.1 URB 生命周期

[分配 URB] → [初始化 URB] → [提交 URB] → [硬件传输] → [完成回调] → [释放 URB]
           ↓                 ↓              ↓              ↓
    (usb_alloc_urb)  (usb_fill_*_urb) (usb_submit_urb) (usb_free_urb)

4.1.2 URB 与 USB 传输类型对应关系

传输类型 对应的 URB 函数 使用场景
控制传输 usb_control_msg 设备配置、命令发送
批量传输 usb_bulk_msg 大数据传输(存储、网络)
中断传输 usb_fill_int_urb 周期性数据(HID 设备)
等时传输 usb_fill_isoc_urb 实时数据(音频、视频)

4.2 核心数据结构

4.2.1 URB 结构详解

/**
 * @struct urb
 * @brief USB 请求块,所有 USB 数据传输的核心结构。
 *        驱动开发者必须正确填充此结构并提交到 USB 核心。
 */
struct urb {
    /* 链表管理和引用计数 */
    struct list_head urb_list;      /**< 链表节点 */
    struct kref kref;               /**< 引用计数 */
    struct usb_device *dev;         /**< 目标设备 */
    struct usb_host_endpoint *ep;   /**< 目标端点 */
    unsigned int pipe;              /**< 管道 (端点+方向+类型) */
​
    /* 状态和长度信息 */
    unsigned int status;            /**< 传输状态 */
    unsigned int actual_length;     /**< 实际传输长度 */
    int error_count;                /**< 错误计数 */
    int start_frame;                /**< 起始帧 (等时传输) */
    int number_of_packets;          /**< 包数量 (等时传输) */
​
    /* 缓冲区信息 */
    void *transfer_buffer;          /**< 传输缓冲区 */
    dma_addr_t transfer_dma;        /**< 缓冲区 DMA 地址 */
    unsigned int transfer_buffer_length; /**< 缓冲区长度 */
​
    /* 列表管理 */
    int interval;                   /**< 中断传输间隔 */
    int num_sgs;                    /**< SG 列表数量 */
    struct scatterlist *sg;         /**< SG 列表指针 */
​
    /* 回调上下文 */
    usb_complete_t complete;        /**< 完成回调函数 */
    void *context;                  /**< 回调上下文 */
    struct usb_iso_packet_descriptor *iso_frame_desc; /**< 等时帧描述符 */
    struct urb *next;               /**< 链表下一个 URB */
};

4.2.2 管道宏定义

/* 管道宏定义 */
#define usb_rcvctrlpipe(dev, ep)  ((dev)->ep0->pipe) + 0x80
#define usb_sndctrlpipe(dev, ep)  ((dev)->ep0->pipe)
#define usb_rcvbulkpipe(dev, ep)  ((dev)->ep0->pipe + 0x80) + (ep)
#define usb_sndbulkpipe(dev, ep)  ((dev)->ep0->pipe) + (ep)
#define usb_rcvintpipe(dev, ep)   ((dev)->ep0->pipe + 0x80) + (ep) + 0x100
#define usb_sndintpipe(dev, ep)   ((dev)->ep0->pipe) + (ep) + 0x100
#define usb_rcvisocpipe(dev, ep)  ((dev)->ep0->pipe + 0x80) + (ep) + 0x200
#define usb_sndisocpipe(dev, ep)  ((dev)->ep0->pipe) + (ep) + 0x200

4.3 核心代码实现

4.3.1 URB 分配与初始化

/**
 * @brief 分配 URB 并初始化(批量传输示例)。
 * @param dev 指向 USB 设备
 * @param endpoint 端点地址
 * @param buffer 数据缓冲区
 * @param len 缓冲区长度
 * @param callback 完成回调函数
 * @param context 回调上下文
 * @return 指向 URB 的指针,失败返回 NULL
 */
static struct urb *usb_alloc_bulk_urb(struct usb_device *dev,
                                      int endpoint,
                                      void *buffer,
                                      int len,
                                      usb_complete_t callback,
                                      void *context)
{
    struct urb *urb;
    int pipe;
​
    // 1. 确定管道方向和类型
    if (endpoint & USB_DIR_IN) {
        pipe = usb_rcvbulkpipe(dev, endpoint & 0x7F);
    } else {
        pipe = usb_sndbulkpipe(dev, endpoint & 0x7F);
    }
​
    // 2. 分配 URB
    urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!urb) return NULL;
​
    // 3. 填充 URB 字段
    urb->dev = dev;
    urb->pipe = pipe;
    urb->transfer_buffer = buffer;
    urb->transfer_buffer_length = len;
    urb->complete = callback;
    urb->context = context;
​
    // 4. 如果是 DMA 传输,设置 DMA 地址
    urb->transfer_dma = dma_map_single(&dev->dev, buffer, len, DMA_FROM_DEVICE);
    if (dma_mapping_error(&dev->dev, urb->transfer_dma)) {
        usb_free_urb(urb);
        return NULL;
    }
​
    return urb;
}
​
/**
 * @brief 初始化中断传输 URB(用于 HID 设备)。
 */
static void usb_fill_hid_urb(struct urb *urb,
                             struct usb_device *dev,
                             int endpoint,
                             void *buffer,
                             int len,
                             usb_complete_t callback,
                             void *context,
                             int interval)
{
    urb->dev = dev;
    urb->pipe = usb_rcvintpipe(dev, endpoint);
    urb->transfer_buffer = buffer;
    urb->transfer_buffer_length = len;
    urb->complete = callback;
    urb->context = context;
    urb->interval = interval;
}

4.3.2 URB 提交与取消

/**
 * @brief 提交 URB 进行异步传输。
 * @param urb 指向 URB
 * @param mem_flags 内存分配标志
 * @return 0 成功,负数错误
 */
static int usb_submit_async_urb(struct urb *urb, gfp_t mem_flags)
{
    int ret;
​
    // 1. 检查 URB 状态
    if (!urb || !urb->dev || urb->status == -EINPROGRESS) {
        return -EINVAL;
    }
​
    // 2. 锁定并提交
    ret = usb_submit_urb(urb, mem_flags);
    if (ret) {
        dev_err(&urb->dev->dev, "URB submit failed: %d\n", ret);
        return ret;
    }
​
    return 0;
}
​
/**
 * @brief 取消正在进行的 URB 传输。
 * @param urb 指向 URB
 * @return 0 成功
 */
static void usb_cancel_async_urb(struct urb *urb)
{
    if (!urb) return;
​
    // 1. 检查 URB 是否正在进行
    if (urb->status == -EINPROGRESS) {
        // 2. 取消 URB
        usb_unlink_urb(urb);
    }
}
​
/**
 * @brief 批量传输示例(同步方式)。
 * @param dev 指向 USB 设备
 * @param endpoint 端点地址
 * @param buffer 数据缓冲区
 * @param len 缓冲区长度
 * @param timeout 超时时间 (ms)
 * @return 0 成功,负数错误
 */
static int usb_bulk_transfer_sync(struct usb_device *dev,
                                  int endpoint,
                                  void *buffer,
                                  int len,
                                  int timeout)
{
    int pipe;
    int actual_length;
    int ret;
​
    // 1. 确定管道
    if (endpoint & USB_DIR_IN) {
        pipe = usb_rcvbulkpipe(dev, endpoint & 0x7F);
    } else {
        pipe = usb_sndbulkpipe(dev, endpoint & 0x7F);
    }
​
    // 2. 执行同步传输
    ret = usb_bulk_msg(dev, pipe, buffer, len, &actual_length, msecs_to_jiffies(timeout));
    if (ret) {
        dev_err(&dev->dev, "Bulk transfer failed: %d\n", ret);
        return ret;
    }
​
    return 0;
}

4.3.3 URB 完成回调处理

/**
 * @brief URB 完成回调函数(批量传输示例)。
 * @param urb 指向完成的 URB
 */
static void usb_bulk_irq_callback(struct urb *urb)
{
    struct usb_device *dev = urb->dev;
    void *context = urb->context;
​
    // 1. 检查传输状态
    if (urb->status == 0) {
        // 传输成功
        dev_info(&dev->dev, "Bulk transfer completed: %d bytes\n", urb->actual_length);
        // 处理接收到的数据
        handle_urb_data(urb->transfer_buffer, urb->actual_length, context);
    } else if (urb->status == -ECONNRESET) {
        // 传输被取消
        dev_dbg(&dev->dev, "Bulk transfer cancelled\n");
    } else if (urb->status == -ESHUTDOWN) {
        // 设备被移除
        dev_warn(&dev->dev, "Device removed while transfer in progress\n");
    } else {
        // 其他错误
        dev_err(&dev->dev, "Bulk transfer error: %d\n", urb->status);
    }
​
    // 2. 释放 URB(如果是单次传输)
    usb_free_urb(urb);
}
​
/**
 * @brief 处理 URB 数据(示例)。
 */
static void handle_urb_data(void *buffer, int len, void *context)
{
    // 处理数据...
    uint8_t *data = (uint8_t *)buffer;
    for (int i = 0; i < len; i++) {
        // 处理每个字节
    }
}

4.3.4 等时传输 URB 处理

/**
 * @brief 等时传输 URB 创建和初始化。
 * @param dev 指向 USB 设备
 * @param endpoint 端点地址
 * @param buffer 数据缓冲区
 * @param packets 包数量
 * @param packets_size 每包大小
 * @return 指向 URB 的指针
 */
static struct urb *usb_alloc_isoc_urb(struct usb_device *dev,
                                      int endpoint,
                                      void *buffer,
                                      int packets,
                                      int packets_size)
{
    struct urb *urb;
    int pipe;
    int i;
​
    // 1. 确定管道
    if (endpoint & USB_DIR_IN) {
        pipe = usb_rcvisocpipe(dev, endpoint & 0x7F);
    } else {
        pipe = usb_sndisocpipe(dev, endpoint & 0x7F);
    }
​
    // 2. 分配 URB(包含等时帧描述符)
    urb = usb_alloc_urb(packets, GFP_KERNEL);
    if (!urb) return NULL;
​
    // 3. 填充 URB 字段
    urb->dev = dev;
    urb->pipe = pipe;
    urb->transfer_buffer = buffer;
    urb->transfer_buffer_length = packets * packets_size;
    urb->number_of_packets = packets;
​
    // 4. 填充等时帧描述符
    for (i = 0; i < packets; i++) {
        urb->iso_frame_desc[i].offset = i * packets_size;
        urb->iso_frame_desc[i].length = packets_size;
    }
​
    return urb;
}
​
/**
 * @brief 等时传输完成回调。
 */
static void usb_isoc_irq_callback(struct urb *urb)
{
    int i;
​
    // 检查每个帧的传输状态
    for (i = 0; i < urb->number_of_packets; i++) {
        struct usb_iso_packet_descriptor *desc = &urb->iso_frame_desc[i];
        if (desc->status == 0) {
            // 该帧传输成功
            process_isoc_frame(urb->transfer_buffer + desc->offset, desc->actual_length);
        } else {
            dev_err(&urb->dev->dev, "ISO frame %d failed: %d\n", i, desc->status);
        }
    }
​
    // 重新提交 URB(等时传输通常循环提交)
    usb_submit_urb(urb, GFP_ATOMIC);
}

4.4 软件设计模式树形分析

URB 异步 I/O 设计模式
├── 工厂模式 (Factory Pattern)
│   ├── usb_alloc_urb():创建并初始化 URB 对象。
│   └── usb_alloc_isoc_urb():为等时传输创建特殊的 URB。
├── 命令模式 (Command Pattern)
│   └── urb 封装了完整的 USB 传输命令,包括数据、地址、回调。
├── 观察者模式 (Observer Pattern)
│   └── urb->complete:完成回调观察 URB 传输完成事件。
├── 策略模式 (Strategy Pattern)
│   ├── usb_fill_int_urb:中断传输策略。
│   ├── usb_fill_bulk_urb:批量传输策略。
│   └── usb_fill_isoc_urb:等时传输策略。
├── 状态模式 (State Pattern)
│   └── urb->status:URB 状态(SUBMITTED、COMPLETED、ERROR)。
├── 装饰器模式 (Decorator Pattern)
│   └── 为 URB 添加 sg 列表(分散/聚合传输)。
└── 代理模式 (Proxy Pattern)
    └── usb_submit_urb():代理对 URB 的提交操作,提供安全性检查。

4.5 URB 调试核心难点

4.5.1 URB 提交后回调未被调用

现象usb_submit_urb() 返回成功,但 complete 回调始终未被调用。

原因

  1. URB 未正确绑定中断/批量/等时端点。

  2. 设备未正确配置,端点未激活。

  3. 缓冲区大小与端点描述符不匹配。

解决方法

  1. 使用 usbmon 检查 URB 是否实际到达硬件。

  2. 确认端点在 probe 中已正确配置。

  3. 检查 USB 速度与端点带宽限制。

4.5.2 URB 缓冲区 DMA 映射失败

现象usb_alloc_urb 后,usb_submit_urb 返回 -ENOMEM

原因

  1. 缓冲区未对齐(要求 4 字节对齐)。

  2. 缓冲区大小超出 DMA 能力。

  3. 设备处于禁用 DMA 状态。

解决方法

  1. 使用 kmalloc 并确保地址对齐(64 字节)。

  2. 使用 usb_buffer_alloc 统一分配 DMA 缓冲。

  3. 检查 HCD 是否支持 DMA。

4.5.3 URB 内存泄漏

现象:长时间运行后内存占用上升。

原因

  1. complete 回调中未调用 usb_free_urb

  2. URB 被重复提交但未正确释放。

  3. 队列中未取消的 URB 累积。

解决方法

  1. disconnect 中使用 usb_kill_urb 取消所有未完成的 URB。

  2. 每次 URB 完成后调用 usb_free_urb

  3. 使用 usb_poison_urb 停止队列中的 URB。

4.5.4 URB 超时处理

现象:URB 提交后长时间无响应。

原因

  1. 设备硬件故障,不响应请求。

  2. 总线上设备过多,带宽不足。

  3. 端点配置错误,传输类型错误。

解决方法

  1. 使用定时器检查 URB 超时。

  2. 重新提交 URB 或触发错误恢复。

  3. 使用 usb_unlink_urb 手动取消。

4.6 与其他模块的协同

模块 协同方式 调试关键点
USB 核心层 通过 URB 提交数据到 HCD,完成回调通知核心层 URB 状态、传输类型
HCD 驱动 将 URB 转换为硬件事务,并调用完成回调 DMA 映射、中断处理
DMA 控制器 批量 URB 使用 DMA 进行数据传输 地址对齐、传输长度
中断控制器 中断 URB 触发硬件中断,调用完成回调 中断频率、延迟
Input 子系统 中断 URB 将 HID 报告转换为 Input 事件 报告解析、事件类型
V4L2 子系统 批量/等时 URB 传输视频帧数据 帧率、缓冲区管理
ALSA 子系统 等时 URB 传输音频数据 采样率、同步
网络子系统 批量 URB 传输网络数据包 吞吐量、丢包率
sysfs 通过 sysfs 控制 URB 调试参数 调试开关、统计信息

第五部分 USB 枚举流程:从插入到配置完成

5.1 枚举流程概述

USB 设备的枚举过程是从物理连接开始,到设备可以被应用程序使用为止的完整初始化序列。整个流程包括:设备插入检测、供电、复位、获取描述符、分配地址、配置设备等步骤。

5.1.1 枚举流程文字流程图

[设备插入] -> [USB端口状态变化] -> [hub_thread 检测到连接]
    ↓
[hub_port_connect_change()] -> [usb_alloc_dev() 创建设备结构]
    ↓
[hub_port_reset()] -> [设备复位 (10ms)] -> [读取设备状态]
    ↓
[usb_get_device_descriptor()] -> [获取设备描述符 (前8字节)]
    ↓
[usb_control_msg()] -> [USB_REQ_SET_ADDRESS] -> [分配地址]
    ↓
[usb_get_device_descriptor()] -> [获取完整设备描述符 (18字节)]
    ↓
[usb_get_configuration()] -> [获取配置描述符]
    ↓
[usb_choose_configuration()] -> [选择配置]
    ↓
[usb_set_configuration()] -> [配置设备]
    ↓
[usb_register_device()] -> [驱动匹配并调用 probe]

5.1.2 枚举状态机

┌──────────────┐      ┌──────────────┐      ┌──────────────┐
│ 未插入       │─────>│ 已插入       │─────>│ 已供电       │
│ (State 0)    │      │ (State 1)    │      │ (State 2)    │
└──────────────┘      └──────────────┘      └──────────────┘
       ↑                      ↓                      ↓
       │                      │                      │
       │                      │                      │
       └──────────────────────┴──────────────────────┘
       │                      │                      │
       │                      │                      │
┌──────────────┐      ┌──────────────┐      ┌──────────────┐
│ 已配置       │<─────│ 已分配地址   │<─────│ 已复位       │
│ (State 5)    │      │ (State 4)    │      │ (State 3)    │
└──────────────┘      └──────────────┘      └──────────────┘

5.2 核心数据结构

5.2.1 枚举过程中的关键结构

/**
 * @struct usb_device_descriptor
 * @brief USB 设备描述符(由主机读取,了解设备能力)。
 */
struct usb_device_descriptor {
    __u8  bLength;          /**< 描述符长度 (18字节) */
    __u8  bDescriptorType;  /**< 描述符类型 (DEVICE = 0x01) */
    __le16 bcdUSB;          /**< USB 协议版本号 (BCD) */
    __u8  bDeviceClass;     /**< 设备类 */
    __u8  bDeviceSubClass;  /**< 设备子类 */
    __u8  bDeviceProtocol;  /**< 设备协议 */
    __u8  bMaxPacketSize0;  /**< 端点0最大包大小 */
    __le16 idVendor;        /**< 厂商 ID */
    __le16 idProduct;       /**< 产品 ID */
    __le16 bcdDevice;       /**< 设备版本号 (BCD) */
    __u8  iManufacturer;    /**< 厂商字符串索引 */
    __u8  iProduct;         /**< 产品字符串索引 */
    __u8  iSerialNumber;    /**< 序列号字符串索引 */
    __u8  bNumConfigurations; /**< 配置数量 */
} __attribute__((packed));
​
/**
 * @struct usb_config_descriptor
 * @brief USB 配置描述符。
 */
struct usb_config_descriptor {
    __u8  bLength;          /**< 描述符长度 (9字节) */
    __u8  bDescriptorType;  /**< 描述符类型 (CONFIGURATION = 0x02) */
    __le16 wTotalLength;    /**< 配置描述符总长度 */
    __u8  bNumInterfaces;   /**< 接口数量 */
    __u8  bConfigurationValue; /**< 配置值 */
    __u8  iConfiguration;   /**< 配置字符串索引 */
    __u8  bmAttributes;     /**< 配置属性 (总线供电/自供电) */
    __u8  bMaxPower;        /**< 最大功耗 (2mA单位) */
} __attribute__((packed));

5.3 核心代码实现

5.3.1 设备插入检测与端口状态轮询

/**
 * @brief 集线器线程,定期轮询端口状态。
 */
static void hub_thread(struct work_struct *work)
{
    struct usb_hub *hub = container_of(work, struct usb_hub, events);
    struct usb_device *hdev = hub->hdev;
    int i;
​
    // 1. 遍历所有端口
    for (i = 1; i <= hub->maxchild; i++) {
        u16 portstatus, portchange;
        int ret;
​
        // 2. 读取端口状态
        ret = hub_port_status(hub, i, &portstatus, &portchange);
        if (ret < 0) continue;
​
        // 3. 检查连接状态变化
        if (portchange & USB_PORT_STAT_C_CONNECTION) {
            // 触发连接变化处理
            hub_port_connect_change(hub, i);
        }
    }
​
    // 4. 继续轮询
    schedule_work(&hub->events);
}

5.3.2 设备复位与地址分配

/**
 * @brief 连接变化处理函数(核心枚举入口)。
 */
static void hub_port_connect_change(struct usb_hub *hub, int port1)
{
    struct usb_device *hdev = hub->hdev;
    struct usb_device *udev;
    u16 portstatus, portchange;
    int ret;
​
    // 1. 读取端口状态
    ret = hub_port_status(hub, port1, &portstatus, &portchange);
    if (ret < 0) return;
​
    // 2. 检查是否连接
    if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
        // 设备已拔出,清理
        usb_remove_device(hub->children[port1]);
        return;
    }
​
    // 3. 分配并初始化设备结构(对应状态:未插入 → 已插入)
    udev = usb_alloc_dev(hdev, hdev->bus, port1);
    if (!udev) return;
​
    // 4. 设备复位(对应状态:已插入 → 已复位)
    ret = hub_port_reset(hub, port1, udev);
    if (ret < 0) {
        dev_err(&hdev->dev, "reset failed, port %d\n", port1);
        usb_put_dev(udev);
        return;
    }
​
    // 5. 设置默认状态
    usb_set_device_state(udev, USB_STATE_DEFAULT);
​
    // 6. 获取设备描述符(前8字节,获取最大包大小)
    ret = usb_get_device_descriptor(udev, 8);
    if (ret < 8) {
        dev_err(&hdev->dev, "get descriptor failed\n");
        usb_put_dev(udev);
        return;
    }
​
    // 7. 分配唯一地址(对应状态:已复位 → 已分配地址)
    udev->devnum = hdev->bus->devnum_next++;
    ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                          USB_REQ_SET_ADDRESS, 0,
                          udev->devnum, 0, NULL, 0, 1000);
    if (ret < 0) {
        dev_err(&hdev->dev, "set address failed\n");
        usb_put_dev(udev);
        return;
    }
    usb_set_device_state(udev, USB_STATE_ADDRESS);
}

5.3.3 获取描述符与配置设备

/**
 * @brief 获取完整设备描述符并配置设备。
 */
static int hub_configure_device(struct usb_device *udev)
{
    int ret;
​
    // 1. 获取完整设备描述符(18字节)
    ret = usb_get_device_descriptor(udev, sizeof(struct usb_device_descriptor));
    if (ret < 0) {
        dev_err(&udev->dev, "get full descriptor failed\n");
        return ret;
    }
​
    // 2. 获取配置描述符(对应状态:已分配地址 → 已配置)
    ret = usb_get_configuration(udev);
    if (ret < 0) {
        dev_err(&udev->dev, "get configuration failed\n");
        return ret;
    }
​
    // 3. 选择并设置配置
    ret = usb_choose_configuration(udev);
    if (ret < 0) {
        dev_err(&udev->dev, "choose configuration failed\n");
        return ret;
    }
​
    // 4. 设置配置(对应状态:已配置)
    ret = usb_set_configuration(udev, ret);
    if (ret < 0) {
        dev_err(&udev->dev, "set configuration failed\n");
        return ret;
    }
​
    // 5. 更新设备状态
    usb_set_device_state(udev, USB_STATE_CONFIGURED);
​
    // 6. 注册设备到系统(触发驱动匹配)
    ret = usb_register_device(udev);
    if (ret < 0) {
        dev_err(&udev->dev, "device registration failed\n");
        return ret;
    }
​
    dev_info(&udev->dev, "USB device configured\n");
    return 0;
}

5.3.4 端点配置与接口解析

/**
 * @brief 解析配置描述符,配置端点。
 */
static int usb_parse_configuration(struct usb_device *dev,
                                   struct usb_config_descriptor *config)
{
    struct usb_interface *interface;
    struct usb_host_interface *altsetting;
    struct usb_endpoint_descriptor *endpoint;
    unsigned char *buffer;
    int interface_count = 0;
    int endpoint_count = 0;
    int i, j;
​
    // 1. 分配接口数组
    dev->actconfig = kzalloc(sizeof(struct usb_host_config), GFP_KERNEL);
    if (!dev->actconfig) return -ENOMEM;
​
    // 2. 遍历配置描述符中的接口
    buffer = (unsigned char *)config + config->bLength;
    for (i = 0; i < config->bNumInterfaces; i++) {
        // 3. 读取接口描述符
        interface = usb_alloc_interface(dev, i);
        if (!interface) return -ENOMEM;
​
        // 4. 解析接口及其替代设置
        altsetting = &interface->altsetting[0];
        memcpy(altsetting, buffer, config->bLength);
​
        // 5. 解析端点
        buffer += altsetting->desc.bLength;
        for (j = 0; j < altsetting->desc.bNumEndpoints; j++) {
            endpoint = (struct usb_endpoint_descriptor *)buffer;
            // 保存端点信息
            altsetting->endpoint[j].desc = *endpoint;
            buffer += endpoint->bLength;
            endpoint_count++;
        }
​
        // 6. 添加接口到设备
        usb_add_interface(dev, interface);
        interface_count++;
    }
​
    dev_info(&dev->dev, "Configured %d interfaces, %d endpoints\n",
             interface_count, endpoint_count);
    return 0;
}

5.4 软件设计模式树形分析

USB 枚举流程设计模式
├── 状态模式 (State Pattern)
│   └── usb_device_state:枚举过程中的状态机(未插入 → 已插入 → 已供电 → 已复位 → 已分配地址 → 已配置)
├── 工厂模式 (Factory Pattern)
│   ├── usb_alloc_dev():创建并初始化设备结构
│   └── usb_alloc_interface():创建并初始化接口结构
├── 模板方法模式 (Template Method Pattern)
│   └── hub_port_connect_change():定义了枚举的标准流程模板(复位 → 分配地址 → 获取描述符 → 配置)
├── 策略模式 (Strategy Pattern)
│   ├── usb_choose_configuration():选择配置策略
│   └── usb_set_configuration():配置设备策略
├── 观察者模式 (Observer Pattern)
│   └── hub_thread:观察端口状态变化,触发枚举
├── 命令模式 (Command Pattern)
│   └── usb_control_msg():发送控制命令请求
└── 适配器模式 (Adapter Pattern)
    └── usb_parse_configuration():将原始描述符数据适配为内核数据结构

5.5 枚举调试核心难点

5.5.1 枚举过程中复位失败

现象hub_port_reset 返回 -ETIMEDOUT,设备无法被识别。

原因

  1. 设备供电不足,无法完成复位。

  2. 设备固件损坏,不响应复位信号。

  3. 总线上存在短接。

解决方法

  1. 检查 USB 供电电压和电流。

  2. 使用专用工具重新烧录设备固件。

  3. 检查 D+/D- 信号质量。

5.5.2 地址分配失败

现象usb_control_msg 返回 -EPIPE,设备无法获取地址。

原因

  1. 设备端点0不响应控制请求。

  2. 设备 ID 冲突(多个设备同地址)。

  3. 总线故障。

解决方法

  1. 使用 usbmon 抓取地址分配请求。

  2. 检查总线上的设备数量是否过多。

  3. 使用独立的 USB 控制器进行测试。

5.5.3 配置描述符解析失败

现象usb_get_configuration 返回 -EPROTO,无法获取配置信息。

原因

  1. 设备返回的配置描述符格式错误。

  2. 设备未能正确响应配置请求。

  3. 描述符长度超限。

解决方法

  1. 使用 usb_control_msg 直接读取配置描述符。

  2. 检查设备配置描述符是否符合规范。

  3. 强制设置最大包大小为 64。

5.5.4 枚举缓慢

现象:设备枚举时间超过 3 秒,系统启动延迟。

原因

  1. 设备响应慢。

  2. 复位时间过短。

  3. 控制传输超时设置过长。

解决方法

  1. 缩短复位等待时间。

  2. 优化描述符读取顺序(先读关键信息)。

  3. 调整控制传输超时值。

5.6 与其他模块的协同

模块 协同方式 调试关键点
USB 核心层 提供枚举 API(usb_get_device_descriptorusb_set_configuration 传输成功率、状态机正确性
HCD 驱动 执行底层控制传输 端点0数据包大小、传输超时
PCI/Platform 驱动 提供物理 USB 控制器支持 寄存器访问、中断分配
Input 子系统 设备配置后触发驱动匹配,加载 HID 驱动 报告描述符解析、设备注册
sysfs 通过 /sys/bus/usb/devices/ 查看枚举结果 设备地址、接口信息
电源管理 枚举完成后设备进入正常工作状态 供电状态、唤醒能力
内核线程 hub_thread 定期轮询端口状态 轮询间隔、资源占用
设备驱动模型 usb_register_device 触发驱动匹配 匹配算法、优先级

第六部分 USB 类驱动:HID、音频、视频、网络等

6.1 USB 类驱动概述

USB 设备类驱动是建立在 USB 核心驱动之上的通用驱动。它们根据设备类型(如 HID、音频、视频、网络、存储)提供标准化的接口,避免开发者重复实现相同功能。Linux 内核中的 USB 类驱动实现了从底层 URB 处理到上层子系统(Input、ALSA、V4L2、netdev)的完整转换。

6.1.1 USB 设备类分类表

设备类 类代码 典型设备 对应的内核子系统
HID 0x03 键盘、鼠标、游戏手柄 Input 子系统
音频 0x01 耳机、麦克风、音箱 ALSA 子系统
视频 0x0E 摄像头、视频采集卡 V4L2 子系统
网络 0xFF 以太网适配器、4G 模块 Netdev 子系统
存储 0x08 U 盘、移动硬盘 SCSI/Block 子系统
CDC 0x02 调制解调器、串口 TTY/CDC 子系统

6.1.2 USB 类驱动在 Linux 内核中的位置

[用户空间]
    ↑
[应用程序] (如 alsa-utils, v4l2-ctl, evtest)
    ↑
[内核子系统] (Input, ALSA, V4L2, Netdev, SCSI)
    ↑
[USB 类驱动] (usbhid, snd-usb-audio, uvcvideo, rtl8152)
    ↑
[USB 核心层] (USB Core)
    ↑
[HCD 驱动] (EHCI/xHCI)
    ↑
[硬件]

6.2 核心数据结构

6.2.1 HID 类驱动核心结构

/**
 * @struct hid_driver
 * @brief HID 设备驱动核心结构。
 *        每个 HID 设备驱动必须填充此结构并注册到 HID 核心。
 */
struct hid_driver {
    const char *name;               /**< 驱动名称 */
    const struct hid_device_id *id_table; /**< 支持的设备 ID 列表 */
    int (*probe)(struct hid_device *hdev, const struct hid_device_id *id);
    void (*remove)(struct hid_device *hdev);
    int (*input_mapping)(struct hid_device *hdev,
                         struct hid_input *hidinput,
                         struct hid_field *field,
                         struct hid_usage *usage,
                         unsigned long **bit,
                         int *max);
    int (*event)(struct hid_device *hdev,
                 struct hid_field *field,
                 struct hid_usage *usage,
                 __s32 value);
    void (*report)(struct hid_device *hdev, struct hid_report *report);
    int (*raw_event)(struct hid_device *hdev,
                     struct hid_report *report,
                     u8 *data, int size);
};
​
/**
 * @struct hid_device
 * @brief HID 设备结构,代表一个物理 HID 设备。
 */
struct hid_device {
    struct device dev;              /**< 通用设备结构 */
    struct usb_interface *intf;     /**< 关联的 USB 接口 */
    unsigned int hiddev;            /**< HID 设备 ID */
    struct hid_report_enum report_enum[HID_REPORT_TYPES];
    struct list_head inputs;        /**< 输入设备列表 */
    struct list_head reports;       /**< 报告列表 */
    struct list_head debug_list;    /**< 调试列表 */
    void *driver_data;              /**< 驱动私有数据 */
};

6.2.2 音频类驱动核心结构

/**
 * @struct snd_usb_audio
 * @brief USB 音频设备核心结构。
 */
struct snd_usb_audio {
    struct snd_card *card;          /**< ALSA 声卡核心 */
    struct usb_device *dev;         /**< USB 设备 */
    struct snd_usb_endpoint *ep;    /**< 端点列表 */
    int samplerate;                 /**< 当前采样率 */
    int channels;                   /**< 当前通道数 */
    int format;                     /**< 音频格式 (PCM/DSD) */
    struct list_head stream_list;   /**< 流列表 */
    struct list_head midi_list;     /**< MIDI 列表 */
    unsigned int autopm:1;          /**< 自动电源管理 */
};

6.3 核心代码实现

6.3.1 USB HID 类驱动实现

/**
 * @brief USB HID 设备 probe 函数。
 * @param intf 指向 USB 接口
 * @param id 指向匹配的 USB 设备 ID
 * @return 0 成功,负数错误
 */
static int usb_hid_probe(struct usb_interface *intf,
                         const struct usb_device_id *id)
{
    struct usb_device *dev = interface_to_usbdev(intf);
    struct hid_device *hdev;
    int ret;
​
    // 1. 分配 HID 设备结构
    hdev = hid_allocate_device();
    if (IS_ERR(hdev)) {
        return PTR_ERR(hdev);
    }
​
    // 2. 设置 HID 设备属性
    hdev->dev.parent = &intf->dev;
    hdev->bus = BUS_USB;
    hdev->vendor = le16_to_cpu(dev->descriptor.idVendor);
    hdev->product = le16_to_cpu(dev->descriptor.idProduct);
    hdev->country = 0;
    hdev->hiddev = 0;
​
    // 3. 设置驱动数据
    hid_set_drvdata(hdev, intf);
    usb_set_intfdata(intf, hdev);
​
    // 4. 读取 HID 报告描述符
    ret = hid_parse_report(hdev, hid_get_descriptor(dev, HID_DT_REPORT, 0),
                           hid_get_descriptor_len(dev, HID_DT_REPORT, 0));
    if (ret) {
        dev_err(&intf->dev, "report descriptor parse failed\n");
        goto error;
    }
​
    // 5. 注册 HID 设备到 HID 核心
    ret = hid_add_device(hdev);
    if (ret) {
        dev_err(&intf->dev, "device add failed\n");
        goto error;
    }
​
    // 6. 注册 Input 设备(如果支持)
    if (hdev->group == HID_GROUP_INPUT) {
        ret = hid_input_register(hdev);
        if (ret) {
            dev_err(&intf->dev, "input register failed\n");
            goto error;
        }
    }
​
    dev_info(&intf->dev, "USB HID device registered\n");
    return 0;
​
error:
    hid_destroy_device(hdev);
    return ret;
}
​
/**
 * @brief 处理 HID 报告(键盘按键处理示例)。
 */
static int usb_hid_event(struct hid_device *hdev,
                         struct hid_field *field,
                         struct hid_usage *usage,
                         __s32 value)
{
    struct input_dev *input = NULL;
    int key = 0;
​
    // 1. 查找对应的 input 设备
    if (field->hidinput) {
        input = field->hidinput->input;
    }
​
    // 2. 处理 HID 用法(Usage)
    switch (usage->hid) {
    case HID_GD_UP:
        key = KEY_UP;
        break;
    case HID_GD_DOWN:
        key = KEY_DOWN;
        break;
    case HID_GD_LEFT:
        key = KEY_LEFT;
        break;
    case HID_GD_RIGHT:
        key = KEY_RIGHT;
        break;
    default:
        // 其他用法处理
        break;
    }
​
    // 3. 上报按键事件
    if (input && key) {
        input_event(input, EV_KEY, key, value);
        input_sync(input);
    }
​
    return 0;
}

6.3.2 USB 音频类驱动实现

/**
 * @brief USB 音频设备驱动 probe 函数。
 * @param intf 指向 USB 接口
 * @param id 指向匹配的 USB 设备 ID
 * @return 0 成功,负数错误
 */
static int snd_usb_audio_probe(struct usb_interface *intf,
                               const struct usb_device_id *id)
{
    struct usb_device *dev = interface_to_usbdev(intf);
    struct snd_usb_audio *chip;
    int ret;
​
    // 1. 分配声卡结构
    chip = snd_usb_create_card(intf, 0);
    if (IS_ERR(chip)) {
        return PTR_ERR(chip);
    }
​
    // 2. 初始化声卡
    chip->dev = dev;
    ret = snd_card_new(&intf->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
                       THIS_MODULE, 0, &chip->card);
    if (ret < 0) {
        dev_err(&intf->dev, "card creation failed\n");
        return ret;
    }
​
    // 3. 解析音频流接口
    ret = snd_usb_parse_audio_interface(chip->card, intf);
    if (ret < 0) {
        dev_err(&intf->dev, "audio interface parse failed\n");
        return ret;
    }
​
    // 4. 注册 ALSA 设备
    ret = snd_card_register(chip->card);
    if (ret < 0) {
        dev_err(&intf->dev, "card register failed\n");
        return ret;
    }
​
    // 5. 设置接口数据
    usb_set_intfdata(intf, chip);
​
    dev_info(&intf->dev, "USB audio device registered\n");
    return 0;
}
​
/**
 * @brief USB 音频播放处理函数。
 */
static int snd_usb_audio_playback(struct snd_usb_audio *chip,
                                  void *buffer,
                                  int length,
                                  int stream_id)
{
    struct urb *urb;
    int ret;
​
    // 1. 创建等时传输 URB
    urb = usb_alloc_urb(8, GFP_ATOMIC);
    if (!urb) return -ENOMEM;
​
    // 2. 填充 URB
    usb_fill_isoc_urb(urb, chip->dev,
                      usb_sndisocpipe(chip->dev, stream_id),
                      buffer, length,
                      snd_usb_audio_irq, chip, 1);
​
    // 3. 提交等时传输
    ret = usb_submit_urb(urb, GFP_ATOMIC);
    if (ret < 0) {
        dev_err(&chip->dev->dev, "isoc urb submit failed\n");
        usb_free_urb(urb);
        return ret;
    }
​
    return 0;
}

6.3.3 USB 视频类驱动实现

/**
 * @brief USB 视频设备驱动 probe 函数。
 * @param intf 指向 USB 接口
 * @param id 指向匹配的 USB 设备 ID
 * @return 0 成功,负数错误
 */
static int uvc_probe(struct usb_interface *intf,
                     const struct usb_device_id *id)
{
    struct usb_device *dev = interface_to_usbdev(intf);
    struct uvc_device *uvc;
    int ret;
​
    // 1. 创建 UVC 设备结构
    uvc = kzalloc(sizeof(struct uvc_device), GFP_KERNEL);
    if (!uvc) return -ENOMEM;
​
    // 2. 初始化 V4L2 设备
    uvc->vdev = video_device_alloc();
    if (!uvc->vdev) {
        kfree(uvc);
        return -ENOMEM;
    }
​
    // 3. 解析 UVC 控制接口
    ret = uvc_parse_control(uvc, intf);
    if (ret < 0) {
        dev_err(&intf->dev, "control interface parse failed\n");
        goto error;
    }
​
    // 4. 创建 V4L2 设备节点
    uvc->vdev->v4l2_dev = &uvc->v4l2_dev;
    uvc->vdev->fops = &uvc_fops;
    uvc->vdev->ioctl_ops = &uvc_ioctl_ops;
    uvc->vdev->release = video_device_release;
​
    ret = video_register_device(uvc->vdev, VFL_TYPE_VIDEO, -1);
    if (ret < 0) {
        dev_err(&intf->dev, "video device register failed\n");
        goto error;
    }
​
    // 5. 设置接口数据
    usb_set_intfdata(intf, uvc);
​
    dev_info(&intf->dev, "UVC camera device registered\n");
    return 0;
​
error:
    kfree(uvc);
    return ret;
}
​
/**
 * @brief UVC 视频帧处理函数。
 */
static int uvc_process_frame(struct uvc_device *uvc,
                             void *buffer,
                             int length)
{
    struct vb2_buffer *vb = uvc->vb2_buf;
    void *dst;
    int ret;
​
    // 1. 获取 VB2 缓冲区
    if (!vb || vb->state != VB2_BUF_STATE_ACTIVE) {
        return -EINVAL;
    }
​
    // 2. 将数据复制到 V4L2 缓冲区
    dst = vb2_plane_vaddr(vb, 0);
    if (!dst) {
        return -EFAULT;
    }
​
    ret = uvc_video_copy_frame(buffer, dst, length);
    if (ret < 0) {
        return ret;
    }
​
    // 3. 标记缓冲区完成
    vb->timestamp = ktime_get_ns();
    vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
​
    return 0;
}

6.4 软件设计模式树形分析

USB 类驱动设计模式
├── 工厂模式 (Factory Pattern)
│   ├── hid_allocate_device():创建 HID 设备结构
│   ├── snd_card_new():创建 ALSA 声卡结构
│   └── video_device_alloc():创建 V4L2 视频设备结构
├── 适配器模式 (Adapter Pattern)
│   ├── usbhid:将 USB 中断传输适配为 HID 报告
│   ├── snd-usb-audio:将 USB 等时传输适配为 ALSA PCM 流
│   └── uvcvideo:将 USB 批量传输适配为 V4L2 视频帧
├── 策略模式 (Strategy Pattern)
│   ├── hid_input_register:注册 Input 设备策略
│   └── uvc_parse_control:解析 UVC 控制策略
├── 观察者模式 (Observer Pattern)
│   ├── hid_report:观察 HID 报告事件
│   └── snd_usb_audio_irq:观察音频完成事件
├── 装饰器模式 (Decorator Pattern)
│   ├── 为 HID 报告添加时间戳
│   └── 为 UVC 帧添加序列号
└── 模板方法模式 (Template Method Pattern)
    ├── usb_hid_probe:定义 HID 设备初始化的标准流程
    ├── snd_usb_audio_probe:定义 USB 音频设备初始化的标准流程
    └── uvc_probe:定义 UVC 设备初始化的标准流程

6.5 类驱动调试核心难点

6.5.1 HID 报告描述符解析失败

现象hid_parse_report 返回 -EINVAL,键盘/鼠标无法工作。

原因

  1. 设备返回的报告描述符格式错误。

  2. 描述符中存在未定义的用法(Usage)。

  3. 描述符长度与设备报告不符。

解决方法

  1. 使用 hidraw 直接读取报告描述符。

  2. 使用 usbmon 抓取描述符传输数据。

  3. 添加调试打印,输出描述符内容。

6.5.2 USB 音频等时传输中断

现象:音频播放过程中出现爆音或卡顿,dmesg 显示 URB_ISO_BUF

原因

  1. USB 带宽不足,无法满足等时传输需求。

  2. 设备驱动中的等时传输参数配置不当。

  3. 系统负载过高,无法及时处理中断。

解决方法

  1. 降低采样率或通道数,减少带宽需求。

  2. 调整等时传输间隔(bInterval)参数。

  3. 使用实时优先级提升音频线程。

6.5.3 UVC 视频帧丢失

现象:视频预览卡顿,dmesg 显示 vb2_buffer 相关错误。

原因

  1. USB 批量传输数据包丢失。

  2. 缓冲区队列设置不当,没有足够缓冲区。

  3. 视频格式协商失败。

解决方法

  1. 增加缓冲区队列深度(VIDIOC_REQBUFS)。

  2. 使用 usb_bulk_msg 替代异步 URB。

  3. 检查视频格式协商是否正确。

6.6 与其他模块的协同

模块 协同方式 调试关键点
HID 核心 将 USB HID 报告转换为标准 Input 事件 报告解析、事件映射
Input 子系统 接收 HID 驱动上报的按键、鼠标事件 事件类型、重复触发
ALSA 核心 通过 PCM 接口播放/录制音频数据 采样率匹配、缓冲区管理
V4L2 核心 通过视频设备节点传输视频帧 帧格式协商、缓冲区队列
NET 核心 通过网卡设备节点传输网络数据包 吞吐量、丢包率
USB 核心层 通过 URB 与 USB 设备进行数据交换 传输类型、端点配置
HCD 驱动 执行底层 USB 数据传输 传输状态、错误处理
sysfs 通过 sysfs 查看和配置类设备参数 设备信息、调试开关
用户空间 通过类设备节点访问设备功能 权限控制、设备命名

第七部分 Linux Platform 驱动与 USB 设备的交互

7.1 Platform 驱动与 USB 的关联

在嵌入式系统中,USB 控制器通常作为 Platform 设备集成在 SoC 内部,而不是通过 PCI 总线连接。Linux USB 子系统需要与 Platform 驱动框架紧密协作,完成 USB 控制器的初始化、电源管理和中断处理。

7.1.1 Platform USB 控制器架构

[设备树 (DTS)]
    ↓
[Platform 设备注册]
    ↓
[USB 控制器 Platform 驱动]
    ├── 硬件初始化 (时钟、复位、PHY)
    ├── HCD 注册 (usb_add_hcd)
    └── 电源管理 (suspend/resume)
    ↓
[USB 核心层] ↔ [HCD 驱动]
    ↓
[USB 设备驱动]

7.1.2 Platform USB 控制器典型硬件架构

+------------------+
|   SoC            |
|  +------------+  |
|  | USB 控制器 |  |
|  | (DWC3/ EHCI)|  |
|  +------------+  |
|         |        |
|  +------------+  |
|  | USB PHY    |  |
|  +------------+  |
+------------------+
         |
    [USB 接口]

7.2 核心数据结构

7.2.1 Platform USB 控制器私有数据结构

/**
 * @struct platform_usb_priv
 * @brief Platform USB 控制器私有数据结构。
 *        管理 Platform 设备特定的硬件资源。
 */
struct platform_usb_priv {
    struct platform_device *pdev;   /**< Platform 设备指针 */
    struct usb_hcd *hcd;            /**< 关联的 HCD */
    struct clk *clk;                /**< 控制器时钟 */
    struct clk *pclk;               /**< 总线时钟 */
    struct reset_control *rst;      /**< 复位控制 */
    struct phy *phy;                /**< USB PHY */
    int irq;                        /**< 中断号 */
    void __iomem *base;             /**< 寄存器基址 */
    dma_addr_t dma_base;            /**< DMA 基址 */
    struct device *dev;             /**< 设备指针 */
    struct regulator *vbus;         /**< VBUS 电源 */
    bool usb2_phy_enabled;          /**< USB2 PHY 使能 */
    bool usb3_phy_enabled;          /**< USB3 PHY 使能 */
    spinlock_t lock;                /**< 自旋锁 */
    struct work_struct irq_work;    /**< 中断工作队列 */
};
​
/**
 * @struct usb_platform_data
 * @brief USB Platform 数据,从设备树解析。
 */
struct usb_platform_data {
    enum usb_dr_mode dr_mode;       /**< 双角色模式 (host/device/otg) */
    bool host_mode;                 /**< 是否 Host 模式 */
    bool device_mode;               /**< 是否 Device 模式 */
    int phy_type;                   /**< PHY 类型 (utmi/ulpi) */
    u32 maximum_speed;              /**< 最大速度 (high/super) */
    int vbus_gpio;                  /**< VBUS 控制 GPIO */
    bool vbus_active_high;          /**< VBUS 高电平有效 */
    int power_off_delay;            /**< 断电延迟 (ms) */
    struct regulator *vbus_reg;     /**< VBUS 调节器 */
};

7.3 核心代码实现

7.3.1 Platform USB 控制器驱动初始化

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/phy/phy.h>
#include <linux/usb/hcd.h>
#include <linux/usb/otg.h>
​
/**
 * @brief Platform USB 控制器 probe 函数。
 * @param pdev Platform 设备指针
 * @return 0 成功,负数错误
 */
static int platform_usb_probe(struct platform_device *pdev)
{
    struct platform_usb_priv *priv;
    struct usb_hcd *hcd;
    struct resource *res;
    int ret;
​
    // 1. 分配私有数据结构
    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv) return -ENOMEM;
    priv->pdev = pdev;
    spin_lock_init(&priv->lock);
    INIT_WORK(&priv->irq_work, platform_usb_irq_work);
​
    // 2. 获取寄存器资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(&pdev->dev, "No memory resource\n");
        return -ENXIO;
    }
    priv->base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(priv->base)) return PTR_ERR(priv->base);
​
    // 3. 获取中断
    priv->irq = platform_get_irq(pdev, 0);
    if (priv->irq < 0) return priv->irq;
​
    // 4. 获取时钟
    priv->clk = devm_clk_get(&pdev->dev, "usb_clk");
    if (IS_ERR(priv->clk)) {
        dev_err(&pdev->dev, "Failed to get usb_clk\n");
        return PTR_ERR(priv->clk);
    }
    priv->pclk = devm_clk_get(&pdev->dev, "pclk");
    if (IS_ERR(priv->pclk)) {
        dev_err(&pdev->dev, "Failed to get pclk\n");
        return PTR_ERR(priv->pclk);
    }
    clk_prepare_enable(priv->clk);
    clk_prepare_enable(priv->pclk);
​
    // 5. 获取复位控制
    priv->rst = devm_reset_control_get(&pdev->dev, "usb");
    if (IS_ERR(priv->rst)) {
        dev_err(&pdev->dev, "Failed to get reset\n");
        ret = PTR_ERR(priv->rst);
        goto err_clk;
    }
​
    // 6. 获取 USB PHY
    priv->phy = devm_phy_get(&pdev->dev, "usb_phy");
    if (IS_ERR(priv->phy)) {
        dev_err(&pdev->dev, "Failed to get phy\n");
        ret = PTR_ERR(priv->phy);
        goto err_clk;
    }
    phy_init(priv->phy);
    phy_power_on(priv->phy);
​
    // 7. 获取 VBUS 电源
    priv->vbus = devm_regulator_get(&pdev->dev, "vbus");
    if (IS_ERR(priv->vbus)) {
        dev_err(&pdev->dev, "Failed to get vbus\n");
        ret = PTR_ERR(priv->vbus);
        goto err_phy;
    }
    regulator_enable(priv->vbus);
​
    // 8. 创建 HCD
    hcd = usb_create_hcd(&platform_usb_hc_driver, &pdev->dev,
                         dev_name(&pdev->dev));
    if (!hcd) {
        dev_err(&pdev->dev, "Failed to create HCD\n");
        ret = -ENOMEM;
        goto err_phy;
    }
​
    // 9. 关联私有数据到 HCD
    hcd->driver_data = priv;
    hcd->has_tt = 1;  // 事务翻译器支持
    hcd->speed = USB_SPEED_HIGH;
    priv->hcd = hcd;
​
    // 10. 注册 HCD
    ret = usb_add_hcd(hcd, priv->irq, IRQF_SHARED);
    if (ret) {
        dev_err(&pdev->dev, "Failed to add HCD\n");
        usb_put_hcd(hcd);
        goto err_phy;
    }
​
    // 11. 设置平台驱动数据
    platform_set_drvdata(pdev, priv);
​
    dev_info(&pdev->dev, "Platform USB controller initialized\n");
    return 0;
​
err_phy:
    phy_power_off(priv->phy);
    phy_exit(priv->phy);
err_clk:
    clk_disable_unprepare(priv->pclk);
    clk_disable_unprepare(priv->clk);
    return ret;
}
​
/**
 * @brief Platform USB 控制器 remove 函数。
 * @param pdev Platform 设备指针
 * @return 0 成功
 */
static int platform_usb_remove(struct platform_device *pdev)
{
    struct platform_usb_priv *priv = platform_get_drvdata(pdev);
​
    // 1. 移除 HCD
    if (priv->hcd) {
        usb_remove_hcd(priv->hcd);
        usb_put_hcd(priv->hcd);
    }
​
    // 2. 关闭 PHY
    if (priv->phy) {
        phy_power_off(priv->phy);
        phy_exit(priv->phy);
    }
​
    // 3. 关闭 VBUS
    if (priv->vbus) {
        regulator_disable(priv->vbus);
    }
​
    // 4. 关闭时钟
    clk_disable_unprepare(priv->pclk);
    clk_disable_unprepare(priv->clk);
​
    // 5. 释放复位
    reset_control_assert(priv->rst);
​
    return 0;
}

7.3.2 设备树绑定解析

/**
 * @brief 从设备树解析 USB 控制器配置。
 * @param pdev Platform 设备指针
 * @param pdata 输出 Platform 数据
 * @return 0 成功,负数错误
 */
static int platform_usb_parse_dt(struct platform_device *pdev,
                                  struct usb_platform_data *pdata)
{
    struct device_node *np = pdev->dev.of_node;
    u32 dr_mode;
    int ret;
​
    // 1. 解析 dr_mode (双角色模式)
    ret = of_property_read_u32(np, "dr_mode", &dr_mode);
    if (ret) {
        pdata->dr_mode = USB_DR_MODE_HOST;
    } else {
        pdata->dr_mode = (enum usb_dr_mode)dr_mode;
    }
​
    // 2. 解析最大速度
    ret = of_property_read_u32(np, "maximum-speed", &pdata->maximum_speed);
    if (ret) {
        pdata->maximum_speed = USB_SPEED_HIGH;
    }
​
    // 3. 解析 PHY 类型
    ret = of_property_read_u32(np, "phy_type", &pdata->phy_type);
    if (ret) {
        pdata->phy_type = 0;  // 默认 UTMI
    }
​
    // 4. 解析 VBUS GPIO
    pdata->vbus_gpio = of_get_named_gpio(np, "vbus-gpios", 0);
    if (gpio_is_valid(pdata->vbus_gpio)) {
        ret = devm_gpio_request_one(&pdev->dev, pdata->vbus_gpio,
                                    GPIOF_OUT_INIT_LOW, "vbus");
        if (ret) {
            dev_err(&pdev->dev, "Failed to request vbus GPIO\n");
            return ret;
        }
    }
​
    // 5. 解析电源管理参数
    of_property_read_u32(np, "power-off-delay", &pdata->power_off_delay);
​
    return 0;
}

7.3.3 Platform USB 控制器中断处理

/**
 * @brief Platform USB 控制器中断处理函数。
 * @param irq 中断号
 * @param dev_id 设备私有数据
 * @return 中断处理结果
 */
static irqreturn_t platform_usb_irq_handler(int irq, void *dev_id)
{
    struct platform_usb_priv *priv = dev_id;
    u32 int_status;
​
    // 1. 读取中断状态寄存器
    int_status = readl(priv->base + 0x20);
    if (!int_status) {
        return IRQ_NONE;
    }
​
    // 2. 清除中断标志
    writel(int_status, priv->base + 0x24);
​
    // 3. 处理特定中断
    if (int_status & 0x01) {
        // 主机模式中断
        schedule_work(&priv->irq_work);
    }
​
    if (int_status & 0x02) {
        // 设备模式中断
        dev_dbg(priv->dev, "Device mode interrupt\n");
    }
​
    // 4. 调用 HCD 中断处理
    if (priv->hcd) {
        usb_hcd_irq(irq, priv->hcd);
    }
​
    return IRQ_HANDLED;
}
​
/**
 * @brief 中断工作队列处理函数。
 */
static void platform_usb_irq_work(struct work_struct *work)
{
    struct platform_usb_priv *priv = container_of(work, struct platform_usb_priv, irq_work);
​
    // 处理需要非中断上下文的工作
    // 例如:电源状态切换、PHY 重新配置等
    if (priv->hcd && priv->hcd->state == HC_STATE_RUNNING) {
        // 保持正常运行
    } else {
        // 可能需要恢复 HCD
        usb_hcd_resume(priv->hcd);
    }
}

7.3.4 设备树节点示例

/* DTS 节点示例: USB 控制器 */
usb@ff500000 {
    compatible = "vendor,platform-usb";
    reg = <0xff500000 0x2000>;
    interrupts = <0 10 4>;
    clocks = <&clk_usb>, <&clk_bus>;
    clock-names = "usb_clk", "pclk";
    resets = <&reset_usb>;
    reset-names = "usb";
    phys = <&usb_phy>;
    phy-names = "usb_phy";
    vbus-supply = <&vbus_reg>;
    dr_mode = "host";
    maximum-speed = "high-speed";
    power-off-delay = <100>;
    status = "okay";
};

7.4 软件设计模式树形分析

Platform USB 驱动设计模式
├── 工厂模式 (Factory Pattern)
│   ├── usb_create_hcd():创建 HCD 实例
│   └── phy_get():获取 PHY 实例
├── 适配器模式 (Adapter Pattern)
│   └── platform_usb_probe():将 Platform 设备适配为 USB HCD
├── 策略模式 (Strategy Pattern)
│   └── usb_dr_mode:Host/Device/OTG 模式切换策略
├── 观察者模式 (Observer Pattern)
│   └── platform_usb_irq_handler:观察硬件中断事件
├── 代理模式 (Proxy Pattern)
│   └── usb_add_hcd():代理 HCD 注册到 USB 核心
└── 模板方法模式 (Template Method Pattern)
    └── platform_usb_probe():定义了 Platform USB 控制器的标准初始化流程

7.5 Platform USB 调试核心难点

7.5.1 设备树解析失败

现象of_property_read_u32 返回 -EINVAL,驱动无法加载。

原因

  1. 设备树节点中缺少必需属性。

  2. 属性名称拼写错误。

  3. 设备树未正确编译。

解决方法

  1. 使用 dtc -I fs /sys/firmware/devicetree/base/ 检查设备树。

  2. 添加调试打印,输出解析参数。

  3. 检查设备树文件是否正确。

7.5.2 PHY 初始化失败

现象phy_init() 返回 -ENODEV,USB 控制器无法工作。

原因

  1. PHY 驱动未加载。

  2. PHY 设备树节点不存在。

  3. PHY 硬件未上电。

解决方法

  1. 检查 ls /sys/class/phy/ 确认 PHY 驱动加载。

  2. 检查设备树中的 PHY 节点。

  3. 检查 PHY 供电和复位电路。

7.5.3 时钟频率错误

现象:USB 控制器无法识别设备,或传输频繁出错。

原因

  1. 时钟频率与 USB 要求不匹配。

  2. 时钟分频设置错误。

  3. 时钟未使能。

解决方法

  1. 使用 clk_summary 检查时钟状态。

  2. 根据 USB 规范配置时钟分频。

  3. 检查时钟驱动实现。

7.6 与其他模块的协同

模块 协同方式 调试关键点
设备树 (DTS) 提供 USB 控制器硬件描述 节点解析、属性检查
时钟控制器 提供 USB 控制器时钟 时钟频率、使能状态
复位控制器 提供 USB 控制器复位 复位序列、延时
PHY 驱动 提供 USB PHY 硬件支持 PHY 状态、电源管理
电源管理 管理 VBUS 电源和控制器电源 上下电序列、唤醒
GPIO 驱动 控制 VBUS 电源 GPIO GPIO 状态、电流能力
中断控制器 路由 USB 控制器中断 中断号、亲和性
DMA 控制器 提供 DMA 传输支持 传输完成、DMA 错误

第八部分 USB 控制器设备树绑定与硬件描述

8.1 设备树在 USB 控制器中的核心作用

在嵌入式系统中,设备树(Device Tree)是描述硬件配置的标准方式。对于 USB 控制器,设备树节点提供了寄存器地址、中断号、时钟、PHY、电源管理、DMA 配置等所有硬件信息。正确的设备树绑定是 USB 控制器驱动初始化的前提。

8.1.1 设备树节点与驱动映射关系

[设备树节点 (DTS)]
    ↓
[内核设备树解析]
    ↓
[Platform 设备注册]
    ↓
[USB 控制器驱动 Probe]
    ↓
[硬件初始化]
    ↓
[USB 核心层注册]

8.1.2 典型 USB 控制器设备树节点结构

usb@ff500000 {
    compatible = "vendor,usb-controller";
    reg = <0xff500000 0x2000>;
    interrupts = <0 10 4>;
    clocks = <&clk_usb>, <&clk_bus>;
    clock-names = "usb_clk", "pclk";
    resets = <&reset_usb>;
    reset-names = "usb";
    phys = <&usb_phy>;
    phy-names = "usb_phy";
    vbus-supply = <&vbus_reg>;
    dr_mode = "host";
    maximum-speed = "high-speed";
    power-off-delay = <100>;
    status = "okay";
};

8.2 核心数据结构

8.2.1 设备树解析相关结构

/**
 * @struct of_usb_config
 * @brief 从设备树解析的 USB 配置结构。
 */
struct of_usb_config {
    struct resource *res;           /**< 寄存器资源 */
    int irq;                        /**< 中断号 */
    struct clk *clk;                /**< 控制器时钟 */
    struct clk *pclk;               /**< 总线时钟 */
    struct reset_control *rst;      /**< 复位控制 */
    struct phy *phy;                /**< USB PHY */
    struct regulator *vbus;         /**< VBUS 电源 */
    enum usb_dr_mode dr_mode;       /**< 双角色模式 */
    enum usb_device_speed max_speed; /**< 最大速度 */
    int vbus_gpio;                  /**< VBUS 控制 GPIO */
    int power_off_delay;            /**< 断电延迟 (ms) */
    const char *phy_type;           /**< PHY 类型 */
    u32 dma_capable;                /**< DMA 能力 */
    u32 quirks;                     /**< 设备特殊标志 */
};
​
/**
 * @struct usb_phy_bind
 * @brief USB PHY 绑定结构。
 */
struct usb_phy_bind {
    const char *compatible;         /**< PHY 兼容字符串 */
    const char *phy_name;           /**< PHY 名称 */
    const char *device_node;        /**< 设备节点名称 */
    const char *phy_type;           /**< PHY 类型 (utmi/ulpi) */
    u32 phy_interface;              /**< PHY 接口类型 */
    u32 phy_id;                     /**< PHY ID */
    u32 phy_version;                /**< PHY 版本 */
};

8.3 核心代码实现

8.3.1 设备树解析核心函数

#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/clk-provider.h>
#include <linux/reset-controller.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
​
/**
 * @brief 从设备树解析 USB 控制器资源。
 * @param pdev Platform 设备指针
 * @param config 输出解析后的配置
 * @return 0 成功,负数错误
 */
static int usb_of_parse_config(struct platform_device *pdev,
                               struct of_usb_config *config)
{
    struct device *dev = &pdev->dev;
    struct device_node *np = pdev->dev.of_node;
    u32 val;
    int ret;
​
    // 1. 解析寄存器资源
    config->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!config->res) {
        dev_err(dev, "No memory resource in device tree\n");
        return -ENXIO;
    }
​
    // 2. 解析中断
    config->irq = platform_get_irq(pdev, 0);
    if (config->irq < 0) {
        dev_err(dev, "No interrupt in device tree\n");
        return config->irq;
    }
​
    // 3. 解析时钟
    config->clk = of_clk_get_by_name(np, "usb_clk");
    if (IS_ERR(config->clk)) {
        dev_err(dev, "No usb_clk in device tree\n");
        return PTR_ERR(config->clk);
    }
​
    config->pclk = of_clk_get_by_name(np, "pclk");
    if (IS_ERR(config->pclk)) {
        dev_err(dev, "No pclk in device tree\n");
        return PTR_ERR(config->pclk);
    }
​
    // 4. 解析复位
    config->rst = of_reset_control_get(np, "usb");
    if (IS_ERR(config->rst)) {
        dev_err(dev, "No reset in device tree\n");
        return PTR_ERR(config->rst);
    }
​
    // 5. 解析 PHY
    config->phy = of_phy_get(np, "usb_phy");
    if (IS_ERR(config->phy)) {
        dev_err(dev, "No PHY in device tree\n");
        return PTR_ERR(config->phy);
    }
​
    // 6. 解析 VBUS 电源
    config->vbus = devm_regulator_get(dev, "vbus");
    if (IS_ERR(config->vbus)) {
        dev_err(dev, "No vbus regulator in device tree\n");
        return PTR_ERR(config->vbus);
    }
​
    // 7. 解析 dr_mode
    ret = of_property_read_u32(np, "dr_mode", &val);
    if (ret) {
        config->dr_mode = USB_DR_MODE_HOST;
    } else {
        config->dr_mode = (enum usb_dr_mode)val;
    }
​
    // 8. 解析最大速度
    ret = of_property_read_u32(np, "maximum-speed", &val);
    if (ret) {
        config->max_speed = USB_SPEED_HIGH;
    } else {
        config->max_speed = (enum usb_device_speed)val;
    }
​
    // 9. 解析 VBUS GPIO
    config->vbus_gpio = of_get_named_gpio(np, "vbus-gpios", 0);
    if (gpio_is_valid(config->vbus_gpio)) {
        ret = devm_gpio_request_one(dev, config->vbus_gpio,
                                    GPIOF_OUT_INIT_LOW, "vbus");
        if (ret) {
            dev_err(dev, "Failed to request vbus GPIO\n");
            return ret;
        }
    }
​
    // 10. 解析其他参数
    of_property_read_u32(np, "power-off-delay", &config->power_off_delay);
    of_property_read_u32(np, "dma-capable", &config->dma_capable);
    of_property_read_u32(np, "quirks", &config->quirks);
​
    return 0;
}

8.3.2 设备树节点匹配与绑定

/**
 * @brief USB 控制器设备树匹配表。
 */
static const struct of_device_id usb_of_match[] = {
    { .compatible = "vendor,usb-controller" },
    { .compatible = "snps,dwc3" },
    { .compatible = "rockchip,rk3399-dwc3" },
    { .compatible = "allwinner,sun8i-h3-dwc3" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, usb_of_match);
​
/**
 * @brief USB 控制器设备树绑定函数。
 * @param pdev Platform 设备指针
 * @return 0 成功,负数错误
 */
static int usb_of_bind(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    struct device_node *phy_node;
    u32 phy_mode;
    int ret;
​
    // 1. 检查兼容性
    if (!of_match_node(usb_of_match, np)) {
        dev_err(&pdev->dev, "No compatible match\n");
        return -ENODEV;
    }
​
    // 2. 获取 PHY 节点
    phy_node = of_parse_phandle(np, "phys", 0);
    if (!phy_node) {
        dev_err(&pdev->dev, "No PHY phandle\n");
        return -ENODEV;
    }
​
    // 3. 解析 PHY 模式
    ret = of_property_read_u32(phy_node, "phy-mode", &phy_mode);
    if (ret) {
        dev_err(&pdev->dev, "No phy-mode in PHY node\n");
        return ret;
    }
​
    // 4. 绑定 PHY
    ret = of_phy_bind(pdev->dev.parent, phy_node, "usb_phy");
    if (ret) {
        dev_err(&pdev->dev, "PHY bind failed\n");
        return ret;
    }
​
    // 5. 设置 DMA 配置
    if (of_dma_is_coherent(np)) {
        dev_dbg(&pdev->dev, "Device is DMA coherent\n");
    }
​
    // 6. 保存绑定信息
    platform_set_drvdata(pdev, (void *)np);
​
    return 0;
}

8.3.3 设备树资源映射

/**
 * @brief 从设备树映射 USB 控制器寄存器。
 * @param pdev Platform 设备指针
 * @param base 输出映射后的寄存器基址
 * @return 0 成功,负数错误
 */
static int usb_of_map_registers(struct platform_device *pdev,
                                void __iomem **base)
{
    struct resource *res;
    void __iomem *reg_base;
​
    // 1. 获取寄存器资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(&pdev->dev, "No memory resource\n");
        return -ENXIO;
    }
​
    // 2. 映射寄存器
    reg_base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(reg_base)) {
        dev_err(&pdev->dev, "Failed to ioremap\n");
        return PTR_ERR(reg_base);
    }
​
    *base = reg_base;
    return 0;
}
​
/**
 * @brief 从设备树获取中断信息。
 * @param pdev Platform 设备指针
 * @param irq 输出中断号
 * @return 0 成功,负数错误
 */
static int usb_of_get_irq(struct platform_device *pdev, int *irq)
{
    int ret;
​
    ret = platform_get_irq(pdev, 0);
    if (ret < 0) {
        dev_err(&pdev->dev, "No interrupt in device tree\n");
        return ret;
    }
​
    *irq = ret;
    return 0;
}

8.3.4 设备树时钟解析

/**
 * @brief 从设备树解析 USB 控制器时钟。
 * @param pdev Platform 设备指针
 * @param clocks 输出时钟结构
 * @return 0 成功,负数错误
 */
static int usb_of_get_clocks(struct platform_device *pdev,
                             struct usb_clocks *clocks)
{
    struct device *dev = &pdev->dev;
    struct device_node *np = pdev->dev.of_node;
    int ret;
​
    // 1. 获取主时钟
    clocks->clk = of_clk_get_by_name(np, "usb_clk");
    if (IS_ERR(clocks->clk)) {
        dev_err(dev, "No usb_clk in device tree\n");
        return PTR_ERR(clocks->clk);
    }
​
    // 2. 获取总线时钟
    clocks->pclk = of_clk_get_by_name(np, "pclk");
    if (IS_ERR(clocks->pclk)) {
        dev_err(dev, "No pclk in device tree\n");
        return PTR_ERR(clocks->pclk);
    }
​
    // 3. 获取 PHY 时钟
    clocks->phy_clk = of_clk_get_by_name(np, "phy_clk");
    if (IS_ERR(clocks->phy_clk)) {
        dev_dbg(dev, "No phy_clk in device tree\n");
    }
​
    // 4. 获取参考时钟
    clocks->ref_clk = of_clk_get_by_name(np, "ref_clk");
    if (IS_ERR(clocks->ref_clk)) {
        dev_dbg(dev, "No ref_clk in device tree\n");
    }
​
    return 0;
}

8.3.5 设备树 PHY 配置解析

/**
 * @brief 从设备树解析 USB PHY 配置。
 * @param pdev Platform 设备指针
 * @param phy_config 输出 PHY 配置
 * @return 0 成功,负数错误
 */
static int usb_of_parse_phy(struct platform_device *pdev,
                            struct usb_phy_config *phy_config)
{
    struct device_node *np = pdev->dev.of_node;
    struct device_node *phy_np;
    u32 phy_type;
    int ret;
​
    // 1. 解析 PHY 节点
    phy_np = of_parse_phandle(np, "phys", 0);
    if (!phy_np) {
        dev_err(&pdev->dev, "No PHY phandle\n");
        return -ENODEV;
    }
​
    // 2. 解析 PHY 类型
    ret = of_property_read_u32(phy_np, "phy-type", &phy_type);
    if (ret) {
        phy_config->phy_type = PHY_TYPE_UTMI;
    } else {
        phy_config->phy_type = (enum phy_type)phy_type;
    }
​
    // 3. 解析 PHY 接口
    ret = of_property_read_u32(phy_np, "phy-interface", &phy_config->phy_interface);
    if (ret) {
        phy_config->phy_interface = PHY_INTERFACE_MODE_UTMI;
    }
​
    // 4. 解析 PHY 速度
    ret = of_property_read_u32(phy_np, "maximum-speed", &phy_config->max_speed);
    if (ret) {
        phy_config->max_speed = USB_SPEED_HIGH;
    }
​
    // 5. 解析 PHY 电源
    of_property_read_bool(phy_np, "power-off-in-suspend", &phy_config->power_off_in_suspend);
​
    return 0;
}

8.3.6 设备树电源管理解析

/**
 * @brief 从设备树解析 USB 电源管理配置。
 * @param pdev Platform 设备指针
 * @param pm_config 输出电源管理配置
 * @return 0 成功,负数错误
 */
static int usb_of_parse_pm(struct platform_device *pdev,
                           struct usb_pm_config *pm_config)
{
    struct device_node *np = pdev->dev.of_node;
    struct device *dev = &pdev->dev;
    u32 val;
    int ret;
​
    // 1. 解析 VBUS 电源
    pm_config->vbus_supply = devm_regulator_get(dev, "vbus");
    if (IS_ERR(pm_config->vbus_supply)) {
        dev_err(dev, "No vbus supply\n");
        return PTR_ERR(pm_config->vbus_supply);
    }
​
    // 2. 解析 VBUS GPIO
    pm_config->vbus_gpio = of_get_named_gpio(np, "vbus-gpios", 0);
    if (gpio_is_valid(pm_config->vbus_gpio)) {
        ret = devm_gpio_request_one(dev, pm_config->vbus_gpio,
                                    GPIOF_OUT_INIT_LOW, "vbus");
        if (ret) {
            dev_err(dev, "Failed to request vbus GPIO\n");
            return ret;
        }
    }
​
    // 3. 解析电源延迟
    ret = of_property_read_u32(np, "power-off-delay", &pm_config->power_off_delay);
    if (ret) {
        pm_config->power_off_delay = 100;
    }
​
    // 4. 解析唤醒能力
    pm_config->wakeup_capable = of_property_read_bool(np, "wakeup-capable");
​
    // 5. 解析暂停策略
    ret = of_property_read_u32(np, "suspend-mode", &pm_config->suspend_mode);
    if (ret) {
        pm_config->suspend_mode = 0;
    }
​
    return 0;
}

8.4 软件设计模式树形分析

USB 控制器设备树绑定设计模式
├── 工厂模式 (Factory Pattern)
│   ├── of_clk_get_by_name():获取时钟实例
│   └── devm_regulator_get():获取电源调节器实例
├── 适配器模式 (Adapter Pattern)
│   └── usb_of_parse_config():将设备树节点适配为驱动配置
├── 策略模式 (Strategy Pattern)
│   ├── usb_dr_mode:Host/Device/OTG 模式策略
│   └── phy_type:不同类型的 PHY 配置策略
├── 观察者模式 (Observer Pattern)
│   └── 设备树节点与驱动的绑定关系通过匹配表实现
├── 单例模式 (Singleton Pattern)
│   └── 每个 USB 控制器只有一个设备树节点
└── 模板方法模式 (Template Method Pattern)
    └── usb_of_parse_config():定义了从设备树解析配置的标准流程

8.5 设备树调试核心难点

8.5.1 设备树节点不匹配

现象of_match_node 返回 NULL,驱动无法加载。

原因

  1. 设备树节点兼容字符串与驱动不匹配。

  2. 设备树文件未编译或未加载。

  3. 节点状态为 disabled

解决方法

  1. 使用 dtc -I fs /sys/firmware/devicetree/base/ 检查设备树节点。

  2. 检查兼容字符串是否正确。

  3. 确认节点状态为 okayenabled

8.5.2 PHY 节点解析失败

现象of_parse_phandle 返回 NULL,PHY 无法初始化。

原因

  1. PHY 节点未在设备树中定义。

  2. PHY 引用 phandle 错误。

  3. PHY 节点状态为 disabled

解决方法

  1. 检查设备树中 PHY 节点是否存在。

  2. 确认 phandle 值正确。

  3. 确认 PHY 节点状态为 okay

8.5.3 时钟获取失败

现象of_clk_get_by_name 返回 ERR_PTR(-ENOENT)

原因

  1. 时钟名称与设备树中的 clock-names 不匹配。

  2. 时钟节点未定义。

  3. 时钟提供方驱动未加载。

解决方法

  1. 检查 clock-names 属性是否正确。

  2. 使用 clk_summary 检查时钟是否存在。

  3. 确认时钟控制器驱动已加载。

8.5.4 设备树资源冲突

现象:多个设备使用相同的寄存器地址或中断号。

原因

  1. 设备树中地址或中断冲突。

  2. 资源复用配置错误。

  3. 硬件设计问题。

解决方法

  1. 检查 reginterrupts 属性是否有重叠。

  2. 使用 dmesg 查看资源冲突信息。

  3. 重新设计硬件或设备树。

8.6 与其他模块的协同

模块 协同方式 调试关键点
设备树编译器 (DTC) 编译设备树文件为二进制格式 语法检查、节点验证
内核设备树解析 解析设备树二进制文件,注册 Platform 设备 节点匹配、属性解析
时钟控制器 提供 USB 控制器所需时钟 时钟频率、使能状态
复位控制器 提供 USB 控制器复位信号 复位序列、延迟
PHY 驱动 提供 USB PHY 配置和初始化 PHY 类型、接口模式
电源管理 管理 VBUS 电源和控制器电源 供电稳定、唤醒能力
GPIO 驱动 控制 VBUS 电源 GPIO GPIO 状态、电流能力
中断控制器 路由 USB 控制器中断 中断号、亲和性

第九部分 USB Gadget 设备端驱动框架

9.1 USB Gadget 框架概述

USB Gadget 是 Linux 内核中用于实现 USB 设备端(Device-side)功能的框架。与 USB Host 驱动不同,Gadget 框架允许 SoC 或专用控制器作为 USB 设备连接到主机,模拟各种 USB 设备类型,如串口、以太网、存储、音频等。

9.1.1 Gadget 框架架构

[用户空间]
    ↑
[Gadget 功能驱动 (Function Drivers)]
    ├── 串口 (g_serial)
    ├── 网卡 (g_ether)
    ├── 存储 (g_mass_storage)
    ├── 音频 (g_audio)
    └── HID (g_hid)
    ↑
[Gadget 核心层 (Gadget Core)]
    ↑
[UDC 驱动 (UDC Driver)] ← 具体硬件驱动
    ↑
[USB 设备控制器硬件 (UDC)]
    ↑
[USB 接口]

9.1.2 Gadget 框架数据流

[功能驱动] → [Gadget 核心] → [UDC 驱动] → [硬件]
    ↓             ↓             ↓             ↓
[用户数据]   [协议处理]   [寄存器操作]   [USB 包]

9.2 核心数据结构

9.2.1 Gadget 核心结构

/**
 * @struct usb_gadget
 * @brief USB Gadget 核心结构,代表一个 USB 设备控制器实例。
 */
struct usb_gadget {
    struct usb_udc *udc;            /**< 关联的 UDC */
    struct usb_gadget_driver *driver; /**< 绑定的 Gadget 驱动 */
    struct usb_device *dev;         /**< USB 设备指针 */
    struct usb_gadget_ops *ops;     /**< 硬件操作函数 */
    struct usb_ep *ep0;             /**< 端点0 (控制端点) */
    struct list_head ep_list;       /**< 端点列表 */
    unsigned int sg_supported:1;    /**< 是否支持 SG 传输 */
    unsigned int is_otg:1;          /**< 是否支持 OTG */
    unsigned int is_a_peripheral:1; /**< 是否作为外设 */
    unsigned int is_self_powered:1; /**< 是否自供电 */
    unsigned int is_fullspeed:1;    /**< 是否全速 */
    unsigned int is_hight_speed:1;  /**< 是否高速 */
    unsigned int is_super_speed:1;  /**< 是否超速 */
    enum usb_device_speed speed;    /**< 当前速度 */
    enum usb_device_state state;    /**< 设备状态 */
    struct work_struct work;        /**< 工作队列 */
    void *private_data;             /**< 私有数据 */
};
​
/**
 * @struct usb_gadget_driver
 * @brief USB Gadget 驱动结构。
 */
struct usb_gadget_driver {
    const char *name;               /**< 驱动名称 */
    int (*bind)(struct usb_gadget *gadget, struct usb_gadget_driver *driver);
    void (*unbind)(struct usb_gadget *gadget);
    int (*setup)(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl);
    void (*disconnect)(struct usb_gadget *gadget);
    void (*suspend)(struct usb_gadget *gadget);
    void (*resume)(struct usb_gadget *gadget);
    void (*reset)(struct usb_gadget *gadget);
    struct device *dev;             /**< 关联的设备 */
};

9.2.2 UDC 驱动结构

/**
 * @struct usb_udc
 * @brief USB 设备控制器驱动结构。
 */
struct usb_udc {
    struct list_head list;          /**< UDC 列表 */
    struct usb_gadget *gadget;      /**< 关联的 Gadget */
    struct usb_gadget_driver *driver; /**< 绑定的 Gadget 驱动 */
    struct device dev;              /**< 设备 */
    const char *name;               /**< 名称 */
    int (*udc_start)(struct usb_udc *udc);
    int (*udc_stop)(struct usb_udc *udc);
    void (*udc_set_selfpowered)(struct usb_udc *udc, int value);
    void *private_data;             /**< 私有数据 */
};
​
/**
 * @struct usb_ep
 * @brief USB 端点结构,代表一个物理端点。
 */
struct usb_ep {
    struct list_head ep_list;       /**< 端点列表 */
    struct usb_endpoint_descriptor *desc; /**< 端点描述符 */
    struct usb_gadget *gadget;      /**< 所属 Gadget */
    int maxpacket;                  /**< 最大包大小 */
    int maxburst;                   /**< 最大突发长度 */
    int mult;                       /**< 多重传输 */
    int address;                    /**< 端点地址 */
    int bEndpointAddress;           /**< 端点地址 */
    int bmAttributes;               /**< 端点属性 */
    int interval;                   /**< 轮询间隔 */
    const struct usb_ep_ops *ops;   /**< 端点操作函数 */
    void *driver_data;              /**< 驱动私有数据 */
};

9.3 核心代码实现

9.3.1 UDC 驱动初始化

#include <linux/module.h>
#include <linux/usb/gadget.h>
#include <linux/dma-mapping.h>
​
/**
 * @brief UDC 驱动 probe 函数。
 * @param pdev Platform 设备指针
 * @return 0 成功,负数错误
 */
static int udc_probe(struct platform_device *pdev)
{
    struct usb_udc *udc;
    struct usb_gadget *gadget;
    int ret;
​
    // 1. 分配 UDC 和 Gadget 结构
    udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
    if (!udc) return -ENOMEM;
​
    gadget = devm_kzalloc(&pdev->dev, sizeof(*gadget), GFP_KERNEL);
    if (!gadget) return -ENOMEM;
​
    // 2. 初始化 Gadget
    gadget->ops = &udc_ops;
    gadget->ep0 = devm_kzalloc(&pdev->dev, sizeof(*gadget->ep0), GFP_KERNEL);
    if (!gadget->ep0) return -ENOMEM;
​
    // 3. 初始化端点0
    gadget->ep0->gadget = gadget;
    gadget->ep0->maxpacket = 64;
    gadget->ep0->bEndpointAddress = 0;
    gadget->ep0->bmAttributes = USB_ENDPOINT_XFER_CONTROL;
    gadget->ep0->ops = &udc_ep0_ops;
​
    // 4. 初始化 UDC
    udc->gadget = gadget;
    udc->name = dev_name(&pdev->dev);
    udc->udc_start = udc_start;
    udc->udc_stop = udc_stop;
​
    // 5. 注册 UDC
    ret = usb_add_gadget_udc(&pdev->dev, gadget);
    if (ret < 0) {
        dev_err(&pdev->dev, "Failed to add gadget UDC\n");
        return ret;
    }
​
    // 6. 设置平台驱动数据
    platform_set_drvdata(pdev, udc);
​
    dev_info(&pdev->dev, "UDC driver initialized\n");
    return 0;
}
​
/**
 * @brief UDC 驱动 remove 函数。
 */
static int udc_remove(struct platform_device *pdev)
{
    struct usb_udc *udc = platform_get_drvdata(pdev);
​
    // 1. 移除 UDC
    usb_del_gadget_udc(udc->gadget);
​
    return 0;
}

9.3.2 Gadget 功能驱动注册

/**
 * @brief Gadget 功能驱动注册(以串口为例)。
 * @param gadget 指向 Gadget
 * @param driver 指向 Gadget 驱动
 * @return 0 成功,负数错误
 */
static int g_serial_bind(struct usb_gadget *gadget,
                         struct usb_gadget_driver *driver)
{
    struct usb_composite_dev *cdev;
    int ret;
​
    // 1. 创建复合设备
    cdev = usb_composite_dev_create(gadget);
    if (IS_ERR(cdev)) {
        return PTR_ERR(cdev);
    }
​
    // 2. 注册串口功能
    ret = usb_string_ids_tab(cdev, g_serial_strings);
    if (ret < 0) {
        dev_err(&gadget->dev, "Failed to register strings\n");
        goto error;
    }
​
    // 3. 创建配置
    ret = usb_composite_add_config(cdev, CONFIG_ID,
                                   &g_serial_config, g_serial_config_ops);
    if (ret < 0) {
        dev_err(&gadget->dev, "Failed to add config\n");
        goto error;
    }
​
    // 4. 注册复合设备
    ret = usb_composite_register(cdev);
    if (ret < 0) {
        dev_err(&gadget->dev, "Failed to register composite\n");
        goto error;
    }
​
    return 0;
​
error:
    usb_composite_dev_destroy(cdev);
    return ret;
}
​
/**
 * @brief Gadget 串口功能驱动结构。
 */
static struct usb_gadget_driver g_serial_driver = {
    .name = "g_serial",
    .bind = g_serial_bind,
    .unbind = g_serial_unbind,
    .setup = g_serial_setup,
    .disconnect = g_serial_disconnect,
    .suspend = g_serial_suspend,
    .resume = g_serial_resume,
    .reset = g_serial_reset,
};
​
/**
 * @brief 注册串口 Gadget 驱动。
 */
static int __init g_serial_init(void)
{
    return usb_gadget_register_driver(&g_serial_driver);
}
​
/**
 * @brief 注销串口 Gadget 驱动。
 */
static void __exit g_serial_exit(void)
{
    usb_gadget_unregister_driver(&g_serial_driver);
}

9.3.3 端点操作实现

/**
 * @brief 端点控制传输处理。
 * @param ep 指向端点
 * @param req 指向请求
 * @return 0 成功,负数错误
 */
static int udc_ep0_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp)
{
    struct usb_gadget *gadget = ep->gadget;
    struct udc_driver *udc = gadget->private_data;
    int ret;
​
    // 1. 检查请求合法性
    if (!req || !req->buf || req->length == 0) {
        return -EINVAL;
    }
​
    // 2. 填充请求到硬件队列
    ret = udc_hw_queue(udc, ep, req);
    if (ret < 0) {
        dev_err(&gadget->dev, "HW queue failed\n");
        return ret;
    }
​
    // 3. 触发硬件传输
    udc_hw_start_transfer(udc, ep);
​
    return 0;
}
​
/**
 * @brief 端点完成回调。
 */
static void udc_ep0_complete(struct usb_ep *ep, struct usb_request *req)
{
    struct usb_gadget *gadget = ep->gadget;
    struct udc_driver *udc = gadget->private_data;
​
    // 1. 处理完成状态
    if (req->status == 0) {
        // 传输成功
        udc->ep0_state = EP0_STATE_COMPLETED;
    } else {
        // 传输失败
        udc->ep0_state = EP0_STATE_ERROR;
    }
​
    // 2. 触发后续处理
    schedule_work(&udc->ep0_work);
}

9.3.4 复合设备配置

/**
 * @brief 复合设备配置结构。
 */
static struct usb_configuration g_serial_config = {
    .label = "Serial Configuration",
    .config.bConfigurationValue = CONFIG_ID,
    .config.bmAttributes = USB_CONFIG_ATT_POWER,
    .config.bMaxPower = 100,
};
​
/**
 * @brief 创建串口功能实例。
 */
static int g_serial_create_interface(struct usb_composite_dev *cdev,
                                      struct usb_gadget *gadget)
{
    struct usb_function *func;
    int ret;
​
    // 1. 创建串口功能
    func = usb_alloc_function("serial", &g_serial_function_ops);
    if (IS_ERR(func)) {
        dev_err(&gadget->dev, "Failed to allocate function\n");
        return PTR_ERR(func);
    }
​
    // 2. 绑定功能到配置
    ret = usb_add_function(&g_serial_config, func);
    if (ret < 0) {
        dev_err(&gadget->dev, "Failed to add function to config\n");
        usb_free_function(func);
        return ret;
    }
​
    return 0;
}

9.3.5 用户空间 Gadget 配置(ConfigFS)

#!/bin/bash
# 配置 USB Gadget 为串口设备
​
# 1. 加载 Gadget 模块
modprobe libcomposite
modprobe usb_f_serial
​
# 2. 创建 Gadget 实例
mkdir -p /sys/kernel/config/usb_gadget/g_serial
cd /sys/kernel/config/usb_gadget/g_serial
​
# 3. 设置 USB ID
echo 0x1d6b > idVendor
echo 0x0104 > idProduct
​
# 4. 设置设备描述符
mkdir -p strings/0x409
echo "Linux Gadget Serial" > strings/0x409/manufacturer
echo "Gadget Serial Device" > strings/0x409/product
echo "123456789" > strings/0x409/serialnumber
​
# 5. 创建配置
mkdir -p configs/c.1
echo 100 > configs/c.1/MaxPower
​
# 6. 创建功能
mkdir -p functions/acm.usb0
ln -s functions/acm.usb0 configs/c.1
​
# 7. 绑定到 UDC
echo "udc0" > UDC
​
# 8. 创建设备节点
mknod /dev/ttyGS0 c 255 0

9.4 软件设计模式树形分析

USB Gadget 框架设计模式
├── 工厂模式 (Factory Pattern)
│   ├── usb_alloc_function():创建功能实例
│   └── usb_composite_dev_create():创建复合设备
├── 适配器模式 (Adapter Pattern)
│   └── usb_gadget_register_driver():适配 Gadget 驱动到 UDC
├── 策略模式 (Strategy Pattern)
│   └── 不同 Gadget 功能(串口/网卡/存储)实现不同的策略
├── 观察者模式 (Observer Pattern)
│   └── 端点完成回调观察硬件传输事件
├── 装饰器模式 (Decorator Pattern)
│   └── 复合设备配置装饰基本功能
├── 单例模式 (Singleton Pattern)
│   └── 每个 UDC 只有一个 Gadget 实例
└── 模板方法模式 (Template Method Pattern)
    └── usb_gadget_register_driver():定义了注册的标准流程

9.5 Gadget 调试核心难点

9.5.1 UDC 绑定失败

现象usb_add_gadget_udc 返回 -ENODEV

原因

  1. UDC 驱动未正确注册。

  2. 设备树中 UDC 节点不存在。

  3. UDC 硬件未使能。

解决方法

  1. 检查 ls /sys/class/udc/ 确认 UDC 设备。

  2. 检查设备树节点。

  3. 确认 UDC 时钟和电源正常。

9.5.2 主机识别失败

现象:插入 USB 线后主机无响应。

原因

  1. Gadget 驱动未正确绑定。

  2. 端点配置错误。

  3. 硬件连接问题。

解决方法

  1. 检查 dmesg | grep gadget

  2. 检查 configfs 配置是否正确。

  3. 检查硬件连接。

9.5.3 传输超时

现象:Gadget 传输超时,功能无法正常工作。

原因

  1. 端点缓冲区大小设置不当。

  2. 传输速度不匹配。

  3. UDC 硬件问题。

解决方法

  1. 增加缓冲区大小。

  2. 调整传输速度。

  3. 检查 UDC 硬件。

9.6 与其他模块的协同

模块 协同方式 调试关键点
Platform 驱动 提供 UDC 硬件访问 资源映射、中断分配
DMA 控制器 提供 Gadget 数据传输 DMA 配置、缓冲区管理
中断控制器 处理 USB 设备端中断 中断号、亲和性
电源管理 管理 Gadget 唤醒和低功耗 唤醒能力、电源状态
ConfigFS 用户空间配置 Gadget 功能 配置语法、功能绑定
用户空间 通过设备节点访问 Gadget 权限、设备命名
USB 核心层 提供 Gadget 框架基础设施 驱动注册、事件处理

第十部分 USB 电源管理与远程唤醒

10.1 USB 电源管理概述

USB 电源管理是确保系统在连接 USB 设备时能够高效使用电能的关键机制。USB 规范定义了多种电源状态,包括 挂起 (Suspend)恢复 (Resume)远程唤醒 (Remote Wakeup)选择性挂起 (Selective Suspend)

10.1.1 USB 电源状态转换图

[正常状态 (D0)]
    ↓
[挂起请求 → D3 (挂起)]
    ↓
[主机唤醒 (Resume)]    ← [设备远程唤醒 (Remote Wakeup)]
    ↓
[恢复到 D0]

10.1.2 USB 设备电源状态

状态 描述 功耗 唤醒能力
D0 正常工作状态 立即响应
D1 深度空闲 支持
D2 轻量挂起 支持
D3 完全挂起 极低 支持远程唤醒

10.2 核心数据结构

10.2.1 USB 电源管理配置结构

/**
 * @struct usb_power_config
 * @brief USB 电源管理配置结构。
 */
struct usb_power_config {
    bool autosuspend_enabled;       /**< 是否启用自动挂起 */
    bool remote_wakeup_enabled;     /**< 是否启用远程唤醒 */
    int autosuspend_delay_ms;       /**< 自动挂起延迟 (毫秒) */
    int wakeup_irq;                 /**< 唤醒中断号 */
    struct device *dev;             /**< 设备指针 */
    struct usb_device *udev;        /**< USB 设备 */
    struct timer_list suspend_timer; /**< 挂起定时器 */
    struct work_struct wakeup_work; /**< 唤醒工作队列 */
    spinlock_t lock;                /**< 自旋锁 */
    bool suspended;                 /**< 是否已挂起 */
    struct usb_power_data *data;    /**< 电源数据 */
};
​
/**
 * @struct usb_power_data
 * @brief USB 电源管理数据。
 */
struct usb_power_data {
    unsigned int hub_good_delay;     /**< 集线器就绪延时 */
    unsigned int remote_wakeup_delay; /**< 远程唤醒延时 */
    unsigned int reset_resume_delay; /**< 复位唤醒延时 */
    unsigned int resume_delay;       /**< 唤醒延时 */
    struct usb_device *udev;         /**< USB 设备 */
    struct work_struct reset_work;   /**< 复位工作队列 */
};

10.3 核心代码实现

10.3.1 USB 设备挂起

/**
 * @brief USB 设备挂起函数。
 * @param udev USB 设备指针
 * @param message 电源消息
 * @return 0 成功,负数错误
 */
static int usb_device_suspend(struct usb_device *udev, pm_message_t message)
{
    struct usb_power_config *config = udev->power_config;
    unsigned long flags;
    int ret;
​
    // 1. 检查是否允许挂起
    if (!config->autosuspend_enabled) {
        dev_dbg(&udev->dev, "Auto-suspend disabled\n");
        return -EBUSY;
    }
​
    // 2. 锁定设备状态
    spin_lock_irqsave(&config->lock, flags);
    if (config->suspended) {
        spin_unlock_irqrestore(&config->lock, flags);
        return 0;
    }
​
    // 3. 通知设备驱动准备挂起
    ret = usb_suspend_both(udev, message);
    if (ret < 0) {
        dev_err(&udev->dev, "Device suspend failed: %d\n", ret);
        spin_unlock_irqrestore(&config->lock, flags);
        return ret;
    }
​
    // 4. 设置挂起状态
    config->suspended = true;
    udev->state = USB_STATE_SUSPENDED;
    spin_unlock_irqrestore(&config->lock, flags);
​
    // 5. 停止定时器
    del_timer(&config->suspend_timer);
​
    dev_info(&udev->dev, "Device suspended\n");
    return 0;
}

10.3.2 USB 设备唤醒

/**
 * @brief USB 设备唤醒函数。
 * @param udev USB 设备指针
 * @return 0 成功,负数错误
 */
static int usb_device_resume(struct usb_device *udev)
{
    struct usb_power_config *config = udev->power_config;
    unsigned long flags;
    int ret;
​
    // 1. 检查状态
    spin_lock_irqsave(&config->lock, flags);
    if (!config->suspended) {
        spin_unlock_irqrestore(&config->lock, flags);
        return 0;
    }
​
    // 2. 通知设备驱动恢复
    ret = usb_resume_both(udev, PM_EVENT_RESUME);
    if (ret < 0) {
        dev_err(&udev->dev, "Device resume failed: %d\n", ret);
        spin_unlock_irqrestore(&config->lock, flags);
        return ret;
    }
​
    // 3. 重置挂起状态
    config->suspended = false;
    udev->state = USB_STATE_CONFIGURED;
    spin_unlock_irqrestore(&config->lock, flags);
​
    // 4. 重新启动挂起定时器
    if (config->autosuspend_enabled) {
        mod_timer(&config->suspend_timer,
                  jiffies + msecs_to_jiffies(config->autosuspend_delay_ms));
    }
​
    dev_info(&udev->dev, "Device resumed\n");
    return 0;
}

10.3.3 远程唤醒实现

/**
 * @brief USB 远程唤醒请求处理。
 * @param udev USB 设备指针
 * @return 0 成功,负数错误
 */
static int usb_remote_wakeup(struct usb_device *udev)
{
    struct usb_power_config *config = udev->power_config;
    int ret;
​
    // 1. 检查唤醒能力
    if (!config->remote_wakeup_enabled) {
        dev_dbg(&udev->dev, "Remote wakeup disabled\n");
        return -EACCES;
    }
​
    // 2. 执行远程唤醒
    ret = usb_device_resume(udev);
    if (ret < 0) {
        dev_err(&udev->dev, "Remote wakeup failed: %d\n", ret);
        return ret;
    }
​
    // 3. 发送唤醒信号到主机
    ret = usb_hcd_resume(udev->bus->hcd);
    if (ret < 0) {
        dev_err(&udev->dev, "HCD resume failed: %d\n", ret);
        return ret;
    }
​
    dev_info(&udev->dev, "Remote wakeup successful\n");
    return 0;
}
​
/**
 * @brief 远程唤醒中断处理函数。
 */
static irqreturn_t usb_wakeup_irq_handler(int irq, void *dev_id)
{
    struct usb_device *udev = dev_id;
    struct usb_power_config *config = udev->power_config;
​
    // 1. 检查设备状态
    if (!config->suspended) {
        return IRQ_NONE;
    }
​
    // 2. 调度唤醒工作队列
    schedule_work(&config->wakeup_work);
​
    return IRQ_HANDLED;
}
​
/**
 * @brief 唤醒工作队列处理函数。
 */
static void usb_wakeup_work_handler(struct work_struct *work)
{
    struct usb_power_config *config = container_of(work, struct usb_power_config, wakeup_work);
    struct usb_device *udev = config->udev;
​
    // 执行远程唤醒
    usb_remote_wakeup(udev);
}

10.3.4 自动挂起定时器

/**
 * @brief 自动挂起定时器回调。
 */
static void usb_autosuspend_timer_callback(struct timer_list *t)
{
    struct usb_power_config *config = from_timer(config, t, suspend_timer);
    struct usb_device *udev = config->udev;
​
    // 1. 检查设备是否繁忙
    if (!config->suspended && !usb_device_is_busy(udev)) {
        // 2. 执行自动挂起
        pm_message_t message;
        message.event = PM_EVENT_SUSPEND;
        usb_device_suspend(udev, message);
    } else {
        // 3. 重置定时器
        mod_timer(&config->suspend_timer,
                  jiffies + msecs_to_jiffies(config->autosuspend_delay_ms));
    }
}
​
/**
 * @brief 启动自动挂起定时器。
 */
static void usb_autosuspend_start(struct usb_device *udev)
{
    struct usb_power_config *config = udev->power_config;
​
    if (!config->autosuspend_enabled) {
        return;
    }
​
    // 1. 初始化定时器
    timer_setup(&config->suspend_timer, usb_autosuspend_timer_callback, 0);
    config->suspend_timer.expires = jiffies + msecs_to_jiffies(config->autosuspend_delay_ms);
    add_timer(&config->suspend_timer);
​
    dev_dbg(&udev->dev, "Auto-suspend timer started\n");
}

10.3.5 电源管理接口

/**
 * @brief USB 电源管理操作结构。
 */
static const struct dev_pm_ops usb_pm_ops = {
    .suspend = usb_pm_suspend,
    .resume = usb_pm_resume,
    .freeze = usb_pm_freeze,
    .thaw = usb_pm_thaw,
    .poweroff = usb_pm_poweroff,
    .restore = usb_pm_restore,
    .runtime_suspend = usb_pm_runtime_suspend,
    .runtime_resume = usb_pm_runtime_resume,
    .runtime_idle = usb_pm_runtime_idle,
};
​
/**
 * @brief 运行时挂起处理。
 */
static int usb_pm_runtime_suspend(struct device *dev)
{
    struct usb_device *udev = to_usb_device(dev);
    pm_message_t message;
​
    message.event = PM_EVENT_SUSPEND;
    return usb_device_suspend(udev, message);
}
​
/**
 * @brief 运行时唤醒处理。
 */
static int usb_pm_runtime_resume(struct device *dev)
{
    struct usb_device *udev = to_usb_device(dev);
    return usb_device_resume(udev);
}

10.4 软件设计模式树形分析

USB 电源管理设计模式
├── 状态模式 (State Pattern)
│   └── USB 设备电源状态 (D0 → D1 → D2 → D3)
├── 观察者模式 (Observer Pattern)
│   ├── 中断处理观察硬件唤醒事件
│   └── 定时器观察设备空闲状态
├── 策略模式 (Strategy Pattern)
│   ├── 自动挂起策略 (延迟时间、条件检查)
│   └── 唤醒策略 (远程唤醒 vs 主机唤醒)
├── 命令模式 (Command Pattern)
│   └── usb_device_suspend/resume 封装为命令
├── 装饰器模式 (Decorator Pattern)
│   └── 为电源管理添加延时和重试机制
└── 模板方法模式 (Template Method Pattern)
    └── usb_pm_runtime_suspend:定义了运行时挂起的标准流程

10.5 电源管理调试核心难点

10.5.1 设备无法挂起

现象usb_device_suspend 返回 -EBUSY,设备无法进入挂起状态。

原因

  1. 设备驱动禁止自动挂起。

  2. 设备上有未完成的数据传输。

  3. 设备引用计数不为零。

解决方法

  1. 检查设备驱动的 autosuspend 设置。

  2. 取消所有未完成的 URB。

  3. 检查引用计数。

10.5.2 远程唤醒无响应

现象:设备发送远程唤醒信号后,主机无响应。

原因

  1. 远程唤醒功能未启用。

  2. 主机不支持远程唤醒。

  3. 唤醒信号被过滤。

解决方法

  1. 检查 sysfspower/wakeup 设置。

  2. 检查主机控制器是否支持远程唤醒。

  3. 使用 usbmon 监测唤醒信号。

10.5.3 自动挂起触发频繁

现象:设备频繁进入和退出挂起状态,影响性能。

原因

  1. 自动挂起延迟设置过短。

  2. 设备间歇性活动。

  3. 驱动未正确处理 URB 活动。

解决方法

  1. 增加自动挂起延迟时间。

  2. 优化设备驱动,减少不必要的活动。

  3. 使用 usb_kill_urb 清理 URB。

10.5.4 唤醒后设备无法正常工作

现象:设备唤醒后,传输错误或功能异常。

原因

  1. 设备驱动未正确处理唤醒事件。

  2. 硬件状态未完全恢复。

  3. 唤醒过程中数据丢失。

解决方法

  1. 在驱动 resume 函数中恢复硬件状态。

  2. 重新初始化设备。

  3. 重新提交 URB。

10.6 与其他模块的协同

模块 协同方式 调试关键点
USB 核心层 提供电源管理接口 设备状态、引用计数
HCD 驱动 执行挂起/唤醒硬件操作 寄存器配置、唤醒信号
PCI/Platform 驱动 管理 USB 控制器电源 时钟、电源域
设备驱动 响应电源管理事件 状态保存、恢复
中断控制器 处理远程唤醒中断 中断号、优先级
电源管理框架 集成系统级电源管理 睡眠、唤醒
sysfs 用户空间控制电源参数 自动挂起、唤醒
用户空间 通过 pm-autosuspend 工具调试 延迟设置、状态查询

第十一部分 USB 与 Input 子系统的集成

11.1 USB HID 与 Input 子系统的关系

USB HID(Human Interface Device)类是 USB 设备中最常见的类型之一,包括键盘、鼠标、触摸屏、游戏手柄等。HID 驱动将 USB 中断传输的数据转换为标准化的输入事件,通过 Input 子系统上报给用户空间。

11.1.1 系统架构

[USB HID 设备] ↔ [USB 核心层] ↔ [usbhid 驱动] ↔ [Input 核心层] ↔ [用户空间]
                                                              ↓
                                                      [evdev 接口]
                                                              ↓
                                                      [应用程序]

11.1.2 数据流

[硬件中断] → [URB 完成] → [HID 报告解析] → [Input 事件映射] → [事件上报]
                     ↓                                 ↓
               [原始 HID 报告]               [input_event 结构]

11.2 核心数据结构

11.2.1 HID 到 Input 的映射结构

/**
 * @struct hid_input
 * @brief HID 输入设备结构,连接 HID 和 Input 子系统。
 */
struct hid_input {
    struct list_head list;          /**< 链表节点 */
    struct hid_device *hid;         /**< 关联的 HID 设备 */
    struct input_dev *input;        /**< Input 设备 */
    struct hid_report *report;      /**< 关联的 HID 报告 */
    unsigned int registered:1;      /**< 是否已注册 */
    unsigned int enabled:1;         /**< 是否已启用 */
    unsigned int quirks;            /**< 特殊标志 */
    void *private_data;             /**< 私有数据 */
};
​
/**
 * @struct hid_usage
 * @brief HID 用法结构,表示 HID 报告的某个字段的含义。
 */
struct hid_usage {
    unsigned int hid;               /**< 用法 HID 代码 */
    unsigned int usage_index;       /**< 用法索引 */
    unsigned int collection_index;  /**< 集合索引 */
    struct hid_field *field;        /**< 关联的字段 */
    struct hid_report *report;      /**< 关联的报告 */
    unsigned int size;              /**< 大小 (位) */
    unsigned int logical_minimum;   /**< 逻辑最小值 */
    unsigned int logical_maximum;   /**< 逻辑最大值 */
    unsigned int physical_minimum;  /**< 物理最小值 */
    unsigned int physical_maximum;  /**< 物理最大值 */
    void *private_data;             /**< 私有数据 */
};
​
/**
 * @struct hid_field
 * @brief HID 字段结构,代表 HID 报告中的一个数据字段。
 */
struct hid_field {
    struct hid_report *report;      /**< 关联的报告 */
    unsigned int index;             /**< 字段索引 */
    unsigned int type;              /**< 字段类型 (输入/输出/特征) */
    unsigned int flags;             /**< 字段标志 */
    unsigned int report_count;      /**< 报告数量 */
    unsigned int report_size;       /**< 报告大小 (位) */
    unsigned int logical_minimum;   /**< 逻辑最小值 */
    unsigned int logical_maximum;   /**< 逻辑最大值 */
    unsigned int physical_minimum;  /**< 物理最小值 */
    unsigned int physical_maximum;  /**< 物理最大值 */
    struct hid_usage *usage;        /**< 用法列表 */
    unsigned int usage_count;       /**< 用法数量 */
};

11.3 核心代码实现

11.3.1 报告解析

/**
 * @brief 解析 HID 报告描述符。
 * @param hdev HID 设备指针
 * @param rdesc 报告描述符数据
 * @param rsize 描述符大小
 * @return 0 成功,负数错误
 */
static int hid_parse_report(struct hid_device *hdev,
                            const u8 *rdesc, int rsize)
{
    struct hid_parser *parser;
    int ret;
​
    // 1. 创建解析器
    parser = hid_parser_alloc(hdev);
    if (!parser) {
        return -ENOMEM;
    }
​
    // 2. 解析报告描述符
    ret = hid_parse_report_parser(parser, rdesc, rsize);
    if (ret < 0) {
        dev_err(&hdev->dev, "Report descriptor parsing failed\n");
        goto error;
    }
​
    // 3. 生成报告映射
    ret = hid_report_parse_to_map(parser);
    if (ret < 0) {
        dev_err(&hdev->dev, "Report mapping failed\n");
        goto error;
    }
​
    // 4. 保存解析结果
    hdev->report_enum = parser->report_enum;
​
    hid_parser_free(parser);
    return 0;
​
error:
    hid_parser_free(parser);
    return ret;
}

11.3.2 Input 设备注册

/**
 * @brief 从 HID 设备注册 Input 设备。
 * @param hdev HID 设备指针
 * @return 0 成功,负数错误
 */
static int hid_register_input_device(struct hid_device *hdev)
{
    struct input_dev *input;
    struct hid_input *hidinput;
    int ret;
​
    // 1. 分配 Input 设备
    input = input_allocate_device();
    if (!input) {
        dev_err(&hdev->dev, "Failed to allocate input device\n");
        return -ENOMEM;
    }
​
    // 2. 设置 Input 设备属性
    input->name = "USB HID Device";
    input->phys = dev_name(&hdev->dev);
    input->dev.parent = &hdev->dev;
    input->id.bustype = BUS_USB;
    input->id.vendor = hdev->vendor;
    input->id.product = hdev->product;
    input->id.version = hdev->version;
​
    // 3. 设置能力位
    __set_bit(EV_KEY, input->evbit);
    __set_bit(EV_REL, input->evbit);
    __set_bit(EV_ABS, input->evbit);
​
    // 4. 注册 Input 设备
    ret = input_register_device(input);
    if (ret < 0) {
        dev_err(&hdev->dev, "Failed to register input device\n");
        input_free_device(input);
        return ret;
    }
​
    // 5. 创建 HID 输入结构
    hidinput = kzalloc(sizeof(struct hid_input), GFP_KERNEL);
    if (!hidinput) {
        input_unregister_device(input);
        return -ENOMEM;
    }
​
    hidinput->hid = hdev;
    hidinput->input = input;
    hidinput->registered = 1;
    list_add_tail(&hidinput->list, &hdev->inputs);
​
    dev_info(&hdev->dev, "Input device registered\n");
    return 0;
}

11.3.3 HID 报告到 Input 事件映射

/**
 * @brief 映射 HID 报告到 Input 事件。
 * @param hdev HID 设备指针
 * @param field HID 字段指针
 * @param usage HID 用法指针
 * @param value 当前值
 * @return 0 成功,负数错误
 */
static int hid_map_usage_to_input(struct hid_device *hdev,
                                  struct hid_field *field,
                                  struct hid_usage *usage,
                                  int value)
{
    struct input_dev *input = hdev->inputs->input;
    int event_code;
    int event_type;
​
    // 1. 确定 HID 用法类型
    switch (usage->hid & HID_USAGE_PAGE) {
    case HID_UP_KEYBOARD:
        // 键盘按键
        event_type = EV_KEY;
        event_code = hid_usage_to_key(usage->hid);
        break;
​
    case HID_UP_BUTTON:
        // 鼠标按键
        event_type = EV_KEY;
        event_code = hid_usage_to_button(usage->hid);
        break;
​
    case HID_UP_GENERIC_DESKTOP:
        switch (usage->hid & 0xFFFF) {
        case HID_GD_X:
            event_type = EV_REL;
            event_code = REL_X;
            break;
        case HID_GD_Y:
            event_type = EV_REL;
            event_code = REL_Y;
            break;
        case HID_GD_Z:
            event_type = EV_REL;
            event_code = REL_Z;
            break;
        case HID_GD_WHEEL:
            event_type = EV_REL;
            event_code = REL_WHEEL;
            break;
        case HID_GD_HWHEEL:
            event_type = EV_REL;
            event_code = REL_HWHEEL;
            break;
        default:
            return -EINVAL;
        }
        break;
​
    default:
        return -EINVAL;
    }
​
    // 2. 映射特殊标志
    if (usage->flags & HID_USAGE_FLAG_SPECIAL) {
        // 处理特殊标志
        usage->flags &= ~HID_USAGE_FLAG_SPECIAL;
    }
​
    // 3. 保存映射
    input_map_usage(usage, field, input, event_type, event_code);
​
    return 0;
}

11.3.4 HID 报告处理

/**
 * @brief 处理接收到的 HID 报告。
 * @param hdev HID 设备指针
 * @param report HID 报告指针
 * @param data 报告数据
 * @param size 报告大小
 * @return 0 成功,负数错误
 */
static int hid_process_report(struct hid_device *hdev,
                              struct hid_report *report,
                              const u8 *data, int size)
{
    struct hid_field *field;
    struct hid_usage *usage;
    struct input_dev *input = hdev->inputs->input;
    int i, j, k;
​
    // 1. 遍历所有字段
    for (i = 0; i < report->maxfield; i++) {
        field = report->field[i];
        if (!field) continue;
​
        // 2. 遍历字段中的所有用法
        for (j = 0; j < field->usage_count; j++) {
            usage = &field->usage[j];
            if (!usage) continue;
​
            // 3. 计算字段值
            int value = hid_field_extract(data, field->report_count, field->report_size, j);
            if (value < field->logical_minimum ||
                value > field->logical_maximum) {
                continue;
            }
​
            // 4. 处理输入字段
            if (field->type == HID_INPUT) {
                // 映射到 Input 事件
                switch (usage->hid & HID_USAGE_PAGE) {
                case HID_UP_KEYBOARD:
                case HID_UP_BUTTON:
                    // 按键事件
                    for (k = 0; k < field->report_count; k++) {
                        int key = hid_usage_to_key(usage->hid);
                        input_report_key(input, key, value);
                    }
                    break;
​
                case HID_UP_GENERIC_DESKTOP:
                    // 鼠标移动
                    switch (usage->hid & 0xFFFF) {
                    case HID_GD_X:
                        input_report_rel(input, REL_X, value);
                        break;
                    case HID_GD_Y:
                        input_report_rel(input, REL_Y, value);
                        break;
                    }
                    break;
                }
            }
        }
    }
​
    // 5. 同步 Input 事件
    input_sync(input);
​
    return 0;
}

11.3.5 键盘按键映射示例

/**
 * @brief HID 用法到键盘按键的映射表。
 */
static const int hid_usage_to_key_map[] = {
    [0x04] = KEY_A,
    [0x05] = KEY_B,
    [0x06] = KEY_C,
    [0x07] = KEY_D,
    [0x08] = KEY_E,
    [0x09] = KEY_F,
    [0x0A] = KEY_G,
    [0x0B] = KEY_H,
    [0x0C] = KEY_I,
    [0x0D] = KEY_J,
    [0x0E] = KEY_K,
    [0x0F] = KEY_L,
    [0x10] = KEY_M,
    [0x11] = KEY_N,
    [0x12] = KEY_O,
    [0x13] = KEY_P,
    [0x14] = KEY_Q,
    [0x15] = KEY_R,
    [0x16] = KEY_S,
    [0x17] = KEY_T,
    [0x18] = KEY_U,
    [0x19] = KEY_V,
    [0x1A] = KEY_W,
    [0x1B] = KEY_X,
    [0x1C] = KEY_Y,
    [0x1D] = KEY_Z,
};
​
/**
 * @brief 从 HID 用法获取键盘按键。
 * @param usage HID 用法代码
 * @return 按键码
 */
static int hid_usage_to_key(int usage)
{
    int idx = usage & 0xFF;
    if (idx < ARRAY_SIZE(hid_usage_to_key_map)) {
        return hid_usage_to_key_map[idx];
    }
    return KEY_UNKNOWN;
}

11.3.6 触摸屏处理

/**
 * @brief 触摸屏 HID 报告处理。
 * @param hdev HID 设备指针
 * @param report HID 报告指针
 * @param data 报告数据
 * @param size 报告大小
 * @return 0 成功,负数错误
 */
static int hid_process_touch_report(struct hid_device *hdev,
                                    struct hid_report *report,
                                    const u8 *data, int size)
{
    struct input_dev *input = hdev->inputs->input;
    struct hid_field *field;
    int i, j;
    int x = 0, y = 0, pressure = 0, contact_id = 0;
​
    // 1. 提取触摸坐标
    for (i = 0; i < report->maxfield; i++) {
        field = report->field[i];
        if (!field) continue;
​
        for (j = 0; j < field->usage_count; j++) {
            struct hid_usage *usage = &field->usage[j];
            int value = hid_field_extract(data, field->report_count, field->report_size, j);
​
            switch (usage->hid & 0xFFFF) {
            case HID_GD_X:
                x = value;
                break;
            case HID_GD_Y:
                y = value;
                break;
            case HID_DG_CONTACTID:
                contact_id = value;
                break;
            case HID_DG_TIPSWITCH:
                input_report_key(input, BTN_TOUCH, value);
                break;
            case HID_DG_PRESSURE:
                pressure = value;
                break;
            }
        }
    }
​
    // 2. 上报触摸事件
    input_report_abs(input, ABS_MT_POSITION_X, x);
    input_report_abs(input, ABS_MT_POSITION_Y, y);
    input_report_abs(input, ABS_MT_PRESSURE, pressure);
    input_report_abs(input, ABS_MT_SLOT_ID, contact_id);
​
    // 3. 同步事件
    input_sync(input);
​
    return 0;
}

11.4 软件设计模式树形分析

USB HID 与 Input 集成设计模式
├── 适配器模式 (Adapter Pattern)
│   ├── HID 报告 → Input 事件转换适配器
│   └── HID 用法 → Input 按键映射适配器
├── 工厂模式 (Factory Pattern)
│   ├── hid_register_input_device():创建 Input 设备实例
│   └── hid_parser_alloc():创建 HID 解析器实例
├── 策略模式 (Strategy Pattern)
│   ├── hid_map_usage_to_input():不同 HID 用法的映射策略
│   └── hid_process_report():不同报告类型的处理策略
├── 观察者模式 (Observer Pattern)
│   ├── 报告处理观察 HID 事件
│   └── 中断处理观察硬件中断
├── 装饰器模式 (Decorator Pattern)
│   └── 为 Input 事件添加时间戳和序列号
└── 模板方法模式 (Template Method Pattern)
    └── hid_process_report():定义了报告处理的标准流程

11.5 资深视角:HID 与 Input 调试核心难点

11.5.1 HID 报告解析错误

现象hid_parse_report 返回 -EINVAL,设备无法识别。

原因

  1. 报告描述符格式错误。

  2. 描述符长度与设备返回不符。

  3. 设备返回的报告描述符不完整。

解决方法

  1. 使用 hidraw 直接读取报告描述符。

  2. 使用 usbmon 抓取描述符传输数据。

  3. 添加调试打印,输出描述符内容。

11.5.2 按键映射错误

现象:按键按下后,系统收到的按键码与物理按键不符。

原因

  1. HID 用法到按键码的映射错误。

  2. HID 报告数据提取错误。

  3. 键盘布局配置错误。

解决方法

  1. 检查 HID 用法到按键码的映射表。

  2. 使用 evtest 查看实际按键码。

  3. 检查键盘布局设置。

11.5.3 触摸屏多点触控无效

现象:触摸屏支持多点触控,但只能识别单点。

原因

  1. 触摸屏 HID 报告不支持多点触控。

  2. 未正确解析 MT 协议。

  3. Input 设备未配置 MT 能力。

解决方法

  1. 检查触摸屏 HID 报告描述符。

  2. 配置 Input 设备的 MT 能力。

  3. 正确上报 MT 事件。

11.5.4 事件上报延迟

现象:输入事件上报有延迟,影响用户体验。

原因

  1. URB 传输间隔设置不当。

  2. 报告处理路径效率低下。

  3. 系统调度延迟。

解决方法

  1. 调整 URB 传输间隔。

  2. 优化报告处理代码。

  3. 使用实时优先级。

11.6 与其他模块的协同

模块 协同方式 调试关键点
USB 核心层 提供 URB 传输和端点配置 传输类型、间隔设置
Input 核心层 提供 Input 设备注册和事件上报 事件类型、设备能力
HID 核心层 提供 HID 报告解析和管理 报告描述符、字段解析
用户空间 通过 evdev 接口获取事件 设备节点、权限控制
evdev 提供用户空间访问接口 事件队列、缓冲区管理
sysfs 查看和配置 HID 设备参数 设备信息、调试开关
电源管理 设备空闲时自动挂起 唤醒能力、自动挂起

第十二部分 USB 与 V4L2 子系统的集成

12.1 USB 视频设备与 V4L2 的关系

USB 视频设备类(UVC)是 USB 设备类中最常见的类型之一,包括网络摄像头、视频采集卡、USB 显微镜等。UVC 驱动将 USB 批量/等时传输的数据转换为标准的 V4L2 视频帧,通过 V4L2 子系统上报给用户空间。

12.1.1 系统架构

[USB 摄像头] ↔ [USB 核心层] ↔ [uvcvideo 驱动] ↔ [V4L2 核心层] ↔ [用户空间]
                                                              ↓
                                                      [V4L2 设备节点]
                                                              ↓
                                                      [应用程序]

12.1.2 数据流

[硬件中断] → [URB 完成] → [帧数据解析] → [V4L2 缓冲区] → [用户空间]
                     ↓                                 ↓
               [原始视频数据]               [v4l2_buffer 结构]

12.2 核心数据结构

12.2.1 UVC 设备结构

/**
 * @struct uvc_device
 * @brief UVC 设备结构,管理 UVC 摄像头实例。
 */
struct uvc_device {
    struct usb_device *udev;        /**< USB 设备指针 */
    struct usb_interface *intf;     /**< USB 接口 */
    struct video_device *vdev;      /**< V4L2 视频设备 */
    struct v4l2_device v4l2_dev;    /**< V4L2 设备 */
    struct uvc_streaming *streaming; /**< 流接口 */
    struct uvc_control *control;    /**< 控制接口 */
    struct list_head entities;      /**< 实体列表 */
    int uvc_version;                /**< UVC 版本 */
    int video_format;               /**< 视频格式 */
    int width;                      /**< 图像宽度 */
    int height;                     /**< 图像高度 */
    int frame_rate;                 /**< 帧率 */
    int frame_interval;             /**< 帧间隔 */
    int payload_size;               /**< 负载大小 */
    struct uvc_buffer *buffer;      /**< 当前缓冲区 */
    struct list_head free_buffers;  /**< 空闲缓冲区列表 */
    struct list_head done_buffers;  /**< 完成缓冲区列表 */
    spinlock_t lock;                /**< 自旋锁 */
    wait_queue_head_t wait;         /**< 等待队列 */
    int streaming_state;            /**< 流状态 */
};
​
/**
 * @struct uvc_streaming
 * @brief UVC 流接口结构。
 */
struct uvc_streaming {
    struct uvc_device *dev;         /**< 所属设备 */
    struct usb_endpoint_descriptor *ep; /**< 端点描述符 */
    int endpoint;                   /**< 端点地址 */
    int bInterval;                  /**< 中断间隔 */
    int max_packet_size;            /**< 最大包大小 */
    int pkt_size;                   /**< 包大小 */
    int nb_isoc_packets;            /**< 等时包数量 */
    struct urb **urb;               /**< URB 数组 */
    struct uvc_video_chain *chain;  /**< 视频链 */
    struct uvc_format *format;      /**< 格式 */
    struct uvc_frame *frame;        /**< 帧 */
    struct list_head urbs;          /**< URB 列表 */
    int packets_dropped;            /**< 丢包计数 */
    int packets_processed;          /**< 已处理包计数 */
    int packets_error;              /**< 错误包计数 */
};

12.3 核心代码实现

12.3.1 UVC 驱动初始化

#include <linux/module.h>
#include <linux/usb.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
​
/**
 * @brief UVC 设备 probe 函数。
 * @param intf USB 接口指针
 * @param id 匹配的 USB 设备 ID
 * @return 0 成功,负数错误
 */
static int uvc_probe(struct usb_interface *intf,
                     const struct usb_device_id *id)
{
    struct usb_device *udev = interface_to_usbdev(intf);
    struct uvc_device *uvc;
    int ret;
​
    // 1. 分配 UVC 设备结构
    uvc = kzalloc(sizeof(struct uvc_device), GFP_KERNEL);
    if (!uvc) return -ENOMEM;
​
    uvc->udev = udev;
    uvc->intf = intf;
    spin_lock_init(&uvc->lock);
    init_waitqueue_head(&uvc->wait);
    INIT_LIST_HEAD(&uvc->free_buffers);
    INIT_LIST_HEAD(&uvc->done_buffers);
​
    // 2. 注册 V4L2 设备
    ret = v4l2_device_register(&intf->dev, &uvc->v4l2_dev);
    if (ret < 0) {
        dev_err(&intf->dev, "Failed to register V4L2 device\n");
        goto error;
    }
​
    // 3. 解析 UVC 控制接口
    ret = uvc_parse_control(uvc);
    if (ret < 0) {
        dev_err(&intf->dev, "Failed to parse control interface\n");
        goto error_v4l2;
    }
​
    // 4. 创建视频设备
    uvc->vdev = video_device_alloc();
    if (!uvc->vdev) {
        ret = -ENOMEM;
        goto error_v4l2;
    }
​
    // 5. 设置视频设备操作
    uvc->vdev->v4l2_dev = &uvc->v4l2_dev;
    uvc->vdev->fops = &uvc_fops;
    uvc->vdev->ioctl_ops = &uvc_ioctl_ops;
    uvc->vdev->release = video_device_release;
    uvc->vdev->queue = &uvc->queue;
    uvc->vdev->name = "USB Camera";
​
    // 6. 注册视频设备
    ret = video_register_device(uvc->vdev, VFL_TYPE_VIDEO, -1);
    if (ret < 0) {
        dev_err(&intf->dev, "Failed to register video device\n");
        goto error_vdev;
    }
​
    // 7. 保存设备数据
    usb_set_intfdata(intf, uvc);
​
    dev_info(&intf->dev, "UVC camera device registered\n");
    return 0;
​
error_vdev:
    video_device_release(uvc->vdev);
error_v4l2:
    v4l2_device_unregister(&uvc->v4l2_dev);
error:
    kfree(uvc);
    return ret;
}

12.3.2 流接口配置

/**
 * @brief 配置 UVC 流接口。
 * @param uvc UVC 设备指针
 * @param format 视频格式
 * @param width 宽度
 * @param height 高度
 * @param frame_rate 帧率
 * @return 0 成功,负数错误
 */
static int uvc_set_streaming_config(struct uvc_device *uvc,
                                    int format, int width, int height,
                                    int frame_rate)
{
    struct uvc_streaming *streaming;
    struct usb_interface *intf = uvc->intf;
    int ret;
​
    // 1. 检查流接口是否存在
    if (uvc->streaming == NULL) {
        dev_err(&intf->dev, "No streaming interface\n");
        return -ENODEV;
    }
​
    streaming = uvc->streaming;
​
    // 2. 选择格式
    ret = uvc_select_format(streaming, format, width, height);
    if (ret < 0) {
        dev_err(&intf->dev, "Format not supported\n");
        return ret;
    }
​
    // 3. 选择帧率和帧大小
    ret = uvc_select_frame(streaming, frame_rate);
    if (ret < 0) {
        dev_err(&intf->dev, "Frame rate not supported\n");
        return ret;
    }
​
    // 4. 保存配置
    uvc->video_format = format;
    uvc->width = width;
    uvc->height = height;
    uvc->frame_rate = frame_rate;
    uvc->payload_size = streaming->pkt_size * streaming->nb_isoc_packets;
​
    // 5. 配置端点
    ret = usb_set_interface(uvc->udev,
                            streaming->ep->bInterfaceNumber,
                            streaming->ep->bAlternateSetting);
    if (ret < 0) {
        dev_err(&intf->dev, "Failed to set interface\n");
        return ret;
    }
​
    dev_info(&intf->dev, "Streaming configured: %dx%d @ %dfps\n",
             width, height, frame_rate);
    return 0;
}

12.3.3 视频流启动

/**
 * @brief 启动视频流。
 * @param uvc UVC 设备指针
 * @return 0 成功,负数错误
 */
static int uvc_start_streaming(struct uvc_device *uvc)
{
    struct uvc_streaming *streaming = uvc->streaming;
    struct urb *urb;
    int i, ret;
​
    // 1. 检查是否已启动
    if (uvc->streaming_state) {
        dev_dbg(&uvc->udev->dev, "Streaming already started\n");
        return 0;
    }
​
    // 2. 分配 URB 数组
    streaming->urb = kcalloc(streaming->nb_isoc_packets,
                             sizeof(struct urb *), GFP_KERNEL);
    if (!streaming->urb) return -ENOMEM;
​
    // 3. 分配并初始化 URB
    for (i = 0; i < streaming->nb_isoc_packets; i++) {
        urb = usb_alloc_urb(streaming->nb_isoc_packets, GFP_KERNEL);
        if (!urb) {
            ret = -ENOMEM;
            goto error_urb;
        }
​
        streaming->urb[i] = urb;
        usb_fill_isoc_urb(urb, uvc->udev,
                          usb_rcvisocpipe(uvc->udev, streaming->endpoint),
                          NULL, streaming->max_packet_size,
                          uvc_iso_irq, uvc, streaming->bInterval);
    }
​
    // 4. 提交 URB
    for (i = 0; i < streaming->nb_isoc_packets; i++) {
        ret = usb_submit_urb(streaming->urb[i], GFP_KERNEL);
        if (ret < 0) {
            dev_err(&uvc->udev->dev, "Failed to submit URB %d\n", i);
            goto error_submit;
        }
    }
​
    uvc->streaming_state = 1;
    dev_info(&uvc->udev->dev, "Video streaming started\n");
    return 0;
​
error_submit:
    for (i = 0; i < streaming->nb_isoc_packets; i++) {
        if (streaming->urb[i]) {
            usb_kill_urb(streaming->urb[i]);
            usb_free_urb(streaming->urb[i]);
        }
    }
error_urb:
    kfree(streaming->urb);
    return ret;
}

12.3.4 视频帧处理

/**
 * @brief 等时传输 URB 完成回调。
 * @param urb 完成 的 URB 指针
 */
static void uvc_iso_irq(struct urb *urb)
{
    struct uvc_device *uvc = urb->context;
    struct uvc_streaming *streaming = uvc->streaming;
    u8 *data = urb->transfer_buffer;
    int i;
​
    // 1. 检查传输状态
    if (urb->status) {
        if (urb->status == -ENODEV || urb->status == -ESHUTDOWN) {
            return;
        }
        dev_err(&uvc->udev->dev, "URB error: %d\n", urb->status);
        return;
    }
​
    // 2. 处理每个包
    for (i = 0; i < urb->number_of_packets; i++) {
        struct usb_iso_packet_descriptor *desc = &urb->iso_frame_desc[i];
        u8 *frame_data = data + desc->offset;
        int length = desc->actual_length;
        int status = desc->status;
​
        // 3. 检查包状态
        if (status) {
            streaming->packets_error++;
            continue;
        }
​
        if (length < 0) {
            streaming->packets_error++;
            continue;
        }
​
        // 4. 处理视频数据
        uvc_process_video_data(uvc, frame_data, length);
    }
​
    // 5. 重新提交 URB
    usb_submit_urb(urb, GFP_ATOMIC);
}
​
/**
 * @brief 处理视频数据。
 * @param uvc UVC 设备指针
 * @param data 视频数据
 * @param length 数据长度
 */
static void uvc_process_video_data(struct uvc_device *uvc,
                                   u8 *data, int length)
{
    struct uvc_streaming *streaming = uvc->streaming;
    struct uvc_buffer *buf;
    int header_len = 0;
​
    // 1. 解析数据包头
    if (length < 2) {
        streaming->packets_dropped++;
        return;
    }
​
    // 2. 确定头部长度和帧信息
    if (data[0] & 0x80) {
        header_len = data[1] & 0x7F;
        if (length < header_len) {
            streaming->packets_dropped++;
            return;
        }
    }
​
    // 3. 检查是否为新帧的开始
    if (data[0] & 0x40) {
        // 新帧开始,提交当前缓冲区
        if (uvc->buffer) {
            uvc_buffer_done(uvc->buffer);
            uvc->buffer = NULL;
        }
    }
​
    // 4. 获取空闲缓冲区
    if (!uvc->buffer) {
        spin_lock(&uvc->lock);
        if (!list_empty(&uvc->free_buffers)) {
            uvc->buffer = list_first_entry(&uvc->free_buffers,
                                           struct uvc_buffer, list);
            list_del(&uvc->buffer->list);
        }
        spin_unlock(&uvc->lock);
    }
​
    // 5. 将数据复制到缓冲区
    if (uvc->buffer) {
        u8 *dst = uvc->buffer->data + uvc->buffer->filled;
        int copy_len = min(length - header_len,
                           uvc->buffer->length - uvc->buffer->filled);
        memcpy(dst, data + header_len, copy_len);
        uvc->buffer->filled += copy_len;
​
        // 6. 检查是否为一帧的结束
        if (data[0] & 0x20) {
            uvc_buffer_done(uvc->buffer);
            uvc->buffer = NULL;
        }
    }
​
    streaming->packets_processed++;
}

12.3.5 VB2 缓冲区管理

/**
 * @brief VB2 缓冲区操作结构。
 */
static const struct vb2_ops uvc_vb2_ops = {
    .queue_setup = uvc_queue_setup,
    .buf_prepare = uvc_buf_prepare,
    .buf_queue = uvc_buf_queue,
    .start_streaming = uvc_start_streaming,
    .stop_streaming = uvc_stop_streaming,
    .wait_prepare = vb2_ops_wait_prepare,
    .wait_finish = vb2_ops_wait_finish,
};
​
/**
 * @brief 处理缓冲区完成。
 * @param buf VB2 缓冲区指针
 */
static void uvc_buffer_done(struct uvc_buffer *buf)
{
    struct uvc_device *uvc = buf->uvc;
​
    // 1. 设置缓冲区状态
    buf->vbuf.sequence = uvc->sequence++;
    buf->vbuf.timestamp = ktime_get_ns();
    buf->vbuf.field = V4L2_FIELD_NONE;
​
    // 2. 标记缓冲区完成
    vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_DONE);
​
    // 3. 唤醒等待进程
    wake_up(&uvc->wait);
}

12.4 软件设计模式树形分析

UVC 与 V4L2 集成设计模式
├── 工厂模式 (Factory Pattern)
│   ├── video_device_alloc():创建 V4L2 视频设备
│   └── kcalloc():创建 URB 数组
├── 适配器模式 (Adapter Pattern)
│   ├── uvc_process_video_data():将 USB 数据适配为 V4L2 帧
│   └── uvc_vb2_ops:适配 VB2 接口
├── 策略模式 (Strategy Pattern)
│   ├── uvc_select_format():选择视频格式策略
│   └── uvc_select_frame():选择帧率策略
├── 观察者模式 (Observer Pattern)
│   ├── uvc_iso_irq():观察 URB 完成事件
│   └── vb2_buffer_done():观察缓冲区完成事件
├── 装饰器模式 (Decorator Pattern)
│   └── 为视频帧添加时间戳和序列号
└── 模板方法模式 (Template Method Pattern)
    └── uvc_process_video_data():定义了视频数据处理的标淮流程

12.5 UVC 与 V4L2 调试核心难点

12.5.1 视频格式协商失败

现象:摄像头无法设置指定分辨率或帧率。

原因

  1. 设备不支持请求的格式。

  2. 格式协商接口未正确实现。

  3. 带宽不足。

解决方法

  1. 使用 v4l2-ctl --list-formats-ext 查看支持格式。

  2. 检查格式协商代码。

  3. 降低分辨率或帧率。

12.5.2 视频帧丢失

现象:视频预览卡顿,dmesg 显示丢包。

原因

  1. USB 带宽不足。

  2. 缓冲区队列设置不当。

  3. 等时传输包损坏。

解决方法

  1. 增加缓冲区数量。

  2. 调整等时传输参数。

  3. 使用批量传输替代等时传输。

12.5.3 视频流无法启动

现象VIDIOC_STREAMON 返回 -EIO

原因

  1. 设备未正确配置。

  2. URB 提交失败。

  3. 带宽不足。

解决方法

  1. 检查设备配置。

  2. 检查 URB 提交状态。

  3. 降低分辨率或帧率。

12.6 与其他模块的协同

模块 协同方式 调试关键点
USB 核心层 提供 URB 传输和端点配置 等时传输、批量传输
V4L2 核心层 提供视频设备注册和缓冲区管理 格式协商、缓冲区队列
VB2 框架 提供视频缓冲区管理 缓冲区分配、准备
用户空间 通过 V4L2 接口获取视频帧 格式协商、缓冲区状态
sysfs 查看和配置视频参数 设备信息、调试开关
DMA 控制器 提供视频数据传输 缓冲区地址、传输完成
电源管理 设备空闲时自动挂起 唤醒能力、自动挂起

第十三部分 USB 与 ALSA 子系统的集成

13.1 USB 音频设备与 ALSA 的关系

USB 音频设备类(UAC)是 USB 设备类中最常见的类型之一,包括 USB 耳机、麦克风、音箱、音频接口等。USB 音频驱动将 USB 等时传输的音频数据转换为标准的 ALSA PCM 流,通过 ALSA 子系统上报给用户空间。

13.1.1 系统架构

[USB 音频设备] ↔ [USB 核心层] ↔ [snd-usb-audio 驱动] ↔ [ALSA 核心层] ↔ [用户空间]
                                                              ↓
                                                      [PCM 设备节点]
                                                              ↓
                                                      [应用程序]

13.1.2 数据流

[硬件中断] → [URB 完成] → [音频数据解析] → [ALSA PCM 缓冲区] → [用户空间]
                     ↓                                 ↓
               [原始音频数据]               [snd_pcm 结构]

13.2 核心数据结构

13.2.1 USB 音频设备结构

/**
 * @struct snd_usb_audio
 * @brief USB 音频设备核心结构。
 */
struct snd_usb_audio {
    struct snd_card *card;          /**< ALSA 声卡核心 */
    struct usb_device *dev;         /**< USB 设备 */
    struct usb_interface *intf;     /**< USB 接口 */
    struct list_head stream_list;   /**< 音频流列表 */
    struct list_head midi_list;     /**< MIDI 列表 */
    struct list_head pcm_list;      /**< PCM 列表 */
    int samplerate;                 /**< 当前采样率 */
    int channels;                   /**< 当前通道数 */
    int format;                     /**< 音频格式 (PCM/DSD) */
    int bits_per_sample;            /**< 采样位深 */
    int buffer_size;                /**< 缓冲区大小 */
    int period_size;                /**< 周期大小 */
    int periods;                    /**< 周期数量 */
    int rate;                       /**< 采样率 */
    int frame_size;                 /**< 帧大小 */
    struct snd_usb_endpoint *ep;    /**< 端点列表 */
    struct snd_usb_substream *substream; /**< 子流 */
    struct snd_usb_pcm *pcm;        /**< PCM 设备 */
    struct snd_usb_midi *midi;      /**< MIDI 设备 */
    spinlock_t lock;                /**< 自旋锁 */
    struct work_struct work;        /**< 工作队列 */
    int autopm:1;                   /**< 自动电源管理 */
    int auto_suspend:1;             /**< 自动挂起 */
    int is_audio:1;                 /**< 是否音频设备 */
    int is_midi:1;                  /**< 是否 MIDI 设备 */
    int is_control:1;               /**< 是否控制设备 */
};
​
/**
 * @struct snd_usb_substream
 * @brief USB 音频子流结构。
 */
struct snd_usb_substream {
    struct snd_usb_audio *chip;     /**< 所属芯片 */
    struct snd_pcm_substream *substream; /**< ALSA 子流 */
    struct snd_usb_endpoint *ep;    /**< 关联端点 */
    struct urb **urb;               /**< URB 数组 */
    int stream_id;                  /**< 流 ID */
    int direction;                  /**< 方向 (播放/录音) */
    int endpoint;                   /**< 端点地址 */
    int bInterval;                  /**< 中断间隔 */
    int max_packet_size;            /**< 最大包大小 */
    int packet_size;                /**< 包大小 */
    int period_frames;              /**< 周期帧数 */
    int period_bytes;               /**< 周期字节数 */
    int buffer_frames;              /**< 缓冲区帧数 */
    int buffer_bytes;               /**< 缓冲区字节数 */
    int sample_rate;                /**< 采样率 */
    int channels;                   /**< 通道数 */
    int format;                     /**< 格式 */
    int bits_per_sample;            /**< 采样位深 */
    int frame_size;                 /**< 帧大小 */
    int packets_dropped;            /**< 丢包计数 */
    int packets_processed;          /**< 已处理包计数 */
    int packets_error;              /**< 错误包计数 */
    dma_addr_t dma_addr;            /**< DMA 地址 */
    void *dma_area;                 /**< DMA 区域 */
    size_t dma_bytes;               /**< DMA 字节数 */
    struct list_head list;          /**< 链表节点 */
    spinlock_t lock;                /**< 自旋锁 */
};

13.3 核心代码实现

13.3.1 USB 音频设备初始化

#include <linux/module.h>
#include <linux/usb.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
​
/**
 * @brief USB 音频设备 probe 函数。
 * @param intf USB 接口指针
 * @param id 匹配的 USB 设备 ID
 * @return 0 成功,负数错误
 */
static int snd_usb_audio_probe(struct usb_interface *intf,
                               const struct usb_device_id *id)
{
    struct usb_device *dev = interface_to_usbdev(intf);
    struct snd_usb_audio *chip;
    struct snd_card *card;
    int ret;
​
    // 1. 分配声卡结构
    ret = snd_card_new(&intf->dev, -1, "USB Audio", THIS_MODULE,
                       sizeof(struct snd_usb_audio), &card);
    if (ret < 0) {
        dev_err(&intf->dev, "Failed to create sound card\n");
        return ret;
    }
​
    // 2. 初始化芯片数据
    chip = card->private_data;
    chip->card = card;
    chip->dev = dev;
    chip->intf = intf;
    spin_lock_init(&chip->lock);
    INIT_LIST_HEAD(&chip->stream_list);
    INIT_LIST_HEAD(&chip->midi_list);
    INIT_LIST_HEAD(&chip->pcm_list);
​
    // 3. 解析音频控制接口
    ret = snd_usb_parse_audio_control(chip);
    if (ret < 0) {
        dev_err(&intf->dev, "Failed to parse audio control\n");
        goto error;
    }
​
    // 4. 解析音频流接口
    ret = snd_usb_parse_audio_streaming(chip);
    if (ret < 0) {
        dev_err(&intf->dev, "Failed to parse audio streaming\n");
        goto error;
    }
​
    // 5. 解析 MIDI 接口
    ret = snd_usb_parse_midi(chip);
    if (ret < 0) {
        dev_err(&intf->dev, "Failed to parse MIDI\n");
        goto error;
    }
​
    // 6. 注册声卡
    ret = snd_card_register(card);
    if (ret < 0) {
        dev_err(&intf->dev, "Failed to register sound card\n");
        goto error;
    }
​
    // 7. 保存设备数据
    usb_set_intfdata(intf, chip);
​
    dev_info(&intf->dev, "USB audio device registered\n");
    return 0;
​
error:
    snd_card_free(card);
    return ret;
}

13.3.2 PCM 设备注册

/**
 * @brief 注册 PCM 设备。
 * @param chip USB 音频芯片指针
 * @return 0 成功,负数错误
 */
static int snd_usb_register_pcm(struct snd_usb_audio *chip)
{
    struct snd_pcm *pcm;
    int ret;
​
    // 1. 分配 PCM 设备
    ret = snd_pcm_new(chip->card, "USB Audio", 0, 1, 1, &pcm);
    if (ret < 0) {
        dev_err(&chip->dev->dev, "Failed to create PCM\n");
        return ret;
    }
​
    // 2. 设置 PCM 操作
    pcm->private_data = chip;
    pcm->ops = &snd_usb_pcm_ops;
    pcm->no_device = 0;
    pcm->dev = &chip->dev->dev;
    pcm->info_flags = 0;
​
    // 3. 设置播放和录音功能
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usb_pcm_ops);
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_pcm_ops);
​
    // 4. 分配 DMA 缓冲区
    ret = snd_pcm_lib_preallocate_pages(pcm, SNDRV_PCM_STREAM_PLAYBACK,
                                        SNDRV_DMA_TYPE_DEV, &chip->dev->dev,
                                        64 * 1024, 64 * 1024);
    if (ret < 0) {
        dev_err(&chip->dev->dev, "Failed to preallocate playback buffer\n");
        return ret;
    }
​
    ret = snd_pcm_lib_preallocate_pages(pcm, SNDRV_PCM_STREAM_CAPTURE,
                                        SNDRV_DMA_TYPE_DEV, &chip->dev->dev,
                                        64 * 1024, 64 * 1024);
    if (ret < 0) {
        dev_err(&chip->dev->dev, "Failed to preallocate capture buffer\n");
        return ret;
    }
​
    // 5. 保存 PCM 设备
    chip->pcm = pcm;
​
    dev_info(&chip->dev->dev, "PCM device registered\n");
    return 0;
}

13.3.3 音频流启动

/**
 * @brief 启动音频流。
 * @param subs USB 音频子流指针
 * @return 0 成功,负数错误
 */
static int snd_usb_start_stream(struct snd_usb_substream *subs)
{
    struct snd_usb_audio *chip = subs->chip;
    struct urb *urb;
    int i, ret;
​
    // 1. 检查是否已启动
    if (subs->ep->state == EP_STATE_RUNNING) {
        return 0;
    }
​
    // 2. 分配 URB 数组
    subs->urb = kcalloc(subs->ep->packets_per_period, sizeof(struct urb *), GFP_KERNEL);
    if (!subs->urb) return -ENOMEM;
​
    // 3. 分配并初始化 URB
    for (i = 0; i < subs->ep->packets_per_period; i++) {
        urb = usb_alloc_urb(subs->ep->packets_per_period, GFP_KERNEL);
        if (!urb) {
            ret = -ENOMEM;
            goto error_urb;
        }
​
        subs->urb[i] = urb;
        if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
            usb_fill_isoc_urb(urb, chip->dev,
                              usb_sndisocpipe(chip->dev, subs->endpoint),
                              subs->dma_area + i * subs->packet_size,
                              subs->packet_size,
                              snd_usb_playback_irq, subs,
                              subs->bInterval);
        } else {
            usb_fill_isoc_urb(urb, chip->dev,
                              usb_rcvisocpipe(chip->dev, subs->endpoint),
                              subs->dma_area + i * subs->packet_size,
                              subs->packet_size,
                              snd_usb_capture_irq, subs,
                              subs->bInterval);
        }
    }
​
    // 4. 提交 URB
    for (i = 0; i < subs->ep->packets_per_period; i++) {
        ret = usb_submit_urb(subs->urb[i], GFP_KERNEL);
        if (ret < 0) {
            dev_err(&chip->dev->dev, "Failed to submit URB %d\n", i);
            goto error_submit;
        }
    }
​
    subs->ep->state = EP_STATE_RUNNING;
    return 0;
​
error_submit:
    for (i = 0; i < subs->ep->packets_per_period; i++) {
        if (subs->urb[i]) {
            usb_kill_urb(subs->urb[i]);
            usb_free_urb(subs->urb[i]);
        }
    }
error_urb:
    kfree(subs->urb);
    return ret;
}

13.3.4 音频播放完成回调

/**
 * @brief 音频播放 URB 完成回调。
 * @param urb 完成的 URB 指针
 */
static void snd_usb_playback_irq(struct urb *urb)
{
    struct snd_usb_substream *subs = urb->context;
    struct snd_usb_audio *chip = subs->chip;
    struct snd_pcm_substream *substream = subs->substream;
    struct snd_pcm_runtime *runtime = substream->runtime;
    int i;
​
    // 1. 检查传输状态
    if (urb->status) {
        if (urb->status == -ENODEV || urb->status == -ESHUTDOWN) {
            return;
        }
        dev_err(&chip->dev->dev, "URB error: %d\n", urb->status);
        return;
    }
​
    // 2. 更新 DMA 缓冲区索引
    subs->current_index = (subs->current_index + 1) % subs->ep->packets_per_period;
​
    // 3. 填充音频数据
    for (i = 0; i < urb->number_of_packets; i++) {
        struct usb_iso_packet_descriptor *desc = &urb->iso_frame_desc[i];
        int length = desc->actual_length;
        int offset = desc->offset;
​
        if (length > 0) {
            // 将数据从 DMA 缓冲区复制到 ALSA 缓冲区
            snd_pcm_sync(substream, runtime, subs->dma_area + offset, length);
        }
    }
​
    // 4. 通知 ALSA 周期完成
    if (subs->period_frames > 0) {
        snd_pcm_period_elapsed(substream);
    }
​
    // 5. 重新提交 URB
    usb_submit_urb(urb, GFP_ATOMIC);
}

13.3.5 音频录完成回调

/**
 * @brief 音频录音 URB 完成回调。
 * @param urb 完成的 URB 指针
 */
static void snd_usb_capture_irq(struct urb *urb)
{
    struct snd_usb_substream *subs = urb->context;
    struct snd_usb_audio *chip = subs->chip;
    struct snd_pcm_substream *substream = subs->substream;
    struct snd_pcm_runtime *runtime = substream->runtime;
    int i;
​
    // 1. 检查传输状态
    if (urb->status) {
        if (urb->status == -ENODEV || urb->status == -ESHUTDOWN) {
            return;
        }
        dev_err(&chip->dev->dev, "URB error: %d\n", urb->status);
        return;
    }
​
    // 2. 处理每个包
    for (i = 0; i < urb->number_of_packets; i++) {
        struct usb_iso_packet_descriptor *desc = &urb->iso_frame_desc[i];
        int length = desc->actual_length;
        int offset = desc->offset;
​
        if (length > 0) {
            // 将数据从 DMA 缓冲区复制到 ALSA 缓冲区
            snd_pcm_sync(substream, runtime, subs->dma_area + offset, length);
        }
    }
​
    // 3. 更新 DMA 缓冲区索引
    subs->current_index = (subs->current_index + 1) % subs->ep->packets_per_period;
​
    // 4. 通知 ALSA 周期完成
    if (subs->period_frames > 0) {
        snd_pcm_period_elapsed(substream);
    }
​
    // 5. 重新提交 URB
    usb_submit_urb(urb, GFP_ATOMIC);
}

13.3.6 PCM 硬件参数配置

/**
 * @brief PCM 硬件参数配置。
 * @param substream ALSA 子流
 * @param params 硬件参数
 * @return 0 成功,负数错误
 */
static int snd_usb_pcm_hw_params(struct snd_pcm_substream *substream,
                                  struct snd_pcm_hw_params *params)
{
    struct snd_usb_substream *subs = substream->private_data;
    int ret;
​
    // 1. 获取参数
    subs->sample_rate = params_rate(params);
    subs->channels = params_channels(params);
    subs->format = params_format(params);
    subs->bits_per_sample = snd_pcm_format_width(subs->format);
​
    // 2. 计算帧大小
    subs->frame_size = (subs->bits_per_sample * subs->channels) / 8;
    subs->period_frames = params_period_size(params);
    subs->buffer_frames = params_buffer_size(params);
    subs->period_bytes = subs->period_frames * subs->frame_size;
    subs->buffer_bytes = subs->buffer_frames * subs->frame_size;
​
    // 3. 分配 DMA 缓冲区
    ret = snd_pcm_lib_malloc_pages(substream, subs->buffer_bytes);
    if (ret < 0) {
        dev_err(&subs->chip->dev->dev, "Failed to allocate DMA buffer\n");
        return ret;
    }
​
    subs->dma_area = substream->runtime->dma_area;
    subs->dma_addr = substream->runtime->dma_addr;
    subs->dma_bytes = subs->buffer_bytes;
​
    // 4. 配置端点
    ret = snd_usb_set_interface(subs, subs->ep);
    if (ret < 0) {
        dev_err(&subs->chip->dev->dev, "Failed to set interface\n");
        return ret;
    }
​
    return 0;
}

13.4 软件设计模式树形分析

USB 音频与 ALSA 集成设计模式
├── 工厂模式 (Factory Pattern)
│   ├── snd_card_new():创建 ALSA 声卡
│   └── snd_pcm_new():创建 PCM 设备
├── 适配器模式 (Adapter Pattern)
│   ├── snd_usb_playback_irq():将 USB 数据适配为 ALSA PCM
│   └── snd_usb_pcm_hw_params():适配 ALSA 参数到 USB
├── 策略模式 (Strategy Pattern)
│   ├── snd_usb_pcm_ops:ALSA PCM 操作策略
│   └── snd_usb_set_interface():设置接口策略
├── 观察者模式 (Observer Pattern)
│   ├── snd_usb_playback_irq():观察 URB 完成事件
│   └── snd_pcm_period_elapsed():观察周期完成事件
├── 装饰器模式 (Decorator Pattern)
│   └── 为音频数据添加时间戳和序列号
└── 模板方法模式 (Template Method Pattern)
    └── snd_usb_audio_probe():定义了 USB 音频设备初始化的标准流程

13.5 资深视角:USB 音频调试核心难点

13.5.1 音频采样率不支持

现象aplayarecord 报错 -EINVAL,无法设置采样率。

原因

  1. 设备不支持指定的采样率。

  2. 采样率转换失败。

  3. 带宽不足。

解决方法

  1. 使用 cat /proc/asound/card0/stream0 查看支持采样率。

  2. 尝试使用 plughw 代替 hw 进行重采样。

  3. 降低采样率。

13.5.2 音频播放爆音

现象:播放音频时出现爆音或卡顿。

原因

  1. USB 带宽不足。

  2. 缓冲区设置不当。

  3. 等时传输不稳定。

解决方法

  1. 增加缓冲区大小。

  2. 调整等时传输参数。

  3. 降低采样率。

13.5.3 录音无声音

现象:录制音频,但得到的文件无声。

原因

  1. 麦克风未启用。

  2. 增益设置过低。

  3. URB 传输错误。

解决方法

  1. 使用 alsamixer 检查麦克风设置。

  2. 增加麦克风增益。

  3. 检查 URB 传输状态。

13.5.4 音频流无法启动

现象snd_pcm_hw_params 成功后,snd_pcm_prepare 返回 -EIO

原因

  1. 设备未正确配置。

  2. URB 提交失败。

  3. 接口设置错误。

解决方法

  1. 检查设备配置。

  2. 检查 URB 提交状态。

  3. 检查接口设置。

13.6 与其他模块的协同

模块 协同方式 调试关键点
USB 核心层 提供 URB 传输和端点配置 等时传输、批量传输
ALSA 核心层 提供音频设备注册和 PCM 管理 采样率、缓冲区管理
用户空间 通过 ALSA 接口播放/录制音频 采样率、通道数
sysfs 查看和配置音频参数 设备信息、调试开关
DMA 控制器 提供音频数据传输 缓冲区地址、传输完成
电源管理 设备空闲时自动挂起 唤醒能力、自动挂起
中断控制器 处理音频中断 中断号、优先级
用户空间工具 aplay/arecord 测试音频功能 设备节点、参数配置

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐