보통은 major number로 다루지만, tty 처럼 minor number 기반으로 처리해야 할 상황들이 있을 때 사용된다.

[root@linux2 my_parm]# tty
/dev/pts/4
[root@linux2 my_parm]# ls -al /dev/pts
합계 0
drwxr-xr-x   2 root root      0  4월 18 03:52 .
drwxr-xr-x  10 root root   5120  4월 19 11:39 ..
crw--w----   1 root tty  136, 1  4월 19 09:53 1
crw--w----   1 root tty  136, 2  4월 19 10:22 2
crw--w----   1 root tty  136, 3  4월 19 09:54 3
crw--w----   1 root tty  136, 4  4월 20 09:49 4
crw--w----   1 root tty  136, 5  4월 20 09:45 5
crw--w----   1 root tty  136, 6  4월 19 15:09 6

같은 major 번호와 다른 minor 번호를 가진다.


mintor 번호를 따로 가진다면, device driver를 따로 가져야 한다.
그렇다면.. 멀티테스킹에서 
write 함수는 minor 번호만큼 가지고 있어야 한다.
하나가지고 공통으로 쓰기 어려우니. minor 번호당 write 함수가 있어야 한다.



minor 기반으로 read/write/ioctl/open/release가 되는 함수를 만들어서 처리하게 하기..


#include <linux/module.h>

#include <linux/init.h>

#include <linux/kernel.h>

#include <linux/major.h>

#include <linux/fs.h>

#include <asm/uaccess.h>        //copy_to_user

#include <asm/io.h>             //outb, inb

#include <linux/delay.h>        //mdelay, udelay



#define FND 0xf1000000 


#define FND_CS0 (*((volatile unsigned short *)(FND))) 

#define FND_CS1 (*((volatile unsigned short *)(FND + 0x00100000))) 

#define FND_CS2 (*((volatile unsigned short *)(FND + 0x00200000))) 

#define FND_CS3 (*((volatile unsigned short *)(FND + 0x00300000)))


#define ADDR_FND_CS0 ((volatile unsigned short *)FND) 

#define ADDR_FND_CS1 ((volatile unsigned short *)(FND + 0x00100000)) 

#define ADDR_FND_CS2 ((volatile unsigned short *)(FND + 0x00200000)) 

#define ADDR_FND_CS3 ((volatile unsigned short *)(FND + 0x00300000)) 


static short fnd_val[] = {0x3f3f,0x0606,0x5b5b,0x4f4f,0x6666,0x6d6d,0x7c7c,0x2727,0x7f7f,0x6767};


static int FND_MAJOR = 0;


//int fnd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);


int fnd0_open (struct inode *inode, struct file *filp)

{

    printk("fnd0_open\n");

    MOD_INC_USE_COUNT;



    return 0;          /* success */

}


int fnd0_release (struct inode *inode, struct file *filp)

{

    printk("fnd0_close\n");

    MOD_DEC_USE_COUNT;

    return 0;

}


ssize_t fnd0_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)

{

short data;


data = FND_CS0;

// equals

//data = inw(ADDR_FND_CS0);


printk("read data => %04x\n",data);

copy_to_user(buf, &data, count);


return(count);

}


ssize_t fnd0_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)

{

int data;


copy_from_user(&data, buf, count);

printk("write data => %08x\n",data);


FND_CS0 = fnd_val[data];

// equals

//outw(fnd_val[data], ADDR_FND_CS0);


return(count);

}


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

{

switch(cmd)

{

  case 10:  

printk("\n");

printk("cmd = 1\n");

FND_CS0 = 0x0000;

break;

default:

break;

}

return 0;

}


int fnd1_open (struct inode *inode, struct file *filp)

{

    printk("fnd1_open\n");

    MOD_INC_USE_COUNT;



    return 0;          /* success */

}


int fnd1_release (struct inode *inode, struct file *filp)

{

    printk("fnd1_close\n");

    MOD_DEC_USE_COUNT;

    return 0;

}


ssize_t fnd1_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)

{

short data;


data = FND_CS1;

// equals

//data = inw(ADDR_FND_CS0);


printk("read data => %04x\n",data);

copy_to_user(buf, &data, count);


return(count);

}


ssize_t fnd1_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)

{

int data;


copy_from_user(&data, buf, count);

printk("write data => %08x\n",data);


FND_CS1 = fnd_val[data];

// equals

//outw(fnd_val[data], ADDR_FND_CS1);


return(count);

}


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

{

switch(cmd)

{

  case 10:  

printk("\n");

printk("cmd = 1\n");

FND_CS1 = 0x0000;

break;

default:

break;

}

return 0;

}



int fnd2_open (struct inode *inode, struct file *filp)

{

    printk("fnd2_open\n");

    MOD_INC_USE_COUNT;



    return 0;          /* success */

}


int fnd2_release (struct inode *inode, struct file *filp)

