mini2440 按键驱动添加输入子系统,让按键可以真正当做键盘一样用

来源:互联网 发布:胡鞍钢为什么得宠知乎 编辑:程序博客网 时间:2024/06/15 16:59

这个实验没有添加太多的东西,就只是在原来的基础上添加了输入子系统的支持,按键的中断发生的时候,会向内核发送event,使得evinput.c和keboard.c等文件里面实现的handler可以对按键事件进行相应,六个按键分别向内核发送KEY_L, KEY_S, KEY_3,KEY_4,KEY_5,KEY_ENTER这六个event,加载驱动之后不需要运行任何自己写的用户层程序,只需要运行cat /dev/tty1(要先在/etc/init.d/rcS里面设置不启动Qt),就可以用按键输入上面的六种字符了,之后输入一次回车之后,缓存在tty1里面的输入数据才会一次全部显示出来。

驱动代码:

#include <linux/module.h>#include <linux/kernel.h>#include <linux/device.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/gpio.h>#include <linux/sched.h>#include <linux/wait.h>#include <linux/poll.h>#include <linux/input.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/io.h>#include <mach/regs-gpio.h>#defineGRH_MODULE_NAME"key_interrupt"static int major;static struct class *key_interrupt_class;static struct class_device *key_interrupt_device; static int key_value;//wait_event_interruptible函数需要的两个变量static DECLARE_WAIT_QUEUE_HEAD(grh_wait_interrupt); //休眠的进程队列头static volatile int sleep_for_interrupt; //这个变量为0的时候read函数会休眠,中断里面将其置1,read函数末尾将其设置为0//异步信号队列定义static struct fasync_struct *grh_async_queue;//定义一个定时器,用于按键防抖动static struct timer_list grh_timer_shake;static int first_timer_timeout;//pin_desc是对每一个按键中断的描述,不仅仅可以是整数,也可以是更复杂到的字段,这里用简单的按键值就行了int pin_desc[6] = {1, 2, 3, 4, 5, 6};//输入子系统相关的数据结构static struct input_dev *key_interrupt_input_dev;//中断处理函数static irqreturn_t grh_handle_key_eint(int irq, void *dev_id){int *p;p = dev_id;//printk(KERN_EMERG"key pressed! key=%d\n", *p);key_value = *p;//重新设置定时器的超时时间,其中HZ是一秒钟对应的系统计数值,下面的定时器是500ms的mod_timer(&grh_timer_shake, jiffies+HZ/2);//唤醒休眠进程和发送异步信号的工作交给定时器超时处理函数来实现return IRQ_HANDLED;}static void grh_timer_shake_handler(unsigned long n){int event_v;if(first_timer_timeout){first_timer_timeout = 0;return;}//唤醒休眠的进程sleep_for_interrupt = 1;wake_up_interruptible(&grh_wait_interrupt);//向用户空间进程发送异步信号kill_fasync(&grh_async_queue, SIGIO, POLL_IN);//上报事件if(key_value == 1)event_v = KEY_L;else if(key_value == 2)event_v = KEY_S;else if(key_value == 6)event_v = KEY_ENTER;elseevent_v = KEY_ESC+key_value;input_event(key_interrupt_input_dev, EV_KEY, event_v, 1); //按键按下事件input_sync(key_interrupt_input_dev);input_event(key_interrupt_input_dev, EV_KEY, event_v, 0); //按键松开事件input_sync(key_interrupt_input_dev);}static void init_key(void){//注册irq中断处理函数,将按键值和中断号绑定,所有清中断操作以及初始化中断相关寄存器的操作全部交给//内核自动完成了,不再需要像裸机程序一样显式地对寄存器进行读写了,中断发生后会自动跳到grh_handle_key_eintrequest_irq(IRQ_EINT8, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key1", pin_desc);request_irq(IRQ_EINT11, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key2", pin_desc+1);request_irq(IRQ_EINT13, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key3", pin_desc+2);request_irq(IRQ_EINT14, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key4", pin_desc+3);request_irq(IRQ_EINT15, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key5", pin_desc+4);request_irq(IRQ_EINT19, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key6", pin_desc+5);}static int key_interrupt_open(struct inode *inode, struct file *file){printk(KERN_EMERG"DRIVER: OPEN\n");sleep_for_interrupt = 0;return 0;}static ssize_t key_interrupt_write(struct inode *inode, const char __user *buf, size_t count, loff_t *ppos){printk(KERN_EMERG"DRIVER: WRITE\n");return 0;}static ssize_t key_interrupt_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){printk(KERN_EMERG"DRIVER: READ\n");//根据sleep_for_interrupt的数值决定是否将驱动进程加进休眠队列grh_wait_interrupt中,立即休眠进程wait_event_interruptible(grh_wait_interrupt, sleep_for_interrupt);copy_to_user(buf, &key_value, 4);//下一次进入read的时候继续休眠等待中断发生sleep_for_interrupt = 0;return 0;}int key_interrupt_release(struct inode *inode, struct file *file){//注销中断free_irq(IRQ_EINT8, pin_desc);free_irq(IRQ_EINT11, pin_desc+1);free_irq(IRQ_EINT13, pin_desc+2);free_irq(IRQ_EINT14, pin_desc+3);free_irq(IRQ_EINT15, pin_desc+4);free_irq(IRQ_EINT19, pin_desc+5);printk(KERN_EMERG"DRIVER: RELEASE\n");return 0;}//sys_poll会反复在死循环里面调用key_interrupt_pollstatic unsigned int key_interrupt_poll(struct file *file, struct poll_table_struct *wait){unsigned int mask;printk(KERN_EMERG"DRIVER POLL\n");poll_wait(file, &grh_wait_interrupt, wait); //把当前进程挂到休眠队列里面,但是不立即休眠mask = 0;if(sleep_for_interrupt){ //中断发生了mask |= POLLIN | POLLRDNORM; //把有可读数据的标志位设置为1,用户层可以得到这个mask}return mask;/*如果返回的mask是0那么进程直接进入定时休眠,如果在定时休眠过程中中断发生了,sys_poll里面的定时休眠结束,sys_poll又会循环调用key_interrupt_poll,但这个时候mask一定返回非零数值了,这时sys_poll中的休眠结束,进程继续运行。如果定时休眠过程中中断一直没有发生,那么定时休眠超时之后,sys_poll再调用一次key_interrupt_poll,然会判断是否超时,如果超时直接结束进程的休眠。这是Linux内核的poll机制的大概原理,这样在用户层调用一次poll函数,进程最多休眠时间就是传进来的wait参数,中断一发生,定时休眠立刻结束,否则进程就休眠到一次定时休眠结束为止。*/}//用户空间程序调用fcntl(fd, F_SETFL, flag | FASYNC)的时候,下面的异步通知设置函数会被调用static int key_interrupt_fasync(int fd, struct file *file, int on){//fasync_helper函数会将用户空间进程的pid传入grh_async_queue里面//这样中断处理函数里发出的信号才能被用户空间的应用程序收到printk(KERN_EMERG"DRIVER : FASYNC\n");//初始化grh_async_queue的工作交给fasync_helper来做,驱动程序不去实现了return fasync_helper(fd, file, on, &grh_async_queue); }static struct file_operations key_interrupt_fops = {.owner = THIS_MODULE,.open = key_interrupt_open,.write = key_interrupt_write,.read = key_interrupt_read,.release = key_interrupt_release,.poll = key_interrupt_poll,.fasync = key_interrupt_fasync,};int key_interrupt_module_init(void){printk(KERN_EMERG"INIT MODULE!\n");//输入子系统相关开始//分配一个input_dev结构体key_interrupt_input_dev = input_allocate_device();//对input_dev进行设置set_bit(EV_KEY, key_interrupt_input_dev->evbit); //设置input_devne能够产生键盘类事件//设置input_dev能够产生哪些键盘事件set_bit(KEY_L, key_interrupt_input_dev->keybit);set_bit(KEY_S, key_interrupt_input_dev->keybit);set_bit(KEY_3, key_interrupt_input_dev->keybit);set_bit(KEY_4, key_interrupt_input_dev->keybit);set_bit(KEY_5, key_interrupt_input_dev->keybit);set_bit(KEY_ENTER, key_interrupt_input_dev->keybit);//注册input_devinput_register_device(key_interrupt_input_dev);//输入子系统相关结束init_key();//初始化防抖动的timer,默认的超时时间是0init_timer(&grh_timer_shake);grh_timer_shake.function = grh_timer_shake_handler;add_timer(&grh_timer_shake);first_timer_timeout = 1;//register the driver with the devicemajor = register_chrdev(0, GRH_MODULE_NAME, &key_interrupt_fops);//create my own device classkey_interrupt_class = class_create(THIS_MODULE, "key_interrupt_class");//create my device of my own classkey_interrupt_device = device_create(key_interrupt_class, NULL, MKDEV(major,0), NULL, "key_interrupt_device");return 0;}void key_interrupt_module_exit(void){unregister_chrdev(major, GRH_MODULE_NAME);device_unregister(key_interrupt_device);class_destroy(key_interrupt_class);input_unregister_device(key_interrupt_input_dev);input_free_device(key_interrupt_input_dev);printk(KERN_EMERG"EXIT MODULE!\n");}module_init(key_interrupt_module_init);module_exit(key_interrupt_module_exit);MODULE_AUTHOR("GRH");MODULE_VERSION("1.0");MODULE_DESCRIPTION("KEY POLL DRIVER");MODULE_LICENSE("GPL");


测试结果:



0 0
原创粉丝点击