[国嵌攻略][138][输入子系统模型解析]
为什么需要输入子系统
在Linux系统中按键属于输入型设备,同样的把按键换成鼠标、键盘等输入型设备,它们的注册和操作方法都是类似的,不同的是在中断处理中对硬件的操作。输入子系统就是把和输入设备有共性的部分提取出来,而把不同的部分让程序员来实现。
输入子系统模型
1.input device drivers,需要程序员来实现
2.input core,属于系统内核实现
3.input event drivers,属于系统内核实现
例如:
当按键被按下时,会产生一个事件,然后事件由input device drivers上报给input core,由input core处理后把事件交给input event drivers。input event drivers需要实现字符驱动和对应的系统调用接口。上层应用从input event drivers实现的系统调用接口中读取数据。input device drivers实现硬件相关的工作,input event drivers实现硬件无关的工作,这样就可以简化代码实现。
输入型设备驱动
输入设备事件:
EV_RST reset EV_KEY 按键 EV_LED LED EV_REL 相对坐标 EV_ABS 绝对坐标
EV_MSC 其他 EV_SND 声音 EV_REP repreat EV_FF 力反馈
当事件类型为EV_KEY时,还需要声明按键类型:
BTN_LEFT:鼠标左键 BTN_RIGHT:鼠标右键 BTN_0:数字0键 BTN_1:数字1键
初始化
1.分配input_dev结构,使用input_allocate_device
2.声明可能会上报的事件类型,使用set_bit
3.如果上报的是按键,声明可能上报的键值
4.注册输入型设备,使用input_register_device
上报
1.上报产生的事件,使用input_report_key
2.告诉核心上报结束,使用input_sync
注册输入型设备时,会自动创建设备文件,在/dev/目录下的event%d文件。
keydev.c
/******************************************************************** *头文件 *********************************************************************/ #include <linux/init.h> #include <linux/module.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <linux/sched.h> /******************************************************************** *宏定义 *********************************************************************/ #define GPGCON 0x56000060 //按键控制寄存器地址 #define GPGDAT 0x56000064 //按键数据寄存器地址 /******************************************************************** *全局变量 *********************************************************************/ unsigned int *keycon; //按键控制指针 unsigned int *keydat; //按键数据指针 struct work_struct *work; //按键工作 struct timer_list timer; //按键定时 struct input_dev *keydev; //按键输入设备 /******************************************************************** *定时处理 *********************************************************************/ //设备定时 void key_timer(unsigned long data){ //读取按键状态 unsigned short datTmp; int key1, key2; datTmp = readw(keydat); //获取GPGDAT值 key1 = datTmp & (1<<0); //获取key1电平 key2 = datTmp & (1<<3); //获取key2电平 //判断按键状态 if(key1 == 0){ input_report_key(keydev, KEY_1, 1); //上报按键事件 printk("key1 down!\n"); } if(key2 == 0){ input_report_key(keydev, KEY_2, 1); //上报按键事件 printk("key2 down!\n"); } //上报事件完成 input_sync(keydev); } /******************************************************************** *中断处理 *********************************************************************/ //设备中断下部 void key_work(struct work_struct *work){ //启动定时设备 mod_timer(&timer, jiffies + HZ/100); //定时10ms,jiffies表示系统当前嘀嗒数,1HZ = 1s = 1000jiffies } //设备中断上部 irqreturn_t key_irq(int irq, void *dev_id){ //处理硬件相关 //提交按键工作 schedule_work(work); return 0; } /******************************************************************** *模块安装 *********************************************************************/ //注册硬件 void handware_init(void){ //初始化按键控制寄存器 unsigned int conTmp; keycon = ioremap(GPGCON, 4); //虚拟地址映射 conTmp = readl(keycon); //获取GPGCON值 conTmp &= ~((0x3<<6) | (0x3<<0)); //GPB3[7:6]:00,GPG0[1:0]:00 conTmp |= ((0x2<<6) | (0x2<<0)); //GPB3[7:6]:EINT[11],GPG0[1:0]:EINT[8] writel(conTmp, keycon); //设置GPGCON值 //初始化按键状态寄存器 keydat = ioremap(GPGDAT, 2); //虚拟地址映射 } //安装模块 static int ikey_init(void){ //分配输入设备 keydev = input_allocate_device(); //声明按键事件 set_bit(EV_KEY, keydev->evbit); //声明按键编号 set_bit(KEY_1, keydev->keybit); set_bit(KEY_2, keydev->keybit); //注册输入设备 input_register_device(keydev); //注册硬件设备 handware_init(); //注册中断处理 request_irq(IRQ_EINT8, key_irq, IRQF_TRIGGER_FALLING, "ikey", (void *)1); //下降沿触发,IRQ_EINT8定义在irqs.h文件中 request_irq(IRQ_EINT11, key_irq, IRQF_TRIGGER_FALLING, "ikey", (void *)2); //注册工作队列 work = kmalloc(sizeof(struct work_struct), GFP_KERNEL); INIT_WORK(work, key_work); //注册定时设备 init_timer(&timer); //初始化定时器 timer.function = key_timer; //添加定时函数 add_timer(&timer); //添加定时设备 return 0; } //卸载模块 static void ikey_exit(void){ //注销输入设备 input_unregister_device(keydev); //注销中断处理 free_irq(IRQ_EINT8, (void *)1); free_irq(IRQ_EINT11, (void *)2); } /******************************************************************** *模块声明 *********************************************************************/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("D"); MODULE_DESCRIPTION(""); MODULE_VERSION("v1.0"); module_init(ikey_init); module_exit(ikey_exit);
keyapp.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <linux/input.h> int main(int argc, char **argv){ //打开设备 int fd; fd = open("/dev/event1", O_RDWR); while(1){ //读取设备 struct input_event keyevt; read(fd, &keyevt, sizeof(keyevt)); //显示数据 if(keyevt.type == EV_KEY){ printf("Event type:%d code:%d value:%d\n", keyevt.type, keyevt.code - 1, keyevt.value); } if(keyevt.type == EV_SYN){ printf("Sync event\n"); } } //关闭设备 close(fd); return 0; }