Linux设备模型

Overview

Linux sysfs 文件系统一般 mount /sys 目录。本文主要介绍 sysfs 文件系统中设备驱动模型的建立过程,内核版本 2.6.29

设备驱动信息主要用来表示设备以及驱动的层次关系,以及处理热插拔等。 /sys 中与之相关的数据有:

class              代表一类设备,比如 mtd net tty 等等

bus         总线,比如 PCI USB I2C

device     代表一个设备

driver      代表一个驱动

 

以下是一些 sysfs 中的全局变量:

//  /sys/class

struct kset * class_kset = kset_create_and_add("class", NULL, NULL);                 

//     /sys/bus

struct kset * bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);    

//     /sys/devices

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

 

1.      Class

1.1           class 的基本结构

struct class {

         const char            *name;

         struct module               *owner;

         struct class_attribute             *class_attrs;

         struct device_attribute           *dev_attrs;

         struct kobject                         *dev_kobj;

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

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

         struct pm_ops *pm;

         struct class_private *p;

};

struct class_private {

         struct kset class_subsys;

         struct list_head class_devices;

         struct list_head class_interfaces;

         struct kset class_dirs;

         struct mutex class_mutex;

         struct class *class;

};

class sysfs 中的层次由 struct class_private 决定。 struct class 只是 class_private 的封装。

struct class_private::class_subsys.kobj.kset = class_kset;  // 父目录为 /sys/class

struct class_private::class_subsys.kobj->name 代表这个 class /sys/class 中显示的名字

struct class::dev_attrs 为设备属性,往 class 中添加设备的时候,这些属性会自动添加到设备目录下。

1.2           新建 class

新建 class 有两种方法:静态创建和动态创建。

l         静态创建

static struct class i2c_adapter_class = {

       .owner                  = THIS_MODULE,

       .name                    = "i2c-adapter",

       .dev_attrs              = i2c_adapter_attrs,

       .class_attrs   = ...

};

int retval = class_register(&i2c_adapter_class)

 

l         动态创建

i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");

class_create 分配申请一块空间给 class 然后对 name owner class_release 函数赋值 并最终调用 class_register

class_register

根据 struct class 的值,设置 struct class_private

调用 add_class_attrs class 中添加属性。

l         class attrs

class 的属性最终是在 /sys/class// 目录下以文件的形式存在。用户程序可以直接对这些属性进行读写。如果要静态创建属性,可以在定义 class 时对 .class_attrs 域赋值 使其指向要添加的 attr 数组。如果要动态创建。可以通过函数 class_create_file 添加。

int class_create_file(struct class *cls, const struct class_attribute *attr);

 

如果是动态创建的属性,需要在模块卸载时调用 class_remove_file 释放。

如果是静态创建的属性,在调用 class_unregister 时会自动释放。

2.      Bus

2.1 bus 的基本结构

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 pm_ext_ops *pm;

         struct bus_type_private *p;

};

struct bus_type_private {

         struct kset subsys;

         struct kset *drivers_kset;

         struct kset *devices_kset;

         struct klist klist_devices;

         struct klist klist_drivers;

         struct blocking_notifier_head bus_notifier;

         unsigned int drivers_autoprobe:1;

         struct bus_type *bus;

};

class 类似, bus sysfs 中的显示由 struct bus_type_private 决定, struct bus_type 只是一个封装。

struct bus_type_private::subsys.kobj 代表 /sys/bus/ 目录。

struct bus_type_private::subsys.kobj.kset = bus_kset; // 默认父目录为 /sys/bus/

struct bus_type_private::subsys.kobj.ktype = &bus_ktype;  // bus 的属性操作

struct bus_type_private::subsys.kobj.name = /sys/bus/ 目录下显示的名字 >;

/sys/bus/ 目录,每创建成功一个 ,都会自动创建两个子目录 drivers devices ,分别代表连到此 的设备和驱动。在 drivers devices 子目录下,每新建一个 driver ,会把 struct bus_type 中的 drv_attrs 属性赋给那个 driver ;每创建一个 device ,会把 struct bus_type dev_attrs 赋给那个 device

2.2 新建 bus

struct bus_type i2c_bus_type = {

       .name             = "i2c",

       .dev_attrs              = i2c_dev_attrs,

       .match           = i2c_device_match,

       .uevent           = i2c_device_uevent,

       .probe            = i2c_device_probe,

       .remove          = i2c_device_remove,

       .shutdown      = i2c_device_shutdown,

       .suspend         = i2c_device_suspend,

       .resume          = i2c_device_resume,

       .bus_attr         = …

};

int ret = bus_register(&i2c_bus_type);

 

int bus_register(struct bus_type *bus);

分配内存给 struct bus_type_private;

根据 struct bus_type 的域设置 bus_type_private;

根据 .bus_attr 设置 bus 的属性,这些属性在 bus_unregister 时会被自动释放。( bus 属性也可通过 bus_create_file 动态添加,但所有动态添加的属性都要在卸载时通过 bus_remove_file 释放。)

 

3 Device

3.1 Device 的基本结构

struct device {

         struct klist           klist_children;

         struct klist_node knode_parent;     /* node in sibling list */

         struct klist_node knode_driver;

         struct klist_node knode_bus;

         struct device                 *parent;

         struct kobject kobj;

         char   bus_id[BUS_ID_SIZE];        /* position on parent bus */

         const char            *init_name; /* initial name of the device */

         struct device_type       *type;

         unsigned              uevent_suppress:1;

         struct semaphore          sem;  /* semaphore to synchronize calls to its driver.         */

         struct bus_type  *bus;          /* type of bus device is on */

         struct device_driver *driver;  /* which driver has allocated this device */

         void            *driver_data;       /* data private to the driver */

         void            *platform_data;  /* Platform specific data, device core doesn't touch it */

         struct dev_pm_info      power;

         u64             *dma_mask;        /* dma mask (if dma'able device) */

         u64             coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */

         struct device_dma_parameters *dma_parms;

         struct list_head   dma_pools;         /* dma pools (if dma'ble) */

         struct dma_coherent_mem    *dma_mem; /* internal for coherent mem

                                                    override */

         /* arch specific additions */

         struct dev_archdata      archdata;

         spinlock_t           devres_lock;

         struct list_head   devres_head;

         struct list_head   node;

         struct class          *class;

         dev_t                            devt; /* dev_t, creates the sysfs "dev" */

         struct attribute_group  **groups;   /* optional groups */

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

};

3.2 device_register

int device_register(struct device *dev) ;

此函数将 device 登记到 sysfs 中。在调用之前,需对 struct device 进行初始化。

struct device dev ;

dev. parent =  ;  // 父设备

dev.release =  ; // 登记释放 dev 时调用的回调函数

dev.class =  ;     // struct class

dev.bus =  ;         // 所属总线

然后调用 device_register(&dev) ;

int device_register(struct device *dev)

{

       device_initialize(dev);

       return device_add(dev);

}

device_initialize

做一些初始化工作, dev->kobj.kset = devices_kset ; // 代表 /sys/device 目录

device_add

// 设置 dev->kobj.parent ,即确定这个 dev 的父目录,详情见下节

setup_parent(dev, dev->parent);

// dev 挂到 dev->kobj.parent 代表的目录 如果没有 parent 父目录默认被设置成 dev->kobj.kset 代表的目录

       kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);

       device_create_file(dev, &uevent_attr) ;  // 给设备添加 uevent 属性

if (MAJOR(dev->devt))

device_create_file(dev, &devt_attr);     // 给设备添加 dev 属性(打印主从设备号)

device_create_sys_dev_entry(dev);      // 在设备的 class 下创建设备链接,比如 /sys/char /sys/block ,链接名字为 major:minor ;如果设备没有 class ,默认为 /sys/char 目录

device_add_class_symlinks(dev);        

// 在设备目录下建立 subsystem 链接,指向其所属的 class

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

                     // 在设备所属 class 目录下建立指向设备的链接,以设备名命名

sysfs_create_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev->bus_id);

