03-USB驱动开发.md 28 KB


title: USB驱动开发 tags: [Linux驱动, USB, 嵌入式, I.MX6ULL] created: 2026-09-17 updated: 2026-09-17

pdf_ref: "【正点原子】I.MX6U嵌入式Linux驱动开发指南V2.0.1 - 第六十七章 Linux USB驱动实验"

USB驱动开发

关联知识:[[03-USB驱动基础]] | [[计算机网络/USB协议]] | [[STM32学习笔记/15-WiFi通信与ESP32-C3]]


一、USB协议概述

1.1 USB总线原理

USB(Universal Serial Bus,通用串行总线)是一种主从式总线架构。主机(Host)发起所有通信,从机(Device)被动响应。总线上通过 HUB 扩展,形成树状拓扑,最多支持 5 级 HUB、127 个设备。

USB 版本演进:

版本 速率 等效编码速率 典型应用
USB 1.1 12 Mbps 1.5 MB/s 键盘鼠标
USB 2.0 480 Mbps 60 MB/s U盘、摄像头
USB 3.0 5 Gbps 500 MB/s 高速存储
USB 3.1 10 Gbps 1.25 GB/s 外置显卡

1.2 USB设备架构

USB 设备采用分层描述模型,从顶层到底层依次为:

graph TD
    accTitle: USB设备分层架构
    accDescr: 从设备到端点的层级关系,设备可包含多个配置,配置包含多个接口,接口包含多个端点。

    A[USB设备<br/>1个设备描述符] --> B[配置1<br/>配置描述符]
    A --> C[配置2<br/>配置描述符]
    B --> D[接口0<br/>接口描述符]
    B --> E[接口1<br/>接口描述符]
    D --> F[端点0<br/>控制端点]
    D --> G[端点1<br/>IN批量端点]
    D --> H[端点2<br/>OUT中断端点]
    E --> I[端点3<br/>IN等时端点]

设备(Device):一个物理 USB 设备,拥有唯一的设备描述符,包含 VID(厂商 ID)和 PID(产品 ID)。

配置(Configuration):设备的一种工作模式。大多数设备只有一个配置,但 OTG 设备可能有多个。配置描述符定义了接口数量和供电参数。

接口(Interface):一个功能单元。比如一个复合设备可以同时提供鼠标接口和键盘接口。接口描述符记录接口编号、端点数量、所属的设备类。

端点(Endpoint):数据传输的实际通道。端点 0 是默认控制端点(双向),其余端点都是单向的。端点描述符定义了传输类型、方向、最大包长度。

五类描述符的类型值:

描述符类型 类型值 说明
Device Descriptor 1 设备基本属性
Configuration Descriptor 2 配置信息
String Descriptor 3 可读字符串
Interface Descriptor 4 接口功能
Endpoint Descriptor 5 端点参数

1.3 USB传输类型

USB 协议定义四种传输类型,由端点描述符的 bmAttributes 字段的 bit1:0 指定:

传输类型 位编码 方向 特点 典型应用
控制传输 00 双向 可靠,有重试机制 枚举、配置设备
同步传输 01 单向 实时,无握手,允许丢数据 音频、视频
批量传输 10 单向 可靠,大数据量,无时间保证 U盘、打印机
中断传输 11 单向 周期性,低延迟 键盘、鼠标、触摸屏

控制传输三阶段:建立阶段(SETUP令牌包)→ 数据阶段(DATA0/DATA1交替)→ 状态阶段(方向与数据阶段相反)。

批量传输过程(以写为例):

  1. 主机发出 OUT 令牌包,包含设备地址和端点号
  2. 主机发送 DATA 数据包
  3. 设备正确接收后返回 ACK 握手包

1.4 USB数据包结构

USB 数据包由同步域(SYNC)、包标识符(PID)、数据域、CRC 校验和包结束(EOP)组成。PID 为 8 位,低 4 位为有效值,高 4 位为低 4 位的取反,传输顺序为 PID0→PID7。

主要包类型:

PID类型 PID名字 PID<3:0> 用途
令牌包 OUT 0001 主机通知要输出数据
令牌包 IN 1001 主机通知要输入数据
令牌包 SETUP 1101 开始控制传输
数据包 DATA0 0011 偶数据包
数据包 DATA1 1011 奇数据包
握手包 ACK 0010 确认,传输正确
握手包 NAK 1010 不确认,设备忙
握手包 STALL 1110 端点挂起

1.5 USB枚举过程

sequenceDiagram
    accTitle: USB枚举流程
    accDescr: 主机检测到USB设备插入后的枚举过程,包括复位、获取描述符、设置地址。

    participant H as 主机
    participant D as 设备

    Note over H: 检测到设备插入
    H->>D: 总线复位信号
    Note over D: 复位完成,地址为0

    rect rgb(230, 245, 255)
    Note over H,D: 第一回合:获取设备描述符
    H->>D: SETUP令牌包 + DATA0(获取设备描述符请求)
    D->>H: DATA1(设备描述符,18字节)
    H->>H: 解析VID/PID
    H->>D: ACK
    end

    rect rgb(255, 245, 230)
    Note over H,D: 第二回合:设置地址
    H->>D: 总线复位
    H->>D: SETUP令牌包 + DATA0(设置地址请求)
    Note over D: 设备进入状态过程
    D->>H: 0字节状态数据包
    H->>D: ACK
    Note over D: 使用新地址通信
    end

    rect rgb(230, 255, 230)
    Note over H,D: 第三回合:获取完整描述符
    H->>D: 获取完整设备描述符(18字节)
    D->>H: 返回设备描述符
    H->>D: 获取配置描述符、字符串描述符等
    end

二、Linux USB架构

2.1 USB子系统架构

graph TB
    accTitle: Linux USB子系统架构
    accDescr: 从用户空间应用到硬件控制器的完整USB栈,分为用户空间、USB核心层和主机控制器驱动层。

    subgraph 用户空间
        APP[应用程序<br/>read/write/ioctl]
    end

    subgraph 内核空间
        UDEV[udev设备管理器<br/>/dev节点]
        UD[USB设备驱动<br/>usb_driver]
        UC[USB核心层<br/>usb_register]
        URB[URB请求块<br/>usb_submit_urb]
        UHCD[USB主机控制器驱动<br/>HCD]
    end

    subgraph 硬件
        HC[USB控制器<br/>EHCI/OHCI/xHCI]
        BUS[USB总线]
        DEV[USB设备]
    end

    APP -->|系统调用| UDEV
    UDEV --> UD
    UD -->|构建URB| URB
    URB --> UC
    UC --> UHCD
    UHCD --> HC
    HC --> BUS
    BUS --> DEV

Linux USB 子系统分为三层:

  • USB设备驱动层:匹配特定 VID/PID 的 USB 设备,处理应用逻辑
  • USB核心层:提供统一的 API 抽象,管理 URB 生命周期,维护 USB 设备拓扑
  • USB主机控制器驱动层:直接操作硬件寄存器,将 URB 转化为总线事务

2.2 主机控制器驱动(HCD)

Linux 内核支持三种 HCD 实现:

HCD 全称 适用场景
EHCI Enhanced Host Controller Interface USB 2.0 高速设备
OHCI Open Host Controller Interface USB 1.1 低速/全速设备
xHCI eXtensible Host Controller Interface USB 3.0+ 设备

I.MX6ULL 使用 EHCI 控制器(USB 2.0),同时集成 OTG 控制器支持主机/从机切换。

2.3 核心数据结构

usb_device — 表示一个 USB 设备实体:

struct usb_device {
    int devnum;                    // 设备地址(1-127)
    char devpath[16];              // 设备路径
    u16 idVendor;                  // 厂商ID
    u16 idProduct;                 // 产品ID
    u16 bcdDevice;                 // 设备版本号
    u8  bDeviceClass;              // 设备类
    u8  bDeviceSubClass;           // 设备子类
    struct usb_device_descriptor descriptor; // 设备描述符
    struct usb_host_config *config; // 当前配置
    struct usb_device *parent;     // 父设备(HUB)
    struct device dev;             // 内核设备模型
};

usb_interface — 表示一个 USB 接口:

