RK3576 ConfigFS Gadget 配置失败排查指南 电子说
适用场景:你在 RK3576 板子上照着教程配好了 ConfigFS,目录该有的都有了,但插上电脑就是不弹出设备、adb devices 为空、或者 UVC 摄像头主机认不到。

先搞清楚:你的 ConfigFS 到底有没有生效
很多人花了一整天折腾 ConfigFS,其实问题根本不在 ConfigFS——前面的 UDC 或 PHY 就没起来。先跑这三条命令确认基础链路:
# ① DWC3 控制器有没有注册dmesg | grep dwc3 | grep -E "probe|USB3"# 正常: [1.232160] dwc3 23000000.usb: USB3 Host (xHCI): 1x USB 3.0 + 1x USB 2.0# 异常:(无输出)← DWC3 没 probe# ② UDC 设备有没有创建ls /sys/class/udc/# 正常: fc400000.dwc3# 异常:(空)← DWC3 没配 gadget 模式# ③ ConfigFS 基础结构ls /sys/kernel/config/usb_gadget/g1/configs/c.1/# 正常: MaxPower bmAttributes strings adb mtp rndis# 异常: 目录不存在 ← 还没创建配置
如果①或②有异常,回去修 DWC3 和 PHY。如果③异常,接着往下看。
一、ConfigFS 配置失败的 dmesg 解读
写入 UDC 触发 bind 时的错误,dmesg 必有记录。
1.1 写入 UDC 时报 "write error: No such device"
echo "fc400000.dwc3" > /sys/kernel/config/usb_gadget/g1/UDC# 正常:(无输出)# 异常: bash: echo: write error: No such device
dmesg 线索:
# 正常:Gadget 成功使能[ 3.456789] configfs-gadget gadget: high-speed config #1: ADB+RNDIS+MTP[ 3.456800] dwc3 23000000.usb: connected to gadget driver# 异常:UDC 名写错[ 3.123456] configfs-gadget gadget: UDC fc400000.usb not found# 异常:UDC 被别的 Gadget 占了[ 2.345678] dwc3 fc400000.dwc3: request for UDC failed: -EBUSY
① high-speed config #1 → 最想看到的。Gadget 在 high-speed 模式使能成功。
② UDC ... not found → 控制器名写错。用 ls /sys/class/udc/ 确认真实名称。注意:rockchip 平台上不是 fc400000.usb,而是fc400000.dwc3。
ls /sys/class/udc/# 正常: fc400000.dwc3# 异常:(空)→ DWC3 驱动没加载
③ -EBUSY → UDC 已被占用。多个 Gadget 实例抢同一个 UDC,或 Init 脚本和你的脚本打架了。
1.2 Function bind 失败
# mass_storage 后端文件找不到[ 4.567890] mass_storage usb0: failed to open backing file /data/disk.img[ 4.567891] configfs-gadget gadget: function mass_storage.usb0 failed to bind: -2# UVC streaming header 没链接[ 4.678901] uvc_gadget: could not link header (0)[ 4.678902] configfs-gadget gadget: function uvc.gs6 failed to bind: -22# ACM 串口被占用[ 4.789012] acm acm.usb0: can't claim interface 0 → -EBUSY(-16)
backing file not found(-ENOENT) → mass_storage 的lun.0/file 指向的文件不存在。ls -l /data/disk.img 确认。不存在就用dd if=/dev/zero of=/data/disk.img bs=1M count=256 创建。
could not link header(-EINVAL) → UVC 的 control/streaming header 符号链接没配全。解法见第四节。
-EBUSY → 端点冲突。多个 Function 争用同一个端点号。
1.3 mkdir function 时报 "No such device"
mkdir /sys/kernel/config/usb_gadget/g1/functions/ffs.adb# 正常:(无输出)# 异常: mkdir: No such device
根因:内核没编对应 function 驱动。
ls /sys/kernel/config/usb_gadget/g1/functions/ # 看已注册的 functionzcat /proc/config.gz | grep CONFIG_USB_CONFIGFS_F_FS # 确认内核配置# 正常: CONFIG_USB_CONFIGFS_F_FS=y# 异常: is not set → 重编内核
二、ADB + MTP + RNDIS 配置脚本(完整可复制)
以下脚本在 RK3576 Android 15 验证过。每行都有注释,别跳着看。
#!/bin/bashCONFIGFS="/sys/kernel/config/usb_gadget"GADGET="${CONFIGFS}/g1"UDC="fc400000.dwc3" # 以 ls /sys/class/udc/ 为准# ---- 0. 环境检查 ----[ ! -d "/sys/class/udc/${UDC}" ] && echo "UDC 不存在,检查 dr_mode" && exit 1mount | grep -q "configfs" || mount -t configfs none "${CONFIGFS%/*}" || exit 1# ---- 1. 清理旧配置 ----# 必须先写 "none" 断开 UDC,否则 rmdir 报 EBUSYif [ -d "${GADGET}" ]; then echo "none" > "${GADGET}/UDC" 2>/dev/null for link in ${GADGET}/configs/c.1/*; do [ -L "$link" ] && rm -f "$link"; done rmdir ${GADGET}/configs/c.1/strings/0x409 2>/dev/null rmdir ${GADGET}/configs/c.1 2>/dev/null for func in ${GADGET}/functions/*; do [ -d "$func" ] && rmdir "$func" 2>/dev/null; done rmdir ${GADGET}/strings/0x409 2>/dev/null rmdir ${GADGET} 2>/dev/nullfi# ---- 2~4. 创建 Gadget / 设备描述符 / 字符串 ----mkdir ${GADGET}echo "0x2207" > ${GADGET}/idVendor # Rockchip VIDecho "0x0013" > ${GADGET}/idProduct # RNDIS+ADB 组合 PIDecho "0x0200" > ${GADGET}/bcdUSB # USB 2.0echo "0xEF" > ${GADGET}/bDeviceClass # 复合设备类echo "0x02" > ${GADGET}/bDeviceSubClassecho "0x01" > ${GADGET}/bDeviceProtocolmkdir -p ${GADGET}/strings/0x409echo "Rockchip" > ${GADGET}/strings/0x409/manufacturerecho "RK3576 Dev Board" > ${GADGET}/strings/0x409/productecho "0123456789ABCDEF" > ${GADGET}/strings/0x409/serialnumber# serialnumber 不能为空,Windows 遇空序列号可能跳过设备# ---- 5. 创建配置 ----mkdir -p ${GADGET}/configs/c.1/strings/0x409echo "ADB+RNDIS+MTP" > ${GADGET}/configs/c.1/strings/0x409/configurationecho 500 > ${GADGET}/configs/c.1/MaxPower # 500mAecho 0xC0 > ${GADGET}/configs/c.1/bmAttributes# ---- 6. 创建 Function ----mkdir ${GADGET}/functions/ffs.adb # ADB → FunctionFSmkdir ${GADGET}/functions/ffs.mtp # MTP → FunctionFSecho 1 > ${GADGET}/os_desc/use # Windows OS 描述符(MTP 必需)echo 1 > ${GADGET}/os_desc/b_vendor_codemkdir -p ${GADGET}/os_desc/b.1echo "MTP" > ${GADGET}/os_desc/b.1/compatible_idmkdir ${GADGET}/functions/rndis.usb0 # RNDIS → 内核原生# dev_addr 和 host_addr 不能相同,否则 DHCP 协商失败echo "1a3c5e:6f" > ${GADGET}/functions/rndis.usb0/dev_addrecho "1a3c5e:70" > ${GADGET}/functions/rndis.usb0/host_addr# ---- 7. 链接 Function 到配置 ----# ln -s 触发内核 config_usb_cfg_link(),调用 bind 回调ln -s ${GADGET}/functions/ffs.adb ${GADGET}/configs/c.1/adbln -s ${GADGET}/functions/ffs.mtp ${GADGET}/configs/c.1/mtpln -s ${GADGET}/functions/rndis.usb0 ${GADGET}/configs/c.1/rndis# 链接名顺序决定接口号顺序:adb=iface0, mtp=iface1, rndis=iface2# ---- 8. 挂载 FunctionFS ----mkdir -p /dev/usb-ffs/adb && mount -t functionfs adb /dev/usb-ffs/adbmkdir -p /dev/usb-ffs/mtp && mount -t functionfs mtp /dev/usb-ffs/mtp# "No such device" → 内核没开 CONFIG_USB_FUNCTIONFS# ---- 9. 等待 adbd 就绪(最关键!避免时序问题)----for i in 1 2 3 4 5; do ready="${GADGET}/configs/c.1/adb/ffs.ready" [ -f "$ready" ] && [ "$(cat $ready)" = "1" ] && break sleep 1done# ---- 10. 关联 UDC,激活 Gadget ----echo "${UDC}" > ${GADGET}/UDC && echo "激活成功" || { dmesg | tail -5; exit 1; }# ---- 最终状态 ----echo "UDC: $(cat ${GADGET}/UDC)" # 正常: fc400000.dwc3echo "Speed: $(cat /sys/class/udc/${UDC}/current_speed)" # high-speedecho "State: $(cat /sys/class/udc/${UDC}/state)" # configuredecho "FFS: $(ls /dev/usb-ffs/adb/ep* 2>/dev/null | xargs)" # ep0 ep1 ep2
预期 vs 异常对照:
| 步骤 | 正常 | 异常 | 排查方向 |
| mkdir ffs.adb | 无错误 | No such device | 内核没配 CONFIG_USB_CONFIGFS_F_FS |
| ln -s ... | 无错误 | Invalid argument | 链接路径不对 |
| mount -t functionfs | 无错误 | No such device | 没开 CONFIG_USB_FUNCTIONFS |
| echo UDC | dmesg 打印配置名 | no UDC available | 控制器名写错或 DWC3 没 probe |
三、FunctionFS 无法挂载的排查
FunctionFS 是 ADB 和 MTP 的基础,这个挂了那俩全废。
3.1 mount 报 "No such device"
mount -t functionfs adb /dev/usb-ffs/adb# mount: /dev/usb-ffs/adb: No such device
排查:
zcat /proc/config.gz | grep CONFIG_USB_FUNCTIONFS # 必须 =ylsmod | grep functionfs # 如果是模块,modprobe functionfsmount | grep configfs # configfs 是否已挂载
3.2 ep0 写入描述符失败
adbd 在跑但端点没出来:
ls -la /dev/usb-ffs/adb/ # 异常: 只有 ep0logcat -s adbd # 正常: descriptors written OK
Invalid argument → 描述符格式不对(adbd 版本问题)。Operation not permitted → SELinux 拦截。
logcat -b events | grep avc # 查 SELinuxsetenforce 0 && retry # 临时放通调试
3.3 主机看不到 ADB 设备
cat /sys/kernel/config/usb_gadget/g1/UDC # 正常: fc400000.dwc3cat /sys/kernel/config/usb_gadget/g1/configs/c.1/adb/ffs.ready # 正常: 1ls -la /dev/usb-ffs/adb/ # 正常: ep0 ep1 ep2ps -A | grep adbd # 进程是否活着
四、UVC Gadget 枚举失败的排查
UVC 的 ConfigFS 是所有 Function 里最复杂的,翻车率最高。
4.1 bind 失败:header 链接没配
# dmesg:[ 5.678901] uvc_gadget: could not link header (0)[ 5.678902] configfs-gadget gadget: function uvc.gs6 failed to bind: -22
修复——确认两条链接都存在:
mkdir -p ${GADGET}/functions/uvc.gs6/control/header/hln -s ${GADGET}/functions/uvc.gs6/control/header/h ${GADGET}/functions/uvc.gs6/control/class/fsln -s ${GADGET}/functions/uvc.gs6/control/header/h ${GADGET}/functions/uvc.gs6/control/class/ssmkdir -p ${GADGET}/functions/uvc.gs6/streaming/header/hln -s ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg ${GADGET}/functions/uvc.gs6/streaming/header/h/
4.2 "no valid formats configured"
# dmesg:[ 5.789012] uvc_gadget: no valid formats configured
ls ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/# 正常: 480p 720p 1080p 异常:(空)→ 没配分辨率cat ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/480p/wWidth# 正常: 640 异常: 文件不存在 → 分辨率目录没建完整
4.3 认到 UVC 但视频流打不开
dmesg | grep uvc_gadget # 正常: registered as video1cat ${GADGET}/functions/uvc.gs6/streaming_maxpacket # USB2=1024, USB3=3072
4.4 UVC 最小配置片段
mkdir ${GADGET}/functions/uvc.gs6# MJPEG 480p @ 30fpsmkdir -p ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/480pecho 640 > ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/480p/wWidthecho 480 > ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/480p/wHeightecho 333333 > ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/480p/dwDefaultFrameIntervalprintf "333333n400000n666666n" > ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/480p/dwFrameInterval# MJPEG 720p @ 30fpsmkdir -p ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/720pecho 1280 > ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/720p/wWidthecho 720 > ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/720p/wHeightecho 333333 > ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/720p/dwDefaultFrameIntervalprintf "333333n666666n" > ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg/720p/dwFrameInterval# Header 链接mkdir -p ${GADGET}/functions/uvc.gs6/streaming/header/hln -s ${GADGET}/functions/uvc.gs6/streaming/uncompress/mjpeg ${GADGET}/functions/uvc.gs6/streaming/header/h/mkdir -p ${GADGET}/functions/uvc.gs6/control/header/hln -s ${GADGET}/functions/uvc.gs6/control/header/h ${GADGET}/functions/uvc.gs6/control/class/fsln -s ${GADGET}/functions/uvc.gs6/control/header/h ${GADGET}/functions/uvc.gs6/control/class/ssln -s ${GADGET}/functions/uvc.gs6 ${GADGET}/configs/c.1/
五、sys.usb.config 属性无法切换的排查
setprop sys.usb.config mtp,adbgetprop sys.usb.config# 异常: adb ← 没切过去!getprop sys.usb.state# 异常: none ← 卡在中间状态
5.1 看 logcat
# Framework 日志logcat -b main -s UsbDeviceManager# 正常: "succeeded switching to mtp,adb"# 异常: "setCurrentFunction failed, switching back to none"# HAL 日志logcat -b system -s android.hardware.usb.gadget# 正常: "pullup succeeded"# 异常: "Cannot create symlink ... errno:17" → 上次链接没清理# "Permission denied" → SELinux
5.2 手动调 HAL 测试
echo none > /sys/kernel/config/usb_gadget/g1/UDC # 断开rm -f /sys/kernel/config/usb_gadget/g1/configs/c.1/* # 清理echo "0x0017" > /sys/kernel/config/usb_gadget/g1/idProduct # MTP+ADB PIDps -A | grep adbd || setprop ctl.start adbd # 确认 adbdecho fc400000.dwc3 > /sys/kernel/config/usb_gadget/g1/UDC # 重新使能
手动可以、框架不行→ 问题在 HAL 或 SELinux。
5.3 查 SELinux
dmesg | grep avc | grep -i usb# avc: denied { write } for comm="vendor.usb.gad" name="UDC"setenforce 0 && retry # 临时放通;好了就补 sepolicy
sepolicy 修复(vendor_usb_gadget.te):
allow vendor_usb_gadget sysfs_usb_gadget:dir rw_dir_perms;allow vendor_usb_gadget sysfs_usb_gadget:file rw_file_perms;allow vendor_usb_gadget sysfs_usb_gadget:lnk_file rw_lnk_file_perms;
5.4 HAL 超时
# logcat: I Waiting for FFS pullup... E FFS pullup timeout!
adbd 挂了或没就绪。ps -A | grep adbd 确认进程,cat .../ffs.ready 确认状态。
六、实战案例

