OS/Linux

Char Device Driver 만들기.

아르비스 2010. 5. 11. 14:31
Linux Kernel Device Driver를 공부하면서
테스트 해본 코드

Struct File Operations
    /usr/src/linux-headers-2.6.27-17-generic/include/linux/fs.h

  struct file_operations {

  struct module *owner;

  loff_t (*llseek) (struct file *, loff_t, int);

  ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

  ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

  ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

  ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

  int (*readdir) (struct file *, void *, filldir_t);

  unsigned int (*poll) (struct file *, struct poll_table_struct *);

  int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

  long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

  long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

  int (*mmap) (struct file *, struct vm_area_struct *);

  int (*open) (struct inode *, struct file *);

  int (*flush) (struct file *, fl_owner_t id);

  int (*release) (struct inode *, struct file *);

  int (*fsync) (struct file *, struct dentry *, int datasync);

  int (*aio_fsync) (struct kiocb *, int datasync);

  int (*fasync) (int, struct file *, int);

  int (*lock) (struct file *, int, struct file_lock *);

  ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

  unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

  int (*check_flags)(int);

  int (*dir_notify)(struct file *filp, unsigned long arg);

  int (*flock) (struct file *, int, struct file_lock *);

  ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);

  ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);

  int (*setlease)(struct file *, long, struct file_lock **);

  int (*fsetattr)(struct file *, struct iattr *);

};


장치 사용 예시
 - Device 사용 함수 작성
   1. Open()함수 작성
 static int my_open( struct inode *inode, struct file *filp )

{

  printk( "[VB] opened\n" );

  return 0;

}

 
   2. struct file_operations 요소 중 .open에 함수를 지정
static struct file_operations vd_fops = {
 …
 .release = my_release
};

  3. 같은 방법으로 read(), write(), release() 등 필요한 함수를 작성
  4. struct file_operations에 위와 같은 방법으로 지정

- Device Driver 등록
int __init my_init( void )
{
  register_chrdev( MAJOR_NUMBER, "virtual_buffer", &vd_fops );
  ......... 기타 필요한 초기화 작업 .............
  buffer = (char*) kmalloc( BUFF_SIZE, GFP_KERNEL );
  memset( buffer, 0, BUFF_SIZE);
  printk( "[VB] initialized\n");
  return 0;
};

- Device Driver 삭제
   모듈을 커널에서 제거하게 되면, unregister_chrdev()를 이용하여, 커널에서 Device Driver의 정보를 삭제함.
void __exit my_exit( void )
{
  unregister_chrdev( MAJOR_NUMBER, "virtual_buffer" );
  kfree( buffer );
  printk( "[VB] exited\n");
}

- 예제 소스
[virtual_buffer.c]
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>

#define  BUFF_SIZE      1024
#define  MAJOR_NUMBER   250

static char *buffer  = NULL;
static int   sz_data = 0;

static int my_open( struct inode *inode, struct file *filp )
{
  printk( "[VB] opened\n" );
  return 0;
}

static int my_release( struct inode *inode, struct file *filp )
{
  printk( "[VB] released\n" );
  return 0;
}

static ssize_t my_write( struct file *filp, const char *buf, size_t count, loff_t *f_pos )
{
   printk( "[VB] write to buffer\n");

   if (BUFF_SIZE < count)  sz_data  = BUFF_SIZE;
                           sz_data  = count;

   strncpy( buffer, buf, sz_data);
   return count;
}

static ssize_t my_read( struct file *filp, char *buf, size_t count, loff_t *f_pos )
{
  printk( "[VB] read from buffer\n" );
  copy_to_user( buf, buffer, sz_data);

  return sz_data;
}

static struct file_operations vd_fops = {
  .read = my_read,
  .write = my_write,
  .open = my_open,
  .release = my_release
};

int __init my_init( void )
{
  register_chrdev( MAJOR_NUMBER, "virtual_buffer", &vd_fops );
  buffer = (char*) kmalloc( BUFF_SIZE, GFP_KERNEL );
  memset( buffer, 0, BUFF_SIZE);
  printk( "[VB] initialized\n");
  return 0;
}

void __exit my_exit( void )
{
  unregister_chrdev( MAJOR_NUMBER, "virtual_buffer" );
  kfree( buffer );
  printk( "[VB] exited\n");
}

module_init( my_init );
module_exit( my_exit );
MODULE_LICENSE( "GPL" );

- Makefile
obj-m := virtual_buffer.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
     $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
     rm -rf *.ko
     rm -rf *.mod.*
     rm -rf .*.cmd
     rm -rf *.o
     rm -rf .tmp_versions
<주의 : 위 default 와 clean 다음 줄에는 TAB을 이용하여 띄워야 함. 안그러면 Error 난다>

- Module Compile
  $> make
     - virtual_buffer.ko 생성됨.

- Module 실행
  = mknod를 이용하여 /dev 안에 node를 생성
    - Device Driver 파일을 만들었다면, 일반 프로그램에서는 그 장치를 파일처럼 다루기 위해, 즉 가상파일 시스템으로 접근할 수 있도록 /dev 안에 node로 등록해야 함.
     $> mknod /dev/virtual_buffer c 250 0

- insmod를 이용하여 디바이스 드라이버를 커널에 등록
  $> insmod virtual_buffer.ko

 [ Application을 이용한 module Test ]
#include <stdio.h>
#include <string.h>  // strlen()
#include <unistd.h>  // read, write
#include <fcntl.h>   // open, close, O_RDWR, O_NONBLOCK

#define  BUFF_SIZE   1024

char *getword()
{
 char inputword[256];

 memset(inputword, 0x0, 256);
 
 printf("input word : ");
 scanf("%s", inputword);
 return inputword;
}

int   main(void)
{
   int   fd;
   char *str_hello   = "Hello, Virtual Buffer!!";
   char  buff[BUFF_SIZE];
   char *str;

   if ( 0  < ( fd = open( "/dev/virtual_buffer", O_RDWR)))
   {
     // init buffer
     memset(buff , 0x0, BUFF_SIZE);
     write( fd, buff, BUFF_SIZE); 
   
     str = getword();

  if (str[0] == '1')
  {
       write( fd, str_hello, strlen( str_hello)+1); // +1: Include NULL
     }
  else
  {
   write( fd, str, strlen(str)+1); // +1: Include NULL
  }
      read(  fd, buff, BUFF_SIZE);
      printf( "\n%s\n", buff);
      close( fd);
   }
   return 0;
}

- Application Compile 및 실행
  $> gcc -o sam sample.c
       -o (실행파일을 만듬)
       sam (실행파일 이름 지정)
       sample.c (compile 할 소스 이름)

  $> ./sam   (실행, run)