// 如果父设备存在,在设备目录下建立指向父设备的链接,以 device 命名

                     sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");

              device_add_attrs(dev);

                     // 如果有 class ,把 class dev_attrs 属性都加上

                     device_add_attributes(dev, class->dev_attrs);

                     // 如果有 type ,把 type dev_attrs 属性都加上

                     device_add_groups(dev, type->groups);

                     // device groups 指向的属性都加上

                     device_add_groups(dev, dev->groups);

              bus_add_device(dev);    // 在设备有 bus 时有效

                     // 如果有 bus ,将 bus dev_attrs 都加上

                     device_add_attrs(bus_get(dev->bus), dev);

                     // bus 中建立指向设备的链接,以设备名命名

sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev->bus_id);

// 在设备中建立指向总线的链接,以 ”subsystem” 命名

sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "subsystem");

              dpm_sysfs_add(dev);    // 建立 power 属性

device_pm_add(dev);

kobject_uevent (&dev->kobj, KOBJ_ADD);

bus_attach_device(dev);

/* 如果总线支持自动检测设备( drivers_autoprobe == 1; 默认都支持),调用 device_attach(dev); device_attach 中,如果发现 dev 已经有 driver 与之关联,作一些 sysfs 的操作;如果没有,对总线中每一个驱动调用 __device_attach

__device_attach à driver_probe_device

driver_probe_device 先调用 bus match 函数,如果返回的是 match ,再调用 really_probe 。( bus match 函数在这里调用)

really_probe 先看 bus 有没有 probe 函数,如果有,调用 bus probe 。如果没有,调用 driver probe 函数(这里就是我们驱动程序的 probe 函数被调用的地方)。 */

if (bus->p->drivers_autoprobe)

       ret = device_attach(dev);

klist_add_tail(&dev->knode_parent, &parent->klist_children);

list_add_tail(&dev->node, &dev->class->p->class_devices);

list_for_each_entry(class_intf, &dev->class->p->class_interfaces, node)

       if (class_intf->add_dev)

              class_intf->add_dev(dev, class_intf);

 

3.3 device 的四种类型

sysfs 的设备模型中,有四种设备类型:

物理设备               parent 设备,没有 class

直接虚拟设备        parent 设备和 class parent 没有 class

间接虚拟设备        parent 设备和 class parent class

纯虚拟设备            没有 parent ,有 class (网络环回设备等)

 

以挂在 PCI 总线上的 I2C 适配器为例,首先需要创建一个设备,使其 bus 域指向 PCI bus ,这是一个物理设备;然后,以这个物理设备为父设备,创建一个 class I2C_adapter_class 的子设备,这个设备是直接虚拟设备,描述 I2C adapter 的功能。 I2C 子系统对每一个 I2C adapter ,又进一步创建了一个字符设备, I2C dev ,这个字符设备的 class 被设置为 I2C_device_class ,这里 I2C dev 就是一个间接虚拟设备。

除非是纯虚设备,否则任何一个虚拟设备向父设备追溯,一定能找到一个物理设备。

 

struct device 中有两个域, bus class ,这两个域不能同时有值。 如果 bus 域非空,说明这个 struct device 是挂在某个总线上,那么它必须是一个物理设备, class 域必须是 NULL ;如果 class 域非空,说明这是一个属于某个类的虚拟设备,那么它的 bus 域就必须是 NULL 。所以,在上节提到的两个函数, device_add_class_symlinks(dev) bus_add_device(dev) 中,虽然都创建了 subsystem 链接,但它们只有一个会起作用,否则系统会崩溃。

 

setup_parent 函数

void setup_parent(struct device *dev, struct device *parent) ;

这个函数用来决定 dev 被加到 sysfs 的哪个目录下。代码逻辑为:

       kobj = get_device_parent(dev, parent);

       if (kobj)

              dev->kobj.parent = kobj;

 

static struct kobject *get_device_parent(struct device *dev, struct device *parent) ;

这个函数会按照设备类型决定设备的父目录  :

l         如果是物理设备且有父设备 dev->class == NULL && dev->parent

       父目录就是父设备代表的目录

l         如果是直接虚拟设备 dev->class && dev->parent && dev->parent->class != NULL

在父设备代表的目录下新建一个子目录,名字为 dev->class->name 。然后把这个新建的目录作为设备的父目录  :/sys/devices//

