platform


  在不同平台的设备下需要主机驱动,如果每个设备都只提供一个设备驱动直接与主机相连,内核代码将会十分臃肿。将主机驱动和设备驱动分隔开,将会简化驱动的开发,linux提供总线(bus)、驱动(driver)和设备(device)模型。

 

 

 当我们向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看有没有与之匹配的设备,如果有的话就将两者联系起来。同样的,当向系统中注册一个设备的时候,总线就会在左侧的驱动中查找看有没有与之匹配的设备,有的话也联系起来。

SOC 中有些外设是没有总线这个概念的,但是又要使用总线、驱动和设备模型,为了解决此问题, Linux 提出了 platform 这个虚拟总线。

(1)platform总线

Linux系统内核使用bus_type结构体表示总线,此结构体定义在文件include/linux/device.

 

struct bus_type {
    const char        *name;
    const char        *dev_name;
    struct device        *dev_root;
    const struct attribute_group **bus_groups;
    const struct attribute_group **dev_groups;
    const struct attribute_group **drv_groups;

    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);

    int (*online)(struct device *dev);
    int (*offline)(struct device *dev);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);

    int (*num_vf)(struct device *dev);

    int (*dma_configure)(struct device *dev);

    const struct dev_pm_ops *pm;

    const struct iommu_ops *iommu_ops;

    struct subsys_private *p;
    struct lock_class_key lock_key;

    bool need_parent_lock;
};

 

platform 总线是 bus_type 的一个具体实例,定义在文件 drivers/base/platform.c

struct bus_type platform_bus_type = {
    .name = "platform",
    .dev_groups = platform_dev_groups,
    .match = platform_match,
    .uevent = platform_uevent,
    .pm = &platform_dev_pm_ops,
};

platform_match 函数定义在文件 drivers/base/platform.c

 

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);
/*When driver_override is set,only bind to the matching driver*/
    if (pdev->driver_override)
    return !strcmp(pdev->driver_override, drv->name);

/* Attempt an OF style match first */
    if (of_driver_match_device(dev, drv))
    return 1;

/* Then try ACPI style match */
    if (acpi_driver_match_device(dev, drv))
    return 1;

/* Then try to match against the id table */
    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);
}

 

采用四种方式匹配:OF 类型的匹配,ACPI 匹配方式,id_table 匹配, name 字段

(2)platform驱动

驱动使用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;
    bool prevent_deferred_probe;
};

 

  probe 函数,当驱动与设备匹配成功以后 probe 函数就会执行。driver 成员,为 device_driver 结构体变量, Linux 内核里面大量使用到了面向对象的思维, device_driver 相当于基类,提供了最基础的驱动框架。 plaform_driver 继承了这个基类,然后在此基础上又添加了一些特有的成员变量。
  在编写 platform 驱动的时候,首先定义一个 platform_driver 结构体变量,然后实现结构体中的各个成员变量,重点是实现匹配方法以及 probe 函数。当驱动和设备匹配成功以后 probe函数就会执行,具体的驱动程序在 probe 函数里面编写,比如字符设备驱动等等。
当我们定义并初始化好 platform_driver 结构体变量以后,需要在驱动入口函数里面调用platform_driver_register 函数向 Linux 内核注册一个 platform 驱动, platform_driver_register 函数原型如下所示 :

int platform_driver_register (struct platform_driver *driver)

 

(3)platform设备

如果内核支持设备树不需要使用platform_device 来描述设备,可以使用设备树描述设备。

struct platform_device {
    const char    *name;
    int        id;
    bool        id_auto;
    struct device    dev;
    u64        platform_dma_mask;
    u32        num_resources;
    struct resource    *resource;

    const struct platform_device_id    *id_entry;
    char *driver_override; /* Driver name to force a match */

    /* MFD cell pointer */
    struct mfd_cell *mfd_cell;

    /* arch specific additions */
    struct pdev_archdata    archdata;
};

  name 表示设备名字,要和所使用的 platform 驱动的 name 字段相同,否则的话设备就无法匹配到对应的驱动;

  num_resources 表示资源数量;

  resource 表示资源,也就是设备信息 

struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    struct resource *parent, *sibling, *child;
};

使用platform_device_register() 将设备信息注册到内核中,

int platform_device_register(struct platform_device *pdev)

 

当无设备树的时候,需要编写设备注册文件,使用platform_device_register()注册设备

当有设备输时,编写设备树设备节点,通过of函数读取设备树。设备信息可以从设备树转化为platform_device结构体,可以通过platform_get_resource()函数进行获取。

 

platform流程

 

 

平台设备

由struct platform_device来描述

struct platform_device{

  const char *name;  //设备名

  int id;  //设备id

  struct device dev;

  u32 num_resources;

  struct resource *resource;  //设备资源

}

 

其中struct resource

struct resource {
    resource_size_t start;   //资源的起始物理地址 
    resource_size_t end;   //资源的结束物理地址 
    const char      *name;
    unsigned long   flags;   //资源类型,如MEN、IO、IRQ类型
    struct resource *parent, *sibling, *child;    //资源链表指针
};    

 

struct platform_device由platform_device_alloc()进行分配

struct platform_device *platform_devcie_alloc(const char *name, int id)

 

获取设备资源,设备信息,比如外设寄存器。

struct resource *platform_get_resource(struct platform_device *dev,
                        unsigned int type, unsigned int num) //dev:资源所属的设备 //type:获取资源类型 //num:获取的资源数

例如:

paltform_get_resource(pdev, IORESOURCE_IRQ, 0)    //获取中断号

 

由platform_device_add()进行注册

int platform_devcie_add(struct platform_devcie *pdev)

 

 

平台驱动

由platdorm_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;
};

 

平台驱动注册函数

int platform_driver_register(struct platform_driver *)

 

platform_device.c

#include <linux/device.h>
#include <linux/modele.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/platform.h>
//#include <linux/modele.h>

MODULE_LICENSE("Dual BSD/GPL");

static struct platform_device *my_device;

static int __init my_device_init(void)
{
    int ret = 0;

    my_device = platform_device_alloc("my_dev", -1);    //  分配结构
    ret = platform_device_add(my_device);   //注册结构
    if(ret)
    {
        platform_device_put(my_device);
    }
    return ret;
}

static void my_device_exit(void)
{
    platform_device_unregister(my_device);
}

module_init(my_device_init);
module_exit(my_device_exit);

 

platform_drivier.c

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

static int my_probe(struct device *dev)
{
    printk("Driver found device which my driver can handle!\n");
    return 0;
}

static int my_remove(struct device *dev)
{
    printk("Driver found device unpluged!\n");
    return 0;
}

static struct platform_driver my_driver = {
    .probe = my_probe,
    .remove = my_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "my_dev",
    },
};

static int __init my_driver_init(void)
{
    return platform_driver_register(&my_driver);
}

static void my_driver_exit(void)
{
    platform_driver_unregister(&my_driver);
}

module_init(my_driver_init);
module_exit(my_driver_exit);

 

make platform_device.c 成功

insmod platform_device.ko

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM