通过 configfs 配置 Linux USB gadget

2013年4月25日

概述

Linux USB Gadget 是一个具有 UDC(USB 设备控制器)的设备,可以连接到 USB 主机,以扩展其额外的功能,例如串行端口或大容量存储功能。

主机将 gadget 视为一组配置,每个配置包含多个接口,从 gadget 的角度来看,这些接口被称为功能(functions),每个功能代表着例如一个串行连接或一个 SCSI 磁盘。

Linux 为 gadget 提供了许多可用的功能。

创建 gadget 意味着决定将有哪些配置以及每个配置将提供哪些功能。

Configfs(请参阅 Configfs - 用户空间驱动的内核对象配置)非常适合用于将上述决定告知内核。本文档介绍了如何操作。

本文档还描述了 configfs 如何集成到 gadget 中的设计。

要求

为了使其正常工作,configfs 必须可用,因此 .config 中的 CONFIGFS_FS 必须为 'y' 或 'm'。截至本文撰写时,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/<gadget name>

例如:

$ mkdir $CONFIGFS_HOME/usb_gadget/g1

...
...
...

$ cd $CONFIGFS_HOME/usb_gadget/g1

每个 gadget 都需要指定其供应商 ID <VID> 和产品 ID <PID>

$ echo <VID> > idVendor
$ echo <PID> > idProduct

gadget 还需要其序列号、制造商和产品字符串。为了有地方存储这些字符串,必须为每种语言创建一个 strings 子目录,例如:

$ mkdir strings/0x409

然后可以指定这些字符串

$ echo <serial number> > strings/0x409/serialnumber
$ echo <manufacturer> > strings/0x409/manufacturer
$ echo <product> > strings/0x409/product

可以作为目录在语言目录下创建进一步的自定义字符串描述符,字符串文本写入到该字符串目录内的“s”属性中

$ mkdir strings/0x409/xu.0 $ echo <string text> > strings/0x409/xu.0/s

如果功能驱动程序支持,功能可能允许符号链接到这些自定义字符串描述符,以将这些字符串与类描述符关联起来。

2. 创建配置

每个 gadget 将由多个配置组成,必须创建其对应的目录

$ mkdir configs/<name>.<number>

其中 <name> 可以是文件系统中合法的任何字符串,而 <number> 是配置的编号,例如:

$ mkdir configs/c.1

...
...
...

每个配置也需要其字符串,因此必须为每种语言创建一个子目录,例如:

$ mkdir configs/c.1/strings/0x409

然后可以指定配置字符串

$ echo <configuration> > configs/c.1/strings/0x409/configuration

还可以为配置设置一些属性,例如:

$ echo 120 > configs/c.1/MaxPower

3. 创建功能

gadget 将提供一些功能,对于每个功能,必须创建其对应的目录

$ mkdir functions/<name>.<instance name>

其中 <name> 对应于允许的功能名称之一,而实例名称是文件系统中允许的任意字符串,例如:

$ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()

...
...
...

每个功能都提供其特定的属性集,具有只读或读写访问权限。在适用情况下,需要适当地写入它们。请参阅 ABI 文件测试/configfs-usb-gadget 获取更多信息。

4. 将功能与其配置关联

此时已创建了多个 gadget,每个 gadget 都指定了多个配置并提供了多个功能。接下来需要指定哪个功能在哪个配置中可用(同一个功能可以在多个配置中使用)。这通过创建符号链接实现

$ ln -s functions/<name>.<instance name> configs/<name>.<number>

例如:

$ 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 name> > UDC

其中 <udc name> 是在 /sys/class/udc/* 中找到的名称之一,例如:

$ echo s3c-hsotg > UDC

6. 禁用 gadget

$ echo "" > UDC

7. 清理

从配置中移除功能

$ rm configs/<config name>.<number>/<function>

其中 <config name>.<number> 指定配置,而 <function> 是一个指向要从配置中移除的功能的符号链接,例如:

$ rm configs/c.1/ncm.usb0

...
...
...

移除配置中的字符串目录

$ rmdir configs/<config name>.<number>/strings/<lang>

例如:

$ rmdir configs/c.1/strings/0x409

...
...
...

并移除配置

$ rmdir configs/<config name>.<number>

例如:

rmdir configs/c.1

...
...
...

移除功能(但功能模块不会被卸载)

$ rmdir functions/<name>.<instance name>

例如:

$ rmdir functions/ncm.usb0

...
...
...

移除 gadget 中的字符串目录

$ rmdir strings/<lang>

例如:

$ rmdir strings/0x409

最后移除 gadget

$ cd ..
$ rmdir <gadget name>

例如:

$ rmdir g1

实现设计

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

文件系统视图将是这样的

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

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

文件名由 config item/group 设计者决定,而目录通常可以随意命名。一个组可以自动创建一些默认子组。

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 的更多信息,请参阅 Configfs - 用户空间驱动的内核对象配置

上述概念转换为 USB gadget 如下

1. gadget 拥有其配置组(config group),其中包含一些属性(如 idVendor、idProduct 等)和默认子组(configs、functions、strings)。写入这些属性会导致信息存储在适当的位置。在 configs、functions 和 strings 子组中,用户可以创建自己的子组来表示给定语言的配置、功能和字符串组。

2. 用户创建配置和功能,并在配置中创建指向功能的符号链接。当 gadget 的 UDC 属性被写入时,此信息会被使用,这意味着将 gadget 绑定到 UDC。drivers/usb/gadget/configfs.c 中的代码会遍历所有配置,并在每个配置中遍历所有功能并绑定它们。通过这种方式,整个 gadget 都被绑定了。

文件 drivers/usb/gadget/configfs.c 包含以下代码:

  1. gadget 的 config_group

    • gadget 的默认组(configs、functions、strings)

    • 将功能与配置关联(符号链接)

    • 4. 每个 USB 功能自然都有其自己的配置视图,因此特定功能的 config_groups 定义在功能实现文件 drivers/usb/gadget/f_*.c 中。

功能代码的编写方式使其使用

  1. usb_get_function_instance(),它又会调用 request_module。因此,只要 modprobe 正常工作,特定功能的模块就会自动加载。请注意,反之则不然:在 gadget 被禁用并拆除后,模块仍然保持加载状态。

© 内核开发社区。 | 由 Sphinx 5.3.0Alabaster 0.7.16 提供技术支持 | 页面源