输入子系统

一.概念

 内核的输入子系统是对分散的、多种不同类别的输入设备(如键盘、鼠标、跟踪球、触摸屏、加速计和手写板等)进行统一处理的驱动程序。我们可以理解为我们使用了现成的驱动,只是将我们的代码融合进去。

  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。

  以前我们的驱动程序打开了一个特定的设备文件(即我们自己分配的类下的节点)。而一般写的应用程序不会去打开这个我们自己写的驱动,我们写的驱动同时也只有我们自己知道。一般打开的都是原有的文件,如“ dev/tty* ” ,甚至是不需要打开tty,而是直接“scanf()”就去获得了按键的输入。以前写的那些驱动程序只能自已使用并不具有通用性。要写一个通用的驱动程序,让其他应用程序“无缝”的使用, 就是说不需要修改人家的应用程序。这需要使用现成的驱动,把自已的设备相关的驱动放到内核中这种驱动架构中去。这个现成的驱动就是“输入子系统--input子系统”。

二.源码分析

  我们要将我们的设备相关的驱动放到内核中这种驱动架构中去。我们就应该知道别人的设备驱动是怎么写的,故而源码分析必不可少。

而我们要分析一个驱动程序就应该从入口函数开始看。

 我们可以看到 这里面有我们很熟悉的注册函数

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

我们之前所写的open,read等等函数都是放在file_operation这个结构体中同样我们也要看看这个系统提供的file_operation结构体里面有什么。

 这就很奇怪了你不是要输入嘛,为什么只有open呢,那我们就要去看看open到底干了什么

 

 

首先我们看第一行定义了一个input_handle*handler=input_table[iminor(inode)>>5];这里我们可以这么理解它在input_table【】这个数组里依据次设备号打开了一项并将其句柄返回回来。紧接着我们看到在第一个if里我们从这个新的handler里得到了一个file_operation结构体给new_fops,之后我们打开的这个文件的file->f_po等于new_fops.然后在调用这个新的new_fops里的open函数。

总结:input_open_file函数就是一个输入子系统得统一接口,应用程序只需要通过调用这个接口。input_open_file函数就会根据传递里面得设备号获取次设备号,找到该设备对应得file_operations,去执行该设备驱动得open函数。

那么我们要知道new_fops,就要知道是谁构造的input_table【】这个数组。

我们看到了input_register_handler函数里它使得input_table【】数组里的(handler->minor>>5)这一项等于handler。我们再次搜索,看哪些函数调用了input_register_handler函数

 我们得到了按键鼠标等等都调用了这个函数,我们随便点进去一个。

 

我们看到了是在初始化的时候调用了input_register_handler函数,所以我们要去看它对evdev_handler这个数组的初始化。

 我们可以将这个数组理解为输入处理器。event表示这个事件处理函数,id_table则表示可以支持哪些设备,minor用来存放次设备号。假设我们要读某个文件最终我们新的file_operation结构体new_fops就等于&evdev_fops。 .connect:连接函数,将设备input_dev和某个input_handler建立连接,如下图 

 那它是这么注册的呢,我们可以看input_register_handler函数

在这个函数中我们看到list_add_tail(&handler->node,&input_handler_list);将handler里的node挂在了inpt_handler_list这个链表里。

之后list_for_each_entry(dev,&input_dev_list,node) input_attach_handler(dev,handler);这两个表示对每一项input_dev调用函数。其作用是根据input_handler的id_table判断能否支持这个input_dev这个输入设备。

那我们再去看看 input_register_device会做什么

 

我们看到它和 input_register_handler函数十分相像,在这个函数中我们看到list_add_tail(&dev->node,&input_dev_list);将dev里的node挂在了inpt_dev_list这个链表里。

之后list_for_each_entry(handler,&input_handler_list,node) input_attach_handler(dev,handler);这两个表示对每一项input_dev调用函数。其作用是根据input_handler的id_table判断能否支持这个input_dev这个输入设备。

从这里我们可以看出无论是左边先注册还是右边先注册最终都会两两相匹配,那我们就要看看这个input_attach_handler函数干了什么。

里面的id=input_match_device(handler->id_table,dev);是 根据input_handler的id_table判断能否支持这个input_dev这个输入设备。可以相互匹配的话就调用下面的connect函数建立链接。我们还是以evdev.c(事件驱动) 的evdev_handler->connect函数来分析是怎样建立连接的。

 在这里我们可以清晰的看到他是通过input_handle(注意这里没有r)结构体进行建立链接的如下图

 建立了连接后,又如何读取evdev.c(事件驱动) 的evdev_handler->.fops->.read函数

 我们可以看到它在没有数据时会进入休眠状态,那么是那个函数来唤醒这个状态的呢。

我们去搜索evdev->wait这个等待队列,找到了evdev_event这个函数里唤醒的,而evdev_event又被谁调用,应该就是之前分析的input_dev那层调用的。

 十分明显就是通过input_event()函数来调用.event事件函数的

 若之前驱动input_dev和处理input_handler已经通过input_handler 的.connect函数建立起了连接,那么就调用evdev_event()的.event事件函数,之后在wake_up_interruptible(&evdev->wait),来唤醒evdev_read函数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值