linux usb gadget driver代码

本文基于linux-5.4.124 aspeed 2600(BMC)的代码实现来描述arm结构下的gadget driver.

在读之前,我们需要了解什么是usb gadget driver,以及它的作用。

从英文字面上翻译看,usb gadget driver是一个usb小工具驱动。这说了等于没说。实际上,当你了解了这个驱动后就会知道:从usb角度来看,这是一个usb slave device driver,就是说由这个driver驱动的usb设备是一个usb从设备,即相当于usb鼠标、usb键盘、usb硬盘等,他们相对于pc来说是一个个的usb从设备。

所以,在我们的BMC system中,从usb来看,BMC是payload(x86)的一个usb从设备。

下面,我们正式进入代码之旅(我们主要基于BMC上电后,开始使用usb gadget的整个过程,来完整地了解这个usb gadget driver):

1. 上电后,kernel启动后会加载usb gadget相关的驱动。

先来看vhub驱动。

file: drivers/usb/gadget/udc/aspeed-vhub/core.c

linux usb gadget driver代码_第1张图片

453行,可以看出,这是一个platform driver, 也就是dts解析完后,这个platform driver就会被调用,调用的probe函数,即ast_vhub_probe().

linux usb gadget driver代码_第2张图片

341-350行,申请ast_vhub结构,从ast_vhub_dt_ids找出dts中配置的vhub。

linux usb gadget driver代码_第3张图片

可以看出,DTS中配置的是”aspeed,ast2600-usb-vhub”.

linux usb gadget driver代码_第4张图片

所以,ast_vhub_dt_ids[]对应的是上图中红框部分,即配置数据data为ast2600_config。

配置数据中最大的port数max_ports为7,最大的generic ep数为21.

linux usb gadget driver代码_第5张图片

351-360行,拿到配置数据后,就开始配置对应个ports和epns空间。

linux usb gadget driver代码_第6张图片

365-366,获取dts中配置的寄存器配置空间物理地址,并映射到虚地址空间,记录到vhub->regs.

linux usb gadget driver代码_第7张图片

linux usb gadget driver代码_第8张图片

386-393行,清零中断控制寄存器和ack中断状态寄存器。

linux usb gadget driver代码_第9张图片

396-406行,获取dts中配置的中断号,并设置对应的中断处理句柄ast_vhub_irq.

linux usb gadget driver代码_第10张图片

linux usb gadget driver代码_第11张图片

409-420行,给vhub以及它的7个port的ep0申请DMA空间,一个ep0一个dma_buf.

425行,调用ast_vhub_init_ep0()初始化vhub ep0.

file: drivers/usb/gadget/udc/aspeed-vhub/ep0.c

linux usb gadget driver代码_第12张图片

linux usb gadget driver代码_第13张图片

ast_vhub_init_ep0()中主要是设置cp0的setup寄存器地址和ctlstat寄存器地址,并切分配它的dma buf.

linux usb gadget driver代码_第14张图片

428-429行,调用ast_vhub_init_dev()初始化vhub的7个port devices.

我们继续来看ast_vhub_init_dev()的代码。

file: drivers/usb/gadget/udc/aspeed-vhub/dev.c

linux usb gadget driver代码_第15张图片

567-576行,ast_vhub_init_dev()给指定index的ast_vhub_dev设置name为”portX”(port1-port7), 并调用ast_vhub_init_ep0()初始化ast_vhub_dev的ep0。

582-583行,给当前的ast_vhub_dev分配epn结构,每个ast_vhub_dev最多vhub->max_epns个(21个).

linux usb gadget driver代码_第16张图片

592-601行,给当前ast_vhub_dev配置device结构,用于注册到内核的device list里面.即将当前的vhub port注册到内核,name为1e6a000:usb-vhub:pX (比如1e6a000:usb-vhub:p1),这里的dev_name(parent)从dts解析而来。

linux usb gadget driver代码_第17张图片

linux usb gadget driver代码_第18张图片

605-617行,配置vhub的单个port的gadget,并调用usb_add_gadget_udc()注册gadget对应的udc到udc_list中,就相当于注册了一个udc device。

file: drivers/usb/gadget/udc/core.c

linux usb gadget driver代码_第19张图片

linux usb gadget driver代码_第20张图片

好了,我们回到原先的probe函数,继续看代码。

file: drivers/usb/gadget/udc/aspeed-vhub/core.c

linux usb gadget driver代码_第21张图片

probe在初始化7个Port后,调用ast_vhub_init_hub().

file: drivers/usb/gadget/udc/aspeed-vhub/hub.c

linux usb gadget driver代码_第22张图片

ast_vhub_init_hub()也可简单,就是初始化了一个work,并记录max_ports数。

至此,vhub probe()函数结束了.

从这个probe()函数的构建的数据结构,我们可以构建出这么一个帮助理解的图构:

linux usb gadget driver代码_第23张图片

下面,我们要从应用层面来配置usb gadget时的步骤,来看内核驱动代码实现。

第一步: mount -t configfs none /sys/kernel/config

这看起来很简单了,就是mount了一个文件系统为configfs,挂载点在/sys/kernel/config.

我们来看看相关的驱动代码。

file: fs/configfs/mount.c

linux usb gadget driver代码_第24张图片

configfs_init()是用core_initcall修饰的,它是在内核在初始化时,调用core.init段时调用的。

configfs_init()的代码实现很简单,就是在/sys/kernel/下创建了一个config目录,并将configfs文件系统注册到内核。

linux usb gadget driver代码_第25张图片

当应用程序用mount命令挂载configfs文件系统时,内核会创建一个vfs_mount, 并调用configs_fs_type.init_fs_context, 即configfs_init_fs_context.

vfs_kern_mount() -> fs_context_for_mount() -> fc->fs_type->init_fs_context(),这里的fc->fs_type->init_fs_context()就是configfs_init_context.

linux usb gadget driver代码_第26张图片

vfs_kern_mount() -> fc_mount() -> vfs_get_tree() -> fc->ops->get_tree(), get_tree()就是configfs_get_tree().

get_tree_single()创建超级块sb后,调用configfs_fill_super填充。

linux usb gadget driver代码_第27张图片

linux usb gadget driver代码_第28张图片

这里创建的root dentry就是configfs文件系统”根目录”。

vfs_kern_mount() -> fc_mount() -> vfs_create_mount()

linux usb gadget driver代码_第29张图片

关联configfs挂载点和configfs的”根目录”。

第一步挂载好后,设备上可以看到如下信息:

linux usb gadget driver代码_第30张图片

第二步:  cd /sys/kernel/config/usb_gadget && mkdir cdrom

命令也很简单,到usb_gadget目录,创建子目录cdrom。所以目录”/sys/kernel/config/usb_gadget”要首先存在。这个由gadget_cfs驱动来创建。

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第31张图片

linux usb gadget driver代码_第32张图片

1904行,gadget_cfs_init()调用configs_register_subsystem()。

file: fs/configfs/dir.c

linux usb gadget driver代码_第33张图片

linux usb gadget driver代码_第34张图片

1886行,root->d_fsdata为configfs_root,见fs/configfs/mount.c的configfs_fill_super().

linux usb gadget driver代码_第35张图片

1892行,给”/sys/kernel/config/usb_gadget”分配dentry, dentry->d_op为sb->s_d_op.

linux usb gadget driver代码_第36张图片

即dentry->d_op为configfs_dentry_ops.

1896行,调用configfs_attach_group() -> configfs_attach_item() -> configfs_create_dir().

file: fs/configfs/dir.c

linux usb gadget driver代码_第37张图片

linux usb gadget driver代码_第38张图片

linux usb gadget driver代码_第39张图片

linux usb gadget driver代码_第40张图片

configfs_create_dir()给”usb_gadget“目录dentry创建inode,并安装inode到dentry.同时创建了”usb_gadget”目录对应的configfs_dirent,并放到configfs_root.s_children列表中。

至此,”/sys/kernel/config/”目录下的”usb_gadget”子目录创建了。

所以,这一步的命令”cd /sys/kernel/config/usb/gadget && mkdir cdrom”就可以执行了。

当在“usb_gadget”目录时, 执行mkdir命令会发生什么?

当进入在”usb_gadget”目录时,内核相当于先Open这个目录,找到这个”usb_gadget”目录的dentry, 并取出dentry->inode,将inode的i_op安装到文件指针filp中。

当执行mkdir命令时,就是调用”usb_gadget”目录的inode的i_op.mkdir,即configfs_dir_inode_operations.mkdir,也就是相当于调用了函数configfs_mkdir.

file: fs/configfs/dir.c

linux usb gadget driver代码_第41张图片

linux usb gadget driver代码_第42张图片

linux usb gadget driver代码_第43张图片

1308行,找到当前目录的ci_type,即”usb_gadget”目录的ci_type, 即 gadgets_type。

1344行,调用用gadgets_type.ct_group_ops->make_group, 即 gadgets_make。

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第44张图片

我们继续看gadget_make()的代码。

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第45张图片

1709行,创建gadget_info结构。

1713行,设置config_group的name为”cdrom”,attr为gadget_root_type.

1715-1729行,创建”functions”,”configs”,”strings”,”os_desc”这些config_group,并放到cdrom这个config_group的default_group。

linux usb gadget driver代码_第46张图片

1743-1750行,配置gadget_driver为configfs_driver_template.

linux usb gadget driver代码_第47张图片

1805-1828行,创建”/dev/usbgX”字符设备文件。

下面回到了configfs_mkdir.

1408行,在gadget_make()返回后,调用configfs_attach_group()。参数item为gadget_make()创建的config_group的cg_item.

configfs_attach_group() -> configfs_attach_item() -> configfs_create_dir(),这里在前面”usb_gadget”目录创建的时候介绍过了。这里就相当于在”usb_gadget”目录下创建了”cdrom”目录。

file: /fs/configfs/dir.c

linux usb gadget driver代码_第48张图片

由于gadget_make()创建的config_group是带有attr的,故configfs_create_dir()返回后,调用populate_attrs()去创建attr文件。

linux usb gadget driver代码_第49张图片

file: fs/configfs/file.c

linux usb gadget driver代码_第50张图片

对于gadget_make()创建的config_group,这里的attr就是gadget_root_type。

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第51张图片

也就是相当于在”cdrom”目录下创建了文件”bDeviceClass”、“bDeviceSubClass“、”bDeviceProtocol“、”bMaxPacketSize0“、”idVendor“、”idProduct“、”bdcDevice“、”bcdUSB“、”UDC“.

file: include/linux/configfs.h

linux usb gadget driver代码_第52张图片

当我们要写这些文件时,先open(“/sys/kernel/config/usb_gadget/cdrom/UDC“), 这是内核会先查找”cdrom“目录的dentry,然后再该dentry下调用lookup(), 即dentry->inode.i_op.lookup(),来查找自己目录下面的文件”UDC”。

这里的”cdrom”的dentry->inode.i_op.lookup()为configfs_lookup().

file: fs/configfs/dir.c

linux usb gadget driver代码_第53张图片

linux usb gadget driver代码_第54张图片

linux usb gadget driver代码_第55张图片

linux usb gadget driver代码_第56张图片

故,当打开文件”UDC”时,会给这个文件创建一个inode,并将文件操作inode->i_fop设置为configfs_file_operation.

file: fs/configfs/file.c

linux usb gadget driver代码_第57张图片

所以对目录”cdrom”下的attr文件的读写,就是调用了configfs_read_file()和configfs_write_file().

linux usb gadget driver代码_第58张图片

linux usb gadget driver代码_第59张图片

configfs_read_file() -> fill_read_buffer() -> buffer->attr->show(),这个buffer->attr->show()就是attr文件的show函数。

linux usb gadget driver代码_第60张图片

linux usb gadget driver代码_第61张图片

configfs_write_file() -> flush_write_buffer () -> buffer->attr->store(), 这个buffer->attr->store()就是attr文件的store函数。

对于“cdrom/UDC“而言,就是gadget_dev_desc_UDC_show()和gadget_dev_desc_UDC_store().

file: include/linux/configfs.h

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第62张图片

linux usb gadget driver代码_第63张图片

linux usb gadget driver代码_第64张图片

关于config_group的attr文件的创建就介绍到这了,我们还是回到configfs_mkdir()在调用gadget_make() 创建”cdrom”目录的dentry后,进入到configfs_attach_group()。

我们在gadget_make()创建”cdrom”目录的config_group的时候,创建很多该config_group下的default config_group,如:”functions”,”configs”,”strings”,”os_desc”。所以这些”cdrom”目录下的子目录是需要创建的。这个创建是configfs_attach_group()在创建”cdrom”目录下的attr文件后,调用populate_groups()来创建这些子目录以及子目录的attr文件的。

file: fs/configfs/dir.c

linux usb gadget driver代码_第65张图片

linux usb gadget driver代码_第66张图片

linux usb gadget driver代码_第67张图片

当”mkdir cdrom”完成后,”cdrom”目录,“cdrom“目录的attr文件,以及”cdrom”目录的子目录和子目录的attr文件都会被创建好了。

linux usb gadget driver代码_第68张图片

好了,第二步“cd /sys/kernel/config/usb_gadget && mkdir cdrom “讲完了。

第三步:cd /sys/kernel/config/usb_gadget/cdrom && echo 0x046b >idVendor

按上面的分析,这个就是调用gadget_dev_desc_idVendor_store()来存储vendor id信息了。

file: include/linux/configfs.h

linux usb gadget driver代码_第69张图片

file: drivers/usb/gadget/configfs.c

336行定义了gadget_dev_desc_idVendor_show()和gadget_dev_desc_idVendor_store()。

这两个函数是怎么定义的?

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第70张图片

linux usb gadget driver代码_第71张图片

上面的代码,定义了gadget_dev_desc_idVendor_show()和gadget_dev_desc_idVendor_store().

通过上面的代码,可以看出,gadget_dev_desc_idVendor_store()是将数据0x046b写入到gadget_info->cdev.desc.idVendor中。

linux usb gadget driver代码_第72张图片

类似的数据store还有如下这些:

file: include/linux/usb/ch9.h

linux usb gadget driver代码_第73张图片

第四步:cd /sys/kernel/config/usb_gadget/cdrom && echo 0xFF20 >idProduct

类似idVendor, 将数据0XFF20写入gadget_info->cdev.desc.idProduct

第五步:cd /sys/kernel/config/usb_gadget/cdrom && mkdir strings/0x409

当创建cdrom目录时,strings是放入它的default group中被创建的。

file: drivers/usb/gadget/configfs.c

strings子目录的attr文件定义在gadget_strings_strings_type, 这个gadget_strings_strings_type是怎么定义的?

file: drivers/usb/gadget/configfs.c

file: include/linux/usb/gadget_configfs.h

linux usb gadget driver代码_第74张图片

linux usb gadget driver代码_第75张图片

linux usb gadget driver代码_第76张图片

97行,定义了gadget_strings_strings_type, 这个定义中没有ct_attrs, 故当strings子目录被创建的时候,里面是没有文件的。需要通过mkdir来创建,这个mkdir,就是93行定义的gadget_strings_strings_make。

即当在cdrom/strings中执行mkdir时,驱动调用的时gadget_strings_strings_make(),这个函数在47行定义。我们来看看这个函数gadget_strings_strings_make()的定义:

linux usb gadget driver代码_第77张图片

64-65行,设置新创建的config_group的attr为gadget_strings_langid_type, 这样的话,在strings目录下创建出的子目录,就会自动创建atrr描述的文件。来看看这个gadget_strings_langid_type定义:

file: drivers/usb/gadget/configfs.c

file: include/linux/usb/gadget_configfs.h

linux usb gadget driver代码_第78张图片

所以,这个strings子目录的attr中的ct_attrs[]为gadget_strings_langid_attrs, 这个gadget_strings_langid_attrs定义如下:

file: include/linux/usb/gadget_configfs.h

linux usb gadget driver代码_第79张图片

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第80张图片

从上面定义可以看处,共定义了3个atrr文件:manufacturer, product, serialnumber。所以,这一步” mkdir strings/0x409”,将在strings目录下创建子目录0x409,同时创建子目录的3个attr文件: manufacturer, product, serialnumber。

