• 正文
    • 源碼編寫
    • hello.c示例源碼
    • 編譯
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

飛凌嵌入式ElfBoard ELF 1板卡-Linux驅(qū)動模塊之helloworld驅(qū)動

03/12 10:00
620
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

例程代碼路徑:ELF 1開發(fā)板資料包3-例程源碼3-2 驅(qū)動例程源碼1_helloworldhello

本章節(jié)先寫一個helloworld驅(qū)動,不涉及對硬件的操作,它的目的是展示驅(qū)動程序的基本結(jié)構(gòu)和加載過程。

源碼編寫

(一)首先包含頭文件

#include <linux/module.h> ??// 包含模塊相關(guān)函數(shù)的頭文件

#include <linux/kernel.h> ??// 包含內(nèi)核相關(guān)函數(shù)的頭文件

#include <linux/init.h> ????// 包含初始化和清理函數(shù)的頭文件

(二)驅(qū)動模塊入口和出口函數(shù)

module_init(helloworld_init); ?// 指定驅(qū)動程序的初始化函數(shù)

module_exit(helloworld_exit); ?// 指定驅(qū)動程序的清理函數(shù)

module_init()的定義:

#define module_init(init_fn)

static inline initcall_t __initcall_##init_fn##_wrapper(void)

{

return init_fn;

}

