본문 바로가기
Linux BSP

[Linux Device Driver] 주번호, 부번호의 개념과 예제

by TYB 2024. 2. 26.
반응형

Makefile

APP := minor_app
MOD := minor_dev
SRC := $(APP).c
obj-m := $(MOD).o

CROSS = ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
CC := arm-linux-gnueabihf-gcc
KDIR := /home/ubuntu/pi_bsp/kernel/linux
PWD := $(shell pwd)

default:$(APP)
        $(MAKE) -C $(KDIR) M=$(PWD) modules $(CROSS)
        cp $(MOD).ko /srv/nfs
$(APP):
        $(CC) $(APP).c -o $(APP)
        cp $(APP) /srv/nfs
clean:
        rm -rf *.ko
        rm -rf *.mod.*
        rm -rf .*.cmd
        rm -rf *.o
        rm -rf modules.order
        rm -rf Module.symvers
        rm -rf $(MOD).mod
        rm -rf .tmp_versions
        rm -rf $(APP)

 

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>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/moduleparam.h>
#include <linux/gpio.h>
#define GPIOLEDCNT 8
#define GPIOKEYCNT 8
#define OFF 0
#define ON 1

#define   MINOR_DEV_NAME        "minordev"
#define   MINOR_DEV_MAJOR            230

int gpioLed[GPIOLEDCNT] = {6,7,8,9,10,11,12,13};
int gpioKey[GPIOKEYCNT] = {16,17,18,19,20,21,22,23};
static int onevalue = 1;
static char * twostring = NULL;
module_param(onevalue, int ,0);
module_param(twostring,charp,0);


int gpioLedInit(void);
void gpioLedSet(long);
void gpioLedFree(void);
int gpioKeyInit(void);
int gpioKeyGet(void);
void gpioKeyFree(void);

int gpioLedInit(void)
{
        int i;
        int ret = 0;
        char gpioName[10];
        for(i=0;i<GPIOLEDCNT;i++)
        {
                sprintf(gpioName,"led%d",i);
                ret = gpio_request(gpioLed[i], gpioName);
                if(ret < 0) {
                        printk("Failed Request gpio%d error\n", 6);
                        return ret;
                }
        }
        for(i=0;i<GPIOLEDCNT;i++)
        {
                ret = gpio_direction_output(gpioLed[i], OFF);
                if(ret < 0) {
                        printk("Failed direction_output gpio%d error\n", 6);
         return ret;
                }
        }
        return ret;
}

void gpioLedSet(long val)
{
        int i;
        for(i=0;i<GPIOLEDCNT;i++)
        {
                gpio_set_value(gpioLed[i], (val>>i) & 0x01);
        }
}
void gpioLedFree(void)
{
        int i;
        for(i=0;i<GPIOLEDCNT;i++)
        {
                gpio_free(gpioLed[i]);
        }
}

int gpioKeyInit(void)
{
        int i;
        int ret=0;
        char gpioName[10];
        for(i=0;i<GPIOKEYCNT;i++)
        {
                sprintf(gpioName,"key%d",i);
                ret = gpio_request(gpioKey[i], gpioName);
                if(ret < 0) {
                        printk("Failed Request gpio%d error\n", 6);
                        return ret;
                }
        }
        for(i=0;i<GPIOKEYCNT;i++)
        {
                ret = gpio_direction_input(gpioKey[i]);
                if(ret < 0) {
                        printk("Failed direction_output gpio%d error\n", 6);
         return ret;
                }
        }
        return ret;
}
int     gpioKeyGet(void)
{
        int i;
        int ret;
        int keyData=0;
        for(i=0;i<GPIOKEYCNT;i++)
        {
                ret=gpio_get_value(gpioKey[i]) << i;
                keyData |= ret;
        }
        return keyData;
}
void gpioKeyFree(void)
{
        int i;
        for(i=0;i<GPIOKEYCNT;i++)
        {
                gpio_free(gpioKey[i]);
        }
}

int minor0_open (struct inode *inode, struct file *filp)
{
    printk( "call minor0_open\n" );
    return 0;
}

ssize_t minor0_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
    unsigned char status;
        int ret;
        //  get_user(status,buf);
    ret=copy_from_user(&status,buf,count);
    gpioLedSet(status);

    return count;
}

int minor0_release (struct inode *inode, struct file *filp)
{
    printk( "call minor0_release\n" );
    return 0;
}

int minor1_open (struct inode *inode, struct file *filp)
{
    printk( "call minor1_open\n" );
    return 0;
}

ssize_t minor1_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
    unsigned char status;
        int ret;
    status = gpioKeyGet();
//  put_user(status,buf);
    ret=copy_to_user(buf,&status,count);

    return count;
}

int minor1_release (struct inode *inode, struct file *filp)
{
    printk( "call minor1_release\n" );
    return 0;
}

struct file_operations minor0_fops =
{
    .owner    = THIS_MODULE,
    .write    = minor0_write,
    .open     = minor0_open,
    .release  = minor0_release,
};

struct file_operations minor1_fops =
{
    .owner    = THIS_MODULE,
    .read     = minor1_read,
    .open     = minor1_open,
    .release  = minor1_release,
};

int minor_open (struct inode *inode, struct file *filp)
{
    printk( "call minor_open\n" );
    switch (MINOR(inode->i_rdev))
    {
    case 0: filp->f_op = &minor0_fops; break;
    case 1: filp->f_op = &minor1_fops; break;
    default : return -ENXIO;
    }

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

    return 0;
}

struct file_operations minor_fops =
{
    .owner    = THIS_MODULE,
    .open     = minor_open,
};

int minor_init(void)
{
    int result;

        result = gpioLedInit();
    if(result < 0)
//      return result;
        return -EBUSY;
    result = gpioKeyInit();
    if(result < 0)
        return result;

    result = register_chrdev( MINOR_DEV_MAJOR, MINOR_DEV_NAME, &minor_fops);
    if (result < 0) return result;

    return 0;
}

void minor_exit(void)
{
    unregister_chrdev( MINOR_DEV_MAJOR, MINOR_DEV_NAME );
        gpioLedSet(0);
    gpioLedFree();
    gpioKeyFree();

}

module_init(minor_init);
module_exit(minor_exit);

MODULE_AUTHOR("KCCI-AIOT KSH");
MODULE_DESCRIPTION("led key test module");
MODULE_LICENSE("Dual BSD/GPL");

 

file operation을 해야하기 때문에 부번호 처리하는 부분이 조금 바뀜.

 

원하는 에러코드를 넣고 싶으면 perror 에러 메시지를 찾아서 -를 붙여주면 되고

 

minor_fops를 살펴보면 2개밖에 없음. 동일한 주번호를 사용하기 때문에, 주번호에 대한 file operation은 몇개 없는거임, 부번호에 따라 file operation이 나뉘는거고

 

부번호에 따른 서로 다른 file operation

 

file 포인터에 file operation을 등록하는 switch문에서 주소를 등록하면 부번호 각각 0,1의 file operation 주소가 fllp->f_op에 등록이 되는거임.

처음에 등록되있던 주번호 주소는 필요가 없어진거임. 부번호에 대한 부분만 남으면 됨.

 

195번 줄에 보면 해당 file operation 파일 포인터에 open 함수가 있으면,

196번줄에서 open 함수를 실행하는 걸 볼 수있음.

 

 

 

 


app.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

#define WRITE_DEVICE_FILENAME  "/dev/minor_write"  //major:230, minor:0
#define READ_DEVICE_FILENAME   "/dev/minor_read"   //major:230, minor:1

int main()
{
    int  read_dev;
    int  write_dev;

    char buff[128];
    int loop;

    read_dev  = open( READ_DEVICE_FILENAME,  O_RDWR|O_NDELAY );
    if( read_dev < 0 )
    {
                perror("open()");
        printf( READ_DEVICE_FILENAME " open error\n" );
        exit(1);
    }

    write_dev = open( WRITE_DEVICE_FILENAME, O_RDWR|O_NDELAY );
    if( write_dev < 0 )
    {
                perror("open()");
        printf( WRITE_DEVICE_FILENAME " open error\n" );
        close( read_dev );
        exit(1);
    }
    printf( "wait... input\n" );
    while(1)
    {
        if( read(read_dev,buff,1 ) == 1  && buff[0] != 0)
        {
            printf( "read data [%02X]\n", buff[0] & 0xFF );
//            if( !(buff[0] & 0x10) ) break;
            if( buff[0] & 0x80) break;
        }
    }
    printf( "input ok...\n");

    printf( "led flashing...\n");
    for( loop=0; loop<5; loop++ )
    {
        buff[0] = 0xFF;
        write(write_dev,buff,1 );
        sleep(1);
        buff[0] = 0x00;
        write(write_dev,buff,1 );
        sleep(1);
    }

    close(read_dev);
    close(write_dev);

    return 0;
}

 

pi@pi08:/mnt/ubuntu_nfs $ sudo rmmod ledkey_array_dev.ko



pi@pi08:/mnt/ubuntu_nfs $ sudo mknod /dev/minor_write c 230 0
pi@pi08:/mnt/ubuntu_nfs $ sudo mknod /dev/minor_read c 230 1


pi@pi08:/mnt/ubuntu_nfs $ ls -l /dev/minor_*
crw-r--r-- 1 root root 230, 1 Feb 26 10:39 /dev/minor_read
crw-r--r-- 1 root root 230, 0 Feb 26 10:39 /dev/minor_write



pi@pi08:/mnt/ubuntu_nfs $ sudo ./minor_app

 

버튼을 하나씩 눌러보면 01, 02,04 08,10 ,20 , 40 , 80 쭈루루루룩 나옴.

폴링방식이라 그렇고

8번째 키를 누르면 led가 5번 깜빡이다가 끝남.

dmesg 보면 이런게 커널 메시지에 출력됨.
여기서 minor_fops를 등록하면서 등록을 하게 되는거고

 


이 상태일 때 dmesg 커널메시지는 우측과 같음.

커널 메시지를 보면 주번호 파일포인터 열고, 부번호1 파일포인터 열고/ 주번호 파일포인터 열고, 부번호0 파일포인터 열고 순으로 진행됨.


결론: app.c에 부번호에 따라 다른 디바이스 드라이버를 호출하도록 설정되어 있고, 다른 함수를 쓰는거임.

고로 같은 장치는 같은 주번호를 쓰고, 다른 동작을 수행하는 거는 부번호로 분리하는거임.

그래서 app이 실행되면서 open을 하는데 

open을 하면 dev.c로 넘어감.

dev.c에서 minor_open으로 넘어가는데, 

 

넘어가서 부번호의 file 포인터로 값이 바뀌게 되고 부번호에 맞는 함수를 실행함.


 


용도에 따른 디바이스 구분( misc계열의 디바이스)

이런식으로 동일 장치가 여러개일 경우 동일한 주번호를 사용하고 부번호를 나눠서 부여해준다.

 

이런식으로 misc계열의 디바이스는 주로 주번호 10번에 정의되어있음.

 

아래는 주번호 별로 사전에 정의되어 있는 사용용도임.

주번호 10번이 기타 디바이스로 MISC에 할당되어 있는거임.
부 번호 10에 할당된 부 번호 디바이스 목록

반응형