Linuxドライバーのデバイスツリー

Linuxドライバーのデバイスツリー

デバイスツリーの原点-デバイスツリーとは

  • オープンファームウェアデバイスツリー開発ファームウェアデバイスツリー
    (1)デバイスツリー CPUの数とタイプ、メモリベースのアドレスとサイズ、バスとブリッジ、周辺機器の接続、割り込みコントローラーと割り込みの使用状況、GPIOコントローラーなどの情報を記述できます。そして、GPIOの使用法、クロックコントローラーとクロックの使用法。
    (2)デバイスツリー情報は、人間の読書習慣に適したxmlファイルに似ASCIIテキストファイルに保存されます。ARMLinuxでは、.dtsファイルはARMマシンに対応し、カーネルのarch / arm / bootに配置されます。 / dts /ディレクトリ(バージョン2.6より前では使用できません)。
    (3)デバイスツリーは、デバイス情報の言語を記述するために使用されるデータ構造です。具体的には、オペレーティングシステムのハードウェアを記述するために使用されるため、デバイスの情報をハードコーディングする必要はありません。
    (4)デバイスツリーは一連の名前付きノードとプロパティ構成され、ノード自体に子ノードを含めることができます。いわゆる属性は、実際にはペアで表示される名前と値です。
    (5)デバイスツリーソースファイルDTSはDTBバイナリファイルにコンパイルされるブートローダが実行されているときに、オペレーティングシステムに渡され、そしてオペレーティングシステム解析し、展開、(フラット化)これにより、ハードウェアデバイスのトポロジ図が生成されます。このトポロジ図を使用すると、プログラミングプロセス中にシステムによって提供されるインターフェイスを介して、デバイスツリーのノードおよび属性情報を直接取得できます。

デバイスツリーは実際にはファイルであり、このファイルには多くのノードが含まれています。これらのノードは、CPU情報、GPIO情報などのデバイス情報を記述するために使用されます。情報には多くの属性が含まれています。属性には、使用するためにカーネルに渡されるさまざまな値が含まれます。カーネルはこれらのファイル情報を解析して、プログラマーが使用できます。

Linuxデバイスツリーの起源-デバイスツリーがある理由

  • Linux 2.6では、arch / arm / plat-xxxとarch / arm / mach-xxxには多くのジャンクコードが含まれています。かなり多くのコードがボードレベルの詳細のみを記述しており、これらのボードレベルの詳細はカーネル専用です。ごみようなプラットフォーム装置、リソース、i2c_board_info、spi_board_infoとボード上のさまざまなハードウェアplatform_dataとして、このようなS3C2410、S3C6410などの一般的なボードレベルのディレクトリ、コードの量は、行の数万です。そのため、カーネルコードが非常に大きく、管理が面倒です。これをハードコーディングと呼びます。
    2.6カーネル記述デバイス構成は、arch / arm / plat-samsung / dev-i2c0.c
    ここに写真の説明を挿入
    arch / arm / mach-s5vp210 /mach-smdkv210.cに類似しています。
    ここに写真の説明を挿入

  • Linus Torvaldsは、この状況に激怒しました。2011年、ARM Linuxメーリングリストは、このARM全体がお尻のひどい痛みであると宣言しました。

  • そのため、Linux開発コミュニティは修正を開始し、デバイスツリーはPowerPCなどの他のアーキテクチャで最初に使用されました。ARMアーキテクチャ開発コミュニティは、デバイス情報を記述するためにデバイスツリーを使用し始めました

デバイスツリーをすばやくコンパイルする-DTC(デバイスツリーコンパイラ)

  • .dtsを.dtbにコンパイルするためのツール
  • DTCのソースコードはカーネルのscripts / dtcディレクトリにあります。Linuxカーネルでデバイスツリーが有効になっていると、カーネルのコンパイル時にホストツールdtcがコンパイルされます。
  • Linuxカーネルarch / arm / boot / dts / Makefileには、特定のsocが選択されたときにコンパイルされる.dtbファイルが記述されています。たとえば、EXYNOSに対応する.dtbには次のものが含まれます。
 dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \ 
 				exynos4210-sndkv310.dtb \
 				exynos4412-origen.dtb \
  • デバイスツリーファイルを個別にコンパイルできます。Linuxカーネルでmakedtbsを実行するときに、前にARCH_EXYNOSを選択すると、上記の.dtbは対応する.dtsによってコンパイルされます。

