Linux 总线、设备、驱动模型的探究

 学习交流加

  • 个人qq:1126137994
  •  个人微信:liu1126137994
  •  学习交流资源分享qq群:962535112

之前一直做项目,做项目的过程虽然也学习到了不少知识,但是,一直没有好好研究总线设备驱动的机制,今天来学习总结一下!

1、设备驱动模型的需求:

    总线,设备驱动程序,其实就是软件工程中的高内聚,低耦合!所谓高内聚低耦合是模块内各元素联系越紧密就代表内聚性就越高,模块间联系越不紧密就代表耦合性低。所以高内聚、低耦合强调的就是内部要紧紧抱团。设备和驱动就是基于这种模型去实现彼此隔离不相干的。

    那么linux内核为什么要采取这种高内聚,低耦合的特性来设计设备驱动呢?

我们拿DM9000网卡驱动程序来分析。DM9000网卡驱动程序是需要接在CPU的内部总线的,需要地址总线,控制总线,数据总线!以及pin管脚等!

那么我们就需要在DM9000网卡驱动里面定义基地址、中断号等!我们使用的DM9000网卡的基地址0x20000000+0x300=0x20000300,中断号是7。那么:

#define DM9000_BASE 0x20000300
#define DM9000_INTERRUPT 7int DM9000_send()
{writel(GITCHAT_BASE + REG, 1);...
}int DM9000_init()
{request_init(GITCHAT_INTERRUPT, ...);...
}0x20000300
#define DM9000_INTERRUPT 7int DM9000_send()
{writel(GITCHAT_BASE + REG, 1);...
}int DM9000_init()
{request_init(GITCHAT_INTERRUPT, ...);...
}

    但是世界上的板子千千万,有三星、华为、飞思卡尔……每个板子的信息也都不一样,站在驱动的角度看,当每次重新换板子的时候,DM9000_BASE 和DM9000_INTERRUPTR 不再一样,那驱动代码也要随之改变。这样的话一万个开发板要写一万个驱动了,这就是文章刚开始提到的高内聚、低耦合的应用场景。

    驱动想以不变应万变的姿态适配各种设备连接的话就要实现设备驱动模型。基本上我们可以认为驱动不会因为 CPU 的改变而改变,所以它应该是跨平台的。自然像 #define DM9000_BASE 0x20000300,#define DM9000_INTERRUPT 7”这样描述和 CPU 相关信息的代码不应该出现在驱动里。

 

 

 

2、设备驱动模型的实现:

    现在 CPU 板级信息和驱动分开的需求已经刻不容缓。但是基地址、中断号等板级信息始终和驱动是有一定联系的,因为驱动毕竟要取出基地址、中断号等。怎么取?有一种方法是 GITCHAT 驱动满世界去询问各个板子:请问你的基地址是多少?中断号是几?这仍然是一个耦合的情况!!!

    对软件工程熟悉的读者肯定立刻想到能不能设计一个类似接口适配器的类(adapter)去适配不同的板级信息,这样板子上的基地址、中断号等信息都在一个 adapter 里去维护,然后驱动通过这个 adapter 不同的 API 去获取对应的硬件信息。没错,Linux 内核里就是运用了这种设计思想去对设备和驱动进行适配隔离的,只不过在内核里我们不叫做适配层,而取名为总线,意为通过这个总线去把驱动和对应的设备绑定一起,如图:

 

 

基于这种设计思想,Linux 把设备驱动分为了总线、设备和驱动三个实体,这三个实体在内核里的职责分别如下:

 

    模型设计好后,下面来看一下具体驱动的实践,首先把板子的硬件信息填入设备端,然后让设备向总线注册,这样总线就间接的知道了设备的硬件信息。比如一个板子上有一个 DM9000,首先向总线注册:

/* lyy 以下为添加* The DM9000 has no eeprom, and it's MAC address is set by* the bootloader before starting the kernel.*//* DM9000AEP 10/100 ethernet controller */#define MACH_SMDK2440_DM9K_BASE (S3C2410_CS4 + 0x300)static struct resource smdk2440_dm9k_resource[] = {[0] = {.start = MACH_SMDK2440_DM9K_BASE,.end   = MACH_SMDK2440_DM9K_BASE + 3,.flags = IORESOURCE_MEM},[1] = {.start = MACH_SMDK2440_DM9K_BASE + 4,.end   = MACH_SMDK2440_DM9K_BASE + 7,.flags = IORESOURCE_MEM},[2] = {.start = IRQ_EINT7,.end   = IRQ_EINT7,.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,}
};static struct dm9000_plat_data smdk2440_dm9k_pdata = {.flags      = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),
};static struct platform_device smdk2440_device_eth = {.name       = "dm9000",.id     = -1,.num_resources  = ARRAY_SIZE(smdk2440_dm9k_resource),.resource   = smdk2440_dm9k_resource,.dev        = {.platform_data  = &smdk2440_dm9k_pdata,},
};/* lyy:以上为添加 */

