1  概述

驱动程序实际上是硬件与应用程序之间的中间层。在Linux操作系统中,设备驱动程序对各种不同的设备提供了一致的訪问接口,把设备映射成一个特殊的设备文件,用户程序能够像其它文件一样对设备文件进行操作。

Linux2.6引入了新的设备管理机制kobject,通过这个数据结构使全部设备在底层都具有统一的接口,kobject提供主要的对象管理,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密联系,每一个在内核中注冊的kobject对象都相应于sysfs文件系统中的一个文件夹。

在这些内核对象机制的基础上,Linux的设备模型包含设备结构device、驱动程序driver、总线bus和设备类结构class几个关键组件。

一个现实的linux设备和驱动通常都须要挂接在一种总线上,比較常见的总线有USB、PCI总线等。可是,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附于此类总线。基于这种背景下,2.6内核增加了platform虚拟总线。Platform机制将设备本身的资源注冊进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性。

Platform设备概念的引入是可以更好地描写叙述设备的资源信息。Platform设备是系统中自治的实体,包含基于port的设备、外围总线和集成入片上系统平台的大多数控制器,它们通常直接通过CPU的总线寻址。每一个platform设备被赋予一个名称,并分配一定数量的资源。

Platform总线对增加到该总线的设备和驱动分别封装了结构体——platform_device和platform_driver而且提供了相应的注冊函数。

Platform虚拟总线

由上图可知,在platform虚拟总线上我们分别对device和driver进行注冊,这样我们可以更加方便的进行驱动设备的管理。这样当有总线或者设备注冊到该虚拟总线上时,内核自己主动的调用platform_match函数将platform_device绑定到platform_driver上。

2  platform数据结构

Linux在启动的时候就注冊了platform总线,看内核源代码:

struct bus_type platform_bus_type = {

.name             ="platform",

.dev_attrs       = platform_dev_attrs,

.match            =platform_match,

.uevent           =platform_uevent,

.pm         =&platform_dev_pm_ops,

};

能够看到,总线中定义了.match函数,当有总线或者设备注冊到platform总线时,内核自己主动调用.match函数,推断device和driver的name是否一致。

platform_device的结构体定义例如以下:

struct platform_device {

constchar       * name;//设备名字,这将取代device->dev_id,用作sys/device下显示文件夹名

int          id;//设备ID,用于给插入该总线而且具有同样name的设备编号,假设仅仅有一个设备的话填-1

structdevice   dev;//结构体中内嵌的device结构体

u32         num_resources;//资源数

structresource * resource;//用于存放资源的数组

conststruct platform_device_id     *id_entry;

/*arch specific additions */

structpdev_archdata      archdata;

};

能够看出,在platform_device中定义了name,而且内嵌了structdevice结构体。另外,包括的structresource例如以下:

struct resource {

resource_size_tstart;

resource_size_tend;

constchar *name;

unsignedlong flags;

structresource *parent, *sibling, *child;

};

platform_driver的结构体定义例如以下:

struct platform_driver {

int(*probe)(struct platform_device *);

int(*remove)(struct platform_device *);

void(*shutdown)(struct platform_device *);

int(*suspend)(struct platform_device *, pm_message_t state);

int(*resume)(struct platform_device *);

structdevice_driver driver;

conststruct platform_device_id *id_table;

};

能够看出,在platform_driver中内嵌了structdevice_driver,以及一些回调函数。structdevice_driver的详细定义例如以下:

struct device_driver {

constchar              *name;

int(*probe) (struct device *dev);

int(*remove) (struct device *dev);

void(*shutdown) (struct device *dev);

int(*suspend) (struct device *dev, pm_message_t state);

int(*resume) (struct device *dev);

conststruct attribute_group **groups;

conststruct dev_pm_ops *pm;

structdriver_private *p;

};

此处定义了name用于和platform_device匹配。由于platform_device和platform_driver的匹配就是通过内嵌的structdevice和structdevice_driver的name进行匹配的。

static int platform_match(struct device *dev,struct device_driver *drv)

{

structplatform_device *pdev = to_platform_device(dev);

structplatform_driver *pdrv = to_platform_driver(drv);

/*match against the id table first */

if(pdrv->id_table)

returnplatform_match_id(pdrv->id_table, pdev) != NULL;

/*fall-back to driver name match */

return(strcmp(pdev->name, drv->name) == 0);

}

3  platform device

3.1  register

