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 대응됩니다.

 

 

 

 

 

 

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