platform 架构

1. 简介:

1.1 platform 总线是虚拟总线,当使用这个虚拟总线是带来的好处主要有两点:

(1)使得设备被挂接在一个总线上,因此,符合 Linux 2.6 的设备模型。其结果是,配套的sysfs 结点、设备电源管理都成为可能。

(2)隔离BSP和驱动。在BSP中定义platform 设备和设备使用的资源、设备的具体配置信息,而在驱动中,只需要通过通用 API 去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。

1.2 对platform中的设备和驱动的理解可以用如下的比喻:

总线是红娘,设备是男方,驱动是女方:
    红娘负责男方和女方的撮合
    男方或女方找到红娘,来登记,看有没有合适的姑娘 -- 设备或驱动的注册
    红娘这个时候看看有没有合适的八字(二者的name字段) -- match 函数进行匹配,看name是不是相同
    如果没有,就等着,知道匹配的了,就结婚,结完婚,男方向女方交代,我有多少存款,我的房子在哪,钱放哪等等(struct resource *resource),女方去拿存款买菜啦,买衣服了等等(匹配成功后执行 int (*probe)(struct platform_device *) 匹配成功后执行的第一个函数),当然,如果男的跟小三跑了,女方也不会继续下去的。

1.3 在 linux 系统中,设备可以是:

    模块文件

    平台文件 (arch/arm/mach-s3c24xx/mach-mini2440.c)

    设备树文件 (arch/arm/boot/dts/)

 

2. 下面就介绍介绍如何使用 platform 总线:

2.1 函数原型

①设备:相关结构体是 struct platform_device

②驱动:相关结构体是 struct platform_driver

需要包含的头文件是:linux/platform_device.h

struct platform_device {
    const char * name; / * 设备名 */
    u32 id;
    struct device dev;
    u32 num_resources; / * 设备所使用各类资源数量 */
    struct resource * resource; / * 资源 */
};

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 (*suspend_late)(struct platform_device *, pm_message_t state);
    int (*resume_early)(struct platform_device *);
    int (*resume)(struct platform_device *);
    struct pm_ext_ops *pm;
    struct device_driver driver;
};

 

①②的实现都遵循: --定义,初始化,注册,释放,

过程设备驱动
定义成员.dev.release 的函数实现.probe .remove 的函数实现
初始化struct platform_device test_device = {...}struct platform_driver test_driver = {...}
注册platform_device_register(&test_device);platform_driver_register(&test_driver);
释放platform_device_unregister(&test_device);platform_driver_unregister(&test_driver);

 

总线的匹配函数在 platform_bus_type 实例中的 match 函数,匹配的方法是看 device 和 driver 的名字是不是相同

具体的:

device方面:

platform_device_register(struct platform_device *dev)
-->platform_device_add(pdev);
-->device_add(&pdev->dev);
-->bus_probe_device(dev);
-->device_attach(dev);
-->bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
-->driver_match_device(drv, dev);成功才向下执行probe!
-->__device_attach
-->drv->bus->match()  这里就是 platform_match

driver方面:
platform_driver_register(struct platform_driver * drv)
-->driver_register(&drv->driver);
-->bus_add_driver(drv);
-->__driver_attach
-->driver_match_device
-->drv->bus->match(dev, drv)

 

2.2 注册和注销

platform_device的定义通常在 BSP 的板文件中实现,在板文件中,将 platform_device 归纳为一个数组,最终通过 platform_add_devices() 函数统一注册。

platform_add_devices() 函数可以将平台设备添加到系统中,这个函数的原型为:

int platform_add_devices(struct platform_device **devs, int num);

devs,平台设备数组的指针

num,平台设备的数量

它内部调用了 platform_device_register() 函数用于注册单个的平台设备。

 

platform_driver 通过platform_driver_register()、platform_driver_unregister()函数进行platform_driver 的注册与注销,而原先对于模块中的字符设备的注册和注销工作要移交到 platform_driver 的 probe() 和 remove() 成员函数中。

 

2.3 为设备添加资源和数据

在 struct platform_device 中填充 struct resource结构体:

struct resource test_res={

    .start = 11,

    .end   = 22,

    .flags = IORESOURCE_MEM,

};

 

内容:

    .start,区域的开始地址,如果是内存类型,表示内存的开始地址,如果是IRQ,则表示中断号的开始值

    .end,区域的结束地址,如果是内存类型,表示内存的结束地址,如果是IRQ,则表示中断号的结束值,如果只有一个中断,则开始值和结束值相同

    .flags,类型:

        IORESOURCE_MEM    ,内存类型

        IORESOURCE_IRQ       ,中断类型

IORESOURCE_DMA     ,

            ...

 

驱动侧用platform_get_resource 来获得设备信息

一般使用方法:

struct resource * res =NULL;

    res=platform_get_resource(dev,IORESOURCE_MEM,0);

 

