Chinaunix首页 | 论坛 | 博客
  • 博客访问: 829077
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: LINUX

2015-11-12 13:54:24

输入子系统:input子系统---> 使用别人做好的框架

1、输入子系统框架

/drivers/input.c 核心层 从入口函数开始分析

err = register_chrdev(INPUT_MAJOR, "input", &input_fops);     注册字符驱动程序驱动程序 INPUT_MAJOR13,主设备号
这个函数中在input_fops的结构中只有open函数
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};

再具体分析
open函数,即input_open_file函数
static int
input_open_file(struct inode *inode, struct file *file)
struct input_handler *handler;
handler = input_table[iminor(inode) >> 5];获取该文件的次设备号,根据这个次设备号在一个数组里面找到对应的handler
if (!handler || !(new_fops = fops_get(handler->fops)))
file->
f_op = new_fops;

给我们打开的这个文件填充新的file_operations结构,这个新的file_operations结构是我们要写的。里面的函数也是我们要实现的
input.c
核心层,以上部分代码都是核心层的


int input_register_handler(struct input_handler *handler);
//evdev.ctsdev.cjoydev.ckeyboard.cmousedev.c 等文件调用了input_register_handler
int input_register_device(struct input_dev *dev);
//我们写的程序里面注册
connect
disconnect函数

以及下面的软件层和设备层用到的 两个链表< input_devinput_handler链表 > 都在/drivers/input.c文件里面 input_handler_listinput_dev_list


2软件层:代表软件,当系统发现有设备进来的时候,它就会比较。这些handler是否支持那些设备

input_table这个数组由谁构造的?
是由 int input_register_handler(struct input_handler *handler)构造的

那么input_register_handler这个函数又是被谁调用的?搜索发现比较多,选择其中一个文件来分析

是由 drivers/input/evdev.c   drivers/input/keyboard.c   drivers/input/mousedev.c 等构造的,以drivers/input/evdev.c为例子来具体分析 :这些是对应纯软件部分
int input_register_handler(struct input_handler *handler)
list_add_tail(&handler->node, &input_handler_list);
注意与下面的设备层对应
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
与下面的设备层的解释一样


drivers/input/
evdev.c里面是一个事件驱动程序
他们向上注册input_register_handler函数 软件部分
static struct
input_handler evdev_handler = {
.event = evdev_event,
.
connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.
id_table = evdev_ids, //表示该handler能够支持哪一些设备,如果支持的话,就会调用connect函数下面的设备层建立连接
};
上面的file->f_op = new_fops;将会变成file->f_op = evdev_fops;

3设备层:代表硬件 以下的分析的文件也是/drivers/input.c

int input_register_device(struct input_dev *dev) //注册输入设备
list_add_tail(&dev->node, &input_dev_list); //
放入链表,
list_for_each_entry(handler, &input_handler_list, node) //
注意前面一行的链表和这一行的链表不是一个哟!
input_attach_handler(dev, handler); //
对于每一个input_handler都调用input_attach_handler函数,这个函数就是根据 input_handlerid_table判断能否支持这一个input_dev输入设备
input_match_device(handler->id_table, dev);
error = handler->connect(handler, dev, id);
如果匹配的话就调用input_handler(不是input_dev里面的connect函数

4、框架总结

上面的驱动程序体现了,设备层和软件层分离的概念。主要意思就是说,不管你是先加载的软件层(input_handler)还是设备层(input_dev)大家都会去对方的链表里面搜索有没有匹配自己的那一部分,如果匹配就将建立链接。
注册input_dev或者input_handler时,会两两比较设备层的input_dev和软件层的input_handler,根据input_handlerid_table判断,input_handler能否支持这个input_dev设备,如果支持的话就调用input_handlerconnect函数建立链接。


5、怎么把input_devinput_handler建立链接分析evdev_handlerconnect函数:即evdev_connect

int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id):该函数也在/drivers/input.c文件里面
struct evdev *evdev;
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
分配一个结构体,里面包含input_handle结构,该结构struct evdev里面,然后在设置input_handle结构
evdev->
handle.dev = input_get_device(dev); 指向设备层注册的输入设备input_dev
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
指向软件层input_handler
evdev->handle.private = evdev;
error =
input_register_handle(&evdev->handle); 把前面分配设置好的input_handle结构注册进入内核

 

