2016-03-24

리눅스 드라이버 종류 및 구성

리눅스 드라이버 종류 및 구성






  • 리눅스 디바이스 드라이버 구성 및 구조



<디바이스 드라이버 기본 구조>

  • 저레벨 함수를 통한 캐릭터 디바이스 드라이버 실행.



  1. 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

  1. 디바이스 드라이버 모듈 작성



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 구조체는 커널 버전 별로 다양한 저베렐 입출력 함수에 대하여 맵핑 할수 있도록 지원한다.
file_operation.PNG


  1. “/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 커널 버전에서는 지원한다.


  1. 커널 모듈을 작성하기 위한 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.*


  1. 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

  • 동작 순서


  1. 사용자 어플리케이션에서, 해당 캐릭터 디바이스 파일을  표준 파일 시스템을 통해 (저레벨 파일 입출력함수) 접근한다.
  2. 파일 시스템은 파일에 대한 입출력을, 연결된 디바이스 파일의 이름과 주(major)넘버를 통해 커널에서 심볼을 찾는다.
  3. 커널은 심볼을 통해 실제 커널 디바이스 드라이버 모듈을 찾고, files_operations 구조체를 통해  파일시스템으로 부터온 저레벨 함수 에 대한 커널 드라이버의 구현 부분을 호출한다.


※ 위 소스 코드의 출처는 한빛 미디어 " 리눅스 디바이스 드라이버" 의 예제 소스를 기본으로 centOS7 ,Kernel 3.14에서 동작 하도록 수정한 것이다.

No comments:

Post a Comment