linux usb gadget driver代码

news2024/10/6 8:31:21

本文基于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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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个).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

file: fs/configfs/mount.c

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

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

当应用程序用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.

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

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

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

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

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

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

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

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

file: drivers/usb/gadget/configfs.c

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

file: fs/configfs/dir.c

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

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

即dentry->d_op为configfs_dentry_ops.

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

file: fs/configfs/dir.c

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

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

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

file: drivers/usb/gadget/configfs.c

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。

1743-1750行,配置gadget_driver为configfs_driver_template.

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

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

file: fs/configfs/file.c

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

file: drivers/usb/gadget/configfs.c

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

file: include/linux/configfs.h

当我们要写这些文件时,先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

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

file: fs/configfs/file.c

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

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

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

关于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

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

好了,第二步“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

file: drivers/usb/gadget/configfs.c

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

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

file: drivers/usb/gadget/configfs.c

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

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

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

file: include/linux/usb/ch9.h

第四步: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

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()的定义:

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

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

file: include/linux/usb/gadget_configfs.h

file: drivers/usb/gadget/configfs.c

从上面定义可以看处,共定义了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

第七步: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

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

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

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

file: drivers/usb/gadget/composite.c

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

file: drivers/usb/gadget/configfs.c

从上面的代码可以看处,次级目录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

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

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.

file: drivers/usb/gadget/configfs.c

file: include/linux/usb/gadget_configfs.h

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

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

file: drivers/usb/gadget/configfs.c

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

file: drivers/usb/gadget/configfs.c

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

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

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

所以,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

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

即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

从上面的代码可以看出, 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

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

第十六步: 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

第十七步: 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

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

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

file: drivers/usb/gadget/functions.c

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

file: include/linux/usb/composite.h

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

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

file: drivers/usb/gadget/functions.c

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

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

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

file: drivers/usb/gadget/configfs.c

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

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

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

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

好了,我们回到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

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

file: drivers/usb/gadget/configfs.c

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

file: drivers/usb/gadget/configfs.c

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

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

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

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

file: drivers/usb/gadget/composite.c

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

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

我们接着看fsg_bind().

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

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

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

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

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

以上这些就是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

这个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

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

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

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

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

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

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

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

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

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

file: drivers/usb/gadget/configfs.c

configfs_composite_setup() -> composite_setup()

file: drivers/usb/gadget/configfs.c

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

file: drivers/usb/gadget/configfs.c

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

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

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

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

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

我们来看get_next_command().

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.

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

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

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

file: drivers/usb/gadget/epautoconf.c

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

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

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

好了,我们回到start_out_transfer().

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

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

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

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

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

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

start_out_transfer() -> start_transfer()

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

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

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

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

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

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

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

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

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

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

中断发生时,通过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

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

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

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/495482.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何通过代码接入手机在网状态 API

引言 在许多场景下,手机号码是一种常用的身份验证信息。而使用手机在网状态 API 可以判断出手机号码是否有效,在一定程度上提高了身份验证的准确性和安全性,它的出现和广泛应用,为各行各业提供了更为便利和高效的解决方案。 本文…

城市夜景照明对于安科瑞智能照明系统的运用

安科瑞 徐浩竣 江苏安科瑞电器制造有限公司 zx acrelxhj 摘要:文章以智能照明控制系统为切入点,介绍了智能照明控制系统在城市夜景照明工程中的应用价值,并结合具体案例分析了城市夜景照明控制管理平台的设计和具体应用。智能照明控制系统…

微软Bing突然爆炸级更新!无需等待人人可用,答案图文并茂

所有人都能上手微软Bing了! 今天,微软突然官宣全面开放BingChat: 无需任何等待。只需注册一个账户,首页即可体验。 更关键的是,还有一大堆堪称“家底”的新功能来袭! 支持100种语言多模态输出、持续聊天…

控制您的 AWS VPC 终端节点

随着 Amazon Web Services (AWS) 越来越受欢迎,现在控制您自己的 AWS 虚拟私有云 (VPC) 终端节点比以往任何时候都更加重要。这可以通过配置和管理 VPC 终端节点中的不同设置来完成。在本文中,我们将讨论为…

yolo目标检测2:yolov1整体思想和网络架构

上一节:https://blog.csdn.net/weixin_39107270/article/details/130408407 概念 You only look once 把检测问题转化成回归问题,一个CNN就搞定了。 2. 核心思想 每个点处有2种候选框,如果候选框内有目标,对候选框进行微调&am…

初学Verilog语言基础笔记整理(实例点灯代码分析)持续更新~

实例:点灯学习 一、Verilog语法学习 1. 参考文章 刚接触Verilog,作为一个硬件小白,只能尝试着去理解,文章未完…持续更新。 参考博客文章: Verilog语言入门学习(1)Verilog语法【Verilog】一文…

NoSQL自述---衍生过程

