[리눅스] 디바이스 드라이버 파라미터 전달 - module_param()
디바이스 드라이버에 Argument 전달
디바이스 드라이버가 등록될때나 실행되고 있을때 인자를 전달할 수 있을까요? 예를 들면 우리가 ls 명령어를 실행할때 ls -l /etc/와 같이 -l /etc/와 같은 인자들을 전달하는 것처럼 말이죠. 응용 프로그램에서는 간단합니다. 우리는 알고 있죠. C언어를 사용한다면 main에서 argc와 args를 사용해서 목적을 달성 할 수 있다는 것을 말이죠.
int main(int argc, char* argv[])
리눅스의 디바이스 드라이버에서도 가능합니다. 바로 아래의 매크로들을 이용하면 됩니다. 포스팅 아래에서는 아래의 매크로를 통해서 예제 코드를 구현합니다.
- module_param(name, type, perm) : 변수 name을 설정합니다. name의 자료형은 type이고, perm은 권한을 나타냅니다.
- module_param_array(name, type, num, perm) : 배열 버전입니다. num이 자료형의 배열 크기를 나타냅니다.
- module_param_cb(name, &ops, &name, perm) : 만약 paremeter의 값이 바뀐 경우 알아차려야한다면, 이 매크로를 사용하면 됩니다. cb는 callback의 약자입니다.
그리고 이러한 파라미터를 읽거나 수정할때 권한(permission)을 지정할 수 있습니다. S_I를 Prefix로 하고, R(Read), W(Write), X(eXecute)를 의미하며 USR은 user, GRP는 group 권한을 의미합니다. |(OR)을 통해서 여러 권한을 줄 수 있습니다.
- S_IRUSR
- S_IWUSR
- S_IXUSR
- S_IRGRP
- S_IWGRP
- S_IXGRP
아래의 코드를 보면서 어떻게 위의 매크로들이 사용되는지 알아보도록 하겠습니다.
소스코드
//passing_params.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
int value, arr_value[4];
char *name;
int cb_value = 0;
module_param(value, int, S_IWUSR|S_IRUSR);
module_param(name, charp, S_IRUSR|S_IWUSR);
module_param_array(arr_value,int, NULL, S_IRUSR|S_IWUSR);
// module_param_cb를 위한 setter
int notify_param(const char *val, const struct kernel_param *kp){
int res = param_set_int(val, kp);
if(res == 0){
printk(KERN_INFO "Call back function called...\n");
printk(KERN_INFO "New value of cb_value = %d\n",cb_value);
return 0;
}
return -1;
}
const struct kernel_param_ops my_param_ops = {
.set = ¬ify_param, //위에 정의한 setter
.get = ¶m_get_int, // standard getter
};
module_param_cb(cb_value, &my_param_ops, &cb_value, S_IRUGO|S_IWUSR);
static int __init my_module_init(void){
int i;
printk(KERN_INFO "===== Print Params =====\n");
printk(KERN_INFO "value = %d \n", value);
printk(KERN_INFO "cb_value = %d \n", cb_value);
printk(KERN_INFO "name = %s\n", name);
for(i = 0; i < sizeof(arr_value)/sizeof(int); i++){
printk(KERN_INFO "arr_value[%d] = %d \n", i, arr_value[i]);
}
return 0;
}
static void __exit my_module_exit(void){
printk(KERN_INFO "Kernel Module Removed ...\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Reakwon");
MODULE_DESCRIPTION("A simple parameter test module");
MODULE_VERSION("1:1.0");
Makefile
obj-m += passing_params.o
KDIR = /lib/modules/$(shell uname -r)/build
all :
make -C $(KDIR) M=$(shell pwd) modules
clean :
make -C $(KDIR) M=$(shell pwd) clean
insmod로 parameter 전달
$ sudo insmod passing_params.ko value=4 name="reakwon" arr_value=1,2,3,4
dmesg를 확인하면 잘 전달되고 읽히는 것을 확인할 수 있습니다.
파라미터 업데이트시 Callback
만약 parameter를 디바이스 드라이버가 실행 중일때 변경하고 이에 따른 동작을 수행하고자 한다면 어떻게 할까요? module_param_cb가 이런 이유때문에 존재합니다.
모듈의 parameter는 /sys/module 아래의 자신의 모듈 이름의 디렉토리 하위 parameters 디렉토리에서 관리가 됩니다. 확인해볼까요?
$ ls /sys/module/passing_params/parameters/
arr_value cb_value name value
그래서 parameters의 하위의 변수들의 값을 바꿀수 있습니다. 이때 callback 함수를 등록하면 호출이 되게 됩니다. 위의 전체 코드 중 이와 관련한 코드가 여깄습니다.
// module_param_cb를 위한 setter
int notify_param(const char *val, const struct kernel_param *kp){
//...//
}
const struct kernel_param_ops my_param_ops = {
.set = ¬ify_param, //위에 정의한 setter
.get = ¶m_get_int, // standard getter
};
module_param_cb(cb_value, &my_param_ops, &cb_value, S_IRUGO|S_IWUSR);
kernel_param_ops가 callback을 관리하는 구조체이고 여기의 멤버로 .set, .get, .free가 있습니다.
struct kernel_param_ops
{
int (*set)(const char *val, const struct kernel_param *kp);
int (*get)(char *buffer, const struct kernel_param *kp);
void (*free)(void *arg);
};
아래와 같이 parameter의 값을 변경해봅시다.
$ sudo sh -c "echo 1010 > /sys/module/passing_args/parameters/cb_value"
그리고 dmesg를 통해서 kernel 메시지를 확인하면 우리가 등록한 notify_param Callback함수가 호출됨을 알 수 있습니다.
이 포스팅은 embetronicx의 contents를 참고하여 작성된 포스팅입니다. 여기에 다 담지못하는 부분은 아래의 페이지에서 참고하시기 바랍니다. 앞으로도 embetronicx의 tutorial을 기반으로 작성할 예정입니다.