리눅스 디바이스 트리의 핵심 원리와 실용적 구현

디바이스 트리 디버깅 기법

디바이스 트리 문제 해결을 위해 필수 도구를 활용하는 방법을 설명합니다. 다음 명령어들은 개발 과정에서 빈번히 사용됩니다:

명령어 기능 사용 예시
dtc -I dtb -O dts DTB를 DTS로 변환 dtc -I dtb -O dts system.dtb > extracted.dts
fdtdump DTB 구조 분석 fdtdump --scan system.dtb
/proc/device-tree 런타임 노드 확인 cat /proc/device-tree/model

실시간 디버깅을 위한 핵심 전략:

# 디바이스 트리 문법 검증
dtc -I dts -o /dev/null device-config.dts

# 커널 로그에서 매칭 정보 추출
dmesg | grep "of_match"

# 동적 드라이버 바인딩 테스트
echo "example,custom-gpio" > /sys/bus/platform/drivers/gpio-driver/new_id

# 핀 제어 설정 확인
find /proc/device-tree/pinctrl -name "*gpio*" -exec cat {} \;

실습: 사용자 정의 GPIO 디바이스 구현

다음 예제는 두 개의 입력 버튼과 단일 LED를 제어하는 디바이스를 구현합니다. 핀 구성은 다음과 같이 정의됩니다:

// gpio-control.dts
/dts-v1/;
/ {
    compatible = "example,custom-board";

    fragment@0 {
        target = <&pinctrl>;
        __overlay__ {
            gpio_ctrl: gpio_ctrl {
                pins = "PB5", "PB6", "PB7";
                function = "gpio";
                drive-strength = <8>;
            };
        };
    };

    fragment@1 {
        target-path = "/";
        __overlay__ {
            custom_gpio {
                compatible = "example,simple-gpio";
                status = "okay";
                pinctrl-0 = <&gpio_ctrl>;
                
                led = <&pio 1 5 GPIO_ACTIVE_LOW>;  // PB5
                btn_primary = <&pio 1 6 GPIO_ACTIVE_HIGH>; // PB6
                btn_secondary = <&pio 1 7 GPIO_ACTIVE_HIGH>; // PB7
            };
        };
    };
};

드라이버 코드는 인터럽트 기반 제어 로직을 구현합니다:

#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>

struct gpio_ctrl {
    struct gpio_desc *led;
    struct gpio_desc *btns[2];
    int irq_primary;
    int irq_secondary;
};

static irqreturn_t btn_handler(int irq, void *data)
{
    struct gpio_ctrl *ctx = data;
    bool primary_pressed = (irq == ctx->irq_primary) || 
                          gpiod_get_value(ctx->btns[0]);
    
    gpiod_set_value(ctx->led, primary_pressed);
    return IRQ_HANDLED;
}

static int gpio_probe(struct platform_device *pdev)
{
    struct gpio_ctrl *ctx;
    int ret;

    ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
    if (!ctx) return -ENOMEM;

    ctx->led = devm_gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);
    ctx->btns[0] = devm_gpiod_get(&pdev->dev, "btn_primary", GPIOD_IN);
    ctx->btns[1] = devm_gpiod_get(&pdev->dev, "btn_secondary", GPIOD_IN);

    ctx->irq_primary = gpiod_to_irq(ctx->btns[0]);
    ret = devm_request_irq(&pdev->dev, ctx->irq_primary, 
                          btn_handler, IRQF_TRIGGER_RISING,
                          "primary_btn", ctx);
    
    platform_set_drvdata(pdev, ctx);
    return 0;
}

static const struct of_device_id gpio_match[] = {
    { .compatible = "example,simple-gpio" },
    { }
};

static struct platform_driver gpio_driver = {
    .probe = gpio_probe,
    .driver = {
        .name = "gpio-controller",
        .of_match_table = gpio_match,
    },
};
module_platform_driver(gpio_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("사용자 정의 GPIO 제어 드라이버");

디바이스 트리 설계 가이드라인

잘못된 디바이스 트리 정의로 인한 커널 부팅 실패를 방지하기 위해 반드시 확인해야 할 사항:

// 오류 사례: 필수 속성 누락
sensor@40000000 {
    compatible = "example,temperature";
    // 주소 매핑 정보 누락!
};

// 올바른 정의
sensor@40000000 {
    compatible = "example,temperature";
    reg = <0x40000000 0x1000>;
    interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
    clock-frequency = <1000000>;
};

핵심 검증 포인트:

  • 모든 메모리 매핑 디바이스에 reg 속성 필수 포함
  • 인터럽트 사용 디바이스는 interruptsinterrupt-parent 정의
  • 동적 핀 제어가 필요한 경우 pinctrl 설정 검증
  • 호환성 문자열(compatible)은 벤더-모델 형식으로 명시

태그: device-tree gpio-driver linux-kernel DTS dtb

9월 20일 07:02에 게시됨