目录

  1、为什么会有平台总线?

  2、平台总线三要素

  3、平台总线编程接口

  4、编写能在多平台下使用的led驱动

 

1、为什么会有平台总线?

 1     用于平台升级:三星: 2410, 2440, 6410, s5pc100  s5pv210  4412
 2         硬件平台升级的时候,部分的模块的控制方式,基本上是类似的
 3         但是模块的地址是不一样
 4 
 5         gpio控制逻辑: 1, 配置gpio的输入输出功能: gpxxconf
 6                       2, 给gpio的数据寄存器设置高低电平: gpxxdata
 7                     逻辑操作基本上是一样的
 8                     但是地址不一样
 9         
10         uart控制:1,设置8n1(数据位、奇偶校验位、停止位),115200, no AFC(流控)
11                     UCON,ULCON, UMODOEN, UDIV
12                 
13                 逻辑基本上是一样的
14                 但是地址不一样
15 
16 问题:
17     当soc升级的时候, 对于相似的设备驱动,需要编写很多次(如果不用平台总线)
18     但是会有大部分重复代码
19 
20 解决:引入平台总线    
21         device(中断/地址)和driver(操作逻辑) 分离
22     在升级的时候,只需要修改device中信息即可(中断/地址)
23     实现一个driver代码能够驱动多个平台相似的模块,并且修改的代码量很少

 

平台总线 —— 平台总线驱动模型

 

 

 

2、平台总线三要素 —— platform_bus、device、driver·

  platform会存在/sys/bus/里面

 如下图所示, platform目录下会有两个文件,分别就是platform设备和platform驱动

 平台总线 —— 平台总线驱动模型

       device设备

  挂接在platform总线下的设备, 使用结构体platform_device描述

    driver驱动

  挂接在platform总线下,是个与某种设备相对于的驱动, 使用结构体platform_driver描述

    platform总线

  是个全局变量,为platform_bus_type,属于虚拟设备总线,通过这个总线将设备和驱动联系起来,属于Linux中bus的一种。

  以下依次介绍其数据结构:

  1)device

 1 struct platform_device {
 2     const char  * name;  //设备名称,要与platform_driver的name一样,这样总线才能匹配成功
 3     u32             id;  //插入总线下相同name的设备编号(一个驱动可以有多个设备),如果只有一个设备填-1
 4     struct device   dev; //具体的device结构体,继承了device父类
 5                          //成员platform_data可以给平台driver提供各种数据(比如:GPIO引脚等等)
 6     u32    num_resources;//资源数目
 7     struct resource    * resource;//资源描述,用来描述io,内存等
 8 };
 9 
10 //资源文件 ,定义在include\linux\ioport.h
11 struct resource {
12     resource_size_t start;  //起始资源,如果是地址的话,必须是物理地址
13     resource_size_t end;    //结束资源,如果是地址的话,必须是物理地址
14     const char *name;       //资源名
15     unsigned long flags;    //资源类型,可以是io/irq/mem等
16     struct resource *parent, *sibling, *child;    //链表结构,可以构成链表
17 };
18 // type
19 #define IORESOURCE_IO         0x00000100    /* Resource type */
20 #define IORESOURCE_MEM        0x00000200
21 #define IORESOURCE_IRQ        0x00000400
22 #define IORESOURCE_DMA        0x00000800

注册和注销

1 int  platform_device_register(struct platform_device * pdev);
2 void  platform_device_unregister(struct platform_device * pdev)

 

2)driver

 1 struct platform_driver {
 2         int (*probe)(struct platform_device *); //匹配成功之后被调用的函数
 3         int (*remove)(struct platform_device *);//device移除的时候调用的函数
 4         struct device_driver driver; //继承了driver父类
 5                             |
 6                             const char        *name;
 7         const struct platform_device_id *id_table; //如果driver支持多个平台,在列表中写出来
 8 }

    注册与注销

1  int platform_driver_register(struct platform_driver *drv);
2 void platform_driver_unregister(struct platform_driver *drv);

 

3)platform_bus

 1 struct bus_type platform_bus_type = {              
 2 .name             = "platform",         //设备名称
 3 .dev_attrs        = platform_dev_attrs, //设备属性、含获取sys文件名,该总线会放在/sys/bus下
 4 .match            = platform_match,     //匹配设备和驱动,匹配成功就调用driver的.probe函数
 5 .uevent           = platform_uevent,    //消息传递,比如热插拔操作
 6 .suspend          = platform_suspend,   //电源管理的低功耗挂起
 7 .suspend_late     = platform_suspend_late,  
 8 .resume_early     = platform_resume_early,
 9 .resume           = platform_resume,   //恢复
10 };
11 

 

  匹配方法:match

 1  static int platform_match(struct device *dev, struct device_driver *drv)
 2 {
 3 //1,优先匹配pdriver中的id_table,里面包含了支持不同的平台的名字
 4 //2,直接匹配driver中名字和device中名字
 5   
 6     struct platform_device *pdev = to_platform_device(dev);
 7     struct platform_driver *pdrv = to_platform_driver(drv);
 8 
 9     if (pdrv->id_table)  // 如果pdrv中有idtable,平台列表名字和pdev中的名字
10         return platform_match_id(pdrv->id_table, pdev) != NULL; 
11 
12     /* fall-back to driver name match */
13     return (strcmp(pdev->name, drv->name) == 0);
14 
15 }    

  如何实现?

