必威体育Betway必威体育官网
当前位置:首页 > IT技术

linux设备模型六(device细节)

时间:2019-08-20 06:44:21来源:IT技术作者:seo实验室小编阅读:56次「手机版」
 

device

device相关的操作接口:

struct device *device_create(struct class *class, struct device *parent,
			     dev_t devt, void *drvdata, const char *fmt, ...);

struct device *device_create_vargs(struct class *class, struct device *parent,
				   dev_t devt, void *drvdata, const char *fmt,
				   va_list args);
int device_register(struct device *dev);

这三个函数的区别在我下面这篇文章总结的很清楚了,这里就不阐述了。

https://blog.csdn.net/qq_16777851/article/details/81259307

下面接着上面没有总结的分析

1  device_initialize


/**
 * device_initialize - init device structure.
 * @dev: device.
 *
 * This prepares the device for use by other layers by initializing
 * its fields.
 * It is the first half of device_register(), if called by
 * that function, though it can also be called separately, so one
 * may use @dev's fields. In particular, get_device()/put_device()
 * may be used for reference counting of @dev after calling this
 * function.
 *
 * NOTE: Use put_device() to give up your reference instead of freeing
 * @dev directly once you have called this function.
 */
void device_initialize(struct device *dev)
{
	dev->kobj.kset = devices_kset;              /* 所有的dev都有公共的device_set,在系统初始化的时候动态申请的,具体的回掉函数show,store,release还是要由dev里的release或dev_type里的release或从属的class里的dev_release来清除 */
	kobject_init(&dev->kobj, &device_ktype);    /* 初始化kobject的ktype,这里是只是设置它uevent_ops,具体的event还要由上层的bus或class实现 */
	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);                      /* 无依赖该节点的设备 */
}

/**
 * device_register - register a device with the system.
 * @dev: pointer to the device structure
 *
 * This hAPPens in two clean steps - initialize the device
 * and add it to the system. The two steps can be called
 * separately, but this is the easiest and most common.
 * I.e. you should only call the two helpers separately if
 * have a clearly defined need to use and refcount the device
 * before it is added to the hierarchy.
 *
 * NOTE: _Never_ directly free @dev after calling this function, even
 * if it returned an ERROR! Always use put_device() to give up the
 * reference initialized in this function instead.
 */
int device_register(struct device *dev)
{
	device_initialize(dev);        /* 初始化通用数据结构 */
	return device_add(dev);        /* 加入到该dev所属bus上 */
}

2 device_add 


/* 很重要的一个结构体 */
int device_private_init(struct device *dev)
{
	dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);         /* 申请空间 */
	if (!dev->p)
		return -ENOMEM;
	dev->p->device = dev;         /* 暂时只明确它是那个设备,先把自己绑定进去,后面再加如bus,driver之类 */
	klist_init(&dev->p->klist_children, klist_children_get,
		   klist_children_put);
	INIT_LIST_HEAD(&dev->p->deferred_probe);
	return 0;
}

/**
 * device_add - add device to device hierarchy.
 * @dev: device.
 *
 * This is part 2 of device_register(), though may be called
 * separately _iff_ device_initialize() has been called separately.
 *
 * This adds @dev to the kobject hierarchy via kobject_add(), adds it
 * to the global and sibling lists for the device, then
 * adds it to the other relevant subsystems of the driver model.
 *
 * Do not call this routine or device_register() more than once for
 * any device structure.  The driver model core is not designed to work
 * with devices that get unregistered and then Spring back to life.
 * (Among other things, it's very hard to guarantee that all references
 * to the previous incarnation of @dev have been dropped.)  Allocate
 * and register a fresh new struct device instead.
 *
 * NOTE: _Never_ directly free @dev after calling this function, even
 * if it returned an error! Always use put_device() to give up your
 * reference instead.
 */
