Linuxデバイスツリー解析実践ガイド:ソースコードからドライバ紐付けまでの仕組み

デバイスツリーの基本構造とC言語による解析

組み込みLinuxシステムにおいて、デバイスツリー(Device Tree)はハードウェアリソースと周辺機器の接続関係を記述するために不可欠です。C言語を用いてデバイスツリーノードを解析することで、ハードウェア構成情報を動的に取得し、ドライバの移植性を向上させることができます。

デバイスツリーの構造

デバイスツリーソース(.dts)はバイナリ形式(.dtb)にコンパイルされ、ブートローダーによってメモリにロードされます。カーネルはこの構造を解析して platform_device を構築します。各ノードには、互換性を示す compatible、アドレスを示す reg、割り込みを示す interrupts などの主要なプロパティが含まれます。

C言語によるノード情報の取得

Linuxカーネルは、デバイスツリーの内容にアクセスするためのAPIを提供しています。代表的な関数には of_find_node_by_name()of_property_read_u32()of_iomap() などがあります。

// デバイスツリーから周辺機器パラメータを解析する実装例
#include <linux/of.h>
#include <linux/platform_device.h>

static int dev_resource_probe(struct platform_device *pdev)
{
    struct device_node *node = pdev->dev.of_node;
    u32 addr_offset, poll_interval;

    // レジスタのベースアドレス・オフセットを読み取り
    if (of_property_read_u32(node, "reg", &addr_offset)) {
        return -EINVAL;
    }

    // 更新間隔(単位:ms)を読み取り、存在しない場合はデフォルト値を設定
    if (of_property_read_u32(node, "update-ms", &poll_interval)) {
        poll_interval = 500; // デフォルト値
    }

    dev_info(&pdev->dev, "Base: 0x%x, Interval: %u ms\n", addr_offset, poll_interval);
    return 0;
}

// マッチングテーブルの定義
static const struct of_device_id custom_driver_match[] = {
    { .compatible = "my-corp,custom-periph" },
    { }
};
MODULE_DEVICE_TABLE(of, custom_driver_match);

このプロセスでは、まず device_node ポインタを取得し、of_property_read_u32 を使用して数値プロパティを抽出します。そして、compatible 文字列を照合することで、ドライバとデバイスノードが紐付けられます。

カーネルにおけるデバイスツリーのロードと初期化

カーネルは起動の初期段階で、U-Bootなどのブートローダーから渡されたDTB(Device Tree Blob)を検証します。early_init_dt_verify() によって整合性が確認された後、解析フェーズに移ります。

情報の展開(Unflattening)

カーネルは unflatten_device_tree() を呼び出し、フラットなバイナリ構造を struct device_node の階層的なオブジェクトへと変換します。これにより、カーネル内の各サブシステムがノード間の親子関係を容易に辿れるようになります。

void __init unflatten_device_tree(void)
{
    __unflatten_device_tree(initial_boot_params, NULL, &of_root,
                           early_init_dt_alloc_memory_arch, false);
}

initial_boot_params はDTBの開始アドレスを指し、of_root に解析後のルートノードが保持されます。

ドライバにおけるデバイスツリー解析のパターン

プラットフォームドライバは、デバイスツリーを利用してハードウェア記述とドライバコードを分離(デカップリング)します。

of_match_table による自動マッチング

ドライバが登録される際、カーネルは of_match_table を参照して適切なデバイスを探索します。

static const struct of_device_id hardware_ids[] = {
    { .compatible = "acme,io-controller-v1", .data = (void *)RESOURCE_TYPE_A },
    { .compatible = "acme,io-controller-v2", .data = (void *)RESOURCE_TYPE_B },
    { /* センチネル */ }
};

デバイスツリー内のノードが compatible = "acme,io-controller-v1"; を持っている場合、このドライバが選択され、probe 関数が実行されます。

リソースの動的取得:I/Oメモリと割り込み

ハードコードされたアドレスを避け、platform_get_resource を用いてリソースを取得するのが現代的な手法です。

struct resource *mem_res;
void __iomem *reg_base;
int irq_line;

// メモリリソースの取得とマッピング
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg_base = devm_ioremap_resource(&pdev->dev, mem_res);

// 割り込み番号の取得
irq_line = platform_get_irq(pdev, 0);

高度な解析テクニックとトラブルシューティング

phandle によるノード間参照

デバイスツリーでは、あるノードが別のノードを参照する場合に phandle を使用します。例えば、GPIOコントローラやクロック源を指定する際に頻用されます。

// DTS内での参照例
display_subsystem {
    pwms = <&pwm1 0 5000000>;
};

ドライバ側では of_parse_phandle() を用いてターゲットとなるノードのポインタを取得できます。

dmesg による不整合の特定

ドライバの紐付けが失敗した場合、dmesg ログを確認することが解決への近道です。よくあるエラーパターンは以下の通りです。

  • status = "disabled": デバイスツリー上でノードが無効化されている。
  • Compatible string mismatch: DTSの文字列とドライバのマッチテーブルが一致していない。
  • Invalid phandle: 参照しているノードが存在しない、または名前が間違っている。

ログに of_parse_phandle: bad phandle などのメッセージが出力されている場合は、DTS内の参照関係を再確認する必要があります。これにより、ハードウェアの定義ミスを早期に発見できます。

タグ: Linux Kernel Device Tree Embedded Systems driver development C Language

9月6日 19:26 投稿