Linux设备驱动模型

kobject原理与实例分析


1、Sysfs文件系统

"sysfsis a ram-based filesystem initially based on ramfs. It provides ameans to export kernel data structures, their attributes, and thelinkages between them to userspace.”

Linux2.6内核引入了sysfs文件系统。sysfs被看成是与proc同类别的文件系统。sysfs把连接在系统上的设备和总线组织成分级的文件,使其从用户空间可以访问到。

Sysfs被加载在 /sys/目录下,它的子目录包括:

1)Block:在系统中发现的每个块设备在该目录下对应一个子目录。每个子目录中

又包含一些属性文件,它们描述了这个块设备的各方面属性,如:设备大小。(loop块设备是使用文件来模拟的)

2)Bus:在内核中注册的每条总线在该目录下对应一个子目录,如: ide pci scsi usbpcmcia 其中每个总线目录内又包含两个子目录:devices和drivers ,devices目录包含了在整个系统中发现的属于该总线类型的设备,drivers目录包含了注册到该总线的所有驱动。

3)Class:将设备按照功能进行的分类,如/sys/class/net目录下包含了所有网络接口。

4)Devices:包含系统所有的设备。

5)Kernel:内核中的配置参数

6)Module:系统中所有模块的信息

7)Firmware:系统中的固件

8)Fs:描述系统中的文件系统

9)Power:系统中电源选项

2、K object

实现了基本的面向对象管理机制,是构成Linux2.6设备模型的核心结构。它与sysfs文件系统紧密相连,在内核中注册的每个kobject对象对应sysfs文件系统中的一个目录。类似于C++中的基类,Kobject常被嵌入于其他类型(即:容器)中。如bus,devices,drivers都是典型的容器。这些容器通过kobject连接起来,形成了一个树状结构。

structk object {

constchar

*name;

structlist_head

entry;

structkobject

*parent;//指向父对象

structkset

*kset;

structkobj_type

*ktype;

structsysfs_dirent *sd;

structkref

kref;//对象引用计数

unsignedint state_initialized:1;

unsignedint state_in_sysfs:1;

unsignedint state_add_uevent_sent:1;

unsignedint state_remove_uevent_sent:1;

};

3、Kobject操作

         1)voidkobject_init(struct kobject * kobj)初始化kobject结构

         2)intkobject_add(struct kobject * kobj)将kobject对象注册到Linux系统

         3)intkobject_init_and_add(struct kobject *kobj, struct kobj_type*ktype,struct kobject *parent, const char *fmt, ...)初始化kobject,并将其注册到linux系统


           4)voidkobject_del(struct kobject * kobj)从Linux系统中删除kobject对象

           5)structkobject *kobject_get(struct kobject*kobj)将kobject对象的引用计数加1,同时返回该对象指针。

           6)voidkobject_put(struct kobject *kobj)将kobject对象的引用计数减1,如果引用计数降为0,则调用release方法释放该kobject对象。

4、  Struct kobj_type


Kobject的ktype成员是一个指向kobj_type结构的指针, 该结构中记录了kobject对象的一些属性。

structkobj_type {

void(*release)(struct kobject *kobj);

structsysfs_ops *sysfs_ops;

structattribute **default_attrs;

};

release:用于释放kobject占用的资源,当kobject的引用计数为0时被调用。

5、Struct attribute

structattribute {

char *name; /*属性文件名*/

structmodule * owner;

mode_tmode; /*属性的保护位*/

};

struct attribute (属性):对应于kobject的目录下的一个文件,Name成员就是文件名。

6、Struct sysfs_ops

structsysfs_ops

{

ssize_t(*show)(struct kobject *, struct attribute *,char *);

ssize_t(*store)(struct kobject *,struct attribute *,const char *,

size_t);

};

           1)Show:当用户读属性文件时,该函数被调用,该函数将属性值存入buffer中返回给用户态;

           2)Store:当用户写属性文件时,该函数被调用,用于存储用户传入的属性值。

7、 kobject实例分析

       kobject.c源码

#include <linux/device.h> 
#include <linux/module.h> 
#include <linux/kernel.h> 
#include <linux/init.h> 
#include <linux/string.h> 
#include <linux/sysfs.h> 
#include <linux/stat.h> 
  
MODULE_AUTHOR("David Xie"); 
MODULE_LICENSE("Dual BSD/GPL");

 

/*声明release、show、store函数*/


void obj_test_release(struct kobject *kobject); 
ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf);
ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);

/*对应于kobject的目录下的一个文件,Name成员就是文件名*/  
struct attribute test_attr = { 
        .name = "kobj_config", 
        .mode = S_IRWXUGO, 
}; 
  
static struct attribute *def_attrs[] = { 
        &test_attr, 
        NULL, 
}; 
  
/kobject对象的操作 
struct sysfs_ops obj_test_sysops = 

        .show = kobj_test_show, 
        .store = kobj_test_store, 
}; 
 
/*定义kobject对象的一些属性及对应的操作*/ 
struct kobj_type ktype =  

        .release = obj_test_release, 
        .sysfs_ops=&obj_test_sysops, 
        .default_attrs=def_attrs, 
};

/*release方法释放该kobject对象*/  
void obj_test_release(struct kobject *kobject) 

        printk("eric_test: release .\n"); 
}

/*当读文件时执行的操作*/ 
ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf)

        printk("have show.\n"); 
        printk("attrname:%s.\n", attr->name); 
        sprintf(buf,"%s\n",attr->name); 
        return strlen(attr->name)+2; 
}

/*当写文件时执行的操作*/  
ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count)

        printk("havestore\n"); 
        printk("write: %s\n",buf); 
        return count; 

  
struct kobject kobj;//声明kobject对象
 
static int kobj_test_init(void) 

        printk("kboject test init.\n"); 
        kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");//初始化kobject对象kobj,并将其注册到linux系统
        return 0; 

  
static void kobj_test_exit(void) 

        printk("kobject test exit.\n"); 
        kobject_del(&kobj); 

  
module_init(kobj_test_init);

module_exit(kobj_test_exit);

kset原理与实例分析


1、 Kset

kset是具有相同类型的kobject的集合,在sysfs中体现成一个目录,在内核中用kset数据结构表示,定义为:

struct kset {

struct list_head list; //连接该kset中所有kobject的链表头

spinlock_t list_lock;

struct kobject kobj; //内嵌的kobject

struct kset_uevent_ops *uevent_ops; //处理热插拔事件的操作集合

}

2、 Kset操作

       1)int kset_register(struct kset *kset)

       在内核中注册一个kset

       2)void kset_unregister(struct kset *kset)

       从内核中注销一个kset

3、 热插拔事件

       在Linux系统中,当系统配置发生变化时,如:添加kset到系统;移动kobject, 一个通知会从内核空间发送到用户空间,这就是热插拔事件。热插拔事件会导致用户空间中相应的处理程序(如udev,mdev)被调用, 这些处理程序会通过加载驱动程序, 创建设备节点等来响应热插拔事件。

4、热插拔事件操作集合

      Struct kset_uevent_ops {

int (*filter)(struct kset *kset, struct kobject *kobj);

const char *(*name)(struct kset *kset, struct kobject *kobj);

int (*uevent)(struct kset *kset, struct kobject *kobj,

struct kobj_uevent_env *env);

}

kset_uevent_ops这三个函数什么时候调用?

当该kset所管理的kobject和kset状态发生变化时(如被加入,移动),这三个函数将被调用。(例:kobject_uevent调用)

这三个函数的功能是什么?

       1)filter:决定是否将事件传递到用户空间。如果filter返回0,将不传递事件。(例: uevent_filter)

       2)name:用于将字符串传递给用户空间的热插拔处理程序。

       3)uevent:将用户空间需要的参数添加到环境变量中。(例:dev_uevent)

5、 kset实例分析

    #include <linux/device.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/string.h>
    #include <linux/sysfs.h>
    #include <linux/stat.h>
    #include <linux/kobject.h>

    MODULE_AUTHOR("yinjiabin");
    MODULE_LICENSE("GPL");

    struct kset *kset_p;
    struct kset kset_c;

    /* 函数声明 */
    void obj_test_release(struct kobject *);
    ssize_t kobj_test_show(struct kobject *,struct attribute *,char *);
    ssize_t kobj_test_store(struct kobject *,struct attribute *,const char *,size_t);

    static struct attribute test_attr =
    {
            .name = "kobj_config",
            .mode = S_IRWXUGO,
    };

    static struct attribute *def_attrs[] =
    {
            &test_attr,
            NULL,
    };

    static struct sysfs_ops obj_test_sysops =
    {
            .show = kobj_test_show,
            .store = kobj_test_store,
    };

    static struct kobj_type ktype =
    {
            .release = obj_test_release,
            .sysfs_ops = &obj_test_sysops,
            .default_attrs = def_attrs,
};

    void obj_test_release(struct kobject *kobject)
    {
            printk("[kobj_test: release!]\n");
    }

    ssize_t kobj_test_show(struct kobject *kobject,struct attribute *attr,char *buf)
    {
            printk("Have show -->\n");
            printk("attrname: %s.\n",attr->name);
            sprintf("buf,%s\n",attr->name);
            return strlen(attr->name) + 2;
    }

    ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr, const char *buf,size_t size)
    {
            printk("Have store -->\n");
            printk("write: %s.\n",buf);
            return size;
    }

    static int kset_filter(struct kset *kset,struct kobject *kobj)
    {
        printk("Filter: kobj %s.\n",kobj->name);
        return 1;
    }

    static const char *kset_name(struct kset *kset,struct kobject *kobj)
    {
        static char buf[20];
        printk("Name kobj %s.\n",kobj->name);
        sprintf(buf,"%s","kset_name");
        return buf;
    }

    static int kset_uevent(struct kset *kset,struct kobject *kobj, struct kobj_uevent_env *env)


{
        int i = 0;
        printk("uevent: kobj %s.\n",kobj->name);

        while(i < env->envp_idx)
        {
            printk("%s.\n",env->envp[i]);
            i ++;
        }

        return 0;
    }

    static struct kset_uevent_ops uevent_ops =
    {
        .filter = kset_filter,
        .name = kset_name,
        .uevent = kset_uevent,
    };


    static int __init kset_test_init(void)
    {
        int ret = 0;

        printk("kset test init!\n");

        /* 创建并注册 kset_p */
        kset_p = kset_create_and_add("kset_p",&uevent_ops,NULL);

        /* 添加 kset_c 到 kset_p */
        kobject_set_name(&kset_c.kobj,"kset_c");
        kset_c.kobj.kset = kset_p;

        /* 对于较新版本的内核,在注册 kset 之前,需要  
             * 填充 kset.kobj 的 ktype 成员,否则注册不会成功 */
        kset_c.kobj.ktype = &ktype;
        ret = kset_register(&kset_c);

        if(ret)
            kset_unregister(kset_p);

        return 0;
    }

    static void __exit kset_test_exit(void)
    {
        printk("kset test exit!\n");
        kset_unregister(kset_p);
        kset_unregister(&kset_c);

    }

    module_init(kset_test_init);
    module_exit(kset_test_exit);


bus(总线)原理与实例分析


1、  设备模型
        随着技术的不断进步,系统的拓扑结构也越来越复杂,对智能电源管理、热插拔的支持要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,Linux 2.6内核提供了全新的内核设备模型。
2、  设备模型元素
        总线


        驱动
        设备
3、 总线
       总线是处理器和设备之间的通道,在设备模型中, 所有的设备都通过总线相连, 甚至是内部的虚拟“platform”总线。 在 Linux 设备模型中, 总线由 bus_type 结构表示, 定
义在 <linux/device.h>
4、 总线描述

struct bus_type {
const char *name; /*总线名称*/
struct bus_attribute *bus_attrs; /*总线属性*/
struct device_attribute *dev_attrs; /*设备属性*/
struct driver_attribute *drv_attrs; /*驱动属性*/
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*suspend_late)(struct device *dev, pm_message_t state);
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
struct dev_pm_ops *pm;
struct bus_type_private *p;
}

5、  总线注册/删除
        1)总线的注册使用:bus_register(struct bus_type * bus)
             若成功,新的总线将被添加进系统,并可在sysfs 的 /sys/bus 下看到。
        2)总线的删除使用:void bus_unregister(struct bus_type *bus)
