FunctionFS:用户态实现 USB Function
1. 定位
f_fs.c(FunctionFS)把接口/端点的定义与数据面交给用户态进程,内核只负责与 composite/UDC 的桥接。ADB 是最典型用户。与 GadgetFS(legacy/inode.c,整设备级、已过时)不同,FunctionFS 是 function 级,可与内核 function 在同一 gadget 中组合。
2. 使用流程
1 | mkdir functions/ffs.adb (configfs) |
挂载点下文件由内核动态生成:ep0一个,数据端点按描述符声明数量生成ep1..epN(按声明顺序,不是端点地址)。
3. 内核侧关键入口(function/f_fs.c)
| 函数 | 行号 | 职责 |
|---|---|---|
functionfs_init() |
1667 | 注册 functionfs 文件系统类型 |
ffs_ep0_write() |
331 | 接收描述符 blob 与字符串表(两阶段状态机) |
ffs_ep0_read() |
507 | 向 daemon 投递事件(BIND/ENABLE/SETUP/SUSPEND…) |
ffs_epfile_read_iter() |
1238 | OUT 端点数据读 |
ffs_epfile_write_iter() |
1201 | IN 端点数据写 |
ffs_epfile_ioctl() |
1300 | FIFO status/flush、descriptor 查询、异步 |
ffs_func_bind() |
3295 | 把用户描述符接入 composite(autoconfig 端点) |
ffs_func_setup() |
3366 | class/vendor setup 转发为 ep0 事件 |
描述符格式为usb_functionfs_descs_head_v2+ 标志(FS/HS/SS/OS descriptor 支持),UAPI 在include/uapi/linux/usb/functionfs.h。
4. 事件模型
daemon 必须持续读ep0处理事件:
| 事件 | 含义 |
|---|---|
FUNCTIONFS_BIND/UNBIND |
function 接入/移出 gadget |
FUNCTIONFS_ENABLE/DISABLE |
SET_CONFIGURATION/alt 切换,数据端点可用性变化 |
FUNCTIONFS_SETUP |
需要 daemon 应答的 EP0 请求(read/write ep0 完成数据/状态阶段) |
FUNCTIONFS_SUSPEND/RESUME |
总线挂起恢复 |
ENABLE 前读写数据端点会失败;DISABLE 后在途 I/O 以错误完成。daemon 崩溃退出会导致 function 不可用,管理服务需负责重启并重放描述符。
5. 数据面语义
- 端点文件支持同步 read/write 与 AIO(
io_submit),ADB 使用 AIO 提升吞吐; - OUT 读长度应为 maxpacket 的整数倍以避免 babble;短包正常结束一次传输;
FUNCTIONFS_ENDPOINT_DESCioctl 可取实际生效的描述符(含协商速度包长);- 大传输由 UDC/DWC3 自动分包,daemon 无需按包切分,但一次 I/O 即一次 USB 传输边界。
6. 与 ConfigFS 的装配顺序
ffs.<name>实例在 daemon 写完描述符前处于”未就绪”状态,configfs_composite_bind会失败。正确顺序是:创建实例 → mount → daemon 写描述符 → 写 UDC。Android 的 setup 脚本即按此顺序处理 adbd。
7. 安全
- FunctionFS 让用户态完全定义接口行为,挂载点与 ep 文件权限必须限制到专用服务账户;
- Host 发来的 SETUP 内容对 daemon 而言是不可信输入;
- 描述符 blob 有长度与结构校验,但业务协议(如 ADB 认证)完全在用户态,内核不提供额外保护。
正在加载留言…