嵌入式软件开发之------浅谈linux驱动模型(三)class

Linux代码版本:linux3.0

开发板环境: tiny4412

导读:很多书籍介绍linux驱动模型的时候,经常会提到bus、device和driver的概念,对class介绍的不是太多。class,类,从名字上来理解也是将设备进行分类,只是和bus标准不同而已。例如一家公司,员工是一个个device,那么所属部门就是bus,公司要进行员工体检,仅仅从部门(bus)的角度分,并不是很好的办法,这个时候从所有的男员工和所有的女员工角度来管理,似乎更合适,这就是员工属于不同的性别(class),员工还是那些员工,从不同的角度看,员工会属于不同的群组,年龄、性别和岗位等信息只是从不同的角度分类方便进行管理。

    而class则是从设备类的角度进行区分,比如i2c设备、input设备、rtc设备和misc等等,sys/class目录下则是对设备的分类

[root@FriendlyARM class]# cd /sys/class/
[root@FriendlyARM class]# ls
android_usb    hwmon          misc           scsi_generic   tvout
bdi            i2c-adapter    mmc_host       scsi_host      ump
block          i2c-dev        net            sec            usb_device
firmware       ieee80211      power_supply   secgpio_check  vc
gpio           input          ppp            sound          video4linux
graphics       lcd            rc             spi_master     vtconsole
hdcp           leds           rfkill         spidev
hdmi_audio     lirc           rtc            switch
hidraw         mali           scsi_device    thermal
host_notify    mem            scsi_disk      tty

一、class结构体

struct class {
	const char		*name;
	struct module		*owner;

	struct class_attribute		*class_attrs;
	struct device_attribute		*dev_attrs;
	struct bin_attribute		*dev_bin_attrs;
	struct kobject			*dev_kobj;

	int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
	char *(*devnode)(struct device *dev, mode_t *mode);

	void (*class_release)(struct class *class);
	void (*dev_release)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	const struct kobj_ns_type_operations *ns_type;
	const void *(*namespace)(struct device *dev);

	const struct dev_pm_ops *pm;

	struct subsys_private *p;
};

const char *name;

class的名称,向bus一样也是要有个名字的

struct module *owner;

class所属的模块

struct class_attribute *class_attrs;

默认的class属性,还记得bus_type的bus_attribute吗

struct device_attribute *dev_attrs:

class下设备的属性,注册设备到内核时会在相应的目录下建立属性文件

struct bin_attribute *dev_bin_attrs;

class下设备的属性,只不过是二进制的形式,注册设备到内核时会在相应的目录下建立属性文件

struct kobject *dev_kobj;

用来确定该class设备在/sys/dev下创建链接的路径,或者是在 /sys/dev/char或者是在/sys/dev/block下,如果没有赋值

则默认创建在/sys/dev/char下

int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);

在注册device发出event时调用,其实就是添加部分环境变量

char *(*devnode)(struct device *dev, mode_t *mode);

返回device节点的相对路径,等代码中见到再详细分析

void (*class_release)(struct class *class);

class释放的时候调用

void (*dev_release)(struct device *dev);

device释放的时候调用

int (*suspend)(struct device *dev, pm_message_t state);

device休眠的时候调用

int (*resume)(struct device *dev);

device恢复的时候调用

const struct kobj_ns_type_operations *ns_type;

暂时不清楚用途,代码中遇到再详细分析

const void *(*namespace)(struct device *dev);

暂时不清楚用途,代码中遇到再详细分析

const struct dev_pm_ops *pm;

电源管理的操作集合,等分析电源管理的时候再详细分析其成员

struct subsys_private *p;

还记得bus_type的私有结构体吗,对,就是这个,class也包含了一个这样的结构体

二、代码分析

sys/class下有多种不同的设备类

[root@FriendlyARM class]# cd /sys/class/
[root@FriendlyARM class]# ls
android_usb    hwmon          misc           scsi_generic   tvout
bdi            i2c-adapter    mmc_host       scsi_host      ump
block          i2c-dev        net            sec            usb_device
firmware       ieee80211      power_supply   secgpio_check  vc
gpio           input          ppp            sound          video4linux
graphics       lcd            rc             spi_master     vtconsole
hdcp           leds           rfkill         spidev
hdmi_audio     lirc           rtc            switch
hidraw         mali           scsi_device    thermal
host_notify    mem            scsi_disk      tty

每个目录都是在代码中找到创建过程,下面是几个例子

static int __init rtc_init(void)
{
    rtc_class = class_create(THIS_MODULE, "rtc");
    .......
}
static int __init i2c_dev_init(void)
{
    ......


    i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
	
    ......
}
static int __init fbmem_init(void)
{
    ......
    fb_class = class_create(THIS_MODULE, "graphics");
    ......
}

都是调用的class_creat函数创建,过程也都基本一样,下面分析i2c_dev_class的创建

