设备模型七(class)

  • 前言
    对于class来说,应用场合是非常多的,但是网上关于其讲解是非常少的,所以这篇要好好讲解这个class

  • 什么是class
    class,即类, 其实跟它的名字一样, 把类似的设备(有共同的属性)归结成一个类,比如tty,在这里,我们举一个小例子,从结果来分析, 什么是class
    这里写图片描述
    我们可以看到
    xx_dev1---->xx_dev4这里有4个设备,这4个设备,都属于类xx_class
    但是呢,它们之间是有区别的
    1.xx_dev1与xx_dev2,这两个设备,(它不是平台设备,比如i2c嵌入式里有些i2c是直接与mcu相连接,所以有些i2c是flatform设备),它是virtual设备,即虚拟设备
    在虚拟设备的范畴里, 比如tty,或者我们非常熟悉的GPIO,把他们都归结为虚拟设备
    即/sys/devices/virtual/tty/xx, /sys/devices/virtula/gpio/yy
    这里的tty与gpio就是class, 其中的xx,与yy就是归属于这个类中的设备,他们有共同的属性
    所以说, xx_dev1与xx_dev2是xx_class类的虚拟设备,其中 xx_dev1是xx_dev2的父设备
    因为归属类 xx_class,所以在/sys/class/xx_class生成链接
    2.xx_dev3与xx_dev4他们是platform设备,他们是xxx_platDev_ma的子设备

(这里插一句,真实的设备都在/sys/devices下,因为在device_register第一步初始化函数就指定了kset为/sys/devices, 所以要在此目录下确定设备的种类,比如/sys/devices/virtual/xx_class/xx_dev1(2),那就是虚拟设备了, 同理, xx_dev3 xx_dev4是paltform设备)

但是他们也归属类xx_class,换句话说具有xx_class的属性, 所以
①. 生成/sys/devices/platform/xx_class/xx_dev3(4),而不是/sys/devices/platform/xx_dev3(4)
②. 在/sys/class生成链接
我们看下/sys/class/xx_class
这里写图片描述
我们可以看到:
/sys/class/xx_class下 生成4个设备连接文件,连接到各自真实的地方
这里我们引出class存在的意思,方便操作,我们可以看到在/sys/devices中生成的设备kobj目录层级关系很繁琐,很难操作,有了class后,很容易管理

  • 例子解析
    本着共享研究的态度,给出上面例子中的源码,之后老样子,分析代码
    前篇的讲述中,描述了添加设备的脉络,其中有一个函数没有分析,setup_parent,这个函数是确定dev->kobj.parent的,就是说到底添加在sysfs的哪个目录下,这回要重点的说明一下,有了这个函数才有了上面例子中xx_dev1–>xx_dev4在sysfs中的合理位置
    (忘说了. 分析的设备模型都是基于 linux 3.2.0)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/kobject.h>
#include <linux/klist.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/atomic.h>
#include <asm/device.h>

MODULE_LICENSE("GPL");

static struct platform_device leds_gpio = {
	.name	= "xxx_platDev_ma",
	.id	= -1,
	.dev	= {
		.platform_data	= NULL,//&gpio_led_info,
	},
};
struct class *ma_class = NULL;
struct device *dev1;
struct device *dev2;
struct device *dev3;
struct device *dev4;

static int ma_class_init(void)
{
	int err;
 	printk(KERN_ALERT "class_init\n");

	err = platform_device_register(&leds_gpio); //过程0
	ma_class = class_create(THIS_MODULE, "xx_class");

	dev1 = device_create(ma_class, NULL, 0, NULL, "xx_dev1");//过程1
	dev2 = device_create(ma_class, dev1, 0, NULL, "xx_dev2");//过程2
	dev3 = device_create(ma_class, &(leds_gpio.dev), 0, NULL, "xx_dev3");//过程3
	dev4 = device_create(ma_class, &(leds_gpio.dev), 0, NULL, "xx_dev4");//过程4
	return 0;
}

static void ma_class_exit(void)
{
	printk(KERN_ALERT "Goodbye, class_init\n");
	platform_device_unregister(&leds_gpio);
	device_destroy(ma_class, 0);
	class_destroy(ma_class);
}

module_init(ma_class_init);
module_exit(ma_class_exit);

这里的device_create函数就是为了确认class与parent,内部在调用device_register函数
似乎之前没有提到这个函数,device_register函数就是调用了device_add函数,摁。。。
关于device_register的流程就不细说了,前面有介绍

  • setup_parent
static void setup_parent(struct device *dev, struct device *parent)
{
	struct kobject *kobj;
	kobj = get_device_parent(dev, parent);
	if (kobj)
		dev->kobj.parent = kobj; //最后会将dev->kobj.parent赋值
}

get_device_parent函数

static struct kobject *get_device_parent(struct device *dev,
					 struct device *parent)
{
	if (dev->class) { //判断dev->class是否存在
		static DEFINE_MUTEX(gdp_mutex);
		struct kobject *kobj = NULL;
		struct kobject *parent_kobj;
		struct kobject *k;

#ifdef CONFIG_BLOCK
		/* block disks show up in /sys/block */
		if (sysfs_deprecated && dev->class == &block_class) {
			if (parent && parent->class == &block_class)
				return &parent->kobj;
			return &block_class.p->subsys.kobj;
		}
#endif

		/*
		 * If we have no parent, we live in "virtual".
		 * Class-devices with a non class-device as parent, live
		 * in a "glue" directory to prevent namespace collisions.
		 */
		if (parent == NULL)
			parent_kobj = virtual_device_parent(dev); //判断1
		else if (parent->class && !dev->class->ns_type)
			return &parent->kobj;
		else
			parent_kobj = &parent->kobj;

		mutex_lock(&gdp_mutex);

		/* find our class-directory at the parent and reference it */
		spin_lock(&dev->class->p->glue_dirs.list_lock);
		list_for_each_entry(k, &dev->class->p->glue_dirs.list, entry)
			if (k->parent == parent_kobj) {
				kobj = kobject_get(k);
				break;
			}
		spin_unlock(&dev->class->p->glue_dirs.list_lock);
		if (kobj) {
			mutex_unlock(&gdp_mutex);
			return kobj;
		}

		/* or create a new class-directory at the parent device */
		k = class_dir_create_and_add(dev->class, parent_kobj);
		/* do not emit an uevent for this simple "glue" directory */
		mutex_unlock(&gdp_mutex);
		return k;
	}

	if (parent) //如果不存在那么就返回 parent->kobj
		return &parent->kobj;
	return NULL;
}

  • 过程0:
    直接注册设备leds_gpio,从上篇讨论来讲,他的父设备是/sys/devices/platform
    leds_gpio设备没有class,所以最后在sysfs生成
    即根据get_device_parent,返回的是platform->dev.kobj,所以生成目录
    /sys/devices/platform/xxx_platDev_ma
  • 过程1:
    创建xx_dev1设备, 类为xx_class,没指定parent,所以走get_device_parent中的判断1
static struct kobject *virtual_device_parent(struct device *dev)
{
	static struct kobject *virtual_dir = NULL;//注意这里是static,代表着virtual这个目录如果被建立的话,下回在建立这个目录就不用建立,根据下面的判断直接return

	if (!virtual_dir)
		virtual_dir = kobject_create_and_add("virtual",
						     &devices_kset->kobj);

	return virtual_dir;
}

即创建 /sys/class/virtual/ 返回的是virtual_dir这个kobj
之后调用class_dir_create_and_add

k = class_dir_create_and_add(dev->class, parent_kobj);//这里dev->class为xx_calss,parent_kobj为virtual

会生成 /sys/class/virtual/xx_class, 之后返回这个kobj,所以最后
返回到函数setup_parent

static void setup_parent(struct device *dev, struct device *parent)
{
	struct kobject *kobj;
	kobj = get_device_parent(dev, parent);
	if (kobj)
		dev->kobj.parent = kobj; //这里的kobj为virtual
}

最后生成的目录为
/sys/class/virtual/xx_class/xx_dev1
因为这个xx_dev1是有class的,所以在device_add的最后会加入到xx_class这个类的设备链表中, 并生成 /sys/class/xx_class/xx_dev1(链接文件)

  • 过程2忽略,没什么好说的

  • 过程3
    生成设备xx_dev3, 父设备为xxx_platDev_ma
    这里根据图关系,代码流程就说了,可以结合下图来看代码
    这里写图片描述

  • 过程4
    生成设备xx_dev4, 父设备为xxx_platDev_ma,属于类xx_class
    同上流程,最后会调用get_device_parent函数

get_device_parent
{
//---
//---
		list_for_each_entry(k, &dev->class->p->glue_dirs.list, entry)//1
			if (k->parent == parent_kobj) { //2
				kobj = kobject_get(k);
				break;
			}
		spin_unlock(&dev->class->p->glue_dirs.list_lock);
		if (kobj) {
			mutex_unlock(&gdp_mutex);
			return kobj;
		}
//---
//---
}

上面代码中1处dev->class(xx_class)->p->glue_dirs.list中(即k)根据上面的图有两个xx_class,分别来源于不同的路径
1./sys/devices/platform/xxx_platDev_ma/xx_class(k)
2./sys/devices/platform/xxx_platDev_mb/xx_class(k)

k->parent是xxx_platDev_ma或是xxx_platDev_mb
这里代码中2处parent_kobj是xxx_platDev_ma
所以根据循环,k->parent是xxx_platDev_ma是符合条件的,所以k就是/sys/devices/platform/xxx_platDev_ma/xx_class中的xx_class
最后在此xx_class下生成xx_dev4

注意:device_create 这个函数 的作用是生成 dev, 结构体,在sys创建节点关系,然后在tmpfs创建节点
但是 并没有加入bus中,即没有加入到 bus的 dev链表中。(因为 dev->bus在此函数中没有被赋值)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值