一. 简介
前面一篇文章简单学习了 Linux内核中platform驱动代码。文章地址如下:
Linux下platform驱动简介-CSDN博客
本文学习编写 platform驱动框架代码。
二. Linux下platform驱动框架编写
1. 编写platform驱动代码的思路
(1) 定义结构体以及实现匹配方法以及 probe 函数
在编写 platform 驱动的时候,首先定义一个 platform_driver 结构体变量。
然后,实现结构体中的各个成员变量,重点是实现匹配方法以及 probe 函数。
当驱动和设备匹配成功以后, probe 函数就会执行,具体的驱动程序在 probe 函数里面编写,比如,字符设备驱动等等。
(2) 向Linux内核注册一个platform驱动
当我们定义并初始化好 platform_driver 结构体变量以后,需要在驱动入口函数里面调用
platform_driver_register 函数向 Linux 内核注册一个 platform 驱动。
platform_driver_register 函数 原型如下所示:
int platform_driver_register (struct platform_driver *driver)
函数参数和返回值含义如下:
driver :要注册的 platform 驱动。
返回值: 负数,失败; 0 ,成功。
(3)卸载platform驱动
还需要在驱动卸载函数中,通过 platform_driver_unregister 函数卸载 platform 驱动,
platform_driver_unregister 函数原型如下:
void platform_driver_unregister(struct platform_driver *drv)
函数参数和返回值含义如下:
drv :要卸载的 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 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("WeiWuXian");
驱动代码说明如下:
第 1~27 行,传统的字符设备驱动,所谓的 platform 驱动并不是独立于字符设备驱动、块设备驱动和网络设备驱动之外的其他种类的驱动。
platform 只是为了驱动的分离与分层而提出来的一种框架,其驱动的具体实现还是需要字符设备驱动、块设备驱动或网络设备驱动。
第 33~39 行, xxx_probe 函数,当驱动和设备匹配成功以后此函数就会执行,以前在驱动入口 init 函数里面编写的字符设备驱动程序,就全部放到此 probe 函数里面。比如,注册字符设备 驱动、添加 cdev 、创建类等等。
第 41~47 行, xxx_remove 函数, platform_driver 结构体中的 remove 成员变量,当关闭 platform
备驱动时,此函数就会执行,以前在驱动卸载 exit 函数里面要做的事情就放到此函数中来。 比如,使用 iounmap 释放内存、删除 cdev ,注销设备号等等。
第 50~53 行, xxx_of_match 匹配表,如果使用设备树的话,将通过此匹配表进行驱动和设备 的匹配。
第 51 行设置了一个匹配项,此匹配项的 compatible 值为“ xxx-gpio ”,因此当设备树中 设备节点的 compatible 属性值为“ xxx-gpio ”的时候此设备就会与此驱动匹配。
第 52 行是一个标记,of_device_id 表最后一个匹配项必须是空的。
第 58~65 行,定义一个 platform_driver 结构体变量 xxx_driver ,表示 platform 驱动。
第 59~62 行设置 paltform_driver 中的 device_driver 成员变量的 name 和 of_match_table 这两个属性。
其中 name 属性用于传统的驱动与设备匹配,也就是检查驱动和设备的 name 字段是不是相同。
of_match_table 属性就是用于设备树下的驱动与设备检查。
对于一个完整的驱动程序,必须提供 有设备树和无设备树两种匹配方法。
最后 63 和 64 这两行设置 probe 和 remove 这两成员变量。
第 68~71 行,驱动入口函数,调用 platform_driver_register 函数向 Linux 内核注册一个 platform 驱动,也就是上面定义的 xxx_driver 结构体变量。
第 74~77 行,驱动出口函数,调用 platform_driver_unregister 函数卸载前面注册的 platform 驱动。
三. 总结
总体来说, platform 驱动还是传统的字符设备驱动、块设备驱动或网络设备驱动,只是套上了一张“ platform ”的皮,目的是为了使用总线、驱动和设备这个驱动模型来实现驱动的分 离与分层。