tiny4412 Linux平台设备驱动模型

   在Linux2.6以上的设备驱动模型中,有三大实体:总线、设备和驱动。总线负责将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个一个驱动的时候,会寻找与之匹配的设备,匹配有总线完成。

  总线、设备、驱动的这三者的关系:

总线负责将设备和驱动绑定,一个现实的Linux设备和驱动通常都需要挂接在一种总线上,如USB、I2C、SPI接口的设备都是有对应的总线来管理,通过总线来操作设备。设备与驱动需要挂载在总线上,需要指明驱动与设备是属于哪条总线的,所以设备与驱动需要注册。而总线在linux系统中也是属于设备,所以总线也要注册,同时要先有总线而后才能注册设备和驱动,所以总线要先注册。总线在linux系统中有俩种,一是实际存在的总线 pci usb 等等,还有一类是虚拟存在的总线 platform ,platform总线主要是用于集成在SoC系统的设备,使得每一个设备都属于一条总线,相应的设备称为platform_device,而驱动成为 platform_driver。

tips:一个驱动可以对应多个设备,一个设备只能有一个驱动。

Platform模型分层模型

1.分为两部分,设备信息和驱动程序。

2.如果要设备能正常工作,就必须把设备信息及驱动程序都注册进内核。

3.设备信息和驱动程序是分别写成独立的ko文件(也可以写成一个ko)

不管先装驱动还是先安装驱动,内核都正确的进行绑定设备和驱动的匹配绑定。

内核为设备找对应的驱动,以及内核为驱动找对应的设备是通过比较它们的name成员所指的字符串是否相同。

写了一个platform来控制led.

device.c代码清单:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>

struct resource	my_resource[] = {
	[0] = {
		.start = 0x110002e0,//物理地址
		.end   = 0x110002e7,
		.name  = "LED",
		.flags = IORESOURCE_MEM,//资源的类型,为内存
	},
	[1] = {
		.start = 0x114000A0,
		.end   = 0x114000A7,
		.name  = "BEEP",
		.flags = IORESOURCE_MEM,//资源的类型,为内存
	},
};

static void dev_release(struct device *dev)
{
	;
}

struct platform_device my_device={
	.name = "test",
	.id	  = -1,
	.num_resources = ARRAY_SIZE(my_resource),//资源个数
	.resource = my_resource,                //设备占用资源的首地址
	.dev ={
		.release = dev_release,
	},
};
		
static __init int tiny4412_device_init(void)
{
	int ret;
	ret = platform_device_register(&my_device);//设备注册
	if(ret < 0){
		printk("my device register error\n");
	}
	return 0;
}

static __exit void tiny4412_device_exit(void)
{
	platform_device_unregister(&my_device);//设备注销函数
}

module_init(tiny4412_device_init);
module_exit(tiny4412_device_exit);

MODULE_LICENSE("GPL");


 driver.c代码清单:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <linux/miscdevice.h> 


#define KEY_GPIO(i) EXYNOS4_GPX3(2+i)

int key_irq[4]={0};

//static struct platform_device *my_device;
struct resource *led_resource;
struct resource *beep_resource;


unsigned int *vir_gpm4con;
unsigned int *vir_gpm4dat;
unsigned int *vir_gpd0con;
unsigned int *vir_gpd0dat;

irqreturn_t key_irq_handle(int irq, void *args)//按键中断函数
{
	int i=0;
	static int num = 0;

	if(num==2){
		for(i = 0; i < 4; i++){
			if(irq==key_irq[i]){
			 printk("key%d Down!\n", i+1);	
			*vir_gpm4dat ^= (0xf);
			*vir_gpd0dat ^= (0x1);
			}
		}
		num=0;
	}
	num++;
	return IRQ_HANDLED;
}
static int led_open(struct inode *i_node, struct file *p_file)
{
	printk("%s\n", __func__);
	*vir_gpm4con &= ~((0xF<<4*0)|(0xF<<4*1)|(0xF<<4*2)|(0xF<<4*3));
	*vir_gpm4con |= (0x1<<4*0)|(0x1<<4*1)|(0x1<<4*2)|(0x1<<4*3);
	*vir_gpm4dat |= (0x1<<0)|(0x1<<1)|(0x1<<2)|(0x1<<3);
	return 0;
}
//驱动程序的 close 函数
static int led_release(struct inode *i_node, struct file *p_file)
{
	printk("%s\n", __func__);
	return 0;
}
//驱动程序的 write 接口
static ssize_t led_write(struct file *i_node,const char __user *buf,size_t count, loff_t *off)
{
	int i = 0,ret = 0;
	char buff[4] = {0};
	printk("%s\n", __func__);
	ret = copy_from_user(buff,buf,count);
	if(ret < 0){
		printk("copy_from_user is fail!!\n");
		return -1;
	}
	for(i=0;i<sizeof(buff);i++)
	{
			if(buff[i] == 0){
			*vir_gpm4dat |= (1<<i);
			printk("led[%d] is off\n",i);
		}
		else if(buff[i] == 1){
			*vir_gpm4dat &= ~(1<<i);
			printk("led[%d] is on\n",i);
		}
		else{
			printk("on/off led err!!\n");
			return -1;
		}
	}
	return 0;
}
//文件操作集合
static struct file_operations f_ops ={
	.open = led_open,
	.release = led_release,
	.write = led_write,
};
// 杂项设备核心结构
static struct miscdevice misc_led = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "led",
	.fops = &f_ops,
};

static int my_probe(struct platform_device *pdev)
{
	int i,ret;
	int size;
	char name[16];
	printk("%s\n", __func__);
	//my_device = pdev;
	led_resource = platform_get_resource(pdev,IORESOURCE_MEM,0);//获取资源
	beep_resource = platform_get_resource(pdev,IORESOURCE_MEM,1);
	if(led_resource == NULL || beep_resource  == NULL){
		printk("platform_get_resource error\n");
		return -1;
	}
	size = led_resource->end - led_resource->start; //结束-开始就是字节数
	vir_gpm4con = ioremap(led_resource->start, size); 
	size = beep_resource->end - beep_resource->start; //结束-开始就是字节数
	vir_gpd0con = ioremap(beep_resource->start, size); 
	if(vir_gpm4con == NULL && vir_gpd0con){
		printk("ioremap error\n");
		return -1;
	}
	for(i=0; i<4; i++){
		memset(name, 0, sizeof(name));

		sprintf(name, "key%d_irq", i);

		key_irq[i] = gpio_to_irq(KEY_GPIO(i));
		ret = request_irq(key_irq[i], key_irq_handle, IRQF_DISABLED | IRQF_TRIGGER_FALLING, name, (void*)(i));
		if(ret < 0){
			printk("request irq error\n");
			goto err_irq;
		}
	}
	ret = misc_register(&misc_led);
	if(ret<0){
		printk("misc_register is fail!!\n");
		return ret;
	}
	
	vir_gpm4dat = vir_gpm4con + 1;
	vir_gpd0dat = vir_gpd0con + 1;
	*vir_gpm4con = 0x1111;
	*vir_gpd0con |= 0x1;
	*vir_gpm4dat |= 0xf;
	*vir_gpd0dat &= ~(0x1);//操作地址
	return 0;
	err_irq:
	while(i--){
		free_irq(key_irq[i], NULL);
	}
	return ret;
}


static int my_remove(struct platform_device *pdev)
{
	printk("%s\n", __func__);
	return 0;
}

static struct platform_driver my_driver = {
	.probe		= my_probe,
	.remove		= my_remove,
	.driver	= {
		.name	= "test",
		.owner	= THIS_MODULE,
	},
};


static __init int tiny4412_driver_init(void)
{
	int ret=0;
	ret = platform_driver_register(&my_driver);//注册
	if(ret < 0){
		printk("driver register error\n");
		return ret;
	}
	
	return 0;
}

static __exit void tiny4412_driver_exit(void)
{
	int i = 4;
	iounmap(vir_gpm4con);
	iounmap(vir_gpd0con);
	while(i--){
			free_irq(key_irq[i], NULL);
		}
	platform_driver_unregister(&my_driver);//销毁
	misc_deregister(&misc_led);
}


module_init(tiny4412_driver_init);
module_exit(tiny4412_driver_exit);

MODULE_LICENSE("GPL");


应用层测试代码:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[])
{
	int fd;
	char buf[4]={0,1,0,1};
	if(argc <2){
		printf("usage : %s <file name >\n",argv[0]);
		return -1;
	}
	fd = open(argv[1],O_RDWR);
	if(fd<0){
		printf("fd = %d,open %d file is fail!!\n",fd,argv[1]);
		return -1;
	}
	write(fd,buf,4);
	sleep(2);
	while(1)
	{
		memset(buf, 0, 4);
		buf[0]=1;
		write(fd,buf,4);
		sleep(2);
		memset(buf, 0, 4);
		buf[1]=1;
		write(fd,buf,4);
		sleep(2);
		memset(buf, 0, 4);
		buf[2]=1;
		write(fd,buf,4);
		sleep(2);
		memset(buf, 0, 4);
		buf[3]=1;
		write(fd,buf,4);
		sleep(2);
	}
	return 0;
}

Makefile

obj-m += device.o driver.o
KERN_DIR=/root/work/tiny4412/linux/linux-3.5
PWD := $(shell pwd)

modules:
	$(MAKE) ARCH=arm -C $(KERN_DIR) M=$(PWD) modules
	arm-linux-gcc app_led_test.c -o app_led_test
	cp app_led_test device.ko driver.ko /root/work/tiny4412/rootfs/root_nfs/root/
clean:
	$(MAKE) ARCH=arm -C $(KERN_DIR) M=$(PWD) modules clean
	

完成后,在Linux中make一下,把生成的两个.ko都insmod到内核中,运行app,输入./app_led_test /dev/led

实验现象:看见开发板上的灯为流水灯。当随便按下一个按键时,蜂鸣器会响,在按一次时,蜂鸣器翻转不响,按键是用中断的方式来触发。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章