platform 架构

1. 简介:

转自: https://blog.csdn.net/qqliyunpeng/article/details/52700331

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 的名字是不是相同
 

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


运行结果也不再打印

转载于:https://www.cnblogs.com/S-ong/p/9313910.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值