linux kernel input 子系统分析.docx
- 文档编号:3516091
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:41
- 大小:69.46KB
linux kernel input 子系统分析.docx
《linux kernel input 子系统分析.docx》由会员分享,可在线阅读,更多相关《linux kernel input 子系统分析.docx(41页珍藏版)》请在冰豆网上搜索。
linuxkernelinput子系统分析
linuxkernelinput子系统分析
Linux内核为了处理各种不同类型的的输入设备,比如说鼠标,键盘,操纵杆,触摸屏,设计并实现了一个对上层应用统一的试图的抽象层,即是Linux输入子系统.
输入子系统的层次结构体如下
从底层到上层,input子系统由设备驱动层,核心层,以及事件处理层3个部分组成
当一个鼠标移动,一个按键按下或弹起,它都需要从底层设备驱动-->核心层-->事件处理层-->用户空间,层层上报,一直到运用程序.
应用这个input 子系统有如下优点:
1.统一了各种各样的输入设备的处理方法.
2.提供了用于分布给用户使用的简单接口
3.提炼了输入驱动程序的通用部分,简化了驱动程序的开发和移植工作.
首先为大家上一个假设有个按键的驱动源,通过这个源码一层一层分析它是怎么事件上报的.
1#include"../bus.h"
2//这里面包含必要的头文件
3
4#defineGPX3CON0x11000C60
5
6
7staticstructinput_dev*device=NULL;
8staticvolatileunsignedint__iomem*gpxconf=NULL;
9
10staticintirqs[]={
11IRQ_EINT(26),
12IRQ_EINT(27),
13IRQ_EINT(28),
14IRQ_EINT(29),
15};
16
17voidHardWare_init(void)
18{
19gpxconf=ioremap(GPX3CON,SZ_4K);
20}
21voidHardWare_exit(void)
22{
23iounmap(gpxconf);
24}
25
26
27irqreturn_tdo_irq(intirqno,void*data)
28{
29//事件上报
30//算出第几个按键
31intkeynum=irqno-IRQ_EINT(26);//0-3
32unsignedintval;
33
34val=ioread32(gpxconf+1);
35val=val>>2;
36
37//printk("keynum:
%dval:
%#x\n",keynum,val);
38switch(keynum)
39{//事件上报函数.
40case0:
input_report_key(device,KEY_A,!
(val&(1< 41case1: input_report_key(device,KEY_B,! (val&(1< 42case2: input_report_key(device,KEY_C,! (val&(1< 43case3: input_report_key(device,KEY_D,! (val&(1< 44} 45 46//同步 47input_sync(device); 48 49returnIRQ_HANDLED; 50} 51 52 53//************************************************* 54//模块入口出口 55 56staticint__inittest_init(void) 57{ 58intret; 59inti; 60 61for(i=0;i 62{//请求中断 63ret=request_irq(irqs[i],do_irq,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING|IRQF_SHARED,"myirq",(void*)88); 64if(ret<0) 65{gotoErr; 66} 67} 68//分配一个事件上报设备结构体 69device=input_allocate_device(); 70if(IS_ERR(device)) 71{ 72ret=PTR_ERR(device); 73gotoErr1; 74} 75 76//#defineBITS_PER_LONG32 77//#defineBIT_MASK(nr)(1UL<<((nr)%BITS_PER_LONG)) 78//#defineBIT_WORD(nr)((nr)/BITS_PER_LONG) 79 80device->evbit[BIT_WORD(EV_KEY)]|=BIT_MASK(EV_KEY);//使能按键事件类型 81device->keybit[BIT_WORD(KEY_A)]|=BIT_MASK(KEY_A);//使能按键值为A的事件 82device->keybit[BIT_WORD(KEY_B)]|=BIT_MASK(KEY_B);//使能按键值为A的事件 83device->keybit[BIT_WORD(KEY_C)]|=BIT_MASK(KEY_C);//使能按键值为A的事件 84device->keybit[BIT_WORD(KEY_D)]|=BIT_MASK(KEY_D);//使能按键值为A的事件 85 86 87//注册一个input设备上报类型上报键值 88ret=input_register_device(device); 89if(ret<0) 90{ 91gotoErr2; 92} 93 94//硬件初始化 95HardWare_init(); 96 97returnret; 98 99Err2: 100input_free_device(device); 101Err1: 102Err: 103while(--i) 104{ 105free_irq(irqs[i],(void*)88); 106} 107 108returnret; 109} 110 111staticvoid__exittest_exit(void) 112{ 113inti; 114 115input_unregister_device(device); 116 117input_free_device(device); 118 119for(i=0;i 120{ 121free_irq(irqs[i],(void*)88); 122} 123 124HardWare_exit(); 125} 126 127module_init(test_init); 128module_exit(test_exit); 129 130MODULE_LICENSE("GPL"); 先对他的一个主体流程进行分析一下 代码分析: 模块入口函数为test_init 它首先去申请了4个按键的中断,双边缘触发. 而后,它分配一个事件上报结构体的空间,并对其进行赋值: 1staticstructinput_dev*device=NULL; 1//分配一个事件上报设备结构体 2device=input_allocate_device(); 1device->evbit[BIT_WORD(EV_KEY)]|=BIT_MASK(EV_KEY);//使能按键事件类型 2device->keybit[BIT_WORD(KEY_A)]|=BIT_MASK(KEY_A);//使能按键值为A的事件 3device->keybit[BIT_WORD(KEY_B)]|=BIT_MASK(KEY_B);//使能按键值为A的事件 4device->keybit[BIT_WORD(KEY_C)]|=BIT_MASK(KEY_C);//使能按键值为A的事件 5device->keybit[BIT_WORD(KEY_D)]|=BIT_MASK(KEY_D);//使能按键值为A的事件 这个结构体的原型是这样的: 1structinput_dev{ 2constchar*name;//设备名字 3constchar*phys;//设备在系统中的物理路径 4constchar*uniq;//设备的唯一标识码 5structinput_idid;//设备id,包含总线id,厂商id,与input_handler匹配的时会用到 6 7unsignedlongpropbit[BITS_TO_LONGS(INPUT_PROP_CNT)];//设备性质 8 9unsignedlongevbit[BITS_TO_LONGS(EV_CNT)];//设备支持的事件类型 10unsignedlongkeybit[BITS_TO_LONGS(KEY_CNT)];//支持的具体的按键。 按钮事件 11unsignedlongrelbit[BITS_TO_LONGS(REL_CNT)];//支持的具体的相对坐标事件 12unsignedlongabsbit[BITS_TO_LONGS(ABS_CNT)];//支持的具体的绝对坐标事件 13unsignedlongmscbit[BITS_TO_LONGS(MSC_CNT)];//支持的具体的混杂事件 14unsignedlongledbit[BITS_TO_LONGS(LED_CNT)];//支持的具体的led指示灯事件 15unsignedlongsndbit[BITS_TO_LONGS(SND_CNT)];//支持的具体的音效事件unsignedlongffbit[BITS_TO_LONGS(FF_CNT)];//支持的具体的力反馈事件 16unsignedlongswbit[BITS_TO_LONGS(SW_CNT)];//支持的具体的开关事件 17 18unsignedinthint_events_per_packet; 19 20unsignedintkeycodemax;//键盘码表的大小 21unsignedintkeycodesize;//键盘码中的元素个数 22void*keycode;//设备的键盘码表 23/*两个可选方法,用于配置和获取键盘码表*/ 24int(*setkeycode)(structinput_dev*dev, 25conststructinput_keymap_entry*ke, 26unsignedint*old_keycode); 27int(*getkeycode)(structinput_dev*dev, 28structinput_keymap_entry*ke); 29 30structff_device*ff;/*如果设备支持力反馈,则该成员将指向力反馈的设备描述 31结构*/ 32 33unsignedintrepeat_key;/*保存上一个键值,实现软件自动重复按键*/ 34structtimer_listtimer;/*用于软件自动重复按键的定时器*/ 35 36intrep[REP_CNT]; 37 38structinput_mt_slot*mt; 39intmtsize; 40intslot; 41inttrkid; 42 43structinput_absinfo*absinfo; 44 45unsignedlongkey[BITS_TO_LONGS(KEY_CNT)];//反映设备按键按钮的当前状态 46unsignedlongled[BITS_TO_LONGS(LED_CNT)];//反映设备led灯的当前状态 47unsignedlongsnd[BITS_TO_LONGS(SND_CNT)];//反映设备音效的当前状态 48unsignedlongsw[BITS_TO_LONGS(SW_CNT)];//反映设备开关的当前状态 49 50/*提供以下4个设备驱动层的操作接口,根据具体的设备需求实现他们*/ 51int(*open)(structinput_dev*dev);//将在input_open_device()中调用 52void(*close)(structinput_dev*dev);//将在input_close_device()中调用 53int(*flush)(structinput_dev*dev,structfile*file); 55/*(event)用于处理送到设备驱动层来的事件,很多事件在事件处理层被处理,但有很多事件仍需 56要送到设备驱动中,如led指示灯事件和音效事件,因为这些操作通常需要设备驱动执行*/ 57int(*event)(structinput_dev*dev,unsignedinttype,unsignedintcode,intvalue); 58 59structinput_handle__rcu*grab; 60 61spinlock_tevent_lock; 62structmutexmutex; 63 64/*记录输入事件处理程序(inputhandlers)调用设备open()方法的次数,保证设备open()是在第一个调用input_open_device()中被调用,设备close()方法是在最后一次调用input_close_device()中被调用*/ 65unsignedintusers; 66boolgoing_away; 67 68boolsync;//上次同步事件(EV_SYNC)发生后没有新事件产生,则被设置为1*/ 69 70structdevicedev;//内嵌设备device结构 71structlist_headh_list;//与该设备相关的输入句柄(inputhandles)列表 72structlist_headnode;//通过该成员,系统中的所有的input_dev对象被管理 73}; 上面是我做的一些注释. 而且在上面的按键设备的驱动中,我还对evbit ,和keybit 两个数值进行赋值. 它那个宏定义是这样的: 1//#defineBITS_PER_LONG32 2//#defineBIT_MASK(nr)(1UL<<((nr)%BITS_PER_LONG)) 3//#defineBIT_WORD(nr)((nr)/BITS_PER_LONG) 这里边所支持的事件类型和支持的具体的按键按钮类型是这样的: 1#defineEV_SYN0x00 2#defineEV_KEY0x01 3#defineEV_REL0x02 4#defineEV_ABS0x03 5#defineEV_MSC0x04 6#defineEV_SW0x05 7#defineEV_LED0x11 8#defineEV_SND0x12 9#defineEV_REP0x14 9#defineEV_FF0x15 10#defineEV_PWR0x16 11#defineEV_FF_STATUS0x17 12#defineEV_MAX0x1f 12#defineEV_CNT(EV_MAX+1) 所支持的按键按钮类型就比较多了 1 2#defineKEY_RESERVED0 3#defineKEY_ESC1 4#defineKEY_12 5#defineKEY_23 6#defineKEY_34 7#defineKEY_45 8#defineKEY_56 9#defineKEY_67 10#defineKEY_78 11#defineKEY_89 12#defineKEY_910 13#defineKEY_011 14...... 这些相对应的类型都是用一些宏定义来定义,到上报个核心层再上报到事件处理层的时候以一个结构体的形式的数据体现给用户,后面会讲到这个结构体 回到源代码: 然后就是注册一个input设备把刚才赋值的事件上报类型传进去: 1//注册一个input设备上报类型上报键值 2ret=input_register_device(device); 3if(ret<0) 4{ 5gotoErr2; 6} 后面的硬件初始化就是对gpio口的配置而已,这里涉及到硬件原理图上的东西,其实就是做了一个寄存器地址的映射 在中断函数内 1irqreturn_tdo_irq(intirqno,void*data) 2{ 3//事件上报 4//算出第几个按键 5intkeynum=irqno-IRQ_EINT(26);//0-3 6unsignedintval; 7 8val=ioread32(gpxconf+1); 9val=val>>2; 10 11//printk("keynum: %dval: %#x\n",keynum,val); 12switch(keynum) 13{ 14case0: input_report_key(device,KEY_A,! (val&(1< 15case1: input_report_key(device,KEY_B,! (val&(1< 16case2: input_report_key(device,KEY_C,! (val&(1< 17case3: input_report_key(device,KEY_D,! (val&(1< 18} 19 20//同步 21input_sync(device); 22 23returnIRQ_HANDLED; 24} 25 其实总的来说我们做了两个事情,第一个就是触发中断后,我们上报了是第几个按键触发的中断,并进行同步处理. 现在,我已经把这个最简单的一个按键事件上报驱动讲解了一遍 ,其实我们有很多疑问: 第一个就是: 我们怎么分配的一个事件上报结构体 第二个问题,我们是怎么将事件层层上报,将inputdriver,inputcore,inputhandler这三层联系起来的. 第三个问题,我们给上层提供的是一个怎么样的借口,给上层上报的是一个怎么的数据. 其实第一个问题比较简单, 我只是为了拿出来缓解一下当前比较紧张的气氛,透露一下,第二个问题才是关键! 第一个问题它只是做了一个分配空间并对其初始化值的操作: 1*///申请一个新的inputdevice 2structinput_dev*input_allocate_device(void) 3{ 4structinput_dev*dev; 5 6dev=kzalloc(sizeof(structinput_dev),GFP_KERNEL); 7if(dev){ 8dev->dev.type=&input_dev_type; 9dev->dev.class=&input_class; 10device_initialize(&dev->dev); 11mutex_init(&dev->mutex); 12spin_lock_init(&dev->event_lock); 13INIT_LIST_HEAD(&dev->h_list); 14INIT_LIST_HEAD(&dev->node); 15 16__module_get(THIS_MODULE); 17} 18 19returndev; 20} 21EXPORT_SYMBOL(input_allocate_device); 是不是觉得自己竟然能两眼就能看懂这些代码,别惊讶,其实你自己挺强的,就连我都要花喵一眼的时间去看才能看懂它 下面才是重头戏: inputcore 代码. 在前面的驱动当中,我们在分配完input_dev结构体后,我们直接在没有产生并触发按键中断之前,抢先注册了一个input设备 1//注册一个input设备上报类型上报键值 2ret=input_register_device(device); 其实如果按照标准的写法 ,我们注册一个input
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux kernel input 子系统分析 子系统 分析