为了方便驱动的编写,提高软件的重用性和跨平台性能,于是就提出了Linux驱动的分离和分层
驱动的分层,分层的目的时为了在不同的层处理不同的内容,最简单的驱动分层是input子系统负责管理所有跟输入有关的驱动、最底层的就是设备原始驱动,负责获取输入设备的原始值,获取到的输入时间上报给input核心层。input核心层会处理各种IO模型,并且提供file_operations操作集合。
input核心层----提供统一的框架
input层
最底层(设备原始驱动)----不同的设备使用统一的框架
根据驱动的分离和分层衍生出总线–驱动–设备驱动框架。总线代码由Linux内核提供。当向总线注册驱动的时候,会检测当前总线下的所有设备有没有和此驱动匹配的设备,如果有,就会执行probe函数(probe函数需要我们自己编写)。如果先安装设备就设备去找驱动,如果先安装驱动就驱动去找设备。
Linux内核提供了一个虚拟总线:platform。相应的就有platform_driver 和 platform_device。
platform驱动就是platform_driver。这个属于一个结构体,定义在include/linux/platform_device.h
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;
};
结论:向内核注册platform驱动的时候,如果驱动和设备匹配成功,最终会执行platform_driver的probe函数 ,无设备树的时候,此时需要驱动开发人员编写设备注册文件,使用platform_device_register函数注册设备;有设备树时,只需要修改设备树的设备节点即可。
platform的匹配过程:platform总线下的match就是platform_mach函数。
本来打算把点灯的功能加上的,但是再把点灯的步骤加上去会导致文章过于冗杂,于是设备文件中有点灯的代码,但是驱动文件中没有写上去。
1.搭建好基本的框架,这里就用到以前所需要的头文件
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*设备加载*/
static int __init leddevice_init(void)
{
return 0;
}
/*设备卸载*/
static void __exit leddevice_exit(void)
{
}
module_init(leddevice_init);
module_exit(leddevice_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kong");
2.添加一个空的设备结构体,并且在设备加载和卸载中执行platform的注册和注销
static struct platform_device leddevice = {
};
/*设备加载*/
static int __init leddevice_init(void)
{
/*注册platform设备*/
return platform_device_register(&leddevice);
}
/*设备卸载*/
static void __exit leddevice_exit(void)
{
/*卸载platform设备*/
platform_device_unregister(&leddevice);
}
3.完善platform_device结构体,并且添加release,注意这里的num_resource和resource在后面几步才编写
void leddevice_release(struct device *dev)
{
printk("leddevice release\r\n");
}
static struct platform_device leddevice = {
.name = "imx6ull-led",
.id = -1, //表示此设备无id
.dev = {
.release = leddevice_release,
},
.num_resources =
.resource =
};
4.添加物理寄存地址和寄存长度,为后面的resource做准备
/* 寄存器物理地址 */
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)
#define REGISTER_LENGTH 4
5.完善resouece
static struct resource led_resources[] = {
[0] = {
.start = CCM_CCGR1_BASE,
.end = CCM_CCGR1_BASE + REGISTER_LENGTH -1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = SW_MUX_GPIO1_IO03_BASE ,
.end = SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH -1,
.flags = IORESOURCE_MEM,
},
[2] = {
.start = SW_PAD_GPIO1_IO03_BASE,
.end = SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH -1,
.flags = IORESOURCE_MEM,
},
[3] = {
.start = GPIO1_DR_BASE,
.end = GPIO1_DR_BASE + REGISTER_LENGTH -1,
.flags = IORESOURCE_MEM,
},
[4] = {
.start = GPIO1_GDIR_BASE,
.end = GPIO1_GDIR_BASE + REGISTER_LENGTH -1,
.flags = IORESOURCE_MEM,
},
};
**
**
**
static struct platform_device leddevice = {
.name = "imx6ull-led",
.id = -1, //表示此设备无id
.dev = {
.release = leddevice_release,
},
.num_resources = ARRAY_SIZE(led_resources),
.resource = led_resources,
};
6.进行编译,加载驱动,并且可以在开发板上看到sys/bus/platform/devices中有我们刚才编写的imx6ull-led,至此设备便创建完成了,接下来创建驱动。
7.新建一个在当前目录下的.c文件,并构建好基本框架
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*platform驱动结构体*/
static struct platform_driver led_driver = {
};
/*驱动加载*/
static int __init leddriver_init(void)
{
/*注册platform驱动*/
return platform_driver_register(&led_driver);
}
/*驱动卸载*/
static void __exit leddriver_exit(void)
{
/*卸载platform驱动*/
platform_driver_unregister(&led_driver);
}
module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kong");
8.在Makefile文件中添加刚才创建的文件
9.完善platform_driver结构体,添加probe和remove函数
static int led_probe(struct platform_device *dev)
{
printk("led driver proe\r\n");
return 0;
}
static int led_remove(struct platform_device *dev)
{
printk("led driver remove\r\n");
return 0;
}
/*platform驱动结构体*/
static struct platform_driver led_driver = {
.driver = {
.name = "imx6ull-led", //驱动名字,用于和设备名字匹配
},
.probe = led_probe,
.remove = led_remove,
};
10.编译,加载驱动,可以看到当加载好了设备后,再加载驱动,当设备和驱动匹配成功后,就会执行probe函数里面的内容,当卸载驱动后,就会执行remove函数里面的内容。注意这里:驱动还是设备的添加先后顺序没有影响。
例程源码:设备完整代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 寄存器物理地址 */
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)
#define REGISTER_LENGTH 4
void leddevice_release(struct device *dev)
{
printk("leddevice release\r\n");
}
static struct resource led_resources[] = {
[0] = {
.start = CCM_CCGR1_BASE,
.end = CCM_CCGR1_BASE + REGISTER_LENGTH -1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = SW_MUX_GPIO1_IO03_BASE ,
.end = SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH -1,
.flags = IORESOURCE_MEM,
},
[2] = {
.start = SW_PAD_GPIO1_IO03_BASE,
.end = SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH -1,
.flags = IORESOURCE_MEM,
},
[3] = {
.start = GPIO1_DR_BASE,
.end = GPIO1_DR_BASE + REGISTER_LENGTH -1,
.flags = IORESOURCE_MEM,
},
[4] = {
.start = GPIO1_GDIR_BASE,
.end = GPIO1_GDIR_BASE + REGISTER_LENGTH -1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device leddevice = {
.name = "imx6ull-led",
.id = -1, //表示此设备无id
.dev = {
.release = leddevice_release,
},
.num_resources = ARRAY_SIZE(led_resources),
.resource = led_resources,
};
/*设备加载*/
static int __init leddevice_init(void)
{
/*注册platform设备*/
return platform_device_register(&leddevice);
}
/*设备卸载*/
static void __exit leddevice_exit(void)
{
/*卸载platform设备*/
platform_device_unregister(&leddevice);
}
module_init(leddevice_init);
module_exit(leddevice_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kong");
驱动完整代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int led_probe(struct platform_device *dev)
{
printk("led driver proe\r\n");
return 0;
}
static int led_remove(struct platform_device *dev)
{
printk("led driver remove\r\n");
return 0;
}
/*platform驱动结构体*/
static struct platform_driver led_driver= {
.driver = {
.name = "imx6ull-led",
},
.probe = led_probe,
.remove = led_remove,
};
/*驱动加载*/
static int __init leddriver_init(void)
{
/*注册platform驱动*/
return platform_driver_register(&led_driver);
}
/*驱动卸载*/
static void __exit leddriver_exit(void)
{
/*卸载platform驱动*/
platform_driver_unregister(&led_driver);
}
module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kong");