struct usb_interface {
    struct usb_host_interface *altsetting; // 备用设置数组
    struct usb_host_interface *cur_altsetting; // 当前设置
    unsigned num_altsetting;        // 备用设置数量
    struct usb_device *usb_dev;     // 关联的usb_device
    int minor;                      // 设备号
    struct device dev;              // 内核设备模型
};

usb_driver — USB 设备驱动结构体:

struct usb_driver {
    const char *name;               // 驱动名称
    int (*probe)(struct usb_interface *interface,
                const struct usb_device_id *id); // 设备匹配回调
    void (*disconnect)(struct usb_interface *interface); // 断开回调
    const struct usb_device_id *id_table; // 匹配表
    struct module *owner;
};

2.4 URB(USB Request Block)

URB 是 Linux USB 子系统的核心数据结构,所有 USB 数据传输都通过 URB 完成。可以理解为 USB 总线上的「快递单」。

struct urb {
    struct kref kref;               // 引用计数
    struct list_head urb_list;      // 链入 HCD 队列
    struct usb_device *dev;         // 目标设备
    unsigned int pipe;              // 管道号(端点+方向)
    int status;                     // 完成状态
    unsigned int transfer_flags;    // 传输标志
    void *transfer_buffer;          // 传输缓冲区(DMA安全)
    int transfer_buffer_length;     // 缓冲区长度
    int actual_length;              // 实际传输字节数
    int number_of_packets;          // 等时传输包数量
    struct usb_iso_packet_descriptor iso_frame_desc[0]; // 等时描述符
};

管道(pipe)编码方式:

bit[31]    = 方向:0=OUT, 1=IN
bit[30:24] = 端点号
bit[23:16] = 设备地址
bit[15:0]  = 保留

宏定义:
usb_rcvctrlpipe(dev, endpoint)  — 控制IN
usb_sndctrlpipe(dev, endpoint)  — 控制OUT
usb_rcvbulkpipe(dev, endpoint)  — 批量IN
usb_sndbulkpipe(dev, endpoint)  — 批量OUT

三、USB设备树配置

3.1 I.MX6ULL USB节点

I.MX6ULL 提供两个 USB 控制器:OTG1(支持主机/从机切换)和 OTG2(仅主机模式)。设备树配置示例:

&usbotg1 {
    dr_mode = "otg";            // 主机/从机切换
    srp-support;                // 支持 SRP
    hnp-support;                // 支持 HNP
    status = "okay";
};

&usbotg2 {
    dr_mode = "host";           // 仅主机模式
    disable-over-current;
    status = "okay";
};

3.2 OTG ID引脚配置

USB OTG 通过 ID 引脚判断主从模式:ID=低电平 → 主机模式,ID=高电平 → 从机模式。

&iomuxc {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_hog_1>;
    imx6ul-evk {
        pinctrl_hog_1: hoggrp-1 {
            fsl,pins = <
                MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058
            >;
        };
    };
};

0x13058 配置 GPIO1_IO00 为默认下拉,即默认工作在主机模式。如需从机模式,需要将电气属性改为上拉。

3.3 USB HUB扩展

I.MX6ULL-ALPHA 开发板通过 GL850G 或 SL2.1A HUB 芯片将 OTG2 扩展为 4 路 HOST 接口:

OTG2 → USB HUB芯片
        ├── HUB_DP1/DM1 → USB HOST接口1
        ├── HUB_DP2/DM2 → USB HOST接口2
        ├── HUB_DP3/DM3 → USB HOST接口3
        └── HUB_DP4/DM4 → 4G模块预留

V2.4 版本底板使用 Type-C 接口替代 Mini USB,通过 CC1/CC2 引脚实现传统 USB ID 功能。


四、USB驱动API

4.1 驱动注册与注销

#include <linux/usb.h>

/* 注册USB驱动 */
int usb_register(struct usb_driver *driver);

/* 注销USB驱动 */
void usb_deregister(struct usb_driver *driver);

/* 宏:自动注册/注销 */
module_usb_driver(my_usb_driver);

4.2 URB操作函数

