字符驱动程序设计学习笔记4-2

来源:互联网 发布:电商源码 编辑:程序博客网 时间:2024/06/05 10:28

       在介绍IO内存资源的获取和使用之前,首先先介绍一下linux的platform机制。

        从Linux 2.6起引入了一套新的驱动管理和注册机制:platform机制,platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。

        Linux platform driver机制和传统的device driver 机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。

  platform_device结构体用来描述设备的名称、资源信息等。该结构被定义在include/linux/platform_device.h中,定义原型如下:

 

  struct platform_device {

     constchar * name;    //定义平台设备的名称

     int id;

     struct device dev;

     u32 num_resources;

     structresource * resource; //定义平台设备的资源。

  };

 

  下面来看一下platform_device结构体中最重要的一个成员struct resource * resource。设备的资源的描述信息存放在 resource 数据结构中, 相同的资源存放在一个树形数据结构中,通过父节点,兄弟节点,子节点相连。 比如中断资源, IO端口资源, IO内存资源, DMA资源有不同资源树。 struct resource被定义在include/linux/ioport.h中,定义原型如下:

 

  struct resource {

      resource_size_tstart;  //定义资源的起始地址

      resource_size_t end;  //定义资源的结束地址

      const char *name;    //定义资源的名称

      unsigned long flags; //定义资源的类型,比如MEM,IO,IRQ,DMA类型

        struct resource *parent, *sibling,*child;  //资源链表指针,资源树的父节点,兄弟节点,字节点指针

 

  };

 

  通过调用函数platform_add_devices()向系统中添加该设备了,该函数内部调用platform_device_register( )进行设备注册。要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行platform_driver_register()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。

  接下来来看platform_driver结构体的原型定义,在include/linux/platform_device.h中,代码如下:

 

  struct platform_driver {

       int (*probe)(structplatform_device *);

             int(*remove)(struct platform_device *);

             void(*shutdown)(struct platform_device *);

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

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

             int(*resume_early)(struct platform_device *);

             int(*resume)(struct platform_device *);

             structdevice_driver driver;

    };

  内核提供的platform_driver结构体的注册函数为platform_driver_register(),其原型定义在driver/base/platform.c文件中,具体实现代码如下:

 

  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;

       if (drv->suspend)

        drv->driver.suspend= platform_drv_suspend;

        if(drv->resume)

        drv->driver.resume= platform_drv_resume;

        returndriver_register(&drv->driver);

  }

       总结,通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独自的资源(地址总线和IRQs),都可以用platform_driver实现。如:LCD,网卡、USB、UART等,都可以用platfrom_driver写,而timer,irq等小系统之内的设备则最好不用platfrom_driver机制。

区别:

(1)platform_device_register和device_register的区别主要是有没有resource的区别,前者的结构体包含后面,并且增加了struct resource结构体成员,后者没有。platform_device_register在device_register的基础上增加了struct resource部分的注册。由此。可以看出,platform_device---paltform_driver_register机制与device-driver的主要区别就在于resource。前者适合于具有独立资源设备的描述,后者则不是。

其实linux的各种其他驱动机制的基础都是device_driver。只不过是增加了部分功能,适合于不同的应用场合。

platform_add_device是device_add的衍生

intplatform_device_add(struct platform_device *pdev)

{

int i, ret = 0;

 

if (!pdev)    /*验证指针的有效性 */

   return -EINVAL;

 

if(!pdev->dev.parent)/*都说总线有两个链表,一个是设备链表(通过device 内嵌)一个是驱动链表(通过device_driver内嵌)这里如果pdev->dev.parent为0,说明设备链表还没有设备,因此处理办法是将platform_bus作为设备链表的开始,一直感觉platform_bus和platform_bus_type很难区分,不过在这里清楚了platform_bus是一个设备,platform_bus_type才是真正的总线*/

pdev->dev.parent= &platform_bus;/*device 的父结点*/

 

pdev->dev.bus= &platform_bus_type;/*device 要挂接在platform_bus_type这个总线上拉,看到了,设备和总线是这么勾搭上滴,很直接,很干脆*/

if (pdev->id!= -1)

   snprintf(pdev->dev.bus_id, BUS_ID_SIZE,"%s.%d", pdev->name,

    pdev->id);/*这个如果看不懂,可以参考LINUX的格式化输出的相关资料*/

else

   strlcpy(pdev->dev.bus_id, pdev->name,BUS_ID_SIZE);

 

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

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

 

   if (r->name == NULL) /*name一般为NULL*/

    r->name = pdev->dev.bus_id; /*资源的名称赋值为pdev->dev.bus_id,如果一个platform_device有多个resource 则出现同名现象*/

 

   p = r->parent;

   if (!p) {  /*父资源为0,说明不是从一个大的资源里面切割出来的*/

    if (r->flags & IORESOURCE_MEM)

     p = &iomem_resource;

    else if (r->flags & IORESOURCE_IO)

     p = &ioport_resource;

   }

 

   if (p && insert_resource(p, r)) { /*如果从父资源里面切割失败,则进行如下处理*/

    printk(KERN_ERR

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

          pdev->dev.bus_id, i);

    ret = -EBUSY;

    goto failed;

   }

}

 

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

   pdev->dev.bus_id,pdev->dev.parent->bus_id);

 

ret =device_add(&pdev->dev);/*资源也分配好了,准备工作也做足,终于可以把设备添加到设备链表里面了*/

if (ret == 0)

   return ret;

 

failed: /*失败处理*/

while (--i>= 0)

   if (pdev->resource[i].flags &(IORESOURCE_MEM|IORESOURCE_IO))

    release_resource(&pdev->resource[i]);

return ret;

}

EXPORT_SYMBOL_GPL(platform_device_add);

 


0 0
原创粉丝点击