設備樹在platform設備驅動開發中的使用解析

參考文章:http://blog.csdn.net/zqixiao_09/article/details/50889458

由上文 “linux下platform總線驅動” 知,platform驅動開發的流程大致爲總線註冊,設備(platform_device)註冊,驅動註冊(platform_driver)註冊三大部分。
設備樹(device tree)主要用來描述設備信息,每一個設備在設備樹中是以節點的形式表現出來的。linux內核會將設備樹中的設備信息自動構造成platform_device結構

設備樹與platform_device的對比

這裏寫圖片描述
可以看到設備樹中的設備節點完全可以替代掉platform_device。

通過device tree實現device 與 driver 的匹配

之前的platform_device 中,是利用 .name 來實現device與driver的匹配的,但現在設備樹替換掉了device,那我們將如何實現二者的匹配呢?
我們先看一下原來是如何匹配的 ,platform_bus_type 下有個match成員,platform_match 定義如下

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

    /* 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_driver_match_device(dev, drv) ,其定義如下:

static inline int of_driver_match_device(struct device *dev,
                     const struct device_driver *drv)
{
    return of_match_device(drv->of_match_table, dev) != NULL;
}

其調用of_match_device(drv->of_match_table, dev) ,繼續追蹤下去,注意這裏的參數drv->of_match_table

const struct of_device_id *of_match_device(const struct of_device_id *matches,
                       const struct device *dev)
{
    if ((!matches) || (!dev->of_node))
        return NULL;
    return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);

又調用 of_match_node(matches, dev->of_node) ,其中matches 是struct of_device_id 類型的

/**
 * of_match_node - Tell if an device_node has a matching of_match structure
 *  @matches:   array of of device match structures to search in
 *  @node:      the of device structure to match against
 *
 *  Low level utility function used by device matching.
 */
const struct of_device_id *of_match_node(const struct of_device_id *matches,
                     const struct device_node *node)
{
    const struct of_device_id *match;
    unsigned long flags;

    raw_spin_lock_irqsave(&devtree_lock, flags);
    match = __of_match_node(matches, node);
    raw_spin_unlock_irqrestore(&devtree_lock, flags);
    return match;
}
EXPORT_SYMBOL(of_match_node);

找到 match = __of_match_node(matches, node); 注意着裏的node是struct device_node 類型的

const struct of_device_id *__of_match_node(const struct of_device_id *matches,
                       const struct device_node *node)
{
    const struct of_device_id *best_match = NULL;
    int score, best_score = 0;

    if (!matches)
        return NULL;

    for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
        score = __of_device_is_compatible(node, matches->compatible,
                          matches->type, matches->name);
        if (score > best_score) {
            best_match = matches;
            best_score = score;
        }
    }

    return best_match;
}

繼續追蹤下去

static int __of_device_is_compatible(const struct device_node *device,
                     const char *compat, const char *type, const char *name)
{
    struct property *prop;
    const char *cp;
    int index = 0, score = 0;

    /* Compatible match has highest priority */
    if (compat && compat[0]) {
        prop = __of_find_property(device, "compatible", NULL);
        for (cp = of_prop_next_string(prop, NULL); cp;
             cp = of_prop_next_string(prop, cp), index++) {
            if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
                score = INT_MAX/2 - (index << 2);
                break;
            }
        }
        if (!score)
            return 0;
    }

    /* Matching type is better than matching name */
    if (type && type[0]) {
        if (!device->type || of_node_cmp(type, device->type))
            return 0;
        score += 2;
    }

    /* Matching name is a bit better than not */
    if (name && name[0]) {
        if (!device->name || of_node_cmp(name, device->name))
            return 0;
        score++;
    }

    return score;
}

看這句 prop = __of_find_property(device, “compatible”, NULL);
可以發現追溯到底,是利用”compatible”來匹配的,即設備樹加載之後,內核會自動把設備樹節點轉換成 platform_device這種格式,同時把名字放到of_node這個地方。

platform_driver 部分

可以看到原來是利用platform_driver 下的 struct driver 結構體中的 name 成員來匹配的,看一下 struct driver 結構體的定義:

struct device_driver {
    const char      *name;
    struct bus_type     *bus;

    struct module       *owner;
    const char      *mod_name;  /* used for built-in modules */

    bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */

    const struct of_device_id   *of_match_table;
    const struct acpi_device_id *acpi_match_table;

    int (*probe) (struct device *dev);
    int (*remove) (struct device *dev);
    void (*shutdown) (struct device *dev);
    int (*suspend) (struct device *dev, pm_message_t state);
    int (*resume) (struct device *dev);
    const struct attribute_group **groups;

    const struct dev_pm_ops *pm;

    struct driver_private *p;
}

成員中有const struct of_device_id*of_match_table; 是struct of_device_id 類型,定義如下:

/*
 * Struct used for matching a device
 */
struct of_device_id
{
    char    name[32];
    char    type[32];
    char    compatible[128];
    const void *data;
};

可以看到其作用就是爲了匹配一個設備。我們所要做的就是對 char compatible[128] 的填充;設備樹加載之後,內核會自動把設備樹節點轉換成 platform_device這種格式,同時把名字放到of_node這個地方。

基於設備樹的driver的結構體的填充

匹配的方式發生了改變,那我們的platform_driver 也要修改了
基於設備樹的driver的結構體的填充:

static struct of_device_id beep_table[] = {
    {.compatible = "fs4412,beep"},
    {}
};
static struct platform_driver beep_driver=
{
    .probe = beep_probe,
    .remove = beep_remove,
    .driver={
        .name = "bigbang",
        .of_match_table = beep_table,
    },
};

原來的driver是這樣的,可以對比一下

static struct platform_driver beep_driver=
{
    .driver.name = "bigbang",
    .probe = beep_probe,
    .remove = beep_remove,
};

設備樹編譯

我們在 arch/arm/boot/dts/exynos4412-fs4412.dts 中添加

fs4412-beep{
         compatible = "fs4412,beep";
         reg = <0x114000a0 0x4 0x139D0000 0x14>;
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章