title: USB驱动开发 tags: [Linux驱动, USB, 嵌入式, I.MX6ULL] created: 2026-09-17 updated: 2026-09-17
关联知识:[[03-USB驱动基础]] | [[计算机网络/USB协议]] | [[STM32学习笔记/15-WiFi通信与ESP32-C3]]
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 | 外置显卡 |
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 | 端点参数 |
USB 协议定义四种传输类型,由端点描述符的 bmAttributes 字段的 bit1:0 指定:
| 传输类型 | 位编码 | 方向 | 特点 | 典型应用 |
|---|---|---|---|---|
| 控制传输 | 00 | 双向 | 可靠,有重试机制 | 枚举、配置设备 |
| 同步传输 | 01 | 单向 | 实时,无握手,允许丢数据 | 音频、视频 |
| 批量传输 | 10 | 单向 | 可靠,大数据量,无时间保证 | U盘、打印机 |
| 中断传输 | 11 | 单向 | 周期性,低延迟 | 键盘、鼠标、触摸屏 |
控制传输三阶段:建立阶段(SETUP令牌包)→ 数据阶段(DATA0/DATA1交替)→ 状态阶段(方向与数据阶段相反)。
批量传输过程(以写为例):
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 | 端点挂起 |
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
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 子系统分为三层:
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 控制器支持主机/从机切换。
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;
};
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
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";
};
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 为默认下拉,即默认工作在主机模式。如需从机模式,需要将电气属性改为上拉。
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 功能。
#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);
| 函数 | 功能 | 说明 |
|---|---|---|
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); // 用户上下文
/* 批量同步传输(内部自动分配/提交/等待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);
/* 控制消息(最通用的控制传输接口) */
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);
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);
以下是基于 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");
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
基于 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类通信设备 */
{ }
};
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)
# 查看系统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
# 插入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/...
# 查看输入设备
cat /proc/bus/input/devices | grep -A 4 "Mouse"
# 读取鼠标原始数据
cat /dev/input/event3 | hexdump
# 移动鼠标或按键时会看到数据变化
# 插入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
# 加载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 的主要区别:
HAL_PCD_*)直接操作 USB 外设寄存器,驱动以回调函数为主RK3568 的优势:
考察点:USB协议基础
参考答案:
USB 定义四种传输类型:
考察点:Linux USB核心机制
参考答案:
URB(USB Request Block)是 Linux USB 子系统中数据传输的核心载体,类似于网络协议中的 sk_buff。它封装了一次 USB 传输的所有信息:目标设备、管道(端点+方向)、传输缓冲区、长度、完成回调等。
工作流程:驱动分配 URB → 填充参数 → 提交给 USB 核心 → 核心转给 HCD → HCD 操作硬件完成传输 → 调用完成回调通知驱动。驱动在回调中处理数据,然后可以重新提交 URB 实现持续接收。
URB 支持异步提交(通过回调通知)和同步提交(usb_bulk_msg 内部封装)。等时传输需要预分配多个 URB 轮转使用。
考察点:USB协议细节
参考答案:
USB 枚举分三个回合:
枚举完成后,主机根据设备类和 VID/PID 加载对应的驱动程序。如果设备是复合设备(如 USB 声卡+麦克风),需要分别匹配不同的接口驱动。
考察点:Linux驱动框架
参考答案:
probe 在设备与驱动匹配成功时调用,完成以下工作:
usb_interface 获取 usb_device 信息usb_set_intfdata)disconnect 在设备拔出或驱动卸载时调用,必须做好清理工作:
usb_kill_urb 取消所有未完成的 URBusb_set_intfdata(NULL))常见错误:在 probe 中未做错误处理导致资源泄漏,disconnect 中未取消 URB 导致内核崩溃。
考察点: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