victor
发布于

Linux USB gadget的configfs和functionfs

一、Linux 支持 usb 主机驱动, 同时也支持从设备驱动。

其主要使用 gadget API 框架实现。 软件一般分为三个层次,

  1. gadget function 对应于 usb function, 即按照协议, usb 设备需要实现的功能。(如 HID, MSC, UVC 等)
  2. gadget api(framework) 即 Linux 提供的 usb gadget 驱动框架
  3. UDC Driver (即 usb 设备控制器驱动), 实现控制器相关驱动

二、关于 usb gadget 设备 function 驱动, 最新 Linux 提供了多种方式。

  1. 使用 usb_compositor_driver, 注册/实现该驱动即可。
  2. 使用 configfs 进行配置, 用户态可以利用配置文件系统创建相应 configuration, interface, endpoint 等, 配合内核态的驱动, 即可实现某种功能的 usb 设备。
  3. 使用 functionfs 进行配置, 最新的安卓相关 usb 驱动, 如 adb, mtp, ptp 就是采用这种方式实现。 即内核态提供功能文件系统, 内部可关联多种标准功能, 具体协议由应用层实现, 即所谓的用户态驱动。

三、通过 configfs 配置的 Linux USB gadget

概述
USB Linux Gadget 是一种具有 UDC (USB 设备控制器)的设备,可以连接到 USB 主机,以扩展其附加功能,如串口或大容量存储能力。
一个 gadget 被它的主机视为一组配置,每个配置都包含一些接口,从 gadget 的角度来看,这些接口被称为功能,每个功能代表一个串行连接或一个 SCSI 磁盘。
Linux 提供了许多 gadget 可以使用的功能。
创建一个 gadget 意味着决定将有哪些配置以及每个配置将提供哪些功能。
Configfs(请参阅 Configfs—用户空间驱动的内核对象配置)非常适合告诉内核上述决定。本文档是关于如何实现这一点的。它还描述了如何将 configfs 集成到 gadget 中。

要求
为了使其工作,配置文件必须可用,因此 CONFIGFS_FS 必须为 'y' 或 'm' 在。config 中。在撰写本文时,USB_LIBCOMPOSITE 选择 CONFIGFS_FS。

