linux驱动程序开发-第九节:应用层阻塞归功于底层驱动采用中断机制

驱动采用:中断+等待队列实现阻塞。

       设置一个等待的条件,条件满足,进程就继续向下执行;条件不满足,进程就阻塞在等待队列上。当条件满足后,中断会唤醒等待队列中的进程,进程再继续向下执行。

视频讲解及详细资料链接:链接:https://pan.baidu.com/s/13xxNzgLur468qeivBw8FUg 
提取码:osb1 
 



程序案列:一个按键对应一个led,按下亮,中断实现底层驱动ioctl()阻塞。

1.key_drv.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <cfg_type.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <mach/devices.h>
#include <linux/sched.h>

#define GEC6818_K2_STA		_IOR('K',  1, unsigned long)
#define GEC6818_K3_STA		_IOR('K',  2, unsigned long)
#define GEC6818_K4_STA		_IOR('K',  3, unsigned long)
#define GEC6818_K6_STA		_IOR('K',  4, unsigned long)
#define GEC6818_KALL_STA	_IOR('K',  5, unsigned long)

static unsigned int key_val=0;
static int key_press_flag=0;
static wait_queue_head_t gec6818_key_wq;
irqreturn_t keys_irq_handler(int irq,void *dev)
{
	
	if(irq == IRQ_GPIO_A_START +28)
	{
		key_val|=gpio_get_value(PAD_GPIO_A+28)? 0: 1<<0;
		
	}
		if(irq == IRQ_GPIO_B_START +9)
	{
		key_val|=gpio_get_value(PAD_GPIO_B+9)? 0: 1<<1;
		
	}
	
		if(irq == IRQ_GPIO_B_START +30)
	{
		key_val|=gpio_get_value(PAD_GPIO_B+30)? 0: 1<<2;
		
	}
	
		if(irq == IRQ_GPIO_B_START +31)
	{
		key_val|=gpio_get_value(PAD_GPIO_B+31)? 0: 1<<3;
		
	}
	//设置等待条件为真
	key_press_flag = 1;
	
	//唤醒等待队列中的进程
	wake_up(&gec6818_key_wq);
	
	
	
	//printk("keys_irq_handler\n");
	
	return IRQ_HANDLED;//告诉内核,中断已经处理完毕
}
static int  gec6818_key_open (struct inode * inode, struct file *file)
{
		
	printk("gec6818_key_open \n");	
	
	return 0;
}
static int  gec6818_key_release (struct inode * inode, struct file *file)
{
	printk("gec6818_key_release \n");
	return 0;
}
static long gec6818_key_ioctl (struct file *filp, unsigned int cmd, unsigned long args)
{
	int rt=0;
	
	switch(cmd)
	{
		case GEC6818_KALL_STA:
		{
			//访问等待队列头,并且去判断key_press_flag是否为真,若是假的话继续阻塞等待
			wait_event_interruptible(gec6818_key_wq,key_press_flag);
			
			key_press_flag=0;

		}break;
		case GEC6818_K2_STA:
		{
			//访问等待队列头,并且去判断key_press_flag是否为真,若是假的话继续阻塞等待
			wait_event_interruptible(gec6818_key_wq,key_press_flag);
			
			key_press_flag=0;
			
		}break;
		
		case GEC6818_K3_STA:
		{
			//访问等待队列头,并且去判断key_press_flag是否为真,若是假的话继续阻塞等待
			wait_event_interruptible(gec6818_key_wq,key_press_flag);
			
			key_press_flag=0;
			
		}break;		
		
		case GEC6818_K4_STA:
		{
			//访问等待队列头,并且去判断key_press_flag是否为真,若是假的话继续阻塞等待
			wait_event_interruptible(gec6818_key_wq,key_press_flag);
			
			key_press_flag=0;
			
		}break;			

		case GEC6818_K6_STA:
		{
			//访问等待队列头,并且去判断key_press_flag是否为真,若是假的话继续阻塞等待
			wait_event_interruptible(gec6818_key_wq,key_press_flag);
			
			key_press_flag=0;
			
		}break;	
		
		default:
			printk("key ENOIOCTLCMD\n");
			return -ENOIOCTLCMD;
	}
			
	
	rt = copy_to_user((void *)args,&key_val,4);
	
	key_val=0;
	
	if(rt != 0)
		return -EFAULT;
	
	return 0;
}
static const struct file_operations gec6818_key_fops = {
 	.owner 			= THIS_MODULE,
	.open 			= gec6818_key_open,
	.release 		= gec6818_key_release,
	.unlocked_ioctl = gec6818_key_ioctl,
};

static struct miscdevice gec6818_key_miscdev = {
	.minor		= MISC_DYNAMIC_MINOR,	//MISC_DYNAMIC_MINOR,动态分配次设备号
	.name		= "wgh_gec6818_keys_irq",		//设备名称,/dev/gec6818_keys	
	.fops		= &gec6818_key_fops,	//文件操作集
};
//入口函数
static int __init gec6818_key_init(void)
{
	
	int rt=0;

	//混杂设备的注册
	rt = misc_register(&gec6818_key_miscdev);
	
	if (rt) 
	{
		printk("misc_register fail\n");
		return rt;
	}
	
	
	rt=request_irq(	gpio_to_irq(PAD_GPIO_A+28),
					keys_irq_handler,
					IRQF_TRIGGER_FALLING,
					"gpioA_28",
					NULL);
	if(rt)
	{
		printk("request_irq fail\n");
		goto request_irq_fail_1;
		
		
	}
	
	rt=request_irq(	gpio_to_irq(PAD_GPIO_B+31),
					keys_irq_handler,
					IRQF_TRIGGER_FALLING,
					"gpioB_31",
					NULL);
	if(rt)
	{
		printk("request_irq fail\n");
		goto request_irq_fail_2;
		
		
	}
	
		rt=request_irq(	gpio_to_irq(PAD_GPIO_B+9),
					keys_irq_handler,
					IRQF_TRIGGER_FALLING,
					"gpioB_9",
					NULL);
	if(rt)
	{
		printk("request_irq fail\n");
		goto request_irq_fail_3;
		
		
	}
	
		rt=request_irq(	gpio_to_irq(PAD_GPIO_B+30),
					keys_irq_handler,
					IRQF_TRIGGER_FALLING,
					"gpioB_30",
					NULL);
	if(rt)
	{
		printk("request_irq fail\n");
		goto request_irq_fail_4;
		
		
	}
	//初始化等待队列头
	init_waitqueue_head(&gec6818_key_wq);
	
	printk("gec6818 key init\n");
	
	return 0;

request_irq_fail_4:
	free_irq(IRQ_GPIO_B_START+30,NULL);
request_irq_fail_3:
	free_irq(IRQ_GPIO_B_START+31,NULL);
request_irq_fail_2:
	free_irq(IRQ_GPIO_A_START+28,NULL);
request_irq_fail_1:
	misc_deregister(&gec6818_key_miscdev);

	
	return rt;


}
//出口函数
static void __exit gec6818_key_exit(void)
{
	
	free_irq(IRQ_GPIO_A_START+28,NULL);
	free_irq(IRQ_GPIO_B_START+9,NULL);
	free_irq(IRQ_GPIO_B_START+30,NULL);
	free_irq(IRQ_GPIO_B_START+31,NULL);
	
	misc_deregister(&gec6818_key_miscdev);
	
	printk("gec6818 key exit\n");
}

//驱动程序的入口:insmod led_drv.ko调用module_init,module_init又会去调用gec6818_key_init。
module_init(gec6818_key_init);

//驱动程序的出口:rmsmod led_drv调用module_exit,module_exit又会去调用gec6818_key_exit。
module_exit(gec6818_key_exit)


//模块描述
MODULE_AUTHOR("[email protected]");			//作者信息
MODULE_DESCRIPTION("gec6818 led driver");		//模块功能说明
MODULE_LICENSE("GPL");				

2.led_drv.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>

#include <linux/io.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/miscdevice.h>
#include <linux/ioctl.h>


#define GEC6818_LED_ON		_IOW('L',  1, unsigned long)
#define GEC6818_LED_OFF		_IOW('L',  2, unsigned long)

struct led_gpio_info{
	unsigned int num;
	char name[20];	
};

static const struct led_gpio_info led_gpio_info_tbl[4]={
	
	{
		.num  = PAD_GPIO_E+13,
		.name = "gpioe13",
	},
	
	{
		.num  = PAD_GPIO_C+7,
		.name = "gpioc7",
	},	
	
	{
		.num  = PAD_GPIO_C+8,
		.name = "gpioc8",
	},		
	
	{
		.num  = PAD_GPIO_C+17,
		.name = "gpioc17",
	},		
};		

static int  gec6818_led_open (struct inode * inode, struct file *file)
{
	int i=0;
	
	//配置GPIOE13、GPIOC17、GPIOC7、GPIOC8为输出模式
	for(i=0; i<4; i++)
		gpio_direction_output(led_gpio_info_tbl[i].num,1);
	
	
	printk("gec6818_led_open \n");
	
	return 0;
}

static int  gec6818_led_release (struct inode * inode, struct file *file)
{
	printk("gec6818_led_release \n");
	
	return 0;
}


static long gec6818_led_ioctl (struct file *filp, unsigned int cmd, unsigned long args)
{
	//获取当前要控制的LED灯
	unsigned long led_pos = args - 7;
	
	switch(cmd)
	{
		case GEC6818_LED_ON:
				gpio_set_value(led_gpio_info_tbl[led_pos].num,0);
		break;
		
		
		case GEC6818_LED_OFF:
				gpio_set_value(led_gpio_info_tbl[led_pos].num,1);
		break;		
		
		default:
			return -ENOIOCTLCMD;
	}
	
	return 0;
}

static ssize_t gec6818_led_read (struct file *file, char __user *buf, size_t len, loff_t * offs)
{
	return 0;
}

static const struct file_operations gec6818_led_fops = {
 	.owner 			= THIS_MODULE,
	.unlocked_ioctl = gec6818_led_ioctl,
	.open 			= gec6818_led_open,
	.release 		= gec6818_led_release,
	.read 			= gec6818_led_read,
};


static struct miscdevice gec6818_led_miscdev = {
	.minor		= MISC_DYNAMIC_MINOR,	//MISC_DYNAMIC_MINOR,动态分配次设备号
	.name		= "wgh_gec6818_leds_riq",		//设备名称,/dev/gec6818_leds	
	.fops		= &gec6818_led_fops,	//文件操作集
};

//入口函数
static int __init gec6818_led_init(void)
{
	
	int rt=0;
	int i=0;

	//混杂设备的注册
	rt = misc_register(&gec6818_led_miscdev);
	
	if (rt) 
	{
		printk("misc_register fail\n");
		return rt;
	}
	
	
	//这招是损招,先释放gpio占用的资源
	for(i=0; i<4; i++)
		gpio_free(led_gpio_info_tbl[i].num);	
	
	//申请GPIOE13、GPIOC7、GPIOC8、GPIOC17
	
	for(i=0; i<4; i++)
	{
		
		rt=gpio_request(led_gpio_info_tbl[i].num,
						led_gpio_info_tbl[i].name);
						
		if(rt < 0)
		{
			printk("gpio_request:%s fail\n",led_gpio_info_tbl[i].name);
			
			goto gpio_request_fail;
			
		}
	}

	
	printk("gec6818 led init\n");
	
	return 0;
	
gpio_request_fail:
	for(i=0; i<4; i++)
		gpio_free(led_gpio_info_tbl[i].num);
	
	misc_deregister(&gec6818_led_miscdev);
	
	return rt;

}


//出口函数
static void __exit gec6818_led_exit(void)
{
	int i;
	
	for(i=0; i<4; i++)
		gpio_free(led_gpio_info_tbl[i].num);
	
	misc_deregister(&gec6818_led_miscdev);
	
	printk("gec6818 led exit\n");
}

//驱动程序的入口:insmod led_drv.ko调用module_init,module_init又会去调用gec6818_led_init。
module_init(gec6818_led_init);

//驱动程序的出口:rmsmod led_drv调用module_exit,module_exit又会去调用gec6818_led_exit。
module_exit(gec6818_led_exit)


//模块描述
MODULE_AUTHOR("[email protected]");			//作者信息
MODULE_DESCRIPTION("gec6818 led driver");		//模块功能说明
MODULE_LICENSE("GPL");							//许可证:驱动遵循GPL协议

3.test.c

#include <stdio.h>
/*
	一个按键对于一个led,按下亮,再按灭,中断实现底层驱动ioctl()阻塞。
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>

#define GEC6818_LED_ON		_IOW('L',  1, unsigned long)
#define GEC6818_LED_OFF		_IOW('L',  2, unsigned long)

#define GEC6818_K2_STA		_IOR('K',  1, unsigned long)
#define GEC6818_K3_STA		_IOR('K',  2, unsigned long)
#define GEC6818_K4_STA		_IOR('K',  3, unsigned long)
#define GEC6818_K6_STA		_IOR('K',  4, unsigned long)

#define GEC6818_KALL_STA	_IOR('K',  5, unsigned long)


int main(int argc, char **argv)
{
	int led_fd=-1;

	int key_fd=-1;

	int key_val=0;
	
	int rt=0;
	
	//打开gec6818_leds设备
	led_fd = open("/dev/wgh_gec6818_leds_riq",O_RDWR);
	
	if(led_fd < 0)
	{
		perror("open /dev/wgh_gec6818_leds_riq:");
		
		return led_fd;
		
	}
	
	//打开gec6818_keys设备
	key_fd = open("/dev/wgh_gec6818_keys_irq",O_RDWR);
	
	if(key_fd < 0)
	{
		perror("open /dev/wgh_gec6818_keys_irq:");
		
		return key_fd;
		
	}

	
	
	while(1)
	{
		//读取按键的状态
		rt = ioctl(key_fd,GEC6818_KALL_STA,&key_val);
		
		if(rt == 0)
		{
			if(key_val&(0x1<<0))
				ioctl(led_fd,GEC6818_LED_ON,7);
			else 
			    ioctl(led_fd,GEC6818_LED_OFF,7);
			
			if(key_val&(0x1<<1))
				ioctl(led_fd,GEC6818_LED_ON,8);
			else 
			    ioctl(led_fd,GEC6818_LED_OFF,8);
			
			if(key_val&(0x1<<2))
				ioctl(led_fd,GEC6818_LED_ON,9);
			else 
			    ioctl(led_fd,GEC6818_LED_OFF,9);
			
			if(key_val&(0x1<<3))
				ioctl(led_fd,GEC6818_LED_ON,10);
			else 
			    ioctl(led_fd,GEC6818_LED_OFF,10);
		}
		printf("read key end\n");
		

	}
	
	close(led_fd);
	close(key_fd);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/wghkemo123/article/details/86498198
今日推荐