1. 程式人生 > >kobject,Kset,bus_type >>Linux裝置驅動程式

kobject,Kset,bus_type >>Linux裝置驅動程式

狀態不錯,快要元旦了,這是我計劃開始的第一年;時間過的真快啊!
也是逃離舒適圈的一年,不能在日復一日的無聊工作沉浸下去,要明白自己今生適合做哪些事情,而不是隨波逐流;

文章目錄

[0x100]內容概述

  1. struct kobject
  2. struct Kset,
  3. struct bus_type

[0x110] 核心的裝置管理特性

  1. 裝置電源管理:啟動到銷燬遵照一定順序,避免出現提前關閉需要的裝置;
  2. 向用戶空間傳遞引數:包括提供系統資訊,操作引數介面等;
  3. 熱插拔管理:
  4. 裝置樹維護;
  5. 裝置型別遍歷 :告知使用者可以使用哪些裝置;

[0x200] 核心物件資料結構

[0x201] 物件資料結構功能

  • 物件引用計數 :沒有被引用的物件,所佔用的資源將被釋放;
  • 物件服務資訊 :描述高階物件,並體現在裝置模型中;
  • 系統檔案系統 :使用物件資料結構將核心中存在物件實現可見表述;
  • 層次結構關係 :通過資料結構之間關聯,維護更大的層次結構關係;

[0x202] kobject 初始化流程

  1. 分配struct kobject 空間,且儲存kset 的空間必須提前清零;
  2. 初始化 struct kobj_type :操作物件釋放或者事件處理的函式指標;
  3. 常規初始化 struct kobject :單裝置物件屬性資訊;
  4. 設定顯示於sysfs目錄中的名稱 :kobject_set_name();

[0x203] kset 初始化流程

  1. 分配struct kset空間,且儲存kset 的空間必須提前清零
  2. 初始化 kset_obj ->kobj.ktype : 繼承組中的物件釋放或者事件處理的函式指標;
  3. 關聯kset_obj ->kobj.ktype : 該成員優先與子物件中的ktype成員;
  4. 常規初始化struct kset :繼承組物件屬性資訊;
  5. 設定顯示於sysfs目錄中的名稱 :kobject_set_name();

[0x210]初始化結構

[0x211] 常規初始化物件結構-- struct kobject

#include<linux/kobject.h>
/*物件屬性*/
struct kobject {
        const char              *name;      /*sysfs目錄中顯示的名稱*/
        struct list_head        entry;      /*核心雙向連結串列頭 */
        struct kobject          *parent;    /*父物件關聯,常用於樹形的連結主類與不同物件*/
        struct kset             *kset;      /*物件組屬性集*/
        struct kobj_type        *ktype;     /*物件操作函式指標,必須實現release函式*/
        struct sysfs_dirent     *sd;             
        struct kref             kref;           /*引用計數結構*/
        unsigned int state_initialized:1;       /*初始化狀態位*/
        unsigned int state_in_sysfs:1;          /*系統檔案系統狀態位*/
        unsigned int state_add_uevent_sent:1;   /*等待事件新增狀態位*/
        unsigned int state_remove_uevent_sent:1;/*等待事件移除狀態位*/
        unsigned int uevent_suppress:1;         /*等待事件抑制狀態位*/
};
/*操作函式集*/
struct kobj_type {
        void (*release)(struct kobject *kobj);    /*當物件的引用數為0時,釋放該物件*/
        const struct sysfs_ops *sysfs_ops;
        struct attribute **default_attrs;
        const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
        const void *(*namespace)(struct kobject *kobj);
};
/*1.初始化物件屬性內容,填充結構體,物件引用計數 = 1,初始化狀態位,這個必須優先做*/
void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
/*2.設定kobject顯示在sysfs目錄中的名稱,可能失敗*/
int kobject_set_name(struct kobject *kobj, const char *name, ...);

[0x212] 常規初始化物件集合-- struct kset

#include <linux/kobject.h>
/*物件組集合 */
struct kset {
        struct list_head list;      /*核心的自定義連結串列*/
        spinlock_t list_lock;    
        struct kobject kobj;        /*main kobject*/
        const struct kset_uevent_ops *uevent_ops;
};
/*1.初始化物件集合條件*/
int kset_register(struct kset *k)
{
        int err;
        if (!k)
                return -EINVAL;
        /*第一步 填充kset 結構體*/        
        kset_init(k);
         /*第二步 相當於 kobject_add()將kset中內嵌的kobject 結構新增到kset本身關聯*/ 
        err = kobject_add_internal(&k->kobj);
        if (err)
                return err;
        kobject_uevent(&k->kobj, KOBJ_ADD);
        return 0;
}
/*2.設定kset 中內嵌的kobject顯示在sysfs目錄中的名稱,可能失敗*/
int kobject_set_name(struct kobject *kobj, const char *name, ...);

[0x220] 操作物件結構相關函式

[0x221] 操作物件引用計數

/*新增模組引用與物件引用 :新增物件引用計數之前必須新增模組引用計數,優先使用這個函式*/
static struct kobject *cdev_get(struct cdev *p)
{
        struct module *owner = p->owner;
        struct kobject *kobj;
        /*檢查模組擁有者是否存在,新增模組引用計數,成功後新增物件引用計數*/
        if (owner && !try_module_get(owner))
                return NULL;
        kobj = kobject_get(&p->kobj);
        /*如果物件已銷燬 即kobj == NULL('\0')*/
        if (!kobj)
         /*如果物件不存在,取消模組引用*/
                module_put(owner);
        return kobj;
}

#include<linux/kobject.h>
/*單功能函式:增加物件應用計數,如果物件被銷燬將返回NULL*/
struct kobject *kobject_get(struct kobject *kobj)
{
        if (kobj)
                /*從這個函式中可以看見物件的引用計數是一個原子變數*/
                kref_get(&kobj->kref);
        return kobj;
}
/*單功能函式:減少物件應用計數,如果物件被銷燬將返回NULL,如果引用計數為0 物件將使用release函式被銷燬*/
void kobject_put(struct kobject *kobj)
{
        if (kobj) {
                if (!kobj->state_initialized)
                        WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
                               "initialized, yet kobject_put() is being "
                               "called.\n", kobject_name(kobj), kobj);
                /*struct kobj_type 中release函式,當 kobject 引用數歸零時,就原子幹掉kobject*/               
                kref_put(&kobj->kref, kobject_release);
        }
}

[0x222] 新增到物件集合

#include<linux/kobject.h>
/*確認待新增的kobject已經初始化完畢,且已經把kset成員指向目的kset,後執行以下函式*/
/*implement kernel-dir/lib/kobject.c   */
int kobject_add(struct kobject *kobj, struct kobject *parent,const char *fmt, ...)
{
        va_list args;
        int retval;
        if (!kobj)
                return -EINVAL;

        if (!kobj->state_initialized) {
                printk(KERN_ERR "kobject '%s' (%p): tried to add an "
                       "uninitialized object, something is seriously wrong.\n",
                       kobject_name(kobj), kobj);
                dump_stack();
                return -EINVAL;
        }
        va_start(args, fmt);
        /*如果 parent 為 NULL 將對應kobject 繫結到kset的 parent 上,如kset的parent也是NULL,將繫結到sysfs 根目錄*/
        retval = kobject_add_varg(kobj, parent, fmt, args);
        va_end(args);

        return retval;
}

/**

Func :新增新物件的到物件組集合kset 中;
args1:待新增物件的指標:注意這裡的結構一定要kobject_init()初始化過的指標;
args2 :待新增物件的父類:如果NULL 將繫結到kset->kobj.parent,如果kset中不存在parent ,將繫結到sysfs 根目錄;
args3 :待新增物件的名稱;
retval :成功返回0 失敗返回錯誤碼;如果失敗就必須呼叫 kobject_put()清理引用,或者直接kfree();

[0x223] 從物件集合移除物件

#include<linux/kobject.h>
void kobject_del(struct kobject *kobj)
{
        if (!kobj)
                return;
        sysfs_remove_dir(kobj);
        kobj->state_in_sysfs = 0;
        kobj_kset_leave(kobj);
        kobject_put(kobj->parent);
        kobj->parent = NULL;
}

[0x230] 操作物件集合相關函式

[0x231] 操作物件集合引用計數

#include<linux/kobject.h>
/*新增集合引用計數*/
static inline struct kset *kset_get(struct kset *k)
{
        return k ? to_kset(kobject_get(&k->kobj)) : NULL;
}
/*減少集合引用計數,必須實現release 函式*/
static inline void kset_put(struct kset *k)
{
        kobject_put(&k->kobj);
}

[0x232] 銷燬物件集合

#include<linux/kobject.h>
/*注意這裡直接幹掉的是kset中 物件主類,幹掉前必須確保 主類沒有關聯任何子類,否則不能刪除*/
void kset_unregister(struct kset *k)
{
        if (!k)
                return;
        kobject_put(&k->kobj);
}

[0x240]硬體物件結構屬性

[0x241]不同種類的屬性

  1. 預設文字類屬性 :用於 使用者與硬體通過核心交換資訊;
  2. 二進位制執行屬性 :用於 使用者向硬體上傳韌體寫入更新程式等等;
  3. 自定義文字屬性;

[0x242] 預設屬性資料結構

#include<linux/kobject.h>
struct kobj_type {                                
        void (*release)(struct kobject *kobj);    /*當物件的引用數為0時,釋放該物件*/
        const struct sysfs_ops                    /*sysfs 的操作函式*/
         {
          /*使用者讀取屬性 :根據 attribute 中資訊確定,使用者空間需要哪個屬性資料,一次讀取的資料不能大於PAGE_SIZE*/ 
           ssize_t (*show)(struct kobject *, struct attribute *,char *);              
           /*使用者寫入屬性 :一次寫入的資料不能大於PAGE_SIZE*/  
           ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); 
           const void *(*namespace)(struct kobject *, const struct attribute *);
         }*sysfs_ops;
        struct attribute **default_attrs;         /*儲存在sysfs中可用的模組的屬性資訊,*/       
        const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
        const void *(*namespace)(struct kobject *kobj);
};
#include <linux/sysfs.h>
struct attribute {      
        const char              *name;      /*sysfs 系統檔名*/
        umode_t                  mode;      /*讀寫許可權*/
#ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lock_class_key   *key;
        struct lock_class_key   skey;
#endif
/*RSIC 處理器架構上好像沒有實現這個功能*/
/*建立 於物件的sysfs目錄中建立自定義屬性檔案*/
int __must_check sysfs_create_file(struct kobject *kobj,const struct attribute *attr);
/*銷燬 於物件的sysfs目錄中建立自定義屬性檔案*/
void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr);
};

[0x243] 二進位制屬性

#include <linux/sysfs.h>
struct bin_attribute 
{
      /*預設的屬性結構,名稱,所有者,讀寫許可權等*/
      struct attribute        attr;
      /*二進位制值的最大長度,如果不知道大小可以設定為0,不限制*/
      size_t                  size;
      void                    *private;
      /*類似驅動中的檔案讀寫函式,每次最大資料量仍然是PAGE_SIZE*/
      ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *,char *, loff_t, size_t);
      ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *,char *, loff_t, size_t);
      int(*mmap)(struct file *, struct kobject *, struct bin_attribute *attr,struct vm_area_struct *vma);
};
/*RSIC 處理器架構上好像沒有實現這個功能*/
/*建立 於物件的sysfs目錄中建立自定義二進位制屬性檔案*/
static inline int sysfs_create_bin_file(struct kobject *kobj,const struct bin_attribute *attr)
/*銷燬 於物件的sysfs目錄中建立自定義二進位制屬性檔案*/
static inline void sysfs_remove_bin_file(struct kobject *kobj,const struct bin_attribute *attr)

[0x300] 裝置間通訊匯流排

[0x301]匯流排傳遞資訊的範圍

  • 處理器 到 其它裝置
  • 不同裝置間通訊資料
  • 虛擬平臺之間的資料

[0x302]註冊通訊匯流排

#include <linux/device.h >
struct bus_type {
        const char              *name;              /*匯流排標識名稱*/
        const char              *dev_name;          /*裝置順序名稱*/
        struct device           *dev_root;          /*裝置的父類*/
        struct bus_attribute    *bus_attrs;         /*匯流排預設匯流排屬性*/
        struct device_attribute *dev_attrs;         /*裝置預設匯流排屬性*/
        struct driver_attribute *drv_attrs;         /*驅動預設匯流排屬性*/
        /*新增驅動或者裝置是否匹配當前匯流排,如匹配,返回非0值*/
        int (*match)(struct device *dev, struct device_driver *drv);  
        /*處理裝置常規事件:新增到環境變數*/     
        int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
        /*初始化裝置:根據新增的裝置,匹配對應驅動程式probe*/
        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);
        /*電源模式管理操作函式指標*/
        const struct dev_pm_ops *pm;
        /*匯流排記憶體管理操作函式指標*/
        struct iommu_ops *iommu_ops;
        /*驅動核心私有資料指標*/
        struct subsys_private 
        {
        struct kset subsys;
        struct kset *devices_kset;
        struct list_head interfaces;
        struct mutex mutex;

        struct kset *drivers_kset;
        struct klist klist_devices;
        struct klist klist_drivers;
        struct blocking_notifier_head bus_notifier;
        unsigned int drivers_autoprobe:1;
        struct bus_type *bus;

        struct kset glue_dirs;
        struct class *class;

       }*p;
};
#define bus_register(subsys)                    \
({                                              \
        static struct lock_class_key __key;     \
        __bus_register(subsys, &__key); \
})
/*註冊匯流排 :裝置kobject、驅動kobject 繫結kset 然後註冊sysfs的過程,核心連結串列,新增匯流排的屬性 */
int __must_check __bus_register(struct bus_type *bus,struct lock_class_key *key)

void bus_unregister(struct bus_type *bus);

[0x303]遍歷指定匯流排

/*遍歷裝置,避免同時使用 有可能會導致自旋死鎖*/
int 
bus_for_each_dev(struct bus_type *bus, struct device *start,void *data, 
                                                            int (*fn)(struct device *, void *))
/*遍歷驅動程式,避免同時使用 有可能會導致自旋死鎖*/
int 
bus_for_each_drv(struct bus_type *bus, struct device_driver *start,void *data, 
                                                            int (*fn)(struct device *, void *))

Func :根據 start 位置開始遍歷裝置,如果start = NULL 將從頭遍歷;
args1:待遍歷匯流排標識;
args2:待遍歷匯流排起始位置,如果是NULL 將從頭遍歷,如果不是則後一個開始遍歷;
args3:向處理函式傳遞的引數;
args4:處理函式指標;
retval:返回非零值

[0x304]初始化匯流排屬性

#include <linux/device.h >
/*跟kobject的預設屬性差不多,畢竟都是需要新增到sysfs 檔案系統中的*/
struct bus_attribute {
        struct attribute        attr;
        ssize_t (*show)(struct bus_type *bus, char *buf);
        ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};
/*1.初始化匯流排屬性,填充屬性結構*/
#define BUS_ATTR(_name, _mode, _show, _store)   \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define __ATTR(_name,_mode,_show,_store) { \
        .attr = {.name = __stringify(_name), .mode = _mode },   \
        .show   = _show,                                        \
        .store  = _store,                                       \
}
/*2.需要提前實現對應show函式和store函式*/
/*3.sysfs下建立對應匯流排檔案*/
nt bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
{
        int error;
        if (bus_get(bus)) {
                error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
                bus_put(bus);
        } else
                error = -EINVAL;
        return error;
}
/*銷燬sysfs檔案*/
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
{
        if (bus_get(bus)) {
                sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);
                bus_put(bus);
        }
}