LV10-10-platform-02-三种匹配方式

本文主要是platform——平台总线驱动中的三种匹配方式的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
Linux开发板 华清远见 底板: FS4412_DEV_V5 核心板: FS4412 V2
u-boot 2013.01
点击查看本文参考资料
参考方向参考原文
------
点击查看相关文件下载
文件下载链接
------

一、匹配方式介绍

1. 匹配函数

在上一篇笔记中已经找过这个匹配函数了,platform匹配函数定义在linux内核源码的下边这个文件中:

1
drivers/base/platform.c

我们打开这文件可以看到这个函数定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);

/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;

/* Then try ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;

/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;

/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}

2. 匹配方式

我们从匹配函数中可以看到,应该有四种匹配方式,但是有一种我们似乎基本们怎么用过,所以这里就只讨论三种匹配方式:

  • 名称匹配

一个驱动只对应一个设备,它是最简单的匹配方式,优先级最低。

  • id匹配(可想象成八字匹配)

一个驱动可以对应多个设备,优先级高于名称匹配,但是低于设备树匹配。

device模块中,idname成员必须与struct platform_device中的name成员内容一致。因此device模块中,struct platform_device中的name成员必须指定。

driver模块中,struct platform_driver成员drivername成员必须指定,但与device模块中name可以不相同。

  • 设备树匹配

内核启动时根据设备树自动产生的设备,优先级最高。我们一般会使用设备树的使用compatible属性进行匹配,注意设备树中compatible属性值不要包含空白字符id_table可不设置,但struct platform_driver成员drivername成员必须设置。

二、名称匹配框架

1. hello_app.c

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : hello_app.c
* Author : qidaink
* Date : 2022-08-29
* Version :
* Description: platform总线驱动 —— 名称匹配基础框架测试APP
* Others :
* Log :
* ======================================================
*/

#include <stdio.h> /* printf */
#include <sys/types.h> /* open */
#include <sys/stat.h> /* open */
#include <fcntl.h> /* open */
#include <unistd.h> /* close sleep */
#include <sys/ioctl.h> /* ioctl */

int main(int argc, char *argv[])
{
/* 0.相关变量定义 */
int fd = -1; /* 文件描述符 */
/* 1.判断参数个数是否合法 */
/* 1.1判断参数数量 */
if (argc < 2)
{
printf("\nusage:\n");
printf("%s /dev/dev_name\n", argv[0]);
return -1;
}
/* 2.打开字符设备 */
if ((fd = open(argv[1], O_RDWR)) < 0) /* 默认是阻塞的 */
{
printf("open %s failed!\n", argv[1]);
return -1;
}

/* 4.关闭字符设备 */
close(fd);
fd = -1;

return 0;
}

2. hello_device.c

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : hello_device.c
* Author : qidaink
* Date : 2022-08-29
* Version :
* Description: platform总线驱动设备程序 —— 名称匹配基础框架
* Others :
* Log :
* ======================================================
*/

/** 头文件 */
#include <linux/module.h> /* MODULE_LICENSE */
#include <linux/kernel.h>
#include <linux/init.h> /* module_init module_exit */
#include <linux/platform_device.h>
//=======================================================//
/** 宏定义 */
#define MODULE_NAME "hello" /* 模块名称 */

//=======================================================//
/** 平台总线相关 */
/**
* @Function : hello_device_release
* @brief : 删除设备
* @param pdev : struct device * 类型
* @return : none
* @Description: 函数名赋值给 struct platform_device 结构体的 .dev.release 成员
*/
void hello_device_release(struct device *pdev)
{
printk("[INFO]hello_device_release is called!\n");
}

/**
* 定义资源数组(注意不能有重叠区域,否则会报错:failed to claim resource 2/3/4/5)
* 结构体变量名赋值给 struct platform_device 结构体的 .resource 成员
*/
struct resource hello_device_res[] = {
[0] = {.start = 0x1000, .end = 0x1003, .name = "reg1", .flags = IORESOURCE_MEM},
[1] = {.start = 0x2000, .end = 0x2003, .name = "reg2", .flags = IORESOURCE_MEM},
[2] = {.start = 10, .end = 10, .name = "irq1", .flags = IORESOURCE_IRQ},
[3] = {.start = 0x3000, .end = 0x3003, .name = "reg3", .flags = IORESOURCE_MEM},
[4] = {.start = 100, .end = 100, .name = "irq2", .flags = IORESOURCE_IRQ},
[5] = {.start = 62, .end = 62, .name = "irq3", .flags = IORESOURCE_IRQ},
};

/** 定义platform平台设备 */
struct platform_device hello_device = {
.name = MODULE_NAME, /* 必须初始化 name 成员 */
.dev.release = hello_device_release, /* 删除设备 */
.resource = hello_device_res, /* 资源数组 */
.num_resources = ARRAY_SIZE(hello_device_res), /* 资源数组元素个数 */
};

//=======================================================//
/** 整个模块相关 */
/* 驱动入口函数 */
int __init hello_device_init(void)
{
/* 1.注册:把指定设备添加到内核中平台总线的设备列表,等待匹配,匹配成功则会调用驱动(driver)中 probe */
platform_device_register(&hello_device);
/* 2.打印加载模块的提示信息 */
printk("[INFO]This module [%s](device) is loaded!\n", MODULE_NAME);
return 0;
}

/* 驱动出口函数 */
void __exit hello_device_exit(void)
{
/* 1.注销:把指定设备从设备列表中删除 */
platform_device_unregister(&hello_device);
/* 2.打印卸载模块的提示信息 */
printk("[INFO]This module [%s](device) is exited!\n", MODULE_NAME);
}

/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(hello_device_init);
module_exit(hello_device_exit);

/* 模块信息(通过 modinfo module_name 查看) */
MODULE_LICENSE("GPL"); /* 源码的许可证协议 */
MODULE_AUTHOR("qidaink"); /* 字符串常量内容为模块作者说明 */
MODULE_DESCRIPTION("Description"); /* 字符串常量内容为模块功能说明 */
MODULE_ALIAS("module's other name"); /* 字符串常量内容为模块别名 */

3. hello_driver.c

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : hello_driver.c
* Author : qidaink
* Date : 2022-08-29
* Version :
* Description: platform总线驱动驱动程序 —— 名称匹配基础框架
* Others :
* Log :
* ======================================================
*/

/** 头文件 */
#include <linux/module.h> /* MODULE_LICENSE */
#include <linux/kernel.h>
#include <linux/init.h> /* module_init module_exit */
#include <linux/platform_device.h> /* platform_get_resource */

//=======================================================//
/** 宏定义 */
#define MODULE_NAME "hello" /* 模块名称 */

//=======================================================//
/** 平台驱动相关 */
/**
* @Function : hello_driver_probe
* @brief : 设备和驱动匹配成功之后的调用函数
* @param p_pltdev : struct device * 类型
* @return : int 类型
* @Description : 函数名赋值给 struct platform_driver 结构体的 .probe 成员
*/
int hello_driver_probe(struct platform_device *p_pltdev)
{
/* 0.相关变量定义 */
struct resource *pres = NULL; /* 暂存资源 */
int i = 0;
/* 1.获取设备寄存器资源 */
printk("=====================================================\n");
for(i = 0; i < 3; i++)
{
pres = platform_get_resource(p_pltdev, IORESOURCE_MEM, i);
printk("IORESOURCE_MEM[%d]start=0x%x,end=0x%x,name=%s\n", i,(unsigned int)pres->start, (unsigned int)pres->end, pres->name);
}
/* 2.获取中断号资源 */
for(i = 0; i < 3; i++)
{
pres = platform_get_resource(p_pltdev, IORESOURCE_IRQ, i);
printk("IORESOURCE_IRQ[%d]start=0x%x,end=0x%x,name=%s\n", i,(int)pres->start, (int)pres->end, pres->name);
}
printk("=====================================================\n");
/* 打印提示信息 */
printk("[INFO]hello_driver_probe is called!\n");
return 0;
}
/**
* @Function : hello_driver_remove
* @brief : 卸载设备
* @param p_pltdev : struct platform_device * 类型
* @return : int 类型
* @Description : 函数名赋值给 struct platform_driver 结构体的 .remove 成员
*/
int hello_driver_remove(struct platform_device *p_pltdev)
{
printk("[INFO]hello_driver_remove is called!\n");
return 0;
}

/** 定义platform平台驱动 */
struct platform_driver hello_driver = {
.driver = {
.name = MODULE_NAME, /* 必须初始化 name 成员 */
},
.probe = hello_driver_probe, /* 设备和驱动匹配成功之后的调用函数 */
.remove = hello_driver_remove, /* 卸载设备 */
};

//=======================================================//
/** 整个模块相关 */
/* 驱动入口函数 */
int __init hello_driver_init(void)
{
/* 1.注册平台设备驱动 */
platform_driver_register(&hello_driver);
/* 2.打印加载模块的提示信息 */
printk("[INFO]This module [%s](driver) is loaded!\n", MODULE_NAME);
return 0;
}

/* 驱动出口函数 */
void __exit hello_driver_exit(void)
{
/* 1.注销平台设备驱动 */
platform_driver_unregister(&hello_driver);
/* 2.打印卸载模块的提示信息 */
printk("[INFO]This module [%s](driver) is exited!\n", MODULE_NAME);
}
/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(hello_driver_init);
module_exit(hello_driver_exit);

/* 模块信息(通过 modinfo module_name 查看) */
MODULE_LICENSE("GPL"); /* 源码的许可证协议 */
MODULE_AUTHOR("qidaink"); /* 字符串常量内容为模块作者说明 */
MODULE_DESCRIPTION("Description"); /* 字符串常量内容为模块功能说明 */
MODULE_ALIAS("module's other name"); /* 字符串常量内容为模块别名 */


4. Makefile

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
##============================================================================#
# Copyright © hk. 2022-2025. All rights reserved.
# File name : Makefile
# Author : qidaink
# Date : 2022-08-28
# Version :
# Description: 驱动模块编译Makefile
# Others :
# Log :
##============================================================================#
##

# 模块名和模块测试APP名称
MODULE_NAME := hello
MODULE_DEVICE_NAME := $(MODULE_NAME)_device
MODULE_DRIVER_NAME := $(MODULE_NAME)_driver
APP_NAME := $(MODULE_NAME)_app

DEVICE_TREE_NAME := exynos4412-fs4412.dts
# NFS 共享目录
ROOTFS ?= /home/hk/4nfs/rootfs
MODULE_DIR ?= $(ROOTFS)/01myDrivers

ifeq ($(KERNELRELEASE),)

# 选择可执行文件运行的平台
ifeq ($(ARCH), arm)
KERNELDIR ?= /home/hk/5linux/linux-3.14
GCC_TOOL ?= arm-none-linux-gnueabi-gcc
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
GCC_TOOL ?= gcc
endif


PWD := $(shell pwd)
DEVICE_TREE_DIR ?= $(KERNELDIR)/arch/arm/boot/dts/


# 编译模块和测试程序
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
$(GCC_TOOL) $(APP_NAME).c -Wall -o $(APP_NAME)

modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

# 拷贝相关文件到nfs共享目录
copy:
sudo cp $(MODULE_DEVICE_NAME).ko $(MODULE_DIR)
sudo cp $(MODULE_DRIVER_NAME).ko $(MODULE_DIR)
sudo cp $(APP_NAME) $(MODULE_DIR)

.PHONY: clean
clean:
rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions .cache.mk $(APP_NAME)

help:
@echo "\033[1;32m================================ Help ================================\033[0m"
@echo "Ubuntu may need to add sudo:"
@echo "insmod <module_name>.ko # Load module"
@echo "rmmod <module_name> # Uninstall the module"
@echo "dmesg -C # Clear the kernel print information"
@echo "lsmod # Check the kernel modules that have been inserted"
@echo "dmesg # View information printed by the kernel"
@echo "file <module_name>.ko # View \".Ko\" file information"
@echo "make ARCH=arm # arm platform"
@echo "\033[1;32m======================================================================\033[0m"

print:
@echo "DEVICE_TREE_DIR = $(DEVICE_TREE_DIR)"

else
CONFIG_MODULE_SIG=n
obj-m += $(MODULE_DEVICE_NAME).o
obj-m += $(MODULE_DRIVER_NAME).o
endif

三、ID匹配框架

1. hello_app.c

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : hello_app.c
* Author : qidaink
* Date : 2022-08-29
* Version :
* Description: platform总线驱动 —— ID匹配基础框架测试APP
* Others :
* Log :
* ======================================================
*/

#include <stdio.h> /* printf */
#include <sys/types.h> /* open */
#include <sys/stat.h> /* open */
#include <fcntl.h> /* open */
#include <unistd.h> /* close sleep */
#include <sys/ioctl.h> /* ioctl */

int main(int argc, char *argv[])
{
/* 0.相关变量定义 */
int fd = -1; /* 文件描述符 */
/* 1.判断参数个数是否合法 */
/* 1.1判断参数数量 */
if (argc < 2)
{
printf("\nusage:\n");
printf("%s /dev/dev_name\n", argv[0]);
return -1;
}
/* 2.打开字符设备 */
if ((fd = open(argv[1], O_RDWR)) < 0) /* 默认是阻塞的 */
{
printf("open %s failed!\n", argv[1]);
return -1;
}

/* 4.关闭字符设备 */
close(fd);
fd = -1;

return 0;
}

2. hello_device.c

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : hello_device.c
* Author : qidaink
* Date : 2022-08-29
* Version :
* Description: platform总线驱动设备程序 —— ID匹配基础框架
* Others :
* Log :
* ======================================================
*/

/** 头文件 */
#include <linux/module.h> /* MODULE_LICENSE */
#include <linux/kernel.h>
#include <linux/init.h> /* module_init module_exit */
#include <linux/platform_device.h>
//=======================================================//
/** 宏定义 */
#define MODULE_NAME "hello_dev" /* 模块名称 */

//=======================================================//
/** 平台总线相关 */
/**
* @Function : hello_device_release
* @brief : 删除设备
* @param pdev : struct device * 类型
* @return : none
* @Description: 函数名赋值给 struct platform_device 结构体的 .dev.release 成员
*/
void hello_device_release(struct device *pdev)
{
printk("[INFO]hello_device_release is called!\n");
}

/**
* 定义资源数组(注意不能有重叠区域,否则会报错:failed to claim resource 2/3/4/5)
* 结构体变量名赋值给 struct platform_device 结构体的 .resource 成员
*/
struct resource hello_device_res[] = {
[0] = {.start = 0x1000, .end = 0x1003, .name = "reg1", .flags = IORESOURCE_MEM},
[1] = {.start = 0x2000, .end = 0x2003, .name = "reg2", .flags = IORESOURCE_MEM},
[2] = {.start = 10, .end = 10, .name = "irq1", .flags = IORESOURCE_IRQ},
[3] = {.start = 0x3000, .end = 0x3003, .name = "reg3", .flags = IORESOURCE_MEM},
[4] = {.start = 100, .end = 100, .name = "irq2", .flags = IORESOURCE_IRQ},
[5] = {.start = 62, .end = 62, .name = "irq3", .flags = IORESOURCE_IRQ},
};

/**
* 定义ID匹配数组
* 结构体变量名赋值给 struct platform_device 结构体的 .id_entry 成员
*/
struct platform_device_id hello_id = {
.name = MODULE_NAME, /* 这里要与 struct platform_device 结构体中 name 成员相同 */
};

/** 定义platform平台总线 */
struct platform_device hello_device = {
.name = MODULE_NAME, /* 必须初始化 name 成员 */
.dev.release = hello_device_release, /* 删除设备 */
.resource = hello_device_res, /* 资源数组 */
.num_resources = ARRAY_SIZE(hello_device_res), /* 资源数组元素个数 */
.id_entry = &hello_id,
};

//=======================================================//
/** 整个模块相关 */
/* 驱动入口函数 */
int __init hello_device_init(void)
{
/* 1.注册:把指定设备添加到内核中平台总线的设备列表,等待匹配,匹配成功则会调用驱动(driver)中 probe */
platform_device_register(&hello_device);
/* 2.打印加载模块的提示信息 */
printk("[INFO]This module [%s](device) is loaded!\n", MODULE_NAME);
return 0;
}

/* 驱动出口函数 */
void __exit hello_device_exit(void)
{
/* 1.注销:把指定设备从设备列表中删除 */
platform_device_unregister(&hello_device);
/* 2.打印卸载模块的提示信息 */
printk("[INFO]This module [%s](device) is exited!\n", MODULE_NAME);
}

/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(hello_device_init);
module_exit(hello_device_exit);

/* 模块信息(通过 modinfo module_name 查看) */
MODULE_LICENSE("GPL"); /* 源码的许可证协议 */
MODULE_AUTHOR("qidaink"); /* 字符串常量内容为模块作者说明 */
MODULE_DESCRIPTION("Description"); /* 字符串常量内容为模块功能说明 */
MODULE_ALIAS("module's other name"); /* 字符串常量内容为模块别名 */

3. hello_driver.c

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : hello_driver.c
* Author : qidaink
* Date : 2022-08-29
* Version :
* Description: platform总线驱动驱动程序 —— ID匹配基础框架
* Others :
* Log :
* ======================================================
*/

/** 头文件 */
#include <linux/module.h> /* MODULE_LICENSE */
#include <linux/kernel.h>
#include <linux/init.h> /* module_init module_exit */
#include <linux/platform_device.h> /* platform_get_resource */

//=======================================================//
/** 宏定义 */
#define MODULE_NAME "hello_drv" /* 模块名称 */

//=======================================================//
/** 平台驱动相关 */
/**
* @Function : hello_driver_probe
* @brief : 设备和驱动匹配成功之后的调用函数
* @param p_pltdev : struct device * 类型
* @return : int 类型
* @Description : 函数名赋值给 struct platform_driver 结构体的 .probe 成员
*/
int hello_driver_probe(struct platform_device *p_pltdev)
{
/* 0.相关变量定义 */
struct resource *pres = NULL; /* 暂存资源 */
int i = 0;
/* 1.获取设备寄存器资源 */
printk("=====================================================\n");
for(i = 0; i < 3; i++)
{
pres = platform_get_resource(p_pltdev, IORESOURCE_MEM, i);
printk("IORESOURCE_MEM[%d]start=0x%x,end=0x%x,name=%s\n", i,(unsigned int)pres->start, (unsigned int)pres->end, pres->name);
}
/* 2.获取中断号资源 */
for(i = 0; i < 3; i++)
{
pres = platform_get_resource(p_pltdev, IORESOURCE_IRQ, i);
printk("IORESOURCE_IRQ[%d]start=0x%x,end=0x%x,name=%s\n", i,(int)pres->start, (int)pres->end, pres->name);
}
printk("=====================================================\n");
/* 打印提示信息 */
printk("[INFO]hello_driver_probe is called!\n");
return 0;
}
/**
* @Function : hello_driver_remove
* @brief : 卸载设备
* @param p_pltdev : struct platform_device * 类型
* @return : int 类型
* @Description : 函数名赋值给 struct platform_driver 结构体的 .remove 成员
*/
int hello_driver_remove(struct platform_device *p_pltdev)
{
printk("[INFO]hello_driver_remove is called!\n");
return 0;
}
/**
* 定义匹配ID数组
* 与数组中含有的名称相同的设备都可以匹配到该驱动
* 结构体变量名赋值给 struct platform_driver 结构体的 .id_table 成员
*/
struct platform_device_id hello_driver_ids[] =
{
[0] = {.name = "hello_dev"},
[1] = {.name = "a"},
[2] = {} /* means ending */
};

/** 定义platform平台驱动 */
struct platform_driver hello_driver = {
.driver = {
.name = MODULE_NAME, /* 必须初始化 name 成员 */
},
.probe = hello_driver_probe, /* 设备和驱动匹配成功之后的调用函数 */
.remove = hello_driver_remove, /* 卸载设备 */
.id_table = hello_driver_ids, /* 可匹配的设备ID数组 */
};

