“ 本文将简单介绍如何编写一个简单的驱动模块,并介绍如何向驱动模块传递参数的方法。”
模块参数
在进行驱动模块设计时,可通过参数传递动态配置驱动模块,以达到灵活配置的目的。
如需要设计一个复杂的驱动模块,有些参数需要软件动态配置。
那么就可以定义一个配置文件,把这个配置文件的路径传递给模块。在驱动模块加载时,根据配置文件定义的参数,配置当前的驱动模块,以达到灵活配置的目的。
如何向模块传递参数?
Linux kernel 提供了一个简单的框架。
//include/linux/moduleparam.h
/**
* module_param - typesafe helper for a module/cmdline parameter
* @value: the variable to alter, and exposed parameter name.
* @type: the type of the parameter
* @perm: visibility in sysfs.
*
* @value becomes the module parameter, or (prefixed by KBUILD_MODNAME and a
* ".") the kernel commandline parameter. Note that - is changed to _, so
* the user can use "foo-bar=1" even for variable "foo_bar".
*
* @perm is 0 if the the variable is not to appear in sysfs, or 0444
* for world-readable, 0644 for root-writable, etc. Note that if it
* is writable, you may need to use kernel_param_lock() around
* accesses (esp. charp, which can be kfreed when it changes).
*
* The @type is simply pasted to refer to a param_ops_##type and a
* param_check_##type: for convenience many standard types are provided but
* you can create your own by defining those variables.
*
* Standard types are:
* byte, short, ushort, int, uint, long, ulong
* charp: a character pointer
* bool: a bool, values 0/1, y/n, Y/N.
* invbool: the above, only sense-reversed (N = true).
*/
#define module_param(name, type, perm) \
module_param_named(name, name, type, perm)
/**
* module_param_named - typesafe helper for a renamed module/cmdline parameter
* @name: a valid C identifier which is the parameter name.
* @value: the actual lvalue to alter.
* @type: the type of the parameter
* @perm: visibility in sysfs.
*
* Usually it's a good idea to have variable names and user-exposed names the
* same, but that's harder if the variable must be non-static or is inside a
* structure. This allows exposure under a different name.
*/
#define module_param_named(name, value, type, perm) \
param_check_##type(name, &(value)); \
module_param_cb(name, ¶m_ops_##type, &value, perm); \
__MODULE_PARM_TYPE(name, #type)
//<perm>,sysfs入口项的访问许可掩码,在<linux/stat.h>中定义。
#define S_IRUSR 00400 文件所有者可读
#define S_IWUSR 00200 文件所有者可写
#define S_IXUSR 00100 文件所有者可执行
#define S_IRGRP 00040 与文件所有者同组的用户可读
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IROTH 00004 与文件所有者不同组的用户可读
#define S_IWOTH 00002
#define S_IXOTH 00001
传递单个参数
static unsigned int val = 0; //声明变量
module_param(val, uint, S_IRUGO); //定义参数
-
val,参数名
-
uint,表示参数的数据类型,是下列之一:byte, short, ushort, int, uint, long, ulong, charp, bool, invbool
-
S_IRUGO,指定了在sysfs中相应文件的访问权限。访问权限与linux文件访问权限相同的方式管理,如0644,或使用stat.h中的宏如S_IRUGO表示。0 表示完全关闭在sysfs中相对应的项。
传递多个参数
static int val[NUM];
static int n_val;
module_param_array(val, int, &n_val, S_IRUGO);
-
val,参数数组,数组的大小决定能输入多少个参数
-
n_val,参数个数,最终传递数组元素个数存在n_val中
-
int,数据类型
-
S_IRUGO,sysfs的访问权限
模块的装载/卸载
insmod <module> <opt: parm1> <opt: parm2>
modprobe <module> <opt: parm1> <opt: parm2>
rmmod <module>
modprobe
modprobe和insmod功能相似。
modprobe除了会装载指定模块之外还会装载指定模块所依赖的其他模块。modprobe只能从标准已安装模块目录中搜索需要装载的模块,因此需要将驱动拷贝到/lib/modules/$(uname -r)/目录下。
简单的示例
源码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/moduleparam.h>
/* FUNCTION CONTROL */
#define TOM_MOD_PARM
/* FUNCTION CONTROL END */
static char* name = "Tom";
#ifdef TOM_MOD_PARM
module_param(name, charp, 0644);
#endif
//使用__init修饰的函数表明该函数仅在初始化阶段有效,初始化结束后释放该函数所占用的资源。
static int __init hello_init(void)
{
printk(KERN_WARNING "Hello %s!\n", name);
printk(KERN_WARNING "Nice to meet you!\n");
//打印当前进程及进程ID
printk(KERN_INFO "This process is \"%s\" (pid %i) \n", current->comm, current->pid);
return 0;
}
//使用__exit修饰的函数会链接到指定的elf段,表明该函数仅用于模块卸载。
static void __exit hello_exit(void)
{
//打印当前进程及进程ID
printk(KERN_INFO "This process is \"%s\" (pid %i) \n", current->comm, current->pid);
printk(KERN_WARNING "Goodbye! %s!\n", name);
return;
}
module_init(hello_init); //注册入口函数
module_exit(hello_exit); //注册出口函数
MODULE_LICENSE("GPL");
编译Makefile
KERNEL_DIR=/<path>/linux-4.1.15
CROSS_COMPILE=arm-none-eabi-
ARCH=arm
obj-m := hello.o
# -C 指定内核源码路径
# -M 指定构造modules目标的源代码所在的目录
# modules 目标指向obj-m变量中设定的模块
all:
make -C $(KERNEL_DIR) M=`pwd` modules
rm *.o *.order .*.cmd *.mod.c *.mod.o .tmp_versions -rf
clean:
rm *.o *.ko *.order *.symvers .*.cmd *.mod.c *.mod.o .tmp_versions -rf
加载模块
# insmod hello.ko
[10689.134471] Hello Tom!
[10689.137296] Nice to meet you!
[10689.140282] This process is "insmod" (pid 236)
#
# rmmod hello.ko
[10694.632803] This process is "rmmod" (pid 237)
[10694.637496] Goodbye! Tom!
结束语
希望本文可以帮助到大家!
如需获取文中的测试程序源码,可通过后台回复获取!水平有限,如有错漏的地方请不吝指出,谢谢!