函数 功能 说明
usb_alloc_urb(iso_packets, mem_flags) 分配URB iso_packets用于等时传输
usb_free_urb(urb) 释放URB 必须在URB未提交时调用
usb_fill_ctrl_urb(urb,dev,pipe,...) 填充控制URB 构建控制传输
usb_fill_bulk_urb(urb,dev,pipe,...) 填充批量URB 构建批量传输
usb_fill_int_urb(urb,dev,pipe,...) 填充中断URB 构建中断传输
usb_submit_urb(urb, mem_flags) 提交URB 异步,通过回调通知完成
usb_kill_urb(urb) 取消URB 等待回调完成
usb_unlink_urb(urb) 取消URB 不等待回调

批量 URB 填充示例:

usb_fill_bulk_urb(urb,
                  dev,
                  usb_rcvbulkpipe(dev, endpoint), // IN方向
                  buffer,
                  buffer_length,
                  my_complete_callback,           // 完成回调
                  context);                       // 用户上下文

4.3 同步传输便捷函数

/* 批量同步传输(内部自动分配/提交/等待URB) */
int usb_bulk_msg(struct usb_device *usb_dev,
                 unsigned int pipe,
                 void *data, int len,
                 int *actual_length,
                 int timeout);

/* 中断同步传输 */
int usb_int_msg(struct usb_device *usb_dev,
                unsigned int pipe,
                void *data, int len,
                int *actual_length,
                int timeout);

4.4 控制传输

/* 控制消息(最通用的控制传输接口) */
int usb_control_msg(struct usb_device *dev,
                    unsigned int pipe,
                    __u8 request,        // 请求类型
                    __u16 value,         // wValue
                    __u16 index,         // wIndex
                    void *data,          // 数据缓冲区
                    __u16 size,          // 数据长度
                    int timeout);        // 超时(ms)

/* 获取描述符的便捷宏 */
int usb_get_descriptor(struct usb_device *dev,
                       u8 type, u8 index,
                       void *buf, int size);

/* 设置配置 */
int usb_set_interface(struct usb_device *dev,
                      int interface,
                      int alternate);

4.5 USB设备ID匹配表

static const struct usb_device_id my_id_table[] = {
    // 精确匹配 VID:PID
    { USB_DEVICE(0x1234, 0x5678) },
    // 匹配设备类
    { USB_DEVICE_INFO(0x08, 0x06, 0x50) }, // Mass Storage类
    // 匹配接口类
    { USB_INTERFACE_INFO(0x03, 0x01, 0x02) }, // HID鼠标
    { /* 结束标记 */ }
};
MODULE_DEVICE_TABLE(usb, my_id_table);

五、完整源码分析

5.1 USB鼠标驱动实例

以下是基于 I.MX6ULL 开发板的 USB 鼠标驱动,展示完整的 USB 驱动框架:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/slab.h>

#define MOUSE_VID   0x046d  /* 罗技 */
#define MOUSE_PID   0xc52b  /* 无线鼠标 */

/* URB完成回调函数 */
static void mouse_urb_complete(struct urb *urb)
{
    unsigned char *data = urb->transfer_buffer;
    int ret;

    if (urb->status) {
        printk(KERN_ERR "URB status: %d\n", urb->status);
        return;
    }

    /* data[0]: 按键状态
     * data[1]: X轴偏移
     * data[2]: Y轴偏移
     * data[3]: 滚轮偏移(如果有) */
    printk(KERN_INFO "Mouse: btn=0x%x X=%d Y=%d\n",
           data[0], (signed char)data[1], (signed char)data[2]);

    /* 重新提交URB,继续接收中断数据 */
    ret = usb_submit_urb(urb, GFP_ATOMIC);
    if (ret)
        printk(KERN_ERR "Resubmit URB failed: %d\n", ret);
}

