linux总线驱动模型之platform总线驱动模型

文章介绍了Linux中的总线驱动模型,特别是Platform总线的概念,以及为何需要Platform总线来管理和组织硬件驱动。通过设备树的方式,平台驱动模型使得驱动与设备的匹配更加规范。文章还给出了使用Platform总线驱动模型的代码框架,展示了如何注册和使用platform_driver。
摘要由CSDN通过智能技术生成

什么是总线驱动模型

总线在linux中分为物理总线和虚拟总线。物理总线是真实存在的线路,在真是总线上传递数据和命令也会有自己的格式约束。如I2C总线、SPI总线、USB总线等等。以I2C总线为例,在同一组I2C总线上连接着不同的I2C设备,对于物理总线来说,怎么操作跟物理总线的标准有比较强的关系,这是由总线本身决定的;虚拟总线(这里指platform总线)实现了跟物理总线类似的功能,该总线上挂着很多的设备和驱动,并且通过一定的机制实现驱动与设备匹配,这个就是linux在分层思想上的一个体现。

为什么需要platform总线

在很久很久之前,我linux是没有platform总线的,那个时候linux甚至都还没流行起来,每个人写驱动就只需要实现file_operations结构体,里面直接操作硬件,注册它后就可以了(一样工作的很好),直到有一天,一个打工人说,内核中为什么有这么多的类似驱动(如led_am335x、led_2410),这里面不是一模一样的嘛!!他很气愤,表达了不满,然后管理linux源码的大佬们得知了该情况,想了一个办法,设计了一套platform总线,告诉他们,你们用我这个框架,把硬件相关的放到一个.c里(platform_device),驱动放一个.c里(platform_driver),驱动基本上都是通用的,你添加你自己的platform_device不久可以解决了吗?打工人又开开心心的打工去了。(本段故事纯属虚构,如有雷同,纯属巧合)
随后,linux发展得越来越好,每个人都往内核里面添加自己的硬件代码,导致内核非常的臃肿,大部分都是描述硬件的,特别是arm的板子,因为arm内核的特性,很多厂商都设计了不同的soc,自然需要很多.c来描述硬件信息,这时候,维护内核的大佬坐不住了,说:“This whole ARM thing is a f*cking pain in the ass”,从此设备树引入,其实设备树的本质跟platform_device的方式并没有本质区别,都是为了实现分层思想,甚至很多设备树的节点都会转化为platform_device节点(物理总线的转化为相应的物理总线节点)。
说了这么多,相信大家知道为什么需要platform总线了吧

怎么让自己的驱动代码用上platform总线驱动模型

先简单的缕一缕,platform总线驱动模型的工作方式是这样的,当装载一个驱动或者设备时,总线的匹配函数就很在总线中寻找匹配的驱动(本文讲设备树方式),找到后,就执行驱动中提供的probe函数。所以在驱动中,我们就需要注册platform driver,提供设备树匹配选项of_device_id数组。
主要的函数有:

int platform_driver_register (struct platform_driver *driver);
void platform_driver_unregister(struct platform_driver *drv);

主要用到的数据结构有:

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;
};

struct of_device_id {
	char name[32];
	char type[32];
	char compatible[128];
	const void *data;
};

具体的代码(只是代码框架,未真实控制硬件)

该代码设备树只需在设备树根节点添加一个新的节点,新节点的compatible属性为“test,xxx”即可匹配成功


#include <linux/ide.h>
#include <linux/module.h>
#include <linux/platform_device.h>

#define XXX_MAJOR 	200 		/* 主设备号 */
#define XXX_NAME 	"xxx_test" 	/* 设备名 */

struct class * xxx_class; /* 类 */
struct device * xxx_device; /* 设备 */


static int xxx_open(struct inode *inode, struct file *filp)
{
	//通常返回:0 成功;其他 失败
	return 0;
}

static ssize_t xxx_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	//通常返回:读取的字节数;小于0,表示读取失败
	return 0;
}

static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	//通常返回:写入的字节数;小于0,表示写入失败
	return 0;
}

static int xxx_release(struct inode *inode, struct file *filp)
{
	//通常返回:0 成功;其他 失败
	return 0;
}

//本xxx_fops只列出了部分常用的操作
static const struct file_operations xxx_fops = {
	//owner 拥有该结构体的模块的指针,一般设置为 THIS_MODULE
	.owner 		= THIS_MODULE,
	//open 函数用于打开设备文件,用户程序打开设备文件会得到一个设备描述符,并执行驱动指定的open函数(不管驱动有无open函数,用户使用都需要打开)
	.open 		= xxx_open,	
	//read 函数用于读取设备文件,应用执行read时会调用该函数
	.read 		= xxx_read,
	//write 函数用于向设备文件写入(发送)数据,应用执行write时会调用该函数
	.write 		= xxx_write,
	//release 函数用于释放(关闭)设备文件,与应用程序中的 close 函数对应
	.release 	= xxx_release,
};

//probe函数,平台设备匹配完成后执行该函数
//设备与驱动匹配成功之后,设备的很多硬件信息可以从“struct platform_device *dev”中获取
//“struct platform_device *dev”中的硬件信息现在大多由设备树转化而来,也可以使用.c注册一个struct platform_device xxx结构体注册,本文不涉及
static int xxx_probe(struct platform_device *dev)
{
	int ret = 0;
	
	//主设备号为0,自动分配设备号,返回值是分配的设备号
	ret = register_chrdev(XXX_MAJOR, XXX_NAME, &xxx_fops);
	//返回值为负数,注册失败
	if(ret < 0)
	{
		return -1;
	}
	
	//创建类,在/dev目录下生产xxx目录
	xxx_class = class_create(THIS_MODULE, "xxx");
	//创建设备节点,在/dev/xxx/目录下生成xxx_driver设备文件
	xxx_device = device_create(xxx_class, NULL, XXX_MAJOR, NULL, "xxx_driver");

	return 0;
}

//remove函数,平台设备卸载时候执行该函数
static int xxx_remove(struct platform_device *dev)
{
	//注销字符设备驱动
	unregister_chrdev(XXX_MAJOR, XXX_NAME);
	
	//销毁设备节点,会删除/dev/xxx/目录下的xxx_driver设备文件
	device_destroy(xxx_class, XXX_MAJOR);
	//销毁类,会删除/dev目录下的xxx目录
	class_destroy(xxx_class);
	
	return 0;
}

//设备树匹配列表,需要在设备树的compatible属性设置为test,xxx,就能与设备匹配了
static const struct of_device_id xxx_of_match[] = {
	{.compatible = "test,xxx"},
	{}
};

//platform 平台驱动结构体
static struct platform_driver xxx_driver = {
	.driver = {
		.name = "xxx",
		//这个是用来匹配设备的,本文采用设备树的方式进行匹配,不涉及用.c的方式
		.of_match_table = xxx_of_match,
	},
	.probe = xxx_probe,
	.remove = xxx_remove,
};

//模块初始化函数,在模块装载的时候调用
static int __init xxx_init(void)
{
	return platform_driver_register(&xxx_driver);
}

//模块退出函数,在模块卸载的时候调用
static void __exit xxx_exit(void)
{
	platform_driver_unregister(&xxx_driver);
}

//向内核表明初始化函数,在模块装载的时候调用
module_init(xxx_init);
//向内核表明退出函数,在模块卸载的时候调用
module_exit(xxx_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaoShuATao");

注:作者水平有限,如有错误,请大家及时指出,我会第一时间修改,谢谢大家了。
版权说明:可自由转载使用,转载请注明出处。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值