嵌入式linux驱动-bus-driver-device模型笔记

一、开发环境

1、内核:Linux 2.6.22.6;

2、JZ2440

3、ubuntu 9.10

二、概念

    一个现实的linux设备驱动通常需要挂接在一种总线上,对于本身依附于PCI,USB,IIC,SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SOC系统中集成的独立的外设控制器,挂接在SOC内存空间的外设等确不依附于此类总线。基于这一背景,linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为platform_driver.Platform总线是linux2.6内核加的一种虚拟总线。Platform驱动与传统的设备驱动模型相比,优势在于Platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,提高了程序的可移植性。Linux 2.6的设备驱动模型中,把I2C、RTC、LCD等都归纳为platform_device。总线将设备和驱动分别注册进各自的链表,在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。

三、过程

1、device层

(1)定义platform_device 结构体类型的变量,如建立led_device 。

static struct platform_device led_device = {
.name = "myled",
.id = -1,
.dev = {
.release = led_release, 
},
.resource = led_resources,
.num_resources= ARRAY_SIZE(led_resources),
};

其中有个重要的成员是resource,是设备的资源信息,如IO地址,中断号等。

(2)定义resource结构体类型的变量。硬件资源就是在这确定的。如定义led_resources

static struct resource led_resources[] = {
[0]={
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM,
},
[1]={
.start = 5,
.end = 5,
.flags = IORESOURCE_IRQ,
}
};

(3)在入口函数注册、注销平台设备。使用函数platform_device_register、platform_device_unregister。如:

static int led_device_init(void)
{
platform_device_register(&led_device);
return 0;
}

static void led_device_exit(void)
{
platform_device_unregister(&led_device);
}

2、driver层

(1)定义platform_driver 结构体类型的变量,如定义led_driver :

struct platform_driver led_driver = {
.probe = led_driver_probe,
.remove = __devexit_p(led_driver_remove),
.driver = {
.name = "myled",
}
};

注意.name,每当注册platform_driver,或上面platform_device 时,在bus层的platform_match函数首先判断是否有id_table,如果有则使用id_table来进行匹配,否则,判断platform_device和platform_driver成员里的name,如果二者的name字段相同则匹配,如果匹配则调用platform_driver的probe函数。

(2)在入口函数注册、注销平台设备驱动。使用函数platform_driver_register、platform_driver_unregister。如:

static int  led_driver_init(void)
{
platform_driver_register(&led_driver);
return 0;
}

static void  led_driver_exit(void)
{
platform_driver_unregister(&led_driver);
}

(3)编写probe函数。

     这里主要可以进行一些初始化操作。如例子中注册了字符设备驱动,设置了IO功能并进行了ioremap。需要注意的是,对于platform_device 里的资源resource 是使用platform_get_resource函数得到的,再对这些资源做具体的操作。

(4)编写remove函数。对probe函数中加载的资源进行释放。

(5)具体的驱动编写。如例子里实现了open、write。

3、bus层

这层不需要编写。韦老师架构图

嵌入式linux驱动-bus-driver-device模型笔记_第1张图片

在device层或driver层注册平台设备或平台设备驱动时,bus会调用device_add或driver_register分别加入各自的链表,名字为各自结构体变量的.name项。接着调用platform_match函数先判断是否有id_table,如果有则使用id_table来进行匹配,否则,判断platform_device和platform_driver成员里的name,如果二者的name字段相同则匹配成功,然后调用platform_driver的probe函数。

四、程序

device.c

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 



static void led_release(struct device * dev)
{
}



static struct resource led_resources[] = {
	[0]={
		.start	= 0x56000050,
		.end	= 0x56000050 + 8 - 1,
		.flags	= IORESOURCE_MEM,
	},

	[1]={
		.start	= 5,
		.end	= 5,
		.flags	= IORESOURCE_IRQ,
	}
};

static struct platform_device led_device = {
	.name		= "myled",
	.id		= -1,
	.dev		= {
//				.platform_data	= &nand_data,
				.release = led_release, 
	},
	.resource	= led_resources,
	.num_resources	= ARRAY_SIZE(led_resources),
};



static int led_device_init(void)
{
	platform_device_register(&led_device);
	return 0;
}

static void led_device_exit(void)
{
	platform_device_unregister(&led_device);
}

module_init(led_device_init);
module_exit(led_device_exit);

MODULE_LICENSE("GPL");

driver.c

/* 分配/设置/注册一个platform_driver */

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static struct class *leds_class;
static struct class_device	*leds_class_devs;
static int pin;

static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;

static int ret;


static int led_open(struct inode *inode, struct file *file)
{
	printk("led_open\n");
	
	/* 配置为输出 */
	*gpio_con &= ~(0x3<<(pin*2));
	*gpio_con |= (0x1<<(pin*2));
	return 0;	
}


static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	int val;

	printk("led_write\n");

	copy_from_user(&val, buf, count); //	copy_to_user();

	if (val == 1)
	{
		// 点灯
		*gpio_dat &= ~(1<start, res->end - res->start + 1);
	gpio_dat = gpio_con + 1;
	
	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	pin = res->start;
	
	ret = register_chrdev(0,"myled",&leds_fops);
	leds_class = class_create(THIS_MODULE, "myled");  
	class_device_create(leds_class, NULL, MKDEV(ret, 0), NULL, "myled");/* /dev/leds */
	return 0;
}


static int  led_driver_remove(struct platform_device *pdev)
{
	printk("led_driver_remove\n");

	class_device_destroy(leds_class, MKDEV(ret, 0));
	class_destroy(leds_class);
	unregister_chrdev(ret, "myled");
	
	iounmap(gpio_con);
	
	return 0;	  
}




struct platform_driver led_driver = {
	.probe		= led_driver_probe,
	.remove		= __devexit_p(led_driver_remove),
	.driver		= {
		.name	= "myled",
	}
};


static int  led_driver_init(void)
{
	platform_driver_register(&led_driver);
	return 0;
}


static void  led_driver_exit(void)
{
	platform_driver_unregister(&led_driver);
}

module_init(led_driver_init);
module_exit(led_driver_exit);

MODULE_LICENSE("GPL");


测试程序test.c

#include 
#include 
#include 
#include 

int main(int argc,char **argv)
{
	int fd;
	int val=1;
	fd=open("/dev/myled",O_RDWR);
	if(fd<0)
	{
		printf("can't open!");

	}
	
	if(argc!=2)
	{
		printf("Usage :\n");
		printf("%s \n",argv[0]);
		return 0;

	}
	
	if(strcmp(argv[1],"on")==0)
	{
		val =1;

	}
	else
	{
		val = 0;
	

	}
write(fd,&val,4);
	
	return 0;

}












你可能感兴趣的:(嵌入式linux驱动)