1. 程式人生 > >linux裝置驅動模型裡兩個重要的資料結構:class和class_device

linux裝置驅動模型裡兩個重要的資料結構:class和class_device

/************************基於linux-2.6.24.7版本核心********************************/

1、class
      一個類是一個裝置的高層檢視,它抽象掉了底層的實現細節。例如,在驅動層面時,你可能會見到SCSI磁碟或者ATA磁碟;但在類層面時,它們都是磁碟。類允許使用者空間基於它們做什麼來使用裝置,而不是它們如何被連線或者它們如何工作。
      class表示一類裝置,所有class都屬於class_subsys(class子系統),即出現在/sys/class目錄下,除了塊裝置(可能出現在/sys/block/或/sys/class/block)。




/* class結構體 */

struct class {
    const char            * name;        /* class的名稱 */
    struct module        * owner;    /* 擁有該class的模組 */
    struct kset            subsys;        /* 該class對應的子系統 */
    struct list_head    children;    /* 該class的class_device列表 */
    struct list_head    devices;    
    struct list_head    interfaces;    
    struct kset            class_dirs;        
    struct semaphore    sem;    /* locks both the children and interfaces lists */
    struct class_attribute            * class_attrs; /* 該class的預設屬性,以NULL結尾 */
    struct class_device_attribute    * class_dev_attrs; /* 新增到class的class_device所擁有的預設屬性 */
    struct device_attribute            * dev_attrs;
    
    /* 該函式提供在產生熱插拔class_device事件時,新增環境變數的能力 */
    int    (*uevent)(struct class_device *dev, struct kobj_uevent_env *env); 
    /* 該函式提供在產生熱插拔device(物理裝置)事件時,新增環境變數的能力 */
    int    (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
    /* 新增到class的class_device移除時,呼叫該函式進行必要的清理工作 */
    void (*release)(struct class_device *dev);    
    /* class被移除時,呼叫該函式進行必要的清理工作 */
    void (*class_release)(struct class *class);
    void (*dev_release)(struct device *dev);
    int    (*suspend)(struct device *, pm_message_t state);
    int    (*resume)(struct device *);
};
/* class註冊函式 */
int __must_check class_register(struct class *);
void class_unregister(struct class *);


 
      建立一個class有兩種方法
      a、根據需要,填充一個struct class,然後再呼叫class_register註冊該class就ok了(此法比較靈活,可以自己定製很多東西)
      b、就是通過下面的class_create來建立一個class,該函式會返回一個指向剛建立的class的指標(建立class的最簡單方法)


/* class_create用於建立一個名為name的class,其owner引數一般為THIS_MODULE。class_create內部呼叫了class_register */
struct class *class_create(struct module *owner, const char *name);
/* class_destroy用於刪除一個class,實際上其內部只是簡單呼叫了class_unregister(cls)來登出cls */
void class_destroy(struct class *cls);


 
      一個class屬性對應於/sys/class/class.name(class.name就是該class的名稱)目錄裡的一個檔案。通過這些檔案,可以向用戶空間輸出一些關於該class的資訊,也可從使用者空間獲取到一些資訊。


/* class屬性結構體 */
struct class_attribute {
    struct attribute    attr;
    /* 當用戶空間讀取該屬性時,呼叫show函式輸出一個"屬性值"給使用者空間 */
    ssize_t (*show)(struct class *, char * buf);
    /* 當用戶空間寫該屬性時,呼叫store函式儲存使用者寫入的"屬性值" */
    ssize_t (*store)(struct class *, const char * buf, size_t count);
};
struct attribute {
    const char        * name;
    struct module    * owner;
    mode_t            mode;
};
/* CLASS_ATTR可以在編譯時建立一個class屬性,該屬性的名稱為class_attr_name */
#define CLASS_ATTR(_name,_mode,_show,_store)            \
struct class_attribute class_attr_##_name = __ATTR(_name,_mode,_show,_store) 
/* class_create_file與class_remove_file用於建立與刪除class預設屬性外的屬性 */
int __must_check class_create_file(struct class *,
                    const struct class_attribute *);
void class_remove_file(struct class *, const struct class_attribute *);


2、class_device
      一個class可以看成是一個容器(一個子系統subsystem),包含了很多的class_device,這些class_device是由class這個大的容器來管理的,而每個class_device都對應著一個具體的裝置。
      每個class物件包括一個class_device連結串列,每個class_device物件表示一個邏輯裝置並通過struct class_device中的dev成員(一個指向struct device的指標)關聯一個物理裝置。一個邏輯裝置總是對應一個物理裝置,而一個物理裝置卻可以對應多個邏輯裝置。
      實際上,class_device在/sys/class/subsystem生成的目錄就是上面提到的class_device。這樣第2點也有了。


/* class_device結構體 */


struct class_device {
    struct list_head    node;            /* 僅供驅動核心內部使用 */
    struct kobject        kobj;            /* 該class_device相應的kobject,僅供驅動核心內部使用 */
    struct class        * class;        /* 該class_device所屬的class,必須有 */
    dev_t                devt;            /* 該class_device的裝置編號,用於建立其dev屬性檔案,僅供驅動核心內部使用 */
    struct device        * dev;            /* 指向該class_device相關的device結構體(物理裝置),可選.若不為NULL,用於建立一個從class入口到/sys/devices下相應入口的符號連線,以便使用者空間查詢裝置入口 */
    void                * class_data;    /* 該class_device的私有資料指標 */
    struct class_device    *parent;        /* parent of this child device, if there is one */
    struct attribute_group ** groups;    /* optional groups */
    void    (*release)(struct class_device *dev);
    int    (*uevent)(struct class_device *dev, struct kobj_uevent_env *env);
    char    class_id[BUS_ID_SIZE];        /* 該class_device的名稱,在其所屬class中應是唯一的,不可重名 */
};
/* class_devic註冊函式 */
int __must_check class_device_register(struct class_device *);
void class_device_unregister(struct class_device *);


 
      與class一樣,建立一個class_device也有兩種方法
      a、根據需要,填充一個struct class_device,然後再呼叫class_device_register註冊該class_device就ok了(此法比較靈活,可以自己定製很多東西)
      b、就是通過下面的class_device_create來建立一個class_device,該函式會返回一個指向剛建立的class_device的指標(建立class_device的最簡單方法)


/* class_device_create用於建立一個class_device,其名稱最後兩個引數決定(類似於printf的格式化字串)
 * cls指明瞭其所屬的class,可以是自己填充的class或由class_create返回的class
 * parent指明瞭該class_device的父class_device,若沒有,則為NULL
 * 該class_device的裝置編號,用於建立其dev屬性檔案,必須指明
 * device指明瞭該class_device(邏輯裝置)對應的device(物理裝置),可有可無,無則為NULL
 * 實際上,class_device_create也就是填充一個class_device,然後呼叫了class_device_register註冊該class_device
 */
struct class_device *class_device_create(struct class *cls,
                        struct class_device *parent,
                        dev_t devt,
                        struct device *device,
                        const char *fmt, ...)
                    __attribute__((format(printf,5,6)));
/* class_destroy用於刪除一個class,內部呼叫了class_unregister(cls)來登出cls */
void class_device_destroy(struct class *cls, dev_t devt);


    
      class_device屬性對應於/sys/class/class.name/class_device.class_id目錄下一個檔案。通過這些檔案,可以向用戶空間輸出一些關於該class_device的資訊,也可從使用者空間獲取到一些資訊。


/* class_device屬性,其show和store函式類似於class屬性的show和store函式 */
struct class_device_attribute {
    struct attribute    attr;
    ssize_t (*show)(struct class_device *, char * buf);
    ssize_t (*store)(struct class_device *, const char * buf, size_t count);
};
/* CLASS_DEVICE_ATTR可以在編譯時建立一個class_device屬性,該屬性的名稱為class_device_attr_name */
#define CLASS_DEVICE_ATTR(_name,_mode,_show,_store)        \
struct class_device_attribute class_device_attr_##_name =     \
    __ATTR(_name,_mode,_show,_store)
/* class_device_create_file與class_device_remove_file用於建立與刪除class_device預設屬性外的屬性 */
int __must_check class_device_create_file(struct class_device *,
                 const struct class_device_attribute *);
void class_device_remove_file(struct class_device * class_dev,
             const struct class_device_attribute * attr);


    
      其實,在呼叫class_device_register註冊一個class_device時,該函式內部呼叫了
int class_device_add(struct class_device *class_dev)
在class_device_add內,通過class_device_create_file建立dev、uevent和該class_device所擁有的預設屬性(由class_device.class->class_dev_attrs指定)等屬性檔案。這樣第3點也有了。
      dev屬性檔案用於向用戶空間輸出該class_device的裝置編號。
      uevent屬性檔案使使用者可以手動觸發uevent事件(通過向該檔案寫,如add、remove等字串)。