l         如果是间接虚拟设备 dev->class && dev->parent && dev->parent->class == NULL

       父目录就是父设备代表的目录

l         如果是纯虚拟设备 dev->class && dev->parent == NULL

       父目录为 /sys/devices/virtual/

l         如果是物理设备且没有父设备 dev->class == NULL && dev->parent == NULL

    本函数不设置父目录,返回 NULL 。但由于此函数返回后会继续调用 kobject_add 所以父目录会设置成 dev->kobj->kset 代表的目录 也就是一开始 device_initialize 函数里 设置的 /sys/devices 目录。可以看出这是物理 root 设备,比如 platform /sys/devices/platform )。

 

 

4.    Driver

4.1 struct device_driver 基本结构

struct device_driver {

         const char            *name;

         struct bus_type            *bus;

         struct module               *owner;

         const char                    *mod_name;        /* used for built-in modules */

         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 pm_ops *pm;

         struct driver_private *p;

};

 

struct driver_private {

         struct kobject kobj;

         struct klist klist_devices;

         struct klist_node knode_bus;

         struct module_kobject *mkobj;

         struct device_driver *driver;

};

4.2 driver_register

driver_register driver 注册到 sysfs 系统中,在注册之前需要对 driver 进行初始化

struct device_driver driver = {

       .name = ;

       .bus = ;

       .probe = ;                // 探测设备

       .remove = ;           // 移除设备

       .suspend = ;         // 挂起设备 进入低功耗状态)

       .resume = ;           // 运行设备(从低功耗状态恢复)

};

 

int driver_register(struct device_driver *drv)              // driver 登记到 sysfs 系统中

       bus_add_driver(drv);

       driver_add_groups(drv, drv->groups);

 

int bus_add_driver(struct device_driver *drv)

       // 分配并初始化 struct driver_private

       。。。

       priv->kobj.kset = bus->p->drivers_kset;      // 父目录指向 bus drivers 子目录

       // driver 新建一个 kobj ,父目录在上一行的 kset 中指定了

       kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, “s%”, drv->name);

       /* 如果 drv bus 支持 autoprobe bus->p->drivers_autoprobe==1; 默认都是 1 ),调用 driver_attach driver_attach bus 中每一个 device ,调用 __driver_attach __driver_attach 调用 driver_probe_device driver_probe_device 的过程在上一章中的 device_register 函数中有描述。 */

       if (drv->bus->p->drivers_autoprobe)

              driver_attach(drv);

// driver 加入到 bus drivers 列表

       klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);             

module_add_driver (drv->owner, drv);

driver_create_file(drv, &driver_attr_uevent);              // 加入 uevent 属性

driver_add_attrs(bus, drv);    // bus drv_attrs 属性列表加入 driver 目录

add_bind_files(drv);              // 加入 bind unbind 属性(与热插拔有关)

kobject_uevent(&priv->kobj, KOBJ_ADD);

 

int driver_add_groups(struct device_driver *drv, struct attribute_group **groups)

group 中每一个 group ,调用 sysfs_create_group driver 下创建一个子目录,并将 group 里的属性作为文件加入到子目录中。

      

5.    sysfs

前面提到的 class bus drivers devices sysfs 系统中都是以目录表示;它们的属性则由文件表示。所有的目录、文件都是通过 sysfs 模块提供的函数创建和维护。这些 sysfs 的函数主要包括:

 

// kobj 代表的目录下新建一个文件

int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)

sysfs_add_file

       sysfs_add_file_mode

 

// kobj 代表的目录下创建链接,指向 target ,链接名为 name

int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name);

 

// 如果 grp->name 存在,在 kobj 代表的子目录下创建以 grp->name 命名的子目录

// kobj 代表的目录或新建的子目录下添加属性文件

int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)

 

// sysfs 中创建子目录

struct kset *kset_create_and_add(const char *name, struct kset_uevent_ops *uevent_ops,

struct kobject *parent_kobj)


来自:http://blog.csdn.net/walkingman321/archive/2010/09/30/5916693.aspx

你可能感兴趣的:(linux,struct,class,null,semaphore,module)