简单分析configfs调用流程

参考资料:https://www.cnblogs.com/linhaostudy/p/17016238.html
https://blog.csdn.net/liujiliei/article/details/105276551
https://www.elecfans.com/d/1847582.html

1、configfs

configfs的文件主要分成三层:

  • configfs_subsystem (子系统)
  • configfs_grounp(组层)
  • config_item
struct gadget_info {
	struct config_group group;
	struct config_group functions_group;
	struct config_group configs_group;
	struct config_group strings_group;
	struct config_group os_desc_group;

	struct mutex lock;
	struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1];
	struct list_head string_list;
	struct list_head available_func;

	struct usb_composite_driver composite;
	struct usb_composite_dev cdev;
	bool use_os_desc;
	char b_vendor_code;
	char qw_sign[OS_STRING_QW_SIGN_LEN];
	spinlock_t spinlock;
	bool unbind;
};

struct config_group {
	struct config_item		cg_item;
	struct list_head		cg_children;
	struct configfs_subsystem 	*cg_subsys;
	struct list_head		default_groups;
	struct list_head		group_entry;
};

configfs.c和uvc_configfs.c联系起来

function_make  //configfs.c
	usb_get_function_instance(func_name)  //configfs.c
		try_get_usb_function_instance  //function.c
			usb_function_instance *uvc_alloc_inst(void)  //f_uvc,c
				ret = uvcg_attach_configfs(opts)  //f_uvc,c

//uvc_configfs.c
int uvcg_attach_configfs(struct f_uvc_opts *opts)
{
	int ret;
	config_group_init_type_name(&opts->func_inst.group, uvc_func_type.name,
				    &uvc_func_type.type);
	ret = uvcg_config_create_children(&opts->func_inst.group,
					  &uvc_func_type);
	if (ret < 0)
		config_group_put(&opts->func_inst.group);
	return ret;
}

ln 操作时会调用alloc_func:

config_group_init_type_name(&cfg->group, name,&gadget_config_type);
	config_usb_cfg_link
		usb_get_function(fi)
			fi->fd->alloc_func(fi)

2、Platform Layer

1、Platform Device

2、Platform Driver

//drivers/usb/dwc2/platform.c
static struct platform_driver dwc2_platform_driver = {
	.driver = {
		.name = dwc2_driver_name,
		.of_match_table = dwc2_of_match_table,
		.pm = &dwc2_dev_pm_ops,
	},
	.probe = dwc2_driver_probe,
	.remove = dwc2_driver_remove,
	.shutdown = dwc2_driver_shutdown,
};

module_platform_driver(dwc2_platform_driver);
//drivers/usb/dwc2/params.c:
const struct of_device_id dwc2_of_match_table[] = {
	{ .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params },
	{ .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params  },
	{ .compatible = "rockchip,rk3066-usb", .data = dwc2_set_rk_params },
	{ .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params },
	{ .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params },
	{ .compatible = "snps,dwc2" },
	{ .compatible = "samsung,s3c6400-hsotg",
	  .data = dwc2_set_s3c6400_params },
	{ .compatible = "amlogic,meson8-usb",
	  .data = dwc2_set_amlogic_params },
	{ .compatible = "amlogic,meson8b-usb",
	  .data = dwc2_set_amlogic_params },
	{ .compatible = "amlogic,meson-gxbb-usb",
	  .data = dwc2_set_amlogic_params },
	{ .compatible = "amlogic,meson-g12a-usb",
	  .data = dwc2_set_amlogic_g12a_params },
	{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
	{ .compatible = "st,stm32f4x9-fsotg",
	  .data = dwc2_set_stm32f4x9_fsotg_params },
	{ .compatible = "st,stm32f4x9-hsotg" },
	{ .compatible = "st,stm32f7-hsotg",
	  .data = dwc2_set_stm32f7_hsotg_params },
	{},
};
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);

该驱动的主要功能是创建和注册 Gadget Device,一个 UDC 对应一个 Gadget Device

//dwc2_driver_probe() → usb_add_gadget_udc() → usb_add_gadget_udc_release()
dwc2_driver_probe(struct platform_device *dev)   // drivers/usb/dwc2/platform.c
	usb_add_gadget_udc(hsotg->dev, &hsotg->gadget)   // gadget/udc/core.c
		usb_add_gadget_udc_release(parent, gadget, NULL)  // gadget/udc/core.c	

3、UDC/Gadget Layer

Gadget Layer 层把各式各样的 UDC 封装成标准的 Gadget Device,提供统一的向上接口。Gadget Driver 又把各式各样的 Function 和 Gadget Device 链接起来。

static struct platform_driver ambarella_udc_driver = {
	.driver		= {
		.name	= "ambarella-udc",
		.owner	= THIS_MODULE,
		.of_match_table	= ambarella_udc_dt_ids,
	},
	.remove		= ambarella_udc_remove,
#ifdef CONFIG_PM
	.suspend	= ambarella_udc_suspend,
	.resume		= ambarella_udc_resume,
#endif
};
module_platform_driver_probe(ambarella_udc_driver, ambarella_udc_probe);

ambarella_udc_probe(struct platform_device *pdev)
	 usb_add_gadget_udc_release
	 	check_pending_gadget_drivers(udc)
	 		udc_bind_to_driver(udc, driver)

1、Gadget Bus

Gadget Layer 层没有定义一个标准的 Bus 总线,而是自定义了两条链表来分别存储 Device 和 Driver
它们的使用场景如下:

  1. 在 Gadget Device 创建时,首先把 Device 加入到 udc_list 链表,然后尝试和 gadget_driver_pending_list 链表中的 Driver 进行 match()
//usb_add_gadget_udc() → usb_add_gadget_udc_release()→ check_pending_gadget_drivers
//→udc_bind_to_driver
usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
		void (*release)(struct device *dev))

/* should be called with udc_lock held */
static int check_pending_gadget_drivers(struct usb_udc *udc)
{
	struct usb_gadget_driver *driver;
	int ret = 0;

	list_for_each_entry(driver, &gadget_driver_pending_list, pending)
		if (!driver->udc_name || strcmp(driver->udc_name,
						dev_name(&udc->dev)) == 0) {
			ret = udc_bind_to_driver(udc, driver);
			if (ret != -EPROBE_DEFER)
				list_del_init(&driver->pending);
			break;
		}
	return ret;
}
  1. 在 Gadget Driver 创建时,首先尝试和 udc_list 链表中的 Device 进行 match(),match() 不成功则把 Driver 加入到 gadget_driver_pending_list 链表中
//gadget_dev_desc_UDC_store() → usb_gadget_probe_driver()
gadget_dev_desc_UDC_store   // gadget/configfs.c
	usb_gadget_probe_driver(&gi->composite.gadget_driver)	// gadget/udc/core.c
		udc_bind_to_driver(udc, driver)  // gadget/udc/core.c
			
  1. 在 Device 和 Driver Match 成功时的 bind() 动作
gadget_dev_desc_UDC_store   // gadget/configfs.c
	usb_gadget_probe_driver(&gi->composite.gadget_driver)	// gadget/udc/core.c
		udc_bind_to_driver(udc, driver)   // gadget/udc/core.c
			driver->bind(udc->gadget, driver)   // gadget/udc/core.c
				configfs_composite_bind	  // gadget/configfs.c
					usb_add_function(c, f)   // gadget/composite.c
						function->bind(config, function) // gadget/composite.c

static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{
	int ret;

	dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
			driver->function);

	udc->driver = driver;
	udc->dev.driver = &driver->driver;
	udc->gadget->dev.driver = &driver->driver;

	usb_gadget_udc_set_speed(udc, driver->max_speed);

	ret = driver->bind(udc->gadget, driver);
	if (ret)
		goto err1;
	ret = usb_gadget_udc_start(udc);
	if (ret) {
		driver->unbind(udc->gadget);
		goto err1;
	}
	usb_udc_connect_control(udc);

	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
	return 0;
err1:
	if (ret != -EISNAM)
		dev_err(&udc->dev, "failed to start %s: %d\n",
			udc->driver->function, ret);
	udc->driver = NULL;
	udc->dev.driver = NULL;
	udc->gadget->dev.driver = NULL;
	return ret;
}

注意:这里和一般的 Device 和 Driver 的适配规则有些不一样。一般的规则是一个 Dirver 可以适配多个 Device,而一个 Device 只能适配一个 Driver。而这里的规则是一个 Gadget Device 只能适配一个 Gadget Driver,而一个 Gadget Driver 只能适配一个 Gadget Device。Gadget Device 代表的是一个 UDC,而 Gadget Driver 代表的是一个 Composite Device。

2、Gadget Device

Gadget Device 的主要作用是提供了 Endpoint 资源,供 Function Layer 使用标准的 Gadget API 来进行访问。

3、UDC Control

注册 Gadget Device 之前,初始化了 Gadget 的 操作函数集

ambarella_init_gadget
	udc->gadget.ops = &ambarella_ops
	
static const struct usb_gadget_ops ambarella_ops = {
	.get_frame		= ambarella_udc_get_frame,
	.wakeup			= ambarella_udc_wakeup,
	.pullup			= ambarella_udc_pullup,
	.vbus_session		= ambarella_udc_vbus_session,
	/*.set_selfpowered: Always selfpowered */
	.udc_start		= ambarella_udc_start,
	.udc_stop		= ambarella_udc_stop,
};

4、Gadget Driver (Configfs)

Gadget Device 支撑了核心 Gadget Api 的实现,而 Function Layer 又需要使用这些 Api。怎么样将两者适配起来?Gadget Driver 就是用来完成这项工作的。
目前存在两种风格的 Gadget Driver,其中包括:

  • Legacy。这是早期风格的 Gadget Driver,只能通过静态编译的方式指定使用哪些 Function。
  • Configfs。这是目前流行的 Gadget Driver,可以通过 configfs 文件系统,不用重新编译内核,动态的配置需要使用的 Function
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在嵌入式Linux下,设备树(device tree)用来描述硬件平台的各种资源,Linux内核在启动过程中,会解析设备树,获取各种硬件资源来初始化硬件。设备树的overlay功能是指可以在系统运行期间动态修改设备树。一般情况下,如上图所示,设备树经过DTC编译器编译为二进制的hello.dtb文件,加载到内存,随Linux内核一起启动后,一般就无法更改了。如果我们想修改设备树,需要修改hello.dts文件文件,重新编译成二进制文件:hello.dtb,然后重新启动内核,重新解析。有了设备树的overlay功能,省去了设备树的重新编译和内核重启,我们可以直接编写一个设备树插件:overlay.dts,编译成overlay.dtbo后,直接给设备树“打补丁”,在运行期间就可以动态添加节点、修改节点...设备树的overlay功能,在很多场合都会用得到,会让我们的开发更加方便:外界插拔设备,无法在设备树中预先描述:耳机树莓派 + FPGA开发板基于I2C的温度传感器管脚的重新配置:PIN multiplexing修改bootcmd、分区...设备树的overlay功能,目前还没有加入到内核mainline(linux-5.10.x),但目前有些开发板和配套的BSP已经支持了,支持在系统运行期间动态修改设备树文件。如果你手头的开发板或内核平台还没有支持device tree overlay,可以学习本期课程,学习内核中设备树overlay的实现原理,如何给内核打补丁,使内核支持设备树的overlay功能。有了本期课程的学习基础,明白了设备树overlay的实现原理和运行机制,你就可以尝试在自己的开发板平台上实现这个功能了。本期课程的主要内容如下:在开发板上如何实现设备树的overlay功能Configfs文件系统的配置与挂载Configfs编程接口如何编写设备树 overlay插件设备树 overlay的编译和运行设备树overlay运行机制分析本期课程适合哪些人学习:嵌入式驱动工程师嵌入式BSP工程师嵌入式软件工程师想从事嵌入式开发的同学全网首家讲解设备树overlay的视频教程。   

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃秃秃秃哇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值