(二)设备结构模型_高级部分(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,可以想像,也无非是对这些结构体中的成员的操作。


本文参考了蜗窝科技-设备驱动模型-(一)~(八)

猜你喜欢

转载自blog.csdn.net/qq_35065875/article/details/83090644