您的位置:首页 > 其它

输入子系统概念介绍

2014-05-02 22:28 351 查看
更多文档:http://pan.baidu.com/s/1jGzEzbw

输入子系统在内核中的位置:/driver/input

drivers/input/input.c:

input_init--->err=register_chrdev(INPUT_MAJOR,"input",&input_fops);

staticconststructfile_operationsinput_fops={

.owner=THIS_MODULE,

.open=input_open_file,

};

问:怎么读按键?

input_open_file

structinput_handler*handler;

handler=input_table[iminor(inode)>>5];

new_fops=fops_get(handler->fops)

file->f_op=new_fops;//这样当调用read时,就调用了具体设备驱动的read

err=new_fops->open(inode,file);

App:read--->…--->file->f_op->read

input_table数组由谁构造?(由于intput_table是一个static类型数组,可以在当前文件看到它是如何构建的)

intinput_register_handler(structinput_handler*handler)

input_table[handler->minor>>5]=handler;

其中函数input_register_handler会被具体的设备驱动程序调用,如:evdev.c、joydev.c、keyboard.c、mousedev.c、rfkill-input.c。

注册input_handler:
input_register_handler
//放入数组
input_table[handler->minor>>5]=handler;

//放入链表
list_add_tail(&handler->node,&input_handler_list);

//对于每个input_dev,调用input_attach_handler
list_for_each_entry(dev,&input_dev_list,node)
input_attach_handler(dev,handler);//根据input_handler的id_table判断能否支持这个input_dev

注册输入设备:
input_register_device
//放入链表
list_add_tail(&dev->node,&input_dev_list);

//对于每一个input_handler,都调用input_attach_handler
list_for_each_entry(handler,&input_handler_list,node)
input_attach_handler(dev,handler);//根据input_handler的id_table判断能否支持这个input_dev

input_attach_handler
id=input_match_device(handler->id_table,dev);

error=handler->connect(handler,dev,id);

注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"

怎么建立连接?(以evdev.c为例)
1.分配一个input_handle结构体
2.
input_handle.dev=input_dev;//指向左边的input_dev
input_handle.handler=input_handler;//指向右边的input_handler
3.注册:
input_handler->h_list=&input_handle;
input_dev->h_list=&input_handle;

evdev_connect
evdev=kzalloc(sizeof(structevdev),GFP_KERNEL);//分配一个input_handle

//设置
evdev->handle.dev=dev;//指向左边的input_dev
evdev->handle.name=evdev->name;
evdev->handle.handler=handler;//指向右边的input_handler
evdev->handle.private=evdev;

//注册
error=input_register_handle(&evdev->handle);

怎么读按键?(以evdev.c为例)
app:read
--------------------------
.......
evdev_read
//无数据并且是非阻塞方式打开,则立刻返回
if(client->head==client->tail&&evdev->exist&&(file->f_flags&O_NONBLOCK))
return-EAGAIN;

//否则休眠
retval=wait_event_interruptible(evdev->wait,
client->head!=client->tail||!evdev->exist);

谁来唤醒?(以evdev.c为例,在当前文件搜索evdev->wait)
evdev_event
wake_up_interruptible(&evdev->wait);

evdev_event被谁调用?
猜:应该是硬件相关的代码,input_dev那层调用的
在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数
gpio_keys_isr
//上报事件
input_event(input,type,button->code,!!state);
input_sync(input);

其中,input_event是在/drivers/input/input.c实现的
input_event(structinput_dev*dev,unsignedinttype,unsignedintcode,intvalue)
structinput_handle*handle;

list_for_each_entry(handle,&dev->h_list,d_node)
if(handle->open)
handle->handler->event(handle,type,code,value);





说明:

drivers/input/input.c构成了输入子系统的核心层。除了核心外,输入子系统分为硬件相关部分和纯软件handler部分,这两个都要调用核心层提供的函数接口,如硬件部分调用核心层提供的input_register_device完成注册,纯软件handler部分调用核心提供的input_register_handler完成注册。

不管是硬件部分还是软件部分先注册,他们都通过input_attach_handler(dev,handler)去匹配相适应的对方,如果匹配到,会调用handler提供的connect函数(在connect函数中会完成设备结点的注册,而在注册),在connect函数中会创建一个handle的结构体,这个结构体同时包含了input_dev和input_handler两个部分的信息,然后会把handle分别跟input_dev和input_handler关联起来,这样input_handler或者input_dev都可以通过handle找到对方。

以读为例,当调用open时,会调到input_handler提供的open函数,调用read时,会调到handler提供的read函数,因为在open时file->f_op被“调包”了。调用read,如果没有数据,read可能阻塞,它的唤醒是在硬件相关部分的中断处理函数中通过调用核心层提供的input_event完成的。核心层的input_event会调用到handler提供的XXX_event函数,在xxx_event函数中完成唤醒操作。

怎么写符合输入子系统框架的驱动程序?
1.分配一个input_dev结构体
2.设置
3.注册
4.硬件相关的代码,比如在中断服务程序里上报事件

