设备驱动模型底层架构

 Linux设备驱动模型是一个比较抽象的概念,它对系统的所有设备和驱动进行了抽象,形成了复杂的设备树型结构,采用面向对象的方法,抽象出了 device 设备、 driver 驱动、 bus 总线和 class 类等概念,所有已经注册的设备和驱动都挂在总线上,总线来完成设备和驱动之间的匹配。总线、设备、驱动以及类之间的关系错综复杂,在 Linux 内核中通过 kobject、 kset 和 subsys 来进行管理。类(class)、总线(bus)、设备(device)、驱动(driver)、mdev(自动创建设备节点和设备类)、sysfs等都属于设备驱动模型的范畴。

下面先来讲讲设备驱动模型中的sysfs:

        sysfs文件系统是内核对象(kobject)、属性(kobj_type)及它们相互关系的一种表现。它只存在于内存中,动态的表示内核数据结构。内核启动挂接文件系统后会将sysfs文件系统挂接到/sys下。内核启动时会初始化并注册一些总线、设备,这些总线、设备等会在sys下创建目录,来存储相关信息,如/sys/bus/platform 下存储了平台设备信息。如何组织管理设备,并创建目录呢,通过设备驱动模型的几个重要结构体:Kobject、kobj_type、kset来组织和管理目录及文件结构。这三个结构是设备模型中的下层架构。模型中的每一个元素都对应一个kobject,kset和kobj_type可以看成是kobject在层次结构与属性结构方面的扩充。

1.kobject结构体(include\linux\kobject.h)

struct kobject {
	const char		*name; // 对象的名字
	struct list_head	entry; //用来指向平行关系中的下一个kobject结构体对象(可以理解为同一个目录下的多个文件或者文件夹给他们链接起来)
	struct kobject		*parent; //用来指向父类对象(也就是他的上一层文件夹所对应的对象)
	struct kset		*kset; //用来指向父类对象的kset
	struct kobj_type	*ktype; //指向一个kobj_type对象
	struct kernfs_node	*sd; /* sysfs directory entry */
	struct kref		kref; //kobject的引用计数
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
	struct delayed_work	release;
#endif
	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;
};

 kobject结构体是设备驱动模型底层的一个结构体,这个结构体是设备驱动模型下的所有对象的一个基本单元,他是对设备驱动模型下所有对象抽象出来的共有的部分;kobject结构体提供了一些公共型的服务:对象引用计数、维护对象链表、对象上锁、对用户空间的表示。设备驱动模型中的各种对象其内部都会包含一个kobject,地位相当于面向对象思想中的总基类。

2.kset结构体(include\linux\kobject.h)

/**
 * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
 *
 * A kset defines a group of kobjects.  They can be individually
 * different "types" but overall these kobjects all want to be grouped
 * together and operated on in the same manner.  ksets are used to
 * define the attribute callbacks and other common events that happen to
 * a kobject.
 *
 * @list: the list of all kobjects for this kset
 * @list_lock: a lock for iterating over the kobjects
 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
 * @uevent_ops: the set of uevent operations for this kset.  These are
 * called whenever a kobject has something happen to it so that the kset
 * can add new environment variables, or filter out the uevents if so
 * desired.
 */
struct kset {
	struct list_head list; //用来链接该目录下的所有kobject对象
	spinlock_t list_lock; //自旋锁
	struct kobject kobj; //这个kobject就是本目录对应的对象结构体
	const struct kset_uevent_ops *uevent_ops;
};

 从kset结构体可以看出来,kobject其实是被kset包含了,kset的主要作用是做顶层kobject的容器类,kset的主要目的是将各个kobject(代表着各个对象)组织出目录层次架构,可以认为kset就是为了在sysfs中弄出目录,从而让设备驱动模型中的多个对象能够有层次有逻辑性的组织在一起。

3.kset和kobject之间的关系

 kset与kobject的关系:kset是kobject的一个容器,可以认为kset是一个目录,而kobject是这个目录下的各个文件,如果这个kset目录下还有子目录,那么这个目录下就会存在子kset。kobject对应文件系统/sys里的一个目录,kset中包含kobject结构体,所以kset也对应于/sys里的一个目录。

       我们可以通过看一张图来加深理解:

设备驱动模型底层架构_第1张图片

