Linux设备模型之input子系统详解.docx

上传人:b****6 文档编号:3480976 上传时间:2022-11-23 格式:DOCX 页数:26 大小:28.22KB
下载 相关 举报
Linux设备模型之input子系统详解.docx_第1页
第1页 / 共26页
Linux设备模型之input子系统详解.docx_第2页
第2页 / 共26页
Linux设备模型之input子系统详解.docx_第3页
第3页 / 共26页
Linux设备模型之input子系统详解.docx_第4页
第4页 / 共26页
Linux设备模型之input子系统详解.docx_第5页
第5页 / 共26页
点击查看更多>>
下载资源
资源描述

Linux设备模型之input子系统详解.docx

《Linux设备模型之input子系统详解.docx》由会员分享,可在线阅读,更多相关《Linux设备模型之input子系统详解.docx(26页珍藏版)》请在冰豆网上搜索。

Linux设备模型之input子系统详解.docx

Linux设备模型之input子系统详解

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

#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);

/*

*Ifdelayandperiodarepre-setbythedriver,thenautorepeating

*ishandledbythedriveritselfandwedon'tdoitininput.c.

*/

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();

/*

*Sincewearesupposedtobecalledfrom->connect()

*whichismutuallyexclusivewith->disconnect()

*wecan'tberacingwithinput_unregister_handle()

*andsoseparatelockisnotneededhere.

*/

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(co

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1