/* USB设备探测回调 */
static int mouse_probe(struct usb_interface *interface,
                       const struct usb_device_id *id)
{
    struct usb_device *udev = interface_to_usbdev(interface);
    struct usb_host_interface *iface_desc = interface->cur_altsetting;
    struct usb_endpoint_descriptor *endpoint;
    struct urb *urb = NULL;
    unsigned char *buf;
    int i, pipe, maxp;
    int ret = -ENOMEM;

    printk(KERN_INFO "Mouse connected: %04x:%04x\n",
           le16_to_cpu(udev->descriptor.idVendor),
           le16_to_cpu(udev->descriptor.idProduct));

    /* 遍历所有端点,找到中断IN端点 */
    for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
        endpoint = &iface_desc->endpoint[i].desc;
        if (usb_endpoint_is_int_in(endpoint)) {
            printk(KERN_INFO "Found interrupt IN endpoint: 0x%02x\n",
                   endpoint->bEndpointAddress);
            break;
        }
    }

    if (i >= iface_desc->desc.bNumEndpoints) {
        printk(KERN_ERR "No interrupt IN endpoint found\n");
        return -ENODEV;
    }

    /* 计算管道和最大包长度 */
    pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
    maxp = usb_maxpacket(udev, pipe);

    /* 分配URB */
    urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!urb)
        return -ENOMEM;

    /* 分配DMA缓冲区 */
    buf = kmalloc(maxp, GFP_KERNEL);
    if (!buf) {
        usb_free_urb(urb);
        return -ENOMEM;
    }

    /* 填充中断URB */
    usb_fill_int_urb(urb, udev, pipe, buf, maxp,
                     mouse_urb_complete,    /* 完成回调 */
                     NULL,                  /* 上下文 */
                     endpoint->bInterval); /* 轮询间隔 */

    /* 提交URB开始数据接收 */
    ret = usb_submit_urb(urb, GFP_KERNEL);
    if (ret) {
        printk(KERN_ERR "Submit URB failed: %d\n", ret);
        kfree(buf);
        usb_free_urb(urb);
        return ret;
    }

    /* 保存私有数据到接口 */
    usb_set_intfdata(interface, urb);

    printk(KERN_INFO "Mouse driver probed successfully\n");
    return 0;
}

/* USB设备断开回调 */
static void mouse_disconnect(struct usb_interface *interface)
{
    struct urb *urb = usb_get_intfdata(interface);

    printk(KERN_INFO "Mouse disconnected\n");

    usb_set_intfdata(interface, NULL);

    if (urb) {
        usb_kill_urb(urb);          /* 取消URB */
        kfree(urb->transfer_buffer); /* 释放缓冲区 */
        usb_free_urb(urb);          /* 释放URB */
    }
}

/* USB设备ID匹配表 */
static const struct usb_device_id mouse_id_table[] = {
    { USB_DEVICE(MOUSE_VID, MOUSE_PID) },
    { /* 结束标记 */ }
};
MODULE_DEVICE_TABLE(usb, mouse_id_table);

/* USB驱动结构体 */
static struct usb_driver mouse_driver = {
    .name       = "imx6ull_mouse",
    .probe      = mouse_probe,
    .disconnect = mouse_disconnect,
    .id_table   = mouse_id_table,
};

module_usb_driver(mouse_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Embedded Linux");
MODULE_DESCRIPTION("USB Mouse Driver for I.MX6ULL");

5.2 URB工作机制详解

sequenceDiagram
    accTitle: URB数据传输流程
    accDescr: USB设备驱动通过URB与USB核心层交互,最终由HCD完成总线传输的完整流程。

    participant 驱动 as USB设备驱动
    participant 核心 as USB核心层
    participant HCD as 主机控制器驱动
    participant HW as USB硬件

    驱动->>核心: usb_alloc_urb() 分配URB
    驱动->>核心: usb_fill_bulk_urb() 填充URB
    驱动->>核心: usb_submit_urb() 提交URB
    核心->>HCD: 将URB加入HCD队列
    HCD->>HW: 操作控制器寄存器
    HW-->>HCD: 硬件中断(传输完成)
    HCD-->>核心: URB完成回调
    核心-->>驱动: 调用urb->complete回调
    驱动->>驱动: 处理数据,重新提交URB

5.3 USB虚拟串口驱动(关键片段)

基于 Linux 内核 cdc_acm 驱动框架的 USB 串口设备识别:

/* cdc_acm匹配表:USB CDC ACM类设备 */
static const struct usb_device_id acm_ids[] = {
    { USB_DEVICE(0x0525, 0x0610) },  /* NetChip ACM */
    { USB_DEVICE(0x067b, 0x2303) },  /* Prolific PL2303 */
    { USB_DEVICE(0x0403, 0x6001) },  /* FTDI FT232R */
    { USB_DEVICE_INFO(0x02, 0x02, 0x01) }, /* CDC类通信设备 */
    { }
};

5.4 USB Gadget驱动(OTG从机)

I.MX6ULL 的 USB OTG 从机功能通过 USB Gadget 框架实现:

/* 模拟U盘的Gadget驱动加载 */
// 1. 加载composite框架
// modprobe libcomposite.ko

// 2. 加载大容量存储功能
// modprobe usb_f_mass_storage.ko

// 3. 加载mass_storage Gadget
// modprobe g_mass_storage.ko file=/dev/sda1 removable=1

对应的内核配置路径:

-> Device Drivers
   -> USB support (USB_SUPPORT [=y])
      -> USB Gadget Support (USB_GADGET [=y])
         -> [M]USB Gadget Drivers
            -> [M]Mass Storage Gadget    // 大容量存储
            -> [M]Audio Gadget           // 音频(UAC 1.0)

六、实验验证

6.1 USB设备识别

# 查看系统USB设备列表
lsusb
# 输出示例:
# Bus 001 Device 002: ID 046d:c52b Logitech
# Bus 001 Device 003: ID 0781:5567 SanDisk U盘

# 查看USB设备详细信息
lsusb -v -d 046d:c52b

# 查看USB设备拓扑
lsusb -t

6.2 内核日志查看

# 插入USB设备后查看内核日志
dmesg | tail -20

# 输出示例:
# [1234.567890] usb 1-1: new high-speed USB device
# [1234.678901] usb 1-1: New USB device found
# [1234.678902] usb 1-1: idVendor=046d, idProduct=c52b
# [1234.678910] input: Logitech USB Receiver as /devices/...

6.3 鼠标输入测试

# 查看输入设备
cat /proc/bus/input/devices | grep -A 4 "Mouse"

# 读取鼠标原始数据
cat /dev/input/event3 | hexdump
# 移动鼠标或按键时会看到数据变化

6.4 U盘挂载操作

# 插入U盘后查看设备节点
ls /dev/sda*

# 创建挂载点
mkdir -p /mnt/usb_disk

# 挂载U盘(FAT32格式)
mount /dev/sda1 /mnt/usb_disk/ -t vfat -o iocharset=utf8

# 查看文件
ls /mnt/usb_disk/

# 安全卸载
sync
cd /
umount /mnt/usb_disk

6.5 模拟U盘测试(Gadget)

# 加载Gadget驱动模块
depmod
modprobe libcomposite
modprobe usb_f_mass_storage
modprobe g_mass_storage file=/dev/sda1 removable=1

# PC端识别到U盘后可读写
# 卸载Gadget
rmmod g_mass_storage

七、跨平台对比

特性 I.MX6ULL (Linux) STM32 (HAL) RK3568 (Linux)
USB控制器 EHCI + OTG OTG FS/HS xHCI + OTG
驱动模型 usb_driver + URB HAL_PCD / HAL_HCD usb_driver + URB
设备树 需要配置 无需(CubeMX) 需要配置
主机模式 ✅ EHCI ✅ HAL_HCD ✅ xHCI
从机模式 ✅ Gadget框架 ✅ HAL_PCD ✅ Gadget框架
OTG切换 ID引脚检测 ID引脚检测 ID引脚 + Type-C CC
USB版本 USB 2.0 USB 2.0 FS/HS USB 3.0
HCD实现 EHCI 库函数封装 xHCI
Gadget支持 完整框架 CDC/MSC/DFU 完整框架

STM32 HAL USB 与 Linux USB 的主要区别

  • STM32 使用 HAL 库函数(HAL_PCD_*)直接操作 USB 外设寄存器,驱动以回调函数为主
  • Linux 通过 URB 抽象层解耦硬件差异,驱动开发者不直接操作寄存器
  • STM32 的 USB 从机需要手动处理 USB 协议栈细节,Linux Gadget 框架封装了协议实现

RK3568 的优势

  • 支持 USB 3.0 xHCI 控制器,传输速率更高
  • 多个独立 USB 控制器,可同时连接更多设备
  • Type-C 控制器原生支持 PD 协议协商

八、面试精选

题目1:USB有哪几种传输类型?各自的应用场景是什么?

考察点:USB协议基础

参考答案

USB 定义四种传输类型:

  1. 控制传输(Control):用于设备枚举和配置,是唯一必须支持的传输类型。分为建立阶段、数据阶段和状态阶段,有重试机制保证可靠性。
  2. 批量传输(Bulk):用于大量数据传输,如 U 盘读写、打印机数据。没有时间保证,但通过握手包保证数据完整性。
  3. 中断传输(Interrupt):用于小数据量、周期性传输,如键盘鼠标。主机按固定间隔轮询端点,保证低延迟。
  4. 同步传输(Isochronous):用于实时音视频流。保证带宽和时延,但不保证数据完整性(无握手包,出错不重传)。

题目2:什么是URB?它在USB数据传输中起什么作用?

考察点:Linux USB核心机制

参考答案

URB(USB Request Block)是 Linux USB 子系统中数据传输的核心载体,类似于网络协议中的 sk_buff。它封装了一次 USB 传输的所有信息:目标设备、管道(端点+方向)、传输缓冲区、长度、完成回调等。

工作流程:驱动分配 URB → 填充参数 → 提交给 USB 核心 → 核心转给 HCD → HCD 操作硬件完成传输 → 调用完成回调通知驱动。驱动在回调中处理数据,然后可以重新提交 URB 实现持续接收。

URB 支持异步提交(通过回调通知)和同步提交(usb_bulk_msg 内部封装)。等时传输需要预分配多个 URB 轮转使用。

题目3:USB枚举过程分为哪几个阶段?

考察点:USB协议细节

参考答案

USB 枚举分三个回合:

  1. 第一回合:主机复位设备,从地址 0 的端点 0 获取设备描述符(18 字节),确认设备存在。
  2. 第二回合:主机再次复位,通过控制传输发送 SET_ADDRESS 请求,为设备分配唯一地址。设备切换到新地址后用该地址通信。
  3. 第三回合:主机用新地址获取完整的设备描述符、配置描述符、字符串描述符等,了解设备的全部能力。

枚举完成后,主机根据设备类和 VID/PID 加载对应的驱动程序。如果设备是复合设备(如 USB 声卡+麦克风),需要分别匹配不同的接口驱动。

题目4:Linux USB驱动中probe和disconnect回调的作用是什么?

考察点:Linux驱动框架

参考答案

probe 在设备与驱动匹配成功时调用,完成以下工作:

  • usb_interface 获取 usb_device 信息
  • 找到合适的端点(如中断 IN 端点)
  • 分配 URB、申请 DMA 缓冲区
  • 填充并提交 URB,开始数据传输
  • 保存私有数据(usb_set_intfdata

disconnect 在设备拔出或驱动卸载时调用,必须做好清理工作:

  • 调用 usb_kill_urb 取消所有未完成的 URB
  • 释放 URB 和缓冲区内存
  • 清除接口私有数据(usb_set_intfdata(NULL)

常见错误:在 probe 中未做错误处理导致资源泄漏,disconnect 中未取消 URB 导致内核崩溃。

题目5:USB Gadget框架是什么?它和USB Host驱动的区别在哪?

考察点:USB从机编程

参考答案

USB Gadget 是 Linux 内核中用于 USB 从机(Device)端的框架,允许嵌入式设备模拟各种 USB 设备(U盘、声卡、网卡等)。与 Host 驱动的区别:

对比项 USB Host 驱动 USB Gadget 驱动
角色 主机,发起通信 从机,被动响应
框架 usb_driver + URB USB Gadget + Function
驱动注册 usb_register() gadget_bind_driver()
数据方向 由主机控制 由主机决定
典型应用 鼠标键盘驱动 模拟U盘、USB声卡

Gadget 架构分为三层:UDC 驱动(硬件层)、Gadget 框架(协议层)、Function 驱动(功能层)。开发时通常只需要编写 Function 驱动,如 g_mass_storage(模拟U盘)、g_audio(模拟声卡)。


最后更新: 2026-09-17