Study/Device Driver

[Device Driver] Character Device Driver Led Driver 예제 참고용

Alex An 2021. 8. 16. 19:42

드라이브 시스템 구조

① 응용 프로그램(Application APP)

  • 파일 함수 호출 : open("/dev/xxx"), read(), write()

 

② 시스템 콜 인터페이스(System call interface)

  • SWI(Software interrupt)로 인해 전달된 파라미터의 값, 즉 Exception의 원인에 따라 다른 예외 처리 함수를 호출

 

③ 가상 파일 시스템(VFS, Virtual File System)

  • sys_open, sys_read ...
  • 열려 있는 여러 디바이스 파일에 따라 다른 드라이버를 찾아 해당 기능을 호출
  • 예를 들어, 캐릭터 디바이스가 열려 있는 경우 커널에 의해 정의된 배열 chrdev[]를 확인
  • 배열의 인덱스는 주 장치 번호이며, 내용은 file_operations 구조체

 

④ 하드웨어

 

 

캐릭터 디바이스 드라이버 작성 예제

캐릭터 디바이스 드라이버 작성 방법

  • open(), read(), write() 등의 함수 사용
static int first_drv_open(struct inode* inode, struct file* file)
{
    //printk("first_drv_open\n");
    
    /*
     * LED1, LED2, LED4 correspond to GPB5, GPB6, GPB7, GPB8
     */
     
    /* Configure GPB5, 6, 7, 8 as output */
    *gpbcon &= ~((0x3 << (5*2)) | (0x3 << (6*2)) | (0x3 << (7*2)) | (0x3 << (8*2));
    *gpbcon |= ~((0x1 << (5*2)) | (0x1 << (6*2)) | (0x1 << (7*2)) | (0x1 << (8*2));
    
    return 0;
}

static ssize_t first_drv_write(struct file* file, const char __user* buf, size_t count, loff_t* ppos)
{
    int val;
    
    //printk("first_drv_write\n");
    
    copy_from_user(&val, buf, count); // copy_to_user();
    
    if(val == 1)
    {
    	//Light up
        *gpbdat &= ~((1 << 5) | (1 << 6) | (1 << 7) | (1 << 8));
    }
    else
    {
    	//Light off
        *gpbdat |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8);
    }
    
    return 0;
}

 

커널이 캐릭터 디바이스 드라이버의 함수를 호출하도록 만드는 방법

 

  ① file_operations 구조체를 정의하고 아래의 방법을 사용하여 초기화

static struct file_operations first_drv_fops = {
    .owner = THIS_MODULE, /* This is a macro that pushes to the __this module variable created automatically when the module is compiled */
    .open = first_drv_open,
    .write = first_drv_write,
};

 

  ② 위 file_operations 구조체를 커널에 등록

major = register_chrdev(0, "first_drv", &first_drv_fops); // register, tell the kernel

 

  ③ 드라이버 모듈 작성

static struct class* firstdrv_class;
static struct class_device* firstdrv_class_dev;

static int first_drv_init(void)
{
    major = register_chrdev(0, "first_drv", &first_drv_fops); // Register, tell the kernel to write 0 for the major device number -- the system will automatically assign us a vacant major device number
    
    firstdrv_class = class_create(THIS_MODULE, "firstdrv");
    firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
    
    gpbcon = (volatile unsigned long*) ioremap(0x56000010, 16); // ioremap -- map physical addresses to virtual addresses
    gpbdat = gpbcon + 1;
    
    return 0;
}

static void first_drv_exit(void)
{
    unregister_chrdev(major, "first_drv"); // uninstall
    
    class_device_unregister(firstdrv_class_dev);
    class_destroy(firstdev_class);
    
    iounmap(gpbcon);
}

module_init(first_drv_init);
module_init(first_drv_exit);
MODULE_LICENSE("GPL");

 

  위 코드로 캐릭터 디바이스 드라이버를 구현

 

  ④ Makefile을 작성하고 해당 드라이버를 커널 모듈로 컴파일

KERN_DIR = /work/system/linux-2.6.22.6 #Specify which Linux kernel to use

all:
    make -C $(KERN_DIR) M = `pwd` modules # Compile into modules in the current directory
clean:
    make -C $(KERN_DIR) M = `pwd` modules clean
    rm -rf modules.order

obj-m += first_drv.o # obj-m compiled into a module

 

  ⑤ 드라이버 모듈 컴파일 및 로드

make led.ko # now you can see the files in the directory

insmod ./led.ko # load the driver

# if you want to uninstall the driver, you can use: rmmod led

 

응용 프로그램에서 디바이스 드라이버를 호출하는 방법

  • 해당 드라이버에는 디바이스 노드를 자동으로 등록하는 기능이 이미 있음
  • 다음 코드를 사용하여 수행
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class NULL, MKDEV(major, 0), NULL, "led");

  위 작업이 성공하면, firstdrv 클래스의 디렉토리가 /sys/class/ 디렉토리에 나타나고,

 

  ./firstdrv/ 디렉토리 아래에 led 디렉토리가 있으며, ./led 디렉토리에는 dev 파일이 생김

 

  #cat dev를 통해 주/부 디바이스 번호를 볼 수 있음

 

  MKDEV(major, 0)는 해당 정보를 사용하여 디바이스 노드를 생성

 

  아래의 테스트 프로그램(test.c) 코드에서 해당 디바이스 노드를 사용하여 led 하드웨어를 작동

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

/* firstdrvteset on
 * firstdrvtest off
 */
 
int main(int argc, char **argv)
{
    int fd;
    int val = 1;
    
    fd = open("/dev/xyz", O_RDWR);
    if(fd < 0)
    {
        printf("can't open!\n");
    }
    
    if(argc != 2)
    {
        printf("Usage :\n");
        printf("%s <on|off>\n", argv[0]);
        
        return 0;
    }
    
    if(strcmp(argv[1], "on") == 0)
    {
        val = 1;
    }
    else
    {
        val = 0;
    }
    
    write(fd, &val, 4);
    
    return 0;
}

 

insmod 및 rmmod를 실행하자마자 디바이스 노드가 자동으로 생성되고 삭제되는 이유

  • 스크립트 파일 /etc/init.d/re.S에 echo/sbin/mdev > /proc/sys/kernel/hotplug를 추가했기 때문
  • Hot Plug 기능을 지원하며, 장치 노드 생성을 자동으로 삽입하거나 분리함

 

 

참고 사이트

https://titanwolf.org/Network/Articles/Article?AID=cc3ae37e-d405-4abd-a771-031eecb99fa8#gsc.tab=0 

 

Character device driver LED driver(Others-Community)

 

titanwolf.org

 

'Study > Device Driver' 카테고리의 다른 글

[Device Driver] Led Device driver 제작  (0) 2021.08.15
[Device Driver] 리눅스 커널  (0) 2021.08.13