天天看點

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/<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 類似, bus 在 sysfs 中的顯示由 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> ,都會自動建立兩個子目錄 drivers 和 devices ,分别代表連到此 <bus> 的裝置和驅動。在 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;    

         struct klist_node knode_driver;

         struct klist_node knode_bus;

         struct device                 *parent;

         struct kobject kobj;

         char   bus_id[BUS_ID_SIZE];       

         const char            *init_name;

         struct device_type       *type;

         unsigned              uevent_suppress:1;

         struct semaphore          sem; 

         struct bus_type  *bus;         

         struct device_driver *driver; 

         void            *driver_data;      

         void            *platform_data; 

         struct dev_pm_info      power;

         u64             *dma_mask;       

         u64             coherent_dma_mask;

         struct device_dma_parameters *dma_parms;

         struct list_head   dma_pools;        

         struct dma_coherent_mem    *dma_mem;

         struct dev_archdata      archdata;

         spinlock_t           devres_lock;

         struct list_head   devres_head;

         struct list_head   node;

         struct class          *class;

         dev_t                            devt;

         struct attribute_group  **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 ,把 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);

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

         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 = <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;      // 父目錄指向 bus 的 drivers 子目錄

       // 為 driver 建立一個 kobj ,父目錄在上一行的 kset 中指定了

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

       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

繼續閱讀