4.1.2. platform驱动

4.1.2.1. 数据结构

linux内核使用bus_type结构体表示总线

       [include/linux/device.h]
   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,
        .dma_configure  = platform_dma_configure,
        .pm     = &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

linux设备驱动模型下的总线都有两个重要的结构体:描述设备和驱动的结构体

[include/linux/platform_device.h]
struct platform_device {
        const char  *name;
        int     id;
        bool        id_auto;
    struct device   dev;      //嵌入device结构体
    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;
};
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;          //嵌入device_driver结构体
        const struct platform_device_id *id_table;
        bool prevent_deferred_probe;
};

4.1.2.2. platform驱动和设备的匹配

platform_bus_type是platform平台总线,其中platfrom_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);

        /* 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);
}

4.1.2.2.1. of设备树匹配

OF类型的匹配,也就是设备树采用的匹配方式,Of_driver_match_device函数在文件include/linux/of_device.h中,device_driver结构体(表示设备驱动)中有个名为 of_match_table的成员变量,此成员变量保存着驱动的compatible匹配表,设备树中每个设备节点的compatible属性会和of_match_table表中的所有成员进行比较,查看 是否有相同的条目,如果有则表示设备和驱动匹配,匹配成功后probe函数就会执行

函数原型如下

/**
 * of_driver_match_device - Tell if a driver's of_match_table matches a device.
 * @drv: the device_driver structure to test
 * @dev: the device structure to match against
 */
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;
}

device_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;
};

of_match_table就是采用设备树的时候驱动使用的匹配表,类型为of_device_id

 struct of_device_id {

         char name[32];
         char type[32];
         char compatible[128];
         const void *data;
};

示例如下

static const struct of_device_id omap_rtc_of_match[] = {
        {
                .compatible = "ti,am3352-rtc",
                .data       = &omap_rtc_am3352_type,
        }, {
                .compatible = "ti,da830-rtc",
                .data       = &omap_rtc_da830_type,
        }, {
                /* sentinel */
        }
};
MODULE_DEVICE_TABLE(of, omap_rtc_of_match);
static struct platform_driver omap_rtc_driver = {
        .probe      = omap_rtc_probe,
        .remove     = omap_rtc_remove,
        .shutdown   = omap_rtc_shutdown,
        .driver     = {
                .name   = "omap_rtc",
                .pm = &omap_rtc_pm_ops,
                .of_match_table = omap_rtc_of_match,
        },
        .id_table   = omap_rtc_id_table,
};

4.1.2.2.2. acpi匹配

如果of_driver_match_device没有匹配到则使用acpi进行匹配。首先从device中找到对应的acpi_device,找到acpi_device设备后就可以进行匹配了。acpi设备使用两种方式匹配

[drivers/acpi/bus.c]
bool acpi_driver_match_device(struct device *dev,
                                      const struct device_driver *drv)
{
        if (!drv->acpi_match_table)
                return acpi_of_match_device(ACPI_COMPANION(dev),
                                                drv->of_match_table,
                                                NULL);

        return __acpi_match_device(acpi_companion_match(dev),
                                       drv->acpi_match_table, drv->of_match_table,
                                       NULL, NULL);
}
EXPORT_SYMBOL_GPL(acpi_driver_match_device);

4.1.2.2.3. id_table匹配

id_table匹配,每个platform_driver结构体有一个id_table成员变量,顾名思义,保存了很多id信息。这些id信息存放着这个platform驱动所支持的驱动类型

示例如下

static const struct platform_device_id omap_rtc_id_table[] = {
        {
                .name   = "omap_rtc",
                .driver_data = (kernel_ulong_t)&omap_rtc_default_type,
        }, {
                .name   = "am3352-rtc",
                .driver_data = (kernel_ulong_t)&omap_rtc_am3352_type,
        }, {
                .name   = "da830-rtc",
                .driver_data = (kernel_ulong_t)&omap_rtc_da830_type,
        }, {
                /* sentinel */
        }
};

4.1.2.2.4. name匹配

如果前三种方式都不存在的话,就直接比较驱动和设备的那么字段,看看是否相同

static struct platform_driver omap_rtc_driver = {
        .probe      = omap_rtc_probe,
        .remove     = omap_rtc_remove,
        .shutdown   = omap_rtc_shutdown,
        .driver     = {
                .name   = "omap_rtc", //此字段为device和driver匹配的最后一种方式
                .pm = &omap_rtc_pm_ops,
                .of_match_table = omap_rtc_of_match,
        },
        .id_table   = omap_rtc_id_table,
};

4.1.2.3. platform总线下的驱动编写流程

  1. 首先定义一个platform_driver结构体变量

  2. 然后实现结构体中各个成员变量,重点是实现匹配方式以及probe函数

  3. 当我们定义并初始化好platform_driver结构体变量以后,需要在驱动入口函数里面调用platform_driver_register函数向内核注册一个platform驱动

  4. 驱动卸载函数中通过platform_driver_unregister函数卸载

框架流程如下

struct xxx_dev{

        struct cdev cdev;
        /* 设备结构体其他具体内容 */
};

struct xxx_dev xxxdev; /* 定义个设备结构体变量 */

static int xxx_open(struct inode *inode, struct file *filp)
{

        /* 函数具体内容 */
        return 0;
}

static ssize_t xxx_write(struct file *filp, const char __user *buf,
size_t cnt, loff_t *offt)
{

        /* 函数具体内容 */
        return 0;
}

/*
* 字符设备驱动操作集
*/
static struct file_operations xxx_fops = {

        .owner = THIS_MODULE,
         .open = xxx_open,
        .write = xxx_write,
};

/*
* platform 驱动的 probe 函数
* 驱动与设备匹配成功以后此函数就会执行
*/
static int xxx_probe(struct platform_device *dev)
{

        ......
        cdev_init(&xxxdev.cdev, &xxx_fops); /* 注册字符设备驱动 */
        /* 函数具体内容 */
        return 0;
}

static int xxx_remove(struct platform_device *dev)
{

        ......
        cdev_del(&xxxdev.cdev);/* 删除 cdev */
        /* 函数具体内容 */
        return 0;
}
/* 匹配列表 */
static const struct of_device_id xxx_of_match[] = {

        {
         .compatible = "xxx-gpio" },
        {
         /* Sentinel */ }
};

/*
* platform 平台驱动结构体
*/
static struct platform_driver xxx_driver = {

        .driver = {

        .name = "xxx",
        .of_match_table = xxx_of_match,
        },
        .probe = xxx_probe,
        .remove = xxx_remove,
};

 /* 驱动模块加载 */
static int __init xxxdriver_init(void)
{

        return platform_driver_register(&xxx_driver);
}

/* 驱动模块卸载 */
static void __exit xxxdriver_exit(void)
{

         platform_driver_unregister(&xxx_driver);
}

 module_init(xxxdriver_init);
 module_exit(xxxdriver_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("yinwg");