LV020-驱动和设备资源的获取
前面我们已经知道了怎么让平台设备和平台驱动进行匹配,那么匹配的目录肯定是驱动获取设备中的资源了,那怎么获取?设备能获取驱动的信息吗,能的话,又该怎么获取呢?
一、设备获取驱动的资源
前面在学习平台设备和平台驱动的时候,我们有看到这个 struct platform_device_id 结构体,它在 struct platform_device 和 struct platform_driver 都有,设备获取驱动资源可以通过这个来进行,接下来就来了解一下吧。
1. struct platform_device_id
先来回顾一下这个结构体,struct platform_device_id 结构体定义如下:
struct platform_device_id {
char name[PLATFORM_NAME_SIZE];
kernel_ulong_t driver_data;
};在 struct platform_device_id 这个结构体中,有两个成员,第一个是数组用于指定驱动的名称,总线进行匹配时,会依据该结构体的 name 成员与 struct platform_device 中的成员 name 进行比较匹配, 另一个成员变量 driver_data,则是用于来保存设备的配置。我们知道在同系列的设备中,往往只是某些寄存器的配置不一样,为了减少代码的冗余, 尽量做到一个驱动可以匹配多个设备的目的。
2. 平台设备怎么得到这个结构体数据?
前面学习 id 匹配的时候,完成 id 匹配的是这个 platform_match_id() 函数:
static const struct platform_device_id *platform_match_id(
const struct platform_device_id *id,
struct platform_device *pdev)
{
while (id->name[0]) {
if (strcmp(pdev->name, id->name) == 0) {
pdev->id_entry = id;
return id;
}
id++;
}
return NULL;
}我们可以看一下传进来的都是什么:

可以看到,传进来的指针 id 指向的是 platform_driver.id_table,而指针 pdev 则是 platform_device 。在函数内部,遍历 platform_driver.id_table 中的成员,当 platform_driver.id_table[i].name 和 platform_device.name 匹配的相等的时候,就将 platform_device.id_entry 这个指针指向 platform_driver.id_table[i] 的地址:

当匹配成功的时候,我们就可以以访问 platform_device.id_entry 来获取驱动中的数据了,即访问平台设备的 platform_device.id_entry. driver_data 成员就是访问平台驱动的 platform_driver.id_table[i].driver_data 成员。
3. 应用场景
可以以以 imx 的串口为例,具体看下这个结构体的作用:
static struct imx_uart_data imx_uart_devdata[] = {
[IMX1_UART] = {
.uts_reg = IMX1_UTS,
.devtype = IMX1_UART,
},
[IMX21_UART] = {
.uts_reg = IMX21_UTS,
.devtype = IMX21_UART,
},
[IMX6Q_UART] = {
.uts_reg = IMX21_UTS,
.devtype = IMX6Q_UART,
},
};
static struct platform_device_id imx_uart_devtype[] = {
{
.name = "imx1-uart",
.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX1_UART],
},
{
.name = "imx21-uart",
.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART],
},
{
.name = "imx6q-uart",
.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART],
},
{
/* sentinel */
}
};在上面的代码中,支持三种设备的串口,支持 imx1、imx21、imx6q 三种不同系列芯片,他们之间区别在于串口的 test 寄存器地址不同。 当总线成功配对平台驱动以及平台设备时,会将对应的 id_table 条目赋值给平台设备的 id_entry 成员,而平台驱动的 probe 函数是以平台设备为参数, 这样的话,就可以拿到当前设备串口的 test 寄存器地址了。
4. 设备获取驱动资源 demo
4.1 demo 源码
06_platform_bus/05_id_match_get_drv_info
4.2 开发板验证
将编译完的 sdriver_demo.ko、sdevice_demo.ko 拷贝到开发板,然后执行以下命令加载:
insmod sdriver_demo.ko
insmod sdevice_demo.ko然后就会看到以下打印信息:

可以看到驱动中的 xxx_probe()函数执行,然后会有提示驱动和设备匹配成功。为了方便查看平台设备获取驱动中资源的情况,我们可以看一下 /sys/bus/platform/devices/sdev 中的属性文件的值:
cat /sys/bus/platform/devices/sdev/sdev_attr_reg
cat /sys/bus/platform/devices/sdev/sdev_attr_type
其实可以在匹配之前看一下的:

二、驱动获取设备资源
1. 怎么获取设备资源?
这个就比较简单了,因为传到驱动中的是平台设备的哪个全局变量,我们可以直接访问平台设备的资源数组。
static int sdrv_probe(struct platform_device *pdev)
{
PRT("probing platform device & driver!pdev->name=%s\n", pdev->name);
// 添加设备特定的操作
return 0;
}例如上面的 sdrv_probe()函数,我们可以通过 pdev 直接访问的,也就是说 pdev 直接指向这个对象:
/**
* 定义一个平台设备全局变量
*/
static struct platform_device g_sdev_platform = {
.name = PLATFORM_DEV_NAME, // 设备名称
.id = -1, // 设备 ID
.num_resources = ARRAY_SIZE(g_sdev_resources), // 资源数量
.resource = g_sdev_resources, // 资源数组
.dev.release = sdev_release, // 释放资源的回调函数
.id_entry = &g_sdev_id, // 用于 ID 匹配时获取驱动返回的匹配信息
};通过 platform_device.resource 就可以获取资源数组中所有数据,通过 platform_device.num_resources 可以获取到设备一共有多少个资源。
2. 获取资源的函数
linux 内核提供了几个函数来帮我们获取资源。其实前面都了解设备信息的时候都了解过了,就那几个函数。
2.1 platform_get_resource()
platform_get_resource() 定义如下:
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
{
int 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;
}
EXPORT_SYMBOL_GPL(platform_get_resource);该函数返回该 dev 中某类型(type)资源中的第几个(num,其中 num 的值从 0 开始) 。
2.2 platform_get_irq()
platform_get_irq() 函数定义如下:
/**
* platform_get_irq - get an IRQ for a device
* @dev: platform device
* @num: IRQ number index
*/
int platform_get_irq(struct platform_device *dev, unsigned int num)
{
//......
}返回该 dev 所用的第几个(num)中断。
2.3 platform_get_resource_byname()
platform_get_resource_byname() 定义如下:
/**
* platform_get_resource_byname - get a resource for a device by name
* @dev: platform device
* @type: resource type
* @name: resource name
*/
struct resource *platform_get_resource_byname(struct platform_device *dev,
unsigned int type,
const char *name)
{
int i;
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
if (unlikely(!r->name))
continue;
if (type == resource_type(r) && !strcmp(r->name, name))
return r;
}
return NULL;
}
EXPORT_SYMBOL_GPL(platform_get_resource_byname);通过名字(name)返回该 dev 的某类型(type)资源。
2.4 platform_get_irq_byname()
/**
* platform_get_irq_byname - get an IRQ for a device by name
* @dev: platform device
* @name: IRQ name
*/
int platform_get_irq_byname(struct platform_device *dev, const char *name)
{
struct resource *r;
if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {
int ret;
ret = of_irq_get_byname(dev->dev.of_node, name);
if (ret > 0 || ret == -EPROBE_DEFER)
return ret;
}
r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name);
return r ? r->start : -ENXIO;
}
EXPORT_SYMBOL_GPL(platform_get_irq_byname);通过名字(name)返回该 dev 的中断号。
2.5 dev_get_platdata()
对于存放在 device 结构体中成员 platform_data 的软件信息,我们可以使用 dev_get_platdata() 函数来获取:
static inline void *dev_get_platdata(const struct device *dev)
{
return dev->platform_data;
}2.6 resource_size()
resource_size() 函数用于获取资源的大小,定义如下:
static inline resource_size_t resource_size(const struct resource *res)
{
return res->end - res->start + 1;
}3. 驱动获取设备资源 demo
3.1 demo 源码
06_platform_bus/06_drv_get_dev_resource
3.2 开发板验证
将编译完的 sdriver_demo.ko、sdevice_demo.ko 拷贝到开发板,然后执行以下命令加载:
insmod sdriver_demo.ko
insmod sdevice_demo.ko然后就会看到以下打印信息:

可以看到驱动中的 xxx_probe()函数执行,然后会有提示驱动和设备匹配成功。然后便会在 xxx_probe()函数中分两种方式打印出从平台设备获取的资源。