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\n",  
  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\n",  
  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 自定义了probe,remove等函数,则会覆盖默认函数,否则使用默认的函数

相反地,要注销平台驱动的话,使用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");

附平台驱动程序

 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!\n");
12     return 0;
13 }
14 static int my_remove(struct platform_device *dev){
15     printk("Driver found device unpluged!\n");
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");

附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

时间: 2024-08-25 11:50:10

20150226 IMX257 总线设备驱动模型编程之平台总线设备platform的相关文章

20150226 IMX257 总线设备驱动模型编程之设备篇

20150226 IMX257 总线设备驱动模型编程之设备篇 2015-02-26 李海沿 前面我们呢实现了总线-设备-驱动模型中的总线,自然,我们的目标就是在我们建立的总线下面创建一个设备. http://www.cnblogs.com/lihaiyan/p/4301072.html 一.程序分析 1. 包含总线 既然我们的设备在总线上,自然我们既要包含总线了 如图所示,使用外部声明将我们的总线的结构体包含进来 2. 定义设备结构体 父目录为 my_bus 3. 定义属性文件结构体 属性文件结

20150226 IMX257 总线设备驱动模型编程之驱动篇

20150226 IMX257 总线设备驱动模型编程之驱动篇 2015-02-26 11:42 李海沿 前面我们已经实现了 总线和设备 的驱动程序,接下来我们的任务就是 实现 驱动 了 地址:http://www.cnblogs.com/lihaiyan/p/4301079.html http://www.cnblogs.com/lihaiyan/p/4301072.html 在实现驱动程序之前,我们来想两个问题: 一.问题分析 1.什么时候驱动程序会在总线上找它可以处理的设备? 在driver

20150225 IMX257 总线设备驱动模型编程之总线篇

20150225 IMX257 总线设备驱动模型编程之总线篇 2015-02-25 19:40 李海沿 从现在开始,我们开始来实现 总线-设备-驱动模型中的总线.. 我们这个程序的目标是在 sysfs文件系统的/sys/bus/ 目录下面建立一个文件夹. 一.总线介绍 1. 总线数据结构bus_type struct bus_type 结构体的定义如下: struct bus_type { const char *name; --总线名 struct bus_attribute *bus_att

字符设备驱动、平台设备驱动、设备驱动模型、sysfs的关系

Linux驱动开发的童鞋们来膜拜吧:-)  学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术.对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术也相对不错了.要深刻理解其中的原理需要非常熟悉设备驱动相关的框架和模型代码.网络上有关这些技术的文章不少,但多是对其中的某一点进行阐述,很难找到对这些技术进行比较和关联的分析.对于开发者而言,能够熟悉某一点并分享出来已很难得,但对于专注传授技术和经验给

[kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术.对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术也相对不错了.要深刻理解其中的原理需要非常熟悉设备驱动相关的框架和模型代码.网络上有关这些技术的文章不少,但多是对其中的某一点进行阐述,很难找到对这些技术进行比较和关

平台设备驱动模型

对于字符设备驱动程序,之前都是在驱动程序中定义并设置file_operations结构体,实现各种需要用到的函数,注册file_operations结构体,框架比较简单,但是在Linux中,却很少看见这样框架的代码,那是因为在Linux中设备驱动模型一般都由总线.设备.驱动这个三大部分组成.这是一种分层分离的思想: 分层:核心层和设备相关层分开. 分离:讲硬件相关的代码和驱动分开. 这就是总线设备驱动(bus_drv_dev)模型: 对于dev模块,当调用device_add函数时: a.会将d

Linux 设备驱动模型

Linux系统将设备和驱动归一到设备驱动模型中了来管理 设备驱动程序功能: 1,对硬件设备初始化和释放 2,对设备进行管理,包括实参设置,以及提供对设备的统一操作接口 3,读取应用程序传递给设备文件的数据或回送应用程序请求的数据 4,检测或处理设备出现的错误 设备驱动模型提供了硬件的抽象包括: 1,电源管理 其实,电源管理就是一些设备不工作的时候,让它歇一会,休眠一会(最低消耗),达到省电的目的 它的一个重要的功能是: 省电模式下,使系统中的设备以一定的先后顺序挂起 在全速工作模式下,使系统的设

第四季-专题13-总线设备驱动模型

专题2-总线设备驱动模型 第1课-总线设备驱动模型 总线模型概述 随着技术的不断进步,系统的拓扑结构也越来越复杂,对热插拔,跨平台移植性的 要求也越来越高,2.4内核已经难以满足这些需求.为适应这种形势的需要,从Linux 2.6内核开始提供了全新的设备模型. 总线 (1)     描述结构 在Linux 内核中, 总线由bus_type 结构表示, 定义在<linux/device.h> struct bus_type { const char *name; /*总线名称*/ const c

6410-混杂设备驱动模型

1.混杂设备驱动模型a.混杂设备描述混杂设备概念:在Linux系统中,存在一类字符设备,它们拥有相同的主设备号(10),但次设备号不同,我们称这类设备为混杂设备(miscdevice).所有的混杂设备形成一个链表,对设备访问时内核根据混杂设备形成一个链表,对设备访问时内核根据次设备号查找到相应的混杂设备.设备描述:struct miscdevice { int minor;//次设备号 const char *name;//设备名 const struct file_operations *fo