6、  总线方法
        1)int (*match)(struct device * dev, struct device_driver * drv)
        当一个新设备或者驱动被添加到这个总线时,该方法被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零值。
        2)int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
        在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量。
7、 总线属性
       总线属性由结构bus_attribute 描述,定义如下:

struct bus_attribute {
struct attribute
attr;
ssize_t (*show)(struct bus_type *, char * buf);
ssize_t (*store)(struct bus_type *, const char *
buf, size_t count);
}

       1)int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
            创建属性
       2)void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
            删除属性

8、实例分析


       Bus_basic.c源码

 

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>

MODULE_AUTHOR("yinjiabin");
MODULE_LICENSE("Dual BSD/GPL");

static char *Version = "$Revision: 1.0 $";

 


/*当一个新设备或者驱动被添加到这个总线时,该方法被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零值。*/


static int my_match(struct device *dev, struct device_driver *driver)
{
        return !strncmp(dev->kobj.name, driver->name, strlen(driver->name));
}


/*声明总线*/
struct bus_type my_bus_type = {
        .name = "my_bus",  //总线名字
        .match = my_match, //总线match函数指针
};

static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
        return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}

/*内核代码中如此定义:#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store),

它将bus_attr_作为给定的name的前缀来创建总线的真正名称。对应下面的是bus_attr_version*/
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

/*模块加载函数*/
static int __init my_bus_init(void)
{
        int ret;

        /*注册总线*/
        ret = bus_register(&my_bus_type);
        if (ret)
                return ret;

        /*创建属性文件*/
        if (bus_create_file(&my_bus_type, &bus_attr_version))
                printk(KERN_NOTICE "Fail to create version attribute!\n");

        return ret;
}

/*模块卸载函数*/
static void my_bus_exit(void)
{
        bus_unregister(&my_bus_type);
}

module_init(my_bus_init);

module_exit(my_bus_exit);

9、 试验结果


device(设备)原理与实例分析


1、 设备描述
        Linux 系统中的每个设备由一个 struct device 描述:

struct device {

...... ...... ...... ...... ...... ......
struct kobject kobj;
char bus_id[BUS_ID_SIZE]; /*在总线上唯一标识该设备的字符串 */


struct bus_type /* 设备所在总线 */
*bus;
struct device_driver *driver; /*管理该设备的驱动*/
void *driver_data;
/*该设备驱动使用的私有数据成员 *
struct klist_node knode_class;
struct class *class;
struct attribute_group
**groups;
void (*release)(struct device *dev);

}

2、 设备注册
       1)int device_register(struct device *dev)
            注册设备
      2)void device_unregister(struct device *dev)
           注销设备
      **一条总线也是个设备,也必须按设备注册**
3、 设备属性
       设备属性由struct device_attribute 描述:

struct device_attribute
{

struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute
*attr,char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);

}


       1)int device_create_file(struct device *device, struct device_attribute * entry)
             创建属性
       2)void device_remove_file(struct device * dev, struct device_attribute * attr)
            删除属性
4、 实例分析

      1)Bus.c源码

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>

MODULE_AUTHOR("yinjiabin");
MODULE_LICENSE("Dual BSD/GPL");

static char *Version = "$Revision: 1.0 $";

 


/*当一个新设备或者驱动被添加到这个总线时,该方法被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零值。*/


static int my_match(struct device *dev, struct device_driver *driver)
{
        return !strncmp(dev->kobj.name, driver->name, strlen(driver->name));
}

static void my_bus_release(struct device *dev)
{
        printk(KERN_DEBUG "my bus release\n");
}

struct device my_bus = {
        .kobj.name   = "my_bus0",
        .release  = my_bus_release
};

/*声明总线*/
struct bus_type my_bus_type = {
        .name = "my_bus",  //总线名字
        .match = my_match, //总线match函数指针
};

EXPORT_SYMBOL(my_bus);      //导出my_bus
EXPORT_SYMBOL(my_bus_type); //导出my_bus_type


/*
 * Export a simple attribute.
 */
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
        return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}

/*内核代码中如此定义:#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store),

它将bus_attr_作为给定的name的前缀来创建总线的真正名称。对应下面的是bus_attr_version*/
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

/*模块加载函数*/
static int __init my_bus_init(void)
{
        int ret;

        /*注册总线*/
        ret = bus_register(&my_bus_type);
        if (ret)
                return ret;

        /*创建属性文件*/
        if (bus_create_file(&my_bus_type, &bus_attr_version))
                printk(KERN_NOTICE "Fail to create version attribute!\n");

        /*注册总线设备*/
        ret = device_register(&my_bus);
        if (ret)
                printk(KERN_NOTICE "Fail to register device:my_bus!\n");

        return ret;
}

/*模块卸载函数*/
static void my_bus_exit(void)
{
        device_unregister(&my_bus);
        bus_unregister(&my_bus_type);
}

module_init(my_bus_init);
module_exit(my_bus_exit);


      2)Device.c源码


#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>

MODULE_AUTHOR("yinjiabin");
MODULE_LICENSE("Dual BSD/GPL");

extern struct device my_bus;
extern struct bus_type my_bus_type;

/* Why need this ?*/
static void my_dev_release(struct device *dev)
{

}

struct device my_dev = {
        .init_name = "my_dev",
        .bus = &my_bus_type,   //指明设备所属的总线
        .parent = &my_bus,     //该设备的父设备,总线也是一种设备
        .release = my_dev_release,
};

static ssize_t mydev_show(struct device *dev,struct device_attribute *attr, char *buf)
{
        return sprintf(buf, "%s\n", "This is my device!");
}

static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);

static int __init my_device_init(void)
{
        int ret = 0;

        /* 初始化设备 */
        ///strncpy(my_dev->kobj.name, "my_dev", strlen(my_dev->name));

        /*注册设备*/
        device_register(&my_dev);

        /*创建属性文件*/
        device_create_file(&my_dev, &dev_attr_dev);

        return ret;

}

static void my_device_exit(void)
{
        device_unregister(&my_dev);
}

module_init(my_device_init);
module_exit(my_device_exit);

driver(驱动)原理与实例分析


1、 驱动描述
       驱动程序由struct device_driver 描述 :

struct device_driver {

const char *name; /*驱动程序的名字( 体现在 sysfs 中 )*/
struct bus_type *bus; /*驱动程序所在的总线*/


struct module
*owner;
const char
*mod_name;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
struct attribute_group **groups;
struct dev_pm_ops *pm;
struct driver_private *p;

}

2、 驱动注册/注册

        1)int driver_register(struct device_driver *drv)

            注册驱动
       2)void driver_unregister(struct device_driver *drv)

           注销驱动

3、 驱动属性
       驱动的属性使用struct driver_attribute 来描述:

struct driver_attribute {

struct attribute attr;
ssize_t (*show)(struct device_driver *drv,
char *buf);
ssize_t (*store)(struct device_driver *drv,
const char *buf, size_t count);

}

      1)int driver_create_file(struct device_driver * drv, struct driver_attribute * attr)
            创建属性
      2)void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr)
            删除属性
4、 实例分析
       driver.c源码

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>

MODULE_AUTHOR("David Xie");
MODULE_LICENSE("Dual BSD/GPL");

extern struct bus_type my_bus_type;

/*当驱动找到对应的设备时会执行该函数*/
static int my_probe(struct device *dev)
{
    printk("Driver found device which my driver can handle!\n");
    return 0;
}

static int my_remove(struct device *dev)
{
    printk("Driver found device unpluged!\n");
    return 0;
}

struct device_driver my_driver = {
        .name = "my_dev",
        .bus = &my_bus_type,
        .probe = my_probe,
        .remove = my_remove,
};


static ssize_t mydriver_show(struct device_driver *driver, char *buf)
{
        return sprintf(buf, "%s\n", "This is my driver!");
}

static DRIVER_ATTR(drv, S_IRUGO, mydriver_show, NULL);

static int __init my_driver_init(void)
{
        int ret = 0;

        /*注册驱动*/
        driver_register(&my_driver);

        /*创建属性文件*/
        driver_create_file(&my_driver, &driver_attr_drv);

        return ret;

}

static void my_driver_exit(void)
{
        driver_unregister(&my_driver);
}

module_init(my_driver_init);

module_exit(my_driver_exit);

5、 试验结果


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值