一、为什么要将驱动模块化
要把编写的驱动程序包含到内核中,主要有两种方法:
1> 把所有需要的功能都编译到Linux内核
主要问题:会导致生成的内核很大,而且如果要在现有内核中新增或删除功能,要重新编译内核代码。
2> 把新的驱动内容做成一个独立的模块,在需要时加载到内核中
主要特点:模块本身不会被编译到内核映像,只需要在使用时加载到内核中。模块一旦被加载,它就和内核中的其他部分完全一样。
二、内核驱动模块的组成
一个最简单的内核驱动模块只需要包含模块加载函数、卸载函数、GPL许可权限声明以及一些描述信息即可。内核驱动模块编译后会生成 xxx.ko 文件,可以直接使用 insmod xxx.ko 即可将其加载到内核中。使用 rmmod xxx.ko 即可将其从内核中卸载。
例如:
#include <linux/init.h>
#include <linux/module.h>
static int __init xxx_init(void)
{
printk("我是模块加载函数\n");
return 0;
}
static void __exit xxx_exit(void)
{
printk("我是模块卸载函数\n");
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LINCESE("GPL");
在上述代码中,printk()函数用于输出内核中的信息,类似于用户空间中使用printf()。可以通过 lsmod命令查看系统已加载的所有模块,类似于Linux中的ls命令。
在模块加载函数中,一般要完成设备号申请、创建设备文件、实现设备操作接口函数、硬件初始化等步骤。与之对应,在卸载函数中完成加载函数的反操作,即释放设备号、销毁设备文件等。
三、内核驱动模块的编译
一般使用Makefile进行管理和编译内核驱动模块,对于这个简单内核驱动模块,编写一个Makefile进行编译,假设上述模块保存为 xxx.c
KERNEL_DIR = /home/linux/linux-4.4.249/ #Linux内核所在目录
CUR_DIR = $(shell pwd) #当前目录
obj-m := xxx.o
make = -C $(KERNEL_DIR) M=$(CUR_DIR)modules
编写好Makefile后,直接在当前目录make即可生成驱动目标文件 xxx.ko
之后,直接使用 insmod xxx.ko 即可将其加载到内核中。至此,一个最简单的内核驱动模块便已完成,后续会继续更新包括接口函数的实现、平台总线写驱动等内容。