int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;
	struct kobject *glue_dir = NULL;

	dev = get_device(dev);                             /* 增加该设备的引用计数 dev->kobj->kref */
	if (!dev)
		goto done;

	if (!dev->p) {                                     
		error = device_private_init(dev);             /* 私有数据没有的话申请并初始化,这个数据很重要,它是连接所属bus,parent,对应驱动等的重要连接点,我放在这个函数上面分析,一般都输在这里申请并初始化部分参数 */
		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);        /* 用dev的init_name初始化dev-kobject->name(目录名) */
		dev->init_name = NULL;                          /* dev的init_name不再用来 */
	}

	/* subsystems can specify simple device enumeration */
    /* dev的init_name不存在且dev-kobject->name不存在,则使用bus的dev_name和dev_id来设置目录名 */
	if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
		dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);

	if (!dev_name(dev)) {    /* 如果上面几个步骤都还没找到可设的目录名,则失败(因为设备必须要放在某个目录下) */
		error = -EINVAL;
		goto name_error;
	}

	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

	parent = get_device(dev->parent);         /* 父节点的引用计数加1 */
	kobj = get_device_parent(dev, parent);    /* 找到其父类,很重要 */
	if (kobj)
		dev->kobj.parent = kobj;              /* 在kobject层实现设备父子关系,这一步真正确定在sysfs中的目录关系了   */

	/* use parent numa_node */
	if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
		set_dev_node(dev, dev_to_node(parent));/* 设置该设备节点为-1,一般未注册前在device_initialize已经初始化为-1 */

	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
    /* 把内嵌的kobject注册到设备模型中将设备加入到kobject模型中,创建sys相关目录 ,目录名字为kobj->name */
	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);           /* 如果platform_notify定义的话,通知平台设备,一般不用 */

	error = device_create_file(dev, &dev_attr_uevent);    /* 创建sys目录下设备的uevent属性文件,通过它可以查看设备的uevent事件,主要是在/sys/devices/.../中添加dev的uevent属性文件 */
	if (error)
		goto attrError;
    /*  */
	error = device_add_class_symlinks(dev);   /* 实际创建的kobject都是在device下面,其他class,bus之类的里面的具体设备都是device目录下设备的符号链接,这里是在class下创建符号链接 */
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);           /* 创建sys目录下设备其他属性文件(添加设备属性文件)       
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);            /* 添加设备的总线属性 将设备加入到管理它的bus总线的设备连表上  创建subsystem链接文件,链接class下的具体的子系统文件夹  将设备添加到其总线的设备列表中。*/
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev);             /* 把设备增加到sysfs电源管理power目录(组)下,如果该设备设置电源管理相关的内容 */
	if (error)
		goto DPMError;
	device_pm_add(dev);                     /* 设备添加到电源管理相关的设备列表中 */

        /* 主设备号存在,则产生dev属性,并在/dev目录下产生设备节点文件  */
	if (MAJOR(dev->devt)) {            
        /* 创建sys目录下设备的设备号属性,即major和minor /主要是在sys/devices/...中添加dev属性文件 */
		error = device_create_file(dev, &dev_attr_dev);
		if (error)
			goto DevAttrError;
        /* 在/sys/dev/char/或者/sys/dev/block/创建devt的属性的连接文件,形如10:45,由主设备号和次设备号构成,指向/sys/devices/.../的具体设备目录,该链接文件只具备读属性,显示主设备号:次设备号,如10:45,用户空间udev响应uevent事件时,将根据设备号在/dev下创建节点文件
		error = device_create_sys_dev_entry(dev);
		if (error)
			goto SysEntryError;
        /* tmp文件系统相关的,不是重点,我也没用到过 */
		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);

	kobject_uevent(&dev->kobj, KOBJ_ADD);        /* 产生一个内核uevent事件(这里是有设备加入),可以是helper,也可是通过netlink机制和用户空间通信该事件可以被内核以及应用层捕获,属于linux设备模型中热插拔机制 */
        /* 画重点了!!!!!!!!!!!!---------给设备探测相应的驱动开始寻找设备所对应的驱动------------  去bus上找dev对应的drv,主要执行__device_attach,主要进行match,sys_add,执行probe函数和绑定等操作 */
	bus_probe_device(dev);
	if (parent)
		klist_add_tail(&dev->p->knode_parent,      /* 该设备添加到其父设备的子列表中 */
			       &parent->p->klist_children);

	if (dev->class) {
        /*  如果改dev有所属类,则将dev的添加到类的设备列表里面 */
		mutex_lock(&dev->class->p->mutex);        /* 这里是对dev所属类操作,所以要使用类的锁 */
		/* tie the class to the device */
		klist_add_tail(&dev->knode_class,
			       &dev->class->p->klist_devices);      /* dev添加到class的klist_device链表(对driver有klist_driver链表) */

		/* 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);    /* 通知有新设备加入,执行该dev的class_intf->add_dev(),这个有个好处,就是只有设备匹配注册成功了,才进行其它的注册工作(如字符设备的注册,生成/dev/***节点文件)以及部分初始化工作。*/
		mutex_unlock(&dev->class->p->mutex);
	}
done:
	put_device(dev);
	return error;
 SysEntryError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &dev_attr_dev);
 DevAttrError:
	device_pm_remove(dev);
	dpm_sysfs_remove(dev);
 DPMError:
	bus_remove_device(dev);
 BusError:
	device_remove_attrs(dev);
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	device_remove_file(dev, &dev_attr_uevent);
 attrError:
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	glue_dir = get_glue_dir(dev);
	kobject_del(&dev->kobj);
 Error:
	cleanup_glue_dir(dev, glue_dir);
	put_device(parent);
name_error:
	kfree(dev->p);
	dev->p = NULL;
	goto done;
}

2.1 上面device_add中bus_add_device

/**
 * bus_add_device - add device to bus
 * @dev: device being added
 *
 * - Add device's bus attributes.
 * - Create links to device's bus.
 * - Add the device to its bus's list of devices.
 */
int bus_add_device(struct device *dev)
{
	struct bus_type *bus = bus_get(dev->bus);    /* 对总线的引用计数加1 */
	int error = 0;

	if (bus) {
		pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
        /* 创建相应的属性文件 */
		error = device_add_attrs(bus, dev);
		if (error)
			goto out_put;
                /* 增加到组中(起始就是再加一层目录封装) */
		error = device_add_groups(dev, bus->dev_groups);
		if (error)
			goto out_id;
                /* 在sys/bus/总线类型/devices/目录下,创建名字为devname(d)指向sys/devices/相同设备名字的 符号链接  (前面创建了class目录下的,这创建bus目录下的符号链接) */
		error = sysfs_create_link(&bus->p->devices_kset->kobj,
						&dev->kobj, dev_name(dev));
		if (error)
			goto out_groups;
                /* 在sys/devices/分类/设备名字/目录下创建目录名字为 subsystem 并且指向在sys/bus/总线类型 的符号链接 */
		error = sysfs_create_link(&dev->kobj,
				&dev->bus->p->subsys.kobj, "subsystem");
		if (error)
			goto out_subsys;
                /* 把设备加入到总线的设备链中,这步才是重点  */
		klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
	}
	return 0;

out_subsys:
	sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
out_groups:
	device_remove_groups(dev, bus->dev_groups);
out_id:
	device_remove_attrs(bus, dev);
out_put:
	bus_put(dev->bus);
	return error;
}
(上面连个符号链接我举例表示一下,以platform总线为基础的fb为例)

它现在在这个目录下面,它是一个符号链接(真正的它是在sys/device/platform/s3cfb目录【上图打印的是从当前目录的相对路径】)

上面代码的第一个符号链接创建形就是上面这个的符号链接

下面看第二个符号链接

对具体的设备s3cfb来说,它的子系统数属于bus/platform,即属于它所属的总线类型

为什么链接文件不直接用platfotm名字而要使用subsystem,是因为很多的设备所属的bus不一样(比如platform,USB,i2c等),但在device层面要屏蔽掉,使用统一的名字方便使用和操作(在device目录下不需要记得操作的设备属于哪个bus,无论它属于哪个bus直接使用subsystem就可以了)。

2.2  device_add中对我们来说最重要的函数bus_probe_device

/**
 * bus_probe_device - probe drivers for a new device
 * @dev: device to probe
 *
 * - Automatically probe for a driver if the bus allows it.
 */
void bus_probe_device(struct device *dev)
{
	struct bus_type *bus = dev->bus;
	struct subsys_interface *sif;

	if (!bus)            /* device要查找自己bus上的driver,所以bus必须存在 */
		return;

	if (bus->p->drivers_autoprobe)      /* drivers_autoprobe是一个bit变量,为l则允许本条总线上的device注册时自动匹配driver,drivers_autoprobe默认总是为1,除非用户空间修改 */
		device_initial_probe(dev);      /* 匹配函数 */

	mutex_lock(&bus->p->mutex);
	list_for_each_entry(sif, &bus->p->interfaces, node)
		if (sif->add_dev)
			sif->add_dev(dev, sif);
	mutex_unlock(&bus->p->mutex);
}

void device_initial_probe(struct device *dev)
{
	__device_attach(dev, true);
}

2.2.1  __device_attach

struct device_attach_data {
struct device * dev;

/ *
*表示我们是否正在考虑异步探测或
*不是。只有设备或驱动程序注册后的初始绑定
*(包括延迟处理)可以异步完成,
*休息总是同步的,正如我们所预期的那样
*来自用户空间的请求。
* /
bool check_async;

/ *
*表示我们是否绑定同步或异步驱动程序。
*启用异步探测时,我们将执行2次传递
*超过司机:第一次通过同步探测,第二次
*进行异步探测(如果同步没有成功 - 
*最有可能的原因是没有驱动程序需要同步
*探测 - 我们在第一次通过时发现了异步驱动程序。
*完成2次传球是因为我们无法拍摄异步
*从bus_for_each_drv()开始探测给定的设备和驱动程序
*驱动程序指针不保证保持有效一次
* bus_for_each_drv()迭代到总线上的下一个驱动程序。
* /
bool want_async;

/ *
*如果在扫描匹配时,我们会将have_async设置为'true'
*驱动程序,我们会遇到一个请求异步探测的人。
* /
bool have_async;
};


static int __device_attach(struct device *dev, bool allow_async)
{
	int ret = 0;

	device_lock(dev);
	if (dev->driver) {     /* driver已经放在device了(初始化device,时,手动添加的driver) */
		if (device_is_bound(dev)) {    /* 判断device是否绑定driver,绑定则返回1*/
			ret = 1;
			goto out_unlock;    
		}
		ret = device_bind_driver(dev); /* 到这里说明,driver放在device里了,但还没真正的绑定 ,则执行这个函数绑定*/
		if (ret == 0)
			ret = 1;
		else {
			dev->driver = NULL;
			ret = 0;
		}
	} else {
        /* 下面的是刚注册的device,没有driver,需要自动查找匹配driver */
		struct device_attach_data data = {
			.dev = dev,
			.check_async = allow_async,
			.want_async = false,
		};

		if (dev->parent)
			pm_runtime_get_sync(dev->parent);    /* 可能要匹配初始化device了,其父dev肯定不能让睡眠(电源管理说起来比较复杂,等我学精了再总结电源管理相关的,这里就先略过 */

		ret = bus_for_each_drv(dev->bus, NULL, &data,
					__device_attach_driver);     /* 遍历总线上的driver链表,一个一个进行匹配 */
		if (!ret && allow_async && data.have_async) {
			/*
			 * If we could not find appropriate driver
			 * synchronously and we are allowed to do
			 * async probes and there are drivers that
			 * want to probe asynchronously, we'll
			 * try them.
			 */
			dev_dbg(dev, "scheduling asynchronous probe\n");
			get_device(dev);
			async_schedule(__device_attach_async_helper, dev);
		} else {
			pm_request_idle(dev);
		}

		if (dev->parent)
			pm_runtime_put(dev->parent);
	}
out_unlock:
	device_unlock(dev);
	return ret;
}

下面按顺序,先分析简单的默认注册的device带有驱动,只需要绑定,不经历匹配。后面分析匹配绑定篇的时候,这边的绑定篇的内容就可以简要是叙述了。

绑定篇

2.2.1.1 device和driver要绑定了

/**
 * device_bind_driver - bind a driver to one device.
 * @dev: device.
 *
 * Allow manual attachment of a driver to a device.
 * Caller must have already set @dev->driver.
 *
 * Note that this does not modify the bus reference count
 * nor take the bus's rwsem. Please verify those are accounted
 * for before calling this. (It is ok to call with no other effort
 * from a driver's probe() method.)
 *
 * This function must be called with the device lock held.
 */
int device_bind_driver(struct device *dev)
{
	int ret;

	ret = driver_sysfs_add(dev);        /* 把dev和driver链接起来 */
	if (!ret)
		driver_bound(dev);              /* device里面私有的driver节点挂接到driver的设备链表(一个driver可能对应多个device) */
	else if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_DRIVER_NOT_BOUND, dev);    /* 通知其它总线或子模块,driver绑定失败 */
	return ret;
}

2.2.1.2绑定第一步,现在sysfs文件系统下创建好device和driver之间的链接(两个本来是分开放的,现在要为了方便找到device后,在device目录下就要用driver,所以符号链接更方便)


static int driver_sysfs_add(struct device *dev)
{
	int ret;

	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_BIND_DRIVER, dev);        /* 通知其它总线将要绑定driver 到device*/

	ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
			  kobject_name(&dev->kobj));    /* 在driver目录下创建device目录的符号链接,名字为设备的名字 */
	if (ret == 0) {
		ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
					"driver");    /* 在device目录下创建driver的目录,名字为driver */
		if (ret)
			sysfs_remove_link(&dev->driver->p->kobj,
					kobject_name(&dev->kobj));
	}
	return ret;
}

2.2.1.3  绑定第二步  driver绑定到device


static void driver_bound(struct device *dev)
{
	if (klist_node_attached(&dev->p->knode_driver)) {        /* 再次检查,确定没绑定 */
		printk(KERN_WARNING "%s: device %s already bound\n",
			__func__, kobject_name(&dev->kobj));
		return;
	}

	pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
		 __func__, dev->driver->name);
    
        /* 绑定!!!!!!   把device私有的p里的knode_driver,绑定到driver里面的klist_device链表上 */
	klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_BOUND_DRIVER, dev);     /* 通知其它子模块以及绑定成功 */

绑定篇大工告成

匹配绑定篇

2.2.2.1 第一步遍历bus上driver调用用match函数匹配

/**
 * bus_for_each_drv - driver iterator
 * @bus: bus we're dealing with.
 * @start: driver to start iterating on.
 * @data: data to pass to the callback.
 * @fn: function to call for each driver.
 *
 * This is nearly identical to the device iterator above.
 * We iterate over each driver that belongs to @bus, and call
 * @fn for each. If @fn returns anything but 0, we break out
 * and return it. If @start is not NULL, we use it as the head
 * of the list.
 *
 * NOTE: we don't return the driver that returns a non-zero
 * value, nor do we leave the reference count incremented for that
 * driver. If the caller needs to know that info, it must set it
 * in the callback. It must also be sure to increment the refcount
 * so it doesn't disappear before returning to the caller.
 */
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
		     void *data, int (*fn)(struct device_driver *, void *))
{
	struct klist_iter i;
	struct device_driver *drv;
	int error = 0;

	if (!bus)
		return -EINVAL;
        /* 把总线上的driver链表的头节点给i(注意头节点是不带有效信息的) */
	klist_iter_init_node(&bus->p->klist_drivers, &i,
			     start ? &start->p->knode_bus : NULL);    /* 注意我们这里上面的start传的是NULL */
	while ((drv = next_driver(&i)) && !error)        /* next_driver是遍历i的下一个节点并返回driver */
		error = fn(drv, data);      /* 调用匹配函数,传参分别是drv和device_attach_data */
	klist_iter_exit(&i);                /* klist使用结束必须要用exit结束 */
	return error;
}

2.2.2.2 工具函数

/**
 * klist_iter_init_node - Initialize a klist_iter structure.
 * @k: klist we're iterating.
 * @i: klist_iter we're filling.
 * @n: node to start with.
 *
 * Similar to klist_iter_init(), but starts the action off with @n,
 * instead of with the list head.
 */
/* 因为我么的n是NULL,所以i->i_cur = NULL */
void klist_iter_init_node(struct klist *k, struct klist_iter *i,
			  struct klist_node *n)
{
	i->i_klist = k;    /*  */
	i->i_cur = NULL;
	if (n && kref_get_unless_zero(&n->n_ref))
		i->i_cur = n;
}

2.2.2.3 回到前面__device_attach函数匹配绑定那里

/* 在前面__device_attach函数匹配绑定,传入的__device_attach_driver就是device和driver匹配的函数,其他操作都是为调用这个函数匹配服务的 */
ret = bus_for_each_drv(dev->bus, NULL, &data,__device_attach_driver);

2.2.2.4真正的匹配函数

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
    /* bus的match存在,则调用bus的match函数匹配,否则返回1 */
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

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;

	/*
	 * Check if device has already been claimed. This may
	 * happen with driver loading, device discovery/registration,
	 * and deferred probe processing happens all at once with
	 * multiple threads.
	 */
	if (dev->driver)        /* 前面各种判断,就是要driver存在 */
		return -EBUSY;

	ret = driver_match_device(drv, dev);    /* bus自带的match匹配,程序在这个函数上面 */
	if (ret == 0) {
		/* no match */
		return 0;
	} else if (ret == -EPROBE_DEFER) {
		dev_dbg(dev, "Device match requests probe deferral\n");
		driver_deferred_probe_add(dev);    /* 重新匹配 */
	} else if (ret < 0) {
		dev_dbg(dev, "Bus failed to match device: %d", ret);
		return ret;                        /* 错误 */
	} /* ret > 0 means positive match */

	async_allowed = driver_allows_async_probing(drv);    /* 看该驱动支不支持异步匹配 */

	if (async_allowed)
		data->have_async = true;            /* 返回给上层 */
     /* 从上面一路分析的check_async-->true  want_async--> false,该设备如果支持异步匹配直接退出,否则执行下面的driver_probe_device函数*/
	if (data->check_async && async_allowed != data->want_async)
		return 0;
    /* 到这里以及说明匹配上了,该执行driver里面的probe函数了 */
	return driver_probe_device(drv, dev);
}

2.2.2.5 以经匹配好了,该执行驱动的probe函数了

static inline int device_is_registered(struct device *dev)
{
    /* 通过查看在sysfs中的状态判断是都已经注册 */
	return dev->kobj.state_in_sysfs;
}

/**
 * driver_probe_device - attempt to bind device & driver together
 * @drv: driver to bind a device to
 * @dev: device to try to bind to the driver
 *
 * This function returns -ENODEV if the device is not registered,
 * 1 if the device is bound successfully and 0 otherwise.
 *
 * This function must be called with @dev lock held.  When called for a
 * USB interface, @dev->parent lock must be held as well.
 *
 * If the device has a parent, runtime-resume the parent before driver probing.
 */
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;

	if (!device_is_registered(dev))    /* 必须注册后的才能匹配,没注册匹配就是乱搞了 */
		return -ENODEV;

	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);

	if (dev->parent)    /* 有依赖的父节点 */
		pm_runtime_get_sync(dev->parent);    /* 则父节点的电源管理引用计数加1(即父设备下的子设备没睡眠前,父设备时不能睡眠的,否则子设备没法工作) */

    /* 下面电源管理的还是不分析了,我学习的也不深 */
	pm_runtime_barrier(dev);
	ret = really_probe(dev, drv);        /* 看来probe流程太长了,内核开发者都有点不耐烦了,直接起名真正的probe */
	pm_request_idle(dev);

	if (dev->parent)
		pm_runtime_put(dev->parent);

	return ret;
}

2.2.2.6真正的probe函数执行


static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = -EPROBE_DEFER;
	int local_trigger_count = atomic_read(&deferred_trigger_count);
	bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&
			   !drv->suppress_bind_attrs;

	if (defer_all_probes) {
        /* 忽略,不是我们的菜 */
		/*
		 * Value of defer_all_probes can be set only by
		 * device_defer_all_probes_enable() which, in turn, will call
		 * wait_for_device_probe() right after that to avoid any races.
		 */
		dev_dbg(dev, "Driver %s force probe deferral\n", drv->name);
		driver_deferred_probe_add(dev);
		return ret;
	}

	atomic_inc(&probe_count);
	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
		 drv->bus->name, __func__, drv->name, dev_name(dev));
	WARN_ON(!list_empty(&dev->devres_head));

re_probe:
	dev->driver = drv;        /* 匹配好后的驱动信息记录到设备内部 */

	/* If using pinctrl, bind pins now before probing 没了解,跳过*/
	ret = pinctrl_bind_pins(dev);
	if (ret)
		goto pinctrl_bind_failed;

	if (driver_sysfs_add(dev)) {        /* driver加入sysfs(其实就是创建各种符号链接,前面device默认绑定有driver那里已经分析过了) */
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}
    
    /* 电源管理,在probe指向前执行activate */
	if (dev->pm_domain && dev->pm_domain->activate) {
		ret = dev->pm_domain->activate(dev);
		if (ret)
			goto probe_failed;
	}

	/*
	 * Ensure devices are listed in devices_kset in correct order
	 * It's important to move Dev to the end of devices_kset before
	 * calling .probe, because it could be recursive and parent Dev
	 * should always go first
	 */
    /* 确保设备按照正确的顺序列在devices_kset中。在调用.probe之前将device移动到devices_kset的末尾非常重要,因为它可能是递归的,并且父device应该始终先行。最后注册的的必须在全部的device链表的末尾 */
	devices_kset_move_last(dev);

	if (dev->bus->probe) {            
		ret = dev->bus->probe(dev);    /* bus的probe函数存在,则执行bus的probe */
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {           /* 否则,指向device的probe函数 */
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}

	if (test_remove) {
		test_remove = false;

		if (dev->bus->remove)
			dev->bus->remove(dev);
		else if (drv->remove)
			drv->remove(dev);

		devres_release_all(dev);
		driver_sysfs_remove(dev);
		dev->driver = NULL;
		dev_set_drvdata(dev, NULL);
		if (dev->pm_domain && dev->pm_domain->dismiss)
			dev->pm_domain->dismiss(dev);
		pm_runtime_reinit(dev);

		goto re_probe;
	}

	pinctrl_init_done(dev);   /* 没了解,跳过 */

	if (dev->pm_domain && dev->pm_domain->sync)
		dev->pm_domain->sync(dev);

	driver_bound(dev);        /* 将设备加入到驱动支持的设备链表中,一个设备需要一个驱动,一个驱动支持多个设备,前面device默认绑定driver那里已经分析过了 */
	ret = 1;
	pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);
	goto done;