第六步:cd /sys/kernel/config/usb_gadget/cdrom && echo AAAABBBBCCCC1 > strings/0x409/serialnumber

根据第五步的分析,这里就是调用gadget_strings_serialnumber_store(),将数据写入到gadget_strings->serialnumber.

类似的还有如下信息:

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第81张图片

第七步:cd /sys/kernel/config/usb_gadget/cdrom && echo American Megatreds Inc. > strings/0x409/manufacturer

和第六步一样,就是调用gadget_strings_manufacturer_store(), 将数据写入到gadget_strings->manufacturer.

第八步:cd /sys/kernel/config/usb_gadget/cdrom && echo Virtual Cdrom Device > strings/0x409/product

和第六步一样,就是调用gadget_strings_product_store(), 将数据写入到gadget_strings->product.

第九步:cd /sys/kernel/config/usb_gadget/cdrom && mkdir configs/c.1

在cdrom创建后,configs作为default group也被一同创建了。我们来看看在这个configs目录下创建子目录c.1的操作。

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第82张图片

1720行显示,configs子目录对应的attr为config_desc_type. 它并没有给出ct_attrs[],故子目录configs刚被创建出来的时候,目录里是空的。我们这一步的操作mkdir configs/c.1,就是调用了config_desc_ops.make_group(),即config_desc_make(). 我们接着看这个函数做了些什么。

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第83张图片

695-707行,分配config_usb_cfg内存,并初始化值。

716行,就是调用usb_add_config_only()将本份配置添加到设备配置列表。

file: drivers/usb/gadget/composite.c

linux usb gadget driver代码_第84张图片

709-710行,设置configs子目录创建的次级目录(c.1)的attr为gadget_config_type。

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第85张图片

从上面的代码可以看处,次级目录c.1有attr文件2个: MaxPower和bmAttributes.

712-714行,设置configs子目录的次级目录(c.1)的default group,即configs子目录的次级目录(c.1)的子目录(strings),以及设置它的attr为gadget_config_name_strings_type.

gadget_config_name_strings_type的定义和第五步的gadget_strings_strings_type一样的定义:

file: drivers/usb/gadget/configfs.c

file: include/linux/usb/gadget_configfs.h

linux usb gadget driver代码_第86张图片

linux usb gadget driver代码_第87张图片

linux usb gadget driver代码_第88张图片

97行定义看出gadget_config_name_strings_type是没有ct_attrs[]的,即cdrom/configs/c.1/strings目录刚被创建出来是没有attr文件的。但是可以通过mkdir来创建。创建时,是通过函数gadget_config_name_strings_make()来创建的。

所以,这一步” mkdir configs/c.1”,是在configs目录下,创建了子目录c.1,以及该子目录的attr文件:MaxPower和bmAttributes,并在该子目录c.1下创建了次级目录strings。

第十步:cd /sys/kernel/config/usb_gadget/cdrom && mkdir configs/c.1/strings/0x409

根据第九步说的,这里是在configs/c.1/strings下创建子目录0x409, 调用的是gadget_config_name_strings_make().

创建的这个子目录的attr为gadget_config_name_langid_type. (64-65行)

gadget_config_name_langid_type的定义:

file: drivers/usb/gadget/configfs.c

file: include/linux/usb/gadget_configfs.h

linux usb gadget driver代码_第89张图片

42行显示gadget_config_name_langid_type有ct_attrs,为gadget_config_name_langid_attrs.

这个gadget_config_name_langid_attrs定义如下:

file: drivers/usb/gadget/configfs.c

可以看出,这个目录下有一个attr文件configuration.

第十一步:cd /sys/kernel/config/usb_gadget/cdrom && echo Virtual Cdrom > configs/c.1/strings/0x409/configuration

这一步,就是调用gadget_config_name_configuration_store(),将数据写入到gadget_config_name-> configuration.

linux usb gadget driver代码_第90张图片

file: drivers/usb/gadget/configfs.c

file: include/linux/usb/gadget_configfs.h

linux usb gadget driver代码_第91张图片

第十二步:cd /sys/kernel/config/usb_gadget/cdrom && mkdir functions/mass_storage.usb0

在创建cdrom目录的时候,functions子目录作为cdrom group的default group也会被创建。

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第92张图片

functions子目录的attr为functions_type,它没有ct_attrs[],故子目录functions刚被创建的时候,是没有attr文件的。但它提供了mkdir的操作function_make()。故,mkdir functions/mass_storage.usb0,就是调用了function_make().

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第93张图片

linux usb gadget driver代码_第94张图片

590行,调用usb_get_function_instance(“mass_storage”)查找“mass_storage”的驱动(function driver).

612行,将function instance添加到gi->available_func中。

我们来看看590行说到的函数usb_get_function_instance()。

file: drivers/usb/gadget/functions.c

linux usb gadget driver代码_第95张图片

linux usb gadget driver代码_第96张图片

usb_get_function_instance () -> try_get_usb_function_instance() 从func_list中,查找”mass_storage”驱动。找到驱动后,调用它的alloc_inst()函数去创建instance, 并返回这个instance.

那这个驱动在哪? 先给结论,这个驱动就是drivers/usb/gadget/function/f_mass_storage.c。

file: include/linux/usb/composite.h

linux usb gadget driver代码_第97张图片

file: drivers/usb/gadget/function/f_mass_storage.c

结合上面的代码,可以看出,f_mass_storage.c驱动加载的时候,调用了mass_storage_mod_init() -> usb_function_register(&mass_storageusb_func)

