阅读Linux设备驱动模型源码之 device结构体成员详解

【前言】

        我们学习 Linux 设备驱动,很多人在一开始往往急于想找到快速入门的方法,希望能有一个提纲挈领的使用说明来帮助我们快速理解 Linux 设备驱动的设计思路和框架,从而摆脱掉 Linux 内核这头庞然怪兽。我自己最初也是这样。然而事与愿违,如果学习 Linux 设备驱动能有这样的捷径让大多数人快速掌握,那么具备开发 Linux 设备驱动的能力也就变得不再值钱了。所以,学习 Linux 设备驱动的正道还是丢掉世俗的浮躁、静下心来好好阅读内核源码。正如 Linus Torvalds 的神谕那样,“Read the fucking source code”。我想,这也是做好其它事情的不二法门。

        在这种想法的指引下,在前一阵子对 platform 驱动模型、i2c 驱动模型、ASoC 驱动模型、kobject+kset+ktype 有了大体认识之后,我再次开始阅读内核中的设备驱动框架代码。说实话,代码中的注释有的时候比我们在书上或网上看到的讲解更加清晰直观。我把 device 结构体的官方注释翻译成了中文写在下面。


【device结构体成员详解】

/**
 * device 结构体 - 设备驱动模型中的基础结构体之一
 * @parent:	设备所依附的“父设备”。
 * 		大多数情况下,这样的父设备是某种总线或主控制器。
 * 		如果该成员变量的值为 NULL 表示当前设备是一个最“顶端”设备,
 * 		通常这样的设备都不是你想得到的那个。
 * @p:		该成员变量是一个指向设备结构体中驱动内核部分的私有数据的指针。
 * 		更详细的信息可以阅读 device_private 结构体的注释。
 * @kobj:	kobj是最“顶层”的抽象类,所有其它的类都继承自它。
 * @init_name:	设备结构体所对应的设备的名称。
 * @type:	设备结构体所对应的设备的类型。
 * 		该成员变量用于标识设备类型,并存储着该类型设备特有的信息。
 * @mutex:	用于同步驱动中的函数调用的互斥锁。
 * @bus:	设备所挂接的总线的类型。
 * @driver:	该成员变量指向开辟当前设备结构体空间的设备驱动。
 * @platform_data: 该成员变量用于保存和设备硬件相关的平台数据。
 * 		示例:对于自定义电路板上的设备来说,典型的情况譬如嵌入式设备
 * 		以及一些基于 SOC 的硬件,Linux 系统通常会使用 platform_data 指针指向
 * 		与电路板硬件相关的结构体,这些结构体中的成员描述了它们对应的设备硬件资源
 * 		以及电路板的电路连接情况。这些被描述的内容可以包括芯片的哪些端口可用,与
 * 		上一代芯片相比有什么改动,哪个 GPIO 引脚有额外的功能等等。
 * 		这个成员变量的实现使得 BSP 的工作量减小了许多,
 * 		同时也减少了我们为兼容不同的电路板而写的 #ifdefs 语句的数量。
 * @driver_data: 指向驱动特定信息的私有指针。
 * @power:	用于设备的电源管理。
 * 		更详细的信息可以阅读 Documentation/power/devices.txt 文档。
 * @pm_domain:	为设备提供当系统被挂起、
 * 		休眠、唤醒和电源状态切换时的回调函数,
 * 		以及子系统级别和驱动级别的回调函数。
 * @pins:	用于设备引脚管理。
 *		更详细的信息可以阅读 Documentation/pinctrl.txt 文档。
 * @msi_list:	主机 MSI 描述符。
 * @msi_domain: 设备结构体所使用的 MSI 域。
 * @numa_node:	与设备结构体最邻近的 NUMA 节点。
 * @dma_mask:	DMA掩码(前提是当前设备可进行 DMA 操作)。
 * @coherent_dma_mask: 作用和 dma_mask 类似,用于一致性 DMA 地址映射。因为并不是所有
 * 		硬件都支持在 64 位地址下开辟连续的内存空间。
 * @dma_pfn_offset: DMA区域范围在内存中的地址偏移量。
 * @dma_parms:	一个低权限级别的驱动会通过这个成员变量告知 IOMMU 代码关于
 * 		段操作限制相关的规则。
 * @dma_pools:	指向 DMA池 的指针(前提是当前设备可进行 DMA 操作)。
 * @dma_mem:	一致性 DMA 的可读写区域。
 * @cma_area:	DMA区域中的连续内存空间。
 * @archdata:	芯片架构相关的内容。
 * @of_node:	设备所在设备树的节点。
 * @fwnode:	平台固件对应的设备树节点。
 * @devt:	用于在 sysfs 中创建设备文件。
 * @id:		设备实例(ID 编号)。
 * @devres_lock: 用于保护设备上的资源访问的自旋锁。
 * @devres_head: 设备上的资源列表。
 * @knode_class: 用于将当前设备加入到类列表的节点。
 * @class:	设备所属的类。
 * @groups:	设备的属性集合(可选)。
 * @release:	当设备的引用计数减少为 0 时,使用该成员变量所指向的析构函数
 * 		释放当前设备结构体。这个成员变量的值应该由当前设备的创建者进行
 * 		设置(比如成功找到设备的总线驱动)。
 * @iommu_group: 设备所属的 IOMMU 集合。
 *
 * @offline_disabled: 如果这个成员变量的值被置 1,则设备将处于永远在线状态。
 * @offline:	如果总线类型所辖的 offline() 函数被成功调用,则该成员变量的值将被置 1。
 *
 * 在最底层级别,Linux系统中的每个设备都被一个 device 结构体的
 * 实例所表示。device 结构体中包含了设备模型内核在描述系统结构时
 * 所需要的信息。大多数子系统在此基础上还会查找它们下属设备的额外信息。
 * 这导致我们很难用少数 device 结构体来表示设备的所有信息,所以通常
 * 还会配合 kobject 结构体来描述设备的更高层信息。
 */
struct device {
	struct device		*parent;

	struct device_private	*p;

	struct kobject kobj;
	const char		*init_name; /* 设备的初始化名称 */
	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;
};


<think>我们被要求介绍platform_device结构体的组成成员。根据引用[1]和[2],platform_deviceLinux内核中用于描述平台设备结构体,它包含了设备的基本信息、资源等。 在Linux内核中,platform_device结构体的定义(以Linux 5.x内核为例)通常包含以下重要成员: 1. `name`:设备名称,用于与platform_driver进行匹配。匹配规则是:当platform_driver的id_table中存在该名称,或者platform_driver的name与该名称相同时,则匹配成功。 2. `id`:设备实例的ID。当系统中存在多个相同设备时,通过ID来区分。如果只有一个设备,则设置为-1。 3. `dev`:内嵌的struct device结构体,表示设备模型中的基础设备。它包含设备模型所需的很多信息,如引用计数、设备树节点、父设备设备组、释放函数等。 4. `resource`:指向设备资源的指针,表示该设备所占用的硬件资源,如I/O内存、中断号等。资源通常是一个数组。 5. `num_resources`:资源的数量。 6. `platform_data`:指向平台特定数据的指针,通常用于传递一些板级信息,这些信息在设备树(Device Tree)出现之前被广泛使用。在设备树中,我们通常使用设备树节点来传递,但平台数据仍然可以用于一些特殊情况。 7. `driver_override`:用于覆盖驱动匹配的强制绑定名称。如果设置,则匹配时忽略其他匹配规则,强制匹配该名称的驱动。 8. `mfd_cell`:如果该设备是多功能设备(MFD)的一部分,则指向MFD单元信息。 9. `archdata`:体系结构特定的数据,通常不需要驱动开发者关心。 10. `of_node`:指向设备树中该设备对应的节点(struct device_node),当设备设备树中创建时,该成员被设置。 11. `fwnode`:设备固件节点,可以是设备树节点,也可以是ACPI节点。 注意:内核版本不同,结构体成员可能略有差异。以上是常见的成员。 下面是一个简化的platform_device结构体定义(实际内核中的定义可能更复杂,包含更多成员): ```c struct platform_device { const char *name; int id; struct device dev; u32 num_resources; struct resource *resource; const struct platform_device_id *id_entry; char *driver_override; /* 设备树相关 */ struct device_node *of_node; struct fwnode_handle *fwnode; void *platform_data; struct mfd_cell *mfd_cell; /* 私有数据 */ void *archdata; }; ``` 注意:实际的内核源码中,结构体可能会包含更多的成员,例如用于电源管理的成员等。 ### 如何查看系统中存在的platform_device? 我们可以通过`/sys/bus/platform/devices/`目录来查看系统中已经注册的平台设备。每个设备在该目录下都有一个子目录,目录名通常是设备名加上ID(如`serial0`、`mmc0`等)。进入设备目录,可以查看资源、驱动绑定等信息。 例如,使用以下命令: ```bash ls /sys/bus/platform/devices/ ``` ### 相关问题 1. 如何为平台设备分配资源(如内存、中断)? 2. 平台设备设备树节点是如何关联的? 3. 如何编写一个平台设备驱动程序(platform_driver)? 4. 在设备树中,如何描述一个平台设备?</think>### `platform_device` 结构体成员详解 `platform_device` 是 Linux 内核中描述平台设备的核心结构体,定义于 `<linux/platform_device.h>`。其主要成员如下: #### 1. **基础标识** - **`name`** (字符串) 设备名称,用于与 `platform_driver` 匹配(如 `"uart0"`)[^1][^2]。 - **`id`** (整数) 设备实例 ID(如多个相同设备时,`id=0,1,2...`)。 #### 2. **设备模型核心** - **`dev`** (`struct device`) 内嵌的标准设备模型结构体,包含: - `parent`:父设备指针 - `fwnode`:固件节点(设备树/ACPI) - `release`:设备释放回调函数 - 设备树节点、电源管理、DMA 配置等[^1][^3]。 #### 3. **硬件资源** - **`resource`** (`struct resource *`) 指向设备资源数组(内存/I/O 区域、中断号等),例如: ```c static struct resource uart_resources[] = { [0] = DEFINE_RES_MEM(0xFE001000, 0x100), // 寄存器基址+长度 [1] = DEFINE_RES_IRQ(42), // 中断号 }; ``` - **`num_resources`** (整数) 资源数组的元素数量。 #### 4. **平台特定数据** - **`platform_data`** (`void *`) 指向板级私有数据的指针(如 GPIO 配置、时钟频率等),由板级初始化代码设置[^1][^2]。 - **`driver_override`** (`const char *`) 强制绑定指定驱动(覆盖自动匹配)。 #### 5. **设备树/固件支持** - **`of_node`** (`struct device_node *`) 关联的设备树节点(Device Tree)。 - **`fwnode`** (`struct fwnode_handle *`) 通用固件节点(支持设备树/ACPI)。 #### 6. **其他关键成员 - **`id_entry`** (`const struct platform_device_id *`) 指向设备 ID 表(用于驱动匹配)。 - **`mfd_cell`** (`struct mfd_cell *`) 多功能设备子单元配置(MFD 设备)。 --- ### 示例:注册 UART 设备 ```c struct platform_device uart_device = { .name = "ttyS0", // 驱动匹配名 .id = 0, // 设备实例 ID .num_resources = ARRAY_SIZE(uart_resources), .resource = uart_resources, // 资源数组 .dev = { .platform_data = &uart_pdata, // 私有数据 .of_node = uart_dt_node, // 设备树节点 } }; platform_device_register(&uart_device); // 注册到内核 ``` ### 查看已注册设备 ```bash ls /sys/bus/platform/devices/ # 列出所有 platform_device cat /sys/devices/platform/uart0/resources # 查看资源分配 ``` --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值