probe_failed:
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
pinctrl_bind_failed:
	devres_release_all(dev);
	driver_sysfs_remove(dev);
	dev->driver = NULL;
	dev_set_drvdata(dev, NULL);
	if (dev->pm_domain && dev->pm_domain->dismiss)
		dev->pm_domain->dismiss(dev);
	pm_runtime_reinit(dev);

	switch (ret) {
	case -EPROBE_DEFER:
		/* Driver requested deferred probing */
		dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
		driver_deferred_probe_add(dev);
		/* Did a trigger occur while probing? Need to re-trigger if yes */
		if (local_trigger_count != atomic_read(&deferred_trigger_count))
			driver_deferred_probe_trigger();
		break;
	case -ENODEV:
	case -ENXIO:
		pr_debug("%s: probe of %s rejects match %d\n",
			 drv->name, dev_name(dev), ret);
		break;
	default:
		/* driver matched but the probe failed */
		printk(KERN_WARNING
		       "%s: probe of %s failed with error %d\n",
		       drv->name, dev_name(dev), ret);
	}
	/*
	 * ignore errors returned by ->probe so that the next driver can try
	 * its luck.
	 */
	ret = 0;
done:
	atomic_dec(&probe_count);
	wake_up(&probe_waitqueue);
	return ret;
}

这个函数总结一点,probe的优先级中,该dev的bus中如果有probe,则用bus的,如果没有则用,dev的。所以编写某个device驱动时,要先看该驱动从属的bus有没有probe,如果没有,则自己的device里必须要实现probe函数,否则可以不实现。

上面的device分为初始注册devide的时候就已经为其设置好了driver,和初始没设置自动在相同bus下的另一条链表driver中使用bus或dev提供的match函数匹配,两种可能。

1.初始注册devide的时候就已经为其设置好了driver,最终只是绑定了两者,并未指行probe函数,需要驱动工程师自己调用指行。

2.初始注册devide的时候没设置其driver,则匹配相同bus下的driver,没匹配到则算了,匹配到会指行bus或dev提供的probe函数。

相关阅读

Linux下安装搜狗拼音输入法

由于Linux下其他输入法不能实现 “输入一串英文之后,按shift键,把英文输出”, 觉得很不好用所以去找了下搜狗拼音输入法在linux下的

Linux信号量 sem_t简介

函数介绍#include<semaphore.h>信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它

sda, sdb, sdc, sda1, sda2在Linux中都代表什么

意义如下:第一个软驱 /dev/fd0.第二个软驱 /dev/fd1.第一块硬盘 /dev/sda.第二块硬盘 /dev/sdb, 以此类推.第一个SCSI CD-ROM /dev

Linux命令中Ctrl+z、Ctrl+c和Ctrl+d的区别和使用

Ctrl+c,Ctrl+d,Ctrl+z在Linux中意义 Ctrl+c和ctrl+z都是中断命令,但是他们的作用却不一样. Ctrl+c是强制中断程序的执行。 Ctrl+z

linux下解压rar文件

1 环境centos7.4(64位)2 安装注意:特地写安装环境的原因是因为系统不同所需要安装的软件也不同,如果安装了跟自己的系统不匹配的软

分享到:

栏目导航

推荐阅读

热门阅读