記事ディレクトリ
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");