Linux驱动模型之注册驱动

前言

驱动的话我们关心几个点:

  1. 驱动是怎么添加到总线管理的设备链表上的?
  2. 注册驱动后,它是怎么和设备匹配,并最终调用驱动中的probe()函数的?

数据结构

首先看下数据结构:

struct device_driver {
	const char		*name;
	struct bus_type		*bus;	//要挂载的总线

	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */

	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
	enum probe_type probe_type;

	const struct of_device_id	*of_match_table;	//匹配表
	const struct acpi_device_id	*acpi_match_table;

	int (*probe) (struct device *dev);	//驱动的probe函数
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;

	const struct dev_pm_ops *pm;

	struct driver_private *p;	//驱动的私有指针
};

这个结构体中我们也先只关注有注释的几个成员。bus就是我们这个驱动将要挂载到的总线(在device结构体中也有这个成员,它是bus 、dev、drv三个形成关联的桥梁),而of_match_table和probe就是我们经常在驱动中需要填充的内容。struct driver_private *p这个成员的话我们可以在驱动中灵活的使用。

驱动注册

/ * 通用驱动注册:
 * 将驱动添加到相应的bus维护的驱动链表上,还有一些其它的工作.
 */
drivers/base/driver.c:
int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	BUG_ON(!drv->bus->p);

	/* 1.首先判断驱动相关的bus->probe和drv-probe函数是否都定义了,然后发出警告。
	 * 这里的目的主要是不管采用哪个probe函数,确保正确的调用到probe。
	 */
	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);

	/* 2.判断总线上是否已经有这个驱动了 */
	other = driver_find(drv->name, drv->bus);
...
	/* 3.bus.c:将驱动添加到总线维护的驱动链表上 */
	ret = bus_add_driver(drv);

	ret = driver_add_groups(drv, drv->groups);

	kobject_uevent(&drv->p->kobj, KOBJ_ADD);

	return ret;
}

驱动的注册相对设备的注册来说,过程会简单很多。这个函数前两步都是做判断的,bus_add_driver才是真正的核心函数。

drivers/base/bus.c:
int bus_add_driver(struct device_driver *drv)
{
	struct bus_type *bus;
	struct driver_private *priv;
	int error = 0;

	bus = bus_get(drv->bus);	//获取bus的类型
/* 1.分配对象及初始化 */
	priv = kzalloc(sizeof(*priv), GFP_KERNEL);

	klist_init(&priv->klist_devices, NULL, NULL);
	priv->driver = drv;
	drv->p = priv;
	priv->kobj.kset = bus->p->drivers_kset;		//重要
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	if (error)
		goto out_unregister;

	/* 2. !将驱动添加到bus维护的驱动链表上 */
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

	/* 3.判断是否自动调用autoprobe函数 */
	if (drv->bus->p->drivers_autoprobe) {
		if (driver_allows_async_probing(drv)) {
			pr_debug("bus: '%s': probing driver %s asynchronously\n",
				drv->bus->name, drv->name);
			async_schedule(driver_attach_async, drv);
		} else {
/* 4.对驱动和设备进行关联,并调用probe函数 */
			error = driver_attach(drv);	
...
		}
	}
	module_add_driver(drv->owner, drv);

	error = driver_create_file(drv, &driver_attr_uevent);
	}
	error = driver_add_groups(drv, bus->drv_groups);
...
}

在这个函数有4个主要的步骤,其它步骤2中是将驱动添加到bus维护的驱动链表上,步骤4对驱动和设备进行关联,并调用probe函数。

drivers/base/dd.c:
int driver_attach(struct device_driver *drv)
{
/* 最后一个参数是一个函数指针,可以跳进去查看 */
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

遍历总线上所有的设备,并且将和驱动进行匹配。

drivers/base/bus.c:
int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus || !bus->p)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while ((dev = next_device(&i)) && !error)
		error = fn(dev, data);
	klist_iter_exit(&i);
	return error;
}

循环每个设备,并调用__driver_attach()函数。

drivers/base/dd.c:
static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;
	int ret;

	/* 1.driver和device进行匹配,会调用bus_type中定义的match函数 */
	ret = driver_match_device(drv, dev);

	device_lock(dev);
	if (!dev->driver)
	    /* 2.probe处理 */
		driver_probe_device(drv, dev);
	device_unlock(dev);
	if (dev->parent)
		device_unlock(dev->parent);

	return 0;
}

到这里之后,就和注册设备时类似,先将drv和dev进行匹配,如果匹配成功就调用驱动中的的probe()回调函数。这里不再做分析,请参考前一篇文章。

相关文章

Linux驱动模型概述
Linux驱动模型之总线
Linux驱动模型之注册设备

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值