디바이스 드라이버 / 부 번호에 의한 파일 처리
부 번호에 의한 파일 처리는 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 대응됩니다.
틀린 점이 있다면 댓글 부탁드리겠습니다. 감사합니다.