输入子系统event层分析.docx
《输入子系统event层分析.docx》由会员分享,可在线阅读,更多相关《输入子系统event层分析.docx(14页珍藏版)》请在冰豆网上搜索。
输入子系统event层分析
输入子系统--event层分析
drivers/input/keyboard/gpio_keys.c:
staticint__devinitgpio_keys_probe(structplatform_device*pdev)
{
structgpio_keys_platform_data*pdata=pdev->dev.platform_data;
structinput_dev*input;
inti,error;input=input_allocate_device();//申请input_dev结构
if(!
input)
return-ENOMEM;platform_set_drvdata(pdev,input);//把input_dev结构放好(以后方便调用)input->evbit[0]=BIT(EV_KEY);//目前event的类型不操作32,所以你会看到对于evbit数组的操作都是对evbit[0]中的位来进行操作.input->name=pdev->name;
input->phys="gpio-keys/input0";
input->dev.parent=&pdev->dev;input->id.bustype=BUS_HOST;
input->id.vendor=0x0001;
input->id.product=0x0001;
input->id.version=0x0100;for(i=0;i<pdata->nbuttons;i++){
structgpio_keys_button*button=&pdata->buttons[i];
intirq=gpio_to_irq(button->gpio);
unsignedinttype=button->type?
:
EV_KEY;set_irq_type(irq,IRQ_TYPE_EDGE_BOTH);/*根据用户所指定的gpio_keys来申请中断和注册中断处理函数*/
error=request_irq(irq,gpio_keys_isr,IRQF_SAMPLE_RANDOM,
button->desc?
button->desc:
"gpio_keys",
pdev);
if(error){
printk(KERN_ERR"gpio-keys:
unabletoclaimirq%d;error%d\n",
irq,error);
gotofail;
}input_set_capability(input,type,button->code);
}error=input_register_device(input);//注册输入设备,并和对应的handler处理函数挂钩
if(error){
printk(KERN_ERR"Unabletoregistergpio-keysinputdevice\n");
gotofail;
}return0;fail:
for(i=i-1;i>=0;i--)
free_irq(gpio_to_irq(pdata->buttons[i].gpio),pdev);input_free_device(input);returnerror;
}提到input_dev结构,以下谈一下我对于它的理解:
structinput_dev{void*private;constchar*name;
constchar*phys;
constchar*uniq;
structinput_idid;/*
*根据各种输入信号的类型来建立类型为unsignedlong的数组,*数组的每1bit代表一种信号类型,*内核中会对其进行置位或清位操作来表示时间的发生和被处理.
*/unsignedlongevbit[NBITS(EV_MAX)];
unsignedlongkeybit[NBITS(KEY_MAX)];
unsignedlongrelbit[NBITS(REL_MAX)];
unsignedlongabsbit[NBITS(ABS_MAX)];
unsignedlongmscbit[NBITS(MSC_MAX)];
unsignedlongledbit[NBITS(LED_MAX)];
unsignedlongsndbit[NBITS(SND_MAX)];
unsignedlongffbit[NBITS(FF_MAX)];
unsignedlongswbit[NBITS(SW_MAX)];.........................................
};/**
*input_set_capability-markdeviceascapableofacertainevent
*@dev:
devicethatiscapableofemittingoracceptingevent
*@type:
typeoftheevent(EV_KEY,EV_REL,etc...)
*@code:
eventcode
*
*Inadditiontosettingupcorrespondingbitinappropriatecapability
*bitmapthefunctionalsoadjustsdev->evbit.
*//*记录本设备对于哪些事件感兴趣(对其进行处理)*/
voidinput_set_capability(structinput_dev*dev,unsignedinttype,unsignedintcode)
{
switch(type){
caseEV_KEY:
__set_bit(code,dev->keybit);//比如按键,应该对哪些键值的按键进行处理(对于其它按键不予理睬)
break;caseEV_REL:
__set_bit(code,dev->relbit);
break;caseEV_ABS:
__set_bit(code,dev->absbit);
break;caseEV_MSC:
__set_bit(code,dev->mscbit);
break;caseEV_SW:
__set_bit(code,dev->swbit);
break;caseEV_LED:
__set_bit(code,dev->ledbit);
break;caseEV_SND:
__set_bit(code,dev->sndbit);
break;caseEV_FF:
__set_bit(code,dev->ffbit);
break;default:
printk(KERN_ERR
"input_set_capability:
unknowntype%u(code%u)\n",
type,code);
dump_stack();
return;
}__set_bit(type,dev->evbit);//感觉和前面重复了(前面一经配置过一次了)
}
EXPORT_SYMBOL(input_set_capability);staticirqreturn_tgpio_keys_isr(intirq,void*dev_id)
{
inti;
structplatform_device*pdev=dev_id;
structgpio_keys_platform_data*pdata=pdev->dev.platform_data;
structinput_dev*input=platform_get_drvdata(pdev);for(i=0;i<pdata->nbuttons;i++){
structgpio_keys_button*button=&pdata->buttons[i];
intgpio=button->gpio;if(irq==gpio_to_irq(gpio)){//判断哪个键被按了?
unsignedinttype=button->type?
:
EV_KEY;
intstate=(gpio_get_value(gpio)?
1:
0)^button->active_low;//记录按键状态input_event(input,type,button->code,!
!
state);//汇报输入事件
input_sync(input);//等待输入事件处理完成
}
}returnIRQ_HANDLED;
}/*
*input_event()-reportnewinputevent
*@dev:
devicethatgeneratedtheevent
*@type:
typeoftheevent
*@code:
eventcode
*@value:
valueoftheevent
*
*Thisfunctionshouldbeusedbydriversimplementingvariousinputdevices
*Seealsoinput_inject_event()
*/
voidinput_event(structinput_dev*dev,unsignedinttype,unsignedintcode,intvalue)
{
structinput_handle*handle;if(type>EV_MAX||!
test_bit(type,dev->evbit))//首先判断该事件类型是否有效且为该设备所接受
return;add_input_randomness(type,code,value);switch(type){caseEV_SYN:
switch(code){
caseSYN_CONFIG:
if(dev->event)
dev->event(dev,type,code,value);
break;caseSYN_REPORT:
if(dev->sync)
return;
dev->sync=1;
break;
}
break;caseEV_KEY:
/*
*这里需要满足几个条件:
*1:
键值有效(不超出定义的键值的有效范围)*2:
键值为设备所能接受(属于该设备所拥有的键值范围)*3:
按键状态改变了
*/if(code>KEY_MAX||!
test_bit(code,dev->keybit)||!
!
test_bit(code,dev->key)==value)
return;if(value==2)
break;change_bit(code,dev->key);//改变对应按键的状态/*如果你希望按键未释放的时候不断汇报按键事件的话需要以下这个(在简单的gpio_keys驱动中不需要这个,暂时不去分析)*/
if(test_bit(EV_REP,dev->evbit)&&
dev->rep[REP_PERIOD]&&dev->rep[REP_DELAY]&&
dev->timer.data&&value){
dev->repeat_key=code;
mod_timer(&dev->timer,jiffies+msecs_to_jiffies(dev->rep[REP_DELAY]));
}break;
........................................................if(type!
=EV_SYN)
dev->sync=0;if(dev->grab)
dev->grab->handler->event(dev->grab,type,code,value);
else
/*
*循环调用所有处理该设备的handle(event,mouse,ts,joy等),*如果有进程打开了这些handle(进行读写),则调用其对应的event接口向气汇报该输入事件.
*/
list_for_each_entry(handle,&dev->h_list,d_node)
if(handle->open)
handle->handler->event(handle,type,code,value);
}
EXPORT_SYMBOL(input_event);#########################################################################
好了,下面再来研究一下event层对于input层报告的这个键盘输入事件是如何来处理的.
#########################################################################drivers/input/evdev.c:
staticstructinput_handlerevdev_handler={
.event=evdev_event,
.connect=evdev_connect,
.disconnect=evdev_disconnect,
.fops=&evdev_fops,
.minor=EVDEV_MINOR_BASE,
.name="evdev",
.id_table=evdev_ids,
};staticvoidevdev_event(structinput_handle*handle,unsignedinttype,unsignedintcode,intvalue)
{
structevdev*evdev=handle->private;
structevdev_client*client;if(evdev->grab){
client=evdev->grab;do_gettimeofday(&client->buffer[client->head].time);
client->buffer[client->head].type=type;
client->buffer[client->head].code=code;
client->buffer[client->head].value=value;
client->head=(client->head+1)&(EVDEV_BUFFER_SIZE-1);kill_fasync(&client->fasync,SIGIO,POLL_IN);
}else
/*遍厉client_list链表中的client结构(代表些打开evdev的进程(个人理解^_^))*/
list_for_each_entry(client,&evdev->client_list,node){
/*填充代表该输入信号的structinput_event结构(事件,类型,键码,键值)*/
do_gettimeofday(&client->buffer[client->head].time);
client->buffer[client->head].type=type;
client->buffer[client->head].code=code;
client->buffer[client->head].value=value;
/*更新写指针*/
client->head=(client->head+1)&(EVDEV_BUFFER_SIZE-1);kill_fasync(&client->fasync,SIGIO,POLL_IN);//通知调用input_sync的进程:
输入事件经已处理完毕(通知底层).
}wake_up_interruptible(&evdev->wait);//唤醒睡眠在evdev->wait等待队列等待输入信息的进程(通知上层).
}###################################################################################
好了,至此一个按键的输入事件处理完毕,现在再来从上到上的来看看用户是如何获取这个输入事件的.
###################################################################################staticconststructfile_operationsevdev_fops={
.owner=THIS_MODULE,
.read=evdev_read,
.write=evdev_write,
.poll=evdev_poll,
.open=evdev_open,
.release=evdev_release,
.unlocked_ioctl=evdev_ioctl,
#ifdefCONFIG_COMPAT
.compat_ioctl=evdev_ioctl_compat,
#endif
.fasync=evdev_fasync,
.flush=evdev_flush
};staticintevdev_open(structinode*inode,structfile*file)
{
structevdev_client*client;
structevdev*evdev;
inti=iminor(inode)-EVDEV_MINOR_BASE;
interror;if(i>=EVDEV_MINORS)
return-ENODEV;evdev=evdev_table[i];if(!
evdev||!
evdev->exist)
return-ENODEV;client=kzalloc(sizeof(structevdev_client),GFP_KERNEL);
if(!
client)
return-ENOMEM;client->evdev=evdev;
/*添加evdev_client结构到链表evdev->client_list中(好让输入事件到来的时候填写该结构并唤醒进程读取)*/
list_add_tail(&client->node,&evdev->client_list);if(!
evdev->open++&&evdev->exist){
error=input_open_device(&evdev->handle);
if(error){
list_del(&client->node);
kfree(client);
returnerror;
}
}file->private_data=client;//存放好evdev_client结构方便以后使用
return0;
}staticssize_tevdev_read(structfile*file,char__user*buffer,size_tcount,loff_t*ppos)
{
structevdev_client*client=file->private_data;
structevdev*evdev=client->evdev;
intretval;if(count<evdev_event_size())//对于每次读取的数据大小是有一定的要求.
return-EINVAL;if(client->head==client->tail&&evdev->exist&&(file->f_flags&O_NONBLOCK))//缓存中没有数据可读且设备是存在的,
如果设置为NONBLOCK方式来读,立即返回.
return-EAGAIN;retval=wait_event_interruptible(evdev->wait,
client->head!
=client->tail||!
evdev->exist);//否则等待缓存有数据可读或设备不存在(被移去)
if(retval)
returnretval;if(!
evdev->exist)
return-ENODEV;while(client->head!
=client->tail&&retval+evdev_event_size()<=count){//下面开始读取数据structinput_event*event=(structinput_event*)client->buffer+client->tail;//获取缓存中的读指针if(evdev_event_to_user(buffer+retval,event))//返回数据给用户
return-EFAULT;