«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
관리 메뉴

printf("ho_tari\n");

8일차 본문

2025.06.20

 

 

리눅스 디바이스 드라이버 ⇒ 임베디드에서 관심 영역

 

  • 역할
    • 디바이스 드라이버는 하드웨어를 사용 가능하게 만들어 줄 뿐 하드웨어를 어떻게 사용 할지에 대한 결정은 응용 프로그램에게 넘겨야 한다.
  • 고려사항
    • 최대한 유연하고 많은 기능을 사용자에게 제공하려고 할 수록 디바이스 드라이버 제작자는 많은 부분을 구현 해야한다.
    • 동기식, 비동기식 모두 지원할 것인가?
    • 장치를 여러 번 열 것 인가?
    • 정책 독립성을 제공할 것인가?
  • 제공되는 디바이스 드라이버 유틸리티
    • 디바이스 제어와 구성을 도울 목적으로 간단한 유틸리티와 디바이스 드라이버를 같이 출시하는 경우가 있다.
  • 모놀리식 방식
    • 초기 리눅스 커널은 모놀로딕 방식으로 커널에 모든 디바이스 드라이버를 포함 시켜야 했다.
    • 이런 방식은 디바이스가 바뀔때마다 커널 컴파일을 다시 해야 한다.
  • 모듈구동 방식
    • 커널이 동작중인 상태에서 디바이스 드라이버를 동적으로 추가하거나 제거할 수 있는 개념으로 개발 시간을 효과적으로 단축할 수 있다.
    • PCI, USB, PCMCIA에 관련된 디바이스의 PNP기능을 지원하려면 모듈 방식이 필요하다.
    • 모듈 방식은 MMU가 있는 프로세서에서만 지원되며, 커널 버전이 동일해야 하는 문제가 있다.

 

hello_module.c

#include <linux/module.h>
#include <linux/init.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple Hello World module");

static int __init hello_init(void) {
    printk(KERN_INFO "Hello World from kernel module!\n");
    return 0;
}

static void __exit hello_exit(void) {
    printk(KERN_INFO "Goodbye from kernel module!\n");
}

module_init(hello_init);
module_exit(hello_exit);

 

Makefile

obj-m := hello_module.o
KDIR := $(HOME)/project/linux
PWD  := $(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	make -C $(KDIR) M=$(PWD) clean

 

make -j12 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-

 

문자 디바이스 드라이버 예제 (하드웨어 불필요)

 

char_dev.c

#include <linux/module.h>
#include <linux/fs.h>

#define DEV_NAME "mychardev"
static int major_num;

static ssize_t dev_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
{
    const char *msg = "Hello from kernel!\n";
    return simple_read_from_buffer(buf, len, offset, msg, strlen(msg));
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = dev_read,
};

static int __init char_dev_init(void)
{
    major_num = register_chrdev(0, DEV_NAME, &fops);
    if (major_num < 0) {
        pr_err("Device registration failed\n");
        return major_num;
    }
    pr_info("Major number: %d\n", major_num);
    return 0;
}

static void __exit char_dev_exit(void)
{
    unregister_chrdev(major_num, DEV_NAME);
    pr_info("char_dev removed\n");
}

module_init(char_dev_init);
module_exit(char_dev_exit);

MODULE_LICENSE("GPL");

 

leddrv.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>

#define DEV_NAME "led-driver"
#define BCM_BASE 0xFE000000
#define GPIO_BASE (BCM_BASE + 0x200000)

#define GPIO_PIN 18

static void __iomem *gpio_base;
#define INP_GPIO(g)   (*(volatile unsigned int *)(gpio_base + ((g)/10)*4) &= ~(7 << (((g)%10)*3)))
#define OUT_GPIO(g)   (*(volatile unsigned int *)(gpio_base + ((g)/10)*4) |=  (1 << (((g)%10)*3)))
#define GPIO_SET(g)   (*(volatile unsigned int *)(gpio_base + 0x1C) = (1 << g))
#define GPIO_CLR(g)   (*(volatile unsigned int *)(gpio_base + 0x28) = (1 << g))

static int major;

static ssize_t myled_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    char kbuf[2] = {0};

    if (copy_from_user(kbuf, buf, 1)) return -EFAULT;

    if (kbuf[0] == '1') {
           GPIO_SET(GPIO_PIN);  // 켜기
           pr_info("LED ON\n");
        }
    else if (kbuf[0] == '0') {
         GPIO_CLR(GPIO_PIN);  // 끄기
        pr_info("LED OFF\n");
}

    return count;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .write = myled_write,
};

static int __init myled_init(void)
{
    major = register_chrdev(0, DEV_NAME, &fops);
    if (major < 0) return major;

    gpio_base = ioremap(GPIO_BASE, 0x100);
    INP_GPIO(GPIO_PIN);
    OUT_GPIO(GPIO_PIN);

    pr_info("myled loaded: /dev/%s (major %d)\n", DEV_NAME, major);
    return 0;
}

static void __exit myled_exit(void)
{
    unregister_chrdev(major, DEV_NAME);
    if (gpio_base) iounmap(gpio_base);
    pr_info("myled unloaded\n");
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

 

myled.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int fd;
    if (argc != 2 || (argv[1][0] != '0' && argv[1][0] != '1')) {
        printf("사용법: %s 0|1\n", argv[0]);
        return 1;
    }

    fd = open("/dev/led-driver", O_WRONLY);
    if (fd < 0) {
        perror("디바이스 열기 실패");
        return 1;
    }

    write(fd, argv[1], 1);
    close(fd);
    return 0;
}

 

sudo mknod /dev/led-driver c $(grep led-driver /proc/devices | awk '{print $1}') 0

 

'(Telechips) AI 시스템 반도체 SW 개발자 교육 > SoC 시스템 반도체를 위한 임베디드 리눅스' 카테고리의 다른 글

10일차  (0) 2025.06.24
9일차  (0) 2025.06.23
7일차  (0) 2025.06.20
6일차  (0) 2025.06.18
5일차  (0) 2025.06.17