函数的原型:

struct resource *platform_get_resource(struct platform_device *dev,

                                                                unsigned int type, unsigned int num)

参数:

    dev,struct platform_device 结构体

    type,同传递过来的设备信息中的flags,有IORESOURCE_MEM    、IORESOURCE_IRQ       ...

    num,同类设备信息的索引,比如类型相同的IORESOURCE_MEM,num会被系统自动分配,从0开始

 

设备除了可以在 BSP 中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断、内存、DMA 通道以外,可能还会有一些配置信息,而这些配置信息也依赖于板,不适宜直接放置在设备驱动本身,因此,platform 也提供了 platform_data 的支持。platform_data 的形式是自定义的,如对于 DM9000 网卡而言,platform_data 为一个 dm9000_plat_data 结构体,我们就可以将 MAC 地址、总线宽度、板上有无 EEPROM 信息等放入 platform_data:

 

static struct dm9000_plat_data ldd6410_dm9000_platdata = {

        .flags = DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM,

        .dev_addr = { 0x0, 0x16, 0xd4, 0x9f, 0xed, 0xa4 },

};

static struct platform_device ldd6410_dm9000 = {

        .name= "dm9000",

        .id= 0,

        .num_resources= ARRAY_SIZE(ldd6410_dm9000_resource),

        .resource =ldd6410_dm9000_resource,

        .dev = {

                .platform_data = &ldd6410_dm9000_platdata,

        }

};

而在 DM9000 网卡的驱动中,通过如下方式就拿到了 platform_data:

struct dm9000_plat_data *pdata = pdev->dev.platform_data;

其中,pdev 为 platform_device 的指针。

 

2.4 对platform的理解可以用如下图:

 

3. 例子:

3.1 对于一个普通的platform架构的程序很简单:

#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>

MODULE_LICENSE("Dual BSD/GPL");

static int driver_probe(struct platform_device *dev)
{
    printk("platform: match ok!\n");
    return 0;
}

static int driver_remove(struct platform_device *dev)
{
    printk("platform: driver remove\n");
    return 0;
}

static void device_release(struct device *dev)
{
    printk("platform: device release\n");
}

struct platform_device test_device = {
    .id = -1,
    .name = "test_device1",
    .dev.release = device_release, 
};


struct platform_driver test_driver = {
    .probe = driver_probe,
    .remove = driver_remove,
    .driver = {
        .name = "test_device",
    },
};


static int __init s5pc100_platform_init(void)
{
    platform_device_register(&test_device);
    platform_driver_register(&test_driver);

    return 0;
}

static void __exit s5pc100_platform_exit(void)
{
    platform_device_unregister(&test_device);
    platform_driver_unregister(&test_driver);

}

module_init(s5pc100_platform_init);
module_exit(s5pc100_platform_exit);

Makefile 参照前边的内容,是一样的

3.2 而对于设备资源的取得例子如下:

一个设备信息:
.
├── dev
│   ├── dev.c
│   └── Makefile
└── dri
    ├── dri.c
    └── Makefile


dev.c
#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>

MODULE_LICENSE("Dual BSD/GPL");

struct resource test_res={
    .start = 11,
    .end   = 22,
    .flags = IORESOURCE_MEM,
};


static void device_release(struct device *dev)
{
    printk("platform: device release\n");
}

struct platform_device test_device = {
    .id = -1,
    .name = "test_device",
    .dev.release = device_release,
    .num_resources =1,
    .resource = &test_res,

};

static int __init s5pc100_platform_init(void)
{
    printk("dev init\n");
    platform_device_register(&test_device);
    return 0;
}

static void __exit s5pc100_platform_exit(void)
{
    printk("dev exit\n");
    platform_device_unregister(&test_device);
}

module_init(s5pc100_platform_init);
module_exit(s5pc100_platform_exit);

dri.c
#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>

MODULE_LICENSE("Dual BSD/GPL");

static int driver_probe(struct platform_device *dev)
{
    struct resource * res =NULL;
    printk("platform: match ok!\n");
    res=platform_get_resource(dev,IORESOURCE_MEM,0);
    if(res == NULL)
    {
        printk("platform_get_resource error\n");
        return -EINVAL;
    }
    printk("res->start:%d\n",res->start); //11
    printk("res->end:%d\n",res->end);     //22
    printk("res->flags:%#x\n",res->flags); //0x200

    printk("platform probe end\n");
    return 0;
}

static int driver_remove(struct platform_device *dev)
{
    printk("platform: driver remove\n");
    return 0;
}

struct platform_driver test_driver = {
    .probe = driver_probe,
    .remove = driver_remove,
    .driver = {
        .name = "test_device",
    },
};

static int __init s5pc100_platform_init(void)
{
    printk("dri init\n");
    platform_driver_register(&test_driver);
    return 0;
}

