1. 程式人生 > >(二)裝置結構模型_高階部分(Bus、Class、Device、Driver)

(二)裝置結構模型_高階部分(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,可以想像,也無非是對這些結構體中的成員的操作。

本文參考了蝸窩科技-裝置驅動模型-(一)~(八)