device的注册流程

本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记

一. device结构体描述
struct device {
	struct device		*parent;                //该设备的父设备,一般是该设备所从属的bus、controller等设备
	struct device_private	*p;                 //device的私有数据
	struct kobject kobj;                        //该数据结构对应的struct kobject
	const char		*init_name;                 //该设备的名称
	struct device_type	*type;
	struct semaphore	sem;
	struct bus_type	*bus;                       //该device属于哪个总线
	struct device_driver *driver;               //该device对应的device driver
	void		*platform_data;                 //一个指针,用于保存具体的平台相关的数据
	struct dev_pm_info	power;                  //电源管理相关的逻辑
#ifdef CONFIG_NUMA
	int		numa_node;
#endif
	u64		*dma_mask;
	u64		coherent_dma_mask;
	struct device_dma_parameters *dma_parms;
	struct list_head	dma_pools;	
	struct dma_coherent_mem	*dma_mem;
	/* arch specific additions */
	struct dev_archdata	archdata;
	dev_t			devt;                        //一个32位的整数,它由两个部分(Major和Minor)组成
	spinlock_t		devres_lock;
	struct list_head	devres_head;
	struct klist_node	knode_class;
	struct class		*class;                   //该设备属于哪个class
	const struct attribute_group **groups;	      //该设备的默认attribute集合
	void	(*release)(struct device *dev);
}
二. device模块初始化
int __init devices_init(void)
{
	devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
	if (!devices_kset)
		return -ENOMEM;
	dev_kobj = kobject_create_and_add("dev", NULL);
	if (!dev_kobj)
		goto dev_kobj_err;
	sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
	if (!sysfs_dev_block_kobj)
		goto block_kobj_err;
	sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
	if (!sysfs_dev_char_kobj)
		goto char_kobj_err;

	return 0;

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

1.创建一个devices_kset,parent = NULL, kset->kobj.kset = NULL,相当于新建文件夹/sys/devices。

devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);

uevent_ops = device_uevent_ops:

2.新建一个dev_kobj,相当于新建一个文件夹/sys/dev,然后在/sys/dev新建/sys/dev/block和/sys/dev/char文件夹。

dev_kobj = kobject_create_and_add("dev", NULL);
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
三. device引用计数

1.增加device引用计数

struct device *get_device(struct device *dev)
{
	return dev ? to_dev(kobject_get(&dev->kobj)) : NULL;
}

2.减小device引用计数

void put_device(struct device *dev)
{
	/* might_sleep(); */
	if (dev)
		kobject_put(&dev->kobj);
}
四. device_initialize函数
void device_initialize(struct device *dev)
{
	dev->kobj.kset = devices_kset;
	kobject_init(&dev->kobj, &device_ktype);
	INIT_LIST_HEAD(&dev->dma_pools);
	init_MUTEX(&dev->sem);
	spin_lock_init(&dev->devres_lock);
	INIT_LIST_HEAD(&dev->devres_head);
	device_init_wakeup(dev, 0);
	device_pm_init(dev);
	set_dev_node(dev, -1);
}

dev->kobj.kset = devices_kset,而devices_kset = device_uevent_ops,如下所示:

static struct kset_uevent_ops device_uevent_ops = {
	.filter =	dev_uevent_filter,
	.name =		dev_uevent_name,
	.uevent =	dev_uevent,
};

dev_uevent_filter函数过滤kobj上抛的消息。

static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
{
	struct kobj_type *ktype = get_ktype(kobj);

	if (ktype == &device_ktype) {
		struct device *dev = to_dev(kobj);
		if (dev->bus)
			return 1;
		if (dev->class)
			return 1;
	}
	return 0;
}

dev_uevent函数主要把device的信息保存在环境变量中,如主设备号,次设备号,设备名字,用户权限等。

static int dev_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env)
{
	struct device *dev = to_dev(kobj);
	int retval = 0;

	/* add device node properties if present */
	if (MAJOR(dev->devt)) {
		const char *tmp;
		const char *name;
		mode_t mode = 0;

		add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt));
		add_uevent_var(env, "MINOR=%u", MINOR(dev->devt));
		name = device_get_devnode(dev, &mode, &tmp);
		if (name) {
			add_uevent_var(env, "DEVNAME=%s", name);
			kfree(tmp);
			if (mode)
				add_uevent_var(env, "DEVMODE=%#o", mode & 0777);
		}
	}

	if (dev->type && dev->type->name)
		add_uevent_var(env, "DEVTYPE=%s", dev->type->name);

	if (dev->driver)
		add_uevent_var(env, "DRIVER=%s", dev->driver->name);