一、平行关系下的连接关系: 

 1.kobject与kobject的连接关系:

        kobject通过list_head结构体中的next指针指向下一个kobject对象的list_head结构体(如果kobject是一个链表尾,则next指向他上层的kset对象中的list_head结构体),通过list_head结构体中的prev指针指向上一个kobject对象的list_head结构体(如果kobject是一个链表头,则prev指向他上层的kset对象中的list_head结构体)。

2.ksetkobject的连接关系:

        kset通过嵌入内部的kobject结构体中的list_head结构体中的next指针指向下一个kobject对象的list_head结构体(如果kset是一个链表尾,则next指向他的上层的kset中的list_head结构体),通过list_head结构体中的prev指针指向上一个kobject对象的list_head结构体(如果kset是一个链表头,则prev指向他上层的kset中的list_head结构体)。

3.ksetkset之间的连接关系:

       kset通过嵌入内部的kobject结构体中的list_head结构体中的next指针指向下一个kset中的kobject结构体中的list_head结构体(如果kset是一个链表尾,则next指向他的上层的kset中的list_head结构体),通过list_head结构体中的prev指针指向上一个kset中的kobject结构体中的list_head结构体(如果kset是一个链表头,则prev指向他上层的kset中的list_head结构体)。

平行关系的连接是通过kobject下的list_head结构体进行连接的,父类kset是通过kset下的list_head连接他下面的所有对象组成的链表头和链表尾。

二、上下级关系下的连接关系:

属于kset容器下的所有kobject对象通过parent指针指向父类kset中的kobject结构体,通过kset指针指向父类kset。属于kset容器下的所有kset子容器通过他下面的kobject结构体中parent指针指向父类kset中的kobject结构体,通过kobject结构体中的kset指针指向父类kset容器。

设备驱动模型底层架构_第2张图片

 

4.kobj_type结构体(include\linux\kobject.h)

struct kobj_type {
	void (*release)(struct kobject *kobj); //用来清除kobject的引用计数、释放占用的资源。
	const struct sysfs_ops *sysfs_ops; //对象在sysfs中的操作方法,show和store
	struct attribute **default_attrs; //对象在sysfs中的属性
	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
};

每个kobject对象都有一些属性,这些属性由kobj_type表示,kobject中有指向kobj_type的指针。每一个kobject都需要绑定一个kobj_type来提供相应功能。

5.kset、kobject、kobj_type三者之间的关系

设备驱动模型底层架构_第3张图片

 

 设备驱动模型下的总线式设备驱动的组织方式

1.总线 struct bus_type(include\linux\device.h)

总线英文名是bus,物理上的作用是将总线两端的设备连接起来,使他们能够通过总线进行通信(数据交互)。设备驱动模型中引入总线其实是借用了总线式的设计优点,与物理总线形成一个对应关系,能够更加方便的管理硬件设备。

Linux系统内核使用bus_type结构体表示总线。应用例子:内核中的platform总线以及pci总线

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};
struct bus_type pci_bus_type = {
	.name		= "pci",
	.match		= pci_bus_match,
	.uevent		= pci_uevent,
	.probe		= pci_device_probe,
	.remove		= pci_device_remove,
	.shutdown	= pci_device_shutdown,
	.dev_groups	= pci_dev_groups,
	.bus_groups	= pci_bus_groups,
	.drv_groups	= pci_drv_groups,
	.pm		= PCI_PM_OPS_PTR,
};

2.设备 struct device(include\linux\device.h)

设备驱动模型中的总线式设计中会包含设备和驱动两个,例如对于platform总线:platform_device和platform_driver;对于pci总线:pci_device和pci_driver 。它们中会包含相应的基类设备和基类驱动:struct device和struct device_driver,就好比和上面的驱动模型中所有对象都包含了基类kobject,所以对于所有具体总线中的设备结构体中包含了基类struct device,驱动结构体中包含了基类struct device_driver。

struct device是硬件设备在内核驱动框架中的抽象,通常device不会单独使用,而是被包含在一个具体设备结构体中,如struct platform_device。

/**
 * struct device - The basic device structure
 * @parent:	The device's "parent" device, the device to which it is attached.
 * 		In most cases, a parent device is some sort of bus or host
 * 		controller. If parent is NULL, the device, is a top-level device,
 * 		which is not usually what you want.
 * @p:		Holds the private data of the driver core portions of the device.
 * 		See the comment of the struct device_private for detail.
 * @kobj:	A top-level, abstract class from which other classes are derived.
 * @init_name:	Initial name of the device.
 * @type:	The type of device.
 * 		This identifies the device type and carries type-specific
 * 		information.
 * @mutex:	Mutex to synchronize calls to its driver.
 * @bus:	Type of bus device is on.
 * @driver:	Which driver has allocated this
 * @platform_data: Platform data specific to the device.
 * 		Example: For devices on custom boards, as typical of embedded
 * 		and SOC based hardware, Linux often uses platform_data to point
 * 		to board-specific structures describing devices and how they
 * 		are wired.  That can include what ports are available, chip
 * 		variants, which GPIO pins act in what additional roles, and so
 * 		on.  This shrinks the "Board Support Packages" (BSPs) and
 * 		minimizes board-specific #ifdefs in drivers.
 * @driver_data: Private pointer for driver specific info.
 * @power:	For device power management.
 * 		See Documentation/power/devices.txt for details.
 * @pm_domain:	Provide callbacks that are executed during system suspend,
 * 		hibernation, system resume and during runtime PM transitions
 * 		along with subsystem-level and driver-level callbacks.
 * @pins:	For device pin management.
 *		See Documentation/pinctrl.txt for details.
 * @msi_list:	Hosts MSI descriptors
 * @msi_domain: The generic MSI domain this device is using.
 * @numa_node:	NUMA node this device is close to.
 * @dma_mask:	Dma mask (if dma'ble device).
 * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
 * 		hardware supports 64-bit addresses for consistent allocations
 * 		such descriptors.
 * @dma_pfn_offset: offset of DMA memory range relatively of RAM
 * @dma_parms:	A low level driver may set these to teach IOMMU code about
 * 		segment limitations.
 * @dma_pools:	Dma pools (if dma'ble device).
 * @dma_mem:	Internal for coherent mem override.
 * @cma_area:	Contiguous memory area for dma allocations
 * @archdata:	For arch-specific additions.
 * @of_node:	Associated device tree node.
 * @fwnode:	Associated device node supplied by platform firmware.
 * @devt:	For creating the sysfs "dev".
 * @id:		device instance
 * @devres_lock: Spinlock to protect the resource of the device.
 * @devres_head: The resources list of the device.
 * @knode_class: The node used to add the device to the class list.
 * @class:	The class of the device.
 * @groups:	Optional attribute groups.
 * @release:	Callback to free the device after all references have
 * 		gone away. This should be set by the allocator of the
 * 		device (i.e. the bus driver that discovered the device).
 * @iommu_group: IOMMU group the device belongs to.
 *
 * @offline_disabled: If set, the device is permanently online.
 * @offline:	Set after successful invocation of bus type's .offline().
 *
 * At the lowest level, every device in a Linux system is represented by an
 * instance of struct device. The device structure contains the information
 * that the device model core needs to model the system. Most subsystems,
 * however, track additional information about the devices they host. As a
 * result, it is rare for devices to be represented by bare device structures;
 * instead, that structure, like kobject structures, is usually embedded within
 * a higher-level representation of the device.
 */
struct device {
	struct device		*parent;

	struct device_private	*p;

	struct kobject kobj; //包含一个kobject结构体,因为属于设备驱动模型中的一个对象
	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	struct bus_type	*bus;		/* type of bus device is on */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	void		*driver_data;	/* Driver data, set and get with
					   dev_set/get_drvdata */
	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;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
	struct list_head	msi_list;
#endif

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations such descriptors. */
	unsigned long	dma_pfn_offset;

	struct device_dma_parameters *dma_parms;

	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
#ifdef CONFIG_DMA_CMA
	struct cma *cma_area;		/* contiguous memory area for dma
					   allocations */
#endif
	/* arch specific additions */
	struct dev_archdata	archdata;

	struct device_node	*of_node; /* associated device tree node */
	struct fwnode_handle	*fwnode; /* firmware device node */

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	struct klist_node	knode_class;
	struct class		*class;
	const struct attribute_group **groups;	/* optional groups */

	void	(*release)(struct device *dev);
	struct iommu_group	*iommu_group;

