Linux驱动.之platform平台总线驱动层(一)

本文参考,https://blog.youkuaiyun.com/suifen_/article/details/135256555?spm=1001.2014.3001.5502

内核版本:Linux 2.6

设备驱动,分离与分层
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、 platform总线、设备与驱动概念
在Linux 2.6的设备驱动模型中,平台总线模型就是把原来的驱动C文件给分成两个C文件,一个是device.c,一个是driver.c,设备驱动分离思想。关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每 注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。

把稳定不变的放在driver.c里面,需要变得放在devic.c里面。

一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2 C、SPI、uart、PCIE、APB、AHB等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等确不依附于此类总 线。基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,并不是一个物理的总线。

platform驱动框架模型

示例代码54.2.2.5 platform驱动框架
 	/* 设备结构体 */
1  	struct xxx_dev{
   
   
2   	struct cdev cdev;
3   	/* 设备结构体其他具体内容 */
4  	};
5  
6  	struct xxx_dev xxxdev;   /* 定义个设备结构体变量 */
7  
8  	static int xxx_open(struct inode *inode, struct file *filp)
9  	{
   
       
10  	/* 函数具体内容 */
11  	return 0;
12 	}
13 
14 static ssize_t xxx_write(struct file *filp, const char __user *buf, 
size_t cnt, loff_t *offt)
15 	{
   
   
16  	/* 函数具体内容 */
17  	return 0;
18 	}
19 
20 /*
21  * 字符设备驱动操作集
22  */
23 	static struct file_operations xxx_fops = {
   
   
24  	.owner = THIS_MODULE,
25  	.open = xxx_open,
26  	.write = xxx_write,
27 	};
28 
29 /*
30  * platform驱动的probe函数
31  * 驱动与设备匹配成功以后此函数就会执行
32  */
33 	static int xxx_probe(struct platform_device *dev)
34 	{
   
       
35  	......
36  	cdev_init(&xxxdev.cdev, &xxx_fops); /* 注册字符设备驱动 */
37  	/* 函数具体内容 */
38  	return 0;
39 	}
40 
41 	static int xxx_remove(struct platform_device *dev)
42 	{
   
   
43  	......
44  	cdev_del(&xxxdev.cdev);/*  删除cdev */
45  	/* 函数具体内容 */
46  	return 0;
47 	}
48
49 /* 匹配列表 */
50 static const struct of_device_id xxx_of_match[] = {
   
   
51  	{
   
    .compatible = "xxx-gpio" },
52 	 	{
   
    /* Sentinel */ }
53 };
54
55 /* 
56  * platform平台驱动结构体
57  */
58 	static struct platform_driver xxx_driver = {
   
   
59  	.driver = {
   
   
60      	.name       = "xxx",
61      	.of_match_table = xxx_of_match,
62  	},
63  	.probe      = xxx_probe,
64  	.remove     = xxx_remove,
65 	};
66   
67 	/* 驱动模块加载 */
68 	static int __init xxxdriver_init(void)
69 	{
   
   
70  	return platform_driver_register(&xxx_driver);
71 }
72 
73 	/* 驱动模块卸载 */
74 	static void __exit xxxdriver_exit(void)
75 	{
   
    
76      	platform_driver_unregister(&xxx_driver);
77 	}
78 
79 	module_init(xxxdriver_init);
80 	module_exit(xxxdriver_exit);
81 	MODULE_LICENSE("GPL");
82 	MODULE_AUTHOR("zuozhongkai");

第1~27行,传统的字符设备驱动,所谓的platform驱动并不是独立于字符设备驱动、块设备驱动和网络设备驱动之外的其他种类的驱动。platform只是为了驱动的分离与分层而提出来的一种框架,其驱动的具体实现还是需要字符设备驱动、块设备驱动或网络设备驱动。

第33~39行,xxx_probe函数,当驱动和设备匹配成功以后此函数就会执行,以前在驱动入口init函数里面编写的字符设备驱动程序就全部放到此probe函数里面。比如注册字符设备驱动、添加cdev、创建类等等。
第41~47行,xxx_remove函数,platform_driver结构体中的remove成员变量,当关闭platfor备驱动的时候此函数就会执行,以前在驱动卸载exit函数里面要做的事情就放到此函数中来。比如,使用iounmap释放内存、删除cdev,注销设备号等等。
第50~53行,xxx_of_match匹配表,如果使用设备树的话将通过此匹配表进行驱动和设备的匹配。第51行设置了一个匹配项,此匹配项的compatible值为“xxx-gpio”,因此当设备树中设备节点的compatible属性值为“xxx-gpio”的时候此设备就会与此驱动匹配。第52行是一个标记,of_device_id表最后一个匹配项必须是空的。
第5865行,定义一个platform_driver结构体变量xxx_driver,表示platform驱动,第5962行设置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”的皮,目的是为了使用总线、驱动和设备这个驱动模型来实现驱动的分离与分层。

device.c里面写的是硬件资源,这里的硬件资源就是指寄存器的地址,中断号,时钟等硬件资源。在linux内核里面,我们是用一个结构体来描述硬件资源的。

资源的类型一般用下面的几个宏定义:

#define IORESOURCE_IO       0x00000100  //IO的内存
#define IORESOURCE_MEM      0x00000200  //表示一段物理内存
#define IORESOURCE_REG      0x00000300  
#define IORESOURCE_IRQ      0x00000400 //表示中断
#define IORESOURCE_DMA      0x00000800
#define IORESOURCE_BUS      0x00001000

在不支持设备树的 Linux 内核版本中需要在通过 platform_device 结构体来描述设备信息,然后使用 platform_device_register 函数将设备信息注册到 Linux 内核中,此函数原型如下所示:

extern int platform_device_register(struct platform_device *);

相应的设备称为platform_device,而驱动称为 platform_driver。

1、platform_device,它主要用于描述SOC上的片上资源,用于描述设备硬件信息的结构体,包括该硬件的所有资源(io,memory、中断、DMA等等)。platform 所描述的资源有一个共同点:在CPU 的总线上直接取址。平台设备会分到一个名称(用在驱动绑定中)以及一系列诸如地址和中断请求号(IRQ)之类的资源。

2、platform_driver,用于注册驱动到platform总线。

注意,所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念,而是Linux系统提供的一种附加手段,例如,在 S3C6410处理器中,把内部集成的I2 C、RTC、SPI、LCD、看门狗等控制器都归纳为platform_device,而它们本身就是字符设备。

1、platform_device,设备
platform_device结构体的定义

struct platform_device {
   
   
	const char * name;/* 设备名 /
	u32 id;
	struct device dev;
	u32 num_resources;/ 设备所使用各类资源数量 /
	struct resource * resource;/ 资源 */
};

实验注册一个device

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h> 
//#include <linux/ioport.h> 
 
struct resource beep_res[] = {
   
   
	[0] = {
   
   
		.start = 0x20AC000,
		.end   = 0x20AC003,
		.flags = IORESOURCE_MEM,
		.name  = "GPIO5_DR"
	}
};
 
void beep_release(struct device *dev)
{
   
   
	printk("beep_release\n");
}
 
struct platform_device beep_device = {
   
   
	.name = "beep_test",//ls /sys/bus/platform/devices/可以看到
	.id   = -1,
	.resource = beep_res,
	.num_resources = ARRAY_SIZE(beep_res),
	.dev = {
   
   
		.release = beep_release
	}
};
 
static int device_init(void)
{
   
   
	printk("device_init \n");
	return platform_device_register(&beep_device);
}
 
static void  device_exit(void)
{
   
   
	platform_device_unregister(&beep_device);
	printk("device_exit \n");
}
 
module_init(device_init);
module_exit(device_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述
在这里插入图片描述

2、platform_driver,驱动
platform_driver这个结构体中包含probe()、remove()、shutdown()、suspend()、 resume()函数,通常也需要由驱动实现
platform_driver结构体

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 (*suspend_late)(struct platform_device *, pm_message_t state);
	int (*resume_early)(struct platform_device *);
	int (*resume)(struct platform_device *);
	struct pm_ext_ops *pm;
	struct device_driver driver;
};

写个代码测试注册驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h> int beep_probe(struct platform_device *pdev)
{
   
   
    pritnk("beep_probe \n");
    return 0;
}int beep_remove(struct platform_device *pdev)
{
   
   
    pritnk("beep_remove \n");
    return 0;
}
​
strcut platform_driver beep_driver = {
   
   
    .probe = beep_probe,
    .remove = beep_remove,
    .driver = {
   
   
        .owner = THIS_MODULE,
        .name  = "beep_test"
    }
};static int beep_driver_init(void)
{
   
   
    printk("beep_driver_init \n");
    return platform_driver_register(&beep_driver);
}static void  beep_driver_exit(void)
{
   
   
    platform_driver_unregister(&beep_driver);
    printk("beep_driver_exit \n");
}module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");

编译上面的驱动,加载驱动后发现没有进入probe函数。没有加name,所以没法匹配

可以发现这样写不会进入到probe函数。因为name不匹配
在这里插入图片描述
继续改
在这里插入图片描述
在这里插入图片描述
可以发现这次调用了probe函数。因为总算匹配上了。

总结
可以发现设备名称name要一样才能进入probe函数,id_table没有初始化值的时候,device name才会与结构体driver成员name匹配。否则会优先与id_table成员name进行匹配,匹配成功才会加载probe函数。
在这里插入图片描述
先加载driver.ko或先加载device.ko没有先后关系

二、字符设备中的两个重要结构体,struct device和struct device_driver

在include/linux/device.h中,Linux内核定义了设备模型中最重要的两个数据结构,struct device和struct device_driver。
内核管理的所有的驱动,都必须包含一个叫struct device_driver成员。描述的硬件设备,必须包含struct device结构体成员。
device和device driver是Linux驱动开发的基本概念。Linux kernel的思路很简单:驱动开发,就是要开发指定的软件(driver)以驱动指定的设备,所以

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值