ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 디바이스 드라이버 / 부 번호에 의한 파일 처리
    Linux/Linux Device Driver 2020. 9. 1. 12:41

    부 번호에 의한 파일 처리는 register_chrdev() 함수를 사용해야 합니다.

     

    그럼 register_chrdev() 함수를 사용하는 이유는 뭘까요?

     

    바로 부 번호로 제어를 하기 위해서는 디바이스 파일과 1:N으로 대응해야 하기 때문입니다.

     

     

    pi@raspberrypi:~/Documents/minor $ nano minor_driver.c

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>		// file_operations 포인터 함수
    #include <linux/uaccess.h>	// copy_to_user()
    #include <linux/slab.h>		// kmalloc()
    
    #define MAJOR_DEV_NAME  "major_dev"
    #define MAJOR_DEV_NUM   200
    
    static int minor1_open(struct inode *inode, struct file *file)
    {
       printk("minor1_open\n");
    
       return 0;
    }
    
    static ssize_t minor1_read(struct file *file, char *buf, size_t count, loff_t *f_pos)
    {
       char *buff;
       int err;
    
       buff = kmalloc(count, GFP_KERNEL);
    
       if(buff == NULL)
       {
          printk("kmalloc error\n");
    
          return 0;
       }
    
       sprintf(buff, "KERNEL DATA");
    
       err = copy_to_user(buf, buff, count);
    
       if(err != 0)
       {
          printk("copy_to_user error\n");
    
          kfree(buff);
    
          return err;
       }
    
       printk("minor1_read -> count: %d, f_pos: %lld\n", count, *f_pos);
    
       kfree(buff);
    
       return 0;
    }
    
    static int minor1_close(struct inode *inode, struct file *file)
    {
       printk("minor1_close\n");
    
       return 0;
    }
    
    struct file_operations minor1_fops = {
    .owner = THIS_MODULE,
    .open = minor1_open,
    .read = minor1_read,
    .release = minor1_close,
    };
    
    static int minor2_open(struct inode *inode, struct file *file)
    {
       printk("minor2_open\n");
    
       return 0;
    }
    
    static ssize_t minor2_write(struct file *file, const char *buf, size_t count, loff_t *f_pos)
    {
       char *buff;
       int err;
    
       buff = kmalloc(count, GFP_KERNEL);
    
       if(buff == NULL)
       {
          printk("kmalloc error\n");
    
          return 0;
       }
    
       err = copy_from_user(buff, buf, count);
    
       if(err != 0)
       {
          printk("copy_from_user error\n");
    
          kfree(buff);
    
          return err;
       }
    
       printk("minor2_write -> buff: %s, count: %d, f_pos: %lld\n", buff, count, *f_pos);
    
       kfree(buff);
    
       return 0;
    }
    static int minor2_close(struct inode *inode, struct file *file)
    {
       printk("minor2_close\n");
    
       return 0;
    }
    
    struct file_operations minor2_fops = {
    .owner = THIS_MODULE,
    .open = minor2_open,
    .write = minor2_write,
    .release = minor2_close,
    };
    
    static int major_open(struct inode *inode, struct file *file)
    {
       printk("major_open\n");
    
       switch(MINOR(inode->i_rdev))
       {
          case 1:
             file->f_op = &minor1_fops;
             break;
          case 2:
             file->f_op = &minor2_fops;
             break;
          default:
             return -ENXIO;
       }
    
       if(file->f_op && file->f_op->open)
       {
          return file->f_op->open(inode, file);
       }
    
       return 0;
    }
    
    struct file_operations major_fops = {
    .owner = THIS_MODULE,
    .open = major_open,
    };
    
    int minor_init(void)
    {
       int err;
    
       printk("minor_init\n");
    
       err = register_chrdev(MAJOR_DEV_NUM, MAJOR_DEV_NAME, &major_fops);
    
       if(err < 0)
       {
          printk("register_chrdev error\n");
    
          return err;
       }
    
       return 0;
    }
    
    void minor_exit(void)
    {
       printk("minor_exit\n");
    
       unregister_chrdev(MAJOR_DEV_NUM, MAJOR_DEV_NAME);
    }
    
    module_init(minor_init);
    module_exit(minor_exit);
    
    MODULE_AUTHOR("icjk1003@gmail.com");
    MODULE_LICENSE("GPL");

     

     

    pi@raspberrypi:~/Documents/minor $ nano minor_app.c

    #include <stdio.h>
    #include <fcntl.h>    // open()
    #include <unistd.h>   // write() read() ..
    #include <stdlib.h>   // exit()
    #include <string.h>   // strlen()
    
    #define MINOR1_DEV_FILE_NAME  "/dev/minor1_dev"
    #define MINOR2_DEV_FILE_NAME  "/dev/minor2_dev"
    
    char *msg = "USER DATA";
    
    int main(int argc, char **argv)
    {
       int minor1_fd;
       int minor2_fd;
    
       char buf[1024];
    
       minor1_fd = open(MINOR1_DEV_FILE_NAME, O_RDWR);
    
       if(minor1_fd < 0)
       {
          printf(MINOR1_DEV_FILE_NAME " open error\n");
    
          exit(1);
       }
    
       minor2_fd = open(MINOR2_DEV_FILE_NAME, O_RDWR);
    
       if(minor2_fd < 0)
       {
          printf(MINOR2_DEV_FILE_NAME " open error\n");
    
          close(minor1_fd);
    
          exit(1);
       }
    
       read(minor1_fd, buf, 20);
       printf("read data: %s\n", buf);
    
    
       write(minor2_fd, msg, strlen(msg)+1);
    
       close(minor1_fd);
       close(minor2_fd);
    
       return 0;
    }

     

     

    pi@raspberrypi:~/Documents/minor $ nano Makefile

    obj-m := minor_driver.o
    KDIR := /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)
    
    default: minor_app minor_driver
    
    minor_app:
    	gcc -o $@ minor_app.c
    minor_driver:
    	make -C $(KDIR) M=$(PWD) modules
    clean:
    	make -C $(KDIR) M=$(PWD) clean
    	rm minor_app

     

    pi@raspberrypi:~/Documents/minor $ make

    pi@raspberrypi:~/Documents/minor $ sudo insmod minor_driver.ko

     

    pi@raspberrypi:~/Documents/minor $ sudo mknod /dev/read_dev c 200 1

    pi@raspberrypi:~/Documents/minor $ sudo mknod /dev/write_dev c 200 2

     

    pi@raspberrypi:~/Documents/minor $ sudo ./minor_app

    pi@raspberrypi:~/Documents/minor $ dmesg

     

    pi@raspberrypi:~/Documents/major $ sudo rmmod minor_driver.ko

     

     

    KERNEL DATA는 유저 영역에서 볼 수 있고 USER DATA는 커널 영역에서 볼 수 있다.

     

    성공..

     

     

    실행 흐름

       
    [minor_app] open() 함수 실행 /dev/minor1_dev 디바이스 파일
    [minor_driver] major_open() 함수 실행
    [minor_driver] major_open() 함수에서 /dev/minor1_dev 디바이스 파일에 대한 오퍼레이션 함수 등록(부 번호 함수)
    [minor_driver] minor1_open() 함수 실행
    [minor_app] open() 함수 실행 /dev/minor2_dev 디바이스 파일
    [minor_driver] major_open() 함수 실행
    [minor_driver] major_open() 함수에서 /dev/minor2_dev 디바이스 파일에 대한 오퍼레이션 함수 등록(부 번호 함수)
    [minor_driver] minor2_open() 함수 실행
    [minor_app] read() 함수 실행
    [minor_driver] minor1_read() 함수 실행
    [minor_app] write() 함수 실행
    [minor_driver] minor2_write() 함수 실행
    [minor_app] close() 함수 실행
    [minor_driver] minor1_close() 함수 실행
    [minor_app] close() 함수 실행
    [minor_driver] minor2_close() 함수 실행

     

     

     

    위 실행 흐름을 보면 /dev/read_dev, /dev/write_dev 둘 중 어느 파일을 읽어도 major_open() 함수가 실행됩니다.

    왜냐하면 주 번호를 인자 값으로 사용하는 register_chrdev() 함수로 등록했기 때문에 디바이스 파일과 1:N로 대응되는 것입니다.  

    주 번호와 부 번호를 인자 값으로 사용하는 register_chrdev_region() 함수로 등록하면 디바이스 파일과는 1:1 대응됩니다.

     

     

     

     

     

     

    틀린 점이 있다면 댓글 부탁드리겠습니다. 감사합니다.

     

     

    댓글

Designed by Tistory.