注冊一个platform层的设备。注冊后,会在sys/device文件夹下创建一个以name命名的文件夹,而且创建软连接到/sys/bus/platform/device下。

int platform_device_register(structplatform_device *pdev)

{

device_initialize(&pdev->dev);

returnplatform_device_add(pdev);

}

当中,第一步device_initialize(在kernel目录下)用于初始化一个structdevice。函数的定义例如以下:

void device_initialize(struct device *dev)

{

dev->kobj.kset= devices_kset;

kobject_init(&dev->kobj,&device_ktype);

INIT_LIST_HEAD(&dev->dma_pools);

mutex_init(&dev->mutex);

lockdep_set_novalidate_class(&dev->mutex);

spin_lock_init(&dev->devres_lock);

INIT_LIST_HEAD(&dev->devres_head);

device_pm_init(dev);

set_dev_node(dev,-1);

}

第二步,加入一个platformdevice到device层 。函数的定义例如以下:

int platform_device_add(struct platform_device*pdev)

{

inti, ret = 0;

if(!pdev)

return -EINVAL;

if(!pdev->dev.parent)

pdev->dev.parent= &platform_bus;//假设p->dev.parent不存在则赋值&platform_bus

pdev->dev.bus= &platform_bus_type;//设置pdev->dev.bus的bus类型

if(pdev->id != -1)    //pdev->id!=-1说明存在多于一个的设备

dev_set_name(&pdev->dev,"%s.%d", pdev->name, pdev->id);

Else   //否则对唯一的设备进行命名

dev_set_name(&pdev->dev,"%s", pdev->name);

for(i = 0; i < pdev->num_resources; i++) {

//遍历资源而且资源增加到资源数组中

struct resource *p, *r =&pdev->resource[i];

if (r->name == NULL)

r->name= dev_name(&pdev->dev);

p = r->parent;

if (!p) {

if(resource_type(r) == IORESOURCE_MEM)

p = &iomem_resource;

elseif (resource_type(r) == IORESOURCE_IO)

p = &ioport_resource;

}

if (p && insert_resource(p, r)) {

printk(KERN_ERR

"%s: failed to claim resource%d\n",

dev_name(&pdev->dev), i);

ret= -EBUSY;

gotofailed;

}

}

pr_debug("Registeringplatform device '%s'. Parent at %s\n",

dev_name(&pdev->dev),dev_name(pdev->dev.parent));

ret= device_add(&pdev->dev);//(在core/core.c中定义)

if(ret == 0)

return ret;

failed:

while(--i >= 0) {

struct resource *r =&pdev->resource[i];

unsigned long type = resource_type(r);

if (type == IORESOURCE_MEM || type ==IORESOURCE_IO)

release_resource(r);

}

returnret;

}

3.2   unregister

platform_device_unregister用于注销一个platform-leveldevice。函数的定义例如以下:

void platform_device_unregister(structplatform_device *pdev)

{

platform_device_del(pdev);

platform_device_put(pdev);

}

在注销一个设备时,第一步调用platform_device_del函数。此函数的定义例如以下:

void platform_device_del(structplatform_device *pdev)

{

inti;

if(pdev) {

device_del(&pdev->dev);

//遍历全部的resource假设是IORESOURCE_MEM或者IORESOURCE_IO类型则调用release_resource函数释放掉resource。

for (i = 0; i <pdev->num_resources; i++) {

structresource *r = &pdev->resource[i];

unsignedlong type = resource_type(r);

if (type == IORESOURCE_MEM || type== IORESOURCE_IO)

release_resource(r);

}

}

}

此函数的作用是:移除一个platform-leveldevice。当中的IOSOURCE_MEM和IORESOURCE_IR是CPU对外设I/O端口物理地址的两种编制方式。Resource是一个指向platform资源数组的指针,该数组有num_resource个资源,以下是资源结构体的定义(linux/ioport.h):

struct resource {

resource_size_tstart;  //起始地址

resource_size_tend;   //终止地址

constchar *name;     //名称

unsignedlong flags;    //标志

structresource *parent, *sibling, *child;

};

在structplatform_device中能够设置多种资源信息。资源的flags标志包含:

#define IORESOURCE_IO      0x00000100   //IO资源

#define IORESOURCE_MEM   0x00000200   //内存资源

#define IORESOURCE_IRQ    0x00000400   //中断资源

#define IORESOURCE_DMA   0x00000800   //DMA资源

第二步,调用platform_device_put函数。

void platform_device_put(structplatform_device *pdev)

{

if(pdev)

put_device(&pdev->dev);

}

销毁一个platformdevice,而且释放全部与这个platformdevice相关的内存。

4  platform driver

4.1  register

通过调用函数platform_driver_register实现为platformlevel的设备注冊一个驱动。注冊成功后,内核会在/sys/bus/platform/driver/文件夹下创建一个名字为driver->name的文件夹。详细的函数定义例如以下:

int platform_driver_register(structplatform_driver *drv)

{

drv->driver.bus= &platform_bus_type;

if(drv->probe)

drv->driver.probe =platform_drv_probe;

if(drv->remove)

drv->driver.remove =platform_drv_remove;

if(drv->shutdown)

drv->driver.shutdown =platform_drv_shutdown;

returndriver_register(&drv->driver);

}

此函数首先对struct  platform_driver变量的driver进行赋值。然后调用driver_register而且返回。driver_register函数的定义例如以下:

int driver_register(struct device_driver *drv)

{

intret;

structdevice_driver *other;

BUG_ON(!drv->bus->p);

//假设driver的方法和总线上的方法不能匹配则驱动的名称须要更新

if((drv->bus->probe && drv->probe) ||

(drv->bus->remove &&drv->remove) ||

(drv->bus->shutdown &&drv->shutdown))

printk(KERN_WARNING "Driver '%s'needs updating - please use "

"bus_typemethods\n", drv->name);

other= driver_find(drv->name, drv->bus);

if(other) {

put_driver(other);

printk(KERN_ERR "Error: Driver '%s'is already registered, "

"aborting...\n",drv->name);

return -EBUSY;

}

ret= bus_add_driver(drv);//将驱动载入到总线上,假设成功就返回

if(ret)

return ret;

ret= driver_add_groups(drv, drv->groups);

if(ret)

bus_remove_driver(drv);

returnret;

}

4.2  unregister

通过调用函数platform_driver_unregister函数实现platform_driver级设备的注销。

void platform_driver_unregister(structplatform_driver *drv)

{

driver_unregister(&drv->driver);

}

在此函数中调用了driver_unregister函数,将驱动从系统中移除。函数的详细定义例如以下:

void driver_unregister(struct device_driver*drv)

{

if(!drv || !drv->p) {

WARN(1, "Unexpected driverunregister!\n");

return;

}

driver_remove_groups(drv,drv->groups);

bus_remove_driver(drv);

}

分两步走,第一步调用driver_remove_groups。函数的详细定义例如以下:

static void driver_remove_groups(structdevice_driver *drv,

const struct attribute_group **groups)

{

inti;

if(groups)

for (i = 0; groups[i]; i++)

sysfs_remove_group(&drv->p->kobj,groups[i]);

}

当中,调用了

static inline void sysfs_remove_group(struct kobject *kobj,

const struct attribute_group *grp)

{

}

第二步从总线中将驱动删除。调用例如以下函数:

void bus_remove_driver(struct device_driver *drv)

{

if (!drv->bus)

return;

if(!drv->suppress_bind_attrs)

remove_bind_files(drv);

driver_remove_attrs(drv->bus,drv);

driver_remove_file(drv,&driver_attr_uevent);

klist_remove(&drv->p->knode_bus);

pr_debug("bus: '%s':remove driver %s\n", drv->bus->name, drv->name);

driver_detach(drv);

module_remove_driver(drv);

kobject_put(&drv->p->kobj);

bus_put(drv->bus);

}

此函数的作用是:将驱动从它控制的设备中卸载,而且从驱动的总线链表中将它移除。

两年前实习时的文档——Platform学习总结的更多相关文章

  1. 两年前实习时的文档——MMC学习总结

    1概述 驱动程序实际上是硬件与应用程序之间的中间层.在Linux操作系统中,设备驱动程序对各种不同的设备提供了一致的訪问接口,把设备映射成一个特殊的设备文件,用户程序能够像其它文件一样对设备文件进行操 ...

  2. webpack搭建vue项目开发环境【文档向学习】

    为何有这篇文章 各个社区已经有无数篇帖子介绍如何使用webpack搭建前端项目,但无论是出于学习webpack的目的还是为了解决工作实际需要都面临着一个现实问题,那就是版本更新.别人的帖子可能刚写好版 ...

  3. 使用Git Wiki 管理文档时,文档编写的基本用法

    自己初次接触GitLab,通过百度和自己查找资料,了解了一部分.在自己的工作中,主要用到GitLab的Wiki文档版本管理能力.我总结了一小部分文本编辑需要用到的东西. 一.文本的排版 为了让文本/文 ...

  4. [知识积累]python3使用xlwt时写入文档字体颜色和边框样式

    可借鉴的网址:https://www.programcreek.com/python/example/39979/xlwt.Alignment 可以直接通过pip安装xlwt 个人理解: xlwt中对 ...

  5. 如何在使用itext生成pdf文档时给文档添加背景图片

    这个问题我在网上搜了很久,没有找到什么解决方案,需求其实很简单,就是添加背景图片.在解决这个问题之前,我们需要了解什么是背景图片?背景图片就是位于文档最底层的图片,文字和其他内容可以浮在它的上面.这又 ...

  6. C# 动态生成word文档 [C#学习笔记3]关于Main(string[ ] args)中args命令行参数 实现DataTables搜索框查询结果高亮显示 二维码神器QRCoder Asp.net MVC 中 CodeFirst 开发模式实例

    C# 动态生成word文档 本文以一个简单的小例子,简述利用C#语言开发word表格相关的知识,仅供学习分享使用,如有不足之处,还请指正. 在工程中引用word的动态库 在项目中,点击项目名称右键-- ...

  7. Unity shader 官网文档全方位学习(一)

    转载:https://my.oschina.net/u/138823/blog/181131 摘要: 这篇文章主要介绍Surface Shaders基础及Examples详尽解析 What?? Sha ...

  8. bootstrap2文档的学习

    就像刚开始的 优雅,直观,强大的前端框架,让web开发更快,更容易,bootstrap给我的感觉就是把常用的布局,组件(导航,列表,按钮,表格),还有规范化颜色等等,同时它的遍历不至于此,他还支持了自 ...

  9. bash帮助文档简单学习;bash手册翻译

    关于bash的四种工作方式的不同,可以参考:http://feihu.me/blog/2014/env-problem-when-ssh-executing-command-on-remote/,但是 ...

随机推荐

  1. PreferenceFragment 使用 小结

    Perference也就是我们常说的偏好设置,首选项设置,能够自己主动保存一些数据,比如我们在上一次使用的时候的一些内容,则在下一次启动后依旧生效,而不须要再进行配置.当用户改变设置时,系统就会更新S ...

  2. LoadRunner性能测试中Controller场景创建需注意的几点

    在LR工具做性能测试中,最关键的一步是Controller场景的设计,因为场景的设计与测试用例的设计相关联,而测试用例的执行,直接影响最终的测试结果是怎么的,因此,我们每设计一种场景,就有可能是一个测 ...

  3. Linux常见目录作用

    Linux中一切皆文件 文件类型: 一般文件 - 目录文件 d 链接文件 l 块设备   b    (以块为单位进行操作,比如硬盘) 字符设备 c  (以字符为单位进行操作,比如主存) socket  ...

  4. php各种编译错误汇总

    PHP编译安装时常见错误解决办法,php编译常见错误 This article is post on https://coderwall.com/p/ggmpfa configure: error: ...

  5. session之退出登陆

    <span style="font-size:32px;">//使用SESSION必须先开启session session_start(); //彻底删除session ...

  6. java 包之 BeanUtils包的使用

    BeanUtils工具包是由Apache公司所开发,主要是方便程序员对Bean类能够进行简便的操作. 在使用BeanUtils工具包之前我们需要的Jar包有以下几种: (1)   BeanUtils相 ...

  7. G - 密码 kmp、贪心、manachar等等等等

    G - 密码 Time Limit:1000MS     Memory Limit:65535KB     64bit IO Format:%lld & %llu Submit Status ...

  8. Codeforces Round #194 (Div. 2) D. Chips

    D. Chips time limit per test:1 second memory limit per test:256 megabytes input:standard input outpu ...

  9. 已知要闪回的大致时间使用基于as of scn的闪回查询

    基本判断出要恢复误操作的dml的时间可以使用如下的方法进行数据的恢复: example: 一.创建test表 -------create table flashback_asof------ crea ...

  10. C++中的类和对象(二)

    一,对象的动态建立和释放 1.什么是对象的动态建立和释放 通常我们创建的对象都是由C++编译器为我们在栈内存中创建的,我们无法对其进行生命周期的管理.所以我们需要动态的去建立该对象,因此我们需要在堆内 ...