4. USB Gadget驱动开发实战:配置UVC、UAC、RNDIS、Mass Storage功能,实现复合设备
好,咱们进入第四章。这一章我打算聊点实在的——怎么把RK芯片变成一个“多功能USB小钢炮”。说白了,就是让设备插上电脑后,既能当摄像头(UVC),又能当麦克风(UAC),还能当网卡(RNDIS)和U盘(Mass Storage)。这种复合设备在视频会议、工业采集、车载系统里非常常见。
我个人习惯把Gadget驱动比作“乐高积木”。每个功能是一块积木,你要做的就是选好积木,然后拼在一起。但拼的时候要注意——有些积木会打架,比如带宽分配和端点冲突。嗯,咱们一步步来。
4.1 复合设备的设计思路
先看整体架构。RK平台的USB Gadget基于ConfigFS和FunctionFS。用户空间通过写配置文件来“组装”设备,内核负责底层传输。
核心原则:每个功能(UVC/UAC/RNDIS/Mass Storage)都是一个独立的Function,通过ConfigFS挂载到同一个配置描述符下。USB主机枚举时,会看到这个配置下包含多个接口(Interface),每个接口对应一个功能。
下面这张图展示了复合设备的逻辑结构,我建议你先看明白再动手。
经验之谈:我在RK3399项目上做过一个四合一复合设备。刚开始没注意端点分配,结果UVC和UAC抢同一个Bulk端点,导致视频流卡顿。后来我强制给每个功能分配独立的端点区间,问题就解决了。你设计时最好先画一张端点分配表。
4.2 各功能模块的配置要点
下面逐个拆解。每个功能我都给出关键配置步骤和避坑点。
4.2.1 UVC(USB Video Class)
UVC让设备模拟摄像头。RK平台通常用g_webcam或uvc-gadget驱动。配置时要注意分辨率和帧率匹配。
关键配置步骤:
- 加载内核模块:
modprobe uvc-gadget - 通过ConfigFS创建UVC功能:
mkdir /sys/kernel/config/usb_gadget/g1/functions/uvc.usb0 - 设置格式描述符(支持的分辨率列表)
- 启动用户空间流服务:
uvc-gadget -u /dev/video0 -r 1920x1080
注意:UVC的实时性要求高。我曾经在RK3288上遇到视频帧率不稳的问题,后来发现是DMA缓冲区太小。建议把vb2-videobuf2-dma-contig的缓冲区数量调到4以上。
4.2.2 UAC(USB Audio Class)
UAC让设备变成USB声卡。RK平台支持UAC1(全速)和UAC2(高速)。我建议用UAC2,延迟更低。
配置示例:
# 创建UAC功能
mkdir /sys/kernel/config/usb_gadget/g1/functions/uac2.usb0
# 设置音频参数(采样率、通道数)
echo 48000 > /sys/kernel/config/usb_gadget/g1/functions/uac2.usb0/p_srate
echo 2 > /sys/kernel/config/usb_gadget/g1/functions/uac2.usb0/p_chmask
# 绑定到配置
ln -s /sys/kernel/config/usb_gadget/g1/functions/uac2.usb0 /sys/kernel/config/usb_gadget/g1/configs/c.1/
避坑指南:UAC和UVC同时工作时,音频流可能被视频流挤占带宽。我曾经在RK3568上遇到音频断断续续的问题,解决办法是把UAC的端点优先级设高(通过修改u_audio.c中的端点描述符)。
4.2.3 RNDIS(Remote NDIS)
RNDIS让设备模拟USB网卡。Windows主机免驱,Linux/macOS需要额外驱动。配置相对简单。
配置步骤:
- 加载模块:
modprobe g_ether或通过ConfigFS创建rndis.usb0 - 设置MAC地址(避免与主机冲突)
- 配置IP地址:
ifconfig usb0 192.168.2.1
小技巧:RNDIS的吞吐量受端点大小影响。我习惯把Bulk端点设为512字节(高速)或1024字节(超高速)。在RK3588上实测,1024字节端点能跑到280Mbps。
4.2.4 Mass Storage
Mass Storage让设备变成U盘。RK平台支持g_mass_storage或ConfigFS方式。我推荐用ConfigFS,更灵活。
配置示例:
# 创建Mass Storage功能
mkdir /sys/kernel/config/usb_gadget/g1/functions/mass_storage.usb0
# 设置存储文件(可以是文件镜像或块设备)
echo /data/usb_storage.img > /sys/kernel/config/usb_gadget/g1/functions/mass_storage.usb0/lun.0/file
# 设置只读属性(可选)
echo 1 > /sys/kernel/config/usb_gadget/g1/functions/mass_storage.usb0/lun.0/ro
注意:存储文件必须是FAT32或ext4格式。我遇到过用NTFS格式导致Windows无法识别的问题。另外,文件大小建议不超过4GB(FAT32限制)。
4.3 复合设备的完整配置流程
好,现在把四个功能拼在一起。下面是一个完整的脚本示例,我在RK3568上验证过。
#!/bin/bash
# 复合设备配置脚本 (UVC + UAC + RNDIS + Mass Storage)
GADGET_DIR=/sys/kernel/config/usb_gadget/g1
# 1. 创建Gadget
mkdir $GADGET_DIR
echo 0x2207 > $GADGET_DIR/idVendor # RK VID
echo 0x0123 > $GADGET_DIR/idProduct # 自定义PID
# 2. 创建配置
mkdir $GADGET_DIR/configs/c.1
echo "Composite Device" > $GADGET_DIR/configs/c.1/configuration
# 3. 创建各功能
# UVC
mkdir $GADGET_DIR/functions/uvc.usb0
echo 1920x1080 > $GADGET_DIR/functions/uvc.usb0/streaming/uncompressed/u/1920x1080/frame.dwFrameInterval
# UAC
mkdir $GADGET_DIR/functions/uac2.usb0
echo 48000 > $GADGET_DIR/functions/uac2.usb0/p_srate
# RNDIS
mkdir $GADGET_DIR/functions/rndis.usb0
echo "00:22:33:44:55:66" > $GADGET_DIR/functions/rndis.usb0/dev_addr
# Mass Storage
mkdir $GADGET_DIR/functions/mass_storage.usb0
echo /data/usb_storage.img > $GADGET_DIR/functions/mass_storage.usb0/lun.0/file
# 4. 绑定功能到配置
ln -s $GADGET_DIR/functions/uvc.usb0 $GADGET_DIR/configs/c.1/
ln -s $GADGET_DIR/functions/uac2.usb0 $GADGET_DIR/configs/c.1/
ln -s $GADGET_DIR/functions/rndis.usb0 $GADGET_DIR/configs/c.1/
ln -s $GADGET_DIR/functions/mass_storage.usb0 $GADGET_DIR/configs/c.1/
# 5. 绑定UDC
echo "fe800000.usb" > $GADGET_DIR/UDC # RK3568 OTG控制器地址
4.4 常见问题与调试方法
配置过程中,你可能会遇到下面这些问题。我把它们整理成表格,方便你对照排查。
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 主机枚举失败,设备无响应 | UDC未正确绑定或驱动未加载 | 检查/sys/class/udc/是否有设备节点;dmesg | grep udc查看错误 |
| UVC视频流卡顿 | 端点带宽不足或缓冲区太小 | 增大vb2缓冲区数量;检查USB速度是否为高速/超高速 |
| UAC音频有杂音 | 采样率不匹配或时钟抖动 | 固定采样率为48kHz;使用外部晶振提供时钟 |
| RNDIS无法获取IP | MAC地址冲突或DHCP服务未启动 | 设置唯一MAC地址;主机端手动配置静态IP |
| Mass Storage无法挂载 | 文件系统格式不支持或镜像损坏 | 使用mkfs.fat重新格式化;检查lun.0/file路径是否正确 |
调试利器:我调试复合设备时,最常用的命令是lsusb -v(查看描述符)和usbmon(抓USB包)。有一次RNDIS死活不通,用usbmon抓包发现控制传输返回了STALL,原来是端点描述符长度写错了。
4.5 性能调优建议
复合设备同时跑四个功能,带宽和CPU资源是瓶颈。下面是我总结的几条调优经验。
- 端点分配:给实时性要求高的功能(UVC、UAC)分配独立的Bulk/Isochronous端点。避免共享端点导致争抢。
- 缓冲区大小:UVC的缓冲区建议设为4~8个帧缓冲(每个1920x1080帧约3MB)。UAC的缓冲区可以小一些,256KB足够。
- 中断频率:RNDIS和Mass Storage对中断不敏感,可以降低中断频率来减少CPU开销。
- DMA一致性:确保所有功能使用DMA一致性内存(
dma_alloc_coherent),避免cache一致性问题。
我的习惯:在RK3588上做复合设备时,我会先单独测试每个功能的吞吐量,确保达标后再组合。比如UVC先测30fps是否稳定,UAC测48kHz无爆音,RNDIS测iperf吞吐量。每个功能都过了,再合起来测。这样定位问题快很多。
嗯,这一章的内容就到这里。复合设备的配置其实不难,难的是把四个功能协调好。你按照上面的步骤走,遇到问题对照表格排查,应该能顺利搞定。记住,调试时多用dmesg和lsusb,它们是你最好的朋友。