1. platform设备驱动简介
基于驱动可重用性考虑,提出驱动分离与分层思想。平台设备驱动就是基于此。
1.1 驱动分隔与分层
1)驱动分隔:
以I2C驱动为例,假设有三类SOC,各自对一个设备写I2C驱动,就需要3个驱动程序。但是一个设备理论上只需要一个驱动就行了,将主机驱动和设备驱动分隔开,中间由一个统一API接口连接,就是驱动分隔思想。
2)驱动分离:
实际开发中主机驱动和设备驱动一般由半导体厂家和设备厂家编写好了,我们需要提供的是设备信息。这样驱动只负责驱动,设备只负责设备,中间由总线匹配,这就是Linux的驱动-总线-设备模型,也就是驱动分离。
3)驱动分层:
在不同的层处理不同的内容。比如Input子系统负责所有输入相关的驱动,其最底层是设备原始驱动,获取到的输入传给input核心层处理IO模型并提供file_operations操作集合。我们编写时只需要处理输入事件的上报即可。
1.2 platform(虚拟总线)平台驱动模型
由于有些外设没有总线概念,又需要使用总线-驱动-设备模型,因此提出platform虚拟总线,对应的由platform_driver,platform_device。Linux内核使用bus_type结构体表示总线。
该结构体中最重要的是match函数,该函数根据注册的设备或者驱动查找相应的驱动或者设备,完成驱动和设备之间的匹配。该函数两个参数dev和drv分别为设备和驱动。
1)paltform总线
//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,
};
//设备驱动匹配方式
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);
/*OF类型匹配:设备树采用的匹配方式,依靠device_driver结构体的of_match_table成员变量查找---必有*/
if (of_driver_match_device(dev, drv))
return 1;
/* ACPI方式 */
if (acpi_driver_match_device(dev, drv))
return 1;
/* id_table匹配,依靠platform_driver结构体的id_table成员变量查找 */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* 如果id_table找不到,就直接比较驱动和设备的name字段---常用 */
return (strcmp(pdev->name, drv->name) == 0);
}
2)paltform驱动
Linux内核使用platform_driver结构体表示虚拟驱动。其中重要的有:
1. probe函数,驱动和设备match后就会执行。
2. driver成员,为device_driver结构体成员变量,该结构体相当于platform_driver的基类。
3. id_table,是一个表(数组),元素类型为platform_device_id。
3)paltform驱动编写流程
①定义platform_driver结构体变量。
②实现match和probe。
③match后,在probe里面编写具体驱动。
④在驱动入口函数调用platform_driver_register函数向内核注册一个platform驱动。
//para:要注册的platform驱动
int platform_driver_register (struct platform_driver *driver)
⑤在驱动卸载函数中通过platform_driver_unregister函数卸载驱动
void platform_driver_unregister(struct platform_driver *drv)
框架:
/* 设备结构体 */
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 函数,以前在驱动init函数的内容写到这*/
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" },
{ } /*of_device_id表最后一个匹配项必须为空*/
};
/*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("zuozhongkai");
4)paltform设备
内核使用platform_device结构体表示虚拟设备。在不使用设备树时可以使用该结构体描述设备信息。
//将设备信息注册到内核
//para:要注册的platform设备
int platform_device_register(struct platform_device *pdev)
//注销设备
void platform_device_unregister(struct platform_device *pdev)
设备信息框架(无设备树时使用):
/* 寄存器地址定义*/
#define PERIPH1_REGISTER_BASE (0X20000000) /* 外设 1 寄存器首地址 */
#define PERIPH2_REGISTER_BASE (0X020E0068) /* 外设 2 寄存器首地址 */
#define REGISTER_LENGTH 4
/* 资源 */
static struct resource xxx_resources[] = {
[0] = {
/*resource结构体的属性*/
.start = PERIPH1_REGISTER_BASE,
.end = (PERIPH1_REGISTER_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM, //设备是内存类型
},
};
/* platform 设备结构体 */
static struct platform_device xxxdevice = {
.name = "xxx-gpio",
.id = -1,
.num_resources = ARRAY_SIZE(xxx_resources),
.resource = xxx_resources,
};
/* 设备模块加载 */
static int __init xxxdevice_init(void) {
return platform_device_register(&xxxdevice);
}
/* 设备模块注销 */
static void __exit xxx_resourcesdevice_exit(void) {
platform_device_unregister(&xxxdevice);
}
module_init(xxxdevice_init);
module_exit(xxxdevice_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");