您好,欢迎访问三七文档
当前位置:首页 > 建筑/环境 > 电气安装工程 > linux input子系统详截
一:前言在键盘驱动代码分析的笔记中,接触到了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#includestaticvoidbutton_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_ERRbutton.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)-returnsthelengthofabitfieldarrayinlongsforxbitsLONG(x)-returnstheindexinthearrayinlongsforbitxBIT(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_INFOinput:%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注册到sysfslist_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_ERRinput: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;iBITS_TO_LONGS(max);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定
本文标题:linux input子系统详截
链接地址:https://www.777doc.com/doc-5087791 .html