输入子系统模型 介绍
对于键盘、鼠标、触摸屏等输入设备的驱动程序来说,用户程序所调用的操作函数集基本上相同,这些驱动的主要区别在于各自的中断处理函数对硬件的访问不同,因此为了简化程序的设计,将所有输入设备的驱动简化,提出一些共性的东西形成输入子系统,让不同的驱动程序实现不同的硬件处理程序即可。
输入子系统如下图所示:
输入子系统由设备驱动层(input device driver),核心层(input core)和事件驱动层(event driver)三部份组成。任何一次输入事件,如鼠标移动,按键按下,都需要通过:
InputDeviceDriver->InputCore->EventDrive
才能到达用户空间的应用程序。
设备驱动层:
将底层的硬件输入转化为统一事件型式,向输入核心(InputCore)汇报。
输入核心层:
为设备驱动层提供输入设备注册与操作接口,如:input_ register_ device;通知事件处理层对事件进行处理;
事件驱动层:
主要作用是和用户空间交互,如提供read,open等设备方法,创建设备文件等。
事件类型:
EV_ RST:Reset
EV_KEY:按键
EV_ REL:相对坐标
EV_ABS:绝对坐标
EV_ MSC:其它
EV_LED :LED
EV_ SND: 声音
EV_ REP: Repeat
EV_ FF :力反馈
当事件类型为EV_KEY时,还需指明按键类型:
BTN_ LEFT:鼠标左键
BTN_ RIGHT:鼠标右键
BTN_0:数字0键
BTN_1:数字1键
输入型驱动模型图:
代码举例:
按键输入驱动设计(将前面的按键驱动进行改造):
驱动程序代码举例:
//初始化入口函数
static int button_init()
{
int ret;
/*分配输入型设备结构*/
button_dev = input_allocate_device();
/*申明所支持的事件类型*/
set_bit(EV_KEY,button_dev->evbit);
/*申明可能上报的键编号*/
set_bit(KEY_3,button_dev->keybit);
set_bit(KEY_4,button_dev->keybit);
/*注册输入型设备*/
input_register_device(button_dev);
//注册中断处理程序
request_irq(IRQ_EINT0,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4);
request_irq(IRQ_EINT2,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3);
//按键初始化
key_hw_init();
//创建工作
work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work1, work1_func);
/* 初始化定时器 */
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
/* 向内核注册一个定时器 */
add_timer(&buttons_timer);
/*初始化等待队列*/
init_waitqueue_head(&key_q);
return 0;
}
//定时器中断函数
void buttons_timer_function(unsigned long data)
{
unsigned int key_val;
key_val = readw(gpio_data)&0x1;
if (key_val == 0)
{
key_num = 4;
//上报事件
input_report_key(button_dev,KEY_4,1);
}
key_val = readw(gpio_data)&0x4;
if (key_val == 0)
{
key_num = 3;
//上报事件
input_report_key(button_dev,KEY_3,1);
}
//告诉核心上报结束
input_sync(button_dev);
}
应用程序代码举例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <linux/input.h>
int main(void)
{
int buttons_fd;
int key_value,i=0,count;
//对应的输入设备结构体
struct input_event ev_key;
buttons_fd = open("/dev/event1", O_RDWR);
if (buttons_fd < 0) {
perror("open device buttons");
exit(1);
}
for (;;) {
//特别注意:这里的读取函数的参数格式是固定的,必须这么写
count = read(buttons_fd,&ev_key,sizeof(struct input_event));
for(i=0; i<(int)count/sizeof(struct input_event); i++)
if(EV_KEY==ev_key.type)
printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code-1,ev_key.value);
if(EV_SYN==ev_key.type)
printf("syn event\n\n");
}
close(buttons_fd);
return 0;
}
触摸屏驱动设计
初始化:
1、使能ADC时钟
2、将物理地址转化为虚拟地址
3、让触摸屏进入到等待中断的模式
4、分配输入设备结构
5、设置可能上报的事件类型和按键类型
6、为TC和ADC中断注册处理函数
7、注册输入型设备
按下处理:
1、判断触摸屏按下还是弹起,按下后进入TC中断处理程序
2、启动X与Y坐标的AD转换,触发ADC中断
3、进行4次ADC转换,获取4次X与Y的值,考虑精度要求
4、计算4次坐标的平均值,并上报内核
源代码位置:
linux-tq2440\drivers\input\touchscreen\tq2440_ts.c