static void __exit s5pc100_platform_exit(void)
{
    printk("dri exit\n");
    platform_driver_unregister(&test_driver);
}

module_init(s5pc100_platform_init);
module_exit(s5pc100_platform_exit);

Makefile这里也不写出来,参照前边的写法

运行结果如下:

$sudo insmod dev.ko 
$ dmesg
[ 2988.851954] dev init
$ cd ../dri/
$ sudo insmod dri.ko 
$ dmesg
[ 2988.851954] dev init
[ 3008.968420] dri init
[ 3008.968466] platform: match ok!
[ 3008.968467] res->start:11
[ 3008.968468] res->end:22
[ 3008.968469] res->flags:0x200
[ 3008.968469] platform probe end
$ sudo rmmod dev 
$ dmesg
[ 2988.851954] dev init
[ 3008.968420] dri init
[ 3008.968466] platform: match ok!
[ 3008.968467] res->start:11
[ 3008.968468] res->end:22
[ 3008.968469] res->flags:0x200
[ 3008.968469] platform probe end
[ 3025.176478] dev exit
[ 3025.176508] platform: driver remove
[ 3025.176556] platform: device release
$ sudo rmmod dri               
$ sudo rmmod dri 
$ dmesg
[ 2988.851954] dev init
[ 3008.968420] dri init
[ 3008.968466] platform: match ok!
[ 3008.968467] res->start:11
[ 3008.968468] res->end:22
[ 3008.968469] res->flags:0x200
[ 3008.968469] platform probe end
[ 3025.176478] dev exit
[ 3025.176508] platform: driver remove
[ 3025.176556] platform: device release
[ 3035.936023] dri exit

 

3.3 多个设备信息:

结构如上边一个设备信息

dev.c
#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>

MODULE_LICENSE("Dual BSD/GPL");

struct resource test_res[]={
    [0]={
        .start = 11,
        .end   = 22,
        .flags = IORESOURCE_MEM,
    },
    [1]={
        .start = 99,
        .end   = 99,
        .flags = IORESOURCE_IRQ,
    },
    [2]={
        .start = 33,
        .end   = 44,
        .flags = IORESOURCE_MEM,
    },
};

static void device_release(struct device *dev)
{
    printk("platform: device release\n");
}

struct platform_device test_device = {
    .id = -1,
     .name = "test_device",
    .dev.release = device_release,
    .num_resources =ARRAY_SIZE(test_res),
    .resource = test_res,

};

static int __init s5pc100_platform_init(void)
{
    printk("dev init\n");
    platform_device_register(&test_device);
    return 0;
}

static void __exit s5pc100_platform_exit(void)
{
    printk("dev exit\n");
    platform_device_unregister(&test_device);
}

module_init(s5pc100_platform_init);
module_exit(s5pc100_platform_exit);


dri.c
#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>

MODULE_LICENSE("Dual BSD/GPL");

static int driver_probe(struct platform_device *dev)
{
    struct resource * res =NULL;
    printk("platform: match ok!\n");
     res=platform_get_resource(dev, IORESOURCE_MEM, 0);
    if(res == NULL)
    {
        printk("platform_get_resource error\n");
        return -EINVAL;
    }
    printk("res->start:%d\n",res->start); //11
    printk("res->end:%d\n",res->end);     //22
    printk("res->flags:%#x\n",res->flags); //0x200

    res=platform_get_resource(dev, IORESOURCE_MEM, 1);
    if(res == NULL)
    {
        printk("platform_get_resource error\n");
        return -EINVAL;
    }
    printk("res->start:%d\n",res->start);   //33
    printk("res->end:%d\n",res->end);     //44
    printk("res->flags:%#x\n",res->flags); //0x200

    res=platform_get_resource(dev,IORESOURCE_IRQ,0);
    if(res == NULL)
    {
        printk("platform_get_resource error\n");
        return -EINVAL;
    }
    printk("res->start:%d\n",res->start);   //99
    printk("res->end:%d\n",res->end);     //99
    printk("res->flags:%#x\n",res->flags); //0x400

    printk("platform probe end\n");
    return 0;
}

static int driver_remove(struct platform_device *dev)
{
    printk("platform: driver remove\n");
    return 0;
}

struct platform_driver test_driver = {
    .probe = driver_probe,
    .remove = driver_remove,
    .driver = {
        .name = "test_device",
    },
};


static int __init s5pc100_platform_init(void)
{
    printk("dri init\n");
    platform_driver_register(&test_driver);
    return 0;
}

static void __exit s5pc100_platform_exit(void)
{
    printk("dri exit\n");
    platform_driver_unregister(&test_driver);
}

module_init(s5pc100_platform_init);
module_exit(s5pc100_platform_exit);


运行结果也不再打印

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值