i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
                {						
                    static struct lock_class_key __key;	
                    __class_create(owner, name, &__key);	
                    {
                        struct class *cls;
                        int retval;
                    
                        cls = kzalloc(sizeof(*cls), GFP_KERNEL);
                        if (!cls) {
                            retval = -ENOMEM;
                            goto error;
                        }
                        /*赋值 i2c_dev_class->name = "i2c-dev"*/
                        cls->name = name;
                        /*赋值i2c_dev_class->owner = THIS_MODULE*/
                        cls->owner = owner;
                        cls->class_release = class_create_release;
                    
                        retval = __class_register(cls, key);
                                 {
                                    struct subsys_private *cp;
                                    int error;
                                
                                    pr_debug("device class '%s': registering\n", cls->name);
                                
                                    cp = kzalloc(sizeof(*cp), GFP_KERNEL);
                                    if (!cp)
                                        return -ENOMEM;
                                    /*初始化 i2c_dev_class->klist_devices
                                    i2c_dev_class->klist_devices->get = klist_class_dev_get
                                    i2c_dev_class->klist_devices->put = klist_class_dev_put*/
                                    klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
                                    {
                                        INIT_LIST_HEAD(&k->k_list);
                                        spin_lock_init(&k->k_lock);
                                        k->get = get;
                                        k->put = put;
                                    }
                                    
                                    INIT_LIST_HEAD(&cp->class_interfaces);
                                    kset_init(&cp->glue_dirs);
                                    __mutex_init(&cp->class_mutex, "struct class mutex", key);
                                    
                                    /*赋值 i2c_dev_class->subsys.kobj->name = "i2c-dev" */
                                    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在/sys/dev下创建链接文件的路径,还记得/sys/dev下char和blok目录吗
                                    sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
                                    sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);*/
                                    if (!cls->dev_kobj)
                                        cls->dev_kobj = sysfs_dev_char_kobj;
                                /*sysfs_deprecated由编译选项CONFIG_SYSFS_DEPRECATED决定,如果没有配置此选项,
                                sysfs_deprecated = 0,这时 block_class ->subsys.kobj.kset = NULL
                                就意味着在sys下创建block目录,这样就会觉得怪怪的,其它的class都创建在sys/class下
                                为什么就 block_class 可以放在sys目录下呢,2.6.22 开始就已标记为过时,为了保持向前的兼容所以才加了个
                                所以才加了个sysfs_deprecated(弃用)的配置项 */
                                 
                                #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;
                                #else
                                    /*赋值i2c_dev_class->p->subsys.kobj.kset = class_kset
                                    还记得sys下class目录的创建吗
                                    class_kset = kset_create_and_add("class", NULL, NULL);*/
                                    cp->subsys.kobj.kset = class_kset;
                                #endif
                                    /*赋值 i2c_dev_class->p->subsys.kobj.ktype = &class_ktype*/
                                    cp->subsys.kobj.ktype = &class_ktype;
                                    /*i2c_dev_class->p->class = i2c_dev_class,到此有没有觉得跟bus注册如出一辙? */
                                    cp->class = cls;
                                    cls->p = cp;
                                    /*为i2c_dev_class创建目录,由于i2c_dev_class->p->subsys.kobj.parent = NULL
                                    i2c_dev_class->p->subsys.kobj.kset = class_kset,所以将"i2c-dev"创建在了sys/class下*/
                                    error = kset_register(&cp->subsys);
                                    if (error) {
                                        kfree(cp);
                                        return error;
                                    }
                                   /*为i2c_dev_class在sys/class/i2c-dev下创建属性文件
                                   i2c_dev_class->class_attrs未赋值,也就是为NULL,所以不会 创建属性文件*/
                                    error = add_class_attrs(class_get(cls));
                                            {
                                                int i;
                                                int error = 0;
                                                /*i2c_dev_class->class_attrs未赋值,也就是为NULL,所以不会 创建属性文件 */
                                                if (cls->class_attrs) {
                                                    for (i = 0; attr_name(cls->class_attrs[i]); i++) {
                                                        error = class_create_file(cls, &cls->class_attrs[i]);
                                                        if (error)
                                                            goto error;
                                                    }
                                                }
                                            done:
                                                return error;
                                            error:
                                                while (--i >= 0)
                                                    class_remove_file(cls, &cls->class_attrs[i]);
                                                goto done;
                                            }
                                    class_put(cls);
                                    return error;
                                }
                        if (retval)
                            goto error;
                    
                        return cls;
                    
                    error:
                        kfree(cls);
                        return ERR_PTR(retval);
                    }
                }

上面代码就完成了/sys/class的i2c-dev目录的创建,并且对外输出 i2c_dev_class。在 device_create 中将会用到i2c_dev_class。

下面将进行分析device和driver的注册过程。


猜你喜欢

转载自blog.csdn.net/lujian186/article/details/80948539