为啥要介绍usb gadget configfs
在之前的2篇文章来介绍并验证usb gadget configfs,为啥呢?因为现在主流配置usb gadget都是采用configfs通过用户空间进行配置,而不是像之前使用hardcode方式专门有个内核模块来配置,来看一下Linux usb gadget的发展历程:
- David Brownell 在2003年之前引入gadget framework,只支持单一的gadget设备;
- composite framework 在2008年的时候加入进来,用来支持多个function的gadget设备;
- FunctionFS在2010年加入,允许用户空间创建function,并与内核寄存的function组成一个支持多个function的gadget设备;
但是上述还存在一个问题,就是还在自定义的内核模块中,将各个功能各个用例绑定在一起不够便利。因此,在Linux 3.11版本引入usb gadget configfs,支持一系列API,用户层可以通过该API定义任意功能和配置,从用户空间定义特定于应用程序的usb复合设备。
在Androidadb驱动实现原理这篇文章中,我们使用传统的hardcode方式来创建一个android adb的usb复合设备。在usb gadget configfs 验证文章中,我们使用usb gadget configfs方式创建一个mass storage的usb复合设备。无论是使用哪种方式,都是为了创建一个usb复合设备。本文将详细介绍通过usb gadget configfs创建usb复合设备的原理。
为啥是configfs
configfs
是基于内存的文件系统,它提供的功能与sysfs
相反。sysfs
是基于文件系统的kernel对象视图,configfs
是基于文件系统的kernel对象管理器或config_items
。
- 在
sysfs
,对象是在kernel中创建的,并在sysfs
中注册,对象的属性就在sysfs
中出现,允许拥护空间读取。某些属性允许通过用户空间写。最重要的一点是,所有的对象都在kernel中创建或销毁,由kernel控制其生命周期; - 在
configfs
,config_group
是一组共享相同属性和操作的config_items
的集合。config_items
是在用户空间通过mkdir
显式的创建,使用rmdir
销毁,在mkdir
之后会出现对应的属性,允许在用户空间进行读写。与sysfs
不同的是,这些对象的生命周期完全由用户空间驱动,kernel驱动必须响应这些。
在kernel内核中,Documentation/filesystems/configfs/configfs.txt
详细的介绍了configfs原理,samples/configfs/configfs_sample.c
介绍了如何使用configfs。
配置步骤
- 创建gadget,所支持的Functions, Configurations and Strings;
- 设置Strings,将Functions和Configurations链接起来;
- 使能gadget;
Action | FileSystem Commands |
---|---|
Create Gadget/Functions/Configurations.. | Make Directory (mkdir) |
Destroy Gadget/Functions/Configurations.. | Remove Directory (rmdir) |
Set Value of Attributes | Write (echo) |
Get Value of Attributes | Read (cat) |
Group Functions | Symlink (ln -s) |
Ungroup Functions | Remove Symlink (rm) |
以下内容将详细介绍使用usb gadget configfs的步骤。
使能相关的宏
想要使用usb gadget configfs,必须使能CONFIG_FS
和CONFIG_LIBCOMPOSITE
的宏,为啥呢?前者提供configfs的API,后者提供USB Composite Framework的功能,当然也包括创建usb gadget configfs的功能。
1 | obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o |
drivers/usb/gadget/configfs.c
这个文件就是USB Composite Framework基于configfs提供的API,用于创建usb gadget configfs。
挂载configfs
使用mount -t configfs none /config/
命令进行挂载之后,在config/
目录下就会自动生成一个usb_gadget/
目录,站在configfs的角度,也就是创建一个名为usb_gadget
的config_items
的属性。这个是由于在configfs.c
中调用了configfs_register_subsystem()
函数注册了一个名为usb_gadget
的configfs子系统。
1 | static struct configfs_group_operations gadgets_ops = { |
创建名为g1的usb复合设备
mkdir usb_gadget/g1
创建名为g1(gadget 1)
的usb复合设备,执行完毕之后,在g1/
目录下会创建很多属性,这些属性在configfs.c
文件中创建,执行了gadgets_make()
函数,主要做了以下几件事:
创建了包括
gadget_root_type
指定的属性,每个属性都定义了与之对应的_show()
和_store()
函数,这样在用户空间就可以对其进行对应的读写。drivers/usb/gadget/configfs.c 1
2
3
4
5
6
7
8
9
10
11
12static struct configfs_attribute *gadget_root_attrs[] = {
&gadget_dev_desc_attr_bDeviceClass,
&gadget_dev_desc_attr_bDeviceSubClass,
&gadget_dev_desc_attr_bDeviceProtocol,
&gadget_dev_desc_attr_bMaxPacketSize0,
&gadget_dev_desc_attr_idVendor,
&gadget_dev_desc_attr_idProduct,
&gadget_dev_desc_attr_bcdDevice,
&gadget_dev_desc_attr_bcdUSB,
&gadget_dev_desc_attr_UDC,
NULL,
};创建usb复合设备所需的
configuration
和functions
的属性。初始化
struct usb_composite_driver composite
的相关属性。drivers/usb/gadget/configfs.c 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19gi->composite.bind = configfs_do_nothing;
gi->composite.unbind = configfs_do_nothing;
gi->composite.suspend = NULL;
gi->composite.resume = NULL;
gi->composite.max_speed = USB_SPEED_SUPER;
mutex_init(&gi->lock);
INIT_LIST_HEAD(&gi->string_list);
INIT_LIST_HEAD(&gi->available_func);
composite_init_dev(&gi->cdev);
gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE;
gi->cdev.desc.bDescriptorType = USB_DT_DEVICE;
gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice());
gi->composite.gadget_driver = configfs_driver_template;
gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL);
gi->composite.name = gi->composite.gadget_driver.function;
配置PID和VID
实际上会调用idVendor
的_store()
函数来往struct usb_device_descriptor
中的idVendor
和idProduct
写入对应的值。
1 | static inline struct gadget_info *to_gadget_info(struct config_item *item) |
创建并配置strings子目录
配置strings首先必须设置language
,这里设置为0x0409
表示使用的是en-us
语言。
创建configuration和字符串
会调用config_desc_make()
来填充struct usb_configuration
的字段,比如mkdir usb_gadget/g1/configs/c.1
,那么会将c
写入到struct usb_configuration
的label
字段,1
写入到bConfigurationValue
的字段表示有几个configurations
。之后调用usb_add_config_only()
将设置的configurations
与特定的usb复合设备绑定起来。
创建functions
一个USB复合设备会有多个功能,每个功能由function来表示,所谓的function就是USB设备支持的功能。当执行mkdir functions/mass_storage.0
的时候,会调用function_make()
函数,调用usb_get_function_instance()
函数传入mass_storage
字段,将会从现有的function list中找到与之匹配的function。现有的function list是由drivers/usb/gadget/function/f_xxx.c
中进行添加的,这就将f_xxx.c
联系起来了。
将functions和configuration关联起来
执行ln -s usb_gadget/g1/functions/mass_storage.0 usb_gadget/g1/configs/c.1
命令将新添加的mass_storage
的functions
添加到configuration
对应的function list中,表示当前usb复合设备中新增了一个function。这时候调用的是config_usb_cfg_link()
函数。至此,functions
和configuration
关联起来了。接下来要将configuration
与特定的UDC设备连接起来。
绑定到UDC,使能gadget
执行echo xxx > usb_gadget/g1/UDC
命令,就会调用gadget_dev_desc_UDC_store()
函数,这里面调用usb composite framework
中的usb_gadget_probe_driver()
函数将gadget driver与USB Controller Driver绑定,这里的Gadget Driver
就是与我们创建的usb复合设备对应的驱动,接下来就会走一系列的bind
流程。
总结
在上述的步骤中,我们通过usb gadget configfs做了以下几件事:
- 创建了一个usb复合设备,以及创建该设备所需的
configuration
和functions
,并将两者绑定起来; - 创建与usb复合设备相对应的
gadget driver
; - 将
gadget driver
与UDC绑定起来,使能usb设备。