input_register_handle中有如下代码:
list_add_tail_rcu(&handle->d_node, &dev->h_list);
input_handle(不是input_handler放入到input_devh_list链表 , h_list h_node都是struct list_head结构
list_add_tail(&handle->h_node, &handler->h_list);
input_handle(不是input_handler放入到input_handlerh_ list链表, h_noded_node都是struct list_head结构,只是区分一下而已。

 
这样的话,由于向内核注册了input_handle(不是input_handler)结构。input_dev层就可以通过自己的input_dev->h_list找到对应的input_handle(因为input_dev->h_list input_handle->d_node是相同的)。找到input_handle后就可以利用input_handle->h_node找到input_handler(因为input_handle->h_nodeinput_handler->h_list是相同的) 这样就建立起来了一个连接。
以上的前提就是input_handle(不是input_handler)结构里面有devstruct input_dev)和handlerstruct input_handler )两个成员
小结:
1
、先分配一个input_handle结构
2
、设置该结构
3
、注册该结构


6怎么读按键

appread
--------------------------------------------
将会导致新的file_operations里面的read函数被调用:即evdev_read函数 是软件层的(input_hander)。
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);

1:那么谁来唤醒它呢?
1:猜测,应该是设备层(input_dev)来触发。wake_up_interruptible(&evdev->wait);这个函数来唤醒

2wake_up_interruptible(&evdev->wait);这个函数谁调用?
2evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)这个函数来调用。这个函数就是软件层(input_handler)里面的event函数。指发生了某些事件。这个函数就是将数据存入缓冲区,然后唤醒读数据函数进程

3evdev_event这个函数又被谁调用呢?
3:猜测。应该是硬件相关的代码,input_dev的硬件层调用。参考/drivers/input/keyboard/gpio_key.c
在设备的中断服务里面,确定事件是什么,然后调用相应的input_handelerevent处理函数

、要上报事件  input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)

input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)    这个函数在2.6.32里面基本不用
input_handle_event(dev, type, code, value);
这个函数和韦东山老师的视频里面讲解的调用不一样,在设备注册函数input_register_device里面会初始化一个定时器,在定时器到来的时候,定时器处理函数里面会调用input_handle_event这个函数,进而上报事件。

eg:定时器处理函数input_repeat_key 一般情况下,中断来了后会初始化一个定时器,里面设置了一个超时处理函数。
input_repeat_key //
定时器超时处理函数
input_pass_event(dev, EV_KEY, dev->repeat_key, 2); //
上报事件
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
input_pass_event(dev, EV_SYN, SYN_REPORT, 1) //
同步信号

、来一个同步信号input_pass_event(dev, EV_SYN, SYN_REPORT, 1); 接上面的例子


7、应用程序怎么操作:其实就是相当于移花接木。我们人为的把别人打开的文件给替换了。

打开一个输入设备文件的时候,会根据设备文件的次设备号找到一个数组,这个数组成员就是一个handlerinput_handler),调用handler里面的open函数,并且把这个文件的file_operations更新为我们写的file_operations。这个数组就是被软件部分(input_handler)构造的。input_register_handler向上注册的,并且填充那个数组。由于这个是纯软件的,要想操作硬件就必须要和设备层联系起来。而设备层也会向上面的核心层注册自己。

既然
软件层设备层都注册了自己。必须得把它们联系起来啊。这个时候就出现了input_handle结构(该结构是input_handlerconnect函数建立的)。该结构就是将它们联系起来的枢纽。

最终,read 一个文件的时候,假如应用程序被阻塞的话,那么必须要有东西来唤醒它,硬件中如果有数据产生的话,会触发一个中断,该中断会和一个设备绑定起来,这个设备就是input_dev(设备层)结构。在中断处理函数里面,会利用该设备(input_dev)找到input_handle结构,再利用 input_handle 结构找到input_handler结构的event函数。该函数就是唤醒应用程序的read操作。进而完成一次读操作。


阅读(723) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~