当前位置: 首页 > 工具软件 > Platform > 使用案例 >

platform_get_resource

颜均
2023-12-01

platform_get_resource函数

在后续Linux设备驱动开发中会用到此函数,且在后续文章中会跳转过来,提前埋下种子。
文中提到的《设备树如何转换成platform_device》和《Linux设备树解析》,后续会更新。

platform_get_resource函数原型

功能:从设备中获取相关资源。
参数@dev:平台设备。描述设备信息的,有设备树描述。
参数@type: 资源类型。下文详细讲解。
参数@num: 资源索引。同类型资源进行重新编号后的下标编号,注意和资源数组中的元素数量即num_resources区别。(要注意这一点),最后总结时会讲到。

/**
 * platform_get_resource - get a resource for a device
 * @dev: platform device
 * @type: resource type
 * @num: resource index
 */
struct resource *platform_get_resource(struct platform_device *dev,
				       unsigned int type, unsigned int num)
{
	u32 i;

	for (i = 0; i < dev->num_resources; i++) {
		struct resource *r = &dev->resource[i];

		if (type == resource_type(r) && num-- == 0)
			return r;
	}
	return NULL;
}

补充Linux下设备资源相关知识

IIC、SPI、GPIO 等这些外设都有对应的寄存器,这些寄存器其实就是一组内存空间,Linux内核使用resource结构体来描述一段内存空间,“resource”翻译出来就是“资源”,因此用resource结构体描述的都是设备资源信息,resource 结构体定义在include/linux/ioport.h 中,定义如下:

/*
 * Resources are tree-like, allowing
 * nesting etc..
 */
 struct resource {
 resource_size_t start;
 resource_size_t end;
 const char *name;
 unsigned long flags;
 unsigned long desc;
 struct resource *parent, *sibling, *child;
 };


对于 32 位的 SOC 来说,resource_size_t 是 u32 类型的。
其中 start 表示开始地址,end 表示结束地址,
name 是这个资源的名字,flags 是资源标志位,一般表示资源类型,
可选的资源标志定义在文件 include/linux/ioport.h 中,如下所示:

/*
 * IO resources have these defined flags.
 *
 * PCI devices expose these flags to userspace in the "resource" sysfs file,
 * so don't move them.
 */
#define IORESOURCE_BITS		    0x000000ff	/* Bus-specific bits */

#define IORESOURCE_TYPE_BITS	0x00001f00	/* Resource type */
#define IORESOURCE_IO		    0x00000100	/* PCI/ISA I/O ports */
#define IORESOURCE_MEM		    0x00000200
#define IORESOURCE_REG		    0x00000300	/* Register offsets */
#define IORESOURCE_IRQ		    0x00000400
#define IORESOURCE_DMA	    	0x00000800
#define IORESOURCE_BUS		    0x00001000

#define IORESOURCE_PREFETCH	    0x00002000	/* No side effects */
#define IORESOURCE_READONLY		0x00004000
#define IORESOURCE_CACHEABLE	0x00008000
#define IORESOURCE_RANGELENGTH	0x00010000
#define IORESOURCE_SHADOWABLE	0x00020000

#define IORESOURCE_SIZEALIGN	0x00040000	/* size indicates alignment */
#define IORESOURCE_STARTALIGN	0x00080000	/* start field is alignment */

#define IORESOURCE_MEM_64		0x00100000
#define IORESOURCE_WINDOW		0x00200000	/* forwarded by bridge */
#define IORESOURCE_MUXED		0x00400000	/* Resource is software muxed */

#define IORESOURCE_EXT_TYPE_BITS 0x01000000	/* Resource extended types */
#define IORESOURCE_SYSRAM		0x01000000	/* System RAM (modifier) */

#define IORESOURCE_EXCLUSIVE	0x08000000	/* Userland may not map this resource */

#define IORESOURCE_DISABLED		0x10000000
#define IORESOURCE_UNSET		0x20000000	/* No address assigned yet */
#define IORESOURCE_AUTO			0x40000000
#define IORESOURCE_BUSY			0x80000000	/* Driver has marked this resource busy */

大家一般最常见的资源标志就是IORESOURCE_MEM ,IORESOURCE_REG 和 IORESOURCE_IRQ 等。比如后续更新的Linux下CAN驱动开发中也使用了platform_get_resource(pdev,IORESOURCE_MEM, 0),IORESOURCE_MEM代表内存地址。

回到正题,继续分析上面函数

描述设备信息,传统的用 platform_device 来描述设备信息。当 Linux 内核支持了设备树以后就不需要用户手动去注册platform 设备了。因为设备信息都放到了设备树中去描述,Linux 内核启动的时候会从设备树中读取设备信息,然后将其组织成 platform_device 形式,至于设备树到 platform_device 的具体过程参考《设备树如何转换成platform_device》

下面列举platform_device这种“古老”方式来编写设备信息,用来分析platform_get_resource如何拿到设备信息的。设备树方式来编写设备信息的框架就不写了,现在很常用,后续更新设备树的详细介绍,包括语法等,参考《Linux设备树解析》。

/******************** 采用自定义platform_device这种“古老”方式来编写设备信息 **********************/
// 简要描述xxxdevice.c 即platform_device框架如下所示
    
#include <linux/xxx.h>
......
  
/* 寄存器地址定义*/
#define PERIPH1_REGISTER_BASE (0X20000000) /* 外设 1 寄存器首地址 */
#define PERIPH2_REGISTER_BASE (0X020E0068) /* 外设 2 寄存器首地址 */
#define REGISTER_LENGTH 4

/* 设备资源信息 */
static struct resource xxx_resources[] = {
	[0] = {
	.start = PERIPH1_REGISTER_BASE,
	.end = (PERIPH1_REGISTER_BASE + REGISTER_LENGTH - 1),
	.flags = IORESOURCE_MEM,
	},
	[1] = {
	.start = PERIPH2_REGISTER_BASE,
	.end = (PERIPH2_REGISTER_BASE + REGISTER_LENGTH - 1),
	.flags = IORESOURCE_MEM,
	},
};

/* platform 设备结构体 */
static struct platform_device xxxdevice = {
	.name = "xxx-peripheral",
	.id = -1,
	.num_resources = ARRAY_SIZE(xxx_resources),
	.resource = xxx_resources,
};

/* 设备模块加载 */
static int __init xxxdevice_init(void)
{
	return platform_device_register(&xxxdevice);
}

/* 设备模块注销 */
static void __exit xxx_resourcesdevice_exit(void)
{
	platform_device_unregister(&xxxdevice);
}

module_init(xxxdevice_init);
module_exit(xxxdevice_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("TieTouXiaoGe");

/*********************************************** 结束 *********************************************/

所以上述for循环的次数即资源数组xxx_resources[]的元素数量。
struct resource *r = &dev->resource[i]; 表示从第一份资源开始逐个搜索,
然后if (type == resource_type( r ) && num-- == 0),这行代码首先通过
type == resource_type( r )判断当前这份资源的类型是否匹配,如果匹配则再通过num-- == 0判断是否是你要的(这里先判断是否等于0再自减1),如果不匹配重新提取下一份资源而不会执行num-- == 0这一句代码。通过以上两步就能定位到你要找的资源了,接着把资源返回即可。如果都不匹配则return NULL。

总结

// 设备驱动开发中常用 
struct resource *res
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
// 获取设备内存地址资源。
    
struct resource *platform_get_resource(struct platform_device *dev,
			                                    unsigned int type, unsigned int num)

unsigned int type决定资源的类型,unsigned int num决定type类型的第几份资源(从0开始)。即使同类型资源在资源数组中不是连续排放也可以定位得到该资源。比如第一份IORESOURCE_IRQ类型资源在resource[2],而第二份在resource[5],那platform_get_resource(pdev,IORESOURCE_IRQ,0);可以定位第一份IORESOURCE_IRQ资源;
platform_get_resource(pdev,IORESOURCE_IRQ,1);可以定位第二份IORESOURCE_IRQ资源。
之所以能定位到资源,在于函数实现中的这一行代码:
if (type == resource_type( r ) && num-- == 0)该行代码,如果没有匹配资源类型,
num-- == 0不会执行而重新提取下一份资源,只有资源匹配了才会寻找该类型的第几份资源,即使这些资源排放不连续。(解释了上面的参数@num)

 类似资料:

相关阅读

相关文章

相关问答