USB Gadget 技术
1. 什么是 USB Gadget?
USB Gadget 是 Linux 内核提供的一项技术,允许具备 USB 设备控制器(USB Device Controller)的硬件(如嵌入式开发板、手机)模拟成各种 USB 设备(如 U 盘、串口、网卡、打印机等),从而与主机(如电脑)进行通信。
简单来说,普通 USB 设备(如 U 盘)是“被动”被主机识别的,而 USB Gadget 让硬件能“主动”扮演不同的 USB 设备角色,实现灵活的 USB 通信功能。
2. 核心原理
2.1 硬件基础
设备需具备 USB 设备控制器(区别于 USB 主机控制器),即硬件支持作为 USB 从设备(Device)与主机(Host)连接。常见于:
- 嵌入式开发板(如树莓派 Zero、BeagleBone)
- 智能手机
- 其他支持 USB OTG 功能的设备
2.2 内核框架
Linux 内核通过 usb_gadget 子系统提供统一接口,开发者无需直接操作硬件,只需通过软件配置:
- 设备描述符(Device Descriptor)
- 配置描述符(Configuration Descriptor)
- 接口描述符(Interface Descriptor)
即可定义设备类型和功能。
2.3 功能模拟
通过加载不同的 Gadget 驱动模块,硬件可模拟成多种 USB 设备:
- 存储设备(U盘):通过
mass_storage模块 - 虚拟串口:通过
g_serial模块 - 网络设备(RNDIS/CDC):通过
g_ether模块 - 复合设备:同时模拟多种类型(如同时作为 U 盘和串口)
3. 典型应用场景
-
嵌入式调试
开发板通过 USB Gadget 模拟成串口(g_serial),直接与电脑连接调试,无需额外串口线。 -
数据传输
开发板模拟成 U 盘(mass_storage),让电脑直接读写板载存储。 -
网络共享
手机通过 USB tethering(USB 共享网络)功能,本质是通过 Gadget 模拟成网卡。 -
工业设备
定制复合 USB 设备,同时提供数据传输(串口)和固件升级(存储)功能。
4. 基本使用(以嵌入式 Linux 为例)
4.1 确认硬件支持
- 检查内核是否启用 USB Gadget 相关配置(
CONFIG_USB_GADGET及具体设备驱动) - 确认硬件有 USB 设备控制器接口(通常标注为“USB OTG”或“USB Device”)
4.2 加载 Gadget 模块
模拟串口
modprobe g_serial # 生成 /dev/ttyGS0(设备端),电脑端识别为虚拟串口(如 COMx 或 /dev/ttyUSBx)
模拟 U 盘
modprobe g_mass_storage file=/path/to/image.bin stall=0 # image.bin 为模拟的磁盘镜像
模拟网卡
modprobe g_ether host_addr=00:11:22:33:44:55 dev_addr=66:77:88:99:aa:bb
4.3 验证功能
将设备通过 USB 线连接到电脑,电脑会自动识别为对应的 USB 设备(如新增串口、U盘或网络适配器)。
5. 关键概念
- Gadget 驱动:内核模块(如
g_serial、g_ether),定义模拟的 USB 设备类型和功能。 - USB 描述符:用于向主机说明设备信息(如厂商 ID、产品 ID、支持的接口等),是主机识别设备的依据。
- 复合设备(Composite Gadget):通过
libcomposite框架组合多个功能(如同时作为串口和 U 盘),需自定义配置描述符。
6. 实例解析:g_serial 虚拟串口
6.1 底层工作原理
g_serial 驱动通过 USB Gadget 框架模拟 USB 串口设备(CDC-ACM 或 Generic Serial) 的硬件行为和协议规范,让电脑误认为连接了真实物理串口。
硬件层面:USB 设备控制器的激活
g_serial驱动初始化 USB 设备控制器,使其工作在“设备模式”- 控制器通过物理 USB 接口(Micro-USB/Type-C)与电脑连接,负责电信号收发
协议层面:模拟 USB 串口标准规范
支持两种规范:
- CDC-ACM:USB 官方通信设备类标准(适用于调制解调器、串口)
- Generic Serial:兼容传统 USB 转串口芯片(如 FTDI、PL2303)
通过 USB 描述符宣告身份:
- 设备描述符:含 VID、PID、设备类型(通信设备类)
- 配置描述符:说明供电模式、接口数量
- 接口描述符:定义数据传输端点(对应串口收发功能)
数据交互:模拟串口读写行为
- 设备端:创建虚拟串口
/dev/ttyGS0,应用程序通过标准串口 API(open/read/write)操作 - 数据转发:
- 设备 → 电脑:数据封装成 USB 批量传输包(Bulk OUT)发送
- 电脑 → 设备:接收 USB 批量传输包(Bulk IN),解析后写入
/dev/ttyGS0
- 控制信号:支持 RTS/CTS 流控制、波特率设置(通过 USB 控制传输实现)
6.2 电脑自动识别的原因
USB 枚举过程
- 设备连接检测:电脑检测到 USB 总线上的电平变化
- 获取设备描述符:电脑读取
g_serial的描述符(含设备类型、VID/PID) - 加载匹配驱动:
- Windows:加载内置
usbser.sys驱动(支持 CDC-ACM) - Linux:通过
cdc_acm驱动识别为ttyACM0 - macOS:通过
AppleUSBACM驱动识别为/dev/tty.usbmodemXXX
- Windows:加载内置
cdc_acm驱动能进行串口数据和USB数据的转换,gadget驱动也是这个作用,最主要的区别就是gadget能让设备作为USB从设备(发送USB描述符)被主设备识别
即插即用的核心
g_serial 遵循 USB 标准设备类协议,主流操作系统已内置标准驱动,因此无需手动安装即可自动识别。
7. gadget – 模拟
注意gadget是把USB模拟为串口、网口…,而非真实的USB设备(比如USB蓝牙适配器)
USB 蓝牙设备(如常见的 USB 蓝牙适配器)通常不基于 USB Gadget 框架,两者的角色和工作原理完全不同,核心区别在于“设备角色”和“实现方式”:
1. USB 蓝牙适配器的本质:USB 外设(从设备)
常见的 USB 蓝牙适配器(如插入电脑的蓝牙模块)是标准的 USB 外设,其工作方式是:
- 作为 USB 从设备(Device)连接到电脑(USB 主机),通过 USB 接口与主机通信。
- 依赖主机端的蓝牙驱动(如 Linux 的
btusb驱动、Windows 的BthUsb.sys)实现蓝牙协议栈(如 BR/EDR、BLE)。 - 功能是“扩展主机的蓝牙能力”,让主机具备蓝牙通信功能(如连接蓝牙鼠标、耳机)。
这类设备的固件和硬件设计目标是“被主机识别并控制”,不涉及 USB Gadget 框架,因为它们本身就是单纯的 USB 外设,而非“模拟其他设备的 Gadget 设备”。
2. USB Gadget 框架的角色:模拟 USB 设备的主设备
USB Gadget 框架的核心是让硬件(如嵌入式开发板)作为 USB 主设备(Host)端的“从设备”,但主动模拟成其他 USB 设备类型(如串口、U盘)。例如:
- 开发板通过 USB Gadget 模拟成“蓝牙适配器”(理论上可行,但极少这样做),此时开发板会被电脑识别为一个 USB 蓝牙设备,这才是基于 Gadget 框架的场景。
但这种情况非常罕见,因为:
- 蓝牙功能通常由专门的蓝牙芯片实现,而非通过软件模拟(Gadget 框架更适合模拟简单的 USB 设备,如串口、存储设备)。
- 模拟蓝牙设备需要实现复杂的蓝牙协议栈(如基带、LMP、L2CAP 等),这远超 Gadget 框架的设计目标(Gadget 仅负责 USB 层面的设备模拟)。
3. 特殊场景:蓝牙设备中的 Gadget 功能
某些嵌入式设备(如智能手机)可能同时具备:
- 蓝牙功能(通过内置蓝牙芯片,作为独立的无线模块)。
- USB Gadget 功能(通过 USB 设备控制器,模拟成串口、网卡等)。
但这两个功能是相互独立的:蓝牙功能由蓝牙芯片和对应的协议栈(如 BlueZ)实现,与 USB Gadget 框架无关;而 USB Gadget 仅负责 USB 层面的设备模拟。
总结
- 普通 USB 蓝牙适配器:是标准 USB 外设,依赖主机端驱动工作,不基于 USB Gadget 框架。
- USB Gadget 框架:用于让硬件模拟成 USB 设备(如串口、U盘),理论上可模拟蓝牙设备,但实际中极少这样做(因蓝牙协议复杂,更适合由专用芯片实现)。
两者的核心区别在于:USB 蓝牙适配器是“真实的 USB 外设”,而 USB Gadget 是“模拟 USB 外设的软件框架”。
