1.新建文本文档,重命名为 hello_dev.c (驱动的开发同应用的开发一样,也是在文本文档下开发的)。
2.包含头文件
内核模块需要包含内核相关头文件,不同模块根据功能的差异,所需要的头文件也不相同,但是和是必不可少的。
#include
#include
3.模块初始化
模块的初始化负责注册模块本身。如果一个内核模块没有被注册,则其内部的各种方法(函数)无法被应用程序使用,只有已注册模块的各种方法才能够被应用程序使用并发挥各方法的实际功能。模块并不是内核内部的代码,而是独立于内核之外,通过初始化,能够让内核之外的代码来替内核完成本应该由内核完成的功能,模块初始化的功能相当于模块与内核之间衔接的桥梁,告知内核“我进来了”。
static int __init module_init_func(void)
{
初始化代码
}
module_init(module_init_func);
说明:
(1) 模块初始化函数一般都需声明为 static,因为初始化函数对于其它文件没有任何意义;
(2) __init 表示初始化函数仅仅在初始化期间使用,一旦初始化完毕,将释放初始化函数所占用的内存,类似的还有__initdata;
(3) module_init 是必须的,没有这个定义,内核将无法执行初始化代码。module_init宏定义会在模块的目标代码中增加一个特殊的代码段,用于说明该初始化函数所在的位置。
当使用 insmod 将模块加载进内核的时候,初始化函数的代码将会被执行。模块初始化代码只与内核模块管理子系统打交道,并不与应用程序交互。
4.模块退出。
当系统不再需要某个模块,可以卸载这个模块以释放该模块所占用的资源。模块的退出相当于告知内核“我要离开了”。实现模块退出的函数常称为模块的退出函数或者清除函数
static void __exit module_exit_func(void)
{
模块退出代码
}
module_exit(module_exit_func);
说明:
(1) 模块退出函数没有返回值;
(2) __exit 标记这段代码仅用于模块卸载;
(3) module_exit 不是必须的。但是,没有 module_exit 定义的模块无法被卸载,如果需要支持模块卸载则必须有 module_exit。
当使用 rmmod 卸载模块时,退出函数的代码将被执行。模块退出代码只与内核模块管理子系统打交道,并不直接与应用程序交互。
5.许可证
Linux 内核是开源的,遵守 GPL 协议,所以要求加载进内核的模块也最好遵循相关协议。为模块指定遵守的协议用 MODULE_LINCENSE 来声明,如:
MODULE_LICENSE("GPL");
内核能够识别的协议有“GPL”、“GPL v2”、“GPL and additional rights(GPL 及附加权利)”、“Dual BSD/GPL(BSD/GPL 双重许可)”、“Dual MPL/GPL(MPL/GPL 双重许可)”以及“Proprietary(私有)”。
如果一个模块没有指定任何许可协议,则会被认为是私有协议。采用私有协议的模块,在加载过程中会出现警告,并且不能被静态编译进内核。
执行完上面步骤,一个最小的linux驱动便写好了。全部代码如下:
6.编译
内核模块的编译需要Makefile文件,在hello_dev.c所在路径建立Makefile文件:
在Makefile输入以下文本并保存。
obj-m := hello_dev.o
PWD := $(shell pwd)
KVER ?= $(shell uname -r)
KDIR :=/home/qlqcetc/nuc970bsp/02.linux内核3.10.x
all:
$(MAKE) -C $(KDIR) M=$(PWD)
clean:
rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions
其中obj-m 指定要编译的源文件是hello_dev.c,KDIR指定内核的路径,驱动模块的编译是需要内核源码的相关文件的,所以需要设置内核源码所在路径。
中断进入驱动源码所在文件夹:
#/home/qlqcetc/nuc970bsp/06.示例驱动程序/00.hello_dev
#ls
编译:
#make
#ls
生成的hello_dev.ko就是驱动模块,使用U盘将其复制到开发板的/lib/modules/路径下(modules文件夹需要创建),就可以加载驱动模块了。
开发板终端输入:
# cd /lib/modules/
# insmod hello_dev.ko
可以看到驱动加载成功,驱动打印出信息:
开发板终端输入:
# mkdir 3.10.101
# rmmod hello_dev.ko
可以看到驱动卸载成功,驱动打印出信息: