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