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

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

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

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

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

源碼編寫(xiě)

(一)首先包含頭文件

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

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

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

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

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

module_exit(helloworld_exit); ?// 指定驅(qū)動(dòng)程序的清理函數(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()宏接受一個(gè)初始化函數(shù)作為參數(shù),并將其定義為內(nèi)聯(lián)函數(shù)__initcall_##init_fn##_wrapper()。然后,它使用__attribute__((alias(#init_fn)))將init_module()函數(shù)與初始化函數(shù)進(jìn)行關(guān)聯(lián)。

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

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

當(dāng)使用insmod將模塊加載進(jìn)內(nèi)核的時(shí)候,初始化函數(shù)的代碼將會(huì)被執(zhí)行。模塊初始化代碼只與內(nèi)核模塊管理子系統(tǒng)打交道,并不與應(yī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()宏接受一個(gè)退出函數(shù)作為參數(shù),并將其定義為內(nèi)聯(lián)函數(shù)__exitcall_##exit_fn##_wrapper()。然后,它使用__attribute__((alias(#exit_fn)))將cleanup_module()函數(shù)與退出函數(shù)進(jìn)行關(guān)聯(lián)。

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

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

幾點(diǎn)說(shuō)明:

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

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

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

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

(三)入口函數(shù)和出口函數(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)核加載時(shí)輸出Hello, World!,內(nèi)核卸載時(shí)輸出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ū)動(dòng)程序的初始化函數(shù)

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

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

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

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

編譯

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

在hello.c同級(jí)目錄下編寫(xiě)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的路徑要以實(shí)際源碼存放的路徑為準(zhǔn)。

注意:復(fù)制粘貼過(guò)程中可能會(huì)出現(xiàn)格式問(wè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文件拷貝到開(kāi)發(fā)板中測(cè)試。在開(kāi)發(fā)板中執(zhí)行insmod加載驅(qū)動(dòng),rmmod卸載驅(qū)動(dòng):

root@ELF1:~#?insmod hello.ko

Hello, World!

root@ELF1:~#?rmmod hello.ko

Goodbye, World!

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

相關(guān)推薦