在后续Linux设备驱动开发中会用到此函数,且在后续文章中会跳转过来,提前埋下种子。
文中提到的《设备树如何转换成platform_device》和《Linux设备树解析》,后续会更新。
功能:从设备中获取相关资源。
参数@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;
}
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)