Linux驱动开发之platform设备驱动实验【完整教程】

为了方便驱动的编写,提高软件的重用性和跨平台性能,于是就提出了Linux驱动的分离和分层

  驱动的分层,分层的目的时为了在不同的层处理不同的内容,最简单的驱动分层是input子系统负责管理所有跟输入有关的驱动、最底层的就是设备原始驱动,负责获取输入设备的原始值,获取到的输入时间上报给input核心层。input核心层会处理各种IO模型,并且提供file_operations操作集合。
                  input核心层----提供统一的框架
                    
                   input层
                    
                  最底层(设备原始驱动)----不同的设备使用统一的框架

根据驱动的分离和分层衍生出总线–驱动–设备驱动框架。总线代码由Linux内核提供。当向总线注册驱动的时候,会检测当前总线下的所有设备有没有和此驱动匹配的设备,如果有,就会执行probe函数(probe函数需要我们自己编写)。如果先安装设备就设备去找驱动,如果先安装驱动就驱动去找设备。

  

platform

  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,至此设备便创建完成了,接下来创建驱动。
Linux驱动开发之platform设备驱动实验【完整教程】_第1张图片
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文件中添加刚才创建的文件
Linux驱动开发之platform设备驱动实验【完整教程】_第2张图片
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函数里面的内容。注意这里:驱动还是设备的添加先后顺序没有影响。
Linux驱动开发之platform设备驱动实验【完整教程】_第3张图片

结论:无设备树下的platform设备驱动是靠设备定义的名字和驱动定义的名字进行匹配,当两者的名字一致时,匹配成功,便去执行驱动的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");

你可能感兴趣的:(Linux,驱动开发,驱动开发,linux,c#,c语言)