注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

学习笔记

正确的方法如同学习书法,开始的时候要临摹,临摹好了然后创造自己的风格。

 
 
 

日志

 
 

[Linux笔记]platform_device和platform_driver注册过程  

2011-07-03 20:09:48|  分类: Linux |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

       基于linux-2.6.24.4

       从Linux 2.6起引入了一套新的驱动管理和注册机制:Platform_device和Platform_driver。
       Linux中大部分的设备驱动,都可以使用这套机制, 设备用Platform_device表示,驱动用Platform_driver进行注册。
       platform是一个虚拟的地址总线,相比PCI、USB,它主要用于描述SOC上的片上资源。比如S3C2410上的集成控制器(LCD、Watchdog、RTC等),platform所描述的资源有一个共同点,在CPU的总线上直接取址。
       Linux platform device driver机制和传统的device driver机制(通过device_register driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。
       Platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。通过Platform机制开发发底层驱动的大致流程为:  定义 platform_add_devices注册 platform_device,定义 platform_add_driver注册 platform_driver。

1、platform_device注册过程:
       首先要确认的就是设备的资源信息,例如设备的地址,中断号等。
       在2.6内核中platform设备用结构体platform_device来描述,该结构体定义:
/***********************************include/linux/platform_device.h***********************************/
struct platform_device {
       const char    * name;
       int        id;
       struct device    dev;
       u32        num_resources;
       struct resource    * resource;
       struct platform_device_id    *id_entry;
};
       该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义:
/***********************************include/linux/platform_device.h***********************************/
struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    struct resource *parent, *sibling, *child;
};
       下面举s3c2410平台的i2c驱动作为例子来说明:
/***********************************arch/arm/mach-s3c2410/mach-smdk2410.c***********************************/
static struct platform_device *smdk2410_devices[] __initdata = {
       &s3c_device_usb,
       &s3c_device_lcd,
       &s3c_device_wdt,
       &s3c_device_i2c,
       &s3c_device_iis,
};
       把一个或几个设备资源放在一起,便于集中管理,其中IIC设备platform_device如下:
/***********************************arch/arm/plat-s3c24xx/devs.c***********************************/
static struct resource s3c_i2c_resource[] = {
       [0] = {
              .start = S3C24XX_PA_IIC,
              .end   = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
              .flags = IORESOURCE_MEM,
       },
       [1] = {
              .start = IRQ_IIC,
              .end   = IRQ_IIC,
              .flags = IORESOURCE_IRQ,
       }
};

struct platform_device s3c_device_i2c = {
       .name             = "s3c2410-i2c",
       .id               = -1,
       .num_resources    = ARRAY_SIZE(s3c_i2c_resource),
       .resource         = s3c_i2c_resource,
};
       这里定义了两组resource,它描述了一个I2C设备的资源,第1组描述了这个I2C设备所占用的总线地址范围,IORESOURCE_MEM表示第1组描述的是内存类型的资源信息,第2组描述了这个I2C设备的中断号,IORESOURCE_IRQ表示第2组描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。
       定义好了platform_device结构体后就可以调用函数platform_add_devices向系统中添加该设备了,之后可以调用platform_driver_register()进行设备注册。
       s3c2410-i2c的platform_device是在系统启动时,在mach-smdk2410.c里的smdk2410_machine_init()函数里进行注册的,这个函数申明为arch_initcall的函数调用,arch_initcall的优先级高于module_init。所以会在Platform驱动注册之前调用。(详细参考imach-smdk2410.c)
/***********************************arch/arm/mach-s3c2410/mach-smdk2410.c***********************************/
static void __init smdk2410_init(void)
{
       platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));          //添加多设备
       smdk_machine_init();
}

int platform_add_devices(struct platform_device **devs, int num)
{
       int i, ret = 0;

       for (i = 0; i < num; i++) {
              ret = platform_device_register(devs[i]);
              if (ret) {
                     while (--i >= 0)
                     platform_device_unregister(devs[i]);
                     break;
              }
       }
       return ret;
}

int platform_device_register(struct platform_device * pdev)
{
       device_initialize(&pdev->dev);
       return platform_device_add(pdev);
}

int platform_device_add(struct platform_device *pdev)
{
       int i, ret = 0;
       if (!pdev)
              return -EINVAL;
       if (!pdev->dev.parent)
              pdev->dev.parent = &platform_bus;
       pdev->dev.bus = &platform_bus_type;
       if (pdev->id != -1)
              snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name, pdev->id);      //如果有id 表示有多个同类设备用pdev->name和pdev->id标识该设备
       else
              strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);      //否则,只用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 (r->flags & IORESOURCE_MEM)
                            p = &iomem_resource;        //  作为IOMEM资源分配
                     else if (r->flags & IORESOURCE_IO)
                            p = &ioport_resource;       //  作为IOPORT资源分配
              }

              if (p && insert_resource(p, r)) {         //将新的resource插入内核resource tree
                     printk(KERN_ERR "%s: failed to claim resource %d\n", pdev->dev.bus_id, i);
                     ret = -EBUSY;
                     goto failed;
              }
       }

       pr_debug("Registering platform 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;
}

2、platform_driver注册过程:
       在platform_device注册完成后,就可以进行platform_driver注册了,在驱动初始化函数中调用函数platform_driver_register()注册platform_driver,需要注意的是s3c_device_i2c结构中name元素和s3c2410_i2c_driver结构中driver.name必须是相同的,这样在platform_driver_register()注册时会对所有已注册的所有platform_device中的name和当前注册的platform_driver的driver.name进行比较,只有找到相同的名称的platfomr_device才能注册成功。
/***********************************drivers/i2c/busses/i2c-s3c2410.c***********************************/
static struct platform_driver s3c2410_i2c_driver = {
       .probe          = s3c24xx_i2c_probe,
       .remove         = s3c24xx_i2c_remove,
       .resume         = s3c24xx_i2c_resume,
       .driver         = {
              .owner  = THIS_MODULE,
              .name   = "s3c2410-i2c",
       },
};
static int __init i2c_adap_s3c_init(void)
{
       return platform_driver_register(&s3c2410_i2c_driver);   //注册IIC驱动
}
/***********************************drivers/base/platform.c***********************************/
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;
       if (drv->suspend)
              drv->driver.suspend = platform_drv_suspend;
       if (drv->resume)
              drv->driver.resume = platform_drv_resume;
       return driver_register(&drv->driver);
}
/***********************************drivers/base/driver.c***********************************/
int driver_register(struct device_driver * drv)
{
       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_type methods\n", drv->name);
              }
       klist_init(&drv->klist_devices, NULL, NULL);
       return bus_add_driver(drv);        //添加驱动到总线上
}
/***********************************drivers/base/bus.c***********************************/
int bus_add_driver(struct device_driver *drv)
{
       struct bus_type * bus = bus_get(drv->bus);
       int error = 0;
      
       if (!bus)
              return -EINVAL;
       pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
       error = kobject_set_name(&drv->kobj, "%s", drv->name);
       if (error)
              goto out_put_bus;
       drv->kobj.kset = &bus->drivers;
       error = kobject_register(&drv->kobj);
       if (error)
              goto out_put_bus;

       if (drv->bus->drivers_autoprobe) {
              error = driver_attach(drv);
              if (error)
                     goto out_unregister;
       }
       klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
       module_add_driver(drv->owner, drv);

       error = driver_create_file(drv, &driver_attr_uevent);
       if (error) {
              printk(KERN_ERR "%s: uevent attr (%s) failed\n", __FUNCTION__, drv->name);
       }
       error = driver_add_attrs(bus, drv);
       if (error) {
       /* How the hell do we get out of this pickle? Give up */
              printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", __FUNCTION__, drv->name);
       }
       error = add_bind_files(drv);
       if (error) {
              /* Ditto */
              printk(KERN_ERR "%s: add_bind_files(%s) failed\n",  __FUNCTION__, drv->name);
       }

       return error;
out_unregister:
       kobject_unregister(&drv->kobj);
out_put_bus:
       bus_put(bus);
       return error;
}

参考资料:http://blog.chinaunix.net/space.php?uid=24789420&do=blog&cuid=2406147

  评论这张
 
阅读(1888)| 评论(1)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018