Linux设备模型6

Linux设备模型6(基于Linux6.6)---device和device_driver细节说明

 


一、Device的接口

 include/linux/device.h

/*
 * Easy functions for dynamically creating devices on the fly
 */
__printf(5, 6) struct device *
device_create(const struct class *cls, struct device *parent, dev_t devt,
	      void *drvdata, const char *fmt, ...);
__printf(6, 7) struct device *
device_create_with_groups(const struct class *cls, struct device *parent, dev_t devt,
			  void *drvdata, const struct attribute_group **groups,
			  const char *fmt, ...);
int __must_check device_register(struct device *dev);

首先看一下device_create函数,注释写的很清楚,创建一个设备,并注册它到sysfs中。提供了强大的格式化注册接口。

drivers/base/core.c

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

	va_start(vargs, fmt);
	dev = device_create_groups_vargs(class, parent, devt, drvdata, NULL,
					  fmt, vargs);
	va_end(vargs);
	return dev;
}
EXPORT_SYMBOL_GPL(device_create);

上面只是为了提供完善的接口,device_create_groups_vargs才是真正干活的。

注释写的也很清楚:

1).创建一个设备(malloc一个device,并填充相关数据)。

2).注册这个设备。

drivers/base/core.c 

static __printf(6, 0) struct device *
device_create_groups_vargs(const struct class *class, struct device *parent,
			   dev_t devt, void *drvdata,
			   const struct attribute_group **groups,
			   const char *fmt, va_list args)
{
	struct device *dev = NULL;
	int retval = -ENODEV;

	if (IS_ERR_OR_NULL(class))
		goto error;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		retval = -ENOMEM;
		goto error;
	}

	device_initialize(dev);
	dev->devt = devt;
	dev->class = class;
	dev->parent = parent;
	dev->groups = groups;
	dev->release = device_create_release;
	dev_set_drvdata(dev, drvdata);

	retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
	if (retval)
		goto error;

	retval = device_add(dev);
	if (retval)
		goto error;

	return dev;

error:
	put_device(dev);
	return ERR_PTR(retval);
}

真正的注册函数,其实是初始化了一些设备中通用参数(自旋锁,链表等),然后调用device_add就是这里。

drivers/base/core.c

int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}
EXPORT_SYMBOL_GPL(device_register);

整体的层次:

+----------------------+
|   Start              |
|   (Device Creation)  |
+----------------------+
          |
          v
+----------------------+
|  device_create()     |
|  (Allocate Memory &  |
|   Setup Device)      |
+----------------------+
          |
          v
+----------------------+
|  kzalloc()           |
|  (Allocate & Zero-   |
|   Initialize Memory) |
+----------------------+
          |
          v
+----------------------+
|  device_initialize() |
|  (Initialize Device  |
|   Structure)         |
+----------------------+
          |
          v
+----------------------+
|  device_register()   |
|  (Register Device in |
|   Kernel)            |
+----------------------+
          |
          v
+----------------------+
|  device_add()        |
|  (Add Device to      |
|   Device Model)      |
+----------------------+
          |
          v
+----------------------+
|   End                |
|   (Device Ready)     |
+----------------------+

 

device_create比device_add多做的事情就非常清楚了。
1). 新建struct device, device_add是不会新建的,只会加。
2). 进行了初始化, 如果不调device_register, 就得自己去调用device_initiali初始化。

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

device_initialize

 drivers/base/core.c

  
/**
 * 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, NUMA_NO_NODE);
	INIT_LIST_HEAD(&dev->links.consumers);
	INIT_LIST_HEAD(&dev->links.suppliers);
	INIT_LIST_HEAD(&dev->links.defer_sync);
	dev->links.status = DL_DEV_NO_DRIVER;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
    defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
    defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
	dev->dma_coherent = dma_default_coherent;
#endif
	swiotlb_dev_init(dev);

}
 
/**
 * 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上 */
}