#ifdef CONFIG_SYSFS_DEPRECATED
	if (dev->class) {
		struct device *parent = dev->parent;

		/* find first bus device in parent chain */
		while (parent && !parent->bus)
			parent = parent->parent;
		if (parent && parent->bus) {
			const char *path;

			path = kobject_get_path(&parent->kobj, GFP_KERNEL);
			if (path) {
				add_uevent_var(env, "PHYSDEVPATH=%s", path);
				kfree(path);
			}

			add_uevent_var(env, "PHYSDEVBUS=%s", parent->bus->name);

			if (parent->driver)
				add_uevent_var(env, "PHYSDEVDRIVER=%s",
					       parent->driver->name);
		}
	} else if (dev->bus) {
		add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name);

		if (dev->driver)
			add_uevent_var(env, "PHYSDEVDRIVER=%s",
				       dev->driver->name);
	}
#endif

	/* have the bus specific function add its stuff */
	if (dev->bus && dev->bus->uevent) {
		retval = dev->bus->uevent(dev, env);
		if (retval)
			pr_debug("device: '%s': %s: bus uevent() returned %d\n",
				 dev_name(dev), __func__, retval);
	}

	/* have the class specific function add its stuff */
	if (dev->class && dev->class->dev_uevent) {
		retval = dev->class->dev_uevent(dev, env);
		if (retval)
			pr_debug("device: '%s': %s: class uevent() "
				 "returned %d\n", dev_name(dev),
				 __func__, retval);
	}

	/* have the device type specific fuction add its stuff */
	if (dev->type && dev->type->uevent) {
		retval = dev->type->uevent(dev, env);
		if (retval)
			pr_debug("device: '%s': %s: dev_type uevent() "
				 "returned %d\n", dev_name(dev),
				 __func__, retval);
	}

	return retval;
}

device_ktype定义如下:

static struct kobj_type device_ktype = {
	.release	= device_release,
	.sysfs_ops	=
	{
		.show	= dev_attr_show,
		.store	= dev_attr_store,
	},
};

static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
	struct device_attribute *dev_attr = to_dev_attr(attr);
	struct device *dev = to_dev(kobj);
	ssize_t ret = -EIO;

	if (dev_attr->show)
		ret = dev_attr->show(dev, dev_attr, buf);
	if (ret >= (ssize_t)PAGE_SIZE) {
		print_symbol("dev_attr_show: %s returned bad count\n",
				(unsigned long)dev_attr->show);
	}
	return ret;
}

static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
	struct device_attribute *dev_attr = to_dev_attr(attr);
	struct device *dev = to_dev(kobj);
	ssize_t ret = -EIO;

	if (dev_attr->store)
		ret = dev_attr->store(dev, dev_attr, buf, count);
	return ret;
}

对device属性文件的读写最终调用dev_attr->show和dev_attr->store两个函数。

五. 添加一个device
int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct class_interface *class_intf;
	int error = -EINVAL;

	dev = get_device(dev);
	if (!dev)
		goto done;

	if (!dev->p) {
		error = device_private_init(dev);
		if (error)
			goto done;
	}

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

	if (!dev_name(dev))
		goto name_error;

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

	parent = get_device(dev->parent);
	setup_parent(dev, 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);
	if (error)
		goto Error;

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

	error = device_create_file(dev, &uevent_attr);
	if (error)
		goto attrError;

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

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

		devtmpfs_create_node(dev);
	}

	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev);
	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);
	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))
		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;
}

1.增加dev的引用计数

dev = get_device(dev);

2.dev的私有数据的内存申请和部分初始化,dev->p表示dev的私有数据。

if (!dev->p) 
{
    error = device_private_init(dev);
}

3.设置dev->kobj的名称,设置为dev->init_name

dev_set_name(dev, "%s", dev->init_name);

4.获得dev的parent,同时将dev->parent引用计数加1

parent = get_device(dev->parent);

5.获取父kobj

setup_parent(dev, parent);

6.把内嵌的kobject注册到设备模型中将设备加入到kobject模型中,创建sys相关目录 ,目录名字为kobj->name

error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);

7.在dev目录下创建uevent文件

error = device_create_file(dev, &uevent_attr);

8.看一下下面这段代码

if (MAJOR(dev->devt)) {
	error = device_create_file(dev, &devt_attr);
	if (error)
		goto ueventattrError;
	error = device_create_sys_dev_entry(dev);
	if (error)
		goto devtattrError;
	devtmpfs_create_node(dev);
}

MAJOR(dev->devt表示主设备是否存在。
device_create_file(dev, &devt_attr)在本文件夹内创建dev文件。

static int device_create_sys_dev_entry(struct device *dev)
{
	struct kobject *kobj = device_to_dev_kobj(dev);
	int error = 0;
	char devt_str[15];

	if (kobj) {
		format_dev_t(devt_str, dev->devt);
		error = sysfs_create_link(kobj, &dev->kobj, devt_str);
	}

	return error;
}

static struct kobject *device_to_dev_kobj(struct device *dev)
{
	struct kobject *kobj;

	if (dev->class)
		kobj = dev->class->dev_kobj;
	else
		kobj = sysfs_dev_char_kobj;

	return kobj;
}

device_to_dev_kobj实际上得到的是上面的sysfs_dev_char_kobj(块设备除外),该kobj所在的目录为/sys/dev/char。
format_dev_t(devt_str, dev->devt)函数,得到devt_str = “主设备:次设备”这种字符串。
device_create_sys_dev_entry函数在/sys/dev/char目录新建名称为 “主设备:次设备”的软链接,链接到/sys/devices/设备所在的目录,如图所示:
在这里插入图片描述
devtmpfs_create_node函数是tmp文件系统相关的,暂时不看。

9.在class目录下创建符号链接,链接device所在的目录。

error = device_add_class_symlinks(dev);
	error = sysfs_create_link(&dev->kobj, &dev->class->p->class_subsys.kobj, "subsystem");
	error = sysfs_create_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev));

在这里插入图片描述

10.创建设备属性文件

error = device_add_attrs(dev);
	error = device_add_attributes(dev, class->dev_attrs);
	device_add_groups(dev, type->groups);
	device_add_groups(dev, dev->groups);

这里要注意,class->class_attrs在__class_register函数中注册,bus->dev_attrs在bus_add_device函数中注册。
class->class_attrs,class->dev_attrs,bus->bus_attrs,bus->dev_attrs这几个属性比较容易混淆。

11.添加设备到总线

error = bus_add_device(dev);
int bus_add_device(struct device *dev)
{
	struct bus_type *bus = bus_get(dev->bus);
	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 = sysfs_create_link(&bus->p->devices_kset->kobj,
						&dev->kobj, dev_name(dev));
		if (error)
			goto out_id;
		error = sysfs_create_link(&dev->kobj,
				&dev->bus->p->subsys.kobj, "subsystem");
		if (error)
			goto out_subsys;
		error = make_deprecated_bus_links(dev);
		if (error)
			goto out_deprecated;
		klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
	}
	return 0;

out_deprecated:
	sysfs_remove_link(&dev->kobj, "subsystem");
out_subsys:
	sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
out_id:
	device_remove_attrs(bus, dev);
out_put:
	bus_put(dev->bus);
	return error;
}

添加device的bus属性文件;
在/sys/bus/platform/devices目录下创建一个软链接,假设device是led,那么可以得到:
/sys/bus/platform/devices/led ------->/sys/devices/platform/led;
在/sys/devices/platform/led目录新建一个软链接,名字是“subsystem”,软链接到/sys/bus/platform;
将dev放入bus的device链表中;

12.dpm_sysfs_add和device_pm_add函数,电源管理相关的函数,暂时不看。

13.通知客户端,有新设备加入

if (dev->bus)
	blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev);

14.产生一个uevent事件,有设备加入

kobject_uevent(&dev->kobj, KOBJ_ADD);

15.给设备匹配驱动程序

bus_probe_device(dev);

16.将该设备添加到其父设备的子列表中

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

dev添加到class的klist_device链表

klist_add_tail(&dev->knode_class, &dev->class->p->class_devices);

通知有新设备加入,执行该dev的class_intf->add_dev()函数

if (class_intf->add_dev)
	class_intf->add_dev(dev, class_intf);
六. 创建一个device

创建一个device调用的函数是device_create

struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
	dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
		dev = kzalloc(sizeof(*dev), GFP_KERNEL);
		retval = device_register(dev);

先申请一个device,然后再调用device_register注册一个device。

七. 注册一个device

注册一个device调用函数device_register

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

先初始化device的部分成员,然后调用上面的device_add函数。
device_register注册一个已知的device,可以被device_create调用,也可以被其他各个模块调用注册一个device。

参考博客:https://blog.csdn.net/qq_16777851/article/details/81437352

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值