在结构体smdk2440_devices中添加网卡成员:

static struct platform_device *smdk2440_devices[] __initdata = {&s3c_device_ohci,&s3c_device_lcd,&s3c_device_wdt,&s3c_device_i2c0,&s3c_device_iis,&smdk2440_device_eth, /* lyy:添加 */
};

在static void __init smdk2440_machine_init(void)函数中添加设备到bus总线:

static void __init smdk2440_machine_init(void)
{s3c24xx_fb_set_platdata(&smdk2440_fb_info);s3c_i2c0_set_platdata(NULL);platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));//lyy添加smdk_machine_init();
}

    现在 platform 总线上自然知道了板子上关于 DM9000网卡设备的硬件信息,一旦 DM9000 的驱动也被注册的话,总线就会把驱动和设备绑定起来,从而驱动就获得了基地址、中断号等板级信息。总线存在的目的就是把设备和对应的驱动绑定起来,让内核成为该是谁的就是谁的和谐世界,有点像我们生活中红娘的角色,把有缘人通过红线牵在一起。设备注册总线的代码例子看完了,下面看下驱动注册总线的代码示例:

static int __devinit
dm9000_probe(struct platform_device *pdev)
{}....        db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);....
}

    从代码中看到驱动是通过总线 API 接口 platform_get_resource 取得板级信息,这样驱动和设备之间就实现了高内聚、低耦合的设计,无论你设备怎么换,我驱动就可以岿然不动。

    设备向总线注册了板级信息,驱动也向总线注册了驱动模块,但总线是怎么做到驱动和设备匹配的呢?接下来就讲下设备和驱动是怎么通过总线进行“联姻”的。

    总线里有很多匹配方式,比如:

static int platform_match(struct device *dev, struct device_driver *drv)
{struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);/* Attempt an OF style match first */if (of_driver_match_device(dev, drv))return 1;/* Then try to match against the id table */if (pdrv->id_table)return platform_match_id(pdrv->id_table, pdev) != NULL;/* fall-back to driver name match */return (strcmp(pdev->name, drv->name) == 0);
}

    从上面可知 platform 总线下的设备和驱动是通过名字进行匹配的,先去匹配 platform_driver 中的 id_table 表中的各个名字与 platform_device->name 名字是否相同,如果相同则匹配。

 

3、设备驱动模型的改善:

 

相信通过上面的学习,相信对于设备、驱动通过总线来匹配的模型已经有所了解。如果写代码的话应该是下面结构图所示:

    最底层是不同板子的板级文件代码,中间层是内核的总线,最上层是对应的驱动,现在描述板级的代码已经和驱动解耦了,这也是 Linux 设备驱动模型最早的实现机制,但随着时代的发展,就像是人类的贪婪促进了社会的进步一样,开发人员对这种模型有了更高的要求,虽然驱动和设备解耦了,但是天下设备千千万,每次设备的需求改动都要去修改 board-xxx.c 设备文件的话,这样下去,有太多的板级文件需要维护。完美的 Linux 怎么会允许这样的事情存在,于是乎,设备树(DTS)就登向了历史舞台,下一篇内容将探讨设备树的实现原理和用法。

 

想一起探讨以及获得各种学习资源加我: 
qq:1126137994 
微信:liu1126137994 
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构与算法等技术问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423166.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二叉树的层序遍历详细讲解(附完整C++程序)

加qq1126137994 微信liu1126137994 一起学习更多技术 1、原理: 层序遍历所要解决的问题很好理解,就是按二叉树从上到下,从左到右依次打印每个节点中存储的数据。如下图: 按层序遍历的原则,打印顺序依次应该是&…

【C++深度剖析教程30】C++中抽象类和接口

加qq1126137994 微信:liu1126137994 1、面向对象中的抽象概念; 在现实中,需要知道具体的图形类型才能知道如何求面积,但是对于抽象的‘图形’,我们是没法求其面积的,而且对其求面积也是没有意义的: cl…

有效沟通力思维导图

有效沟通,适用于生活和工作中,甚至是孩子教育上面,分享一下;

数据结构思维导图