structinput_dev{

void*private;

constchar*name;
constchar*phys;
constchar*uniq;
structinput_idid;

unsignedlongevbit[NBITS(EV_MAX)];//表示能产生哪类事件
unsignedlongkeybit[NBITS(KEY_MAX)];//表示能产生哪些按键
unsignedlongrelbit[NBITS(REL_MAX)];//表示能产生哪些相对位移事件,x,y,滚轮
unsignedlongabsbit[NBITS(ABS_MAX)];//表示能产生哪些绝对位移事件,x,y
unsignedlongmscbit[NBITS(MSC_MAX)];
unsignedlongledbit[NBITS(LED_MAX)];
unsignedlongsndbit[NBITS(SND_MAX)];
unsignedlongffbit[NBITS(FF_MAX)];
unsignedlongswbit[NBITS(SW_MAX)];

测试:
1.
hexdump/dev/event1(open(/dev/event1),read(),)
秒微秒类codevalue
00000000bb200000e48000c0001002600010000
00000100bb200000e54000c0000000000000000
00000200bb200005815000e0001002600000000
00000300bb20000581f000e0000000000000000

2.如果没有启动QT:
cat/dev/tty1
按:s2,s3,s4
就可以得到ls

或者:
exec0</dev/tty1
然后可以使用按键来输入

3.如果已经启动了QT:
可以点开记事本
然后按:s2,s3,s4

下面是buttons.c,可以在tq2440上运行:

[code]


/*参考drivers\input\keyboard\gpio_keys.c*/




#include<linux/module.h>


#include<linux/version.h>




#include<linux/init.h>


#include<linux/fs.h>


#include<linux/interrupt.h>


#include<linux/irq.h>


#include<linux/sched.h>


#include<linux/pm.h>


#include<linux/sysctl.h>


#include<linux/proc_fs.h>


#include<linux/delay.h>


#include<linux/platform_device.h>


#include<linux/input.h>


#include<linux/irq.h>




#include<asm/gpio.h>


#include<asm/io.h>


#include<mach/regs-gpio.h>




structpin_desc{


intirq;


char*name;


unsignedintpin;


unsignedintkey_val;


};




structpin_descpins_desc[4]={


{IRQ_EINT1,"K1",S3C2410_GPF1,KEY_L},


{IRQ_EINT4,"K2",S3C2410_GPF4,KEY_S},


{IRQ_EINT2,"K3",S3C2410_GPF2,KEY_ENTER},


{IRQ_EINT0,"K4",S3C2410_GPF0,KEY_LEFTSHIFT},


};




staticstructinput_dev*buttons_dev;


staticstructpin_desc*irq_pd;


staticstructtimer_listbuttons_timer;




staticirqreturn_tbuttons_irq(intirq,void*dev_id)


{


/*10ms后启动定时器*/


irq_pd=(structpin_desc*)dev_id;


mod_timer(&buttons_timer,jiffies+HZ/100);


returnIRQ_RETVAL(IRQ_HANDLED);


}




staticvoidbuttons_timer_function(unsignedlongdata)


{


structpin_desc*pindesc=irq_pd;


unsignedintpinval;




if(!pindesc)


return;




pinval=s3c2410_gpio_getpin(pindesc->pin);




if(pinval)


{


/*松开:最后一个参数:0-松开,1-按下*/


input_event(buttons_dev,EV_KEY,pindesc->key_val,0);//pindesc->key_val表示键值


input_sync(buttons_dev);


}


else


{


/*按下*/


input_event(buttons_dev,EV_KEY,pindesc->key_val,1);


input_sync(buttons_dev);


}


}




staticintbuttons_init(void)


{


inti;




/*1.分配一个input_dev结构体*/


buttons_dev=input_allocate_device();;




/*2.设置*/


/*2.1能产生哪类事件*/


set_bit(EV_KEY,buttons_dev->evbit);


set_bit(EV_REP,buttons_dev->evbit);//重复




/*2.2能产生这类操作里的哪些事件:L,S,ENTER,LEFTSHIT*/


set_bit(KEY_L,buttons_dev->keybit);//映射成字符'l'


set_bit(KEY_S,buttons_dev->keybit);//映射成字符's'


set_bit(KEY_ENTER,buttons_dev->keybit);//映射成回车键


set_bit(KEY_LEFTSHIFT,buttons_dev->keybit);//映射成shift键




/*3.注册*/


input_register_device(buttons_dev);




/*4.硬件相关的操作*/


init_timer(&buttons_timer);


buttons_timer.function=buttons_timer_function;


add_timer(&buttons_timer);




for(i=0;i<4;i++)


{


request_irq(pins_desc[i].irq,buttons_irq,IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,pins_desc[i].name,&pins_desc[i]);


}




return0;


}




staticvoidbuttons_exit(void)


{


inti;


for(i=0;i<4;i++)


{


free_irq(pins_desc[i].irq,&pins_desc[i]);


}




del_timer(&buttons_timer);


input_unregister_device(buttons_dev);


input_free_device(buttons_dev);


}




module_init(buttons_init);




module_exit(buttons_exit);




MODULE_LICENSE("GPL");







[/code]

下面是Makefile的内容:



[code]KERN_DIR=/home/pengdl/tq2440/kernel/origin/linux-2.6.30.4




all:


make-C$(KERN_DIR)M=`pwd`modules




clean:


make-C$(KERN_DIR)M=`pwd`modulesclean


rm-rfmodules.order




obj-m+=buttons.o

[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: