linux usb gadget driver代码

2023-05-16

本文基于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就介绍到这了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux usb gadget driver代码 的相关文章

  • pprof 和 ps 之间的内存使用差异

    我一直在尝试分析用 cobra 构建的 cli 工具的堆使用情况 这pprof工具显示如下 Flat Flat Sum Cum Cum Name Inlined 1 58GB 49 98 49 98 1 58GB 49 98 os Read
  • termios 库中如何表示标志?

    我是 C 语言和驱动程序编程的新手 目前 我正在编写一个用户空间驱动程序 以便使用 Debian 通过 USB 与 RS232 进行通信 在研究时 我遇到了以下代码 tty c cflag PARENB No Parity tty c cf
  • 从c调用汇编函数

    我试图从 c 调用汇编函数 但我不断收到错误 text globl integrate type integrate function integrate push ebp mov esp ebp mov 0 edi start loop
  • 码头无故停止

    我需要经验丰富的码头用户的建议 我在负载均衡器 亚马逊云 后面维护着 2 台 Linux 机器 使用 Jetty 9 0 3 有时我的 Jetty 容器会被 Thread 2 无故关闭 同时地 显示以下日志并且容器无故停止 没有错误 没有例
  • 找出 Linux 上的默认语言

    有没有办法从C语言中找出Linux系统的默认语言 有 POSIX API 可以实现这个功能吗 例如 我想要一个人类可读格式的字符串 即德语系统上的 German 或 Deutsch 法语系统上的 French 或 Francais 等 有类
  • 从 Python 访问 802.11 无线管理帧

    我想从 Linux 上的 Python 嗅探 802 11 管理 探测请求 帧 这可以从 Scapy 中实现 如下所示 coding utf 8 from scapy all import def proc p if p haslayer
  • Fortran 中的共享库,最小示例不起作用

    我试图了解如何在 Linux 下的 Fortran 中动态创建和链接共享库 我有两个文件 第一个 liblol f90 看起来像这样 subroutine func print lol end subroutine func 我用它编译gf
  • 如何确定代码是否在信号处理程序上下文中运行?

    我刚刚发现有人正在从信号处理程序调用我编写的绝对不是异步信号安全的函数 所以 现在我很好奇 如何避免这种情况再次发生 我希望能够轻松确定我的代码是否在信号处理程序上下文中运行 语言是 C 但该解决方案不适用于任何语言吗 int myfunc
  • 在 Docker 容器中以主机用户身份运行

    在我的团队中 我们在进行开发时使用 Docker 容器在本地运行我们的网站应用程序 假设我正在开发 Flask 应用程序app py具有依赖关系requirements txt 工作流程大致如下 I am robin and I am in
  • 运行此处编译的 C 程序会导致在另一台服务器上找不到 GLIBC 库错误 - 是我的错还是他们的错?

    此处编译的 C 程序在我们的 Ubuntu 服务器上运行良好 但是当其他人尝试在他们的特定 Linux 服务器上运行它时 他们会收到以下错误 myprog install lib tls libc so 6 version GLIBC 2
  • 使用c在linux上分块读写

    我有一个 ASCII 文件 其中每一行都包含一个可变长度的记录 例如 Record 1 15 characters Record 2 200 characters Record 3 500 characters Record n X cha
  • 可以作为命令行参数传递多少数据?

    在 Linux 下生成进程时可以发送多少字节作为命令行参数 gahooa 推荐了一篇好文章http www in ulm de mascheck various argmax http www in ulm de mascheck vari
  • 如何在perl中使用O_ASYNC和fcntl?

    我想使用 O ASYNC 选项 当管道可以读取时 SIGIO 的处理程序将运行 但以下代码不起作用 任何人都可以帮助我吗 bin env perl use Fcntl SIG IO sub print catch SIGIO n my fl
  • 在非实时操作系统/内核上执行接近实时任务的最佳方法是什么?

    在一台 GNU Linux 机器上 如果想要执行 实时 亚毫秒级时间关键 任务 您几乎总是必须经历漫长 复杂且容易出现问题的内核补丁过程 以提供足够的支持 1 http en wikipedia org wiki RTLinux Backg
  • 提高mysql导入速度[关闭]

    Closed 这个问题是与编程或软件开发无关 help closed questions 目前不接受答案 我有一个很大的数据库22GB 我曾经用过进行备份mysqldumpgzip 格式的命令 当我提取 gz 文件时 它会生成 sql文件的
  • 用于时间线数据的类似 gnuplot 的程序

    我正在寻找一个类似 gnuplot用于在时间轴中绘制数据图表的程序 类似 gnuplot 在 Linux 上运行 命令行功能 GUI 对我帮助不大 可编写脚本的语法 输出为 jpg png svg 或 gif 输出应该是这样的 set5 s
  • 在嵌入式系统上将内核控制台发送到哪里?

    我正在开发一个嵌入式系统 该系统当前通过串行端口 1 上的控制台输出启动 Linux 使用启动加载程序中的控制台启动参数 然而 最终我们将使用这个串行端口 内核控制台输出的最佳解决方案是什么 dev null 能否以某种方式将其放在 pty
  • 如何回忆上一个 bash 命令的参数?

    Bash 有没有办法回忆上一个命令的参数 我通常这样做vi file c其次是gcc file c Bash 有没有办法回忆上一个命令的参数 您可以使用 or 调用上一个命令的最后一个参数 Also Alt can be used to r
  • 使用 --prof 选项创建多个日志文件而不是一个 v8.log 的节点

    我正在尝试使用 prof 选项来分析我的 Node 应用程序 但我发现不是一个单一的 v8 log 文件 而是使用诸如isolate 0x9582b40 v8 log isolate 0xa1cab78 v8 6049 等前缀创建的多个文件
  • 如何指定配置脚本的包含目录

    我的工作场所有一个 Linux 系统 其中包含相当旧的软件包 并且没有 root 访问权限 我正在从源代码编译我需要的包 prefix somewhere in homedir 我的问题是我只是不知道如何说服配置在特定目录中查找头文件 源码