案例一:RNDIS 配好了但主机拿不到 IP
现象:dmesg 显示正常,Windows 出现 RNDIS 设备,但 IP 一直是 169.254.x.x。
排查:
echo "1a3c5e:6f" > ${GADGET}/functions/rndis.usb0/dev_addrecho "1a3c5e:70" > ${GADGET}/functions/rndis.usb0/host_addrecho none > ${GADGET}/UDC && echo fc400000.dwc3 > ${GADGET}/UDCifconfig usb0 192.168.42.1 netmask 255.255.255.0 up # 可选:手动配 IP
根因:dev_addr 和host_addr 写成了相同值,ARP 协议冲突。
修复:
logcat -b main -s adbd# E adbd: usb_ffs: failed to read from ep1: Connection timed outcat /sys/kernel/debug/dwc3/ 2>/dev/null # 需开启 CONFIG_USB_DWC3_DEBUG*/ep1/state 2>/dev/null || echo "(需开启 CONFIG_USB_DWC3_DEBUG)" # stopped ← 端点没使能dmesg | grep "configfs-gadget"# 显示 bind 成功但 ffs.ready 当时是 0 → 时序问题
案例二:ADB 识别但 adb shell 卡住
现象:adb devices 可见,adb shell 报error: device offline。
排查:
# 方案一:轮询等待 ffs.ready(推荐脚本用)until [ "$(cat ${GADGET}/configs/c.1/adb/ffs.ready 2>/dev/null)" = "1" ]; do sleep 1doneecho fc400000.dwc3 > ${GADGET}/UDC# 方案二:Android 标准做法——HAL 的 MonitorFfs 机制# HAL 监控 FFS 端点变化,所有 FFS function 就绪后才写 UDC# 方案三:检查 adbd 用户权限ls -l /dev/usb-ffs/adb/ep1 # 如果属主 root 但 adbd 跑在 shell → 权限问题
根因:echo UDC 时 adbd 还没写完描述符。
修复:
# 方案一:轮询等待 ffs.ready(推荐脚本用)until [ "$(cat ${GADGET}/configs/c.1/adb/ffs.ready 2>/dev/null)" = "1" ]; do sleep 1doneecho fc400000.dwc3 > ${GADGET}/UDC# 方案二:Android 标准做法——HAL 的 MonitorFfs 机制# HAL 监控 FFS 端点变化,所有 FFS function 就绪后才写 UDC# 方案三:检查 adbd 用户权限ls -l /dev/usb-ffs/adb/ep1 # 如果属主 root 但 adbd 跑在 shell → 权限问题
案例三:MTP 在 Windows 上不弹窗
现象:Linux 正常,Windows 显示 "Unknown USB Device"。
排查:
cat /sys/kernel/config/usb_gadget/g1/os_desc/use# 0 ← 没开!
根因:Windows 需要 Microsoft OS 描述符识别 MTP。没开时 Windows 不认复合设备。
修复:
echo 1 > ${GADGET}/os_desc/useecho 1 > ${GADGET}/os_desc/b_vendor_codemkdir -p ${GADGET}/os_desc/b.1 && echo "MTP" > ${GADGET}/os_desc/b.1/compatible_id# 容易漏的一步:ln -s ${GADGET}/os_desc/b.1 ${GADGET}/configs/c.1/os_descecho none > ${GADGET}/UDC && echo fc400000.dwc3 > ${GADGET}/UDC# 同时确认 mtpd 在跑ps -A | grep mtpls -la /dev/usb-ffs/mtp/ # 正常: ep0 ep1 ep2 ep3
案例四:UAC2 有设备但没声音
现象:主机认到 USB Audio Device,播放音乐没声。
排查:
cat /proc/asound/cards # 有 UAC2_Gadgetcat ${GADGET}/functions/uac2.0/p_chmask # 3 ← OKcat ${GADGET}/functions/uac2.0/c_chmask # 0 ← 采集通道关了!
根因:c_chmask=0 禁用采集路径。部分主机 USB Audio 驱动要求播放和采集同时存在。
修复:
echo 3 > ${GADGET}/functions/uac2.0/c_chmaskecho 48000 > ${GADGET}/functions/uac2.0/c_srateecho 2 > ${GADGET}/functions/uac2.0/c_ssizeecho none > ${GADGET}/UDC && echo fc400000.dwc3 > ${GADGET}/UDC
调试命令收藏
全链路排查,从底层到应用:
| 目标 | 命令 | 正常输出示例 | 异常输出示例 |
| UDC 是否注册 | ls /sys/class/udc/ | fc400000.dwc3 | (空) |
| 绑了哪个 UDC | cat confgfs/g1/UDC | fc400000.dwc3 | (空) |
| Gadget 使能 | dmesg | grep configfs-gadget | high-speed config #1 | no UDC available |
| UDC 状态 | cat /sys/class/udc/*/state | configured | not attached |
| USB 速率 | cat /sys/class/udc/*/current_speed | high-speed | unknown |
| Function 链接 | ls -l confgfs/g1/configs/c.1/ | adb -> ../../ffs.adb | 空目录 |
| FFS 就绪 | cat confgfs/g1/configs/c.1/adb/ffs.ready | 1 | 0 |
| FFS 端点 | ls /dev/usb-ffs/adb/ | ep0 ep1 ep2 | 只有ep0 |
| USB 配置 | getprop sys.usb.config | mtp,adb | none |
| HAL 日志 | logcat -s android.hardware.usb.gadget | pullup succeeded | Permission denied |
| UVC 节点 | dmesg | grep uvc_gadget | registered as video1 | UVC init failed |
| SELinux 拦截 | dmesg | grep avc | grep usb | (无输出) | avc: denied |
一条命令快速巡检:
echo "UDC:$(ls /sys/class/udc/) State:$(cat /sys/class/udc/*/state 2>/dev/null)" && echo "Speed:$(cat /sys/class/udc/*/current_speed 2>/dev/null)" && ls /sys/kernel/config/usb_gadget/g1/configs/c.1/ 2>/dev/null && ls /dev/usb-ffs/*/ep* 2>/dev/null || echo "FFS not mounted" && dmesg | grep -E "bind failed|uvc.*fail" | tail -3
写在最后
ConfigFS 的问题排查有一个通用思路:从下往上,逐层验证。
1.最底层:UDC 存在吗?state 是 configured 吗?
2.中间层:Function 的 bind 成功了吗?dmesg 有没有 error?
3.最上层:FFS 端点文件创建了吗?用户空间守护进程活着吗?
大部分问题靠dmesg | tail 就能定位。内核比你想象的更话痨,关键你得知道它在说什么。
额外建议:调试时尽量别直接物理拔插 USB 线。用 echo none > UDC 再echo UDC 来重新使能。拔插会触发 PHY 重初始化,掩盖问题到底是出在 ConfigFS 还是 PHY。
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !