介绍linux的platform设备驱动(平台设备驱动)
Linux驱动的分离与分层
驱动的分隔和分离
如果对驱动不进行分隔和分离,则会产生以下影响,如表。
主机驱动 | 设备驱动 |
---|---|
A平台主机驱动 | MPU6050驱动 |
B平台主机驱动 | MPU6050驱动 |
C平台主机驱动 | MPU6050驱动 |
在三个平台A,B,C上均要编写MOU6050驱动,那么此时就要编写三个不同的设备驱动,因此我们可以通过一个统一的接口(主机驱动),每个设备也只提供一个驱动程序(设备驱动),每个设备通过统一的I2C接口驱动来访问,这样就可以大大简化驱动文件。
主机驱动 | 核心层 | 设备驱动 |
---|---|---|
A平台I2C主机驱动 | 统一接口API | I2C设备1(MPU驱动) |
B平台I2C主机驱动 | 统一接口API | I2C设备2) |
C平台I2C主机驱动 | 统一接口API | I2C设备3 |
这样就实现了驱动的分隔,也就是将主机驱动和设备驱动层分割开来,比如I2C和SPI等等都会采用驱动分隔的方式来简化驱动的开发。实际驱动开发中。主机驱动半导体厂家编写好了,设备驱动也由设备器件的厂家编写好了,我们只需要提供设备信息,比如I2C设备的话提供设备连接到了哪个I2C接口上,I2C的速度是多少等等。相当于将设备信息从设备驱动中剥离开来,驱动使用标准方法去获取到设备信息(设备树),也就是驱动负责驱动,设备负责设备。这就是Linux中的总线,驱动,设备模型,如下。
驱动 | 总线 | 设备信息 |
---|---|---|
驱动1 | 总线 | 设备1 |
驱动2 | 总线 | 设备2 |
驱动3 | 总线 | 设备3 |
驱动的分层
linux驱动是分层的,分层的目的是为了在不同的层处理不同的内容。
platform平台驱动模型简介
由于soc中有些外设是没有总线这个概念,但是又要使用到总线,驱动和设备模型。为此,linux提出了platform这个虚拟总线,相应就有platform_driver 和 platform_device。
platform 总线
linux系统内核使用bus_type结构体表示总线,此结构体定义在文件include/linux/device.h
驱动和设备匹配方法有四种
- of类型匹配。
- ACPI匹配方式
- id_table匹配
- 驱动和设备的name字段匹配
platform驱动
platform_driver结构体表示platform驱动,定义在文件include/linux/platform_device.h
struct platform_driver{
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
int (*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函数就会执行,非常重要的函数。
driver成员,为device_driver结构体变量,device_driver相当于基类,提供了最基础的驱动框架。platform_driver继承了这个基类,然后在此基础上又添加了一些特有的成员变量。
编写platform驱动的时候,首先定义一个platform_driver结构体变量,然后实现结构中的各个成员变量,重点是实现匹配方法和probe函数。当驱动和设备匹配成功后probe函数就会执行。当我们定义好platform_driver结构体变量以后,需要在驱动入口函数里面调用platform_driver_register函数向linux内核注册一个platform驱动,同样需要在驱动卸载函数中通过platform_driver_unregister函数卸载platform驱动。
这里写一个platform驱动框架
/**
*platform驱动并不是独立于字符设备驱动,块设备驱动,网络设备驱动
*/
struct xxx_dev{
struct cdev cdev;
/* 设备结构体其他具体内容 */
};
struct xxx_dev xxxdev; //设备实例化
static int xxx_open(struct inode *inode,struct file *filp){
return 0;
}
static int 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)'
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 int __exit xxxdriver_exit(void){
platform_driver_unregister(&xxx_driver);
}
module_init(xxxdriver_init);
module_exit(xxxdriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Domic");
platform设备
platform 驱动写好后还需要platform设备,否则单单一个驱动也做不了什么。如果支持设备树就不用使用platform_device来描述设备,因为改用设备树去描述了,
platform_device这个结构体表示platform设备
platform_device结构体定义在文件include/linux/platform_device.h
struct platform_device{
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const char platform_device_id *id_entry;
char *driver_override;
struct mfd_cell *mfd_cell;
struct pdev_archdate archdata;
};
/* 资源resource */
static struct resource xxx_resources[] = {
[0] = {
.start = PERIPH1_REGISTER_BASE,
.end = (PERIPH1_REGISTER_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,
},
[1] = {
.start = PERIPH2_REGISTER_BASE,
.end = (PERIPH2_REGISTER_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,
},
};
static struct platform_device xxxdevice = {
.name = "xxx-gpio", // 和platform_driver 的name 要匹配
.id = -1;
.num_resources = ARRAY_SIZE(xxx_resources),
.resource = xxx_resources,
};
/* 设备模块加载 */
static int __init xxxdevice_init(void){
return platform_driver_register(&xxxdevice);
}
/* 设备模块卸载 */
static int __exit xxxdevice_exit(void){
platform_driver_unregister(&xxxdevice);
}
module_init(xxxdevice_init);
module_exit(xxxdevice_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Domic");