随机推荐

  • STM32F407 内部自带FLASH 模拟 EEPROM

    STM32F407 内部自带FLASH 模拟 EEPROM 一 STM32F407自带FLASH STM32F4 本身没有自带 EEPROM xff0c 但是 STM32F4 具有 IAP xff08 在应用编程 xff09 功能 xff0
  • 国标GB/T 18384.1 绝缘检测算法 推导公式

    国标GB T 18384 1 绝缘检测算法 推导公式 国标中关于绝缘检测算法只有结果 xff0c 没有推导公式 xff0c 如下 xff1a 根据电流回路相等原则 xff0c 推导出公式1 xff1a V1 V1 为正常情况下电池正负极电压
  • LTC6820和isoSPI使用笔记

    一 LTC6820使用笔记 1 MSTR主控 受控 MSTR 引脚 11 引脚 12 xff1a 串行接口主 从选择器输入 位于隔离式接口的主控器侧 xff08 SPI主机 xff09 xff0c 引脚接 VDD 位于隔离式接口的受控器侧
  • ESP8266学习笔记1--硬件

    1 ESP12F模块 原理图 自制开发板 原理图
  • ESP8266学习笔记2--Arduino环境搭建

    1 安装Arduino IDE https www arduino cn thread 5838 1 1 html 2 安装esp8266扩展 https www arduino cn thread 76029 1 1 html 安装成功后
  • ESP8266学习笔记3-闪存文件系统

    3 3 1 闪存文件SPIFFS基本操作 程序来源 xff1a 太极创客http www taichi maker com homepage esp8266 nodemcu iot iot c spiffs spiffs operation
  • 同一寄存器不同位域赋值的两种方法

    当一个寄存器有不同位域时 xff0c 我们需要给不同位域赋值 如何赋值方便呢 xff1f 下面有两种方法 xff0c 总结一下 个人觉得位域写法更简洁 整体寄存器法 typedef struct StrNa uint32 t reg1 re
  • mavlink解析

    之前看了mavlink协议 xff0c 网上关于mavlink的资料不多 本系列共三篇 xff0c 这是第一篇 本文大概总结了下对mavlink协议的理解 以下如不说明都是说mavlink v1 0版本 首先附上mavlink的各个消息的简
  • Tomcat部署及优化

    目录 1 Tomcat概述 1 Tomcat的概念 2 Tomcat的核心组件 3 Java Servlet 的概念 4 JSP的概念 5 Tomcat中最顶层的容器 server 6 四个子容器的作用 7 Tomcat请求过程 2 Tom
  • STC89C52系列单片机内部资源——串口通信

    计算机通信是将计算机技术和通信技术的相结合 xff0c 完成计算机与外部设备或计算机与计算机之间的信息交换 可以分为两大类 xff1a 并行通信与串行通信 并行通信通常是将数据字节的各位用多条数据线同时进行传送 并行通信控制简单 传输速度快
  • 用TCP/UDP 网络调试助手(PC版)无法获取网页信息

    以前的网页均是http开头的 xff0c 是没有加密的 xff0c 以前用GET就能获取网页的信息 xff0c 但是现在的基本是https开头的 xff0c 是加密的 xff0c 所以现在用以前的方法 xff0c 只能返回301错误 现在想
  • Ubuntu 20.04安装Ros Noetic及Ubuntu 18.04安装ROS Melodic(两版本详细填坑)

    Ubuntu 20 04安装Ros Noetic及18 04安装ROS Melodic 表1 1 ROS的历史版本 1 设置安装源2 添加秘钥3 更新列表4 开始安装5 配置ROS环境变量6 安装rosinstall6 1 初始化核心组件r
  • linux 根文件系统,根设备,sys_open, sys_read, sys_write, sys_mount, sys_mknod

    笔者语 xff1a 1 内容涉及比较多 xff0c 自己也没有分章节 xff0c 因为觉得这些内容关联性很强 xff0c 自己也懒的去弄了 2 本文涉及以下内容 xff1a 2 1 内核启动过程中 xff0c 第一个文件系统为rootfs
  • uboot的配置(make xxx_config)和编译(make)工程解读

    uboot编译三步走 make xxx configmakemake install 第一步make xxx config 这一步是产生板子的配置文件 我们假设是配置ast2500evb板子 xff0c 那么这里的配置命令就是 make a
  • uboot启动之第一次运行C函数到uboot重定位

    接上一篇博文 uboot启动流程之上电启动到第一次准备好C语言运行环境 xff0c 本文从board init f 开始 board init f定义在uboot common board f c中 CONFIG SYS GENERIC B
  • pci总线扫描及pci网卡驱动

    本文讲述的基于intel 总线架构的硬件架构为例来说明linux是如何扫描总线上的PCI设备 CPU通过前端总线FS连接到北桥芯片North Bridge Chip 又称host Bridge 北桥芯片本身也是PCI总线0上的PCI设备 北
  • k8s下POD之间的通信过程

    本文主要描述同一个node之内的pod之间的通信 xff0c 以及不同node之间的pod之间的通信 同一个 node 上的不同 pod 之间的通信 xff1a 假设上图的POD A要和POD B 通信 POD A 发送一个包 xff0c
  • bash: ./<one_executable_file>: no such file or directory

    关于这个问题 xff0c 原因很多 但大部分的资料都是说PATH环境变量坏掉了 xff0c 或者 etc profile坏掉了 但其实 xff0c 还有一种原因 xff0c 就是你的可执行程序 lt one executable file
  • linux下动态库so的debug方式

    1 查看哪些进程使用了特定的动态库so lsof lib arm linux gnueabi libselinux so 1 COMMAND PID USER FD TYPE DEVICE SIZE OFF NODE NAME init 1
  • linux usb gadget driver代码

    本文基于linux 5 4 124 aspeed 2600 BMC 的代码实现来描述arm结构下的gadget driver 在读之前 xff0c 我们需要了解什么是usb gadget driver xff0c 以及它的作用 从英文字面上