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

實驗現象:看見開發板上的燈爲流水燈。當隨便按下一個按鍵時,蜂鳴器會響,在按一次時,蜂鳴器翻轉不響,按鍵是用中斷的方式來觸發。

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