平台总线、平台设备、平台驱动学习笔记

内核版本:linux-4.19.63

Allwinner H3 SPI驱动为例:
spi-sun6i.c

static struct platform_driver sun6i_spi_driver = {
	.probe	= sun6i_spi_probe,
	.remove	= sun6i_spi_remove,
	.driver	= {
		.name		= "sun6i-spi",
		.of_match_table	= sun6i_spi_match,
		.pm		= &sun6i_spi_pm_ops,
	},
};
module_platform_driver(sun6i_spi_driver);

module_platform_driver 是一个宏,展开来看:

#define platform_driver_register(drv) \
	__platform_driver_register(drv, THIS_MODULE)
extern void platform_driver_unregister(struct platform_driver *);

#define module_platform_driver(__platform_driver) \
	module_driver(__platform_driver, platform_driver_register, \
			platform_driver_unregister)

#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
	return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
	__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

其实module_platform_driver(xxx) 简化了代码;就是填充了 module_init ,module_exit 代码

直接看平台驱动注册函数:

/**
 * __platform_driver_register - register a driver for platform-level devices
 * @drv: platform driver structure
 * @owner: owning module/driver
 */
int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;    //bus 类型为 platform_bus_type
	drv->driver.probe = platform_drv_probe;
	drv->driver.remove = platform_drv_remove;
	drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);  //往下会调用总线的match函数匹配成功后调用 platform_drv_probe
	  platform_drv_probe
	    xxx_probe     //最终调用具体实现的probe 如:sun6i_spi_probe
}

其实就是在 platform_bus 上注册了一个 driver 。
linux 下的驱动框架是总线、驱动、设备。驱动只有在有对应的设备的时候才会调用probe函数。另外总线也是要注册的。
问题1:platform_bus 总线是何时注册的?
问题2:platform_device 是何时注册的?

1:platform_bus 总线是何时注册的?
platform_bus_type

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};

platform_bus 注册过程:内核启动时调用了do_basic_setup() 最终 bus_register(&platform_bus_type);

do_basic_setup();   //内核启动时会调用
  driver_init();
    platform_bus_init();
      bus_register(&platform_bus_type); //注册总线
        priv->subsys.kobj.kset = bus_kset;
	    priv->subsys.kobj.ktype = &bus_ktype;
	    priv->drivers_autoprobe = 1;   //总是为1

2:platform_device 是何时注册的?
platform device 的注册:

#define __define_initcall(fn, id) \
	static initcall_t __initcall_##fn##id __used \
	__attribute__((__section__(".initcall" #id ".init"))) = fn;
#define arch_initcall_sync(fn)		__define_initcall(fn, 3s)
arch_initcall_sync(of_platform_default_populate_init);   //内核启动时由do_initcalls 调用
  of_platform_default_populate_init
    of_platform_default_populate
      of_platform_populate
        dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
          if (!of_device_is_available(np) ||
	      of_node_test_and_set_flag(np, OF_POPULATED))
		  return NULL;        // of_device_is_available 里判断dts设备节点的status是否等于"okay"
		  dev = of_device_alloc(np, bus_id, parent);
		  dev->dev.bus = &platform_bus_type;     //bus 类型为 platform_bus_type
	      dev->dev.platform_data = platform_data;
	      of_device_add(dev)
	        return device_add(&ofdev->dev);   

内核启动时会解析设备树文件,将各设备节点(status = “okay” 的节点)注册到platform总线下。

问题3:设备与驱动如何匹配?
driver_register 和 device_add 这两个函数最终都会试图去调用 bus 里的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);
}

of_driver_match_device
  of_match_device
    of_match_node
      __of_match_node(matches, node);
        __of_device_is_compatible
static int __of_device_is_compatible(const struct device_node *device,
				     const char *compat, const char *type, const char *name)
{
	struct property *prop;
	const char *cp;
	int index = 0, score = 0;

	/* Compatible match has highest priority */
	if (compat && compat[0]) {
		prop = __of_find_property(device, "compatible", NULL);  //一般通过比较compatible来进行匹配
		for (cp = of_prop_next_string(prop, NULL); cp;
		     cp = of_prop_next_string(prop, cp), index++) {
			if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
				score = INT_MAX/2 - (index << 2);
				break;
			}
		}
		if (!score)
			return 0;
	}

	/* Matching type is better than matching name */
	if (type && type[0]) {
		if (!device->type || of_node_cmp(type, device->type))
			return 0;
		score += 2;
	}

	/* Matching name is a bit better than not */
	if (name && name[0]) {
		if (!device->name || of_node_cmp(name, device->name))
			return 0;
		score++;
	}

	return score;
}

可以看出优先是使用compatible来进行匹配的。

问题5:匹配成功后做了什么?
match函数匹配成功会后调用platform_bus里的 platform_drv_probe,最终调用driver里的 probe,以sun6i_spi 为例就是 sun6i_spi_probe

sun6i_spi_probe
  master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi));
  master->set_cs = sun6i_spi_set_cs;
  master->transfer_one = sun6i_spi_transfer_one;
  devm_spi_register_master(&pdev->dev, master)
    devm_spi_register_controller
      spi_register_controller
        of_register_spi_devices
          for_each_available_child_of_node
            of_register_spi_device
              spi_alloc_device
              of_modalias_node
              of_spi_parse_dt
              spi_add_device(spi);
                spi_dev_set_name(spi);
                spi_setup(spi);
                device_add(&spi->dev);

总结梳理:
linux 下平台驱动一般流程是
1.注册总线(bus)
2.注册设备(device)
3.注册驱动(driver)
4.设备与匹配成功后,调用 probe

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值