【TINY4412】LINUX移植筆記:(17)設備樹HELLO WORLD驅動

【TINY4412】LINUX移植筆記:(17)設備樹 HELLO WORLD驅動

宿主機 : 虛擬機 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 <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 <[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],表示編譯成獨立的模塊。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章