Linux 驱动的基本框架主要由模块加载函数,模块卸载函数,模块许可证声明,模块参数,模块导出符号,模块作者信息等几部分组成,其中模块参数,模块导出符号,模块作者信息是可选的部分,也就是可要可不要。剩余部分是必须有的。
模块加载函数
当加载驱动模块时,内核会通过module_init(xxx_init);
执行模块加载函数,完成模块加载函数中的初始化工作。一般模块加载函数形式如下:
static int __init xxx_init(void)
{
//驱动加载需要完成的任务
return 0;
}
其中xxx
处可添加独有的函数名,而后面的_init
是不能够更改的,函数参数也不用更改。
模块卸载函数
卸载某模块时,内核会通过module_exit(xxx_exit);
执行模块卸载函数,完成模块卸载函数中的退出工作。卸载驱动模块的函数原型如下:
//驱动的出口函数
static void __exit xxx_exit(void)
{
// 做卸载需要完成的任务
}
与驱动加载一样,其中xxx
处可添加独有的函数名,而后面的_exit
是不能够更改的,函数参数也不用更改。
模块许可证声明
许可证声明描述了内核模块的许可权限,如果不声明模块许可,模块在加载的时候,会收到“内核被污染(kernel tainted)”的警告。可接受的内核模块声明许可包括GPL
、GPL v2
。具体设置驱动的许可声明可使用函数MODULE_LICENSE(“GPL v2”);
,函数参数为许可类型。
模块参数
模块参数是指模块被加载的时候传递给模块的值。通常使用函数:
module_param_array(name, type, nump, perm)
:传递数组给内核module_param_string(name, string, len, perm)
:传递字符串给内核 module_param(name, type, perm)
:传递单个参数给内核完成参数传递。详细说明可见此文
模块导出符号
内核模块可以导出的符号,如果导出,其他模块可以使用本模块中的变量或函数。通常使用EXPORT_SYMBOL()
函数导出变量或者函数。
模块作者信息
作者信息主要说明该模块是谁创作的,可使用函数 MODULE_AUTHOR(“zxj”);
来指明,其中参数就是作者。
除此之外,还可以使用MODULE_DESCRIPTION()
函数通过传入参数描述驱动。
下面的驱动是一个最佳单的驱动源码,能够完成在加载驱动时打印“hello word”,在卸载驱动时打印“baibai”。
#include <linux/module.h>
#include <linux/kernel.h>
// 驱动的入口函数
static int __init hello_init(void)
{
printk(KERN_EMERG"hello word!\r\n");
return 0;
}
//驱动的出口函数
static void __exit hello_exit(void)
{
printk(KERN_EMERG"baibai\r\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("zxj");
MODULE_DESCRIPTION("output hello when insmod ;output baibai when rmmod");
其中,printk()
函数用于内核打印数据,而函数参数中的KERN_EMERG
用来描述打印信息的优先级。
在将内核编译完成生成的.ko文件上传到开发板后:
通过insmod + 驱动名.ko
可加载驱动到内核。
通过lsmod
可查看当前已经加载成功的驱动。
通过ls /dev/设备名
可查看驱动加载成功后创建的设备名。
通过rmmod + 驱动名
可卸载驱动。
驱动加载卸载后的执行结果如下:
此处需要注意,加载驱动时驱动名后面需要加上.ko
,而卸载驱动时直接是驱动名即可,不需要加后缀。