Linux学习_驱动编写方案与总线驱动模型

Linux学习_驱动编写方案与总线驱动模型

  • 驱动编写的三种方法
    • 传统写法
    • 改进写法
    • 总线设备驱动模型
      • platform总线模型platform_bus_type
      • platform_match函数
      • 注册平台设备流程
      • 注册平台驱动
      • 常用函数
  • 具体程序在D:\6ull\git仓库\01_all_series_quickstart\05_嵌入式Linux驱动开发基础知识\source\02_led_drv\04_led_drv_template_bus_dev_drv

驱动编写的三种方法

传统写法

在这里插入图片描述
使用哪个引脚,怎么操作引脚,都写死在代码中。最简单,不考虑扩展性,可以快速实现功能。但是修改引脚时,需要重新编译。

改进写法

利用面向对象的设计思想,对驱动进行分层、分离等操作,将驱动整体依据通用性上下分层,上层注册驱动,下层实现硬件操作,如图所示
Linux学习_驱动编写方案与总线驱动模型_第1张图片
这样我们在更换硬件时,只需要更改board_xxx.c函数即可,提高了代码的复用性,而在下层的硬件操作中,我们可以再做细分,将硬件资源与引脚选取、初始化进行“分离”这一操作,如图所示
Linux学习_驱动编写方案与总线驱动模型_第2张图片
图中led_resource实现led资源的定义,board_xxx实现引脚的选用,chip_gpio实现了对led资源的init和ctrl函数

总线设备驱动模型

Linux学习_驱动编写方案与总线驱动模型_第3张图片
这里直接构建了一个总的device结构体platform_device,把整个板子上所有的硬件资源和初始化模板都塞进去,构建成一个庞大又臃肿的资源结合体。
Linux学习_驱动编写方案与总线驱动模型_第4张图片

platform总线模型platform_bus_type

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,	//bind platform device to platform driver.
	.uevent		= platform_uevent,
	.dma_configure	= platform_dma_configure,
	.pm		= &platform_dev_pm_ops,
};

这个结构体里面有个platform_match成员,在外部有该成员函数的定义,负责为BUS比较DeviceDriver是否匹配,在注册驱动时,整个driver_match_device(drv, dev)函数把platform_match(drv, dev)塞进去,比较的顺序如下。

  1. platform_device. driver_override(device指定要这个名字的driver)和 platform_driver.driver.name(driver的名字)/不太常用
  2. 设备树
  3. platform_device. name(device的名字)和 platform_driver.id_table[i].name(idtable里面存放着本driver支持的device名)/常用
  4. platform_device.name(device的名字)和 platform_driver.driver.name(driver的名字)//常用

platform_match函数

platform_match函数代码如下:

static int platform_match(struct device *dev, struct device_driver *drv){
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}

注册平台设备流程

总体的递归调用流程就是这样的,一层一层的追溯到最底层就是一个probe

int platform_device_register(struct platform_device *pdev)
	platform_device_add(pdev);
		device_add(&pdev->dev);				//struct device
			error = bus_add_device(dev);	//放入链表
			bus_probe_device(dev);			//probe drivers for a new device
				device_initial_probe(dev);
					__device_attach(dev, true);
						bus_for_each_drv(dev->bus, NULL, &data,__device_attach_driver);	//在drv链表中, bus_type->subsys_private->klist_drivers
							__device_attach_driver				//attempt to bind device & driver together.			struct device_driver和struct device
								driver_match_device(drv, dev);	//查看drv和dev是否匹配,drv->bus->match ? drv->bus->match(dev, drv) : 1;在这里会调用bus里面的match函数,指向了platform_match
								driver_probe_device(drv, dev);	//attempt to bind device & driver together,调用drv的probe函数
									really_probe(dev, drv);
										dev->driver = drv;		//dev里面的driver = drv。将两者联系起来了。
										dev->bus->probe(dev);	//在platform_bus_type中没有定义probe函数,所以会调用drv->probe(dev)函数
										//或者
										drv->probe(dev);

注册平台驱动

platform_driver_register(drv) 
	int __platform_driver_register(struct platform_driver *drv, struct module *owner)
		drv->driver.owner = owner;
		drv->driver.bus = &platform_bus_type;
		drv->driver.probe = platform_drv_probe;
		drv->driver.remove = platform_drv_remove;
		drv->driver.shutdown = platform_drv_shutdown;
		driver_register(&drv->driver);	//register driver with bus
			bus_add_driver(drv);		//放入链表,这种bus type的驱动列表
				driver_attach(drv);		//try to bind driver to devices.
					bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
						__driver_attach
							driver_match_device(drv, dev);	//查看drv和dev是否匹配,drv->bus->match ? drv->bus->match(dev, drv) : 1;在这里会调用bus里面的match函数,指向了platform_match
							device_driver_attach(drv, dev);	//attach a specific driver to a specific device
								driver_probe_device(drv, dev);
									really_probe(dev, drv);
										dev->driver = drv;		//dev里面的driver = drv。将两者联系起来了。
										dev->bus->probe(dev);	//在platform_bus_type中没有定义probe函数,所以会调用drv->probe(dev)函数
										//或者
										drv->probe(dev);

常用函数

注册/注销

platform_device_register/ platform_device_unregister
platform_driver_register/ platform_driver_unregister
platform_add_devices // 注册多个 device

查询函数
返回该 dev 中某类型(type)资源中的第 n 个

struct resource *platform_get_resource(struct platform_device *dev, unsigned int type,unsigned int n)

返回该 dev 所用的第 n 个中断

int platform_get_irq(struct platform_device *dev, unsigned int n)

通过名字(name)返回该 dev 的某类型(type)资源

struct resource *platform_get_resource_byname(struct platform_device *dev, unsigned int type, const char *name)

通过名字(name)返回该 dev 的中断号

int platform_get_irq_byname(struct platform_device *dev, const char *name)

具体程序在D:\6ull\git仓库\01_all_series_quickstart\05_嵌入式Linux驱动开发基础知识\source\02_led_drv\04_led_drv_template_bus_dev_drv

你可能感兴趣的:(Linux学习,linux,学习,运维)