天天看點

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

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

繼續閱讀