kobject,Kset,bus_type >>Linux裝置驅動程式
阿新 • • 發佈:2019-01-05
狀態不錯,快要元旦了,這是我計劃開始的第一年;時間過的真快啊!
也是逃離舒適圈的一年,不能在日復一日的無聊工作沉浸下去,要明白自己今生適合做哪些事情,而不是隨波逐流;
文章目錄
- [0x100]內容概述
- [0x200] 核心物件資料結構
- [0x300] 裝置間通訊匯流排
[0x100]內容概述
- struct kobject
- struct Kset,
- struct bus_type
[0x110] 核心的裝置管理特性
- 裝置電源管理:啟動到銷燬遵照一定順序,避免出現提前關閉需要的裝置;
- 向用戶空間傳遞引數:包括提供系統資訊,操作引數介面等;
- 熱插拔管理:
- 裝置樹維護;
- 裝置型別遍歷 :告知使用者可以使用哪些裝置;
[0x200] 核心物件資料結構
[0x201] 物件資料結構功能
- 物件引用計數 :沒有被引用的物件,所佔用的資源將被釋放;
- 物件服務資訊 :描述高階物件,並體現在裝置模型中;
- 系統檔案系統 :使用物件資料結構將核心中存在物件實現可見表述;
- 層次結構關係 :通過資料結構之間關聯,維護更大的層次結構關係;
[0x202] kobject 初始化流程
- 分配struct kobject 空間,且儲存kset 的空間必須提前清零;
- 初始化 struct kobj_type :操作物件釋放或者事件處理的函式指標;
- 常規初始化 struct kobject :單裝置物件屬性資訊;
- 設定顯示於sysfs目錄中的名稱 :kobject_set_name();
[0x203] kset 初始化流程
- 分配struct kset空間,且儲存kset 的空間必須提前清零
- 初始化 kset_obj ->kobj.ktype : 繼承組中的物件釋放或者事件處理的函式指標;
- 關聯kset_obj ->kobj.ktype : 該成員優先與子物件中的ktype成員;
- 常規初始化struct kset :繼承組物件屬性資訊;
- 設定顯示於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]不同種類的屬性
- 預設文字類屬性 :用於 使用者與硬體通過核心交換資訊;
- 二進位制執行屬性 :用於 使用者向硬體上傳韌體寫入更新程式等等;
- 自定義文字屬性;
[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);
}
}