	bool			offline_disabled:1;
	bool			offline:1;
};

3.驱动 struct device_driver(include\linux\device.h)

跟上面一样,struct device_driver是驱动程序在内核驱动模型中的抽象,提供了最基础的驱动框架。

/**
 * struct device_driver - The basic device driver structure
 * @name:	Name of the device driver.
 * @bus:	The bus which the device of this driver belongs to.
 * @owner:	The module owner.
 * @mod_name:	Used for built-in modules.
 * @suppress_bind_attrs: Disables bind/unbind via sysfs.
 * @probe_type:	Type of the probe (synchronous or asynchronous) to use.
 * @of_match_table: The open firmware table.
 * @acpi_match_table: The ACPI match table.
 * @probe:	Called to query the existence of a specific device,
 *		whether this driver can work with it, and bind the driver
 *		to a specific device.
 * @remove:	Called when the device is removed from the system to
 *		unbind a device from this driver.
 * @shutdown:	Called at shut-down time to quiesce the device.
 * @suspend:	Called to put the device to sleep mode. Usually to a
 *		low power state.
 * @resume:	Called to bring a device from sleep mode.
 * @groups:	Default attributes that get created by the driver core
 *		automatically.
 * @pm:		Power management operations of the device which matched
 *		this driver.
 * @p:		Driver core's private data, no one other than the driver
 *		core can touch this.
 *
 * The device driver-model tracks all of the drivers known to the system.
 * The main reason for this tracking is to enable the driver core to match
 * up drivers with new devices. Once drivers are known objects within the
 * system, however, a number of other things become possible. Device drivers
 * can export information and configuration variables that are independent
 * of any specific device.
 */
struct device_driver {
	//驱动的名字,总线下的match函数进行设备和驱动的匹配就是使用name
	const char		*name; 
	struct bus_type		*bus;

	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */

	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
	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);
	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;
};

4.类 struct class (include\linux\device.h)

类(class),设备类,完全是抽象出来的概念,没有对应的实体。所谓设备类,是指提供的用户接口相似的一类设备的集合,常见的设备类有block、tty、input、usb等等。

       class的真正意义在于作为同属于一个class的多个设备的容器,目的就是为了对各种设备进行分类管理。一个设备可以从类这个角度讲他是属于哪个类,也可以从总线的角度讲他是挂在哪个总线下的。

/**
 * struct class - device classes
 * @name:	Name of the class.
 * @owner:	The module owner.
 * @class_attrs: Default attributes of this class.
 * @dev_groups:	Default attributes of the devices that belong to the class.
 * @dev_kobj:	The kobject that represents this class and links it into the hierarchy.
 * @dev_uevent:	Called when a device is added, removed from this class, or a
 *		few other things that generate uevents to add the environment
 *		variables.
 * @devnode:	Callback to provide the devtmpfs.
 * @class_release: Called to release this class.
 * @dev_release: Called to release the device.
 * @suspend:	Used to put the device to sleep mode, usually to a low power
 *		state.
 * @resume:	Used to bring the device from the sleep mode.
 * @ns_type:	Callbacks so sysfs can detemine namespaces.
 * @namespace:	Namespace of the device belongs to this class.
 * @pm:		The default device power management operations of this class.
 * @p:		The private data of the driver core, no one other than the
 *		driver core can touch this.
 *
 * A class is a higher-level view of a device that abstracts out low-level
 * implementation details. Drivers may see a SCSI disk or an ATA disk, but,
 * at the class level, they are all simply disks. Classes allow user space
 * to work with devices based on what they do, rather than how they are
 * connected or how they work.
 */
struct class {
	const char		*name; //类名称
	struct module		*owner;

	struct class_attribute		*class_attrs; //class给自己添加的属性
	const struct attribute_group	**dev_groups; //class给所包含的设备添加的属性
	struct kobject			*dev_kobj;

	int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
	char *(*devnode)(struct device *dev, umode_t *mode);

	void (*class_release)(struct class *class);
	void (*dev_release)(struct device *dev);

	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;
};

参考:https://www.cnblogs.com/deng-tao/p/6033932.html

《Linux Device Drivers, 3rd Edition》,By Jonathan Corbet, Alessandro Rubini, Greg Kroah-Hartman

 

你可能感兴趣的:(Linux驱动开发)