linux input子系统详截.docx
《linux input子系统详截.docx》由会员分享,可在线阅读,更多相关《linux input子系统详截.docx(22页珍藏版)》请在冰豆网上搜索。
linuxinput子系统详截
一:
前言
在键盘驱动代码分析的笔记中,接触到了input子系统.键盘驱动,键盘驱动将检测到的所有按键都上报给了input子系统。
Input子系统是所有I/O设备驱动的中间层,为上层提供了一个统一的界面。
例如,在终端系统中,我们不需要去管有多少个键盘,多少个鼠标。
它只要从input子系统中去取对应的事件(按键,鼠标移位等)就可以了。
今天就对input子系统做一个详尽的分析.
下面的代码是基于linuxkernel2.6.25.分析的代码主要位于kernel2.6.25/drivers/input下面.
二:
使用input子系统的例子
在内核自带的文档Documentation/input/input-programming.txt中。
有一个使用input子系统的例子,并附带相应的说明。
以此为例分析如下:
#include
#include
#include
#include
#include
staticvoidbutton_interrupt(intirq,void*dummy,structpt_regs*fp)
{
input_report_key(&button_dev,BTN_1,inb(BUTTON_PORT)&1);
input_sync(&button_dev);
}
staticint__initbutton_init(void)
{
if(request_irq(BUTTON_IRQ,button_interrupt,0,"button",NULL)){
printk(KERN_ERR"button.c:
Can''tallocateirq%d\n",button_irq);
return-EBUSY;
}
button_dev.evbit[0]=BIT(EV_KEY);
button_dev.keybit[LONG(BTN_0)]=BIT(BTN_0);
input_register_device(&button_dev);
}
staticvoid__exitbutton_exit(void)
{
input_unregister_device(&button_dev);
free_irq(BUTTON_IRQ,button_interrupt);
}
module_init(button_init);
module_exit(button_exit);
这个示例module代码还是比较简单,在初始化函数里注册了一个中断处理例程。
然后注册了一个inputdevice.在中断处理程序里,将接收到的按键上报给input子系统。
文档的作者在之后的分析里又对这个module作了优化。
主要是在注册中断处理的时序上。
在修改过后的代码里,为inputdevice定义了open函数,在open的时候再去注册中断处理例程。
具体的信息请自行参考这篇文档。
在资料缺乏的情况下,kernel自带的文档就是剖析kernel相关知识的最好资料.
文档的作者还分析了几个api函数。
列举如下:
1):
set_bit(EV_KEY,button_dev.evbit);
set_bit(BTN_0,button_dev.keybit);
分别用来设置设备所产生的事件以及上报的按键值。
Structiput_dev中有两个成员,一个是evbit.一个是keybit.分别用表示设备所支持的动作和按键类型。
2):
input_register_device(&button_dev);
用来注册一个inputdevice.
3):
input_report_key()
用于给上层上报一个按键动作
4):
input_sync()
用来告诉上层,本次的事件已经完成了.
5):
NBITS(x)-returnsthelengthofabitfieldarrayinlongsforxbits
LONG(x)-returnstheindexinthearrayinlongsforbitx
BIT(x)-returnstheindexinalongforbitx
这几个宏在input子系统中经常用到。
上面的英文解释已经很清楚了。
三:
input设备注册分析.
Input设备注册的接口为:
input_register_device()。
代码如下:
intinput_register_device(structinput_dev*dev)
{
staticatomic_tinput_no=ATOMIC_INIT(0);
structinput_handler*handler;
constchar*path;
interror;
__set_bit(EV_SYN,dev->evbit);
init_timer(&dev->timer);
if(!
dev->rep[REP_DELAY]&&!
dev->rep[REP_PERIOD]){
dev->timer.data=(long)dev;
dev->timer.function=input_repeat_key;
dev->rep[REP_DELAY]=250;
dev->rep[REP_PERIOD]=33;
}
在前面的分析中曾分析过。
Input_device的evbit表示该设备所支持的事件。
在这里将其EV_SYN置位,即所有设备都支持这个事件.如果dev->rep[REP_DELAY]和dev->rep[REP_PERIOD]没有设值,则将其赋默认值。
这主要是处理重复按键的.
if(!
dev->getkeycode)
dev->getkeycode=input_default_getkeycode;
if(!
dev->setkeycode)
dev->setkeycode=input_default_setkeycode;
snprintf(dev->dev.bus_id,sizeof(dev->dev.bus_id),
"input%ld",(unsignedlong)atomic_inc_return(&input_no)-1);
error=device_add(&dev->dev);
if(error)
returnerror;
path=kobject_get_path(&dev->dev.kobj,GFP_KERNEL);
printk(KERN_INFO"input:
%sas%s\n",
dev->name?
dev->name:
"Unspecifieddevice",path?
path:
"N/A");
kfree(path);
error=mutex_lock_interruptible(&input_mutex);
if(error){
device_del(&dev->dev);
returnerror;
}
如果inputdevice没有定义getkeycode和setkeycode.则将其赋默认值。
还记得在键盘驱动中的分析吗?
这两个操作函数就可以用来取键的扫描码和设置键的扫描码。
然后调用device_add()将input_dev中封装的device注册到sysfs
list_add_tail(&dev->node,&input_dev_list);
list_for_each_entry(handler,&input_handler_list,node)
input_attach_handler(dev,handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return0;
}
这里就是重点了。
将inputdevice挂到input_dev_list链表上.然后,对每一个挂在input_handler_list的handler调用input_attach_handler().在这里的情况有好比设备模型中的device和driver的匹配。
所有的inputdevice都挂在input_dev_list链上。
所有的handle都挂在input_handler_list上。
看一下这个匹配的详细过程。
匹配是在input_attach_handler()中完成的。
代码如下:
staticintinput_attach_handler(structinput_dev*dev,structinput_handler*handler)
{
conststructinput_device_id*id;
interror;
if(handler->blacklist&&input_match_device(handler->blacklist,dev))
return-ENODEV;
id=input_match_device(handler->id_table,dev);
if(!
id)
return-ENODEV;
error=handler->connect(handler,dev,id);
if(error&&error!
=-ENODEV)
printk(KERN_ERR
"input:
failedtoattachhandler%stodevice%s,"
"error:
%d\n",
handler->name,kobject_name(&dev->dev.kobj),error);
returnerror;
}
如果handle的blacklist被赋值。
要先匹配blacklist中的数据跟dev->id的数据是否匹配。
匹配成功过后再来匹配handle->id和dev->id中的数据。
如果匹配成功,则调用handler->connect().
来看一下具体的数据匹配过程,这是在input_match_device()中完成的。
代码如下:
staticconststructinput_device_id*input_match_device(conststructinput_device_id*id,
structinput_dev*dev)
{
inti;
for(;id->flags||id->driver_info;id++){
if(id->flags&INPUT_DEVICE_ID_MATCH_BUS)
if(id->bustype!
=dev->id.bustype)
continue;
if(id->flags&INPUT_DEVICE_ID_MATCH_VENDOR)
if(id->vendor!
=dev->id.vendor)
continue;
if(id->flags&INPUT_DEVICE_ID_MATCH_PRODUCT)
if(id->product!
=dev->id.product)
continue;
if(id->flags&INPUT_DEVICE_ID_MATCH_VERSION)
if(id->version!
=dev->id.version)
continue;
MATCH_BIT(evbit,EV_MAX);
MATCH_BIT(,,KEY_MAX);
MATCH_BIT(relbit,REL_MAX);
MATCH_BIT(absbit,ABS_MAX);
MATCH_BIT(mscbit,MSC_MAX);
MATCH_BIT(ledbit,LED_MAX);
MATCH_BIT(sndbit,SND_MAX);
MATCH_BIT(ffbit,FF_MAX);
MATCH_BIT(swbit,SW_MAX);
returnid;
}
returnNULL;
}
MATCH_BIT宏的定义如下:
#defineMATCH_BIT(bit,max)
for(i=0;iif((id->bit[i]&dev->bit[i])!
=id->bit[i])
break;
if(i!
=BITS_TO_LONGS(max))
continue;
由此看到。
在id->flags中定义了要匹配的项。
定义INPUT_DEVICE_ID_MATCH_BUS。
则是要比较inputdevice和inputhandler的总线类型。
INPUT_DEVICE_ID_MATCH_VENDOR,INPUT_DEVICE_ID_MATCH_PRODUCT,INPUT_DEVICE_ID_MATCH_VERSION分别要求设备厂商。
设备号和设备版本.
如果id->flags定义的类型匹配成功。
或者是id->flags没有定义,就会进入到MATCH_BIT的匹配项了.从MATCH_BIT宏的定义可以看出。
只有当iputdevice和inputhandler的id成员在evbit,keybit,…swbit项相同才会匹配成功。
而且匹配的顺序是从evbit,keybit到swbit.只要有一项不同,就会循环到id中的下一项进行比较.
简而言之,注册inputdevice的过程就是为inputdevice设置默认值,并将其挂以input_dev_list.与挂载在input_handler_list中的handler相匹配。
如果匹配成功,就会调用handler的connect函数.
四:
handler注册分析
Handler注册的接口如下所示:
intinput_register_handler(structinput_handler*handler)
{
structinput_dev*dev;
intretval;
retval=mutex_lock_interruptible(&input_mutex);
if(retval)
returnretval;
INIT_LIST_HEAD(&handler->h_list);
if(handler->fops!
=NULL){
if(input_table[handler->minor>>5]){
retval=-EBUSY;
gotoout;
}
input_table[handler->minor>>5]=handler;
}
list_add_tail(&handler->node,&input_handler_list);
list_for_each_entry(dev,&input_dev_list,node)
input_attach_handler(dev,handler);
input_wakeup_procfs_readers();
out:
mutex_unlock(&input_mutex);
returnretval;
}
handler->minor表示对应input设备节点的次设备号.以handler->minor右移五位做为索引值插入到input_table[]中..之后再来分析input_talbe[]的作用.
然后将handler挂到input_handler_list中.然后将其与挂在input_dev_list中的inputdevice匹配.这个过程和inputdevice的注册有相似的地方.都是注册到各自的链表,.然后与另外一条链表的对象相匹配.
五:
handle的注册
intinput_register_handle(structinput_handle*handle)
{
structinput_handler*handler=handle->handler;
structinput_dev*dev=handle->dev;
interror;
/*
*Wetakedev->mutexheretopreventracewith
*input_release_device().
*/
error=mutex_lock_interruptible(&dev->mutex);
if(error)
returnerror;
list_add_tail_rcu(&handle->d_node,&dev->h_list);
mutex_unlock(&dev->mutex);
synchronize_rcu();
list_add_tail(&handle->h_node,&handler->h_list);
if(handler->start)
handler->start(handle);
return0;
}
在这个函数里所做的处理其实很简单.将handle挂到所对应inputdevice的h_list链表上.还将handle挂到对应的handler的hlist链表上.如果handler定义了start函数,将调用之.
到这里,我们已经看到了inputdevice,handler和handle是怎么关联起来的了.以图的方式总结如下:
六:
event事件的处理
我们在开篇的时候曾以linuxkernel文档中自带的代码作分析.提出了几个事件上报的API.这些API其实都是input_event()的封装.代码如下:
voidinput_event(structinput_dev*dev,
unsignedinttype,unsignedintcode,intvalue)
{
unsignedlongflags;
//判断设备是否支持这类事件
if(is_event_supported(type,dev->evbit,EV_MAX)){
spin_lock_irqsave(&dev->event_lock,flags);
//利用键盘输入来调整随机数产生器
add_input_randomness(type,code,value);
input_handle_event(dev,type,code,value);
spin_unlock_irqrestore(&dev->event_lock,flags);
}
}
首先,先判断设备产生的这个事件是否合法.如果合法,流程转入到input_handle_event()中.
代码如下:
staticvoidinput_handle_event(structinput_dev*dev,
unsignedinttype,unsignedintcode,intvalue)
{
intdisposition=INPUT_IGNORE_EVENT;
switch(type){
caseEV_SYN:
switch(code){
caseSYN_CONFIG:
disposition=INPUT_PASS_TO_ALL;
break;
caseSYN_REPORT:
if(!
dev->sync){
dev->sync=1;
disposition=INPUT_PASS_TO_HANDLERS;
}
break;
}
break;
caseEV_KEY:
//判断按键值是否被支持
if(is_event_supported(code,dev->keybit,KEY_MAX)&&
!
!
test_bit(code,dev->key)!
=value){
if(value!
=2){
__change_bit(code,dev->key);
if(value)
input_start_autorepeat(dev,code);
}
disposition=INPUT_PASS_TO_HANDLERS;
}
break;
caseEV_SW:
if(is_event_supported(code,dev->swbit,SW_MAX)&&
!
!
test_bit(code,dev->sw)!
=value){
__change_bit(code,dev->sw);
disposition=INPUT_PASS_TO_HANDLERS;
}
break;
caseEV_ABS:
if(is_event_supported(code,dev->absbit,ABS_MAX)){
value=input_defuzz_abs_event(value,
dev->abs[code],dev->absfuzz[code]);
if(dev->abs[code]!
=value){
dev->abs[code]=value;
disposition=INPUT_PASS_TO_HANDLERS;
}
}
break;
caseEV_REL:
if(is_event_supported(code,dev->relbit,REL_MAX)&&value)
disposition=INPUT_PASS_TO_HANDLERS;
break;
caseEV_MSC:
if(is_event_supported(code,dev->mscbit,MSC_MAX))
disposition=INPUT_PASS_TO_ALL;
break;
caseEV_LED:
if(is_event_supported(code,dev->ledbit,LED_MAX)&&
!
!
test_bit(code,dev->led)!
=value){
__change_bit(code,dev->led);
disposition=INPUT_PASS_TO_ALL;
}
break;
caseEV_SND:
if(is_event_supported(code,dev->sndbit,SND_MAX)){
if(!
!
test_bit(code,dev->snd)!
=!
!
value)
__change_bit(code,dev->snd);
disposition=INPUT_PASS_TO_ALL;
}
break;
caseEV_REP:
if(code<=REP_MAX&&value>=0&&dev->rep[code]!
=value){
dev->rep[code]=value;
disposition=INPUT_PASS_TO_ALL;
}
break;
caseE