这里usb_function_register()就将驱动注册到func_list上了。

file: drivers/usb/gadget/functions.c

linux usb gadget driver代码_第98张图片

所以,usb_get_function_instance () -> try_get_usb_function_instance() -> fd->alloc_inst()就是drivers/usb/gadget/function/f_mass_storage.c驱动文件4300行的fsg_alloc_inst().

我们来看这个函数fsg_alloc_inst().

file: drivers/usb/gadget/function/f_mass_storage.c

linux usb gadget driver代码_第99张图片

linux usb gadget driver代码_第100张图片

linux usb gadget driver代码_第101张图片

4115-4129行,分配fsg_opts内存,并初始化。

4130-4134行,分配fsg_common内存,并初始化。

4145行,调用fsg_common_create_lun()时,由于common->sysfs为0,故此时并不创建lun.0设备文件。

4157行,注册了主次设备号,次设备号数目为MSG_MINORS。

4175行,设置mass_storage.usb0的attr为fsg_func_type, 定义如下:

file: drivers/usb/gadget/function/f_mass_storage.c

linux usb gadget driver代码_第102张图片

即functions/mass_storage.usb0/目录下有attr文件stall.

并且functions/mass_storage.usb0目录提供了mkdir的操作,即fsg_lun_make().

4177-4178行,在functions/mass_storage.usb0目录下,创建子目录lun.0, 并设置attr为fsg_lun_type.

file: drivers/usb/gadget/function/f_mass_storage.c

linux usb gadget driver代码_第103张图片

从上面的代码可以看出, functions/mass_storage.usb0/lun.0/目录下的attr文件有file, ro, removable, cdrom, nofua, inquiry_string.

第十三步: cd /sys/kernel/config/usb_gadget/cdrom && mkdir functions/mass_storage.usb0/lun.1 (如果是lun.0,则跳过这一步)

根据第十二步的分析,这里mkdir lun.1就是调用fsg_lun_make().

file: drivers/usb/gadget/function/f_mass_storage.c

linux usb gadget driver代码_第104张图片

3946行,分配lun内存,并占用common->lun[1] = lun, 和lun.0一样,此时common->sysfs为0, 并步创建lun.1设备文件。

3956行,设置functions/mass_storage.usb0/lun.1/的attr, 并创建对应的attr文件,上一步中已经说过,这里不再赘述。

第十四步: cd /sys/kernel/config/usb_gadget/cdrom && echo 1 > functions/mass_storage.usb0/lun.0/removable

根据第十二步的分析,这里就是调用fsg_lun_opts_removable_store(),将lun->removable设置为1.

file: drivers/usb/gadget/function/f_mass_storage.c

第十五步: cd /sys/kernel/config/usb_gadget/cdrom && echo 1 > functions/mass_storage.usb0/lun.0/cdrom

根据第十二步的分析,这里就是调用fsg_lun_opts_cdrom_store(),将lun->cdrom设置为1, 并设置lun->ro为1.

file: drivers/usb/gadget/function/f_mass_storage.c

linux usb gadget driver代码_第105张图片

第十六步: cd /sys/kernel/config/usb_gadget/cdrom && echo 1 > functions/mass_storage.usb0/lun.0/ro

根据第十二步的分析,这里就是调用fsg_lun_opts_ro_store(),将lun->ro设置为1.

file: drivers/usb/gadget/function/f_mass_storage.c

linux usb gadget driver代码_第106张图片

第十七步: cd /sys/kernel/config/usb_gadget/cdrom && echo 0 > functions/mass_storage.usb0/lun.0/nofua

根据第十二步的分析,这里就是调用fsg_lun_opts_nofua_store(),将lun->nofua设置为0.

file: drivers/usb/gadget/function/f_mass_storage.c

第十八步: echo 'AMI     Virtual CDROM0  1.00' > /sys/kernel/config/usb_gadget/cdrom/functions/mass_storage.usb0/lun.0/inquiry_string

根据第十二步的分析,这里就是调用fsg_lun_opts_inquiry_string_store(),将lun-> inquiry_string设置为'AMI     Virtual CDROM0  1.00'.

file: drivers/usb/gadget/function/f_mass_storage.c

第十九步: cd /sys/kernel/config/usb_gadget/cdrom && ln -s functions/mass_storage.usb0 configs/c.1

建立configs/c.1到functions/mass_storage.usb0的软链接,这个操作是在configs/c.1目录下完成的,那我们来看这个Link操作做了什么。

file: drivers/usb/gadget/configfs.c

从上面的代码可以看到,这里ln的操作,就是调用config_usb_cfg_link().

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第107张图片

linux usb gadget driver代码_第108张图片

linux usb gadget driver代码_第109张图片

421行,这里从gi->available_func拿到的是前面’mkdir functions/mass_storage.usb0’时f_mass_storage.c创建的usb_function_instance.

437行,调用usb_get_function()。

file: drivers/usb/gadget/functions.c

linux usb gadget driver代码_第110张图片

这里的fi->fd就是f_mass_storage.c中定义的,在之前mkdir functions/mass_storage.usb0的时候,调用try_get_usb_function_instance()就指向的。

file: drivers/usb/gadget/functions.c

linux usb gadget driver代码_第111张图片

所以,28行这里fd->alloc_inst()就是fsg_alloc().

file: include/linux/usb/composite.h

linux usb gadget driver代码_第112张图片

file: drivers/usb/gadget/function/f_mass_storage.c

我们来看一下这个fsg_alloc().

file: drivers/usb/gadget/functions.c

