Linux按键驱动程序设计(7)-阻塞型驱动程序设计

1.阻塞必要性 

  • 当一个设备无法立刻满足用户的读写请求时应当如何处理? 例如:调用read时,设备没有数据提供, 但以后可能会有;或者一个进程试图向设备写入数据,但是设备暂时没有准备好接收数据。当上述情况发生的时候,驱动程序应当(缺省地)阻塞进程,使它进入等待(睡眠)状态,直到请求可以得到满足。

2.内核等待队列

  • 在实现阻塞驱动的过程中,需要有一个“候车室”来安排被阻塞的进程“休息”,当唤醒它们的条件成熟时,则可以从“候车室”中将这些进程唤醒。而这个“候车室”就是内核等待队列
  • 内核等待队列的使用:
    • 1.定义等待队列:wait_queue_head_t my_queue
    • 2.初始化等待队列:init_waitqueue_head(&my_queue)
    • 3.定义和初始化等待队列:DECLARE_WAIT_QUEUE_HEAD(my_queue)
    • 4.进入等待队列,睡眠(下面3个函数都可让进程进入睡眠,只是睡眠状态不同)
      • wait_event(queue, condition)
        • 当condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂载queue参数所指定的等待队列上。
      • wait_event_interruptible(queue, condition)
        • 当condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_INTERRUPTIBLE的睡眠,并挂在queue参数所指定的等待队列上。
      • int wait_event_killable(queue, condition)
        • 当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠,并挂在queue参数所指定的等待队列上。
    • 5.从等待队列中唤醒进程
      • wake_up(wait_queue_t *q)
        • 从等待队列中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE的所有进程。
      • wake_up_interruptible(wait_queue_t *q)
        • 从等待队列中唤醒状态为TASK_INTERRUPTIBLE的进程。

3.对按键驱动进行阻塞型改造

  • key.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <linux/sched.h>

#define GPGCON 0x56000060
#define GPGDAT 0x56000064

struct work_struct *key_work; 
struct timer_list key_timer;

unsigned int *gpio_data;
unsigned int key_num = 0;

wait_queue_head_t key_q;  // 定义等待队列

// 工作队列函数
void key_work_func(struct work_struct *work)
{
	mod_timer(&key_timer, jiffies + HZ/10);  // 延时100ms	
}

// 判断按键是否真正按下
void key_timer_func(unsigned long data)
{
	unsigned short key_vall;
	key_vall = readw(gpio_data) & 0x01;
	if (key_vall == 0)
	{
		printk("key1 down!\n");
		key_num = 1;
	}

	key_vall = readw(gpio_data) & 0x20;
	if (key_vall == 0)
	{
		printk("key3 down!\n");
		key_num = 3;
	}
        
	wake_up(&key_q);  // 从等待队列中唤醒进程      
}

// 中断处理函数
irqreturn_t key_int(int irq, void *dev_id)
{
	// 1.检测是否发生了按键中断	
    
	// 2.清除已经发生的按键中断
    
	// 3.提交下半部分
	schedule_work(key_work);
    
	return IRQ_HANDLED;
}

void key_hw_init()
{
	unsigned int *gpio_config;
	unsigned int data;
    
	gpio_config = ioremap(GPGCON, 4);	
	data = readl(gpio_config);
	data &= ~0xc03;        // 设置GPIO寄存器
	data |= 0x802;
	writel(data, gpio_config);	
    
	gpio_data = ioremap(GPGDAT, 4);	
}                                                

int key_open(struct inode *node, struct file *filp)
{
	return 0;
}

ssize_t key_read (struct file *filp, char __user *buf, size_t size, loff_t *pos)
{	
	wait_event(key_q, key_num);  // 进入等待队列,睡眠
  
	printk("in kernel :key num is %d\n",key_num);
	copy_to_user(buf, &key_num, 4);
    
	key_num = 0;  // 清空
    
	return 4;	
}

const struct file_operations key_fops = 
{	
	.open = key_open,
	.read = key_read,		
};

// 初始化miscdevice
struct miscdevice key_miscdev = 
{
	.minor = 200,
	.name = "mykey",
	.fops = &key_fops,
};	

static int button_init()
{
	// 注册miscdevice
	misc_register(&key_miscdev);
    
	// 注册中断处理程序
	request_irq(IRQ_EINT8, key_int, IRQF_TRIGGER_FALLING, "mykey", 0);  // K1
	request_irq(IRQ_EINT13, key_int, IRQF_TRIGGER_FALLING, "mykey", 0);  // K3
    
	// 按键初始化
	key_hw_init();
    
	// 创建工作1
	key_work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
	INIT_WORK(key_work, key_work_func);
    
	// 初始化定时器
	init_timer(&key_timer);
	key_timer.function = key_timer_func;
    
	// 注册定时器
	add_timer(&key_timer);
    
	// 初始化等待队列
	init_waitqueue_head(&key_q);
    
	return 0;		
}

static void button_exit()
{	
    // 注销miscdevice
	misc_deregister(&key_miscdev);  
    
	// 注销中断处理程序
	free_irq(IRQ_EINT8, 0);
	free_irq(IRQ_EINT13, 0);
}

MODULE_LICENSE("GPL");

module_init(button_init);
module_exit(button_exit);

​
  • key_app.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    int fd;
    int key_num;   
   
    // 打开设备
    fd = open("/dev/mini2440key",0);
    
    if (fd < 0)
        printf("open device fail!\n");
    
    // 读取设备
    read(fd, &key_num, 4);
    printf("key is %d\n",key_num);
    
    // 关闭设备 
    close(fd);	
		
}
  • 驱动程序默认都是阻塞型驱动

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值