linux内核之class介绍(三)

前两节介绍了class,device结构体的成员,在这一节主要介绍class和device的关键函数.
1.classes_init()
系统调用路径:
start_kernel()->>rest_init()->>kernel_init()->>do_basic_setup()->>driver_init()->>classes_init();
函数源码:

int __init classes_init(void)
{
	// 创建/sys/class目录
	class_kset = kset_create_and_add("class", NULL, NULL);
	if (!class_kset)
		return -ENOMEM;
	return 0;
}

说明:
该函数动态创建一个kset(class_kset ),并把它注册进系统.会在sysfs文件系统创建sys/class目录

2.class_create()
调用路径:
am335x_gpio_init()->>class_create();
驱动模块初始化函数中调用.

函数源码:

/* This is a #define to keep the compiler from merging different
 * instances of the __key variable */
#define class_create(owner, name)		\
({						\
	static struct lock_class_key __key;	\
	__class_create(owner, name, &__key);	\
})

struct class *__class_create(struct module *owner, const char *name,
			     struct lock_class_key *key)
{
	struct class *cls;
	int retval;

	// 动态创建class
	cls = kzalloc(sizeof(*cls), GFP_KERNEL);
	if (!cls) {
		retval = -ENOMEM;
		goto error;
	}

	cls->name = name;
	cls->owner = owner;
	cls->class_release = class_create_release;
	
	// 注册该class
	retval = __class_register(cls, key);
	if (retval)
		goto error;

	return cls;

error:
	kfree(cls);
	return ERR_PTR(retval);
}

int __class_register(struct class *cls, struct lock_class_key *key)
{
	struct subsys_private *cp;
	int error;

	pr_debug("device class '%s': registering\n", cls->name);

	//动态创建subsys_private结构体
	cp = kzalloc(sizeof(*cp), GFP_KERNEL);
	if (!cp)
		return -ENOMEM;
	//初始化subsys_private中的设备链表klist_devices
	klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
	//初始化subsys_private中的接口链表class_interfaces
	INIT_LIST_HEAD(&cp->class_interfaces);
	kset_init(&cp->glue_dirs);
	__mutex_init(&cp->class_mutex, "struct class mutex", key);

	//设置class名称为subsys_private.subsys.kobj的名字,用于创建/sys/class/name目录
	error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
	if (error) {
		kfree(cp);
		return error;
	}

	/* set the default /sys/dev directory for devices of this class */
	//设置class.dev_kobj成员指向sysfs_dev_char_kobj,该kobject表示/sys/dev/char目录
	if (!cls->dev_kobj)
		cls->dev_kobj = sysfs_dev_char_kobj;// sys/dev/char

#if defined(CONFIG_BLOCK)
	/* let the block class directory show up in the root of sysfs */
	if (!sysfs_deprecated || cls != &block_class)
		cp->subsys.kobj.kset = class_kset; //class_kset在classes_init()中创建,表示/sys/class目录
#else
	cp->subsys.kobj.kset = class_kset;
#endif
	cp->subsys.kobj.ktype = &class_ktype;
	cp->class = cls;
	cls->p = cp; // 设置p指针

	// 创建/sys/class/name目录
	error = kset_register(&cp->subsys);
	if (error) {
		kfree(cp);
		return error;
	}
	// 创建class自己的属性文件class_attrs,这里好像没有,呵呵
	error = add_class_attrs(class_get(cls));
	class_put(cls);
	return error;
}

说明:
1).动态创建class
2).注册该class
a.动态创建subsys_private结构体
b.初始化subsys_private中的设备链表klist_devices
c.初始化subsys_private中的接口链表class_interfaces
d.设置class名称为subsys_private.subsys.kobj的名字,用于创建/sys/class/name目录
e.设置class.dev_kobj成员指向sysfs_dev_char_kobj,该kobject表示/sys/dev/char目录
f.创建/sys/class/name目录
g.创建class自己的属性文件class_attrs,如果有

3.device_create
调用路径:
am335x_gpio_init()->>device_create();
class_dev = device_create(gpio_class,NULL,dev,0,“am335x_gpio”)
驱动模块初始化函数中调用.
函数源码:

struct device *device_create(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_vargs(class, parent, devt, drvdata, fmt, vargs);
	va_end(vargs);
	return dev;
}

说明:
1).va_start(),va_end()函数的用法在后面会讲解
2).该函数功能解析参数,调用device_create_vargs().

struct device *device_create_vargs(struct class *class, struct device *parent,
				   dev_t devt, void *drvdata, const char *fmt,
				   va_list args)
{
	struct device *dev = NULL;
	int retval = -ENODEV;

	if (class == NULL || IS_ERR(class))
		goto error;

	// 动态创建device
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		retval = -ENOMEM;
		goto error;
	}
	// 初始化device
	dev->devt = devt;
	dev->class = class;
	dev->parent = parent;// NULL
	dev->release = device_create_release;
	dev_set_drvdata(dev, drvdata); // drvdata = 0

	// 设置dev->kobj.name = "am335x_gpio"
	retval = kobject_set_name_vargs(&dev->kobj, fmt, args); 
	if (retval)
		goto error;

	// 调用device_register()
	retval = device_register(dev);
	if (retval)
		goto error;

	return dev;

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

说明:
1). 动态创建device
2). 初始化device
3).设置dev->kobj.name = “am335x_gpio”
4).调用device_register()

int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}
void device_initialize(struct device *dev)
{
	dev->kobj.kset = devices_kset; // devices_kset指向sys/devices目录的kset
	kobject_init(&dev->kobj, &device_ktype);//A top-level, abstract class from which other classes are derived.
	INIT_LIST_HEAD(&dev->dma_pools);/* dma pools (if dma'ble) */
	mutex_init(&dev->mutex);/* mutex to synchronize calls to its driver.*/
	lockdep_set_novalidate_class(&dev->mutex);
	spin_lock_init(&dev->devres_lock);//Spinlock to protect the resource of the device.
	INIT_LIST_HEAD(&dev->devres_head);//The resources list of the device.
	device_pm_init(dev);//For device power management.
	set_dev_node(dev, -1);// CONFIG_NUMA未定义,所以该函数是空函数
}
int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct class_interface *class_intf;
	int error = -EINVAL;

	//增加device的引用计数器
	dev = get_device(dev);
	if (!dev)
		goto done;

	//检测device->p私有数据成员,如果空则需要初始化
	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()
	 */
	// 设置dev.kobject.name = dev->init_name
	if (dev->init_name) {
		dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}
	// 回读验证name是否已经设置
	if (!dev_name(dev)) {
		error = -EINVAL;
		goto name_error;
	}

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

	//增加device->parent的引用计数器,因为要增加设备节点
	parent = get_device(dev->parent);

	//设置dev->kobj.parent = parent,如果未设置parent,则创建到"sys/devices/virtual/class名"目录里
	setup_parent(dev, parent);

	/* use parent numa_node */
	if (parent)
		set_dev_node(dev, dev_to_node(parent));//CONFIG_NUMA未定义,空函数

	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
	//创建设备目录,如:/sys/devices/virtual/am335x_gpio/am335x_gpio
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error)
		goto Error;

	/* notify platform of device entry */
	//调用平台相关的通知函数
	if (platform_notify)
		platform_notify(dev);

	// 创建uevent属性文件
	error = device_create_file(dev, &uevent_attr);
	if (error)
		goto attrError;

	
	if (MAJOR(dev->devt)) {
		// 创建dev属性文件
		error = device_create_file(dev, &devt_attr);
		if (error)
			goto ueventattrError;
		//在sys/dev/char目录创建文件连接到dev->kobj目录,如: 251:0 -> ../../devices/virtual/am335x_gpio/am335x_gpio
		error = device_create_sys_dev_entry(dev);
		if (error)
			goto devtattrError;

		//创建设备节点,原理还需要再研究???
		devtmpfs_create_node(dev);
	}

	// 创建设备和class目录的符号连接,
	//1,创建文件名subsystem到class的连接,如:subsystem -> ../../../../class/am335x_gpio
	//2.创建class目录到当前目录的连接,如:am335x_gpio -> ../../devices/virtual/am335x_gpio/am335x_gpio
	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	// 创建设备相关的属性文件
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	// 添加总线相关的属性文件,并加入总线的设备链表(这里没有添加到bus)
	error = bus_add_device(dev);
	if (error)
		goto BusError;
	// 添加电源管理属性文件
	error = dpm_sysfs_add(dev);
	if (error)
		goto DPMError;
	//Add a device to the PM core's list of active devices.
	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的KOBJ_ADD事件
	kobject_uevent(&dev->kobj, KOBJ_ADD);

	// probe drivers for a new device
	bus_probe_device(dev);

	// 添加到设备树
	if (parent)
		klist_add_tail(&dev->p->knode_parent,
			       &parent->p->klist_children);
	// 添加设备到class,并调用相应的interfaces
	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->klist_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;
}

说明:
1).增加device的引用计数器
2).检测device->p私有数据成员,如果空则需要初始化
3).回读验证name是否已经设置
4).设置dev->kobj.parent = parent,如果未设置parent,则创建到"sys/devices/virtual/class名"目录里
5).创建设备目录,如:/sys/devices/virtual/am335x_gpio/am335x_gpio
6).调用平台相关的通知函数
7).创建uevent属性文件
8).创建dev属性文件
9).在sys/dev/char目录创建文件连接到dev->kobj目录,如: 251:0 -> …/…/devices/virtual/am335x_gpio/am335x_gpio
10).创建设备节点,原理还需要再研究???
11).创建设备和class目录的符号连接
a.创建文件名subsystem到class的连接,如:subsystem -> …/…/…/…/class/am335x_gpio
b.创建class目录到当前目录的连接,如:am335x_gpio -> …/…/devices/virtual/am335x_gpio/am335x_gpio
12).创建设备相关的属性文件
13).添加总线相关的属性文件,并加入总线的设备链表(这里没有添加到bus)
14).添加电源管理属性文件
15).添加device到pm(电源管理)链表
16).通知用户空间kobject的KOBJ_ADD事件
17). 添加到设备树
18).添加设备到class,并调用相应的interfaces

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值