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下其他输入法不能实现 “输入一串英文之后,按shift键,把英文输出”, 觉得很不好用所以去找了下搜狗拼音输入法在linux下的
函数介绍#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
1 环境centos7.4(64位)2 安装注意:特地写安装环境的原因是因为系统不同所需要安装的软件也不同,如果安装了跟自己的系统不匹配的软