RK3576 ConfigFS Gadget 配置失败排查指南

电子说

1.4w人已加入

描述

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

rk3576

先搞清楚:你的 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 确认状态。

六、实战案例

rk3576

案例一: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。

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 相关推荐
  • 热点推荐
  • rk3576

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分