Linux驱动模型分析之platform

Linux驱动模型分析之platform

概述

  1. 一个现实的Linux设备和驱动通常需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI等设备而言,这自然不是问题。但是在嵌入式系统里面,SOC系统中集成的独立的外设控制器,挂接在SOC内存空间的外设却不需要依附于此类总线。基于这一背景,Linux发明了一种虚拟总线,就是platform总线,相应设备称为platform_device,而驱动称为platform_driver。也就是说platform的设备是外设或者其控制器,就像EC外设一样,被申请为platform结构。

  2. 模型
    platform模型关心总线,设备和驱动3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动,相反的,系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。

platform总线

数据结构

  • 设备结构
struct device platform_bus =
{     
     .init_name = "platform",
};

platform也是一种设备,所以初始化一个device结构,设备名称platform,因为没有指定父设备,所以注册后将会在/sys/device/下出现一个platform目录。

  • 总线结构
struct bus_type platform_bus_type =
{
     .name = "platform",                    
     .dev_attrs = platform_dev_attrs,     //设备属性
     .match =platform_match,               //match函数,完成设备和驱动的匹配工作
     .uevent =platform_uevent,               //热插拔操作
     .pm = &platform_dev_pm_ops,
};

注册:

在系统中platform对应的文件是drivers/base/platform.c,他不是作为一个模块注册到内核的,关键的注册总线的函数由系统初始化部分,对应/init/main.c中的do_basic_setup函数间接调用。
- platform总线注册函数:__init platform_bus_init()

int __init platform_bus_init(void)
{
     int err;
     early_platform_cleanup();
     err = device_register(&platform_bus);//总线也是设备
     if(err)
          return err;
     err = bus_register(&platform_bus_type);//注册platform_bus_type总线到内核
     if(err)
          device_unregister(&platform_bus);
     return err;
}

这个函数向内核注册了一种总线,首先由driver_init函数调用,driver_init函数由do_basic_setup函数调用,do_basic_setup函数由kernel_init调用。
所以platform总线在内核初始化的时候就注册进了内核。

platform设备

数据结构

platform_device结构
struct platform_device
{
     const char *name;     //设备名
     int id;                         //设备编号
     struct device dev;
     u32 num_resource;     //设备使用资源数目
     struct resources *resource;     //设备使用资源
     const struct platform_device_id *id_entry;
     struct pdev_archdata archdata;
};

其中主要包含了device的属性。

主要成员:
- const char *name;
匹配用的name。

  • int id;
    设备id,用于给要插入该总线的具有相同name的设备编号,如果只有一个设备编号-1.

  • u32 num_resource;
    资源数

  • struct resource *resource;
    存放资源的数组

resource结构
struct resource
{
     resource_size_t start;     //资源起始地址
     resource_size_t end;     // 资源结束地址 
     const char *name;
     unsigned long flags;     //资源类型
     struct resource *parent, *sibling, *child;
};

该结构体主要是资源分配。

主要成员:
- resource_size_t start;
资源的起始地址,含义随着flags变化

  • resource_size_t end;
    资源的结束地址,含义随着flags变化

  • const char *name;
    匹配用的name,要和platform_device和platform_driver中的name一致。

  • unsigned long flags;
    资源类型标志位。可以为:IORESOURCE_IO,IORESOURCE_MEM,IORESOURCE_IRQ,IORESOURCE_DMA
    如当flags为IORESOURCE_MEM时,start和end分别表示该platform_device占据的内存的起始地址和结束地址;
    当flags为IORESOURCE_IRQ时,start和end分别表示该platform_device使用的中断号的起始值和结束值;如果只有一个中断号,开始和结束值相同。

注册

设备注册函数:platform_device_register
int platform_device_register(struct platform_device *pdev)
{     
     device_initialize(&pdev->dev);
     return platform_device_add(pdev);
}

这个函数首先初始化了platform_device的device结构,然后调用platform_device_add,这个是注册函数的关键,下面分析:

int platform_device_add(struct platform_device *pdev)
{
     inti, ret = 0;

     if(!pdev)
          {
               return -EINVALL;    
          }
     if(!pdev -> dev.parent)
          pdev -> dev.parent = &platform_bus;     //总线的设备结构:platform设备的父设备一般都是platform_bus

     pdev -> dev.bus = &platform_bus_type;     //总线的总线结构:挂到总线上

     if(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;                                   //分别赋予iomem_resource;  和ioport_resource; 
                         else if(resource_type(r) == IORESOURCE_IO)
                              p = &ioport_resource;
                    }

               if(p && insert_resource(p, r))          //调用insert_resource 插入资源
                    {
                         printk(KERN_ERR "%s:failed to claim resource %d\n", dev_name(&pdev->dev), 1);
                         ret = -EBUSY;
                         goto failed;
                    }
          }

     pr_debug("registering platform device '%s', parent at %s\n", dev_name(&pdev->dev), dev_name(pdev->dev.parent));

     ret = device_add(&pdev->dev);     //注册到设备模型中

     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);
               }

          return ret;    

}

platform驱动

数据结构

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 *);
     struct device_driver driver;
     const struct platform_device_id *id_table;
};

其中包含了probe()、remove()、shutdown()、suspend()、resume()等函数,这些函数需要驱动来实现。

主要成员:
- int (probe)(struct platform_device );
这是一个匹配函数,通过比较platform_driver和platform_device的name来进行匹配。还有就是会在这个函数中初始化一些和该驱动有关的初始化。

  • struct device_driver driver;
    在这个结构体中,主要实现const char *name和struct module *owner这两个成员。
    其中name就是用来匹配设备和驱动的。必须与device的name一致。
    owner表明该模块的拥有者,一般都是THIS_MODULE;

  • const struct platform_device_id *id_table;
    这个不需要必然实现的,但是这个实现是另外一种匹配设备和驱动的方法。通过id_table来实现匹配。但是最终还是通过名字来对应匹配,只是匹配的名字被列入一张表中,platform_device中的name和这个表中的每一个值进行比较,直到找到相同的那一个。主要是为了实现一个驱动对应多个设备的情况。设备之间的区别通过id_table.driver_data成员进行区分,从而在连接设备和驱动的时候可以针对设备区别,对加载的驱动程序进行调整。

注册

驱动注册函数:platform_driver_register
int platform_driver_register(struct platform_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;

          }

     return driver_register(&drv->driver);
}

这个函数首先使驱动属于platform_bus_type总线,将platform_driver结构中定义的probe,remove,shutdown赋值给device_driver结构中对应的成员,以供Linux设备模型核心调用。然后调用driver_register 将设备驱动注册到Linux设备模型核心中。

总线,设备,驱动的整合

使用函数:platform_match
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);


     if(pdrv->id_table)
          return platform_match_id(pdrv->id_table, pdev) != NULL;

     return (strcmp(pdev->name, drv->name) == 0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值