toradex的个人空间 https://blog.eetop.cn/1539194 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

定制 Linux Kernel Driver 编译示例

已有 73 次阅读| 2025-7-8 14:05 |系统分类:嵌入式| ARM, Linux, Kernel, NXP, iMX8MP

By Toradex秦海

1). 简介

Linux Kernel 通常包含了常见的硬件驱动,只需要通过 Kernel Configuration 来使能即可。不过有的时候还是会遇到需要针对特定硬件或者功能来开发定制化驱动的场景,本文就用一个简单的 Hello World 设备驱动示例相关驱动的开发编译调试流程。

 

本文所演示的平台来自于 Toradex Verdin i.MX8MP 嵌入式平台

 

 

2. 准备

a). Verdin i.MX8MP ARM核心版配合Dahlia 载板,并连接调试串口用于测试

 

 

3). Linux Kernel 源码下载和编译

a). 参考这里文章说明,下载适用于 Verdin iMX8MP Linux Kernel 源码,以及配置交叉编译 SDK。本文演示使用基于 NXP Downstream Kernel 版本进行操作,如果需要使用 Upstream Kernel 版本,除了上述文章说明也可以参考这里

./ 首先下载对应版本的 Linux Kernel 源代码。如果是 Upstream Kernel 版本,下载源码后还需要应用一系列补丁文件:

---------------------------------------

$ cd <work_dir>

$ git clone -b toradex_6.6-2.2.x-imx git://git.toradex.com/linux-toradex.git

---------------------------------------

 

./ 参考这里说明下载交叉编译 tool chain,根据具体使用应用平台选择 32-bit 还是 64-bit 版本,本文这里针对 i.MX8MP 平台使用 64-bit 版本:

---------------------------------------

$ cd ~

$ wget -O arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz "https://developer.arm.com/-/media/Files/downloads/gnu/12.3.rel1/binrel/arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz?rev=cf8baa0ef2e54e9286f0409cdda4f66c&hash=4E1BA6BFC2C09EA04DBD36C393C9DD3A"

$ tar xvf arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz

$ ln -s arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu gcc-linaro

---------------------------------------

 

./ 在准备进行后续编译的 Terminal 终端窗口下 export 相关 tool chain 环境变量:

---------------------------------------

$ export ARCH=arm64

$ export DTC_FLAGS="-@"

$ export PATH=~/gcc-linaro/bin/:$PATH

$ export CROSS_COMPILE=aarch64-none-linux-gnu-

---------------------------------------

 

b). 参考这里说明下载针对 Verdin i.MX8MP 硬件的初始化 Kernel Configuration 文件,然后配置

---------------------------------------

$ cd <work_dir>

$ wget https://artifacts.toradex.com:443/artifactory/tdxref-oe-prod-frankfurt/scarthgap-7.x.y/release/5/verdin-imx8mp/tdx-xwayland/tdx-reference-multimedia-image/oedeploy/kernel-config

$ cp kernel-config linux-toradex/.config

$ cd linux-toradex/

$ make olddefconfig

---------------------------------------

 

c). 由于 NXP i.MX8 系列 SoC 默认 Yocto Linux GPU 驱动使用 out of tree 驱动,而单独编译 Kernel 源码时候则需要将 built-in 版本 GPU 驱动使能,否则显示相关就会异常。当然后续如果采用 built-in 方式编译定制化驱动也需要在这里使能

---------------------------------------

$ make menuconfig

Device Drivers > MXC support drivers > MXC Vivante GPU support

<*> MXC Vivante GPU support

---------------------------------------

 

./ 当然也可以将如下配置直接添加到上述步骤 b 下载的 kernel-config 文件中去使能

---------------------------------------

CONFIG_MXC_GPU_VIV=y

---------------------------------------

 

d). 编译 Kernel Binary Image

---------------------------------------

$ make -j$(nproc) Image.gz 2>&1 | tee build.log

---------------------------------------

 

e). 编译和打包 Kernel Modules

---------------------------------------

