컴퓨터/디바이스 드라이버

[리눅스] 디바이스 드라이버 파라미터 전달 - module_param()

REAKWON 2023. 3. 23. 21:36

디바이스 드라이버에 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 = &notify_param, //위에 정의한 setter
        .get = &param_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를 확인하면 잘 전달되고 읽히는 것을 확인할 수 있습니다.

insmod parameter 전달

 


파라미터 업데이트시 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 = &notify_param, //위에 정의한 setter
        .get = &param_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함수가 호출됨을 알 수 있습니다. 

callback 호출

 

 

이 포스팅은 embetronicx의 contents를 참고하여 작성된 포스팅입니다. 여기에 다 담지못하는 부분은 아래의 페이지에서 참고하시기 바랍니다. 앞으로도 embetronicx의 tutorial을 기반으로 작성할 예정입니다.

https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-part-3-passing-arguments-to-device-driver/

 

반응형