二、 Device增加和匹配

 

  
/* 很重要的一个结构体 */
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 subsys_private *sp;
	struct device *parent;
	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:
	dev->driver = NULL;
	bus_remove_device(dev);
 BusError:
	device_remove_attrs(dev);
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	device_remove_file(dev, &dev_attr_uevent);
 attrError:
	device_platform_notify_remove(dev);
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	glue_dir = get_glue_dir(dev);
	kobject_del(&dev->kobj);
 Error:
	cleanup_glue_dir(dev, glue_dir);
parent_error:
	put_device(parent);
name_error:
	kfree(dev->p);
	dev->p = NULL;
	goto done;
}
EXPORT_SYMBOL_GPL(device_add);

2.1、 Device_add中bus_add_device

 drivers/base/bus.c

 /**
 * 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)
{
	if (!sp) {
		/*
		 * This is a normal operation for many devices that do not
		 * have a bus assigned to them, just say that all went
		 * well.
		 */
		return 0;
	}

	/*
	 * Reference in sp is now incremented and will be dropped when
	 * the device is removed from the bus
	 */

	pr_debug("bus: '%s': add device %s\n", sp->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(&sp->devices_kset->kobj, dev_name(dev));
out_groups:
	device_remove_groups(dev, sp->bus->dev_groups);
out_put:
	subsys_put(sp);
	return error;
}

它现在在这个目录下面,它是一个符号链接(真正的它是在/sys/devices/platform/adc-keys/目录)

上面代码的第一个符号链接创建形就是上面这个的符号链接。下面看第二个符号链接:

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

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

2.2、  Device_add-->最重要的函数bus_probe_device

 drivers/base/bus.c

 /**
 * 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 subsys_private *sp = bus_to_subsys(dev->bus);
	struct subsys_interface *sif;

	if (!sp) /* device要查找自己bus上的driver,所以bus必须存在 */
		return;
 
 
	if (sp->drivers_autoprobe)      /* drivers_autoprobe是一个bit变量,为l则允许本条总线上的device注册时自动匹配driver,drivers_autoprobe默认总是为1,除非用户空间修改 */
		device_initial_probe(dev);      /* 匹配函数 */
 
	mutex_lock(&sp->mutex);
	list_for_each_entry(sif, &sp->interfaces, node)
		if (sif->add_dev)
			sif->add_dev(dev, sif);
	mutex_unlock(&sp->mutex);
}
 


drivers/base/dd.c
void device_initial_probe(struct device *dev)
{
	__device_attach(dev, true);
}

2.2.1、  __device_attach

 drivers/base/dd.c

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);
	if (async)
		async_schedule_dev(__device_attach_async_helper, dev);
	return ret;
}

下面按顺序,先分析简单的默认注册的device带有驱动,只需要绑定,不经历匹配。

2.2.2、 device和driver要绑定了

 drivers/base/dd.c


int device_bind_driver(struct device *dev)
{
	int ret;

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

2.2.3、绑定第一步

现在sysfs文件系统下创建好device和driver之间的链接。drivers/base/dd.c


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

	bus_notify(dev, BUS_NOTIFY_BIND_DRIVER); /* 通知其它总线将要绑定driver 到device*/

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

	ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
				"driver"); /* 在device目录下创建driver的目录,名字为driver */
	if (ret)
		goto rm_dev;

	if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump)
		return 0;

	ret = device_create_file(dev, &dev_attr_coredump);
	if (!ret)
		return 0;

	sysfs_remove_link(&dev->kobj, "driver");

rm_dev:
	sysfs_remove_link(&dev->driver->p->kobj,
			  kobject_name(&dev->kobj));

fail:
	return ret;
}

2.2.4、绑定第二步

   driver绑定到device。drivers/base/dd.c


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

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

	device_pm_check_callbacks(dev);

	/*
	 * Make sure the device is no longer in one of the deferred lists and
	 * kick off retrying all pending devices
	 */
	driver_deferred_probe_del(dev);
	driver_deferred_probe_trigger();

	bus_notify(dev, BUS_NOTIFY_BOUND_DRIVER);
/* 通知其它子模块以及绑定成功 */
	kobject_uevent(&dev->kobj, KOBJ_BIND);
}

绑定篇大工告成

