input 子系统
Linux系统提供了input子系统,按键、触摸屏、鼠标等输入型设备都可以使用input接口函数模型来设计设备驱动,通过这些设备Linux能够获取到一些按键键值、坐标等信息。
input子系统模型。
系统由3部分组成,1:driver驱动程序(工程师需要完成)2:input core 3:事件处理Event Handler,这两者内核已提供。我们只需按照input core接口规范实现驱动程序。
当一个事件发生时,如鼠标移动,键盘按下,事件首先进入driver层,然后driver把事件传给input core,再把时间传给event handler,然后把事件反馈到设备文件(用户空间)。
input体系结构
驱动层:把底层的硬件输入转化成input子系统规定的统一事件形式,向input core汇报。
input core: 把事件交给event handler处理。
Event Handler:主要作用同用户空间应用程序交互,把事件交给用户空间。
input驱动程序设计
设备用input_dev结构描述,驱动核心工作是向系统报告按键、触摸屏、键盘、鼠标等输入事件(event,通过input_event结构体描述),不再需要关心文件操作的接口file_operation,用为input子系统已经完成文件操作的接口。
int input_register_device (struct input_dev *dev) 注册输入设备的函数
void input_unregister_device (struct input_dev *dev ) 注销输入设备的函数
驱动的实现 :
设备驱动通过set_bit()告诉input子系统它支持那些事件,那些按键等
set_bit(EV_KEY, button_dev.evbit) 第二的参数button_dev是struct input_dev类型,有两个成员evbit:事件类型,keybit:按键类型,告诉设备支持按键。
事件类型:
EV_RST Reset EV_KEY 按键 EV_REL 相对坐标
EV_ABS 相对坐标 EV_MSC 其他 EV_LED LED
EV_SND 声音 EV_REP Repeat EV_FF 力反馈
当事件类型为EV_KEY时,还需指明按键类型,设置进keybit
BTN_LEFT:鼠标左键 BTN_0:数字0键 BTN_1:数字1键
BTN_RIGHT:鼠标右键 BTN_MIDDLE:鼠标中键
当事件发生时,比如按键按下或弹起,应往input子系统报告这些事件,应使用input_report_key(struct input_dev *dev,unsigned int code, int value)报告EV_KEY事件,
code:如果按下按键,code记录按下的是那个键,如果事件类型是EV_KEY,改代码则为设备的键盘代码,例如键盘上的按键代码值为0~127,鼠标按键代码为0x110~0x116,其中0x110(BTN_LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标右键,0x112(BTN_MIDDLE)为鼠标中键,其他代码参照include/linux/input.h
value:事件的值。如果事件的类型为EV_KEY,当按键按下时值为1,松开时值为0。
报告结束后通过input_sync()告诉input子系统整个事件报告完了。
在触摸屏驱动程序,一次点击坐标信息的整个报告过程:
input_report_abs (input_dev, ABS_X, x) ; //X坐标
input_report_abs (input_dev, ABS_Y, y) ; //Y坐标
input_report_abs (input_dev, ABS_pressure, 1) ; //按下or弹起
input_sync(input_dev) ; //报告结束
按键驱动程序的改写
static int __init button_init (void ) { //模块初始化函数
if(request(BUTTON_IRQ, button_interrupt, 0, “button”, NULL)) //申请中断
return -EBUSY ;
set_bit(EV_KEY, button_dev.evbit);
//设备button_dev支持的集合里包含按键EV_KEY事件,放evbit里
set_bit(BTN_0, button_dev.keybit); //设置支持那些按键,放keybit里
set_bit(BTN_1, button_dev.keybit); //设备支持两个键
input_register_device(&button_dev); //注册input设备
}
//在按键中断中报告事件,支持的每个键都要报
static void button_interrupt (int irq, void *dummy, struct pt_regs *fp) {
input_report_key(&button_dev, BTN_0, inb(BUTTON_PART0)) ;
//通过读取寄存器BUTTON_PART0得到0号按键状态
input_report_key(&button_dev, BTN_1, inb(BUTTON_PART1));
input_sync(&button_dev);
}
剩下的工作,input core 和event handler 收集报告的事件,形成相应的数据,放到file_operation、字符文件相关的buffer里,供用户空间读取,在用户空间看到的还是字符设备,input子系统简化程序设计,两步,先初始化,后报告。
按键驱动程序的应用程序改写:
#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 ;
button_fd = open(“/dev/ event0”, O_RDWR) ;
for (; ;) {
count = read(buttons_fd, &ev_key, sizeof(struct input_event)) ;
//访问输入型设备读取的是一个struct input_event结构,包含了发生的事件
for(i = 0; i < (int)count/sizeof(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, ev_key.value) ;
if(EV_SYN == ev_key.type)
printf(“syn event\n\n”) ;
}
close(button_fd) ;
return 0 ;
}