【TINY4412】LINUX移植笔记:(17)设备树HELLOWORLD驱动

环境

宿主机 : 虚拟机 Ubuntu 16.04 LTS / X64
目标板[底板]: Tiny4412SDK - 1506
目标板[核心板]: Tiny4412 - 1412
LINUX内核: 4.12.0
交叉编译器: arm-none-linux-gnueabi-gcc(gcc version 4.8.3 20140320)
日期: 2017-9-9 20:30:09
作者: SY

简介

通过这个基于设备树的例程,熟悉Linux内核驱动。

设备树

1
2
3
4
HelloWorld {
compatible = "tiny4412, hello_world";
status = "okay";
};

源文件

新建一个hello_world.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
root@ubuntu:/opt/linux-4.12# more drivers/tiny4412/hello_world.c
/*
* Hello World Driver for Tiny4412
*
* Copyright (c) 2017
* Author: SY <1530454315@qq.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
static int hello_world_probe(struct platform_device *pdev)
{
pr_notice("------------------- %s\n", __func__);
return 0;
}
static int hello_world_remove(struct platform_device *pdev)
{
pr_notice("------------------- %s\n", __func__);
return 0;
}
static void hello_world_shutdown(struct platform_device *pdev)
{
pr_notice("------------------- %s\n", __func__);
return ;
}
static int hello_world_suspend(struct platform_device *pdev, pm_message_t state)
{
pr_notice("------------------- %s\n", __func__);
return 0;
}
static int hello_world_resume(struct platform_device *pdev)
{
pr_notice("------------------- %s\n", __func__);
return 0;
}
static const struct of_device_id tiny4412_hello_world_dt_match[] = {
{ .compatible = "tiny4412, hello_world" },
{},
};
static struct platform_driver tiny4412_hello_world_driver = {
.probe = hello_world_probe,
.remove = hello_world_remove,
.shutdown = hello_world_shutdown,
.suspend = hello_world_suspend,
.resume = hello_world_resume,
.driver = {
.name = "hello world",
.of_match_table = tiny4412_hello_world_dt_match,
},
};
module_platform_driver(tiny4412_hello_world_driver);
MODULE_AUTHOR("SY <1530454315@qq.com>");
MODULE_DESCRIPTION("TINY4412 Hello World driver");
MODULE_LICENSE("GPL v2");

写一个Makefile文件

1
2
root@ubuntu:/opt/linux-4.12# cat drivers/tiny4412/Makefile
obj-$(CONFIG_BSP_HELLO_WORLD) += hello_world.o

在上一级目录的Makefile末尾添加

1
2
3
root@ubuntu:/opt/linux-4.12# cat drivers/Makefile
# tiny4412
obj-$(CONFIG_TINY4412_BSP) += tiny4412/

写一个Kconfig配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@ubuntu:/opt/linux-4.12# cat drivers/tiny4412/Kconfig
menuconfig TINY4412_BSP
bool "TINY4412 BSP Support"
help
Select [Y] to enable tiny4412 driver.
if TINY4412_BSP
config BSP_HELLO_WORLD
tristate "HELLO WORLD Support"
help
This option enables hello_world in /tiny4412/hello_world.
You'll need this to open hello_world driver.
It can be built as a module.
endif # TINY4412_BSP

在上一级的Kconfig中添加

1
2
root@ubuntu:/opt/linux-4.12# cat drivers/Kconfig
source "drivers/tiny4412/Kconfig"

编译hello_world.c

1
root@ubuntu:/opt/linux-4.12# make modules

安装hello_world.ko

1
root@ubuntu:/opt/linux-4.12# make modules_install INSTALL_MOD_PATH=/opt/fs/rootfs/rootfs/

测试

使用NFS进入LINUX内核

1
[root@TINY4412:~]# depmod

depmod命令可产生模块依赖的映射文件;

加载模块

1
2
[root@TINY4412:~]# modprobe hello_world
[ 1561.495147] ------------------- hello_world_probe

卸载模块

1
2
[root@TINY4412:~]# rmmod hello_world
[ 1596.936763] ------------------- hello_world_remove

关机

1
2
3
4
5
6
7
8
9
[root@TINY4412:~]# modprobe hello_world
[ 1561.495147] ------------------- hello_world_probe
[root@TINY4412:~]# reboot
The system is going down NOW!
Sent SIGTERM to all processes
Terminated
Sent SIGKILL to all processes
Requesting system reboot
[ 39.681554] ------------------- hello_world_shutdown

进阶探索

基于当前的hello_world.c,我们添加一些元素,了解linux内核驱动加载流程

hello_world.c中添加

1
2
3
4
5
6
7
8
9
10
11
static int hello_world_probe(struct platform_device *pdev)
{
void *platdata = NULL;
pr_notice("------------------- %s\n", __func__);
platdata = dev_get_platdata(&pdev->dev);
pr_notice("platdata = %p\n", platdata);
return 0;
}

测试

1
2
3
4
[root@TINY4412:~]# modprobe hello_world
[ 279.524148] hello_world: loading out-of-tree module taints kernel.
[ 279.526495] ------------------- hello_world_probe
[ 279.526609] platdata = (null)

可以看出来,在第一次执行probe时,还没有platdata

hello_world.c中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static int hello_world_probe(struct platform_device *pdev)
{
void *platdata = NULL;
int count = 0;
pr_notice("------------------- %s\n", __func__);
platdata = dev_get_platdata(&pdev->dev);
pr_notice("platdata = %p\n", platdata);
pr_notice("device_tree = %p\n", pdev->dev.of_node);
count = of_get_child_count(pdev->dev.of_node);
pr_notice("child_count = %d\n", count);
return 0;
}

测试

1
2
3
4
5
6
[root@TINY4412:~]# modprobe hello_world
[ 279.524148] hello_world: loading out-of-tree module taints kernel.
[ 279.526495] ------------------- hello_world_probe
[ 279.526609] platdata = (null)
[ 279.526679] device_tree = ef7f5e0c
[ 279.526753] child_count = 0

可以看出,设备树节点已经有值。(废话! compatible字段匹配了,当然有值 :-) ),打印出来的孩子个数为0。修改设备树

1
2
3
4
5
6
7
8
HelloWorld {
compatible = "tiny4412, hello_world";
status = "okay";
instance {
aaa = <123>;
};
};

测试

1
2
3
4
5
6
[root@TINY4412:~]# modprobe hello_world
[ 279.524148] hello_world: loading out-of-tree module taints kernel.
[ 279.526495] ------------------- hello_world_probe
[ 279.526609] platdata = (null)
[ 279.526679] device_tree = ef7f5e0c
[ 279.526753] child_count = 1

可以看出,子节点就是嵌套在包含compatible字段的{...}内的节点。

总结

  • 如果驱动与设备树的compatible字段不匹配,不会加载驱动。
  • 如果设备树中的status = disabled ,也不会加载驱动,如果不写或者设置为status = okay,则加载驱动的probe方法。
  • ​设备在menuconfig中如果配置为[*],表示编译进内核;如果配置为[M],表示编译成独立的模块。
SY wechat
扫一扫,用手机访问本站