2.2.5、匹配绑定篇

第一步遍历bus上driver调用用match函数匹配.drivers/base/bus.c


int bus_for_each_drv(const struct bus_type *bus, struct device_driver *start,
		     void *data, int (*fn)(struct device_driver *, void *))
{
	struct subsys_private *sp = bus_to_subsys(bus);
	struct klist_iter i;
	struct device_driver *drv;
	int error = 0;

	if (!sp)
		return -EINVAL;
        /* 把总线上的driver链表的头节点给i(注意头节点是不带有效信息的) */
	klist_iter_init_node(&sp->klist_drivers, &i,
			     start ? &sp->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结束 */
	subsys_put(sp);
	return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_drv);

 工具函数。lib/klist.c

/**
 * 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.
 */
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;
}
EXPORT_SYMBOL_GPL(klist_iter_init_node);

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

drivers/base/dd.c   __device_attach函数

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

2.2.6、真正的匹配函数

drivers/base/base.h
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;
}

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;
 
	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函数了 */
	/*
	 * Ignore errors returned by ->probe so that the next driver can try
	 * its luck.
	 */
	ret = driver_probe_device(drv, dev);
	if (ret < 0)
		return ret;
	return ret == 0;
}

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

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

drivers/base/dd.c

/**
 * 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);
 
	pm_runtime_put_suppliers(dev);
	return ret;
}

2.2.8、真正的probe函数执行

drivers/base/dd.c 

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

	if (defer_all_probes) {
		/*
		 * Value of defer_all_probes can be set only by
		 * device_block_probing() 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);
		return -EPROBE_DEFER;
	}

	link_ret = device_links_check_suppliers(dev);
	if (link_ret == -EPROBE_DEFER)
		return link_ret;

	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
		 drv->bus->name, __func__, drv->name, dev_name(dev));
	if (!list_empty(&dev->devres_head)) {
		dev_crit(dev, "Resources present before probing\n");
		ret = -EBUSY;
		goto done;
	}
 
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;

		device_remove(dev);
		driver_sysfs_remove(dev);
		if (dev->bus && dev->bus->dma_cleanup)
			dev->bus->dma_cleanup(dev);
		device_unbind_cleanup(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;
 
dev_sysfs_state_synced_failed:
dev_groups_failed:
	device_remove(dev);
probe_failed:
	driver_sysfs_remove(dev);
sysfs_failed:
	bus_notify(dev, BUS_NOTIFY_DRIVER_NOT_BOUND);
	if (dev->bus && dev->bus->dma_cleanup)
		dev->bus->dma_cleanup(dev);
pinctrl_bind_failed:
	device_links_no_driver(dev);
	device_unbind_cleanup(dev);
done:
	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函数。

下面介绍Drive是如何注册的详细过程:

三、Driver的注册过程

drivers/base/driver.c

 /**
 * driver_register - register driver with bus
 * @drv: driver to register
 *
 * We pass off most of the work to the bus_add_driver() call,
 * since most of the things we have to do deal with the bus
 * structures.
 */
int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;
 
   /* driver的总线必须要有自己的subsys,因为这个才是整个bus连接device和driver的核心 */
 
	if (!bus_is_registered(drv->bus)) {
		pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
			   drv->name, drv->bus->name);
		return -EINVAL;
	}
    /* driver和bus两种都实现了下面函数,而实际最只能执行一个,所以告警说重复 */
	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);
 
    /* 查找驱动是否已经装载,已经装载的则直接 */
	other = driver_find(drv->name, drv->bus);    
	if (other) {
		put_driver(other);
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}
    
    /* 把驱动加入总线的驱动链表 */
	ret = bus_add_driver(drv);
	if (ret)
		return ret;
    /* 驱动加组 */
	ret = driver_add_groups(drv, drv->groups);
	if (ret) {
		bus_remove_driver(drv);
		return ret;
	}
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);        /* 向上增报告一个增加事件 */
 	deferred_probe_extend_timeout();
 
	return ret;
}

3.1、 查找总线bus下的driver链表有没有名为name的driver

 drivers/base/bus.c

