当前位置:首页>Linux>RK linux平台usb gadget 管理框架

RK linux平台usb gadget 管理框架

  • 2026-10-11 07:16:00
RK linux平台usb gadget 管理框架
之前在Android 平台上开发usb gadget功能时, Google提供了两种管理usb gadget 功能得方式。
1,  rc 文件得方式
android/system/core/rootdir/init.usb.configfs.rc 
2. usb gadget hal
android 的usb device manager 直接通过binder 和usb gadget hal 通信进行管理。
但是linux平台上,由于太开放了,大家都有一套自己的玩法,最简单的就是在系统系统后,运行个脚本,挂载configfs, 然后配置function ,连接功能等。
但是有一些问题,比如:
① 热插拔如何管理?
② 运行过程中想修改function 该怎么办?
③ 有新增function加入怎么快速导入?
这两天在rk3572 的linux平台上适配usb gadget 功能,发现rk有一套管理框架,于是就研究了下,在rk的框架上添加业务自定义的function
最核心的功能:
① 我们只需要提供function的实例名映射
就是在configfs/usb_gadget/rockchip/functions 下面的功能
② 框架定义了一些hook, 可以在加载流程以及function连接前后定制操作
一:框架概述
1.1 核心文件
文件路径说明
框架主脚本device/rockchip/common/overlays/rootfs/usb-gadget/usr/bin/usb-gadget1035 行,管理 configfs 全生命周期
安装脚本device/rockchip/common/overlays/rootfs/usb-gadget/install.sh编译时安装 overlay 到 rootfs
udev 规则device/rockchip/common/overlays/rootfs/usb-gadget/lib/udev/rules.d/61-usb-gadget.rulesUSB 热插拔自动触发
Kconfigdevice/rockchip/common/configs/post/Config.in.usb-gadget编译期配置菜单
1.2 运行时的文件
文件路径说明
环境变量/etc/profile.d/usb-gadget.sh编译时由 install.sh 生成,定义 USB_FUNCS 等
Hook 脚本/etc/usb-gadget.d/*.sh用户自定义 hook,运行时被 source
运行时状态/var/run/usb-gadget/funcs记录当前运行的 function 列表
日志/var/log/usb-gadget.log运行日志
1.3 守护进程架构
┌─────────────────────────────────────────────────┐│  usb-gadget 命令 (用户执行)                       ││  usb-gadget start/stop/restart/update            ││       │                                          ││       ▼                                          ││  echo $ACTION >> /var/run/usb-gadget/commands    ││       │                                          ││       ▼                                          ││  ┌─────────────────────────────────────────┐     ││  │  usb-service 守护进程 (后台常驻)          │     ││  │  tail -f commands | while read ACTION    │     ││  │       │                                  │     ││  │       ▼                                  │     ││  │  usb_handle_request($ACTION)             │     ││  │       │                                  │     ││  │       ▼                                  │     ││  │  usb_prepare() → usb_start()             │     ││  └─────────────────────────────────────────┘     │└─────────────────────────────────────────────────┘
重要:usb-gadget restart 等命令不直接执行,而是通过命令文件发给后台守护进程。因此 export USB_FUNCS=xxx 后直接 usb-gadget restart 不会生效——守护进程用的是自己的环境变量(启动时从 /etc/profile 读取)。
1.4 udev自动触发
SUBSYSTEM=="android_usb", ACTION=="change", RUN+="/usr/bin/usb-gadget update"SUBSYSTEM=="udc",         RUN+="/usr/bin/usb-gadget update"SUBSYSTEM=="extcon", ENV{NAME}=="extcon-usb-port*", RUN+="/usr/bin/usb-gadget update"
写入 UDC 文件、USB 状态变化、Type-C 插拔都会触发 usb-gadget update。
二、Hook 机制详解
2.1 Hook 加载流程
usb_prepare() 阶段(每次 start/update/restart 都会执行):
usb_prepare(){    # 1. source 所有 hook 脚本    if [ -d /etc/usb-gadget.d ]; then        for hook in $(find /etc/usb-gadget.d/ -type f); do            source "$hook"                        # ← hook 脚本中的变量和函数在这里加载        done    fi    USB_FUNCS=${USB_FUNCS:-adb}                  # ← 注意:如果 USB_FUNCS 为空会被覆盖为 adb    # 2. 创建 gadget 目录(仅首次)    if [ ! -d $USB_GADGET_DIR ]; then        mkdir -p $USB_GADGET_DIR        usb_run_stage usb init                   # ← usb_post_init_hook 在这里调用    fi    # 3. 检查 function 是否变化    OLD_FUNCS=$(usb_get_started)    if [ -n "$OLD_FUNCS" ] && [ "$OLD_FUNCS" != "$USB_FUNCS" ]; then        usb_run_stage usb stop                   # ← 变化时先 stop    fi}

2.2 Hook 调度函数

核心调度器 usb_run_stage()(第 291 行):
usb_run_stage(){    for f in $1_pre_$2_hook $1_$2 $1_post_$2_hook; do        type $f >/dev/null 2>/dev/null || continue   # 函数不存在则跳过        usb_msg "Run stage: $f"        eval $f || return 1                          # 执行,失败则中断    done}
传入两个参数(function名 + 阶段名),自动构造 3 个函数名依次执行:
调用示例依次尝试的函数
usb_run_stage usb initusb_pre_init_hook → usb_init → usb_post_init_hook
usb_run_stage usb prepareusb_pre_prepare_hook → usb_prepare → usb_post_prepare_hook
usb_run_stage usb startusb_pre_start_hook → usb_start → usb_post_start_hook
usb_run_stage usb stopusb_pre_stop_hook → usb_stop → usb_post_stop_hook
usb_run_stage adb prepareadb_pre_prepare_hook → adb_prepare → adb_post_prepare_hook
usb_run_stage custom_audio preparecustom_audio_pre_prepare_hook → custom_audio_prepare → custom_audio_post_prepare_hook

2.3 完整生命周期

usb_handle_request("start")    │    ├── usb_enable()                          # 创建 /var/run/usb-gadget/funcs    │    ├── usb_prepare()    │   ├── source /etc/usb-gadget.d/*.sh     # 加载 hook 脚本    │   ├── usb_run_stage usb init            # 仅首次(gadget 目录不存在时)    │   │   ├── usb_pre_init_hook()           # ★ 可定义:加载内核模块    │   │   ├── usb_init()                    # 框架:写 idVendor/strings/MaxPower    │   │   └── usb_post_init_hook()          # ★ 可定义    │   │    │   └── 检查 USB_FUNCS 是否变化    │    └── usb_start()        │        ├── for func in $USB_FUNCS:           # 遍历每个 function        │   ├── mkdir functions/$instance     # 框架自动创建 configfs 目录        │   ├── cd functions/$instance        │   ├── usb_run_stage $func prepare   # 调用 <func>_prepare()        │   │   ├── <func>_pre_prepare_hook() # ★ 可定义        │   │   ├── <func>_prepare()          # ★ 可定义:配置 configfs 属性        │   │   └── <func>_post_prepare_hook()# ★ 可定义        │   └── symlink → configs/b.1/f-$instance  # 框架自动创建        │        ├── echo $UDC > UDC                  # 绑定 UDC,激活 gadget        │        └── for func in $USB_FUNCS:            └── usb_run_stage $func start     # 调用 <func>_start()                ├── <func>_pre_start_hook()   # ★ 可定义                ├── <func>_start()            # ★ 可定义                └── <func>_post_start_hook()  # ★ 可定义
Stop 流程:
usb_handle_request("stop")    ├── usb_run_stage usb stop    │   ├── usb_pre_stop_hook()              # ★ 可定义    │   ├── usb_stop()                       # 框架:解绑 UDC,删 symlink,删 function 目录    │   └── usb_post_stop_hook()             # ★ 可定义    └── usb_disable()                        # 删除 /var/run/usb-gadget/funcs

2.4 Hook 调用时机注意事项

Hook调用频率适用场景
usb_pre_init_hook仅首次(gadget 目录不存在时)加载内核模块
usb_post_init_hook仅首次不要在这里创建 function(restart 时不会重新执行)
usb_pre_prepare_hook每次 start/update/restart动态调整变量
usb_post_prepare_hook每次同上(但注意过度执行可能导致 USB 反复断开重连)
<func>_prepare每次 start 时,针对每个 function推荐:配置 configfs 属性
<func>_pre_start_hook每次 start 时function 启动前的准备工作
usb_pre_stop_hook每次 stop 时清理资源

三、自定义 Function 开发

3.1 推荐方案:让框架管理你的 Function

将自定义 function 注册到 USB_FUNCS,框架自动处理 mkdir、symlink、stop 清理。

步骤 1:创建 hook 脚本

文件位置:device/cvt/common/model/<机型>/customer-usb-gadget.sh

#!/bin/bash## Customer USB Gadget configfs hook 配置## 注意:USB_FUNCS 不在这里设置,由 /etc/profile.d/usb-gadget.sh 控制# 本脚本只负责:加载内核模块 + 定义各 function 的 _prepare() 函数## 覆盖框架变量USB_VENDOR_ID="<YOUR_VID>"USB_PRODUCT_ID="<YOUR_PID>"USB_MANUFACTURER="Customer"USB_PRODUCT="Customer Device"# ============================# 实例名映射 (框架的 usb_instances() 用)# ============================# usb_instances() 通过 <FUNC大写>_INSTANCES 查找实例名# 未定义时默认 <func>.0CUSTOMER_COMM_INSTANCES="customer_comm.0"CUSTOMER_HID_INSTANCES="customer_hid.1"HID_FEATURE_CTRL_INSTANCES="hid_feature_ctrl.2"CUSTOMER_AUDIO_INSTANCES="customer_audio.3"# ============================# 加载内核模块 (仅首次执行)# ============================usb_pre_init_hook(){    insmod /lib/modules/customer_mic_buffer.ko 2>/dev/null    insmod /lib/modules/customer_aout_buffer.ko 2>/dev/null    insmod /lib/modules/usb_f_customer_audio.ko 2>/dev/null    insmod /lib/modules/usb_f_customer_hid.ko 2>/dev/null    insmod /lib/modules/usb_f_customer_comm.ko 2>/dev/null    insmod /lib/modules/usb_f_hid_feature_ctrl.ko 2>/dev/null}# ============================# 每个 function 的 prepare 函数# 框架自动: mkdir → cd 到 function 目录 → 调用 _prepare() → symlink# ============================customer_audio_prepare(){echo"Speaker"    > str_spk_nameecho"Microphone" > str_mic_nameecho"Customer Audio" > str_card_name}customer_comm_prepare(){echo"Customer Communication" > str_intf_name}customer_hid_prepare(){    :   # 无需额外配置}hid_feature_ctrl_prepare(){    :   # 无需额外配置}

步骤 2:在 defconfig 中注册 function

# device/cvt/customer/<客户>/rockchip_rk3572_customer_ab_defconfig# 自定义 function 列表(框架会遍历 USB_FUNCS 逐个创建)RK_USB_EXTRA="customer_comm customer_hid hid_feature_ctrl customer_audio"# 可选:关闭 ADB# RK_USB_ADBD is not set

install.sh 编译时会生成:

# /etc/profile.d/usb-gadget.sh (自动生成)exportUSB_FUNCS="adb customer_comm customer_hid hid_feature_ctrl customer_audio"#                   ^^^ 来自 RK_USB_ADBD=y          ^^^^^^^^^^^^^^^^^^^^^^^^ 来自 RK_USB_EXTRAexportUSB_VENDOR_ID="0x2207"exportUSB_FW_VERSION="0x0310"exportUSB_MANUFACTURER="Rockchip"exportUSB_PRODUCT="rk3xxx"

步骤 3:安装 hook 脚本

在 defconfig 中配置 RK_USB_HOOKS:

RK_USB_HOOKS="customer-usb-gadget.sh"

install.sh 会从 RK_CHIP_DIR(device/rockchip/.chips/rk3572/)查找并安装到 rootfs/etc/usb-gadget.d/。

3.2 框架对每个 function 自动执行的操作

USB_FUNCS="customer_comm customer_hid hid_feature_ctrl customer_audio"    │    ▼ usb_instances() 展开customer_comm → customer_comm.0         ← CUSTOMER_COMM_INSTANCEScustomer_hid → customer_hid.1           ← CUSTOMER_HID_INSTANCEShid_feature_ctrl → hid_feature_ctrl.2customer_audio → customer_audio.3       ← CUSTOMER_AUDIO_INSTANCES    │    ▼ 遍历每个 instance (usb_start 第 906-924 行)mkdir -p functions/customer_comm.0      ← 框架自动cd functions/customer_comm.0customer_comm_prepare()                 ← 你的函数symlink → configs/b.1/f-customer_comm.0 ← 框架自动    │    ▼ 所有 function 处理完后echo 24000000.usb > UDC            ← 框架自动绑定    │    ▼ Stop 时rm configs/b.1/f-*                  ← 框架自动删除 symlinkrmdir functions/customer_*              ← 框架自动删除目录(保留 ffs.*)

3.3 框架内置 Function 的 prepare 实现参考

# adb (第 442-458 行)ADB_INSTANCES="ffs.adb"adb_prepare(){    mount -t devpts none /dev/pts                    # ADB 需要 PTS    ifup --force lo                                  # ADB 需要 loopback    usb_mount ffs.adb /dev/usb-ffs/adb -t functionfs # 挂载 FunctionFS    usb_start_daemon /usr/bin/adbd                   # 启动 adbd 守护进程    usb_wait_access -m /dev/usb-ffs/adb              # 等待挂载完成}# uac1 (第 480-495 行)uac1_prepare(){echo3 > p_chmask                               # 播放声道数echo2 > p_ssize                                 # 播放采样大小 (字节)echo8000,16000,44100,48000 > p_srate            # 播放采样率echo3 > c_chmask                               # 录音声道数echo2 > c_ssize                                 # 录音采样大小echo4 > req_number                              # 请求数echo8000,16000,44100,48000 > c_srate            # 录音采样率}# ums (第 714-764 行)ums_prepare(){# 创建磁盘镜像文件(如果不存在)if [ ! -f"$UMS_FILE" ] && [ ! -b"$UMS_FILE" ]; then        ums_formatfi}

四、运行时动态控制 Function

4.1 控制哪些 Function 生效

通过修改 /etc/profile.d/usb-gadget.sh 中的 USB_FUNCS 来控制:

# 全部启用 (ADB + 自定义)exportUSB_FUNCS="adb customer_comm customer_hid hid_feature_ctrl customer_audio"# 只开自定义 function,不要 ADBexportUSB_FUNCS="customer_comm customer_hid hid_feature_ctrl customer_audio"# 只开 ADBexportUSB_FUNCS="adb"# 只开 audioexportUSB_FUNCS="customer_audio"

4.2 正确重启 usb-gadget 的方法

因为 usb-gadget restart 发命令给守护进程,环境变量不会传递。必须改守护进程能读到的配置,然后重启守护进程:

# 1. 修改 profile.d(守护进程启动时读取)echo'export USB_FUNCS="customer_comm customer_hid hid_feature_ctrl customer_audio"' \    > /etc/profile.d/usb-gadget.sh# 2. 重启守护进程(不是 usb-gadget restart!)/etc/init.d/S50usb-gadget.sh stop/etc/init.d/S50usb-gadget.sh start# 或用 systemdsystemctl restart usb-gadget

4.3 临时禁用 usb-gadget 自动恢复

udev 规则会在 UDC 变化时自动触发 usb-gadget update。临时禁用:

# 方法1:让 update 跳过(推荐)rm /var/run/usb-gadget/funcs# usb_is_enabled 返回 false → update 直接 return# 方法2:删 udev 规则rm /lib/udev/rules.d/61-usb-gadget.rulesudevadm control --reload-rules# 方法3:停服务/etc/init.d/S50usb-gadget.sh stop

4.4 恢复

# 重新启用touch /var/run/usb-gadget/funcs/etc/init.d/S50usb-gadget.sh stop/etc/init.d/S50usb-gadget.sh start

五、常见问题与排查

5.1 usb-gadget restart 后环境变量不生效

原因:usb-gadget restart 通过命令文件发给后台守护进程,守护进程用的是自己启动时的环境变量。

解决: 修改 /etc/profile.d/usb-gadget.sh,然后重启整个服务(S50usb-gadget.sh stop/start)。

5.2 USB_FUNCS="" 设置后 ADB 还在

原因: 框架第 840 行有默认值保护:

USB_FUNCS=${USB_FUNCS:-adb}# 空值会被覆盖为 adb

解决: 改为 USB_FUNCS=${USB_FUNCS:-}(去掉默认值)。

5.3 restart 后自定义 function 消失

原因:usb_post_init_hook() 只在 gadget 目录不存在时调用(首次启动)。restart 时 gadget 目录已存在,usb_init() 不执行,hook 不会被调用。

解决: 不要用 usb_post_init_hook() 创建 function。把 function 注册到 USB_FUNCS,用 _prepare() 函数配置,框架会在每次 start 时自动创建。

5.4 echo none > UDC 后自动恢复

原因: udev 规则 SUBSYSTEM=="udc" 触发 usb-gadget update 重新绑定。

解决: 先 rm /var/run/usb-gadget/funcs,再写 UDC。

5.5 删除 function symlink 后绑 UDC 报 Device or resource busy

原因: configfs 要求 configs/b.1/ 下至少有一个 function symlink。

解决: 先链接一个其他 function 进去,再删除目标 symlink。

5.6 USB 反复断开重连

原因:usb_post_prepare_hook 在每次 usb-gadget update(udev 触发)时都执行,可能导致反复操作。

解决: 不要用 usb_post_prepare_hook 创建/删除 function。用 USB_FUNCS + _prepare() 让框架管理生命周期。

5.7 日志查看

cat /var/log/usb-gadget.log | tail -30

关键日志:

Run stage: usb_pre_init_hook       # 加载内核模块Run stage: usb_init                # 框架初始化 gadgetPreparing instance: customer_audio.3   # 框架创建自定义 functionRun stage: customer_audio_prepare      # 你的 prepare 函数执行Starting functions: adb customer_comm  # UDC 绑定后的 function 列表

六、USB_FUNCS 变量传递链路

defconfig                          install.sh (编译时)              板子运行时─────────                          ────────────────              ──────────RK_USB_ADBD=y         ──┐RK_USB_ACM=y          ──┤RK_USB_UVC=y          ──┤...                     ├──→ usb_funcs() ──→ /etc/profile.d/usb-gadget.shRK_USB_EXTRA="customer_   ──┘                    export USB_FUNCS="adb customer_comm ..."  comm customer_hid ..."                              │                                                  ▼                                            source /etc/profile                                                  │                                                  ▼                                            usb_prepare() 读取 USB_FUNCS                                                  │                                                  ▼                                            usb_start() 遍历创建 function

最新文章

随机文章