输入子系统
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
其实,输入子系统就是具体到某一类字符设备驱动框架,后续还有总线设备驱动框架等等一些字符驱动框架。
首先,从应用程序先是调用input.c文件,在编写比较基础的一些字符设备驱动时,我们写测试程序都是在主函数中调用到open函数打开dev目录下的设备节点。
为了无缝衔接应用程序(中的那些库函数)。
比如我们在开发Qt程序,一般我们都见不到设备节点的。
所以引入输入子系统等一些具体到一类的字符设备驱动。
字符设备驱动程序框架,注册一个file_operations结构体,接着通过class自动创建设备节点,然后进行一些寄存器地址映射,最后构造file_operations结构体中的函数。
Input.c文件也类似。
这边有个概念就是分层分离的概念(本文不加赘述)input.c作为核心层,底下分别拥有一个设备层(device)和一个事件处理层(handler),正常情况下Driver->Inputcore->Event handler->userspace的顺序到达用户控件的应用程序。
但是在理解上更像是二者向一的关系,比如driver和event_handler都需要向input.c注册,二者需要通过input.c中的函数input_register_handler和函数input_register_device相互匹配。
Device上报事件(即调用input_event函数)在input_event函数中调用connect函数。
Input handler负责处理事件
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
现在开始从调用input.c文件开始往下分析:应用程序调用一系列系统接口,最后调用到input.c文件,入口函数中注册类和fops结构体,接着在input_open_file函数中定义一个input_handler结构体handler和input_table结构体数组(以次设备号)接着获得handler中的结构体给新的fops结构体再让应用层(file->f_op = new_fops;)直接调用到input_handler 中的fops结构体。
接着打开new_fops结构体中的open函数err = new_fops->open(inode, file)。
在input_open_file函数中看到input_handler结构体以及input_table数组结构体,该数组在input_register_handler函数中进行处理,由前文可知handler和device都需要向input.c文件注册。
应该就是这个函数还有一个input_register_device函数,在input_register_handler函数中除了处理input_table数组还有将handler->node加入input_handler_list链表,然后列出每一个input_dev_list链表中的条目接着调用input_attach_handler(dev, handler);在这里面又干什么?在里面根据id_table与dev进行匹配input_match_device(handler->id_table, dev);最后如果匹配成功就进行连接handler->connect(handler, dev, id);在input_register_device也是一样的处理,不过加入的链表是设备链表(input_dev_list)列出的是handler的链表进行匹配和连接。
实际中就是该输入子系统,我们需要做的就是编写设备层中的驱动程序。
意思就是在内核中就有这么一套机制,就是事件处理层已经拥有。
现在具体到一个事件处理层驱动程序evdev.c文件,在入口函数就是注册一个input_handler结构体
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,(次设备号64)
.name = "evdev",
.id_table = evdev_ids,
};
看到这个结构体联系上文就知道,在input_register_handler中处理input_table[64>>5](相当于2)接着根据evdev_ids进行匹配如果匹配成功,执行evdev_connect函数(注意这边用
evdev_ids进行匹配,但是在后面的总线驱动框架中用名字来匹配的)在evdev_connect函数中陷入休眠,创建设备节点,最后注册一个input_register_handle函数。
在函数里面将input_handle的d_node链表和h_node链表分别与input_dev结构体中的h_list链表和input_handler结构体中的h_list链表进行关联。
最终效果就是input_handler和input_dev通过input_handle进行相互联系,可以通过input_dev结构体中的h_list找到input_handler结构体中的h_list,反之也成。
给出内核提供的例子gpio_keys.c文件,在此该文件属于设备层,前文说到:设备层提供上报事件,首先,该文件的入口函数也是向input.c文件注册一个platform_driver(平台驱动结构体),其实这个文件比较适合讲解后面的总线驱动框架不过没事一个道理。
后面讲解的就是设备层和事件处理层connect成功就调用probe函数,输入子系统可以简洁一点就是把probe函数移到入口函数中去。
写设备层驱动程序步骤①分配一个input_dev结构体②设置(比如产生哪类事件以及在该类产生哪些事件,按键类事件,哪个按键)③注册input_dev 结构体④硬件操作。
其次,上报事件两个函数input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)和input_sync(struct input_dev *dev),上报完事件,在evdev.c 文件中的evdev_event函数中唤醒进程wake_up_interruptible(&evdev->wait);至此分析结束。
总而言之,输入子系统只是把字符设备驱动程序分开成纯软件文件和硬件操作的文件,两个文件相互联系共同服务上层应用程序。