$ make -j$(nproc) modules

//Kernel 源码目录 linux-toradex 外创建 kernel-modules 目录

$ mkdir ../kernel-modules

// 通过pwd 命令获取 kernel-modules 目录路径 <path-to-kernel-modules>

$ cd ../kernel-modules

$ pwd

// 部署 kernel modules

$ cd ../linux-toradex

$ sudo -E env "PATH=$PATH" make INSTALL_MOD_PATH=<path-to-kernel-modules>/ modules_install

// 打包 kernel modules

$ cd ../kernel-modules

$ tar cjvf kernel-modules.tar.bz2 lib/modules/*

---------------------------------------

 

f). 上传生成的 Image.gz  kenrel-modules.tar.bz2 文件到 Verdin i.MX8MP 模块这里使用 ssh 网络上传,也可以使用 SD/U盘复制

---------------------------------------

$ cd ../linux-toradex/

$ scp arch/arm64/boot/Image.gz ../kernel-modules/kernel-modules.tar.bz2 root@<ip_address_verdin_imx8mp>:/home/root/

---------------------------------------

 

g). Verdin i.MX8MP 上面部署新的 Linux Kernel Kernel Modules,重启后新的部署生效

---------------------------------------

root@verdin-imx8mp-06849028:~# cd /home/root                         

root@verdin-imx8mp-06849028:/home/root# cp Image.gz /boot/

root@verdin-imx8mp-06849028:/home/root# cd /

root@verdin-imx8mp-06849028:/# tar xvf ~/kernel-modules.tar.bz2

root@verdin-imx8mp-06849028:/# reboot

---------------------------------------

 

 

4). 定制 Linux Driver 采用 Out-of-Tree 方式单独编译部署

a). Kernel 源码目录 linux-toradex 外创建定制驱动工作目录,并创建驱动源码 c 代码以及 Makefile 文件

-------------------------------

$ mkdir ../hello-world-driver

$ cd ../hello-world-driver/

$ tree -L 1

.

├── hello_world.c

└── Makefile

-------------------------------

 

./ 驱动源代码文件为 hello_world.c,完整代码如下:

-------------------------------

#include <linux/init.h>

#include <linux/module.h>

#include <linux/device.h>

#include <linux/kernel.h>

 

static struct class *hello_class;

static struct device *hello_device;

 

static int __init hello_init(void)

{

    int ret;

 

    // 创建一个设备类

    hello_class = class_create("hello_debug");

    if (IS_ERR(hello_class)) {

        ret = PTR_ERR(hello_class);

        pr_err("Failed to create class: %d\n", ret);

        return ret;

    }

 

    // 创建一个设备实例

    hello_device = device_create(hello_class, NULL, 0, NULL, "hello%d", 0);

    if (IS_ERR(hello_device)) {

        ret = PTR_ERR(hello_device);

        pr_err("Failed to create device: %d\n", ret);

        class_destroy(hello_class);

        return ret;

    }

 

    // 使用 dev_dbg 输出调试信息

    dev_dbg(hello_device, "Hello, world! Debug message enabled.\n");

    pr_info("Hello module loaded successfully.\n");

    

    return 0;

}

 

static void __exit hello_exit(void)

{

    dev_dbg(hello_device, "Goodbye, world! Debug message enabled.\n");    

device_destroy(hello_class, 0);

class_destroy(hello_class);

    pr_info("Hello module unloaded.\n");

}

 

module_init(hello_init);

module_exit(hello_exit);

 

MODULE_LICENSE("GPL");

MODULE_DESCRIPTION("A simple hello-world kernel module with dev_dbg");

MODULE_AUTHOR("Toradex");

-------------------------------

 

### 代码创建一个 hello 设备类和实例,在正常挂载和移除时候通过 pr_info 命令来打印正常系统信息;同时通过 dev_dbg 命令来打印调试信息 ###

-------------------------------

// module loaded

dev_dbg(hello_device, "Hello, world! Debug message enabled.\n");

pr_info("Hello module loaded successfully.\n");

// module unloaded

dev_dbg(hello_device, "Goodbye, world! Debug message enabled.\n");

pr_info("Hello module unloaded.\n");

-------------------------------

 

./ Makefile 文件完整代码:

-------------------------------

obj-m += hello_world.o

 

# Linux Kernel PathModify accordingly

KDIR ?= /<work_dir>/linux-toradex

 

all:

        make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules

 

clean:

        make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) clean

-------------------------------

 

### KDIR 指定 linux-toradex Kernel 源代码路径,根据实际情况修改 ###

 

b). 编译并将生成的 hello_world.ko 驱动文件上传到 Verdin i.MX8MP $HOME 目录

-------------------------------

$ make

$ scp hello_world.ko root@<ip_address_verdin_imx8mp>:/home/root/

-------------------------------

 

c). 加载测试,由于未使能 DEBUG,因此只有 pr_info 信息被打印输出(确保Linux log level 设置要支持,详细说明可以查看这里

-------------------------------

root@verdin-imx8mp-06849028:~# insmod hello_world.ko

[ 3746.252795] hello_world: loading out-of-tree module taints kernel.

[ 3746.261753] Hello module loaded successfully.

root@verdin-imx8mp-06849028:~# dmesg |grep hello

[ 3746.252795] hello_world: loading out-of-tree module taints kernel.

root@verdin-imx8mp-06849028:~# rmmod hello_world.ko

[ 3808.225680] Hello module unloaded.

root@verdin-imx8mp-06849028:~# dmesg |grep hello

[ 3746.252795] hello_world: loading out-of-tree module taints kernel.

-------------------------------

 

d). 使能静态 DEBUG 测试 (需要重新编译驱动源代码)

./ Linux Kernel 支持 debug 信息显示,需要如下配置:

-------------------------------

Kernel hacking → Kernel debugging (CONFIG_DEBUG_KERNEL=y)

Kernel hacking → Compile-time checks and compiler options Debug information → Disable debug information (CONFIG_DEBUG_INFO_NONE=y)

Kernel hacking → Debug Filesystem (CONFIG_DEBUG_FS=y)

-------------------------------

 

./ 编译定制驱动源码时候使能 DEBUG

i). 在驱动源码 C 文件所有头文件声明之前增加如下声明:

-------------------------------

#define DEBUG

-------------------------------

 

Ii). MakeFile 文件增加如下任一 CFLAGS 声明:

-------------------------------

// 针对某一个驱动文件生效

CFLAGS_hello_world.o := -DDEBUG

// 对于 Makefile 编译的所有驱动文件生效

EXTRA_CFLAGS += -DDEBUG

-------------------------------

 

./ 通过上述任一种方式使能 DEBUG 后加载测试,debug 信息被打印到 Linux log buffer,可以通过 dmesg 查看到:

-------------------------------

root@verdin-imx8mp-06849028:~# insmod hello_world.ko

[  635.927571] hello_world: loading out-of-tree module taints kernel.

[  635.934286] Hello module loaded successfully.

root@verdin-imx8mp-06849028:~# dmesg |grep hello

[  635.927571] hello_world: loading out-of-tree module taints kernel.

[  635.934274] hello_debug hello0: Hello, world! Debug message enabled.

root@verdin-imx8mp-06849028:~# rmmod hello_world.ko

[  679.999629] Hello module unloaded.

root@verdin-imx8mp-06849028:~# dmesg |grep hello

[  635.927571] hello_world: loading out-of-tree module taints kernel.

[  635.934274] hello_debug hello0: Hello, world! Debug message enabled.

[  679.999479] hello_debug hello0: Goodbye, world! Debug message enabled.

-------------------------------

 

e). 使能动态 DEBUG 测试 (无需重新编译源代码)

./ Linux Kernel 支持动态 debug 显示,和静态 debug 信息相比优势是无需重新编译驱动或者 Kernel 源代码,而且没有静态编译 debug 代码负担,只在需要显示的时候动态显示。

 

./ 支持 Dynamic Debug 需要使能如下 Kernel 配置,更多详细说明请见这里

-------------------------------

Kernel hacking → Debug Filesystem (CONFIG_DEBUG_FS=y)

Kernel hacking printk and dmesg options Enable dynamic printk() support (CONFIG_DYNAMIC_DEBUG=y)

-------------------------------

 

./ 使能 Hello_world 驱动 Dynamic Debug 功能:

-------------------------------

// 如果已经默认挂载则不需要重复操作

$ mount -t debugfs none /sys/kernel/debug/

// 加载驱动,此时 DEBUG 未使能

root@verdin-imx8mp-06849028:~# insmod hello_world.ko

[  359.310527] hello_world: loading out-of-tree module taints kernel.

[  359.317172] Hello module loaded successfully.

root@verdin-imx8mp-06849028:~# dmesg |grep hello

[  359.310527] hello_world: loading out-of-tree module taints kernel.

// Dynamic Debug 也未使能 hello_world 驱动 Debug 信息打印

root@verdin-imx8mp-06849028:~# cat /sys/kernel/debug/dynamic_debug/control |grep hello

/home/simon/local/tdx_source_local/imx8/v7.2-20250619/hello-world-driver/hello_world.c:40 [hello

_world]hello_exit =_ "Goodbye, world! Debug message enabled.\n"

/home/simon/local/tdx_source_local/imx8/v7.2-20250619/hello-world-driver/hello_world.c:32 [hello

_world]hello_init =_ "Hello, world! Debug message enabled.\n"

// 使能 Dynamic Debug

root@verdin-imx8mp-06849028:~# echo "file hello_world.c +p" > /sys/kernel/debug/dynamic_debug/co

ntrol

root@verdin-imx8mp-06849028:~# cat /sys/kernel/debug/dynamic_debug/control |grep hello

/home/simon/local/tdx_source_local/imx8/v7.2-20250619/hello-world-driver/hello_world.c:40 [hello

_world]hello_exit =p "Goodbye, world! Debug message enabled.\n"

/home/simon/local/tdx_source_local/imx8/v7.2-20250619/hello-world-driver/hello_world.c:32 [hello

_world]hello_init =p "Hello, world! Debug message enabled.\n"

// 卸载驱动,此时 Dynamic Debug 已经生效,Debug 信息已经可以打印

root@verdin-imx8mp-06849028:~# rmmod hello_world.ko                                             

[  400.480206] Hello module unloaded.

root@verdin-imx8mp-06849028:~# dmesg |grep hello

[  359.310527] hello_world: loading out-of-tree module taints kernel.

[  400.480054] hello_debug hello0: Goodbye, world! Debug message enabled.

-------------------------------

 

 

5). 定制 Linux Driver 采用集成到 Linux Kernel 源代码中一起编译部署

a). Kernel 源码目录 linux-toradex 外创建定制驱动工作目录,并创建驱动源码 c 代码以及 Kconfig Makefile 文件

-------------------------------

// 进入 Linux Kernel 源码

$ cd <work_dir>/linux-toradex

// Character 驱动下创建 Hello_world 驱动

$ mkdr drivers/char/hello_world_driver

$ cd drivers/char/hello_world_driver/

$ tree -L 1

.

├── hello_world.c

├── Kconfig

└── Makefile

-------------------------------

 

./ 驱动源代码文件 hello_world.c 和章节3 中一致无需任何修改。

 

./ Kconfig 文件完整代码,用于 Kernel Configuration 的相关驱动配置说明:

------------------------------

#

# Hello World Driver

#

 

config HELLO_WORLD

    tristate "Hello World Driver"

    depends on ARM || ARM64

    help

      This is a simple hello world driver for testing.

------------------------------

 

./ Makefile 文件完整代码:

------------------------------

#

# Makefile for hello world driver

#

#

obj-$(CONFIG_HELLO_WORLD)         += hello_world.o

 

# Enable Debug Info

#CFLAGS_hello_world.o := -DDEBUG

#EXTRA_CFLAGS += -DDEBUG

------------------------------

 

b). 修改高一级 Kconfig Makefile 文件增加 Hello_world 驱动相关配置

------------------------------

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig

index 86cb16e65d88..d9c09281c841 100644

--- a/drivers/char/Kconfig

+++ b/drivers/char/Kconfig

@@ -423,5 +423,6 @@ config ADI

          driver include crash and makedumpfile.

 

 source "drivers/char/imx_amp/Kconfig"

+source "drivers/char/hello_world_driver/Kconfig"

 

 endmenu

diff --git a/drivers/char/Makefile b/drivers/char/Makefile

index b84b1a6db304..142d00e1d883 100644

--- a/drivers/char/Makefile

+++ b/drivers/char/Makefile

@@ -45,3 +45,4 @@ obj-$(CONFIG_XILLYBUS_CLASS)  += xillybus/

 obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o

 obj-$(CONFIG_ADI)              += adi.o

 obj-$(CONFIG_HAVE_IMX_AMP)      += imx_amp/

+obj-$(CONFIG_HELLO_WORLD)      += hello_world_driver/

------------------------------

 

c). Built-in 方式编译,将 Hello_world 驱动编译进 Kernel Binary Image

./ 配置内核并重新编译 Kernel Image

------------------------------

$ cd <work_dir>/linux-toradex

$ make menuconfig

Device Drivers > Character devices

<*> Hello World Driver

$ make -j$(nproc) Image.gz 2>&1 | tee build.log

------------------------------

 

./ 和章节 3 同样方法将 Kernel Image 上传到 Verdin i.MX8MP 并部署后重启测试,驱动加载成功:

------------------------------

root@verdin-imx8mp-06849028:~# dmesg |grep Hello

[    1.577391] Hello module loaded successfully.

------------------------------

 

./ 使能静态 DEBUG 功能:

### 两种使能代码方式 ###

i). 在驱动源码 C 文件所有头文件声明之前增加如下声明:

-------------------------------

#define DEBUG

-------------------------------

 

Ii). MakeFile 文件增加如下任一 CFLAGS 声明:

-------------------------------

// 针对某一个驱动文件生效

CFLAGS_hello_world.o := -DDEBUG

// 针对一个 Kernel 配置驱动生效

ccflags-$(CONFIG_HELLO_WORLD) += -DDEBUG

// 对于 Makefile 编译的所有配置驱动生效

EXTRA_CFLAGS += -DDEBUG

-------------------------------

 

### 通过上述任一种方式使能 DEBUG 后加载测试,debug 信息被打印到 Linux log buffer,可以通过 dmesg 查看到 ###

-------------------------------

root@verdin-imx8mp-06849028:~# dmesg |grep Hello

[    1.575157] hello_debug hello0: Hello, world! Debug message enabled.

[    1.575165] Hello module loaded successfully.

------------------------------

 

./ 使能动态 DEBUG 功能:

------------------------------

// 默认未使能

root@verdin-imx8mp-06849028:~# cat /sys/kernel/debug/dynamic_debug/control |grep hello

drivers/char/hello_world_driver/hello_world.c:32 [hello_world]hello_init =_ "Hello, world! Debug

 message enabled.\n"

drivers/char/hello_world_driver/hello_world.c:40 [hello_world]hello_exit =_ "Goodbye, world! Deb

ug message enabled.\n"

// 通过设置 U-Boot 环境变量在启动过程使能

root@verdin-imx8mp-06849028:~# fw_setenv tdxargs 'dyndbg=\\"file hello_world.c +p;\\"'          

root@verdin-imx8mp-06849028:~# fw_printenv tdxargs

tdxargs=dyndbg=\\"file hello_world.c +p;\\"

root@verdin-imx8mp-06849028:~# reboot

// 重启后 Dynamic Debug 使能

root@verdin-imx8mp-06849028:~# cat /sys/kernel/debug/dynamic_debug/control |grep hello

drivers/char/hello_world_driver/hello_world.c:32 [hello_world]hello_init =p "Hello, world! Debug

 message enabled.\n"

drivers/char/hello_world_driver/hello_world.c:40 [hello_world]hello_exit =p "Goodbye, world! Deb

ug message enabled.\n"

// Debug log 打印

root@verdin-imx8mp-06849028:~# dmesg |grep Hello

[    1.579354] hello_debug hello0: Hello, world! Debug message enabled.

[    1.579363] Hello module loaded successfully.

------------------------------

 

d). Kernel Module 方式编译,将 Hello_world 驱动编译为 Kernel Module

./ 配置内核并重新编译 Kernel Image Kernel Modules

------------------------------

$ cd <work_dir>/linux-toradex

$ make menuconfig

Device Drivers > Character devices

<M> Hello World Driver

$ make -j$(nproc) Image.gz 2>&1 | tee build.log

$ make -j$(nproc) modules

------------------------------

 

./ 和章节 3 同样方法将 Kernel Image Kernel Modules 压缩包上传到 Verdin i.MX8MP 并部署后重启测试,驱动加载成功:

------------------------------

root@verdin-imx8mp-06849028:~# modprobe hello_world    

[   69.862475] Hello module loaded successfully.

root@verdin-imx8mp-06849028:~# dmesg |grep Hello

[   69.862475] Hello module loaded successfully.

------------------------------

 

./ 使能静态 DEBUG 功能,使能方式和上述 Built-in 编译方式一致,这里不再赘述。

 

./ 使能动态 DEBUG 功能:

------------------------------

// 默认未使能

root@verdin-imx8mp-06849028:~# cat /sys/kernel/debug/dynamic_debug/control |grep hello

drivers/char/hello_world_driver/hello_world.c:32 [hello_world]hello_init =_ "Hello, world! Debug

 message enabled.\n"

drivers/char/hello_world_driver/hello_world.c:40 [hello_world]hello_exit =_ "Goodbye, world! Deb

ug message enabled.\n"

// 通过设置 U-Boot 环境变量或者 Modprobe 配置文件在加载 Hello_world 驱动过程使能

i). U-Boot 环境变量

root@verdin-imx8mp-06849028:~# fw_setenv tdxargs 'hello_world.dyndbg=\\"+p;\\"'

root@verdin-imx8mp-06849028:~# fw_printenv tdxargs

tdxargs=hello_world.dyndbg=\\"+p;\\"

ii). Modprobe 配置文件

root@verdin-imx8mp-06849028:~# vi /etc/modprobe.d/test_debug.conf

options hello_world dyndbg=+p

root@verdin-imx8mp-06849028:~# reboot

// 重启后 Dynamic Debug 使能

root@verdin-imx8mp-06849028:~# modprobe hello_world    

[   43.410229] Hello module loaded successfully.

root@verdin-imx8mp-06849028:~# cat /sys/kernel/debug/dynamic_debug/control |grep hello

drivers/char/hello_world_driver/hello_world.c:40 [hello_world]hello_exit =p "Goodbye, world! Deb

ug message enabled.\n"

drivers/char/hello_world_driver/hello_world.c:32 [hello_world]hello_init =p "Hello, world! Debug

 message enabled.\n"

// Debug log 打印

root@verdin-imx8mp-06849028:~# dmesg |grep Hello

[   43.410211] hello_debug hello0: Hello, world! Debug message enabled.

[   43.410229] Hello module loaded successfully.

------------------------------

 

 

6). 总结

本文基于 NXP i.MX8MP 处理器平台演示了定制化 Linux Hello World 设备驱动开发编译和调试流程。



点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 3

    周排名
  • 10

    月排名
  • 0

    总排名
  • 0

    关注
  • 3

    粉丝
  • 0

    好友
  • 1

    获赞
  • 2

    评论
  • 13925

    访问数
关闭

站长推荐 上一条 /1 下一条


小黑屋| 手机版| 关于我们| 联系我们| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2025-7-8 23:01 , Processed in 0.016355 second(s), 7 queries , Gzip On, MemCached On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部