用法
(描述 configfs 提供的第一个功能的原始帖子可以在这里看到:http://www.spinics.net/lists/linux-usb/msg76388.html)

modprobe libcomposite mount none $CONFIGFS_HOME -t configfs
其中 CONFIGFS_HOME 是 configfs 的挂载点。

1. 创建 gadget
对于每个要创建的 gadget,必须创建相应的目录:

mkdir CONFIGFS_HOME/usb_gadget/
例如:

mkdir CONFIGFS_HOME/usb_gadget/g1
cd CONFIGFS_HOME/usb_gadget/g1
每个 gadget 需要指定其 vendor id 和 product id :

echo <VID> > idVendor echo > idProduct
gadget 还需要它的序列号、制造商和产品字符串。为了有一个地方存储它们,必须为每种语言创建一个字符串子目录,例如:

$ mkdir strings/0x409
然后可以指定字符串:

echo <serial number> > strings/0x409/serialnumber echo > strings/0x409/manufacturer
$ echo > strings/0x409/product
2. 创建配置
每个 gadget 将由许多配置组成,必须创建相应的目录:

$ mkdir configs/.
可以是文件系统中合法的任意字符串,而是配置的编号,例如:

$ mkdir configs/c.1
每个配置也需要它的字符串,所以必须为每种语言创建一个子目录,例如:

$ mkdir configs/c.1/strings/0x409
然后可以指定配置字符串:

$ echo > configs/c.1/strings/0x409/configuration
也可以为配置设置一些属性,例如:

$ echo 120 > configs/c.1/MaxPower
3. 创建功能
gadget 将提供一些功能,对于每个功能,必须创建相应的目录:

$ mkdir functions/.
其中对应于一个允许的功能名称,是文件系统中允许的任意字符串,例如:

$ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()
每个函数都提供其特定的属性集,具有只读或读写访问权限。如适用,需要酌情写入。更多信息请参考 Documentation/ABI/testing/configfs-usb-gadget。

4. 关联功能及其配置
此时,许多 gadget 被创建出来,每个 gadget 都有一些指定的配置和一些可用的功能。剩下的就是指定哪个功能在哪个配置中可用(同一个功能可以在多个配置中使用)。这是通过创建符号链接来实现的:

$ ln -s functions/. configs/.
例如:

$ ln -s functions/ncm.usb0 configs/c.1
5. 启用 gadget
以上所有步骤的目的是组成 gadget 的配置和功能。

示例目录结构可能看起来像这样:

  .
  ./strings
  ./strings/0x409
  ./strings/0x409/serialnumber
  ./strings/0x409/product
  ./strings/0x409/manufacturer
  ./configs
  ./configs/c.1
  ./configs/c.1/ncm.usb0 -> ../../../../usb_gadget/g1/functions/ncm.usb0
  ./configs/c.1/strings
  ./configs/c.1/strings/0x409
  ./configs/c.1/strings/0x409/configuration
  ./configs/c.1/bmAttributes
  ./configs/c.1/MaxPower
  ./functions
  ./functions/ncm.usb0
  ./functions/ncm.usb0/ifname
  ./functions/ncm.usb0/qmult
  ./functions/ncm.usb0/host_addr
  ./functions/ncm.usb0/dev_addr
  ./UDC
  ./bcdUSB
  ./bcdDevice
  ./idProduct
  ./idVendor
  ./bMaxPacketSize0
  ./bDeviceProtocol
  ./bDeviceSubClass
  ./bDeviceClass

这样的 gadget 必须最终启用,以便 USB 主机能够枚举它。

为了启用 gadget,它必须绑定到 UDC (USB 设备控制器):

$ echo > UDC
其中是在/sys/class/udc/*,例如:

$ echo s3c-hsotg > UDC

6. 禁用 gadget
$ echo "" > UDC

7. 清理
从配置中删除功能:

$ rm configs/./
.指定配置,是指向从配置中删除的功能的符号链接,例如:

$ rm configs/c.1/ncm.usb0
删除配置中的字符串目录:

$ rmdir configs/./strings/
例如:

$ rmdir configs/c.1/strings/0x409
并删除配置:

$ rmdir configs/.
例如:

rmdir configs/c.1
删除功能(功能模块不会被卸载):

$ rmdir functions/.
例如:

$ rmdir functions/ncm.usb0
删除 gadget 中的字符串目录:

$ rmdir strings/
例如:

$ rmdir strings/0x409
最后移除 gadget:

cd .. rmdir
例如:

$ rmdir g1

实施设计
下面介绍 configfs 的工作原理。在 configfs 中有项目和组,它们都表示为目录。项和组之间的区别在于,组可以包含其他组。下图中只显示了一个项目。项和组都可以具有属性,这些属性表示为文件。用户可以创建和删除目录,但不能删除文件,文件可以是只读的或读写的,这取决于它们所代表的内容。

configfs 的文件系统部分操作 config_items/groups 和 configfs_attributes,它们是通用的,对所有配置的元素具有相同的类型。但是,它们被嵌入到特定于使用的更大的结构中。下面的图片中有一个“cs”,它包含一个 config_item 和一个“sa”,它包含一个 configfs_attribute。

文件系统视图是这样的:


  ./
  ./cs        (directory)
     |
     +--sa    (file)
     |
     .
     .
     .

每当用户读取/写入“sa”文件时,都会调用一个函数,该函数接受一个 struct config_item 和一个 struct configfs_attribute。在上述函数中,使用众所周知的 container_of 技术检索“cs”和“sa”,并调用适当的 sa 函数(show 或 store)并传递“cs”和字符缓冲区。“show”用于显示文件的内容(将数据从 cs 复制到缓冲区),而“store”用于修改文件的内容(将数据从缓冲区复制到 cs),但这取决于两个函数的实现者来决定它们的操作。

typedef struct configured_structure cs;
typedef struct specific_attribute sa;

                                       sa
                       +----------------------------------+
        cs             |  (*show)(cs *, buffer);          |
+-----------------+    |  (*store)(cs *, buffer, length); |
|                 |    |                                  |
| +-------------+ |    |       +------------------+       |
| | struct      |-|----|------>|struct            |       |
| | config_item | |    |       |configfs_attribute|       |
| +-------------+ |    |       +------------------+       |
|                 |    +----------------------------------+
| data to be set  |                .
|                 |                .
+-----------------+                .

文件名由配置项/组设计器决定,而目录通常可以随意命名。一个组可以有许多自动创建的默认子组。

有关 configfs 的更多信息,请参见 Documentation/filesystems/configfs.rst

上面描述的概念转化为 USB gadget 如下:

  1. 一个小工具有它的配置组,它有一些属性(idVendor, idProduct 等)和默认子组(configs, functions, strings)。写入属性将导致信息存储在适当的位置。在配置、函数和字符串子组中,用户可以创建它们的子组来表示给定语言中的配置、函数和字符串组。

  2. 用户创建配置和函数,在配置中创建到函数的符号链接。当将 gadget 的 UDC 属性写入时使用此信息,这意味着将 gadget 绑定到 UDC。驱动程序/usb/gadget/configfs.c 中的代码遍历所有配置,并且在每个配置中遍历所有函数并绑定它们。这样整个 gadget 就被绑定了。

  3. 文件驱动程序/usb/gadget/configfs.c 包含以下代码:

gadget's config_group
gadget's default groups (configs, functions, strings)
associating functions with configurations (symlinks)
  1. 个 USB 函数自然都有自己想要配置的视图,所以特定函数的 config_groups 定义在函数实现文件 drivers/ USB /gadget/f_*.c 中。

  2. 函数的代码是以它所使用的方式编写的。

Usb_get_function_instance(),它反过来调用 request_module。因此,只要 modprobe 工作正常,特定函数的模块就会自动加载。请注意,相反的情况是不正确的: 在 gadget 被禁用和卸载后,模块仍然是加载的。

浏览 (302) 点赞 收藏 分享
评论