デバイスツリーをすばやく理解する—デバイスツリーファイルをコンパイルします

  • 原産地ボードのデバイスツリーファイルを参照してください
cp arch/arm/boot/dts/exynos4410-origen.dts  arch/arm/boot/dts/exynos4412-fs4412.dts
  • 新しいファイルを追加するには、Makefileを変更してコンパイルする必要があります
vim arch/arm/boot/dts/Makefile ,在 exynos4410-origen.dtb \ 下添加如下内容
exynos4412-fs4412.dtb
  • デバイスツリーファイルをコンパイルします
make dtbs
  • カーネルお​​よびデバイスツリーファイルを/ tftpbootディレクトリにコピーします
  • 起動パラメータを設定する
set bootcmd tftp 0x41000000 uImage \; tftp 0x42000000 exynos4410-fs4412.dtb \; bootm 0x41000000 - 0x42000000

dtbが使用するプロセス

ここに写真の説明を挿入

デバイスツリーの構文と内部構造

  • 部分名詞
DT: Device Tree
FDT: Flattened Device Tree
OF: Open Firmware
DTS: device tree source
DTSI: device tree source include
DTB: device tree blob
DTC: device tree compiler

デバイスツリーの構文

(1)ノード
(2)属性
(3)ルートノード
(4)互換性のある属性
(5)Reg属性
(6)#address-cellsおよび#address-siz属性
(7)割り込み情報属性-中断および中断

  • シンプルなデバイスツリーコンテンツ
/ {
	node1{
		a-string-property = "A string";
		a-string-list-property = "first string","second string";
		a-byte-data-property = [0x01 0x23 0x34 0x56];
		child-node1{
			first-child-property;
			second-child-property = <1>;
			a-string-property = "Hello,world";
			};
			child-node2{
				
			};		
	};
	
	node2{
		an-empty-property;
		a-cell-property = <1 2 3 4>;  /*each number (cell) is a uint32*/
		child-node1{
		};
	};
};
  • ノード
    ノード名:各ノードには、「<name> [@ <deviceaddress>]」の形式の名前を付ける必要があります。
    < name >は、31文字以下の単純なascii文字列です。ノードの名前は、その名前に基づいている必要があります。どんな機材かを反映しています。たとえば、3 comイーサネットアダプタのノードは、デバイスのメインアドレスにアクセスするために、3com509
    <デバイスアドレス>ではなくethernetという名前にする必要があります。また、アドレスはノードのreg属性にもリストされます。同じレベルのノード名は次のようになります。一意ですが、アドレスが異なる限り、複数のノードで同じ共通名を使用することもできます。もちろん、デバイスアドレスもオプションであり、オプションです。
    デバイスを表すツリー内のすべてのノードには、互換性のある属性が必要です
  • プロパティ
    ここに写真の説明を挿入
  • 共通属性—互換性のある属性
    ここに写真の説明を挿入
  • 共通の属性-#address-cellsおよび#size-cells
    ここに写真の説明を挿入
  • 共通属性-reg属性
    ここに写真の説明を挿入
  • 一般的な属性-割り込み情報の
    ここに写真の説明を挿入
    例:類似
/ {
	compatible = "acme,coyotes-revenge";
	#address-cells = <1>;
	#size-cells = <1>;
	interrupt-parent = <&intc>;
	
	serial@101f0000 {
		compatible = "arm,pl011";
		reg = <0x10170000,0x1000>;
		interrupts = <1 0>;
	};

	intc:interrupt-controller@10140000{
		compatible = "arm,pl190";
		reg = <0x10140000,0x1000>;
		interrupt-controller;
		#interrupt-cells = <2>;
	};
}

アームアーキテクチャの場合、記号は特定の意味ですDocumentation / devicetree / bindings / arm / gic.txt
ここに写真の説明を挿入

