設備類class理解


http://blog.chinaunix.net/uid-20729583-id-1884552.html

設備類struct class
一個類是一個設備的高級視圖,它抽象出低級的實現細節。例如,驅動可以見到一個SCSI磁盤或者一個ATA磁盤,在類的級別,他們都是磁盤,類允許用戶空間基於它們作什么來使用設備,而不是它們如何被連接或者它們如何工作。

設備類表示一類設備,所有的class對象都屬於class_subsys子系統
struct class
{
    const char *name;//類名稱
    struct module *owner;//對應模塊
    struct subsystem subsys;//對應的subsystem;
    struct list_head children;//class_device鏈表
    struct list_head  interfaces;//class_interface鏈表
    struct semaphore  sem;//children和interfaces鏈表鎖
    struct class_attribute *class_attrs;//類屬性
    int (*uevent)(struct class_device *dev,char **envp,int num_envp,char *buffer,int buffer_size);//事件
    void (*release)(struct class_device *dev);//釋放類設備的方法
    void (*class_release)(struct class *class);    //釋放類的方法
}
一、struct list_head結構體
  struct list_head {
          struct list_head *next, *prev;  //此處進行了結構體list_head的定義系統使用這個鏈表的形式進行系統文件資源的管理
  };
二、semaphore 結構體
 struct semaphore {
          atomic_t count;       // 右邊是atomic 的定義 typedef struct { volatile int counter; } atomic_t;   (這里的volatile int 指的是counter變量可以被其他的程序更改,但是不能被你自己更改)此處使用了atomic_t類型
//是為了是引用計數不被自己更改,增強安全性,由於這個計數變量只能由其他程序更改,而其他程序只能是系統中的管理類設備的程序,所以增強了安全性
          wait_queue_head_t wait;    //即等待隊列頭,(在linux驅動中,可以通過等待隊列來實現阻塞進程的呼喚)右邊是wait_queue_head_t的定義   typedef struct __wait_queue_head wait_queue_head_t;
   //下面是__wait_queue_head 的定義
  /*
      struct __wait_queue_head {
          spinlock_t  lock;
下面是spinlock_t的定義
 /*
#include 
  19
  20typedef struct {
  21        raw_spinlock_t raw_lock;
  22#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP)
  23        unsigned int break_lock;
  24#endif
  25#ifdef CONFIG_DEBUG_SPINLOCK
  26        unsigned int magic, owner_cpu;
  27        void *owner;
  28#endif
  29#ifdef CONFIG_DEBUG_LOCK_ALLOC
  30        struct lockdep_map dep_map;
  31#endif
  32} spinlock_t;
*/
          struct list_head task_list;
  };
  */
  };

struct semaphore {
          atomic_t count;
          int sleepers;
          wait_queue_head_t wait;
  };
三、類屬性class_attribute
下面是結構體class_attribute的定義
struct class_attribute {
 210        struct attribute        attr;
/*
28struct attribute {
  29        const char              * name;    //類屬性的名稱
  30        struct module           * owner;   //類屬性所述模塊
  31        mode_t                  mode;     //保護位,如果是只讀,就要設置成 S_IRUGO,讀寫就是 S_IWUSR。
  32};
*/
 211        ssize_t (*show)(struct class *, char * buf);  //具體實現在用戶空間/sys下面的讀操作
  /*

*/
 212        ssize_t (*store)(struct class *, const char * buf, size_t count);     //存儲用戶通過buffer傳入的屬性值
 213};
對於這個屬性結構體,可以使用函數來進行新屬性的添加
int device_create_file(struct device *,struct device_attribute *);
void device_remove_file(struct device *,struct device_attribute *);
四、用戶事件 uevent(struct class_device *dev,char **envp,int num_envp,char *buffer,int buffer_size);(即user event)
下面介紹一個與此十分相似的相關函數
static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,int num_envp, char *buffer, int buffer_size);
           
五、void (*release)(struct class_device *dev)
六、class_release
static void class_release(struct kobject * kobj)
  51{
  52        struct class *class = to_class(kobj);//此處的to_class()為一個宏定義,這個宏定義用來指向設備類,定義如右:#define to_class(obj) container_of(obj, struct class, subsys.kobj)
  53
  54        pr_debug("class '%s': release.\n", class->name);
  55
  56        if (class->class_release)
  57                class->class_release(class);
  58        else
  59                pr_debug("class '%s' does not have a release() function, "   "be careful\n", class->name); //  這是一個宏定義:#define pr_debug(fmt,arg...)      printk(KERN_DEBUG fmt,##arg)
/*上面的這個宏是一個可變參數宏,詳細的解釋如下:
在 GNU C 中,宏可以接受可變數目的參數,就象函數一樣,例如:

 include/linux/kernel.h
110: #define pr_debug(fmt,arg...) \
111:         printk(KERN_DEBUG fmt,##arg)

這里 arg 表示其余的參數,可以是零個或多個,這些參數以及參數之間的逗號構
成 arg 的值,在宏擴展時替換 arg,例如:

    pr_debug("%s:%d",filename,line)

擴展為

    printk("<7>" "%s:%d", filename, line)

使用 ## 的原因是處理 arg 不匹配任何參數的情況,這時 arg 的值為空,GNU
C 預處理器在這種特殊情況下,丟棄 ## 之前的逗號,這樣

    pr_debug("success!\n")

擴展為

    printk("<7>" "success!\n")

注意最后沒有逗號。


*/
  60}

下面是注冊和注銷class的兩個函數:
int class_register(struct class *cls);
void class_unregister(struct class *cls);

int class_register(struct class * cls)
 138{
 139        int error;
 140
 141        pr_debug("device class '%s': registering\n", cls->name);
 142
 143        INIT_LIST_HEAD(&cls->children);//這個函數用來初始化class_device鏈表
/*內聯函數INIT_LIST_HEAD的定義
static inline void INIT_LIST_HEAD(struct list_head *list)
  31{
  32        list->next = list;
  33        list->prev = list;
  34}
*/
 144        INIT_LIST_HEAD(&cls->devices);    //功能同上
 145        INIT_LIST_HEAD(&cls->interfaces);      //功能同上
 146        kset_init(&cls->class_dirs);

 147        init_MUTEX(&cls->sem);           //初始化信號量
 148        error = kobject_set_name(&cls->subsys.kobj, "%s", cls->name);   //設置相應的kobject對象的名稱
 149        if (error)
 150                return error;
 151
 152        subsys_set_kset(cls, class_subsys);     //設置相應的subsystem中的kset對象
 153
 154        error = subsystem_register(&cls->subsys);  //注冊subsystem
 155        if (!error) {
 156                error = add_class_attrs(class_get(cls));  //添加類屬性
 157                class_put(cls);  //減少類的計數
 158        }
 159        return error;
 160}
 161
 162void class_unregister(struct class * cls)
 163{
 164        pr_debug("device class '%s': unregistering\n", cls->name);
 165        remove_class_attrs(cls);  //進行類屬性移除操作
 166        subsystem_unregister(&cls->subsys);   //從所掛載的subsystem中移除
 167}
 168

二、class_device結構體
一個class可以看成是一個容器,容器總包含了很多的class_device,這些class_device是由class這個大的容器來管理的,而每個class_device都對應着一個具體的設備。
一個類的真正目的是作為一個是該類成員的設備的容器. 一個成員由 struct class_device 來表示:

每個class對象包括一個class_device鏈表,每個class_device對象表示一個邏輯設備並通過struct class_device中的dev成員(一個指向struct device的指針)關聯一個物理設備。一個邏輯設備總是對應一個物理設備,而一個物理設備卻可以對應多個邏輯設備。

struct class_device
{
    struct list_head node;

    struct kobject  kobj;//內嵌的kobject;

    struct class   *class;//所屬的類;

    dev_t   devt;//dev_t設備號

    struct class_device_attribute  *devt_attr;

    struct class_device_attribute  uevent_attr;

    struct device  *dev;//如果存在,創建到/sys/devices相應入口的符號鏈接

    void *class_data;//私有數據

    struct class_device  *parent;//父設備

    void (*release)(struct class_device *dev);//釋放對應類實際設備的方法

    int(*uevent)(struct class_device *dev,char **envp,int num_envp,char *buffer,int buffer_size);

    char class_id[BUS_IO_SIZE];  //  u32 class_id;類標志
}
class_id 成員持有設備名子, 如同它在 sysfs 中的一樣. class 指針應當指向持有這個設備的類, 並且 dev 應當指向關聯的設備結構. 設置 dev 是可選的; 如果它是非 NULL, 它用來創建一個符號連接從類入口到對應的在 /sys/devices 下的入口, 使得易於在用戶空間找到設備入口. 類可以使用 class_data 來持有一個私有指針.
一、class_device_attribute  
這個結構體為類設備屬性,定義如下:
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
int class_device_register(struct class_device *class_dev);    //注冊
void class_device_unregister(struct class_device *class_dev);   //注銷



 675int class_device_register(struct class_device *class_dev)
 676{
 677        class_device_initialize(class_dev);   //初始化類的對應的類設備
 678        return class_device_add(class_dev);    //返回class_device_add(class_dev);
 679}
下面是class_device_register中兩個函數的具體實現代碼:

 567int class_device_add(struct class_device *class_dev)   //該函數用於進行設備的添加
 568{
 569        struct class *parent_class = NULL;           //初始化父指針為空,
 570        struct class_device *parent_class_dev = NULL;    //初始化設備類父指針為空,實現功能和上面的函數想類似
 571        struct class_interface *class_intf;
 572        int error = -EINVAL;//初始化error為-EINVAL(對應數值22)
 573
 574        class_dev = class_device_get(class_dev);   //此函數是class_device對象的計數加1,返回class_device對象的指針
 575        if (!class_dev)     
 576                return -EINVAL;    
 577
 578        if (!strlen(class_dev->class_id))    
 579                goto out1;
 580
 581        parent_class = class_get(class_dev->class);     
 582        if (!parent_class)
 583                goto out1;
 584
 585        parent_class_dev = class_device_get(class_dev->parent);
 586
 587        pr_debug("CLASS: registering class device: ID = '%s'\n",
 588                 class_dev->class_id);
 589
 590        /* first, register with generic layer. */
 591        error = kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);
 592        if (error)
 593                goto out2;
 594
 595        if (parent_class_dev)
 596                class_dev->kobj.parent = &parent_class_dev->kobj;
 597        else
 598                class_dev->kobj.parent = &parent_class->subsys.kobj;
 599
 600        error = kobject_add(&class_dev->kobj);
 601        if (error)
 602                goto out2;
 603
 604        /* add the needed attributes to this device */
 605        error = sysfs_create_link(&class_dev->kobj,
 606                                  &parent_class->subsys.kobj, "subsystem");
 607        if (error)
 608                goto out3;
 609
 610        error = class_device_create_file(class_dev, &class_uevent_attr);
 611        if (error)
 612                goto out3;
 613
 614        if (MAJOR(class_dev->devt)) {
 615                error = class_device_create_file(class_dev, &class_devt_attr);
 616                if (error)
 617                        goto out4;
 618        }
 619
 620        error = class_device_add_attrs(class_dev);
 621        if (error)
 622                goto out5;
 623
 624        if (class_dev->dev) {
 625                error = sysfs_create_link(&class_dev->kobj,
 626                                          &class_dev->dev->kobj, "device");
 627                if (error)
 628                        goto out6;
 629        }
 630
 631        error = class_device_add_groups(class_dev);
 632        if (error)
 633                goto out7;
 634
 635        error = make_deprecated_class_device_links(class_dev);
 636        if (error)
 637                goto out8;
 638
 639        kobject_uevent(&class_dev->kobj, KOBJ_ADD);
 640
 641        /* notify any interfaces this device is now here */

 642        down(&parent_class->sem);
 643        list_add_tail(&class_dev->node, &parent_class->children);
 644        list_for_each_entry(class_intf, &parent_class->interfaces, node) {
 645                if (class_intf->add)
 646                        class_intf->add(class_dev, class_intf);
 647        }
 648        up(&parent_class->sem);
 649
 650        goto out1;
 651
 652 out8:
 653        class_device_remove_groups(class_dev);
 654 out7:
 655        if (class_dev->dev)
 656                sysfs_remove_link(&class_dev->kobj, "device");
 657 out6:
 658        class_device_remove_attrs(class_dev);
 659 out5:
 660        if (MAJOR(class_dev->devt))
 661                class_device_remove_file(class_dev, &class_devt_attr);
 662 out4:
 663        class_device_remove_file(class_dev, &class_uevent_attr);
 664 out3:
 665        kobject_del(&class_dev->kobj);
 666 out2:
 667        if(parent_class_dev)
 668                class_device_put(parent_class_dev);
 669        class_put(parent_class);
 670 out1:
 671        class_device_put(class_dev);
 672        return error;
 673}
上面的這個函數比較容易理解,這里就不再一一的給出每個代碼的解釋了。

 674

下面這個函數為初始化class_device.
void class_device_initialize(struct class_device *class_dev)
 561{
 562        kobj_set_kset_s(class_dev, class_obj_subsys);
 563        kobject_init(&class_dev->kobj);//對於這個函數,一般情況下,在進行設備對象的初始化時都會設計到這個函數的調用,每一個設備對象都對應kobject這樣的一個對象,這個對象的作用很重要,主要是用於對設備模型的結構化的描述。
 564        INIT_LIST_HEAD(&class_dev->node);
 565}


三、類接口 class_interface
struct class_interface
{
    struct list_head node;   //
    struct class *class;   //相應的class
    int (*add)(struct class_device *,struct class_interface *);  //設備加入時觸發
    void (*remove)(struct class_device *,struct class_interface *);//設備移出時觸發
}
與class和class_device相似,class_interface也存在兩個函數用於注冊和注銷class_interface
int class_interface_register(struct class_interface *class_intf);
void class_interface_unregister(struct class_interface *class_intf);
如果想對上面的兩個函數進行徹底的了解,請參閱http://lxr.linux.no/#linux/init/do_mounts_rd.c#L17


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM