tiny4412 Linux platform device driver model

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/weixin_44317448/article/details/91469856

   In the above Linux2.6 device driver model, there are three entities: bus, device and driver. Bus driver is responsible for equipment and bindings, registered in the system each time a device will look for matching drive; the contrary, in a system driven by a registered every time, will look for matching devices, there are match bus carry out.

  Bus, device, driver of these three relationships:

Bus driver is responsible for equipment and bindings, a reality of Linux devices and drivers often need to mount on of bus, such as USB, I2C, SPI interface devices are there to manage the corresponding bus, operated by the bus device . Need to mount the driving device on the bus, the device driver needs to specify which part of the bus bar, so the device driver needs to register. The bus system in linux is also part of the device, so the buses have registered, but first have to register the device and then the bus driver, the bus must first register. There are two kinds of buses in linux system, one is actually present in the bus pci usb and so on, there is a class of virtual presence of the bus platform, platform bus is mainly used for SoC system integrated in the device, making each device belonging to a bus, the corresponding device is called platform_device, driving becomes platform_driver.

tips: a plurality of driving devices may correspond, a device can have only one drive.

Platform model hierarchical model

1 divided into two parts, the device information and the driver.

2. If you want the device to work properly, we must take the device and driver information are registered into the kernel.

3. The device information and the driver is written in each file separate ko (ko can also be written a)

Whether to install drivers or to install the driver, the kernel will be correct and binding device driver matching binding.

Kernel to find the corresponding driving apparatus, and a kernel driver is looking is the same as the corresponding device by comparing their name string members referred to.

Write a platform to control the led.

device.c Listing:

#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 Listing:

#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");


Application Layer test code:

#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
	

When finished, make it in Linux, the generation of two .ko are insmod the kernel, running app, enter ./app_led_test / dev / led

Experimental phenomena: to see the development board for the light water lights. When just the press of a button, the buzzer will sound, when you press a buzzer does not sound flip, it is a key way to trigger an interrupt.

Guess you like

Origin blog.csdn.net/weixin_44317448/article/details/91469856