デバイスツリーの戦闘

  • 対応するボードに次の情報を追加します:(テストのみ)
  test_node@123456 {
            compatible = "farsight,test";
             reg = <0x114001E0 0x24
                    0x11400c20 0x24>;
            testprop,mytest;
            test_list_string = "read fish","blue fish";
            interrupt-parent = <&gpx1>;   //因为按键接到了 gpx1_1
            interrupts = <2 2>;
            //因为按键接到了gpx1_1,如果接到了gpx2_1,那么就是 <2 2>

    };  

make dtbsが
開発ボードにダウンロードされた後、/ proc / device_tree / device_treeに多くのノードが表示され、test_node @ 123456ノードのあるフォルダーが表示されます。

  • OF API OFによって提供される一般的に使用される機能は
    主に/ drivers / of /ディレクトリに集中しています。
    ここに写真の説明を挿入
  • デバイスツリープロセスプログラミングを分析する
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>


#define U32_DATA_LEN 4

static int is_good;
static int irqno;

irqreturn_t key_irq_handler(int irqno,void *devid)
{
    
    
	printk("key pressed\n");
	return IRQ_HANDLED;
}

static int __init dt_drv_init(void)
{
    
    
	/*
		 test_node@123456 {
            compatible = "farsight,test";
            reg = <0x12345678 0x24
                    0x87654321 0x24>;
            testprop,mytest;
            test_list_string = "read fish","blue fish";
    };  
	*/
	//在代码中获取节点的所有信息
	//先把节点获取到
	struct device_node *np = NULL;
	struct property *prop = NULL;
	
	np = of_find_node_by_path("/test_node@123456");
	if(np)
	{
    
    
		printk("find test node ok\n");
		printk("find name = %s\n",np->name);
		printk("find full_name = %s\n",np->full_name);
	}
	else
	{
    
    
		printk("find test node failed\n");
	}


	//获取到节点中的属性
	prop = of_find_property(np,"compatible",NULL);
	if(prop)
	{
    
    
		printk("find compatible ok\n");
		printk("compatible value = %s\n",prop->value);
		printk("compatible name = %s\n",prop->name);
	}
	else
	{
    
    
		printk("find compatible failed\n");
	}

	if(of_device_is_compatible(np,"farsight,test"))
	{
    
    
		printk("we have a compatible name called farsight,test\n");
	}
	else
	{
    
    
		printk("This compatible is none\n");
	}
	

	//获取到属性中整数的数组
	u32 regdata[U32_DATA_LEN];
	int ret;
	
	ret = of_property_read_u32_array(np,"reg",regdata,U32_DATA_LEN);
	if(!ret)
	{
    
    
		printk("get reg data succeed\n");
		int i;
		for(i = 0; i < U32_DATA_LEN; i++)
		{
    
    
			printk("regdata[%d] = 0x%x\n",i,regdata[i]);
		}
	}
	else
	{
    
    
		printk("get reg data failed\n");
	}


	//读取属性中的字符串数组
	const char *pstr[3];
	int i;
	for(i = 0; i < 3; i++)
	{
    
    
		ret = of_property_read_string_index(np,"test_list_string",i,&pstr[i]);
		
		if(!ret)
		{
    
    
			printk("pstr[%d] = %s\n",i,pstr[i]);
		}
		else
		{
    
    
			printk("get pstr data failed\n");
		}
	}


	//属性值为空,可以用于设置标志
	if(of_find_property(np," testprop,mytest",NULL))
	{
    
    
		is_good = 1;
		printk("is good = %d\n",is_good);
	}

	//获取到中断的号码
	irqno = irq_of_parse_and_map(np,0);
	printk("irqno = %d\n",irqno);
	
	//验证中断号码是否有效
	ret = request_irq(irqno,key_irq_handler,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"key_irq",NULL);
	if(ret)
	{
    
    
		printk("request_irq error\n");
		return -EBUSY;
	}

	
	
	return 0;
}

static void __exit dt_drv_exit(void)
{
    
    
	free_irq(irqno,NULL);
}

module_init(dt_drv_init);
module_exit(dt_drv_exit);
MODULE_LICENSE("GPL");

おすすめ

転載: blog.csdn.net/qq_41782149/article/details/101149210