学习是一个不断渐进的过程,最近整理了一下数据结构思维导图,分享一下,后续更新中 总结:算法实际上属于,数据建模,首先是问题的抽象,采用数学公式来表示(数据建模:将问题…

(转)代理模式(Proxy)

原文地址:http://www.cnblogs.com/QinBaoBei/archive/2010/05/18/1737866.html 为了深刻点理解代理模式,我们先来看一个 Demo , 首先这个 Demo 是用来测试 QQ 号码是否在线, 这里涉及到的内容是 Web 服务的使用, 这个 …

【C++深度剖析教程31】被遗弃的多重继承

加qq1126137994 微信:liu1126137994 C中是否允许一个类继承多个父类? C支持编写多重继承的代码: 一个子类可以拥有多个父类子类拥有所有父类的成员变量子类继承父类所有的成员函数子类对象可以当做任意父类对象使用 多重继承的语法规则&a…

zookeeper思维导图

之前用word文档记笔记,但是没有思维导图清晰,又整理了一下,分享一下;

redis 思维导图

之前整理的redis 思维导图,分享一下,后续持续更新;

网站切图初学

先做一个简单的说明为什么选择Photoshop软件而没有选择Firework软件。Friework Dreamweaver Flash号称网页制作三剑客,Friework与Dreamwaver整合得更为紧密,在这里只所以选择photoshop只是因为我的偏好,况且我对 firework软件使用的次数很少&…

main函数的参数的含义

转载自:点击链接 链接2 加qq1126137994 微信:liu1126137994 一起学习更多技术!!! 最近学习服务器网络编程,遇到了一个问题,main函数的参数,特意整理资料记录之!&#…

【C++深度剖析教程35】函数模板的概念和意义

加qq1126137994 微信&#xff1a;liu1126137994 一起学习更多技术&#xff01;&#xff01;&#xff01; 1、问题引入&#xff1a; C中有几种变量交换的方法&#xff1f; 定义宏代码块和定义函数 #include <iostream> #include <string>using namespace std;#d…

【C++深度剖析教程36】深入理解函数模板

加qq1126137994 微信&#xff1a;liu1126137994 一起学习更多技术&#xff01;&#xff01;&#xff01; 1、函数模板深入理解 编译器从函数模板通过具体类型产生不同的函数编译器会对函数模板进行两次编译 *对模板进行编译 *对参数替换后的函数进行编译 注意事项&#xf…

【C++深度剖析教程37】类模板的概念和意义

加qq1126137994 微信&#xff1a;liu1126137994 一起学习更多技术&#xff01;&#xff01;&#xff01; 1、类模板 一些类主要用于存储和组织数据元素类中数据的组织方式和数据元素的具体类型无关如 数组类&#xff0c;链表类&#xff0c;stack类&#xff0c;queue类等 C中…

【C++深度剖析教程38】类模板深度剖析

加qq1126137994 微信&#xff1a;liu1126137994 一起学习更多技术&#xff01;&#xff01;&#xff01; 1、多参数类模板 类模板可以定义任意多个不同的类型参数 类模板可以被特化&#xff1a; 指定类模板的特定实现部分类型参数必须显示指定根据类型参数分开实现类模板…

dbeaver 连接hbase 数据库

1.安装dbeaver 参考: https://blog.csdn.net/volitationLong/article/details/80583977 安装dbeaver 2.配置hosts 文件 10.21.21.89 nn01.as 10.21.21.94 nn02.as 10.21.21.93 dn01.as 3.配置用户变量 4. 配置hbase 驱动 连接参数 点击“新建连接”按钮 4.1 选择:a…

docker思维导图

之前学习的总结的思维导图&#xff0c;后续持续更新

ODI配置Mysql5.1数据库服务器

场景&#xff1a;服务器端mysql装了是Mysql5.1版本&#xff0c;最开始使用mysql-connector-java-5.0.6-bin.jar 驱动&#xff0c;经测试&#xff0c;无法连接成功&#xff0c;后来再网络上下载了最新的驱动mysql-connector-java-5.1.12-bin.jar 发现还是没法连接成功。最后面使…

【Linux进程、线程、任务调度】一 Linux进程生命周期 僵尸进程的含义 停止状态与作业控制 内存泄漏的真实含义 task_struct以及task_struct之间的关系

学习交流加&#xff08;可免费帮忙下载CSDN资源&#xff09;&#xff1a;个人微信&#xff1a; liu1126137994学习交流资源分享qq群1&#xff08;已满&#xff09;&#xff1a; 962535112学习交流资源分享qq群2&#xff1a; 780902027 文章目录1、进程控制块PCB2、进程的生命周…

Ubuntu 8.04下Netbeans的字体反锯齿解决(转)

原帖地址&#xff1a;http://www.oklinux.cn/html/Basic/jyjq/20081011/62034.html 测试可用&#xff0c;网上搜的其他方法如复制字体文件等都不成功。 首先说明&#xff0c;我是被锯齿的Netbeans折磨了半年才下决心解决来解决这个问题&#xff0c; 想想还真是冤枉。 我用的Li…