본문 바로가기
Linux BSP

[Raspberry Pi Device Driver] 디바이스 드라이버(app과 dev)를 통한 device 제어

by TYB 2024. 2. 22.
반응형

 

call_dev.c

우측은 device driver에서 호출할 때 사용되는 함수고,      좌측은 어플리케이션에서 사용하는 함수

#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 230

static int call_open(struct inode *inode, struct file *filp)
{
        int num = MINOR(inode->i_rdev);
        printk("call open -> minor : %d\n", num);
        num = MAJOR(inode->i_rdev);
        printk("call open -> major : %d\n", num);
        return 0;
}

static loff_t call_llseek(struct file *filp, loff_t off, int whence)
{
        printk("call llseek -> off : %08X, whence : %08X\n", (unsigned int)off, whence);
        return 0x23;
}
static ssize_t call_read(struct file *filp, char *buf,size_t count, loff_t *f_pos)
{
        printk("call read -> buf : %08X, count : %08X\n", (unsigned int)buf, count);
        return 0x33;
}
static 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", (unsigned int)buf, count);
        return 0x43;
}
static long call_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
        printk("call ioctl -> cmd : %08X, arg : %08X\n", cmd, (unsigned int)arg);
        return 0x53;
}
static int call_release(struct inode *inode, struct file *filp)
{
        printk("call release\n");
        return 0;
}
//선언과 동시에 초기화하기 위해서 .owner = THIS_MODULE 이런식으로 해주는거임.
//여기서 들어가는 값들은 함수포인터들임.
//왼쪽에 있는 .read가 file_operations 구조체에 정의되어 있는 사용가능한 함수원형이고 call_read는 우리가 그 원형에 맞게 구현해둔 함수임.
struct file_operations call_fops =
{
        .owner = THIS_MODULE,
        .llseek = call_llseek,
        .read = call_read,
        .write = call_write,
        .unlocked_ioctl = call_ioctl,
        .open = call_open,
        .release = call_release,
};
static int call_init(void)
{
        int result;
        printk("call call_init  \n");
        result =register_chrdev(CALL_DEV_MAJOR,CALL_DEV_NAME, &call_fops);//커널에 device driver를 적재해주는 코드
        //230번에 calldev라는 이름으로 file_operations 구조체를 등록시켜주는거임.
        if(result < 0) return result;
        return 0;
}

static void 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_LICENSE("Dual BSD/GPL");

 

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(void)
{
        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 dev:%d\n",dev);
                ret=lseek(dev,0x20, SEEK_SET);
                printf("ret = %08X\n",ret);
                printf("3) read function call \n");
                ret = read(dev,(char*)0x30,0x31);
                printf("ret = %08X\n",ret);
                printf("4) write function call \n");
                ret = write(dev,(char*)0x40,0x41);
                printf("ret = %08X\n",ret);
                printf("5) ioctl function call \n");
                ret = ioctl(dev,(char*)0x51,0x52);
                printf("ret = %08X\n",ret);
                printf("6) device file close \n");
                ret = close(dev);
                printf("ret = %08X\n",ret);
        }
        else
                perror("open");
        return 0;
}

 

Makefile

APP := call_app
MOD := call_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)

 

make를 하고 call_app과 call_dev가 컴파일되면 ko파일이 자동으로 라즈베리파이의 nfs로 복사가 되고

 

라즈베리의 nfs로 가서 실행시켜보자

call_dev.ko와 call_app 실행파일 2가지가 있음.

 

 

pi@pi08:/mnt/ubuntu_nfs $ cat /proc/devices

사용중인 device들의 번호를 확인해주고 안쓰는걸로 하나 잡아

 

 

그래서 call_dev.ko 등록시켜주고 

pi@pi08:/mnt/ubuntu_nfs $ sudo mknod /dev/calldev c 230 32

pi@pi08:/mnt/ubuntu_nfs $ sudo insmod call_dev.ko

 

dmesg하면 커널 메시지 출력되어 있고

 

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

앱 실행시켜보면 정상적으로 device driver를 통해 file 입출력이 가능함.

dmesg하면 커널 메시지 출력되어 있음.

 

 

 

앱과 device driver의 관계를 보는게 이번 실습의 목표이고 어떤 값들을 return하는지 보기 위한 예제일뿐임.

 

반응형