/**
 * driver_find - locate driver on a bus by its name.
 * @name: name of the driver.
 * @bus: bus to scan for the driver.
 *
 * Call kset_find_obj() to iterate over list of drivers on
 * a bus to find driver by name. Return driver if found.
 *
 * This routine provides no locking to prevent the driver it returns
 * from being unregistered or unloaded while the caller is using it.
 * The caller is responsible for preventing this.
 */
struct device_driver *driver_find(const char *name, const struct bus_type *bus)
{
	struct subsys_private *sp = bus_to_subsys(bus);
	struct kobject *k;
	struct driver_private *priv;

	if (!sp)
		return NULL;

	k = kset_find_obj(sp->drivers_kset, name);   /* 在bus所管理的driver链表中,查找有没有name这个driver */
	subsys_put(sp);
	if (!k)
		return NULL;

	priv = to_driver(k);

	/* Drop reference added by kset_find_obj() */
	kobject_put(k); /* 把该driver的kobject引用计数减1,kset_find_obj函数对其+1了 */
	return priv->driver;  /* 通过driver里面的kobject返回driver */
}
EXPORT_SYMBOL_GPL(driver_find);

3.1.1、  遍历kset的kobject链表中有没有名为name的kobject

 lib/kobject.c

  
/**
 * kset_find_obj - search for object in kset.
 * @kset: kset we're looking in.
 * @name: object's name.
 *
 * Lock kset via @kset->subsys, and iterate over @kset->list,
 * looking for a matching kobject. If matching object is found
 * take a reference and return the object.
 */
struct kobject *kset_find_obj(struct kset *kset, const char *name)
{
	struct kobject *k;
	struct kobject *ret = NULL;
 
	spin_lock(&kset->list_lock);
    /* 查找方法很简单,依次遍历bus的driver链表每个driver的kobject的name,如果有相同的返回对应的kobject */
	list_for_each_entry(k, &kset->list, entry) {    
		if (kobject_name(k) && !strcmp(kobject_name(k), name)) {
			ret = kobject_get(k);    /* 记住,找到的话,这里对其引用计数+1了 */
			break;
		}
	}
	spin_unlock(&kset->list_lock);
	return ret;
}

上面步骤很简单,下图可以更形象的描述上面的过程:

3.2、 把driver放入bus的driver链表中

drivers/base/bus.c 

/**
 * bus_add_driver - Add a driver to the bus.
 * @drv: driver.
 */
int bus_add_driver(struct device_driver *drv)
{
	struct subsys_private *sp = bus_to_subsys(drv->bus);
	struct driver_private *priv;
	int error = 0;

	if (!sp)
		return -EINVAL;

	/*
	 * Reference in sp is now incremented and will be dropped when
	 * the driver is removed from the bus
	 */
	pr_debug("bus: '%s': add driver %s\n", sp->bus->name, drv->name);
 
        /* bus有自己的private,device有自己的private,driver也必须有自己的啊,他们的功能就是负责连接对方 */
	priv = kzalloc(sizeof(*priv), GFP_KERNEL);    
	if (!priv) {
		error = -ENOMEM;
		goto out_put_bus;
	}
    /* 初始化klist,以及填充dricer的private里面的内容 */
	klist_init(&priv->klist_devices, NULL, NULL);
	priv->driver = drv;
	drv->p = priv;
	priv->kobj.kset = bus->p->drivers_kset;        /* driver绑定bus(通过各自里面的privte) */
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	if (error)
		goto out_unregister;
    
    /* 把driver在bus的节点,加入到bus的driver链表的最后一个 */
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
 
    /* 前面算是已经把driver注册到对应的bus里面了 */
    
    /* 默认所有的bus都是开启autoprobe的,除非应用层给写0,关闭了 */
	if (drv->bus->p->drivers_autoprobe) {  
		if (driver_allows_async_probing(drv)) {    /* 查看driver的probe_type支不支持异步匹配 */
			pr_debug("bus: '%s': probing driver %s asynchronously\n",
				drv->bus->name, drv->name);
			async_schedule(driver_attach_async, drv);    /* 支持异步匹配,则重开一个线程,执行driver_attach_async函数,在那个线程中执行匹配 */
		} else {
			error = driver_attach(drv);        /* 不支持异步匹配,则直接在这个函数中匹配 */
			if (error)
				goto out_unregister;
		}
	}
	module_add_driver(drv->owner, drv);
 
	error = driver_create_file(drv, &driver_attr_uevent);
	if (error) {
		printk(KERN_ERR "%s: uevent attr (%s) failed\n",
			__func__, drv->name);
	}
	error = driver_add_groups(drv, bus->drv_groups);
	if (error) {
		/* How the hell do we get out of this pickle? Give up */
		printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
			__func__, drv->name);
	}
 
	if (!drv->suppress_bind_attrs) {
		error = add_bind_files(drv);
		if (error) {
			/* Ditto */
			printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
				__func__, drv->name);
		}
	}
 
	return 0;
 
