本文参考,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个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每 注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。

一个现实的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)以驱动指定的设备,所以

最低0.47元/天 解锁文章
356

被折叠的 条评论
为什么被折叠?



