10 새로운 문자 장치 드라이버 파일

새로운 문자 장치 드라이버 원리

register_chrdev 및 unregister_chrdev 함수는 구형 드라이버에서 사용되던 방식입니다. 현재는 새롭게 개선된 문자 장치 드라이버 API를 활용해야 합니다.

장치 번호 할당 및 해제

register_chrdev 함수는 단일 주 장치 번호만 제공받을 수 있었으나, 이는 다음과 같은 문제점을 초래했습니다:

  1. 사용 중인 주 장치 번호를 사전에 확인해야 했습니다.
  2. 주 장치 번호 아래 모든 부 장치 번호를 차지하게 되었습니다.

이러한 문제를 해결하기 위해 Linux 커널에 장치 번호를 요청하는 방식을 권장합니다. 필요 없는 경우 다음 함수를 사용할 수 있습니다:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

사용자가 주/부 장치 번호를 직접 지정하는 경우 다음 함수를 사용할 수 있습니다:

int register_chrdev_region(dev_t from, unsigned count, const char *name);

모든 방식에서 동일한 해제 함수를 사용합니다:

void unregister_chrdev_region(dev_t from, unsigned count);

장치 번호 할당 예시

int device_major; /* 주 장치 번호 */
int device_minor; /* 부 장치 번호 */
dev_t device_id; /* 장치 ID */

/* 장치 번호 할당 */
if (device_major) {
    device_id = MKDEV(device_major, 0);
    register_chrdev_region(device_id, 1, "test");
} else {
    alloc_chrdev_region(&device_id, 0, 1, "test");
    device_major = MAJOR(device_id);
    device_minor = MINOR(device_id);
}

/* 장치 번호 해제 */
unregister_chrdev_region(device_id, 1);

자동 장치 노드 생성

modprobe로 드라이버 모듈을 로드하면 /dev 디렉토리에 자동으로 장치 파일이 생성됩니다.

mdev 사용

mdev를 통해 장치 노드의 생성 및 제거를 처리할 수 있으며, Buildroot에서 이를 자동으로 관리합니다.

클래스 및 장치 생성

자동 장치 노드 생성은 드라이버 진입 함수에서 수행됩니다:

struct class *class_create(struct module *owner, const char *name);
void class_destroy(struct class *cls);

장치 생성 시 다음 함수를 사용합니다:

struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);

삭제 시:

void device_destroy(struct class *cls, dev_t devt);

예시

struct class *device_class;
struct device *device_node;
dev_t device_id;

static int __init led_init(void) {
    device_class = class_create(THIS_MODULE, "xxx");
    device_node = device_create(device_class, NULL, device_id, NULL, "xxx");
    return 0;
}

static void __exit led_exit(void) {
    device_destroy(device_class, device_id);
    class_destroy(device_class);
}

파일 기반 데이터 설정

하드웨어 장치의 속성을 구조체로 정리하는 것이 좋습니다:

struct test_dev {
    dev_t device_id;
    struct cdev cdev;
    struct class *device_class;
    struct device *device_node;
    int device_major;
    int device_minor;
};

open 함수에서 파일 기반 데이터를 설정합니다:

static int test_open(struct inode *inode, struct file *filp) {
    filp->private_data = &testdev;
    return 0;
}

드라이버 작성

LED 드라이버 예시

#include 
#include 
#include 
#include 
#include 

#define DEVICE_COUNT 1
#define DEVICE_NAME "led_driver"
#define LED_ON 1
#define LED_OFF 0

struct led_dev {
    dev_t device_id;
    struct cdev cdev;
    struct class *device_class;
    struct device *device_node;
    int device_major;
    int device_minor;
};

struct led_dev led_dev;

static int led_open(struct inode *inode, struct file *filp) {
    filp->private_data = &led_dev;
    return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {
    unsigned char data;
    copy_from_user(&data, buf, cnt);
    
    if (data == LED_ON) {
        // LED 켜기 로직
    } else if (data == LED_OFF) {
        // LED 끄기 로직
    }
    return cnt;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .write = led_write,
};

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author");

테스트 앱 작성

#include 
#include 
#include 

int main(int argc, char **argv) {
    int fd;
    int value;
    
    if (argc != 3) {
        printf("Usage: %s  <value>\n", argv[0]);
        return -1;
    }

    fd = open(argv[1], O_RDWR);
    if (fd < 0) {
        perror("open failed");
        return -1;
    }

    value = atoi(argv[2]);
    write(fd, &value, sizeof(value));
    close(fd);
    return 0;
}

실행 및 테스트

Makefile 예시:

KERNELDIR := /path/to/kernel
CURRENT_PATH := $(shell pwd)
obj-m := led_driver.o

build:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

명령어:

make
arm-none-linux-gnueabihf-gcc test_app.c -o test_app
depmod
modprobe led_driver
./test_app /dev/led_device 1
./test_app /dev/led_device 0
rmmod led_driver

태그: Linux_Kernel Character_Device_Driver CDev_API Device_Node_Creation Module_Programming

5월 23일 11:00에 게시됨