out_del_list:
	klist_del(&priv->knode_bus);
out_unregister:
	kobject_put(&priv->kobj);
	/* drv->p is freed in driver_release()  */
	drv->p = NULL;
out_put_bus:
	subsys_put(sp);
	return error;
}

3.2.1、 异步匹配

可以看一下下面的,异步匹配和同步匹配是完全一样的代码,只不过异步匹配是多开一个线程(多核cpu有效),在里面指行匹配(这样可以不影响本cpu的效率,因为一般同一个bus上的device还是很多的,一个一个匹配要很久的。)

 static void driver_attach_async(void *_drv, async_cookie_t cookie)
{
	struct device_driver *drv = _drv;
	int ret;
 
	ret = driver_attach(drv);
 
	pr_debug("bus: '%s': driver %s async attach completed: %d\n",
		 drv->bus->name, drv->name, ret);
}

3.2.2、 通过driver匹配同一bus上的device

 drivers/base/dd.c

 /**
 * driver_attach - try to bind driver to devices.
 * @drv: driver.
 *
 * Walk the list of devices that the bus has on it and try to
 * match the driver with each one.  If driver_probe_device()
 * returns 0 and the @dev->driver is set, we've found a
 * compatible pair.
 */
int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

给出匹配函数,driver匹配同一总线下的device。注意对比上篇的bus_for_each_drv,比较异同。

 drivers/base/bus.c

 /**
 * bus_for_each_dev - device iterator.
 * @bus: bus type.
 * @start: device to start iterating from.
 * @data: data for the callback.
 * @fn: function to be called for each device.
 *
 * Iterate over @bus's list of devices, and call @fn for each,
 * passing it @data. If @start is not NULL, we use that device to
 * begin iterating from.
 *
 * We check the return of @fn each time. If it returns anything
 * other than 0, we break out and return that value.
 *
 * NOTE: The device that returns a non-zero value is not retained
 * in any way, nor is its refcount incremented. If the caller needs
 * to retain this data, it should do so, and increment the reference
 * count in the supplied callback.
 */
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;
 
     /* 把总线上的device链表的头节点给i(注意头节点是不带有效信息的) */
	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while ((dev = next_device(&i)) && !error)    /* 从第一个device开始,调用fn函数,匹配driver和device */
		error = fn(dev, data);
	klist_iter_exit(&i);        /* 结束使用klist */
	subsys_put(sp);
	return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);

注意:一个驱动可能匹配到多个device,所以上面的匹配除非发生错误,否则就要匹配整个链表的device。

drivace匹配device函数,对比上篇的__device_attach_driver函数,结构个功能都是一样的。

drivers/base/base.h
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;
}


drivers/base/dd.c
static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;
	bool async = false;
	int ret;
 
	/*
	 * Lock device and try to bind to it. We drop the error
	 * here and always return 0, because we need to keep trying
	 * to bind to devices and some drivers will return an error
	 * simply if it didn't support the device.
	 *
	 * driver_probe_device() will spit a warning if there
	 * is an error.
	 */
 
	ret = driver_match_device(drv, dev);    /* 在本函数上面,使用bus的match函数匹配 */
	if (ret == 0) {                                             /* 为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 */                        /* 大于0,标明正在匹配(配上了,但还有一些操作要执行) */
 
 
    /* 到这里以及匹配到了,接下来就是要真正连接device和driver,以及执行probe函数了 */
 
	if (driver_allows_async_probing(drv)) {
		/*
		 * Instead of probing the device synchronously we will
		 * probe it asynchronously to allow for more parallelism.
		 *
		 * We only take the device lock here in order to guarantee
		 * that the dev->driver and async_driver fields are protected
		 */
		dev_dbg(dev, "probing driver %s asynchronously\n", drv->name);
		device_lock(dev); * 设备的parent锁定,不能执行睡眠,卸载之类的操作,否则下面driver_probe_device函数如果匹配上指行probe函数,就没parent了 */
		if (!dev->driver && !dev->p->async_driver) {
			get_device(dev);
			dev->p->async_driver = drv;
			async = true;
		}
		device_unlock(dev);
		if (async)
			async_schedule_dev(__driver_attach_async_helper, dev);
		return 0;
	}

	__device_driver_lock(dev, dev->parent);/* 要用device了,device肯定不能出乱子啊,否则删除了,这里的device接下来的操作就成野指针了 */
	driver_probe_device(drv, dev);/* 绑定device和driver,调用probe函数, */
	__device_driver_unlock(dev, dev->parent);

	return 0;
}

device和driver绑定,并指向probe函数。最终完成。

 

 

四、总结

4.1、为什么device匹配一次driver,又driver匹配一次divice?

在 Linux 内核中,设备和驱动程序的匹配过程涉及两个阶段:设备匹配驱动驱动匹配设备。这两个过程看似重复,但它们的目的和实现方式是不同的。

1. 设备匹配驱动(Device to Driver)

首先,内核需要确定一个设备应该由哪个驱动程序来管理。这是因为系统中可能有多个设备,且每个设备可能由多个驱动程序支持,因此内核需要根据设备的特征(如设备ID、设备类型等)来选择合适的驱动程序。

  • 设备匹配驱动的过程:

    • 内核会遍历所有已注册的驱动程序,检查每个驱动程序的设备支持范围。驱动程序通常会注册一个或多个 设备ID表(如 driver_device_table)。
    • 在这个阶段,内核会将设备的属性(如设备的 vendordevice ID、class 等)与驱动程序的设备支持表进行匹配,找到与设备相匹配的驱动程序。
  • 设备到驱动的匹配 过程类似于:

    1. 内核发现新设备,设备被创建并注册。
    2. 驱动程序通过设备ID表注册并提供它支持的设备信息。
    3. 内核在设备注册时,通过查找已注册的驱动程序,并通过设备ID或其他属性匹配找到最合适的驱动程序。

2. 驱动匹配设备(Driver to Device)

驱动程序本身也需要确认自己能够处理哪个设备。驱动程序通过注册自己的设备ID表来告诉内核它能支持哪些设备。在设备驱动加载后,内核会反向查找,确保驱动程序能成功绑定到对应的设备。

  • 驱动匹配设备的过程:
    • 一旦内核决定某个驱动程序应该管理某个设备,它就会通过设备与驱动程序的匹配,启动设备驱动的绑定过程。
    • 驱动程序通过 probe() 函数与设备进行匹配。probe() 函数通常会检查设备的属性、功能等,进一步确认设备和驱动程序之间的兼容性。

在驱动匹配设备时,主要完成以下几个步骤:

  1. 驱动通过 driver->probe() 函数与设备进行具体的交互和验证。
  2. 驱动程序和设备之间的硬件资源(如内存、I/O 地址、IRQ 等)进行分配和初始化。
  3. 驱动程序会完成设备的初始化,并准备好为设备提供服务。

为什么需要两次匹配?

两次匹配的原因在于:

  1. 设备和驱动之间的松耦合:设备和驱动之间应该保持一定的独立性,设备不应该直接知道它应该由哪个驱动来管理。设备只关心它被正确创建、注册,并且可以由系统识别。
  2. 驱动注册时并不知道所有设备的具体情况:虽然设备在内核中注册时会有一个唯一标识符(如 dev_t),但设备的细节、功能等信息只有在驱动通过 probe 函数处理设备时才能获得。因此,驱动程序需要通过 probe 函数来决定是否能成功绑定到该设备。

