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,一经查实,立即删除!

相关文章

面试题:移动数组的元素

加qq 1126137994 一起学习更多技术!!! 描述 试设计一个算法,将数组a中的元素a[0]至a[n-1]循环右移k位,并要求只用一个元素大小的附加存储,元素移动或交换次数为O(n)。 输入 先输入一个大于1且小于100的…

n个字符串按照字典序排列

题目描述 给定n个字符串,请对n个字符串按照字典序排列。 输入描述: 输入第一行为一个正整数n(1≤n≤1000),下面n行为n个字符串(字符串长度≤100),字符串中只含有大小写字母。 输出描述: 数据输出n行,输出结果为按照字典序排列的字符串。 输入例子:…

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

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

不同DPI下窗体的自适应的有关注意点(转)

不同DPI下窗体的自适应的有关注意点(转自:http://hi.baidu.com/superkinger/blog/item/79e047ec6c80563526979197.html) 1. 在设计窗体的时候需要考虑是否允许程序在不同DPI自适应窗体,不自适应的优点是在不同的DPI下看到的…

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

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

有效沟通力思维导图

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

[转]Microsoft Solution Framework 微软解决方案框架结构

本文转自:http://baike.baidu.com/view/1291436.htmMSF(Microsoft Solution Framework)微软解决方案框架结构是一组建立、开发和实现分布式企业系统应用的工作模型、开发准则和应用指南。它帮助企业融合商业和技术的目标,降低采用…

指针的大小与什么有关

指针的大小与什么有关? cpu位数(32位数4字节,64位数8字节)操作系统位数(32位数4字节,64位数8字节)编译器的位数(32位数4字节,64位数8字节) 当上述3种位数不…

数据结构思维导图

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

(转)代理模式(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文档记笔记,但是没有思维导图清晰,又整理了一下,分享一下;

管理软件本质论

我们上管理软件到底是为了什么?1 为员工提供自动化工具,可以让他们节省出更多的时间可以做更多的工作?2 可以通过管理软件的互联网联网特性或局域网联网特性,让部门和部门之间、总部和分公司之间按业务流程通常运营?2 …

【C++深度剖析教程32】new/malloc区别 delete/free区别

加qq1126137994 微信:liu1126137994 一起学习更多技术!!! 1、new与malloc的区别: new是关键字,它是C语言的一部分,而malloc是由C库提供的函数new分配的内存的单位是具体的类型大小&#xff0c…

redis 思维导图

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

软件汉化教程

看到网上经常有人问汉化方面的东西,我今天也来灌水一篇,来个汉化扫盲教程。写的不好的地方欢迎大家指正!OK,现在我们进入正题。我这里所说的汉化,是指汉化 Windows 下的 PE 文件,把其他语言界面的程序翻译为…

图的DFS深度遍历

最近复习了一下图的内容,记录一下,后续添加详解(无向图的深度遍历) package com.qey.learn;import java.util.ArrayList; import java.util.Arrays;/*** ClassName graph* Description* Author qianxl* Date 2021-03-06 17:18* V…

【C++深度剖析教程33】C++中的构造函数与析构函数是否可以为虚函数

加qq1126137994 微信:liu1126137994 一起学习更多技术!!! 问题一:构造函数与析构函数可以成为虚函数么? 答案: 1、构造函数不可以成为虚函数 因为在构造函数执行结束后,虚函数表…

网站切图初学

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

【C++深度剖析教程34】C++中的强制类型转换dynamic_cast

加qq1126137994 微信:liu1126137994 一起学习更多技术!!! C中的继承中,如何使用强制类型转换? 知识点: dynamic_cast是与继承相关的类型转换关键字dynamic_cast要求相关的类中必须有虚函数dy…