{

    printk("fnd2_close\n");

    MOD_DEC_USE_COUNT;

    return 0;

}


ssize_t fnd2_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)

{

short data;


data = FND_CS2;

// equals

//data = inw(ADDR_FND_CS2);


printk("read data => %04x\n",data);

copy_to_user(buf, &data, count);


return(count);

}


ssize_t fnd2_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)

{

int data;


copy_from_user(&data, buf, count);

printk("write data => %08x\n",data);


FND_CS2 = fnd_val[data];

// equals

//outw(fnd_val[data], ADDR_FND_CS2);


return(count);

}


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

{

switch(cmd)

{

  case 10:  

printk("\n");

printk("cmd = 1\n");

FND_CS2 = 0x0000;

break;

default:

break;

}

return 0;

}


int fnd3_open (struct inode *inode, struct file *filp)

{

    printk("fnd3_open\n");

    MOD_INC_USE_COUNT;



    return 0;          /* success */

}


int fnd3_release (struct inode *inode, struct file *filp)

{

    printk("fnd3_close\n");

    MOD_DEC_USE_COUNT;

    return 0;

}


ssize_t fnd3_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)

{

short data;


data = FND_CS3;

// equals

//data = inw(ADDR_FND_CS3);


printk("read data => %04x\n",data);

copy_to_user(buf, &data, count);


return(count);

}


ssize_t fnd3_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)

{

int data;


copy_from_user(&data, buf, count);

printk("write data => %08x\n",data);


FND_CS3 = fnd_val[data];

// equals

//outw(fnd_val[data], ADDR_FND_CS0);


return(count);

}


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

{

switch(cmd)

{

  case 10:  

printk("\n");

printk("cmd = 1\n");

FND_CS3 = 0x0000;

break;

default:

break;

}

return 0;

}




void fnd_clear(void)

{

FND_CS0 = 0x0000;

FND_CS1 = 0x0000;

FND_CS2 = 0x0000;

FND_CS3 = 0x0000;

//mdelay(500);

}

/*

struct file_operations fnd_fops = {

    read:       fnd_read,

    write:      fnd_write,

    open:       fnd_open,

    ioctl:    fnd_ioctl,

    release:    fnd_release,

};

*/


struct file_operations minor0_fops =

{

read     : fnd0_read,

    write    : fnd0_write,

    open     : fnd0_open,

    release  : fnd0_release,

    ioctl    : fnd0_ioctl,

};


struct file_operations minor1_fops =

{

read     : fnd1_read,

    write    : fnd1_write,

    open     : fnd1_open,

    release  : fnd1_release,

    ioctl    : fnd1_ioctl,

};


struct file_operations minor2_fops =

{

read     : fnd2_read,

    write    : fnd2_write,

    open     : fnd2_open,

    release  : fnd2_release,

    ioctl    : fnd2_ioctl,

};


struct file_operations minor3_fops =

{

read     : fnd3_read,

    write    : fnd3_write,

    open     : fnd3_open,

    release  : fnd3_release,

    ioctl    : fnd3_ioctl,

};


int minor_open (struct inode *inode, struct file *filp)

{

    printk( "call minor_open\n" );

    printk("MINOR(inode->i_rdev) : %d\n", MINOR(inode->i_rdev));

    

    switch (MINOR(inode->i_rdev))

    {

    case 0: filp->f_op = &minor0_fops; break;

    case 1: filp->f_op = &minor1_fops; break;

    case 2: filp->f_op = &minor2_fops; break;

    case 3: filp->f_op = &minor3_fops; break;

    default : return -ENXIO;

    }


    if (filp->f_op && filp->f_op->open)

        return filp->f_op->open(inode,filp);


    return -2;

}


struct file_operations minor_fops =

{

    open     : minor_open,

};




static int __init fnd_init(void)

{

    int result;


    printk("FND module is up...\n");


    fnd_clear();    // H/W Initialization


    //result = register_chrdev(FND_MAJOR, "FND", &fnd_fops);

    result = register_chrdev(FND_MAJOR, "FND", &minor_fops);

    

    

    if (result < 0) {

        printk(KERN_WARNING " can't get major \n");

        return result;

    }

    if(FND_MAJOR == 0)

FND_MAJOR = result;


    printk("FND_MAJOR = %d\n", FND_MAJOR);

    return 0;

}


static void __exit fnd_exit(void)

{

unregister_chrdev(FND_MAJOR, "FND");

    printk("FND module is down...\n");

}


module_init(fnd_init);

module_exit(fnd_exit);





테스트 app

/***************************************

 * Filename: fnd_app.c

 * Title: fnd Device Application

 * Desc: Implementation of system call

 ***************************************/

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <fcntl.h>


int main(void)

{

    int retn;

    int fd;


    int flag = 0;

    int fnd_no, menu;

    char dev_name[100];

    

    

    while(1)

    {

        printf("\n\n===== FND Test Program ====\n");

        printf(" 0. FND0 Test\n");

        printf(" 1. FND1 Test\n");

        printf(" 2. FND2 Test\n");

        printf(" 3. FND3 Test\n");

        printf(" 4. Program Quit\n");

        do {

            printf(" Select Menu => ");

            scanf("%d", &fnd_no);

        } while(fnd_no<0 || fnd_no>4);

        if(fnd_no == 4) break;


        while(1)

        {

            printf("\nFND%d Test...\n", fnd_no);

            do {

                printf("Select(0.0_print,1.1_print, ...,9.9_print,10.clear,11.Quit) => ");

                scanf("%d", &menu);

            } while(menu<0 || menu>11);

            if(menu == 11) break;

    

            sprintf(dev_name, "/dev/FND%d", fnd_no);

            fd = open(dev_name, O_RDWR);

            if (fd<0) {

                perror(dev_name);

                exit(-1);

            }

            else printf("/dev/FND%d detected(fd:%d)...\n", fnd_no, fd);

    

      if (menu == 10)  {

       ioctl(fd, menu, flag); // clear

      }

      else { // menu is 4byte(int)

       write(fd, &menu, sizeof(menu));

      }


            close(fd);

        }

    }

    

    return 0;

}


 

Makefile 

CC      = arm-linux-gcc

INCLUDEDIR = /root/linux/include

#CFLAGS += -D__KERNEL__ -DMODULE -I$(INCLUDEDIR) -Wall -O

CFLAGS += -D__KERNEL__ -DMODULE -I$(INCLUDEDIR) -Wall -march=armv4 -DMODULE


#CFLAGS += -D__KERNEL__ -I$(INCLUDEDIR) -Wall -Wstrict-prototypes \

#            -Wno-trigraphs -Os -mapcs -fno-strict-aliasing -fno-common \

#           -fno-common -pipe -mapcs-32 -march=armv4 -Wa,  -mtune=strongarm \

#           -mshort-load-bytes -msoft-float -DMODULE



TARGET = fnd

OBJS = $(TARGET).o



All: $(TARGET).o $(TARGET)_app


$(TARGET).o: $(TARGET).c

    $(CC) $(CFLAGS) -c -o $(OBJS) $(TARGET).c


$(TARGET)_app : $(TARGET)_app.c

    $(CC) -o $(TARGET)_app $(TARGET)_app.c


clean :

    rm -rf *.o $(TARGET)_app





보드에서 커널 모듈 등록
 


[root@Xhyper255 /root]$insmod fnd.o
FND module is up...
FND_MAJOR = 253



[root@Xhyper255 /root]$mknod /dev/FND0 c 253 0
[root@Xhyper255 /root]$mknod /dev/FND1 c 253 1
[root@Xhyper255 /root]$mknod /dev/FND2 c 253 2
[root@Xhyper255 /root]$mknod /dev/FND3 c 253 3
[root@Xhyper255 /root]$ls -al /dev/FND*
crw-r--r--    1 root     root     253,   0 Jan  1 00:14 /dev/FND0
crw-r--r--    1 root     root     253,   1 Jan  1 00:14 /dev/FND1
crw-r--r--    1 root     root     253,   2 Jan  1 00:14 /dev/FND2
crw-r--r--    1 root     root     253,   3 Jan  1 00:14 /dev/FND3




실행코드


[root@Xhyper255 /root]$./fnd_app


===== FND Test Program ====
 0. FND0 Test
 1. FND1 Test
 2. FND2 Test
 3. FND3 Test
 4. Program Quit
 Select Menu => 0

FND0 Test...
Select(0.0_print,1.1_print, ...,9.9_print,10.clear,11.Quit) => 1
call minor_open
MINOR(inode->i_rdev) : 0
fnd0_open
/dev/FND0 detected(fd:3)...
write data => 00000001
fnd0_close

FND0 Test...
Select(0.0_print,1.1_print, ...,9.9_print,10.clear,11.Quit) => 11


===== FND Test Program ====
 0. FND0 Test
 1. FND1 Test
 2. FND2 Test
 3. FND3 Test
 4. Program Quit
 Select Menu => 3

FND3 Test...
Select(0.0_print,1.1_print, ...,9.9_print,10.clear,11.Quit) => 8
call minor_open
MINOR(inode->i_rdev) : 3
fnd3_open
/dev/FND3 detected(fd:3)...
write data => 00000008
fnd3_close

FND3 Test...
Select(0.0_print,1.1_print, ...,9.9_print,10.clear,11.Quit) =>



와우 겁나 잼있다..


ioctl의 cmd 부분은 실제로는 다르게 사용되고 있다.
cmd의 구성은 구분번호, 매직번호, 데이터 크기. 읽기 쓰기 구분으로 나눠서 argument를 전달 할 수 있다. 

 
Posted by '김용환'
,