Linux设备模型

Overview

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

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

class              代表一类设备,比如mtdnettty等等

bus         总线,比如PCIUSBI2C

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;

};

classsysfs中的层次由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然后对nameownerclass_release函数赋值并最终调用class_register

class_register

根据struct class的值,设置struct class_private

调用add_class_attrsclass中添加属性。

l         class attrs

class的属性最终是在/sys/class/<new 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类似,bussysfs中的显示由struct bus_type_private决定,struct bus_type只是一个封装。

struct bus_type_private::subsys.kobj代表/sys/bus/<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 = <bus/sys/bus/目录下显示的名字>;

/sys/bus/目录,每创建成功一个<bus>,都会自动创建两个子目录driversdevices,分别代表连到此<bus>的设备和驱动。在driversdevices子目录下,每新建一个driver,会把struct bus_type中的drv_attrs属性赋给那个driver;每创建一个device,会把struct bus_typedev_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释放。)

 

3Device

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 = <parent dev> ;  // 父设备

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

dev.class = <class> ;     // struct class

dev.bus = <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,把classdev_attrs属性都加上

                     device_add_attributes(dev, class->dev_attrs);

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

                     device_add_groups(dev, type->groups);

                     // devicegroups指向的属性都加上

                     device_add_groups(dev, dev->groups);

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

                     // 如果有bus,将busdev_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先调用busmatch函数,如果返回的是match,再调用really_probe。(busmatch函数在这里调用)

really_probe先看bus有没有probe函数,如果有,调用busprobe。如果没有,调用driverprobe函数(这里就是我们驱动程序的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设备和classparent没有class

间接虚拟设备       parent设备和classparentclass

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

 

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

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

 

struct device中有两个域,busclass,这两个域不能同时有值。如果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/<parent_name >/<class_name>

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

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

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

       父目录为/sys/devices/virtual/<class_name>

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_registerdriver注册到sysfs系统中,在注册之前需要对driver进行初始化

struct device_driver driver = {

       .name = <driver name>;

       .bus = <bus>;

       .probe = <probe func>;                // 探测设备

       .remove = <remove func>;           // 移除设备

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

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

};

 

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;      // 父目录指向busdrivers子目录

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

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

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

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

              driver_attach(drv);

// driver加入到busdrivers列表

       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);    // busdrv_attrs属性列表加入driver目录

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

kobject_uevent(&priv->kobj, KOBJ_ADD);

 

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

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

      

5.   sysfs

前面提到的classbusdriversdevicessysfs系统中都是以目录表示;它们的属性则由文件表示。所有的目录、文件都是通过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)

你可能感兴趣的:(linux,struct,Module,Semaphore,null,Class)