Linux驱动模型之注册设备

说明

设备的话我们关心几个点:

  1. 设备是怎么添加到总线管理的设备链表上的?
  2. 注册设备后,它是怎么和驱动匹配,并最终调用驱动中的probe()函数的?
  3. 其实还有跟sysfs、event相关的一些内容,也很重要,后面再说吧。

数据结构

还是先看下内核中代表设备的结构体:

struct device {
	struct device		*parent;

	struct device_private	*p;

	struct kobject kobj;
	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	/* 挂在到哪个总线上 */
	struct bus_type	*bus;		/* type of bus device is on */
	/* 指向设备对应的驱动 */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	void		*driver_data;	/* Driver data, set and get with
					   dev_set/get_drvdata */
	struct dev_links_info	links;
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;
...
	struct device_node	*of_node; /* associated device tree node */
	struct fwnode_handle	*fwnode; /* firmware device node */

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	/* 这个是跟设备资源管理相关的,设备资源都会添加到这个链表上 */
	struct list_head	devres_head;

	struct klist_node	knode_class;
	struct class		*class;
	const struct attribute_group **groups;	/* optional groups */

	void	(*release)(struct device *dev);
};

这个结构体有点大,删除了部分内容,可以浏览下它大概的成员。现在只需要关注struct bus_type *bus这个成员。

设备注册过程

下面开始分析设备的注册过程:

drivers/base/core.c:
int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}

这个函数中包含了2个函数,第一个函数主要是初始化,第二个才是dev相关的核心函数。

drivers/base/core.c:
void device_initialize(struct device *dev)
{
	dev->kobj.kset = devices_kset;			//指向devices_kset
	kobject_init(&dev->kobj, &device_ktype);
	INIT_LIST_HEAD(&dev->dma_pools);
	mutex_init(&dev->mutex);
	lockdep_set_novalidate_class(&dev->mutex);
	spin_lock_init(&dev->devres_lock);
	INIT_LIST_HEAD(&dev->devres_head);
	device_pm_init(dev);
	set_dev_node(dev, -1);
#ifdef CONFIG_GENERIC_MSI_IRQ
	INIT_LIST_HEAD(&dev->msi_list);
#endif
	INIT_LIST_HEAD(&dev->links.consumers);
	INIT_LIST_HEAD(&dev->links.suppliers);
	dev->links.status = DL_DEV_NO_DRIVER;
}

在device_initialize(dev);中主要初始化了dev的相关成员。

/* 添加的一个设备到管理设备层次结构中,向系统中添加设备,
 * 在总线设备管理链表上添加设备。
 * 也有用户空间相关的操作,比如在sysf中出现设备信息.
 *
 * 1.在总线设备管理链表上添加设备。
 * 2.在sysfs文件系统(/sys)中添加设备信息,通过event事件的方式,
 * 在/dev目录下生成用户可以操作的设备节点。
 * 3.尝试调用驱动中的probe()函数。
 *
 * 注意:不管是cdev这种直接注册设备的方式,
 * 还是i2c这种通过设备树注册设备的方式,最终都会调用到这个函数。
 */
drivers/base/core.c:
int device_add(struct device *dev)
{
	struct device *parent;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;
	struct kobject *glue_dir = NULL;

	dev = get_device(dev);	//增加引用计数,实际是调用get_kobject().
	if (!dev)
		goto done;

	if (!dev->p) {					//如果没有私有数据
		error = device_private_init(dev);	//私有数据初始化
		if (error)
			goto done;
	}

	/*
	 * for statically allocated devices, which should all be converted
	 * some day, we need to initialize the name. We prevent reading back
	 * the name, and force the use of dev_name()
	 */
	if (dev->init_name) {
		dev_set_name(dev, "%s", dev->init_name);	//将名字赋值给kobject->name
		dev->init_name = NULL;
	}

	/* subsystems can specify simple device enumeration */
	if (!dev_name(dev) && dev->bus && dev->bus->dev_name)		//如果没有设备名,并且有关联的设备总线和总线名字
		dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);	//根据总线上的设备名字和id来设置名字
...
	/* 获取parent,有时候会为NULL */
	parent = get_device(dev->parent);
	kobj = get_device_parent(dev, parent);
	if (IS_ERR(kobj)) {
		error = PTR_ERR(kobj);
		goto parent_error;
	}
	if (kobj)
		dev->kobj.parent = kobj;

	/* use parent numa_node */
	if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
		set_dev_node(dev, dev_to_node(parent));

	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error) {
		glue_dir = get_glue_dir(dev);
		goto Error;
	}

	/* notify platform of device entry */
	if (platform_notify)
		platform_notify(dev);

	/* 在sys文件系统下创建文件 */
	error = device_create_file(dev, &dev_attr_uevent);
	if (error)
		goto attrError;

	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	/* bus.c:在总线上添加设备,包括相关的文件 */
	error = bus_add_device(dev);
	if (error)
		goto BusError;
	/* dpm:device power manage */
	error = dpm_sysfs_add(dev);
	if (error)
		goto DPMError;
	device_pm_add(dev);

	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &dev_attr_dev);
		if (error)
			goto DevAttrError;

		error = device_create_sys_dev_entry(dev);
		if (error)
			goto SysEntryError;

		devtmpfs_create_node(dev);
	}

	/* Notify clients of device addition.  This call must come
	 * after dpm_sysfs_add() and before kobject_uevent().
	 */
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_ADD_DEVICE, dev);

	/* 通过向用户空间发送添加事件,使用netlink或者uevent_helper.
	 * 用户空间的程序,比如udev/mdev捕获到此事件后进行处理,
	 * 比如解析设备号通过mknod来创建设备节点.
	 */
	kobject_uevent(&dev->kobj, KOBJ_ADD);

	/* !调用probe函数 */
	bus_probe_device(dev);

	/* 如果有parent,就添加到parent的子链表中 */
	if (parent)
		klist_add_tail(&dev->p->knode_parent,
			       &parent->p->klist_children);

	/* class相关操作 */
	if (dev->class) {
		mutex_lock(&dev->class->p->mutex);
		/* tie the class to the device */
		klist_add_tail(&dev->knode_class,
			       &dev->class->p->klist_devices);

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf,
				    &dev->class->p->interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->mutex);
	}
done:
	put_device(dev);
	return error;
...
}

这个函数内容有点多,请大家看下注释,或者多看几遍。如果有部分内容还是看不懂,等把后面的sysfs、event、class的知识掌握了再回来看,就会简单很多。
继续分析调用的函数:

drivers/base/bus.c:
int bus_add_device(struct device *dev)
{
	struct bus_type *bus = bus_get(dev->bus);

	if (bus) {
...
		/* !将设备添加到总线中设备管理链表上 */
		klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
	}
...
}

可以看到确实想总线上添加了设备。

再关注bus_probe_device(dev)这个函数,因为这几篇文章我主要是想将总线、设备、驱动它们之间的关系及操作调用过程讲清楚。
简单的追踪下调用过程:

drivers/base/bus.c:
void bus_probe_device(struct device *dev)
device_initial_probe(dev);
__device_attach(dev, false);

drivers/base/dd.c:
static int __device_attach(struct device *dev, bool allow_async)
{
...
		/* 注意最后一个参数 */
		ret = bus_for_each_drv(dev->bus, NULL, &data,
					__device_attach_driver);
...
}

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;
...
	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);
}

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

drivers/base/dd.c:
static int __device_attach_driver(struct device_driver *drv, void *_data)
{
	struct device_attach_data *data = _data;
	struct device *dev = data->dev;
	bool async_allowed;
	int ret;
...
	/* 1.驱动和设备进行匹配 */
	ret = driver_match_device(drv, dev);
	async_allowed = driver_allows_async_probing(drv);

	/* 2.尝试绑定设备和驱动,然后调用probe函数*/
	return driver_probe_device(drv, dev);
}

从这个函数的名字基本上就知道了它的作用,这个函数也是非常关键的,设备注册的目的也是在这里体现的。

drivers/base/base.h:

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

上面函数中的这行代码可能就是很多初学者的疑惑?想要找到在总线中实现的match()函数在在系统中是怎么被调用的?这句代码首先判断了驱动挂载的总线上是否注册了match函数,如果注册了,那么就将dev和drv作为参数传递给它,然后调用它。实际上就是调用了当前总线中实现的match()函数。比如我们可以在match()函数中通过名字、设备树等方式来匹配,但是一般不需要我们来编写。
我们在来看看第二函数:

drivers/base/dd.c:
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	ret = really_probe(dev, drv);	//probe函数
}

补充一下:上面这个函数不仅会在设备注册的过程中被调用,也会在驱动的注册过程中被调用。因为在dev和drv匹配完成后,下一步就是需要执行probe()函数。

drivers/base/dd.c:
static int really_probe(struct device *dev, struct device_driver *drv)
{
	/* 优先调用bus的probe,再调用驱动的probe */
	if (dev->bus->probe) {
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}
...
}

在dev和drv匹配成功后,就会调用驱动中的probe()函数了。如上,优先调用bus的probe(),再调用驱动的probe(),也就是我们在写驱动时,向系统注册的probe()函数。
到此,我们就将驱动中的注册的match()和probe()回调函数在系统的怎么被调用的过程就讲清楚了。
总的来说,如果只是分析总线、设备、驱动这几个的关系和它们在系统中的处理流程,其实并不复杂。

相关文章

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值