platform设备驱动实验

436 阅读4分钟

一、设备、驱动、总线分离模型

二、platform平台驱动模型简介

SOC中有I2C、SPI、USB等总线,但是在SOC中有些外设是没有总线这个概念的,因此linux提出了platform这个虚拟总线,相应的就有platform_driver和platform_device。

1. platform虚拟总线

  • 所有总线结构体
/* include/linux/device.h 总线结构体 */
    struct bus_type {
    const char *name; /* 总线名字 */
    const char *dev_name;
    struct device *dev_root;
    struct device_attribute *dev_attrs;
    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);I.MX6U 嵌入式 Linux 驱动开发指南
    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(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;
};
其中的match函数就是设备与驱动的匹配函数。
  • 总线结构体实例之platform总线结构体
/*  drivers/base/platform.c platform总线实例 */
    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总线结构体中的匹配函数
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);
}
1. 先比较platform_device.driver_override和platform_driver.driver.name
2. 然后采用of类型匹配即设备树的匹配方式。device_driver结构体中利用of_match_table成员变量保存compatible匹配表与设备树中的compatible属性进行比较匹配。
3. ACPI匹配
4. id_table匹配 每个platform_driver中有一个id_table成员变量,内部存放着该驱动所支持的驱动类型。
5. 比较驱动与设备的name

2. platform驱动

/* include/linux/platform_device.h */
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函数。非常重要!
dirver成员:Linux内核里面大量使用到了面向对象的思维,device_driver相当于基类,提供了最基础的驱动框架。plaform_driver继承了这个基类,然后在此基础上又添加了一些特有的成员变量。
id table表:匹配的第3中方法。
struct platform_device_id {
    char name[PLATFORM_NAME_SIZE];
    kernel_ulong_t driver_data;
};
  • 基类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
  • of_device_id
struct of_device_id {
char name[32];
char type[32];
char compatible[128];
const void *data;
};
compatible:实现与设备数中compatible的匹配
  • platform驱动模板
/* 设备结构体 */
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("ht");

驱动模板总结:

  1. 将之前字符设备驱动模板中init函数的内容放入probe中;
  2. 将之前字符设备驱动模板中exit函数的内容放入remove中;
  3. 构建匹配列表匹配设备树;
  4. 利用platform_driver结构体整合;
  5. init和exit函数分别注册、注销platform_driver即可;

3. platform设备(老式)

22 struct platform_device {
23 const char *name;
24 int id;
25 bool id_auto;
26 struct device dev;
27 u32 num_resources;
28 struct resource *resource;
29
30 const struct platform_device_id *id_entry;
31 char *driver_override; /* Driver name to force a match */
32
33 /* MFD cell pointer */
34 struct mfd_cell *mfd_cell;
35
36 /* arch specific additions */
37 struct pdev_archdata archdata;
38 };

23 name:要与platform驱动的name字段一致,用于bus中将dev与drv匹配函数;