linux input子系统详截.docx
- 文档编号:6048001
- 上传时间:2023-01-03
- 格式:DOCX
- 页数:22
- 大小:69.84KB
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;i if((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
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux input子系统详截 input 子系统