驱动中的device和device_driver结构体

device和device driver是Linux驱动开发的基本概念。Linux kernel的思路很简单:驱动开发,就是要开发指定的软件(driver)以驱动指定的设备,所以kernel就为设备和驱动它的driver定义了两个数据结构,分别是device和device_driver。

1. struct device

c 复制代码
/**
 * 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.
 * @iommu_fwspec: IOMMU-specific properties supplied by firmware.
 *
 * @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;
	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_links_info	links;
	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;
	struct iommu_fwspec	*iommu_fwspec;

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

struct device 是 Linux 设备模型中用于描述设备的基础结构体,包含设备驱动程序中几乎所有的关键信息。这个结构体的每个成员都代表设备的不同属性或控制与管理功能。

主要成员变量

  1. struct device *parent
    • 设备的父设备指针。大多数情况下,父设备是某种总线或控制器(如 PCI 控制器)。如果为 NULL,则表示该设备是顶层设备。
  2. struct device_private *p
    • 保存设备私有数据的指针,用于设备模型核心管理设备的内部数据。通常开发者不直接操作这个字段。
  3. struct kobject kobj
    • kobject 是 Linux 内核中的一种对象管理机制,kobj 用于在 /sys 文件系统中表示设备,通过它可以导出设备的属性到用户空间。
  4. const char *init_name
    • 设备的初始名称,用于在注册时给设备指定名称。
    • 注1:在设备模型中,名称是一个非常重要的变量,任何注册到内核中的设备,都必须有一个合法的名称,可以在初始化时给出,也可以由内核根据"bus name + device ID"的方式创造。
  5. const struct device_type *type
    • 设备类型指针,定义了设备的类型及其类型特定信息。
  6. struct mutex mutex
    • 设备互斥锁,用于同步对该设备的操作,避免多个线程同时访问设备驱动的共享资源时引发冲突。
  7. struct bus_type *bus
    • 指向设备所在的总线类型(如 PCI、I2C、SPI 等)的指针,用于描述设备属于哪个总线。这里要留意一下,后面会讲到平台总线
  8. struct device_driver *driver
    • 指向分配该设备的驱动程序结构体的指针,表示当前哪个驱动程序在控制这个设备。
  9. void *platform_data
    • 平台相关的数据指针。对于嵌入式或 SoC 硬件,platform_data 通常指向特定设备的硬件配置数据,例如可用的端口、芯片变体等。它帮助减少驱动程序中的板级特定代码。
  10. void *driver_data
    • 驱动程序的私有数据,驱动可以通过 dev_set_drvdata()dev_get_drvdata() 来设置和获取该字段。
  11. struct dev_links_info links
    • 存储与设备之间的拓扑关系信息,如设备间的依赖性。
  12. struct dev_pm_info power
    • 设备电源管理信息。包含电源状态、挂起、恢复等操作相关的字段,涉及设备的电源管理。
  13. struct dev_pm_domain *pm_domain
    • 设备电源管理域的指针,定义系统挂起、休眠、恢复时需要执行的回调函数。
  14. struct irq_domain *msi_domain
    • 当设备使用 Message Signaled Interrupts (MSI) 时,指向管理设备 MSI 中断的域。
  15. struct dev_pin_info *pins
    • 引脚管理信息,设备在引脚控制方面的数据结构,用于 GPIO 和 pinctrl 子系统。
  16. struct list_head msi_list
    • 保存设备的 MSI 描述符的链表头。
  17. int numa_node
    • 设备所在的 NUMA(Non-Uniform Memory Access)节点。
  18. u64 *dma_mask
    • DMA 掩码,用于描述设备支持的 DMA 地址范围。设备使用 DMA 时,用来表示设备能够访问的物理内存地址空间的最大范围。
  19. u64 coherent_dma_mask
    • dma_mask 类似,但专门用于一致性内存的 DMA 掩码。
  20. unsigned long dma_pfn_offset
    • DMA 内存与物理内存之间的偏移量。
  21. struct device_dma_parameters *dma_parms
    • DMA 参数,低层驱动可以通过它告知 IOMMU 关于段限制等特性。
  22. struct list_head dma_pools
    • 设备的 DMA 池列表头,表示设备的 DMA 内存池。
  23. struct dma_coherent_mem *dma_mem
    • 用于管理一致性 DMA 内存的内部数据结构。
  24. struct cma *cma_area
    • 用于 DMA 分配的连续内存区域指针。
  25. struct dev_archdata archdata
    • 设备的体系结构特定数据。不同平台可以利用这个字段扩展额外的体系结构相关的信息。
  26. struct device_node *of_node
    • 指向与设备相关的设备树节点。对于使用设备树的系统,of_node 记录设备在设备树中的描述信息。
  27. struct fwnode_handle *fwnode
    • 设备的固件节点,通常用于指向 ACPI 或设备树中的设备描述。
  28. dev_t devt
    • 设备的 dev_t 类型,设备号,表示设备的主设备号和次设备号,用于创建设备节点。
  29. u32 id
    • 设备实例 ID,用于标识同类设备的不同实例。
  30. spinlock_t devres_lock
    • 设备资源锁,用于保护设备的资源列表。
  31. struct list_head devres_head
    • 设备资源链表头,记录与设备相关的资源(如内存、I/O 端口等)。
  32. struct klist_node knode_class
    • 用于将设备添加到设备类链表中的节点。
  33. struct class *class
    • 设备的类,用于分类设备(如输入设备、网络设备等)。设备类定义设备的行为及其属性。
  34. const struct attribute_group **groups
    • 可选的属性组,用于定义设备在 /sys 文件系统中暴露的额外属性。
  35. void (*release)(struct device *dev)
    • 设备的释放回调函数,当所有对设备的引用都释放后,调用该函数释放设备占用的资源。
  36. struct iommu_group *iommu_group
    • 设备所属的 IOMMU 组指针。IOMMU 组用于管理设备之间的内存隔离。
  37. struct iommu_fwspec *iommu_fwspec
    • 由固件提供的 IOMMU 特定属性。

状态标志位

  1. bool offline_disabled:1
    • 标志位,指示设备是否永久在线,如果设置为 true,设备无法进入离线状态。
  2. bool offline:1
    • 标志位,指示设备是否离线。

struct device 是 Linux 设备模型中非常重要的结构体,负责设备的管理与控制。它包含了设备在总线上的位置信息、与驱动程序的关联、DMA 信息、电源管理、IOMMU 等多种设备相关的属性。在实际使用时,struct device 通常是被嵌入到更高级的设备结构中,用于表示具体的硬件设备,比如平台platfrom_device结构体中。其实这些成员内核开发者也给了详细的注解。

2. struct device_driver

c 复制代码
/**
 * 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 {
    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;
};

struct device_driver 是 Linux 内核中用于描述设备驱动程序的基本结构体,它用于表示设备驱动的属性和操作。该结构体负责跟踪系统中已知的所有驱动程序,并为新设备和驱动程序之间进行匹配提供基础。

主要成员变量

  1. const char *name
    • 驱动程序的名称。在设备和驱动之间进行匹配时,设备的名称和驱动的名称会进行比较。
  2. struct bus_type *bus
    • 驱动程序所依附的总线类型。每个驱动程序都属于某个总线类型,例如 PCI、I2C、SPI 等。
  3. struct module *owner
    • 模块的所有者,指向实现该驱动的模块(内核模块)结构体。它确保驱动所在模块在使用期间不会被卸载。
  4. const char *mod_name
    • 模块的名称。对于内建模块(built-in modules),这个字段用于指明模块的名字。
  5. bool suppress_bind_attrs
    • 控制是否通过 sysfs 禁止手动绑定/解绑设备。如果设置为 true,则无法通过 /sys 文件系统中的 bind 和 unbind 文件手动进行设备和驱动的绑定/解绑。
  6. enum probe_type probe_type
    • 控制驱动程序的 probe 操作的类型,可以是同步的(synchronous)或异步的(asynchronous)。它决定了驱动程序在处理设备时,probe 函数是阻塞执行还是非阻塞执行。
  7. const struct of_device_id *of_match_table
    • 用于设备树(Device Tree)的匹配表。驱动程序可以通过该表与设备树中的设备节点进行匹配。
  8. const struct acpi_device_id *acpi_match_table
    • 用于 ACPI(高级配置与电源接口)的匹配表。驱动程序通过该表与系统的 ACPI 表进行匹配,从而识别设备。

驱动操作接口

  1. **int (*probe)(struct device *dev)**** **
    • probe 函数用于在设备检测到时调用,它用于检查该驱动是否可以支持指定的设备,并在可以支持时绑定设备和驱动。返回 0 表示成功,非零表示失败。
    • 所谓的"probe",是指在Linux内核中,如果存在相同名称的device和device_driver(注:还存在其它方式),内核就会执行device_driver中的probe回调函数,而该函数就是所有driver的入口,可以执行诸如硬件设备初始化、字符设备注册、设备文件操作ops注册等动作。
    • 这里先不讲,在bus中再提
  2. int (*remove)(struct device *dev)
    • 当设备被从系统中移除时调用,用于解除设备与驱动的绑定并释放相关资源。通常在卸载设备时调用。
  3. void (*shutdown)(struct device *dev)
    • 系统关闭时调用,用于关闭设备并确保其处于安静状态。这通常在系统关机或重启时调用。
  4. int (*suspend)(struct device *dev, pm_message_t state)
    • 将设备挂起(进入低功耗模式)时调用。挂起操作通常在设备空闲或系统进入休眠模式时触发。
  5. int (*resume)(struct device *dev)
    • 将设备从挂起状态恢复到正常运行状态时调用,通常在系统恢复时或设备被再次使用时调用。

其他成员

  1. const struct attribute_group **groups
    • 驱动程序导出的默认属性组。内核会自动创建这些属性组,使驱动程序可以将配置或状态信息通过 /sys 文件系统提供给用户空间。
  2. const struct dev_pm_ops *pm
    • 设备电源管理操作的指针。通过该指针,驱动程序可以定义设备的电源管理操作(如挂起、恢复、系统睡眠等)。
  3. struct driver_private *p
    • 驱动程序核心的私有数据字段。只有驱动程序核心可以访问该字段,通常开发者不需要直接使用这个字段。

struct device_driver 是 Linux 驱动模型中用于描述设备驱动程序的核心结构体,它定义了驱动程序的各种操作接口(如 proberemoveshutdownsuspendresume 等),并包含了一些驱动程序的元数据(如名称、所属总线类型、匹配表等)。通过这个结构体,驱动程序可以被内核识别,并通过合适的接口与设备进行绑定、控制和管理。

3. 驱动开发的基本步骤

设备模型框架下,设备驱动的开发是一件很简单的事情,主要包括2个步骤:

步骤1:分配一个struct device类型的变量,填充必要的信息后,把它注册到内核中。

步骤2:分配一个struct device_driver类型的变量,填充必要的信息后,把它注册到内核中。

完成这两步,内核会在何时的时机对他们进行匹配,当然匹配的前提是这两个结构体中的name是相同的(同一bus的情况下)

但是,一般情况下,Linux驱动开发很少直接使用device和device_driver,因为内核在它们之上又封装了一层,如soc device、platform device等等,而这些层次提供的接口更为简单、易用(也正是因为这个原因,并不会过多涉及device、device_driver等模块的实现细节)。

就比如platform device,很多情况下不需要我们自己去填充,只需要在设备树(后续会讲)中去定义好设备的相关信息(寄存器?中断资源?引脚?),平台就会自己帮我们注册号platform device。而platform其实依靠的也是bus总线,关于总线和平台设备具体的会另开文章讲解。

相关推荐
winddevil21 小时前
[rCore学习笔记 029] 动态内存分配器实现-以buddy_system_allocator源码为例
rust·嵌入式·rcore
卑微求AC1 天前
(C语言贪吃蛇)14.用绝对值方式解决不合理的走位
linux·c语言·开发语言·嵌入式·c语言贪吃蛇
玉树临风江流儿1 天前
Linux驱动开发(速记版)--设备树插件
linux·驱动开发
Svan.1 天前
基于Arduino的宠物食物分配器
驱动开发·硬件工程·宠物
卑微求AC2 天前
(C语言贪吃蛇)13.实现贪吃蛇四方向的移动
linux·c语言·嵌入式·c语言贪吃蛇
玄奕子2 天前
GPT对话知识库——bootloader是什么?ymodel协议是什么?
stm32·gpt·嵌入式·传输协议·嵌入式驱动
FreakStudio3 天前
全网最适合入门的面向对象编程教程:55 Python字符串与序列化-字节序列类型和可变字节字符串
python·单片机·嵌入式·面向对象·电子diy
不会编程的小江江3 天前
【Linux】Linux内核结构基础
linux·驱动开发
我想学LINUX3 天前
一文带你掌握 tmux -- 高效的终端复用工具
linux·嵌入式硬件·嵌入式·策略模式·tmux·tmux命令