(二)裝置結構模型_高階部分(Bus、Class、Device、Driver)
高階部分(Bus、Class、Device、Driver)
深入,並且廣泛
-沉默犀牛
這篇文章只分析Bus、Class的作用,和表示它們的結構體。不分析介面函式
Bus
Bus是處理器與一個或者多個device之間的通道。在裝置模型中,所有的device都通過bus相連,這意味著,系統中的每一個device都要連線在一個Bus上,這個Bus可以是內部Bus,虛擬Bus,或者platform Bus。Bus之間可以相互穿插,比如一個USB控制器通常是一個PCI裝置。以下分析代表Bus的結構體:bus_type
struct bus_type { const char *name; //該bus的名稱,會在sysfs中以目錄的形式存在, //如platform bus在sysfs中表現為"/sys/bus/platform” const char *dev_name; //對有些裝置而言(例如批量化的USB裝置),設計者根本就懶得 //為它起名字的,而核心也支援這種懶惰,允許將裝置的名字留空。 //這樣當設備註冊到核心後,裝置模型的核心邏輯 //就會用"bus->dev_name+device ID”的形式, //為這樣的裝置生成一個名稱。 struct device *dev_root; //bus的預設父裝置 struct device_attribute *dev_attrs; //以下是預設的attribute const struct attribute_group **bus_groups; const struct attribute_group **dev_groups; const struct attribute_group **drv_groups; int (*match)(struct device *dev, struct device_driver *drv); //一個由具體的bus driver實現的回撥函式。 //當任何屬於該Bus的device或者device_driver //新增到核心時,核心都會呼叫該介面,如果 //新加的device或device_driver匹配上了自己 //的另一半的話,該介面要返回非零值,此時 //Bus模組的核心邏輯就會執行後續的處理。 int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); //probe和remove這兩個函式,和device_driver中 //的非常類似,但它們的存在是非常有意義的。可 //以想象一下,如果需要probe(其實就是初始化) //指定的device話,需要保證該device所在的 //bus是被初始化過、確保能正確工作的。這就要 //就在執行device_driver的probe前,先執行 //它的bus的probe。remove的過程相反。 //注1:並不是所有的bus都需要probe和remove介面的,因為對有些bus來說 //(例如platform bus),它本身就是一個虛擬的匯流排,無所謂初始化,直接 //就能使用,因此這些bus的driver就可以將這兩個回撥函式留空。 int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); //shutdown、suspend、resume是電源管理相關的 int (*online)(struct device *dev); int (*offline)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); const struct dev_pm_ops *pm; const struct iommu_ops *iommu_ops; struct subsys_private *p; //比較重要的私有型別結構指標,再做分析 struct lock_class_key lock_key; };
通過以上的註釋,我們可以理解Bus的作用,比較重要的就是match函式。之前說過,所有的Device都要連線到Bus上,那這些Device要怎麼用,是其對應的Driver來實現的。Driver可以看成每一個Device的用法,Bus上會有很多個Device,也相應的會有很多個Driver(雖然二者可能數目不相等,因為存在熱拔插),那為了讓每一個Device找到自己對應的Driver,match函式就在這裡起作用,幫助Device找到對應的Driver。
我們在sysfs中可以看到sys/bus目錄下有i2c、usb、platform等,而且每一個bus下又有devices、drivers目錄。上一篇文章講到了,如果要在sysfs中有目錄,那麼就必須有Kobject結構體才行,可是我們現在沒有在bus_type中看到kobject結構體啊?答案就在p指向的subsys_private結構體中:
struct subsys_private { struct kset subsys; //本bus(kset是同類kobject的集合,用來作為表示bus非常合適 // kset中有kobject,所以在sysfs中有目錄) struct kset *devices_kset; //本bus下所有的device struct list_head interfaces; //用於儲存該bus下所有的interface,下做介紹 struct mutex mutex; struct kset *drivers_kset; //本bus下所有的driver struct klist klist_devices; //這是兩個連結串列,用於儲存本bus下所有的device和device_driver的指標 struct klist klist_drivers; struct blocking_notifier_head bus_notifier; unsigned int drivers_autoprobe:1; //用於控制該bus下的drivers或者device是否自動probe struct bus_type *bus; //用於儲存上層的bus struct kset glue_dirs; struct class *class; //用於儲存上層的Class };
以上就能完全看出Bus的用途了。此外在對interface做個介紹:
struct subsys_interface {
const char *name; //interface的名稱
struct bus_type *subsys; //interface所屬的bus
struct list_head node; //用於將interface掛到bus中
int (*add_dev)(struct device *dev, struct subsys_interface *sif);
int (*remove_dev)(struct device *dev, struct subsys_interface *sif);
//兩個回撥函式,subsys interface的核心功能。當bus下有裝置增加或者刪
除的時候,bus core會呼叫它下面所有subsys interface
的add_dev或者remove_dev回撥。設計者可以在這兩個回撥函式
中實現所需功能,例如繫結該“specific functionality”所
對應的driver,等等。
};
Class
在裝置模型中,Bus、Device、Device driver等等,都比較好理解,因為它們對應了實實在在的東西,所有的邏輯都是圍繞著這些實體展開的。但是Class就有些不同了,因為它是虛擬出來的,只是為了抽象裝置的共性。
舉個例子,一些年齡相仿、需要獲取的知識相似的人,聚在一起學習,就構成了一個班級(Class)。這個班級可以有自己的名稱(如295),但如果離開構成它的學生(device),它就沒有任何存在意義。另外,班級存在的最大意義是什麼呢?是由老師講授的每一個課程!因為老師只需要講一遍,一個班的學生都可以聽到。不然的話(例如每個學生都在家學習),就要為每人請一個老師,講授一遍。而講的內容,大多是一樣的,這就是極大的浪費。
裝置模型中的Class所提供的功能也一樣了,例如一些相似的device(學生),需要向用戶空間提供相似的介面(課程),如果每個裝置的驅動都實現一遍的話,就會導致核心有大量的冗餘程式碼,這就是極大的浪費。所以,Class說了,我幫你們實現吧,你們會用就行了。
接下來看一下代表Class的結構體class:
struct class {
const char *name; //class的名稱,會在“/sys/class/”目錄下體現
struct module *owner;
struct class_attribute *class_attrs; //該class的預設attribute,會在class註冊到
//核心時,自動在“/sys/class/xxx_class”下建立
//對應的attribute檔案
const struct attribute_group **dev_groups; //該class下每個裝置的attribute,會在設備註冊
//到核心時,自動在該裝置的sysfs目錄下建立對應
//的attribute檔案
struct kobject *dev_kobj; //表示該class下的裝置在/sys/dev/下的目錄
//現在一般有char和block兩個,如果dev_kobj
//為NULL,則預設選擇char
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
//當該class下有裝置發生變化時,會呼叫class
//的uevent回撥函式。
char *(*devnode)(struct device *dev, umode_t *mode);
void (*class_release)(struct class *class); //用於release自身的回撥函式。
void (*dev_release)(struct device *dev); //用於release class內裝置的回撥函式。
//在device_release介面中,會依次檢查Device、
//Device Type以及Device所在的class,
//是否註冊release介面,如果有則呼叫相應
//的release介面release裝置指標。
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct kobj_ns_type_operations *ns_type;
const void *(*namespace)(struct device *dev);
const struct dev_pm_ops *pm;
struct subsys_private *p; //與之前bus中的一樣
};
我們瞭解struct device和struct device_driver這兩個資料結構,其中struct device結構會包含一個struct class指標(這從側面說明了class是device的集合,甚至,class可以是device的driver)
從蝸窩科技中原文作者有一個對於bus和class的看法,我覺得很好:
對於bus和class,我的理解是: 同一個bus下的裝置,是一種“空間上(或物理上)”聚集,之所以加引號,可能是虛擬的; 同一個class下的裝置,是一種“文化上”的聚集,例如我們有共同的特徵、共同的興趣愛好等等。
那麼,一個裝置是否可能既從屬於某一個bus,又從屬於某一個class?是可以的。通常的做法是: 該裝置的device指標(由裝置模型管理),和bus打交道,如某一個platform裝置下的device指標; 如果需要加入某一個class,則新添一個子裝置,讓這個裝置加入到class。
Device
device結構體代表了每一個裝置,看過結構體後就知道它是什麼了:
struct device {
struct device *parent; //該裝置的父裝置,一般是該裝置所從屬的bus、controller等裝置。
struct device_private *p; //一個用於struct device的私有資料結構指標
//該指標中會儲存子裝置連結串列、用於新增到bus/driver/prent等裝置
//中的連結串列頭等等
struct kobject kobj; //該資料結構對應的struct kobject。
const char *init_name; //該裝置的名稱
//在裝置模型中,名稱是一個非常重要的變數,任何註冊到核心中的裝置
//都必須有一個合法的名稱,可以在初始化時給出,也可以由核心根
//據“bus name + device ID”的方式創造
const struct device_type *type; //device_type與device的關係,非常像ktype與kobject的關係
struct mutex mutex;
struct bus_type *bus; //該device屬於哪個匯流排
struct device_driver *driver; //該device對應的device driver
void *platform_data; //一個指標,用於儲存具體的平臺相關的資料
void *driver_data;
struct dev_pm_info power; //電源管理相關的邏輯
struct dev_pm_domain *pm_domain; //電源管理相關的邏輯
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
struct irq_domain *msi_domain;
#endif
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins; //"PINCTRL”功能
#endif
#ifdef CONFIG_NUMA
int numa_node; //"NUMA”功能
#endif
u64 *dma_mask;
u64 coherent_dma_mask;
unsigned long dma_pfn_offset;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools;
struct dma_coherent_mem *dma_mem;
#ifdef CONFIG_DMA_CMA
struct cma *cma_area;
#endif
struct removed_region *removed_mem;
struct dev_archdata archdata;
struct device_node *of_node;
struct acpi_dev_node acpi_node;
dev_t devt; //dev_t是一個32位的整數,它由兩個部分(Major和Minor)組成
//在需要以裝置節點的形式(字元裝置和塊裝置)向用戶空間提供
//介面的裝置中,當作裝置號使用
u32 id;
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class; //該裝置屬於哪個class
const struct attribute_group **groups; //該裝置的預設attribute集合。
//將會在設備註冊時自動在sysfs中建立對應的檔案。
void (*release)(struct device *dev);
struct iommu_group *iommu_group;
bool offline_disabled:1;
bool offline:1;
};
Device_driver
struct device_driver {
const char *name; //該driver的名稱。和device結構一樣,該名稱非常重要3
struct bus_type *bus; //該driver所驅動裝置的匯流排裝置
struct module *owner; //核心module相關的變數
const char *mod_name; //核心module相關的變數
bool suppress_bind_attrs; //是不在sysfs中啟用bind和unbind attribute
//在kernel中,bind/unbind是從使用者空間手動的為driver
//繫結/解繫結指定的裝置的機制。
enum probe_type probe_type;
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
//probe、remove,這兩個介面函式用於實現driver邏輯的開始和結束。
//Driver是一段軟體code,因此會有開始和結束兩個程式碼邏輯,就像
//PC程式,會有一個main函式,main函式的開始就是開始,return的地方
//就是結束。而核心driver卻有其特殊性:在裝置模型的結構下,只有
//driver和device同時存在時,才需要開始執行driver的程式碼邏輯。這
//也是probe和remove兩個介面名稱的由來:檢測到了裝置和移除了裝置
//(就是為熱拔插起的!)
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups; //預設屬性
const struct dev_pm_ops *pm;
struct driver_private *p;
};
這兩篇裝置結構模型_低階/高階部分,解釋了為什麼bus、class、device會出現在sysfs中(因為內嵌了kobject),也解釋清楚了它們之間的關係。這樣就從邏輯上把握住了裝置模型的整體框架,至於涉及到的API,可以想像,也無非是對這些結構體中的成員的操作。
本文參考了蝸窩科技-裝置驅動模型-(一)~(八)