//=======================================================//
/** 整个模块相关 */
/* 驱动入口函数 */
int __init hello_driver_init(void)
{
/* 1.注册平台设备驱动 */
platform_driver_register(&hello_driver);
/* 2.打印加载模块的提示信息 */
printk("[INFO]This module [%s](driver) is loaded!\n", MODULE_NAME);
return 0;
}

/* 驱动出口函数 */
void __exit hello_driver_exit(void)
{
/* 1.注销平台设备驱动 */
platform_driver_unregister(&hello_driver);
/* 2.打印卸载模块的提示信息 */
printk("[INFO]This module [%s](driver) is exited!\n", MODULE_NAME);
}
/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(hello_driver_init);
module_exit(hello_driver_exit);
/* 模块信息(通过 modinfo module_name 查看) */
MODULE_LICENSE("GPL"); /* 源码的许可证协议 */
MODULE_AUTHOR("qidaink <2038035593@qq.com>"); /* 字符串常量内容为模块作者说明 */
MODULE_DESCRIPTION("Description"); /* 字符串常量内容为模块功能说明 */
MODULE_ALIAS("module's other name"); /* 字符串常量内容为模块别名 */

4. Makefile

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
##============================================================================#
# Copyright © hk. 2022-2025. All rights reserved.
# File name : Makefile
# Author : qidaink
# Date : 2022-08-28
# Version :
# Description: 驱动模块编译Makefile
# Others :
# Log :
##============================================================================#
##

# 模块名和模块测试APP名称
MODULE_NAME := hello
MODULE_DEVICE_NAME := $(MODULE_NAME)_device
MODULE_DRIVER_NAME := $(MODULE_NAME)_driver
APP_NAME := $(MODULE_NAME)_app

DEVICE_TREE_NAME := exynos4412-fs4412.dts
# NFS 共享目录
ROOTFS ?= /home/hk/4nfs/rootfs
MODULE_DIR ?= $(ROOTFS)/01myDrivers

ifeq ($(KERNELRELEASE),)

# 选择可执行文件运行的平台
ifeq ($(ARCH), arm)
KERNELDIR ?= /home/hk/5linux/linux-3.14
GCC_TOOL ?= arm-none-linux-gnueabi-gcc
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
GCC_TOOL ?= gcc
endif


PWD := $(shell pwd)
DEVICE_TREE_DIR ?= $(KERNELDIR)/arch/arm/boot/dts/


# 编译模块和测试程序
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
$(GCC_TOOL) $(APP_NAME).c -Wall -o $(APP_NAME)

modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

# 拷贝相关文件到nfs共享目录
copy:
sudo cp $(MODULE_DEVICE_NAME).ko $(MODULE_DIR)
sudo cp $(MODULE_DRIVER_NAME).ko $(MODULE_DIR)
sudo cp $(APP_NAME) $(MODULE_DIR)

.PHONY: clean
clean:
rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions .cache.mk $(APP_NAME)

help:
@echo "\033[1;32m================================ Help ================================\033[0m"
@echo "Ubuntu may need to add sudo:"
@echo "insmod <module_name>.ko # Load module"
@echo "rmmod <module_name> # Uninstall the module"
@echo "dmesg -C # Clear the kernel print information"
@echo "lsmod # Check the kernel modules that have been inserted"
@echo "dmesg # View information printed by the kernel"
@echo "file <module_name>.ko # View \".Ko\" file information"
@echo "make ARCH=arm # arm platform"
@echo "\033[1;32m======================================================================\033[0m"

print:
@echo "DEVICE_TREE_DIR = $(DEVICE_TREE_DIR)"

else
CONFIG_MODULE_SIG=n
obj-m += $(MODULE_DEVICE_NAME).o
obj-m += $(MODULE_DRIVER_NAME).o
endif

四、设备树匹配框架

1. hello_app.c

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : hello_app.c
* Author : qidaink
* Date : 2022-08-29
* Version :
* Description: platform总线驱动 —— 设备树匹配基础框架测试APP
* Others :
* Log :
* ======================================================
*/

#include <stdio.h> /* printf */
#include <sys/types.h> /* open */
#include <sys/stat.h> /* open */
#include <fcntl.h> /* open */
#include <unistd.h> /* close sleep */
#include <sys/ioctl.h> /* ioctl */

int main(int argc, char *argv[])
{
/* 0.相关变量定义 */
int fd = -1; /* 文件描述符 */
/* 1.判断参数个数是否合法 */
/* 1.1判断参数数量 */
if (argc < 2)
{
printf("\nusage:\n");
printf("%s /dev/dev_name\n", argv[0]);
return -1;
}
/* 2.打开字符设备 */
if ((fd = open(argv[1], O_RDWR)) < 0) /* 默认是阻塞的 */
{
printf("open %s failed!\n", argv[1]);
return -1;
}

/* 4.关闭字符设备 */
close(fd);
fd = -1;

return 0;
}

2.hello_device.c

设备数匹配的话,不需要设备程序,在设备树中添加设备信息即可,在加载驱动模块的时候,会自动与设备树中的设备进行匹配。

3. hello_driver.c

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : hello_driver.c
* Author : qidaink
* Date : 2022-08-29
* Version :
* Description: platform总线驱动驱动程序 —— 设备树匹配基础框架
* Others :
* Log :
* ======================================================
*/

/** 头文件 */
#include <linux/module.h> /* MODULE_LICENSE */
#include <linux/kernel.h>
#include <linux/init.h> /* module_init module_exit */
#include <linux/platform_device.h> /* platform_get_resource */
#include <linux/of.h>
//=======================================================//
/** 宏定义 */
#define MODULE_NAME "hello_drv" /* 模块名称 */

//=======================================================//
/** 平台驱动相关 */
/**
* @Function : hello_driver_probe
* @brief : 设备和驱动匹配成功之后的调用函数
* @param p_pltdev : struct device * 类型
* @return : int 类型
* @Description : 函数名赋值给 struct platform_driver 结构体的 .probe 成员
*/
int hello_driver_probe(struct platform_device *p_pltdev)
{
/* 0.相关变量定义 */
struct device_node *pnode = NULL; /* 存储设备树节点 */
const char *str = NULL;
/* 1.获取设备树节点 */
pnode = p_pltdev->dev.of_node;
/* 2.获取设备树reg数据 */
printk("=====================================================\n");
if(of_property_read_string(pnode, "my-name", &str) < 0)
printk("of_property_read_string failed!\n");
else
printk("name=%s\n", str);
printk("=====================================================\n");
/* 打印提示信息 */
printk("[INFO]hello_driver_probe is called!\n");
return 0;
}
/**
* @Function : hello_driver_remove
* @brief : 卸载设备
* @param p_pltdev : struct platform_device * 类型
* @return : int 类型
* @Description : 函数名赋值给 struct platform_driver 结构体的 .remove 成员
*/
int hello_driver_remove(struct platform_device *p_pltdev)
{
printk("[INFO]hello_driver_remove is called!\n");
return 0;
}

/**
* 定义匹配设备树数组
* 与数组中含有的名称相同的设备都可以匹配到该驱动
* 结构体变量名赋值给 struct platform_driver 结构体的 ..driver.of_match_table 成员
*/
struct of_device_id hello_driver_dt_ids[] = {
[0] = {.compatible = "fs4412,hello-dt"}, /* 设备树中 compatible 的名称 */
[1] = {.compatible = "origen4412,hello-dt"},
[2] = {} /* means ending */
};

/** 定义platform平台驱动 */
struct platform_driver hello_driver = {
.driver = {
.name = MODULE_NAME, /* 必须初始化 name 成员 */
.of_match_table = hello_driver_dt_ids,
},
.probe = hello_driver_probe, /* 设备和驱动匹配成功之后的调用函数 */
.remove = hello_driver_remove, /* 卸载设备 */
// .id_table = hello_driver_ids, /* 可匹配的设备ID数组 */
};

//=======================================================//
/** 整个模块相关 */
/* 驱动入口函数 */
int __init hello_driver_init(void)
{
/* 1.注册平台设备驱动 */
platform_driver_register(&hello_driver);
/* 2.打印加载模块的提示信息 */
printk("[INFO]This module [%s](driver) is loaded!\n", MODULE_NAME);
return 0;
}

/* 驱动出口函数 */
void __exit hello_driver_exit(void)
{
/* 1.注销平台设备驱动 */
platform_driver_unregister(&hello_driver);
/* 2.打印卸载模块的提示信息 */
printk("[INFO]This module [%s](driver) is exited!\n", MODULE_NAME);
}
/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(hello_driver_init);
module_exit(hello_driver_exit);
/* 模块信息(通过 modinfo module_name 查看) */
MODULE_LICENSE("GPL"); /* 源码的许可证协议 */
MODULE_AUTHOR("qidaink <2038035593@qq.com>"); /* 字符串常量内容为模块作者说明 */
MODULE_DESCRIPTION("Description"); /* 字符串常量内容为模块功能说明 */
MODULE_ALIAS("module's other name"); /* 字符串常量内容为模块别名 */

4. 设备树文件

在设备树文件根节点中添加以下内容:

1
2
3
4
fs4412-hello-dt{
compatible = "fs4412,hello-dt";
my-name = "my-hello-dt";
};

我使用的是fs4412开发板,所以直接修改了下边这文件:

1
linux3.14/arch/arm/boot/dts/exynos4412-fs4412.dts

5. Makefile

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
##============================================================================#
# Copyright © hk. 2022-2025. All rights reserved.
# File name : Makefile
# Author : qidaink
# Date : 2022-08-28
# Version :
# Description: 驱动模块编译Makefile
# Others :
# Log :
##============================================================================#
##

# 模块名和模块测试APP名称
MODULE_NAME := hello
MODULE_DRIVER_NAME := $(MODULE_NAME)_driver
APP_NAME := $(MODULE_NAME)_app

DEVICE_TREE_NAME := exynos4412-fs4412.dts
# NFS 共享目录
ROOTFS ?= /home/hk/4nfs/rootfs
MODULE_DIR ?= $(ROOTFS)/01myDrivers

ifeq ($(KERNELRELEASE),)

# 选择可执行文件运行的平台
ifeq ($(ARCH), arm)
KERNELDIR ?= /home/hk/5linux/linux-3.14
GCC_TOOL ?= arm-none-linux-gnueabi-gcc
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
GCC_TOOL ?= gcc
endif


PWD := $(shell pwd)
DEVICE_TREE_DIR ?= $(KERNELDIR)/arch/arm/boot/dts/


# 编译模块和测试程序
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
$(GCC_TOOL) $(APP_NAME).c -Wall -o $(APP_NAME)

modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

# 拷贝相关文件到nfs共享目录
copy:
sudo cp $(MODULE_DRIVER_NAME).ko $(MODULE_DIR)
sudo cp $(APP_NAME) $(MODULE_DIR)

.PHONY: clean
clean:
rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions .cache.mk $(APP_NAME)

help:
@echo "\033[1;32m================================ Help ================================\033[0m"
@echo "Ubuntu may need to add sudo:"
@echo "insmod <module_name>.ko # Load module"
@echo "rmmod <module_name> # Uninstall the module"
@echo "dmesg -C # Clear the kernel print information"
@echo "lsmod # Check the kernel modules that have been inserted"
@echo "dmesg # View information printed by the kernel"
@echo "file <module_name>.ko # View \".Ko\" file information"
@echo "make ARCH=arm # arm platform"
@echo "\033[1;32m======================================================================\033[0m"

print:
@echo "DEVICE_TREE_DIR = $(DEVICE_TREE_DIR)"

else
CONFIG_MODULE_SIG=n
obj-m += $(MODULE_DRIVER_NAME).o
endif

五、platform开发总结

点击查看思维导图
01

六、一个编写驱动的宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 定义platform驱动 */
struct platform_driver xxx = {
...
};

/* 模块入口函数与出口函数的宏定义 */
module_platform_driver(xxx);
/* module_platform_driver宏(定义在 linux/platform_device.h 文件中)最终展开后就是如下形式 */
static int __init xxx_init(void)
{
return platform_driver_register(&xxx);
}

static void __exit xxx_init(void)
{
return platform_driver_unregister(&xxx);
}

module_exit(xxx_exit);
module_init(xxx_init);