insmod해서 나온 major number  나온 값을 가지고 device파일을 생성되고.
그걸 가지고. app에서 그 device를 가지고 처리하는 것이 핵심이다.

device 프로그래밍할 때의 큰 개념은
insmod에서 register하고,
rmmod에서 unregister되게 하고,
모듈 등록하면 ok~

 
보드가 2.4 커널 기반이라서 2.4로 해야했다.
module_init, module_exit 을 이용하고.. 실제 file_operation할 때 어떤 메소드가 호출될지를 지정한다. 


struct file_operations mina_fops = {
    read:       mina_read,
    write:      mina_write,
    open:       main_open,
    ioctl: mina_ioctl,
    release:    mina_release
};

int mina_init(void)
{
int error;

printk("<1> Mina Module id Up....\n");

/** important **/
error = register_chrdev(mina_major, "mina", &mina_fops);
if(error < 0) {
printk(KERN_WARNING "mina:register_chrdev() error!!\n");
return error;
} else if(sk_major==0) mina_major = error;
printk("<1> mina:register_chrdev() ok! => major_num:%d\n", mina_major);

printk(KERN_WARNING "Mina Module Insert Done!!\n");
return 0;
}

void mina_exit(void)
{
printk("<1> Mina Module id Down....\n");

unregister_chrdev(mina_major, "mina");

printk(KERN_WARNING "Mina Module Delete Done!!\n");
}

module_init(mina_init);
module_exit(mina_exit);

MODULE_LICENSE("Dual BSD/GPL");



register 함수가 참 중요하다.

함수의 원형은 다음과 같다. 
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

커널 모듈시 등록시  register_chrdev() 함수의 세번째 파라미터인 fops 를 파라미터로 넣는다.
독특해 보이는데, 그 이유는 함수형 포인터때문이다. 이것을 이용해서 write 시스템 콜이 호출되면, VFS에서 그것을 커널에 등록된 write로 매핑된 함수를 호출하도록 되어 있는 것이다.

linux/fs.h 파일에 정의되어 있는 구조체는 다음과 같다. 


struct file_operations {
  struct module *owner;
  loff_t (*llseek) (struct file *, loff_t, int);
  ssize_t (*read) (struct file *, char *, size_t, loff_t *);
  ssize_t (*write) (struct file *, const char *, size_t, 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);
  int (*mmap) (struct file *, struct vm_area_struct *);
  int (*open) (struct inode *, struct file *);
  int (*flush) (struct file *);
  int (*release) (struct inode *, struct file *);
  int (*fsync) (struct file *, struct dentry *, int datasync);
  int (*fasync) (int, struct file *, int);
  int (*lock) (struct file *, int, struct file_lock *);
  ssize_t (*readv)  (struct file *, const struct iovec *, unsigned long, loff_t *);
  ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
};




register_chrdev() 함수의 두번째 major number 파라미터이다. major number는 unique해야 하는데,  사실 모든 major 번호를 찾기가 어렵다.  major number를 0을 주면 알아서 major 번호를 준다.
리턴값으로 major 정보를 전달한다.  그게 0 이하면 에러를 의미한다.

이렇게 만들어진 커널모듈을 리눅스에 올려보자..
 



[root@linux2 01.sk_basic]# insmod miua_drv.ko
[root@linux2 01.sk_basic]# lsmod | grep sk
mina_drv                  2692  0
[root@linux2 01.sk_basic]# dmesg |
Enabling unmasked SIMD FPU exception support... done.
VFS: Disk quotas dquot_6.5.1
RAMDISK driver initialized: 16 RAM disks of 16384K size 1024 blocksize
Attached scsi disk sda at scsi0, channel 0, id 0, lun 0
 mina:register_chrdev() ok! => major_num:254




254 major 넘버로 사용하고, char device로 하고, minor는 아무거나 한다.

[root@linux2 mina]# mknod /dev/mina c 254 0

[root@linux2 mina]# ls -al /dev/mina
crw-r--r--  1 root root 254, 0  4월 19 11:39 /dev/mina

[root@linux2 mina]# ./mina_test_app
/dev/mina file open ok!!
app => write request(tx_data:33)!!
app => write done(ret:4)!!
app => read request!!
app => read done(rx_data:1, ret:4)!!
app => ioctl request(cmd:1)!!
app => ioctl done(ret:0)!!



dmesg로 커널단에서 어떻게 호출되었는지 확인가능하다.

[DD]open..
[DD]write..
[DD]read..
[DD]ioctl..
[DD]release..



참고로.. .  ioctl 함수에 재미있는 것이 있다. 

int mina_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)

마지막 파라미터는 unsigned long 은 포인터로 오는 경우를 의미할 수도 있다. 단순한 숫자뿐 아니라 포인터도 보낼 수 있다는 의미이다.











Posted by '김용환'
,