一,input子系统介绍
linux系统提供了input子系统,按键,触摸屏,键盘,鼠标等输入都可以利用input接口函数来实现设备驱动,
linux内核中,input设备用input_dev结构体描述,使用input子系统实现输入设备驱动的时候,驱动的核心工作是向系统报告按键,触摸屏,键盘,鼠标等输入事件(event,通过input_event结构体描述),不再需要关心文件操作接口,因为input子系统已经完成了文件操作接口,驱动报告的事件经过InputCore和Eventhandler最终到达用户空间。
在Linux中,输入子系统是由输入子系统设备驱动层、输入子系统核心层(Input Core)和输入子系统事件处理层(Event Handler)组成。其中设备驱动层提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转换为标准的输入事件,再通过核心层提交给事件处理层;而核心层对下提供了设备驱动层的编程接口,对上又提供了事件处理层的编程接口;而事件处理层就为我们用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。所以这使得我们输入设备的驱动部分不在用关心对设备文件的操作,而是要关心对各硬件寄存器的操作和提交的输入事件。
在 Linux中,输入子系统作为一个模块存在,向上,为用户层提供接口函数,向下,为驱动层程序提供统一的接口函数。其构建非常灵活,只需要调用一些简单的函数,就可以将一个输入设备的功能呈现给应用程序。这样,就能够使输入设备的事件通过输入子系统发送给用户层应用程序,用户层应用程序也可以通过输入子系统通知驱动程序完成某项功能。
二,input工作结构图
通过input子系统,具体的输入设备驱动只需要完成如下工作。
(1)在模块加载函数中告知input子系统它可以报告的事件。
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 力反馈
set_bit(EV_KEY,button_dev.evbit);
在模块加载函数中注册输入设备
(2)注册输入设备的函数为:
int input_register_device(struct input_dev *dev);
在键被按下/抬起,触摸屏被触摸/抬起/移动,鼠标被移动/单击/抬起时
通过input_report_xxx()报告发送的事件及对应的键值/坐标等状态。
主要的事件类型包括EV_KEY(按键事件),EV_REL(相对值,如光标移动,报告的是相对最后一次位置的偏移)和EV_ABS(绝对值,如触摸屏和操作杆,它们工作在绝对坐标系统)。
用于报告EV_KEY,EV_REL,EV_ABS事件的函数分别为:
void input_report_key(struct input_dev *dev,unsigned int code,int value);
void input_report_rel(struct input_dev *dev,unsigned int code,int value);
void input_report_abs(struct input_dev *dev,unsigned int code,int value);
code:事件的代码。如果事件的类型是EV_KEY,该代码code为设备键盘代码,代码值0~127为键盘上的按键代码,0x110~0x116为鼠标上按键代码,其中0x110(BTN_LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标右键,0x112(BTN_MIDDLE)为鼠标中键,其它代码含义参看include/linux/input.h文件。
value:事件的值,如果事件的类型是EV_KEY,当按键按下时值为1,松开时值为0.
input_sync()用于事件同步,它告知事件的接收者驱动已经发出了一个完整的报告。
(3)例如,在触摸屏设备驱动中,一次坐标及按下状态的整个报告过程如下: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,pres);//压力
input_sync(input_dev);//同步,告知所有事件已经报告完毕
(4) 在模块卸载函数中注销输入设备
注销输入设备的函数为:
void input_unregister_device(struct input_dev *dev);
以下是一个最简单的使用input接口实现按键设备驱动的范例,它在中断服务程序中向系统报告按键及同步事件。
eg:最简单的input设备驱动
//在按键中断中报告事件
static void button_interrupt(int irq,void *dummy,struct pt_regs *fp)
{
input_report_key(&button_dev,BIN_1,inb(BUTTON_PORT)&1);
input_sync(&button_dev);
}
static int __init button_init(void)
{
//申请中断
if(request_irq(BUTTON_IRQ,button_interrupt,0,"button",NULL))
{
printk(KERN_ERR "button.c:Can't allocate irq %d\n",button_irq);
return -EBUSY;
}
button_dev.evbit[0]= BIT(EV_KEY);//支持EV_KEY事件
button_dev.keybit[LONG(BTN_0)]=BIT(BTN_0);
input_register_device(&button_dev)//注册input设备
}
static void __exit button_exit(void)
{
input_unregister_device(&button_dev);//注销input设备
free_irq(BUTTON_IRQ,button_interrupt);//释放中断
}