int init_module(void) __attribute__((alias(#init_fn)));

module_init()宏接受一個初始化函數(shù)作為參數(shù),并將其定義為內(nèi)聯(lián)函數(shù)__initcall_##init_fn##_wrapper()。然后,它使用__attribute__((alias(#init_fn)))將init_module()函數(shù)與初始化函數(shù)進行關(guān)聯(lián)。

Linux內(nèi)核加載模塊時,會調(diào)用init_module()函數(shù),它實際上是一個入口點函數(shù)。通過將module_init()定義為init_module()的別名,使得初始化函數(shù)成為模塊加載時的入口點函數(shù)。

使用module_init()宏的目的是在驅(qū)動程序中指定初始化函數(shù),以便在模塊加載時調(diào)用該函數(shù)執(zhí)行必要的初始化操作。

當使用insmod將模塊加載進內(nèi)核的時候,初始化函數(shù)的代碼將會被執(zhí)行。模塊初始化代碼只與內(nèi)核模塊管理子系統(tǒng)打交道,并不與應用程序直接交互。

module_exit()的定義:

#define module_exit(exit_fn)

static inline exitcall_t __exitcall_##exit_fn##_wrapper(void)

{

return exit_fn;

}

void cleanup_module(void) __attribute__((alias(#exit_fn)));

module_exit()宏接受一個退出函數(shù)作為參數(shù),并將其定義為內(nèi)聯(lián)函數(shù)__exitcall_##exit_fn##_wrapper()。然后,它使用__attribute__((alias(#exit_fn)))將cleanup_module()函數(shù)與退出函數(shù)進行關(guān)聯(lián)。

在Linux內(nèi)核卸載模塊時,會調(diào)用cleanup_module()函數(shù),它實際上是一個出口點函數(shù)。通過將module_exit()定義為cleanup_module()的別名,使得退出函數(shù)成為模塊卸載時的出口點函數(shù)。

使用module_exit()宏的目的是在驅(qū)動程序中指定退出函數(shù),以便在模塊卸載時調(diào)用該函數(shù)執(zhí)行必要的清理操作。

幾點說明:

(1)模塊退出沒有返回值;

(2)__exit標記這段代碼僅用于模塊卸載;

(3)module_exit不是必須的。但是,沒有module_exit定義的模塊無法被卸載,如果需要支持模塊卸載則必須有module_exit;

當使用當使用rmmod卸載模塊時,退出函數(shù)的代碼將被執(zhí)行。模塊退出代碼只與內(nèi)核模塊管理子系統(tǒng)打交道,并不直接與應用程序交互。

(三)入口函數(shù)和出口函數(shù)實現(xiàn)

static int __init helloworld_init(void)

{

printk(KERN_INFO "Hello, World!n"); ?// 打印消息到內(nèi)核日志

return 0;

}

static void __exit helloworld_exit(void)

{

printk(KERN_INFO "Goodbye, World!n"); ?// 打印消息到內(nèi)核日志

}

內(nèi)核加載時輸出Hello, World!,內(nèi)核卸載時輸出Goodbye, World!

(四)聲明模塊信息

MODULE_LICENSE("GPL"); ?// 指定模塊的許可證信息

MODULE_AUTHOR("Your Name"); ?// 指定模塊的作者信息

MODULE_DESCRIPTION("A simple Hello World driver"); ?// 指定模塊的描述信息

hello.c示例源碼

#include <linux/module.h> ??// 包含模塊相關(guān)函數(shù)的頭文件

#include <linux/kernel.h> ??// 包含內(nèi)核相關(guān)函數(shù)的頭文件

#include <linux/init.h> ????// 包含初始化和清理函數(shù)的頭文件

static int __init helloworld_init(void)

{

printk(KERN_INFO "Hello, World!n"); // 打印消息到內(nèi)核日志

return 0;

}

static void __exit helloworld_exit(void)

{

printk(KERN_INFO "Goodbye, World!n"); // 打印消息到內(nèi)核日志

}

module_init(helloworld_init); // 指定驅(qū)動程序的初始化函數(shù)

module_exit(helloworld_exit); // 指定驅(qū)動程序的清理函數(shù)

MODULE_LICENSE("GPL"); // 指定模塊的許可證信息

MODULE_AUTHOR("Your Name"); // 指定模塊的作者信息

MODULE_DESCRIPTION("A simple Hello World driver"); // 指定模塊的描述信息

編譯

編譯驅(qū)動分為兩種形式,一種是編譯現(xiàn)成模塊,生成.ko文件然后通過手動加載;另一種是編譯到內(nèi)核,在內(nèi)核啟動時自動加載。一般在驅(qū)動調(diào)試階段會編譯成模塊,這樣便于調(diào)試,不用每次都重新編譯整個內(nèi)核,本章節(jié)以編譯成模塊進行講解。

在hello.c同級目錄下編寫Makefile:

LINUX_KERNEL_PATH = /home/elf/work/linux-imx-imx_4.1.15_2.0.0_ga

CURRENT_PATH := $(shell pwd)

CROSS_COMPILE = arm-poky-linux-gnueabi-

CC = $(CROSS_COMPILE)gcc -march=armv7ve -mfpu=neon -mcpu=cortex-a7 --sysroot=/opt/fsl-imx-x11/4.1.15-2.0.0/sysroots/cortexa7hf-neon-poky-linux-gnueabi

obj-m :=hello.o

all:

$(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules

clean:

$(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules clean

.PHONY: modules clean

-C:參數(shù)后面加.config文件所在的文件夾;

M:參數(shù)后面是要編譯的模塊;

LINUX_KERNEL_PATH:為要依賴內(nèi)核的目錄;

CURRENT_PATH:目前模塊的目錄;

CROSS_COMPILE:編譯器的前綴;

CC:編譯器以及參數(shù);

注意:LINUX_KERNEL_PATH的路徑要以實際源碼存放的路徑為準。

注意:復制粘貼過程中可能會出現(xiàn)格式問題。

添加后效果如下:

設(shè)置環(huán)境變量,編譯:

?. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/01_helloworld/hello$ make

將生成的hello.ko文件拷貝到開發(fā)板中測試。在開發(fā)板中執(zhí)行insmod加載驅(qū)動,rmmod卸載驅(qū)動:

root@ELF1:~#?insmod hello.ko

Hello, World!

root@ELF1:~#?rmmod hello.ko

Goodbye, World!

可以看出加載時進入到了helloworld_init函數(shù),并打印了Hello, World!,卸載函數(shù)時進入了helloworld_exit函數(shù)并打印了Goodbye, World!

相關(guān)推薦