工控網首頁
>

應用設計

>

定制 Linux Kernel Driver 編譯示例

定制 Linux Kernel Driver 編譯示例

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

$ 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

$ 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 目錄路徑

$ cd ../kernel-modules

$ pwd

// 部署 kernel modules

$ cd ../linux-toradex

$ sudo -E env "PATH=$PATH" make INSTALL_MOD_PATH=/ 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@:/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

#include

#include

#include

 

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 ?= //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@:/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/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/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/linux-toradex

$ make menuconfig

Device Drivers > Character devices

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 設備驅動開發編譯和調試流程。

審核編輯(
王靜
)
投訴建議

提交

查看更多評論
其他資訊

查看更多

在 NXP i.MX 95 上運行 Zephyr 實現非對稱多核處理

HDMI 顯示器熱插拔對應顯示應用啟停測試

Yocto meta-toradex-security layer 創建加密數據分區應用說明

NXP iMX8MP ARM 平臺多屏幕克隆顯示測試

Yocto meta-toradex-security layer 創建獨立數據分區