USB 主机与设备驱动
本文档介绍 RK3588 USB 2.0 / 3.0 控制器在 Linux 6.1 内核中的配置、Host/Device 模式切换,以及 gadget(把开发板模拟成串口、网卡、U 盘等 USB 设备的内核框架)的使用,涵盖 OTG(On-The-Go,按线缆连接情况自动切换主机/设备角色)模式。
适用范围: RK3588 Linux 6.1 Debian 12 / Ubuntu 22.04 系统,C-3588LQ V1.0 + SHIMETAPI-M5-R2A V1.0 底板。
1. 概念基础
1.1 USB 是什么
USB(Universal Serial Bus)是嵌入式系统最常用的接口之一。RK3588 内置多路 USB 控制器:
| 类型 | 速率 | 模式 |
|---|---|---|
| USB 2.0 | 480 Mbps | Host / Device / OTG |
| USB 3.0 | 5 Gbps | Host (典型) |
1.2 应用场景
| 模式 | 用途 |
|---|---|
| USB Host | U 盘、移动硬盘、键鼠、UVC |
| USB Device | USB Gadget(串口 / 网卡 / 存储) |
| OTG | Type-C 动态切换 |
1.3 控制器基地址
| 控制器 | 基地址 | 备注 |
|---|---|---|
| USB 2.0 Host0 EHCI | 0xfd800000 | USB 2.0 |
| USB 2.0 Host1 EHCI | 0xfd840000 | USB 2.0 |
| USB 3.0 OTG0 DWC3 | 0xfce80000 | 含 XHCI |
实际 UDC 节点如
fc000000.usb(本机)。
2. 准备工作
2.1 硬件清单
| 组件 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 主控板 | C-3588LQ 核心板 + SHIMETAPI-M5-R2A 底板 | 1 | |
| 调试串口线 | USB 转 TTL(3.3V) | 1 | 波特率 1500000 |
| USB U 盘 | 任意 USB 2.0/3.0 U 盘 | 1 | Host 测试 |
| USB 线 | Type-A / Type-C | 1 | Host/Device 测试 |
| USB 键盘/鼠标(可选) | USB HID 设备 | 1 | Host 测试 |
2.2 引脚
底板已集成 USB Type-A 与 Type-C 接口。Type-C 同时支持 Host / Device 模式(OTG)。
3. 软件准备
3.1 内核配置
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y /* USB 3.0 Host */
CONFIG_USB_EHCI_HCD=y /* USB 2.0 Host */
CONFIG_USB_OHCI_HCD=y /* USB 1.1 Host */
CONFIG_USB_DWC3=y /* Rockchip DWC3 OTG 控制器 */
CONFIG_USB_DWC3_OF_SIMPLE=y
CONFIG_USB_STORAGE=y /* U 盘 / 移动硬盘 */
CONFIG_USB_UAS=y /* USB Attached SCSI */
CONFIG_USB_SERIAL=y /* USB 转串口 */
CONFIG_USB_SERIAL_CH341=y
CONFIG_USB_SERIAL_CP210X=y
CONFIG_USB_SERIAL_FTDI_SIO=y
CONFIG_USB_ACM=y /* USB CDC/ACM gadget */
CONFIG_USB_ETH=y /* USB RNDIS / CDC-Ether gadget */
CONFIG_OF=y3.2 设备树
SoC 级定义(rk3588.dtsi):
/* USB 2.0 Host0 */
usb_host0_ehci: usb@fd800000 {
compatible = "generic-ehci";
reg = <0x0 0xfd800000 0x0 0x20000>;
interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru HCLK_USB2HOST0>, <&cru Clk_USB2HOST0_REF>;
clock-names = "usb", "ref_clk";
phys = <&u2phy0_otg>;
phy-names = "usb-phy";
status = "okay";
};
/* USB 2.0 Host1 */
usb_host1_ehci: usb@fd840000 {
compatible = "generic-ehci";
reg = <0x0 0xfd840000 0x0 0x20000>;
interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru HCLK_USB2HOST1>, <&cru Clk_USB2HOST1_REF>;
clock-names = "usb", "ref_clk";
phys = <&u2phy1_otg>;
status = "okay";
};
/* USB 3.0 OTG(DWC3 + XHCI) */
usb3otg0: usb@fce80000 {
compatible = "rockchip,rk3588-dwc3", "rockchip,rk3568-dwc3";
reg = <0x0 0xfce80000 0x0 0x10000>;
resets = <&cru SRST_A_USB3OTG0>, <&cru SRST_USB3OTG0>;
reset-names = "usb3-otg", "usb3-otg-upstream";
clocks = <&cru CLK_USB3OTG0_REF>, <&cru CLK_USB3OTG0_SUSPEND>,
<&cru ACLK_USB3OTG0>, <&cru CLK_UTMI_OHCI_USB3OTG0>;
clock-names = "ref_clk", "suspend_clk", "aclk_u3", "hclk_u3";
phys = <&u2phy0_otg>, <&usb2phy0>;
phy-names = "usb-phy", "u2phy";
dr_mode = "otg";
status = "okay";
};4. 设备端 USB
4.1 USB 主机已自动枚举
lsusb实机输出如下:
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub本机已自动枚举 6 个 root hub,对应 RK3588 的 6 路 USB 控制器。
4.2 查看 USB 控制器拓扑
lsusb -t输出示例:
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 5000M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/1p, 480M
...
5000M表示 USB 3.0,USB 2.0 root hub 通常显示480M。
4.3 U 盘挂载
# 插入 U 盘后查看块设备
lsblk
# 输出示例:sda 8:0 0 29.7G 0 disk
# └─sda1 8:1 0 29.7G 0 part
# 挂载(FAT32 / ext4 都可)
mkdir -p /mnt/usb
mount /dev/sda1 /mnt/usb
ls /mnt/usb
# 测速
dd if=/dev/zero of=/mnt/usb/test.bin bs=1M count=10 conv=fsync
umount /mnt/usb4.4 USB 转串口(CH340 / CP2102)
# 接 CH340 USB-TTL 模块
ls /dev/ttyUSB*
# 输出:/dev/ttyUSB0
# 串口通信(minicom/picocom)
minicom -D /dev/ttyUSB0 -b 1152005. USB OTG 模式(Device / Peripheral)
5.1 查看当前模式
cat /sys/class/udc/fc000000.usb/current_role实机输出如下(默认 Host 模式):
host注意:Type-C 物理插头决定默认角色。插入 OTG 线 / 接 PC 时切换为 peripheral。
5.2 USB Gadget 串口(USB CDC-ACM)
下面的脚本用 configfs(内核提供的一种通过建目录、写文件来配置内核对象的接口)搭建一个 CDC-ACM 串口 gadget,让 PC 把开发板认成一个 USB 串口:
# 加载内核模块
modprobe libcomposite
modprobe u_serial
# 在 configfs 创建 gadget
cd /sys/kernel/config/usb_gadget/
mkdir -p g1/acm.us0
cd g1
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0100 > bcdDevice
echo 0x0200 > bcdUSB
mkdir -p strings/0x409
echo "rk3588" > strings/0x409/serialnumber
echo "RK3588 Gadget" > strings/0x409/product
mkdir -p configs/c.1
mkdir -p functions/acm.us0
ln -s functions/acm.us0 configs/c.1/
# 绑定 UDC(本机为 fc000000.usb)
ls /sys/class/udc/
echo "fc000000.usb" > UDC5.3 主机端识别
在 PC 上执行:
dmesg | tail
# 或
lsusb
# 应出现 idVendor=0x1d6b idProduct=0x0104 的设备
# 并生成 /dev/ttyACM05.4 关闭 gadget
echo "" > /sys/kernel/config/usb_gadget/g1/UDC
rm -rf /sys/kernel/config/usb_gadget/g16. 设备树中配置 gadget(PC 端)
如需开机自动配置 USB 串口 gadget:
[电脑端] 编辑板级 .dts:
&usb3otg0 {
dr_mode = "peripheral"; /* 强制 device 模式 */
};
/* 可选:定义 gadget 子节点 */
&usbdrd3_0_dwc3 {
/* 配合 dr_mode 使用 */
};[电脑端] 重新编译并烧录:
./build.sh kernel
./mkfirmware.sh开发板端可在开机后由 init 脚本继续配置
configfs。
7. 代码解读
7.1 驱动注册(USB 3.0 OTG)
// drivers/usb/dwc3/dwc3-rockchip.c(简化)
static struct platform_driver rockchip_dwc3_driver = {
.probe = dwc3_rockchip_probe,
.remove = dwc3_rockchip_remove,
.driver = {
.name = "rockchip-dwc3",
.of_match_table = rockchip_dwc3_match,
},
};
static const struct of_device_id rockchip_dwc3_match[] = {
{ .compatible = "rockchip,rk3588-dwc3" },
{ /* sentinel */ }
};7.2 Gadget 调用链
用户空间: echo "fc000000.usb" > /sys/kernel/config/usb_gadget/g1/UDC
→ configfs_set_U DC() /* configfs */
→ usb_gadget_bind_driver()
→ udc_bind_to_driver() /* UDC core */
→ dwc3_gadget_start() /* DWC3 */
→ 设置 USB 设备描述符,启动 EP8. 故障排除
8.1 U 盘不识别
先看内核日志里 USB 的枚举过程:
dmesg | grep -i usb
# 检查 VBUS 电源(5V)
# 插入式设备通常由 USB 口供电8.2 USB 3.0 速度降为 2.0
lsusb -t # 看实际速率
# 检查数据线(必须是 SS 线,标准 3.0)
# 检查 PHY 时钟
cat /sys/kernel/debug/clk/clk_usb3otg0_ref/clk_rate8.3 Gadget 不识别
按下面的顺序排查:
- 检查
ls /sys/class/udc/是否有对应节点 - 确认已
modprobe libcomposite - 检查
dr_mode = "peripheral"(或otg)
