在开发linux相关应用程序时,为了使应用程序更加灵活地执行用户的预期功能,我们会通过main
函数参数传递一些参数到程序中,代码逻辑处理块根据不同的参数执行不同的任务。类似地,linux内核提供驱动传参机制,编写驱动程序时只要实现传参接口,用户在加载驱动时即可以传入指定参数,使得驱动模块更加灵活。
增加驱动资源占用,包括内存空间、存储空间
驱动复杂化,非单一功能,增加冗余代码
驱动传参一般用于工作状态、处理逻辑、运作模式在使用过程中可能改变的驱动模块中,一方面适配用户业务繁琐逻辑,另一方面兼容不同的硬件设备。典型的应用有如下的场景。
内核支持的驱动传递参数类型包含了C语言中常用的数据类型
实现驱动传参,只需在驱动程序中调用module_param
系列宏即可,module_param
系列宏位于“/include/linux/moduleparam.h”
中定义,包括module_param_array
、module_param_string
。
#define module_param(name, type, perm) \
module_param_named(name, name, type, perm)
#define module_param_array(name, type, nump, perm) \
module_param_array_named(name, name, type, nump, perm)
#define module_param_string(name, string, len, perm) \
static const struct kparam_string __param_string_##name \
= { len, string }; \
__module_param_call(MODULE_PARAM_PREFIX, name, \
¶m_ops_string, \
.str = &__param_string_##name, perm, -1, 0);\
__MODULE_PARM_TYPE(name, "string")
module_param
用于处理基本类型参数,module_param_array
用于处理数组类型参数,module_param_string
用于处理字符串类型参数。
module_param(name, type, perm)
name
,驱动程序中变量名称,同时又是用户传入参数时的名称type
,参数类型perm
,该参数指定sysfs访问权限,位于"/include/linux/stat.h"
定义,一般使用S_IRUGO
;也可以直接用数字表示,如0444
表示S_IRUGO
/* include/linux/stat.h */
#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO) /* 所有用户可读、写、执行 */
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)/* 所有用户可读、写、执行*/
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) /* 所有用户可读 */
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) /* 所有用户可写 */
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH) /* 所有用户可执行 */
/* include/uapi/linux/stat.h */
/* 三者分别表示用于者权限、用户组权限、其他访问者权限
* bit[0]、bit[1]、bit[2]分别表示可执行、可写、可读属性
*/
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
示例:
static int mode = 0;
static char *p = NULL;
module_param(mode, int , S_IRUGO); /* int型 */
module_param(p, charp, S_IRUGO); /* 指针 */
module_param_array(name, type, nump, perm)
name
,驱动程序中数组名称,同时又是用户传入参数时的名称type
,数组类型,int、char等nump
,数组存储大小perm
,该参数指定sysfs访问权限,位于"/include/linux/stat.h"
定义,一般使用S_IRUGO
;也可以直接用数字表示,如0444
表示S_IRUGO
示例:
static int array[3] = {0};
static int array_size = 5;
module_param_array(array, int, &array_size, S_IRUGO);
module_param_string(name, string, len, perm)
name
,用户传入参数时的名称,可以与驱动程序中变量名称string
相同string
,驱动程序中变量名称len
,缓存大小perm
,该参数指定sysfs访问权限,位于"/include/linux/stat.h"
定义,一般使用S_IRUGO
;也可以直接用数字表示,如0444
表示S_IRUGO
示例:
static char string[6] = {0};
module_param_string(usestr, string, sizeof(string), S_IRUGO);
用户向驱动模块传递参数时,参数较多的情况下,应用开发工程师不易记住;因此,一般都会增加准确、清晰的参数描述信息,描述不同参数代表的含义,用户调用时首先查询驱动模块的参数描叙信息,进而有目的地传入具体参数。参数描述信息通过MODULE_PARM_DESC
宏实现,该宏位于“/include/linux/moduleparam.h”
中定义
#define MODULE_PARM_DESC(_parm, desc) \
__MODULE_INFO(parm, _parm, #_parm ":" desc)
_parm
,参数名称desc
,描述信息,字符串类型示例:
static int mode = 0;
module_param(mode, int , S_IRUGO);
MODULE_PARM_DESC(mode, "0:mode0; 1:mode1; 2:mode2");
编写一个基本的linux驱动,实现用户往驱动传递参数的功能,加载驱动时传入指定参数。
驱动源码:
#include
#include
#include
#include
#include
static int mode = 0;
static char *p = NULL;
static int array[3] = {0};
static int array_size = 5;
static char string[6] = {0};
/* 驱动传递参数一般放置于程序开头处,但也有放置于程序结尾处 */
module_param(mode, int , S_IRUGO); /* 整型 */
MODULE_PARM_DESC(mode, "0:mode0; 1:mode1; 2:mode2");
module_param(p, charp, S_IRUGO); /* 指针 */
MODULE_PARM_DESC(p, "pointer select:i2c0; i2c1; i2c2");
module_param_array(array, int, &array_size, S_IRUGO);/* 数组 */
MODULE_PARM_DESC(array, "set array value");
module_param_string(usestr, string, sizeof(string), S_IRUGO);/* 字符串 */
MODULE_PARM_DESC(usestr, "string select:spi00; spi01; spi10");
static int __init base_init(void)
{
int i = 0;
printk("base driver init\n");
printk("mode: %d\n", mode);
printk("p: %s\n", p);
for(i = 0; i < array_size; i++)
{
printk("array[%d]:%d\n", i, array[i]);
}
printk("string: %s\n", string);
return 0;
}
static void __exit base_exit(void)
{
}
module_init(base_init);
module_exit(base_exit);
MODULE_LICENSE("GPL");
Makefile文件:
ifeq ($(KERNELRELEASE),)
KERNELDIR = /usr/src/linux-headers-4.15.0-107-generic
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *.ko .mod.o *.mod.c *.symvers
else
obj-m := base.o
endif
编译并执行测试:
执行"modinfo base.ko"
查看驱动模块信息,可看到我们在驱动程序中添加的参数,以及参数描述信息。
root@ubuntu:/mnt/hgfs/LSW/STHB/drivers64/base# modinfo base.ko
filename: /mnt/hgfs/LSW/STHB/drivers64/base/base.ko
license: GPL
srcversion: 4219F31AD24A00B43DC97BC
depends:
retpoline: Y
name: base
vermagic: 4.15.0-107-generic SMP mod_unload
parm: mode:0:mode0; 1:mode1; 2:mode2 (int)
parm: p:pointer select:i2c0; i2c1; i2c2 (charp)
parm: array:set array value (array of int)
parm: usestr:string select:spi00; spi01; spi10 (string)
加载驱动同时指定传递参数;内核默认是不开启将printk
信息输出到终端,可以执行"dmesg -c"
将驱动程序的printk
信息输出到终端;如果开启了内核打印等级,加载驱动后会立即输出参数信息。
root@ubuntu:/mnt/hgfs/LSW/STHB/drivers64/base# insmod base.ko mode=1 p="i2c0" array=1,2,3 usestr="spi00"
root@ubuntu:/mnt/hgfs/LSW/STHB/drivers64/base# dmesg -c
[ 378.047187] base driver init
[ 378.047188] mode: 1
[ 378.047188] p: i2c0
[ 378.047188] array[0]:1
[ 378.047189] array[1]:2
[ 378.047189] array[2]:3
[ 378.047189] string: spi00