NoSQL概述 一.数据存储的演化史 1.单机MySQL的美好年代 在90年代,一个网站的访问量一般都不大,用单个数据库完全可以轻松应付。在那个时候,更多的都是静态网页,动态交互类型的网站不多。 上述架构下,我们来看看数据…

计算机毕业论文内容参考|软件工程|基于java开发汽车销售系统资料

文章目录 导文资料1简述模块与功能总结资料二摘要前言绪论课题背景国内外现状与趋势相关技术与方法介绍导文 如下是一个 Java 实现的汽车销售系统的简单描述,供参考。 计算机毕业论文内容参考|软件工程|基于java开发汽车销售系统资料 资料1 简述 汽车销售系统是专门为汽车销…

jetcache:阿里这款多级缓存框架一定要掌握

0. 引言 之前我们讲解了本地缓存ehcache组件,在实际应用中,并不是单一的使用本地缓存或者redis,更多是组合使用来满足不同的业务场景,于是如何优雅的组合本地缓存和远程缓存就成了我们要研究的问题,而这一点&#xff…

前端002_初始化项目

1、命名和启动项目 将目录名 vue-admin-template-master 重命名为 db-manager-system 将 db-manager-system/package.json 中的 name 值改为 db-manager-system {"name": "db-manager-system","version": "1.0.1","descriptio…

车载搭载SystemUI音频技术,全方位呈现高品质音效

SystemUI概述 SystemUI 是 Android 操作系统中的一个系统服务,主要负责管理和显示系统界面元素,例如状态栏、通知栏、键盘和屏幕截图等。SystemUI 系统服务是系统级别的组件,提供的功能对于用户体验和系统安全性都非常重要。 SystemUI 的主…

KNN中不同距离度量对比和介绍

k近邻算法KNN是一种简单而强大的算法,可用于分类和回归任务。他实现简单,主要依赖不同的距离度量来判断向量间的区别,但是有很多距离度量可以使用,所以本文演示了KNN与三种不同距离度量(Euclidean、Minkowski和Manhattan)的使用。…

Three——四、几何体、高光网络材质、锯齿模糊以及GUI库的使用

文章: Three.js——一、初识Three以及基础的前端场景搭建(结尾含源码)Three——二、加强对三维空间的认识Three——三、动画执行、画布大小、渲染帧率和相机适配体验Three——四、几何体、高光网络材质、锯齿模糊以及GUI库的使用 Threejs 常见几何体简介 Three.j…

为什么我们要使用向量化运算

问题背景 如果你是matlab用户,你一般都会使用向量化运算进行编程。原因也许很简单,因为matlab针对向量化运算在底层做了深度优化,尤其是针对矩阵乘法调用了MKL之类的高度优化的第三库来加速。所以我们在推演算法的阶段,尽量的以向…

管理后台项目-08-首页使用echarts展示图表数据

目录 1-需求页面 2-头部需求分析 3-中间部分需求分析 3.1-mock数据 3.2-动态渲染数据 4-底部需求分析 1-需求页面 2-头部需求分析 上面头部有四个card,card分为上中下三部分,其中上都是文字描述和一个小图标;中间部分有文字,折…

达梦数据库管理系统 DM8

检查数据库版本及服务状态 //查看达梦数据库运行状态 SELECT status$ as 状态 FROM v$instance; //查看达梦数据库版本 SELECT banner as 版本信息 FROM v$version;创建用户 //创建用户 CREATE USER DM IDENTIFIED BY "dameng123";授予用户基本权限 使用 GRANT 语…

重大问题,Windows11出现重大BUG(开始菜单掉帧,卡顿)

重大问题,Windows11出现重大BUG 这种Windows11操作系统出现BUG已经可以说是非常常见的,但是,今天我将代表所有微软用户,解决一个关于UI设计非常不舒服的功能 关闭多平面覆盖 事情叙述问题 微软社区解决方案自己发现的解决方案解决…

[链表OJ题 2] 链表的中间结点 -- 快慢指针找链表的中间节点

目录 题目来源: 代码实现 思路分析: 1.当链表个数为奇数 2.当链表个数为偶数 总结: 题目来源: 876. 链表的中间结点 - 力扣(LeetCode) (leetcode-cn.com) 题目描述: 代码实现 struct Li…

Dart中的factory关键字用法

factory简介 在Dart中,factory关键字用于定义工厂构造函数。它与普通的构造函数有以下几个区别: factory构造函数的调用可以返回子类型或其它类型的实例。普通构造函数总是返回其包含的类型的实例。factory构造函数可以有返回值。普通构造函数的返回值永远是其包含的类型的实…

服务器中了勒索病毒,malox勒索病毒的加密方式及如何应对勒索病毒攻击

随着计算机技术的发展,计算机成为现代人工作和生活中必不可少的电子产品。但随着很多企业和个人用户的信息化建设不断升级,也经常会出现许多恶意软件。其中包括malox勒索病毒,malox勒索病毒是mallox勒索病毒新升级的加密程序,下面…