FunctionFS 如何工作¶
概述¶
从内核的角度来看,它只是一个具有一些独特行为的复合函数。只有在用户空间驱动程序通过写入描述符和字符串注册后,才能将其添加到 USB 配置中(用户空间程序必须提供内核级复合函数添加到配置时提供的相同信息)。
这尤其意味着复合初始化函数可能不在 init 部分(即,可能不使用 __init 标签)。
从用户空间的角度来看,它是一个文件系统,当挂载时会提供一个“ep0”文件。 用户空间驱动程序需要将描述符和字符串写入该文件。 它不需要担心端点、接口或字符串编号,只需提供描述符,就像该函数是唯一一个一样(端点和字符串编号从 1 开始,接口编号从 0 开始)。 FunctionFS 会根据需要更改它们,并处理不同配置中数字不同的情况。
有关 FunctionFS 描述符的更多信息,请参阅 FunctionFS 描述符
当写入描述符和字符串时,“ep#”文件会出现(每个声明的端点一个),用于处理单个端点上的通信。 同样,FunctionFS 会处理真实的编号和配置的更改(这意味着“ep1”文件可能实际上映射到(例如)端点 3(当配置更改为(例如)端点 2 时))。“ep0”用于接收事件和处理设置请求。
当所有文件关闭时,该函数将禁用自身。
我还想提到的是,FunctionFS 的设计方式允许多次挂载它,因此最终一个设备可以使用多个 FunctionFS 函数。 这种想法是每个 FunctionFS 实例都由挂载时使用的设备名称标识。
可以想象一个具有以太网、MTP 和 HID 接口的设备,其中后两个通过 FunctionFS 实现。 在用户空间级别,它看起来像这样
$ insmod g_ffs.ko idVendor=<ID> iSerialNumber=<string> functions=mtp,hid
$ mkdir /dev/ffs-mtp && mount -t functionfs mtp /dev/ffs-mtp
$ ( cd /dev/ffs-mtp && mtp-daemon ) &
$ mkdir /dev/ffs-hid && mount -t functionfs hid /dev/ffs-hid
$ ( cd /dev/ffs-hid && hid-daemon ) &
在内核级别,该设备检查 ffs_data->dev_name 以确定其 FunctionFS 是为 MTP (“mtp”) 还是 HID (“hid”) 设计的。
如果没有提供“functions”模块参数,则驱动程序只接受一个具有任何名称的函数。
当提供“functions”模块参数时,仅接受具有所列名称的函数。 特别是,如果“functions”参数的值只是一个单元素列表,则行为类似于根本没有“functions”的情况; 但是,仅接受具有指定名称的函数。
只有在所有声明的函数文件系统都已挂载并且所有函数的 USB 描述符都已写入其 ep0 后,才会注册该设备。
相反,在第一个 USB 函数关闭其端点后,该设备将注销。
DMABUF 接口¶
FunctionFS 另外支持基于 DMABUF 的接口,其中用户空间可以将 DMABUF 对象(外部创建的)附加到端点,然后使用它们进行数据传输。
然后,用户空间应用程序可以使用此接口在多个接口之间共享 DMABUF 对象,从而允许它以零拷贝的方式传输数据,例如在 IIO 和 USB 堆栈之间。
作为此接口的一部分,添加了三个新的 IOCTL。 这三个 IOCTL 必须在数据端点上执行(即不是 ep0)。 它们是
FUNCTIONFS_DMABUF_ATTACH(int)
将由其文件描述符标识的 DMABUF 对象附加到数据端点。 成功时返回零,错误时返回负的 errno 值。
FUNCTIONFS_DMABUF_DETACH(int)
从数据端点分离给定的 DMABUF 对象,该对象由其文件描述符标识。 成功时返回零,错误时返回负的 errno 值。 请注意,关闭端点的文件描述符将自动分离所有附加的 DMABUF。
FUNCTIONFS_DMABUF_TRANSFER(struct usb_ffs_dmabuf_transfer_req *)
将先前附加的 DMABUF 排入传输队列。 参数是一个结构体,它打包了 DMABUF 的文件描述符、要传输的字节大小(通常应对应于 DMABUF 的大小)以及一个目前未使用的“标志”字段。 成功时返回零,错误时返回负的 errno 值。