Linux输入子系统——按键驱动实例

来源:互联网 发布:ubuntu不安装图形界面 编辑:程序博客网 时间:2024/06/08 18:53
<span style="font-size:18px;">#include<linux/init.h> #include<linux/module.h>#include<linux/fs.h>#include<linux/device.h>   #include<linux/slab.h>#include<linux/cdev.h>#include<linux/uaccess.h>#include<linux/miscdevice.h>#include<asm/io.h>// ioremap,ioread32等IO内存操作函数定义#include<linux/irq.h>// 中断相关#include<linux/interrupt.h>#include<asm/irq.h>#include <linux/sched.h>// 等待队列相关头文件#include <linux/poll.h>// poll#include <linux/input.h>// Input子系统头语言件#define DEV_NAME "key_input"// 设备名#define GPFCON   0X56000050// EINT 0 - 4, 即GPFCON控制引脚寄存器地址#define GPFDAT 0X56000054// EINT 0 - 4,也即GPFDAT寄存器地址#define GPFUDP 0X56000058// Pull-up/down control register for port F// IO内存映射后的虚拟地址,对其进行读写volatile unsigned long *gpfcon = NULL;volatile unsigned long *gpfdat = NULL;volatile unsigned long *gpfupd = NULL;/*--------------- 按键描述 ----------------*/struct key_des{int irq;// 按键中断号int number;// 按键编号(对应实际键盘值)unsigned long flags;// 中断触发标志,上升?下降?双边沿?char *name;// 中断名称};/* Helper2416开发板上按键定义 */static struct key_des key_irqs[] = {{IRQ_EINT0,0,IRQ_TYPE_EDGE_BOTH,"KEY1"},// 双边沿触发{IRQ_EINT1,1,IRQ_TYPE_EDGE_BOTH,"KEY2"},{IRQ_EINT2,2,IRQ_TYPE_EDGE_BOTH,"KEY3"},{IRQ_EINT3,3,IRQ_TYPE_EDGE_BOTH,"KEY4"},{IRQ_EINT4,4,IRQ_TYPE_EDGE_BOTH,"KEY5"},};static struct key_des *key_event;// key_event变量(用于中断源检测)static struct input_dev *key_input_dev;// 定义一个input设备(指针)/*---------定时器-----------------*/static struct timer_list key_timer;/*-------- 定时器服务函数-------- */void key_timer_func(unsigned long arg){int number = key_event->number;// 获得中断标号int code = -1;int value;// 读取IO引脚电平,获理相应中断值value  = ( ioread32(gpfdat) & (1 << number) )? 1:0 ;// 按键功能重定义switch(number){case 0:{code = KEY_ENTER;break;}case 1:{code = KEY_RIGHT;break;}case 2:{code = KEY_LEFT; break;}case 3:{code = KEY_DOWN; break;}case 4:{code = KEY_UP;   break;}}// 上报用户键值信息/*input_event() - report new input event* @dev:   device that generated the event* @type:  type of the event* @code:  event code* @value: value of the event*/input_event(key_input_dev,EV_KEY,code,value);input_sync(key_input_dev);}// 中断服务程序static irqreturn_t key_interrupt_func(int irq, void *dev_id){// 读取得到按键源key_event = (struct key_des *)dev_id;// HZ就是一秒,除以100就是100msmod_timer(&key_timer,jiffies + HZ/50);return IRQ_RETVAL(IRQ_HANDLED);}//  input initstatic int s3c2416_key_init(void){int i;int error;// 1. 分配一个结构体并对其成员进行初始化key_input_dev = input_allocate_device();if(key_input_dev == NULL){printk(KERN_ERR "key_input.c: Not enough memory\n");return -ENOMEM;}// 2.  设置// 2.1 type of the eventset_bit(EV_KEY,key_input_dev->evbit);// 产生按键事件// 2.2 event codeset_bit(KEY_UP,   key_input_dev->keybit);    set_bit(KEY_DOWN, key_input_dev->keybit);set_bit(KEY_LEFT, key_input_dev->keybit);set_bit(KEY_REGHT,key_input_dev->keybit);set_bit(KEY_ENTER,key_input_dev->keybit);// 注册input deviceerror = input_register_device(key_input_dev);if(error){printk(KERN_ERR "key_input.c: input_register_device error\n");}/* 初始化定时器 */init_timer(&key_timer);// 初始化定时器key_timer.function = &key_timer_func;// 安装定时器服务函数add_timer(&key_timer);// 添加定时器到内核定时器动态链表// 申请中断资源for(i = 0; i < sizeof(key_irqs)/sizeof(key_irqs[0]); i ++){error = request_irq(key_irqs[i].irq, // 中断号  key_interrupt_func,// 中断服务函数  key_irqs[i].flags,// 中断标志  key_irqs[i].name, // 中断名称  (void *)&key_irqs[i]); // 传递给中服务函数的参数}// 3. 将物理寄存器地址映射到IO内存gpfcon = (volatile unsigned long *)ioremap(GPFCON, 4);gpfdat = (volatile unsigned long *)ioremap(GPFDAT, 4);gpfupd = (volatile unsigned long *)ioremap(GPFUDP, 4);// 执行到此,中断注册成功, 配置GPFUPD寄存器为上拉*gpfupd &= ~ ( (3 << 0) | (3 << 2) | (3 << 4) | (3 << 6) | (3 << 8) );*gpfupd |= ((2 << 0) | (2 << 2) | (2 << 4) | (2 << 6) | (2 << 8));return 0;}static void s3c2416_key_exit(void){int i;// 释放申请的中断资源for(i = 0; i < sizeof(key_irqs)/sizeof(key_irqs[0]); i ++){free_irq(key_irqs[i].irq,(void *)&key_irqs[i]);}// 2. 释放映射的IO内存空间iounmap(gpfcon);iounmap(gpfdat);iounmap(gpfupd);// 删除定时器del_timer(&key_timer);// 取消注册 input_unregister_device(key_input_dev);// 释放input devinput_free_device(key_input_dev);}module_init(s3c2416_key_init);module_exit(s3c2416_key_exit);MODULE_LICENSE("GPL");</span>

0 0
原创粉丝点击