匹配过程总结

  • 设备到驱动的匹配:内核通过设备的硬件信息(如设备ID)来选择一个可能的驱动程序。
  • 驱动到设备的匹配:驱动程序通过 probe() 函数进一步确认自己是否能够正确操作这个设备。

 

4.2、bus有自己的subsys_private ,device有自己的device_private ,driver也有自己的driver_private,他们之间是通过klist_node 强化版的链表?

在 Linux 内核中,bus, device, 和 driver 都有自己的私有数据结构(subsys_private, device_private, driver_private),它们通过链表结构(例如 klist_node)相互关联和管理。这样设计的目的是为了使得这些不同的对象能够进行高效的管理和动态绑定,同时保持相对的松耦合性。

1. subsys_private (总线私有数据)

bus 是 Linux 中的一个概念,表示硬件设备的总线(例如,PCI、USB、I2C 等)。每个总线都可以包含多个设备和驱动程序。bus 通常会有一个 subsys_private 字段,这个字段用于存储与该总线相关的私有数据。

  • subsys_private 允许每个总线类型(如 PCI 或 USB)存储特定于该总线的状态信息、资源和管理信息。这使得内核能够管理不同总线类型的设备,并为它们提供灵活的处理方式。

  • 每个 bus 还可以有一个 klist,它用来存储与该总线相关的设备列表。这些设备是通过 klist_node 连接的。

2. device_private (设备私有数据)

device_private 是设备对象的私有数据区域。每个设备在内核中都是一个 device 结构体,该结构体包含有关硬件设备的信息(例如,设备ID、状态、总线类型、驱动程序等)。device_private 使得每个设备能够存储自己特定的状态信息或配置。

  • 设备通过 klist_node 与其他设备或驱动程序相关联。klist_node 是一个链表节点结构,允许设备对象在相关链表中进行插入和删除。具体来说,设备通常会被添加到一个总线(bus)的设备链表中,或者一个驱动(driver)的设备链表中。

3. driver_private (驱动程序私有数据)

driver_private 是驱动程序对象的私有数据区域。每个驱动程序通过 driver 结构体进行管理,该结构体包含了驱动程序的核心信息(例如,支持的设备类型、设备ID、驱动程序的初始化函数等)。driver_private 用于存储驱动程序相关的特定数据,如初始化状态、控制信息等。

  • 驱动程序通过 klist_node 将设备绑定到自己。每个驱动程序可能会管理多个设备,驱动和设备通过双向链表(klist)相互连接。

4. klist_node 强化版链表

klist_node 是内核中用来管理链表的一种结构,它提供了更高效、更灵活的链表操作,比标准的 list_head 更具特定的功能。

  • klist_node 通常用于在总线、设备、驱动程序之间建立关系。它能够高效地管理这些对象之间的连接,因为它不仅包含指向前后节点的指针,还可以包含每个节点的特定数据。

  • klistklist_node 的集合,它用于表示一个对象集合。例如,一个 bus 结构体会有一个 klist 来管理它下属的设备,或者一个 driver 结构体会有一个 klist 来管理它所绑定的设备。

这种方式的好处是:

  • 动态性:设备和驱动之间的绑定关系可以在运行时动态变化。例如,当设备被添加或移除时,相关的 klist_node 可以高效地插入或删除。
  • 高效性klist 提供了比普通链表更加优化的插入和删除操作,因此能更好地支持复杂的设备和驱动管理。

5. 小结

  • busdevicedriver 各自有自己的私有数据结构(subsys_private, device_private, driver_private),这些结构体用于存储特定的信息和状态。
  • 它们之间通过 klist_node 进行关联,klist_node 是强化版的链表节点,能够高效管理不同对象间的连接。
  • 通过 klist,总线、设备和驱动可以灵活地管理彼此的关系,从而实现更动态和高效的硬件资源管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值