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

二、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");
驱动模板总结:
- 将之前字符设备驱动模板中init函数的内容放入probe中;
- 将之前字符设备驱动模板中exit函数的内容放入remove中;
- 构建匹配列表匹配设备树;
- 利用platform_driver结构体整合;
- 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匹配函数;