커널 포팅 (4. 드라이버 포팅 / 4.3 새로운 드라이버 추가하기)

2025. 3. 6. 19:44프로그래밍/시스템

4.3 새로운 드라이버 추가하기

리눅스에서 새로운 디바이스 드라이버를 추가하는 과정은 크게 다음과 같습니다.

  1. 간단한 문자 디바이스 드라이버 작성 (register_chrdev(), cdev_add())
  2. /dev 노드 생성 및 접근 (mknod, udev)
  3. 새로운 하드웨어 인터페이스 추가 (I2C, SPI, GPIO 등)

🔹 간단한 문자(Character) 디바이스 드라이버 작성

문자 디바이스 드라이버는 데이터를 바이트 단위로 읽고 쓰는 장치를 제어하는 드라이버입니다.
예를 들어 시리얼 포트(UART), 키보드, 마우스 등이 문자 디바이스로 동작합니다.

1️⃣ 문자 드라이버 기본 코드 (my_char_driver.c)

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

#define DEVICE_NAME "my_char_device"

static int major;
static struct cdev my_cdev;
static char device_buffer[256];

static int my_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "my_char_device: Opened\n");
    return 0;
}

static int my_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO "my_char_device: Closed\n");
    return 0;
}

static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {
    int bytes_read = count < sizeof(device_buffer) ? count : sizeof(device_buffer);
    if (copy_to_user(buf, device_buffer, bytes_read))
        return -EFAULT;
    return bytes_read;
}

static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
    int bytes_written = count < sizeof(device_buffer) ? count : sizeof(device_buffer);
    if (copy_from_user(device_buffer, buf, bytes_written))
        return -EFAULT;
    return bytes_written;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_release,
    .read = my_read,
    .write = my_write,
};

static int __init my_init(void) {
    dev_t dev;
    
    if (alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME) < 0)
        return -1;
    
    major = MAJOR(dev);
    cdev_init(&my_cdev, &fops);
    if (cdev_add(&my_cdev, dev, 1) < 0) {
        unregister_chrdev_region(dev, 1);
        return -1;
    }

    printk(KERN_INFO "my_char_device: Registered with major number %d\n", major);
    return 0;
}

static void __exit my_exit(void) {
    cdev_del(&my_cdev);
    unregister_chrdev_region(MKDEV(major, 0), 1);
    printk(KERN_INFO "my_char_device: Unregistered\n");
}

module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple Character Device Driver");

🔹 /dev 노드 생성 및 접근 (mknod, udev)

디바이스 드라이버를 로드하면, /dev 디렉토리 아래에 장치 파일을 생성해야 사용자 공간에서 접근할 수 있습니다.

1️⃣ 커널 모듈 빌드

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules

2️⃣ 커널 모듈 로드

sudo insmod my_char_driver.ko

3️⃣ /dev 노드 수동 생성 (mknod) 커널 모듈을 로드하면 dmesg를 통해 major 번호를 확인하고, /dev에 장치 노드를 생성합니다.

dmesg | grep my_char_device

출력 예시:

my_char_device: Registered with major number 240

mknod 명령어로 /dev 노드를 생성:

sudo mknod /dev/my_char_device c 240 0
sudo chmod 666 /dev/my_char_device

4️⃣ 자동으로 /dev 노드 생성 (udev 설정) 리눅스에서는 udev를 사용하여 자동으로 /dev 노드를 생성할 수도 있습니다.

/etc/udev/rules.d/99-mydevice.rules 파일을 생성:

echo 'KERNEL=="my_char_device", MODE="0666"' | sudo tee /etc/udev/rules.d/99-mydevice.rules

udev 설정 적용:

sudo udevadm control --reload-rules
sudo udevadm trigger

5️⃣ 장치 테스트

echo "Hello Device" > /dev/my_char_device
cat /dev/my_char_device

출력 예시:

Hello Device

6️⃣ 드라이버 언로드

sudo rmmod my_char_driver

🔹 새로운 하드웨어 인터페이스 추가 (I2C, SPI, GPIO 등)

리눅스에서는 문자 디바이스 드라이버를 활용하여 I2C, SPI, GPIO 등과 연결된 하드웨어를 제어할 수 있습니다.

📌 1️⃣ I2C 디바이스 드라이버 예제

I2C 드라이버 코드 추가 (my_i2c_driver.c)

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

#define DEVICE_NAME "my_i2c_device"

static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) {
    printk(KERN_INFO "I2C Device Detected: %s\n", id->name);
    return 0;
}

static int my_i2c_remove(struct i2c_client *client) {
    printk(KERN_INFO "I2C Device Removed\n");
    return 0;
}

static struct i2c_device_id my_i2c_idtable[] = {
    { DEVICE_NAME, 0 },
    { }
};

static struct i2c_driver my_i2c_driver = {
    .driver = {
        .name = DEVICE_NAME,
    },
    .probe = my_i2c_probe,
    .remove = my_i2c_remove,
    .id_table = my_i2c_idtable,
};

module_i2c_driver(my_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple I2C Device Driver");

I2C 드라이버 빌드 및 로드

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo insmod my_i2c_driver.ko

I2C 디바이스 목록 확인

ls /sys/class/i2c-adapter/
i2cdetect -y 1  # I2C 버스에서 디바이스 검색

✅ 정리

  1. 문자 디바이스 드라이버 작성
    • register_chrdev(), cdev_add()를 사용하여 간단한 문자 디바이스 생성
    • /dev/my_char_device를 통해 사용자 공간에서 접근 가능
  2. /dev 노드 생성 및 접근
    • mknod 명령어로 장치 파일 수동 생성
    • udev 설정을 통해 /dev 자동 생성 가능
  3. 새로운 하드웨어 인터페이스 추가
    • I2C, SPI, GPIO 등의 하드웨어 인터페이스를 문자 디바이스 드라이버로 확장 가능
    • i2c_driver 구조체를 사용하여 새로운 I2C 디바이스 지원 가능