專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

platform平臺(tái)總結(jié)

作者:龔平   來源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時(shí)間:2014年03月14日   【字體:

總線設(shè)備驅(qū)動(dòng)模型主要包含總線、設(shè)備、驅(qū)動(dòng)三個(gè)部分,總線可以是一條真實(shí)存在的總線,例如USB、I2C等典型的設(shè)備。但是對(duì)于一些設(shè)備(內(nèi)部的設(shè)備)可能沒有現(xiàn)成的總線。Linux 2.6內(nèi)核中引入了總線設(shè)備驅(qū)動(dòng)模型�?偩€設(shè)備驅(qū)動(dòng)模型與之前的三類驅(qū)動(dòng)(字符、塊設(shè)備、網(wǎng)絡(luò)設(shè)備)沒有必然的聯(lián)系。設(shè)備只是搭載到了總線中。在 linux內(nèi)核中假設(shè)存在一條虛擬總線,稱之為platform總線。

platform總線相比與常規(guī)的總線模型其優(yōu)勢(shì)主要是platform總線是由內(nèi)核實(shí)現(xiàn)的,而不用自己定義總線類型,總線設(shè)備來加載總線。platform總線是內(nèi)核已經(jīng)實(shí)現(xiàn)好的。只需要添加相應(yīng)的platform device和platform driver。具體的實(shí)現(xiàn)過程主要包括如下的過程:

 

整體而言只需要完成兩個(gè)步驟,也就是設(shè)備的實(shí)現(xiàn)和驅(qū)動(dòng)的實(shí)現(xiàn),每一個(gè)實(shí)現(xiàn)都包括相關(guān)結(jié)構(gòu)體的定義和注冊(cè)。

platform_device注冊(cè)

需要注意的是platform_device 實(shí)質(zhì)上是經(jīng)過處理過的設(shè)備,在platform_device結(jié)構(gòu)體中存在一個(gè)設(shè)備結(jié)構(gòu)體,與之前的設(shè)備存在差別的是引入了設(shè)備資源。這些設(shè)備資源就能實(shí)現(xiàn)對(duì)設(shè)備寄存器,中斷等資源的訪問。平臺(tái)設(shè)備的基本結(jié)構(gòu)體如下:

struct platform_device {

   /*設(shè)備名*/
    const char    * name;
    /*設(shè)備ID號(hào)*/

    int        id;

   /*結(jié)構(gòu)體包含一個(gè)具體的device結(jié)構(gòu)體*/
    struct device    dev;

    /*資源的數(shù)量*/
    u32        num_resources;

    /*資源結(jié)構(gòu)體,用來保存硬件的資源*/
    struct resource    * resource;

    /*平臺(tái)設(shè)備的ID*/
    struct platform_device_id    *id_entry;
};

其中struct device 和 struct resource 是重要的結(jié)構(gòu)體。struct device 在總線設(shè)備驅(qū)動(dòng)模型中已經(jīng)提到了。這次討論一下struct resource。

struct resource {
    /*資源的起始值,如果是地址,那么是物理地址,不是虛擬地址*/
    resource_size_t start;
    /*資源的結(jié)束值,如果是地址,那么是物理地址,不是虛擬地址*/
    resource_size_t end;
    /*資源名*/
    const char *name;
    /*資源的標(biāo)示,用來識(shí)別不同的資源*/
    unsigned long flags;
    /*資源指針,可以構(gòu)成鏈表*/
    struct resource *parent, *sibling, *child;
};

platform_device 的注冊(cè)很簡(jiǎn)單,只需要在設(shè)備的初始化函數(shù)中首先定義相應(yīng)的設(shè)備,通常采用函數(shù)platform_device *platform_device_alloc(const char *name, int id)動(dòng)態(tài)申請(qǐng),通常name就是需要申請(qǐng)的設(shè)備名,而id為-1。然后采用

int platform_device_add(struct platform_device *pdev)

或者

int platform_device_register(struct platform_device *pdev)

注冊(cè)定義好的設(shè)備即可。

同樣在退出函數(shù)中釋放注冊(cè)好的設(shè)備即可,可以采用函數(shù):

void platform_device_unregister(struct platform_device *pdev)。

然后一個(gè)平臺(tái)設(shè)備就完成了,不需要像自己實(shí)現(xiàn)模型時(shí)定義相關(guān)的文件屬性等。

設(shè)備資源可以通過相關(guān)函數(shù)得到:

struct resource *platform_get_resource(struct platform_device *dev,
                       unsigned int type, unsigned int num)

中斷資源也可以通過:

int platform_get_irq(struct platform_device *dev, unsigned int num)

資源的使用主要是驅(qū)動(dòng)實(shí)現(xiàn)過程中需要使用到的,但是后期的使用一般需要在驅(qū)動(dòng)的probe函數(shù)中實(shí)現(xiàn)申請(qǐng)中斷或者IO內(nèi)存才能使用,而不能直接使用。特別是資源中的地址通常是物理地址,需要通過申請(qǐng)IO內(nèi)存和映射完成物理到虛擬地址的轉(zhuǎn)換,便于進(jìn)程的訪問。

platform_driver注冊(cè)

平臺(tái)驅(qū)動(dòng)結(jié)構(gòu)體platform_driver實(shí)現(xiàn)如下:

struct platform_driver {

    /*平臺(tái)驅(qū)動(dòng)需要實(shí)現(xiàn)的相關(guān)函數(shù)操作,

    其中的前4個(gè)函數(shù)與最后一個(gè)函數(shù)與device_driver中的函數(shù)是相同的

   本質(zhì)是實(shí)現(xiàn)對(duì)device_driver 中相關(guān)函數(shù)的賦值。

   */
    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 *);
    /*內(nèi)嵌了一個(gè)設(shè)備驅(qū)動(dòng)結(jié)構(gòu)體*/
    struct device_driver driver;
    /*平臺(tái)設(shè)備ID,這與platform_device中的struct platform_device_id *id_entry是相同的

     主要是完成總線的匹配操作,platform總線的匹配操作第一匹配要素就是該元素。而不再是簡(jiǎn)單的name選項(xiàng)。

   */
    struct platform_device_id *id_table;
};

通常驅(qū)動(dòng)的入口函數(shù):

int (*probe)(struct platform_device *);
當(dāng)總線完成了設(shè)備的match 操作以后就會(huì)進(jìn)入驅(qū)動(dòng)中該函數(shù)的運(yùn)行。

總線函數(shù)的匹配操作如下:

static int platform_match(struct device *dev, struct device_driver *drv)
{

    /*得到平臺(tái)設(shè)備的指針*/
    struct platform_device *pdev = to_platform_device(dev);

    /*得到平臺(tái)驅(qū)動(dòng)指針*/
    struct platform_driver *pdrv = to_platform_driver(drv);

    /* match against the id table first */

    /*從定義上分析,id_table是首先匹配的對(duì)象,然后才是name的匹配,當(dāng)ID匹配完成時(shí)就說明匹配好了*/
    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);
}

 

從上面的定義可以知道platform總線的匹配函數(shù)手下是比較id_table是匹配的首選項(xiàng)。

probe 函數(shù)稱之為探針函數(shù),用于檢測(cè)總線上有該驅(qū)動(dòng)能夠處理的設(shè)備,而remove函數(shù)則是為了說明總線上該驅(qū)動(dòng)能夠處理的設(shè)備被移除。

因此這兩個(gè)函數(shù)是在平臺(tái)設(shè)備中一定要被實(shí)現(xiàn)的函數(shù)。

其他的函數(shù)則不一樣要求實(shí)現(xiàn)。

平臺(tái)驅(qū)動(dòng)的設(shè)計(jì)主要是完成平臺(tái)驅(qū)動(dòng)結(jié)構(gòu)體的填充和注冊(cè)。

通常的平臺(tái)驅(qū)動(dòng)結(jié)構(gòu)體實(shí)現(xiàn)如下:

static struct platform_driver my_driver =
{
        /*平臺(tái)驅(qū)動(dòng)的probe函數(shù)實(shí)現(xiàn)*/
        .probe = my_probe,
        /*平臺(tái)驅(qū)動(dòng)的remove函數(shù)實(shí)現(xiàn)*/
        .remove = my_remove,
        /*實(shí)現(xiàn)設(shè)備驅(qū)動(dòng)的name和owner變量*/
        .driver =
        {
                /*該參數(shù)主要實(shí)現(xiàn)總線中的匹配函數(shù)調(diào)用*/
                .name = "my_dev",
                /*該函數(shù)表示模塊的擁有者*/
                .owner = THIS_MODULE,
        },
};

其中的my_probe和my_remove是自己定義的probe和remove函數(shù)。

最主要的是內(nèi)嵌設(shè)備驅(qū)動(dòng)結(jié)構(gòu)體的填充,主要的填充包括name和owner兩個(gè),當(dāng)然也可以包括其他的。由于沒有填充id_table,那么 name就是總線匹配操作的第一選擇。因此如果沒有填充好id_table,那么name元素是一定要實(shí)現(xiàn)的,不然不能完成相應(yīng)的設(shè)備驅(qū)動(dòng)匹配操作。

完成platform_driver結(jié)構(gòu)體的填充過后就是完成驅(qū)動(dòng)的在初始化階段的注冊(cè)以及退出階段的釋放操作,基本的實(shí)現(xiàn)函數(shù)為:

注冊(cè)函數(shù),通常在驅(qū)動(dòng)初始化函數(shù)中調(diào)用:

int platform_driver_register(struct platform_driver *drv)

釋放函數(shù),通常在驅(qū)動(dòng)退出函數(shù)調(diào)用:

void platform_driver_unregister(struct platform_driver *drv)

完成相關(guān)的注冊(cè)以后總線、設(shè)備、驅(qū)動(dòng)的大概框架就完成啦。

但是這只是常用的框架,還不能在應(yīng)用程序中使用。

基于平臺(tái)驅(qū)動(dòng)的設(shè)備驅(qū)動(dòng)都是基于總線架構(gòu)的,基本的實(shí)現(xiàn)過程與之前的簡(jiǎn)單字符設(shè)備存在較大的差別,主要的區(qū)別在驅(qū)動(dòng)的初始化不在是平臺(tái)設(shè)備驅(qū)動(dòng)的初始化函數(shù)中實(shí)現(xiàn),而是在probe函數(shù)中實(shí)現(xiàn)。而驅(qū)動(dòng)的卸載函數(shù)則是在remove函數(shù)中實(shí)現(xiàn)。probe函數(shù)是平臺(tái)總線實(shí)現(xiàn)匹配以后首先被調(diào)用的函數(shù),因此在其中實(shí)現(xiàn)字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)的初始化是有意義的,這樣的設(shè)備驅(qū)動(dòng)就是基于平臺(tái)總線的設(shè)備驅(qū)動(dòng),便于維護(hù)。

平臺(tái)總線驅(qū)動(dòng)的注冊(cè)過程分析:

int platform_driver_register(struct platform_driver *drv)
{

   /*第一步,仍然是完成結(jié)構(gòu)體的填充操作*/

    /*驅(qū)動(dòng)的總線類型*/
    drv->driver.bus = &platform_bus_type;

   /*將自己定義的probe函數(shù)賦值給平臺(tái)驅(qū)動(dòng)中設(shè)備驅(qū)動(dòng)的probe函數(shù),其他函數(shù)類似*/
    if (drv->probe)
        drv->driver.probe = platform_drv_probe;
    if (drv->remove)
        drv->driver.remove = platform_drv_remove;
    if (drv->shutdown)
        drv->driver.shutdown = platform_drv_shutdown;
    if (drv->suspend)
        drv->driver.suspend = platform_drv_suspend;
    if (drv->resume)
        drv->driver.resume = platform_drv_resume;

    /*第二步,仍然是完成一般設(shè)備驅(qū)動(dòng)的注冊(cè)操作*/

    /*然手就是一般驅(qū)動(dòng)的注冊(cè),這樣就完成了設(shè)備的注冊(cè)*/
    return driver_register(&drv->driver);
}

/*設(shè)備驅(qū)動(dòng)的probe函數(shù)的賦值過程*/

static int platform_drv_probe(struct device *_dev)
{

    /*得到設(shè)備對(duì)應(yīng)的平臺(tái)驅(qū)動(dòng)*/
    struct platform_driver *drv = to_platform_driver(_dev->driver);

   /*得到設(shè)備的平臺(tái)設(shè)備*/
    struct platform_device *dev = to_platform_device(_dev);

  /*下面的probe是自己實(shí)現(xiàn)的probe函數(shù)。具體的實(shí)現(xiàn)思路:

     根據(jù)一般設(shè)備找對(duì)應(yīng)的平臺(tái)設(shè)備,同時(shí)根據(jù)設(shè)備的驅(qū)動(dòng)找到平臺(tái)驅(qū)動(dòng)。

    然后返回平臺(tái)驅(qū)動(dòng)的probe函數(shù)(自己實(shí)現(xiàn)通常是初始化操作)地址。

  */
    return drv->probe(dev);
}

實(shí)現(xiàn)的總線平臺(tái)驅(qū)動(dòng)模型的最簡(jiǎn)單源碼:

平臺(tái)設(shè)備的實(shí)現(xiàn):device.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>

/*平臺(tái)模型驅(qū)動(dòng)的平臺(tái)設(shè)備對(duì)象*/
static struct platform_device *my_device;

/*初始化函數(shù)*/
static int __init my_device_init(void)
{
        int ret = 0;

        /*采用platform_device_alloc分配一個(gè)platform_device對(duì)象
          參數(shù)分別為platform_device的name,和id。
        */
        my_device = platform_device_alloc("my_dev",-1);

        /*注冊(cè)設(shè)備,注意不是platform_device_register,將平臺(tái)設(shè)備注冊(cè)到內(nèi)核中*/
        ret = platform_device_add(my_device);

        /*如果出錯(cuò)釋放相關(guān)的內(nèi)存單元*/
        if(ret)
        {
                platform_device_put(my_device);
        }

        return ret;
}

/*卸載處理函數(shù)*/
static void __exit my_device_exit(void)

{
        platform_device_unregister(my_device);
}

module_init(my_device_init);
module_exit(my_device_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("GP-<gp19861112@yahoo.com.cn>");

 

平臺(tái)驅(qū)動(dòng)的實(shí)現(xiàn):driver.c

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

 

/*平臺(tái)驅(qū)動(dòng)中的probe和remove函數(shù)是必須實(shí)現(xiàn)的函數(shù)*/

/*設(shè)備驅(qū)動(dòng)的探測(cè)函數(shù),主要實(shí)現(xiàn)檢測(cè)總線上是否有該驅(qū)動(dòng)對(duì)應(yīng)的設(shè)備*/
static my_probe(struct device *dev)
{

        /*

           如果添加實(shí)際的設(shè)備到該平臺(tái)總線設(shè)備驅(qū)動(dòng)模型中,則可以在該函數(shù)

           中實(shí)現(xiàn)具體的設(shè)備驅(qū)動(dòng)函數(shù)的初始化操作,包括設(shè)備號(hào)的申請(qǐng),設(shè)備

            的初始化,添加。自動(dòng)設(shè)備文件創(chuàng)建函數(shù)的添加等操作。

            或者是混雜字符設(shè)備的相關(guān)初始化操作。當(dāng)然結(jié)構(gòu)體的相關(guān)處理仍

            然采取全局變量的形式。

        */
        printk("Driver found devices which this driver can be handle\n");
        return 0;
}

/*設(shè)備驅(qū)動(dòng)的移除函數(shù),主要檢測(cè)該驅(qū)動(dòng)支持設(shè)備的移除活動(dòng)檢測(cè)*/
static my_remove(struct device *dev)
{

        /*

            如果添加實(shí)際的設(shè)備到該平臺(tái)總線設(shè)備驅(qū)動(dòng)模型中,則可以在該函數(shù)

            中實(shí)現(xiàn)具體的設(shè)備的釋放,包括設(shè)備的刪除,設(shè)備號(hào)的注銷等操作。

       */
        printk("Driver found device unpluded\n");
        return 0;
}

static struct platform_driver my_driver =
{
        /*平臺(tái)驅(qū)動(dòng)的probe函數(shù)實(shí)現(xiàn)*/
        .probe = my_probe,
        /*平臺(tái)驅(qū)動(dòng)的remove函數(shù)實(shí)現(xiàn)*/
        .remove = my_remove,
        /*實(shí)現(xiàn)設(shè)備驅(qū)動(dòng)的name和owner變量*/
        .driver =
        {
                /*該參數(shù)主要實(shí)現(xiàn)總線中的匹配函數(shù)調(diào)用*/
                .name = "my_dev",
                /*該函數(shù)表示模塊的擁有者*/
                .owner = THIS_MODULE,
        },
};

/*初始化函數(shù)*/
static int __init my_driver_init(void)
{
        /*注冊(cè)平臺(tái)驅(qū)動(dòng)*/
        return platform_driver_register(&my_driver);
}
/*退出函數(shù)*/
static void __exit my_driver_exit(void)
{
        /*注銷平臺(tái)驅(qū)動(dòng)*/
        return platform_driver_unregister(&my_driver);
}

/*加載和卸載*/
module_init(my_driver_init);
module_exit(my_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("GP-<gp19861112@yahoo.com.cn>");

 

將一般設(shè)備驅(qū)動(dòng)加入到總線設(shè)備模型中的相關(guān)操作是后期總結(jié)和學(xué)習(xí)的內(nèi)容。設(shè)備驅(qū)動(dòng)實(shí)現(xiàn)的實(shí)現(xiàn)原理還是之前的那些操作,但是初始化和推出函數(shù)發(fā)生了改變。

總結(jié):

platform總線的驅(qū)動(dòng)模型只是在一般總線模型的基礎(chǔ)上做了相關(guān)的延伸,實(shí)質(zhì)上只要弄清除總線模型的一般原理,學(xué)習(xí)platform總線也就簡(jiǎn)單不少。但是畢竟還是學(xué)習(xí)階段。

關(guān)閉窗口

相關(guān)文章