您的位置:首页 > 运维架构 > Linux

linux设备驱动模型一上层容器之device

2012-05-19 09:17 489 查看
系统中任一设备都有一个device对象描述,我们先来看一下它在内核中的表示:
struct device {
	struct device		*parent;//父设备

	struct device_private	*p;//设备的私有数据

	struct kobject kobj;//表示该设备并把它链接到设备模型中的kobject
	const char		*init_name;//初始化名字
	struct device_type	*type;//设备的类型

	struct mutex		mutex;	//用于互斥访问

	struct bus_type	*bus;	//	标识了该设备链接在哪一个总线上
	struct device_driver *driver;	// 管理该设备的驱动程序,一个设备只能有一个驱动程序
	void		*platform_data;	//平台的特定数据 
	struct dev_pm_info	power;

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	u64		*dma_mask;	//指向设备DMA屏蔽字
	u64		coherent_dma_mask;//设备一致性DMA的屏蔽字

	struct device_dma_parameters *dma_parms;

	struct list_head	dma_pools;	//聚集的DMA缓冲池
	struct dma_coherent_mem	*dma_mem; //指向设备所使用的一致性DMA存储器描述符的指针
	struct dev_archdata	archdata;
#ifdef CONFIG_OF
	struct device_node	*of_node;
#endif

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */

	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 device_private {
	struct klist klist_children;//该设备的子设备链表
	struct klist_node knode_parent;//此结点将device链入device的parent的children链表
	struct klist_node knode_driver;//此结点将device链入device对应的driver的devices链表
	struct klist_node knode_bus;//此结点将device链入到对应的bus的devices链表
	void *driver_data;//设备驱动程序使用的私有数据
	struct device *device;//指向与之相关的device
};


可以看到device_private 的作用主要是把device链接入n个链表,以方便查找设备。
有关device,最重要的一个函数就是device_register,它的作用主要是注册设备驱动程序,搜寻驱动drv对应的设备,并与之关联。
int device_register(struct device *dev)
{
	device_initialize(dev);//初始化dev
	return device_add(dev); //添加dev
}
这个函数比较简单,主要是调用另外两个函数进行设备的初始化和添加操作,首先看下device_initialize
void device_initialize(struct device *dev)
{
	dev->kobj.kset = devices_kset;//设置设备的kobject所属集合,devices_kset其实在第一层,sys/devices/
	kobject_init(&dev->kobj, &device_ktype);  //初始化设备的kobject
	INIT_LIST_HEAD(&dev->dma_pools); //初始化设备的DMA池,用于传递大数据
	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);// 此函数初始化device的power域
	set_dev_node(dev, -1);
}
devices_kset是所有dev的kset,也就是所有dev都被链接在该kset下,这个kset是在设备初始化的时候创建的

int __init devices_init(void)
{
	devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
	......

 char_kobj_err:
	kobject_put(sysfs_dev_block_kobj);
 block_kobj_err:
	kobject_put(dev_kobj);
 dev_kobj_err:
	kset_unregister(devices_kset);
	return -ENOMEM;
}
parent=NULL,所以生成/sys/devices目录
void device_initialize(struct device *dev)
{
	dev->kobj.kset = devices_kset;//设置设备的kobject所属集合,devices_kset其实在第一层,sys/devices/
	kobject_init(&dev->kobj, &device_ktype);  //初始化设备的kobject
	INIT_LIST_HEAD(&dev->dma_pools); //初始化设备的DMA池,用于传递大数据
	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);// 此函数初始化device的power域
	set_dev_node(dev, -1);
}
device_initialize对设备结构进行初始化,这里的ktype是device_ktype,kset是devices_kset

接着调用device_add添加到驱动模型中去。

int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct class_interface *class_intf;
	int error = -EINVAL;

	dev = get_device(dev);//增加设备的kobject的引用计数
	if (!dev)
		goto done;

	if (!dev->p) {
		error = device_private_init(dev);//初始化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_name函数获取
		dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}

	if (!dev_name(dev)) {
		error = -EINVAL;
		goto name_error;
	}

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

	parent = get_device(dev->parent);//返回父节点,增加父节点引用计数,如果有返回,没有返回NULL
	setup_parent(dev, parent);//以上层devices为准重设dev->kobj.parent

	/* use parent numa_node */
	if (parent)
		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); //设置dev->kobj的名字和父对象,并建立相应目录
	if (error)
		goto Error;

	/* notify platform of device entry */
	if (platform_notify)
		platform_notify(dev);
	
	error = device_create_file(dev, &uevent_attr); //建立uevent属性文件
	if (error)
		goto attrError;

	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &devt_attr);//在sys下产生dev属性文件
		if (error)
			goto ueventattrError;

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

		devtmpfs_create_node(dev);
	}
	//建立subsystem链接文件连接到所属class
	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	//添加dev的属性描述文件
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	error = bus_add_device(dev); //添加链接文件至所属bus
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev); //添加power文件
	if (error)
		goto DPMError;
	device_pm_add(dev);

	/* Notify clients of device addition.  This call must come
	 * after dpm_sysf_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);//向用户空间发出KOBJ_ADD 事件
	bus_probe_device(dev);//检测驱动中有无适合的设备进行匹配,现在只添加了设备,还没有加载驱动,所以不会进行匹配
	if (parent)
		klist_add_tail(&dev->p->knode_parent, //把该设备的节点挂到其父节点的链表
			       &parent->p->klist_children);

	if (dev->class) {
		mutex_lock(&dev->class->p->class_mutex);
		/* tie the class to the device */
		klist_add_tail(&dev->knode_class,
			       &dev->class->p->class_devices);

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf,
				    &dev->class->p->class_interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->class_mutex);
	}
