platform模型

360阅读 0评论2016-10-06 诺亚方舟破土巴郎
分类:LINUX


一。平台设备模型(platform)
2.6内核开始引入了一套新的设备驱动模型。平台设备(platform_device)和平台驱动(platform_driver)。平台设备与一般的device,driver模型区别是,平台设备将设备本身的资源注册进内核,可以由内核统一管理。
在文件include\linux\platform_device.h中,定义platform_device结构体。

点击(此处)折叠或打开

  1. struct platform_device {
  2.     const char    * name;    //平台设备名称
  3.     u32        id;                //驱动绑定,一般为-1
  4.     struct device    dev;        //device设备
  5.     u32        num_resources;    //引用的资源数
  6.     struct resource    * resource;    //引用的资源
  7. };
platform_drvier也在同样的文件中定义。

点击(此处)折叠或打开

  1. struct platform_driver {
  2.     int (*probe)(struct platform_device *);    //设备和驱动匹配时将执行此函数
  3.     int (*remove)(struct platform_device *); //移除时执行的函数
  4.     void (*shutdown)(struct platform_device *);
  5.     int (*suspend)(struct platform_device *, pm_message_t state);
  6.     int (*resume)(struct platform_device *);
  7.     struct device_driver driver;    //device_driver
  8. };
系统初始化时,会生成好平台设备总线等,建立平台设备模型 .

点击(此处)折叠或打开

  1. int __init platform_bus_init(void)
  2. {
  3.     int error;

  4.     early_platform_cleanup(); //clean up early platform code

  5.     error = device_register(&platform_bus);    //注册platform_bus 设备 (1)
  6.     if (error)
  7.         return error;
  8.     error = bus_register(&platform_bus_type);    //注册平台设备总线 (2)
  9.     if (error)
  10.         device_unregister(&platform_bus);
  11.     return error;
  12. }
(1)platform_bus 的定义,实际的总线也是一个设备

点击(此处)折叠或打开

  1. struct device platform_bus = {
  2.     .init_name    = "platform",
  3. };
这个名为platform的设备添加后,/sys/device/plaform将出现
(2)platform_bus_type定义 这是platform总线类型

点击(此处)折叠或打开

  1. struct bus_type platform_bus_type = {
  2.     .name        = "platform",
  3.     .dev_attrs    = platform_dev_attrs,
  4.     .match        = platform_match,
  5.     .uevent        = platform_uevent,
  6.     .pm        = &platform_dev_pm_ops,
  7. };

平台设备的资源
平台设备的一个特点就是将此平台设备所用到的资源,独立出来,注册进内核。这样可方便的管理资源,使资源可以和驱动主体分离,便于修改移植等。资源文件一般都定义在板级的文件中。驱动可以通过函数来获取。
资源结构resource的定义:

点击(此处)折叠或打开

  1. struct resource {
  2.     resource_size_t start;    //资源的开始地址
  3.     resource_size_t end;        //资源的结束地址
  4.     const char *name;                //资源的名称
  5.     unsigned long flags;        //资源的标志 eg,IORESOURCE_IO(IO端口),IORESOURCE_MEM(内存资源)IORESOURCE_IRQ(中断)
  6.     struct resource *parent, *sibling, *child;    //资源关系结构
  7. };

我们这里看一个资源的例子,s3c的RTC时钟模块是一个平台设备,其资源定义如下(arch/arm/plat-s3c24xx/devs.c).
s3c_rtc_resource是一个资源数组。

点击(此处)折叠或打开

  1. static struct resource s3c_rtc_resource[] = {
  2.     //RTC IO内存资源
  3.     [0] = {
  4.         .start = S3C24XX_PA_RTC,    //IO内存的开始地址。(RTC寄存器地址)
  5.         .end = S3C24XX_PA_RTC + 0xff,    //IO内存的结束地址
  6.         .flags = IORESOURCE_MEM,    //设置这个资源的类型是IO内存资源
  7.     },
  8.     //RTC 使用的中断资源
  9.     [1] = {
  10.         .start = IRQ_RTC,    //RTC滴答所使用的中断源号为46
  11.         .end = IRQ_RTC,
  12.         .flags = IORESOURCE_IRQ, //资源类型为中断
  13.     },
  14.     //
  15.     [2] = {
  16.         .start = IRQ_TICK,    //RTC报警所使用的中断号
  17.         .end = IRQ_TICK,
  18.         .flags = IORESOURCE_IRQ //资源类型为中断
  19.     }
  20. };
这些资源的设备的关联会在定义设备时关联,如

点击(此处)折叠或打开

  1. struct platform_device s3c_device_rtc = {
  2.     .name         = "s3c2410-rtc",
  3.     .id         = -1,
  4.     .num_resources     = ARRAY_SIZE(s3c_rtc_resource),
  5.     .resource     = s3c_rtc_resource,    //设备的资源为s3c_rtc_resource
  6. };


二、注册平台设备
平台设备的注册函数会在相应的板级文件中初始化部分被调用到,例如mini2440的板级文件arch/arm/math-s3c2440/math-mini2440.c中mini2440_init()函数中一句platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
mini2440_devices变量就是保存mimi2440所有平台设备的数组,

点击(此处)折叠或打开

  1. static struct platform_device *mini2440_devices[] __initdata = {
  2.     &s3c_device_usb,
  3.     &s3c_device_wdt,
  4.     ...
  5.     ...

平台设备注册函数platform_add_devices,此函数会在板级文件初始化函数(_init函数)中调用如mini2440_init。

点击(此处)折叠或打开

  1. /**
  2.  * platform_add_devices - add a numbers of platform devices 添加一组平台设备
  3.  * @devs: array of platform devices to add    平台设备的数组指针
  4.  * @num: number of platform devices in array 数组中平台设备的个数
  5.  */
  6. int platform_add_devices(struct platform_device **devs, int num)
  7. {
  8.     int i, ret = 0;

  9.     for (i = 0; i < num; i++) {
  10.         ret = platform_device_register(devs[i]);    //注册一个平台设备
  11.         if (ret) {
  12.             while (--i >= 0)
  13.                 platform_device_unregister(devs[i]); //注册未成功则注销这个平台设备
  14.             break;
  15.         }
  16.     }

  17.     return ret;
  18. }

点击(此处)折叠或打开

  1. /**
  2.  * platform_device_register - add a platform-level device 添加一个平台设备
  3.  * @pdev: platform device we're adding
  4.  */
  5. int platform_device_register(struct platform_device *pdev)
  6. {
  7.     device_initialize(&pdev->dev);    //初始化平台设备(平台设备结构中的device结构)
  8.     return platform_device_add(pdev);    //添加这个平台设备
  9. }
  10. /**
  11.  * platform_device_add - add a platform device to device hierarchy 添加平台设备到设备树中
  12.  * @pdev: platform device we're adding
  13.  *
  14.  * This is part 2 of platform_device_register(), though may be called
  15.  * separately _iff_ pdev was allocated by platform_device_alloc().
  16.  */
  17. int platform_device_add(struct platform_device *pdev)
  18. {
  19.     int i, ret = 0;

  20.     if (!pdev)
  21.         return -EINVAL;

  22.     if (!pdev->dev.parent)    //设备还没有设置父设备,则设置为platform_bus设备
  23.         pdev->dev.parent = &platform_bus;

  24.     pdev->dev.bus = &platform_bus_type;    //设置设备总线类型为平台总线类型

  25.     if (pdev->id != -1)
  26.         dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
  27.     else    //一般均为-1
  28.         dev_set_name(&pdev->dev, "%s", pdev->name);
  29.         //设置设备名称(实现语句:kobject_set_name_vargs(&dev->kobj, fmt, vargs);)

  30.     for (i = 0; i < pdev->num_resources; i++) {
  31.         struct resource *p, *r = &pdev->resource[i];

  32.         if (r->name == NULL) //如果资源没有名称,则设置为设备的名称
  33.             r->name = dev_name(&pdev->dev);

  34.         p = r->parent;        //父资源
  35.         if (!p) {    //没有父资源则
  36.             if (resource_type(r) == IORESOURCE_MEM)
  37.                 p = &iomem_resource;    //如果资源的类型是内存资源,则将其父资源指向iomem_resource资源
  38.             else if (resource_type(r) == IORESOURCE_IO)
  39.                 p = &ioport_resource; //如果资源的类型是IO资源,则将其父资源指向ioport_resource资源
  40.         }

  41.         if (p && insert_resource(p, r)) {
  42.             printk(KERN_ERR
  43.              "%s: failed to claim resource %d\n",
  44.              dev_name(&pdev->dev), i);
  45.             ret = -EBUSY;
  46.             goto failed;
  47.         }
  48.     }

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

  51.     ret = device_add(&pdev->dev);    //将设备添加入系统
  52.     if (ret == 0)
  53.         return ret;

  54.  failed:
  55.     while (--i >= 0) {
  56.         struct resource *r = &pdev->resource[i];
  57.         unsigned long type = resource_type(r);

  58.         if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
  59.             release_resource(r);
  60.     }

  61.     return ret;
  62. }
我们看一下在platform核心层的这个函数主要做了什么
设置了驱动的父设备(指向platform_bus总线设备)
设置了总线类型(platform_bus_type总线)
设置设备名称
设置了资源
最后注册进设备模型核心(device_add)



三、注册平台设备驱动
使用函数platform_driver_register(),函数在drivers/base/platfrom.c中,此函数一般在platform的驱动程序文件中的入口函数被调用,在驱动程序中一般会定义一个platform_driver的一个实体变量,来表示这个驱动。例如这个RTC驱动,

点击(此处)折叠或打开

  1. static struct platform_driver s3c2410_rtc_driver = {
  2.     .probe        = s3c_rtc_probe,
  3.     .remove        = __devexit_p(s3c_rtc_remove),
  4.     .suspend    = s3c_rtc_suspend,
  5.     .resume        = s3c_rtc_resume,
  6.     .driver        = {
  7.         .name    = "s3c2410-rtc",
  8.         .owner    = THIS_MODULE,
  9.     },
  10. };
注册platform驱动函数如下,

点击(此处)折叠或打开

  1. int platform_driver_register(struct platform_driver *drv)
  2. {
  3.     drv->driver.bus = &platform_bus_type;    //(1)
  4.     if (drv->probe)
  5.         drv->driver.probe = platform_drv_probe; //(2)
  6.     if (drv->remove)
  7.         drv->driver.remove = platform_drv_remove;    //(3)
  8.     if (drv->shutdown)
  9.         drv->driver.shutdown = platform_drv_shutdown; //(4)
  10.     if (drv->suspend)
  11.         drv->driver.suspend = platform_drv_suspend;    //(5)
  12.     if (drv->resume)
  13.         drv->driver.resume = platform_drv_resume;    //(6)
  14.     return driver_register(&drv->driver);    //(7)
  15. }

可以看出此函数主要是设置了platform_driver结构体,最后注册结构体中的drvier型变量。
(1)设置device_driver 的总线类型bus,为platform_bus_type, 

点击(此处)折叠或打开

  1. struct bus_type platform_bus_type = {
  2.     .name        = "platform",    //总线类型的名称
  3.     .match        = platform_match,    //<1>
  4.     .suspend    = platform_suspend, //<2>
  5.     .resume        = platform_resume, //<3>
  6. };
<1>platform_match函数定义了platform类型总线,device和driver之间的匹配方法。在文件drivers/base/Platform.c

点击(此处)折叠或打开

  1. static int platform_match(struct device * dev, struct device_driver * drv)
  2. {
  3.     struct platform_device *pdev = container_of(dev, struct platform_device, dev);
  4.     //通过container_of函数数获得包含dev的platform_device变量。
  5.     return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
  6.     //platform模型是通过比较设备名称和驱动名称还进行匹配的。
  7. }
<2>platform总线上的挂起函数platform_suspend是调用相应device设备的driver的suspend函数
<3>platform总线上的恢复函数同上。

(2)--(6)都是在注册platform_driver变量时,检查是否定义了相应的函数,如果定义则使用默认函数,默认的函数会将platform_driver的父结构device_driver的操作函数设置成platform_driver中定义的函数。probe函数如下

点击(此处)折叠或打开

  1. static int platform_drv_probe(struct device *_dev)
  2. {
  3.     struct platform_driver *drv = to_platform_driver(_dev->driver);    //获得子结构platform_driver
  4.     struct platform_device *dev = to_platform_device(_dev);                //获得子结构platform_device

  5.     return drv->probe(dev);    //调用的是platform_driver的probe函数。
  6. }

(7)调用driver_register将platform_driver的父结构device_driver注册进设备模型核心。


平台设备和平台驱动的关联
注册平台设备驱动时即platform_driver_register函数,会调用到driver_register函数,此函数会调用driver_attach(),这个函数将在对应总线上寻找匹配的设备。





上一篇:设备驱动模型(2)
下一篇:字符设备模型学习