信盈达—您身边的嵌入式&人工智能专家
全国免费咨询热线:400-8788-909

嵌入式培训: Linux设备树详解

时间:2018-08-24 00:00:00 来源:信盈达 作者:信盈达

ARM Linux社区为什么要引入设备树

 

Linux之父Linus Torvalds闲来无事,在翻看ARM Linux代码的时候,有一天终于忍不住了。他在2011317日的ARM Linux邮件列表中说道:“This whole ARM thing is a f*cking pain in the ass”。这句话迫使ARM Linux社区引入了设备树。

 

Linus Torvalds为什么会发飙呢?ARM Linux社区的牛人为什么又乖乖地听话了?你得首先理解Linux设备驱动框架中一个非常好的设计:设备信息和驱动分离。

 

为了什么说明设备信息和驱动分离的概念,先来看一个简单的模拟代码实例:

 

【例-1】实现一个代码,把要使用的信息简单写死在代码中:

 

int add() /*模拟驱动代码*/

 

{

 

return 3+5; /*模拟设备信息*/

 

}

 

优点:简单

 

缺点:一旦加数和被加数发生变化就得改代码

 

改进设计如下:

 

【例-2】实现一个代码,把要使用的信息和操作代码分离开来:

 

struct dev{

 

int id;

 

int x;

 

int y;

 

}; /*模拟设备信息结构*/

 

strcut drv{

 

int id;

 

int (*add)(struct dev *info);

 

}; /*模拟驱动结构*/

 

int add(struct dev *info) /*模拟驱动代码*/

 

{

 

return info->x + info->y; /*模拟设备信息-通过参数传递进来*/

 

}

 

struct drv drv = {

 

.id = 1,

 

.add = add,

 

};

 

/*模拟设备信息*/

 

struct dev dev = {

 

.id = 1,

 

.x = 3,

 

.y = 5,

 

};

 

/*模拟总线初始化匹配设备信息和驱动代码*/

 

int bus()

 

{

 

if(dev.id == drv.id){

 

return drv.add(&dev);

 

}

 

...

 

}

 

优点:不管加数和被加数怎么变化,不需要修改代码,仅需要修改信息

 

缺点:结构比较复杂

 

那这个设备信息和驱动分离的设计跟驱动有什么关系呢?熟悉硬件编程的同学都知道,硬件一般的构成可以使用下图简单表述:

 

 

 

操作外设的驱动代码逻辑,只要硬件是一样的,就不会变化。但是外设挂到不同的主机上,可能会存在I/O地址的变化,如果有中断也是一样的,中断号也可能不同。这些I/O地址和中断号就是设备信息,使用这些信息来操作控制硬件的代码就是驱动。

 

如果采用【例-1】的设计方式,那么同一个硬件外设接到不同的主机,或是换了地址线/中断线,设备信息就变化了,得去修改驱动。但是采用【例-2】的方式进行设计,问题就迎刃而解:不管同样的外设硬件接到哪里或是那个平台,其驱动代码逻辑并不需要改动,而仅仅需要改变下设备信息,主要的就是I/O地址和中断号。

 

说了这么半天,跟引入设备树有什么关系呢?华清教学使用的开发板(A8/A9)都使用DM9000网卡芯片。DM9000驱动是开源的,在主线内核源码中就有。我们每次基于A8/A9板子移植的时候,DM9000驱动并没有修改过,仅仅是选配了下,主要的工作是在板级文件中添加了设备信息。DM9000驱动使用的是platform框架,所以添加了一份DM9000网卡芯片的platform_device信息。问题来了,如果使用C代码的形式来描述设备信息,则在内核源码中,将会有多份DM9000platform_device设备信息,造成了内核代码冗余。

 

解决这个问题的办法就是引入设备树,改造【例-2】来说明设备树的作用。

 

【例-3】实现一个代码,不仅把要使用的信息和操作代码分离开来,而且信息不是C代码编写的,而是文本配置文件保存的:

 

struct dev{

 

int id;

 

int x;

 

int y;

 

}; /*模拟设备信息结构*/

 

strcut drv{

 

int id;

 

int (*add)(struct dev *info);

 

}; /*模拟驱动结构*/

 

int add(struct dev *info) /*模拟驱动代码*/

 

{

 

return info->x + info->y; /*模拟设备信息-通过参数传递进来*/

 

}

 

struct drv drv = {

 

.id = 1,

 

.add = add,

 

};

 

/*模拟设备树-一个特殊的配置文件,xxx.dtbs的文本文件*/

 

/{

 

......

 

Dm9000{

 

x = 3;

 

y = 5;

 

};

 

......

 

};

 

/*模拟总线初始化匹配设备信息和驱动代码*/

 

int bus()

 

{

 

/*模拟设备树初始化处理*/

 

读文件(xxx.dtbs);

 

解析文件内容(根据设备树的规则来解析);

 

生成struct dev设备信息;

 

if(dev.id == drv.id){

 

return drv.add(&dev);

 

}

 

...

 

}

 

如果像【例-3】这样,就可以解决大量设备信息的代码冗余问题。

 

推而广之,系统的软硬件信息都可以使用设备树来描述。这样的话,ARM Linux社区就不会因为支持板子和驱动越来越多造成内核源码中出现很多冗余代码(主要是板级文件),仅仅需要移植者,把系统的软硬件信息通过设备树提供出来,选配一下内核代码,就可以了。

信盈达2008年在深圳特区南山高新科技园成立。自成立至今近九年来专注为企业和个人提供高端方案设计、高端嵌入式/Android培训等服务。公司下设信盈达实训学院、信盈达研发中心、信盈达教学仪器三大业务板块。九年来公司坚持"技术领先、服务领先",以雄厚的实力和专业的品质成为国内唯一有实力从产品最底层研发到系统层开发的嵌入式实训、产品解决方案提供商。为中国IT行业提供最具价值的职业教育服务。专业嵌入式、物联网、人工智能Java、单片机等课程,想了解更多信息点击立马咨询