linux usb gadget driver代码_第113张图片

linux usb gadget driver代码_第114张图片

linux usb gadget driver代码_第115张图片

linux usb gadget driver代码_第116张图片

fsg_alloc()主要是创建fsg_dev,注册字符设备fsg->cdev, 字符设备号major在alloc_inst中就申请好了, 4259-4274还创建了’usbcd’,’usbhd’或’usbport_b_hddisk’设备文件。最后返回创建的usb_function.

444行(config_usb_cfg_link()),将437行创建的usb_function放到cfg->func_list中。

第二十步: echo ‘/dev/nbd0’ > /sys/kernel/config/usb_gadget/cdrom/functions/mass_storage.usb0/lun.0/file

根据第十二步的分析,这里就是调用fsg_lun_opts_file_store()

file: drivers/usb/gadget/function/f_mass_storage.c

linux usb gadget driver代码_第117张图片

fsg_lun_opts_file_store() -> fsg_store_file() 打开文件’/dev/nbd0’,并将文件句柄写到lun->filp.

在这一步时,fsg_opts->common->fsg为NULL,故3782行返回了。

第二十一步: cd /sys/kernel/config/usb_gadget/cdrom && echo 1e6a0000.usb-vhub:p1 >UDC

前面介绍过,这个echo操作就是调用了gadget_dev_desc_UDC_store().

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第118张图片

我们来看看这个gadget_dev_desc_UDC_store()做了什么。

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第119张图片

317行,将gadeget_driver.udc_name设置为‘1e6a0000.usb-vhub:p1’,然后调用usb_gadget_probe_driver().

file: drivers/usb/gadget/udc/core.c

linux usb gadget driver代码_第120张图片

linux usb gadget driver代码_第121张图片

1486-1496行,遍历udc_list,找出name是‘1e6a0000.usb-vhub:p1’的usb_udc设备。找到后,跳到1515行,调用udc_bind_to_driver().

这里有一个问题,设备‘1e6a0000.usb-vhub:p1’存在吗?

答案是设备‘1e6a0000.usb-vhub:p1’存在,它就是vhub的port 1,在vhub的驱动模块中注册的:

file: drivers/usb/gadget/udc/aspeed_vhub/core.c

ast_vhub_probe() -> ast_vhub_init_dev() -> usb_add_gadget_udc() -> usb_add_gadget_udc_release().

file: drivers/usb/gadget/udc/aspeed_vhub/dev.c

linux usb gadget driver代码_第122张图片

file: drivers/usb/gadget/udc/aspeed_vhub/core.c

linux usb gadget driver代码_第123张图片

好了,我们回到usb_gadget_probe_driver(). usb_gadget_probe_driver()找到udc device后,调用udc_bind_to_driver().

这个udc_bind_to_driver()又在做什么?

file: drivers/usb/gadget/udc/aspeed_vhub/core.c

linux usb gadget driver代码_第124张图片

1454行,先调用driver的bind()函数。这个driver是在创建cdrom的时候就分配了。

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第125张图片

linux usb gadget driver代码_第126张图片

所以, driver->bind()就是configfs_composite_bind().

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第127张图片

configfs_composite_bind() 调用composite_dev_prepare()->usb_ep_alloc_request() -> ep0->ops->alloc_request(),

linux usb gadget driver代码_第128张图片

ep0->ops->alloc_request()就是ast_vhub_alloc_request(), 就是申请usb_request内存,之后设置数据操作完成函数composite_setup_complete().

file: drivers/usb/gadget/udc/aspeed_vhub/ep0.c

linux usb gadget driver代码_第129张图片

linux usb gadget driver代码_第130张图片

之后, 1533-1535行,configfs_composite_bind()调用usb_add_function()。

file: drivers/usb/gadget/composite.c

linux usb gadget driver代码_第131张图片

usb_add_function()在323行调用function->bind(),这里就是前面ln操作时创建的usb_function中给出个fsg_bind()函数。

file: drivers/usb/gadget/functions/f_mass_storage.c fsg_alloc()

linux usb gadget driver代码_第132张图片

我们接着看fsg_bind().

file: drivers/usb/gadget/functions/f_mass_storage.c

linux usb gadget driver代码_第133张图片

linux usb gadget driver代码_第134张图片

fsg_bind()主要是创建了内核线程fsg_main_thread(),这个内核线程用于处理从usb总线上拿到的属于本port的包请求,或者向usb总线发送数据。

然后, configfs_composite_bind()调用usb_gadget_vhub_upstream_enalbe() -> gadget->ops->udc_upstream_connect()。

file: drivers/usb/gadget/udc/core.c

linux usb gadget driver代码_第135张图片

这里的gadget->ops->udc_upstream_connect()就是ast_vhub_udc_upstream_connect().

file: drivers/usb/gadget/udc/aspeed_vhub/dev.c

linux usb gadget driver代码_第136张图片

ast_vhub_upstream_connect()就是配置vhub一系列寄存器,比如reset vhub,enable interrupt,设置ep0 dma buf, enable upstream port connection等。

file: drivers/usb/gadget/udc/aspeed_vhub/hub.c

linux usb gadget driver代码_第137张图片

linux usb gadget driver代码_第138张图片

以上这些就是udc_bind_to_driver()调用driver->bind()做的事情,总的来说,就是配置vhub的寄存器。

回到udc_bind_to_driver()函数,继续看后续的操作。

1457行,调用usb_gadget_udc_start() -> udc->gadget->ops->udc_start(). 这里udc->gadget->ops->udc_start()就是ast_vhub_udc_start().

file: drivers/usb/gadget/udc/aspeed_vhub/dev.c

linux usb gadget driver代码_第139张图片

linux usb gadget driver代码_第140张图片

这个ast_vhub_udc_start,就是将d->driver关联到usb_gadget_driver,即configfs_driver_template.

1462行,调用usb_udc_connect_control() -> usb_gadget_connect() -> gadget->ops->pullup(gadget, 1), 这个gadget->ops->pullup()就是ast_vhub_udc_pull_up().

file: drivers/usb/gadget/udc/aspeed_vhub/dev.c

linux usb gadget driver代码_第141张图片

linux usb gadget driver代码_第142张图片

ast_vhub_udc_pull_up()调用ast_vhub_device_connect()将当前vhub的port设置为connection状态。

好了,第二十一步就这些了。到了这里,我们知道vhub upstream port被reset了,port 1也被配置了。接下去就是usb协议相关的了。

我们假设vhub port 1 reset后,开始了配置。

file: drivers/usb/gadget/udc/aspeed-vhub/core.c

linux usb gadget driver代码_第143张图片

linux usb gadget driver代码_第144张图片

vhub发生中断请求后,ast_vhub_irq() 这个中断处理函数会被调用。如果中断是vhub port的中断,则调用ast_vhub_dev_irq().

file: drivers/usb/gadget/udc/aspeed-vhub/dev.c

linux usb gadget driver代码_第145张图片

vhub port reset后,上位肯定是要先配置该usb的。所以,ast_vhub_dev_irq() -> ast_vhub_ep0_handle_setup()来处理setup请求。

file: drivers/usb/gadget/udc/aspeed-vhub/ep0.c

linux usb gadget driver代码_第146张图片

linux usb gadget driver代码_第147张图片

vhub port的ep0->dev指向port本身,故这里会调用138行的ast_vhub_std_dev_request().

file: drivers/usb/gadget/udc/aspeed-vhub/dev.c

linux usb gadget driver代码_第148张图片

ast_vhub_std_dev_request()用来处理具体的请求,比如set address,get status等。

ast_vhub_ep0_handle_setup()在调用ast_vhub_std_dev_request()后,如果ast_vhub_std_dev_request()有不能处理的请求,返回std_req_driver, 则ast_vhub_ep0_handle_setup()调用ep->dev->driver->setup()处理。那么这里的ep->dev->driver是什么?

由于当前是port的中断,所以ep->dev是vhub的ast_vhub_dev, ep->dev->driver的设置在’echo 0e6a0000:p1 > UDC’的时候,usb_gadget_probe_driver() -> udc_bind_to_driver() -> usb_gadget_udc_start() -> udc->gadget->ops->udc_start(), 这个udc->gadget->ops->udc_start()为ast_vhub_udc_start(),在ast_vhub_udc_start ()中,将ep->dev->driver设置为configfs_driver_template.

file: drivers/usb/gadget/udc/aspeed-vhub/dev.c

linux usb gadget driver代码_第149张图片

所以,这里的ep->dev->driver->setup()就是configfs_composite_setup()

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第150张图片

configfs_composite_setup() -> composite_setup()

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第151张图片

linux usb gadget driver代码_第152张图片

linux usb gadget driver代码_第153张图片

linux usb gadget driver代码_第154张图片

composite_setup()可以处理更多的usb请求,比如get descriptor等。然后通过composite_ep0_queue()将请求的数据发送出去。

file: drivers/usb/gadget/configfs.c

linux usb gadget driver代码_第155张图片

file: drivers/usb/gadget/udc/core.c

linux usb gadget driver代码_第156张图片

这里的ep->ops->queue()为ast_vhub_ep0_queue()

file: drivers/usb/gadget/udc/aspeed-vhub/ep0.c

linux usb gadget driver代码_第157张图片

linux usb gadget driver代码_第158张图片

linux usb gadget driver代码_第159张图片

linux usb gadget driver代码_第160张图片

ast_vhub_ep0_queue()最后调用ast_vhub_ep0_send()将请求的数据放到dma,并告诉硬件,将数据发送走。

usb协议相关的其他操作就不具体介绍了,我们回到f_mass_storage.c中来看那个被创建的内核线程fsg_main_thread().

file: drivers/usb/gadget/functions/f_mass_storage.c

linux usb gadget driver代码_第161张图片

2707行,get_next_command()被调用,用于获取host那边过来的请求命令,然后2709行来处理的具体的请求,比如read, write, verify等。

我们来看get_next_command().

linux usb gadget driver代码_第162张图片

linux usb gadget driver代码_第163张图片

linux usb gadget driver代码_第164张图片

handle_exception()中的2563行common->fsg是在do_set_interface的时候设置的,具体的过程为: ast_vhub_irq() -> ast_vhub_dev_irq() -> ast_vhub_ep0_handle_setup() -> ep->dev->driver->setup(), 这个ep->dev->driver->setup()为configfs_composite_setup(),

configfs_composite_setup() -> composite_setup() -> f->set_alt(), 这里的f->set_alt()为fsg_set_alt().

fsg_set_alt() -> __raise_exception(FSG_STATE_CONFIG_CHANGE), __raise_exception()将common->state设置为FSG_STATE_CONFIG_CHANGE. 然后,fsg_main_thread() -> handle_exception() -> do_set_interface(),do_set_interface()就会将当前的fsg_dev赋值为common->fsg.

linux usb gadget driver代码_第165张图片

linux usb gadget driver代码_第166张图片

get_next_command()的2380行的common->next_buffhd_to_fill对应的状态是BUF_STATE_EMPTY, 所以2381行,不会sleep,会马上返回。

linux usb gadget driver代码_第167张图片

2386-2387行,是为了获取HOST传过来的command而给出usb_request内存空间。我们来看这个start_out_transfer().

linux usb gadget driver代码_第168张图片

655行,这里的common->fsg->bulk_out来自哪里?这个是在fsg_bind()的时候构建的。

linux usb gadget driver代码_第169张图片

file: drivers/usb/gadget/epautoconf.c

linux usb gadget driver代码_第170张图片

linux usb gadget driver代码_第171张图片

fsg_bind() -> usb_ep_autoconfig() -> usb_ep_autoconfig_ss() -> gadget->ops->match_ep(), 这个gadget->ops->match_ep()为ast_vhub_udc_match_ep().

file: drivers/usb/gadget/udc/aspeed-vhub/dev.c

linux usb gadget driver代码_第172张图片

linux usb gadget driver代码_第173张图片

linux usb gadget driver代码_第174张图片

file: drivers/usb/gadget/udc/aspeed-vhub/epn.c

linux usb gadget driver代码_第175张图片

linux usb gadget driver代码_第176张图片

现在我们知道了common->fsg->bulk_out的由来,后面的enqueue, dequeue就知道是什么函数了。

好了,我们回到start_out_transfer().

知道了common->fsg->bulk_out,还需要知道bh->outreq, 这个bh->outreq是在do_set_interface()的时候配置的。

linux usb gadget driver代码_第177张图片

linux usb gadget driver代码_第178张图片

file: drivers/usb/gadget/udc/core.c

linux usb gadget driver代码_第179张图片

do_set_interface() -> alloc_request() -> ep->ops->alloc_request(), 这个ep->ops->alloc_request()就是ast_vhub_alloc_request().

file: drivers/usb/gadget/udc/aspeed-vhub/epn.c

linux usb gadget driver代码_第180张图片

file: drivers/usb/gadget/udc/aspeed-vhub/core.c

linux usb gadget driver代码_第181张图片

好了,我们知道了bh->outreq和bh->inreq的来源,且知道了他们的complete函数分别为bulk_out_complete()和bulk_in_complete().

好了,我们回到start_out_transfer()函数,继续…….

start_out_transfer() -> start_transfer()

linux usb gadget driver代码_第182张图片

linux usb gadget driver代码_第183张图片

start_transfer() -> usb_ep_queue() -> ep->ops_queue(), ep->ops_queue()就是ast_vhub_epn_queue().

file: drivers/usb/gadget/udc/aspeed-vhub/epn.c

linux usb gadget driver代码_第184张图片

linux usb gadget driver代码_第185张图片

linux usb gadget driver代码_第186张图片

ast_vhub_epn_queue()调用usb_gadget_map_request()将req->buf的物理地址用作dma地址,然后调用ast_vhub_epn_kick() //我们使用的是single dma 模式,ep->epn.desc_mode表示多dma模式。

linux usb gadget driver代码_第187张图片

ast_vhub_epn_kick()就是将dma地址提交,方便来数据的时候,数据直接dma到这里。

提交outreq后,由于start_out_transfer()设置了bh->state为BUF_STATE_RECEIVING,

linux usb gadget driver代码_第188张图片

当get_next_command()从start_out_transfer()返回后,调用sleep_thread()会block住,等待HOST发送请求过来。

linux usb gadget driver代码_第189张图片

当有数据到来时,中断处理函数ast_vhub_irq()被调用。

file: drivers/usb/gadget/udc/aspeed-vhub/core.c

linux usb gadget driver代码_第190张图片

file: drivers/usb/gadget/udc/aspeed-vhub/epn.c

linux usb gadget driver代码_第191张图片

linux usb gadget driver代码_第192张图片

file: drivers/usb/gadget/udc/aspeed-vhub/core.c

linux usb gadget driver代码_第193张图片

linux usb gadget driver代码_第194张图片

中断发生时,通过ast_vhub_irq() -> ast_vhub_epn_ack_irq() -> ast_vhub_epn_handle_ack() -> ast_vhub_done() -> usb_gadget_giveback_request(),将数据放到req->buf中,收完后,调用req->complete()来结束,这个req->complete()就是bulk_out_complete和bulk_in_complete.

file: drivers/usb/gadget/functions/f_mass_storage.c

linux usb gadget driver代码_第195张图片

bulk_out_complete()会将fsg_main_thread()线程唤醒。

这样,fsg_main_thread()就可以从2392行的sleep_thread()返回了,进入received_cbw()。

linux usb gadget driver代码_第196张图片

linux usb gadget driver代码_第197张图片

linux usb gadget driver代码_第198张图片

received_cbw()主要是拿到请求数据,解析对应LUN index, 返回后,fsg_main_thread()调用do_scsi_command()处理。

对于read请求,do_scsi_command() -> do_read()

do_read()从’/dev/nbd’读取数据,然后通过inreq,将数据发送给HOST.

linux usb gadget driver代码_第199张图片

linux usb gadget driver代码_第200张图片

好了,基本的过程都描述过了,这里最后再补充一点,f_mass_storage中的IN, OUT都是从HOST角度来看的,即IN表示BMC向HOST发数据,OUT表示HOST向BMC发数据。

好了,usb gadget就介绍到这了。

你可能感兴趣的:(linux,驱动开发,单片机)