done:
	put_device(dev);
	return error;
 DPMError:
	bus_remove_device(dev);
 BusError:
	device_remove_attrs(dev);
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	if (MAJOR(dev->devt))
		devtmpfs_delete_node(dev);
	if (MAJOR(dev->devt))
		device_remove_sys_dev_entry(dev);
 devtattrError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &devt_attr);
 ueventattrError:
	device_remove_file(dev, &uevent_attr);
 attrError:
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	kobject_del(&dev->kobj);
 Error:
	cleanup_device_parent(dev);
	if (parent)
		put_device(parent);
name_error:
	kfree(dev->p);
	dev->p = NULL;
	goto done;
}
这里看一下设备匹配:

void bus_probe_device(struct device *dev)//匹配driver以及初始化过程
{
	struct bus_type *bus = dev->bus;
	int ret;

	if (bus && bus->p->drivers_autoprobe) {//设置了自动匹配初始化那么就开始匹配
		ret = device_attach(dev);
		WARN_ON(ret < 0);
	}
}
device_attach进行匹配操作。

int device_attach(struct device *dev)
{
	int ret = 0;

	device_lock(dev);
	if (dev->driver) {//默认指定了driver就直接绑定
		ret = device_bind_driver(dev);
		if (ret == 0)
			ret = 1;
		else {
			dev->driver = NULL;
			ret = 0;
		}
	} else {//没有指定就进行遍历匹配
		pm_runtime_get_noresume(dev);
		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
		pm_runtime_put_sync(dev);
	}
	device_unlock(dev);
	return ret;
}
device_bind_driver进行驱动的绑定。

int device_bind_driver(struct device *dev)
{
	int ret;
	//将driver和dev使用link,链接到一起,使他们真正相关
	ret = driver_sysfs_add(dev);
	if (!ret)
		driver_bound(dev);//将私有成员的driver节点挂到了driver的设备链表
	return ret;
}
driver_sysfs_add建立软链,它会在驱动目录下建立一个到设备的同名链接,并且在设备目录下建立一个名为 driver.到驱动的链接

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

	ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,//驱动目录下dev->kobj目录链接到dev->kobj
			  kobject_name(&dev->kobj));
	if (ret == 0) {
		ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,//在dev->kobj目录下的driver目录链接到其驱动目录
					"driver");
		if (ret)
			sysfs_remove_link(&dev->driver->p->kobj,
					kobject_name(&dev->kobj));
	}
	return ret;
}
driver_bound将私有成员的driver节点挂到了driver的设备链表

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

	klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); //将设备添加到driver的链表

	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_BOUND_DRIVER, dev);//通知bus上所有设备bound消息
}
回到device_attach,如果没有指定就进行遍历匹配
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;

	klist_iter_init_node(&bus->p->klist_drivers, &i,//和driver遍历device类似,从头开始遍历bus的driver链表
			     start ? &start->p->knode_bus : NULL); //发现一个driver就调用fn即__device_attach进行匹配
	while ((drv = next_driver(&i)) && !error)
		error = fn(drv, data);
	klist_iter_exit(&i);
	return error;
}
再来看下__device_attach
static int __device_attach(struct device_driver *drv, void *data)
{
	struct device *dev = data;

	if (!driver_match_device(drv, dev))//匹配条件检查
		return 0;

	return driver_probe_device(drv, dev);//进行真正的匹配操作
}
看下driver_match_device
static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)//bus的match存在就用bus的否则就直接匹配成功.match通常实现为首先扫描driver支持的id设备表,如果为NULL就用名字进行匹配
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
再来看下driver_probe_device,进行匹配操作
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);

	pm_runtime_get_noresume(dev);
	pm_runtime_barrier(dev);
	ret = really_probe(dev, drv);//调用really_probe
	pm_runtime_put_sync(dev);

	return ret;
}

首先调用查看是否已经注册:
static inline int device_is_registered(struct device *dev)
{
	return dev->kobj.state_in_sysfs; //在sysfs中表示已经注册
}

再调用 really_probe进行匹配
static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;

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

	dev->driver = drv; //device的driver初始化成该driver
	if (driver_sysfs_add(dev)) {//驱动目录下建立一个到设备的同名链接,并且在设备目录下建立一个名为 driver.到驱动的链接
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}

	if (dev->bus->probe) { //利用probe初始化设备
		ret = dev->bus->probe(dev); //如果bus的probe存在就用bus的,
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {//如果bus的不存在driver的存在
		ret = drv->probe(dev);//再用driver的
		if (ret)
			goto probe_failed;
	}

	driver_bound(dev);//调用driver_bound进行绑定
	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:
	devres_release_all(dev);
	driver_sysfs_remove(dev);
	dev->driver = NULL;

	if (ret != -ENODEV && ret != -ENXIO) {
		/* 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;
}
再看下driver_bound
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);

	klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); //将设备添加到driver的链表

	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_BOUND_DRIVER, dev);//通知bus上所有设备bound消息
}


到这里,设备就已经匹配成功了,这样就把设备和相应的驱动挂钩上了。

最后看一下他们的关系图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: