宿主机 : 虚拟机 Ubuntu 16.04 LTS / X64
目标板[底板]: Tiny4412SDK - 1506
目标板[核心板]: Tiny4412 - 1412
LINUX内核: 4.12.0
交叉编译器: gcc-arm-none-eabi-5_4-2016q3
日期: 2017-8-7 20:46:09
作者: SY
通过这个基于设备树的例程,熟悉Linux内核
驱动。
HelloWorld {
compatible = "tiny4412, hello_world";
status = "okay";
};
新建一个hello_world.c
源文件
root@ubuntu:/opt/linux-4.12# more drivers/tiny4412/hello_world.c
/*
* Hello World Driver for Tiny4412
*
* Copyright (c) 2017
* Author: SY <[email protected]>
*
* 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
#include
#include
#include
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 <[email protected]>");
MODULE_DESCRIPTION("TINY4412 Hello World driver");
MODULE_LICENSE("GPL v2");
写一个Makefile
文件
root@ubuntu:/opt/linux-4.12# cat drivers/tiny4412/Makefile
obj-$(CONFIG_BSP_HELLO_WORLD) += hello_world.o
在上一级目录的Makefile
末尾添加
root@ubuntu:/opt/linux-4.12# cat drivers/Makefile
# tiny4412
obj-$(CONFIG_TINY4412_BSP) += tiny4412/
写一个Kconfig
配置文件
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
中添加
root@ubuntu:/opt/linux-4.12# cat drivers/Kconfig
source "drivers/tiny4412/Kconfig"
编译hello_world.c
root@ubuntu:/opt/linux-4.12# make modules
安装hello_world.ko
root@ubuntu:/opt/linux-4.12# make modules_install INSTALL_MOD_PATH=/opt/fs/rootfs/rootfs/
使用NFS
进入LINUX内核
[root@TINY4412:~]# depmod
depmod
命令可产生模块依赖的映射文件;
[root@TINY4412:~]# modprobe hello_world
[ 1561.495147] ------------------- hello_world_probe
[root@TINY4412:~]# rmmod hello_world
[ 1596.936763] ------------------- hello_world_remove
[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
中添加
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;
}
测试
[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
中添加
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;
}
测试
[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
。修改设备树
HelloWorld {
compatible = "tiny4412, hello_world";
status = "okay";
instance {
aaa = <123>;
};
};
测试
[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]
,表示编译成独立的模块。