平台总线 —— 平台总线驱动模型

 

在pdev与pdrv指向的device、driver结构体中,各自都有一个成员 -- 父类结构体,实际上向总线注册的是这个父类结构体指针。通过container_of,可以通过成员找到包含该成员的整个结构体。

1 #define to_platform_device(x)   container_of((x), struct platform_device, dev)
3 #define to_platform_driver(drv) (container_of((drv), struct platform_driver, driver))

 

3、平台总线编程接口

  1) pdev 注册和注销

1 int platform_device_register(struct platform_device * pdev);
2 void  platform_device_unregister(struct platform_device * pdev);

  2)pdrv注册与注销

1 int platform_device_register(struct platform_device * pdev);
2 void  platform_device_unregister(struct platform_device * pdev);

  3)获取资源数据(对资源的定义可以参考内核/arch/arm/mach-xxx.c文件)

1 int platform_get_irq(struct platform_device * dev,unsigned int num);                                        
2 struct resource * platform_get_resource_byname(struct platform_device * dev,unsigned int type,const char * name);

 

 4、编写能在多平台下使用的LED驱动

   1)注册一个platform_device,定义资源:地址和中断

1 struct resource {
2     resource_size_t start;// 开始
3     resource_size_t end;  //结束
4     const char *name;     //描述,自定义
5     unsigned long flags; //区分当前资源描述的是中断(IORESOURCE_IRQ)还是内存(IORESOURCE_MEM)
6     struct resource *parent, *sibling, *child;
7 };

 

  2)注册一个platform_driver,实现操作失败的代码

 1    注册完毕,同时如果和pdev匹配成功,自动调用probe方法:
 2          probe方法: 对硬件进行操作
 3               a,注册设备号,并且注册fops--为用户提供一个设备标示,同时提供文件操作io接口
 4               b,创建设备节点
 5               c,初始化硬件
 6                       ioremap(地址);  //地址从pdev需要获取
 7                       readl/writle();
 8               d,实现各种io接口: xxx_open, xxx_read, ..
 9 

 

 1 获取资源的方式:        
 2  //获取资源
 3  struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num)
 4 {
 5     int i;
 6 
 7     for (i = 0; i < dev->num_resources; i++) {
 8         struct resource *r = &dev->resource[i];
 9 
10         if (type == resource_type(r) && num-- == 0)
11             return r;
12     }
13     return NULL;
14 }
15 // 参数1: 从哪个pdev中获取资源
16 // 参数2:  资源类型
17 // 参数3: 表示获取同种资源的第几个(0,1,2,3.....)
18 //返回值:返回一个指针,指向想获取的资源项

 

 示例:编写平台驱动,实现最基本的匹配

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/ioport.h>
 4 #include <plat/irqs.h>
 5 #include <linux/platform_device.h>
 6 
 7 
 8 static int led_pdrv_probe(struct platform_device * pdev)
 9 {
10     printk("--------------%s-------------\n",__FUNCTION__);
11     return 0;
12 }
13 
14 
15 static int led_pdrv_remove(struct platform_device * pdev)
16 {
17 
18     return 0;
19 }
20 
21 
22 //id_table:平台的id列表,包含支出不同平台的名字
23 const struct platform_device_id led_id_table[] = {
24     {"exynos_4412_led", 0x4444}, 
25     {"s3c2410_led",    0x2244},
26     {"s5pv210_led",    0x3344},
27 };
28 
29 struct platform_driver led_pdrv = {
30     .probe  = led_pdrv_probe,
31     .remove = led_pdrv_remove,
32     .driver = {  
33         .name = "samsung_led_drv",
34         //可以用作匹配
35         // /sys/bus/platform/drivers/samsung_led_drv
36     },
37     .id_table = led_id_table,
38 
39 };
40 
41 
42 static int __init plat_led_drv_init(void)
43 {
44     printk("---------------%s---------------\n",__FUNCTION__);
45     //注册一个平台驱动
46     return platform_driver_register(&led_pdrv);
47 
48 }
49 
50 static void __exit plat_led_drv_exit(void)
51 {
52 
53     platform_driver_unregister(&led_pdrv);
54 }
55 
56 
57 
58 module_init(plat_led_drv_init);
59 module_exit(plat_led_drv_exit);
60 
61 MODULE_LICENSE("GPL");
plat_led_pdrv.c

相关文章: