zoukankan      html  css  js  c++  java
  • 20150226 IMX257 总线设备驱动模型编程之平台总线设备platform

    20150226 IMX257 总线设备驱动模型编程之平台总线设备platform

    2015-02-26 李海沿

    前面我们实现了总线设备驱动模型,下面我们来了解一下平台总线,平台设备驱动

    分为平台设备和平台驱动两种,和前面所说的设备驱动差不多

    platform总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver。Linux 2.6的设备驱动模型中,把I2C、RTC、LCD等都归纳为platform_device。

    一、平台设备介绍

    1. platform_bus_type 结构体

    struct bus_type platform_bus_type = {

        .name = "platform",

      .dev_attrs = platform_dev_attrs,

      .match = platform_match, //设备和驱动使用match函数来判断是否匹配

      .uevent = platform_uevent,

      .pm = PLATFORM_PM_OPS_PTR,

    };

    这个结构体就是linux中平台设备的结构体,所以在sysfs文件系统中的/sys/bus/目录下面会有一个 名字为platform的目录。

    2. platform_match 函数

    platform_match函数首先判断是否由id_table,如果有则使用id_table来进行匹配,否则,判断platform_device和platform_driver成员里的name,如果二者的name字段相同则匹配,如果匹配则调用platform_driver的probe函数。

    /* 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);

      /* match against the id table first */

      if (pdrv->id_table)

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

      /* fall-back to driver name match */

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

    }

    3. platform_device 结构体

    struct platform_device {

      const char * name; /* 名字 。这个名字用于与驱动进行匹配*/

      int id; /* 设备编号 */

      struct device dev;

      u32 num_resources; /* 资源总数 */

      struct resource * resource; /* 资源 */

      struct platform_device_id *id_entry;

    };

    其中有个重要的成员是resource,是设备的资源信息,如IO地址,中断号等。

    struct resource {

      resource_size_t start; //资源的起始值

      resource_size_t end; //资源的结束值

      const char *name;

      unsigned long flags; //资源的类型,

      //如IORESOURCE_IO,IORESOURCE_MEM,IORESOURCE_IRQ,IORESOURCE_DMA

      struct resource *parent, *sibling, *child;

    };

    有的设备可能有多个资源,通常使用platform_get_resource函数来获取资源

    /**

    * platform_get_resource - get a resource for a device

    * @dev: platform device

    * @type: resource type

    * @num: resource index

    */

    struct resource *platform_get_resource(struct platform_device *dev,

    unsigned int type, unsigned int num)

    {

      int i;

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

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

         if (type == resource_type(r) && num-- == 0)

          return r;

      }

      return NULL;

    }

    4. platform平台设备注册函数

    int platform_device_register(struct platform_device *pdev)

    {

      device_initialize(&pdev->dev);

      return platform_device_add(pdev);

    }

    从上面代码得知,platform_device_register函数先通过device_initialize函数初始化platform_device的device成员,然后调用platform_device_add向内核添加一个平台设备。

    1. int platform_device_add(struct platform_device *pdev)  
    2. {  
    3.     int i, ret = 0;  
    4.     
    5.     if (!pdev)  /* 如果pdev为空则返回EINVAL */  
    6.         return -EINVAL;  
    7.     
    8.     /* 如果pdev->dev.parent为空则将pdev->dev.parent设置为platform_bus */  
    9.     if (!pdev->dev.parent)  
    10.         pdev->dev.parent = &platform_bus;  
    11.     
    12.     pdev->dev.bus = &platform_bus_type;  /* 设置总线类型 */  
    13.     
    14.     if (pdev->id != -1)      /* 如果id = -1则表示自动分配name */  
    15.         dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);  
    16.     else  
    17.         dev_set_name(&pdev->dev, pdev->name);  
    18.     
    19.     for (i = 0; i < pdev->num_resources; i++) {  
    20.         struct resource *p, *r = &pdev->resource[i]; /* 获取资源 */  
    21.     
    22.         if (r->name == NULL)  
    23.             r->name = dev_name(&pdev->dev);  
    24.     
    25.         p = r->parent;  
    26.         if (!p) {  
    27.             if (resource_type(r) == IORESOURCE_MEM) /* 设置资源类型 */  
    28.                 p = &iomem_resource;  
    29.             else if (resource_type(r) == IORESOURCE_IO)  
    30.                 p = &ioport_resource;  
    31.         }  
    32.     
    33.         if (p && insert_resource(p, r)) {  
    34.             printk(KERN_ERR  
    35.                    "%s: failed to claim resource %d ",  
    36.                    dev_name(&pdev->dev), i);  
    37.             ret = -EBUSY;  
    38.             goto failed;  
    39.         }  
    40.     }  
    41.     
    42.     pr_debug("Registering platform device '%s'. Parent at %s ",  
    43.          dev_name(&pdev->dev), dev_name(pdev->dev.parent));  
    44.     
    45.     /* 向内核添加一个device */  
    46.     ret = device_add(&pdev->dev);  
    47.     if (ret == 0)  
    48.         return ret;  
    49.     
    50.  failed:  
    51.     while (--i >= 0) {  
    52.         struct resource *r = &pdev->resource[i];  
    53.         unsigned long type = resource_type(r);  
    54.     
    55.         if (type == IORESOURCE_MEM || type == IORESOURCE_IO)  
    56.             release_resource(r);  
    57.     }  
    58.     
    59.     return ret;  
    60. }  

    platform_device_add最终调用device_add来完成平台设备的注册。

    相反地,如果要注销平台设备则使用platform_device_unregister函数

    void platform_device_unregister(struct platform_device *pdev)

    {

        platform_device_del(pdev);

        platform_device_put(pdev);

    }

    platform_device_unregister函数调用platform_device_del函数来注销平台设备

    void platform_device_del(struct platform_device *pdev)

    {

      int i;

      if (pdev) {

        device_del(&pdev->dev);

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

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

          unsigned long type = resource_type(r);

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

            release_resource(r);

        }

      }

    }

    platform_device_del函数调用device_del函数来删除平台设备,相应地,要释放资源应调用release_resource函数,前提是资源的类型必须为IORESOURCE_MEM或者IORESOURCE_IO

    二、平台驱动介绍

    1. 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 *);

        struct device_driver driver;

        const struct platform_device_id *id_table;

    };

    platform_driver结构体有device_driver成员

    2. device_driver

    struct device_driver {

        const char *name; //这个名字用于与设备进行匹配

        struct bus_type *bus;

        struct module *owner;

        const char *mod_name; /* used for built-in modules */

        bool suppress_bind_attrs; /* disables bind/unbind via sysfs */

        const struct of_device_id *of_match_table;

        const struct acpi_device_id *acpi_match_table;

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

        const struct attribute_group **groups;

        const struct dev_pm_ops *pm;

        struct driver_private *p;

    };

    device_driver也有probe、remove、shutdown等函数,在平台驱动注册的时候被初始化

    当系统中存在有平台设备和平台驱动通过总线的match函数匹配后则会调用platform_driver的probe函数,参数为platform_device,有时候也通过id_table来判断是否匹配。

    struct platform_device_id {

        char name[PLATFORM_NAME_SIZE];

        kernel_ulong_t driver_data

          __attribute__((aligned(sizeof(kernel_ulong_t))));

    };

    3. 平台驱动注册函数

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

    }

    如果platform_driver 自定义了proberemove等函数,则会覆盖默认函数,否则使用默认的函数

    相反地,要注销平台驱动的话,使用platform_driver_unregister函数

    void platform_driver_unregister(struct platform_driver *drv)

    {

      driver_unregister(&drv->driver);

    }

    三、平台设备实例分析

    1. 定义平台设备结构体

    2.在初始化函数中分配平台设备结构体

    如图所示,在init函数中先分配平台设备结构体,然后再注册设备

    3. 在exit函数中卸载设备

    很简单吧,下面我们来看看平台驱动

    四、平台驱动实例分析

    1.定义平台驱动结构体

    如图所示,先定义平台驱动结构体,然后实现相关的函数。

    注意.driver = 这个是一个device_driver结构体,其中的成员.name就是用于和设备进行匹配。

    2.注册平台驱动

    如图所示,剩下的工作就是分别在 init 和exit 函数中注册和卸载驱动了。

    五、编译测试

    上面有关平台设备的部分知识点摘自大牛文章:

    http://blog.csdn.net/lwj103862095/article/details/17957637

    附平台设备程序;

     1 //platform_device.c
     2 #include <linux/device.h>
     3 #include <linux/module.h>
     4 #include <linux/kernel.h>
     5 #include <linux/init.h>
     6 #include <linux/string.h>
     7 #include <linux/platform_device.h>
     8 
     9 //定义平台设备结构体
    10 static struct platform_device *my_device;
    11 
    12 static int __init my_device_init(void){
    13     int ret = 0;
    14     /* 分配结构体 */
    15     my_device = platform_device_alloc("my_platform_dev",-1);
    16 
    17     /* 注册设备 */
    18     ret = platform_device_add(my_device);
    19 
    20     /* 注册失败,则释放相关内存 */
    21     if(ret)
    22         platform_device_put(my_device);
    23     return ret;
    24 }
    25 
    26 static void my_device_exit(void){
    27     platform_device_unregister(my_device);
    28 }
    29 
    30 module_init(my_device_init);
    31 module_exit(my_device_exit);
    32 
    33 MODULE_AUTHOR("Lover雪儿");
    34 MODULE_LICENSE("GPL");
    View Code

    附平台驱动程序

     1 //platform_driver.c
     2 #include <linux/device.h>
     3 #include <linux/module.h>
     4 #include <linux/kernel.h>
     5 #include <linux/init.h>
     6 #include <linux/string.h>
     7 #include <linux/platform_device.h>
     8 
     9 //探测函数
    10 static int my_probe(struct platform_device *dev){
    11     printk("Driver found device which my driver can handle!
    ");
    12     return 0;
    13 }
    14 static int my_remove(struct platform_device *dev){
    15     printk("Driver found device unpluged!
    ");
    16     return 0;
    17 }
    18 //定义平台驱动结构体
    19 static struct platform_driver my_driver = {
    20     .probe = my_probe,
    21     .remove = my_remove,
    22     .driver = {
    23         .owner = THIS_MODULE,
    24         .name  = "my_platform_dev",
    25     },
    26 };
    27 
    28 static int __init my_driver_init(void){
    29     /* 注册平台驱动 */
    30     return platform_driver_register(&my_driver);
    31 }
    32 
    33 static void my_driver_exit(void){
    34     platform_driver_unregister(&my_driver);
    35 }
    36 
    37 module_init(my_driver_init);
    38 module_exit(my_driver_exit);
    39 
    40 MODULE_AUTHOR("Lover雪儿");
    41 MODULE_LICENSE("GPL");
    View Code

    附MAKEFILE程序

     1 ifeq ($(KERNELRELEASE),)
     2     KERNELDIR ?= /home/study/system/linux-2.6.31
     3     PWD := $(shell pwd)
     4 modules:
     5     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
     6 modules_install:
     7     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
     8 clean:
     9     rm -rf *.o *~ core .depend  *.cmd *.ko *.mod.c .tmp_versions *.markers *.order *.symvers
    10 
    11 else
    12     obj-m := platform_device.o platform_driver.o
    13 endif
    View Code
  • 相关阅读:
    python爬取北京政府信件信息03
    Python3.7 lxml引入etree
    python爬取北京政府信件信息02
    python爬取北京政府信件信息01
    2020.12.2收获
    2020.12.1收获
    2020.11.30收获
    2020.11.29收获
    2020.11.28收获
    2020.11.27收获
  • 原文地址:https://www.cnblogs.com/lihaiyan/p/4301415.html
Copyright © 2011-2022 走看看