리눅스 드라이버 종류 및 구성
리눅스 디바이스 드라이버 구성 및 구조
<디바이스 드라이버 기본 구조>
저레벨 함수를 통한 캐릭터 디바이스 드라이버 실행.
mknod 통해 캐릭터 디바이스 설정.
mknod 명령으로 캐릭터 디바이스 파일 생성
# mknod /dev/calldev c 240 0
# ls -al /dev/calldev
crw-r--r-- 1 root root 240, 1 Mar 24 01:48 /dev/callde
|
디바이스 드라이버 모듈 작성
call_dev.c
|
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#define CALL_DEV_NAME "calldev"
#define CALL_DEV_MAJOR 240 // 디바이스 주넘버 230까지는 정해진 사용자가 있다.
int call_open (struct inode *inode, struct file *filp)
{
int num = MINOR(inode->i_rdev);
printk( "call open -> minor : %d\n", num );
return 0;
}
loff_t call_llseek (struct file *filp, loff_t off, int whence )
{
printk( "call llseek -> off : %08X, whenec : %08X\n", off, whence );
return 0x23;
}
ssize_t call_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
printk( "call read -> buf : %08X, count : %08X \n", buf, count );
return 0x33;
}
ssize_t call_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
printk( "call write -> buf : %08X, count : %08X \n", buf, count );
return 0x43;
}
int call_release (struct inode *inode, struct file *filp)
{
printk( "call release \n" );
return 0;
}
// file_operations 구조체에 실제 실행될 저레벨 함수를 연결하기 위한 리소트를 저장한다
struct file_operations call_fops =
{
.owner = THIS_MODULE,
.llseek = call_llseek,
.read = call_read,
.write = call_write,
.open = call_open,
.release = call_release, //...... 모든 구현을 다 할 필요는 없다.
};
int __init call_init(void)
{
int result;
printk( "call call_init \n" );
// 커널에 문자 디바이스를 등록한다
// 이때, 맵핑되는 저레벨 입출력함수들을 등록한 file_operations 구조체를 커널에 등록한다.
result = register_chrdev( CALL_DEV_MAJOR, CALL_DEV_NAME, &call_fops);
if (result < 0) return result;
return 0;
}
void __exit call_exit(void)
{
printk( "call call_exit \n" );
unregister_chrdev( CALL_DEV_MAJOR, CALL_DEV_NAME ); // 커널에서 문자 디바이스를 제거한다.
}
module_init(call_init);
module_exit(call_exit);
MODULE_AUTHOR("root");
MODULE_DESCRIPTION("The test for calling a character device.");
MODULE_LICENSE("BSD");
|
※ file_operations 구조체는 커널 버전 별로 다양한 저베렐 입출력 함수에 대하여 맵핑 할수 있도록 지원한다.
“/dev/calldev” 캐릭터 디바이스를 저레벨 입출력 함수로 동작 시키기 위한 어플리케이션 작성
call_app.c
|
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#define DEVICE_FILENAME "/dev/calldev"
int main()
{
int dev;
char buff[128];
int ret;
printf( "1) device file open\n");
dev = open( DEVICE_FILENAME, O_RDWR|O_NDELAY );
if( dev >= 0 )
{
printf( "2) seek function call\n");
ret = lseek( dev, 0x20, SEEK_SET );
printf( "ret = %08X\n", ret );
printf( "3) read function call\n");
ret = read(dev,0x30, 0x31 );
printf( "ret = %08X\n", ret );
printf( "4) write function call\n");
ret = write(dev,0x40,0x41 );
printf( "ret = %08X\n", ret );
printf( "6) device file close\n");
ret = close(dev);
printf( "ret = %08X\n", ret );
}
return 0;
}
|
※ CentOS7 에서 테스트를 진행 했는데, 3.14 로 커널 버전을 업데이트 했다.
3.14 버전에서는 캐릭터 디바이스 드라이버에 대한 다양한 입출력 함수중 ioctl 에 대한 함수를 제공하지 않는다. 2.6 커널 버전에서는 지원한다.
커널 모듈을 작성하기 위한 Makefile 작성
Makefile
|
KERNEL_DIR := /lib/modules/`uname -r`/build
BUILD_DIR := `pwd`
VERBOSE := 1
obj-m := call_dev.o
all:
make -C $(KERNEL_DIR) SUBDIRS=$(BUILD_DIR) KBUILD_VERBOSE=$(VERBOSE) modules
clean:
rm -rf *.o *.ko *.mod.c *.symvers *.order .tmp_versions .call_dev.c.*
|
call_app으 통한 저레벨 함수 사용 예제
call_app 실행 결과
# gcc call_app.c
# ./a.out
1) device file open
2) seek function call
ret = 00000023
3) read function call
ret = 00000033
4) write function call
ret = 00000043
6) device file close
ret = 00000000
|
동작 순서
- 사용자 어플리케이션에서, 해당 캐릭터 디바이스 파일을 표준 파일 시스템을 통해 (저레벨 파일 입출력함수) 접근한다.
- 파일 시스템은 파일에 대한 입출력을, 연결된 디바이스 파일의 이름과 주(major)넘버를 통해 커널에서 심볼을 찾는다.
- 커널은 심볼을 통해 실제 커널 디바이스 드라이버 모듈을 찾고, files_operations 구조체를 통해 파일시스템으로 부터온 저레벨 함수 에 대한 커널 드라이버의 구현 부분을 호출한다.
※ 위 소스 코드의 출처는 한빛 미디어 " 리눅스 디바이스 드라이버" 의 예제 소스를 기본으로 centOS7 ,Kernel 3.14에서 동작 하도록 수정한 것이다.
No comments:
Post a Comment